aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile51
-rw-r--r--src/android/jni/Android.mk3
-rw-r--r--src/audio.c1386
-rw-r--r--src/audio.h68
-rw-r--r--src/camera.c523
-rw-r--r--src/camera.h484
-rw-r--r--src/core.c577
-rw-r--r--src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h151
-rw-r--r--src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h4
-rw-r--r--src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h3
-rw-r--r--src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h3
-rw-r--r--src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h9
-rw-r--r--src/external/OculusSDK/LibOVR/Include/OVR_Version.h2
-rw-r--r--src/external/OculusSDK/LibOVR/LibOVRRT32_1.dllbin1023952 -> 1044944 bytes
-rw-r--r--src/external/dr_flac.h4395
-rw-r--r--src/external/jar_mod.h6
-rw-r--r--src/external/jar_xm.h62
-rw-r--r--src/external/lua/include/lauxlib.h256
-rw-r--r--src/external/lua/include/lua.h486
-rw-r--r--src/external/lua/include/lua.hpp9
-rw-r--r--src/external/lua/include/luaconf.h769
-rw-r--r--src/external/lua/include/lualib.h58
-rw-r--r--src/external/lua/lib/liblua53.abin0 -> 322424 bytes
-rw-r--r--src/external/lua/lib/liblua53dll.abin0 -> 91416 bytes
-rw-r--r--src/external/openal_soft/include/AL/alext.h5
-rw-r--r--src/external/openal_soft/lib/win32/libOpenAL32.abin0 -> 2226842 bytes
-rw-r--r--src/external/pthread/lib/pthreadGC2.dllbin0 -> 119888 bytes
-rw-r--r--src/gestures.c423
-rw-r--r--src/gestures.h480
-rw-r--r--src/models.c551
-rw-r--r--src/raygui.h1499
-rw-r--r--src/raylib.h764
-rw-r--r--src/rlgl.c300
-rw-r--r--src/rlgl.h38
-rw-r--r--src/rlua.h4289
-rw-r--r--src/shader_standard.h10
-rw-r--r--src/shapes.c20
-rw-r--r--src/text.c244
-rw-r--r--src/textures.c529
39 files changed, 13538 insertions, 4919 deletions
diff --git a/src/Makefile b/src/Makefile
index c41fe2f5..ee3f0c12 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -35,7 +35,7 @@
# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB
PLATFORM ?= PLATFORM_DESKTOP
-# define if you want shared or static version of library.
+# define YES if you want shared/dynamic version of library instead of static (default)
SHARED ?= NO
# determine if the file has root access (only for installing raylib)
@@ -95,8 +95,13 @@ endif
# -Wno-missing-braces ignore invalid warning (GCC bug 53119)
CFLAGS = -O1 -Wall -std=gnu99 -fgnu89-inline -Wno-missing-braces
+# if shared library required, make sure code is compiled as position independent
ifeq ($(SHARED),YES)
CFLAGS += -fPIC
+ SHAREDFLAG = BUILDING_DLL
+ SHAREDLIBS = -Lexternal/glfw3/lib/win32 -Lexternal/openal_soft/lib/win32 -lglfw3 -lopenal32 -lgdi32
+else
+ SHAREDFLAG = BUILDING_STATIC
endif
#CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes
@@ -152,12 +157,16 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
@echo "libraylib.bc generated (web version)!"
else
ifeq ($(SHARED),YES)
- ifeq ($(PLATFORM_OS),LINUX)
- # compile raylib to shared library version for GNU/Linux.
- # WARNING: you should type "make clean" before doing this target
- $(CC) -shared -o $(OUTPUT_PATH)/libraylib.so $(OBJS)
- @echo "libraylib.so generated (shared library)!"
- endif
+ ifeq ($(PLATFORM_OS),LINUX)
+ # compile raylib to shared library version for GNU/Linux.
+ # WARNING: you should type "make clean" before doing this target
+ $(CC) -shared -o $(OUTPUT_PATH)/libraylib.so $(OBJS)
+ @echo "raylib shared library (libraylib.so) generated!"
+ endif
+ ifeq ($(PLATFORM_OS),WINDOWS)
+ $(CC) -shared -o $(OUTPUT_PATH)/raylib.dll $(OBJS) $(SHAREDLIBS) -Wl,--out-implib,$(OUTPUT_PATH)/libraylibdll.a
+ @echo "raylib dynamic library (raylib.dll) and MSVC required import library (libraylibdll.a) generated!"
+ endif
else
# compile raylib static library for desktop platforms.
ar rcs $(OUTPUT_PATH)/libraylib.a $(OBJS)
@@ -168,8 +177,8 @@ endif
# compile all modules with their prerequisites
# compile core module
-core.o : core.c raylib.h rlgl.h utils.h raymath.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM)
+core.o : core.c raylib.h rlgl.h utils.h raymath.h gestures.h
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(SHAREDFLAG)
# compile rlgl module
rlgl.o : rlgl.c rlgl.h raymath.h
@@ -177,23 +186,23 @@ rlgl.o : rlgl.c rlgl.h raymath.h
# compile shapes module
shapes.o : shapes.c raylib.h rlgl.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES)
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(SHAREDFLAG)
# compile textures module
textures.o : textures.c rlgl.h utils.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS)
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(GRAPHICS) -D$(SHAREDFLAG)
# compile text module
text.o : text.c raylib.h utils.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES)
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(SHAREDFLAG)
# compile models module
models.o : models.c raylib.h rlgl.h raymath.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM)
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(SHAREDFLAG)
# compile audio module
audio.o : audio.c raylib.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM)
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(SHAREDFLAG)
# compile stb_vorbis library
external/stb_vorbis.o: external/stb_vorbis.c external/stb_vorbis.h
@@ -201,15 +210,7 @@ external/stb_vorbis.o: external/stb_vorbis.c external/stb_vorbis.h
# compile utils module
utils.o : utils.c utils.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM)
-
-# compile camera module
-camera.o : camera.c raylib.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES)
-
-#compile gestures module
-gestures.o : gestures.c raylib.h
- $(CC) -c $< $(CFLAGS) $(INCLUDES)
+ $(CC) -c $< $(CFLAGS) $(INCLUDES) -D$(PLATFORM) -D$(SHAREDFLAG)
# It installs generated and needed files to compile projects using raylib.
# The installation works manually.
@@ -222,10 +223,10 @@ ifeq ($(ROOT),root)
# /usr/local/include/) are for libraries that are installed
# manually (without a package manager).
ifeq ($(SHARED),YES)
- cp --update libraylib.so /usr/local/lib/libraylib.so
+ cp --update $(OUTPUT_PATH)/libraylib.so /usr/local/lib/libraylib.so
else
cp --update raylib.h /usr/local/include/raylib.h
- cp --update libraylib.a /usr/local/lib/libraylib.a
+ cp --update $(OUTPUT_PATH)/libraylib.a /usr/local/lib/libraylib.a
endif
@echo "raylib dev files installed/updated!"
else
diff --git a/src/android/jni/Android.mk b/src/android/jni/Android.mk
index 66851d08..687c6577 100644
--- a/src/android/jni/Android.mk
+++ b/src/android/jni/Android.mk
@@ -4,7 +4,7 @@
#
# Static library compilation
#
-# Copyright (c) 2014 Ramon Santamaria (Ray San - raysan@raysanweb.com)
+# Copyright (c) 2014-2016 Ramon Santamaria (@raysan5)
#
# 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.
@@ -42,7 +42,6 @@ LOCAL_SRC_FILES :=\
../../textures.c \
../../text.c \
../../shapes.c \
- ../../gestures.c \
../../models.c \
../../utils.c \
../../audio.c \
diff --git a/src/audio.c b/src/audio.c
index 38fefd12..9d2eeb47 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -2,7 +2,7 @@
*
* raylib.audio
*
-* Basic functions to manage Audio:
+* Basic functions to manage Audio:
* Manage audio device (init/close)
* Load and Unload audio files
* Play/Stop/Pause/Resume loaded audio
@@ -55,6 +55,7 @@
#include <string.h> // Required for: strcmp(), strncmp()
#include <stdio.h> // Required for: FILE, fopen(), fclose(), fread()
+// Tokens defined by OpenAL extension: AL_EXT_float32
#ifndef AL_FORMAT_MONO_FLOAT32
#define AL_FORMAT_MONO_FLOAT32 0x10010
#endif
@@ -78,75 +79,45 @@
#define JAR_MOD_IMPLEMENTATION
#include "external/jar_mod.h" // MOD loading functions
+#define DR_FLAC_IMPLEMENTATION
+#define DR_FLAC_NO_WIN32_IO
+#include "external/dr_flac.h" // FLAC loading functions
+
+#ifdef _MSC_VER
+ #undef bool
+#endif
+
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define MAX_STREAM_BUFFERS 2 // Number of buffers for each source
-#define MAX_MUSIC_STREAMS 2 // Number of simultanious music sources
-#define MAX_MIX_CHANNELS 4 // Number of mix channels (OpenAL 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_SHORT 4096*8 // PCM data buffer (short) - 64Kb
- #define MUSIC_BUFFER_SIZE_FLOAT 4096*4 // PCM data buffer (float) - 64Kb
-#endif
+#define MAX_STREAM_BUFFERS 2 // Number of buffers for each audio stream
+
+// NOTE: Music buffer size is defined by number of samples, independent of sample size
+// After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds
+// and double-buffering system, I concluded that a 4096 samples buffer should be enough
+// In case of music-stalls, just increase this number
+#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. short: 32Kb)
//----------------------------------------------------------------------------------
// 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 {
- 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;
+typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_AUDIO_FLAC, MUSIC_MODULE_XM, MUSIC_MODULE_MOD } MusicContextType;
// Music type (file streaming from memory)
-// NOTE: Anything longer than ~10 seconds should be streamed into a mix channel...
-typedef struct Music {
- stb_vorbis *stream;
- jar_xm_context_t *xmctx; // XM chiptune context
- jar_mod_context_t modctx; // MOD chiptune context
- MixChannel *mixc; // Mix channel
-
- unsigned int totalSamplesLeft;
- float totalLengthSeconds;
- bool loop;
- bool chipTune; // chiptune is loaded?
- bool enabled;
-} 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;
+typedef struct MusicData {
+ MusicContextType ctxType; // Type of music context (OGG, XM, MOD)
+ stb_vorbis *ctxOgg; // OGG audio context
+ drflac *ctxFlac; // FLAC audio context
+ jar_xm_context_t *ctxXm; // XM chiptune context
+ jar_mod_context_t ctxMod; // MOD chiptune context
+
+ AudioStream stream; // Audio stream (double buffering)
+
+ bool loop; // Repeat music after finish (loop)
+ unsigned int totalSamples; // Total number of samples
+ unsigned int samplesLeft; // Number of samples left to end
+} MusicData, *Music;
#if defined(AUDIO_STANDALONE)
typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
@@ -155,26 +126,14 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
-static Music musicStreams[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time
-static MixChannel *mixChannels[MAX_MIX_CHANNELS]; // Mix channels currently active (from music streams)
-
-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 bool BufferMusicStream(int index, int numBuffers); // Fill music buffers with data
-static void EmptyMusicStream(int index); // Empty music buffers
-
-static MixChannel *InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint);
-static void CloseMixChannel(MixChannel *mixc); // Frees mix channel
-static int BufferMixChannel(MixChannel *mixc, void *data, int numberElements); // Pushes more audio data into mix channel
-//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 Wave LoadWAV(const char *fileName); // Load WAV file
+static Wave LoadOGG(const char *fileName); // Load OGG file
+static Wave LoadFLAC(const char *fileName); // Load FLAC file
#if defined(AUDIO_STANDALONE)
const char *GetExtension(const char *fileName); // Get the extension for a filename
@@ -185,362 +144,121 @@ void TraceLog(int msgType, const char *text, ...); // Outputs a trace log messa
// Module Functions Definition - Audio Device initialization and Closing
//----------------------------------------------------------------------------------
-// Initialize audio device and mixc
+// Initialize audio device
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");
-
- ALCcontext *context = alcCreateContext(device, NULL);
-
- if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE))
+ else
{
- if (context != NULL) alcDestroyContext(context);
+ ALCcontext *context = alcCreateContext(device, NULL);
- alcCloseDevice(device);
+ if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE))
+ {
+ if (context != NULL) alcDestroyContext(context);
- TraceLog(ERROR, "Could not setup mix channel");
- }
+ alcCloseDevice(device);
- TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER));
+ TraceLog(ERROR, "Could not initialize audio context");
+ }
+ else
+ {
+ TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER));
- // Listener definition (just for 2D)
- alListener3f(AL_POSITION, 0, 0, 0);
- alListener3f(AL_VELOCITY, 0, 0, 0);
- alListener3f(AL_ORIENTATION, 0, 0, -1);
+ // Listener definition (just for 2D)
+ alListener3f(AL_POSITION, 0, 0, 0);
+ alListener3f(AL_VELOCITY, 0, 0, 0);
+ alListener3f(AL_ORIENTATION, 0, 0, -1);
+ }
+ }
}
// Close the audio device for all contexts
void CloseAudioDevice(void)
{
- for (int index = 0; index < MAX_MUSIC_STREAMS; index++)
- {
- if (musicStreams[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 mix channel for closing");
+ if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing");
device = alcGetContextsDevice(context);
alcMakeContextCurrent(NULL);
alcDestroyContext(context);
alcCloseDevice(device);
+
+ TraceLog(INFO, "Audio device closed successfully");
}
// Check if device has been initialized successfully
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
+// Module Functions Definition - Sounds loading and playing (.WAV)
//----------------------------------------------------------------------------------
-// Init mix channel for streaming
-// 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.
-static MixChannel *InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint)
+// Load wave data from file into RAM
+Wave LoadWave(const char *fileName)
{
- if (mixChannel >= MAX_MIX_CHANNELS) return NULL;
- if (!IsAudioDeviceReady()) InitAudioDevice();
-
- if (!mixChannels[mixChannel])
- {
- MixChannel *mixc = (MixChannel *)malloc(sizeof(MixChannel));
- mixc->sampleRate = sampleRate;
- mixc->channels = channels;
- mixc->mixChannel = mixChannel;
- mixc->floatingPoint = floatingPoint;
- mixChannels[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++)
- {
- // Initialize buffer with zeros by default
- if (mixc->floatingPoint)
- {
- float pcm[MUSIC_BUFFER_SIZE_FLOAT] = { 0.0f };
- alBufferData(mixc->alBuffer[i], mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_FLOAT*sizeof(float), mixc->sampleRate);
- }
- else
- {
- short pcm[MUSIC_BUFFER_SIZE_SHORT] = { 0 };
- alBufferData(mixc->alBuffer[i], mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_SHORT*sizeof(short), mixc->sampleRate);
- }
- }
-
- alSourceQueueBuffers(mixc->alSource, MAX_STREAM_BUFFERS, mixc->alBuffer);
- mixc->playing = true;
- alSourcePlay(mixc->alSource);
-
- return mixc;
- }
-
- return NULL;
-}
+ Wave wave = { 0 };
-// Frees buffer in mix channel
-static void CloseMixChannel(MixChannel *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[mixc->mixChannel] = NULL;
- free(mixc);
- mixc = NULL;
- }
+ if (strcmp(GetExtension(fileName), "wav") == 0) wave = LoadWAV(fileName);
+ else if (strcmp(GetExtension(fileName), "ogg") == 0) wave = LoadOGG(fileName);
+ else if (strcmp(GetExtension(fileName), "flac") == 0) wave = LoadFLAC(fileName);
+ else TraceLog(WARNING, "[%s] File extension not recognized, it can't be loaded", fileName);
+
+ return wave;
}
-// Pushes more audio data into 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 *mixc, void *data, int numberElements)
+// Load wave data from float array data (32bit)
+Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels)
{
- if (!mixc || (mixChannels[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;
+ Wave wave;
- 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);
- }
+ wave.data = data;
+ wave.sampleCount = sampleCount;
+ wave.sampleRate = sampleRate;
+ wave.sampleSize = 32;
+ wave.channels = channels;
- alSourceQueueBuffers(mixc->alSource, 1, &buffer);
+ // NOTE: Copy wave data to work with,
+ // user is responsible of input data to free
+ Wave cwave = WaveCopy(wave);
- return numberElements;
-}
-
-/*
-// Convert data from short to float
-// 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;
- }
-}
-
-// Convert data from float to short
-// 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;
- }
-}
-*/
-
-// Initialize raw audio mix channel for audio buffering
-// NOTE: Returns mix channel index or -1 if it fails (errors are registered on lastAudioError)
-int InitRawMixChannel(int sampleRate, int channels, bool floatingPoint)
-{
- int mixIndex;
-
- for (mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot
- {
- if (mixChannels[mixIndex] == NULL) break;
- else if (mixIndex == (MAX_MIX_CHANNELS - 1))
- {
- lastAudioError = ERROR_OUT_OF_MIX_CHANNELS;
- return -1;
- }
- }
+ WaveFormat(&cwave, sampleRate, sampleSize, channels);
- if (InitMixChannel(sampleRate, mixIndex, channels, floatingPoint)) return mixIndex;
- else
- {
- lastAudioError = ERROR_RAW_CONTEXT_CREATION;
- return -1;
- }
+ return cwave;
}
-// Buffers data directly to raw mix channel
-// if 0 is returned, buffers are still full and you need to keep trying with the same data
-// otherwise it will return number of samples buffered.
-// NOTE: Data could be either be an array of floats or shorts, depending on the created context
-int BufferRawAudioContext(int ctx, void *data, unsigned short numberElements)
+// Load sound to memory
+// NOTE: The entire file is loaded to memory to be played (no-streaming)
+Sound LoadSound(const char *fileName)
{
- int numBuffered = 0;
+ Wave wave = LoadWave(fileName);
- if (ctx >= 0)
- {
- MixChannel *mixc = mixChannels[ctx];
- numBuffered = BufferMixChannel(mixc, data, numberElements);
- }
+ Sound sound = LoadSoundFromWave(wave);
- return numBuffered;
-}
-
-// Closes and frees raw mix channel
-void CloseRawAudioContext(int ctx)
-{
- if (mixChannels[ctx]) CloseMixChannel(mixChannels[ctx]);
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition - Sounds loading and playing (.WAV)
-//----------------------------------------------------------------------------------
-
-// Load sound to memory
-Sound LoadSound(char *fileName)
-{
- Sound sound = { 0 };
- Wave wave = { 0 };
-
- // NOTE: The entire file is loaded to memory to play it all at once (no-streaming)
-
- // Audio file loading
- // NOTE: Buffer space is allocated inside function, Wave must be freed
-
- 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);
-
- // TODO: Find a better way to register errors (similar to glGetError())
- lastAudioError = ERROR_EXTENSION_NOT_RECOGNIZED;
- }
-
- if (wave.data != NULL)
- {
- ALenum format = 0;
- // The OpenAL format is worked out by looking at the number of channels and the bits per sample
- if (wave.channels == 1)
- {
- if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
- else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
- }
- else if (wave.channels == 2)
- {
- if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
- else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
- }
-
- // Create an audio source
- ALuint source;
- alGenSources(1, &source); // Generate pointer to audio source
-
- alSourcef(source, AL_PITCH, 1);
- alSourcef(source, AL_GAIN, 1);
- alSource3f(source, AL_POSITION, 0, 0, 0);
- alSource3f(source, AL_VELOCITY, 0, 0, 0);
- alSourcei(source, AL_LOOPING, AL_FALSE);
-
- // Convert loaded data to OpenAL buffer
- //----------------------------------------
- ALuint buffer;
- alGenBuffers(1, &buffer); // Generate pointer to buffer
-
- // Upload sound data to buffer
- alBufferData(buffer, format, wave.data, wave.dataSize, wave.sampleRate);
-
- // Attach sound buffer to source
- alSourcei(source, AL_BUFFER, buffer);
-
- TraceLog(INFO, "[%s] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels);
-
- // Unallocate WAV data
- UnloadWave(wave);
-
- sound.source = source;
- sound.buffer = buffer;
- }
+ UnloadWave(wave); // Sound is loaded, we can unload wave
return sound;
}
// Load sound from wave data
+// NOTE: Wave data must be unallocated manually
Sound LoadSoundFromWave(Wave wave)
{
Sound sound = { 0 };
@@ -548,17 +266,29 @@ Sound LoadSoundFromWave(Wave wave)
if (wave.data != NULL)
{
ALenum format = 0;
- // The OpenAL format is worked out by looking at the number of channels and the bits per sample
+
+ // The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample)
if (wave.channels == 1)
{
- if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
- else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
+ switch (wave.sampleSize)
+ {
+ case 8: format = AL_FORMAT_MONO8; break;
+ case 16: format = AL_FORMAT_MONO16; break;
+ case 32: format = AL_FORMAT_MONO_FLOAT32; break;
+ default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break;
+ }
}
else if (wave.channels == 2)
{
- if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
- else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
+ switch (wave.sampleSize)
+ {
+ case 8: format = AL_FORMAT_STEREO8; break;
+ case 16: format = AL_FORMAT_STEREO16; break;
+ case 32: format = AL_FORMAT_STEREO_FLOAT32; break;
+ default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break;
+ }
}
+ else TraceLog(WARNING, "Wave number of channels not supported: %i", wave.channels);
// Create an audio source
ALuint source;
@@ -575,19 +305,19 @@ Sound LoadSoundFromWave(Wave wave)
ALuint buffer;
alGenBuffers(1, &buffer); // Generate pointer to buffer
+ unsigned int dataSize = wave.sampleCount*wave.sampleSize/8; // Size in bytes
+
// Upload sound data to buffer
- alBufferData(buffer, format, wave.data, wave.dataSize, wave.sampleRate);
+ alBufferData(buffer, format, wave.data, dataSize, wave.sampleRate);
// Attach sound buffer to source
alSourcei(source, AL_BUFFER, buffer);
- // Unallocate WAV data
- UnloadWave(wave);
-
- TraceLog(INFO, "[Wave] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", wave.sampleRate, wave.bitsPerSample, wave.channels);
+ TraceLog(INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", source, buffer, wave.sampleRate, wave.sampleSize, wave.channels);
sound.source = source;
sound.buffer = buffer;
+ sound.format = format;
}
return sound;
@@ -602,9 +332,9 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
#if defined(AUDIO_STANDALONE)
TraceLog(WARNING, "Sound loading from rRES resource file not supported on standalone mode");
#else
-
+
bool found = false;
-
+
char id[4]; // rRES file identifier
unsigned char version; // rRES file version and subversion
char useless; // rRES header reserved data
@@ -614,11 +344,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
FILE *rresFile = fopen(rresName, "rb");
- if (rresFile == NULL)
- {
- TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
- lastAudioError = ERROR_UNABLE_TO_OPEN_RRES_FILE;
- }
+ if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName);
else
{
// Read rres file (basic file check - id)
@@ -632,7 +358,6 @@ 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
{
@@ -664,8 +389,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
fread(&reserved, 1, 1, rresFile); // <reserved>
wave.sampleRate = sampleRate;
- wave.dataSize = infoHeader.srcSize;
- wave.bitsPerSample = bps;
+ wave.sampleSize = bps;
wave.channels = (short)channels;
unsigned char *data = malloc(infoHeader.size);
@@ -676,55 +400,12 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
free(data);
- // Convert wave to Sound (OpenAL)
- ALenum format = 0;
-
- // The OpenAL format is worked out by looking at the number of channels and the bits per sample
- if (wave.channels == 1)
- {
- if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
- else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
- }
- else if (wave.channels == 2)
- {
- if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
- else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
- }
-
- // Create an audio source
- ALuint source;
- alGenSources(1, &source); // Generate pointer to audio source
-
- alSourcef(source, AL_PITCH, 1);
- alSourcef(source, AL_GAIN, 1);
- alSource3f(source, AL_POSITION, 0, 0, 0);
- alSource3f(source, AL_VELOCITY, 0, 0, 0);
- alSourcei(source, AL_LOOPING, AL_FALSE);
-
- // Convert loaded data to OpenAL buffer
- //----------------------------------------
- ALuint buffer;
- alGenBuffers(1, &buffer); // Generate pointer to buffer
-
- // Upload sound data to buffer
- alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate);
-
- // Attach sound buffer to source
- alSourcei(source, AL_BUFFER, buffer);
-
- TraceLog(INFO, "[%s] Sound loaded successfully from resource (SampleRate: %i, BitRate: %i, Channels: %i)", rresName, wave.sampleRate, wave.bitsPerSample, wave.channels);
-
- // Unallocate WAV data
- UnloadWave(wave);
+ sound = LoadSoundFromWave(wave);
- sound.source = source;
- sound.buffer = buffer;
- }
- else
- {
- TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName);
- lastAudioError = ERROR_INVALID_RRES_RESOURCE;
+ // Sound is loaded, we can unload wave data
+ UnloadWave(wave);
}
+ else TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName);
}
else
{
@@ -753,13 +434,48 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
return sound;
}
+// Unload Wave data
+void UnloadWave(Wave wave)
+{
+ free(wave.data);
+
+ TraceLog(INFO, "Unloaded wave data from RAM");
+}
+
// Unload sound
void UnloadSound(Sound sound)
{
alDeleteSources(1, &sound.source);
alDeleteBuffers(1, &sound.buffer);
+
+ TraceLog(INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer);
+}
+
+// Update sound buffer with new data
+// NOTE: data must match sound.format
+void UpdateSound(Sound sound, void *data, int numSamples)
+{
+ ALint sampleRate, sampleSize, channels;
+ alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate);
+ alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format
+ alGetBufferi(sound.buffer, AL_CHANNELS, &channels); // It could also be retrieved from sound.format
- TraceLog(INFO, "Unloaded sound data");
+ TraceLog(DEBUG, "UpdateSound() : AL_FREQUENCY: %i", sampleRate);
+ TraceLog(DEBUG, "UpdateSound() : AL_BITS: %i", sampleSize);
+ TraceLog(DEBUG, "UpdateSound() : AL_CHANNELS: %i", channels);
+
+ unsigned int dataSize = numSamples*sampleSize/8; // Size of data in bytes
+
+ alSourceStop(sound.source); // Stop sound
+ alSourcei(sound.source, AL_BUFFER, 0); // Unbind buffer from sound to update
+ //alDeleteBuffers(1, &sound.buffer); // Delete current buffer data
+ //alGenBuffers(1, &sound.buffer); // Generate new buffer
+
+ // Upload new data to sound buffer
+ alBufferData(sound.buffer, sound.format, data, dataSize, sampleRate);
+
+ // Attach sound buffer to source again
+ alSourcei(sound.source, AL_BUFFER, sound.buffer);
}
// Play a sound
@@ -777,7 +493,7 @@ void PlaySound(Sound sound)
//int sampleRate;
//alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); // AL_CHANNELS, AL_BITS (bps)
- //float seconds = (float)byteOffset / sampleRate; // Number of seconds since the beginning of the sound
+ //float seconds = (float)byteOffset/sampleRate; // Number of seconds since the beginning of the sound
//or
//float result;
//alGetSourcef(sound.source, AL_SEC_OFFSET, &result); // AL_SAMPLE_OFFSET
@@ -789,6 +505,16 @@ void PauseSound(Sound sound)
alSourcePause(sound.source);
}
+// Resume a paused sound
+void ResumeSound(Sound sound)
+{
+ ALenum state;
+
+ alGetSourcei(sound.source, AL_SOURCE_STATE, &state);
+
+ if (state == AL_PAUSED) alSourcePlay(sound.source);
+}
+
// Stop reproducing a sound
void StopSound(Sound sound)
{
@@ -819,396 +545,571 @@ void SetSoundPitch(Sound sound, float pitch)
alSourcef(sound.source, AL_PITCH, pitch);
}
-//----------------------------------------------------------------------------------
-// Module Functions Definition - Music loading and stream playing (.OGG)
-//----------------------------------------------------------------------------------
-
-// Start music playing (open stream)
-// returns 0 on success or error code
-int PlayMusicStream(int index, char *fileName)
+// Convert wave data to desired format
+// TODO: Consider channels (mono - stereo)
+void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
{
- int mixIndex;
-
- if (musicStreams[index].stream || musicStreams[index].xmctx) return ERROR_UNINITIALIZED_CHANNELS; // error
-
- for (mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot
- {
- if (mixChannels[mixIndex] == NULL) break;
- else if (mixIndex == (MAX_MIX_CHANNELS - 1)) return ERROR_OUT_OF_MIX_CHANNELS; // error
- }
-
- if (strcmp(GetExtension(fileName),"ogg") == 0)
+ if (wave->sampleSize != sampleSize)
{
- // Open audio stream
- musicStreams[index].stream = stb_vorbis_open_filename(fileName, NULL, NULL);
+ float *samples = GetWaveData(*wave); //Color *pixels = GetImageData(*image);
- if (musicStreams[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(musicStreams[index].stream);
+ free(wave->data);
+
+ wave->sampleSize = sampleSize;
- 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);
+ //sample *= 4.0f; // Arbitrary gain to get reasonable output volume...
+ //if (sample > 1.0f) sample = 1.0f;
+ //if (sample < -1.0f) sample = -1.0f;
- musicStreams[index].loop = true; // We loop by default
- musicStreams[index].enabled = true;
-
+ if (sampleSize == 8)
+ {
+ wave->data = (unsigned char *)malloc(wave->sampleCount*sizeof(unsigned char));
- musicStreams[index].totalSamplesLeft = (unsigned int)stb_vorbis_stream_length_in_samples(musicStreams[index].stream) * info.channels;
- musicStreams[index].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(musicStreams[index].stream);
-
- if (info.channels == 2)
+ for (int i = 0; i < wave->sampleCount; i++)
{
- musicStreams[index].mixc = InitMixChannel(info.sample_rate, mixIndex, 2, false);
- musicStreams[index].mixc->playing = true;
+ ((unsigned char *)wave->data)[i] = (unsigned char)((float)samples[i]*127 + 128);
}
- else
- {
- musicStreams[index].mixc = InitMixChannel(info.sample_rate, mixIndex, 1, false);
- musicStreams[index].mixc->playing = true;
- }
-
- if (!musicStreams[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(&musicStreams[index].xmctx, 48000, fileName))
+ else if (sampleSize == 16)
{
- musicStreams[index].chipTune = true;
- musicStreams[index].loop = true;
- jar_xm_set_max_loop_count(musicStreams[index].xmctx, 0); // infinite number of loops
- musicStreams[index].totalSamplesLeft = (unsigned int)jar_xm_get_remaining_samples(musicStreams[index].xmctx);
- musicStreams[index].totalLengthSeconds = ((float)musicStreams[index].totalSamplesLeft)/48000.0f;
- musicStreams[index].enabled = true;
-
- TraceLog(INFO, "[%s] XM number of samples: %i", fileName, musicStreams[index].totalSamplesLeft);
- TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, musicStreams[index].totalLengthSeconds);
-
- musicStreams[index].mixc = InitMixChannel(48000, mixIndex, 2, true);
+ wave->data = (short *)malloc(wave->sampleCount*sizeof(short));
- if (!musicStreams[index].mixc) return ERROR_XM_CONTEXT_CREATION; // error
-
- musicStreams[index].mixc->playing = true;
+ for (int i = 0; i < wave->sampleCount; i++)
+ {
+ ((short *)wave->data)[i] = (short)((float)samples[i]*32000); // SHRT_MAX = 32767
+ }
}
- else
+ else if (sampleSize == 32)
{
- TraceLog(WARNING, "[%s] XM file could not be opened", fileName);
- return ERROR_LOADING_XM; // error
+ wave->data = (float *)malloc(wave->sampleCount*sizeof(float));
+
+ for (int i = 0; i < wave->sampleCount; i++)
+ {
+ ((float *)wave->data)[i] = (float)samples[i];
+ }
}
+ else TraceLog(WARNING, "Wave formatting: Sample size not supported");
+
+ free(samples);
}
- else if (strcmp(GetExtension(fileName),"mod") == 0)
+
+ // NOTE: Only supported 1 or 2 channels (mono or stereo)
+ if ((channels > 0) && (channels < 3) && (wave->channels != channels))
{
- jar_mod_init(&musicStreams[index].modctx);
-
- if (jar_mod_load_file(&musicStreams[index].modctx, fileName))
- {
- musicStreams[index].chipTune = true;
- musicStreams[index].loop = true;
- musicStreams[index].totalSamplesLeft = (unsigned int)jar_mod_max_samples(&musicStreams[index].modctx);
- musicStreams[index].totalLengthSeconds = ((float)musicStreams[index].totalSamplesLeft)/48000.0f;
- musicStreams[index].enabled = true;
-
- TraceLog(INFO, "[%s] MOD number of samples: %i", fileName, musicStreams[index].totalSamplesLeft);
- TraceLog(INFO, "[%s] MOD track length: %11.6f sec", fileName, musicStreams[index].totalLengthSeconds);
-
- musicStreams[index].mixc = InitMixChannel(48000, mixIndex, 2, false);
-
- if (!musicStreams[index].mixc) return ERROR_MOD_CONTEXT_CREATION; // error
-
- musicStreams[index].mixc->playing = true;
- }
- else
- {
- TraceLog(WARNING, "[%s] MOD file could not be opened", fileName);
- return ERROR_LOADING_MOD; // error
- }
+ // TODO: Add/remove channels interlaced data if required...
}
- else
+}
+
+// Copy a wave to a new wave
+Wave WaveCopy(Wave wave)
+{
+ Wave newWave;
+
+ if (wave.sampleSize == 8) newWave.data = (unsigned char *)malloc(wave.sampleCount*wave.channels*sizeof(unsigned char));
+ else if (wave.sampleSize == 16) newWave.data = (short *)malloc(wave.sampleCount*wave.channels*sizeof(short));
+ else if (wave.sampleSize == 32) newWave.data = (float *)malloc(wave.sampleCount*wave.channels*sizeof(float));
+ else TraceLog(WARNING, "Wave sample size not supported for copy");
+
+ if (newWave.data != NULL)
{
- TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
- return ERROR_EXTENSION_NOT_RECOGNIZED; // error
+ // NOTE: Size must be provided in bytes
+ memcpy(newWave.data, wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8);
+
+ newWave.sampleCount = wave.sampleCount;
+ newWave.sampleRate = wave.sampleRate;
+ newWave.sampleSize = wave.sampleSize;
+ newWave.channels = wave.channels;
}
-
- return 0; // normal return
+
+ return newWave;
}
-// Stop music playing for individual music index of musicStreams array (close stream)
-void StopMusicStream(int index)
+// Crop a wave to defined samples range
+// NOTE: Security check in case of out-of-range
+void WaveCrop(Wave *wave, int initSample, int finalSample)
{
- if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc)
+ if ((initSample >= 0) && (initSample < finalSample) &&
+ (finalSample > 0) && (finalSample < wave->sampleCount))
{
- CloseMixChannel(musicStreams[index].mixc);
-
- if (musicStreams[index].xmctx)
- jar_xm_free_context(musicStreams[index].xmctx);
- else if (musicStreams[index].modctx.mod_loaded)
- jar_mod_unload(&musicStreams[index].modctx);
- else
- stb_vorbis_close(musicStreams[index].stream);
+ // TODO: Review cropping (it could be simplified...)
- musicStreams[index].enabled = false;
+ float *samples = GetWaveData(*wave);
+ float *cropSamples = (float *)malloc((finalSample - initSample)*sizeof(float));
- if (musicStreams[index].stream || musicStreams[index].xmctx)
- {
- musicStreams[index].stream = NULL;
- musicStreams[index].xmctx = NULL;
- }
+ for (int i = initSample; i < finalSample; i++) cropSamples[i] = samples[i];
+
+ free(wave->data);
+ wave->data = cropSamples;
+ int sampleSize = wave->sampleSize;
+ wave->sampleSize = 32;
+
+ WaveFormat(wave, wave->sampleRate, sampleSize, wave->channels);
}
+ else TraceLog(WARNING, "Wave crop range out of bounds");
}
-// Update (re-fill) music buffers if data already processed
-void UpdateMusicStream(int index)
+// Get samples data from wave as a floats array
+// NOTE: Returned sample values are normalized to range [-1..1]
+// TODO: Consider multiple channels (mono - stereo)
+float *GetWaveData(Wave wave)
{
- ALenum state;
- bool active = true;
- ALint processed = 0;
+ float *samples = (float *)malloc(wave.sampleCount*sizeof(float));
- // Determine if music stream is ready to be written
- alGetSourcei(musicStreams[index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed);
+ for (int i = 0; i < wave.sampleCount; i++)
+ {
+ if (wave.sampleSize == 8) samples[i] = (float)(((unsigned char *)wave.data)[i] - 127)/256.0f;
+ else if (wave.sampleSize == 16) samples[i] = (float)((short *)wave.data)[i]/32767.0f;
+ else if (wave.sampleSize == 32) samples[i] = ((float *)wave.data)[i];
+ }
- if (musicStreams[index].mixc->playing && (index < MAX_MUSIC_STREAMS) && musicStreams[index].enabled && musicStreams[index].mixc && (processed > 0))
+ return samples;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Music loading and stream playing (.OGG)
+//----------------------------------------------------------------------------------
+
+// Load music stream from file
+Music LoadMusicStream(const char *fileName)
+{
+ Music music = (MusicData *)malloc(sizeof(MusicData));
+
+ if (strcmp(GetExtension(fileName), "ogg") == 0)
{
- active = BufferMusicStream(index, processed);
+ // Open ogg audio stream
+ music->ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL);
+
+ if (music->ctxOgg == NULL) TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName);
+ else
+ {
+ stb_vorbis_info info = stb_vorbis_get_info(music->ctxOgg); // Get Ogg file info
+ //float totalLengthSeconds = stb_vorbis_stream_length_in_seconds(music->ctxOgg);
+
+ // TODO: Support 32-bit sampleSize OGGs
+ music->stream = InitAudioStream(info.sample_rate, 16, info.channels);
+ music->totalSamples = (unsigned int)stb_vorbis_stream_length_in_samples(music->ctxOgg)*info.channels;
+ music->samplesLeft = music->totalSamples;
+ music->ctxType = MUSIC_AUDIO_OGG;
+ music->loop = true; // We loop by default
+
+ TraceLog(DEBUG, "[%s] OGG sample rate: %i", fileName, info.sample_rate);
+ TraceLog(DEBUG, "[%s] OGG channels: %i", fileName, info.channels);
+ TraceLog(DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required);
+
+ }
+ }
+ else if (strcmp(GetExtension(fileName), "flac") == 0)
+ {
+ music->ctxFlac = drflac_open_file(fileName);
- if (!active && musicStreams[index].loop)
+ if (music->ctxFlac == NULL) TraceLog(WARNING, "[%s] FLAC audio file could not be opened", fileName);
+ else
{
- if (musicStreams[index].chipTune)
- {
- if(musicStreams[index].modctx.mod_loaded) jar_mod_seek_start(&musicStreams[index].modctx);
-
- musicStreams[index].totalSamplesLeft = musicStreams[index].totalLengthSeconds*48000.0f;
- }
- else
- {
- stb_vorbis_seek_start(musicStreams[index].stream);
- musicStreams[index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(musicStreams[index].stream)*musicStreams[index].mixc->channels;
- }
-
- // Determine if music stream is ready to be written
- alGetSourcei(musicStreams[index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed);
+ music->stream = InitAudioStream(music->ctxFlac->sampleRate, music->ctxFlac->bitsPerSample, music->ctxFlac->channels);
+ music->totalSamples = music->ctxFlac->totalSampleCount;
+ music->samplesLeft = music->totalSamples;
+ music->ctxType = MUSIC_AUDIO_FLAC;
+ music->loop = true; // We loop by default
- active = BufferMusicStream(index, processed);
+ TraceLog(DEBUG, "[%s] FLAC sample rate: %i", fileName, music->ctxFlac->sampleRate);
+ TraceLog(DEBUG, "[%s] FLAC bits per sample: %i", fileName, music->ctxFlac->bitsPerSample);
+ TraceLog(DEBUG, "[%s] FLAC channels: %i", fileName, music->ctxFlac->channels);
}
+ }
+ else if (strcmp(GetExtension(fileName), "xm") == 0)
+ {
+ int result = jar_xm_create_context_from_file(&music->ctxXm, 48000, fileName);
- if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data...");
-
- alGetSourcei(musicStreams[index].mixc->alSource, AL_SOURCE_STATE, &state);
+ if (!result) // XM context created successfully
+ {
+ jar_xm_set_max_loop_count(music->ctxXm, 0); // Set infinite number of loops
- if (state != AL_PLAYING && active) alSourcePlay(musicStreams[index].mixc->alSource);
+ // NOTE: Only stereo is supported for XM
+ music->stream = InitAudioStream(48000, 32, 2);
+ music->totalSamples = (unsigned int)jar_xm_get_remaining_samples(music->ctxXm);
+ music->samplesLeft = music->totalSamples;
+ music->ctxType = MUSIC_MODULE_XM;
+ music->loop = true;
- if (!active) StopMusicStream(index);
-
+ TraceLog(DEBUG, "[%s] XM number of samples: %i", fileName, music->totalSamples);
+ TraceLog(DEBUG, "[%s] XM track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f);
+ }
+ else TraceLog(WARNING, "[%s] XM file could not be opened", fileName);
}
+ else if (strcmp(GetExtension(fileName), "mod") == 0)
+ {
+ jar_mod_init(&music->ctxMod);
+
+ if (jar_mod_load_file(&music->ctxMod, fileName))
+ {
+ music->stream = InitAudioStream(48000, 16, 2);
+ music->totalSamples = (unsigned int)jar_mod_max_samples(&music->ctxMod);
+ music->samplesLeft = music->totalSamples;
+ music->ctxType = MUSIC_MODULE_MOD;
+ music->loop = true;
+
+ TraceLog(INFO, "[%s] MOD number of samples: %i", fileName, music->samplesLeft);
+ TraceLog(INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f);
+ }
+ else TraceLog(WARNING, "[%s] MOD file could not be opened", fileName);
+ }
+ else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
+
+ return music;
}
-//get number of music channels active at this time, this does not mean they are playing
-int GetMusicStreamCount(void)
+// Unload music stream
+void UnloadMusicStream(Music music)
{
- int musicCount = 0;
-
- // Find empty music slot
- for (int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++)
- {
- if(musicStreams[musicIndex].stream != NULL || musicStreams[musicIndex].chipTune) musicCount++;
- }
-
- return musicCount;
+ CloseAudioStream(music->stream);
+
+ if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
+ else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
+ else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context(music->ctxXm);
+ else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod);
+
+ free(music);
+}
+
+// Start music playing (open stream)
+void PlayMusicStream(Music music)
+{
+ alSourcePlay(music->stream.source);
}
// Pause music playing
-void PauseMusicStream(int index)
+void PauseMusicStream(Music music)
+{
+ alSourcePause(music->stream.source);
+}
+
+// Resume music playing
+void ResumeMusicStream(Music music)
+{
+ ALenum state;
+ alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state);
+
+ if (state == AL_PAUSED) alSourcePlay(music->stream.source);
+}
+
+// Stop music playing (close stream)
+// TODO: Restart XM context
+void StopMusicStream(Music music)
{
- // Pause music stream if music available!
- if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc && musicStreams[index].enabled)
+ alSourceStop(music->stream.source);
+
+ switch (music->ctxType)
{
- TraceLog(INFO, "Pausing music stream");
- alSourcePause(musicStreams[index].mixc->alSource);
- musicStreams[index].mixc->playing = false;
+ case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break;
+ case MUSIC_MODULE_XM: break;
+ case MUSIC_MODULE_MOD: jar_mod_seek_start(&music->ctxMod); break;
+ default: break;
}
+
+ music->samplesLeft = music->totalSamples;
}
-// Resume music playing
-void ResumeMusicStream(int index)
+// Update (re-fill) music buffers if data already processed
+void UpdateMusicStream(Music music)
{
- // Resume music playing... if music available!
ALenum state;
+ ALint processed = 0;
- if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc)
+ alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); // Get music stream state
+ alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed); // Get processed buffers
+
+ if (processed > 0)
{
- alGetSourcei(musicStreams[index].mixc->alSource, AL_SOURCE_STATE, &state);
+ bool active = true;
+ short pcm[AUDIO_BUFFER_SIZE];
+ float pcmf[AUDIO_BUFFER_SIZE];
+ int numBuffersToProcess = processed;
+
+ int numSamples = 0; // Total size of data steamed in L+R samples for xm floats,
+ // individual L or R for ogg shorts
+
+ for (int i = 0; i < numBuffersToProcess; i++)
+ {
+ switch (music->ctxType)
+ {
+ case MUSIC_AUDIO_OGG:
+ {
+ if (music->samplesLeft >= AUDIO_BUFFER_SIZE) numSamples = AUDIO_BUFFER_SIZE;
+ else numSamples = music->samplesLeft;
+
+ // NOTE: Returns the number of samples to process (should be the same as numSamples -> it is)
+ int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, pcm, numSamples);
+
+ // TODO: Review stereo channels Ogg, not enough samples served!
+ UpdateAudioStream(music->stream, pcm, numSamplesOgg*music->stream.channels);
+ music->samplesLeft -= (numSamplesOgg*music->stream.channels);
+
+ } break;
+ case MUSIC_AUDIO_FLAC:
+ {
+ if (music->samplesLeft >= AUDIO_BUFFER_SIZE) numSamples = AUDIO_BUFFER_SIZE;
+ else numSamples = music->samplesLeft;
+
+ int pcmi[AUDIO_BUFFER_SIZE];
+
+ // NOTE: Returns the number of samples to process (should be the same as numSamples)
+ int numSamplesFlac = drflac_read_s32(music->ctxFlac, numSamples, pcmi);
+
+ UpdateAudioStream(music->stream, pcmi, numSamplesFlac*music->stream.channels);
+ music->samplesLeft -= (numSamples*music->stream.channels);
+
+ } break;
+ case MUSIC_MODULE_XM:
+ {
+ if (music->samplesLeft >= AUDIO_BUFFER_SIZE/2) numSamples = AUDIO_BUFFER_SIZE/2;
+ else numSamples = music->samplesLeft;
+
+ // NOTE: Output buffer is 2*numsamples elements (left and right value for each sample)
+ jar_xm_generate_samples(music->ctxXm, pcmf, numSamples);
+ UpdateAudioStream(music->stream, pcmf, numSamples*2); // Using 32bit PCM data
+ music->samplesLeft -= numSamples;
+
+ //TraceLog(INFO, "Samples left: %i", music->samplesLeft);
+
+ } break;
+ case MUSIC_MODULE_MOD:
+ {
+ if (music->samplesLeft >= AUDIO_BUFFER_SIZE/2) numSamples = AUDIO_BUFFER_SIZE/2;
+ else numSamples = music->samplesLeft;
+
+ // NOTE: Output buffer size is nbsample*channels (default: 48000Hz, 16bit, Stereo)
+ jar_mod_fillbuffer(&music->ctxMod, pcm, numSamples, 0);
+ UpdateAudioStream(music->stream, pcm, numSamples*2);
+ music->samplesLeft -= numSamples;
+
+ } break;
+ default: break;
+ }
+
+ if (music->samplesLeft <= 0)
+ {
+ active = false;
+ break;
+ }
+ }
- if (state == AL_PAUSED)
+ // This error is registered when UpdateAudioStream() fails
+ if (alGetError() == AL_INVALID_VALUE) TraceLog(WARNING, "OpenAL: Error buffering data...");
+
+ // Reset audio stream for looping
+ if (!active)
+ {
+ StopMusicStream(music); // Stop music (and reset)
+
+ if (music->loop) PlayMusicStream(music); // Play again
+ }
+ else
{
- TraceLog(INFO, "Resuming music stream");
- alSourcePlay(musicStreams[index].mixc->alSource);
- musicStreams[index].mixc->playing = true;
+ // NOTE: In case window is minimized, music stream is stopped,
+ // just make sure to play again on window restore
+ if (state != AL_PLAYING) PlayMusicStream(music);
}
}
}
// Check if any music is playing
-bool IsMusicPlaying(int index)
+bool IsMusicPlaying(Music music)
{
bool playing = false;
ALint state;
-
- if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc)
- {
- alGetSourcei(musicStreams[index].mixc->alSource, AL_SOURCE_STATE, &state);
-
- if (state == AL_PLAYING) playing = true;
- }
+
+ alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state);
+
+ if (state == AL_PLAYING) playing = true;
return playing;
}
// Set volume for music
-void SetMusicVolume(int index, float volume)
+void SetMusicVolume(Music music, float volume)
{
- if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc)
- {
- alSourcef(musicStreams[index].mixc->alSource, AL_GAIN, volume);
- }
+ alSourcef(music->stream.source, AL_GAIN, volume);
}
// Set pitch for music
-void SetMusicPitch(int index, float pitch)
+void SetMusicPitch(Music music, float pitch)
{
- if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc)
- {
- alSourcef(musicStreams[index].mixc->alSource, AL_PITCH, pitch);
- }
+ alSourcef(music->stream.source, AL_PITCH, pitch);
}
// Get music time length (in seconds)
-float GetMusicTimeLength(int index)
+float GetMusicTimeLength(Music music)
{
- float totalSeconds;
-
- if (musicStreams[index].chipTune) totalSeconds = (float)musicStreams[index].totalLengthSeconds;
- else totalSeconds = stb_vorbis_stream_length_in_seconds(musicStreams[index].stream);
+ float totalSeconds = (float)music->totalSamples/music->stream.sampleRate;
return totalSeconds;
}
// Get current music time played (in seconds)
-float GetMusicTimePlayed(int index)
+float GetMusicTimePlayed(Music music)
{
float secondsPlayed = 0.0f;
-
- if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc)
+
+ unsigned int samplesPlayed = music->totalSamples - music->samplesLeft;
+ secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels);
+
+ return secondsPlayed;
+}
+
+// Init audio stream (to stream audio pcm data)
+AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels)
+{
+ AudioStream stream = { 0 };
+
+ stream.sampleRate = sampleRate;
+ stream.sampleSize = sampleSize;
+ stream.channels = channels;
+
+ // Setup OpenAL format
+ if (channels == 1)
{
- if (musicStreams[index].chipTune && musicStreams[index].xmctx)
- {
- uint64_t samples;
- jar_xm_get_position(musicStreams[index].xmctx, NULL, NULL, NULL, &samples);
- secondsPlayed = (float)samples/(48000.0f*musicStreams[index].mixc->channels); // Not sure if this is the correct value
- }
- else if(musicStreams[index].chipTune && musicStreams[index].modctx.mod_loaded)
+ switch (sampleSize)
{
- long numsamp = jar_mod_current_samples(&musicStreams[index].modctx);
- secondsPlayed = (float)numsamp/(48000.0f);
+ case 8: stream.format = AL_FORMAT_MONO8; break;
+ case 16: stream.format = AL_FORMAT_MONO16; break;
+ case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break;
+ default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break;
}
- else
+ }
+ else if (channels == 2)
+ {
+ switch (sampleSize)
{
- int totalSamples = stb_vorbis_stream_length_in_samples(musicStreams[index].stream)*musicStreams[index].mixc->channels;
- int samplesPlayed = totalSamples - musicStreams[index].totalSamplesLeft;
- secondsPlayed = (float)samplesPlayed/(musicStreams[index].mixc->sampleRate*musicStreams[index].mixc->channels);
+ case 8: stream.format = AL_FORMAT_STEREO8; break;
+ case 16: stream.format = AL_FORMAT_STEREO16; break;
+ case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break;
+ default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break;
}
}
+ else TraceLog(WARNING, "Init audio stream: Number of channels not supported: %i", channels);
- return secondsPlayed;
-}
+ // Create an audio source
+ alGenSources(1, &stream.source);
+ alSourcef(stream.source, AL_PITCH, 1);
+ alSourcef(stream.source, AL_GAIN, 1);
+ alSource3f(stream.source, AL_POSITION, 0, 0, 0);
+ alSource3f(stream.source, AL_VELOCITY, 0, 0, 0);
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
+ // Create Buffers (double buffering)
+ alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers);
-// Fill music buffers with new data from music stream
-static bool BufferMusicStream(int index, int numBuffers)
-{
- 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 (musicStreams[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes.
+ // Initialize buffer with zeros by default
+ for (int i = 0; i < MAX_STREAM_BUFFERS; i++)
{
- for (int i = 0; i < numBuffers; i++)
+ if (stream.sampleSize == 8)
{
- if (musicStreams[index].modctx.mod_loaded)
- {
- if (musicStreams[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) size = MUSIC_BUFFER_SIZE_SHORT/2;
- else size = musicStreams[index].totalSamplesLeft/2;
-
- jar_mod_fillbuffer(&musicStreams[index].modctx, pcm, size, 0 );
- BufferMixChannel(musicStreams[index].mixc, pcm, size*2);
- }
- else if (musicStreams[index].xmctx)
- {
- if (musicStreams[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_FLOAT) size = MUSIC_BUFFER_SIZE_FLOAT/2;
- else size = musicStreams[index].totalSamplesLeft/2;
-
- jar_xm_generate_samples(musicStreams[index].xmctx, pcmf, size); // reads 2*readlen shorts and moves them to buffer+size memory location
- BufferMixChannel(musicStreams[index].mixc, pcmf, size*2);
- }
-
- musicStreams[index].totalSamplesLeft -= size;
-
- if (musicStreams[index].totalSamplesLeft <= 0)
- {
- active = false;
- break;
- }
+ unsigned char pcm[AUDIO_BUFFER_SIZE] = { 0 };
+ alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(unsigned char), stream.sampleRate);
}
- }
- else
- {
- if (musicStreams[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) size = MUSIC_BUFFER_SIZE_SHORT;
- else size = musicStreams[index].totalSamplesLeft;
-
- for (int i = 0; i < numBuffers; i++)
+ else if (stream.sampleSize == 16)
{
- int streamedBytes = stb_vorbis_get_samples_short_interleaved(musicStreams[index].stream, musicStreams[index].mixc->channels, pcm, size);
- BufferMixChannel(musicStreams[index].mixc, pcm, streamedBytes * musicStreams[index].mixc->channels);
- musicStreams[index].totalSamplesLeft -= streamedBytes * musicStreams[index].mixc->channels;
-
- if (musicStreams[index].totalSamplesLeft <= 0)
- {
- active = false;
- break;
- }
+ short pcm[AUDIO_BUFFER_SIZE] = { 0 };
+ alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(short), stream.sampleRate);
+ }
+ else if (stream.sampleSize == 32)
+ {
+ float pcm[AUDIO_BUFFER_SIZE] = { 0.0f };
+ alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(float), stream.sampleRate);
}
}
- return active;
+ alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers);
+
+ TraceLog(INFO, "[AUD ID %i] Audio stream loaded successfully", stream.source);
+
+ return stream;
}
-// Empty music buffers
-static void EmptyMusicStream(int index)
+// Close audio stream and free memory
+void CloseAudioStream(AudioStream stream)
{
- ALuint buffer = 0;
+ // Stop playing channel
+ alSourceStop(stream.source);
+
+ // Flush out all queued buffers
int queued = 0;
+ alGetSourcei(stream.source, AL_BUFFERS_QUEUED, &queued);
- alGetSourcei(musicStreams[index].mixc->alSource, AL_BUFFERS_QUEUED, &queued);
+ ALuint buffer = 0;
while (queued > 0)
{
- alSourceUnqueueBuffers(musicStreams[index].mixc->alSource, 1, &buffer);
-
+ alSourceUnqueueBuffers(stream.source, 1, &buffer);
queued--;
}
+
+ // Delete source and buffers
+ alDeleteSources(1, &stream.source);
+ alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers);
+
+ TraceLog(INFO, "[AUD ID %i] Unloaded audio stream data", stream.source);
+}
+
+// Update audio stream buffers with data
+// NOTE: Only one buffer per call
+void UpdateAudioStream(AudioStream stream, void *data, int numSamples)
+{
+ ALuint buffer = 0;
+ alSourceUnqueueBuffers(stream.source, 1, &buffer);
+
+ // Check if any buffer was available for unqueue
+ if (alGetError() != AL_INVALID_VALUE)
+ {
+ if (stream.sampleSize == 8) alBufferData(buffer, stream.format, (unsigned char *)data, numSamples*sizeof(unsigned char), stream.sampleRate);
+ else if (stream.sampleSize == 16) alBufferData(buffer, stream.format, (short *)data, numSamples*sizeof(short), stream.sampleRate);
+ else if (stream.sampleSize == 32) alBufferData(buffer, stream.format, (float *)data, numSamples*sizeof(float), stream.sampleRate);
+
+ alSourceQueueBuffers(stream.source, 1, &buffer);
+ }
+}
+
+// Check if any audio stream buffers requires refill
+bool IsAudioBufferProcessed(AudioStream stream)
+{
+ ALint processed = 0;
+
+ // Determine if music stream is ready to be written
+ alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed);
+
+ return (processed > 0);
+}
+
+// Play audio stream
+void PlayAudioStream(AudioStream stream)
+{
+ alSourcePlay(stream.source);
+}
+
+// Play audio stream
+void PauseAudioStream(AudioStream stream)
+{
+ alSourcePause(stream.source);
}
+// Resume audio stream playing
+void ResumeAudioStream(AudioStream stream)
+{
+ ALenum state;
+ alGetSourcei(stream.source, AL_SOURCE_STATE, &state);
+
+ if (state == AL_PAUSED) alSourcePlay(stream.source);
+}
+
+// Stop audio stream
+void StopAudioStream(AudioStream stream)
+{
+ alSourceStop(stream.source);
+}
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+
// Load WAV file into Wave structure
static Wave LoadWAV(const char *fileName)
{
@@ -1288,18 +1189,18 @@ static Wave LoadWAV(const char *fileName)
else
{
// Allocate memory for data
- wave.data = (unsigned char *)malloc(sizeof(unsigned char) * waveData.subChunkSize);
+ wave.data = (unsigned char *)malloc(sizeof(unsigned char)*waveData.subChunkSize);
// Read in the sound data into the soundData variable
fread(wave.data, waveData.subChunkSize, 1, wavFile);
// Now we set the variables that we need later
- wave.dataSize = waveData.subChunkSize;
+ wave.sampleCount = waveData.subChunkSize;
wave.sampleRate = waveFormat.sampleRate;
+ wave.sampleSize = waveFormat.bitsPerSample;
wave.channels = waveFormat.numChannels;
- wave.bitsPerSample = waveFormat.bitsPerSample;
- TraceLog(INFO, "[%s] WAV file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels);
+ TraceLog(INFO, "[%s] WAV file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", fileName, wave.sampleRate, wave.sampleSize, wave.channels);
}
}
}
@@ -1312,7 +1213,7 @@ static Wave LoadWAV(const char *fileName)
// Load OGG file into Wave structure
// NOTE: Using stb_vorbis library
-static Wave LoadOGG(char *fileName)
+static Wave LoadOGG(const char *fileName)
{
Wave wave;
@@ -1328,35 +1229,24 @@ static Wave LoadOGG(char *fileName)
stb_vorbis_info info = stb_vorbis_get_info(oggFile);
wave.sampleRate = info.sample_rate;
- wave.bitsPerSample = 16;
+ wave.sampleSize = 16; // 16 bit per sample (short)
wave.channels = info.channels;
- TraceLog(DEBUG, "[%s] Ogg sample rate: %i", fileName, info.sample_rate);
- TraceLog(DEBUG, "[%s] Ogg channels: %i", fileName, info.channels);
-
- int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile) * info.channels);
-
- wave.dataSize = totalSamplesLength*sizeof(short); // Size must be in bytes
-
- TraceLog(DEBUG, "[%s] Samples length: %i", fileName, totalSamplesLength);
-
+ int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile)*info.channels);
float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile);
- TraceLog(DEBUG, "[%s] Total seconds: %f", fileName, totalSeconds);
-
if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio lenght is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds);
int totalSamples = totalSeconds*info.sample_rate*info.channels;
+ wave.sampleCount = totalSamples;
- TraceLog(DEBUG, "[%s] Total samples calculated: %i", fileName, totalSamples);
-
- wave.data = malloc(sizeof(short)*totalSamplesLength);
+ wave.data = (short *)malloc(totalSamplesLength*sizeof(short));
- int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength);
+ int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, totalSamplesLength);
TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained);
- TraceLog(INFO, "[%s] OGG file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels);
+ TraceLog(INFO, "[%s] OGG file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", fileName, wave.sampleRate, wave.sampleSize, wave.channels);
stb_vorbis_close(oggFile);
}
@@ -1364,12 +1254,22 @@ static Wave LoadOGG(char *fileName)
return wave;
}
-// Unload Wave data
-static void UnloadWave(Wave wave)
+// Load FLAC file into Wave structure
+// NOTE: Using dr_flac library
+static Wave LoadFLAC(const char *fileName)
{
- free(wave.data);
+ Wave wave;
+
+ // Decode an entire FLAC file in one go
+ uint64_t totalSampleCount;
+ wave.data = drflac_open_and_decode_file_s32(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount);
- TraceLog(INFO, "Unloaded wave data");
+ wave.sampleCount = (int)totalSampleCount;
+ wave.sampleSize = 32;
+
+ if (wave.data == NULL) TraceLog(WARNING, "[%s] FLAC data could not be loaded", fileName);
+
+ return wave;
}
// Some required functions for audio standalone module version
@@ -1378,7 +1278,7 @@ static void UnloadWave(Wave wave)
const char *GetExtension(const char *fileName)
{
const char *dot = strrchr(fileName, '.');
- if(!dot || dot == fileName) return "";
+ if (!dot || dot == fileName) return "";
return (dot + 1);
}
@@ -1393,7 +1293,7 @@ void TraceLog(int msgType, const char *text, ...)
traceDebugMsgs = 0;
#endif
- switch(msgType)
+ switch (msgType)
{
case INFO: fprintf(stdout, "INFO: "); break;
case ERROR: fprintf(stdout, "ERROR: "); break;
diff --git a/src/audio.h b/src/audio.h
index b6850911..923492ca 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -2,7 +2,7 @@
*
* raylib.audio
*
-* Basic functions to manage Audio:
+* Basic functions to manage Audio:
* Manage audio device (init/close)
* Load and Unload audio files
* Play/Stop/Pause/Resume loaded audio
@@ -68,13 +68,29 @@ typedef struct Sound {
// Wave type, defines audio wave data
typedef struct Wave {
+ unsigned int sampleCount; // Number of samples
+ unsigned int sampleRate; // Frequency (samples per second)
+ unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ unsigned int channels; // Number of channels (1-mono, 2-stereo)
void *data; // Buffer data pointer
- unsigned int dataSize; // Data size in bytes
- unsigned int sampleRate; // Samples per second to be played
- short bitsPerSample; // Sample size in bits
- short channels;
} Wave;
+// Music type (file streaming from memory)
+// NOTE: Anything longer than ~10 seconds should be streamed
+typedef struct Music *Music;
+
+// Audio stream type
+// NOTE: Useful to create custom audio streams not bound to a specific file
+typedef struct AudioStream {
+ unsigned int sampleRate; // Frequency (samples per second)
+ unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ unsigned int channels; // Number of channels (1-mono, 2-stereo)
+
+ int format; // OpenAL audio format specifier
+ unsigned int source; // OpenAL audio source id
+ unsigned int buffers[2]; // OpenAL audio buffers (double buffering)
+} AudioStream;
+
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
@@ -89,34 +105,44 @@ 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); // Check if device has been initialized successfully
+bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
Sound LoadSound(char *fileName); // Load sound to memory
Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data
Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource)
+void UpdateSound(Sound sound, void *data, int numSamples); // Update sound buffer with new data
void UnloadSound(Sound sound); // Unload sound
void PlaySound(Sound sound); // Play a sound
void PauseSound(Sound sound); // Pause a sound
+void ResumeSound(Sound sound); // Resume a paused sound
void StopSound(Sound sound); // Stop playing a sound
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)
-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)
-void SetMusicPitch(int index, float pitch); // Set pitch for a music (1.0 is base 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); // Get number of streams loaded
-
-int InitRawMixChannel(int sampleRate, int channels, bool floatingPoint); // Initialize raw audio mix channel for audio buffering
-int BufferRawMixChannel(int mixc, void *data, unsigned short numberElements); // Buffers data directly to raw mix channel
-void CloseRawMixChannel(int mixc); // Closes and frees raw mix channel
+Music LoadMusicStream(char *fileName); // Load music stream from file
+void UnloadMusicStream(Music music); // Unload music stream
+void PlayMusicStream(Music music); // Start music playing (open stream)
+void UpdateMusicStream(Music music); // Updates buffers for music streaming
+void StopMusicStream(Music music); // Stop music playing (close stream)
+void PauseMusicStream(Music music); // Pause music playing
+void ResumeMusicStream(Music music); // Resume playing paused music
+bool IsMusicPlaying(Music music); // Check if music is playing
+void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level)
+void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level)
+float GetMusicTimeLength(Music music); // Get music time length (in seconds)
+float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)
+
+AudioStream InitAudioStream(unsigned int sampleRate,
+ unsigned int sampleSize,
+ unsigned int channels); // Init audio stream (to stream audio pcm data)
+void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data
+void CloseAudioStream(AudioStream stream); // Close audio stream and free memory
+bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill
+void PlayAudioStream(AudioStream stream); // Play audio stream
+void PauseAudioStream(AudioStream stream); // Pause audio stream
+void ResumeAudioStream(AudioStream stream); // Resume audio stream
+void StopAudioStream(AudioStream stream); // Stop audio stream
#ifdef __cplusplus
}
diff --git a/src/camera.c b/src/camera.c
deleted file mode 100644
index 11571cca..00000000
--- a/src/camera.c
+++ /dev/null
@@ -1,523 +0,0 @@
-/**********************************************************************************************
-*
-* raylib Camera System - Camera Modes Setup and Control Functions
-*
-* Copyright (c) 2015 Marc Palau and Ramon Santamaria (@raysan5)
-*
-* 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.
-*
-**********************************************************************************************/
-
-//#define CAMERA_STANDALONE // NOTE: To use the camera module as standalone lib, just uncomment this line
- // NOTE: ProcessCamera() should be reviewed to adapt inputs to other systems
-
-#if defined(CAMERA_STANDALONE)
- #include "camera.h"
-#else
- #include "raylib.h"
-#endif
-
-#include <math.h> // Required for: sqrt(), sin(), cos()
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-// CAMERA_GENERIC
-#define CAMERA_SCROLL_SENSITIVITY 1.5f
-
-// FREE_CAMERA
-#define FREE_CAMERA_MOUSE_SENSITIVITY 0.01f
-#define FREE_CAMERA_DISTANCE_MIN_CLAMP 0.3f
-#define FREE_CAMERA_DISTANCE_MAX_CLAMP 120.0f
-#define FREE_CAMERA_MIN_CLAMP 85.0f
-#define FREE_CAMERA_MAX_CLAMP -85.0f
-#define FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY 0.05f
-#define FREE_CAMERA_PANNING_DIVIDER 5.1f
-
-// ORBITAL_CAMERA
-#define ORBITAL_CAMERA_SPEED 0.01f
-
-// FIRST_PERSON
-//#define FIRST_PERSON_MOUSE_SENSITIVITY 0.003f
-#define FIRST_PERSON_FOCUS_DISTANCE 25.0f
-#define FIRST_PERSON_MIN_CLAMP 85.0f
-#define FIRST_PERSON_MAX_CLAMP -85.0f
-
-#define FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0f
-#define FIRST_PERSON_STEP_DIVIDER 30.0f
-#define FIRST_PERSON_WAVING_DIVIDER 200.0f
-
-#define FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION 0.85f
-
-// THIRD_PERSON
-//#define THIRD_PERSON_MOUSE_SENSITIVITY 0.003f
-#define THIRD_PERSON_DISTANCE_CLAMP 1.2f
-#define THIRD_PERSON_MIN_CLAMP 5.0f
-#define THIRD_PERSON_MAX_CLAMP -85.0f
-#define THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f }
-
-// PLAYER (used by camera)
-#define PLAYER_WIDTH 0.4f
-#define PLAYER_HEIGHT 0.9f
-#define PLAYER_DEPTH 0.4f
-#define PLAYER_MOVEMENT_DIVIDER 20.0f
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-// Camera move modes (first person and third person cameras)
-typedef enum { MOVE_FRONT = 0, MOVE_LEFT, MOVE_BACK, MOVE_RIGHT, MOVE_UP, MOVE_DOWN } CameraMove;
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-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 };
-static Vector2 cameraMouseVariation = { 0.0f, 0.0f };
-static float mouseSensitivity = 0.003f;
-static int cameraMoveControl[6] = { 'W', 'A', 'S', 'D', 'E', 'Q' };
-static int cameraMoveCounter = 0;
-static int cameraUseGravity = 1;
-static int panControlKey = 2; // raylib: MOUSE_MIDDLE_BUTTON
-static int altControlKey = 342; // raylib: KEY_LEFT_ALT
-static int smoothZoomControlKey = 341; // raylib: KEY_LEFT_CONTROL
-
-static int cameraMode = CAMERA_CUSTOM;
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Declaration
-//----------------------------------------------------------------------------------
-static void ProcessCamera(Camera *camera, Vector3 *playerPosition);
-
-#if defined(CAMERA_STANDALONE)
-// NOTE: Camera controls depend on some raylib input functions
-// TODO: Set your own input functions (used in ProcessCamera())
-static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; }
-static void SetMousePosition(Vector2 pos) {}
-static int IsMouseButtonDown(int button) { return 0;}
-static int GetMouseWheelMove() { return 0; }
-static int GetScreenWidth() { return 1280; }
-static int GetScreenHeight() { return 720; }
-static void ShowCursor() {}
-static void HideCursor() {}
-static int IsKeyDown(int key) { return 0; }
-#endif
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition
-//----------------------------------------------------------------------------------
-
-// Select camera mode (multiple camera modes available)
-// TODO: Review hardcoded values when changing modes...
-void SetCameraMode(int mode)
-{
- if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_FREE))
- {
- cameraMode = CAMERA_THIRD_PERSON;
- cameraTargetDistance = 5.0f;
- cameraAngle.y = -40*DEG2RAD;
- ProcessCamera(&internalCamera, &internalCamera.position);
- }
- else if ((cameraMode == CAMERA_FIRST_PERSON) && (mode == CAMERA_ORBITAL))
- {
- cameraMode = CAMERA_THIRD_PERSON;
- cameraTargetDistance = 5.0f;
- cameraAngle.y = -40*DEG2RAD;
- ProcessCamera(&internalCamera, &internalCamera.position);
- }
- else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_FREE))
- {
- cameraTargetDistance = 10.0f;
- cameraAngle.x = 45*DEG2RAD;
- cameraAngle.y = -40*DEG2RAD;
- internalCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
- ProcessCamera(&internalCamera, &internalCamera.position);
-
- ShowCursor();
- }
- else if ((cameraMode == CAMERA_CUSTOM) && (mode == CAMERA_ORBITAL))
- {
- cameraTargetDistance = 10.0f;
- cameraAngle.x = 225*DEG2RAD;
- cameraAngle.y = -40*DEG2RAD;
- internalCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
- ProcessCamera(&internalCamera, &internalCamera.position);
- }
-
- cameraMode = mode;
-}
-
-// Update camera (player position is ignored)
-void UpdateCamera(Camera *camera)
-{
- Vector3 position = { 0.0f, 0.0f, 0.0f };
-
- // Process internal camera and player position (if required)
- if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, &position);
-
- *camera = internalCamera;
-}
-
-// Update camera and player position (1st person and 3rd person cameras)
-void UpdateCameraPlayer(Camera *camera, Vector3 *position)
-{
- // Process internal camera and player position (if required)
- if (cameraMode != CAMERA_CUSTOM) ProcessCamera(&internalCamera, position);
-
- *camera = internalCamera;
-}
-
-// Set internal camera position
-void SetCameraPosition(Vector3 position)
-{
- internalCamera.position = position;
-
- Vector3 v1 = internalCamera.position;
- Vector3 v2 = internalCamera.target;
-
- float dx = v2.x - v1.x;
- float dy = v2.y - v1.y;
- float dz = v2.z - v1.z;
-
- cameraTargetDistance = sqrt(dx*dx + dy*dy + dz*dz);
-}
-
-// Set internal camera target
-void SetCameraTarget(Vector3 target)
-{
- internalCamera.target = target;
-
- Vector3 v1 = internalCamera.position;
- Vector3 v2 = internalCamera.target;
-
- float dx = v2.x - v1.x;
- float dy = v2.y - v1.y;
- float dz = v2.z - v1.z;
-
- 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)
-{
- panControlKey = panKey;
-}
-
-// Set camera alt key to combine with mouse movement (free camera)
-void SetCameraAltControl(int altKey)
-{
- altControlKey = altKey;
-}
-
-// Set camera smooth zoom key to combine with mouse (free camera)
-void SetCameraSmoothZoomControl(int szKey)
-{
- smoothZoomControlKey = szKey;
-}
-
-// Set camera move controls (1st person and 3rd person cameras)
-void SetCameraMoveControls(int frontKey, int backKey, int leftKey, int rightKey, int upKey, int downKey)
-{
- cameraMoveControl[MOVE_FRONT] = frontKey;
- cameraMoveControl[MOVE_LEFT] = leftKey;
- cameraMoveControl[MOVE_BACK] = backKey;
- cameraMoveControl[MOVE_RIGHT] = rightKey;
- cameraMoveControl[MOVE_UP] = upKey;
- cameraMoveControl[MOVE_DOWN] = downKey;
-}
-
-// Set camera mouse sensitivity (1st person and 3rd person cameras)
-void SetCameraMouseSensitivity(float sensitivity)
-{
- mouseSensitivity = (sensitivity/10000.0f);
-}
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
-
-// Process desired camera mode and controls
-// NOTE: Camera controls depend on some raylib functions:
-// Mouse: GetMousePosition(), SetMousePosition(), IsMouseButtonDown(), GetMouseWheelMove()
-// System: GetScreenWidth(), GetScreenHeight(), ShowCursor(), HideCursor()
-// Keys: IsKeyDown()
-static void ProcessCamera(Camera *camera, Vector3 *playerPosition)
-{
- // Mouse movement detection
- Vector2 mousePosition = GetMousePosition();
- int mouseWheelMove = GetMouseWheelMove();
- int panKey = IsMouseButtonDown(panControlKey); // bool value
-
- int screenWidth = GetScreenWidth();
- int screenHeight = GetScreenHeight();
-
- if ((cameraMode != CAMERA_FREE) && (cameraMode != CAMERA_ORBITAL))
- {
- HideCursor();
-
- if (mousePosition.x < screenHeight/3) SetMousePosition((Vector2){ screenWidth - screenHeight/3, mousePosition.y});
- else if (mousePosition.y < screenHeight/3) SetMousePosition((Vector2){ mousePosition.x, screenHeight - screenHeight/3});
- else if (mousePosition.x > screenWidth - screenHeight/3) SetMousePosition((Vector2) { screenHeight/3, mousePosition.y});
- else if (mousePosition.y > screenHeight - screenHeight/3) SetMousePosition((Vector2){ mousePosition.x, screenHeight/3});
- else
- {
- cameraMouseVariation.x = mousePosition.x - cameraMousePosition.x;
- cameraMouseVariation.y = mousePosition.y - cameraMousePosition.y;
- }
- }
- else
- {
- ShowCursor();
-
- cameraMouseVariation.x = mousePosition.x - cameraMousePosition.x;
- cameraMouseVariation.y = mousePosition.y - cameraMousePosition.y;
- }
-
- // NOTE: We GetMousePosition() again because it can be modified by a previous SetMousePosition() call
- // If using directly mousePosition variable we have problems on CAMERA_FIRST_PERSON and CAMERA_THIRD_PERSON
- cameraMousePosition = GetMousePosition();
-
- // Support for multiple automatic camera modes
- switch (cameraMode)
- {
- case CAMERA_FREE:
- {
- // Camera zoom
- if ((cameraTargetDistance < FREE_CAMERA_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
- {
- cameraTargetDistance -= (mouseWheelMove*CAMERA_SCROLL_SENSITIVITY);
-
- if (cameraTargetDistance > FREE_CAMERA_DISTANCE_MAX_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MAX_CLAMP;
- }
- // Camera looking down
- else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
- {
- camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- }
- else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0))
- {
- camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
-
- // if (camera->target.y < 0) camera->target.y = -0.001;
- }
- else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0))
- {
- cameraTargetDistance -= (mouseWheelMove*CAMERA_SCROLL_SENSITIVITY);
- if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP;
- }
- // Camera looking up
- else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == FREE_CAMERA_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
- {
- camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- }
- else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0))
- {
- camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
- camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_SCROLL_SENSITIVITY/cameraTargetDistance;
-
- // if (camera->target.y > 0) camera->target.y = 0.001;
- }
- else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0))
- {
- cameraTargetDistance -= (mouseWheelMove*CAMERA_SCROLL_SENSITIVITY);
- if (cameraTargetDistance < FREE_CAMERA_DISTANCE_MIN_CLAMP) cameraTargetDistance = FREE_CAMERA_DISTANCE_MIN_CLAMP;
- }
-
- // Inputs
- if (IsKeyDown(altControlKey))
- {
- if (IsKeyDown(smoothZoomControlKey))
- {
- // Camera smooth zoom
- if (panKey) cameraTargetDistance += (cameraMouseVariation.y*FREE_CAMERA_SMOOTH_ZOOM_SENSITIVITY);
- }
- // Camera orientation calculation
- else if (panKey)
- {
- // Camera orientation calculation
- // Get the mouse sensitivity
- cameraAngle.x += cameraMouseVariation.x*-FREE_CAMERA_MOUSE_SENSITIVITY;
- cameraAngle.y += cameraMouseVariation.y*-FREE_CAMERA_MOUSE_SENSITIVITY;
-
- // Angle clamp
- if (cameraAngle.y > FREE_CAMERA_MIN_CLAMP*DEG2RAD) cameraAngle.y = FREE_CAMERA_MIN_CLAMP*DEG2RAD;
- else if (cameraAngle.y < FREE_CAMERA_MAX_CLAMP*DEG2RAD) cameraAngle.y = FREE_CAMERA_MAX_CLAMP*DEG2RAD;
- }
- }
- // Paning
- else if (panKey)
- {
- camera->target.x += ((cameraMouseVariation.x*-FREE_CAMERA_MOUSE_SENSITIVITY)*cos(cameraAngle.x) + (cameraMouseVariation.y*FREE_CAMERA_MOUSE_SENSITIVITY)*sin(cameraAngle.x)*sin(cameraAngle.y))*(cameraTargetDistance/FREE_CAMERA_PANNING_DIVIDER);
- camera->target.y += ((cameraMouseVariation.y*FREE_CAMERA_MOUSE_SENSITIVITY)*cos(cameraAngle.y))*(cameraTargetDistance/FREE_CAMERA_PANNING_DIVIDER);
- camera->target.z += ((cameraMouseVariation.x*FREE_CAMERA_MOUSE_SENSITIVITY)*sin(cameraAngle.x) + (cameraMouseVariation.y*FREE_CAMERA_MOUSE_SENSITIVITY)*cos(cameraAngle.x)*sin(cameraAngle.y))*(cameraTargetDistance/FREE_CAMERA_PANNING_DIVIDER);
- }
-
- // Focus to center
- // TODO: Move this function out of this module?
- if (IsKeyDown('Z')) camera->target = (Vector3){ 0.0f, 0.0f, 0.0f };
-
- // Camera position update
- camera->position.x = sin(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.x;
-
- if (cameraAngle.y <= 0.0f) camera->position.y = sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
- else camera->position.y = -sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
-
- camera->position.z = cos(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.z;
-
- } break;
- case CAMERA_ORBITAL:
- {
- cameraAngle.x += ORBITAL_CAMERA_SPEED;
-
- // Camera zoom
- cameraTargetDistance -= (mouseWheelMove*CAMERA_SCROLL_SENSITIVITY);
-
- // Camera distance clamp
- if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP;
-
- // Focus to center
- if (IsKeyDown('Z')) camera->target = (Vector3){ 0.0f, 0.0f, 0.0f };
-
- // Camera position update
- camera->position.x = sin(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.x;
-
- if (cameraAngle.y <= 0.0f) camera->position.y = sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
- else camera->position.y = -sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
-
- camera->position.z = cos(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.z;
-
- } break;
- case CAMERA_FIRST_PERSON:
- case CAMERA_THIRD_PERSON:
- {
- bool isMoving = false;
-
- // Keyboard inputs
- if (IsKeyDown(cameraMoveControl[MOVE_FRONT]))
- {
- playerPosition->x -= sin(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z -= cos(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
-
- if (!cameraUseGravity) camera->position.y += sin(cameraAngle.y)/PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
- else if (IsKeyDown(cameraMoveControl[MOVE_BACK]))
- {
- playerPosition->x += sin(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z += cos(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
-
- if (!cameraUseGravity) camera->position.y -= sin(cameraAngle.y)/PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
-
- if (IsKeyDown(cameraMoveControl[MOVE_LEFT]))
- {
- playerPosition->x -= cos(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z += sin(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
- else if (IsKeyDown(cameraMoveControl[MOVE_RIGHT]))
- {
- playerPosition->x += cos(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
- playerPosition->z -= sin(cameraAngle.x)/PLAYER_MOVEMENT_DIVIDER;
-
- isMoving = true;
- }
-
- if (IsKeyDown(cameraMoveControl[MOVE_UP]))
- {
- if (!cameraUseGravity) playerPosition->y += 1.0f/PLAYER_MOVEMENT_DIVIDER;
- }
- else if (IsKeyDown(cameraMoveControl[MOVE_DOWN]))
- {
- if (!cameraUseGravity) playerPosition->y -= 1.0f/PLAYER_MOVEMENT_DIVIDER;
- }
-
- if (cameraMode == CAMERA_THIRD_PERSON)
- {
- // Camera orientation calculation
- cameraAngle.x += cameraMouseVariation.x*-mouseSensitivity;
- cameraAngle.y += cameraMouseVariation.y*-mouseSensitivity;
-
- // Angle clamp
- if (cameraAngle.y > THIRD_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = THIRD_PERSON_MIN_CLAMP*DEG2RAD;
- else if (cameraAngle.y < THIRD_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = THIRD_PERSON_MAX_CLAMP*DEG2RAD;
-
- // Camera zoom
- cameraTargetDistance -= (mouseWheelMove*CAMERA_SCROLL_SENSITIVITY);
-
- // Camera distance clamp
- if (cameraTargetDistance < THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = THIRD_PERSON_DISTANCE_CLAMP;
-
- // Camera is always looking at player
- camera->target.x = playerPosition->x + THIRD_PERSON_OFFSET.x*cos(cameraAngle.x) + THIRD_PERSON_OFFSET.z*sin(cameraAngle.x);
- camera->target.y = playerPosition->y + PLAYER_HEIGHT*FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION + THIRD_PERSON_OFFSET.y;
- camera->target.z = playerPosition->z + THIRD_PERSON_OFFSET.z*sin(cameraAngle.x) - THIRD_PERSON_OFFSET.x*sin(cameraAngle.x);
-
- // Camera position update
- camera->position.x = sin(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.x;
-
- if (cameraAngle.y <= 0.0f) camera->position.y = sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
- else camera->position.y = -sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
-
- camera->position.z = cos(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.z;
- }
- else // CAMERA_FIRST_PERSON
- {
- if (isMoving) cameraMoveCounter++;
-
- // Camera orientation calculation
- cameraAngle.x += (cameraMouseVariation.x * -mouseSensitivity);
- cameraAngle.y += (cameraMouseVariation.y * -mouseSensitivity);
-
- // Angle clamp
- if (cameraAngle.y > FIRST_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = FIRST_PERSON_MIN_CLAMP*DEG2RAD;
- else if (cameraAngle.y < FIRST_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = FIRST_PERSON_MAX_CLAMP*DEG2RAD;
-
- // Camera is always looking at player
- camera->target.x = camera->position.x - sin(cameraAngle.x)*FIRST_PERSON_FOCUS_DISTANCE;
- camera->target.y = camera->position.y + sin(cameraAngle.y)*FIRST_PERSON_FOCUS_DISTANCE;
- camera->target.z = camera->position.z - cos(cameraAngle.x)*FIRST_PERSON_FOCUS_DISTANCE;
-
- camera->position.x = playerPosition->x;
- camera->position.y = (playerPosition->y + PLAYER_HEIGHT*FIRST_PERSON_HEIGHT_RELATIVE_EYES_POSITION) - sin(cameraMoveCounter/FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/FIRST_PERSON_STEP_DIVIDER;
- camera->position.z = playerPosition->z;
-
- camera->up.x = sin(cameraMoveCounter/(FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/FIRST_PERSON_WAVING_DIVIDER;
- camera->up.z = -sin(cameraMoveCounter/(FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/FIRST_PERSON_WAVING_DIVIDER;
- }
- } break;
- default: break;
- }
-}
diff --git a/src/camera.h b/src/camera.h
index 8d8029af..33220390 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -2,7 +2,19 @@
*
* raylib Camera System - Camera Modes Setup and Control Functions
*
-* Copyright (c) 2015 Marc Palau and Ramon Santamaria
+* #define CAMERA_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+* #define CAMERA_STANDALONE
+* If defined, the library can be used as standalone as a camera system but some
+* functions must be redefined to manage inputs accordingly.
+*
+* NOTE: Memory footprint of this library is aproximately 52 bytes (global variables)
+*
+* Initial design by Marc Palau (2014)
+* Reviewed by Ramon Santamaria (2015-2016)
*
* 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.
@@ -24,13 +36,6 @@
#ifndef CAMERA_H
#define CAMERA_H
-#ifndef PI
- #define PI 3.14159265358979323846
-#endif
-
-#define DEG2RAD (PI/180.0f)
-#define RAD2DEG (180.0f/PI)
-
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
@@ -40,28 +45,37 @@
// Types and Structures Definition
// NOTE: Below types are required for CAMERA_STANDALONE usage
//----------------------------------------------------------------------------------
-// Camera modes
-typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode;
-
-// Vector2 type
-typedef struct Vector2 {
- float x;
- float y;
-} Vector2;
-
-// Vector3 type
-typedef struct Vector3 {
- float x;
- float y;
- float z;
-} Vector3;
-
-// Camera type, defines a camera position/orientation in 3d space
-typedef struct Camera {
- Vector3 position;
- Vector3 target;
- Vector3 up;
-} Camera;
+#if defined(CAMERA_STANDALONE)
+ // Camera modes
+ typedef enum {
+ CAMERA_CUSTOM = 0,
+ CAMERA_FREE,
+ CAMERA_ORBITAL,
+ CAMERA_FIRST_PERSON,
+ CAMERA_THIRD_PERSON
+ } CameraMode;
+
+ // Vector2 type
+ typedef struct Vector2 {
+ float x;
+ float y;
+ } Vector2;
+
+ // Vector3 type
+ typedef struct Vector3 {
+ float x;
+ float y;
+ float z;
+ } Vector3;
+
+ // Camera type, defines a camera position/orientation in 3d space
+ typedef struct Camera {
+ Vector3 position;
+ Vector3 target;
+ Vector3 up;
+ float fovy;
+ } Camera;
+#endif
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
@@ -75,25 +89,415 @@ extern "C" { // Prevents name mangling of functions
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
-void SetCameraMode(int mode); // Set camera mode (multiple camera modes available)
-void UpdateCamera(Camera *camera); // Update camera (player position is ignored)
-void UpdateCameraPlayer(Camera *camera, Vector3 *position); // Update camera and player position (1st person and 3rd person cameras)
-
-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
+#if defined(CAMERA_STANDALONE)
+void SetCameraMode(Camera camera, int mode); // Set camera mode (multiple camera modes available)
+void UpdateCamera(Camera *camera); // Update camera position for selected mode
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)
void SetCameraSmoothZoomControl(int szKey); // Set camera smooth zoom key to combine with mouse (free camera)
-
void SetCameraMoveControls(int frontKey, int backKey,
- int leftKey, int rightKey,
+ int rightKey, int leftKey,
int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras)
-void SetCameraMouseSensitivity(float sensitivity); // Set camera mouse sensitivity (1st person and 3rd person cameras)
+#endif
#ifdef __cplusplus
}
#endif
#endif // CAMERA_H
+
+
+/***********************************************************************************
+*
+* CAMERA IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(CAMERA_IMPLEMENTATION)
+
+#include <math.h> // Required for: sqrt(), sin(), cos()
+
+#ifndef PI
+ #define PI 3.14159265358979323846
+#endif
+
+#ifndef DEG2RAD
+ #define DEG2RAD (PI/180.0f)
+#endif
+
+#ifndef RAD2DEG
+ #define RAD2DEG (180.0f/PI)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Camera mouse movement sensitivity
+#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f
+#define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f
+
+// FREE_CAMERA
+#define CAMERA_FREE_MOUSE_SENSITIVITY 0.01f
+#define CAMERA_FREE_DISTANCE_MIN_CLAMP 0.3f
+#define CAMERA_FREE_DISTANCE_MAX_CLAMP 120.0f
+#define CAMERA_FREE_MIN_CLAMP 85.0f
+#define CAMERA_FREE_MAX_CLAMP -85.0f
+#define CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY 0.05f
+#define CAMERA_FREE_PANNING_DIVIDER 5.1f
+
+// ORBITAL_CAMERA
+#define CAMERA_ORBITAL_SPEED 0.01f // Radians per frame
+
+// FIRST_PERSON
+//#define CAMERA_FIRST_PERSON_MOUSE_SENSITIVITY 0.003f
+#define CAMERA_FIRST_PERSON_FOCUS_DISTANCE 25.0f
+#define CAMERA_FIRST_PERSON_MIN_CLAMP 85.0f
+#define CAMERA_FIRST_PERSON_MAX_CLAMP -85.0f
+
+#define CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER 5.0f
+#define CAMERA_FIRST_PERSON_STEP_DIVIDER 30.0f
+#define CAMERA_FIRST_PERSON_WAVING_DIVIDER 200.0f
+
+// THIRD_PERSON
+//#define CAMERA_THIRD_PERSON_MOUSE_SENSITIVITY 0.003f
+#define CAMERA_THIRD_PERSON_DISTANCE_CLAMP 1.2f
+#define CAMERA_THIRD_PERSON_MIN_CLAMP 5.0f
+#define CAMERA_THIRD_PERSON_MAX_CLAMP -85.0f
+#define CAMERA_THIRD_PERSON_OFFSET (Vector3){ 0.4f, 0.0f, 0.0f }
+
+// PLAYER (used by camera)
+#define PLAYER_MOVEMENT_SENSITIVITY 20.0f
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Camera move modes (first person and third person cameras)
+typedef enum { MOVE_FRONT = 0, MOVE_BACK, MOVE_RIGHT, MOVE_LEFT, MOVE_UP, MOVE_DOWN } CameraMove;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static Vector2 cameraAngle = { 0.0f, 0.0f }; // TODO: Remove! Compute it in UpdateCamera()
+static float cameraTargetDistance = 0.0f; // TODO: Remove! Compute it in UpdateCamera()
+static float playerEyesPosition = 1.85f; // Default player eyes position from ground (in meters)
+
+static int cameraMoveControl[6] = { 'W', 'S', 'D', 'A', 'E', 'Q' };
+static int cameraPanControlKey = 2; // raylib: MOUSE_MIDDLE_BUTTON
+static int cameraAltControlKey = 342; // raylib: KEY_LEFT_ALT
+static int cameraSmoothZoomControlKey = 341; // raylib: KEY_LEFT_CONTROL
+
+static int cameraMode = CAMERA_CUSTOM; // Current camera mode
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+#if defined(CAMERA_STANDALONE)
+// NOTE: Camera controls depend on some raylib input functions
+// TODO: Set your own input functions (used in UpdateCamera())
+static Vector2 GetMousePosition() { return (Vector2){ 0.0f, 0.0f }; }
+static void SetMousePosition(Vector2 pos) {}
+static int IsMouseButtonDown(int button) { return 0;}
+static int GetMouseWheelMove() { return 0; }
+static int GetScreenWidth() { return 1280; }
+static int GetScreenHeight() { return 720; }
+static void ShowCursor() {}
+static void HideCursor() {}
+static int IsKeyDown(int key) { return 0; }
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+
+// Select camera mode (multiple camera modes available)
+void SetCameraMode(Camera camera, int mode)
+{
+ // TODO: cameraTargetDistance and cameraAngle should be
+ // calculated using camera parameters on UpdateCamera()
+
+ Vector3 v1 = camera.position;
+ Vector3 v2 = camera.target;
+
+ float dx = v2.x - v1.x;
+ float dy = v2.y - v1.y;
+ float dz = v2.z - v1.z;
+
+ cameraTargetDistance = sqrt(dx*dx + dy*dy + dz*dz);
+
+ Vector2 distance = { 0.0f, 0.0f };
+ distance.x = sqrt(dx*dx + dz*dz);
+ distance.y = sqrt(dx*dx + dy*dy);
+
+ // Camera angle calculation
+ cameraAngle.x = asin(fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
+ cameraAngle.y = -asin(fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW)
+
+ // NOTE: Just testing what cameraAngle means
+ //cameraAngle.x = 0.0f*DEG2RAD; // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
+ //cameraAngle.y = -60.0f*DEG2RAD; // Camera angle in plane XY (0 aligned with X, move positive CW)
+
+ playerEyesPosition = camera.position.y;
+
+ cameraMode = mode;
+}
+
+// Update camera depending on selected mode
+// NOTE: Camera controls depend on some raylib functions:
+// Mouse: GetMousePosition(), SetMousePosition(), IsMouseButtonDown(), GetMouseWheelMove()
+// System: GetScreenWidth(), GetScreenHeight(), ShowCursor(), HideCursor()
+// Keys: IsKeyDown()
+// TODO: Port to quaternion-based camera
+void UpdateCamera(Camera *camera)
+{
+ static int swingCounter = 0; // Used for 1st person swinging movement
+ static Vector2 previousMousePosition = { 0.0f, 0.0f };
+
+ // TODO: Compute cameraTargetDistance and cameraAngle here
+
+ // Mouse movement detection
+ Vector2 mousePositionDelta = { 0.0f, 0.0f };
+ Vector2 mousePosition = GetMousePosition();
+ int mouseWheelMove = GetMouseWheelMove();
+
+ // Keys input detection
+ bool panKey = IsMouseButtonDown(cameraPanControlKey);
+ bool altKey = IsKeyDown(cameraAltControlKey);
+ bool szoomKey = IsKeyDown(cameraSmoothZoomControlKey);
+
+ bool direction[6] = { IsKeyDown(cameraMoveControl[MOVE_FRONT]),
+ IsKeyDown(cameraMoveControl[MOVE_BACK]),
+ IsKeyDown(cameraMoveControl[MOVE_RIGHT]),
+ IsKeyDown(cameraMoveControl[MOVE_LEFT]),
+ IsKeyDown(cameraMoveControl[MOVE_UP]),
+ IsKeyDown(cameraMoveControl[MOVE_DOWN]) };
+
+ // TODO: Consider touch inputs for camera
+
+ if (cameraMode != CAMERA_CUSTOM)
+ {
+ // Get screen size
+ int screenWidth = GetScreenWidth();
+ int screenHeight = GetScreenHeight();
+
+ if ((cameraMode == CAMERA_FIRST_PERSON) ||
+ (cameraMode == CAMERA_THIRD_PERSON))
+ {
+ HideCursor();
+
+ if (mousePosition.x < screenHeight/3) SetMousePosition((Vector2){ screenWidth - screenHeight/3, mousePosition.y });
+ else if (mousePosition.y < screenHeight/3) SetMousePosition((Vector2){ mousePosition.x, screenHeight - screenHeight/3 });
+ else if (mousePosition.x > (screenWidth - screenHeight/3)) SetMousePosition((Vector2){ screenHeight/3, mousePosition.y });
+ else if (mousePosition.y > (screenHeight - screenHeight/3)) SetMousePosition((Vector2){ mousePosition.x, screenHeight/3 });
+ else
+ {
+ mousePositionDelta.x = mousePosition.x - previousMousePosition.x;
+ mousePositionDelta.y = mousePosition.y - previousMousePosition.y;
+ }
+ }
+ else // CAMERA_FREE, CAMERA_ORBITAL
+ {
+ ShowCursor();
+
+ mousePositionDelta.x = mousePosition.x - previousMousePosition.x;
+ mousePositionDelta.y = mousePosition.y - previousMousePosition.y;
+ }
+
+ // NOTE: We GetMousePosition() again because it can be modified by a previous SetMousePosition() call
+ // If using directly mousePosition variable we have problems on CAMERA_FIRST_PERSON and CAMERA_THIRD_PERSON
+ previousMousePosition = GetMousePosition();
+ }
+
+ // Support for multiple automatic camera modes
+ switch (cameraMode)
+ {
+ case CAMERA_FREE:
+ {
+ // Camera zoom
+ if ((cameraTargetDistance < CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
+ {
+ cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+
+ if (cameraTargetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP;
+ }
+ // Camera looking down
+ else if ((camera->position.y > camera->target.y) && (cameraTargetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
+ {
+ camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ }
+ else if ((camera->position.y > camera->target.y) && (camera->target.y >= 0))
+ {
+ camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+
+ // if (camera->target.y < 0) camera->target.y = -0.001;
+ }
+ else if ((camera->position.y > camera->target.y) && (camera->target.y < 0) && (mouseWheelMove > 0))
+ {
+ cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+ if (cameraTargetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP;
+ }
+ // Camera looking up
+ else if ((camera->position.y < camera->target.y) && (cameraTargetDistance == CAMERA_FREE_DISTANCE_MAX_CLAMP) && (mouseWheelMove < 0))
+ {
+ camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ }
+ else if ((camera->position.y < camera->target.y) && (camera->target.y <= 0))
+ {
+ camera->target.x += mouseWheelMove*(camera->target.x - camera->position.x)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.y += mouseWheelMove*(camera->target.y - camera->position.y)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+ camera->target.z += mouseWheelMove*(camera->target.z - camera->position.z)*CAMERA_MOUSE_SCROLL_SENSITIVITY/cameraTargetDistance;
+
+ // if (camera->target.y > 0) camera->target.y = 0.001;
+ }
+ else if ((camera->position.y < camera->target.y) && (camera->target.y > 0) && (mouseWheelMove > 0))
+ {
+ cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+ if (cameraTargetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP;
+ }
+
+ // Input keys checks
+ if (panKey)
+ {
+ if (altKey) // Alternative key behaviour
+ {
+ if (szoomKey)
+ {
+ // Camera smooth zoom
+ cameraTargetDistance += (mousePositionDelta.y*CAMERA_FREE_SMOOTH_ZOOM_SENSITIVITY);
+ }
+ else
+ {
+ // Camera rotation
+ cameraAngle.x += mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY;
+ cameraAngle.y += mousePositionDelta.y*-CAMERA_FREE_MOUSE_SENSITIVITY;
+
+ // Angle clamp
+ if (cameraAngle.y > CAMERA_FREE_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FREE_MIN_CLAMP*DEG2RAD;
+ else if (cameraAngle.y < CAMERA_FREE_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FREE_MAX_CLAMP*DEG2RAD;
+ }
+ }
+ else
+ {
+ // Camera panning
+ camera->target.x += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*cos(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*sin(cameraAngle.x)*sin(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER);
+ camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cos(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER);
+ camera->target.z += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*sin(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cos(cameraAngle.x)*sin(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER);
+ }
+ }
+
+ } break;
+ case CAMERA_ORBITAL:
+ {
+ cameraAngle.x += CAMERA_ORBITAL_SPEED; // Camera orbit angle
+ cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY); // Camera zoom
+
+ // Camera distance clamp
+ if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP;
+
+ } break;
+ case CAMERA_FIRST_PERSON:
+ case CAMERA_THIRD_PERSON:
+ {
+ camera->position.x += (sin(cameraAngle.x)*direction[MOVE_BACK] -
+ sin(cameraAngle.x)*direction[MOVE_FRONT] -
+ cos(cameraAngle.x)*direction[MOVE_LEFT] +
+ cos(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
+
+ camera->position.y += (sin(cameraAngle.y)*direction[MOVE_FRONT] -
+ sin(cameraAngle.y)*direction[MOVE_BACK] +
+ 1.0f*direction[MOVE_UP] - 1.0f*direction[MOVE_DOWN])/PLAYER_MOVEMENT_SENSITIVITY;
+
+ camera->position.z += (cos(cameraAngle.x)*direction[MOVE_BACK] -
+ cos(cameraAngle.x)*direction[MOVE_FRONT] +
+ sin(cameraAngle.x)*direction[MOVE_LEFT] -
+ sin(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY;
+
+ bool isMoving = false; // Required for swinging
+
+ for (int i = 0; i < 6; i++) if (direction[i]) { isMoving = true; break; }
+
+ // Camera orientation calculation
+ cameraAngle.x += (mousePositionDelta.x*-CAMERA_MOUSE_MOVE_SENSITIVITY);
+ cameraAngle.y += (mousePositionDelta.y*-CAMERA_MOUSE_MOVE_SENSITIVITY);
+
+ if (cameraMode == CAMERA_THIRD_PERSON)
+ {
+ // Angle clamp
+ if (cameraAngle.y > CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MIN_CLAMP*DEG2RAD;
+ else if (cameraAngle.y < CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_THIRD_PERSON_MAX_CLAMP*DEG2RAD;
+
+ // Camera zoom
+ cameraTargetDistance -= (mouseWheelMove*CAMERA_MOUSE_SCROLL_SENSITIVITY);
+
+ // Camera distance clamp
+ if (cameraTargetDistance < CAMERA_THIRD_PERSON_DISTANCE_CLAMP) cameraTargetDistance = CAMERA_THIRD_PERSON_DISTANCE_CLAMP;
+
+ // Camera is always looking at player
+ camera->target.x = camera->position.x + CAMERA_THIRD_PERSON_OFFSET.x*cos(cameraAngle.x) + CAMERA_THIRD_PERSON_OFFSET.z*sin(cameraAngle.x);
+ camera->target.y = camera->position.y + CAMERA_THIRD_PERSON_OFFSET.y;
+ camera->target.z = camera->position.z + CAMERA_THIRD_PERSON_OFFSET.z*sin(cameraAngle.x) - CAMERA_THIRD_PERSON_OFFSET.x*sin(cameraAngle.x);
+ }
+ else // CAMERA_FIRST_PERSON
+ {
+ // Angle clamp
+ if (cameraAngle.y > CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MIN_CLAMP*DEG2RAD;
+ else if (cameraAngle.y < CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD) cameraAngle.y = CAMERA_FIRST_PERSON_MAX_CLAMP*DEG2RAD;
+
+ // Camera is always looking at player
+ camera->target.x = camera->position.x - sin(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE;
+ camera->target.y = camera->position.y + sin(cameraAngle.y)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE;
+ camera->target.z = camera->position.z - cos(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE;
+
+ if (isMoving) swingCounter++;
+
+ // Camera position update
+ // NOTE: On CAMERA_FIRST_PERSON player Y-movement is limited to player 'eyes position'
+ camera->position.y = playerEyesPosition - sin(swingCounter/CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER)/CAMERA_FIRST_PERSON_STEP_DIVIDER;
+
+ camera->up.x = sin(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER;
+ camera->up.z = -sin(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER;
+ }
+ } break;
+ default: break;
+ }
+
+ // Update camera position with changes
+ if ((cameraMode == CAMERA_FREE) ||
+ (cameraMode == CAMERA_ORBITAL) ||
+ (cameraMode == CAMERA_THIRD_PERSON))
+ {
+ // TODO: It seems camera->position is not correctly updated or some rounding issue makes the camera move straight to camera->target...
+ camera->position.x = sin(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.x;
+ if (cameraAngle.y <= 0.0f) camera->position.y = sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
+ else camera->position.y = -sin(cameraAngle.y)*cameraTargetDistance*sin(cameraAngle.y) + camera->target.y;
+ camera->position.z = cos(cameraAngle.x)*cameraTargetDistance*cos(cameraAngle.y) + camera->target.z;
+ }
+}
+
+// Set camera pan key to combine with mouse movement (free camera)
+void SetCameraPanControl(int panKey) { cameraPanControlKey = panKey; }
+
+// Set camera alt key to combine with mouse movement (free camera)
+void SetCameraAltControl(int altKey) { cameraAltControlKey = altKey; }
+
+// Set camera smooth zoom key to combine with mouse (free camera)
+void SetCameraSmoothZoomControl(int szKey) { cameraSmoothZoomControlKey = szKey; }
+
+// Set camera move controls (1st person and 3rd person cameras)
+void SetCameraMoveControls(int frontKey, int backKey, int rightKey, int leftKey, int upKey, int downKey)
+{
+ cameraMoveControl[MOVE_FRONT] = frontKey;
+ cameraMoveControl[MOVE_BACK] = backKey;
+ cameraMoveControl[MOVE_RIGHT] = rightKey;
+ cameraMoveControl[MOVE_LEFT] = leftKey;
+ cameraMoveControl[MOVE_UP] = upKey;
+ cameraMoveControl[MOVE_DOWN] = downKey;
+}
+
+#endif // CAMERA_IMPLEMENTATION
diff --git a/src/core.c b/src/core.c
index a3253d79..3bd85fdd 100644
--- a/src/core.c
+++ b/src/core.c
@@ -18,6 +18,10 @@
*
* On PLATFORM_RPI, graphic device is managed by EGL and input system is coded in raw mode.
*
+* Module Configuration Flags:
+*
+* RL_LOAD_DEFAULT_FONT - Use external module functions to load default raylib font (module: text)
+*
* Copyright (c) 2014 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
@@ -39,13 +43,20 @@
#include "raylib.h" // raylib main header
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
-#include "utils.h" // TraceLog() function
- // NOTE: Includes Android fopen map, InitAssetManager()
-
+#include "utils.h" // Includes Android fopen map, InitAssetManager(), TraceLog()
+
#define RAYMATH_IMPLEMENTATION // Use raymath as a header-only library (includes implementation)
#define RAYMATH_EXTERN_INLINE // Compile raymath functions as static inline (remember, it's a compiler hint)
#include "raymath.h" // Required for Vector3 and Matrix functions
+#define GESTURES_IMPLEMENTATION
+#include "gestures.h" // Gestures detection functionality
+
+#if !defined(PLATFORM_ANDROID)
+ #define CAMERA_IMPLEMENTATION
+ #include "camera.h" // Camera system functionality
+#endif
+
#include <stdio.h> // Standard input / output lib
#include <stdlib.h> // Declares malloc() and free() for memory management, rand(), atexit()
#include <stdint.h> // Required for typedef unsigned long long int uint64_t, used by hi-res timer
@@ -115,14 +126,16 @@
//#define DEFAULT_KEYBOARD_DEV "/dev/input/eventN"
//#define DEFAULT_MOUSE_DEV "/dev/input/eventN"
//#define DEFAULT_GAMEPAD_DEV "/dev/input/eventN"
-
+
#define MOUSE_SENSITIVITY 0.8f
-
- #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
+#define MAX_GAMEPADS 4 // 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)
+
+#define RL_LOAD_DEFAULT_FONT // Load default font on window initialization (module: text)
+
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@@ -161,15 +174,11 @@ static int defaultKeyboardMode; // Used to store default keyboar
// Mouse input variables
static int mouseStream = -1; // Mouse device file descriptor
static bool mouseReady = false; // Flag to know if mouse is ready
-pthread_t mouseThreadId; // Mouse reading thread id
+static pthread_t mouseThreadId; // Mouse reading thread id
// Gamepad input variables
-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_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Gamepad buttons state
-float gamepadAxisValues[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
+static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor
+static pthread_t gamepadThreadId; // Gamepad reading thread id
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
@@ -194,18 +203,23 @@ static Matrix downscaleView; // Matrix to downscale view (in case
static const char *windowTitle; // Window text title...
static bool cursorOnScreen = false; // Tracks if cursor is inside client area
-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 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
+// Register keyboard states
+static char previousKeyState[512] = { 0 }; // Registers previous frame key state
+static char currentKeyState[512] = { 0 }; // Registers current frame key state
-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
+// Register mouse states
+static char previousMouseState[3] = { 0 }; // Registers previous mouse button state
+static char currentMouseState[3] = { 0 }; // Registers current mouse button state
+static int previousMouseWheelY = 0; // Registers previous mouse wheel variation
+static int currentMouseWheelY = 0; // Registers current mouse wheel variation
-static int previousMouseWheelY = 0; // Required to track mouse wheel variation
-static int currentMouseWheelY = 0; // Required to track mouse wheel variation
+// Register gamepads states
+static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready
+static float gamepadAxisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
+static char previousGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS] = { 0 };
+static char currentGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS] = { 0 };
+// Keyboard configuration
static int exitKey = KEY_ESCAPE; // Default exit key (ESC)
static int lastKeyPressed = -1; // Register last key pressed
@@ -231,8 +245,10 @@ static bool showLogo = false; // Track if showing logo at init is
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by core)
//----------------------------------------------------------------------------------
+#if defined(RL_LOAD_DEFAULT_FONT)
extern void LoadDefaultFont(void); // [Module: text] Loads default font on InitWindow()
extern void UnloadDefaultFont(void); // [Module: text] Unloads default font from GPU memory
+#endif
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
@@ -293,7 +309,7 @@ static void *GamepadThread(void *arg); // Mouse reading thread
// Initialize Window and Graphics Context (OpenGL)
void InitWindow(int width, int height, const char *title)
{
- TraceLog(INFO, "Initializing raylib (v1.5.0)");
+ TraceLog(INFO, "Initializing raylib (v1.6.0)");
// Store window title (could be useful...)
windowTitle = title;
@@ -301,9 +317,11 @@ void InitWindow(int width, int height, const char *title)
// Init graphics device (display device and OpenGL context)
InitGraphicsDevice(width, height);
- // Load default font for convenience
+#if defined(RL_LOAD_DEFAULT_FONT)
+ // Load default font
// NOTE: External function (defined in module: text)
LoadDefaultFont();
+#endif
// Init hi-res timer
InitTimer();
@@ -325,7 +343,7 @@ void InitWindow(int width, int height, const char *title)
emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenInputCallback);
emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenInputCallback);
emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenInputCallback);
-
+
// TODO: Add gamepad support (not provided by GLFW3 on emscripten)
//emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenInputCallback);
//emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenInputCallback);
@@ -347,7 +365,7 @@ void InitWindow(int width, int height, const char *title)
// Android activity initialization
void InitWindow(int width, int height, struct android_app *state)
{
- TraceLog(INFO, "Initializing raylib (v1.5.0)");
+ TraceLog(INFO, "Initializing raylib (v1.6.0)");
app_dummy();
@@ -386,7 +404,7 @@ void InitWindow(int width, int height, struct android_app *state)
//state->userData = &engine;
app->onAppCmd = AndroidCommandCallback;
app->onInputEvent = AndroidInputCallback;
-
+
InitAssetManager(app->activity->assetManager);
TraceLog(INFO, "Android app initialized successfully");
@@ -410,7 +428,9 @@ void InitWindow(int width, int height, struct android_app *state)
// Close Window and Terminate Context
void CloseWindow(void)
{
+#if defined(RL_LOAD_DEFAULT_FONT)
UnloadDefaultFont();
+#endif
rlglClose(); // De-init rlgl
@@ -439,7 +459,7 @@ void CloseWindow(void)
eglTerminate(display);
display = EGL_NO_DISPLAY;
- }
+ }
#endif
#if defined(PLATFORM_RPI)
@@ -519,7 +539,7 @@ void BeginDrawing(void)
currentTime = GetTime(); // Number of elapsed seconds since InitTimer() was called
updateTime = currentTime - previousTime;
previousTime = currentTime;
-
+
rlClearScreenBuffers(); // Clear current framebuffers
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
rlMultMatrixf(MatrixToFloat(downscaleView)); // If downscale required, apply it here
@@ -535,7 +555,7 @@ void EndDrawing(void)
SwapBuffers(); // Copy back buffer to front buffer
PollInputEvents(); // Poll user events
-
+
// Frame time control system
currentTime = GetTime();
drawTime = currentTime - previousTime;
@@ -567,9 +587,9 @@ void Begin2dMode(Camera2D camera)
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));
}
@@ -585,14 +605,14 @@ void End2dMode(void)
void Begin3dMode(Camera camera)
{
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
-
+
if (IsVrDeviceReady()) BeginVrDrawing();
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
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.01*tan(camera.fovy*PI/360.0);
@@ -607,15 +627,15 @@ void Begin3dMode(Camera camera)
// Setup Camera view
Matrix 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
void End3dMode(void)
-{
+{
rlglDraw(); // Process internal buffers (update + draw)
-
+
if (IsVrDeviceReady()) EndVrDrawing();
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
@@ -625,7 +645,7 @@ 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
}
@@ -637,16 +657,16 @@ void BeginTextureMode(RenderTexture2D target)
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);
-
+ 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);
+ 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)
@@ -664,10 +684,10 @@ void EndTextureMode(void)
// 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);
@@ -679,7 +699,8 @@ void EndTextureMode(void)
// Set target FPS for the game
void SetTargetFPS(int fps)
{
- targetTime = 1.0/(double)fps;
+ if (fps < 1) targetTime = 0.0;
+ else targetTime = 1.0/(double)fps;
TraceLog(INFO, "Target time per frame: %02.03f milliseconds", (float)targetTime*1000);
}
@@ -693,7 +714,7 @@ float GetFPS(void)
// Returns time in seconds for one frame
float GetFrameTime(void)
{
- // As we are operate quite a lot with frameTime,
+ // As we are operate quite a lot with frameTime,
// it could be no stable, so we round it before passing it around
// NOTE: There are still problems with high framerates (>500fps)
double roundedFrameTime = round(frameTime*10000)/10000.0;
@@ -727,7 +748,7 @@ float *VectorToFloat(Vector3 vec)
}
// Converts Matrix to float array
-// NOTE: Returned vector is a transposed version of the Matrix struct,
+// 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)
@@ -791,7 +812,7 @@ Color Fade(Color color, float alpha)
{
if (alpha < 0.0f) alpha = 0.0f;
else if (alpha > 1.0f) alpha = 1.0f;
-
+
float colorAlpha = (float)color.a*alpha;
return (Color){color.r, color.g, color.b, (unsigned char)colorAlpha};
@@ -833,9 +854,9 @@ void ClearDroppedFiles(void)
if (dropFilesCount > 0)
{
for (int i = 0; i < dropFilesCount; i++) free(dropFilesPath[i]);
-
+
free(dropFilesPath);
-
+
dropFilesCount = 0;
}
}
@@ -846,7 +867,7 @@ void ClearDroppedFiles(void)
void StorageSaveValue(int position, int value)
{
FILE *storageFile = NULL;
-
+
char path[128];
#if defined(PLATFORM_ANDROID)
strcpy(path, internalDataPath);
@@ -857,7 +878,7 @@ void StorageSaveValue(int position, int value)
#endif
// Try open existing file to append data
- storageFile = fopen(path, "rb+");
+ storageFile = fopen(path, "rb+");
// If file doesn't exist, create a new storage data file
if (!storageFile) storageFile = fopen(path, "wb");
@@ -869,14 +890,14 @@ void StorageSaveValue(int position, int value)
fseek(storageFile, 0, SEEK_END);
int fileSize = ftell(storageFile); // Size in bytes
fseek(storageFile, 0, SEEK_SET);
-
+
if (fileSize < (position*4)) TraceLog(WARNING, "Storage position could not be found");
else
{
fseek(storageFile, (position*4), SEEK_SET);
fwrite(&value, 1, 4, storageFile);
}
-
+
fclose(storageFile);
}
}
@@ -886,7 +907,7 @@ void StorageSaveValue(int position, int value)
int StorageLoadValue(int position)
{
int value = 0;
-
+
char path[128];
#if defined(PLATFORM_ANDROID)
strcpy(path, internalDataPath);
@@ -895,9 +916,9 @@ int StorageLoadValue(int position)
#else
strcpy(path, STORAGE_FILENAME);
#endif
-
+
// Try open existing file to append data
- FILE *storageFile = fopen(path, "rb");
+ FILE *storageFile = fopen(path, "rb");
if (!storageFile) TraceLog(WARNING, "Storage data file could not be found");
else
@@ -906,42 +927,42 @@ int StorageLoadValue(int position)
fseek(storageFile, 0, SEEK_END);
int fileSize = ftell(storageFile); // Size in bytes
rewind(storageFile);
-
+
if (fileSize < (position*4)) TraceLog(WARNING, "Storage position could not be found");
else
{
fseek(storageFile, (position*4), SEEK_SET);
fread(&value, 1, 4, storageFile);
}
-
+
fclose(storageFile);
}
-
+
return value;
}
// Returns a ray trace from mouse position
Ray GetMouseRay(Vector2 mousePosition, Camera camera)
-{
+{
Ray ray;
-
+
// Calculate normalized device coordinates
// NOTE: y value is negative
float x = (2.0f*mousePosition.x)/(float)GetScreenWidth() - 1.0f;
float y = 1.0f - (2.0f*mousePosition.y)/(float)GetScreenHeight();
float z = 1.0f;
-
+
// Store values in a vector
Vector3 deviceCoords = { x, y, 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(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 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.
@@ -949,10 +970,10 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
// 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);
@@ -961,56 +982,56 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
// 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 };
-
+
// 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};
#endif
-
+
// Calculate normalized direction vector
Vector3 direction = VectorSubtract(farPoint, nearPoint);
VectorNormalize(&direction);
-
+
// Apply calculated vectors to ray
ray.position = camera.position;
ray.direction = direction;
-
+
return ray;
}
// Returns the screen space position from a 3d world space position
Vector2 GetWorldToScreen(Vector3 position, Camera camera)
-{
+{
// 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 (and transpose it)
Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
MatrixTranspose(&matView);
-
+
// Convert world position vector to quaternion
Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
-
+
// Transform world position to view
QuaternionTransform(&worldPos, matView);
-
+
// Transform result to projection (clip space position)
QuaternionTransform(&worldPos, matProj);
-
+
// Calculate normalized device coordinates (inverted y)
- Vector3 ndcPos = { worldPos.x / worldPos.w, -worldPos.y / worldPos.w, worldPos.z / worldPos.z };
-
+ Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.z };
+
// Calculate 2d screen position vector
Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)GetScreenWidth(), (ndcPos.y + 1.0f)/2.0f*(float)GetScreenHeight() };
-
+
return screenPosition;
}
@@ -1136,34 +1157,32 @@ bool IsCursorHidden()
bool IsGamepadAvailable(int gamepad)
{
bool result = false;
-
-#if defined(PLATFORM_RPI)
+
if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad]) result = true;
-#else
- if (glfwJoystickPresent(gamepad) == 1) result = true;
-#endif
return result;
}
+// Return gamepad internal name id
+const char *GetGamepadName(int gamepad)
+{
+#if defined(PLATFORM_DESKTOP)
+ if (glfwJoystickPresent(gamepad) == 1) return glfwGetJoystickName(gamepad);
+ else return NULL;
+#else
+ return NULL;
+#endif
+}
+
// Return axis movement vector for a gamepad
float GetGamepadAxisMovement(int gamepad, int axis)
{
float value = 0;
-
-#if defined(PLATFORM_RPI)
- if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad])
+
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (axis < MAX_GAMEPAD_AXIS))
{
- if (axis < MAX_GAMEPAD_AXIS) value = gamepadAxisValues[gamepad][axis];
+ value = gamepadAxisState[gamepad][axis];
}
-#else
- const float *axes;
- int axisCount = 0;
-
- axes = glfwGetJoystickAxes(gamepad, &axisCount);
-
- if (axis < axisCount) value = axes[axis];
-#endif
return value;
}
@@ -1172,15 +1191,10 @@ float GetGamepadAxisMovement(int gamepad, int axis)
bool IsGamepadButtonPressed(int gamepad, int button)
{
bool pressed = false;
-
- currentGamepadState[button] = IsGamepadButtonDown(gamepad, button);
-
- if (currentGamepadState[button] != previousGamepadState[button])
- {
- if (currentGamepadState[button]) pressed = true;
- previousGamepadState[button] = currentGamepadState[button];
- }
- else pressed = false;
+
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) &&
+ (currentGamepadState[gamepad][button] == 1)) pressed = true;
return pressed;
}
@@ -1189,21 +1203,10 @@ bool IsGamepadButtonPressed(int gamepad, int button)
bool IsGamepadButtonDown(int gamepad, int button)
{
bool result = false;
-
-#if defined(PLATFORM_RPI)
- // Get gamepad buttons information
- if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (gamepadButtons[gamepad][button] == 1)) result = true;
- else result = false;
-#else
- const unsigned char *buttons;
- int buttonsCount;
-
- buttons = glfwGetJoystickButtons(gamepad, &buttonsCount);
- if ((buttons != NULL) && (buttons[button] == GLFW_PRESS)) result = true;
- else result = false;
-#endif
-
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] == 1)) result = true;
+
return result;
}
@@ -1211,15 +1214,10 @@ bool IsGamepadButtonDown(int gamepad, int button)
bool IsGamepadButtonReleased(int gamepad, int button)
{
bool released = false;
-
- currentGamepadState[button] = IsGamepadButtonUp(gamepad, button);
-
- if (currentGamepadState[button] != previousGamepadState[button])
- {
- if (currentGamepadState[button]) released = true;
- previousGamepadState[button] = currentGamepadState[button];
- }
- else released = false;
+
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) &&
+ (currentGamepadState[gamepad][button] == 0)) released = true;
return released;
}
@@ -1229,19 +1227,8 @@ bool IsGamepadButtonUp(int gamepad, int button)
{
bool result = false;
-#if defined(PLATFORM_RPI)
- // Get gamepad buttons information
- if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (gamepadButtons[gamepad][button] == 0)) result = true;
- else result = false;
-#else
- const unsigned char *buttons;
- int buttonsCount;
-
- buttons = glfwGetJoystickButtons(gamepad, &buttonsCount);
-
- if ((buttons != NULL) && (buttons[button] == GLFW_RELEASE)) result = true;
- else result = false;
-#endif
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] == 0)) result = true;
return result;
}
@@ -1252,7 +1239,7 @@ bool IsGamepadButtonUp(int gamepad, int button)
bool IsMouseButtonPressed(int button)
{
bool pressed = false;
-
+
#if defined(PLATFORM_ANDROID)
if (IsGestureDetected(GESTURE_TAP)) pressed = true;
#else
@@ -1266,13 +1253,13 @@ bool IsMouseButtonPressed(int button)
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;
}
@@ -1280,7 +1267,7 @@ bool IsMouseButtonDown(int button)
bool IsMouseButtonReleased(int button)
{
bool released = false;
-
+
#if !defined(PLATFORM_ANDROID)
if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 0)) released = true;
#endif
@@ -1292,7 +1279,7 @@ bool IsMouseButtonReleased(int button)
bool IsMouseButtonUp(int button)
{
bool up = false;
-
+
#if !defined(PLATFORM_ANDROID)
if (GetMouseButtonStatus(button) == 0) up = true;
#endif
@@ -1377,7 +1364,7 @@ int GetTouchY(void)
Vector2 GetTouchPosition(int index)
{
Vector2 position = { -1.0f, -1.0f };
-
+
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
if (index < MAX_TOUCH_POINTS) position = touchPosition[index];
else TraceLog(WARNING, "Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
@@ -1472,23 +1459,28 @@ static void InitGraphicsDevice(int width, int height)
displayHeight = screenHeight;
#endif // defined(PLATFORM_WEB)
- glfwDefaultWindowHints(); // Set default windows hints
+ glfwDefaultWindowHints(); // Set default windows hints
- glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable
- //glfwWindowHint(GLFW_DECORATED, GL_TRUE); // Border and buttons on Window
- //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits
- //glfwWindowHint(GLFW_DEPTH_BITS, 16); // Depthbuffer bits (24 by default)
- //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
+ if (configFlags & FLAG_RESIZABLE_WINDOW)
+ {
+ glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // Resizable window
+ }
+ else glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable
+
+ //glfwWindowHint(GLFW_DECORATED, GL_TRUE); // Border and buttons on Window
+ //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits
+ //glfwWindowHint(GLFW_DEPTH_BITS, 16); // Depthbuffer bits (24 by default)
+ //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
//glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // Default OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
- //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
+ //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
// NOTE: When asking for an OpenGL context version, most drivers provide highest supported version
// with forward compatibility to older OpenGL versions.
// For example, if using OpenGL 1.1, driver can provide a 3.3 context fordward compatible.
-
+
if (configFlags & FLAG_MSAA_4X_HINT)
{
- glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0
+ glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0
TraceLog(INFO, "Trying to enable MSAA x4");
}
@@ -1505,7 +1497,7 @@ static void InitGraphicsDevice(int width, int height)
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
+ 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
@@ -1515,9 +1507,9 @@ static void InitGraphicsDevice(int width, int height)
if (fullscreen)
{
// Obtain recommended displayWidth/displayHeight from a valid videomode for the monitor
- int count;
+ int count;
const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count);
-
+
// Get closest videomode to desired screenWidth/screenHeight
for (int i = 0; i < count; i++)
{
@@ -1531,7 +1523,7 @@ static void InitGraphicsDevice(int width, int height)
}
}
}
-
+
TraceLog(WARNING, "Closest fullscreen videomode: %i x %i", displayWidth, displayHeight);
// NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
@@ -1540,12 +1532,12 @@ static void InitGraphicsDevice(int width, int height)
// by the sides to fit all monitor space...
// At this point we need to manage render size vs screen size
- // NOTE: This function uses and modifies global module variables:
+ // NOTE: This function uses and modifies global module variables:
// screenWidth/screenHeight - renderWidth/renderHeight - downscaleView
SetupFramebufferSize(displayWidth, displayHeight);
window = glfwCreateWindow(displayWidth, displayHeight, windowTitle, glfwGetPrimaryMonitor(), NULL);
-
+
// NOTE: Full-screen change, not working properly...
//glfwSetWindowMonitor(window, glfwGetPrimaryMonitor(), 0, 0, screenWidth, screenHeight, GLFW_DONT_CARE);
}
@@ -1553,15 +1545,15 @@ static void InitGraphicsDevice(int width, int height)
{
// No-fullscreen window creation
window = glfwCreateWindow(screenWidth, screenHeight, windowTitle, NULL, NULL);
-
+
#if defined(PLATFORM_DESKTOP)
// Center window on screen
int windowPosX = displayWidth/2 - screenWidth/2;
int windowPosY = displayHeight/2 - screenHeight/2;
-
+
if (windowPosX < 0) windowPosX = 0;
if (windowPosY < 0) windowPosY = 0;
-
+
glfwSetWindowPos(window, windowPosX, windowPosY);
#endif
renderWidth = screenWidth;
@@ -1604,7 +1596,7 @@ static void InitGraphicsDevice(int width, int height)
// NOTE: GLFW loader function is passed as parameter
rlglLoadExtensions(glfwGetProcAddress);
#endif
-
+
// Enables GPU v-sync, so frames are not limited to screen refresh rate (60Hz -> 60 FPS)
// If not set, swap interval uses GPU v-sync configuration
// Framerate can be setup using SetTargetFPS()
@@ -1613,8 +1605,6 @@ static void InitGraphicsDevice(int width, int height)
glfwSwapInterval(1);
TraceLog(INFO, "Trying to enable VSYNC");
}
-
- //glfwGetFramebufferSize(window, &renderWidth, &renderHeight); // Get framebuffer size of current window
#endif // defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
@@ -1637,13 +1627,13 @@ static void InitGraphicsDevice(int width, int height)
EGLint samples = 0;
EGLint sampleBuffer = 0;
- if (configFlags & FLAG_MSAA_4X_HINT)
+ if (configFlags & FLAG_MSAA_4X_HINT)
{
samples = 4;
sampleBuffer = 1;
TraceLog(INFO, "Trying to enable MSAA x4");
}
-
+
const EGLint framebufferAttribs[] =
{
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI?
@@ -1765,17 +1755,28 @@ static void InitGraphicsDevice(int width, int height)
#endif // defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
// Initialize OpenGL context (states and resources)
+ // NOTE: screenWidth and screenHeight not used, just stored as globals
rlglInit(screenWidth, screenHeight);
-
+
+#ifdef __APPLE__
+ // Get framebuffer size of current window
+ // NOTE: Required to handle HighDPI display correctly on OSX because framebuffer
+ // is automatically reasized to adapt to new DPI.
+ // When OS does that, it can be detected using GLFW3 callback: glfwSetFramebufferSizeCallback()
+ int fbWidth, fbHeight;
+ glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
+ rlViewport(renderOffsetX/2, renderOffsetY/2, fbWidth - renderOffsetX, fbHeight - renderOffsetY);
+#else
// Initialize screen viewport (area of the screen that you will actually draw to)
// NOTE: Viewport must be recalculated if screen is resized
rlViewport(renderOffsetX/2, renderOffsetY/2, renderWidth - renderOffsetX, renderHeight - renderOffsetY);
+#endif
// Initialize internal projection and modelview matrices
// NOTE: Default to orthographic projection mode with top-left corner at (0,0)
rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix
rlLoadIdentity(); // Reset current matrix (PROJECTION)
- rlOrtho(0, renderWidth - renderOffsetX, renderHeight - renderOffsetY, 0, 0.0f, 1.0f);
+ rlOrtho(0, renderWidth - renderOffsetX, renderHeight - renderOffsetY, 0, 0.0f, 1.0f);
rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW)
@@ -1789,7 +1790,7 @@ static void InitGraphicsDevice(int width, int height)
// Compute framebuffer size relative to screen size and display size
// NOTE: Global variables renderWidth/renderHeight and renderOffsetX/renderOffsetY can be modified
static void SetupFramebufferSize(int displayWidth, int displayHeight)
-{
+{
// Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var)
if ((screenWidth > displayWidth) || (screenHeight > displayHeight))
{
@@ -1928,7 +1929,7 @@ 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;
@@ -1950,8 +1951,45 @@ static void PollInputEvents(void)
previousMouseWheelY = currentMouseWheelY;
currentMouseWheelY = 0;
+
+ // Register gamepads buttons events
+ for (int i = 0; i < MAX_GAMEPADS; i++)
+ {
+ if (glfwJoystickPresent(i)) // Check if gamepad is available
+ {
+ gamepadReady[i] = true;
+
+ // Register previous gamepad states
+ for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) previousGamepadState[i][k] = currentGamepadState[i][k];
+
+ // Get current gamepad state
+ // NOTE: There is no callback available, so we get it manually
+ const unsigned char *buttons;
+ int buttonsCount;
+
+ buttons = glfwGetJoystickButtons(i, &buttonsCount);
+
+ for (int k = 0; (buttons != NULL) && (k < buttonsCount) && (buttonsCount < MAX_GAMEPAD_BUTTONS); k++)
+ {
+ if (buttons[i] == GLFW_PRESS) currentGamepadState[i][k] = 1;
+ else currentGamepadState[i][k] = 0;
+ }
+
+ // Get current axis state
+ const float *axes;
+ int axisCount = 0;
+
+ axes = glfwGetJoystickAxes(i, &axisCount);
+
+ for (int k = 0; (axes != NULL) && (k < axisCount) && (k < MAX_GAMEPAD_AXIS); k++)
+ {
+ gamepadAxisState[i][k] = axes[k];
+ }
+ }
+ else gamepadReady[i] = false;
+ }
- glfwPollEvents(); // Register keyboard/mouse events... and window events!
+ glfwPollEvents(); // Register keyboard/mouse events (callbacks)... and window events!
#endif
#if defined(PLATFORM_ANDROID)
@@ -2008,7 +2046,7 @@ static void TakeScreenshot(void)
unsigned char *imgData = rlglReadScreenPixels(renderWidth, renderHeight);
sprintf(buffer, "screenshot%03i.png", shotNum);
-
+
// Save image as PNG
WritePNG(buffer, imgData, renderWidth, renderHeight, 4);
@@ -2043,12 +2081,9 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
// NOTE: Before closing window, while loop must be left!
}
#if defined(PLATFORM_DESKTOP)
- else if (key == GLFW_KEY_F12 && action == GLFW_PRESS)
- {
- TakeScreenshot();
- }
+ else if (key == GLFW_KEY_F12 && action == GLFW_PRESS) TakeScreenshot();
#endif
- else
+ else
{
currentKeyState[key] = action;
if (action == GLFW_PRESS) lastKeyPressed = key;
@@ -2059,27 +2094,27 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
{
currentMouseState[button] = action;
-
+
#define ENABLE_MOUSE_GESTURES
#if defined(ENABLE_MOUSE_GESTURES)
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent;
-
+
// Register touch actions
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_DOWN;
else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_UP;
-
+
// NOTE: TOUCH_MOVE event is registered in MouseCursorPosCallback()
-
+
// Assign a pointer ID
gestureEvent.pointerId[0] = 0;
-
+
// Register touch points count
gestureEvent.pointCount = 1;
-
+
// Register touch points position, only one point registered
gestureEvent.position[0] = GetMousePosition();
-
+
// Normalize gestureEvent.position[0] for screenWidth and screenHeight
gestureEvent.position[0].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
@@ -2098,20 +2133,20 @@ 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;
-
+
// 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].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
// Gesture data is sent to gestures system for processing
@@ -2152,7 +2187,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height)
screenHeight = height;
renderWidth = width;
renderHeight = height;
-
+
// NOTE: Postprocessing texture is not scaled to new size
}
@@ -2171,9 +2206,9 @@ static void WindowIconifyCallback(GLFWwindow* window, int iconified)
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
{
ClearDroppedFiles();
-
+
dropFilesPath = (char **)malloc(sizeof(char *)*count);
-
+
for (int i = 0; i < count; i++)
{
dropFilesPath[i] = (char *)malloc(sizeof(char)*256); // Max path length set to 256 char
@@ -2223,9 +2258,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Init graphics device (display device and OpenGL context)
InitGraphicsDevice(screenWidth, screenHeight);
- // Load default font for convenience
+ #if defined(RL_LOAD_DEFAULT_FONT)
+ // Load default font
// NOTE: External function (defined in module: text)
LoadDefaultFont();
+ #endif
// TODO: GPU assets reload in case of lost focus (lost context)
// NOTE: This problem has been solved just unbinding and rebinding context from display
@@ -2235,7 +2272,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
for (int i = 0; i < assetsCount; i++)
{
// TODO: Unload old asset if required
-
+
// Load texture again to pointed texture
(*textureAsset + i) = LoadTexture(assetPath[i]);
}
@@ -2278,7 +2315,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// NOTE 2: In some cases (too many context loaded), OS could unload context automatically... :(
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(display, surface);
-
+
contextRebindRequired = true;
TraceLog(INFO, "APP_CMD_TERM_WINDOW");
@@ -2315,7 +2352,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
//http://developer.android.com/ndk/reference/index.html
-
+
int type = AInputEvent_getType(event);
if (type == AINPUT_EVENT_TYPE_MOTION)
@@ -2323,7 +2360,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
// Get first touch position
touchPosition[0].x = AMotionEvent_getX(event, 0);
touchPosition[0].y = AMotionEvent_getY(event, 0);
-
+
// Get second touch position
touchPosition[1].x = AMotionEvent_getX(event, 1);
touchPosition[1].y = AMotionEvent_getY(event, 1);
@@ -2332,7 +2369,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
int32_t keycode = AKeyEvent_getKeyCode(event);
//int32_t AKeyEvent_getMetaState(event);
-
+
// Save current button and its state
currentButtonState[keycode] = AKeyEvent_getAction(event); // Down = 0, Up = 1
@@ -2344,7 +2381,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
// NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
// Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
return 0;
- }
+ }
else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
{
// Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
@@ -2356,36 +2393,36 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
return 0;
}
}
-
+
int32_t action = AMotionEvent_getAction(event);
unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
-
+
GestureEvent gestureEvent;
-
+
// Register touch actions
if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_DOWN;
else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_UP;
else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_MOVE;
-
+
// Register touch points count
gestureEvent.pointCount = AMotionEvent_getPointerCount(event);
-
+
// Register touch points id
gestureEvent.pointerId[0] = AMotionEvent_getPointerId(event, 0);
gestureEvent.pointerId[1] = AMotionEvent_getPointerId(event, 1);
-
+
// Register touch points position
// NOTE: Only two points registered
gestureEvent.position[0] = (Vector2){ AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0) };
gestureEvent.position[1] = (Vector2){ AMotionEvent_getX(event, 1), AMotionEvent_getY(event, 1) };
-
+
// Normalize gestureEvent.position[x] for screenWidth and screenHeight
- gestureEvent.position[0].x /= (float)GetScreenWidth();
+ gestureEvent.position[0].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
-
- gestureEvent.position[1].x /= (float)GetScreenWidth();
+
+ gestureEvent.position[1].x /= (float)GetScreenWidth();
gestureEvent.position[1].y /= (float)GetScreenHeight();
-
+
// Gesture data is sent to gestures system for processing
ProcessGestureEvent(gestureEvent);
@@ -2396,13 +2433,13 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
#if defined(PLATFORM_WEB)
static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData)
{
- //isFullscreen: int e->isFullscreen
- //fullscreenEnabled: int e->fullscreenEnabled
+ //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
-
+
if (e->isFullscreen)
{
TraceLog(INFO, "Canvas scaled to fullscreen. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight);
@@ -2411,7 +2448,7 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte
{
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 0;
@@ -2431,33 +2468,33 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent
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" : "");
- for(int i = 0; i < event->numTouches; ++i)
+ 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;
// 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!
@@ -2468,12 +2505,12 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent
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].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
-
- gestureEvent.position[1].x /= (float)GetScreenWidth();
+
+ gestureEvent.position[1].x /= (float)GetScreenWidth();
gestureEvent.position[1].y /= (float)GetScreenHeight();
// Gesture data is sent to gestures system for processing
@@ -2519,7 +2556,7 @@ static void InitKeyboard(void)
else
{
// We reconfigure keyboard mode to get:
- // - scancodes (K_RAW)
+ // - scancodes (K_RAW)
// - keycodes (K_MEDIUMRAW)
// - ASCII chars (K_XLATE)
// - UNICODE chars (K_UNICODE)
@@ -2535,7 +2572,7 @@ static void InitKeyboard(void)
static void ProcessKeyboard(void)
{
#define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read
-
+
// Keyboard input polling (fill keys[256] array with status)
int bufferByteCount = 0; // Bytes available on the buffer
char keysBuffer[MAX_KEYBUFFER_SIZE]; // Max keys to be read at a time
@@ -2550,7 +2587,7 @@ static void ProcessKeyboard(void)
for (int i = 0; i < bufferByteCount; i++)
{
TraceLog(DEBUG, "Bytes on keysBuffer: %i", bufferByteCount);
-
+
//printf("Key(s) bytes: ");
//for (int i = 0; i < bufferByteCount; i++) printf("0x%02x ", keysBuffer[i]);
//printf("\n");
@@ -2584,7 +2621,7 @@ static void ProcessKeyboard(void)
case 0x34: currentKeyState[301] = 1; break; // raylib KEY_F12
default: break;
}
-
+
if (keysBuffer[i + 2] == 0x5b) i += 4;
else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
}
@@ -2601,7 +2638,7 @@ static void ProcessKeyboard(void)
i += 3; // Jump to next key
}
-
+
// NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
}
}
@@ -2611,7 +2648,7 @@ static void ProcessKeyboard(void)
else
{
TraceLog(DEBUG, "Pressed key (ASCII): 0x%02x", keysBuffer[i]);
-
+
// Translate lowercase a-z letters to A-Z
if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
{
@@ -2620,10 +2657,10 @@ static void ProcessKeyboard(void)
else currentKeyState[(int)keysBuffer[i]] = 1;
}
}
-
+
// Check exit key (same functionality as GLFW3 KeyCallback())
if (currentKeyState[exitKey] == 1) windowShouldClose = true;
-
+
// Check screen capture key
if (currentKeyState[301] == 1) TakeScreenshot(); // raylib key: KEY_F12 (GLFW_KEY_F12)
}
@@ -2633,7 +2670,7 @@ static void RestoreKeyboard(void)
{
// Reset to default keyboard settings
tcsetattr(STDIN_FILENO, TCSANOW, &defaultKeyboardSettings);
-
+
// Reconfigure keyboard to default mode
ioctl(STDIN_FILENO, KDSKBMODE, defaultKeyboardMode);
}
@@ -2663,14 +2700,14 @@ static void InitMouse(void)
static void *MouseThread(void *arg)
{
const unsigned char XSIGN = 1<<4, YSIGN = 1<<5;
-
- typedef struct {
+
+ typedef struct {
char buttons;
- char dx, dy;
+ char dx, dy;
} MouseEvent;
-
+
MouseEvent mouse;
-
+
int mouseRelX = 0;
int mouseRelY = 0;
@@ -2679,7 +2716,7 @@ static void *MouseThread(void *arg)
if (read(mouseStream, &mouse, sizeof(MouseEvent)) == (int)sizeof(MouseEvent))
{
if ((mouse.buttons & 0x08) == 0) break; // This bit should always be set
-
+
// Check Left button pressed
if ((mouse.buttons & 0x01) > 0) currentMouseState[0] = 1;
else currentMouseState[0] = 0;
@@ -2687,27 +2724,27 @@ static void *MouseThread(void *arg)
// Check Right button pressed
if ((mouse.buttons & 0x02) > 0) currentMouseState[1] = 1;
else currentMouseState[1] = 0;
-
+
// Check Middle button pressed
if ((mouse.buttons & 0x04) > 0) currentMouseState[2] = 1;
else currentMouseState[2] = 0;
-
+
mouseRelX = (int)mouse.dx;
mouseRelY = (int)mouse.dy;
-
+
if ((mouse.buttons & XSIGN) > 0) mouseRelX = -1*(255 - mouseRelX);
if ((mouse.buttons & YSIGN) > 0) mouseRelY = -1*(255 - mouseRelY);
-
+
// NOTE: Mouse movement is normalized to not be screen resolution dependant
// We suppose 2*255 (max relative movement) is equivalent to screenWidth (max pixels width)
// Result after normalization is multiplied by MOUSE_SENSITIVITY factor
mousePosition.x += (float)mouseRelX*((float)screenWidth/(2*255))*MOUSE_SENSITIVITY;
mousePosition.y -= (float)mouseRelY*((float)screenHeight/(2*255))*MOUSE_SENSITIVITY;
-
+
if (mousePosition.x < 0) mousePosition.x = 0;
if (mousePosition.y < 0) mousePosition.y = 0;
-
+
if (mousePosition.x > screenWidth) mousePosition.x = screenWidth;
if (mousePosition.y > screenHeight) mousePosition.y = screenHeight;
}
@@ -2721,12 +2758,12 @@ static void *MouseThread(void *arg)
static void InitGamepad(void)
{
char gamepadDev[128] = "";
-
+
for (int i = 0; i < MAX_GAMEPADS; i++)
{
sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
-
- if ((gamepadStream[i] = open(gamepadDev, O_RDONLY|O_NONBLOCK)) < 0)
+
+ 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");
@@ -2744,7 +2781,7 @@ static void InitGamepad(void)
else TraceLog(INFO, "Gamepad device initialized successfully");
}
}
- }
+ }
}
// Process Gamepad (/dev/input/js0)
@@ -2763,7 +2800,7 @@ static void *GamepadThread(void *arg)
// Read gamepad event
struct js_event gamepadEvent;
-
+
while (!windowShouldClose)
{
for (int i = 0; i < MAX_GAMEPADS; i++)
@@ -2771,26 +2808,26 @@ static void *GamepadThread(void *arg)
if (read(gamepadStream[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
{
gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events
-
+
// Process gamepad events by type
- if (gamepadEvent.type == JS_EVENT_BUTTON)
+ if (gamepadEvent.type == JS_EVENT_BUTTON)
{
TraceLog(DEBUG, "Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
-
- if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
+
+ if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
{
// 1 - button pressed, 0 - button released
- gamepadButtons[i][gamepadEvent.number] = (int)gamepadEvent.value;
+ currentGamepadState[i][gamepadEvent.number] = (int)gamepadEvent.value;
}
}
- else if (gamepadEvent.type == JS_EVENT_AXIS)
+ else if (gamepadEvent.type == JS_EVENT_AXIS)
{
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;
+ gamepadAxisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
}
}
}
diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h
index 53340478..eaabcf59 100644
--- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h
+++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h
@@ -271,6 +271,12 @@ typedef char ovrBool; ///< Boolean type
//-----------------------------------------------------------------------------------
// ***** Simple Math Structures
+/// A RGBA color with normalized float components.
+typedef struct OVR_ALIGNAS(4) ovrColorf_
+{
+ float r, g, b, a;
+} ovrColorf;
+
/// A 2D vector with integer components.
typedef struct OVR_ALIGNAS(4) ovrVector2i_
{
@@ -326,7 +332,7 @@ typedef struct OVR_ALIGNAS(4) ovrPosef_
/// A full pose (rigid body) configuration with first and second derivatives.
///
/// Body refers to any object for which ovrPoseStatef is providing data.
-/// It can be the HMD, Touch controller, sensor or something else. The context
+/// It can be the HMD, Touch controller, sensor or something else. The context
/// depends on the usage of the struct.
typedef struct OVR_ALIGNAS(8) ovrPoseStatef_
{
@@ -687,7 +693,7 @@ typedef enum ovrTextureFormat_
///
typedef enum ovrTextureMiscFlags_
{
- ovrTextureMisc_None,
+ ovrTextureMisc_None,
/// DX only: The underlying texture is created with a TYPELESS equivalent of the
/// format specified in the texture desc. The SDK will still access the
@@ -745,12 +751,12 @@ typedef struct ovrMirrorTextureData* ovrMirrorTexture;
//-----------------------------------------------------------------------------------
/// Describes button input types.
-/// Button inputs are combined; that is they will be reported as pressed if they are
+/// Button inputs are combined; that is they will be reported as pressed if they are
/// pressed on either one of the two devices.
/// The ovrButton_Up/Down/Left/Right map to both XBox D-Pad and directional buttons.
/// The ovrButton_Enter and ovrButton_Return map to Start and Back controller buttons, respectively.
typedef enum ovrButton_
-{
+{
ovrButton_A = 0x00000001,
ovrButton_B = 0x00000002,
ovrButton_RThumb = 0x00000004,
@@ -758,7 +764,7 @@ typedef enum ovrButton_
ovrButton_X = 0x00000100,
ovrButton_Y = 0x00000200,
- ovrButton_LThumb = 0x00000400,
+ ovrButton_LThumb = 0x00000400,
ovrButton_LShoulder = 0x00000800,
// Navigation through DPad.
@@ -770,7 +776,7 @@ typedef enum ovrButton_
ovrButton_Back = 0x00200000, // Back on Xbox controller.
ovrButton_VolUp = 0x00400000, // only supported by Remote.
ovrButton_VolDown = 0x00800000, // only supported by Remote.
- ovrButton_Home = 0x01000000,
+ ovrButton_Home = 0x01000000,
ovrButton_Private = ovrButton_VolUp | ovrButton_VolDown | ovrButton_Home,
// Bit mask of all buttons on the right Touch controller
@@ -807,7 +813,7 @@ typedef enum ovrTouch_
// Bit mask of all the button touches on the left controller
ovrTouch_LButtonMask = ovrTouch_X | ovrTouch_Y | ovrTouch_LThumb | ovrTouch_LThumbRest | ovrTouch_LIndexTrigger,
- // Finger pose state
+ // Finger pose state
// Derived internally based on distance, proximity to sensors and filtering.
ovrTouch_RIndexPointing = 0x00000020,
ovrTouch_RThumbUp = 0x00000040,
@@ -883,11 +889,20 @@ typedef struct ovrHapticsPlaybackState_
int SamplesQueued;
} ovrHapticsPlaybackState;
+/// Position tracked devices
+typedef enum ovrTrackedDeviceType_
+{
+ ovrTrackedDevice_HMD = 0x0001,
+ ovrTrackedDevice_LTouch = 0x0002,
+ ovrTrackedDevice_RTouch = 0x0004,
+ ovrTrackedDevice_Touch = 0x0006,
+ ovrTrackedDevice_All = 0xFFFF,
+} ovrTrackedDeviceType;
/// Provides names for the left and right hand array indexes.
///
/// \see ovrInputState, ovrTrackingState
-///
+///
typedef enum ovrHandType_
{
ovrHand_Left = 0,
@@ -903,27 +918,43 @@ typedef enum ovrHandType_
/// their inputs are combined.
typedef struct ovrInputState_
{
- // System type when the controller state was last updated.
+ /// System type when the controller state was last updated.
double TimeInSeconds;
- // Values for buttons described by ovrButton.
+ /// Values for buttons described by ovrButton.
unsigned int Buttons;
- // Touch values for buttons and sensors as described by ovrTouch.
+ /// Touch values for buttons and sensors as described by ovrTouch.
unsigned int Touches;
-
- // Left and right finger trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f.
+
+ /// Left and right finger trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f.
+ /// Returns 0 if the value would otherwise be less than 0.1176, for ovrControllerType_XBox
float IndexTrigger[ovrHand_Count];
-
- // Left and right hand trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f.
+
+ /// Left and right hand trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f.
float HandTrigger[ovrHand_Count];
- // Horizontal and vertical thumbstick axis values (ovrHand_Left and ovrHand_Right), in the range -1.0f to 1.0f.
+ /// Horizontal and vertical thumbstick axis values (ovrHand_Left and ovrHand_Right), in the range -1.0f to 1.0f.
+ /// Returns a deadzone (value 0) per each axis if the value on that axis would otherwise have been between -.2746 to +.2746, for ovrControllerType_XBox
ovrVector2f Thumbstick[ovrHand_Count];
- // The type of the controller this state is for.
+ /// The type of the controller this state is for.
ovrControllerType ControllerType;
-
+
+ /// Left and right finger trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f.
+ /// Does not apply a deadzone
+ /// Added in 1.7
+ float IndexTriggerNoDeadzone[ovrHand_Count];
+
+ /// Left and right hand trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f.
+ /// Does not apply a deadzone
+ /// Added in 1.7
+ float HandTriggerNoDeadzone[ovrHand_Count];
+
+ /// Horizontal and vertical thumbstick axis values (ovrHand_Left and ovrHand_Right), in the range -1.0f to 1.0f
+ /// Does not apply a deadzone
+ /// Added in 1.7
+ ovrVector2f ThumbstickNoDeadzone[ovrHand_Count];
} ovrInputState;
@@ -996,8 +1027,8 @@ typedef struct OVR_ALIGNAS(8) ovrInitParams_
/// Use NULL to specify no log callback.
ovrLogCallback LogCallback;
- /// User-supplied data which is passed as-is to LogCallback. Typically this
- /// is used to store an application-specific pointer which is read in the
+ /// User-supplied data which is passed as-is to LogCallback. Typically this
+ /// is used to store an application-specific pointer which is read in the
/// callback function.
uintptr_t UserData;
@@ -1014,6 +1045,7 @@ typedef struct OVR_ALIGNAS(8) ovrInitParams_
extern "C" {
#endif
+#if !defined(OVR_EXPORTING_CAPI)
// -----------------------------------------------------------------------------------
// ***** API Interfaces
@@ -1026,7 +1058,7 @@ extern "C" {
/// followed by a call to ovr_Shutdown. ovr_Initialize calls are idempotent.
/// Calling ovr_Initialize twice does not require two matching calls to ovr_Shutdown.
/// If already initialized, the return value is ovr_Success.
-///
+///
/// LibOVRRT shared library search order:
/// -# Current working directory (often the same as the application directory).
/// -# Module directory (usually the same as the application directory,
@@ -1166,21 +1198,21 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_IdentifyClient(const char* identity);
///
/// ovr_Initialize must have first been called in order for this to succeed, otherwise ovrHmdDesc::Type
/// will be reported as ovrHmd_None.
-///
+///
/// \param[in] session Specifies an ovrSession previously returned by ovr_Create, else NULL in which
/// case this function detects whether an HMD is present and returns its info if so.
///
-/// \return Returns an ovrHmdDesc. If the hmd is NULL and ovrHmdDesc::Type is ovrHmd_None then
+/// \return Returns an ovrHmdDesc. If the hmd is NULL and ovrHmdDesc::Type is ovrHmd_None then
/// no HMD is present.
///
OVR_PUBLIC_FUNCTION(ovrHmdDesc) ovr_GetHmdDesc(ovrSession session);
-/// Returns the number of sensors.
+/// Returns the number of sensors.
///
-/// The number of sensors may change at any time, so this function should be called before use
+/// The number of sensors may change at any time, so this function should be called before use
/// as opposed to once on startup.
-///
+///
/// \param[in] session Specifies an ovrSession previously returned by ovr_Create.
///
/// \return Returns unsigned int count.
@@ -1190,15 +1222,15 @@ OVR_PUBLIC_FUNCTION(unsigned int) ovr_GetTrackerCount(ovrSession session);
/// Returns a given sensor description.
///
-/// It's possible that sensor desc [0] may indicate a unconnnected or non-pose tracked sensor, but
+/// It's possible that sensor desc [0] may indicate a unconnnected or non-pose tracked sensor, but
/// sensor desc [1] may be connected.
///
/// ovr_Initialize must have first been called in order for this to succeed, otherwise the returned
/// trackerDescArray will be zero-initialized. The data returned by this function can change at runtime.
-///
+///
/// \param[in] session Specifies an ovrSession previously returned by ovr_Create.
-///
-/// \param[in] trackerDescIndex Specifies a sensor index. The valid indexes are in the range of 0 to
+///
+/// \param[in] trackerDescIndex Specifies a sensor index. The valid indexes are in the range of 0 to
/// the sensor count returned by ovr_GetTrackerCount.
///
/// \return Returns ovrTrackerDesc. An empty ovrTrackerDesc will be returned if trackerDescIndex is out of range.
@@ -1242,6 +1274,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_Create(ovrSession* pSession, ovrGraphicsLuid*
///
OVR_PUBLIC_FUNCTION(void) ovr_Destroy(ovrSession session);
+#endif // !defined(OVR_EXPORTING_CAPI)
/// Specifies status information for the current session.
///
@@ -1253,10 +1286,11 @@ typedef struct ovrSessionStatus_
ovrBool HmdPresent; ///< True if an HMD is present.
ovrBool HmdMounted; ///< True if the HMD is on the user's head.
ovrBool DisplayLost; ///< True if the session is in a display-lost state. See ovr_SubmitFrame.
- ovrBool ShouldQuit; ///< True if the application should initiate shutdown.
+ ovrBool ShouldQuit; ///< True if the application should initiate shutdown.
ovrBool ShouldRecenter; ///< True if UX has requested re-centering. Must call ovr_ClearShouldRecenterFlag or ovr_RecenterTrackingOrigin.
}ovrSessionStatus;
+#if !defined(OVR_EXPORTING_CAPI)
/// Returns status information for the application.
///
@@ -1293,7 +1327,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetSessionStatus(ovrSession session, ovrSessi
///
/// When the tracking origin is changed, all of the calls that either provide
/// or accept ovrPosef will use the new tracking origin provided.
-///
+///
/// \param[in] session Specifies an ovrSession previously returned by ovr_Create.
/// \param[in] origin Specifies an ovrTrackingOrigin to be used for all ovrPosef
///
@@ -1305,7 +1339,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SetTrackingOriginType(ovrSession session, ovr
/// Gets the tracking origin state
-///
+///
/// \param[in] session Specifies an ovrSession previously returned by ovr_Create.
///
/// \return Returns the ovrTrackingOrigin that was either set by default, or previous set by the application.
@@ -1319,9 +1353,9 @@ OVR_PUBLIC_FUNCTION(ovrTrackingOrigin) ovr_GetTrackingOriginType(ovrSession sess
/// This resets the (x,y,z) positional components and the yaw orientation component.
/// The Roll and pitch orientation components are always determined by gravity and cannot
/// be redefined. All future tracking will report values relative to this new reference position.
-/// If you are using ovrTrackerPoses then you will need to call ovr_GetTrackerPose after
+/// If you are using ovrTrackerPoses then you will need to call ovr_GetTrackerPose after
/// this, because the sensor position(s) will change as a result of this.
-///
+///
/// The headset cannot be facing vertically upward or downward but rather must be roughly
/// level otherwise this function will fail with ovrError_InvalidHeadsetOrientation.
///
@@ -1343,7 +1377,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_RecenterTrackingOrigin(ovrSession session);
/// Clears the ShouldRecenter status bit in ovrSessionStatus.
///
-/// Clears the ShouldRecenter status bit in ovrSessionStatus, allowing further recenter
+/// Clears the ShouldRecenter status bit in ovrSessionStatus, allowing further recenter
/// requests to be detected. Since this is automatically done by ovr_RecenterTrackingOrigin,
/// this is only needs to be called when application is doing its own re-centering.
OVR_PUBLIC_FUNCTION(void) ovr_ClearShouldRecenterFlag(ovrSession session);
@@ -1444,11 +1478,11 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SubmitControllerVibration(ovrSession session,
/// \param[in] outState State of the haptics engine.
/// \return Returns ovrSuccess upon success.
/// \see ovrHapticsPlaybackState
-///
+///
OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetControllerVibrationState(ovrSession session, ovrControllerType controllerType, ovrHapticsPlaybackState* outState);
-///@}
+#endif // !defined(OVR_EXPORTING_CAPI)
//-------------------------------------------------------------------------------------
// @name Layers
@@ -1672,7 +1706,7 @@ typedef union ovrLayer_Union_
//@}
-
+#if !defined(OVR_EXPORTING_CAPI)
/// @name SDK Distortion Rendering
///
@@ -1695,7 +1729,7 @@ typedef union ovrLayer_Union_
/// \param[in] chain Specifies the ovrTextureSwapChain for which the length should be retrieved.
/// \param[out] out_Length Returns the number of buffers in the specified chain.
///
-/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
+/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
///
/// \see ovr_CreateTextureSwapChainDX, ovr_CreateTextureSwapChainGL
///
@@ -1707,7 +1741,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetTextureSwapChainLength(ovrSession session,
/// \param[in] chain Specifies the ovrTextureSwapChain for which the index should be retrieved.
/// \param[out] out_Index Returns the current (free) index in specified chain.
///
-/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
+/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
///
/// \see ovr_CreateTextureSwapChainDX, ovr_CreateTextureSwapChainGL
///
@@ -1719,7 +1753,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetTextureSwapChainCurrentIndex(ovrSession se
/// \param[in] chain Specifies the ovrTextureSwapChain for which the description should be retrieved.
/// \param[out] out_Desc Returns the description of the specified chain.
///
-/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
+/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
///
/// \see ovr_CreateTextureSwapChainDX, ovr_CreateTextureSwapChainGL
///
@@ -1736,7 +1770,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetTextureSwapChainDesc(ovrSession session, o
/// it will synchronize with the app's graphics context and pick up the submitted index, opening up
/// room in the swap chain for further commits.
///
-/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
+/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error.
/// Failures include but aren't limited to:
/// - ovrError_TextureSwapChainFull: ovr_CommitTextureSwapChain was called too many times on a texture swapchain without calling submit to use the chain.
///
@@ -1864,7 +1898,7 @@ OVR_PUBLIC_FUNCTION(ovrEyeRenderDesc) ovr_GetRenderDesc(ovrSession session,
/// destroyed (ovr_Destroy) and recreated (ovr_Create), and new resources need to be created
/// (ovr_CreateTextureSwapChainXXX). The application's existing private graphics resources do not
/// need to be recreated unless the new ovr_Create call returns a different GraphicsLuid.
-/// - ovrError_TextureSwapChainInvalid: The ovrTextureSwapChain is in an incomplete or inconsistent state.
+/// - ovrError_TextureSwapChainInvalid: The ovrTextureSwapChain is in an incomplete or inconsistent state.
/// Ensure ovr_CommitTextureSwapChain was called at least once first.
///
/// \see ovr_GetPredictedDisplayTime, ovrViewScaleDesc, ovrLayerHeader
@@ -1874,7 +1908,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SubmitFrame(ovrSession session, long long fra
ovrLayerHeader const * const * layerPtrList, unsigned int layerCount);
///@}
-
+#endif // !defined(OVR_EXPORTING_CAPI)
//-------------------------------------------------------------------------------------
/// @name Frame Timing
@@ -1882,26 +1916,28 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SubmitFrame(ovrSession session, long long fra
//@{
+#if !defined(OVR_EXPORTING_CAPI)
+
/// Gets the time of the specified frame midpoint.
///
-/// Predicts the time at which the given frame will be displayed. The predicted time
-/// is the middle of the time period during which the corresponding eye images will
-/// be displayed.
+/// Predicts the time at which the given frame will be displayed. The predicted time
+/// is the middle of the time period during which the corresponding eye images will
+/// be displayed.
///
/// The application should increment frameIndex for each successively targeted frame,
-/// and pass that index to any relevent OVR functions that need to apply to the frame
+/// and pass that index to any relevant OVR functions that need to apply to the frame
/// identified by that index.
///
/// This function is thread-safe and allows for multiple application threads to target
/// their processing to the same displayed frame.
-///
+///
/// In the even that prediction fails due to various reasons (e.g. the display being off
/// or app has yet to present any frames), the return value will be current CPU time.
-///
+///
/// \param[in] session Specifies an ovrSession previously returned by ovr_Create.
/// \param[in] frameIndex Identifies the frame the caller wishes to target.
/// A value of zero returns the next frame index.
-/// \return Returns the absolute frame midpoint time for the given frameIndex.
+/// \return Returns the absolute frame midpoint time for the given frameIndex.
/// \see ovr_GetTimeInSeconds
///
OVR_PUBLIC_FUNCTION(double) ovr_GetPredictedDisplayTime(ovrSession session, long long frameIndex);
@@ -1917,6 +1953,7 @@ OVR_PUBLIC_FUNCTION(double) ovr_GetPredictedDisplayTime(ovrSession session, long
///
OVR_PUBLIC_FUNCTION(double) ovr_GetTimeInSeconds();
+#endif // !defined(OVR_EXPORTING_CAPI)
/// Performance HUD enables the HMD user to see information critical to
/// the real-time operation of the VR application such as latency timing,
@@ -1958,7 +1995,7 @@ typedef enum ovrLayerHudMode_
///@}
/// Debug HUD is provided to help developers gauge and debug the fidelity of their app's
-/// stereo rendering characteristics. Using the provided quad and crosshair guides,
+/// stereo rendering characteristics. Using the provided quad and crosshair guides,
/// the developer can verify various aspects such as VR tracking units (e.g. meters),
/// stereo camera-parallax properties (e.g. making sure objects at infinity are rendered
/// with the proper separation), measuring VR geometry sizes and distances and more.
@@ -1984,7 +2021,7 @@ typedef enum ovrDebugHudStereoMode_
} ovrDebugHudStereoMode;
-
+#if !defined(OVR_EXPORTING_CAPI)
// -----------------------------------------------------------------------------------
/// @name Property Access
@@ -2102,7 +2139,7 @@ OVR_PUBLIC_FUNCTION(ovrBool) ovr_SetString(ovrSession session, const char* prope
///@}
-
+#endif // !defined(OVR_EXPORTING_CAPI)
#ifdef __cplusplus
} // extern "C"
@@ -2163,10 +2200,10 @@ OVR_STATIC_ASSERT(sizeof(ovrLogLevel) == 4, "ovrLogLevel size mismatch");
OVR_STATIC_ASSERT(sizeof(ovrInitParams) == 4 + 4 + sizeof(ovrLogCallback) + sizeof(uintptr_t) + 4 + 4,
"ovrInitParams size mismatch");
-OVR_STATIC_ASSERT(sizeof(ovrHmdDesc) ==
+OVR_STATIC_ASSERT(sizeof(ovrHmdDesc) ==
+ sizeof(ovrHmdType) // Type
OVR_ON64(+ 4) // pad0
- + 64 // ProductName
+ + 64 // ProductName
+ 64 // Manufacturer
+ 2 // VendorId
+ 2 // ProductId
diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h
index 930dfcbe..dc61e19e 100644
--- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h
+++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h
@@ -17,6 +17,8 @@
#include "OVR_CAPI.h"
#define OVR_AUDIO_MAX_DEVICE_STR_SIZE 128
+#if !defined(OVR_EXPORTING_CAPI)
+
/// Gets the ID of the preferred VR audio output device.
///
/// \param[out] deviceOutId The ID of the user's preferred VR audio device to use, which will be valid upon a successful return value, else it will be WAVE_MAPPER.
@@ -75,6 +77,8 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetAudioDeviceInGuidStr(WCHAR deviceInStrBuff
///
OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetAudioDeviceInGuid(GUID* deviceInGuid);
+#endif // !defined(OVR_EXPORTING_CAPI)
+
#endif //OVR_OS_MS
#endif // OVR_CAPI_Audio_h
diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h
index 982af8f0..374dab84 100644
--- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h
+++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h
@@ -14,6 +14,8 @@
#if defined (_WIN32)
#include <Unknwn.h>
+#if !defined(OVR_EXPORTING_CAPI)
+
//-----------------------------------------------------------------------------------
// ***** Direct3D Specific
@@ -149,6 +151,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetMirrorTextureBufferDX(ovrSession session,
IID iid,
void** out_Buffer);
+#endif // !defined(OVR_EXPORTING_CAPI)
#endif // _WIN32
diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h
index 81487947..1c073f46 100644
--- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h
+++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h
@@ -9,6 +9,8 @@
#include "OVR_CAPI.h"
+#if !defined(OVR_EXPORTING_CAPI)
+
/// Creates a TextureSwapChain suitable for use with OpenGL.
///
/// \param[in] session Specifies an ovrSession previously returned by ovr_Create.
@@ -95,5 +97,6 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetMirrorTextureBufferGL(ovrSession session,
ovrMirrorTexture mirrorTexture,
unsigned int* out_TexId);
+#endif // !defined(OVR_EXPORTING_CAPI)
#endif // OVR_CAPI_GL_h
diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h b/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h
index 9fc527c7..a8b810ea 100644
--- a/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h
+++ b/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h
@@ -88,6 +88,7 @@ typedef enum ovrErrorType_
ovrError_ClientSkippedDestroy = -1012, ///< The client failed to call ovr_Destroy on an active session before calling ovr_Shutdown. Or the client crashed.
ovrError_ClientSkippedShutdown = -1013, ///< The client failed to call ovr_Shutdown or the client crashed.
ovrError_ServiceDeadlockDetected = -1014, ///< The service watchdog discovered a deadlock.
+ ovrError_InvalidOperation = -1015, ///< Function call is invalid for object's current state
/* Audio error range, reserved for Audio errors. */
ovrError_AudioDeviceNotFound = -2001, ///< Failure to find the specified audio device.
@@ -115,6 +116,9 @@ typedef enum ovrErrorType_
ovrError_HybridGraphicsNotSupported = -3018, ///< The system is using hybrid graphics (Optimus, etc...), which is not support.
ovrError_DisplayManagerInit = -3019, ///< Initialization of the DisplayManager failed.
ovrError_TrackerDriverInit = -3020, ///< Failed to get the interface for an attached tracker
+ ovrError_LibSignCheck = -3021, ///< LibOVRRT signature check failure.
+ ovrError_LibPath = -3022, ///< LibOVRRT path failure.
+ ovrError_LibSymbols = -3023, ///< LibOVRRT symbol resolution failure.
/* Rendering errors */
ovrError_DisplayLost = -6000, ///< In the event of a system-wide graphics reset or cable unplug this is returned to the app.
@@ -130,6 +134,11 @@ typedef enum ovrErrorType_
/* Fatal errors */
ovrError_RuntimeException = -7000, ///< A runtime exception occurred. The application is required to shutdown LibOVR and re-initialize it before this error state will be cleared.
+ /* Calibration errors */
+ ovrError_NoCalibration = -9000, ///< Result of a missing calibration block
+ ovrError_OldVersion = -9001, ///< Result of an old calibration block
+ ovrError_MisformattedBlock = -9002, ///< Result of a bad calibration block due to lengths
+
} ovrErrorType;
diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_Version.h b/src/external/OculusSDK/LibOVR/Include/OVR_Version.h
index d8378304..8d213df5 100644
--- a/src/external/OculusSDK/LibOVR/Include/OVR_Version.h
+++ b/src/external/OculusSDK/LibOVR/Include/OVR_Version.h
@@ -19,7 +19,7 @@
// Master version numbers
#define OVR_PRODUCT_VERSION 1 // Product version doesn't participate in semantic versioning.
#define OVR_MAJOR_VERSION 1 // If you change these values then you need to also make sure to change LibOVR/Projects/Windows/LibOVR.props in parallel.
-#define OVR_MINOR_VERSION 6 //
+#define OVR_MINOR_VERSION 7 //
#define OVR_PATCH_VERSION 0
#define OVR_BUILD_NUMBER 0
diff --git a/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll b/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll
index 843c0e44..7a42d443 100644
--- a/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll
+++ b/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll
Binary files differ
diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h
new file mode 100644
index 00000000..d7b66f20
--- /dev/null
+++ b/src/external/dr_flac.h
@@ -0,0 +1,4395 @@
+// FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file.
+// dr_flac - v0.4 - 2016-09-29
+//
+// David Reid - mackron@gmail.com
+
+// USAGE
+//
+// dr_flac is a single-file library. To use it, do something like the following in one .c file.
+// #define DR_FLAC_IMPLEMENTATION
+// #include "dr_flac.h"
+//
+// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
+// do something like the following:
+//
+// drflac* pFlac = drflac_open_file("MySong.flac");
+// if (pFlac == NULL) {
+// // Failed to open FLAC file
+// }
+//
+// int32_t* pSamples = malloc(pFlac->totalSampleCount * sizeof(int32_t));
+// uint64_t numberOfInterleavedSamplesActuallyRead = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSamples);
+//
+// The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of
+// channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are
+// always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has
+// seamless support for Ogg encapsulated FLAC streams as well.
+//
+// You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and
+// the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of
+// samples, just call it again. Example:
+//
+// while (drflac_read_s32(pFlac, chunkSize, pChunkSamples) > 0) {
+// do_something();
+// }
+//
+// You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example,
+// if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel.
+// The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the
+// left channel, etc.
+//
+//
+// If you just want to quickly decode an entire FLAC file in one go you can do something like this:
+//
+// unsigned int channels;
+// unsigned int sampleRate;
+// uint64_t totalSampleCount;
+// int32_t* pSampleData = drflac_open_and_decode_file("MySong.flac", &channels, &sampleRate, &totalSampleCount);
+// if (pSampleData == NULL) {
+// // Failed to open and decode FLAC file.
+// }
+//
+// ...
+//
+// drflac_free(pSampleData);
+//
+//
+// If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or
+// drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the
+// normal versions and also just a little bit harder to use.
+//
+// dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before
+// drflac_open_with_metdata() returns. See https://github.com/mackron/dr_libs_tests/blob/master/dr_flac/dr_flac_test_2.c for
+// an example on how to read metadata.
+//
+//
+//
+// OPTIONS
+// #define these options before including this file.
+//
+// #define DR_FLAC_NO_STDIO
+// Disable drflac_open_file().
+//
+// #define DR_FLAC_NO_OGG
+// Disables support for Ogg/FLAC streams.
+//
+// #define DR_FLAC_NO_WIN32_IO
+// In the Win32 build, dr_flac uses the Win32 IO APIs for drflac_open_file() by default. This setting will make it use the
+// standard FILE APIs instead. Ignored when DR_FLAC_NO_STDIO is #defined. (The rationale for this configuration is that
+// there's a bug in one compiler's Win32 implementation of the FILE APIs which is not present in the Win32 IO APIs.)
+//
+// #define DR_FLAC_BUFFER_SIZE <number>
+// Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls
+// back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing
+// returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of
+// onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
+//
+//
+//
+// QUICK NOTES
+// - Based on my tests, the performance of the 32-bit build is at about parity with the reference implementation. The 64-bit build
+// is slightly faster.
+// - dr_flac does not currently do any CRC checks.
+// - dr_flac should work fine with valid native FLAC files, but for broadcast streams it won't work if the header and STREAMINFO
+// block is unavailable.
+// - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as.
+// - This has not been tested on big-endian architectures.
+// - Rice codes in unencoded binary form (see https://xiph.org/flac/format.html#rice_partition) has not been tested. If anybody
+// knows where I can find some test files for this, let me know.
+// - Perverse and erroneous files have not been tested. Again, if you know where I can get some test files let me know.
+// - dr_flac is not thread-safe, but it's APIs can be called from any thread so long as you do your own synchronization.
+
+#ifndef dr_flac_h
+#define dr_flac_h
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifndef DR_BOOL_DEFINED
+#define DR_BOOL_DEFINED
+#ifdef _WIN32
+typedef char drBool8;
+typedef int drBool32;
+#else
+#include <stdint.h>
+typedef int8_t drBool8;
+typedef int32_t drBool32;
+#endif
+#define DR_TRUE 1
+#define DR_FALSE 0
+#endif
+
+// As data is read from the client it is placed into an internal buffer for fast access. This controls the
+// size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing
+// returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8.
+#ifndef DR_FLAC_BUFFER_SIZE
+#define DR_FLAC_BUFFER_SIZE 4096
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Check if we can enable 64-bit optimizations.
+#if defined(_WIN64)
+#define DRFLAC_64BIT
+#endif
+
+#if defined(__GNUC__)
+#if defined(__x86_64__) || defined(__ppc64__)
+#define DRFLAC_64BIT
+#endif
+#endif
+
+#ifdef DRFLAC_64BIT
+typedef uint64_t drflac_cache_t;
+#else
+typedef uint32_t drflac_cache_t;
+#endif
+
+// The various metadata block types.
+#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
+#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
+#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
+#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
+#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
+#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5
+#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
+#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
+
+// The various picture types specified in the PICTURE block.
+#define DRFLAC_PICTURE_TYPE_OTHER 0
+#define DRFLAC_PICTURE_TYPE_FILE_ICON 1
+#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
+#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3
+#define DRFLAC_PICTURE_TYPE_COVER_BACK 4
+#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5
+#define DRFLAC_PICTURE_TYPE_MEDIA 6
+#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7
+#define DRFLAC_PICTURE_TYPE_ARTIST 8
+#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9
+#define DRFLAC_PICTURE_TYPE_BAND 10
+#define DRFLAC_PICTURE_TYPE_COMPOSER 11
+#define DRFLAC_PICTURE_TYPE_LYRICIST 12
+#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13
+#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14
+#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
+#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
+#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
+#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18
+#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
+#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
+
+typedef enum
+{
+ drflac_container_native,
+ drflac_container_ogg
+} drflac_container;
+
+typedef enum
+{
+ drflac_seek_origin_start,
+ drflac_seek_origin_current
+} drflac_seek_origin;
+
+// Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block.
+#pragma pack(2)
+typedef struct
+{
+ uint64_t firstSample;
+ uint64_t frameOffset; // The offset from the first byte of the header of the first frame.
+ uint16_t sampleCount;
+} drflac_seekpoint;
+#pragma pack()
+
+typedef struct
+{
+ uint16_t minBlockSize;
+ uint16_t maxBlockSize;
+ uint32_t minFrameSize;
+ uint32_t maxFrameSize;
+ uint32_t sampleRate;
+ uint8_t channels;
+ uint8_t bitsPerSample;
+ uint64_t totalSampleCount;
+ uint8_t md5[16];
+} drflac_streaminfo;
+
+typedef struct
+{
+ // The metadata type. Use this to know how to interpret the data below.
+ uint32_t type;
+
+ // A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
+ // not modify the contents of this buffer. Use the structures below for more meaningful and structured
+ // information about the metadata. It's possible for this to be null.
+ const void* pRawData;
+
+ // The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL.
+ uint32_t rawDataSize;
+
+ union
+ {
+ drflac_streaminfo streaminfo;
+
+ struct
+ {
+ int unused;
+ } padding;
+
+ struct
+ {
+ uint32_t id;
+ const void* pData;
+ uint32_t dataSize;
+ } application;
+
+ struct
+ {
+ uint32_t seekpointCount;
+ const drflac_seekpoint* pSeekpoints;
+ } seektable;
+
+ struct
+ {
+ uint32_t vendorLength;
+ const char* vendor;
+ uint32_t commentCount;
+ const char* comments;
+ } vorbis_comment;
+
+ struct
+ {
+ char catalog[128];
+ uint64_t leadInSampleCount;
+ drBool32 isCD;
+ uint8_t trackCount;
+ const uint8_t* pTrackData;
+ } cuesheet;
+
+ struct
+ {
+ uint32_t type;
+ uint32_t mimeLength;
+ const char* mime;
+ uint32_t descriptionLength;
+ const char* description;
+ uint32_t width;
+ uint32_t height;
+ uint32_t colorDepth;
+ uint32_t indexColorCount;
+ uint32_t pictureDataSize;
+ const uint8_t* pPictureData;
+ } picture;
+ } data;
+
+} drflac_metadata;
+
+
+// Callback for when data needs to be read from the client.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// pBufferOut [out] The output buffer.
+// bytesToRead [in] The number of bytes to read.
+//
+// Returns the number of bytes actually read.
+typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+// Callback for when data needs to be seeked.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+// origin [in] The origin of the seek - the current position or the start of the stream.
+//
+// Returns whether or not the seek was successful.
+//
+// The offset will never be negative. Whether or not it is relative to the beginning or current position is determined
+// by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current.
+typedef drBool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
+
+// Callback for when a metadata block is read.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// pMetadata [in] A pointer to a structure containing the data of the metadata block.
+//
+// Use pMetadata->type to determine which metadata block is being handled and how to read the data.
+typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
+
+
+// Structure for internal use. Only used for decoders opened with drflac_open_memory.
+typedef struct
+{
+ const uint8_t* data;
+ size_t dataSize;
+ size_t currentReadPos;
+} drflac__memory_stream;
+
+// Structure for internal use. Used for bit streaming.
+typedef struct
+{
+ // The function to call when more data needs to be read.
+ drflac_read_proc onRead;
+
+ // The function to call when the current read position needs to be moved.
+ drflac_seek_proc onSeek;
+
+ // The user data to pass around to onRead and onSeek.
+ void* pUserData;
+
+
+ // The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the
+ // stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether
+ // or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t).
+ size_t unalignedByteCount;
+
+ // The content of the unaligned bytes.
+ drflac_cache_t unalignedCache;
+
+ // The index of the next valid cache line in the "L2" cache.
+ size_t nextL2Line;
+
+ // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining.
+ size_t consumedBits;
+
+ // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such:
+ // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions.
+ drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
+ drflac_cache_t cache;
+
+} drflac_bs;
+
+typedef struct
+{
+ // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC.
+ uint8_t subframeType;
+
+ // The number of wasted bits per sample as specified by the sub-frame header.
+ uint8_t wastedBitsPerSample;
+
+ // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC.
+ uint8_t lpcOrder;
+
+ // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because
+ // an extra bit is required for side channels when interchannel decorrelation is being used.
+ uint32_t bitsPerSample;
+
+ // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData, or
+ // NULL if the heap is not being used. Note that it's a signed 32-bit integer for each value.
+ int32_t* pDecodedSamples;
+
+} drflac_subframe;
+
+typedef struct
+{
+ // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will
+ // always be set to 0.
+ uint64_t sampleNumber;
+
+ // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0.
+ uint32_t frameNumber;
+
+ // The sample rate of this frame.
+ uint32_t sampleRate;
+
+ // The number of samples in each sub-frame within this frame.
+ uint16_t blockSize;
+
+ // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this
+ // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE.
+ uint8_t channelAssignment;
+
+ // The number of bits per sample within this frame.
+ uint8_t bitsPerSample;
+
+ // The frame's CRC. This is set, but unused at the moment.
+ uint8_t crc8;
+
+} drflac_frame_header;
+
+typedef struct
+{
+ // The header.
+ drflac_frame_header header;
+
+ // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples
+ // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame.
+ uint32_t samplesRemaining;
+
+ // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels.
+ drflac_subframe subframes[8];
+
+} drflac_frame;
+
+typedef struct
+{
+ // The function to call when a metadata block is read.
+ drflac_meta_proc onMeta;
+
+ // The user data posted to the metadata callback function.
+ void* pUserDataMD;
+
+
+ // The sample rate. Will be set to something like 44100.
+ uint32_t sampleRate;
+
+ // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the
+ // value specified in the STREAMINFO block.
+ uint8_t channels;
+
+ // The bits per sample. Will be set to somthing like 16, 24, etc.
+ uint8_t bitsPerSample;
+
+ // The maximum block size, in samples. This number represents the number of samples in each channel (not combined).
+ uint16_t maxBlockSize;
+
+ // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels,
+ // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a
+ // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio.
+ uint64_t totalSampleCount;
+
+
+ // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream.
+ drflac_container container;
+
+
+ // The position of the seektable in the file.
+ uint64_t seektablePos;
+
+ // The size of the seektable.
+ uint32_t seektableSize;
+
+
+ // Information about the frame the decoder is currently sitting on.
+ drflac_frame currentFrame;
+
+ // The position of the first frame in the stream. This is only ever used for seeking.
+ uint64_t firstFramePos;
+
+
+ // A hack to avoid a malloc() when opening a decoder with drflac_open_memory().
+ drflac__memory_stream memoryStream;
+
+
+
+ // A pointer to the decoded sample data. This is an offset of pExtraData.
+ int32_t* pDecodedSamples;
+
+
+ // The bit streamer. The raw FLAC data is fed through this object.
+ drflac_bs bs;
+
+ // Variable length extra data. We attach this to the end of the object so we avoid unnecessary mallocs.
+ uint8_t pExtraData[1];
+
+} drflac;
+
+
+// Opens a FLAC decoder.
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated
+// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with
+// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos.
+//
+// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// The STREAMINFO block must be present for this to succeed.
+//
+// See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close()
+drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData);
+
+// Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.).
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// onMeta [in] The function to call for every metadata block.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a malloc()
+// and free() for every metadata block except for STREAMINFO and PADDING blocks.
+//
+// The caller is notified of the metadata via the onMeta callback. All metadata blocks with be handled before the function
+// returns.
+//
+// See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close()
+drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData);
+
+// Closes the given FLAC decoder.
+//
+// pFlac [in] The decoder to close.
+//
+// This will destroy the decoder object.
+void drflac_close(drflac* pFlac);
+
+
+// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM.
+//
+// pFlac [in] The decoder.
+// samplesToRead [in] The number of samples to read.
+// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
+//
+// Returns the number of samples actually read.
+//
+// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples
+// seeked.
+uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* pBufferOut);
+
+// Seeks to the sample at the given index.
+//
+// pFlac [in] The decoder.
+// sampleIndex [in] The index of the sample to seek to. See notes below.
+//
+// Returns DR_TRUE if successful; DR_FALSE otherwise.
+//
+// The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample
+// in the left channel; the sample at index 1 is the first sample on the right channel, and so on.
+//
+// When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with
+// something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels)))
+drBool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex);
+
+
+
+#ifndef DR_FLAC_NO_STDIO
+// Opens a FLAC decoder from the file at the given path.
+//
+// filename [in] The path of the file to open, either absolute or relative to the current directory.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the
+// number of files a process can have open at any given time, so keep this mind if you have many decoders open at the
+// same time.
+//
+// See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close()
+drflac* drflac_open_file(const char* filename);
+
+// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.)
+//
+// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData);
+#endif
+
+// Opens a FLAC decoder from a pre-allocated block of memory
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the decoder.
+drflac* drflac_open_memory(const void* data, size_t dataSize);
+
+// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.)
+//
+// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData);
+
+
+
+//// High Level APIs ////
+
+// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a
+// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with drflac_free().
+//
+// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously
+// read samples into a dynamically sized buffer on the heap until no samples are left.
+//
+// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot).
+int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount);
+
+#ifndef DR_FLAC_NO_STDIO
+// Same as drflac_open_and_decode_s32() except opens the decoder from a file.
+int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount);
+#endif
+
+// Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory.
+int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount);
+
+// Frees data returned by drflac_open_and_decode_*().
+void drflac_free(void* pSampleDataReturnedByOpenAndDecode);
+
+
+// Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block.
+typedef struct
+{
+ uint32_t countRemaining;
+ const char* pRunningData;
+} drflac_vorbis_comment_iterator;
+
+// Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT
+// metadata block.
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, uint32_t commentCount, const char* pComments);
+
+// Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The
+// returned string is NOT null terminated.
+const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, uint32_t* pCommentLengthOut);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif //dr_flac_h
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+///////////////////////////////////////////////////////////////////////////////
+#ifdef DR_FLAC_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef _MSC_VER
+#include <intrin.h> // For _byteswap_ulong and _byteswap_uint64
+#endif
+
+#ifdef __linux__
+#define _BSD_SOURCE
+#include <endian.h>
+#endif
+
+#ifdef _MSC_VER
+#define DRFLAC_INLINE __forceinline
+#else
+#define DRFLAC_INLINE inline
+#endif
+
+#define DRFLAC_SUBFRAME_CONSTANT 0
+#define DRFLAC_SUBFRAME_VERBATIM 1
+#define DRFLAC_SUBFRAME_FIXED 8
+#define DRFLAC_SUBFRAME_LPC 32
+#define DRFLAC_SUBFRAME_RESERVED 255
+
+#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
+#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
+
+#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
+#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
+#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
+#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
+
+
+//// Endian Management ////
+static DRFLAC_INLINE drBool32 drflac__is_little_endian()
+{
+ int n = 1;
+ return (*(char*)&n) == 1;
+}
+
+static DRFLAC_INLINE uint16_t drflac__swap_endian_uint16(uint16_t n)
+{
+#ifdef _MSC_VER
+ return _byteswap_ushort(n);
+#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ return __builtin_bswap16(n);
+#else
+ return ((n & 0xFF00) >> 8) |
+ ((n & 0x00FF) << 8);
+#endif
+}
+
+static DRFLAC_INLINE uint32_t drflac__swap_endian_uint32(uint32_t n)
+{
+#ifdef _MSC_VER
+ return _byteswap_ulong(n);
+#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ return __builtin_bswap32(n);
+#else
+ return ((n & 0xFF000000) >> 24) |
+ ((n & 0x00FF0000) >> 8) |
+ ((n & 0x0000FF00) << 8) |
+ ((n & 0x000000FF) << 24);
+#endif
+}
+
+static DRFLAC_INLINE uint64_t drflac__swap_endian_uint64(uint64_t n)
+{
+#ifdef _MSC_VER
+ return _byteswap_uint64(n);
+#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ return __builtin_bswap64(n);
+#else
+ return ((n & 0xFF00000000000000ULL) >> 56) |
+ ((n & 0x00FF000000000000ULL) >> 40) |
+ ((n & 0x0000FF0000000000ULL) >> 24) |
+ ((n & 0x000000FF00000000ULL) >> 8) |
+ ((n & 0x00000000FF000000ULL) << 8) |
+ ((n & 0x0000000000FF0000ULL) << 24) |
+ ((n & 0x000000000000FF00ULL) << 40) |
+ ((n & 0x00000000000000FFULL) << 56);
+#endif
+}
+
+static DRFLAC_INLINE uint16_t drflac__be2host_16(uint16_t n)
+{
+#ifdef __linux__
+ return be16toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint16(n);
+ }
+
+ return n;
+#endif
+}
+
+static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n)
+{
+#ifdef __linux__
+ return be32toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+#endif
+}
+
+static DRFLAC_INLINE uint64_t drflac__be2host_64(uint64_t n)
+{
+#ifdef __linux__
+ return be64toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint64(n);
+ }
+
+ return n;
+#endif
+}
+
+
+static DRFLAC_INLINE uint32_t drflac__le2host_32(uint32_t n)
+{
+#ifdef __linux__
+ return le32toh(n);
+#else
+ if (!drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+#endif
+}
+
+
+#ifdef DRFLAC_64BIT
+#define drflac__be2host__cache_line drflac__be2host_64
+#else
+#define drflac__be2host__cache_line drflac__be2host_32
+#endif
+
+
+// BIT READING ATTEMPT #2
+//
+// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting
+// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache
+// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an
+// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data
+// from onRead() is read into.
+#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
+#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
+#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - ((bs)->consumedBits))
+#ifdef DRFLAC_64BIT
+#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((uint64_t)-1LL) >> (_bitCount)))
+#else
+#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((uint32_t)-1) >> (_bitCount)))
+#endif
+#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
+#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
+#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), _bitCount) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), _bitCount))
+#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
+#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
+#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
+
+static DRFLAC_INLINE drBool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
+{
+ // Fast path. Try loading straight from L2.
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DR_TRUE;
+ }
+
+ // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's
+ // any left.
+ if (bs->unalignedByteCount > 0) {
+ return DR_FALSE; // If we have any unaligned bytes it means there's not more aligned bytes left in the client.
+ }
+
+ size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
+
+ bs->nextL2Line = 0;
+ if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DR_TRUE;
+ }
+
+
+ // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably
+ // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer
+ // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to
+ // the size of the L1 so we'll need to seek backwards by any misaligned bytes.
+ size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
+
+ // We need to keep track of any unaligned bytes for later use.
+ bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
+ if (bs->unalignedByteCount > 0) {
+ bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
+ }
+
+ if (alignedL1LineCount > 0)
+ {
+ size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
+ for (size_t i = alignedL1LineCount; i > 0; --i) {
+ bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
+ }
+
+ bs->nextL2Line = offset;
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DR_TRUE;
+ }
+ else
+ {
+ // If we get into this branch it means we weren't able to load any L1-aligned data.
+ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
+ return DR_FALSE;
+ }
+}
+
+static drBool32 drflac__reload_cache(drflac_bs* bs)
+{
+ // Fast path. Try just moving the next value in the L2 cache to the L1 cache.
+ if (drflac__reload_l1_cache_from_l2(bs)) {
+ bs->cache = drflac__be2host__cache_line(bs->cache);
+ bs->consumedBits = 0;
+ return DR_TRUE;
+ }
+
+ // Slow path.
+
+ // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last
+ // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the
+ // data from the unaligned cache.
+ size_t bytesRead = bs->unalignedByteCount;
+ if (bytesRead == 0) {
+ return DR_FALSE;
+ }
+
+ assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs));
+ bs->consumedBits = (DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
+
+ bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
+ bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property.
+ return DR_TRUE;
+}
+
+static void drflac__reset_cache(drflac_bs* bs)
+{
+ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); // <-- This clears the L2 cache.
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- This clears the L1 cache.
+ bs->cache = 0;
+ bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes.
+ bs->unalignedCache = 0;
+}
+
+static drBool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
+{
+ if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ bs->consumedBits += bitsToSeek;
+ bs->cache <<= bitsToSeek;
+ return DR_TRUE;
+ } else {
+ // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here.
+ bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ bs->cache = 0;
+
+ size_t wholeBytesRemaining = bitsToSeek/8;
+ if (wholeBytesRemaining > 0)
+ {
+ // The next bytes to seek will be located in the L2 cache. The problem is that the L2 cache is not byte aligned,
+ // but rather DRFLAC_CACHE_L1_SIZE_BYTES aligned (usually 4 or 8). If, for example, the number of bytes to seek is
+ // 3, we'll need to handle it in a special way.
+ size_t wholeCacheLinesRemaining = wholeBytesRemaining / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
+ if (wholeCacheLinesRemaining < DRFLAC_CACHE_L2_LINES_REMAINING(bs))
+ {
+ wholeBytesRemaining -= wholeCacheLinesRemaining * DRFLAC_CACHE_L1_SIZE_BYTES(bs);
+ bitsToSeek -= wholeCacheLinesRemaining * DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ bs->nextL2Line += wholeCacheLinesRemaining;
+ }
+ else
+ {
+ wholeBytesRemaining -= DRFLAC_CACHE_L2_LINES_REMAINING(bs) * DRFLAC_CACHE_L1_SIZE_BYTES(bs);
+ bitsToSeek -= DRFLAC_CACHE_L2_LINES_REMAINING(bs) * DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ bs->nextL2Line += DRFLAC_CACHE_L2_LINES_REMAINING(bs);
+
+ if (wholeBytesRemaining > 0) {
+ bs->onSeek(bs->pUserData, (int)wholeBytesRemaining, drflac_seek_origin_current);
+ bitsToSeek -= wholeBytesRemaining*8;
+ }
+ }
+ }
+
+
+ if (bitsToSeek > 0) {
+ if (!drflac__reload_cache(bs)) {
+ return DR_FALSE;
+ }
+
+ return drflac__seek_bits(bs, bitsToSeek);
+ }
+
+ return DR_TRUE;
+ }
+}
+
+static drBool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, uint32_t* pResultOut)
+{
+ assert(bs != NULL);
+ assert(pResultOut != NULL);
+ assert(bitCount > 0);
+ assert(bitCount <= 32);
+
+ if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ if (!drflac__reload_cache(bs)) {
+ return DR_FALSE;
+ }
+ }
+
+ if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ *pResultOut = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
+ bs->consumedBits += bitCount;
+ bs->cache <<= bitCount;
+ } else {
+ *pResultOut = (uint32_t)bs->cache;
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ bs->cache = 0;
+ }
+ return DR_TRUE;
+ } else {
+ // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them.
+ size_t bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ size_t bitCountLo = bitCount - bitCountHi;
+ uint32_t resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
+
+ if (!drflac__reload_cache(bs)) {
+ return DR_FALSE;
+ }
+
+ *pResultOut = (resultHi << bitCountLo) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
+ bs->consumedBits += bitCountLo;
+ bs->cache <<= bitCountLo;
+ return DR_TRUE;
+ }
+}
+
+static drBool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, int32_t* pResult)
+{
+ assert(bs != NULL);
+ assert(pResult != NULL);
+ assert(bitCount > 0);
+ assert(bitCount <= 32);
+
+ uint32_t result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DR_FALSE;
+ }
+
+ uint32_t signbit = ((result >> (bitCount-1)) & 0x01);
+ result |= (~signbit + 1) << bitCount;
+
+ *pResult = (int32_t)result;
+ return DR_TRUE;
+}
+
+static drBool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, uint64_t* pResultOut)
+{
+ assert(bitCount <= 64);
+ assert(bitCount > 32);
+
+ uint32_t resultHi;
+ if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
+ return DR_FALSE;
+ }
+
+ uint32_t resultLo;
+ if (!drflac__read_uint32(bs, 32, &resultLo)) {
+ return DR_FALSE;
+ }
+
+ *pResultOut = (((uint64_t)resultHi) << 32) | ((uint64_t)resultLo);
+ return DR_TRUE;
+}
+
+// Function below is unused, but leaving it here in case I need to quickly add it again.
+#if 0
+static drBool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, int64_t* pResultOut)
+{
+ assert(bitCount <= 64);
+
+ uint64_t result;
+ if (!drflac__read_uint64(bs, bitCount, &result)) {
+ return DR_FALSE;
+ }
+
+ uint64_t signbit = ((result >> (bitCount-1)) & 0x01);
+ result |= (~signbit + 1) << bitCount;
+
+ *pResultOut = (int64_t)result;
+ return DR_TRUE;
+}
+#endif
+
+static drBool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, uint16_t* pResult)
+{
+ assert(bs != NULL);
+ assert(pResult != NULL);
+ assert(bitCount > 0);
+ assert(bitCount <= 16);
+
+ uint32_t result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DR_FALSE;
+ }
+
+ *pResult = (uint16_t)result;
+ return DR_TRUE;
+}
+
+static drBool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, int16_t* pResult)
+{
+ assert(bs != NULL);
+ assert(pResult != NULL);
+ assert(bitCount > 0);
+ assert(bitCount <= 16);
+
+ int32_t result;
+ if (!drflac__read_int32(bs, bitCount, &result)) {
+ return DR_FALSE;
+ }
+
+ *pResult = (int16_t)result;
+ return DR_TRUE;
+}
+
+static drBool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, uint8_t* pResult)
+{
+ assert(bs != NULL);
+ assert(pResult != NULL);
+ assert(bitCount > 0);
+ assert(bitCount <= 8);
+
+ uint32_t result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DR_FALSE;
+ }
+
+ *pResult = (uint8_t)result;
+ return DR_TRUE;
+}
+
+static drBool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, int8_t* pResult)
+{
+ assert(bs != NULL);
+ assert(pResult != NULL);
+ assert(bitCount > 0);
+ assert(bitCount <= 8);
+
+ int32_t result;
+ if (!drflac__read_int32(bs, bitCount, &result)) {
+ return DR_FALSE;
+ }
+
+ *pResult = (int8_t)result;
+ return DR_TRUE;
+}
+
+
+static inline drBool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
+{
+ unsigned int zeroCounter = 0;
+ while (bs->cache == 0) {
+ zeroCounter += (unsigned int)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DR_FALSE;
+ }
+ }
+
+ // At this point the cache should not be zero, in which case we know the first set bit should be somewhere in here. There is
+ // no need for us to perform any cache reloading logic here which should make things much faster.
+ assert(bs->cache != 0);
+
+ unsigned int bitOffsetTable[] = {
+ 0,
+ 4,
+ 3, 3,
+ 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+ unsigned int setBitOffsetPlus1 = bitOffsetTable[DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, 4)];
+ if (setBitOffsetPlus1 == 0) {
+ if (bs->cache == 1) {
+ setBitOffsetPlus1 = DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ } else {
+ setBitOffsetPlus1 = 5;
+ for (;;)
+ {
+ if ((bs->cache & DRFLAC_CACHE_L1_SELECT(bs, setBitOffsetPlus1))) {
+ break;
+ }
+
+ setBitOffsetPlus1 += 1;
+ }
+ }
+ }
+
+ bs->consumedBits += setBitOffsetPlus1;
+ bs->cache <<= setBitOffsetPlus1;
+
+ *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
+ return DR_TRUE;
+}
+
+
+
+static drBool32 drflac__seek_to_byte(drflac_bs* bs, uint64_t offsetFromStart)
+{
+ assert(bs != NULL);
+ assert(offsetFromStart > 0);
+
+ // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which
+ // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit.
+ // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder.
+ if (offsetFromStart > 0x7FFFFFFF)
+ {
+ uint64_t bytesRemaining = offsetFromStart;
+ if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+ return DR_FALSE;
+ }
+ bytesRemaining -= 0x7FFFFFFF;
+
+
+ while (bytesRemaining > 0x7FFFFFFF) {
+ if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ bytesRemaining -= 0x7FFFFFFF;
+ }
+
+
+ if (bytesRemaining > 0) {
+ if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ }
+ }
+ else
+ {
+ if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
+ return DR_FALSE;
+ }
+ }
+
+
+ // The cache should be reset to force a reload of fresh data from the client.
+ drflac__reset_cache(bs);
+ return DR_TRUE;
+}
+
+
+static drBool32 drflac__read_utf8_coded_number(drflac_bs* bs, uint64_t* pNumberOut)
+{
+ assert(bs != NULL);
+ assert(pNumberOut != NULL);
+
+ unsigned char utf8[7] = {0};
+ if (!drflac__read_uint8(bs, 8, utf8)) {
+ *pNumberOut = 0;
+ return DR_FALSE;
+ }
+
+ if ((utf8[0] & 0x80) == 0) {
+ *pNumberOut = utf8[0];
+ return DR_TRUE;
+ }
+
+ int byteCount = 1;
+ if ((utf8[0] & 0xE0) == 0xC0) {
+ byteCount = 2;
+ } else if ((utf8[0] & 0xF0) == 0xE0) {
+ byteCount = 3;
+ } else if ((utf8[0] & 0xF8) == 0xF0) {
+ byteCount = 4;
+ } else if ((utf8[0] & 0xFC) == 0xF8) {
+ byteCount = 5;
+ } else if ((utf8[0] & 0xFE) == 0xFC) {
+ byteCount = 6;
+ } else if ((utf8[0] & 0xFF) == 0xFE) {
+ byteCount = 7;
+ } else {
+ *pNumberOut = 0;
+ return DR_FALSE; // Bad UTF-8 encoding.
+ }
+
+ // Read extra bytes.
+ assert(byteCount > 1);
+
+ uint64_t result = (uint64_t)(utf8[0] & (0xFF >> (byteCount + 1)));
+ for (int i = 1; i < byteCount; ++i) {
+ if (!drflac__read_uint8(bs, 8, utf8 + i)) {
+ *pNumberOut = 0;
+ return DR_FALSE;
+ }
+
+ result = (result << 6) | (utf8[i] & 0x3F);
+ }
+
+ *pNumberOut = result;
+ return DR_TRUE;
+}
+
+
+
+static DRFLAC_INLINE drBool32 drflac__read_and_seek_rice(drflac_bs* bs, uint8_t m)
+{
+ unsigned int unused;
+ if (!drflac__seek_past_next_set_bit(bs, &unused)) {
+ return DR_FALSE;
+ }
+
+ if (m > 0) {
+ if (!drflac__seek_bits(bs, m)) {
+ return DR_FALSE;
+ }
+ }
+
+ return DR_TRUE;
+}
+
+
+// The next two functions are responsible for calculating the prediction.
+//
+// When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's
+// safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16.
+static DRFLAC_INLINE int32_t drflac__calculate_prediction_32(uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples)
+{
+ assert(order <= 32);
+
+ // 32-bit version.
+
+ // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers.
+ int32_t prediction = 0;
+
+ switch (order)
+ {
+ case 32: prediction += coefficients[31] * pDecodedSamples[-32];
+ case 31: prediction += coefficients[30] * pDecodedSamples[-31];
+ case 30: prediction += coefficients[29] * pDecodedSamples[-30];
+ case 29: prediction += coefficients[28] * pDecodedSamples[-29];
+ case 28: prediction += coefficients[27] * pDecodedSamples[-28];
+ case 27: prediction += coefficients[26] * pDecodedSamples[-27];
+ case 26: prediction += coefficients[25] * pDecodedSamples[-26];
+ case 25: prediction += coefficients[24] * pDecodedSamples[-25];
+ case 24: prediction += coefficients[23] * pDecodedSamples[-24];
+ case 23: prediction += coefficients[22] * pDecodedSamples[-23];
+ case 22: prediction += coefficients[21] * pDecodedSamples[-22];
+ case 21: prediction += coefficients[20] * pDecodedSamples[-21];
+ case 20: prediction += coefficients[19] * pDecodedSamples[-20];
+ case 19: prediction += coefficients[18] * pDecodedSamples[-19];
+ case 18: prediction += coefficients[17] * pDecodedSamples[-18];
+ case 17: prediction += coefficients[16] * pDecodedSamples[-17];
+ case 16: prediction += coefficients[15] * pDecodedSamples[-16];
+ case 15: prediction += coefficients[14] * pDecodedSamples[-15];
+ case 14: prediction += coefficients[13] * pDecodedSamples[-14];
+ case 13: prediction += coefficients[12] * pDecodedSamples[-13];
+ case 12: prediction += coefficients[11] * pDecodedSamples[-12];
+ case 11: prediction += coefficients[10] * pDecodedSamples[-11];
+ case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
+ case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
+ case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
+ case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
+ case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
+ case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
+ case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
+ case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
+ case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
+ case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
+ }
+
+ return (int32_t)(prediction >> shift);
+}
+
+static DRFLAC_INLINE int32_t drflac__calculate_prediction_64(uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples)
+{
+ assert(order <= 32);
+
+ // 64-bit version.
+
+ // This method is faster on the 32-bit build when compiling with VC++. See note below.
+#ifndef DRFLAC_64BIT
+ int64_t prediction;
+ if (order == 8)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ prediction += coefficients[5] * (int64_t)pDecodedSamples[-6];
+ prediction += coefficients[6] * (int64_t)pDecodedSamples[-7];
+ prediction += coefficients[7] * (int64_t)pDecodedSamples[-8];
+ }
+ else if (order == 7)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ prediction += coefficients[5] * (int64_t)pDecodedSamples[-6];
+ prediction += coefficients[6] * (int64_t)pDecodedSamples[-7];
+ }
+ else if (order == 3)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ }
+ else if (order == 6)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ prediction += coefficients[5] * (int64_t)pDecodedSamples[-6];
+ }
+ else if (order == 5)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ }
+ else if (order == 4)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ }
+ else if (order == 12)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ prediction += coefficients[5] * (int64_t)pDecodedSamples[-6];
+ prediction += coefficients[6] * (int64_t)pDecodedSamples[-7];
+ prediction += coefficients[7] * (int64_t)pDecodedSamples[-8];
+ prediction += coefficients[8] * (int64_t)pDecodedSamples[-9];
+ prediction += coefficients[9] * (int64_t)pDecodedSamples[-10];
+ prediction += coefficients[10] * (int64_t)pDecodedSamples[-11];
+ prediction += coefficients[11] * (int64_t)pDecodedSamples[-12];
+ }
+ else if (order == 2)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ }
+ else if (order == 1)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ }
+ else if (order == 10)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ prediction += coefficients[5] * (int64_t)pDecodedSamples[-6];
+ prediction += coefficients[6] * (int64_t)pDecodedSamples[-7];
+ prediction += coefficients[7] * (int64_t)pDecodedSamples[-8];
+ prediction += coefficients[8] * (int64_t)pDecodedSamples[-9];
+ prediction += coefficients[9] * (int64_t)pDecodedSamples[-10];
+ }
+ else if (order == 9)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ prediction += coefficients[5] * (int64_t)pDecodedSamples[-6];
+ prediction += coefficients[6] * (int64_t)pDecodedSamples[-7];
+ prediction += coefficients[7] * (int64_t)pDecodedSamples[-8];
+ prediction += coefficients[8] * (int64_t)pDecodedSamples[-9];
+ }
+ else if (order == 11)
+ {
+ prediction = coefficients[0] * (int64_t)pDecodedSamples[-1];
+ prediction += coefficients[1] * (int64_t)pDecodedSamples[-2];
+ prediction += coefficients[2] * (int64_t)pDecodedSamples[-3];
+ prediction += coefficients[3] * (int64_t)pDecodedSamples[-4];
+ prediction += coefficients[4] * (int64_t)pDecodedSamples[-5];
+ prediction += coefficients[5] * (int64_t)pDecodedSamples[-6];
+ prediction += coefficients[6] * (int64_t)pDecodedSamples[-7];
+ prediction += coefficients[7] * (int64_t)pDecodedSamples[-8];
+ prediction += coefficients[8] * (int64_t)pDecodedSamples[-9];
+ prediction += coefficients[9] * (int64_t)pDecodedSamples[-10];
+ prediction += coefficients[10] * (int64_t)pDecodedSamples[-11];
+ }
+ else
+ {
+ prediction = 0;
+ for (int j = 0; j < (int)order; ++j) {
+ prediction += coefficients[j] * (int64_t)pDecodedSamples[-j-1];
+ }
+ }
+#endif
+
+ // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some
+ // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform.
+#ifdef DRFLAC_64BIT
+ int64_t prediction = 0;
+
+ switch (order)
+ {
+ case 32: prediction += coefficients[31] * (int64_t)pDecodedSamples[-32];
+ case 31: prediction += coefficients[30] * (int64_t)pDecodedSamples[-31];
+ case 30: prediction += coefficients[29] * (int64_t)pDecodedSamples[-30];
+ case 29: prediction += coefficients[28] * (int64_t)pDecodedSamples[-29];
+ case 28: prediction += coefficients[27] * (int64_t)pDecodedSamples[-28];
+ case 27: prediction += coefficients[26] * (int64_t)pDecodedSamples[-27];
+ case 26: prediction += coefficients[25] * (int64_t)pDecodedSamples[-26];
+ case 25: prediction += coefficients[24] * (int64_t)pDecodedSamples[-25];
+ case 24: prediction += coefficients[23] * (int64_t)pDecodedSamples[-24];
+ case 23: prediction += coefficients[22] * (int64_t)pDecodedSamples[-23];
+ case 22: prediction += coefficients[21] * (int64_t)pDecodedSamples[-22];
+ case 21: prediction += coefficients[20] * (int64_t)pDecodedSamples[-21];
+ case 20: prediction += coefficients[19] * (int64_t)pDecodedSamples[-20];
+ case 19: prediction += coefficients[18] * (int64_t)pDecodedSamples[-19];
+ case 18: prediction += coefficients[17] * (int64_t)pDecodedSamples[-18];
+ case 17: prediction += coefficients[16] * (int64_t)pDecodedSamples[-17];
+ case 16: prediction += coefficients[15] * (int64_t)pDecodedSamples[-16];
+ case 15: prediction += coefficients[14] * (int64_t)pDecodedSamples[-15];
+ case 14: prediction += coefficients[13] * (int64_t)pDecodedSamples[-14];
+ case 13: prediction += coefficients[12] * (int64_t)pDecodedSamples[-13];
+ case 12: prediction += coefficients[11] * (int64_t)pDecodedSamples[-12];
+ case 11: prediction += coefficients[10] * (int64_t)pDecodedSamples[-11];
+ case 10: prediction += coefficients[ 9] * (int64_t)pDecodedSamples[-10];
+ case 9: prediction += coefficients[ 8] * (int64_t)pDecodedSamples[- 9];
+ case 8: prediction += coefficients[ 7] * (int64_t)pDecodedSamples[- 8];
+ case 7: prediction += coefficients[ 6] * (int64_t)pDecodedSamples[- 7];
+ case 6: prediction += coefficients[ 5] * (int64_t)pDecodedSamples[- 6];
+ case 5: prediction += coefficients[ 4] * (int64_t)pDecodedSamples[- 5];
+ case 4: prediction += coefficients[ 3] * (int64_t)pDecodedSamples[- 4];
+ case 3: prediction += coefficients[ 2] * (int64_t)pDecodedSamples[- 3];
+ case 2: prediction += coefficients[ 1] * (int64_t)pDecodedSamples[- 2];
+ case 1: prediction += coefficients[ 0] * (int64_t)pDecodedSamples[- 1];
+ }
+#endif
+
+ return (int32_t)(prediction >> shift);
+}
+
+
+// Reads and decodes a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes.
+//
+// This is the most frequently called function in the library. It does both the Rice decoding and the prediction in a single loop
+// iteration. The prediction is done at the end, and there's an annoying branch I'd like to avoid so the main function is defined
+// as a #define - sue me!
+#define DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(funcName, predictionFunc) \
+static drBool32 funcName (drflac_bs* bs, uint32_t count, uint8_t riceParam, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pSamplesOut) \
+{ \
+ assert(bs != NULL); \
+ assert(count > 0); \
+ assert(pSamplesOut != NULL); \
+ \
+ static unsigned int bitOffsetTable[] = { \
+ 0, \
+ 4, \
+ 3, 3, \
+ 2, 2, 2, 2, \
+ 1, 1, 1, 1, 1, 1, 1, 1 \
+ }; \
+ \
+ drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); \
+ drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; \
+ \
+ for (int i = 0; i < (int)count; ++i) \
+ { \
+ unsigned int zeroCounter = 0; \
+ while (bs->cache == 0) { \
+ zeroCounter += (unsigned int)DRFLAC_CACHE_L1_BITS_REMAINING(bs); \
+ if (!drflac__reload_cache(bs)) { \
+ return DR_FALSE; \
+ } \
+ } \
+ \
+ /* At this point the cache should not be zero, in which case we know the first set bit should be somewhere in here. There is \
+ no need for us to perform any cache reloading logic here which should make things much faster. */ \
+ assert(bs->cache != 0); \
+ unsigned int decodedRice; \
+ \
+ unsigned int setBitOffsetPlus1 = bitOffsetTable[DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, 4)]; \
+ if (setBitOffsetPlus1 > 0) { \
+ decodedRice = (zeroCounter + (setBitOffsetPlus1-1)) << riceParam; \
+ } else { \
+ if (bs->cache == 1) { \
+ setBitOffsetPlus1 = DRFLAC_CACHE_L1_SIZE_BITS(bs); \
+ decodedRice = (zeroCounter + (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)) << riceParam; \
+ } else { \
+ setBitOffsetPlus1 = 5; \
+ for (;;) \
+ { \
+ if ((bs->cache & DRFLAC_CACHE_L1_SELECT(bs, setBitOffsetPlus1))) { \
+ decodedRice = (zeroCounter + (setBitOffsetPlus1-1)) << riceParam; \
+ break; \
+ } \
+ \
+ setBitOffsetPlus1 += 1; \
+ } \
+ } \
+ } \
+ \
+ \
+ unsigned int bitsLo = 0; \
+ unsigned int riceLength = setBitOffsetPlus1 + riceParam; \
+ if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) \
+ { \
+ bitsLo = (unsigned int)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); \
+ \
+ bs->consumedBits += riceLength; \
+ bs->cache <<= riceLength; \
+ } \
+ else \
+ { \
+ bs->consumedBits += riceLength; \
+ bs->cache <<= setBitOffsetPlus1; \
+ \
+ /* It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. */ \
+ size_t bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); \
+ drflac_cache_t resultHi = bs->cache & riceParamMask; /* <-- This mask is OK because all bits after the first bits are always zero. */ \
+ \
+ \
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { \
+ bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); \
+ } else { \
+ /* Slow path. We need to fetch more data from the client. */ \
+ if (!drflac__reload_cache(bs)) { \
+ return DR_FALSE; \
+ } \
+ } \
+ \
+ bitsLo = (unsigned int)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); \
+ bs->consumedBits = bitCountLo; \
+ bs->cache <<= bitCountLo; \
+ } \
+ \
+ decodedRice |= bitsLo; \
+ decodedRice = (decodedRice >> 1) ^ (~(decodedRice & 0x01) + 1); /* <-- Ah, much faster! :) */ \
+ /* \
+ if ((decodedRice & 0x01)) { \
+ decodedRice = ~(decodedRice >> 1); \
+ } else { \
+ decodedRice = (decodedRice >> 1); \
+ } \
+ */ \
+ \
+ /* In order to properly calculate the prediction when the bits per sample is >16 we need to do it using 64-bit arithmetic. We can assume this \
+ is probably going to be slower on 32-bit systems so we'll do a more optimized 32-bit version when the bits per sample is low enough.*/ \
+ pSamplesOut[i] = ((int)decodedRice + predictionFunc(order, shift, coefficients, pSamplesOut + i)); \
+ } \
+ \
+ return DR_TRUE; \
+} \
+
+DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(drflac__decode_samples_with_residual__rice_64, drflac__calculate_prediction_64)
+DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(drflac__decode_samples_with_residual__rice_32, drflac__calculate_prediction_32)
+
+
+// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes.
+static drBool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, uint32_t count, uint8_t riceParam)
+{
+ assert(bs != NULL);
+ assert(count > 0);
+
+ for (uint32_t i = 0; i < count; ++i) {
+ if (!drflac__read_and_seek_rice(bs, riceParam)) {
+ return DR_FALSE;
+ }
+ }
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, uint32_t bitsPerSample, uint32_t count, uint8_t unencodedBitsPerSample, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pSamplesOut)
+{
+ assert(bs != NULL);
+ assert(count > 0);
+ assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32);
+ assert(pSamplesOut != NULL);
+
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
+ return DR_FALSE;
+ }
+
+ if (bitsPerSample > 16) {
+ pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
+ } else {
+ pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
+ }
+ }
+
+ return DR_TRUE;
+}
+
+
+// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called
+// when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be ignored. The
+// <blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+static drBool32 drflac__decode_samples_with_residual(drflac_bs* bs, uint32_t bitsPerSample, uint32_t blockSize, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples)
+{
+ assert(bs != NULL);
+ assert(blockSize != 0);
+ assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode?
+
+ uint8_t residualMethod;
+ if (!drflac__read_uint8(bs, 2, &residualMethod)) {
+ return DR_FALSE;
+ }
+
+ if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ return DR_FALSE; // Unknown or unsupported residual coding method.
+ }
+
+ // Ignore the first <order> values.
+ pDecodedSamples += order;
+
+
+ uint8_t partitionOrder;
+ if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
+ return DR_FALSE;
+ }
+
+
+ uint32_t samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ uint32_t partitionsRemaining = (1 << partitionOrder);
+ for (;;)
+ {
+ uint8_t riceParam = 0;
+ if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
+ if (!drflac__read_uint8(bs, 4, &riceParam)) {
+ return DR_FALSE;
+ }
+ if (riceParam == 16) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DR_FALSE;
+ }
+ if (riceParam == 32) {
+ riceParam = 0xFF;
+ }
+ }
+
+ if (riceParam != 0xFF) {
+ if (bitsPerSample > 16) {
+ if (!drflac__decode_samples_with_residual__rice_64(bs, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) {
+ return DR_FALSE;
+ }
+ } else {
+ if (!drflac__decode_samples_with_residual__rice_32(bs, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) {
+ return DR_FALSE;
+ }
+ }
+ } else {
+ unsigned char unencodedBitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
+ return DR_FALSE;
+ }
+
+ if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) {
+ return DR_FALSE;
+ }
+ }
+
+ pDecodedSamples += samplesInPartition;
+
+
+ if (partitionsRemaining == 1) {
+ break;
+ }
+
+ partitionsRemaining -= 1;
+ samplesInPartition = blockSize / (1 << partitionOrder);
+ }
+
+ return DR_TRUE;
+}
+
+// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called
+// when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be set to 0. The
+// <blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+static drBool32 drflac__read_and_seek_residual(drflac_bs* bs, uint32_t blockSize, uint32_t order)
+{
+ assert(bs != NULL);
+ assert(blockSize != 0);
+
+ uint8_t residualMethod;
+ if (!drflac__read_uint8(bs, 2, &residualMethod)) {
+ return DR_FALSE;
+ }
+
+ if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ return DR_FALSE; // Unknown or unsupported residual coding method.
+ }
+
+ uint8_t partitionOrder;
+ if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
+ return DR_FALSE;
+ }
+
+ uint32_t samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ uint32_t partitionsRemaining = (1 << partitionOrder);
+ for (;;)
+ {
+ uint8_t riceParam = 0;
+ if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
+ if (!drflac__read_uint8(bs, 4, &riceParam)) {
+ return DR_FALSE;
+ }
+ if (riceParam == 16) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DR_FALSE;
+ }
+ if (riceParam == 32) {
+ riceParam = 0xFF;
+ }
+ }
+
+ if (riceParam != 0xFF) {
+ if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
+ return DR_FALSE;
+ }
+ } else {
+ unsigned char unencodedBitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
+ return DR_FALSE;
+ }
+
+ if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
+ return DR_FALSE;
+ }
+ }
+
+
+ if (partitionsRemaining == 1) {
+ break;
+ }
+
+ partitionsRemaining -= 1;
+ samplesInPartition = blockSize / (1 << partitionOrder);
+ }
+
+ return DR_TRUE;
+}
+
+
+static drBool32 drflac__decode_samples__constant(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples)
+{
+ // Only a single sample needs to be decoded here.
+ int32_t sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DR_FALSE;
+ }
+
+ // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely)
+ // we'll want to look at a more efficient way.
+ for (uint32_t i = 0; i < blockSize; ++i) {
+ pDecodedSamples[i] = sample;
+ }
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__decode_samples__verbatim(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples)
+{
+ for (uint32_t i = 0; i < blockSize; ++i) {
+ int32_t sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DR_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__decode_samples__fixed(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples)
+{
+ short lpcCoefficientsTable[5][4] = {
+ {0, 0, 0, 0},
+ {1, 0, 0, 0},
+ {2, -1, 0, 0},
+ {3, -3, 1, 0},
+ {4, -6, 4, -1}
+ };
+
+ // Warm up samples and coefficients.
+ for (uint32_t i = 0; i < lpcOrder; ++i) {
+ int32_t sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DR_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+
+ if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
+ return DR_FALSE;
+ }
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__decode_samples__lpc(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples)
+{
+ // Warm up samples.
+ for (uint8_t i = 0; i < lpcOrder; ++i) {
+ int32_t sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DR_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+ uint8_t lpcPrecision;
+ if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
+ return DR_FALSE;
+ }
+ if (lpcPrecision == 15) {
+ return DR_FALSE; // Invalid.
+ }
+ lpcPrecision += 1;
+
+
+ int8_t lpcShift;
+ if (!drflac__read_int8(bs, 5, &lpcShift)) {
+ return DR_FALSE;
+ }
+
+
+ int16_t coefficients[32];
+ for (uint8_t i = 0; i < lpcOrder; ++i) {
+ if (!drflac__read_int16(bs, lpcPrecision, coefficients + i)) {
+ return DR_FALSE;
+ }
+ }
+
+ if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) {
+ return DR_FALSE;
+ }
+
+ return DR_TRUE;
+}
+
+
+static drBool32 drflac__read_next_frame_header(drflac_bs* bs, uint8_t streaminfoBitsPerSample, drflac_frame_header* header)
+{
+ assert(bs != NULL);
+ assert(header != NULL);
+
+ // At the moment the sync code is as a form of basic validation. The CRC is stored, but is unused at the moment. This
+ // should probably be handled better in the future.
+
+ const uint32_t sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
+ const uint8_t bitsPerSampleTable[8] = {0, 8, 12, (uint8_t)-1, 16, 20, 24, (uint8_t)-1}; // -1 = reserved.
+
+ uint16_t syncCode = 0;
+ if (!drflac__read_uint16(bs, 14, &syncCode)) {
+ return DR_FALSE;
+ }
+
+ if (syncCode != 0x3FFE) {
+ // TODO: Try and recover by attempting to seek to and read the next frame?
+ return DR_FALSE;
+ }
+
+ uint8_t reserved;
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DR_FALSE;
+ }
+
+ uint8_t blockingStrategy = 0;
+ if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
+ return DR_FALSE;
+ }
+
+
+
+ uint8_t blockSize = 0;
+ if (!drflac__read_uint8(bs, 4, &blockSize)) {
+ return DR_FALSE;
+ }
+
+ uint8_t sampleRate = 0;
+ if (!drflac__read_uint8(bs, 4, &sampleRate)) {
+ return DR_FALSE;
+ }
+
+ uint8_t channelAssignment = 0;
+ if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
+ return DR_FALSE;
+ }
+
+ uint8_t bitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
+ return DR_FALSE;
+ }
+
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DR_FALSE;
+ }
+
+
+ drBool32 isVariableBlockSize = blockingStrategy == 1;
+ if (isVariableBlockSize) {
+ uint64_t sampleNumber;
+ if (!drflac__read_utf8_coded_number(bs, &sampleNumber)) {
+ return DR_FALSE;
+ }
+ header->frameNumber = 0;
+ header->sampleNumber = sampleNumber;
+ } else {
+ uint64_t frameNumber = 0;
+ if (!drflac__read_utf8_coded_number(bs, &frameNumber)) {
+ return DR_FALSE;
+ }
+ header->frameNumber = (uint32_t)frameNumber; // <-- Safe cast.
+ header->sampleNumber = 0;
+ }
+
+
+ if (blockSize == 1) {
+ header->blockSize = 192;
+ } else if (blockSize >= 2 && blockSize <= 5) {
+ header->blockSize = 576 * (1 << (blockSize - 2));
+ } else if (blockSize == 6) {
+ if (!drflac__read_uint16(bs, 8, &header->blockSize)) {
+ return DR_FALSE;
+ }
+ header->blockSize += 1;
+ } else if (blockSize == 7) {
+ if (!drflac__read_uint16(bs, 16, &header->blockSize)) {
+ return DR_FALSE;
+ }
+ header->blockSize += 1;
+ } else {
+ header->blockSize = 256 * (1 << (blockSize - 8));
+ }
+
+
+ if (sampleRate <= 11) {
+ header->sampleRate = sampleRateTable[sampleRate];
+ } else if (sampleRate == 12) {
+ if (!drflac__read_uint32(bs, 8, &header->sampleRate)) {
+ return DR_FALSE;
+ }
+ header->sampleRate *= 1000;
+ } else if (sampleRate == 13) {
+ if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
+ return DR_FALSE;
+ }
+ } else if (sampleRate == 14) {
+ if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
+ return DR_FALSE;
+ }
+ header->sampleRate *= 10;
+ } else {
+ return DR_FALSE; // Invalid.
+ }
+
+
+ header->channelAssignment = channelAssignment;
+
+ header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
+ if (header->bitsPerSample == 0) {
+ header->bitsPerSample = streaminfoBitsPerSample;
+ }
+
+ if (drflac__read_uint8(bs, 8, &header->crc8) != 1) {
+ return DR_FALSE;
+ }
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
+{
+ uint8_t header;
+ if (!drflac__read_uint8(bs, 8, &header)) {
+ return DR_FALSE;
+ }
+
+ // First bit should always be 0.
+ if ((header & 0x80) != 0) {
+ return DR_FALSE;
+ }
+
+ int type = (header & 0x7E) >> 1;
+ if (type == 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
+ } else if (type == 1) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM;
+ } else {
+ if ((type & 0x20) != 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_LPC;
+ pSubframe->lpcOrder = (type & 0x1F) + 1;
+ } else if ((type & 0x08) != 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED;
+ pSubframe->lpcOrder = (type & 0x07);
+ if (pSubframe->lpcOrder > 4) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
+ pSubframe->lpcOrder = 0;
+ }
+ } else {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
+ }
+ }
+
+ if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) {
+ return DR_FALSE;
+ }
+
+ // Wasted bits per sample.
+ pSubframe->wastedBitsPerSample = 0;
+ if ((header & 0x01) == 1) {
+ unsigned int wastedBitsPerSample;
+ if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
+ return DR_FALSE;
+ }
+ pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1;
+ }
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, int32_t* pDecodedSamplesOut)
+{
+ assert(bs != NULL);
+ assert(frame != NULL);
+
+ drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ if (!drflac__read_subframe_header(bs, pSubframe)) {
+ return DR_FALSE;
+ }
+
+ // Side channels require an extra bit per sample. Took a while to figure that one out...
+ pSubframe->bitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ pSubframe->bitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ pSubframe->bitsPerSample += 1;
+ }
+
+ // Need to handle wasted bits per sample.
+ pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
+ pSubframe->pDecodedSamples = pDecodedSamplesOut;
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ drflac__decode_samples__constant(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ drflac__decode_samples__verbatim(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ drflac__decode_samples__fixed(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples);
+ } break;
+
+ default: return DR_FALSE;
+ }
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
+{
+ assert(bs != NULL);
+ assert(frame != NULL);
+
+ drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ if (!drflac__read_subframe_header(bs, pSubframe)) {
+ return DR_FALSE;
+ }
+
+ // Side channels require an extra bit per sample. Took a while to figure that one out...
+ pSubframe->bitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ pSubframe->bitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ pSubframe->bitsPerSample += 1;
+ }
+
+ // Need to handle wasted bits per sample.
+ pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
+ pSubframe->pDecodedSamples = NULL;
+ //pSubframe->pDecodedSamples = pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * subframeIndex);
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) {
+ return DR_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DR_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DR_FALSE;
+ }
+
+ if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) {
+ return DR_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DR_FALSE;
+ }
+
+ unsigned char lpcPrecision;
+ if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
+ return DR_FALSE;
+ }
+ if (lpcPrecision == 15) {
+ return DR_FALSE; // Invalid.
+ }
+ lpcPrecision += 1;
+
+
+ bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift.
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DR_FALSE;
+ }
+
+ if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) {
+ return DR_FALSE;
+ }
+ } break;
+
+ default: return DR_FALSE;
+ }
+
+ return DR_TRUE;
+}
+
+
+static DRFLAC_INLINE uint8_t drflac__get_channel_count_from_channel_assignment(int8_t channelAssignment)
+{
+ assert(channelAssignment <= 10);
+
+ uint8_t lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
+ return lookup[channelAssignment];
+}
+
+static drBool32 drflac__decode_frame(drflac* pFlac)
+{
+ // This function should be called while the stream is sitting on the first byte after the frame header.
+ memset(pFlac->currentFrame.subframes, 0, sizeof(pFlac->currentFrame.subframes));
+
+ int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ for (int i = 0; i < channelCount; ++i)
+ {
+ if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) {
+ return DR_FALSE;
+ }
+ }
+
+ // At the end of the frame sits the padding and CRC. We don't use these so we can just seek past.
+ if (!drflac__seek_bits(&pFlac->bs, (DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7) + 16)) {
+ return DR_FALSE;
+ }
+
+
+ pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount;
+
+ return DR_TRUE;
+}
+
+static drBool32 drflac__seek_frame(drflac* pFlac)
+{
+ int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ for (int i = 0; i < channelCount; ++i)
+ {
+ if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) {
+ return DR_FALSE;
+ }
+ }
+
+ // Padding and CRC.
+ return drflac__seek_bits(&pFlac->bs, (DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7) + 16);
+}
+
+static drBool32 drflac__read_and_decode_next_frame(drflac* pFlac)
+{
+ assert(pFlac != NULL);
+
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DR_FALSE;
+ }
+
+ return drflac__decode_frame(pFlac);
+}
+
+
+static void drflac__get_current_frame_sample_range(drflac* pFlac, uint64_t* pFirstSampleInFrameOut, uint64_t* pLastSampleInFrameOut)
+{
+ assert(pFlac != NULL);
+
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+
+ uint64_t firstSampleInFrame = pFlac->currentFrame.header.sampleNumber;
+ if (firstSampleInFrame == 0) {
+ firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount;
+ }
+
+ uint64_t lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount);
+ if (lastSampleInFrame > 0) {
+ lastSampleInFrame -= 1; // Needs to be zero based.
+ }
+
+
+ if (pFirstSampleInFrameOut) {
+ *pFirstSampleInFrameOut = firstSampleInFrame;
+ }
+ if (pLastSampleInFrameOut) {
+ *pLastSampleInFrameOut = lastSampleInFrame;
+ }
+}
+
+static drBool32 drflac__seek_to_first_frame(drflac* pFlac)
+{
+ assert(pFlac != NULL);
+
+ drBool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos);
+
+ memset(&pFlac->currentFrame, 0, sizeof(pFlac->currentFrame));
+ return result;
+}
+
+static DRFLAC_INLINE drBool32 drflac__seek_to_next_frame(drflac* pFlac)
+{
+ // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section.
+ assert(pFlac != NULL);
+ return drflac__seek_frame(pFlac);
+}
+
+static drBool32 drflac__seek_to_frame_containing_sample(drflac* pFlac, uint64_t sampleIndex)
+{
+ assert(pFlac != NULL);
+
+ if (!drflac__seek_to_first_frame(pFlac)) {
+ return DR_FALSE;
+ }
+
+ uint64_t firstSampleInFrame = 0;
+ uint64_t lastSampleInFrame = 0;
+ for (;;)
+ {
+ // We need to read the frame's header in order to determine the range of samples it contains.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DR_FALSE;
+ }
+
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+ if (sampleIndex >= firstSampleInFrame && sampleIndex <= lastSampleInFrame) {
+ break; // The sample is in this frame.
+ }
+
+ if (!drflac__seek_to_next_frame(pFlac)) {
+ return DR_FALSE;
+ }
+ }
+
+ // If we get here we should be right at the start of the frame containing the sample.
+ return DR_TRUE;
+}
+
+static drBool32 drflac__seek_to_sample__brute_force(drflac* pFlac, uint64_t sampleIndex)
+{
+ if (!drflac__seek_to_frame_containing_sample(pFlac, sampleIndex)) {
+ return DR_FALSE;
+ }
+
+ // At this point we should be sitting on the first byte of the frame containing the sample. We need to decode every sample up to (but
+ // not including) the sample we're seeking to.
+ uint64_t firstSampleInFrame = 0;
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, NULL);
+
+ assert(firstSampleInFrame <= sampleIndex);
+ size_t samplesToDecode = (size_t)(sampleIndex - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535.
+ if (samplesToDecode == 0) {
+ return DR_TRUE;
+ }
+
+ // At this point we are just sitting on the byte after the frame header. We need to decode the frame before reading anything from it.
+ if (!drflac__decode_frame(pFlac)) {
+ return DR_FALSE;
+ }
+
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0;
+}
+
+
+static drBool32 drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t sampleIndex)
+{
+ assert(pFlac != NULL);
+
+ if (pFlac->seektablePos == 0) {
+ return DR_FALSE;
+ }
+
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) {
+ return DR_FALSE;
+ }
+
+ // The number of seek points is derived from the size of the SEEKTABLE block.
+ uint32_t seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point.
+ if (seekpointCount == 0) {
+ return DR_FALSE; // Would this ever happen?
+ }
+
+
+ drflac_seekpoint closestSeekpoint = {0, 0, 0};
+
+ uint32_t seekpointsRemaining = seekpointCount;
+ while (seekpointsRemaining > 0)
+ {
+ drflac_seekpoint seekpoint;
+ if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) {
+ break;
+ }
+ if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) {
+ break;
+ }
+ if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) {
+ break;
+ }
+
+ if (seekpoint.firstSample * pFlac->channels > sampleIndex) {
+ break;
+ }
+
+ closestSeekpoint = seekpoint;
+ seekpointsRemaining -= 1;
+ }
+
+ // At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same
+ // technique as we use with the brute force method.
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) {
+ return DR_FALSE;
+ }
+
+
+ uint64_t firstSampleInFrame = 0;
+ uint64_t lastSampleInFrame = 0;
+ for (;;)
+ {
+ // We need to read the frame's header in order to determine the range of samples it contains.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DR_FALSE;
+ }
+
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+ if (sampleIndex >= firstSampleInFrame && sampleIndex <= lastSampleInFrame) {
+ break; // The sample is in this frame.
+ }
+
+ if (!drflac__seek_to_next_frame(pFlac)) {
+ return DR_FALSE;
+ }
+ }
+
+ assert(firstSampleInFrame <= sampleIndex);
+
+ // At this point we are just sitting on the byte after the frame header. We need to decode the frame before reading anything from it.
+ if (!drflac__decode_frame(pFlac)) {
+ return DR_FALSE;
+ }
+
+ size_t samplesToDecode = (size_t)(sampleIndex - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535.
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode;
+}
+
+
+#ifndef DR_FLAC_NO_OGG
+typedef struct
+{
+ uint8_t capturePattern[4]; // Should be "OggS"
+ uint8_t structureVersion; // Always 0.
+ uint8_t headerType;
+ uint64_t granulePosition;
+ uint32_t serialNumber;
+ uint32_t sequenceNumber;
+ uint32_t checksum;
+ uint8_t segmentCount;
+ uint8_t segmentTable[255];
+} drflac_ogg_page_header;
+#endif
+
+typedef struct
+{
+ drflac_read_proc onRead;
+ drflac_seek_proc onSeek;
+ drflac_meta_proc onMeta;
+ void* pUserData;
+ void* pUserDataMD;
+ drflac_container container;
+ uint32_t sampleRate;
+ uint8_t channels;
+ uint8_t bitsPerSample;
+ uint64_t totalSampleCount;
+ uint16_t maxBlockSize;
+ uint64_t runningFilePos;
+ drBool32 hasMetadataBlocks;
+
+#ifndef DR_FLAC_NO_OGG
+ uint32_t oggSerial;
+ uint64_t oggFirstBytePos;
+ drflac_ogg_page_header oggBosHeader;
+#endif
+} drflac_init_info;
+
+static DRFLAC_INLINE void drflac__decode_block_header(uint32_t blockHeader, uint8_t* isLastBlock, uint8_t* blockType, uint32_t* blockSize)
+{
+ blockHeader = drflac__be2host_32(blockHeader);
+ *isLastBlock = (blockHeader & (0x01 << 31)) >> 31;
+ *blockType = (blockHeader & (0x7F << 24)) >> 24;
+ *blockSize = (blockHeader & 0xFFFFFF);
+}
+
+static DRFLAC_INLINE drBool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, uint8_t* isLastBlock, uint8_t* blockType, uint32_t* blockSize)
+{
+ uint32_t blockHeader;
+ if (onRead(pUserData, &blockHeader, 4) != 4) {
+ return DR_FALSE;
+ }
+
+ drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
+ return DR_TRUE;
+}
+
+drBool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
+{
+ // min/max block size.
+ uint32_t blockSizes;
+ if (onRead(pUserData, &blockSizes, 4) != 4) {
+ return DR_FALSE;
+ }
+
+ // min/max frame size.
+ uint64_t frameSizes = 0;
+ if (onRead(pUserData, &frameSizes, 6) != 6) {
+ return DR_FALSE;
+ }
+
+ // Sample rate, channels, bits per sample and total sample count.
+ uint64_t importantProps;
+ if (onRead(pUserData, &importantProps, 8) != 8) {
+ return DR_FALSE;
+ }
+
+ // MD5
+ uint8_t md5[16];
+ if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
+ return DR_FALSE;
+ }
+
+ blockSizes = drflac__be2host_32(blockSizes);
+ frameSizes = drflac__be2host_64(frameSizes);
+ importantProps = drflac__be2host_64(importantProps);
+
+ pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16;
+ pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF;
+ pStreamInfo->minFrameSize = (uint32_t)((frameSizes & 0xFFFFFF0000000000ULL) >> 40ULL);
+ pStreamInfo->maxFrameSize = (uint32_t)((frameSizes & 0x000000FFFFFF0000ULL) >> 16ULL);
+ pStreamInfo->sampleRate = (uint32_t)((importantProps & 0xFFFFF00000000000ULL) >> 44ULL);
+ pStreamInfo->channels = (uint8_t )((importantProps & 0x00000E0000000000ULL) >> 41ULL) + 1;
+ pStreamInfo->bitsPerSample = (uint8_t )((importantProps & 0x000001F000000000ULL) >> 36ULL) + 1;
+ pStreamInfo->totalSampleCount = (importantProps & 0x0000000FFFFFFFFFULL) * pStreamInfo->channels;
+ memcpy(pStreamInfo->md5, md5, sizeof(md5));
+
+ return DR_TRUE;
+}
+
+drBool32 drflac__read_and_decode_metadata(drflac* pFlac)
+{
+ assert(pFlac != NULL);
+
+ // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that
+ // we'll be sitting on byte 42.
+ uint64_t runningFilePos = 42;
+ uint64_t seektablePos = 0;
+ uint32_t seektableSize = 0;
+
+ for (;;)
+ {
+ uint8_t isLastBlock = 0;
+ uint8_t blockType;
+ uint32_t blockSize;
+ if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DR_FALSE;
+ }
+ runningFilePos += 4;
+
+
+ drflac_metadata metadata;
+ metadata.type = blockType;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+
+ switch (blockType)
+ {
+ case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
+ {
+ if (pFlac->onMeta) {
+ void* pRawData = malloc(blockSize);
+ if (pRawData == NULL) {
+ return DR_FALSE;
+ }
+
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) {
+ free(pRawData);
+ return DR_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ metadata.data.application.id = drflac__be2host_32(*(uint32_t*)pRawData);
+ metadata.data.application.pData = (const void*)((uint8_t*)pRawData + sizeof(uint32_t));
+ metadata.data.application.dataSize = blockSize - sizeof(uint32_t);
+ pFlac->onMeta(pFlac->pUserDataMD, &metadata);
+
+ free(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
+ {
+ seektablePos = runningFilePos;
+ seektableSize = blockSize;
+
+ if (pFlac->onMeta) {
+ void* pRawData = malloc(blockSize);
+ if (pRawData == NULL) {
+ return DR_FALSE;
+ }
+
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) {
+ free(pRawData);
+ return DR_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint);
+ metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
+
+ // Endian swap.
+ for (uint32_t iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
+ drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
+ pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample);
+ pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset);
+ pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount);
+ }
+
+ pFlac->onMeta(pFlac->pUserDataMD, &metadata);
+
+ free(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
+ {
+ if (pFlac->onMeta) {
+ void* pRawData = malloc(blockSize);
+ if (pRawData == NULL) {
+ return DR_FALSE;
+ }
+
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) {
+ free(pRawData);
+ return DR_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ const char* pRunningData = (const char*)pRawData;
+ metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
+ metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.vorbis_comment.comments = pRunningData;
+ pFlac->onMeta(pFlac->pUserDataMD, &metadata);
+
+ free(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
+ {
+ if (pFlac->onMeta) {
+ void* pRawData = malloc(blockSize);
+ if (pRawData == NULL) {
+ return DR_FALSE;
+ }
+
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) {
+ free(pRawData);
+ return DR_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ const char* pRunningData = (const char*)pRawData;
+ memcpy(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
+ metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(uint64_t*)pRunningData); pRunningData += 4;
+ metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259;
+ metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
+ metadata.data.cuesheet.pTrackData = (const uint8_t*)pRunningData;
+ pFlac->onMeta(pFlac->pUserDataMD, &metadata);
+
+ free(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
+ {
+ if (pFlac->onMeta) {
+ void* pRawData = malloc(blockSize);
+ if (pRawData == NULL) {
+ return DR_FALSE;
+ }
+
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) {
+ free(pRawData);
+ return DR_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ const char* pRunningData = (const char*)pRawData;
+ metadata.data.picture.type = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.mimeLength = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
+ metadata.data.picture.descriptionLength = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.description = pRunningData;
+ metadata.data.picture.width = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.height = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.colorDepth = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.indexColorCount = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pictureDataSize = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pPictureData = (const uint8_t*)pRunningData;
+ pFlac->onMeta(pFlac->pUserDataMD, &metadata);
+
+ free(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_PADDING:
+ {
+ if (pFlac->onMeta) {
+ metadata.data.padding.unused = 0;
+
+ // Padding doesn't have anything meaningful in it, so just skip over it.
+ if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+
+ pFlac->onMeta(pFlac->pUserDataMD, &metadata);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
+ {
+ // Invalid chunk. Just skip over this one.
+ if (pFlac->onMeta) {
+ if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ }
+ }
+
+ default:
+ {
+ // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we
+ // can at the very least report the chunk to the application and let it look at the raw data.
+ if (pFlac->onMeta) {
+ void* pRawData = malloc(blockSize);
+ if (pRawData == NULL) {
+ return DR_FALSE;
+ }
+
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) {
+ free(pRawData);
+ return DR_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ pFlac->onMeta(pFlac->pUserDataMD, &metadata);
+
+ free(pRawData);
+ }
+ } break;
+ }
+
+ // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above.
+ if (pFlac->onMeta == NULL) {
+ if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ }
+
+ runningFilePos += blockSize;
+ if (isLastBlock) {
+ break;
+ }
+ }
+
+ pFlac->seektablePos = seektablePos;
+ pFlac->seektableSize = seektableSize;
+ pFlac->firstFramePos = runningFilePos;
+
+ return DR_TRUE;
+}
+
+drBool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD)
+{
+ (void)onSeek;
+
+ // Pre: The bit stream should be sitting just past the 4-byte id header.
+
+ pInit->container = drflac_container_native;
+
+ // The first metadata block should be the STREAMINFO block.
+ uint8_t isLastBlock;
+ uint8_t blockType;
+ uint32_t blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DR_FALSE;
+ }
+
+ if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
+ return DR_FALSE; // Invalid block type. First block must be the STREAMINFO block.
+ }
+
+
+ drflac_streaminfo streaminfo;
+ if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
+ return DR_FALSE;
+ }
+
+ pInit->sampleRate = streaminfo.sampleRate;
+ pInit->channels = streaminfo.channels;
+ pInit->bitsPerSample = streaminfo.bitsPerSample;
+ pInit->totalSampleCount = streaminfo.totalSampleCount;
+ pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation).
+
+ if (onMeta) {
+ drflac_metadata metadata;
+ metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+ metadata.data.streaminfo = streaminfo;
+ onMeta(pUserDataMD, &metadata);
+ }
+
+ pInit->hasMetadataBlocks = !isLastBlock;
+ return DR_TRUE;
+}
+
+#ifndef DR_FLAC_NO_OGG
+static DRFLAC_INLINE drBool32 drflac_ogg__is_capture_pattern(uint8_t pattern[4])
+{
+ return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
+}
+
+static DRFLAC_INLINE uint32_t drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader)
+{
+ return 27 + pHeader->segmentCount;
+}
+
+static DRFLAC_INLINE uint32_t drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
+{
+ uint32_t pageBodySize = 0;
+ for (int i = 0; i < pHeader->segmentCount; ++i) {
+ pageBodySize += pHeader->segmentTable[i];
+ }
+
+ return pageBodySize;
+}
+
+drBool32 drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, uint32_t* pHeaderSize)
+{
+ if (onRead(pUserData, &pHeader->structureVersion, 1) != 1 || pHeader->structureVersion != 0) {
+ return DR_FALSE; // Unknown structure version. Possibly corrupt stream.
+ }
+ if (onRead(pUserData, &pHeader->headerType, 1) != 1) {
+ return DR_FALSE;
+ }
+ if (onRead(pUserData, &pHeader->granulePosition, 8) != 8) {
+ return DR_FALSE;
+ }
+ if (onRead(pUserData, &pHeader->serialNumber, 4) != 4) {
+ return DR_FALSE;
+ }
+ if (onRead(pUserData, &pHeader->sequenceNumber, 4) != 4) {
+ return DR_FALSE;
+ }
+ if (onRead(pUserData, &pHeader->checksum, 4) != 4) {
+ return DR_FALSE;
+ }
+ if (onRead(pUserData, &pHeader->segmentCount, 1) != 1 || pHeader->segmentCount == 0) {
+ return DR_FALSE; // Should not have a segment count of 0.
+ }
+ if (onRead(pUserData, &pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
+ return DR_FALSE;
+ }
+
+ if (pHeaderSize) *pHeaderSize = (27 + pHeader->segmentCount);
+ return DR_TRUE;
+}
+
+drBool32 drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, uint32_t* pHeaderSize)
+{
+ uint8_t id[4];
+ if (onRead(pUserData, id, 4) != 4) {
+ return DR_FALSE;
+ }
+
+ if (id[0] != 'O' || id[1] != 'g' || id[2] != 'g' || id[3] != 'S') {
+ return DR_FALSE;
+ }
+
+ return drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pHeaderSize);
+}
+
+
+// The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works
+// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is architecured
+// in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type
+// dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from
+// the physical Ogg bitstream are converted and delivered in native FLAC format.
+typedef struct
+{
+ drflac_read_proc onRead; // The original onRead callback from drflac_open() and family.
+ drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family.
+ void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family.
+ uint64_t currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking.
+ uint64_t firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page.
+ uint32_t serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization.
+ drflac_ogg_page_header bosPageHeader; // Used for seeking.
+ drflac_ogg_page_header currentPageHeader;
+ uint32_t bytesRemainingInPage;
+} drflac_oggbs; // oggbs = Ogg Bitstream
+
+static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
+{
+ size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
+ oggbs->currentBytePos += bytesActuallyRead;
+
+ return bytesActuallyRead;
+}
+
+static drBool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, uint64_t offset, drflac_seek_origin origin)
+{
+ if (origin == drflac_seek_origin_start)
+ {
+ if (offset <= 0x7FFFFFFF) {
+ if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
+ return DR_FALSE;
+ }
+ oggbs->currentBytePos = offset;
+
+ return DR_TRUE;
+ } else {
+ if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+ return DR_FALSE;
+ }
+ oggbs->currentBytePos = offset;
+
+ return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
+ }
+ }
+ else
+ {
+ while (offset > 0x7FFFFFFF) {
+ if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ oggbs->currentBytePos += 0x7FFFFFFF;
+ offset -= 0x7FFFFFFF;
+ }
+
+ if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above.
+ return DR_FALSE;
+ }
+ oggbs->currentBytePos += offset;
+
+ return DR_TRUE;
+ }
+}
+
+static drBool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs)
+{
+ drflac_ogg_page_header header;
+ for (;;)
+ {
+ uint32_t headerSize;
+ if (!drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &headerSize)) {
+ return DR_FALSE;
+ }
+ oggbs->currentBytePos += headerSize;
+
+
+ uint32_t pageBodySize = drflac_ogg__get_page_body_size(&header);
+
+ if (header.serialNumber == oggbs->serialNumber) {
+ oggbs->currentPageHeader = header;
+ oggbs->bytesRemainingInPage = pageBodySize;
+ return DR_TRUE;
+ }
+
+ // If we get here it means the page is not a FLAC page - skip it.
+ if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { // <-- Safe cast - maximum size of a page is way below that of an int.
+ return DR_FALSE;
+ }
+ }
+}
+
+// Function below is unused at the moment, but I might be re-adding it later.
+#if 0
+static uint8_t drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, uint8_t* pBytesRemainingInSeg)
+{
+ uint32_t bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
+ uint8_t iSeg = 0;
+ uint32_t iByte = 0;
+ while (iByte < bytesConsumedInPage)
+ {
+ uint8_t segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
+ if (iByte + segmentSize > bytesConsumedInPage) {
+ break;
+ } else {
+ iSeg += 1;
+ iByte += segmentSize;
+ }
+ }
+
+ *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (uint8_t)(bytesConsumedInPage - iByte);
+ return iSeg;
+}
+
+static drBool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
+{
+ // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page.
+ for (;;) // <-- Loop over pages.
+ {
+ drBool32 atEndOfPage = DR_FALSE;
+
+ uint8_t bytesRemainingInSeg;
+ uint8_t iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
+
+ uint32_t bytesToEndOfPacketOrPage = bytesRemainingInSeg;
+ for (uint8_t iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
+ uint8_t segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
+ if (segmentSize < 255) {
+ if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
+ atEndOfPage = DR_TRUE;
+ }
+
+ break;
+ }
+
+ bytesToEndOfPacketOrPage += segmentSize;
+ }
+
+ // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll
+ // want to load the next page and keep searching for the end of the frame.
+ drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
+ oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
+
+ if (atEndOfPage)
+ {
+ // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may
+ // straddle pages.
+ if (!drflac_oggbs__goto_next_page(oggbs)) {
+ return DR_FALSE;
+ }
+
+ // If it's a fresh packet it most likely means we're at the next packet.
+ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
+ return DR_TRUE;
+ }
+ }
+ else
+ {
+ // We're at the next frame.
+ return DR_TRUE;
+ }
+ }
+}
+
+static drBool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
+{
+ // The bitstream should be sitting on the first byte just after the header of the frame.
+
+ // What we're actually doing here is seeking to the start of the next packet.
+ return drflac_oggbs__seek_to_next_packet(oggbs);
+}
+#endif
+
+static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
+ assert(oggbs != NULL);
+
+ uint8_t* pRunningBufferOut = (uint8_t*)bufferOut;
+
+ // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one.
+ size_t bytesRead = 0;
+ while (bytesRead < bytesToRead)
+ {
+ size_t bytesRemainingToRead = bytesToRead - bytesRead;
+
+ if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
+ bytesRead += oggbs->onRead(oggbs->pUserData, pRunningBufferOut, bytesRemainingToRead);
+ oggbs->bytesRemainingInPage -= (uint32_t)bytesRemainingToRead;
+ break;
+ }
+
+ // If we get here it means some of the requested data is contained in the next pages.
+ if (oggbs->bytesRemainingInPage > 0) {
+ size_t bytesJustRead = oggbs->onRead(oggbs->pUserData, pRunningBufferOut, oggbs->bytesRemainingInPage);
+ bytesRead += bytesJustRead;
+ pRunningBufferOut += bytesJustRead;
+
+ if (bytesJustRead != oggbs->bytesRemainingInPage) {
+ break; // Ran out of data.
+ }
+ }
+
+ assert(bytesRemainingToRead > 0);
+ if (!drflac_oggbs__goto_next_page(oggbs)) {
+ break; // Failed to go to the next chunk. Might have simply hit the end of the stream.
+ }
+ }
+
+ oggbs->currentBytePos += bytesRead;
+ return bytesRead;
+}
+
+static drBool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
+ assert(oggbs != NULL);
+ assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
+
+ // Seeking is always forward which makes things a lot simpler.
+ if (origin == drflac_seek_origin_start) {
+ int startBytePos = (int)oggbs->firstBytePos + (79-42); // 79 = size of bos page; 42 = size of FLAC header data. Seek up to the first byte of the native FLAC data.
+ if (!drflac_oggbs__seek_physical(oggbs, startBytePos, drflac_seek_origin_start)) {
+ return DR_FALSE;
+ }
+
+ oggbs->currentPageHeader = oggbs->bosPageHeader;
+ oggbs->bytesRemainingInPage = 42; // 42 = size of the native FLAC header data. That's our start point for seeking.
+
+ return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
+ }
+
+
+ assert(origin == drflac_seek_origin_current);
+
+ int bytesSeeked = 0;
+ while (bytesSeeked < offset)
+ {
+ int bytesRemainingToSeek = offset - bytesSeeked;
+ assert(bytesRemainingToSeek >= 0);
+
+ if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
+ if (!drflac_oggbs__seek_physical(oggbs, bytesRemainingToSeek, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+
+ bytesSeeked += bytesRemainingToSeek;
+ oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
+ break;
+ }
+
+ // If we get here it means some of the requested data is contained in the next pages.
+ if (oggbs->bytesRemainingInPage > 0) {
+ if (!drflac_oggbs__seek_physical(oggbs, oggbs->bytesRemainingInPage, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+
+ bytesSeeked += (int)oggbs->bytesRemainingInPage;
+ }
+
+ assert(bytesRemainingToSeek > 0);
+ if (!drflac_oggbs__goto_next_page(oggbs)) {
+ break; // Failed to go to the next chunk. Might have simply hit the end of the stream.
+ }
+ }
+
+ return DR_TRUE;
+}
+
+drBool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels);
+
+ uint64_t originalBytePos = oggbs->currentBytePos; // For recovery.
+
+ // First seek to the first frame.
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) {
+ return DR_FALSE;
+ }
+ oggbs->bytesRemainingInPage = 0;
+
+ uint64_t runningGranulePosition = 0;
+ uint64_t runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier.
+ for (;;)
+ {
+ if (!drflac_oggbs__goto_next_page(oggbs)) {
+ drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
+ return DR_FALSE; // Never did find that sample...
+ }
+
+ runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader);
+ if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sample) {
+ break; // The sample is somewhere in the previous page.
+ }
+
+
+ // At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we
+ // disregard any pages that do not begin a fresh packet.
+ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page?
+ if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
+ uint8_t firstBytesInPage[2];
+ if (drflac_oggbs__read_physical(oggbs, firstBytesInPage, 2) != 2) {
+ drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
+ return DR_FALSE;
+ }
+ if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code?
+ runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels;
+ }
+
+ if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->bytesRemainingInPage-2, drflac_seek_origin_current)) {
+ drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
+ return DR_FALSE;
+ }
+
+ continue;
+ }
+ }
+
+ if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->bytesRemainingInPage, drflac_seek_origin_current)) {
+ drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
+ return DR_FALSE;
+ }
+ }
+
+
+ // We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the
+ // start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of
+ // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until
+ // we find the one containing the target sample.
+ if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
+ return DR_FALSE;
+ }
+ if (!drflac_oggbs__goto_next_page(oggbs)) {
+ return DR_FALSE;
+ }
+
+
+ // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep
+ // looping over these frames until we find the one containing the sample we're after.
+ uint64_t firstSampleInFrame = runningGranulePosition;
+ for (;;)
+ {
+ // NOTE for later: When using Ogg's page/segment based seeking later on we can't use this function (or any drflac__*
+ // reading functions) because otherwise it will pull extra data for use in it's own internal caches which will then
+ // break the positioning of the read pointer for the Ogg bitstream.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DR_FALSE;
+ }
+
+ int channels = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ uint64_t lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channels);
+ lastSampleInFrame -= 1; // <-- Zero based.
+
+ if (sample >= firstSampleInFrame && sample <= lastSampleInFrame) {
+ break; // The sample is in this frame.
+ }
+
+
+ // If we get here it means the sample is not in this frame so we need to move to the next one. Now the cool thing
+ // with Ogg is that we can efficiently seek past the frame by looking at the lacing values of each segment in
+ // the page.
+ firstSampleInFrame = lastSampleInFrame+1;
+
+#if 1
+ // Slow way. This uses the native FLAC decoder to seek past the frame. This is slow because it needs to do a partial
+ // decode of the frame. Although this is how the native version works, we can use Ogg's framing system to make it
+ // more efficient. Leaving this here for reference and to use as a basis for debugging purposes.
+ if (!drflac__seek_to_next_frame(pFlac)) {
+ return DR_FALSE;
+ }
+#else
+ // TODO: This is not yet complete. See note at the top of this loop body.
+
+ // Fast(er) way. This uses Ogg's framing system to seek past the frame. This should be much more efficient than the
+ // native FLAC seeking.
+ if (!drflac_oggbs__seek_to_next_frame(oggbs)) {
+ return DR_FALSE;
+ }
+#endif
+ }
+
+ assert(firstSampleInFrame <= sample);
+
+ if (!drflac__decode_frame(pFlac)) {
+ return DR_FALSE;
+ }
+
+ size_t samplesToDecode = (size_t)(sample - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535.
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode;
+}
+
+
+drBool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD)
+{
+ // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern.
+
+ pInit->container = drflac_container_ogg;
+ pInit->oggFirstBytePos = 0;
+
+ // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the
+ // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if
+ // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed.
+ drflac_ogg_page_header header;
+
+ uint32_t headerSize;
+ if (!drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &headerSize)) {
+ return DR_FALSE;
+ }
+ pInit->runningFilePos = headerSize;
+
+ for (;;)
+ {
+ // Break if we're past the beginning of stream page.
+ if ((header.headerType & 0x02) == 0) {
+ return DR_FALSE;
+ }
+
+
+ // Check if it's a FLAC header.
+ int pageBodySize = drflac_ogg__get_page_body_size(&header);
+ if (pageBodySize == 51) // 51 = the lacing value of the FLAC header packet.
+ {
+ // It could be a FLAC page...
+ uint32_t bytesRemainingInPage = pageBodySize;
+
+ uint8_t packetType;
+ if (onRead(pUserData, &packetType, 1) != 1) {
+ return DR_FALSE;
+ }
+
+ bytesRemainingInPage -= 1;
+ if (packetType == 0x7F)
+ {
+ // Increasingly more likely to be a FLAC page...
+ uint8_t sig[4];
+ if (onRead(pUserData, sig, 4) != 4) {
+ return DR_FALSE;
+ }
+
+ bytesRemainingInPage -= 4;
+ if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C')
+ {
+ // Almost certainly a FLAC page...
+ uint8_t mappingVersion[2];
+ if (onRead(pUserData, mappingVersion, 2) != 2) {
+ return DR_FALSE;
+ }
+
+ if (mappingVersion[0] != 1) {
+ return DR_FALSE; // Only supporting version 1.x of the Ogg mapping.
+ }
+
+ // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to
+ // be handling it in a generic way based on the serial number and packet types.
+ if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+
+ // Expecting the native FLAC signature "fLaC".
+ if (onRead(pUserData, sig, 4) != 4) {
+ return DR_FALSE;
+ }
+
+ if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C')
+ {
+ // The remaining data in the page should be the STREAMINFO block.
+ uint8_t isLastBlock;
+ uint8_t blockType;
+ uint32_t blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DR_FALSE;
+ }
+
+ if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
+ return DR_FALSE; // Invalid block type. First block must be the STREAMINFO block.
+ }
+
+ drflac_streaminfo streaminfo;
+ if (drflac__read_streaminfo(onRead, pUserData, &streaminfo))
+ {
+ // Success!
+ pInit->sampleRate = streaminfo.sampleRate;
+ pInit->channels = streaminfo.channels;
+ pInit->bitsPerSample = streaminfo.bitsPerSample;
+ pInit->totalSampleCount = streaminfo.totalSampleCount;
+ pInit->maxBlockSize = streaminfo.maxBlockSize;
+
+ if (onMeta) {
+ drflac_metadata metadata;
+ metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+ metadata.data.streaminfo = streaminfo;
+ onMeta(pUserDataMD, &metadata);
+ }
+
+ pInit->runningFilePos += pageBodySize;
+ pInit->oggFirstBytePos = pInit->runningFilePos - 79; // Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page.
+ pInit->oggSerial = header.serialNumber;
+ pInit->oggBosHeader = header;
+ break;
+ }
+ else
+ {
+ // Failed to read STREAMINFO block. Aww, so close...
+ return DR_FALSE;
+ }
+ }
+ else
+ {
+ // Invalid file.
+ return DR_FALSE;
+ }
+ }
+ else
+ {
+ // Not a FLAC header. Skip it.
+ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ }
+ }
+ else
+ {
+ // Not a FLAC header. Seek past the entire page and move on to the next.
+ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ }
+ }
+ else
+ {
+ if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
+ return DR_FALSE;
+ }
+ }
+
+ pInit->runningFilePos += pageBodySize;
+
+
+ // Read the header of the next page.
+ if (!drflac_ogg__read_page_header(onRead, pUserData, &header, &headerSize)) {
+ return DR_FALSE;
+ }
+ pInit->runningFilePos += headerSize;
+ }
+
+
+ // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next
+ // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialiation phase for Ogg is to create the
+ // Ogg bistream object.
+ pInit->hasMetadataBlocks = DR_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block.
+ return DR_TRUE;
+}
+#endif
+
+drBool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD)
+{
+ if (pInit == NULL || onRead == NULL || onSeek == NULL) {
+ return DR_FALSE;
+ }
+
+ pInit->onRead = onRead;
+ pInit->onSeek = onSeek;
+ pInit->onMeta = onMeta;
+ pInit->pUserData = pUserData;
+ pInit->pUserDataMD = pUserDataMD;
+
+ uint8_t id[4];
+ if (onRead(pUserData, id, 4) != 4) {
+ return DR_FALSE;
+ }
+
+ if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
+ return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD);
+ }
+
+#ifndef DR_FLAC_NO_OGG
+ if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
+ return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD);
+ }
+#endif
+
+ // Unsupported container.
+ return DR_FALSE;
+}
+
+void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit)
+{
+ assert(pFlac != NULL);
+ assert(pInit != NULL);
+
+ memset(pFlac, 0, sizeof(*pFlac));
+ pFlac->bs.onRead = pInit->onRead;
+ pFlac->bs.onSeek = pInit->onSeek;
+ pFlac->bs.pUserData = pInit->pUserData;
+ pFlac->bs.nextL2Line = sizeof(pFlac->bs.cacheL2) / sizeof(pFlac->bs.cacheL2[0]); // <-- Initialize to this to force a client-side data retrieval right from the start.
+ pFlac->bs.consumedBits = sizeof(pFlac->bs.cache)*8;
+
+ pFlac->onMeta = pInit->onMeta;
+ pFlac->pUserDataMD = pInit->pUserDataMD;
+ pFlac->maxBlockSize = pInit->maxBlockSize;
+ pFlac->sampleRate = pInit->sampleRate;
+ pFlac->channels = (uint8_t)pInit->channels;
+ pFlac->bitsPerSample = (uint8_t)pInit->bitsPerSample;
+ pFlac->totalSampleCount = pInit->totalSampleCount;
+ pFlac->container = pInit->container;
+}
+
+drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD)
+{
+ drflac_init_info init;
+ if (!drflac__init_private(&init, onRead, onSeek, onMeta, pUserData, pUserDataMD)) {
+ return NULL;
+ }
+
+ size_t allocationSize = sizeof(drflac);
+ allocationSize += init.maxBlockSize * init.channels * sizeof(int32_t);
+ //allocationSize += init.seektableSize;
+
+
+#ifndef DR_FLAC_NO_OGG
+ // There's additional data required for Ogg streams.
+ if (init.container == drflac_container_ogg) {
+ allocationSize += sizeof(drflac_oggbs);
+ }
+#endif
+
+ drflac* pFlac = (drflac*)malloc(allocationSize);
+ drflac__init_from_info(pFlac, &init);
+ pFlac->pDecodedSamples = (int32_t*)pFlac->pExtraData;
+
+#ifndef DR_FLAC_NO_OGG
+ if (init.container == drflac_container_ogg) {
+ drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + init.maxBlockSize*init.channels);
+ oggbs->onRead = onRead;
+ oggbs->onSeek = onSeek;
+ oggbs->pUserData = pUserData;
+ oggbs->currentBytePos = init.oggFirstBytePos;
+ oggbs->firstBytePos = init.oggFirstBytePos;
+ oggbs->serialNumber = init.oggSerial;
+ oggbs->bosPageHeader = init.oggBosHeader;
+ oggbs->bytesRemainingInPage = 0;
+
+ // The Ogg bistream needs to be layered on top of the original bitstream.
+ pFlac->bs.onRead = drflac__on_read_ogg;
+ pFlac->bs.onSeek = drflac__on_seek_ogg;
+ pFlac->bs.pUserData = (void*)oggbs;
+ }
+#endif
+
+ // Decode metadata before returning.
+ if (init.hasMetadataBlocks) {
+ if (!drflac__read_and_decode_metadata(pFlac)) {
+ free(pFlac);
+ return NULL;
+ }
+ }
+
+ return pFlac;
+}
+
+
+
+#ifndef DR_FLAC_NO_STDIO
+typedef void* drflac_file;
+
+#if defined(DR_FLAC_NO_WIN32_IO) || !defined(_WIN32)
+#include <stdio.h>
+
+static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static drBool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
+
+ return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+static drflac_file drflac__open_file_handle(const char* filename)
+{
+ FILE* pFile;
+#ifdef _MSC_VER
+ if (fopen_s(&pFile, filename, "rb") != 0) {
+ return NULL;
+ }
+#else
+ pFile = fopen(filename, "rb");
+ if (pFile == NULL) {
+ return NULL;
+ }
+#endif
+
+ return (drflac_file)pFile;
+}
+
+static void drflac__close_file_handle(drflac_file file)
+{
+ fclose((FILE*)file);
+}
+#else
+#include <windows.h>
+
+static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion.
+
+ DWORD bytesRead;
+ ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL);
+
+ return (size_t)bytesRead;
+}
+
+static drBool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
+
+ return SetFilePointer((HANDLE)pUserData, offset, NULL, (origin == drflac_seek_origin_current) ? FILE_CURRENT : FILE_BEGIN) != INVALID_SET_FILE_POINTER;
+}
+
+static drflac_file drflac__open_file_handle(const char* filename)
+{
+ HANDLE hFile = CreateFileA(filename, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return NULL;
+ }
+
+ return (drflac_file)hFile;
+}
+
+static void drflac__close_file_handle(drflac_file file)
+{
+ CloseHandle((HANDLE)file);
+}
+#endif
+
+
+drflac* drflac_open_file(const char* filename)
+{
+ drflac_file file = drflac__open_file_handle(filename);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ drflac* pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)file);
+ if (pFlac == NULL) {
+ drflac__close_file_handle(file);
+ return NULL;
+ }
+
+ return pFlac;
+}
+
+drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData)
+{
+ drflac_file file = drflac__open_file_handle(filename);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, (void*)file, pUserData);
+ if (pFlac == NULL) {
+ drflac__close_file_handle(file);
+ return pFlac;
+ }
+
+ return pFlac;
+}
+#endif //DR_FLAC_NO_STDIO
+
+static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+ assert(memoryStream != NULL);
+ assert(memoryStream->dataSize >= memoryStream->currentReadPos);
+
+ size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = bytesRemaining;
+ }
+
+ if (bytesToRead > 0) {
+ memcpy(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
+ memoryStream->currentReadPos += bytesToRead;
+ }
+
+ return bytesToRead;
+}
+
+static drBool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+ assert(memoryStream != NULL);
+ assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start));
+
+ if (origin == drflac_seek_origin_current) {
+ if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos += offset;
+ } else {
+ memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
+ }
+ } else {
+ if ((uint32_t)offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos = offset;
+ } else {
+ memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward.
+ }
+ }
+
+ return DR_TRUE;
+}
+
+drflac* drflac_open_memory(const void* data, size_t dataSize)
+{
+ drflac__memory_stream memoryStream;
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ pFlac->memoryStream = memoryStream;
+
+ // This is an awful hack...
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels);
+ oggbs->pUserData = &pFlac->memoryStream;
+ }
+ else
+#endif
+ {
+ pFlac->bs.pUserData = &pFlac->memoryStream;
+ }
+
+ return pFlac;
+}
+
+drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData)
+{
+ drflac__memory_stream memoryStream;
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, &memoryStream, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ pFlac->memoryStream = memoryStream;
+
+ // This is an awful hack...
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels);
+ oggbs->pUserData = &pFlac->memoryStream;
+ }
+ else
+#endif
+ {
+ pFlac->bs.pUserData = &pFlac->memoryStream;
+ }
+
+ return pFlac;
+}
+
+
+
+drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, NULL, pUserData, pUserData);
+}
+
+drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, onMeta, pUserData, pUserData);
+}
+
+void drflac_close(drflac* pFlac)
+{
+ if (pFlac == NULL) {
+ return;
+ }
+
+#ifndef DR_FLAC_NO_STDIO
+ // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file()
+ // was used by looking at the callbacks.
+ if (pFlac->bs.onRead == drflac__on_read_stdio) {
+ drflac__close_file_handle((drflac_file)pFlac->bs.pUserData);
+ }
+
+#ifndef DR_FLAC_NO_OGG
+ // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained.
+ if (pFlac->container == drflac_container_ogg) {
+ assert(pFlac->bs.onRead == drflac__on_read_ogg);
+ drflac_oggbs* oggbs = (drflac_oggbs*)((int32_t*)pFlac->pExtraData + pFlac->maxBlockSize*pFlac->channels);
+ if (oggbs->onRead == drflac__on_read_stdio) {
+ drflac__close_file_handle((drflac_file)oggbs->pUserData);
+ }
+ }
+#endif
+#endif
+
+ free(pFlac);
+}
+
+uint64_t drflac__read_s32__misaligned(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferOut)
+{
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+
+ // We should never be calling this when the number of samples to read is >= the sample count.
+ assert(samplesToRead < channelCount);
+ assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining);
+
+
+ uint64_t samplesRead = 0;
+ while (samplesToRead > 0)
+ {
+ uint64_t totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
+ uint64_t samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
+ unsigned int channelIndex = samplesReadFromFrameSoFar % channelCount;
+
+ uint64_t nextSampleInFrame = samplesReadFromFrameSoFar / channelCount;
+
+ int decodedSample = 0;
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ if (channelIndex == 0) {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame];
+ } else {
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
+ int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame];
+ decodedSample = left - side;
+ }
+
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ if (channelIndex == 0) {
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
+ int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame];
+ decodedSample = side + right;
+ } else {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame];
+ }
+
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ int mid;
+ int side;
+ if (channelIndex == 0) {
+ mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
+ side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame];
+
+ mid = (((unsigned int)mid) << 1) | (side & 0x01);
+ decodedSample = (mid + side) >> 1;
+ } else {
+ mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame];
+ side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame];
+
+ mid = (((unsigned int)mid) << 1) | (side & 0x01);
+ decodedSample = (mid - side) >> 1;
+ }
+
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame];
+ } break;
+ }
+
+
+ decodedSample <<= ((32 - pFlac->bitsPerSample) + pFlac->currentFrame.subframes[channelIndex].wastedBitsPerSample);
+
+ if (bufferOut) {
+ *bufferOut++ = decodedSample;
+ }
+
+ samplesRead += 1;
+ pFlac->currentFrame.samplesRemaining -= 1;
+ samplesToRead -= 1;
+ }
+
+ return samplesRead;
+}
+
+uint64_t drflac__seek_forward_by_samples(drflac* pFlac, uint64_t samplesToRead)
+{
+ uint64_t samplesRead = 0;
+ while (samplesToRead > 0)
+ {
+ if (pFlac->currentFrame.samplesRemaining == 0)
+ {
+ if (!drflac__read_and_decode_next_frame(pFlac)) {
+ break; // Couldn't read the next frame, so just break from the loop and return.
+ }
+ }
+ else
+ {
+ samplesRead += 1;
+ pFlac->currentFrame.samplesRemaining -= 1;
+ samplesToRead -= 1;
+ }
+ }
+
+ return samplesRead;
+}
+
+uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferOut)
+{
+ // Note that <bufferOut> is allowed to be null, in which case this will be treated as something like a seek.
+ if (pFlac == NULL || samplesToRead == 0) {
+ return 0;
+ }
+
+ if (bufferOut == NULL) {
+ return drflac__seek_forward_by_samples(pFlac, samplesToRead);
+ }
+
+
+ uint64_t samplesRead = 0;
+ while (samplesToRead > 0)
+ {
+ // If we've run out of samples in this frame, go to the next.
+ if (pFlac->currentFrame.samplesRemaining == 0)
+ {
+ if (!drflac__read_and_decode_next_frame(pFlac)) {
+ break; // Couldn't read the next frame, so just break from the loop and return.
+ }
+ }
+ else
+ {
+ // Here is where we grab the samples and interleave them.
+
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ uint64_t totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
+ uint64_t samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
+
+ int misalignedSampleCount = samplesReadFromFrameSoFar % channelCount;
+ if (misalignedSampleCount > 0) {
+ uint64_t misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut);
+ samplesRead += misalignedSamplesRead;
+ samplesReadFromFrameSoFar += misalignedSamplesRead;
+ bufferOut += misalignedSamplesRead;
+ samplesToRead -= misalignedSamplesRead;
+ }
+
+
+ uint64_t alignedSampleCountPerChannel = samplesToRead / channelCount;
+ if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) {
+ alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount;
+ }
+
+ uint64_t firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount;
+ unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int left = pDecodedSamples0[i];
+ int side = pDecodedSamples1[i];
+ int right = left - side;
+
+ bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int side = pDecodedSamples0[i];
+ int right = pDecodedSamples1[i];
+ int left = right + side;
+
+ bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int side = pDecodedSamples1[i];
+ int mid = (((uint32_t)pDecodedSamples0[i]) << 1) | (side & 0x01);
+
+ bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo
+ {
+ // Stereo optimized inner loop unroll.
+ const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) {
+ bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ }
+ }
+ else
+ {
+ // Generic interleaving.
+ for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) {
+ for (unsigned int j = 0; j < channelCount; ++j) {
+ bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample);
+ }
+ }
+ }
+ } break;
+ }
+
+ uint64_t alignedSamplesRead = alignedSampleCountPerChannel * channelCount;
+ samplesRead += alignedSamplesRead;
+ samplesReadFromFrameSoFar += alignedSamplesRead;
+ bufferOut += alignedSamplesRead;
+ samplesToRead -= alignedSamplesRead;
+ pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead;
+
+
+
+ // At this point we may still have some excess samples left to read.
+ if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0)
+ {
+ uint64_t excessSamplesRead = 0;
+ if (samplesToRead < pFlac->currentFrame.samplesRemaining) {
+ excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut);
+ } else {
+ excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut);
+ }
+
+ samplesRead += excessSamplesRead;
+ samplesReadFromFrameSoFar += excessSamplesRead;
+ bufferOut += excessSamplesRead;
+ samplesToRead -= excessSamplesRead;
+ }
+ }
+ }
+
+ return samplesRead;
+}
+
+drBool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex)
+{
+ if (pFlac == NULL) {
+ return DR_FALSE;
+ }
+
+ if (sampleIndex == 0) {
+ return drflac__seek_to_first_frame(pFlac);
+ }
+
+ // Clamp the sample to the end.
+ if (sampleIndex >= pFlac->totalSampleCount) {
+ sampleIndex = pFlac->totalSampleCount - 1;
+ }
+
+
+ // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so
+ // we'll instead use Ogg's natural seeking facility.
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ return drflac_ogg__seek_to_sample(pFlac, sampleIndex);
+ }
+ else
+#endif
+ {
+ // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower.
+ if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) {
+ return drflac__seek_to_sample__brute_force(pFlac, sampleIndex);
+ }
+ }
+
+
+ return DR_TRUE;
+}
+
+
+
+//// High Level APIs ////
+
+int32_t* drflac__full_decode_and_close(drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, uint64_t* totalSampleCountOut)
+{
+ assert(pFlac != NULL);
+
+ int32_t* pSampleData = NULL;
+ uint64_t totalSampleCount = pFlac->totalSampleCount;
+
+ if (totalSampleCount == 0)
+ {
+ int32_t buffer[4096];
+
+ size_t sampleDataBufferSize = sizeof(buffer);
+ pSampleData = (int32_t*)malloc(sampleDataBufferSize);
+ if (pSampleData == NULL) {
+ goto on_error;
+ }
+
+ uint64_t samplesRead;
+ while ((samplesRead = (uint64_t)drflac_read_s32(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0)
+ {
+ if (((totalSampleCount + samplesRead) * sizeof(int32_t)) > sampleDataBufferSize) {
+ sampleDataBufferSize *= 2;
+ int32_t* pNewSampleData = (int32_t*)realloc(pSampleData, sampleDataBufferSize);
+ if (pNewSampleData == NULL) {
+ free(pSampleData);
+ goto on_error;
+ }
+
+ pSampleData = pNewSampleData;
+ }
+
+ memcpy(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(int32_t)));
+ totalSampleCount += samplesRead;
+ }
+
+ // At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to
+ // protect those ears from random noise!
+ memset(pSampleData + totalSampleCount, 0, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(int32_t)));
+ }
+ else
+ {
+ uint64_t dataSize = totalSampleCount * sizeof(int32_t);
+ if (dataSize > SIZE_MAX) {
+ goto on_error; // The decoded data is too big.
+ }
+
+ pSampleData = (int32_t*)malloc((size_t)dataSize); // <-- Safe cast as per the check above.
+ if (pSampleData == NULL) {
+ goto on_error;
+ }
+
+ uint64_t samplesDecoded = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSampleData);
+ if (samplesDecoded != pFlac->totalSampleCount) {
+ free(pSampleData);
+ goto on_error; // Something went wrong when decoding the FLAC stream.
+ }
+ }
+
+
+ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate;
+ if (channelsOut) *channelsOut = pFlac->channels;
+ if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount;
+
+ drflac_close(pFlac);
+ return pSampleData;
+
+on_error:
+ drflac_close(pFlac);
+ return NULL;
+}
+
+int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount)
+{
+ // Safety.
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+#ifndef DR_FLAC_NO_STDIO
+int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_file(filename);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount);
+}
+#endif
+
+int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+void drflac_free(void* pSampleDataReturnedByOpenAndDecode)
+{
+ free(pSampleDataReturnedByOpenAndDecode);
+}
+
+
+
+
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, uint32_t commentCount, const char* pComments)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = commentCount;
+ pIter->pRunningData = pComments;
+}
+
+const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, uint32_t* pCommentLengthOut)
+{
+ // Safety.
+ if (pCommentLengthOut) *pCommentLengthOut = 0;
+
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return NULL;
+ }
+
+ uint32_t length = drflac__le2host_32(*(uint32_t*)pIter->pRunningData);
+ pIter->pRunningData += 4;
+
+ const char* pComment = pIter->pRunningData;
+ pIter->pRunningData += length;
+ pIter->countRemaining -= 1;
+
+ if (pCommentLengthOut) *pCommentLengthOut = length;
+ return pComment;
+}
+#endif //DR_FLAC_IMPLEMENTATION
+
+
+// REVISION HISTORY
+//
+// v0.4 - 2016-09-29
+// - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type.
+// - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32()
+// - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to
+// keep it consistent with dr_audio.
+//
+// v0.3f - 2016-09-21
+// - Fix a warning with GCC.
+//
+// v0.3e - 2016-09-18
+// - Fixed a bug where GCC 4.3+ was not getting properly identified.
+// - Fixed a few typos.
+// - Changed date formats to ISO 8601 (YYYY-MM-DD).
+//
+// v0.3d - 2016-06-11
+// - Minor clean up.
+//
+// v0.3c - 2016-05-28
+// - Fixed compilation error.
+//
+// v0.3b - 2016-05-16
+// - Fixed Linux/GCC build.
+// - Updated documentation.
+//
+// v0.3a - 2016-05-15
+// - Minor fixes to documentation.
+//
+// v0.3 - 2016-05-11
+// - Optimizations. Now at about parity with the reference implementation on 32-bit builds.
+// - Lots of clean up.
+//
+// v0.2b - 2016-05-10
+// - Bug fixes.
+//
+// v0.2a - 2016-05-10
+// - Made drflac_open_and_decode() more robust.
+// - Removed an unused debugging variable
+//
+// v0.2 - 2016-05-09
+// - Added support for Ogg encapsulation.
+// - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek
+// should be relative to the start or the current position. Also changes the seeking rules such that
+// seeking offsets will never be negative.
+// - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count.
+//
+// v0.1b - 2016-05-07
+// - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize.
+// - Removed a stale comment.
+//
+// v0.1a - 2016-05-05
+// - Minor formatting changes.
+// - Fixed a warning on the GCC build.
+//
+// v0.1 - 2016-05-03
+// - Initial versioned release.
+
+
+// TODO
+// - Add support for initializing the decoder without a header STREAMINFO block.
+// - Test CUESHEET metadata blocks.
+
+
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
diff --git a/src/external/jar_mod.h b/src/external/jar_mod.h
index c39db65a..bee9f6ee 100644
--- a/src/external/jar_mod.h
+++ b/src/external/jar_mod.h
@@ -59,7 +59,7 @@
// - 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)
+// mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename)
//
// - "Load" a MOD from file, context must already be initialized.
// Return size of file in bytes.
@@ -247,7 +247,7 @@ 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_load_file(jar_mod_context_t * modctx, const 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);
@@ -1516,7 +1516,7 @@ void jar_mod_unload( jar_mod_context_t * modctx)
-mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename)
+mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename)
{
mulong fsize = 0;
if(modctx->modfile)
diff --git a/src/external/jar_xm.h b/src/external/jar_xm.h
index f9ddb511..7f0517df 100644
--- a/src/external/jar_xm.h
+++ b/src/external/jar_xm.h
@@ -102,7 +102,7 @@ int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const
* @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);
+int jar_xm_create_context(jar_xm_context_t** ctx, const char* moddata, uint32_t rate);
/** Create a XM context.
*
@@ -114,17 +114,17 @@ int jar_xm_create_context(jar_xm_context_t**, const char* moddata, uint32_t rate
* @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);
+int jar_xm_create_context_safe(jar_xm_context_t** ctx, 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*);
+void jar_xm_free_context(jar_xm_context_t* ctx);
/** 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);
+void jar_xm_generate_samples(jar_xm_context_t* ctx, float* output, size_t numsamples);
/** Play the module, resample from 32 bit to 16 bit, and put the sound samples in an output buffer.
*
@@ -173,12 +173,12 @@ void jar_xm_generate_samples_8bit(jar_xm_context_t* ctx, char* output, size_t nu
*
* @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);
+void jar_xm_set_max_loop_count(jar_xm_context_t* ctx, 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*);
+uint8_t jar_xm_get_loop_count(jar_xm_context_t* ctx);
@@ -188,7 +188,7 @@ uint8_t jar_xm_get_loop_count(jar_xm_context_t*);
*
* @return whether the channel was muted.
*/
-bool jar_xm_mute_channel(jar_xm_context_t*, uint16_t, bool);
+bool jar_xm_mute_channel(jar_xm_context_t* ctx, uint16_t, bool);
/** Mute or unmute an instrument.
*
@@ -197,43 +197,43 @@ bool jar_xm_mute_channel(jar_xm_context_t*, uint16_t, bool);
*
* @return whether the instrument was muted.
*/
-bool jar_xm_mute_instrument(jar_xm_context_t*, uint16_t, bool);
+bool jar_xm_mute_instrument(jar_xm_context_t* ctx, uint16_t, bool);
/** Get the module name as a NUL-terminated string. */
-const char* jar_xm_get_module_name(jar_xm_context_t*);
+const char* jar_xm_get_module_name(jar_xm_context_t* ctx);
/** Get the tracker name as a NUL-terminated string. */
-const char* jar_xm_get_tracker_name(jar_xm_context_t*);
+const char* jar_xm_get_tracker_name(jar_xm_context_t* ctx);
/** Get the number of channels. */
-uint16_t jar_xm_get_number_of_channels(jar_xm_context_t*);
+uint16_t jar_xm_get_number_of_channels(jar_xm_context_t* ctx);
/** 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*);
+uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t* ctx);
/** 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);
+uint16_t jar_xm_get_number_of_rows(jar_xm_context_t* ctx, uint16_t);
/** Get the number of instruments. */
-uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t*);
+uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t* ctx);
/** 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);
+uint16_t jar_xm_get_number_of_samples(jar_xm_context_t* ctx, uint16_t);
@@ -242,7 +242,7 @@ uint16_t jar_xm_get_number_of_samples(jar_xm_context_t*, uint16_t);
* @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);
+void jar_xm_get_playing_speed(jar_xm_context_t* ctx, uint16_t* bpm, uint16_t* tempo);
/** Get the current position in the module being played.
*
@@ -257,7 +257,7 @@ void jar_xm_get_playing_speed(jar_xm_context_t*, uint16_t* bpm, uint16_t* tempo)
* 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);
+void jar_xm_get_position(jar_xm_context_t* ctx, 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.
@@ -265,7 +265,7 @@ void jar_xm_get_position(jar_xm_context_t*, uint8_t* pattern_index, uint8_t* pat
* @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);
+uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t* ctx, uint16_t);
/** Get the latest time (in number of generated samples) when a
* particular sample was triggered in any channel.
@@ -276,21 +276,21 @@ uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t*, uint16_t);
* @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);
+uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t* ctx, 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);
+uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t* ctx, 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*);
+uint64_t jar_xm_get_remaining_samples(jar_xm_context_t* ctx);
#ifdef __cplusplus
}
@@ -308,7 +308,7 @@ uint64_t jar_xm_get_remaining_samples(jar_xm_context_t*);
#include <math.h>
#include <string.h>
-#if JAR_XM_DEBUG
+#ifdef JAR_XM_DEBUG
#include <stdio.h>
#define DEBUG(fmt, ...) do { \
fprintf(stderr, "%s(): " fmt "\n", __func__, __VA_ARGS__); \
@@ -638,7 +638,7 @@ int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, siz
/* 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 = (*ctxp = (jar_xm_context_t *)mempool);
ctx->allocated_memory = mempool; /* Keep original pointer for free() */
mempool += sizeof(jar_xm_context_t);
@@ -685,20 +685,18 @@ int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, siz
return 0;
}
-void jar_xm_free_context(jar_xm_context_t* context) {
- free(context->allocated_memory);
+void jar_xm_free_context(jar_xm_context_t* ctx) {
+ free(ctx->allocated_memory);
}
-void jar_xm_set_max_loop_count(jar_xm_context_t* context, uint8_t loopcnt) {
- context->max_loop_count = loopcnt;
+void jar_xm_set_max_loop_count(jar_xm_context_t* ctx, uint8_t loopcnt) {
+ ctx->max_loop_count = loopcnt;
}
-uint8_t jar_xm_get_loop_count(jar_xm_context_t* context) {
- return context->loop_count;
+uint8_t jar_xm_get_loop_count(jar_xm_context_t* ctx) {
+ return ctx->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;
@@ -1435,7 +1433,7 @@ static void jar_xm_volume_slide(jar_xm_channel_context_t* ch, uint8_t rawval) {
}
}
-static float jar_xm_envelope_lerp(jar_xm_envelope_point_t* restrict a, jar_xm_envelope_point_t* restrict b, uint16_t pos) {
+static float jar_xm_envelope_lerp(jar_xm_envelope_point_t* a, jar_xm_envelope_point_t* 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;
diff --git a/src/external/lua/include/lauxlib.h b/src/external/lua/include/lauxlib.h
new file mode 100644
index 00000000..ddb7c228
--- /dev/null
+++ b/src/external/lua/include/lauxlib.h
@@ -0,0 +1,256 @@
+/*
+** $Id: lauxlib.h,v 1.129 2015/11/23 11:29:43 roberto Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+
+/* extra error code for 'luaL_load' */
+#define LUA_ERRFILE (LUA_ERRERR+1)
+
+
+typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;
+
+
+#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
+
+LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
+#define luaL_checkversion(L) \
+ luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
+
+LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
+LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
+LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
+ size_t *l);
+LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
+ const char *def, size_t *l);
+LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
+LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
+
+LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
+LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
+ lua_Integer def);
+
+LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
+LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
+LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
+
+LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
+LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
+LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
+LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
+
+LUALIB_API void (luaL_where) (lua_State *L, int lvl);
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+
+LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
+ const char *const lst[]);
+
+LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
+LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
+
+/* predefined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+
+LUALIB_API int (luaL_ref) (lua_State *L, int t);
+LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+
+LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
+ const char *mode);
+
+#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
+
+LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
+ const char *name, const char *mode);
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
+
+LUALIB_API lua_State *(luaL_newstate) (void);
+
+LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
+
+LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
+ const char *r);
+
+LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
+
+LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
+
+LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
+ const char *msg, int level);
+
+LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb);
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+
+#define luaL_newlibtable(L,l) \
+ lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
+
+#define luaL_newlib(L,l) \
+ (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
+
+#define luaL_argcheck(L, cond,arg,extramsg) \
+ ((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
+#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
+#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
+
+#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
+
+#define luaL_dofile(L, fn) \
+ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_dostring(L, s) \
+ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
+
+#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
+
+#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+typedef struct luaL_Buffer {
+ char *b; /* buffer address */
+ size_t size; /* buffer size */
+ size_t n; /* number of characters in buffer */
+ lua_State *L;
+ char initb[LUAL_BUFFERSIZE]; /* initial buffer */
+} luaL_Buffer;
+
+
+#define luaL_addchar(B,c) \
+ ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
+ ((B)->b[(B)->n++] = (c)))
+
+#define luaL_addsize(B,s) ((B)->n += (s))
+
+LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
+LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
+LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
+LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
+
+#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** File handles for IO library
+** =======================================================
+*/
+
+/*
+** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
+** initial structure 'luaL_Stream' (it may contain other fields
+** after that initial structure).
+*/
+
+#define LUA_FILEHANDLE "FILE*"
+
+
+typedef struct luaL_Stream {
+ FILE *f; /* stream (NULL for incompletely created streams) */
+ lua_CFunction closef; /* to close stream (NULL for closed streams) */
+} luaL_Stream;
+
+/* }====================================================== */
+
+
+
+/* compatibility with old module system */
+#if defined(LUA_COMPAT_MODULE)
+
+LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname,
+ int sizehint);
+LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname,
+ const luaL_Reg *l, int nup);
+
+#define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0))
+
+#endif
+
+
+/*
+** {==================================================================
+** "Abstraction Layer" for basic report of messages and errors
+** ===================================================================
+*/
+
+/* print a string */
+#if !defined(lua_writestring)
+#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
+#endif
+
+/* print a newline and flush the output */
+#if !defined(lua_writeline)
+#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
+#endif
+
+/* print an error message */
+#if !defined(lua_writestringerror)
+#define lua_writestringerror(s,p) \
+ (fprintf(stderr, (s), (p)), fflush(stderr))
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {============================================================
+** Compatibility with deprecated conversions
+** =============================================================
+*/
+#if defined(LUA_COMPAT_APIINTCASTS)
+
+#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
+#define luaL_optunsigned(L,a,d) \
+ ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
+
+#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
+#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
+
+#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
+#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
+
+#endif
+/* }============================================================ */
+
+
+
+#endif
+
+
diff --git a/src/external/lua/include/lua.h b/src/external/lua/include/lua.h
new file mode 100644
index 00000000..f78899fc
--- /dev/null
+++ b/src/external/lua/include/lua.h
@@ -0,0 +1,486 @@
+/*
+** $Id: lua.h,v 1.331 2016/05/30 15:53:28 roberto Exp $
+** Lua - A Scripting Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#include "luaconf.h"
+
+
+#define LUA_VERSION_MAJOR "5"
+#define LUA_VERSION_MINOR "3"
+#define LUA_VERSION_NUM 503
+#define LUA_VERSION_RELEASE "3"
+
+#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
+#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
+#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2016 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
+
+
+/* mark for precompiled code ('<esc>Lua') */
+#define LUA_SIGNATURE "\x1bLua"
+
+/* option for multiple returns in 'lua_pcall' and 'lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** Pseudo-indices
+** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
+** space after that to help overflow detection)
+*/
+#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
+#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
+
+
+/* thread status */
+#define LUA_OK 0
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRGCMM 5
+#define LUA_ERRERR 6
+
+
+typedef struct lua_State lua_State;
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TLIGHTUSERDATA 2
+#define LUA_TNUMBER 3
+#define LUA_TSTRING 4
+#define LUA_TTABLE 5
+#define LUA_TFUNCTION 6
+#define LUA_TUSERDATA 7
+#define LUA_TTHREAD 8
+
+#define LUA_NUMTAGS 9
+
+
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/* predefined values in the registry */
+#define LUA_RIDX_MAINTHREAD 1
+#define LUA_RIDX_GLOBALS 2
+#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+/* unsigned integer type */
+typedef LUA_UNSIGNED lua_Unsigned;
+
+/* type for continuation-function contexts */
+typedef LUA_KCONTEXT lua_KContext;
+
+
+/*
+** Type for C functions registered with Lua
+*/
+typedef int (*lua_CFunction) (lua_State *L);
+
+/*
+** Type for continuation functions
+*/
+typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);
+
+
+/*
+** Type for functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
+
+
+/*
+** Type for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/*
+** RCS ident string
+*/
+extern const char lua_ident[];
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+LUA_API const lua_Number *(lua_version) (lua_State *L);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_absindex) (lua_State *L, int idx);
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_rotate) (lua_State *L, int idx, int n);
+LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx);
+LUA_API int (lua_checkstack) (lua_State *L, int n);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isinteger) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
+LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API size_t (lua_rawlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** Comparison and arithmetic functions
+*/
+
+#define LUA_OPADD 0 /* ORDER TM, ORDER OP */
+#define LUA_OPSUB 1
+#define LUA_OPMUL 2
+#define LUA_OPMOD 3
+#define LUA_OPPOW 4
+#define LUA_OPDIV 5
+#define LUA_OPIDIV 6
+#define LUA_OPBAND 7
+#define LUA_OPBOR 8
+#define LUA_OPBXOR 9
+#define LUA_OPSHL 10
+#define LUA_OPSHR 11
+#define LUA_OPUNM 12
+#define LUA_OPBNOT 13
+
+LUA_API void (lua_arith) (lua_State *L, int op);
+
+#define LUA_OPEQ 0
+#define LUA_OPLT 1
+#define LUA_OPLE 2
+
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
+LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API int (lua_getglobal) (lua_State *L, const char *name);
+LUA_API int (lua_gettable) (lua_State *L, int idx);
+LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
+LUA_API int (lua_rawget) (lua_State *L, int idx);
+LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
+LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
+
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_getuservalue) (lua_State *L, int idx);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_setglobal) (lua_State *L, const char *name);
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
+LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API void (lua_setuservalue) (lua_State *L, int idx);
+
+
+/*
+** 'load' and 'call' functions (load and run Lua code)
+*/
+LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults,
+ lua_KContext ctx, lua_KFunction k);
+#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
+
+LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
+ lua_KContext ctx, lua_KFunction k);
+#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
+
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname, const char *mode);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx,
+ lua_KFunction k);
+LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg);
+LUA_API int (lua_status) (lua_State *L);
+LUA_API int (lua_isyieldable) (lua_State *L);
+
+#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
+
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+#define LUA_GCISRUNNING 9
+
+LUA_API int (lua_gc) (lua_State *L, int what, int data);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+LUA_API void (lua_len) (lua_State *L, int idx);
+
+LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
+
+
+
+/*
+** {==============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE))
+
+#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL)
+#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL)
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) lua_pushstring(L, "" s)
+
+#define lua_pushglobaltable(L) \
+ ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
+
+#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
+
+#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
+
+/* }============================================================== */
+
+
+/*
+** {==============================================================
+** compatibility macros for unsigned conversions
+** ===============================================================
+*/
+#if defined(LUA_COMPAT_APIINTCASTS)
+
+#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n))
+#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is))
+#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL)
+
+#endif
+/* }============================================================== */
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILCALL 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+
+
+/* Functions to be called by the debugger in specific events */
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
+LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n);
+LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n);
+
+LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n);
+LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1,
+ int fidx2, int n2);
+
+LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook (lua_gethook) (lua_State *L);
+LUA_API int (lua_gethookmask) (lua_State *L);
+LUA_API int (lua_gethookcount) (lua_State *L);
+
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
+ const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ unsigned char nups; /* (u) number of upvalues */
+ unsigned char nparams;/* (u) number of parameters */
+ char isvararg; /* (u) */
+ char istailcall; /* (t) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ struct CallInfo *i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+
+/******************************************************************************
+* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+
+#endif
diff --git a/src/external/lua/include/lua.hpp b/src/external/lua/include/lua.hpp
new file mode 100644
index 00000000..ec417f59
--- /dev/null
+++ b/src/external/lua/include/lua.hpp
@@ -0,0 +1,9 @@
+// lua.hpp
+// Lua header files for C++
+// <<extern "C">> not supplied automatically because Lua also compiles as C++
+
+extern "C" {
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+}
diff --git a/src/external/lua/include/luaconf.h b/src/external/lua/include/luaconf.h
new file mode 100644
index 00000000..867e9cb1
--- /dev/null
+++ b/src/external/lua/include/luaconf.h
@@ -0,0 +1,769 @@
+/*
+** $Id: luaconf.h,v 1.255 2016/05/01 20:06:09 roberto Exp $
+** Configuration file for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef luaconf_h
+#define luaconf_h
+
+#include <limits.h>
+#include <stddef.h>
+
+
+/*
+** ===================================================================
+** Search for "@@" to find all configurable definitions.
+** ===================================================================
+*/
+
+
+/*
+** {====================================================================
+** System Configuration: macros to adapt (if needed) Lua to some
+** particular platform, for instance compiling it with 32-bit numbers or
+** restricting it to C89.
+** =====================================================================
+*/
+
+/*
+@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. You
+** can also define LUA_32BITS in the make file, but changing here you
+** ensure that all software connected to Lua will be compiled with the
+** same configuration.
+*/
+/* #define LUA_32BITS */
+
+
+/*
+@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
+** Define it if you want Lua to avoid the use of a few C99 features
+** or Windows-specific features on Windows.
+*/
+/* #define LUA_USE_C89 */
+
+
+/*
+** By default, Lua on Windows use (some) specific Windows features
+*/
+#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE)
+#define LUA_USE_WINDOWS /* enable goodies for regular Windows */
+#endif
+
+
+#if defined(LUA_USE_WINDOWS)
+#define LUA_DL_DLL /* enable support for DLL */
+#define LUA_USE_C89 /* broadly, Windows is C89 */
+#endif
+
+
+#if defined(LUA_USE_LINUX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
+#define LUA_USE_READLINE /* needs some extra libraries */
+#endif
+
+
+#if defined(LUA_USE_MACOSX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* MacOS does not need -ldl */
+#define LUA_USE_READLINE /* needs an extra library: -lreadline */
+#endif
+
+
+/*
+@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
+** C89 ('long' and 'double'); Windows always has '__int64', so it does
+** not need to use this case.
+*/
+#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
+#define LUA_C89_NUMBERS
+#endif
+
+
+
+/*
+@@ LUAI_BITSINT defines the (minimum) number of bits in an 'int'.
+*/
+/* avoid undefined shifts */
+#if ((INT_MAX >> 15) >> 15) >= 1
+#define LUAI_BITSINT 32
+#else
+/* 'int' always must have at least 16 bits */
+#define LUAI_BITSINT 16
+#endif
+
+
+/*
+@@ LUA_INT_TYPE defines the type for Lua integers.
+@@ LUA_FLOAT_TYPE defines the type for Lua floats.
+** Lua should work fine with any mix of these options (if supported
+** by your C compiler). The usual configurations are 64-bit integers
+** and 'double' (the default), 32-bit integers and 'float' (for
+** restricted platforms), and 'long'/'double' (for C compilers not
+** compliant with C99, which may not have support for 'long long').
+*/
+
+/* predefined options for LUA_INT_TYPE */
+#define LUA_INT_INT 1
+#define LUA_INT_LONG 2
+#define LUA_INT_LONGLONG 3
+
+/* predefined options for LUA_FLOAT_TYPE */
+#define LUA_FLOAT_FLOAT 1
+#define LUA_FLOAT_DOUBLE 2
+#define LUA_FLOAT_LONGDOUBLE 3
+
+#if defined(LUA_32BITS) /* { */
+/*
+** 32-bit integers and 'float'
+*/
+#if LUAI_BITSINT >= 32 /* use 'int' if big enough */
+#define LUA_INT_TYPE LUA_INT_INT
+#else /* otherwise use 'long' */
+#define LUA_INT_TYPE LUA_INT_LONG
+#endif
+#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
+
+#elif defined(LUA_C89_NUMBERS) /* }{ */
+/*
+** largest types available for C89 ('long' and 'double')
+*/
+#define LUA_INT_TYPE LUA_INT_LONG
+#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
+
+#endif /* } */
+
+
+/*
+** default configuration for 64-bit Lua ('long long' and 'double')
+*/
+#if !defined(LUA_INT_TYPE)
+#define LUA_INT_TYPE LUA_INT_LONGLONG
+#endif
+
+#if !defined(LUA_FLOAT_TYPE)
+#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
+#endif
+
+/* }================================================================== */
+
+
+
+
+/*
+** {==================================================================
+** Configuration for Paths.
+** ===================================================================
+*/
+
+/*
+@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
+** Lua libraries.
+@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
+** C libraries.
+** CHANGE them if your machine has a non-conventional directory
+** hierarchy or if you want to install your libraries in
+** non-conventional directories.
+*/
+#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
+#if defined(_WIN32) /* { */
+/*
+** In Windows, any exclamation mark ('!') in the path is replaced by the
+** path of the directory of the executable file of the current process.
+*/
+#define LUA_LDIR "!\\lua\\"
+#define LUA_CDIR "!\\"
+#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\"
+#define LUA_PATH_DEFAULT \
+ LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \
+ LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \
+ ".\\?.lua;" ".\\?\\init.lua"
+#define LUA_CPATH_DEFAULT \
+ LUA_CDIR"?.dll;" \
+ LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
+ LUA_CDIR"loadall.dll;" ".\\?.dll;" \
+ LUA_CDIR"?53.dll;" ".\\?53.dll"
+
+#else /* }{ */
+
+#define LUA_ROOT "/usr/local/"
+#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/"
+#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/"
+#define LUA_PATH_DEFAULT \
+ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
+ "./?.lua;" "./?/init.lua"
+#define LUA_CPATH_DEFAULT \
+ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so;" \
+ LUA_CDIR"lib?53.so;" "./lib?53.so"
+#endif /* } */
+
+
+/*
+@@ LUA_DIRSEP is the directory separator (for submodules).
+** CHANGE it if your machine does not use "/" as the directory separator
+** and is not Windows. (On Windows Lua automatically uses "\".)
+*/
+#if defined(_WIN32)
+#define LUA_DIRSEP "\\"
+#else
+#define LUA_DIRSEP "/"
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Marks for exported symbols in the C code
+** ===================================================================
+*/
+
+/*
+@@ LUA_API is a mark for all core API functions.
+@@ LUALIB_API is a mark for all auxiliary library functions.
+@@ LUAMOD_API is a mark for all standard library opening functions.
+** CHANGE them if you need to define those functions in some special way.
+** For instance, if you want to create one Windows DLL with the core and
+** the libraries, you may want to use the following definition (define
+** LUA_BUILD_AS_DLL to get it).
+*/
+#if defined(LUA_BUILD_AS_DLL) /* { */
+
+#if defined(LUA_CORE) || defined(LUA_LIB) /* { */
+#define LUA_API __declspec(dllexport)
+#else /* }{ */
+#define LUA_API __declspec(dllimport)
+#endif /* } */
+
+#else /* }{ */
+
+#define LUA_API extern
+
+#endif /* } */
+
+
+/* more often than not the libs go together with the core */
+#define LUALIB_API LUA_API
+#define LUAMOD_API LUALIB_API
+
+
+/*
+@@ LUAI_FUNC is a mark for all extern functions that are not to be
+** exported to outside modules.
+@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables
+** that are not to be exported to outside modules (LUAI_DDEF for
+** definitions and LUAI_DDEC for declarations).
+** CHANGE them if you need to mark them in some special way. Elf/gcc
+** (versions 3.2 and later) mark them as "hidden" to optimize access
+** when Lua is compiled as a shared library. Not all elf targets support
+** this attribute. Unfortunately, gcc does not offer a way to check
+** whether the target offers that support, and those without support
+** give a warning about it. To avoid these warnings, change to the
+** default definition.
+*/
+#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
+ defined(__ELF__) /* { */
+#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
+#else /* }{ */
+#define LUAI_FUNC extern
+#endif /* } */
+
+#define LUAI_DDEC LUAI_FUNC
+#define LUAI_DDEF /* empty */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Compatibility with previous versions
+** ===================================================================
+*/
+
+/*
+@@ LUA_COMPAT_5_2 controls other macros for compatibility with Lua 5.2.
+@@ LUA_COMPAT_5_1 controls other macros for compatibility with Lua 5.1.
+** You can define it to get all options, or change specific options
+** to fit your specific needs.
+*/
+#if defined(LUA_COMPAT_5_2) /* { */
+
+/*
+@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
+** functions in the mathematical library.
+*/
+#define LUA_COMPAT_MATHLIB
+
+/*
+@@ LUA_COMPAT_BITLIB controls the presence of library 'bit32'.
+*/
+#define LUA_COMPAT_BITLIB
+
+/*
+@@ LUA_COMPAT_IPAIRS controls the effectiveness of the __ipairs metamethod.
+*/
+#define LUA_COMPAT_IPAIRS
+
+/*
+@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for
+** manipulating other integer types (lua_pushunsigned, lua_tounsigned,
+** luaL_checkint, luaL_checklong, etc.)
+*/
+#define LUA_COMPAT_APIINTCASTS
+
+#endif /* } */
+
+
+#if defined(LUA_COMPAT_5_1) /* { */
+
+/* Incompatibilities from 5.2 -> 5.3 */
+#define LUA_COMPAT_MATHLIB
+#define LUA_COMPAT_APIINTCASTS
+
+/*
+@@ LUA_COMPAT_UNPACK controls the presence of global 'unpack'.
+** You can replace it with 'table.unpack'.
+*/
+#define LUA_COMPAT_UNPACK
+
+/*
+@@ LUA_COMPAT_LOADERS controls the presence of table 'package.loaders'.
+** You can replace it with 'package.searchers'.
+*/
+#define LUA_COMPAT_LOADERS
+
+/*
+@@ macro 'lua_cpcall' emulates deprecated function lua_cpcall.
+** You can call your C function directly (with light C functions).
+*/
+#define lua_cpcall(L,f,u) \
+ (lua_pushcfunction(L, (f)), \
+ lua_pushlightuserdata(L,(u)), \
+ lua_pcall(L,1,0,0))
+
+
+/*
+@@ LUA_COMPAT_LOG10 defines the function 'log10' in the math library.
+** You can rewrite 'log10(x)' as 'log(x, 10)'.
+*/
+#define LUA_COMPAT_LOG10
+
+/*
+@@ LUA_COMPAT_LOADSTRING defines the function 'loadstring' in the base
+** library. You can rewrite 'loadstring(s)' as 'load(s)'.
+*/
+#define LUA_COMPAT_LOADSTRING
+
+/*
+@@ LUA_COMPAT_MAXN defines the function 'maxn' in the table library.
+*/
+#define LUA_COMPAT_MAXN
+
+/*
+@@ The following macros supply trivial compatibility for some
+** changes in the API. The macros themselves document how to
+** change your code to avoid using them.
+*/
+#define lua_strlen(L,i) lua_rawlen(L, (i))
+
+#define lua_objlen(L,i) lua_rawlen(L, (i))
+
+#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
+#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
+
+/*
+@@ LUA_COMPAT_MODULE controls compatibility with previous
+** module functions 'module' (Lua) and 'luaL_register' (C).
+*/
+#define LUA_COMPAT_MODULE
+
+#endif /* } */
+
+
+/*
+@@ LUA_COMPAT_FLOATSTRING makes Lua format integral floats without a
+@@ a float mark ('.0').
+** This macro is not on by default even in compatibility mode,
+** because this is not really an incompatibility.
+*/
+/* #define LUA_COMPAT_FLOATSTRING */
+
+/* }================================================================== */
+
+
+
+/*
+** {==================================================================
+** Configuration for Numbers.
+** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
+** satisfy your needs.
+** ===================================================================
+*/
+
+/*
+@@ LUA_NUMBER is the floating-point type used by Lua.
+@@ LUAI_UACNUMBER is the result of an 'usual argument conversion'
+@@ over a floating number.
+@@ l_mathlim(x) corrects limit name 'x' to the proper float type
+** by prefixing it with one of FLT/DBL/LDBL.
+@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
+@@ LUA_NUMBER_FMT is the format for writing floats.
+@@ lua_number2str converts a float to a string.
+@@ l_mathop allows the addition of an 'l' or 'f' to all math operations.
+@@ l_floor takes the floor of a float.
+@@ lua_str2number converts a decimal numeric string to a number.
+*/
+
+
+/* The following definitions are good for most cases here */
+
+#define l_floor(x) (l_mathop(floor)(x))
+
+#define lua_number2str(s,sz,n) l_sprintf((s), sz, LUA_NUMBER_FMT, (n))
+
+/*
+@@ lua_numbertointeger converts a float number to an integer, or
+** returns 0 if float is not within the range of a lua_Integer.
+** (The range comparisons are tricky because of rounding. The tests
+** here assume a two-complement representation, where MININTEGER always
+** has an exact representation as a float; MAXINTEGER may not have one,
+** and therefore its conversion to float may have an ill-defined value.)
+*/
+#define lua_numbertointeger(n,p) \
+ ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
+ (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
+ (*(p) = (LUA_INTEGER)(n), 1))
+
+
+/* now the variable definitions */
+
+#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
+
+#define LUA_NUMBER float
+
+#define l_mathlim(n) (FLT_##n)
+
+#define LUAI_UACNUMBER double
+
+#define LUA_NUMBER_FRMLEN ""
+#define LUA_NUMBER_FMT "%.7g"
+
+#define l_mathop(op) op##f
+
+#define lua_str2number(s,p) strtof((s), (p))
+
+
+#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */
+
+#define LUA_NUMBER long double
+
+#define l_mathlim(n) (LDBL_##n)
+
+#define LUAI_UACNUMBER long double
+
+#define LUA_NUMBER_FRMLEN "L"
+#define LUA_NUMBER_FMT "%.19Lg"
+
+#define l_mathop(op) op##l
+
+#define lua_str2number(s,p) strtold((s), (p))
+
+#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */
+
+#define LUA_NUMBER double
+
+#define l_mathlim(n) (DBL_##n)
+
+#define LUAI_UACNUMBER double
+
+#define LUA_NUMBER_FRMLEN ""
+#define LUA_NUMBER_FMT "%.14g"
+
+#define l_mathop(op) op
+
+#define lua_str2number(s,p) strtod((s), (p))
+
+#else /* }{ */
+
+#error "numeric float type not defined"
+
+#endif /* } */
+
+
+
+/*
+@@ LUA_INTEGER is the integer type used by Lua.
+**
+@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
+**
+@@ LUAI_UACINT is the result of an 'usual argument conversion'
+@@ over a lUA_INTEGER.
+@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
+@@ LUA_INTEGER_FMT is the format for writing integers.
+@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
+@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
+@@ lua_integer2str converts an integer to a string.
+*/
+
+
+/* The following definitions are good for most cases here */
+
+#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d"
+#define lua_integer2str(s,sz,n) l_sprintf((s), sz, LUA_INTEGER_FMT, (n))
+
+#define LUAI_UACINT LUA_INTEGER
+
+/*
+** use LUAI_UACINT here to avoid problems with promotions (which
+** can turn a comparison between unsigneds into a signed comparison)
+*/
+#define LUA_UNSIGNED unsigned LUAI_UACINT
+
+
+/* now the variable definitions */
+
+#if LUA_INT_TYPE == LUA_INT_INT /* { int */
+
+#define LUA_INTEGER int
+#define LUA_INTEGER_FRMLEN ""
+
+#define LUA_MAXINTEGER INT_MAX
+#define LUA_MININTEGER INT_MIN
+
+#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */
+
+#define LUA_INTEGER long
+#define LUA_INTEGER_FRMLEN "l"
+
+#define LUA_MAXINTEGER LONG_MAX
+#define LUA_MININTEGER LONG_MIN
+
+#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
+
+/* use presence of macro LLONG_MAX as proxy for C99 compliance */
+#if defined(LLONG_MAX) /* { */
+/* use ISO C99 stuff */
+
+#define LUA_INTEGER long long
+#define LUA_INTEGER_FRMLEN "ll"
+
+#define LUA_MAXINTEGER LLONG_MAX
+#define LUA_MININTEGER LLONG_MIN
+
+#elif defined(LUA_USE_WINDOWS) /* }{ */
+/* in Windows, can use specific Windows types */
+
+#define LUA_INTEGER __int64
+#define LUA_INTEGER_FRMLEN "I64"
+
+#define LUA_MAXINTEGER _I64_MAX
+#define LUA_MININTEGER _I64_MIN
+
+#else /* }{ */
+
+#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \
+ or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)"
+
+#endif /* } */
+
+#else /* }{ */
+
+#error "numeric integer type not defined"
+
+#endif /* } */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Dependencies with C99 and other C details
+** ===================================================================
+*/
+
+/*
+@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89.
+** (All uses in Lua have only one format item.)
+*/
+#if !defined(LUA_USE_C89)
+#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i)
+#else
+#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i))
+#endif
+
+
+/*
+@@ lua_strx2number converts an hexadecimal numeric string to a number.
+** In C99, 'strtod' does that conversion. Otherwise, you can
+** leave 'lua_strx2number' undefined and Lua will provide its own
+** implementation.
+*/
+#if !defined(LUA_USE_C89)
+#define lua_strx2number(s,p) lua_str2number(s,p)
+#endif
+
+
+/*
+@@ lua_number2strx converts a float to an hexadecimal numeric string.
+** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that.
+** Otherwise, you can leave 'lua_number2strx' undefined and Lua will
+** provide its own implementation.
+*/
+#if !defined(LUA_USE_C89)
+#define lua_number2strx(L,b,sz,f,n) ((void)L, l_sprintf(b,sz,f,n))
+#endif
+
+
+/*
+** 'strtof' and 'opf' variants for math functions are not valid in
+** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the
+** availability of these variants. ('math.h' is already included in
+** all files that use these macros.)
+*/
+#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF))
+#undef l_mathop /* variants not available */
+#undef lua_str2number
+#define l_mathop(op) (lua_Number)op /* no variant */
+#define lua_str2number(s,p) ((lua_Number)strtod((s), (p)))
+#endif
+
+
+/*
+@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation
+** functions. It must be a numerical type; Lua will use 'intptr_t' if
+** available, otherwise it will use 'ptrdiff_t' (the nearest thing to
+** 'intptr_t' in C89)
+*/
+#define LUA_KCONTEXT ptrdiff_t
+
+#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
+ __STDC_VERSION__ >= 199901L
+#include <stdint.h>
+#if defined(INTPTR_MAX) /* even in C99 this type is optional */
+#undef LUA_KCONTEXT
+#define LUA_KCONTEXT intptr_t
+#endif
+#endif
+
+
+/*
+@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point).
+** Change that if you do not want to use C locales. (Code using this
+** macro must include header 'locale.h'.)
+*/
+#if !defined(lua_getlocaledecpoint)
+#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Language Variations
+** =====================================================================
+*/
+
+/*
+@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some
+** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from
+** numbers to strings. Define LUA_NOCVTS2N to turn off automatic
+** coercion from strings to numbers.
+*/
+/* #define LUA_NOCVTN2S */
+/* #define LUA_NOCVTS2N */
+
+
+/*
+@@ LUA_USE_APICHECK turns on several consistency checks on the C API.
+** Define it as a help when debugging C code.
+*/
+#if defined(LUA_USE_APICHECK)
+#include <assert.h>
+#define luai_apicheck(l,e) assert(e)
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Macros that affect the API and must be stable (that is, must be the
+** same when you compile Lua and when you compile code that links to
+** Lua). You probably do not want/need to change them.
+** =====================================================================
+*/
+
+/*
+@@ LUAI_MAXSTACK limits the size of the Lua stack.
+** CHANGE it if you need a different limit. This limit is arbitrary;
+** its only purpose is to stop Lua from consuming unlimited stack
+** space (and to reserve some numbers for pseudo-indices).
+*/
+#if LUAI_BITSINT >= 32
+#define LUAI_MAXSTACK 1000000
+#else
+#define LUAI_MAXSTACK 15000
+#endif
+
+
+/*
+@@ LUA_EXTRASPACE defines the size of a raw memory area associated with
+** a Lua state with very fast access.
+** CHANGE it if you need a different size.
+*/
+#define LUA_EXTRASPACE (sizeof(void *))
+
+
+/*
+@@ LUA_IDSIZE gives the maximum size for the description of the source
+@@ of a function in debug information.
+** CHANGE it if you want a different size.
+*/
+#define LUA_IDSIZE 60
+
+
+/*
+@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
+** CHANGE it if it uses too much C-stack space. (For long double,
+** 'string.format("%.99f", 1e4932)' needs ~5030 bytes, so a
+** smaller buffer would force a memory allocation for each call to
+** 'string.format'.)
+*/
+#if defined(LUA_FLOAT_LONGDOUBLE)
+#define LUAL_BUFFERSIZE 8192
+#else
+#define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer)))
+#endif
+
+/* }================================================================== */
+
+
+/*
+@@ LUA_QL describes how error messages quote program elements.
+** Lua does not use these macros anymore; they are here for
+** compatibility only.
+*/
+#define LUA_QL(x) "'" x "'"
+#define LUA_QS LUA_QL("%s")
+
+
+
+
+/* =================================================================== */
+
+/*
+** Local configuration. You can use this space to add your redefinitions
+** without modifying the main part of the file.
+*/
+
+
+
+
+
+#endif
+
diff --git a/src/external/lua/include/lualib.h b/src/external/lua/include/lualib.h
new file mode 100644
index 00000000..5165c0fb
--- /dev/null
+++ b/src/external/lua/include/lualib.h
@@ -0,0 +1,58 @@
+/*
+** $Id: lualib.h,v 1.44 2014/02/06 17:32:33 roberto Exp $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+
+LUAMOD_API int (luaopen_base) (lua_State *L);
+
+#define LUA_COLIBNAME "coroutine"
+LUAMOD_API int (luaopen_coroutine) (lua_State *L);
+
+#define LUA_TABLIBNAME "table"
+LUAMOD_API int (luaopen_table) (lua_State *L);
+
+#define LUA_IOLIBNAME "io"
+LUAMOD_API int (luaopen_io) (lua_State *L);
+
+#define LUA_OSLIBNAME "os"
+LUAMOD_API int (luaopen_os) (lua_State *L);
+
+#define LUA_STRLIBNAME "string"
+LUAMOD_API int (luaopen_string) (lua_State *L);
+
+#define LUA_UTF8LIBNAME "utf8"
+LUAMOD_API int (luaopen_utf8) (lua_State *L);
+
+#define LUA_BITLIBNAME "bit32"
+LUAMOD_API int (luaopen_bit32) (lua_State *L);
+
+#define LUA_MATHLIBNAME "math"
+LUAMOD_API int (luaopen_math) (lua_State *L);
+
+#define LUA_DBLIBNAME "debug"
+LUAMOD_API int (luaopen_debug) (lua_State *L);
+
+#define LUA_LOADLIBNAME "package"
+LUAMOD_API int (luaopen_package) (lua_State *L);
+
+
+/* open all previous libraries */
+LUALIB_API void (luaL_openlibs) (lua_State *L);
+
+
+
+#if !defined(lua_assert)
+#define lua_assert(x) ((void)0)
+#endif
+
+
+#endif
diff --git a/src/external/lua/lib/liblua53.a b/src/external/lua/lib/liblua53.a
new file mode 100644
index 00000000..e51c0c80
--- /dev/null
+++ b/src/external/lua/lib/liblua53.a
Binary files differ
diff --git a/src/external/lua/lib/liblua53dll.a b/src/external/lua/lib/liblua53dll.a
new file mode 100644
index 00000000..32646db3
--- /dev/null
+++ b/src/external/lua/lib/liblua53dll.a
Binary files differ
diff --git a/src/external/openal_soft/include/AL/alext.h b/src/external/openal_soft/include/AL/alext.h
index 6af581aa..0090c804 100644
--- a/src/external/openal_soft/include/AL/alext.h
+++ b/src/external/openal_soft/include/AL/alext.h
@@ -431,6 +431,11 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi
#endif
#endif
+#ifndef AL_SOFT_gain_clamp_ex
+#define AL_SOFT_gain_clamp_ex 1
+#define AL_GAIN_LIMIT_SOFT 0x200E
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/external/openal_soft/lib/win32/libOpenAL32.a b/src/external/openal_soft/lib/win32/libOpenAL32.a
new file mode 100644
index 00000000..3c0df3c7
--- /dev/null
+++ b/src/external/openal_soft/lib/win32/libOpenAL32.a
Binary files differ
diff --git a/src/external/pthread/lib/pthreadGC2.dll b/src/external/pthread/lib/pthreadGC2.dll
new file mode 100644
index 00000000..67b9289d
--- /dev/null
+++ b/src/external/pthread/lib/pthreadGC2.dll
Binary files differ
diff --git a/src/gestures.c b/src/gestures.c
deleted file mode 100644
index 57b96bd2..00000000
--- a/src/gestures.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/**********************************************************************************************
-*
-* raylib Gestures System - Gestures Processing based on input gesture events (touch/mouse)
-*
-* Initial design by Marc Palau
-* Redesigned by Albert Martos and Ian Eito
-* Reviewed by Ramon Santamaria (@raysan5)
-*
-* 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.
-*
-**********************************************************************************************/
-
-//#define GESTURES_STANDALONE // NOTE: To use the gestures module as standalone lib, just uncomment this line
-
-#if defined(GESTURES_STANDALONE)
- #include "gestures.h"
-#else
- #include "raylib.h" // Required for: Vector2, Gestures
-#endif
-
-#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> // Required for: timespec
- #include <time.h> // Required for: clock_gettime()
-#endif
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-#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 // Measured in normalized screen units (0.0f to 1.0f)
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-// ...
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-
-// Touch gesture variables
-static Vector2 touchDownPosition = { 0.0f, 0.0f };
-static Vector2 touchDownPosition2 = { 0.0f, 0.0f };
-static Vector2 touchDownDragPosition = { 0.0f, 0.0f };
-static Vector2 touchUpPosition = { 0.0f, 0.0f };
-static Vector2 moveDownPosition = { 0.0f, 0.0f };
-static Vector2 moveDownPosition2 = { 0.0f, 0.0f };
-static int numTap = 0;
-
-static int pointCount = 0;
-static int firstTouchId = -1;
-
-static double eventTime = 0.0;
-static double swipeTime = 0.0;
-
-// Hold gesture variables
-static int numHold = 0;
-static float timeHold = 0.0f;
-
-// Drag gesture variables
-static Vector2 dragVector = { 0.0f , 0.0f }; // DRAG vector (between initial and current position)
-static float dragAngle = 0.0f; // DRAG angle (relative to x-axis)
-static float dragDistance = 0.0f; // DRAG distance (from initial touch point to final) (normalized [0..1])
-static float dragIntensity = 0.0f; // DRAG intensity, how far why did the DRAG (pixels per frame)
-static bool startMoving = false; // SWIPE used to define when start measuring swipeTime
-
-// Pinch gesture variables
-static Vector2 pinchVector = { 0.0f , 0.0f }; // PINCH vector (between first and second touch points)
-static float pinchAngle = 0.0f; // PINCH angle (relative to x-axis)
-static float pinchDistance = 0.0f; // PINCH displacement distance (normalized [0..1])
-
-// Detected gestures
-static int previousGesture = GESTURE_NONE;
-static int currentGesture = GESTURE_NONE;
-
-// Enabled gestures flags, all gestures enabled by default
-static unsigned int enabledGestures = 0b0000001111111111;
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Declaration
-//----------------------------------------------------------------------------------
-static float Vector2Angle(Vector2 initialPosition, Vector2 finalPosition);
-static float Vector2Distance(Vector2 v1, Vector2 v2);
-static double GetCurrentTime(void);
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition
-//----------------------------------------------------------------------------------
-
-// Enable only desired getures to be detected
-void SetGesturesEnabled(unsigned int gestureFlags)
-{
- enabledGestures = gestureFlags;
-}
-
-// Check if a gesture have been detected
-bool IsGestureDetected(int gesture)
-{
- if ((enabledGestures & currentGesture) == gesture) return true;
- else return false;
-}
-
-// Process gesture event and translate it into gestures
-void ProcessGestureEvent(GestureEvent event)
-{
- // Reset required variables
- previousGesture = currentGesture;
-
- pointCount = event.pointCount; // Required on UpdateGestures()
-
- if (pointCount < 2)
- {
- if (event.touchAction == TOUCH_DOWN)
- {
- numTap++; // Tap counter
-
- // Detect GESTURE_DOUBLE_TAP
- if ((currentGesture == GESTURE_NONE) && (numTap >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (Vector2Distance(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE))
- {
- currentGesture = GESTURE_DOUBLETAP;
- numTap = 0;
- }
- else // Detect GESTURE_TAP
- {
- numTap = 1;
- currentGesture = GESTURE_TAP;
- }
-
- touchDownPosition = event.position[0];
- touchDownDragPosition = event.position[0];
-
- touchUpPosition = touchDownPosition;
- eventTime = GetCurrentTime();
-
- firstTouchId = event.pointerId[0];
-
- dragVector = (Vector2){ 0.0f, 0.0f };
- }
- else if (event.touchAction == TOUCH_UP)
- {
- if (currentGesture == GESTURE_DRAG) touchUpPosition = event.position[0];
-
- // NOTE: dragIntensity dependend on the resolution of the screen
- dragDistance = Vector2Distance(touchDownPosition, touchUpPosition);
- dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime));
-
- startMoving = false;
-
- // Detect GESTURE_SWIPE
- if ((dragIntensity > FORCE_TO_SWIPE) && firstTouchId == event.pointerId[0])
- {
- // NOTE: Angle should be inverted in Y
- dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition);
-
- if ((dragAngle < 30) || (dragAngle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right
- else if ((dragAngle > 30) && (dragAngle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up
- else if ((dragAngle > 120) && (dragAngle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left
- else if ((dragAngle > 210) && (dragAngle < 300)) currentGesture = GESTURE_SWIPE_DOWN; // Down
- else currentGesture = GESTURE_NONE;
- }
- else
- {
- dragDistance = 0.0f;
- dragIntensity = 0.0f;
- dragAngle = 0.0f;
-
- currentGesture = GESTURE_NONE;
- }
-
- touchDownDragPosition = (Vector2){ 0.0f, 0.0f };
- pointCount = 0;
- }
- else if (event.touchAction == TOUCH_MOVE)
- {
- if (currentGesture == GESTURE_DRAG) eventTime = GetCurrentTime();
-
- if (!startMoving)
- {
- swipeTime = GetCurrentTime();
- startMoving = true;
- }
-
- moveDownPosition = event.position[0];
-
- if (currentGesture == GESTURE_HOLD)
- {
- if (numHold == 1) touchDownPosition = event.position[0];
-
- numHold = 2;
-
- // Detect GESTURE_DRAG
- if (Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_DRAG)
- {
- eventTime = GetCurrentTime();
- currentGesture = GESTURE_DRAG;
- }
- }
-
- dragVector.x = moveDownPosition.x - touchDownDragPosition.x;
- dragVector.y = moveDownPosition.y - touchDownDragPosition.y;
- }
- }
- else // Two touch points
- {
- if (event.touchAction == TOUCH_DOWN)
- {
- touchDownPosition = event.position[0];
- touchDownPosition2 = event.position[1];
-
- //pinchDistance = Vector2Distance(touchDownPosition, touchDownPosition2);
-
- pinchVector.x = touchDownPosition2.x - touchDownPosition.x;
- pinchVector.y = touchDownPosition2.y - touchDownPosition.y;
-
- currentGesture = GESTURE_HOLD;
- timeHold = GetCurrentTime();
- }
- else if (event.touchAction == TOUCH_MOVE)
- {
- pinchDistance = Vector2Distance(moveDownPosition, moveDownPosition2);
-
- touchDownPosition = moveDownPosition;
- touchDownPosition2 = moveDownPosition2;
-
- moveDownPosition = event.position[0];
- moveDownPosition2 = event.position[1];
-
- pinchVector.x = moveDownPosition2.x - moveDownPosition.x;
- pinchVector.y = moveDownPosition2.y - moveDownPosition.y;
-
- if ((Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_PINCH) || (Vector2Distance(touchDownPosition2, moveDownPosition2) >= MINIMUM_PINCH))
- {
- if ((Vector2Distance(moveDownPosition, moveDownPosition2) - pinchDistance) < 0) currentGesture = GESTURE_PINCH_IN;
- else currentGesture = GESTURE_PINCH_OUT;
- }
- else
- {
- currentGesture = GESTURE_HOLD;
- timeHold = GetCurrentTime();
- }
-
- // NOTE: Angle should be inverted in Y
- pinchAngle = 360.0f - Vector2Angle(moveDownPosition, moveDownPosition2);
- }
- else if (event.touchAction == TOUCH_UP)
- {
- pinchDistance = 0.0f;
- pinchAngle = 0.0f;
- pinchVector = (Vector2){ 0.0f, 0.0f };
- pointCount = 0;
-
- currentGesture = GESTURE_NONE;
- }
- }
-}
-
-// Update gestures detected (must be called every frame)
-void UpdateGestures(void)
-{
- // NOTE: Gestures are processed through system callbacks on touch events
-
- // Detect GESTURE_HOLD
- if (((currentGesture == GESTURE_TAP) || (currentGesture == GESTURE_DOUBLETAP)) && (pointCount < 2))
- {
- currentGesture = GESTURE_HOLD;
- timeHold = GetCurrentTime();
- }
-
- if (((GetCurrentTime() - eventTime) > TAP_TIMEOUT) && (currentGesture == GESTURE_DRAG) && (pointCount < 2))
- {
- currentGesture = GESTURE_HOLD;
- timeHold = GetCurrentTime();
- numHold = 1;
- }
-
- // Detect GESTURE_NONE
- if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN))
- {
- currentGesture = GESTURE_NONE;
- }
-}
-
-// Get number of touch points
-int GetTouchPointsCount(void)
-{
- // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called
-
- return pointCount;
-}
-
-// Get latest detected gesture
-int GetGestureDetected(void)
-{
- // Get current gesture only if enabled
- return (enabledGestures & currentGesture);
-}
-
-// Hold time measured in ms
-float GetGestureHoldDuration(void)
-{
- // NOTE: time is calculated on current gesture HOLD
-
- float time = 0.0f;
-
- if (currentGesture == GESTURE_HOLD) time = (float)GetCurrentTime() - timeHold;
-
- return time;
-}
-
-// Get drag vector (between initial touch point to current)
-Vector2 GetGestureDragVector(void)
-{
- // NOTE: drag vector is calculated on one touch points TOUCH_MOVE
-
- return dragVector;
-}
-
-// Get drag angle
-// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
-float GetGestureDragAngle(void)
-{
- // NOTE: drag angle is calculated on one touch points TOUCH_UP
-
- return dragAngle;
-}
-
-// Get distance between two pinch points
-Vector2 GetGesturePinchVector(void)
-{
- // NOTE: The position values used for pinchDistance are not modified like the position values of [core.c]-->GetTouchPosition(int index)
- // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE
-
- return pinchVector;
-}
-
-// Get angle beween two pinch points
-// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
-float GetGesturePinchAngle(void)
-{
- // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE
-
- return pinchAngle;
-}
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
-
-// Returns angle from two-points vector with X-axis
-static float Vector2Angle(Vector2 initialPosition, Vector2 finalPosition)
-{
- float angle;
-
- angle = atan2(finalPosition.y - initialPosition.y, finalPosition.x - initialPosition.x);
- angle *= RAD2DEG;
-
- if (angle < 0) angle += 360.0f;
-
- return angle;
-}
-
-// Calculate distance between two Vector2
-static float Vector2Distance(Vector2 v1, Vector2 v2)
-{
- float result;
-
- float dx = v2.x - v1.x;
- float dy = v2.y - v1.y;
-
- result = sqrt(dx*dx + dy*dy);
-
- return result;
-}
-
-// Time measure returned are milliseconds
-static double GetCurrentTime(void)
-{
- double time = 0;
-
-#if defined(_WIN32)
- unsigned long long int clockFrequency, currentTime;
-
- QueryPerformanceFrequency(&clockFrequency);
- QueryPerformanceCounter(&currentTime);
-
- time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds
-#endif
-
-#if defined(__linux)
- // NOTE: Only for Linux-based systems
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds
-
- time = ((double)nowTime/1000000.0); // Time in miliseconds
-#endif
-
- return time;
-}
diff --git a/src/gestures.h b/src/gestures.h
index 912d0b92..481ef317 100644
--- a/src/gestures.h
+++ b/src/gestures.h
@@ -1,8 +1,21 @@
/**********************************************************************************************
*
-* raylib Gestures System - Gestures Detection and Usage Functions (Android and HTML5)
+* raylib Gestures System - Gestures Processing based on input gesture events (touch/mouse)
*
-* Copyright (c) 2015 Marc Palau and Ramon Santamaria
+* #define GESTURES_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+* #define GESTURES_STANDALONE
+* If defined, the library can be used as standalone to process gesture events with
+* no external dependencies.
+*
+* NOTE: Memory footprint of this library is aproximately 128 bytes
+*
+* Initial design by Marc Palau (2014)
+* Redesigned by Albert Martos and Ian Eito (2015)
+* Reviewed by Ramon Santamaria (2015-2016)
*
* 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.
@@ -28,9 +41,6 @@
#define PI 3.14159265358979323846
#endif
-#define DEG2RAD (PI / 180.0f)
-#define RAD2DEG (180.0f / PI)
-
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
@@ -40,32 +50,34 @@
// Types and Structures Definition
// NOTE: Below types are required for GESTURES_STANDALONE usage
//----------------------------------------------------------------------------------
-#ifndef __cplusplus
- // Boolean type
- typedef enum { false, true } bool;
-#endif
+#if defined(GESTURES_STANDALONE)
+ #ifndef __cplusplus
+ // Boolean type
+ typedef enum { false, true } bool;
+ #endif
+
+ // Vector2 type
+ typedef struct Vector2 {
+ float x;
+ float y;
+ } Vector2;
-// Vector2 type
-typedef struct Vector2 {
- float x;
- float y;
-} Vector2;
-
-// Gestures type
-// NOTE: It could be used as flags to enable only some gestures
-typedef enum {
- GESTURE_NONE = 1,
- GESTURE_TAP = 2,
- GESTURE_DOUBLETAP = 4,
- GESTURE_HOLD = 8,
- GESTURE_DRAG = 16,
- GESTURE_SWIPE_RIGHT = 32,
- GESTURE_SWIPE_LEFT = 64,
- GESTURE_SWIPE_UP = 128,
- GESTURE_SWIPE_DOWN = 256,
- GESTURE_PINCH_IN = 512,
- GESTURE_PINCH_OUT = 1024
-} Gestures;
+ // Gestures type
+ // NOTE: It could be used as flags to enable only some gestures
+ typedef enum {
+ GESTURE_NONE = 1,
+ GESTURE_TAP = 2,
+ GESTURE_DOUBLETAP = 4,
+ GESTURE_HOLD = 8,
+ GESTURE_DRAG = 16,
+ GESTURE_SWIPE_RIGHT = 32,
+ GESTURE_SWIPE_LEFT = 64,
+ GESTURE_SWIPE_UP = 128,
+ GESTURE_SWIPE_DOWN = 256,
+ GESTURE_PINCH_IN = 512,
+ GESTURE_PINCH_OUT = 1024
+ } Gestures;
+#endif
typedef enum { TOUCH_UP, TOUCH_DOWN, TOUCH_MOVE } TouchAction;
@@ -90,22 +102,422 @@ extern "C" { // Prevents name mangling of functions
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
-void SetGesturesEnabled(unsigned int gestureFlags); // Enable a set of gestures using flags
-bool IsGestureDetected(int gesture); // Check if a gesture have been detected
void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures
void UpdateGestures(void); // Update gestures detected (must be called every frame)
-int GetTouchPointsCount(void); // Get touch points count
+#if defined(GESTURES_STANDALONE)
+void SetGesturesEnabled(unsigned int gestureFlags); // Enable a set of gestures using flags
+bool IsGestureDetected(int gesture); // Check if a gesture have been detected
int GetGestureDetected(void); // Get latest detected gesture
+int GetTouchPointsCount(void); // Get touch points count
float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds
Vector2 GetGestureDragVector(void); // Get gesture drag vector
float GetGestureDragAngle(void); // Get gesture drag angle
Vector2 GetGesturePinchVector(void); // Get gesture pinch delta
float GetGesturePinchAngle(void); // Get gesture pinch angle
-
+#endif
#ifdef __cplusplus
}
#endif
#endif // GESTURES_H
+
+/***********************************************************************************
+*
+* GESTURES IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(GESTURES_IMPLEMENTATION)
+
+#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> // Required for: timespec
+ #include <time.h> // Required for: clock_gettime()
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#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 // Measured in normalized screen units (0.0f to 1.0f)
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+
+// Touch gesture variables
+static Vector2 touchDownPosition = { 0.0f, 0.0f };
+static Vector2 touchDownPosition2 = { 0.0f, 0.0f };
+static Vector2 touchDownDragPosition = { 0.0f, 0.0f };
+static Vector2 touchUpPosition = { 0.0f, 0.0f };
+static Vector2 moveDownPosition = { 0.0f, 0.0f };
+static Vector2 moveDownPosition2 = { 0.0f, 0.0f };
+
+static int pointCount = 0; // Touch points counter
+static int firstTouchId = -1; // Touch id for first touch point
+static double eventTime = 0.0; // Time stamp when an event happened
+
+// Tap gesture variables
+static int tapCounter = 0; // TAP counter (one tap implies TOUCH_DOWN and TOUCH_UP actions)
+
+// Hold gesture variables
+static bool resetHold = false; // HOLD reset to get first touch point again
+static float timeHold = 0.0f; // HOLD duration in milliseconds
+
+// Drag gesture variables
+static Vector2 dragVector = { 0.0f , 0.0f }; // DRAG vector (between initial and current position)
+static float dragAngle = 0.0f; // DRAG angle (relative to x-axis)
+static float dragDistance = 0.0f; // DRAG distance (from initial touch point to final) (normalized [0..1])
+static float dragIntensity = 0.0f; // DRAG intensity, how far why did the DRAG (pixels per frame)
+
+// Swipe gestures variables
+static bool startMoving = false; // SWIPE used to define when start measuring swipeTime
+static double swipeTime = 0.0; // SWIPE time to calculate drag intensity
+
+// Pinch gesture variables
+static Vector2 pinchVector = { 0.0f , 0.0f }; // PINCH vector (between first and second touch points)
+static float pinchAngle = 0.0f; // PINCH angle (relative to x-axis)
+static float pinchDistance = 0.0f; // PINCH displacement distance (normalized [0..1])
+
+static int currentGesture = GESTURE_NONE; // Current detected gesture
+
+// Enabled gestures flags, all gestures enabled by default
+static unsigned int enabledGestures = 0b0000001111111111;
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+static float Vector2Angle(Vector2 initialPosition, Vector2 finalPosition);
+static float Vector2Distance(Vector2 v1, Vector2 v2);
+static double GetCurrentTime(void);
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+
+// Enable only desired getures to be detected
+void SetGesturesEnabled(unsigned int gestureFlags)
+{
+ enabledGestures = gestureFlags;
+}
+
+// Check if a gesture have been detected
+bool IsGestureDetected(int gesture)
+{
+ if ((enabledGestures & currentGesture) == gesture) return true;
+ else return false;
+}
+
+// Process gesture event and translate it into gestures
+void ProcessGestureEvent(GestureEvent event)
+{
+ // Reset required variables
+ pointCount = event.pointCount; // Required on UpdateGestures()
+
+ if (pointCount < 2)
+ {
+ if (event.touchAction == TOUCH_DOWN)
+ {
+ tapCounter++; // Tap counter
+
+ // Detect GESTURE_DOUBLE_TAP
+ if ((currentGesture == GESTURE_NONE) && (tapCounter >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (Vector2Distance(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE))
+ {
+ currentGesture = GESTURE_DOUBLETAP;
+ tapCounter = 0;
+ }
+ else // Detect GESTURE_TAP
+ {
+ tapCounter = 1;
+ currentGesture = GESTURE_TAP;
+ }
+
+ touchDownPosition = event.position[0];
+ touchDownDragPosition = event.position[0];
+
+ touchUpPosition = touchDownPosition;
+ eventTime = GetCurrentTime();
+
+ firstTouchId = event.pointerId[0];
+
+ dragVector = (Vector2){ 0.0f, 0.0f };
+ }
+ else if (event.touchAction == TOUCH_UP)
+ {
+ if (currentGesture == GESTURE_DRAG) touchUpPosition = event.position[0];
+
+ // NOTE: dragIntensity dependend on the resolution of the screen
+ dragDistance = Vector2Distance(touchDownPosition, touchUpPosition);
+ dragIntensity = dragDistance/(float)((GetCurrentTime() - swipeTime));
+
+ startMoving = false;
+
+ // Detect GESTURE_SWIPE
+ if ((dragIntensity > FORCE_TO_SWIPE) && (firstTouchId == event.pointerId[0]))
+ {
+ // NOTE: Angle should be inverted in Y
+ dragAngle = 360.0f - Vector2Angle(touchDownPosition, touchUpPosition);
+
+ if ((dragAngle < 30) || (dragAngle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right
+ else if ((dragAngle > 30) && (dragAngle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up
+ else if ((dragAngle > 120) && (dragAngle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left
+ else if ((dragAngle > 210) && (dragAngle < 300)) currentGesture = GESTURE_SWIPE_DOWN; // Down
+ else currentGesture = GESTURE_NONE;
+ }
+ else
+ {
+ dragDistance = 0.0f;
+ dragIntensity = 0.0f;
+ dragAngle = 0.0f;
+
+ currentGesture = GESTURE_NONE;
+ }
+
+ touchDownDragPosition = (Vector2){ 0.0f, 0.0f };
+ pointCount = 0;
+ }
+ else if (event.touchAction == TOUCH_MOVE)
+ {
+ if (currentGesture == GESTURE_DRAG) eventTime = GetCurrentTime();
+
+ if (!startMoving)
+ {
+ swipeTime = GetCurrentTime();
+ startMoving = true;
+ }
+
+ moveDownPosition = event.position[0];
+
+ if (currentGesture == GESTURE_HOLD)
+ {
+ if (resetHold) touchDownPosition = event.position[0];
+
+ resetHold = false;
+
+ // Detect GESTURE_DRAG
+ if (Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_DRAG)
+ {
+ eventTime = GetCurrentTime();
+ currentGesture = GESTURE_DRAG;
+ }
+ }
+
+ dragVector.x = moveDownPosition.x - touchDownDragPosition.x;
+ dragVector.y = moveDownPosition.y - touchDownDragPosition.y;
+ }
+ }
+ else // Two touch points
+ {
+ if (event.touchAction == TOUCH_DOWN)
+ {
+ touchDownPosition = event.position[0];
+ touchDownPosition2 = event.position[1];
+
+ //pinchDistance = Vector2Distance(touchDownPosition, touchDownPosition2);
+
+ pinchVector.x = touchDownPosition2.x - touchDownPosition.x;
+ pinchVector.y = touchDownPosition2.y - touchDownPosition.y;
+
+ currentGesture = GESTURE_HOLD;
+ timeHold = GetCurrentTime();
+ }
+ else if (event.touchAction == TOUCH_MOVE)
+ {
+ pinchDistance = Vector2Distance(moveDownPosition, moveDownPosition2);
+
+ touchDownPosition = moveDownPosition;
+ touchDownPosition2 = moveDownPosition2;
+
+ moveDownPosition = event.position[0];
+ moveDownPosition2 = event.position[1];
+
+ pinchVector.x = moveDownPosition2.x - moveDownPosition.x;
+ pinchVector.y = moveDownPosition2.y - moveDownPosition.y;
+
+ if ((Vector2Distance(touchDownPosition, moveDownPosition) >= MINIMUM_PINCH) || (Vector2Distance(touchDownPosition2, moveDownPosition2) >= MINIMUM_PINCH))
+ {
+ if ((Vector2Distance(moveDownPosition, moveDownPosition2) - pinchDistance) < 0) currentGesture = GESTURE_PINCH_IN;
+ else currentGesture = GESTURE_PINCH_OUT;
+ }
+ else
+ {
+ currentGesture = GESTURE_HOLD;
+ timeHold = GetCurrentTime();
+ }
+
+ // NOTE: Angle should be inverted in Y
+ pinchAngle = 360.0f - Vector2Angle(moveDownPosition, moveDownPosition2);
+ }
+ else if (event.touchAction == TOUCH_UP)
+ {
+ pinchDistance = 0.0f;
+ pinchAngle = 0.0f;
+ pinchVector = (Vector2){ 0.0f, 0.0f };
+ pointCount = 0;
+
+ currentGesture = GESTURE_NONE;
+ }
+ }
+}
+
+// Update gestures detected (must be called every frame)
+void UpdateGestures(void)
+{
+ // NOTE: Gestures are processed through system callbacks on touch events
+
+ // Detect GESTURE_HOLD
+ if (((currentGesture == GESTURE_TAP) || (currentGesture == GESTURE_DOUBLETAP)) && (pointCount < 2))
+ {
+ currentGesture = GESTURE_HOLD;
+ timeHold = GetCurrentTime();
+ }
+
+ if (((GetCurrentTime() - eventTime) > TAP_TIMEOUT) && (currentGesture == GESTURE_DRAG) && (pointCount < 2))
+ {
+ currentGesture = GESTURE_HOLD;
+ timeHold = GetCurrentTime();
+ resetHold = true;
+ }
+
+ // Detect GESTURE_NONE
+ if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN))
+ {
+ currentGesture = GESTURE_NONE;
+ }
+}
+
+// Get number of touch points
+int GetTouchPointsCount(void)
+{
+ // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called
+
+ return pointCount;
+}
+
+// Get latest detected gesture
+int GetGestureDetected(void)
+{
+ // Get current gesture only if enabled
+ return (enabledGestures & currentGesture);
+}
+
+// Hold time measured in ms
+float GetGestureHoldDuration(void)
+{
+ // NOTE: time is calculated on current gesture HOLD
+
+ float time = 0.0f;
+
+ if (currentGesture == GESTURE_HOLD) time = (float)GetCurrentTime() - timeHold;
+
+ return time;
+}
+
+// Get drag vector (between initial touch point to current)
+Vector2 GetGestureDragVector(void)
+{
+ // NOTE: drag vector is calculated on one touch points TOUCH_MOVE
+
+ return dragVector;
+}
+
+// Get drag angle
+// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
+float GetGestureDragAngle(void)
+{
+ // NOTE: drag angle is calculated on one touch points TOUCH_UP
+
+ return dragAngle;
+}
+
+// Get distance between two pinch points
+Vector2 GetGesturePinchVector(void)
+{
+ // NOTE: The position values used for pinchDistance are not modified like the position values of [core.c]-->GetTouchPosition(int index)
+ // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE
+
+ return pinchVector;
+}
+
+// Get angle beween two pinch points
+// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
+float GetGesturePinchAngle(void)
+{
+ // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE
+
+ return pinchAngle;
+}
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+
+// Returns angle from two-points vector with X-axis
+static float Vector2Angle(Vector2 initialPosition, Vector2 finalPosition)
+{
+ float angle;
+
+ angle = atan2(finalPosition.y - initialPosition.y, finalPosition.x - initialPosition.x)*(180.0f/PI);
+
+ if (angle < 0) angle += 360.0f;
+
+ return angle;
+}
+
+// Calculate distance between two Vector2
+static float Vector2Distance(Vector2 v1, Vector2 v2)
+{
+ float result;
+
+ float dx = v2.x - v1.x;
+ float dy = v2.y - v1.y;
+
+ result = sqrt(dx*dx + dy*dy);
+
+ return result;
+}
+
+// Time measure returned are milliseconds
+static double GetCurrentTime(void)
+{
+ double time = 0;
+
+#if defined(_WIN32)
+ unsigned long long int clockFrequency, currentTime;
+
+ QueryPerformanceFrequency(&clockFrequency);
+ QueryPerformanceCounter(&currentTime);
+
+ time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds
+#endif
+
+#if defined(__linux)
+ // NOTE: Only for Linux-based systems
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time in nanoseconds
+
+ time = ((double)nowTime/1000000.0); // Time in miliseconds
+#endif
+
+ return time;
+}
+
+#endif // GESTURES_IMPLEMENTATION
diff --git a/src/models.c b/src/models.c
index b194a0db..822da6e9 100644
--- a/src/models.c
+++ b/src/models.c
@@ -66,7 +66,7 @@ static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize);
//----------------------------------------------------------------------------------
// Draw a line in 3D world space
-void Draw3DLine(Vector3 startPos, Vector3 endPos, Color color)
+void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color)
{
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
@@ -76,19 +76,19 @@ void Draw3DLine(Vector3 startPos, Vector3 endPos, Color color)
}
// Draw a circle in 3D world space
-void Draw3DCircle(Vector3 center, float radius, float rotationAngle, Vector3 rotation, Color color)
+void DrawCircle3D(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);
+ rlVertex3f(sin(DEG2RAD*(i + 10))*radius, cos(DEG2RAD*(i + 10))*radius, 0.0f);
}
rlEnd();
rlPopMatrix();
@@ -270,25 +270,25 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left Of The Texture and Quad
// Back Face
- rlNormal3f( 0.0f, 0.0f,-1.0f); // Normal Pointing Away From Viewer
+ rlNormal3f(0.0f, 0.0f,-1.0f); // Normal Pointing Away From Viewer
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Left Of The Texture and Quad
// Top Face
- rlNormal3f( 0.0f, 1.0f, 0.0f); // Normal Pointing Up
+ rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Bottom Left Of The Texture and Quad
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad
// Bottom Face
- rlNormal3f( 0.0f,-1.0f, 0.0f); // Normal Pointing Down
+ rlNormal3f(0.0f,-1.0f, 0.0f); // Normal Pointing Down
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Left Of The Texture and Quad
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left Of The Texture and Quad
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Right Of The Texture and Quad
// Right face
- rlNormal3f( 1.0f, 0.0f, 0.0f); // Normal Pointing Right
+ rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right
rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right Of The Texture and Quad
rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad
rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left Of The Texture and Quad
@@ -325,25 +325,25 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
{
for (int j = 0; j < slices; j++)
{
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
+ cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)),
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices)));
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i))) * sin(DEG2RAD*((j+1)*360/slices)),
+ cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i)))*sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i))) * cos(DEG2RAD*((j+1)*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i)))*cos(DEG2RAD*((j+1)*360/slices)));
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
}
}
rlEnd();
@@ -364,26 +364,26 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col
{
for (int j = 0; j < slices; j++)
{
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
+ cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*((j+1)*360/slices)),
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*((j+1)*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)),
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices)));
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * sin(DEG2RAD*(j*360/slices)),
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1))) * cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i)) * sin(DEG2RAD*(j*360/slices)),
+ cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i)) * cos(DEG2RAD*(j*360/slices)));
+ cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
}
}
rlEnd();
@@ -407,21 +407,21 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
// Draw Body -------------------------------------------------------------------------------------
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
- rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); //Top Right
+ 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
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop); //Top Right
- rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop); //Top Left
- rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom); //Bottom Left
- rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop); //Top Right
+ rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop); //Top Left
+ rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom); //Bottom Left
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop); //Top Right
}
// Draw Cap --------------------------------------------------------------------------------------
for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, height, 0);
- rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop);
- rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop);
+ rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop);
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop);
}
}
else
@@ -430,8 +430,8 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, height, 0);
- rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom);
- rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom);
+ rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
}
}
@@ -439,8 +439,8 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
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);
- rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom);
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
+ rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
}
rlEnd();
rlPopMatrix();
@@ -460,17 +460,17 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl
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);
+ rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
- rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusBottom, 0, cos(DEG2RAD*(i+360/sides)) * radiusBottom);
- rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop);
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop);
- rlVertex3f(sin(DEG2RAD*(i+360/sides)) * radiusTop, height, cos(DEG2RAD*(i+360/sides)) * radiusTop);
- rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop);
+ rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop);
+ rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop);
- rlVertex3f(sin(DEG2RAD*i) * radiusTop, height, cos(DEG2RAD*i) * radiusTop);
- rlVertex3f(sin(DEG2RAD*i) * radiusBottom, 0, cos(DEG2RAD*i) * radiusBottom);
+ rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop);
+ rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
}
rlEnd();
rlPopMatrix();
@@ -516,7 +516,7 @@ void DrawRay(Ray ray, Color color)
// Draw a grid centered at (0, 0, 0)
void DrawGrid(int slices, float spacing)
{
- int halfSlices = slices / 2;
+ int halfSlices = slices/2;
rlBegin(RL_LINES);
for (int i = -halfSlices; i <= halfSlices; i++)
@@ -577,22 +577,30 @@ void DrawLight(Light light)
{
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));
+ DrawSphereWires(light->position, 0.3f*light->intensity, 8, 8, (light->enabled ? light->diffuse : GRAY));
+
+ DrawCircle3D(light->position, light->radius, 0.0f, (Vector3){ 0, 0, 0 }, (light->enabled ? light->diffuse : GRAY));
+ DrawCircle3D(light->position, light->radius, 90.0f, (Vector3){ 1, 0, 0 }, (light->enabled ? light->diffuse : GRAY));
+ DrawCircle3D(light->position, light->radius, 90.0f, (Vector3){ 0, 1, 0 }, (light->enabled ? light->diffuse : GRAY));
} 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));
+ {
+ DrawLine3D(light->position, light->target, (light->enabled ? light->diffuse : GRAY));
+
+ DrawSphereWires(light->position, 0.3f*light->intensity, 8, 8, (light->enabled ? light->diffuse : GRAY));
+ DrawCubeWires(light->target, 0.3f, 0.3f, 0.3f, (light->enabled ? light->diffuse : GRAY));
} 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));
+ {
+ DrawLine3D(light->position, light->target, (light->enabled ? light->diffuse : GRAY));
+
+ Vector3 dir = VectorSubtract(light->target, light->position);
+ VectorNormalize(&dir);
+
+ DrawCircle3D(light->position, 0.5f, 0.0f, dir, (light->enabled ? light->diffuse : GRAY));
+
+ //DrawCylinderWires(light->position, 0.0f, 0.3f*light->coneAngle/50, 0.6f, 5, (light->enabled ? light->diffuse : GRAY));
+ DrawCubeWires(light->target, 0.3f, 0.3f, 0.3f, (light->enabled ? light->diffuse : GRAY));
} break;
default: break;
}
@@ -602,7 +610,7 @@ void DrawLight(Light light)
Model LoadModel(const char *fileName)
{
Model model = { 0 };
-
+
// TODO: Initialize default data for model in case loading fails, maybe a cube?
if (strcmp(GetExtension(fileName), "obj") == 0) model.mesh = LoadOBJ(fileName);
@@ -612,7 +620,7 @@ Model LoadModel(const char *fileName)
else
{
rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model)
-
+
model.transform = MatrixIdentity();
model.material = LoadDefaultMaterial();
}
@@ -626,12 +634,12 @@ Model LoadModelEx(Mesh data, bool dynamic)
Model model = { 0 };
model.mesh = data;
-
+
rlglLoadMesh(&model.mesh, dynamic); // Upload vertex data to GPU
-
+
model.transform = MatrixIdentity();
model.material = LoadDefaultMaterial();
-
+
return model;
}
@@ -723,11 +731,11 @@ Model LoadModelFromRES(const char *rresName, int resId)
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();
@@ -738,11 +746,11 @@ Model LoadHeightmap(Image heightmap, Vector3 size)
Model LoadCubicmap(Image cubicmap)
{
Model model = { 0 };
-
+
model.mesh = GenMeshCubicmap(cubicmap, (Vector3){ 1.0f, 1.5f, 1.0f });
-
+
rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model)
-
+
model.transform = MatrixIdentity();
model.material = LoadDefaultMaterial();
@@ -755,7 +763,7 @@ void UnloadModel(Model model)
rlglUnloadMesh(&model.mesh);
UnloadMaterial(model.material);
-
+
TraceLog(INFO, "Unloaded model data from RAM and VRAM");
}
@@ -763,10 +771,10 @@ void UnloadModel(Model model)
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;
}
@@ -774,7 +782,7 @@ Material LoadMaterial(const char *fileName)
Material LoadDefaultMaterial(void)
{
Material material = { 0 };
-
+
material.shader = GetDefaultShader();
material.texDiffuse = GetDefaultTexture(); // White texture (1x1 pixel)
//material.texNormal; // NOTE: By default, not set
@@ -783,9 +791,9 @@ Material LoadDefaultMaterial(void)
material.colDiffuse = WHITE; // Diffuse color
material.colAmbient = WHITE; // Ambient color
material.colSpecular = WHITE; // Specular color
-
+
material.glossiness = 100.0f; // Glossiness level
-
+
return material;
}
@@ -794,7 +802,7 @@ Material LoadDefaultMaterial(void)
Material LoadStandardMaterial(void)
{
Material material = LoadDefaultMaterial();
-
+
material.shader = GetStandardShader();
return material;
@@ -812,12 +820,12 @@ void UnloadMaterial(Material material)
static Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
{
#define GRAY_VALUE(c) ((c.r+c.g+c.b)/3)
-
+
Mesh mesh = { 0 };
int mapX = heightmap.width;
int mapZ = heightmap.height;
-
+
Color *pixels = GetImageData(heightmap);
// NOTE: One vertex per pixel
@@ -908,7 +916,7 @@ static Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
trisCounter += 2;
}
}
-
+
free(pixels);
return mesh;
@@ -919,9 +927,9 @@ static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
Mesh mesh = { 0 };
Color *cubicmapPixels = GetImageData(cubicmap);
-
- int mapWidth = cubicmap.width*(int)cubeSize.x;
- int mapHeight = cubicmap.height*(int)cubeSize.z;
+
+ int mapWidth = cubicmap.width;
+ int mapHeight = cubicmap.height;
// NOTE: Max possible number of triangles numCubes * (12 triangles by cube)
int maxTriangles = cubicmap.width*cubicmap.height*12;
@@ -961,19 +969,19 @@ static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
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 += cubeSize.z)
+ for (int z = 0; z < mapHeight; ++z)
{
- for (int x = 0; x < mapWidth; x += cubeSize.x)
+ for (int x = 0; x < mapWidth; ++x)
{
// Define the 8 vertex of the cube, we will combine them accordingly later...
- Vector3 v1 = { x - w/2, h2, z - h/2 };
- Vector3 v2 = { x - w/2, h2, z + h/2 };
- Vector3 v3 = { x + w/2, h2, z + h/2 };
- Vector3 v4 = { x + w/2, h2, z - h/2 };
- Vector3 v5 = { x + w/2, 0, z - h/2 };
- Vector3 v6 = { x - w/2, 0, z - h/2 };
- Vector3 v7 = { x - w/2, 0, z + h/2 };
- Vector3 v8 = { x + w/2, 0, z + h/2 };
+ Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
+ Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) };
+ Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) };
+ Vector3 v4 = { w*(x + 0.5f), h2, h*(z - 0.5f) };
+ Vector3 v5 = { w*(x + 0.5f), 0, h*(z - 0.5f) };
+ Vector3 v6 = { w*(x - 0.5f), 0, h*(z - 0.5f) };
+ Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) };
+ Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) };
// We check pixel color to be WHITE, we will full cubes
if ((cubicmapPixels[z*cubicmap.width + x].r == 255) &&
@@ -1262,9 +1270,9 @@ static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
free(mapVertices);
free(mapNormals);
free(mapTexcoords);
-
+
free(cubicmapPixels); // Free image pixel data
-
+
return mesh;
}
@@ -1273,7 +1281,7 @@ 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);
}
@@ -1285,13 +1293,13 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
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);
}
@@ -1299,9 +1307,9 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
{
rlEnableWireMode();
-
+
DrawModel(model, position, scale, tint);
-
+
rlDisableWireMode();
}
@@ -1309,9 +1317,9 @@ void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
{
rlEnableWireMode();
-
+
DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
-
+
rlDisableWireMode();
}
@@ -1319,7 +1327,7 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
{
Rectangle sourceRec = { 0, 0, texture.width, texture.height };
-
+
DrawBillboardRec(camera, texture, sourceRec, center, size, tint);
}
@@ -1334,7 +1342,7 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 };
//Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 };
-
+
// NOTE: Billboard locked on axis-Y
Vector3 up = { 0.0f, 1.0f, 0.0f };
/*
@@ -1361,19 +1369,19 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
// Bottom-left corner for texture and quad
- rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height);
+ rlTexCoord2f((float)sourceRec.x/texture.width, (float)sourceRec.y/texture.height);
rlVertex3f(a.x, a.y, a.z);
// Top-left corner for texture and quad
- rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
+ rlTexCoord2f((float)sourceRec.x/texture.width, (float)(sourceRec.y + sourceRec.height)/texture.height);
rlVertex3f(d.x, d.y, d.z);
// Top-right corner for texture and quad
- rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
+ rlTexCoord2f((float)(sourceRec.x + sourceRec.width)/texture.width, (float)(sourceRec.y + sourceRec.height)/texture.height);
rlVertex3f(c.x, c.y, c.z);
// Bottom-right corner for texture and quad
- rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
+ rlTexCoord2f((float)(sourceRec.x + sourceRec.width)/texture.width, (float)sourceRec.y/texture.height);
rlVertex3f(b.x, b.y, b.z);
rlEnd();
@@ -1384,13 +1392,13 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
void DrawBoundingBox(BoundingBox box, Color color)
{
Vector3 size;
-
+
size.x = fabsf(box.max.x - box.min.x);
size.y = fabsf(box.max.y - box.min.y);
size.z = fabsf(box.max.z - box.min.z);
-
+
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, color);
}
@@ -1451,14 +1459,14 @@ bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radius
bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius)
{
bool collision = false;
-
+
Vector3 raySpherePos = VectorSubtract(spherePosition, ray.position);
float distance = VectorLength(raySpherePos);
float vector = VectorDotProduct(raySpherePos, ray.direction);
float d = sphereRadius*sphereRadius - (distance*distance - vector*vector);
-
+
if (d >= 0.0f) collision = true;
-
+
return collision;
}
@@ -1466,29 +1474,29 @@ bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius
bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint)
{
bool collision = false;
-
+
Vector3 raySpherePos = VectorSubtract(spherePosition, ray.position);
float distance = VectorLength(raySpherePos);
float vector = VectorDotProduct(raySpherePos, ray.direction);
float d = sphereRadius*sphereRadius - (distance*distance - vector*vector);
-
+
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);
else collisionDistance = vector - sqrt(d);
-
+
VectorScale(&offset, collisionDistance);
Vector3 cPoint = VectorAdd(ray.position, offset);
-
+
collisionPoint->x = cPoint.x;
collisionPoint->y = cPoint.y;
collisionPoint->z = cPoint.z;
-
+
return collision;
}
@@ -1496,7 +1504,7 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi
bool CheckCollisionRayBox(Ray ray, BoundingBox box)
{
bool collision = false;
-
+
float t[8];
t[0] = (box.min.x - ray.position.x)/ray.direction.x;
t[1] = (box.max.x - ray.position.x)/ray.direction.x;
@@ -1506,9 +1514,9 @@ bool CheckCollisionRayBox(Ray ray, BoundingBox box)
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]));
-
+
collision = !(t[7] < 0 || t[6] > t[7]);
-
+
return collision;
}
@@ -1524,270 +1532,20 @@ BoundingBox CalculateBoundingBox(Mesh mesh)
{
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
BoundingBox box;
box.min = minVertex;
box.max = maxVertex;
-
- return box;
-}
-
-// Detect and resolve cubicmap collisions
-// NOTE: player position (or camera) is modified inside this function
-// TODO: This functions needs to be completely reviewed!
-Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius)
-{
- #define CUBIC_MAP_HALF_BLOCK_SIZE 0.5
-
- Color *cubicmapPixels = GetImageData(cubicmap);
-
- // Detect the cell where the player is located
- Vector3 impactDirection = { 0.0f, 0.0f, 0.0f };
-
- int locationCellX = 0;
- int locationCellY = 0;
-
- locationCellX = floor(playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE);
- locationCellY = floor(playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE);
-
- if ((locationCellX >= 0) && (locationCellY >= 0) && (locationCellX < cubicmap.width) && (locationCellY < cubicmap.height))
- {
- // Multiple Axis --------------------------------------------------------------------------------------------
-
- // Axis x-, y-
- if ((locationCellX > 0) && (locationCellY > 0))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX - 1)].r != 0) &&
- (cubicmapPixels[(locationCellY - 1)*cubicmap.width + (locationCellX)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
- {
- playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
-
- // Axis x-, y+
- if ((locationCellX > 0) && (locationCellY < cubicmap.height - 1))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX - 1)].r != 0) &&
- (cubicmapPixels[(locationCellY + 1)*cubicmap.width + (locationCellX)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
- {
- playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
- // Axis x+, y-
- if ((locationCellX < cubicmap.width - 1) && (locationCellY > 0))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX + 1)].r != 0) &&
- (cubicmapPixels[(locationCellY - 1)*cubicmap.width + (locationCellX)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
- {
- playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
-
- // Axis x+, y+
- if ((locationCellX < cubicmap.width - 1) && (locationCellY < cubicmap.height - 1))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX + 1)].r != 0) &&
- (cubicmapPixels[(locationCellY + 1)*cubicmap.width + (locationCellX)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
- {
- playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
-
- // Single Axis ---------------------------------------------------------------------------------------------------
-
- // Axis x-
- if (locationCellX > 0)
- {
- if (cubicmapPixels[locationCellY*cubicmap.width + (locationCellX - 1)].r != 0)
- {
- if ((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius)
- {
- playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 1.0f, 0.0f, 0.0f };
- }
- }
- }
- // Axis x+
- if (locationCellX < cubicmap.width - 1)
- {
- if (cubicmapPixels[locationCellY*cubicmap.width + (locationCellX + 1)].r != 0)
- {
- if ((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius)
- {
- playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 1.0f, 0.0f, 0.0f };
- }
- }
- }
- // Axis y-
- if (locationCellY > 0)
- {
- if (cubicmapPixels[(locationCellY - 1)*cubicmap.width + (locationCellX)].r != 0)
- {
- if ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius)
- {
- playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 0.0f, 0.0f, 1.0f };
- }
- }
- }
- // Axis y+
- if (locationCellY < cubicmap.height - 1)
- {
- if (cubicmapPixels[(locationCellY + 1)*cubicmap.width + (locationCellX)].r != 0)
- {
- if ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius)
- {
- playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- impactDirection = (Vector3){ 0.0f, 0.0f, 1.0f };
- }
- }
- }
-
- // Diagonals -------------------------------------------------------------------------------------------------------
-
- // Axis x-, y-
- if ((locationCellX > 0) && (locationCellY > 0))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX - 1)].r == 0) &&
- (cubicmapPixels[(locationCellY - 1)*cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmapPixels[(locationCellY - 1)*cubicmap.width + (locationCellX - 1)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- else playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
-
- // Return ricochet
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3))
- {
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
- }
-
- // Axis x-, y+
- if ((locationCellX > 0) && (locationCellY < cubicmap.height - 1))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX - 1)].r == 0) &&
- (cubicmapPixels[(locationCellY + 1)*cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmapPixels[(locationCellY + 1)*cubicmap.width + (locationCellX - 1)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) > (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + mapPosition.x - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- else playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
-
- // Return ricochet
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX < radius / 3) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3))
- {
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
- }
-
- // Axis x+, y-
- if ((locationCellX < cubicmap.width - 1) && (locationCellY > 0))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX + 1)].r == 0) &&
- (cubicmapPixels[(locationCellY - 1)*cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmapPixels[(locationCellY - 1)*cubicmap.width + (locationCellX + 1)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < (1 - ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY))) playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- else playerPosition->z = locationCellY + mapPosition.z - (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
-
- // Return ricochet
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY < radius / 3))
- {
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
- }
-
- // Axis x+, y+
- if ((locationCellX < cubicmap.width - 1) && (locationCellY < cubicmap.height - 1))
- {
- if ((cubicmapPixels[locationCellY*cubicmap.width + (locationCellX + 1)].r == 0) &&
- (cubicmapPixels[(locationCellY + 1)*cubicmap.width + (locationCellX)].r == 0) &&
- (cubicmapPixels[(locationCellY + 1)*cubicmap.width + (locationCellX + 1)].r != 0))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius))
- {
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX) < ((playerPosition->z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY)) playerPosition->x = locationCellX + mapPosition.x + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
- else playerPosition->z = locationCellY + mapPosition.z + (CUBIC_MAP_HALF_BLOCK_SIZE - radius);
-
- // Return ricochet
- if (((playerPosition->x - mapPosition.x + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellX > 1 - radius / 3) &&
- ((playerPosition->z - mapPosition.z + CUBIC_MAP_HALF_BLOCK_SIZE) - locationCellY > 1 - radius / 3))
- {
- impactDirection = (Vector3){ 1.0f, 0.0f, 1.0f };
- }
- }
- }
- }
- }
-
- // Floor collision
- if (playerPosition->y <= radius)
- {
- playerPosition->y = radius + 0.01f;
- impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z};
- }
- // Roof collision
- else if (playerPosition->y >= (1.5f - radius))
- {
- playerPosition->y = (1.5f - radius) - 0.01f;
- impactDirection = (Vector3) { impactDirection.x, 1, impactDirection.z};
- }
-
- free(cubicmapPixels);
-
- return impactDirection;
+ return box;
}
//----------------------------------------------------------------------------------
@@ -1898,23 +1656,21 @@ static Mesh LoadOBJ(const char *fileName)
if (dataType == 't') // Read texCoord
{
- float useless = 0;
-
- fscanf(objFile, "%f %f %f", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y, &useless);
+ fscanf(objFile, "%f %f%*[^\n]s\n", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y);
countTexCoords++;
fscanf(objFile, "%c", &dataType);
}
else if (dataType == 'n') // Read normals
{
- fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z );
+ fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z);
countNormals++;
fscanf(objFile, "%c", &dataType);
}
else // Read vertex
{
- fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z );
+ fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z);
countVertex++;
fscanf(objFile, "%c", &dataType);
@@ -1959,6 +1715,7 @@ static Mesh LoadOBJ(const char *fileName)
if ((numNormals == 0) && (numTexCoords == 0)) fscanf(objFile, "%i %i %i", &vNum[0], &vNum[1], &vNum[2]);
else if (numNormals == 0) fscanf(objFile, "%i/%i %i/%i %i/%i", &vNum[0], &vtNum[0], &vNum[1], &vtNum[1], &vNum[2], &vtNum[2]);
+ else if (numTexCoords == 0) fscanf(objFile, "%i//%i %i//%i %i//%i", &vNum[0], &vnNum[0], &vNum[1], &vnNum[1], &vNum[2], &vnNum[2]);
else fscanf(objFile, "%i/%i/%i %i/%i/%i %i/%i/%i", &vNum[0], &vtNum[0], &vnNum[0], &vNum[1], &vtNum[1], &vnNum[1], &vNum[2], &vtNum[2], &vnNum[2]);
mesh.vertices[vCounter] = midVertices[vNum[0]-1].x;
@@ -2049,9 +1806,9 @@ static Mesh LoadOBJ(const char *fileName)
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;
@@ -2069,14 +1826,14 @@ static Material LoadMTL(const char *fileName)
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
@@ -2123,7 +1880,7 @@ static Material LoadMTL(const char *fileName)
{
int shininess = 0;
sscanf(buffer, "Ns %i", &shininess);
-
+
material.glossiness = (float)shininess;
}
else if (buffer[1] == 'i') // Ni int Refraction index.
@@ -2192,7 +1949,7 @@ static Material LoadMTL(const char *fileName)
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;
@@ -2203,6 +1960,6 @@ static Material LoadMTL(const char *fileName)
// NOTE: At this point we have all material data
TraceLog(INFO, "[%s] Material loaded successfully", fileName);
-
+
return material;
}
diff --git a/src/raygui.h b/src/raygui.h
deleted file mode 100644
index 42cf264b..00000000
--- a/src/raygui.h
+++ /dev/null
@@ -1,1499 +0,0 @@
-/*******************************************************************************************
-*
-* raygui 1.0 - IMGUI (Immedite Mode GUI) library for raylib (https://github.com/raysan5/raylib)
-*
-* raygui is a library for creating simple IMGUI interfaces using raylib.
-* It provides a set of basic components:
-*
-* - Label
-* - Button
-* - ToggleButton
-* - ToggleGroup
-* - ComboBox
-* - CheckBox
-* - Slider
-* - SliderBar
-* - ProgressBar
-* - Spinner
-* - TextBox
-*
-* It also provides a set of functions for styling the components based on its properties (size, color).
-*
-* CONFIGURATION:
-*
-* #define RAYGUI_IMPLEMENTATION
-* Generates the implementation of the library into the included file.
-* If not defined, the library is in header only mode and can be included in other headers
-* or source files without problems. But only ONE file should hold the implementation.
-*
-* #define RAYGUI_STATIC (defined by default)
-* The generated implementation will stay private inside implementation file and all
-* internal symbols and functions will only be visible inside that file.
-*
-* #define RAYGUI_STANDALONE
-* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
-* internally in the library and input management and drawing functions must be provided by
-* the user (check library implementation for further details).
-*
-* #define RAYGUI_MALLOC()
-* #define RAYGUI_FREE()
-* You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions.
-* Otherwise it will include stdlib.h and use the C standard library malloc()/free() function.
-*
-* LIMITATIONS:
-*
-* // TODO.
-*
-* VERSIONS:
-*
-* 1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
-* 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
-* 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
-*
-* CONTRIBUTORS:
-* Ramon Santamaria: Functions design and naming conventions.
-* Kevin Gato: Initial implementation of basic components.
-* Daniel Nicolas: Initial implementation of basic components.
-* Albert Martos: Review and testing of library.
-* Ian Eito: Review and testing of the library.
-* Sergio Martinez: Review and testing of the library.
-*
-* LICENSE: zlib/libpng
-*
-* Copyright (c) 2015-2016 emegeme (@emegemegames)
-*
-* 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 RAYGUI_H
-#define RAYGUI_H
-
-#if !defined(RAYGUI_STANDALONE)
- #include "raylib.h"
-#endif
-
-#define RAYGUI_STATIC
-#ifdef RAYGUI_STATIC
- #define RAYGUIDEF static // Functions just visible to module including this file
-#else
- #ifdef __cplusplus
- #define RAYGUIDEF extern "C" // Functions visible from other files (no name mangling of functions in C++)
- #else
- #define RAYGUIDEF extern // Functions visible from other files
- #endif
-#endif
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-#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,
- GLOBAL_TEXT_COLOR,
- GLOBAL_TEXT_FONTSIZE,
- GLOBAL_BORDER_WIDTH,
- BACKGROUND_COLOR,
- LABEL_BORDER_WIDTH,
- LABEL_TEXT_COLOR,
- LABEL_TEXT_PADDING,
- BUTTON_BORDER_WIDTH,
- BUTTON_TEXT_PADDING,
- BUTTON_DEFAULT_BORDER_COLOR,
- BUTTON_DEFAULT_INSIDE_COLOR,
- BUTTON_DEFAULT_TEXT_COLOR,
- BUTTON_HOVER_BORDER_COLOR,
- BUTTON_HOVER_INSIDE_COLOR,
- BUTTON_HOVER_TEXT_COLOR,
- BUTTON_PRESSED_BORDER_COLOR,
- BUTTON_PRESSED_INSIDE_COLOR,
- BUTTON_PRESSED_TEXT_COLOR,
- TOGGLE_TEXT_PADDING,
- TOGGLE_BORDER_WIDTH,
- TOGGLE_DEFAULT_BORDER_COLOR,
- TOGGLE_DEFAULT_INSIDE_COLOR,
- TOGGLE_DEFAULT_TEXT_COLOR,
- TOGGLE_HOVER_BORDER_COLOR,
- TOGGLE_HOVER_INSIDE_COLOR,
- TOGGLE_HOVER_TEXT_COLOR,
- TOGGLE_PRESSED_BORDER_COLOR,
- TOGGLE_PRESSED_INSIDE_COLOR,
- TOGGLE_PRESSED_TEXT_COLOR,
- TOGGLE_ACTIVE_BORDER_COLOR,
- TOGGLE_ACTIVE_INSIDE_COLOR,
- TOGGLE_ACTIVE_TEXT_COLOR,
- TOGGLEGROUP_PADDING,
- SLIDER_BORDER_WIDTH,
- SLIDER_BUTTON_BORDER_WIDTH,
- SLIDER_BORDER_COLOR,
- SLIDER_INSIDE_COLOR,
- SLIDER_DEFAULT_COLOR,
- SLIDER_HOVER_COLOR,
- SLIDER_ACTIVE_COLOR,
- SLIDERBAR_BORDER_COLOR,
- SLIDERBAR_INSIDE_COLOR,
- SLIDERBAR_DEFAULT_COLOR,
- SLIDERBAR_HOVER_COLOR,
- SLIDERBAR_ACTIVE_COLOR,
- SLIDERBAR_ZERO_LINE_COLOR,
- PROGRESSBAR_BORDER_COLOR,
- PROGRESSBAR_INSIDE_COLOR,
- PROGRESSBAR_PROGRESS_COLOR,
- PROGRESSBAR_BORDER_WIDTH,
- SPINNER_LABEL_BORDER_COLOR,
- SPINNER_LABEL_INSIDE_COLOR,
- SPINNER_DEFAULT_BUTTON_BORDER_COLOR,
- SPINNER_DEFAULT_BUTTON_INSIDE_COLOR,
- SPINNER_DEFAULT_SYMBOL_COLOR,
- SPINNER_DEFAULT_TEXT_COLOR,
- SPINNER_HOVER_BUTTON_BORDER_COLOR,
- SPINNER_HOVER_BUTTON_INSIDE_COLOR,
- SPINNER_HOVER_SYMBOL_COLOR,
- SPINNER_HOVER_TEXT_COLOR,
- SPINNER_PRESSED_BUTTON_BORDER_COLOR,
- SPINNER_PRESSED_BUTTON_INSIDE_COLOR,
- SPINNER_PRESSED_SYMBOL_COLOR,
- SPINNER_PRESSED_TEXT_COLOR,
- COMBOBOX_PADDING,
- COMBOBOX_BUTTON_WIDTH,
- COMBOBOX_BUTTON_HEIGHT,
- COMBOBOX_BORDER_WIDTH,
- COMBOBOX_DEFAULT_BORDER_COLOR,
- COMBOBOX_DEFAULT_INSIDE_COLOR,
- COMBOBOX_DEFAULT_TEXT_COLOR,
- COMBOBOX_DEFAULT_LIST_TEXT_COLOR,
- COMBOBOX_HOVER_BORDER_COLOR,
- COMBOBOX_HOVER_INSIDE_COLOR,
- COMBOBOX_HOVER_TEXT_COLOR,
- COMBOBOX_HOVER_LIST_TEXT_COLOR,
- COMBOBOX_PRESSED_BORDER_COLOR,
- COMBOBOX_PRESSED_INSIDE_COLOR,
- COMBOBOX_PRESSED_TEXT_COLOR,
- COMBOBOX_PRESSED_LIST_BORDER_COLOR,
- COMBOBOX_PRESSED_LIST_INSIDE_COLOR,
- COMBOBOX_PRESSED_LIST_TEXT_COLOR,
- CHECKBOX_DEFAULT_BORDER_COLOR,
- CHECKBOX_DEFAULT_INSIDE_COLOR,
- CHECKBOX_HOVER_BORDER_COLOR,
- CHECKBOX_HOVER_INSIDE_COLOR,
- CHECKBOX_CLICK_BORDER_COLOR,
- CHECKBOX_CLICK_INSIDE_COLOR,
- CHECKBOX_STATUS_ACTIVE_COLOR,
- CHECKBOX_INSIDE_WIDTH,
- TEXTBOX_BORDER_WIDTH,
- TEXTBOX_BORDER_COLOR,
- TEXTBOX_INSIDE_COLOR,
- TEXTBOX_TEXT_COLOR,
- TEXTBOX_LINE_COLOR,
- TEXTBOX_TEXT_FONTSIZE
-} GuiProperty;
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-// ...
-
-//----------------------------------------------------------------------------------
-// Module Functions Declaration
-//----------------------------------------------------------------------------------
-RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text); // Label element, show text
-RAYGUIDEF void GuiLabelEx(Rectangle bounds, const char *text, Color textColor, Color border, Color inner); // Label element extended, configurable colors
-RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text); // Button element, returns true when clicked
-RAYGUIDEF bool GuiToggleButton(Rectangle bounds, const char *text, bool toggle); // Toggle Button element, returns true when active
-RAYGUIDEF int GuiToggleGroup(Rectangle bounds, int toggleNum, char **toggleText, int toggleActive); // Toggle Group element, returns toggled button index
-RAYGUIDEF int GuiComboBox(Rectangle bounds, int comboNum, char **comboText, int comboActive); // Combo Box element, returns selected item index
-RAYGUIDEF bool GuiCheckBox(Rectangle bounds, const char *text, bool checked); // Check Box element, returns true when active
-RAYGUIDEF float GuiSlider(Rectangle bounds, float value, float minValue, float maxValue); // Slider element, returns selected value
-RAYGUIDEF float GuiSliderBar(Rectangle bounds, float value, float minValue, float maxValue); // Slider Bar element, returns selected value
-RAYGUIDEF void GuiProgressBar(Rectangle bounds, float value); // Progress Bar element, shows current progress value
-RAYGUIDEF int GuiSpinner(Rectangle bounds, int value, int minValue, int maxValue); // Spinner element, returns selected value
-RAYGUIDEF char *GuiTextBox(Rectangle bounds, char *text); // Text Box element, returns input text
-
-RAYGUIDEF void SaveGuiStyle(const char *fileName); // Save GUI style file
-RAYGUIDEF void LoadGuiStyle(const char *fileName); // Load GUI style file
-
-RAYGUIDEF void SetStyleProperty(int guiProperty, int value); // Set one style property
-RAYGUIDEF int GetStyleProperty(int guiProperty); // Get one style property
-
-#endif // RAYGUI_H
-
-
-/***********************************************************************************
-*
-* RAYGUI IMPLEMENTATION
-*
-************************************************************************************/
-
-#if defined(RAYGUI_IMPLEMENTATION)
-
-#include <stdio.h> // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf()
- // NOTE: Those functions are only used in SaveGuiStyle() and LoadGuiStyle()
-
-// Check if custom malloc/free functions defined, if not, using standard ones
-#if !defined(RAYGUI_MALLOC)
- #include <stdlib.h> // Required for: malloc(), free() [Used only on LoadGuiStyle()]
-
- #define RAYGUI_MALLOC(size) malloc(size)
- #define RAYGUI_FREE(ptr) free(ptr)
-#endif
-
-#include <string.h> // Required for: strcmp() [Used only on LoadGuiStyle()]
-#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
-//----------------------------------------------------------------------------------
-
-// GUI elements states
-typedef enum { BUTTON_DEFAULT, BUTTON_HOVER, BUTTON_PRESSED, BUTTON_CLICKED } ButtonState;
-typedef enum { TOGGLE_UNACTIVE, TOGGLE_HOVER, TOGGLE_PRESSED, TOGGLE_ACTIVE } ToggleState;
-typedef enum { COMBOBOX_UNACTIVE, COMBOBOX_HOVER, COMBOBOX_PRESSED, COMBOBOX_ACTIVE } ComboBoxState;
-typedef enum { SPINNER_DEFAULT, SPINNER_HOVER, SPINNER_PRESSED } SpinnerState;
-typedef enum { CHECKBOX_STATUS, CHECKBOX_HOVER, CHECKBOX_PRESSED } CheckBoxState;
-typedef enum { SLIDER_DEFAULT, SLIDER_HOVER, SLIDER_ACTIVE } SliderState;
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-
-// Current GUI style (default light)
-static int style[NUM_PROPERTIES] = {
- 0xf5f5f5ff, // GLOBAL_BASE_COLOR,
- 0xf5f5f5ff, // GLOBAL_BORDER_COLOR,
- 0xf5f5f5ff, // GLOBAL_TEXT_COLOR,
- 10, // GLOBAL_TEXT_FONTSIZE
- 1, // GLOBAL_BORDER_WIDTH
- 0xf5f5f5ff, // BACKGROUND_COLOR
- 1, // LABEL_BORDER_WIDTH
- 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
- 0x4d4d4dff, // BUTTON_DEFAULT_TEXT_COLOR
- 0xc8c8c8ff, // BUTTON_HOVER_BORDER_COLOR
- 0xffffffff, // BUTTON_HOVER_INSIDE_COLOR
- 0x353535ff, // BUTTON_HOVER_TEXT_COLOR
- 0x7bb0d6ff, // BUTTON_PRESSED_BORDER_COLOR
- 0xbcecffff, // BUTTON_PRESSED_INSIDE_COLOR
- 0x5f9aa7ff, // BUTTON_PRESSED_TEXT_COLOR
- 20, // TOGGLE_TEXT_PADDING
- 1, // TOGGLE_BORDER_WIDTH
- 0x828282ff, // TOGGLE_DEFAULT_BORDER_COLOR
- 0xc8c8c8ff, // TOGGLE_DEFAULT_INSIDE_COLOR
- 0x828282ff, // TOGGLE_DEFAULT_TEXT_COLOR
- 0xc8c8c8ff, // TOGGLE_HOVER_BORDER_COLOR
- 0xffffffff, // TOGGLE_HOVER_INSIDE_COLOR
- 0x828282ff, // TOGGLE_HOVER_TEXT_COLOR
- 0xbdd7eaff, // TOGGLE_PRESSED_BORDER_COLOR
- 0xddf5ffff, // TOGGLE_PRESSED_INSIDE_COLOR
- 0xafccd3ff, // TOGGLE_PRESSED_TEXT_COLOR
- 0x7bb0d6ff, // TOGGLE_ACTIVE_BORDER_COLOR
- 0xbcecffff, // TOGGLE_ACTIVE_INSIDE_COLOR
- 0x5f9aa7ff, // TOGGLE_ACTIVE_TEXT_COLOR
- 3, // TOGGLEGROUP_PADDING
- 1, // SLIDER_BORDER_WIDTH
- 1, // SLIDER_BUTTON_BORDER_WIDTH
- 0x828282ff, // SLIDER_BORDER_COLOR
- 0xc8c8c8ff, // SLIDER_INSIDE_COLOR
- 0xbcecffff, // SLIDER_DEFAULT_COLOR
- 0xffffffff, // SLIDER_HOVER_COLOR
- 0xddf5ffff, // SLIDER_ACTIVE_COLOR
- 0x828282ff, // SLIDERBAR_BORDER_COLOR
- 0xc8c8c8ff, // SLIDERBAR_INSIDE_COLOR
- 0xbcecffff, // SLIDERBAR_DEFAULT_COLOR
- 0xffffffff, // SLIDERBAR_HOVER_COLOR
- 0xddf5ffff, // SLIDERBAR_ACTIVE_COLOR
- 0x828282ff, // SLIDERBAR_ZERO_LINE_COLOR
- 0x828282ff, // PROGRESSBAR_BORDER_COLOR
- 0xc8c8c8ff, // PROGRESSBAR_INSIDE_COLOR
- 0xbcecffff, // PROGRESSBAR_PROGRESS_COLOR
- 2, // PROGRESSBAR_BORDER_WIDTH
- 0x828282ff, // SPINNER_LABEL_BORDER_COLOR
- 0xc8c8c8ff, // SPINNER_LABEL_INSIDE_COLOR
- 0x828282ff, // SPINNER_DEFAULT_BUTTON_BORDER_COLOR
- 0xc8c8c8ff, // SPINNER_DEFAULT_BUTTON_INSIDE_COLOR
- 0x000000ff, // SPINNER_DEFAULT_SYMBOL_COLOR
- 0x000000ff, // SPINNER_DEFAULT_TEXT_COLOR
- 0xc8c8c8ff, // SPINNER_HOVER_BUTTON_BORDER_COLOR
- 0xffffffff, // SPINNER_HOVER_BUTTON_INSIDE_COLOR
- 0x000000ff, // SPINNER_HOVER_SYMBOL_COLOR
- 0x000000ff, // SPINNER_HOVER_TEXT_COLOR
- 0x7bb0d6ff, // SPINNER_PRESSED_BUTTON_BORDER_COLOR
- 0xbcecffff, // SPINNER_PRESSED_BUTTON_INSIDE_COLOR
- 0x5f9aa7ff, // SPINNER_PRESSED_SYMBOL_COLOR
- 0x000000ff, // SPINNER_PRESSED_TEXT_COLOR
- 1, // COMBOBOX_PADDING
- 30, // COMBOBOX_BUTTON_WIDTH
- 20, // COMBOBOX_BUTTON_HEIGHT
- 1, // COMBOBOX_BORDER_WIDTH
- 0x828282ff, // COMBOBOX_DEFAULT_BORDER_COLOR
- 0xc8c8c8ff, // COMBOBOX_DEFAULT_INSIDE_COLOR
- 0x828282ff, // COMBOBOX_DEFAULT_TEXT_COLOR
- 0x828282ff, // COMBOBOX_DEFAULT_LIST_TEXT_COLOR
- 0xc8c8c8ff, // COMBOBOX_HOVER_BORDER_COLOR
- 0xffffffff, // COMBOBOX_HOVER_INSIDE_COLOR
- 0x828282ff, // COMBOBOX_HOVER_TEXT_COLOR
- 0x828282ff, // COMBOBOX_HOVER_LIST_TEXT_COLOR
- 0x7bb0d6ff, // COMBOBOX_PRESSED_BORDER_COLOR
- 0xbcecffff, // COMBOBOX_PRESSED_INSIDE_COLOR
- 0x5f9aa7ff, // COMBOBOX_PRESSED_TEXT_COLOR
- 0x0078acff, // COMBOBOX_PRESSED_LIST_BORDER_COLOR
- 0x66e7ffff, // COMBOBOX_PRESSED_LIST_INSIDE_COLOR
- 0x0078acff, // COMBOBOX_PRESSED_LIST_TEXT_COLOR
- 0x828282ff, // CHECKBOX_DEFAULT_BORDER_COLOR
- 0xffffffff, // CHECKBOX_DEFAULT_INSIDE_COLOR
- 0xc8c8c8ff, // CHECKBOX_HOVER_BORDER_COLOR
- 0xffffffff, // CHECKBOX_HOVER_INSIDE_COLOR
- 0x66e7ffff, // CHECKBOX_CLICK_BORDER_COLOR
- 0xddf5ffff, // CHECKBOX_CLICK_INSIDE_COLOR
- 0x7bb0d6ff, // CHECKBOX_STATUS_ACTIVE_COLOR
- 4, // CHECKBOX_INSIDE_WIDTH
- 1, // TEXTBOX_BORDER_WIDTH
- 0x828282ff, // TEXTBOX_BORDER_COLOR
- 0xf5f5f5ff, // TEXTBOX_INSIDE_COLOR
- 0x000000ff, // TEXTBOX_TEXT_COLOR
- 0x000000ff, // TEXTBOX_LINE_COLOR
- 10 // TEXTBOX_TEXT_FONTSIZE
-};
-
-// GUI property names (to read/write style text files)
-static const char *guiPropertyName[] = {
- "GLOBAL_BASE_COLOR",
- "GLOBAL_BORDER_COLOR",
- "GLOBAL_TEXT_COLOR",
- "GLOBAL_TEXT_FONTSIZE",
- "GLOBAL_BORDER_WIDTH",
- "BACKGROUND_COLOR",
- "LABEL_BORDER_WIDTH",
- "LABEL_TEXT_COLOR",
- "LABEL_TEXT_PADDING",
- "BUTTON_BORDER_WIDTH",
- "BUTTON_TEXT_PADDING",
- "BUTTON_DEFAULT_BORDER_COLOR",
- "BUTTON_DEFAULT_INSIDE_COLOR",
- "BUTTON_DEFAULT_TEXT_COLOR",
- "BUTTON_HOVER_BORDER_COLOR",
- "BUTTON_HOVER_INSIDE_COLOR",
- "BUTTON_HOVER_TEXT_COLOR",
- "BUTTON_PRESSED_BORDER_COLOR",
- "BUTTON_PRESSED_INSIDE_COLOR",
- "BUTTON_PRESSED_TEXT_COLOR",
- "TOGGLE_TEXT_PADDING",
- "TOGGLE_BORDER_WIDTH",
- "TOGGLE_DEFAULT_BORDER_COLOR",
- "TOGGLE_DEFAULT_INSIDE_COLOR",
- "TOGGLE_DEFAULT_TEXT_COLOR",
- "TOGGLE_HOVER_BORDER_COLOR",
- "TOGGLE_HOVER_INSIDE_COLOR",
- "TOGGLE_HOVER_TEXT_COLOR",
- "TOGGLE_PRESSED_BORDER_COLOR",
- "TOGGLE_PRESSED_INSIDE_COLOR",
- "TOGGLE_PRESSED_TEXT_COLOR",
- "TOGGLE_ACTIVE_BORDER_COLOR",
- "TOGGLE_ACTIVE_INSIDE_COLOR",
- "TOGGLE_ACTIVE_TEXT_COLOR",
- "TOGGLEGROUP_PADDING",
- "SLIDER_BORDER_WIDTH",
- "SLIDER_BUTTON_BORDER_WIDTH",
- "SLIDER_BORDER_COLOR",
- "SLIDER_INSIDE_COLOR",
- "SLIDER_DEFAULT_COLOR",
- "SLIDER_HOVER_COLOR",
- "SLIDER_ACTIVE_COLOR",
- "SLIDERBAR_BORDER_COLOR",
- "SLIDERBAR_INSIDE_COLOR",
- "SLIDERBAR_DEFAULT_COLOR",
- "SLIDERBAR_HOVER_COLOR",
- "SLIDERBAR_ACTIVE_COLOR",
- "SLIDERBAR_ZERO_LINE_COLOR",
- "PROGRESSBAR_BORDER_COLOR",
- "PROGRESSBAR_INSIDE_COLOR",
- "PROGRESSBAR_PROGRESS_COLOR",
- "PROGRESSBAR_BORDER_WIDTH",
- "SPINNER_LABEL_BORDER_COLOR",
- "SPINNER_LABEL_INSIDE_COLOR",
- "SPINNER_DEFAULT_BUTTON_BORDER_COLOR",
- "SPINNER_DEFAULT_BUTTON_INSIDE_COLOR",
- "SPINNER_DEFAULT_SYMBOL_COLOR",
- "SPINNER_DEFAULT_TEXT_COLOR",
- "SPINNER_HOVER_BUTTON_BORDER_COLOR",
- "SPINNER_HOVER_BUTTON_INSIDE_COLOR",
- "SPINNER_HOVER_SYMBOL_COLOR",
- "SPINNER_HOVER_TEXT_COLOR",
- "SPINNER_PRESSED_BUTTON_BORDER_COLOR",
- "SPINNER_PRESSED_BUTTON_INSIDE_COLOR",
- "SPINNER_PRESSED_SYMBOL_COLOR",
- "SPINNER_PRESSED_TEXT_COLOR",
- "COMBOBOX_PADDING",
- "COMBOBOX_BUTTON_WIDTH",
- "COMBOBOX_BUTTON_HEIGHT",
- "COMBOBOX_BORDER_WIDTH",
- "COMBOBOX_DEFAULT_BORDER_COLOR",
- "COMBOBOX_DEFAULT_INSIDE_COLOR",
- "COMBOBOX_DEFAULT_TEXT_COLOR",
- "COMBOBOX_DEFAULT_LIST_TEXT_COLOR",
- "COMBOBOX_HOVER_BORDER_COLOR",
- "COMBOBOX_HOVER_INSIDE_COLOR",
- "COMBOBOX_HOVER_TEXT_COLOR",
- "COMBOBOX_HOVER_LIST_TEXT_COLOR",
- "COMBOBOX_PRESSED_BORDER_COLOR",
- "COMBOBOX_PRESSED_INSIDE_COLOR",
- "COMBOBOX_PRESSED_TEXT_COLOR",
- "COMBOBOX_PRESSED_LIST_BORDER_COLOR",
- "COMBOBOX_PRESSED_LIST_INSIDE_COLOR",
- "COMBOBOX_PRESSED_LIST_TEXT_COLOR",
- "CHECKBOX_DEFAULT_BORDER_COLOR",
- "CHECKBOX_DEFAULT_INSIDE_COLOR",
- "CHECKBOX_HOVER_BORDER_COLOR",
- "CHECKBOX_HOVER_INSIDE_COLOR",
- "CHECKBOX_CLICK_BORDER_COLOR",
- "CHECKBOX_CLICK_INSIDE_COLOR",
- "CHECKBOX_STATUS_ACTIVE_COLOR",
- "CHECKBOX_INSIDE_WIDTH",
- "TEXTBOX_BORDER_WIDTH",
- "TEXTBOX_BORDER_COLOR",
- "TEXTBOX_INSIDE_COLOR",
- "TEXTBOX_TEXT_COLOR",
- "TEXTBOX_LINE_COLOR",
- "TEXTBOX_TEXT_FONTSIZE"
-};
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Declaration
-//----------------------------------------------------------------------------------
-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: To use raygui as standalone library, those functions must be overwrite by custom ones
-
-// Input management 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()
-
-// Drawing related functions
-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
-//----------------------------------------------------------------------------------
-
-// Label element, show text
-RAYGUIDEF void GuiLabel(Rectangle bounds, const char *text)
-{
- #define BLANK (Color){ 0, 0, 0, 0 } // Blank (Transparent)
-
- GuiLabelEx(bounds, text, GetColor(style[LABEL_TEXT_COLOR]), BLANK, BLANK);
-}
-
-// Label element extended, configurable colors
-RAYGUIDEF void GuiLabelEx(Rectangle bounds, const char *text, Color textColor, Color border, Color inner)
-{
- // Update control
- //--------------------------------------------------------------------
- int textWidth = MeasureText(text, style[GLOBAL_TEXT_FONTSIZE]);
- 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;
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- DrawRectangleRec(bounds, border);
- DrawRectangle(bounds.x + style[LABEL_BORDER_WIDTH], bounds.y + style[LABEL_BORDER_WIDTH], bounds.width - (2 * style[LABEL_BORDER_WIDTH]), bounds.height - (2 * style[LABEL_BORDER_WIDTH]), inner);
- DrawText(text, bounds.x + ((bounds.width/2) - (textWidth/2)), bounds.y + ((bounds.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], textColor);
- //--------------------------------------------------------------------
-}
-
-// Button element, returns true when clicked
-RAYGUIDEF bool GuiButton(Rectangle bounds, const char *text)
-{
- ButtonState buttonState = BUTTON_DEFAULT;
- Vector2 mousePoint = GetMousePosition();
-
- int textWidth = MeasureText(text, style[GLOBAL_TEXT_FONTSIZE]);
- int textHeight = style[GLOBAL_TEXT_FONTSIZE];
-
- // Update control
- //--------------------------------------------------------------------
- if (bounds.width < textWidth) bounds.width = textWidth + style[BUTTON_TEXT_PADDING];
- if (bounds.height < textHeight) bounds.height = textHeight + style[BUTTON_TEXT_PADDING]/2;
-
- if (CheckCollisionPointRec(mousePoint, bounds))
- {
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) buttonState = BUTTON_PRESSED;
- else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) buttonState = BUTTON_CLICKED;
- else buttonState = BUTTON_HOVER;
- }
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- switch (buttonState)
- {
- case BUTTON_DEFAULT:
- {
- DrawRectangleRec(bounds, GetColor(style[BUTTON_DEFAULT_BORDER_COLOR]));
- DrawRectangle((int)(bounds.x + style[BUTTON_BORDER_WIDTH]), (int)(bounds.y + style[BUTTON_BORDER_WIDTH]) , (int)(bounds.width - (2 * style[BUTTON_BORDER_WIDTH])), (int)(bounds.height - (2 * style[BUTTON_BORDER_WIDTH])), GetColor(style[BUTTON_DEFAULT_INSIDE_COLOR]));
- DrawText(text, bounds.x + ((bounds.width/2) - (MeasureText(text, style[GLOBAL_TEXT_FONTSIZE])/2)), bounds.y + ((bounds.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[BUTTON_DEFAULT_TEXT_COLOR]));
- } break;
- case BUTTON_HOVER:
- {
- DrawRectangleRec(bounds, GetColor(style[BUTTON_HOVER_BORDER_COLOR]));
- DrawRectangle((int)(bounds.x + style[BUTTON_BORDER_WIDTH]), (int)(bounds.y + style[BUTTON_BORDER_WIDTH]) , (int)(bounds.width - (2 * style[BUTTON_BORDER_WIDTH])), (int)(bounds.height - (2 * style[BUTTON_BORDER_WIDTH])), GetColor(style[BUTTON_HOVER_INSIDE_COLOR]));
- DrawText(text, bounds.x + ((bounds.width/2) - (MeasureText(text, style[GLOBAL_TEXT_FONTSIZE])/2)), bounds.y + ((bounds.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[BUTTON_HOVER_TEXT_COLOR]));
- } break;
- case BUTTON_PRESSED:
- {
- DrawRectangleRec(bounds, GetColor(style[BUTTON_PRESSED_BORDER_COLOR]));
- DrawRectangle((int)(bounds.x + style[BUTTON_BORDER_WIDTH]), (int)(bounds.y + style[BUTTON_BORDER_WIDTH]) , (int)(bounds.width - (2 * style[BUTTON_BORDER_WIDTH])), (int)(bounds.height - (2 * style[BUTTON_BORDER_WIDTH])), GetColor(style[BUTTON_PRESSED_INSIDE_COLOR]));
- DrawText(text, bounds.x + ((bounds.width/2) - (MeasureText(text, style[GLOBAL_TEXT_FONTSIZE])/2)), bounds.y + ((bounds.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[BUTTON_PRESSED_TEXT_COLOR]));
- } break;
- case BUTTON_CLICKED:
- {
- DrawRectangleRec(bounds, GetColor(style[BUTTON_PRESSED_BORDER_COLOR]));
- DrawRectangle((int)(bounds.x + style[BUTTON_BORDER_WIDTH]), (int)(bounds.y + style[BUTTON_BORDER_WIDTH]) , (int)(bounds.width - (2 * style[BUTTON_BORDER_WIDTH])), (int)(bounds.height - (2 * style[BUTTON_BORDER_WIDTH])), GetColor(style[BUTTON_PRESSED_INSIDE_COLOR]));
- } break;
- default: break;
- }
- //------------------------------------------------------------------
-
- if (buttonState == BUTTON_CLICKED) return true;
- else return false;
-}
-
-// Toggle Button element, returns true when active
-RAYGUIDEF bool GuiToggleButton(Rectangle bounds, const char *text, bool toggle)
-{
- ToggleState toggleState = TOGGLE_UNACTIVE;
- Rectangle toggleButton = bounds;
- Vector2 mousePoint = GetMousePosition();
-
- int textWidth = MeasureText(text, style[GLOBAL_TEXT_FONTSIZE]);
- 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))
- {
- if (toggle)
- {
- toggle = false;
- toggleState = TOGGLE_UNACTIVE;
- }
- else
- {
- toggle = true;
- toggleState = TOGGLE_ACTIVE;
- }
- }
- else toggleState = TOGGLE_HOVER;
- }
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- switch (toggleState)
- {
- case TOGGLE_UNACTIVE:
- {
- DrawRectangleRec(toggleButton, GetColor(style[TOGGLE_DEFAULT_BORDER_COLOR]));
- DrawRectangle((int)(toggleButton.x + style[TOGGLE_BORDER_WIDTH]), (int)(toggleButton.y + style[TOGGLE_BORDER_WIDTH]) , (int)(toggleButton.width - (2 * style[TOGGLE_BORDER_WIDTH])), (int)(toggleButton.height - (2 * style[TOGGLE_BORDER_WIDTH])), GetColor(style[TOGGLE_DEFAULT_INSIDE_COLOR]));
- DrawText(text, toggleButton.x + ((toggleButton.width/2) - (MeasureText(text, style[GLOBAL_TEXT_FONTSIZE])/2)), toggleButton.y + ((toggleButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[TOGGLE_DEFAULT_TEXT_COLOR]));
- } break;
- case TOGGLE_HOVER:
- {
- DrawRectangleRec(toggleButton, GetColor(style[TOGGLE_HOVER_BORDER_COLOR]));
- DrawRectangle((int)(toggleButton.x + style[TOGGLE_BORDER_WIDTH]), (int)(toggleButton.y + style[TOGGLE_BORDER_WIDTH]) , (int)(toggleButton.width - (2 * style[TOGGLE_BORDER_WIDTH])), (int)(toggleButton.height - (2 * style[TOGGLE_BORDER_WIDTH])), GetColor(style[TOGGLE_HOVER_INSIDE_COLOR]));
- DrawText(text, toggleButton.x + ((toggleButton.width/2) - (MeasureText(text, style[GLOBAL_TEXT_FONTSIZE])/2)), toggleButton.y + ((toggleButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[TOGGLE_HOVER_TEXT_COLOR]));
- } break;
- case TOGGLE_PRESSED:
- {
- DrawRectangleRec(toggleButton, GetColor(style[TOGGLE_PRESSED_BORDER_COLOR]));
- DrawRectangle((int)(toggleButton.x + style[TOGGLE_BORDER_WIDTH]), (int)(toggleButton.y + style[TOGGLE_BORDER_WIDTH]) , (int)(toggleButton.width - (2 * style[TOGGLE_BORDER_WIDTH])), (int)(toggleButton.height - (2 * style[TOGGLE_BORDER_WIDTH])), GetColor(style[TOGGLE_PRESSED_INSIDE_COLOR]));
- DrawText(text, toggleButton.x + ((toggleButton.width/2) - (MeasureText(text, style[GLOBAL_TEXT_FONTSIZE])/2)), toggleButton.y + ((toggleButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[TOGGLE_PRESSED_TEXT_COLOR]));
- } break;
- case TOGGLE_ACTIVE:
- {
- DrawRectangleRec(toggleButton, GetColor(style[TOGGLE_ACTIVE_BORDER_COLOR]));
- DrawRectangle((int)(toggleButton.x + style[TOGGLE_BORDER_WIDTH]), (int)(toggleButton.y + style[TOGGLE_BORDER_WIDTH]) , (int)(toggleButton.width - (2 * style[TOGGLE_BORDER_WIDTH])), (int)(toggleButton.height - (2 * style[TOGGLE_BORDER_WIDTH])), GetColor(style[TOGGLE_ACTIVE_INSIDE_COLOR]));
- DrawText(text, toggleButton.x + ((toggleButton.width/2) - (MeasureText(text, style[GLOBAL_TEXT_FONTSIZE])/2)), toggleButton.y + ((toggleButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[TOGGLE_ACTIVE_TEXT_COLOR]));
- } break;
- default: break;
- }
- //--------------------------------------------------------------------
-
- return toggle;
-}
-
-// Toggle Group element, returns toggled button index
-RAYGUIDEF int GuiToggleGroup(Rectangle bounds, int toggleNum, char **toggleText, int toggleActive)
-{
- for (int i = 0; i < toggleNum; i++)
- {
- if (i == toggleActive) GuiToggleButton((Rectangle){bounds.x + i*(bounds.width + style[TOGGLEGROUP_PADDING]),bounds.y,bounds.width,bounds.height}, toggleText[i], true);
- else if (GuiToggleButton((Rectangle){bounds.x + i*(bounds.width + style[TOGGLEGROUP_PADDING]),bounds.y,bounds.width,bounds.height}, toggleText[i], false) == true) toggleActive = i;
- }
-
- return toggleActive;
-}
-
-// Combo Box element, returns selected item index
-RAYGUIDEF int GuiComboBox(Rectangle bounds, int comboNum, char **comboText, int comboActive)
-{
- ComboBoxState comboBoxState = COMBOBOX_UNACTIVE;
- Rectangle comboBoxButton = bounds;
- Rectangle click = { bounds.x + bounds.width + style[COMBOBOX_PADDING], bounds.y, style[COMBOBOX_BUTTON_WIDTH], style[COMBOBOX_BUTTON_HEIGHT] };
- Vector2 mousePoint = GetMousePosition();
-
- int textHeight = style[GLOBAL_TEXT_FONTSIZE];
-
- for (int i = 0; i < comboNum; i++)
- {
- if (i == comboActive)
- {
- // Update control
- //--------------------------------------------------------------------
- int textWidth = MeasureText(comboText[i], style[GLOBAL_TEXT_FONTSIZE]);
-
- if (comboBoxButton.width < textWidth) comboBoxButton.width = textWidth + style[TOGGLE_TEXT_PADDING];
- if (comboBoxButton.height < textHeight) comboBoxButton.height = textHeight + style[TOGGLE_TEXT_PADDING]/2;
-
- if (CheckCollisionPointRec(mousePoint, comboBoxButton) || CheckCollisionPointRec(mousePoint, click))
- {
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) comboBoxState = COMBOBOX_PRESSED;
- else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) comboBoxState = COMBOBOX_ACTIVE;
- else comboBoxState = COMBOBOX_HOVER;
- }
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- switch (comboBoxState)
- {
- case COMBOBOX_UNACTIVE:
- {
- DrawRectangleRec(comboBoxButton, GetColor(style[COMBOBOX_DEFAULT_BORDER_COLOR]));
- DrawRectangle((int)(comboBoxButton.x + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.y + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.width - (2 * style[COMBOBOX_BORDER_WIDTH])), (int)(comboBoxButton.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_DEFAULT_INSIDE_COLOR]));
-
- DrawRectangleRec(click, GetColor(style[COMBOBOX_DEFAULT_BORDER_COLOR]));
- DrawRectangle((int)(click.x + style[COMBOBOX_BORDER_WIDTH]), (int)(click.y + style[COMBOBOX_BORDER_WIDTH]) , (int)(click.width - (2*style[COMBOBOX_BORDER_WIDTH])), (int)(click.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_DEFAULT_INSIDE_COLOR]));
- DrawText(FormatText("%i/%i", comboActive + 1, comboNum), click.x + ((click.width/2) - (MeasureText(FormatText("%i/%i", comboActive + 1, comboNum), style[GLOBAL_TEXT_FONTSIZE])/2)), click.y + ((click.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_DEFAULT_LIST_TEXT_COLOR]));
- DrawText(comboText[i], comboBoxButton.x + ((comboBoxButton.width/2) - (MeasureText(comboText[i], style[GLOBAL_TEXT_FONTSIZE])/2)), comboBoxButton.y + ((comboBoxButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_DEFAULT_TEXT_COLOR]));
- } break;
- case COMBOBOX_HOVER:
- {
- DrawRectangleRec(comboBoxButton, GetColor(style[COMBOBOX_HOVER_BORDER_COLOR]));
- DrawRectangle((int)(comboBoxButton.x + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.y + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.width - (2 * style[COMBOBOX_BORDER_WIDTH])), (int)(comboBoxButton.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_HOVER_INSIDE_COLOR]));
-
- DrawRectangleRec(click, GetColor(style[COMBOBOX_HOVER_BORDER_COLOR]));
- DrawRectangle((int)(click.x + style[COMBOBOX_BORDER_WIDTH]), (int)(click.y + style[COMBOBOX_BORDER_WIDTH]) , (int)(click.width - (2*style[COMBOBOX_BORDER_WIDTH])), (int)(click.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_HOVER_INSIDE_COLOR]));
- DrawText(FormatText("%i/%i", comboActive + 1, comboNum), click.x + ((click.width/2) - (MeasureText(FormatText("%i/%i", comboActive + 1, comboNum), style[GLOBAL_TEXT_FONTSIZE])/2)), click.y + ((click.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_HOVER_LIST_TEXT_COLOR]));
- DrawText(comboText[i], comboBoxButton.x + ((comboBoxButton.width/2) - (MeasureText(comboText[i], style[GLOBAL_TEXT_FONTSIZE])/2)), comboBoxButton.y + ((comboBoxButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_HOVER_TEXT_COLOR]));
- } break;
- case COMBOBOX_PRESSED:
- {
- DrawRectangleRec(comboBoxButton, GetColor(style[COMBOBOX_PRESSED_BORDER_COLOR]));
- DrawRectangle((int)(comboBoxButton.x + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.y + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.width - (2 * style[COMBOBOX_BORDER_WIDTH])), (int)(comboBoxButton.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_PRESSED_INSIDE_COLOR]));
-
- DrawRectangleRec(click, GetColor(style[COMBOBOX_PRESSED_LIST_BORDER_COLOR]));
- DrawRectangle((int)(click.x + style[COMBOBOX_BORDER_WIDTH]), (int)(click.y + style[COMBOBOX_BORDER_WIDTH]) , (int)(click.width - (2*style[COMBOBOX_BORDER_WIDTH])), (int)(click.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_PRESSED_LIST_INSIDE_COLOR]));
- DrawText(FormatText("%i/%i", comboActive + 1, comboNum), click.x + ((click.width/2) - (MeasureText(FormatText("%i/%i", comboActive + 1, comboNum), style[GLOBAL_TEXT_FONTSIZE])/2)), click.y + ((click.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_PRESSED_LIST_TEXT_COLOR]));
- DrawText(comboText[i], comboBoxButton.x + ((comboBoxButton.width/2) - (MeasureText(comboText[i], style[GLOBAL_TEXT_FONTSIZE])/2)), comboBoxButton.y + ((comboBoxButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_PRESSED_TEXT_COLOR]));
- } break;
- case COMBOBOX_ACTIVE:
- {
- DrawRectangleRec(comboBoxButton, GetColor(style[COMBOBOX_PRESSED_BORDER_COLOR]));
- DrawRectangle((int)(comboBoxButton.x + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.y + style[COMBOBOX_BORDER_WIDTH]), (int)(comboBoxButton.width - (2 * style[COMBOBOX_BORDER_WIDTH])), (int)(comboBoxButton.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_PRESSED_INSIDE_COLOR]));
-
- DrawRectangleRec(click, GetColor(style[COMBOBOX_PRESSED_LIST_BORDER_COLOR]));
- DrawRectangle((int)(click.x + style[COMBOBOX_BORDER_WIDTH]), (int)(click.y + style[COMBOBOX_BORDER_WIDTH]) , (int)(click.width - (2*style[COMBOBOX_BORDER_WIDTH])), (int)(click.height - (2*style[COMBOBOX_BORDER_WIDTH])), GetColor(style[COMBOBOX_PRESSED_LIST_INSIDE_COLOR]));
- DrawText(FormatText("%i/%i", comboActive + 1, comboNum), click.x + ((click.width/2) - (MeasureText(FormatText("%i/%i", comboActive + 1, comboNum), style[GLOBAL_TEXT_FONTSIZE])/2)), click.y + ((click.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_PRESSED_LIST_TEXT_COLOR]));
- DrawText(comboText[i], comboBoxButton.x + ((comboBoxButton.width/2) - (MeasureText(comboText[i], style[GLOBAL_TEXT_FONTSIZE])/2)), comboBoxButton.y + ((comboBoxButton.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[COMBOBOX_PRESSED_TEXT_COLOR]));
- } break;
- default: break;
- }
-
- //DrawText(comboText[i], comboBoxButton.x + ((comboBoxButton.width/2) - (MeasureText(comboText[i], style[]globalTextFontSize)/2)), comboBoxButton.y + ((comboBoxButton.height/2) - (style[]globalTextFontSize/2)), style[]globalTextFontSize, COMBOBOX_PRESSED_TEXT_COLOR);
- //--------------------------------------------------------------------
- }
- }
-
- if (CheckCollisionPointRec(GetMousePosition(), bounds) || CheckCollisionPointRec(GetMousePosition(), click))
- {
- if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
- {
- comboActive += 1;
- if(comboActive >= comboNum) comboActive = 0;
- }
- }
-
- return comboActive;
-}
-
-// Check Box element, returns true when active
-RAYGUIDEF bool GuiCheckBox(Rectangle checkBoxBounds, const char *text, bool checked)
-{
- CheckBoxState checkBoxState = CHECKBOX_STATUS;
- Vector2 mousePoint = GetMousePosition();
-
- // Update control
- //--------------------------------------------------------------------
- if (CheckCollisionPointRec(mousePoint, checkBoxBounds))
- {
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) checkBoxState = CHECKBOX_PRESSED;
- else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
- {
- checkBoxState = CHECKBOX_STATUS;
- checked = !checked;
- }
- else checkBoxState = CHECKBOX_HOVER;
- }
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- switch (checkBoxState)
- {
- case CHECKBOX_HOVER:
- {
- DrawRectangleRec(checkBoxBounds, GetColor(style[CHECKBOX_HOVER_BORDER_COLOR]));
- DrawRectangle((int)(checkBoxBounds.x + style[TOGGLE_BORDER_WIDTH]), (int)(checkBoxBounds.y + style[TOGGLE_BORDER_WIDTH]), (int)(checkBoxBounds.width - (2*style[TOGGLE_BORDER_WIDTH])), (int)(checkBoxBounds.height - (2*style[TOGGLE_BORDER_WIDTH])), GetColor(style[CHECKBOX_HOVER_INSIDE_COLOR]));
- } break;
- case CHECKBOX_STATUS:
- {
- DrawRectangleRec(checkBoxBounds, GetColor(style[CHECKBOX_DEFAULT_BORDER_COLOR]));
- DrawRectangle((int)(checkBoxBounds.x + style[TOGGLE_BORDER_WIDTH]), (int)(checkBoxBounds.y + style[TOGGLE_BORDER_WIDTH]), (int)(checkBoxBounds.width - (2*style[TOGGLE_BORDER_WIDTH])), (int)(checkBoxBounds.height - (2*style[TOGGLE_BORDER_WIDTH])), GetColor(style[CHECKBOX_DEFAULT_INSIDE_COLOR]));
- } break;
- case CHECKBOX_PRESSED:
- {
- DrawRectangleRec(checkBoxBounds, GetColor(style[CHECKBOX_CLICK_BORDER_COLOR]));
- DrawRectangle((int)(checkBoxBounds.x + style[TOGGLE_BORDER_WIDTH]), (int)(checkBoxBounds.y + style[TOGGLE_BORDER_WIDTH]), (int)(checkBoxBounds.width - (2*style[TOGGLE_BORDER_WIDTH])), (int)(checkBoxBounds.height - (2*style[TOGGLE_BORDER_WIDTH])), GetColor(style[CHECKBOX_CLICK_INSIDE_COLOR]));
- } break;
- default: break;
- }
-
- if (text != NULL) DrawText(text, checkBoxBounds.x + checkBoxBounds.width + 2, checkBoxBounds.y + ((checkBoxBounds.height/2) - (style[GLOBAL_TEXT_FONTSIZE]/2) + 1), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[LABEL_TEXT_COLOR]));
-
- if (checked)
- {
- DrawRectangle((int)(checkBoxBounds.x + style[CHECKBOX_INSIDE_WIDTH]), (int)(checkBoxBounds.y + style[CHECKBOX_INSIDE_WIDTH]), (int)(checkBoxBounds.width - (2*style[CHECKBOX_INSIDE_WIDTH])), (int)(checkBoxBounds.height - (2*style[CHECKBOX_INSIDE_WIDTH])), GetColor(style[CHECKBOX_STATUS_ACTIVE_COLOR]));
- }
- //--------------------------------------------------------------------
-
- return checked;
-}
-
-// Slider element, returns selected value
-RAYGUIDEF float GuiSlider(Rectangle bounds, float value, float minValue, float maxValue)
-{
- SliderState sliderState = SLIDER_DEFAULT;
- float buttonTravelDistance = 0;
- float sliderPos = 0;
- Vector2 mousePoint = GetMousePosition();
-
- // Update control
- //--------------------------------------------------------------------
- if (value < minValue) value = minValue;
- else if (value >= maxValue) value = maxValue;
-
- sliderPos = (value - minValue)/(maxValue - minValue);
-
- Rectangle sliderButton;
- sliderButton.width = ((int)(bounds.width - (2 * style[SLIDER_BUTTON_BORDER_WIDTH]))/10 - 8);
- sliderButton.height =((int)(bounds.height - ( 2 * style[SLIDER_BORDER_WIDTH] + 2 * style[SLIDER_BUTTON_BORDER_WIDTH])));
-
- float sliderButtonMinPos = bounds.x + style[SLIDER_BORDER_WIDTH] + style[SLIDER_BUTTON_BORDER_WIDTH];
- float sliderButtonMaxPos = bounds.x + bounds.width - (style[SLIDER_BORDER_WIDTH] + style[SLIDER_BUTTON_BORDER_WIDTH] + sliderButton.width);
-
- buttonTravelDistance = sliderButtonMaxPos - sliderButtonMinPos;
-
- sliderButton.x = ((int)(bounds.x + style[SLIDER_BORDER_WIDTH] + style[SLIDER_BUTTON_BORDER_WIDTH]) + (sliderPos * buttonTravelDistance));
- sliderButton.y = ((int)(bounds.y + style[SLIDER_BORDER_WIDTH] + style[SLIDER_BUTTON_BORDER_WIDTH]));
-
- if (CheckCollisionPointRec(mousePoint, bounds))
- {
- sliderState = SLIDER_HOVER;
-
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) sliderState = SLIDER_ACTIVE;
-
- if ((sliderState == SLIDER_ACTIVE) && (IsMouseButtonDown(MOUSE_LEFT_BUTTON)))
- {
- sliderButton.x = mousePoint.x - sliderButton.width / 2;
-
- if (sliderButton.x <= sliderButtonMinPos) sliderButton.x = sliderButtonMinPos;
- else if (sliderButton.x >= sliderButtonMaxPos) sliderButton.x = sliderButtonMaxPos;
-
- sliderPos = (sliderButton.x - sliderButtonMinPos) / buttonTravelDistance;
- }
- }
- else sliderState = SLIDER_DEFAULT;
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- DrawRectangleRec(bounds, GetColor(style[SLIDER_BORDER_COLOR]));
- DrawRectangle((int)(bounds.x + style[SLIDER_BORDER_WIDTH]), (int)(bounds.y + style[SLIDER_BORDER_WIDTH]), (int)(bounds.width - (2*style[SLIDER_BORDER_WIDTH])), (int)(bounds.height - (2*style[SLIDER_BORDER_WIDTH])), GetColor(style[SLIDER_INSIDE_COLOR]));
-
- switch (sliderState)
- {
- case SLIDER_DEFAULT: DrawRectangleRec(sliderButton, GetColor(style[SLIDER_DEFAULT_COLOR])); break;
- case SLIDER_HOVER: DrawRectangleRec(sliderButton, GetColor(style[SLIDER_HOVER_COLOR])); break;
- case SLIDER_ACTIVE: DrawRectangleRec(sliderButton, GetColor(style[SLIDER_ACTIVE_COLOR])); break;
- default: break;
- }
- //--------------------------------------------------------------------
-
- return minValue + (maxValue - minValue)*sliderPos;
-}
-
-// Slider Bar element, returns selected value
-RAYGUIDEF float GuiSliderBar(Rectangle bounds, float value, float minValue, float maxValue)
-{
- SliderState sliderState = SLIDER_DEFAULT;
- Vector2 mousePoint = GetMousePosition();
- float fixedValue;
- float fixedMinValue;
-
- fixedValue = value - minValue;
- maxValue = maxValue - minValue;
- fixedMinValue = 0;
-
- // Update control
- //--------------------------------------------------------------------
- if (fixedValue <= fixedMinValue) fixedValue = fixedMinValue;
- else if (fixedValue >= maxValue) fixedValue = maxValue;
-
- Rectangle sliderBar;
-
- sliderBar.x = bounds.x + style[SLIDER_BORDER_WIDTH];
- sliderBar.y = bounds.y + style[SLIDER_BORDER_WIDTH];
- sliderBar.width = ((fixedValue*((float)bounds.width - 2*style[SLIDER_BORDER_WIDTH]))/(maxValue - fixedMinValue));
- sliderBar.height = bounds.height - 2*style[SLIDER_BORDER_WIDTH];
-
- if (CheckCollisionPointRec(mousePoint, bounds))
- {
- sliderState = SLIDER_HOVER;
-
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
- {
- sliderState = SLIDER_ACTIVE;
-
- sliderBar.width = (mousePoint.x - bounds.x - style[SLIDER_BORDER_WIDTH]);
-
- if (mousePoint.x <= (bounds.x + style[SLIDER_BORDER_WIDTH])) sliderBar.width = 0;
- else if (mousePoint.x >= (bounds.x + bounds.width - style[SLIDER_BORDER_WIDTH])) sliderBar.width = bounds.width - 2*style[SLIDER_BORDER_WIDTH];
- }
- }
- else sliderState = SLIDER_DEFAULT;
-
- fixedValue = ((float)sliderBar.width*(maxValue - fixedMinValue))/((float)bounds.width - 2*style[SLIDER_BORDER_WIDTH]);
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- DrawRectangleRec(bounds, GetColor(style[SLIDERBAR_BORDER_COLOR]));
- DrawRectangle((int)(bounds.x + style[SLIDER_BORDER_WIDTH]), (int)(bounds.y + style[SLIDER_BORDER_WIDTH]) , (int)(bounds.width - (2*style[SLIDER_BORDER_WIDTH])), (int)(bounds.height - (2*style[SLIDER_BORDER_WIDTH])), GetColor(style[SLIDERBAR_INSIDE_COLOR]));
-
- switch (sliderState)
- {
- case SLIDER_DEFAULT: DrawRectangleRec(sliderBar, GetColor(style[SLIDERBAR_DEFAULT_COLOR])); break;
- case SLIDER_HOVER: DrawRectangleRec(sliderBar, GetColor(style[SLIDERBAR_HOVER_COLOR])); break;
- case SLIDER_ACTIVE: DrawRectangleRec(sliderBar, GetColor(style[SLIDERBAR_ACTIVE_COLOR])); break;
- default: break;
- }
-
- if (minValue < 0 && maxValue > 0) DrawRectangle((bounds.x + style[SLIDER_BORDER_WIDTH]) - (minValue * ((bounds.width - (style[SLIDER_BORDER_WIDTH]*2))/maxValue)), sliderBar.y, 1, sliderBar.height, GetColor(style[SLIDERBAR_ZERO_LINE_COLOR]));
- //--------------------------------------------------------------------
-
- return fixedValue + minValue;
-}
-
-// Progress Bar element, shows current progress value
-RAYGUIDEF void GuiProgressBar(Rectangle bounds, float value)
-{
- if (value > 1.0f) value = 1.0f;
- else if (value < 0.0f) value = 0.0f;
-
- Rectangle progressBar = { bounds.x + style[PROGRESSBAR_BORDER_WIDTH], bounds.y + style[PROGRESSBAR_BORDER_WIDTH], bounds.width - (style[PROGRESSBAR_BORDER_WIDTH] * 2), bounds.height - (style[PROGRESSBAR_BORDER_WIDTH] * 2)};
- Rectangle progressValue = { bounds.x + style[PROGRESSBAR_BORDER_WIDTH], bounds.y + style[PROGRESSBAR_BORDER_WIDTH], value * (bounds.width - (style[PROGRESSBAR_BORDER_WIDTH] * 2)), bounds.height - (style[PROGRESSBAR_BORDER_WIDTH] * 2)};
-
- // Draw control
- //--------------------------------------------------------------------
- DrawRectangleRec(bounds, GetColor(style[PROGRESSBAR_BORDER_COLOR]));
- DrawRectangleRec(progressBar, GetColor(style[PROGRESSBAR_INSIDE_COLOR]));
- DrawRectangleRec(progressValue, GetColor(style[PROGRESSBAR_PROGRESS_COLOR]));
- //--------------------------------------------------------------------
-}
-
-// Spinner element, returns selected value
-// NOTE: Requires static variables: framesCounter, valueSpeed - ERROR!
-RAYGUIDEF int GuiSpinner(Rectangle bounds, int value, int minValue, int maxValue)
-{
- SpinnerState spinnerState = SPINNER_DEFAULT;
- Rectangle labelBoxBound = { bounds.x + bounds.width/4 + 1, bounds.y, bounds.width/2, bounds.height };
- Rectangle leftButtonBound = { bounds.x, bounds.y, bounds.width/4, bounds.height };
- Rectangle rightButtonBound = { bounds.x + bounds.width - bounds.width/4 + 1, bounds.y, bounds.width/4, bounds.height };
- Vector2 mousePoint = GetMousePosition();
-
- int textWidth = MeasureText(FormatText("%i", value), style[GLOBAL_TEXT_FONTSIZE]);
- //int textHeight = style[GLOBAL_TEXT_FONTSIZE]; // Unused variable
-
- int buttonSide = 0;
-
- static int framesCounter = 0;
- static bool valueSpeed = false;;
-
- //if (comboBoxButton.width < textWidth) comboBoxButton.width = textWidth + style[TOGGLE_TEXT_PADDING];
- //if (comboBoxButton.height < textHeight) comboBoxButton.height = textHeight + style[TOGGLE_TEXT_PADDING]/2;
-
- // Update control
- //--------------------------------------------------------------------
- if (CheckCollisionPointRec(mousePoint, leftButtonBound) || CheckCollisionPointRec(mousePoint, rightButtonBound) || CheckCollisionPointRec(mousePoint, labelBoxBound))
- {
- if (IsKeyDown(KEY_LEFT))
- {
- spinnerState = SPINNER_PRESSED;
- buttonSide = 1;
-
- if (value > minValue) value -= 1;
- }
- else if (IsKeyDown(KEY_RIGHT))
- {
- spinnerState = SPINNER_PRESSED;
- buttonSide = 2;
-
- if (value < maxValue) value += 1;
- }
- }
-
- if (CheckCollisionPointRec(mousePoint, leftButtonBound))
- {
- buttonSide = 1;
- spinnerState = SPINNER_HOVER;
-
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
- {
- if (!valueSpeed)
- {
- if (value > minValue) value--;
- valueSpeed = true;
- }
- else framesCounter++;
-
- spinnerState = SPINNER_PRESSED;
-
- if (value > minValue)
- {
- if (framesCounter >= 30) value -= 1;
- }
- }
- }
- else if (CheckCollisionPointRec(mousePoint, rightButtonBound))
- {
- buttonSide = 2;
- spinnerState = SPINNER_HOVER;
-
- if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
- {
- if (!valueSpeed)
- {
- if (value < maxValue) value++;
- valueSpeed = true;
- }
- else framesCounter++;
-
- spinnerState = SPINNER_PRESSED;
-
- if (value < maxValue)
- {
- if (framesCounter >= 30) value += 1;
- }
- }
- }
- else if (!CheckCollisionPointRec(mousePoint, labelBoxBound)) buttonSide = 0;
-
- if (IsMouseButtonUp(MOUSE_LEFT_BUTTON))
- {
- valueSpeed = false;
- framesCounter = 0;
- }
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- switch (spinnerState)
- {
- case SPINNER_DEFAULT:
- {
- DrawRectangleRec(leftButtonBound, GetColor(style[SPINNER_DEFAULT_BUTTON_BORDER_COLOR]));
- DrawRectangle(leftButtonBound.x + 2, leftButtonBound.y + 2, leftButtonBound.width - 4, leftButtonBound.height - 4, GetColor(style[SPINNER_DEFAULT_BUTTON_INSIDE_COLOR]));
-
- DrawRectangleRec(rightButtonBound, GetColor(style[SPINNER_DEFAULT_BUTTON_BORDER_COLOR]));
- DrawRectangle(rightButtonBound.x + 2, rightButtonBound.y + 2, rightButtonBound.width - 4, rightButtonBound.height - 4, GetColor(style[SPINNER_DEFAULT_BUTTON_INSIDE_COLOR]));
-
- DrawText(FormatText("-"), leftButtonBound.x + (leftButtonBound.width/2 - (MeasureText(FormatText("+"), style[GLOBAL_TEXT_FONTSIZE]))/2), leftButtonBound.y + (leftButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_DEFAULT_SYMBOL_COLOR]));
- DrawText(FormatText("+"), rightButtonBound.x + (rightButtonBound.width/2 - (MeasureText(FormatText("-"), style[GLOBAL_TEXT_FONTSIZE]))/2), rightButtonBound.y + (rightButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_DEFAULT_SYMBOL_COLOR]));
-
- DrawRectangleRec(labelBoxBound, GetColor(style[SPINNER_LABEL_BORDER_COLOR]));
- DrawRectangle(labelBoxBound.x + 1, labelBoxBound.y + 1, labelBoxBound.width - 2, labelBoxBound.height - 2, GetColor(style[SPINNER_LABEL_INSIDE_COLOR]));
-
- DrawText(FormatText("%i", value), labelBoxBound.x + (labelBoxBound.width/2 - textWidth/2), labelBoxBound.y + (labelBoxBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_DEFAULT_TEXT_COLOR]));
- } break;
- case SPINNER_HOVER:
- {
- if (buttonSide == 1)
- {
- DrawRectangleRec(leftButtonBound, GetColor(style[SPINNER_HOVER_BUTTON_BORDER_COLOR]));
- DrawRectangle(leftButtonBound.x + 2, leftButtonBound.y + 2, leftButtonBound.width - 4, leftButtonBound.height - 4, GetColor(style[SPINNER_HOVER_BUTTON_INSIDE_COLOR]));
-
- DrawRectangleRec(rightButtonBound, GetColor(style[SPINNER_DEFAULT_BUTTON_BORDER_COLOR]));
- DrawRectangle(rightButtonBound.x + 2, rightButtonBound.y + 2, rightButtonBound.width - 4, rightButtonBound.height - 4, GetColor(style[SPINNER_DEFAULT_BUTTON_INSIDE_COLOR]));
-
- DrawText(FormatText("-"), leftButtonBound.x + (leftButtonBound.width/2 - (MeasureText(FormatText("+"), style[GLOBAL_TEXT_FONTSIZE]))/2), leftButtonBound.y + (leftButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_HOVER_SYMBOL_COLOR]));
- DrawText(FormatText("+"), rightButtonBound.x + (rightButtonBound.width/2 - (MeasureText(FormatText("-"), style[GLOBAL_TEXT_FONTSIZE]))/2), rightButtonBound.y + (rightButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_DEFAULT_SYMBOL_COLOR]));
- }
- else if (buttonSide == 2)
- {
- DrawRectangleRec(leftButtonBound, GetColor(style[SPINNER_DEFAULT_BUTTON_BORDER_COLOR]));
- DrawRectangle(leftButtonBound.x + 2, leftButtonBound.y + 2, leftButtonBound.width - 4, leftButtonBound.height - 4, GetColor(style[SPINNER_DEFAULT_BUTTON_INSIDE_COLOR]));
-
- DrawRectangleRec(rightButtonBound, GetColor(style[SPINNER_HOVER_BUTTON_BORDER_COLOR]));
- DrawRectangle(rightButtonBound.x + 2, rightButtonBound.y + 2, rightButtonBound.width - 4, rightButtonBound.height - 4, GetColor(style[SPINNER_HOVER_BUTTON_INSIDE_COLOR]));
-
- DrawText(FormatText("-"), leftButtonBound.x + (leftButtonBound.width/2 - (MeasureText(FormatText("+"), style[GLOBAL_TEXT_FONTSIZE]))/2), leftButtonBound.y + (leftButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_DEFAULT_SYMBOL_COLOR]));
- DrawText(FormatText("+"), rightButtonBound.x + (rightButtonBound.width/2 - (MeasureText(FormatText("-"), style[GLOBAL_TEXT_FONTSIZE]))/2), rightButtonBound.y + (rightButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_HOVER_SYMBOL_COLOR]));
- }
-
- DrawRectangleRec(labelBoxBound, GetColor(style[SPINNER_LABEL_BORDER_COLOR]));
- DrawRectangle(labelBoxBound.x + 1, labelBoxBound.y + 1, labelBoxBound.width - 2, labelBoxBound.height - 2, GetColor(style[SPINNER_LABEL_INSIDE_COLOR]));
-
- DrawText(FormatText("%i", value), labelBoxBound.x + (labelBoxBound.width/2 - textWidth/2), labelBoxBound.y + (labelBoxBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_HOVER_TEXT_COLOR]));
- } break;
- case SPINNER_PRESSED:
- {
- if (buttonSide == 1)
- {
- DrawRectangleRec(leftButtonBound, GetColor(style[SPINNER_PRESSED_BUTTON_BORDER_COLOR]));
- DrawRectangle(leftButtonBound.x + 2, leftButtonBound.y + 2, leftButtonBound.width - 4, leftButtonBound.height - 4, GetColor(style[SPINNER_PRESSED_BUTTON_INSIDE_COLOR]));
-
- DrawRectangleRec(rightButtonBound, GetColor(style[SPINNER_DEFAULT_BUTTON_BORDER_COLOR]));
- DrawRectangle(rightButtonBound.x + 2, rightButtonBound.y + 2, rightButtonBound.width - 4, rightButtonBound.height - 4, GetColor(style[SPINNER_DEFAULT_BUTTON_INSIDE_COLOR]));
-
- DrawText(FormatText("-"), leftButtonBound.x + (leftButtonBound.width/2 - (MeasureText(FormatText("+"), style[GLOBAL_TEXT_FONTSIZE]))/2), leftButtonBound.y + (leftButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_PRESSED_SYMBOL_COLOR]));
- DrawText(FormatText("+"), rightButtonBound.x + (rightButtonBound.width/2 - (MeasureText(FormatText("-"), style[GLOBAL_TEXT_FONTSIZE]))/2), rightButtonBound.y + (rightButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_DEFAULT_SYMBOL_COLOR]));
- }
- else if (buttonSide == 2)
- {
- DrawRectangleRec(leftButtonBound, GetColor(style[SPINNER_DEFAULT_BUTTON_BORDER_COLOR]));
- DrawRectangle(leftButtonBound.x + 2, leftButtonBound.y + 2, leftButtonBound.width - 4, leftButtonBound.height - 4, GetColor(style[SPINNER_DEFAULT_BUTTON_INSIDE_COLOR]));
-
- DrawRectangleRec(rightButtonBound, GetColor(style[SPINNER_PRESSED_BUTTON_BORDER_COLOR]));
- DrawRectangle(rightButtonBound.x + 2, rightButtonBound.y + 2, rightButtonBound.width - 4, rightButtonBound.height - 4, GetColor(style[SPINNER_PRESSED_BUTTON_INSIDE_COLOR]));
-
- DrawText(FormatText("-"), leftButtonBound.x + (leftButtonBound.width/2 - (MeasureText(FormatText("+"), style[GLOBAL_TEXT_FONTSIZE]))/2), leftButtonBound.y + (leftButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_DEFAULT_SYMBOL_COLOR]));
- DrawText(FormatText("+"), rightButtonBound.x + (rightButtonBound.width/2 - (MeasureText(FormatText("-"), style[GLOBAL_TEXT_FONTSIZE]))/2), rightButtonBound.y + (rightButtonBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_PRESSED_SYMBOL_COLOR]));
- }
-
- DrawRectangleRec(labelBoxBound, GetColor(style[SPINNER_LABEL_BORDER_COLOR]));
- DrawRectangle(labelBoxBound.x + 1, labelBoxBound.y + 1, labelBoxBound.width - 2, labelBoxBound.height - 2, GetColor(style[SPINNER_LABEL_INSIDE_COLOR]));
-
- DrawText(FormatText("%i", value), labelBoxBound.x + (labelBoxBound.width/2 - textWidth/2), labelBoxBound.y + (labelBoxBound.height/2 - (style[GLOBAL_TEXT_FONTSIZE]/2)), style[GLOBAL_TEXT_FONTSIZE], GetColor(style[SPINNER_PRESSED_TEXT_COLOR]));
- } break;
- default: break;
- }
-
- return value;
-}
-
-// Text Box element, returns input text
-// NOTE: Requires static variables: framesCounter - ERROR!
-RAYGUIDEF char *GuiTextBox(Rectangle bounds, char *text)
-{
- #define MAX_CHARS_LENGTH 20
- #define KEY_BACKSPACE_TEXT 259 // GLFW BACKSPACE: 3 + 256
-
- int initPos = bounds.x + 4;
- int letter = -1;
- static int framesCounter = 0;
- Vector2 mousePoint = GetMousePosition();
-
- // Update control
- //--------------------------------------------------------------------
- framesCounter++;
-
- letter = GetKeyPressed();
-
- if (CheckCollisionPointRec(mousePoint, bounds))
- {
- if (letter != -1)
- {
- if (letter == KEY_BACKSPACE_TEXT)
- {
- for (int i = 0; i < MAX_CHARS_LENGTH; i++)
- {
- if ((text[i] == '\0') && (i > 0))
- {
- text[i - 1] = '\0';
- break;
- }
- }
-
- text[MAX_CHARS_LENGTH - 1] = '\0';
- }
- else
- {
- if ((letter >= 32) && (letter < 127))
- {
- for (int i = 0; i < MAX_CHARS_LENGTH; i++)
- {
- if (text[i] == '\0')
- {
- text[i] = (char)letter;
- break;
- }
- }
- }
- }
- }
- }
- //--------------------------------------------------------------------
-
- // Draw control
- //--------------------------------------------------------------------
- if (CheckCollisionPointRec(mousePoint, bounds)) DrawRectangleRec(bounds, GetColor(style[TOGGLE_ACTIVE_BORDER_COLOR]));
- else DrawRectangleRec(bounds, GetColor(style[TEXTBOX_BORDER_COLOR]));
-
- DrawRectangle(bounds.x + style[TEXTBOX_BORDER_WIDTH], bounds.y + style[TEXTBOX_BORDER_WIDTH], bounds.width - (style[TEXTBOX_BORDER_WIDTH] * 2), bounds.height - (style[TEXTBOX_BORDER_WIDTH] * 2), GetColor(style[TEXTBOX_INSIDE_COLOR]));
-
- for (int i = 0; i < MAX_CHARS_LENGTH; i++)
- {
- if (text[i] == '\0') break;
-
- DrawText(FormatText("%c", text[i]), initPos, bounds.y + style[TEXTBOX_TEXT_FONTSIZE], style[TEXTBOX_TEXT_FONTSIZE], GetColor(style[TEXTBOX_TEXT_COLOR]));
-
- 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)) DrawRectangle(initPos + 2, bounds.y + 5, 1, 20, GetColor(style[TEXTBOX_LINE_COLOR]));
- //--------------------------------------------------------------------
-
- return text;
-}
-
-// Save current GUI style into a text file
-RAYGUIDEF void SaveGuiStyle(const char *fileName)
-{
- FILE *styleFile = fopen(fileName, "wt");
-
- for (int i = 0; i < NUM_PROPERTIES; i++) fprintf(styleFile, "%-40s0x%x\n", guiPropertyName[i], GetStyleProperty(i));
-
- fclose(styleFile);
-}
-
-// Load GUI style from a text file
-RAYGUIDEF void LoadGuiStyle(const char *fileName)
-{
- #define MAX_STYLE_PROPERTIES 128
-
- typedef struct {
- char id[64];
- int value;
- } StyleProperty;
-
- StyleProperty *styleProp = (StyleProperty *)RAYGUI_MALLOC(MAX_STYLE_PROPERTIES*sizeof(StyleProperty));;
- int counter = 0;
-
- FILE *styleFile = fopen(fileName, "rt");
-
- while (!feof(styleFile))
- {
- fscanf(styleFile, "%s %i\n", styleProp[counter].id, &styleProp[counter].value);
- counter++;
- }
-
- fclose(styleFile);
-
- for (int i = 0; i < counter; i++)
- {
- for (int j = 0; j < NUM_PROPERTIES; j++)
- {
- if (strcmp(styleProp[i].id, guiPropertyName[j]) == 0)
- {
- // Assign correct property to style
- style[j] = styleProp[i].value;
- }
- }
- }
-
- RAYGUI_FREE(styleProp);
-}
-
-// Set one style property value
-RAYGUIDEF void SetStyleProperty(int guiProperty, int value)
-{
- #define NUM_COLOR_SAMPLES 10
-
- if (guiProperty == GLOBAL_BASE_COLOR)
- {
- Color baseColor = GetColor(value);
- Color fadeColor[NUM_COLOR_SAMPLES];
-
- for (int i = 0; i < NUM_COLOR_SAMPLES; i++) fadeColor[i] = ColorMultiply(baseColor, 1.0f - (float)i/(NUM_COLOR_SAMPLES - 1));
-
- style[GLOBAL_BASE_COLOR] = value;
- style[BACKGROUND_COLOR] = GetHexValue(fadeColor[3]);
- style[BUTTON_DEFAULT_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[BUTTON_HOVER_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[BUTTON_PRESSED_INSIDE_COLOR] = GetHexValue(fadeColor[5]);
- style[TOGGLE_DEFAULT_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[TOGGLE_HOVER_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[TOGGLE_PRESSED_INSIDE_COLOR] = GetHexValue(fadeColor[5]);
- style[TOGGLE_ACTIVE_INSIDE_COLOR] = GetHexValue(fadeColor[8]);
- style[SLIDER_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[SLIDER_DEFAULT_COLOR] = GetHexValue(fadeColor[6]);
- style[SLIDER_HOVER_COLOR] = GetHexValue(fadeColor[7]);
- style[SLIDER_ACTIVE_COLOR] = GetHexValue(fadeColor[9]);
- style[SLIDERBAR_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[SLIDERBAR_DEFAULT_COLOR] = GetHexValue(fadeColor[6]);
- style[SLIDERBAR_HOVER_COLOR] = GetHexValue(fadeColor[7]);
- style[SLIDERBAR_ACTIVE_COLOR] = GetHexValue(fadeColor[9]);
- style[SLIDERBAR_ZERO_LINE_COLOR] = GetHexValue(fadeColor[8]);
- style[PROGRESSBAR_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[PROGRESSBAR_PROGRESS_COLOR] = GetHexValue(fadeColor[6]);
- style[SPINNER_LABEL_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[SPINNER_DEFAULT_BUTTON_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[SPINNER_HOVER_BUTTON_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[SPINNER_PRESSED_BUTTON_INSIDE_COLOR] = GetHexValue(fadeColor[5]);
- style[COMBOBOX_DEFAULT_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[COMBOBOX_HOVER_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[COMBOBOX_PRESSED_INSIDE_COLOR] = GetHexValue(fadeColor[8]);
- style[COMBOBOX_PRESSED_LIST_INSIDE_COLOR] = GetHexValue(fadeColor[8]);
- style[CHECKBOX_DEFAULT_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- style[CHECKBOX_CLICK_INSIDE_COLOR] = GetHexValue(fadeColor[6]);
- style[CHECKBOX_STATUS_ACTIVE_COLOR] = GetHexValue(fadeColor[8]);
- style[TEXTBOX_INSIDE_COLOR] = GetHexValue(fadeColor[4]);
- }
- else if (guiProperty == GLOBAL_BORDER_COLOR)
- {
- Color baseColor = GetColor(value);
- Color fadeColor[NUM_COLOR_SAMPLES];
-
- for (int i = 0; i < NUM_COLOR_SAMPLES; i++) fadeColor[i] = ColorMultiply(baseColor, 1.0f - (float)i/(NUM_COLOR_SAMPLES - 1));
-
- style[GLOBAL_BORDER_COLOR] = value;
- style[BUTTON_DEFAULT_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[BUTTON_HOVER_BORDER_COLOR] = GetHexValue(fadeColor[8]);
- style[BUTTON_PRESSED_BORDER_COLOR] = GetHexValue(fadeColor[9]);
- style[TOGGLE_DEFAULT_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[TOGGLE_HOVER_BORDER_COLOR] = GetHexValue(fadeColor[8]);
- style[TOGGLE_PRESSED_BORDER_COLOR] = GetHexValue(fadeColor[9]);
- style[TOGGLE_ACTIVE_BORDER_COLOR] = GetHexValue(fadeColor[9]);
- style[SLIDER_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[SLIDERBAR_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[PROGRESSBAR_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[SPINNER_LABEL_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[SPINNER_DEFAULT_BUTTON_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[SPINNER_HOVER_BUTTON_BORDER_COLOR] = GetHexValue(fadeColor[8]);
- style[SPINNER_PRESSED_BUTTON_BORDER_COLOR] = GetHexValue(fadeColor[9]);
- style[COMBOBOX_DEFAULT_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[COMBOBOX_HOVER_BORDER_COLOR] = GetHexValue(fadeColor[8]);
- style[COMBOBOX_PRESSED_BORDER_COLOR] = GetHexValue(fadeColor[9]);
- style[COMBOBOX_PRESSED_LIST_BORDER_COLOR] = GetHexValue(fadeColor[9]);
- style[CHECKBOX_DEFAULT_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- style[CHECKBOX_HOVER_BORDER_COLOR] = GetHexValue(fadeColor[8]);
- style[CHECKBOX_CLICK_BORDER_COLOR] = GetHexValue(fadeColor[9]);
- style[TEXTBOX_BORDER_COLOR] = GetHexValue(fadeColor[7]);
- }
- else if (guiProperty == GLOBAL_TEXT_COLOR)
- {
- Color baseColor = GetColor(value);
- Color fadeColor[NUM_COLOR_SAMPLES];
-
- for (int i = 0; i < NUM_COLOR_SAMPLES; i++) fadeColor[i] = ColorMultiply(baseColor, 1.0f - (float)i/(NUM_COLOR_SAMPLES - 1));
-
- style[GLOBAL_TEXT_COLOR] = value;
- style[LABEL_TEXT_COLOR] = GetHexValue(fadeColor[9]);
- style[BUTTON_DEFAULT_TEXT_COLOR] = GetHexValue(fadeColor[9]);
- style[BUTTON_HOVER_TEXT_COLOR] = GetHexValue(fadeColor[8]);
- style[BUTTON_PRESSED_TEXT_COLOR] = GetHexValue(fadeColor[5]);
- style[TOGGLE_DEFAULT_TEXT_COLOR] = GetHexValue(fadeColor[9]);
- style[TOGGLE_HOVER_TEXT_COLOR] = GetHexValue(fadeColor[8]);
- style[TOGGLE_PRESSED_TEXT_COLOR] = GetHexValue(fadeColor[5]);
- style[TOGGLE_ACTIVE_TEXT_COLOR] = GetHexValue(fadeColor[5]);
- style[SPINNER_DEFAULT_SYMBOL_COLOR] = GetHexValue(fadeColor[9]);
- style[SPINNER_DEFAULT_TEXT_COLOR] = GetHexValue(fadeColor[9]);
- style[SPINNER_HOVER_SYMBOL_COLOR] = GetHexValue(fadeColor[8]);
- style[SPINNER_HOVER_TEXT_COLOR] = GetHexValue(fadeColor[8]);
- style[SPINNER_PRESSED_SYMBOL_COLOR] = GetHexValue(fadeColor[5]);
- style[SPINNER_PRESSED_TEXT_COLOR] = GetHexValue(fadeColor[5]);
- style[COMBOBOX_DEFAULT_TEXT_COLOR] = GetHexValue(fadeColor[9]);
- style[COMBOBOX_DEFAULT_LIST_TEXT_COLOR] = GetHexValue(fadeColor[9]);
- style[COMBOBOX_HOVER_TEXT_COLOR] = GetHexValue(fadeColor[8]);
- style[COMBOBOX_HOVER_LIST_TEXT_COLOR] = GetHexValue(fadeColor[8]);
- style[COMBOBOX_PRESSED_TEXT_COLOR] = GetHexValue(fadeColor[4]);
- style[COMBOBOX_PRESSED_LIST_TEXT_COLOR] = GetHexValue(fadeColor[4]);
- style[TEXTBOX_TEXT_COLOR] = GetHexValue(fadeColor[9]);
- style[TEXTBOX_LINE_COLOR] = GetHexValue(fadeColor[6]);
- }
- else style[guiProperty] = value;
-
-}
-
-// Get one style property value
-RAYGUIDEF int GetStyleProperty(int guiProperty) { return style[guiProperty]; }
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
-
-static Color ColorMultiply(Color baseColor, float value)
-{
- Color multColor = baseColor;
-
- if (value > 1.0f) value = 1.0f;
- else if (value < 0.0f) value = 0.0f;
-
- multColor.r += (255 - multColor.r)*value;
- multColor.g += (255 - multColor.g)*value;
- multColor.b += (255 - multColor.b)*value;
-
- return multColor;
-}
-
-#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
-
-#endif // RAYGUI_IMPLEMENTATION
-
diff --git a/src/raylib.h b/src/raylib.h
index fee6aa91..5834d1c9 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -1,6 +1,6 @@
/**********************************************************************************************
*
-* raylib 1.5.0 (www.raylib.com)
+* raylib 1.6.0 (www.raylib.com)
*
* A simple and easy-to-use library to learn videogames programming
*
@@ -81,6 +81,14 @@
typedef struct android_app; // Define android_app struct (android_native_app_glue.h)
#endif
+#if defined(_WIN32) && defined(BUILDING_DLL)
+ #define RLAPI __declspec(dllexport) // We are building raylib as a Win32 DLL
+#elif defined(_WIN32) && defined(RAYLIB_DLL)
+ #define RLAPI __declspec(dllimport) // We are using raylib as a Win32 DLL
+#else
+ #define RLAPI // We are building or using raylib as a static library (or Linux shared library)
+#endif
+
//----------------------------------------------------------------------------------
// Some basic Defines
//----------------------------------------------------------------------------------
@@ -93,11 +101,12 @@
// raylib Config Flags
#define FLAG_FULLSCREEN_MODE 1
-#define FLAG_SHOW_LOGO 2
-#define FLAG_SHOW_MOUSE_CURSOR 4
-#define FLAG_CENTERED_MODE 8
-#define FLAG_MSAA_4X_HINT 16
-#define FLAG_VSYNC_HINT 32
+#define FLAG_RESIZABLE_WINDOW 2
+#define FLAG_SHOW_LOGO 4
+#define FLAG_SHOW_MOUSE_CURSOR 8
+#define FLAG_CENTERED_MODE 16
+#define FLAG_MSAA_4X_HINT 32
+#define FLAG_VSYNC_HINT 64
// Keyboard Function Keys
#define KEY_SPACE 32
@@ -167,13 +176,8 @@
// Mouse Buttons
#define MOUSE_LEFT_BUTTON 0
-#if defined(PLATFORM_WEB)
- #define MOUSE_RIGHT_BUTTON 2
- #define MOUSE_MIDDLE_BUTTON 1
-#else
- #define MOUSE_RIGHT_BUTTON 1
- #define MOUSE_MIDDLE_BUTTON 2
-#endif
+#define MOUSE_RIGHT_BUTTON 1
+#define MOUSE_MIDDLE_BUTTON 2
// Touch points registered
#define MAX_TOUCH_POINTS 2
@@ -181,21 +185,24 @@
// Gamepad Number
#define GAMEPAD_PLAYER1 0
#define GAMEPAD_PLAYER2 1
-#define GAMEPAD_PLAYER3 2 // Not supported
-#define GAMEPAD_PLAYER4 3 // Not supported
+#define GAMEPAD_PLAYER3 2
+#define GAMEPAD_PLAYER4 3
// Gamepad Buttons
-// NOTE: Adjusted for a PS3 USB Controller
-#define GAMEPAD_BUTTON_A 2
-#define GAMEPAD_BUTTON_B 1
-#define GAMEPAD_BUTTON_X 3
-#define GAMEPAD_BUTTON_Y 4
-#define GAMEPAD_BUTTON_R1 7
-#define GAMEPAD_BUTTON_R2 5
-#define GAMEPAD_BUTTON_L1 6
-#define GAMEPAD_BUTTON_L2 8
-#define GAMEPAD_BUTTON_SELECT 9
-#define GAMEPAD_BUTTON_START 10
+
+// PS3 USB Controller
+#define GAMEPAD_PS3_BUTTON_A 2
+#define GAMEPAD_PS3_BUTTON_B 1
+#define GAMEPAD_PS3_BUTTON_X 3
+#define GAMEPAD_PS3_BUTTON_Y 4
+#define GAMEPAD_PS3_BUTTON_R1 7
+#define GAMEPAD_PS3_BUTTON_R2 5
+#define GAMEPAD_PS3_BUTTON_L1 6
+#define GAMEPAD_PS3_BUTTON_L2 8
+#define GAMEPAD_PS3_BUTTON_SELECT 9
+#define GAMEPAD_PS3_BUTTON_START 10
+
+// TODO: Add PS3 d-pad axis
// Xbox360 USB Controller Buttons
#define GAMEPAD_XBOX_BUTTON_A 0
@@ -233,35 +240,43 @@
#define ANDROID_VOLUME_UP 24
#define ANDROID_VOLUME_DOWN 25
+// NOTE: MSC C++ compiler does not support compound literals (C99 feature)
+// Plain structures in C++ (without constructors) can be initialized from { } initializers.
+#ifdef __cplusplus
+ #define CLITERAL
+#else
+ #define CLITERAL (Color)
+#endif
+
// Some Basic Colors
// NOTE: Custom raylib color palette for amazing visuals on WHITE background
-#define LIGHTGRAY (Color){ 200, 200, 200, 255 } // Light Gray
-#define GRAY (Color){ 130, 130, 130, 255 } // Gray
-#define DARKGRAY (Color){ 80, 80, 80, 255 } // Dark Gray
-#define YELLOW (Color){ 253, 249, 0, 255 } // Yellow
-#define GOLD (Color){ 255, 203, 0, 255 } // Gold
-#define ORANGE (Color){ 255, 161, 0, 255 } // Orange
-#define PINK (Color){ 255, 109, 194, 255 } // Pink
-#define RED (Color){ 230, 41, 55, 255 } // Red
-#define MAROON (Color){ 190, 33, 55, 255 } // Maroon
-#define GREEN (Color){ 0, 228, 48, 255 } // Green
-#define LIME (Color){ 0, 158, 47, 255 } // Lime
-#define DARKGREEN (Color){ 0, 117, 44, 255 } // Dark Green
-#define SKYBLUE (Color){ 102, 191, 255, 255 } // Sky Blue
-#define BLUE (Color){ 0, 121, 241, 255 } // Blue
-#define DARKBLUE (Color){ 0, 82, 172, 255 } // Dark Blue
-#define PURPLE (Color){ 200, 122, 255, 255 } // Purple
-#define VIOLET (Color){ 135, 60, 190, 255 } // Violet
-#define DARKPURPLE (Color){ 112, 31, 126, 255 } // Dark Purple
-#define BEIGE (Color){ 211, 176, 131, 255 } // Beige
-#define BROWN (Color){ 127, 106, 79, 255 } // Brown
-#define DARKBROWN (Color){ 76, 63, 47, 255 } // Dark Brown
-
-#define WHITE (Color){ 255, 255, 255, 255 } // White
-#define BLACK (Color){ 0, 0, 0, 255 } // Black
-#define BLANK (Color){ 0, 0, 0, 0 } // Blank (Transparent)
-#define MAGENTA (Color){ 255, 0, 255, 255 } // Magenta
-#define RAYWHITE (Color){ 245, 245, 245, 255 } // My own White (raylib logo)
+#define LIGHTGRAY CLITERAL{ 200, 200, 200, 255 } // Light Gray
+#define GRAY CLITERAL{ 130, 130, 130, 255 } // Gray
+#define DARKGRAY CLITERAL{ 80, 80, 80, 255 } // Dark Gray
+#define YELLOW CLITERAL{ 253, 249, 0, 255 } // Yellow
+#define GOLD CLITERAL{ 255, 203, 0, 255 } // Gold
+#define ORANGE CLITERAL{ 255, 161, 0, 255 } // Orange
+#define PINK CLITERAL{ 255, 109, 194, 255 } // Pink
+#define RED CLITERAL{ 230, 41, 55, 255 } // Red
+#define MAROON CLITERAL{ 190, 33, 55, 255 } // Maroon
+#define GREEN CLITERAL{ 0, 228, 48, 255 } // Green
+#define LIME CLITERAL{ 0, 158, 47, 255 } // Lime
+#define DARKGREEN CLITERAL{ 0, 117, 44, 255 } // Dark Green
+#define SKYBLUE CLITERAL{ 102, 191, 255, 255 } // Sky Blue
+#define BLUE CLITERAL{ 0, 121, 241, 255 } // Blue
+#define DARKBLUE CLITERAL{ 0, 82, 172, 255 } // Dark Blue
+#define PURPLE CLITERAL{ 200, 122, 255, 255 } // Purple
+#define VIOLET CLITERAL{ 135, 60, 190, 255 } // Violet
+#define DARKPURPLE CLITERAL{ 112, 31, 126, 255 } // Dark Purple
+#define BEIGE CLITERAL{ 211, 176, 131, 255 } // Beige
+#define BROWN CLITERAL{ 127, 106, 79, 255 } // Brown
+#define DARKBROWN CLITERAL{ 76, 63, 47, 255 } // Dark Brown
+
+#define WHITE CLITERAL{ 255, 255, 255, 255 } // White
+#define BLACK CLITERAL{ 0, 0, 0, 255 } // Black
+#define BLANK CLITERAL{ 0, 0, 0, 0 } // Blank (Transparent)
+#define MAGENTA CLITERAL{ 255, 0, 255, 255 } // Magenta
+#define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo)
//----------------------------------------------------------------------------------
// Types and Structures Definition
@@ -397,7 +412,7 @@ typedef struct Mesh {
// Shader type (generic shader)
typedef struct Shader {
unsigned int id; // Shader program id
-
+
// Vertex attributes locations (default locations)
int vertexLoc; // Vertex attribute location point (default-location = 0)
int texcoordLoc; // Texcoord attribute location point (default-location = 1)
@@ -408,8 +423,10 @@ typedef struct Shader {
// Uniform locations
int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader)
- int tintColorLoc; // Diffuse color uniform location point (fragment shader)
-
+ int colDiffuseLoc; // Diffuse color uniform location point (fragment shader)
+ int colAmbientLoc; // Ambient color uniform location point (fragment shader)
+ int colSpecularLoc; // Specular color 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)
@@ -423,11 +440,11 @@ typedef struct Material {
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; // Diffuse color
Color colAmbient; // Ambient color
Color colSpecular; // Specular color
-
+
float glossiness; // Glossiness level (Ranges from 0 to 1000)
} Material;
@@ -443,14 +460,14 @@ typedef struct LightData {
unsigned int id; // Light unique id
bool enabled; // Light enabled
int type; // Light type: LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT
-
+
Vector3 position; // Light position
- Vector3 target; // Light target: LIGHT_DIRECTIONAL and LIGHT_SPOT (cone direction target)
+ Vector3 target; // Light direction: 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;
@@ -463,21 +480,38 @@ typedef struct Ray {
Vector3 direction; // Ray direction
} Ray;
-// Sound source type
-typedef struct Sound {
- unsigned int source; // Sound audio source id
- unsigned int buffer; // Sound audio buffer id
-} Sound;
-
// Wave type, defines audio wave data
typedef struct Wave {
+ unsigned int sampleCount; // Number of samples
+ unsigned int sampleRate; // Frequency (samples per second)
+ unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ unsigned int channels; // Number of channels (1-mono, 2-stereo)
void *data; // Buffer data pointer
- unsigned int dataSize; // Data size in bytes
- unsigned int sampleRate; // Samples per second to be played
- short bitsPerSample; // Sample size in bits
- short channels;
} Wave;
+// Sound source type
+typedef struct Sound {
+ unsigned int source; // OpenAL audio source id
+ unsigned int buffer; // OpenAL audio buffer id
+ int format; // OpenAL audio format specifier
+} Sound;
+
+// Music type (file streaming from memory)
+// NOTE: Anything longer than ~10 seconds should be streamed
+typedef struct MusicData *Music;
+
+// Audio stream type
+// NOTE: Useful to create custom audio streams not bound to a specific file
+typedef struct AudioStream {
+ unsigned int sampleRate; // Frequency (samples per second)
+ unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
+ unsigned int channels; // Number of channels (1-mono, 2-stereo)
+
+ int format; // OpenAL audio format specifier
+ unsigned int source; // OpenAL audio source id
+ unsigned int buffers[2]; // OpenAL audio buffers (double buffering)
+} AudioStream;
+
// Texture formats
// NOTE: Support depends on OpenGL version and platform
typedef enum {
@@ -520,20 +554,14 @@ typedef enum {
GESTURE_PINCH_OUT = 512
} Gestures;
-// Touch action (fingers or mouse)
-typedef enum { TOUCH_UP, TOUCH_DOWN, TOUCH_MOVE } TouchAction;
-
-// Gesture events
-// NOTE: MAX_TOUCH_POINTS fixed to 2
-typedef struct GestureEvent {
- int touchAction;
- int pointCount;
- int pointerId[MAX_TOUCH_POINTS];
- Vector2 position[MAX_TOUCH_POINTS];
-} GestureEvent;
-
// Camera system modes
-typedef enum { CAMERA_CUSTOM = 0, CAMERA_FREE, CAMERA_ORBITAL, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON } CameraMode;
+typedef enum {
+ CAMERA_CUSTOM = 0,
+ CAMERA_FREE,
+ CAMERA_ORBITAL,
+ CAMERA_FIRST_PERSON,
+ CAMERA_THIRD_PERSON
+} CameraMode;
// Head Mounted Display devices
typedef enum {
@@ -561,94 +589,95 @@ extern "C" { // Prevents name mangling of functions
// Window and Graphics Device Functions (Module: core)
//------------------------------------------------------------------------------------
#if defined(PLATFORM_ANDROID)
-void InitWindow(int width, int height, struct android_app *state); // Init Android Activity and OpenGL Graphics
+RLAPI void InitWindow(int width, int height, struct android_app *state); // Init Android Activity and OpenGL Graphics
#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
-void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics
+RLAPI void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics
#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)
-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 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 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)
-float GetFPS(void); // Returns current FPS
-float GetFrameTime(void); // Returns time in seconds for one frame
-
-Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value
-int GetHexValue(Color color); // Returns hexadecimal value for a Color
-float *ColorToFloat(Color color); // Converts Color to float array and normalizes
-float *VectorToFloat(Vector3 vec); // Converts Vector3 to float array
-float *MatrixToFloat(Matrix mat); // Converts Matrix to float array
-
-int GetRandomValue(int min, int max); // Returns a random value between min and max (both included)
-Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
-
-void SetConfigFlags(char flags); // Setup some window configuration flags
-void ShowLogo(void); // Activates raylib logo at startup (can be done with flags)
-
-bool IsFileDropped(void); // Check if a file have been dropped into window
-char **GetDroppedFiles(int *count); // Retrieve dropped files into window
-void ClearDroppedFiles(void); // Clear dropped files paths buffer
-
-void StorageSaveValue(int position, int value); // Storage save integer value (to defined position)
-int StorageLoadValue(int position); // Storage load integer value (from defined position)
+RLAPI void CloseWindow(void); // Close Window and Terminate Context
+RLAPI bool WindowShouldClose(void); // Detect if KEY_ESCAPE pressed or Close icon pressed
+RLAPI bool IsWindowMinimized(void); // Detect if window has been minimized (or lost focus)
+RLAPI void ToggleFullscreen(void); // Fullscreen toggle (only PLATFORM_DESKTOP)
+RLAPI int GetScreenWidth(void); // Get current screen width
+RLAPI int GetScreenHeight(void); // Get current screen height
+
+RLAPI void ShowCursor(void); // Shows cursor
+RLAPI void HideCursor(void); // Hides cursor
+RLAPI bool IsCursorHidden(void); // Returns true if cursor is not visible
+RLAPI void EnableCursor(void); // Enables cursor
+RLAPI void DisableCursor(void); // Disables cursor
+
+RLAPI void ClearBackground(Color color); // Sets Background Color
+RLAPI void BeginDrawing(void); // Setup drawing canvas to start drawing
+RLAPI void EndDrawing(void); // End canvas drawing and Swap Buffers (Double Buffering)
+
+RLAPI void Begin2dMode(Camera2D camera); // Initialize 2D mode with custom camera
+RLAPI void End2dMode(void); // Ends 2D mode custom camera usage
+RLAPI void Begin3dMode(Camera camera); // Initializes 3D mode for drawing (Camera setup)
+RLAPI void End3dMode(void); // Ends 3D mode and returns to default 2D orthographic mode
+RLAPI void BeginTextureMode(RenderTexture2D target); // Initializes render texture for drawing
+RLAPI void EndTextureMode(void); // Ends drawing to render texture
+
+RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a ray trace from mouse position
+RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position from a 3d world space position
+RLAPI Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix)
+
+RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
+RLAPI float GetFPS(void); // Returns current FPS
+RLAPI float GetFrameTime(void); // Returns time in seconds for one frame
+
+RLAPI Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value
+RLAPI int GetHexValue(Color color); // Returns hexadecimal value for a Color
+RLAPI float *ColorToFloat(Color color); // Converts Color to float array and normalizes
+RLAPI float *VectorToFloat(Vector3 vec); // Converts Vector3 to float array
+RLAPI float *MatrixToFloat(Matrix mat); // Converts Matrix to float array
+
+RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included)
+RLAPI Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
+
+RLAPI void SetConfigFlags(char flags); // Setup some window configuration flags
+RLAPI void ShowLogo(void); // Activates raylib logo at startup (can be done with flags)
+
+RLAPI bool IsFileDropped(void); // Check if a file have been dropped into window
+RLAPI char **GetDroppedFiles(int *count); // Retrieve dropped files into window
+RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer
+
+RLAPI void StorageSaveValue(int position, int value); // Storage save integer value (to defined position)
+RLAPI int StorageLoadValue(int position); // Storage load integer value (from defined position)
//------------------------------------------------------------------------------------
// Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
-bool IsKeyPressed(int key); // Detect if a key has been pressed once
-bool IsKeyDown(int key); // Detect if a key is being pressed
-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
+RLAPI bool IsKeyPressed(int key); // Detect if a key has been pressed once
+RLAPI bool IsKeyDown(int key); // Detect if a key is being pressed
+RLAPI bool IsKeyReleased(int key); // Detect if a key has been released once
+RLAPI bool IsKeyUp(int key); // Detect if a key is NOT being pressed
+RLAPI int GetKeyPressed(void); // Get latest key pressed
+RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
+
+RLAPI bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available
+RLAPI const char *GetGamepadName(int gamepad); // Return gamepad internal name id
+RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis
+RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once
+RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gamepad button is being pressed
+RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once
+RLAPI 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
-bool IsMouseButtonReleased(int button); // Detect if a mouse button has been released once
-bool IsMouseButtonUp(int button); // Detect if a mouse button is NOT being pressed
-int GetMouseX(void); // Returns mouse position X
-int GetMouseY(void); // Returns mouse position Y
-Vector2 GetMousePosition(void); // Returns mouse position XY
-void SetMousePosition(Vector2 position); // Set mouse position XY
-int GetMouseWheelMove(void); // Returns mouse wheel movement Y
+RLAPI bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once
+RLAPI bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed
+RLAPI bool IsMouseButtonReleased(int button); // Detect if a mouse button has been released once
+RLAPI bool IsMouseButtonUp(int button); // Detect if a mouse button is NOT being pressed
+RLAPI int GetMouseX(void); // Returns mouse position X
+RLAPI int GetMouseY(void); // Returns mouse position Y
+RLAPI Vector2 GetMousePosition(void); // Returns mouse position XY
+RLAPI void SetMousePosition(Vector2 position); // Set mouse position XY
+RLAPI int GetMouseWheelMove(void); // Returns mouse wheel movement Y
-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)
+RLAPI int GetTouchX(void); // Returns touch position X for touch point 0 (relative to screen size)
+RLAPI int GetTouchY(void); // Returns touch position Y for touch point 0 (relative to screen size)
+RLAPI Vector2 GetTouchPosition(int index); // Returns touch position XY for a touch point index (relative to screen size)
#if defined(PLATFORM_ANDROID)
bool IsButtonPressed(int button); // Detect if an android physic button has been pressed
@@ -659,254 +688,263 @@ bool IsButtonReleased(int button); // Detect if an android
//------------------------------------------------------------------------------------
// Gestures and Touch Handling Functions (Module: gestures)
//------------------------------------------------------------------------------------
-void SetGesturesEnabled(unsigned int gestureFlags); // Enable a set of gestures using flags
-bool IsGestureDetected(int gesture); // Check if a gesture have been detected
-void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures
-void UpdateGestures(void); // Update gestures detected (called automatically in PollInputEvents())
-
-int GetTouchPointsCount(void); // Get touch points count
-int GetGestureDetected(void); // Get latest detected gesture
-float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds
-Vector2 GetGestureDragVector(void); // Get gesture drag vector
-float GetGestureDragAngle(void); // Get gesture drag angle
-Vector2 GetGesturePinchVector(void); // Get gesture pinch delta
-float GetGesturePinchAngle(void); // Get gesture pinch angle
+RLAPI void SetGesturesEnabled(unsigned int gestureFlags); // Enable a set of gestures using flags
+RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected
+RLAPI int GetGestureDetected(void); // Get latest detected gesture
+RLAPI int GetTouchPointsCount(void); // Get touch points count
+RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds
+RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector
+RLAPI float GetGestureDragAngle(void); // Get gesture drag angle
+RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta
+RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle
//------------------------------------------------------------------------------------
// Camera System Functions (Module: camera)
//------------------------------------------------------------------------------------
-void SetCameraMode(int mode); // Set camera mode (multiple camera modes available)
-void UpdateCamera(Camera *camera); // Update camera (player position is ignored)
-void UpdateCameraPlayer(Camera *camera, Vector3 *position); // Update camera and player position (1st person and 3rd person cameras)
-
-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
+RLAPI void SetCameraMode(Camera, int mode); // Set camera mode (multiple camera modes available)
+RLAPI void UpdateCamera(Camera *camera); // Update camera position for selected mode
-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)
-void SetCameraSmoothZoomControl(int szKey); // Set camera smooth zoom key to combine with mouse (free camera)
-
-void SetCameraMoveControls(int frontKey, int backKey,
- int leftKey, int rightKey,
- int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras)
-void SetCameraMouseSensitivity(float sensitivity); // Set camera mouse sensitivity (1st person and 3rd person cameras)
+RLAPI void SetCameraPanControl(int panKey); // Set camera pan key to combine with mouse movement (free camera)
+RLAPI void SetCameraAltControl(int altKey); // Set camera alt key to combine with mouse movement (free camera)
+RLAPI void SetCameraSmoothZoomControl(int szKey); // Set camera smooth zoom key to combine with mouse (free camera)
+RLAPI void SetCameraMoveControls(int frontKey, int backKey,
+ int rightKey, int leftKey,
+ int upKey, int downKey); // Set camera move controls (1st person and 3rd person cameras)
//------------------------------------------------------------------------------------
// Basic Shapes Drawing Functions (Module: shapes)
//------------------------------------------------------------------------------------
-void DrawPixel(int posX, int posY, Color color); // Draw a pixel
-void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version)
-void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line
-void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version)
-void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle
-void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle
-void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version)
-void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
-void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle
-void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle
-void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle
-void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version)
-void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline
-void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle
-void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline
-void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version)
-void DrawPolyEx(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points
-void DrawPolyExLines(Vector2 *points, int numPoints, Color color); // Draw polygon lines
-
-bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles
-bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles
-bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle
-Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision
-bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle
-bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle
-bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle
+RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel
+RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version)
+RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line
+RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version)
+RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle
+RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle
+RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version)
+RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline
+RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle
+RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle
+RLAPI void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle
+RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version)
+RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline
+RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle
+RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline
+RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version)
+RLAPI void DrawPolyEx(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points
+RLAPI void DrawPolyExLines(Vector2 *points, int numPoints, Color color); // Draw polygon lines
+
+RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles
+RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles
+RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle
+RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision
+RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle
+RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle
+RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle
//------------------------------------------------------------------------------------
// Texture Loading and Drawing Functions (Module: textures)
//------------------------------------------------------------------------------------
-Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM)
-Image LoadImageEx(Color *pixels, int width, int height); // Load image data from Color array data (RGBA - 32bit)
-Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image data from RAW file
-Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource)
-Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory
-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)
-void ImageFormat(Image *image, int newFormat); // Convert image data to desired format
-void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
-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 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
-void ImageColorInvert(Image *image); // Modify image color: invert
-void ImageColorGrayscale(Image *image); // Modify image color: grayscale
-void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100)
-void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255)
-void GenTextureMipmaps(Texture2D texture); // Generate GPU mipmaps for a texture
-void UpdateTexture(Texture2D texture, void *pixels); // Update GPU texture with new data
-
-void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D
-void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2
-void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters
-void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle
-void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, // Draw a part of a texture defined by a rectangle with 'pro' parameters
+RLAPI Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM)
+RLAPI Image LoadImageEx(Color *pixels, int width, int height); // Load image data from Color array data (RGBA - 32bit)
+RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image data from RAW file
+RLAPI Image LoadImageFromRES(const char *rresName, int resId); // Load an image from rRES file (raylib Resource)
+RLAPI Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory
+RLAPI Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat); // Load a texture from raw data into GPU memory
+RLAPI Texture2D LoadTextureFromRES(const char *rresName, int resId); // Load an image as texture from rRES file (raylib Resource)
+RLAPI Texture2D LoadTextureFromImage(Image image); // Load a texture from image data
+RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load a texture to be used for rendering
+RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM)
+RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory
+RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory
+RLAPI Color *GetImageData(Image image); // Get pixel data from image as a Color struct array
+RLAPI Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image
+RLAPI void ImageToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two)
+RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format
+RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image
+RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
+RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations)
+RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle
+RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize and image (bilinear filtering)
+RLAPI void ImageResizeNN(Image *image,int newWidth,int newHeight); // Resize and image (Nearest-Neighbor scaling algorithm)
+RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font)
+RLAPI Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing, Color tint); // Create an image from text (custom sprite font)
+RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image
+RLAPI void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color); // Draw text (default font) within an image (destination)
+RLAPI void ImageDrawTextEx(Image *dst, Vector2 position, SpriteFont font, const char *text, float fontSize, int spacing, Color color); // Draw text (custom sprite font) within an image (destination)
+RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
+RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally
+RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint
+RLAPI void ImageColorInvert(Image *image); // Modify image color: invert
+RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale
+RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100)
+RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255)
+RLAPI void GenTextureMipmaps(Texture2D texture); // Generate GPU mipmaps for a texture
+RLAPI void UpdateTexture(Texture2D texture, void *pixels); // Update GPU texture with new data
+
+RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D
+RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2
+RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters
+RLAPI void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle
+RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, // Draw a part of a texture defined by a rectangle with 'pro' parameters
float rotation, Color tint);
//------------------------------------------------------------------------------------
// Font Loading and Text Drawing Functions (Module: text)
//------------------------------------------------------------------------------------
-SpriteFont GetDefaultFont(void); // Get the default SpriteFont
-SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory
-void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory
+RLAPI SpriteFont GetDefaultFont(void); // Get the default SpriteFont
+RLAPI SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory
+RLAPI void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory
-void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
-void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, // Draw text using SpriteFont and additional parameters
- int fontSize, int spacing, Color tint);
-int MeasureText(const char *text, int fontSize); // Measure string width for default font
-Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int spacing); // Measure string size for SpriteFont
+RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
+RLAPI void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, // Draw text using SpriteFont and additional parameters
+ float fontSize, int spacing, Color tint);
+RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
+RLAPI Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int spacing); // Measure string size for SpriteFont
-void DrawFPS(int posX, int posY); // Shows current FPS on top-left corner
-const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
-const char *SubText(const char *text, int position, int length); // Get a piece of a text string
+RLAPI void DrawFPS(int posX, int posY); // Shows current FPS on top-left corner
+RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
+RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string
//------------------------------------------------------------------------------------
// Basic 3d Shapes Drawing Functions (Module: models)
//------------------------------------------------------------------------------------
-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
-void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float lenght, Color color); // Draw cube textured
-void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere
-void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters
-void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires
-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 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
-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
+RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space
+RLAPI void DrawCircle3D(Vector3 center, float radius, float rotationAngle, Vector3 rotation, Color color); // Draw a circle in 3D world space
+RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube
+RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version)
+RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires
+RLAPI void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color); // Draw cube textured
+RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere
+RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters
+RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires
+RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone
+RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires
+RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ
+RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line
+RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0))
+RLAPI void DrawGizmo(Vector3 position); // Draw simple gizmo
+RLAPI 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, 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
-
-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 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
-bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); // Detect collision between two spheres
-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, 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
+RLAPI Model LoadModel(const char *fileName); // Load a 3d model (.OBJ)
+RLAPI Model LoadModelEx(Mesh data, bool dynamic); // Load a 3d model (from mesh data)
+RLAPI Model LoadModelFromRES(const char *rresName, int resId); // Load a 3d model from rRES file (raylib Resource)
+RLAPI Model LoadHeightmap(Image heightmap, Vector3 size); // Load a heightmap image as a 3d model
+RLAPI Model LoadCubicmap(Image cubicmap); // Load a map image as a 3d model (cubes based)
+RLAPI void UnloadModel(Model model); // Unload 3d model from memory
+
+RLAPI Material LoadMaterial(const char *fileName); // Load material data (.MTL)
+RLAPI Material LoadDefaultMaterial(void); // Load default material (uses default models shader)
+RLAPI Material LoadStandardMaterial(void); // Load standard material (uses material attributes and lighting shader)
+RLAPI void UnloadMaterial(Material material); // Unload material textures from VRAM
+
+RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set)
+RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
+RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set)
+RLAPI 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
+RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires)
+
+RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint); // Draw a billboard texture
+RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec
+
+RLAPI BoundingBox CalculateBoundingBox(Mesh mesh); // Calculate mesh bounding box limits
+RLAPI bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB); // Detect collision between two spheres
+RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Detect collision between two bounding boxes
+RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 centerSphere, float radiusSphere); // Detect collision between box and sphere
+RLAPI bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius); // Detect collision between ray and sphere
+RLAPI bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint); // Detect collision between ray and sphere with extended parameters and collision point detection
+RLAPI bool CheckCollisionRayBox(Ray ray, BoundingBox box); // Detect collision between ray and box
+
//------------------------------------------------------------------------------------
// Shaders System Functions (Module: rlgl)
// NOTE: This functions are useless when using OpenGL 1.1
//------------------------------------------------------------------------------------
-Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations
-void UnloadShader(Shader shader); // Unload a custom shader from memory
+RLAPI Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations
+RLAPI void UnloadShader(Shader shader); // Unload a custom shader from memory
-Shader GetDefaultShader(void); // Get default shader
-Shader GetStandardShader(void); // Get standard shader
-Texture2D GetDefaultTexture(void); // Get default texture
+RLAPI Shader GetDefaultShader(void); // Get default shader
+RLAPI Shader GetStandardShader(void); // Get standard shader
+RLAPI 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)
+RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location
+RLAPI void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float)
+RLAPI void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int)
+RLAPI 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)
+RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix)
+RLAPI 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)
+RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing
+RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader)
+RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied)
+RLAPI 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
+RLAPI Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool
+RLAPI void DestroyLight(Light light); // Destroy a light and take it out of the list
//------------------------------------------------------------------------------------
// VR experience Functions (Module: rlgl)
// NOTE: This functions are useless when using OpenGL 1.1
//------------------------------------------------------------------------------------
-void InitVrDevice(int vdDevice); // Init VR device
-void CloseVrDevice(void); // Close VR device
-void UpdateVrTracking(void); // Update VR tracking (position and orientation)
-void BeginVrDrawing(void); // Begin VR drawing configuration
-void EndVrDrawing(void); // End VR drawing process (and desktop mirror)
-bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready
-void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator)
+RLAPI void InitVrDevice(int vdDevice); // Init VR device
+RLAPI void CloseVrDevice(void); // Close VR device
+RLAPI bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready
+RLAPI bool IsVrSimulator(void); // Detect if VR simulator is running
+RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera
+RLAPI void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator)
//------------------------------------------------------------------------------------
// 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
-Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource)
-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 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)
-
-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)
-void SetMusicPitch(int index, float pitch); // Set pitch for a music (1.0 is base 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); // Get number of streams loaded
+RLAPI void InitAudioDevice(void); // Initialize audio device and context
+RLAPI void CloseAudioDevice(void); // Close the audio device and context (and music stream)
+RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully
+
+RLAPI Wave LoadWave(const char *fileName); // Load wave data from file into RAM
+RLAPI Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from float array data (32bit)
+RLAPI Sound LoadSound(const char *fileName); // Load sound to memory
+RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data
+RLAPI Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource)
+RLAPI void UpdateSound(Sound sound, void *data, int numSamples); // Update sound buffer with new data
+RLAPI void UnloadWave(Wave wave); // Unload wave data
+RLAPI void UnloadSound(Sound sound); // Unload sound
+RLAPI void PlaySound(Sound sound); // Play a sound
+RLAPI void PauseSound(Sound sound); // Pause a sound
+RLAPI void ResumeSound(Sound sound); // Resume a paused sound
+RLAPI void StopSound(Sound sound); // Stop playing a sound
+RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
+RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
+RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
+RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
+RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave
+RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range
+RLAPI float *GetWaveData(Wave wave); // Get samples data from wave as a floats array
+RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file
+RLAPI void UnloadMusicStream(Music music); // Unload music stream
+RLAPI void PlayMusicStream(Music music); // Start music playing (open stream)
+RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming
+RLAPI void StopMusicStream(Music music); // Stop music playing (close stream)
+RLAPI void PauseMusicStream(Music music); // Pause music playing
+RLAPI void ResumeMusicStream(Music music); // Resume playing paused music
+RLAPI bool IsMusicPlaying(Music music); // Check if music is playing
+RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level)
+RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level)
+RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds)
+RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)
+
+RLAPI AudioStream InitAudioStream(unsigned int sampleRate,
+ unsigned int sampleSize,
+ unsigned int channels); // Init audio stream (to stream audio pcm data)
+RLAPI void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data
+RLAPI void CloseAudioStream(AudioStream stream); // Close audio stream and free memory
+RLAPI bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill
+RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream
+RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream
+RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream
+RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream
#ifdef __cplusplus
}
diff --git a/src/rlgl.c b/src/rlgl.c
index 2b551469..e8607925 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -337,6 +337,8 @@ static int screenHeight; // Default framebuffer height
// Lighting data
static Light lights[MAX_LIGHTS]; // Lights pool
static int lightsCount = 0; // Enabled lights counter
+static int lightsLocs[MAX_LIGHTS][8]; // Lights location points in shader: 8 possible points per light:
+ // enabled, type, position, target, radius, diffuse, intensity, coneAngle
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
@@ -362,9 +364,10 @@ static void SetStereoConfig(VrDeviceInfo info);
// Set internal projection and modelview matrix depending on eyes tracking data
static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView);
-static void SetShaderLights(Shader shader); // Sets shader uniform values for lights array
+static void GetShaderLightsLocations(Shader shader); // Get shader locations for lights (up to MAX_LIGHTS)
+static void SetShaderLightsValues(Shader shader); // Set shader uniform values for lights
-static char *ReadTextFile(const char *fileName); // Read chars array from text file
+static char *ReadTextFile(const char *fileName); // Read chars array from text file
#endif
#if defined(RLGL_OCULUS_SUPPORT)
@@ -415,14 +418,14 @@ void rlMatrixMode(int mode)
}
}
-void rlFrustum(double left, double right, double bottom, double top, double near, double far)
+void rlFrustum(double left, double right, double bottom, double top, double zNear, double zFar)
{
- glFrustum(left, right, bottom, top, near, far);
+ glFrustum(left, right, bottom, top, zNear, zFar);
}
-void rlOrtho(double left, double right, double bottom, double top, double near, double far)
+void rlOrtho(double left, double right, double bottom, double top, double zNear, double zFar)
{
- glOrtho(left, right, bottom, top, near, far);
+ glOrtho(left, right, bottom, top, zNear, zFar);
}
void rlPushMatrix(void) { glPushMatrix(); }
@@ -705,7 +708,7 @@ void rlVertex3f(float x, float y, float z)
case RL_LINES:
{
// Verify that MAX_LINES_BATCH limit not reached
- if (lines.vCounter / 2 < MAX_LINES_BATCH)
+ if (lines.vCounter/2 < MAX_LINES_BATCH)
{
lines.vertices[3*lines.vCounter] = x;
lines.vertices[3*lines.vCounter + 1] = y;
@@ -719,7 +722,7 @@ void rlVertex3f(float x, float y, float z)
case RL_TRIANGLES:
{
// Verify that MAX_TRIANGLES_BATCH limit not reached
- if (triangles.vCounter / 3 < MAX_TRIANGLES_BATCH)
+ if (triangles.vCounter/3 < MAX_TRIANGLES_BATCH)
{
triangles.vertices[3*triangles.vCounter] = x;
triangles.vertices[3*triangles.vCounter + 1] = y;
@@ -733,7 +736,7 @@ void rlVertex3f(float x, float y, float z)
case RL_QUADS:
{
// Verify that MAX_QUADS_BATCH limit not reached
- if (quads.vCounter / 4 < MAX_QUADS_BATCH)
+ if (quads.vCounter/4 < MAX_QUADS_BATCH)
{
quads.vertices[3*quads.vCounter] = x;
quads.vertices[3*quads.vCounter + 1] = y;
@@ -1056,8 +1059,13 @@ void rlglInit(int width, int height)
// 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];
+#ifdef _MSC_VER
+ const char **extList = malloc(sizeof(const char *)*numExt);
+#else
+ const char *extList[numExt];
+#endif
+
for (int i = 0; i < numExt; i++) extList[i] = (char *)glGetStringi(GL_EXTENSIONS, i);
#elif defined(GRAPHICS_API_OPENGL_ES2)
@@ -1137,6 +1145,10 @@ void rlglInit(int width, int height)
if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true;
}
+#ifdef _MSC_VER
+ free(extList);
+#endif
+
#if defined(GRAPHICS_API_OPENGL_ES2)
if (vaoSupported) TraceLog(INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully");
else TraceLog(WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported");
@@ -1510,7 +1522,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height)
target.texture.id = 0;
target.texture.width = width;
target.texture.height = height;
- target.texture.format = UNCOMPRESSED_R8G8B8;
+ target.texture.format = UNCOMPRESSED_R8G8B8A8;
target.texture.mipmaps = 1;
target.depth.id = 0;
@@ -1527,7 +1539,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height)
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);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
#if defined(GRAPHICS_API_OPENGL_33)
@@ -1930,9 +1942,14 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform)
glUseProgram(material.shader.id);
// 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);
+ glUniform4f(material.shader.colDiffuseLoc, (float)material.colDiffuse.r/255, (float)material.colDiffuse.g/255, (float)material.colDiffuse.b/255, (float)material.colDiffuse.a/255);
+ // Upload to shader material.colAmbient (if available)
+ if (material.shader.colAmbientLoc != -1) glUniform4f(material.shader.colAmbientLoc, (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 (if available)
+ if (material.shader.colSpecularLoc != -1) glUniform4f(material.shader.colSpecularLoc, (float)material.colSpecular.r/255, (float)material.colSpecular.g/255, (float)material.colSpecular.b/255, (float)material.colSpecular.a/255);
+
// 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)
@@ -1941,32 +1958,35 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform)
// Calculate model-view matrix combining matModel and matView
Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates
- // 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)
+ // If not using default shader, we check for some additional location points
+ // NOTE: This method is quite inefficient... it's a temporal solution while looking for a better one
+ if (material.shader.id != defaultShader.id)
{
- // Transpose and inverse model transformations matrix for fragment normal calculations
- Matrix transInvTransform = transform;
- MatrixTranspose(&transInvTransform);
- MatrixInvert(&transInvTransform);
-
- // Send model transformations matrix to shader
- glUniformMatrix4fv(glGetUniformLocation(material.shader.id, "modelMatrix"), 1, false, MatrixToFloat(transInvTransform));
-
- // 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);
+ // Check if model matrix is located in shader and upload value
+ int modelMatrixLoc = glGetUniformLocation(material.shader.id, "modelMatrix");
+ if (modelMatrixLoc != -1)
+ {
+ // Transpose and inverse model transformations matrix for fragment normal calculations
+ Matrix transInvTransform = transform;
+ MatrixTranspose(&transInvTransform);
+ MatrixInvert(&transInvTransform);
+
+ // Send model transformations matrix to shader
+ glUniformMatrix4fv(modelMatrixLoc, 1, false, MatrixToFloat(transInvTransform));
+ }
+
+ // Check if view direction is located in shader and upload value
+ // NOTE: View matrix values m8, m9 and m10 are view direction vector axis (target - position)
+ int viewDirLoc = glGetUniformLocation(material.shader.id, "viewDir");
+ if (viewDirLoc != -1) glUniform3f(viewDirLoc, matView.m8, matView.m9, matView.m10);
+
+ // Check if glossiness is located in shader and upload value
+ int glossinessLoc = glGetUniformLocation(material.shader.id, "glossiness");
+ if (glossinessLoc != -1) glUniform1f(glossinessLoc, material.glossiness);
+
+ // Set shader lights values for enabled lights
+ // NOTE: Lights array location points are obtained on shader loading (if available)
+ if (lightsCount > 0) SetShaderLightsValues(material.shader);
}
// Set shader textures (diffuse, normal, specular)
@@ -2550,11 +2570,13 @@ void DestroyLight(Light light)
{
if (light != NULL)
{
+ int lightId = light->id;
+
// Free dynamic memory allocation
- free(lights[light->id]);
-
+ free(lights[lightId]);
+
// Remove *obj from the pointers array
- for (int i = light->id; i < lightsCount; i++)
+ for (int i = lightId; i < lightsCount; i++)
{
// Resort all the following pointers of the array
if ((i + 1) < lightsCount)
@@ -2562,7 +2584,6 @@ void DestroyLight(Light light)
lights[i] = lights[i + 1];
lights[i]->id = lights[i + 1]->id;
}
- else free(lights[i]);
}
// Decrease enabled physic objects count
@@ -2689,6 +2710,12 @@ bool IsVrDeviceReady(void)
return (vrDeviceReady || vrSimulator) && vrEnabled;
}
+// Detect if VR simulator is running
+bool IsVrSimulator(void)
+{
+ return vrSimulator;
+}
+
// Enable/Disable VR experience (device or simulator)
void ToggleVrMode(void)
{
@@ -2707,16 +2734,17 @@ void ToggleVrMode(void)
#endif
}
-// Update VR tracking (position and orientation)
-void UpdateVrTracking(void)
+// Update VR tracking (position and orientation) and camera
+void UpdateVrTracking(Camera *camera)
{
#if defined(RLGL_OCULUS_SUPPORT)
- if (vrDeviceReady) UpdateOculusTracking();
- else
-#endif
+ if (vrDeviceReady)
{
- // TODO: Use alternative inputs (mouse, keyboard) to simulate tracking data (eyes position/orientation)
+ UpdateOculusTracking();
+
+ // TODO: Update camera data (position, target, up) with tracking data
}
+#endif
}
// Begin Oculus drawing configuration
@@ -2891,11 +2919,18 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
+#ifdef _MSC_VER
+ char *log = malloc(maxLength);
+#else
char log[maxLength];
-
+#endif
glGetShaderInfoLog(vertexShader, maxLength, &length, log);
TraceLog(INFO, "%s", log);
+
+#ifdef _MSC_VER
+ free(log);
+#endif
}
else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader);
@@ -2912,11 +2947,18 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
+#ifdef _MSC_VER
+ char *log = malloc(maxLength);
+#else
char log[maxLength];
-
+#endif
glGetShaderInfoLog(fragmentShader, maxLength, &length, log);
TraceLog(INFO, "%s", log);
+
+#ifdef _MSC_VER
+ free(log);
+#endif
}
else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader);
@@ -2950,12 +2992,18 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
+#ifdef _MSC_VER
+ char *log = malloc(maxLength);
+#else
char log[maxLength];
-
+#endif
glGetProgramInfoLog(program, maxLength, &length, log);
TraceLog(INFO, "%s", log);
-
+
+#ifdef _MSC_VER
+ free(log);
+#endif
glDeleteProgram(program);
program = 0;
@@ -3070,7 +3118,7 @@ static Shader LoadStandardShader(void)
shader = GetDefaultShader();
}
#else
- shader = defaultShader;
+ shader = GetDefaultShader();
TraceLog(WARNING, "[SHDR ID %i] Standard shader not available, using default shader", shader.id);
#endif
@@ -3082,12 +3130,12 @@ static Shader LoadStandardShader(void)
static void LoadDefaultShaderLocations(Shader *shader)
{
// 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
+ // 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);
@@ -3101,10 +3149,18 @@ static void LoadDefaultShaderLocations(Shader *shader)
shader->mvpLoc = glGetUniformLocation(shader->id, "mvpMatrix");
// Get handles to GLSL uniform locations (fragment shader)
- shader->tintColorLoc = glGetUniformLocation(shader->id, "colDiffuse");
+ shader->colDiffuseLoc = glGetUniformLocation(shader->id, "colDiffuse");
+ shader->colAmbientLoc = glGetUniformLocation(shader->id, "colAmbient");
+ shader->colSpecularLoc = glGetUniformLocation(shader->id, "colSpecular");
+
shader->mapTexture0Loc = glGetUniformLocation(shader->id, "texture0");
shader->mapTexture1Loc = glGetUniformLocation(shader->id, "texture1");
shader->mapTexture2Loc = glGetUniformLocation(shader->id, "texture2");
+
+ // TODO: Try to find all expected/recognized shader locations (predefined names, must be documented)
+
+ // Try to get lights location points (if available)
+ GetShaderLightsLocations(*shader);
}
// Unload default shader
@@ -3395,7 +3451,7 @@ static void DrawDefaultBuffers(int eyesCount)
Matrix matMVP = MatrixMultiply(modelview, projection);
glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(matMVP));
- glUniform4f(currentShader.tintColorLoc, 1.0f, 1.0f, 1.0f, 1.0f);
+ glUniform4f(currentShader.colDiffuseLoc, 1.0f, 1.0f, 1.0f, 1.0f);
glUniform1i(currentShader.mapTexture0Loc, 0);
// NOTE: Additional map textures not considered for default buffers drawing
@@ -3493,7 +3549,7 @@ static void DrawDefaultBuffers(int eyesCount)
for (int i = 0; i < drawsCounter; i++)
{
quadsCount = draws[i].vertexCount/4;
- numIndicesToProcess = quadsCount*6; // Get number of Quads * 6 index by Quad
+ numIndicesToProcess = quadsCount*6; // Get number of Quads*6 index by Quad
//TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount);
@@ -3590,82 +3646,100 @@ static void UnloadDefaultBuffers(void)
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)
+// Get shader locations for lights (up to MAX_LIGHTS)
+static void GetShaderLightsLocations(Shader shader)
{
- int locPoint = -1;
- char locName[32] = "lights[x].position\0";
-
+ char locName[32] = "lights[x].\0";
+ char locNameUpdated[64];
+
for (int i = 0; i < MAX_LIGHTS; i++)
{
locName[7] = '0' + i;
+
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "enabled\0");
+ lightsLocs[i][0] = glGetUniformLocation(shader.id, locNameUpdated);
+
+ locNameUpdated[0] = '\0';
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "type\0");
+ lightsLocs[i][1] = glGetUniformLocation(shader.id, locNameUpdated);
+
+ locNameUpdated[0] = '\0';
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "position\0");
+ lightsLocs[i][2] = glGetUniformLocation(shader.id, locNameUpdated);
+
+ locNameUpdated[0] = '\0';
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "direction\0");
+ lightsLocs[i][3] = glGetUniformLocation(shader.id, locNameUpdated);
+
+ locNameUpdated[0] = '\0';
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "radius\0");
+ lightsLocs[i][4] = glGetUniformLocation(shader.id, locNameUpdated);
+
+ locNameUpdated[0] = '\0';
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "diffuse\0");
+ lightsLocs[i][5] = glGetUniformLocation(shader.id, locNameUpdated);
+
+ locNameUpdated[0] = '\0';
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "intensity\0");
+ lightsLocs[i][6] = glGetUniformLocation(shader.id, locNameUpdated);
+
+ locNameUpdated[0] = '\0';
+ strcpy(locNameUpdated, locName);
+ strcat(locNameUpdated, "coneAngle\0");
+ lightsLocs[i][7] = glGetUniformLocation(shader.id, locNameUpdated);
+ }
+}
- if (lights[i] != NULL) // Only upload registered lights data
+// Set shader uniform values for lights
+// NOTE: It would be far easier with shader UBOs but are not supported on OpenGL ES 2.0
+static void SetShaderLightsValues(Shader shader)
+{
+ for (int i = 0; i < MAX_LIGHTS; i++)
+ {
+ if (i < lightsCount)
{
- 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);
+ glUniform1i(lightsLocs[i][0], lights[i]->enabled);
+
+ glUniform1i(lightsLocs[i][1], lights[i]->type);
+ glUniform4f(lightsLocs[i][5], (float)lights[i]->diffuse.r/255, (float)lights[i]->diffuse.g/255, (float)lights[i]->diffuse.b/255, (float)lights[i]->diffuse.a/255);
+ glUniform1f(lightsLocs[i][6], 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);
+ glUniform3f(lightsLocs[i][2], lights[i]->position.x, lights[i]->position.y, lights[i]->position.z);
+ glUniform1f(lightsLocs[i][4], 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 };
+ Vector3 direction = VectorSubtract(lights[i]->target, lights[i]->position);
VectorNormalize(&direction);
- glUniform3f(locPoint, direction.x, direction.y, direction.z);
+ glUniform3f(lightsLocs[i][3], 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);
+ glUniform3f(lightsLocs[i][2], lights[i]->position.x, lights[i]->position.y, lights[i]->position.z);
- 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 };
+ Vector3 direction = VectorSubtract(lights[i]->target, lights[i]->position);
VectorNormalize(&direction);
- glUniform3f(locPoint, direction.x, direction.y, direction.z);
+ glUniform3f(lightsLocs[i][3], direction.x, direction.y, direction.z);
- memcpy(&locName[10], "coneAngle\0", strlen("coneAngle\0"));
- locPoint = GetShaderLocation(shader, locName);
- glUniform1f(locPoint, lights[i]->coneAngle);
+ glUniform1f(lightsLocs[i][7], lights[i]->coneAngle);
} break;
default: break;
}
-
- // TODO: Pass to the shader any other required data from LightData struct
}
- else // Not enabled lights
+ else
{
- memcpy(&locName[10], "enabled\0", strlen("enabled\0") + 1);
- locPoint = GetShaderLocation(shader, locName);
- glUniform1i(locPoint, 0);
+ glUniform1i(lightsLocs[i][0], 0); // Light disabled
}
}
}
diff --git a/src/rlgl.h b/src/rlgl.h
index 425871a9..3a47b4c8 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -107,7 +107,7 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
// byte type
typedef unsigned char byte;
-
+
// Color type, RGBA (32bit)
typedef struct Color {
unsigned char r;
@@ -117,7 +117,7 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
} Color;
// Texture formats (support depends on OpenGL version)
- typedef enum {
+ typedef enum {
UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha)
UNCOMPRESSED_GRAY_ALPHA,
UNCOMPRESSED_R5G6B5, // 16 bpp
@@ -157,7 +157,7 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
// Shader type (generic shader)
typedef struct Shader {
unsigned int id; // Shader program id
-
+
// Vertex attributes locations (default locations)
int vertexLoc; // Vertex attribute location point (default-location = 0)
int texcoordLoc; // Texcoord attribute location point (default-location = 1)
@@ -168,8 +168,10 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
// Uniform locations
int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader)
- int tintColorLoc; // Color uniform location point (fragment shader)
-
+ int colDiffuseLoc; // Color uniform location point (fragment shader)
+ int colAmbientLoc; // Ambient color uniform location point (fragment shader)
+ int colSpecularLoc; // Specular color 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)
@@ -185,14 +187,14 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
int mipmaps; // Mipmap levels, 1 by default
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;
-
+
// Material type
typedef struct Material {
Shader shader; // Standard shader (supports 3 map types: diffuse, normal, specular)
@@ -204,10 +206,10 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
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
@@ -225,22 +227,22 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
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;
-
+
// TraceLog message types
typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
-
+
// VR Head Mounted Display devices
typedef enum {
HMD_DEFAULT_DEVICE = 0,
@@ -332,6 +334,10 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world
unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer)
void *rlglReadTexturePixels(Texture2D texture); // Read texture pixel data
+// VR functions exposed to core module but not to raylib users
+void BeginVrDrawing(void); // Begin VR drawing configuration
+void EndVrDrawing(void); // End VR drawing process (and desktop mirror)
+
// 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
@@ -368,10 +374,8 @@ float *MatrixToFloat(Matrix mat);
void InitVrDevice(int vrDevice); // Init VR device
void CloseVrDevice(void); // Close VR device
-void UpdateVrTracking(void); // Update VR tracking (position and orientation)
-void BeginVrDrawing(void); // Begin VR drawing configuration
-void EndVrDrawing(void); // End VR drawing process (and desktop mirror)
bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready
+void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera
void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator)
// Oculus Rift API for direct access the device (no simulator)
diff --git a/src/rlua.h b/src/rlua.h
new file mode 100644
index 00000000..97d22922
--- /dev/null
+++ b/src/rlua.h
@@ -0,0 +1,4289 @@
+/**********************************************************************************************
+*
+* rlua - raylib Lua bindings
+*
+* NOTE 01:
+* The following types:
+* Color, Vector2, Vector3, Rectangle, Ray, Camera, Camera2D
+* are treated as objects with named fields, same as in C.
+*
+* Lua defines utility functions for creating those objects.
+* Usage:
+* local cl = Color(255,255,255,255)
+* local rec = Rectangle(10, 10, 100, 100)
+* local ray = Ray(Vector3(20, 20, 20), Vector3(50, 50, 50))
+* local x2 = rec.x + rec.width
+*
+* The following types:
+* Image, Texture2D, RenderTexture2D, SpriteFont
+* are immutable, and you can only read their non-pointer arguments (e.g. sprfnt.size).
+*
+* All other object types are opaque, that is, you cannot access or
+* change their fields directly.
+*
+* Remember that ALL raylib types have REFERENCE SEMANTICS in Lua.
+* There is currently no way to create a copy of an opaque object.
+*
+* NOTE 02:
+* Some raylib functions take a pointer to an array, and the size of that array.
+* The equivalent Lua functions take only an array table of the specified type UNLESS
+* it's a pointer to a large char array (e.g. for images), then it takes (and potentially returns)
+* a Lua string (without the size argument, as Lua strings are sized by default).
+*
+* NOTE 03:
+* Some raylib functions take pointers to objects to modify (e.g. ImageToPOT, etc.)
+* In Lua, these functions take values and return a new changed value, instead.
+* So, in C:
+* ImageToPOT(&img, BLACK);
+* In Lua becomes:
+* img = ImageToPOT(img, BLACK)
+*
+* Remember that functions can return multiple values, so:
+* UpdateCameraPlayer(&cam, &playerPos);
+* Vector3 vec = ResolveCollisionCubicmap(img, mapPos, &playerPos, 5.0);
+* becomes:
+* cam, playerPos = UpdateCameraPlayer(cam, playerPos)
+* vec, playerPos = ResolveCollisionCubicmap(img, mapPos, playerPos, 5)
+*
+* This is to preserve value semantics of raylib objects.
+*
+*
+* This Lua binding for raylib was originally created by Ghassan Al-Mashareqa (ghassan@ghassan.pl)
+* for raylib 1.3 and later on reviewed and updated to raylib 1.6 by Ramon Santamaria.
+*
+* Copyright (c) 2015-2016 Ghassan Al-Mashareqa and Ramon Santamaria (@raysan5)
+*
+* 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.
+*
+**********************************************************************************************/
+
+#pragma once
+
+#include "raylib.h"
+
+#define RLUA_STATIC
+#ifdef RLUA_STATIC
+ #define RLUADEF static // Functions just visible to module including this file
+#else
+ #ifdef __cplusplus
+ #define RLUADEF extern "C" // Functions visible from other files (no name mangling of functions in C++)
+ #else
+ #define RLUADEF extern // Functions visible from other files
+ #endif
+#endif
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+RLUADEF void InitLuaDevice(void); // Initialize Lua system
+RLUADEF void ExecuteLuaCode(const char *code); // Execute raylib Lua code
+RLUADEF void ExecuteLuaFile(const char *filename); // Execute raylib Lua script
+RLUADEF void CloseLuaDevice(void); // De-initialize Lua system
+
+/***********************************************************************************
+*
+* RLUA IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RLUA_IMPLEMENTATION)
+
+#include "raylib.h"
+#include "utils.h"
+#include "raymath.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define LuaPush_Image(L, img) LuaPushOpaqueTypeWithMetatable(L, img, Image)
+#define LuaPush_Texture2D(L, tex) LuaPushOpaqueTypeWithMetatable(L, tex, Texture2D)
+#define LuaPush_RenderTexture2D(L, tex) LuaPushOpaqueTypeWithMetatable(L, tex, RenderTexture2D)
+#define LuaPush_SpriteFont(L, sf) LuaPushOpaqueTypeWithMetatable(L, sf, SpriteFont)
+#define LuaPush_Mesh(L, vd) LuaPushOpaqueType(L, vd)
+#define LuaPush_Shader(L, s) LuaPushOpaqueType(L, s)
+#define LuaPush_Light(L, light) LuaPushOpaqueTypeWithMetatable(L, light, Light)
+#define LuaPush_Sound(L, snd) LuaPushOpaqueType(L, snd)
+#define LuaPush_Wave(L, wav) LuaPushOpaqueType(L, wav)
+#define LuaPush_Music(L, mus) LuaPushOpaqueType(L, mus)
+#define LuaPush_AudioStream(L, aud) LuaPushOpaqueType(L, aud)
+
+#define LuaGetArgument_string luaL_checkstring
+#define LuaGetArgument_int (int)luaL_checkinteger
+#define LuaGetArgument_unsigned (unsigned)luaL_checkinteger
+#define LuaGetArgument_char (char)luaL_checkinteger
+#define LuaGetArgument_float (float)luaL_checknumber
+#define LuaGetArgument_double luaL_checknumber
+
+#define LuaGetArgument_Image(L, img) *(Image*)LuaGetArgumentOpaqueTypeWithMetatable(L, img, "Image")
+#define LuaGetArgument_Texture2D(L, tex) *(Texture2D*)LuaGetArgumentOpaqueTypeWithMetatable(L, tex, "Texture2D")
+#define LuaGetArgument_RenderTexture2D(L, rtex) *(RenderTexture2D*)LuaGetArgumentOpaqueTypeWithMetatable(L, rtex, "RenderTexture2D")
+#define LuaGetArgument_SpriteFont(L, sf) *(SpriteFont*)LuaGetArgumentOpaqueTypeWithMetatable(L, sf, "SpriteFont")
+#define LuaGetArgument_Mesh(L, vd) *(Mesh*)LuaGetArgumentOpaqueType(L, vd)
+#define LuaGetArgument_Shader(L, s) *(Shader*)LuaGetArgumentOpaqueType(L, s)
+#define LuaGetArgument_Light(L, light) *(Light*)LuaGetArgumentOpaqueType(L, light)
+#define LuaGetArgument_Sound(L, snd) *(Sound*)LuaGetArgumentOpaqueType(L, snd)
+#define LuaGetArgument_Wave(L, wav) *(Wave*)LuaGetArgumentOpaqueType(L, wav)
+#define LuaGetArgument_Music(L, mus) *(Music*)LuaGetArgumentOpaqueType(L, mus)
+#define LuaGetArgument_AudioStream(L, aud) *(AudioStream*)LuaGetArgumentOpaqueType(L, aud)
+
+#define LuaPushOpaqueType(L, str) LuaPushOpaque(L, &str, sizeof(str))
+#define LuaPushOpaqueTypeWithMetatable(L, str, meta) LuaPushOpaqueWithMetatable(L, &str, sizeof(str), #meta)
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static lua_State* mainLuaState = 0;
+static lua_State* L = 0;
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+static void LuaPush_Color(lua_State* L, Color color);
+static void LuaPush_Vector2(lua_State* L, Vector2 vec);
+static void LuaPush_Vector3(lua_State* L, Vector3 vec);
+static void LuaPush_Quaternion(lua_State* L, Quaternion vec);
+static void LuaPush_Matrix(lua_State* L, Matrix *matrix);
+static void LuaPush_Rectangle(lua_State* L, Rectangle rect);
+static void LuaPush_Model(lua_State* L, Model mdl);
+static void LuaPush_Ray(lua_State* L, Ray ray);
+static void LuaPush_Camera(lua_State* L, Camera cam);
+
+static Vector2 LuaGetArgument_Vector2(lua_State* L, int index);
+static Vector3 LuaGetArgument_Vector3(lua_State* L, int index);
+static Quaternion LuaGetArgument_Quaternion(lua_State* L, int index);
+static Color LuaGetArgument_Color(lua_State* L, int index);
+static Rectangle LuaGetArgument_Rectangle(lua_State* L, int index);
+static Camera LuaGetArgument_Camera(lua_State* L, int index);
+static Camera2D LuaGetArgument_Camera2D(lua_State* L, int index);
+static Ray LuaGetArgument_Ray(lua_State* L, int index);
+static Matrix LuaGetArgument_Matrix(lua_State* L, int index);
+static Model LuaGetArgument_Model(lua_State* L, int index);
+
+//----------------------------------------------------------------------------------
+// rlua Helper Functions
+//----------------------------------------------------------------------------------
+static void LuaStartEnum(void)
+{
+ lua_newtable(L);
+}
+
+static void LuaSetEnum(const char *name, int value)
+{
+ lua_pushinteger(L, value);
+ lua_setfield(L, -2, name);
+}
+
+static void LuaSetEnumColor(const char *name, Color color)
+{
+ LuaPush_Color(L, color);
+ lua_setfield(L, -2, name);
+}
+
+static void LuaEndEnum(const char *name)
+{
+ lua_setglobal(L, name);
+}
+
+static void LuaPushOpaque(lua_State* L, void *ptr, size_t size)
+{
+ void *ud = lua_newuserdata(L, size);
+ memcpy(ud, ptr, size);
+}
+
+static void LuaPushOpaqueWithMetatable(lua_State* L, void *ptr, size_t size, const char *metatable_name)
+{
+ void *ud = lua_newuserdata(L, size);
+ memcpy(ud, ptr, size);
+ luaL_setmetatable(L, metatable_name);
+}
+
+static void* LuaGetArgumentOpaqueType(lua_State* L, int index)
+{
+ return lua_touserdata(L, index);
+}
+
+static void* LuaGetArgumentOpaqueTypeWithMetatable(lua_State* L, int index, const char *metatable_name)
+{
+ return luaL_checkudata(L, index, metatable_name);
+}
+
+//----------------------------------------------------------------------------------
+// LuaIndex* functions
+//----------------------------------------------------------------------------------
+static int LuaIndexImage(lua_State* L)
+{
+ Image img = LuaGetArgument_Image(L, 1);
+ const char *key = luaL_checkstring(L, 2);
+ if (!strcmp(key, "width"))
+ lua_pushinteger(L, img.width);
+ else if (!strcmp(key, "height"))
+ lua_pushinteger(L, img.height);
+ else if (!strcmp(key, "mipmaps"))
+ lua_pushinteger(L, img.mipmaps);
+ else if (!strcmp(key, "format"))
+ lua_pushinteger(L, img.format);
+ else
+ return 0;
+ return 1;
+}
+
+static int LuaIndexTexture2D(lua_State* L)
+{
+ Texture2D img = LuaGetArgument_Texture2D(L, 1);
+ const char *key = luaL_checkstring(L, 2);
+ if (!strcmp(key, "width"))
+ lua_pushinteger(L, img.width);
+ else if (!strcmp(key, "height"))
+ lua_pushinteger(L, img.height);
+ else if (!strcmp(key, "mipmaps"))
+ lua_pushinteger(L, img.mipmaps);
+ else if (!strcmp(key, "format"))
+ lua_pushinteger(L, img.format);
+ else if (!strcmp(key, "id"))
+ lua_pushinteger(L, img.id);
+ else
+ return 0;
+ return 1;
+}
+
+static int LuaIndexRenderTexture2D(lua_State* L)
+{
+ RenderTexture2D img = LuaGetArgument_RenderTexture2D(L, 1);
+ const char *key = luaL_checkstring(L, 2);
+ if (!strcmp(key, "texture"))
+ LuaPush_Texture2D(L, img.texture);
+ else if (!strcmp(key, "depth"))
+ LuaPush_Texture2D(L, img.depth);
+ else
+ return 0;
+ return 1;
+}
+
+static int LuaIndexSpriteFont(lua_State* L)
+{
+ SpriteFont img = LuaGetArgument_SpriteFont(L, 1);
+ const char *key = luaL_checkstring(L, 2);
+ if (!strcmp(key, "size"))
+ lua_pushinteger(L, img.size);
+ else if (!strcmp(key, "texture"))
+ LuaPush_Texture2D(L, img.texture);
+ else if (!strcmp(key, "numChars"))
+ lua_pushinteger(L, img.numChars);
+ else
+ return 0;
+ return 1;
+}
+
+static int LuaIndexLight(lua_State* L)
+{
+ Light light = LuaGetArgument_Light(L, 1);
+ const char *key = luaL_checkstring(L, 2);
+ if (!strcmp(key, "id"))
+ lua_pushinteger(L, light->id);
+ else if (!strcmp(key, "enabled"))
+ lua_pushboolean(L, light->enabled);
+ else if (!strcmp(key, "type"))
+ lua_pushinteger(L, light->type);
+ else if (!strcmp(key, "position"))
+ LuaPush_Vector3(L, light->position);
+ else if (!strcmp(key, "target"))
+ LuaPush_Vector3(L, light->target);
+ else if (!strcmp(key, "radius"))
+ lua_pushnumber(L, light->radius);
+ else if (!strcmp(key, "diffuse"))
+ LuaPush_Color(L, light->diffuse);
+ else if (!strcmp(key, "intensity"))
+ lua_pushnumber(L, light->intensity);
+ else if (!strcmp(key, "coneAngle"))
+ lua_pushnumber(L, light->coneAngle);
+ else
+ return 0;
+ return 1;
+}
+
+static int LuaNewIndexLight(lua_State* L)
+{
+ Light light = LuaGetArgument_Light(L, 1);
+ const char *key = luaL_checkstring(L, 2);
+ if (!strcmp(key, "id"))
+ light->id = LuaGetArgument_int(L, 3);
+ else if (!strcmp(key, "enabled"))
+ light->enabled = lua_toboolean(L, 3);
+ else if (!strcmp(key, "type"))
+ light->type = LuaGetArgument_int(L, 3);
+ else if (!strcmp(key, "position"))
+ light->position = LuaGetArgument_Vector3(L, 3);
+ else if (!strcmp(key, "target"))
+ light->target = LuaGetArgument_Vector3(L, 3);
+ else if (!strcmp(key, "radius"))
+ light->radius = LuaGetArgument_float(L, 3);
+ else if (!strcmp(key, "diffuse"))
+ light->diffuse = LuaGetArgument_Color(L, 3);
+ else if (!strcmp(key, "intensity"))
+ light->intensity = LuaGetArgument_float(L, 3);
+ else if (!strcmp(key, "coneAngle"))
+ light->coneAngle = LuaGetArgument_float(L, 3);
+ return 0;
+}
+
+static void LuaBuildOpaqueMetatables(void)
+{
+ luaL_newmetatable(L, "Image");
+ lua_pushcfunction(L, &LuaIndexImage);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, "Texture2D");
+ lua_pushcfunction(L, &LuaIndexTexture2D);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, "RenderTexture2D");
+ lua_pushcfunction(L, &LuaIndexRenderTexture2D);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, "SpriteFont");
+ lua_pushcfunction(L, &LuaIndexSpriteFont);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, "Light");
+ lua_pushcfunction(L, &LuaIndexLight);
+ lua_setfield(L, -2, "__index");
+ lua_pushcfunction(L, &LuaNewIndexLight);
+ lua_setfield(L, -2, "__newindex");
+ lua_pop(L, 1);
+}
+
+//----------------------------------------------------------------------------------
+// LuaGetArgument functions
+//----------------------------------------------------------------------------------
+
+static Vector2 LuaGetArgument_Vector2(lua_State* L, int index)
+{
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Vector2");
+ float x = (float)lua_tonumber(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Vector2");
+ float y = (float)lua_tonumber(L, -1);
+ lua_pop(L, 2);
+ return (Vector2) { x, y };
+}
+
+static Vector3 LuaGetArgument_Vector3(lua_State* L, int index)
+{
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Vector3");
+ float x = (float)lua_tonumber(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Vector3");
+ float y = (float)lua_tonumber(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "z") == LUA_TNUMBER, index, "Expected Vector3");
+ float z = (float)lua_tonumber(L, -1);
+ lua_pop(L, 3);
+ return (Vector3) { x, y, z };
+}
+
+static Quaternion LuaGetArgument_Quaternion(lua_State* L, int index)
+{
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Quaternion");
+ float x = (float)lua_tonumber(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Quaternion");
+ float y = (float)lua_tonumber(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "z") == LUA_TNUMBER, index, "Expected Quaternion");
+ float z = (float)lua_tonumber(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "w") == LUA_TNUMBER, index, "Expected Quaternion");
+ float w = (float)lua_tonumber(L, -1);
+ lua_pop(L, 4);
+ return (Quaternion) { x, y, z, w };
+}
+
+static Color LuaGetArgument_Color(lua_State* L, int index)
+{
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "r") == LUA_TNUMBER, index, "Expected Color");
+ unsigned char r = (unsigned char)lua_tointeger(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "g") == LUA_TNUMBER, index, "Expected Color");
+ unsigned char g = (unsigned char)lua_tointeger(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "b") == LUA_TNUMBER, index, "Expected Color");
+ unsigned char b = (unsigned char)lua_tointeger(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "a") == LUA_TNUMBER, index, "Expected Color");
+ unsigned char a = (unsigned char)lua_tointeger(L, -1);
+ lua_pop(L, 4);
+ return (Color) { r, g, b, a };
+}
+
+static Rectangle LuaGetArgument_Rectangle(lua_State* L, int index)
+{
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Rectangle");
+ int x = (int)lua_tointeger(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Rectangle");
+ int y = (int)lua_tointeger(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "width") == LUA_TNUMBER, index, "Expected Rectangle");
+ int w = (int)lua_tointeger(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "height") == LUA_TNUMBER, index, "Expected Rectangle");
+ int h = (int)lua_tointeger(L, -1);
+ lua_pop(L, 4);
+ return (Rectangle) { x, y, w, h };
+}
+
+static Camera LuaGetArgument_Camera(lua_State* L, int index)
+{
+ Camera result;
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "position") == LUA_TTABLE, index, "Expected Camera");
+ result.position = LuaGetArgument_Vector3(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "target") == LUA_TTABLE, index, "Expected Camera");
+ result.target = LuaGetArgument_Vector3(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "up") == LUA_TTABLE, index, "Expected Camera");
+ result.up = LuaGetArgument_Vector3(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "fovy") == LUA_TNUMBER, index, "Expected Camera");
+ result.fovy = LuaGetArgument_float(L, -1);
+ lua_pop(L, 4);
+ return result;
+}
+
+static Camera2D LuaGetArgument_Camera2D(lua_State* L, int index)
+{
+ Camera2D result;
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "offset") == LUA_TTABLE, index, "Expected Camera2D");
+ result.offset = LuaGetArgument_Vector2(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "target") == LUA_TTABLE, index, "Expected Camera2D");
+ result.target = LuaGetArgument_Vector2(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "rotation") == LUA_TNUMBER, index, "Expected Camera2D");
+ result.rotation = LuaGetArgument_float(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "zoom") == LUA_TNUMBER, index, "Expected Camera2D");
+ result.zoom = LuaGetArgument_float(L, -1);
+ lua_pop(L, 4);
+ return result;
+}
+
+static BoundingBox LuaGetArgument_BoundingBox(lua_State* L, int index)
+{
+ BoundingBox result;
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "min") == LUA_TTABLE, index, "Expected BoundingBox");
+ result.min = LuaGetArgument_Vector3(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "max") == LUA_TTABLE, index, "Expected BoundingBox");
+ result.max = LuaGetArgument_Vector3(L, -1);
+ lua_pop(L, 2);
+ return result;
+}
+
+static Ray LuaGetArgument_Ray(lua_State* L, int index)
+{
+ Ray result;
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "position") == LUA_TTABLE, index, "Expected Ray");
+ result.position = LuaGetArgument_Vector3(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "direction") == LUA_TTABLE, index, "Expected Ray");
+ result.direction = LuaGetArgument_Vector3(L, -1);
+ lua_pop(L, 2);
+ return result;
+}
+
+static Matrix LuaGetArgument_Matrix(lua_State* L, int index)
+{
+ Matrix result = { 0 };
+ float* ptr = &result.m0;
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+
+ for (int i = 0; i < 16; i++)
+ {
+ lua_geti(L, index, i+1);
+ ptr[i] = luaL_checknumber(L, -1);
+ }
+ lua_pop(L, 16);
+ return result;
+}
+
+static Material LuaGetArgument_Material(lua_State* L, int index)
+{
+ Material result;
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "shader") == LUA_TUSERDATA, index, "Expected Material");
+ result.shader = LuaGetArgument_Shader(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "texDiffuse") == LUA_TUSERDATA, index, "Expected Material");
+ result.texDiffuse = LuaGetArgument_Texture2D(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "texNormal") == LUA_TUSERDATA, index, "Expected Material");
+ result.texNormal = LuaGetArgument_Texture2D(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "texSpecular") == LUA_TUSERDATA, index, "Expected Material");
+ result.texSpecular = LuaGetArgument_Texture2D(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "colDiffuse") == LUA_TTABLE, index, "Expected Material");
+ result.colDiffuse = LuaGetArgument_Color(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "colAmbient") == LUA_TTABLE, index, "Expected Material");
+ result.colAmbient = LuaGetArgument_Color(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "colSpecular") == LUA_TTABLE, index, "Expected Material");
+ result.colSpecular = LuaGetArgument_Color(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "glossiness") == LUA_TNUMBER, index, "Expected Material");
+ result.glossiness = LuaGetArgument_float(L, -1);
+ lua_pop(L, 8);
+ return result;
+}
+
+static Model LuaGetArgument_Model(lua_State* L, int index)
+{
+ Model result;
+ index = lua_absindex(L, index); // Makes sure we use absolute indices because we push multiple values
+ luaL_argcheck(L, lua_getfield(L, index, "mesh") == LUA_TUSERDATA, index, "Expected Model");
+ result.mesh = LuaGetArgument_Mesh(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "transform") == LUA_TTABLE, index, "Expected Model");
+ result.transform = LuaGetArgument_Matrix(L, -1);
+ luaL_argcheck(L, lua_getfield(L, index, "material") == LUA_TTABLE, index, "Expected Model");
+ result.material = LuaGetArgument_Material(L, -1);
+ lua_pop(L, 3);
+ return result;
+}
+
+//----------------------------------------------------------------------------------
+// LuaPush functions
+//----------------------------------------------------------------------------------
+static void LuaPush_Color(lua_State* L, Color color)
+{
+ lua_createtable(L, 0, 4);
+ lua_pushinteger(L, color.r);
+ lua_setfield(L, -2, "r");
+ lua_pushinteger(L, color.g);
+ lua_setfield(L, -2, "g");
+ lua_pushinteger(L, color.b);
+ lua_setfield(L, -2, "b");
+ lua_pushinteger(L, color.a);
+ lua_setfield(L, -2, "a");
+}
+
+static void LuaPush_Vector2(lua_State* L, Vector2 vec)
+{
+ lua_createtable(L, 0, 2);
+ lua_pushnumber(L, vec.x);
+ lua_setfield(L, -2, "x");
+ lua_pushnumber(L, vec.y);
+ lua_setfield(L, -2, "y");
+}
+
+static void LuaPush_Vector3(lua_State* L, Vector3 vec)
+{
+ lua_createtable(L, 0, 3);
+ lua_pushnumber(L, vec.x);
+ lua_setfield(L, -2, "x");
+ lua_pushnumber(L, vec.y);
+ lua_setfield(L, -2, "y");
+ lua_pushnumber(L, vec.z);
+ lua_setfield(L, -2, "z");
+}
+
+static void LuaPush_Quaternion(lua_State* L, Quaternion vec)
+{
+ lua_createtable(L, 0, 4);
+ lua_pushnumber(L, vec.x);
+ lua_setfield(L, -2, "x");
+ lua_pushnumber(L, vec.y);
+ lua_setfield(L, -2, "y");
+ lua_pushnumber(L, vec.z);
+ lua_setfield(L, -2, "z");
+ lua_pushnumber(L, vec.w);
+ lua_setfield(L, -2, "w");
+}
+
+static void LuaPush_Matrix(lua_State* L, Matrix *matrix)
+{
+ int i;
+ lua_createtable(L, 16, 0);
+ float* num = (&matrix->m0);
+ for (i = 0; i < 16; i++)
+ {
+ lua_pushnumber(L, num[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+}
+
+static void LuaPush_Rectangle(lua_State* L, Rectangle rect)
+{
+ lua_createtable(L, 0, 4);
+ lua_pushinteger(L, rect.x);
+ lua_setfield(L, -2, "x");
+ lua_pushinteger(L, rect.y);
+ lua_setfield(L, -2, "y");
+ lua_pushinteger(L, rect.width);
+ lua_setfield(L, -2, "width");
+ lua_pushinteger(L, rect.height);
+ lua_setfield(L, -2, "height");
+}
+
+static void LuaPush_Ray(lua_State* L, Ray ray)
+{
+ lua_createtable(L, 0, 2);
+ LuaPush_Vector3(L, ray.position);
+ lua_setfield(L, -2, "position");
+ LuaPush_Vector3(L, ray.direction);
+ lua_setfield(L, -2, "direction");
+}
+
+static void LuaPush_BoundingBox(lua_State* L, BoundingBox bb)
+{
+ lua_createtable(L, 0, 2);
+ LuaPush_Vector3(L, bb.min);
+ lua_setfield(L, -2, "min");
+ LuaPush_Vector3(L, bb.max);
+ lua_setfield(L, -2, "max");
+}
+
+static void LuaPush_Camera(lua_State* L, Camera cam)
+{
+ lua_createtable(L, 0, 4);
+ LuaPush_Vector3(L, cam.position);
+ lua_setfield(L, -2, "position");
+ LuaPush_Vector3(L, cam.target);
+ lua_setfield(L, -2, "target");
+ LuaPush_Vector3(L, cam.up);
+ lua_setfield(L, -2, "up");
+ lua_pushnumber(L, cam.fovy);
+ lua_setfield(L, -2, "fovy");
+}
+
+static void LuaPush_Camera2D(lua_State* L, Camera2D cam)
+{
+ lua_createtable(L, 0, 4);
+ LuaPush_Vector2(L, cam.offset);
+ lua_setfield(L, -2, "offset");
+ LuaPush_Vector2(L, cam.target);
+ lua_setfield(L, -2, "target");
+ lua_pushnumber(L, cam.rotation);
+ lua_setfield(L, -2, "rotation");
+ lua_pushnumber(L, cam.zoom);
+ lua_setfield(L, -2, "zoom");
+}
+
+static void LuaPush_Material(lua_State* L, Material mat)
+{
+ lua_createtable(L, 0, 8);
+ LuaPush_Shader(L, mat.shader);
+ lua_setfield(L, -2, "shader");
+ LuaPush_Texture2D(L, mat.texDiffuse);
+ lua_setfield(L, -2, "texDiffuse");
+ LuaPush_Texture2D(L, mat.texNormal);
+ lua_setfield(L, -2, "texNormal");
+ LuaPush_Texture2D(L, mat.texSpecular);
+ lua_setfield(L, -2, "texSpecular");
+ LuaPush_Color(L, mat.colDiffuse);
+ lua_setfield(L, -2, "colDiffuse");
+ LuaPush_Color(L, mat.colAmbient);
+ lua_setfield(L, -2, "colAmbient");
+ LuaPush_Color(L, mat.colSpecular);
+ lua_setfield(L, -2, "colSpecular");
+ lua_pushnumber(L, mat.glossiness);
+ lua_setfield(L, -2, "glossiness");
+}
+
+static void LuaPush_Model(lua_State* L, Model mdl)
+{
+ lua_createtable(L, 0, 4);
+ LuaPush_Mesh(L, mdl.mesh);
+ lua_setfield(L, -2, "mesh");
+ LuaPush_Matrix(L, &mdl.transform);
+ lua_setfield(L, -2, "transform");
+ LuaPush_Material(L, mdl.material);
+ lua_setfield(L, -2, "material");
+}
+
+//----------------------------------------------------------------------------------
+// raylib Lua Structure constructors
+//----------------------------------------------------------------------------------
+static int lua_Color(lua_State* L)
+{
+ LuaPush_Color(L, (Color) { (unsigned char)luaL_checkinteger(L, 1), (unsigned char)luaL_checkinteger(L, 2), (unsigned char)luaL_checkinteger(L, 3), (unsigned char)luaL_checkinteger(L, 4) });
+ return 1;
+}
+
+static int lua_Vector2(lua_State* L)
+{
+ LuaPush_Vector2(L, (Vector2) { (float)luaL_checknumber(L, 1), (float)luaL_checknumber(L, 2) });
+ return 1;
+}
+
+static int lua_Vector3(lua_State* L)
+{
+ LuaPush_Vector3(L, (Vector3) { (float)luaL_checknumber(L, 1), (float)luaL_checknumber(L, 2), (float)luaL_checknumber(L, 3) });
+ return 1;
+}
+
+static int lua_Quaternion(lua_State* L)
+{
+ LuaPush_Quaternion(L, (Quaternion) { (float)luaL_checknumber(L, 1), (float)luaL_checknumber(L, 2), (float)luaL_checknumber(L, 3), (float)luaL_checknumber(L, 4) });
+ return 1;
+}
+
+static int lua_Rectangle(lua_State* L)
+{
+ LuaPush_Rectangle(L, (Rectangle) { (int)luaL_checkinteger(L, 1), (int)luaL_checkinteger(L, 2), (int)luaL_checkinteger(L, 3), (int)luaL_checkinteger(L, 4) });
+ return 1;
+}
+
+static int lua_Ray(lua_State* L)
+{
+ Vector2 pos = LuaGetArgument_Vector2(L, 1);
+ Vector2 dir = LuaGetArgument_Vector2(L, 2);
+ LuaPush_Ray(L, (Ray) { { pos.x, pos.y }, { dir.x, dir.y } });
+ return 1;
+}
+
+static int lua_BoundingBox(lua_State* L)
+{
+ Vector3 min = LuaGetArgument_Vector3(L, 1);
+ Vector3 max = LuaGetArgument_Vector3(L, 2);
+ LuaPush_BoundingBox(L, (BoundingBox) { { min.x, min.y, min.z }, { max.x, max.y, max.z } });
+ return 1;
+}
+
+static int lua_Camera(lua_State* L)
+{
+ Vector3 pos = LuaGetArgument_Vector3(L, 1);
+ Vector3 tar = LuaGetArgument_Vector3(L, 2);
+ Vector3 up = LuaGetArgument_Vector3(L, 3);
+ float fovy = LuaGetArgument_float(L, 4);
+ LuaPush_Camera(L, (Camera) { { pos.x, pos.y, pos.z }, { tar.x, tar.y, tar.z }, { up.x, up.y, up.z }, fovy });
+ return 1;
+}
+
+static int lua_Camera2D(lua_State* L)
+{
+ Vector2 off = LuaGetArgument_Vector2(L, 1);
+ Vector2 tar = LuaGetArgument_Vector2(L, 2);
+ float rot = LuaGetArgument_float(L, 3);
+ float zoom = LuaGetArgument_float(L, 4);
+ LuaPush_Camera2D(L, (Camera2D) { { off.x, off.y }, { tar.x, tar.y }, rot, zoom });
+ return 1;
+}
+
+/*************************************************************************************
+* raylib Lua Functions Bindings
+**************************************************************************************/
+
+//------------------------------------------------------------------------------------
+// raylib [core] module functions - Window and Graphics Device
+//------------------------------------------------------------------------------------
+int lua_InitWindow(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ const char * arg3 = LuaGetArgument_string(L, 3);
+ InitWindow(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_CloseWindow(lua_State* L)
+{
+ CloseWindow();
+ return 0;
+}
+
+int lua_WindowShouldClose(lua_State* L)
+{
+ bool result = WindowShouldClose();
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsWindowMinimized(lua_State* L)
+{
+ bool result = IsWindowMinimized();
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_ToggleFullscreen(lua_State* L)
+{
+ ToggleFullscreen();
+ return 0;
+}
+
+int lua_GetScreenWidth(lua_State* L)
+{
+ int result = GetScreenWidth();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetScreenHeight(lua_State* L)
+{
+ int result = GetScreenHeight();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_ShowCursor(lua_State* L)
+{
+ ShowCursor();
+ return 0;
+}
+
+int lua_HideCursor(lua_State* L)
+{
+ HideCursor();
+ return 0;
+}
+
+int lua_IsCursorHidden(lua_State* L)
+{
+ bool result = IsCursorHidden();
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_EnableCursor(lua_State* L)
+{
+ EnableCursor();
+ return 0;
+}
+
+int lua_DisableCursor(lua_State* L)
+{
+ DisableCursor();
+ return 0;
+}
+
+int lua_ClearBackground(lua_State* L)
+{
+ Color arg1 = LuaGetArgument_Color(L, 1);
+ ClearBackground(arg1);
+ return 0;
+}
+
+int lua_BeginDrawing(lua_State* L)
+{
+ BeginDrawing();
+ return 0;
+}
+
+int lua_EndDrawing(lua_State* L)
+{
+ EndDrawing();
+ return 0;
+}
+
+int lua_Begin2dMode(lua_State* L)
+{
+ Camera2D arg1 = LuaGetArgument_Camera2D(L, 1);
+ Begin2dMode(arg1);
+ return 0;
+}
+
+int lua_End2dMode(lua_State* L)
+{
+ End2dMode();
+ return 0;
+}
+
+int lua_Begin3dMode(lua_State* L)
+{
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ Begin3dMode(arg1);
+ return 0;
+}
+
+int lua_End3dMode(lua_State* L)
+{
+ End3dMode();
+ return 0;
+}
+
+int lua_BeginTextureMode(lua_State* L)
+{
+ RenderTexture2D arg1 = LuaGetArgument_RenderTexture2D(L, 1);
+ BeginTextureMode(arg1);
+ return 0;
+}
+
+int lua_EndTextureMode(lua_State* L)
+{
+ EndTextureMode();
+ return 0;
+}
+
+int lua_GetMouseRay(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Camera arg2 = LuaGetArgument_Camera(L, 2);
+ Ray result = GetMouseRay(arg1, arg2);
+ LuaPush_Ray(L, result);
+ return 1;
+}
+
+int lua_GetWorldToScreen(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Camera arg2 = LuaGetArgument_Camera(L, 2);
+ Vector2 result = GetWorldToScreen(arg1, arg2);
+ LuaPush_Vector2(L, result);
+ return 1;
+}
+
+int lua_GetCameraMatrix(lua_State* L)
+{
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ Matrix result = GetCameraMatrix(arg1);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+#if defined(PLATFORM_WEB)
+
+static int LuaDrawLoopFunc;
+
+static void LuaDrawLoop()
+{
+ lua_rawgeti(L, LUA_REGISTRYINDEX, LuaDrawLoopFunc);
+ lua_call(L, 0, 0);
+}
+
+int lua_SetDrawingLoop(lua_State* L)
+{
+ luaL_argcheck(L, lua_isfunction(L, 1), 1, "Loop function expected");
+ lua_pushvalue(L, 1);
+ LuaDrawLoopFunc = luaL_ref(L, LUA_REGISTRYINDEX);
+ SetDrawingLoop(&LuaDrawLoop);
+ return 0;
+}
+
+#else
+
+int lua_SetTargetFPS(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ SetTargetFPS(arg1);
+ return 0;
+}
+#endif
+
+int lua_GetFPS(lua_State* L)
+{
+ float result = GetFPS();
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_GetFrameTime(lua_State* L)
+{
+ float result = GetFrameTime();
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_GetColor(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ Color result = GetColor(arg1);
+ LuaPush_Color(L, result);
+ return 1;
+}
+
+int lua_GetHexValue(lua_State* L)
+{
+ Color arg1 = LuaGetArgument_Color(L, 1);
+ int result = GetHexValue(arg1);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_ColorToFloat(lua_State* L)
+{
+ Color arg1 = LuaGetArgument_Color(L, 1);
+ float * result = ColorToFloat(arg1);
+ lua_createtable(L, 4, 0);
+ for (int i = 0; i < 4; i++)
+ {
+ lua_pushnumber(L, result[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ free(result);
+ return 1;
+}
+
+int lua_VectorToFloat(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float * result = VectorToFloat(arg1);
+ lua_createtable(L, 3, 0);
+ for (int i = 0; i < 3; i++)
+ {
+ lua_pushnumber(L, result[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ free(result);
+ return 1;
+}
+
+int lua_MatrixToFloat(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ float * result = MatrixToFloat(arg1);
+ lua_createtable(L, 16, 0);
+ for (int i = 0; i < 16; i++)
+ {
+ lua_pushnumber(L, result[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ free(result);
+ return 1;
+}
+
+int lua_GetRandomValue(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int result = GetRandomValue(arg1, arg2);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_Fade(lua_State* L)
+{
+ Color arg1 = LuaGetArgument_Color(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Color result = Fade(arg1, arg2);
+ LuaPush_Color(L, result);
+ return 1;
+}
+
+int lua_SetConfigFlags(lua_State* L)
+{
+ char arg1 = LuaGetArgument_char(L, 1);
+ SetConfigFlags(arg1);
+ return 0;
+}
+
+int lua_ShowLogo(lua_State* L)
+{
+ ShowLogo();
+ return 0;
+}
+
+int lua_IsFileDropped(lua_State* L)
+{
+ bool result = IsFileDropped();
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_GetDroppedFiles(lua_State* L)
+{
+ int count = 0;
+ char ** result = GetDroppedFiles(&count);
+ lua_createtable(L, count, 0);
+ for (int i = 0; i < count; i++)
+ {
+ lua_pushstring(L, result[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ return 1;
+}
+
+int lua_ClearDroppedFiles(lua_State* L)
+{
+ ClearDroppedFiles();
+ return 0;
+}
+
+int lua_StorageSaveValue(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ StorageSaveValue(arg1, arg2);
+ return 0;
+}
+
+int lua_StorageLoadValue(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int result = StorageLoadValue(arg1);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [core] module functions - Input Handling
+//------------------------------------------------------------------------------------
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
+int lua_IsKeyPressed(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsKeyPressed(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsKeyDown(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsKeyDown(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsKeyReleased(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsKeyReleased(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsKeyUp(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsKeyUp(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_GetKeyPressed(lua_State* L)
+{
+ int result = GetKeyPressed();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_SetExitKey(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ SetExitKey(arg1);
+ return 0;
+}
+
+int lua_IsGamepadAvailable(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsGamepadAvailable(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_GetGamepadAxisMovement(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ float result = GetGamepadAxisMovement(arg1, arg2);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_IsGamepadButtonPressed(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ bool result = IsGamepadButtonPressed(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsGamepadButtonDown(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ bool result = IsGamepadButtonDown(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsGamepadButtonReleased(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ bool result = IsGamepadButtonReleased(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsGamepadButtonUp(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ bool result = IsGamepadButtonUp(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+#endif
+
+int lua_IsMouseButtonPressed(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsMouseButtonPressed(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsMouseButtonDown(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsMouseButtonDown(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsMouseButtonReleased(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsMouseButtonReleased(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsMouseButtonUp(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsMouseButtonUp(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_GetMouseX(lua_State* L)
+{
+ int result = GetMouseX();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetMouseY(lua_State* L)
+{
+ int result = GetMouseY();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetMousePosition(lua_State* L)
+{
+ Vector2 result = GetMousePosition();
+ LuaPush_Vector2(L, result);
+ return 1;
+}
+
+int lua_SetMousePosition(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ SetMousePosition(arg1);
+ return 0;
+}
+
+int lua_GetMouseWheelMove(lua_State* L)
+{
+ int result = GetMouseWheelMove();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetTouchX(lua_State* L)
+{
+ int result = GetTouchX();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetTouchY(lua_State* L)
+{
+ int result = GetTouchY();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetTouchPosition(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ Vector2 result = GetTouchPosition(arg1);
+ LuaPush_Vector2(L, result);
+ return 1;
+}
+
+
+#if defined(PLATFORM_ANDROID)
+int lua_IsButtonPressed(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsButtonPressed(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsButtonDown(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsButtonDown(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_IsButtonReleased(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsButtonReleased(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+#endif
+
+//------------------------------------------------------------------------------------
+// raylib [gestures] module functions - Gestures and Touch Handling
+//------------------------------------------------------------------------------------
+int lua_SetGesturesEnabled(lua_State* L)
+{
+ unsigned arg1 = LuaGetArgument_unsigned(L, 1);
+ SetGesturesEnabled(arg1);
+ return 0;
+}
+
+int lua_IsGestureDetected(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ bool result = IsGestureDetected(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_GetTouchPointsCount(lua_State* L)
+{
+ int result = GetTouchPointsCount();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetGestureDetected(lua_State* L)
+{
+ int result = GetGestureDetected();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetGestureHoldDuration(lua_State* L)
+{
+ int result = GetGestureHoldDuration();
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_GetGestureDragVector(lua_State* L)
+{
+ Vector2 result = GetGestureDragVector();
+ LuaPush_Vector2(L, result);
+ return 1;
+}
+
+int lua_GetGestureDragAngle(lua_State* L)
+{
+ float result = GetGestureDragAngle();
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_GetGesturePinchVector(lua_State* L)
+{
+ Vector2 result = GetGesturePinchVector();
+ LuaPush_Vector2(L, result);
+ return 1;
+}
+
+int lua_GetGesturePinchAngle(lua_State* L)
+{
+ float result = GetGesturePinchAngle();
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [camera] module functions - Camera System
+//------------------------------------------------------------------------------------
+int lua_SetCameraMode(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ SetCameraMode(arg1);
+ return 0;
+}
+
+int lua_UpdateCamera(lua_State* L)
+{
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ UpdateCamera(&arg1);
+ LuaPush_Camera(L, arg1);
+ return 1;
+}
+
+int lua_UpdateCameraPlayer(lua_State* L)
+{
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ UpdateCameraPlayer(&arg1, &arg2);
+ LuaPush_Camera(L, arg1);
+ LuaPush_Vector3(L, arg2);
+ return 2;
+}
+
+int lua_SetCameraPosition(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ SetCameraPosition(arg1);
+ return 0;
+}
+
+int lua_SetCameraTarget(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ SetCameraTarget(arg1);
+ return 0;
+}
+
+int lua_SetCameraFovy(lua_State* L)
+{
+ float arg1 = LuaGetArgument_float(L, 1);
+ SetCameraFovy(arg1);
+ return 0;
+}
+
+int lua_SetCameraPanControl(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ SetCameraPanControl(arg1);
+ return 0;
+}
+
+int lua_SetCameraAltControl(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ SetCameraAltControl(arg1);
+ return 0;
+}
+
+int lua_SetCameraSmoothZoomControl(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ SetCameraSmoothZoomControl(arg1);
+ return 0;
+}
+
+int lua_SetCameraMoveControls(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ int arg6 = LuaGetArgument_int(L, 6);
+ SetCameraMoveControls(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_SetCameraMouseSensitivity(lua_State* L)
+{
+ float arg1 = LuaGetArgument_float(L, 1);
+ SetCameraMouseSensitivity(arg1);
+ return 0;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [shapes] module functions - Basic Shapes Drawing
+//------------------------------------------------------------------------------------
+int lua_DrawPixel(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawPixel(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawPixelV(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Color arg2 = LuaGetArgument_Color(L, 2);
+ DrawPixelV(arg1, arg2);
+ return 0;
+}
+
+int lua_DrawLine(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawLine(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawLineV(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawLineV(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawCircle(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawCircle(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawCircleGradient(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawCircleGradient(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawCircleV(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawCircleV(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawCircleLines(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawCircleLines(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawRectangle(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawRectangle(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawRectangleRec(lua_State* L)
+{
+ Rectangle arg1 = LuaGetArgument_Rectangle(L, 1);
+ Color arg2 = LuaGetArgument_Color(L, 2);
+ DrawRectangleRec(arg1, arg2);
+ return 0;
+}
+
+int lua_DrawRectangleGradient(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawRectangleGradient(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_DrawRectangleV(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawRectangleV(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawRectangleLines(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawRectangleLines(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawTriangle(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ Vector2 arg3 = LuaGetArgument_Vector2(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawTriangle(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawTriangleLines(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ Vector2 arg3 = LuaGetArgument_Vector2(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawTriangleLines(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawPoly(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawPoly(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+#define GET_TABLE(type, name, index) \
+ type* name = 0; \
+ size_t name##_size = 0; \
+ { \
+ size_t sz = 0; \
+ luaL_checktype(L, index, LUA_TTABLE); \
+ lua_pushnil(L); \
+ while (lua_next(L, index)) { \
+ LuaGetArgument_##type(L, -1); \
+ sz++; \
+ lua_pop(L, 1); \
+ } \
+ name = calloc(sz, sizeof(type)); \
+ sz = 0; \
+ lua_pushnil(L); \
+ while (lua_next(L, index)) { \
+ name[sz] = LuaGetArgument_##type(L, -1); \
+ sz++; \
+ lua_pop(L, 1); \
+ } \
+ lua_pop(L, 1); \
+ name##_size = sz; \
+ }
+
+
+int lua_DrawPolyEx(lua_State* L)
+{
+ GET_TABLE(Vector2, arg1, 1);
+ Color arg2 = LuaGetArgument_Color(L, 2);
+ DrawPolyEx(arg1, arg1_size, arg2);
+ free(arg1);
+ return 0;
+}
+
+int lua_DrawPolyExLines(lua_State* L)
+{
+ GET_TABLE(Vector2, arg1, 1);
+ Color arg2 = LuaGetArgument_Color(L, 2);
+ DrawPolyExLines(arg1, arg1_size, arg2);
+ free(arg1);
+ return 0;
+}
+
+int lua_CheckCollisionRecs(lua_State* L)
+{
+ Rectangle arg1 = LuaGetArgument_Rectangle(L, 1);
+ Rectangle arg2 = LuaGetArgument_Rectangle(L, 2);
+ bool result = CheckCollisionRecs(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionCircles(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Vector2 arg3 = LuaGetArgument_Vector2(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ bool result = CheckCollisionCircles(arg1, arg2, arg3, arg4);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionCircleRec(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Rectangle arg3 = LuaGetArgument_Rectangle(L, 3);
+ bool result = CheckCollisionCircleRec(arg1, arg2, arg3);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_GetCollisionRec(lua_State* L)
+{
+ Rectangle arg1 = LuaGetArgument_Rectangle(L, 1);
+ Rectangle arg2 = LuaGetArgument_Rectangle(L, 2);
+ Rectangle result = GetCollisionRec(arg1, arg2);
+ LuaPush_Rectangle(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionPointRec(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Rectangle arg2 = LuaGetArgument_Rectangle(L, 2);
+ bool result = CheckCollisionPointRec(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionPointCircle(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ bool result = CheckCollisionPointCircle(arg1, arg2, arg3);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionPointTriangle(lua_State* L)
+{
+ Vector2 arg1 = LuaGetArgument_Vector2(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ Vector2 arg3 = LuaGetArgument_Vector2(L, 3);
+ Vector2 arg4 = LuaGetArgument_Vector2(L, 4);
+ bool result = CheckCollisionPointTriangle(arg1, arg2, arg3, arg4);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [textures] module functions - Texture Loading and Drawing
+//------------------------------------------------------------------------------------
+int lua_LoadImage(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Image result = LoadImage(arg1);
+ LuaPush_Image(L, result);
+ return 1;
+}
+
+int lua_LoadImageEx(lua_State* L)
+{
+ GET_TABLE(Color, arg1, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ Image result = LoadImageEx(arg1, arg2, arg3); // ISSUE: #3 number expected, got no value
+ LuaPush_Image(L, result);
+ free(arg1);
+ return 1;
+}
+
+int lua_LoadImageRaw(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ Image result = LoadImageRaw(arg1, arg2, arg3, arg4, arg5);
+ LuaPush_Image(L, result);
+ return 1;
+}
+
+int lua_LoadImageFromRES(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ Image result = LoadImageFromRES(arg1, arg2);
+ LuaPush_Image(L, result);
+ return 1;
+}
+
+int lua_LoadTexture(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Texture2D result = LoadTexture(arg1);
+ LuaPush_Texture2D(L, result);
+ return 1;
+}
+
+int lua_LoadTextureEx(lua_State* L)
+{
+ void * arg1 = (char *)LuaGetArgument_string(L, 1); // NOTE: getting argument as string?
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Texture2D result = LoadTextureEx(arg1, arg2, arg3, arg4);
+ LuaPush_Texture2D(L, result);
+ return 1;
+}
+
+int lua_LoadTextureFromRES(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ Texture2D result = LoadTextureFromRES(arg1, arg2);
+ LuaPush_Texture2D(L, result);
+ return 1;
+}
+
+int lua_LoadTextureFromImage(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Texture2D result = LoadTextureFromImage(arg1);
+ LuaPush_Texture2D(L, result);
+ return 1;
+}
+
+int lua_LoadRenderTexture(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ RenderTexture2D result = LoadRenderTexture(arg1, arg2);
+ LuaPush_RenderTexture2D(L, result);
+ return 1;
+}
+
+int lua_UnloadImage(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ UnloadImage(arg1);
+ return 0;
+}
+
+int lua_UnloadTexture(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ UnloadTexture(arg1);
+ return 0;
+}
+
+int lua_UnloadRenderTexture(lua_State* L)
+{
+ RenderTexture2D arg1 = LuaGetArgument_RenderTexture2D(L, 1);
+ UnloadRenderTexture(arg1);
+ return 0;
+}
+
+int lua_GetImageData(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Color * result = GetImageData(arg1);
+ lua_createtable(L, arg1.width*arg1.height, 0);
+ for (int i = 0; i < arg1.width*arg1.height; i++)
+ {
+ LuaPush_Color(L, result[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ free(result);
+ return 1;
+}
+
+int lua_GetTextureData(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ Image result = GetTextureData(arg1);
+ LuaPush_Image(L, result);
+ return 1;
+}
+
+int lua_ImageToPOT(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Color arg2 = LuaGetArgument_Color(L, 2);
+ ImageToPOT(&arg1, arg2);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageFormat(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ ImageFormat(&arg1, arg2);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageDither(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ ImageDither(&arg1, arg2, arg3, arg4, arg5);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageCopy(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Image result = ImageCopy(arg1);
+ LuaPush_Image(L, result);
+ return 1;
+}
+
+int lua_ImageCrop(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Rectangle arg2 = LuaGetArgument_Rectangle(L, 2);
+ ImageCrop(&arg1, arg2);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageResize(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ ImageResize(&arg1, arg2, arg3);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageResizeNN(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ ImageResizeNN(&arg1, arg2, arg3);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageText(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ Image result = ImageText(arg1, arg2, arg3);
+ LuaPush_Image(L, result);
+ return 1;
+}
+
+int lua_ImageTextEx(lua_State* L)
+{
+ SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1);
+ const char * arg2 = LuaGetArgument_string(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ Image result = ImageTextEx(arg1, arg2, arg3, arg4, arg5);
+ LuaPush_Image(L, result);
+ return 1;
+}
+
+int lua_ImageDraw(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Image arg2 = LuaGetArgument_Image(L, 2);
+ Rectangle arg3 = LuaGetArgument_Rectangle(L, 3);
+ Rectangle arg4 = LuaGetArgument_Rectangle(L, 4);
+ ImageDraw(&arg1, arg2, arg3, arg4);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageDrawText(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ const char * arg3 = LuaGetArgument_string(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ ImageDrawText(&arg1, arg2, arg3, arg4, arg5);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageDrawTextEx(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ SpriteFont arg3 = LuaGetArgument_SpriteFont(L, 3);
+ const char * arg4 = LuaGetArgument_string(L, 4);
+ float arg5 = LuaGetArgument_float(L, 5);
+ int arg6 = LuaGetArgument_int(L, 6);
+ Color arg7 = LuaGetArgument_Color(L, 7);
+ ImageDrawTextEx(&arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageFlipVertical(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ ImageFlipVertical(&arg1);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageFlipHorizontal(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ ImageFlipHorizontal(&arg1);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageColorTint(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Color arg2 = LuaGetArgument_Color(L, 2);
+ ImageColorTint(&arg1, arg2);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageColorInvert(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ ImageColorInvert(&arg1);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageColorGrayscale(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ ImageColorGrayscale(&arg1);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageColorContrast(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ ImageColorContrast(&arg1, arg2);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_ImageColorBrightness(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ ImageColorBrightness(&arg1, arg2);
+ LuaPush_Image(L, arg1);
+ return 1;
+}
+
+int lua_GenTextureMipmaps(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ GenTextureMipmaps(arg1);
+ return 0;
+}
+
+int lua_UpdateTexture(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ void * arg2 = (char *)LuaGetArgument_string(L, 2); // NOTE: Getting (void *) as string?
+ UpdateTexture(arg1, arg2); // ISSUE: #2 string expected, got table -> GetImageData() returns a table!
+ return 0;
+}
+
+int lua_DrawTexture(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawTexture(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawTextureV(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawTextureV(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawTextureEx(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawTextureEx(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawTextureRec(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ Rectangle arg2 = LuaGetArgument_Rectangle(L, 2);
+ Vector2 arg3 = LuaGetArgument_Vector2(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawTextureRec(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawTexturePro(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ Rectangle arg2 = LuaGetArgument_Rectangle(L, 2);
+ Rectangle arg3 = LuaGetArgument_Rectangle(L, 3);
+ Vector2 arg4 = LuaGetArgument_Vector2(L, 4);
+ float arg5 = LuaGetArgument_float(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawTexturePro(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [text] module functions - Font Loading and Text Drawing
+//------------------------------------------------------------------------------------
+int lua_GetDefaultFont(lua_State* L)
+{
+ SpriteFont result = GetDefaultFont();
+ LuaPush_SpriteFont(L, result);
+ return 1;
+}
+
+int lua_LoadSpriteFont(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ SpriteFont result = LoadSpriteFont(arg1);
+ LuaPush_SpriteFont(L, result);
+ return 1;
+}
+
+int lua_UnloadSpriteFont(lua_State* L)
+{
+ SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1);
+ UnloadSpriteFont(arg1);
+ return 0;
+}
+
+int lua_DrawText(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawText(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawTextEx(lua_State* L)
+{
+ SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1);
+ const char * arg2 = LuaGetArgument_string(L, 2);
+ Vector2 arg3 = LuaGetArgument_Vector2(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawTextEx(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_MeasureText(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int result = MeasureText(arg1, arg2);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_MeasureTextEx(lua_State* L)
+{
+ SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1);
+ const char * arg2 = LuaGetArgument_string(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Vector2 result = MeasureTextEx(arg1, arg2, arg3, arg4);
+ LuaPush_Vector2(L, result);
+ return 1;
+}
+
+int lua_DrawFPS(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ DrawFPS(arg1, arg2);
+ return 0;
+}
+
+// NOTE: FormatText() can be replaced by Lua function: string.format()
+// NOTE: SubText() can be replaced by Lua function: string.sub()
+
+//------------------------------------------------------------------------------------
+// raylib [models] module functions - Basic 3d Shapes Drawing Functions
+//------------------------------------------------------------------------------------
+int lua_DrawLine3D(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawLine3D(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawCircle3D(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Vector3 arg4 = LuaGetArgument_Vector3(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawCircle3D(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawCube(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawCube(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawCubeV(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawCubeV(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawCubeWires(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawCubeWires(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawCubeTexture(lua_State* L)
+{
+ Texture2D arg1 = LuaGetArgument_Texture2D(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ float arg5 = LuaGetArgument_float(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawCubeTexture(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_DrawSphere(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawSphere(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawSphereEx(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawSphereEx(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawSphereWires(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawSphereWires(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawCylinder(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawCylinder(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_DrawCylinderWires(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawCylinderWires(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_DrawPlane(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector2 arg2 = LuaGetArgument_Vector2(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ DrawPlane(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_DrawRay(lua_State* L)
+{
+ Ray arg1 = LuaGetArgument_Ray(L, 1);
+ Color arg2 = LuaGetArgument_Color(L, 2);
+ DrawRay(arg1, arg2);
+ return 0;
+}
+
+int lua_DrawGrid(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ DrawGrid(arg1, arg2);
+ return 0;
+}
+
+int lua_DrawGizmo(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ DrawGizmo(arg1);
+ return 0;
+}
+
+int lua_DrawLight(lua_State* L)
+{
+ Light arg1 = LuaGetArgument_Light(L, 1);
+ DrawLight(arg1);
+ return 0;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [models] module functions
+//------------------------------------------------------------------------------------
+int lua_LoadModel(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Model result = LoadModel(arg1);
+ LuaPush_Model(L, result);
+ return 1;
+}
+
+int lua_LoadModelEx(lua_State* L)
+{
+ Mesh arg1 = LuaGetArgument_Mesh(L, 1);
+ bool arg2 = LuaGetArgument_int(L, 2);
+ Model result = LoadModelEx(arg1, arg2);
+ LuaPush_Model(L, result);
+ return 1;
+}
+
+int lua_LoadModelFromRES(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ Model result = LoadModelFromRES(arg1, arg2);
+ LuaPush_Model(L, result);
+ return 1;
+}
+
+int lua_LoadHeightmap(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Model result = LoadHeightmap(arg1, arg2);
+ LuaPush_Model(L, result);
+ return 1;
+}
+
+int lua_LoadCubicmap(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Model result = LoadCubicmap(arg1);
+ LuaPush_Model(L, result);
+ return 1;
+}
+
+int lua_UnloadModel(lua_State* L)
+{
+ Model arg1 = LuaGetArgument_Model(L, 1);
+ UnloadModel(arg1);
+ return 0;
+}
+
+// TODO: GenMesh*() functionality (not ready yet on raylib 1.6)
+
+int lua_LoadMaterial(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Material result = LoadMaterial(arg1);
+ LuaPush_Material(L, result);
+ return 1;
+}
+
+int lua_LoadDefaultMaterial(lua_State* L)
+{
+ Material result = LoadDefaultMaterial();
+ LuaPush_Material(L, result);
+ return 1;
+}
+
+int lua_LoadStandardMaterial(lua_State* L)
+{
+ Material result = LoadStandardMaterial();
+ LuaPush_Material(L, result);
+ return 1;
+}
+
+int lua_UnloadMaterial(lua_State* L)
+{
+ Material arg1 = LuaGetArgument_Material(L, 1);
+ UnloadMaterial(arg1);
+ return 0;
+}
+
+int lua_DrawModel(lua_State* L)
+{
+ Model arg1 = LuaGetArgument_Model(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawModel(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawModelEx(lua_State* L)
+{
+ Model arg1 = LuaGetArgument_Model(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Vector3 arg5 = LuaGetArgument_Vector3(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawModelEx(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_DrawModelWires(lua_State* L)
+{
+ Model arg1 = LuaGetArgument_Model(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Color arg4 = LuaGetArgument_Color(L, 4);
+ DrawModelWires(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_DrawModelWiresEx(lua_State* L)
+{
+ Model arg1 = LuaGetArgument_Model(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Vector3 arg5 = LuaGetArgument_Vector3(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawModelWiresEx(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_DrawBillboard(lua_State* L)
+{
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ Texture2D arg2 = LuaGetArgument_Texture2D(L, 2);
+ Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Color arg5 = LuaGetArgument_Color(L, 5);
+ DrawBillboard(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+
+int lua_DrawBillboardRec(lua_State* L)
+{
+ Camera arg1 = LuaGetArgument_Camera(L, 1);
+ Texture2D arg2 = LuaGetArgument_Texture2D(L, 2);
+ Rectangle arg3 = LuaGetArgument_Rectangle(L, 3);
+ Vector3 arg4 = LuaGetArgument_Vector3(L, 4);
+ float arg5 = LuaGetArgument_float(L, 5);
+ Color arg6 = LuaGetArgument_Color(L, 6);
+ DrawBillboardRec(arg1, arg2, arg3, arg4, arg5, arg6);
+ return 0;
+}
+
+int lua_CalculateBoundingBox(lua_State* L)
+{
+ Mesh arg1 = LuaGetArgument_Mesh(L, 1);
+ BoundingBox result = CalculateBoundingBox(arg1);
+ LuaPush_BoundingBox(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionSpheres(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ bool result = CheckCollisionSpheres(arg1, arg2, arg3, arg4);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionBoxes(lua_State* L)
+{
+ BoundingBox arg1 = LuaGetArgument_BoundingBox(L, 1);
+ BoundingBox arg2 = LuaGetArgument_BoundingBox(L, 2);
+ bool result = CheckCollisionBoxes(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionBoxSphere(lua_State* L)
+{
+ BoundingBox arg1 = LuaGetArgument_BoundingBox(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ bool result = CheckCollisionBoxSphere(arg1, arg2, arg3);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionRaySphere(lua_State* L)
+{
+ Ray arg1 = LuaGetArgument_Ray(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ bool result = CheckCollisionRaySphere(arg1, arg2, arg3);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_CheckCollisionRaySphereEx(lua_State* L)
+{
+ Ray arg1 = LuaGetArgument_Ray(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Vector3 arg4 = LuaGetArgument_Vector3(L, 4);
+ bool result = CheckCollisionRaySphereEx(arg1, arg2, arg3, &arg4);
+ lua_pushboolean(L, result);
+ LuaPush_Vector3(L, arg4);
+ return 2;
+}
+
+int lua_CheckCollisionRayBox(lua_State* L)
+{
+ Ray arg1 = LuaGetArgument_Ray(L, 1);
+ BoundingBox arg2 = LuaGetArgument_BoundingBox(L, 2);
+ bool result = CheckCollisionRayBox(arg1, arg2);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_ResolveCollisionCubicmap(lua_State* L)
+{
+ Image arg1 = LuaGetArgument_Image(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
+ float arg4 = LuaGetArgument_float(L, 4);
+ Vector3 result = ResolveCollisionCubicmap(arg1, arg2, &arg3, arg4);
+ LuaPush_Vector3(L, result);
+ LuaPush_Vector3(L, arg3);
+ return 2;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [raymath] module functions - Shaders
+//------------------------------------------------------------------------------------
+int lua_LoadShader(lua_State* L)
+{
+ char * arg1 = (char *)LuaGetArgument_string(L, 1);
+ char * arg2 = (char *)LuaGetArgument_string(L, 2);
+ Shader result = LoadShader(arg1, arg2);
+ LuaPush_Shader(L, result);
+ return 1;
+}
+
+int lua_UnloadShader(lua_State* L)
+{
+ Shader arg1 = LuaGetArgument_Shader(L, 1);
+ UnloadShader(arg1);
+ return 0;
+}
+
+int lua_GetDefaultShader(lua_State* L)
+{
+ Shader result = GetDefaultShader();
+ LuaPush_Shader(L, result);
+ return 1;
+}
+
+int lua_GetStandardShader(lua_State* L)
+{
+ Shader result = GetStandardShader();
+ LuaPush_Shader(L, result);
+ return 1;
+}
+
+int lua_GetDefaultTexture(lua_State* L)
+{
+ Texture2D result = GetDefaultTexture();
+ LuaPush_Texture2D(L, result);
+ return 1;
+}
+
+int lua_GetShaderLocation(lua_State* L)
+{
+ Shader arg1 = LuaGetArgument_Shader(L, 1);
+ const char * arg2 = LuaGetArgument_string(L, 2);
+ int result = GetShaderLocation(arg1, arg2);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+int lua_SetShaderValue(lua_State* L)
+{
+ Shader arg1 = LuaGetArgument_Shader(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ GET_TABLE(float, arg3, 3);
+ SetShaderValue(arg1, arg2, arg3, arg3_size);
+ free(arg3);
+ return 0;
+}
+
+int lua_SetShaderValuei(lua_State* L)
+{
+ Shader arg1 = LuaGetArgument_Shader(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ GET_TABLE(int, arg3, 3);
+ SetShaderValuei(arg1, arg2, arg3, arg3_size);
+ free(arg3);
+ return 0;
+}
+
+int lua_SetShaderValueMatrix(lua_State* L)
+{
+ Shader arg1 = LuaGetArgument_Shader(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ Matrix arg3 = LuaGetArgument_Matrix(L, 3);
+ SetShaderValueMatrix(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_SetMatrixProjection(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ SetMatrixProjection(arg1);
+ return 0;
+}
+
+int lua_SetMatrixModelview(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ SetMatrixModelview(arg1);
+ return 0;
+}
+
+int lua_BeginShaderMode(lua_State* L)
+{
+ Shader arg1 = LuaGetArgument_Shader(L, 1);
+ BeginShaderMode(arg1);
+ return 0;
+}
+
+int lua_EndShaderMode(lua_State* L)
+{
+ EndShaderMode();
+ return 0;
+}
+
+int lua_BeginBlendMode(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ BeginBlendMode(arg1);
+ return 0;
+}
+
+int lua_EndBlendMode(lua_State* L)
+{
+ EndBlendMode();
+ return 0;
+}
+
+int lua_CreateLight(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Color arg3 = LuaGetArgument_Color(L, 3);
+ Light result = CreateLight(arg1, arg2, arg3);
+ LuaPush_Light(L, result);
+ return 1;
+}
+
+int lua_DestroyLight(lua_State* L)
+{
+ Light arg1 = LuaGetArgument_Light(L, 1);
+ DestroyLight(arg1);
+ return 0;
+}
+
+
+//------------------------------------------------------------------------------------
+// raylib [rlgl] module functions - VR experience
+//------------------------------------------------------------------------------------
+int lua_InitVrDevice(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ InitVrDevice(arg1);
+ return 0;
+}
+
+int lua_CloseVrDevice(lua_State* L)
+{
+ CloseVrDevice();
+ return 0;
+}
+
+int lua_IsVrDeviceReady(lua_State* L)
+{
+ bool result = IsVrDeviceReady();
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_UpdateVrTracking(lua_State* L)
+{
+ UpdateVrTracking();
+ return 0;
+}
+
+int lua_ToggleVrMode(lua_State* L)
+{
+ ToggleVrMode();
+ return 0;
+}
+
+//------------------------------------------------------------------------------------
+// raylib [audio] module functions - Audio Loading and Playing
+//------------------------------------------------------------------------------------
+int lua_InitAudioDevice(lua_State* L)
+{
+ InitAudioDevice();
+ return 0;
+}
+
+int lua_CloseAudioDevice(lua_State* L)
+{
+ CloseAudioDevice();
+ return 0;
+}
+
+int lua_IsAudioDeviceReady(lua_State* L)
+{
+ bool result = IsAudioDeviceReady();
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_LoadWave(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Wave result = LoadWave((char *)arg1);
+ LuaPush_Wave(L, result);
+ return 1;
+}
+
+int lua_LoadWaveEx(lua_State* L)
+{
+ // TODO: Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels);
+
+ int arg1 = 0;
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ Wave result = LoadWaveEx(arg1, arg2, arg3, arg4, arg5);
+ LuaPush_Wave(L, result);
+ return 1;
+}
+
+int lua_LoadSound(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Sound result = LoadSound((char*)arg1);
+ LuaPush_Sound(L, result);
+ return 1;
+}
+
+int lua_LoadSoundFromWave(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ Sound result = LoadSoundFromWave(arg1);
+ LuaPush_Sound(L, result);
+ return 1;
+}
+
+int lua_LoadSoundFromRES(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ Sound result = LoadSoundFromRES(arg1, arg2);
+ LuaPush_Sound(L, result);
+ return 1;
+}
+
+int lua_UpdateSound(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ const char * arg2 = LuaGetArgument_string(L, 2);
+ int * arg3 = LuaGetArgument_int(L, 3);
+ UpdateSound(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_UnloadWave(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ UnloadWave(arg1);
+ return 0;
+}
+
+int lua_UnloadSound(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ UnloadSound(arg1);
+ return 0;
+}
+
+int lua_PlaySound(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ PlaySound(arg1);
+ return 0;
+}
+
+int lua_PauseSound(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ PauseSound(arg1);
+ return 0;
+}
+
+int lua_ResumeSound(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ ResumeSound(arg1);
+ return 0;
+}
+
+int lua_StopSound(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ StopSound(arg1);
+ return 0;
+}
+
+int lua_IsSoundPlaying(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ bool result = IsSoundPlaying(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_SetSoundVolume(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ SetSoundVolume(arg1, arg2);
+ return 0;
+}
+
+int lua_SetSoundPitch(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ SetSoundPitch(arg1, arg2);
+ return 0;
+}
+
+int lua_WaveFormat(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ WaveFormat(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_LoadMusicStream(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ Wave result = WaveCopy(arg1);
+ LuaPush_Wave(L, result);
+ return 1;
+}
+
+int lua_WaveCrop(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ WaveCrop(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_GetWaveData(lua_State* L)
+{
+ // TODO: float *GetWaveData(Wave wave);
+
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ float result = GetWaveData(arg1);
+ LuaPush_float(L, result);
+ return 1;
+}
+
+int lua_LoadMusicStream(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Music result = LoadMusicStream((char *)arg1);
+ LuaPush_Music(L, result);
+ return 1;
+}
+
+int lua_UnloadMusicStream(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ UnloadMusicStream(arg1);
+ return 0;
+}
+
+int lua_UpdateMusicStream(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ UpdateMusicStream(arg1);
+ return 0;
+}
+
+int lua_PlayMusicStream(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ PlayMusicStream(arg1);
+ return 0;
+}
+
+
+int lua_StopMusicStream(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ StopMusicStream(arg1);
+ return 0;
+}
+
+int lua_PauseMusicStream(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ PauseMusicStream(arg1);
+ return 0;
+}
+
+int lua_ResumeMusicStream(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ ResumeMusicStream(arg1);
+ return 0;
+}
+
+int lua_IsMusicPlaying(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ bool result = IsMusicPlaying(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_SetMusicVolume(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ SetMusicVolume(arg1, arg2);
+ return 0;
+}
+
+int lua_SetMusicPitch(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ SetMusicPitch(arg1, arg2);
+ return 0;
+}
+
+int lua_GetMusicTimeLength(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ float result = GetMusicTimeLength(arg1);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_GetMusicTimePlayed(lua_State* L)
+{
+ Music arg1 = LuaGetArgument_Music(L, 1);
+ float result = GetMusicTimePlayed(arg1);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_InitAudioStream(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ AudioStream result = InitAudioStream(arg1, arg2, arg3);
+ LuaPush_AudioStream(L, result);
+ return 1;
+}
+
+int lua_CloseAudioStream(lua_State* L)
+{
+ AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
+ CloseAudioStream(arg1);
+ return 0;
+}
+
+int lua_UpdateAudioStream(lua_State* L)
+{
+ AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
+ void * arg2 = (char *)LuaGetArgument_string(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ UpdateAudioStream(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_IsAudioBufferProcessed(lua_State* L)
+{
+ AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
+ bool result = IsAudioBufferProcessed(arg1);
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+int lua_PlayAudioStream(lua_State* L)
+{
+ AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
+ PlayAudioStream(arg1);
+ return 0;
+}
+
+
+int lua_StopAudioStream(lua_State* L)
+{
+ AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
+ StopAudioStream(arg1);
+ return 0;
+}
+
+int lua_PauseAudioStream(lua_State* L)
+{
+ AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
+ PauseAudioStream(arg1);
+ return 0;
+}
+
+int lua_ResumeAudioStream(lua_State* L)
+{
+ AudioStream arg1 = LuaGetArgument_AudioStream(L, 1);
+ ResumeAudioStream(arg1);
+ return 0;
+}
+
+//----------------------------------------------------------------------------------
+// raylib [utils] module functions
+//----------------------------------------------------------------------------------
+int lua_DecompressData(lua_State* L)
+{
+ unsigned char *arg1 = (unsigned char *)LuaGetArgument_string(L, 1);
+ unsigned arg2 = (unsigned)LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ unsigned char *result = DecompressData(arg1, arg2, arg3);
+ lua_pushlstring(L, (const char *)result, arg3);
+ return 1;
+}
+
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
+int lua_WriteBitmap(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ unsigned char* arg2 = (unsigned char*)LuaGetArgument_string(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ WriteBitmap(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_WritePNG(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ unsigned char* arg2 = (unsigned char*)LuaGetArgument_string(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ WritePNG(arg1, arg2, arg3, arg4, arg5);
+ return 0;
+}
+#endif
+
+int lua_TraceLog(lua_State* L)
+{
+ int num_args = lua_gettop(L) - 1;
+ int arg1 = LuaGetArgument_int(L, 1);
+
+ /// type, fmt, args...
+
+ lua_rotate(L, 1, -1); /// fmt, args..., type
+ lua_pop(L, 1); /// fmt, args...
+
+ lua_getglobal(L, "string"); /// fmt, args..., [string]
+ lua_getfield(L, 1, "format"); /// fmt, args..., [string], format()
+ lua_rotate(L, 1, 2); /// [string], format(), fmt, args...
+ lua_call(L, num_args, 1); /// [string], formatted_string
+
+ TraceLog(arg1, "%s", luaL_checkstring(L,-1));
+ return 0;
+}
+
+int lua_GetExtension(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ const char* result = GetExtension(arg1);
+ lua_pushstring(L, result);
+ return 1;
+}
+
+int lua_GetNextPOT(lua_State* L)
+{
+ int arg1 = LuaGetArgument_int(L, 1);
+ int result = GetNextPOT(arg1);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
+//----------------------------------------------------------------------------------
+// raylib [raymath] module functions - Vector3 math
+//----------------------------------------------------------------------------------
+int lua_VectorAdd(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 result = VectorAdd(arg1, arg2);
+ LuaPush_Vector3(L, result);
+ return 1;
+}
+
+int lua_VectorSubtract(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 result = VectorSubtract(arg1, arg2);
+ LuaPush_Vector3(L, result);
+ return 1;
+}
+
+int lua_VectorCrossProduct(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 result = VectorCrossProduct(arg1, arg2);
+ LuaPush_Vector3(L, result);
+ return 1;
+}
+
+int lua_VectorPerpendicular(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 result = VectorPerpendicular(arg1);
+ LuaPush_Vector3(L, result);
+ return 1;
+}
+
+int lua_VectorDotProduct(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float result = VectorDotProduct(arg1, arg2);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_VectorLength(lua_State* L)
+{
+ const Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float result = VectorLength(arg1);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_VectorScale(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ VectorScale(&arg1, arg2);
+ LuaPush_Vector3(L, arg1);
+ return 1;
+}
+
+int lua_VectorNegate(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ VectorNegate(&arg1);
+ LuaPush_Vector3(L, arg1);
+ return 1;
+}
+
+int lua_VectorNormalize(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ VectorNormalize(&arg1);
+ LuaPush_Vector3(L, arg1);
+ return 1;
+}
+
+int lua_VectorDistance(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float result = VectorDistance(arg1, arg2);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_VectorLerp(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Vector3 result = VectorLerp(arg1, arg2, arg3);
+ LuaPush_Vector3(L, result);
+ return 1;
+}
+
+int lua_VectorReflect(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 result = VectorReflect(arg1, arg2);
+ LuaPush_Vector3(L, result);
+ return 1;
+}
+
+int lua_VectorTransform(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Matrix arg2 = LuaGetArgument_Matrix(L, 2);
+ VectorTransform(&arg1, arg2);
+ LuaPush_Vector3(L, arg1);
+ return 1;
+}
+
+int lua_VectorZero(lua_State* L)
+{
+ Vector3 result = VectorZero();
+ LuaPush_Vector3(L, result);
+ return 1;
+}
+
+//----------------------------------------------------------------------------------
+// raylib [raymath] module functions - Matrix math
+//----------------------------------------------------------------------------------
+int lua_MatrixDeterminant(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ float result = MatrixDeterminant(arg1);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_MatrixTrace(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ float result = MatrixTrace(arg1);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_MatrixTranspose(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ MatrixTranspose(&arg1);
+ LuaPush_Matrix(L, &arg1);
+ return 1;
+}
+
+int lua_MatrixInvert(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ MatrixInvert(&arg1);
+ LuaPush_Matrix(L, &arg1);
+ return 1;
+}
+
+int lua_MatrixNormalize(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ MatrixNormalize(&arg1);
+ LuaPush_Matrix(L, &arg1);
+ return 1;
+}
+
+int lua_MatrixIdentity(lua_State* L)
+{
+ Matrix result = MatrixIdentity();
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixAdd(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ Matrix arg2 = LuaGetArgument_Matrix(L, 2);
+ Matrix result = MatrixAdd(arg1, arg2);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixSubstract(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ Matrix arg2 = LuaGetArgument_Matrix(L, 2);
+ Matrix result = MatrixSubstract(arg1, arg2);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixTranslate(lua_State* L)
+{
+ float arg1 = LuaGetArgument_float(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Matrix result = MatrixTranslate(arg1, arg2, arg3);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixRotate(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Matrix result = MatrixRotate(arg1, arg2);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixRotateX(lua_State* L)
+{
+ float arg1 = LuaGetArgument_float(L, 1);
+ Matrix result = MatrixRotateX(arg1);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixRotateY(lua_State* L)
+{
+ float arg1 = LuaGetArgument_float(L, 1);
+ Matrix result = MatrixRotateY(arg1);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixRotateZ(lua_State* L)
+{
+ float arg1 = LuaGetArgument_float(L, 1);
+ Matrix result = MatrixRotateZ(arg1);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixScale(lua_State* L)
+{
+ float arg1 = LuaGetArgument_float(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Matrix result = MatrixScale(arg1, arg2, arg3);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixMultiply(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ Matrix arg2 = LuaGetArgument_Matrix(L, 2);
+ Matrix result = MatrixMultiply(arg1, arg2);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixFrustum(lua_State* L)
+{
+ double arg1 = LuaGetArgument_double(L, 1);
+ double arg2 = LuaGetArgument_double(L, 2);
+ double arg3 = LuaGetArgument_double(L, 3);
+ double arg4 = LuaGetArgument_double(L, 4);
+ double arg5 = LuaGetArgument_double(L, 5);
+ double arg6 = LuaGetArgument_double(L, 6);
+ Matrix result = MatrixFrustum(arg1, arg2, arg3, arg4, arg5, arg6);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixPerspective(lua_State* L)
+{
+ double arg1 = LuaGetArgument_double(L, 1);
+ double arg2 = LuaGetArgument_double(L, 2);
+ double arg3 = LuaGetArgument_double(L, 3);
+ double arg4 = LuaGetArgument_double(L, 4);
+ Matrix result = MatrixPerspective(arg1, arg2, arg3, arg4);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixOrtho(lua_State* L)
+{
+ double arg1 = LuaGetArgument_double(L, 1);
+ double arg2 = LuaGetArgument_double(L, 2);
+ double arg3 = LuaGetArgument_double(L, 3);
+ double arg4 = LuaGetArgument_double(L, 4);
+ double arg5 = LuaGetArgument_double(L, 5);
+ double arg6 = LuaGetArgument_double(L, 6);
+ Matrix result = MatrixOrtho(arg1, arg2, arg3, arg4, arg5, arg6);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_MatrixLookAt(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ Vector3 arg2 = LuaGetArgument_Vector3(L, 2);
+ Vector3 arg3 = LuaGetArgument_Vector3(L, 3);
+ Matrix result = MatrixLookAt(arg1, arg2, arg3);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+//----------------------------------------------------------------------------------
+// raylib [raymath] module functions - Quaternion math
+//----------------------------------------------------------------------------------
+int lua_QuaternionLength(lua_State* L)
+{
+ Quaternion arg1 = LuaGetArgument_Quaternion(L, 1);
+ float result = QuaternionLength(arg1);
+ lua_pushnumber(L, result);
+ return 1;
+}
+
+int lua_QuaternionNormalize(lua_State* L)
+{
+ Quaternion arg1 = LuaGetArgument_Quaternion(L, 1);
+ QuaternionNormalize(&arg1);
+ LuaPush_Quaternion(L, arg1);
+ return 1;
+}
+
+int lua_QuaternionMultiply(lua_State* L)
+{
+ Quaternion arg1 = LuaGetArgument_Quaternion(L, 1);
+ Quaternion arg2 = LuaGetArgument_Quaternion(L, 2);
+ Quaternion result = QuaternionMultiply(arg1, arg2);
+ LuaPush_Quaternion(L, result);
+ return 1;
+}
+
+int lua_QuaternionSlerp(lua_State* L)
+{
+ Quaternion arg1 = LuaGetArgument_Quaternion(L, 1);
+ Quaternion arg2 = LuaGetArgument_Quaternion(L, 2);
+ float arg3 = LuaGetArgument_float(L, 3);
+ Quaternion result = QuaternionSlerp(arg1, arg2, arg3);
+ LuaPush_Quaternion(L, result);
+ return 1;
+}
+
+int lua_QuaternionFromMatrix(lua_State* L)
+{
+ Matrix arg1 = LuaGetArgument_Matrix(L, 1);
+ Quaternion result = QuaternionFromMatrix(arg1);
+ LuaPush_Quaternion(L, result);
+ return 1;
+}
+
+int lua_QuaternionToMatrix(lua_State* L)
+{
+ Quaternion arg1 = LuaGetArgument_Quaternion(L, 1);
+ Matrix result = QuaternionToMatrix(arg1);
+ LuaPush_Matrix(L, &result);
+ return 1;
+}
+
+int lua_QuaternionFromAxisAngle(lua_State* L)
+{
+ Vector3 arg1 = LuaGetArgument_Vector3(L, 1);
+ float arg2 = LuaGetArgument_float(L, 2);
+ Quaternion result = QuaternionFromAxisAngle(arg1, arg2);
+ LuaPush_Quaternion(L, result);
+ return 1;
+}
+
+int lua_QuaternionToAxisAngle(lua_State* L)
+{
+ Quaternion arg1 = LuaGetArgument_Quaternion(L, 1);
+ Vector3 arg2;
+ float arg3 = 0;
+ QuaternionToAxisAngle(arg1, &arg2, &arg3);
+ LuaPush_Vector3(L, arg2);
+ lua_pushnumber(L, arg3);
+ return 2;
+}
+
+int lua_QuaternionTransform(lua_State* L)
+{
+ Quaternion arg1 = LuaGetArgument_Quaternion(L, 1);
+ Matrix arg2 = LuaGetArgument_Matrix(L, 2);
+ QuaternionTransform(&arg1, arg2);
+ LuaPush_Quaternion(L, arg1);
+ return 1;
+}
+
+
+//----------------------------------------------------------------------------------
+// Functions Registering
+//----------------------------------------------------------------------------------
+#define REG(name) { #name, lua_##name },
+
+// raylib Functions (and data types) list
+static luaL_Reg raylib_functions[] = {
+
+ // Register non-opaque data types
+ REG(Color)
+ REG(Vector2)
+ REG(Vector3)
+ //REG(Matrix)
+ REG(Quaternion)
+ REG(Rectangle)
+ REG(Ray)
+ REG(Camera)
+ REG(Camera2D)
+ REG(BoundingBox)
+ //REG(Material)
+
+ // Register functions
+ REG(InitWindow)
+ REG(CloseWindow)
+ REG(WindowShouldClose)
+ REG(IsWindowMinimized)
+ REG(ToggleFullscreen)
+ REG(GetScreenWidth)
+ REG(GetScreenHeight)
+
+ REG(ShowCursor)
+ REG(HideCursor)
+ REG(IsCursorHidden)
+ REG(EnableCursor)
+ REG(DisableCursor)
+
+ REG(ClearBackground)
+ REG(BeginDrawing)
+ REG(EndDrawing)
+ REG(Begin2dMode)
+ REG(End2dMode)
+ REG(Begin3dMode)
+ REG(End3dMode)
+ REG(BeginTextureMode)
+ REG(EndTextureMode)
+
+ REG(GetMouseRay)
+ REG(GetWorldToScreen)
+ REG(GetCameraMatrix)
+
+#if defined(PLATFORM_WEB)
+ REG(SetDrawingLoop)
+#else
+ REG(SetTargetFPS)
+#endif
+ REG(GetFPS)
+ REG(GetFrameTime)
+
+ REG(GetColor)
+ REG(GetHexValue)
+ REG(ColorToFloat)
+ REG(VectorToFloat)
+ REG(MatrixToFloat)
+ REG(GetRandomValue)
+ REG(Fade)
+ REG(SetConfigFlags)
+ REG(ShowLogo)
+
+ REG(IsFileDropped)
+ REG(GetDroppedFiles)
+ REG(ClearDroppedFiles)
+ REG(StorageSaveValue)
+ REG(StorageLoadValue)
+
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
+ REG(IsKeyPressed)
+ REG(IsKeyDown)
+ REG(IsKeyReleased)
+ REG(IsKeyUp)
+ REG(GetKeyPressed)
+ REG(SetExitKey)
+
+ REG(IsGamepadAvailable)
+ REG(GetGamepadAxisMovement)
+ REG(IsGamepadButtonPressed)
+ REG(IsGamepadButtonDown)
+ REG(IsGamepadButtonReleased)
+ REG(IsGamepadButtonUp)
+#endif
+
+ REG(IsMouseButtonPressed)
+ REG(IsMouseButtonDown)
+ REG(IsMouseButtonReleased)
+ REG(IsMouseButtonUp)
+ REG(GetMouseX)
+ REG(GetMouseY)
+ REG(GetMousePosition)
+ REG(SetMousePosition)
+ REG(GetMouseWheelMove)
+ REG(GetTouchX)
+ REG(GetTouchY)
+ REG(GetTouchPosition)
+
+#if defined(PLATFORM_ANDROID)
+ REG(IsButtonPressed)
+ REG(IsButtonDown)
+ REG(IsButtonReleased)
+#endif
+
+ REG(SetGesturesEnabled)
+ REG(IsGestureDetected)
+ REG(GetGestureDetected)
+ REG(GetTouchPointsCount)
+ REG(GetGestureHoldDuration)
+ REG(GetGestureDragVector)
+ REG(GetGestureDragAngle)
+ REG(GetGesturePinchVector)
+ REG(GetGesturePinchAngle)
+
+ REG(SetCameraMode)
+ REG(UpdateCamera)
+ REG(UpdateCameraPlayer)
+ REG(SetCameraPosition)
+ REG(SetCameraTarget)
+ REG(SetCameraFovy)
+
+ REG(SetCameraPanControl)
+ REG(SetCameraAltControl)
+ REG(SetCameraSmoothZoomControl)
+ REG(SetCameraMoveControls)
+ REG(SetCameraMouseSensitivity)
+
+ REG(DrawPixel)
+ REG(DrawPixelV)
+ REG(DrawLine)
+ REG(DrawLineV)
+ REG(DrawCircle)
+ REG(DrawCircleGradient)
+ REG(DrawCircleV)
+ REG(DrawCircleLines)
+ REG(DrawRectangle)
+ REG(DrawRectangleRec)
+ REG(DrawRectangleGradient)
+ REG(DrawRectangleV)
+ REG(DrawRectangleLines)
+ REG(DrawTriangle)
+ REG(DrawTriangleLines)
+ REG(DrawPoly)
+ REG(DrawPolyEx)
+ REG(DrawPolyExLines)
+
+ REG(CheckCollisionRecs)
+ REG(CheckCollisionCircles)
+ REG(CheckCollisionCircleRec)
+ REG(GetCollisionRec)
+ REG(CheckCollisionPointRec)
+ REG(CheckCollisionPointCircle)
+ REG(CheckCollisionPointTriangle)
+
+ REG(LoadImage)
+ REG(LoadImageEx)
+ REG(LoadImageRaw)
+ REG(LoadImageFromRES)
+ REG(LoadTexture)
+ REG(LoadTextureEx)
+ REG(LoadTextureFromRES)
+ REG(LoadTextureFromImage)
+ REG(LoadRenderTexture)
+ REG(UnloadImage)
+ REG(UnloadTexture)
+ REG(UnloadRenderTexture)
+ REG(GetImageData)
+ REG(GetTextureData)
+ REG(ImageToPOT)
+ REG(ImageFormat)
+ REG(ImageDither)
+ REG(ImageCopy)
+ REG(ImageCrop)
+ REG(ImageResize)
+ REG(ImageResizeNN)
+ REG(ImageText)
+ REG(ImageTextEx)
+ REG(ImageDraw)
+ REG(ImageDrawText)
+ REG(ImageDrawTextEx)
+ REG(ImageFlipVertical)
+ REG(ImageFlipHorizontal)
+ REG(ImageColorTint)
+ REG(ImageColorInvert)
+ REG(ImageColorGrayscale)
+ REG(ImageColorContrast)
+ REG(ImageColorBrightness)
+ REG(GenTextureMipmaps)
+ REG(UpdateTexture)
+
+ REG(DrawTexture)
+ REG(DrawTextureV)
+ REG(DrawTextureEx)
+ REG(DrawTextureRec)
+ REG(DrawTexturePro)
+
+ REG(GetDefaultFont)
+ REG(LoadSpriteFont)
+ REG(UnloadSpriteFont)
+ REG(DrawText)
+ REG(DrawTextEx)
+ REG(MeasureText)
+ REG(MeasureTextEx)
+ REG(DrawFPS)
+
+ REG(DrawLine3D)
+ REG(DrawCircle3D)
+ REG(DrawCube)
+ REG(DrawCubeV)
+ REG(DrawCubeWires)
+ REG(DrawCubeTexture)
+ REG(DrawSphere)
+ REG(DrawSphereEx)
+ REG(DrawSphereWires)
+ REG(DrawCylinder)
+ REG(DrawCylinderWires)
+ REG(DrawPlane)
+ REG(DrawRay)
+ REG(DrawGrid)
+ REG(DrawGizmo)
+ REG(DrawLight)
+
+ REG(LoadModel)
+ REG(LoadModelEx)
+ REG(LoadModelFromRES)
+ REG(LoadHeightmap)
+ REG(LoadCubicmap)
+ REG(UnloadModel)
+ REG(LoadMaterial)
+ REG(LoadDefaultMaterial)
+ REG(LoadStandardMaterial)
+ REG(UnloadMaterial)
+ //REG(GenMesh*) // Not ready yet...
+
+ REG(DrawModel)
+ REG(DrawModelEx)
+ REG(DrawModelWires)
+ REG(DrawModelWiresEx)
+ REG(DrawBillboard)
+ REG(DrawBillboardRec)
+ REG(CalculateBoundingBox)
+ REG(CheckCollisionSpheres)
+ REG(CheckCollisionBoxes)
+ REG(CheckCollisionBoxSphere)
+ REG(CheckCollisionRaySphere)
+ REG(CheckCollisionRaySphereEx)
+ REG(CheckCollisionRayBox)
+ REG(ResolveCollisionCubicmap)
+
+ REG(LoadShader)
+ REG(UnloadShader)
+ REG(GetDefaultShader)
+ REG(GetStandardShader)
+ REG(GetDefaultTexture)
+ REG(GetShaderLocation)
+ REG(SetShaderValue)
+ REG(SetShaderValuei)
+ REG(SetShaderValueMatrix)
+ REG(SetMatrixProjection)
+ REG(SetMatrixModelview)
+ REG(BeginShaderMode)
+ REG(EndShaderMode)
+ REG(BeginBlendMode)
+ REG(EndBlendMode)
+ REG(CreateLight)
+ REG(DestroyLight)
+
+ REG(InitVrDevice)
+ REG(CloseVrDevice)
+ REG(IsVrDeviceReady)
+ REG(UpdateVrTracking)
+ REG(ToggleVrMode)
+
+ REG(InitAudioDevice)
+ REG(CloseAudioDevice)
+ REG(IsAudioDeviceReady)
+ REG(LoadWave)
+ REG(LoadWaveEx)
+ REG(LoadSound)
+ REG(LoadSoundFromWave)
+ REG(LoadSoundFromRES)
+ REG(UpdateSound)
+ REG(UnloadWave)
+ REG(UnloadSound)
+ REG(PlaySound)
+ REG(PauseSound)
+ REG(ResumeSound)
+ REG(StopSound)
+ REG(IsSoundPlaying)
+ REG(SetSoundVolume)
+ REG(SetSoundPitch)
+ REG(WaveFormat)
+ REG(WaveCopy)
+ REG(WaveCrop)
+ REG(GetWaveData)
+
+ REG(LoadMusicStream)
+ REG(UnloadMusicStream)
+ REG(UpdateMusicStream)
+ REG(PlayMusicStream)
+ REG(StopMusicStream)
+ REG(PauseMusicStream)
+ REG(ResumeMusicStream)
+ REG(IsMusicPlaying)
+ REG(SetMusicVolume)
+ REG(SetMusicPitch)
+ REG(GetMusicTimeLength)
+ REG(GetMusicTimePlayed)
+
+ REG(InitAudioStream)
+ REG(UpdateAudioStream)
+ REG(CloseAudioStream)
+ REG(IsAudioBufferProcessed)
+ REG(PlayAudioStream)
+ REG(PauseAudioStream)
+ REG(ResumeAudioStream)
+ REG(StopAudioStream)
+
+ /// Math and util
+ REG(DecompressData)
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
+ REG(WriteBitmap)
+ REG(WritePNG)
+#endif
+ REG(TraceLog)
+ REG(GetExtension)
+ REG(GetNextPOT)
+ REG(VectorAdd)
+ REG(VectorSubtract)
+ REG(VectorCrossProduct)
+ REG(VectorPerpendicular)
+ REG(VectorDotProduct)
+ REG(VectorLength)
+ REG(VectorScale)
+ REG(VectorNegate)
+ REG(VectorNormalize)
+ REG(VectorDistance)
+ REG(VectorLerp)
+ REG(VectorReflect)
+ REG(VectorTransform)
+ REG(VectorZero)
+ REG(MatrixDeterminant)
+ REG(MatrixTrace)
+ REG(MatrixTranspose)
+ REG(MatrixInvert)
+ REG(MatrixNormalize)
+ REG(MatrixIdentity)
+ REG(MatrixAdd)
+ REG(MatrixSubstract)
+ REG(MatrixTranslate)
+ REG(MatrixRotate)
+ REG(MatrixRotateX)
+ REG(MatrixRotateY)
+ REG(MatrixRotateZ)
+ REG(MatrixScale)
+ REG(MatrixMultiply)
+ REG(MatrixFrustum)
+ REG(MatrixPerspective)
+ REG(MatrixOrtho)
+ REG(MatrixLookAt)
+ REG(QuaternionLength)
+ REG(QuaternionNormalize)
+ REG(QuaternionMultiply)
+ REG(QuaternionSlerp)
+ REG(QuaternionFromMatrix)
+ REG(QuaternionToMatrix)
+ REG(QuaternionFromAxisAngle)
+ REG(QuaternionToAxisAngle)
+ REG(QuaternionTransform)
+
+ {NULL, NULL} // sentinel: end signal
+};
+
+// Register raylib functionality
+static void LuaRegisterRayLib(const char *opt_table)
+{
+ if (opt_table) lua_createtable(L, 0, sizeof(raylib_functions)/sizeof(raylib_functions[0]));
+ else lua_pushglobaltable(L);
+
+ luaL_setfuncs(L, raylib_functions, 0);
+}
+
+//----------------------------------------------------------------------------------
+// raylib Lua API
+//----------------------------------------------------------------------------------
+
+// Initialize Lua system
+RLUADEF void InitLuaDevice(void)
+{
+ mainLuaState = luaL_newstate();
+ L = mainLuaState;
+
+ LuaStartEnum();
+ LuaSetEnum("FULLSCREEN_MODE", 1);
+ LuaSetEnum("SHOW_LOGO", 2);
+ LuaSetEnum("SHOW_MOUSE_CURSOR", 4);
+ LuaSetEnum("CENTERED_MODE", 8);
+ LuaSetEnum("MSAA_4X_HINT", 16);
+ LuaSetEnum("VSYNC_HINT", 32);
+ LuaEndEnum("FLAG");
+
+ LuaStartEnum();
+ LuaSetEnum("SPACE", 32);
+ LuaSetEnum("ESCAPE", 256);
+ LuaSetEnum("ENTER", 257);
+ LuaSetEnum("BACKSPACE", 259);
+ LuaSetEnum("RIGHT", 262);
+ LuaSetEnum("LEFT", 263);
+ LuaSetEnum("DOWN", 264);
+ LuaSetEnum("UP", 265);
+ LuaSetEnum("F1", 290);
+ LuaSetEnum("F2", 291);
+ LuaSetEnum("F3", 292);
+ LuaSetEnum("F4", 293);
+ LuaSetEnum("F5", 294);
+ LuaSetEnum("F6", 295);
+ LuaSetEnum("F7", 296);
+ LuaSetEnum("F8", 297);
+ LuaSetEnum("F9", 298);
+ LuaSetEnum("F10", 299);
+ LuaSetEnum("LEFT_SHIFT", 340);
+ LuaSetEnum("LEFT_CONTROL", 341);
+ LuaSetEnum("LEFT_ALT", 342);
+ LuaSetEnum("RIGHT_SHIFT", 344);
+ LuaSetEnum("RIGHT_CONTROL", 345);
+ LuaSetEnum("RIGHT_ALT", 346);
+ LuaSetEnum("ZERO", 48);
+ LuaSetEnum("ONE", 49);
+ LuaSetEnum("TWO", 50);
+ LuaSetEnum("THREE", 51);
+ LuaSetEnum("FOUR", 52);
+ LuaSetEnum("FIVE", 53);
+ LuaSetEnum("SIX", 54);
+ LuaSetEnum("SEVEN", 55);
+ LuaSetEnum("EIGHT", 56);
+ LuaSetEnum("NINE", 57);
+ LuaSetEnum("A", 65);
+ LuaSetEnum("B", 66);
+ LuaSetEnum("C", 67);
+ LuaSetEnum("D", 68);
+ LuaSetEnum("E", 69);
+ LuaSetEnum("F", 70);
+ LuaSetEnum("G", 71);
+ LuaSetEnum("H", 72);
+ LuaSetEnum("I", 73);
+ LuaSetEnum("J", 74);
+ LuaSetEnum("K", 75);
+ LuaSetEnum("L", 76);
+ LuaSetEnum("M", 77);
+ LuaSetEnum("N", 78);
+ LuaSetEnum("O", 79);
+ LuaSetEnum("P", 80);
+ LuaSetEnum("Q", 81);
+ LuaSetEnum("R", 82);
+ LuaSetEnum("S", 83);
+ LuaSetEnum("T", 84);
+ LuaSetEnum("U", 85);
+ LuaSetEnum("V", 86);
+ LuaSetEnum("W", 87);
+ LuaSetEnum("X", 88);
+ LuaSetEnum("Y", 89);
+ LuaSetEnum("Z", 90);
+ LuaEndEnum("KEY");
+
+ LuaStartEnum();
+ LuaSetEnum("LEFT_BUTTON", 0);
+ LuaSetEnum("RIGHT_BUTTON", 1);
+ LuaSetEnum("MIDDLE_BUTTON", 2);
+ LuaEndEnum("MOUSE");
+
+ LuaStartEnum();
+ LuaSetEnum("PLAYER1", 0);
+ LuaSetEnum("PLAYER2", 1);
+ LuaSetEnum("PLAYER3", 2);
+ LuaSetEnum("PLAYER4", 3);
+
+ LuaSetEnum("PS3_BUTTON_A", 2);
+ LuaSetEnum("PS3_BUTTON_B", 1);
+ LuaSetEnum("PS3_BUTTON_X", 3);
+ LuaSetEnum("PS3_BUTTON_Y", 4);
+ LuaSetEnum("PS3_BUTTON_R1", 7);
+ LuaSetEnum("PS3_BUTTON_R2", 5);
+ LuaSetEnum("PS3_BUTTON_L1", 6);
+ LuaSetEnum("PS3_BUTTON_L2", 8);
+ LuaSetEnum("PS3_BUTTON_SELECT", 9);
+ LuaSetEnum("PS3_BUTTON_START", 10);
+
+ LuaSetEnum("XBOX_BUTTON_A", 0);
+ LuaSetEnum("XBOX_BUTTON_B", 1);
+ LuaSetEnum("XBOX_BUTTON_X", 2);
+ LuaSetEnum("XBOX_BUTTON_Y", 3);
+ LuaSetEnum("XBOX_BUTTON_LB", 4);
+ LuaSetEnum("XBOX_BUTTON_RB", 5);
+ LuaSetEnum("XBOX_BUTTON_SELECT", 6);
+ LuaSetEnum("XBOX_BUTTON_START", 7);
+
+#if defined(PLATFORM_RPI)
+ LuaSetEnum("XBOX_AXIS_DPAD_X", 7);
+ LuaSetEnum("XBOX_AXIS_DPAD_Y", 6);
+ LuaSetEnum("XBOX_AXIS_RIGHT_X", 3);
+ LuaSetEnum("XBOX_AXIS_RIGHT_Y", 4);
+ LuaSetEnum("XBOX_AXIS_LT", 2);
+ LuaSetEnum("XBOX_AXIS_RT", 5);
+#else
+ LuaSetEnum("XBOX_BUTTON_UP", 10);
+ LuaSetEnum("XBOX_BUTTON_DOWN", 12);
+ LuaSetEnum("XBOX_BUTTON_LEFT", 13);
+ LuaSetEnum("XBOX_BUTTON_RIGHT", 11);
+ LuaSetEnum("XBOX_AXIS_RIGHT_X", 4);
+ LuaSetEnum("XBOX_AXIS_RIGHT_Y", 3);
+ LuaSetEnum("XBOX_AXIS_LT_RT", 2);
+#endif
+ LuaSetEnum("XBOX_AXIS_LEFT_X", 0);
+ LuaSetEnum("XBOX_AXIS_LEFT_Y", 1);
+ LuaEndEnum("GAMEPAD");
+
+ lua_pushglobaltable(L);
+ LuaSetEnumColor("LIGHTGRAY", LIGHTGRAY);
+ LuaSetEnumColor("GRAY", GRAY);
+ LuaSetEnumColor("DARKGRAY", DARKGRAY);
+ LuaSetEnumColor("YELLOW", YELLOW);
+ LuaSetEnumColor("GOLD", GOLD);
+ LuaSetEnumColor("ORANGE", ORANGE);
+ LuaSetEnumColor("PINK", PINK);
+ LuaSetEnumColor("RED", RED);
+ LuaSetEnumColor("MAROON", MAROON);
+ LuaSetEnumColor("GREEN", GREEN);
+ LuaSetEnumColor("LIME", LIME);
+ LuaSetEnumColor("DARKGREEN", DARKGREEN);
+ LuaSetEnumColor("SKYBLUE", SKYBLUE);
+ LuaSetEnumColor("BLUE", BLUE);
+ LuaSetEnumColor("DARKBLUE", DARKBLUE);
+ LuaSetEnumColor("PURPLE", PURPLE);
+ LuaSetEnumColor("VIOLET", VIOLET);
+ LuaSetEnumColor("DARKPURPLE", DARKPURPLE);
+ LuaSetEnumColor("BEIGE", BEIGE);
+ LuaSetEnumColor("BROWN", BROWN);
+ LuaSetEnumColor("DARKBROWN", DARKBROWN);
+ LuaSetEnumColor("WHITE", WHITE);
+ LuaSetEnumColor("BLACK", BLACK);
+ LuaSetEnumColor("BLANK", BLANK);
+ LuaSetEnumColor("MAGENTA", MAGENTA);
+ LuaSetEnumColor("RAYWHITE", RAYWHITE);
+ lua_pop(L, 1);
+
+ LuaStartEnum();
+ LuaSetEnum("UNCOMPRESSED_GRAYSCALE", UNCOMPRESSED_GRAYSCALE);
+ LuaSetEnum("UNCOMPRESSED_GRAY_ALPHA", UNCOMPRESSED_GRAY_ALPHA);
+ LuaSetEnum("UNCOMPRESSED_R5G6B5", UNCOMPRESSED_R5G6B5);
+ LuaSetEnum("UNCOMPRESSED_R8G8B8", UNCOMPRESSED_R8G8B8);
+ LuaSetEnum("UNCOMPRESSED_R5G5B5A1", UNCOMPRESSED_R5G5B5A1);
+ LuaSetEnum("UNCOMPRESSED_R4G4B4A4", UNCOMPRESSED_R4G4B4A4);
+ LuaSetEnum("UNCOMPRESSED_R8G8B8A8", UNCOMPRESSED_R8G8B8A8);
+ LuaSetEnum("COMPRESSED_DXT1_RGB", COMPRESSED_DXT1_RGB);
+ LuaSetEnum("COMPRESSED_DXT1_RGBA", COMPRESSED_DXT1_RGBA);
+ LuaSetEnum("COMPRESSED_DXT3_RGBA", COMPRESSED_DXT3_RGBA);
+ LuaSetEnum("COMPRESSED_DXT5_RGBA", COMPRESSED_DXT5_RGBA);
+ LuaSetEnum("COMPRESSED_ETC1_RGB", COMPRESSED_ETC1_RGB);
+ LuaSetEnum("COMPRESSED_ETC2_RGB", COMPRESSED_ETC2_RGB);
+ LuaSetEnum("COMPRESSED_ETC2_EAC_RGBA", COMPRESSED_ETC2_EAC_RGBA);
+ LuaSetEnum("COMPRESSED_PVRT_RGB", COMPRESSED_PVRT_RGB);
+ LuaSetEnum("COMPRESSED_PVRT_RGBA", COMPRESSED_PVRT_RGBA);
+ LuaSetEnum("COMPRESSED_ASTC_4x4_RGBA", COMPRESSED_ASTC_4x4_RGBA);
+ LuaSetEnum("COMPRESSED_ASTC_8x8_RGBA", COMPRESSED_ASTC_8x8_RGBA);
+ LuaEndEnum("TextureFormat");
+
+ LuaStartEnum();
+ LuaSetEnum("ALPHA", BLEND_ALPHA);
+ LuaSetEnum("ADDITIVE", BLEND_ADDITIVE);
+ LuaSetEnum("MULTIPLIED", BLEND_MULTIPLIED);
+ LuaEndEnum("BlendMode");
+
+ LuaStartEnum();
+ LuaSetEnum("POINT", LIGHT_POINT);
+ LuaSetEnum("DIRECTIONAL", LIGHT_DIRECTIONAL);
+ LuaSetEnum("SPOT", LIGHT_SPOT);
+ LuaEndEnum("LightType");
+
+ LuaStartEnum();
+ LuaSetEnum("NONE", GESTURE_NONE);
+ LuaSetEnum("TAP", GESTURE_TAP);
+ LuaSetEnum("DOUBLETAP", GESTURE_DOUBLETAP);
+ LuaSetEnum("HOLD", GESTURE_HOLD);
+ LuaSetEnum("DRAG", GESTURE_DRAG);
+ LuaSetEnum("SWIPE_RIGHT", GESTURE_SWIPE_RIGHT);
+ LuaSetEnum("SWIPE_LEFT", GESTURE_SWIPE_LEFT);
+ LuaSetEnum("SWIPE_UP", GESTURE_SWIPE_UP);
+ LuaSetEnum("SWIPE_DOWN", GESTURE_SWIPE_DOWN);
+ LuaSetEnum("PINCH_IN", GESTURE_PINCH_IN);
+ LuaSetEnum("PINCH_OUT", GESTURE_PINCH_OUT);
+ LuaEndEnum("Gestures");
+
+ LuaStartEnum();
+ LuaSetEnum("CUSTOM", CAMERA_CUSTOM);
+ LuaSetEnum("FREE", CAMERA_FREE);
+ LuaSetEnum("ORBITAL", CAMERA_ORBITAL);
+ LuaSetEnum("FIRST_PERSON", CAMERA_FIRST_PERSON);
+ LuaSetEnum("THIRD_PERSON", CAMERA_THIRD_PERSON);
+ LuaEndEnum("CameraMode");
+
+ LuaStartEnum();
+ LuaSetEnum("DEFAULT_DEVICE", HMD_DEFAULT_DEVICE);
+ LuaSetEnum("OCULUS_RIFT_DK2", HMD_OCULUS_RIFT_DK2);
+ LuaSetEnum("OCULUS_RIFT_CV1", HMD_OCULUS_RIFT_CV1);
+ LuaSetEnum("VALVE_HTC_VIVE", HMD_VALVE_HTC_VIVE);
+ LuaSetEnum("SAMSUNG_GEAR_VR", HMD_SAMSUNG_GEAR_VR);
+ LuaSetEnum("GOOGLE_CARDBOARD", HMD_GOOGLE_CARDBOARD);
+ LuaSetEnum("SONY_PLAYSTATION_VR", HMD_SONY_PLAYSTATION_VR);
+ LuaSetEnum("RAZER_OSVR", HMD_RAZER_OSVR);
+ LuaSetEnum("FOVE_VR", HMD_FOVE_VR);
+ LuaEndEnum("VrDevice");
+
+ lua_pushglobaltable(L);
+ LuaSetEnum("INFO", INFO);
+ LuaSetEnum("ERROR", ERROR);
+ LuaSetEnum("WARNING", WARNING);
+ LuaSetEnum("DEBUG", DEBUG);
+ LuaSetEnum("OTHER", OTHER);
+ lua_pop(L, 1);
+
+ lua_pushboolean(L, true);
+#if defined(PLATFORM_DESKTOP)
+ lua_setglobal(L, "PLATFORM_DESKTOP");
+#elif defined(PLATFORM_ANDROID)
+ lua_setglobal(L, "PLATFORM_ANDROID");
+#elif defined(PLATFORM_RPI)
+ lua_setglobal(L, "PLATFORM_RPI");
+#elif defined(PLATFORM_WEB)
+ lua_setglobal(L, "PLATFORM_WEB");
+#endif
+
+ luaL_openlibs(L);
+ LuaBuildOpaqueMetatables();
+
+ LuaRegisterRayLib(0);
+}
+
+// De-initialize Lua system
+RLUADEF void CloseLuaDevice(void)
+{
+ if (mainLuaState)
+ {
+ lua_close(mainLuaState);
+ mainLuaState = 0;
+ L = 0;
+ }
+}
+
+// Execute raylib Lua code
+RLUADEF void ExecuteLuaCode(const char *code)
+{
+ if (!mainLuaState)
+ {
+ TraceLog(WARNING, "Lua device not initialized");
+ return;
+ }
+
+ int result = luaL_dostring(L, code);
+
+ switch (result)
+ {
+ case LUA_OK: break;
+ case LUA_ERRRUN: TraceLog(ERROR, "Lua Runtime Error: %s", lua_tostring(L, -1)); break;
+ case LUA_ERRMEM: TraceLog(ERROR, "Lua Memory Error: %s", lua_tostring(L, -1)); break;
+ default: TraceLog(ERROR, "Lua Error: %s", lua_tostring(L, -1)); break;
+ }
+}
+
+// Execute raylib Lua script
+RLUADEF void ExecuteLuaFile(const char *filename)
+{
+ if (!mainLuaState)
+ {
+ TraceLog(WARNING, "Lua device not initialized");
+ return;
+ }
+
+ int result = luaL_dofile(L, filename);
+
+ switch (result)
+ {
+ case LUA_OK: break;
+ case LUA_ERRRUN: TraceLog(ERROR, "Lua Runtime Error: %s", lua_tostring(L, -1));
+ case LUA_ERRMEM: TraceLog(ERROR, "Lua Memory Error: %s", lua_tostring(L, -1));
+ default: TraceLog(ERROR, "Lua Error: %s", lua_tostring(L, -1));
+ }
+}
+
+#endif // RLUA_IMPLEMENTATION
diff --git a/src/shader_standard.h b/src/shader_standard.h
index deae7fe1..995c62ea 100644
--- a/src/shader_standard.h
+++ b/src/shader_standard.h
@@ -90,7 +90,7 @@ static const char fStandardShaderStr[] =
" if (diff > 0.0)\n"
" {\n"
" vec3 h = normalize(-l.direction + v);\n"
-" spec = pow(dot(n, h), 3.0 + glossiness)*s;\n"
+" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n"
" }\n"
" return (diff*l.diffuse.rgb + spec*colSpecular.rgb);\n"
"}\n"
@@ -103,7 +103,7 @@ static const char fStandardShaderStr[] =
" if (diff > 0.0)\n"
" {\n"
" vec3 h = normalize(lightDir + v);\n"
-" spec = pow(dot(n, h), 3.0 + glossiness)*s;\n"
+" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n"
" }\n"
" return (diff*l.intensity*l.diffuse.rgb + spec*colSpecular.rgb);\n"
"}\n"
@@ -124,7 +124,7 @@ static const char fStandardShaderStr[] =
" if (diffAttenuation > 0.0)\n"
" {\n"
" vec3 h = normalize(lightDir + v);\n"
-" spec = pow(dot(n, h), 3.0 + glossiness)*s;\n"
+" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n"
" }\n"
" return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));\n"
"}\n"
@@ -152,9 +152,9 @@ static const char fStandardShaderStr[] =
" }\n"
" float spec = 1.0;\n"
#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21)
-" if (useSpecular == 1) spec *= normalize(texture2D(texture2, fragTexCoord).r);\n"
+" if (useSpecular == 1) spec = texture2D(texture2, fragTexCoord).r;\n"
#elif defined(GRAPHICS_API_OPENGL_33)
-" if (useSpecular == 1) spec *= normalize(texture(texture2, fragTexCoord).r);\n"
+" if (useSpecular == 1) spec = texture(texture2, fragTexCoord).r;\n"
#endif
" for (int i = 0; i < maxLights; i++)\n"
" {\n"
diff --git a/src/shapes.c b/src/shapes.c
index d9b172f1..9fcbeff7 100644
--- a/src/shapes.c
+++ b/src/shapes.c
@@ -113,7 +113,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
rlVertex2f(centerX + sin(DEG2RAD*i)*radius, centerY + cos(DEG2RAD*i)*radius);
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
- rlVertex2f(centerX + sin(DEG2RAD*(i + 10)) * radius, centerY + cos(DEG2RAD*(i + 10))*radius);
+ rlVertex2f(centerX + sin(DEG2RAD*(i + 10))*radius, centerY + cos(DEG2RAD*(i + 10))*radius);
}
rlEnd();
}
@@ -129,9 +129,9 @@ void DrawCircleV(Vector2 center, float radius, Color color)
{
rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(center.x, center.y);
+ rlVertex2f(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 + 10))*radius, center.y + cos(DEG2RAD*(i + 10))*radius);
}
rlEnd();
}
@@ -144,10 +144,10 @@ void DrawCircleV(Vector2 center, float radius, Color color)
{
rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(center.x, center.y);
+ rlVertex2f(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);
+ 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();
@@ -165,7 +165,7 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color)
for (int i = 0; i < 360; i += 10)
{
rlVertex2f(centerX + sin(DEG2RAD*i)*radius, centerY + cos(DEG2RAD*i)*radius);
- rlVertex2f(centerX + sin(DEG2RAD*(i + 10)) * radius, centerY + cos(DEG2RAD*(i + 10))*radius);
+ rlVertex2f(centerX + sin(DEG2RAD*(i + 10))*radius, centerY + cos(DEG2RAD*(i + 10))*radius);
}
rlEnd();
}
@@ -330,7 +330,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
{
rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(0, 0);
+ rlVertex2f(0, 0);
rlVertex2f(sin(DEG2RAD*i)*radius, cos(DEG2RAD*i)*radius);
rlVertex2f(sin(DEG2RAD*(i + 360/sides))*radius, cos(DEG2RAD*(i + 360/sides))*radius);
}
@@ -347,11 +347,11 @@ void DrawPolyEx(Vector2 *points, int numPoints, Color color)
rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
- for (int i = 0; i < numPoints - 2; i++)
+ for (int i = 1; i < numPoints - 1; i++)
{
+ rlVertex2f(points[0].x, points[0].y);
rlVertex2f(points[i].x, points[i].y);
rlVertex2f(points[i + 1].x, points[i + 1].y);
- rlVertex2f(points[i + 2].x, points[i + 2].y);
}
rlEnd();
}
diff --git a/src/text.c b/src/text.c
index ec2480e3..d1d7602f 100644
--- a/src/text.c
+++ b/src/text.c
@@ -34,7 +34,7 @@
// Following libs are used on LoadTTF()
#define STB_TRUETYPE_IMPLEMENTATION
-#include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap()
+#include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap()
// Rectangle packing functions (not used at the moment)
//#define STB_RECT_PACK_IMPLEMENTATION
@@ -43,8 +43,7 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define FONT_FIRST_CHAR 32
-#define MAX_FONTCHARS 128
+#define FONT_FIRST_CHAR 32 // NOTE: Expected first char for a sprite font
#define MAX_FORMATTEXT_LENGTH 64
#define MAX_SUBTEXT_LENGTH 64
@@ -72,7 +71,9 @@ static SpriteFont defaultFont; // Default font provided by raylib
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)
+
+// Generate a sprite font image from TTF data
+static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars);
extern void LoadDefaultFont(void);
extern void UnloadDefaultFont(void);
@@ -150,7 +151,7 @@ extern void LoadDefaultFont(void)
//----------------------------------------------------------------------
int imWidth = 128;
int imHeight = 128;
-
+
Color *imagePixels = (Color *)malloc(imWidth*imHeight*sizeof(Color));
for (int i = 0; i < imWidth*imHeight; i++) imagePixels[i] = BLANK; // Initialize array
@@ -173,7 +174,7 @@ extern void LoadDefaultFont(void)
//FILE *myimage = fopen("default_font.raw", "wb");
//fwrite(image.pixels, 1, 128*128*4, myimage);
//fclose(myimage);
-
+
Image image = LoadImageEx(imagePixels, imWidth, imHeight);
ImageFormat(&image, UNCOMPRESSED_GRAY_ALPHA);
@@ -184,13 +185,13 @@ extern void LoadDefaultFont(void)
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars
//------------------------------------------------------------------------------
- defaultFont.charValues = (int *)malloc(defaultFont.numChars*sizeof(int));
+ defaultFont.charValues = (int *)malloc(defaultFont.numChars*sizeof(int));
defaultFont.charRecs = (Rectangle *)malloc(defaultFont.numChars*sizeof(Rectangle)); // Allocate space for our character rectangle data
// This memory should be freed at end! --> Done on CloseWindow()
-
+
defaultFont.charOffsets = (Vector2 *)malloc(defaultFont.numChars*sizeof(Vector2));
defaultFont.charAdvanceX = (int *)malloc(defaultFont.numChars*sizeof(int));
-
+
int currentLine = 0;
int currentPosX = charsDivisor;
int testPosX = charsDivisor;
@@ -198,9 +199,9 @@ extern void LoadDefaultFont(void)
for (int i = 0; i < defaultFont.numChars; i++)
{
defaultFont.charValues[i] = FONT_FIRST_CHAR + i; // First char is 32
-
+
defaultFont.charRecs[i].x = currentPosX;
- defaultFont.charRecs[i].y = charsDivisor + currentLine * (charsHeight + charsDivisor);
+ defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor);
defaultFont.charRecs[i].width = charsWidth[i];
defaultFont.charRecs[i].height = charsHeight;
@@ -216,12 +217,12 @@ extern void LoadDefaultFont(void)
defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor);
}
else currentPosX = testPosX;
-
+
// NOTE: On default font character offsets and xAdvance are not required
defaultFont.charOffsets[i] = (Vector2){ 0.0f, 0.0f };
defaultFont.charAdvanceX[i] = 0;
}
-
+
defaultFont.size = defaultFont.charRecs[0].height;
TraceLog(INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id);
@@ -245,19 +246,23 @@ SpriteFont GetDefaultFont()
// Load a SpriteFont image into GPU memory
SpriteFont LoadSpriteFont(const char *fileName)
{
+ // Default hardcoded values for ttf file loading
+ #define DEFAULT_TTF_FONTSIZE 32 // Font first character (32 - space)
+ #define DEFAULT_TTF_NUMCHARS 95 // ASCII 32..126 is 95 glyphs
+
SpriteFont spriteFont = { 0 };
// Check file extension
if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName);
- else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadTTF(fileName, 32);
+ else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadTTF(fileName, DEFAULT_TTF_FONTSIZE, FONT_FIRST_CHAR, DEFAULT_TTF_NUMCHARS);
else if (strcmp(GetExtension(fileName),"fnt") == 0) spriteFont = LoadBMFont(fileName);
else
{
Image image = LoadImage(fileName);
- if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, 32);
+ if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, FONT_FIRST_CHAR);
UnloadImage(image);
}
-
+
if (spriteFont.texture.id == 0)
{
TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName);
@@ -288,18 +293,22 @@ void UnloadSpriteFont(SpriteFont spriteFont)
// NOTE: chars spacing is proportional to fontSize
void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
{
- Vector2 position = { (float)posX, (float)posY };
+ // Check if default font has been loaded
+ if (defaultFont.texture.id != 0)
+ {
+ Vector2 position = { (float)posX, (float)posY };
- int defaultFontSize = 10; // Default Font chars height in pixel
- if (fontSize < defaultFontSize) fontSize = defaultFontSize;
- int spacing = fontSize / defaultFontSize;
+ int defaultFontSize = 10; // Default Font chars height in pixel
+ if (fontSize < defaultFontSize) fontSize = defaultFontSize;
+ int spacing = fontSize/defaultFontSize;
- DrawTextEx(defaultFont, text, position, fontSize, spacing, color);
+ DrawTextEx(GetDefaultFont(), text, position, (float)fontSize, spacing, color);
+ }
}
// Draw text using SpriteFont
// NOTE: chars spacing is NOT proportional to fontSize
-void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int fontSize, int spacing, Color tint)
+void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float fontSize, int spacing, Color tint)
{
int length = strlen(text);
int textOffsetX = 0;
@@ -309,17 +318,17 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int f
Rectangle rec;
- scaleFactor = (float)fontSize/spriteFont.size;
+ scaleFactor = fontSize/spriteFont.size;
- // NOTE: Some ugly hacks are made to support Latin-1 Extended characters directly
+ // NOTE: Some ugly hacks are made to support Latin-1 Extended characters directly
// written in C code files (codified by default as UTF-8)
-
- for(int i = 0; i < length; i++)
+
+ for (int i = 0; i < length; i++)
{
// TODO: Right now we are supposing characters that follow a continous order and start at FONT_FIRST_CHAR,
// this sytem can be improved to support any characters order and init value...
// An intermediate table could be created to link char values with predefined char position index in chars rectangle array
-
+
if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification HACK!
{
// Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿)
@@ -348,8 +357,8 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, int f
if (rec.x > 0)
{
- DrawTexturePro(spriteFont.texture, rec, (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].x*scaleFactor,
- position.y + textOffsetY + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].y*scaleFactor,
+ DrawTexturePro(spriteFont.texture, rec, (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].x*scaleFactor,
+ position.y + textOffsetY + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].y*scaleFactor,
rec.width*scaleFactor, rec.height*scaleFactor} , (Vector2){ 0, 0 }, 0.0f, tint);
if (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR] == 0) textOffsetX += (rec.width*scaleFactor + spacing);
@@ -376,15 +385,15 @@ const char *SubText(const char *text, int position, int length)
{
static char buffer[MAX_SUBTEXT_LENGTH];
int textLength = strlen(text);
-
+
if (position >= textLength)
{
position = textLength - 1;
length = 0;
}
-
+
if (length >= textLength) length = textLength;
-
+
for (int c = 0 ; c < length ; c++)
{
*(buffer+c) = *(text+position);
@@ -399,13 +408,17 @@ const char *SubText(const char *text, int position, int length)
// Measure string width for default font
int MeasureText(const char *text, int fontSize)
{
- Vector2 vec;
+ Vector2 vec = { 0.0f, 0.0f };
- int defaultFontSize = 10; // Default Font chars height in pixel
- if (fontSize < defaultFontSize) fontSize = defaultFontSize;
- int spacing = fontSize / defaultFontSize;
+ // Check if default font has been loaded
+ if (defaultFont.texture.id != 0)
+ {
+ int defaultFontSize = 10; // Default Font chars height in pixel
+ if (fontSize < defaultFontSize) fontSize = defaultFontSize;
+ int spacing = fontSize/defaultFontSize;
- vec = MeasureTextEx(defaultFont, text, fontSize, spacing);
+ vec = MeasureTextEx(GetDefaultFont(), text, fontSize, spacing);
+ }
return (int)vec.x;
}
@@ -416,17 +429,17 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int
int len = strlen(text);
int tempLen = 0; // Used to count longer text line num chars
int lenCounter = 0;
-
+
int textWidth = 0;
int tempTextWidth = 0; // Used to count longer text line width
-
+
int textHeight = spriteFont.size;
float scaleFactor;
for (int i = 0; i < len; i++)
{
lenCounter++;
-
+
if (text[i] != '\n')
{
if (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR] != 0) textWidth += spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR];
@@ -439,10 +452,10 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int
textWidth = 0;
textHeight += (spriteFont.size + spriteFont.size/2); // NOTE: Fixed line spacing of 1.5 lines
}
-
+
if (tempLen < lenCounter) tempLen = lenCounter;
}
-
+
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
if (fontSize <= spriteFont.size) scaleFactor = 1.0f;
@@ -491,24 +504,27 @@ void DrawFPS(int posX, int posY)
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;
int x = 0;
int y = 0;
- // We allocate a temporal arrays for chars data measures,
+ // Default number of characters expected supported
+ #define MAX_FONTCHARS 128
+
+ // We allocate a temporal arrays for chars data measures,
// once we get the actual number of chars, we copy data to a sized arrays
int tempCharValues[MAX_FONTCHARS];
Rectangle tempCharRecs[MAX_FONTCHARS];
-
+
Color *pixels = GetImageData(image);
// Parse image data to get charSpacing and lineSpacing
- for(y = 0; y < image.height; y++)
+ for (y = 0; y < image.height; y++)
{
- for(x = 0; x < image.width; x++)
+ for (x = 0; x < image.width; x++)
{
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
}
@@ -521,7 +537,7 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
int charHeight = 0;
int j = 0;
- while(!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
+ while (!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
charHeight = j;
@@ -531,20 +547,20 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
int xPosToRead = charSpacing;
// Parse image data to get rectangle sizes
- while((lineSpacing + lineToRead * (charHeight + lineSpacing)) < image.height)
+ while ((lineSpacing + lineToRead*(charHeight + lineSpacing)) < image.height)
{
- while((xPosToRead < image.width) &&
+ while ((xPosToRead < image.width) &&
!COLOR_EQUAL((pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead]), key))
{
tempCharValues[index] = firstChar + index;
-
+
tempCharRecs[index].x = xPosToRead;
- tempCharRecs[index].y = lineSpacing + lineToRead * (charHeight + lineSpacing);
+ tempCharRecs[index].y = lineSpacing + lineToRead*(charHeight + lineSpacing);
tempCharRecs[index].height = charHeight;
int charWidth = 0;
- while(!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
+ while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
tempCharRecs[index].width = charWidth;
@@ -556,14 +572,14 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
lineToRead++;
xPosToRead = charSpacing;
}
-
+
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;
@@ -578,13 +594,15 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
{
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;
+
+ TraceLog(INFO, "Image file loaded correctly as SpriteFont");
return spriteFont;
}
@@ -623,7 +641,7 @@ static SpriteFont LoadRBMF(const char *fileName)
if (rbmfFile == NULL)
{
TraceLog(WARNING, "[%s] rBMF font file could not be opened, using default font", fileName);
-
+
spriteFont = GetDefaultFont();
}
else
@@ -634,15 +652,15 @@ static SpriteFont LoadRBMF(const char *fileName)
spriteFont.numChars = (int)rbmfHeader.numChars;
- int numPixelBits = rbmfHeader.imgWidth * rbmfHeader.imgHeight / 32;
+ int numPixelBits = rbmfHeader.imgWidth*rbmfHeader.imgHeight/32;
- rbmfFileData = (unsigned int *)malloc(numPixelBits * sizeof(unsigned int));
+ rbmfFileData = (unsigned int *)malloc(numPixelBits*sizeof(unsigned int));
- for(int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile);
+ for (int i = 0; i < numPixelBits; i++) fread(&rbmfFileData[i], sizeof(unsigned int), 1, rbmfFile);
- rbmfCharWidthData = (unsigned char *)malloc(spriteFont.numChars * sizeof(unsigned char));
+ rbmfCharWidthData = (unsigned char *)malloc(spriteFont.numChars*sizeof(unsigned char));
- for(int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile);
+ for (int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile);
// Re-construct image from rbmfFileData
//-----------------------------------------
@@ -662,10 +680,10 @@ static SpriteFont LoadRBMF(const char *fileName)
counter++;
}
-
+
Image image = LoadImageEx(imagePixels, rbmfHeader.imgWidth, rbmfHeader.imgHeight);
ImageFormat(&image, UNCOMPRESSED_GRAY_ALPHA);
-
+
free(imagePixels);
TraceLog(DEBUG, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
@@ -677,7 +695,7 @@ static SpriteFont LoadRBMF(const char *fileName)
//TraceLog(INFO, "[%s] Starting chars set reconstruction", fileName);
// Get characters data using rbmfCharWidthData, rbmfHeader.charHeight, charsDivisor, rbmfHeader.numChars
- spriteFont.charValues = (int *)malloc(spriteFont.numChars*sizeof(int));
+ spriteFont.charValues = (int *)malloc(spriteFont.numChars*sizeof(int));
spriteFont.charRecs = (Rectangle *)malloc(spriteFont.numChars*sizeof(Rectangle));
spriteFont.charOffsets = (Vector2 *)malloc(spriteFont.numChars*sizeof(Vector2));
spriteFont.charAdvanceX = (int *)malloc(spriteFont.numChars*sizeof(int));
@@ -689,12 +707,12 @@ static SpriteFont LoadRBMF(const char *fileName)
for (int i = 0; i < spriteFont.numChars; i++)
{
spriteFont.charValues[i] = (int)rbmfHeader.firstChar + i;
-
+
spriteFont.charRecs[i].x = currentPosX;
- spriteFont.charRecs[i].y = charsDivisor + currentLine * ((int)rbmfHeader.charHeight + charsDivisor);
+ spriteFont.charRecs[i].y = charsDivisor + currentLine*((int)rbmfHeader.charHeight + charsDivisor);
spriteFont.charRecs[i].width = (int)rbmfCharWidthData[i];
spriteFont.charRecs[i].height = (int)rbmfHeader.charHeight;
-
+
// 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;
@@ -704,15 +722,15 @@ static SpriteFont LoadRBMF(const char *fileName)
if (testPosX > spriteFont.texture.width)
{
currentLine++;
- currentPosX = 2 * charsDivisor + (int)rbmfCharWidthData[i];
+ currentPosX = 2*charsDivisor + (int)rbmfCharWidthData[i];
testPosX = currentPosX;
spriteFont.charRecs[i].x = charsDivisor;
- spriteFont.charRecs[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor);
+ spriteFont.charRecs[i].y = charsDivisor + currentLine*(rbmfHeader.charHeight + charsDivisor);
}
else currentPosX = testPosX;
}
-
+
spriteFont.size = spriteFont.charRecs[0].height;
TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName);
@@ -730,20 +748,20 @@ static SpriteFont LoadRBMF(const char *fileName)
static SpriteFont LoadBMFont(const char *fileName)
{
#define MAX_BUFFER_SIZE 256
-
+
SpriteFont font = { 0 };
font.texture.id = 0;
-
+
char buffer[MAX_BUFFER_SIZE];
char *searchPoint = NULL;
-
+
int fontSize = 0;
int texWidth, texHeight;
char texFileName[128];
int numChars = 0;
int base; // Useless data
-
+
FILE *fntFile;
fntFile = fopen(fileName, "rt");
@@ -758,42 +776,42 @@ static SpriteFont LoadBMFont(const char *fileName)
fgets(buffer, MAX_BUFFER_SIZE, fntFile);
//searchPoint = strstr(buffer, "size");
//sscanf(searchPoint, "size=%i", &fontSize);
-
+
fgets(buffer, MAX_BUFFER_SIZE, fntFile);
searchPoint = strstr(buffer, "lineHeight");
sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &texWidth, &texHeight);
-
+
TraceLog(DEBUG, "[%s] Font size: %i", fileName, fontSize);
TraceLog(DEBUG, "[%s] Font texture scale: %ix%i", fileName, texWidth, texHeight);
-
+
fgets(buffer, MAX_BUFFER_SIZE, fntFile);
searchPoint = strstr(buffer, "file");
sscanf(searchPoint, "file=\"%128[^\"]\"", texFileName);
-
+
TraceLog(DEBUG, "[%s] Font texture filename: %s", fileName, texFileName);
-
+
fgets(buffer, MAX_BUFFER_SIZE, fntFile);
searchPoint = strstr(buffer, "count");
sscanf(searchPoint, "count=%i", &numChars);
-
+
TraceLog(DEBUG, "[%s] Font num chars: %i", fileName, numChars);
-
+
// Compose correct path using route of .fnt file (fileName) and texFileName
char *texPath = NULL;
char *lastSlash = NULL;
lastSlash = strrchr(fileName, '/');
-
+
// NOTE: We need some extra space to avoid memory corruption on next allocations!
texPath = malloc(strlen(fileName) - strlen(lastSlash) + strlen(texFileName) + 4);
-
+
// NOTE: strcat() and strncat() required a '\0' terminated string to work!
*texPath = '\0';
strncat(texPath, fileName, strlen(fileName) - strlen(lastSlash) + 1);
strncat(texPath, texFileName, strlen(texFileName));
TraceLog(DEBUG, "[%s] Font texture loading path: %s", fileName, texPath);
-
+
font.texture = LoadTexture(texPath);
font.size = fontSize;
font.numChars = numChars;
@@ -801,35 +819,35 @@ static SpriteFont LoadBMFont(const char *fileName)
font.charRecs = (Rectangle *)malloc(numChars*sizeof(Rectangle));
font.charOffsets = (Vector2 *)malloc(numChars*sizeof(Vector2));
font.charAdvanceX = (int *)malloc(numChars*sizeof(int));
-
+
free(texPath);
-
+
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",
+ 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;
font.charRecs[i] = (Rectangle){ charX, charY, charWidth, charHeight };
font.charOffsets[i] = (Vector2){ (float)charOffsetX, (float)charOffsetY };
font.charAdvanceX[i] = charAdvanceX;
}
-
+
fclose(fntFile);
-
+
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) || (font.texture.id == 0))
{
@@ -843,22 +861,20 @@ static SpriteFont LoadBMFont(const char *fileName)
// Generate a sprite font from TTF file data (font size required)
// TODO: Review texture packing method and generation (use oversampling)
-static SpriteFont LoadTTF(const char *fileName, int fontSize)
+static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars)
{
// NOTE: Generated font uses some hardcoded values
#define FONT_TEXTURE_WIDTH 512 // Font texture width
#define FONT_TEXTURE_HEIGHT 512 // Font texture height
- #define FONT_FIRST_CHAR 32 // Font first character (32 - space)
- #define FONT_NUM_CHARS 95 // ASCII 32..126 is 95 glyphs
unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25);
unsigned char *dataBitmap = (unsigned char *)malloc(FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT*sizeof(unsigned char)); // One channel bitmap returned!
- stbtt_bakedchar charData[FONT_NUM_CHARS]; // ASCII 32..126 is 95 glyphs
+ stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*numChars);
SpriteFont font = { 0 };
-
+
FILE *ttfFile = fopen(fileName, "rb");
-
+
if (ttfFile == NULL)
{
TraceLog(WARNING, "[%s] FNT file could not be opened", fileName);
@@ -868,14 +884,14 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize)
fread(ttfBuffer, 1, 1<<25, ttfFile);
// NOTE: Using stb_truetype crappy packing method, no guarante the font fits the image...
- stbtt_BakeFontBitmap(ttfBuffer,0, fontSize, dataBitmap, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, FONT_FIRST_CHAR, FONT_NUM_CHARS, charData);
+ stbtt_BakeFontBitmap(ttfBuffer,0, fontSize, dataBitmap, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, firstChar, numChars, charData);
free(ttfBuffer);
-
+
// Convert image data from grayscale to to UNCOMPRESSED_GRAY_ALPHA
unsigned char *dataGrayAlpha = (unsigned char *)malloc(FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT*sizeof(unsigned char)*2); // Two channels
int k = 0;
-
+
for (int i = 0; i < FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT; i++)
{
dataGrayAlpha[k] = 255;
@@ -883,9 +899,9 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize)
k += 2;
}
-
+
free(dataBitmap);
-
+
// Sprite font generation from TTF extracted data
Image image;
image.width = FONT_TEXTURE_WIDTH;
@@ -898,24 +914,26 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize)
UnloadImage(image); // Unloads dataGrayAlpha
font.size = fontSize;
- font.numChars = FONT_NUM_CHARS;
+ font.numChars = numChars;
font.charValues = (int *)malloc(font.numChars*sizeof(int));
font.charRecs = (Rectangle *)malloc(font.numChars*sizeof(Rectangle));
font.charOffsets = (Vector2 *)malloc(font.numChars*sizeof(Vector2));
font.charAdvanceX = (int *)malloc(font.numChars*sizeof(int));
-
+
for (int i = 0; i < font.numChars; i++)
{
- font.charValues[i] = i + FONT_FIRST_CHAR;
+ font.charValues[i] = i + firstChar;
font.charRecs[i].x = (int)charData[i].x0;
font.charRecs[i].y = (int)charData[i].y0;
font.charRecs[i].width = (int)charData[i].x1 - (int)charData[i].x0;
font.charRecs[i].height = (int)charData[i].y1 - (int)charData[i].y0;
-
+
font.charOffsets[i] = (Vector2){ charData[i].xoff, charData[i].yoff };
font.charAdvanceX[i] = (int)charData[i].xadvance;
}
+ free(charData);
+
return font;
} \ No newline at end of file
diff --git a/src/textures.c b/src/textures.c
index f5523a3e..fd5bdd80 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -33,18 +33,28 @@
#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(),
+ // Required: rlglLoadTexture() rlDeleteTextures(),
// rlglGenerateMipmaps(), some funcs for DrawTexturePro()
#include "utils.h" // rRES data decompression utility function
// NOTE: Includes Android fopen function map
+// Support only desired texture formats, by default: JPEG, PNG, BMP, TGA
+//#define STBI_NO_JPEG // Image format .jpg and .jpeg
+//#define STBI_NO_PNG
+//#define STBI_NO_BMP
+//#define STBI_NO_TGA
+#define STBI_NO_PSD
+#define STBI_NO_GIF
+#define STBI_NO_HDR
+#define STBI_NO_PIC
+#define STBI_NO_PNM // Image format .ppm and .pgm
#define STB_IMAGE_IMPLEMENTATION
#include "external/stb_image.h" // Required for: stbi_load()
// NOTE: Used to read image data (multiple formats support)
#define STB_IMAGE_RESIZE_IMPLEMENTATION
-#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8()
+#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8()
// NOTE: Used for image scaling on ImageResize()
//----------------------------------------------------------------------------------
@@ -95,22 +105,29 @@ Image LoadImage(const char *fileName)
if ((strcmp(GetExtension(fileName),"png") == 0) ||
(strcmp(GetExtension(fileName),"bmp") == 0) ||
(strcmp(GetExtension(fileName),"tga") == 0) ||
- (strcmp(GetExtension(fileName),"jpg") == 0) ||
- (strcmp(GetExtension(fileName),"gif") == 0) ||
- (strcmp(GetExtension(fileName),"psd") == 0) ||
- (strcmp(GetExtension(fileName),"pic") == 0))
+ (strcmp(GetExtension(fileName),"jpg") == 0)
+#ifndef STBI_NO_GIF
+ || (strcmp(GetExtension(fileName),"gif") == 0)
+#endif
+#ifndef STBI_NO_PSD
+ || (strcmp(GetExtension(fileName),"psd") == 0)
+#endif
+#ifndef STBI_NO_PIC
+ || (strcmp(GetExtension(fileName),"pic") == 0)
+#endif
+ )
{
int imgWidth = 0;
int imgHeight = 0;
int imgBpp = 0;
-
+
// NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...)
image.data = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 0);
image.width = imgWidth;
image.height = imgHeight;
image.mipmaps = 1;
-
+
if (imgBpp == 1) image.format = UNCOMPRESSED_GRAYSCALE;
else if (imgBpp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA;
else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8;
@@ -121,12 +138,12 @@ Image LoadImage(const char *fileName)
else if (strcmp(GetExtension(fileName),"ktx") == 0) image = LoadKTX(fileName);
else if (strcmp(GetExtension(fileName),"pvr") == 0) image = LoadPVR(fileName);
else if (strcmp(GetExtension(fileName),"astc") == 0) image = LoadASTC(fileName);
-
+
if (image.data != NULL)
- {
+ {
TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height);
}
- else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName);
+ else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", fileName);
return image;
}
@@ -141,11 +158,11 @@ Image LoadImageEx(Color *pixels, int width, int height)
image.height = height;
image.mipmaps = 1;
image.format = UNCOMPRESSED_R8G8B8A8;
-
+
int k = 0;
image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char));
-
+
for (int i = 0; i < image.width*image.height*4; i += 4)
{
((unsigned char *)image.data)[i] = pixels[k].r;
@@ -180,7 +197,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
if (headerSize > 0) fseek(rawFile, headerSize, SEEK_SET);
unsigned int size = width*height;
-
+
switch (format)
{
case UNCOMPRESSED_GRAYSCALE: image.data = (unsigned char *)malloc(size); break; // 8 bit per pixel (no alpha)
@@ -192,16 +209,16 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
case UNCOMPRESSED_R8G8B8A8: image.data = (unsigned char *)malloc(size*4); size *= 4; break; // 32 bpp
default: TraceLog(WARNING, "Image format not suported"); break;
}
-
+
fread(image.data, size, 1, rawFile);
-
+
// TODO: Check if data have been read
-
+
image.width = width;
image.height = height;
image.mipmaps = 0;
image.format = format;
-
+
fclose(rawFile);
}
@@ -212,7 +229,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
// TODO: Review function to support multiple color modes
Image LoadImageFromRES(const char *rresName, int resId)
{
- Image image;
+ Image image = { 0 };
bool found = false;
char id[4]; // rRES file identifier
@@ -326,9 +343,9 @@ Texture2D LoadTexture(const char *fileName)
Texture2D texture;
Image image = LoadImage(fileName);
-
+
if (image.data != NULL)
- {
+ {
texture = LoadTextureFromImage(image);
UnloadImage(image);
}
@@ -350,9 +367,9 @@ Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat)
texture.height = height;
texture.mipmaps = 1;
texture.format = textureFormat;
-
+
texture.id = rlglLoadTexture(data, width, height, textureFormat, 1);
-
+
return texture;
}
@@ -380,7 +397,7 @@ Texture2D LoadTextureFromImage(Image image)
texture.height = 0;
texture.mipmaps = 0;
texture.format = 0;
-
+
texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps);
texture.width = image.width;
@@ -395,7 +412,7 @@ Texture2D LoadTextureFromImage(Image image)
RenderTexture2D LoadRenderTexture(int width, int height)
{
RenderTexture2D target = rlglLoadRenderTexture(width, height);
-
+
return target;
}
@@ -403,7 +420,7 @@ RenderTexture2D LoadRenderTexture(int width, int height)
void UnloadImage(Image image)
{
free(image.data);
-
+
// NOTE: It becomes anoying every time a texture is loaded
//TraceLog(INFO, "Unloaded image data");
}
@@ -414,7 +431,7 @@ void UnloadTexture(Texture2D texture)
if (texture.id != 0)
{
rlDeleteTextures(texture.id);
-
+
TraceLog(INFO, "[TEX ID %i] Unloaded texture data from VRAM (GPU)", texture.id);
}
}
@@ -429,7 +446,7 @@ void UnloadRenderTexture(RenderTexture2D target)
Color *GetImageData(Image image)
{
Color *pixels = (Color *)malloc(image.width*image.height*sizeof(Color));
-
+
int k = 0;
for (int i = 0; i < image.width*image.height; i++)
@@ -442,7 +459,7 @@ Color *GetImageData(Image image)
pixels[i].g = ((unsigned char *)image.data)[k];
pixels[i].b = ((unsigned char *)image.data)[k];
pixels[i].a = 255;
-
+
k++;
} break;
case UNCOMPRESSED_GRAY_ALPHA:
@@ -451,7 +468,7 @@ Color *GetImageData(Image image)
pixels[i].g = ((unsigned char *)image.data)[k];
pixels[i].b = ((unsigned char *)image.data)[k];
pixels[i].a = ((unsigned char *)image.data)[k + 1];
-
+
k += 2;
} break;
case UNCOMPRESSED_R5G5B5A1:
@@ -462,7 +479,7 @@ Color *GetImageData(Image image)
pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255);
-
+
k++;
} break;
case UNCOMPRESSED_R5G6B5:
@@ -473,18 +490,18 @@ Color *GetImageData(Image image)
pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
pixels[i].a = 255;
-
+
k++;
} break;
case UNCOMPRESSED_R4G4B4A4:
{
unsigned short pixel = ((unsigned short *)image.data)[k];
-
+
pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
-
+
k++;
} break;
case UNCOMPRESSED_R8G8B8A8:
@@ -493,7 +510,7 @@ Color *GetImageData(Image image)
pixels[i].g = ((unsigned char *)image.data)[k + 1];
pixels[i].b = ((unsigned char *)image.data)[k + 2];
pixels[i].a = ((unsigned char *)image.data)[k + 3];
-
+
k += 4;
} break;
case UNCOMPRESSED_R8G8B8:
@@ -502,11 +519,11 @@ Color *GetImageData(Image image)
pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1];
pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2];
pixels[i].a = 255;
-
+
k += 3;
} break;
default: TraceLog(WARNING, "Format not supported for pixel data retrieval"); break;
- }
+ }
}
return pixels;
@@ -522,7 +539,7 @@ Image GetTextureData(Texture2D texture)
if (texture.format < 8)
{
image.data = rlglReadTexturePixels(texture);
-
+
if (image.data != NULL)
{
image.width = texture.width;
@@ -548,32 +565,32 @@ void ImageFormat(Image *image, int newFormat)
{
if (image->format != newFormat)
{
- if ((image->format < 8) && (newFormat < 8))
+ if ((image->format < COMPRESSED_DXT1_RGB) && (newFormat < COMPRESSED_DXT1_RGB))
{
Color *pixels = GetImageData(*image);
-
+
free(image->data);
-
+
image->format = newFormat;
int k = 0;
-
+
switch (image->format)
{
case UNCOMPRESSED_GRAYSCALE:
{
image->data = (unsigned char *)malloc(image->width*image->height*sizeof(unsigned char));
-
+
for (int i = 0; i < image->width*image->height; i++)
{
((unsigned char *)image->data)[i] = (unsigned char)((float)pixels[i].r*0.299f + (float)pixels[i].g*0.587f + (float)pixels[i].b*0.114f);
}
-
+
} break;
case UNCOMPRESSED_GRAY_ALPHA:
{
image->data = (unsigned char *)malloc(image->width*image->height*2*sizeof(unsigned char));
-
+
for (int i = 0; i < image->width*image->height*2; i += 2)
{
((unsigned char *)image->data)[i] = (unsigned char)((float)pixels[k].r*0.299f + (float)pixels[k].g*0.587f + (float)pixels[k].b*0.114f);
@@ -585,17 +602,17 @@ void ImageFormat(Image *image, int newFormat)
case UNCOMPRESSED_R5G6B5:
{
image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short));
-
+
unsigned char r = 0;
unsigned char g = 0;
unsigned char b = 0;
-
+
for (int i = 0; i < image->width*image->height; i++)
{
r = (unsigned char)(round((float)pixels[k].r*31/255));
g = (unsigned char)(round((float)pixels[k].g*63/255));
b = (unsigned char)(round((float)pixels[k].b*31/255));
-
+
((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
}
@@ -603,7 +620,7 @@ void ImageFormat(Image *image, int newFormat)
case UNCOMPRESSED_R8G8B8:
{
image->data = (unsigned char *)malloc(image->width*image->height*3*sizeof(unsigned char));
-
+
for (int i = 0; i < image->width*image->height*3; i += 3)
{
((unsigned char *)image->data)[i] = pixels[k].r;
@@ -615,49 +632,49 @@ void ImageFormat(Image *image, int newFormat)
case UNCOMPRESSED_R5G5B5A1:
{
#define ALPHA_THRESHOLD 50
-
+
image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short));
-
+
unsigned char r = 0;
unsigned char g = 0;
unsigned char b = 0;
unsigned char a = 0;
-
+
for (int i = 0; i < image->width*image->height; i++)
{
r = (unsigned char)(round((float)pixels[i].r*31/255));
g = (unsigned char)(round((float)pixels[i].g*31/255));
b = (unsigned char)(round((float)pixels[i].b*31/255));
a = (pixels[i].a > ALPHA_THRESHOLD) ? 1 : 0;
-
+
((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
}
-
+
} break;
case UNCOMPRESSED_R4G4B4A4:
{
image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short));
-
+
unsigned char r = 0;
unsigned char g = 0;
unsigned char b = 0;
unsigned char a = 0;
-
+
for (int i = 0; i < image->width*image->height; i++)
{
r = (unsigned char)(round((float)pixels[i].r*15/255));
g = (unsigned char)(round((float)pixels[i].g*15/255));
b = (unsigned char)(round((float)pixels[i].b*15/255));
a = (unsigned char)(round((float)pixels[i].a*15/255));
-
+
((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8| (unsigned short)b << 4| (unsigned short)a;
}
-
+
} break;
case UNCOMPRESSED_R8G8B8A8:
{
image->data = (unsigned char *)malloc(image->width*image->height*4*sizeof(unsigned char));
-
+
for (int i = 0; i < image->width*image->height*4; i += 4)
{
((unsigned char *)image->data)[i] = pixels[k].r;
@@ -669,19 +686,48 @@ void ImageFormat(Image *image, int newFormat)
} break;
default: break;
}
-
+
free(pixels);
}
else TraceLog(WARNING, "Image data format is compressed, can not be converted");
}
}
+// Apply alpha mask to image
+// NOTE 1: Returned image is RGBA - 32bit
+// NOTE 2: alphaMask should be same size as image
+void ImageAlphaMask(Image *image, Image alphaMask)
+{
+ if (image->format >= COMPRESSED_DXT1_RGB)
+ {
+ TraceLog(WARNING, "Alpha mask can not be applied to compressed data formats");
+ return;
+ }
+ else
+ {
+ // Force mask to be Grayscale
+ Image mask = ImageCopy(alphaMask);
+ ImageFormat(&mask, UNCOMPRESSED_GRAYSCALE);
+
+ // Convert image to RGBA
+ if (image->format != UNCOMPRESSED_R8G8B8A8) ImageFormat(image, UNCOMPRESSED_R8G8B8A8);
+
+ // Apply alpha mask to alpha channel
+ for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4)
+ {
+ ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
+ }
+
+ UnloadImage(mask);
+ }
+}
+
// Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
-// NOTE: In case selected bpp do not represent an known 16bit format,
+// NOTE: In case selected bpp do not represent an known 16bit format,
// dithered data is stored in the LSB part of the unsigned short
void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
{
- if (image->format >= 8)
+ if (image->format >= COMPRESSED_DXT1_RGB)
{
TraceLog(WARNING, "Compressed data formats can not be dithered");
return;
@@ -694,14 +740,14 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
else
{
Color *pixels = GetImageData(*image);
-
+
free(image->data); // free old image data
-
+
if ((image->format != UNCOMPRESSED_R8G8B8) && (image->format != UNCOMPRESSED_R8G8B8A8))
{
TraceLog(WARNING, "Image format is already 16bpp or lower, dithering could have no effect");
}
-
+
// Define new image format, check if desired bpp match internal known format
if ((rBpp == 5) && (gBpp == 6) && (bBpp == 5) && (aBpp == 0)) image->format = UNCOMPRESSED_R5G6B5;
else if ((rBpp == 5) && (gBpp == 5) && (bBpp == 5) && (aBpp == 1)) image->format = UNCOMPRESSED_R5G5B5A1;
@@ -714,13 +760,13 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
// NOTE: We will store the dithered data as unsigned short (16bpp)
image->data = (unsigned short *)malloc(image->width*image->height*sizeof(unsigned short));
-
+
Color oldpixel = WHITE;
Color newpixel = WHITE;
-
+
int error_r, error_g, error_b;
unsigned short pixel_r, pixel_g, pixel_b, pixel_a; // Used for 16bit pixel composition
-
+
#define MIN(a,b) (((a)<(b))?(a):(b))
for (int y = 0; y < image->height; y++)
@@ -728,7 +774,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
for (int x = 0; x < image->width; x++)
{
oldpixel = pixels[y*image->width + x];
-
+
// NOTE: New pixel obtained by bits truncate, it would be better to round values (check ImageFormat())
newpixel.r = oldpixel.r>>(8 - rBpp); // R bits
newpixel.g = oldpixel.g>>(8 - gBpp); // G bits
@@ -740,7 +786,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
error_r = (int)oldpixel.r - (int)(newpixel.r<<(8 - rBpp));
error_g = (int)oldpixel.g - (int)(newpixel.g<<(8 - gBpp));
error_b = (int)oldpixel.b - (int)(newpixel.b<<(8 - bBpp));
-
+
pixels[y*image->width + x] = newpixel;
// NOTE: Some cases are out of the array and should be ignored
@@ -750,21 +796,21 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)error_g*7.0f/16), 0xff);
pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)error_b*7.0f/16), 0xff);
}
-
+
if ((x > 0) && (y < (image->height - 1)))
{
pixels[(y+1)*image->width + x-1].r = MIN((int)pixels[(y+1)*image->width + x-1].r + (int)((float)error_r*3.0f/16), 0xff);
pixels[(y+1)*image->width + x-1].g = MIN((int)pixels[(y+1)*image->width + x-1].g + (int)((float)error_g*3.0f/16), 0xff);
pixels[(y+1)*image->width + x-1].b = MIN((int)pixels[(y+1)*image->width + x-1].b + (int)((float)error_b*3.0f/16), 0xff);
}
-
+
if (y < (image->height - 1))
{
pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)error_r*5.0f/16), 0xff);
pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)error_g*5.0f/16), 0xff);
pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)error_b*5.0f/16), 0xff);
}
-
+
if ((x < (image->width - 1)) && (y < (image->height - 1)))
{
pixels[(y+1)*image->width + x+1].r = MIN((int)pixels[(y+1)*image->width + x+1].r + (int)((float)error_r*1.0f/16), 0xff);
@@ -776,7 +822,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
pixel_g = (unsigned short)newpixel.g;
pixel_b = (unsigned short)newpixel.b;
pixel_a = (unsigned short)newpixel.a;
-
+
((unsigned short *)image->data)[y*image->width + x] = (pixel_r<<(gBpp + bBpp + aBpp)) | (pixel_g<<(bBpp + aBpp)) | (pixel_b<<aBpp) | pixel_a;
}
}
@@ -790,7 +836,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
void ImageToPOT(Image *image, Color fillColor)
{
Color *pixels = GetImageData(*image); // Get pixels data
-
+
// Just add the required amount of pixels at the right and bottom sides of image...
int potWidth = GetNextPOT(image->width);
int potHeight = GetNextPOT(image->height);
@@ -816,13 +862,13 @@ void ImageToPOT(Image *image, Color fillColor)
free(pixels); // Free pixels data
free(image->data); // Free old image data
-
+
int format = image->format; // Store image data format to reconvert later
-
+
// TODO: Image width and height changes... do we want to store new values or keep the old ones?
// NOTE: Issues when using image.width and image.height for sprite animations...
*image = LoadImageEx(pixelsPOT, potWidth, potHeight);
-
+
free(pixelsPOT); // Free POT pixels data
ImageFormat(image, format); // Reconvert image to previous format
@@ -833,9 +879,9 @@ void ImageToPOT(Image *image, Color fillColor)
Image ImageCopy(Image image)
{
Image newImage;
-
+
int size = image.width*image.height;
-
+
switch (image.format)
{
case UNCOMPRESSED_GRAYSCALE: newImage.data = (unsigned char *)malloc(size); break; // 8 bit per pixel (no alpha)
@@ -847,24 +893,24 @@ Image ImageCopy(Image image)
case UNCOMPRESSED_R8G8B8A8: newImage.data = (unsigned char *)malloc(size*4); size *= 4; break; // 32 bpp
default: TraceLog(WARNING, "Image format not suported for copy"); break;
}
-
+
if (newImage.data != NULL)
{
// NOTE: Size must be provided in bytes
memcpy(newImage.data, image.data, size);
-
+
newImage.width = image.width;
newImage.height = image.height;
newImage.mipmaps = image.mipmaps;
newImage.format = image.format;
}
-
+
return newImage;
}
// Crop an image to area defined by a rectangle
// NOTE: Security checks are performed in case rectangle goes out of bounds
-void ImageCrop(Image *image, Rectangle crop)
+void ImageCrop(Image *image, Rectangle crop)
{
// Security checks to make sure cropping rectangle is inside margins
if ((crop.x + crop.width) > image->width)
@@ -872,13 +918,13 @@ void ImageCrop(Image *image, Rectangle crop)
crop.width = image->width - crop.x;
TraceLog(WARNING, "Crop rectangle width out of bounds, rescaled crop width: %i", crop.width);
}
-
+
if ((crop.y + crop.height) > image->height)
{
crop.height = image->height - crop.y;
TraceLog(WARNING, "Crop rectangle height out of bounds, rescaled crop height: %i", crop.height);
}
-
+
if ((crop.x < image->width) && (crop.y < image->height))
{
// Start the cropping process
@@ -903,7 +949,7 @@ void ImageCrop(Image *image, Rectangle crop)
free(cropPixels);
- // Reformat 32bit RGBA image to original format
+ // Reformat 32bit RGBA image to original format
ImageFormat(image, format);
}
else
@@ -916,7 +962,7 @@ void ImageCrop(Image *image, Rectangle crop)
// NOTE: Uses stb default scaling filters (both bicubic):
// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
// STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL (high-quality Catmull-Rom)
-void ImageResize(Image *image, int newWidth, int newHeight)
+void ImageResize(Image *image, int newWidth, int newHeight)
{
// Get data as Color pixels array to work with it
Color *pixels = GetImageData(*image);
@@ -930,107 +976,132 @@ void ImageResize(Image *image, int newWidth, int newHeight)
UnloadImage(*image);
*image = LoadImageEx(output, newWidth, newHeight);
- ImageFormat(image, format); // Reformat 32bit RGBA image to original format
-
+ ImageFormat(image, format); // Reformat 32bit RGBA image to original format
+
free(output);
free(pixels);
}
// Resize and image to new size using Nearest-Neighbor scaling algorithm
-void ImageResizeNN(Image *image,int newWidth,int newHeight)
+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 i = 0; i < newHeight; i++)
{
- for (int j = 0; j < newWidth; j++)
+ 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
-
+ 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)
+// TODO: Feel this function could be simplified...
+void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
{
+ bool cropRequired = false;
+
// Security checks to avoid size and rectangle issues (out of bounds)
// Check that srcRec is inside src image
if (srcRec.x < 0) srcRec.x = 0;
if (srcRec.y < 0) srcRec.y = 0;
-
+
if ((srcRec.x + srcRec.width) > src.width)
{
srcRec.width = src.width - srcRec.x;
TraceLog(WARNING, "Source rectangle width out of bounds, rescaled width: %i", srcRec.width);
}
-
+
if ((srcRec.y + srcRec.height) > src.height)
{
srcRec.height = src.height - srcRec.y;
TraceLog(WARNING, "Source rectangle height out of bounds, rescaled height: %i", srcRec.height);
+ cropRequired = true;
}
+ Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it
+ ImageCrop(&srcCopy, srcRec); // Crop source image to desired source rectangle
+
// Check that dstRec is inside dst image
+ // TODO: Allow negative position within destination with cropping
if (dstRec.x < 0) dstRec.x = 0;
if (dstRec.y < 0) dstRec.y = 0;
+ // Scale source image in case destination rec size is different than source rec size
+ if ((dstRec.width != srcRec.width) || (dstRec.height != srcRec.height))
+ {
+ ImageResize(&srcCopy, dstRec.width, dstRec.height);
+ }
+
if ((dstRec.x + dstRec.width) > dst->width)
{
dstRec.width = dst->width - dstRec.x;
TraceLog(WARNING, "Destination rectangle width out of bounds, rescaled width: %i", dstRec.width);
+ cropRequired = true;
}
-
+
if ((dstRec.y + dstRec.height) > dst->height)
{
dstRec.height = dst->height - dstRec.y;
TraceLog(WARNING, "Destination rectangle height out of bounds, rescaled height: %i", dstRec.height);
+ cropRequired = true;
}
- // Get dstination image data as Color pixels array to work with it
- Color *dstPixels = GetImageData(*dst);
-
- Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it
- ImageCrop(&srcCopy, srcRec); // Crop source image to desired source rectangle
-
- // Scale source image in case destination rec size is different than source rec size
- if ((dstRec.width != srcRec.width) || (dstRec.height != srcRec.height))
+ if (cropRequired)
{
- ImageResize(&srcCopy, dstRec.width, dstRec.height);
+ // Crop destination rectangle if out of bounds
+ Rectangle crop = { 0, 0, dstRec.width, dstRec.height };
+ ImageCrop(&srcCopy, crop);
}
-
- // Get source image data as Color array
+
+ // Get image data as Color pixels array to work with it
+ Color *dstPixels = GetImageData(*dst);
Color *srcPixels = GetImageData(srcCopy);
+
+ UnloadImage(srcCopy); // Source copy not required any more...
- UnloadImage(srcCopy);
+ Color srcCol, dstCol;
// Blit pixels, copy source image into destination
+ // TODO: Probably out-of-bounds blitting could be considering here instead of so much cropping...
for (int j = dstRec.y; j < (dstRec.y + dstRec.height); j++)
{
for (int i = dstRec.x; i < (dstRec.x + dstRec.width); i++)
{
- dstPixels[j*dst->width + i] = srcPixels[(j - dstRec.y)*dstRec.width + (i - dstRec.x)];
+ // Alpha blending implementation
+ dstCol = dstPixels[j*dst->width + i];
+ srcCol = srcPixels[(j - dstRec.y)*dstRec.width + (i - dstRec.x)];
+
+ dstCol.r = ((srcCol.a*(srcCol.r - dstCol.r)) >> 8) + dstCol.r;
+ dstCol.g = ((srcCol.a*(srcCol.g - dstCol.g)) >> 8) + dstCol.g;
+ dstCol.b = ((srcCol.a*(srcCol.b - dstCol.b)) >> 8) + dstCol.b;
+
+ dstPixels[j*dst->width + i] = dstCol;
+
+ // TODO: Support other blending options
}
}
-
+
UnloadImage(*dst); // NOTE: Only dst->data is unloaded
*dst = LoadImageEx(dstPixels, dst->width, dst->height);
@@ -1045,15 +1116,15 @@ Image ImageText(const char *text, int fontSize, Color color)
{
int defaultFontSize = 10; // Default Font chars height in pixel
if (fontSize < defaultFontSize) fontSize = defaultFontSize;
- int spacing = fontSize / defaultFontSize;
-
+ int spacing = fontSize/defaultFontSize;
+
Image imText = ImageTextEx(GetDefaultFont(), text, fontSize, spacing, color);
-
+
return imText;
}
// Create an image from text (custom sprite font)
-Image ImageTextEx(SpriteFont font, const char *text, int fontSize, int spacing, Color tint)
+Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing, Color tint)
{
int length = strlen(text);
int posX = 0;
@@ -1062,19 +1133,20 @@ Image ImageTextEx(SpriteFont font, const char *text, int fontSize, int spacing,
// NOTE: GetTextureData() not available in OpenGL ES
Image imFont = GetTextureData(font.texture);
-
+
ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Required for color tint
ImageColorTint(&imFont, tint); // Apply color tint to font
Color *fontPixels = GetImageData(imFont);
-
+
// Create image to store text
- Color *pixels = (Color *)malloc(sizeof(Color)*(int)imSize.x*(int)imSize.y);
-
+ // NOTE: Pixels are initialized to BLANK color (0, 0, 0, 0)
+ Color *pixels = (Color *)calloc((int)imSize.x*(int)imSize.y, sizeof(Color));
+
for (int i = 0; i < length; i++)
{
Rectangle letterRec = font.charRecs[(int)text[i] - 32];
-
+
for (int y = letterRec.y; y < (letterRec.y + letterRec.height); y++)
{
for (int x = posX; x < (posX + letterRec.width); x++)
@@ -1082,47 +1154,48 @@ Image ImageTextEx(SpriteFont font, const char *text, int fontSize, int spacing,
pixels[(y - letterRec.y)*(int)imSize.x + x] = fontPixels[y*font.texture.width + (x - posX + letterRec.x)];
}
}
-
+
posX += letterRec.width + spacing;
}
-
+
UnloadImage(imFont);
-
+
Image imText = LoadImageEx(pixels, (int)imSize.x, (int)imSize.y);
-
+
// Scale image depending on text size
- if (fontSize > (int)imSize.y)
+ if (fontSize > imSize.y)
{
- float scaleFactor = (float)fontSize/imSize.y;
+ float scaleFactor = fontSize/imSize.y;
TraceLog(INFO, "Scalefactor: %f", 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);
free(fontPixels);
-
+
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);
+ // NOTE: For default font, sapcing is set to desired font size / default font size (10)
+ ImageDrawTextEx(dst, position, GetDefaultFont(), text, fontSize, fontSize/10, 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)
+void ImageDrawTextEx(Image *dst, Vector2 position, SpriteFont font, const char *text, float 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);
}
@@ -1131,7 +1204,7 @@ void ImageFlipVertical(Image *image)
{
Color *srcPixels = GetImageData(*image);
Color *dstPixels = (Color *)malloc(sizeof(Color)*image->width*image->height);
-
+
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
@@ -1139,14 +1212,14 @@ void ImageFlipVertical(Image *image)
dstPixels[y*image->width + x] = srcPixels[(image->height - 1 - y)*image->width + x];
}
}
-
+
Image processed = LoadImageEx(dstPixels, image->width, image->height);
ImageFormat(&processed, image->format);
UnloadImage(*image);
-
+
free(srcPixels);
free(dstPixels);
-
+
image->data = processed.data;
}
@@ -1155,7 +1228,7 @@ void ImageFlipHorizontal(Image *image)
{
Color *srcPixels = GetImageData(*image);
Color *dstPixels = (Color *)malloc(sizeof(Color)*image->width*image->height);
-
+
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
@@ -1163,14 +1236,14 @@ void ImageFlipHorizontal(Image *image)
dstPixels[y*image->width + x] = srcPixels[y*image->width + (image->width - 1 - x)];
}
}
-
+
Image processed = LoadImageEx(dstPixels, image->width, image->height);
ImageFormat(&processed, image->format);
UnloadImage(*image);
-
+
free(srcPixels);
free(dstPixels);
-
+
image->data = processed.data;
}
@@ -1178,12 +1251,12 @@ void ImageFlipHorizontal(Image *image)
void ImageColorTint(Image *image, Color color)
{
Color *pixels = GetImageData(*image);
-
+
float cR = (float)color.r/255;
float cG = (float)color.g/255;
float cB = (float)color.b/255;
float cA = (float)color.a/255;
-
+
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
@@ -1204,7 +1277,7 @@ void ImageColorTint(Image *image, Color color)
ImageFormat(&processed, image->format);
UnloadImage(*image);
free(pixels);
-
+
image->data = processed.data;
}
@@ -1212,7 +1285,7 @@ void ImageColorTint(Image *image, Color color)
void ImageColorInvert(Image *image)
{
Color *pixels = GetImageData(*image);
-
+
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
@@ -1222,12 +1295,12 @@ void ImageColorInvert(Image *image)
pixels[y*image->width + x].b = 255 - pixels[y*image->width + x].b;
}
}
-
+
Image processed = LoadImageEx(pixels, image->width, image->height);
ImageFormat(&processed, image->format);
UnloadImage(*image);
free(pixels);
-
+
image->data = processed.data;
}
@@ -1243,12 +1316,12 @@ void ImageColorContrast(Image *image, float contrast)
{
if (contrast < -100) contrast = -100;
if (contrast > 100) contrast = 100;
-
+
contrast = (100.0 + contrast)/100.0;
contrast *= contrast;
-
+
Color *pixels = GetImageData(*image);
-
+
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
@@ -1287,7 +1360,7 @@ void ImageColorContrast(Image *image, float contrast)
ImageFormat(&processed, image->format);
UnloadImage(*image);
free(pixels);
-
+
image->data = processed.data;
}
@@ -1297,9 +1370,9 @@ void ImageColorBrightness(Image *image, int brightness)
{
if (brightness < -255) brightness = -255;
if (brightness > 255) brightness = 255;
-
+
Color *pixels = GetImageData(*image);
-
+
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
@@ -1316,7 +1389,7 @@ void ImageColorBrightness(Image *image, int brightness)
if (cB < 0) cB = 1;
if (cB > 255) cB = 255;
-
+
pixels[y*image->width + x].r = (unsigned char)cR;
pixels[y*image->width + x].g = (unsigned char)cG;
pixels[y*image->width + x].b = (unsigned char)cB;
@@ -1327,7 +1400,7 @@ void ImageColorBrightness(Image *image, int brightness)
ImageFormat(&processed, image->format);
UnloadImage(*image);
free(pixels);
-
+
image->data = processed.data;
}
@@ -1396,7 +1469,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
{
if (sourceRec.width < 0) sourceRec.x -= sourceRec.width;
if (sourceRec.height < 0) sourceRec.y -= sourceRec.height;
-
+
rlEnableTexture(texture.id);
rlPushMatrix();
@@ -1409,19 +1482,19 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
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);
+ 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);
+ 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);
+ 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);
+ rlTexCoord2f((float)(sourceRec.x + sourceRec.width)/texture.width, (float)sourceRec.y/texture.height);
rlVertex2f(destRec.width, 0.0f);
rlEnd();
rlPopMatrix();
@@ -1439,13 +1512,13 @@ static Image LoadDDS(const char *fileName)
{
// Required extension:
// GL_EXT_texture_compression_s3tc
-
+
// Supported tokens (defined by extensions)
// GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
// GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
// GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
// GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
-
+
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
@@ -1508,7 +1581,7 @@ static Image LoadDDS(const char *fileName)
else
{
ddsHeader header;
-
+
// Get the image header
fread(&header, sizeof(ddsHeader), 1, ddsFile);
@@ -1537,9 +1610,9 @@ static Image LoadDDS(const char *fileName)
{
image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short));
fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile);
-
+
unsigned char alpha = 0;
-
+
// NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
for (int i = 0; i < image.width*image.height; i++)
{
@@ -1554,9 +1627,9 @@ static Image LoadDDS(const char *fileName)
{
image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short));
fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile);
-
+
unsigned char alpha = 0;
-
+
// NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
for (int i = 0; i < image.width*image.height; i++)
{
@@ -1564,7 +1637,7 @@ static Image LoadDDS(const char *fileName)
((unsigned short *)image.data)[i] = ((unsigned short *)image.data)[i] << 4;
((unsigned short *)image.data)[i] += alpha;
}
-
+
image.format = UNCOMPRESSED_R4G4B4A4;
}
}
@@ -1574,14 +1647,14 @@ static Image LoadDDS(const char *fileName)
// NOTE: not sure if this case exists...
image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char));
fread(image.data, image.width*image.height*3, 1, ddsFile);
-
+
image.format = UNCOMPRESSED_R8G8B8;
}
else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed
{
image.data = (unsigned char *)malloc(image.width*image.height*4*sizeof(unsigned char));
fread(image.data, image.width*image.height*4, 1, ddsFile);
-
+
unsigned char blue = 0;
// NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
@@ -1593,7 +1666,7 @@ static Image LoadDDS(const char *fileName)
((unsigned char *)image.data)[i] = ((unsigned char *)image.data)[i + 2];
((unsigned char *)image.data)[i + 2] = blue;
}
-
+
image.format = UNCOMPRESSED_R8G8B8A8;
}
else if (((header.ddspf.flags == 0x04) || (header.ddspf.flags == 0x05)) && (header.ddspf.fourCC > 0)) // Compressed
@@ -1603,7 +1676,7 @@ static Image LoadDDS(const char *fileName)
// Calculate data size, including all mipmaps
if (header.mipmapCount > 1) bufsize = header.pitchOrLinearSize*2;
else bufsize = header.pitchOrLinearSize;
-
+
TraceLog(DEBUG, "Pitch or linear size: %i", header.pitchOrLinearSize);
image.data = (unsigned char*)malloc(bufsize*sizeof(unsigned char));
@@ -1612,7 +1685,7 @@ static Image LoadDDS(const char *fileName)
image.mipmaps = header.mipmapCount;
- switch(header.ddspf.fourCC)
+ switch (header.ddspf.fourCC)
{
case FOURCC_DXT1:
{
@@ -1625,7 +1698,7 @@ static Image LoadDDS(const char *fileName)
}
}
}
-
+
fclose(ddsFile); // Close file pointer
}
@@ -1640,9 +1713,9 @@ static Image LoadPKM(const char *fileName)
// Required extensions:
// GL_OES_compressed_ETC1_RGB8_texture (ETC1) (OpenGL ES 2.0)
// GL_ARB_ES3_compatibility (ETC2/EAC) (OpenGL ES 3.0)
-
+
// Supported tokens (defined by extensions)
- // GL_ETC1_RGB8_OES 0x8D64
+ // GL_ETC1_RGB8_OES 0x8D64
// GL_COMPRESSED_RGB8_ETC2 0x9274
// GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
@@ -1656,7 +1729,7 @@ static Image LoadPKM(const char *fileName)
unsigned short origWidth; // Original width (big-endian)
unsigned short origHeight; // Original height (big-endian)
} pkmHeader;
-
+
// Formats list
// version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
// version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R
@@ -1665,7 +1738,7 @@ static Image LoadPKM(const char *fileName)
// NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
Image image;
-
+
image.data = NULL;
image.width = 0;
image.height = 0;
@@ -1695,21 +1768,21 @@ static Image LoadPKM(const char *fileName)
header.format = ((header.format & 0x00FF) << 8) | ((header.format & 0xFF00) >> 8);
header.width = ((header.width & 0x00FF) << 8) | ((header.width & 0xFF00) >> 8);
header.height = ((header.height & 0x00FF) << 8) | ((header.height & 0xFF00) >> 8);
-
+
TraceLog(DEBUG, "PKM (ETC) image width: %i", header.width);
TraceLog(DEBUG, "PKM (ETC) image height: %i", header.height);
TraceLog(DEBUG, "PKM (ETC) image format: %i", header.format);
-
+
image.width = header.width;
image.height = header.height;
image.mipmaps = 1;
-
+
int bpp = 4;
if (header.format == 3) bpp = 8;
-
+
int size = image.width*image.height*bpp/8; // Total data size in bytes
- image.data = (unsigned char*)malloc(size * sizeof(unsigned char));
+ image.data = (unsigned char*)malloc(size*sizeof(unsigned char));
fread(image.data, 1, size, pkmFile);
@@ -1717,7 +1790,7 @@ static Image LoadPKM(const char *fileName)
else if (header.format == 1) image.format = COMPRESSED_ETC2_RGB;
else if (header.format == 3) image.format = COMPRESSED_ETC2_EAC_RGBA;
}
-
+
fclose(pkmFile); // Close file pointer
}
@@ -1730,12 +1803,12 @@ static Image LoadKTX(const char *fileName)
// Required extensions:
// GL_OES_compressed_ETC1_RGB8_texture (ETC1)
// GL_ARB_ES3_compatibility (ETC2/EAC)
-
+
// Supported tokens (defined by extensions)
// GL_ETC1_RGB8_OES 0x8D64
// GL_COMPRESSED_RGB8_ETC2 0x9274
// GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
-
+
// KTX file Header (64 bytes)
// https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
typedef struct {
@@ -1754,16 +1827,16 @@ static Image LoadKTX(const char *fileName)
unsigned int mipmapLevels; // Non-mipmapped textures = 1
unsigned int keyValueDataSize; // Used to encode any arbitrary data...
} ktxHeader;
-
+
// NOTE: Before start of every mipmap data block, we have: unsigned int dataSize
-
+
Image image;
image.width = 0;
image.height = 0;
image.mipmaps = 0;
image.format = 0;
-
+
FILE *ktxFile = fopen(fileName, "rb");
if (ktxFile == NULL)
@@ -1783,26 +1856,26 @@ static Image LoadKTX(const char *fileName)
TraceLog(WARNING, "[%s] KTX file does not seem to be a valid file", fileName);
}
else
- {
+ {
image.width = header.width;
image.height = header.height;
image.mipmaps = header.mipmapLevels;
-
+
TraceLog(DEBUG, "KTX (ETC) image width: %i", header.width);
TraceLog(DEBUG, "KTX (ETC) image height: %i", header.height);
TraceLog(DEBUG, "KTX (ETC) image format: 0x%x", header.glInternalFormat);
-
+
unsigned char unused;
-
+
if (header.keyValueDataSize > 0)
{
for (int i = 0; i < header.keyValueDataSize; i++) fread(&unused, 1, 1, ktxFile);
}
-
+
int dataSize;
fread(&dataSize, sizeof(unsigned int), 1, ktxFile);
- image.data = (unsigned char*)malloc(dataSize * sizeof(unsigned char));
+ image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char));
fread(image.data, 1, dataSize, ktxFile);
@@ -1810,10 +1883,10 @@ static Image LoadKTX(const char *fileName)
else if (header.glInternalFormat == 0x9274) image.format = COMPRESSED_ETC2_RGB;
else if (header.glInternalFormat == 0x9278) image.format = COMPRESSED_ETC2_EAC_RGBA;
}
-
+
fclose(ktxFile); // Close file pointer
}
-
+
return image;
}
@@ -1823,11 +1896,11 @@ static Image LoadPVR(const char *fileName)
{
// Required extension:
// GL_IMG_texture_compression_pvrtc
-
+
// Supported tokens (defined by extensions)
// GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
// GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
-
+
#if 0 // Not used...
// PVR file v2 Header (52 bytes)
typedef struct {
@@ -1864,7 +1937,7 @@ static Image LoadPVR(const char *fileName)
unsigned int numMipmaps;
unsigned int metaDataSize;
} pvrHeaderV3;
-
+
#if 0 // Not used...
// Metadata (usually 15 bytes)
typedef struct {
@@ -1872,7 +1945,7 @@ static Image LoadPVR(const char *fileName)
unsigned int key;
unsigned int dataSize; // Not used?
unsigned char *data; // Not used?
- } pvrMetadata;
+ } pvrMetadata;
#endif
Image image;
@@ -1895,15 +1968,15 @@ static Image LoadPVR(const char *fileName)
unsigned char pvrVersion = 0;
fread(&pvrVersion, sizeof(unsigned char), 1, pvrFile);
fseek(pvrFile, 0, SEEK_SET);
-
+
// Load different PVR data formats
if (pvrVersion == 0x50)
{
pvrHeaderV3 header;
-
+
// Get PVR image header
fread(&header, sizeof(pvrHeaderV3), 1, pvrFile);
-
+
if ((header.id[0] != 'P') || (header.id[1] != 'V') || (header.id[2] != 'R') || (header.id[3] != 3))
{
TraceLog(WARNING, "[%s] PVR file does not seem to be a valid image", fileName);
@@ -1913,7 +1986,7 @@ static Image LoadPVR(const char *fileName)
image.width = header.width;
image.height = header.height;
image.mipmaps = header.numMipmaps;
-
+
// Check data format
if (((header.channels[0] == 'l') && (header.channels[1] == 0)) && (header.channelDepth[0] == 8)) image.format = UNCOMPRESSED_GRAYSCALE;
else if (((header.channels[0] == 'l') && (header.channels[1] == 'a')) && ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8))) image.format = UNCOMPRESSED_GRAY_ALPHA;
@@ -1933,14 +2006,14 @@ static Image LoadPVR(const char *fileName)
}
else if (header.channels[0] == 2) image.format = COMPRESSED_PVRT_RGB;
else if (header.channels[0] == 3) image.format = COMPRESSED_PVRT_RGBA;
-
+
// Skip meta data header
unsigned char unused = 0;
for (int i = 0; i < header.metaDataSize; i++) fread(&unused, sizeof(unsigned char), 1, pvrFile);
-
+
// Calculate data size (depends on format)
int bpp = 0;
-
+
switch (image.format)
{
case UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
@@ -1954,7 +2027,7 @@ static Image LoadPVR(const char *fileName)
case COMPRESSED_PVRT_RGBA: bpp = 4; break;
default: break;
}
-
+
int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char));
@@ -1976,11 +2049,11 @@ static Image LoadASTC(const char *fileName)
// Required extensions:
// GL_KHR_texture_compression_astc_hdr
// GL_KHR_texture_compression_astc_ldr
-
+
// Supported tokens (defined by extensions)
// GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93b0
// GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7
-
+
// ASTC file Header (16 bytes)
typedef struct {
unsigned char id[4]; // Signature: 0x13 0xAB 0xA1 0x5C
@@ -1999,7 +2072,7 @@ static Image LoadASTC(const char *fileName)
image.height = 0;
image.mipmaps = 0;
image.format = 0;
-
+
FILE *astcFile = fopen(fileName, "rb");
if (astcFile == NULL)
@@ -2012,7 +2085,7 @@ static Image LoadASTC(const char *fileName)
// Get ASTC image header
fread(&header, sizeof(astcHeader), 1, astcFile);
-
+
if ((header.id[3] != 0x5c) || (header.id[2] != 0xa1) || (header.id[1] != 0xab) || (header.id[0] != 0x13))
{
TraceLog(WARNING, "[%s] ASTC file does not seem to be a valid image", fileName);
@@ -2022,31 +2095,31 @@ static Image LoadASTC(const char *fileName)
// NOTE: Assuming Little Endian (could it be wrong?)
image.width = 0x00000000 | ((int)header.width[2] << 16) | ((int)header.width[1] << 8) | ((int)header.width[0]);
image.height = 0x00000000 | ((int)header.height[2] << 16) | ((int)header.height[1] << 8) | ((int)header.height[0]);
-
+
// NOTE: ASTC format only contains one mipmap level
image.mipmaps = 1;
-
+
TraceLog(DEBUG, "ASTC image width: %i", image.width);
TraceLog(DEBUG, "ASTC image height: %i", image.height);
TraceLog(DEBUG, "ASTC image blocks: %ix%i", header.blockX, header.blockY);
-
+
// NOTE: Each block is always stored in 128bit so we can calculate the bpp
int bpp = 128/(header.blockX*header.blockY);
// NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
- if ((bpp == 8) || (bpp == 2))
+ if ((bpp == 8) || (bpp == 2))
{
int dataSize = image.width*image.height*bpp/8; // Data size in bytes
-
+
image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
fread(image.data, dataSize, 1, astcFile);
-
+
if (bpp == 8) image.format = COMPRESSED_ASTC_4x4_RGBA;
else if (bpp == 2) image.format = COMPRESSED_ASTC_4x4_RGBA;
}
else TraceLog(WARNING, "[%s] ASTC block size configuration not supported", fileName);
}
-
+
fclose(astcFile);
}