aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRay <raysan5@gmail.com>2016-06-06 20:46:06 +0200
committerRay <raysan5@gmail.com>2016-06-06 20:46:06 +0200
commit1c98e6b698b8002e0c6c769c6d9f23a6e15f3bdf (patch)
tree0aba231bb77034cae38dc44e39d53b63197c6a2c /src
parent75a73d94171051037fcf670852877977d9251520 (diff)
parent4dada3269374a82fa2c4a06bd29dfc0f37a64380 (diff)
downloadraylib-1c98e6b698b8002e0c6c769c6d9f23a6e15f3bdf.tar.gz
raylib-1c98e6b698b8002e0c6c769c6d9f23a6e15f3bdf.zip
Merge pull request #125 from raysan5/develop
Develop branch integration
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt32
-rw-r--r--src/Makefile (renamed from src/makefile)69
-rw-r--r--src/android/jni/Android.mk59
-rw-r--r--src/android/jni/Application.mk3
-rw-r--r--src/android/jni/include/AL/al.h825
-rw-r--r--src/android/jni/include/AL/alc.h285
-rw-r--r--src/android/jni/include/AL/alext.h165
-rw-r--r--src/android/jni/include/AL/efx-creative.h3
-rw-r--r--src/android/jni/include/AL/efx.h758
-rw-r--r--src/android/jni/include/AL/oalMacOSX_OALExtensions.h161
-rw-r--r--src/android/jni/include/AL/oalStaticBufferExtension.h0
-rw-r--r--src/android/jni/include/android_native_app_glue.h349
-rw-r--r--src/audio.c791
-rw-r--r--src/audio.h39
-rw-r--r--src/camera.c10
-rw-r--r--src/camera.h1
-rw-r--r--src/core.c1678
-rw-r--r--src/easings.h22
-rw-r--r--src/external/glad.c (renamed from src/glad.c)0
-rw-r--r--src/external/glad.h (renamed from src/glad.h)0
-rw-r--r--src/external/glfw3/COPYING.txt22
-rw-r--r--src/external/glfw3/include/GLFW/glfw3.h4235
-rw-r--r--src/external/glfw3/include/GLFW/glfw3native.h456
-rw-r--r--src/external/glfw3/lib/linux/libglfw3.abin0 -> 176482 bytes
-rw-r--r--src/external/glfw3/lib/osx/libglfw.3.0.dylibbin0 -> 127856 bytes
-rw-r--r--src/external/glfw3/lib/osx/libglfw.3.dylib1
-rw-r--r--src/external/glfw3/lib/osx/libglfw.dylib1
-rw-r--r--src/external/glfw3/lib/win32/glfw3.dllbin0 -> 305452 bytes
-rw-r--r--src/external/glfw3/lib/win32/libglfw3.abin0 -> 148800 bytes
-rw-r--r--src/external/glfw3/lib/win32/libglfw3dll.abin0 -> 67426 bytes
-rw-r--r--src/external/jar_mod.h1587
-rw-r--r--src/external/jar_xm.h2666
-rw-r--r--src/external/openal_soft/COPYING484
-rw-r--r--src/external/openal_soft/include/AL/al.h656
-rw-r--r--src/external/openal_soft/include/AL/alc.h237
-rw-r--r--src/external/openal_soft/include/AL/alext.h438
-rw-r--r--src/external/openal_soft/include/AL/efx-creative.h3
-rw-r--r--src/external/openal_soft/include/AL/efx-presets.h402
-rw-r--r--src/external/openal_soft/include/AL/efx.h761
-rw-r--r--src/external/openal_soft/lib/win32/OpenAL32.dllbin0 -> 845045 bytes
-rw-r--r--src/external/openal_soft/lib/win32/libOpenAL32.dll.abin0 -> 100246 bytes
-rw-r--r--src/external/stb_image.h (renamed from src/stb_image.h)263
-rw-r--r--src/external/stb_image_resize.h (renamed from src/stb_image_resize.h)110
-rw-r--r--src/external/stb_image_write.h (renamed from src/stb_image_write.h)14
-rw-r--r--src/external/stb_rect_pack.h (renamed from src/stb_rect_pack.h)26
-rw-r--r--src/external/stb_truetype.h (renamed from src/stb_truetype.h)52
-rw-r--r--src/external/stb_vorbis.c (renamed from src/stb_vorbis.c)112
-rw-r--r--src/external/stb_vorbis.h (renamed from src/stb_vorbis.h)20
-rw-r--r--src/external/tinfl.c (renamed from src/tinfl.c)0
-rw-r--r--src/gestures.c38
-rw-r--r--src/gestures.h4
-rw-r--r--src/libraylib.bcbin603552 -> 710808 bytes
-rw-r--r--src/models.c806
-rw-r--r--src/physac.c729
-rw-r--r--src/physac.h67
-rw-r--r--src/raygui.c162
-rw-r--r--src/raygui.h37
-rw-r--r--src/raylib.h411
-rw-r--r--src/raymath.h43
-rw-r--r--src/rlgl.c2664
-rw-r--r--src/rlgl.h202
-rw-r--r--src/shapes.c100
-rw-r--r--src/text.c132
-rw-r--r--src/textures.c150
-rw-r--r--src/utils.c15
-rw-r--r--src/utils.h4
66 files changed, 20034 insertions, 3326 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..c094ad96
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required (VERSION 3.0)
+project (raylib)
+SET(PLATFORM_TO_USE "PLATFORM_DESKTOP" CACHE STRING "Platform to compile for")
+SET_PROPERTY(CACHE PLATFORM_TO_USE PROPERTY STRINGS PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB)
+
+set(CMAKE_C_FLAGS "-O1 -Wall -std=gnu99 -fgnu89-inline -Wno-missing-braces")
+
+IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_DESKTOP")
+
+ add_definitions(-DPLATFORM_DESKTOP, -DGRAPHICS_API_OPENGL_33)
+ include_directories("." "external/" "external/openal_soft/include" "external/glfw3/include")
+
+ENDIF()
+
+IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_RPI")
+
+ add_definitions(-DPLATFORM_RPI, -GRAPHICS_API_OPENGL_ES2)
+ include_directories("." "external/" "/opt/vc/include" "/opt/vc/include/interface/vmcs_host/linux" "/opt/vc/include/interface/vcos/pthreads")
+
+ENDIF()
+
+IF(${PLATFORM_TO_USE} MATCHES "PLATFORM_WEB")
+
+ add_definitions(-DPLATFORM_WEB, -GRAPHICS_API_OPENGL_ES2)
+ include_directories("." "external/" "external/openal_soft/include" "external/glfw3/include")
+
+ENDIF()
+
+
+file(GLOB SOURCES "*.c" "external/*.c")
+add_library(raylib STATIC ${SOURCES})
+install(TARGETS raylib DESTINATION ../lib/) \ No newline at end of file
diff --git a/src/makefile b/src/Makefile
index cab2ced0..92e37cfb 100644
--- a/src/makefile
+++ b/src/Makefile
@@ -21,6 +21,8 @@
#
#**************************************************************************************************
+.PHONY: all clean
+
# define raylib platform to compile for
# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB
PLATFORM ?= PLATFORM_DESKTOP
@@ -67,27 +69,26 @@ else
endif
# define compiler flags:
-# -O1 defines optimization level
-# -Wall turns on most, but not all, compiler warnings
-# -std=c99 defines C language mode (standard C from 1999 revision)
-# -std=gnu99 defines C language mode (GNU C from 1999 revision)
-# -fgnu89-inline declaring inline functions support (GCC optimized, faster)
-CFLAGS = -O1 -Wall -std=gnu99 -fgnu89-inline
+# -O1 defines optimization level
+# -Wall turns on most, but not all, compiler warnings
+# -std=c99 defines C language mode (standard C from 1999 revision)
+# -std=gnu99 defines C language mode (GNU C from 1999 revision)
+# -fgnu89-inline declaring inline functions support (GCC optimized, faster)
+# -Wno-missing-braces ignore invalid warning (GCC bug 53119)
+CFLAGS = -O1 -Wall -std=gnu99 -fgnu89-inline -Wno-missing-braces
#CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes
# define any directories containing required header files
ifeq ($(PLATFORM),PLATFORM_RPI)
- INCLUDES = -I. -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads
+ INCLUDES = -I. -Iexternal -I/opt/vc/include -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads
else
- INCLUDES = -I. -I../src
-# external libraries headers
-# GLFW3
- INCLUDES += -I../external/glfw3/include
-# GLEW
- INCLUDES += -I../external/glew/include
-# OpenAL Soft
- INCLUDES += -I../external/openal_soft/include
+# STB libraries and others
+ INCLUDES = -I. -Iexternal
+# GLFW3 library
+ INCLUDES += -Iexternal/glfw3/include
+# OpenAL Soft library
+ INCLUDES += -Iexternal/openal_soft/include
endif
# define all object files required
@@ -99,9 +100,9 @@ else
endif
-# typing 'make' will invoke the first target entry in the file,
+# typing 'make' will invoke the default target entry called 'all',
# in this case, the 'default' target entry is raylib
-default: raylib
+all: raylib
# compile raylib library
raylib: $(OBJS)
@@ -120,10 +121,6 @@ core.o: core.c
rlgl.o: rlgl.c
$(CC) -c rlgl.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS)
-# compile glad module
-glad.o: glad.c
- $(CC) -c glad.c $(CFLAGS) $(INCLUDES)
-
# compile shapes module
shapes.o: shapes.c
$(CC) -c shapes.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS)
@@ -143,10 +140,6 @@ models.o: models.c
# compile audio module
audio.o: audio.c
$(CC) -c audio.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM)
-
-# compile stb_vorbis library
-stb_vorbis.o: stb_vorbis.c
- $(CC) -c stb_vorbis.c -O1 $(INCLUDES) -D$(PLATFORM)
# compile utils module
utils.o: utils.c
@@ -160,25 +153,33 @@ camera.o: camera.c
gestures.o: gestures.c
$(CC) -c gestures.c $(CFLAGS) $(INCLUDES) -D$(PLATFORM)
+# compile glad module
+glad.o: external/glad.c
+ $(CC) -c external/glad.c $(CFLAGS) $(INCLUDES)
+
+# compile stb_vorbis library
+stb_vorbis.o: external/stb_vorbis.c
+ $(CC) -c external/stb_vorbis.c -O1 $(INCLUDES) -D$(PLATFORM)
+
# clean everything
clean:
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
- ifeq ($(PLATFORM_OS),OSX)
- rm -f *.o libraylib.a
- else
- ifeq ($(PLATFORM_OS),LINUX)
- find -type f -executable | xargs file -i | grep -E 'x-object|x-archive|x-sharedlib|x-executable' | rev | cut -d ':' -f 2- | rev | xargs rm -f
- else
+ ifeq ($(PLATFORM_OS),WINDOWS)
del *.o libraylib.a
+ else
+ rm -f *.o libraylib.a
endif
+endif
+ifeq ($(PLATFORM),PLATFORM_WEB)
+ ifeq ($(PLATFORM_OS),WINDOWS)
+ del *.o libraylib.bc
+ else
+ rm -f *.o libraylib.bc
endif
endif
ifeq ($(PLATFORM),PLATFORM_RPI)
rm -f *.o libraylib.a
endif
-ifeq ($(PLATFORM),PLATFORM_WEB)
- del *.o libraylib.bc
-endif
@echo Cleaning done
# instead of defining every module one by one, we can define a pattern
diff --git a/src/android/jni/Android.mk b/src/android/jni/Android.mk
new file mode 100644
index 00000000..0325d1f5
--- /dev/null
+++ b/src/android/jni/Android.mk
@@ -0,0 +1,59 @@
+#**************************************************************************************************
+#
+# raylib for Android
+#
+# Static library compilation
+#
+# Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com)
+#
+# 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.
+#
+#**************************************************************************************************
+
+# Path of the current directory (i.e. the directory containing the Android.mk file itself)
+LOCAL_PATH := $(call my-dir)
+
+# raylib static library compilation
+# NOTE: It uses source placed on relative path ../../src from this file
+#-----------------------------------------------------------------------
+# Makefile that will clear many LOCAL_XXX variables for you
+include $(CLEAR_VARS)
+
+# Module name
+LOCAL_MODULE := raylib
+
+# Module source files
+LOCAL_SRC_FILES :=\
+ ../../core.c \
+ ../../rlgl.c \
+ ../../textures.c \
+ ../../text.c \
+ ../../shapes.c \
+ ../../gestures.c \
+ ../../models.c \
+ ../../utils.c \
+ ../../audio.c \
+ ../../stb_vorbis.c \
+
+# Required includes paths (.h)
+LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/include $(LOCAL_PATH)/../..
+
+# Required flags for compilation: defines PLATFORM_ANDROID and GRAPHICS_API_OPENGL_ES2
+LOCAL_CFLAGS := -Wall -std=c99 -Wno-missing-braces -g -DPLATFORM_ANDROID -DGRAPHICS_API_OPENGL_ES2
+
+# Build the static library libraylib.a
+include $(BUILD_STATIC_LIBRARY)
+#--------------------------------------------------------------------
diff --git a/src/android/jni/Application.mk b/src/android/jni/Application.mk
new file mode 100644
index 00000000..fab0d7e5
--- /dev/null
+++ b/src/android/jni/Application.mk
@@ -0,0 +1,3 @@
+APP_ABI := $(TARGET_ARCH_ABI)
+# $(warning APP_ABI $(APP_ABI))
+# $(warning LOCAL_ARM_NEON $(LOCAL_ARM_NEON))
diff --git a/src/android/jni/include/AL/al.h b/src/android/jni/include/AL/al.h
new file mode 100644
index 00000000..e084b3ed
--- /dev/null
+++ b/src/android/jni/include/AL/al.h
@@ -0,0 +1,825 @@
+#ifndef AL_AL_H
+#define AL_AL_H
+
+#ifdef ANDROID
+#include <android/log.h>
+#ifndef LOGI
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"OpenAL",__VA_ARGS__)
+#endif
+#ifndef LOGE
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,"OpenAL",__VA_ARGS__)
+#endif
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(AL_LIBTYPE_STATIC)
+ #define AL_API
+#elif defined(_WIN32) && !defined(_XBOX)
+ #if defined(AL_BUILD_LIBRARY)
+ #define AL_API __declspec(dllexport)
+ #else
+ #define AL_API __declspec(dllimport)
+ #endif
+#else
+ #if defined(AL_BUILD_LIBRARY) && defined(HAVE_GCC_VISIBILITY)
+ #define AL_API __attribute__((visibility("protected")))
+ #else
+ #define AL_API extern
+ #endif
+#endif
+
+#if defined(_WIN32)
+ #define AL_APIENTRY __cdecl
+#else
+ #define AL_APIENTRY
+#endif
+
+#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
+ #pragma export on
+#endif
+
+/*
+ * The OPENAL, ALAPI, ALAPIENTRY, AL_INVALID, AL_ILLEGAL_ENUM, and
+ * AL_ILLEGAL_COMMAND macros are deprecated, but are included for
+ * applications porting code from AL 1.0
+ */
+#define OPENAL
+#define ALAPI AL_API
+#define ALAPIENTRY AL_APIENTRY
+#define AL_INVALID (-1)
+#define AL_ILLEGAL_ENUM AL_INVALID_ENUM
+#define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
+
+#define AL_VERSION_1_0
+#define AL_VERSION_1_1
+
+
+/** 8-bit boolean */
+typedef char ALboolean;
+
+/** character */
+typedef char ALchar;
+
+/** signed 8-bit 2's complement integer */
+typedef signed char ALbyte;
+
+/** unsigned 8-bit integer */
+typedef unsigned char ALubyte;
+
+/** signed 16-bit 2's complement integer */
+typedef short ALshort;
+
+/** unsigned 16-bit integer */
+typedef unsigned short ALushort;
+
+/** signed 32-bit 2's complement integer */
+typedef int ALint;
+
+/** unsigned 32-bit integer */
+typedef unsigned int ALuint;
+
+/** non-negative 32-bit binary integer size */
+typedef int ALsizei;
+
+/** enumerated 32-bit value */
+typedef int ALenum;
+
+/** 32-bit IEEE754 floating-point */
+typedef float ALfloat;
+
+/** 64-bit IEEE754 floating-point */
+typedef double ALdouble;
+
+#ifdef OPENAL_FIXED_POINT
+/* Apportable tries to define int64_t and int32_t if it thinks it is needed.
+ * But this is breaking in a complex project involving both pure C and C++
+ * something is triggering redefinition errors. The workaround seems to be just using stdint.h.
+ */
+#include <stdint.h>
+/** Types and Macros for fixed-point math */
+#ifndef INT64_MAX
+typedef long long int64_t;
+#define INT64_MAX 9223372036854775807LL
+
+#endif
+#ifndef INT32_MAX
+typedef int int32_t;
+#define INT32_MAX 2147483647
+#endif
+
+// FIXME(apportable) make this int32_t
+typedef int64_t ALfp;
+typedef int64_t ALdfp;
+
+#define ONE (1<<OPENAL_FIXED_POINT_SHIFT)
+#define TWO (2<<OPENAL_FIXED_POINT_SHIFT)
+
+#define float2ALfp(x) ((ALfp)((x) * (1<<OPENAL_FIXED_POINT_SHIFT) + ((x)>=0 ? 0.5 : -0.5)))
+#define ALfp2float(x) ((float)(x) / (1<<OPENAL_FIXED_POINT_SHIFT))
+
+#define double2ALdfp(x) ((ALdfp)((x) * (1<<OPENAL_FIXED_POINT_SHIFT) + ((x)>=0 ? 0.5 : -0.5)))
+#define ALdfp2double(x) ((double)(x) / (1<<OPENAL_FIXED_POINT_SHIFT))
+
+#define int2ALfp(x) ((ALfp)(x) << OPENAL_FIXED_POINT_SHIFT)
+#define ALfp2int(x) ((ALint)((x) >> OPENAL_FIXED_POINT_SHIFT))
+
+#define int2ALdfp(x) ((ALdfp)(x) << OPENAL_FIXED_POINT_SHIFT)
+#define ALdfp2int(x) ((ALint)((x) >> OPENAL_FIXED_POINT_SHIFT))
+
+#define ALfpMult(x,y) ((ALfp)((((int64_t)(x))*((int64_t)(y)))>>OPENAL_FIXED_POINT_SHIFT))
+#define ALfpDiv(x,y) ((ALfp)(((int64_t)(x) << OPENAL_FIXED_POINT_SHIFT) / (y)))
+
+#define ALdfpMult(x,y) ALfpMult(x,y)
+#define ALdfpDiv(x,y) ALfpDiv(x,y)
+
+#define __isnan(x) (0)
+#define __cos(x) (float2ALfp(cos(ALfp2float(x))))
+#define __sin(x) (float2ALfp(sin(ALfp2float(x))))
+#define __log10(x) (float2ALfp(log10(ALfp2float(x))))
+#define __atan(x) (float2ALfp(atan(ALfp2float(x))))
+
+#define toALfpConst(x) ((x)*(1<<OPENAL_FIXED_POINT_SHIFT))
+
+#else
+typedef float ALfp;
+typedef double ALdfp;
+
+#define float2ALfp(x) (x)
+#define ALfp2float(x) (x)
+
+#define double2ALdfp(x) (x)
+#define ALdfp2double(x) (x)
+
+#define int2ALfp(x) ((ALfp)(x))
+#define ALfp2int(x) ((ALint)(x))
+
+#define int2ALdfp(x) ((ALdfp)(x))
+#define ALdfp2int(x) ((ALint)(x))
+
+#define ALfpMult(x,y) ((x)*(y))
+#define ALfpDiv(x,y) ((x)/(y))
+
+#define ALdfpMult(x,y) ALfpMult((x),(y))
+#define ALdfpDiv(x,y) ALfpDiv((x),(y))
+
+#define __isnan(x) (0)
+#define __cos(x) cos((x))
+#define __sin(x) sin((x))
+#define __log10(x) log10((x))
+#define __atan(x) atan((x))
+
+#define toALfpConst(x) (x)
+
+#endif
+
+/** void type (for opaque pointers only) */
+typedef void ALvoid;
+
+
+/* Enumerant values begin at column 50. No tabs. */
+
+/* "no distance model" or "no buffer" */
+#define AL_NONE 0
+
+/* Boolean False. */
+#define AL_FALSE 0
+
+/** Boolean True. */
+#define AL_TRUE 1
+
+/** Indicate Source has relative coordinates. */
+#define AL_SOURCE_RELATIVE 0x202
+
+
+
+/**
+ * Directional source, inner cone angle, in degrees.
+ * Range: [0-360]
+ * Default: 360
+ */
+#define AL_CONE_INNER_ANGLE 0x1001
+
+/**
+ * Directional source, outer cone angle, in degrees.
+ * Range: [0-360]
+ * Default: 360
+ */
+#define AL_CONE_OUTER_ANGLE 0x1002
+
+/**
+ * Specify the pitch to be applied at source.
+ * Range: [0.5-2.0]
+ * Default: 1.0
+ */
+#define AL_PITCH 0x1003
+
+/**
+ * Specify the current location in three dimensional space.
+ * OpenAL, like OpenGL, uses a right handed coordinate system,
+ * where in a frontal default view X (thumb) points right,
+ * Y points up (index finger), and Z points towards the
+ * viewer/camera (middle finger).
+ * To switch from a left handed coordinate system, flip the
+ * sign on the Z coordinate.
+ * Listener position is always in the world coordinate system.
+ */
+#define AL_POSITION 0x1004
+
+/** Specify the current direction. */
+#define AL_DIRECTION 0x1005
+
+/** Specify the current velocity in three dimensional space. */
+#define AL_VELOCITY 0x1006
+
+/**
+ * Indicate whether source is looping.
+ * Type: ALboolean?
+ * Range: [AL_TRUE, AL_FALSE]
+ * Default: FALSE.
+ */
+#define AL_LOOPING 0x1007
+
+/**
+ * Indicate the buffer to provide sound samples.
+ * Type: ALuint.
+ * Range: any valid Buffer id.
+ */
+#define AL_BUFFER 0x1009
+
+/**
+ * Indicate the gain (volume amplification) applied.
+ * Type: ALfloat.
+ * Range: ]0.0- ]
+ * A value of 1.0 means un-attenuated/unchanged.
+ * Each division by 2 equals an attenuation of -6dB.
+ * Each multiplicaton with 2 equals an amplification of +6dB.
+ * A value of 0.0 is meaningless with respect to a logarithmic
+ * scale; it is interpreted as zero volume - the channel
+ * is effectively disabled.
+ */
+#define AL_GAIN 0x100A
+
+/*
+ * Indicate minimum source attenuation
+ * Type: ALfloat
+ * Range: [0.0 - 1.0]
+ *
+ * Logarthmic
+ */
+#define AL_MIN_GAIN 0x100D
+
+/**
+ * Indicate maximum source attenuation
+ * Type: ALfloat
+ * Range: [0.0 - 1.0]
+ *
+ * Logarthmic
+ */
+#define AL_MAX_GAIN 0x100E
+
+/**
+ * Indicate listener orientation.
+ *
+ * at/up
+ */
+#define AL_ORIENTATION 0x100F
+
+/**
+ * Source state information.
+ */
+#define AL_SOURCE_STATE 0x1010
+#define AL_INITIAL 0x1011
+#define AL_PLAYING 0x1012
+#define AL_PAUSED 0x1013
+#define AL_STOPPED 0x1014
+
+/**
+ * Buffer Queue params
+ */
+#define AL_BUFFERS_QUEUED 0x1015
+#define AL_BUFFERS_PROCESSED 0x1016
+
+/**
+ * Source buffer position information
+ */
+#define AL_SEC_OFFSET 0x1024
+#define AL_SAMPLE_OFFSET 0x1025
+#define AL_BYTE_OFFSET 0x1026
+
+/*
+ * Source type (Static, Streaming or undetermined)
+ * Source is Static if a Buffer has been attached using AL_BUFFER
+ * Source is Streaming if one or more Buffers have been attached using alSourceQueueBuffers
+ * Source is undetermined when it has the NULL buffer attached
+ */
+#define AL_SOURCE_TYPE 0x1027
+#define AL_STATIC 0x1028
+#define AL_STREAMING 0x1029
+#define AL_UNDETERMINED 0x1030
+
+/** Sound samples: format specifier. */
+#define AL_FORMAT_MONO8 0x1100
+#define AL_FORMAT_MONO16 0x1101
+#define AL_FORMAT_STEREO8 0x1102
+#define AL_FORMAT_STEREO16 0x1103
+
+/**
+ * source specific reference distance
+ * Type: ALfloat
+ * Range: 0.0 - +inf
+ *
+ * At 0.0, no distance attenuation occurs. Default is
+ * 1.0.
+ */
+#define AL_REFERENCE_DISTANCE 0x1020
+
+/**
+ * source specific rolloff factor
+ * Type: ALfloat
+ * Range: 0.0 - +inf
+ *
+ */
+#define AL_ROLLOFF_FACTOR 0x1021
+
+/**
+ * Directional source, outer cone gain.
+ *
+ * Default: 0.0
+ * Range: [0.0 - 1.0]
+ * Logarithmic
+ */
+#define AL_CONE_OUTER_GAIN 0x1022
+
+/**
+ * Indicate distance above which sources are not
+ * attenuated using the inverse clamped distance model.
+ *
+ * Default: +inf
+ * Type: ALfloat
+ * Range: 0.0 - +inf
+ */
+#define AL_MAX_DISTANCE 0x1023
+
+/**
+ * Sound samples: frequency, in units of Hertz [Hz].
+ * This is the number of samples per second. Half of the
+ * sample frequency marks the maximum significant
+ * frequency component.
+ */
+#define AL_FREQUENCY 0x2001
+#define AL_BITS 0x2002
+#define AL_CHANNELS 0x2003
+#define AL_SIZE 0x2004
+
+/**
+ * Buffer state.
+ *
+ * Not supported for public use (yet).
+ */
+#define AL_UNUSED 0x2010
+#define AL_PENDING 0x2011
+#define AL_PROCESSED 0x2012
+
+
+/** Errors: No Error. */
+#define AL_NO_ERROR AL_FALSE
+
+/**
+ * Invalid Name paramater passed to AL call.
+ */
+#define AL_INVALID_NAME 0xA001
+
+/**
+ * Invalid parameter passed to AL call.
+ */
+#define AL_INVALID_ENUM 0xA002
+
+/**
+ * Invalid enum parameter value.
+ */
+#define AL_INVALID_VALUE 0xA003
+
+/**
+ * Illegal call.
+ */
+#define AL_INVALID_OPERATION 0xA004
+
+
+/**
+ * No mojo.
+ */
+#define AL_OUT_OF_MEMORY 0xA005
+
+
+/** Context strings: Vendor Name. */
+#define AL_VENDOR 0xB001
+#define AL_VERSION 0xB002
+#define AL_RENDERER 0xB003
+#define AL_EXTENSIONS 0xB004
+
+/** Global tweakage. */
+
+/**
+ * Doppler scale. Default 1.0
+ */
+#define AL_DOPPLER_FACTOR 0xC000
+
+/**
+ * Tweaks speed of propagation.
+ */
+#define AL_DOPPLER_VELOCITY 0xC001
+
+/**
+ * Speed of Sound in units per second
+ */
+#define AL_SPEED_OF_SOUND 0xC003
+
+/**
+ * Distance models
+ *
+ * used in conjunction with DistanceModel
+ *
+ * implicit: NONE, which disances distance attenuation.
+ */
+#define AL_DISTANCE_MODEL 0xD000
+#define AL_INVERSE_DISTANCE 0xD001
+#define AL_INVERSE_DISTANCE_CLAMPED 0xD002
+#define AL_LINEAR_DISTANCE 0xD003
+#define AL_LINEAR_DISTANCE_CLAMPED 0xD004
+#define AL_EXPONENT_DISTANCE 0xD005
+#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006
+
+/**
+ * Priority
+ *
+ * Apportable Extension.
+ * Used to prevent dynamic throttling of this source.
+ *
+ */
+#define AL_PRIORITY 0xE001
+#define AL_PRIORITY_SLOTS 0xE002
+/*
+ * Renderer State management
+ */
+AL_API void AL_APIENTRY alEnable( ALenum capability );
+
+AL_API void AL_APIENTRY alDisable( ALenum capability );
+
+AL_API ALboolean AL_APIENTRY alIsEnabled( ALenum capability );
+
+
+/*
+ * State retrieval
+ */
+AL_API const ALchar* AL_APIENTRY alGetString( ALenum param );
+
+AL_API void AL_APIENTRY alGetBooleanv( ALenum param, ALboolean* data );
+
+AL_API void AL_APIENTRY alGetIntegerv( ALenum param, ALint* data );
+
+AL_API void AL_APIENTRY alGetFloatv( ALenum param, ALfloat* data );
+
+AL_API void AL_APIENTRY alGetDoublev( ALenum param, ALdouble* data );
+
+AL_API ALboolean AL_APIENTRY alGetBoolean( ALenum param );
+
+AL_API ALint AL_APIENTRY alGetInteger( ALenum param );
+
+AL_API ALfloat AL_APIENTRY alGetFloat( ALenum param );
+
+AL_API ALdouble AL_APIENTRY alGetDouble( ALenum param );
+
+
+/*
+ * Error support.
+ * Obtain the most recent error generated in the AL state machine.
+ */
+AL_API ALenum AL_APIENTRY alGetError( void );
+
+
+/*
+ * Extension support.
+ * Query for the presence of an extension, and obtain any appropriate
+ * function pointers and enum values.
+ */
+AL_API ALboolean AL_APIENTRY alIsExtensionPresent( const ALchar* extname );
+
+AL_API void* AL_APIENTRY alGetProcAddress( const ALchar* fname );
+
+AL_API ALenum AL_APIENTRY alGetEnumValue( const ALchar* ename );
+
+
+/*
+ * LISTENER
+ * Listener represents the location and orientation of the
+ * 'user' in 3D-space.
+ *
+ * Properties include: -
+ *
+ * Gain AL_GAIN ALfloat
+ * Position AL_POSITION ALfloat[3]
+ * Velocity AL_VELOCITY ALfloat[3]
+ * Orientation AL_ORIENTATION ALfloat[6] (Forward then Up vectors)
+*/
+
+/*
+ * Set Listener parameters
+ */
+AL_API void AL_APIENTRY alListenerf( ALenum param, ALfloat value );
+
+AL_API void AL_APIENTRY alListener3f( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 );
+
+AL_API void AL_APIENTRY alListenerfv( ALenum param, const ALfloat* values );
+
+AL_API void AL_APIENTRY alListeneri( ALenum param, ALint value );
+
+AL_API void AL_APIENTRY alListener3i( ALenum param, ALint value1, ALint value2, ALint value3 );
+
+AL_API void AL_APIENTRY alListeneriv( ALenum param, const ALint* values );
+
+/*
+ * Get Listener parameters
+ */
+AL_API void AL_APIENTRY alGetListenerf( ALenum param, ALfloat* value );
+
+AL_API void AL_APIENTRY alGetListener3f( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 );
+
+AL_API void AL_APIENTRY alGetListenerfv( ALenum param, ALfloat* values );
+
+AL_API void AL_APIENTRY alGetListeneri( ALenum param, ALint* value );
+
+AL_API void AL_APIENTRY alGetListener3i( ALenum param, ALint *value1, ALint *value2, ALint *value3 );
+
+AL_API void AL_APIENTRY alGetListeneriv( ALenum param, ALint* values );
+
+
+/**
+ * SOURCE
+ * Sources represent individual sound objects in 3D-space.
+ * Sources take the PCM data provided in the specified Buffer,
+ * apply Source-specific modifications, and then
+ * submit them to be mixed according to spatial arrangement etc.
+ *
+ * Properties include: -
+ *
+ * Gain AL_GAIN ALfloat
+ * Min Gain AL_MIN_GAIN ALfloat
+ * Max Gain AL_MAX_GAIN ALfloat
+ * Position AL_POSITION ALfloat[3]
+ * Velocity AL_VELOCITY ALfloat[3]
+ * Direction AL_DIRECTION ALfloat[3]
+ * Head Relative Mode AL_SOURCE_RELATIVE ALint (AL_TRUE or AL_FALSE)
+ * Reference Distance AL_REFERENCE_DISTANCE ALfloat
+ * Max Distance AL_MAX_DISTANCE ALfloat
+ * RollOff Factor AL_ROLLOFF_FACTOR ALfloat
+ * Inner Angle AL_CONE_INNER_ANGLE ALint or ALfloat
+ * Outer Angle AL_CONE_OUTER_ANGLE ALint or ALfloat
+ * Cone Outer Gain AL_CONE_OUTER_GAIN ALint or ALfloat
+ * Pitch AL_PITCH ALfloat
+ * Looping AL_LOOPING ALint (AL_TRUE or AL_FALSE)
+ * MS Offset AL_MSEC_OFFSET ALint or ALfloat
+ * Byte Offset AL_BYTE_OFFSET ALint or ALfloat
+ * Sample Offset AL_SAMPLE_OFFSET ALint or ALfloat
+ * Attached Buffer AL_BUFFER ALint
+ * State (Query only) AL_SOURCE_STATE ALint
+ * Buffers Queued (Query only) AL_BUFFERS_QUEUED ALint
+ * Buffers Processed (Query only) AL_BUFFERS_PROCESSED ALint
+ */
+
+/* Create Source objects */
+AL_API void AL_APIENTRY alGenSources( ALsizei n, ALuint* sources );
+
+/* Delete Source objects */
+AL_API void AL_APIENTRY alDeleteSources( ALsizei n, const ALuint* sources );
+
+/* Verify a handle is a valid Source */
+AL_API ALboolean AL_APIENTRY alIsSource( ALuint sid );
+
+/*
+ * Set Source parameters
+ */
+AL_API void AL_APIENTRY alSourcef( ALuint sid, ALenum param, ALfloat value );
+
+AL_API void AL_APIENTRY alSource3f( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 );
+
+AL_API void AL_APIENTRY alSourcefv( ALuint sid, ALenum param, const ALfloat* values );
+
+AL_API void AL_APIENTRY alSourcei( ALuint sid, ALenum param, ALint value );
+
+AL_API void AL_APIENTRY alSource3i( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 );
+
+AL_API void AL_APIENTRY alSourceiv( ALuint sid, ALenum param, const ALint* values );
+
+/*
+ * Get Source parameters
+ */
+AL_API void AL_APIENTRY alGetSourcef( ALuint sid, ALenum param, ALfloat* value );
+
+AL_API void AL_APIENTRY alGetSource3f( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3);
+
+AL_API void AL_APIENTRY alGetSourcefv( ALuint sid, ALenum param, ALfloat* values );
+
+AL_API void AL_APIENTRY alGetSourcei( ALuint sid, ALenum param, ALint* value );
+
+AL_API void AL_APIENTRY alGetSource3i( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3);
+
+AL_API void AL_APIENTRY alGetSourceiv( ALuint sid, ALenum param, ALint* values );
+
+
+/*
+ * Source vector based playback calls
+ */
+
+/* Play, replay, or resume (if paused) a list of Sources */
+AL_API void AL_APIENTRY alSourcePlayv( ALsizei ns, const ALuint *sids );
+
+/* Stop a list of Sources */
+AL_API void AL_APIENTRY alSourceStopv( ALsizei ns, const ALuint *sids );
+
+/* Rewind a list of Sources */
+AL_API void AL_APIENTRY alSourceRewindv( ALsizei ns, const ALuint *sids );
+
+/* Pause a list of Sources */
+AL_API void AL_APIENTRY alSourcePausev( ALsizei ns, const ALuint *sids );
+
+/*
+ * Source based playback calls
+ */
+
+/* Play, replay, or resume a Source */
+AL_API void AL_APIENTRY alSourcePlay( ALuint sid );
+
+/* Stop a Source */
+AL_API void AL_APIENTRY alSourceStop( ALuint sid );
+
+/* Rewind a Source (set playback postiton to beginning) */
+AL_API void AL_APIENTRY alSourceRewind( ALuint sid );
+
+/* Pause a Source */
+AL_API void AL_APIENTRY alSourcePause( ALuint sid );
+
+/*
+ * Source Queuing
+ */
+AL_API void AL_APIENTRY alSourceQueueBuffers( ALuint sid, ALsizei numEntries, const ALuint *bids );
+
+AL_API void AL_APIENTRY alSourceUnqueueBuffers( ALuint sid, ALsizei numEntries, ALuint *bids );
+
+
+/**
+ * BUFFER
+ * Buffer objects are storage space for sample data.
+ * Buffers are referred to by Sources. One Buffer can be used
+ * by multiple Sources.
+ *
+ * Properties include: -
+ *
+ * Frequency (Query only) AL_FREQUENCY ALint
+ * Size (Query only) AL_SIZE ALint
+ * Bits (Query only) AL_BITS ALint
+ * Channels (Query only) AL_CHANNELS ALint
+ */
+
+/* Create Buffer objects */
+AL_API void AL_APIENTRY alGenBuffers( ALsizei n, ALuint* buffers );
+
+/* Delete Buffer objects */
+AL_API void AL_APIENTRY alDeleteBuffers( ALsizei n, const ALuint* buffers );
+
+/* Verify a handle is a valid Buffer */
+AL_API ALboolean AL_APIENTRY alIsBuffer( ALuint bid );
+
+/* Specify the data to be copied into a buffer */
+AL_API void AL_APIENTRY alBufferData( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq );
+
+/*
+ * Set Buffer parameters
+ */
+AL_API void AL_APIENTRY alBufferf( ALuint bid, ALenum param, ALfloat value );
+
+AL_API void AL_APIENTRY alBuffer3f( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 );
+
+AL_API void AL_APIENTRY alBufferfv( ALuint bid, ALenum param, const ALfloat* values );
+
+AL_API void AL_APIENTRY alBufferi( ALuint bid, ALenum param, ALint value );
+
+AL_API void AL_APIENTRY alBuffer3i( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 );
+
+AL_API void AL_APIENTRY alBufferiv( ALuint bid, ALenum param, const ALint* values );
+
+/*
+ * Get Buffer parameters
+ */
+AL_API void AL_APIENTRY alGetBufferf( ALuint bid, ALenum param, ALfloat* value );
+
+AL_API void AL_APIENTRY alGetBuffer3f( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3);
+
+AL_API void AL_APIENTRY alGetBufferfv( ALuint bid, ALenum param, ALfloat* values );
+
+AL_API void AL_APIENTRY alGetBufferi( ALuint bid, ALenum param, ALint* value );
+
+AL_API void AL_APIENTRY alGetBuffer3i( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3);
+
+AL_API void AL_APIENTRY alGetBufferiv( ALuint bid, ALenum param, ALint* values );
+
+
+/*
+ * Global Parameters
+ */
+AL_API void AL_APIENTRY alDopplerFactor( ALfloat value );
+
+AL_API void AL_APIENTRY alDopplerVelocity( ALfloat value );
+
+AL_API void AL_APIENTRY alSpeedOfSound( ALfloat value );
+
+AL_API void AL_APIENTRY alDistanceModel( ALenum distanceModel );
+
+/*
+ * Pointer-to-function types, useful for dynamically getting AL entry points.
+ */
+typedef void (AL_APIENTRY *LPALENABLE)( ALenum capability );
+typedef void (AL_APIENTRY *LPALDISABLE)( ALenum capability );
+typedef ALboolean (AL_APIENTRY *LPALISENABLED)( ALenum capability );
+typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)( ALenum param );
+typedef void (AL_APIENTRY *LPALGETBOOLEANV)( ALenum param, ALboolean* data );
+typedef void (AL_APIENTRY *LPALGETINTEGERV)( ALenum param, ALint* data );
+typedef void (AL_APIENTRY *LPALGETFLOATV)( ALenum param, ALfloat* data );
+typedef void (AL_APIENTRY *LPALGETDOUBLEV)( ALenum param, ALdouble* data );
+typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)( ALenum param );
+typedef ALint (AL_APIENTRY *LPALGETINTEGER)( ALenum param );
+typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)( ALenum param );
+typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)( ALenum param );
+typedef ALenum (AL_APIENTRY *LPALGETERROR)( void );
+typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar* extname );
+typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)( const ALchar* fname );
+typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)( const ALchar* ename );
+typedef void (AL_APIENTRY *LPALLISTENERF)( ALenum param, ALfloat value );
+typedef void (AL_APIENTRY *LPALLISTENER3F)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 );
+typedef void (AL_APIENTRY *LPALLISTENERFV)( ALenum param, const ALfloat* values );
+typedef void (AL_APIENTRY *LPALLISTENERI)( ALenum param, ALint value );
+typedef void (AL_APIENTRY *LPALLISTENER3I)( ALenum param, ALint value1, ALint value2, ALint value3 );
+typedef void (AL_APIENTRY *LPALLISTENERIV)( ALenum param, const ALint* values );
+typedef void (AL_APIENTRY *LPALGETLISTENERF)( ALenum param, ALfloat* value );
+typedef void (AL_APIENTRY *LPALGETLISTENER3F)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 );
+typedef void (AL_APIENTRY *LPALGETLISTENERFV)( ALenum param, ALfloat* values );
+typedef void (AL_APIENTRY *LPALGETLISTENERI)( ALenum param, ALint* value );
+typedef void (AL_APIENTRY *LPALGETLISTENER3I)( ALenum param, ALint *value1, ALint *value2, ALint *value3 );
+typedef void (AL_APIENTRY *LPALGETLISTENERIV)( ALenum param, ALint* values );
+typedef void (AL_APIENTRY *LPALGENSOURCES)( ALsizei n, ALuint* sources );
+typedef void (AL_APIENTRY *LPALDELETESOURCES)( ALsizei n, const ALuint* sources );
+typedef ALboolean (AL_APIENTRY *LPALISSOURCE)( ALuint sid );
+typedef void (AL_APIENTRY *LPALSOURCEF)( ALuint sid, ALenum param, ALfloat value);
+typedef void (AL_APIENTRY *LPALSOURCE3F)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 );
+typedef void (AL_APIENTRY *LPALSOURCEFV)( ALuint sid, ALenum param, const ALfloat* values );
+typedef void (AL_APIENTRY *LPALSOURCEI)( ALuint sid, ALenum param, ALint value);
+typedef void (AL_APIENTRY *LPALSOURCE3I)( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 );
+typedef void (AL_APIENTRY *LPALSOURCEIV)( ALuint sid, ALenum param, const ALint* values );
+typedef void (AL_APIENTRY *LPALGETSOURCEF)( ALuint sid, ALenum param, ALfloat* value );
+typedef void (AL_APIENTRY *LPALGETSOURCE3F)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3);
+typedef void (AL_APIENTRY *LPALGETSOURCEFV)( ALuint sid, ALenum param, ALfloat* values );
+typedef void (AL_APIENTRY *LPALGETSOURCEI)( ALuint sid, ALenum param, ALint* value );
+typedef void (AL_APIENTRY *LPALGETSOURCE3I)( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3);
+typedef void (AL_APIENTRY *LPALGETSOURCEIV)( ALuint sid, ALenum param, ALint* values );
+typedef void (AL_APIENTRY *LPALSOURCEPLAYV)( ALsizei ns, const ALuint *sids );
+typedef void (AL_APIENTRY *LPALSOURCESTOPV)( ALsizei ns, const ALuint *sids );
+typedef void (AL_APIENTRY *LPALSOURCEREWINDV)( ALsizei ns, const ALuint *sids );
+typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)( ALsizei ns, const ALuint *sids );
+typedef void (AL_APIENTRY *LPALSOURCEPLAY)( ALuint sid );
+typedef void (AL_APIENTRY *LPALSOURCESTOP)( ALuint sid );
+typedef void (AL_APIENTRY *LPALSOURCEREWIND)( ALuint sid );
+typedef void (AL_APIENTRY *LPALSOURCEPAUSE)( ALuint sid );
+typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, const ALuint *bids );
+typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, ALuint *bids );
+typedef void (AL_APIENTRY *LPALGENBUFFERS)( ALsizei n, ALuint* buffers );
+typedef void (AL_APIENTRY *LPALDELETEBUFFERS)( ALsizei n, const ALuint* buffers );
+typedef ALboolean (AL_APIENTRY *LPALISBUFFER)( ALuint bid );
+typedef void (AL_APIENTRY *LPALBUFFERDATA)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq );
+typedef void (AL_APIENTRY *LPALBUFFERF)( ALuint bid, ALenum param, ALfloat value);
+typedef void (AL_APIENTRY *LPALBUFFER3F)( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 );
+typedef void (AL_APIENTRY *LPALBUFFERFV)( ALuint bid, ALenum param, const ALfloat* values );
+typedef void (AL_APIENTRY *LPALBUFFERI)( ALuint bid, ALenum param, ALint value);
+typedef void (AL_APIENTRY *LPALBUFFER3I)( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 );
+typedef void (AL_APIENTRY *LPALBUFFERIV)( ALuint bid, ALenum param, const ALint* values );
+typedef void (AL_APIENTRY *LPALGETBUFFERF)( ALuint bid, ALenum param, ALfloat* value );
+typedef void (AL_APIENTRY *LPALGETBUFFER3F)( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3);
+typedef void (AL_APIENTRY *LPALGETBUFFERFV)( ALuint bid, ALenum param, ALfloat* values );
+typedef void (AL_APIENTRY *LPALGETBUFFERI)( ALuint bid, ALenum param, ALint* value );
+typedef void (AL_APIENTRY *LPALGETBUFFER3I)( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3);
+typedef void (AL_APIENTRY *LPALGETBUFFERIV)( ALuint bid, ALenum param, ALint* values );
+typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)( ALfloat value );
+typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)( ALfloat value );
+typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)( ALfloat value );
+typedef void (AL_APIENTRY *LPALDISTANCEMODEL)( ALenum distanceModel );
+
+#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
+ #pragma export off
+#endif
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif /* AL_AL_H */
diff --git a/src/android/jni/include/AL/alc.h b/src/android/jni/include/AL/alc.h
new file mode 100644
index 00000000..43b2ef5f
--- /dev/null
+++ b/src/android/jni/include/AL/alc.h
@@ -0,0 +1,285 @@
+#ifndef AL_ALC_H
+#define AL_ALC_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(AL_LIBTYPE_STATIC)
+ #define ALC_API
+#elif defined(_WIN32) && !defined(_XBOX)
+ #if defined(AL_BUILD_LIBRARY)
+ #define ALC_API __declspec(dllexport)
+ #else
+ #define ALC_API __declspec(dllimport)
+ #endif
+#else
+ #if defined(AL_BUILD_LIBRARY) && defined(HAVE_GCC_VISIBILITY)
+ #define ALC_API __attribute__((visibility("protected")))
+ #else
+ #define ALC_API extern
+ #endif
+#endif
+
+#if defined(_WIN32)
+ #define ALC_APIENTRY __cdecl
+#else
+ #define ALC_APIENTRY
+#endif
+
+#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
+ #pragma export on
+#endif
+
+/*
+ * The ALCAPI, ALCAPIENTRY, and ALC_INVALID macros are deprecated, but are
+ * included for applications porting code from AL 1.0
+ */
+#define ALCAPI ALC_API
+#define ALCAPIENTRY ALC_APIENTRY
+#define ALC_INVALID 0
+
+
+#define ALC_VERSION_0_1 1
+
+typedef struct ALCdevice_struct ALCdevice;
+typedef struct ALCcontext_struct ALCcontext;
+
+
+/** 8-bit boolean */
+typedef char ALCboolean;
+
+/** character */
+typedef char ALCchar;
+
+/** signed 8-bit 2's complement integer */
+typedef signed char ALCbyte;
+
+/** unsigned 8-bit integer */
+typedef unsigned char ALCubyte;
+
+/** signed 16-bit 2's complement integer */
+typedef short ALCshort;
+
+/** unsigned 16-bit integer */
+typedef unsigned short ALCushort;
+
+/** signed 32-bit 2's complement integer */
+typedef int ALCint;
+
+/** unsigned 32-bit integer */
+typedef unsigned int ALCuint;
+
+/** non-negative 32-bit binary integer size */
+typedef int ALCsizei;
+
+/** enumerated 32-bit value */
+typedef int ALCenum;
+
+/** 32-bit IEEE754 floating-point */
+typedef float ALCfloat;
+
+/** 64-bit IEEE754 floating-point */
+typedef double ALCdouble;
+
+/** void type (for opaque pointers only) */
+typedef void ALCvoid;
+
+
+/* Enumerant values begin at column 50. No tabs. */
+
+/* Boolean False. */
+#define ALC_FALSE 0
+
+/* Boolean True. */
+#define ALC_TRUE 1
+
+/**
+ * followed by <int> Hz
+ */
+#define ALC_FREQUENCY 0x1007
+
+/**
+ * followed by <int> Hz
+ */
+#define ALC_REFRESH 0x1008
+
+/**
+ * followed by AL_TRUE, AL_FALSE
+ */
+#define ALC_SYNC 0x1009
+
+/**
+ * followed by <int> Num of requested Mono (3D) Sources
+ */
+#define ALC_MONO_SOURCES 0x1010
+
+/**
+ * followed by <int> Num of requested Stereo Sources
+ */
+#define ALC_STEREO_SOURCES 0x1011
+
+/**
+ * errors
+ */
+
+/**
+ * No error
+ */
+#define ALC_NO_ERROR ALC_FALSE
+
+/**
+ * No device
+ */
+#define ALC_INVALID_DEVICE 0xA001
+
+/**
+ * invalid context ID
+ */
+#define ALC_INVALID_CONTEXT 0xA002
+
+/**
+ * bad enum
+ */
+#define ALC_INVALID_ENUM 0xA003
+
+/**
+ * bad value
+ */
+#define ALC_INVALID_VALUE 0xA004
+
+/**
+ * Out of memory.
+ */
+#define ALC_OUT_OF_MEMORY 0xA005
+
+
+/**
+ * The Specifier string for default device
+ */
+#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004
+#define ALC_DEVICE_SPECIFIER 0x1005
+#define ALC_EXTENSIONS 0x1006
+
+#define ALC_MAJOR_VERSION 0x1000
+#define ALC_MINOR_VERSION 0x1001
+
+#define ALC_ATTRIBUTES_SIZE 0x1002
+#define ALC_ALL_ATTRIBUTES 0x1003
+
+
+/**
+ * Capture extension
+ */
+#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310
+#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311
+#define ALC_CAPTURE_SAMPLES 0x312
+
+
+/*
+ * Context Management
+ */
+ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist );
+
+ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context );
+
+ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context );
+
+ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context );
+
+ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context );
+
+ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( void );
+
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context );
+
+
+/*
+ * Device Management
+ */
+ALC_API ALCdevice * ALC_APIENTRY alcOpenDevice( const ALCchar *devicename );
+
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice( ALCdevice *device );
+
+
+/*
+ * Error support.
+ * Obtain the most recent Context error
+ */
+ALC_API ALCenum ALC_APIENTRY alcGetError( ALCdevice *device );
+
+
+/*
+ * Extension support.
+ * Query for the presence of an extension, and obtain any appropriate
+ * function pointers and enum values.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname );
+
+ALC_API void * ALC_APIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname );
+
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname );
+
+
+/*
+ * Query functions
+ */
+ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param );
+
+ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data );
+
+
+/*
+ * Capture functions
+ */
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize );
+
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice( ALCdevice *device );
+
+ALC_API void ALC_APIENTRY alcCaptureStart( ALCdevice *device );
+
+ALC_API void ALC_APIENTRY alcCaptureStop( ALCdevice *device );
+
+ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples );
+
+/*
+ * Pointer-to-function types, useful for dynamically getting ALC entry points.
+ */
+typedef ALCcontext * (ALC_APIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist);
+typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context );
+typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context );
+typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context );
+typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context );
+typedef ALCcontext * (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)( void );
+typedef ALCdevice * (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context );
+typedef ALCdevice * (ALC_APIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename );
+typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device );
+typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)( ALCdevice *device );
+typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname );
+typedef void * (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname );
+typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname );
+typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param );
+typedef void (ALC_APIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest );
+typedef ALCdevice * (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize );
+typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)( ALCdevice *device );
+typedef void (ALC_APIENTRY *LPALCCAPTURESTART)( ALCdevice *device );
+typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)( ALCdevice *device );
+typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples );
+
+#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
+ #pragma export off
+#endif
+
+#if defined(ANDROID)
+/*
+ * OpenAL extension for suspend/resume of audio throughout application lifecycle
+ */
+ALC_API void ALC_APIENTRY alcSuspend( void );
+ALC_API void ALC_APIENTRY alcResume( void );
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* AL_ALC_H */
diff --git a/src/android/jni/include/AL/alext.h b/src/android/jni/include/AL/alext.h
new file mode 100644
index 00000000..f3c7bcae
--- /dev/null
+++ b/src/android/jni/include/AL/alext.h
@@ -0,0 +1,165 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2008 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef AL_ALEXT_H
+#define AL_ALEXT_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef AL_LOKI_IMA_ADPCM_format
+#define AL_LOKI_IMA_ADPCM_format 1
+#define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000
+#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT 0x10001
+#endif
+
+#ifndef AL_LOKI_WAVE_format
+#define AL_LOKI_WAVE_format 1
+#define AL_FORMAT_WAVE_EXT 0x10002
+#endif
+
+#ifndef AL_EXT_vorbis
+#define AL_EXT_vorbis 1
+#define AL_FORMAT_VORBIS_EXT 0x10003
+#endif
+
+#ifndef AL_LOKI_quadriphonic
+#define AL_LOKI_quadriphonic 1
+#define AL_FORMAT_QUAD8_LOKI 0x10004
+#define AL_FORMAT_QUAD16_LOKI 0x10005
+#endif
+
+#ifndef AL_EXT_float32
+#define AL_EXT_float32 1
+#define AL_FORMAT_MONO_FLOAT32 0x10010
+#define AL_FORMAT_STEREO_FLOAT32 0x10011
+#endif
+
+#ifndef AL_EXT_double
+#define AL_EXT_double 1
+#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012
+#define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013
+#endif
+
+#ifndef ALC_LOKI_audio_channel
+#define ALC_LOKI_audio_channel 1
+#define ALC_CHAN_MAIN_LOKI 0x500001
+#define ALC_CHAN_PCM_LOKI 0x500002
+#define ALC_CHAN_CD_LOKI 0x500003
+#endif
+
+#ifndef ALC_ENUMERATE_ALL_EXT
+#define ALC_ENUMERATE_ALL_EXT 1
+#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012
+#define ALC_ALL_DEVICES_SPECIFIER 0x1013
+#endif
+
+#ifndef AL_EXT_MCFORMATS
+#define AL_EXT_MCFORMATS 1
+#define AL_FORMAT_QUAD8 0x1204
+#define AL_FORMAT_QUAD16 0x1205
+#define AL_FORMAT_QUAD32 0x1206
+#define AL_FORMAT_REAR8 0x1207
+#define AL_FORMAT_REAR16 0x1208
+#define AL_FORMAT_REAR32 0x1209
+#define AL_FORMAT_51CHN8 0x120A
+#define AL_FORMAT_51CHN16 0x120B
+#define AL_FORMAT_51CHN32 0x120C
+#define AL_FORMAT_61CHN8 0x120D
+#define AL_FORMAT_61CHN16 0x120E
+#define AL_FORMAT_61CHN32 0x120F
+#define AL_FORMAT_71CHN8 0x1210
+#define AL_FORMAT_71CHN16 0x1211
+#define AL_FORMAT_71CHN32 0x1212
+#endif
+
+#ifndef AL_EXT_MULAW_MCFORMATS
+#define AL_EXT_MULAW_MCFORMATS 1
+#define AL_FORMAT_MONO_MULAW 0x10014
+#define AL_FORMAT_STEREO_MULAW 0x10015
+#define AL_FORMAT_QUAD_MULAW 0x10021
+#define AL_FORMAT_REAR_MULAW 0x10022
+#define AL_FORMAT_51CHN_MULAW 0x10023
+#define AL_FORMAT_61CHN_MULAW 0x10024
+#define AL_FORMAT_71CHN_MULAW 0x10025
+#endif
+
+#ifndef AL_EXT_IMA4
+#define AL_EXT_IMA4 1
+#define AL_FORMAT_MONO_IMA4 0x1300
+#define AL_FORMAT_STEREO_IMA4 0x1301
+#endif
+
+#ifndef AL_EXT_STATIC_BUFFER
+#define AL_EXT_STATIC_BUFFER 1
+typedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq);
+#endif
+#endif
+
+#ifndef ALC_EXT_EFX
+#define ALC_EXT_EFX 1
+#include "efx.h"
+#endif
+
+#ifndef ALC_EXT_disconnect
+#define ALC_EXT_disconnect 1
+#define ALC_CONNECTED 0x313
+#endif
+
+#ifndef ALC_EXT_thread_local_context
+#define ALC_EXT_thread_local_context 1
+typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context);
+typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context);
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);
+#endif
+#endif
+
+#ifndef AL_EXT_source_distance_model
+#define AL_EXT_source_distance_model 1
+#define AL_SOURCE_DISTANCE_MODEL 0x200
+#endif
+
+#ifndef AL_SOFT_buffer_sub_data
+#define AL_SOFT_buffer_sub_data 1
+#define AL_BYTE_RW_OFFSETS_SOFT 0x1031
+#define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032
+typedef ALvoid (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length);
+#endif
+#endif
+
+#ifndef AL_SOFT_loop_points
+#define AL_SOFT_loop_points 1
+#define AL_LOOP_POINTS_SOFT 0x2015
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/android/jni/include/AL/efx-creative.h b/src/android/jni/include/AL/efx-creative.h
new file mode 100644
index 00000000..0a04c982
--- /dev/null
+++ b/src/android/jni/include/AL/efx-creative.h
@@ -0,0 +1,3 @@
+/* The tokens that would be defined here are already defined in efx.h. This
+ * empty file is here to provide compatibility with Windows-based projects
+ * that would include it. */
diff --git a/src/android/jni/include/AL/efx.h b/src/android/jni/include/AL/efx.h
new file mode 100644
index 00000000..0ccef95d
--- /dev/null
+++ b/src/android/jni/include/AL/efx.h
@@ -0,0 +1,758 @@
+#ifndef AL_EFX_H
+#define AL_EFX_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALC_EXT_EFX_NAME "ALC_EXT_EFX"
+
+#define ALC_EFX_MAJOR_VERSION 0x20001
+#define ALC_EFX_MINOR_VERSION 0x20002
+#define ALC_MAX_AUXILIARY_SENDS 0x20003
+
+
+/* Listener properties. */
+#define AL_METERS_PER_UNIT 0x20004
+
+/* Source properties. */
+#define AL_DIRECT_FILTER 0x20005
+#define AL_AUXILIARY_SEND_FILTER 0x20006
+#define AL_AIR_ABSORPTION_FACTOR 0x20007
+#define AL_ROOM_ROLLOFF_FACTOR 0x20008
+#define AL_CONE_OUTER_GAINHF 0x20009
+#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A
+#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B
+#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C
+
+
+/* Effect properties. */
+
+/* Reverb effect parameters */
+#define AL_REVERB_DENSITY 0x0001
+#define AL_REVERB_DIFFUSION 0x0002
+#define AL_REVERB_GAIN 0x0003
+#define AL_REVERB_GAINHF 0x0004
+#define AL_REVERB_DECAY_TIME 0x0005
+#define AL_REVERB_DECAY_HFRATIO 0x0006
+#define AL_REVERB_REFLECTIONS_GAIN 0x0007
+#define AL_REVERB_REFLECTIONS_DELAY 0x0008
+#define AL_REVERB_LATE_REVERB_GAIN 0x0009
+#define AL_REVERB_LATE_REVERB_DELAY 0x000A
+#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B
+#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C
+#define AL_REVERB_DECAY_HFLIMIT 0x000D
+
+/* EAX Reverb effect parameters */
+#define AL_EAXREVERB_DENSITY 0x0001
+#define AL_EAXREVERB_DIFFUSION 0x0002
+#define AL_EAXREVERB_GAIN 0x0003
+#define AL_EAXREVERB_GAINHF 0x0004
+#define AL_EAXREVERB_GAINLF 0x0005
+#define AL_EAXREVERB_DECAY_TIME 0x0006
+#define AL_EAXREVERB_DECAY_HFRATIO 0x0007
+#define AL_EAXREVERB_DECAY_LFRATIO 0x0008
+#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009
+#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A
+#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B
+#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C
+#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D
+#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E
+#define AL_EAXREVERB_ECHO_TIME 0x000F
+#define AL_EAXREVERB_ECHO_DEPTH 0x0010
+#define AL_EAXREVERB_MODULATION_TIME 0x0011
+#define AL_EAXREVERB_MODULATION_DEPTH 0x0012
+#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013
+#define AL_EAXREVERB_HFREFERENCE 0x0014
+#define AL_EAXREVERB_LFREFERENCE 0x0015
+#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016
+#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017
+
+/* Chorus effect parameters */
+#define AL_CHORUS_WAVEFORM 0x0001
+#define AL_CHORUS_PHASE 0x0002
+#define AL_CHORUS_RATE 0x0003
+#define AL_CHORUS_DEPTH 0x0004
+#define AL_CHORUS_FEEDBACK 0x0005
+#define AL_CHORUS_DELAY 0x0006
+
+/* Distortion effect parameters */
+#define AL_DISTORTION_EDGE 0x0001
+#define AL_DISTORTION_GAIN 0x0002
+#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003
+#define AL_DISTORTION_EQCENTER 0x0004
+#define AL_DISTORTION_EQBANDWIDTH 0x0005
+
+/* Echo effect parameters */
+#define AL_ECHO_DELAY 0x0001
+#define AL_ECHO_LRDELAY 0x0002
+#define AL_ECHO_DAMPING 0x0003
+#define AL_ECHO_FEEDBACK 0x0004
+#define AL_ECHO_SPREAD 0x0005
+
+/* Flanger effect parameters */
+#define AL_FLANGER_WAVEFORM 0x0001
+#define AL_FLANGER_PHASE 0x0002
+#define AL_FLANGER_RATE 0x0003
+#define AL_FLANGER_DEPTH 0x0004
+#define AL_FLANGER_FEEDBACK 0x0005
+#define AL_FLANGER_DELAY 0x0006
+
+/* Frequency shifter effect parameters */
+#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001
+#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002
+#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003
+
+/* Vocal morpher effect parameters */
+#define AL_VOCAL_MORPHER_PHONEMEA 0x0001
+#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002
+#define AL_VOCAL_MORPHER_PHONEMEB 0x0003
+#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004
+#define AL_VOCAL_MORPHER_WAVEFORM 0x0005
+#define AL_VOCAL_MORPHER_RATE 0x0006
+
+/* Pitchshifter effect parameters */
+#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001
+#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002
+
+/* Ringmodulator effect parameters */
+#define AL_RING_MODULATOR_FREQUENCY 0x0001
+#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002
+#define AL_RING_MODULATOR_WAVEFORM 0x0003
+
+/* Autowah effect parameters */
+#define AL_AUTOWAH_ATTACK_TIME 0x0001
+#define AL_AUTOWAH_RELEASE_TIME 0x0002
+#define AL_AUTOWAH_RESONANCE 0x0003
+#define AL_AUTOWAH_PEAK_GAIN 0x0004
+
+/* Compressor effect parameters */
+#define AL_COMPRESSOR_ONOFF 0x0001
+
+/* Equalizer effect parameters */
+#define AL_EQUALIZER_LOW_GAIN 0x0001
+#define AL_EQUALIZER_LOW_CUTOFF 0x0002
+#define AL_EQUALIZER_MID1_GAIN 0x0003
+#define AL_EQUALIZER_MID1_CENTER 0x0004
+#define AL_EQUALIZER_MID1_WIDTH 0x0005
+#define AL_EQUALIZER_MID2_GAIN 0x0006
+#define AL_EQUALIZER_MID2_CENTER 0x0007
+#define AL_EQUALIZER_MID2_WIDTH 0x0008
+#define AL_EQUALIZER_HIGH_GAIN 0x0009
+#define AL_EQUALIZER_HIGH_CUTOFF 0x000A
+
+/* Effect type */
+#define AL_EFFECT_FIRST_PARAMETER 0x0000
+#define AL_EFFECT_LAST_PARAMETER 0x8000
+#define AL_EFFECT_TYPE 0x8001
+
+/* Effect types, used with the AL_EFFECT_TYPE property */
+#define AL_EFFECT_NULL 0x0000
+#define AL_EFFECT_REVERB 0x0001
+#define AL_EFFECT_CHORUS 0x0002
+#define AL_EFFECT_DISTORTION 0x0003
+#define AL_EFFECT_ECHO 0x0004
+#define AL_EFFECT_FLANGER 0x0005
+#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006
+#define AL_EFFECT_VOCAL_MORPHER 0x0007
+#define AL_EFFECT_PITCH_SHIFTER 0x0008
+#define AL_EFFECT_RING_MODULATOR 0x0009
+#define AL_EFFECT_AUTOWAH 0x000A
+#define AL_EFFECT_COMPRESSOR 0x000B
+#define AL_EFFECT_EQUALIZER 0x000C
+#define AL_EFFECT_EAXREVERB 0x8000
+
+/* Auxiliary Effect Slot properties. */
+#define AL_EFFECTSLOT_EFFECT 0x0001
+#define AL_EFFECTSLOT_GAIN 0x0002
+#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003
+
+/* NULL Auxiliary Slot ID to disable a source send. */
+#define AL_EFFECTSLOT_NULL 0x0000
+
+
+/* Filter properties. */
+
+/* Lowpass filter parameters */
+#define AL_LOWPASS_GAIN 0x0001
+#define AL_LOWPASS_GAINHF 0x0002
+
+/* Highpass filter parameters */
+#define AL_HIGHPASS_GAIN 0x0001
+#define AL_HIGHPASS_GAINLF 0x0002
+
+/* Bandpass filter parameters */
+#define AL_BANDPASS_GAIN 0x0001
+#define AL_BANDPASS_GAINLF 0x0002
+#define AL_BANDPASS_GAINHF 0x0003
+
+/* Filter type */
+#define AL_FILTER_FIRST_PARAMETER 0x0000
+#define AL_FILTER_LAST_PARAMETER 0x8000
+#define AL_FILTER_TYPE 0x8001
+
+/* Filter types, used with the AL_FILTER_TYPE property */
+#define AL_FILTER_NULL 0x0000
+#define AL_FILTER_LOWPASS 0x0001
+#define AL_FILTER_HIGHPASS 0x0002
+#define AL_FILTER_BANDPASS 0x0003
+
+
+/* Effect object function types. */
+typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint);
+typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*);
+
+/* Filter object function types. */
+typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint);
+typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*);
+
+/* Auxiliary Effect Slot object function types. */
+typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*);
+
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects);
+AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, ALuint *effects);
+AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect);
+AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
+
+AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters);
+AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, ALuint *filters);
+AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter);
+AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
+
+AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
+AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
+AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
+#endif
+
+/* Filter ranges and defaults. */
+
+/* Lowpass filter */
+#define LOWPASS_MIN_GAIN (0.0f)
+#define LOWPASS_MAX_GAIN (1.0f)
+#define LOWPASS_DEFAULT_GAIN (1.0f)
+
+#define LOWPASS_MIN_GAINHF (0.0f)
+#define LOWPASS_MAX_GAINHF (1.0f)
+#define LOWPASS_DEFAULT_GAINHF (1.0f)
+
+/* Highpass filter */
+#define HIGHPASS_MIN_GAIN (0.0f)
+#define HIGHPASS_MAX_GAIN (1.0f)
+#define HIGHPASS_DEFAULT_GAIN (1.0f)
+
+#define HIGHPASS_MIN_GAINLF (0.0f)
+#define HIGHPASS_MAX_GAINLF (1.0f)
+#define HIGHPASS_DEFAULT_GAINLF (1.0f)
+
+/* Bandpass filter */
+#define BANDPASS_MIN_GAIN (0.0f)
+#define BANDPASS_MAX_GAIN (1.0f)
+#define BANDPASS_DEFAULT_GAIN (1.0f)
+
+#define BANDPASS_MIN_GAINHF (0.0f)
+#define BANDPASS_MAX_GAINHF (1.0f)
+#define BANDPASS_DEFAULT_GAINHF (1.0f)
+
+#define BANDPASS_MIN_GAINLF (0.0f)
+#define BANDPASS_MAX_GAINLF (1.0f)
+#define BANDPASS_DEFAULT_GAINLF (1.0f)
+
+
+/* Effect parameter ranges and defaults. */
+
+/* Standard reverb effect */
+#define AL_REVERB_MIN_DENSITY (0.0f)
+#define AL_REVERB_MAX_DENSITY (1.0f)
+#define AL_REVERB_DEFAULT_DENSITY (1.0f)
+
+#define AL_REVERB_MIN_DIFFUSION (0.0f)
+#define AL_REVERB_MAX_DIFFUSION (1.0f)
+#define AL_REVERB_DEFAULT_DIFFUSION (1.0f)
+
+#define AL_REVERB_MIN_GAIN (0.0f)
+#define AL_REVERB_MAX_GAIN (1.0f)
+#define AL_REVERB_DEFAULT_GAIN (0.32f)
+
+#define AL_REVERB_MIN_GAINHF (0.0f)
+#define AL_REVERB_MAX_GAINHF (1.0f)
+#define AL_REVERB_DEFAULT_GAINHF (0.89f)
+
+#define AL_REVERB_MIN_DECAY_TIME (0.1f)
+#define AL_REVERB_MAX_DECAY_TIME (20.0f)
+#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f)
+
+#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f)
+#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f)
+#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f)
+
+#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f)
+#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f)
+#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
+
+#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f)
+#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f)
+#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
+
+#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f)
+#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f)
+#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
+
+#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f)
+#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f)
+#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
+
+#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
+#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
+#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
+
+#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
+#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
+#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+
+#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE
+#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE
+#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE
+
+/* EAX reverb effect */
+#define AL_EAXREVERB_MIN_DENSITY (0.0f)
+#define AL_EAXREVERB_MAX_DENSITY (1.0f)
+#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f)
+
+#define AL_EAXREVERB_MIN_DIFFUSION (0.0f)
+#define AL_EAXREVERB_MAX_DIFFUSION (1.0f)
+#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f)
+
+#define AL_EAXREVERB_MIN_GAIN (0.0f)
+#define AL_EAXREVERB_MAX_GAIN (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAIN (0.32f)
+
+#define AL_EAXREVERB_MIN_GAINHF (0.0f)
+#define AL_EAXREVERB_MAX_GAINHF (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f)
+
+#define AL_EAXREVERB_MIN_GAINLF (0.0f)
+#define AL_EAXREVERB_MAX_GAINLF (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f)
+
+#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f)
+
+#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f)
+
+#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f)
+
+#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f)
+#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f)
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
+
+#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f)
+#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f)
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
+
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
+
+#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f)
+#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f)
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
+
+#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f)
+#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f)
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
+
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
+
+#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f)
+#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f)
+#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f)
+
+#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f)
+#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f)
+#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f)
+
+#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f)
+#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f)
+#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f)
+
+#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f)
+#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f)
+#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f)
+
+#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
+#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
+#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
+
+#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f)
+#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f)
+#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f)
+
+#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f)
+#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f)
+#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f)
+
+#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
+#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
+#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+
+#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE
+#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE
+#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE
+
+/* Chorus effect */
+#define AL_CHORUS_WAVEFORM_SINUSOID (0)
+#define AL_CHORUS_WAVEFORM_TRIANGLE (1)
+
+#define AL_CHORUS_MIN_WAVEFORM (0)
+#define AL_CHORUS_MAX_WAVEFORM (1)
+#define AL_CHORUS_DEFAULT_WAVEFORM (1)
+
+#define AL_CHORUS_MIN_PHASE (-180)
+#define AL_CHORUS_MAX_PHASE (180)
+#define AL_CHORUS_DEFAULT_PHASE (90)
+
+#define AL_CHORUS_MIN_RATE (0.0f)
+#define AL_CHORUS_MAX_RATE (10.0f)
+#define AL_CHORUS_DEFAULT_RATE (1.1f)
+
+#define AL_CHORUS_MIN_DEPTH (0.0f)
+#define AL_CHORUS_MAX_DEPTH (1.0f)
+#define AL_CHORUS_DEFAULT_DEPTH (0.1f)
+
+#define AL_CHORUS_MIN_FEEDBACK (-1.0f)
+#define AL_CHORUS_MAX_FEEDBACK (1.0f)
+#define AL_CHORUS_DEFAULT_FEEDBACK (0.25f)
+
+#define AL_CHORUS_MIN_DELAY (0.0f)
+#define AL_CHORUS_MAX_DELAY (0.016f)
+#define AL_CHORUS_DEFAULT_DELAY (0.016f)
+
+/* Distortion effect */
+#define AL_DISTORTION_MIN_EDGE (0.0f)
+#define AL_DISTORTION_MAX_EDGE (1.0f)
+#define AL_DISTORTION_DEFAULT_EDGE (0.2f)
+
+#define AL_DISTORTION_MIN_GAIN (0.01f)
+#define AL_DISTORTION_MAX_GAIN (1.0f)
+#define AL_DISTORTION_DEFAULT_GAIN (0.05f)
+
+#define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f)
+#define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f)
+#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f)
+
+#define AL_DISTORTION_MIN_EQCENTER (80.0f)
+#define AL_DISTORTION_MAX_EQCENTER (24000.0f)
+#define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f)
+
+#define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f)
+#define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f)
+#define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f)
+
+/* Echo effect */
+#define AL_ECHO_MIN_DELAY (0.0f)
+#define AL_ECHO_MAX_DELAY (0.207f)
+#define AL_ECHO_DEFAULT_DELAY (0.1f)
+
+#define AL_ECHO_MIN_LRDELAY (0.0f)
+#define AL_ECHO_MAX_LRDELAY (0.404f)
+#define AL_ECHO_DEFAULT_LRDELAY (0.1f)
+
+#define AL_ECHO_MIN_DAMPING (0.0f)
+#define AL_ECHO_MAX_DAMPING (0.99f)
+#define AL_ECHO_DEFAULT_DAMPING (0.5f)
+
+#define AL_ECHO_MIN_FEEDBACK (0.0f)
+#define AL_ECHO_MAX_FEEDBACK (1.0f)
+#define AL_ECHO_DEFAULT_FEEDBACK (0.5f)
+
+#define AL_ECHO_MIN_SPREAD (-1.0f)
+#define AL_ECHO_MAX_SPREAD (1.0f)
+#define AL_ECHO_DEFAULT_SPREAD (-1.0f)
+
+/* Flanger effect */
+#define AL_FLANGER_WAVEFORM_SINUSOID (0)
+#define AL_FLANGER_WAVEFORM_TRIANGLE (1)
+
+#define AL_FLANGER_MIN_WAVEFORM (0)
+#define AL_FLANGER_MAX_WAVEFORM (1)
+#define AL_FLANGER_DEFAULT_WAVEFORM (1)
+
+#define AL_FLANGER_MIN_PHASE (-180)
+#define AL_FLANGER_MAX_PHASE (180)
+#define AL_FLANGER_DEFAULT_PHASE (0)
+
+#define AL_FLANGER_MIN_RATE (0.0f)
+#define AL_FLANGER_MAX_RATE (10.0f)
+#define AL_FLANGER_DEFAULT_RATE (0.27f)
+
+#define AL_FLANGER_MIN_DEPTH (0.0f)
+#define AL_FLANGER_MAX_DEPTH (1.0f)
+#define AL_FLANGER_DEFAULT_DEPTH (1.0f)
+
+#define AL_FLANGER_MIN_FEEDBACK (-1.0f)
+#define AL_FLANGER_MAX_FEEDBACK (1.0f)
+#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f)
+
+#define AL_FLANGER_MIN_DELAY (0.0f)
+#define AL_FLANGER_MAX_DELAY (0.004f)
+#define AL_FLANGER_DEFAULT_DELAY (0.002f)
+
+/* Frequency shifter effect */
+#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f)
+#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f)
+
+#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0)
+#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0)
+
+#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0)
+#define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1)
+#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2)
+
+#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0)
+#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0)
+
+/* Vocal morpher effect */
+#define AL_VOCAL_MORPHER_MIN_PHONEMEA (0)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEA (29)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEB (0)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEB (29)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0)
+
+#define AL_VOCAL_MORPHER_PHONEME_A (0)
+#define AL_VOCAL_MORPHER_PHONEME_E (1)
+#define AL_VOCAL_MORPHER_PHONEME_I (2)
+#define AL_VOCAL_MORPHER_PHONEME_O (3)
+#define AL_VOCAL_MORPHER_PHONEME_U (4)
+#define AL_VOCAL_MORPHER_PHONEME_AA (5)
+#define AL_VOCAL_MORPHER_PHONEME_AE (6)
+#define AL_VOCAL_MORPHER_PHONEME_AH (7)
+#define AL_VOCAL_MORPHER_PHONEME_AO (8)
+#define AL_VOCAL_MORPHER_PHONEME_EH (9)
+#define AL_VOCAL_MORPHER_PHONEME_ER (10)
+#define AL_VOCAL_MORPHER_PHONEME_IH (11)
+#define AL_VOCAL_MORPHER_PHONEME_IY (12)
+#define AL_VOCAL_MORPHER_PHONEME_UH (13)
+#define AL_VOCAL_MORPHER_PHONEME_UW (14)
+#define AL_VOCAL_MORPHER_PHONEME_B (15)
+#define AL_VOCAL_MORPHER_PHONEME_D (16)
+#define AL_VOCAL_MORPHER_PHONEME_F (17)
+#define AL_VOCAL_MORPHER_PHONEME_G (18)
+#define AL_VOCAL_MORPHER_PHONEME_J (19)
+#define AL_VOCAL_MORPHER_PHONEME_K (20)
+#define AL_VOCAL_MORPHER_PHONEME_L (21)
+#define AL_VOCAL_MORPHER_PHONEME_M (22)
+#define AL_VOCAL_MORPHER_PHONEME_N (23)
+#define AL_VOCAL_MORPHER_PHONEME_P (24)
+#define AL_VOCAL_MORPHER_PHONEME_R (25)
+#define AL_VOCAL_MORPHER_PHONEME_S (26)
+#define AL_VOCAL_MORPHER_PHONEME_T (27)
+#define AL_VOCAL_MORPHER_PHONEME_V (28)
+#define AL_VOCAL_MORPHER_PHONEME_Z (29)
+
+#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0)
+#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1)
+#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2)
+
+#define AL_VOCAL_MORPHER_MIN_WAVEFORM (0)
+#define AL_VOCAL_MORPHER_MAX_WAVEFORM (2)
+#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0)
+
+#define AL_VOCAL_MORPHER_MIN_RATE (0.0f)
+#define AL_VOCAL_MORPHER_MAX_RATE (10.0f)
+#define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f)
+
+/* Pitch shifter effect */
+#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12)
+#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12)
+#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12)
+
+#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50)
+#define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50)
+#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0)
+
+/* Ring modulator effect */
+#define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f)
+#define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f)
+#define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f)
+
+#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f)
+#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f)
+#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f)
+
+#define AL_RING_MODULATOR_SINUSOID (0)
+#define AL_RING_MODULATOR_SAWTOOTH (1)
+#define AL_RING_MODULATOR_SQUARE (2)
+
+#define AL_RING_MODULATOR_MIN_WAVEFORM (0)
+#define AL_RING_MODULATOR_MAX_WAVEFORM (2)
+#define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0)
+
+/* Autowah effect */
+#define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f)
+#define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f)
+#define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f)
+
+#define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f)
+#define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f)
+#define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f)
+
+#define AL_AUTOWAH_MIN_RESONANCE (2.0f)
+#define AL_AUTOWAH_MAX_RESONANCE (1000.0f)
+#define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f)
+
+#define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f)
+#define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f)
+#define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f)
+
+/* Compressor effect */
+#define AL_COMPRESSOR_MIN_ONOFF (0)
+#define AL_COMPRESSOR_MAX_ONOFF (1)
+#define AL_COMPRESSOR_DEFAULT_ONOFF (1)
+
+/* Equalizer effect */
+#define AL_EQUALIZER_MIN_LOW_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_LOW_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f)
+#define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f)
+#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f)
+
+#define AL_EQUALIZER_MIN_MID1_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_MID1_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_MID1_CENTER (200.0f)
+#define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f)
+#define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f)
+
+#define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f)
+#define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f)
+#define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f)
+
+#define AL_EQUALIZER_MIN_MID2_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_MID2_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f)
+#define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f)
+#define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f)
+
+#define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f)
+#define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f)
+#define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f)
+
+#define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f)
+#define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f)
+#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f)
+
+
+/* Source parameter value ranges and defaults. */
+#define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f)
+#define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f)
+#define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f)
+
+#define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
+#define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
+#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+
+#define AL_MIN_CONE_OUTER_GAINHF (0.0f)
+#define AL_MAX_CONE_OUTER_GAINHF (1.0f)
+#define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f)
+
+#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE
+#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE
+#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE
+
+#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE
+#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
+#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
+
+#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE
+#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+
+
+/* Listener parameter value ranges and defaults. */
+#define AL_MIN_METERS_PER_UNIT FLT_MIN
+#define AL_MAX_METERS_PER_UNIT FLT_MAX
+#define AL_DEFAULT_METERS_PER_UNIT (1.0f)
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* AL_EFX_H */
diff --git a/src/android/jni/include/AL/oalMacOSX_OALExtensions.h b/src/android/jni/include/AL/oalMacOSX_OALExtensions.h
new file mode 100644
index 00000000..c3db3054
--- /dev/null
+++ b/src/android/jni/include/AL/oalMacOSX_OALExtensions.h
@@ -0,0 +1,161 @@
+/**********************************************************************************************************************************
+*
+* OpenAL cross platform audio library
+* Copyright (c) 2004-2006, Apple Computer, Inc. All rights reserved.
+* Copyright (c) 2007-2008, Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
+* conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided with the distribution.
+* 3. Neither the name of Apple Inc. ("Apple") nor the names of its contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+**********************************************************************************************************************************/
+
+#ifndef __OAL_MAC_OSX_OAL_EXTENSIONS_H__
+#define __OAL_MAC_OSX_OAL_EXTENSIONS_H__
+
+#include <OpenAL/al.h>
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ALC_EXT_MAC_OSX
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+// Retrieve functions via alGetProcAddress() by passing in strings: alcMacOSXMixerOutputRate or alcMacOSXGetMixerOutputRate
+
+// Setting the Mixer Output Rate effectively sets the samnple rate at which the mixer
+typedef ALvoid (*alcMacOSXRenderingQualityProcPtr) (ALint value);
+typedef ALvoid (*alMacOSXRenderChannelCountProcPtr) (ALint value);
+typedef ALvoid (*alcMacOSXMixerMaxiumumBussesProcPtr) (ALint value);
+typedef ALvoid (*alcMacOSXMixerOutputRateProcPtr) (ALdouble value);
+
+typedef ALint (*alcMacOSXGetRenderingQualityProcPtr) ();
+typedef ALint (*alMacOSXGetRenderChannelCountProcPtr) ();
+typedef ALint (*alcMacOSXGetMixerMaxiumumBussesProcPtr) ();
+typedef ALdouble (*alcMacOSXGetMixerOutputRateProcPtr) ();
+
+/* Render Quality. Used with alcMacOSXRenderingQuality() */
+
+ #define ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH 'rqhi'
+ #define ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW 'rdlo'
+
+ // High Quality Spatial Algorithm suitable only for headphone use
+ #define ALC_IPHONE_SPATIAL_RENDERING_QUALITY_HEADPHONES 'hdph'
+
+/*
+ Render Channels. Used with alMacOSXRenderChannelCount()
+ Allows a user to force OpenAL to render to stereo, regardless of the audio hardware being used
+*/
+ #define ALC_MAC_OSX_RENDER_CHANNEL_COUNT_STEREO 'rcst'
+
+/* GameKit extension */
+
+ #define AL_GAMEKIT 'gksr'
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ AL_EXT_SOURCE_NOTIFICATIONS
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/*
+ Source Notifications
+
+ Eliminates the need for continuous polling for source state by providing a
+ mechanism for the application to receive source state change notifications.
+ Upon receiving a notification, the application can retrieve the actual state
+ corresponding to the notification ID for which the notification was sent.
+ */
+
+#define AL_QUEUE_HAS_LOOPED 0x9000
+
+/*
+ Notification Proc: ALSourceNotificationProc
+
+ sid - source id
+ notificationID - id of state that has changed
+ userData - user data provided to alSourceAddNotification()
+ */
+
+typedef ALvoid (*alSourceNotificationProc)(ALuint sid, ALuint notificationID, ALvoid* userData);
+
+/*
+ API: alSourceAddNotification
+
+ sid - source id
+ notificationID - id of state for which caller wants to be notified of a change
+ notifyProc - notification proc
+ userData - ptr to applications user data, will be returned in the notification proc
+
+ Returns AL_NO_ERROR if request is successful.
+
+ Valid IDs:
+ AL_SOURCE_STATE
+ AL_BUFFERS_PROCESSED
+ AL_QUEUE_HAS_LOOPED - notification sent when a looping source has looped to it's start point
+ */
+typedef ALenum (*alSourceAddNotificationProcPtr) (ALuint sid, ALuint notificationID, alSourceNotificationProc notifyProc, ALvoid* userData);
+
+/*
+ API: alSourceRemoveStateNotification
+
+ sid - source id
+ notificationID - id of state for which caller wants to remove an existing notification
+ notifyProc - notification proc
+ userData - ptr to applications user data, will be returned in the notification proc
+ */
+typedef ALvoid (*alSourceRemoveNotificationProcPtr) (ALuint sid, ALuint notificationID, alSourceNotificationProc notifyProc, ALvoid* userData);
+
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ALC_EXT_ASA : Apple Spatial Audio Extension
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+/*
+ Used with the ASA API calls: alcASAGetSource(), alcASASetSource(), alcASAGetListener(), alcASASetListener()
+*/
+
+typedef ALenum (*alcASAGetSourceProcPtr) (ALuint property, ALuint source, ALvoid *data, ALuint* dataSize);
+typedef ALenum (*alcASASetSourceProcPtr) (ALuint property, ALuint source, ALvoid *data, ALuint dataSize);
+typedef ALenum (*alcASAGetListenerProcPtr) (ALuint property, ALvoid *data, ALuint* dataSize);
+typedef ALenum (*alcASASetListenerProcPtr) (ALuint property, ALvoid *data, ALuint dataSize);
+
+ /* listener properties */
+ #define ALC_ASA_REVERB_ON 'rvon' // type ALuint
+ #define ALC_ASA_REVERB_GLOBAL_LEVEL 'rvgl' // type ALfloat -40.0 db - 40.0 db
+
+ #define ALC_ASA_REVERB_ROOM_TYPE 'rvrt' // type ALint
+
+ /* reverb room type presets for the ALC_ASA_REVERB_ROOM_TYPE property */
+ #define ALC_ASA_REVERB_ROOM_TYPE_SmallRoom 0
+ #define ALC_ASA_REVERB_ROOM_TYPE_MediumRoom 1
+ #define ALC_ASA_REVERB_ROOM_TYPE_LargeRoom 2
+ #define ALC_ASA_REVERB_ROOM_TYPE_MediumHall 3
+ #define ALC_ASA_REVERB_ROOM_TYPE_LargeHall 4
+ #define ALC_ASA_REVERB_ROOM_TYPE_Plate 5
+ #define ALC_ASA_REVERB_ROOM_TYPE_MediumChamber 6
+ #define ALC_ASA_REVERB_ROOM_TYPE_LargeChamber 7
+ #define ALC_ASA_REVERB_ROOM_TYPE_Cathedral 8
+ #define ALC_ASA_REVERB_ROOM_TYPE_LargeRoom2 9
+ #define ALC_ASA_REVERB_ROOM_TYPE_MediumHall2 10
+ #define ALC_ASA_REVERB_ROOM_TYPE_MediumHall3 11
+ #define ALC_ASA_REVERB_ROOM_TYPE_LargeHall2 12
+
+ #define ALC_ASA_REVERB_EQ_GAIN 'rveg' // type ALfloat
+ #define ALC_ASA_REVERB_EQ_BANDWITH 'rveb' // type ALfloat
+ #define ALC_ASA_REVERB_EQ_FREQ 'rvef' // type ALfloat
+
+ /* source properties */
+ #define ALC_ASA_REVERB_SEND_LEVEL 'rvsl' // type ALfloat 0.0 (dry) - 1.0 (wet) (0-100% dry/wet mix, 0.0 default)
+ #define ALC_ASA_OCCLUSION 'occl' // type ALfloat -100.0 db (most occlusion) - 0.0 db (no occlusion, 0.0 default)
+ #define ALC_ASA_OBSTRUCTION 'obst' // type ALfloat -100.0 db (most obstruction) - 0.0 db (no obstruction, 0.0 default)
+
+#endif // __OAL_MAC_OSX_OAL_EXTENSIONS_H__
diff --git a/src/android/jni/include/AL/oalStaticBufferExtension.h b/src/android/jni/include/AL/oalStaticBufferExtension.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/android/jni/include/AL/oalStaticBufferExtension.h
diff --git a/src/android/jni/include/android_native_app_glue.h b/src/android/jni/include/android_native_app_glue.h
new file mode 100644
index 00000000..1b8c1f10
--- /dev/null
+++ b/src/android/jni/include/android_native_app_glue.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef _ANDROID_NATIVE_APP_GLUE_H
+#define _ANDROID_NATIVE_APP_GLUE_H
+
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+
+#include <android/configuration.h>
+#include <android/looper.h>
+#include <android/native_activity.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The native activity interface provided by <android/native_activity.h>
+ * is based on a set of application-provided callbacks that will be called
+ * by the Activity's main thread when certain events occur.
+ *
+ * This means that each one of this callbacks _should_ _not_ block, or they
+ * risk having the system force-close the application. This programming
+ * model is direct, lightweight, but constraining.
+ *
+ * The 'threaded_native_app' static library is used to provide a different
+ * execution model where the application can implement its own main event
+ * loop in a different thread instead. Here's how it works:
+ *
+ * 1/ The application must provide a function named "android_main()" that
+ * will be called when the activity is created, in a new thread that is
+ * distinct from the activity's main thread.
+ *
+ * 2/ android_main() receives a pointer to a valid "android_app" structure
+ * that contains references to other important objects, e.g. the
+ * ANativeActivity obejct instance the application is running in.
+ *
+ * 3/ the "android_app" object holds an ALooper instance that already
+ * listens to two important things:
+ *
+ * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
+ * declarations below.
+ *
+ * - input events coming from the AInputQueue attached to the activity.
+ *
+ * Each of these correspond to an ALooper identifier returned by
+ * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
+ * respectively.
+ *
+ * Your application can use the same ALooper to listen to additional
+ * file-descriptors. They can either be callback based, or with return
+ * identifiers starting with LOOPER_ID_USER.
+ *
+ * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
+ * the returned data will point to an android_poll_source structure. You
+ * can call the process() function on it, and fill in android_app->onAppCmd
+ * and android_app->onInputEvent to be called for your own processing
+ * of the event.
+ *
+ * Alternatively, you can call the low-level functions to read and process
+ * the data directly... look at the process_cmd() and process_input()
+ * implementations in the glue to see how to do this.
+ *
+ * See the sample named "native-activity" that comes with the NDK with a
+ * full usage example. Also look at the JavaDoc of NativeActivity.
+ */
+
+struct android_app;
+
+/**
+ * Data associated with an ALooper fd that will be returned as the "outData"
+ * when that source has data ready.
+ */
+struct android_poll_source {
+ // The identifier of this source. May be LOOPER_ID_MAIN or
+ // LOOPER_ID_INPUT.
+ int32_t id;
+
+ // The android_app this ident is associated with.
+ struct android_app* app;
+
+ // Function to call to perform the standard processing of data from
+ // this source.
+ void (*process)(struct android_app* app, struct android_poll_source* source);
+};
+
+/**
+ * This is the interface for the standard glue code of a threaded
+ * application. In this model, the application's code is running
+ * in its own thread separate from the main thread of the process.
+ * It is not required that this thread be associated with the Java
+ * VM, although it will need to be in order to make JNI calls any
+ * Java objects.
+ */
+struct android_app {
+ // The application can place a pointer to its own state object
+ // here if it likes.
+ void* userData;
+
+ // Fill this in with the function to process main app commands (APP_CMD_*)
+ void (*onAppCmd)(struct android_app* app, int32_t cmd);
+
+ // Fill this in with the function to process input events. At this point
+ // the event has already been pre-dispatched, and it will be finished upon
+ // return. Return 1 if you have handled the event, 0 for any default
+ // dispatching.
+ int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
+
+ // The ANativeActivity object instance that this app is running in.
+ ANativeActivity* activity;
+
+ // The current configuration the app is running in.
+ AConfiguration* config;
+
+ // This is the last instance's saved state, as provided at creation time.
+ // It is NULL if there was no state. You can use this as you need; the
+ // memory will remain around until you call android_app_exec_cmd() for
+ // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
+ // These variables should only be changed when processing a APP_CMD_SAVE_STATE,
+ // at which point they will be initialized to NULL and you can malloc your
+ // state and place the information here. In that case the memory will be
+ // freed for you later.
+ void* savedState;
+ size_t savedStateSize;
+
+ // The ALooper associated with the app's thread.
+ ALooper* looper;
+
+ // When non-NULL, this is the input queue from which the app will
+ // receive user input events.
+ AInputQueue* inputQueue;
+
+ // When non-NULL, this is the window surface that the app can draw in.
+ ANativeWindow* window;
+
+ // Current content rectangle of the window; this is the area where the
+ // window's content should be placed to be seen by the user.
+ ARect contentRect;
+
+ // Current state of the app's activity. May be either APP_CMD_START,
+ // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
+ int activityState;
+
+ // This is non-zero when the application's NativeActivity is being
+ // destroyed and waiting for the app thread to complete.
+ int destroyRequested;
+
+ // -------------------------------------------------
+ // Below are "private" implementation of the glue code.
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+
+ int msgread;
+ int msgwrite;
+
+ pthread_t thread;
+
+ struct android_poll_source cmdPollSource;
+ struct android_poll_source inputPollSource;
+
+ int running;
+ int stateSaved;
+ int destroyed;
+ int redrawNeeded;
+ AInputQueue* pendingInputQueue;
+ ANativeWindow* pendingWindow;
+ ARect pendingContentRect;
+};
+
+enum {
+ /**
+ * Looper data ID of commands coming from the app's main thread, which
+ * is returned as an identifier from ALooper_pollOnce(). The data for this
+ * identifier is a pointer to an android_poll_source structure.
+ * These can be retrieved and processed with android_app_read_cmd()
+ * and android_app_exec_cmd().
+ */
+ LOOPER_ID_MAIN = 1,
+
+ /**
+ * Looper data ID of events coming from the AInputQueue of the
+ * application's window, which is returned as an identifier from
+ * ALooper_pollOnce(). The data for this identifier is a pointer to an
+ * android_poll_source structure. These can be read via the inputQueue
+ * object of android_app.
+ */
+ LOOPER_ID_INPUT = 2,
+
+ /**
+ * Start of user-defined ALooper identifiers.
+ */
+ LOOPER_ID_USER = 3,
+};
+
+enum {
+ /**
+ * Command from main thread: the AInputQueue has changed. Upon processing
+ * this command, android_app->inputQueue will be updated to the new queue
+ * (or NULL).
+ */
+ APP_CMD_INPUT_CHANGED,
+
+ /**
+ * Command from main thread: a new ANativeWindow is ready for use. Upon
+ * receiving this command, android_app->window will contain the new window
+ * surface.
+ */
+ APP_CMD_INIT_WINDOW,
+
+ /**
+ * Command from main thread: the existing ANativeWindow needs to be
+ * terminated. Upon receiving this command, android_app->window still
+ * contains the existing window; after calling android_app_exec_cmd
+ * it will be set to NULL.
+ */
+ APP_CMD_TERM_WINDOW,
+
+ /**
+ * Command from main thread: the current ANativeWindow has been resized.
+ * Please redraw with its new size.
+ */
+ APP_CMD_WINDOW_RESIZED,
+
+ /**
+ * Command from main thread: the system needs that the current ANativeWindow
+ * be redrawn. You should redraw the window before handing this to
+ * android_app_exec_cmd() in order to avoid transient drawing glitches.
+ */
+ APP_CMD_WINDOW_REDRAW_NEEDED,
+
+ /**
+ * Command from main thread: the content area of the window has changed,
+ * such as from the soft input window being shown or hidden. You can
+ * find the new content rect in android_app::contentRect.
+ */
+ APP_CMD_CONTENT_RECT_CHANGED,
+
+ /**
+ * Command from main thread: the app's activity window has gained
+ * input focus.
+ */
+ APP_CMD_GAINED_FOCUS,
+
+ /**
+ * Command from main thread: the app's activity window has lost
+ * input focus.
+ */
+ APP_CMD_LOST_FOCUS,
+
+ /**
+ * Command from main thread: the current device configuration has changed.
+ */
+ APP_CMD_CONFIG_CHANGED,
+
+ /**
+ * Command from main thread: the system is running low on memory.
+ * Try to reduce your memory use.
+ */
+ APP_CMD_LOW_MEMORY,
+
+ /**
+ * Command from main thread: the app's activity has been started.
+ */
+ APP_CMD_START,
+
+ /**
+ * Command from main thread: the app's activity has been resumed.
+ */
+ APP_CMD_RESUME,
+
+ /**
+ * Command from main thread: the app should generate a new saved state
+ * for itself, to restore from later if needed. If you have saved state,
+ * allocate it with malloc and place it in android_app.savedState with
+ * the size in android_app.savedStateSize. The will be freed for you
+ * later.
+ */
+ APP_CMD_SAVE_STATE,
+
+ /**
+ * Command from main thread: the app's activity has been paused.
+ */
+ APP_CMD_PAUSE,
+
+ /**
+ * Command from main thread: the app's activity has been stopped.
+ */
+ APP_CMD_STOP,
+
+ /**
+ * Command from main thread: the app's activity is being destroyed,
+ * and waiting for the app thread to clean up and exit before proceeding.
+ */
+ APP_CMD_DESTROY,
+};
+
+/**
+ * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
+ * app command message.
+ */
+int8_t android_app_read_cmd(struct android_app* android_app);
+
+/**
+ * Call with the command returned by android_app_read_cmd() to do the
+ * initial pre-processing of the given command. You can perform your own
+ * actions for the command after calling this function.
+ */
+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
+
+/**
+ * Call with the command returned by android_app_read_cmd() to do the
+ * final post-processing of the given command. You must have done your own
+ * actions for the command before calling this function.
+ */
+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
+
+/**
+ * Dummy function you can call to ensure glue code isn't stripped.
+ */
+void app_dummy();
+
+/**
+ * This is the function that application code must implement, representing
+ * the main entry to the app.
+ */
+extern void android_main(struct android_app* app);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ANDROID_NATIVE_APP_GLUE_H */
diff --git a/src/audio.c b/src/audio.c
index 260f6778..ad1d7eba 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -35,56 +35,99 @@
#include "raylib.h"
#endif
-#include "AL/al.h" // OpenAL basic header
-#include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work)
+#include "AL/al.h" // OpenAL basic header
+#include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work)
+#include "AL/alext.h" // OpenAL extensions for other format types
-#include <stdlib.h> // Declares malloc() and free() for memory management
-#include <string.h> // Required for strcmp()
-#include <stdio.h> // Used for .WAV loading
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strcmp(), strncmp()
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
#if defined(AUDIO_STANDALONE)
- #include <stdarg.h> // Used for functions with variable number of parameters (TraceLog())
+ #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
#else
- #include "utils.h" // rRES data decompression utility function
- // NOTE: Includes Android fopen function map
+ #include "utils.h" // Required for: DecompressData()
+ // NOTE: Includes Android fopen() function map
#endif
//#define STB_VORBIS_HEADER_ONLY
-#include "stb_vorbis.h" // OGG loading functions
+#include "external/stb_vorbis.h" // OGG loading functions
+
+#define JAR_XM_IMPLEMENTATION
+#include "external/jar_xm.h" // XM loading functions
+
+#define JAR_MOD_IMPLEMENTATION
+#include "external/jar_mod.h" // MOD loading functions
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define MUSIC_STREAM_BUFFERS 2
-
-#if defined(PLATFORM_RPI)
- // NOTE: On RPI should be lower to avoid frame-stalls
- #define MUSIC_BUFFER_SIZE 4096*2 // PCM data buffer (short) - 16Kb (RPI)
+#define MAX_STREAM_BUFFERS 2 // Number of buffers for each alSource
+#define MAX_MIX_CHANNELS 4 // Number of open AL sources
+#define MAX_MUSIC_STREAMS 2 // Number of simultanious music sources
+
+#if defined(PLATFORM_RPI) || defined(PLATFORM_ANDROID)
+ // NOTE: On RPI and Android should be lower to avoid frame-stalls
+ #define MUSIC_BUFFER_SIZE_SHORT 4096*2 // PCM data buffer (short) - 16Kb (RPI)
+ #define MUSIC_BUFFER_SIZE_FLOAT 4096 // PCM data buffer (float) - 16Kb (RPI)
#else
// NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care...
- #define MUSIC_BUFFER_SIZE 4096*8 // PCM data buffer (short) - 64Kb
+ #define MUSIC_BUFFER_SIZE_SHORT 4096*8 // PCM data buffer (short) - 64Kb
+ #define MUSIC_BUFFER_SIZE_FLOAT 4096*4 // PCM data buffer (float) - 64Kb
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
+// Used to create custom audio streams that are not bound to a specific file. There can be
+// no more than 4 concurrent mixchannels in use. This is due to each active mixc being tied to
+// a dedicated mix channel.
+typedef struct MixChannel_t {
+ unsigned short sampleRate; // default is 48000
+ unsigned char channels; // 1=mono,2=stereo
+ unsigned char mixChannel; // 0-3 or mixA-mixD, each mix channel can receive up to one dedicated audio stream
+ bool floatingPoint; // if false then the short datatype is used instead
+ bool playing; // false if paused
+
+ ALenum alFormat; // OpenAL format specifier
+ ALuint alSource; // OpenAL source
+ ALuint alBuffer[MAX_STREAM_BUFFERS]; // OpenAL sample buffer
+} MixChannel_t;
+
// Music type (file streaming from memory)
-// NOTE: Anything longer than ~10 seconds should be streamed...
+// NOTE: Anything longer than ~10 seconds should be streamed into a mix channel...
typedef struct Music {
stb_vorbis *stream;
-
- ALuint buffers[MUSIC_STREAM_BUFFERS];
- ALuint source;
- ALenum format;
-
- int channels;
- int sampleRate;
- int totalSamplesLeft;
+ jar_xm_context_t *xmctx; // XM chiptune context
+ jar_mod_context_t modctx; // MOD chiptune context
+ MixChannel_t *mixc; // mix channel
+
+ unsigned int totalSamplesLeft;
+ float totalLengthSeconds;
bool loop;
-
+ bool chipTune; // chiptune is loaded?
} Music;
+// Audio errors registered
+typedef enum {
+ ERROR_RAW_CONTEXT_CREATION = 1,
+ ERROR_XM_CONTEXT_CREATION = 2,
+ ERROR_MOD_CONTEXT_CREATION = 4,
+ ERROR_MIX_CHANNEL_CREATION = 8,
+ ERROR_MUSIC_CHANNEL_CREATION = 16,
+ ERROR_LOADING_XM = 32,
+ ERROR_LOADING_MOD = 64,
+ ERROR_LOADING_WAV = 128,
+ ERROR_LOADING_OGG = 256,
+ ERROR_OUT_OF_MIX_CHANNELS = 512,
+ ERROR_EXTENSION_NOT_RECOGNIZED = 1024,
+ ERROR_UNABLE_TO_OPEN_RRES_FILE = 2048,
+ ERROR_INVALID_RRES_FILE = 4096,
+ ERROR_INVALID_RRES_RESOURCE = 8192,
+ ERROR_UNINITIALIZED_CHANNELS = 16384
+} AudioError;
+
#if defined(AUDIO_STANDALONE)
typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
#endif
@@ -92,19 +135,29 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
-static bool musicEnabled = false;
-static Music currentMusic; // Current music loaded
- // NOTE: Only one music file playing at a time
+static Music musicChannels_g[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time
+static MixChannel_t *mixChannels_g[MAX_MIX_CHANNELS]; // What mix channels are currently active
+static bool musicEnabled_g = false;
+
+static int lastAudioError = 0; // Registers last audio error
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
-static Wave LoadWAV(const char *fileName); // Load WAV file
-static Wave LoadOGG(char *fileName); // Load OGG file
-static void UnloadWave(Wave wave); // Unload wave data
+static Wave LoadWAV(const char *fileName); // Load WAV file
+static Wave LoadOGG(char *fileName); // Load OGG file
+static void UnloadWave(Wave wave); // Unload wave data
-static bool BufferMusicStream(ALuint buffer); // Fill music buffers with data
-static void EmptyMusicStream(void); // Empty music buffers
+static bool BufferMusicStream(int index, int numBuffers); // Fill music buffers with data
+static void EmptyMusicStream(int index); // Empty music buffers
+
+static MixChannel_t *InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint); // For streaming into mix channels.
+static void CloseMixChannel(MixChannel_t *mixc); // Frees mix channel
+static int BufferMixChannel(MixChannel_t *mixc, void *data, int numberElements); // Pushes more audio data into mixc mix channel, if NULL is passed it pauses
+static int FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer); // Fill buffer with zeros, returns number processed
+static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // Pass two arrays of the same legnth in
+static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // Pass two arrays of same length in
+static int IsMusicStreamReadyForBuffering(int index); // Checks if music buffer is ready to be refilled
#if defined(AUDIO_STANDALONE)
const char *GetExtension(const char *fileName); // Get the extension for a filename
@@ -115,23 +168,23 @@ void TraceLog(int msgType, const char *text, ...); // Outputs a trace log messa
// Module Functions Definition - Audio Device initialization and Closing
//----------------------------------------------------------------------------------
-// Initialize audio device and context
+// Initialize audio device and mixc
void InitAudioDevice(void)
{
// Open and initialize a device with default settings
ALCdevice *device = alcOpenDevice(NULL);
- if(!device) TraceLog(ERROR, "Audio device could not be opened");
+ if (!device) TraceLog(ERROR, "Audio device could not be opened");
ALCcontext *context = alcCreateContext(device, NULL);
- if(context == NULL || alcMakeContextCurrent(context) == ALC_FALSE)
+ if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE))
{
- if(context != NULL) alcDestroyContext(context);
+ if (context != NULL) alcDestroyContext(context);
alcCloseDevice(device);
- TraceLog(ERROR, "Could not setup audio context");
+ TraceLog(ERROR, "Could not setup mix channel");
}
TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER));
@@ -142,15 +195,18 @@ void InitAudioDevice(void)
alListener3f(AL_ORIENTATION, 0, 0, -1);
}
-// Close the audio device for the current context, and destroys the context
+// Close the audio device for all contexts
void CloseAudioDevice(void)
{
- StopMusicStream(); // Stop music streaming and close current stream
+ for (int index=0; index<MAX_MUSIC_STREAMS; index++)
+ {
+ if (musicChannels_g[index].mixc) StopMusicStream(index); // Stop music streaming and close current stream
+ }
ALCdevice *device;
ALCcontext *context = alcGetCurrentContext();
- if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing");
+ if (context == NULL) TraceLog(WARNING, "Could not get current mix channel for closing");
device = alcGetContextsDevice(context);
@@ -159,6 +215,232 @@ void CloseAudioDevice(void)
alcCloseDevice(device);
}
+// True if call to InitAudioDevice() was successful and CloseAudioDevice() has not been called yet
+bool IsAudioDeviceReady(void)
+{
+ ALCcontext *context = alcGetCurrentContext();
+
+ if (context == NULL) return false;
+ else
+ {
+ ALCdevice *device = alcGetContextsDevice(context);
+
+ if (device == NULL) return false;
+ else return true;
+ }
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Custom audio output
+//----------------------------------------------------------------------------------
+
+// For streaming into mix channels.
+// The mixChannel is what audio muxing channel you want to operate on, 0-3 are the ones available. Each mix channel can only be used one at a time.
+// exmple usage is InitMixChannel(48000, 0, 2, true); // mixchannel 1, 48khz, stereo, floating point
+static MixChannel_t *InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint)
+{
+ if (mixChannel >= MAX_MIX_CHANNELS) return NULL;
+ if (!IsAudioDeviceReady()) InitAudioDevice();
+
+ if (!mixChannels_g[mixChannel])
+ {
+ MixChannel_t *mixc = (MixChannel_t *)malloc(sizeof(MixChannel_t));
+ mixc->sampleRate = sampleRate;
+ mixc->channels = channels;
+ mixc->mixChannel = mixChannel;
+ mixc->floatingPoint = floatingPoint;
+ mixChannels_g[mixChannel] = mixc;
+
+ // Setup OpenAL format
+ if (channels == 1)
+ {
+ if (floatingPoint) mixc->alFormat = AL_FORMAT_MONO_FLOAT32;
+ else mixc->alFormat = AL_FORMAT_MONO16;
+ }
+ else if (channels == 2)
+ {
+ if (floatingPoint) mixc->alFormat = AL_FORMAT_STEREO_FLOAT32;
+ else mixc->alFormat = AL_FORMAT_STEREO16;
+ }
+
+ // Create an audio source
+ alGenSources(1, &mixc->alSource);
+ alSourcef(mixc->alSource, AL_PITCH, 1);
+ alSourcef(mixc->alSource, AL_GAIN, 1);
+ alSource3f(mixc->alSource, AL_POSITION, 0, 0, 0);
+ alSource3f(mixc->alSource, AL_VELOCITY, 0, 0, 0);
+
+ // Create Buffer
+ alGenBuffers(MAX_STREAM_BUFFERS, mixc->alBuffer);
+
+ // Fill buffers
+ for (int i = 0; i < MAX_STREAM_BUFFERS; i++) FillAlBufferWithSilence(mixc, mixc->alBuffer[i]);
+
+ alSourceQueueBuffers(mixc->alSource, MAX_STREAM_BUFFERS, mixc->alBuffer);
+ mixc->playing = true;
+ alSourcePlay(mixc->alSource);
+
+ return mixc;
+ }
+
+ return NULL;
+}
+
+// Frees buffer in mix channel
+static void CloseMixChannel(MixChannel_t *mixc)
+{
+ if (mixc)
+ {
+ alSourceStop(mixc->alSource);
+ mixc->playing = false;
+
+ // Flush out all queued buffers
+ ALuint buffer = 0;
+ int queued = 0;
+ alGetSourcei(mixc->alSource, AL_BUFFERS_QUEUED, &queued);
+
+ while (queued > 0)
+ {
+ alSourceUnqueueBuffers(mixc->alSource, 1, &buffer);
+ queued--;
+ }
+
+ // Delete source and buffers
+ alDeleteSources(1, &mixc->alSource);
+ alDeleteBuffers(MAX_STREAM_BUFFERS, mixc->alBuffer);
+ mixChannels_g[mixc->mixChannel] = NULL;
+ free(mixc);
+ mixc = NULL;
+ }
+}
+
+// Pushes more audio data into mixc mix channel, only one buffer per call
+// Call "BufferMixChannel(mixc, NULL, 0)" if you want to pause the audio.
+// @Returns number of samples that where processed.
+static int BufferMixChannel(MixChannel_t *mixc, void *data, int numberElements)
+{
+ if (!mixc || (mixChannels_g[mixc->mixChannel] != mixc)) return 0; // When there is two channels there must be an even number of samples
+
+ if (!data || !numberElements)
+ {
+ // Pauses audio until data is given
+ if (mixc->playing)
+ {
+ alSourcePause(mixc->alSource);
+ mixc->playing = false;
+ }
+
+ return 0;
+ }
+ else if (!mixc->playing)
+ {
+ // Restart audio otherwise
+ alSourcePlay(mixc->alSource);
+ mixc->playing = true;
+ }
+
+ ALuint buffer = 0;
+
+ alSourceUnqueueBuffers(mixc->alSource, 1, &buffer);
+ if (!buffer) return 0;
+
+ if (mixc->floatingPoint)
+ {
+ // Process float buffers
+ float *ptr = (float *)data;
+ alBufferData(buffer, mixc->alFormat, ptr, numberElements*sizeof(float), mixc->sampleRate);
+ }
+ else
+ {
+ // Process short buffers
+ short *ptr = (short *)data;
+ alBufferData(buffer, mixc->alFormat, ptr, numberElements*sizeof(short), mixc->sampleRate);
+ }
+
+ alSourceQueueBuffers(mixc->alSource, 1, &buffer);
+
+ return numberElements;
+}
+
+// fill buffer with zeros, returns number processed
+static int FillAlBufferWithSilence(MixChannel_t *mixc, ALuint buffer)
+{
+ if (mixc->floatingPoint)
+ {
+ float pcm[MUSIC_BUFFER_SIZE_FLOAT] = { 0.0f };
+ alBufferData(buffer, mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_FLOAT*sizeof(float), mixc->sampleRate);
+
+ return MUSIC_BUFFER_SIZE_FLOAT;
+ }
+ else
+ {
+ short pcm[MUSIC_BUFFER_SIZE_SHORT] = { 0 };
+ alBufferData(buffer, mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_SHORT*sizeof(short), mixc->sampleRate);
+
+ return MUSIC_BUFFER_SIZE_SHORT;
+ }
+}
+
+// example usage:
+// short sh[3] = {1,2,3};float fl[3];
+// ResampleShortToFloat(sh,fl,3);
+static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len)
+{
+ for (int i = 0; i < len; i++)
+ {
+ if (shorts[i] < 0) floats[i] = (float)shorts[i]/32766.0f;
+ else floats[i] = (float)shorts[i]/32767.0f;
+ }
+}
+
+// example usage:
+// char ch[3] = {1,2,3};float fl[3];
+// ResampleByteToFloat(ch,fl,3);
+static void ResampleByteToFloat(char *chars, float *floats, unsigned short len)
+{
+ for (int i = 0; i < len; i++)
+ {
+ if (chars[i] < 0) floats[i] = (float)chars[i]/127.0f;
+ else floats[i] = (float)chars[i]/128.0f;
+ }
+}
+
+// used to output raw audio streams, returns negative numbers on error, + number represents the mix channel index
+// if floating point is false the data size is 16bit short, otherwise it is float 32bit
+RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint)
+{
+ int mixIndex;
+ for (mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot
+ {
+ if (mixChannels_g[mixIndex] == NULL) break;
+ else if (mixIndex == (MAX_MIX_CHANNELS - 1)) return ERROR_OUT_OF_MIX_CHANNELS; // error
+ }
+
+ if (InitMixChannel(sampleRate, mixIndex, channels, floatingPoint)) return mixIndex;
+ else return ERROR_RAW_CONTEXT_CREATION; // error
+}
+
+void CloseRawAudioContext(RawAudioContext ctx)
+{
+ if (mixChannels_g[ctx]) CloseMixChannel(mixChannels_g[ctx]);
+}
+
+// if 0 is returned, the buffers are still full and you need to keep trying with the same data until a + number is returned.
+// any + number returned is the number of samples that was processed and passed into buffer.
+// data either needs to be array of floats or shorts.
+int BufferRawAudioContext(RawAudioContext ctx, void *data, unsigned short numberElements)
+{
+ int numBuffered = 0;
+
+ if (ctx >= 0)
+ {
+ MixChannel_t* mixc = mixChannels_g[ctx];
+ numBuffered = BufferMixChannel(mixc, data, numberElements);
+ }
+
+ return numBuffered;
+}
+
//----------------------------------------------------------------------------------
// Module Functions Definition - Sounds loading and playing (.WAV)
//----------------------------------------------------------------------------------
@@ -176,7 +458,13 @@ Sound LoadSound(char *fileName)
if (strcmp(GetExtension(fileName),"wav") == 0) wave = LoadWAV(fileName);
else if (strcmp(GetExtension(fileName),"ogg") == 0) wave = LoadOGG(fileName);
- else TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName);
+ else
+ {
+ TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName);
+
+ // TODO: Find a better way to register errors (similar to glGetError())
+ lastAudioError = ERROR_EXTENSION_NOT_RECOGNIZED;
+ }
if (wave.data != NULL)
{
@@ -303,6 +591,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
if (rresFile == NULL)
{
TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
+ lastAudioError = ERROR_UNABLE_TO_OPEN_RRES_FILE;
}
else
{
@@ -317,6 +606,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
{
TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName);
+ lastAudioError = ERROR_INVALID_RRES_FILE;
}
else
{
@@ -407,6 +697,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
else
{
TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName);
+ lastAudioError = ERROR_INVALID_RRES_RESOURCE;
}
}
else
@@ -479,7 +770,7 @@ void StopSound(Sound sound)
}
// Check if a sound is playing
-bool SoundIsPlaying(Sound sound)
+bool IsSoundPlaying(Sound sound)
{
bool playing = false;
ALint state;
@@ -507,145 +798,262 @@ void SetSoundPitch(Sound sound, float pitch)
//----------------------------------------------------------------------------------
// Start music playing (open stream)
-void PlayMusicStream(char *fileName)
+// returns 0 on success
+int PlayMusicStream(int index, char *fileName)
{
+ int mixIndex;
+
+ if (musicChannels_g[index].stream || musicChannels_g[index].xmctx) return ERROR_UNINITIALIZED_CHANNELS; // error
+
+ for (mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot
+ {
+ if (mixChannels_g[mixIndex] == NULL) break;
+ else if (mixIndex == (MAX_MIX_CHANNELS - 1)) return ERROR_OUT_OF_MIX_CHANNELS; // error
+ }
+
if (strcmp(GetExtension(fileName),"ogg") == 0)
{
- // Stop current music, clean buffers, unload current stream
- StopMusicStream();
-
// Open audio stream
- currentMusic.stream = stb_vorbis_open_filename(fileName, NULL, NULL);
+ musicChannels_g[index].stream = stb_vorbis_open_filename(fileName, NULL, NULL);
- if (currentMusic.stream == NULL)
+ if (musicChannels_g[index].stream == NULL)
{
TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName);
+ return ERROR_LOADING_OGG; // error
}
else
{
// Get file info
- stb_vorbis_info info = stb_vorbis_get_info(currentMusic.stream);
-
- currentMusic.channels = info.channels;
- currentMusic.sampleRate = info.sample_rate;
+ stb_vorbis_info info = stb_vorbis_get_info(musicChannels_g[index].stream);
TraceLog(INFO, "[%s] Ogg sample rate: %i", fileName, info.sample_rate);
TraceLog(INFO, "[%s] Ogg channels: %i", fileName, info.channels);
TraceLog(DEBUG, "[%s] Temp memory required: %i", fileName, info.temp_memory_required);
- if (info.channels == 2) currentMusic.format = AL_FORMAT_STEREO16;
- else currentMusic.format = AL_FORMAT_MONO16;
-
- currentMusic.loop = true; // We loop by default
- musicEnabled = true;
-
- // Create an audio source
- alGenSources(1, &currentMusic.source); // Generate pointer to audio source
+ musicChannels_g[index].loop = true; // We loop by default
+ musicEnabled_g = true;
+
- alSourcef(currentMusic.source, AL_PITCH, 1);
- alSourcef(currentMusic.source, AL_GAIN, 1);
- alSource3f(currentMusic.source, AL_POSITION, 0, 0, 0);
- alSource3f(currentMusic.source, AL_VELOCITY, 0, 0, 0);
- //alSourcei(currentMusic.source, AL_LOOPING, AL_TRUE); // ERROR: Buffers do not queue!
-
- // Generate two OpenAL buffers
- alGenBuffers(2, currentMusic.buffers);
-
- // Fill buffers with music...
- BufferMusicStream(currentMusic.buffers[0]);
- BufferMusicStream(currentMusic.buffers[1]);
-
- // Queue buffers and start playing
- alSourceQueueBuffers(currentMusic.source, 2, currentMusic.buffers);
- alSourcePlay(currentMusic.source);
-
- // NOTE: Regularly, we must check if a buffer has been processed and refill it: UpdateMusicStream()
-
- currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
+ musicChannels_g[index].totalSamplesLeft = (unsigned int)stb_vorbis_stream_length_in_samples(musicChannels_g[index].stream) * info.channels;
+ musicChannels_g[index].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(musicChannels_g[index].stream);
+
+ if (info.channels == 2)
+ {
+ musicChannels_g[index].mixc = InitMixChannel(info.sample_rate, mixIndex, 2, false);
+ musicChannels_g[index].mixc->playing = true;
+ }
+ else
+ {
+ musicChannels_g[index].mixc = InitMixChannel(info.sample_rate, mixIndex, 1, false);
+ musicChannels_g[index].mixc->playing = true;
+ }
+
+ if (!musicChannels_g[index].mixc) return ERROR_LOADING_OGG; // error
+ }
+ }
+ else if (strcmp(GetExtension(fileName),"xm") == 0)
+ {
+ // only stereo is supported for xm
+ if (!jar_xm_create_context_from_file(&musicChannels_g[index].xmctx, 48000, fileName))
+ {
+ musicChannels_g[index].chipTune = true;
+ musicChannels_g[index].loop = true;
+ jar_xm_set_max_loop_count(musicChannels_g[index].xmctx, 0); // infinite number of loops
+ musicChannels_g[index].totalSamplesLeft = (unsigned int)jar_xm_get_remaining_samples(musicChannels_g[index].xmctx);
+ musicChannels_g[index].totalLengthSeconds = ((float)musicChannels_g[index].totalSamplesLeft) / 48000.f;
+ musicEnabled_g = true;
+
+ TraceLog(INFO, "[%s] XM number of samples: %i", fileName, musicChannels_g[index].totalSamplesLeft);
+ TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, musicChannels_g[index].totalLengthSeconds);
+
+ musicChannels_g[index].mixc = InitMixChannel(48000, mixIndex, 2, true);
+
+ if (!musicChannels_g[index].mixc) return ERROR_XM_CONTEXT_CREATION; // error
+
+ musicChannels_g[index].mixc->playing = true;
+ }
+ else
+ {
+ TraceLog(WARNING, "[%s] XM file could not be opened", fileName);
+ return ERROR_LOADING_XM; // error
+ }
+ }
+ else if (strcmp(GetExtension(fileName),"mod") == 0)
+ {
+ jar_mod_init(&musicChannels_g[index].modctx);
+
+ if (jar_mod_load_file(&musicChannels_g[index].modctx, fileName))
+ {
+ musicChannels_g[index].chipTune = true;
+ musicChannels_g[index].loop = true;
+ musicChannels_g[index].totalSamplesLeft = (unsigned int)jar_mod_max_samples(&musicChannels_g[index].modctx);
+ musicChannels_g[index].totalLengthSeconds = ((float)musicChannels_g[index].totalSamplesLeft) / 48000.f;
+ musicEnabled_g = true;
+
+ TraceLog(INFO, "[%s] MOD number of samples: %i", fileName, musicChannels_g[index].totalSamplesLeft);
+ TraceLog(INFO, "[%s] MOD track length: %11.6f sec", fileName, musicChannels_g[index].totalLengthSeconds);
+
+ musicChannels_g[index].mixc = InitMixChannel(48000, mixIndex, 2, false);
+
+ if (!musicChannels_g[index].mixc) return ERROR_MOD_CONTEXT_CREATION; // error
+
+ musicChannels_g[index].mixc->playing = true;
+ }
+ else
+ {
+ TraceLog(WARNING, "[%s] MOD file could not be opened", fileName);
+ return ERROR_LOADING_MOD; // error
}
}
- else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
+ else
+ {
+ TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
+ return ERROR_EXTENSION_NOT_RECOGNIZED; // error
+ }
+
+ return 0; // normal return
}
-// Stop music playing (close stream)
-void StopMusicStream(void)
+// Stop music playing for individual music index of musicChannels_g array (close stream)
+void StopMusicStream(int index)
{
- if (musicEnabled)
+ if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
{
- alSourceStop(currentMusic.source);
-
- EmptyMusicStream(); // Empty music buffers
-
- alDeleteSources(1, &currentMusic.source);
- alDeleteBuffers(2, currentMusic.buffers);
-
- stb_vorbis_close(currentMusic.stream);
+ CloseMixChannel(musicChannels_g[index].mixc);
+
+ if (musicChannels_g[index].chipTune && musicChannels_g[index].xmctx)
+ {
+ jar_xm_free_context(musicChannels_g[index].xmctx);
+ musicChannels_g[index].xmctx = 0;
+ }
+ else if (musicChannels_g[index].chipTune && musicChannels_g[index].modctx.mod_loaded) jar_mod_unload(&musicChannels_g[index].modctx);
+ else stb_vorbis_close(musicChannels_g[index].stream);
+
+ if (!GetMusicStreamCount()) musicEnabled_g = false;
+
+ if (musicChannels_g[index].stream || musicChannels_g[index].xmctx)
+ {
+ musicChannels_g[index].stream = NULL;
+ musicChannels_g[index].xmctx = NULL;
+ }
}
+}
- musicEnabled = false;
+//get number of music channels active at this time, this does not mean they are playing
+int GetMusicStreamCount(void)
+{
+ int musicCount = 0;
+
+ // Find empty music slot
+ for (int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++)
+ {
+ if(musicChannels_g[musicIndex].stream != NULL || musicChannels_g[musicIndex].chipTune) musicCount++;
+ }
+
+ return musicCount;
}
// Pause music playing
-void PauseMusicStream(void)
+void PauseMusicStream(int index)
{
// Pause music stream if music available!
- if (musicEnabled)
+ if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc && musicEnabled_g)
{
TraceLog(INFO, "Pausing music stream");
- alSourcePause(currentMusic.source);
- musicEnabled = false;
+ alSourcePause(musicChannels_g[index].mixc->alSource);
+ musicChannels_g[index].mixc->playing = false;
}
}
// Resume music playing
-void ResumeMusicStream(void)
+void ResumeMusicStream(int index)
{
// Resume music playing... if music available!
ALenum state;
- alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
-
- if (state == AL_PAUSED)
+
+ if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
{
- TraceLog(INFO, "Resuming music stream");
- alSourcePlay(currentMusic.source);
- musicEnabled = true;
+ alGetSourcei(musicChannels_g[index].mixc->alSource, AL_SOURCE_STATE, &state);
+
+ if (state == AL_PAUSED)
+ {
+ TraceLog(INFO, "Resuming music stream");
+ alSourcePlay(musicChannels_g[index].mixc->alSource);
+ musicChannels_g[index].mixc->playing = true;
+ }
}
}
-// Check if music is playing
-bool MusicIsPlaying(void)
+// Check if any music is playing
+bool IsMusicPlaying(int index)
{
bool playing = false;
ALint state;
-
- alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
- if (state == AL_PLAYING) playing = true;
+
+ if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
+ {
+ alGetSourcei(musicChannels_g[index].mixc->alSource, AL_SOURCE_STATE, &state);
+
+ if (state == AL_PLAYING) playing = true;
+ }
return playing;
}
// Set volume for music
-void SetMusicVolume(float volume)
+void SetMusicVolume(int index, float volume)
{
- alSourcef(currentMusic.source, AL_GAIN, volume);
+ if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
+ {
+ alSourcef(musicChannels_g[index].mixc->alSource, AL_GAIN, volume);
+ }
}
-// Get current music time length (in seconds)
-float GetMusicTimeLength(void)
+// Set pitch for music
+void SetMusicPitch(int index, float pitch)
{
- float totalSeconds = stb_vorbis_stream_length_in_seconds(currentMusic.stream);
+ if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
+ {
+ alSourcef(musicChannels_g[index].mixc->alSource, AL_PITCH, pitch);
+ }
+}
+
+// Get music time length (in seconds)
+float GetMusicTimeLength(int index)
+{
+ float totalSeconds;
+
+ if (musicChannels_g[index].chipTune) totalSeconds = (float)musicChannels_g[index].totalLengthSeconds;
+ else totalSeconds = stb_vorbis_stream_length_in_seconds(musicChannels_g[index].stream);
return totalSeconds;
}
// Get current music time played (in seconds)
-float GetMusicTimePlayed(void)
+float GetMusicTimePlayed(int index)
{
- int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
-
- int samplesPlayed = totalSamples - currentMusic.totalSamplesLeft;
-
- float secondsPlayed = (float)samplesPlayed / (currentMusic.sampleRate * currentMusic.channels);
+ float secondsPlayed = 0.0f;
+
+ if (index < MAX_MUSIC_STREAMS && musicChannels_g[index].mixc)
+ {
+ if (musicChannels_g[index].chipTune && musicChannels_g[index].xmctx)
+ {
+ uint64_t samples;
+ jar_xm_get_position(musicChannels_g[index].xmctx, NULL, NULL, NULL, &samples);
+ secondsPlayed = (float)samples / (48000.f * musicChannels_g[index].mixc->channels); // Not sure if this is the correct value
+ }
+ else if(musicChannels_g[index].chipTune && musicChannels_g[index].modctx.mod_loaded)
+ {
+ long numsamp = jar_mod_current_samples(&musicChannels_g[index].modctx);
+ secondsPlayed = (float)numsamp / (48000.f);
+ }
+ else
+ {
+ int totalSamples = stb_vorbis_stream_length_in_samples(musicChannels_g[index].stream) * musicChannels_g[index].mixc->channels;
+ int samplesPlayed = totalSamples - musicChannels_g[index].totalSamplesLeft;
+ secondsPlayed = (float)samplesPlayed / (musicChannels_g[index].mixc->sampleRate * musicChannels_g[index].mixc->channels);
+ }
+ }
return secondsPlayed;
}
@@ -655,102 +1063,125 @@ float GetMusicTimePlayed(void)
//----------------------------------------------------------------------------------
// Fill music buffers with new data from music stream
-static bool BufferMusicStream(ALuint buffer)
+static bool BufferMusicStream(int index, int numBuffers)
{
- short pcm[MUSIC_BUFFER_SIZE];
-
- int size = 0; // Total size of data steamed (in bytes)
- int streamedBytes = 0; // Bytes of data obtained in one samples get
-
- bool active = true; // We can get more data from stream (not finished)
-
- if (musicEnabled)
+ short pcm[MUSIC_BUFFER_SIZE_SHORT];
+ float pcmf[MUSIC_BUFFER_SIZE_FLOAT];
+
+ int size = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts
+ bool active = true; // We can get more data from stream (not finished)
+
+ if (musicChannels_g[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes.
{
- while (size < MUSIC_BUFFER_SIZE)
+ for (int i = 0; i < numBuffers; i++)
{
- streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic.stream, currentMusic.channels, pcm + size, MUSIC_BUFFER_SIZE - size);
+ if (musicChannels_g[index].modctx.mod_loaded)
+ {
+ if (musicChannels_g[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) size = MUSIC_BUFFER_SIZE_SHORT/2;
+ else size = musicChannels_g[index].totalSamplesLeft/2;
+
+ jar_mod_fillbuffer(&musicChannels_g[index].modctx, pcm, size, 0 );
+ BufferMixChannel(musicChannels_g[index].mixc, pcm, size*2);
+ }
+ else if (musicChannels_g[index].xmctx)
+ {
+ if (musicChannels_g[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_FLOAT) size = MUSIC_BUFFER_SIZE_FLOAT/2;
+ else size = musicChannels_g[index].totalSamplesLeft/2;
+
+ jar_xm_generate_samples(musicChannels_g[index].xmctx, pcmf, size); // reads 2*readlen shorts and moves them to buffer+size memory location
+ BufferMixChannel(musicChannels_g[index].mixc, pcmf, size*2);
+ }
- if (streamedBytes > 0) size += (streamedBytes*currentMusic.channels);
- else break;
+ musicChannels_g[index].totalSamplesLeft -= size;
+
+ if (musicChannels_g[index].totalSamplesLeft <= 0)
+ {
+ active = false;
+ break;
+ }
}
-
- //TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size);
- }
-
- if (size > 0)
- {
- alBufferData(buffer, currentMusic.format, pcm, size*sizeof(short), currentMusic.sampleRate);
-
- currentMusic.totalSamplesLeft -= size;
}
else
{
- active = false;
- TraceLog(WARNING, "No more data obtained from stream");
+ if (musicChannels_g[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) size = MUSIC_BUFFER_SIZE_SHORT;
+ else size = musicChannels_g[index].totalSamplesLeft;
+
+ for (int i = 0; i < numBuffers; i++)
+ {
+ int streamedBytes = stb_vorbis_get_samples_short_interleaved(musicChannels_g[index].stream, musicChannels_g[index].mixc->channels, pcm, size);
+ BufferMixChannel(musicChannels_g[index].mixc, pcm, streamedBytes * musicChannels_g[index].mixc->channels);
+ musicChannels_g[index].totalSamplesLeft -= streamedBytes * musicChannels_g[index].mixc->channels;
+
+ if (musicChannels_g[index].totalSamplesLeft <= 0)
+ {
+ active = false;
+ break;
+ }
+ }
}
return active;
}
// Empty music buffers
-static void EmptyMusicStream(void)
+static void EmptyMusicStream(int index)
{
ALuint buffer = 0;
int queued = 0;
- alGetSourcei(currentMusic.source, AL_BUFFERS_QUEUED, &queued);
+ alGetSourcei(musicChannels_g[index].mixc->alSource, AL_BUFFERS_QUEUED, &queued);
while (queued > 0)
{
- alSourceUnqueueBuffers(currentMusic.source, 1, &buffer);
+ alSourceUnqueueBuffers(musicChannels_g[index].mixc->alSource, 1, &buffer);
queued--;
}
}
-// Update (re-fill) music buffers if data already processed
-void UpdateMusicStream(void)
+// Determine if a music stream is ready to be written
+static int IsMusicStreamReadyForBuffering(int index)
{
- ALuint buffer = 0;
ALint processed = 0;
- bool active = true;
+ alGetSourcei(musicChannels_g[index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed);
+ return processed;
+}
- if (musicEnabled)
+// Update (re-fill) music buffers if data already processed
+void UpdateMusicStream(int index)
+{
+ ALenum state;
+ bool active = true;
+ int numBuffers = IsMusicStreamReadyForBuffering(index);
+
+ if (musicChannels_g[index].mixc->playing && (index < MAX_MUSIC_STREAMS) && musicEnabled_g && musicChannels_g[index].mixc && numBuffers)
{
- // Get the number of already processed buffers (if any)
- alGetSourcei(currentMusic.source, AL_BUFFERS_PROCESSED, &processed);
-
- while (processed > 0)
+ active = BufferMusicStream(index, numBuffers);
+
+ if (!active && musicChannels_g[index].loop)
{
- // Recover processed buffer for refill
- alSourceUnqueueBuffers(currentMusic.source, 1, &buffer);
-
- // Refill buffer
- active = BufferMusicStream(buffer);
-
- // If no more data to stream, restart music (if loop)
- if ((!active) && (currentMusic.loop))
+ if (musicChannels_g[index].chipTune)
{
- stb_vorbis_seek_start(currentMusic.stream);
- currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream)*currentMusic.channels;
-
- active = BufferMusicStream(buffer);
+ if(musicChannels_g[index].modctx.mod_loaded) jar_mod_seek_start(&musicChannels_g[index].modctx);
+ musicChannels_g[index].totalSamplesLeft = musicChannels_g[index].totalLengthSeconds * 48000;
}
-
- // Add refilled buffer to queue again... don't let the music stop!
- alSourceQueueBuffers(currentMusic.source, 1, &buffer);
-
- if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Ogg playing, error buffering data...");
-
- processed--;
+ else
+ {
+ stb_vorbis_seek_start(musicChannels_g[index].stream);
+ musicChannels_g[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(musicChannels_g[index].stream) * musicChannels_g[index].mixc->channels;
+ }
+
+ active = true;
}
- ALenum state;
- alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
+ if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data...");
+
+ alGetSourcei(musicChannels_g[index].mixc->alSource, AL_SOURCE_STATE, &state);
- if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic.source);
+ if (state != AL_PLAYING && active) alSourcePlay(musicChannels_g[index].mixc->alSource);
- if (!active) StopMusicStream();
+ if (!active) StopMusicStream(index);
+
}
}
diff --git a/src/audio.h b/src/audio.h
index ed156532..fe72d866 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -41,13 +41,17 @@
//----------------------------------------------------------------------------------
#ifndef __cplusplus
// Boolean type
-typedef enum { false, true } bool;
+ #if !defined(_STDBOOL_H)
+ typedef enum { false, true } bool;
+ #define _STDBOOL_H
+ #endif
#endif
// Sound source type
typedef struct Sound {
unsigned int source;
unsigned int buffer;
+ AudioError error; // if there was any error during the creation or use of this Sound
} Sound;
// Wave type, defines audio wave data
@@ -59,6 +63,9 @@ typedef struct Wave {
short channels;
} Wave;
+typedef int RawAudioContext;
+
+
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
@@ -73,6 +80,7 @@ extern "C" { // Prevents name mangling of functions
//----------------------------------------------------------------------------------
void InitAudioDevice(void); // Initialize audio device and context
void CloseAudioDevice(void); // Close the audio device and context (and music stream)
+bool IsAudioDeviceReady(void); // True if call to InitAudioDevice() was successful and CloseAudioDevice() has not been called yet
Sound LoadSound(char *fileName); // Load sound to memory
Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data
@@ -81,19 +89,28 @@ void UnloadSound(Sound sound); // Unload sound
void PlaySound(Sound sound); // Play a sound
void PauseSound(Sound sound); // Pause a sound
void StopSound(Sound sound); // Stop playing a sound
-bool SoundIsPlaying(Sound sound); // Check if a sound is currently playing
+bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
-void PlayMusicStream(char *fileName); // Start music playing (open stream)
-void UpdateMusicStream(void); // Updates buffers for music streaming
-void StopMusicStream(void); // Stop music playing (close stream)
-void PauseMusicStream(void); // Pause music playing
-void ResumeMusicStream(void); // Resume playing paused music
-bool MusicIsPlaying(void); // Check if music is playing
-void SetMusicVolume(float volume); // Set volume for music (1.0 is max level)
-float GetMusicTimeLength(void); // Get current music time length (in seconds)
-float GetMusicTimePlayed(void); // Get current music time played (in seconds)
+int PlayMusicStream(int index, char *fileName); // Start music playing (open stream)
+void UpdateMusicStream(int index); // Updates buffers for music streaming
+void StopMusicStream(int index); // Stop music playing (close stream)
+void PauseMusicStream(int index); // Pause music playing
+void ResumeMusicStream(int index); // Resume playing paused music
+bool IsMusicPlaying(int index); // Check if music is playing
+void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level)
+float GetMusicTimeLength(int index); // Get music time length (in seconds)
+float GetMusicTimePlayed(int index); // Get current music time played (in seconds)
+int GetMusicStreamCount(void);
+void SetMusicPitch(int index, float pitch);
+
+// used to output raw audio streams, returns negative numbers on error
+// if floating point is false the data size is 16bit short, otherwise it is float 32bit
+RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint);
+
+void CloseRawAudioContext(RawAudioContext ctx);
+int BufferRawAudioContext(RawAudioContext ctx, void *data, unsigned short numberElements); // returns number of elements buffered
#ifdef __cplusplus
}
diff --git a/src/camera.c b/src/camera.c
index 517e4a2b..11571cca 100644
--- a/src/camera.c
+++ b/src/camera.c
@@ -30,7 +30,7 @@
#include "raylib.h"
#endif
-#include <math.h>
+#include <math.h> // Required for: sqrt(), sin(), cos()
//----------------------------------------------------------------------------------
// Defines and Macros
@@ -84,7 +84,7 @@ typedef enum { MOVE_FRONT = 0, MOVE_LEFT, MOVE_BACK, MOVE_RIGHT, MOVE_UP, MOVE_D
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
-static Camera internalCamera = {{ 2.0f, 0.0f, 2.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }};
+static Camera internalCamera = {{ 2.0f, 0.0f, 2.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f };
static Vector2 cameraAngle = { 0.0f, 0.0f };
static float cameraTargetDistance = 5.0f;
static Vector2 cameraMousePosition = { 0.0f, 0.0f };
@@ -212,6 +212,12 @@ void SetCameraTarget(Vector3 target)
cameraTargetDistance = sqrt(dx*dx + dy*dy + dz*dz);
}
+// Set internal camera fovy
+void SetCameraFovy(float fovy)
+{
+ internalCamera.fovy = fovy;
+}
+
// Set camera pan key to combine with mouse movement (free camera)
void SetCameraPanControl(int panKey)
{
diff --git a/src/camera.h b/src/camera.h
index 9ad09c6f..8d8029af 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -81,6 +81,7 @@ void UpdateCameraPlayer(Camera *camera, Vector3 *position); // Update camera and
void SetCameraPosition(Vector3 position); // Set internal camera position
void SetCameraTarget(Vector3 target); // Set internal camera target
+void SetCameraFovy(float fovy); // Set internal camera field-of-view-y
void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera)
void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera)
diff --git a/src/core.c b/src/core.c
index dbcfa6bc..4ba7505b 100644
--- a/src/core.c
+++ b/src/core.c
@@ -9,6 +9,7 @@
* PLATFORM_ANDROID - Only OpenGL ES 2.0 devices
* PLATFORM_RPI - Rapsberry Pi (tested on Raspbian)
* PLATFORM_WEB - Emscripten, HTML5
+* PLATFORM_OCULUS - Oculus Rift CV1 (with desktop mirror)
*
* On PLATFORM_DESKTOP, the external lib GLFW3 (www.glfw.com) is used to manage graphic
* device, OpenGL context and input on multiple operating systems (Windows, Linux, OSX).
@@ -53,14 +54,16 @@
#include <string.h> // String function definitions, memset()
#include <errno.h> // Macros for reporting and retrieving error conditions through error codes
+#if defined(PLATFORM_OCULUS)
+ #define PLATFORM_DESKTOP // Enable PLATFORM_DESKTOP code-base
+#endif
+
#if defined(PLATFORM_DESKTOP)
- #define GLAD_EXTENSIONS_LOADER
- #if defined(GLEW_EXTENSIONS_LOADER)
- #define GLEW_STATIC
- #include <GL/glew.h> // GLEW extensions loading lib
- #elif defined(GLAD_EXTENSIONS_LOADER)
- #include "glad.h" // GLAD library: Manage OpenGL headers and extensions
- #endif
+ #include "external/glad.h" // GLAD library: Manage OpenGL headers and extensions
+#endif
+
+#if defined(PLATFORM_OCULUS)
+ #include "../examples/oculus_glfw_sample/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h" // Oculus SDK for OpenGL
#endif
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
@@ -116,9 +119,9 @@
#if defined(PLATFORM_RPI)
// Old device inputs system
- #define DEFAULT_KEYBOARD_DEV STDIN_FILENO // Standard input
- #define DEFAULT_MOUSE_DEV "/dev/input/mouse0"
- #define DEFAULT_GAMEPAD_DEV "/dev/input/js0"
+ #define DEFAULT_KEYBOARD_DEV STDIN_FILENO // Standard input
+ #define DEFAULT_MOUSE_DEV "/dev/input/mouse0" // Mouse input
+ #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...)
// New device input events (evdev) (must be detected)
//#define DEFAULT_KEYBOARD_DEV "/dev/input/eventN"
@@ -126,13 +129,40 @@
//#define DEFAULT_GAMEPAD_DEV "/dev/input/eventN"
#define MOUSE_SENSITIVITY 0.8f
- #define MAX_GAMEPAD_BUTTONS 11
+
+ #define MAX_GAMEPADS 2 // Max number of gamepads supported
+ #define MAX_GAMEPAD_BUTTONS 11 // Max bumber of buttons supported (per gamepad)
+ #define MAX_GAMEPAD_AXIS 8 // Max number of axis supported (per gamepad)
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
-// ...
+#if defined(PLATFORM_OCULUS)
+typedef struct OculusBuffer {
+ ovrTextureSwapChain textureChain;
+ GLuint depthId;
+ GLuint fboId;
+ int width;
+ int height;
+} OculusBuffer;
+
+typedef struct OculusMirror {
+ ovrMirrorTexture texture;
+ GLuint fboId;
+ int width;
+ int height;
+} OculusMirror;
+
+typedef struct OculusLayer {
+ ovrViewScaleDesc viewScaleDesc;
+ ovrLayerEyeFov eyeLayer; // layer 0
+ //ovrLayerQuad quadLayer; // TODO: layer 1: '2D' quad for GUI
+ Matrix eyeProjections[2];
+ int width;
+ int height;
+} OculusLayer;
+#endif
//----------------------------------------------------------------------------------
// Global Variables Definition
@@ -140,16 +170,23 @@
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
static GLFWwindow *window; // Native window (graphic device)
static bool windowMinimized = false;
-#elif defined(PLATFORM_ANDROID)
+#endif
+
+#if defined(PLATFORM_ANDROID)
static struct android_app *app; // Android activity
static struct android_poll_source *source; // Android events polling source
-static int ident, events;
+static int ident, events; // Android ALooper_pollAll() variables
+static const char *internalDataPath; // Android internal data path to write data (/data/data/<package>/files)
+
static bool windowReady = false; // Used to detect display initialization
static bool appEnabled = true; // Used to detec if app is active
static bool contextRebindRequired = false; // Used to know context rebind required
+
static int previousButtonState[128] = { 1 }; // Required to check if button pressed/released once
static int currentButtonState[128] = { 1 }; // Required to check if button pressed/released once
-#elif defined(PLATFORM_RPI)
+#endif
+
+#if defined(PLATFORM_RPI)
static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device)
// Keyboard input variables
@@ -163,13 +200,12 @@ static bool mouseReady = false; // Flag to know if mouse is read
pthread_t mouseThreadId; // Mouse reading thread id
// Gamepad input variables
-static int gamepadStream = -1; // Gamepad device file descriptor
-static bool gamepadReady = false; // Flag to know if gamepad is ready
-pthread_t gamepadThreadId; // Gamepad reading thread id
+static int gamepadStream[MAX_GAMEPADS] = { -1 }; // Gamepad device file descriptor (two gamepads supported)
+static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready (two gamepads supported)
+pthread_t gamepadThreadId; // Gamepad reading thread id
-int gamepadButtons[MAX_GAMEPAD_BUTTONS];
-int gamepadAxisX = 0;
-int gamepadAxisY = 0;
+int gamepadButtons[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Gamepad buttons state
+float gamepadAxisValues[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
@@ -181,6 +217,17 @@ static uint64_t baseTime; // Base time measure for hi-res time
static bool windowShouldClose = false; // Flag to set window for closing
#endif
+#if defined(PLATFORM_OCULUS)
+// OVR device variables
+static ovrSession session;
+static ovrHmdDesc hmdDesc;
+static ovrGraphicsLuid luid;
+static OculusLayer layer;
+static OculusBuffer buffer;
+static OculusMirror mirror;
+static unsigned int frameIndex = 0;
+#endif
+
static unsigned int displayWidth, displayHeight; // Display width and height (monitor, device-screen, LCD, ...)
static int screenWidth, screenHeight; // Screen width and height (used render area)
static int renderWidth, renderHeight; // Framebuffer width and height (render area)
@@ -190,29 +237,21 @@ static int renderOffsetX = 0; // Offset X from render area (must b
static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2)
static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP)
static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size)
-
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
-static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen
-#endif
+static Matrix cameraView; // Store camera view matrix (required for Oculus Rift)
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
static const char *windowTitle; // Window text title...
-
-static bool customCursor = false; // Tracks if custom cursor has been set
static bool cursorOnScreen = false; // Tracks if cursor is inside client area
-static Texture2D cursor; // Cursor texture
-
-static Vector2 mousePosition; // Mouse position on screen
static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once
static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once
-static char previousMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
-static char currentMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
-
static char previousGamepadState[32] = {0}; // Required to check if gamepad btn pressed/released once
static char currentGamepadState[32] = {0}; // Required to check if gamepad btn pressed/released once
+static char previousMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
+static char currentMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
+
static int previousMouseWheelY = 0; // Required to track mouse wheel variation
static int currentMouseWheelY = 0; // Required to track mouse wheel variation
@@ -222,6 +261,9 @@ static int lastKeyPressed = -1; // Register last key pressed
static bool cursorHidden; // Track if cursor is hidden
#endif
+static Vector2 mousePosition; // Mouse position on screen
+static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen
+
#if defined(PLATFORM_DESKTOP)
static char **dropFilesPath; // Store dropped files paths as strings
static int dropFilesCount = 0; // Count stored strings
@@ -246,23 +288,16 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads defaul
//----------------------------------------------------------------------------------
static void InitDisplay(int width, int height); // Initialize display device and framebuffer
static void InitGraphics(void); // Initialize OpenGL graphics
+static void SetupFramebufferSize(int displayWidth, int displayHeight);
static void InitTimer(void); // Initialize timer
static double GetTime(void); // Returns time since InitTimer() was run
static bool GetKeyStatus(int key); // Returns if a key has been pressed
static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed
-static void SwapBuffers(void); // Copy back buffer to front buffers
static void PollInputEvents(void); // Register user events
+static void SwapBuffers(void); // Copy back buffer to front buffers
static void LogoAnimation(void); // Plays raylib logo appearing animation
-static void SetupFramebufferSize(int displayWidth, int displayHeight);
-
-#if defined(PLATFORM_RPI)
-static void InitKeyboard(void); // Init raw keyboard system (standard input reading)
-static void ProcessKeyboard(void); // Process keyboard events
-static void RestoreKeyboard(void); // Restore keyboard system
-static void InitMouse(void); // Mouse initialization (including mouse thread)
-static void *MouseThread(void *arg); // Mouse reading thread
-static void InitGamepad(void); // Init raw gamepad input
-static void *GamepadThread(void *arg); // Mouse reading thread
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
+static void TakeScreenshot(void); // Takes a screenshot and saves it in the same folder as executable
#endif
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
@@ -281,10 +316,6 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified);
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
#endif
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
-static void TakeScreenshot(void); // Takes a screenshot and saves it in the same folder as executable
-#endif
-
#if defined(PLATFORM_ANDROID)
static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs
@@ -295,6 +326,29 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte
static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
#endif
+#if defined(PLATFORM_RPI)
+static void InitKeyboard(void); // Init raw keyboard system (standard input reading)
+static void ProcessKeyboard(void); // Process keyboard events
+static void RestoreKeyboard(void); // Restore keyboard system
+static void InitMouse(void); // Mouse initialization (including mouse thread)
+static void *MouseThread(void *arg); // Mouse reading thread
+static void InitGamepad(void); // Init raw gamepad input
+static void *GamepadThread(void *arg); // Mouse reading thread
+#endif
+
+#if defined(PLATFORM_OCULUS)
+// Oculus Rift functions
+static Matrix FromOvrMatrix(ovrMatrix4f ovrM);
+static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height);
+static void UnloadOculusBuffer(ovrSession session, OculusBuffer buffer);
+static void SetOculusBuffer(ovrSession session, OculusBuffer buffer);
+static void UnsetOculusBuffer(OculusBuffer buffer);
+static OculusMirror LoadOculusMirror(ovrSession session, int width, int height); // Load Oculus mirror buffers
+static void UnloadOculusMirror(ovrSession session, OculusMirror mirror); // Unload Oculus mirror buffers
+static void BlitOculusMirror(ovrSession session, OculusMirror mirror);
+static OculusLayer InitOculusLayer(ovrSession session);
+#endif
+
//----------------------------------------------------------------------------------
// Module Functions Definition - Window and OpenGL Context Functions
//----------------------------------------------------------------------------------
@@ -302,7 +356,7 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent
// Initialize Window and Graphics Context (OpenGL)
void InitWindow(int width, int height, const char *title)
{
- TraceLog(INFO, "Initializing raylib (v1.4.0)");
+ TraceLog(INFO, "Initializing raylib (v1.5.0)");
// Store window title (could be useful...)
windowTitle = title;
@@ -343,6 +397,11 @@ void InitWindow(int width, int height, const char *title)
//emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenInputCallback);
#endif
+#if defined(PLATFORM_OCULUS)
+ // Recenter OVR tracking origin
+ ovr_RecenterTrackingOrigin(session);
+#endif
+
mousePosition.x = (float)screenWidth/2.0f;
mousePosition.y = (float)screenHeight/2.0f;
@@ -353,12 +412,13 @@ void InitWindow(int width, int height, const char *title)
LogoAnimation();
}
}
+#endif
-#elif defined(PLATFORM_ANDROID)
+#if defined(PLATFORM_ANDROID)
// Android activity initialization
void InitWindow(int width, int height, struct android_app *state)
{
- TraceLog(INFO, "Initializing raylib (v1.4.0)");
+ TraceLog(INFO, "Initializing raylib (v1.5.0)");
app_dummy();
@@ -366,6 +426,7 @@ void InitWindow(int width, int height, struct android_app *state)
screenHeight = height;
app = state;
+ internalDataPath = app->activity->internalDataPath;
// Set desired windows flags before initializing anything
ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
@@ -401,13 +462,6 @@ void InitWindow(int width, int height, struct android_app *state)
TraceLog(INFO, "Android app initialized successfully");
- // Init button states values (default up)
- for(int i = 0; i < 128; i++)
- {
- currentButtonState[i] = 1;
- previousButtonState[i] = 1;
- }
-
// Wait for window to be initialized (display and context)
while (!windowReady)
{
@@ -434,7 +488,9 @@ void CloseWindow(void)
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
glfwDestroyWindow(window);
glfwTerminate();
-#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+#endif
+
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
// Close surface, context and display
if (display != EGL_NO_DISPLAY)
{
@@ -457,9 +513,71 @@ void CloseWindow(void)
}
#endif
+#if defined(PLATFORM_OCULUS)
+ ovr_Destroy(session); // Must be called after glfwTerminate()
+ ovr_Shutdown();
+#endif
+
TraceLog(INFO, "Window closed successfully");
}
+#if defined(PLATFORM_OCULUS)
+// Init Oculus Rift device
+// NOTE: Device initialization should be done before window creation?
+void InitOculusDevice(void)
+{
+ ovrResult result = ovr_Initialize(NULL);
+ if (OVR_FAILURE(result)) TraceLog(ERROR, "OVR: Could not initialize Oculus device");
+
+ result = ovr_Create(&session, &luid);
+ if (OVR_FAILURE(result))
+ {
+ TraceLog(WARNING, "OVR: Could not create Oculus session");
+ ovr_Shutdown();
+ }
+
+ hmdDesc = ovr_GetHmdDesc(session);
+
+ TraceLog(INFO, "OVR: Product Name: %s", hmdDesc.ProductName);
+ TraceLog(INFO, "OVR: Manufacturer: %s", hmdDesc.Manufacturer);
+ TraceLog(INFO, "OVR: Product ID: %i", hmdDesc.ProductId);
+ TraceLog(INFO, "OVR: Product Type: %i", hmdDesc.Type);
+ TraceLog(INFO, "OVR: Serian Number: %s", hmdDesc.SerialNumber);
+ TraceLog(INFO, "OVR: Resolution: %ix%i", hmdDesc.Resolution.w, hmdDesc.Resolution.h);
+
+ screenWidth = hmdDesc.Resolution.w/2;
+ screenHeight = hmdDesc.Resolution.h/2;
+
+ // Initialize Oculus Buffers
+ layer = InitOculusLayer(session);
+ buffer = LoadOculusBuffer(session, layer.width, layer.height);
+ mirror = LoadOculusMirror(session, hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2);
+ layer.eyeLayer.ColorTexture[0] = buffer.textureChain; //SetOculusLayerTexture(eyeLayer, buffer.textureChain);
+}
+
+// Close Oculus Rift device
+void CloseOculusDevice(void)
+{
+ UnloadOculusMirror(session, mirror); // Unload Oculus mirror buffer
+ UnloadOculusBuffer(session, buffer); // Unload Oculus texture buffers
+
+ ovr_Destroy(session); // Must be called after glfwTerminate() --> REALLY???
+ ovr_Shutdown();
+}
+
+// Update Oculus Rift tracking (position and orientation)
+void UpdateOculusTracking(void)
+{
+ frameIndex++;
+
+ ovrPosef eyePoses[2];
+ ovr_GetEyePoses(session, frameIndex, ovrTrue, layer.viewScaleDesc.HmdToEyeOffset, eyePoses, &layer.eyeLayer.SensorSampleTime);
+
+ layer.eyeLayer.RenderPose[0] = eyePoses[0];
+ layer.eyeLayer.RenderPose[1] = eyePoses[1];
+}
+#endif
+
// Detect if KEY_ESCAPE pressed or Close icon pressed
bool WindowShouldClose(void)
{
@@ -468,7 +586,9 @@ bool WindowShouldClose(void)
while (windowMinimized) glfwPollEvents();
return (glfwWindowShouldClose(window));
-#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+#endif
+
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
return windowShouldClose;
#endif
}
@@ -494,34 +614,13 @@ void ToggleFullscreen(void)
glfwDestroyWindow(window); // Destroy the current window (we will recreate it!)
InitWindow(screenWidth, screenHeight, windowTitle);
-#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
- TraceLog(WARNING, "Could not toggle to windowed mode");
#endif
-}
-
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
-// Set a custom cursor icon/image
-void SetCustomCursor(const char *cursorImage)
-{
- if (customCursor) UnloadTexture(cursor);
-
- cursor = LoadTexture(cursorImage);
-#if defined(PLATFORM_DESKTOP)
- // NOTE: emscripten not implemented
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+ TraceLog(WARNING, "Could not toggle to windowed mode");
#endif
- customCursor = true;
}
-// Set a custom key to exit program
-// NOTE: default exitKey is ESCAPE
-void SetExitKey(int key)
-{
- exitKey = key;
-}
-#endif
-
// Get current screen width
int GetScreenWidth(void)
{
@@ -547,41 +646,78 @@ void BeginDrawing(void)
currentTime = GetTime(); // Number of elapsed seconds since InitTimer() was called
updateTime = currentTime - previousTime;
previousTime = currentTime;
+
+#if defined(PLATFORM_OCULUS)
+ frameIndex++;
+
+ ovrPosef eyePoses[2];
+ ovr_GetEyePoses(session, frameIndex, ovrTrue, layer.viewScaleDesc.HmdToEyeOffset, eyePoses, &layer.eyeLayer.SensorSampleTime);
+
+ layer.eyeLayer.RenderPose[0] = eyePoses[0];
+ layer.eyeLayer.RenderPose[1] = eyePoses[1];
+
+ SetOculusBuffer(session, buffer);
+#endif
- if (IsPosproShaderEnabled()) rlEnablePostproFBO();
-
- rlClearScreenBuffers();
-
+ rlClearScreenBuffers(); // Clear current framebuffers
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
-
rlMultMatrixf(MatrixToFloat(downscaleView)); // If downscale required, apply it here
//rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
// NOTE: Not required with OpenGL 3.3+
}
-// Setup drawing canvas with extended parameters
-void BeginDrawingEx(int blendMode, Shader shader, Matrix transform)
+// End canvas drawing and Swap Buffers (Double Buffering)
+void EndDrawing(void)
{
- BeginDrawing();
+#if defined(PLATFORM_OCULUS)
+ for (int eye = 0; eye < 2; eye++)
+ {
+ rlViewport(layer.eyeLayer.Viewport[eye].Pos.x, layer.eyeLayer.Viewport[eye].Pos.y, layer.eyeLayer.Viewport[eye].Size.w, layer.eyeLayer.Viewport[eye].Size.h);
+
+ Quaternion eyeRPose = (Quaternion){ layer.eyeLayer.RenderPose[eye].Orientation.x,
+ layer.eyeLayer.RenderPose[eye].Orientation.y,
+ layer.eyeLayer.RenderPose[eye].Orientation.z,
+ layer.eyeLayer.RenderPose[eye].Orientation.w };
+ QuaternionInvert(&eyeRPose);
+ Matrix eyeOrientation = QuaternionToMatrix(eyeRPose);
+ Matrix eyeTranslation = MatrixTranslate(-layer.eyeLayer.RenderPose[eye].Position.x,
+ -layer.eyeLayer.RenderPose[eye].Position.y,
+ -layer.eyeLayer.RenderPose[eye].Position.z);
+
+ Matrix eyeView = MatrixMultiply(eyeTranslation, eyeOrientation);
+ Matrix modelEyeView = MatrixMultiply(cameraView, eyeView); // Using internal camera modelview matrix
+
+ SetMatrixModelview(modelEyeView);
+ SetMatrixProjection(layer.eyeProjections[eye]);
+#endif
- SetBlendMode(blendMode);
- SetPostproShader(shader);
+ rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
+
+#if defined(PLATFORM_OCULUS)
+ }
- rlMultMatrixf(MatrixToFloat(transform));
-}
+ UnsetOculusBuffer(buffer);
+
+ ovr_CommitTextureSwapChain(session, buffer.textureChain);
+
+ ovrLayerHeader *layers = &layer.eyeLayer.Header;
+ ovr_SubmitFrame(session, frameIndex, &layer.viewScaleDesc, &layers, 1);
-// End canvas drawing and Swap Buffers (Double Buffering)
-void EndDrawing(void)
-{
- rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
+ // Blit mirror texture to back buffer
+ BlitOculusMirror(session, mirror);
- if (IsPosproShaderEnabled()) rlglDrawPostpro(); // Draw postprocessing effect (shader)
+ // Get session status information
+ ovrSessionStatus sessionStatus;
+ ovr_GetSessionStatus(session, &sessionStatus);
+ if (sessionStatus.ShouldQuit) TraceLog(WARNING, "OVR: Session should quit...");
+ if (sessionStatus.ShouldRecenter) ovr_RecenterTrackingOrigin(session);
+#endif
SwapBuffers(); // Copy back buffer to front buffer
-
PollInputEvents(); // Poll user events
+ // Frame time control system
currentTime = GetTime();
drawTime = currentTime - previousTime;
previousTime = currentTime;
@@ -600,6 +736,32 @@ void EndDrawing(void)
}
}
+// Initialize 2D mode with custom camera
+void Begin2dMode(Camera2D camera)
+{
+ rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
+
+ rlLoadIdentity(); // Reset current matrix (MODELVIEW)
+
+ // Camera rotation and scaling is always relative to target
+ Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
+ Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
+ Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
+ Matrix matTranslation = MatrixTranslate(camera.offset.x + camera.target.x, camera.offset.y + camera.target.y, 0.0f);
+
+ Matrix matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
+
+ rlMultMatrixf(MatrixToFloat(matTransform));
+}
+
+// Ends 2D mode custom camera usage
+void End2dMode(void)
+{
+ rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
+
+ rlLoadIdentity(); // Reset current matrix (MODELVIEW)
+}
+
// Initializes 3D mode for drawing (Camera setup)
void Begin3dMode(Camera camera)
{
@@ -609,21 +771,23 @@ void Begin3dMode(Camera camera)
rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
rlLoadIdentity(); // Reset current matrix (PROJECTION)
-
+
// Setup perspective projection
float aspect = (float)screenWidth/(float)screenHeight;
- double top = 0.1*tan(45.0*PI/360.0);
+ double top = 0.01*tan(camera.fovy*PI/360.0);
double right = top*aspect;
// NOTE: zNear and zFar values are important when computing depth buffer values
- rlFrustum(-right, right, -top, top, 0.1f, 1000.0f);
+ rlFrustum(-right, right, -top, top, 0.01, 1000.0);
rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
// Setup Camera view
- Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
- rlMultMatrixf(MatrixToFloat(matView)); // Multiply MODELVIEW matrix by view matrix (camera)
+ cameraView = MatrixLookAt(camera.position, camera.target, camera.up);
+ rlMultMatrixf(MatrixToFloat(cameraView)); // Multiply MODELVIEW matrix by view matrix (camera)
+
+ rlEnableDepthTest(); // Enable DEPTH_TEST for 3D
}
// Ends 3D mode and returns to default 2D orthographic mode
@@ -638,6 +802,55 @@ void End3dMode(void)
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
//rlTranslatef(0.375, 0.375, 0); // HACK to ensure pixel-perfect drawing on OpenGL (after exiting 3D mode)
+
+ rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
+}
+
+// Initializes render texture for drawing
+void BeginTextureMode(RenderTexture2D target)
+{
+ rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
+
+ rlEnableRenderTexture(target.id); // Enable render target
+
+ rlClearScreenBuffers(); // Clear render texture buffers
+
+ // Set viewport to framebuffer size
+ rlViewport(0, 0, target.texture.width, target.texture.height);
+
+ rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix
+ rlLoadIdentity(); // Reset current matrix (PROJECTION)
+
+ // Set orthographic projection to current framebuffer size
+ // NOTE: Configured top-left corner as (0, 0)
+ rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
+
+ rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
+ rlLoadIdentity(); // Reset current matrix (MODELVIEW)
+
+ //rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?)
+}
+
+// Ends drawing to render texture
+void EndTextureMode(void)
+{
+ rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
+
+ rlDisableRenderTexture(); // Disable render target
+
+ // Set viewport to default framebuffer size (screen size)
+ // TODO: consider possible viewport offsets
+ rlViewport(0, 0, GetScreenWidth(), GetScreenHeight());
+
+ rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix
+ rlLoadIdentity(); // Reset current matrix (PROJECTION)
+
+ // Set orthographic projection to current framebuffer size
+ // NOTE: Configured top-left corner as (0, 0)
+ rlOrtho(0, GetScreenWidth(), GetScreenHeight(), 0, 0.0f, 1.0f);
+
+ rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
+ rlLoadIdentity(); // Reset current matrix (MODELVIEW)
}
// Set target FPS for the game
@@ -810,12 +1023,21 @@ void ClearDroppedFiles(void)
void StorageSaveValue(int position, int value)
{
FILE *storageFile = NULL;
+
+ char path[128];
+#if defined(PLATFORM_ANDROID)
+ strcpy(path, internalDataPath);
+ strcat(path, "/");
+ strcat(path, STORAGE_FILENAME);
+#else
+ strcpy(path, STORAGE_FILENAME);
+#endif
// Try open existing file to append data
- storageFile = fopen(STORAGE_FILENAME, "rb+");
+ storageFile = fopen(path, "rb+");
// If file doesn't exist, create a new storage data file
- if (!storageFile) storageFile = fopen(STORAGE_FILENAME, "wb");
+ if (!storageFile) storageFile = fopen(path, "wb");
if (!storageFile) TraceLog(WARNING, "Storage data file could not be created");
else
@@ -842,8 +1064,17 @@ int StorageLoadValue(int position)
{
int value = 0;
+ char path[128];
+#if defined(PLATFORM_ANDROID)
+ strcpy(path, internalDataPath);
+ strcat(path, "/");
+ strcat(path, STORAGE_FILENAME);
+#else
+ strcpy(path, STORAGE_FILENAME);
+#endif
+
// Try open existing file to append data
- FILE *storageFile = fopen(STORAGE_FILENAME, "rb");
+ FILE *storageFile = fopen(path, "rb");
if (!storageFile) TraceLog(WARNING, "Storage data file could not be found");
else
@@ -867,16 +1098,8 @@ int StorageLoadValue(int position)
}
// Returns a ray trace from mouse position
-//http://www.songho.ca/opengl/gl_transform.html
-//http://www.songho.ca/opengl/gl_matrix.html
-//http://www.sjbaker.org/steve/omniv/matrices_can_be_your_friends.html
-//https://www.opengl.org/archives/resources/faq/technical/transformations.htm
Ray GetMouseRay(Vector2 mousePosition, Camera camera)
-{
- // Tutorial used: https://mkonrad.net/2014/08/07/simple-opengl-object-picking-in-3d.html
- // Similar to http://antongerdelan.net, the problem is maybe in MatrixPerspective vs MatrixFrustum
- // or matrix order (transpose it or not... that's the question)
-
+{
Ray ray;
// Calculate normalized device coordinates
@@ -886,40 +1109,48 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
float z = 1.0f;
// Store values in a vector
- Vector3 deviceCoords = {x, y, z};
+ Vector3 deviceCoords = { x, y, z };
- // Device debug message
- TraceLog(INFO, "device(%f, %f, %f)", deviceCoords.x, deviceCoords.y, deviceCoords.z);
+ TraceLog(DEBUG, "Device coordinates: (%f, %f, %f)", deviceCoords.x, deviceCoords.y, deviceCoords.z);
- // Calculate projection matrix (from perspective instead of frustum
- Matrix matProj = MatrixPerspective(45.0f, (float)((float)GetScreenWidth() / (float)GetScreenHeight()), 0.01f, 1000.0f);
+ // Calculate projection matrix (from perspective instead of frustum)
+ Matrix matProj = MatrixPerspective(camera.fovy, ((double)GetScreenWidth()/(double)GetScreenHeight()), 0.01, 1000.0);
// Calculate view matrix from camera look at
Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
// Do I need to transpose it? It seems that yes...
- // NOTE: matrix order is maybe incorrect... In OpenGL to get world position from
+ // NOTE: matrix order may be incorrect... In OpenGL to get world position from
// camera view it just needs to get inverted, but here we need to transpose it too.
// For example, if you get view matrix, transpose and inverted and you transform it
// to a vector, you will get its 3d world position coordinates (camera.position).
// If you don't transpose, final position will be wrong.
MatrixTranspose(&matView);
+//#define USE_RLGL_UNPROJECT
+#if defined(USE_RLGL_UNPROJECT) // OPTION 1: Use rlglUnproject()
+
+ Vector3 nearPoint = rlglUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
+ Vector3 farPoint = rlglUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
+
+#else // OPTION 2: Compute unprojection directly here
+
// Calculate unproject matrix (multiply projection matrix and view matrix) and invert it
Matrix matProjView = MatrixMultiply(matProj, matView);
MatrixInvert(&matProjView);
// Calculate far and near points
- Quaternion near = { deviceCoords.x, deviceCoords.y, 0.0f, 1.0f};
- Quaternion far = { deviceCoords.x, deviceCoords.y, 1.0f, 1.0f};
+ Quaternion near = { deviceCoords.x, deviceCoords.y, 0.0f, 1.0f };
+ Quaternion far = { deviceCoords.x, deviceCoords.y, 1.0f, 1.0f };
// Multiply points by unproject matrix
QuaternionTransform(&near, matProjView);
QuaternionTransform(&far, matProjView);
// Calculate normalized world points in vectors
- Vector3 nearPoint = {near.x / near.w, near.y / near.w, near.z / near.w};
- Vector3 farPoint = {far.x / far.w, far.y / far.w, far.z / far.w};
+ Vector3 nearPoint = { near.x/near.w, near.y/near.w, near.z/near.w};
+ Vector3 farPoint = { far.x/far.w, far.y/far.w, far.z/far.w};
+#endif
// Calculate normalized direction vector
Vector3 direction = VectorSubtract(farPoint, nearPoint);
@@ -933,10 +1164,10 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
}
// Returns the screen space position from a 3d world space position
-Vector2 WorldToScreen(Vector3 position, Camera camera)
+Vector2 GetWorldToScreen(Vector3 position, Camera camera)
{
// Calculate projection matrix (from perspective instead of frustum
- Matrix matProj = MatrixPerspective(45.0f, (float)((float)GetScreenWidth() / (float)GetScreenHeight()), 0.01f, 1000.0f);
+ Matrix matProj = MatrixPerspective(camera.fovy, (double)GetScreenWidth()/(double)GetScreenHeight(), 0.01, 1000.0);
// Calculate view matrix from camera look at (and transpose it)
Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
@@ -1012,78 +1243,11 @@ int GetKeyPressed(void)
return lastKeyPressed;
}
-// Detect if a mouse button has been pressed once
-bool IsMouseButtonPressed(int button)
-{
- bool pressed = false;
-
- if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true;
- else pressed = false;
-
- return pressed;
-}
-
-// Detect if a mouse button is being pressed
-bool IsMouseButtonDown(int button)
-{
- if (GetMouseButtonStatus(button) == 1) return true;
- else return false;
-}
-
-// Detect if a mouse button has been released once
-bool IsMouseButtonReleased(int button)
-{
- bool released = false;
-
- if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 0)) released = true;
- else released = false;
-
- return released;
-}
-
-// Detect if a mouse button is NOT being pressed
-bool IsMouseButtonUp(int button)
-{
- if (GetMouseButtonStatus(button) == 0) return true;
- else return false;
-}
-
-// Returns mouse position X
-int GetMouseX(void)
-{
- return (int)mousePosition.x;
-}
-
-// Returns mouse position Y
-int GetMouseY(void)
-{
- return (int)mousePosition.y;
-}
-
-// Returns mouse position XY
-Vector2 GetMousePosition(void)
-{
- return mousePosition;
-}
-
-// Set mouse position XY
-void SetMousePosition(Vector2 position)
-{
- mousePosition = position;
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
- // NOTE: emscripten not implemented
- glfwSetCursorPos(window, position.x, position.y);
-#endif
-}
-
-// Returns mouse wheel movement Y
-int GetMouseWheelMove(void)
+// Set a custom key to exit program
+// NOTE: default exitKey is ESCAPE
+void SetExitKey(int key)
{
-#if defined(PLATFORM_WEB)
- return previousMouseWheelY/100;
-#else
- return previousMouseWheelY;
-#endif
+ exitKey = key;
}
// Hide mouse cursor
@@ -1151,7 +1315,7 @@ bool IsGamepadAvailable(int gamepad)
bool result = false;
#if defined(PLATFORM_RPI)
- if (gamepadReady && (gamepad == 0)) result = true;
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad]) result = true;
#else
if (glfwJoystickPresent(gamepad) == 1) result = true;
#endif
@@ -1160,30 +1324,25 @@ bool IsGamepadAvailable(int gamepad)
}
// Return axis movement vector for a gamepad
-Vector2 GetGamepadMovement(int gamepad)
+float GetGamepadAxisMovement(int gamepad, int axis)
{
- Vector2 vec = { 0, 0 };
-
- const float *axes;
- int axisCount = 0;
+ float value = 0;
#if defined(PLATFORM_RPI)
- // TODO: Get gamepad axis information
- // Use gamepadAxisX, gamepadAxisY
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad])
+ {
+ if (axis < MAX_GAMEPAD_AXIS) value = gamepadAxisValues[gamepad][axis];
+ }
#else
+ const float *axes;
+ int axisCount = 0;
+
axes = glfwGetJoystickAxes(gamepad, &axisCount);
-#endif
- if (axisCount >= 2)
- {
- vec.x = axes[0]; // Left joystick X
- vec.y = axes[1]; // Left joystick Y
-
- //vec.x = axes[2]; // Right joystick X
- //vec.x = axes[3]; // Right joystick Y
- }
+ if (axis < axisCount) value = axes[axis];
+#endif
- return vec;
+ return value;
}
// Detect if a gamepad button has been pressed once
@@ -1210,7 +1369,7 @@ bool IsGamepadButtonDown(int gamepad, int button)
#if defined(PLATFORM_RPI)
// Get gamepad buttons information
- if ((gamepad == 0) && (gamepadButtons[button] == 1)) result = true;
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (gamepadButtons[gamepad][button] == 1)) result = true;
else result = false;
#else
const unsigned char *buttons;
@@ -1249,7 +1408,7 @@ bool IsGamepadButtonUp(int gamepad, int button)
#if defined(PLATFORM_RPI)
// Get gamepad buttons information
- if ((gamepad == 0) && (gamepadButtons[button] == 0)) result = true;
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (gamepadButtons[gamepad][button] == 0)) result = true;
else result = false;
#else
const unsigned char *buttons;
@@ -1265,6 +1424,111 @@ bool IsGamepadButtonUp(int gamepad, int button)
}
#endif //defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
+
+// Detect if a mouse button has been pressed once
+bool IsMouseButtonPressed(int button)
+{
+ bool pressed = false;
+
+#if defined(PLATFORM_ANDROID)
+ if (IsGestureDetected(GESTURE_TAP)) pressed = true;
+#else
+ if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true;
+#endif
+
+ return pressed;
+}
+
+// Detect if a mouse button is being pressed
+bool IsMouseButtonDown(int button)
+{
+ bool down = false;
+
+#if defined(PLATFORM_ANDROID)
+ if (IsGestureDetected(GESTURE_HOLD)) down = true;
+#else
+ if (GetMouseButtonStatus(button) == 1) down = true;
+#endif
+
+ return down;
+}
+
+// Detect if a mouse button has been released once
+bool IsMouseButtonReleased(int button)
+{
+ bool released = false;
+
+#if !defined(PLATFORM_ANDROID)
+ if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 0)) released = true;
+#endif
+
+ return released;
+}
+
+// Detect if a mouse button is NOT being pressed
+bool IsMouseButtonUp(int button)
+{
+ bool up = false;
+
+#if !defined(PLATFORM_ANDROID)
+ if (GetMouseButtonStatus(button) == 0) up = true;
+#endif
+
+ return up;
+}
+
+// Returns mouse position X
+int GetMouseX(void)
+{
+#if defined(PLATFORM_ANDROID)
+ return (int)touchPosition[0].x;
+#else
+ return (int)mousePosition.x;
+#endif
+}
+
+// Returns mouse position Y
+int GetMouseY(void)
+{
+#if defined(PLATFORM_ANDROID)
+ return (int)touchPosition[0].x;
+#else
+ return (int)mousePosition.y;
+#endif
+}
+
+// Returns mouse position XY
+Vector2 GetMousePosition(void)
+{
+#if defined(PLATFORM_ANDROID)
+ return GetTouchPosition(0);
+#else
+ return mousePosition;
+#endif
+}
+
+// Set mouse position XY
+void SetMousePosition(Vector2 position)
+{
+ mousePosition = position;
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
+ // NOTE: emscripten not implemented
+ glfwSetCursorPos(window, position.x, position.y);
+#endif
+}
+
+// Returns mouse wheel movement Y
+int GetMouseWheelMove(void)
+{
+#if defined(PLATFORM_ANDROID)
+ return 0;
+#elif defined(PLATFORM_WEB)
+ return previousMouseWheelY/100;
+#else
+ return previousMouseWheelY;
+#endif
+}
+
// Returns touch position X
int GetTouchX(void)
{
@@ -1361,7 +1625,31 @@ static void InitDisplay(int width, int height)
// Downscale matrix is required in case desired screen area is bigger than display area
downscaleView = MatrixIdentity();
+
+#if defined(PLATFORM_OCULUS)
+ ovrResult result = ovr_Initialize(NULL);
+ if (OVR_FAILURE(result)) TraceLog(ERROR, "OVR: Could not initialize Oculus device");
+ result = ovr_Create(&session, &luid);
+ if (OVR_FAILURE(result))
+ {
+ TraceLog(WARNING, "OVR: Could not create Oculus session");
+ ovr_Shutdown();
+ }
+
+ hmdDesc = ovr_GetHmdDesc(session);
+
+ TraceLog(INFO, "OVR: Product Name: %s", hmdDesc.ProductName);
+ TraceLog(INFO, "OVR: Manufacturer: %s", hmdDesc.Manufacturer);
+ TraceLog(INFO, "OVR: Product ID: %i", hmdDesc.ProductId);
+ TraceLog(INFO, "OVR: Product Type: %i", hmdDesc.Type);
+ TraceLog(INFO, "OVR: Serian Number: %s", hmdDesc.SerialNumber);
+ TraceLog(INFO, "OVR: Resolution: %ix%i", hmdDesc.Resolution.w, hmdDesc.Resolution.h);
+
+ screenWidth = hmdDesc.Resolution.w/2;
+ screenHeight = hmdDesc.Resolution.h/2;
+#endif
+
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
glfwSetErrorCallback(ErrorCallback);
@@ -1378,10 +1666,12 @@ static void InitDisplay(int width, int height)
// Screen size security check
if (screenWidth <= 0) screenWidth = displayWidth;
if (screenHeight <= 0) screenHeight = displayHeight;
-#elif defined(PLATFORM_WEB)
+#endif // defined(PLATFORM_DESKTOP)
+
+#if defined(PLATFORM_WEB)
displayWidth = screenWidth;
displayHeight = screenHeight;
-#endif
+#endif // defined(PLATFORM_WEB)
glfwDefaultWindowHints(); // Set default windows hints
@@ -1410,7 +1700,12 @@ static void InitDisplay(int width, int height)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
// Other values: GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
+#ifdef __APPLE__
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OSX Requires
+#else
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); // Fordward Compatibility Hint: Only 3.3 and above!
+#endif
+ //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
}
if (fullscreen)
@@ -1432,7 +1727,7 @@ static void InitDisplay(int width, int height)
// TODO: Check modes[i]->width;
// TODO: Check modes[i]->height;
}
-
+
window = glfwCreateWindow(screenWidth, screenHeight, windowTitle, glfwGetPrimaryMonitor(), NULL);
}
else
@@ -1470,11 +1765,11 @@ static void InitDisplay(int width, int height)
TraceLog(INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY);
}
- glfwSetWindowSizeCallback(window, WindowSizeCallback);
+ glfwSetWindowSizeCallback(window, WindowSizeCallback); // NOTE: Resizing not allowed by default!
glfwSetCursorEnterCallback(window, CursorEnterCallback);
glfwSetKeyCallback(window, KeyCallback);
glfwSetMouseButtonCallback(window, MouseButtonCallback);
- glfwSetCursorPosCallback(window, MouseCursorPosCallback); // Track mouse position changes
+ glfwSetCursorPosCallback(window, MouseCursorPosCallback); // Track mouse position changes
glfwSetCharCallback(window, CharCallback);
glfwSetScrollCallback(window, ScrollCallback);
glfwSetWindowIconifyCallback(window, WindowIconifyCallback);
@@ -1483,38 +1778,23 @@ static void InitDisplay(int width, int height)
#endif
glfwMakeContextCurrent(window);
+#if defined(PLATFORM_OCULUS)
+ glfwSwapInterval(0);
+#endif
#if defined(PLATFORM_DESKTOP)
- // Extensions initialization for OpenGL 3.3
+ // Load OpenGL 3.3 extensions using GLAD
if (rlGetVersion() == OPENGL_33)
{
- #if defined(GLEW_EXTENSIONS_LOADER)
- // Initialize extensions using GLEW
- glewExperimental = 1; // Needed for core profile
- GLenum error = glewInit();
-
- if (error != GLEW_OK) TraceLog(ERROR, "Failed to initialize GLEW - Error Code: %s\n", glewGetErrorString(error));
-
- if (glewIsSupported("GL_VERSION_3_3")) TraceLog(INFO, "OpenGL 3.3 Core profile supported");
- else TraceLog(ERROR, "OpenGL 3.3 Core profile not supported");
-
- // With GLEW, we can check if an extension has been loaded in two ways:
- //if (GLEW_ARB_vertex_array_object) { }
- //if (glewIsSupported("GL_ARB_vertex_array_object")) { }
-
- // NOTE: GLEW is a big library that loads ALL extensions, we can use some alternative to load only required ones
- // Alternatives: glLoadGen, glad, libepoxy
- #elif defined(GLAD_EXTENSIONS_LOADER)
- // NOTE: glad is generated and contains only required OpenGL version and Core extensions
- //if (!gladLoadGL()) TraceLog(ERROR, "Failed to initialize glad\n");
- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) TraceLog(ERROR, "Failed to initialize glad\n"); // No GLFW3 in this module...
-
- if (GLAD_GL_VERSION_3_3) TraceLog(INFO, "OpenGL 3.3 Core profile supported");
- else TraceLog(ERROR, "OpenGL 3.3 Core profile not supported");
-
- // With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans
- //if (GLAD_GL_ARB_vertex_array_object) // Use GL_ARB_vertex_array_object
- #endif
+ // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions
+ if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) TraceLog(WARNING, "GLAD: Cannot load OpenGL extensions");
+ else TraceLog(INFO, "GLAD: OpenGL extensions loaded successfully");
+
+ if (GLAD_GL_VERSION_3_3) TraceLog(INFO, "OpenGL 3.3 Core profile supported");
+ else TraceLog(ERROR, "OpenGL 3.3 Core profile not supported");
+
+ // With GLAD, we can check if an extension is supported using the GLAD_GL_xxx booleans
+ //if (GLAD_GL_ARB_vertex_array_object) // Use GL_ARB_vertex_array_object
}
#endif
@@ -1528,8 +1808,9 @@ static void InitDisplay(int width, int height)
}
//glfwGetFramebufferSize(window, &renderWidth, &renderHeight); // Get framebuffer size of current window
+#endif // defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
-#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
fullscreen = true;
// Screen size security check
@@ -1614,8 +1895,9 @@ static void InitDisplay(int width, int height)
//ANativeWindow_setBuffersGeometry(app->window, 0, 0, displayFormat); // Force use of native display size
surface = eglCreateWindowSurface(display, config, app->window, NULL);
+#endif // defined(PLATFORM_ANDROID)
-#elif defined(PLATFORM_RPI)
+#if defined(PLATFORM_RPI)
graphics_get_display_size(0, &displayWidth, &displayHeight);
// At this point we need to manage render size vs screen size
@@ -1653,7 +1935,7 @@ static void InitDisplay(int width, int height)
surface = eglCreateWindowSurface(display, config, &nativeWindow, NULL);
//---------------------------------------------------------------------------------
-#endif
+#endif // defined(PLATFORM_RPI)
// There must be at least one frame displayed before the buffers are swapped
//eglSwapInterval(display, 1);
@@ -1673,16 +1955,23 @@ static void InitDisplay(int width, int height)
TraceLog(INFO, "Screen size: %i x %i", screenWidth, screenHeight);
TraceLog(INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY);
}
-#endif
+#endif // defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
}
// Initialize OpenGL graphics
static void InitGraphics(void)
{
rlglInit(); // Init rlgl
-
rlglInitGraphics(renderOffsetX, renderOffsetY, renderWidth, renderHeight); // Init graphics (OpenGL stuff)
+#if defined(PLATFORM_OCULUS)
+ // Initialize Oculus Buffers
+ layer = InitOculusLayer(session);
+ buffer = LoadOculusBuffer(session, layer.width, layer.height);
+ mirror = LoadOculusMirror(session, hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2);
+ layer.eyeLayer.ColorTexture[0] = buffer.textureChain; //SetOculusLayerTexture(eyeLayer, buffer.textureChain);
+#endif
+
ClearBackground(RAYWHITE); // Default background color for raylib games :P
#if defined(PLATFORM_ANDROID)
@@ -1690,6 +1979,244 @@ static void InitGraphics(void)
#endif
}
+// Compute framebuffer size relative to screen size and display size
+// NOTE: Global variables renderWidth/renderHeight can be modified
+static void SetupFramebufferSize(int displayWidth, int displayHeight)
+{
+ // TODO: SetupFramebufferSize() does not consider properly display video modes.
+ // It setups a renderWidth/renderHeight with black bars that could not match a valid video mode,
+ // and so, framebuffer is not scaled properly to some monitors.
+
+ // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var)
+ if ((screenWidth > displayWidth) || (screenHeight > displayHeight))
+ {
+ TraceLog(WARNING, "DOWNSCALING: Required screen size (%ix%i) is bigger than display size (%ix%i)", screenWidth, screenHeight, displayWidth, displayHeight);
+
+ // Downscaling to fit display with border-bars
+ float widthRatio = (float)displayWidth/(float)screenWidth;
+ float heightRatio = (float)displayHeight/(float)screenHeight;
+
+ if (widthRatio <= heightRatio)
+ {
+ renderWidth = displayWidth;
+ renderHeight = (int)round((float)screenHeight*widthRatio);
+ renderOffsetX = 0;
+ renderOffsetY = (displayHeight - renderHeight);
+ }
+ else
+ {
+ renderWidth = (int)round((float)screenWidth*heightRatio);
+ renderHeight = displayHeight;
+ renderOffsetX = (displayWidth - renderWidth);
+ renderOffsetY = 0;
+ }
+
+ // NOTE: downscale matrix required!
+ float scaleRatio = (float)renderWidth/(float)screenWidth;
+
+ downscaleView = MatrixScale(scaleRatio, scaleRatio, scaleRatio);
+
+ // NOTE: We render to full display resolution!
+ // We just need to calculate above parameters for downscale matrix and offsets
+ renderWidth = displayWidth;
+ renderHeight = displayHeight;
+
+ TraceLog(WARNING, "Downscale matrix generated, content will be rendered at: %i x %i", renderWidth, renderHeight);
+ }
+ else if ((screenWidth < displayWidth) || (screenHeight < displayHeight))
+ {
+ // Required screen size is smaller than display size
+ TraceLog(INFO, "UPSCALING: Required screen size: %i x %i -> Display size: %i x %i", screenWidth, screenHeight, displayWidth, displayHeight);
+
+ // Upscaling to fit display with border-bars
+ float displayRatio = (float)displayWidth/(float)displayHeight;
+ float screenRatio = (float)screenWidth/(float)screenHeight;
+
+ if (displayRatio <= screenRatio)
+ {
+ renderWidth = screenWidth;
+ renderHeight = (int)round((float)screenWidth/displayRatio);
+ renderOffsetX = 0;
+ renderOffsetY = (renderHeight - screenHeight);
+ }
+ else
+ {
+ renderWidth = (int)round((float)screenHeight*displayRatio);
+ renderHeight = screenHeight;
+ renderOffsetX = (renderWidth - screenWidth);
+ renderOffsetY = 0;
+ }
+ }
+ else // screen == display
+ {
+ renderWidth = screenWidth;
+ renderHeight = screenHeight;
+ renderOffsetX = 0;
+ renderOffsetY = 0;
+ }
+}
+
+// Initialize hi-resolution timer
+static void InitTimer(void)
+{
+ srand(time(NULL)); // Initialize random seed
+
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+ struct timespec now;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
+ {
+ baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec;
+ }
+ else TraceLog(WARNING, "No hi-resolution timer available");
+#endif
+
+ previousTime = GetTime(); // Get time as double
+}
+
+// Get current time measure (in seconds) since InitTimer()
+static double GetTime(void)
+{
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
+ return glfwGetTime();
+#endif
+
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ uint64_t time = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec;
+
+ return (double)(time - baseTime)*1e-9;
+#endif
+}
+
+// Get one key state
+static bool GetKeyStatus(int key)
+{
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
+ return glfwGetKey(window, key);
+#elif defined(PLATFORM_ANDROID)
+ // TODO: Check for virtual keyboard
+ return false;
+#elif defined(PLATFORM_RPI)
+ // NOTE: Keys states are filled in PollInputEvents()
+ if (key < 0 || key > 511) return false;
+ else return currentKeyState[key];
+#endif
+}
+
+// Get one mouse button state
+static bool GetMouseButtonStatus(int button)
+{
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
+ return glfwGetMouseButton(window, button);
+#elif defined(PLATFORM_ANDROID)
+ // TODO: Check for virtual mouse
+ return false;
+#elif defined(PLATFORM_RPI)
+ // NOTE: Mouse buttons states are filled in PollInputEvents()
+ return currentMouseState[button];
+#endif
+}
+
+// Poll (store) all input events
+static void PollInputEvents(void)
+{
+ // NOTE: Gestures update must be called every frame to reset gestures correctly
+ // because ProcessGestureEvent() is just called on an event, not every frame
+ UpdateGestures();
+
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
+ // Mouse input polling
+ double mouseX;
+ double mouseY;
+
+ glfwGetCursorPos(window, &mouseX, &mouseY);
+
+ mousePosition.x = (float)mouseX;
+ mousePosition.y = (float)mouseY;
+
+ // Keyboard input polling (automatically managed by GLFW3 through callback)
+ lastKeyPressed = -1;
+
+ // Register previous keys states
+ for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
+
+ // Register previous mouse states
+ for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i];
+
+ previousMouseWheelY = currentMouseWheelY;
+ currentMouseWheelY = 0;
+
+ glfwPollEvents(); // Register keyboard/mouse events... and window events!
+#endif
+
+#if defined(PLATFORM_ANDROID)
+ // Register previous keys states
+ for (int i = 0; i < 128; i++) previousButtonState[i] = currentButtonState[i];
+
+ // Poll Events (registered events)
+ // NOTE: Activity is paused if not enabled (appEnabled)
+ while ((ident = ALooper_pollAll(appEnabled ? 0 : -1, NULL, &events,(void**)&source)) >= 0)
+ {
+ // Process this event
+ if (source != NULL) source->process(app, source);
+
+ // NOTE: Never close window, native activity is controlled by the system!
+ if (app->destroyRequested != 0)
+ {
+ //TraceLog(INFO, "Closing Window...");
+ //windowShouldClose = true;
+ //ANativeActivity_finish(app->activity);
+ }
+ }
+#endif
+
+#if defined(PLATFORM_RPI)
+ // NOTE: Mouse input events polling is done asynchonously in another pthread - MouseThread()
+
+ // NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin,
+ // we use method 2 (stdin) but maybe in a future we should change to method 1...
+ ProcessKeyboard();
+
+ // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread()
+#endif
+}
+
+// Copy back buffer to front buffers
+static void SwapBuffers(void)
+{
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
+ glfwSwapBuffers(window);
+#endif
+
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
+ eglSwapBuffers(display, surface);
+#endif
+}
+
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
+// Takes a screenshot and saves it in the same folder as executable
+static void TakeScreenshot(void)
+{
+ static int shotNum = 0; // Screenshot number, increments every screenshot take during program execution
+ char buffer[20]; // Buffer to store file name
+
+ unsigned char *imgData = rlglReadScreenPixels(renderWidth, renderHeight);
+
+ sprintf(buffer, "screenshot%03i.png", shotNum);
+
+ // Save image as PNG
+ WritePNG(buffer, imgData, renderWidth, renderHeight, 4);
+
+ free(imgData);
+
+ shotNum++;
+
+ TraceLog(INFO, "[%s] Screenshot taken!", buffer);
+}
+#endif
+
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
// GLFW3 Error Callback, runs on GLFW3 error
static void ErrorCallback(int error, const char *description)
@@ -1718,10 +2245,11 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
TakeScreenshot();
}
#endif
- else currentKeyState[key] = action;
-
- // TODO: Review (and remove) this HACK for GuiTextBox, to deteck back key
- if ((key == 259) && (action == GLFW_PRESS)) lastKeyPressed = 3;
+ else
+ {
+ currentKeyState[key] = action;
+ if (action == GLFW_PRESS) lastKeyPressed = key;
+ }
}
// GLFW3 Mouse Button Callback, runs on mouse button pressed
@@ -1750,10 +2278,10 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
gestureEvent.position[0] = GetMousePosition();
// Normalize gestureEvent.position[0] for screenWidth and screenHeight
- gestureEvent.position[0].x /= (float)GetScreenWidth();
+ gestureEvent.position[0].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
-
- // Gesture data is sent to gestures system for processing
+
+ // Gesture data is sent to gestures system for processing
ProcessGestureEvent(gestureEvent);
#endif
}
@@ -1767,6 +2295,9 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
GestureEvent gestureEvent;
gestureEvent.touchAction = TOUCH_MOVE;
+
+ // Assign a pointer ID
+ gestureEvent.pointerId[0] = 0;
// Register touch points count
gestureEvent.pointCount = 1;
@@ -1774,6 +2305,8 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
// Register touch points position, only one point registered
gestureEvent.position[0] = (Vector2){ (float)x, (float)y };
+ touchPosition[0] = gestureEvent.position[0];
+
// Normalize gestureEvent.position[0] for screenWidth and screenHeight
gestureEvent.position[0].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
@@ -1799,16 +2332,19 @@ static void CursorEnterCallback(GLFWwindow *window, int enter)
}
// GLFW3 WindowSize Callback, runs when window is resized
+// NOTE: Window resizing not allowed by default
static void WindowSizeCallback(GLFWwindow *window, int width, int height)
{
// If window is resized, graphics device is re-initialized (but only ortho mode)
- rlglInitGraphics(renderOffsetX, renderOffsetY, renderWidth, renderHeight);
+ rlglInitGraphics(0, 0, width, height);
// Window size must be updated to be used on 3D mode to get new aspect ratio (Begin3dMode())
- //screenWidth = width;
- //screenHeight = height;
-
- // TODO: Update render size?
+ screenWidth = width;
+ screenHeight = height;
+ renderWidth = width;
+ renderHeight = height;
+
+ // NOTE: Postprocessing texture is not scaled to new size
// Background must be also re-cleared
ClearBackground(RAYWHITE);
@@ -1887,10 +2423,10 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Load default font for convenience
// NOTE: External function (defined in module: text)
LoadDefaultFont();
-
+
// TODO: GPU assets reload in case of lost focus (lost context)
// NOTE: This problem has been solved just unbinding and rebinding context from display
- /*
+ /*
if (assetsReloadRequired)
{
for (int i = 0; i < assetsCount; i++)
@@ -2054,151 +2590,95 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
}
#endif
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
-// Takes a screenshot and saves it in the same folder as executable
-static void TakeScreenshot(void)
+#if defined(PLATFORM_WEB)
+static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData)
{
- static int shotNum = 0; // Screenshot number, increments every screenshot take during program execution
- char buffer[20]; // Buffer to store file name
-
- unsigned char *imgData = rlglReadScreenPixels(renderWidth, renderHeight);
-
- sprintf(buffer, "screenshot%03i.png", shotNum);
+ //isFullscreen: int e->isFullscreen
+ //fullscreenEnabled: int e->fullscreenEnabled
+ //fs element nodeName: (char *) e->nodeName
+ //fs element id: (char *) e->id
+ //Current element size: (int) e->elementWidth, (int) e->elementHeight
+ //Screen size:(int) e->screenWidth, (int) e->screenHeight
- // Save image as PNG
- WritePNG(buffer, imgData, renderWidth, renderHeight, 4);
-
- free(imgData);
-
- shotNum++;
-
- TraceLog(INFO, "[%s] Screenshot taken!", buffer);
-}
-#endif
-
-// Initialize hi-resolution timer
-static void InitTimer(void)
-{
- srand(time(NULL)); // Initialize random seed
-
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
- struct timespec now;
-
- if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
+ if (e->isFullscreen)
{
- baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec;
+ TraceLog(INFO, "Canvas scaled to fullscreen. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight);
}
- else TraceLog(WARNING, "No hi-resolution timer available");
-#endif
-
- previousTime = GetTime(); // Get time as double
-}
-
-// Get current time measure (in seconds) since InitTimer()
-static double GetTime(void)
-{
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
- return glfwGetTime();
-#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- uint64_t time = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec;
+ else
+ {
+ TraceLog(INFO, "Canvas scaled to windowed. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight);
+ }
+
+ // TODO: Depending on scaling factor (screen vs element), calculate factor to scale mouse/touch input
- return (double)(time - baseTime)*1e-9;
-#endif
+ return 0;
}
-// Get one key state
-static bool GetKeyStatus(int key)
+// Web: Get input events
+static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
- return glfwGetKey(window, key);
-#elif defined(PLATFORM_ANDROID)
- // TODO: Check for virtual keyboard
- return false;
-#elif defined(PLATFORM_RPI)
- // NOTE: Keys states are filled in PollInputEvents()
- if (key < 0 || key > 511) return false;
- else return currentKeyState[key];
-#endif
-}
+ /*
+ for (int i = 0; i < touchEvent->numTouches; i++)
+ {
+ long x, y, id;
-// Get one mouse button state
-static bool GetMouseButtonStatus(int button)
-{
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
- return glfwGetMouseButton(window, button);
-#elif defined(PLATFORM_ANDROID)
- // TODO: Check for virtual keyboard
- return false;
-#elif defined(PLATFORM_RPI)
- // NOTE: Mouse buttons states are filled in PollInputEvents()
- return currentMouseState[button];
-#endif
-}
+ if (!touchEvent->touches[i].isChanged) continue;
-// Poll (store) all input events
-static void PollInputEvents(void)
-{
- // NOTE: Gestures update must be called every frame to reset gestures correctly
- // because ProcessGestureEvent() is just called on an event, not every frame
- UpdateGestures();
+ id = touchEvent->touches[i].identifier;
+ x = touchEvent->touches[i].canvasX;
+ y = touchEvent->touches[i].canvasY;
+ }
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
- // Mouse input polling
- double mouseX;
- double mouseY;
-
- glfwGetCursorPos(window, &mouseX, &mouseY);
-
- mousePosition.x = (float)mouseX;
- mousePosition.y = (float)mouseY;
-
- // Keyboard input polling (automatically managed by GLFW3 through callback)
- lastKeyPressed = -1;
-
- // Register previous keys states
- for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
-
- // Register previous mouse states
- for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i];
-
- previousMouseWheelY = currentMouseWheelY;
- currentMouseWheelY = 0;
-
- glfwPollEvents(); // Register keyboard/mouse events... and window events!
-#elif defined(PLATFORM_ANDROID)
-
- // Register previous keys states
- for (int i = 0; i < 128; i++) previousButtonState[i] = currentButtonState[i];
+ printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches,
+ event->ctrlKey ? " CTRL" : "", event->shiftKey ? " SHIFT" : "", event->altKey ? " ALT" : "", event->metaKey ? " META" : "");
- // Poll Events (registered events)
- // NOTE: Activity is paused if not enabled (appEnabled)
- while ((ident = ALooper_pollAll(appEnabled ? 0 : -1, NULL, &events,(void**)&source)) >= 0)
+ for(int i = 0; i < event->numTouches; ++i)
{
- // Process this event
- if (source != NULL) source->process(app, source);
-
- // NOTE: Never close window, native activity is controlled by the system!
- if (app->destroyRequested != 0)
- {
- //TraceLog(INFO, "Closing Window...");
- //windowShouldClose = true;
- //ANativeActivity_finish(app->activity);
- }
+ const EmscriptenTouchPoint *t = &event->touches[i];
+
+ printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n",
+ t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY);
}
-#elif defined(PLATFORM_RPI)
+ */
+
+ GestureEvent gestureEvent;
- // NOTE: Mouse input events polling is done asynchonously in another pthread - MouseThread()
+ // Register touch actions
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_DOWN;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_UP;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_MOVE;
+
+ // Register touch points count
+ gestureEvent.pointCount = touchEvent->numTouches;
+
+ // Register touch points id
+ gestureEvent.pointerId[0] = touchEvent->touches[0].identifier;
+ gestureEvent.pointerId[1] = touchEvent->touches[1].identifier;
+
+ // Register touch points position
+ // NOTE: Only two points registered
+ // TODO: Touch data should be scaled accordingly!
+ //gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].canvasX, touchEvent->touches[0].canvasY };
+ //gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].canvasX, touchEvent->touches[1].canvasY };
+ gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].targetX, touchEvent->touches[0].targetY };
+ gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].targetX, touchEvent->touches[1].targetY };
- // NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin,
- // we use method 2 (stdin) but maybe in a future we should change to method 1...
- ProcessKeyboard();
+ touchPosition[0] = gestureEvent.position[0];
+ touchPosition[1] = gestureEvent.position[1];
+
+ // Normalize gestureEvent.position[x] for screenWidth and screenHeight
+ gestureEvent.position[0].x /= (float)GetScreenWidth();
+ gestureEvent.position[0].y /= (float)GetScreenHeight();
+
+ gestureEvent.position[1].x /= (float)GetScreenWidth();
+ gestureEvent.position[1].y /= (float)GetScreenHeight();
- // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread()
+ // Gesture data is sent to gestures system for processing
+ ProcessGestureEvent(gestureEvent);
-#endif
+ return 1;
}
+#endif
#if defined(PLATFORM_RPI)
// Initialize Keyboard system (using standard input)
@@ -2437,19 +2917,31 @@ static void *MouseThread(void *arg)
// Init gamepad system
static void InitGamepad(void)
{
- if ((gamepadStream = open(DEFAULT_GAMEPAD_DEV, O_RDONLY|O_NONBLOCK)) < 0)
- {
- TraceLog(WARNING, "Gamepad device could not be opened, no gamepad available");
- }
- else
+ char gamepadDev[128] = "";
+
+ for (int i = 0; i < MAX_GAMEPADS; i++)
{
- gamepadReady = true;
+ sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
+
+ if ((gamepadStream[i] = open(gamepadDev, O_RDONLY|O_NONBLOCK)) < 0)
+ {
+ // NOTE: Only show message for first gamepad
+ if (i == 0) TraceLog(WARNING, "Gamepad device could not be opened, no gamepad available");
+ }
+ else
+ {
+ gamepadReady[i] = true;
- int error = pthread_create(&gamepadThreadId, NULL, &GamepadThread, NULL);
+ // NOTE: Only create one thread
+ if (i == 0)
+ {
+ int error = pthread_create(&gamepadThreadId, NULL, &GamepadThread, NULL);
- if (error != 0) TraceLog(WARNING, "Error creating gamepad input event thread");
- else TraceLog(INFO, "Gamepad device initialized successfully");
- }
+ if (error != 0) TraceLog(WARNING, "Error creating gamepad input event thread");
+ else TraceLog(INFO, "Gamepad device initialized successfully");
+ }
+ }
+ }
}
// Process Gamepad (/dev/input/js0)
@@ -2466,227 +2958,251 @@ static void *GamepadThread(void *arg)
unsigned char number; // event axis/button number
};
- // These values are sensible on Logitech Dual Action Rumble and Xbox360 controller
- const int joystickAxisX = 0;
- const int joystickAxisY = 1;
-
// Read gamepad event
- struct js_event gamepadEvent;
+ struct js_event gamepadEvent;
- while (1)
+ while (1)
{
- if (read(gamepadStream, &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
+ for (int i = 0; i < MAX_GAMEPADS; i++)
{
- gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events
-
- // Process gamepad events by type
- if (gamepadEvent.type == JS_EVENT_BUTTON)
+ if (read(gamepadStream[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
{
- TraceLog(DEBUG, "Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
+ gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events
- if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
+ // Process gamepad events by type
+ if (gamepadEvent.type == JS_EVENT_BUTTON)
{
- // 1 - button pressed, 0 - button released
- gamepadButtons[gamepadEvent.number] = (int)gamepadEvent.value;
+ TraceLog(DEBUG, "Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
+
+ if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
+ {
+ // 1 - button pressed, 0 - button released
+ gamepadButtons[i][gamepadEvent.number] = (int)gamepadEvent.value;
+ }
}
- }
- else if (gamepadEvent.type == JS_EVENT_AXIS)
- {
- TraceLog(DEBUG, "Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
-
- if (gamepadEvent.number == joystickAxisX) gamepadAxisX = (int)gamepadEvent.value;
- if (gamepadEvent.number == joystickAxisY) gamepadAxisY = (int)gamepadEvent.value;
- /*
- switch (gamepadEvent.number)
+ else if (gamepadEvent.type == JS_EVENT_AXIS)
{
- case 0: // 1st Axis X
- case 1: // 1st Axis Y
- case 2: // 2st Axis X
- case 3: // 2st Axis Y
+ TraceLog(DEBUG, "Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
+
+ if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
+ {
+ // NOTE: Scaling of gamepadEvent.value to get values between -1..1
+ gamepadAxisValues[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
+ }
}
- */
}
}
- }
-
+ }
+
return NULL;
}
#endif
-// Copy back buffer to front buffers
-static void SwapBuffers(void)
+
+#if defined(PLATFORM_OCULUS)
+// Convert from Oculus ovrMatrix4f struct to raymath Matrix struct
+static Matrix FromOvrMatrix(ovrMatrix4f ovrmat)
{
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
- glfwSwapBuffers(window);
-#elif defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
- eglSwapBuffers(display, surface);
-#endif
+ Matrix rmat;
+
+ rmat.m0 = ovrmat.M[0][0];
+ rmat.m1 = ovrmat.M[1][0];
+ rmat.m2 = ovrmat.M[2][0];
+ rmat.m3 = ovrmat.M[3][0];
+ rmat.m4 = ovrmat.M[0][1];
+ rmat.m5 = ovrmat.M[1][1];
+ rmat.m6 = ovrmat.M[2][1];
+ rmat.m7 = ovrmat.M[3][1];
+ rmat.m8 = ovrmat.M[0][2];
+ rmat.m9 = ovrmat.M[1][2];
+ rmat.m10 = ovrmat.M[2][2];
+ rmat.m11 = ovrmat.M[3][2];
+ rmat.m12 = ovrmat.M[0][3];
+ rmat.m13 = ovrmat.M[1][3];
+ rmat.m14 = ovrmat.M[2][3];
+ rmat.m15 = ovrmat.M[3][3];
+
+ MatrixTranspose(&rmat);
+
+ return rmat;
}
-// Compute framebuffer size relative to screen size and display size
-// NOTE: Global variables renderWidth/renderHeight can be modified
-static void SetupFramebufferSize(int displayWidth, int displayHeight)
+// Load Oculus required buffers: texture-swap-chain, fbo, texture-depth
+static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height)
{
- // TODO: SetupFramebufferSize() does not consider properly display video modes.
- // It setups a renderWidth/renderHeight with black bars that could not match a valid video mode,
- // and so, framebuffer is not scaled properly to some monitors.
+ OculusBuffer buffer;
+ buffer.width = width;
+ buffer.height = height;
- // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var)
- if ((screenWidth > displayWidth) || (screenHeight > displayHeight))
- {
- TraceLog(WARNING, "DOWNSCALING: Required screen size (%ix%i) is bigger than display size (%ix%i)", screenWidth, screenHeight, displayWidth, displayHeight);
-
- // Downscaling to fit display with border-bars
- float widthRatio = (float)displayWidth/(float)screenWidth;
- float heightRatio = (float)displayHeight/(float)screenHeight;
+ // Create OVR texture chain
+ ovrTextureSwapChainDesc desc = {};
+ desc.Type = ovrTexture_2D;
+ desc.ArraySize = 1;
+ desc.Width = width;
+ desc.Height = height;
+ desc.MipLevels = 1;
+ desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; // Requires glEnable(GL_FRAMEBUFFER_SRGB);
+ desc.SampleCount = 1;
+ desc.StaticImage = ovrFalse;
- if (widthRatio <= heightRatio)
- {
- renderWidth = displayWidth;
- renderHeight = (int)round((float)screenHeight*widthRatio);
- renderOffsetX = 0;
- renderOffsetY = (displayHeight - renderHeight);
- }
- else
- {
- renderWidth = (int)round((float)screenWidth*heightRatio);
- renderHeight = displayHeight;
- renderOffsetX = (displayWidth - renderWidth);
- renderOffsetY = 0;
- }
-
- // NOTE: downscale matrix required!
- float scaleRatio = (float)renderWidth/(float)screenWidth;
-
- downscaleView = MatrixScale(scaleRatio, scaleRatio, scaleRatio);
+ ovrResult result = ovr_CreateTextureSwapChainGL(session, &desc, &buffer.textureChain);
+
+ if (!OVR_SUCCESS(result)) TraceLog(WARNING, "OVR: Failed to create swap textures buffer");
- // NOTE: We render to full display resolution!
- // We just need to calculate above parameters for downscale matrix and offsets
- renderWidth = displayWidth;
- renderHeight = displayHeight;
+ int textureCount = 0;
+ ovr_GetTextureSwapChainLength(session, buffer.textureChain, &textureCount);
+
+ if (!OVR_SUCCESS(result) || !textureCount) TraceLog(WARNING, "OVR: Unable to count swap chain textures");
- TraceLog(WARNING, "Downscale matrix generated, content will be rendered at: %i x %i", renderWidth, renderHeight);
- }
- else if ((screenWidth < displayWidth) || (screenHeight < displayHeight))
+ for (int i = 0; i < textureCount; ++i)
{
- // Required screen size is smaller than display size
- TraceLog(INFO, "UPSCALING: Required screen size: %i x %i -> Display size: %i x %i", screenWidth, screenHeight, displayWidth, displayHeight);
+ GLuint chainTexId;
+ ovr_GetTextureSwapChainBufferGL(session, buffer.textureChain, i, &chainTexId);
+ glBindTexture(GL_TEXTURE_2D, chainTexId);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ /*
+ // Setup framebuffer object (using depth texture)
+ glGenFramebuffers(1, &buffer.fboId);
+ glGenTextures(1, &buffer.depthId);
+ glBindTexture(GL_TEXTURE_2D, buffer.depthId);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, buffer.width, buffer.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
+ */
+
+ // Setup framebuffer object (using depth renderbuffer)
+ glGenFramebuffers(1, &buffer.fboId);
+ glGenRenderbuffers(1, &buffer.depthId);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer.fboId);
+ glBindRenderbuffer(GL_RENDERBUFFER, buffer.depthId);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, buffer.width, buffer.height);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer.depthId);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- // Upscaling to fit display with border-bars
- float displayRatio = (float)displayWidth/(float)displayHeight;
- float screenRatio = (float)screenWidth/(float)screenHeight;
+ return buffer;
+}
- if (displayRatio <= screenRatio)
- {
- renderWidth = screenWidth;
- renderHeight = (int)round((float)screenWidth/displayRatio);
- renderOffsetX = 0;
- renderOffsetY = (renderHeight - screenHeight);
- }
- else
- {
- renderWidth = (int)round((float)screenHeight*displayRatio);
- renderHeight = screenHeight;
- renderOffsetX = (renderWidth - screenWidth);
- renderOffsetY = 0;
- }
- }
- else // screen == display
+// Unload texture required buffers
+static void UnloadOculusBuffer(ovrSession session, OculusBuffer buffer)
+{
+ if (buffer.textureChain)
{
- renderWidth = screenWidth;
- renderHeight = screenHeight;
- renderOffsetX = 0;
- renderOffsetY = 0;
+ ovr_DestroyTextureSwapChain(session, buffer.textureChain);
+ buffer.textureChain = NULL;
}
+
+ if (buffer.depthId != 0) glDeleteTextures(1, &buffer.depthId);
+ if (buffer.fboId != 0) glDeleteFramebuffers(1, &buffer.fboId);
}
-#if defined(PLATFORM_WEB)
-static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData)
+// Set current Oculus buffer
+static void SetOculusBuffer(ovrSession session, OculusBuffer buffer)
{
- //isFullscreen: int e->isFullscreen
- //fullscreenEnabled: int e->fullscreenEnabled
- //fs element nodeName: (char *) e->nodeName
- //fs element id: (char *) e->id
- //Current element size: (int) e->elementWidth, (int) e->elementHeight
- //Screen size:(int) e->screenWidth, (int) e->screenHeight
+ GLuint currentTexId;
+ int currentIndex;
- if (e->isFullscreen)
- {
- TraceLog(INFO, "Canvas scaled to fullscreen. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight);
- }
- else
- {
- TraceLog(INFO, "Canvas scaled to windowed. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight);
- }
+ ovr_GetTextureSwapChainCurrentIndex(session, buffer.textureChain, &currentIndex);
+ ovr_GetTextureSwapChainBufferGL(session, buffer.textureChain, currentIndex, &currentTexId);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer.fboId);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currentTexId, 0);
+ //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, buffer.depthId, 0); // Already binded
+
+ //glViewport(0, 0, buffer.width, buffer.height); // Useful if rendering to separate framebuffers (every eye)
+ //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- // TODO: Depending on scaling factor (screen vs element), calculate factor to scale mouse/touch input
+ // Required if OculusBuffer format is OVR_FORMAT_R8G8B8A8_UNORM_SRGB
+ glEnable(GL_FRAMEBUFFER_SRGB);
+}
- return 0;
+// Unset Oculus buffer
+static void UnsetOculusBuffer(OculusBuffer buffer)
+{
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
-// Web: Get input events
-static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
+// Load Oculus mirror buffers
+static OculusMirror LoadOculusMirror(ovrSession session, int width, int height)
{
- /*
- for (int i = 0; i < touchEvent->numTouches; i++)
- {
- long x, y, id;
+ OculusMirror mirror;
+ mirror.width = width;
+ mirror.height = height;
+
+ ovrMirrorTextureDesc mirrorDesc;
+ memset(&mirrorDesc, 0, sizeof(mirrorDesc));
+ mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
+ mirrorDesc.Width = mirror.width;
+ mirrorDesc.Height = mirror.height;
+
+ if (!OVR_SUCCESS(ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirror.texture))) TraceLog(WARNING, "Could not create mirror texture");
- if (!touchEvent->touches[i].isChanged) continue;
+ glGenFramebuffers(1, &mirror.fboId);
- id = touchEvent->touches[i].identifier;
- x = touchEvent->touches[i].canvasX;
- y = touchEvent->touches[i].canvasY;
- }
-
- printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches,
- event->ctrlKey ? " CTRL" : "", event->shiftKey ? " SHIFT" : "", event->altKey ? " ALT" : "", event->metaKey ? " META" : "");
+ return mirror;
+}
- for(int i = 0; i < event->numTouches; ++i)
- {
- const EmscriptenTouchPoint *t = &event->touches[i];
-
- printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n",
- t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY);
- }
- */
-
- GestureEvent gestureEvent;
+// Unload Oculus mirror buffers
+static void UnloadOculusMirror(ovrSession session, OculusMirror mirror)
+{
+ if (mirror.fboId != 0) glDeleteFramebuffers(1, &mirror.fboId);
+ if (mirror.texture) ovr_DestroyMirrorTexture(session, mirror.texture);
+}
- // Register touch actions
- if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_DOWN;
- else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_UP;
- else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_MOVE;
-
- // Register touch points count
- gestureEvent.pointCount = touchEvent->numTouches;
+static void BlitOculusMirror(ovrSession session, OculusMirror mirror)
+{
+ GLuint mirrorTextureId;
- // Register touch points id
- gestureEvent.pointerId[0] = touchEvent->touches[0].identifier;
- gestureEvent.pointerId[1] = touchEvent->touches[1].identifier;
+ ovr_GetMirrorTextureBufferGL(session, mirror.texture, &mirrorTextureId);
- // Register touch points position
- // NOTE: Only two points registered
- // TODO: Touch data should be scaled accordingly!
- //gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].canvasX, touchEvent->touches[0].canvasY };
- //gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].canvasX, touchEvent->touches[1].canvasY };
- gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].targetX, touchEvent->touches[0].targetY };
- gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].targetX, touchEvent->touches[1].targetY };
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mirror.fboId);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mirrorTextureId, 0);
+ glBlitFramebuffer(0, 0, mirror.width, mirror.height, 0, mirror.height, mirror.width, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+}
- touchPosition[0] = gestureEvent.position[0];
- touchPosition[1] = gestureEvent.position[1];
+// Requires: session, hmdDesc
+static OculusLayer InitOculusLayer(ovrSession session)
+{
+ OculusLayer layer = { 0 };
- // Normalize gestureEvent.position[x] for screenWidth and screenHeight
- gestureEvent.position[0].x /= (float)GetScreenWidth();
- gestureEvent.position[0].y /= (float)GetScreenHeight();
+ layer.viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
+
+ memset(&layer.eyeLayer, 0, sizeof(ovrLayerEyeFov));
+ layer.eyeLayer.Header.Type = ovrLayerType_EyeFov;
+ layer.eyeLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
+
+ ovrEyeRenderDesc eyeRenderDescs[2];
- gestureEvent.position[1].x /= (float)GetScreenWidth();
- gestureEvent.position[1].y /= (float)GetScreenHeight();
+ for (int eye = 0; eye < 2; eye++)
+ {
+ eyeRenderDescs[eye] = ovr_GetRenderDesc(session, eye, hmdDesc.DefaultEyeFov[eye]);
+ ovrMatrix4f ovrPerspectiveProjection = ovrMatrix4f_Projection(eyeRenderDescs[eye].Fov, 0.01f, 10000.0f, ovrProjection_None); //ovrProjection_ClipRangeOpenGL);
+ layer.eyeProjections[eye] = FromOvrMatrix(ovrPerspectiveProjection); // NOTE: struct ovrMatrix4f { float M[4][4] } --> struct Matrix
- // Gesture data is sent to gestures system for processing
- ProcessGestureEvent(gestureEvent); // Process obtained gestures data
+ layer.viewScaleDesc.HmdToEyeOffset[eye] = eyeRenderDescs[eye].HmdToEyeOffset;
+ layer.eyeLayer.Fov[eye] = eyeRenderDescs[eye].Fov;
+
+ ovrSizei eyeSize = ovr_GetFovTextureSize(session, eye, layer.eyeLayer.Fov[eye], 1.0f);
+ layer.eyeLayer.Viewport[eye].Size = eyeSize;
+ layer.eyeLayer.Viewport[eye].Pos.x = layer.width;
+ layer.eyeLayer.Viewport[eye].Pos.y = 0;
- return 1;
+ layer.height = eyeSize.h; //std::max(renderTargetSize.y, (uint32_t)eyeSize.h);
+ layer.width += eyeSize.w;
+ }
+
+ return layer;
}
#endif
diff --git a/src/easings.h b/src/easings.h
index a198be4d..527970ab 100644
--- a/src/easings.h
+++ b/src/easings.h
@@ -7,6 +7,26 @@
* This header uses:
* #define EASINGS_STATIC_INLINE // Inlines all functions code, so it runs faster.
* // This requires lots of memory on system.
+* How to use:
+* The four inputs t,b,c,d are defined as follows:
+* t = current time (in any unit measure, but same unit as duration)
+* b = starting value to interpolate
+* c = the total change in value of b that needs to occur
+* d = total time it should take to complete (duration)
+*
+* Example:
+*
+* int currentTime = 0;
+* int duration = 100;
+* float startPositionX = 0.0f;
+* float finalPositionX = 30.0f;
+* float currentPositionX = startPositionX;
+*
+* while (currentPositionX < finalPositionX)
+* {
+* currentPositionX = EaseSineIn(currentTime, startPositionX, finalPositionX - startPositionX, duration);
+* currentTime++;
+* }
*
* A port of Robert Penner's easing equations to C (http://robertpenner.com/easing/)
*
@@ -70,7 +90,7 @@
#define EASEDEF extern
#endif
-#include <math.h>
+#include <math.h> // Required for: sin(), cos(), sqrt(), pow()
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
diff --git a/src/glad.c b/src/external/glad.c
index aace05a7..aace05a7 100644
--- a/src/glad.c
+++ b/src/external/glad.c
diff --git a/src/glad.h b/src/external/glad.h
index 56bb622d..56bb622d 100644
--- a/src/glad.h
+++ b/src/external/glad.h
diff --git a/src/external/glfw3/COPYING.txt b/src/external/glfw3/COPYING.txt
new file mode 100644
index 00000000..ad16462a
--- /dev/null
+++ b/src/external/glfw3/COPYING.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2002-2006 Marcus Geelnard
+Copyright (c) 2006-2016 Camilla Berglund <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.
+
diff --git a/src/external/glfw3/include/GLFW/glfw3.h b/src/external/glfw3/include/GLFW/glfw3.h
new file mode 100644
index 00000000..5a0c4508
--- /dev/null
+++ b/src/external/glfw3/include/GLFW/glfw3.h
@@ -0,0 +1,4235 @@
+/*************************************************************************
+ * GLFW 3.2 - www.glfw.org
+ * A library for OpenGL, window and input
+ *------------------------------------------------------------------------
+ * Copyright (c) 2002-2006 Marcus Geelnard
+ * Copyright (c) 2006-2016 Camilla Berglund <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.
+ *
+ *************************************************************************/
+
+#ifndef _glfw3_h_
+#define _glfw3_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************************************************************************
+ * Doxygen documentation
+ *************************************************************************/
+
+/*! @file glfw3.h
+ * @brief The header of the GLFW 3 API.
+ *
+ * This is the header file of the GLFW 3 API. It defines all its types and
+ * declares all its functions.
+ *
+ * For more information about how to use this file, see @ref build_include.
+ */
+/*! @defgroup context Context reference
+ *
+ * This is the reference documentation for OpenGL and OpenGL ES context related
+ * functions. For more task-oriented information, see the @ref context_guide.
+ */
+/*! @defgroup vulkan Vulkan reference
+ *
+ * This is the reference documentation for Vulkan related functions and types.
+ * For more task-oriented information, see the @ref vulkan_guide.
+ */
+/*! @defgroup init Initialization, version and error reference
+ *
+ * This is the reference documentation for initialization and termination of
+ * the library, version management and error handling. For more task-oriented
+ * information, see the @ref intro_guide.
+ */
+/*! @defgroup input Input reference
+ *
+ * This is the reference documentation for input related functions and types.
+ * For more task-oriented information, see the @ref input_guide.
+ */
+/*! @defgroup monitor Monitor reference
+ *
+ * This is the reference documentation for monitor related functions and types.
+ * For more task-oriented information, see the @ref monitor_guide.
+ */
+/*! @defgroup window Window reference
+ *
+ * This is the reference documentation for window related functions and types,
+ * including creation, deletion and event polling. For more task-oriented
+ * information, see the @ref window_guide.
+ */
+
+
+/*************************************************************************
+ * Compiler- and platform-specific preprocessor work
+ *************************************************************************/
+
+/* If we are we on Windows, we want a single define for it.
+ */
+#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__))
+ #define _WIN32
+#endif /* _WIN32 */
+
+/* It is customary to use APIENTRY for OpenGL function pointer declarations on
+ * all platforms. Additionally, the Windows OpenGL header needs APIENTRY.
+ */
+#ifndef APIENTRY
+ #ifdef _WIN32
+ #define APIENTRY __stdcall
+ #else
+ #define APIENTRY
+ #endif
+#endif /* APIENTRY */
+
+/* Some Windows OpenGL headers need this.
+ */
+#if !defined(WINGDIAPI) && defined(_WIN32)
+ #define WINGDIAPI __declspec(dllimport)
+ #define GLFW_WINGDIAPI_DEFINED
+#endif /* WINGDIAPI */
+
+/* Some Windows GLU headers need this.
+ */
+#if !defined(CALLBACK) && defined(_WIN32)
+ #define CALLBACK __stdcall
+ #define GLFW_CALLBACK_DEFINED
+#endif /* CALLBACK */
+
+/* Most Windows GLU headers need wchar_t.
+ * The OS X OpenGL header blocks the definition of ptrdiff_t by glext.h.
+ * Include it unconditionally to avoid surprising side-effects.
+ */
+#include <stddef.h>
+#include <stdint.h>
+
+/* Include the chosen client API headers.
+ */
+#if defined(__APPLE__)
+ #if defined(GLFW_INCLUDE_GLCOREARB)
+ #include <OpenGL/gl3.h>
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include <OpenGL/gl3ext.h>
+ #endif
+ #elif !defined(GLFW_INCLUDE_NONE)
+ #if !defined(GLFW_INCLUDE_GLEXT)
+ #define GL_GLEXT_LEGACY
+ #endif
+ #include <OpenGL/gl.h>
+ #endif
+ #if defined(GLFW_INCLUDE_GLU)
+ #include <OpenGL/glu.h>
+ #endif
+#else
+ #if defined(GLFW_INCLUDE_GLCOREARB)
+ #include <GL/glcorearb.h>
+ #elif defined(GLFW_INCLUDE_ES1)
+ #include <GLES/gl.h>
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include <GLES/glext.h>
+ #endif
+ #elif defined(GLFW_INCLUDE_ES2)
+ #include <GLES2/gl2.h>
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include <GLES2/gl2ext.h>
+ #endif
+ #elif defined(GLFW_INCLUDE_ES3)
+ #include <GLES3/gl3.h>
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include <GLES2/gl2ext.h>
+ #endif
+ #elif defined(GLFW_INCLUDE_ES31)
+ #include <GLES3/gl31.h>
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include <GLES2/gl2ext.h>
+ #endif
+ #elif defined(GLFW_INCLUDE_VULKAN)
+ #include <vulkan/vulkan.h>
+ #elif !defined(GLFW_INCLUDE_NONE)
+ #include <GL/gl.h>
+ #if defined(GLFW_INCLUDE_GLEXT)
+ #include <GL/glext.h>
+ #endif
+ #endif
+ #if defined(GLFW_INCLUDE_GLU)
+ #include <GL/glu.h>
+ #endif
+#endif
+
+#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL)
+ /* GLFW_DLL must be defined by applications that are linking against the DLL
+ * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW
+ * configuration header when compiling the DLL version of the library.
+ */
+ #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined"
+#endif
+
+/* GLFWAPI is used to declare public API functions for export
+ * from the DLL / shared library / dynamic library.
+ */
+#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)
+ /* We are building GLFW as a Win32 DLL */
+ #define GLFWAPI __declspec(dllexport)
+#elif defined(_WIN32) && defined(GLFW_DLL)
+ /* We are calling GLFW as a Win32 DLL */
+ #define GLFWAPI __declspec(dllimport)
+#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)
+ /* We are building GLFW as a shared / dynamic library */
+ #define GLFWAPI __attribute__((visibility("default")))
+#else
+ /* We are building or calling GLFW as a static library */
+ #define GLFWAPI
+#endif
+
+
+/*************************************************************************
+ * GLFW API tokens
+ *************************************************************************/
+
+/*! @name GLFW version macros
+ * @{ */
+/*! @brief The major version number of the GLFW library.
+ *
+ * This is incremented when the API is changed in non-compatible ways.
+ * @ingroup init
+ */
+#define GLFW_VERSION_MAJOR 3
+/*! @brief The minor version number of the GLFW library.
+ *
+ * This is incremented when features are added to the API but it remains
+ * backward-compatible.
+ * @ingroup init
+ */
+#define GLFW_VERSION_MINOR 2
+/*! @brief The revision number of the GLFW library.
+ *
+ * This is incremented when a bug fix release is made that does not contain any
+ * API changes.
+ * @ingroup init
+ */
+#define GLFW_VERSION_REVISION 0
+/*! @} */
+
+/*! @name Boolean values
+ * @{ */
+/*! @brief One.
+ *
+ * One. Seriously. You don't _need_ to use this symbol in your code. It's
+ * just semantic sugar for the number 1. You can use `1` or `true` or `_True`
+ * or `GL_TRUE` or whatever you want.
+ */
+#define GLFW_TRUE 1
+/*! @brief Zero.
+ *
+ * Zero. Seriously. You don't _need_ to use this symbol in your code. It's
+ * just just semantic sugar for the number 0. You can use `0` or `false` or
+ * `_False` or `GL_FALSE` or whatever you want.
+ */
+#define GLFW_FALSE 0
+/*! @} */
+
+/*! @name Key and button actions
+ * @{ */
+/*! @brief The key or mouse button was released.
+ *
+ * The key or mouse button was released.
+ *
+ * @ingroup input
+ */
+#define GLFW_RELEASE 0
+/*! @brief The key or mouse button was pressed.
+ *
+ * The key or mouse button was pressed.
+ *
+ * @ingroup input
+ */
+#define GLFW_PRESS 1
+/*! @brief The key was held down until it repeated.
+ *
+ * The key was held down until it repeated.
+ *
+ * @ingroup input
+ */
+#define GLFW_REPEAT 2
+/*! @} */
+
+/*! @defgroup keys Keyboard keys
+ *
+ * See [key input](@ref input_key) for how these are used.
+ *
+ * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60),
+ * but re-arranged to map to 7-bit ASCII for printable keys (function keys are
+ * put in the 256+ range).
+ *
+ * The naming of the key codes follow these rules:
+ * - The US keyboard layout is used
+ * - Names of printable alpha-numeric characters are used (e.g. "A", "R",
+ * "3", etc.)
+ * - For non-alphanumeric characters, Unicode:ish names are used (e.g.
+ * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not
+ * correspond to the Unicode standard (usually for brevity)
+ * - Keys that lack a clear US mapping are named "WORLD_x"
+ * - For non-printable keys, custom names are used (e.g. "F4",
+ * "BACKSPACE", etc.)
+ *
+ * @ingroup input
+ * @{
+ */
+
+/* The unknown key */
+#define GLFW_KEY_UNKNOWN -1
+
+/* Printable keys */
+#define GLFW_KEY_SPACE 32
+#define GLFW_KEY_APOSTROPHE 39 /* ' */
+#define GLFW_KEY_COMMA 44 /* , */
+#define GLFW_KEY_MINUS 45 /* - */
+#define GLFW_KEY_PERIOD 46 /* . */
+#define GLFW_KEY_SLASH 47 /* / */
+#define GLFW_KEY_0 48
+#define GLFW_KEY_1 49
+#define GLFW_KEY_2 50
+#define GLFW_KEY_3 51
+#define GLFW_KEY_4 52
+#define GLFW_KEY_5 53
+#define GLFW_KEY_6 54
+#define GLFW_KEY_7 55
+#define GLFW_KEY_8 56
+#define GLFW_KEY_9 57
+#define GLFW_KEY_SEMICOLON 59 /* ; */
+#define GLFW_KEY_EQUAL 61 /* = */
+#define GLFW_KEY_A 65
+#define GLFW_KEY_B 66
+#define GLFW_KEY_C 67
+#define GLFW_KEY_D 68
+#define GLFW_KEY_E 69
+#define GLFW_KEY_F 70
+#define GLFW_KEY_G 71
+#define GLFW_KEY_H 72
+#define GLFW_KEY_I 73
+#define GLFW_KEY_J 74
+#define GLFW_KEY_K 75
+#define GLFW_KEY_L 76
+#define GLFW_KEY_M 77
+#define GLFW_KEY_N 78
+#define GLFW_KEY_O 79
+#define GLFW_KEY_P 80
+#define GLFW_KEY_Q 81
+#define GLFW_KEY_R 82
+#define GLFW_KEY_S 83
+#define GLFW_KEY_T 84
+#define GLFW_KEY_U 85
+#define GLFW_KEY_V 86
+#define GLFW_KEY_W 87
+#define GLFW_KEY_X 88
+#define GLFW_KEY_Y 89
+#define GLFW_KEY_Z 90
+#define GLFW_KEY_LEFT_BRACKET 91 /* [ */
+#define GLFW_KEY_BACKSLASH 92 /* \ */
+#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */
+#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */
+#define GLFW_KEY_WORLD_1 161 /* non-US #1 */
+#define GLFW_KEY_WORLD_2 162 /* non-US #2 */
+
+/* Function keys */
+#define GLFW_KEY_ESCAPE 256
+#define GLFW_KEY_ENTER 257
+#define GLFW_KEY_TAB 258
+#define GLFW_KEY_BACKSPACE 259
+#define GLFW_KEY_INSERT 260
+#define GLFW_KEY_DELETE 261
+#define GLFW_KEY_RIGHT 262
+#define GLFW_KEY_LEFT 263
+#define GLFW_KEY_DOWN 264
+#define GLFW_KEY_UP 265
+#define GLFW_KEY_PAGE_UP 266
+#define GLFW_KEY_PAGE_DOWN 267
+#define GLFW_KEY_HOME 268
+#define GLFW_KEY_END 269
+#define GLFW_KEY_CAPS_LOCK 280
+#define GLFW_KEY_SCROLL_LOCK 281
+#define GLFW_KEY_NUM_LOCK 282
+#define GLFW_KEY_PRINT_SCREEN 283
+#define GLFW_KEY_PAUSE 284
+#define GLFW_KEY_F1 290
+#define GLFW_KEY_F2 291
+#define GLFW_KEY_F3 292
+#define GLFW_KEY_F4 293
+#define GLFW_KEY_F5 294
+#define GLFW_KEY_F6 295
+#define GLFW_KEY_F7 296
+#define GLFW_KEY_F8 297
+#define GLFW_KEY_F9 298
+#define GLFW_KEY_F10 299
+#define GLFW_KEY_F11 300
+#define GLFW_KEY_F12 301
+#define GLFW_KEY_F13 302
+#define GLFW_KEY_F14 303
+#define GLFW_KEY_F15 304
+#define GLFW_KEY_F16 305
+#define GLFW_KEY_F17 306
+#define GLFW_KEY_F18 307
+#define GLFW_KEY_F19 308
+#define GLFW_KEY_F20 309
+#define GLFW_KEY_F21 310
+#define GLFW_KEY_F22 311
+#define GLFW_KEY_F23 312
+#define GLFW_KEY_F24 313
+#define GLFW_KEY_F25 314
+#define GLFW_KEY_KP_0 320
+#define GLFW_KEY_KP_1 321
+#define GLFW_KEY_KP_2 322
+#define GLFW_KEY_KP_3 323
+#define GLFW_KEY_KP_4 324
+#define GLFW_KEY_KP_5 325
+#define GLFW_KEY_KP_6 326
+#define GLFW_KEY_KP_7 327
+#define GLFW_KEY_KP_8 328
+#define GLFW_KEY_KP_9 329
+#define GLFW_KEY_KP_DECIMAL 330
+#define GLFW_KEY_KP_DIVIDE 331
+#define GLFW_KEY_KP_MULTIPLY 332
+#define GLFW_KEY_KP_SUBTRACT 333
+#define GLFW_KEY_KP_ADD 334
+#define GLFW_KEY_KP_ENTER 335
+#define GLFW_KEY_KP_EQUAL 336
+#define GLFW_KEY_LEFT_SHIFT 340
+#define GLFW_KEY_LEFT_CONTROL 341
+#define GLFW_KEY_LEFT_ALT 342
+#define GLFW_KEY_LEFT_SUPER 343
+#define GLFW_KEY_RIGHT_SHIFT 344
+#define GLFW_KEY_RIGHT_CONTROL 345
+#define GLFW_KEY_RIGHT_ALT 346
+#define GLFW_KEY_RIGHT_SUPER 347
+#define GLFW_KEY_MENU 348
+
+#define GLFW_KEY_LAST GLFW_KEY_MENU
+
+/*! @} */
+
+/*! @defgroup mods Modifier key flags
+ *
+ * See [key input](@ref input_key) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+
+/*! @brief If this bit is set one or more Shift keys were held down.
+ */
+#define GLFW_MOD_SHIFT 0x0001
+/*! @brief If this bit is set one or more Control keys were held down.
+ */
+#define GLFW_MOD_CONTROL 0x0002
+/*! @brief If this bit is set one or more Alt keys were held down.
+ */
+#define GLFW_MOD_ALT 0x0004
+/*! @brief If this bit is set one or more Super keys were held down.
+ */
+#define GLFW_MOD_SUPER 0x0008
+
+/*! @} */
+
+/*! @defgroup buttons Mouse buttons
+ *
+ * See [mouse button input](@ref input_mouse_button) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+#define GLFW_MOUSE_BUTTON_1 0
+#define GLFW_MOUSE_BUTTON_2 1
+#define GLFW_MOUSE_BUTTON_3 2
+#define GLFW_MOUSE_BUTTON_4 3
+#define GLFW_MOUSE_BUTTON_5 4
+#define GLFW_MOUSE_BUTTON_6 5
+#define GLFW_MOUSE_BUTTON_7 6
+#define GLFW_MOUSE_BUTTON_8 7
+#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8
+#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1
+#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2
+#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3
+/*! @} */
+
+/*! @defgroup joysticks Joysticks
+ *
+ * See [joystick input](@ref joystick) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+#define GLFW_JOYSTICK_1 0
+#define GLFW_JOYSTICK_2 1
+#define GLFW_JOYSTICK_3 2
+#define GLFW_JOYSTICK_4 3
+#define GLFW_JOYSTICK_5 4
+#define GLFW_JOYSTICK_6 5
+#define GLFW_JOYSTICK_7 6
+#define GLFW_JOYSTICK_8 7
+#define GLFW_JOYSTICK_9 8
+#define GLFW_JOYSTICK_10 9
+#define GLFW_JOYSTICK_11 10
+#define GLFW_JOYSTICK_12 11
+#define GLFW_JOYSTICK_13 12
+#define GLFW_JOYSTICK_14 13
+#define GLFW_JOYSTICK_15 14
+#define GLFW_JOYSTICK_16 15
+#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16
+/*! @} */
+
+/*! @defgroup errors Error codes
+ *
+ * See [error handling](@ref error_handling) for how these are used.
+ *
+ * @ingroup init
+ * @{ */
+/*! @brief GLFW has not been initialized.
+ *
+ * This occurs if a GLFW function was called that must not be called unless the
+ * library is [initialized](@ref intro_init).
+ *
+ * @analysis Application programmer error. Initialize GLFW before calling any
+ * function that requires initialization.
+ */
+#define GLFW_NOT_INITIALIZED 0x00010001
+/*! @brief No context is current for this thread.
+ *
+ * This occurs if a GLFW function was called that needs and operates on the
+ * current OpenGL or OpenGL ES context but no context is current on the calling
+ * thread. One such function is @ref glfwSwapInterval.
+ *
+ * @analysis Application programmer error. Ensure a context is current before
+ * calling functions that require a current context.
+ */
+#define GLFW_NO_CURRENT_CONTEXT 0x00010002
+/*! @brief One of the arguments to the function was an invalid enum value.
+ *
+ * One of the arguments to the function was an invalid enum value, for example
+ * requesting [GLFW_RED_BITS](@ref window_hints_fb) with @ref
+ * glfwGetWindowAttrib.
+ *
+ * @analysis Application programmer error. Fix the offending call.
+ */
+#define GLFW_INVALID_ENUM 0x00010003
+/*! @brief One of the arguments to the function was an invalid value.
+ *
+ * One of the arguments to the function was an invalid value, for example
+ * requesting a non-existent OpenGL or OpenGL ES version like 2.7.
+ *
+ * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead
+ * result in a @ref GLFW_VERSION_UNAVAILABLE error.
+ *
+ * @analysis Application programmer error. Fix the offending call.
+ */
+#define GLFW_INVALID_VALUE 0x00010004
+/*! @brief A memory allocation failed.
+ *
+ * A memory allocation failed.
+ *
+ * @analysis A bug in GLFW or the underlying operating system. Report the bug
+ * to our [issue tracker](https://github.com/glfw/glfw/issues).
+ */
+#define GLFW_OUT_OF_MEMORY 0x00010005
+/*! @brief GLFW could not find support for the requested API on the system.
+ *
+ * GLFW could not find support for the requested API on the system.
+ *
+ * @analysis The installed graphics driver does not support the requested
+ * API, or does not support it via the chosen context creation backend.
+ * Below are a few examples.
+ *
+ * @par
+ * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only
+ * supports OpenGL ES via EGL, while Nvidia and Intel only support it via
+ * a WGL or GLX extension. OS X does not provide OpenGL ES at all. The Mesa
+ * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary
+ * driver. Older graphics drivers do not support Vulkan.
+ */
+#define GLFW_API_UNAVAILABLE 0x00010006
+/*! @brief The requested OpenGL or OpenGL ES version is not available.
+ *
+ * The requested OpenGL or OpenGL ES version (including any requested context
+ * or framebuffer hints) is not available on this machine.
+ *
+ * @analysis The machine does not support your requirements. If your
+ * application is sufficiently flexible, downgrade your requirements and try
+ * again. Otherwise, inform the user that their machine does not match your
+ * requirements.
+ *
+ * @par
+ * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0
+ * comes out before the 4.x series gets that far, also fail with this error and
+ * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions
+ * will exist.
+ */
+#define GLFW_VERSION_UNAVAILABLE 0x00010007
+/*! @brief A platform-specific error occurred that does not match any of the
+ * more specific categories.
+ *
+ * A platform-specific error occurred that does not match any of the more
+ * specific categories.
+ *
+ * @analysis A bug or configuration error in GLFW, the underlying operating
+ * system or its drivers, or a lack of required resources. Report the issue to
+ * our [issue tracker](https://github.com/glfw/glfw/issues).
+ */
+#define GLFW_PLATFORM_ERROR 0x00010008
+/*! @brief The requested format is not supported or available.
+ *
+ * If emitted during window creation, the requested pixel format is not
+ * supported.
+ *
+ * If emitted when querying the clipboard, the contents of the clipboard could
+ * not be converted to the requested format.
+ *
+ * @analysis If emitted during window creation, one or more
+ * [hard constraints](@ref window_hints_hard) did not match any of the
+ * available pixel formats. If your application is sufficiently flexible,
+ * downgrade your requirements and try again. Otherwise, inform the user that
+ * their machine does not match your requirements.
+ *
+ * @par
+ * If emitted when querying the clipboard, ignore the error or report it to
+ * the user, as appropriate.
+ */
+#define GLFW_FORMAT_UNAVAILABLE 0x00010009
+/*! @brief The specified window does not have an OpenGL or OpenGL ES context.
+ *
+ * A window that does not have an OpenGL or OpenGL ES context was passed to
+ * a function that requires it to have one.
+ *
+ * @analysis Application programmer error. Fix the offending call.
+ */
+#define GLFW_NO_WINDOW_CONTEXT 0x0001000A
+/*! @} */
+
+#define GLFW_FOCUSED 0x00020001
+#define GLFW_ICONIFIED 0x00020002
+#define GLFW_RESIZABLE 0x00020003
+#define GLFW_VISIBLE 0x00020004
+#define GLFW_DECORATED 0x00020005
+#define GLFW_AUTO_ICONIFY 0x00020006
+#define GLFW_FLOATING 0x00020007
+#define GLFW_MAXIMIZED 0x00020008
+
+#define GLFW_RED_BITS 0x00021001
+#define GLFW_GREEN_BITS 0x00021002
+#define GLFW_BLUE_BITS 0x00021003
+#define GLFW_ALPHA_BITS 0x00021004
+#define GLFW_DEPTH_BITS 0x00021005
+#define GLFW_STENCIL_BITS 0x00021006
+#define GLFW_ACCUM_RED_BITS 0x00021007
+#define GLFW_ACCUM_GREEN_BITS 0x00021008
+#define GLFW_ACCUM_BLUE_BITS 0x00021009
+#define GLFW_ACCUM_ALPHA_BITS 0x0002100A
+#define GLFW_AUX_BUFFERS 0x0002100B
+#define GLFW_STEREO 0x0002100C
+#define GLFW_SAMPLES 0x0002100D
+#define GLFW_SRGB_CAPABLE 0x0002100E
+#define GLFW_REFRESH_RATE 0x0002100F
+#define GLFW_DOUBLEBUFFER 0x00021010
+
+#define GLFW_CLIENT_API 0x00022001
+#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002
+#define GLFW_CONTEXT_VERSION_MINOR 0x00022003
+#define GLFW_CONTEXT_REVISION 0x00022004
+#define GLFW_CONTEXT_ROBUSTNESS 0x00022005
+#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006
+#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007
+#define GLFW_OPENGL_PROFILE 0x00022008
+#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009
+#define GLFW_CONTEXT_NO_ERROR 0x0002200A
+#define GLFW_CONTEXT_CREATION_API 0x0002200B
+
+#define GLFW_NO_API 0
+#define GLFW_OPENGL_API 0x00030001
+#define GLFW_OPENGL_ES_API 0x00030002
+
+#define GLFW_NO_ROBUSTNESS 0
+#define GLFW_NO_RESET_NOTIFICATION 0x00031001
+#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002
+
+#define GLFW_OPENGL_ANY_PROFILE 0
+#define GLFW_OPENGL_CORE_PROFILE 0x00032001
+#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002
+
+#define GLFW_CURSOR 0x00033001
+#define GLFW_STICKY_KEYS 0x00033002
+#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
+
+#define GLFW_CURSOR_NORMAL 0x00034001
+#define GLFW_CURSOR_HIDDEN 0x00034002
+#define GLFW_CURSOR_DISABLED 0x00034003
+
+#define GLFW_ANY_RELEASE_BEHAVIOR 0
+#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001
+#define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002
+
+#define GLFW_NATIVE_CONTEXT_API 0x00036001
+#define GLFW_EGL_CONTEXT_API 0x00036002
+
+/*! @defgroup shapes Standard cursor shapes
+ *
+ * See [standard cursor creation](@ref cursor_standard) for how these are used.
+ *
+ * @ingroup input
+ * @{ */
+
+/*! @brief The regular arrow cursor shape.
+ *
+ * The regular arrow cursor.
+ */
+#define GLFW_ARROW_CURSOR 0x00036001
+/*! @brief The text input I-beam cursor shape.
+ *
+ * The text input I-beam cursor shape.
+ */
+#define GLFW_IBEAM_CURSOR 0x00036002
+/*! @brief The crosshair shape.
+ *
+ * The crosshair shape.
+ */
+#define GLFW_CROSSHAIR_CURSOR 0x00036003
+/*! @brief The hand shape.
+ *
+ * The hand shape.
+ */
+#define GLFW_HAND_CURSOR 0x00036004
+/*! @brief The horizontal resize arrow shape.
+ *
+ * The horizontal resize arrow shape.
+ */
+#define GLFW_HRESIZE_CURSOR 0x00036005
+/*! @brief The vertical resize arrow shape.
+ *
+ * The vertical resize arrow shape.
+ */
+#define GLFW_VRESIZE_CURSOR 0x00036006
+/*! @} */
+
+#define GLFW_CONNECTED 0x00040001
+#define GLFW_DISCONNECTED 0x00040002
+
+#define GLFW_DONT_CARE -1
+
+
+/*************************************************************************
+ * GLFW API types
+ *************************************************************************/
+
+/*! @brief Client API function pointer type.
+ *
+ * Generic function pointer used for returning client API function pointers
+ * without forcing a cast from a regular pointer.
+ *
+ * @sa @ref context_glext
+ * @sa glfwGetProcAddress
+ *
+ * @since Added in version 3.0.
+
+ * @ingroup context
+ */
+typedef void (*GLFWglproc)(void);
+
+/*! @brief Vulkan API function pointer type.
+ *
+ * Generic function pointer used for returning Vulkan API function pointers
+ * without forcing a cast from a regular pointer.
+ *
+ * @sa @ref vulkan_proc
+ * @sa glfwGetInstanceProcAddress
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+typedef void (*GLFWvkproc)(void);
+
+/*! @brief Opaque monitor object.
+ *
+ * Opaque monitor object.
+ *
+ * @see @ref monitor_object
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+typedef struct GLFWmonitor GLFWmonitor;
+
+/*! @brief Opaque window object.
+ *
+ * Opaque window object.
+ *
+ * @see @ref window_object
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef struct GLFWwindow GLFWwindow;
+
+/*! @brief Opaque cursor object.
+ *
+ * Opaque cursor object.
+ *
+ * @see @ref cursor_object
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup cursor
+ */
+typedef struct GLFWcursor GLFWcursor;
+
+/*! @brief The function signature for error callbacks.
+ *
+ * This is the function signature for error callback functions.
+ *
+ * @param[in] error An [error code](@ref errors).
+ * @param[in] description A UTF-8 encoded string describing the error.
+ *
+ * @sa @ref error_handling
+ * @sa glfwSetErrorCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup init
+ */
+typedef void (* GLFWerrorfun)(int,const char*);
+
+/*! @brief The function signature for window position callbacks.
+ *
+ * This is the function signature for window position callback functions.
+ *
+ * @param[in] window The window that was moved.
+ * @param[in] xpos The new x-coordinate, in screen coordinates, of the
+ * upper-left corner of the client area of the window.
+ * @param[in] ypos The new y-coordinate, in screen coordinates, of the
+ * upper-left corner of the client area of the window.
+ *
+ * @sa @ref window_pos
+ * @sa glfwSetWindowPosCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int);
+
+/*! @brief The function signature for window resize callbacks.
+ *
+ * This is the function signature for window size callback functions.
+ *
+ * @param[in] window The window that was resized.
+ * @param[in] width The new width, in screen coordinates, of the window.
+ * @param[in] height The new height, in screen coordinates, of the window.
+ *
+ * @sa @ref window_size
+ * @sa glfwSetWindowSizeCallback
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int);
+
+/*! @brief The function signature for window close callbacks.
+ *
+ * This is the function signature for window close callback functions.
+ *
+ * @param[in] window The window that the user attempted to close.
+ *
+ * @sa @ref window_close
+ * @sa glfwSetWindowCloseCallback
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowclosefun)(GLFWwindow*);
+
+/*! @brief The function signature for window content refresh callbacks.
+ *
+ * This is the function signature for window refresh callback functions.
+ *
+ * @param[in] window The window whose content needs to be refreshed.
+ *
+ * @sa @ref window_refresh
+ * @sa glfwSetWindowRefreshCallback
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowrefreshfun)(GLFWwindow*);
+
+/*! @brief The function signature for window focus/defocus callbacks.
+ *
+ * This is the function signature for window focus callback functions.
+ *
+ * @param[in] window The window that gained or lost input focus.
+ * @param[in] focused `GLFW_TRUE` if the window was given input focus, or
+ * `GLFW_FALSE` if it lost it.
+ *
+ * @sa @ref window_focus
+ * @sa glfwSetWindowFocusCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int);
+
+/*! @brief The function signature for window iconify/restore callbacks.
+ *
+ * This is the function signature for window iconify/restore callback
+ * functions.
+ *
+ * @param[in] window The window that was iconified or restored.
+ * @param[in] iconified `GLFW_TRUE` if the window was iconified, or
+ * `GLFW_FALSE` if it was restored.
+ *
+ * @sa @ref window_iconify
+ * @sa glfwSetWindowIconifyCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int);
+
+/*! @brief The function signature for framebuffer resize callbacks.
+ *
+ * This is the function signature for framebuffer resize callback
+ * functions.
+ *
+ * @param[in] window The window whose framebuffer was resized.
+ * @param[in] width The new width, in pixels, of the framebuffer.
+ * @param[in] height The new height, in pixels, of the framebuffer.
+ *
+ * @sa @ref window_fbsize
+ * @sa glfwSetFramebufferSizeCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int);
+
+/*! @brief The function signature for mouse button callbacks.
+ *
+ * This is the function signature for mouse button callback functions.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] button The [mouse button](@ref buttons) that was pressed or
+ * released.
+ * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`.
+ * @param[in] mods Bit field describing which [modifier keys](@ref mods) were
+ * held down.
+ *
+ * @sa @ref input_mouse_button
+ * @sa glfwSetMouseButtonCallback
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle and modifier mask parameters.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int);
+
+/*! @brief The function signature for cursor position callbacks.
+ *
+ * This is the function signature for cursor position callback functions.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] xpos The new cursor x-coordinate, relative to the left edge of
+ * the client area.
+ * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the
+ * client area.
+ *
+ * @sa @ref cursor_pos
+ * @sa glfwSetCursorPosCallback
+ *
+ * @since Added in version 3.0. Replaces `GLFWmouseposfun`.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double);
+
+/*! @brief The function signature for cursor enter/leave callbacks.
+ *
+ * This is the function signature for cursor enter/leave callback functions.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client
+ * area, or `GLFW_FALSE` if it left it.
+ *
+ * @sa @ref cursor_enter
+ * @sa glfwSetCursorEnterCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
+
+/*! @brief The function signature for scroll callbacks.
+ *
+ * This is the function signature for scroll callback functions.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] xoffset The scroll offset along the x-axis.
+ * @param[in] yoffset The scroll offset along the y-axis.
+ *
+ * @sa @ref scrolling
+ * @sa glfwSetScrollCallback
+ *
+ * @since Added in version 3.0. Replaces `GLFWmousewheelfun`.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWscrollfun)(GLFWwindow*,double,double);
+
+/*! @brief The function signature for keyboard key callbacks.
+ *
+ * This is the function signature for keyboard key callback functions.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] key The [keyboard key](@ref keys) that was pressed or released.
+ * @param[in] scancode The system-specific scancode of the key.
+ * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
+ * @param[in] mods Bit field describing which [modifier keys](@ref mods) were
+ * held down.
+ *
+ * @sa @ref input_key
+ * @sa glfwSetKeyCallback
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle, scancode and modifier mask parameters.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int);
+
+/*! @brief The function signature for Unicode character callbacks.
+ *
+ * This is the function signature for Unicode character callback functions.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] codepoint The Unicode code point of the character.
+ *
+ * @sa @ref input_char
+ * @sa glfwSetCharCallback
+ *
+ * @since Added in version 2.4.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
+
+/*! @brief The function signature for Unicode character with modifiers
+ * callbacks.
+ *
+ * This is the function signature for Unicode character with modifiers callback
+ * functions. It is called for each input character, regardless of what
+ * modifier keys are held down.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] codepoint The Unicode code point of the character.
+ * @param[in] mods Bit field describing which [modifier keys](@ref mods) were
+ * held down.
+ *
+ * @sa @ref input_char
+ * @sa glfwSetCharModsCallback
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int);
+
+/*! @brief The function signature for file drop callbacks.
+ *
+ * This is the function signature for file drop callbacks.
+ *
+ * @param[in] window The window that received the event.
+ * @param[in] count The number of dropped files.
+ * @param[in] paths The UTF-8 encoded file and/or directory path names.
+ *
+ * @sa @ref path_drop
+ * @sa glfwSetDropCallback
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**);
+
+/*! @brief The function signature for monitor configuration callbacks.
+ *
+ * This is the function signature for monitor configuration callback functions.
+ *
+ * @param[in] monitor The monitor that was connected or disconnected.
+ * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
+ *
+ * @sa @ref monitor_event
+ * @sa glfwSetMonitorCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+typedef void (* GLFWmonitorfun)(GLFWmonitor*,int);
+
+/*! @brief The function signature for joystick configuration callbacks.
+ *
+ * This is the function signature for joystick configuration callback
+ * functions.
+ *
+ * @param[in] joy The joystick that was connected or disconnected.
+ * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
+ *
+ * @sa @ref joystick_event
+ * @sa glfwSetJoystickCallback
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+typedef void (* GLFWjoystickfun)(int,int);
+
+/*! @brief Video mode type.
+ *
+ * This describes a single video mode.
+ *
+ * @sa @ref monitor_modes
+ * @sa glfwGetVideoMode glfwGetVideoModes
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added refresh rate member.
+ *
+ * @ingroup monitor
+ */
+typedef struct GLFWvidmode
+{
+ /*! The width, in screen coordinates, of the video mode.
+ */
+ int width;
+ /*! The height, in screen coordinates, of the video mode.
+ */
+ int height;
+ /*! The bit depth of the red channel of the video mode.
+ */
+ int redBits;
+ /*! The bit depth of the green channel of the video mode.
+ */
+ int greenBits;
+ /*! The bit depth of the blue channel of the video mode.
+ */
+ int blueBits;
+ /*! The refresh rate, in Hz, of the video mode.
+ */
+ int refreshRate;
+} GLFWvidmode;
+
+/*! @brief Gamma ramp.
+ *
+ * This describes the gamma ramp for a monitor.
+ *
+ * @sa @ref monitor_gamma
+ * @sa glfwGetGammaRamp glfwSetGammaRamp
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+typedef struct GLFWgammaramp
+{
+ /*! An array of value describing the response of the red channel.
+ */
+ unsigned short* red;
+ /*! An array of value describing the response of the green channel.
+ */
+ unsigned short* green;
+ /*! An array of value describing the response of the blue channel.
+ */
+ unsigned short* blue;
+ /*! The number of elements in each array.
+ */
+ unsigned int size;
+} GLFWgammaramp;
+
+/*! @brief Image data.
+ *
+ * @sa @ref cursor_custom
+ *
+ * @since Added in version 2.1.
+ * @glfw3 Removed format and bytes-per-pixel members.
+ */
+typedef struct GLFWimage
+{
+ /*! The width, in pixels, of this image.
+ */
+ int width;
+ /*! The height, in pixels, of this image.
+ */
+ int height;
+ /*! The pixel data of this image, arranged left-to-right, top-to-bottom.
+ */
+ unsigned char* pixels;
+} GLFWimage;
+
+
+/*************************************************************************
+ * GLFW API functions
+ *************************************************************************/
+
+/*! @brief Initializes the GLFW library.
+ *
+ * This function initializes the GLFW library. Before most GLFW functions can
+ * be used, GLFW must be initialized, and before an application terminates GLFW
+ * should be terminated in order to free any resources allocated during or
+ * after initialization.
+ *
+ * If this function fails, it calls @ref glfwTerminate before returning. If it
+ * succeeds, you should call @ref glfwTerminate before the application exits.
+ *
+ * Additional calls to this function after successful initialization but before
+ * termination will return `GLFW_TRUE` immediately.
+ *
+ * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark @osx This function will change the current directory of the
+ * application to the `Contents/Resources` subdirectory of the application's
+ * bundle, if present. This can be disabled with a
+ * [compile-time option](@ref compile_options_osx).
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref intro_init
+ * @sa glfwTerminate
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI int glfwInit(void);
+
+/*! @brief Terminates the GLFW library.
+ *
+ * This function destroys all remaining windows and cursors, restores any
+ * modified gamma ramps and frees any other allocated resources. Once this
+ * function is called, you must again call @ref glfwInit successfully before
+ * you will be able to use most GLFW functions.
+ *
+ * If GLFW has been successfully initialized, this function should be called
+ * before the application exits. If initialization fails, there is no need to
+ * call this function, as it is called by @ref glfwInit before it returns
+ * failure.
+ *
+ * @errors Possible errors include @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @warning The contexts of any remaining windows must not be current on any
+ * other thread when this function is called.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref intro_init
+ * @sa glfwInit
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI void glfwTerminate(void);
+
+/*! @brief Retrieves the version of the GLFW library.
+ *
+ * This function retrieves the major, minor and revision numbers of the GLFW
+ * library. It is intended for when you are using GLFW as a shared library and
+ * want to ensure that you are using the minimum required version.
+ *
+ * Any or all of the version arguments may be `NULL`.
+ *
+ * @param[out] major Where to store the major version number, or `NULL`.
+ * @param[out] minor Where to store the minor version number, or `NULL`.
+ * @param[out] rev Where to store the revision number, or `NULL`.
+ *
+ * @errors None.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref intro_version
+ * @sa glfwGetVersionString
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev);
+
+/*! @brief Returns a string describing the compile-time configuration.
+ *
+ * This function returns the compile-time generated
+ * [version string](@ref intro_version_string) of the GLFW library binary. It
+ * describes the version, platform, compiler and any platform-specific
+ * compile-time options. It should not be confused with the OpenGL or OpenGL
+ * ES version string, queried with `glGetString`.
+ *
+ * __Do not use the version string__ to parse the GLFW library version. The
+ * @ref glfwGetVersion function provides the version of the running library
+ * binary in numerical format.
+ *
+ * @return The ASCII encoded GLFW version string.
+ *
+ * @errors None.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @pointer_lifetime The returned string is static and compile-time generated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref intro_version
+ * @sa glfwGetVersion
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI const char* glfwGetVersionString(void);
+
+/*! @brief Sets the error callback.
+ *
+ * This function sets the error callback, which is called with an error code
+ * and a human-readable description each time a GLFW error occurs.
+ *
+ * The error callback is called on the thread where the error occurred. If you
+ * are using GLFW from multiple threads, your error callback needs to be
+ * written accordingly.
+ *
+ * Because the description string may have been generated specifically for that
+ * error, it is not guaranteed to be valid after the callback has returned. If
+ * you wish to use it after the callback returns, you need to make a copy.
+ *
+ * Once set, the error callback remains set even after the library has been
+ * terminated.
+ *
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set.
+ *
+ * @errors None.
+ *
+ * @remark This function may be called before @ref glfwInit.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref error_handling
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup init
+ */
+GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun);
+
+/*! @brief Returns the currently connected monitors.
+ *
+ * This function returns an array of handles for all currently connected
+ * monitors. The primary monitor is always first in the returned array. If no
+ * monitors were found, this function returns `NULL`.
+ *
+ * @param[out] count Where to store the number of monitors in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of monitor handles, or `NULL` if no monitors were found or
+ * if an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is guaranteed to be valid only until the
+ * monitor configuration changes or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_monitors
+ * @sa @ref monitor_event
+ * @sa glfwGetPrimaryMonitor
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI GLFWmonitor** glfwGetMonitors(int* count);
+
+/*! @brief Returns the primary monitor.
+ *
+ * This function returns the primary monitor. This is usually the monitor
+ * where elements like the task bar or global menu bar are located.
+ *
+ * @return The primary monitor, or `NULL` if no monitors were found or if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @remark The primary monitor is always first in the array returned by @ref
+ * glfwGetMonitors.
+ *
+ * @sa @ref monitor_monitors
+ * @sa glfwGetMonitors
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void);
+
+/*! @brief Returns the position of the monitor's viewport on the virtual screen.
+ *
+ * This function returns the position, in screen coordinates, of the upper-left
+ * corner of the specified monitor.
+ *
+ * Any or all of the position arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` position arguments will be set to zero.
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`.
+ * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_properties
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos);
+
+/*! @brief Returns the physical size of the monitor.
+ *
+ * This function returns the size, in millimetres, of the display area of the
+ * specified monitor.
+ *
+ * Some systems do not provide accurate monitor size information, either
+ * because the monitor
+ * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data)
+ * data is incorrect or because the driver does not report it accurately.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] widthMM Where to store the width, in millimetres, of the
+ * monitor's display area, or `NULL`.
+ * @param[out] heightMM Where to store the height, in millimetres, of the
+ * monitor's display area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark @win32 calculates the returned physical size from the
+ * current resolution and system DPI instead of querying the monitor EDID data.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_properties
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM);
+
+/*! @brief Returns the name of the specified monitor.
+ *
+ * This function returns a human-readable name, encoded as UTF-8, of the
+ * specified monitor. The name typically reflects the make and model of the
+ * monitor and is not guaranteed to be unique among the connected monitors.
+ *
+ * @param[in] monitor The monitor to query.
+ * @return The UTF-8 encoded name of the monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified monitor is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_properties
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor);
+
+/*! @brief Sets the monitor configuration callback.
+ *
+ * This function sets the monitor configuration callback, or removes the
+ * currently set callback. This is called when a monitor is connected to or
+ * disconnected from the system.
+ *
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_event
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun);
+
+/*! @brief Returns the available video modes for the specified monitor.
+ *
+ * This function returns an array of all video modes supported by the specified
+ * monitor. The returned array is sorted in ascending order, first by color
+ * bit depth (the sum of all channel depths) and then by resolution area (the
+ * product of width and height).
+ *
+ * @param[in] monitor The monitor to query.
+ * @param[out] count Where to store the number of video modes in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of video modes, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified monitor is
+ * disconnected, this function is called again for that monitor or the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_modes
+ * @sa glfwGetVideoMode
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Changed to return an array of modes for a specific monitor.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count);
+
+/*! @brief Returns the current mode of the specified monitor.
+ *
+ * This function returns the current video mode of the specified monitor. If
+ * you have created a full screen window for that monitor, the return value
+ * will depend on whether that window is iconified.
+ *
+ * @param[in] monitor The monitor to query.
+ * @return The current mode of the monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified monitor is
+ * disconnected or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_modes
+ * @sa glfwGetVideoModes
+ *
+ * @since Added in version 3.0. Replaces `glfwGetDesktopMode`.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor);
+
+/*! @brief Generates a gamma ramp and sets it for the specified monitor.
+ *
+ * This function generates a 256-element gamma ramp from the specified exponent
+ * and then calls @ref glfwSetGammaRamp with it. The value must be a finite
+ * number greater than zero.
+ *
+ * @param[in] monitor The monitor whose gamma ramp to set.
+ * @param[in] gamma The desired exponent.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_gamma
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma);
+
+/*! @brief Returns the current gamma ramp for the specified monitor.
+ *
+ * This function returns the current gamma ramp of the specified monitor.
+ *
+ * @param[in] monitor The monitor to query.
+ * @return The current gamma ramp, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned structure and its arrays are allocated and
+ * freed by GLFW. You should not free them yourself. They are valid until the
+ * specified monitor is disconnected, this function is called again for that
+ * monitor or the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_gamma
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor);
+
+/*! @brief Sets the current gamma ramp for the specified monitor.
+ *
+ * This function sets the current gamma ramp for the specified monitor. The
+ * original gamma ramp for that monitor is saved by GLFW the first time this
+ * function is called and is restored by @ref glfwTerminate.
+ *
+ * @param[in] monitor The monitor whose gamma ramp to set.
+ * @param[in] ramp The gamma ramp to use.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark Gamma ramp sizes other than 256 are not supported by all platforms
+ * or graphics hardware.
+ *
+ * @remark @win32 The gamma ramp size must be 256.
+ *
+ * @pointer_lifetime The specified gamma ramp is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref monitor_gamma
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup monitor
+ */
+GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp);
+
+/*! @brief Resets all window hints to their default values.
+ *
+ * This function resets all window hints to their
+ * [default values](@ref window_hints_values).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hints
+ * @sa glfwWindowHint
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwDefaultWindowHints(void);
+
+/*! @brief Sets the specified window hint to the desired value.
+ *
+ * This function sets hints for the next call to @ref glfwCreateWindow. The
+ * hints, once set, retain their values until changed by a call to @ref
+ * glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is
+ * terminated.
+ *
+ * This function does not check whether the specified hint values are valid.
+ * If you set hints to invalid values this will instead be reported by the next
+ * call to @ref glfwCreateWindow.
+ *
+ * @param[in] hint The [window hint](@ref window_hints) to set.
+ * @param[in] value The new value of the window hint.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hints
+ * @sa glfwDefaultWindowHints
+ *
+ * @since Added in version 3.0. Replaces `glfwOpenWindowHint`.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwWindowHint(int hint, int value);
+
+/*! @brief Creates a window and its associated context.
+ *
+ * This function creates a window and its associated OpenGL or OpenGL ES
+ * context. Most of the options controlling how the window and its context
+ * should be created are specified with [window hints](@ref window_hints).
+ *
+ * Successful creation does not change which context is current. Before you
+ * can use the newly created context, you need to
+ * [make it current](@ref context_current). For information about the `share`
+ * parameter, see @ref context_sharing.
+ *
+ * The created window, framebuffer and context may differ from what you
+ * requested, as not all parameters and hints are
+ * [hard constraints](@ref window_hints_hard). This includes the size of the
+ * window, especially for full screen windows. To query the actual attributes
+ * of the created window, framebuffer and context, see @ref
+ * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize.
+ *
+ * To create a full screen window, you need to specify the monitor the window
+ * will cover. If no monitor is specified, the window will be windowed mode.
+ * Unless you have a way for the user to choose a specific monitor, it is
+ * recommended that you pick the primary monitor. For more information on how
+ * to query connected monitors, see @ref monitor_monitors.
+ *
+ * For full screen windows, the specified size becomes the resolution of the
+ * window's _desired video mode_. As long as a full screen window is not
+ * iconified, the supported video mode most closely matching the desired video
+ * mode is set for the specified monitor. For more information about full
+ * screen windows, including the creation of so called _windowed full screen_
+ * or _borderless full screen_ windows, see @ref window_windowed_full_screen.
+ *
+ * By default, newly created windows use the placement recommended by the
+ * window system. To create the window at a specific position, make it
+ * initially invisible using the [GLFW_VISIBLE](@ref window_hints_wnd) window
+ * hint, set its [position](@ref window_pos) and then [show](@ref window_hide)
+ * it.
+ *
+ * As long as at least one full screen window is not iconified, the screensaver
+ * is prohibited from starting.
+ *
+ * Window systems put limits on window sizes. Very large or very small window
+ * dimensions may be overridden by the window system on creation. Check the
+ * actual [size](@ref window_size) after creation.
+ *
+ * The [swap interval](@ref buffer_swap) is not set during window creation and
+ * the initial value may vary depending on driver settings and defaults.
+ *
+ * @param[in] width The desired width, in screen coordinates, of the window.
+ * This must be greater than zero.
+ * @param[in] height The desired height, in screen coordinates, of the window.
+ * This must be greater than zero.
+ * @param[in] title The initial, UTF-8 encoded window title.
+ * @param[in] monitor The monitor to use for full screen mode, or `NULL` for
+ * windowed mode.
+ * @param[in] share The window whose context to share resources with, or `NULL`
+ * to not share resources.
+ * @return The handle of the created window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref
+ * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @win32 Window creation will fail if the Microsoft GDI software
+ * OpenGL implementation is the only one available.
+ *
+ * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it
+ * will be set as the initial icon for the window. If no such icon is present,
+ * the `IDI_WINLOGO` icon will be used instead. To set a different icon, see
+ * @ref glfwSetWindowIcon.
+ *
+ * @remark @win32 The context to share resources with must not be current on
+ * any other thread.
+ *
+ * @remark @osx The GLFW window has no icon, as it is not a document
+ * window, but the dock icon will be the same as the application bundle's icon.
+ * For more information on bundles, see the
+ * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
+ * in the Mac Developer Library.
+ *
+ * @remark @osx The first time a window is created the menu bar is populated
+ * with common commands like Hide, Quit and About. The About entry opens
+ * a minimal about dialog with information from the application's bundle. The
+ * menu bar can be disabled with a
+ * [compile-time option](@ref compile_options_osx).
+ *
+ * @remark @osx On OS X 10.10 and later the window frame will not be rendered
+ * at full resolution on Retina displays unless the `NSHighResolutionCapable`
+ * key is enabled in the application bundle's `Info.plist`. For more
+ * information, see
+ * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html)
+ * in the Mac Developer Library. The GLFW test and example programs use
+ * a custom `Info.plist` template for this, which can be found as
+ * `CMake/MacOSXBundleInfo.plist.in` in the source tree.
+ *
+ * @remark @x11 Some window managers will not respect the placement of
+ * initially hidden windows.
+ *
+ * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for
+ * a window to reach its requested state. This means you may not be able to
+ * query the final size, position or other attributes directly after window
+ * creation.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_creation
+ * @sa glfwDestroyWindow
+ *
+ * @since Added in version 3.0. Replaces `glfwOpenWindow`.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);
+
+/*! @brief Destroys the specified window and its context.
+ *
+ * This function destroys the specified window and its context. On calling
+ * this function, no further callbacks will be called for that window.
+ *
+ * If the context of the specified window is current on the main thread, it is
+ * detached before being destroyed.
+ *
+ * @param[in] window The window to destroy.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @note The context of the specified window must not be current on any other
+ * thread when this function is called.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_creation
+ * @sa glfwCreateWindow
+ *
+ * @since Added in version 3.0. Replaces `glfwCloseWindow`.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwDestroyWindow(GLFWwindow* window);
+
+/*! @brief Checks the close flag of the specified window.
+ *
+ * This function returns the value of the close flag of the specified window.
+ *
+ * @param[in] window The window to query.
+ * @return The value of the close flag.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_close
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI int glfwWindowShouldClose(GLFWwindow* window);
+
+/*! @brief Sets the close flag of the specified window.
+ *
+ * This function sets the value of the close flag of the specified window.
+ * This can be used to override the user's attempt to close the window, or
+ * to signal that it should be closed.
+ *
+ * @param[in] window The window whose flag to change.
+ * @param[in] value The new value.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_close
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value);
+
+/*! @brief Sets the title of the specified window.
+ *
+ * This function sets the window title, encoded as UTF-8, of the specified
+ * window.
+ *
+ * @param[in] window The window whose title to change.
+ * @param[in] title The UTF-8 encoded window title.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @remark @osx The window title will not be updated until the next time you
+ * process events.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_title
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
+
+/*! @brief Sets the icon for the specified window.
+ *
+ * This function sets the icon of the specified window. If passed an array of
+ * candidate images, those of or closest to the sizes desired by the system are
+ * selected. If no images are specified, the window reverts to its default
+ * icon.
+ *
+ * The desired image sizes varies depending on platform and system settings.
+ * The selected images will be rescaled as needed. Good sizes include 16x16,
+ * 32x32 and 48x48.
+ *
+ * @param[in] window The window whose icon to set.
+ * @param[in] count The number of images in the specified array, or zero to
+ * revert to the default window icon.
+ * @param[in] images The images to create the icon from. This is ignored if
+ * count is zero.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified image data is copied before this function
+ * returns.
+ *
+ * @remark @osx The GLFW window has no icon, as it is not a document
+ * window, so this function does nothing. The dock icon will be the same as
+ * the application bundle's icon. For more information on bundles, see the
+ * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
+ * in the Mac Developer Library.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_icon
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images);
+
+/*! @brief Retrieves the position of the client area of the specified window.
+ *
+ * This function retrieves the position, in screen coordinates, of the
+ * upper-left corner of the client area of the specified window.
+ *
+ * Any or all of the position arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` position arguments will be set to zero.
+ *
+ * @param[in] window The window to query.
+ * @param[out] xpos Where to store the x-coordinate of the upper-left corner of
+ * the client area, or `NULL`.
+ * @param[out] ypos Where to store the y-coordinate of the upper-left corner of
+ * the client area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_pos
+ * @sa glfwSetWindowPos
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos);
+
+/*! @brief Sets the position of the client area of the specified window.
+ *
+ * This function sets the position, in screen coordinates, of the upper-left
+ * corner of the client area of the specified windowed mode window. If the
+ * window is a full screen window, this function does nothing.
+ *
+ * __Do not use this function__ to move an already visible window unless you
+ * have very good reasons for doing so, as it will confuse and annoy the user.
+ *
+ * The window manager may put limits on what positions are allowed. GLFW
+ * cannot and should not override these limits.
+ *
+ * @param[in] window The window to query.
+ * @param[in] xpos The x-coordinate of the upper-left corner of the client area.
+ * @param[in] ypos The y-coordinate of the upper-left corner of the client area.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_pos
+ * @sa glfwGetWindowPos
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos);
+
+/*! @brief Retrieves the size of the client area of the specified window.
+ *
+ * This function retrieves the size, in screen coordinates, of the client area
+ * of the specified window. If you wish to retrieve the size of the
+ * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] window The window whose size to retrieve.
+ * @param[out] width Where to store the width, in screen coordinates, of the
+ * client area, or `NULL`.
+ * @param[out] height Where to store the height, in screen coordinates, of the
+ * client area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ * @sa glfwSetWindowSize
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height);
+
+/*! @brief Sets the size limits of the specified window.
+ *
+ * This function sets the size limits of the client area of the specified
+ * window. If the window is full screen, the size limits only take effect
+ * once it is made windowed. If the window is not resizable, this function
+ * does nothing.
+ *
+ * The size limits are applied immediately to a windowed mode window and may
+ * cause it to be resized.
+ *
+ * The maximum dimensions must be greater than or equal to the minimum
+ * dimensions and all must be greater than or equal to zero.
+ *
+ * @param[in] window The window to set limits for.
+ * @param[in] minwidth The minimum width, in screen coordinates, of the client
+ * area, or `GLFW_DONT_CARE`.
+ * @param[in] minheight The minimum height, in screen coordinates, of the
+ * client area, or `GLFW_DONT_CARE`.
+ * @param[in] maxwidth The maximum width, in screen coordinates, of the client
+ * area, or `GLFW_DONT_CARE`.
+ * @param[in] maxheight The maximum height, in screen coordinates, of the
+ * client area, or `GLFW_DONT_CARE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark If you set size limits and an aspect ratio that conflict, the
+ * results are undefined.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_sizelimits
+ * @sa glfwSetWindowAspectRatio
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight);
+
+/*! @brief Sets the aspect ratio of the specified window.
+ *
+ * This function sets the required aspect ratio of the client area of the
+ * specified window. If the window is full screen, the aspect ratio only takes
+ * effect once it is made windowed. If the window is not resizable, this
+ * function does nothing.
+ *
+ * The aspect ratio is specified as a numerator and a denominator and both
+ * values must be greater than zero. For example, the common 16:9 aspect ratio
+ * is specified as 16 and 9, respectively.
+ *
+ * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect
+ * ratio limit is disabled.
+ *
+ * The aspect ratio is applied immediately to a windowed mode window and may
+ * cause it to be resized.
+ *
+ * @param[in] window The window to set limits for.
+ * @param[in] numer The numerator of the desired aspect ratio, or
+ * `GLFW_DONT_CARE`.
+ * @param[in] denom The denominator of the desired aspect ratio, or
+ * `GLFW_DONT_CARE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark If you set size limits and an aspect ratio that conflict, the
+ * results are undefined.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_sizelimits
+ * @sa glfwSetWindowSizeLimits
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom);
+
+/*! @brief Sets the size of the client area of the specified window.
+ *
+ * This function sets the size, in screen coordinates, of the client area of
+ * the specified window.
+ *
+ * For full screen windows, this function updates the resolution of its desired
+ * video mode and switches to the video mode closest to it, without affecting
+ * the window's context. As the context is unaffected, the bit depths of the
+ * framebuffer remain unchanged.
+ *
+ * If you wish to update the refresh rate of the desired video mode in addition
+ * to its resolution, see @ref glfwSetWindowMonitor.
+ *
+ * The window manager may put limits on what sizes are allowed. GLFW cannot
+ * and should not override these limits.
+ *
+ * @param[in] window The window to resize.
+ * @param[in] width The desired width, in screen coordinates, of the window
+ * client area.
+ * @param[in] height The desired height, in screen coordinates, of the window
+ * client area.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ * @sa glfwGetWindowSize
+ * @sa glfwSetWindowMonitor
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height);
+
+/*! @brief Retrieves the size of the framebuffer of the specified window.
+ *
+ * This function retrieves the size, in pixels, of the framebuffer of the
+ * specified window. If you wish to retrieve the size of the window in screen
+ * coordinates, see @ref glfwGetWindowSize.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] window The window whose framebuffer to query.
+ * @param[out] width Where to store the width, in pixels, of the framebuffer,
+ * or `NULL`.
+ * @param[out] height Where to store the height, in pixels, of the framebuffer,
+ * or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_fbsize
+ * @sa glfwSetFramebufferSizeCallback
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height);
+
+/*! @brief Retrieves the size of the frame of the window.
+ *
+ * This function retrieves the size, in screen coordinates, of each edge of the
+ * frame of the specified window. This size includes the title bar, if the
+ * window has one. The size of the frame may vary depending on the
+ * [window-related hints](@ref window_hints_wnd) used to create it.
+ *
+ * Because this function retrieves the size of each window frame edge and not
+ * the offset along a particular coordinate axis, the retrieved values will
+ * always be zero or positive.
+ *
+ * Any or all of the size arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` size arguments will be set to zero.
+ *
+ * @param[in] window The window whose frame size to query.
+ * @param[out] left Where to store the size, in screen coordinates, of the left
+ * edge of the window frame, or `NULL`.
+ * @param[out] top Where to store the size, in screen coordinates, of the top
+ * edge of the window frame, or `NULL`.
+ * @param[out] right Where to store the size, in screen coordinates, of the
+ * right edge of the window frame, or `NULL`.
+ * @param[out] bottom Where to store the size, in screen coordinates, of the
+ * bottom edge of the window frame, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom);
+
+/*! @brief Iconifies the specified window.
+ *
+ * This function iconifies (minimizes) the specified window if it was
+ * previously restored. If the window is already iconified, this function does
+ * nothing.
+ *
+ * If the specified window is a full screen window, the original monitor
+ * resolution is restored until the window is restored.
+ *
+ * @param[in] window The window to iconify.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ * @sa glfwRestoreWindow
+ * @sa glfwMaximizeWindow
+ *
+ * @since Added in version 2.1.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwIconifyWindow(GLFWwindow* window);
+
+/*! @brief Restores the specified window.
+ *
+ * This function restores the specified window if it was previously iconified
+ * (minimized) or maximized. If the window is already restored, this function
+ * does nothing.
+ *
+ * If the specified window is a full screen window, the resolution chosen for
+ * the window is restored on the selected monitor.
+ *
+ * @param[in] window The window to restore.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ * @sa glfwIconifyWindow
+ * @sa glfwMaximizeWindow
+ *
+ * @since Added in version 2.1.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwRestoreWindow(GLFWwindow* window);
+
+/*! @brief Maximizes the specified window.
+ *
+ * This function maximizes the specified window if it was previously not
+ * maximized. If the window is already maximized, this function does nothing.
+ *
+ * If the specified window is a full screen window, this function does nothing.
+ *
+ * @param[in] window The window to maximize.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @par Thread Safety
+ * This function may only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ * @sa glfwIconifyWindow
+ * @sa glfwRestoreWindow
+ *
+ * @since Added in GLFW 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
+
+/*! @brief Makes the specified window visible.
+ *
+ * This function makes the specified window visible if it was previously
+ * hidden. If the window is already visible or is in full screen mode, this
+ * function does nothing.
+ *
+ * @param[in] window The window to make visible.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hide
+ * @sa glfwHideWindow
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwShowWindow(GLFWwindow* window);
+
+/*! @brief Hides the specified window.
+ *
+ * This function hides the specified window if it was previously visible. If
+ * the window is already hidden or is in full screen mode, this function does
+ * nothing.
+ *
+ * @param[in] window The window to hide.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_hide
+ * @sa glfwShowWindow
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwHideWindow(GLFWwindow* window);
+
+/*! @brief Brings the specified window to front and sets input focus.
+ *
+ * This function brings the specified window to front and sets input focus.
+ * The window should already be visible and not iconified.
+ *
+ * By default, both windowed and full screen mode windows are focused when
+ * initially created. Set the [GLFW_FOCUSED](@ref window_hints_wnd) to disable
+ * this behavior.
+ *
+ * __Do not use this function__ to steal focus from other applications unless
+ * you are certain that is what the user wants. Focus stealing can be
+ * extremely disruptive.
+ *
+ * @param[in] window The window to give input focus.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_focus
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwFocusWindow(GLFWwindow* window);
+
+/*! @brief Returns the monitor that the window uses for full screen mode.
+ *
+ * This function returns the handle of the monitor that the specified window is
+ * in full screen on.
+ *
+ * @param[in] window The window to query.
+ * @return The monitor, or `NULL` if the window is in windowed mode or an error
+ * occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_monitor
+ * @sa glfwSetWindowMonitor
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window);
+
+/*! @brief Sets the mode, monitor, video mode and placement of a window.
+ *
+ * This function sets the monitor that the window uses for full screen mode or,
+ * if the monitor is `NULL`, makes it windowed mode.
+ *
+ * When setting a monitor, this function updates the width, height and refresh
+ * rate of the desired video mode and switches to the video mode closest to it.
+ * The window position is ignored when setting a monitor.
+ *
+ * When the monitor is `NULL`, the position, width and height are used to
+ * place the window client area. The refresh rate is ignored when no monitor
+ * is specified.
+ *
+ * If you only wish to update the resolution of a full screen window or the
+ * size of a windowed mode window, see @ref glfwSetWindowSize.
+ *
+ * When a window transitions from full screen to windowed mode, this function
+ * restores any previous window settings such as whether it is decorated,
+ * floating, resizable, has size or aspect ratio limits, etc..
+ *
+ * @param[in] window The window whose monitor, size or video mode to set.
+ * @param[in] monitor The desired monitor, or `NULL` to set windowed mode.
+ * @param[in] xpos The desired x-coordinate of the upper-left corner of the
+ * client area.
+ * @param[in] ypos The desired y-coordinate of the upper-left corner of the
+ * client area.
+ * @param[in] width The desired with, in screen coordinates, of the client area
+ * or video mode.
+ * @param[in] height The desired height, in screen coordinates, of the client
+ * area or video mode.
+ * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode,
+ * or `GLFW_DONT_CARE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_monitor
+ * @sa @ref window_full_screen
+ * @sa glfwGetWindowMonitor
+ * @sa glfwSetWindowSize
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
+
+/*! @brief Returns an attribute of the specified window.
+ *
+ * This function returns the value of an attribute of the specified window or
+ * its OpenGL or OpenGL ES context.
+ *
+ * @param[in] window The window to query.
+ * @param[in] attrib The [window attribute](@ref window_attribs) whose value to
+ * return.
+ * @return The value of the attribute, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark Framebuffer related hints are not window attributes. See @ref
+ * window_attribs_fb for more information.
+ *
+ * @remark Zero is a valid value for many window and context related
+ * attributes so you cannot use a return value of zero as an indication of
+ * errors. However, this function should not fail as long as it is passed
+ * valid arguments and the library has been [initialized](@ref intro_init).
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_attribs
+ *
+ * @since Added in version 3.0. Replaces `glfwGetWindowParam` and
+ * `glfwGetGLVersion`.
+ *
+ * @ingroup window
+ */
+GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
+
+/*! @brief Sets the user pointer of the specified window.
+ *
+ * This function sets the user-defined pointer of the specified window. The
+ * current value is retained until the window is destroyed. The initial value
+ * is `NULL`.
+ *
+ * @param[in] window The window whose pointer to set.
+ * @param[in] pointer The new value.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_userptr
+ * @sa glfwGetWindowUserPointer
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer);
+
+/*! @brief Returns the user pointer of the specified window.
+ *
+ * This function returns the current value of the user-defined pointer of the
+ * specified window. The initial value is `NULL`.
+ *
+ * @param[in] window The window whose pointer to return.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @sa @ref window_userptr
+ * @sa glfwSetWindowUserPointer
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window);
+
+/*! @brief Sets the position callback for the specified window.
+ *
+ * This function sets the position callback of the specified window, which is
+ * called when the window is moved. The callback is provided with the screen
+ * position of the upper-left corner of the client area of the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_pos
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun cbfun);
+
+/*! @brief Sets the size callback for the specified window.
+ *
+ * This function sets the size callback of the specified window, which is
+ * called when the window is resized. The callback is provided with the size,
+ * in screen coordinates, of the client area of the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_size
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun cbfun);
+
+/*! @brief Sets the close callback for the specified window.
+ *
+ * This function sets the close callback of the specified window, which is
+ * called when the user attempts to close the window, for example by clicking
+ * the close widget in the title bar.
+ *
+ * The close flag is set before this callback is called, but you can modify it
+ * at any time with @ref glfwSetWindowShouldClose.
+ *
+ * The close callback is not triggered by @ref glfwDestroyWindow.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @remark @osx Selecting Quit from the application menu will trigger the close
+ * callback for all windows.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_close
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun cbfun);
+
+/*! @brief Sets the refresh callback for the specified window.
+ *
+ * This function sets the refresh callback of the specified window, which is
+ * called when the client area of the window needs to be redrawn, for example
+ * if the window has been exposed after having been covered by another window.
+ *
+ * On compositing window systems such as Aero, Compiz or Aqua, where the window
+ * contents are saved off-screen, this callback may be called only very
+ * infrequently or never at all.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_refresh
+ *
+ * @since Added in version 2.5.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun cbfun);
+
+/*! @brief Sets the focus callback for the specified window.
+ *
+ * This function sets the focus callback of the specified window, which is
+ * called when the window gains or loses input focus.
+ *
+ * After the focus callback is called for a window that lost input focus,
+ * synthetic key and mouse button release events will be generated for all such
+ * that had been pressed. For more information, see @ref glfwSetKeyCallback
+ * and @ref glfwSetMouseButtonCallback.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_focus
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun cbfun);
+
+/*! @brief Sets the iconify callback for the specified window.
+ *
+ * This function sets the iconification callback of the specified window, which
+ * is called when the window is iconified or restored.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_iconify
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun);
+
+/*! @brief Sets the framebuffer resize callback for the specified window.
+ *
+ * This function sets the framebuffer resize callback of the specified window,
+ * which is called when the framebuffer of the specified window is resized.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref window_fbsize
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun);
+
+/*! @brief Processes all pending events.
+ *
+ * This function processes only those events that are already in the event
+ * queue and then returns immediately. Processing events will cause the window
+ * and input callbacks associated with those events to be called.
+ *
+ * On some platforms, a window move, resize or menu operation will cause event
+ * processing to block. This is due to how event processing is designed on
+ * those platforms. You can use the
+ * [window refresh callback](@ref window_refresh) to redraw the contents of
+ * your window when necessary during such operations.
+ *
+ * On some platforms, certain events are sent directly to the application
+ * without going through the event queue, causing callbacks to be called
+ * outside of a call to one of the event processing functions.
+ *
+ * Event processing is not required for joystick input to work.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref events
+ * @sa glfwWaitEvents
+ * @sa glfwWaitEventsTimeout
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwPollEvents(void);
+
+/*! @brief Waits until events are queued and processes them.
+ *
+ * This function puts the calling thread to sleep until at least one event is
+ * available in the event queue. Once one or more events are available,
+ * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue
+ * are processed and the function then returns immediately. Processing events
+ * will cause the window and input callbacks associated with those events to be
+ * called.
+ *
+ * Since not all events are associated with callbacks, this function may return
+ * without a callback having been called even if you are monitoring all
+ * callbacks.
+ *
+ * On some platforms, a window move, resize or menu operation will cause event
+ * processing to block. This is due to how event processing is designed on
+ * those platforms. You can use the
+ * [window refresh callback](@ref window_refresh) to redraw the contents of
+ * your window when necessary during such operations.
+ *
+ * On some platforms, certain callbacks may be called outside of a call to one
+ * of the event processing functions.
+ *
+ * If no windows exist, this function returns immediately. For synchronization
+ * of threads in applications that do not create windows, use your threading
+ * library of choice.
+ *
+ * Event processing is not required for joystick input to work.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref events
+ * @sa glfwPollEvents
+ * @sa glfwWaitEventsTimeout
+ *
+ * @since Added in version 2.5.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwWaitEvents(void);
+
+/*! @brief Waits with timeout until events are queued and processes them.
+ *
+ * This function puts the calling thread to sleep until at least one event is
+ * available in the event queue, or until the specified timeout is reached. If
+ * one or more events are available, it behaves exactly like @ref
+ * glfwPollEvents, i.e. the events in the queue are processed and the function
+ * then returns immediately. Processing events will cause the window and input
+ * callbacks associated with those events to be called.
+ *
+ * The timeout value must be a positive finite number.
+ *
+ * Since not all events are associated with callbacks, this function may return
+ * without a callback having been called even if you are monitoring all
+ * callbacks.
+ *
+ * On some platforms, a window move, resize or menu operation will cause event
+ * processing to block. This is due to how event processing is designed on
+ * those platforms. You can use the
+ * [window refresh callback](@ref window_refresh) to redraw the contents of
+ * your window when necessary during such operations.
+ *
+ * On some platforms, certain callbacks may be called outside of a call to one
+ * of the event processing functions.
+ *
+ * If no windows exist, this function returns immediately. For synchronization
+ * of threads in applications that do not create windows, use your threading
+ * library of choice.
+ *
+ * Event processing is not required for joystick input to work.
+ *
+ * @param[in] timeout The maximum amount of time, in seconds, to wait.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref events
+ * @sa glfwPollEvents
+ * @sa glfwWaitEvents
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwWaitEventsTimeout(double timeout);
+
+/*! @brief Posts an empty event to the event queue.
+ *
+ * This function posts an empty event from the current thread to the event
+ * queue, causing @ref glfwWaitEvents to return.
+ *
+ * If no windows exist, this function returns immediately. For synchronization
+ * of threads in applications that do not create windows, use your threading
+ * library of choice.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref events
+ * @sa glfwWaitEvents
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwPostEmptyEvent(void);
+
+/*! @brief Returns the value of an input option for the specified window.
+ *
+ * This function returns the value of an input option for the specified window.
+ * The mode must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
+ * `GLFW_STICKY_MOUSE_BUTTONS`.
+ *
+ * @param[in] window The window to query.
+ * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
+ * `GLFW_STICKY_MOUSE_BUTTONS`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa glfwSetInputMode
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
+
+/*! @brief Sets an input option for the specified window.
+ *
+ * This function sets an input mode option for the specified window. The mode
+ * must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
+ * `GLFW_STICKY_MOUSE_BUTTONS`.
+ *
+ * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
+ * modes:
+ * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally.
+ * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client
+ * area of the window but does not restrict the cursor from leaving.
+ * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual
+ * and unlimited cursor movement. This is useful for implementing for
+ * example 3D camera controls.
+ *
+ * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to
+ * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are
+ * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS`
+ * the next time it is called even if the key had been released before the
+ * call. This is useful when you are only interested in whether keys have been
+ * pressed but not when or in which order.
+ *
+ * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either
+ * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it.
+ * If sticky mouse buttons are enabled, a mouse button press will ensure that
+ * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even
+ * if the mouse button had been released before the call. This is useful when
+ * you are only interested in whether mouse buttons have been pressed but not
+ * when or in which order.
+ *
+ * @param[in] window The window whose input mode to set.
+ * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
+ * `GLFW_STICKY_MOUSE_BUTTONS`.
+ * @param[in] value The new value of the specified input mode.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa glfwGetInputMode
+ *
+ * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value);
+
+/*! @brief Returns the localized name of the specified printable key.
+ *
+ * This function returns the localized name of the specified printable key.
+ * This is intended for displaying key bindings to the user.
+ *
+ * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used instead, otherwise
+ * the scancode is ignored. If a non-printable key or (if the key is
+ * `GLFW_KEY_UNKNOWN`) a scancode that maps to a non-printable key is
+ * specified, this function returns `NULL`.
+ *
+ * This behavior allows you to pass in the arguments passed to the
+ * [key callback](@ref input_key) without modification.
+ *
+ * The printable keys are:
+ * - `GLFW_KEY_APOSTROPHE`
+ * - `GLFW_KEY_COMMA`
+ * - `GLFW_KEY_MINUS`
+ * - `GLFW_KEY_PERIOD`
+ * - `GLFW_KEY_SLASH`
+ * - `GLFW_KEY_SEMICOLON`
+ * - `GLFW_KEY_EQUAL`
+ * - `GLFW_KEY_LEFT_BRACKET`
+ * - `GLFW_KEY_RIGHT_BRACKET`
+ * - `GLFW_KEY_BACKSLASH`
+ * - `GLFW_KEY_WORLD_1`
+ * - `GLFW_KEY_WORLD_2`
+ * - `GLFW_KEY_0` to `GLFW_KEY_9`
+ * - `GLFW_KEY_A` to `GLFW_KEY_Z`
+ * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9`
+ * - `GLFW_KEY_KP_DECIMAL`
+ * - `GLFW_KEY_KP_DIVIDE`
+ * - `GLFW_KEY_KP_MULTIPLY`
+ * - `GLFW_KEY_KP_SUBTRACT`
+ * - `GLFW_KEY_KP_ADD`
+ * - `GLFW_KEY_KP_EQUAL`
+ *
+ * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`.
+ * @param[in] scancode The scancode of the key to query.
+ * @return The localized name of the key, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the next call to @ref
+ * glfwGetKeyName, or until the library is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_key_name
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetKeyName(int key, int scancode);
+
+/*! @brief Returns the last reported state of a keyboard key for the specified
+ * window.
+ *
+ * This function returns the last state reported for the specified key to the
+ * specified window. The returned state is one of `GLFW_PRESS` or
+ * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to
+ * the key callback.
+ *
+ * If the `GLFW_STICKY_KEYS` input mode is enabled, this function returns
+ * `GLFW_PRESS` the first time you call it for a key that was pressed, even if
+ * that key has already been released.
+ *
+ * The key functions deal with physical keys, with [key tokens](@ref keys)
+ * named after their use on the standard US keyboard layout. If you want to
+ * input text, use the Unicode character callback instead.
+ *
+ * The [modifier key bit masks](@ref mods) are not key tokens and cannot be
+ * used with this function.
+ *
+ * __Do not use this function__ to implement [text input](@ref input_char).
+ *
+ * @param[in] window The desired window.
+ * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is
+ * not a valid key for this function.
+ * @return One of `GLFW_PRESS` or `GLFW_RELEASE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_key
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetKey(GLFWwindow* window, int key);
+
+/*! @brief Returns the last reported state of a mouse button for the specified
+ * window.
+ *
+ * This function returns the last state reported for the specified mouse button
+ * to the specified window. The returned state is one of `GLFW_PRESS` or
+ * `GLFW_RELEASE`.
+ *
+ * If the `GLFW_STICKY_MOUSE_BUTTONS` input mode is enabled, this function
+ * `GLFW_PRESS` the first time you call it for a mouse button that was pressed,
+ * even if that mouse button has already been released.
+ *
+ * @param[in] window The desired window.
+ * @param[in] button The desired [mouse button](@ref buttons).
+ * @return One of `GLFW_PRESS` or `GLFW_RELEASE`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_ENUM.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_mouse_button
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button);
+
+/*! @brief Retrieves the position of the cursor relative to the client area of
+ * the window.
+ *
+ * This function returns the position of the cursor, in screen coordinates,
+ * relative to the upper-left corner of the client area of the specified
+ * window.
+ *
+ * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor
+ * position is unbounded and limited only by the minimum and maximum values of
+ * a `double`.
+ *
+ * The coordinate can be converted to their integer equivalents with the
+ * `floor` function. Casting directly to an integer type works for positive
+ * coordinates, but fails for negative ones.
+ *
+ * Any or all of the position arguments may be `NULL`. If an error occurs, all
+ * non-`NULL` position arguments will be set to zero.
+ *
+ * @param[in] window The desired window.
+ * @param[out] xpos Where to store the cursor x-coordinate, relative to the
+ * left edge of the client area, or `NULL`.
+ * @param[out] ypos Where to store the cursor y-coordinate, relative to the to
+ * top edge of the client area, or `NULL`.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_pos
+ * @sa glfwSetCursorPos
+ *
+ * @since Added in version 3.0. Replaces `glfwGetMousePos`.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos);
+
+/*! @brief Sets the position of the cursor, relative to the client area of the
+ * window.
+ *
+ * This function sets the position, in screen coordinates, of the cursor
+ * relative to the upper-left corner of the client area of the specified
+ * window. The window must have input focus. If the window does not have
+ * input focus when this function is called, it fails silently.
+ *
+ * __Do not use this function__ to implement things like camera controls. GLFW
+ * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the
+ * cursor, transparently re-centers it and provides unconstrained cursor
+ * motion. See @ref glfwSetInputMode for more information.
+ *
+ * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is
+ * unconstrained and limited only by the minimum and maximum values of
+ * a `double`.
+ *
+ * @param[in] window The desired window.
+ * @param[in] xpos The desired x-coordinate, relative to the left edge of the
+ * client area.
+ * @param[in] ypos The desired y-coordinate, relative to the top edge of the
+ * client area.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_pos
+ * @sa glfwGetCursorPos
+ *
+ * @since Added in version 3.0. Replaces `glfwSetMousePos`.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos);
+
+/*! @brief Creates a custom cursor.
+ *
+ * Creates a new custom cursor image that can be set for a window with @ref
+ * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor.
+ * Any remaining cursors are destroyed by @ref glfwTerminate.
+ *
+ * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight
+ * bits per channel. They are arranged canonically as packed sequential rows,
+ * starting from the top-left corner.
+ *
+ * The cursor hotspot is specified in pixels, relative to the upper-left corner
+ * of the cursor image. Like all other coordinate systems in GLFW, the X-axis
+ * points to the right and the Y-axis points down.
+ *
+ * @param[in] image The desired cursor image.
+ * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot.
+ * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot.
+ * @return The handle of the created cursor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified image data is copied before this function
+ * returns.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ * @sa glfwDestroyCursor
+ * @sa glfwCreateStandardCursor
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot);
+
+/*! @brief Creates a cursor with a standard shape.
+ *
+ * Returns a cursor with a [standard shape](@ref shapes), that can be set for
+ * a window with @ref glfwSetCursor.
+ *
+ * @param[in] shape One of the [standard shapes](@ref shapes).
+ * @return A new cursor ready to use or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ * @sa glfwCreateCursor
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape);
+
+/*! @brief Destroys a cursor.
+ *
+ * This function destroys a cursor previously created with @ref
+ * glfwCreateCursor. Any remaining cursors will be destroyed by @ref
+ * glfwTerminate.
+ *
+ * @param[in] cursor The cursor object to destroy.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @reentrancy This function must not be called from a callback.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ * @sa glfwCreateCursor
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
+
+/*! @brief Sets the cursor for the window.
+ *
+ * This function sets the cursor image to be used when the cursor is over the
+ * client area of the specified window. The set cursor will only be visible
+ * when the [cursor mode](@ref cursor_mode) of the window is
+ * `GLFW_CURSOR_NORMAL`.
+ *
+ * On some platforms, the set cursor may not be visible unless the window also
+ * has input focus.
+ *
+ * @param[in] window The window to set the cursor for.
+ * @param[in] cursor The cursor to set, or `NULL` to switch back to the default
+ * arrow cursor.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_object
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
+
+/*! @brief Sets the key callback.
+ *
+ * This function sets the key callback of the specified window, which is called
+ * when a key is pressed, repeated or released.
+ *
+ * The key functions deal with physical keys, with layout independent
+ * [key tokens](@ref keys) named after their values in the standard US keyboard
+ * layout. If you want to input text, use the
+ * [character callback](@ref glfwSetCharCallback) instead.
+ *
+ * When a window loses input focus, it will generate synthetic key release
+ * events for all pressed keys. You can tell these events from user-generated
+ * events by the fact that the synthetic ones are generated after the focus
+ * loss event has been processed, i.e. after the
+ * [window focus callback](@ref glfwSetWindowFocusCallback) has been called.
+ *
+ * The scancode of a key is specific to that platform or sometimes even to that
+ * machine. Scancodes are intended to allow users to bind keys that don't have
+ * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their
+ * state is not saved and so it cannot be queried with @ref glfwGetKey.
+ *
+ * Sometimes GLFW needs to generate synthetic key events, in which case the
+ * scancode may be zero.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new key callback, or `NULL` to remove the currently
+ * set callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_key
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun);
+
+/*! @brief Sets the Unicode character callback.
+ *
+ * This function sets the character callback of the specified window, which is
+ * called when a Unicode character is input.
+ *
+ * The character callback is intended for Unicode text input. As it deals with
+ * characters, it is keyboard layout dependent, whereas the
+ * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1
+ * to physical keys, as a key may produce zero, one or more characters. If you
+ * want to know whether a specific physical key was pressed or released, see
+ * the key callback instead.
+ *
+ * The character callback behaves as system text input normally does and will
+ * not be called if modifier keys are held down that would prevent normal text
+ * input on that platform, for example a Super (Command) key on OS X or Alt key
+ * on Windows. There is a
+ * [character with modifiers callback](@ref glfwSetCharModsCallback) that
+ * receives these events.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_char
+ *
+ * @since Added in version 2.4.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun);
+
+/*! @brief Sets the Unicode character with modifiers callback.
+ *
+ * This function sets the character with modifiers callback of the specified
+ * window, which is called when a Unicode character is input regardless of what
+ * modifier keys are used.
+ *
+ * The character with modifiers callback is intended for implementing custom
+ * Unicode character input. For regular Unicode text input, see the
+ * [character callback](@ref glfwSetCharCallback). Like the character
+ * callback, the character with modifiers callback deals with characters and is
+ * keyboard layout dependent. Characters do not map 1:1 to physical keys, as
+ * a key may produce zero, one or more characters. If you want to know whether
+ * a specific physical key was pressed or released, see the
+ * [key callback](@ref glfwSetKeyCallback) instead.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or an
+ * error occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_char
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun);
+
+/*! @brief Sets the mouse button callback.
+ *
+ * This function sets the mouse button callback of the specified window, which
+ * is called when a mouse button is pressed or released.
+ *
+ * When a window loses input focus, it will generate synthetic mouse button
+ * release events for all pressed mouse buttons. You can tell these events
+ * from user-generated events by the fact that the synthetic ones are generated
+ * after the focus loss event has been processed, i.e. after the
+ * [window focus callback](@ref glfwSetWindowFocusCallback) has been called.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref input_mouse_button
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter and return value.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun cbfun);
+
+/*! @brief Sets the cursor position callback.
+ *
+ * This function sets the cursor position callback of the specified window,
+ * which is called when the cursor is moved. The callback is provided with the
+ * position, in screen coordinates, relative to the upper-left corner of the
+ * client area of the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_pos
+ *
+ * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun cbfun);
+
+/*! @brief Sets the cursor enter/exit callback.
+ *
+ * This function sets the cursor boundary crossing callback of the specified
+ * window, which is called when the cursor enters or leaves the client area of
+ * the window.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref cursor_enter
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun cbfun);
+
+/*! @brief Sets the scroll callback.
+ *
+ * This function sets the scroll callback of the specified window, which is
+ * called when a scrolling device is used, such as a mouse wheel or scrolling
+ * area of a touchpad.
+ *
+ * The scroll callback receives all scrolling input, like that from a mouse
+ * wheel or a touchpad scrolling area.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new scroll callback, or `NULL` to remove the currently
+ * set callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref scrolling
+ *
+ * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun);
+
+/*! @brief Sets the file drop callback.
+ *
+ * This function sets the file drop callback of the specified window, which is
+ * called when one or more dragged files are dropped on the window.
+ *
+ * Because the path array and its strings may have been generated specifically
+ * for that event, they are not guaranteed to be valid after the callback has
+ * returned. If you wish to use them after the callback returns, you need to
+ * make a deep copy.
+ *
+ * @param[in] window The window whose callback to set.
+ * @param[in] cbfun The new file drop callback, or `NULL` to remove the
+ * currently set callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref path_drop
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun);
+
+/*! @brief Returns whether the specified joystick is present.
+ *
+ * This function returns whether the specified joystick is present.
+ *
+ * @param[in] joy The [joystick](@ref joysticks) to query.
+ * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick
+ *
+ * @since Added in version 3.0. Replaces `glfwGetJoystickParam`.
+ *
+ * @ingroup input
+ */
+GLFWAPI int glfwJoystickPresent(int joy);
+
+/*! @brief Returns the values of all axes of the specified joystick.
+ *
+ * This function returns the values of all axes of the specified joystick.
+ * Each element in the array is a value between -1.0 and 1.0.
+ *
+ * Querying a joystick slot with no device present is not an error, but will
+ * cause this function to return `NULL`. Call @ref glfwJoystickPresent to
+ * check device presence.
+ *
+ * @param[in] joy The [joystick](@ref joysticks) to query.
+ * @param[out] count Where to store the number of axis values in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of axis values, or `NULL` if the joystick is not present.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected, this function is called again for that joystick or the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_axis
+ *
+ * @since Added in version 3.0. Replaces `glfwGetJoystickPos`.
+ *
+ * @ingroup input
+ */
+GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count);
+
+/*! @brief Returns the state of all buttons of the specified joystick.
+ *
+ * This function returns the state of all buttons of the specified joystick.
+ * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`.
+ *
+ * Querying a joystick slot with no device present is not an error, but will
+ * cause this function to return `NULL`. Call @ref glfwJoystickPresent to
+ * check device presence.
+ *
+ * @param[in] joy The [joystick](@ref joysticks) to query.
+ * @param[out] count Where to store the number of button states in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of button states, or `NULL` if the joystick is not present.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected, this function is called again for that joystick or the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_button
+ *
+ * @since Added in version 2.2.
+ * @glfw3 Changed to return a dynamic array.
+ *
+ * @ingroup input
+ */
+GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count);
+
+/*! @brief Returns the name of the specified joystick.
+ *
+ * This function returns the name, encoded as UTF-8, of the specified joystick.
+ * The returned string is allocated and freed by GLFW. You should not free it
+ * yourself.
+ *
+ * Querying a joystick slot with no device present is not an error, but will
+ * cause this function to return `NULL`. Call @ref glfwJoystickPresent to
+ * check device presence.
+ *
+ * @param[in] joy The [joystick](@ref joysticks) to query.
+ * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick
+ * is not present.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the specified joystick is
+ * disconnected, this function is called again for that joystick or the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_name
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetJoystickName(int joy);
+
+/*! @brief Sets the joystick configuration callback.
+ *
+ * This function sets the joystick configuration callback, or removes the
+ * currently set callback. This is called when a joystick is connected to or
+ * disconnected from the system.
+ *
+ * @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ * callback.
+ * @return The previously set callback, or `NULL` if no callback was set or the
+ * library had not been [initialized](@ref intro_init).
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref joystick_event
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun);
+
+/*! @brief Sets the clipboard to the specified string.
+ *
+ * This function sets the system clipboard to the specified, UTF-8 encoded
+ * string.
+ *
+ * @param[in] window The window that will own the clipboard contents.
+ * @param[in] string A UTF-8 encoded string.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The specified string is copied before this function
+ * returns.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref clipboard
+ * @sa glfwGetClipboardString
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string);
+
+/*! @brief Returns the contents of the clipboard as a string.
+ *
+ * This function returns the contents of the system clipboard, if it contains
+ * or is convertible to a UTF-8 encoded string. If the clipboard is empty or
+ * if its contents cannot be converted, `NULL` is returned and a @ref
+ * GLFW_FORMAT_UNAVAILABLE error is generated.
+ *
+ * @param[in] window The window that will request the clipboard contents.
+ * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL`
+ * if an [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @pointer_lifetime The returned string is allocated and freed by GLFW. You
+ * should not free it yourself. It is valid until the next call to @ref
+ * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library
+ * is terminated.
+ *
+ * @thread_safety This function must only be called from the main thread.
+ *
+ * @sa @ref clipboard
+ * @sa glfwSetClipboardString
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window);
+
+/*! @brief Returns the value of the GLFW timer.
+ *
+ * This function returns the value of the GLFW timer. Unless the timer has
+ * been set using @ref glfwSetTime, the timer measures time elapsed since GLFW
+ * was initialized.
+ *
+ * The resolution of the timer is system dependent, but is usually on the order
+ * of a few micro- or nanoseconds. It uses the highest-resolution monotonic
+ * time source on each supported platform.
+ *
+ * @return The current value, in seconds, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread. Reading and
+ * writing of the internal timer offset is not atomic, so it needs to be
+ * externally synchronized with calls to @ref glfwSetTime.
+ *
+ * @sa @ref time
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup input
+ */
+GLFWAPI double glfwGetTime(void);
+
+/*! @brief Sets the GLFW timer.
+ *
+ * This function sets the value of the GLFW timer. It then continues to count
+ * up from that value. The value must be a positive finite number less than
+ * or equal to 18446744073.0, which is approximately 584.5 years.
+ *
+ * @param[in] time The new value, in seconds.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_INVALID_VALUE.
+ *
+ * @remark The upper limit of the timer is calculated as
+ * floor((2<sup>64</sup> - 1) / 10<sup>9</sup>) and is due to implementations
+ * storing nanoseconds in 64 bits. The limit may be increased in the future.
+ *
+ * @thread_safety This function may be called from any thread. Reading and
+ * writing of the internal timer offset is not atomic, so it needs to be
+ * externally synchronized with calls to @ref glfwGetTime.
+ *
+ * @sa @ref time
+ *
+ * @since Added in version 2.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI void glfwSetTime(double time);
+
+/*! @brief Returns the current value of the raw timer.
+ *
+ * This function returns the current value of the raw timer, measured in
+ * 1&nbsp;/&nbsp;frequency seconds. To get the frequency, call @ref
+ * glfwGetTimerFrequency.
+ *
+ * @return The value of the timer, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref time
+ * @sa glfwGetTimerFrequency
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI uint64_t glfwGetTimerValue(void);
+
+/*! @brief Returns the frequency, in Hz, of the raw timer.
+ *
+ * This function returns the frequency, in Hz, of the raw timer.
+ *
+ * @return The frequency of the timer, in Hz, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref time
+ * @sa glfwGetTimerValue
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup input
+ */
+GLFWAPI uint64_t glfwGetTimerFrequency(void);
+
+/*! @brief Makes the context of the specified window current for the calling
+ * thread.
+ *
+ * This function makes the OpenGL or OpenGL ES context of the specified window
+ * current on the calling thread. A context can only be made current on
+ * a single thread at a time and each thread can have only a single current
+ * context at a time.
+ *
+ * By default, making a context non-current implicitly forces a pipeline flush.
+ * On machines that support `GL_KHR_context_flush_control`, you can control
+ * whether a context performs this flush by setting the
+ * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref window_hints_ctx) window hint.
+ *
+ * The specified window must have an OpenGL or OpenGL ES context. Specifying
+ * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT
+ * error.
+ *
+ * @param[in] window The window whose context to make current, or `NULL` to
+ * detach the current context.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_current
+ * @sa glfwGetCurrentContext
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window);
+
+/*! @brief Returns the window whose context is current on the calling thread.
+ *
+ * This function returns the window whose OpenGL or OpenGL ES context is
+ * current on the calling thread.
+ *
+ * @return The window whose context is current, or `NULL` if no window's
+ * context is current.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_current
+ * @sa glfwMakeContextCurrent
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI GLFWwindow* glfwGetCurrentContext(void);
+
+/*! @brief Swaps the front and back buffers of the specified window.
+ *
+ * This function swaps the front and back buffers of the specified window when
+ * rendering with OpenGL or OpenGL ES. If the swap interval is greater than
+ * zero, the GPU driver waits the specified number of screen updates before
+ * swapping the buffers.
+ *
+ * The specified window must have an OpenGL or OpenGL ES context. Specifying
+ * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT
+ * error.
+ *
+ * This function does not apply to Vulkan. If you are rendering with Vulkan,
+ * see `vkQueuePresentKHR` instead.
+ *
+ * @param[in] window The window whose buffers to swap.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark __EGL:__ The context of the specified window must be current on the
+ * calling thread.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref buffer_swap
+ * @sa glfwSwapInterval
+ *
+ * @since Added in version 1.0.
+ * @glfw3 Added window handle parameter.
+ *
+ * @ingroup window
+ */
+GLFWAPI void glfwSwapBuffers(GLFWwindow* window);
+
+/*! @brief Sets the swap interval for the current context.
+ *
+ * This function sets the swap interval for the current OpenGL or OpenGL ES
+ * context, i.e. the number of screen updates to wait from the time @ref
+ * glfwSwapBuffers was called before swapping the buffers and returning. This
+ * is sometimes called _vertical synchronization_, _vertical retrace
+ * synchronization_ or just _vsync_.
+ *
+ * Contexts that support either of the `WGL_EXT_swap_control_tear` and
+ * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals,
+ * which allow the driver to swap even if a frame arrives a little bit late.
+ * You can check for the presence of these extensions using @ref
+ * glfwExtensionSupported. For more information about swap tearing, see the
+ * extension specifications.
+ *
+ * A context must be current on the calling thread. Calling this function
+ * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.
+ *
+ * This function does not apply to Vulkan. If you are rendering with Vulkan,
+ * see the present mode of your swapchain instead.
+ *
+ * @param[in] interval The minimum number of screen updates to wait for
+ * until the buffers are swapped by @ref glfwSwapBuffers.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark This function is not called during context creation, leaving the
+ * swap interval set to whatever is the default on that platform. This is done
+ * because some swap interval extensions used by GLFW do not allow the swap
+ * interval to be reset to zero once it has been set to a non-zero value.
+ *
+ * @remark Some GPU drivers do not honor the requested swap interval, either
+ * because of a user setting that overrides the application's request or due to
+ * bugs in the driver.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref buffer_swap
+ * @sa glfwSwapBuffers
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI void glfwSwapInterval(int interval);
+
+/*! @brief Returns whether the specified extension is available.
+ *
+ * This function returns whether the specified
+ * [API extension](@ref context_glext) is supported by the current OpenGL or
+ * OpenGL ES context. It searches both for client API extension and context
+ * creation API extensions.
+ *
+ * A context must be current on the calling thread. Calling this function
+ * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.
+ *
+ * As this functions retrieves and searches one or more extension strings each
+ * call, it is recommended that you cache its results if it is going to be used
+ * frequently. The extension strings will not change during the lifetime of
+ * a context, so there is no danger in doing this.
+ *
+ * This function does not apply to Vulkan. If you are using Vulkan, see @ref
+ * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties`
+ * and `vkEnumerateDeviceExtensionProperties` instead.
+ *
+ * @param[in] extension The ASCII encoded name of the extension.
+ * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE`
+ * otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref
+ * GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_glext
+ * @sa glfwGetProcAddress
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI int glfwExtensionSupported(const char* extension);
+
+/*! @brief Returns the address of the specified function for the current
+ * context.
+ *
+ * This function returns the address of the specified OpenGL or OpenGL ES
+ * [core or extension function](@ref context_glext), if it is supported
+ * by the current context.
+ *
+ * A context must be current on the calling thread. Calling this function
+ * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error.
+ *
+ * This function does not apply to Vulkan. If you are rendering with Vulkan,
+ * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and
+ * `vkGetDeviceProcAddr` instead.
+ *
+ * @param[in] procname The ASCII encoded name of the function.
+ * @return The address of the function, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remark The address of a given function is not guaranteed to be the same
+ * between contexts.
+ *
+ * @remark This function may return a non-`NULL` address despite the
+ * associated version or extension not being available. Always check the
+ * context version or extension string first.
+ *
+ * @pointer_lifetime The returned function pointer is valid until the context
+ * is destroyed or the library is terminated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref context_glext
+ * @sa glfwExtensionSupported
+ *
+ * @since Added in version 1.0.
+ *
+ * @ingroup context
+ */
+GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname);
+
+/*! @brief Returns whether the Vulkan loader has been found.
+ *
+ * This function returns whether the Vulkan loader has been found. This check
+ * is performed by @ref glfwInit.
+ *
+ * The availability of a Vulkan loader does not by itself guarantee that window
+ * surface creation or even device creation is possible. Call @ref
+ * glfwGetRequiredInstanceExtensions to check whether the extensions necessary
+ * for Vulkan surface creation are available and @ref
+ * glfwGetPhysicalDevicePresentationSupport to check whether a queue family of
+ * a physical device supports image presentation.
+ *
+ * @return `GLFW_TRUE` if Vulkan is available, or `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref vulkan_support
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI int glfwVulkanSupported(void);
+
+/*! @brief Returns the Vulkan instance extensions required by GLFW.
+ *
+ * This function returns an array of names of Vulkan instance extensions required
+ * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the
+ * list will always contains `VK_KHR_surface`, so if you don't require any
+ * additional extensions you can pass this list directly to the
+ * `VkInstanceCreateInfo` struct.
+ *
+ * If Vulkan is not available on the machine, this function returns `NULL` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported
+ * to check whether Vulkan is available.
+ *
+ * If Vulkan is available but no set of extensions allowing window surface
+ * creation was found, this function returns `NULL`. You may still use Vulkan
+ * for off-screen rendering and compute work.
+ *
+ * @param[out] count Where to store the number of extensions in the returned
+ * array. This is set to zero if an error occurred.
+ * @return An array of ASCII encoded extension names, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_API_UNAVAILABLE.
+ *
+ * @remarks Additional extensions may be required by future versions of GLFW.
+ * You should check if any extensions you wish to enable are already in the
+ * returned array, as it is an error to specify an extension more than once in
+ * the `VkInstanceCreateInfo` struct.
+ *
+ * @pointer_lifetime The returned array is allocated and freed by GLFW. You
+ * should not free it yourself. It is guaranteed to be valid only until the
+ * library is terminated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref vulkan_ext
+ * @sa glfwCreateWindowSurface
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count);
+
+#if defined(VK_VERSION_1_0)
+
+/*! @brief Returns the address of the specified Vulkan instance function.
+ *
+ * This function returns the address of the specified Vulkan core or extension
+ * function for the specified instance. If instance is set to `NULL` it can
+ * return any function exported from the Vulkan loader, including at least the
+ * following functions:
+ *
+ * - `vkEnumerateInstanceExtensionProperties`
+ * - `vkEnumerateInstanceLayerProperties`
+ * - `vkCreateInstance`
+ * - `vkGetInstanceProcAddr`
+ *
+ * If Vulkan is not available on the machine, this function returns `NULL` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported
+ * to check whether Vulkan is available.
+ *
+ * This function is equivalent to calling `vkGetInstanceProcAddr` with
+ * a platform-specific query of the Vulkan loader as a fallback.
+ *
+ * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve
+ * functions related to instance creation.
+ * @param[in] procname The ASCII encoded name of the function.
+ * @return The address of the function, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
+ * GLFW_API_UNAVAILABLE.
+ *
+ * @pointer_lifetime The returned function pointer is valid until the library
+ * is terminated.
+ *
+ * @thread_safety This function may be called from any thread.
+ *
+ * @sa @ref vulkan_proc
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname);
+
+/*! @brief Returns whether the specified queue family can present images.
+ *
+ * This function returns whether the specified queue family of the specified
+ * physical device supports presentation to the platform GLFW was built for.
+ *
+ * If Vulkan or the required window surface creation instance extensions are
+ * not available on the machine, or if the specified instance was not created
+ * with the required extensions, this function returns `GLFW_FALSE` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported
+ * to check whether Vulkan is available and @ref
+ * glfwGetRequiredInstanceExtensions to check what instance extensions are
+ * required.
+ *
+ * @param[in] instance The instance that the physical device belongs to.
+ * @param[in] device The physical device that the queue family belongs to.
+ * @param[in] queuefamily The index of the queue family to query.
+ * @return `GLFW_TRUE` if the queue family supports presentation, or
+ * `GLFW_FALSE` otherwise.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @thread_safety This function may be called from any thread. For
+ * synchronization details of Vulkan objects, see the Vulkan specification.
+ *
+ * @sa @ref vulkan_present
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily);
+
+/*! @brief Creates a Vulkan surface for the specified window.
+ *
+ * This function creates a Vulkan surface for the specified window.
+ *
+ * If the Vulkan loader was not found at initialization, this function returns
+ * `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref GLFW_API_UNAVAILABLE
+ * error. Call @ref glfwVulkanSupported to check whether the Vulkan loader was
+ * found.
+ *
+ * If the required window surface creation instance extensions are not
+ * available or if the specified instance was not created with these extensions
+ * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and
+ * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref
+ * glfwGetRequiredInstanceExtensions to check what instance extensions are
+ * required.
+ *
+ * The window surface must be destroyed before the specified Vulkan instance.
+ * It is the responsibility of the caller to destroy the window surface. GLFW
+ * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the
+ * surface.
+ *
+ * @param[in] instance The Vulkan instance to create the surface in.
+ * @param[in] window The window to create the surface for.
+ * @param[in] allocator The allocator to use, or `NULL` to use the default
+ * allocator.
+ * @param[out] surface Where to store the handle of the surface. This is set
+ * to `VK_NULL_HANDLE` if an error occurred.
+ * @return `VK_SUCCESS` if successful, or a Vulkan error code if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
+ * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR.
+ *
+ * @remarks If an error occurs before the creation call is made, GLFW returns
+ * the Vulkan error code most appropriate for the error. Appropriate use of
+ * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should
+ * eliminate almost all occurrences of these errors.
+ *
+ * @thread_safety This function may be called from any thread. For
+ * synchronization details of Vulkan objects, see the Vulkan specification.
+ *
+ * @sa @ref vulkan_surface
+ * @sa glfwGetRequiredInstanceExtensions
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup vulkan
+ */
+GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
+
+#endif /*VK_VERSION_1_0*/
+
+
+/*************************************************************************
+ * Global definition cleanup
+ *************************************************************************/
+
+/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */
+
+#ifdef GLFW_WINGDIAPI_DEFINED
+ #undef WINGDIAPI
+ #undef GLFW_WINGDIAPI_DEFINED
+#endif
+
+#ifdef GLFW_CALLBACK_DEFINED
+ #undef CALLBACK
+ #undef GLFW_CALLBACK_DEFINED
+#endif
+
+/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _glfw3_h_ */
+
diff --git a/src/external/glfw3/include/GLFW/glfw3native.h b/src/external/glfw3/include/GLFW/glfw3native.h
new file mode 100644
index 00000000..30e1a570
--- /dev/null
+++ b/src/external/glfw3/include/GLFW/glfw3native.h
@@ -0,0 +1,456 @@
+/*************************************************************************
+ * GLFW 3.2 - www.glfw.org
+ * A library for OpenGL, window and input
+ *------------------------------------------------------------------------
+ * Copyright (c) 2002-2006 Marcus Geelnard
+ * Copyright (c) 2006-2016 Camilla Berglund <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.
+ *
+ *************************************************************************/
+
+#ifndef _glfw3_native_h_
+#define _glfw3_native_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*************************************************************************
+ * Doxygen documentation
+ *************************************************************************/
+
+/*! @file glfw3native.h
+ * @brief The header of the native access functions.
+ *
+ * This is the header file of the native access functions. See @ref native for
+ * more information.
+ */
+/*! @defgroup native Native access
+ *
+ * **By using the native access functions you assert that you know what you're
+ * doing and how to fix problems caused by using them. If you don't, you
+ * shouldn't be using them.**
+ *
+ * Before the inclusion of @ref glfw3native.h, you may define exactly one
+ * window system API macro and zero or more context creation API macros.
+ *
+ * The chosen backends must match those the library was compiled for. Failure
+ * to do this will cause a link-time error.
+ *
+ * The available window API macros are:
+ * * `GLFW_EXPOSE_NATIVE_WIN32`
+ * * `GLFW_EXPOSE_NATIVE_COCOA`
+ * * `GLFW_EXPOSE_NATIVE_X11`
+ * * `GLFW_EXPOSE_NATIVE_WAYLAND`
+ * * `GLFW_EXPOSE_NATIVE_MIR`
+ *
+ * The available context API macros are:
+ * * `GLFW_EXPOSE_NATIVE_WGL`
+ * * `GLFW_EXPOSE_NATIVE_NSGL`
+ * * `GLFW_EXPOSE_NATIVE_GLX`
+ * * `GLFW_EXPOSE_NATIVE_EGL`
+ *
+ * These macros select which of the native access functions that are declared
+ * and which platform-specific headers to include. It is then up your (by
+ * definition platform-specific) code to handle which of these should be
+ * defined.
+ */
+
+
+/*************************************************************************
+ * System headers and types
+ *************************************************************************/
+
+#if defined(GLFW_EXPOSE_NATIVE_WIN32)
+ // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
+ // example to allow applications to correctly declare a GL_ARB_debug_output
+ // callback) but windows.h assumes no one will define APIENTRY before it does
+ #undef APIENTRY
+ #include <windows.h>
+#elif defined(GLFW_EXPOSE_NATIVE_COCOA)
+ #include <ApplicationServices/ApplicationServices.h>
+ #if defined(__OBJC__)
+ #import <Cocoa/Cocoa.h>
+ #else
+ typedef void* id;
+ #endif
+#elif defined(GLFW_EXPOSE_NATIVE_X11)
+ #include <X11/Xlib.h>
+ #include <X11/extensions/Xrandr.h>
+#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
+ #include <wayland-client.h>
+#elif defined(GLFW_EXPOSE_NATIVE_MIR)
+ #include <mir_toolkit/mir_client_library.h>
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_WGL)
+ /* WGL is declared by windows.h */
+#endif
+#if defined(GLFW_EXPOSE_NATIVE_NSGL)
+ /* NSGL is declared by Cocoa.h */
+#endif
+#if defined(GLFW_EXPOSE_NATIVE_GLX)
+ #include <GL/glx.h>
+#endif
+#if defined(GLFW_EXPOSE_NATIVE_EGL)
+ #include <EGL/egl.h>
+#endif
+
+
+/*************************************************************************
+ * Functions
+ *************************************************************************/
+
+#if defined(GLFW_EXPOSE_NATIVE_WIN32)
+/*! @brief Returns the adapter device name of the specified monitor.
+ *
+ * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
+ * of the specified monitor, or `NULL` if an [error](@ref error_handling)
+ * occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
+
+/*! @brief Returns the display device name of the specified monitor.
+ *
+ * @return The UTF-8 encoded display device name (for example
+ * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the `HWND` of the specified window.
+ *
+ * @return The `HWND` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_WGL)
+/*! @brief Returns the `HGLRC` of the specified window.
+ *
+ * @return The `HGLRC` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_COCOA)
+/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
+ *
+ * @return The `CGDirectDisplayID` of the specified monitor, or
+ * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the `NSWindow` of the specified window.
+ *
+ * @return The `NSWindow` of the specified window, or `nil` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_NSGL)
+/*! @brief Returns the `NSOpenGLContext` of the specified window.
+ *
+ * @return The `NSOpenGLContext` of the specified window, or `nil` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_X11)
+/*! @brief Returns the `Display` used by GLFW.
+ *
+ * @return The `Display` used by GLFW, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI Display* glfwGetX11Display(void);
+
+/*! @brief Returns the `RRCrtc` of the specified monitor.
+ *
+ * @return The `RRCrtc` of the specified monitor, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
+
+/*! @brief Returns the `RROutput` of the specified monitor.
+ *
+ * @return The `RROutput` of the specified monitor, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.1.
+ *
+ * @ingroup native
+ */
+GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the `Window` of the specified window.
+ *
+ * @return The `Window` of the specified window, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_GLX)
+/*! @brief Returns the `GLXContext` of the specified window.
+ *
+ * @return The `GLXContext` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
+
+/*! @brief Returns the `GLXWindow` of the specified window.
+ *
+ * @return The `GLXWindow` of the specified window, or `None` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
+/*! @brief Returns the `struct wl_display*` used by GLFW.
+ *
+ * @return The `struct wl_display*` used by GLFW, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
+
+/*! @brief Returns the `struct wl_output*` of the specified monitor.
+ *
+ * @return The `struct wl_output*` of the specified monitor, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the main `struct wl_surface*` of the specified window.
+ *
+ * @return The main `struct wl_surface*` of the specified window, or `NULL` if
+ * an [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_MIR)
+/*! @brief Returns the `MirConnection*` used by GLFW.
+ *
+ * @return The `MirConnection*` used by GLFW, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI MirConnection* glfwGetMirDisplay(void);
+
+/*! @brief Returns the Mir output ID of the specified monitor.
+ *
+ * @return The Mir output ID of the specified monitor, or zero if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor);
+
+/*! @brief Returns the `MirSurface*` of the specified window.
+ *
+ * @return The `MirSurface*` of the specified window, or `NULL` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.2.
+ *
+ * @ingroup native
+ */
+GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window);
+#endif
+
+#if defined(GLFW_EXPOSE_NATIVE_EGL)
+/*! @brief Returns the `EGLDisplay` used by GLFW.
+ *
+ * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
+
+/*! @brief Returns the `EGLContext` of the specified window.
+ *
+ * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
+
+/*! @brief Returns the `EGLSurface` of the specified window.
+ *
+ * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
+ * [error](@ref error_handling) occurred.
+ *
+ * @thread_safety This function may be called from any thread. Access is not
+ * synchronized.
+ *
+ * @since Added in version 3.0.
+ *
+ * @ingroup native
+ */
+GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _glfw3_native_h_ */
+
diff --git a/src/external/glfw3/lib/linux/libglfw3.a b/src/external/glfw3/lib/linux/libglfw3.a
new file mode 100644
index 00000000..84cc1d20
--- /dev/null
+++ b/src/external/glfw3/lib/linux/libglfw3.a
Binary files differ
diff --git a/src/external/glfw3/lib/osx/libglfw.3.0.dylib b/src/external/glfw3/lib/osx/libglfw.3.0.dylib
new file mode 100644
index 00000000..15674573
--- /dev/null
+++ b/src/external/glfw3/lib/osx/libglfw.3.0.dylib
Binary files differ
diff --git a/src/external/glfw3/lib/osx/libglfw.3.dylib b/src/external/glfw3/lib/osx/libglfw.3.dylib
new file mode 100644
index 00000000..cd20112e
--- /dev/null
+++ b/src/external/glfw3/lib/osx/libglfw.3.dylib
@@ -0,0 +1 @@
+libglfw.3.0.dylib \ No newline at end of file
diff --git a/src/external/glfw3/lib/osx/libglfw.dylib b/src/external/glfw3/lib/osx/libglfw.dylib
new file mode 100644
index 00000000..d4bd51e1
--- /dev/null
+++ b/src/external/glfw3/lib/osx/libglfw.dylib
@@ -0,0 +1 @@
+libglfw.3.dylib \ No newline at end of file
diff --git a/src/external/glfw3/lib/win32/glfw3.dll b/src/external/glfw3/lib/win32/glfw3.dll
new file mode 100644
index 00000000..9f5d40f6
--- /dev/null
+++ b/src/external/glfw3/lib/win32/glfw3.dll
Binary files differ
diff --git a/src/external/glfw3/lib/win32/libglfw3.a b/src/external/glfw3/lib/win32/libglfw3.a
new file mode 100644
index 00000000..d50ffa72
--- /dev/null
+++ b/src/external/glfw3/lib/win32/libglfw3.a
Binary files differ
diff --git a/src/external/glfw3/lib/win32/libglfw3dll.a b/src/external/glfw3/lib/win32/libglfw3dll.a
new file mode 100644
index 00000000..a42a400b
--- /dev/null
+++ b/src/external/glfw3/lib/win32/libglfw3dll.a
Binary files differ
diff --git a/src/external/jar_mod.h b/src/external/jar_mod.h
new file mode 100644
index 00000000..2ddc61d1
--- /dev/null
+++ b/src/external/jar_mod.h
@@ -0,0 +1,1587 @@
+// jar_mod.h - v0.01 - public domain C0 - Joshua Reisenauer
+//
+// HISTORY:
+//
+// v0.01 2016-03-12 Setup
+//
+//
+// USAGE:
+//
+// In ONE source file, put:
+//
+// #define JAR_MOD_IMPLEMENTATION
+// #include "jar_mod.h"
+//
+// Other source files should just include jar_mod.h
+//
+// SAMPLE CODE:
+// jar_mod_context_t modctx;
+// short samplebuff[4096];
+// bool bufferFull = false;
+// int intro_load(void)
+// {
+// jar_mod_init(&modctx);
+// jar_mod_load_file(&modctx, "file.mod");
+// return 1;
+// }
+// int intro_unload(void)
+// {
+// jar_mod_unload(&modctx);
+// return 1;
+// }
+// int intro_tick(long counter)
+// {
+// if(!bufferFull)
+// {
+// jar_mod_fillbuffer(&modctx, samplebuff, 4096, 0);
+// bufferFull=true;
+// }
+// if(IsKeyDown(KEY_ENTER))
+// return 1;
+// return 0;
+// }
+//
+//
+// LISCENSE:
+//
+// Written by: Jean-François DEL NERO (http://hxc2001.com/) <Email : jeanfrancoisdelnero <> free.fr>
+// Adapted to jar_mod by: Joshua Adam Reisenauer <kd7tck@gmail.com>
+// This program is free software. It comes without any warranty, to the
+// extent permitted by applicable law. You can redistribute it and/or
+// modify it under the terms of the Do What The Fuck You Want To Public
+// License, Version 2, as published by Sam Hocevar. See
+// http://sam.zoy.org/wtfpl/COPYING for more details.
+///////////////////////////////////////////////////////////////////////////////////
+// HxCMOD Core API:
+// -------------------------------------------
+// int jar_mod_init(jar_mod_context_t * modctx)
+//
+// - Initialize the jar_mod_context_t buffer. Must be called before doing anything else.
+// Return 1 if success. 0 in case of error.
+// -------------------------------------------
+// mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename)
+//
+// - "Load" a MOD from file, context must already be initialized.
+// Return size of file in bytes.
+// -------------------------------------------
+// void jar_mod_fillbuffer( jar_mod_context_t * modctx, unsigned short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf )
+//
+// - Generate and return the next samples chunk to outbuffer.
+// nbsample specify the number of stereo 16bits samples you want.
+// The output format is by default signed 48000Hz 16-bit Stereo PCM samples, otherwise it is changed with jar_mod_setcfg().
+// The output buffer size in bytes must be equal to ( nbsample * 2 * channels ).
+// The optional trkbuf parameter can be used to get detailed status of the player. Put NULL/0 is unused.
+// -------------------------------------------
+// void jar_mod_unload( jar_mod_context_t * modctx )
+// - "Unload" / clear the player status.
+// -------------------------------------------
+///////////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef INCLUDE_JAR_MOD_H
+#define INCLUDE_JAR_MOD_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+// Basic type
+typedef unsigned char muchar;
+typedef unsigned short muint;
+typedef short mint;
+typedef unsigned long mulong;
+
+#define NUMMAXCHANNELS 32
+#define MAXNOTES 12*12
+#define DEFAULT_SAMPLE_RATE 48000
+//
+// MOD file structures
+//
+
+#pragma pack(1)
+
+typedef struct {
+ muchar name[22];
+ muint length;
+ muchar finetune;
+ muchar volume;
+ muint reppnt;
+ muint replen;
+} sample;
+
+typedef struct {
+ muchar sampperiod;
+ muchar period;
+ muchar sampeffect;
+ muchar effect;
+} note;
+
+typedef struct {
+ muchar title[20];
+ sample samples[31];
+ muchar length; // length of tablepos
+ muchar protracker;
+ muchar patterntable[128];
+ muchar signature[4];
+ muchar speed;
+} module;
+
+#pragma pack()
+
+//
+// HxCMod Internal structures
+//
+typedef struct {
+ char* sampdata;
+ muint sampnum;
+ muint length;
+ muint reppnt;
+ muint replen;
+ mulong samppos;
+ muint period;
+ muchar volume;
+ mulong ticks;
+ muchar effect;
+ muchar parameffect;
+ muint effect_code;
+ mint decalperiod;
+ mint portaspeed;
+ mint portaperiod;
+ mint vibraperiod;
+ mint Arpperiods[3];
+ muchar ArpIndex;
+ mint oldk;
+ muchar volumeslide;
+ muchar vibraparam;
+ muchar vibrapointeur;
+ muchar finetune;
+ muchar cut_param;
+ muint patternloopcnt;
+ muint patternloopstartpoint;
+} channel;
+
+typedef struct {
+ module song;
+ char* sampledata[31];
+ note* patterndata[128];
+
+ mulong playrate;
+ muint tablepos;
+ muint patternpos;
+ muint patterndelay;
+ muint jump_loop_effect;
+ muchar bpm;
+ mulong patternticks;
+ mulong patterntickse;
+ mulong patternticksaim;
+ mulong sampleticksconst;
+ mulong samplenb;
+ channel channels[NUMMAXCHANNELS];
+ muint number_of_channels;
+ muint fullperiod[MAXNOTES * 8];
+ muint mod_loaded;
+ mint last_r_sample;
+ mint last_l_sample;
+ mint stereo;
+ mint stereo_separation;
+ mint bits;
+ mint filter;
+
+ muchar *modfile; // the raw mod file
+ mulong modfilesize;
+ muint loopcount;
+} jar_mod_context_t;
+
+//
+// Player states structures
+//
+typedef struct track_state_
+{
+ unsigned char instrument_number;
+ unsigned short cur_period;
+ unsigned char cur_volume;
+ unsigned short cur_effect;
+ unsigned short cur_parameffect;
+}track_state;
+
+typedef struct tracker_state_
+{
+ int number_of_tracks;
+ int bpm;
+ int speed;
+ int cur_pattern;
+ int cur_pattern_pos;
+ int cur_pattern_table_pos;
+ unsigned int buf_index;
+ track_state tracks[32];
+}tracker_state;
+
+typedef struct tracker_state_instrument_
+{
+ char name[22];
+ int active;
+}tracker_state_instrument;
+
+typedef struct jar_mod_tracker_buffer_state_
+{
+ int nb_max_of_state;
+ int nb_of_state;
+ int cur_rd_index;
+ int sample_step;
+ char name[64];
+ tracker_state_instrument instruments[31];
+ tracker_state * track_state_buf;
+}jar_mod_tracker_buffer_state;
+
+
+
+bool jar_mod_init(jar_mod_context_t * modctx);
+bool jar_mod_setcfg(jar_mod_context_t * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter);
+void jar_mod_fillbuffer(jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf);
+void jar_mod_unload(jar_mod_context_t * modctx);
+mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename);
+mulong jar_mod_current_samples(jar_mod_context_t * modctx);
+mulong jar_mod_max_samples(jar_mod_context_t * modctx);
+void jar_mod_seek_start(jar_mod_context_t * ctx);
+
+#ifdef __cplusplus
+}
+#endif
+//--------------------------------------------------------------------
+
+
+
+//-------------------------------------------------------------------------------
+#ifdef JAR_MOD_IMPLEMENTATION
+
+// Effects list
+#define EFFECT_ARPEGGIO 0x0 // Supported
+#define EFFECT_PORTAMENTO_UP 0x1 // Supported
+#define EFFECT_PORTAMENTO_DOWN 0x2 // Supported
+#define EFFECT_TONE_PORTAMENTO 0x3 // Supported
+#define EFFECT_VIBRATO 0x4 // Supported
+#define EFFECT_VOLSLIDE_TONEPORTA 0x5 // Supported
+#define EFFECT_VOLSLIDE_VIBRATO 0x6 // Supported
+#define EFFECT_VOLSLIDE_TREMOLO 0x7 // - TO BE DONE -
+#define EFFECT_SET_PANNING 0x8 // - TO BE DONE -
+#define EFFECT_SET_OFFSET 0x9 // Supported
+#define EFFECT_VOLUME_SLIDE 0xA // Supported
+#define EFFECT_JUMP_POSITION 0xB // Supported
+#define EFFECT_SET_VOLUME 0xC // Supported
+#define EFFECT_PATTERN_BREAK 0xD // Supported
+
+#define EFFECT_EXTENDED 0xE
+#define EFFECT_E_FINE_PORTA_UP 0x1 // Supported
+#define EFFECT_E_FINE_PORTA_DOWN 0x2 // Supported
+#define EFFECT_E_GLISSANDO_CTRL 0x3 // - TO BE DONE -
+#define EFFECT_E_VIBRATO_WAVEFORM 0x4 // - TO BE DONE -
+#define EFFECT_E_SET_FINETUNE 0x5 // - TO BE DONE -
+#define EFFECT_E_PATTERN_LOOP 0x6 // Supported
+#define EFFECT_E_TREMOLO_WAVEFORM 0x7 // - TO BE DONE -
+#define EFFECT_E_SET_PANNING_2 0x8 // - TO BE DONE -
+#define EFFECT_E_RETRIGGER_NOTE 0x9 // - TO BE DONE -
+#define EFFECT_E_FINE_VOLSLIDE_UP 0xA // Supported
+#define EFFECT_E_FINE_VOLSLIDE_DOWN 0xB // Supported
+#define EFFECT_E_NOTE_CUT 0xC // Supported
+#define EFFECT_E_NOTE_DELAY 0xD // - TO BE DONE -
+#define EFFECT_E_PATTERN_DELAY 0xE // Supported
+#define EFFECT_E_INVERT_LOOP 0xF // - TO BE DONE -
+#define EFFECT_SET_SPEED 0xF0 // Supported
+#define EFFECT_SET_TEMPO 0xF2 // Supported
+
+#define PERIOD_TABLE_LENGTH MAXNOTES
+#define FULL_PERIOD_TABLE_LENGTH ( PERIOD_TABLE_LENGTH * 8 )
+
+static const short periodtable[]=
+{
+ 27392, 25856, 24384, 23040, 21696, 20480, 19328, 18240, 17216, 16256, 15360, 14496,
+ 13696, 12928, 12192, 11520, 10848, 10240, 9664, 9120, 8606, 8128, 7680, 7248,
+ 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624,
+ 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812,
+ 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 906,
+ 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
+ 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
+ 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
+ 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56,
+ 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28,
+ 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14,
+ 13, 13, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7
+};
+
+static const short sintable[]={
+ 0, 24, 49, 74, 97, 120, 141,161,
+ 180, 197, 212, 224, 235, 244, 250,253,
+ 255, 253, 250, 244, 235, 224, 212,197,
+ 180, 161, 141, 120, 97, 74, 49, 24
+};
+
+typedef struct modtype_
+{
+ unsigned char signature[5];
+ int numberofchannels;
+}modtype;
+
+modtype modlist[]=
+{
+ { "M!K!",4},
+ { "M.K.",4},
+ { "FLT4",4},
+ { "FLT8",8},
+ { "4CHN",4},
+ { "6CHN",6},
+ { "8CHN",8},
+ { "10CH",10},
+ { "12CH",12},
+ { "14CH",14},
+ { "16CH",16},
+ { "18CH",18},
+ { "20CH",20},
+ { "22CH",22},
+ { "24CH",24},
+ { "26CH",26},
+ { "28CH",28},
+ { "30CH",30},
+ { "32CH",32},
+ { "",0}
+};
+
+///////////////////////////////////////////////////////////////////////////////////
+
+static void memcopy( void * dest, void *source, unsigned long size )
+{
+ unsigned long i;
+ unsigned char * d,*s;
+
+ d=(unsigned char*)dest;
+ s=(unsigned char*)source;
+ for(i=0;i<size;i++)
+ {
+ d[i]=s[i];
+ }
+}
+
+static void memclear( void * dest, unsigned char value, unsigned long size )
+{
+ unsigned long i;
+ unsigned char * d;
+
+ d=(unsigned char*)dest;
+ for(i=0;i<size;i++)
+ {
+ d[i]=value;
+ }
+}
+
+static int memcompare( unsigned char * buf1, unsigned char * buf2, unsigned int size )
+{
+ unsigned int i;
+
+ i = 0;
+
+ while(i<size)
+ {
+ if(buf1[i] != buf2[i])
+ {
+ return 0;
+ }
+ i++;
+ }
+
+ return 1;
+}
+
+static int getnote( jar_mod_context_t * mod, unsigned short period, int finetune )
+{
+ int i;
+
+ for(i = 0; i < FULL_PERIOD_TABLE_LENGTH; i++)
+ {
+ if(period >= mod->fullperiod[i])
+ {
+ return i;
+ }
+ }
+
+ return MAXNOTES;
+}
+
+static void worknote( note * nptr, channel * cptr, char t, jar_mod_context_t * mod )
+{
+ muint sample, period, effect, operiod;
+ muint curnote, arpnote;
+
+ sample = (nptr->sampperiod & 0xF0) | (nptr->sampeffect >> 4);
+ period = ((nptr->sampperiod & 0xF) << 8) | nptr->period;
+ effect = ((nptr->sampeffect & 0xF) << 8) | nptr->effect;
+
+ operiod = cptr->period;
+
+ if ( period || sample )
+ {
+ if( sample && sample < 32 )
+ {
+ cptr->sampnum = sample - 1;
+ }
+
+ if( period || sample )
+ {
+ cptr->sampdata = (char *) mod->sampledata[cptr->sampnum];
+ cptr->length = mod->song.samples[cptr->sampnum].length;
+ cptr->reppnt = mod->song.samples[cptr->sampnum].reppnt;
+ cptr->replen = mod->song.samples[cptr->sampnum].replen;
+
+ cptr->finetune = (mod->song.samples[cptr->sampnum].finetune)&0xF;
+
+ if(effect>>8!=4 && effect>>8!=6)
+ {
+ cptr->vibraperiod=0;
+ cptr->vibrapointeur=0;
+ }
+ }
+
+ if( (sample != 0) && ( (effect>>8) != EFFECT_VOLSLIDE_TONEPORTA ) )
+ {
+ cptr->volume = mod->song.samples[cptr->sampnum].volume;
+ cptr->volumeslide = 0;
+ }
+
+ if( ( (effect>>8) != EFFECT_TONE_PORTAMENTO && (effect>>8)!=EFFECT_VOLSLIDE_TONEPORTA) )
+ {
+ if (period!=0)
+ cptr->samppos = 0;
+ }
+
+ cptr->decalperiod = 0;
+ if( period )
+ {
+ if(cptr->finetune)
+ {
+ if( cptr->finetune <= 7 )
+ {
+ period = mod->fullperiod[getnote(mod,period,0) + cptr->finetune];
+ }
+ else
+ {
+ period = mod->fullperiod[getnote(mod,period,0) - (16 - (cptr->finetune)) ];
+ }
+ }
+
+ cptr->period = period;
+ }
+
+ }
+
+ cptr->effect = 0;
+ cptr->parameffect = 0;
+ cptr->effect_code = effect;
+
+ switch (effect >> 8)
+ {
+ case EFFECT_ARPEGGIO:
+ /*
+ [0]: Arpeggio
+ Where [0][x][y] means "play note, note+x semitones, note+y
+ semitones, then return to original note". The fluctuations are
+ carried out evenly spaced in one pattern division. They are usually
+ used to simulate chords, but this doesn't work too well. They are
+ also used to produce heavy vibrato. A major chord is when x=4, y=7.
+ A minor chord is when x=3, y=7.
+ */
+
+ if(effect&0xff)
+ {
+ cptr->effect = EFFECT_ARPEGGIO;
+ cptr->parameffect = effect&0xff;
+
+ cptr->ArpIndex = 0;
+
+ curnote = getnote(mod,cptr->period,cptr->finetune);
+
+ cptr->Arpperiods[0] = cptr->period;
+
+ arpnote = curnote + (((cptr->parameffect>>4)&0xF)*8);
+ if( arpnote >= FULL_PERIOD_TABLE_LENGTH )
+ arpnote = FULL_PERIOD_TABLE_LENGTH - 1;
+
+ cptr->Arpperiods[1] = mod->fullperiod[arpnote];
+
+ arpnote = curnote + (((cptr->parameffect)&0xF)*8);
+ if( arpnote >= FULL_PERIOD_TABLE_LENGTH )
+ arpnote = FULL_PERIOD_TABLE_LENGTH - 1;
+
+ cptr->Arpperiods[2] = mod->fullperiod[arpnote];
+ }
+ break;
+
+ case EFFECT_PORTAMENTO_UP:
+ /*
+ [1]: Slide up
+ Where [1][x][y] means "smoothly decrease the period of current
+ sample by x*16+y after each tick in the division". The
+ ticks/division are set with the 'set speed' effect (see below). If
+ the period of the note being played is z, then the final period
+ will be z - (x*16 + y)*(ticks - 1). As the slide rate depends on
+ the speed, changing the speed will change the slide. You cannot
+ slide beyond the note B3 (period 113).
+ */
+
+ cptr->effect = EFFECT_PORTAMENTO_UP;
+ cptr->parameffect = effect&0xff;
+ break;
+
+ case EFFECT_PORTAMENTO_DOWN:
+ /*
+ [2]: Slide down
+ Where [2][x][y] means "smoothly increase the period of current
+ sample by x*16+y after each tick in the division". Similar to [1],
+ but lowers the pitch. You cannot slide beyond the note C1 (period
+ 856).
+ */
+
+ cptr->effect = EFFECT_PORTAMENTO_DOWN;
+ cptr->parameffect = effect&0xff;
+ break;
+
+ case EFFECT_TONE_PORTAMENTO:
+ /*
+ [3]: Slide to note
+ Where [3][x][y] means "smoothly change the period of current sample
+ by x*16+y after each tick in the division, never sliding beyond
+ current period". The period-length in this channel's division is a
+ parameter to this effect, and hence is not played. Sliding to a
+ note is similar to effects [1] and [2], but the slide will not go
+ beyond the given period, and the direction is implied by that
+ period. If x and y are both 0, then the old slide will continue.
+ */
+
+ cptr->effect = EFFECT_TONE_PORTAMENTO;
+ if( (effect&0xff) != 0 )
+ {
+ cptr->portaspeed = (short)(effect&0xff);
+ }
+
+ if(period!=0)
+ {
+ cptr->portaperiod = period;
+ cptr->period = operiod;
+ }
+ break;
+
+ case EFFECT_VIBRATO:
+ /*
+ [4]: Vibrato
+ Where [4][x][y] means "oscillate the sample pitch using a
+ particular waveform with amplitude y/16 semitones, such that (x *
+ ticks)/64 cycles occur in the division". The waveform is set using
+ effect [14][4]. By placing vibrato effects on consecutive
+ divisions, the vibrato effect can be maintained. If either x or y
+ are 0, then the old vibrato values will be used.
+ */
+
+ cptr->effect = EFFECT_VIBRATO;
+ if( ( effect & 0x0F ) != 0 ) // Depth continue or change ?
+ cptr->vibraparam = (cptr->vibraparam & 0xF0) | ( effect & 0x0F );
+ if( ( effect & 0xF0 ) != 0 ) // Speed continue or change ?
+ cptr->vibraparam = (cptr->vibraparam & 0x0F) | ( effect & 0xF0 );
+
+ break;
+
+ case EFFECT_VOLSLIDE_TONEPORTA:
+ /*
+ [5]: Continue 'Slide to note', but also do Volume slide
+ Where [5][x][y] means "either slide the volume up x*(ticks - 1) or
+ slide the volume down y*(ticks - 1), at the same time as continuing
+ the last 'Slide to note'". It is illegal for both x and y to be
+ non-zero. You cannot slide outside the volume range 0..64. The
+ period-length in this channel's division is a parameter to this
+ effect, and hence is not played.
+ */
+
+ if( period != 0 )
+ {
+ cptr->portaperiod = period;
+ cptr->period = operiod;
+ }
+
+ cptr->effect = EFFECT_VOLSLIDE_TONEPORTA;
+ if( ( effect & 0xFF ) != 0 )
+ cptr->volumeslide = ( effect & 0xFF );
+
+ break;
+
+ case EFFECT_VOLSLIDE_VIBRATO:
+ /*
+ [6]: Continue 'Vibrato', but also do Volume slide
+ Where [6][x][y] means "either slide the volume up x*(ticks - 1) or
+ slide the volume down y*(ticks - 1), at the same time as continuing
+ the last 'Vibrato'". It is illegal for both x and y to be non-zero.
+ You cannot slide outside the volume range 0..64.
+ */
+
+ cptr->effect = EFFECT_VOLSLIDE_VIBRATO;
+ if( (effect & 0xFF) != 0 )
+ cptr->volumeslide = (effect & 0xFF);
+ break;
+
+ case EFFECT_SET_OFFSET:
+ /*
+ [9]: Set sample offset
+ Where [9][x][y] means "play the sample from offset x*4096 + y*256".
+ The offset is measured in words. If no sample is given, yet one is
+ still playing on this channel, it should be retriggered to the new
+ offset using the current volume.
+ */
+
+ cptr->samppos = ((effect>>4) * 4096) + ((effect&0xF)*256);
+
+ break;
+
+ case EFFECT_VOLUME_SLIDE:
+ /*
+ [10]: Volume slide
+ Where [10][x][y] means "either slide the volume up x*(ticks - 1) or
+ slide the volume down y*(ticks - 1)". If both x and y are non-zero,
+ then the y value is ignored (assumed to be 0). You cannot slide
+ outside the volume range 0..64.
+ */
+
+ cptr->effect = EFFECT_VOLUME_SLIDE;
+ cptr->volumeslide = (effect & 0xFF);
+ break;
+
+ case EFFECT_JUMP_POSITION:
+ /*
+ [11]: Position Jump
+ Where [11][x][y] means "stop the pattern after this division, and
+ continue the song at song-position x*16+y". This shifts the
+ 'pattern-cursor' in the pattern table (see above). Legal values for
+ x*16+y are from 0 to 127.
+ */
+
+ mod->tablepos = (effect & 0xFF);
+ if(mod->tablepos >= mod->song.length)
+ {
+ mod->tablepos = 0;
+ }
+ mod->patternpos = 0;
+ mod->jump_loop_effect = 1;
+
+ break;
+
+ case EFFECT_SET_VOLUME:
+ /*
+ [12]: Set volume
+ Where [12][x][y] means "set current sample's volume to x*16+y".
+ Legal volumes are 0..64.
+ */
+
+ cptr->volume = (effect & 0xFF);
+ break;
+
+ case EFFECT_PATTERN_BREAK:
+ /*
+ [13]: Pattern Break
+ Where [13][x][y] means "stop the pattern after this division, and
+ continue the song at the next pattern at division x*10+y" (the 10
+ is not a typo). Legal divisions are from 0 to 63 (note Protracker
+ exception above).
+ */
+
+ mod->patternpos = ( ((effect>>4)&0xF)*10 + (effect&0xF) ) * mod->number_of_channels;
+ mod->jump_loop_effect = 1;
+ mod->tablepos++;
+ if(mod->tablepos >= mod->song.length)
+ {
+ mod->tablepos = 0;
+ }
+
+ break;
+
+ case EFFECT_EXTENDED:
+ switch( (effect>>4) & 0xF )
+ {
+ case EFFECT_E_FINE_PORTA_UP:
+ /*
+ [14][1]: Fineslide up
+ Where [14][1][x] means "decrement the period of the current sample
+ by x". The incrementing takes place at the beginning of the
+ division, and hence there is no actual sliding. You cannot slide
+ beyond the note B3 (period 113).
+ */
+
+ cptr->period -= (effect & 0xF);
+ if( cptr->period < 113 )
+ cptr->period = 113;
+ break;
+
+ case EFFECT_E_FINE_PORTA_DOWN:
+ /*
+ [14][2]: Fineslide down
+ Where [14][2][x] means "increment the period of the current sample
+ by x". Similar to [14][1] but shifts the pitch down. You cannot
+ slide beyond the note C1 (period 856).
+ */
+
+ cptr->period += (effect & 0xF);
+ if( cptr->period > 856 )
+ cptr->period = 856;
+ break;
+
+ case EFFECT_E_FINE_VOLSLIDE_UP:
+ /*
+ [14][10]: Fine volume slide up
+ Where [14][10][x] means "increment the volume of the current sample
+ by x". The incrementing takes place at the beginning of the
+ division, and hence there is no sliding. You cannot slide beyond
+ volume 64.
+ */
+
+ cptr->volume += (effect & 0xF);
+ if( cptr->volume>64 )
+ cptr->volume = 64;
+ break;
+
+ case EFFECT_E_FINE_VOLSLIDE_DOWN:
+ /*
+ [14][11]: Fine volume slide down
+ Where [14][11][x] means "decrement the volume of the current sample
+ by x". Similar to [14][10] but lowers volume. You cannot slide
+ beyond volume 0.
+ */
+
+ cptr->volume -= (effect & 0xF);
+ if( cptr->volume > 200 )
+ cptr->volume = 0;
+ break;
+
+ case EFFECT_E_PATTERN_LOOP:
+ /*
+ [14][6]: Loop pattern
+ Where [14][6][x] means "set the start of a loop to this division if
+ x is 0, otherwise after this division, jump back to the start of a
+ loop and play it another x times before continuing". If the start
+ of the loop was not set, it will default to the start of the
+ current pattern. Hence 'loop pattern' cannot be performed across
+ multiple patterns. Note that loops do not support nesting, and you
+ may generate an infinite loop if you try to nest 'loop pattern's.
+ */
+
+ if( effect & 0xF )
+ {
+ if( cptr->patternloopcnt )
+ {
+ cptr->patternloopcnt--;
+ if( cptr->patternloopcnt )
+ {
+ mod->patternpos = cptr->patternloopstartpoint;
+ mod->jump_loop_effect = 1;
+ }
+ else
+ {
+ cptr->patternloopstartpoint = mod->patternpos ;
+ }
+ }
+ else
+ {
+ cptr->patternloopcnt = (effect & 0xF);
+ mod->patternpos = cptr->patternloopstartpoint;
+ mod->jump_loop_effect = 1;
+ }
+ }
+ else // Start point
+ {
+ cptr->patternloopstartpoint = mod->patternpos;
+ }
+
+ break;
+
+ case EFFECT_E_PATTERN_DELAY:
+ /*
+ [14][14]: Delay pattern
+ Where [14][14][x] means "after this division there will be a delay
+ equivalent to the time taken to play x divisions after which the
+ pattern will be resumed". The delay only relates to the
+ interpreting of new divisions, and all effects and previous notes
+ continue during delay.
+ */
+
+ mod->patterndelay = (effect & 0xF);
+ break;
+
+ case EFFECT_E_NOTE_CUT:
+ /*
+ [14][12]: Cut sample
+ Where [14][12][x] means "after the current sample has been played
+ for x ticks in this division, its volume will be set to 0". This
+ implies that if x is 0, then you will not hear any of the sample.
+ If you wish to insert "silence" in a pattern, it is better to use a
+ "silence"-sample (see above) due to the lack of proper support for
+ this effect.
+ */
+ cptr->effect = EFFECT_E_NOTE_CUT;
+ cptr->cut_param = (effect & 0xF);
+ if(!cptr->cut_param)
+ cptr->volume = 0;
+ break;
+
+ default:
+
+ break;
+ }
+ break;
+
+ case 0xF:
+ /*
+ [15]: Set speed
+ Where [15][x][y] means "set speed to x*16+y". Though it is nowhere
+ near that simple. Let z = x*16+y. Depending on what values z takes,
+ different units of speed are set, there being two: ticks/division
+ and beats/minute (though this one is only a label and not strictly
+ true). If z=0, then what should technically happen is that the
+ module stops, but in practice it is treated as if z=1, because
+ there is already a method for stopping the module (running out of
+ patterns). If z<=32, then it means "set ticks/division to z"
+ otherwise it means "set beats/minute to z" (convention says that
+ this should read "If z<32.." but there are some composers out there
+ that defy conventions). Default values are 6 ticks/division, and
+ 125 beats/minute (4 divisions = 1 beat). The beats/minute tag is
+ only meaningful for 6 ticks/division. To get a more accurate view
+ of how things work, use the following formula:
+ 24 * beats/minute
+ divisions/minute = -----------------
+ ticks/division
+ Hence divisions/minute range from 24.75 to 6120, eg. to get a value
+ of 2000 divisions/minute use 3 ticks/division and 250 beats/minute.
+ If multiple "set speed" effects are performed in a single division,
+ the ones on higher-numbered channels take precedence over the ones
+ on lower-numbered channels. This effect has a large number of
+ different implementations, but the one described here has the
+ widest usage.
+ */
+
+ if( (effect&0xFF) < 0x21 )
+ {
+ if( effect&0xFF )
+ {
+ mod->song.speed = effect&0xFF;
+ mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm)));
+ }
+ }
+
+ if( (effect&0xFF) >= 0x21 )
+ {
+ /// HZ = 2 * BPM / 5
+ mod->bpm = effect&0xFF;
+ mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm)));
+ }
+
+ break;
+
+ default:
+ // Unsupported effect
+ break;
+
+ }
+
+}
+
+static void workeffect( note * nptr, channel * cptr )
+{
+ switch(cptr->effect)
+ {
+ case EFFECT_ARPEGGIO:
+
+ if( cptr->parameffect )
+ {
+ cptr->decalperiod = cptr->period - cptr->Arpperiods[cptr->ArpIndex];
+
+ cptr->ArpIndex++;
+ if( cptr->ArpIndex>2 )
+ cptr->ArpIndex = 0;
+ }
+ break;
+
+ case EFFECT_PORTAMENTO_UP:
+
+ if(cptr->period)
+ {
+ cptr->period -= cptr->parameffect;
+
+ if( cptr->period < 113 || cptr->period > 20000 )
+ cptr->period = 113;
+ }
+
+ break;
+
+ case EFFECT_PORTAMENTO_DOWN:
+
+ if(cptr->period)
+ {
+ cptr->period += cptr->parameffect;
+
+ if( cptr->period > 20000 )
+ cptr->period = 20000;
+ }
+
+ break;
+
+ case EFFECT_VOLSLIDE_TONEPORTA:
+ case EFFECT_TONE_PORTAMENTO:
+
+ if( cptr->period && ( cptr->period != cptr->portaperiod ) && cptr->portaperiod )
+ {
+ if( cptr->period > cptr->portaperiod )
+ {
+ if( cptr->period - cptr->portaperiod >= cptr->portaspeed )
+ {
+ cptr->period -= cptr->portaspeed;
+ }
+ else
+ {
+ cptr->period = cptr->portaperiod;
+ }
+ }
+ else
+ {
+ if( cptr->portaperiod - cptr->period >= cptr->portaspeed )
+ {
+ cptr->period += cptr->portaspeed;
+ }
+ else
+ {
+ cptr->period = cptr->portaperiod;
+ }
+ }
+
+ if( cptr->period == cptr->portaperiod )
+ {
+ // If the slide is over, don't let it to be retriggered.
+ cptr->portaperiod = 0;
+ }
+ }
+
+ if( cptr->effect == EFFECT_VOLSLIDE_TONEPORTA )
+ {
+ if( cptr->volumeslide > 0x0F )
+ {
+ cptr->volume = cptr->volume + (cptr->volumeslide>>4);
+
+ if(cptr->volume>63)
+ cptr->volume = 63;
+ }
+ else
+ {
+ cptr->volume = cptr->volume - (cptr->volumeslide);
+
+ if(cptr->volume>63)
+ cptr->volume=0;
+ }
+ }
+ break;
+
+ case EFFECT_VOLSLIDE_VIBRATO:
+ case EFFECT_VIBRATO:
+
+ cptr->vibraperiod = ( (cptr->vibraparam&0xF) * sintable[cptr->vibrapointeur&0x1F] )>>7;
+
+ if( cptr->vibrapointeur > 31 )
+ cptr->vibraperiod = -cptr->vibraperiod;
+
+ cptr->vibrapointeur = (cptr->vibrapointeur+(((cptr->vibraparam>>4))&0xf)) & 0x3F;
+
+ if( cptr->effect == EFFECT_VOLSLIDE_VIBRATO )
+ {
+ if( cptr->volumeslide > 0xF )
+ {
+ cptr->volume = cptr->volume+(cptr->volumeslide>>4);
+
+ if( cptr->volume > 64 )
+ cptr->volume = 64;
+ }
+ else
+ {
+ cptr->volume = cptr->volume - cptr->volumeslide;
+
+ if( cptr->volume > 64 )
+ cptr->volume = 0;
+ }
+ }
+
+ break;
+
+ case EFFECT_VOLUME_SLIDE:
+
+ if( cptr->volumeslide > 0xF )
+ {
+ cptr->volume += (cptr->volumeslide>>4);
+
+ if( cptr->volume > 64 )
+ cptr->volume = 64;
+ }
+ else
+ {
+ cptr->volume -= (cptr->volumeslide&0xf);
+
+ if( cptr->volume > 64 )
+ cptr->volume = 0;
+ }
+ break;
+
+ case EFFECT_E_NOTE_CUT:
+ if(cptr->cut_param)
+ cptr->cut_param--;
+
+ if(!cptr->cut_param)
+ cptr->volume = 0;
+ break;
+
+ default:
+ break;
+
+ }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+bool jar_mod_init(jar_mod_context_t * modctx)
+{
+ muint i,j;
+
+ if( modctx )
+ {
+ memclear(modctx, 0, sizeof(jar_mod_context_t));
+ modctx->playrate = DEFAULT_SAMPLE_RATE;
+ modctx->stereo = 1;
+ modctx->stereo_separation = 1;
+ modctx->bits = 16;
+ modctx->filter = 1;
+ modctx->loopcount = 0;
+
+ for(i=0; i < PERIOD_TABLE_LENGTH - 1; i++)
+ {
+ for(j=0; j < 8; j++)
+ {
+ modctx->fullperiod[(i*8) + j] = periodtable[i] - ((( periodtable[i] - periodtable[i+1] ) / 8) * j);
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+bool jar_mod_setcfg(jar_mod_context_t * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter)
+{
+ if( modctx )
+ {
+ modctx->playrate = samplerate;
+
+ if( stereo )
+ modctx->stereo = 1;
+ else
+ modctx->stereo = 0;
+
+ if(stereo_separation < 4)
+ {
+ modctx->stereo_separation = stereo_separation;
+ }
+
+ if( bits == 8 || bits == 16 )
+ modctx->bits = bits;
+ else
+ modctx->bits = 16;
+
+ if( filter )
+ modctx->filter = 1;
+ else
+ modctx->filter = 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+// make certain that mod_data stays in memory while playing
+static bool jar_mod_load( jar_mod_context_t * modctx, void * mod_data, int mod_data_size )
+{
+ muint i, max;
+ unsigned short t;
+ sample *sptr;
+ unsigned char * modmemory,* endmodmemory;
+
+ modmemory = (unsigned char *)mod_data;
+ endmodmemory = modmemory + mod_data_size;
+
+
+
+ if(modmemory)
+ {
+ if( modctx )
+ {
+ memcopy(&(modctx->song.title),modmemory,1084);
+
+ i = 0;
+ modctx->number_of_channels = 0;
+ while(modlist[i].numberofchannels)
+ {
+ if(memcompare(modctx->song.signature,modlist[i].signature,4))
+ {
+ modctx->number_of_channels = modlist[i].numberofchannels;
+ }
+
+ i++;
+ }
+
+ if( !modctx->number_of_channels )
+ {
+ // 15 Samples modules support
+ // Shift the whole datas to make it look likes a standard 4 channels mod.
+ memcopy(&(modctx->song.signature), "M.K.", 4);
+ memcopy(&(modctx->song.length), &(modctx->song.samples[15]), 130);
+ memclear(&(modctx->song.samples[15]), 0, 480);
+ modmemory += 600;
+ modctx->number_of_channels = 4;
+ }
+ else
+ {
+ modmemory += 1084;
+ }
+
+ if( modmemory >= endmodmemory )
+ return 0; // End passed ? - Probably a bad file !
+
+ // Patterns loading
+ for (i = max = 0; i < 128; i++)
+ {
+ while (max <= modctx->song.patterntable[i])
+ {
+ modctx->patterndata[max] = (note*)modmemory;
+ modmemory += (256*modctx->number_of_channels);
+ max++;
+
+ if( modmemory >= endmodmemory )
+ return 0; // End passed ? - Probably a bad file !
+ }
+ }
+
+ for (i = 0; i < 31; i++)
+ modctx->sampledata[i]=0;
+
+ // Samples loading
+ for (i = 0, sptr = modctx->song.samples; i <31; i++, sptr++)
+ {
+ t= (sptr->length &0xFF00)>>8 | (sptr->length &0xFF)<<8;
+ sptr->length = t*2;
+
+ t= (sptr->reppnt &0xFF00)>>8 | (sptr->reppnt &0xFF)<<8;
+ sptr->reppnt = t*2;
+
+ t= (sptr->replen &0xFF00)>>8 | (sptr->replen &0xFF)<<8;
+ sptr->replen = t*2;
+
+
+ if (sptr->length == 0) continue;
+
+ modctx->sampledata[i] = (char*)modmemory;
+ modmemory += sptr->length;
+
+ if (sptr->replen + sptr->reppnt > sptr->length)
+ sptr->replen = sptr->length - sptr->reppnt;
+
+ if( modmemory > endmodmemory )
+ return 0; // End passed ? - Probably a bad file !
+ }
+
+ // States init
+
+ modctx->tablepos = 0;
+ modctx->patternpos = 0;
+ modctx->song.speed = 6;
+ modctx->bpm = 125;
+ modctx->samplenb = 0;
+
+ modctx->patternticks = (((long)modctx->song.speed * modctx->playrate * 5)/ (2 * modctx->bpm)) + 1;
+ modctx->patternticksaim = ((long)modctx->song.speed * modctx->playrate * 5) / (2 * modctx->bpm);
+
+ modctx->sampleticksconst = 3546894UL / modctx->playrate; //8448*428/playrate;
+
+ for(i=0; i < modctx->number_of_channels; i++)
+ {
+ modctx->channels[i].volume = 0;
+ modctx->channels[i].period = 0;
+ }
+
+ modctx->mod_loaded = 1;
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void jar_mod_fillbuffer( jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf )
+{
+ unsigned long i, j;
+ unsigned long k;
+ unsigned char c;
+ unsigned int state_remaining_steps;
+ int l,r;
+ int ll,lr;
+ int tl,tr;
+ short finalperiod;
+ note *nptr;
+ channel *cptr;
+
+ if( modctx && outbuffer )
+ {
+ if(modctx->mod_loaded)
+ {
+ state_remaining_steps = 0;
+
+ if( trkbuf )
+ {
+ trkbuf->cur_rd_index = 0;
+
+ memcopy(trkbuf->name,modctx->song.title,sizeof(modctx->song.title));
+
+ for(i=0;i<31;i++)
+ {
+ memcopy(trkbuf->instruments[i].name,modctx->song.samples[i].name,sizeof(trkbuf->instruments[i].name));
+ }
+ }
+
+ ll = modctx->last_l_sample;
+ lr = modctx->last_r_sample;
+
+ for (i = 0; i < nbsample; i++)
+ {
+ //---------------------------------------
+ if( modctx->patternticks++ > modctx->patternticksaim )
+ {
+ if( !modctx->patterndelay )
+ {
+ nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]];
+ nptr = nptr + modctx->patternpos;
+ cptr = modctx->channels;
+
+ modctx->patternticks = 0;
+ modctx->patterntickse = 0;
+
+ for(c=0;c<modctx->number_of_channels;c++)
+ {
+ worknote((note*)(nptr+c), (channel*)(cptr+c),(char)(c+1),modctx);
+ }
+
+ if( !modctx->jump_loop_effect )
+ modctx->patternpos += modctx->number_of_channels;
+ else
+ modctx->jump_loop_effect = 0;
+
+ if( modctx->patternpos == 64*modctx->number_of_channels )
+ {
+ modctx->tablepos++;
+ modctx->patternpos = 0;
+ if(modctx->tablepos >= modctx->song.length)
+ {
+ modctx->tablepos = 0;
+ modctx->loopcount++; // count next loop
+ }
+ }
+ }
+ else
+ {
+ modctx->patterndelay--;
+ modctx->patternticks = 0;
+ modctx->patterntickse = 0;
+ }
+
+ }
+
+ if( modctx->patterntickse++ > (modctx->patternticksaim/modctx->song.speed) )
+ {
+ nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]];
+ nptr = nptr + modctx->patternpos;
+ cptr = modctx->channels;
+
+ for(c=0;c<modctx->number_of_channels;c++)
+ {
+ workeffect(nptr+c, cptr+c);
+ }
+
+ modctx->patterntickse = 0;
+ }
+
+ //---------------------------------------
+
+ if( trkbuf && !state_remaining_steps )
+ {
+ if( trkbuf->nb_of_state < trkbuf->nb_max_of_state )
+ {
+ memclear(&trkbuf->track_state_buf[trkbuf->nb_of_state], 0, sizeof(tracker_state));
+ }
+ }
+
+ l=0;
+ r=0;
+
+ for(j =0, cptr = modctx->channels; j < modctx->number_of_channels ; j++, cptr++)
+ {
+ if( cptr->period != 0 )
+ {
+ finalperiod = cptr->period - cptr->decalperiod - cptr->vibraperiod;
+ if( finalperiod )
+ {
+ cptr->samppos += ( (modctx->sampleticksconst<<10) / finalperiod );
+ }
+
+ cptr->ticks++;
+
+ if( cptr->replen<=2 )
+ {
+ if( (cptr->samppos>>10) >= (cptr->length) )
+ {
+ cptr->length = 0;
+ cptr->reppnt = 0;
+
+ if( cptr->length )
+ cptr->samppos = cptr->samppos % (((unsigned long)cptr->length)<<10);
+ else
+ cptr->samppos = 0;
+ }
+ }
+ else
+ {
+ if( (cptr->samppos>>10) >= (unsigned long)(cptr->replen+cptr->reppnt) )
+ {
+ cptr->samppos = ((unsigned long)(cptr->reppnt)<<10) + (cptr->samppos % ((unsigned long)(cptr->replen+cptr->reppnt)<<10));
+ }
+ }
+
+ k = cptr->samppos >> 10;
+
+ if( cptr->sampdata!=0 && ( ((j&3)==1) || ((j&3)==2) ) )
+ {
+ r += ( cptr->sampdata[k] * cptr->volume );
+ }
+
+ if( cptr->sampdata!=0 && ( ((j&3)==0) || ((j&3)==3) ) )
+ {
+ l += ( cptr->sampdata[k] * cptr->volume );
+ }
+
+ if( trkbuf && !state_remaining_steps )
+ {
+ if( trkbuf->nb_of_state < trkbuf->nb_max_of_state )
+ {
+ trkbuf->track_state_buf[trkbuf->nb_of_state].number_of_tracks = modctx->number_of_channels;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].buf_index = i;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern = modctx->song.patterntable[modctx->tablepos];
+ trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_pos = modctx->patternpos / modctx->number_of_channels;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_table_pos = modctx->tablepos;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].bpm = modctx->bpm;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].speed = modctx->song.speed;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_effect = cptr->effect_code;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_parameffect = cptr->parameffect;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_period = finalperiod;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_volume = cptr->volume;
+ trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].instrument_number = (unsigned char)cptr->sampnum;
+ }
+ }
+ }
+ }
+
+ if( trkbuf && !state_remaining_steps )
+ {
+ state_remaining_steps = trkbuf->sample_step;
+
+ if(trkbuf->nb_of_state < trkbuf->nb_max_of_state)
+ trkbuf->nb_of_state++;
+ }
+ else
+ {
+ state_remaining_steps--;
+ }
+
+ tl = (short)l;
+ tr = (short)r;
+
+ if ( modctx->filter )
+ {
+ // Filter
+ l = (l+ll)>>1;
+ r = (r+lr)>>1;
+ }
+
+ if ( modctx->stereo_separation == 1 )
+ {
+ // Left & Right Stereo panning
+ l = (l+(r>>1));
+ r = (r+(l>>1));
+ }
+
+ // Level limitation
+ if( l > 32767 ) l = 32767;
+ if( l < -32768 ) l = -32768;
+ if( r > 32767 ) r = 32767;
+ if( r < -32768 ) r = -32768;
+
+ // Store the final sample.
+ outbuffer[(i*2)] = l;
+ outbuffer[(i*2)+1] = r;
+
+ ll = tl;
+ lr = tr;
+
+ }
+
+ modctx->last_l_sample = ll;
+ modctx->last_r_sample = lr;
+
+ modctx->samplenb = modctx->samplenb+nbsample;
+ }
+ else
+ {
+ for (i = 0; i < nbsample; i++)
+ {
+ // Mod not loaded. Return blank buffer.
+ outbuffer[(i*2)] = 0;
+ outbuffer[(i*2)+1] = 0;
+ }
+
+ if(trkbuf)
+ {
+ trkbuf->nb_of_state = 0;
+ trkbuf->cur_rd_index = 0;
+ trkbuf->name[0] = 0;
+ memclear(trkbuf->track_state_buf, 0, sizeof(tracker_state) * trkbuf->nb_max_of_state);
+ memclear(trkbuf->instruments, 0, sizeof(trkbuf->instruments));
+ }
+ }
+ }
+}
+
+//resets internals for mod context
+static void jar_mod_reset( jar_mod_context_t * modctx)
+{
+ if(modctx)
+ {
+ memclear(&modctx->song, 0, sizeof(modctx->song));
+ memclear(&modctx->sampledata, 0, sizeof(modctx->sampledata));
+ memclear(&modctx->patterndata, 0, sizeof(modctx->patterndata));
+ modctx->tablepos = 0;
+ modctx->patternpos = 0;
+ modctx->patterndelay = 0;
+ modctx->jump_loop_effect = 0;
+ modctx->bpm = 0;
+ modctx->patternticks = 0;
+ modctx->patterntickse = 0;
+ modctx->patternticksaim = 0;
+ modctx->sampleticksconst = 0;
+ modctx->loopcount = 0;
+ modctx->samplenb = 0;
+ memclear(modctx->channels, 0, sizeof(modctx->channels));
+ modctx->number_of_channels = 0;
+ modctx->mod_loaded = 0;
+ modctx->last_r_sample = 0;
+ modctx->last_l_sample = 0;
+
+ jar_mod_init(modctx);
+ }
+}
+
+void jar_mod_unload( jar_mod_context_t * modctx)
+{
+ if(modctx)
+ {
+ if(modctx->modfile)
+ {
+ free(modctx->modfile);
+ modctx->modfile = 0;
+ }
+ jar_mod_reset(modctx);
+ }
+}
+
+
+
+mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename)
+{
+ mulong fsize = 0;
+ if(modctx->modfile)
+ {
+ free(modctx->modfile);
+ modctx->modfile = 0;
+ }
+
+ FILE *f = fopen(filename, "rb");
+ if(f)
+ {
+ fseek(f,0,SEEK_END);
+ fsize = ftell(f);
+ fseek(f,0,SEEK_SET);
+
+ if(fsize && fsize < 32*1024*1024)
+ {
+ modctx->modfile = malloc(fsize);
+ modctx->modfilesize = fsize;
+ memset(modctx->modfile, 0, fsize);
+ fread(modctx->modfile, fsize, 1, f);
+ fclose(f);
+
+ if(!jar_mod_load(modctx, (void*)modctx->modfile, fsize)) fsize = 0;
+ } else fsize = 0;
+ }
+ return fsize;
+}
+
+mulong jar_mod_current_samples(jar_mod_context_t * modctx)
+{
+ if(modctx)
+ return modctx->samplenb;
+
+ return 0;
+}
+
+// Works, however it is very slow, this data should be cached to ensure it is run only once per file
+mulong jar_mod_max_samples(jar_mod_context_t * ctx)
+{
+ jar_mod_context_t tmpctx;
+ jar_mod_init(&tmpctx);
+ if(!jar_mod_load(&tmpctx, (void*)ctx->modfile, ctx->modfilesize)) return 0;
+
+ muint buff[2];
+ mulong lastcount = tmpctx.loopcount;
+
+ while(1){
+ jar_mod_fillbuffer( &tmpctx, buff, 1, 0 );
+ if(tmpctx.loopcount > lastcount) break;
+ }
+ return tmpctx.samplenb;
+}
+
+// move seek_val to sample index, 0 -> jar_mod_max_samples is the range
+void jar_mod_seek_start(jar_mod_context_t * ctx)
+{
+ if(ctx)
+ {
+ jar_mod_reset(ctx);
+ jar_mod_load(ctx, ctx->modfile, ctx->modfilesize);
+ }
+}
+
+#endif // end of JAR_MOD_IMPLEMENTATION
+//-------------------------------------------------------------------------------
+
+
+#endif //end of header file \ No newline at end of file
diff --git a/src/external/jar_xm.h b/src/external/jar_xm.h
new file mode 100644
index 00000000..f9ddb511
--- /dev/null
+++ b/src/external/jar_xm.h
@@ -0,0 +1,2666 @@
+// jar_xm.h - v0.01 - public domain - Joshua Reisenauer, MAR 2016
+//
+// HISTORY:
+//
+// v0.01 2016-02-22 Setup
+//
+//
+// USAGE:
+//
+// In ONE source file, put:
+//
+// #define JAR_XM_IMPLEMENTATION
+// #include "jar_xm.h"
+//
+// Other source files should just include jar_xm.h
+//
+// SAMPLE CODE:
+//
+// jar_xm_context_t *musicptr;
+// float musicBuffer[48000 / 60];
+// int intro_load(void)
+// {
+// jar_xm_create_context_from_file(&musicptr, 48000, "Song.XM");
+// return 1;
+// }
+// int intro_unload(void)
+// {
+// jar_xm_free_context(musicptr);
+// return 1;
+// }
+// int intro_tick(long counter)
+// {
+// jar_xm_generate_samples(musicptr, musicBuffer, (48000 / 60) / 2);
+// if(IsKeyDown(KEY_ENTER))
+// return 1;
+// return 0;
+// }
+//
+//
+// LISCENSE - FOR LIBXM:
+//
+// Author: Romain "Artefact2" Dalmaso <artefact2@gmail.com>
+// Contributor: Dan Spencer <dan@atomicpotato.net>
+// Repackaged into jar_xm.h By: Joshua Adam Reisenauer <kd7tck@gmail.com>
+// This program is free software. It comes without any warranty, to the
+// extent permitted by applicable law. You can redistribute it and/or
+// modify it under the terms of the Do What The Fuck You Want To Public
+// License, Version 2, as published by Sam Hocevar. See
+// http://sam.zoy.org/wtfpl/COPYING for more details.
+
+#ifndef INCLUDE_JAR_XM_H
+#define INCLUDE_JAR_XM_H
+
+#define JAR_XM_DEBUG 0
+#define JAR_XM_LINEAR_INTERPOLATION 1 // speed increase with decrease in quality
+#define JAR_XM_DEFENSIVE 1
+#define JAR_XM_RAMPING 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+
+
+
+//-------------------------------------------------------------------------------
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct jar_xm_context_s;
+typedef struct jar_xm_context_s jar_xm_context_t;
+
+/** Create a XM context.
+ *
+ * @param moddata the contents of the module
+ * @param rate play rate in Hz, recommended value of 48000
+ *
+ * @returns 0 on success
+ * @returns 1 if module data is not sane
+ * @returns 2 if memory allocation failed
+ * @returns 3 unable to open input file
+ * @returns 4 fseek() failed
+ * @returns 5 fread() failed
+ * @returns 6 unkown error
+ *
+ * @deprecated This function is unsafe!
+ * @see jar_xm_create_context_safe()
+ */
+int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const char* filename);
+
+/** Create a XM context.
+ *
+ * @param moddata the contents of the module
+ * @param rate play rate in Hz, recommended value of 48000
+ *
+ * @returns 0 on success
+ * @returns 1 if module data is not sane
+ * @returns 2 if memory allocation failed
+ *
+ * @deprecated This function is unsafe!
+ * @see jar_xm_create_context_safe()
+ */
+int jar_xm_create_context(jar_xm_context_t**, const char* moddata, uint32_t rate);
+
+/** Create a XM context.
+ *
+ * @param moddata the contents of the module
+ * @param moddata_length the length of the contents of the module, in bytes
+ * @param rate play rate in Hz, recommended value of 48000
+ *
+ * @returns 0 on success
+ * @returns 1 if module data is not sane
+ * @returns 2 if memory allocation failed
+ */
+int jar_xm_create_context_safe(jar_xm_context_t**, const char* moddata, size_t moddata_length, uint32_t rate);
+
+/** Free a XM context created by jar_xm_create_context(). */
+void jar_xm_free_context(jar_xm_context_t*);
+
+/** Play the module and put the sound samples in an output buffer.
+ *
+ * @param output buffer of 2*numsamples elements (A left and right value for each sample)
+ * @param numsamples number of samples to generate
+ */
+void jar_xm_generate_samples(jar_xm_context_t*, float* output, size_t numsamples);
+
+/** Play the module, resample from 32 bit to 16 bit, and put the sound samples in an output buffer.
+ *
+ * @param output buffer of 2*numsamples elements (A left and right value for each sample)
+ * @param numsamples number of samples to generate
+ */
+void jar_xm_generate_samples_16bit(jar_xm_context_t* ctx, short* output, size_t numsamples)
+{
+ float* musicBuffer = malloc((2*numsamples)*sizeof(float));
+ jar_xm_generate_samples(ctx, musicBuffer, numsamples);
+
+ if(output){
+ int x;
+ for(x=0;x<2*numsamples;x++)
+ output[x] = musicBuffer[x] * SHRT_MAX;
+ }
+
+ free(musicBuffer);
+}
+
+/** Play the module, resample from 32 bit to 8 bit, and put the sound samples in an output buffer.
+ *
+ * @param output buffer of 2*numsamples elements (A left and right value for each sample)
+ * @param numsamples number of samples to generate
+ */
+void jar_xm_generate_samples_8bit(jar_xm_context_t* ctx, char* output, size_t numsamples)
+{
+ float* musicBuffer = malloc((2*numsamples)*sizeof(float));
+ jar_xm_generate_samples(ctx, musicBuffer, numsamples);
+
+ if(output){
+ int x;
+ for(x=0;x<2*numsamples;x++)
+ output[x] = musicBuffer[x] * CHAR_MAX;
+ }
+
+ free(musicBuffer);
+}
+
+
+
+/** Set the maximum number of times a module can loop. After the
+ * specified number of loops, calls to jar_xm_generate_samples will only
+ * generate silence. You can control the current number of loops with
+ * jar_xm_get_loop_count().
+ *
+ * @param loopcnt maximum number of loops. Use 0 to loop
+ * indefinitely. */
+void jar_xm_set_max_loop_count(jar_xm_context_t*, uint8_t loopcnt);
+
+/** Get the loop count of the currently playing module. This value is
+ * 0 when the module is still playing, 1 when the module has looped
+ * once, etc. */
+uint8_t jar_xm_get_loop_count(jar_xm_context_t*);
+
+
+
+/** Mute or unmute a channel.
+ *
+ * @note Channel numbers go from 1 to jar_xm_get_number_of_channels(...).
+ *
+ * @return whether the channel was muted.
+ */
+bool jar_xm_mute_channel(jar_xm_context_t*, uint16_t, bool);
+
+/** Mute or unmute an instrument.
+ *
+ * @note Instrument numbers go from 1 to
+ * jar_xm_get_number_of_instruments(...).
+ *
+ * @return whether the instrument was muted.
+ */
+bool jar_xm_mute_instrument(jar_xm_context_t*, uint16_t, bool);
+
+
+
+/** Get the module name as a NUL-terminated string. */
+const char* jar_xm_get_module_name(jar_xm_context_t*);
+
+/** Get the tracker name as a NUL-terminated string. */
+const char* jar_xm_get_tracker_name(jar_xm_context_t*);
+
+
+
+/** Get the number of channels. */
+uint16_t jar_xm_get_number_of_channels(jar_xm_context_t*);
+
+/** Get the module length (in patterns). */
+uint16_t jar_xm_get_module_length(jar_xm_context_t*);
+
+/** Get the number of patterns. */
+uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t*);
+
+/** Get the number of rows of a pattern.
+ *
+ * @note Pattern numbers go from 0 to
+ * jar_xm_get_number_of_patterns(...)-1.
+ */
+uint16_t jar_xm_get_number_of_rows(jar_xm_context_t*, uint16_t);
+
+/** Get the number of instruments. */
+uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t*);
+
+/** Get the number of samples of an instrument.
+ *
+ * @note Instrument numbers go from 1 to
+ * jar_xm_get_number_of_instruments(...).
+ */
+uint16_t jar_xm_get_number_of_samples(jar_xm_context_t*, uint16_t);
+
+
+
+/** Get the current module speed.
+ *
+ * @param bpm will receive the current BPM
+ * @param tempo will receive the current tempo (ticks per line)
+ */
+void jar_xm_get_playing_speed(jar_xm_context_t*, uint16_t* bpm, uint16_t* tempo);
+
+/** Get the current position in the module being played.
+ *
+ * @param pattern_index if not NULL, will receive the current pattern
+ * index in the POT (pattern order table)
+ *
+ * @param pattern if not NULL, will receive the current pattern number
+ *
+ * @param row if not NULL, will receive the current row
+ *
+ * @param samples if not NULL, will receive the total number of
+ * generated samples (divide by sample rate to get seconds of
+ * generated audio)
+ */
+void jar_xm_get_position(jar_xm_context_t*, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples);
+
+/** Get the latest time (in number of generated samples) when a
+ * particular instrument was triggered in any channel.
+ *
+ * @note Instrument numbers go from 1 to
+ * jar_xm_get_number_of_instruments(...).
+ */
+uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t*, uint16_t);
+
+/** Get the latest time (in number of generated samples) when a
+ * particular sample was triggered in any channel.
+ *
+ * @note Instrument numbers go from 1 to
+ * jar_xm_get_number_of_instruments(...).
+ *
+ * @note Sample numbers go from 0 to
+ * jar_xm_get_nubmer_of_samples(...,instr)-1.
+ */
+uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t*, uint16_t instr, uint16_t sample);
+
+/** Get the latest time (in number of generated samples) when any
+ * instrument was triggered in a given channel.
+ *
+ * @note Channel numbers go from 1 to jar_xm_get_number_of_channels(...).
+ */
+uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t*, uint16_t);
+
+/** Get the number of remaining samples. Divide by 2 to get the number of individual LR data samples.
+ *
+ * @note This is the remaining number of samples before the loop starts module again, or halts if on last pass.
+ * @note This function is very slow and should only be run once, if at all.
+ */
+uint64_t jar_xm_get_remaining_samples(jar_xm_context_t*);
+
+#ifdef __cplusplus
+}
+#endif
+//-------------------------------------------------------------------------------
+
+
+
+
+
+
+//Function Definitions-----------------------------------------------------------
+#ifdef JAR_XM_IMPLEMENTATION
+
+#include <math.h>
+#include <string.h>
+
+#if JAR_XM_DEBUG
+#include <stdio.h>
+#define DEBUG(fmt, ...) do { \
+ fprintf(stderr, "%s(): " fmt "\n", __func__, __VA_ARGS__); \
+ fflush(stderr); \
+ } while(0)
+#else
+#define DEBUG(...)
+#endif
+
+#if jar_xm_BIG_ENDIAN
+#error "Big endian platforms are not yet supported, sorry"
+/* Make sure the compiler stops, even if #error is ignored */
+extern int __fail[-1];
+#endif
+
+/* ----- XM constants ----- */
+
+#define SAMPLE_NAME_LENGTH 22
+#define INSTRUMENT_NAME_LENGTH 22
+#define MODULE_NAME_LENGTH 20
+#define TRACKER_NAME_LENGTH 20
+#define PATTERN_ORDER_TABLE_LENGTH 256
+#define NUM_NOTES 96
+#define NUM_ENVELOPE_POINTS 12
+#define MAX_NUM_ROWS 256
+
+#if JAR_XM_RAMPING
+#define jar_xm_SAMPLE_RAMPING_POINTS 0x20
+#endif
+
+/* ----- Data types ----- */
+
+enum jar_xm_waveform_type_e {
+ jar_xm_SINE_WAVEFORM = 0,
+ jar_xm_RAMP_DOWN_WAVEFORM = 1,
+ jar_xm_SQUARE_WAVEFORM = 2,
+ jar_xm_RANDOM_WAVEFORM = 3,
+ jar_xm_RAMP_UP_WAVEFORM = 4,
+};
+typedef enum jar_xm_waveform_type_e jar_xm_waveform_type_t;
+
+enum jar_xm_loop_type_e {
+ jar_xm_NO_LOOP,
+ jar_xm_FORWARD_LOOP,
+ jar_xm_PING_PONG_LOOP,
+};
+typedef enum jar_xm_loop_type_e jar_xm_loop_type_t;
+
+enum jar_xm_frequency_type_e {
+ jar_xm_LINEAR_FREQUENCIES,
+ jar_xm_AMIGA_FREQUENCIES,
+};
+typedef enum jar_xm_frequency_type_e jar_xm_frequency_type_t;
+
+struct jar_xm_envelope_point_s {
+ uint16_t frame;
+ uint16_t value;
+};
+typedef struct jar_xm_envelope_point_s jar_xm_envelope_point_t;
+
+struct jar_xm_envelope_s {
+ jar_xm_envelope_point_t points[NUM_ENVELOPE_POINTS];
+ uint8_t num_points;
+ uint8_t sustain_point;
+ uint8_t loop_start_point;
+ uint8_t loop_end_point;
+ bool enabled;
+ bool sustain_enabled;
+ bool loop_enabled;
+};
+typedef struct jar_xm_envelope_s jar_xm_envelope_t;
+
+struct jar_xm_sample_s {
+ char name[SAMPLE_NAME_LENGTH + 1];
+ int8_t bits; /* Either 8 or 16 */
+
+ uint32_t length;
+ uint32_t loop_start;
+ uint32_t loop_length;
+ uint32_t loop_end;
+ float volume;
+ int8_t finetune;
+ jar_xm_loop_type_t loop_type;
+ float panning;
+ int8_t relative_note;
+ uint64_t latest_trigger;
+
+ float* data;
+ };
+ typedef struct jar_xm_sample_s jar_xm_sample_t;
+
+ struct jar_xm_instrument_s {
+ char name[INSTRUMENT_NAME_LENGTH + 1];
+ uint16_t num_samples;
+ uint8_t sample_of_notes[NUM_NOTES];
+ jar_xm_envelope_t volume_envelope;
+ jar_xm_envelope_t panning_envelope;
+ jar_xm_waveform_type_t vibrato_type;
+ uint8_t vibrato_sweep;
+ uint8_t vibrato_depth;
+ uint8_t vibrato_rate;
+ uint16_t volume_fadeout;
+ uint64_t latest_trigger;
+ bool muted;
+
+ jar_xm_sample_t* samples;
+ };
+ typedef struct jar_xm_instrument_s jar_xm_instrument_t;
+
+ struct jar_xm_pattern_slot_s {
+ uint8_t note; /* 1-96, 97 = Key Off note */
+ uint8_t instrument; /* 1-128 */
+ uint8_t volume_column;
+ uint8_t effect_type;
+ uint8_t effect_param;
+ };
+ typedef struct jar_xm_pattern_slot_s jar_xm_pattern_slot_t;
+
+ struct jar_xm_pattern_s {
+ uint16_t num_rows;
+ jar_xm_pattern_slot_t* slots; /* Array of size num_rows * num_channels */
+ };
+ typedef struct jar_xm_pattern_s jar_xm_pattern_t;
+
+ struct jar_xm_module_s {
+ char name[MODULE_NAME_LENGTH + 1];
+ char trackername[TRACKER_NAME_LENGTH + 1];
+ uint16_t length;
+ uint16_t restart_position;
+ uint16_t num_channels;
+ uint16_t num_patterns;
+ uint16_t num_instruments;
+ jar_xm_frequency_type_t frequency_type;
+ uint8_t pattern_table[PATTERN_ORDER_TABLE_LENGTH];
+
+ jar_xm_pattern_t* patterns;
+ jar_xm_instrument_t* instruments; /* Instrument 1 has index 0,
+ * instrument 2 has index 1, etc. */
+ };
+ typedef struct jar_xm_module_s jar_xm_module_t;
+
+ struct jar_xm_channel_context_s {
+ float note;
+ float orig_note; /* The original note before effect modifications, as read in the pattern. */
+ jar_xm_instrument_t* instrument; /* Could be NULL */
+ jar_xm_sample_t* sample; /* Could be NULL */
+ jar_xm_pattern_slot_t* current;
+
+ float sample_position;
+ float period;
+ float frequency;
+ float step;
+ bool ping; /* For ping-pong samples: true is -->, false is <-- */
+
+ float volume; /* Ideally between 0 (muted) and 1 (loudest) */
+ float panning; /* Between 0 (left) and 1 (right); 0.5 is centered */
+
+ uint16_t autovibrato_ticks;
+
+ bool sustained;
+ float fadeout_volume;
+ float volume_envelope_volume;
+ float panning_envelope_panning;
+ uint16_t volume_envelope_frame_count;
+ uint16_t panning_envelope_frame_count;
+
+ float autovibrato_note_offset;
+
+ bool arp_in_progress;
+ uint8_t arp_note_offset;
+ uint8_t volume_slide_param;
+ uint8_t fine_volume_slide_param;
+ uint8_t global_volume_slide_param;
+ uint8_t panning_slide_param;
+ uint8_t portamento_up_param;
+ uint8_t portamento_down_param;
+ uint8_t fine_portamento_up_param;
+ uint8_t fine_portamento_down_param;
+ uint8_t extra_fine_portamento_up_param;
+ uint8_t extra_fine_portamento_down_param;
+ uint8_t tone_portamento_param;
+ float tone_portamento_target_period;
+ uint8_t multi_retrig_param;
+ uint8_t note_delay_param;
+ uint8_t pattern_loop_origin; /* Where to restart a E6y loop */
+ uint8_t pattern_loop_count; /* How many loop passes have been done */
+ bool vibrato_in_progress;
+ jar_xm_waveform_type_t vibrato_waveform;
+ bool vibrato_waveform_retrigger; /* True if a new note retriggers the waveform */
+ uint8_t vibrato_param;
+ uint16_t vibrato_ticks; /* Position in the waveform */
+ float vibrato_note_offset;
+ jar_xm_waveform_type_t tremolo_waveform;
+ bool tremolo_waveform_retrigger;
+ uint8_t tremolo_param;
+ uint8_t tremolo_ticks;
+ float tremolo_volume;
+ uint8_t tremor_param;
+ bool tremor_on;
+
+ uint64_t latest_trigger;
+ bool muted;
+
+#if JAR_XM_RAMPING
+ /* These values are updated at the end of each tick, to save
+ * a couple of float operations on every generated sample. */
+ float target_panning;
+ float target_volume;
+
+ unsigned long frame_count;
+ float end_of_previous_sample[jar_xm_SAMPLE_RAMPING_POINTS];
+#endif
+
+ float actual_panning;
+ float actual_volume;
+ };
+ typedef struct jar_xm_channel_context_s jar_xm_channel_context_t;
+
+ struct jar_xm_context_s {
+ void* allocated_memory;
+ jar_xm_module_t module;
+ uint32_t rate;
+
+ uint16_t tempo;
+ uint16_t bpm;
+ float global_volume;
+ float amplification;
+
+#if JAR_XM_RAMPING
+ /* How much is a channel final volume allowed to change per
+ * sample; this is used to avoid abrubt volume changes which
+ * manifest as "clicks" in the generated sound. */
+ float volume_ramp;
+ float panning_ramp; /* Same for panning. */
+#endif
+
+ uint8_t current_table_index;
+ uint8_t current_row;
+ uint16_t current_tick; /* Can go below 255, with high tempo and a pattern delay */
+ float remaining_samples_in_tick;
+ uint64_t generated_samples;
+
+ bool position_jump;
+ bool pattern_break;
+ uint8_t jump_dest;
+ uint8_t jump_row;
+
+ /* Extra ticks to be played before going to the next row -
+ * Used for EEy effect */
+ uint16_t extra_ticks;
+
+ uint8_t* row_loop_count; /* Array of size MAX_NUM_ROWS * module_length */
+ uint8_t loop_count;
+ uint8_t max_loop_count;
+
+ jar_xm_channel_context_t* channels;
+};
+
+/* ----- Internal API ----- */
+
+#if JAR_XM_DEFENSIVE
+
+/** Check the module data for errors/inconsistencies.
+ *
+ * @returns 0 if everything looks OK. Module should be safe to load.
+ */
+int jar_xm_check_sanity_preload(const char*, size_t);
+
+/** Check a loaded module for errors/inconsistencies.
+ *
+ * @returns 0 if everything looks OK.
+ */
+int jar_xm_check_sanity_postload(jar_xm_context_t*);
+
+#endif
+
+/** Get the number of bytes needed to store the module data in a
+ * dynamically allocated blank context.
+ *
+ * Things that are dynamically allocated:
+ * - sample data
+ * - sample structures in instruments
+ * - pattern data
+ * - row loop count arrays
+ * - pattern structures in module
+ * - instrument structures in module
+ * - channel contexts
+ * - context structure itself
+
+ * @returns 0 if everything looks OK.
+ */
+size_t jar_xm_get_memory_needed_for_context(const char*, size_t);
+
+/** Populate the context from module data.
+ *
+ * @returns pointer to the memory pool
+ */
+char* jar_xm_load_module(jar_xm_context_t*, const char*, size_t, char*);
+
+int jar_xm_create_context(jar_xm_context_t** ctxp, const char* moddata, uint32_t rate) {
+ return jar_xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate);
+}
+
+int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) {
+#if JAR_XM_DEFENSIVE
+ int ret;
+#endif
+ size_t bytes_needed;
+ char* mempool;
+ jar_xm_context_t* ctx;
+
+#if JAR_XM_DEFENSIVE
+ if((ret = jar_xm_check_sanity_preload(moddata, moddata_length))) {
+ DEBUG("jar_xm_check_sanity_preload() returned %i, module is not safe to load", ret);
+ return 1;
+ }
+#endif
+
+ bytes_needed = jar_xm_get_memory_needed_for_context(moddata, moddata_length);
+ mempool = malloc(bytes_needed);
+ if(mempool == NULL && bytes_needed > 0) {
+ /* malloc() failed, trouble ahead */
+ DEBUG("call to malloc() failed, returned %p", (void*)mempool);
+ return 2;
+ }
+
+ /* Initialize most of the fields to 0, 0.f, NULL or false depending on type */
+ memset(mempool, 0, bytes_needed);
+
+ ctx = (*ctxp = (jar_xm_context_t*)mempool);
+ ctx->allocated_memory = mempool; /* Keep original pointer for free() */
+ mempool += sizeof(jar_xm_context_t);
+
+ ctx->rate = rate;
+ mempool = jar_xm_load_module(ctx, moddata, moddata_length, mempool);
+
+ ctx->channels = (jar_xm_channel_context_t*)mempool;
+ mempool += ctx->module.num_channels * sizeof(jar_xm_channel_context_t);
+
+ ctx->global_volume = 1.f;
+ ctx->amplification = .25f; /* XXX: some bad modules may still clip. Find out something better. */
+
+#if JAR_XM_RAMPING
+ ctx->volume_ramp = (1.f / 128.f);
+ ctx->panning_ramp = (1.f / 128.f);
+#endif
+
+ for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
+ jar_xm_channel_context_t* ch = ctx->channels + i;
+
+ ch->ping = true;
+ ch->vibrato_waveform = jar_xm_SINE_WAVEFORM;
+ ch->vibrato_waveform_retrigger = true;
+ ch->tremolo_waveform = jar_xm_SINE_WAVEFORM;
+ ch->tremolo_waveform_retrigger = true;
+
+ ch->volume = ch->volume_envelope_volume = ch->fadeout_volume = 1.0f;
+ ch->panning = ch->panning_envelope_panning = .5f;
+ ch->actual_volume = .0f;
+ ch->actual_panning = .5f;
+ }
+
+ ctx->row_loop_count = (uint8_t*)mempool;
+ mempool += MAX_NUM_ROWS * sizeof(uint8_t);
+
+#if JAR_XM_DEFENSIVE
+ if((ret = jar_xm_check_sanity_postload(ctx))) {
+ DEBUG("jar_xm_check_sanity_postload() returned %i, module is not safe to play", ret);
+ jar_xm_free_context(ctx);
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+void jar_xm_free_context(jar_xm_context_t* context) {
+ free(context->allocated_memory);
+}
+
+void jar_xm_set_max_loop_count(jar_xm_context_t* context, uint8_t loopcnt) {
+ context->max_loop_count = loopcnt;
+}
+
+uint8_t jar_xm_get_loop_count(jar_xm_context_t* context) {
+ return context->loop_count;
+}
+
+
+
+bool jar_xm_mute_channel(jar_xm_context_t* ctx, uint16_t channel, bool mute) {
+ bool old = ctx->channels[channel - 1].muted;
+ ctx->channels[channel - 1].muted = mute;
+ return old;
+}
+
+bool jar_xm_mute_instrument(jar_xm_context_t* ctx, uint16_t instr, bool mute) {
+ bool old = ctx->module.instruments[instr - 1].muted;
+ ctx->module.instruments[instr - 1].muted = mute;
+ return old;
+}
+
+
+
+const char* jar_xm_get_module_name(jar_xm_context_t* ctx) {
+ return ctx->module.name;
+}
+
+const char* jar_xm_get_tracker_name(jar_xm_context_t* ctx) {
+ return ctx->module.trackername;
+}
+
+
+
+uint16_t jar_xm_get_number_of_channels(jar_xm_context_t* ctx) {
+ return ctx->module.num_channels;
+}
+
+uint16_t jar_xm_get_module_length(jar_xm_context_t* ctx) {
+ return ctx->module.length;
+}
+
+uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t* ctx) {
+ return ctx->module.num_patterns;
+}
+
+uint16_t jar_xm_get_number_of_rows(jar_xm_context_t* ctx, uint16_t pattern) {
+ return ctx->module.patterns[pattern].num_rows;
+}
+
+uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t* ctx) {
+ return ctx->module.num_instruments;
+}
+
+uint16_t jar_xm_get_number_of_samples(jar_xm_context_t* ctx, uint16_t instrument) {
+ return ctx->module.instruments[instrument - 1].num_samples;
+}
+
+
+
+void jar_xm_get_playing_speed(jar_xm_context_t* ctx, uint16_t* bpm, uint16_t* tempo) {
+ if(bpm) *bpm = ctx->bpm;
+ if(tempo) *tempo = ctx->tempo;
+}
+
+void jar_xm_get_position(jar_xm_context_t* ctx, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples) {
+ if(pattern_index) *pattern_index = ctx->current_table_index;
+ if(pattern) *pattern = ctx->module.pattern_table[ctx->current_table_index];
+ if(row) *row = ctx->current_row;
+ if(samples) *samples = ctx->generated_samples;
+}
+
+uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t* ctx, uint16_t instr) {
+ return ctx->module.instruments[instr - 1].latest_trigger;
+}
+
+uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t* ctx, uint16_t instr, uint16_t sample) {
+ return ctx->module.instruments[instr - 1].samples[sample].latest_trigger;
+}
+
+uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t* ctx, uint16_t chn) {
+ return ctx->channels[chn - 1].latest_trigger;
+}
+
+/* .xm files are little-endian. (XXX: Are they really?) */
+
+/* Bounded reader macros.
+ * If we attempt to read the buffer out-of-bounds, pretend that the buffer is
+ * infinitely padded with zeroes.
+ */
+#define READ_U8(offset) (((offset) < moddata_length) ? (*(uint8_t*)(moddata + (offset))) : 0)
+#define READ_U16(offset) ((uint16_t)READ_U8(offset) | ((uint16_t)READ_U8((offset) + 1) << 8))
+#define READ_U32(offset) ((uint32_t)READ_U16(offset) | ((uint32_t)READ_U16((offset) + 2) << 16))
+#define READ_MEMCPY(ptr, offset, length) memcpy_pad(ptr, length, moddata, moddata_length, offset)
+
+static inline void memcpy_pad(void* dst, size_t dst_len, const void* src, size_t src_len, size_t offset) {
+ uint8_t* dst_c = dst;
+ const uint8_t* src_c = src;
+
+ /* how many bytes can be copied without overrunning `src` */
+ size_t copy_bytes = (src_len >= offset) ? (src_len - offset) : 0;
+ copy_bytes = copy_bytes > dst_len ? dst_len : copy_bytes;
+
+ memcpy(dst_c, src_c + offset, copy_bytes);
+ /* padded bytes */
+ memset(dst_c + copy_bytes, 0, dst_len - copy_bytes);
+}
+
+#if JAR_XM_DEFENSIVE
+
+int jar_xm_check_sanity_preload(const char* module, size_t module_length) {
+ if(module_length < 60) {
+ return 4;
+ }
+
+ if(memcmp("Extended Module: ", module, 17) != 0) {
+ return 1;
+ }
+
+ if(module[37] != 0x1A) {
+ return 2;
+ }
+
+ if(module[59] != 0x01 || module[58] != 0x04) {
+ /* Not XM 1.04 */
+ return 3;
+ }
+
+ return 0;
+}
+
+int jar_xm_check_sanity_postload(jar_xm_context_t* ctx) {
+ /* @todo: plenty of stuff to do here… */
+
+ /* Check the POT */
+ for(uint8_t i = 0; i < ctx->module.length; ++i) {
+ if(ctx->module.pattern_table[i] >= ctx->module.num_patterns) {
+ if(i+1 == ctx->module.length && ctx->module.length > 1) {
+ /* Cheap fix */
+ --ctx->module.length;
+ DEBUG("trimming invalid POT at pos %X", i);
+ } else {
+ DEBUG("module has invalid POT, pos %X references nonexistent pattern %X",
+ i,
+ ctx->module.pattern_table[i]);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+size_t jar_xm_get_memory_needed_for_context(const char* moddata, size_t moddata_length) {
+ size_t memory_needed = 0;
+ size_t offset = 60; /* Skip the first header */
+ uint16_t num_channels;
+ uint16_t num_patterns;
+ uint16_t num_instruments;
+
+ /* Read the module header */
+
+ num_channels = READ_U16(offset + 8);
+ num_channels = READ_U16(offset + 8);
+
+ num_patterns = READ_U16(offset + 10);
+ memory_needed += num_patterns * sizeof(jar_xm_pattern_t);
+
+ num_instruments = READ_U16(offset + 12);
+ memory_needed += num_instruments * sizeof(jar_xm_instrument_t);
+
+ memory_needed += MAX_NUM_ROWS * READ_U16(offset + 4) * sizeof(uint8_t); /* Module length */
+
+ /* Header size */
+ offset += READ_U32(offset);
+
+ /* Read pattern headers */
+ for(uint16_t i = 0; i < num_patterns; ++i) {
+ uint16_t num_rows;
+
+ num_rows = READ_U16(offset + 5);
+ memory_needed += num_rows * num_channels * sizeof(jar_xm_pattern_slot_t);
+
+ /* Pattern header length + packed pattern data size */
+ offset += READ_U32(offset) + READ_U16(offset + 7);
+ }
+
+ /* Read instrument headers */
+ for(uint16_t i = 0; i < num_instruments; ++i) {
+ uint16_t num_samples;
+ uint32_t sample_header_size = 0;
+ uint32_t sample_size_aggregate = 0;
+
+ num_samples = READ_U16(offset + 27);
+ memory_needed += num_samples * sizeof(jar_xm_sample_t);
+
+ if(num_samples > 0) {
+ sample_header_size = READ_U32(offset + 29);
+ }
+
+ /* Instrument header size */
+ offset += READ_U32(offset);
+
+ for(uint16_t j = 0; j < num_samples; ++j) {
+ uint32_t sample_size;
+ uint8_t flags;
+
+ sample_size = READ_U32(offset);
+ flags = READ_U8(offset + 14);
+ sample_size_aggregate += sample_size;
+
+ if(flags & (1 << 4)) {
+ /* 16 bit sample */
+ memory_needed += sample_size * (sizeof(float) >> 1);
+ } else {
+ /* 8 bit sample */
+ memory_needed += sample_size * sizeof(float);
+ }
+
+ offset += sample_header_size;
+ }
+
+ offset += sample_size_aggregate;
+ }
+
+ memory_needed += num_channels * sizeof(jar_xm_channel_context_t);
+ memory_needed += sizeof(jar_xm_context_t);
+
+ return memory_needed;
+}
+
+char* jar_xm_load_module(jar_xm_context_t* ctx, const char* moddata, size_t moddata_length, char* mempool) {
+ size_t offset = 0;
+ jar_xm_module_t* mod = &(ctx->module);
+
+ /* Read XM header */
+ READ_MEMCPY(mod->name, offset + 17, MODULE_NAME_LENGTH);
+ READ_MEMCPY(mod->trackername, offset + 38, TRACKER_NAME_LENGTH);
+ offset += 60;
+
+ /* Read module header */
+ uint32_t header_size = READ_U32(offset);
+
+ mod->length = READ_U16(offset + 4);
+ mod->restart_position = READ_U16(offset + 6);
+ mod->num_channels = READ_U16(offset + 8);
+ mod->num_patterns = READ_U16(offset + 10);
+ mod->num_instruments = READ_U16(offset + 12);
+
+ mod->patterns = (jar_xm_pattern_t*)mempool;
+ mempool += mod->num_patterns * sizeof(jar_xm_pattern_t);
+
+ mod->instruments = (jar_xm_instrument_t*)mempool;
+ mempool += mod->num_instruments * sizeof(jar_xm_instrument_t);
+
+ uint16_t flags = READ_U32(offset + 14);
+ mod->frequency_type = (flags & (1 << 0)) ? jar_xm_LINEAR_FREQUENCIES : jar_xm_AMIGA_FREQUENCIES;
+
+ ctx->tempo = READ_U16(offset + 16);
+ ctx->bpm = READ_U16(offset + 18);
+
+ READ_MEMCPY(mod->pattern_table, offset + 20, PATTERN_ORDER_TABLE_LENGTH);
+ offset += header_size;
+
+ /* Read patterns */
+ for(uint16_t i = 0; i < mod->num_patterns; ++i) {
+ uint16_t packed_patterndata_size = READ_U16(offset + 7);
+ jar_xm_pattern_t* pat = mod->patterns + i;
+
+ pat->num_rows = READ_U16(offset + 5);
+
+ pat->slots = (jar_xm_pattern_slot_t*)mempool;
+ mempool += mod->num_channels * pat->num_rows * sizeof(jar_xm_pattern_slot_t);
+
+ /* Pattern header length */
+ offset += READ_U32(offset);
+
+ if(packed_patterndata_size == 0) {
+ /* No pattern data is present */
+ memset(pat->slots, 0, sizeof(jar_xm_pattern_slot_t) * pat->num_rows * mod->num_channels);
+ } else {
+ /* This isn't your typical for loop */
+ for(uint16_t j = 0, k = 0; j < packed_patterndata_size; ++k) {
+ uint8_t note = READ_U8(offset + j);
+ jar_xm_pattern_slot_t* slot = pat->slots + k;
+
+ if(note & (1 << 7)) {
+ /* MSB is set, this is a compressed packet */
+ ++j;
+
+ if(note & (1 << 0)) {
+ /* Note follows */
+ slot->note = READ_U8(offset + j);
+ ++j;
+ } else {
+ slot->note = 0;
+ }
+
+ if(note & (1 << 1)) {
+ /* Instrument follows */
+ slot->instrument = READ_U8(offset + j);
+ ++j;
+ } else {
+ slot->instrument = 0;
+ }
+
+ if(note & (1 << 2)) {
+ /* Volume column follows */
+ slot->volume_column = READ_U8(offset + j);
+ ++j;
+ } else {
+ slot->volume_column = 0;
+ }
+
+ if(note & (1 << 3)) {
+ /* Effect follows */
+ slot->effect_type = READ_U8(offset + j);
+ ++j;
+ } else {
+ slot->effect_type = 0;
+ }
+
+ if(note & (1 << 4)) {
+ /* Effect parameter follows */
+ slot->effect_param = READ_U8(offset + j);
+ ++j;
+ } else {
+ slot->effect_param = 0;
+ }
+ } else {
+ /* Uncompressed packet */
+ slot->note = note;
+ slot->instrument = READ_U8(offset + j + 1);
+ slot->volume_column = READ_U8(offset + j + 2);
+ slot->effect_type = READ_U8(offset + j + 3);
+ slot->effect_param = READ_U8(offset + j + 4);
+ j += 5;
+ }
+ }
+ }
+
+ offset += packed_patterndata_size;
+ }
+
+ /* Read instruments */
+ for(uint16_t i = 0; i < ctx->module.num_instruments; ++i) {
+ uint32_t sample_header_size = 0;
+ jar_xm_instrument_t* instr = mod->instruments + i;
+
+ READ_MEMCPY(instr->name, offset + 4, INSTRUMENT_NAME_LENGTH);
+ instr->num_samples = READ_U16(offset + 27);
+
+ if(instr->num_samples > 0) {
+ /* Read extra header properties */
+ sample_header_size = READ_U32(offset + 29);
+ READ_MEMCPY(instr->sample_of_notes, offset + 33, NUM_NOTES);
+
+ instr->volume_envelope.num_points = READ_U8(offset + 225);
+ instr->panning_envelope.num_points = READ_U8(offset + 226);
+
+ for(uint8_t j = 0; j < instr->volume_envelope.num_points; ++j) {
+ instr->volume_envelope.points[j].frame = READ_U16(offset + 129 + 4 * j);
+ instr->volume_envelope.points[j].value = READ_U16(offset + 129 + 4 * j + 2);
+ }
+
+ for(uint8_t j = 0; j < instr->panning_envelope.num_points; ++j) {
+ instr->panning_envelope.points[j].frame = READ_U16(offset + 177 + 4 * j);
+ instr->panning_envelope.points[j].value = READ_U16(offset + 177 + 4 * j + 2);
+ }
+
+ instr->volume_envelope.sustain_point = READ_U8(offset + 227);
+ instr->volume_envelope.loop_start_point = READ_U8(offset + 228);
+ instr->volume_envelope.loop_end_point = READ_U8(offset + 229);
+
+ instr->panning_envelope.sustain_point = READ_U8(offset + 230);
+ instr->panning_envelope.loop_start_point = READ_U8(offset + 231);
+ instr->panning_envelope.loop_end_point = READ_U8(offset + 232);
+
+ uint8_t flags = READ_U8(offset + 233);
+ instr->volume_envelope.enabled = flags & (1 << 0);
+ instr->volume_envelope.sustain_enabled = flags & (1 << 1);
+ instr->volume_envelope.loop_enabled = flags & (1 << 2);
+
+ flags = READ_U8(offset + 234);
+ instr->panning_envelope.enabled = flags & (1 << 0);
+ instr->panning_envelope.sustain_enabled = flags & (1 << 1);
+ instr->panning_envelope.loop_enabled = flags & (1 << 2);
+
+ instr->vibrato_type = READ_U8(offset + 235);
+ if(instr->vibrato_type == 2) {
+ instr->vibrato_type = 1;
+ } else if(instr->vibrato_type == 1) {
+ instr->vibrato_type = 2;
+ }
+ instr->vibrato_sweep = READ_U8(offset + 236);
+ instr->vibrato_depth = READ_U8(offset + 237);
+ instr->vibrato_rate = READ_U8(offset + 238);
+ instr->volume_fadeout = READ_U16(offset + 239);
+
+ instr->samples = (jar_xm_sample_t*)mempool;
+ mempool += instr->num_samples * sizeof(jar_xm_sample_t);
+ } else {
+ instr->samples = NULL;
+ }
+
+ /* Instrument header size */
+ offset += READ_U32(offset);
+
+ for(uint16_t j = 0; j < instr->num_samples; ++j) {
+ /* Read sample header */
+ jar_xm_sample_t* sample = instr->samples + j;
+
+ sample->length = READ_U32(offset);
+ sample->loop_start = READ_U32(offset + 4);
+ sample->loop_length = READ_U32(offset + 8);
+ sample->loop_end = sample->loop_start + sample->loop_length;
+ sample->volume = (float)READ_U8(offset + 12) / (float)0x40;
+ sample->finetune = (int8_t)READ_U8(offset + 13);
+
+ uint8_t flags = READ_U8(offset + 14);
+ if((flags & 3) == 0) {
+ sample->loop_type = jar_xm_NO_LOOP;
+ } else if((flags & 3) == 1) {
+ sample->loop_type = jar_xm_FORWARD_LOOP;
+ } else {
+ sample->loop_type = jar_xm_PING_PONG_LOOP;
+ }
+
+ sample->bits = (flags & (1 << 4)) ? 16 : 8;
+
+ sample->panning = (float)READ_U8(offset + 15) / (float)0xFF;
+ sample->relative_note = (int8_t)READ_U8(offset + 16);
+ READ_MEMCPY(sample->name, 18, SAMPLE_NAME_LENGTH);
+ sample->data = (float*)mempool;
+
+ if(sample->bits == 16) {
+ /* 16 bit sample */
+ mempool += sample->length * (sizeof(float) >> 1);
+ sample->loop_start >>= 1;
+ sample->loop_length >>= 1;
+ sample->loop_end >>= 1;
+ sample->length >>= 1;
+ } else {
+ /* 8 bit sample */
+ mempool += sample->length * sizeof(float);
+ }
+
+ offset += sample_header_size;
+ }
+
+ for(uint16_t j = 0; j < instr->num_samples; ++j) {
+ /* Read sample data */
+ jar_xm_sample_t* sample = instr->samples + j;
+ uint32_t length = sample->length;
+
+ if(sample->bits == 16) {
+ int16_t v = 0;
+ for(uint32_t k = 0; k < length; ++k) {
+ v = v + (int16_t)READ_U16(offset + (k << 1));
+ sample->data[k] = (float)v / (float)(1 << 15);
+ }
+ offset += sample->length << 1;
+ } else {
+ int8_t v = 0;
+ for(uint32_t k = 0; k < length; ++k) {
+ v = v + (int8_t)READ_U8(offset + k);
+ sample->data[k] = (float)v / (float)(1 << 7);
+ }
+ offset += sample->length;
+ }
+ }
+ }
+
+ return mempool;
+}
+
+//-------------------------------------------------------------------------------
+//THE FOLLOWING IS FOR PLAYING
+//-------------------------------------------------------------------------------
+
+/* ----- Static functions ----- */
+
+static float jar_xm_waveform(jar_xm_waveform_type_t, uint8_t);
+static void jar_xm_autovibrato(jar_xm_context_t*, jar_xm_channel_context_t*);
+static void jar_xm_vibrato(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t);
+static void jar_xm_tremolo(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t);
+static void jar_xm_arpeggio(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t);
+static void jar_xm_tone_portamento(jar_xm_context_t*, jar_xm_channel_context_t*);
+static void jar_xm_pitch_slide(jar_xm_context_t*, jar_xm_channel_context_t*, float);
+static void jar_xm_panning_slide(jar_xm_channel_context_t*, uint8_t);
+static void jar_xm_volume_slide(jar_xm_channel_context_t*, uint8_t);
+
+static float jar_xm_envelope_lerp(jar_xm_envelope_point_t*, jar_xm_envelope_point_t*, uint16_t);
+static void jar_xm_envelope_tick(jar_xm_channel_context_t*, jar_xm_envelope_t*, uint16_t*, float*);
+static void jar_xm_envelopes(jar_xm_channel_context_t*);
+
+static float jar_xm_linear_period(float);
+static float jar_xm_linear_frequency(float);
+static float jar_xm_amiga_period(float);
+static float jar_xm_amiga_frequency(float);
+static float jar_xm_period(jar_xm_context_t*, float);
+static float jar_xm_frequency(jar_xm_context_t*, float, float);
+static void jar_xm_update_frequency(jar_xm_context_t*, jar_xm_channel_context_t*);
+
+static void jar_xm_handle_note_and_instrument(jar_xm_context_t*, jar_xm_channel_context_t*, jar_xm_pattern_slot_t*);
+static void jar_xm_trigger_note(jar_xm_context_t*, jar_xm_channel_context_t*, unsigned int flags);
+static void jar_xm_cut_note(jar_xm_channel_context_t*);
+static void jar_xm_key_off(jar_xm_channel_context_t*);
+
+static void jar_xm_post_pattern_change(jar_xm_context_t*);
+static void jar_xm_row(jar_xm_context_t*);
+static void jar_xm_tick(jar_xm_context_t*);
+
+static float jar_xm_next_of_sample(jar_xm_channel_context_t*);
+static void jar_xm_sample(jar_xm_context_t*, float*, float*);
+
+/* ----- Other oddities ----- */
+
+#define jar_xm_TRIGGER_KEEP_VOLUME (1 << 0)
+#define jar_xm_TRIGGER_KEEP_PERIOD (1 << 1)
+#define jar_xm_TRIGGER_KEEP_SAMPLE_POSITION (1 << 2)
+
+static const uint16_t amiga_frequencies[] = {
+ 1712, 1616, 1525, 1440, /* C-2, C#2, D-2, D#2 */
+ 1357, 1281, 1209, 1141, /* E-2, F-2, F#2, G-2 */
+ 1077, 1017, 961, 907, /* G#2, A-2, A#2, B-2 */
+ 856, /* C-3 */
+};
+
+static const float multi_retrig_add[] = {
+ 0.f, -1.f, -2.f, -4.f, /* 0, 1, 2, 3 */
+ -8.f, -16.f, 0.f, 0.f, /* 4, 5, 6, 7 */
+ 0.f, 1.f, 2.f, 4.f, /* 8, 9, A, B */
+ 8.f, 16.f, 0.f, 0.f /* C, D, E, F */
+};
+
+static const float multi_retrig_multiply[] = {
+ 1.f, 1.f, 1.f, 1.f, /* 0, 1, 2, 3 */
+ 1.f, 1.f, .6666667f, .5f, /* 4, 5, 6, 7 */
+ 1.f, 1.f, 1.f, 1.f, /* 8, 9, A, B */
+ 1.f, 1.f, 1.5f, 2.f /* C, D, E, F */
+};
+
+#define jar_xm_CLAMP_UP1F(vol, limit) do { \
+ if((vol) > (limit)) (vol) = (limit); \
+ } while(0)
+#define jar_xm_CLAMP_UP(vol) jar_xm_CLAMP_UP1F((vol), 1.f)
+
+#define jar_xm_CLAMP_DOWN1F(vol, limit) do { \
+ if((vol) < (limit)) (vol) = (limit); \
+ } while(0)
+#define jar_xm_CLAMP_DOWN(vol) jar_xm_CLAMP_DOWN1F((vol), .0f)
+
+#define jar_xm_CLAMP2F(vol, up, down) do { \
+ if((vol) > (up)) (vol) = (up); \
+ else if((vol) < (down)) (vol) = (down); \
+ } while(0)
+#define jar_xm_CLAMP(vol) jar_xm_CLAMP2F((vol), 1.f, .0f)
+
+#define jar_xm_SLIDE_TOWARDS(val, goal, incr) do { \
+ if((val) > (goal)) { \
+ (val) -= (incr); \
+ jar_xm_CLAMP_DOWN1F((val), (goal)); \
+ } else if((val) < (goal)) { \
+ (val) += (incr); \
+ jar_xm_CLAMP_UP1F((val), (goal)); \
+ } \
+ } while(0)
+
+#define jar_xm_LERP(u, v, t) ((u) + (t) * ((v) - (u)))
+#define jar_xm_INVERSE_LERP(u, v, lerp) (((lerp) - (u)) / ((v) - (u)))
+
+#define HAS_TONE_PORTAMENTO(s) ((s)->effect_type == 3 \
+ || (s)->effect_type == 5 \
+ || ((s)->volume_column >> 4) == 0xF)
+#define HAS_ARPEGGIO(s) ((s)->effect_type == 0 \
+ && (s)->effect_param != 0)
+#define HAS_VIBRATO(s) ((s)->effect_type == 4 \
+ || (s)->effect_param == 6 \
+ || ((s)->volume_column >> 4) == 0xB)
+#define NOTE_IS_VALID(n) ((n) > 0 && (n) < 97)
+
+/* ----- Function definitions ----- */
+
+static float jar_xm_waveform(jar_xm_waveform_type_t waveform, uint8_t step) {
+ static unsigned int next_rand = 24492;
+ step %= 0x40;
+
+ switch(waveform) {
+
+ case jar_xm_SINE_WAVEFORM:
+ /* Why not use a table? For saving space, and because there's
+ * very very little actual performance gain. */
+ return -sinf(2.f * 3.141592f * (float)step / (float)0x40);
+
+ case jar_xm_RAMP_DOWN_WAVEFORM:
+ /* Ramp down: 1.0f when step = 0; -1.0f when step = 0x40 */
+ return (float)(0x20 - step) / 0x20;
+
+ case jar_xm_SQUARE_WAVEFORM:
+ /* Square with a 50% duty */
+ return (step >= 0x20) ? 1.f : -1.f;
+
+ case jar_xm_RANDOM_WAVEFORM:
+ /* Use the POSIX.1-2001 example, just to be deterministic
+ * across different machines */
+ next_rand = next_rand * 1103515245 + 12345;
+ return (float)((next_rand >> 16) & 0x7FFF) / (float)0x4000 - 1.f;
+
+ case jar_xm_RAMP_UP_WAVEFORM:
+ /* Ramp up: -1.f when step = 0; 1.f when step = 0x40 */
+ return (float)(step - 0x20) / 0x20;
+
+ default:
+ break;
+
+ }
+
+ return .0f;
+}
+
+static void jar_xm_autovibrato(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) {
+ if(ch->instrument == NULL || ch->instrument->vibrato_depth == 0) return;
+ jar_xm_instrument_t* instr = ch->instrument;
+ float sweep = 1.f;
+
+ if(ch->autovibrato_ticks < instr->vibrato_sweep) {
+ /* No idea if this is correct, but it sounds close enough… */
+ sweep = jar_xm_LERP(0.f, 1.f, (float)ch->autovibrato_ticks / (float)instr->vibrato_sweep);
+ }
+
+ unsigned int step = ((ch->autovibrato_ticks++) * instr->vibrato_rate) >> 2;
+ ch->autovibrato_note_offset = .25f * jar_xm_waveform(instr->vibrato_type, step)
+ * (float)instr->vibrato_depth / (float)0xF * sweep;
+ jar_xm_update_frequency(ctx, ch);
+}
+
+static void jar_xm_vibrato(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t pos) {
+ unsigned int step = pos * (param >> 4);
+ ch->vibrato_note_offset =
+ 2.f
+ * jar_xm_waveform(ch->vibrato_waveform, step)
+ * (float)(param & 0x0F) / (float)0xF;
+ jar_xm_update_frequency(ctx, ch);
+}
+
+static void jar_xm_tremolo(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t pos) {
+ unsigned int step = pos * (param >> 4);
+ /* Not so sure about this, it sounds correct by ear compared with
+ * MilkyTracker, but it could come from other bugs */
+ ch->tremolo_volume = -1.f * jar_xm_waveform(ch->tremolo_waveform, step)
+ * (float)(param & 0x0F) / (float)0xF;
+}
+
+static void jar_xm_arpeggio(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t tick) {
+ switch(tick % 3) {
+ case 0:
+ ch->arp_in_progress = false;
+ ch->arp_note_offset = 0;
+ break;
+ case 2:
+ ch->arp_in_progress = true;
+ ch->arp_note_offset = param >> 4;
+ break;
+ case 1:
+ ch->arp_in_progress = true;
+ ch->arp_note_offset = param & 0x0F;
+ break;
+ }
+
+ jar_xm_update_frequency(ctx, ch);
+}
+
+static void jar_xm_tone_portamento(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) {
+ /* 3xx called without a note, wait until we get an actual
+ * target note. */
+ if(ch->tone_portamento_target_period == 0.f) return;
+
+ if(ch->period != ch->tone_portamento_target_period) {
+ jar_xm_SLIDE_TOWARDS(ch->period,
+ ch->tone_portamento_target_period,
+ (ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES ?
+ 4.f : 1.f) * ch->tone_portamento_param
+ );
+ jar_xm_update_frequency(ctx, ch);
+ }
+}
+
+static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) {
+ /* Don't ask about the 4.f coefficient. I found mention of it
+ * nowhere. Found by earâ„¢. */
+ if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {
+ period_offset *= 4.f;
+ }
+
+ ch->period += period_offset;
+ jar_xm_CLAMP_DOWN(ch->period);
+ /* XXX: upper bound of period ? */
+
+ jar_xm_update_frequency(ctx, ch);
+}
+
+static void jar_xm_panning_slide(jar_xm_channel_context_t* ch, uint8_t rawval) {
+ float f;
+
+ if((rawval & 0xF0) && (rawval & 0x0F)) {
+ /* Illegal state */
+ return;
+ }
+
+ if(rawval & 0xF0) {
+ /* Slide right */
+ f = (float)(rawval >> 4) / (float)0xFF;
+ ch->panning += f;
+ jar_xm_CLAMP_UP(ch->panning);
+ } else {
+ /* Slide left */
+ f = (float)(rawval & 0x0F) / (float)0xFF;
+ ch->panning -= f;
+ jar_xm_CLAMP_DOWN(ch->panning);
+ }
+}
+
+static void jar_xm_volume_slide(jar_xm_channel_context_t* ch, uint8_t rawval) {
+ float f;
+
+ if((rawval & 0xF0) && (rawval & 0x0F)) {
+ /* Illegal state */
+ return;
+ }
+
+ if(rawval & 0xF0) {
+ /* Slide up */
+ f = (float)(rawval >> 4) / (float)0x40;
+ ch->volume += f;
+ jar_xm_CLAMP_UP(ch->volume);
+ } else {
+ /* Slide down */
+ f = (float)(rawval & 0x0F) / (float)0x40;
+ ch->volume -= f;
+ jar_xm_CLAMP_DOWN(ch->volume);
+ }
+}
+
+static float jar_xm_envelope_lerp(jar_xm_envelope_point_t* restrict a, jar_xm_envelope_point_t* restrict b, uint16_t pos) {
+ /* Linear interpolation between two envelope points */
+ if(pos <= a->frame) return a->value;
+ else if(pos >= b->frame) return b->value;
+ else {
+ float p = (float)(pos - a->frame) / (float)(b->frame - a->frame);
+ return a->value * (1 - p) + b->value * p;
+ }
+}
+
+static void jar_xm_post_pattern_change(jar_xm_context_t* ctx) {
+ /* Loop if necessary */
+ if(ctx->current_table_index >= ctx->module.length) {
+ ctx->current_table_index = ctx->module.restart_position;
+ }
+}
+
+static float jar_xm_linear_period(float note) {
+ return 7680.f - note * 64.f;
+}
+
+static float jar_xm_linear_frequency(float period) {
+ return 8363.f * powf(2.f, (4608.f - period) / 768.f);
+}
+
+static float jar_xm_amiga_period(float note) {
+ unsigned int intnote = note;
+ uint8_t a = intnote % 12;
+ int8_t octave = note / 12.f - 2;
+ uint16_t p1 = amiga_frequencies[a], p2 = amiga_frequencies[a + 1];
+
+ if(octave > 0) {
+ p1 >>= octave;
+ p2 >>= octave;
+ } else if(octave < 0) {
+ p1 <<= (-octave);
+ p2 <<= (-octave);
+ }
+
+ return jar_xm_LERP(p1, p2, note - intnote);
+}
+
+static float jar_xm_amiga_frequency(float period) {
+ if(period == .0f) return .0f;
+
+ /* This is the PAL value. No reason to choose this one over the
+ * NTSC value. */
+ return 7093789.2f / (period * 2.f);
+}
+
+static float jar_xm_period(jar_xm_context_t* ctx, float note) {
+ switch(ctx->module.frequency_type) {
+ case jar_xm_LINEAR_FREQUENCIES:
+ return jar_xm_linear_period(note);
+ case jar_xm_AMIGA_FREQUENCIES:
+ return jar_xm_amiga_period(note);
+ }
+ return .0f;
+}
+
+static float jar_xm_frequency(jar_xm_context_t* ctx, float period, float note_offset) {
+ uint8_t a;
+ int8_t octave;
+ float note;
+ uint16_t p1, p2;
+
+ switch(ctx->module.frequency_type) {
+
+ case jar_xm_LINEAR_FREQUENCIES:
+ return jar_xm_linear_frequency(period - 64.f * note_offset);
+
+ case jar_xm_AMIGA_FREQUENCIES:
+ if(note_offset == 0) {
+ /* A chance to escape from insanity */
+ return jar_xm_amiga_frequency(period);
+ }
+
+ /* FIXME: this is very crappy at best */
+ a = octave = 0;
+
+ /* Find the octave of the current period */
+ if(period > amiga_frequencies[0]) {
+ --octave;
+ while(period > (amiga_frequencies[0] << (-octave))) --octave;
+ } else if(period < amiga_frequencies[12]) {
+ ++octave;
+ while(period < (amiga_frequencies[12] >> octave)) ++octave;
+ }
+
+ /* Find the smallest note closest to the current period */
+ for(uint8_t i = 0; i < 12; ++i) {
+ p1 = amiga_frequencies[i], p2 = amiga_frequencies[i + 1];
+
+ if(octave > 0) {
+ p1 >>= octave;
+ p2 >>= octave;
+ } else if(octave < 0) {
+ p1 <<= (-octave);
+ p2 <<= (-octave);
+ }
+
+ if(p2 <= period && period <= p1) {
+ a = i;
+ break;
+ }
+ }
+
+ if(JAR_XM_DEBUG && (p1 < period || p2 > period)) {
+ DEBUG("%i <= %f <= %i should hold but doesn't, this is a bug", p2, period, p1);
+ }
+
+ note = 12.f * (octave + 2) + a + jar_xm_INVERSE_LERP(p1, p2, period);
+
+ return jar_xm_amiga_frequency(jar_xm_amiga_period(note + note_offset));
+
+ }
+
+ return .0f;
+}
+
+static void jar_xm_update_frequency(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) {
+ ch->frequency = jar_xm_frequency(
+ ctx, ch->period,
+ (ch->arp_note_offset > 0 ? ch->arp_note_offset : (
+ ch->vibrato_note_offset + ch->autovibrato_note_offset
+ ))
+ );
+ ch->step = ch->frequency / ctx->rate;
+}
+
+static void jar_xm_handle_note_and_instrument(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch,
+ jar_xm_pattern_slot_t* s) {
+ if(s->instrument > 0) {
+ if(HAS_TONE_PORTAMENTO(ch->current) && ch->instrument != NULL && ch->sample != NULL) {
+ /* Tone portamento in effect, unclear stuff happens */
+ jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_PERIOD | jar_xm_TRIGGER_KEEP_SAMPLE_POSITION);
+ } else if(s->instrument > ctx->module.num_instruments) {
+ /* Invalid instrument, Cut current note */
+ jar_xm_cut_note(ch);
+ ch->instrument = NULL;
+ ch->sample = NULL;
+ } else {
+ ch->instrument = ctx->module.instruments + (s->instrument - 1);
+ if(s->note == 0 && ch->sample != NULL) {
+ /* Ghost instrument, trigger note */
+ /* Sample position is kept, but envelopes are reset */
+ jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_SAMPLE_POSITION);
+ }
+ }
+ }
+
+ if(NOTE_IS_VALID(s->note)) {
+ /* Yes, the real note number is s->note -1. Try finding
+ * THAT in any of the specs! :-) */
+
+ jar_xm_instrument_t* instr = ch->instrument;
+
+ if(HAS_TONE_PORTAMENTO(ch->current) && instr != NULL && ch->sample != NULL) {
+ /* Tone portamento in effect */
+ ch->note = s->note + ch->sample->relative_note + ch->sample->finetune / 128.f - 1.f;
+ ch->tone_portamento_target_period = jar_xm_period(ctx, ch->note);
+ } else if(instr == NULL || ch->instrument->num_samples == 0) {
+ /* Bad instrument */
+ jar_xm_cut_note(ch);
+ } else {
+ if(instr->sample_of_notes[s->note - 1] < instr->num_samples) {
+#if JAR_XM_RAMPING
+ for(unsigned int z = 0; z < jar_xm_SAMPLE_RAMPING_POINTS; ++z) {
+ ch->end_of_previous_sample[z] = jar_xm_next_of_sample(ch);
+ }
+ ch->frame_count = 0;
+#endif
+ ch->sample = instr->samples + instr->sample_of_notes[s->note - 1];
+ ch->orig_note = ch->note = s->note + ch->sample->relative_note
+ + ch->sample->finetune / 128.f - 1.f;
+ if(s->instrument > 0) {
+ jar_xm_trigger_note(ctx, ch, 0);
+ } else {
+ /* Ghost note: keep old volume */
+ jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_VOLUME);
+ }
+ } else {
+ /* Bad sample */
+ jar_xm_cut_note(ch);
+ }
+ }
+ } else if(s->note == 97) {
+ /* Key Off */
+ jar_xm_key_off(ch);
+ }
+
+ switch(s->volume_column >> 4) {
+
+ case 0x5:
+ if(s->volume_column > 0x50) break;
+ case 0x1:
+ case 0x2:
+ case 0x3:
+ case 0x4:
+ /* Set volume */
+ ch->volume = (float)(s->volume_column - 0x10) / (float)0x40;
+ break;
+
+ case 0x8: /* Fine volume slide down */
+ jar_xm_volume_slide(ch, s->volume_column & 0x0F);
+ break;
+
+ case 0x9: /* Fine volume slide up */
+ jar_xm_volume_slide(ch, s->volume_column << 4);
+ break;
+
+ case 0xA: /* Set vibrato speed */
+ ch->vibrato_param = (ch->vibrato_param & 0x0F) | ((s->volume_column & 0x0F) << 4);
+ break;
+
+ case 0xC: /* Set panning */
+ ch->panning = (float)(
+ ((s->volume_column & 0x0F) << 4) | (s->volume_column & 0x0F)
+ ) / (float)0xFF;
+ break;
+
+ case 0xF: /* Tone portamento */
+ if(s->volume_column & 0x0F) {
+ ch->tone_portamento_param = ((s->volume_column & 0x0F) << 4)
+ | (s->volume_column & 0x0F);
+ }
+ break;
+
+ default:
+ break;
+
+ }
+
+ switch(s->effect_type) {
+
+ case 1: /* 1xx: Portamento up */
+ if(s->effect_param > 0) {
+ ch->portamento_up_param = s->effect_param;
+ }
+ break;
+
+ case 2: /* 2xx: Portamento down */
+ if(s->effect_param > 0) {
+ ch->portamento_down_param = s->effect_param;
+ }
+ break;
+
+ case 3: /* 3xx: Tone portamento */
+ if(s->effect_param > 0) {
+ ch->tone_portamento_param = s->effect_param;
+ }
+ break;
+
+ case 4: /* 4xy: Vibrato */
+ if(s->effect_param & 0x0F) {
+ /* Set vibrato depth */
+ ch->vibrato_param = (ch->vibrato_param & 0xF0) | (s->effect_param & 0x0F);
+ }
+ if(s->effect_param >> 4) {
+ /* Set vibrato speed */
+ ch->vibrato_param = (s->effect_param & 0xF0) | (ch->vibrato_param & 0x0F);
+ }
+ break;
+
+ case 5: /* 5xy: Tone portamento + Volume slide */
+ if(s->effect_param > 0) {
+ ch->volume_slide_param = s->effect_param;
+ }
+ break;
+
+ case 6: /* 6xy: Vibrato + Volume slide */
+ if(s->effect_param > 0) {
+ ch->volume_slide_param = s->effect_param;
+ }
+ break;
+
+ case 7: /* 7xy: Tremolo */
+ if(s->effect_param & 0x0F) {
+ /* Set tremolo depth */
+ ch->tremolo_param = (ch->tremolo_param & 0xF0) | (s->effect_param & 0x0F);
+ }
+ if(s->effect_param >> 4) {
+ /* Set tremolo speed */
+ ch->tremolo_param = (s->effect_param & 0xF0) | (ch->tremolo_param & 0x0F);
+ }
+ break;
+
+ case 8: /* 8xx: Set panning */
+ ch->panning = (float)s->effect_param / (float)0xFF;
+ break;
+
+ case 9: /* 9xx: Sample offset */
+ if(ch->sample != NULL && NOTE_IS_VALID(s->note)) {
+ uint32_t final_offset = s->effect_param << (ch->sample->bits == 16 ? 7 : 8);
+ if(final_offset >= ch->sample->length) {
+ /* Pretend the sample dosen't loop and is done playing */
+ ch->sample_position = -1;
+ break;
+ }
+ ch->sample_position = final_offset;
+ }
+ break;
+
+ case 0xA: /* Axy: Volume slide */
+ if(s->effect_param > 0) {
+ ch->volume_slide_param = s->effect_param;
+ }
+ break;
+
+ case 0xB: /* Bxx: Position jump */
+ if(s->effect_param < ctx->module.length) {
+ ctx->position_jump = true;
+ ctx->jump_dest = s->effect_param;
+ }
+ break;
+
+ case 0xC: /* Cxx: Set volume */
+ ch->volume = (float)((s->effect_param > 0x40)
+ ? 0x40 : s->effect_param) / (float)0x40;
+ break;
+
+ case 0xD: /* Dxx: Pattern break */
+ /* Jump after playing this line */
+ ctx->pattern_break = true;
+ ctx->jump_row = (s->effect_param >> 4) * 10 + (s->effect_param & 0x0F);
+ break;
+
+ case 0xE: /* EXy: Extended command */
+ switch(s->effect_param >> 4) {
+
+ case 1: /* E1y: Fine portamento up */
+ if(s->effect_param & 0x0F) {
+ ch->fine_portamento_up_param = s->effect_param & 0x0F;
+ }
+ jar_xm_pitch_slide(ctx, ch, -ch->fine_portamento_up_param);
+ break;
+
+ case 2: /* E2y: Fine portamento down */
+ if(s->effect_param & 0x0F) {
+ ch->fine_portamento_down_param = s->effect_param & 0x0F;
+ }
+ jar_xm_pitch_slide(ctx, ch, ch->fine_portamento_down_param);
+ break;
+
+ case 4: /* E4y: Set vibrato control */
+ ch->vibrato_waveform = s->effect_param & 3;
+ ch->vibrato_waveform_retrigger = !((s->effect_param >> 2) & 1);
+ break;
+
+ case 5: /* E5y: Set finetune */
+ if(NOTE_IS_VALID(ch->current->note) && ch->sample != NULL) {
+ ch->note = ch->current->note + ch->sample->relative_note +
+ (float)(((s->effect_param & 0x0F) - 8) << 4) / 128.f - 1.f;
+ ch->period = jar_xm_period(ctx, ch->note);
+ jar_xm_update_frequency(ctx, ch);
+ }
+ break;
+
+ case 6: /* E6y: Pattern loop */
+ if(s->effect_param & 0x0F) {
+ if((s->effect_param & 0x0F) == ch->pattern_loop_count) {
+ /* Loop is over */
+ ch->pattern_loop_count = 0;
+ break;
+ }
+
+ /* Jump to the beginning of the loop */
+ ch->pattern_loop_count++;
+ ctx->position_jump = true;
+ ctx->jump_row = ch->pattern_loop_origin;
+ ctx->jump_dest = ctx->current_table_index;
+ } else {
+ /* Set loop start point */
+ ch->pattern_loop_origin = ctx->current_row;
+ /* Replicate FT2 E60 bug */
+ ctx->jump_row = ch->pattern_loop_origin;
+ }
+ break;
+
+ case 7: /* E7y: Set tremolo control */
+ ch->tremolo_waveform = s->effect_param & 3;
+ ch->tremolo_waveform_retrigger = !((s->effect_param >> 2) & 1);
+ break;
+
+ case 0xA: /* EAy: Fine volume slide up */
+ if(s->effect_param & 0x0F) {
+ ch->fine_volume_slide_param = s->effect_param & 0x0F;
+ }
+ jar_xm_volume_slide(ch, ch->fine_volume_slide_param << 4);
+ break;
+
+ case 0xB: /* EBy: Fine volume slide down */
+ if(s->effect_param & 0x0F) {
+ ch->fine_volume_slide_param = s->effect_param & 0x0F;
+ }
+ jar_xm_volume_slide(ch, ch->fine_volume_slide_param);
+ break;
+
+ case 0xD: /* EDy: Note delay */
+ /* XXX: figure this out better. EDx triggers
+ * the note even when there no note and no
+ * instrument. But ED0 acts like like a ghost
+ * note, EDx (x ≠ 0) does not. */
+ if(s->note == 0 && s->instrument == 0) {
+ unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME;
+
+ if(ch->current->effect_param & 0x0F) {
+ ch->note = ch->orig_note;
+ jar_xm_trigger_note(ctx, ch, flags);
+ } else {
+ jar_xm_trigger_note(
+ ctx, ch,
+ flags
+ | jar_xm_TRIGGER_KEEP_PERIOD
+ | jar_xm_TRIGGER_KEEP_SAMPLE_POSITION
+ );
+ }
+ }
+ break;
+
+ case 0xE: /* EEy: Pattern delay */
+ ctx->extra_ticks = (ch->current->effect_param & 0x0F) * ctx->tempo;
+ break;
+
+ default:
+ break;
+
+ }
+ break;
+
+ case 0xF: /* Fxx: Set tempo/BPM */
+ if(s->effect_param > 0) {
+ if(s->effect_param <= 0x1F) {
+ ctx->tempo = s->effect_param;
+ } else {
+ ctx->bpm = s->effect_param;
+ }
+ }
+ break;
+
+ case 16: /* Gxx: Set global volume */
+ ctx->global_volume = (float)((s->effect_param > 0x40)
+ ? 0x40 : s->effect_param) / (float)0x40;
+ break;
+
+ case 17: /* Hxy: Global volume slide */
+ if(s->effect_param > 0) {
+ ch->global_volume_slide_param = s->effect_param;
+ }
+ break;
+
+ case 21: /* Lxx: Set envelope position */
+ ch->volume_envelope_frame_count = s->effect_param;
+ ch->panning_envelope_frame_count = s->effect_param;
+ break;
+
+ case 25: /* Pxy: Panning slide */
+ if(s->effect_param > 0) {
+ ch->panning_slide_param = s->effect_param;
+ }
+ break;
+
+ case 27: /* Rxy: Multi retrig note */
+ if(s->effect_param > 0) {
+ if((s->effect_param >> 4) == 0) {
+ /* Keep previous x value */
+ ch->multi_retrig_param = (ch->multi_retrig_param & 0xF0) | (s->effect_param & 0x0F);
+ } else {
+ ch->multi_retrig_param = s->effect_param;
+ }
+ }
+ break;
+
+ case 29: /* Txy: Tremor */
+ if(s->effect_param > 0) {
+ /* Tremor x and y params do not appear to be separately
+ * kept in memory, unlike Rxy */
+ ch->tremor_param = s->effect_param;
+ }
+ break;
+
+ case 33: /* Xxy: Extra stuff */
+ switch(s->effect_param >> 4) {
+
+ case 1: /* X1y: Extra fine portamento up */
+ if(s->effect_param & 0x0F) {
+ ch->extra_fine_portamento_up_param = s->effect_param & 0x0F;
+ }
+ jar_xm_pitch_slide(ctx, ch, -1.0f * ch->extra_fine_portamento_up_param);
+ break;
+
+ case 2: /* X2y: Extra fine portamento down */
+ if(s->effect_param & 0x0F) {
+ ch->extra_fine_portamento_down_param = s->effect_param & 0x0F;
+ }
+ jar_xm_pitch_slide(ctx, ch, ch->extra_fine_portamento_down_param);
+ break;
+
+ default:
+ break;
+
+ }
+ break;
+
+ default:
+ break;
+
+ }
+}
+
+static void jar_xm_trigger_note(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, unsigned int flags) {
+ if(!(flags & jar_xm_TRIGGER_KEEP_SAMPLE_POSITION)) {
+ ch->sample_position = 0.f;
+ ch->ping = true;
+ }
+
+ if(ch->sample != NULL) {
+ if(!(flags & jar_xm_TRIGGER_KEEP_VOLUME)) {
+ ch->volume = ch->sample->volume;
+ }
+
+ ch->panning = ch->sample->panning;
+ }
+
+ ch->sustained = true;
+ ch->fadeout_volume = ch->volume_envelope_volume = 1.0f;
+ ch->panning_envelope_panning = .5f;
+ ch->volume_envelope_frame_count = ch->panning_envelope_frame_count = 0;
+ ch->vibrato_note_offset = 0.f;
+ ch->tremolo_volume = 0.f;
+ ch->tremor_on = false;
+
+ ch->autovibrato_ticks = 0;
+
+ if(ch->vibrato_waveform_retrigger) {
+ ch->vibrato_ticks = 0; /* XXX: should the waveform itself also
+ * be reset to sine? */
+ }
+ if(ch->tremolo_waveform_retrigger) {
+ ch->tremolo_ticks = 0;
+ }
+
+ if(!(flags & jar_xm_TRIGGER_KEEP_PERIOD)) {
+ ch->period = jar_xm_period(ctx, ch->note);
+ jar_xm_update_frequency(ctx, ch);
+ }
+
+ ch->latest_trigger = ctx->generated_samples;
+ if(ch->instrument != NULL) {
+ ch->instrument->latest_trigger = ctx->generated_samples;
+ }
+ if(ch->sample != NULL) {
+ ch->sample->latest_trigger = ctx->generated_samples;
+ }
+}
+
+static void jar_xm_cut_note(jar_xm_channel_context_t* ch) {
+ /* NB: this is not the same as Key Off */
+ ch->volume = .0f;
+}
+
+static void jar_xm_key_off(jar_xm_channel_context_t* ch) {
+ /* Key Off */
+ ch->sustained = false;
+
+ /* If no volume envelope is used, also cut the note */
+ if(ch->instrument == NULL || !ch->instrument->volume_envelope.enabled) {
+ jar_xm_cut_note(ch);
+ }
+}
+
+static void jar_xm_row(jar_xm_context_t* ctx) {
+ if(ctx->position_jump) {
+ ctx->current_table_index = ctx->jump_dest;
+ ctx->current_row = ctx->jump_row;
+ ctx->position_jump = false;
+ ctx->pattern_break = false;
+ ctx->jump_row = 0;
+ jar_xm_post_pattern_change(ctx);
+ } else if(ctx->pattern_break) {
+ ctx->current_table_index++;
+ ctx->current_row = ctx->jump_row;
+ ctx->pattern_break = false;
+ ctx->jump_row = 0;
+ jar_xm_post_pattern_change(ctx);
+ }
+
+ jar_xm_pattern_t* cur = ctx->module.patterns + ctx->module.pattern_table[ctx->current_table_index];
+ bool in_a_loop = false;
+
+ /* Read notes… */
+ for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
+ jar_xm_pattern_slot_t* s = cur->slots + ctx->current_row * ctx->module.num_channels + i;
+ jar_xm_channel_context_t* ch = ctx->channels + i;
+
+ ch->current = s;
+
+ if(s->effect_type != 0xE || s->effect_param >> 4 != 0xD) {
+ jar_xm_handle_note_and_instrument(ctx, ch, s);
+ } else {
+ ch->note_delay_param = s->effect_param & 0x0F;
+ }
+
+ if(!in_a_loop && ch->pattern_loop_count > 0) {
+ in_a_loop = true;
+ }
+ }
+
+ if(!in_a_loop) {
+ /* No E6y loop is in effect (or we are in the first pass) */
+ ctx->loop_count = (ctx->row_loop_count[MAX_NUM_ROWS * ctx->current_table_index + ctx->current_row]++);
+ }
+
+ ctx->current_row++; /* Since this is an uint8, this line can
+ * increment from 255 to 0, in which case it
+ * is still necessary to go the next
+ * pattern. */
+ if(!ctx->position_jump && !ctx->pattern_break &&
+ (ctx->current_row >= cur->num_rows || ctx->current_row == 0)) {
+ ctx->current_table_index++;
+ ctx->current_row = ctx->jump_row; /* This will be 0 most of
+ * the time, except when E60
+ * is used */
+ ctx->jump_row = 0;
+ jar_xm_post_pattern_change(ctx);
+ }
+}
+
+static void jar_xm_envelope_tick(jar_xm_channel_context_t* ch,
+ jar_xm_envelope_t* env,
+ uint16_t* counter,
+ float* outval) {
+ if(env->num_points < 2) {
+ /* Don't really know what to do… */
+ if(env->num_points == 1) {
+ /* XXX I am pulling this out of my ass */
+ *outval = (float)env->points[0].value / (float)0x40;
+ if(*outval > 1) {
+ *outval = 1;
+ }
+ }
+
+ return;
+ } else {
+ uint8_t j;
+
+ if(env->loop_enabled) {
+ uint16_t loop_start = env->points[env->loop_start_point].frame;
+ uint16_t loop_end = env->points[env->loop_end_point].frame;
+ uint16_t loop_length = loop_end - loop_start;
+
+ if(*counter >= loop_end) {
+ *counter -= loop_length;
+ }
+ }
+
+ for(j = 0; j < (env->num_points - 2); ++j) {
+ if(env->points[j].frame <= *counter &&
+ env->points[j+1].frame >= *counter) {
+ break;
+ }
+ }
+
+ *outval = jar_xm_envelope_lerp(env->points + j, env->points + j + 1, *counter) / (float)0x40;
+
+ /* Make sure it is safe to increment frame count */
+ if(!ch->sustained || !env->sustain_enabled ||
+ *counter != env->points[env->sustain_point].frame) {
+ (*counter)++;
+ }
+ }
+}
+
+static void jar_xm_envelopes(jar_xm_channel_context_t* ch) {
+ if(ch->instrument != NULL) {
+ if(ch->instrument->volume_envelope.enabled) {
+ if(!ch->sustained) {
+ ch->fadeout_volume -= (float)ch->instrument->volume_fadeout / 65536.f;
+ jar_xm_CLAMP_DOWN(ch->fadeout_volume);
+ }
+
+ jar_xm_envelope_tick(ch,
+ &(ch->instrument->volume_envelope),
+ &(ch->volume_envelope_frame_count),
+ &(ch->volume_envelope_volume));
+ }
+
+ if(ch->instrument->panning_envelope.enabled) {
+ jar_xm_envelope_tick(ch,
+ &(ch->instrument->panning_envelope),
+ &(ch->panning_envelope_frame_count),
+ &(ch->panning_envelope_panning));
+ }
+ }
+}
+
+static void jar_xm_tick(jar_xm_context_t* ctx) {
+ if(ctx->current_tick == 0) {
+ jar_xm_row(ctx);
+ }
+
+ for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
+ jar_xm_channel_context_t* ch = ctx->channels + i;
+
+ jar_xm_envelopes(ch);
+ jar_xm_autovibrato(ctx, ch);
+
+ if(ch->arp_in_progress && !HAS_ARPEGGIO(ch->current)) {
+ ch->arp_in_progress = false;
+ ch->arp_note_offset = 0;
+ jar_xm_update_frequency(ctx, ch);
+ }
+ if(ch->vibrato_in_progress && !HAS_VIBRATO(ch->current)) {
+ ch->vibrato_in_progress = false;
+ ch->vibrato_note_offset = 0.f;
+ jar_xm_update_frequency(ctx, ch);
+ }
+
+ switch(ch->current->volume_column >> 4) {
+
+ case 0x6: /* Volume slide down */
+ if(ctx->current_tick == 0) break;
+ jar_xm_volume_slide(ch, ch->current->volume_column & 0x0F);
+ break;
+
+ case 0x7: /* Volume slide up */
+ if(ctx->current_tick == 0) break;
+ jar_xm_volume_slide(ch, ch->current->volume_column << 4);
+ break;
+
+ case 0xB: /* Vibrato */
+ if(ctx->current_tick == 0) break;
+ ch->vibrato_in_progress = false;
+ jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++);
+ break;
+
+ case 0xD: /* Panning slide left */
+ if(ctx->current_tick == 0) break;
+ jar_xm_panning_slide(ch, ch->current->volume_column & 0x0F);
+ break;
+
+ case 0xE: /* Panning slide right */
+ if(ctx->current_tick == 0) break;
+ jar_xm_panning_slide(ch, ch->current->volume_column << 4);
+ break;
+
+ case 0xF: /* Tone portamento */
+ if(ctx->current_tick == 0) break;
+ jar_xm_tone_portamento(ctx, ch);
+ break;
+
+ default:
+ break;
+
+ }
+
+ switch(ch->current->effect_type) {
+
+ case 0: /* 0xy: Arpeggio */
+ if(ch->current->effect_param > 0) {
+ char arp_offset = ctx->tempo % 3;
+ switch(arp_offset) {
+ case 2: /* 0 -> x -> 0 -> y -> x -> … */
+ if(ctx->current_tick == 1) {
+ ch->arp_in_progress = true;
+ ch->arp_note_offset = ch->current->effect_param >> 4;
+ jar_xm_update_frequency(ctx, ch);
+ break;
+ }
+ /* No break here, this is intended */
+ case 1: /* 0 -> 0 -> y -> x -> … */
+ if(ctx->current_tick == 0) {
+ ch->arp_in_progress = false;
+ ch->arp_note_offset = 0;
+ jar_xm_update_frequency(ctx, ch);
+ break;
+ }
+ /* No break here, this is intended */
+ case 0: /* 0 -> y -> x -> … */
+ jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset);
+ default:
+ break;
+ }
+ }
+ break;
+
+ case 1: /* 1xx: Portamento up */
+ if(ctx->current_tick == 0) break;
+ jar_xm_pitch_slide(ctx, ch, -ch->portamento_up_param);
+ break;
+
+ case 2: /* 2xx: Portamento down */
+ if(ctx->current_tick == 0) break;
+ jar_xm_pitch_slide(ctx, ch, ch->portamento_down_param);
+ break;
+
+ case 3: /* 3xx: Tone portamento */
+ if(ctx->current_tick == 0) break;
+ jar_xm_tone_portamento(ctx, ch);
+ break;
+
+ case 4: /* 4xy: Vibrato */
+ if(ctx->current_tick == 0) break;
+ ch->vibrato_in_progress = true;
+ jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++);
+ break;
+
+ case 5: /* 5xy: Tone portamento + Volume slide */
+ if(ctx->current_tick == 0) break;
+ jar_xm_tone_portamento(ctx, ch);
+ jar_xm_volume_slide(ch, ch->volume_slide_param);
+ break;
+
+ case 6: /* 6xy: Vibrato + Volume slide */
+ if(ctx->current_tick == 0) break;
+ ch->vibrato_in_progress = true;
+ jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++);
+ jar_xm_volume_slide(ch, ch->volume_slide_param);
+ break;
+
+ case 7: /* 7xy: Tremolo */
+ if(ctx->current_tick == 0) break;
+ jar_xm_tremolo(ctx, ch, ch->tremolo_param, ch->tremolo_ticks++);
+ break;
+
+ case 0xA: /* Axy: Volume slide */
+ if(ctx->current_tick == 0) break;
+ jar_xm_volume_slide(ch, ch->volume_slide_param);
+ break;
+
+ case 0xE: /* EXy: Extended command */
+ switch(ch->current->effect_param >> 4) {
+
+ case 0x9: /* E9y: Retrigger note */
+ if(ctx->current_tick != 0 && ch->current->effect_param & 0x0F) {
+ if(!(ctx->current_tick % (ch->current->effect_param & 0x0F))) {
+ jar_xm_trigger_note(ctx, ch, 0);
+ jar_xm_envelopes(ch);
+ }
+ }
+ break;
+
+ case 0xC: /* ECy: Note cut */
+ if((ch->current->effect_param & 0x0F) == ctx->current_tick) {
+ jar_xm_cut_note(ch);
+ }
+ break;
+
+ case 0xD: /* EDy: Note delay */
+ if(ch->note_delay_param == ctx->current_tick) {
+ jar_xm_handle_note_and_instrument(ctx, ch, ch->current);
+ jar_xm_envelopes(ch);
+ }
+ break;
+
+ default:
+ break;
+
+ }
+ break;
+
+ case 17: /* Hxy: Global volume slide */
+ if(ctx->current_tick == 0) break;
+ if((ch->global_volume_slide_param & 0xF0) &&
+ (ch->global_volume_slide_param & 0x0F)) {
+ /* Illegal state */
+ break;
+ }
+ if(ch->global_volume_slide_param & 0xF0) {
+ /* Global slide up */
+ float f = (float)(ch->global_volume_slide_param >> 4) / (float)0x40;
+ ctx->global_volume += f;
+ jar_xm_CLAMP_UP(ctx->global_volume);
+ } else {
+ /* Global slide down */
+ float f = (float)(ch->global_volume_slide_param & 0x0F) / (float)0x40;
+ ctx->global_volume -= f;
+ jar_xm_CLAMP_DOWN(ctx->global_volume);
+ }
+ break;
+
+ case 20: /* Kxx: Key off */
+ /* Most documentations will tell you the parameter has no
+ * use. Don't be fooled. */
+ if(ctx->current_tick == ch->current->effect_param) {
+ jar_xm_key_off(ch);
+ }
+ break;
+
+ case 25: /* Pxy: Panning slide */
+ if(ctx->current_tick == 0) break;
+ jar_xm_panning_slide(ch, ch->panning_slide_param);
+ break;
+
+ case 27: /* Rxy: Multi retrig note */
+ if(ctx->current_tick == 0) break;
+ if(((ch->multi_retrig_param) & 0x0F) == 0) break;
+ if((ctx->current_tick % (ch->multi_retrig_param & 0x0F)) == 0) {
+ float v = ch->volume * multi_retrig_multiply[ch->multi_retrig_param >> 4]
+ + multi_retrig_add[ch->multi_retrig_param >> 4];
+ jar_xm_CLAMP(v);
+ jar_xm_trigger_note(ctx, ch, 0);
+ ch->volume = v;
+ }
+ break;
+
+ case 29: /* Txy: Tremor */
+ if(ctx->current_tick == 0) break;
+ ch->tremor_on = (
+ (ctx->current_tick - 1) % ((ch->tremor_param >> 4) + (ch->tremor_param & 0x0F) + 2)
+ >
+ (ch->tremor_param >> 4)
+ );
+ break;
+
+ default:
+ break;
+
+ }
+
+ float panning, volume;
+
+ panning = ch->panning +
+ (ch->panning_envelope_panning - .5f) * (.5f - fabsf(ch->panning - .5f)) * 2.0f;
+
+ if(ch->tremor_on) {
+ volume = .0f;
+ } else {
+ volume = ch->volume + ch->tremolo_volume;
+ jar_xm_CLAMP(volume);
+ volume *= ch->fadeout_volume * ch->volume_envelope_volume;
+ }
+
+#if JAR_XM_RAMPING
+ ch->target_panning = panning;
+ ch->target_volume = volume;
+#else
+ ch->actual_panning = panning;
+ ch->actual_volume = volume;
+#endif
+ }
+
+ ctx->current_tick++;
+ if(ctx->current_tick >= ctx->tempo + ctx->extra_ticks) {
+ ctx->current_tick = 0;
+ ctx->extra_ticks = 0;
+ }
+
+ /* FT2 manual says number of ticks / second = BPM * 0.4 */
+ ctx->remaining_samples_in_tick += (float)ctx->rate / ((float)ctx->bpm * 0.4f);
+}
+
+static float jar_xm_next_of_sample(jar_xm_channel_context_t* ch) {
+ if(ch->instrument == NULL || ch->sample == NULL || ch->sample_position < 0) {
+#if JAR_XM_RAMPING
+ if(ch->frame_count < jar_xm_SAMPLE_RAMPING_POINTS) {
+ return jar_xm_LERP(ch->end_of_previous_sample[ch->frame_count], .0f,
+ (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS);
+ }
+#endif
+ return .0f;
+ }
+ if(ch->sample->length == 0) {
+ return .0f;
+ }
+
+ float u, v, t;
+ uint32_t a, b;
+ a = (uint32_t)ch->sample_position; /* This cast is fine,
+ * sample_position will not
+ * go above integer
+ * ranges */
+ if(JAR_XM_LINEAR_INTERPOLATION) {
+ b = a + 1;
+ t = ch->sample_position - a; /* Cheaper than fmodf(., 1.f) */
+ }
+ u = ch->sample->data[a];
+
+ switch(ch->sample->loop_type) {
+
+ case jar_xm_NO_LOOP:
+ if(JAR_XM_LINEAR_INTERPOLATION) {
+ v = (b < ch->sample->length) ? ch->sample->data[b] : .0f;
+ }
+ ch->sample_position += ch->step;
+ if(ch->sample_position >= ch->sample->length) {
+ ch->sample_position = -1;
+ }
+ break;
+
+ case jar_xm_FORWARD_LOOP:
+ if(JAR_XM_LINEAR_INTERPOLATION) {
+ v = ch->sample->data[
+ (b == ch->sample->loop_end) ? ch->sample->loop_start : b
+ ];
+ }
+ ch->sample_position += ch->step;
+ while(ch->sample_position >= ch->sample->loop_end) {
+ ch->sample_position -= ch->sample->loop_length;
+ }
+ break;
+
+ case jar_xm_PING_PONG_LOOP:
+ if(ch->ping) {
+ ch->sample_position += ch->step;
+ } else {
+ ch->sample_position -= ch->step;
+ }
+ /* XXX: this may not work for very tight ping-pong loops
+ * (ie switches direction more than once per sample */
+ if(ch->ping) {
+ if(JAR_XM_LINEAR_INTERPOLATION) {
+ v = (b >= ch->sample->loop_end) ? ch->sample->data[a] : ch->sample->data[b];
+ }
+ if(ch->sample_position >= ch->sample->loop_end) {
+ ch->ping = false;
+ ch->sample_position = (ch->sample->loop_end << 1) - ch->sample_position;
+ }
+ /* sanity checking */
+ if(ch->sample_position >= ch->sample->length) {
+ ch->ping = false;
+ ch->sample_position -= ch->sample->length - 1;
+ }
+ } else {
+ if(JAR_XM_LINEAR_INTERPOLATION) {
+ v = u;
+ u = (b == 1 || b - 2 <= ch->sample->loop_start) ? ch->sample->data[a] : ch->sample->data[b - 2];
+ }
+ if(ch->sample_position <= ch->sample->loop_start) {
+ ch->ping = true;
+ ch->sample_position = (ch->sample->loop_start << 1) - ch->sample_position;
+ }
+ /* sanity checking */
+ if(ch->sample_position <= .0f) {
+ ch->ping = true;
+ ch->sample_position = .0f;
+ }
+ }
+ break;
+
+ default:
+ v = .0f;
+ break;
+ }
+
+ float endval = JAR_XM_LINEAR_INTERPOLATION ? jar_xm_LERP(u, v, t) : u;
+
+#if JAR_XM_RAMPING
+ if(ch->frame_count < jar_xm_SAMPLE_RAMPING_POINTS) {
+ /* Smoothly transition between old and new sample. */
+ return jar_xm_LERP(ch->end_of_previous_sample[ch->frame_count], endval,
+ (float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS);
+ }
+#endif
+
+ return endval;
+}
+
+static void jar_xm_sample(jar_xm_context_t* ctx, float* left, float* right) {
+ if(ctx->remaining_samples_in_tick <= 0) {
+ jar_xm_tick(ctx);
+ }
+ ctx->remaining_samples_in_tick--;
+
+ *left = 0.f;
+ *right = 0.f;
+
+ if(ctx->max_loop_count > 0 && ctx->loop_count >= ctx->max_loop_count) {
+ return;
+ }
+
+ for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
+ jar_xm_channel_context_t* ch = ctx->channels + i;
+
+ if(ch->instrument == NULL || ch->sample == NULL || ch->sample_position < 0) {
+ continue;
+ }
+
+ const float fval = jar_xm_next_of_sample(ch);
+
+ if(!ch->muted && !ch->instrument->muted) {
+ *left += fval * ch->actual_volume * (1.f - ch->actual_panning);
+ *right += fval * ch->actual_volume * ch->actual_panning;
+ }
+
+#if JAR_XM_RAMPING
+ ch->frame_count++;
+ jar_xm_SLIDE_TOWARDS(ch->actual_volume, ch->target_volume, ctx->volume_ramp);
+ jar_xm_SLIDE_TOWARDS(ch->actual_panning, ch->target_panning, ctx->panning_ramp);
+#endif
+ }
+
+ const float fgvol = ctx->global_volume * ctx->amplification;
+ *left *= fgvol;
+ *right *= fgvol;
+
+#if JAR_XM_DEBUG
+ if(fabs(*left) > 1 || fabs(*right) > 1) {
+ DEBUG("clipping frame: %f %f, this is a bad module or a libxm bug", *left, *right);
+ }
+#endif
+}
+
+void jar_xm_generate_samples(jar_xm_context_t* ctx, float* output, size_t numsamples) {
+ if(ctx && output) {
+ ctx->generated_samples += numsamples;
+ for(size_t i = 0; i < numsamples; i++) {
+ jar_xm_sample(ctx, output + (2 * i), output + (2 * i + 1));
+ }
+ }
+}
+
+uint64_t jar_xm_get_remaining_samples(jar_xm_context_t* ctx)
+{
+ uint64_t total = 0;
+ uint8_t currentLoopCount = jar_xm_get_loop_count(ctx);
+ jar_xm_set_max_loop_count(ctx, 0);
+
+ while(jar_xm_get_loop_count(ctx) == currentLoopCount)
+ {
+ total += ctx->remaining_samples_in_tick;
+ ctx->remaining_samples_in_tick = 0;
+ jar_xm_tick(ctx);
+ }
+
+ ctx->loop_count = currentLoopCount;
+ return total;
+}
+
+
+
+
+
+//--------------------------------------------
+//FILE LOADER - TODO - NEEDS TO BE CLEANED UP
+//--------------------------------------------
+
+
+
+#undef DEBUG
+#define DEBUG(...) do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ } while(0)
+
+#define DEBUG_ERR(...) do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ } while(0)
+
+#define FATAL(...) do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ exit(1); \
+ } while(0)
+
+#define FATAL_ERR(...) do { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ exit(1); \
+ } while(0)
+
+
+int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const char* filename) {
+ FILE* xmf;
+ int size;
+
+ xmf = fopen(filename, "rb");
+ if(xmf == NULL) {
+ DEBUG_ERR("Could not open input file");
+ *ctx = NULL;
+ return 3;
+ }
+
+ fseek(xmf, 0, SEEK_END);
+ size = ftell(xmf);
+ rewind(xmf);
+ if(size == -1) {
+ fclose(xmf);
+ DEBUG_ERR("fseek() failed");
+ *ctx = NULL;
+ return 4;
+ }
+
+ char* data = malloc(size + 1);
+ if(fread(data, 1, size, xmf) < size) {
+ fclose(xmf);
+ DEBUG_ERR("fread() failed");
+ *ctx = NULL;
+ return 5;
+ }
+
+ fclose(xmf);
+
+ switch(jar_xm_create_context_safe(ctx, data, size, rate)) {
+ case 0:
+ break;
+
+ case 1:
+ DEBUG("could not create context: module is not sane\n");
+ *ctx = NULL;
+ return 1;
+ break;
+
+ case 2:
+ FATAL("could not create context: malloc failed\n");
+ return 2;
+ break;
+
+ default:
+ FATAL("could not create context: unknown error\n");
+ return 6;
+ break;
+
+ }
+
+ return 0;
+}
+
+
+
+
+#endif//end of JAR_XM_IMPLEMENTATION
+//-------------------------------------------------------------------------------
+
+
+
+
+#endif//end of INCLUDE_JAR_XM_H
diff --git a/src/external/openal_soft/COPYING b/src/external/openal_soft/COPYING
new file mode 100644
index 00000000..d0c89786
--- /dev/null
+++ b/src/external/openal_soft/COPYING
@@ -0,0 +1,484 @@
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
diff --git a/src/external/openal_soft/include/AL/al.h b/src/external/openal_soft/include/AL/al.h
new file mode 100644
index 00000000..413b3833
--- /dev/null
+++ b/src/external/openal_soft/include/AL/al.h
@@ -0,0 +1,656 @@
+#ifndef AL_AL_H
+#define AL_AL_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef AL_API
+ #if defined(AL_LIBTYPE_STATIC)
+ #define AL_API
+ #elif defined(_WIN32)
+ #define AL_API __declspec(dllimport)
+ #else
+ #define AL_API extern
+ #endif
+#endif
+
+#if defined(_WIN32)
+ #define AL_APIENTRY __cdecl
+#else
+ #define AL_APIENTRY
+#endif
+
+
+/** Deprecated macro. */
+#define OPENAL
+#define ALAPI AL_API
+#define ALAPIENTRY AL_APIENTRY
+#define AL_INVALID (-1)
+#define AL_ILLEGAL_ENUM AL_INVALID_ENUM
+#define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
+
+/** Supported AL version. */
+#define AL_VERSION_1_0
+#define AL_VERSION_1_1
+
+/** 8-bit boolean */
+typedef char ALboolean;
+
+/** character */
+typedef char ALchar;
+
+/** signed 8-bit 2's complement integer */
+typedef signed char ALbyte;
+
+/** unsigned 8-bit integer */
+typedef unsigned char ALubyte;
+
+/** signed 16-bit 2's complement integer */
+typedef short ALshort;
+
+/** unsigned 16-bit integer */
+typedef unsigned short ALushort;
+
+/** signed 32-bit 2's complement integer */
+typedef int ALint;
+
+/** unsigned 32-bit integer */
+typedef unsigned int ALuint;
+
+/** non-negative 32-bit binary integer size */
+typedef int ALsizei;
+
+/** enumerated 32-bit value */
+typedef int ALenum;
+
+/** 32-bit IEEE754 floating-point */
+typedef float ALfloat;
+
+/** 64-bit IEEE754 floating-point */
+typedef double ALdouble;
+
+/** void type (for opaque pointers only) */
+typedef void ALvoid;
+
+
+/* Enumerant values begin at column 50. No tabs. */
+
+/** "no distance model" or "no buffer" */
+#define AL_NONE 0
+
+/** Boolean False. */
+#define AL_FALSE 0
+
+/** Boolean True. */
+#define AL_TRUE 1
+
+
+/**
+ * Relative source.
+ * Type: ALboolean
+ * Range: [AL_TRUE, AL_FALSE]
+ * Default: AL_FALSE
+ *
+ * Specifies if the Source has relative coordinates.
+ */
+#define AL_SOURCE_RELATIVE 0x202
+
+
+/**
+ * Inner cone angle, in degrees.
+ * Type: ALint, ALfloat
+ * Range: [0 - 360]
+ * Default: 360
+ *
+ * The angle covered by the inner cone, where the source will not attenuate.
+ */
+#define AL_CONE_INNER_ANGLE 0x1001
+
+/**
+ * Outer cone angle, in degrees.
+ * Range: [0 - 360]
+ * Default: 360
+ *
+ * The angle covered by the outer cone, where the source will be fully
+ * attenuated.
+ */
+#define AL_CONE_OUTER_ANGLE 0x1002
+
+/**
+ * Source pitch.
+ * Type: ALfloat
+ * Range: [0.5 - 2.0]
+ * Default: 1.0
+ *
+ * A multiplier for the frequency (sample rate) of the source's buffer.
+ */
+#define AL_PITCH 0x1003
+
+/**
+ * Source or listener position.
+ * Type: ALfloat[3], ALint[3]
+ * Default: {0, 0, 0}
+ *
+ * The source or listener location in three dimensional space.
+ *
+ * OpenAL, like OpenGL, uses a right handed coordinate system, where in a
+ * frontal default view X (thumb) points right, Y points up (index finger), and
+ * Z points towards the viewer/camera (middle finger).
+ *
+ * To switch from a left handed coordinate system, flip the sign on the Z
+ * coordinate.
+ */
+#define AL_POSITION 0x1004
+
+/**
+ * Source direction.
+ * Type: ALfloat[3], ALint[3]
+ * Default: {0, 0, 0}
+ *
+ * Specifies the current direction in local space.
+ * A zero-length vector specifies an omni-directional source (cone is ignored).
+ */
+#define AL_DIRECTION 0x1005
+
+/**
+ * Source or listener velocity.
+ * Type: ALfloat[3], ALint[3]
+ * Default: {0, 0, 0}
+ *
+ * Specifies the current velocity in local space.
+ */
+#define AL_VELOCITY 0x1006
+
+/**
+ * Source looping.
+ * Type: ALboolean
+ * Range: [AL_TRUE, AL_FALSE]
+ * Default: AL_FALSE
+ *
+ * Specifies whether source is looping.
+ */
+#define AL_LOOPING 0x1007
+
+/**
+ * Source buffer.
+ * Type: ALuint
+ * Range: any valid Buffer.
+ *
+ * Specifies the buffer to provide sound samples.
+ */
+#define AL_BUFFER 0x1009
+
+/**
+ * Source or listener gain.
+ * Type: ALfloat
+ * Range: [0.0 - ]
+ *
+ * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation
+ * of about -6dB. Each multiplicaton by 2 equals an amplification of about
+ * +6dB.
+ *
+ * A value of 0.0 is meaningless with respect to a logarithmic scale; it is
+ * silent.
+ */
+#define AL_GAIN 0x100A
+
+/**
+ * Minimum source gain.
+ * Type: ALfloat
+ * Range: [0.0 - 1.0]
+ *
+ * The minimum gain allowed for a source, after distance and cone attenation is
+ * applied (if applicable).
+ */
+#define AL_MIN_GAIN 0x100D
+
+/**
+ * Maximum source gain.
+ * Type: ALfloat
+ * Range: [0.0 - 1.0]
+ *
+ * The maximum gain allowed for a source, after distance and cone attenation is
+ * applied (if applicable).
+ */
+#define AL_MAX_GAIN 0x100E
+
+/**
+ * Listener orientation.
+ * Type: ALfloat[6]
+ * Default: {0.0, 0.0, -1.0, 0.0, 1.0, 0.0}
+ *
+ * Effectively two three dimensional vectors. The first vector is the front (or
+ * "at") and the second is the top (or "up").
+ *
+ * Both vectors are in local space.
+ */
+#define AL_ORIENTATION 0x100F
+
+/**
+ * Source state (query only).
+ * Type: ALint
+ * Range: [AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED]
+ */
+#define AL_SOURCE_STATE 0x1010
+
+/** Source state value. */
+#define AL_INITIAL 0x1011
+#define AL_PLAYING 0x1012
+#define AL_PAUSED 0x1013
+#define AL_STOPPED 0x1014
+
+/**
+ * Source Buffer Queue size (query only).
+ * Type: ALint
+ *
+ * The number of buffers queued using alSourceQueueBuffers, minus the buffers
+ * removed with alSourceUnqueueBuffers.
+ */
+#define AL_BUFFERS_QUEUED 0x1015
+
+/**
+ * Source Buffer Queue processed count (query only).
+ * Type: ALint
+ *
+ * The number of queued buffers that have been fully processed, and can be
+ * removed with alSourceUnqueueBuffers.
+ *
+ * Looping sources will never fully process buffers because they will be set to
+ * play again for when the source loops.
+ */
+#define AL_BUFFERS_PROCESSED 0x1016
+
+/**
+ * Source reference distance.
+ * Type: ALfloat
+ * Range: [0.0 - ]
+ * Default: 1.0
+ *
+ * The distance in units that no attenuation occurs.
+ *
+ * At 0.0, no distance attenuation ever occurs on non-linear attenuation models.
+ */
+#define AL_REFERENCE_DISTANCE 0x1020
+
+/**
+ * Source rolloff factor.
+ * Type: ALfloat
+ * Range: [0.0 - ]
+ * Default: 1.0
+ *
+ * Multiplier to exaggerate or diminish distance attenuation.
+ *
+ * At 0.0, no distance attenuation ever occurs.
+ */
+#define AL_ROLLOFF_FACTOR 0x1021
+
+/**
+ * Outer cone gain.
+ * Type: ALfloat
+ * Range: [0.0 - 1.0]
+ * Default: 0.0
+ *
+ * The gain attenuation applied when the listener is outside of the source's
+ * outer cone.
+ */
+#define AL_CONE_OUTER_GAIN 0x1022
+
+/**
+ * Source maximum distance.
+ * Type: ALfloat
+ * Range: [0.0 - ]
+ * Default: +inf
+ *
+ * The distance above which the source is not attenuated any further with a
+ * clamped distance model, or where attenuation reaches 0.0 gain for linear
+ * distance models with a default rolloff factor.
+ */
+#define AL_MAX_DISTANCE 0x1023
+
+/** Source buffer position, in seconds */
+#define AL_SEC_OFFSET 0x1024
+/** Source buffer position, in sample frames */
+#define AL_SAMPLE_OFFSET 0x1025
+/** Source buffer position, in bytes */
+#define AL_BYTE_OFFSET 0x1026
+
+/**
+ * Source type (query only).
+ * Type: ALint
+ * Range: [AL_STATIC, AL_STREAMING, AL_UNDETERMINED]
+ *
+ * A Source is Static if a Buffer has been attached using AL_BUFFER.
+ *
+ * A Source is Streaming if one or more Buffers have been attached using
+ * alSourceQueueBuffers.
+ *
+ * A Source is Undetermined when it has the NULL buffer attached using
+ * AL_BUFFER.
+ */
+#define AL_SOURCE_TYPE 0x1027
+
+/** Source type value. */
+#define AL_STATIC 0x1028
+#define AL_STREAMING 0x1029
+#define AL_UNDETERMINED 0x1030
+
+/** Buffer format specifier. */
+#define AL_FORMAT_MONO8 0x1100
+#define AL_FORMAT_MONO16 0x1101
+#define AL_FORMAT_STEREO8 0x1102
+#define AL_FORMAT_STEREO16 0x1103
+
+/** Buffer frequency (query only). */
+#define AL_FREQUENCY 0x2001
+/** Buffer bits per sample (query only). */
+#define AL_BITS 0x2002
+/** Buffer channel count (query only). */
+#define AL_CHANNELS 0x2003
+/** Buffer data size (query only). */
+#define AL_SIZE 0x2004
+
+/**
+ * Buffer state.
+ *
+ * Not for public use.
+ */
+#define AL_UNUSED 0x2010
+#define AL_PENDING 0x2011
+#define AL_PROCESSED 0x2012
+
+
+/** No error. */
+#define AL_NO_ERROR 0
+
+/** Invalid name paramater passed to AL call. */
+#define AL_INVALID_NAME 0xA001
+
+/** Invalid enum parameter passed to AL call. */
+#define AL_INVALID_ENUM 0xA002
+
+/** Invalid value parameter passed to AL call. */
+#define AL_INVALID_VALUE 0xA003
+
+/** Illegal AL call. */
+#define AL_INVALID_OPERATION 0xA004
+
+/** Not enough memory. */
+#define AL_OUT_OF_MEMORY 0xA005
+
+
+/** Context string: Vendor ID. */
+#define AL_VENDOR 0xB001
+/** Context string: Version. */
+#define AL_VERSION 0xB002
+/** Context string: Renderer ID. */
+#define AL_RENDERER 0xB003
+/** Context string: Space-separated extension list. */
+#define AL_EXTENSIONS 0xB004
+
+
+/**
+ * Doppler scale.
+ * Type: ALfloat
+ * Range: [0.0 - ]
+ * Default: 1.0
+ *
+ * Scale for source and listener velocities.
+ */
+#define AL_DOPPLER_FACTOR 0xC000
+AL_API void AL_APIENTRY alDopplerFactor(ALfloat value);
+
+/**
+ * Doppler velocity (deprecated).
+ *
+ * A multiplier applied to the Speed of Sound.
+ */
+#define AL_DOPPLER_VELOCITY 0xC001
+AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value);
+
+/**
+ * Speed of Sound, in units per second.
+ * Type: ALfloat
+ * Range: [0.0001 - ]
+ * Default: 343.3
+ *
+ * The speed at which sound waves are assumed to travel, when calculating the
+ * doppler effect.
+ */
+#define AL_SPEED_OF_SOUND 0xC003
+AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value);
+
+/**
+ * Distance attenuation model.
+ * Type: ALint
+ * Range: [AL_NONE, AL_INVERSE_DISTANCE, AL_INVERSE_DISTANCE_CLAMPED,
+ * AL_LINEAR_DISTANCE, AL_LINEAR_DISTANCE_CLAMPED,
+ * AL_EXPONENT_DISTANCE, AL_EXPONENT_DISTANCE_CLAMPED]
+ * Default: AL_INVERSE_DISTANCE_CLAMPED
+ *
+ * The model by which sources attenuate with distance.
+ *
+ * None - No distance attenuation.
+ * Inverse - Doubling the distance halves the source gain.
+ * Linear - Linear gain scaling between the reference and max distances.
+ * Exponent - Exponential gain dropoff.
+ *
+ * Clamped variations work like the non-clamped counterparts, except the
+ * distance calculated is clamped between the reference and max distances.
+ */
+#define AL_DISTANCE_MODEL 0xD000
+AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel);
+
+/** Distance model value. */
+#define AL_INVERSE_DISTANCE 0xD001
+#define AL_INVERSE_DISTANCE_CLAMPED 0xD002
+#define AL_LINEAR_DISTANCE 0xD003
+#define AL_LINEAR_DISTANCE_CLAMPED 0xD004
+#define AL_EXPONENT_DISTANCE 0xD005
+#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006
+
+/** Renderer State management. */
+AL_API void AL_APIENTRY alEnable(ALenum capability);
+AL_API void AL_APIENTRY alDisable(ALenum capability);
+AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability);
+
+/** State retrieval. */
+AL_API const ALchar* AL_APIENTRY alGetString(ALenum param);
+AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values);
+AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values);
+AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values);
+AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param);
+AL_API ALint AL_APIENTRY alGetInteger(ALenum param);
+AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param);
+AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param);
+
+/**
+ * Error retrieval.
+ *
+ * Obtain the first error generated in the AL context since the last check.
+ */
+AL_API ALenum AL_APIENTRY alGetError(void);
+
+/**
+ * Extension support.
+ *
+ * Query for the presence of an extension, and obtain any appropriate function
+ * pointers and enum values.
+ */
+AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname);
+AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname);
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename);
+
+
+/** Set Listener parameters */
+AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value);
+AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values);
+AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value);
+AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3);
+AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values);
+
+/** Get Listener parameters */
+AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value);
+AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value);
+AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3);
+AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values);
+
+
+/** Create Source objects. */
+AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources);
+/** Delete Source objects. */
+AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources);
+/** Verify a handle is a valid Source. */
+AL_API ALboolean AL_APIENTRY alIsSource(ALuint source);
+
+/** Set Source parameters. */
+AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value);
+AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values);
+AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value);
+AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
+AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values);
+
+/** Get Source parameters. */
+AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value);
+AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value);
+AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values);
+
+
+/** Play, replay, or resume (if paused) a list of Sources */
+AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources);
+/** Stop a list of Sources */
+AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources);
+/** Rewind a list of Sources */
+AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources);
+/** Pause a list of Sources */
+AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources);
+
+/** Play, replay, or resume a Source */
+AL_API void AL_APIENTRY alSourcePlay(ALuint source);
+/** Stop a Source */
+AL_API void AL_APIENTRY alSourceStop(ALuint source);
+/** Rewind a Source (set playback postiton to beginning) */
+AL_API void AL_APIENTRY alSourceRewind(ALuint source);
+/** Pause a Source */
+AL_API void AL_APIENTRY alSourcePause(ALuint source);
+
+/** Queue buffers onto a source */
+AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers);
+/** Unqueue processed buffers from a source */
+AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers);
+
+
+/** Create Buffer objects */
+AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers);
+/** Delete Buffer objects */
+AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers);
+/** Verify a handle is a valid Buffer */
+AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer);
+
+/** Specifies the data to be copied into a buffer */
+AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq);
+
+/** Set Buffer parameters, */
+AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value);
+AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values);
+AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value);
+AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
+AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values);
+
+/** Get Buffer parameters. */
+AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value);
+AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values);
+AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value);
+AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values);
+
+/** Pointer-to-function type, useful for dynamically getting AL entry points. */
+typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability);
+typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability);
+typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability);
+typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param);
+typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values);
+typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values);
+typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values);
+typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values);
+typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param);
+typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param);
+typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param);
+typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param);
+typedef ALenum (AL_APIENTRY *LPALGETERROR)(void);
+typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname);
+typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname);
+typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename);
+typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value);
+typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values);
+typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value);
+typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3);
+typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values);
+typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value);
+typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values);
+typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value);
+typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3);
+typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values);
+typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources);
+typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources);
+typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source);
+typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value);
+typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values);
+typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value);
+typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
+typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values);
+typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value);
+typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values);
+typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value);
+typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values);
+typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources);
+typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources);
+typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources);
+typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources);
+typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source);
+typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source);
+typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source);
+typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source);
+typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers);
+typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers);
+typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers);
+typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers);
+typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer);
+typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq);
+typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value);
+typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
+typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values);
+typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value);
+typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
+typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values);
+typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value);
+typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
+typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values);
+typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value);
+typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
+typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values);
+typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value);
+typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value);
+typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value);
+typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif /* AL_AL_H */
diff --git a/src/external/openal_soft/include/AL/alc.h b/src/external/openal_soft/include/AL/alc.h
new file mode 100644
index 00000000..294e8b33
--- /dev/null
+++ b/src/external/openal_soft/include/AL/alc.h
@@ -0,0 +1,237 @@
+#ifndef AL_ALC_H
+#define AL_ALC_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef ALC_API
+ #if defined(AL_LIBTYPE_STATIC)
+ #define ALC_API
+ #elif defined(_WIN32)
+ #define ALC_API __declspec(dllimport)
+ #else
+ #define ALC_API extern
+ #endif
+#endif
+
+#if defined(_WIN32)
+ #define ALC_APIENTRY __cdecl
+#else
+ #define ALC_APIENTRY
+#endif
+
+
+/** Deprecated macro. */
+#define ALCAPI ALC_API
+#define ALCAPIENTRY ALC_APIENTRY
+#define ALC_INVALID 0
+
+/** Supported ALC version? */
+#define ALC_VERSION_0_1 1
+
+/** Opaque device handle */
+typedef struct ALCdevice_struct ALCdevice;
+/** Opaque context handle */
+typedef struct ALCcontext_struct ALCcontext;
+
+/** 8-bit boolean */
+typedef char ALCboolean;
+
+/** character */
+typedef char ALCchar;
+
+/** signed 8-bit 2's complement integer */
+typedef signed char ALCbyte;
+
+/** unsigned 8-bit integer */
+typedef unsigned char ALCubyte;
+
+/** signed 16-bit 2's complement integer */
+typedef short ALCshort;
+
+/** unsigned 16-bit integer */
+typedef unsigned short ALCushort;
+
+/** signed 32-bit 2's complement integer */
+typedef int ALCint;
+
+/** unsigned 32-bit integer */
+typedef unsigned int ALCuint;
+
+/** non-negative 32-bit binary integer size */
+typedef int ALCsizei;
+
+/** enumerated 32-bit value */
+typedef int ALCenum;
+
+/** 32-bit IEEE754 floating-point */
+typedef float ALCfloat;
+
+/** 64-bit IEEE754 floating-point */
+typedef double ALCdouble;
+
+/** void type (for opaque pointers only) */
+typedef void ALCvoid;
+
+
+/* Enumerant values begin at column 50. No tabs. */
+
+/** Boolean False. */
+#define ALC_FALSE 0
+
+/** Boolean True. */
+#define ALC_TRUE 1
+
+/** Context attribute: <int> Hz. */
+#define ALC_FREQUENCY 0x1007
+
+/** Context attribute: <int> Hz. */
+#define ALC_REFRESH 0x1008
+
+/** Context attribute: AL_TRUE or AL_FALSE. */
+#define ALC_SYNC 0x1009
+
+/** Context attribute: <int> requested Mono (3D) Sources. */
+#define ALC_MONO_SOURCES 0x1010
+
+/** Context attribute: <int> requested Stereo Sources. */
+#define ALC_STEREO_SOURCES 0x1011
+
+/** No error. */
+#define ALC_NO_ERROR 0
+
+/** Invalid device handle. */
+#define ALC_INVALID_DEVICE 0xA001
+
+/** Invalid context handle. */
+#define ALC_INVALID_CONTEXT 0xA002
+
+/** Invalid enum parameter passed to an ALC call. */
+#define ALC_INVALID_ENUM 0xA003
+
+/** Invalid value parameter passed to an ALC call. */
+#define ALC_INVALID_VALUE 0xA004
+
+/** Out of memory. */
+#define ALC_OUT_OF_MEMORY 0xA005
+
+
+/** Runtime ALC version. */
+#define ALC_MAJOR_VERSION 0x1000
+#define ALC_MINOR_VERSION 0x1001
+
+/** Context attribute list properties. */
+#define ALC_ATTRIBUTES_SIZE 0x1002
+#define ALC_ALL_ATTRIBUTES 0x1003
+
+/** String for the default device specifier. */
+#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004
+/**
+ * String for the given device's specifier.
+ *
+ * If device handle is NULL, it is instead a null-char separated list of
+ * strings of known device specifiers (list ends with an empty string).
+ */
+#define ALC_DEVICE_SPECIFIER 0x1005
+/** String for space-separated list of ALC extensions. */
+#define ALC_EXTENSIONS 0x1006
+
+
+/** Capture extension */
+#define ALC_EXT_CAPTURE 1
+/**
+ * String for the given capture device's specifier.
+ *
+ * If device handle is NULL, it is instead a null-char separated list of
+ * strings of known capture device specifiers (list ends with an empty string).
+ */
+#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310
+/** String for the default capture device specifier. */
+#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311
+/** Number of sample frames available for capture. */
+#define ALC_CAPTURE_SAMPLES 0x312
+
+
+/** Enumerate All extension */
+#define ALC_ENUMERATE_ALL_EXT 1
+/** String for the default extended device specifier. */
+#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012
+/**
+ * String for the given extended device's specifier.
+ *
+ * If device handle is NULL, it is instead a null-char separated list of
+ * strings of known extended device specifiers (list ends with an empty string).
+ */
+#define ALC_ALL_DEVICES_SPECIFIER 0x1013
+
+
+/** Context management. */
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint* attrlist);
+ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context);
+ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context);
+ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context);
+ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context);
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void);
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context);
+
+/** Device management. */
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename);
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device);
+
+
+/**
+ * Error support.
+ *
+ * Obtain the most recent Device error.
+ */
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device);
+
+/**
+ * Extension support.
+ *
+ * Query for the presence of an extension, and obtain any appropriate
+ * function pointers and enum values.
+ */
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname);
+ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname);
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname);
+
+/** Query function. */
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param);
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
+
+/** Capture function. */
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+
+/** Pointer-to-function type, useful for dynamically getting ALC entry points. */
+typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist);
+typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context);
+typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context);
+typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context);
+typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context);
+typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void);
+typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context);
+typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename);
+typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device);
+typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device);
+typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname);
+typedef void* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname);
+typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname);
+typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param);
+typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
+typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
+typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device);
+typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device);
+typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device);
+typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* AL_ALC_H */
diff --git a/src/external/openal_soft/include/AL/alext.h b/src/external/openal_soft/include/AL/alext.h
new file mode 100644
index 00000000..6af581aa
--- /dev/null
+++ b/src/external/openal_soft/include/AL/alext.h
@@ -0,0 +1,438 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2008 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef AL_ALEXT_H
+#define AL_ALEXT_H
+
+#include <stddef.h>
+/* Define int64_t and uint64_t types */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+#elif defined(_WIN32) && defined(__GNUC__)
+#include <stdint.h>
+#elif defined(_WIN32)
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+/* Fallback if nothing above works */
+#include <inttypes.h>
+#endif
+
+#include "alc.h"
+#include "al.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef AL_LOKI_IMA_ADPCM_format
+#define AL_LOKI_IMA_ADPCM_format 1
+#define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000
+#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT 0x10001
+#endif
+
+#ifndef AL_LOKI_WAVE_format
+#define AL_LOKI_WAVE_format 1
+#define AL_FORMAT_WAVE_EXT 0x10002
+#endif
+
+#ifndef AL_EXT_vorbis
+#define AL_EXT_vorbis 1
+#define AL_FORMAT_VORBIS_EXT 0x10003
+#endif
+
+#ifndef AL_LOKI_quadriphonic
+#define AL_LOKI_quadriphonic 1
+#define AL_FORMAT_QUAD8_LOKI 0x10004
+#define AL_FORMAT_QUAD16_LOKI 0x10005
+#endif
+
+#ifndef AL_EXT_float32
+#define AL_EXT_float32 1
+#define AL_FORMAT_MONO_FLOAT32 0x10010
+#define AL_FORMAT_STEREO_FLOAT32 0x10011
+#endif
+
+#ifndef AL_EXT_double
+#define AL_EXT_double 1
+#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012
+#define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013
+#endif
+
+#ifndef AL_EXT_MULAW
+#define AL_EXT_MULAW 1
+#define AL_FORMAT_MONO_MULAW_EXT 0x10014
+#define AL_FORMAT_STEREO_MULAW_EXT 0x10015
+#endif
+
+#ifndef AL_EXT_ALAW
+#define AL_EXT_ALAW 1
+#define AL_FORMAT_MONO_ALAW_EXT 0x10016
+#define AL_FORMAT_STEREO_ALAW_EXT 0x10017
+#endif
+
+#ifndef ALC_LOKI_audio_channel
+#define ALC_LOKI_audio_channel 1
+#define ALC_CHAN_MAIN_LOKI 0x500001
+#define ALC_CHAN_PCM_LOKI 0x500002
+#define ALC_CHAN_CD_LOKI 0x500003
+#endif
+
+#ifndef AL_EXT_MCFORMATS
+#define AL_EXT_MCFORMATS 1
+#define AL_FORMAT_QUAD8 0x1204
+#define AL_FORMAT_QUAD16 0x1205
+#define AL_FORMAT_QUAD32 0x1206
+#define AL_FORMAT_REAR8 0x1207
+#define AL_FORMAT_REAR16 0x1208
+#define AL_FORMAT_REAR32 0x1209
+#define AL_FORMAT_51CHN8 0x120A
+#define AL_FORMAT_51CHN16 0x120B
+#define AL_FORMAT_51CHN32 0x120C
+#define AL_FORMAT_61CHN8 0x120D
+#define AL_FORMAT_61CHN16 0x120E
+#define AL_FORMAT_61CHN32 0x120F
+#define AL_FORMAT_71CHN8 0x1210
+#define AL_FORMAT_71CHN16 0x1211
+#define AL_FORMAT_71CHN32 0x1212
+#endif
+
+#ifndef AL_EXT_MULAW_MCFORMATS
+#define AL_EXT_MULAW_MCFORMATS 1
+#define AL_FORMAT_MONO_MULAW 0x10014
+#define AL_FORMAT_STEREO_MULAW 0x10015
+#define AL_FORMAT_QUAD_MULAW 0x10021
+#define AL_FORMAT_REAR_MULAW 0x10022
+#define AL_FORMAT_51CHN_MULAW 0x10023
+#define AL_FORMAT_61CHN_MULAW 0x10024
+#define AL_FORMAT_71CHN_MULAW 0x10025
+#endif
+
+#ifndef AL_EXT_IMA4
+#define AL_EXT_IMA4 1
+#define AL_FORMAT_MONO_IMA4 0x1300
+#define AL_FORMAT_STEREO_IMA4 0x1301
+#endif
+
+#ifndef AL_EXT_STATIC_BUFFER
+#define AL_EXT_STATIC_BUFFER 1
+typedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq);
+#endif
+#endif
+
+#ifndef ALC_EXT_EFX
+#define ALC_EXT_EFX 1
+#include "efx.h"
+#endif
+
+#ifndef ALC_EXT_disconnect
+#define ALC_EXT_disconnect 1
+#define ALC_CONNECTED 0x313
+#endif
+
+#ifndef ALC_EXT_thread_local_context
+#define ALC_EXT_thread_local_context 1
+typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context);
+typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context);
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);
+#endif
+#endif
+
+#ifndef AL_EXT_source_distance_model
+#define AL_EXT_source_distance_model 1
+#define AL_SOURCE_DISTANCE_MODEL 0x200
+#endif
+
+#ifndef AL_SOFT_buffer_sub_data
+#define AL_SOFT_buffer_sub_data 1
+#define AL_BYTE_RW_OFFSETS_SOFT 0x1031
+#define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032
+typedef ALvoid (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length);
+#endif
+#endif
+
+#ifndef AL_SOFT_loop_points
+#define AL_SOFT_loop_points 1
+#define AL_LOOP_POINTS_SOFT 0x2015
+#endif
+
+#ifndef AL_EXT_FOLDBACK
+#define AL_EXT_FOLDBACK 1
+#define AL_EXT_FOLDBACK_NAME "AL_EXT_FOLDBACK"
+#define AL_FOLDBACK_EVENT_BLOCK 0x4112
+#define AL_FOLDBACK_EVENT_START 0x4111
+#define AL_FOLDBACK_EVENT_STOP 0x4113
+#define AL_FOLDBACK_MODE_MONO 0x4101
+#define AL_FOLDBACK_MODE_STEREO 0x4102
+typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei);
+typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK);
+typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback);
+AL_API void AL_APIENTRY alRequestFoldbackStop(void);
+#endif
+#endif
+
+#ifndef ALC_EXT_DEDICATED
+#define ALC_EXT_DEDICATED 1
+#define AL_DEDICATED_GAIN 0x0001
+#define AL_EFFECT_DEDICATED_DIALOGUE 0x9001
+#define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000
+#endif
+
+#ifndef AL_SOFT_buffer_samples
+#define AL_SOFT_buffer_samples 1
+/* Channel configurations */
+#define AL_MONO_SOFT 0x1500
+#define AL_STEREO_SOFT 0x1501
+#define AL_REAR_SOFT 0x1502
+#define AL_QUAD_SOFT 0x1503
+#define AL_5POINT1_SOFT 0x1504
+#define AL_6POINT1_SOFT 0x1505
+#define AL_7POINT1_SOFT 0x1506
+
+/* Sample types */
+#define AL_BYTE_SOFT 0x1400
+#define AL_UNSIGNED_BYTE_SOFT 0x1401
+#define AL_SHORT_SOFT 0x1402
+#define AL_UNSIGNED_SHORT_SOFT 0x1403
+#define AL_INT_SOFT 0x1404
+#define AL_UNSIGNED_INT_SOFT 0x1405
+#define AL_FLOAT_SOFT 0x1406
+#define AL_DOUBLE_SOFT 0x1407
+#define AL_BYTE3_SOFT 0x1408
+#define AL_UNSIGNED_BYTE3_SOFT 0x1409
+
+/* Storage formats */
+#define AL_MONO8_SOFT 0x1100
+#define AL_MONO16_SOFT 0x1101
+#define AL_MONO32F_SOFT 0x10010
+#define AL_STEREO8_SOFT 0x1102
+#define AL_STEREO16_SOFT 0x1103
+#define AL_STEREO32F_SOFT 0x10011
+#define AL_QUAD8_SOFT 0x1204
+#define AL_QUAD16_SOFT 0x1205
+#define AL_QUAD32F_SOFT 0x1206
+#define AL_REAR8_SOFT 0x1207
+#define AL_REAR16_SOFT 0x1208
+#define AL_REAR32F_SOFT 0x1209
+#define AL_5POINT1_8_SOFT 0x120A
+#define AL_5POINT1_16_SOFT 0x120B
+#define AL_5POINT1_32F_SOFT 0x120C
+#define AL_6POINT1_8_SOFT 0x120D
+#define AL_6POINT1_16_SOFT 0x120E
+#define AL_6POINT1_32F_SOFT 0x120F
+#define AL_7POINT1_8_SOFT 0x1210
+#define AL_7POINT1_16_SOFT 0x1211
+#define AL_7POINT1_32F_SOFT 0x1212
+
+/* Buffer attributes */
+#define AL_INTERNAL_FORMAT_SOFT 0x2008
+#define AL_BYTE_LENGTH_SOFT 0x2009
+#define AL_SAMPLE_LENGTH_SOFT 0x200A
+#define AL_SEC_LENGTH_SOFT 0x200B
+
+typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*);
+typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*);
+typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*);
+typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
+AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
+AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data);
+AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);
+#endif
+#endif
+
+#ifndef AL_SOFT_direct_channels
+#define AL_SOFT_direct_channels 1
+#define AL_DIRECT_CHANNELS_SOFT 0x1033
+#endif
+
+#ifndef ALC_SOFT_loopback
+#define ALC_SOFT_loopback 1
+#define ALC_FORMAT_CHANNELS_SOFT 0x1990
+#define ALC_FORMAT_TYPE_SOFT 0x1991
+
+/* Sample types */
+#define ALC_BYTE_SOFT 0x1400
+#define ALC_UNSIGNED_BYTE_SOFT 0x1401
+#define ALC_SHORT_SOFT 0x1402
+#define ALC_UNSIGNED_SHORT_SOFT 0x1403
+#define ALC_INT_SOFT 0x1404
+#define ALC_UNSIGNED_INT_SOFT 0x1405
+#define ALC_FLOAT_SOFT 0x1406
+
+/* Channel configurations */
+#define ALC_MONO_SOFT 0x1500
+#define ALC_STEREO_SOFT 0x1501
+#define ALC_QUAD_SOFT 0x1503
+#define ALC_5POINT1_SOFT 0x1504
+#define ALC_6POINT1_SOFT 0x1505
+#define ALC_7POINT1_SOFT 0x1506
+
+typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*);
+typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum);
+typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName);
+ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type);
+ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+#endif
+#endif
+
+#ifndef AL_EXT_STEREO_ANGLES
+#define AL_EXT_STEREO_ANGLES 1
+#define AL_STEREO_ANGLES 0x1030
+#endif
+
+#ifndef AL_EXT_SOURCE_RADIUS
+#define AL_EXT_SOURCE_RADIUS 1
+#define AL_SOURCE_RADIUS 0x1031
+#endif
+
+#ifndef AL_SOFT_source_latency
+#define AL_SOFT_source_latency 1
+#define AL_SAMPLE_OFFSET_LATENCY_SOFT 0x1200
+#define AL_SEC_OFFSET_LATENCY_SOFT 0x1201
+typedef int64_t ALint64SOFT;
+typedef uint64_t ALuint64SOFT;
+typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble);
+typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble);
+typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*);
+typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*);
+typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*);
+typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*);
+typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT);
+typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT);
+typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*);
+typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*);
+typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*);
+typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value);
+AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3);
+AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values);
+AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value);
+AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3);
+AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values);
+AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value);
+AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3);
+AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values);
+AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value);
+AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3);
+AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values);
+#endif
+#endif
+
+#ifndef ALC_EXT_DEFAULT_FILTER_ORDER
+#define ALC_EXT_DEFAULT_FILTER_ORDER 1
+#define ALC_DEFAULT_FILTER_ORDER 0x1100
+#endif
+
+#ifndef AL_SOFT_deferred_updates
+#define AL_SOFT_deferred_updates 1
+#define AL_DEFERRED_UPDATES_SOFT 0xC002
+typedef ALvoid (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void);
+typedef ALvoid (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void);
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alDeferUpdatesSOFT(void);
+AL_API ALvoid AL_APIENTRY alProcessUpdatesSOFT(void);
+#endif
+#endif
+
+#ifndef AL_SOFT_block_alignment
+#define AL_SOFT_block_alignment 1
+#define AL_UNPACK_BLOCK_ALIGNMENT_SOFT 0x200C
+#define AL_PACK_BLOCK_ALIGNMENT_SOFT 0x200D
+#endif
+
+#ifndef AL_SOFT_MSADPCM
+#define AL_SOFT_MSADPCM 1
+#define AL_FORMAT_MONO_MSADPCM_SOFT 0x1302
+#define AL_FORMAT_STEREO_MSADPCM_SOFT 0x1303
+#endif
+
+#ifndef AL_SOFT_source_length
+#define AL_SOFT_source_length 1
+/*#define AL_BYTE_LENGTH_SOFT 0x2009*/
+/*#define AL_SAMPLE_LENGTH_SOFT 0x200A*/
+/*#define AL_SEC_LENGTH_SOFT 0x200B*/
+#endif
+
+#ifndef ALC_SOFT_pause_device
+#define ALC_SOFT_pause_device 1
+typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device);
+typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
+#endif
+#endif
+
+#ifndef AL_EXT_BFORMAT
+#define AL_EXT_BFORMAT 1
+#define AL_FORMAT_BFORMAT2D_8 0x20021
+#define AL_FORMAT_BFORMAT2D_16 0x20022
+#define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023
+#define AL_FORMAT_BFORMAT3D_8 0x20031
+#define AL_FORMAT_BFORMAT3D_16 0x20032
+#define AL_FORMAT_BFORMAT3D_FLOAT32 0x20033
+#endif
+
+#ifndef AL_EXT_MULAW_BFORMAT
+#define AL_EXT_MULAW_BFORMAT 1
+#define AL_FORMAT_BFORMAT2D_MULAW 0x10031
+#define AL_FORMAT_BFORMAT3D_MULAW 0x10032
+#endif
+
+#ifndef ALC_SOFT_HRTF
+#define ALC_SOFT_HRTF 1
+#define ALC_HRTF_SOFT 0x1992
+#define ALC_DONT_CARE_SOFT 0x0002
+#define ALC_HRTF_STATUS_SOFT 0x1993
+#define ALC_HRTF_DISABLED_SOFT 0x0000
+#define ALC_HRTF_ENABLED_SOFT 0x0001
+#define ALC_HRTF_DENIED_SOFT 0x0002
+#define ALC_HRTF_REQUIRED_SOFT 0x0003
+#define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004
+#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005
+#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994
+#define ALC_HRTF_SPECIFIER_SOFT 0x1995
+#define ALC_HRTF_ID_SOFT 0x1996
+typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
+typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
+#ifdef AL_ALEXT_PROTOTYPES
+ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
+ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/external/openal_soft/include/AL/efx-creative.h b/src/external/openal_soft/include/AL/efx-creative.h
new file mode 100644
index 00000000..0a04c982
--- /dev/null
+++ b/src/external/openal_soft/include/AL/efx-creative.h
@@ -0,0 +1,3 @@
+/* The tokens that would be defined here are already defined in efx.h. This
+ * empty file is here to provide compatibility with Windows-based projects
+ * that would include it. */
diff --git a/src/external/openal_soft/include/AL/efx-presets.h b/src/external/openal_soft/include/AL/efx-presets.h
new file mode 100644
index 00000000..8539fd51
--- /dev/null
+++ b/src/external/openal_soft/include/AL/efx-presets.h
@@ -0,0 +1,402 @@
+/* Reverb presets for EFX */
+
+#ifndef EFX_PRESETS_H
+#define EFX_PRESETS_H
+
+#ifndef EFXEAXREVERBPROPERTIES_DEFINED
+#define EFXEAXREVERBPROPERTIES_DEFINED
+typedef struct {
+ float flDensity;
+ float flDiffusion;
+ float flGain;
+ float flGainHF;
+ float flGainLF;
+ float flDecayTime;
+ float flDecayHFRatio;
+ float flDecayLFRatio;
+ float flReflectionsGain;
+ float flReflectionsDelay;
+ float flReflectionsPan[3];
+ float flLateReverbGain;
+ float flLateReverbDelay;
+ float flLateReverbPan[3];
+ float flEchoTime;
+ float flEchoDepth;
+ float flModulationTime;
+ float flModulationDepth;
+ float flAirAbsorptionGainHF;
+ float flHFReference;
+ float flLFReference;
+ float flRoomRolloffFactor;
+ int iDecayHFLimit;
+} EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES;
+#endif
+
+/* Default Presets */
+
+#define EFX_REVERB_PRESET_GENERIC \
+ { 1.0000f, 1.0000f, 0.3162f, 0.8913f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PADDEDCELL \
+ { 0.1715f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.1700f, 0.1000f, 1.0000f, 0.2500f, 0.0010f, { 0.0000f, 0.0000f, 0.0000f }, 1.2691f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ROOM \
+ { 0.4287f, 1.0000f, 0.3162f, 0.5929f, 1.0000f, 0.4000f, 0.8300f, 1.0000f, 0.1503f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.0629f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_BATHROOM \
+ { 0.1715f, 1.0000f, 0.3162f, 0.2512f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.6531f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 3.2734f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_LIVINGROOM \
+ { 0.9766f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.5000f, 0.1000f, 1.0000f, 0.2051f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2805f, 0.0040f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_STONEROOM \
+ { 1.0000f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 2.3100f, 0.6400f, 1.0000f, 0.4411f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1003f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_AUDITORIUM \
+ { 1.0000f, 1.0000f, 0.3162f, 0.5781f, 1.0000f, 4.3200f, 0.5900f, 1.0000f, 0.4032f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7170f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CONCERTHALL \
+ { 1.0000f, 1.0000f, 0.3162f, 0.5623f, 1.0000f, 3.9200f, 0.7000f, 1.0000f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.9977f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CAVE \
+ { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 2.9100f, 1.3000f, 1.0000f, 0.5000f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.7063f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_ARENA \
+ { 1.0000f, 1.0000f, 0.3162f, 0.4477f, 1.0000f, 7.2400f, 0.3300f, 1.0000f, 0.2612f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.0186f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_HANGAR \
+ { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 10.0500f, 0.2300f, 1.0000f, 0.5000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2560f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CARPETEDHALLWAY \
+ { 0.4287f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 0.3000f, 0.1000f, 1.0000f, 0.1215f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.1531f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_HALLWAY \
+ { 0.3645f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 1.4900f, 0.5900f, 1.0000f, 0.2458f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.6615f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_STONECORRIDOR \
+ { 1.0000f, 1.0000f, 0.3162f, 0.7612f, 1.0000f, 2.7000f, 0.7900f, 1.0000f, 0.2472f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 1.5758f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ALLEY \
+ { 1.0000f, 0.3000f, 0.3162f, 0.7328f, 1.0000f, 1.4900f, 0.8600f, 1.0000f, 0.2500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.9954f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.9500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FOREST \
+ { 1.0000f, 0.3000f, 0.3162f, 0.0224f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.0525f, 0.1620f, { 0.0000f, 0.0000f, 0.0000f }, 0.7682f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY \
+ { 1.0000f, 0.5000f, 0.3162f, 0.3981f, 1.0000f, 1.4900f, 0.6700f, 1.0000f, 0.0730f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1427f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_MOUNTAINS \
+ { 1.0000f, 0.2700f, 0.3162f, 0.0562f, 1.0000f, 1.4900f, 0.2100f, 1.0000f, 0.0407f, 0.3000f, { 0.0000f, 0.0000f, 0.0000f }, 0.1919f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_QUARRY \
+ { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0000f, 0.0610f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.7000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PLAIN \
+ { 1.0000f, 0.2100f, 0.3162f, 0.1000f, 1.0000f, 1.4900f, 0.5000f, 1.0000f, 0.0585f, 0.1790f, { 0.0000f, 0.0000f, 0.0000f }, 0.1089f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PARKINGLOT \
+ { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 1.6500f, 1.5000f, 1.0000f, 0.2082f, 0.0080f, { 0.0000f, 0.0000f, 0.0000f }, 0.2652f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_SEWERPIPE \
+ { 0.3071f, 0.8000f, 0.3162f, 0.3162f, 1.0000f, 2.8100f, 0.1400f, 1.0000f, 1.6387f, 0.0140f, { 0.0000f, 0.0000f, 0.0000f }, 3.2471f, 0.0210f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_UNDERWATER \
+ { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRUGGED \
+ { 0.4287f, 0.5000f, 0.3162f, 1.0000f, 1.0000f, 8.3900f, 1.3900f, 1.0000f, 0.8760f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 3.1081f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DIZZY \
+ { 0.3645f, 0.6000f, 0.3162f, 0.6310f, 1.0000f, 17.2300f, 0.5600f, 1.0000f, 0.1392f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4937f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.8100f, 0.3100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PSYCHOTIC \
+ { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+/* Castle Presets */
+
+#define EFX_REVERB_PRESET_CASTLE_SMALLROOM \
+ { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 1.2200f, 0.8300f, 0.3100f, 0.8913f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE \
+ { 1.0000f, 0.8900f, 0.3162f, 0.3162f, 0.1000f, 2.3200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_MEDIUMROOM \
+ { 1.0000f, 0.9300f, 0.3162f, 0.2818f, 0.1000f, 2.0400f, 0.8300f, 0.4600f, 0.6310f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1550f, 0.0300f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_LARGEROOM \
+ { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.1259f, 2.5300f, 0.8300f, 0.5000f, 0.4467f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1850f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_LONGPASSAGE \
+ { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 3.4200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_HALL \
+ { 1.0000f, 0.8100f, 0.3162f, 0.2818f, 0.1778f, 3.1400f, 0.7900f, 0.6200f, 0.1778f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_CUPBOARD \
+ { 1.0000f, 0.8900f, 0.3162f, 0.2818f, 0.1000f, 0.6700f, 0.8700f, 0.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 3.5481f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CASTLE_COURTYARD \
+ { 1.0000f, 0.4200f, 0.3162f, 0.4467f, 0.1995f, 2.1300f, 0.6100f, 0.2300f, 0.2239f, 0.1600f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3700f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_CASTLE_ALCOVE \
+ { 1.0000f, 0.8900f, 0.3162f, 0.5012f, 0.1000f, 1.6400f, 0.8700f, 0.3100f, 1.0000f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 }
+
+/* Factory Presets */
+
+#define EFX_REVERB_PRESET_FACTORY_SMALLROOM \
+ { 0.3645f, 0.8200f, 0.3162f, 0.7943f, 0.5012f, 1.7200f, 0.6500f, 1.3100f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.1190f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE \
+ { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 2.5300f, 0.6500f, 1.3100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_MEDIUMROOM \
+ { 0.4287f, 0.8200f, 0.2512f, 0.7943f, 0.5012f, 2.7600f, 0.6500f, 1.3100f, 0.2818f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1740f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_LARGEROOM \
+ { 0.4287f, 0.7500f, 0.2512f, 0.7079f, 0.6310f, 4.2400f, 0.5100f, 1.3100f, 0.1778f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2310f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_LONGPASSAGE \
+ { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 4.0600f, 0.6500f, 1.3100f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_HALL \
+ { 0.4287f, 0.7500f, 0.3162f, 0.7079f, 0.6310f, 7.4300f, 0.5100f, 1.3100f, 0.0631f, 0.0730f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_CUPBOARD \
+ { 0.3071f, 0.6300f, 0.2512f, 0.7943f, 0.5012f, 0.4900f, 0.6500f, 1.3100f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.1070f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_COURTYARD \
+ { 0.3071f, 0.5700f, 0.3162f, 0.3162f, 0.6310f, 2.3200f, 0.2900f, 0.5600f, 0.2239f, 0.1400f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2900f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_FACTORY_ALCOVE \
+ { 0.3645f, 0.5900f, 0.2512f, 0.7943f, 0.5012f, 3.1400f, 0.6500f, 1.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1140f, 0.1000f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 }
+
+/* Ice Palace Presets */
+
+#define EFX_REVERB_PRESET_ICEPALACE_SMALLROOM \
+ { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 1.5100f, 1.5300f, 0.2700f, 0.8913f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1640f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE \
+ { 1.0000f, 0.7500f, 0.3162f, 0.5623f, 0.2818f, 1.7900f, 1.4600f, 0.2800f, 0.5012f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM \
+ { 1.0000f, 0.8700f, 0.3162f, 0.5623f, 0.4467f, 2.2200f, 1.5300f, 0.3200f, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_LARGEROOM \
+ { 1.0000f, 0.8100f, 0.3162f, 0.5623f, 0.4467f, 3.1400f, 1.5300f, 0.3200f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE \
+ { 1.0000f, 0.7700f, 0.3162f, 0.5623f, 0.3981f, 3.0100f, 1.4600f, 0.2800f, 0.7943f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.0400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_HALL \
+ { 1.0000f, 0.7600f, 0.3162f, 0.4467f, 0.5623f, 5.4900f, 1.5300f, 0.3800f, 0.1122f, 0.0540f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0520f, { 0.0000f, 0.0000f, 0.0000f }, 0.2260f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_CUPBOARD \
+ { 1.0000f, 0.8300f, 0.3162f, 0.5012f, 0.2239f, 0.7600f, 1.5300f, 0.2600f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1430f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_COURTYARD \
+ { 1.0000f, 0.5900f, 0.3162f, 0.2818f, 0.3162f, 2.0400f, 1.2000f, 0.3800f, 0.3162f, 0.1730f, { 0.0000f, 0.0000f, 0.0000f }, 0.3162f, 0.0430f, { 0.0000f, 0.0000f, 0.0000f }, 0.2350f, 0.4800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_ICEPALACE_ALCOVE \
+ { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 2.7600f, 1.4600f, 0.2800f, 1.1220f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1610f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 }
+
+/* Space Station Presets */
+
+#define EFX_REVERB_PRESET_SPACESTATION_SMALLROOM \
+ { 0.2109f, 0.7000f, 0.3162f, 0.7079f, 0.8913f, 1.7200f, 0.8200f, 0.5500f, 0.7943f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 0.1880f, 0.2600f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE \
+ { 0.2109f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 3.5700f, 0.5000f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1720f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM \
+ { 0.2109f, 0.7500f, 0.3162f, 0.6310f, 0.8913f, 3.0100f, 0.5000f, 0.5500f, 0.3981f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2090f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_LARGEROOM \
+ { 0.3645f, 0.8100f, 0.3162f, 0.6310f, 0.8913f, 3.8900f, 0.3800f, 0.6100f, 0.3162f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2330f, 0.2800f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE \
+ { 0.4287f, 0.8200f, 0.3162f, 0.6310f, 0.8913f, 4.6200f, 0.6200f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_HALL \
+ { 0.4287f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 7.1100f, 0.3800f, 0.6100f, 0.1778f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2500f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_CUPBOARD \
+ { 0.1715f, 0.5600f, 0.3162f, 0.7079f, 0.8913f, 0.7900f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1810f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPACESTATION_ALCOVE \
+ { 0.2109f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.1600f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1920f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 }
+
+/* Wooden Galleon Presets */
+
+#define EFX_REVERB_PRESET_WOODEN_SMALLROOM \
+ { 1.0000f, 1.0000f, 0.3162f, 0.1122f, 0.3162f, 0.7900f, 0.3200f, 0.8700f, 1.0000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE \
+ { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.7500f, 0.5000f, 0.8700f, 0.8913f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_MEDIUMROOM \
+ { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.2818f, 1.4700f, 0.4200f, 0.8200f, 0.8913f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_LARGEROOM \
+ { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.2818f, 2.6500f, 0.3300f, 0.8200f, 0.8913f, 0.0660f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_LONGPASSAGE \
+ { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.3162f, 1.9900f, 0.4000f, 0.7900f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4467f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_HALL \
+ { 1.0000f, 1.0000f, 0.3162f, 0.0794f, 0.2818f, 3.4500f, 0.3000f, 0.8200f, 0.8913f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_CUPBOARD \
+ { 1.0000f, 1.0000f, 0.3162f, 0.1413f, 0.3162f, 0.5600f, 0.4600f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_COURTYARD \
+ { 1.0000f, 0.6500f, 0.3162f, 0.0794f, 0.3162f, 1.7900f, 0.3500f, 0.7900f, 0.5623f, 0.1230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_WOODEN_ALCOVE \
+ { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.2200f, 0.6200f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 }
+
+/* Sports Presets */
+
+#define EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM \
+ { 1.0000f, 1.0000f, 0.3162f, 0.4467f, 0.7943f, 6.2600f, 0.5100f, 1.1000f, 0.0631f, 0.1830f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_SQUASHCOURT \
+ { 1.0000f, 0.7500f, 0.3162f, 0.3162f, 0.7943f, 2.2200f, 0.9100f, 1.1600f, 0.4467f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1260f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \
+ { 1.0000f, 0.7000f, 0.3162f, 0.7943f, 0.8913f, 2.7600f, 1.2500f, 1.1400f, 0.6310f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL \
+ { 1.0000f, 0.8200f, 0.3162f, 0.7943f, 1.0000f, 5.4900f, 1.3100f, 1.1400f, 0.4467f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2220f, 0.5500f, 1.1590f, 0.2100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_SPORT_GYMNASIUM \
+ { 1.0000f, 0.8100f, 0.3162f, 0.4467f, 0.8913f, 3.1400f, 1.0600f, 1.3500f, 0.3981f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0450f, { 0.0000f, 0.0000f, 0.0000f }, 0.1460f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_FULLSTADIUM \
+ { 1.0000f, 1.0000f, 0.3162f, 0.0708f, 0.7943f, 5.2500f, 0.1700f, 0.8000f, 0.1000f, 0.1880f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SPORT_STADIUMTANNOY \
+ { 1.0000f, 0.7800f, 0.3162f, 0.5623f, 0.5012f, 2.5300f, 0.8800f, 0.6800f, 0.2818f, 0.2300f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+/* Prefab Presets */
+
+#define EFX_REVERB_PRESET_PREFAB_WORKSHOP \
+ { 0.4287f, 1.0000f, 0.3162f, 0.1413f, 0.3981f, 0.7600f, 1.0000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PREFAB_SCHOOLROOM \
+ { 0.4022f, 0.6900f, 0.3162f, 0.6310f, 0.5012f, 0.9800f, 0.4500f, 0.1800f, 1.4125f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PREFAB_PRACTISEROOM \
+ { 0.4022f, 0.8700f, 0.3162f, 0.3981f, 0.5012f, 1.1200f, 0.5600f, 0.1800f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PREFAB_OUTHOUSE \
+ { 1.0000f, 0.8200f, 0.3162f, 0.1122f, 0.1585f, 1.3800f, 0.3800f, 0.3500f, 0.8913f, 0.0240f, { 0.0000f, 0.0000f, -0.0000f }, 0.6310f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.1210f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PREFAB_CARAVAN \
+ { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.1259f, 0.4300f, 1.5000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+/* Dome and Pipe Presets */
+
+#define EFX_REVERB_PRESET_DOME_TOMB \
+ { 1.0000f, 0.7900f, 0.3162f, 0.3548f, 0.2239f, 4.1800f, 0.2100f, 0.1000f, 0.3868f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 1.6788f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PIPE_SMALL \
+ { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 5.0400f, 0.1000f, 0.1000f, 0.5012f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 2.5119f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DOME_SAINTPAULS \
+ { 1.0000f, 0.8700f, 0.3162f, 0.3548f, 0.2239f, 10.4800f, 0.1900f, 0.1000f, 0.1778f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0420f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PIPE_LONGTHIN \
+ { 0.2560f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 9.2100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_PIPE_LARGE \
+ { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 8.4500f, 0.1000f, 0.1000f, 0.3981f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_PIPE_RESONANT \
+ { 0.1373f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 6.8100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 }
+
+/* Outdoors Presets */
+
+#define EFX_REVERB_PRESET_OUTDOORS_BACKYARD \
+ { 1.0000f, 0.4500f, 0.3162f, 0.2512f, 0.5012f, 1.1200f, 0.3400f, 0.4600f, 0.4467f, 0.0690f, { 0.0000f, 0.0000f, -0.0000f }, 0.7079f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \
+ { 1.0000f, 0.0000f, 0.3162f, 0.0112f, 0.6310f, 2.1300f, 0.2100f, 0.4600f, 0.1778f, 0.3000f, { 0.0000f, 0.0000f, -0.0000f }, 0.4467f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON \
+ { 1.0000f, 0.7400f, 0.3162f, 0.1778f, 0.6310f, 3.8900f, 0.2100f, 0.4600f, 0.3162f, 0.2230f, { 0.0000f, 0.0000f, -0.0000f }, 0.3548f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_CREEK \
+ { 1.0000f, 0.3500f, 0.3162f, 0.1778f, 0.5012f, 2.1300f, 0.2100f, 0.4600f, 0.3981f, 0.1150f, { 0.0000f, 0.0000f, -0.0000f }, 0.1995f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_OUTDOORS_VALLEY \
+ { 1.0000f, 0.2800f, 0.3162f, 0.0282f, 0.1585f, 2.8800f, 0.2600f, 0.3500f, 0.1413f, 0.2630f, { 0.0000f, 0.0000f, -0.0000f }, 0.3981f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+/* Mood Presets */
+
+#define EFX_REVERB_PRESET_MOOD_HEAVEN \
+ { 1.0000f, 0.9400f, 0.3162f, 0.7943f, 0.4467f, 5.0400f, 1.1200f, 0.5600f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0800f, 2.7420f, 0.0500f, 0.9977f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_MOOD_HELL \
+ { 1.0000f, 0.5700f, 0.3162f, 0.3548f, 0.4467f, 3.5700f, 0.4900f, 2.0000f, 0.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1100f, 0.0400f, 2.1090f, 0.5200f, 0.9943f, 5000.0000f, 139.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_MOOD_MEMORY \
+ { 1.0000f, 0.8500f, 0.3162f, 0.6310f, 0.3548f, 4.0600f, 0.8200f, 0.5600f, 0.0398f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.4740f, 0.4500f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+/* Driving Presets */
+
+#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \
+ { 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \
+ { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DRIVING_INCAR_RACER \
+ { 0.0832f, 0.8000f, 0.3162f, 1.0000f, 0.7943f, 0.1700f, 2.0000f, 0.4100f, 1.7783f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS \
+ { 0.0832f, 0.8000f, 0.3162f, 0.6310f, 1.0000f, 0.1700f, 0.7500f, 0.4100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY \
+ { 0.2560f, 1.0000f, 0.3162f, 0.1000f, 0.5012f, 0.1300f, 0.4100f, 0.4600f, 0.7943f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND \
+ { 1.0000f, 1.0000f, 0.3162f, 0.2818f, 0.6310f, 3.0100f, 1.3700f, 1.2800f, 0.3548f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.1778f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \
+ { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 0.7943f, 4.6200f, 1.7500f, 1.4000f, 0.2082f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_DRIVING_TUNNEL \
+ { 1.0000f, 0.8100f, 0.3162f, 0.3981f, 0.8913f, 3.4200f, 0.9400f, 1.3100f, 0.7079f, 0.0510f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.0500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 155.3000f, 0.0000f, 0x1 }
+
+/* City Presets */
+
+#define EFX_REVERB_PRESET_CITY_STREETS \
+ { 1.0000f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.7900f, 1.1200f, 0.9100f, 0.2818f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 0.1995f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY_SUBWAY \
+ { 1.0000f, 0.7400f, 0.3162f, 0.7079f, 0.8913f, 3.0100f, 1.2300f, 0.9100f, 0.7079f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY_MUSEUM \
+ { 1.0000f, 0.8200f, 0.3162f, 0.1778f, 0.1778f, 3.2800f, 1.4000f, 0.5700f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_CITY_LIBRARY \
+ { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.0891f, 2.7600f, 0.8900f, 0.4100f, 0.3548f, 0.0290f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 }
+
+#define EFX_REVERB_PRESET_CITY_UNDERPASS \
+ { 1.0000f, 0.8200f, 0.3162f, 0.4467f, 0.8913f, 3.5700f, 1.1200f, 0.9100f, 0.3981f, 0.0590f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1400f, 0.2500f, 0.0000f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CITY_ABANDONED \
+ { 1.0000f, 0.6900f, 0.3162f, 0.7943f, 0.8913f, 3.2800f, 1.1700f, 0.9100f, 0.4467f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9966f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+/* Misc. Presets */
+
+#define EFX_REVERB_PRESET_DUSTYROOM \
+ { 0.3645f, 0.5600f, 0.3162f, 0.7943f, 0.7079f, 1.7900f, 0.3800f, 0.2100f, 0.5012f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0060f, { 0.0000f, 0.0000f, 0.0000f }, 0.2020f, 0.0500f, 0.2500f, 0.0000f, 0.9886f, 13046.0000f, 163.3000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_CHAPEL \
+ { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 1.0000f, 4.6200f, 0.6400f, 1.2300f, 0.4467f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.1100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
+
+#define EFX_REVERB_PRESET_SMALLWATERROOM \
+ { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
+
+#endif /* EFX_PRESETS_H */
diff --git a/src/external/openal_soft/include/AL/efx.h b/src/external/openal_soft/include/AL/efx.h
new file mode 100644
index 00000000..57766983
--- /dev/null
+++ b/src/external/openal_soft/include/AL/efx.h
@@ -0,0 +1,761 @@
+#ifndef AL_EFX_H
+#define AL_EFX_H
+
+
+#include "alc.h"
+#include "al.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALC_EXT_EFX_NAME "ALC_EXT_EFX"
+
+#define ALC_EFX_MAJOR_VERSION 0x20001
+#define ALC_EFX_MINOR_VERSION 0x20002
+#define ALC_MAX_AUXILIARY_SENDS 0x20003
+
+
+/* Listener properties. */
+#define AL_METERS_PER_UNIT 0x20004
+
+/* Source properties. */
+#define AL_DIRECT_FILTER 0x20005
+#define AL_AUXILIARY_SEND_FILTER 0x20006
+#define AL_AIR_ABSORPTION_FACTOR 0x20007
+#define AL_ROOM_ROLLOFF_FACTOR 0x20008
+#define AL_CONE_OUTER_GAINHF 0x20009
+#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A
+#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B
+#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C
+
+
+/* Effect properties. */
+
+/* Reverb effect parameters */
+#define AL_REVERB_DENSITY 0x0001
+#define AL_REVERB_DIFFUSION 0x0002
+#define AL_REVERB_GAIN 0x0003
+#define AL_REVERB_GAINHF 0x0004
+#define AL_REVERB_DECAY_TIME 0x0005
+#define AL_REVERB_DECAY_HFRATIO 0x0006
+#define AL_REVERB_REFLECTIONS_GAIN 0x0007
+#define AL_REVERB_REFLECTIONS_DELAY 0x0008
+#define AL_REVERB_LATE_REVERB_GAIN 0x0009
+#define AL_REVERB_LATE_REVERB_DELAY 0x000A
+#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B
+#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C
+#define AL_REVERB_DECAY_HFLIMIT 0x000D
+
+/* EAX Reverb effect parameters */
+#define AL_EAXREVERB_DENSITY 0x0001
+#define AL_EAXREVERB_DIFFUSION 0x0002
+#define AL_EAXREVERB_GAIN 0x0003
+#define AL_EAXREVERB_GAINHF 0x0004
+#define AL_EAXREVERB_GAINLF 0x0005
+#define AL_EAXREVERB_DECAY_TIME 0x0006
+#define AL_EAXREVERB_DECAY_HFRATIO 0x0007
+#define AL_EAXREVERB_DECAY_LFRATIO 0x0008
+#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009
+#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A
+#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B
+#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C
+#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D
+#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E
+#define AL_EAXREVERB_ECHO_TIME 0x000F
+#define AL_EAXREVERB_ECHO_DEPTH 0x0010
+#define AL_EAXREVERB_MODULATION_TIME 0x0011
+#define AL_EAXREVERB_MODULATION_DEPTH 0x0012
+#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013
+#define AL_EAXREVERB_HFREFERENCE 0x0014
+#define AL_EAXREVERB_LFREFERENCE 0x0015
+#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016
+#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017
+
+/* Chorus effect parameters */
+#define AL_CHORUS_WAVEFORM 0x0001
+#define AL_CHORUS_PHASE 0x0002
+#define AL_CHORUS_RATE 0x0003
+#define AL_CHORUS_DEPTH 0x0004
+#define AL_CHORUS_FEEDBACK 0x0005
+#define AL_CHORUS_DELAY 0x0006
+
+/* Distortion effect parameters */
+#define AL_DISTORTION_EDGE 0x0001
+#define AL_DISTORTION_GAIN 0x0002
+#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003
+#define AL_DISTORTION_EQCENTER 0x0004
+#define AL_DISTORTION_EQBANDWIDTH 0x0005
+
+/* Echo effect parameters */
+#define AL_ECHO_DELAY 0x0001
+#define AL_ECHO_LRDELAY 0x0002
+#define AL_ECHO_DAMPING 0x0003
+#define AL_ECHO_FEEDBACK 0x0004
+#define AL_ECHO_SPREAD 0x0005
+
+/* Flanger effect parameters */
+#define AL_FLANGER_WAVEFORM 0x0001
+#define AL_FLANGER_PHASE 0x0002
+#define AL_FLANGER_RATE 0x0003
+#define AL_FLANGER_DEPTH 0x0004
+#define AL_FLANGER_FEEDBACK 0x0005
+#define AL_FLANGER_DELAY 0x0006
+
+/* Frequency shifter effect parameters */
+#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001
+#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002
+#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003
+
+/* Vocal morpher effect parameters */
+#define AL_VOCAL_MORPHER_PHONEMEA 0x0001
+#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002
+#define AL_VOCAL_MORPHER_PHONEMEB 0x0003
+#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004
+#define AL_VOCAL_MORPHER_WAVEFORM 0x0005
+#define AL_VOCAL_MORPHER_RATE 0x0006
+
+/* Pitchshifter effect parameters */
+#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001
+#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002
+
+/* Ringmodulator effect parameters */
+#define AL_RING_MODULATOR_FREQUENCY 0x0001
+#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002
+#define AL_RING_MODULATOR_WAVEFORM 0x0003
+
+/* Autowah effect parameters */
+#define AL_AUTOWAH_ATTACK_TIME 0x0001
+#define AL_AUTOWAH_RELEASE_TIME 0x0002
+#define AL_AUTOWAH_RESONANCE 0x0003
+#define AL_AUTOWAH_PEAK_GAIN 0x0004
+
+/* Compressor effect parameters */
+#define AL_COMPRESSOR_ONOFF 0x0001
+
+/* Equalizer effect parameters */
+#define AL_EQUALIZER_LOW_GAIN 0x0001
+#define AL_EQUALIZER_LOW_CUTOFF 0x0002
+#define AL_EQUALIZER_MID1_GAIN 0x0003
+#define AL_EQUALIZER_MID1_CENTER 0x0004
+#define AL_EQUALIZER_MID1_WIDTH 0x0005
+#define AL_EQUALIZER_MID2_GAIN 0x0006
+#define AL_EQUALIZER_MID2_CENTER 0x0007
+#define AL_EQUALIZER_MID2_WIDTH 0x0008
+#define AL_EQUALIZER_HIGH_GAIN 0x0009
+#define AL_EQUALIZER_HIGH_CUTOFF 0x000A
+
+/* Effect type */
+#define AL_EFFECT_FIRST_PARAMETER 0x0000
+#define AL_EFFECT_LAST_PARAMETER 0x8000
+#define AL_EFFECT_TYPE 0x8001
+
+/* Effect types, used with the AL_EFFECT_TYPE property */
+#define AL_EFFECT_NULL 0x0000
+#define AL_EFFECT_REVERB 0x0001
+#define AL_EFFECT_CHORUS 0x0002
+#define AL_EFFECT_DISTORTION 0x0003
+#define AL_EFFECT_ECHO 0x0004
+#define AL_EFFECT_FLANGER 0x0005
+#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006
+#define AL_EFFECT_VOCAL_MORPHER 0x0007
+#define AL_EFFECT_PITCH_SHIFTER 0x0008
+#define AL_EFFECT_RING_MODULATOR 0x0009
+#define AL_EFFECT_AUTOWAH 0x000A
+#define AL_EFFECT_COMPRESSOR 0x000B
+#define AL_EFFECT_EQUALIZER 0x000C
+#define AL_EFFECT_EAXREVERB 0x8000
+
+/* Auxiliary Effect Slot properties. */
+#define AL_EFFECTSLOT_EFFECT 0x0001
+#define AL_EFFECTSLOT_GAIN 0x0002
+#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003
+
+/* NULL Auxiliary Slot ID to disable a source send. */
+#define AL_EFFECTSLOT_NULL 0x0000
+
+
+/* Filter properties. */
+
+/* Lowpass filter parameters */
+#define AL_LOWPASS_GAIN 0x0001
+#define AL_LOWPASS_GAINHF 0x0002
+
+/* Highpass filter parameters */
+#define AL_HIGHPASS_GAIN 0x0001
+#define AL_HIGHPASS_GAINLF 0x0002
+
+/* Bandpass filter parameters */
+#define AL_BANDPASS_GAIN 0x0001
+#define AL_BANDPASS_GAINLF 0x0002
+#define AL_BANDPASS_GAINHF 0x0003
+
+/* Filter type */
+#define AL_FILTER_FIRST_PARAMETER 0x0000
+#define AL_FILTER_LAST_PARAMETER 0x8000
+#define AL_FILTER_TYPE 0x8001
+
+/* Filter types, used with the AL_FILTER_TYPE property */
+#define AL_FILTER_NULL 0x0000
+#define AL_FILTER_LOWPASS 0x0001
+#define AL_FILTER_HIGHPASS 0x0002
+#define AL_FILTER_BANDPASS 0x0003
+
+
+/* Effect object function types. */
+typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint);
+typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*);
+typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*);
+typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*);
+
+/* Filter object function types. */
+typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint);
+typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*);
+typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*);
+typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*);
+
+/* Auxiliary Effect Slot object function types. */
+typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*);
+typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*);
+typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat);
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*);
+
+#ifdef AL_ALEXT_PROTOTYPES
+AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects);
+AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects);
+AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect);
+AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues);
+AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
+
+AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters);
+AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters);
+AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter);
+AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues);
+AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
+
+AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
+AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots);
+AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue);
+AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue);
+AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
+#endif
+
+/* Filter ranges and defaults. */
+
+/* Lowpass filter */
+#define AL_LOWPASS_MIN_GAIN (0.0f)
+#define AL_LOWPASS_MAX_GAIN (1.0f)
+#define AL_LOWPASS_DEFAULT_GAIN (1.0f)
+
+#define AL_LOWPASS_MIN_GAINHF (0.0f)
+#define AL_LOWPASS_MAX_GAINHF (1.0f)
+#define AL_LOWPASS_DEFAULT_GAINHF (1.0f)
+
+/* Highpass filter */
+#define AL_HIGHPASS_MIN_GAIN (0.0f)
+#define AL_HIGHPASS_MAX_GAIN (1.0f)
+#define AL_HIGHPASS_DEFAULT_GAIN (1.0f)
+
+#define AL_HIGHPASS_MIN_GAINLF (0.0f)
+#define AL_HIGHPASS_MAX_GAINLF (1.0f)
+#define AL_HIGHPASS_DEFAULT_GAINLF (1.0f)
+
+/* Bandpass filter */
+#define AL_BANDPASS_MIN_GAIN (0.0f)
+#define AL_BANDPASS_MAX_GAIN (1.0f)
+#define AL_BANDPASS_DEFAULT_GAIN (1.0f)
+
+#define AL_BANDPASS_MIN_GAINHF (0.0f)
+#define AL_BANDPASS_MAX_GAINHF (1.0f)
+#define AL_BANDPASS_DEFAULT_GAINHF (1.0f)
+
+#define AL_BANDPASS_MIN_GAINLF (0.0f)
+#define AL_BANDPASS_MAX_GAINLF (1.0f)
+#define AL_BANDPASS_DEFAULT_GAINLF (1.0f)
+
+
+/* Effect parameter ranges and defaults. */
+
+/* Standard reverb effect */
+#define AL_REVERB_MIN_DENSITY (0.0f)
+#define AL_REVERB_MAX_DENSITY (1.0f)
+#define AL_REVERB_DEFAULT_DENSITY (1.0f)
+
+#define AL_REVERB_MIN_DIFFUSION (0.0f)
+#define AL_REVERB_MAX_DIFFUSION (1.0f)
+#define AL_REVERB_DEFAULT_DIFFUSION (1.0f)
+
+#define AL_REVERB_MIN_GAIN (0.0f)
+#define AL_REVERB_MAX_GAIN (1.0f)
+#define AL_REVERB_DEFAULT_GAIN (0.32f)
+
+#define AL_REVERB_MIN_GAINHF (0.0f)
+#define AL_REVERB_MAX_GAINHF (1.0f)
+#define AL_REVERB_DEFAULT_GAINHF (0.89f)
+
+#define AL_REVERB_MIN_DECAY_TIME (0.1f)
+#define AL_REVERB_MAX_DECAY_TIME (20.0f)
+#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f)
+
+#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f)
+#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f)
+#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f)
+
+#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f)
+#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f)
+#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
+
+#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f)
+#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f)
+#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
+
+#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f)
+#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f)
+#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
+
+#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f)
+#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f)
+#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
+
+#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
+#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
+#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
+
+#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
+#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
+#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+
+#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE
+#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE
+#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE
+
+/* EAX reverb effect */
+#define AL_EAXREVERB_MIN_DENSITY (0.0f)
+#define AL_EAXREVERB_MAX_DENSITY (1.0f)
+#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f)
+
+#define AL_EAXREVERB_MIN_DIFFUSION (0.0f)
+#define AL_EAXREVERB_MAX_DIFFUSION (1.0f)
+#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f)
+
+#define AL_EAXREVERB_MIN_GAIN (0.0f)
+#define AL_EAXREVERB_MAX_GAIN (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAIN (0.32f)
+
+#define AL_EAXREVERB_MIN_GAINHF (0.0f)
+#define AL_EAXREVERB_MAX_GAINHF (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f)
+
+#define AL_EAXREVERB_MIN_GAINLF (0.0f)
+#define AL_EAXREVERB_MAX_GAINLF (1.0f)
+#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f)
+
+#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f)
+
+#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f)
+
+#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f)
+#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f)
+#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f)
+
+#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f)
+#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f)
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
+
+#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f)
+#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f)
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
+
+#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
+
+#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f)
+#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f)
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
+
+#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f)
+#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f)
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
+
+#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
+
+#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f)
+#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f)
+#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f)
+
+#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f)
+#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f)
+#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f)
+
+#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f)
+#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f)
+#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f)
+
+#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f)
+#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f)
+#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f)
+
+#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
+#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
+#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
+
+#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f)
+#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f)
+#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f)
+
+#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f)
+#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f)
+#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f)
+
+#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
+#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
+#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+
+#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE
+#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE
+#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE
+
+/* Chorus effect */
+#define AL_CHORUS_WAVEFORM_SINUSOID (0)
+#define AL_CHORUS_WAVEFORM_TRIANGLE (1)
+
+#define AL_CHORUS_MIN_WAVEFORM (0)
+#define AL_CHORUS_MAX_WAVEFORM (1)
+#define AL_CHORUS_DEFAULT_WAVEFORM (1)
+
+#define AL_CHORUS_MIN_PHASE (-180)
+#define AL_CHORUS_MAX_PHASE (180)
+#define AL_CHORUS_DEFAULT_PHASE (90)
+
+#define AL_CHORUS_MIN_RATE (0.0f)
+#define AL_CHORUS_MAX_RATE (10.0f)
+#define AL_CHORUS_DEFAULT_RATE (1.1f)
+
+#define AL_CHORUS_MIN_DEPTH (0.0f)
+#define AL_CHORUS_MAX_DEPTH (1.0f)
+#define AL_CHORUS_DEFAULT_DEPTH (0.1f)
+
+#define AL_CHORUS_MIN_FEEDBACK (-1.0f)
+#define AL_CHORUS_MAX_FEEDBACK (1.0f)
+#define AL_CHORUS_DEFAULT_FEEDBACK (0.25f)
+
+#define AL_CHORUS_MIN_DELAY (0.0f)
+#define AL_CHORUS_MAX_DELAY (0.016f)
+#define AL_CHORUS_DEFAULT_DELAY (0.016f)
+
+/* Distortion effect */
+#define AL_DISTORTION_MIN_EDGE (0.0f)
+#define AL_DISTORTION_MAX_EDGE (1.0f)
+#define AL_DISTORTION_DEFAULT_EDGE (0.2f)
+
+#define AL_DISTORTION_MIN_GAIN (0.01f)
+#define AL_DISTORTION_MAX_GAIN (1.0f)
+#define AL_DISTORTION_DEFAULT_GAIN (0.05f)
+
+#define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f)
+#define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f)
+#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f)
+
+#define AL_DISTORTION_MIN_EQCENTER (80.0f)
+#define AL_DISTORTION_MAX_EQCENTER (24000.0f)
+#define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f)
+
+#define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f)
+#define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f)
+#define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f)
+
+/* Echo effect */
+#define AL_ECHO_MIN_DELAY (0.0f)
+#define AL_ECHO_MAX_DELAY (0.207f)
+#define AL_ECHO_DEFAULT_DELAY (0.1f)
+
+#define AL_ECHO_MIN_LRDELAY (0.0f)
+#define AL_ECHO_MAX_LRDELAY (0.404f)
+#define AL_ECHO_DEFAULT_LRDELAY (0.1f)
+
+#define AL_ECHO_MIN_DAMPING (0.0f)
+#define AL_ECHO_MAX_DAMPING (0.99f)
+#define AL_ECHO_DEFAULT_DAMPING (0.5f)
+
+#define AL_ECHO_MIN_FEEDBACK (0.0f)
+#define AL_ECHO_MAX_FEEDBACK (1.0f)
+#define AL_ECHO_DEFAULT_FEEDBACK (0.5f)
+
+#define AL_ECHO_MIN_SPREAD (-1.0f)
+#define AL_ECHO_MAX_SPREAD (1.0f)
+#define AL_ECHO_DEFAULT_SPREAD (-1.0f)
+
+/* Flanger effect */
+#define AL_FLANGER_WAVEFORM_SINUSOID (0)
+#define AL_FLANGER_WAVEFORM_TRIANGLE (1)
+
+#define AL_FLANGER_MIN_WAVEFORM (0)
+#define AL_FLANGER_MAX_WAVEFORM (1)
+#define AL_FLANGER_DEFAULT_WAVEFORM (1)
+
+#define AL_FLANGER_MIN_PHASE (-180)
+#define AL_FLANGER_MAX_PHASE (180)
+#define AL_FLANGER_DEFAULT_PHASE (0)
+
+#define AL_FLANGER_MIN_RATE (0.0f)
+#define AL_FLANGER_MAX_RATE (10.0f)
+#define AL_FLANGER_DEFAULT_RATE (0.27f)
+
+#define AL_FLANGER_MIN_DEPTH (0.0f)
+#define AL_FLANGER_MAX_DEPTH (1.0f)
+#define AL_FLANGER_DEFAULT_DEPTH (1.0f)
+
+#define AL_FLANGER_MIN_FEEDBACK (-1.0f)
+#define AL_FLANGER_MAX_FEEDBACK (1.0f)
+#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f)
+
+#define AL_FLANGER_MIN_DELAY (0.0f)
+#define AL_FLANGER_MAX_DELAY (0.004f)
+#define AL_FLANGER_DEFAULT_DELAY (0.002f)
+
+/* Frequency shifter effect */
+#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f)
+#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f)
+
+#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0)
+#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0)
+
+#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0)
+#define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1)
+#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2)
+
+#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0)
+#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2)
+#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0)
+
+/* Vocal morpher effect */
+#define AL_VOCAL_MORPHER_MIN_PHONEMEA (0)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEA (29)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEB (0)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEB (29)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10)
+
+#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24)
+#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24)
+#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0)
+
+#define AL_VOCAL_MORPHER_PHONEME_A (0)
+#define AL_VOCAL_MORPHER_PHONEME_E (1)
+#define AL_VOCAL_MORPHER_PHONEME_I (2)
+#define AL_VOCAL_MORPHER_PHONEME_O (3)
+#define AL_VOCAL_MORPHER_PHONEME_U (4)
+#define AL_VOCAL_MORPHER_PHONEME_AA (5)
+#define AL_VOCAL_MORPHER_PHONEME_AE (6)
+#define AL_VOCAL_MORPHER_PHONEME_AH (7)
+#define AL_VOCAL_MORPHER_PHONEME_AO (8)
+#define AL_VOCAL_MORPHER_PHONEME_EH (9)
+#define AL_VOCAL_MORPHER_PHONEME_ER (10)
+#define AL_VOCAL_MORPHER_PHONEME_IH (11)
+#define AL_VOCAL_MORPHER_PHONEME_IY (12)
+#define AL_VOCAL_MORPHER_PHONEME_UH (13)
+#define AL_VOCAL_MORPHER_PHONEME_UW (14)
+#define AL_VOCAL_MORPHER_PHONEME_B (15)
+#define AL_VOCAL_MORPHER_PHONEME_D (16)
+#define AL_VOCAL_MORPHER_PHONEME_F (17)
+#define AL_VOCAL_MORPHER_PHONEME_G (18)
+#define AL_VOCAL_MORPHER_PHONEME_J (19)
+#define AL_VOCAL_MORPHER_PHONEME_K (20)
+#define AL_VOCAL_MORPHER_PHONEME_L (21)
+#define AL_VOCAL_MORPHER_PHONEME_M (22)
+#define AL_VOCAL_MORPHER_PHONEME_N (23)
+#define AL_VOCAL_MORPHER_PHONEME_P (24)
+#define AL_VOCAL_MORPHER_PHONEME_R (25)
+#define AL_VOCAL_MORPHER_PHONEME_S (26)
+#define AL_VOCAL_MORPHER_PHONEME_T (27)
+#define AL_VOCAL_MORPHER_PHONEME_V (28)
+#define AL_VOCAL_MORPHER_PHONEME_Z (29)
+
+#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0)
+#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1)
+#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2)
+
+#define AL_VOCAL_MORPHER_MIN_WAVEFORM (0)
+#define AL_VOCAL_MORPHER_MAX_WAVEFORM (2)
+#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0)
+
+#define AL_VOCAL_MORPHER_MIN_RATE (0.0f)
+#define AL_VOCAL_MORPHER_MAX_RATE (10.0f)
+#define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f)
+
+/* Pitch shifter effect */
+#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12)
+#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12)
+#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12)
+
+#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50)
+#define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50)
+#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0)
+
+/* Ring modulator effect */
+#define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f)
+#define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f)
+#define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f)
+
+#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f)
+#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f)
+#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f)
+
+#define AL_RING_MODULATOR_SINUSOID (0)
+#define AL_RING_MODULATOR_SAWTOOTH (1)
+#define AL_RING_MODULATOR_SQUARE (2)
+
+#define AL_RING_MODULATOR_MIN_WAVEFORM (0)
+#define AL_RING_MODULATOR_MAX_WAVEFORM (2)
+#define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0)
+
+/* Autowah effect */
+#define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f)
+#define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f)
+#define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f)
+
+#define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f)
+#define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f)
+#define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f)
+
+#define AL_AUTOWAH_MIN_RESONANCE (2.0f)
+#define AL_AUTOWAH_MAX_RESONANCE (1000.0f)
+#define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f)
+
+#define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f)
+#define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f)
+#define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f)
+
+/* Compressor effect */
+#define AL_COMPRESSOR_MIN_ONOFF (0)
+#define AL_COMPRESSOR_MAX_ONOFF (1)
+#define AL_COMPRESSOR_DEFAULT_ONOFF (1)
+
+/* Equalizer effect */
+#define AL_EQUALIZER_MIN_LOW_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_LOW_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f)
+#define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f)
+#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f)
+
+#define AL_EQUALIZER_MIN_MID1_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_MID1_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_MID1_CENTER (200.0f)
+#define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f)
+#define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f)
+
+#define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f)
+#define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f)
+#define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f)
+
+#define AL_EQUALIZER_MIN_MID2_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_MID2_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f)
+#define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f)
+#define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f)
+
+#define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f)
+#define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f)
+#define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f)
+
+#define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f)
+#define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f)
+#define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f)
+
+#define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f)
+#define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f)
+#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f)
+
+
+/* Source parameter value ranges and defaults. */
+#define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f)
+#define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f)
+#define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f)
+
+#define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
+#define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
+#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+
+#define AL_MIN_CONE_OUTER_GAINHF (0.0f)
+#define AL_MAX_CONE_OUTER_GAINHF (1.0f)
+#define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f)
+
+#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE
+#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE
+#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE
+
+#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE
+#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
+#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
+
+#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE
+#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+
+
+/* Listener parameter value ranges and defaults. */
+#define AL_MIN_METERS_PER_UNIT FLT_MIN
+#define AL_MAX_METERS_PER_UNIT FLT_MAX
+#define AL_DEFAULT_METERS_PER_UNIT (1.0f)
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* AL_EFX_H */
diff --git a/src/external/openal_soft/lib/win32/OpenAL32.dll b/src/external/openal_soft/lib/win32/OpenAL32.dll
new file mode 100644
index 00000000..1e3bddd5
--- /dev/null
+++ b/src/external/openal_soft/lib/win32/OpenAL32.dll
Binary files differ
diff --git a/src/external/openal_soft/lib/win32/libOpenAL32.dll.a b/src/external/openal_soft/lib/win32/libOpenAL32.dll.a
new file mode 100644
index 00000000..1c4c63c8
--- /dev/null
+++ b/src/external/openal_soft/lib/win32/libOpenAL32.dll.a
Binary files differ
diff --git a/src/stb_image.h b/src/external/stb_image.h
index c28b5286..ce87646d 100644
--- a/src/stb_image.h
+++ b/src/external/stb_image.h
@@ -1,4 +1,4 @@
-/* stb_image - v2.09 - public domain image loader - http://nothings.org/stb_image.h
+/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h
no warranty implied; use at your own risk
Do this:
@@ -146,6 +146,12 @@
Latest revision history:
+ 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
+ 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64
+ RGB-format JPEG; remove white matting in PSD;
+ allocate large structures on the stack;
+ correct channel count for PNG & BMP
+ 2.10 (2016-01-22) avoid warning introduced in 2.09
2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
2.07 (2015-09-13) partial animated GIF support
@@ -166,10 +172,6 @@
STBI_MALLOC,STBI_REALLOC,STBI_FREE
STBI_NO_*, STBI_ONLY_*
GIF bugfix
- 1.48 (2014-12-14) fix incorrectly-named assert()
- 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted)
- optimize PNG
- fix bug in interlaced PNG with user-specified channel count
See end of file for full revision history.
@@ -200,17 +202,17 @@
Janez Zemva John Bartholomew Michal Cichon svdijk@github
Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson
Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github
- Aruelien Pocheville Thibault Reuille Cass Everitt
- Ryamond Barbiero Paul Du Bois Engin Manap
+ Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan
+ Ryamond Barbiero Paul Du Bois Engin Manap snagar@github
+ Michaelangel007@github Oriol Ferrer Mesia socks-the-fox
Blazej Dariusz Roszkowski
- Michaelangel007@github
LICENSE
-This software is in the public domain. Where that dedication is not
-recognized, you are granted a perpetual, irrevocable license to copy,
-distribute, and modify this file as you see fit.
+This software is dual-licensed to the public domain and under the following
+license: you are granted a perpetual, irrevocable license to copy, modify,
+publish, and distribute this file as you see fit.
*/
@@ -679,7 +681,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#define STBI_NO_SIMD
#endif
-#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET)
+#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
#define STBI_SSE2
#include <emmintrin.h>
@@ -1513,6 +1515,7 @@ typedef struct
int succ_high;
int succ_low;
int eob_run;
+ int rgb;
int scan_n, order[4];
int restart_interval, todo;
@@ -2724,11 +2727,17 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan)
if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG");
+ z->rgb = 0;
for (i=0; i < s->img_n; ++i) {
+ static unsigned char rgb[3] = { 'R', 'G', 'B' };
z->img_comp[i].id = stbi__get8(s);
if (z->img_comp[i].id != i+1) // JFIF requires
- if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files!
- return stbi__err("bad component ID","Corrupt JPEG");
+ if (z->img_comp[i].id != i) { // some version of jpegtran outputs non-JFIF-compliant files!
+ // somethings output this (see http://fileformats.archiveteam.org/wiki/JPEG#Color_format)
+ if (z->img_comp[i].id != rgb[i])
+ return stbi__err("bad component ID","Corrupt JPEG");
+ ++z->rgb;
+ }
q = stbi__get8(s);
z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG");
z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG");
@@ -3390,7 +3399,17 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
if (n >= 3) {
stbi_uc *y = coutput[0];
if (z->s->img_n == 3) {
- z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ if (z->rgb == 3) {
+ for (i=0; i < z->s->img_x; ++i) {
+ out[0] = y[i];
+ out[1] = coutput[1][i];
+ out[2] = coutput[2][i];
+ out[3] = 255;
+ out += n;
+ }
+ } else {
+ z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ }
} else
for (i=0; i < z->s->img_x; ++i) {
out[0] = out[1] = out[2] = y[i];
@@ -3415,10 +3434,13 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp
static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
- stbi__jpeg j;
- j.s = s;
- stbi__setup_jpeg(&j);
- return load_jpeg_image(&j, x,y,comp,req_comp);
+ unsigned char* result;
+ stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
+ j->s = s;
+ stbi__setup_jpeg(j);
+ result = load_jpeg_image(j, x,y,comp,req_comp);
+ STBI_FREE(j);
+ return result;
}
static int stbi__jpeg_test(stbi__context *s)
@@ -3446,9 +3468,12 @@ static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)
static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
{
- stbi__jpeg j;
- j.s = s;
- return stbi__jpeg_info_raw(&j, x, y, comp);
+ int result;
+ stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
+ j->s = s;
+ result = stbi__jpeg_info_raw(j, x, y, comp);
+ STBI_FREE(j);
+ return result;
}
#endif
@@ -3629,6 +3654,7 @@ static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room
while (cur + n > limit)
limit *= 2;
q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
+ STBI_NOTUSED(old_limit);
if (q == NULL) return stbi__err("outofmem", "Out of memory");
z->zout_start = q;
z->zout = q + cur;
@@ -3738,7 +3764,7 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a)
return 1;
}
-static int stbi__parse_uncomperssed_block(stbi__zbuf *a)
+static int stbi__parse_uncompressed_block(stbi__zbuf *a)
{
stbi_uc header[4];
int len,nlen,k;
@@ -3804,7 +3830,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
final = stbi__zreceive(a,1);
type = stbi__zreceive(a,2);
if (type == 0) {
- if (!stbi__parse_uncomperssed_block(a)) return 0;
+ if (!stbi__parse_uncompressed_block(a)) return 0;
} else if (type == 3) {
return 0;
} else {
@@ -3946,6 +3972,7 @@ typedef struct
{
stbi__context *s;
stbi_uc *idata, *expanded, *out;
+ int depth;
} stbi__png;
@@ -3985,14 +4012,19 @@ static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x0
// create the png data from post-deflated data
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
{
+ int bytes = (depth == 16? 2 : 1);
stbi__context *s = a->s;
- stbi__uint32 i,j,stride = x*out_n;
+ stbi__uint32 i,j,stride = x*out_n*bytes;
stbi__uint32 img_len, img_width_bytes;
int k;
int img_n = s->img_n; // copy it into a local for later
+ int output_bytes = out_n*bytes;
+ int filter_bytes = img_n*bytes;
+ int width = x;
+
STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
- a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into
+ a->out = (stbi_uc *) stbi__malloc(x * y * output_bytes); // extra bytes to write off the end into
if (!a->out) return stbi__err("outofmem", "Out of memory");
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
@@ -4007,8 +4039,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
stbi_uc *cur = a->out + stride*j;
stbi_uc *prior = cur - stride;
int filter = *raw++;
- int filter_bytes = img_n;
- int width = x;
+
if (filter > 4)
return stbi__err("invalid filter","Corrupt PNG");
@@ -4041,6 +4072,14 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
raw += img_n;
cur += out_n;
prior += out_n;
+ } else if (depth == 16) {
+ if (img_n != out_n) {
+ cur[filter_bytes] = 255; // first pixel top byte
+ cur[filter_bytes+1] = 255; // first pixel bottom byte
+ }
+ raw += filter_bytes;
+ cur += output_bytes;
+ prior += output_bytes;
} else {
raw += 1;
cur += 1;
@@ -4049,7 +4088,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
// this is a little gross, so that we don't switch per-pixel or per-component
if (depth < 8 || img_n == out_n) {
- int nk = (width - 1)*img_n;
+ int nk = (width - 1)*filter_bytes;
#define CASE(f) \
case f: \
for (k=0; k < nk; ++k)
@@ -4069,18 +4108,27 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
STBI_ASSERT(img_n+1 == out_n);
#define CASE(f) \
case f: \
- for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
- for (k=0; k < img_n; ++k)
+ for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
+ for (k=0; k < filter_bytes; ++k)
switch (filter) {
CASE(STBI__F_none) cur[k] = raw[k]; break;
- CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break;
+ CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); break;
CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
- CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break;
- CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;
- CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break;
- CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break;
+ CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); break;
+ CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); break;
+ CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); break;
+ CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); break;
}
#undef CASE
+
+ // the loop above sets the high byte of the pixels' alpha, but for
+ // 16 bit png files we also need the low byte set. we'll do that here.
+ if (depth == 16) {
+ cur = a->out + stride*j; // start at the beginning of the row again
+ for (i=0; i < x; ++i,cur+=output_bytes) {
+ cur[filter_bytes+1] = 255;
+ }
+ }
}
}
@@ -4156,6 +4204,17 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
}
}
}
+ } else if (depth == 16) {
+ // force the image data from big-endian to platform-native.
+ // this is done in a separate pass due to the decoding relying
+ // on the data being untouched, but could probably be done
+ // per-line during decode if care is taken.
+ stbi_uc *cur = a->out;
+ stbi__uint16 *cur16 = (stbi__uint16*)cur;
+
+ for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
+ *cur16 = (cur[0] << 8) | cur[1];
+ }
}
return 1;
@@ -4228,6 +4287,31 @@ static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)
return 1;
}
+static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)
+{
+ stbi__context *s = z->s;
+ stbi__uint32 i, pixel_count = s->img_x * s->img_y;
+ stbi__uint16 *p = (stbi__uint16*) z->out;
+
+ // compute color-based transparency, assuming we've
+ // already got 65535 as the alpha value in the output
+ STBI_ASSERT(out_n == 2 || out_n == 4);
+
+ if (out_n == 2) {
+ for (i = 0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 65535);
+ p += 2;
+ }
+ } else {
+ for (i = 0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)
{
stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
@@ -4265,6 +4349,26 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int
return 1;
}
+static int stbi__reduce_png(stbi__png *p)
+{
+ int i;
+ int img_len = p->s->img_x * p->s->img_y * p->s->img_out_n;
+ stbi_uc *reduced;
+ stbi__uint16 *orig = (stbi__uint16*)p->out;
+
+ if (p->depth != 16) return 1; // don't need to do anything if not 16-bit data
+
+ reduced = (stbi_uc *)stbi__malloc(img_len);
+ if (p == NULL) return stbi__err("outofmem", "Out of memory");
+
+ for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is a decent approx of 16->8 bit scaling
+
+ p->out = reduced;
+ STBI_FREE(orig);
+
+ return 1;
+}
+
static int stbi__unpremultiply_on_load = 0;
static int stbi__de_iphone_flag = 0;
@@ -4326,8 +4430,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
{
stbi_uc palette[1024], pal_img_n=0;
stbi_uc has_trans=0, tc[3];
+ stbi__uint16 tc16[3];
stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
- int first=1,k,interlace=0, color=0, depth=0, is_iphone=0;
+ int first=1,k,interlace=0, color=0, is_iphone=0;
stbi__context *s = z->s;
z->expanded = NULL;
@@ -4352,8 +4457,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
- depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only");
+ z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
+ if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG");
if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG");
filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
@@ -4401,8 +4507,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
has_trans = 1;
- for (k=0; k < s->img_n; ++k)
- tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger
+ if (z->depth == 16) {
+ for (k = 0; k < s->img_n; ++k) tc16[k] = stbi__get16be(s); // copy the values as-is
+ } else {
+ for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
+ }
}
break;
}
@@ -4418,6 +4527,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
while (ioff + c.length > idata_limit)
idata_limit *= 2;
+ STBI_NOTUSED(idata_limit_old);
p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
z->idata = p;
}
@@ -4432,7 +4542,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (scan != STBI__SCAN_load) return 1;
if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
// initial guess for decoded data size to avoid unnecessary reallocs
- bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component
+ bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component
raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
if (z->expanded == NULL) return 0; // zlib should set error
@@ -4441,9 +4551,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
s->img_out_n = s->img_n+1;
else
s->img_out_n = s->img_n;
- if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0;
- if (has_trans)
- if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
+ if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
+ if (has_trans) {
+ if (z->depth == 16) {
+ if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
+ } else {
+ if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
+ }
+ }
if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
stbi__de_iphone(z);
if (pal_img_n) {
@@ -4485,6 +4600,11 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req
unsigned char *result=NULL;
if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
+ if (p->depth == 16) {
+ if (!stbi__reduce_png(p)) {
+ return result;
+ }
+ }
result = p->out;
p->out = NULL;
if (req_comp && req_comp != p->s->img_out_n) {
@@ -4494,7 +4614,7 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req
}
*x = p->s->img_x;
*y = p->s->img_y;
- if (n) *n = p->s->img_out_n;
+ if (n) *n = p->s->img_n;
}
STBI_FREE(p->out); p->out = NULL;
STBI_FREE(p->expanded); p->expanded = NULL;
@@ -4619,6 +4739,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
stbi__get16le(s); // discard reserved
info->offset = stbi__get32le(s);
info->hsz = hsz = stbi__get32le(s);
+ info->mr = info->mg = info->mb = info->ma = 0;
if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
if (hsz == 12) {
@@ -4647,7 +4768,6 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
stbi__get32le(s);
}
if (info->bpp == 16 || info->bpp == 32) {
- info->mr = info->mg = info->mb = 0;
if (compress == 0) {
if (info->bpp == 32) {
info->mr = 0xffu << 16;
@@ -5342,6 +5462,21 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int
}
}
+ if (channelCount >= 4) {
+ for (i=0; i < w*h; ++i) {
+ unsigned char *pixel = out + 4*i;
+ if (pixel[3] != 0 && pixel[3] != 255) {
+ // remove weird white matte from PSD
+ float a = pixel[3] / 255.0f;
+ float ra = 1.0f / a;
+ float inv_a = 255.0f * (1 - ra);
+ pixel[0] = (unsigned char) (pixel[0]*ra + inv_a);
+ pixel[1] = (unsigned char) (pixel[1]*ra + inv_a);
+ pixel[2] = (unsigned char) (pixel[2]*ra + inv_a);
+ }
+ }
+ }
+
if (req_comp && req_comp != 4) {
out = stbi__convert_format(out, 4, req_comp, w, h);
if (out == NULL) return out; // stbi__convert_format frees input on failure
@@ -5654,13 +5789,15 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in
static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
{
- stbi__gif g;
- if (!stbi__gif_header(s, &g, comp, 1)) {
+ stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
+ if (!stbi__gif_header(s, g, comp, 1)) {
+ STBI_FREE(g);
stbi__rewind( s );
return 0;
}
- if (x) *x = g.w;
- if (y) *y = g.h;
+ if (x) *x = g->w;
+ if (y) *y = g->h;
+ STBI_FREE(g);
return 1;
}
@@ -5913,20 +6050,20 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
stbi_uc *u = 0;
- stbi__gif g;
- memset(&g, 0, sizeof(g));
+ stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
+ memset(g, 0, sizeof(*g));
- u = stbi__gif_load_next(s, &g, comp, req_comp);
+ u = stbi__gif_load_next(s, g, comp, req_comp);
if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
if (u) {
- *x = g.w;
- *y = g.h;
+ *x = g->w;
+ *y = g->h;
if (req_comp && req_comp != 4)
- u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
+ u = stbi__convert_format(u, 4, req_comp, g->w, g->h);
}
- else if (g.out)
- STBI_FREE(g.out);
-
+ else if (g->out)
+ STBI_FREE(g->out);
+ STBI_FREE(g);
return u;
}
@@ -6464,6 +6601,14 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
/*
revision history:
+ 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
+ 2.11 (2016-04-02) allocate large structures on the stack
+ remove white matting for transparent PSD
+ fix reported channel count for PNG & BMP
+ re-enable SSE2 in non-gcc 64-bit
+ support RGB-formatted JPEG
+ read 16-bit PNGs (only as 8-bit)
+ 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED
2.09 (2016-01-16) allow comments in PNM files
16-bit-per-pixel TGA (not bit-per-component)
info() for TGA could break due to .hdr handling
diff --git a/src/stb_image_resize.h b/src/external/stb_image_resize.h
index 4ce7ddbf..4cabe540 100644
--- a/src/stb_image_resize.h
+++ b/src/external/stb_image_resize.h
@@ -1,4 +1,4 @@
-/* stb_image_resize - v0.90 - public domain image resizing
+/* stb_image_resize - v0.91 - public domain image resizing
by Jorge L Rodriguez (@VinoBS) - 2014
http://github.com/nothings/stb
@@ -156,13 +156,14 @@
Sean Barrett: API design, optimizations
REVISIONS
+ 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions
0.90 (2014-09-17) first released version
LICENSE
- This software is in the public domain. Where that dedication is not
- recognized, you are granted a perpetual, irrevocable license to copy,
- distribute, and modify this file as you see fit.
+ This software is dual-licensed to the public domain and under the following
+ license: you are granted a perpetual, irrevocable license to copy, modify,
+ publish, and distribute this file as you see fit.
TODO
Don't decode all of the image data when only processing a partial tile
@@ -383,15 +384,6 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int
#define STBIR_ASSERT(x) assert(x)
#endif
-#ifdef STBIR_DEBUG
-#define STBIR__DEBUG_ASSERT STBIR_ASSERT
-#else
-#define STBIR__DEBUG_ASSERT
-#endif
-
-// If you hit this it means I haven't done it yet.
-#define STBIR__UNIMPLEMENTED(x) STBIR_ASSERT(!(x))
-
// For memset
#include <string.h>
@@ -758,7 +750,7 @@ static float stbir__filter_trapezoid(float x, float scale)
{
float halfscale = scale / 2;
float t = 0.5f + halfscale;
- STBIR__DEBUG_ASSERT(scale <= 1);
+ STBIR_ASSERT(scale <= 1);
x = (float)fabs(x);
@@ -776,7 +768,7 @@ static float stbir__filter_trapezoid(float x, float scale)
static float stbir__support_trapezoid(float scale)
{
- STBIR__DEBUG_ASSERT(scale <= 1);
+ STBIR_ASSERT(scale <= 1);
return 0.5f + scale / 2;
}
@@ -990,7 +982,7 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max)
return n; // NOTREACHED
default:
- STBIR__UNIMPLEMENTED("Unimplemented edge type");
+ STBIR_ASSERT(!"Unimplemented edge type");
return 0;
}
}
@@ -1039,12 +1031,12 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi
float total_filter = 0;
float filter_scale;
- STBIR__DEBUG_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
+ STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
contributor->n0 = in_first_pixel;
contributor->n1 = in_last_pixel;
- STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0);
+ STBIR_ASSERT(contributor->n1 >= contributor->n0);
for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
{
@@ -1062,10 +1054,10 @@ static void stbir__calculate_coefficients_upsample(stbir__info* stbir_info, stbi
total_filter += coefficient_group[i];
}
- STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0);
+ STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0);
- STBIR__DEBUG_ASSERT(total_filter > 0.9);
- STBIR__DEBUG_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
+ STBIR_ASSERT(total_filter > 0.9);
+ STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
// Make sure the sum of all coefficients is 1.
filter_scale = 1 / total_filter;
@@ -1087,12 +1079,12 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st
{
int i;
- STBIR__DEBUG_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
+ STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical.
contributor->n0 = out_first_pixel;
contributor->n1 = out_last_pixel;
- STBIR__DEBUG_ASSERT(contributor->n1 >= contributor->n0);
+ STBIR_ASSERT(contributor->n1 >= contributor->n0);
for (i = 0; i <= out_last_pixel - out_first_pixel; i++)
{
@@ -1101,7 +1093,7 @@ static void stbir__calculate_coefficients_downsample(stbir__info* stbir_info, st
coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
}
- STBIR__DEBUG_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
+ STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0);
for (i = out_last_pixel - out_first_pixel; i >= 0; i--)
{
@@ -1136,8 +1128,8 @@ static void stbir__normalize_downsample_coefficients(stbir__info* stbir_info, st
break;
}
- STBIR__DEBUG_ASSERT(total > 0.9f);
- STBIR__DEBUG_ASSERT(total < 1.1f);
+ STBIR_ASSERT(total > 0.9f);
+ STBIR_ASSERT(total < 1.1f);
scale = 1 / total;
@@ -1364,7 +1356,7 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n)
break;
default:
- STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination.");
+ STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
break;
}
@@ -1425,7 +1417,7 @@ static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n)
else
{
ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline) + 1) % stbir_info->vertical_filter_pixel_width;
- STBIR__DEBUG_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index);
+ STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index);
}
ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float));
@@ -1457,11 +1449,11 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
int coefficient_group = coefficient_width * x;
int coefficient_counter = 0;
- STBIR__DEBUG_ASSERT(n1 >= n0);
- STBIR__DEBUG_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin);
- STBIR__DEBUG_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin);
- STBIR__DEBUG_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
- STBIR__DEBUG_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
+ STBIR_ASSERT(n1 >= n0);
+ STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin);
+ STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin);
+ STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
+ STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin);
switch (channels) {
case 1:
@@ -1469,7 +1461,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
{
int in_pixel_index = k * 1;
float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
}
break;
@@ -1478,7 +1470,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
{
int in_pixel_index = k * 2;
float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
}
@@ -1488,7 +1480,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
{
int in_pixel_index = k * 3;
float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1499,7 +1491,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
{
int in_pixel_index = k * 4;
float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1512,7 +1504,7 @@ static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, int n,
int in_pixel_index = k * channels;
float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++];
int c;
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
for (c = 0; c < channels; c++)
output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
}
@@ -1535,7 +1527,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin;
int max_x = input_w + filter_pixel_margin * 2;
- STBIR__DEBUG_ASSERT(!stbir__use_width_upsampling(stbir_info));
+ STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info));
switch (channels) {
case 1:
@@ -1553,7 +1545,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
{
int out_pixel_index = k * 1;
float coefficient = horizontal_coefficients[coefficient_group + k - n0];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
}
}
@@ -1574,7 +1566,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
{
int out_pixel_index = k * 2;
float coefficient = horizontal_coefficients[coefficient_group + k - n0];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
}
@@ -1596,7 +1588,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
{
int out_pixel_index = k * 3;
float coefficient = horizontal_coefficients[coefficient_group + k - n0];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1619,7 +1611,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
{
int out_pixel_index = k * 4;
float coefficient = horizontal_coefficients[coefficient_group + k - n0];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient;
output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient;
@@ -1644,7 +1636,7 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int n
int c;
int out_pixel_index = k * channels;
float coefficient = horizontal_coefficients[coefficient_group + k - n0];
- STBIR__DEBUG_ASSERT(coefficient != 0);
+ STBIR_ASSERT(coefficient != 0);
for (c = 0; c < channels; c++)
output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient;
}
@@ -1856,7 +1848,7 @@ static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void
break;
default:
- STBIR__UNIMPLEMENTED("Unknown type/colorspace/channels combination.");
+ STBIR_ASSERT(!"Unknown type/colorspace/channels combination.");
break;
}
}
@@ -1893,7 +1885,7 @@ static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n, in
output_row_start = n * stbir_info->output_stride_bytes;
- STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info));
+ STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
memset(encode_buffer, 0, output_w * sizeof(float) * channels);
@@ -2003,7 +1995,7 @@ static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n,
n0 = vertical_contributors[contributor].n0;
n1 = vertical_contributors[contributor].n1;
- STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info));
+ STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
for (k = n0; k <= n1; k++)
{
@@ -2068,7 +2060,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
float scale_ratio = stbir_info->vertical_scale;
float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio;
- STBIR__DEBUG_ASSERT(stbir__use_height_upsampling(stbir_info));
+ STBIR_ASSERT(stbir__use_height_upsampling(stbir_info));
for (y = 0; y < stbir_info->output_h; y++)
{
@@ -2077,7 +2069,7 @@ static void stbir__buffer_loop_upsample(stbir__info* stbir_info)
stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out);
- STBIR__DEBUG_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width);
+ STBIR_ASSERT(in_last_scanline - in_first_scanline <= stbir_info->vertical_filter_pixel_width);
if (stbir_info->ring_buffer_begin_index >= 0)
{
@@ -2169,7 +2161,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
int pixel_margin = stbir_info->vertical_filter_pixel_margin;
int max_y = stbir_info->input_h + pixel_margin;
- STBIR__DEBUG_ASSERT(!stbir__use_height_upsampling(stbir_info));
+ STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info));
for (y = -pixel_margin; y < max_y; y++)
{
@@ -2178,7 +2170,7 @@ static void stbir__buffer_loop_downsample(stbir__info* stbir_info)
stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in);
- STBIR__DEBUG_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width);
+ STBIR_ASSERT(out_last_scanline - out_first_scanline <= stbir_info->vertical_filter_pixel_width);
if (out_last_scanline < 0 || out_first_scanline >= output_h)
continue;
@@ -2229,8 +2221,8 @@ static void stbir__calculate_transform(stbir__info *info, float s0, float t0, fl
info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0);
info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0);
- info->horizontal_shift = s0 * info->input_w / (s1 - s0);
- info->vertical_shift = t0 * info->input_h / (t1 - t0);
+ info->horizontal_shift = s0 * info->output_w / (s1 - s0);
+ info->vertical_shift = t0 * info->output_h / (t1 - t0);
}
}
@@ -2380,7 +2372,7 @@ static int stbir__resize_allocated(stbir__info *info,
info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float);
info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float);
- STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
+ STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
}
else
{
@@ -2388,7 +2380,7 @@ static int stbir__resize_allocated(stbir__info *info,
info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float);
info->encode_buffer = NULL;
- STBIR__DEBUG_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
+ STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes);
}
#undef STBIR__NEXT_MEMPTR
@@ -2409,10 +2401,10 @@ static int stbir__resize_allocated(stbir__info *info,
STBIR_PROGRESS_REPORT(1);
#ifdef STBIR_DEBUG_OVERWRITE_TEST
- STBIR__DEBUG_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
- STBIR__DEBUG_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0);
- STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
- STBIR__DEBUG_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0);
+ STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
+ STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0);
+ STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0);
+ STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0);
#endif
return 1;
diff --git a/src/stb_image_write.h b/src/external/stb_image_write.h
index 98fa4103..4319c0de 100644
--- a/src/stb_image_write.h
+++ b/src/external/stb_image_write.h
@@ -1,4 +1,4 @@
-/* stb_image_write - v1.01 - public domain - http://nothings.org/stb/stb_image_write.h
+/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h
writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015
no warranty implied; use at your own risk
@@ -102,12 +102,13 @@ CREDITS:
Sergio Gonzalez
Jonas Karlsson
Filip Wasil
+ Thatcher Ulrich
LICENSE
-This software is in the public domain. Where that dedication is not
-recognized, you are granted a perpetual, irrevocable license to copy,
-distribute, and modify this file as you see fit.
+This software is dual-licensed to the public domain and under the following
+license: you are granted a perpetual, irrevocable license to copy, modify,
+publish, and distribute this file as you see fit.
*/
@@ -736,7 +737,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
unsigned int bitbuf=0;
int i,j, bitcount=0;
unsigned char *out = NULL;
- unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack!
+ unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**));
if (quality < 5) quality = 5;
stbiw__sbpush(out, 0x78); // DEFLATE 32K window
@@ -808,6 +809,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
for (i=0; i < stbiw__ZHASH; ++i)
(void) stbiw__sbfree(hash_table[i]);
+ STBIW_FREE(hash_table);
{
// compute adler32 on input
@@ -1015,6 +1017,8 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x,
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history
+ 1.02 (2016-04-02)
+ avoid allocating large structures on the stack
1.01 (2016-01-16)
STBIW_REALLOC_SIZED: support allocators with no realloc support
avoid race-condition in crc initialization
diff --git a/src/stb_rect_pack.h b/src/external/stb_rect_pack.h
index 63a5b159..bd1cfc60 100644
--- a/src/stb_rect_pack.h
+++ b/src/external/stb_rect_pack.h
@@ -1,4 +1,4 @@
-// stb_rect_pack.h - v0.06 - public domain - rectangle packing
+// stb_rect_pack.h - v0.08 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
@@ -28,14 +28,22 @@
// Minor features
// Martins Mozeiko
// Bugfixes / warning fixes
-// [your name could be here]
+// Jeremy Jaussaud
//
// Version history:
//
+// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
+// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
+//
+// LICENSE
+//
+// This software is dual-licensed to the public domain and under the following
+// license: you are granted a perpetual, irrevocable license to copy, modify,
+// publish, and distribute this file as you see fit.
//////////////////////////////////////////////////////////////////////////////
//
@@ -541,12 +549,16 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
- stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
- if (fr.prev_link) {
- rects[i].x = (stbrp_coord) fr.x;
- rects[i].y = (stbrp_coord) fr.y;
+ if (rects[i].w == 0 || rects[i].h == 0) {
+ rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
- rects[i].x = rects[i].y = STBRP__MAXVAL;
+ stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
+ if (fr.prev_link) {
+ rects[i].x = (stbrp_coord) fr.x;
+ rects[i].y = (stbrp_coord) fr.y;
+ } else {
+ rects[i].x = rects[i].y = STBRP__MAXVAL;
+ }
}
}
diff --git a/src/stb_truetype.h b/src/external/stb_truetype.h
index bfb1841f..d360d609 100644
--- a/src/stb_truetype.h
+++ b/src/external/stb_truetype.h
@@ -1,4 +1,4 @@
-// stb_truetype.h - v1.09 - public domain
+// stb_truetype.h - v1.11 - public domain
// authored from 2009-2015 by Sean Barrett / RAD Game Tools
//
// This library processes TrueType files:
@@ -21,6 +21,10 @@
// Mikko Mononen: compound shape support, more cmap formats
// Tor Andersson: kerning, subpixel rendering
//
+// Misc other:
+// Ryan Gordon
+// Simon Glass
+//
// Bug/warning reports/fixes:
// "Zer" on mollyrocket (with fix)
// Cass Everitt
@@ -45,11 +49,10 @@
// Thomas Fields
// Derek Vinyard
//
-// Misc other:
-// Ryan Gordon
-//
// VERSION HISTORY
//
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
@@ -68,9 +71,9 @@
//
// LICENSE
//
-// This software is in the public domain. Where that dedication is not
-// recognized, you are granted a perpetual, irrevocable license to copy,
-// distribute, and modify this file as you see fit.
+// This software is dual-licensed to the public domain and under the following
+// license: you are granted a perpetual, irrevocable license to copy, modify,
+// publish, and distribute this file as you see fit.
//
// USAGE
//
@@ -406,6 +409,11 @@ int main(int arg, char **argv)
#define STBTT_sqrt(x) sqrt(x)
#endif
+ #ifndef STBTT_fabs
+ #include <math.h>
+ #define STBTT_fabs(x) fabs(x)
+ #endif
+
// #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
#ifndef STBTT_malloc
#include <stdlib.h>
@@ -629,7 +637,7 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
// The following structure is defined publically so you can declare one on
// the stack or as a global or etc, but you should treat it as opaque.
-typedef struct stbtt_fontinfo
+struct stbtt_fontinfo
{
void * userdata;
unsigned char * data; // pointer to .ttf file
@@ -640,7 +648,7 @@ typedef struct stbtt_fontinfo
int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
int index_map; // a cmap mapping for our chosen character encoding
int indexToLocFormat; // format needed to map from glyph index to glyph
-} stbtt_fontinfo;
+};
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
// Given an offset into the file that defines a font, this function builds
@@ -942,6 +950,12 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS
#define STBTT_RASTERIZER_VERSION 2
#endif
+#ifdef _MSC_VER
+#define STBTT__NOTUSED(v) (void)(v)
+#else
+#define STBTT__NOTUSED(v) (void)sizeof(v)
+#endif
+
//////////////////////////////////////////////////////////////////////////
//
// accessors to parse data from file
@@ -1993,7 +2007,7 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
}
y_crossing += dy * (x2 - (x1+1));
- STBTT_assert(fabs(area) <= 1.01f);
+ STBTT_assert(STBTT_fabs(area) <= 1.01f);
scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
@@ -2071,6 +2085,8 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
int y,j=0, i;
float scanline_data[129], *scanline, *scanline2;
+ STBTT__NOTUSED(vsubsample);
+
if (result->w > 64)
scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
else
@@ -2129,7 +2145,7 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e,
int m;
sum += scanline2[i];
k = scanline[i] + sum;
- k = (float) fabs(k)*255 + 0.5f;
+ k = (float) STBTT_fabs(k)*255 + 0.5f;
m = (int) k;
if (m > 255) m = 255;
result->pixels[j*result->stride + i] = (unsigned char) m;
@@ -2430,7 +2446,10 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info
if (scale_x == 0) scale_x = scale_y;
if (scale_y == 0) {
- if (scale_x == 0) return NULL;
+ if (scale_x == 0) {
+ STBTT_free(vertices, info->userdata);
+ return NULL;
+ }
scale_y = scale_x;
}
@@ -2586,11 +2605,6 @@ STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int
//
#ifndef STB_RECT_PACK_VERSION
-#ifdef _MSC_VER
-#define STBTT__NOTUSED(v) (void)(v)
-#else
-#define STBTT__NOTUSED(v) (void)sizeof(v)
-#endif
typedef int stbrp_coord;
@@ -3205,6 +3219,10 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const
// FULL VERSION HISTORY
//
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) allow user-defined fabs() replacement
+// fix memory leak if fontsize=0.0
+// fix warning from duplicate typedef
// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
diff --git a/src/stb_vorbis.c b/src/external/stb_vorbis.c
index 0510edc7..07d79318 100644
--- a/src/stb_vorbis.c
+++ b/src/external/stb_vorbis.c
@@ -168,6 +168,9 @@
#include <math.h>
#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh))
#include <malloc.h>
+#if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__)
+#include <alloca.h>
+#endif
#endif
#else // STB_VORBIS_NO_CRT
#define NULL 0
@@ -1484,85 +1487,6 @@ static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **out
return TRUE;
}
-#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
-static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **outputs, int *c_inter_p, int *p_inter_p, int len, int total_decode)
-{
- int c_inter = *c_inter_p;
- int p_inter = *p_inter_p;
- int i,z, effective = c->dimensions;
-
- // type 0 is only legal in a scalar context
- if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream);
-
- while (total_decode > 0) {
- float last = CODEBOOK_ELEMENT_BASE(c);
- DECODE_VQ(z,f,c);
-
- if (z < 0) {
- if (!f->bytes_in_seg)
- if (f->last_seg) return FALSE;
- return error(f, VORBIS_invalid_stream);
- }
-
- // if this will take us off the end of the buffers, stop short!
- // we check by computing the length of the virtual interleaved
- // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter),
- // and the length we'll be using (effective)
- if (c_inter + p_inter*2 + effective > len * 2) {
- effective = len*2 - (p_inter*2 - c_inter);
- }
-
- {
- z *= c->dimensions;
- if (c->sequence_p) {
- // haven't optimized this case because I don't have any examples
- for (i=0; i < effective; ++i) {
- float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
- if (outputs[c_inter])
- outputs[c_inter][p_inter] += val;
- if (++c_inter == 2) { c_inter = 0; ++p_inter; }
- last = val;
- }
- } else {
- i=0;
- if (c_inter == 1 && i < effective) {
- float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
- if (outputs[c_inter])
- outputs[c_inter][p_inter] += val;
- c_inter = 0; ++p_inter;
- ++i;
- }
- {
- float *z0 = outputs[0];
- float *z1 = outputs[1];
- for (; i+1 < effective;) {
- float v0 = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
- float v1 = CODEBOOK_ELEMENT_FAST(c,z+i+1) + last;
- if (z0)
- z0[p_inter] += v0;
- if (z1)
- z1[p_inter] += v1;
- ++p_inter;
- i += 2;
- }
- }
- if (i < effective) {
- float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last;
- if (outputs[c_inter])
- outputs[c_inter][p_inter] += val;
- if (++c_inter == 2) { c_inter = 0; ++p_inter; }
- }
- }
- }
-
- total_decode -= effective;
- }
- *c_inter_p = c_inter;
- *p_inter_p = p_inter;
- return TRUE;
-}
-#endif
-
static int predict_point(int x, int x0, int x1, int y0, int y1)
{
int dy = y1 - y0;
@@ -1941,6 +1865,11 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int
}
done:
CHECK(f);
+ #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
+ temp_free(f,part_classdata);
+ #else
+ temp_free(f,classifications);
+ #endif
temp_alloc_restore(f,temp_alloc_point);
}
@@ -2586,6 +2515,7 @@ static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype)
}
}
+ temp_free(f,buf2);
temp_alloc_restore(f,save_point);
}
@@ -3499,7 +3429,6 @@ static int start_decoder(vorb *f)
}
}
}
- setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
c->lookup_type = 2;
}
else
@@ -3515,11 +3444,11 @@ static int start_decoder(vorb *f)
if (c->sequence_p)
last = val;
}
- setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values);
}
#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK
skip:;
#endif
+ setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values);
CHECK(f);
}
@@ -4045,7 +3974,7 @@ int stb_vorbis_decode_frame_pushdata(
while (get8_packet(f) != EOP)
if (f->eof) break;
*samples = 0;
- return f->stream - data;
+ return (int) (f->stream - data);
}
if (error == VORBIS_continued_packet_flag_invalid) {
if (f->previous_length == 0) {
@@ -4055,7 +3984,7 @@ int stb_vorbis_decode_frame_pushdata(
while (get8_packet(f) != EOP)
if (f->eof) break;
*samples = 0;
- return f->stream - data;
+ return (int) (f->stream - data);
}
}
// if we get an error while parsing, what to do?
@@ -4075,7 +4004,7 @@ int stb_vorbis_decode_frame_pushdata(
if (channels) *channels = f->channels;
*samples = len;
*output = f->outputs;
- return f->stream - data;
+ return (int) (f->stream - data);
}
stb_vorbis *stb_vorbis_open_pushdata(
@@ -4098,7 +4027,7 @@ stb_vorbis *stb_vorbis_open_pushdata(
f = vorbis_alloc(&p);
if (f) {
*f = p;
- *data_used = f->stream - data;
+ *data_used = (int) (f->stream - data);
*error = 0;
return f;
} else {
@@ -4113,9 +4042,9 @@ unsigned int stb_vorbis_get_file_offset(stb_vorbis *f)
#ifndef STB_VORBIS_NO_PUSHDATA_API
if (f->push_mode) return 0;
#endif
- if (USE_MEMORY(f)) return f->stream - f->stream_start;
+ if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start);
#ifndef STB_VORBIS_NO_STDIO
- return ftell(f->f) - f->f_start;
+ return (unsigned int) (ftell(f->f) - f->f_start);
#endif
}
@@ -4611,7 +4540,7 @@ stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *er
stb_vorbis *f, p;
vorbis_init(&p, alloc);
p.f = file;
- p.f_start = ftell(file);
+ p.f_start = (uint32) ftell(file);
p.stream_len = length;
p.close_on_free = close_on_free;
if (start_decoder(&p)) {
@@ -4630,9 +4559,9 @@ stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *er
stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc)
{
unsigned int len, start;
- start = ftell(file);
+ start = (unsigned int) ftell(file);
fseek(file, 0, SEEK_END);
- len = ftell(file) - start;
+ len = (unsigned int) (ftell(file) - start);
fseek(file, start, SEEK_SET);
return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len);
}
@@ -5027,6 +4956,9 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
#endif // STB_VORBIS_NO_PULLDATA_API
/* Version history
+ 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version
+ 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks;
+ avoid discarding last frame of audio data
1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API
some more crash fixes when out of memory or with corrupt files
1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
diff --git a/src/stb_vorbis.h b/src/external/stb_vorbis.h
index bd318972..624ce4bc 100644
--- a/src/stb_vorbis.h
+++ b/src/external/stb_vorbis.h
@@ -1,4 +1,4 @@
-// Ogg Vorbis audio decoder - v1.07 - public domain
+// Ogg Vorbis audio decoder - v1.09 - public domain
// http://nothings.org/stb_vorbis/
//
// Original version written by Sean Barrett in 2007.
@@ -9,9 +9,9 @@
//
// LICENSE
//
-// This software is in the public domain. Where that dedication is not
-// recognized, you are granted a perpetual, irrevocable license to copy,
-// distribute, and modify this file as you see fit.
+// This software is dual-licensed to the public domain and under the following
+// license: you are granted a perpetual, irrevocable license to copy, modify,
+// publish, and distribute this file as you see fit.
//
// No warranty for any purpose is expressed or implied by the author (nor
// by RAD Game Tools). Report bugs and send enhancements to the author.
@@ -31,11 +31,14 @@
// Terje Mathisen Niklas Frykholm Andy Hill
// Casey Muratori John Bolton Gargaj
// Laurent Gomila Marc LeBlanc Ronny Chevalier
-// Bernhard Wodo Evan Balster "alxprd"@github
+// Bernhard Wodo Evan Balster alxprd@github
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
-// Phillip Bennefall Rohit
+// Phillip Bennefall Rohit Thiago Goulart
+// manxorist@github saga musix
//
// Partial history:
+// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version
+// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame
// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const
// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson)
// some crash fixes when out of memory or with corrupt files
@@ -72,11 +75,6 @@
#include "utils.h" // Android fopen function map
#endif
-// RaySan: Added for Linux
-#ifdef __linux
- #include <alloca.h>
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/src/tinfl.c b/src/external/tinfl.c
index a17a156b..a17a156b 100644
--- a/src/tinfl.c
+++ b/src/external/tinfl.c
diff --git a/src/gestures.c b/src/gestures.c
index 3638f23e..90371620 100644
--- a/src/gestures.c
+++ b/src/gestures.c
@@ -28,30 +28,30 @@
#if defined(GESTURES_STANDALONE)
#include "gestures.h"
#else
- #include "raylib.h" // Required for typedef(s): Vector2, Gestures
+ #include "raylib.h" // Required for: Vector2, Gestures
#endif
-#include <math.h> // Used for: atan2(), sqrt()
-#include <stdint.h> // Defines int32_t, int64_t
+#include <math.h> // Required for: atan2(), sqrt()
+#include <stdint.h> // Required for: uint64_t
#if defined(_WIN32)
// Functions required to query time on Windows
int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
#elif defined(__linux)
- #include <sys/time.h> // Declares storage size of ‘now’
- #include <time.h> // Used for clock functions
+ #include <sys/time.h> // Required for: timespec
+ #include <time.h> // Required for: clock_gettime()
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define FORCE_TO_SWIPE 0.0005f // Measured in normalized pixels / time
-#define MINIMUM_DRAG 0.015f // Measured in normalized pixels [0..1]
-#define MINIMUM_PINCH 0.005f // Measured in normalized pixels [0..1]
+#define FORCE_TO_SWIPE 0.0005f // Measured in normalized screen units/time
+#define MINIMUM_DRAG 0.015f // Measured in normalized screen units (0.0f to 1.0f)
+#define MINIMUM_PINCH 0.005f // Measured in normalized screen units (0.0f to 1.0f)
#define TAP_TIMEOUT 300 // Time in milliseconds
#define PINCH_TIMEOUT 300 // Time in milliseconds
-#define DOUBLETAP_RANGE 0.03f
+#define DOUBLETAP_RANGE 0.03f // Measured in normalized screen units (0.0f to 1.0f)
//----------------------------------------------------------------------------------
// Types and Structures Definition
@@ -72,7 +72,7 @@ static Vector2 moveDownPosition2 = { 0.0f, 0.0f };
static int numTap = 0;
static int pointCount = 0;
-static int touchId = -1;
+static int firstTouchId = -1;
static double eventTime = 0.0;
static double swipeTime = 0.0;
@@ -120,9 +120,7 @@ void ProcessGestureEvent(GestureEvent event)
pointCount = event.pointCount; // Required on UpdateGestures()
if (pointCount < 2)
- {
- touchId = event.pointerId[0];
-
+ {
if (event.touchAction == TOUCH_DOWN)
{
numTap++; // Tap counter
@@ -145,6 +143,8 @@ void ProcessGestureEvent(GestureEvent event)
touchUpPosition = touchDownPosition;
eventTime = GetCurrentTime();
+ firstTouchId = event.pointerId[0];
+
dragVector = (Vector2){ 0.0f, 0.0f };
}
else if (event.touchAction == TOUCH_UP)
@@ -155,12 +155,10 @@ void ProcessGestureEvent(GestureEvent event)
dragDistance = Vector2Distance(touchDownPosition, touchUpPosition);
dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime));
- // TODO: Make getures detection resolution independant
-
startMoving = false;
// Detect GESTURE_SWIPE
- if ((dragIntensity > FORCE_TO_SWIPE) && (touchId == 0)) // RAY: why check (touchId == 0)???
+ if ((dragIntensity > FORCE_TO_SWIPE) && firstTouchId == event.pointerId[0])
{
// NOTE: Angle should be inverted in Y
dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition);
@@ -181,6 +179,7 @@ void ProcessGestureEvent(GestureEvent event)
}
touchDownDragPosition = (Vector2){ 0.0f, 0.0f };
+ pointCount = 0;
}
else if (event.touchAction == TOUCH_MOVE)
{
@@ -259,6 +258,7 @@ void ProcessGestureEvent(GestureEvent event)
pinchDistance = 0.0f;
pinchAngle = 0.0f;
pinchVector = (Vector2){ 0.0f, 0.0f };
+ pointCount = 0;
currentGesture = GESTURE_NONE;
}
@@ -292,14 +292,14 @@ void UpdateGestures(void)
}
// Check if a gesture have been detected
-bool IsGestureDetected(void)
+bool IsGestureDetected(int gesture)
{
- if ((enabledGestures & currentGesture) != GESTURE_NONE) return true;
+ if ((enabledGestures & currentGesture) == gesture) return true;
else return false;
}
// Check gesture type
-int GetGestureType(void)
+int GetGestureDetected(void)
{
// Get current gesture only if enabled
return (enabledGestures & currentGesture);
diff --git a/src/gestures.h b/src/gestures.h
index 5468eb54..f2bdaba4 100644
--- a/src/gestures.h
+++ b/src/gestures.h
@@ -92,8 +92,8 @@ extern "C" { // Prevents name mangling of functions
//----------------------------------------------------------------------------------
void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures
void UpdateGestures(void); // Update gestures detected (must be called every frame)
-bool IsGestureDetected(void); // Check if a gesture have been detected
-int GetGestureType(void); // Get latest detected gesture
+bool IsGestureDetected(int gesture); // Check if a gesture have been detected
+int GetGestureDetected(void); // Get latest detected gesture
void SetGesturesEnabled(unsigned int gestureFlags); // Enable a set of gestures using flags
int GetTouchPointsCount(void); // Get touch points count
diff --git a/src/libraylib.bc b/src/libraylib.bc
index 21039250..0b385eb8 100644
--- a/src/libraylib.bc
+++ b/src/libraylib.bc
Binary files differ
diff --git a/src/models.c b/src/models.c
index 8a36c279..15565c98 100644
--- a/src/models.c
+++ b/src/models.c
@@ -26,16 +26,16 @@
#include "raylib.h"
#if defined(PLATFORM_ANDROID)
- #include "utils.h" // Android fopen function map
+ #include "utils.h" // Android fopen function map
#endif
-#include <stdio.h> // Standard input/output functions, used to read model files data
-#include <stdlib.h> // Declares malloc() and free() for memory management
-#include <string.h> // Required for strcmp()
-#include <math.h> // Used for sin, cos, tan
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), fscanf(), feof(), rewind(), fgets()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strcmp()
+#include <math.h> // Required for: sin(), cos()
-#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
-#include "raymath.h" // Required for data type Matrix and Matrix functions
+#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
+#include "raymath.h" // Matrix data type and Matrix functions
//----------------------------------------------------------------------------------
// Defines and Macros
@@ -55,12 +55,45 @@ extern unsigned int whiteTexture;
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
-static Mesh LoadOBJ(const char *fileName);
+static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data
+static Material LoadMTL(const char *fileName); // Load MTL material data
+
+static Mesh GenMeshHeightmap(Image image, Vector3 size);
+static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize);
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
+// Draw a line in 3D world space
+void Draw3DLine(Vector3 startPos, Vector3 endPos, Color color)
+{
+ rlBegin(RL_LINES);
+ rlColor4ub(color.r, color.g, color.b, color.a);
+ rlVertex3f(startPos.x, startPos.y, startPos.z);
+ rlVertex3f(endPos.x, endPos.y, endPos.z);
+ rlEnd();
+}
+
+// Draw a circle in 3D world space
+void Draw3DCircle(Vector3 center, float radius, float rotationAngle, Vector3 rotation, Color color)
+{
+ rlPushMatrix();
+ rlTranslatef(center.x, center.y, center.z);
+ rlRotatef(rotationAngle, rotation.x, rotation.y, rotation.z);
+
+ rlBegin(RL_LINES);
+ for (int i = 0; i < 360; i += 10)
+ {
+ rlColor4ub(color.r, color.g, color.b, color.a);
+
+ rlVertex3f(sin(DEG2RAD*i)*radius, cos(DEG2RAD*i)*radius, 0.0f);
+ rlVertex3f(sin(DEG2RAD*(i + 10)) * radius, cos(DEG2RAD*(i + 10)) * radius, 0.0f);
+ }
+ rlEnd();
+ rlPopMatrix();
+}
+
// Draw cube
// NOTE: Cube position is the center position
void DrawCube(Vector3 position, float width, float height, float length, Color color)
@@ -288,9 +321,9 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
- for(int i = 0; i < (rings + 2); i++)
+ for (int i = 0; i < (rings + 2); i++)
{
- for(int j = 0; j < slices; j++)
+ for (int j = 0; j < slices; j++)
{
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
@@ -327,9 +360,9 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
- for(int i = 0; i < (rings + 2); i++)
+ for (int i = 0; i < (rings + 2); i++)
{
- for(int j = 0; j < slices; j++)
+ for (int j = 0; j < slices; j++)
{
rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
@@ -372,7 +405,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
if (radiusTop > 0)
{
// Draw Body -------------------------------------------------------------------------------------
- for(int i = 0; i < 360; i += 360/sides)
+ for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); //Bottom Left
rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom); //Bottom Right
@@ -384,7 +417,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
}
// Draw Cap --------------------------------------------------------------------------------------
- for(int i = 0; i < 360; i += 360/sides)
+ for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, height, 0);
rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop);
@@ -394,7 +427,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
else
{
// Draw Cone -------------------------------------------------------------------------------------
- for(int i = 0; i < 360; i += 360/sides)
+ for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, height, 0);
rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom);
@@ -403,7 +436,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
}
// Draw Base -----------------------------------------------------------------------------------------
- for(int i = 0; i < 360; i += 360/sides)
+ for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, 0, 0);
rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom);
@@ -417,7 +450,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
// NOTE: It could be also used for pyramid and cone
void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
{
- if(sides < 3) sides = 3;
+ if (sides < 3) sides = 3;
rlPushMatrix();
rlTranslatef(position.x, position.y, position.z);
@@ -425,7 +458,7 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
- for(int i = 0; i < 360; i += 360/sides)
+ for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom);
rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom);
@@ -446,41 +479,24 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl
// Draw a plane
void DrawPlane(Vector3 centerPos, Vector2 size, Color color)
{
- // NOTE: QUADS usage require defining a texture on OpenGL 3.3+
- if (rlGetVersion() != OPENGL_11) rlEnableTexture(whiteTexture); // Default white texture
-
// NOTE: Plane is always created on XZ ground
rlPushMatrix();
rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
rlScalef(size.x, 1.0f, size.y);
- rlBegin(RL_QUADS);
+ rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlNormal3f(0.0f, 1.0f, 0.0f);
- rlTexCoord2f(0.0f, 0.0f); rlVertex3f(-0.5f, 0.0f, -0.5f);
- rlTexCoord2f(1.0f, 0.0f); rlVertex3f(-0.5f, 0.0f, 0.5f);
- rlTexCoord2f(1.0f, 1.0f); rlVertex3f(0.5f, 0.0f, 0.5f);
- rlTexCoord2f(0.0f, 1.0f); rlVertex3f(0.5f, 0.0f, -0.5f);
- rlEnd();
- rlPopMatrix();
- if (rlGetVersion() != OPENGL_11) rlDisableTexture();
-}
-
-// Draw a quad
-void DrawQuad(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Color color)
-{
- // TODO: Calculate normals from vertex position
-
- rlBegin(RL_QUADS);
- rlColor4ub(color.r, color.g, color.b, color.a);
- //rlNormal3f(0.0f, 0.0f, 0.0f);
+ rlVertex3f(0.5f, 0.0f, -0.5f);
+ rlVertex3f(-0.5f, 0.0f, -0.5f);
+ rlVertex3f(-0.5f, 0.0f, 0.5f);
- rlVertex3f(v1.x, v1.y, v1.z);
- rlVertex3f(v2.x, v2.y, v2.z);
- rlVertex3f(v3.x, v3.y, v3.z);
- rlVertex3f(v4.x, v4.y, v4.z);
- rlEnd();
+ rlVertex3f(-0.5f, 0.0f, 0.5f);
+ rlVertex3f(0.5f, 0.0f, 0.5f);
+ rlVertex3f(0.5f, 0.0f, -0.5f);
+ rlEnd();
+ rlPopMatrix();
}
// Draw a ray line
@@ -503,7 +519,7 @@ void DrawGrid(int slices, float spacing)
int halfSlices = slices / 2;
rlBegin(RL_LINES);
- for(int i = -halfSlices; i <= halfSlices; i++)
+ for (int i = -halfSlices; i <= halfSlices; i++)
{
if (i == 0)
{
@@ -553,66 +569,258 @@ void DrawGizmo(Vector3 position)
rlPopMatrix();
}
+
+// Draw light in 3D world
+void DrawLight(Light light)
+{
+ switch (light->type)
+ {
+ case LIGHT_POINT:
+ {
+ DrawSphereWires(light->position, 0.3f*light->intensity, 4, 8, (light->enabled ? light->diffuse : BLACK));
+ Draw3DCircle(light->position, light->radius, 0.0f, (Vector3){ 0, 0, 0 }, (light->enabled ? light->diffuse : BLACK));
+ Draw3DCircle(light->position, light->radius, 90.0f, (Vector3){ 1, 0, 0 }, (light->enabled ? light->diffuse : BLACK));
+ Draw3DCircle(light->position, light->radius, 90.0f, (Vector3){ 0, 1, 0 }, (light->enabled ? light->diffuse : BLACK));
+ } break;
+ case LIGHT_DIRECTIONAL:
+ {
+ Draw3DLine(light->position, light->target, (light->enabled ? light->diffuse : BLACK));
+ DrawSphereWires(light->position, 0.3f*light->intensity, 4, 8, (light->enabled ? light->diffuse : BLACK));
+ DrawCubeWires(light->target, 0.3f, 0.3f, 0.3f, (light->enabled ? light->diffuse : BLACK));
+ } break;
+ case LIGHT_SPOT:
+ {
+ Draw3DLine(light->position, light->target, (light->enabled ? light->diffuse : BLACK));
+ DrawCylinderWires(light->position, 0.0f, 0.3f*light->coneAngle/50, 0.6f, 5, (light->enabled ? light->diffuse : BLACK));
+ DrawCubeWires(light->target, 0.3f, 0.3f, 0.3f, (light->enabled ? light->diffuse : BLACK));
+ } break;
+ default: break;
+ }
+}
+
// Load a 3d model (from file)
Model LoadModel(const char *fileName)
{
Model model = { 0 };
- Mesh mesh = { 0 };
- // NOTE: Initialize default data for model in case loading fails, maybe a cube?
+ // TODO: Initialize default data for model in case loading fails, maybe a cube?
- if (strcmp(GetExtension(fileName),"obj") == 0) mesh = LoadOBJ(fileName);
+ if (strcmp(GetExtension(fileName), "obj") == 0) model.mesh = LoadOBJ(fileName);
else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName);
- // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct
-
- if (mesh.vertexCount == 0)
- {
- TraceLog(WARNING, "Model could not be loaded");
- }
+ if (model.mesh.vertexCount == 0) TraceLog(WARNING, "Model could not be loaded");
else
{
- // NOTE: model properties (transform, texture, shader) are initialized inside rlglLoadModel()
- model = rlglLoadModel(mesh); // Upload vertex data to GPU
-
- // Now that vertex data is uploaded to GPU, we can free arrays
- // NOTE 1: We don't need CPU vertex data on OpenGL 3.3 or ES2... for static meshes...
- // NOTE 2: ...but we could keep CPU vertex data in case we need to update the mesh
+ rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model)
- /*
- if (rlGetVersion() != OPENGL_11)
- {
- free(mesh.vertices);
- free(mesh.texcoords);
- free(mesh.normals);
- free(mesh.colors);
- }
- */
+ model.transform = MatrixIdentity();
+ model.material = LoadDefaultMaterial();
}
return model;
}
// Load a 3d model (from vertex data)
-Model LoadModelEx(Mesh data)
+Model LoadModelEx(Mesh data, bool dynamic)
{
- Model model;
+ Model model = { 0 };
- // NOTE: model properties (transform, texture, shader) are initialized inside rlglLoadModel()
- model = rlglLoadModel(data); // Upload vertex data to GPU
+ model.mesh = data;
+
+ rlglLoadMesh(&model.mesh, dynamic); // Upload vertex data to GPU
- // NOTE: Vertex data is managed externally, must be deallocated manually
+ model.transform = MatrixIdentity();
+ model.material = LoadDefaultMaterial();
return model;
}
+// Load a 3d model from rRES file (raylib Resource)
+Model LoadModelFromRES(const char *rresName, int resId)
+{
+ Model model = { 0 };
+ bool found = false;
+
+ char id[4]; // rRES file identifier
+ unsigned char version; // rRES file version and subversion
+ char useless; // rRES header reserved data
+ short numRes;
+
+ ResInfoHeader infoHeader;
+
+ FILE *rresFile = fopen(rresName, "rb");
+
+ if (rresFile == NULL)
+ {
+ TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
+ }
+ else
+ {
+ // Read rres file (basic file check - id)
+ fread(&id[0], sizeof(char), 1, rresFile);
+ fread(&id[1], sizeof(char), 1, rresFile);
+ fread(&id[2], sizeof(char), 1, rresFile);
+ fread(&id[3], sizeof(char), 1, rresFile);
+ fread(&version, sizeof(char), 1, rresFile);
+ fread(&useless, sizeof(char), 1, rresFile);
+
+ if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
+ {
+ TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName);
+ }
+ else
+ {
+ // Read number of resources embedded
+ fread(&numRes, sizeof(short), 1, rresFile);
+
+ for (int i = 0; i < numRes; i++)
+ {
+ fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile);
+
+ if (infoHeader.id == resId)
+ {
+ found = true;
+
+ // Check data is of valid MODEL type
+ if (infoHeader.type == 8)
+ {
+ // TODO: Load model data
+ }
+ else
+ {
+ TraceLog(WARNING, "[%s] Required resource do not seem to be a valid MODEL resource", rresName);
+ }
+ }
+ else
+ {
+ // Depending on type, skip the right amount of parameters
+ switch (infoHeader.type)
+ {
+ case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters
+ case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters
+ case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review)
+ case 3: break; // TEXT: No parameters
+ case 4: break; // RAW: No parameters
+ default: break;
+ }
+
+ // Jump DATA to read next infoHeader
+ fseek(rresFile, infoHeader.size, SEEK_CUR);
+ }
+ }
+ }
+
+ fclose(rresFile);
+ }
+
+ if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId);
+
+ return model;
+}
+
// Load a heightmap image as a 3d model
// NOTE: model map size is defined in generic units
Model LoadHeightmap(Image heightmap, Vector3 size)
{
+ Model model = { 0 };
+
+ model.mesh = GenMeshHeightmap(heightmap, size);
+
+ rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model)
+
+ model.transform = MatrixIdentity();
+ model.material = LoadDefaultMaterial();
+
+ return model;
+}
+
+// Load a map image as a 3d model (cubes based)
+Model LoadCubicmap(Image cubicmap)
+{
+ Model model = { 0 };
+
+ model.mesh = GenMeshCubicmap(cubicmap, (Vector3){ 1.0, 1.0, 1.5f });
+
+ rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model)
+
+ model.transform = MatrixIdentity();
+ model.material = LoadDefaultMaterial();
+
+ return model;
+}
+
+// Unload 3d model from memory (mesh and material)
+void UnloadModel(Model model)
+{
+ rlglUnloadMesh(&model.mesh);
+
+ UnloadMaterial(model.material);
+
+ TraceLog(INFO, "Unloaded model data from RAM and VRAM");
+}
+
+// Load material data (from file)
+Material LoadMaterial(const char *fileName)
+{
+ Material material = { 0 };
+
+ if (strcmp(GetExtension(fileName), "mtl") == 0) material = LoadMTL(fileName);
+ else TraceLog(WARNING, "[%s] Material extension not recognized, it can't be loaded", fileName);
+
+ return material;
+}
+
+// Load default material (uses default models shader)
+Material LoadDefaultMaterial(void)
+{
+ Material material = { 0 };
+
+ material.shader = GetDefaultShader();
+ material.texDiffuse = GetDefaultTexture(); // White texture (1x1 pixel)
+ //material.texNormal; // NOTE: By default, not set
+ //material.texSpecular; // NOTE: By default, not set
+
+ material.colDiffuse = WHITE; // Diffuse color
+ material.colAmbient = WHITE; // Ambient color
+ material.colSpecular = WHITE; // Specular color
+
+ material.glossiness = 100.0f; // Glossiness level
+
+ return material;
+}
+
+// Load standard material (uses material attributes and lighting shader)
+// NOTE: Standard shader supports multiple maps and lights
+Material LoadStandardMaterial(void)
+{
+ Material material = LoadDefaultMaterial();
+
+ material.shader = GetStandardShader();
+
+ return material;
+}
+
+// Unload material from memory
+void UnloadMaterial(Material material)
+{
+ rlDeleteTextures(material.texDiffuse.id);
+ rlDeleteTextures(material.texNormal.id);
+ rlDeleteTextures(material.texSpecular.id);
+}
+
+// Link a texture to a model
+void SetModelTexture(Model *model, Texture2D texture)
+{
+ if (texture.id <= 0) model->material.texDiffuse.id = whiteTexture; // Use default white texture
+ else model->material.texDiffuse = texture;
+}
+
+// Generate a mesh from heightmap
+static Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
+{
#define GRAY_VALUE(c) ((c.r+c.g+c.b)/3)
- Mesh mesh;
+ Mesh mesh = { 0 };
int mapX = heightmap.width;
int mapZ = heightmap.height;
@@ -627,7 +835,7 @@ Model LoadHeightmap(Image heightmap, Vector3 size)
mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
- mesh.colors = (unsigned char *)malloc(mesh.vertexCount*4*sizeof(unsigned char)); // Not used...
+ mesh.colors = NULL;
int vCounter = 0; // Used to count vertices float by float
int tcCounter = 0; // Used to count texcoords float by float
@@ -637,9 +845,9 @@ Model LoadHeightmap(Image heightmap, Vector3 size)
Vector3 scaleFactor = { size.x/mapX, size.y/255.0f, size.z/mapZ };
- for(int z = 0; z < mapZ-1; z++)
+ for (int z = 0; z < mapZ-1; z++)
{
- for(int x = 0; x < mapX-1; x++)
+ for (int x = 0; x < mapX-1; x++)
{
// Fill vertices array with data
//----------------------------------------------------------
@@ -710,38 +918,17 @@ Model LoadHeightmap(Image heightmap, Vector3 size)
free(pixels);
- // Fill color data
- // NOTE: Not used any more... just one plain color defined at DrawModel()
- for (int i = 0; i < (4*mesh.vertexCount); i++) mesh.colors[i] = 255;
-
- // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct
-
- Model model = rlglLoadModel(mesh);
-
- // Now that vertex data is uploaded to GPU, we can free arrays
- // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2
- if (rlGetVersion() != OPENGL_11)
- {
- free(mesh.vertices);
- free(mesh.texcoords);
- free(mesh.normals);
- free(mesh.colors);
- }
-
- return model;
+ return mesh;
}
-// Load a map image as a 3d model (cubes based)
-Model LoadCubicmap(Image cubicmap)
+static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
{
- Mesh mesh;
+ Mesh mesh = { 0 };
Color *cubicmapPixels = GetImageData(cubicmap);
- // Map cube size will be 1.0
- float mapCubeSide = 1.0f;
- int mapWidth = cubicmap.width*(int)mapCubeSide;
- int mapHeight = cubicmap.height*(int)mapCubeSide;
+ int mapWidth = cubicmap.width*(int)cubeSize.x;
+ int mapHeight = cubicmap.height*(int)cubeSize.z;
// NOTE: Max possible number of triangles numCubes * (12 triangles by cube)
int maxTriangles = cubicmap.width*cubicmap.height*12;
@@ -750,9 +937,9 @@ Model LoadCubicmap(Image cubicmap)
int tcCounter = 0; // Used to count texcoords
int nCounter = 0; // Used to count normals
- float w = mapCubeSide;
- float h = mapCubeSide;
- float h2 = mapCubeSide*1.5f; // TODO: Review walls height...
+ float w = cubeSize.x;
+ float h = cubeSize.z;
+ float h2 = cubeSize.y;
Vector3 *mapVertices = (Vector3 *)malloc(maxTriangles*3*sizeof(Vector3));
Vector2 *mapTexcoords = (Vector2 *)malloc(maxTriangles*3*sizeof(Vector2));
@@ -781,9 +968,9 @@ Model LoadCubicmap(Image cubicmap)
RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f };
RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f };
- for (int z = 0; z < mapHeight; z += mapCubeSide)
+ for (int z = 0; z < mapHeight; z += cubeSize.z)
{
- for (int x = 0; x < mapWidth; x += mapCubeSide)
+ for (int x = 0; x < mapWidth; x += cubeSize.x)
{
// Define the 8 vertex of the cube, we will combine them accordingly later...
Vector3 v1 = { x - w/2, h2, z - h/2 };
@@ -1045,11 +1232,7 @@ Model LoadCubicmap(Image cubicmap)
mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
- mesh.colors = (unsigned char *)malloc(mesh.vertexCount*4*sizeof(unsigned char)); // Not used...
-
- // Fill color data
- // NOTE: Not used any more... just one plain color defined at DrawModel()
- for (int i = 0; i < (4*mesh.vertexCount); i++) mesh.colors[i] = 255;
+ mesh.colors = NULL;
int fCounter = 0;
@@ -1087,59 +1270,9 @@ Model LoadCubicmap(Image cubicmap)
free(mapNormals);
free(mapTexcoords);
- free(cubicmapPixels);
-
- // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct
-
- Model model = rlglLoadModel(mesh);
-
- // Now that vertex data is uploaded to GPU, we can free arrays
- // NOTE: We don't need CPU vertex data on OpenGL 3.3 or ES2
- if (rlGetVersion() != OPENGL_11)
- {
- free(mesh.vertices);
- free(mesh.texcoords);
- free(mesh.normals);
- free(mesh.colors);
- }
-
- return model;
-}
-
-// Unload 3d model from memory
-void UnloadModel(Model model)
-{
- if (rlGetVersion() == OPENGL_11)
- {
- free(model.mesh.vertices);
- free(model.mesh.texcoords);
- free(model.mesh.normals);
- }
-
- rlDeleteBuffers(model.mesh.vboId[0]);
- rlDeleteBuffers(model.mesh.vboId[1]);
- rlDeleteBuffers(model.mesh.vboId[2]);
-
- rlDeleteVertexArrays(model.mesh.vaoId);
+ free(cubicmapPixels); // Free image pixel data
- if (model.mesh.vaoId > 0) TraceLog(INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", model.mesh.vaoId);
- else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i] Unloaded model data from VRAM (GPU)", model.mesh.vboId[0], model.mesh.vboId[1], model.mesh.vboId[2]);
-}
-
-// Link a texture to a model
-void SetModelTexture(Model *model, Texture2D texture)
-{
- if (texture.id <= 0)
- {
- // Use default white texture (use mesh color)
- model->texture.id = whiteTexture; // OpenGL 1.1
- model->shader.texDiffuseId = whiteTexture; // OpenGL 3.3 / ES 2.0
- }
- else
- {
- model->texture = texture;
- model->shader.texDiffuseId = texture.id;
- }
+ return mesh;
}
// Draw a model (with texture if set)
@@ -1147,90 +1280,70 @@ void DrawModel(Model model, Vector3 position, float scale, Color tint)
{
Vector3 vScale = { scale, scale, scale };
Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f };
-
+
DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint);
}
// Draw a model with extended parameters
void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
{
- // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel()
- rlglDrawModel(model, position, rotationAxis, rotationAngle, scale, tint, false);
+ // Calculate transformation matrix from function parameters
+ // Get transform matrix (rotation -> scale -> translation)
+ Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
+ Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
+ Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
+
+ // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
+ //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates
+
+ model.transform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
+ model.material.colDiffuse = tint; // TODO: Multiply tint color by diffuse color?
+
+ rlglDrawMesh(model.mesh, model.material, model.transform);
}
// Draw a model wires (with texture if set)
-void DrawModelWires(Model model, Vector3 position, float scale, Color color)
+void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
{
- Vector3 vScale = { scale, scale, scale };
- Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f };
-
- rlglDrawModel(model, position, rotationAxis, 0.0f, vScale, color, true);
+ rlEnableWireMode();
+
+ DrawModel(model, position, scale, tint);
+
+ rlDisableWireMode();
}
// Draw a model wires (with texture if set) with extended parameters
void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
{
- // NOTE: Rotation must be provided in degrees, it's converted to radians inside rlglDrawModel()
- rlglDrawModel(model, position, rotationAxis, rotationAngle, scale, tint, true);
+ rlEnableWireMode();
+
+ DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
+
+ rlDisableWireMode();
}
// Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
{
- // NOTE: Billboard size will maintain texture aspect ratio, size will be billboard width
- Vector2 sizeRatio = { size, size * (float)texture.height/texture.width };
-
- Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up);
- MatrixTranspose(&viewMatrix);
-
- Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
- //Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
+ Rectangle sourceRec = { 0, 0, texture.width, texture.height };
- // NOTE: Billboard locked to axis-Y
- Vector3 up = { 0.0f, 1.0f, 0.0f };
-/*
- a-------b
- | |
- | * |
- | |
- d-------c
-*/
- VectorScale(&right, sizeRatio.x/2);
- VectorScale(&up, sizeRatio.y/2);
-
- Vector3 p1 = VectorAdd(right, up);
- Vector3 p2 = VectorSubtract(right, up);
-
- Vector3 a = VectorSubtract(center, p2);
- Vector3 b = VectorAdd(center, p1);
- Vector3 c = VectorAdd(center, p2);
- Vector3 d = VectorSubtract(center, p1);
-
- rlEnableTexture(texture.id);
-
- rlBegin(RL_QUADS);
- rlColor4ub(tint.r, tint.g, tint.b, tint.a);
-
- rlTexCoord2f(0.0f, 0.0f); rlVertex3f(a.x, a.y, a.z);
- rlTexCoord2f(0.0f, 1.0f); rlVertex3f(d.x, d.y, d.z);
- rlTexCoord2f(1.0f, 1.0f); rlVertex3f(c.x, c.y, c.z);
- rlTexCoord2f(1.0f, 0.0f); rlVertex3f(b.x, b.y, b.z);
- rlEnd();
-
- rlDisableTexture();
+ DrawBillboardRec(camera, texture, sourceRec, center, size, tint);
}
// Draw a billboard (part of a texture defined by a rectangle)
void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint)
{
// NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width
- Vector2 sizeRatio = { size, size * (float)sourceRec.height/sourceRec.width };
+ Vector2 sizeRatio = { size, size*(float)sourceRec.height/sourceRec.width };
Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up);
MatrixTranspose(&viewMatrix);
Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
- Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
+ //Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
+
+ // NOTE: Billboard locked on axis-Y
+ Vector3 up = { 0.0f, 1.0f, 0.0f };
/*
a-------b
| |
@@ -1275,7 +1388,7 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
}
// Draw a bounding box with wires
-void DrawBoundingBox(BoundingBox box)
+void DrawBoundingBox(BoundingBox box, Color color)
{
Vector3 size;
@@ -1285,7 +1398,7 @@ void DrawBoundingBox(BoundingBox box)
Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f };
- DrawCubeWires(center, size.x, size.y, size.z, GREEN);
+ DrawCubeWires(center, size.x, size.y, size.z, color);
}
// Detect collision between two spheres
@@ -1306,14 +1419,14 @@ bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, floa
// Detect collision between two boxes
// NOTE: Boxes are defined by two points minimum and maximum
-bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2)
+bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2)
{
bool collision = true;
- if ((maxBBox1.x >= minBBox2.x) && (minBBox1.x <= maxBBox2.x))
+ if ((box1.max.x >= box2.min.x) && (box1.min.x <= box2.max.x))
{
- if ((maxBBox1.y < minBBox2.y) || (minBBox1.y > maxBBox2.y)) collision = false;
- if ((maxBBox1.z < minBBox2.z) || (minBBox1.z > maxBBox2.z)) collision = false;
+ if ((box1.max.y < box2.min.y) || (box1.min.y > box2.max.y)) collision = false;
+ if ((box1.max.z < box2.min.z) || (box1.min.z > box2.max.z)) collision = false;
}
else collision = false;
@@ -1321,30 +1434,22 @@ bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, V
}
// Detect collision between box and sphere
-bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere)
+bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radiusSphere)
{
bool collision = false;
- if ((centerSphere.x - minBBox.x > radiusSphere) && (centerSphere.y - minBBox.y > radiusSphere) && (centerSphere.z - minBBox.z > radiusSphere) &&
- (maxBBox.x - centerSphere.x > radiusSphere) && (maxBBox.y - centerSphere.y > radiusSphere) && (maxBBox.z - centerSphere.z > radiusSphere))
- {
- collision = true;
- }
- else
- {
- float dmin = 0;
+ float dmin = 0;
- if (centerSphere.x - minBBox.x <= radiusSphere) dmin += (centerSphere.x - minBBox.x)*(centerSphere.x - minBBox.x);
- else if (maxBBox.x - centerSphere.x <= radiusSphere) dmin += (centerSphere.x - maxBBox.x)*(centerSphere.x - maxBBox.x);
+ if (centerSphere.x < box.min.x) dmin += pow(centerSphere.x - box.min.x, 2);
+ else if (centerSphere.x > box.max.x) dmin += pow(centerSphere.x - box.max.x, 2);
- if (centerSphere.y - minBBox.y <= radiusSphere) dmin += (centerSphere.y - minBBox.y)*(centerSphere.y - minBBox.y);
- else if (maxBBox.y - centerSphere.y <= radiusSphere) dmin += (centerSphere.y - maxBBox.y)*(centerSphere.y - maxBBox.y);
+ if (centerSphere.y < box.min.y) dmin += pow(centerSphere.y - box.min.y, 2);
+ else if (centerSphere.y > box.max.y) dmin += pow(centerSphere.y - box.max.y, 2);
- if (centerSphere.z - minBBox.z <= radiusSphere) dmin += (centerSphere.z - minBBox.z)*(centerSphere.z - minBBox.z);
- else if (maxBBox.z - centerSphere.z <= radiusSphere) dmin += (centerSphere.z - maxBBox.z)*(centerSphere.z - maxBBox.z);
+ if (centerSphere.z < box.min.z) dmin += pow(centerSphere.z - box.min.z, 2);
+ else if (centerSphere.z > box.max.z) dmin += pow(centerSphere.z - box.max.z, 2);
- if (dmin <= radiusSphere*radiusSphere) collision = true;
- }
+ if (dmin <= (radiusSphere*radiusSphere)) collision = true;
return collision;
}
@@ -1359,7 +1464,7 @@ bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius
float vector = VectorDotProduct(raySpherePos, ray.direction);
float d = sphereRadius*sphereRadius - (distance*distance - vector*vector);
- if(d >= 0.0f) collision = true;
+ if (d >= 0.0f) collision = true;
return collision;
}
@@ -1374,14 +1479,14 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi
float vector = VectorDotProduct(raySpherePos, ray.direction);
float d = sphereRadius*sphereRadius - (distance*distance - vector*vector);
- if(d >= 0.0f) collision = true;
+ if (d >= 0.0f) collision = true;
// Calculate collision point
Vector3 offset = ray.direction;
float collisionDistance = 0;
// Check if ray origin is inside the sphere to calculate the correct collision point
- if(distance < sphereRadius) collisionDistance = vector + sqrt(d);
+ if (distance < sphereRadius) collisionDistance = vector + sqrt(d);
else collisionDistance = vector - sqrt(d);
VectorScale(&offset, collisionDistance);
@@ -1395,17 +1500,17 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi
}
// Detect collision between ray and bounding box
-bool CheckCollisionRayBox(Ray ray, Vector3 minBBox, Vector3 maxBBox)
+bool CheckCollisionRayBox(Ray ray, BoundingBox box)
{
bool collision = false;
float t[8];
- t[0] = (minBBox.x - ray.position.x)/ray.direction.x;
- t[1] = (maxBBox.x - ray.position.x)/ray.direction.x;
- t[2] = (minBBox.y - ray.position.y)/ray.direction.y;
- t[3] = (maxBBox.y - ray.position.y)/ray.direction.y;
- t[4] = (minBBox.z - ray.position.z)/ray.direction.z;
- t[5] = (maxBBox.z - ray.position.z)/ray.direction.z;
+ t[0] = (box.min.x - ray.position.x)/ray.direction.x;
+ t[1] = (box.max.x - ray.position.x)/ray.direction.x;
+ t[2] = (box.min.y - ray.position.y)/ray.direction.y;
+ t[3] = (box.max.y - ray.position.y)/ray.direction.y;
+ t[4] = (box.min.z - ray.position.z)/ray.direction.z;
+ t[5] = (box.max.z - ray.position.z)/ray.direction.z;
t[6] = fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
t[7] = fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));
@@ -1419,13 +1524,19 @@ bool CheckCollisionRayBox(Ray ray, Vector3 minBBox, Vector3 maxBBox)
BoundingBox CalculateBoundingBox(Mesh mesh)
{
// Get min and max vertex to construct bounds (AABB)
- Vector3 minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
- Vector3 maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
+ Vector3 minVertex = { 0 };
+ Vector3 maxVertex = { 0 };
- for (int i = 1; i < mesh.vertexCount; i++)
+ if (mesh.vertices != NULL)
{
- minVertex = VectorMin(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
- maxVertex = VectorMax(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
+ minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
+ maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
+
+ for (int i = 1; i < mesh.vertexCount; i++)
+ {
+ minVertex = VectorMin(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
+ maxVertex = VectorMax(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
+ }
}
// Create the bounding box
@@ -1712,12 +1823,12 @@ static Mesh LoadOBJ(const char *fileName)
// First reading pass: Get numVertex, numNormals, numTexCoords, numTriangles
// NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition)
- // NOTE: faces MUST be defined as TRIANGLES, not QUADS
- while(!feof(objFile))
+ // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face)
+ while (!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
- switch(dataType)
+ switch (dataType)
{
case '#': // Comments
case 'o': // Object name (One OBJ file can contain multible named meshes)
@@ -1778,11 +1889,11 @@ static Mesh LoadOBJ(const char *fileName)
// Second reading pass: Get vertex data to fill intermediate arrays
// NOTE: This second pass is required in case of multiple meshes defined in same OBJ
// TODO: Consider that different meshes can have different vertex data available (position, texcoords, normals)
- while(!feof(objFile))
+ while (!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
- switch(dataType)
+ switch (dataType)
{
case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'f': fgets(comments, 200, objFile); break;
case 'v':
@@ -1826,7 +1937,7 @@ static Mesh LoadOBJ(const char *fileName)
mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
- mesh.colors = (unsigned char *)malloc(mesh.vertexCount*4*sizeof(unsigned char));
+ mesh.colors = NULL;
int vCounter = 0; // Used to count vertices float by float
int tcCounter = 0; // Used to count texcoords float by float
@@ -1839,11 +1950,11 @@ static Mesh LoadOBJ(const char *fileName)
if (numNormals == 0) TraceLog(INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName);
// Third reading pass: Get faces (triangles) data and fill VertexArray
- while(!feof(objFile))
+ while (!feof(objFile))
{
fscanf(objFile, "%c", &dataType);
- switch(dataType)
+ switch (dataType)
{
case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'v': fgets(comments, 200, objFile); break;
case 'f':
@@ -1925,10 +2036,6 @@ static Mesh LoadOBJ(const char *fileName)
// Security check, just in case no normals or no texcoords defined in OBJ
if (numTexCoords == 0) for (int i = 0; i < (2*mesh.vertexCount); i++) mesh.texcoords[i] = 0.0f;
-
- // NOTE: We set all vertex colors to white
- // NOTE: Not used any more... just one plain color defined at DrawModel()
- for (int i = 0; i < (4*mesh.vertexCount); i++) mesh.colors[i] = 255;
// Now we can free temp mid* arrays
free(midVertices);
@@ -1940,3 +2047,166 @@ static Mesh LoadOBJ(const char *fileName)
return mesh;
}
+
+// Load MTL material data (specs: http://paulbourke.net/dataformats/mtl/)
+// NOTE: Texture map parameters are not supported
+static Material LoadMTL(const char *fileName)
+{
+ #define MAX_BUFFER_SIZE 128
+
+ Material material = { 0 }; // LoadDefaultMaterial();
+
+ char buffer[MAX_BUFFER_SIZE];
+ Vector3 color = { 1.0f, 1.0f, 1.0f };
+ char *mapFileName = NULL;
+
+ FILE *mtlFile;
+
+ mtlFile = fopen(fileName, "rt");
+
+ if (mtlFile == NULL)
+ {
+ TraceLog(WARNING, "[%s] MTL file could not be opened", fileName);
+ return material;
+ }
+
+ while (!feof(mtlFile))
+ {
+ fgets(buffer, MAX_BUFFER_SIZE, mtlFile);
+
+ switch (buffer[0])
+ {
+ case 'n': // newmtl string Material name. Begins a new material description.
+ {
+ // TODO: Support multiple materials in a single .mtl
+ sscanf(buffer, "newmtl %s", mapFileName);
+
+ TraceLog(INFO, "[%s] Loading material...", mapFileName);
+ }
+ case 'i': // illum int Illumination model
+ {
+ // illum = 1 if specular disabled
+ // illum = 2 if specular enabled (lambertian model)
+ // ...
+ }
+ case 'K': // Ka, Kd, Ks, Ke
+ {
+ switch (buffer[1])
+ {
+ case 'a': // Ka float float float Ambient color (RGB)
+ {
+ sscanf(buffer, "Ka %f %f %f", &color.x, &color.y, &color.z);
+ material.colAmbient.r = (unsigned char)(color.x*255);
+ material.colAmbient.g = (unsigned char)(color.y*255);
+ material.colAmbient.b = (unsigned char)(color.z*255);
+ } break;
+ case 'd': // Kd float float float Diffuse color (RGB)
+ {
+ sscanf(buffer, "Kd %f %f %f", &color.x, &color.y, &color.z);
+ material.colDiffuse.r = (unsigned char)(color.x*255);
+ material.colDiffuse.g = (unsigned char)(color.y*255);
+ material.colDiffuse.b = (unsigned char)(color.z*255);
+ } break;
+ case 's': // Ks float float float Specular color (RGB)
+ {
+ sscanf(buffer, "Ks %f %f %f", &color.x, &color.y, &color.z);
+ material.colSpecular.r = (unsigned char)(color.x*255);
+ material.colSpecular.g = (unsigned char)(color.y*255);
+ material.colSpecular.b = (unsigned char)(color.z*255);
+ } break;
+ case 'e': // Ke float float float Emmisive color (RGB)
+ {
+ // TODO: Support Ke ?
+ } break;
+ default: break;
+ }
+ } break;
+ case 'N': // Ns, Ni
+ {
+ if (buffer[1] == 's') // Ns int Shininess (specular exponent). Ranges from 0 to 1000.
+ {
+ int shininess = 0;
+ sscanf(buffer, "Ns %i", &shininess);
+
+ material.glossiness = (float)shininess;
+ }
+ else if (buffer[1] == 'i') // Ni int Refraction index.
+ {
+ // Not supported...
+ }
+ } break;
+ case 'm': // map_Kd, map_Ks, map_Ka, map_Bump, map_d
+ {
+ switch (buffer[4])
+ {
+ case 'K': // Color texture maps
+ {
+ if (buffer[5] == 'd') // map_Kd string Diffuse color texture map.
+ {
+ sscanf(buffer, "map_Kd %s", mapFileName);
+ if (mapFileName != NULL) material.texDiffuse = LoadTexture(mapFileName);
+ }
+ else if (buffer[5] == 's') // map_Ks string Specular color texture map.
+ {
+ sscanf(buffer, "map_Ks %s", mapFileName);
+ if (mapFileName != NULL) material.texSpecular = LoadTexture(mapFileName);
+ }
+ else if (buffer[5] == 'a') // map_Ka string Ambient color texture map.
+ {
+ // Not supported...
+ }
+ } break;
+ case 'B': // map_Bump string Bump texture map.
+ {
+ sscanf(buffer, "map_Bump %s", mapFileName);
+ if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName);
+ } break;
+ case 'b': // map_bump string Bump texture map.
+ {
+ sscanf(buffer, "map_bump %s", mapFileName);
+ if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName);
+ } break;
+ case 'd': // map_d string Opacity texture map.
+ {
+ // Not supported...
+ } break;
+ default: break;
+ }
+ } break;
+ case 'd': // d, disp
+ {
+ if (buffer[1] == ' ') // d float Dissolve factor. d is inverse of Tr
+ {
+ float alpha = 1.0f;
+ sscanf(buffer, "d %f", &alpha);
+ material.colDiffuse.a = (unsigned char)(alpha*255);
+ }
+ else if (buffer[1] == 'i') // disp string Displacement map
+ {
+ // Not supported...
+ }
+ } break;
+ case 'b': // bump string Bump texture map
+ {
+ sscanf(buffer, "bump %s", mapFileName);
+ if (mapFileName != NULL) material.texNormal = LoadTexture(mapFileName);
+ } break;
+ case 'T': // Tr float Transparency Tr (alpha). Tr is inverse of d
+ {
+ float ialpha = 0.0f;
+ sscanf(buffer, "Tr %f", &ialpha);
+ material.colDiffuse.a = (unsigned char)((1.0f - ialpha)*255);
+
+ } break;
+ case 'r': // refl string Reflection texture map
+ default: break;
+ }
+ }
+
+ fclose(mtlFile);
+
+ // NOTE: At this point we have all material data
+ TraceLog(INFO, "[%s] Material loaded successfully", fileName);
+
+ return material;
+}
diff --git a/src/physac.c b/src/physac.c
index 4c50dd41..1d577d3d 100644
--- a/src/physac.c
+++ b/src/physac.c
@@ -1,8 +1,8 @@
/**********************************************************************************************
*
-* [physac] raylib physics engine module - Basic functions to apply physics to 2D objects
+* [physac] raylib physics module - Basic functions to apply physics to 2D objects
*
-* Copyright (c) 2015 Victor Fisac and Ramon Santamaria
+* Copyright (c) 2016 Victor Fisac and Ramon Santamaria
*
* 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.
@@ -29,329 +29,566 @@
#include "raylib.h"
#endif
-#include <math.h>
-#include <stdlib.h> // Required for: malloc(), free()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <math.h> // Required for: cos(), sin(), abs(), fminf()
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define DECIMAL_FIX 0.26f // Decimal margin for collision checks (avoid rigidbodies shake)
+#define MAX_PHYSIC_OBJECTS 256 // Maximum available physic object slots in objects pool
+#define PHYSICS_STEPS 450 // Physics update steps number (divided calculations in steps per frame) to get more accurately collisions detections
+#define PHYSICS_ACCURACY 0.0001f // Velocity subtract operations round filter (friction)
+#define PHYSICS_ERRORPERCENT 0.001f // Collision resolve position fix
//----------------------------------------------------------------------------------
// Types and Structures Definition
+// NOTE: Below types are required for PHYSAC_STANDALONE usage
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
-static Collider *colliders; // Colliders array, dynamically allocated at runtime
-static Rigidbody *rigidbodies; // Rigitbody array, dynamically allocated at runtime
-static bool collisionChecker;
-
-static int maxElements; // Max physic elements to compute
-static bool enabled; // Physics enabled? (true by default)
-static Vector2 gravity; // Gravity value used for physic calculations
+static PhysicObject physicObjects[MAX_PHYSIC_OBJECTS]; // Physic objects pool
+static int physicObjectsCount; // Counts current enabled physic objects
+static Vector2 gravityForce; // Gravity force
//----------------------------------------------------------------------------------
-// Module specific Functions Declarations
+// Module specific Functions Declaration
//----------------------------------------------------------------------------------
-static float Vector2Length(Vector2 vector);
-static float Vector2Distance(Vector2 a, Vector2 b);
-static void Vector2Normalize(Vector2 *vector);
+static float Vector2DotProduct(Vector2 v1, Vector2 v2); // Returns the dot product of two Vector2
+static float Vector2Length(Vector2 v); // Returns the length of a Vector2
//----------------------------------------------------------------------------------
-// Module Functions Definitions
+// Module Functions Definition
//----------------------------------------------------------------------------------
-void InitPhysics(int maxPhysicElements)
-{
- maxElements = maxPhysicElements;
-
- colliders = (Collider *)malloc(maxElements*sizeof(Collider));
- rigidbodies = (Rigidbody *)malloc(maxElements*sizeof(Rigidbody));
-
- for (int i = 0; i < maxElements; i++)
- {
- colliders[i].enabled = false;
- colliders[i].bounds = (Rectangle){ 0, 0, 0, 0 };
- colliders[i].radius = 0;
-
- rigidbodies[i].enabled = false;
- rigidbodies[i].mass = 0.0f;
- rigidbodies[i].velocity = (Vector2){ 0.0f, 0.0f };
- rigidbodies[i].acceleration = (Vector2){ 0.0f, 0.0f };
- rigidbodies[i].isGrounded = false;
- rigidbodies[i].isContact = false;
- rigidbodies[i].friction = 0.0f;
- }
-
- collisionChecker = false;
- enabled = true;
-
- // NOTE: To get better results, gravity needs to be 1:10 from original parameter
- gravity = (Vector2){ 0.0f, -9.81f/10.0f }; // By default, standard gravity
-}
-
-void UnloadPhysics()
-{
- free(colliders);
- free(rigidbodies);
-}
-
-void AddCollider(int index, Collider collider)
-{
- colliders[index] = collider;
-}
-void AddRigidbody(int index, Rigidbody rigidbody)
+// Initializes pointers array (just pointers, fixed size)
+void InitPhysics(Vector2 gravity)
{
- rigidbodies[index] = rigidbody;
+ // Initialize physics variables
+ physicObjectsCount = 0;
+ gravityForce = gravity;
}
-void ApplyPhysics(int index, Vector2 *position)
+// Update physic objects, calculating physic behaviours and collisions detection
+void UpdatePhysics()
{
- if (rigidbodies[index].enabled)
+ // Reset all physic objects is grounded state
+ for (int i = 0; i < physicObjectsCount; i++) physicObjects[i]->rigidbody.isGrounded = false;
+
+ for (int steps = 0; steps < PHYSICS_STEPS; steps++)
{
- // Apply friction to acceleration
- if (rigidbodies[index].acceleration.x > DECIMAL_FIX)
- {
- rigidbodies[index].acceleration.x -= rigidbodies[index].friction;
- }
- else if (rigidbodies[index].acceleration.x < -DECIMAL_FIX)
- {
- rigidbodies[index].acceleration.x += rigidbodies[index].friction;
- }
- else
- {
- rigidbodies[index].acceleration.x = 0;
- }
-
- if (rigidbodies[index].acceleration.y > DECIMAL_FIX / 2)
- {
- rigidbodies[index].acceleration.y -= rigidbodies[index].friction;
- }
- else if (rigidbodies[index].acceleration.y < -DECIMAL_FIX / 2)
- {
- rigidbodies[index].acceleration.y += rigidbodies[index].friction;
- }
- else
- {
- rigidbodies[index].acceleration.y = 0;
- }
-
- // Apply friction to velocity
- if (rigidbodies[index].isGrounded)
- {
- if (rigidbodies[index].velocity.x > DECIMAL_FIX)
- {
- rigidbodies[index].velocity.x -= rigidbodies[index].friction;
- }
- else if (rigidbodies[index].velocity.x < -DECIMAL_FIX)
- {
- rigidbodies[index].velocity.x += rigidbodies[index].friction;
- }
- else
- {
- rigidbodies[index].velocity.x = 0;
- }
- }
-
- if (rigidbodies[index].velocity.y > DECIMAL_FIX / 2)
+ for (int i = 0; i < physicObjectsCount; i++)
{
- rigidbodies[index].velocity.y -= rigidbodies[index].friction;
- }
- else if (rigidbodies[index].velocity.y < -DECIMAL_FIX / 2)
- {
- rigidbodies[index].velocity.y += rigidbodies[index].friction;
- }
- else
- {
- rigidbodies[index].velocity.y = 0;
- }
-
- // Apply gravity
- rigidbodies[index].velocity.y += gravity.y;
- rigidbodies[index].velocity.x += gravity.x;
-
- // Apply acceleration
- rigidbodies[index].velocity.y += rigidbodies[index].acceleration.y;
- rigidbodies[index].velocity.x += rigidbodies[index].acceleration.x;
-
- // Update position vector
- position->x += rigidbodies[index].velocity.x;
- position->y -= rigidbodies[index].velocity.y;
-
- // Update collider bounds
- colliders[index].bounds.x = position->x;
- colliders[index].bounds.y = position->y;
-
- // Check collision with other colliders
- collisionChecker = false;
- rigidbodies[index].isContact = false;
- for (int j = 0; j < maxElements; j++)
- {
- if (index != j)
+ if (physicObjects[i]->enabled)
{
- if (colliders[index].enabled && colliders[j].enabled)
+ // Update physic behaviour
+ if (physicObjects[i]->rigidbody.enabled)
{
- if (colliders[index].type == COLLIDER_RECTANGLE)
+ // Apply friction to acceleration in X axis
+ if (physicObjects[i]->rigidbody.acceleration.x > PHYSICS_ACCURACY) physicObjects[i]->rigidbody.acceleration.x -= physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else if (physicObjects[i]->rigidbody.acceleration.x < PHYSICS_ACCURACY) physicObjects[i]->rigidbody.acceleration.x += physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else physicObjects[i]->rigidbody.acceleration.x = 0.0f;
+
+ // Apply friction to acceleration in Y axis
+ if (physicObjects[i]->rigidbody.acceleration.y > PHYSICS_ACCURACY) physicObjects[i]->rigidbody.acceleration.y -= physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else if (physicObjects[i]->rigidbody.acceleration.y < PHYSICS_ACCURACY) physicObjects[i]->rigidbody.acceleration.y += physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else physicObjects[i]->rigidbody.acceleration.y = 0.0f;
+
+ // Apply friction to velocity in X axis
+ if (physicObjects[i]->rigidbody.velocity.x > PHYSICS_ACCURACY) physicObjects[i]->rigidbody.velocity.x -= physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else if (physicObjects[i]->rigidbody.velocity.x < PHYSICS_ACCURACY) physicObjects[i]->rigidbody.velocity.x += physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else physicObjects[i]->rigidbody.velocity.x = 0.0f;
+
+ // Apply friction to velocity in Y axis
+ if (physicObjects[i]->rigidbody.velocity.y > PHYSICS_ACCURACY) physicObjects[i]->rigidbody.velocity.y -= physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else if (physicObjects[i]->rigidbody.velocity.y < PHYSICS_ACCURACY) physicObjects[i]->rigidbody.velocity.y += physicObjects[i]->rigidbody.friction/PHYSICS_STEPS;
+ else physicObjects[i]->rigidbody.velocity.y = 0.0f;
+
+ // Apply gravity to velocity
+ if (physicObjects[i]->rigidbody.applyGravity)
{
- if (colliders[j].type == COLLIDER_RECTANGLE)
+ physicObjects[i]->rigidbody.velocity.x += gravityForce.x/PHYSICS_STEPS;
+ physicObjects[i]->rigidbody.velocity.y += gravityForce.y/PHYSICS_STEPS;
+ }
+
+ // Apply acceleration to velocity
+ physicObjects[i]->rigidbody.velocity.x += physicObjects[i]->rigidbody.acceleration.x/PHYSICS_STEPS;
+ physicObjects[i]->rigidbody.velocity.y += physicObjects[i]->rigidbody.acceleration.y/PHYSICS_STEPS;
+
+ // Apply velocity to position
+ physicObjects[i]->transform.position.x += physicObjects[i]->rigidbody.velocity.x/PHYSICS_STEPS;
+ physicObjects[i]->transform.position.y -= physicObjects[i]->rigidbody.velocity.y/PHYSICS_STEPS;
+ }
+
+ // Update collision detection
+ if (physicObjects[i]->collider.enabled)
+ {
+ // Update collider bounds
+ physicObjects[i]->collider.bounds = TransformToRectangle(physicObjects[i]->transform);
+
+ // Check collision with other colliders
+ for (int k = 0; k < physicObjectsCount; k++)
+ {
+ if (physicObjects[k]->collider.enabled && i != k)
{
- if (CheckCollisionRecs(colliders[index].bounds, colliders[j].bounds))
+ // Resolve physic collision
+ // NOTE: collision resolve is generic for all directions and conditions (no axis separated cases behaviours)
+ // and it is separated in rigidbody attributes resolve (velocity changes by impulse) and position correction (position overlap)
+
+ // 1. Calculate collision normal
+ // -------------------------------------------------------------------------------------------------------------------------------------
+
+ // Define collision contact normal, direction and penetration depth
+ Vector2 contactNormal = { 0.0f, 0.0f };
+ Vector2 direction = { 0.0f, 0.0f };
+ float penetrationDepth = 0.0f;
+
+ switch (physicObjects[i]->collider.type)
{
- collisionChecker = true;
-
- if ((colliders[index].bounds.y + colliders[index].bounds.height <= colliders[j].bounds.y) == false)
+ case COLLIDER_RECTANGLE:
{
- rigidbodies[index].isContact = true;
- }
+ switch (physicObjects[k]->collider.type)
+ {
+ case COLLIDER_RECTANGLE:
+ {
+ // Check if colliders are overlapped
+ if (CheckCollisionRecs(physicObjects[i]->collider.bounds, physicObjects[k]->collider.bounds))
+ {
+ // Calculate direction vector from i to k
+ direction.x = (physicObjects[k]->transform.position.x + physicObjects[k]->transform.scale.x/2) - (physicObjects[i]->transform.position.x + physicObjects[i]->transform.scale.x/2);
+ direction.y = (physicObjects[k]->transform.position.y + physicObjects[k]->transform.scale.y/2) - (physicObjects[i]->transform.position.y + physicObjects[i]->transform.scale.y/2);
+
+ // Define overlapping and penetration attributes
+ Vector2 overlap;
+
+ // Calculate overlap on X axis
+ overlap.x = (physicObjects[i]->transform.scale.x + physicObjects[k]->transform.scale.x)/2 - abs(direction.x);
+
+ // SAT test on X axis
+ if (overlap.x > 0.0f)
+ {
+ // Calculate overlap on Y axis
+ overlap.y = (physicObjects[i]->transform.scale.y + physicObjects[k]->transform.scale.y)/2 - abs(direction.y);
+
+ // SAT test on Y axis
+ if (overlap.y > 0.0f)
+ {
+ // Find out which axis is axis of least penetration
+ if (overlap.y > overlap.x)
+ {
+ // Point towards k knowing that direction points from i to k
+ if (direction.x < 0.0f) contactNormal = (Vector2){ -1.0f, 0.0f };
+ else contactNormal = (Vector2){ 1.0f, 0.0f };
+
+ // Update penetration depth for position correction
+ penetrationDepth = overlap.x;
+ }
+ else
+ {
+ // Point towards k knowing that direction points from i to k
+ if (direction.y < 0.0f) contactNormal = (Vector2){ 0.0f, 1.0f };
+ else contactNormal = (Vector2){ 0.0f, -1.0f };
+
+ // Update penetration depth for position correction
+ penetrationDepth = overlap.y;
+ }
+ }
+ }
+ }
+ } break;
+ case COLLIDER_CIRCLE:
+ {
+ if (CheckCollisionCircleRec(physicObjects[k]->transform.position, physicObjects[k]->collider.radius, physicObjects[i]->collider.bounds))
+ {
+ // Calculate direction vector between circles
+ direction.x = physicObjects[k]->transform.position.x - physicObjects[i]->transform.position.x + physicObjects[i]->transform.scale.x/2;
+ direction.y = physicObjects[k]->transform.position.y - physicObjects[i]->transform.position.y + physicObjects[i]->transform.scale.y/2;
+
+ // Calculate closest point on rectangle to circle
+ Vector2 closestPoint = { 0.0f, 0.0f };
+ if (direction.x > 0.0f) closestPoint.x = physicObjects[i]->collider.bounds.x + physicObjects[i]->collider.bounds.width;
+ else closestPoint.x = physicObjects[i]->collider.bounds.x;
+
+ if (direction.y > 0.0f) closestPoint.y = physicObjects[i]->collider.bounds.y + physicObjects[i]->collider.bounds.height;
+ else closestPoint.y = physicObjects[i]->collider.bounds.y;
+
+ // Check if the closest point is inside the circle
+ if (CheckCollisionPointCircle(closestPoint, physicObjects[k]->transform.position, physicObjects[k]->collider.radius))
+ {
+ // Recalculate direction based on closest point position
+ direction.x = physicObjects[k]->transform.position.x - closestPoint.x;
+ direction.y = physicObjects[k]->transform.position.y - closestPoint.y;
+ float distance = Vector2Length(direction);
+
+ // Calculate final contact normal
+ contactNormal.x = direction.x/distance;
+ contactNormal.y = -direction.y/distance;
+
+ // Calculate penetration depth
+ penetrationDepth = physicObjects[k]->collider.radius - distance;
+ }
+ else
+ {
+ if (abs(direction.y) < abs(direction.x))
+ {
+ // Calculate final contact normal
+ if (direction.y > 0.0f)
+ {
+ contactNormal = (Vector2){ 0.0f, -1.0f };
+ penetrationDepth = fabs(physicObjects[i]->collider.bounds.y - physicObjects[k]->transform.position.y - physicObjects[k]->collider.radius);
+ }
+ else
+ {
+ contactNormal = (Vector2){ 0.0f, 1.0f };
+ penetrationDepth = fabs(physicObjects[i]->collider.bounds.y - physicObjects[k]->transform.position.y + physicObjects[k]->collider.radius);
+ }
+ }
+ else
+ {
+ // Calculate final contact normal
+ if (direction.x > 0.0f)
+ {
+ contactNormal = (Vector2){ 1.0f, 0.0f };
+ penetrationDepth = fabs(physicObjects[k]->transform.position.x + physicObjects[k]->collider.radius - physicObjects[i]->collider.bounds.x);
+ }
+ else
+ {
+ contactNormal = (Vector2){ -1.0f, 0.0f };
+ penetrationDepth = fabs(physicObjects[i]->collider.bounds.x + physicObjects[i]->collider.bounds.width - physicObjects[k]->transform.position.x - physicObjects[k]->collider.radius);
+ }
+ }
+ }
+ }
+ } break;
+ }
+ } break;
+ case COLLIDER_CIRCLE:
+ {
+ switch (physicObjects[k]->collider.type)
+ {
+ case COLLIDER_RECTANGLE:
+ {
+ if (CheckCollisionCircleRec(physicObjects[i]->transform.position, physicObjects[i]->collider.radius, physicObjects[k]->collider.bounds))
+ {
+ // Calculate direction vector between circles
+ direction.x = physicObjects[k]->transform.position.x + physicObjects[i]->transform.scale.x/2 - physicObjects[i]->transform.position.x;
+ direction.y = physicObjects[k]->transform.position.y + physicObjects[i]->transform.scale.y/2 - physicObjects[i]->transform.position.y;
+
+ // Calculate closest point on rectangle to circle
+ Vector2 closestPoint = { 0.0f, 0.0f };
+ if (direction.x > 0.0f) closestPoint.x = physicObjects[k]->collider.bounds.x + physicObjects[k]->collider.bounds.width;
+ else closestPoint.x = physicObjects[k]->collider.bounds.x;
+
+ if (direction.y > 0.0f) closestPoint.y = physicObjects[k]->collider.bounds.y + physicObjects[k]->collider.bounds.height;
+ else closestPoint.y = physicObjects[k]->collider.bounds.y;
+
+ // Check if the closest point is inside the circle
+ if (CheckCollisionPointCircle(closestPoint, physicObjects[i]->transform.position, physicObjects[i]->collider.radius))
+ {
+ // Recalculate direction based on closest point position
+ direction.x = physicObjects[i]->transform.position.x - closestPoint.x;
+ direction.y = physicObjects[i]->transform.position.y - closestPoint.y;
+ float distance = Vector2Length(direction);
+
+ // Calculate final contact normal
+ contactNormal.x = direction.x/distance;
+ contactNormal.y = -direction.y/distance;
+
+ // Calculate penetration depth
+ penetrationDepth = physicObjects[k]->collider.radius - distance;
+ }
+ else
+ {
+ if (abs(direction.y) < abs(direction.x))
+ {
+ // Calculate final contact normal
+ if (direction.y > 0.0f)
+ {
+ contactNormal = (Vector2){ 0.0f, -1.0f };
+ penetrationDepth = fabs(physicObjects[k]->collider.bounds.y - physicObjects[i]->transform.position.y - physicObjects[i]->collider.radius);
+ }
+ else
+ {
+ contactNormal = (Vector2){ 0.0f, 1.0f };
+ penetrationDepth = fabs(physicObjects[k]->collider.bounds.y - physicObjects[i]->transform.position.y + physicObjects[i]->collider.radius);
+ }
+ }
+ else
+ {
+ // Calculate final contact normal and penetration depth
+ if (direction.x > 0.0f)
+ {
+ contactNormal = (Vector2){ 1.0f, 0.0f };
+ penetrationDepth = fabs(physicObjects[i]->transform.position.x + physicObjects[i]->collider.radius - physicObjects[k]->collider.bounds.x);
+ }
+ else
+ {
+ contactNormal = (Vector2){ -1.0f, 0.0f };
+ penetrationDepth = fabs(physicObjects[k]->collider.bounds.x + physicObjects[k]->collider.bounds.width - physicObjects[i]->transform.position.x - physicObjects[i]->collider.radius);
+ }
+ }
+ }
+ }
+ } break;
+ case COLLIDER_CIRCLE:
+ {
+ // Check if colliders are overlapped
+ if (CheckCollisionCircles(physicObjects[i]->transform.position, physicObjects[i]->collider.radius, physicObjects[k]->transform.position, physicObjects[k]->collider.radius))
+ {
+ // Calculate direction vector between circles
+ direction.x = physicObjects[k]->transform.position.x - physicObjects[i]->transform.position.x;
+ direction.y = physicObjects[k]->transform.position.y - physicObjects[i]->transform.position.y;
+
+ // Calculate distance between circles
+ float distance = Vector2Length(direction);
+
+ // Check if circles are not completely overlapped
+ if (distance != 0.0f)
+ {
+ // Calculate contact normal direction (Y axis needs to be flipped)
+ contactNormal.x = direction.x/distance;
+ contactNormal.y = -direction.y/distance;
+ }
+ else contactNormal = (Vector2){ 1.0f, 0.0f }; // Choose random (but consistent) values
+ }
+ } break;
+ default: break;
+ }
+ } break;
+ default: break;
}
- }
- else
- {
- if (CheckCollisionCircleRec((Vector2){colliders[j].bounds.x, colliders[j].bounds.y}, colliders[j].radius, colliders[index].bounds))
+
+ // Update rigidbody grounded state
+ if (physicObjects[i]->rigidbody.enabled)
{
- collisionChecker = true;
+ if (contactNormal.y < 0.0f) physicObjects[i]->rigidbody.isGrounded = true;
}
- }
- }
- else
- {
- if (colliders[j].type == COLLIDER_RECTANGLE)
- {
- if (CheckCollisionCircleRec((Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius, colliders[j].bounds))
- {
- collisionChecker = true;
- }
- }
- else
- {
- if (CheckCollisionCircles((Vector2){colliders[j].bounds.x, colliders[j].bounds.y}, colliders[j].radius, (Vector2){colliders[index].bounds.x, colliders[index].bounds.y}, colliders[index].radius))
+
+ // 2. Calculate collision impulse
+ // -------------------------------------------------------------------------------------------------------------------------------------
+
+ // Calculate relative velocity
+ Vector2 relVelocity = { 0.0f, 0.0f };
+ relVelocity.x = physicObjects[k]->rigidbody.velocity.x - physicObjects[i]->rigidbody.velocity.x;
+ relVelocity.y = physicObjects[k]->rigidbody.velocity.y - physicObjects[i]->rigidbody.velocity.y;
+
+ // Calculate relative velocity in terms of the normal direction
+ float velAlongNormal = Vector2DotProduct(relVelocity, contactNormal);
+
+ // Dot not resolve if velocities are separating
+ if (velAlongNormal <= 0.0f)
{
- collisionChecker = true;
+ // Calculate minimum bounciness value from both objects
+ float e = fminf(physicObjects[i]->rigidbody.bounciness, physicObjects[k]->rigidbody.bounciness);
+
+ // Calculate impulse scalar value
+ float j = -(1.0f + e)*velAlongNormal;
+ j /= 1.0f/physicObjects[i]->rigidbody.mass + 1.0f/physicObjects[k]->rigidbody.mass;
+
+ // Calculate final impulse vector
+ Vector2 impulse = { j*contactNormal.x, j*contactNormal.y };
+
+ // Calculate collision mass ration
+ float massSum = physicObjects[i]->rigidbody.mass + physicObjects[k]->rigidbody.mass;
+ float ratio = 0.0f;
+
+ // Apply impulse to current rigidbodies velocities if they are enabled
+ if (physicObjects[i]->rigidbody.enabled)
+ {
+ // Calculate inverted mass ration
+ ratio = physicObjects[i]->rigidbody.mass/massSum;
+
+ // Apply impulse direction to velocity
+ physicObjects[i]->rigidbody.velocity.x -= impulse.x*ratio*(1.0f+physicObjects[i]->rigidbody.bounciness);
+ physicObjects[i]->rigidbody.velocity.y -= impulse.y*ratio*(1.0f+physicObjects[i]->rigidbody.bounciness);
+ }
+
+ if (physicObjects[k]->rigidbody.enabled)
+ {
+ // Calculate inverted mass ration
+ ratio = physicObjects[k]->rigidbody.mass/massSum;
+
+ // Apply impulse direction to velocity
+ physicObjects[k]->rigidbody.velocity.x += impulse.x*ratio*(1.0f+physicObjects[i]->rigidbody.bounciness);
+ physicObjects[k]->rigidbody.velocity.y += impulse.y*ratio*(1.0f+physicObjects[i]->rigidbody.bounciness);
+ }
+
+ // 3. Correct colliders overlaping (transform position)
+ // ---------------------------------------------------------------------------------------------------------------------------------
+
+ // Calculate transform position penetration correction
+ Vector2 posCorrection;
+ posCorrection.x = penetrationDepth/((1.0f/physicObjects[i]->rigidbody.mass) + (1.0f/physicObjects[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.x;
+ posCorrection.y = penetrationDepth/((1.0f/physicObjects[i]->rigidbody.mass) + (1.0f/physicObjects[k]->rigidbody.mass))*PHYSICS_ERRORPERCENT*contactNormal.y;
+
+ // Fix transform positions
+ if (physicObjects[i]->rigidbody.enabled)
+ {
+ // Fix physic objects transform position
+ physicObjects[i]->transform.position.x -= 1.0f/physicObjects[i]->rigidbody.mass*posCorrection.x;
+ physicObjects[i]->transform.position.y += 1.0f/physicObjects[i]->rigidbody.mass*posCorrection.y;
+
+ // Update collider bounds
+ physicObjects[i]->collider.bounds = TransformToRectangle(physicObjects[i]->transform);
+
+ if (physicObjects[k]->rigidbody.enabled)
+ {
+ // Fix physic objects transform position
+ physicObjects[k]->transform.position.x += 1.0f/physicObjects[k]->rigidbody.mass*posCorrection.x;
+ physicObjects[k]->transform.position.y -= 1.0f/physicObjects[k]->rigidbody.mass*posCorrection.y;
+
+ // Update collider bounds
+ physicObjects[k]->collider.bounds = TransformToRectangle(physicObjects[k]->transform);
+ }
+ }
}
}
}
}
}
}
-
- // Update grounded rigidbody state
- rigidbodies[index].isGrounded = collisionChecker;
-
- // Set grounded state if needed (fix overlap and set y velocity)
- if (collisionChecker && rigidbodies[index].velocity.y != 0)
- {
- position->y += rigidbodies[index].velocity.y;
- rigidbodies[index].velocity.y = -rigidbodies[index].velocity.y * rigidbodies[index].bounciness;
- }
-
- if (rigidbodies[index].isContact)
- {
- position->x -= rigidbodies[index].velocity.x;
- rigidbodies[index].velocity.x = rigidbodies[index].velocity.x;
- }
}
}
-void SetRigidbodyEnabled(int index, bool state)
+// Unitialize all physic objects and empty the objects pool
+void ClosePhysics()
{
- rigidbodies[index].enabled = state;
+ // Free all dynamic memory allocations
+ for (int i = 0; i < physicObjectsCount; i++) free(physicObjects[i]);
+
+ // Reset enabled physic objects count
+ physicObjectsCount = 0;
}
-void SetRigidbodyVelocity(int index, Vector2 velocity)
+// Create a new physic object dinamically, initialize it and add to pool
+PhysicObject CreatePhysicObject(Vector2 position, float rotation, Vector2 scale)
{
- rigidbodies[index].velocity.x = velocity.x;
- rigidbodies[index].velocity.y = velocity.y;
+ // Allocate dynamic memory
+ PhysicObject obj = (PhysicObject)malloc(sizeof(PhysicObjectData));
+
+ // Initialize physic object values with generic values
+ obj->id = physicObjectsCount;
+ obj->enabled = true;
+
+ obj->transform = (Transform){ (Vector2){ position.x - scale.x/2, position.y - scale.y/2 }, rotation, scale };
+
+ obj->rigidbody.enabled = false;
+ obj->rigidbody.mass = 1.0f;
+ obj->rigidbody.acceleration = (Vector2){ 0.0f, 0.0f };
+ obj->rigidbody.velocity = (Vector2){ 0.0f, 0.0f };
+ obj->rigidbody.applyGravity = false;
+ obj->rigidbody.isGrounded = false;
+ obj->rigidbody.friction = 0.0f;
+ obj->rigidbody.bounciness = 0.0f;
+
+ obj->collider.enabled = true;
+ obj->collider.type = COLLIDER_RECTANGLE;
+ obj->collider.bounds = TransformToRectangle(obj->transform);
+ obj->collider.radius = 0.0f;
+
+ // Add new physic object to the pointers array
+ physicObjects[physicObjectsCount] = obj;
+
+ // Increase enabled physic objects count
+ physicObjectsCount++;
+
+ return obj;
}
-void SetRigidbodyAcceleration(int index, Vector2 acceleration)
+// Destroy a specific physic object and take it out of the list
+void DestroyPhysicObject(PhysicObject pObj)
{
- rigidbodies[index].acceleration.x = acceleration.x;
- rigidbodies[index].acceleration.y = acceleration.y;
+ // Free dynamic memory allocation
+ free(physicObjects[pObj->id]);
+
+ // Remove *obj from the pointers array
+ for (int i = pObj->id; i < physicObjectsCount; i++)
+ {
+ // Resort all the following pointers of the array
+ if ((i + 1) < physicObjectsCount)
+ {
+ physicObjects[i] = physicObjects[i + 1];
+ physicObjects[i]->id = physicObjects[i + 1]->id;
+ }
+ else free(physicObjects[i]);
+ }
+
+ // Decrease enabled physic objects count
+ physicObjectsCount--;
}
-void AddRigidbodyForce(int index, Vector2 force)
+// Apply directional force to a physic object
+void ApplyForce(PhysicObject pObj, Vector2 force)
{
- rigidbodies[index].acceleration.x = force.x / rigidbodies[index].mass;
- rigidbodies[index].acceleration.y = force.y / rigidbodies[index].mass;
+ if (pObj->rigidbody.enabled)
+ {
+ pObj->rigidbody.velocity.x += force.x/pObj->rigidbody.mass;
+ pObj->rigidbody.velocity.y += force.y/pObj->rigidbody.mass;
+ }
}
-void AddForceAtPosition(Vector2 position, float intensity, float radius)
+// Apply radial force to all physic objects in range
+void ApplyForceAtPosition(Vector2 position, float force, float radius)
{
- for(int i = 0; i < maxElements; i++)
+ for (int i = 0; i < physicObjectsCount; i++)
{
- if(rigidbodies[i].enabled)
+ if (physicObjects[i]->rigidbody.enabled)
{
- // Get position from its collider
- Vector2 pos = {colliders[i].bounds.x, colliders[i].bounds.y};
+ // Calculate direction and distance between force and physic object pposition
+ Vector2 distance = (Vector2){ physicObjects[i]->transform.position.x - position.x, physicObjects[i]->transform.position.y - position.y };
+
+ if (physicObjects[i]->collider.type == COLLIDER_RECTANGLE)
+ {
+ distance.x += physicObjects[i]->transform.scale.x/2;
+ distance.y += physicObjects[i]->transform.scale.y/2;
+ }
- // Get distance between rigidbody position and target position
- float distance = Vector2Distance(position, pos);
+ float distanceLength = Vector2Length(distance);
- if(distance <= radius)
+ // Check if physic object is in force range
+ if (distanceLength <= radius)
{
- // Calculate force based on direction
- Vector2 force = {colliders[i].bounds.x - position.x, colliders[i].bounds.y - position.y};
+ // Normalize force direction
+ distance.x /= distanceLength;
+ distance.y /= -distanceLength;
- // Normalize the direction vector
- Vector2Normalize(&force);
+ // Calculate final force
+ Vector2 finalForce = { distance.x*force, distance.y*force };
- // Invert y value
- force.y *= -1;
-
- // Apply intensity and distance
- force = (Vector2){force.x * intensity / distance, force.y * intensity / distance};
-
- // Add calculated force to the rigidbodies
- AddRigidbodyForce(i, force);
+ // Apply force to the physic object
+ ApplyForce(physicObjects[i], finalForce);
}
}
}
}
-void SetColliderEnabled(int index, bool state)
-{
- colliders[index].enabled = state;
-}
-
-Collider GetCollider(int index)
+// Convert Transform data type to Rectangle (position and scale)
+Rectangle TransformToRectangle(Transform transform)
{
- return colliders[index];
-}
-
-Rigidbody GetRigidbody(int index)
-{
- return rigidbodies[index];
+ return (Rectangle){transform.position.x, transform.position.y, transform.scale.x, transform.scale.y};
}
//----------------------------------------------------------------------------------
-// Module specific Functions Definitions
+// Module specific Functions Definition
//----------------------------------------------------------------------------------
-static float Vector2Length(Vector2 vector)
-{
- return sqrt((vector.x * vector.x) + (vector.y * vector.y));
-}
-static float Vector2Distance(Vector2 a, Vector2 b)
+// Returns the dot product of two Vector2
+static float Vector2DotProduct(Vector2 v1, Vector2 v2)
{
- Vector2 vector = {b.x - a.x, b.y - a.y};
- return sqrt((vector.x * vector.x) + (vector.y * vector.y));
+ float result;
+
+ result = v1.x*v2.x + v1.y*v2.y;
+
+ return result;
}
-static void Vector2Normalize(Vector2 *vector)
+static float Vector2Length(Vector2 v)
{
- float length = Vector2Length(*vector);
+ float result;
- if (length != 0.0f)
- {
- vector->x /= length;
- vector->y /= length;
- }
- else
- {
- vector->x = 0.0f;
- vector->y = 0.0f;
- }
+ result = sqrt(v.x*v.x + v.y*v.y);
+
+ return result;
}
diff --git a/src/physac.h b/src/physac.h
index 9e1b0b88..b2ae2766 100644
--- a/src/physac.h
+++ b/src/physac.h
@@ -1,8 +1,8 @@
/**********************************************************************************************
*
-* [physac] raylib physics engine module - Basic functions to apply physics to 2D objects
+* [physac] raylib physics module - Basic functions to apply physics to 2D objects
*
-* Copyright (c) 2015 Victor Fisac and Ramon Santamaria
+* Copyright (c) 2016 Victor Fisac and Ramon Santamaria
*
* 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.
@@ -31,62 +31,67 @@
//----------------------------------------------------------------------------------
// Types and Structures Definition
+// NOTE: Below types are required for PHYSAC_STANDALONE usage
//----------------------------------------------------------------------------------
-// Collider types
-typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType;
-// Transform struct
+// Vector2 type
+typedef struct Vector2 {
+ float x;
+ float y;
+} Vector2;
+
+typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE } ColliderType;
+
typedef struct Transform {
Vector2 position;
- float rotation;
- Vector2 scale;
+ float rotation; // Radians (not used)
+ Vector2 scale; // Just for rectangle physic objects, for circle physic objects use collider radius and keep scale as { 0, 0 }
} Transform;
-// Rigidbody struct
typedef struct Rigidbody {
- bool enabled;
+ bool enabled; // Acts as kinematic state (collisions are calculated anyway)
float mass;
Vector2 acceleration;
Vector2 velocity;
- bool isGrounded;
- bool isContact; // Avoid freeze player when touching floor
bool applyGravity;
- float friction; // 0.0f to 1.0f
- float bounciness; // 0.0f to 1.0f
+ bool isGrounded;
+ float friction; // Normalized value
+ float bounciness;
} Rigidbody;
-// Collider struct
typedef struct Collider {
bool enabled;
ColliderType type;
- Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE
- int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE
+ Rectangle bounds; // Used for COLLIDER_RECTANGLE
+ int radius; // Used for COLLIDER_CIRCLE
} Collider;
+typedef struct PhysicObjectData {
+ unsigned int id;
+ Transform transform;
+ Rigidbody rigidbody;
+ Collider collider;
+ bool enabled;
+} PhysicObjectData, *PhysicObject;
+
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
//----------------------------------------------------------------------------------
-// Module Functions Declarations
+// Module Functions Declaration
//----------------------------------------------------------------------------------
-void InitPhysics(int maxPhysicElements); // Initialize all internal physics values
-void UnloadPhysics(); // Unload physic elements arrays
-
-void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot
-void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot
+void InitPhysics(Vector2 gravity); // Initializes pointers array (just pointers, fixed size)
+void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection
+void ClosePhysics(); // Unitialize all physic objects and empty the objects pool
-void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter
-void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody
-void SetRigidbodyVelocity(int index, Vector2 velocity); // Set velocity of rigidbody (without considering of mass value)
-void SetRigidbodyAcceleration(int index, Vector2 acceleration); // Set acceleration of rigidbody (without considering of mass value)
-void AddRigidbodyForce(int index, Vector2 force); // Set rigidbody force (considering mass value)
-void AddForceAtPosition(Vector2 position, float intensity, float radius); // Add a force to all enabled rigidbodies at a position
+PhysicObject CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool
+void DestroyPhysicObject(PhysicObject pObj); // Destroy a specific physic object and take it out of the list
-void SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider
+void ApplyForce(PhysicObject pObj, Vector2 force); // Apply directional force to a physic object
+void ApplyForceAtPosition(Vector2 position, float force, float radius); // Apply radial force to all physic objects in range
-Rigidbody GetRigidbody(int index); // Returns the internal rigidbody data defined by index parameter
-Collider GetCollider(int index); // Returns the internal collider data defined by index parameter
+Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale)
#ifdef __cplusplus
}
diff --git a/src/raygui.c b/src/raygui.c
index 60df2121..5064f123 100644
--- a/src/raygui.c
+++ b/src/raygui.c
@@ -22,17 +22,30 @@
*
**********************************************************************************************/
+//#define RAYGUI_STANDALONE // To use the raygui module as standalone lib, just uncomment this line
+ // NOTE: Some external funtions are required for drawing and input management
+
+#if !defined(RAYGUI_STANDALONE)
+ #include "raylib.h"
+#endif
+
#include "raygui.h"
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h> // Required for malloc(), free()
-#include <string.h> // Required for strcmp()
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf()
+ // NOTE: Those functions are only used in SaveGuiStyle() and LoadGuiStyle()
+
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strcmp()
+#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-//...
+#if defined(RAYGUI_STANDALONE)
+ #define KEY_LEFT 263
+ #define KEY_RIGHT 262
+ #define MOUSE_LEFT_BUTTON 0
+#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
@@ -50,7 +63,7 @@ typedef enum { SLIDER_DEFAULT, SLIDER_HOVER, SLIDER_ACTIVE } SliderState;
// Global Variables Definition
//----------------------------------------------------------------------------------
-//Current GUI style (default light)
+// Current GUI style (default light)
static int style[NUM_PROPERTIES] = {
0xf5f5f5ff, // GLOBAL_BASE_COLOR,
0xf5f5f5ff, // GLOBAL_BORDER_COLOR,
@@ -59,16 +72,16 @@ static int style[NUM_PROPERTIES] = {
1, // GLOBAL_BORDER_WIDTH
0xf5f5f5ff, // BACKGROUND_COLOR
1, // LABEL_BORDER_WIDTH
- 0x000000ff, // LABEL_TEXT_COLOR
+ 0x4d4d4dff, // LABEL_TEXT_COLOR
20, // LABEL_TEXT_PADDING
2, // BUTTON_BORDER_WIDTH
20, // BUTTON_TEXT_PADDING
0x828282ff, // BUTTON_DEFAULT_BORDER_COLOR
0xc8c8c8ff, // BUTTON_DEFAULT_INSIDE_COLOR
- 0x000000ff, // BUTTON_DEFAULT_TEXT_COLOR
+ 0x4d4d4dff, // BUTTON_DEFAULT_TEXT_COLOR
0xc8c8c8ff, // BUTTON_HOVER_BORDER_COLOR
0xffffffff, // BUTTON_HOVER_INSIDE_COLOR
- 0x000000ff, // BUTTON_HOVER_TEXT_COLOR
+ 0x353535ff, // BUTTON_HOVER_TEXT_COLOR
0x7bb0d6ff, // BUTTON_PRESSED_BORDER_COLOR
0xbcecffff, // BUTTON_PRESSED_INSIDE_COLOR
0x5f9aa7ff, // BUTTON_PRESSED_TEXT_COLOR
@@ -120,7 +133,7 @@ static int style[NUM_PROPERTIES] = {
0x000000ff, // SPINNER_PRESSED_TEXT_COLOR
1, // COMBOBOX_PADDING
30, // COMBOBOX_BUTTON_WIDTH
- 30, // COMBOBOX_BUTTON_HEIGHT
+ 20, // COMBOBOX_BUTTON_HEIGHT
1, // COMBOBOX_BORDER_WIDTH
0x828282ff, // COMBOBOX_DEFAULT_BORDER_COLOR
0xc8c8c8ff, // COMBOBOX_DEFAULT_INSIDE_COLOR
@@ -157,6 +170,29 @@ static int style[NUM_PROPERTIES] = {
//----------------------------------------------------------------------------------
static Color ColorMultiply(Color baseColor, float value);
+#if defined RAYGUI_STANDALONE
+static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value
+static int GetHexValue(Color color); // Returns hexadecimal value for a Color
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle
+static const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
+
+// NOTE: raygui depend on some raylib input and drawing functions
+// TODO: Replace by your own functions
+static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; }
+static int IsMouseButtonDown(int button) { return 0; }
+static int IsMouseButtonPressed(int button) { return 0; }
+static int IsMouseButtonReleased(int button) { return 0; }
+static int IsMouseButtonUp(int button) { return 0; }
+
+static int GetKeyPressed(void) { return 0; } // NOTE: Only used by GuiTextBox()
+static int IsKeyDown(int key) { return 0; } // NOTE: Only used by GuiSpinner()
+
+static int MeasureText(const char *text, int fontSize) { return 0; }
+static void DrawText(const char *text, int posX, int posY, int fontSize, Color color) { }
+static void DrawRectangleRec(Rectangle rec, Color color) { }
+static void DrawRectangle(int posX, int posY, int width, int height, Color color) { DrawRectangleRec((Rectangle){ posX, posY, width, height }, color); }
+#endif
+
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
@@ -164,7 +200,9 @@ static Color ColorMultiply(Color baseColor, float value);
// Label element, show text
void GuiLabel(Rectangle bounds, const char *text)
{
- GuiLabelEx(bounds,text, GetColor(style[LABEL_TEXT_COLOR]), BLANK, BLANK);
+ #define BLANK (Color){ 0, 0, 0, 0 } // Blank (Transparent)
+
+ GuiLabelEx(bounds, text, GetColor(style[LABEL_TEXT_COLOR]), BLANK, BLANK);
}
// Label element extended, configurable colors
@@ -173,7 +211,7 @@ void GuiLabelEx(Rectangle bounds, const char *text, Color textColor, Color borde
// Update control
//--------------------------------------------------------------------
int textWidth = MeasureText(text, style[GLOBAL_TEXT_FONTSIZE]);
- int textHeight = GetDefaultFont().size;
+ int textHeight = style[GLOBAL_TEXT_FONTSIZE];
if (bounds.width < textWidth) bounds.width = textWidth + style[LABEL_TEXT_PADDING];
if (bounds.height < textHeight) bounds.height = textHeight + style[LABEL_TEXT_PADDING]/2;
@@ -194,7 +232,7 @@ bool GuiButton(Rectangle bounds, const char *text)
Vector2 mousePoint = GetMousePosition();
int textWidth = MeasureText(text, style[GLOBAL_TEXT_FONTSIZE]);
- int textHeight = GetDefaultFont().size;
+ int textHeight = style[GLOBAL_TEXT_FONTSIZE];
// Update control
//--------------------------------------------------------------------
@@ -252,21 +290,34 @@ bool GuiToggleButton(Rectangle bounds, const char *text, bool toggle)
Vector2 mousePoint = GetMousePosition();
int textWidth = MeasureText(text, style[GLOBAL_TEXT_FONTSIZE]);
- int textHeight = GetDefaultFont().size;
+ int textHeight = style[GLOBAL_TEXT_FONTSIZE];
// Update control
//--------------------------------------------------------------------
if (toggleButton.width < textWidth) toggleButton.width = textWidth + style[TOGGLE_TEXT_PADDING];
if (toggleButton.height < textHeight) toggleButton.height = textHeight + style[TOGGLE_TEXT_PADDING]/2;
+
+ if (toggle) toggleState = TOGGLE_ACTIVE;
+ else toggleState = TOGGLE_UNACTIVE;
+
if (CheckCollisionPointRec(mousePoint, toggleButton))
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) toggleState = TOGGLE_PRESSED;
- else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) toggleState = TOGGLE_ACTIVE;
+ else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+ {
+ if (toggle)
+ {
+ toggle = false;
+ toggleState = TOGGLE_UNACTIVE;
+ }
+ else
+ {
+ toggle = true;
+ toggleState = TOGGLE_ACTIVE;
+ }
+ }
else toggleState = TOGGLE_HOVER;
}
-
- if (toggleState == TOGGLE_ACTIVE && !toggle) toggle = true;
- if (toggle) toggleState = TOGGLE_ACTIVE;
//--------------------------------------------------------------------
// Draw control
@@ -324,7 +375,7 @@ int GuiComboBox(Rectangle bounds, int comboNum, char **comboText, int comboActiv
Rectangle click = { bounds.x + bounds.width + style[COMBOBOX_PADDING], bounds.y, style[COMBOBOX_BUTTON_WIDTH], style[COMBOBOX_BUTTON_HEIGHT] };
Vector2 mousePoint = GetMousePosition();
- int textHeight = GetDefaultFont().size;
+ int textHeight = style[GLOBAL_TEXT_FONTSIZE];
for (int i = 0; i < comboNum; i++)
{
@@ -614,9 +665,8 @@ int GuiSpinner(Rectangle bounds, int value, int minValue, int maxValue)
Rectangle rightButtonBound = { bounds.x + bounds.width - bounds.width/4 + 1, bounds.y, bounds.width/4, bounds.height };
Vector2 mousePoint = GetMousePosition();
- int textHeight = GetDefaultFont().size;
-
int textWidth = MeasureText(FormatText("%i", value), style[GLOBAL_TEXT_FONTSIZE]);
+ //int textHeight = style[GLOBAL_TEXT_FONTSIZE]; // Unused variable
int buttonSide = 0;
@@ -789,11 +839,11 @@ int GuiSpinner(Rectangle bounds, int value, int minValue, int maxValue)
// NOTE: Requires static variables: framesCounter - ERROR!
char *GuiTextBox(Rectangle bounds, char *text)
{
- #define MAX_CHARS_LENGTH 20
- #define KEY_BACKSPACE_TEXT 3
+ #define MAX_CHARS_LENGTH 20
+ #define KEY_BACKSPACE_TEXT 259 // GLFW BACKSPACE: 3 + 256
int initPos = bounds.x + 4;
- char letter = -1;
+ int letter = -1;
static int framesCounter = 0;
Vector2 mousePoint = GetMousePosition();
@@ -822,12 +872,15 @@ char *GuiTextBox(Rectangle bounds, char *text)
}
else
{
- for (int i = 0; i < MAX_CHARS_LENGTH; i++)
+ if ((letter >= 32) && (letter < 127))
{
- if (text[i] == '\0')
+ for (int i = 0; i < MAX_CHARS_LENGTH; i++)
{
- text[i] = letter;
- break;
+ if (text[i] == '\0')
+ {
+ text[i] = (char)letter;
+ break;
+ }
}
}
}
@@ -848,10 +901,11 @@ char *GuiTextBox(Rectangle bounds, char *text)
DrawText(FormatText("%c", text[i]), initPos, bounds.y + style[TEXTBOX_TEXT_FONTSIZE], style[TEXTBOX_TEXT_FONTSIZE], GetColor(style[TEXTBOX_TEXT_COLOR]));
- initPos += ((GetDefaultFont().charRecs[(int)text[i] - 32].width + 2));
+ initPos += (MeasureText(FormatText("%c", text[i]), style[GLOBAL_TEXT_FONTSIZE]) + 2);
+ //initPos += ((GetDefaultFont().charRecs[(int)text[i] - 32].width + 2));
}
- if ((framesCounter/20)%2 && CheckCollisionPointRec(mousePoint, bounds)) DrawLine(initPos + 2, bounds.y + 5, initPos + 2, bounds.y + 10 + 15, GetColor(style[TEXTBOX_LINE_COLOR]));
+ if ((framesCounter/20)%2 && CheckCollisionPointRec(mousePoint, bounds)) DrawRectangle(initPos + 2, bounds.y + 5, 1, 20, GetColor(style[TEXTBOX_LINE_COLOR]));
//--------------------------------------------------------------------
return text;
@@ -1035,4 +1089,50 @@ static Color ColorMultiply(Color baseColor, float value)
multColor.b += (255 - multColor.b)*value;
return multColor;
-} \ No newline at end of file
+}
+
+#if defined (RAYGUI_STANDALONE)
+// Returns a Color struct from hexadecimal value
+static Color GetColor(int hexValue)
+{
+ Color color;
+
+ color.r = (unsigned char)(hexValue >> 24) & 0xFF;
+ color.g = (unsigned char)(hexValue >> 16) & 0xFF;
+ color.b = (unsigned char)(hexValue >> 8) & 0xFF;
+ color.a = (unsigned char)hexValue & 0xFF;
+
+ return color;
+}
+
+// Returns hexadecimal value for a Color
+static int GetHexValue(Color color)
+{
+ return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
+}
+
+// Check if point is inside rectangle
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
+{
+ bool collision = false;
+
+ if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true;
+
+ return collision;
+}
+
+// Formatting of text with variables to 'embed'
+static const char *FormatText(const char *text, ...)
+{
+ #define MAX_FORMATTEXT_LENGTH 64
+
+ static char buffer[MAX_FORMATTEXT_LENGTH];
+
+ va_list args;
+ va_start(args, text);
+ vsprintf(buffer, text, args);
+ va_end(args);
+
+ return buffer;
+}
+#endif \ No newline at end of file
diff --git a/src/raygui.h b/src/raygui.h
index 6906eca7..61741254 100644
--- a/src/raygui.h
+++ b/src/raygui.h
@@ -23,16 +23,49 @@
#ifndef RAYGUI_H
#define RAYGUI_H
-#include "raylib.h"
+//#include "raylib.h"
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define NUM_PROPERTIES 98
+#define NUM_PROPERTIES 98
//----------------------------------------------------------------------------------
// Types and Structures Definition
+// NOTE: Some types are required for RAYGUI_STANDALONE usage
//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+ #ifndef __cplusplus
+ // Boolean type
+ #ifndef true
+ typedef enum { false, true } bool;
+ #endif
+ #endif
+
+ // Vector2 type
+ typedef struct Vector2 {
+ float x;
+ float y;
+ } Vector2;
+
+ // Color type, RGBA (32bit)
+ typedef struct Color {
+ unsigned char r;
+ unsigned char g;
+ unsigned char b;
+ unsigned char a;
+ } Color;
+
+ // Rectangle type
+ typedef struct Rectangle {
+ int x;
+ int y;
+ int width;
+ int height;
+ } Rectangle;
+#endif
+
+// Gui properties enumeration
typedef enum GuiProperty {
GLOBAL_BASE_COLOR = 0,
GLOBAL_BORDER_COLOR,
diff --git a/src/raylib.h b/src/raylib.h
index c598ec30..706c4f4a 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -1,6 +1,6 @@
/**********************************************************************************************
*
-* raylib 1.4.0 (www.raylib.com)
+* raylib 1.5.0 (www.raylib.com)
*
* A simple and easy-to-use library to learn videogames programming
*
@@ -64,6 +64,7 @@
//#define PLATFORM_ANDROID // Android device
//#define PLATFORM_RPI // Raspberry Pi
//#define PLATFORM_WEB // HTML5 (emscripten, asm.js)
+//#define PLATFORM_OCULUS // Oculus Rift CV1
// Security check in case no PLATFORM_* defined
#if !defined(PLATFORM_DESKTOP) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_RPI) && !defined(PLATFORM_WEB)
@@ -71,7 +72,7 @@
#endif
#if defined(PLATFORM_ANDROID)
- typedef struct android_app; // Define android_app struct (android_native_app_glue.h)
+ typedef struct android_app; // Define android_app struct (android_native_app_glue.h)
#endif
//----------------------------------------------------------------------------------
@@ -174,8 +175,8 @@
// Gamepad Number
#define GAMEPAD_PLAYER1 0
#define GAMEPAD_PLAYER2 1
-#define GAMEPAD_PLAYER3 2
-#define GAMEPAD_PLAYER4 3
+#define GAMEPAD_PLAYER3 2 // Not supported
+#define GAMEPAD_PLAYER4 3 // Not supported
// Gamepad Buttons
// NOTE: Adjusted for a PS3 USB Controller
@@ -190,7 +191,35 @@
#define GAMEPAD_BUTTON_SELECT 9
#define GAMEPAD_BUTTON_START 10
-// TODO: Review Xbox360 USB Controller Buttons
+// Xbox360 USB Controller Buttons
+#define GAMEPAD_XBOX_BUTTON_A 0
+#define GAMEPAD_XBOX_BUTTON_B 1
+#define GAMEPAD_XBOX_BUTTON_X 2
+#define GAMEPAD_XBOX_BUTTON_Y 3
+#define GAMEPAD_XBOX_BUTTON_LB 4
+#define GAMEPAD_XBOX_BUTTON_RB 5
+#define GAMEPAD_XBOX_BUTTON_SELECT 6
+#define GAMEPAD_XBOX_BUTTON_START 7
+
+#if defined(PLATFORM_RPI)
+ #define GAMEPAD_XBOX_AXIS_DPAD_X 7
+ #define GAMEPAD_XBOX_AXIS_DPAD_Y 6
+ #define GAMEPAD_XBOX_AXIS_RIGHT_X 3
+ #define GAMEPAD_XBOX_AXIS_RIGHT_Y 4
+ #define GAMEPAD_XBOX_AXIS_LT 2
+ #define GAMEPAD_XBOX_AXIS_RT 5
+#else
+ #define GAMEPAD_XBOX_BUTTON_UP 10
+ #define GAMEPAD_XBOX_BUTTON_DOWN 12
+ #define GAMEPAD_XBOX_BUTTON_LEFT 13
+ #define GAMEPAD_XBOX_BUTTON_RIGHT 11
+ #define GAMEPAD_XBOX_AXIS_RIGHT_X 4
+ #define GAMEPAD_XBOX_AXIS_RIGHT_Y 3
+ #define GAMEPAD_XBOX_AXIS_LT_RT 2
+#endif
+
+#define GAMEPAD_XBOX_AXIS_LEFT_X 0
+#define GAMEPAD_XBOX_AXIS_LEFT_Y 1
// Android Physic Buttons
#define ANDROID_BACK 4
@@ -233,7 +262,10 @@
//----------------------------------------------------------------------------------
#ifndef __cplusplus
// Boolean type
-typedef enum { false, true } bool;
+ #if !defined(_STDBOOL_H)
+ typedef enum { false, true } bool;
+ #define _STDBOOL_H
+ #endif
#endif
// byte type
@@ -296,6 +328,13 @@ typedef struct Texture2D {
int format; // Data format (TextureFormat)
} Texture2D;
+// RenderTexture2D type, for texture rendering
+typedef struct RenderTexture2D {
+ unsigned int id; // Render texture (fbo) id
+ Texture2D texture; // Color buffer attachment texture
+ Texture2D depth; // Depth buffer attachment texture
+} RenderTexture2D;
+
// SpriteFont type, includes texture and charSet array data
typedef struct SpriteFont {
Texture2D texture; // Font texture
@@ -309,105 +348,128 @@ typedef struct SpriteFont {
// Camera type, defines a camera position/orientation in 3d space
typedef struct Camera {
- Vector3 position;
- Vector3 target;
- Vector3 up;
+ Vector3 position; // Camera position
+ Vector3 target; // Camera target it looks-at
+ Vector3 up; // Camera up vector (rotation over its axis)
+ float fovy; // Camera field-of-view apperture in Y (degrees)
} Camera;
+// Camera2D type, defines a 2d camera
+typedef struct Camera2D {
+ Vector2 offset; // Camera offset (displacement from target)
+ Vector2 target; // Camera target (rotation and zoom origin)
+ float rotation; // Camera rotation in degrees
+ float zoom; // Camera zoom (scaling), should be 1.0f by default
+} Camera2D;
+
// Bounding box type
typedef struct BoundingBox {
- Vector3 min;
- Vector3 max;
+ Vector3 min; // minimum vertex box-corner
+ Vector3 max; // maximum vertex box-corner
} BoundingBox;
// Vertex data definning a mesh
typedef struct Mesh {
- int vertexCount; // num vertices
- float *vertices; // vertex position (XYZ - 3 components per vertex)
- float *texcoords; // vertex texture coordinates (UV - 2 components per vertex)
- float *texcoords2; // vertex second texture coordinates (useful for lightmaps)
- float *normals; // vertex normals (XYZ - 3 components per vertex)
- float *tangents; // vertex tangents (XYZ - 3 components per vertex)
- unsigned char *colors; // vertex colors (RGBA - 4 components per vertex)
-
- BoundingBox bounds; // mesh limits defined by min and max points
-
- unsigned int vaoId; // OpenGL Vertex Array Object id
- unsigned int vboId[6]; // OpenGL Vertex Buffer Objects id (6 types of vertex data)
+ int vertexCount; // number of vertices stored in arrays
+ int triangleCount; // number of triangles stored (indexed or not)
+ float *vertices; // vertex position (XYZ - 3 components per vertex) (shader-location = 0)
+ float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
+ float *texcoords2; // vertex second texture coordinates (useful for lightmaps) (shader-location = 5)
+ float *normals; // vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
+ float *tangents; // vertex tangents (XYZ - 3 components per vertex) (shader-location = 4)
+ unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
+ unsigned short *indices;// vertex indices (in case vertex data comes indexed)
+
+ unsigned int vaoId; // OpenGL Vertex Array Object id
+ unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data)
} Mesh;
// Shader type (generic shader)
typedef struct Shader {
- unsigned int id; // Shader program id
-
- // TODO: This should be Texture2D objects
- unsigned int texDiffuseId; // Diffuse texture id
- unsigned int texNormalId; // Normal texture id
- unsigned int texSpecularId; // Specular texture id
+ unsigned int id; // Shader program id
- // Variable attributes
- int vertexLoc; // Vertex attribute location point (vertex shader)
- int texcoordLoc; // Texcoord attribute location point (vertex shader)
- int normalLoc; // Normal attribute location point (vertex shader)
- int colorLoc; // Color attibute location point (vertex shader)
-
- // Uniforms
- int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader)
- int tintColorLoc; // Color uniform location point (fragment shader)
+ // Vertex attributes locations (default locations)
+ int vertexLoc; // Vertex attribute location point (default-location = 0)
+ int texcoordLoc; // Texcoord attribute location point (default-location = 1)
+ int texcoord2Loc; // Texcoord2 attribute location point (default-location = 5)
+ int normalLoc; // Normal attribute location point (default-location = 2)
+ int tangentLoc; // Tangent attribute location point (default-location = 4)
+ int colorLoc; // Color attibute location point (default-location = 3)
+
+ // Uniform locations
+ int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader)
+ int tintColorLoc; // Diffuse color uniform location point (fragment shader)
- int mapDiffuseLoc; // Diffuse map texture uniform location point (fragment shader)
- int mapNormalLoc; // Normal map texture uniform location point (fragment shader)
- int mapSpecularLoc; // Specular map texture uniform location point (fragment shader)
+ // Texture map locations (generic for any kind of map)
+ int mapTexture0Loc; // Map texture uniform location point (default-texture-unit = 0)
+ int mapTexture1Loc; // Map texture uniform location point (default-texture-unit = 1)
+ int mapTexture2Loc; // Map texture uniform location point (default-texture-unit = 2)
} Shader;
// Material type
-// TODO: Redesign material-shaders-textures system
typedef struct Material {
- //Shader shader;
+ Shader shader; // Standard shader (supports 3 map textures)
- //Texture2D texDiffuse; // Diffuse texture
- //Texture2D texNormal; // Normal texture
- //Texture2D texSpecular; // Specular texture
+ Texture2D texDiffuse; // Diffuse texture (binded to shader mapTexture0Loc)
+ Texture2D texNormal; // Normal texture (binded to shader mapTexture1Loc)
+ Texture2D texSpecular; // Specular texture (binded to shader mapTexture2Loc)
- Color colDiffuse;
- Color colAmbient;
- Color colSpecular;
+ Color colDiffuse; // Diffuse color
+ Color colAmbient; // Ambient color
+ Color colSpecular; // Specular color
- float glossiness;
- float normalDepth;
+ float glossiness; // Glossiness level (Ranges from 0 to 1000)
} Material;
-// 3d Model type
-// TODO: Replace shader/testure by material
+// Model type
typedef struct Model {
- Mesh mesh;
- Matrix transform;
- Texture2D texture; // Only for OpenGL 1.1, on newer versions this should be in the shader
- Shader shader;
- //Material material;
+ Mesh mesh; // Vertex data buffers (RAM and VRAM)
+ Matrix transform; // Local transform matrix
+ Material material; // Shader and textures data
} Model;
+// Light type
+typedef struct LightData {
+ unsigned int id; // Light unique id
+ int type; // Light type: LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT
+ bool enabled; // Light enabled
+
+ Vector3 position; // Light position
+ Vector3 target; // Light target: LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction target)
+ float radius; // Light attenuation radius light intensity reduced with distance (world distance)
+
+ Color diffuse; // Light diffuse color
+ float intensity; // Light intensity level
+
+ float coneAngle; // Light cone max angle: LIGHT_SPOT
+} LightData, *Light;
+
+// Light types
+typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType;
+
// Ray type (useful for raycast)
typedef struct Ray {
- Vector3 position;
- Vector3 direction;
+ Vector3 position; // Ray position (origin)
+ Vector3 direction; // Ray direction
} Ray;
// Sound source type
typedef struct Sound {
- unsigned int source;
- unsigned int buffer;
+ unsigned int source; // Sound audio source id
+ unsigned int buffer; // Sound audio buffer id
} Sound;
// Wave type, defines audio wave data
typedef struct Wave {
void *data; // Buffer data pointer
unsigned int dataSize; // Data size in bytes
- unsigned int sampleRate;
- short bitsPerSample;
+ unsigned int sampleRate; // Samples per second to be played
+ short bitsPerSample; // Sample size in bits
short channels;
} Wave;
+typedef int RawAudioContext;
+
// Texture formats
// NOTE: Support depends on OpenGL version and platform
typedef enum {
@@ -455,7 +517,7 @@ typedef enum { TOUCH_UP, TOUCH_DOWN, TOUCH_MOVE } TouchAction;
// Gesture events
// NOTE: MAX_TOUCH_POINTS fixed to 2
-typedef struct {
+typedef struct GestureEvent {
int touchAction;
int pointCount;
int pointerId[MAX_TOUCH_POINTS];
@@ -465,37 +527,40 @@ typedef struct {
// Camera system modes
typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode;
-// Collider types
-typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE, COLLIDER_CAPSULE } ColliderType;
+typedef enum { COLLIDER_CIRCLE, COLLIDER_RECTANGLE } ColliderType;
-// Transform struct
typedef struct Transform {
Vector2 position;
- float rotation;
- Vector2 scale;
+ float rotation; // Radians (not used)
+ Vector2 scale; // Just for rectangle physic objects, for circle physic objects use collider radius and keep scale as { 0, 0 }
} Transform;
-// Rigidbody struct
typedef struct Rigidbody {
- bool enabled;
+ bool enabled; // Acts as kinematic state (collisions are calculated anyway)
float mass;
Vector2 acceleration;
Vector2 velocity;
- bool isGrounded;
- bool isContact; // Avoid freeze player when touching floor
bool applyGravity;
- float friction; // 0.0f to 1.0f
- float bounciness; // 0.0f to 1.0f
+ bool isGrounded;
+ float friction; // Normalized value
+ float bounciness;
} Rigidbody;
-// Collider struct
typedef struct Collider {
bool enabled;
ColliderType type;
- Rectangle bounds; // Used for COLLIDER_RECTANGLE and COLLIDER_CAPSULE
- int radius; // Used for COLLIDER_CIRCLE and COLLIDER_CAPSULE
+ Rectangle bounds; // Used for COLLIDER_RECTANGLE
+ int radius; // Used for COLLIDER_CIRCLE
} Collider;
+typedef struct PhysicObjectData {
+ unsigned int id;
+ Transform transform;
+ Rigidbody rigidbody;
+ Collider collider;
+ bool enabled;
+} PhysicObjectData, *PhysicObject;
+
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
@@ -514,27 +579,38 @@ void InitWindow(int width, int height, struct android_app *state); // Init Andr
void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics
#endif
+#if defined(PLATFORM_OCULUS)
+void InitOculusDevice(void); // Init Oculus Rift device
+void CloseOculusDevice(void); // Close Oculus Rift device
+void UpdateOculusTracking(void); // Update Oculus Rift tracking (position and orientation)
+#endif
+
void CloseWindow(void); // Close Window and Terminate Context
bool WindowShouldClose(void); // Detect if KEY_ESCAPE pressed or Close icon pressed
bool IsWindowMinimized(void); // Detect if window has been minimized (or lost focus)
void ToggleFullscreen(void); // Fullscreen toggle (only PLATFORM_DESKTOP)
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
-void SetCustomCursor(const char *cursorImage); // Set a custom cursor icon/image
-void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
-#endif
int GetScreenWidth(void); // Get current screen width
int GetScreenHeight(void); // Get current screen height
+void ShowCursor(void); // Shows cursor
+void HideCursor(void); // Hides cursor
+bool IsCursorHidden(void); // Returns true if cursor is not visible
+void EnableCursor(void); // Enables cursor
+void DisableCursor(void); // Disables cursor
+
void ClearBackground(Color color); // Sets Background Color
void BeginDrawing(void); // Setup drawing canvas to start drawing
-void BeginDrawingEx(int blendMode, Shader shader, Matrix transform); // Setup drawing canvas with extended parameters
void EndDrawing(void); // End canvas drawing and Swap Buffers (Double Buffering)
+void Begin2dMode(Camera2D camera); // Initialize 2D mode with custom camera
+void End2dMode(void); // Ends 2D mode custom camera usage
void Begin3dMode(Camera camera); // Initializes 3D mode for drawing (Camera setup)
void End3dMode(void); // Ends 3D mode and returns to default 2D orthographic mode
+void BeginTextureMode(RenderTexture2D target); // Initializes render texture for drawing
+void EndTextureMode(void); // Ends drawing to render texture
Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a ray trace from mouse position
-Vector2 WorldToScreen(Vector3 position, Camera camera); // Returns the screen space position from a 3d world space position
+Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position from a 3d world space position
Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix)
void SetTargetFPS(int fps); // Set target FPS (maximum)
@@ -569,6 +645,15 @@ bool IsKeyDown(int key); // Detect if a key is be
bool IsKeyReleased(int key); // Detect if a key has been released once
bool IsKeyUp(int key); // Detect if a key is NOT being pressed
int GetKeyPressed(void); // Get latest key pressed
+void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
+
+bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available
+float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis
+bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once
+bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gamepad button is being pressed
+bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once
+bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed
+#endif
bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once
bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed
@@ -580,20 +665,6 @@ Vector2 GetMousePosition(void); // Returns mouse positio
void SetMousePosition(Vector2 position); // Set mouse position XY
int GetMouseWheelMove(void); // Returns mouse wheel movement Y
-void ShowCursor(void); // Shows cursor
-void HideCursor(void); // Hides cursor
-void EnableCursor(void); // Enables cursor
-void DisableCursor(void); // Disables cursor
-bool IsCursorHidden(void); // Returns true if cursor is not visible
-
-bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available
-Vector2 GetGamepadMovement(int gamepad); // Return axis movement vector for a gamepad
-bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once
-bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gamepad button is being pressed
-bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once
-bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed
-#endif
-
int GetTouchX(void); // Returns touch position X for touch point 0 (relative to screen size)
int GetTouchY(void); // Returns touch position Y for touch point 0 (relative to screen size)
Vector2 GetTouchPosition(int index); // Returns touch position XY for a touch point index (relative to screen size)
@@ -608,9 +679,9 @@ bool IsButtonReleased(int button); // Detect if an android
// Gestures and Touch Handling Functions (Module: gestures)
//------------------------------------------------------------------------------------
void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures
-void UpdateGestures(void); // Update gestures detected (must be called every frame)
-bool IsGestureDetected(void); // Check if a gesture have been detected
-int GetGestureType(void); // Get latest detected gesture
+void UpdateGestures(void); // Update gestures detected (called automatically in PollInputEvents())
+bool IsGestureDetected(int gesture); // Check if a gesture have been detected
+int GetGestureDetected(void); // Get latest detected gesture
void SetGesturesEnabled(unsigned int gestureFlags); // Enable a set of gestures using flags
int GetTouchPointsCount(void); // Get touch points count
@@ -629,6 +700,7 @@ void UpdateCameraPlayer(Camera *camera, Vector3 *position); // Update camera and
void SetCameraPosition(Vector3 position); // Set internal camera position
void SetCameraTarget(Vector3 target); // Set internal camera target
+void SetCameraFovy(float fovy); // Set internal camera field-of-view-y
void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera)
void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera)
@@ -680,8 +752,10 @@ Texture2D LoadTexture(const char *fileName);
Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat); // Load a texture from raw data into GPU memory
Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource)
Texture2D LoadTextureFromImage(Image image); // Load a texture from image data
+RenderTexture2D LoadRenderTexture(int width, int height); // Load a texture to be used for rendering
void UnloadImage(Image image); // Unload image from CPU memory (RAM)
void UnloadTexture(Texture2D texture); // Unload texture from GPU memory
+void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory
Color *GetImageData(Image image); // Get pixel data from image as a Color struct array
Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image
void ImageToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two)
@@ -690,9 +764,12 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp);
Image ImageCopy(Image image); // Create an image duplicate (useful for transformations)
void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle
void ImageResize(Image *image, int newWidth, int newHeight); // Resize and image (bilinear filtering)
-void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image
+void ImageResizeNN(Image *image,int newWidth,int newHeight); // Resize and image (Nearest-Neighbor scaling algorithm)
Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font)
Image ImageTextEx(SpriteFont font, const char *text, int fontSize, int spacing, Color tint); // Create an image from text (custom sprite font)
+void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image
+void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color); // Draw text (default font) within an image (destination)
+void ImageDrawTextEx(Image *dst, Vector2 position, SpriteFont font, const char *text, int fontSize, int spacing, Color color); // Draw text (custom sprite font) within an image (destination)
void ImageFlipVertical(Image *image); // Flip image vertically
void ImageFlipHorizontal(Image *image); // Flip image horizontally
void ImageColorTint(Image *image, Color color); // Modify image color: tint
@@ -730,6 +807,8 @@ const char *SubText(const char *text, int position, int length);
//------------------------------------------------------------------------------------
// Basic 3d Shapes Drawing Functions (Module: models)
//------------------------------------------------------------------------------------
+void Draw3DLine(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space
+void Draw3DCircle(Vector3 center, float radius, float rotationAngle, Vector3 rotation, Color color); // Draw a circle in 3D world space
void DrawCube(Vector3 position, float width, float height, float lenght, Color color); // Draw cube
void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version)
void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color); // Draw cube wires
@@ -740,39 +819,44 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col
void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone
void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires
void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ
-void DrawQuad(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Color color); // Draw a quad
void DrawRay(Ray ray, Color color); // Draw a ray line
void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0))
void DrawGizmo(Vector3 position); // Draw simple gizmo
+void DrawLight(Light light); // Draw light in 3D world
//DrawTorus(), DrawTeapot() are useless...
//------------------------------------------------------------------------------------
// Model 3d Loading and Drawing Functions (Module: models)
//------------------------------------------------------------------------------------
-Model LoadModel(const char *fileName); // Load a 3d model (.OBJ)
-Model LoadModelEx(Mesh data); // Load a 3d model (from vertex data)
-//Model LoadModelFromRES(const char *rresName, int resId); // TODO: Load a 3d model from rRES file (raylib Resource)
-Model LoadHeightmap(Image heightmap, Vector3 size); // Load a heightmap image as a 3d model
-Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based)
-void UnloadModel(Model model); // Unload 3d model from memory
-void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model
+Model LoadModel(const char *fileName); // Load a 3d model (.OBJ)
+Model LoadModelEx(Mesh data, bool dynamic); // Load a 3d model (from mesh data)
+Model LoadModelFromRES(const char *rresName, int resId); // Load a 3d model from rRES file (raylib Resource)
+Model LoadHeightmap(Image heightmap, Vector3 size); // Load a heightmap image as a 3d model
+Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based)
+void UnloadModel(Model model); // Unload 3d model from memory
+void SetModelTexture(Model *model, Texture2D texture); // Link a texture to a model
+
+Material LoadMaterial(const char *fileName); // Load material data (from file)
+Material LoadDefaultMaterial(void); // Load default material (uses default models shader)
+Material LoadStandardMaterial(void); // Load standard material (uses material attributes and lighting shader)
+void UnloadMaterial(Material material); // Unload material textures from VRAM
void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set)
void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
-void DrawModelWires(Model model, Vector3 position, float scale, Color color); // Draw a model wires (with texture if set)
-void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
-void DrawBoundingBox(BoundingBox box); // Draw bounding box (wires)
+void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set)
+void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
+void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires)
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture
void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec
-BoundingBox CalculateBoundingBox(Mesh mesh); // Calculate mesh bounding box limits
+BoundingBox CalculateBoundingBox(Mesh mesh); // Calculate mesh bounding box limits
bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); // Detect collision between two spheres
-bool CheckCollisionBoxes(Vector3 minBBox1, Vector3 maxBBox1, Vector3 minBBox2, Vector3 maxBBox2); // Detect collision between two boxes
-bool CheckCollisionBoxSphere(Vector3 minBBox, Vector3 maxBBox, Vector3 centerSphere, float radiusSphere); // Detect collision between box and sphere
+bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Detect collision between two bounding boxes
+bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radiusSphere); // Detect collision between box and sphere
bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius); // Detect collision between ray and sphere
bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint); // Detect collision between ray and sphere with extended parameters and collision point detection
-bool CheckCollisionRayBox(Ray ray, Vector3 minBBox, Vector3 maxBBox); // Detect collision between ray and box
+bool CheckCollisionRayBox(Ray ray, BoundingBox box); // Detect collision between ray and box
Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius); // Detect collision of player radius with cubicmap
// NOTE: Return the normal vector of the impacted surface
//------------------------------------------------------------------------------------
@@ -780,51 +864,49 @@ Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *p
// NOTE: This functions are useless when using OpenGL 1.1
//------------------------------------------------------------------------------------
Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations
-unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shaders strings and return program id
void UnloadShader(Shader shader); // Unload a custom shader from memory
-void SetPostproShader(Shader shader); // Set fullscreen postproduction shader
-void SetCustomShader(Shader shader); // Set custom shader to be used in batch draw
-void SetDefaultShader(void); // Set default shader to be used in batch draw
-void SetModelShader(Model *model, Shader shader); // Link a shader to a model
-bool IsPosproShaderEnabled(void); // Check if postprocessing shader is enabled
-
-int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location
-void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float)
-void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int)
-void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4)
-void SetShaderMapDiffuse(Shader *shader, Texture2D texture); // Default diffuse shader map texture assignment
-void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture); // Normal map texture shader assignment
-void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture); // Specular map texture shader assignment
-void SetShaderMap(Shader *shader, int mapLocation, Texture2D texture, int textureUnit); // TODO: Generic shader map assignment
-
-void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied)
+
+Shader GetDefaultShader(void); // Get default shader
+Shader GetStandardShader(void); // Get default shader
+Texture2D GetDefaultTexture(void); // Get default texture
+
+int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location
+void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float)
+void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int)
+void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4)
+
+void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix)
+void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix)
+
+void BeginShaderMode(Shader shader); // Begin custom shader drawing
+void EndShaderMode(void); // End custom shader drawing (use default shader)
+void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied)
+void EndBlendMode(void); // End blending mode (reset to default: alpha blending)
+
+Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool
+void DestroyLight(Light light); // Destroy a light and take it out of the list
//----------------------------------------------------------------------------------
-// Physics System Functions (engine-module: physac)
+// Physics System Functions (Module: physac)
//----------------------------------------------------------------------------------
-void InitPhysics(int maxPhysicElements); // Initialize all internal physics values
-void UnloadPhysics(); // Unload physic elements arrays
-
-void AddRigidbody(int index, Rigidbody rigidbody); // Initialize a new rigidbody with parameters to internal index slot
-void AddCollider(int index, Collider collider); // Initialize a new Collider with parameters to internal index slot
+void InitPhysics(Vector2 gravity); // Initializes pointers array (just pointers, fixed size)
+void UpdatePhysics(); // Update physic objects, calculating physic behaviours and collisions detection
+void ClosePhysics(); // Unitialize all physic objects and empty the objects pool
-void ApplyPhysics(int index, Vector2 *position); // Apply physics to internal rigidbody, physics calculations are applied to position pointer parameter
-void SetRigidbodyEnabled(int index, bool state); // Set enabled state to a defined rigidbody
-void SetRigidbodyVelocity(int index, Vector2 velocity); // Set velocity of rigidbody (without considering of mass value)
-void SetRigidbodyAcceleration(int index, Vector2 acceleration); // Set acceleration of rigidbody (without considering of mass value)
-void AddRigidbodyForce(int index, Vector2 force); // Set rigidbody force (considering mass value)
-void AddForceAtPosition(Vector2 position, float intensity, float radius); // Add a force to all enabled rigidbodies at a position
+PhysicObject CreatePhysicObject(Vector2 position, float rotation, Vector2 scale); // Create a new physic object dinamically, initialize it and add to pool
+void DestroyPhysicObject(PhysicObject pObj); // Destroy a specific physic object and take it out of the list
-void SetColliderEnabled(int index, bool state); // Set enabled state to a defined collider
+void ApplyForce(PhysicObject pObj, Vector2 force); // Apply directional force to a physic object
+void ApplyForceAtPosition(Vector2 position, float force, float radius); // Apply radial force to all physic objects in range
-Rigidbody GetRigidbody(int index); // Returns the internal rigidbody data defined by index parameter
-Collider GetCollider(int index); // Returns the internal collider data defined by index parameter
+Rectangle TransformToRectangle(Transform transform); // Convert Transform data type to Rectangle (position and scale)
//------------------------------------------------------------------------------------
// Audio Loading and Playing Functions (Module: audio)
//------------------------------------------------------------------------------------
void InitAudioDevice(void); // Initialize audio device and context
void CloseAudioDevice(void); // Close the audio device and context (and music stream)
+bool IsAudioDeviceReady(void); // True if call to InitAudioDevice() was successful and CloseAudioDevice() has not been called yet
Sound LoadSound(char *fileName); // Load sound to memory
Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data
@@ -833,19 +915,28 @@ void UnloadSound(Sound sound); // Unload sound
void PlaySound(Sound sound); // Play a sound
void PauseSound(Sound sound); // Pause a sound
void StopSound(Sound sound); // Stop playing a sound
-bool SoundIsPlaying(Sound sound); // Check if a sound is currently playing
+bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
-void PlayMusicStream(char *fileName); // Start music playing (open stream)
-void UpdateMusicStream(void); // Updates buffers for music streaming
-void StopMusicStream(void); // Stop music playing (close stream)
-void PauseMusicStream(void); // Pause music playing
-void ResumeMusicStream(void); // Resume playing paused music
-bool MusicIsPlaying(void); // Check if music is playing
-void SetMusicVolume(float volume); // Set volume for music (1.0 is max level)
-float GetMusicTimeLength(void); // Get current music time length (in seconds)
-float GetMusicTimePlayed(void); // Get current music time played (in seconds)
+int PlayMusicStream(int index, char *fileName); // Start music playing (open stream)
+void UpdateMusicStream(int index); // Updates buffers for music streaming
+void StopMusicStream(int index); // Stop music playing (close stream)
+void PauseMusicStream(int index); // Pause music playing
+void ResumeMusicStream(int index); // Resume playing paused music
+bool IsMusicPlaying(int index); // Check if music is playing
+void SetMusicVolume(int index, float volume); // Set volume for music (1.0 is max level)
+float GetMusicTimeLength(int index); // Get current music time length (in seconds)
+float GetMusicTimePlayed(int index); // Get current music time played (in seconds)
+int GetMusicStreamCount(void);
+void SetMusicPitch(int index, float pitch);
+
+// used to output raw audio streams, returns negative numbers on error
+// if floating point is false the data size is 16bit short, otherwise it is float 32bit
+RawAudioContext InitRawAudioContext(int sampleRate, int channels, bool floatingPoint);
+
+void CloseRawAudioContext(RawAudioContext ctx);
+int BufferRawAudioContext(RawAudioContext ctx, void *data, unsigned short numberElements); // returns number of elements buffered
#ifdef __cplusplus
}
diff --git a/src/raymath.h b/src/raymath.h
index 35cee39f..4075a1a9 100644
--- a/src/raymath.h
+++ b/src/raymath.h
@@ -73,7 +73,7 @@
//----------------------------------------------------------------------------------
#if defined(RAYMATH_STANDALONE)
- // Vector2 type
+ // Vector2 type
typedef struct Vector2 {
float x;
float y;
@@ -151,13 +151,13 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top,
RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far); // Returns perspective projection matrix
RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far); // Returns orthographic projection matrix
RMDEF Matrix MatrixLookAt(Vector3 position, Vector3 target, Vector3 up); // Returns camera look-at matrix (view matrix)
-RMDEF void PrintMatrix(Matrix m); // Print matrix utility
//------------------------------------------------------------------------------------
// Functions Declaration to work with Quaternions
//------------------------------------------------------------------------------------
RMDEF float QuaternionLength(Quaternion quat); // Compute the length of a quaternion
RMDEF void QuaternionNormalize(Quaternion *q); // Normalize provided quaternion
+RMDEF void QuaternionInvert(Quaternion *quat); // Invert provided quaternion
RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2); // Calculate two quaternion multiplication
RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float slerp); // Calculates spherical linear interpolation between two quaternions
RMDEF Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion for a given rotation matrix
@@ -177,9 +177,7 @@ RMDEF void QuaternionTransform(Quaternion *q, Matrix mat); // Transfo
#if defined(RAYMATH_IMPLEMENTATION) || defined(RAYMATH_EXTERN_INLINE)
-#include <stdio.h> // Used only on PrintMatrix()
-#include <math.h> // Standard math libary: sin(), cos(), tan()...
-#include <stdlib.h> // Used for abs()
+#include <math.h> // Required for: sinf(), cosf(), tan(), fabs()
//----------------------------------------------------------------------------------
// Module Functions Definition - Vector3 math
@@ -341,15 +339,14 @@ RMDEF Vector3 VectorReflect(Vector3 vector, Vector3 normal)
return result;
}
-// Transforms a Vector3 with a given Matrix
+// Transforms a Vector3 by a given Matrix
+// TODO: Review math (matrix transpose required?)
RMDEF void VectorTransform(Vector3 *v, Matrix mat)
{
float x = v->x;
float y = v->y;
float z = v->z;
- //MatrixTranspose(&mat);
-
v->x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12;
v->y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13;
v->z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14;
@@ -803,7 +800,7 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top,
// Returns perspective projection matrix
RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far)
{
- double top = near*tanf(fovy*PI/360.0f);
+ double top = near*tan(fovy*PI/360.0);
double right = top*aspect;
return MatrixFrustum(-right, right, -top, top, near, far);
@@ -870,17 +867,6 @@ RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up)
return result;
}
-// Print matrix utility (for debug)
-RMDEF void PrintMatrix(Matrix m)
-{
- printf("----------------------\n");
- printf("%2.2f %2.2f %2.2f %2.2f\n", m.m0, m.m4, m.m8, m.m12);
- printf("%2.2f %2.2f %2.2f %2.2f\n", m.m1, m.m5, m.m9, m.m13);
- printf("%2.2f %2.2f %2.2f %2.2f\n", m.m2, m.m6, m.m10, m.m14);
- printf("%2.2f %2.2f %2.2f %2.2f\n", m.m3, m.m7, m.m11, m.m15);
- printf("----------------------\n");
-}
-
//----------------------------------------------------------------------------------
// Module Functions Definition - Quaternion math
//----------------------------------------------------------------------------------
@@ -908,6 +894,23 @@ RMDEF void QuaternionNormalize(Quaternion *q)
q->w *= ilength;
}
+// Invert provided quaternion
+RMDEF void QuaternionInvert(Quaternion *quat)
+{
+ float length = QuaternionLength(*quat);
+ float lengthSq = length*length;
+
+ if (lengthSq != 0.0)
+ {
+ float i = 1.0f/lengthSq;
+
+ quat->x *= -i;
+ quat->y *= -i;
+ quat->z *= -i;
+ quat->w *= i;
+ }
+}
+
// Calculate two quaternion multiplication
RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2)
{
diff --git a/src/rlgl.c b/src/rlgl.c
index 5f8a5ea1..9a102c01 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -28,38 +28,38 @@
#include "rlgl.h"
-#include <stdio.h> // Standard input / output lib
-#include <stdlib.h> // Declares malloc() and free() for memory management, rand()
-#include <string.h> // Declares strcmp(), strlen(), strtok()
+#include <stdio.h> // Required for: fopen(), fclose(), fread()... [Used only on ReadTextFile()]
+#include <stdlib.h> // Required for: malloc(), free(), rand()
+#include <string.h> // Required for: strcmp(), strlen(), strtok()
-#include "raymath.h" // Required for Vector3 and Matrix functions
+#ifndef RLGL_STANDALONE
+ #include "raymath.h" // Required for Vector3 and Matrix functions
+#endif
#if defined(GRAPHICS_API_OPENGL_11)
- #ifdef __APPLE__ // OpenGL include for OSX
- #include <OpenGL/gl.h>
+ #ifdef __APPLE__
+ #include <OpenGL/gl.h> // OpenGL 1.1 library for OSX
#else
- #include <GL/gl.h> // Basic OpenGL include
+ #include <GL/gl.h> // OpenGL 1.1 library
#endif
#endif
#if defined(GRAPHICS_API_OPENGL_33)
- #ifdef __APPLE__ // OpenGL include for OSX
- #include <OpenGL/gl3.h>
+ #ifdef __APPLE__
+ #include <OpenGL/gl3.h> // OpenGL 3 library for OSX
#else
- //#define GLEW_STATIC
- //#include <GL/glew.h> // GLEW header, includes OpenGL headers
- #include "glad.h" // glad header, includes OpenGL headers
+ #include "external/glad.h" // GLAD library, includes OpenGL headers
#endif
#endif
#if defined(GRAPHICS_API_OPENGL_ES2)
- #include <EGL/egl.h>
- #include <GLES2/gl2.h>
- #include <GLES2/gl2ext.h>
+ #include <EGL/egl.h> // EGL library
+ #include <GLES2/gl2.h> // OpenGL ES 2.0 library
+ #include <GLES2/gl2ext.h> // OpenGL ES 2.0 extensions library
#endif
#if defined(RLGL_STANDALONE)
- #include <stdarg.h> // Used for functions with variable number of parameters (TraceLog())
+ #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() [Used only on TraceLog()]
#endif
//----------------------------------------------------------------------------------
@@ -69,6 +69,8 @@
#define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes
#define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations)
// NOTE: Every vertex are 3 floats (12 bytes)
+
+#define MAX_LIGHTS 8 // Max lights supported by standard shader
#ifndef GL_SHADING_LANGUAGE_VERSION
#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
@@ -113,58 +115,36 @@
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
#endif
+
+// Default vertex attribute names on shader to set location points
+#define DEFAULT_ATTRIB_POSITION_NAME "vertexPosition" // shader-location = 0
+#define DEFAULT_ATTRIB_TEXCOORD_NAME "vertexTexCoord" // shader-location = 1
+#define DEFAULT_ATTRIB_NORMAL_NAME "vertexNormal" // shader-location = 2
+#define DEFAULT_ATTRIB_COLOR_NAME "vertexColor" // shader-location = 3
+#define DEFAULT_ATTRIB_TANGENT_NAME "vertexTangent" // shader-location = 4
+#define DEFAULT_ATTRIB_TEXCOORD2_NAME "vertexTexCoord2" // shader-location = 5
+
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
-// Vertex buffer (position + color arrays)
-// NOTE: Used for lines and triangles VAOs
-typedef struct {
- int vCounter;
- int cCounter;
- float *vertices; // 3 components per vertex
- unsigned char *colors; // 4 components per vertex
-} VertexPositionColorBuffer;
-
-// Vertex buffer (position + texcoords + color arrays)
-// NOTE: Not used
-typedef struct {
- int vCounter;
- int tcCounter;
- int cCounter;
- float *vertices; // 3 components per vertex
- float *texcoords; // 2 components per vertex
- unsigned char *colors; // 4 components per vertex
-} VertexPositionColorTextureBuffer;
-
-// Vertex buffer (position + texcoords + normals arrays)
-// NOTE: Not used
+// Dynamic vertex buffers (position + texcoords + colors + indices arrays)
typedef struct {
- int vCounter;
- int tcCounter;
- int nCounter;
- float *vertices; // 3 components per vertex
- float *texcoords; // 2 components per vertex
- float *normals; // 3 components per vertex
- //short *normals; // NOTE: Less data load... but padding issues and normalizing required!
-} VertexPositionTextureNormalBuffer;
-
-// Vertex buffer (position + texcoords + colors + indices arrays)
-// NOTE: Used for quads VAO
-typedef struct {
- int vCounter;
- int tcCounter;
- int cCounter;
- float *vertices; // 3 components per vertex
- float *texcoords; // 2 components per vertex
- unsigned char *colors; // 4 components per vertex
+ int vCounter; // vertex position counter to process (and draw) from full buffer
+ int tcCounter; // vertex texcoord counter to process (and draw) from full buffer
+ int cCounter; // vertex color counter to process (and draw) from full buffer
+ float *vertices; // vertex position (XYZ - 3 components per vertex) (shader-location = 0)
+ float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
+ unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
- unsigned int *indices; // 6 indices per quad (could be int)
+ unsigned int *indices; // vertex indices (in case vertex data comes indexed) (6 indices per quad)
#elif defined(GRAPHICS_API_OPENGL_ES2)
- unsigned short *indices; // 6 indices per quad (must be short)
+ unsigned short *indices; // vertex indices (in case vertex data comes indexed) (6 indices per quad)
// NOTE: 6*2 byte = 12 byte, not alignment problem!
#endif
-} VertexPositionColorTextureIndexBuffer;
+ unsigned int vaoId; // OpenGL Vertex Array Object id
+ unsigned int vboId[4]; // OpenGL Vertex Buffer Objects id (4 types of vertex data)
+} DynamicBuffer;
// Draw call type
// NOTE: Used to track required draw-calls, organized by texture
@@ -174,22 +154,6 @@ typedef struct {
// TODO: Store draw state -> blending mode, shader
} DrawCall;
-// pixel type (same as Color type)
-// NOTE: Used exclusively in mipmap generation functions
-typedef struct {
- unsigned char r;
- unsigned char g;
- unsigned char b;
- unsigned char a;
-} pixel;
-
-// Framebuffer Object type
-typedef struct {
- GLuint id;
- GLuint colorTextureId;
- GLuint depthTextureId;
-} FBO;
-
#if defined(RLGL_STANDALONE)
typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
#endif
@@ -210,23 +174,11 @@ static DrawMode currentDrawMode;
static float currentDepth = -1.0f;
-// Vertex arrays for lines, triangles and quads
-static VertexPositionColorBuffer lines; // No texture support
-static VertexPositionColorBuffer triangles; // No texture support
-static VertexPositionColorTextureIndexBuffer quads;
-
-// Shader Programs
-static Shader defaultShader, simpleShader;
-static Shader currentShader; // By default, defaultShader
-
-// Vertex Array Objects (VAO)
-static GLuint vaoLines, vaoTriangles, vaoQuads;
-
-// Vertex Buffer Objects (VBO)
-static GLuint linesBuffer[2];
-static GLuint trianglesBuffer[2];
-static GLuint quadsBuffer[4];
+static DynamicBuffer lines;
+static DynamicBuffer triangles;
+static DynamicBuffer quads;
+// Default buffers draw calls
static DrawCall *draws;
static int drawsCounter;
@@ -235,27 +187,28 @@ static Vector3 *tempBuffer;
static int tempBufferCount = 0;
static bool useTempBuffer = false;
+// Shader Programs
+static Shader defaultShader;
+static Shader standardShader;
+static Shader currentShader; // By default, defaultShader
+
// Flags for supported extensions
static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension)
// Compressed textures support flags
-//static bool texCompDXTSupported = false; // DDS texture compression support
static bool texCompETC1Supported = false; // ETC1 texture compression support
static bool texCompETC2Supported = false; // ETC2/EAC texture compression support
static bool texCompPVRTSupported = false; // PVR texture compression support
static bool texCompASTCSupported = false; // ASTC texture compression support
-// Framebuffer object and texture
-static FBO postproFbo;
-static Model postproQuad;
-
-// Shaders related variables
-static bool enabledPostpro = false;
+// Lighting data
+static Light lights[MAX_LIGHTS]; // Lights pool
+static int lightsCount; // Counts current enabled physic objects
#endif
// Compressed textures support flags
-static bool texCompDXTSupported = false; // DDS texture compression support
-static bool npotSupported = false; // NPOT textures full support
+static bool texCompDXTSupported = false; // DDS texture compression support
+static bool npotSupported = false; // NPOT textures full support
#if defined(GRAPHICS_API_OPENGL_ES2)
// NOTE: VAO functionality is exposed through extensions (OES)
@@ -265,9 +218,6 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays;
//static PFNGLISVERTEXARRAYOESPROC glIsVertexArray; // NOTE: Fails in WebGL, omitted
#endif
-// Save screen size data (render size), required for postpro quad
-static int screenWidth, screenHeight;
-
static int blendMode = 0;
// White texture useful for plain color polys (required by shader)
@@ -278,40 +228,33 @@ unsigned int whiteTexture;
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-static Shader LoadDefaultShader(void);
-static Shader LoadSimpleShader(void);
-static void InitializeBuffers(void);
-static void InitializeBuffersGPU(void);
-static void UpdateBuffers(void);
-static char *TextFileRead(char *fn);
-
static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat);
+static unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shader strings and return program id
+
+static Shader LoadDefaultShader(void); // Load default shader (just vertex positioning and texture coloring)
+static Shader LoadStandardShader(void); // Load standard shader (support materials and lighting)
+static void LoadDefaultShaderLocations(Shader *shader); // Bind default shader locations (attributes and uniforms)
+static void UnloadDefaultShader(void); // Unload default shader
+static void UnloadStandardShader(void); // Unload standard shader
-FBO rlglLoadFBO(int width, int height);
-void rlglUnloadFBO(FBO fbo);
+static void LoadDefaultBuffers(void); // Load default internal buffers (lines, triangles, quads)
+static void UpdateDefaultBuffers(void); // Update default internal buffers (VAOs/VBOs) with vertex data
+static void DrawDefaultBuffers(void); // Draw default internal buffers vertex data
+static void UnloadDefaultBuffers(void); // Unload default internal buffers vertex data from CPU and GPU
+
+static void SetShaderLights(Shader shader); // Sets shader uniform values for lights array
+
+static char *ReadTextFile(const char *fileName);
#endif
#if defined(GRAPHICS_API_OPENGL_11)
static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight);
-static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight);
+static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight);
#endif
#if defined(RLGL_STANDALONE)
static void TraceLog(int msgType, const char *text, ...);
-#endif
-
-#if defined(GRAPHICS_API_OPENGL_ES2)
-// NOTE: strdup() functions replacement (not C99, POSIX function, not available on emscripten)
-// Duplicates a string, returning an identical malloc'd string
-char *mystrdup(const char *str)
-{
- size_t len = strlen(str) + 1;
- void *newstr = malloc(len);
-
- if (newstr == NULL) return NULL;
-
- return (char *)memcpy(newstr, str, len);
-}
+float *MatrixToFloat(Matrix mat); // Converts Matrix to float array
#endif
//----------------------------------------------------------------------------------
@@ -412,7 +355,6 @@ void rlRotatef(float angleDeg, float x, float y, float z)
Vector3 axis = (Vector3){ x, y, z };
VectorNormalize(&axis);
matRotation = MatrixRotate(axis, angleDeg*DEG2RAD);
-
MatrixTranspose(&matRotation);
*currentMatrix = MatrixMultiply(*currentMatrix, matRotation);
@@ -459,6 +401,12 @@ void rlOrtho(double left, double right, double bottom, double top, double near,
#endif
+// Set the viewport area (trasnformation from normalized device coordinates to window coordinates)
+void rlViewport(int x, int y, int width, int height)
+{
+ glViewport(x, y, width, height);
+}
+
//----------------------------------------------------------------------------------
// Module Functions Definition - Vertex level operations
//----------------------------------------------------------------------------------
@@ -780,17 +728,71 @@ void rlDisableTexture(void)
#endif
}
+// Enable rendering to texture (fbo)
+void rlEnableRenderTexture(unsigned int id)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ glBindFramebuffer(GL_FRAMEBUFFER, id);
+
+ //glDisable(GL_CULL_FACE); // Allow double side drawing for texture flipping
+ //glCullFace(GL_FRONT);
+#endif
+}
+
+// Disable rendering to texture
+void rlDisableRenderTexture(void)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ //glEnable(GL_CULL_FACE);
+ //glCullFace(GL_BACK);
+#endif
+}
+
+// Enable depth test
+void rlEnableDepthTest(void)
+{
+ glEnable(GL_DEPTH_TEST);
+}
+
+// Disable depth test
+void rlDisableDepthTest(void)
+{
+ glDisable(GL_DEPTH_TEST);
+}
+
+// Enable wire mode
+void rlEnableWireMode(void)
+{
+#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
+ // NOTE: glPolygonMode() not available on OpenGL ES
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+#endif
+}
+
+// Disable wire mode
+void rlDisableWireMode(void)
+{
+#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
+ // NOTE: glPolygonMode() not available on OpenGL ES
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+#endif
+}
+
// Unload texture from GPU memory
void rlDeleteTextures(unsigned int id)
{
- glDeleteTextures(1, &id);
+ if (id != 0) glDeleteTextures(1, &id);
}
-// Enable rendering to postprocessing FBO
-void rlEnablePostproFBO()
+// Unload render texture from GPU memory
+void rlDeleteRenderTextures(RenderTexture2D target)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- glBindFramebuffer(GL_FRAMEBUFFER, postproFbo.id);
+ if (target.id != 0) glDeleteFramebuffers(1, &target.id);
+ if (target.texture.id != 0) glDeleteTextures(1, &target.texture.id);
+ if (target.depth.id != 0) glDeleteTextures(1, &target.depth.id);
#endif
}
@@ -798,7 +800,7 @@ void rlEnablePostproFBO()
void rlDeleteShader(unsigned int id)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- glDeleteProgram(id);
+ if (id != 0) glDeleteProgram(id);
#endif
}
@@ -806,7 +808,11 @@ void rlDeleteShader(unsigned int id)
void rlDeleteVertexArrays(unsigned int id)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- if (vaoSupported) glDeleteVertexArrays(1, &id);
+ if (vaoSupported)
+ {
+ if (id != 0) glDeleteVertexArrays(1, &id);
+ TraceLog(INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", id);
+ }
#endif
}
@@ -814,7 +820,11 @@ void rlDeleteVertexArrays(unsigned int id)
void rlDeleteBuffers(unsigned int id)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- glDeleteBuffers(1, &id);
+ if (id != 0)
+ {
+ glDeleteBuffers(1, &id);
+ if (!vaoSupported) TraceLog(INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id);
+ }
#endif
}
@@ -899,8 +909,8 @@ void rlglInit(void)
vaoSupported = true;
npotSupported = true;
- // NOTE: We don't need to check again supported extensions but we do (in case GLEW is replaced sometime)
// We get a list of available extensions and we check for some of them (compressed textures)
+ // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that)
glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
const char *extList[numExt];
@@ -911,13 +921,18 @@ void rlglInit(void)
// NOTE: We have to duplicate string because glGetString() returns a const value
// If not duplicated, it fails in some systems (Raspberry Pi)
- char *extensionsDup = mystrdup(extensions);
+ // Equivalent to function: char *strdup(const char *str)
+ char *extensionsDup;
+ size_t len = strlen(extensions) + 1;
+ void *newstr = malloc(len);
+ if (newstr == NULL) extensionsDup = NULL;
+ extensionsDup = (char *)memcpy(newstr, extensions, len);
// NOTE: String could be splitted using strtok() function (string.h)
// NOTE: strtok() modifies the received string, it can not be const
char *extList[512]; // Allocate 512 strings pointers (2 KB)
-
+
extList[numExt] = strtok(extensionsDup, " ");
while (extList[numExt] != NULL)
@@ -961,10 +976,12 @@ void rlglInit(void)
// DDS texture compression support
if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) ||
+ (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) ||
(strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) texCompDXTSupported = true;
// ETC1 texture compression support
- if (strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) texCompETC1Supported = true;
+ if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) ||
+ (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) texCompETC1Supported = true;
// ETC2/EAC texture compression support
if (strcmp(extList[i], (const char *)"GL_ARB_ES3_compatibility") == 0) texCompETC2Supported = true;
@@ -1012,15 +1029,12 @@ void rlglInit(void)
if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture);
else TraceLog(WARNING, "Base white texture could not be loaded");
- // Init default Shader (Custom for GL 3.3 and ES2)
+ // Init default Shader (customized for GL 3.3 and ES2)
defaultShader = LoadDefaultShader();
- simpleShader = LoadSimpleShader();
- //customShader = LoadShader("custom.vs", "custom.fs"); // Works ok
-
+ standardShader = LoadStandardShader();
currentShader = defaultShader;
- InitializeBuffers(); // Init vertex arrays
- InitializeBuffersGPU(); // Init VBO and VAO
+ LoadDefaultBuffers(); // Initialize default vertex arrays buffers (lines, triangles, quads)
// Init temp vertex buffer, used when transformation required (translate, rotate, scale)
tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE);
@@ -1041,197 +1055,23 @@ void rlglInit(void)
#endif
}
-// Init postpro system
-// NOTE: Uses global variables screenWidth and screenHeight
-// Modifies global variables: postproFbo, postproQuad
-void rlglInitPostpro(void)
-{
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- postproFbo = rlglLoadFBO(screenWidth, screenHeight);
-
- if (postproFbo.id > 0)
- {
- // Create a simple quad model to render fbo texture
- Mesh quad;
-
- quad.vertexCount = 6;
-
- float w = (float)screenWidth;
- float h = (float)screenHeight;
-
- float quadPositions[6*3] = { w, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, h, 0.0f, 0.0f, h, 0.0f, w, h, 0.0f, w, 0.0f, 0.0f };
- float quadTexcoords[6*2] = { 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
- float quadNormals[6*3] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f };
- unsigned char quadColors[6*4] = { 255 };
-
- quad.vertices = quadPositions;
- quad.texcoords = quadTexcoords;
- quad.normals = quadNormals;
- quad.colors = quadColors;
-
- postproQuad = rlglLoadModel(quad);
-
- // NOTE: postproFbo.colorTextureId must be assigned to postproQuad model shader
- }
-#endif
-}
-
-// Load a framebuffer object
-FBO rlglLoadFBO(int width, int height)
-{
- FBO fbo;
- fbo.id = 0;
-
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- // Create the texture that will serve as the color attachment for the framebuffer
- glGenTextures(1, &fbo.colorTextureId);
- glBindTexture(GL_TEXTURE_2D, fbo.colorTextureId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- // Create the renderbuffer that will serve as the depth attachment for the framebuffer.
- glGenRenderbuffers(1, &fbo.depthTextureId);
- glBindRenderbuffer(GL_RENDERBUFFER, fbo.depthTextureId);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
-
- // NOTE: We can also use a texture for depth buffer (GL_ARB_depth_texture/GL_OES_depth_texture extensions)
- // A renderbuffer is simpler than a texture and could offer better performance on embedded devices
-/*
- glGenTextures(1, &fbo.depthTextureId);
- glBindTexture(GL_TEXTURE_2D, fbo.depthTextureId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-*/
- // Create the framebuffer object
- glGenFramebuffers(1, &fbo.id);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo.id);
-
- // Attach color texture and depth renderbuffer to FBO
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.colorTextureId, 0);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo.depthTextureId);
-
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-
- if (status != GL_FRAMEBUFFER_COMPLETE)
- {
- TraceLog(WARNING, "Framebuffer object could not be created...");
-
- switch(status)
- {
- case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(WARNING, "Framebuffer is unsupported"); break;
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete attachment"); break;
-#if defined(GRAPHICS_API_OPENGL_ES2)
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(WARNING, "Framebuffer incomplete dimensions"); break;
-#endif
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete missing attachment"); break;
- default: break;
- }
-
- glDeleteTextures(1, &fbo.colorTextureId);
- glDeleteTextures(1, &fbo.depthTextureId);
- }
- else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", fbo);
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-#endif
-
- return fbo;
-}
-
-// Unload framebuffer object
-void rlglUnloadFBO(FBO fbo)
-{
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- glDeleteFramebuffers(1, &fbo.id);
- glDeleteTextures(1, &fbo.colorTextureId);
- glDeleteTextures(1, &fbo.depthTextureId);
-
- TraceLog(INFO, "[FBO ID %i] Unloaded framebuffer object successfully", fbo.id);
-#endif
-}
-
// Vertex Buffer Object deinitialization (memory free)
void rlglClose(void)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- // Unbind everything
- if (vaoSupported) glBindVertexArray(0);
- glDisableVertexAttribArray(0);
- glDisableVertexAttribArray(1);
- glDisableVertexAttribArray(2);
- glDisableVertexAttribArray(3);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-
- glUseProgram(0);
-
- // Delete VBOs
- glDeleteBuffers(1, &linesBuffer[0]);
- glDeleteBuffers(1, &linesBuffer[1]);
- glDeleteBuffers(1, &trianglesBuffer[0]);
- glDeleteBuffers(1, &trianglesBuffer[1]);
- glDeleteBuffers(1, &quadsBuffer[0]);
- glDeleteBuffers(1, &quadsBuffer[1]);
- glDeleteBuffers(1, &quadsBuffer[2]);
- glDeleteBuffers(1, &quadsBuffer[3]);
-
- if (vaoSupported)
- {
- // Delete VAOs
- glDeleteVertexArrays(1, &vaoLines);
- glDeleteVertexArrays(1, &vaoTriangles);
- glDeleteVertexArrays(1, &vaoQuads);
- }
-
- //glDetachShader(defaultShaderProgram, v);
- //glDetachShader(defaultShaderProgram, f);
- //glDeleteShader(v);
- //glDeleteShader(f);
- glDeleteProgram(defaultShader.id);
- glDeleteProgram(simpleShader.id);
-
- // Free vertex arrays memory
- free(lines.vertices);
- free(lines.colors);
-
- free(triangles.vertices);
- free(triangles.colors);
-
- free(quads.vertices);
- free(quads.texcoords);
- free(quads.colors);
- free(quads.indices);
-
- // Free GPU texture
+ UnloadDefaultShader();
+ UnloadStandardShader();
+ UnloadDefaultBuffers();
+
+ // Delete default white texture
glDeleteTextures(1, &whiteTexture);
TraceLog(INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture);
-
- if (postproFbo.id != 0)
+
+ // Unload lights
+ if (lightsCount > 0)
{
- rlglUnloadFBO(postproFbo);
-
- // Unload postpro quad model data
-#if defined(GRAPHICS_API_OPENGL_11)
- free(postproQuad.mesh.vertices);
- free(postproQuad.mesh.texcoords);
- free(postproQuad.mesh.normals);
-#endif
-
- rlDeleteBuffers(postproQuad.mesh.vboId[0]);
- rlDeleteBuffers(postproQuad.mesh.vboId[1]);
- rlDeleteBuffers(postproQuad.mesh.vboId[2]);
-
- rlDeleteVertexArrays(postproQuad.mesh.vaoId);
-
- TraceLog(INFO, "Unloaded postprocessing data");
+ for (int i = 0; i < lightsCount; i++) free(lights[i]);
+ lightsCount = 0;
}
free(draws);
@@ -1242,331 +1082,22 @@ void rlglClose(void)
void rlglDraw(void)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- UpdateBuffers();
-
- if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0))
- {
- glUseProgram(currentShader.id);
-
- Matrix matMVP = MatrixMultiply(modelview, projection); // Create modelview-projection matrix
-
- glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(matMVP));
- glUniform1i(currentShader.mapDiffuseLoc, 0);
- }
-
- // NOTE: We draw in this order: lines, triangles, quads
-
- if (lines.vCounter > 0)
- {
- glBindTexture(GL_TEXTURE_2D, whiteTexture);
-
- if (vaoSupported)
- {
- glBindVertexArray(vaoLines);
- }
- else
- {
- glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]);
- glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(currentShader.vertexLoc);
-
- if (currentShader.colorLoc != -1)
- {
- glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]);
- glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- glEnableVertexAttribArray(currentShader.colorLoc);
- }
- }
-
- glDrawArrays(GL_LINES, 0, lines.vCounter);
-
- if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- if (triangles.vCounter > 0)
- {
- glBindTexture(GL_TEXTURE_2D, whiteTexture);
-
- if (vaoSupported)
- {
- glBindVertexArray(vaoTriangles);
- }
- else
- {
- glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]);
- glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(currentShader.vertexLoc);
-
- if (currentShader.colorLoc != -1)
- {
- glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]);
- glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- glEnableVertexAttribArray(currentShader.colorLoc);
- }
- }
-
- glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter);
-
- if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- if (quads.vCounter > 0)
- {
- int quadsCount = 0;
- int numIndicesToProcess = 0;
- int indicesOffset = 0;
-
- if (vaoSupported)
- {
- glBindVertexArray(vaoQuads);
- }
- else
- {
- // Enable vertex attributes
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]);
- glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(currentShader.vertexLoc);
-
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]);
- glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(currentShader.texcoordLoc);
-
- if (currentShader.colorLoc != -1)
- {
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]);
- glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- glEnableVertexAttribArray(currentShader.colorLoc);
- }
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]);
- }
-
- //TraceLog(DEBUG, "Draws required per frame: %i", drawsCounter);
-
- for (int i = 0; i < drawsCounter; i++)
- {
- quadsCount = draws[i].vertexCount/4;
- numIndicesToProcess = quadsCount*6; // Get number of Quads * 6 index by Quad
-
- //TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount);
-
- glBindTexture(GL_TEXTURE_2D, draws[i].textureId);
-
- // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process
-#if defined(GRAPHICS_API_OPENGL_33)
- glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid*) (sizeof(GLuint) * indicesOffset));
-#elif defined(GRAPHICS_API_OPENGL_ES2)
- glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid*) (sizeof(GLushort) * indicesOffset));
-#endif
- //GLenum err;
- //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM!
-
- indicesOffset += draws[i].vertexCount/4*6;
- }
-
- if (!vaoSupported)
- {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- }
-
- glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
- }
-
- if (vaoSupported) glBindVertexArray(0); // Unbind VAO
-
- glUseProgram(0); // Unbind shader program
-
- // Reset draws counter
- drawsCounter = 1;
- draws[0].textureId = whiteTexture;
- draws[0].vertexCount = 0;
-
- // Reset vertex counters for next frame
- lines.vCounter = 0;
- lines.cCounter = 0;
-
- triangles.vCounter = 0;
- triangles.cCounter = 0;
-
- quads.vCounter = 0;
- quads.tcCounter = 0;
- quads.cCounter = 0;
-
- // Reset depth for next draw
- currentDepth = -1.0f;
-#endif
-}
-
-// Draw with postprocessing shader
-void rlglDrawPostpro(void)
-{
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- rlglDrawModel(postproQuad, (Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.0f, (Vector3){1.0f, 1.0f, 1.0f}, (Color){ 255, 255, 255, 255 }, false);
-#endif
-}
-
-// Draw a 3d model
-// NOTE: Model transform can come within model struct
-void rlglDrawModel(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color color, bool wires)
-{
-#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
- // NOTE: glPolygonMode() not available on OpenGL ES
- if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-#endif
-
-#if defined(GRAPHICS_API_OPENGL_11)
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, model.texture.id);
-
- // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model
- glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array
- glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array
- glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array
-
- glVertexPointer(3, GL_FLOAT, 0, model.mesh.vertices); // Pointer to vertex coords array
- glTexCoordPointer(2, GL_FLOAT, 0, model.mesh.texcoords); // Pointer to texture coords array
- glNormalPointer(GL_FLOAT, 0, model.mesh.normals); // Pointer to normals array
- //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.mesh.colors); // Pointer to colors array (NOT USED)
-
- rlPushMatrix();
- rlTranslatef(position.x, position.y, position.z);
- rlScalef(scale.x, scale.y, scale.z);
- rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
-
- rlColor4ub(color.r, color.g, color.b, color.a);
-
- glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount);
- rlPopMatrix();
-
- glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array
- glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array
- glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array
-
- glDisable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, 0);
-#endif
-
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- glUseProgram(model.shader.id);
-
- // At this point the modelview matrix just contains the view matrix (camera)
- // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix()
- Matrix matView = modelview; // View matrix (camera)
- Matrix matProjection = projection; // Projection matrix (perspective)
-
- // Calculate transformation matrix from function parameters
- // Get transform matrix (rotation -> scale -> translation)
- Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
- Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
- Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
- Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
-
- // Combine model internal transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
- Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates
-
- // Calculate model-view matrix combining matModel and matView
- Matrix matModelView = MatrixMultiply(matModel, matView); // Transform to camera-space coordinates
-
- // Calculate model-view-projection matrix (MVP)
- Matrix matMVP = MatrixMultiply(matModelView, matProjection); // Transform to screen-space coordinates
-
- // Send combined model-view-projection matrix to shader
- glUniformMatrix4fv(model.shader.mvpLoc, 1, false, MatrixToFloat(matMVP));
-
- // Apply color tinting to model
- // NOTE: Just update one uniform on fragment shader
- float vColor[4] = { (float)color.r/255, (float)color.g/255, (float)color.b/255, (float)color.a/255 };
- glUniform4fv(model.shader.tintColorLoc, 1, vColor);
-
- // Set shader textures (diffuse, normal, specular)
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, model.shader.texDiffuseId);
- glUniform1i(model.shader.mapDiffuseLoc, 0);
-
- if (model.shader.texNormalId != 0)
- {
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, model.shader.texNormalId);
- }
-
- if (model.shader.texSpecularId != 0)
- {
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, model.shader.texSpecularId);
- }
-
- if (vaoSupported)
- {
- glBindVertexArray(model.mesh.vaoId);
- }
- else
- {
- // Bind model VBOs data
- glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[0]);
- glVertexAttribPointer(model.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(model.shader.vertexLoc);
-
- glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[1]);
- glVertexAttribPointer(model.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(model.shader.texcoordLoc);
-
- // Add normals support
- if (model.shader.normalLoc != -1)
- {
- glBindBuffer(GL_ARRAY_BUFFER, model.mesh.vboId[2]);
- glVertexAttribPointer(model.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(model.shader.normalLoc);
- }
- }
-
- // Draw call!
- glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount);
-
- //glDisableVertexAttribArray(model.shader.vertexLoc);
- //glDisableVertexAttribArray(model.shader.texcoordLoc);
- //if (model.shader.normalLoc != -1) glDisableVertexAttribArray(model.shader.normalLoc);
-
- if (model.shader.texNormalId != 0)
- {
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- if (model.shader.texSpecularId != 0)
+/*
+ for (int i = 0; i < modelsCount; i++)
{
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, 0);
+ rlglDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform);
}
-
- glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0
- glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
-
- if (vaoSupported) glBindVertexArray(0); // Unbind VAO
- else glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs
-
- glUseProgram(0); // Unbind shader program
-#endif
-
-#if defined (GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
- // NOTE: glPolygonMode() not available on OpenGL ES
- if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+*/
+ // NOTE: Default buffers always drawn at the end
+ UpdateDefaultBuffers();
+ DrawDefaultBuffers();
#endif
}
// Initialize Graphics Device (OpenGL stuff)
// NOTE: Stores global variables screenWidth and screenHeight
void rlglInitGraphics(int offsetX, int offsetY, int width, int height)
-{
- // Save screen size data (global vars), required on postpro quad
- // NOTE: Size represents render size, it could differ from screen size!
- screenWidth = width;
- screenHeight = height;
-
+{
// NOTE: Required! viewport must be recalculated if screen resized!
glViewport(offsetX/2, offsetY/2, width - offsetX, height - offsetY); // Set viewport width and height
@@ -1577,7 +1108,7 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height)
//glClearDepth(1.0f); // Clear depth buffer (default)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers, depth buffer is used for 3D
- glEnable(GL_DEPTH_TEST); // Enables depth testing (required for 3D)
+ glDisable(GL_DEPTH_TEST); // Disable depth testing for 2D (only used for 3D)
glDepthFunc(GL_LEQUAL); // Type of depth testing to apply
glEnable(GL_BLEND); // Enable color blending (required to work with transparencies)
@@ -1611,55 +1142,24 @@ void rlglInitGraphics(int offsetX, int offsetY, int width, int height)
}
// Get world coordinates from screen coordinates
-// TODO: It doesn't work! It drives me crazy!
-// NOTE: Using global variables: screenWidth, screenHeight
Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view)
{
- Vector3 result = { 0.0f, 0.0f, 0.0f }; // Object coordinates
+ Vector3 result = { 0.0f, 0.0f, 0.0f };
- //GLint viewport[4];
- //glGetIntegerv(GL_VIEWPORT, viewport); // Not available on OpenGL ES 2.0
-
- // Viewport data
- int x = 0; // viewport[0]
- int y = 0; // viewport[1]
- int width = screenWidth; // viewport[2]
- int height = screenHeight; // viewport[3]
+ // Calculate unproject matrix (multiply projection matrix and view matrix) and invert it
+ Matrix matProjView = MatrixMultiply(proj, view);
+ MatrixInvert(&matProjView);
- Matrix modelviewprojection = MatrixMultiply(view, proj);
- MatrixInvert(&modelviewprojection);
-/*
- // NOTE: Compute unproject using Vector3
-
- // Transformation of normalized coordinates between -1 and 1
- result.x = ((source.x - (float)x)/(float)width)*2.0f - 1.0f;
- result.y = ((source.y - (float)y)/(float)height)*2.0f - 1.0f;
- result.z = source.z*2.0f - 1.0f;
-
- // Object coordinates (multiply vector by matrix)
- VectorTransform(&result, modelviewprojection);
-*/
-
- // NOTE: Compute unproject using Quaternion (Vector4)
- Quaternion quat;
+ // Create quaternion from source point
+ Quaternion quat = { source.x, source.y, source.z, 1.0f };
- quat.x = ((source.x - (float)x)/(float)width)*2.0f - 1.0f;
- quat.y = ((source.y - (float)y)/(float)height)*2.0f - 1.0f;
- quat.z = source.z*2.0f - 1.0f;
- quat.w = 1.0f;
+ // Multiply quat point by unproject matrix
+ QuaternionTransform(&quat, matProjView);
- QuaternionTransform(&quat, modelviewprojection);
-
- if (quat.w != 0.0f)
- {
- quat.x /= quat.w;
- quat.y /= quat.w;
- quat.z /= quat.w;
- }
-
- result.x = quat.x;
- result.y = quat.y;
- result.z = quat.z;
+ // Normalized world points in vectors
+ result.x = quat.x/quat.w;
+ result.y = quat.y/quat.w;
+ result.z = quat.z/quat.w;
return result;
}
@@ -1843,6 +1343,102 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma
return id;
}
+// Load a texture to be used for rendering (fbo with color and depth attachments)
+RenderTexture2D rlglLoadRenderTexture(int width, int height)
+{
+ RenderTexture2D target;
+
+ target.id = 0;
+
+ target.texture.id = 0;
+ target.texture.width = width;
+ target.texture.height = height;
+ target.texture.format = UNCOMPRESSED_R8G8B8;
+ target.texture.mipmaps = 1;
+
+ target.depth.id = 0;
+ target.depth.width = width;
+ target.depth.height = height;
+ target.depth.format = 19; //DEPTH_COMPONENT_24BIT
+ target.depth.mipmaps = 1;
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ // Create the texture that will serve as the color attachment for the framebuffer
+ glGenTextures(1, &target.texture.id);
+ glBindTexture(GL_TEXTURE_2D, target.texture.id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+#if defined(GRAPHICS_API_OPENGL_33)
+ #define USE_DEPTH_TEXTURE
+#else
+ #define USE_DEPTH_RENDERBUFFER
+#endif
+
+#if defined(USE_DEPTH_RENDERBUFFER)
+ // Create the renderbuffer that will serve as the depth attachment for the framebuffer.
+ glGenRenderbuffers(1, &target.depth.id);
+ glBindRenderbuffer(GL_RENDERBUFFER, target.depth.id);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); // GL_DEPTH_COMPONENT24 not supported on Android
+#elif defined(USE_DEPTH_TEXTURE)
+ // NOTE: We can also use a texture for depth buffer (GL_ARB_depth_texture/GL_OES_depth_texture extension required)
+ // A renderbuffer is simpler than a texture and could offer better performance on embedded devices
+ glGenTextures(1, &target.depth.id);
+ glBindTexture(GL_TEXTURE_2D, target.depth.id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+#endif
+
+ // Create the framebuffer object
+ glGenFramebuffers(1, &target.id);
+ glBindFramebuffer(GL_FRAMEBUFFER, target.id);
+
+ // Attach color texture and depth renderbuffer to FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target.texture.id, 0);
+#if defined(USE_DEPTH_RENDERBUFFER)
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, target.depth.id);
+#elif defined(USE_DEPTH_TEXTURE)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, target.depth.id, 0);
+#endif
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ TraceLog(WARNING, "Framebuffer object could not be created...");
+
+ switch (status)
+ {
+ case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(WARNING, "Framebuffer is unsupported"); break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete attachment"); break;
+#if defined(GRAPHICS_API_OPENGL_ES2)
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(WARNING, "Framebuffer incomplete dimensions"); break;
+#endif
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete missing attachment"); break;
+ default: break;
+ }
+
+ glDeleteTextures(1, &target.texture.id);
+ glDeleteTextures(1, &target.depth.id);
+ glDeleteFramebuffers(1, &target.id);
+ }
+ else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", target.id);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+#endif
+
+ return target;
+}
+
+// Update already loaded texture in GPU with new data
void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data)
{
glBindTexture(GL_TEXTURE_2D, id);
@@ -1893,9 +1489,9 @@ void rlglGenerateMipmaps(Texture2D texture)
void *data = rlglReadTexturePixels(texture);
// NOTE: data size is reallocated to fit mipmaps data
+ // NOTE: CPU mipmap generation only supports RGBA 32bit data
int mipmapCount = GenerateMipmaps(data, texture.width, texture.height);
- // TODO: Adjust mipmap size depending on texture format!
int size = texture.width*texture.height*4; // RGBA 32bit only
int offset = size;
@@ -1916,7 +1512,12 @@ void rlglGenerateMipmaps(Texture2D texture)
TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture.id);
-#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ // NOTE: Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data
+ free(data);
+
+#endif
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically
TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture.id);
@@ -1925,84 +1526,415 @@ void rlglGenerateMipmaps(Texture2D texture)
#endif
}
else TraceLog(WARNING, "[TEX ID %i] Mipmaps can not be generated", texture.id);
-
+
glBindTexture(GL_TEXTURE_2D, 0);
}
-// Load vertex data into a VAO (if supported) and VBO
-Model rlglLoadModel(Mesh mesh)
+// Upload vertex data into a VAO (if supported) and VBO
+void rlglLoadMesh(Mesh *mesh, bool dynamic)
{
- Model model;
+ mesh->vaoId = 0; // Vertex Array Object
+ mesh->vboId[0] = 0; // Vertex positions VBO
+ mesh->vboId[1] = 0; // Vertex texcoords VBO
+ mesh->vboId[2] = 0; // Vertex normals VBO
+ mesh->vboId[3] = 0; // Vertex colors VBO
+ mesh->vboId[4] = 0; // Vertex tangents VBO
+ mesh->vboId[5] = 0; // Vertex texcoords2 VBO
+ mesh->vboId[6] = 0; // Vertex indices VBO
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ int drawHint = GL_STATIC_DRAW;
+ if (dynamic) drawHint = GL_DYNAMIC_DRAW;
- model.mesh = mesh;
- model.transform = MatrixIdentity();
- model.mesh.vaoId = 0; // Vertex Array Object
- model.mesh.vboId[0] = 0; // Vertex position VBO
- model.mesh.vboId[1] = 0; // Texcoords VBO
- model.mesh.vboId[2] = 0; // Normals VBO
+ GLuint vaoId = 0; // Vertex Array Objects (VAO)
+ GLuint vboId[7]; // Vertex Buffer Objects (VBOs)
-#if defined(GRAPHICS_API_OPENGL_11)
- model.texture.id = 0; // No texture required
- model.shader.id = 0; // No shader used
+ if (vaoSupported)
+ {
+ // Initialize Quads VAO (Buffer A)
+ glGenVertexArrays(1, &vaoId);
+ glBindVertexArray(vaoId);
+ }
+
+ // NOTE: Attributes must be uploaded considering default locations points
+
+ // Enable vertex attributes: position (shader-location = 0)
+ glGenBuffers(1, &vboId[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, drawHint);
+ glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(0);
+
+ // Enable vertex attributes: texcoords (shader-location = 1)
+ glGenBuffers(1, &vboId[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId[1]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, drawHint);
+ glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(1);
+
+ // Enable vertex attributes: normals (shader-location = 2)
+ if (mesh->normals != NULL)
+ {
+ glGenBuffers(1, &vboId[2]);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId[2]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, drawHint);
+ glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(2);
+ }
+ else
+ {
+ // Default color vertex attribute set to WHITE
+ glVertexAttrib3f(2, 1.0f, 1.0f, 1.0f);
+ glDisableVertexAttribArray(2);
+ }
+
+ // Default color vertex attribute (shader-location = 3)
+ if (mesh->colors != NULL)
+ {
+ glGenBuffers(1, &vboId[3]);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId[3]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, drawHint);
+ glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(3);
+ }
+ else
+ {
+ // Default color vertex attribute set to WHITE
+ glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f);
+ glDisableVertexAttribArray(3);
+ }
+
+ // Default tangent vertex attribute (shader-location = 4)
+ if (mesh->tangents != NULL)
+ {
+ glGenBuffers(1, &vboId[4]);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId[4]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->tangents, drawHint);
+ glVertexAttribPointer(4, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(4);
+ }
+ else
+ {
+ // Default tangents vertex attribute
+ glVertexAttrib3f(4, 0.0f, 0.0f, 0.0f);
+ glDisableVertexAttribArray(4);
+ }
+
+ // Default texcoord2 vertex attribute (shader-location = 5)
+ if (mesh->texcoords2 != NULL)
+ {
+ glGenBuffers(1, &vboId[5]);
+ glBindBuffer(GL_ARRAY_BUFFER, vboId[5]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, drawHint);
+ glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(5);
+ }
+ else
+ {
+ // Default tangents vertex attribute
+ glVertexAttrib2f(5, 0.0f, 0.0f);
+ glDisableVertexAttribArray(5);
+ }
+
+ if (mesh->indices != NULL)
+ {
+ glGenBuffers(1, &vboId[6]);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[6]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*mesh->triangleCount*3, mesh->indices, GL_STATIC_DRAW);
+ }
-#elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- model.texture.id = whiteTexture; // Default whiteTexture
- model.texture.width = 1; // Default whiteTexture width
- model.texture.height = 1; // Default whiteTexture height
- model.texture.format = UNCOMPRESSED_R8G8B8A8; // Default whiteTexture format
- model.shader = simpleShader; // Default model shader
- GLuint vaoModel = 0; // Vertex Array Objects (VAO)
- GLuint vertexBuffer[3]; // Vertex Buffer Objects (VBO)
+ mesh->vboId[0] = vboId[0]; // Vertex position VBO
+ mesh->vboId[1] = vboId[1]; // Texcoords VBO
+ mesh->vboId[2] = vboId[2]; // Normals VBO
+ mesh->vboId[3] = vboId[3]; // Colors VBO
+ mesh->vboId[4] = vboId[4]; // Tangents VBO
+ mesh->vboId[5] = vboId[5]; // Texcoords2 VBO
+ mesh->vboId[6] = vboId[6]; // Indices VBO
if (vaoSupported)
{
- // Initialize Quads VAO (Buffer A)
- glGenVertexArrays(1, &vaoModel);
- glBindVertexArray(vaoModel);
+ if (vaoId > 0)
+ {
+ mesh->vaoId = vaoId;
+ TraceLog(INFO, "[VAO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId);
+ }
+ else TraceLog(WARNING, "Mesh could not be uploaded to VRAM (GPU)");
}
+ else
+ {
+ TraceLog(INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)");
+ }
+#endif
+}
+
+// Update vertex data on GPU (upload new data to one buffer)
+void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ // Activate mesh VAO
+ if (vaoSupported) glBindVertexArray(mesh.vaoId);
+
+ switch (buffer)
+ {
+ case 0: // Update vertices (vertex position)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]);
+ if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.vertices, GL_DYNAMIC_DRAW);
+ else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.vertices);
+
+ } break;
+ case 1: // Update texcoords (vertex texture coordinates)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]);
+ if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords, GL_DYNAMIC_DRAW);
+ else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords);
+
+ } break;
+ case 2: // Update normals (vertex normals)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]);
+ if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.normals, GL_DYNAMIC_DRAW);
+ else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.normals);
+
+ } break;
+ case 3: // Update colors (vertex colors)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]);
+ if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*numVertex, mesh.colors, GL_DYNAMIC_DRAW);
+ else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*numVertex, mesh.colors);
+
+ } break;
+ case 4: // Update tangents (vertex tangents)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]);
+ if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.tangents, GL_DYNAMIC_DRAW);
+ else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.tangents);
+ } break;
+ case 5: // Update texcoords2 (vertex second texture coordinates)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]);
+ if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords2, GL_DYNAMIC_DRAW);
+ else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords2);
+ } break;
+ default: break;
+ }
+
+ // Unbind the current VAO
+ if (vaoSupported) glBindVertexArray(0);
+
+ // Another option would be using buffer mapping...
+ //mesh.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
+ // Now we can modify vertices
+ //glUnmapBuffer(GL_ARRAY_BUFFER);
+#endif
+}
- // Create buffers for our vertex data (positions, texcoords, normals)
- glGenBuffers(3, vertexBuffer);
+// Draw a 3d mesh with material and transform
+void rlglDrawMesh(Mesh mesh, Material material, Matrix transform)
+{
+#if defined(GRAPHICS_API_OPENGL_11)
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id);
+
+ // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model
+ glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array
+ if (mesh.normals != NULL) glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array
+ if (mesh.colors != NULL) glEnableClientState(GL_COLOR_ARRAY); // Enable colors array
+
+ glVertexPointer(3, GL_FLOAT, 0, mesh.vertices); // Pointer to vertex coords array
+ glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array
+ if (mesh.normals != NULL) glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array
+ if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array
+
+ rlPushMatrix();
+ rlMultMatrixf(MatrixToFloat(transform));
+ rlColor4ub(material.colDiffuse.r, material.colDiffuse.g, material.colDiffuse.b, material.colDiffuse.a);
+
+ if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices);
+ else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount);
+ rlPopMatrix();
- // Enable vertex attributes: position
- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
- glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.vertices, GL_STATIC_DRAW);
- glVertexAttribPointer(model.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(model.shader.vertexLoc);
+ glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array
+ if (mesh.normals != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array
+ if (mesh.colors != NULL) glDisableClientState(GL_NORMAL_ARRAY); // Disable colors array
- // Enable vertex attributes: texcoords
- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
- glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.vertexCount, mesh.texcoords, GL_STATIC_DRAW);
- glVertexAttribPointer(model.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(model.shader.texcoordLoc);
+ glDisable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, 0);
+#endif
- // Enable vertex attributes: normals
- glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
- glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW);
- glVertexAttribPointer(model.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0);
- glEnableVertexAttribArray(model.shader.normalLoc);
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ glUseProgram(material.shader.id);
+
+ // At this point the modelview matrix just contains the view matrix (camera)
+ // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix()
+ Matrix matView = modelview; // View matrix (camera)
+ Matrix matProjection = projection; // Projection matrix (perspective)
- model.mesh.vboId[0] = vertexBuffer[0]; // Vertex position VBO
- model.mesh.vboId[1] = vertexBuffer[1]; // Texcoords VBO
- model.mesh.vboId[2] = vertexBuffer[2]; // Normals VBO
+ // Calculate model-view matrix combining matModel and matView
+ Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates
+
+ // Calculate model-view-projection matrix (MVP)
+ Matrix matMVP = MatrixMultiply(matModelView, matProjection); // Transform to screen-space coordinates
+
+ // Send combined model-view-projection matrix to shader
+ glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP));
+
+ // Upload to shader material.colDiffuse
+ float vColorDiffuse[4] = { (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255 };
+ glUniform4fv(material.shader.tintColorLoc, 1, vColorDiffuse);
+
+ // Check if using standard shader to get location points
+ // NOTE: standard shader specific locations are got at render time to keep Shader struct as simple as possible (with just default shader locations)
+ if (material.shader.id == standardShader.id)
+ {
+ // Send model transformations matrix to shader
+ glUniformMatrix4fv(glGetUniformLocation(material.shader.id, "modelMatrix"), 1, false, MatrixToFloat(transform));
+
+ // Send view transformation matrix to shader. View matrix 8, 9 and 10 are view direction vector axis values (target - position)
+ glUniform3f(glGetUniformLocation(material.shader.id, "viewDir"), matView.m8, matView.m9, matView.m10);
+
+ // Setup shader uniforms for lights
+ SetShaderLights(material.shader);
+
+ // Upload to shader material.colAmbient
+ glUniform4f(glGetUniformLocation(material.shader.id, "colAmbient"), (float)material.colAmbient.r/255, (float)material.colAmbient.g/255, (float)material.colAmbient.b/255, (float)material.colAmbient.a/255);
+
+ // Upload to shader material.colSpecular
+ glUniform4f(glGetUniformLocation(material.shader.id, "colSpecular"), (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255);
+
+ // Upload to shader glossiness
+ glUniform1f(glGetUniformLocation(material.shader.id, "glossiness"), material.glossiness);
+ }
+
+ // Set shader textures (diffuse, normal, specular)
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id);
+ glUniform1i(material.shader.mapTexture0Loc, 0); // Diffuse texture fits in active texture unit 0
+
+ if ((material.texNormal.id != 0) && (material.shader.mapTexture1Loc != -1))
+ {
+ // Upload to shader specular map flag
+ glUniform1i(glGetUniformLocation(material.shader.id, "useNormal"), 1);
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, material.texNormal.id);
+ glUniform1i(material.shader.mapTexture1Loc, 1); // Normal texture fits in active texture unit 1
+ }
+
+ if ((material.texSpecular.id != 0) && (material.shader.mapTexture2Loc != -1))
+ {
+ // Upload to shader specular map flag
+ glUniform1i(glGetUniformLocation(material.shader.id, "useSpecular"), 1);
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, material.texSpecular.id);
+ glUniform1i(material.shader.mapTexture2Loc, 2); // Specular texture fits in active texture unit 2
+ }
if (vaoSupported)
{
- if (vaoModel > 0)
+ glBindVertexArray(mesh.vaoId);
+ }
+ else
+ {
+ // Bind mesh VBO data: vertex position (shader-location = 0)
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]);
+ glVertexAttribPointer(material.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.vertexLoc);
+
+ // Bind mesh VBO data: vertex texcoords (shader-location = 1)
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]);
+ glVertexAttribPointer(material.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.texcoordLoc);
+
+ // Bind mesh VBO data: vertex normals (shader-location = 2, if available)
+ if (material.shader.normalLoc != -1)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]);
+ glVertexAttribPointer(material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.normalLoc);
+ }
+
+ // Bind mesh VBO data: vertex colors (shader-location = 3, if available) , tangents, texcoords2 (if available)
+ if (material.shader.colorLoc != -1)
{
- model.mesh.vaoId = vaoModel;
- TraceLog(INFO, "[VAO ID %i] Model uploaded successfully to VRAM (GPU)", vaoModel);
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]);
+ glVertexAttribPointer(material.shader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(material.shader.colorLoc);
}
- else TraceLog(WARNING, "Model could not be uploaded to VRAM (GPU)");
+
+ // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
+ if (material.shader.tangentLoc != -1)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]);
+ glVertexAttribPointer(material.shader.tangentLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.tangentLoc);
+ }
+
+ // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
+ if (material.shader.texcoord2Loc != -1)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]);
+ glVertexAttribPointer(material.shader.texcoord2Loc, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(material.shader.texcoord2Loc);
+ }
+
+ if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]);
+ }
+
+ // Draw call!
+ if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw
+ else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount);
+
+ if (material.texNormal.id != 0)
+ {
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, 0);
}
+
+ if (material.texSpecular.id != 0)
+ {
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0
+ glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
+
+ if (vaoSupported) glBindVertexArray(0); // Unbind VAO
else
{
- TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i] Model uploaded successfully to VRAM (GPU)", model.mesh.vboId[0], model.mesh.vboId[1], model.mesh.vboId[2]);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs
+ if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
+
+ glUseProgram(0); // Unbind shader program
#endif
+}
+
+// Unload mesh data from CPU and GPU
+void rlglUnloadMesh(Mesh *mesh)
+{
+ if (mesh->vertices != NULL) free(mesh->vertices);
+ if (mesh->texcoords != NULL) free(mesh->texcoords);
+ if (mesh->normals != NULL) free(mesh->normals);
+ if (mesh->colors != NULL) free(mesh->colors);
+ if (mesh->tangents != NULL) free(mesh->tangents);
+ if (mesh->texcoords2 != NULL) free(mesh->texcoords2);
+ if (mesh->indices != NULL) free(mesh->indices);
- return model;
+ rlDeleteBuffers(mesh->vboId[0]); // vertex
+ rlDeleteBuffers(mesh->vboId[1]); // texcoords
+ rlDeleteBuffers(mesh->vboId[2]); // normals
+ rlDeleteBuffers(mesh->vboId[3]); // colors
+ rlDeleteBuffers(mesh->vboId[4]); // tangents
+ rlDeleteBuffers(mesh->vboId[5]); // texcoords2
+ rlDeleteBuffers(mesh->vboId[6]); // indices
+
+ rlDeleteVertexArrays(mesh->vaoId);
}
// Read screen pixel data (color buffer)
@@ -2089,8 +2021,9 @@ void *rlglReadTexturePixels(Texture2D texture)
#endif
#if defined(GRAPHICS_API_OPENGL_ES2)
- FBO fbo = rlglLoadFBO(texture.width, texture.height);
-
+
+ RenderTexture2D fbo = rlglLoadRenderTexture(texture.width, texture.height);
+
// NOTE: Two possible Options:
// 1 - Bind texture to color fbo attachment and glReadPixels()
// 2 - Create an fbo, activate it, render quad with texture, glReadPixels()
@@ -2111,7 +2044,7 @@ void *rlglReadTexturePixels(Texture2D texture)
glReadPixels(0, 0, texture.width, texture.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Re-attach internal FBO color texture before deleting it
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.colorTextureId, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.texture.id, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -2135,7 +2068,7 @@ void *rlglReadTexturePixels(Texture2D texture)
Model quad;
//quad.mesh = GenMeshQuad(width, height);
quad.transform = MatrixIdentity();
- quad.shader = simpleShader;
+ quad.shader = defaultShader;
DrawModel(quad, (Vector3){ 0.0f, 0.0f, 0.0f }, 1.0f, WHITE);
@@ -2150,7 +2083,8 @@ void *rlglReadTexturePixels(Texture2D texture)
#endif // GET_TEXTURE_FBO_OPTION
// Clean up temporal fbo
- rlglUnloadFBO(fbo);
+ rlDeleteRenderTextures(fbo);
+
#endif
return pixels;
@@ -2162,169 +2096,52 @@ void *rlglReadTexturePixels(Texture2D texture)
// NOTE: Those functions are exposed directly to the user in raylib.h
//----------------------------------------------------------------------------------
+// Get default internal texture (white texture)
+Texture2D GetDefaultTexture(void)
+{
+ Texture2D texture;
+
+ texture.id = whiteTexture;
+ texture.width = 1;
+ texture.height = 1;
+ texture.mipmaps = 1;
+ texture.format = UNCOMPRESSED_R8G8B8A8;
+
+ return texture;
+}
+
// Load a custom shader and bind default locations
Shader LoadShader(char *vsFileName, char *fsFileName)
{
- Shader shader;
-
- shader.id = 0; // Default value in case of loading failure
+ Shader shader = { 0 };
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Shaders loading from external text file
- char *vShaderStr = TextFileRead(vsFileName);
- char *fShaderStr = TextFileRead(fsFileName);
+ char *vShaderStr = ReadTextFile(vsFileName);
+ char *fShaderStr = ReadTextFile(fsFileName);
if ((vShaderStr != NULL) && (fShaderStr != NULL))
{
shader.id = LoadShaderProgram(vShaderStr, fShaderStr);
- if (shader.id != 0)
- {
- TraceLog(INFO, "[SHDR ID %i] Custom shader loaded successfully", shader.id);
-
- // Set shader textures ids (all 0 by default)
- shader.texDiffuseId = 0;
- shader.texNormalId = 0;
- shader.texSpecularId = 0;
-
- // Get handles to GLSL input attibute locations
- //-------------------------------------------------------------------
- shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition");
- shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord");
- shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal");
- // NOTE: custom shader does not use colorLoc
- shader.colorLoc = -1;
-
- // Get handles to GLSL uniform locations (vertex shader)
- shader.mvpLoc = glGetUniformLocation(shader.id, "mvpMatrix");
-
- // Get handles to GLSL uniform locations (fragment shader)
- shader.tintColorLoc = glGetUniformLocation(shader.id, "fragTintColor");
- shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0");
- shader.mapNormalLoc = -1; // It can be set later
- shader.mapSpecularLoc = -1; // It can be set later
- //--------------------------------------------------------------------
- }
- else
- {
- TraceLog(WARNING, "Custom shader could not be loaded");
- shader = simpleShader;
- }
+ // After shader loading, we try to load default location names
+ if (shader.id != 0) LoadDefaultShaderLocations(&shader);
// Shader strings must be freed
free(vShaderStr);
free(fShaderStr);
}
- else
+
+ if (shader.id == 0)
{
TraceLog(WARNING, "Custom shader could not be loaded");
- shader = simpleShader;
+ shader = defaultShader;
}
#endif
return shader;
}
-// Load custom shader strings and return program id
-unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr)
-{
- unsigned int program = 0;
-
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- GLuint vertexShader;
- GLuint fragmentShader;
-
- vertexShader = glCreateShader(GL_VERTEX_SHADER);
- fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
-
- const char *pvs = vShaderStr;
- const char *pfs = fShaderStr;
-
- glShaderSource(vertexShader, 1, &pvs, NULL);
- glShaderSource(fragmentShader, 1, &pfs, NULL);
-
- GLint success = 0;
-
- glCompileShader(vertexShader);
-
- glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
-
- if (success != GL_TRUE)
- {
- TraceLog(WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader);
-
- int maxLength = 0;
- int length;
-
- glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
-
- char log[maxLength];
-
- glGetShaderInfoLog(vertexShader, maxLength, &length, log);
-
- TraceLog(INFO, "%s", log);
- }
- else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader);
-
- glCompileShader(fragmentShader);
-
- glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
-
- if (success != GL_TRUE)
- {
- TraceLog(WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader);
-
- int maxLength = 0;
- int length;
-
- glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
-
- char log[maxLength];
-
- glGetShaderInfoLog(fragmentShader, maxLength, &length, log);
-
- TraceLog(INFO, "%s", log);
- }
- else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader);
-
- program = glCreateProgram();
-
- glAttachShader(program, vertexShader);
- glAttachShader(program, fragmentShader);
-
- glLinkProgram(program);
-
- // NOTE: All uniform variables are intitialised to 0 when a program links
-
- glGetProgramiv(program, GL_LINK_STATUS, &success);
-
- if (success == GL_FALSE)
- {
- TraceLog(WARNING, "[SHDR ID %i] Failed to link shader program...", program);
-
- int maxLength = 0;
- int length;
-
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
-
- char log[maxLength];
-
- glGetProgramInfoLog(program, maxLength, &length, log);
-
- TraceLog(INFO, "%s", log);
-
- glDeleteProgram(program);
-
- program = 0;
- }
- else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program);
-
- glDeleteShader(vertexShader);
- glDeleteShader(fragmentShader);
-#endif
- return program;
-}
-
// Unload a custom shader from memory
void UnloadShader(Shader shader)
{
@@ -2335,120 +2152,45 @@ void UnloadShader(Shader shader)
}
}
-// Set custom shader to be used on batch draw
-void SetCustomShader(Shader shader)
+// Begin custom shader mode
+void BeginShaderMode(Shader shader)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
if (currentShader.id != shader.id)
{
rlglDraw();
-
currentShader = shader;
-/*
- if (vaoSupported) glBindVertexArray(vaoQuads);
-
- // Enable vertex attributes: position
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]);
- glEnableVertexAttribArray(currentShader.vertexLoc);
- glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
-
- // Enable vertex attributes: texcoords
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]);
- glEnableVertexAttribArray(currentShader.texcoordLoc);
- glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
-
- // Enable vertex attributes: colors
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]);
- glEnableVertexAttribArray(currentShader.colorLoc);
- glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
-
- if (vaoSupported) glBindVertexArray(0); // Unbind VAO
-*/
}
#endif
}
-// Set postprocessing shader
-// NOTE: Uses global variables screenWidth and screenHeight
-void SetPostproShader(Shader shader)
+// End custom shader mode (returns to default shader)
+void EndShaderMode(void)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- if (!enabledPostpro)
- {
- enabledPostpro = true;
- rlglInitPostpro();
- }
-
- SetModelShader(&postproQuad, shader);
-
- Texture2D texture;
- texture.id = postproFbo.colorTextureId;
- texture.width = screenWidth;
- texture.height = screenHeight;
-
- SetShaderMapDiffuse(&postproQuad.shader, texture);
-
- //TraceLog(DEBUG, "Postproquad texture id: %i", postproQuad.texture.id);
- //TraceLog(DEBUG, "Postproquad shader diffuse map id: %i", postproQuad.shader.texDiffuseId);
- //TraceLog(DEBUG, "Shader diffuse map id: %i", shader.texDiffuseId);
-#elif defined(GRAPHICS_API_OPENGL_11)
- TraceLog(WARNING, "Shaders not supported on OpenGL 1.1");
+ BeginShaderMode(defaultShader);
#endif
}
-// Set default shader to be used in batch draw
-void SetDefaultShader(void)
+// Get default shader
+Shader GetDefaultShader(void)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- SetCustomShader(defaultShader);
-
- if (enabledPostpro)
- {
- SetPostproShader(defaultShader);
- enabledPostpro = false;
- }
-#endif
-}
-
-// Link shader to model
-void SetModelShader(Model *model, Shader shader)
-{
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- model->shader = shader;
-
- if (vaoSupported) glBindVertexArray(model->mesh.vaoId);
-
- // Enable vertex attributes: position
- glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[0]);
- glEnableVertexAttribArray(shader.vertexLoc);
- glVertexAttribPointer(shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
-
- // Enable vertex attributes: texcoords
- glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[1]);
- glEnableVertexAttribArray(shader.texcoordLoc);
- glVertexAttribPointer(shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
-
- // Enable vertex attributes: normals
- glBindBuffer(GL_ARRAY_BUFFER, model->mesh.vboId[2]);
- glEnableVertexAttribArray(shader.normalLoc);
- glVertexAttribPointer(shader.normalLoc, 3, GL_FLOAT, 0, 0, 0);
-
- if (vaoSupported) glBindVertexArray(0); // Unbind VAO
-
- // NOTE: If SetModelTexture() is called previously, texture is not assigned to new shader
- if (model->texture.id > 0) model->shader.texDiffuseId = model->texture.id;
-#elif (GRAPHICS_API_OPENGL_11)
- TraceLog(WARNING, "Shaders not supported on OpenGL 1.1");
+ return defaultShader;
+#else
+ Shader shader = { 0 };
+ return shader;
#endif
}
-// Check if postprocessing is enabled (used in module: core)
-bool IsPosproShaderEnabled(void)
+// Get default shader
+Shader GetStandardShader(void)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- return enabledPostpro;
-#elif defined(GRAPHICS_API_OPENGL_11)
- return false;
+ return standardShader;
+#else
+ Shader shader = { 0 };
+ return shader;
#endif
}
@@ -2508,107 +2250,25 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat)
#endif
}
-// Default diffuse shader map texture assignment
-void SetShaderMapDiffuse(Shader *shader, Texture2D texture)
-{
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- shader->texDiffuseId = texture.id;
-
- glUseProgram(shader->id);
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, shader->texDiffuseId);
-
- glUniform1i(shader->mapDiffuseLoc, 0); // Texture fits in active texture unit 0
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
- glUseProgram(0);
-#endif
-}
-
-// Normal map texture shader assignment
-void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture)
+// Set a custom projection matrix (replaces internal projection matrix)
+void SetMatrixProjection(Matrix proj)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- shader->mapNormalLoc = glGetUniformLocation(shader->id, uniformName);
-
- if (shader->mapNormalLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName);
- else
- {
- shader->texNormalId = texture.id;
-
- glUseProgram(shader->id);
-
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, shader->texNormalId);
-
- glUniform1i(shader->mapNormalLoc, 1); // Texture fits in active texture unit 1
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
- glUseProgram(0);
- }
+ projection = proj;
#endif
}
-// Specular map texture shader assignment
-void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture)
+// Set a custom modelview matrix (replaces internal modelview matrix)
+void SetMatrixModelview(Matrix view)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- shader->mapSpecularLoc = glGetUniformLocation(shader->id, uniformName);
-
- if (shader->mapSpecularLoc == -1) TraceLog(WARNING, "[SHDR ID %i] Shader location for %s could not be found", shader->id, uniformName);
- else
- {
- shader->texSpecularId = texture.id;
-
- glUseProgram(shader->id);
-
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, shader->texSpecularId);
-
- glUniform1i(shader->mapSpecularLoc, 2); // Texture fits in active texture unit 2
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
- glUseProgram(0);
- }
-#endif
-}
-
-// Generic shader maps assignment
-// TODO: Trying to find a generic shader to allow any kind of map
-// NOTE: mapLocation should be retrieved by user with GetShaderLocation()
-// ISSUE: mapTextureId: Shader should contain a reference to map texture and corresponding textureUnit,
-// so it can be automatically checked and used in rlglDrawModel()
-void SetShaderMap(Shader *shader, int mapLocation, Texture2D texture, int textureUnit)
-{
-/*
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- if (mapLocation == -1) TraceLog(WARNING, "[SHDR ID %i] Map location could not be found", shader->id);
- else
- {
- shader->mapTextureId = texture.id;
-
- glUseProgram(shader->id);
-
- glActiveTexture(GL_TEXTURE0 + textureUnit);
- glBindTexture(GL_TEXTURE_2D, shader->mapTextureId);
-
- glUniform1i(mapLocation, textureUnit); // Texture fits in active textureUnit
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glActiveTexture(GL_TEXTURE0);
- glUseProgram(0);
- }
+ modelview = view;
#endif
-*/
}
-// Set blending mode (alpha, additive, multiplied)
-// NOTE: Only 3 blending modes predefined
-void SetBlendMode(int mode)
+// Begin blending mode (alpha, additive, multiplied)
+// NOTE: Only 3 blending modes supported, default blend mode is alpha
+void BeginBlendMode(int mode)
{
if ((blendMode != mode) && (mode < 3))
{
@@ -2626,17 +2286,67 @@ void SetBlendMode(int mode)
}
}
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-void PrintProjectionMatrix(void)
+// End blending mode (reset to default: alpha blending)
+void EndBlendMode(void)
{
- PrintMatrix(projection);
+ BeginBlendMode(BLEND_ALPHA);
}
-void PrintModelviewMatrix(void)
+// Create a new light, initialize it and add to pool
+Light CreateLight(int type, Vector3 position, Color diffuse)
{
- PrintMatrix(modelview);
+ Light light = NULL;
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ // Allocate dynamic memory
+ light = (Light)malloc(sizeof(LightData));
+
+ // Initialize light values with generic values
+ light->id = lightsCount;
+ light->type = type;
+ light->enabled = true;
+
+ light->position = position;
+ light->target = (Vector3){ 0.0f, 0.0f, 0.0f };
+ light->intensity = 1.0f;
+ light->diffuse = diffuse;
+
+ // Add new light to the array
+ lights[lightsCount] = light;
+
+ // Increase enabled lights count
+ lightsCount++;
+#else
+ // TODO: Support OpenGL 1.1 lighting system
+ TraceLog(WARNING, "Lighting currently not supported on OpenGL 1.1");
+#endif
+
+ return light;
}
+
+// Destroy a light and take it out of the list
+void DestroyLight(Light light)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ // Free dynamic memory allocation
+ free(lights[light->id]);
+
+ // Remove *obj from the pointers array
+ for (int i = light->id; i < lightsCount; i++)
+ {
+ // Resort all the following pointers of the array
+ if ((i + 1) < lightsCount)
+ {
+ lights[i] = lights[i + 1];
+ lights[i]->id = lights[i + 1]->id;
+ }
+ else free(lights[i]);
+ }
+
+ // Decrease enabled physic objects count
+ lightsCount--;
#endif
+}
//----------------------------------------------------------------------------------
// Module specific Functions Definition
@@ -2679,116 +2389,144 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in
}
}
-// Load Shader (Vertex and Fragment)
-// NOTE: This shader program is used only for batch buffers (lines, triangles, quads)
-static Shader LoadDefaultShader(void)
+// Load custom shader strings and return program id
+static unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr)
{
- Shader shader;
+ unsigned int program = 0;
- // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2
- // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+
- // Just defined #version 330 despite shader is #version 110
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ GLuint vertexShader;
+ GLuint fragmentShader;
- // Vertex shader directly defined, no external file required
-#if defined(GRAPHICS_API_OPENGL_33)
- char vShaderStr[] = "#version 330 \n"
- "in vec3 vertexPosition; \n"
- "in vec2 vertexTexCoord; \n"
- "in vec4 vertexColor; \n"
- "out vec2 fragTexCoord; \n"
- "out vec4 fragTintColor; \n"
-#elif defined(GRAPHICS_API_OPENGL_ES2)
- char vShaderStr[] = "#version 100 \n"
- "attribute vec3 vertexPosition; \n"
- "attribute vec2 vertexTexCoord; \n"
- "attribute vec4 vertexColor; \n"
- "varying vec2 fragTexCoord; \n"
- "varying vec4 fragTintColor; \n"
-#endif
- "uniform mat4 mvpMatrix; \n"
- "void main() \n"
- "{ \n"
- " fragTexCoord = vertexTexCoord; \n"
- " fragTintColor = vertexColor; \n"
- " gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n"
- "} \n";
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
- // Fragment shader directly defined, no external file required
-#if defined(GRAPHICS_API_OPENGL_33)
- char fShaderStr[] = "#version 330 \n"
- "in vec2 fragTexCoord; \n"
- "in vec4 fragTintColor; \n"
-#elif defined(GRAPHICS_API_OPENGL_ES2)
- char fShaderStr[] = "#version 100 \n"
- "precision mediump float; \n" // precision required for OpenGL ES2 (WebGL)
- "varying vec2 fragTexCoord; \n"
- "varying vec4 fragTintColor; \n"
-#endif
- "uniform sampler2D texture0; \n"
- "void main() \n"
- "{ \n"
- " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0, use texture() instead
- " gl_FragColor = texelColor*fragTintColor; \n"
- "} \n";
+ const char *pvs = vShaderStr;
+ const char *pfs = fShaderStr;
- shader.id = LoadShaderProgram(vShaderStr, fShaderStr);
+ glShaderSource(vertexShader, 1, &pvs, NULL);
+ glShaderSource(fragmentShader, 1, &pfs, NULL);
- if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id);
- else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id);
+ GLint success = 0;
- // Get handles to GLSL input attibute locations
- //-------------------------------------------------------------------
- shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition");
- shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord");
- shader.colorLoc = glGetAttribLocation(shader.id, "vertexColor");
- // NOTE: default shader does not use normalLoc
- shader.normalLoc = -1;
+ glCompileShader(vertexShader);
- // Get handles to GLSL uniform locations (vertex shader)
- shader.mvpLoc = glGetUniformLocation(shader.id, "mvpMatrix");
+ glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
- // Get handles to GLSL uniform locations (fragment shader)
- shader.tintColorLoc = -1;
- shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0");
- shader.mapNormalLoc = -1; // It can be set later
- shader.mapSpecularLoc = -1; // It can be set later
+ if (success != GL_TRUE)
+ {
+ TraceLog(WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader);
+
+ int maxLength = 0;
+ int length;
+
+ glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
+
+ char log[maxLength];
+
+ glGetShaderInfoLog(vertexShader, maxLength, &length, log);
+
+ TraceLog(INFO, "%s", log);
+ }
+ else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader);
+
+ glCompileShader(fragmentShader);
+
+ glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
+
+ if (success != GL_TRUE)
+ {
+ TraceLog(WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader);
+
+ int maxLength = 0;
+ int length;
+
+ glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
+
+ char log[maxLength];
+
+ glGetShaderInfoLog(fragmentShader, maxLength, &length, log);
+
+ TraceLog(INFO, "%s", log);
+ }
+ else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader);
+
+ program = glCreateProgram();
+
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
- shader.texDiffuseId = whiteTexture; // Default white texture
- shader.texNormalId = 0;
- shader.texSpecularId = 0;
- //--------------------------------------------------------------------
+ // NOTE: Default attribute shader locations must be binded before linking
+ glBindAttribLocation(program, 0, DEFAULT_ATTRIB_POSITION_NAME);
+ glBindAttribLocation(program, 1, DEFAULT_ATTRIB_TEXCOORD_NAME);
+ glBindAttribLocation(program, 2, DEFAULT_ATTRIB_NORMAL_NAME);
+ glBindAttribLocation(program, 3, DEFAULT_ATTRIB_COLOR_NAME);
+ glBindAttribLocation(program, 4, DEFAULT_ATTRIB_TANGENT_NAME);
+ glBindAttribLocation(program, 5, DEFAULT_ATTRIB_TEXCOORD2_NAME);
+
+ // NOTE: If some attrib name is no found on the shader, it locations becomes -1
+
+ glLinkProgram(program);
+
+ // NOTE: All uniform variables are intitialised to 0 when a program links
- return shader;
+ glGetProgramiv(program, GL_LINK_STATUS, &success);
+
+ if (success == GL_FALSE)
+ {
+ TraceLog(WARNING, "[SHDR ID %i] Failed to link shader program...", program);
+
+ int maxLength = 0;
+ int length;
+
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
+
+ char log[maxLength];
+
+ glGetProgramInfoLog(program, maxLength, &length, log);
+
+ TraceLog(INFO, "%s", log);
+
+ glDeleteProgram(program);
+
+ program = 0;
+ }
+ else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program);
+
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+#endif
+ return program;
}
-// Load Simple Shader (Vertex and Fragment)
-// NOTE: This shader program is used to render models
-static Shader LoadSimpleShader(void)
+
+// Load default shader (just vertex positioning and texture coloring)
+// NOTE: This shader program is used for batch buffers (lines, triangles, quads)
+static Shader LoadDefaultShader(void)
{
Shader shader;
- // NOTE: Shaders are written using GLSL 110 (desktop), that is equivalent to GLSL 100 on ES2
- // NOTE: Detected an error on ATI cards if defined #version 110 while OpenGL 3.3+
- // Just defined #version 330 despite shader is #version 110
-
// Vertex shader directly defined, no external file required
#if defined(GRAPHICS_API_OPENGL_33)
char vShaderStr[] = "#version 330 \n"
"in vec3 vertexPosition; \n"
"in vec2 vertexTexCoord; \n"
- "in vec3 vertexNormal; \n"
+ "in vec4 vertexColor; \n"
"out vec2 fragTexCoord; \n"
+ "out vec4 fragColor; \n"
#elif defined(GRAPHICS_API_OPENGL_ES2)
char vShaderStr[] = "#version 100 \n"
"attribute vec3 vertexPosition; \n"
"attribute vec2 vertexTexCoord; \n"
- "attribute vec3 vertexNormal; \n"
+ "attribute vec4 vertexColor; \n"
"varying vec2 fragTexCoord; \n"
+ "varying vec4 fragColor; \n"
#endif
"uniform mat4 mvpMatrix; \n"
"void main() \n"
"{ \n"
" fragTexCoord = vertexTexCoord; \n"
+ " fragColor = vertexColor; \n"
" gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n"
"} \n";
@@ -2796,107 +2534,143 @@ static Shader LoadSimpleShader(void)
#if defined(GRAPHICS_API_OPENGL_33)
char fShaderStr[] = "#version 330 \n"
"in vec2 fragTexCoord; \n"
+ "in vec4 fragColor; \n"
+ "out vec4 finalColor; \n"
#elif defined(GRAPHICS_API_OPENGL_ES2)
char fShaderStr[] = "#version 100 \n"
"precision mediump float; \n" // precision required for OpenGL ES2 (WebGL)
"varying vec2 fragTexCoord; \n"
+ "varying vec4 fragColor; \n"
#endif
"uniform sampler2D texture0; \n"
- "uniform vec4 fragTintColor; \n"
+ "uniform vec4 colDiffuse; \n"
"void main() \n"
"{ \n"
- " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0, use texture() instead
- " gl_FragColor = texelColor*fragTintColor; \n"
+#if defined(GRAPHICS_API_OPENGL_33)
+ " vec4 texelColor = texture(texture0, fragTexCoord); \n"
+ " finalColor = texelColor*colDiffuse*fragColor; \n"
+#elif defined(GRAPHICS_API_OPENGL_ES2)
+ " vec4 texelColor = texture2D(texture0, fragTexCoord); \n" // NOTE: texture2D() is deprecated on OpenGL 3.3 and ES 3.0
+ " gl_FragColor = texelColor*colDiffuse*fragColor; \n"
+#endif
"} \n";
shader.id = LoadShaderProgram(vShaderStr, fShaderStr);
- if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Simple shader loaded successfully", shader.id);
- else TraceLog(WARNING, "[SHDR ID %i] Simple shader could not be loaded", shader.id);
+ if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id);
+ else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id);
- // Get handles to GLSL input attibute locations
- //-------------------------------------------------------------------
- shader.vertexLoc = glGetAttribLocation(shader.id, "vertexPosition");
- shader.texcoordLoc = glGetAttribLocation(shader.id, "vertexTexCoord");
- shader.normalLoc = glGetAttribLocation(shader.id, "vertexNormal");
- // NOTE: simple shader does not use colorLoc
- shader.colorLoc = -1;
+ if (shader.id != 0) LoadDefaultShaderLocations(&shader);
- // Get handles to GLSL uniform locations (vertex shader)
- shader.mvpLoc = glGetUniformLocation(shader.id, "mvpMatrix");
+ return shader;
+}
- // Get handles to GLSL uniform locations (fragment shader)
- shader.tintColorLoc = glGetUniformLocation(shader.id, "fragTintColor");
- shader.mapDiffuseLoc = glGetUniformLocation(shader.id, "texture0");
- shader.mapNormalLoc = -1; // It can be set later
- shader.mapSpecularLoc = -1; // It can be set later
-
- shader.texDiffuseId = whiteTexture; // Default white texture
- shader.texNormalId = 0;
- shader.texSpecularId = 0;
- //--------------------------------------------------------------------
+// Load standard shader
+// NOTE: This shader supports:
+// - Up to 3 different maps: diffuse, normal, specular
+// - Material properties: colAmbient, colDiffuse, colSpecular, glossiness
+// - Up to 8 lights: Point, Directional or Spot
+static Shader LoadStandardShader(void)
+{
+ // Load standard shader (TODO: rewrite as char pointers)
+ Shader shader = LoadShader("resources/shaders/standard.vs", "resources/shaders/standard.fs");
+
+ if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Standard shader loaded successfully", shader.id);
+ else TraceLog(WARNING, "[SHDR ID %i] Standard shader could not be loaded", shader.id);
+
+ if (shader.id != 0) LoadDefaultShaderLocations(&shader);
return shader;
}
-// Read text file
-// NOTE: text chars array should be freed manually
-static char *TextFileRead(char *fileName)
+// Get location handlers to for shader attributes and uniforms
+// NOTE: If any location is not found, loc point becomes -1
+static void LoadDefaultShaderLocations(Shader *shader)
{
- FILE *textFile;
- char *text = NULL;
+ // NOTE: Default shader attrib locations have been fixed before linking:
+ // vertex position location = 0
+ // vertex texcoord location = 1
+ // vertex normal location = 2
+ // vertex color location = 3
+ // vertex tangent location = 4
+ // vertex texcoord2 location = 5
+
+ // Get handles to GLSL input attibute locations
+ shader->vertexLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME);
+ shader->texcoordLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME);
+ shader->texcoord2Loc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD2_NAME);
+ shader->normalLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_NORMAL_NAME);
+ shader->tangentLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TANGENT_NAME);
+ shader->colorLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_COLOR_NAME);
- int count = 0;
+ // Get handles to GLSL uniform locations (vertex shader)
+ shader->mvpLoc = glGetUniformLocation(shader->id, "mvpMatrix");
- if (fileName != NULL)
- {
- textFile = fopen(fileName,"rt");
+ // Get handles to GLSL uniform locations (fragment shader)
+ shader->tintColorLoc = glGetUniformLocation(shader->id, "colDiffuse");
+ shader->mapTexture0Loc = glGetUniformLocation(shader->id, "texture0");
+ shader->mapTexture1Loc = glGetUniformLocation(shader->id, "texture1");
+ shader->mapTexture2Loc = glGetUniformLocation(shader->id, "texture2");
+}
- if (textFile != NULL)
- {
- fseek(textFile, 0, SEEK_END);
- count = ftell(textFile);
- rewind(textFile);
+// Unload default shader
+static void UnloadDefaultShader(void)
+{
+ glUseProgram(0);
- if (count > 0)
- {
- text = (char *)malloc(sizeof(char)*(count + 1));
- count = fread(text, sizeof(char), count, textFile);
- text[count] = '\0';
- }
+ //glDetachShader(defaultShader, vertexShader);
+ //glDetachShader(defaultShader, fragmentShader);
+ //glDeleteShader(vertexShader); // Already deleted on shader compilation
+ //glDeleteShader(fragmentShader); // Already deleted on shader compilation
+ glDeleteProgram(defaultShader.id);
+}
- fclose(textFile);
- }
- else TraceLog(WARNING, "[%s] Text file could not be opened", fileName);
- }
+// Unload standard shader
+static void UnloadStandardShader(void)
+{
+ glUseProgram(0);
- return text;
+ //glDetachShader(defaultShader, vertexShader);
+ //glDetachShader(defaultShader, fragmentShader);
+ //glDeleteShader(vertexShader); // Already deleted on shader compilation
+ //glDeleteShader(fragmentShader); // Already deleted on shader compilation
+ glDeleteProgram(standardShader.id);
}
-// Allocate and initialize float array buffers to store vertex data (lines, triangles, quads)
-static void InitializeBuffers(void)
+
+// Load default internal buffers (lines, triangles, quads)
+static void LoadDefaultBuffers(void)
{
- // Initialize lines arrays (vertex position and color data)
+ // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads)
+ //--------------------------------------------------------------------------------------------
+
+ // Lines - Initialize arrays (vertex position and color data)
lines.vertices = (float *)malloc(sizeof(float)*3*2*MAX_LINES_BATCH); // 3 float by vertex, 2 vertex by line
lines.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*2*MAX_LINES_BATCH); // 4 float by color, 2 colors by line
+ lines.texcoords = NULL;
+ lines.indices = NULL;
for (int i = 0; i < (3*2*MAX_LINES_BATCH); i++) lines.vertices[i] = 0.0f;
for (int i = 0; i < (4*2*MAX_LINES_BATCH); i++) lines.colors[i] = 0;
lines.vCounter = 0;
lines.cCounter = 0;
+ lines.tcCounter = 0;
- // Initialize triangles arrays (vertex position and color data)
+ // Triangles - Initialize arrays (vertex position and color data)
triangles.vertices = (float *)malloc(sizeof(float)*3*3*MAX_TRIANGLES_BATCH); // 3 float by vertex, 3 vertex by triangle
triangles.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH); // 4 float by color, 3 colors by triangle
+ triangles.texcoords = NULL;
+ triangles.indices = NULL;
for (int i = 0; i < (3*3*MAX_TRIANGLES_BATCH); i++) triangles.vertices[i] = 0.0f;
for (int i = 0; i < (4*3*MAX_TRIANGLES_BATCH); i++) triangles.colors[i] = 0;
triangles.vCounter = 0;
triangles.cCounter = 0;
+ triangles.tcCounter = 0;
- // Initialize quads arrays (vertex position, texcoord and color data... and indexes)
+ // Quads - Initialize arrays (vertex position, texcoord, color data and indexes)
quads.vertices = (float *)malloc(sizeof(float)*3*4*MAX_QUADS_BATCH); // 3 float by vertex, 4 vertex by quad
quads.texcoords = (float *)malloc(sizeof(float)*2*4*MAX_QUADS_BATCH); // 2 float by texcoord, 4 texcoord by quad
quads.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*4*MAX_QUADS_BATCH); // 4 float by color, 4 colors by quad
@@ -2906,7 +2680,6 @@ static void InitializeBuffers(void)
quads.indices = (unsigned short *)malloc(sizeof(short)*6*MAX_QUADS_BATCH); // 6 int by quad (indices)
#endif
-
for (int i = 0; i < (3*4*MAX_QUADS_BATCH); i++) quads.vertices[i] = 0.0f;
for (int i = 0; i < (2*4*MAX_QUADS_BATCH); i++) quads.texcoords[i] = 0.0f;
for (int i = 0; i < (4*4*MAX_QUADS_BATCH); i++) quads.colors[i] = 0;
@@ -2930,166 +2703,174 @@ static void InitializeBuffers(void)
quads.tcCounter = 0;
quads.cCounter = 0;
- TraceLog(INFO, "CPU buffers (lines, triangles, quads) initialized successfully");
-}
-
-// Initialize Vertex Array Objects (Contain VBO)
-// NOTE: lines, triangles and quads buffers use currentShader
-static void InitializeBuffersGPU(void)
-{
+ TraceLog(INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)");
+ //--------------------------------------------------------------------------------------------
+
+ // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads)
+ // NOTE: Default buffers are linked to use currentShader (defaultShader)
+ //--------------------------------------------------------------------------------------------
+
+ // Upload and link lines vertex buffers
if (vaoSupported)
{
// Initialize Lines VAO
- glGenVertexArrays(1, &vaoLines);
- glBindVertexArray(vaoLines);
+ glGenVertexArrays(1, &lines.vaoId);
+ glBindVertexArray(lines.vaoId);
}
- // Create buffers for our vertex data
- glGenBuffers(2, linesBuffer);
-
- // Lines - Vertex positions buffer binding and attributes enable
- glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]);
+ // Lines - Vertex buffers binding and attributes enable
+ // Vertex position buffer (shader-location = 0)
+ glGenBuffers(2, &lines.vboId[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(currentShader.vertexLoc);
glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- // Lines - colors buffer
- glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]);
+ // Vertex color buffer (shader-location = 3)
+ glGenBuffers(2, &lines.vboId[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(currentShader.colorLoc);
glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Lines VAO initialized successfully", vaoLines);
- else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Lines VBOs initialized successfully", linesBuffer[0], linesBuffer[1]);
- //--------------------------------------------------------------
+ if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", lines.vaoId);
+ else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", lines.vboId[0], lines.vboId[1]);
+ // Upload and link triangles vertex buffers
if (vaoSupported)
{
// Initialize Triangles VAO
- glGenVertexArrays(1, &vaoTriangles);
- glBindVertexArray(vaoTriangles);
+ glGenVertexArrays(1, &triangles.vaoId);
+ glBindVertexArray(triangles.vaoId);
}
- // Create buffers for our vertex data
- glGenBuffers(2, trianglesBuffer);
-
- // Enable vertex attributes
- glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]);
+ // Triangles - Vertex buffers binding and attributes enable
+ // Vertex position buffer (shader-location = 0)
+ glGenBuffers(1, &triangles.vboId[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(currentShader.vertexLoc);
glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]);
+ // Vertex color buffer (shader-location = 3)
+ glGenBuffers(1, &triangles.vboId[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(currentShader.colorLoc);
glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
- if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Triangles VAO initialized successfully", vaoTriangles);
- else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Triangles VBOs initialized successfully", trianglesBuffer[0], trianglesBuffer[1]);
- //--------------------------------------------------------------
+ if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", triangles.vaoId);
+ else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully(triangles)", triangles.vboId[0], triangles.vboId[1]);
+ // Upload and link quads vertex buffers
if (vaoSupported)
{
// Initialize Quads VAO
- glGenVertexArrays(1, &vaoQuads);
- glBindVertexArray(vaoQuads);
+ glGenVertexArrays(1, &quads.vaoId);
+ glBindVertexArray(quads.vaoId);
}
- // Create buffers for our vertex data
- glGenBuffers(4, quadsBuffer);
-
- // Enable vertex attributes
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]);
+ // Quads - Vertex buffers binding and attributes enable
+ // Vertex position buffer (shader-location = 0)
+ glGenBuffers(1, &quads.vboId[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(currentShader.vertexLoc);
glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]);
+ // Vertex texcoord buffer (shader-location = 1)
+ glGenBuffers(1, &quads.vboId[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(currentShader.texcoordLoc);
glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]);
+ // Vertex color buffer (shader-location = 3)
+ glGenBuffers(1, &quads.vboId[2]);
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(currentShader.colorLoc);
glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
// Fill index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]);
+ glGenBuffers(1, &quads.vboId[3]);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]);
#if defined(GRAPHICS_API_OPENGL_33)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW);
#elif defined(GRAPHICS_API_OPENGL_ES2)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW);
#endif
- if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Quads VAO initialized successfully", vaoQuads);
- else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Quads VBOs initialized successfully", quadsBuffer[0], quadsBuffer[1], quadsBuffer[2], quadsBuffer[3]);
+ if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", quads.vaoId);
+ else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quads.vboId[0], quads.vboId[1], quads.vboId[2], quads.vboId[3]);
// Unbind the current VAO
if (vaoSupported) glBindVertexArray(0);
+ //--------------------------------------------------------------------------------------------
}
-// Update VBOs with vertex array data
+// Update default internal buffers (VAOs/VBOs) with vertex array data
// NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0)
-// TODO: If no data changed on the CPU arrays --> No need to update GPU arrays (change flag required)
-static void UpdateBuffers(void)
+// TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required)
+static void UpdateDefaultBuffers(void)
{
+ // Update lines vertex buffers
if (lines.vCounter > 0)
{
// Activate Lines VAO
- if (vaoSupported) glBindVertexArray(vaoLines);
+ if (vaoSupported) glBindVertexArray(lines.vaoId);
// Lines - vertex positions buffer
- glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*lines.vCounter, lines.vertices); // target - offset (in bytes) - size (in bytes) - data pointer
// Lines - colors buffer
- glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*lines.cCounter, lines.colors);
}
- //--------------------------------------------------------------
+ // Update triangles vertex buffers
if (triangles.vCounter > 0)
{
// Activate Triangles VAO
- if (vaoSupported) glBindVertexArray(vaoTriangles);
+ if (vaoSupported) glBindVertexArray(triangles.vaoId);
// Triangles - vertex positions buffer
- glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*triangles.vCounter, triangles.vertices);
// Triangles - colors buffer
- glBindBuffer(GL_ARRAY_BUFFER, trianglesBuffer[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*triangles.cCounter, triangles.colors);
}
- //--------------------------------------------------------------
+ // Update quads vertex buffers
if (quads.vCounter > 0)
{
// Activate Quads VAO
- if (vaoSupported) glBindVertexArray(vaoQuads);
+ if (vaoSupported) glBindVertexArray(quads.vaoId);
// Quads - vertex positions buffer
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[0]);
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*quads.vCounter, quads.vertices);
// Quads - texture coordinates buffer
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[1]);
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*quads.vCounter, quads.texcoords);
// Quads - colors buffer
- glBindBuffer(GL_ARRAY_BUFFER, quadsBuffer[2]);
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]);
//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*quads.vCounter, quads.colors);
// Another option would be using buffer mapping...
- //triangles.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
+ //quads.vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
// Now we can modify vertices
//glUnmapBuffer(GL_ARRAY_BUFFER);
}
@@ -3098,6 +2879,316 @@ static void UpdateBuffers(void)
// Unbind the current VAO
if (vaoSupported) glBindVertexArray(0);
}
+
+// Draw default internal buffers vertex data
+// NOTE: We draw in this order: lines, triangles, quads
+static void DrawDefaultBuffers(void)
+{
+ // Set current shader and upload current MVP matrix
+ if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0))
+ {
+ glUseProgram(currentShader.id);
+
+ // Create modelview-projection matrix
+ Matrix matMVP = MatrixMultiply(modelview, projection);
+
+ glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(matMVP));
+ glUniform4f(currentShader.tintColorLoc, 1.0f, 1.0f, 1.0f, 1.0f);
+ glUniform1i(currentShader.mapTexture0Loc, 0);
+
+ // NOTE: Additional map textures not considered for default buffers drawing
+ }
+
+ // Draw lines buffers
+ if (lines.vCounter > 0)
+ {
+ glBindTexture(GL_TEXTURE_2D, whiteTexture);
+
+ if (vaoSupported)
+ {
+ glBindVertexArray(lines.vaoId);
+ }
+ else
+ {
+ // Bind vertex attrib: position (shader-location = 0)
+ glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
+
+ // Bind vertex attrib: color (shader-location = 3)
+ glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
+ }
+
+ glDrawArrays(GL_LINES, 0, lines.vCounter);
+
+ if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ // Draw triangles buffers
+ if (triangles.vCounter > 0)
+ {
+ glBindTexture(GL_TEXTURE_2D, whiteTexture);
+
+ if (vaoSupported)
+ {
+ glBindVertexArray(triangles.vaoId);
+ }
+ else
+ {
+ // Bind vertex attrib: position (shader-location = 0)
+ glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
+
+ // Bind vertex attrib: color (shader-location = 3)
+ glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter);
+
+ if (!vaoSupported) glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ // Draw quads buffers
+ if (quads.vCounter > 0)
+ {
+ int quadsCount = 0;
+ int numIndicesToProcess = 0;
+ int indicesOffset = 0;
+
+ if (vaoSupported)
+ {
+ glBindVertexArray(quads.vaoId);
+ }
+ else
+ {
+ // Bind vertex attrib: position (shader-location = 0)
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]);
+ glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.vertexLoc);
+
+ // Bind vertex attrib: texcoord (shader-location = 1)
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]);
+ glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
+ glEnableVertexAttribArray(currentShader.texcoordLoc);
+
+ // Bind vertex attrib: color (shader-location = 3)
+ glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]);
+ glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
+ glEnableVertexAttribArray(currentShader.colorLoc);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]);
+ }
+
+ //TraceLog(DEBUG, "Draws required per frame: %i", drawsCounter);
+
+ for (int i = 0; i < drawsCounter; i++)
+ {
+ quadsCount = draws[i].vertexCount/4;
+ numIndicesToProcess = quadsCount*6; // Get number of Quads * 6 index by Quad
+
+ //TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount);
+
+ glBindTexture(GL_TEXTURE_2D, draws[i].textureId);
+
+ // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process
+#if defined(GRAPHICS_API_OPENGL_33)
+ glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid *)(sizeof(GLuint)*indicesOffset));
+#elif defined(GRAPHICS_API_OPENGL_ES2)
+ glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*indicesOffset));
+#endif
+ //GLenum err;
+ //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM!
+
+ indicesOffset += draws[i].vertexCount/4*6;
+ }
+
+ if (!vaoSupported)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures
+ }
+
+ if (vaoSupported) glBindVertexArray(0); // Unbind VAO
+
+ glUseProgram(0); // Unbind shader program
+
+ // Reset draws counter
+ drawsCounter = 1;
+ draws[0].textureId = whiteTexture;
+ draws[0].vertexCount = 0;
+
+ // Reset vertex counters for next frame
+ lines.vCounter = 0;
+ lines.cCounter = 0;
+ triangles.vCounter = 0;
+ triangles.cCounter = 0;
+ quads.vCounter = 0;
+ quads.tcCounter = 0;
+ quads.cCounter = 0;
+
+ // Reset depth for next draw
+ currentDepth = -1.0f;
+}
+
+// Unload default internal buffers vertex data from CPU and GPU
+static void UnloadDefaultBuffers(void)
+{
+ // Unbind everything
+ if (vaoSupported) glBindVertexArray(0);
+ glDisableVertexAttribArray(0);
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(2);
+ glDisableVertexAttribArray(3);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ // Delete VBOs from GPU (VRAM)
+ glDeleteBuffers(1, &lines.vboId[0]);
+ glDeleteBuffers(1, &lines.vboId[1]);
+ glDeleteBuffers(1, &triangles.vboId[0]);
+ glDeleteBuffers(1, &triangles.vboId[1]);
+ glDeleteBuffers(1, &quads.vboId[0]);
+ glDeleteBuffers(1, &quads.vboId[1]);
+ glDeleteBuffers(1, &quads.vboId[2]);
+ glDeleteBuffers(1, &quads.vboId[3]);
+
+ if (vaoSupported)
+ {
+ // Delete VAOs from GPU (VRAM)
+ glDeleteVertexArrays(1, &lines.vaoId);
+ glDeleteVertexArrays(1, &triangles.vaoId);
+ glDeleteVertexArrays(1, &quads.vaoId);
+ }
+
+ // Free vertex arrays memory from CPU (RAM)
+ free(lines.vertices);
+ free(lines.colors);
+
+ free(triangles.vertices);
+ free(triangles.colors);
+
+ free(quads.vertices);
+ free(quads.texcoords);
+ free(quads.colors);
+ free(quads.indices);
+}
+
+// Setup shader uniform values for lights array
+// NOTE: It would be far easier with shader UBOs but are not supported on OpenGL ES 2.0f
+static void SetShaderLights(Shader shader)
+{
+ int locPoint = glGetUniformLocation(shader.id, "lightsCount");
+ glUniform1i(locPoint, lightsCount);
+
+ char locName[32] = "lights[x].position\0";
+
+ for (int i = 0; i < lightsCount; i++)
+ {
+ locName[7] = '0' + i;
+
+ memcpy(&locName[10], "enabled\0", strlen("enabled\0") + 1);
+ locPoint = GetShaderLocation(shader, locName);
+ glUniform1i(locPoint, lights[i]->enabled);
+
+ memcpy(&locName[10], "type\0", strlen("type\0") + 1);
+ locPoint = GetShaderLocation(shader, locName);
+ glUniform1i(locPoint, lights[i]->type);
+
+ memcpy(&locName[10], "diffuse\0", strlen("diffuse\0") + 2);
+ locPoint = glGetUniformLocation(shader.id, locName);
+ glUniform4f(locPoint, (float)lights[i]->diffuse.r/255, (float)lights[i]->diffuse.g/255, (float)lights[i]->diffuse.b/255, (float)lights[i]->diffuse.a/255);
+
+ memcpy(&locName[10], "intensity\0", strlen("intensity\0"));
+ locPoint = glGetUniformLocation(shader.id, locName);
+ glUniform1f(locPoint, lights[i]->intensity);
+
+ switch (lights[i]->type)
+ {
+ case LIGHT_POINT:
+ {
+ memcpy(&locName[10], "position\0", strlen("position\0") + 1);
+ locPoint = GetShaderLocation(shader, locName);
+ glUniform3f(locPoint, lights[i]->position.x, lights[i]->position.y, lights[i]->position.z);
+
+ memcpy(&locName[10], "radius\0", strlen("radius\0") + 2);
+ locPoint = GetShaderLocation(shader, locName);
+ glUniform1f(locPoint, lights[i]->radius);
+ } break;
+ case LIGHT_DIRECTIONAL:
+ {
+ memcpy(&locName[10], "direction\0", strlen("direction\0") + 2);
+ locPoint = GetShaderLocation(shader, locName);
+ Vector3 direction = { lights[i]->target.x - lights[i]->position.x, lights[i]->target.y - lights[i]->position.y, lights[i]->target.z - lights[i]->position.z };
+ VectorNormalize(&direction);
+ glUniform3f(locPoint, direction.x, direction.y, direction.z);
+ } break;
+ case LIGHT_SPOT:
+ {
+ memcpy(&locName[10], "position\0", strlen("position\0") + 1);
+ locPoint = GetShaderLocation(shader, locName);
+ glUniform3f(locPoint, lights[i]->position.x, lights[i]->position.y, lights[i]->position.z);
+
+ memcpy(&locName[10], "direction\0", strlen("direction\0") + 2);
+ locPoint = GetShaderLocation(shader, locName);
+
+ Vector3 direction = { lights[i]->target.x - lights[i]->position.x, lights[i]->target.y - lights[i]->position.y, lights[i]->target.z - lights[i]->position.z };
+ VectorNormalize(&direction);
+ glUniform3f(locPoint, direction.x, direction.y, direction.z);
+
+ memcpy(&locName[10], "coneAngle\0", strlen("coneAngle\0"));
+ locPoint = GetShaderLocation(shader, locName);
+ glUniform1f(locPoint, lights[i]->coneAngle);
+ } break;
+ default: break;
+ }
+
+ // TODO: Pass to the shader any other required data from LightData struct
+ }
+}
+
+// Read text data from file
+// NOTE: text chars array should be freed manually
+static char *ReadTextFile(const char *fileName)
+{
+ FILE *textFile;
+ char *text = NULL;
+
+ int count = 0;
+
+ if (fileName != NULL)
+ {
+ textFile = fopen(fileName,"rt");
+
+ if (textFile != NULL)
+ {
+ fseek(textFile, 0, SEEK_END);
+ count = ftell(textFile);
+ rewind(textFile);
+
+ if (count > 0)
+ {
+ text = (char *)malloc(sizeof(char)*(count + 1));
+ count = fread(text, sizeof(char), count, textFile);
+ text[count] = '\0';
+ }
+
+ fclose(textFile);
+ }
+ else TraceLog(WARNING, "[%s] Text file could not be opened", fileName);
+ }
+
+ return text;
+}
#endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
#if defined(GRAPHICS_API_OPENGL_11)
@@ -3107,7 +3198,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight)
int mipmapCount = 1; // Required mipmap levels count (including base level)
int width = baseWidth;
int height = baseHeight;
- int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...)
+ int size = baseWidth*baseHeight*4; // Size in bytes (will include mipmaps...), RGBA only
// Count mipmap levels required
while ((width != 1) && (height != 1))
@@ -3136,8 +3227,8 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight)
// Generate mipmaps
// NOTE: Every mipmap data is stored after data
- pixel *image = (pixel *)malloc(width*height*sizeof(pixel));
- pixel *mipmap = NULL;
+ Color *image = (Color *)malloc(width*height*sizeof(Color));
+ Color *mipmap = NULL;
int offset = 0;
int j = 0;
@@ -3185,15 +3276,15 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight)
}
// Manual mipmap generation (basic scaling algorithm)
-static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight)
+static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight)
{
int x2, y2;
- pixel prow, pcol;
+ Color prow, pcol;
int width = srcWidth/2;
int height = srcHeight/2;
- pixel *mipmap = (pixel *)malloc(width*height*sizeof(pixel));
+ Color *mipmap = (Color *)malloc(width*height*sizeof(Color));
// Scaling algorithm works perfectly (box-filter)
for (int y = 0; y < height; y++)
@@ -3228,7 +3319,6 @@ static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight)
#endif
#if defined(RLGL_STANDALONE)
-
// Output a trace log message
// NOTE: Expected msgType: (0)Info, (1)Error, (2)Warning
static void TraceLog(int msgType, const char *text, ...)
@@ -3236,7 +3326,7 @@ static void TraceLog(int msgType, const char *text, ...)
va_list args;
va_start(args, text);
- switch(msgType)
+ switch (msgType)
{
case INFO: fprintf(stdout, "INFO: "); break;
case ERROR: fprintf(stdout, "ERROR: "); break;
@@ -3252,4 +3342,32 @@ static void TraceLog(int msgType, const char *text, ...)
if (msgType == ERROR) exit(1);
}
+
+// Converts Matrix to float array
+// NOTE: Returned vector is a transposed version of the Matrix struct,
+// it should be this way because, despite raymath use OpenGL column-major convention,
+// Matrix struct memory alignment and variables naming are not coherent
+float *MatrixToFloat(Matrix mat)
+{
+ static float buffer[16];
+
+ buffer[0] = mat.m0;
+ buffer[1] = mat.m4;
+ buffer[2] = mat.m8;
+ buffer[3] = mat.m12;
+ buffer[4] = mat.m1;
+ buffer[5] = mat.m5;
+ buffer[6] = mat.m9;
+ buffer[7] = mat.m13;
+ buffer[8] = mat.m2;
+ buffer[9] = mat.m6;
+ buffer[10] = mat.m10;
+ buffer[11] = mat.m14;
+ buffer[12] = mat.m3;
+ buffer[13] = mat.m7;
+ buffer[14] = mat.m11;
+ buffer[15] = mat.m15;
+
+ return buffer;
+}
#endif
diff --git a/src/rlgl.h b/src/rlgl.h
index 9e0aaaaa..2a578a1f 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -32,11 +32,15 @@
//#define RLGL_STANDALONE // NOTE: To use rlgl as standalone lib, just uncomment this line
#ifndef RLGL_STANDALONE
- #include "raylib.h" // Required for typedef(s): Model, Shader, Texture2D
- #include "utils.h" // Required for function TraceLog()
+ #include "raylib.h" // Required for: Model, Shader, Texture2D
+ #include "utils.h" // Required for: TraceLog()
#endif
-#include "raymath.h"
+#ifdef RLGL_STANDALONE
+ #define RAYMATH_STANDALONE
+#endif
+
+#include "raymath.h" // Required for: Vector3, Matrix
// Select desired OpenGL version
// NOTE: Those preprocessor defines are only used on rlgl module,
@@ -126,52 +130,43 @@ typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion;
COMPRESSED_ASTC_4x4_RGBA, // 8 bpp
COMPRESSED_ASTC_8x8_RGBA // 2 bpp
} TextureFormat;
-
- // Bounding box type
- typedef struct BoundingBox {
- Vector3 min;
- Vector3 max;
- } BoundingBox;
-
- // Mesh with vertex data type
- // NOTE: If using OpenGL 1.1, data loaded in CPU; if OpenGL 3.3+ data loaded in GPU (vaoId)
+
+ // Vertex data definning a mesh
typedef struct Mesh {
- int vertexCount; // num vertices
- float *vertices; // vertex position (XYZ - 3 components per vertex)
- float *texcoords; // vertex texture coordinates (UV - 2 components per vertex)
- float *texcoords2; // vertex second texture coordinates (useful for lightmaps)
- float *normals; // vertex normals (XYZ - 3 components per vertex)
- float *tangents; // vertex tangents (XYZ - 3 components per vertex)
- unsigned char *colors; // vertex colors (RGBA - 4 components per vertex)
-
- BoundingBox bounds; // mesh limits defined by min and max points
-
- unsigned int vaoId; // OpenGL Vertex Array Object id
- unsigned int vboId[6]; // OpenGL Vertex Buffer Objects id (6 types of vertex data)
+ int vertexCount; // number of vertices stored in arrays
+ int triangleCount; // number of triangles stored (indexed or not)
+ float *vertices; // vertex position (XYZ - 3 components per vertex) (shader-location = 0)
+ float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
+ float *texcoords2; // vertex second texture coordinates (useful for lightmaps) (shader-location = 5)
+ float *normals; // vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
+ float *tangents; // vertex tangents (XYZ - 3 components per vertex) (shader-location = 4)
+ unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
+ unsigned short *indices;// vertex indices (in case vertex data comes indexed)
+
+ unsigned int vaoId; // OpenGL Vertex Array Object id
+ unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data)
} Mesh;
- // Shader type
+ // Shader type (generic shader)
typedef struct Shader {
- unsigned int id; // Shader program id
-
- // TODO: This should be Texture2D objects
- unsigned int texDiffuseId; // Diffuse texture id
- unsigned int texNormalId; // Normal texture id
- unsigned int texSpecularId; // Specular texture id
+ unsigned int id; // Shader program id
- // Variable attributes
- int vertexLoc; // Vertex attribute location point (vertex shader)
- int texcoordLoc; // Texcoord attribute location point (vertex shader)
- int normalLoc; // Normal attribute location point (vertex shader)
- int colorLoc; // Color attibute location point (vertex shader)
-
- // Uniforms
- int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader)
- int tintColorLoc; // Color uniform location point (fragment shader)
+ // Vertex attributes locations (default locations)
+ int vertexLoc; // Vertex attribute location point (default-location = 0)
+ int texcoordLoc; // Texcoord attribute location point (default-location = 1)
+ int normalLoc; // Normal attribute location point (default-location = 2)
+ int colorLoc; // Color attibute location point (default-location = 3)
+ int tangentLoc; // Tangent attribute location point (default-location = 4)
+ int texcoord2Loc; // Texcoord2 attribute location point (default-location = 5)
+
+ // Uniform locations
+ int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader)
+ int tintColorLoc; // Color uniform location point (fragment shader)
- int mapDiffuseLoc; // Diffuse map texture uniform location point (fragment shader)
- int mapNormalLoc; // Normal map texture uniform location point (fragment shader)
- int mapSpecularLoc; // Specular map texture uniform location point (fragment shader)
+ // Texture map locations (generic for any kind of map)
+ int mapTexture0Loc; // Map texture uniform location point (default-texture-unit = 0)
+ int mapTexture1Loc; // Map texture uniform location point (default-texture-unit = 1)
+ int mapTexture2Loc; // Map texture uniform location point (default-texture-unit = 2)
} Shader;
// Texture2D type
@@ -184,14 +179,55 @@ typedef enum { OPENGL_11 = 1, OPENGL_33, OPENGL_ES_20 } GlVersion;
int format; // Data format (TextureFormat)
} Texture2D;
- // 3d Model type
- typedef struct Model {
- Mesh mesh;
- Matrix transform;
- Texture2D texture;
- Shader shader;
- } Model;
-
+ // RenderTexture2D type, for texture rendering
+ typedef struct RenderTexture2D {
+ unsigned int id; // Render texture (fbo) id
+ Texture2D texture; // Color buffer attachment texture
+ Texture2D depth; // Depth buffer attachment texture
+ } RenderTexture2D;
+
+ // Material type
+ typedef struct Material {
+ Shader shader; // Standard shader (supports 3 map types: diffuse, normal, specular)
+
+ Texture2D texDiffuse; // Diffuse texture
+ Texture2D texNormal; // Normal texture
+ Texture2D texSpecular; // Specular texture
+
+ Color colDiffuse; // Diffuse color
+ Color colAmbient; // Ambient color
+ Color colSpecular; // Specular color
+
+ float glossiness; // Glossiness level (Ranges from 0 to 1000)
+ } Material;
+
+ // Camera type, defines a camera position/orientation in 3d space
+ typedef struct Camera {
+ Vector3 position; // Camera position
+ Vector3 target; // Camera target it looks-at
+ Vector3 up; // Camera up vector (rotation over its axis)
+ float fovy; // Camera field-of-view apperture in Y (degrees)
+ } Camera;
+
+ // Light type
+ typedef struct LightData {
+ unsigned int id; // Light unique id
+ int type; // Light type: LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT
+ bool enabled; // Light enabled
+
+ Vector3 position; // Light position
+ Vector3 target; // Light target: LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction target)
+ float radius; // Light attenuation radius light intensity reduced with distance (world distance)
+
+ Color diffuse; // Light diffuse color
+ float intensity; // Light intensity level
+
+ float coneAngle; // Light cone max angle: LIGHT_SPOT
+ } LightData, *Light;
+
+ // Light types
+ typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType;
+
// Color blending modes (pre-defined)
typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode;
#endif
@@ -213,6 +249,7 @@ void rlScalef(float x, float y, float z); // Multiply the current matrix b
void rlMultMatrixf(float *mat); // Multiply the current matrix by another matrix
void rlFrustum(double left, double right, double bottom, double top, double near, double far);
void rlOrtho(double left, double right, double bottom, double top, double near, double far);
+void rlViewport(int x, int y, int width, int height); // Set the viewport area
//------------------------------------------------------------------------------------
// Functions Declaration - Vertex level operations
@@ -234,14 +271,20 @@ void rlColor4f(float x, float y, float z, float w); // Define one vertex (color)
//------------------------------------------------------------------------------------
void rlEnableTexture(unsigned int id); // Enable texture usage
void rlDisableTexture(void); // Disable texture usage
+void rlEnableRenderTexture(unsigned int id); // Enable render texture (fbo)
+void rlDisableRenderTexture(void); // Disable render texture (fbo), return to default framebuffer
+void rlEnableDepthTest(void); // Enable depth test
+void rlDisableDepthTest(void); // Disable depth test
+void rlEnableWireMode(void); // Enable wire mode
+void rlDisableWireMode(void); // Disable wire mode
void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU
+void rlDeleteRenderTextures(RenderTexture2D target); // Delete render textures (fbo) from GPU
void rlDeleteShader(unsigned int id); // Delete OpenGL shader program from GPU
void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (VAO) from GPU memory
void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory
void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color
void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth)
int rlGetVersion(void); // Returns current OpenGL version
-void rlEnablePostproFBO(void); // Enable rendering to postprocessing FBO
//------------------------------------------------------------------------------------
// Functions Declaration - rlgl functionality
@@ -252,27 +295,22 @@ void rlglDraw(void); // Draw VAO/VBO
void rlglInitGraphics(int offsetX, int offsetY, int width, int height); // Initialize Graphics (OpenGL stuff)
unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount); // Load texture in GPU
+RenderTexture2D rlglLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with color and depth attachments)
void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data); // Update GPU texture with new data
void rlglGenerateMipmaps(Texture2D texture); // Generate mipmap data for selected texture
-// NOTE: There is a set of shader related functions that are available to end user,
-// to avoid creating function wrappers through core module, they have been directly declared in raylib.h
-
-void rlglInitPostpro(void); // Initialize postprocessing system
-void rlglDrawPostpro(void); // Draw with postprocessing shader
-
-Model rlglLoadModel(Mesh mesh); // Upload vertex data into GPU and provided VAO/VBO ids
-void rlglDrawModel(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color color, bool wires);
+void rlglLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids
+void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex); // Update vertex data on GPU (upload new data to one buffer)
+void rlglDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform
+void rlglUnloadMesh(Mesh *mesh); // Unload mesh data from CPU and GPU
Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates
unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer)
void *rlglReadTexturePixels(Texture2D texture); // Read texture pixel data
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-void PrintProjectionMatrix(void); // DEBUG: Print projection matrix
-void PrintModelviewMatrix(void); // DEBUG: Print modelview matrix
-#endif
+// NOTE: There is a set of shader related functions that are available to end user,
+// to avoid creating function wrappers through core module, they have been directly declared in raylib.h
#if defined(RLGL_STANDALONE)
//------------------------------------------------------------------------------------
@@ -280,23 +318,27 @@ void PrintModelviewMatrix(void); // DEBUG: Print modelview matrix
// NOTE: This functions are useless when using OpenGL 1.1
//------------------------------------------------------------------------------------
Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations
-unsigned int LoadShaderProgram(char *vShaderStr, char *fShaderStr); // Load custom shader strings and return program id
void UnloadShader(Shader shader); // Unload a custom shader from memory
-void SetPostproShader(Shader shader); // Set fullscreen postproduction shader
-void SetCustomShader(Shader shader); // Set custom shader to be used in batch draw
-void SetDefaultShader(void); // Set default shader to be used in batch draw
-void SetModelShader(Model *model, Shader shader); // Link a shader to a model
-bool IsPosproShaderEnabled(void); // Check if postprocessing shader is enabled
-
-int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location
-void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float)
-void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int)
-void SetShaderMapDiffuse(Shader *shader, Texture2D texture); // Default diffuse shader map texture assignment
-void SetShaderMapNormal(Shader *shader, const char *uniformName, Texture2D texture); // Normal map texture shader assignment
-void SetShaderMapSpecular(Shader *shader, const char *uniformName, Texture2D texture); // Specular map texture shader assignment
-void SetShaderMap(Shader *shader, int mapLocation, Texture2D texture, int textureUnit); // TODO: Generic shader map assignment
-
-void SetBlendMode(int mode); // Set blending mode (alpha, additive, multiplied)
+
+Shader GetDefaultShader(void); // Get default shader
+Shader GetStandardShader(void); // Get default shader
+Texture2D GetDefaultTexture(void); // Get default texture
+
+int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location
+void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float)
+void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int)
+void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4)
+
+void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix)
+void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix)
+
+void BeginShaderMode(Shader shader); // Begin custom shader drawing
+void EndShaderMode(void); // End custom shader drawing (use default shader)
+void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied)
+void EndBlendMode(void); // End blending mode (reset to default: alpha blending)
+
+Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool
+void DestroyLight(Light light); // Destroy a light and take it out of the list
#endif
#ifdef __cplusplus
diff --git a/src/shapes.c b/src/shapes.c
index 65e3621b..7129ac17 100644
--- a/src/shapes.c
+++ b/src/shapes.c
@@ -98,7 +98,7 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
// Draw a color-filled circle
void DrawCircle(int centerX, int centerY, float radius, Color color)
{
- DrawPoly((Vector2){ centerX, centerY }, 36, radius, 0, color);
+ DrawCircleV((Vector2){ centerX, centerY }, radius, color);
}
// Draw a gradient-filled circle
@@ -119,17 +119,40 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
}
// Draw a color-filled circle (Vector version)
+// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawCircleV(Vector2 center, float radius, Color color)
{
- rlBegin(RL_TRIANGLES);
- for (int i = 0; i < 360; i += 10)
- {
- rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(center.x, center.y);
- rlVertex2f(center.x + sin(DEG2RAD*i)*radius, center.y + cos(DEG2RAD*i)*radius);
- rlVertex2f(center.x + sin(DEG2RAD*(i + 10)) * radius, center.y + cos(DEG2RAD*(i + 10)) * radius);
- }
- rlEnd();
+ if (rlGetVersion() == OPENGL_11)
+ {
+ rlBegin(RL_TRIANGLES);
+ for (int i = 0; i < 360; i += 10)
+ {
+ rlColor4ub(color.r, color.g, color.b, color.a);
+
+ rlVertex2i(center.x, center.y);
+ rlVertex2f(center.x + sin(DEG2RAD*i)*radius, center.y + cos(DEG2RAD*i)*radius);
+ rlVertex2f(center.x + sin(DEG2RAD*(i + 10)) * radius, center.y + cos(DEG2RAD*(i + 10)) * radius);
+ }
+ rlEnd();
+ }
+ else if ((rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20))
+ {
+ rlEnableTexture(whiteTexture); // Default white texture
+
+ rlBegin(RL_QUADS);
+ for (int i = 0; i < 360; i += 20)
+ {
+ rlColor4ub(color.r, color.g, color.b, color.a);
+
+ rlVertex2i(center.x, center.y);
+ rlVertex2f(center.x + sin(DEG2RAD*i)*radius, center.y + cos(DEG2RAD*i)*radius);
+ rlVertex2f(center.x + sin(DEG2RAD*(i + 10)) * radius, center.y + cos(DEG2RAD*(i + 10)) * radius);
+ rlVertex2f(center.x + sin(DEG2RAD*(i + 20)) * radius, center.y + cos(DEG2RAD*(i + 20)) * radius);
+ }
+ rlEnd();
+
+ rlDisableTexture();
+ }
}
// Draw circle outline
@@ -178,6 +201,7 @@ void DrawRectangleGradient(int posX, int posY, int width, int height, Color colo
}
// Draw a color-filled rectangle (Vector version)
+// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawRectangleV(Vector2 position, Vector2 size, Color color)
{
if (rlGetVersion() == OPENGL_11)
@@ -196,7 +220,6 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color)
}
else if ((rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20))
{
- // NOTE: This shape uses QUADS to avoid drawing order issues (view rlglDraw)
rlEnableTexture(whiteTexture); // Default white texture
rlBegin(RL_QUADS);
@@ -221,22 +244,33 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color)
}
// Draw rectangle outline
+// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
-{
- rlBegin(RL_LINES);
- rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(posX + 1, posY + 1);
- rlVertex2i(posX + width, posY + 1);
+{
+ if (rlGetVersion() == OPENGL_11)
+ {
+ rlBegin(RL_LINES);
+ rlColor4ub(color.r, color.g, color.b, color.a);
+ rlVertex2i(posX + 1, posY + 1);
+ rlVertex2i(posX + width, posY + 1);
- rlVertex2i(posX + width, posY + 1);
- rlVertex2i(posX + width, posY + height);
+ rlVertex2i(posX + width, posY + 1);
+ rlVertex2i(posX + width, posY + height);
- rlVertex2i(posX + width, posY + height);
- rlVertex2i(posX + 1, posY + height);
+ rlVertex2i(posX + width, posY + height);
+ rlVertex2i(posX + 1, posY + height);
- rlVertex2i(posX + 1, posY + height);
- rlVertex2i(posX + 1, posY + 1);
- rlEnd();
+ rlVertex2i(posX + 1, posY + height);
+ rlVertex2i(posX + 1, posY + 1);
+ rlEnd();
+ }
+ else if ((rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20))
+ {
+ DrawRectangle(posX, posY, width, 1, color);
+ DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
+ DrawRectangle(posX, posY + height - 1, width, 1, color);
+ DrawRectangle(posX, posY + 1, 1, height - 2, color);
+ }
}
// Draw a triangle
@@ -412,7 +446,6 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
}
// Get collision rectangle for two rectangles collision
-// TODO: Depending on rec1 and rec2 order, it fails -> Review!
Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
{
Rectangle retRec = { 0, 0, 0, 0 };
@@ -457,8 +490,23 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
}
}
- if (retRec.width >= rec2.width) retRec.width = rec2.width;
- if (retRec.height >= rec2.height) retRec.height = rec2.height;
+ if (rec1.width > rec2.width)
+ {
+ if (retRec.width >= rec2.width) retRec.width = rec2.width;
+ }
+ else
+ {
+ if (retRec.width >= rec1.width) retRec.width = rec1.width;
+ }
+
+ if (rec1.height > rec2.height)
+ {
+ if (retRec.height >= rec2.height) retRec.height = rec2.height;
+ }
+ else
+ {
+ if (retRec.height >= rec1.height) retRec.height = rec1.height;
+ }
}
return retRec;
diff --git a/src/text.c b/src/text.c
index e4c7bbf3..b5f7ad0c 100644
--- a/src/text.c
+++ b/src/text.c
@@ -25,16 +25,16 @@
#include "raylib.h"
-#include <stdlib.h> // Declares malloc() and free() for memory management
-#include <string.h> // String management functions (just strlen() is used)
-#include <stdarg.h> // Used for functions with variable number of parameters (FormatText())
-#include <stdio.h> // Standard input / output lib
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strlen()
+#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
+#include <stdio.h> // Required for: FILE, fopen(), fclose(), fscanf(), feof(), rewind(), fgets()
-#include "utils.h" // Required for function GetExtension()
+#include "utils.h" // Required for: GetExtension()
// Following libs are used on LoadTTF()
#define STB_TRUETYPE_IMPLEMENTATION
-#include "stb_truetype.h"
+#include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap()
// Rectangle packing functions (not used at the moment)
//#define STB_RECT_PACK_IMPLEMENTATION
@@ -69,8 +69,7 @@ static SpriteFont defaultFont; // Default font provided by raylib
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
-static bool PixelIsMagenta(Color p); // Check if a pixel is magenta
-static int ParseImageData(Image image, int **charValues, Rectangle **charSet); // Parse image pixel data to obtain characters data
+static SpriteFont LoadImageFont(Image image, Color key, int firstChar); // Load a Image font file (XNA style)
static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font)
static SpriteFont LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
static SpriteFont LoadTTF(const char *fileName, int fontSize); // Generate a sprite font image from TTF data (font size required)
@@ -210,7 +209,7 @@ extern void LoadDefaultFont(void)
if (testPosX >= defaultFont.texture.width)
{
currentLine++;
- currentPosX = 2 * charsDivisor + charsWidth[i];
+ currentPosX = 2*charsDivisor + charsWidth[i];
testPosX = currentPosX;
defaultFont.charRecs[i].x = charsDivisor;
@@ -246,7 +245,7 @@ SpriteFont GetDefaultFont()
// Load a SpriteFont image into GPU memory
SpriteFont LoadSpriteFont(const char *fileName)
{
- SpriteFont spriteFont;
+ SpriteFont spriteFont = { 0 };
// Check file extension
if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName);
@@ -255,36 +254,7 @@ SpriteFont LoadSpriteFont(const char *fileName)
else
{
Image image = LoadImage(fileName);
-
- if (image.data != NULL)
- {
- // Process bitmap font pixel data to get characters measures
- // spriteFont chars data is filled inside the function and memory is allocated!
- int numChars = ParseImageData(image, &spriteFont.charValues, &spriteFont.charRecs);
-
- TraceLog(DEBUG, "[%s] SpriteFont data parsed correctly", fileName);
- TraceLog(DEBUG, "[%s] SpriteFont num chars detected: %i", fileName, numChars);
-
- spriteFont.numChars = numChars;
- spriteFont.texture = LoadTextureFromImage(image); // Convert loaded image to OpenGL texture
- spriteFont.size = spriteFont.charRecs[0].height;
-
- spriteFont.charOffsets = (Vector2 *)malloc(spriteFont.numChars*sizeof(Vector2));
- spriteFont.charAdvanceX = (int *)malloc(spriteFont.numChars*sizeof(int));
-
- for (int i = 0; i < spriteFont.numChars; i++)
- {
- // NOTE: On image based fonts (XNA style), character offsets and xAdvance are not required (set to 0)
- spriteFont.charOffsets[i] = (Vector2){ 0.0f, 0.0f };
- spriteFont.charAdvanceX[i] = 0;
- }
- }
- else
- {
- TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName);
- spriteFont = GetDefaultFont();
- }
-
+ if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, 32);
UnloadImage(image);
}
@@ -309,7 +279,7 @@ void UnloadSpriteFont(SpriteFont spriteFont)
free(spriteFont.charOffsets);
free(spriteFont.charAdvanceX);
- TraceLog(INFO, "Unloaded sprite font data");
+ TraceLog(DEBUG, "Unloaded sprite font data");
}
}
@@ -517,15 +487,11 @@ void DrawFPS(int posX, int posY)
// Module specific Functions Definition
//----------------------------------------------------------------------------------
-// Check if a pixel is magenta
-static bool PixelIsMagenta(Color p)
-{
- return ((p.r == 255) && (p.g == 0) && (p.b == 255) && (p.a == 255));
-}
-
-// Parse image pixel data to obtain characters data (measures)
-static int ParseImageData(Image image, int **charValues, Rectangle **charRecs)
+// Load a Image font file (XNA style)
+static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
{
+ #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
+
int charSpacing = 0;
int lineSpacing = 0;
@@ -539,13 +505,14 @@ static int ParseImageData(Image image, int **charValues, Rectangle **charRecs)
Color *pixels = GetImageData(image);
+ // Parse image data to get charSpacing and lineSpacing
for(y = 0; y < image.height; y++)
{
for(x = 0; x < image.width; x++)
{
- if (!PixelIsMagenta(pixels[y*image.width + x])) break;
+ if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
}
- if (!PixelIsMagenta(pixels[y*image.width + x])) break;
+ if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
}
charSpacing = x;
@@ -554,7 +521,7 @@ static int ParseImageData(Image image, int **charValues, Rectangle **charRecs)
int charHeight = 0;
int j = 0;
- while(!PixelIsMagenta(pixels[(lineSpacing + j)*image.width + charSpacing])) j++;
+ while(!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
charHeight = j;
@@ -563,12 +530,13 @@ static int ParseImageData(Image image, int **charValues, Rectangle **charRecs)
int lineToRead = 0;
int xPosToRead = charSpacing;
+ // Parse image data to get rectangle sizes
while((lineSpacing + lineToRead * (charHeight + lineSpacing)) < image.height)
{
while((xPosToRead < image.width) &&
- !PixelIsMagenta((pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead])))
+ !COLOR_EQUAL((pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead]), key))
{
- tempCharValues[index] = FONT_FIRST_CHAR + index;
+ tempCharValues[index] = firstChar + index;
tempCharRecs[index].x = xPosToRead;
tempCharRecs[index].y = lineSpacing + lineToRead * (charHeight + lineSpacing);
@@ -576,7 +544,7 @@ static int ParseImageData(Image image, int **charValues, Rectangle **charRecs)
int charWidth = 0;
- while(!PixelIsMagenta(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth])) charWidth++;
+ while(!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
tempCharRecs[index].width = charWidth;
@@ -590,20 +558,35 @@ static int ParseImageData(Image image, int **charValues, Rectangle **charRecs)
}
free(pixels);
+
+ TraceLog(DEBUG, "SpriteFont data parsed correctly from image");
+
+ // Create spritefont with all data parsed from image
+ SpriteFont spriteFont = { 0 };
+
+ spriteFont.texture = LoadTextureFromImage(image); // Convert loaded image to OpenGL texture
+ spriteFont.numChars = index;
// We got tempCharValues and tempCharsRecs populated with chars data
- // Now we move temp data to sized charValues and charRecs arrays (passed as parameter to the function)
- // NOTE: This memory should be freed!
- (*charRecs) = (Rectangle *)malloc(index*sizeof(Rectangle));
- (*charValues) = (int *)malloc(index*sizeof(int));
+ // Now we move temp data to sized charValues and charRecs arrays
+ spriteFont.charRecs = (Rectangle *)malloc(spriteFont.numChars*sizeof(Rectangle));
+ spriteFont.charValues = (int *)malloc(spriteFont.numChars*sizeof(int));
+ spriteFont.charOffsets = (Vector2 *)malloc(spriteFont.numChars*sizeof(Vector2));
+ spriteFont.charAdvanceX = (int *)malloc(spriteFont.numChars*sizeof(int));
- for (int i = 0; i < index; i++)
+ for (int i = 0; i < spriteFont.numChars; i++)
{
- (*charValues)[i] = tempCharValues[i];
- (*charRecs)[i] = tempCharRecs[i];
+ spriteFont.charValues[i] = tempCharValues[i];
+ spriteFont.charRecs[i] = tempCharRecs[i];
+
+ // NOTE: On image based fonts (XNA style), character offsets and xAdvance are not required (set to 0)
+ spriteFont.charOffsets[i] = (Vector2){ 0.0f, 0.0f };
+ spriteFont.charAdvanceX[i] = 0;
}
+
+ spriteFont.size = spriteFont.charRecs[0].height;
- return index;
+ return spriteFont;
}
// Load a rBMF font file (raylib BitMap Font)
@@ -687,6 +670,7 @@ static SpriteFont LoadRBMF(const char *fileName)
TraceLog(DEBUG, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
+ // Create spritefont with all data read from rbmf file
spriteFont.texture = LoadTextureFromImage(image);
UnloadImage(image); // Unload image data
@@ -819,11 +803,17 @@ static SpriteFont LoadBMFont(const char *fileName)
int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
+ bool unorderedChars = false;
+ int firstChar = 0;
+
for (int i = 0; i < numChars; i++)
{
fgets(buffer, MAX_BUFFER_SIZE, fntFile);
sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
+
+ if (i == 0) firstChar = charId;
+ else if (i != (charId - firstChar)) unorderedChars = true;
// Save data properly in sprite font
font.charValues[i] = charId;
@@ -832,14 +822,20 @@ static SpriteFont LoadBMFont(const char *fileName)
font.charAdvanceX[i] = charAdvanceX;
}
- // TODO: Font data could be not ordered by charId: 32,33,34,35... review charValues and charRecs order
-
fclose(fntFile);
-
- TraceLog(INFO, "[%s] SpriteFont loaded successfully", fileName);
+
+ if (firstChar != FONT_FIRST_CHAR) TraceLog(WARNING, "BMFont not supported: expected SPACE(32) as first character, falling back to default font");
+ else if (unorderedChars) TraceLog(WARNING, "BMFont not supported: unordered chars data, falling back to default font");
+
+ // NOTE: Font data could be not ordered by charId: 32,33,34,35... raylib does not support unordered BMFonts
+ if ((firstChar != FONT_FIRST_CHAR) || (unorderedChars))
+ {
+ UnloadSpriteFont(font);
+ font = GetDefaultFont();
+ }
+ else TraceLog(INFO, "[%s] SpriteFont loaded successfully", fileName);
return font;
-
}
// Generate a sprite font from TTF file data (font size required)
diff --git a/src/textures.c b/src/textures.c
index 36819daf..518348f4 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -29,21 +29,23 @@
#include "raylib.h"
-#include <stdlib.h> // Declares malloc() and free() for memory management
-#include <string.h> // Required for strcmp(), strrchr(), strncmp()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strcmp(), strrchr(), strncmp()
-#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
- // Required: rlglLoadTexture() rlDeleteTextures(),
- // rlglGenerateMipmaps(), some funcs for DrawTexturePro()
+#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
+ // Required: rlglLoadTexture() rlDeleteTextures(),
+ // rlglGenerateMipmaps(), some funcs for DrawTexturePro()
-#include "utils.h" // rRES data decompression utility function
- // NOTE: Includes Android fopen function map
+#include "utils.h" // rRES data decompression utility function
+ // NOTE: Includes Android fopen function map
#define STB_IMAGE_IMPLEMENTATION
-#include "stb_image.h" // Used to read image data (multiple formats support)
+#include "external/stb_image.h" // Required for: stbi_load()
+ // NOTE: Used to read image data (multiple formats support)
#define STB_IMAGE_RESIZE_IMPLEMENTATION
-#include "stb_image_resize.h"
+#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8()
+ // NOTE: Used for image scaling on ImageResize()
//----------------------------------------------------------------------------------
// Defines and Macros
@@ -130,6 +132,7 @@ Image LoadImage(const char *fileName)
}
// Load image data from Color array data (RGBA - 32bit)
+// NOTE: Creates a copy of pixels data array
Image LoadImageEx(Color *pixels, int width, int height)
{
Image image;
@@ -388,6 +391,14 @@ Texture2D LoadTextureFromImage(Image image)
return texture;
}
+// Load a texture to be used for rendering
+RenderTexture2D LoadRenderTexture(int width, int height)
+{
+ RenderTexture2D target = rlglLoadRenderTexture(width, height);
+
+ return target;
+}
+
// Unload image from CPU memory (RAM)
void UnloadImage(Image image)
{
@@ -408,6 +419,17 @@ void UnloadTexture(Texture2D texture)
}
}
+// Unload render texture from GPU memory
+void UnloadRenderTexture(RenderTexture2D target)
+{
+ if (target.id != 0)
+ {
+ rlDeleteRenderTextures(target);
+
+ TraceLog(INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id);
+ }
+}
+
// Get pixel data from image in the form of Color struct array
Color *GetImageData(Image image)
{
@@ -919,6 +941,39 @@ void ImageResize(Image *image, int newWidth, int newHeight)
free(pixels);
}
+// Resize and image to new size using Nearest-Neighbor scaling algorithm
+void ImageResizeNN(Image *image,int newWidth,int newHeight)
+{
+ Color *pixels = GetImageData(*image);
+ Color *output = (Color *)malloc(newWidth*newHeight*sizeof(Color));
+
+ // EDIT: added +1 to account for an early rounding problem
+ int x_ratio = (int)((image->width<<16)/newWidth) + 1;
+ int y_ratio = (int)((image->height<<16)/newHeight) + 1;
+
+ int x2, y2;
+ for (int i = 0; i < newHeight; i++)
+ {
+ for (int j = 0; j < newWidth; j++)
+ {
+ x2 = ((j*x_ratio) >> 16);
+ y2 = ((i*y_ratio) >> 16);
+
+ output[(i*newWidth) + j] = pixels[(y2*image->width) + x2] ;
+ }
+ }
+
+ int format = image->format;
+
+ UnloadImage(*image);
+
+ *image = LoadImageEx(output, newWidth, newHeight);
+ ImageFormat(image, format); // Reformat 32bit RGBA image to original format
+
+ free(output);
+ free(pixels);
+}
+
// Draw an image (source) within an image (destination)
void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
{
@@ -1046,8 +1101,9 @@ Image ImageTextEx(SpriteFont font, const char *text, int fontSize, int spacing,
float scaleFactor = (float)fontSize/imSize.y;
TraceLog(INFO, "Scalefactor: %f", scaleFactor);
- // TODO: Allow nearest-neighbor scaling algorithm
- ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
+ // Using nearest-neighbor scaling algorithm for default font
+ if (font.texture.id == GetDefaultFont().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
+ else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
}
free(pixels);
@@ -1056,6 +1112,25 @@ Image ImageTextEx(SpriteFont font, const char *text, int fontSize, int spacing,
return imText;
}
+// Draw text (default font) within an image (destination)
+void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color)
+{
+ ImageDrawTextEx(dst, position, GetDefaultFont(), text, fontSize, 0, color);
+}
+
+// Draw text (custom sprite font) within an image (destination)
+void ImageDrawTextEx(Image *dst, Vector2 position, SpriteFont font, const char *text, int fontSize, int spacing, Color color)
+{
+ Image imText = ImageTextEx(font, text, fontSize, spacing, color);
+
+ Rectangle srcRec = { 0, 0, imText.width, imText.height };
+ Rectangle dstRec = { (int)position.x, (int)position.y, imText.width, imText.height };
+
+ ImageDraw(dst, imText, srcRec, dstRec);
+
+ UnloadImage(imText);
+}
+
// Flip image vertically
void ImageFlipVertical(Image *image)
{
@@ -1321,36 +1396,43 @@ void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Co
// NOTE: origin is relative to destination rectangle size
void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint)
{
- rlEnableTexture(texture.id);
+ // Check if texture is valid
+ if (texture.id != 0)
+ {
+ if (sourceRec.width < 0) sourceRec.x -= sourceRec.width;
+ if (sourceRec.height < 0) sourceRec.y -= sourceRec.height;
+
+ rlEnableTexture(texture.id);
- rlPushMatrix();
- rlTranslatef(destRec.x, destRec.y, 0);
- rlRotatef(rotation, 0, 0, 1);
- rlTranslatef(-origin.x, -origin.y, 0);
+ rlPushMatrix();
+ rlTranslatef(destRec.x, destRec.y, 0);
+ rlRotatef(rotation, 0, 0, 1);
+ rlTranslatef(-origin.x, -origin.y, 0);
- rlBegin(RL_QUADS);
- rlColor4ub(tint.r, tint.g, tint.b, tint.a);
- rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
+ rlBegin(RL_QUADS);
+ rlColor4ub(tint.r, tint.g, tint.b, tint.a);
+ rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
- // Bottom-left corner for texture and quad
- rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
- rlVertex2f(0.0f, 0.0f);
+ // Bottom-left corner for texture and quad
+ rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
+ rlVertex2f(0.0f, 0.0f);
- // Bottom-right corner for texture and quad
- rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
- rlVertex2f(0.0f, destRec.height);
+ // Bottom-right corner for texture and quad
+ rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
+ rlVertex2f(0.0f, destRec.height);
- // Top-right corner for texture and quad
- rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
- rlVertex2f(destRec.width, destRec.height);
+ // Top-right corner for texture and quad
+ rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
+ rlVertex2f(destRec.width, destRec.height);
- // Top-left corner for texture and quad
- rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
- rlVertex2f(destRec.width, 0.0f);
- rlEnd();
- rlPopMatrix();
+ // Top-left corner for texture and quad
+ rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
+ rlVertex2f(destRec.width, 0.0f);
+ rlEnd();
+ rlPopMatrix();
- rlDisableTexture();
+ rlDisableTexture();
+ }
}
//----------------------------------------------------------------------------------
diff --git a/src/utils.c b/src/utils.c
index 974088f3..5340b3e5 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -35,17 +35,18 @@
#include <android/asset_manager.h>
#endif
-#include <stdlib.h> // malloc(), free()
-#include <stdio.h> // printf(), fprintf()
-#include <stdarg.h> // Used for functions with variable number of parameters (TraceLog())
-//#include <string.h> // String management functions: strlen(), strrchr(), strcmp()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <stdio.h> // Required for: fopen(), fclose(), fputc(), fwrite(), printf(), fprintf(), funopen()
+#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
+//#include <string.h> // Required for: strlen(), strrchr(), strcmp()
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
#define STB_IMAGE_WRITE_IMPLEMENTATION
- #include "stb_image_write.h" // Create PNG file
+ #include "external/stb_image_write.h" // Required for: stbi_write_png()
#endif
-#include "tinfl.c"
+#include "external/tinfl.c" // Required for: tinfl_decompress_mem_to_mem()
+ // NOTE: Deflate algorythm data decompression
//----------------------------------------------------------------------------------
// Global Variables Definition
@@ -247,7 +248,7 @@ FILE *android_fopen(const char *fileName, const char *mode)
AAsset *asset = AAssetManager_open(assetManager, fileName, 0);
- if(!asset) return NULL;
+ if (!asset) return NULL;
return funopen(asset, android_read, android_write, android_seek, android_close);
}
diff --git a/src/utils.h b/src/utils.h
index 77909ba6..899cf583 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -27,8 +27,8 @@
#define UTILS_H
#if defined(PLATFORM_ANDROID)
- #include <stdio.h> // Defines FILE struct
- #include <android/asset_manager.h> // defines AAssetManager struct
+ #include <stdio.h> // Required for: FILE
+ #include <android/asset_manager.h> // Required for: AAssetManager
#endif
//----------------------------------------------------------------------------------