diff options
Diffstat (limited to 'src')
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 @@ -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 Binary files differindex 843c0e44..7a42d443 100644 --- a/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll +++ b/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll 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 Binary files differnew file mode 100644 index 00000000..e51c0c80 --- /dev/null +++ b/src/external/lua/lib/liblua53.a diff --git a/src/external/lua/lib/liblua53dll.a b/src/external/lua/lib/liblua53dll.a Binary files differnew file mode 100644 index 00000000..32646db3 --- /dev/null +++ b/src/external/lua/lib/liblua53dll.a 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 Binary files differnew file mode 100644 index 00000000..3c0df3c7 --- /dev/null +++ b/src/external/openal_soft/lib/win32/libOpenAL32.a diff --git a/src/external/pthread/lib/pthreadGC2.dll b/src/external/pthread/lib/pthreadGC2.dll Binary files differnew file mode 100644 index 00000000..67b9289d --- /dev/null +++ b/src/external/pthread/lib/pthreadGC2.dll 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(¤tTime); - - 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(¤tTime); + + 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 } @@ -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 } } } @@ -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(); } @@ -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); } |
