diff options
| author | victorfisac <[email protected]> | 2017-03-06 09:40:04 +0100 |
|---|---|---|
| committer | victorfisac <[email protected]> | 2017-03-06 09:40:04 +0100 |
| commit | 9261c3b8dc03d093bff5246a18ad9310ae8eaeb3 (patch) | |
| tree | af87165723ac563ee1a7e1c605c7a4df821d74ea /src | |
| parent | e8630c78d069a1cba50b1a78108663ebc19e5b9b (diff) | |
| parent | b734802743f2089c8d649b27aea48ab71fa653b3 (diff) | |
| download | raylib-9261c3b8dc03d093bff5246a18ad9310ae8eaeb3.tar.gz raylib-9261c3b8dc03d093bff5246a18ad9310ae8eaeb3.zip | |
Merge remote-tracking branch 'refs/remotes/raysan5/develop' into develop
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 119 | ||||
| -rw-r--r-- | src/audio.c | 710 | ||||
| -rw-r--r-- | src/audio.h | 19 | ||||
| -rw-r--r-- | src/camera.h | 84 | ||||
| -rw-r--r-- | src/core.c | 516 | ||||
| -rw-r--r-- | src/external/dr_flac.h | 323 | ||||
| -rw-r--r-- | src/external/jar_mod.h | 3 | ||||
| -rw-r--r-- | src/external/stb_image.h | 905 | ||||
| -rw-r--r-- | src/external/stb_image_resize.h | 8 | ||||
| -rw-r--r-- | src/external/stb_image_write.h | 5 | ||||
| -rw-r--r-- | src/external/stb_rect_pack.h | 29 | ||||
| -rw-r--r-- | src/external/stb_truetype.h | 857 | ||||
| -rw-r--r-- | src/gestures.h | 51 | ||||
| -rw-r--r-- | src/models.c | 341 | ||||
| -rw-r--r-- | src/physac.h | 24 | ||||
| -rw-r--r-- | src/raylib.h | 267 | ||||
| -rw-r--r-- | src/raymath.h | 68 | ||||
| -rw-r--r-- | src/rlgl.c | 941 | ||||
| -rw-r--r-- | src/rlgl.h | 28 | ||||
| -rw-r--r-- | src/rlua.h | 4318 | ||||
| -rw-r--r-- | src/rres.h | 439 | ||||
| -rw-r--r-- | src/shader_standard.h | 173 | ||||
| -rw-r--r-- | src/shapes.c | 85 | ||||
| -rw-r--r-- | src/text.c | 350 | ||||
| -rw-r--r-- | src/textures.c | 782 | ||||
| -rw-r--r-- | src/utils.c | 208 | ||||
| -rw-r--r-- | src/utils.h | 24 |
27 files changed, 4499 insertions, 7178 deletions
diff --git a/src/Makefile b/src/Makefile index 2e263189..4c2278f5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -32,7 +32,7 @@ .PHONY: all clean install unistall # define raylib platform to compile for -# possible platforms: PLATFORM_DESKTOP PLATFORM_RPI PLATFORM_WEB +# possible platforms: PLATFORM_DESKTOP PLATFORM_ANDROID PLATFORM_RPI PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # define YES if you want shared/dynamic version of library instead of static (default) @@ -69,7 +69,28 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif +ifeq ($(PLATFORM),PLATFORM_ANDROID) + # path to Android NDK + ANDROID_NDK = $(ANDROID_NDK_HOME) + + # possible Android architectures: ARM ARM64 + ANDROID_ARCH ?= ARM + + # define YES to use clang instead of gcc + ANDROID_LLVM ?= NO + + # standalone Android toolchain install dir + ANDROID_TOOLCHAIN = $(CURDIR)/toolchain +endif + +ifeq ($(PLATFORM),PLATFORM_RPI) + CROSS_COMPILE ?= NO +endif + # define raylib graphics api depending on selected platform +ifeq ($(PLATFORM),PLATFORM_ANDROID) + GRAPHICS = GRAPHICS_API_OPENGL_ES2 +endif ifeq ($(PLATFORM),PLATFORM_RPI) # define raylib graphics api to use (on RPI, OpenGL ES 2.0 must be used) GRAPHICS = GRAPHICS_API_OPENGL_ES2 @@ -86,12 +107,48 @@ endif # NOTE: makefiles targets require tab indentation # define compiler: gcc for C program, define as g++ for C++ + +# default gcc compiler +CC = gcc + +ifeq ($(PLATFORM),PLATFORM_ANDROID) + ifeq ($(ANDROID_ARCH),ARM) + ifeq ($(ANDROID_LLVM),YES) + CC = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-clang + else + CC = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-gcc + endif + endif + ifeq ($(ANDROID_ARCH),ARM64) + ifeq ($(ANDROID_LLVM),YES) + CC = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-clang + else + CC = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-gcc + endif + endif +endif + +ifeq ($(PLATFORM),PLATFORM_RPI) + ifeq ($(CROSS_COMPILE),YES) + # rpi compiler + CC = armv6j-hardfloat-linux-gnueabi-gcc + endif +endif + ifeq ($(PLATFORM),PLATFORM_WEB) # emscripten compiler CC = emcc -else - # default gcc compiler - CC = gcc +endif + +AR = ar + +ifeq ($(PLATFORM),PLATFORM_ANDROID) + ifeq ($(ANDROID_ARCH),ARM) + AR = $(ANDROID_TOOLCHAIN)/bin/arm-linux-androideabi-ar + endif + ifeq ($(ANDROID_ARCH),ARM64) + AR = $(ANDROID_TOOLCHAIN)/bin/aarch64-linux-android-ar + endif endif # define compiler flags: @@ -124,10 +181,14 @@ endif #CFLAGSEXTRA = -Wextra -Wmissing-prototypes -Wstrict-prototypes # define any directories containing required header files -ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDES = -I. -Iexternal -I/opt/vc/include \ - -I/opt/vc/include/interface/vmcs_host/linux \ - -I/opt/vc/include/interface/vcos/pthreads +ifeq ($(PLATFORM),PLATFORM_ANDROID) +# STB libraries and others + INCLUDES = -I. -Iexternal +# OpenAL Soft library + INCLUDES += -Iexternal/openal_soft/include +# Android includes + INCLUDES += -I$(ANDROID_TOOLCHAIN)/sysroot/usr/include + INCLUDES += -I$(ANDROID_NDK)/sources/android/native_app_glue else # STB libraries and others INCLUDES = -I. -Iexternal @@ -136,6 +197,16 @@ else # OpenAL Soft library INCLUDES += -Iexternal/openal_soft/include endif +ifeq ($(PLATFORM),PLATFORM_RPI) +# STB libraries and others + INCLUDES = -I. -Iexternal +# RPi libraries + INCLUDES += -I/opt/vc/include + INCLUDES += -I/opt/vc/include/interface/vmcs_host/linux + INCLUDES += -I/opt/vc/include/interface/vcos/pthreads +# OpenAL Soft library + INCLUDES += -Iexternal/openal_soft/include +endif # define output directory for compiled library ifeq ($(PLATFORM),PLATFORM_DESKTOP) @@ -149,6 +220,14 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) OUTPUT_PATH = ../release/osx endif endif +ifeq ($(PLATFORM),PLATFORM_ANDROID) + ifeq ($(ANDROID_ARCH),ARM) + OUTPUT_PATH = ../release/android/armeabi-v7a + endif + ifeq ($(ANDROID_ARCH),ARM64) + OUTPUT_PATH = ../release/android/arm64-v8a + endif +endif ifeq ($(PLATFORM),PLATFORM_WEB) OUTPUT_PATH = ../release/html5 endif @@ -164,7 +243,18 @@ OBJS += external/stb_vorbis.o # typing 'make' will invoke the default target entry called 'all', # in this case, the 'default' target entry is raylib -all: raylib +all: toolchain raylib + +# make standalone Android toolchain +toolchain: +ifeq ($(PLATFORM),PLATFORM_ANDROID) + ifeq ($(ANDROID_ARCH),ARM) + $(ANDROID_NDK)/build/tools/make-standalone-toolchain.sh --platform=android-9 --toolchain=arm-linux-androideabi-4.9 --use-llvm --install-dir=$(ANDROID_TOOLCHAIN) + endif + ifeq ($(ANDROID_ARCH),ARM64) + $(ANDROID_NDK)/build/tools/make-standalone-toolchain.sh --platform=android-21 --toolchain=aarch64-linux-androideabi-4.9 --use-llvm --install-dir=$(ANDROID_TOOLCHAIN) + endif +endif # compile raylib library raylib: $(OBJS) @@ -184,9 +274,13 @@ else $(CC) -shared -o $(OUTPUT_PATH)/raylib.dll $(OBJS) $(SHAREDLIBS) -Wl,--out-implib,$(OUTPUT_PATH)/libraylibdll.a @echo "raylib dynamic library (raylib.dll) and import library (libraylibdll.a) generated!" endif + ifeq ($(PLATFORM),PLATFORM_ANDROID) + $(CC) -shared -o $(OUTPUT_PATH)/libraylib.so $(OBJS) + @echo "raylib shared library (libraylib.so) generated!" + endif else - # compile raylib static library for desktop platforms. - ar rcs $(OUTPUT_PATH)/libraylib.a $(OBJS) + # compile raylib static library. + $(AR) rcs $(OUTPUT_PATH)/libraylib.a $(OBJS) @echo "libraylib.a generated (static library)!" ifeq ($(SHARED_OPENAL),NO) @echo "expected OpenAL Soft static library linking" @@ -284,4 +378,7 @@ ifeq ($(PLATFORM_OS),WINDOWS) else rm -f *.o $(OUTPUT_PATH)/libraylib.a $(OUTPUT_PATH)/libraylib.bc $(OUTPUT_PATH)/libraylib.so external/stb_vorbis.o endif +ifeq ($(PLATFORM),PLATFORM_ANDROID) + rm -rf $(ANDROID_TOOLCHAIN) +endif @echo "removed all generated files!" diff --git a/src/audio.c b/src/audio.c index 49aca4b0..659ead0f 100644 --- a/src/audio.c +++ b/src/audio.c @@ -3,28 +3,50 @@ * raylib.audio * * This module provides basic functionality to work with audio: -* Manage audio device (init/close) -* Load and Unload audio files (WAV, OGG, FLAC, XM, MOD) -* Play/Stop/Pause/Resume loaded audio -* Manage mixing channels -* Manage raw audio context +* Manage audio device (init/close) +* Load and Unload audio files (WAV, OGG, FLAC, XM, MOD) +* Play/Stop/Pause/Resume loaded audio +* Manage mixing channels +* Manage raw audio context * -* External libs: +* NOTES: +* +* Only up to two channels supported: MONO and STEREO (for additional channels, use AL_EXT_MCFORMATS) +* Only the following sample sizes supported: 8bit PCM, 16bit PCM, 32-bit float PCM (using AL_EXT_FLOAT32) +* +* CONFIGURATION: +* +* #define AUDIO_STANDALONE +* If defined, the module can be used as standalone library (independently of raylib). +* Required types and functions are defined in the same module. +* +* #define SUPPORT_FILEFORMAT_WAV / SUPPORT_LOAD_WAV / ENABLE_LOAD_WAV +* #define SUPPORT_FILEFORMAT_OGG +* #define SUPPORT_FILEFORMAT_XM +* #define SUPPORT_FILEFORMAT_MOD +* #define SUPPORT_FILEFORMAT_FLAC +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module +* +* #define SUPPORT_RAW_AUDIO_BUFFERS +* +* DEPENDENCIES: * OpenAL Soft - Audio device management (http://kcat.strangesoft.net/openal.html) * stb_vorbis - OGG audio files loading (http://www.nothings.org/stb_vorbis/) * jar_xm - XM module file loading * jar_mod - MOD audio file loading * dr_flac - FLAC audio file loading * -* Module Configuration Flags: -* AUDIO_STANDALONE - Use this module as standalone library (independently of raylib) +* CONTRIBUTORS: * * Many thanks to Joshua Reisenauer (github: @kd7tck) for the following additions: -* XM audio module support (jar_xm) -* MOD audio module support (jar_mod) -* Mixing channels support -* Raw audio context support +* XM audio module support (jar_xm) +* MOD audio module support (jar_mod) +* Mixing channels support +* Raw audio context support +* * +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -52,25 +74,25 @@ #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() #else #include "raylib.h" - #include "utils.h" // Required for: DecompressData() - // NOTE: Includes Android fopen() function map + #include "utils.h" // Required for: fopen() Android mapping, TraceLog() #endif -#include "AL/al.h" // OpenAL basic header -#include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) +#ifdef __APPLE__ + #include "OpenAL/al.h" // OpenAL basic header + #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) +#else + #include "AL/al.h" // OpenAL basic header + #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work) + //#include "AL/alext.h" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS +#endif + +// OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples +// OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1) #include <stdlib.h> // Required for: malloc(), free() #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 -#ifndef AL_FORMAT_STEREO_FLOAT32 - #define AL_FORMAT_STEREO_FLOAT32 0x10011 -#endif - //#define STB_VORBIS_HEADER_ONLY #include "external/stb_vorbis.h" // OGG loading functions @@ -93,11 +115,20 @@ //---------------------------------------------------------------------------------- #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 +// NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number // 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) +#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb) + +// Support uncompressed PCM data in 32-bit float IEEE format +// NOTE: This definition is included in "AL/alext.h", but some OpenAL implementations +// could not provide the extensions header (Android), so its defined here +#if !defined(AL_EXT_float32) + #define AL_EXT_float32 1 + #define AL_FORMAT_MONO_FLOAT32 0x10010 + #define AL_FORMAT_STEREO_FLOAT32 0x10011 +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -115,7 +146,7 @@ typedef struct MusicData { AudioStream stream; // Audio stream (double buffering) - bool loop; // Repeat music after finish (loop) + int loopCount; // Loops count (times music repeats), -1 means infinite loop unsigned int totalSamples; // Total number of samples unsigned int samplesLeft; // Number of samples left to end } MusicData; @@ -169,9 +200,11 @@ void InitAudioDevice(void) 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); + alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); + alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f); + + alListenerf(AL_GAIN, 1.0f); } } } @@ -208,11 +241,20 @@ bool IsAudioDeviceReady(void) } } +// Set master volume (listener) +void SetMasterVolume(float volume) +{ + if (volume < 0.0f) volume = 0.0f; + else if (volume > 1.0f) volume = 1.0f; + + alListenerf(AL_GAIN, volume); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Sounds loading and playing (.WAV) //---------------------------------------------------------------------------------- -// Load wave data from file into RAM +// Load wave data from file Wave LoadWave(const char *fileName) { Wave wave = { 0 }; @@ -220,39 +262,49 @@ Wave LoadWave(const char *fileName) 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 if (strcmp(GetExtension(fileName),"rres") == 0) + { + RRES rres = LoadResource(fileName, 0); + + // NOTE: Parameters for RRES_TYPE_WAVE are: sampleCount, sampleRate, sampleSize, channels + + if (rres[0].type == RRES_TYPE_WAVE) wave = LoadWaveEx(rres[0].data, rres[0].param1, rres[0].param2, rres[0].param3, rres[0].param4); + else TraceLog(WARNING, "[%s] Resource file does not contain wave data", fileName); + + UnloadResource(rres); + } else TraceLog(WARNING, "[%s] File extension not recognized, it can't be loaded", fileName); return wave; } -// Load wave data from float array data (32bit) -Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels) +// Load wave data from raw array data +Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels) { Wave wave; - + wave.data = data; wave.sampleCount = sampleCount; wave.sampleRate = sampleRate; - wave.sampleSize = 32; + wave.sampleSize = sampleSize; wave.channels = channels; - - // NOTE: Copy wave data to work with, - // user is responsible of input data to free + + // NOTE: Copy wave data to work with, user is responsible of input data to free Wave cwave = WaveCopy(wave); - + WaveFormat(&cwave, sampleRate, sampleSize, channels); - + return cwave; } -// Load sound to memory +// Load sound from file // NOTE: The entire file is loaded to memory to be played (no-streaming) Sound LoadSound(const char *fileName) { Wave wave = LoadWave(fileName); - + Sound sound = LoadSoundFromWave(wave); - + UnloadWave(wave); // Sound is loaded, we can unload wave return sound; @@ -275,7 +327,7 @@ Sound LoadSoundFromWave(Wave wave) { case 8: format = AL_FORMAT_MONO8; break; case 16: format = AL_FORMAT_MONO16; break; - case 32: format = AL_FORMAT_MONO_FLOAT32; break; + case 32: format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; } } @@ -285,7 +337,7 @@ Sound LoadSoundFromWave(Wave wave) { case 8: format = AL_FORMAT_STEREO8; break; case 16: format = AL_FORMAT_STEREO16; break; - case 32: format = AL_FORMAT_STEREO_FLOAT32; break; + case 32: format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; } } @@ -295,10 +347,10 @@ Sound LoadSoundFromWave(Wave wave) 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); + alSourcef(source, AL_PITCH, 1.0f); + alSourcef(source, AL_GAIN, 1.0f); + alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); alSourcei(source, AL_LOOPING, AL_FALSE); // Convert loaded data to OpenAL buffer @@ -306,7 +358,7 @@ Sound LoadSoundFromWave(Wave wave) ALuint buffer; alGenBuffers(1, &buffer); // Generate pointer to buffer - unsigned int dataSize = wave.sampleCount*wave.sampleSize/8; // Size in bytes + unsigned int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; // Size in bytes // Upload sound data to buffer alBufferData(buffer, format, wave.data, dataSize, wave.sampleRate); @@ -314,7 +366,7 @@ Sound LoadSoundFromWave(Wave wave) // Attach sound buffer to source alSourcei(source, AL_BUFFER, buffer); - 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); + TraceLog(INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (%i Hz, %i bit, %s)", source, buffer, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); sound.source = source; sound.buffer = buffer; @@ -324,121 +376,10 @@ Sound LoadSoundFromWave(Wave wave) return sound; } -// Load sound to memory from rRES file (raylib Resource) -// TODO: Maybe rresName could be directly a char array with all the data? -Sound LoadSoundFromRES(const char *rresName, int resId) -{ - Sound sound = { 0 }; - -#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 - short numRes; - - ResInfoHeader infoHeader; - - FILE *rresFile = fopen(rresName, "rb"); - - if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); - else - { - // Read rres file (basic file check - id) - fread(&id[0], sizeof(char), 1, rresFile); - fread(&id[1], sizeof(char), 1, rresFile); - fread(&id[2], sizeof(char), 1, rresFile); - fread(&id[3], sizeof(char), 1, rresFile); - fread(&version, sizeof(char), 1, rresFile); - fread(&useless, sizeof(char), 1, rresFile); - - if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) - { - TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); - } - else - { - // Read number of resources embedded - fread(&numRes, sizeof(short), 1, rresFile); - - for (int i = 0; i < numRes; i++) - { - fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); - - if (infoHeader.id == resId) - { - found = true; - - // Check data is of valid SOUND type - if (infoHeader.type == 1) // SOUND data type - { - // TODO: Check data compression type - // NOTE: We suppose compression type 2 (DEFLATE - default) - - // Reading SOUND parameters - Wave wave; - short sampleRate, bps; - char channels, reserved; - - fread(&sampleRate, sizeof(short), 1, rresFile); // Sample rate (frequency) - fread(&bps, sizeof(short), 1, rresFile); // Bits per sample - fread(&channels, 1, 1, rresFile); // Channels (1 - mono, 2 - stereo) - fread(&reserved, 1, 1, rresFile); // <reserved> - - wave.sampleRate = sampleRate; - wave.sampleSize = bps; - wave.channels = (short)channels; - - unsigned char *data = malloc(infoHeader.size); - - fread(data, infoHeader.size, 1, rresFile); - - wave.data = DecompressData(data, infoHeader.size, infoHeader.srcSize); - - free(data); - - sound = LoadSoundFromWave(wave); - - // 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 - { - // Depending on type, skip the right amount of parameters - switch (infoHeader.type) - { - case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters - case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters - case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) - case 3: break; // TEXT: No parameters - case 4: break; // RAW: No parameters - default: break; - } - - // Jump DATA to read next infoHeader - fseek(rresFile, infoHeader.size, SEEK_CUR); - } - } - } - - fclose(rresFile); - } - - if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); -#endif - return sound; -} - -// Unload Wave data +// Unload wave data void UnloadWave(Wave wave) { - free(wave.data); + if (wave.data != NULL) free(wave.data); TraceLog(INFO, "Unloaded wave data from RAM"); } @@ -446,6 +387,8 @@ void UnloadWave(Wave wave) // Unload sound void UnloadSound(Sound sound) { + alSourceStop(sound.source); + alDeleteSources(1, &sound.source); alDeleteBuffers(1, &sound.buffer); @@ -454,19 +397,19 @@ void UnloadSound(Sound sound) // Update sound buffer with new data // NOTE: data must match sound.format -void UpdateSound(Sound sound, void *data, int numSamples) +void UpdateSound(Sound sound, const void *data, int samplesCount) { 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 - + 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(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 - + unsigned int dataSize = samplesCount*channels*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 @@ -547,69 +490,94 @@ void SetSoundPitch(Sound sound, float pitch) } // Convert wave data to desired format -// TODO: Consider channels (mono - stereo) void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) { - if (wave->sampleSize != sampleSize) + // Format sample rate + // NOTE: Only supported 22050 <--> 44100 + if (wave->sampleRate != sampleRate) { - float *samples = GetWaveData(*wave); //Color *pixels = GetImageData(*image); + // TODO: Resample wave data (upsampling or downsampling) + // NOTE 1: To downsample, you have to drop samples or average them. + // NOTE 2: To upsample, you have to interpolate new samples. - free(wave->data); - - wave->sampleSize = sampleSize; + wave->sampleRate = sampleRate; + } - //sample *= 4.0f; // Arbitrary gain to get reasonable output volume... - //if (sample > 1.0f) sample = 1.0f; - //if (sample < -1.0f) sample = -1.0f; + // Format sample size + // NOTE: Only supported 8 bit <--> 16 bit <--> 32 bit + if (wave->sampleSize != sampleSize) + { + void *data = malloc(wave->sampleCount*wave->channels*sampleSize/8); - if (sampleSize == 8) + for (int i = 0; i < wave->sampleCount; i++) { - wave->data = (unsigned char *)malloc(wave->sampleCount*sizeof(unsigned char)); - - for (int i = 0; i < wave->sampleCount; i++) + for (int j = 0; j < wave->channels; j++) { - ((unsigned char *)wave->data)[i] = (unsigned char)((float)samples[i]*127 + 128); + if (sampleSize == 8) + { + if (wave->sampleSize == 16) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float)(((short *)wave->data)[wave->channels*i + j])/32767.0f)*256); + else if (wave->sampleSize == 32) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float *)wave->data)[wave->channels*i + j]*127.0f + 127); + } + else if (sampleSize == 16) + { + if (wave->sampleSize == 8) ((short *)data)[wave->channels*i + j] = (short)(((float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f)*32767); + else if (wave->sampleSize == 32) ((short *)data)[wave->channels*i + j] = (short)((((float *)wave->data)[wave->channels*i + j])*32767); + } + else if (sampleSize == 32) + { + if (wave->sampleSize == 8) ((float *)data)[wave->channels*i + j] = (float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f; + else if (wave->sampleSize == 16) ((float *)data)[wave->channels*i + j] = (float)(((short *)wave->data)[wave->channels*i + j])/32767.0f; + } } } - else if (sampleSize == 16) + + wave->sampleSize = sampleSize; + free(wave->data); + wave->data = data; + } + + // Format channels (interlaced mode) + // NOTE: Only supported mono <--> stereo + if (wave->channels != channels) + { + void *data = malloc(wave->sampleCount*channels*wave->sampleSize/8); + + if ((wave->channels == 1) && (channels == 2)) // mono ---> stereo (duplicate mono information) { - wave->data = (short *)malloc(wave->sampleCount*sizeof(short)); - for (int i = 0; i < wave->sampleCount; i++) { - ((short *)wave->data)[i] = (short)((float)samples[i]*32000); // SHRT_MAX = 32767 + for (int j = 0; j < channels; j++) + { + if (wave->sampleSize == 8) ((unsigned char *)data)[channels*i + j] = ((unsigned char *)wave->data)[i]; + else if (wave->sampleSize == 16) ((short *)data)[channels*i + j] = ((short *)wave->data)[i]; + else if (wave->sampleSize == 32) ((float *)data)[channels*i + j] = ((float *)wave->data)[i]; + } } } - else if (sampleSize == 32) + else if ((wave->channels == 2) && (channels == 1)) // stereo ---> mono (mix stereo channels) { - wave->data = (float *)malloc(wave->sampleCount*sizeof(float)); - - for (int i = 0; i < wave->sampleCount; i++) + for (int i = 0, j = 0; i < wave->sampleCount; i++, j += 2) { - ((float *)wave->data)[i] = (float)samples[i]; + if (wave->sampleSize == 8) ((unsigned char *)data)[i] = (((unsigned char *)wave->data)[j] + ((unsigned char *)wave->data)[j + 1])/2; + else if (wave->sampleSize == 16) ((short *)data)[i] = (((short *)wave->data)[j] + ((short *)wave->data)[j + 1])/2; + else if (wave->sampleSize == 32) ((float *)data)[i] = (((float *)wave->data)[j] + ((float *)wave->data)[j + 1])/2.0f; } } - else TraceLog(WARNING, "Wave formatting: Sample size not supported"); - - free(samples); - } - - // NOTE: Only supported 1 or 2 channels (mono or stereo) - if ((channels > 0) && (channels < 3) && (wave->channels != channels)) - { - // TODO: Add/remove channels interlaced data if required... + + // TODO: Add/remove additional interlaced channels + + wave->channels = channels; + free(wave->data); + wave->data = data; } } // Copy a wave to a new wave Wave WaveCopy(Wave wave) { - Wave newWave; + Wave newWave = { 0 }; - 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"); + newWave.data = malloc(wave.sampleCount*wave.channels*wave.sampleSize/8); if (newWave.data != NULL) { @@ -629,40 +597,37 @@ Wave WaveCopy(Wave wave) // NOTE: Security check in case of out-of-range void WaveCrop(Wave *wave, int initSample, int finalSample) { - if ((initSample >= 0) && (initSample < finalSample) && + if ((initSample >= 0) && (initSample < finalSample) && (finalSample > 0) && (finalSample < wave->sampleCount)) { - // TODO: Review cropping (it could be simplified...) - - float *samples = GetWaveData(*wave); - float *cropSamples = (float *)malloc((finalSample - initSample)*sizeof(float)); - - for (int i = initSample; i < finalSample; i++) cropSamples[i] = samples[i]; + int sampleCount = finalSample - initSample; - free(wave->data); - wave->data = cropSamples; - int sampleSize = wave->sampleSize; - wave->sampleSize = 32; + void *data = malloc(sampleCount*wave->channels*wave->sampleSize/8); - WaveFormat(wave, wave->sampleRate, sampleSize, wave->channels); + memcpy(data, (unsigned char*)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8); + + free(wave->data); + wave->data = data; } else TraceLog(WARNING, "Wave crop range out of bounds"); } // 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) { - float *samples = (float *)malloc(wave.sampleCount*sizeof(float)); - + float *samples = (float *)malloc(wave.sampleCount*wave.channels*sizeof(float)); + 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]; + for (int j = 0; j < wave.channels; j++) + { + if (wave.sampleSize == 8) samples[wave.channels*i + j] = (float)(((unsigned char *)wave.data)[wave.channels*i + j] - 127)/256.0f; + else if (wave.sampleSize == 16) samples[wave.channels*i + j] = (float)((short *)wave.data)[wave.channels*i + j]/32767.0f; + else if (wave.sampleSize == 32) samples[wave.channels*i + j] = ((float *)wave.data)[wave.channels*i + j]; + } } - + return samples; } @@ -684,34 +649,34 @@ Music LoadMusicStream(const char *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 + // OGG bit rate defaults to 16 bit, it's enough for compressed format music->stream = InitAudioStream(info.sample_rate, 16, info.channels); - music->totalSamples = (unsigned int)stb_vorbis_stream_length_in_samples(music->ctxOgg)*info.channels; + music->totalSamples = (unsigned int)stb_vorbis_stream_length_in_samples(music->ctxOgg); // Independent by channel music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_AUDIO_OGG; - music->loop = true; // We loop by default + music->loopCount = -1; // Infinite loop by default + TraceLog(DEBUG, "[%s] FLAC total samples: %i", fileName, music->totalSamples); 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 (music->ctxFlac == NULL) TraceLog(WARNING, "[%s] FLAC audio file could not be opened", fileName); else { music->stream = InitAudioStream(music->ctxFlac->sampleRate, music->ctxFlac->bitsPerSample, music->ctxFlac->channels); - music->totalSamples = (unsigned int)music->ctxFlac->totalSampleCount; + music->totalSamples = (unsigned int)music->ctxFlac->totalSampleCount/music->ctxFlac->channels; music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_AUDIO_FLAC; - music->loop = true; // We loop by default - + music->loopCount = -1; // Infinite loop by default + + TraceLog(DEBUG, "[%s] FLAC total samples: %i", fileName, music->totalSamples); 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); @@ -723,14 +688,14 @@ Music LoadMusicStream(const char *fileName) if (!result) // XM context created successfully { - jar_xm_set_max_loop_count(music->ctxXm, 0); // Set infinite number of loops + jar_xm_set_max_loop_count(music->ctxXm, 0); // Set infinite number of loops // NOTE: Only stereo is supported for XM - music->stream = InitAudioStream(48000, 32, 2); + music->stream = InitAudioStream(48000, 16, 2); music->totalSamples = (unsigned int)jar_xm_get_remaining_samples(music->ctxXm); music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_MODULE_XM; - music->loop = true; + music->loopCount = -1; // Infinite loop by default 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); @@ -747,10 +712,10 @@ Music LoadMusicStream(const char *fileName) music->totalSamples = (unsigned int)jar_mod_max_samples(&music->ctxMod); music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_MODULE_MOD; - music->loop = true; + music->loopCount = -1; // Infinite loop by default - 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); + TraceLog(DEBUG, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); + TraceLog(DEBUG, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); } else TraceLog(WARNING, "[%s] MOD file could not be opened", fileName); } @@ -794,115 +759,102 @@ void ResumeMusicStream(Music music) } // Stop music playing (close stream) -// TODO: Restart XM context void StopMusicStream(Music music) { alSourceStop(music->stream.source); + // Clear stream buffers + void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1); + + for (int i = 0; i < MAX_STREAM_BUFFERS; i++) + { + alBufferData(music->stream.buffers[i], music->stream.format, pcm, AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, music->stream.sampleRate); + } + + free(pcm); + + // Restart music context switch (music->ctxType) { case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break; - case MUSIC_MODULE_XM: break; + case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break; case MUSIC_MODULE_MOD: jar_mod_seek_start(&music->ctxMod); break; default: break; } - + music->samplesLeft = music->totalSamples; } // Update (re-fill) music buffers if data already processed +// TODO: Make sure buffers are ready for update... check music state void UpdateMusicStream(Music music) { ALenum state; ALint processed = 0; - + 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) { 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 + // NOTE: Using dynamic allocation because it could require more than 16KB + void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.channels*music->stream.sampleSize/8, 1); + + int numBuffersToProcess = processed; + int samplesCount = 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++) { + if (music->samplesLeft >= AUDIO_BUFFER_SIZE) samplesCount = AUDIO_BUFFER_SIZE; + else samplesCount = music->samplesLeft; + + // TODO: Really don't like ctxType thingy... 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); + // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) + int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount*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) - unsigned int numSamplesFlac = (unsigned int)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; + // NOTE: Returns the number of samples to process + unsigned int numSamplesFlac = (unsigned int)drflac_read_s16(music->ctxFlac, samplesCount*music->stream.channels, (short *)pcm); } break; + case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break; + case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break; default: break; } + UpdateAudioStream(music->stream, pcm, samplesCount); + music->samplesLeft -= samplesCount; + if (music->samplesLeft <= 0) { active = false; break; } } - + // 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) + if (!active) { StopMusicStream(music); // Stop music (and reset) - if (music->loop) PlayMusicStream(music); // Play again + // Decrease loopCount to stop when required + if (music->loopCount > 0) + { + music->loopCount--; // Decrease loop count + PlayMusicStream(music); // Play again + } } else { @@ -910,6 +862,8 @@ void UpdateMusicStream(Music music) // just make sure to play again on window restore if (state != AL_PLAYING) PlayMusicStream(music); } + + free(pcm); } } @@ -938,6 +892,13 @@ void SetMusicPitch(Music music, float pitch) alSourcef(music->stream.source, AL_PITCH, pitch); } +// Set music loop count (loop repeats) +// NOTE: If set to -1, means infinite loop +void SetMusicLoopCount(Music music, float count) +{ + music->loopCount = count; +} + // Get music time length (in seconds) float GetMusicTimeLength(Music music) { @@ -952,7 +913,7 @@ float GetMusicTimePlayed(Music music) float secondsPlayed = 0.0f; unsigned int samplesPlayed = music->totalSamples - music->samplesLeft; - secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels); + secondsPlayed = (float)samplesPlayed/music->stream.sampleRate; return secondsPlayed; } @@ -964,64 +925,61 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un stream.sampleRate = sampleRate; stream.sampleSize = sampleSize; - stream.channels = channels; + + // Only mono and stereo channels are supported, more channels require AL_EXT_MCFORMATS extension + if ((channels > 0) && (channels < 3)) stream.channels = channels; + else + { + TraceLog(WARNING, "Init audio stream: Number of channels not supported: %i", channels); + stream.channels = 1; // Fallback to mono channel + } // Setup OpenAL format - if (channels == 1) + if (stream.channels == 1) { switch (sampleSize) { 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; + case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; } } - else if (channels == 2) + else if (stream.channels == 2) { switch (sampleSize) { 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; + case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32 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); // 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); + alSourcef(stream.source, AL_PITCH, 1.0f); + alSourcef(stream.source, AL_GAIN, 1.0f); + alSource3f(stream.source, AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(stream.source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); // Create Buffers (double buffering) alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers); // Initialize buffer with zeros by default + // NOTE: Using dynamic allocation because it requires more than 16KB + void *pcm = calloc(AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, 1); + for (int i = 0; i < MAX_STREAM_BUFFERS; i++) { - if (stream.sampleSize == 8) - { - unsigned char pcm[AUDIO_BUFFER_SIZE] = { 0 }; - alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(unsigned char), stream.sampleRate); - } - else if (stream.sampleSize == 16) - { - 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); - } + alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, stream.sampleRate); } + free(pcm); + alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); - TraceLog(INFO, "[AUD ID %i] Audio stream loaded successfully", stream.source); + TraceLog(INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo"); return stream; } @@ -1052,8 +1010,8 @@ void CloseAudioStream(AudioStream stream) } // Update audio stream buffers with data -// NOTE: Only one buffer per call -void UpdateAudioStream(AudioStream stream, void *data, int numSamples) +// NOTE: Only updates one buffer per call +void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { ALuint buffer = 0; alSourceUnqueueBuffers(stream.source, 1, &buffer); @@ -1061,10 +1019,7 @@ void UpdateAudioStream(AudioStream stream, void *data, int numSamples) // 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); - + alBufferData(buffer, stream.format, data, samplesCount*stream.channels*stream.sampleSize/8, stream.sampleRate); alSourceQueueBuffers(stream.source, 1, &buffer); } } @@ -1119,7 +1074,7 @@ static Wave LoadWAV(const char *fileName) char chunkID[4]; int chunkSize; char format[4]; - } RiffHeader; + } WAVRiffHeader; typedef struct { char subChunkID[4]; @@ -1130,16 +1085,16 @@ static Wave LoadWAV(const char *fileName) int byteRate; short blockAlign; short bitsPerSample; - } WaveFormat; + } WAVFormat; typedef struct { char subChunkID[4]; int subChunkSize; - } WaveData; + } WAVData; - RiffHeader riffHeader; - WaveFormat waveFormat; - WaveData waveData; + WAVRiffHeader wavRiffHeader; + WAVFormat wavFormat; + WAVData wavData; Wave wave = { 0 }; FILE *wavFile; @@ -1154,54 +1109,70 @@ static Wave LoadWAV(const char *fileName) else { // Read in the first chunk into the struct - fread(&riffHeader, sizeof(RiffHeader), 1, wavFile); + fread(&wavRiffHeader, sizeof(WAVRiffHeader), 1, wavFile); // Check for RIFF and WAVE tags - if (strncmp(riffHeader.chunkID, "RIFF", 4) || - strncmp(riffHeader.format, "WAVE", 4)) + if (strncmp(wavRiffHeader.chunkID, "RIFF", 4) || + strncmp(wavRiffHeader.format, "WAVE", 4)) { TraceLog(WARNING, "[%s] Invalid RIFF or WAVE Header", fileName); } else { // Read in the 2nd chunk for the wave info - fread(&waveFormat, sizeof(WaveFormat), 1, wavFile); + fread(&wavFormat, sizeof(WAVFormat), 1, wavFile); // Check for fmt tag - if ((waveFormat.subChunkID[0] != 'f') || (waveFormat.subChunkID[1] != 'm') || - (waveFormat.subChunkID[2] != 't') || (waveFormat.subChunkID[3] != ' ')) + if ((wavFormat.subChunkID[0] != 'f') || (wavFormat.subChunkID[1] != 'm') || + (wavFormat.subChunkID[2] != 't') || (wavFormat.subChunkID[3] != ' ')) { TraceLog(WARNING, "[%s] Invalid Wave format", fileName); } else { // Check for extra parameters; - if (waveFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR); + if (wavFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR); // Read in the the last byte of data before the sound file - fread(&waveData, sizeof(WaveData), 1, wavFile); + fread(&wavData, sizeof(WAVData), 1, wavFile); // Check for data tag - if ((waveData.subChunkID[0] != 'd') || (waveData.subChunkID[1] != 'a') || - (waveData.subChunkID[2] != 't') || (waveData.subChunkID[3] != 'a')) + if ((wavData.subChunkID[0] != 'd') || (wavData.subChunkID[1] != 'a') || + (wavData.subChunkID[2] != 't') || (wavData.subChunkID[3] != 'a')) { TraceLog(WARNING, "[%s] Invalid data header", fileName); } else { // Allocate memory for data - wave.data = (unsigned char *)malloc(sizeof(unsigned char)*waveData.subChunkSize); + wave.data = malloc(wavData.subChunkSize); // Read in the sound data into the soundData variable - fread(wave.data, waveData.subChunkSize, 1, wavFile); + fread(wave.data, wavData.subChunkSize, 1, wavFile); + + // Store wave parameters + wave.sampleRate = wavFormat.sampleRate; + wave.sampleSize = wavFormat.bitsPerSample; + wave.channels = wavFormat.numChannels; + + // NOTE: Only support 8 bit, 16 bit and 32 bit sample sizes + if ((wave.sampleSize != 8) && (wave.sampleSize != 16) && (wave.sampleSize != 32)) + { + TraceLog(WARNING, "[%s] WAV sample size (%ibit) not supported, converted to 16bit", fileName, wave.sampleSize); + WaveFormat(&wave, wave.sampleRate, 16, wave.channels); + } + + // NOTE: Only support up to 2 channels (mono, stereo) + if (wave.channels > 2) + { + WaveFormat(&wave, wave.sampleRate, wave.sampleSize, 2); + TraceLog(WARNING, "[%s] WAV channels number (%i) not supported, converted to 2 channels", fileName, wave.channels); + } - // Now we set the variables that we need later - wave.sampleCount = waveData.subChunkSize; - wave.sampleRate = waveFormat.sampleRate; - wave.sampleSize = waveFormat.bitsPerSample; - wave.channels = waveFormat.numChannels; + // NOTE: subChunkSize comes in bytes, we need to translate it to number of samples + wave.sampleCount = (wavData.subChunkSize/(wave.sampleSize/8))/wave.channels; - TraceLog(INFO, "[%s] WAV file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", fileName, wave.sampleRate, wave.sampleSize, wave.channels); + TraceLog(INFO, "[%s] WAV file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); } } } @@ -1232,22 +1203,19 @@ static Wave LoadOGG(const char *fileName) wave.sampleRate = info.sample_rate; wave.sampleSize = 16; // 16 bit per sample (short) wave.channels = info.channels; + wave.sampleCount = (int)stb_vorbis_stream_length_in_samples(oggFile); - int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile)*info.channels); float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile); - 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 = (int)(totalSeconds*info.sample_rate*info.channels); - wave.sampleCount = totalSamples; + wave.data = (short *)malloc(wave.sampleCount*wave.channels*sizeof(short)); - wave.data = (short *)malloc(totalSamplesLength*sizeof(short)); + // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) + int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, wave.sampleCount*wave.channels); - int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, (short *)wave.data, totalSamplesLength); + TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, numSamplesOgg); - TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained); - - TraceLog(INFO, "[%s] OGG file loaded successfully (SampleRate: %i, SampleSize: %i, Channels: %i)", fileName, wave.sampleRate, wave.sampleSize, wave.channels); + TraceLog(INFO, "[%s] OGG file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); stb_vorbis_close(oggFile); } @@ -1263,13 +1231,17 @@ static Wave LoadFLAC(const char *fileName) // 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); - - wave.sampleCount = (int)totalSampleCount; - wave.sampleSize = 32; - + wave.data = drflac_open_and_decode_file_s16(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount); + + wave.sampleCount = (int)totalSampleCount/wave.channels; + wave.sampleSize = 16; + + // NOTE: Only support up to 2 channels (mono, stereo) + if (wave.channels > 2) TraceLog(WARNING, "[%s] FLAC channels number (%i) not supported", fileName, wave.channels); + if (wave.data == NULL) TraceLog(WARNING, "[%s] FLAC data could not be loaded", fileName); - + else TraceLog(INFO, "[%s] FLAC file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); + return wave; } @@ -1314,4 +1286,4 @@ void TraceLog(int msgType, const char *text, ...) if (msgType == ERROR) exit(1); // If ERROR message, exit program } -#endif
\ No newline at end of file +#endif diff --git a/src/audio.h b/src/audio.h index 2b3c5933..01ed9f72 100644 --- a/src/audio.h +++ b/src/audio.h @@ -9,7 +9,7 @@ * Manage mixing channels * Manage raw audio context * -* External libs: +* DEPENDENCIES: * OpenAL Soft - Audio device management (http://kcat.strangesoft.net/openal.html) * stb_vorbis - OGG audio files loading (http://www.nothings.org/stb_vorbis/) * jar_xm - XM module file loading @@ -23,6 +23,8 @@ * Raw audio context support * * +* LICENSE: zlib/libpng +* * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event @@ -109,13 +111,13 @@ extern "C" { // Prevents name mangling of functions void InitAudioDevice(void); // Initialize audio device and context void CloseAudioDevice(void); // Close the audio device and context bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully +void SetMasterVolume(float volume); // Set master volume (listener) -Wave LoadWave(const char *fileName); // Load wave data from file into RAM -Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from float array data (32bit) -Sound LoadSound(const 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 +Wave LoadWave(const char *fileName); // Load wave data from file +Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data +Sound LoadSound(const char *fileName); // Load sound from file +Sound LoadSoundFromWave(Wave wave); // Load sound from wave data +void UpdateSound(Sound sound, const void *data, int samplesCount); // Update sound buffer with new data void UnloadWave(Wave wave); // Unload wave data void UnloadSound(Sound sound); // Unload sound void PlaySound(Sound sound); // Play a sound @@ -139,13 +141,14 @@ void ResumeMusicStream(Music music); // Resume playin 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) +void SetMusicLoopCount(Music music, float count); // Set music loop count (loop repeats) 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 raw audio pcm data) -void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data +void UpdateAudioStream(AudioStream stream, void *data, int samplesCount); // 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 diff --git a/src/camera.h b/src/camera.h index 33220390..87ba1942 100644 --- a/src/camera.h +++ b/src/camera.h @@ -2,6 +2,10 @@ * * raylib Camera System - Camera Modes Setup and Control Functions * +* NOTE: Memory footprint of this library is aproximately 52 bytes (global variables) +* +* CONFIGURATION: +* * #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 @@ -11,10 +15,14 @@ * 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) +* CONTRIBUTORS: +* Marc Palau: Initial implementation (2014) +* Ramon Santamaria: Supervision, review, update and maintenance +* +* +* LICENSE: zlib/libpng * -* Initial design by Marc Palau (2014) -* Reviewed by Ramon Santamaria (2015-2016) +* Copyright (c) 2015-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. @@ -223,15 +231,15 @@ void SetCameraMode(Camera camera, int mode) float dy = v2.y - v1.y; float dz = v2.z - v1.z; - cameraTargetDistance = sqrt(dx*dx + dy*dy + dz*dz); + cameraTargetDistance = sqrtf(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); + distance.x = sqrtf(dx*dx + dz*dz); + distance.y = sqrtf(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) + cameraAngle.x = asinf(fabsf(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW) + cameraAngle.y = -asinf(fabsf(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) @@ -285,10 +293,10 @@ void UpdateCamera(Camera *camera) { 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 }); + if (mousePosition.x < (float)screenHeight/3.0f) SetMousePosition((Vector2){ screenWidth - screenHeight/3, mousePosition.y }); + else if (mousePosition.y < (float)screenHeight/3.0f) SetMousePosition((Vector2){ mousePosition.x, screenHeight - screenHeight/3 }); + else if (mousePosition.x > (screenWidth - (float)screenHeight/3.0f)) SetMousePosition((Vector2){ screenHeight/3, mousePosition.y }); + else if (mousePosition.y > (screenHeight - (float)screenHeight/3.0f)) SetMousePosition((Vector2){ mousePosition.x, screenHeight/3 }); else { mousePositionDelta.x = mousePosition.x - previousMousePosition.x; @@ -321,6 +329,7 @@ void UpdateCamera(Camera *camera) if (cameraTargetDistance > CAMERA_FREE_DISTANCE_MAX_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MAX_CLAMP; } // Camera looking down + // TODO: Review, weird comparisson of cameraTargetDistance == 120.0f? 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; @@ -341,6 +350,7 @@ void UpdateCamera(Camera *camera) if (cameraTargetDistance < CAMERA_FREE_DISTANCE_MIN_CLAMP) cameraTargetDistance = CAMERA_FREE_DISTANCE_MIN_CLAMP; } // Camera looking up + // TODO: Review, weird comparisson of cameraTargetDistance == 120.0f? 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; @@ -385,9 +395,9 @@ void UpdateCamera(Camera *camera) 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); + camera->target.x += ((mousePositionDelta.x*-CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER); + camera->target.y += ((mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER); + camera->target.z += ((mousePositionDelta.x*CAMERA_FREE_MOUSE_SENSITIVITY)*sinf(cameraAngle.x) + (mousePositionDelta.y*CAMERA_FREE_MOUSE_SENSITIVITY)*cosf(cameraAngle.x)*sinf(cameraAngle.y))*(cameraTargetDistance/CAMERA_FREE_PANNING_DIVIDER); } } @@ -404,19 +414,19 @@ void UpdateCamera(Camera *camera) 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.x += (sinf(cameraAngle.x)*direction[MOVE_BACK] - + sinf(cameraAngle.x)*direction[MOVE_FRONT] - + cosf(cameraAngle.x)*direction[MOVE_LEFT] + + cosf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; - camera->position.y += (sin(cameraAngle.y)*direction[MOVE_FRONT] - - sin(cameraAngle.y)*direction[MOVE_BACK] + + camera->position.y += (sinf(cameraAngle.y)*direction[MOVE_FRONT] - + sinf(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; + camera->position.z += (cosf(cameraAngle.x)*direction[MOVE_BACK] - + cosf(cameraAngle.x)*direction[MOVE_FRONT] + + sinf(cameraAngle.x)*direction[MOVE_LEFT] - + sinf(cameraAngle.x)*direction[MOVE_RIGHT])/PLAYER_MOVEMENT_SENSITIVITY; bool isMoving = false; // Required for swinging @@ -439,9 +449,9 @@ void UpdateCamera(Camera *camera) 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.x = camera->position.x + CAMERA_THIRD_PERSON_OFFSET.x*cosf(cameraAngle.x) + CAMERA_THIRD_PERSON_OFFSET.z*sinf(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); + camera->target.z = camera->position.z + CAMERA_THIRD_PERSON_OFFSET.z*sinf(cameraAngle.x) - CAMERA_THIRD_PERSON_OFFSET.x*sinf(cameraAngle.x); } else // CAMERA_FIRST_PERSON { @@ -450,18 +460,18 @@ void UpdateCamera(Camera *camera) 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; + camera->target.x = camera->position.x - sinf(cameraAngle.x)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; + camera->target.y = camera->position.y + sinf(cameraAngle.y)*CAMERA_FIRST_PERSON_FOCUS_DISTANCE; + camera->target.z = camera->position.z - cosf(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->position.y = playerEyesPosition - sinf(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; + camera->up.x = sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; + camera->up.z = -sinf(swingCounter/(CAMERA_FIRST_PERSON_STEP_TRIGONOMETRIC_DIVIDER*2))/CAMERA_FIRST_PERSON_WAVING_DIVIDER; } } break; default: break; @@ -473,10 +483,10 @@ void UpdateCamera(Camera *camera) (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; + camera->position.x = sinf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.x; + if (cameraAngle.y <= 0.0f) camera->position.y = sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; + else camera->position.y = -sinf(cameraAngle.y)*cameraTargetDistance*sinf(cameraAngle.y) + camera->target.y; + camera->position.z = cosf(cameraAngle.x)*cameraTargetDistance*cosf(cameraAngle.y) + camera->target.z; } } @@ -1,27 +1,46 @@ /********************************************************************************************** * -* raylib.core -* -* Basic functions to manage windows, OpenGL context and input on multiple platforms +* raylib.core - Basic functions to manage windows, OpenGL context and input on multiple platforms * * The following platforms are supported: Windows, Linux, Mac (OSX), Android, Raspberry Pi, HTML5, Oculus Rift CV1 * -* External libs: +* CONFIGURATION: +* +* #define PLATFORM_DESKTOP +* Windowing and input system configured for desktop platforms: Windows, Linux, OSX (managed by GLFW3 library) +* NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [rlgl] module to enable it +* +* #define PLATFORM_ANDROID +* Windowing and input system configured for Android device, app activity managed internally in this module. +* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL +* +* #define PLATFORM_RPI +* Windowing and input system configured for Raspberry Pi (tested on Raspbian), graphic device is managed by EGL +* and inputs are processed is raw mode, reading from /dev/input/ +* +* #define PLATFORM_WEB +* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js +* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. +* +* #define LOAD_DEFAULT_FONT (defined by default) +* Default font is loaded on window initialization to be available for the user to render simple text. +* NOTE: If enabled, uses external module functions to load default raylib font (module: text) +* +* #define INCLUDE_CAMERA_SYSTEM / SUPPORT_CAMERA_SYSTEM +* +* #define INCLUDE_GESTURES_SYSTEM / SUPPORT_GESTURES_SYSTEM +* +* #define SUPPORT_MOUSE_GESTURES +* Mouse gestures are directly mapped like touches and processed by gestures system. +* +* DEPENDENCIES: * GLFW3 - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX) * raymath - 3D math functionality (Vector3, Matrix, Quaternion) -* camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person) +* camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person) * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) * -* Module Configuration Flags: -* PLATFORM_DESKTOP - Windows, Linux, Mac (OSX) -* PLATFORM_ANDROID - Android (only OpenGL ES 2.0 devices), graphic device is managed by EGL and input system by Android activity. -* PLATFORM_RPI - Rapsberry Pi (tested on Raspbian), graphic device is managed by EGL and input system is coded in raw mode. -* PLATFORM_WEB - HTML5 (using emscripten compiler) -* -* RL_LOAD_DEFAULT_FONT - Use external module functions to load default raylib font (module: text) -* -* NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for render mirror - View [rlgl] module to enable it * +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -42,13 +61,14 @@ * **********************************************************************************************/ -#include "raylib.h" // raylib main header +#include "raylib.h" + #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -#include "utils.h" // Includes Android fopen map, InitAssetManager(), TraceLog() +#include "utils.h" // Required for: fopen() Android mapping, 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 +#include "raymath.h" // Required for: Vector3 and Matrix functions #define GESTURES_IMPLEMENTATION #include "gestures.h" // Gestures detection functionality @@ -66,9 +86,15 @@ #include <string.h> // Required for: strcmp() //#include <errno.h> // Macros for reporting and retrieving error conditions through error codes +#if defined __linux || defined(PLATFORM_WEB) + #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX +#elif defined __APPLE__ + #include <unistd.h> // Required for: usleep() +#endif + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - //#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 - #include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management + //#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + #include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management #ifdef __linux #define GLFW_EXPOSE_NATIVE_X11 // Linux specific definitions for getting @@ -98,7 +124,7 @@ #include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition #include <linux/input.h> // Linux: Keycodes constants definition (KEY_A, ...) #include <linux/joystick.h> // Linux: Joystick support library - + #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions #include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions @@ -120,6 +146,7 @@ // Old device inputs system #define DEFAULT_KEYBOARD_DEV STDIN_FILENO // Standard input #define DEFAULT_MOUSE_DEV "/dev/input/mouse0" // Mouse input + #define DEFAULT_TOUCH_DEV "/dev/input/event4" // Touch input virtual device (created by ts_uinput) #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) // New device input events (evdev) (must be detected) @@ -134,7 +161,7 @@ #define MAX_GAMEPAD_BUTTONS 32 // 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) +#define LOAD_DEFAULT_FONT // Load default font on window initialization (module: text) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -173,6 +200,11 @@ static int mouseStream = -1; // Mouse device file descriptor static bool mouseReady = false; // Flag to know if mouse is ready static pthread_t mouseThreadId; // Mouse reading thread id +// Touch input variables +static int touchStream = -1; // Touch device file descriptor +static bool touchReady = false; // Flag to know if touch interface is ready +static pthread_t touchThreadId; // Touch reading thread id + // Gamepad input variables static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor static pthread_t gamepadThreadId; // Gamepad reading thread id @@ -236,7 +268,7 @@ static int dropFilesCount = 0; // Count stored strings static double currentTime, previousTime; // Used to track timmings static double updateTime, drawTime; // Time measures for update and draw -static double frameTime; // Time measure for one frame +static double frameTime = 0.0; // Time measure for one frame static double targetTime = 0.0; // Desired time for one frame, if 0 not applied static char configFlags = 0; // Configuration flags (bit based) @@ -245,7 +277,7 @@ static bool showLogo = false; // Track if showing logo at init is //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by core) //---------------------------------------------------------------------------------- -#if defined(RL_LOAD_DEFAULT_FONT) +#if defined(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 @@ -257,11 +289,13 @@ static void InitGraphicsDevice(int width, int height); // Initialize graphics d static void SetupFramebufferSize(int displayWidth, int displayHeight); static void InitTimer(void); // Initialize timer static double GetTime(void); // Returns time since InitTimer() was run +static void Wait(float ms); // Wait for some milliseconds (stop program execution) static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed static void PollInputEvents(void); // Register user events static void SwapBuffers(void); // Copy back buffer to front buffers static void LogoAnimation(void); // Plays raylib logo appearing animation +static void SetupViewport(void); // Set viewport parameters #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) static void TakeScreenshot(void); // Takes a screenshot and saves it in the same folder as executable #endif @@ -290,6 +324,7 @@ 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); static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); #endif #if defined(PLATFORM_RPI) @@ -298,10 +333,17 @@ static void ProcessKeyboard(void); // Process keyboard even static void RestoreKeyboard(void); // Restore keyboard system static void InitMouse(void); // Mouse initialization (including mouse thread) static void *MouseThread(void *arg); // Mouse reading thread +static void InitTouch(void); // Touch device initialization (including touch thread) +static void *TouchThread(void *arg); // Touch device reading thread static void InitGamepad(void); // Init raw gamepad input static void *GamepadThread(void *arg); // Mouse reading thread #endif +#if defined(_WIN32) + // NOTE: We include Sleep() function signature here to avoid windows.h inclusion + void __stdcall Sleep(unsigned long msTimeout); // Required for Wait() +#endif + //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- @@ -309,7 +351,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.6.0)"); + TraceLog(INFO, "Initializing raylib (v1.7.0)"); // Store window title (could be useful...) windowTitle = title; @@ -317,7 +359,7 @@ void InitWindow(int width, int height, const char *title) // Init graphics device (display device and OpenGL context) InitGraphicsDevice(width, height); -#if defined(RL_LOAD_DEFAULT_FONT) +#if defined(LOAD_DEFAULT_FONT) // Load default font // NOTE: External function (defined in module: text) LoadDefaultFont(); @@ -329,6 +371,7 @@ void InitWindow(int width, int height, const char *title) #if defined(PLATFORM_RPI) // Init raw input system InitMouse(); // Mouse init + InitTouch(); // Touch init InitKeyboard(); // Keyboard init InitGamepad(); // Gamepad init #endif @@ -344,9 +387,9 @@ void InitWindow(int width, int height, const char *title) 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); + // Support gamepad (not provided by GLFW3 on emscripten) + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); #endif mousePosition.x = (float)screenWidth/2.0f; @@ -365,7 +408,7 @@ void InitWindow(int width, int height, const char *title) // Android activity initialization void InitWindow(int width, int height, void *state) { - TraceLog(INFO, "Initializing raylib (v1.6.0)"); + TraceLog(INFO, "Initializing raylib (v1.7.0)"); app_dummy(); @@ -428,7 +471,7 @@ void InitWindow(int width, int height, void *state) // Close Window and Terminate Context void CloseWindow(void) { -#if defined(RL_LOAD_DEFAULT_FONT) +#if defined(LOAD_DEFAULT_FONT) UnloadDefaultFont(); #endif @@ -466,10 +509,11 @@ void CloseWindow(void) // Wait for mouse and gamepad threads to finish before closing // NOTE: Those threads should already have finished at this point // because they are controlled by windowShouldClose variable - + windowShouldClose = true; // Added to force threads to exit when the close window is called - + pthread_join(mouseThreadId, NULL); + pthread_join(touchThreadId, NULL); pthread_join(gamepadThreadId, NULL); #endif @@ -481,7 +525,7 @@ bool WindowShouldClose(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // While window minimized, stop loop execution - while (windowMinimized) glfwPollEvents(); + while (windowMinimized) glfwWaitEvents(); return (glfwWindowShouldClose(window)); #endif @@ -501,7 +545,7 @@ bool IsWindowMinimized(void) #endif } -// Fullscreen toggle +// Fullscreen toggle (only PLATFORM_DESKTOP) void ToggleFullscreen(void) { #if defined(PLATFORM_DESKTOP) @@ -517,6 +561,45 @@ void ToggleFullscreen(void) #endif } +// Set icon for window (only PLATFORM_DESKTOP) +void SetWindowIcon(Image image) +{ +#if defined(PLATFORM_DESKTOP) + ImageFormat(&image, UNCOMPRESSED_R8G8B8A8); + + GLFWimage icon[1]; + + icon[0].width = image.width; + icon[0].height = image.height; + icon[0].pixels = (unsigned char *)image.data; + + // NOTE: We only support one image icon + glfwSetWindowIcon(window, 1, icon); + + // TODO: Support multi-image icons --> image.mipmaps +#endif +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + glfwSetWindowPos(window, x, y); +} + +// Set monitor for the current window (fullscreen mode) +void SetWindowMonitor(int monitor) +{ + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + glfwSetWindowMonitor(window, monitors[monitor], 0, 0, screenWidth, screenHeight, GLFW_DONT_CARE); + TraceLog(INFO, "Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + } + else TraceLog(WARNING, "Selected monitor not found"); +} + // Get current screen width int GetScreenWidth(void) { @@ -548,14 +631,14 @@ void HideCursor() { #if defined(PLATFORM_DESKTOP) #ifdef __linux - XColor Col; - const char Nil[] = {0}; + XColor col; + const char nil[] = {0}; - Pixmap Pix = XCreateBitmapFromData(glfwGetX11Display(), glfwGetX11Window(window), Nil, 1, 1); - Cursor Cur = XCreatePixmapCursor(glfwGetX11Display(), Pix, Pix, &Col, &Col, 0, 0); + Pixmap pix = XCreateBitmapFromData(glfwGetX11Display(), glfwGetX11Window(window), nil, 1, 1); + Cursor cur = XCreatePixmapCursor(glfwGetX11Display(), pix, pix, &col, &col, 0, 0); - XDefineCursor(glfwGetX11Display(), glfwGetX11Window(window), Cur); - XFreeCursor(glfwGetX11Display(), Cur); + XDefineCursor(glfwGetX11Display(), glfwGetX11Window(window), cur); + XFreeCursor(glfwGetX11Display(), cur); #else glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); #endif @@ -622,17 +705,18 @@ void EndDrawing(void) currentTime = GetTime(); drawTime = currentTime - previousTime; previousTime = currentTime; - + frameTime = updateTime + drawTime; - double extraTime = 0.0; - - while (frameTime < targetTime) + // Wait for some milliseconds... + if (frameTime < targetTime) { - // Implement a delay + Wait((targetTime - frameTime)*1000.0f); + currentTime = GetTime(); - extraTime = currentTime - previousTime; + double extraTime = currentTime - previousTime; previousTime = currentTime; + frameTime += extraTime; } } @@ -744,8 +828,7 @@ void EndTextureMode(void) rlDisableRenderTexture(); // Disable render target // Set viewport to default framebuffer size (screen size) - // TODO: consider possible viewport offsets - rlViewport(0, 0, GetScreenWidth(), GetScreenHeight()); + SetupViewport(); rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix rlLoadIdentity(); // Reset current matrix (PROJECTION) @@ -768,20 +851,16 @@ void SetTargetFPS(int fps) } // Returns current FPS -float GetFPS(void) +int GetFPS(void) { - return (float)(1.0/frameTime); + return (int)(1.0f/GetFrameTime()); } // Returns time in seconds for one frame float GetFrameTime(void) { - // 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; - - return (float)roundedFrameTime; // Time in seconds to run a frame + // NOTE: We round value to milliseconds + return (float)frameTime; } // Converts Color to float array and normalizes @@ -987,14 +1066,14 @@ int StorageLoadValue(int position) { // Get file size fseek(storageFile, 0, SEEK_END); - int fileSize = ftell(storageFile); // Size in bytes + 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); + fread(&value, 4, 1, storageFile); // Read 1 element of 4 bytes size } fclose(storageFile); @@ -1089,7 +1168,7 @@ Vector2 GetWorldToScreen(Vector3 position, Camera camera) 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.w }; // Calculate 2d screen position vector Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)GetScreenWidth(), (ndcPos.y + 1.0f)/2.0f*(float)GetScreenHeight() }; @@ -1128,7 +1207,7 @@ bool IsKeyDown(int key) bool IsKeyReleased(int key) { bool released = false; - + if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 0)) released = true; else released = false; @@ -1163,7 +1242,7 @@ void SetExitKey(int key) bool IsGamepadAvailable(int gamepad) { bool result = false; - + #if !defined(PLATFORM_ANDROID) if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad]) result = true; #endif @@ -1177,7 +1256,7 @@ bool IsGamepadName(int gamepad, const char *name) bool result = false; #if !defined(PLATFORM_ANDROID) - const char *gamepadName = NULL; + const char *gamepadName = NULL; if (gamepadReady[gamepad]) gamepadName = GetGamepadName(gamepad); if ((name != NULL) && (gamepadName != NULL)) result = (strcmp(name, gamepadName) == 0); @@ -1193,7 +1272,7 @@ const char *GetGamepadName(int gamepad) if (gamepadReady[gamepad]) return glfwGetJoystickName(gamepad); else return NULL; #elif defined(PLATFORM_RPI) - if (gamepadReady[gamepad]) ioctl(gamepadStream[gamepad], JSIOCGNAME(64), &gamepadName); + if (gamepadReady[gamepad]) ioctl(gamepadStream[gamepad], JSIOCGNAME(64), &gamepadName); return gamepadName; #else @@ -1216,7 +1295,7 @@ int GetGamepadAxisCount(int gamepad) float GetGamepadAxisMovement(int gamepad, int axis) { float value = 0; - + #if !defined(PLATFORM_ANDROID) if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (axis < MAX_GAMEPAD_AXIS)) value = gamepadAxisState[gamepad][axis]; #endif @@ -1230,8 +1309,8 @@ bool IsGamepadButtonPressed(int gamepad, int button) bool pressed = false; #if !defined(PLATFORM_ANDROID) - if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && - (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) && + if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && + (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) && (currentGamepadState[gamepad][button] == 1)) pressed = true; #endif @@ -1255,10 +1334,10 @@ bool IsGamepadButtonDown(int gamepad, int button) bool IsGamepadButtonReleased(int gamepad, int button) { bool released = false; - + #if !defined(PLATFORM_ANDROID) - if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && - (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) && + if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && + (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) && (currentGamepadState[gamepad][button] == 0)) released = true; #endif @@ -1271,7 +1350,7 @@ bool IsGamepadButtonUp(int gamepad, int button) bool result = false; #if !defined(PLATFORM_ANDROID) - if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && + if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && (currentGamepadState[gamepad][button] == 0)) result = true; #endif @@ -1479,13 +1558,23 @@ static void InitGraphicsDevice(int width, int height) glfwDefaultWindowHints(); // Set default windows hints - if (configFlags & FLAG_RESIZABLE_WINDOW) + // Check some Window creation flags + if (configFlags & FLAG_WINDOW_RESIZABLE) glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // Resizable window + else glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable + + if (configFlags & FLAG_WINDOW_DECORATED) glfwWindowHint(GLFW_DECORATED, GL_TRUE); // Border and buttons on Window + + if (configFlags & FLAG_WINDOW_TRANSPARENT) { - glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // Resizable window + // TODO: Enable transparent window (not ready yet on GLFW 3.2) + } + + if (configFlags & FLAG_MSAA_4X_HINT) + { + glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 + TraceLog(INFO, "Trying to enable MSAA x4"); } - 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 @@ -1494,13 +1583,7 @@ static void InitGraphicsDevice(int width, int height) // 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 - TraceLog(INFO, "Trying to enable MSAA x4"); - } + // For example, if using OpenGL 1.1, driver can provide a 4.3 context forward compatible. // Check selection OpenGL version if (rlGetVersion() == OPENGL_21) @@ -1607,7 +1690,10 @@ static void InitGraphicsDevice(int width, int height) #endif glfwMakeContextCurrent(window); - glfwSwapInterval(0); // Disable VSync by default + + // Try to disable GPU V-Sync by default, set framerate using SetTargetFPS() + // NOTE: V-Sync can be enabled by graphic driver configuration + glfwSwapInterval(0); #if defined(PLATFORM_DESKTOP) // Load OpenGL 3.3 extensions @@ -1615,9 +1701,8 @@ static void InitGraphicsDevice(int width, int height) 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() + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration if (configFlags & FLAG_VSYNC_HINT) { glfwSwapInterval(1); @@ -1776,19 +1861,8 @@ static void InitGraphicsDevice(int width, int height) // 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 + // Setup default viewport + SetupViewport(); // Initialize internal projection and modelview matrices // NOTE: Default to orthographic projection mode with top-left corner at (0,0) @@ -1805,6 +1879,24 @@ static void InitGraphicsDevice(int width, int height) #endif } +// Set viewport parameters +static void SetupViewport(void) +{ +#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 +} + // 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) @@ -1912,6 +2004,34 @@ static double GetTime(void) #endif } +// Wait for some milliseconds (stop program execution) +static void Wait(float ms) +{ +#define SUPPORT_BUSY_WAIT_LOOP +#if defined(SUPPORT_BUSY_WAIT_LOOP) + double prevTime = GetTime(); + double nextTime = 0.0; + + // Busy wait loop + while ((nextTime - prevTime) < ms/1000.0f) nextTime = GetTime(); +#else + #if defined _WIN32 + Sleep(ms); + #elif defined __linux || defined(PLATFORM_WEB) + struct timespec req = { 0 }; + time_t sec = (int)(ms/1000.0f); + ms -= (sec*1000); + req.tv_sec = sec; + req.tv_nsec = ms*1000000L; + + // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated. + while (nanosleep(&req, &req) == -1) continue; + #elif defined __APPLE__ + usleep(ms*1000.0f); + #endif +#endif +} + // Get one key state static bool GetKeyStatus(int key) { @@ -1934,7 +2054,7 @@ static bool GetMouseButtonStatus(int button) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) return glfwGetMouseButton(window, button); #elif defined(PLATFORM_ANDROID) - // TODO: Check for virtual mouse + // TODO: Check for virtual mouse? return false; #elif defined(PLATFORM_RPI) // NOTE: Mouse buttons states are filled in PollInputEvents() @@ -1948,10 +2068,10 @@ 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(); - + // Reset last key pressed registered lastKeyPressed = -1; - + #if !defined(PLATFORM_RPI) // Reset last gamepad button/axis registered state lastGamepadButtonPressed = -1; @@ -1967,7 +2087,7 @@ static void PollInputEvents(void) mousePosition.x = (float)mouseX; mousePosition.y = (float)mouseY; - + // Keyboard input polling (automatically managed by GLFW3 through callback) // Register previous keys states @@ -1980,8 +2100,6 @@ static void PollInputEvents(void) currentMouseWheelY = 0; #endif -// NOTE: GLFW3 joystick functionality not available in web -// TODO: Support joysticks using emscripten API #if defined(PLATFORM_DESKTOP) // Check if gamepads are ready // NOTE: We do it here in case of disconection @@ -1990,7 +2108,7 @@ static void PollInputEvents(void) if (glfwJoystickPresent(i)) gamepadReady[i] = true; else gamepadReady[i] = false; } - + // Register gamepads buttons events for (int i = 0; i < MAX_GAMEPADS; i++) { @@ -1998,14 +2116,14 @@ static void PollInputEvents(void) { // 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[k] == GLFW_PRESS) @@ -2015,18 +2133,18 @@ static void PollInputEvents(void) } 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]; } - + gamepadAxisCount = axisCount; } } @@ -2034,6 +2152,47 @@ static void PollInputEvents(void) glfwPollEvents(); // Register keyboard/mouse events (callbacks)... and window events! #endif +// Gamepad support using emscripten API +// NOTE: GLFW3 joystick functionality not available in web +#if defined(PLATFORM_WEB) + // Get number of gamepads connected + int numGamepads = emscripten_get_num_gamepads(); + + for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++) + { + // Register previous gamepad button states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) previousGamepadState[i][k] = currentGamepadState[i][k]; + + EmscriptenGamepadEvent gamepadState; + + int result = emscripten_get_gamepad_status(i, &gamepadState); + + if (result == EMSCRIPTEN_RESULT_SUCCESS) + { + // Register buttons data for every connected gamepad + for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) + { + if (gamepadState.digitalButton[j] == 1) + { + currentGamepadState[i][j] = 1; + lastGamepadButtonPressed = j; + } + else currentGamepadState[i][j] = 0; + + //printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + } + + // Register axis data for every connected gamepad + for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) + { + gamepadAxisState[i][j] = gamepadState.axis[j]; + } + + gamepadAxisCount = gamepadState.numAxes; + } + } +#endif + #if defined(PLATFORM_ANDROID) // Register previous keys states // NOTE: Android supports up to 260 keys @@ -2091,7 +2250,7 @@ static void TakeScreenshot(void) sprintf(buffer, "screenshot%03i.png", shotNum); // Save image as PNG - WritePNG(buffer, imgData, renderWidth, renderHeight, 4); + SavePNG(buffer, imgData, renderWidth, renderHeight, 4); free(imgData); @@ -2301,7 +2460,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Init graphics device (display device and OpenGL context) InitGraphicsDevice(screenWidth, screenHeight); - #if defined(RL_LOAD_DEFAULT_FONT) + #if defined(LOAD_DEFAULT_FONT) // Load default font // NOTE: External function (defined in module: text) LoadDefaultFont(); @@ -2480,6 +2639,8 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) #endif #if defined(PLATFORM_WEB) + +// Register fullscreen change events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData) { //isFullscreen: int e->isFullscreen @@ -2503,7 +2664,7 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte return 0; } -// Web: Get input events +// Register touch input events static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) { /* @@ -2567,6 +2728,26 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent return 1; } + +// Register connected/disconnected gamepads events +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) +{ + /* + printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n", + eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state", + gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); + + for(int i = 0; i < gamepadEvent->numAxes; ++i) printf("Axis %d: %g\n", i, gamepadEvent->axis[i]); + for(int i = 0; i < gamepadEvent->numButtons; ++i) printf("Button %d: Digital: %d, Analog: %g\n", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + */ + + if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) gamepadReady[gamepadEvent->index] = true; + else gamepadReady[gamepadEvent->index] = false; + + // TODO: Test gamepadEvent->index + + return 0; +} #endif #if defined(PLATFORM_RPI) @@ -2748,7 +2929,8 @@ static void InitMouse(void) // if too much time passes between reads, queue gets full and new events override older ones... static void *MouseThread(void *arg) { - const unsigned char XSIGN = 1<<4, YSIGN = 1<<5; + const unsigned char XSIGN = (1 << 4); + const unsigned char YSIGN = (1 << 5); typedef struct { char buttons; @@ -2803,6 +2985,110 @@ static void *MouseThread(void *arg) return NULL; } +// Touch initialization (including touch thread) +static void InitTouch(void) +{ + if ((touchStream = open(DEFAULT_TOUCH_DEV, O_RDONLY|O_NONBLOCK)) < 0) + { + TraceLog(WARNING, "Touch device could not be opened, no touchscreen available"); + } + else + { + touchReady = true; + + int error = pthread_create(&touchThreadId, NULL, &TouchThread, NULL); + + if (error != 0) TraceLog(WARNING, "Error creating touch input event thread"); + else TraceLog(INFO, "Touch device initialized successfully"); + } +} + +// Touch reading thread. +// This reads from a Virtual Input Event /dev/input/event4 which is +// created by the ts_uinput daemon. This takes, filters and scales +// raw input from the Touchscreen (which appears in /dev/input/event3) +// based on the Calibration data referenced by tslib. +static void *TouchThread(void *arg) +{ + struct input_event ev; + GestureEvent gestureEvent; + + while (!windowShouldClose) + { + if (read(touchStream, &ev, sizeof(ev)) == (int)sizeof(ev)) + { + // if pressure > 0 then simulate left mouse button click + if (ev.type == EV_ABS && ev.code == 24 && ev.value == 0 && currentMouseState[0] == 1) + { + currentMouseState[0] = 0; + gestureEvent.touchAction = TOUCH_UP; + gestureEvent.pointCount = 1; + gestureEvent.pointerId[0] = 0; + gestureEvent.pointerId[1] = 1; + gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + gestureEvent.position[1].x /= (float)GetScreenWidth(); + gestureEvent.position[1].y /= (float)GetScreenHeight(); + ProcessGestureEvent(gestureEvent); + } + if (ev.type == EV_ABS && ev.code == 24 && ev.value > 0 && currentMouseState[0] == 0) + { + currentMouseState[0] = 1; + gestureEvent.touchAction = TOUCH_DOWN; + gestureEvent.pointCount = 1; + gestureEvent.pointerId[0] = 0; + gestureEvent.pointerId[1] = 1; + gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + gestureEvent.position[1].x /= (float)GetScreenWidth(); + gestureEvent.position[1].y /= (float)GetScreenHeight(); + ProcessGestureEvent(gestureEvent); + } + // x & y values supplied by event4 have been scaled & de-jittered using tslib calibration data + if (ev.type == EV_ABS && ev.code == 0) + { + mousePosition.x = ev.value; + if (mousePosition.x < 0) mousePosition.x = 0; + if (mousePosition.x > screenWidth) mousePosition.x = screenWidth; + gestureEvent.touchAction = TOUCH_MOVE; + gestureEvent.pointCount = 1; + gestureEvent.pointerId[0] = 0; + gestureEvent.pointerId[1] = 1; + gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + gestureEvent.position[1].x /= (float)GetScreenWidth(); + gestureEvent.position[1].y /= (float)GetScreenHeight(); + ProcessGestureEvent(gestureEvent); + } + if (ev.type == EV_ABS && ev.code == 1) + { + mousePosition.y = ev.value; + if (mousePosition.y < 0) mousePosition.y = 0; + if (mousePosition.y > screenHeight) mousePosition.y = screenHeight; + gestureEvent.touchAction = TOUCH_MOVE; + gestureEvent.pointCount = 1; + gestureEvent.pointerId[0] = 0; + gestureEvent.pointerId[1] = 1; + gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y }; + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + gestureEvent.position[1].x /= (float)GetScreenWidth(); + gestureEvent.position[1].y /= (float)GetScreenHeight(); + ProcessGestureEvent(gestureEvent); + } + + } + } + return NULL; +} + // Init gamepad system static void InitGamepad(void) { @@ -2867,7 +3153,7 @@ static void *GamepadThread(void *arg) { // 1 - button pressed, 0 - button released currentGamepadState[i][gamepadEvent.number] = (int)gamepadEvent.value; - + if ((int)gamepadEvent.value == 1) lastGamepadButtonPressed = gamepadEvent.number; else lastGamepadButtonPressed = -1; } diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h index d7b66f20..d2bebea5 100644 --- a/src/external/dr_flac.h +++ b/src/external/dr_flac.h @@ -1,5 +1,5 @@ // FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file. -// dr_flac - v0.4 - 2016-09-29 +// dr_flac - v0.4c - 2016-12-26 // // David Reid - [email protected] @@ -105,18 +105,32 @@ #include <stdint.h> #include <stddef.h> -#ifndef DR_BOOL_DEFINED -#define DR_BOOL_DEFINED -#ifdef _WIN32 -typedef char drBool8; -typedef int drBool32; +#ifndef DR_SIZED_TYPES_DEFINED +#define DR_SIZED_TYPES_DEFINED +#if defined(_MSC_VER) && _MSC_VER < 1600 +typedef signed char dr_int8; +typedef unsigned char dr_uint8; +typedef signed short dr_int16; +typedef unsigned short dr_uint16; +typedef signed int dr_int32; +typedef unsigned int dr_uint32; +typedef signed __int64 dr_int64; +typedef unsigned __int64 dr_uint64; #else #include <stdint.h> -typedef int8_t drBool8; -typedef int32_t drBool32; +typedef int8_t dr_int8; +typedef uint8_t dr_uint8; +typedef int16_t dr_int16; +typedef uint16_t dr_uint16; +typedef int32_t dr_int32; +typedef uint32_t dr_uint32; +typedef int64_t dr_int64; +typedef uint64_t dr_uint64; #endif -#define DR_TRUE 1 -#define DR_FALSE 0 +typedef dr_int8 dr_bool8; +typedef dr_int32 dr_bool32; +#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 @@ -262,7 +276,7 @@ typedef struct { char catalog[128]; uint64_t leadInSampleCount; - drBool32 isCD; + dr_bool32 isCD; uint8_t trackCount; const uint8_t* pTrackData; } cuesheet; @@ -305,7 +319,7 @@ typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t by // // 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); +typedef dr_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); // Callback for when a metadata block is read. // @@ -546,6 +560,10 @@ void drflac_close(drflac* pFlac); // seeked. uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* pBufferOut); +// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. Note +// that this is lossey. +uint64_t drflac_read_s16(drflac* pFlac, uint64_t samplesToRead, int16_t* pBufferOut); + // Seeks to the sample at the given index. // // pFlac [in] The decoder. @@ -558,7 +576,7 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* pBuffer // // 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); +dr_bool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex); @@ -607,14 +625,17 @@ drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drfl // // 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); +int16_t* drflac_open_and_decode_s16(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); +int16_t* drflac_open_and_decode_file_s16(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); +int16_t* drflac_open_and_decode_memory_s16(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); @@ -684,7 +705,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, ui //// Endian Management //// -static DRFLAC_INLINE drBool32 drflac__is_little_endian() +static DRFLAC_INLINE dr_bool32 drflac__is_little_endian() { int n = 1; return (*(char*)&n) == 1; @@ -817,7 +838,7 @@ static DRFLAC_INLINE uint32_t drflac__le2host_32(uint32_t n) #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) +static DRFLAC_INLINE dr_bool32 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)) { @@ -871,7 +892,7 @@ static DRFLAC_INLINE drBool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) } } -static drBool32 drflac__reload_cache(drflac_bs* bs) +static dr_bool32 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)) { @@ -907,7 +928,7 @@ static void drflac__reset_cache(drflac_bs* bs) bs->unalignedCache = 0; } -static drBool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +static dr_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) { if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { bs->consumedBits += bitsToSeek; @@ -958,7 +979,7 @@ static drBool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) } } -static drBool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, uint32_t* pResultOut) +static dr_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, uint32_t* pResultOut) { assert(bs != NULL); assert(pResultOut != NULL); @@ -999,7 +1020,7 @@ static drBool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, uint32 } } -static drBool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, int32_t* pResult) +static dr_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, int32_t* pResult) { assert(bs != NULL); assert(pResult != NULL); @@ -1018,7 +1039,7 @@ static drBool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, int32_t return DR_TRUE; } -static drBool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, uint64_t* pResultOut) +static dr_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, uint64_t* pResultOut) { assert(bitCount <= 64); assert(bitCount > 32); @@ -1039,7 +1060,7 @@ static drBool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, uint64 // 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) +static dr_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, int64_t* pResultOut) { assert(bitCount <= 64); @@ -1056,7 +1077,7 @@ static drBool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, int64_t } #endif -static drBool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, uint16_t* pResult) +static dr_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, uint16_t* pResult) { assert(bs != NULL); assert(pResult != NULL); @@ -1072,7 +1093,7 @@ static drBool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, uint16 return DR_TRUE; } -static drBool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, int16_t* pResult) +static dr_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, int16_t* pResult) { assert(bs != NULL); assert(pResult != NULL); @@ -1088,7 +1109,7 @@ static drBool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, int16_t return DR_TRUE; } -static drBool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, uint8_t* pResult) +static dr_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, uint8_t* pResult) { assert(bs != NULL); assert(pResult != NULL); @@ -1104,7 +1125,7 @@ static drBool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, uint8_t return DR_TRUE; } -static drBool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, int8_t* pResult) +static dr_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, int8_t* pResult) { assert(bs != NULL); assert(pResult != NULL); @@ -1121,7 +1142,7 @@ static drBool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, int8_t* } -static inline drBool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +static inline dr_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) { unsigned int zeroCounter = 0; while (bs->cache == 0) { @@ -1169,7 +1190,7 @@ static inline drBool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned in -static drBool32 drflac__seek_to_byte(drflac_bs* bs, uint64_t offsetFromStart) +static dr_bool32 drflac__seek_to_byte(drflac_bs* bs, uint64_t offsetFromStart) { assert(bs != NULL); assert(offsetFromStart > 0); @@ -1214,7 +1235,7 @@ static drBool32 drflac__seek_to_byte(drflac_bs* bs, uint64_t offsetFromStart) } -static drBool32 drflac__read_utf8_coded_number(drflac_bs* bs, uint64_t* pNumberOut) +static dr_bool32 drflac__read_utf8_coded_number(drflac_bs* bs, uint64_t* pNumberOut) { assert(bs != NULL); assert(pNumberOut != NULL); @@ -1267,7 +1288,7 @@ static drBool32 drflac__read_utf8_coded_number(drflac_bs* bs, uint64_t* pNumberO -static DRFLAC_INLINE drBool32 drflac__read_and_seek_rice(drflac_bs* bs, uint8_t m) +static DRFLAC_INLINE dr_bool32 drflac__read_and_seek_rice(drflac_bs* bs, uint8_t m) { unsigned int unused; if (!drflac__seek_past_next_set_bit(bs, &unused)) { @@ -1520,7 +1541,7 @@ static DRFLAC_INLINE int32_t drflac__calculate_prediction_64(uint32_t order, int // 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) \ +static dr_bool32 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); \ @@ -1630,7 +1651,7 @@ DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(drflac__decode_samples_with_res // 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) +static dr_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, uint32_t count, uint8_t riceParam) { assert(bs != NULL); assert(count > 0); @@ -1644,7 +1665,7 @@ static drBool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, uint32_t cou 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) +static dr_bool32 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); @@ -1671,7 +1692,7 @@ static drBool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, u // 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) +static dr_bool32 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); @@ -1755,7 +1776,7 @@ static drBool32 drflac__decode_samples_with_residual(drflac_bs* bs, uint32_t bit // 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) +static dr_bool32 drflac__read_and_seek_residual(drflac_bs* bs, uint32_t blockSize, uint32_t order) { assert(bs != NULL); assert(blockSize != 0); @@ -1823,7 +1844,7 @@ static drBool32 drflac__read_and_seek_residual(drflac_bs* bs, uint32_t blockSize } -static drBool32 drflac__decode_samples__constant(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples) +static dr_bool32 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; @@ -1840,7 +1861,7 @@ static drBool32 drflac__decode_samples__constant(drflac_bs* bs, uint32_t blockSi return DR_TRUE; } -static drBool32 drflac__decode_samples__verbatim(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples) +static dr_bool32 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; @@ -1854,7 +1875,7 @@ static drBool32 drflac__decode_samples__verbatim(drflac_bs* bs, uint32_t blockSi return DR_TRUE; } -static drBool32 drflac__decode_samples__fixed(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples) +static dr_bool32 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}, @@ -1882,7 +1903,7 @@ static drBool32 drflac__decode_samples__fixed(drflac_bs* bs, uint32_t blockSize, return DR_TRUE; } -static drBool32 drflac__decode_samples__lpc(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples) +static dr_bool32 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) { @@ -1925,7 +1946,7 @@ static drBool32 drflac__decode_samples__lpc(drflac_bs* bs, uint32_t blockSize, u } -static drBool32 drflac__read_next_frame_header(drflac_bs* bs, uint8_t streaminfoBitsPerSample, drflac_frame_header* header) +static dr_bool32 drflac__read_next_frame_header(drflac_bs* bs, uint8_t streaminfoBitsPerSample, drflac_frame_header* header) { assert(bs != NULL); assert(header != NULL); @@ -1983,7 +2004,7 @@ static drBool32 drflac__read_next_frame_header(drflac_bs* bs, uint8_t streaminfo } - drBool32 isVariableBlockSize = blockingStrategy == 1; + dr_bool32 isVariableBlockSize = blockingStrategy == 1; if (isVariableBlockSize) { uint64_t sampleNumber; if (!drflac__read_utf8_coded_number(bs, &sampleNumber)) { @@ -2055,7 +2076,7 @@ static drBool32 drflac__read_next_frame_header(drflac_bs* bs, uint8_t streaminfo return DR_TRUE; } -static drBool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +static dr_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) { uint8_t header; if (!drflac__read_uint8(bs, 8, &header)) { @@ -2105,7 +2126,7 @@ static drBool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSu return DR_TRUE; } -static drBool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, int32_t* pDecodedSamplesOut) +static dr_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, int32_t* pDecodedSamplesOut) { assert(bs != NULL); assert(frame != NULL); @@ -2155,7 +2176,7 @@ static drBool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int return DR_TRUE; } -static drBool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +static dr_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) { assert(bs != NULL); assert(frame != NULL); @@ -2249,7 +2270,7 @@ static DRFLAC_INLINE uint8_t drflac__get_channel_count_from_channel_assignment(i return lookup[channelAssignment]; } -static drBool32 drflac__decode_frame(drflac* pFlac) +static dr_bool32 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)); @@ -2273,7 +2294,7 @@ static drBool32 drflac__decode_frame(drflac* pFlac) return DR_TRUE; } -static drBool32 drflac__seek_frame(drflac* pFlac) +static dr_bool32 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) @@ -2287,7 +2308,7 @@ static drBool32 drflac__seek_frame(drflac* pFlac) 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) +static dr_bool32 drflac__read_and_decode_next_frame(drflac* pFlac) { assert(pFlac != NULL); @@ -2324,24 +2345,24 @@ static void drflac__get_current_frame_sample_range(drflac* pFlac, uint64_t* pFir } } -static drBool32 drflac__seek_to_first_frame(drflac* pFlac) +static dr_bool32 drflac__seek_to_first_frame(drflac* pFlac) { assert(pFlac != NULL); - drBool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); + dr_bool32 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) +static DRFLAC_INLINE dr_bool32 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) +static dr_bool32 drflac__seek_to_frame_containing_sample(drflac* pFlac, uint64_t sampleIndex) { assert(pFlac != NULL); @@ -2372,7 +2393,7 @@ static drBool32 drflac__seek_to_frame_containing_sample(drflac* pFlac, uint64_t return DR_TRUE; } -static drBool32 drflac__seek_to_sample__brute_force(drflac* pFlac, uint64_t sampleIndex) +static dr_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, uint64_t sampleIndex) { if (!drflac__seek_to_frame_containing_sample(pFlac, sampleIndex)) { return DR_FALSE; @@ -2398,7 +2419,7 @@ static drBool32 drflac__seek_to_sample__brute_force(drflac* pFlac, uint64_t samp } -static drBool32 drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t sampleIndex) +static dr_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t sampleIndex) { assert(pFlac != NULL); @@ -2508,7 +2529,7 @@ typedef struct uint64_t totalSampleCount; uint16_t maxBlockSize; uint64_t runningFilePos; - drBool32 hasMetadataBlocks; + dr_bool32 hasMetadataBlocks; #ifndef DR_FLAC_NO_OGG uint32_t oggSerial; @@ -2525,7 +2546,7 @@ static DRFLAC_INLINE void drflac__decode_block_header(uint32_t blockHeader, uint *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) +static DRFLAC_INLINE dr_bool32 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) { @@ -2536,7 +2557,7 @@ static DRFLAC_INLINE drBool32 drflac__read_and_decode_block_header(drflac_read_p return DR_TRUE; } -drBool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +dr_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) { // min/max block size. uint32_t blockSizes; @@ -2579,7 +2600,7 @@ drBool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drfla return DR_TRUE; } -drBool32 drflac__read_and_decode_metadata(drflac* pFlac) +dr_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { assert(pFlac != NULL); @@ -2823,7 +2844,7 @@ drBool32 drflac__read_and_decode_metadata(drflac* pFlac) 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) +dr_bool32 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; @@ -2869,7 +2890,7 @@ drBool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc } #ifndef DR_FLAC_NO_OGG -static DRFLAC_INLINE drBool32 drflac_ogg__is_capture_pattern(uint8_t pattern[4]) +static DRFLAC_INLINE dr_bool32 drflac_ogg__is_capture_pattern(uint8_t pattern[4]) { return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; } @@ -2889,7 +2910,7 @@ static DRFLAC_INLINE uint32_t drflac_ogg__get_page_body_size(drflac_ogg_page_hea 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) +dr_bool32 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. @@ -2920,7 +2941,7 @@ drBool32 drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onR return DR_TRUE; } -drBool32 drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, uint32_t* pHeaderSize) +dr_bool32 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) { @@ -2961,7 +2982,7 @@ static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, return bytesActuallyRead; } -static drBool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, uint64_t offset, drflac_seek_origin origin) +static dr_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, uint64_t offset, drflac_seek_origin origin) { if (origin == drflac_seek_origin_start) { @@ -3000,7 +3021,7 @@ static drBool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, uint64_t offset } } -static drBool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs) +static dr_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs) { drflac_ogg_page_header header; for (;;) @@ -3049,12 +3070,12 @@ static uint8_t drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, uint return iSeg; } -static drBool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +static dr_bool32 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; + dr_bool32 atEndOfPage = DR_FALSE; uint8_t bytesRemainingInSeg; uint8_t iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); @@ -3099,7 +3120,7 @@ static drBool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) } } -static drBool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +static dr_bool32 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. @@ -3148,7 +3169,7 @@ static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytes return bytesRead; } -static drBool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +static dr_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) { drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; assert(oggbs != NULL); @@ -3204,7 +3225,7 @@ static drBool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_ori return DR_TRUE; } -drBool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample) +dr_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample) { drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels); @@ -3327,7 +3348,7 @@ drBool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample) } -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) +dr_bool32 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. @@ -3491,7 +3512,7 @@ drBool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onR } #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) +dr_bool32 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; @@ -3610,7 +3631,7 @@ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t byt return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); } -static drBool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +static dr_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); @@ -3651,7 +3672,7 @@ static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t byt return (size_t)bytesRead; } -static drBool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +static dr_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); @@ -3727,7 +3748,7 @@ static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t by return bytesToRead; } -static drBool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +static dr_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; assert(memoryStream != NULL); @@ -4107,7 +4128,32 @@ uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferO return samplesRead; } -drBool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex) +uint64_t drflac_read_s16(drflac* pFlac, uint64_t samplesToRead, int16_t* pBufferOut) +{ + // This reads samples in 2 passes and can probably be optimized. + uint64_t samplesRead = 0; + + while (samplesToRead > 0) { + int32_t samples32[4096]; + uint64_t samplesJustRead = drflac_read_s32(pFlac, samplesToRead > 4096 ? 4096 : samplesToRead, samples32); + if (samplesJustRead == 0) { + break; // Reached the end. + } + + // s32 -> s16 + for (uint64_t i = 0; i < samplesJustRead; ++i) { + pBufferOut[i] = (int16_t)(samples32[i] >> 16); + } + + samplesRead += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; + } + + return samplesRead; +} + +dr_bool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex) { if (pFlac == NULL) { return DR_FALSE; @@ -4147,7 +4193,7 @@ drBool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex) //// High Level APIs //// -int32_t* drflac__full_decode_and_close(drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, uint64_t* totalSampleCountOut) +int32_t* drflac__full_decode_and_close_s32(drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, uint64_t* totalSampleCountOut) { assert(pFlac != NULL); @@ -4218,6 +4264,77 @@ on_error: return NULL; } +int16_t* drflac__full_decode_and_close_s16(drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, uint64_t* totalSampleCountOut) +{ + assert(pFlac != NULL); + + int16_t* pSampleData = NULL; + uint64_t totalSampleCount = pFlac->totalSampleCount; + + if (totalSampleCount == 0) + { + int16_t buffer[4096]; + + size_t sampleDataBufferSize = sizeof(buffer); + pSampleData = (int16_t*)malloc(sampleDataBufferSize); + if (pSampleData == NULL) { + goto on_error; + } + + uint64_t samplesRead; + while ((samplesRead = (uint64_t)drflac_read_s16(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) + { + if (((totalSampleCount + samplesRead) * sizeof(int16_t)) > sampleDataBufferSize) { + sampleDataBufferSize *= 2; + int16_t* pNewSampleData = (int16_t*)realloc(pSampleData, sampleDataBufferSize); + if (pNewSampleData == NULL) { + free(pSampleData); + goto on_error; + } + + pSampleData = pNewSampleData; + } + + memcpy(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(int16_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(int16_t))); + } + else + { + uint64_t dataSize = totalSampleCount * sizeof(int16_t); + if (dataSize > SIZE_MAX) { + goto on_error; // The decoded data is too big. + } + + pSampleData = (int16_t*)malloc((size_t)dataSize); // <-- Safe cast as per the check above. + if (pSampleData == NULL) { + goto on_error; + } + + uint64_t samplesDecoded = drflac_read_s16(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. @@ -4230,7 +4347,22 @@ int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc on return NULL; } - return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +int16_t* drflac_open_and_decode_s16(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_s16(pFlac, channels, sampleRate, totalSampleCount); } #ifndef DR_FLAC_NO_STDIO @@ -4245,7 +4377,21 @@ int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* cha return NULL; } - return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +int16_t* drflac_open_and_decode_file_s16(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_s16(pFlac, channels, sampleRate, totalSampleCount); } #endif @@ -4260,7 +4406,21 @@ int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, un return NULL; } - return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount); + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +int16_t* drflac_open_and_decode_memory_s16(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_s16(pFlac, channels, sampleRate, totalSampleCount); } void drflac_free(void* pSampleDataReturnedByOpenAndDecode) @@ -4305,6 +4465,15 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, ui // REVISION HISTORY // +// v0.4c - 2016-12-26 +// - Add support for signed 16-bit integer PCM decoding. +// +// v0.4b - 2016-10-23 +// - A minor change to dr_bool8 and dr_bool32 types. +// +// v0.4a - 2016-10-11 +// - Rename drBool32 to dr_bool32 for styling consistency. +// // 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() diff --git a/src/external/jar_mod.h b/src/external/jar_mod.h index bee9f6ee..c17130a6 100644 --- a/src/external/jar_mod.h +++ b/src/external/jar_mod.h @@ -83,8 +83,7 @@ #include <stdio.h> #include <stdlib.h> -#include <stdbool.h> - +//#include <stdbool.h> #ifdef __cplusplus diff --git a/src/external/stb_image.h b/src/external/stb_image.h index 5572a880..4d8d0133 100644 --- a/src/external/stb_image.h +++ b/src/external/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h +/* stb_image - v2.14 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: @@ -146,6 +146,7 @@ Latest revision history: + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 RGB-format JPEG; remove white matting in PSD; @@ -157,21 +158,6 @@ 2.07 (2015-09-13) partial animated GIF support limited 16-bit PSD support minor bugs, code cleanup, and compiler warnings - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) additional corruption checking - stbi_set_flip_vertically_on_load - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix See end of file for full revision history. @@ -186,9 +172,9 @@ Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - urraka@github (animated gif) Junggon Kim (PNM comments) + github:urraka (animated gif) Junggon Kim (PNM comments) Daniel Gibson (16-bit TGA) - + socks-the-fox (16-bit TGA) Optimizations & bugfixes Fabian "ryg" Giesen Arseny Kapoulkine @@ -199,13 +185,14 @@ Dave Moore Roy Eltham Hayaki Saito Phil Jordan Won Chun Luke Graham Johan Duparc Nathan Reed the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis - Janez Zemva John Bartholomew Michal Cichon svdijk@github + Janez Zemva John Bartholomew Michal Cichon github:svdijk Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson - Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github + Laurent Gomila Cort Stratton Sergio Gonzalez github:romigrou Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan - Ryamond Barbiero Paul Du Bois Engin Manap snagar@github - Michaelangel007@github Oriol Ferrer Mesia socks-the-fox - Blazej Dariusz Roszkowski + Ryamond Barbiero Paul Du Bois Engin Manap github:snagar + Michaelangel007@github Oriol Ferrer Mesia Dale Weiler github:Zelex + Philipp Wiesemann Josh Tobin github:rlyeh github:grim210@github + Blazej Dariusz Roszkowski github:sammyhw LICENSE @@ -238,10 +225,10 @@ publish, and distribute this file as you see fit. // stbi_image_free(data) // // Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is @@ -389,13 +376,13 @@ publish, and distribute this file as you see fit. // -#define STBI_NO_HDR // RaySan: not required by raylib -//#define STBI_NO_SIMD // RaySan: issues when compiling with GCC 4.7.2 - #ifndef STBI_NO_STDIO #include <stdio.h> #endif // STBI_NO_STDIO +#define STBI_NO_HDR // RaySan: not required by raylib +//#define STBI_NO_SIMD // RaySan: issues when compiling with GCC 4.7.2 + // NOTE: Added to work with raylib on Android #if defined(PLATFORM_ANDROID) #include "utils.h" // RaySan: Android fopen function map @@ -414,6 +401,7 @@ enum }; typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { @@ -441,22 +429,42 @@ typedef struct int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif +// @TODO the other variants + +//////////////////////////////////// +// +// float-per-channel interface +// #ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif @@ -574,6 +582,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include <stddef.h> // ptrdiff_t on osx #include <stdlib.h> #include <string.h> +#include <limits.h> #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) #include <math.h> // ldexp @@ -835,57 +844,70 @@ static void stbi__rewind(stbi__context *s) s->img_buffer_end = s->img_buffer_original_end; } +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif @@ -908,6 +930,77 @@ static void *stbi__malloc(size_t size) return STBI_MALLOC(size); } +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -943,33 +1036,38 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) stbi__vertically_flip_on_load = flag_true_if_should_flip; } -static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #endif #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif @@ -977,35 +1075,117 @@ static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *com #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); + return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } -static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { - unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + int i; + int img_len = w * h * channels; + stbi_uc *reduced; - if (stbi__vertically_flip_on_load && result != NULL) { + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; + int channels = req_comp ? req_comp : *comp; int row,col,z; - stbi_uc temp; + stbi_uc *image = (stbi_uc *) result; // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once for (row = 0; row < (h>>1); row++) { for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; + for (z = 0; z < channels; z++) { + stbi_uc temp = image[(row * w + col) * channels + z]; + image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; + image[((h - row - 1) * w + col) * channels + z] = temp; } } } } - return result; + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int w = *x, h = *y; + int channels = req_comp ? req_comp : *comp; + int row,col,z; + stbi__uint16 *image = (stbi__uint16 *) result; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < channels; z++) { + stbi__uint16 temp = image[(row * w + col) * channels + z]; + image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; + image[((h - row - 1) * w + col) * channels + z] = temp; + } + } + } + } + + return (stbi__uint16 *) result; } #ifndef STBI_NO_HDR @@ -1061,27 +1241,52 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req unsigned char *result; stbi__context s; stbi__start_file(&s,f); - result = stbi__load_flip(&s,x,y,comp,req_comp); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + #endif //!STBI_NO_STDIO STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_flip(&s,x,y,comp,req_comp); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } #ifndef STBI_NO_LINEAR @@ -1090,13 +1295,14 @@ static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif - data = stbi__load_flip(s, x, y, comp, req_comp); + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); @@ -1354,7 +1560,7 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (unsigned char *) stbi__malloc(req_comp * x * y); + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); @@ -1364,26 +1570,75 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) dest[0]=src[0], dest[1]=255; break; - CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; - CASE(2,1) dest[0]=src[0]; break; - CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; - CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; - CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } - #undef CASE + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE } STBI_FREE(data); @@ -1394,7 +1649,9 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -1414,7 +1671,9 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; @@ -2709,6 +2968,28 @@ static int stbi__process_scan_header(stbi__jpeg *z) return 1; } +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; @@ -2746,7 +3027,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (scan != STBI__SCAN_load) return 1; - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; @@ -2758,6 +3039,7 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; @@ -2769,28 +3051,27 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; } } @@ -3296,23 +3577,7 @@ static void stbi__setup_jpeg(stbi__jpeg *j) // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } + stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct @@ -3376,7 +3641,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample @@ -3432,7 +3697,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } } -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); @@ -3729,6 +3994,7 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { @@ -3738,27 +4004,29 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; - while (n < hlit + hdist) { + while (n < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); n += c; } } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; @@ -4024,7 +4292,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r int width = x; STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * output_bytes); // extra bytes to write off the end into + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); @@ -4089,37 +4357,37 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { int nk = (width - 1)*filter_bytes; - #define CASE(f) \ + #define STBI__CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } - #undef CASE + #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ + #define STBI__CASE(f) \ case f: \ for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ for (k=0; k < filter_bytes; ++k) switch (filter) { - CASE(STBI__F_none) cur[k] = raw[k]; break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); break; + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; } - #undef CASE + #undef STBI__CASE // the loop above sets the high byte of the pixels' alpha, but for // 16 bit png files we also need the low byte set. we'll do that here. @@ -4222,13 +4490,15 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4248,8 +4518,8 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); } } STBI_FREE(a->out); @@ -4317,7 +4587,7 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak @@ -4349,26 +4619,6 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int return 1; } -static int stbi__reduce_png(stbi__png *p) -{ - int i; - int img_len = p->s->img_x * p->s->img_y * p->s->img_out_n; - stbi_uc *reduced; - stbi__uint16 *orig = (stbi__uint16*)p->out; - - if (p->depth != 16) return 1; // don't need to do anything if not 16-bit data - - reduced = (stbi_uc *)stbi__malloc(img_len); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is a decent approx of 16->8 bit scaling - - p->out = reduced; - STBI_FREE(orig); - - return 1; -} - static int stbi__unpremultiply_on_load = 0; static int stbi__de_iphone_flag = 0; @@ -4508,7 +4758,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = stbi__get16be(s); // copy the values as-is + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } @@ -4595,20 +4845,22 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) } } -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { - unsigned char *result=NULL; + void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth == 16) { - if (!stbi__reduce_png(p)) { - return result; - } - } + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } @@ -4623,11 +4875,11 @@ static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req return result; } -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); + return stbi__do_png(&p, x,y,comp,req_comp, ri); } static int stbi__png_test(stbi__context *s) @@ -4815,7 +5067,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, all_a; @@ -4823,6 +5075,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int int psize=0,i,j,width; int flip_vertically, pad, target; stbi__bmp_data info; + STBI_NOTUSED(ri); info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) @@ -4851,7 +5104,11 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int else target = s->img_n; // if they want monochrome, we'll post-convert - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.bpp < 16) { int z=0; @@ -5085,18 +5342,18 @@ errorEnd: } // read 16bit value and convert to 24bit RGB -void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) { - stbi__uint16 px = stbi__get16le(s); + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); stbi__uint16 fiveBitMask = 31; // we have 3 channels with 5bits each int r = (px >> 10) & fiveBitMask; int g = (px >> 5) & fiveBitMask; int b = px & fiveBitMask; // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (r * 255)/31; - out[1] = (g * 255)/31; - out[2] = (b * 255)/31; + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); // some people claim that the most significant bit might be used for alpha // (possibly if an alpha-bit is set in the "image descriptor byte") @@ -5104,7 +5361,7 @@ void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // read in the TGA header stuff int tga_offset = stbi__get8(s); @@ -5126,10 +5383,11 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; - unsigned char raw_data[4]; + unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; + STBI_NOTUSED(ri); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5151,7 +5409,10 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int *y = tga_height; if (comp) *comp = tga_comp; - tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) @@ -5170,7 +5431,7 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); @@ -5306,14 +5567,53 @@ static int stbi__psd_test(stbi__context *s) return r; } -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { - int pixelCount; + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; int channelCount, compression; - int channel, i, count, len; + int channel, i; int bitdepth; int w,h; stbi_uc *out; + STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" @@ -5370,8 +5670,18 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; @@ -5403,82 +5713,86 @@ static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; if (channel >= channelCount) { // Fill this channel with default data. - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } else { - // Read the data. - if (bitdepth == 16) { - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } } } } } + // remove weird white matte from PSD if (channelCount >= 4) { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - // remove weird white matte from PSD - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } } } } + // convert to desired output format if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } @@ -5662,10 +5976,11 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c return result; } -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; int i, x,y; + STBI_NOTUSED(ri); for (i=0; i<92; ++i) stbi__get8(s); @@ -5673,14 +5988,14 @@ static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int re x = stbi__get16be(s); y = stbi__get16be(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -5939,8 +6254,11 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) + return stbi__errpuc("too large", "GIF too large"); + prev_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); switch ((g->eflags & 0x1C) >> 2) { @@ -6047,11 +6365,12 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i STBI_NOTUSED(req_comp); } -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); memset(g, 0, sizeof(*g)); + STBI_NOTUSED(ri); u = stbi__gif_load_next(s, g, comp, req_comp); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6077,20 +6396,24 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) +static int stbi__hdr_test_core(stbi__context *s, const char *signature) { - const char *signature = "#?RADIANCE\n"; int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) - return 0; + return 0; + stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { - int r = stbi__hdr_test_core(s); + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } return r; } @@ -6144,7 +6467,7 @@ static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) } } -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { char buffer[STBI__HDR_BUFLEN]; char *token; @@ -6155,10 +6478,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re int len; unsigned char count, value; int i, j, k, c1,c2, z; - + const char *headerToken; + STBI_NOTUSED(ri); // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header @@ -6187,8 +6512,13 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca @@ -6227,20 +6557,29 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } for (k = 0; k < 4; ++k) { + int nleft; i = 0; - while (i < width) { + while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -6249,7 +6588,8 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } - STBI_FREE(scanline); + if (scanline) + STBI_FREE(scanline); } return hdr_data; @@ -6427,16 +6767,22 @@ static int stbi__pnm_test(stbi__context *s) return 1; } -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; + STBI_NOTUSED(ri); + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + *x = s->img_x; *y = s->img_y; *comp = s->img_n; - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); @@ -6601,6 +6947,7 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int /* revision history: + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) allocate large structures on the stack remove white matting for transparent PSD diff --git a/src/external/stb_image_resize.h b/src/external/stb_image_resize.h index 4cabe540..5214ad6e 100644 --- a/src/external/stb_image_resize.h +++ b/src/external/stb_image_resize.h @@ -1,4 +1,4 @@ -/* stb_image_resize - v0.91 - public domain image resizing +/* stb_image_resize - v0.92 - public domain image resizing by Jorge L Rodriguez (@VinoBS) - 2014 http://github.com/nothings/stb @@ -154,8 +154,10 @@ ADDITIONAL CONTRIBUTORS Sean Barrett: API design, optimizations + Aras Pranckevicius: bugfix REVISIONS + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions 0.90 (2014-09-17) first released version @@ -1239,11 +1241,11 @@ static void stbir__decode_scanline(stbir__info* stbir_info, int n) int type = stbir_info->type; int colorspace = stbir_info->colorspace; int input_w = stbir_info->input_w; - int input_stride_bytes = stbir_info->input_stride_bytes; + size_t input_stride_bytes = stbir_info->input_stride_bytes; float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir_edge edge_horizontal = stbir_info->edge_horizontal; stbir_edge edge_vertical = stbir_info->edge_vertical; - int in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; int decode = STBIR__DECODE(type, colorspace); diff --git a/src/external/stb_image_write.h b/src/external/stb_image_write.h index 4319c0de..ae9180b0 100644 --- a/src/external/stb_image_write.h +++ b/src/external/stb_image_write.h @@ -1,4 +1,4 @@ -/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v1.03 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk @@ -103,6 +103,7 @@ CREDITS: Jonas Karlsson Filip Wasil Thatcher Ulrich + github:poppolopoppo LICENSE @@ -475,7 +476,6 @@ int stbi_write_tga(char const *filename, int x, int y, int comp, const void *dat // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson -#ifndef STBI_WRITE_NO_STDIO #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) @@ -630,6 +630,7 @@ int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, i return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } +#ifndef STBI_WRITE_NO_STDIO int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s; diff --git a/src/external/stb_rect_pack.h b/src/external/stb_rect_pack.h index bd1cfc60..c75527da 100644 --- a/src/external/stb_rect_pack.h +++ b/src/external/stb_rect_pack.h @@ -1,4 +1,4 @@ -// stb_rect_pack.h - v0.08 - public domain - rectangle packing +// stb_rect_pack.h - v0.10 - public domain - rectangle packing // Sean Barrett 2014 // // Useful for e.g. packing rectangular textures into an atlas. @@ -32,6 +32,8 @@ // // Version history: // +// 0.10 (2016-10-25) remove cast-away-const to avoid warnings +// 0.09 (2016-08-27) fix compiler warnings // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort @@ -148,7 +150,7 @@ enum { STBRP_HEURISTIC_Skyline_default=0, STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, - STBRP_HEURISTIC_Skyline_BF_sortHeight, + STBRP_HEURISTIC_Skyline_BF_sortHeight }; @@ -198,9 +200,15 @@ struct stbrp_context #define STBRP_ASSERT assert #endif +#ifdef _MSC_VER +#define STBRP__NOTUSED(v) (void)(v) +#else +#define STBRP__NOTUSED(v) (void)sizeof(v) +#endif + enum { - STBRP__INIT_skyline = 1, + STBRP__INIT_skyline = 1 }; STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) @@ -273,6 +281,9 @@ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0 stbrp_node *node = first; int x1 = x0 + width; int min_y, visited_width, waste_area; + + STBRP__NOTUSED(c); + STBRP_ASSERT(first->x <= x0); #if 0 @@ -500,8 +511,8 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i static int rect_height_compare(const void *a, const void *b) { - stbrp_rect *p = (stbrp_rect *) a; - stbrp_rect *q = (stbrp_rect *) b; + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; if (p->h > q->h) return -1; if (p->h < q->h) @@ -511,8 +522,8 @@ static int rect_height_compare(const void *a, const void *b) static int rect_width_compare(const void *a, const void *b) { - stbrp_rect *p = (stbrp_rect *) a; - stbrp_rect *q = (stbrp_rect *) b; + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; if (p->w > q->w) return -1; if (p->w < q->w) @@ -522,8 +533,8 @@ static int rect_width_compare(const void *a, const void *b) static int rect_original_order(const void *a, const void *b) { - stbrp_rect *p = (stbrp_rect *) a; - stbrp_rect *q = (stbrp_rect *) b; + const stbrp_rect *p = (const stbrp_rect *) a; + const stbrp_rect *q = (const stbrp_rect *) b; return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); } diff --git a/src/external/stb_truetype.h b/src/external/stb_truetype.h index d360d609..92b9a875 100644 --- a/src/external/stb_truetype.h +++ b/src/external/stb_truetype.h @@ -1,5 +1,5 @@ -// stb_truetype.h - v1.11 - public domain -// authored from 2009-2015 by Sean Barrett / RAD Game Tools +// stb_truetype.h - v1.14 - public domain +// authored from 2009-2016 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: // parse files @@ -20,10 +20,12 @@ // // Mikko Mononen: compound shape support, more cmap formats // Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling // // Misc other: // Ryan Gordon // Simon Glass +// github:IntellectualKitty // // Bug/warning reports/fixes: // "Zer" on mollyrocket (with fix) @@ -51,6 +53,8 @@ // // VERSION HISTORY // +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts, num-fonts-in-TTC function +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual // 1.11 (2016-04-02) fix unused-variable warning // 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly @@ -60,12 +64,6 @@ // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); // fixed an assert() bug in the new rasterizer // replace assert() with STBTT_assert() in new rasterizer -// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) -// also more precise AA rasterizer, except if shapes overlap -// remove need for STBTT_sort -// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC -// 1.04 (2015-04-15) typo in example -// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes // // Full history can be found at the end of this file. // @@ -100,7 +98,8 @@ // // "Load" a font file from a memory buffer (you have to keep the buffer loaded) // stbtt_InitFont() -// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections // // Render a unicode codepoint to a bitmap // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap @@ -458,6 +457,14 @@ int main(int arg, char **argv) extern "C" { #endif +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + ////////////////////////////////////////////////////////////////////////////// // // TEXTURE BAKING API @@ -527,7 +534,7 @@ typedef struct stbrp_rect stbrp_rect; STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); // Initializes a packing context stored in the passed-in stbtt_pack_context. // Future calls using this context will pack characters into the bitmap passed -// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is // the distance from one row to the next (or 0 to mean they are packed tightly // together). "padding" is the amount of padding to leave between each // character (normally you want '1' for bitmaps you'll use as textures with @@ -593,9 +600,9 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, stbtt_aligned_quad *q, // output: quad to draw int align_to_integer); -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); // Calling these functions in sequence is roughly equivalent to calling // stbtt_PackFontRanges(). If you more control over the packing of multiple // fonts, or if you want to pack custom data into a font texture, take a look @@ -626,14 +633,19 @@ struct stbtt_pack_context { // // +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); // Each .ttf/.ttc file may have more than one font. Each font has a sequential // index number starting from 0. Call this function to get the font offset for // a given index; it returns -1 if the index is out of range. A regular .ttf // file will only define one font and it always be at offset 0, so it will -// return '0' for index 0, and -1 for all other indices. You can just skip -// this step if you know it's that kind of font. - +// return '0' for index 0, and -1 for all other indices. // The following structure is defined publically so you can declare one on // the stack or as a global or etc, but you should treat it as opaque. @@ -648,6 +660,13 @@ struct stbtt_fontinfo int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict }; STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); @@ -725,7 +744,8 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in enum { STBTT_vmove=1, STBTT_vline, - STBTT_vcurve + STBTT_vcurve, + STBTT_vcubic }; #endif @@ -734,7 +754,7 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file typedef struct { - stbtt_vertex_type x,y,cx,cy; + stbtt_vertex_type x,y,cx,cy,cx1,cy1; unsigned char type,padding; } stbtt_vertex; #endif @@ -958,6 +978,152 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS ////////////////////////////////////////////////////////////////////////// // +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// // accessors to parse data from file // @@ -968,32 +1134,22 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS #define ttCHAR(p) (* (stbtt_int8 *) (p)) #define ttFixed(p) ttLONG(p) -#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) - - #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) - #define ttSHORT(p) (* (stbtt_int16 *) (p)) - #define ttULONG(p) (* (stbtt_uint32 *) (p)) - #define ttLONG(p) (* (stbtt_int32 *) (p)) - -#else - - static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#endif +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } #define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) #define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) -static int stbtt__isfont(const stbtt_uint8 *font) +static int stbtt__isfont(stbtt_uint8 *font) { // check the version number if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts return 0; } @@ -1011,7 +1167,7 @@ static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, return 0; } -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) { // if it's just a font, there's only one valid index if (stbtt__isfont(font_collection)) @@ -1030,14 +1186,43 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, return -1; } -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) { - stbtt_uint8 *data = (stbtt_uint8 *) data2; stbtt_uint32 cmap, t; stbtt_int32 i,numTables; info->data = data; info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); cmap = stbtt__find_table(data, fontstart, "cmap"); // required info->loca = stbtt__find_table(data, fontstart, "loca"); // required @@ -1046,8 +1231,61 @@ STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, i info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required info->kern = stbtt__find_table(data, fontstart, "kern"); // not required - if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + + if (!cmap || !info->head || !info->hhea || !info->hmtx) return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } t = stbtt__find_table(data, fontstart, "maxp"); if (t) @@ -1198,6 +1436,8 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) { int g1,g2; + STBTT_assert(!info->cff.size); + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format @@ -1212,15 +1452,21 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) return g1==g2 ? -1 : g1; // if length is 0, return -1 } +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) { - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; - if (x0) *x0 = ttSHORT(info->data + g + 2); - if (y0) *y0 = ttSHORT(info->data + g + 4); - if (x1) *x1 = ttSHORT(info->data + g + 6); - if (y1) *y1 = ttSHORT(info->data + g + 8); + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } return 1; } @@ -1232,7 +1478,10 @@ STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, i STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) { stbtt_int16 numberOfContours; - int g = stbtt__GetGlyfOffset(info, glyph_index); + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); if (g < 0) return 1; numberOfContours = ttSHORT(info->data + g); return numberOfContours == 0; @@ -1254,7 +1503,7 @@ static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_ return num_vertices; } -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { stbtt_int16 numberOfContours; stbtt_uint8 *endPtsOfContours; @@ -1480,6 +1729,416 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s return num_vertices; } +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // fallthrough + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) { + *x0 = r ? c.min_x : 0; + *y0 = r ? c.min_y : 0; + *x1 = r ? c.max_x : 0; + *y1 = r ? c.max_y : 0; + } + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) { stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); @@ -2350,6 +3009,48 @@ static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x return 1; } +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + // returns number of contours static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) { @@ -2406,6 +3107,14 @@ static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, objspace_flatness_squared, 0); x = vertices[i].x, y = vertices[i].y; break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; } } (*contour_lengths)[n] = num_points - start; @@ -2532,7 +3241,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned ch // // This is SUPER-CRAPPY packing to keep source code small -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) float pixel_height, // height of font in pixels unsigned char *pixels, int pw, int ph, // bitmap to be filled in int first_char, int num_chars, // characters to bake @@ -2862,7 +3571,7 @@ static float stbtt__oversample_shift(int oversample) } // rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; @@ -2891,7 +3600,7 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fon } // rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k, return_value = 1; @@ -3060,7 +3769,7 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i // // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; @@ -3099,9 +3808,9 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 return i; } -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); } // returns results in whatever encoding you request... but note that 2-byte encodings @@ -3157,7 +3866,7 @@ static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, return 1; } else if (matchlen < nlen && name[matchlen] == ' ') { ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) return 1; } } else { @@ -3203,7 +3912,7 @@ static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *nam return 0; } -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) { stbtt_int32 i; for (i=0;;++i) { @@ -3214,11 +3923,53 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const } } +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + #endif // STB_TRUETYPE_IMPLEMENTATION // FULL VERSION HISTORY // +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual // 1.11 (2016-04-02) fix unused-variable warning // 1.10 (2016-04-02) allow user-defined fabs() replacement // fix memory leak if fontsize=0.0 diff --git a/src/gestures.h b/src/gestures.h index 481ef317..99f49d2a 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -2,6 +2,10 @@ * * raylib Gestures System - Gestures Processing based on input gesture events (touch/mouse) * +* NOTE: Memory footprint of this library is aproximately 128 bytes (global variables) +* +* CONFIGURATION: +* * #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 @@ -11,11 +15,16 @@ * 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 +* CONTRIBUTORS: +* Marc Palau: Initial implementation (2014) +* Albert Martos: Complete redesign and testing (2015) +* Ian Eito: Complete redesign and testing (2015) +* Ramon Santamaria: Supervision, review, update and maintenance +* * -* Initial design by Marc Palau (2014) -* Redesigned by Albert Martos and Ian Eito (2015) -* Reviewed by Ramon Santamaria (2015-2016) +* LICENSE: zlib/libpng +* +* 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. @@ -65,17 +74,17 @@ // 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 + GESTURE_NONE = 0, + GESTURE_TAP = 1, + GESTURE_DOUBLETAP = 2, + GESTURE_HOLD = 4, + GESTURE_DRAG = 8, + GESTURE_SWIPE_RIGHT = 16, + GESTURE_SWIPE_LEFT = 32, + GESTURE_SWIPE_UP = 64, + GESTURE_SWIPE_DOWN = 128, + GESTURE_PINCH_IN = 256, + GESTURE_PINCH_OUT = 512 } Gestures; #endif @@ -179,7 +188,7 @@ static int tapCounter = 0; // TAP counter (one tap implies // Hold gesture variables static bool resetHold = false; // HOLD reset to get first touch point again -static float timeHold = 0.0f; // HOLD duration in milliseconds +static double timeHold = 0.0f; // HOLD duration in milliseconds // Drag gesture variables static Vector2 dragVector = { 0.0f , 0.0f }; // DRAG vector (between initial and current position) @@ -423,11 +432,11 @@ float GetGestureHoldDuration(void) { // NOTE: time is calculated on current gesture HOLD - float time = 0.0f; + double time = 0.0; - if (currentGesture == GESTURE_HOLD) time = (float)GetCurrentTime() - timeHold; + if (currentGesture == GESTURE_HOLD) time = GetCurrentTime() - timeHold; - return time; + return (float)time; } // Get drag vector (between initial touch point to current) @@ -474,7 +483,7 @@ static float Vector2Angle(Vector2 initialPosition, Vector2 finalPosition) { float angle; - angle = atan2(finalPosition.y - initialPosition.y, finalPosition.x - initialPosition.x)*(180.0f/PI); + angle = atan2f(finalPosition.y - initialPosition.y, finalPosition.x - initialPosition.x)*(180.0f/PI); if (angle < 0) angle += 360.0f; @@ -489,7 +498,7 @@ static float Vector2Distance(Vector2 v1, Vector2 v2) float dx = v2.x - v1.x; float dy = v2.y - v1.y; - result = sqrt(dx*dx + dy*dy); + result = (float)sqrt(dx*dx + dy*dy); return result; } diff --git a/src/models.c b/src/models.c index 48f8b813..bef19e10 100644 --- a/src/models.c +++ b/src/models.c @@ -1,14 +1,15 @@ /********************************************************************************************** * -* raylib.models +* raylib.models - Basic functions to draw 3d shapes and 3d models * -* Basic functions to draw 3d shapes and load/draw 3d models (.OBJ) +* CONFIGURATION: * -* External libs: -* rlgl - raylib OpenGL abstraction layer +* #define SUPPORT_FILEFORMAT_OBJ / SUPPORT_LOAD_OBJ * -* Module Configuration Flags: -* ... +* #define SUPPORT_FILEFORMAT_MTL +* +* +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -574,164 +575,71 @@ void DrawGizmo(Vector3 position) rlPopMatrix(); } - -// Draw light in 3D world -void DrawLight(Light light) +// Load mesh from file +Mesh LoadMesh(const char *fileName) { - switch (light->type) - { - case LIGHT_POINT: - { - DrawSphereWires(light->position, 0.3f*light->intensity, 8, 8, (light->enabled ? light->diffuse : GRAY)); - - DrawCircle3D(light->position, light->radius, (Vector3){ 0, 0, 0 }, 0.0f, (light->enabled ? light->diffuse : GRAY)); - DrawCircle3D(light->position, light->radius, (Vector3){ 1, 0, 0 }, 90.0f, (light->enabled ? light->diffuse : GRAY)); - DrawCircle3D(light->position, light->radius, (Vector3){ 0, 1, 0 },90.0f, (light->enabled ? light->diffuse : GRAY)); - } break; - case LIGHT_DIRECTIONAL: - { - 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: - { - DrawLine3D(light->position, light->target, (light->enabled ? light->diffuse : GRAY)); - - Vector3 dir = VectorSubtract(light->target, light->position); - VectorNormalize(&dir); - - DrawCircle3D(light->position, 0.5f, dir, 0.0f, (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; - } -} + Mesh mesh = { 0 }; -// Load a 3d model (from file) -Model LoadModel(const char *fileName) -{ - Model model = { 0 }; + if (strcmp(GetExtension(fileName), "obj") == 0) mesh = LoadOBJ(fileName); + else TraceLog(WARNING, "[%s] Mesh extension not recognized, it can't be loaded", fileName); - // TODO: Initialize default data for model in case loading fails, maybe a cube? + if (mesh.vertexCount == 0) TraceLog(WARNING, "Mesh could not be loaded"); + else rlglLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) - if (strcmp(GetExtension(fileName), "obj") == 0) model.mesh = LoadOBJ(fileName); - else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName); + // TODO: Initialize default mesh data in case loading fails, maybe a cube? - if (model.mesh.vertexCount == 0) TraceLog(WARNING, "Model could not be loaded"); - else - { - rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model) + return mesh; +} - model.transform = MatrixIdentity(); - model.material = LoadDefaultMaterial(); - } +// Load mesh from vertex data +// NOTE: All vertex data arrays must be same size: numVertex +Mesh LoadMeshEx(int numVertex, float *vData, float *vtData, float *vnData, Color *cData) +{ + Mesh mesh = { 0 }; - return model; + mesh.vertexCount = numVertex; + mesh.triangleCount = numVertex/3; + mesh.vertices = vData; + mesh.texcoords = vtData; + mesh.texcoords2 = NULL; + mesh.normals = vnData; + mesh.tangents = NULL; + mesh.colors = (unsigned char *)cData; + mesh.indices = NULL; + + rlglLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) + + return mesh; } -// Load a 3d model (from vertex data) -Model LoadModelEx(Mesh data, bool dynamic) +// Load model from file +Model LoadModel(const char *fileName) { Model model = { 0 }; - model.mesh = data; - - rlglLoadMesh(&model.mesh, dynamic); // Upload vertex data to GPU - + model.mesh = LoadMesh(fileName); model.transform = MatrixIdentity(); model.material = LoadDefaultMaterial(); return model; } -// Load a 3d model from rRES file (raylib Resource) -Model LoadModelFromRES(const char *rresName, int resId) +// Load model from mesh data +Model LoadModelFromMesh(Mesh data, bool dynamic) { Model model = { 0 }; - bool found = false; - - char id[4]; // rRES file identifier - unsigned char version; // rRES file version and subversion - char useless; // rRES header reserved data - short numRes; - - ResInfoHeader infoHeader; - FILE *rresFile = fopen(rresName, "rb"); - - if (rresFile == NULL) - { - TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); - } - else - { - // Read rres file (basic file check - id) - fread(&id[0], sizeof(char), 1, rresFile); - fread(&id[1], sizeof(char), 1, rresFile); - fread(&id[2], sizeof(char), 1, rresFile); - fread(&id[3], sizeof(char), 1, rresFile); - fread(&version, sizeof(char), 1, rresFile); - fread(&useless, sizeof(char), 1, rresFile); - - if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) - { - TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); - } - else - { - // Read number of resources embedded - fread(&numRes, sizeof(short), 1, rresFile); - - for (int i = 0; i < numRes; i++) - { - fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); - - if (infoHeader.id == resId) - { - found = true; - - // Check data is of valid MODEL type - if (infoHeader.type == 8) - { - // TODO: Load model data - } - else - { - TraceLog(WARNING, "[%s] Required resource do not seem to be a valid MODEL resource", rresName); - } - } - else - { - // Depending on type, skip the right amount of parameters - switch (infoHeader.type) - { - case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters - case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters - case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) - case 3: break; // TEXT: No parameters - case 4: break; // RAW: No parameters - default: break; - } - - // Jump DATA to read next infoHeader - fseek(rresFile, infoHeader.size, SEEK_CUR); - } - } - } + model.mesh = data; - fclose(rresFile); - } + rlglLoadMesh(&model.mesh, dynamic); // Upload vertex data to GPU - if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); + model.transform = MatrixIdentity(); + model.material = LoadDefaultMaterial(); return model; } -// Load a heightmap image as a 3d model +// Load heightmap model from image data // NOTE: model map size is defined in generic units Model LoadHeightmap(Image heightmap, Vector3 size) { @@ -747,7 +655,7 @@ Model LoadHeightmap(Image heightmap, Vector3 size) return model; } -// Load a map image as a 3d model (cubes based) +// Load cubes-based map model from image data Model LoadCubicmap(Image cubicmap) { Model model = { 0 }; @@ -762,14 +670,19 @@ Model LoadCubicmap(Image cubicmap) return model; } -// Unload 3d model from memory (mesh and material) -void UnloadModel(Model model) +// Unload mesh from memory (RAM and/or VRAM) +void UnloadMesh(Mesh *mesh) { - rlglUnloadMesh(&model.mesh); + rlglUnloadMesh(mesh); +} +// Unload model from memory (RAM and/or VRAM) +void UnloadModel(Model model) +{ + UnloadMesh(&model.mesh); UnloadMaterial(model.material); - TraceLog(INFO, "Unloaded model data from RAM and VRAM"); + TraceLog(INFO, "Unloaded model data (mesh and material) from RAM and VRAM"); } // Load material data (from file) @@ -802,17 +715,6 @@ Material LoadDefaultMaterial(void) return material; } -// Load standard material (uses material attributes and lighting shader) -// NOTE: Standard shader supports multiple maps and lights -Material LoadStandardMaterial(void) -{ - Material material = LoadDefaultMaterial(); - - material.shader = GetStandardShader(); - - return material; -} - // Unload material from memory void UnloadMaterial(Material material) { @@ -1517,14 +1419,143 @@ bool CheckCollisionRayBox(Ray ray, BoundingBox box) t[3] = (box.max.y - ray.position.y)/ray.direction.y; t[4] = (box.min.z - ray.position.z)/ray.direction.z; t[5] = (box.max.z - ray.position.z)/ray.direction.z; - t[6] = fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5])); - t[7] = fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5])); + t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5])); + t[7] = (float)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; } +// Get collision info between ray and mesh +RayHitInfo GetCollisionRayMesh(Ray ray, Mesh *mesh) +{ + RayHitInfo result = { 0 }; + + // If mesh doesn't have vertex data on CPU, can't test it. + if (!mesh->vertices) return result; + + // mesh->triangleCount may not be set, vertexCount is more reliable + int triangleCount = mesh->vertexCount/3; + + // Test against all triangles in mesh + for (int i = 0; i < triangleCount; i++) + { + Vector3 a, b, c; + Vector3 *vertdata = (Vector3 *)mesh->vertices; + + if (mesh->indices) + { + a = vertdata[mesh->indices[i*3 + 0]]; + b = vertdata[mesh->indices[i*3 + 1]]; + c = vertdata[mesh->indices[i*3 + 2]]; + } + else + { + a = vertdata[i*3 + 0]; + b = vertdata[i*3 + 1]; + c = vertdata[i*3 + 2]; + } + + RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); + + if (triHitInfo.hit) + { + // Save the closest hit triangle + if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo; + } + } + + return result; +} + +// Get collision info between ray and triangle +// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm +RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) +{ + #define EPSILON 0.000001 // A small number + + Vector3 edge1, edge2; + Vector3 p, q, tv; + float det, invDet, u, v, t; + RayHitInfo result = {0}; + + // Find vectors for two edges sharing V1 + edge1 = VectorSubtract(p2, p1); + edge2 = VectorSubtract(p3, p1); + + // Begin calculating determinant - also used to calculate u parameter + p = VectorCrossProduct(ray.direction, edge2); + + // If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle + det = VectorDotProduct(edge1, p); + + // Avoid culling! + if ((det > -EPSILON) && (det < EPSILON)) return result; + + invDet = 1.0f/det; + + // Calculate distance from V1 to ray origin + tv = VectorSubtract(ray.position, p1); + + // Calculate u parameter and test bound + u = VectorDotProduct(tv, p)*invDet; + + // The intersection lies outside of the triangle + if ((u < 0.0f) || (u > 1.0f)) return result; + + // Prepare to test v parameter + q = VectorCrossProduct(tv, edge1); + + // Calculate V parameter and test bound + v = VectorDotProduct(ray.direction, q)*invDet; + + // The intersection lies outside of the triangle + if ((v < 0.0f) || ((u + v) > 1.0f)) return result; + + t = VectorDotProduct(edge2, q)*invDet; + + if (t > EPSILON) + { + // Ray hit, get hit point and normal + result.hit = true; + result.distance = t; + result.hit = true; + result.hitNormal = VectorCrossProduct(edge1, edge2); + VectorNormalize(&result.hitNormal); + Vector3 rayDir = ray.direction; + VectorScale(&rayDir, t); + result.hitPosition = VectorAdd(ray.position, rayDir); + } + + return result; +} + +// Get collision info between ray and ground plane (Y-normal plane) +RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) +{ + #define EPSILON 0.000001 // A small number + + RayHitInfo result = { 0 }; + + if (fabsf(ray.direction.y) > EPSILON) + { + float t = (ray.position.y - groundHeight)/-ray.direction.y; + + if (t >= 0.0) + { + Vector3 rayDir = ray.direction; + VectorScale(&rayDir, t); + result.hit = true; + result.distance = t; + result.hitNormal = (Vector3){ 0.0, 1.0, 0.0 }; + result.hitPosition = VectorAdd(ray.position, rayDir); + } + } + + return result; +} + // Calculate mesh bounding box limits // NOTE: minVertex and maxVertex should be transformed by model transform matrix (position, scale, rotate) BoundingBox CalculateBoundingBox(Mesh mesh) diff --git a/src/physac.h b/src/physac.h index e807ffa6..cb0e3f3c 100644 --- a/src/physac.h +++ b/src/physac.h @@ -1,11 +1,13 @@ /********************************************************************************************** * -* Physac - 2D Physics library for videogames +* Physac v1.0 - 2D Physics library for videogames * -* Description: Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop -* to simluate physics. A physics step contains the following phases: get collision information, apply dynamics, -* collision solving and position correction. It uses a very simple struct for physic bodies with a position vector -* to be used in any 3D rendering API. +* DESCRIPTION: +* +* Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop +* to simluate physics. A physics step contains the following phases: get collision information, +* apply dynamics, collision solving and position correction. It uses a very simple struct for physic +* bodies with a position vector to be used in any 3D rendering API. * * CONFIGURATION: * @@ -37,7 +39,8 @@ * Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. * * VERY THANKS TO: -* - Ramón Santamaria (@raysan5) +* Ramón Santamaria (@raysan5) +* * * LICENSE: zlib/libpng * @@ -239,9 +242,10 @@ PHYSACDEF void ClosePhysics(void); // 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) +#elif defined(__linux) || defined(PLATFORM_WEB) #include <sys/time.h> // Required for: timespec #include <time.h> // Required for: clock_gettime() + #include <stdint.h> #endif //---------------------------------------------------------------------------------- @@ -266,7 +270,7 @@ PHYSACDEF void ClosePhysics(void); static unsigned int usedMemory = 0; // Total allocated dynamic memory static bool physicsThreadEnabled = false; // Physics thread enabled state static double currentTime = 0; // Current time in milliseconds -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(__linux) || defined(PLATFORM_WEB) static double baseTime = 0; // Android and RPI platforms base time #endif static double startTime = 0; // Start time in milliseconds @@ -1942,7 +1946,7 @@ static double GetCurrentTime(void) { double time = 0; - #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + #if defined(_WIN32) unsigned long long int clockFrequency, currentTime; QueryPerformanceFrequency(&clockFrequency); @@ -1951,7 +1955,7 @@ static double GetCurrentTime(void) time = (double)((double)currentTime/clockFrequency)*1000; #endif - #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) + #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(__linux) || defined(PLATFORM_WEB) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); uint64_t temp = (uint64_t)ts.tv_sec*1000000000LLU + (uint64_t)ts.tv_nsec; diff --git a/src/raylib.h b/src/raylib.h index 6d67051e..b0f03bbe 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,10 +1,10 @@ /********************************************************************************************** * -* raylib 1.6.0 (www.raylib.com) +* raylib v1.7.0 (www.raylib.com) * * A simple and easy-to-use library to learn videogames programming * -* Features: +* FEATURES: * Library written in plain C code (C99) * Uses PascalCase/camelCase notation * Hardware accelerated with OpenGL (1.1, 2.1, 3.3 or ES 2.0) @@ -12,16 +12,21 @@ * Powerful fonts module with SpriteFonts support (XNA bitmap fonts, AngelCode fonts, TTF) * Multiple textures support, including compressed formats and mipmaps generation * Basic 3d support for Shapes, Models, Billboards, Heightmaps and Cubicmaps -* Materials (diffuse, normal, specular) and Lighting (point, directional, spot) support * Powerful math module for Vector, Matrix and Quaternion operations: [raymath] * Audio loading and playing with streaming support and mixing channels [audio] * VR stereo rendering support with configurable HMD device parameters * Multiple platforms support: Windows, Linux, Mac, Android, Raspberry Pi, HTML5 and Oculus Rift CV1 * Custom color palette for fancy visuals on raywhite background * Minimal external dependencies (GLFW3, OpenGL, OpenAL) -* Complete binding for LUA [rlua] +* Complete binding for Lua [rlua] * -* External libs: +* NOTES: +* 32bit Colors - All defined color are always RGBA (struct Color is 4 byte) +* One custom default font could be loaded automatically when InitWindow() [core] +* If using OpenGL 3.3 or ES2, several vertex buffers (VAO/VBO) are created to manage lines-triangles-quads +* If using OpenGL 3.3 or ES2, two default shaders could be loaded automatically (internally defined) +* +* DEPENDENCIES: * GLFW3 (www.glfw.org) for window/context management and input [core] * GLAD for OpenGL extensions loading (3.3 Core profile, only PLATFORM_DESKTOP) [rlgl] * stb_image (Sean Barret) for images loading (JPEG, PNG, BMP, TGA) [textures] @@ -34,13 +39,8 @@ * OpenAL Soft for audio device/context management [audio] * tinfl for data decompression (DEFLATE algorithm) [utils] * -* Some design decisions: -* 32bit Colors - All defined color are always RGBA (struct Color is 4 byte) -* One custom default font could be loaded automatically when InitWindow() [core] -* If using OpenGL 3.3 or ES2, several vertex buffers (VAO/VBO) are created to manage lines-triangles-quads -* If using OpenGL 3.3 or ES2, two default shaders could be loaded automatically (internally defined) * -* -- LICENSE -- +* LICENSE: zlib/libpng * * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software: @@ -98,13 +98,13 @@ #define RAD2DEG (180.0f/PI) // raylib Config Flags -#define FLAG_FULLSCREEN_MODE 1 -#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 +#define FLAG_SHOW_LOGO 1 // Set to show raylib logo at startup +#define FLAG_FULLSCREEN_MODE 2 // Set to run program in fullscreen +#define FLAG_WINDOW_RESIZABLE 4 // Set to allow resizable window +#define FLAG_WINDOW_DECORATED 8 // Set to show window decoration (frame and buttons) +#define FLAG_WINDOW_TRANSPARENT 16 // Set to allow transparent window +#define FLAG_MSAA_4X_HINT 32 // Set to try enabling MSAA 4X +#define FLAG_VSYNC_HINT 64 // Set to try enabling V-Sync on GPU // Keyboard Function Keys #define KEY_SPACE 32 @@ -297,13 +297,9 @@ //---------------------------------------------------------------------------------- #ifndef __cplusplus // Boolean type - #ifndef __APPLE__ - #if !defined(_STDBOOL_H) - typedef enum { false, true } bool; - #define _STDBOOL_H - #endif - #else - #include <stdbool.h> + #if !defined(_STDBOOL_H) + typedef enum { false, true } bool; + #define _STDBOOL_H #endif #endif @@ -351,7 +347,7 @@ typedef struct Image { int width; // Image base width int height; // Image base height int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (TextureFormat) + int format; // Data format (TextureFormat type) } Image; // Texture2D type, bpp always RGBA (32bit) @@ -361,25 +357,31 @@ typedef struct Texture2D { int width; // Texture base width int height; // Texture base height int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (TextureFormat) + int format; // Data format (TextureFormat type) } Texture2D; // RenderTexture2D type, for texture rendering typedef struct RenderTexture2D { - unsigned int id; // Render texture (fbo) id + unsigned int id; // OpenGL Framebuffer Object (FBO) id Texture2D texture; // Color buffer attachment texture Texture2D depth; // Depth buffer attachment texture } RenderTexture2D; +// SpriteFont character info +typedef struct CharInfo { + int value; // Character value (Unicode) + Rectangle rec; // Character rectangle in sprite font + int offsetX; // Character offset X when drawing + int offsetY; // Character offset Y when drawing + int advanceX; // Character advance position X +} CharInfo; + // SpriteFont type, includes texture and charSet array data typedef struct SpriteFont { Texture2D texture; // Font texture - int size; // Base size (default chars height) - int numChars; // Number of characters - int *charValues; // Characters values array - Rectangle *charRecs; // Characters rectangles within the texture - Vector2 *charOffsets; // Characters offsets (on drawing) - int *charAdvanceX; // Characters x advance (on drawing) + int baseSize; // Base size (default chars height) + int charsCount; // Number of characters + CharInfo *chars; // Characters info data } SpriteFont; // Camera type, defines a camera position/orientation in 3d space @@ -466,31 +468,20 @@ typedef struct Model { Material material; // Shader and textures data } Model; -// Light type -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 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; - -// Light types -typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType; - // Ray type (useful for raycast) typedef struct Ray { Vector3 position; // Ray position (origin) Vector3 direction; // Ray direction } Ray; +// Information returned from a raycast +typedef struct RayHitInfo { + bool hit; // Did the ray hit something? + float distance; // Distance to nearest hit + Vector3 hitPosition; // Position of nearest hit + Vector3 hitNormal; // Surface normal of hit +} RayHitInfo; + // Wave type, defines audio wave data typedef struct Wave { unsigned int sampleCount; // Number of samples @@ -549,7 +540,7 @@ typedef enum { // Texture parameters: filter mode // NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 2: Filter is accordingly set for minification and magnification -typedef enum { +typedef enum { FILTER_POINT = 0, // No filter, just pixel aproximation FILTER_BILINEAR, // Linear filtering FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) @@ -581,12 +572,12 @@ typedef enum { } Gestures; // Camera system modes -typedef enum { - CAMERA_CUSTOM = 0, - CAMERA_FREE, - CAMERA_ORBITAL, - CAMERA_FIRST_PERSON, - CAMERA_THIRD_PERSON +typedef enum { + CAMERA_CUSTOM = 0, + CAMERA_FREE, + CAMERA_ORBITAL, + CAMERA_FIRST_PERSON, + CAMERA_THIRD_PERSON } CameraMode; // Head Mounted Display devices @@ -602,6 +593,34 @@ typedef enum { HMD_FOVE_VR, } VrDevice; +// rRES data returned when reading a resource, +// it contains all required data for user (24 byte) +typedef struct RRESData { + unsigned int type; // Resource type (4 byte) + + unsigned int param1; // Resouce parameter 1 (4 byte) + unsigned int param2; // Resouce parameter 2 (4 byte) + unsigned int param3; // Resouce parameter 3 (4 byte) + unsigned int param4; // Resouce parameter 4 (4 byte) + + void *data; // Resource data pointer (4 byte) +} RRESData; + +// RRESData type +typedef enum { + RRES_TYPE_RAW = 0, + RRES_TYPE_IMAGE, + RRES_TYPE_WAVE, + RRES_TYPE_VERTEX, + RRES_TYPE_TEXT, + RRES_TYPE_FONT_IMAGE, + RRES_TYPE_FONT_CHARDATA, // CharInfo data array + RRES_TYPE_DIRECTORY +} RRESDataType; + +// RRES type (pointer to RRESData array) +typedef struct RRESData *RRES; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -624,6 +643,9 @@ RLAPI void CloseWindow(void); // Close Windo 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 void SetWindowIcon(Image image); // Set icon for window (only PLATFORM_DESKTOP) +RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) +RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenHeight(void); // Get current screen height @@ -651,7 +673,7 @@ RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the 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 int 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 @@ -741,12 +763,14 @@ RLAPI void DrawPixel(int posX, int posY, Color color); 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 DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line defining thickness 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 DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters 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 @@ -767,21 +791,19 @@ RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Ve //------------------------------------------------------------------------------------ // Texture Loading and Drawing Functions (Module: textures) //------------------------------------------------------------------------------------ -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 Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) +RLAPI Image LoadImageEx(Color *pixels, int width, int height); // Load image from Color array data (RGBA - 32bit) +RLAPI Image LoadImagePro(void *data, int width, int height, int format); // Load image from raw data with parameters +RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data +RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) +RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data +RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) 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 void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) +RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) 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 UpdateTexture(Texture2D texture, void *pixels); // Update GPU texture with new data +RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data 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 @@ -794,7 +816,8 @@ RLAPI Image ImageText(const char *text, int fontSize, Color color); 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 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 @@ -802,7 +825,7 @@ RLAPI void ImageColorInvert(Image *image); 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 GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture RLAPI void SetTextureFilter(Texture2D texture, int filterMode); // Set texture scaling filter mode RLAPI void SetTextureWrap(Texture2D texture, int wrapMode); // Set texture wrapping mode @@ -817,9 +840,9 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle dest // Font Loading and Text Drawing Functions (Module: text) //------------------------------------------------------------------------------------ RLAPI SpriteFont GetDefaultFont(void); // Get the default SpriteFont -RLAPI SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory -RLAPI SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars); // Load a SpriteFont from TTF font with parameters -RLAPI void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory +RLAPI SpriteFont LoadSpriteFont(const char *fileName); // Load SpriteFont from file into GPU memory (VRAM) +RLAPI SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load SpriteFont from TTF font file with generation parameters +RLAPI void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory (VRAM) 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 @@ -849,50 +872,58 @@ RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); 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() could be useful? //------------------------------------------------------------------------------------ // Model 3d Loading and Drawing Functions (Module: models) //------------------------------------------------------------------------------------ -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 Mesh LoadMesh(const char *fileName); // Load mesh from file +RLAPI Mesh LoadMeshEx(int numVertex, float *vData, float *vtData, float *vnData, Color *cData); // Load mesh from vertex data +RLAPI Model LoadModel(const char *fileName); // Load model from file +RLAPI Model LoadModelFromMesh(Mesh data, bool dynamic); // Load model from mesh data +RLAPI Model LoadHeightmap(Image heightmap, Vector3 size); // Load heightmap model from image data +RLAPI Model LoadCubicmap(Image cubicmap); // Load cubes-based map model from image data +RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) +RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) + +RLAPI Material LoadMaterial(const char *fileName); // Load material from file +RLAPI Material LoadMaterialEx(Shader shader, Texture2D diffuse, Color color); // Load material from basic shading data +RLAPI Material LoadDefaultMaterial(void); // Load default material (uses default models shader) +RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (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 +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, returns collision point +RLAPI bool CheckCollisionRayBox(Ray ray, BoundingBox box); // Detect collision between ray and box +RLAPI RayHitInfo GetCollisionRayMesh(Ray ray, Mesh *mesh); // Get collision info between ray and mesh +RLAPI RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle +RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight); // Get collision info between ray and ground plane (Y-normal plane) //------------------------------------------------------------------------------------ // Shaders System Functions (Module: rlgl) // NOTE: This functions are useless when using OpenGL 1.1 //------------------------------------------------------------------------------------ -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 +RLAPI char *LoadText(const char *fileName); // Load chars array from text file +RLAPI Shader LoadShader(char *vsFileName, char *fsFileName); // Load shader from files and bind default locations +RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) RLAPI Shader GetDefaultShader(void); // Get default shader -RLAPI Shader GetStandardShader(void); // Get standard shader RLAPI Texture2D GetDefaultTexture(void); // Get default texture RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location @@ -908,9 +939,6 @@ RLAPI void EndShaderMode(void); // End RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) -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 @@ -928,13 +956,13 @@ RLAPI void ToggleVrMode(void); // Enable/Disable VR experienc RLAPI void InitAudioDevice(void); // Initialize audio device and context RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully +RLAPI void SetMasterVolume(float volume); // Set master volume (listener) -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 Wave LoadWave(const char *fileName); // Load wave data from file +RLAPI Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); // Load wave data from raw array data +RLAPI Sound LoadSound(const char *fileName); // Load sound from file +RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data +RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// 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 @@ -958,13 +986,14 @@ RLAPI void ResumeMusicStream(Music music); // Resume 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 void SetMusicLoopCount(Music music, float count); // Set music loop count (loop repeats) 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 raw audio pcm data) -RLAPI void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data +RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount); // 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 diff --git a/src/raymath.h b/src/raymath.h index 10eabb6b..a2263f19 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1,22 +1,23 @@ /********************************************************************************************** * -* raymath (header only file) +* raymath v1.0 - Some useful functions to work with Vector3, Matrix and Quaternions * -* Some useful functions to work with Vector3, Matrix and Quaternions +* CONFIGURATION: * -* You must: -* #define RAYMATH_IMPLEMENTATION -* before you include this file in *only one* C or C++ file to create the implementation. +* #define RAYMATH_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. * -* Example: -* #define RAYMATH_IMPLEMENTATION -* #include "raymath.h" +* #define RAYMATH_EXTERN_INLINE +* Inlines all functions code, so it runs faster. This requires lots of memory on system. +* +* #define RAYMATH_STANDALONE +* Avoid raylib.h header inclusion in this file. +* Vector3 and Matrix data types are defined internally in raymath module. * -* You can also use: -* #define RAYMATH_EXTERN_INLINE // Inlines all functions code, so it runs faster. -* // This requires lots of memory on system. -* #define RAYMATH_STANDALONE // Not dependent on raylib.h structs: Vector3, Matrix. * +* LICENSE: zlib/libpng * * Copyright (c) 2015 Ramon Santamaria (@raysan5) * @@ -130,6 +131,7 @@ RMDEF void VectorTransform(Vector3 *v, Matrix mat); // Transforms a Ve RMDEF Vector3 VectorZero(void); // Return a Vector3 init to zero RMDEF Vector3 VectorMin(Vector3 vec1, Vector3 vec2); // Return min value for each pair of components RMDEF Vector3 VectorMax(Vector3 vec1, Vector3 vec2); // Return max value for each pair of components +RMDEF Vector3 Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c); // Barycenter coords for p in triangle abc //------------------------------------------------------------------------------------ // Functions Declaration to work with Matrix @@ -222,16 +224,16 @@ RMDEF Vector3 VectorPerpendicular(Vector3 v) { Vector3 result; - float min = fabs(v.x); + float min = fabsf(v.x); Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f}; - if (fabs(v.y) < min) + if (fabsf(v.y) < min) { - min = fabs(v.y); + min = fabsf(v.y); cardinalAxis = (Vector3){0.0f, 1.0f, 0.0f}; } - if(fabs(v.z) < min) + if(fabsf(v.z) < min) { cardinalAxis = (Vector3){0.0f, 0.0f, 1.0f}; } @@ -256,7 +258,7 @@ RMDEF float VectorLength(const Vector3 v) { float length; - length = sqrt(v.x*v.x + v.y*v.y + v.z*v.z); + length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); return length; } @@ -284,7 +286,7 @@ RMDEF void VectorNormalize(Vector3 *v) length = VectorLength(*v); - if (length == 0) length = 1.0f; + if (length == 0.0f) length = 1.0f; ilength = 1.0f/length; @@ -302,7 +304,7 @@ RMDEF float VectorDistance(Vector3 v1, Vector3 v2) float dy = v2.y - v1.y; float dz = v2.z - v1.z; - result = sqrt(dx*dx + dy*dy + dz*dz); + result = sqrtf(dx*dx + dy*dy + dz*dz); return result; } @@ -382,6 +384,32 @@ RMDEF Vector3 VectorMax(Vector3 vec1, Vector3 vec2) return result; } +// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c) +// NOTE: Assumes P is on the plane of the triangle +RMDEF Vector3 Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) +{ + //Vector v0 = b - a, v1 = c - a, v2 = p - a; + + Vector3 v0 = VectorSubtract(b, a); + Vector3 v1 = VectorSubtract(c, a); + Vector3 v2 = VectorSubtract(p, a); + float d00 = VectorDotProduct(v0, v0); + float d01 = VectorDotProduct(v0, v1); + float d11 = VectorDotProduct(v1, v1); + float d20 = VectorDotProduct(v2, v0); + float d21 = VectorDotProduct(v2, v1); + + float denom = d00*d11 - d01*d01; + + Vector3 result; + + result.y = (d11*d20 - d01*d21)/denom; + result.z = (d00*d21 - d01*d20)/denom; + result.x = 1.0f - (result.z + result.y); + + return result; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix math //---------------------------------------------------------------------------------- @@ -590,7 +618,7 @@ RMDEF Matrix MatrixRotate(Vector3 axis, float angle) float x = axis.x, y = axis.y, z = axis.z; - float length = sqrt(x*x + y*y + z*z); + float length = sqrtf(x*x + y*y + z*z); if ((length != 1.0f) && (length != 0.0f)) { @@ -2,30 +2,55 @@ * * rlgl - raylib OpenGL abstraction layer * -* rlgl allows usage of OpenGL 1.1 style functions (rlVertex) that are internally mapped to -* selected OpenGL version (1.1, 2.1, 3.3 Core, ES 2.0). +* DESCRIPTION: * -* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal +* rlgl allows usage of OpenGL 1.1 style functions (rlVertex) that are internally mapped to +* selected OpenGL version (1.1, 2.1, 3.3 Core, ES 2.0). +* +* When chosing an OpenGL version greater than OpenGL 1.1, rlgl stores vertex data on internal * VBO buffers (and VAOs if available). It requires calling 3 functions: * rlglInit() - Initialize internal buffers and auxiliar resources * rlglDraw() - Process internal buffers and send required draw calls * rlglClose() - De-initialize internal buffers data and other auxiliar resources * -* External libs: +* CONFIGURATION: +* +* #define GRAPHICS_API_OPENGL_11 +* Use OpenGL 1.1 backend +* +* #define GRAPHICS_API_OPENGL_21 +* Use OpenGL 2.1 backend +* +* #define GRAPHICS_API_OPENGL_33 +* Use OpenGL 3.3 Core profile backend +* +* #define GRAPHICS_API_OPENGL_ES2 +* Use OpenGL ES 2.0 backend +* +* #define RLGL_STANDALONE +* Use rlgl as standalone library (no raylib dependency) +* +* #define RLGL_NO_DISTORTION_SHADER +* Avoid stereo rendering distortion sahder (shader_distortion.h) inclusion +* +* #define SUPPORT_SHADER_DEFAULT / ENABLE_SHADER_DEFAULT +* +* #define SUPPORT_SHADER_DISTORTION +* +* +* #define SUPPORT_OCULUS_RIFT_CV1 / RLGL_OCULUS_SUPPORT +* Enable Oculus Rift CV1 functionality +* +* #define SUPPORT_STEREO_RENDERING +* +* #define RLGL_NO_DEFAULT_SHADER +* +* DEPENDENCIES: * raymath - 3D math functionality (Vector3, Matrix, Quaternion) * GLAD - OpenGL extensions loading (OpenGL 3.3 Core only) * -* Module Configuration Flags: -* GRAPHICS_API_OPENGL_11 - Use OpenGL 1.1 backend -* GRAPHICS_API_OPENGL_21 - Use OpenGL 2.1 backend -* GRAPHICS_API_OPENGL_33 - Use OpenGL 3.3 Core profile backend -* GRAPHICS_API_OPENGL_ES2 - Use OpenGL ES 2.0 backend -* -* RLGL_STANDALONE - Use rlgl as standalone library (no raylib dependency) -* RLGL_NO_STANDARD_SHADER - Avoid standard shader (shader_standard.h) inclusion -* RLGL_NO_DISTORTION_SHADER - Avoid stereo rendering distortion sahder (shader_distortion.h) inclusion -* RLGL_OCULUS_SUPPORT - Enable Oculus Rift CV1 functionality * +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -48,7 +73,7 @@ #include "rlgl.h" -#include <stdio.h> // Required for: fopen(), fclose(), fread()... [Used only on ReadTextFile()] +#include <stdio.h> // Required for: fopen(), fclose(), fread()... [Used only on LoadText()] #include <stdlib.h> // Required for: malloc(), free(), rand() #include <string.h> // Required for: strcmp(), strlen(), strtok() #include <math.h> // Required for: atan2() @@ -58,7 +83,7 @@ #endif #if defined(GRAPHICS_API_OPENGL_11) - #ifdef __APPLE__ + #ifdef __APPLE__ #include <OpenGL/gl.h> // OpenGL 1.1 library for OSX #else #include <GL/gl.h> // OpenGL 1.1 library @@ -70,7 +95,7 @@ #endif #if defined(GRAPHICS_API_OPENGL_33) - #ifdef __APPLE__ + #ifdef __APPLE__ #include <OpenGL/gl3.h> // OpenGL 3 library for OSX #else #define GLAD_IMPLEMENTATION @@ -92,10 +117,6 @@ #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() [Used only on TraceLog()] #endif -#if !defined(GRAPHICS_API_OPENGL_11) && !defined(RLGL_NO_STANDARD_SHADER) - #include "shader_standard.h" // Standard shader to be embedded -#endif - #if !defined(GRAPHICS_API_OPENGL_11) && !defined(RLGL_NO_DISTORTION_SHADER) #include "shader_distortion.h" // Distortion shader to be embedded #endif @@ -118,8 +139,6 @@ #define MAX_DRAWS_BY_TEXTURE 256 // Draws are organized by texture changes #define TEMP_VERTEX_BUFFER_SIZE 4096 // Temporal Vertex Buffer (required for vertex-transformations) // NOTE: Every vertex are 3 floats (12 bytes) - -#define MAX_LIGHTS 8 // Max lights supported by standard shader #ifndef GL_SHADING_LANGUAGE_VERSION #define GL_SHADING_LANGUAGE_VERSION 0x8B8C @@ -175,7 +194,7 @@ #if defined(GRAPHICS_API_OPENGL_ES2) #define glClearDepth glClearDepthf - #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER #endif @@ -192,7 +211,7 @@ //---------------------------------------------------------------------------------- // Dynamic vertex buffers (position + texcoords + colors + indices arrays) -typedef struct { +typedef struct DynamicBuffer { int vCounter; // vertex position counter to process (and draw) from full buffer int tcCounter; // vertex texcoord counter to process (and draw) from full buffer int cCounter; // vertex color counter to process (and draw) from full buffer @@ -211,7 +230,7 @@ typedef struct { // Draw call type // NOTE: Used to track required draw-calls, organized by texture -typedef struct { +typedef struct DrawCall { int vertexCount; GLuint vaoId; GLuint textureId; @@ -226,7 +245,7 @@ typedef struct { } DrawCall; // Head-Mounted-Display device parameters -typedef struct { +typedef struct VrDeviceInfo { int hResolution; // HMD horizontal resolution in pixels int vResolution; // HMD vertical resolution in pixels float hScreenSize; // HMD horizontal size in meters @@ -240,7 +259,7 @@ typedef struct { } VrDeviceInfo; // VR Stereo rendering configuration for simulator -typedef struct { +typedef struct VrStereoConfig { RenderTexture2D stereoFbo; // VR stereo rendering framebuffer Shader distortionShader; // VR stereo rendering distortion shader //Rectangle eyesViewport[2]; // VR stereo rendering eyes viewports @@ -305,10 +324,7 @@ static bool useTempBuffer = false; // Shader Programs static Shader defaultShader; // Basic shader, support vertex color and diffuse texture -static Shader standardShader; // Shader with support for lighting and materials - // NOTE: Lazy initialization when GetStandardShader() static Shader currentShader; // Shader to be used on rendering (by default, defaultShader) -static bool standardShaderLoaded = false; // Flag to track if standard shader has been loaded // Extension supported flag: VAO static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension) @@ -318,6 +334,7 @@ static bool texCompETC1Supported = false; // ETC1 texture compression support static bool texCompETC2Supported = false; // ETC2/EAC texture compression support static bool texCompPVRTSupported = false; // PVR texture compression support static bool texCompASTCSupported = false; // ASTC texture compression support +#endif // Extension supported flag: Anisotropic filtering static bool texAnisotropicFilterSupported = false; // Anisotropic texture filtering support @@ -325,7 +342,6 @@ static float maxAnisotropicLevel = 0.0f; // Maximum anisotropy level supp // Extension supported flag: Clamp mirror wrap mode static bool texClampMirrorSupported = false; // Clamp mirror wrap mode supported -#endif #if defined(RLGL_OCULUS_SUPPORT) // OVR device variables @@ -368,12 +384,6 @@ static unsigned int whiteTexture; static int screenWidth; // Default framebuffer width 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 //---------------------------------------------------------------------------------- @@ -382,10 +392,8 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShaderStr); // Load custom shader strings and return program id static Shader LoadDefaultShader(void); // Load default shader (just vertex positioning and texture coloring) -static Shader LoadStandardShader(void); // Load standard shader (support materials and lighting) static void LoadDefaultShaderLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) static void UnloadDefaultShader(void); // Unload default shader -static void UnloadStandardShader(void); // Unload standard shader static void LoadDefaultBuffers(void); // Load default internal buffers (lines, triangles, quads) static void UpdateDefaultBuffers(void); // Update default internal buffers (VAOs/VBOs) with vertex data @@ -397,11 +405,6 @@ 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 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 #endif #if defined(RLGL_OCULUS_SUPPORT) @@ -718,7 +721,7 @@ void rlEnd(void) } break; default: break; } - + // NOTE: Depth increment is dependant on rlOrtho(): z-near and z-far values, // as well as depth buffer bit-depth (16bit or 24bit or 32bit) // Correct increment formula would be: depthInc = (zfar - znear)/pow(2, bits) @@ -902,6 +905,10 @@ void rlDisableTexture(void) #if defined(GRAPHICS_API_OPENGL_11) glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); +#else + // NOTE: If quads batch limit is reached, + // we force a draw call and next batch starts + if (quads.vCounter/4 >= MAX_QUADS_BATCH) rlglDraw(); #endif } @@ -922,11 +929,11 @@ void rlTextureParameters(unsigned int id, int param, int value) case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; case RL_TEXTURE_ANISOTROPIC_FILTER: { - if (value <= maxAnisotropicLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, value); + if (value <= maxAnisotropicLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); else if (maxAnisotropicLevel > 0.0f) { TraceLog(WARNING, "[TEX ID %i] Maximum anisotropic filter level supported is %iX", id, maxAnisotropicLevel); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, value); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); } else TraceLog(WARNING, "Anisotropic filtering not supported"); } break; @@ -1001,7 +1008,7 @@ void rlDeleteRenderTextures(RenderTexture2D target) if (target.id != 0) glDeleteFramebuffers(1, &target.id); if (target.texture.id != 0) glDeleteTextures(1, &target.texture.id); if (target.depth.id != 0) glDeleteTextures(1, &target.depth.id); - + TraceLog(INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); #endif } @@ -1018,7 +1025,7 @@ void rlDeleteShader(unsigned int id) void rlDeleteVertexArrays(unsigned int id) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (vaoSupported) + if (vaoSupported) { if (id != 0) glDeleteVertexArrays(1, &id); TraceLog(INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", id); @@ -1080,7 +1087,7 @@ void rlglInit(int width, int height) { // Check OpenGL information and capabilities //------------------------------------------------------------------------------ - + // Print current OpenGL and GLSL version TraceLog(INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); TraceLog(INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); @@ -1091,14 +1098,14 @@ void rlglInit(int width, int height) //int maxTexSize; //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); //TraceLog(INFO, "GL_MAX_TEXTURE_SIZE: %i", maxTexSize); - + //GL_MAX_TEXTURE_IMAGE_UNITS //GL_MAX_VIEWPORT_DIMS //int numAuxBuffers; //glGetIntegerv(GL_AUX_BUFFERS, &numAuxBuffers); //TraceLog(INFO, "GL_AUX_BUFFERS: %i", numAuxBuffers); - + //GLint numComp = 0; //GLint format[32] = { 0 }; //glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); @@ -1106,7 +1113,7 @@ void rlglInit(int width, int height) //for (int i = 0; i < numComp; i++) TraceLog(INFO, "Supported compressed format: 0x%x", format[i]); // NOTE: We don't need that much data on screen... right now... - + #if defined(GRAPHICS_API_OPENGL_11) //TraceLog(INFO, "OpenGL 1.1 (or driver default) profile initialized"); #endif @@ -1114,7 +1121,7 @@ void rlglInit(int width, int height) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Get supported extensions list GLint numExt = 0; - + #if defined(GRAPHICS_API_OPENGL_33) // NOTE: On OpenGL 3.3 VAO and NPOT are supported by default @@ -1124,18 +1131,18 @@ 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); - + #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) char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string - + // NOTE: We have to duplicate string because glGetString() returns a const value // If not duplicated, it fails in some systems (Raspberry Pi) // Equivalent to function: char *strdup(const char *str) @@ -1144,12 +1151,12 @@ void rlglInit(int width, int height) void *newstr = malloc(len); if (newstr == NULL) extensionsDup = NULL; extensionsDup = (char *)memcpy(newstr, extensions, len); - + // NOTE: String could be splitted using strtok() function (string.h) // NOTE: strtok() modifies the received string, it can not be const - + char *extList[512]; // Allocate 512 strings pointers (2 KB) - + extList[numExt] = strtok(extensionsDup, " "); while (extList[numExt] != NULL) @@ -1157,9 +1164,9 @@ void rlglInit(int width, int height) numExt++; extList[numExt] = strtok(NULL, " "); } - + free(extensionsDup); // Duplicated string must be deallocated - + numExt -= 1; #endif @@ -1177,25 +1184,25 @@ void rlglInit(int width, int height) if (strcmp(extList[i], (const char *)"GL_OES_vertex_array_object") == 0) { vaoSupported = true; - - // The extension is supported by our hardware and driver, try to get related functions pointers + + // The extension is supported by our hardware and driver, try to get related functions pointers // NOTE: emscripten does not support VAOs natively, it uses emulation and it reduces overall performance... glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES"); glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES"); //glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES"); // NOTE: Fails in WebGL, omitted } - + // Check NPOT textures support // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) npotSupported = true; #endif - + // DDS texture compression support if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) || - (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) texCompDXTSupported = true; - + (strcmp(extList[i], (const char *)"GL_WEBKIT_WEBGL_compressed_texture_s3tc") == 0)) texCompDXTSupported = true; + // ETC1 texture compression support if ((strcmp(extList[i], (const char *)"GL_OES_compressed_ETC1_RGB8_texture") == 0) || (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_etc1") == 0)) texCompETC1Supported = true; @@ -1208,26 +1215,26 @@ void rlglInit(int width, int height) // ASTC texture compression support if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true; - + // Anisotropic texture filter support if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0) { texAnisotropicFilterSupported = true; - glGetFloatv(0x84FF, &maxAnisotropicLevel); // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + glGetFloatv(0x84FF, &maxAnisotropicLevel); // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT } - + // Clamp mirror wrap mode supported if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) texClampMirrorSupported = 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"); - + if (npotSupported) TraceLog(INFO, "[EXTENSION] NPOT textures extension detected, full NPOT textures supported"); else TraceLog(WARNING, "[EXTENSION] NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); #endif @@ -1237,13 +1244,13 @@ void rlglInit(int width, int height) if (texCompETC2Supported) TraceLog(INFO, "[EXTENSION] ETC2/EAC compressed textures supported"); if (texCompPVRTSupported) TraceLog(INFO, "[EXTENSION] PVRT compressed textures supported"); if (texCompASTCSupported) TraceLog(INFO, "[EXTENSION] ASTC compressed textures supported"); - + if (texAnisotropicFilterSupported) TraceLog(INFO, "[EXTENSION] Anisotropic textures filtering supported (max: %.0fX)", maxAnisotropicLevel); if (texClampMirrorSupported) TraceLog(INFO, "[EXTENSION] Clamp mirror wrap texture mode supported"); // Initialize buffers, default shaders and default textures //---------------------------------------------------------- - + // Init default white texture unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) @@ -1257,7 +1264,7 @@ void rlglInit(int width, int height) currentShader = defaultShader; // Init default vertex arrays buffers (lines, triangles, quads) - LoadDefaultBuffers(); + LoadDefaultBuffers(); // Init temp vertex buffer, used when transformation required (translate, rotate, scale) tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); @@ -1276,7 +1283,7 @@ void rlglInit(int width, int height) drawsCounter = 1; draws[drawsCounter - 1].textureId = whiteTexture; currentDrawMode = RL_TRIANGLES; // Set default draw mode - + // Init internal matrix stack (emulating OpenGL 1.1) for (int i = 0; i < MATRIX_STACK_SIZE; i++) stack[i] = MatrixIdentity(); @@ -1305,7 +1312,7 @@ void rlglInit(int width, int height) #if defined(GRAPHICS_API_OPENGL_11) // Init state: Color hints (deprecated in OpenGL 3.0+) - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) #endif @@ -1313,7 +1320,7 @@ void rlglInit(int width, int height) glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color (black) glClearDepth(1.0f); // Set clear depth value (default) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers (depth buffer required for 3D) - + // Store screen size into global variables screenWidth = width; screenHeight = height; @@ -1326,19 +1333,11 @@ void rlglClose(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) UnloadDefaultShader(); - UnloadStandardShader(); UnloadDefaultBuffers(); - + // Delete default white texture glDeleteTextures(1, &whiteTexture); TraceLog(INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); - - // Unload lights - if (lightsCount > 0) - { - for (int i = 0; i < lightsCount; i++) free(lights[i]); - lightsCount = 0; - } free(draws); #endif @@ -1353,7 +1352,7 @@ void rlglDraw(void) // NOTE: Default buffers upload and draw UpdateDefaultBuffers(); - + if (vrEnabled && vrRendering) DrawDefaultBuffers(2); else DrawDefaultBuffers(1); #endif @@ -1369,7 +1368,7 @@ void rlglLoadExtensions(void *loader) if (!gladLoadGLLoader((GLADloadproc)loader)) TraceLog(WARNING, "GLAD: Cannot load OpenGL extensions"); else TraceLog(INFO, "GLAD: OpenGL extensions loaded successfully"); #endif - + #if defined(GRAPHICS_API_OPENGL_21) #ifndef __APPLE__ if (GLAD_GL_VERSION_2_1) TraceLog(INFO, "OpenGL 2.1 profile supported"); @@ -1390,17 +1389,17 @@ void rlglLoadExtensions(void *loader) Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) { Vector3 result = { 0.0f, 0.0f, 0.0f }; - + // Calculate unproject matrix (multiply projection matrix and view matrix) and invert it Matrix matProjView = MatrixMultiply(proj, view); MatrixInvert(&matProjView); - + // Create quaternion from source point Quaternion quat = { source.x, source.y, source.z, 1.0f }; - + // Multiply quat point by unproject matrix QuaternionTransform(&quat, matProjView); - + // Normalized world points in vectors result.x = quat.x/quat.w; result.y = quat.y/quat.w; @@ -1415,41 +1414,41 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding GLuint id = 0; - + // Check texture format support by OpenGL 1.1 (compressed textures not supported) -#if defined(GRAPHICS_API_OPENGL_11) +#if defined(GRAPHICS_API_OPENGL_11) if (textureFormat >= 8) { TraceLog(WARNING, "OpenGL 1.1 does not support GPU compressed texture formats"); return id; } #endif - + if ((!texCompDXTSupported) && ((textureFormat == COMPRESSED_DXT1_RGB) || (textureFormat == COMPRESSED_DXT1_RGBA) || (textureFormat == COMPRESSED_DXT3_RGBA) || (textureFormat == COMPRESSED_DXT5_RGBA))) { TraceLog(WARNING, "DXT compressed texture format not supported"); return id; } -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((!texCompETC1Supported) && (textureFormat == COMPRESSED_ETC1_RGB)) { TraceLog(WARNING, "ETC1 compressed texture format not supported"); return id; } - + if ((!texCompETC2Supported) && ((textureFormat == COMPRESSED_ETC2_RGB) || (textureFormat == COMPRESSED_ETC2_EAC_RGBA))) { TraceLog(WARNING, "ETC2 compressed texture format not supported"); return id; } - + if ((!texCompPVRTSupported) && ((textureFormat == COMPRESSED_PVRT_RGB) || (textureFormat == COMPRESSED_PVRT_RGBA))) { TraceLog(WARNING, "PVRT compressed texture format not supported"); return id; } - + if ((!texCompASTCSupported) && ((textureFormat == COMPRESSED_ASTC_4x4_RGBA) || (textureFormat == COMPRESSED_ASTC_8x8_RGBA))) { TraceLog(WARNING, "ASTC compressed texture format not supported"); @@ -1477,24 +1476,24 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma // GL_RGBA4 GL_RGBA GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4 // GL_RGBA8 GL_RGBA GL_UNSIGNED_BYTE // GL_RGB8 GL_RGB GL_UNSIGNED_BYTE - + switch (textureFormat) { case UNCOMPRESSED_GRAYSCALE: { glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, (unsigned char *)data); - + // With swizzleMask we define how a one channel texture will be mapped to RGBA // Required GL >= 3.3 or EXT_texture_swizzle/ARB_texture_swizzle GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); - + TraceLog(INFO, "[TEX ID %i] Grayscale texture loaded and swizzled", id); } break; case UNCOMPRESSED_GRAY_ALPHA: { glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, width, height, 0, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data); - + GLint swizzleMask[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); } break; @@ -1568,7 +1567,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma // Magnification and minification filters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Alternative: GL_LINEAR glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Alternative: GL_LINEAR - + #if defined(GRAPHICS_API_OPENGL_33) if (mipmapCount > 1) { @@ -1578,7 +1577,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma #endif // At this point we have the texture loaded in GPU and texture parameters configured - + // NOTE: If mipmaps were not in data, they are not generated automatically // Unbind current texture @@ -1594,15 +1593,15 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int textureForma RenderTexture2D rlglLoadRenderTexture(int width, int height) { RenderTexture2D target; - + target.id = 0; - + target.texture.id = 0; target.texture.width = width; target.texture.height = height; target.texture.format = UNCOMPRESSED_R8G8B8A8; target.texture.mipmaps = 1; - + target.depth.id = 0; target.depth.width = width; target.depth.height = height; @@ -1619,13 +1618,13 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 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) #define USE_DEPTH_TEXTURE #else #define USE_DEPTH_RENDERBUFFER #endif - + #if defined(USE_DEPTH_RENDERBUFFER) // Create the renderbuffer that will serve as the depth attachment for the framebuffer. glGenRenderbuffers(1, &target.depth.id); @@ -1661,7 +1660,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height) if (status != GL_FRAMEBUFFER_COMPLETE) { TraceLog(WARNING, "Framebuffer object could not be created..."); - + switch (status) { case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(WARNING, "Framebuffer is unsupported"); break; @@ -1672,21 +1671,21 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height) case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete missing attachment"); break; default: break; } - + glDeleteTextures(1, &target.texture.id); glDeleteTextures(1, &target.depth.id); glDeleteFramebuffers(1, &target.id); } else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); - + glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif - return target; + return target; } // Update already loaded texture in GPU with new data -void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data) +void rlglUpdateTexture(unsigned int id, int width, int height, int format, const void *data) { glBindTexture(GL_TEXTURE_2D, id); @@ -1719,31 +1718,31 @@ void rlglUpdateTexture(unsigned int id, int width, int height, int format, void } // Generate mipmap data for selected texture -void rlglGenerateMipmaps(Texture2D texture) +void rlglGenerateMipmaps(Texture2D *texture) { - glBindTexture(GL_TEXTURE_2D, texture.id); - + glBindTexture(GL_TEXTURE_2D, texture->id); + // Check if texture is power-of-two (POT) bool texIsPOT = false; - - if (((texture.width > 0) && ((texture.width & (texture.width - 1)) == 0)) && - ((texture.height > 0) && ((texture.height & (texture.height - 1)) == 0))) texIsPOT = true; + + if (((texture->width > 0) && ((texture->width & (texture->width - 1)) == 0)) && + ((texture->height > 0) && ((texture->height & (texture->height - 1)) == 0))) texIsPOT = true; if ((texIsPOT) || (npotSupported)) { #if defined(GRAPHICS_API_OPENGL_11) // Compute required mipmaps - void *data = rlglReadTexturePixels(texture); - + void *data = rlglReadTexturePixels(*texture); + // NOTE: data size is reallocated to fit mipmaps data // NOTE: CPU mipmap generation only supports RGBA 32bit data - int mipmapCount = GenerateMipmaps(data, texture.width, texture.height); + int mipmapCount = GenerateMipmaps(data, texture->width, texture->height); - int size = texture.width*texture.height*4; // RGBA 32bit only + int size = texture->width*texture->height*4; // RGBA 32bit only int offset = size; - int mipWidth = texture.width/2; - int mipHeight = texture.height/2; + int mipWidth = texture->width/2; + int mipHeight = texture->height/2; // Load the mipmaps for (int level = 1; level < mipmapCount; level++) @@ -1756,24 +1755,30 @@ void rlglGenerateMipmaps(Texture2D texture) mipWidth /= 2; mipHeight /= 2; } - - TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture.id); - + + TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture->id); + // NOTE: Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data free(data); - + + texture->mipmaps = mipmapCount + 1; #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically - TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture.id); + TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture->id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps (must be available) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Activate Trilinear filtering for mipmaps + + #define MIN(a,b) (((a)<(b))?(a):(b)) + #define MAX(a,b) (((a)>(b))?(a):(b)) + + texture->mipmaps = 1 + (int)floor(log(MAX(texture->width, texture->height))/log(2)); #endif } - else TraceLog(WARNING, "[TEX ID %i] Mipmaps can not be generated", texture.id); + else TraceLog(WARNING, "[TEX ID %i] Mipmaps can not be generated", texture->id); glBindTexture(GL_TEXTURE_2D, 0); } @@ -1789,7 +1794,7 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) mesh->vboId[4] = 0; // Vertex tangents VBO mesh->vboId[5] = 0; // Vertex texcoords2 VBO mesh->vboId[6] = 0; // Vertex indices VBO - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) int drawHint = GL_STATIC_DRAW; if (dynamic) drawHint = GL_DYNAMIC_DRAW; @@ -1804,8 +1809,8 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) glBindVertexArray(vaoId); } - // NOTE: Attributes must be uploaded considering default locations points - + // NOTE: Attributes must be uploaded considering default locations points + // Enable vertex attributes: position (shader-location = 0) glGenBuffers(1, &vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, vboId[0]); @@ -1835,7 +1840,7 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) glVertexAttrib3f(2, 1.0f, 1.0f, 1.0f); glDisableVertexAttribArray(2); } - + // Default color vertex attribute (shader-location = 3) if (mesh->colors != NULL) { @@ -1851,7 +1856,7 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f); glDisableVertexAttribArray(3); } - + // Default tangent vertex attribute (shader-location = 4) if (mesh->tangents != NULL) { @@ -1867,7 +1872,7 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) glVertexAttrib3f(4, 0.0f, 0.0f, 0.0f); glDisableVertexAttribArray(4); } - + // Default texcoord2 vertex attribute (shader-location = 5) if (mesh->texcoords2 != NULL) { @@ -1883,7 +1888,7 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) glVertexAttrib2f(5, 0.0f, 0.0f); glDisableVertexAttribArray(5); } - + if (mesh->indices != NULL) { glGenBuffers(1, &vboId[6]); @@ -1921,7 +1926,7 @@ void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Activate mesh VAO if (vaoSupported) glBindVertexArray(mesh.vaoId); - + switch (buffer) { case 0: // Update vertices (vertex position) @@ -1929,44 +1934,44 @@ void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex) glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.vertices, GL_DYNAMIC_DRAW); else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.vertices); - + } break; case 1: // Update texcoords (vertex texture coordinates) { glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords, GL_DYNAMIC_DRAW); else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords); - + } break; case 2: // Update normals (vertex normals) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.normals, GL_DYNAMIC_DRAW); else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.normals); - + } break; case 3: // Update colors (vertex colors) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*numVertex, mesh.colors, GL_DYNAMIC_DRAW); else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(unsigned char)*4*numVertex, mesh.colors); - + } break; case 4: // Update tangents (vertex tangents) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*numVertex, mesh.tangents, GL_DYNAMIC_DRAW); else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*numVertex, mesh.tangents); } break; case 5: // Update texcoords2 (vertex second texture coordinates) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); if (numVertex >= mesh.vertexCount) glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*numVertex, mesh.texcoords2, GL_DYNAMIC_DRAW); else glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*2*numVertex, mesh.texcoords2); } break; default: break; } - + // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); @@ -1994,13 +1999,11 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords); // Pointer to texture coords array if (mesh.normals != NULL) glNormalPointer(GL_FLOAT, 0, mesh.normals); // Pointer to normals array if (mesh.colors != NULL) glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh.colors); // Pointer to colors array - - // TODO: Support OpenGL 1.1 lighting system rlPushMatrix(); rlMultMatrixf(MatrixToFloat(transform)); rlColor4ub(material.colDiffuse.r, material.colDiffuse.g, material.colDiffuse.b, material.colDiffuse.a); - + if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); rlPopMatrix(); @@ -2019,13 +2022,13 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) if (vrEnabled) eyesCount = 2; glUseProgram(material.shader.id); - + // Upload to shader material.colDiffuse 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); @@ -2033,7 +2036,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() Matrix matView = modelview; // View matrix (camera) Matrix matProjection = projection; // Projection matrix (perspective) - + // Calculate model-view matrix combining matModel and matView Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates @@ -2049,7 +2052,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) Matrix transInvTransform = transform; MatrixTranspose(&transInvTransform); MatrixInvert(&transInvTransform); - + // Send model transformations matrix to shader glUniformMatrix4fv(modelMatrixLoc, 1, false, MatrixToFloat(transInvTransform)); } @@ -2062,11 +2065,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // 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) glActiveTexture(GL_TEXTURE0); @@ -2077,22 +2076,22 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) { // Upload to shader specular map flag glUniform1i(glGetUniformLocation(material.shader.id, "useNormal"), 1); - + glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, material.texNormal.id); glUniform1i(material.shader.mapTexture1Loc, 1); // Normal texture fits in active texture unit 1 } - + if ((material.texSpecular.id != 0) && (material.shader.mapTexture2Loc != -1)) { // Upload to shader specular map flag glUniform1i(glGetUniformLocation(material.shader.id, "useSpecular"), 1); - + glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); glUniform1i(material.shader.mapTexture2Loc, 2); // Specular texture fits in active texture unit 2 } - + if (vaoSupported) { glBindVertexArray(mesh.vaoId); @@ -2116,7 +2115,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glVertexAttribPointer(material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(material.shader.normalLoc); } - + // Bind mesh VBO data: vertex colors (shader-location = 3, if available) if (material.shader.colorLoc != -1) { @@ -2134,7 +2133,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glDisableVertexAttribArray(material.shader.colorLoc); } } - + // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) if (material.shader.tangentLoc != -1) { @@ -2142,7 +2141,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glVertexAttribPointer(material.shader.tangentLoc, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(material.shader.tangentLoc); } - + // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) if (material.shader.texcoord2Loc != -1) { @@ -2150,7 +2149,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) glVertexAttribPointer(material.shader.texcoord2Loc, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(material.shader.texcoord2Loc); } - + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); } @@ -2169,13 +2168,13 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); } - + if (material.texNormal.id != 0) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); } - + if (material.texSpecular.id != 0) { glActiveTexture(GL_TEXTURE2); @@ -2193,7 +2192,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) } glUseProgram(0); // Unbind shader program - + // Restore projection/modelview matrices projection = matProjection; modelview = matView; @@ -2225,7 +2224,7 @@ void rlglUnloadMesh(Mesh *mesh) // Read screen pixel data (color buffer) unsigned char *rlglReadScreenPixels(int width, int height) { - unsigned char *screenData = (unsigned char *)malloc(width*height*sizeof(unsigned char)*4); + unsigned char *screenData = (unsigned char *)calloc(width*height*4, sizeof(unsigned char)); // NOTE: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); @@ -2239,7 +2238,7 @@ unsigned char *rlglReadScreenPixels(int width, int height) { // Flip line imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; - + // Set alpha component value to 255 (no trasparent image retrieval) // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; @@ -2257,10 +2256,10 @@ unsigned char *rlglReadScreenPixels(int width, int height) void *rlglReadTexturePixels(Texture2D texture) { void *pixels = NULL; - + #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) glBindTexture(GL_TEXTURE_2D, texture.id); - + // NOTE: Using texture.id, we can retrieve some texture info (but not on OpenGL ES 2.0) /* int width, height, format; @@ -2269,11 +2268,11 @@ void *rlglReadTexturePixels(Texture2D texture) glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format); // Other texture info: GL_TEXTURE_RED_SIZE, GL_TEXTURE_GREEN_SIZE, GL_TEXTURE_BLUE_SIZE, GL_TEXTURE_ALPHA_SIZE */ - + int glFormat = 0, glType = 0; unsigned int size = texture.width*texture.height; - + // NOTE: GL_LUMINANCE and GL_LUMINANCE_ALPHA are removed since OpenGL 3.1 // Must be replaced by GL_RED and GL_RG on Core OpenGL 3.3 @@ -2282,8 +2281,8 @@ void *rlglReadTexturePixels(Texture2D texture) #if defined(GRAPHICS_API_OPENGL_11) case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_LUMINANCE; glType = GL_UNSIGNED_BYTE; break; // 8 bit per pixel (no alpha) case UNCOMPRESSED_GRAY_ALPHA: pixels = (unsigned char *)malloc(size*2); glFormat = GL_LUMINANCE_ALPHA; glType = GL_UNSIGNED_BYTE; break; // 16 bpp (2 channels) -#elif defined(GRAPHICS_API_OPENGL_33) - case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_RED; glType = GL_UNSIGNED_BYTE; break; +#elif defined(GRAPHICS_API_OPENGL_33) + case UNCOMPRESSED_GRAYSCALE: pixels = (unsigned char *)malloc(size); glFormat = GL_RED; glType = GL_UNSIGNED_BYTE; break; case UNCOMPRESSED_GRAY_ALPHA: pixels = (unsigned char *)malloc(size*2); glFormat = GL_RG; glType = GL_UNSIGNED_BYTE; break; #endif case UNCOMPRESSED_R5G6B5: pixels = (unsigned short *)malloc(size); glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; break; // 16 bpp @@ -2293,15 +2292,15 @@ void *rlglReadTexturePixels(Texture2D texture) case UNCOMPRESSED_R8G8B8A8: pixels = (unsigned char *)malloc(size*4); glFormat = GL_RGBA; glType = GL_UNSIGNED_BYTE; break; // 32 bpp default: TraceLog(WARNING, "Texture data retrieval, format not suported"); break; } - + // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding. - // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. - // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) + // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting. + // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.) glPixelStorei(GL_PACK_ALIGNMENT, 1); glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); - + glBindTexture(GL_TEXTURE_2D, 0); #endif @@ -2312,7 +2311,7 @@ void *rlglReadTexturePixels(Texture2D texture) // NOTE: Two possible Options: // 1 - Bind texture to color fbo attachment and glReadPixels() // 2 - Create an fbo, activate it, render quad with texture, glReadPixels() - + #define GET_TEXTURE_FBO_OPTION_1 // It works #if defined(GET_TEXTURE_FBO_OPTION_1) @@ -2322,48 +2321,48 @@ void *rlglReadTexturePixels(Texture2D texture) // Attach our texture to FBO -> Texture must be RGB // NOTE: Previoust attached texture is automatically detached glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.id, 0); - + pixels = (unsigned char *)malloc(texture.width*texture.height*4*sizeof(unsigned char)); - + // NOTE: Despite FBO color texture is RGB, we read data as RGBA... reading as RGB doesn't work... o__O glReadPixels(0, 0, texture.width, texture.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - + // Re-attach internal FBO color texture before deleting it glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo.texture.id, 0); - + glBindFramebuffer(GL_FRAMEBUFFER, 0); - + #elif defined(GET_TEXTURE_FBO_OPTION_2) // Render texture to fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo.id); - + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepthf(1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, width, height); //glMatrixMode(GL_PROJECTION); //glLoadIdentity(); - rlOrtho(0.0, width, height, 0.0, 0.0, 1.0); + rlOrtho(0.0, width, height, 0.0, 0.0, 1.0); //glMatrixMode(GL_MODELVIEW); //glLoadIdentity(); //glDisable(GL_TEXTURE_2D); //glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); - + Model quad; //quad.mesh = GenMeshQuad(width, height); quad.transform = MatrixIdentity(); quad.shader = defaultShader; - + DrawModel(quad, (Vector3){ 0.0f, 0.0f, 0.0f }, 1.0f, WHITE); - + pixels = (unsigned char *)malloc(texture.width*texture.height*3*sizeof(unsigned char)); - + glReadPixels(0, 0, texture.width, texture.height, GL_RGB, GL_UNSIGNED_BYTE, pixels); // Bind framebuffer 0, which means render to back buffer glBindFramebuffer(GL_FRAMEBUFFER, 0); - + UnloadModel(quad); #endif // GET_TEXTURE_FBO_OPTION @@ -2388,7 +2387,7 @@ void rlglRecordDraw(void) draws[drawsCounter].projection = projection; draws[drawsCounter].modelview = modelview; draws[drawsCounter].vertexCount = currentState.vertexCount; - + drawsCounter++; #endif } @@ -2403,49 +2402,83 @@ void rlglRecordDraw(void) Texture2D GetDefaultTexture(void) { Texture2D texture; - + texture.id = whiteTexture; texture.width = 1; texture.height = 1; texture.mipmaps = 1; texture.format = UNCOMPRESSED_R8G8B8A8; - + return texture; } -// Load a custom shader and bind default locations +// Load text data from file +// NOTE: text chars array should be freed manually +char *LoadText(const char *fileName) +{ + FILE *textFile; + char *text = NULL; + + int count = 0; + + if (fileName != NULL) + { + textFile = fopen(fileName,"rt"); + + if (textFile != NULL) + { + fseek(textFile, 0, SEEK_END); + count = ftell(textFile); + rewind(textFile); + + if (count > 0) + { + text = (char *)malloc(sizeof(char)*(count + 1)); + count = fread(text, sizeof(char), count, textFile); + text[count] = '\0'; + } + + fclose(textFile); + } + else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); + } + + return text; +} + +// Load shader from files and bind default locations Shader LoadShader(char *vsFileName, char *fsFileName) { Shader shader = { 0 }; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Shaders loading from external text file - char *vShaderStr = ReadTextFile(vsFileName); - char *fShaderStr = ReadTextFile(fsFileName); - + char *vShaderStr = LoadText(vsFileName); + char *fShaderStr = LoadText(fsFileName); + if ((vShaderStr != NULL) && (fShaderStr != NULL)) { shader.id = LoadShaderProgram(vShaderStr, fShaderStr); // After shader loading, we try to load default location names if (shader.id != 0) LoadDefaultShaderLocations(&shader); - + // Shader strings must be freed free(vShaderStr); free(fShaderStr); } - + if (shader.id == 0) { TraceLog(WARNING, "Custom shader could not be loaded"); shader = defaultShader; - } + } #endif return shader; } -// Unload a custom shader from memory +// Unload shader from GPU memory (VRAM) void UnloadShader(Shader shader) { if (shader.id != 0) @@ -2486,32 +2519,13 @@ Shader GetDefaultShader(void) #endif } -// Get default shader -// NOTE: Inits global variable standardShader -Shader GetStandardShader(void) -{ - Shader shader = { 0 }; - -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if (standardShaderLoaded) shader = standardShader; - else - { - // Lazy initialization of standard shader - standardShader = LoadStandardShader(); - shader = standardShader; - } -#endif - - return shader; -} - // Get shader uniform location int GetShaderLocation(Shader shader, const char *uniformName) { int location = -1; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) location = glGetUniformLocation(shader.id, uniformName); - + if (location == -1) TraceLog(DEBUG, "[SHDR ID %i] Shader location for %s could not be found", shader.id, uniformName); #endif return location; @@ -2528,8 +2542,8 @@ void SetShaderValue(Shader shader, int uniformLoc, float *value, int size) else if (size == 3) glUniform3fv(uniformLoc, 1, value); // Shader uniform type: vec3 else if (size == 4) glUniform4fv(uniformLoc, 1, value); // Shader uniform type: vec4 else TraceLog(WARNING, "Shader value float array size not supported"); - - glUseProgram(0); + + //glUseProgram(0); // Avoid reseting current shader program, in case other uniforms are set #endif } @@ -2544,8 +2558,8 @@ void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size) else if (size == 3) glUniform3iv(uniformLoc, 1, value); // Shader uniform type: ivec3 else if (size == 4) glUniform4iv(uniformLoc, 1, value); // Shader uniform type: ivec4 else TraceLog(WARNING, "Shader value int array size not supported"); - - glUseProgram(0); + + //glUseProgram(0); #endif } @@ -2556,8 +2570,8 @@ void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat) glUseProgram(shader.id); glUniformMatrix4fv(uniformLoc, 1, false, MatrixToFloat(mat)); - - glUseProgram(0); + + //glUseProgram(0); #endif } @@ -2584,7 +2598,7 @@ void BeginBlendMode(int mode) if ((blendMode != mode) && (mode < 3)) { rlglDraw(); - + switch (mode) { case BLEND_ALPHA: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; @@ -2592,7 +2606,7 @@ void BeginBlendMode(int mode) case BLEND_MULTIPLIED: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break; default: break; } - + blendMode = mode; } } @@ -2603,73 +2617,6 @@ void EndBlendMode(void) BeginBlendMode(BLEND_ALPHA); } -// Create a new light, initialize it and add to pool -Light CreateLight(int type, Vector3 position, Color diffuse) -{ - Light light = NULL; - - if (lightsCount < MAX_LIGHTS) - { - // Allocate dynamic memory - light = (Light)malloc(sizeof(LightData)); - - // Initialize light values with generic values - light->id = lightsCount; - light->type = type; - light->enabled = true; - - light->position = position; - light->target = (Vector3){ 0.0f, 0.0f, 0.0f }; - light->intensity = 1.0f; - light->diffuse = diffuse; - - // Add new light to the array - lights[lightsCount] = light; - - // Increase enabled lights count - lightsCount++; - } - else - { - TraceLog(WARNING, "Too many lights, only supported up to %i lights", MAX_LIGHTS); - - // NOTE: Returning latest created light to avoid crashes - light = lights[lightsCount]; - } - -#if defined(GRAPHICS_API_OPENGL_11) - TraceLog(WARNING, "Lighting currently not supported on OpenGL 1.1"); -#endif - - return light; -} - -// Destroy a light and take it out of the list -void DestroyLight(Light light) -{ - if (light != NULL) - { - int lightId = light->id; - - // Free dynamic memory allocation - free(lights[lightId]); - - // Remove *obj from the pointers array - for (int i = lightId; i < lightsCount; i++) - { - // Resort all the following pointers of the array - if ((i + 1) < lightsCount) - { - lights[i] = lights[i + 1]; - lights[i]->id = lights[i + 1]->id; - } - } - - // Decrease enabled physic objects count - lightsCount--; - } -} - // Init VR device (or simulator) // NOTE: If device is not available, it fallbacks to default device (simulator) // NOTE: It modifies the global variable: VrDeviceInfo hmd @@ -2725,9 +2672,9 @@ void InitVrDevice(int vrDevice) { // Oculus Rift CV1 parameters // NOTE: CV1 represents a complete HMD redesign compared to previous versions, - // new Fresnel-hybrid-asymmetric lenses have been added and, consequently, - // previous parameters (DK2) and distortion shader (DK2) doesn't work any more. - // I just defined a set of parameters for simulator that approximate to CV1 stereo rendering + // new Fresnel-hybrid-asymmetric lenses have been added and, consequently, + // previous parameters (DK2) and distortion shader (DK2) doesn't work any more. + // I just defined a set of parameters for simulator that approximate to CV1 stereo rendering // but result is not the same obtained with Oculus PC SDK. hmd.hResolution = 2160; // HMD horizontal resolution in pixels hmd.vResolution = 1200; // HMD vertical resolution in pixels @@ -2746,17 +2693,17 @@ void InitVrDevice(int vrDevice) hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 } - + // Initialize framebuffer and textures for stereo rendering // NOTE: screen size should match HMD aspect ratio vrConfig.stereoFbo = rlglLoadRenderTexture(screenWidth, screenHeight); - + // Load distortion shader (initialized by default with Oculus Rift CV1 parameters) vrConfig.distortionShader.id = LoadShaderProgram(vDistortionShaderStr, fDistortionShaderStr); if (vrConfig.distortionShader.id != 0) LoadDefaultShaderLocations(&vrConfig.distortionShader); SetStereoConfig(hmd); - + vrSimulator = true; vrEnabled = true; } @@ -2801,9 +2748,9 @@ void ToggleVrMode(void) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (vrDeviceReady || vrSimulator) vrEnabled = !vrEnabled; else vrEnabled = false; - + if (!vrEnabled) - { + { // Reset viewport and default projection-modelview matrices rlViewport(0, 0, screenWidth, screenHeight); projection = MatrixOrtho(0, screenWidth, screenHeight, 0, 0.0f, 1.0f); @@ -2838,15 +2785,15 @@ void BeginVrDrawing(void) rlEnableRenderTexture(vrConfig.stereoFbo.id); } - // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) + // NOTE: If your application is configured to treat the texture as a linear format (e.g. GL_RGBA) // and performs linear-to-gamma conversion in GLSL or does not care about gamma-correction, then: // - Require OculusBuffer format to be OVR_FORMAT_R8G8B8A8_UNORM_SRGB // - Do NOT enable GL_FRAMEBUFFER_SRGB //glEnable(GL_FRAMEBUFFER_SRGB); - + //glViewport(0, 0, buffer.width, buffer.height); // Useful if rendering to separate framebuffers (every eye) rlClearScreenBuffers(); // Clear current framebuffer(s) - + vrRendering = true; #endif } @@ -2865,12 +2812,12 @@ void EndVrDrawing(void) { // Unbind current framebuffer rlDisableRenderTexture(); - + rlClearScreenBuffers(); // Clear current framebuffer // Set viewport to default framebuffer size (screen size) rlViewport(0, 0, screenWidth, screenHeight); - + // Let rlgl reconfigure internal matrices rlMatrixMode(RL_PROJECTION); // Enable internal projection matrix rlLoadIdentity(); // Reset internal projection matrix @@ -2878,7 +2825,7 @@ void EndVrDrawing(void) rlMatrixMode(RL_MODELVIEW); // Enable internal modelview matrix rlLoadIdentity(); // Reset internal modelview matrix - // Draw RenderTexture (stereoFbo) using distortion shader + // Draw RenderTexture (stereoFbo) using distortion shader currentShader = vrConfig.distortionShader; rlEnableTexture(vrConfig.stereoFbo.texture.id); @@ -2915,7 +2862,7 @@ void EndVrDrawing(void) } rlDisableDepthTest(); - + vrRendering = false; #endif } @@ -2946,7 +2893,7 @@ static void LoadCompressedTexture(unsigned char *data, int width, int height, in for (int level = 0; level < mipmapCount && (width || height); level++) { unsigned int size = 0; - + size = ((width + 3)/4)*((height + 3)/4)*blockSize; glCompressedTexImage2D(GL_TEXTURE_2D, level, compressedFormat, width, height, 0, size, data + offset); @@ -3002,7 +2949,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade glGetShaderInfoLog(vertexShader, maxLength, &length, log); TraceLog(INFO, "%s", log); - + #ifdef _MSC_VER free(log); #endif @@ -3041,7 +2988,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); - + // NOTE: Default attribute shader locations must be binded before linking glBindAttribLocation(program, 0, DEFAULT_ATTRIB_POSITION_NAME); glBindAttribLocation(program, 1, DEFAULT_ATTRIB_TEXCOORD_NAME); @@ -3049,11 +2996,11 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade glBindAttribLocation(program, 3, DEFAULT_ATTRIB_COLOR_NAME); glBindAttribLocation(program, 4, DEFAULT_ATTRIB_TANGENT_NAME); glBindAttribLocation(program, 5, DEFAULT_ATTRIB_TEXCOORD2_NAME); - + // NOTE: If some attrib name is no found on the shader, it locations becomes -1 - + glLinkProgram(program); - + // NOTE: All uniform variables are intitialised to 0 when a program links glGetProgramiv(program, GL_LINK_STATUS, &success); @@ -3075,7 +3022,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade glGetProgramInfoLog(program, maxLength, &length, log); TraceLog(INFO, "%s", log); - + #ifdef _MSC_VER free(log); #endif @@ -3167,39 +3114,6 @@ static Shader LoadDefaultShader(void) return shader; } -// Load standard shader -// NOTE: This shader supports: -// - Up to 3 different maps: diffuse, normal, specular -// - Material properties: colAmbient, colDiffuse, colSpecular, glossiness -// - Up to 8 lights: Point, Directional or Spot -static Shader LoadStandardShader(void) -{ - Shader shader; - -#if !defined(RLGL_NO_STANDARD_SHADER) - // Load standard shader (embeded in standard_shader.h) - shader.id = LoadShaderProgram(vStandardShaderStr, fStandardShaderStr); - - if (shader.id != 0) - { - LoadDefaultShaderLocations(&shader); - TraceLog(INFO, "[SHDR ID %i] Standard shader loaded successfully", shader.id); - - standardShaderLoaded = true; - } - else - { - TraceLog(WARNING, "[SHDR ID %i] Standard shader could not be loaded, using default shader", shader.id); - shader = GetDefaultShader(); - } -#else - shader = GetDefaultShader(); - TraceLog(WARNING, "[SHDR ID %i] Standard shader not available, using default shader", shader.id); -#endif - - return shader; -} - // Get location handlers to for shader attributes and uniforms // NOTE: If any location is not found, loc point becomes -1 static void LoadDefaultShaderLocations(Shader *shader) @@ -3211,7 +3125,7 @@ static void LoadDefaultShaderLocations(Shader *shader) // vertex color location = 3 // vertex tangent location = 4 // vertex texcoord2 location = 5 - + // Get handles to GLSL input attibute locations shader->vertexLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME); shader->texcoordLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME); @@ -3227,18 +3141,15 @@ static void LoadDefaultShaderLocations(Shader *shader) 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 +// Unload default shader static void UnloadDefaultShader(void) { glUseProgram(0); @@ -3250,26 +3161,12 @@ static void UnloadDefaultShader(void) glDeleteProgram(defaultShader.id); } -// Unload standard shader -static void UnloadStandardShader(void) -{ - glUseProgram(0); -#if !defined(RLGL_NO_STANDARD_SHADER) - //glDetachShader(defaultShader, vertexShader); - //glDetachShader(defaultShader, fragmentShader); - //glDeleteShader(vertexShader); // Already deleted on shader compilation - //glDeleteShader(fragmentShader); // Already deleted on shader compilation - glDeleteProgram(standardShader.id); -#endif -} - - // Load default internal buffers (lines, triangles, quads) static void LoadDefaultBuffers(void) { // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) //-------------------------------------------------------------------------------------------- - + // Lines - Initialize arrays (vertex position and color data) lines.vertices = (float *)malloc(sizeof(float)*3*2*MAX_LINES_BATCH); // 3 float by vertex, 2 vertex by line lines.colors = (unsigned char *)malloc(sizeof(unsigned char)*4*2*MAX_LINES_BATCH); // 4 float by color, 2 colors by line @@ -3331,11 +3228,11 @@ static void LoadDefaultBuffers(void) TraceLog(INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); //-------------------------------------------------------------------------------------------- - + // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads) // NOTE: Default buffers are linked to use currentShader (defaultShader) //-------------------------------------------------------------------------------------------- - + // Upload and link lines vertex buffers if (vaoSupported) { @@ -3344,7 +3241,7 @@ static void LoadDefaultBuffers(void) glBindVertexArray(lines.vaoId); } - // Lines - Vertex buffers binding and attributes enable + // Lines - Vertex buffers binding and attributes enable // Vertex position buffer (shader-location = 0) glGenBuffers(2, &lines.vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); @@ -3370,7 +3267,7 @@ static void LoadDefaultBuffers(void) glBindVertexArray(triangles.vaoId); } - // Triangles - Vertex buffers binding and attributes enable + // Triangles - Vertex buffers binding and attributes enable // Vertex position buffer (shader-location = 0) glGenBuffers(1, &triangles.vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); @@ -3396,7 +3293,7 @@ static void LoadDefaultBuffers(void) glBindVertexArray(quads.vaoId); } - // Quads - Vertex buffers binding and attributes enable + // Quads - Vertex buffers binding and attributes enable // Vertex position buffer (shader-location = 0) glGenBuffers(1, &quads.vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); @@ -3512,7 +3409,7 @@ static void DrawDefaultBuffers(int eyesCount) { Matrix matProjection = projection; Matrix matModelView = modelview; - + for (int eye = 0; eye < eyesCount; eye++) { if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); @@ -3521,17 +3418,17 @@ static void DrawDefaultBuffers(int eyesCount) if ((lines.vCounter > 0) || (triangles.vCounter > 0) || (quads.vCounter > 0)) { glUseProgram(currentShader.id); - + // Create modelview-projection matrix Matrix matMVP = MatrixMultiply(modelview, projection); glUniformMatrix4fv(currentShader.mvpLoc, 1, false, MatrixToFloat(matMVP)); glUniform4f(currentShader.colDiffuseLoc, 1.0f, 1.0f, 1.0f, 1.0f); glUniform1i(currentShader.mapTexture0Loc, 0); - + // NOTE: Additional map textures not considered for default buffers drawing } - + // Draw lines buffers if (lines.vCounter > 0) { @@ -3615,7 +3512,7 @@ static void DrawDefaultBuffers(int eyesCount) glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); glEnableVertexAttribArray(currentShader.colorLoc); - + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); } @@ -3655,7 +3552,7 @@ static void DrawDefaultBuffers(int eyesCount) glUseProgram(0); // Unbind shader program } - + // Reset draws counter drawsCounter = 1; draws[0].textureId = whiteTexture; @@ -3669,10 +3566,10 @@ static void DrawDefaultBuffers(int eyesCount) quads.vCounter = 0; quads.tcCounter = 0; quads.cCounter = 0; - + // Reset depth for next draw currentDepth = -1.0f; - + // Restore projection/modelview matrices projection = matProjection; modelview = matModelView; @@ -3721,138 +3618,6 @@ static void UnloadDefaultBuffers(void) free(quads.indices); } -// Get shader locations for lights (up to MAX_LIGHTS) -static void GetShaderLightsLocations(Shader shader) -{ - 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); - } -} - -// 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) - { - 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: - { - 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: - { - Vector3 direction = VectorSubtract(lights[i]->target, lights[i]->position); - VectorNormalize(&direction); - glUniform3f(lightsLocs[i][3], direction.x, direction.y, direction.z); - } break; - case LIGHT_SPOT: - { - glUniform3f(lightsLocs[i][2], lights[i]->position.x, lights[i]->position.y, lights[i]->position.z); - - Vector3 direction = VectorSubtract(lights[i]->target, lights[i]->position); - VectorNormalize(&direction); - glUniform3f(lightsLocs[i][3], direction.x, direction.y, direction.z); - - glUniform1f(lightsLocs[i][7], lights[i]->coneAngle); - } break; - default: break; - } - } - else - { - glUniform1i(lightsLocs[i][0], 0); // Light disabled - } - } -} - -// Read text data from file -// NOTE: text chars array should be freed manually -static char *ReadTextFile(const char *fileName) -{ - FILE *textFile; - char *text = NULL; - - int count = 0; - - if (fileName != NULL) - { - textFile = fopen(fileName,"rt"); - - if (textFile != NULL) - { - fseek(textFile, 0, SEEK_END); - count = ftell(textFile); - rewind(textFile); - - if (count > 0) - { - text = (char *)malloc(sizeof(char)*(count + 1)); - count = fread(text, sizeof(char), count, textFile); - text[count] = '\0'; - } - - fclose(textFile); - } - else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); - } - - return text; -} - // Configure stereo rendering (including distortion shader) with HMD device parameters static void SetStereoConfig(VrDeviceInfo hmd) { @@ -3865,34 +3630,34 @@ static void SetStereoConfig(VrDeviceInfo hmd) float rightLensCenter[2] = { 0.75f - lensShift, 0.5f }; float leftScreenCenter[2] = { 0.25f, 0.5f }; float rightScreenCenter[2] = { 0.75f, 0.5f }; - + // Compute distortion scale parameters // NOTE: To get lens max radius, lensShift must be normalized to [-1..1] float lensRadius = fabsf(-1.0f - 4.0f*lensShift); float lensRadiusSq = lensRadius*lensRadius; - float distortionScale = hmd.distortionK[0] + - hmd.distortionK[1]*lensRadiusSq + - hmd.distortionK[2]*lensRadiusSq*lensRadiusSq + + float distortionScale = hmd.distortionK[0] + + hmd.distortionK[1]*lensRadiusSq + + hmd.distortionK[2]*lensRadiusSq*lensRadiusSq + hmd.distortionK[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; - + TraceLog(DEBUG, "VR: Distortion Scale: %f", distortionScale); - + float normScreenWidth = 0.5f; float normScreenHeight = 1.0f; float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect }; float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale }; - + TraceLog(DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); TraceLog(DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); TraceLog(DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); TraceLog(DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); - + // Update distortion shader with lens and distortion-scale parameters SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftLensCenter"), leftLensCenter, 2); SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightLensCenter"), rightLensCenter, 2); SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "leftScreenCenter"), leftScreenCenter, 2); SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "rightScreenCenter"), rightScreenCenter, 2); - + SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scale"), scale, 2); SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "scaleIn"), scaleIn, 2); SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "hmdWarpParam"), hmd.distortionK, 4); @@ -3902,24 +3667,24 @@ static void SetStereoConfig(VrDeviceInfo hmd) // ...but with lens distortion it is increased (see Oculus SDK Documentation) //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance)*RAD2DEG; // Really need distortionScale? float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance)*RAD2DEG; - + // Compute camera projection matrices float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] Matrix proj = MatrixPerspective(fovy, aspect, 0.01, 1000.0); vrConfig.eyesProjection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f)); vrConfig.eyesProjection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f)); - + // NOTE: Projection matrices must be transposed due to raymath convention MatrixTranspose(&vrConfig.eyesProjection[0]); MatrixTranspose(&vrConfig.eyesProjection[1]); - + // Compute camera transformation matrices - // NOTE: Camera movement might seem more natural if we model the head. - // Our axis of rotation is the base of our head, so we might want to add + // NOTE: Camera movement might seem more natural if we model the head. + // Our axis of rotation is the base of our head, so we might want to add // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions. vrConfig.eyesViewOffset[0] = MatrixTranslate(-hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); - + // Compute eyes Viewports //vrConfig.eyesViewport[0] = (Rectangle){ 0, 0, hmd.hResolution/2, hmd.vResolution }; //vrConfig.eyesViewport[1] = (Rectangle){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution }; @@ -3936,17 +3701,17 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) #if defined(RLGL_OCULUS_SUPPORT) if (vrDeviceReady) { - rlViewport(layer.eyeLayer.Viewport[eye].Pos.x, layer.eyeLayer.Viewport[eye].Pos.y, + rlViewport(layer.eyeLayer.Viewport[eye].Pos.x, layer.eyeLayer.Viewport[eye].Pos.y, layer.eyeLayer.Viewport[eye].Size.w, layer.eyeLayer.Viewport[eye].Size.h); - Quaternion eyeRenderPose = (Quaternion){ layer.eyeLayer.RenderPose[eye].Orientation.x, - layer.eyeLayer.RenderPose[eye].Orientation.y, - layer.eyeLayer.RenderPose[eye].Orientation.z, + Quaternion eyeRenderPose = (Quaternion){ layer.eyeLayer.RenderPose[eye].Orientation.x, + layer.eyeLayer.RenderPose[eye].Orientation.y, + layer.eyeLayer.RenderPose[eye].Orientation.z, layer.eyeLayer.RenderPose[eye].Orientation.w }; QuaternionInvert(&eyeRenderPose); Matrix eyeOrientation = QuaternionToMatrix(eyeRenderPose); - Matrix eyeTranslation = MatrixTranslate(-layer.eyeLayer.RenderPose[eye].Position.x, - -layer.eyeLayer.RenderPose[eye].Position.y, + Matrix eyeTranslation = MatrixTranslate(-layer.eyeLayer.RenderPose[eye].Position.x, + -layer.eyeLayer.RenderPose[eye].Position.y, -layer.eyeLayer.RenderPose[eye].Position.z); Matrix eyeView = MatrixMultiply(eyeTranslation, eyeOrientation); // Matrix containing eye-head movement @@ -3962,7 +3727,7 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView) // Apply view offset to modelview matrix eyeModelView = MatrixMultiply(matModelView, vrConfig.eyesViewOffset[eye]); - + eyeProjection = vrConfig.eyesProjection[eye]; } @@ -4106,7 +3871,7 @@ OCULUSAPI bool InitOculusDevice(void) bool oculusReady = false; ovrResult result = ovr_Initialize(NULL); - + if (OVR_FAILURE(result)) TraceLog(WARNING, "OVR: Could not initialize Oculus device"); else { @@ -4126,24 +3891,24 @@ OCULUSAPI bool InitOculusDevice(void) TraceLog(INFO, "OVR: Product Type: %i", hmdDesc.Type); //TraceLog(INFO, "OVR: Serial Number: %s", hmdDesc.SerialNumber); TraceLog(INFO, "OVR: Resolution: %ix%i", hmdDesc.Resolution.w, hmdDesc.Resolution.h); - + // NOTE: Oculus mirror is set to defined screenWidth and screenHeight... // ...ideally, it should be (hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2) - + // Initialize Oculus Buffers - layer = InitOculusLayer(session); + layer = InitOculusLayer(session); buffer = LoadOculusBuffer(session, layer.width, layer.height); mirror = LoadOculusMirror(session, hmdDesc.Resolution.w/2, hmdDesc.Resolution.h/2); // NOTE: hardcoded... layer.eyeLayer.ColorTexture[0] = buffer.textureChain; //SetOculusLayerTexture(eyeLayer, buffer.textureChain); - + // Recenter OVR tracking origin ovr_RecenterTrackingOrigin(session); - + oculusReady = true; vrEnabled = true; } } - + return oculusReady; } @@ -4164,18 +3929,18 @@ OCULUSAPI void UpdateOculusTracking(Camera *camera) ovrPosef eyePoses[2]; ovr_GetEyePoses(session, frameIndex, ovrTrue, layer.viewScaleDesc.HmdToEyeOffset, eyePoses, &layer.eyeLayer.SensorSampleTime); - + layer.eyeLayer.RenderPose[0] = eyePoses[0]; layer.eyeLayer.RenderPose[1] = eyePoses[1]; - + // TODO: Update external camera with eyePoses data (position, orientation) // NOTE: We can simplify to simple camera if we consider IPD and HMD device configuration again later // it will be useful for the user to draw, lets say, billboards oriented to camera - + // Get session status information ovrSessionStatus sessionStatus; ovr_GetSessionStatus(session, &sessionStatus); - + if (sessionStatus.ShouldQuit) TraceLog(WARNING, "OVR: Session should quit..."); if (sessionStatus.ShouldRecenter) ovr_RecenterTrackingOrigin(session); //if (sessionStatus.HmdPresent) // HMD is present. @@ -4189,7 +3954,7 @@ OCULUSAPI void BeginOculusDrawing(void) { GLuint currentTexId; int currentIndex; - + ovr_GetTextureSwapChainCurrentIndex(session, buffer.textureChain, ¤tIndex); ovr_GetTextureSwapChainBufferGL(session, buffer.textureChain, currentIndex, ¤tTexId); @@ -4204,9 +3969,9 @@ OCULUSAPI void EndOculusDrawing(void) // Unbind current framebuffer (Oculus buffer) glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - + ovr_CommitTextureSwapChain(session, buffer.textureChain); - + ovrLayerHeader *layers = &layer.eyeLayer.Header; ovr_SubmitFrame(session, frameIndex, &layer.viewScaleDesc, &layers, 1); @@ -4220,7 +3985,7 @@ static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height) OculusBuffer buffer; buffer.width = width; buffer.height = height; - + // Create OVR texture chain ovrTextureSwapChainDesc desc = {}; desc.Type = ovrTexture_2D; @@ -4233,12 +3998,12 @@ static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height) desc.StaticImage = ovrFalse; ovrResult result = ovr_CreateTextureSwapChainGL(session, &desc, &buffer.textureChain); - + if (!OVR_SUCCESS(result)) TraceLog(WARNING, "OVR: Failed to create swap textures buffer"); int textureCount = 0; ovr_GetTextureSwapChainLength(session, buffer.textureChain, &textureCount); - + if (!OVR_SUCCESS(result) || !textureCount) TraceLog(WARNING, "OVR: Unable to count swap chain textures"); for (int i = 0; i < textureCount; ++i) @@ -4251,9 +4016,9 @@ static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - + glBindTexture(GL_TEXTURE_2D, 0); - + /* // Setup framebuffer object (using depth texture) glGenFramebuffers(1, &buffer.fboId); @@ -4265,7 +4030,7 @@ static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, buffer.width, buffer.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); */ - + // Setup framebuffer object (using depth renderbuffer) glGenFramebuffers(1, &buffer.fboId); glGenRenderbuffers(1, &buffer.depthId); @@ -4298,13 +4063,13 @@ static OculusMirror LoadOculusMirror(ovrSession session, int width, int height) OculusMirror mirror; mirror.width = width; mirror.height = height; - + ovrMirrorTextureDesc mirrorDesc; memset(&mirrorDesc, 0, sizeof(mirrorDesc)); mirrorDesc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; mirrorDesc.Width = mirror.width; mirrorDesc.Height = mirror.height; - + if (!OVR_SUCCESS(ovr_CreateMirrorTextureGL(session, &mirrorDesc, &mirror.texture))) TraceLog(WARNING, "Could not create mirror texture"); glGenFramebuffers(1, &mirror.fboId); @@ -4323,9 +4088,9 @@ static void UnloadOculusMirror(ovrSession session, OculusMirror mirror) static void BlitOculusMirror(ovrSession session, OculusMirror mirror) { GLuint mirrorTextureId; - + ovr_GetMirrorTextureBufferGL(session, mirror.texture, &mirrorTextureId); - + glBindFramebuffer(GL_READ_FRAMEBUFFER, mirror.fboId); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mirrorTextureId, 0); #if defined(GRAPHICS_API_OPENGL_33) @@ -4339,7 +4104,7 @@ static void BlitOculusMirror(ovrSession session, OculusMirror mirror) static OculusLayer InitOculusLayer(ovrSession session) { OculusLayer layer = { 0 }; - + layer.viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; memset(&layer.eyeLayer, 0, sizeof(ovrLayerEyeFov)); @@ -4347,7 +4112,7 @@ static OculusLayer InitOculusLayer(ovrSession session) layer.eyeLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; ovrEyeRenderDesc eyeRenderDescs[2]; - + for (int eye = 0; eye < 2; eye++) { eyeRenderDescs[eye] = ovr_GetRenderDesc(session, eye, hmdDesc.DefaultEyeFov[eye]); @@ -4356,7 +4121,7 @@ static OculusLayer InitOculusLayer(ovrSession session) layer.viewScaleDesc.HmdToEyeOffset[eye] = eyeRenderDescs[eye].HmdToEyeOffset; layer.eyeLayer.Fov[eye] = eyeRenderDescs[eye].Fov; - + ovrSizei eyeSize = ovr_GetFovTextureSize(session, eye, layer.eyeLayer.Fov[eye], 1.0f); layer.eyeLayer.Viewport[eye].Size = eyeSize; layer.eyeLayer.Viewport[eye].Pos.x = layer.width; @@ -4365,7 +4130,7 @@ static OculusLayer InitOculusLayer(ovrSession session) layer.height = eyeSize.h; //std::max(renderTargetSize.y, (uint32_t)eyeSize.h); layer.width += eyeSize.w; } - + return layer; } @@ -4373,7 +4138,7 @@ static OculusLayer InitOculusLayer(ovrSession session) static Matrix FromOvrMatrix(ovrMatrix4f ovrmat) { Matrix rmat; - + rmat.m0 = ovrmat.M[0][0]; rmat.m1 = ovrmat.M[1][0]; rmat.m2 = ovrmat.M[2][0]; @@ -4390,9 +4155,9 @@ static Matrix FromOvrMatrix(ovrMatrix4f ovrmat) rmat.m13 = ovrmat.M[1][3]; rmat.m14 = ovrmat.M[2][3]; rmat.m15 = ovrmat.M[3][3]; - + MatrixTranspose(&rmat); - + return rmat; } #endif @@ -4423,7 +4188,7 @@ void TraceLog(int msgType, const char *text, ...) } // 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) @@ -97,7 +97,7 @@ // NOTE: This is the maximum amount of lines, triangles and quads per frame, be careful! #define MAX_LINES_BATCH 8192 #define MAX_TRIANGLES_BATCH 4096 - #define MAX_QUADS_BATCH 4096 + #define MAX_QUADS_BATCH 8192 #elif defined(GRAPHICS_API_OPENGL_ES2) // NOTE: Reduce memory sizes for embedded systems (RPI and HTML5) // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care... @@ -251,25 +251,6 @@ typedef unsigned char byte; float fovy; // Camera field-of-view apperture in Y (degrees) } Camera; - // Light type - 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) - 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; - // Texture parameters: filter mode // NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 2: Filter is accordingly set for minification and magnification @@ -370,8 +351,8 @@ void rlglLoadExtensions(void *loader); // Load OpenGL extensions unsigned int rlglLoadTexture(void *data, int width, int height, int textureFormat, int mipmapCount); // Load texture in GPU RenderTexture2D rlglLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with color and depth attachments) -void rlglUpdateTexture(unsigned int id, int width, int height, int format, void *data); // Update GPU texture with new data -void rlglGenerateMipmaps(Texture2D texture); // Generate mipmap data for selected texture +void rlglUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data +void rlglGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture void rlglLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex); // Update vertex data on GPU (upload new data to one buffer) @@ -415,9 +396,6 @@ void EndShaderMode(void); // End custo void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) void EndBlendMode(void); // End blending mode (reset to default: alpha blending) -Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool -void DestroyLight(Light light); // Destroy a light and take it out of the list - void TraceLog(int msgType, const char *text, ...); float *MatrixToFloat(Matrix mat); diff --git a/src/rlua.h b/src/rlua.h deleted file mode 100644 index c801a328..00000000 --- a/src/rlua.h +++ /dev/null @@ -1,4318 +0,0 @@ -/********************************************************************************************** -* -* 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 ([email protected]) -* 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 -//------------------------------------------------------------------------------------ -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_IsGamepadName(lua_State* L) -{ - int arg1 = LuaGetArgument_int(L, 1); - const char * arg2 = LuaGetArgument_string(L, 2); - bool result = IsGamepadName(arg1, arg2); - lua_pushboolean(L, result); - return 1; -} - -int lua_GetGamepadName(lua_State* L) -{ - // TODO: Return gamepad name id - - int arg1 = LuaGetArgument_int(L, 1); - char * result = GetGamepadName(arg1); - //lua_pushboolean(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; -} - -int lua_GetGamepadButtonPressed(lua_State* L) -{ - int result = GetGamepadButtonPressed(); - lua_pushinteger(L, result); - return 1; -} - -int lua_GetGamepadAxisCount(lua_State* L) -{ - int arg1 = LuaGetArgument_int(L, 1); - int result = GetGamepadAxisCount(arg1); - lua_pushinteger(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_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) -{ - Camera arg1 = LuaGetArgument_Camera(L, 1); - int arg2 = LuaGetArgument_int(L, 2); - SetCameraMode(arg1, arg2); - return 0; -} - -int lua_UpdateCamera(lua_State* L) -{ - Camera arg1 = LuaGetArgument_Camera(L, 1); - UpdateCamera(&arg1); - LuaPush_Camera(L, arg1); - return 1; -} - -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; -} - -//------------------------------------------------------------------------------------ -// 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) -{ - // TODO: Image LoadImageEx(Color *pixels, int width, int height); - - 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) -{ - // TODO: Color *GetImageData(Image image); - - 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_UpdateTexture(lua_State* L) -{ - // TODO: void UpdateTexture(Texture2D texture, void *pixels); - - 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_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_SetTextureFilter(lua_State* L) -{ - Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); - int arg2 = LuaGetArgument_int(L, 2); - SetTextureFilter(arg1, arg2); - return 0; -} - -int lua_SetTextureWrap(lua_State* L) -{ - Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); - int arg2 = LuaGetArgument_int(L, 2); - SetTextureWrap(arg1, arg2); - 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_LoadSpriteFontTTF(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); - //LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars); - SpriteFont result = LoadSpriteFontTTF(arg1, arg2, arg3, &arg4); - 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); - Vector3 arg3 = LuaGetArgument_Vector3(L, 3); - float arg4 = LuaGetArgument_float(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; -} - -//------------------------------------------------------------------------------------ -// 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_IsVrSimulator(lua_State* L) -{ - bool result = IsVrSimulator(); - lua_pushboolean(L, result); - return 1; -} - -int lua_UpdateVrTracking(lua_State* L) -{ - Camera arg1 = LuaGetArgument_Camera(L, 1); - UpdateVrTracking(&arg1); - LuaPush_Camera(L, arg1); - return 1; -} - -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) -{ - // TODO: void UpdateSound(Sound sound, void *data, int numSamples); - - 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_WaveCopy(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); - //lua_pushnumber(L, result); - return 0; -} - -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) -{ - // TODO: void UpdateAudioStream(AudioStream stream, void *data, int numSamples); - - 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) - - REG(IsKeyPressed) - REG(IsKeyDown) - REG(IsKeyReleased) - REG(IsKeyUp) - REG(GetKeyPressed) - REG(SetExitKey) - - REG(IsGamepadAvailable) - REG(IsGamepadName) - REG(GetGamepadName) - REG(IsGamepadButtonPressed) - REG(IsGamepadButtonDown) - REG(IsGamepadButtonReleased) - REG(IsGamepadButtonUp) - REG(GetGamepadButtonPressed) - REG(GetGamepadAxisCount) - REG(GetGamepadAxisMovement) - - 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(SetCameraPanControl) - REG(SetCameraAltControl) - REG(SetCameraSmoothZoomControl) - REG(SetCameraMoveControls) - - 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(UpdateTexture) - 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(SetTextureFilter) - REG(SetTextureWrap) - - REG(DrawTexture) - REG(DrawTextureV) - REG(DrawTextureEx) - REG(DrawTextureRec) - REG(DrawTexturePro) - - REG(GetDefaultFont) - REG(LoadSpriteFont) - REG(LoadSpriteFontTTF) - 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(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(IsVrSimulator) - 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/rres.h b/src/rres.h new file mode 100644 index 00000000..362da10d --- /dev/null +++ b/src/rres.h @@ -0,0 +1,439 @@ +/********************************************************************************************** +* +* rres - raylib Resource custom format management functions +* +* Basic functions to load/save rRES resource files +* +* CONFIGURATION: +* +* #define RREM_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. +* +* DEPENDENCIES: +* tinfl - DEFLATE decompression functions +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2016-2017 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. +* +**********************************************************************************************/ + +#ifndef RRES_H +#define RRES_H + +#if !defined(RRES_STANDALONE) + #include "raylib.h" +#endif + +//#define RRES_STATIC +#ifdef RRES_STATIC + #define RRESDEF static // Functions just visible to module including this file +#else + #ifdef __cplusplus + #define RRESDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) + #else + #define RRESDEF extern // Functions visible from other files + #endif +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define MAX_RESOURCES_SUPPORTED 256 + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Some types are required for RAYGUI_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(RRES_STANDALONE) + // rRES data returned when reading a resource, it contains all required data for user (24 byte) + // NOTE: Using void *data pointer, so we can load to image.data, wave.data, mesh.*, (unsigned char *) + typedef struct RRESData { + unsigned int type; // Resource type (4 byte) + + unsigned int param1; // Resouce parameter 1 (4 byte) + unsigned int param2; // Resouce parameter 2 (4 byte) + unsigned int param3; // Resouce parameter 3 (4 byte) + unsigned int param4; // Resouce parameter 4 (4 byte) + + void *data; // Resource data pointer (4 byte) + } RRESData; + + // RRESData type + typedef enum { + RRES_TYPE_RAW = 0, + RRES_TYPE_IMAGE, + RRES_TYPE_WAVE, + RRES_TYPE_VERTEX, + RRES_TYPE_TEXT, + RRES_TYPE_FONT_IMAGE, + RRES_TYPE_FONT_CHARDATA, // Character { int value, recX, recY, recWidth, recHeight, offsetX, offsetY, xAdvance } + RRES_TYPE_DIRECTORY + } RRESDataType; + + // RRES type (pointer to RRESData array) + typedef struct RRESData *RRES; +#endif + +//---------------------------------------------------------------------------------- +// Global variables +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +//RRESDEF RRESData LoadResourceData(const char *rresFileName, int rresId, int part); +RRESDEF RRES LoadResource(const char *fileName, int rresId); +RRESDEF void UnloadResource(RRES rres); + +#endif // RRES_H + + +/*********************************************************************************** +* +* RRES IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RRES_IMPLEMENTATION) + +#include <stdio.h> // Required for: FILE, fopen(), fclose() + +// Check if custom malloc/free functions defined, if not, using standard ones +#if !defined(RRES_MALLOC) + #include <stdlib.h> // Required for: malloc(), free() + + #define RRES_MALLOC(size) malloc(size) + #define RRES_FREE(ptr) free(ptr) +#endif + +#include "external/tinfl.c" // Required for: tinfl_decompress_mem_to_mem() + // NOTE: DEFLATE algorythm data decompression + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// rRES file header (8 byte) +typedef struct { + char id[4]; // File identifier: rRES (4 byte) + unsigned short version; // File version and subversion (2 byte) + unsigned short count; // Number of resources in this file (2 byte) +} RRESFileHeader; + +// rRES info header, every resource includes this header (16 byte + 16 byte) +typedef struct { + unsigned int id; // Resource unique identifier (4 byte) + unsigned char dataType; // Resource data type (1 byte) + unsigned char compType; // Resource data compression type (1 byte) + unsigned char cryptoType; // Resource data encryption type (1 byte) + unsigned char partsCount; // Resource data parts count, used for splitted data (1 byte) + unsigned int dataSize; // Resource data size (compressed or not, only DATA) (4 byte) + unsigned int uncompSize; // Resource data size (uncompressed, only DATA) (4 byte) + + unsigned int param1; // Resouce parameter 1 (4 byte) + unsigned int param2; // Resouce parameter 2 (4 byte) + unsigned int param3; // Resouce parameter 3 (4 byte) + unsigned int param4; // Resouce parameter 4 (4 byte) +} RRESInfoHeader; + +// Compression types +typedef enum { + RRES_COMP_NONE = 0, // No data compression + RRES_COMP_DEFLATE, // DEFLATE compression + RRES_COMP_LZ4, // LZ4 compression + RRES_COMP_LZMA, // LZMA compression + RRES_COMP_BROTLI, // BROTLI compression + // gzip, zopfli, lzo, zstd // Other compression algorythms... +} RRESCompressionType; + +typedef enum { + RRES_CRYPTO_NONE = 0, // No data encryption + RRES_CRYPTO_XOR, // XOR (128 bit) encryption + RRES_CRYPTO_AES, // RIJNDAEL (128 bit) encryption (AES) + RRES_CRYPTO_TDES, // Triple DES encryption + RRES_CRYPTO_BLOWFISH, // BLOWFISH encryption + RRES_CRYPTO_XTEA, // XTEA encryption + // twofish, RC5, RC6 // Other encryption algorythm... +} RRESEncryptionType; + +typedef enum { + RRES_IM_UNCOMP_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + RRES_IM_UNCOMP_GRAY_ALPHA, // 16 bpp (2 channels) + RRES_IM_UNCOMP_R5G6B5, // 16 bpp + RRES_IM_UNCOMP_R8G8B8, // 24 bpp + RRES_IM_UNCOMP_R5G5B5A1, // 16 bpp (1 bit alpha) + RRES_IM_UNCOMP_R4G4B4A4, // 16 bpp (4 bit alpha) + RRES_IM_UNCOMP_R8G8B8A8, // 32 bpp + RRES_IM_COMP_DXT1_RGB, // 4 bpp (no alpha) + RRES_IM_COMP_DXT1_RGBA, // 4 bpp (1 bit alpha) + RRES_IM_COMP_DXT3_RGBA, // 8 bpp + RRES_IM_COMP_DXT5_RGBA, // 8 bpp + RRES_IM_COMP_ETC1_RGB, // 4 bpp + RRES_IM_COMP_ETC2_RGB, // 4 bpp + RRES_IM_COMP_ETC2_EAC_RGBA, // 8 bpp + RRES_IM_COMP_PVRT_RGB, // 4 bpp + RRES_IM_COMP_PVRT_RGBA, // 4 bpp + RRES_IM_COMP_ASTC_4x4_RGBA, // 8 bpp + RRES_IM_COMP_ASTC_8x8_RGBA // 2 bpp + //... +} RRESImageFormat; + +typedef enum { + RRES_VERT_POSITION, + RRES_VERT_TEXCOORD1, + RRES_VERT_TEXCOORD2, + RRES_VERT_TEXCOORD3, + RRES_VERT_TEXCOORD4, + RRES_VERT_NORMAL, + RRES_VERT_TANGENT, + RRES_VERT_COLOR, + RRES_VERT_INDEX, + //... +} RRESVertexType; + +typedef enum { + RRES_VERT_BYTE, + RRES_VERT_SHORT, + RRES_VERT_INT, + RRES_VERT_HFLOAT, + RRES_VERT_FLOAT, + //... +} RRESVertexFormat; + +#if defined(RRES_STANDALONE) +typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static void *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize); + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Load resource from file by id (could be multiple parts) +// NOTE: Returns uncompressed data with parameters, search resource by id +RRESDEF RRES LoadResource(const char *fileName, int rresId) +{ + RRES rres = { 0 }; + + RRESFileHeader fileHeader; + RRESInfoHeader infoHeader; + + FILE *rresFile = fopen(fileName, "rb"); + + if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", fileName); + else + { + // Read rres file info header + fread(&fileHeader.id[0], sizeof(char), 1, rresFile); + fread(&fileHeader.id[1], sizeof(char), 1, rresFile); + fread(&fileHeader.id[2], sizeof(char), 1, rresFile); + fread(&fileHeader.id[3], sizeof(char), 1, rresFile); + fread(&fileHeader.version, sizeof(short), 1, rresFile); + fread(&fileHeader.count, sizeof(short), 1, rresFile); + + // Verify "rRES" identifier + if ((fileHeader.id[0] != 'r') && (fileHeader.id[1] != 'R') && (fileHeader.id[2] != 'E') && (fileHeader.id[3] != 'S')) + { + TraceLog(WARNING, "[%s] This is not a valid raylib resource file", fileName); + } + else + { + for (int i = 0; i < fileHeader.count; i++) + { + // Read resource info and parameters + fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile); + + rres = (RRES)malloc(sizeof(RRESData)*infoHeader.partsCount); + + if (infoHeader.id == rresId) + { + // Load all required resources parts + for (int k = 0; k < infoHeader.partsCount; k++) + { + // TODO: Verify again that rresId is the same in every part + + // Register data type and parameters + rres[k].type = infoHeader.dataType; + rres[k].param1 = infoHeader.param1; + rres[k].param2 = infoHeader.param2; + rres[k].param3 = infoHeader.param3; + rres[k].param4 = infoHeader.param4; + + // Read resource data block + void *data = RRES_MALLOC(infoHeader.dataSize); + fread(data, infoHeader.dataSize, 1, rresFile); + + if (infoHeader.compType == RRES_COMP_DEFLATE) + { + void *uncompData = DecompressData(data, infoHeader.dataSize, infoHeader.uncompSize); + + rres[k].data = uncompData; + + RRES_FREE(data); + } + else rres[k].data = data; + + if (rres[k].data != NULL) TraceLog(INFO, "[%s][ID %i] Resource data loaded successfully", fileName, (int)infoHeader.id); + + // Read next part + fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile); + } + } + else + { + // Skip required data to read next resource infoHeader + fseek(rresFile, infoHeader.dataSize, SEEK_CUR); + } + } + + if (rres[0].data == NULL) TraceLog(WARNING, "[%s][ID %i] Requested resource could not be found", fileName, (int)rresId); + } + + fclose(rresFile); + } + + return rres; +} + +RRESDEF void UnloadResource(RRES rres) +{ + if (rres[0].data != NULL) free(rres[0].data); +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// Data decompression function +// NOTE: Allocated data MUST be freed by user +static void *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize) +{ + int tempUncompSize; + void *uncompData; + + // Allocate buffer to hold decompressed data + uncompData = (mz_uint8 *)RRES_MALLOC((size_t)uncompSize); + + // Check correct memory allocation + if (uncompData == NULL) + { + TraceLog(WARNING, "Out of memory while decompressing data"); + } + else + { + // Decompress data + tempUncompSize = tinfl_decompress_mem_to_mem(uncompData, (size_t)uncompSize, data, compSize, 1); + + if (tempUncompSize == -1) + { + TraceLog(WARNING, "Data decompression failed"); + RRES_FREE(uncompData); + } + + if (uncompSize != (int)tempUncompSize) + { + TraceLog(WARNING, "Expected uncompressed size do not match, data may be corrupted"); + TraceLog(WARNING, " -- Expected uncompressed size: %i", uncompSize); + TraceLog(WARNING, " -- Returned uncompressed size: %i", tempUncompSize); + } + + TraceLog(INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize); + } + + return uncompData; +} + +// Some required functions for rres standalone module version +#if defined(RRES_STANDALONE) +// Outputs a trace log message (INFO, ERROR, WARNING) +// NOTE: If a file has been init, output log is written there +void TraceLog(int logType, const char *text, ...) +{ + va_list args; + int traceDebugMsgs = 0; + +#ifdef DO_NOT_TRACE_DEBUG_MSGS + traceDebugMsgs = 0; +#endif + + switch (msgType) + { + case LOG_INFO: fprintf(stdout, "INFO: "); break; + case LOG_ERROR: fprintf(stdout, "ERROR: "); break; + case LOG_WARNING: fprintf(stdout, "WARNING: "); break; + case LOG_DEBUG: if (traceDebugMsgs) fprintf(stdout, "DEBUG: "); break; + default: break; + } + + if ((msgType != LOG_DEBUG) || ((msgType == LOG_DEBUG) && (traceDebugMsgs))) + { + va_start(args, text); + vfprintf(stdout, text, args); + va_end(args); + + fprintf(stdout, "\n"); + } + + if (msgType == ERROR) exit(1); // If ERROR message, exit program +} +#endif + +#endif // RAYGUI_IMPLEMENTATION + +/* +Mesh LoadMeshEx(int numVertex, float *vData, float *vtData, float *vnData, Color *cData); +Mesh LoadMeshEx(rres.param1, rres.data, rres.data + offset, rres.data + offset*2, rres.data + offset*3); + +Shader LoadShader(const char *vsText, int vsLength); +Shader LoadShaderV(rres.data, rres.param1); + +// Parameters information depending on resource type + +// RRES_TYPE_IMAGE params: imgWidth, imgHeight, format, mipmaps; +// RRES_TYPE_WAVE params: sampleCount, sampleRate, sampleSize, channels; +// RRES_TYPE_FONT_IMAGE params: imgWidth, imgHeight, format, mipmaps; +// RRES_TYPE_FONT_DATA params: charsCount, baseSize +// RRES_TYPE_VERTEX params: vertexCount, vertexType, vertexFormat // Use masks instead? +// RRES_TYPE_TEXT params: charsCount, cultureCode +// RRES_TYPE_DIRECTORY params: fileCount, directoryCount + +// SpriteFont = RRES_TYPE_FONT_IMAGE chunk + RRES_TYPE_FONT_DATA chunk +// Mesh = multiple RRES_TYPE_VERTEX chunks + +Ref: RIFF file-format: http://www.johnloomis.org/cpe102/asgn/asgn1/riff.html + +*/
\ No newline at end of file diff --git a/src/shader_standard.h b/src/shader_standard.h deleted file mode 100644 index 995c62ea..00000000 --- a/src/shader_standard.h +++ /dev/null @@ -1,173 +0,0 @@ - -// Vertex shader definition to embed, no external file required -static const char vStandardShaderStr[] = -#if defined(GRAPHICS_API_OPENGL_21) -"#version 120 \n" -#elif defined(GRAPHICS_API_OPENGL_ES2) -"#version 100 \n" -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) -"attribute vec3 vertexPosition; \n" -"attribute vec3 vertexNormal; \n" -"attribute vec2 vertexTexCoord; \n" -"attribute vec4 vertexColor; \n" -"varying vec3 fragPosition; \n" -"varying vec3 fragNormal; \n" -"varying vec2 fragTexCoord; \n" -"varying vec4 fragColor; \n" -#elif defined(GRAPHICS_API_OPENGL_33) -"#version 330 \n" -"in vec3 vertexPosition; \n" -"in vec3 vertexNormal; \n" -"in vec2 vertexTexCoord; \n" -"in vec4 vertexColor; \n" -"out vec3 fragPosition; \n" -"out vec3 fragNormal; \n" -"out vec2 fragTexCoord; \n" -"out vec4 fragColor; \n" -#endif -"uniform mat4 mvpMatrix; \n" -"void main() \n" -"{ \n" -" fragPosition = vertexPosition; \n" -" fragNormal = vertexNormal; \n" -" fragTexCoord = vertexTexCoord; \n" -" fragColor = vertexColor; \n" -" gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n" -"} \n"; - -// Fragment shader definition to embed, no external file required -static const char fStandardShaderStr[] = -#if defined(GRAPHICS_API_OPENGL_21) -"#version 120 \n" -#elif defined(GRAPHICS_API_OPENGL_ES2) -"#version 100 \n" -"precision mediump float; \n" // precision required for OpenGL ES2 (WebGL) -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) -"varying vec3 fragPosition; \n" -"varying vec3 fragNormal; \n" -"varying vec2 fragTexCoord; \n" -"varying vec4 fragColor; \n" -#elif defined(GRAPHICS_API_OPENGL_33) -"#version 330 \n" -"in vec3 fragPosition; \n" -"in vec3 fragNormal; \n" -"in vec2 fragTexCoord; \n" -"in vec4 fragColor; \n" -"out vec4 finalColor; \n" -#endif -"uniform sampler2D texture0; \n" -"uniform sampler2D texture1; \n" -"uniform sampler2D texture2; \n" -"uniform vec4 colAmbient; \n" -"uniform vec4 colDiffuse; \n" -"uniform vec4 colSpecular; \n" -"uniform float glossiness; \n" -"uniform int useNormal; \n" -"uniform int useSpecular; \n" -"uniform mat4 modelMatrix; \n" -"uniform vec3 viewDir; \n" -"struct Light { \n" -" int enabled; \n" -" int type; \n" -" vec3 position; \n" -" vec3 direction; \n" -" vec4 diffuse; \n" -" float intensity; \n" -" float radius; \n" -" float coneAngle; }; \n" -"const int maxLights = 8; \n" -"uniform Light lights[maxLights]; \n" -"\n" -"vec3 CalcPointLight(Light l, vec3 n, vec3 v, float s) \n" -"{\n" -" vec3 surfacePos = vec3(modelMatrix*vec4(fragPosition, 1));\n" -" vec3 surfaceToLight = l.position - surfacePos;\n" -" float brightness = clamp(float(dot(n, surfaceToLight)/(length(surfaceToLight)*length(n))), 0.0, 1.0);\n" -" float diff = 1.0/dot(surfaceToLight/l.radius, surfaceToLight/l.radius)*brightness*l.intensity;\n" -" float spec = 0.0;\n" -" if (diff > 0.0)\n" -" {\n" -" vec3 h = normalize(-l.direction + v);\n" -" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n" -" }\n" -" return (diff*l.diffuse.rgb + spec*colSpecular.rgb);\n" -"}\n" -"\n" -"vec3 CalcDirectionalLight(Light l, vec3 n, vec3 v, float s)\n" -"{\n" -" vec3 lightDir = normalize(-l.direction);\n" -" float diff = clamp(float(dot(n, lightDir)), 0.0, 1.0)*l.intensity;\n" -" float spec = 0.0;\n" -" if (diff > 0.0)\n" -" {\n" -" vec3 h = normalize(lightDir + v);\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" -"\n" -"vec3 CalcSpotLight(Light l, vec3 n, vec3 v, float s)\n" -"{\n" -" vec3 surfacePos = vec3(modelMatrix*vec4(fragPosition, 1.0));\n" -" vec3 lightToSurface = normalize(surfacePos - l.position);\n" -" vec3 lightDir = normalize(-l.direction);\n" -" float diff = clamp(float(dot(n, lightDir)), 0.0, 1.0)*l.intensity;\n" -" float attenuation = clamp(float(dot(n, lightToSurface)), 0.0, 1.0);\n" -" attenuation = dot(lightToSurface, -lightDir);\n" -" float lightToSurfaceAngle = degrees(acos(attenuation));\n" -" if (lightToSurfaceAngle > l.coneAngle) attenuation = 0.0;\n" -" float falloff = (l.coneAngle - lightToSurfaceAngle)/l.coneAngle;\n" -" float diffAttenuation = diff*attenuation;\n" -" float spec = 0.0;\n" -" if (diffAttenuation > 0.0)\n" -" {\n" -" vec3 h = normalize(lightDir + v);\n" -" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n" -" }\n" -" return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));\n" -"}\n" -"\n" -"void main()\n" -"{\n" -" mat3 normalMatrix = mat3(modelMatrix);\n" -" vec3 normal = normalize(normalMatrix*fragNormal);\n" -" vec3 n = normalize(normal);\n" -" vec3 v = normalize(viewDir);\n" -#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) -" vec4 texelColor = texture2D(texture0, fragTexCoord);\n" -#elif defined(GRAPHICS_API_OPENGL_33) -" vec4 texelColor = texture(texture0, fragTexCoord);\n" -#endif -" vec3 lighting = colAmbient.rgb;\n" -" if (useNormal == 1)\n" -" {\n" -#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) -" n *= texture2D(texture1, fragTexCoord).rgb;\n" -#elif defined(GRAPHICS_API_OPENGL_33) -" n *= texture(texture1, fragTexCoord).rgb;\n" -#endif -" n = normalize(n);\n" -" }\n" -" float spec = 1.0;\n" -#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) -" if (useSpecular == 1) spec = texture2D(texture2, fragTexCoord).r;\n" -#elif defined(GRAPHICS_API_OPENGL_33) -" if (useSpecular == 1) spec = texture(texture2, fragTexCoord).r;\n" -#endif -" for (int i = 0; i < maxLights; i++)\n" -" {\n" -" if (lights[i].enabled == 1)\n" -" {\n" -" if(lights[i].type == 0) lighting += CalcPointLight(lights[i], n, v, spec);\n" -" else if(lights[i].type == 1) lighting += CalcDirectionalLight(lights[i], n, v, spec);\n" -" else if(lights[i].type == 2) lighting += CalcSpotLight(lights[i], n, v, spec);\n" -" }\n" -" }\n" -#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21) -" gl_FragColor = vec4(texelColor.rgb*lighting*colDiffuse.rgb, texelColor.a*colDiffuse.a); \n" -#elif defined(GRAPHICS_API_OPENGL_33) -" finalColor = vec4(texelColor.rgb*lighting*colDiffuse.rgb, texelColor.a*colDiffuse.a); \n" -#endif -"}\n"; diff --git a/src/shapes.c b/src/shapes.c index 70aad59a..9cbe1da4 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -1,14 +1,17 @@ /********************************************************************************************** * -* raylib.shapes +* raylib.shapes - Basic functions to draw 2d Shapes and check collisions * -* Basic functions to draw 2d Shapes and check collisions +* CONFIGURATION: * -* External libs: -* rlgl - raylib OpenGL abstraction layer +* #define SUPPORT_QUADS_ONLY +* Draw shapes using only QUADS, vertex are accumulated in QUADS arrays (like textures) * -* Module Configuration Flags: -* ... +* #define SUPPORT_TRIANGLES_ONLY +* Draw shapes using only TRIANGLES, vertex are accumulated in TRIANGLES arrays +* +* +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -100,6 +103,36 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color) rlEnd(); } +// Draw a line defining thickness +void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) +{ + float dx = endPos.x - startPos.x; + float dy = endPos.y - startPos.y; + + float d = sqrtf(dx*dx + dy*dy); + float angle = asinf(dy/d); + + rlEnableTexture(GetDefaultTexture().id); + + rlPushMatrix(); + rlTranslatef((float)startPos.x, (float)startPos.y, 0); + rlRotatef(-RAD2DEG*angle, 0, 0, 1); + rlTranslatef(0, -thick/2.0f, 0); + + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + rlNormal3f(0.0f, 0.0f, 1.0f); + + rlVertex2f(0.0f, 0.0f); + rlVertex2f(0.0f, thick); + rlVertex2f(d, thick); + rlVertex2f(d, 0.0f); + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); +} + // Draw a color-filled circle void DrawCircle(int centerX, int centerY, float radius, Color color) { @@ -190,6 +223,29 @@ void DrawRectangleRec(Rectangle rec, Color color) DrawRectangle(rec.x, rec.y, rec.width, rec.height, color); } +void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color) +{ + rlEnableTexture(GetDefaultTexture().id); + + rlPushMatrix(); + rlTranslatef((float)rec.x, (float)rec.y, 0); + rlRotatef(rotation, 0, 0, 1); + rlTranslatef(-origin.x, -origin.y, 0); + + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + rlVertex2f(0.0f, 0.0f); + rlVertex2f(0.0f, (float)rec.height); + rlVertex2f((float)rec.width, (float)rec.height); + rlVertex2f((float)rec.width, 0.0f); + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); +} + // Draw a gradient-filled rectangle // NOTE: Gradient goes from bottom (color1) to top (color2) void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2) @@ -453,16 +509,17 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec) int recCenterX = rec.x + rec.width/2; int recCenterY = rec.y + rec.height/2; - float dx = fabs(center.x - recCenterX); - float dy = fabs(center.y - recCenterY); + float dx = fabsf(center.x - recCenterX); + float dy = fabsf(center.y - recCenterY); - if (dx > (rec.width/2 + radius)) { return false; } - if (dy > (rec.height/2 + radius)) { return false; } + if (dx > ((float)rec.width/2.0f + radius)) { return false; } + if (dy > ((float)rec.height/2.0f + radius)) { return false; } - if (dx <= (rec.width/2)) { return true; } - if (dy <= (rec.height/2)) { return true; } + if (dx <= ((float)rec.width/2.0f)) { return true; } + if (dy <= ((float)rec.height/2.0f)) { return true; } - float cornerDistanceSq = (dx - rec.width/2)*(dx - rec.width/2) + (dy - rec.height/2)*(dy - rec.height/2); + float cornerDistanceSq = (dx - (float)rec.width/2.0f)*(dx - (float)rec.width/2.0f) + + (dy - (float)rec.height/2.0f)*(dy - (float)rec.height/2.0f); return (cornerDistanceSq <= (radius*radius)); } @@ -532,4 +589,4 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) } return retRec; -}
\ No newline at end of file +} @@ -1,14 +1,22 @@ /********************************************************************************************** * -* raylib.text +* raylib.text - Basic functions to load SpriteFonts and draw Text * -* Basic functions to load SpriteFonts and draw Text +* CONFIGURATION: * -* External libs: +* #define SUPPORT_FILEFORMAT_FNT +* #define SUPPORT_FILEFORMAT_TTF / INCLUDE_STB_TRUETYPE +* #define SUPPORT_FILEFORMAT_IMAGE_FONT +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module +* +* #define INCLUDE_DEFAULT_FONT / SUPPORT_DEFAULT_FONT +* +* DEPENDENCIES: * stb_truetype - Load TTF file and rasterize characters data * -* Module Configuration Flags: -* ... +* +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -36,7 +44,7 @@ #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() #include <stdio.h> // Required for: FILE, fopen(), fclose(), fscanf(), feof(), rewind(), fgets() -#include "utils.h" // Required for: GetExtension(), GetNextPOT() +#include "utils.h" // Required for: GetExtension() // Following libs are used on LoadTTF() #define STBTT_STATIC // Define stb_truetype functions static to this module @@ -53,7 +61,7 @@ #define MAX_FORMATTEXT_LENGTH 64 #define MAX_SUBTEXT_LENGTH 64 -#define BIT_CHECK(a,b) ((a) & (1<<(b))) +#define BIT_CHECK(a,b) ((a) & (1 << (b))) //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -79,7 +87,7 @@ static int GetCharIndex(SpriteFont font, int letter); 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, int numChars, int *fontChars); // Load spritefont from TTF data +static SpriteFont LoadTTF(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load spritefont from TTF data extern void LoadDefaultFont(void); extern void UnloadDefaultFont(void); @@ -92,7 +100,7 @@ extern void LoadDefaultFont(void) // NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement // http://www.utf8-chartable.de/unicode-utf8-table.pl - defaultFont.numChars = 224; // Number of chars included in our default font + defaultFont.charsCount = 224; // Number of chars included in our default font // Default font is directly defined here (data generated from a sprite font image) // This way, we reconstruct SpriteFont without creating large global variables @@ -189,29 +197,27 @@ extern void LoadDefaultFont(void) defaultFont.texture = LoadTextureFromImage(image); UnloadImage(image); - // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars + // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount //------------------------------------------------------------------------------ - 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)); + + // Allocate space for our characters info data + // NOTE: This memory should be freed at end! --> CloseWindow() + defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo)); int currentLine = 0; int currentPosX = charsDivisor; int testPosX = charsDivisor; - for (int i = 0; i < defaultFont.numChars; i++) + for (int i = 0; i < defaultFont.charsCount; i++) { - defaultFont.charValues[i] = 32 + i; // First char is 32 + defaultFont.chars[i].value = 32 + i; // First char is 32 - defaultFont.charRecs[i].x = currentPosX; - defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor); - defaultFont.charRecs[i].width = charsWidth[i]; - defaultFont.charRecs[i].height = charsHeight; + defaultFont.chars[i].rec.x = currentPosX; + defaultFont.chars[i].rec.y = charsDivisor + currentLine*(charsHeight + charsDivisor); + defaultFont.chars[i].rec.width = charsWidth[i]; + defaultFont.chars[i].rec.height = charsHeight; - testPosX += (defaultFont.charRecs[i].width + charsDivisor); + testPosX += (defaultFont.chars[i].rec.width + charsDivisor); if (testPosX >= defaultFont.texture.width) { @@ -219,17 +225,18 @@ extern void LoadDefaultFont(void) currentPosX = 2*charsDivisor + charsWidth[i]; testPosX = currentPosX; - defaultFont.charRecs[i].x = charsDivisor; - defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor); + defaultFont.chars[i].rec.x = charsDivisor; + defaultFont.chars[i].rec.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.chars[i].offsetX = 0; + defaultFont.chars[i].offsetY = 0; + defaultFont.chars[i].advanceX = 0; } - defaultFont.size = defaultFont.charRecs[0].height; + defaultFont.baseSize = defaultFont.chars[0].rec.height; TraceLog(INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id); } @@ -237,10 +244,7 @@ extern void LoadDefaultFont(void) extern void UnloadDefaultFont(void) { UnloadTexture(defaultFont.texture); - free(defaultFont.charValues); - free(defaultFont.charRecs); - free(defaultFont.charOffsets); - free(defaultFont.charAdvanceX); + free(defaultFont.chars); } // Get the default font, useful to be used with extended parameters @@ -249,7 +253,7 @@ SpriteFont GetDefaultFont() return defaultFont; } -// Load a SpriteFont image into GPU memory +// Load SpriteFont from file into GPU memory (VRAM) SpriteFont LoadSpriteFont(const char *fileName) { // Default hardcoded values for ttf file loading @@ -260,9 +264,35 @@ SpriteFont LoadSpriteFont(const char *fileName) SpriteFont spriteFont = { 0 }; // Check file extension - if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName); + if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName); // TODO: DELETE... SOON... else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadSpriteFontTTF(fileName, DEFAULT_TTF_FONTSIZE, 0, NULL); else if (strcmp(GetExtension(fileName),"fnt") == 0) spriteFont = LoadBMFont(fileName); + else if (strcmp(GetExtension(fileName),"rres") == 0) + { + // TODO: Read multiple resource blocks from file (RRES_FONT_IMAGE, RRES_FONT_CHARDATA) + RRES rres = LoadResource(fileName, 0); + + // Load sprite font texture + if (rres[0].type == RRES_TYPE_FONT_IMAGE) + { + // NOTE: Parameters for RRES_FONT_IMAGE type are: width, height, format, mipmaps + Image image = LoadImagePro(rres[0].data, rres[0].param1, rres[0].param2, rres[0].param3); + spriteFont.texture = LoadTextureFromImage(image); + UnloadImage(image); + } + + // Load sprite characters data + if (rres[1].type == RRES_TYPE_FONT_CHARDATA) + { + // NOTE: Parameters for RRES_FONT_CHARDATA type are: fontSize, charsCount + spriteFont.baseSize = rres[1].param1; + spriteFont.charsCount = rres[1].param2; + spriteFont.chars = rres[1].data; + } + + // TODO: Do not free rres.data memory (chars info data!) + //UnloadResource(rres[0]); + } else { Image image = LoadImage(fileName); @@ -280,26 +310,26 @@ SpriteFont LoadSpriteFont(const char *fileName) return spriteFont; } -// Load SpriteFont from TTF file with custom parameters +// Load SpriteFont from TTF font file with generation parameters // NOTE: You can pass an array with desired characters, those characters should be available in the font // if array is NULL, default char set is selected 32..126 -SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars) +SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int charsCount, int *fontChars) { SpriteFont spriteFont = { 0 }; - - if (strcmp(GetExtension(fileName),"ttf") == 0) + + if (strcmp(GetExtension(fileName),"ttf") == 0) { - if ((fontChars == NULL) || (numChars == 0)) + if ((fontChars == NULL) || (charsCount == 0)) { int totalChars = 95; // Default charset [32..126] - + int *defaultFontChars = (int *)malloc(totalChars*sizeof(int)); - + for (int i = 0; i < totalChars; i++) defaultFontChars[i] = i + 32; // Default first character: SPACE[32] - + spriteFont = LoadTTF(fileName, fontSize, totalChars, defaultFontChars); } - else spriteFont = LoadTTF(fileName, fontSize, numChars, fontChars); + else spriteFont = LoadTTF(fileName, fontSize, charsCount, fontChars); } if (spriteFont.texture.id == 0) @@ -311,17 +341,14 @@ SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, i return spriteFont; } -// Unload SpriteFont from GPU memory +// Unload SpriteFont from GPU memory (VRAM) void UnloadSpriteFont(SpriteFont spriteFont) { // NOTE: Make sure spriteFont is not default font (fallback) if (spriteFont.texture.id != defaultFont.texture.id) { UnloadTexture(spriteFont.texture); - free(spriteFont.charValues); - free(spriteFont.charRecs); - free(spriteFont.charOffsets); - free(spriteFont.charAdvanceX); + free(spriteFont.chars); TraceLog(DEBUG, "Unloaded sprite font data"); } @@ -353,11 +380,11 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float int textOffsetX = 0; // Offset between characters int textOffsetY = 0; // Required for line break! float scaleFactor; - + unsigned char letter; // Current character int index; // Index position in sprite font - - scaleFactor = fontSize/spriteFont.size; + + scaleFactor = fontSize/spriteFont.baseSize; // NOTE: Some ugly hacks are made to support Latin-1 Extended characters directly // written in C code files (codified by default as UTF-8) @@ -367,7 +394,7 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float if ((unsigned char)text[i] == '\n') { // NOTE: Fixed line spacing of 1.5 lines - textOffsetY += ((spriteFont.size + spriteFont.size/2)*scaleFactor); + textOffsetY += (int)((spriteFont.baseSize + spriteFont.baseSize/2)*scaleFactor); textOffsetX = 0; } else @@ -388,14 +415,14 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float } else index = GetCharIndex(spriteFont, (int)text[i]); - DrawTexturePro(spriteFont.texture, spriteFont.charRecs[index], - (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[index].x*scaleFactor, - position.y + textOffsetY + spriteFont.charOffsets[index].y*scaleFactor, - spriteFont.charRecs[index].width*scaleFactor, - spriteFont.charRecs[index].height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint); + DrawTexturePro(spriteFont.texture, spriteFont.chars[index].rec, + (Rectangle){ position.x + textOffsetX + spriteFont.chars[index].offsetX*scaleFactor, + position.y + textOffsetY + spriteFont.chars[index].offsetY*scaleFactor, + spriteFont.chars[index].rec.width*scaleFactor, + spriteFont.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint); - if (spriteFont.charAdvanceX[index] == 0) textOffsetX += (spriteFont.charRecs[index].width*scaleFactor + spacing); - else textOffsetX += (spriteFont.charAdvanceX[index]*scaleFactor + spacing); + if (spriteFont.chars[index].advanceX == 0) textOffsetX += (int)(spriteFont.chars[index].rec.width*scaleFactor + spacing); + else textOffsetX += (int)(spriteFont.chars[index].advanceX*scaleFactor + spacing); } } } @@ -460,14 +487,14 @@ int MeasureText(const char *text, int fontSize) Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, int spacing) { int len = strlen(text); - int tempLen = 0; // Used to count longer text line num chars + 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 + float textWidth = 0; + float tempTextWidth = 0; // Used to count longer text line width - int textHeight = spriteFont.size; - float scaleFactor = fontSize/spriteFont.size; + float textHeight = (float)spriteFont.baseSize; + float scaleFactor = fontSize/(float)spriteFont.baseSize; for (int i = 0; i < len; i++) { @@ -476,16 +503,16 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, i if (text[i] != '\n') { int index = GetCharIndex(spriteFont, (int)text[i]); - - if (spriteFont.charAdvanceX[index] != 0) textWidth += spriteFont.charAdvanceX[index]; - else textWidth += (spriteFont.charRecs[index].width + spriteFont.charOffsets[index].x); + + if (spriteFont.chars[index].advanceX != 0) textWidth += spriteFont.chars[index].advanceX; + else textWidth += (spriteFont.chars[index].rec.width + spriteFont.chars[index].offsetX); } else { if (tempTextWidth < textWidth) tempTextWidth = textWidth; lenCounter = 0; textWidth = 0; - textHeight += (spriteFont.size + spriteFont.size/2); // NOTE: Fixed line spacing of 1.5 lines + textHeight += ((float)spriteFont.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines } if (tempLen < lenCounter) tempLen = lenCounter; @@ -494,8 +521,8 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, i if (tempTextWidth < textWidth) tempTextWidth = textWidth; Vector2 vec; - vec.x = (float)tempTextWidth*scaleFactor + (tempLen - 1)*spacing; // Adds chars spacing to measure - vec.y = (float)textHeight*scaleFactor; + vec.x = tempTextWidth*scaleFactor + (float)((tempLen - 1)*spacing); // Adds chars spacing to measure + vec.y = textHeight*scaleFactor; return vec; } @@ -504,28 +531,22 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, i // NOTE: Uses default font void DrawFPS(int posX, int posY) { - char buffer[20]; - // NOTE: We are rendering fps every second for better viewing on high framerates - // TODO: Not working properly on ANDROID and RPI - static float fps = 0.0f; + static int fps = 0; static int counter = 0; static int refreshRate = 20; - if (counter < refreshRate) - { - counter++; - } + if (counter < refreshRate) counter++; else { fps = GetFPS(); refreshRate = fps; counter = 0; } - - sprintf(buffer, "%2.0f FPS", fps); - DrawText(buffer, posX, posY, 20, LIME); + + // NOTE: We have rounding errors every frame, so it oscillates a lot + DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME); } //---------------------------------------------------------------------------------- @@ -537,16 +558,16 @@ static int GetCharIndex(SpriteFont font, int letter) #define UNORDERED_CHARSET #if defined(UNORDERED_CHARSET) int index = 0; - - for (int i = 0; i < font.numChars; i++) + + for (int i = 0; i < font.charsCount; i++) { - if (font.charValues[i] == letter) + if (font.chars[i].value == letter) { index = i; break; } } - + return index; #else return (letter - 32); @@ -627,43 +648,41 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar) } TraceLog(DEBUG, "SpriteFont data parsed correctly from image"); - - // NOTE: We need to remove key color borders from image to avoid weird + + // NOTE: We need to remove key color borders from image to avoid weird // artifacts on texture scaling when using FILTER_BILINEAR or FILTER_TRILINEAR for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK; // Create a new image with the processed color data (key color replaced by BLANK) Image fontClear = LoadImageEx(pixels, image.width, image.height); - + free(pixels); // Free pixels array memory // Create spritefont with all data parsed from image SpriteFont spriteFont = { 0 }; spriteFont.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture - spriteFont.numChars = index; - + spriteFont.charsCount = index; + UnloadImage(fontClear); // Unload processed image once converted to texture // We got tempCharValues and tempCharsRecs populated with chars data // Now we move temp data to sized charValues and charRecs arrays - spriteFont.charRecs = (Rectangle *)malloc(spriteFont.numChars*sizeof(Rectangle)); - spriteFont.charValues = (int *)malloc(spriteFont.numChars*sizeof(int)); - spriteFont.charOffsets = (Vector2 *)malloc(spriteFont.numChars*sizeof(Vector2)); - spriteFont.charAdvanceX = (int *)malloc(spriteFont.numChars*sizeof(int)); + spriteFont.chars = (CharInfo *)malloc(spriteFont.charsCount*sizeof(CharInfo)); - for (int i = 0; i < spriteFont.numChars; i++) + for (int i = 0; i < spriteFont.charsCount; i++) { - spriteFont.charValues[i] = tempCharValues[i]; - spriteFont.charRecs[i] = tempCharRecs[i]; + spriteFont.chars[i].value = tempCharValues[i]; + spriteFont.chars[i].rec = 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.chars[i].offsetX = 0; + spriteFont.chars[i].offsetY = 0; + spriteFont.chars[i].advanceX = 0; } - spriteFont.size = spriteFont.charRecs[0].height; - + spriteFont.baseSize = spriteFont.chars[0].rec.height; + TraceLog(INFO, "Image file loaded correctly as SpriteFont"); return spriteFont; @@ -692,6 +711,8 @@ static SpriteFont LoadRBMF(const char *fileName) SpriteFont spriteFont = { 0 }; + // REMOVE SOON!!! +/* rbmfInfoHeader rbmfHeader; unsigned int *rbmfFileData = NULL; unsigned char *rbmfCharWidthData = NULL; @@ -757,29 +778,27 @@ 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.charRecs = (Rectangle *)malloc(spriteFont.numChars*sizeof(Rectangle)); - spriteFont.charOffsets = (Vector2 *)malloc(spriteFont.numChars*sizeof(Vector2)); - spriteFont.charAdvanceX = (int *)malloc(spriteFont.numChars*sizeof(int)); + spriteFont.chars = (CharInfo *)malloc(spriteFont.charsCount*sizeof(CharInfo)); int currentLine = 0; int currentPosX = charsDivisor; int testPosX = charsDivisor; - for (int i = 0; i < spriteFont.numChars; i++) + for (int i = 0; i < spriteFont.charsCount; i++) { - spriteFont.charValues[i] = (int)rbmfHeader.firstChar + i; + spriteFont.chars[i].value = (int)rbmfHeader.firstChar + i; - spriteFont.charRecs[i].x = currentPosX; - spriteFont.charRecs[i].y = charsDivisor + currentLine*((int)rbmfHeader.charHeight + charsDivisor); - spriteFont.charRecs[i].width = (int)rbmfCharWidthData[i]; - spriteFont.charRecs[i].height = (int)rbmfHeader.charHeight; + spriteFont.chars[i].rec.x = currentPosX; + spriteFont.chars[i].rec.y = charsDivisor + currentLine*((int)rbmfHeader.charHeight + charsDivisor); + spriteFont.chars[i].rec.width = (int)rbmfCharWidthData[i]; + spriteFont.chars[i].rec.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; + spriteFont.chars[i].offsetX = 0; + spriteFont.chars[i].offsetY = 0; + spriteFont.chars[i].advanceX = 0; - testPosX += (spriteFont.charRecs[i].width + charsDivisor); + testPosX += (spriteFont.chars[i].rec.width + charsDivisor); if (testPosX > spriteFont.texture.width) { @@ -787,13 +806,13 @@ static SpriteFont LoadRBMF(const char *fileName) currentPosX = 2*charsDivisor + (int)rbmfCharWidthData[i]; testPosX = currentPosX; - spriteFont.charRecs[i].x = charsDivisor; - spriteFont.charRecs[i].y = charsDivisor + currentLine*(rbmfHeader.charHeight + charsDivisor); + spriteFont.chars[i].rec.x = charsDivisor; + spriteFont.chars[i].rec.y = charsDivisor + currentLine*(rbmfHeader.charHeight + charsDivisor); } else currentPosX = testPosX; } - spriteFont.size = spriteFont.charRecs[0].height; + spriteFont.baseSize = spriteFont.charRecs[0].height; TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName); } @@ -802,6 +821,7 @@ static SpriteFont LoadRBMF(const char *fileName) free(rbmfFileData); // Now we can free loaded data from RAM memory free(rbmfCharWidthData); +*/ return spriteFont; } @@ -820,7 +840,7 @@ static SpriteFont LoadBMFont(const char *fileName) int fontSize = 0; int texWidth, texHeight; char texFileName[128]; - int numChars = 0; + int charsCount = 0; int base; // Useless data @@ -854,9 +874,9 @@ static SpriteFont LoadBMFont(const char *fileName) fgets(buffer, MAX_BUFFER_SIZE, fntFile); searchPoint = strstr(buffer, "count"); - sscanf(searchPoint, "count=%i", &numChars); + sscanf(searchPoint, "count=%i", &charsCount); - TraceLog(DEBUG, "[%s] Font num chars: %i", fileName, numChars); + TraceLog(DEBUG, "[%s] Font num chars: %i", fileName, charsCount); // Compose correct path using route of .fnt file (fileName) and texFileName char *texPath = NULL; @@ -873,13 +893,13 @@ static SpriteFont LoadBMFont(const char *fileName) strncat(texPath, texFileName, strlen(texFileName)); TraceLog(DEBUG, "[%s] Font texture loading path: %s", fileName, texPath); - + Image imFont = LoadImage(texPath); - - if (imFont.format == UNCOMPRESSED_GRAYSCALE) + + if (imFont.format == UNCOMPRESSED_GRAYSCALE) { Image imCopy = ImageCopy(imFont); - + for (int i = 0; i < imCopy.width*imCopy.height; i++) ((unsigned char *)imCopy.data)[i] = 0xff; // WHITE pixel ImageAlphaMask(&imCopy, imFont); @@ -887,31 +907,29 @@ static SpriteFont LoadBMFont(const char *fileName) UnloadImage(imCopy); } else font.texture = LoadTextureFromImage(imFont); - - font.size = fontSize; - font.numChars = numChars; - font.charValues = (int *)malloc(numChars*sizeof(int)); - font.charRecs = (Rectangle *)malloc(numChars*sizeof(Rectangle)); - font.charOffsets = (Vector2 *)malloc(numChars*sizeof(Vector2)); - font.charAdvanceX = (int *)malloc(numChars*sizeof(int)); + + font.baseSize = fontSize; + font.charsCount = charsCount; + font.chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); UnloadImage(imFont); - + free(texPath); int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX; - for (int i = 0; i < numChars; i++) + for (int i = 0; i < charsCount; i++) { fgets(buffer, MAX_BUFFER_SIZE, fntFile); sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); // 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; + font.chars[i].value = charId; + font.chars[i].rec = (Rectangle){ charX, charY, charWidth, charHeight }; + font.chars[i].offsetX = charOffsetX; + font.chars[i].offsetY = charOffsetY; + font.chars[i].advanceX = charAdvanceX; } fclose(fntFile); @@ -928,18 +946,23 @@ 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, int numChars, int *fontChars) +static SpriteFont LoadTTF(const char *fileName, int fontSize, int charsCount, int *fontChars) { + #define MAX_TTF_SIZE 16 // Maximum ttf file size in MB + // NOTE: Font texture size is predicted (being as much conservative as possible) // Predictive method consist of supposing same number of chars by line-column (sqrtf) // and a maximum character width of 3/4 of fontSize... it worked ok with all my tests... - int textureSize = GetNextPOT(ceil((float)fontSize*3/4)*ceil(sqrtf((float)numChars))); - + + // Calculate next power-of-two value + float guessSize = ceilf((float)fontSize*3/4)*ceilf(sqrtf((float)charsCount)); + int textureSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT + TraceLog(INFO, "TTF spritefont loading: Predicted texture size: %ix%i", textureSize, textureSize); - unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25); + unsigned char *ttfBuffer = (unsigned char *)malloc(MAX_TTF_SIZE*1024*1024); unsigned char *dataBitmap = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)); // One channel bitmap returned! - stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*numChars); + stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*charsCount); SpriteFont font = { 0 }; @@ -951,18 +974,19 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int numChars, int return font; } - fread(ttfBuffer, 1, 1<<25, ttfFile); - + // NOTE: We try reading up to 16 MB of elements of 1 byte + fread(ttfBuffer, 1, MAX_TTF_SIZE*1024*1024, ttfFile); + if (fontChars[0] != 32) TraceLog(WARNING, "TTF spritefont loading: first character is not SPACE(32) character"); // NOTE: Using stb_truetype crappy packing method, no guarante the font fits the image... // TODO: Replace this function by a proper packing method and support random chars order, // we already receive a list (fontChars) with the ordered expected characters - int result = stbtt_BakeFontBitmap(ttfBuffer, 0, fontSize, dataBitmap, textureSize, textureSize, fontChars[0], numChars, charData); + int result = stbtt_BakeFontBitmap(ttfBuffer, 0, fontSize, dataBitmap, textureSize, textureSize, fontChars[0], charsCount, charData); //if (result > 0) TraceLog(INFO, "TTF spritefont loading: first unused row of generated bitmap: %i", result); if (result < 0) TraceLog(WARNING, "TTF spritefont loading: Not all the characters fit in the font"); - + free(ttfBuffer); // Convert image data from grayscale to to UNCOMPRESSED_GRAY_ALPHA @@ -983,31 +1007,29 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int numChars, int image.mipmaps = 1; image.format = UNCOMPRESSED_GRAY_ALPHA; image.data = dataGrayAlpha; - + font.texture = LoadTextureFromImage(image); - - //WritePNG("generated_ttf_image.png", (unsigned char *)image.data, image.width, image.height, 2); - + + //SavePNG("generated_ttf_image.png", (unsigned char *)image.data, image.width, image.height, 2); + UnloadImage(image); // Unloads dataGrayAlpha - font.size = fontSize; - 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)); + font.baseSize = fontSize; + font.charsCount = charsCount; + font.chars = (CharInfo *)malloc(font.charsCount*sizeof(CharInfo)); - for (int i = 0; i < font.numChars; i++) + for (int i = 0; i < font.charsCount; i++) { - font.charValues[i] = fontChars[i]; + font.chars[i].value = fontChars[i]; - 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.chars[i].rec.x = (int)charData[i].x0; + font.chars[i].rec.y = (int)charData[i].y0; + font.chars[i].rec.width = (int)charData[i].x1 - (int)charData[i].x0; + font.chars[i].rec.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; + font.chars[i].offsetX = charData[i].xoff; + font.chars[i].offsetY = charData[i].yoff; + font.chars[i].advanceX = (int)charData[i].xadvance; } free(charData); diff --git a/src/textures.c b/src/textures.c index af59d035..7db3bf56 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1,16 +1,35 @@ /********************************************************************************************** * -* raylib.textures +* raylib.textures - Basic functions to load and draw Textures (2d) * -* Basic functions to load and draw Textures (2d) +* CONFIGURATION: * -* External libs: +* #define SUPPORT_STB_IMAGE / INCLUDE_STB_IMAGE +* +* #define SUPPORT_FILEFORMAT_BMP / SUPPORT_LOAD_BMP +* #define SUPPORT_FILEFORMAT_PNG / SUPPORT_LOAD_PNG +* #define SUPPORT_FILEFORMAT_TGA +* #define SUPPORT_FILEFORMAT_JPG / ENABLE_LOAD_JPG +* #define SUPPORT_FILEFORMAT_GIF +* #define SUPPORT_FILEFORMAT_HDR +* #define SUPPORT_FILEFORMAT_DDS / ENABLE_LOAD_DDS +* #define SUPPORT_FILEFORMAT_PKM +* #define SUPPORT_FILEFORMAT_KTX +* #define SUPPORT_FILEFORMAT_PVR +* #define SUPPORT_FILEFORMAT_ASTC +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module +* +* #define SUPPORT_IMAGE_RESIZE / INCLUDE_STB_IMAGE_RESIZE +* #define SUPPORT_IMAGE_MANIPULATION +* +* DEPENDENCIES: * stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) * NOTE: stb_image has been slightly modified to support Android platform. * stb_image_resize - Multiple image resize algorythms * -* Module Configuration Flags: -* ... +* +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -37,11 +56,10 @@ #include <string.h> // Required for: strcmp(), strrchr(), strncmp() #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 - // Required: rlglLoadTexture() rlDeleteTextures(), - // rlglGenerateMipmaps(), some funcs for DrawTexturePro() + // Required for: rlglLoadTexture() rlDeleteTextures(), + // rlglGenerateMipmaps(), some funcs for DrawTexturePro() -#include "utils.h" // rRES data decompression utility function - // NOTE: Includes Android fopen function map +#include "utils.h" // Required for: fopen() Android mapping, TraceLog() // Support only desired texture formats, by default: JPEG, PNG, BMP, TGA //#define STBI_NO_JPEG // Image format .jpg and .jpeg @@ -94,7 +112,7 @@ static Image LoadASTC(const char *fileName); // Load ASTC file // Module Functions Definition //---------------------------------------------------------------------------------- -// Load an image into CPU memory (RAM) +// Load image from file into CPU memory (RAM) Image LoadImage(const char *fileName) { Image image; @@ -142,17 +160,25 @@ 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) + else if (strcmp(GetExtension(fileName),"rres") == 0) { - TraceLog(INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height); + RRES rres = LoadResource(fileName, 0); + + // NOTE: Parameters for RRES_TYPE_IMAGE are: width, height, format, mipmaps + + if (rres[0].type == RRES_TYPE_IMAGE) image = LoadImagePro(rres[0].data, rres[0].param1, rres[0].param2, rres[0].param3); + else TraceLog(WARNING, "[%s] Resource file does not contain image data", fileName); + + UnloadResource(rres); } - else TraceLog(WARNING, "[%s] Image could not be loaded, file not recognized", 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", fileName); return image; } -// Load image data from Color array data (RGBA - 32bit) +// Load image from Color array data (RGBA - 32bit) // NOTE: Creates a copy of pixels data array Image LoadImageEx(Color *pixels, int width, int height) { @@ -179,7 +205,24 @@ Image LoadImageEx(Color *pixels, int width, int height) return image; } -// Load an image from RAW file +// Load image from raw data with parameters +// NOTE: This functions makes a copy of provided data +Image LoadImagePro(void *data, int width, int height, int format) +{ + Image srcImage = { 0 }; + + srcImage.data = data; + srcImage.width = width; + srcImage.height = height; + srcImage.mipmaps = 1; + srcImage.format = format; + + Image dstImage = ImageCopy(srcImage); + + return dstImage; +} + +// Load an image from RAW file data Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize) { Image image; @@ -214,134 +257,32 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int default: TraceLog(WARNING, "Image format not suported"); break; } - fread(image.data, size, 1, rawFile); - - // TODO: Check if data have been read + // NOTE: fread() returns num read elements instead of bytes, + // to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element) + int bytes = fread(image.data, 1, size, rawFile); - image.width = width; - image.height = height; - image.mipmaps = 0; - image.format = format; - - fclose(rawFile); - } - - return image; -} - -// Load an image from rRES file (raylib Resource) -// TODO: Review function to support multiple color modes -Image LoadImageFromRES(const char *rresName, int resId) -{ - Image image = { 0 }; - bool found = false; - - char id[4]; // rRES file identifier - unsigned char version; // rRES file version and subversion - char useless; // rRES header reserved data - short numRes; - - ResInfoHeader infoHeader; - - FILE *rresFile = fopen(rresName, "rb"); - - if (rresFile == NULL) - { - TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); - } - else - { - // Read rres file (basic file check - id) - fread(&id[0], sizeof(char), 1, rresFile); - fread(&id[1], sizeof(char), 1, rresFile); - fread(&id[2], sizeof(char), 1, rresFile); - fread(&id[3], sizeof(char), 1, rresFile); - fread(&version, sizeof(char), 1, rresFile); - fread(&useless, sizeof(char), 1, rresFile); - - if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) + // Check if data has been read successfully + if (bytes < size) { - TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); + TraceLog(WARNING, "[%s] RAW image data can not be read, wrong requested format or size", fileName); + + if (image.data != NULL) free(image.data); } else { - // Read number of resources embedded - fread(&numRes, sizeof(short), 1, rresFile); - - for (int i = 0; i < numRes; i++) - { - fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile); - - if (infoHeader.id == resId) - { - found = true; - - // Check data is of valid IMAGE type - if (infoHeader.type == 0) // IMAGE data type - { - // TODO: Check data compression type - // NOTE: We suppose compression type 2 (DEFLATE - default) - - short imgWidth, imgHeight; - char colorFormat, mipmaps; - - fread(&imgWidth, sizeof(short), 1, rresFile); // Image width - fread(&imgHeight, sizeof(short), 1, rresFile); // Image height - fread(&colorFormat, 1, 1, rresFile); // Image data color format (default: RGBA 32 bit) - fread(&mipmaps, 1, 1, rresFile); // Mipmap images included (default: 0) - - image.width = (int)imgWidth; - image.height = (int)imgHeight; - - unsigned char *compData = malloc(infoHeader.size); - - fread(compData, infoHeader.size, 1, rresFile); - - unsigned char *imgData = DecompressData(compData, infoHeader.size, infoHeader.srcSize); - - // TODO: Review color mode - //image.data = (unsigned char *)malloc(sizeof(unsigned char)*imgWidth*imgHeight*4); - image.data = imgData; - - //free(imgData); - - free(compData); - - TraceLog(INFO, "[%s] Image loaded successfully from resource, size: %ix%i", rresName, image.width, image.height); - } - else - { - TraceLog(WARNING, "[%s] Required resource do not seem to be a valid IMAGE resource", rresName); - } - } - else - { - // Depending on type, skip the right amount of parameters - switch (infoHeader.type) - { - case 0: fseek(rresFile, 6, SEEK_CUR); break; // IMAGE: Jump 6 bytes of parameters - case 1: fseek(rresFile, 6, SEEK_CUR); break; // SOUND: Jump 6 bytes of parameters - case 2: fseek(rresFile, 5, SEEK_CUR); break; // MODEL: Jump 5 bytes of parameters (TODO: Review) - case 3: break; // TEXT: No parameters - case 4: break; // RAW: No parameters - default: break; - } - - // Jump DATA to read next infoHeader - fseek(rresFile, infoHeader.size, SEEK_CUR); - } - } + image.width = width; + image.height = height; + image.mipmaps = 0; + image.format = format; } - fclose(rresFile); + fclose(rawFile); } - if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId); - return image; } -// Load an image as texture into GPU memory +// Load texture from file into GPU memory (VRAM) Texture2D LoadTexture(const char *fileName) { Texture2D texture; @@ -362,33 +303,6 @@ Texture2D LoadTexture(const char *fileName) return texture; } -// Load a texture from raw data into GPU memory -Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat) -{ - Texture2D texture; - - texture.width = width; - texture.height = height; - texture.mipmaps = 1; - texture.format = textureFormat; - - texture.id = rlglLoadTexture(data, width, height, textureFormat, 1); - - return texture; -} - -// Load an image as texture from rRES file (raylib Resource) -Texture2D LoadTextureFromRES(const char *rresName, int resId) -{ - Texture2D texture; - - Image image = LoadImageFromRES(rresName, resId); - texture = LoadTextureFromImage(image); - UnloadImage(image); - - return texture; -} - // Load a texture from image data // NOTE: image is not unloaded, it must be done manually Texture2D LoadTextureFromImage(Image image) @@ -412,7 +326,7 @@ Texture2D LoadTextureFromImage(Image image) return texture; } -// Load a texture to be used for rendering +// Load texture for rendering (framebuffer) RenderTexture2D LoadRenderTexture(int width, int height) { RenderTexture2D target = rlglLoadRenderTexture(width, height); @@ -429,7 +343,7 @@ void UnloadImage(Image image) //TraceLog(INFO, "Unloaded image data"); } -// Unload texture from GPU memory +// Unload texture from GPU memory (VRAM) void UnloadTexture(Texture2D texture) { if (texture.id != 0) @@ -440,102 +354,12 @@ void UnloadTexture(Texture2D texture) } } -// Unload render texture from GPU memory +// Unload render texture from GPU memory (VRAM) void UnloadRenderTexture(RenderTexture2D target) { if (target.id != 0) rlDeleteRenderTextures(target); } -// Set texture scaling filter mode -void SetTextureFilter(Texture2D texture, int filterMode) -{ - switch (filterMode) - { - case FILTER_POINT: - { - if (texture.mipmaps > 1) - { - // RL_FILTER_MIP_NEAREST - tex filter: POINT, mipmaps filter: POINT (sharp switching between mipmaps) - rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_MIP_NEAREST); - - // RL_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps - rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_NEAREST); - } - else - { - // RL_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps - rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_NEAREST); - rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_NEAREST); - } - } break; - case FILTER_BILINEAR: - { - if (texture.mipmaps > 1) - { - // RL_FILTER_LINEAR_MIP_NEAREST - tex filter: BILINEAR, mipmaps filter: POINT (sharp switching between mipmaps) - // Alternative: RL_FILTER_NEAREST_MIP_LINEAR - tex filter: POINT, mipmaps filter: BILINEAR (smooth transition between mipmaps) - rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR_MIP_NEAREST); - - // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); - } - else - { - // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR); - rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); - } - } break; - case FILTER_TRILINEAR: - { - if (texture.mipmaps > 1) - { - // RL_FILTER_MIP_LINEAR - tex filter: BILINEAR, mipmaps filter: BILINEAR (smooth transition between mipmaps) - rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_MIP_LINEAR); - - // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); - } - else - { - TraceLog(WARNING, "[TEX ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id); - - // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR); - rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); - } - } break; - case FILTER_ANISOTROPIC_4X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 4); break; - case FILTER_ANISOTROPIC_8X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 8); break; - case FILTER_ANISOTROPIC_16X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 16); break; - default: break; - } -} - -// Set texture wrapping mode -void SetTextureWrap(Texture2D texture, int wrapMode) -{ - switch (wrapMode) - { - case WRAP_REPEAT: - { - rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_REPEAT); - rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_REPEAT); - } break; - case WRAP_CLAMP: - { - rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_CLAMP); - rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_CLAMP); - } break; - case WRAP_MIRROR: - { - rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_CLAMP_MIRROR); - rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_CLAMP_MIRROR); - } break; - default: break; - } -} - // Get pixel data from image in the form of Color struct array Color *GetImageData(Image image) { @@ -654,6 +478,13 @@ Image GetTextureData(Texture2D texture) return image; } +// Update GPU texture with new data +// NOTE: pixels data must match texture.format +void UpdateTexture(Texture2D texture, const void *pixels) +{ + rlglUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels); +} + // Convert image data to desired format void ImageFormat(Image *image, int newFormat) { @@ -805,12 +636,12 @@ void ImageAlphaMask(Image *image, Image alphaMask) // Force mask to be Grayscale Image mask = ImageCopy(alphaMask); if (mask.format != UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, UNCOMPRESSED_GRAYSCALE); - + // In case image is only grayscale, we just add alpha channel if (image->format == UNCOMPRESSED_GRAYSCALE) { ImageFormat(image, UNCOMPRESSED_GRAY_ALPHA); - + // Apply alpha mask to alpha channel for (int i = 0, k = 1; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2) { @@ -872,11 +703,11 @@ 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; + 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 + int rError, gError, bError; + unsigned short rPixel, gPixel, bPixel, aPixel; // Used for 16bit pixel composition #define MIN(a,b) (((a)<(b))?(a):(b)) @@ -884,57 +715,57 @@ 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]; + 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 - newpixel.b = oldpixel.b>>(8 - bBpp); // B bits - newpixel.a = oldpixel.a>>(8 - aBpp); // A bits (not used on dithering) + newPixel.r = oldPixel.r >> (8 - rBpp); // R bits + newPixel.g = oldPixel.g >> (8 - gBpp); // G bits + newPixel.b = oldPixel.b >> (8 - bBpp); // B bits + newPixel.a = oldPixel.a >> (8 - aBpp); // A bits (not used on dithering) // NOTE: Error must be computed between new and old pixel but using same number of bits! // We want to know how much color precision we have lost... - 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)); + rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp)); + gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp)); + bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp)); - pixels[y*image->width + x] = newpixel; + pixels[y*image->width + x] = newPixel; // NOTE: Some cases are out of the array and should be ignored if (x < (image->width - 1)) { - pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)error_r*7.0f/16), 0xff); - 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); + pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)rError*7.0f/16), 0xff); + pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)gError*7.0f/16), 0xff); + pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)bError*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); + pixels[(y+1)*image->width + x-1].r = MIN((int)pixels[(y+1)*image->width + x-1].r + (int)((float)rError*3.0f/16), 0xff); + pixels[(y+1)*image->width + x-1].g = MIN((int)pixels[(y+1)*image->width + x-1].g + (int)((float)gError*3.0f/16), 0xff); + pixels[(y+1)*image->width + x-1].b = MIN((int)pixels[(y+1)*image->width + x-1].b + (int)((float)bError*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); + pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)rError*5.0f/16), 0xff); + pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)gError*5.0f/16), 0xff); + pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)bError*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); - pixels[(y+1)*image->width + x+1].g = MIN((int)pixels[(y+1)*image->width + x+1].g + (int)((float)error_g*1.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*1.0f/16), 0xff); + pixels[(y+1)*image->width + x+1].r = MIN((int)pixels[(y+1)*image->width + x+1].r + (int)((float)rError*1.0f/16), 0xff); + pixels[(y+1)*image->width + x+1].g = MIN((int)pixels[(y+1)*image->width + x+1].g + (int)((float)gError*1.0f/16), 0xff); + pixels[(y+1)*image->width + x+1].b = MIN((int)pixels[(y+1)*image->width + x+1].b + (int)((float)bError*1.0f/16), 0xff); } - pixel_r = (unsigned short)newpixel.r; - pixel_g = (unsigned short)newpixel.g; - pixel_b = (unsigned short)newpixel.b; - pixel_a = (unsigned short)newpixel.a; + rPixel = (unsigned short)newPixel.r; + gPixel = (unsigned short)newPixel.g; + bPixel = (unsigned short)newPixel.b; + aPixel = (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; + ((unsigned short *)image->data)[y*image->width + x] = (rPixel << (gBpp + bBpp + aBpp)) | (gPixel << (bBpp + aBpp)) | (bPixel << aBpp) | aPixel; } } @@ -948,9 +779,10 @@ 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); + // Calculate next power-of-two values + // NOTE: Just add the required amount of pixels at the right and bottom sides of image... + int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2))); + int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2))); // Check if POT texture generation is required (if texture is not already POT) if ((potWidth != image->width) || (potHeight != image->height)) @@ -991,24 +823,37 @@ Image ImageCopy(Image image) { Image newImage; - int size = image.width*image.height; + int byteSize = image.width*image.height; switch (image.format) { - case UNCOMPRESSED_GRAYSCALE: newImage.data = (unsigned char *)malloc(size); break; // 8 bit per pixel (no alpha) - case UNCOMPRESSED_GRAY_ALPHA: newImage.data = (unsigned char *)malloc(size*2); size *= 2; break; // 16 bpp (2 channels) - case UNCOMPRESSED_R5G6B5: newImage.data = (unsigned short *)malloc(size); size *= 2; break; // 16 bpp - case UNCOMPRESSED_R8G8B8: newImage.data = (unsigned char *)malloc(size*3); size *= 3; break; // 24 bpp - case UNCOMPRESSED_R5G5B5A1: newImage.data = (unsigned short *)malloc(size); size *= 2; break; // 16 bpp (1 bit alpha) - case UNCOMPRESSED_R4G4B4A4: newImage.data = (unsigned short *)malloc(size); size *= 2; break; // 16 bpp (4 bit alpha) - 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; + case UNCOMPRESSED_GRAYSCALE: break; // 8 bpp (1 byte) + case UNCOMPRESSED_GRAY_ALPHA: // 16 bpp + case UNCOMPRESSED_R5G6B5: // 16 bpp + case UNCOMPRESSED_R5G5B5A1: // 16 bpp + case UNCOMPRESSED_R4G4B4A4: byteSize *= 2; break; // 16 bpp (2 bytes) + case UNCOMPRESSED_R8G8B8: byteSize *= 3; break; // 24 bpp (3 bytes) + case UNCOMPRESSED_R8G8B8A8: byteSize *= 4; break; // 32 bpp (4 bytes) + case COMPRESSED_DXT3_RGBA: + case COMPRESSED_DXT5_RGBA: + case COMPRESSED_ETC2_EAC_RGBA: + case COMPRESSED_ASTC_4x4_RGBA: break; // 8 bpp (1 byte) + case COMPRESSED_DXT1_RGB: + case COMPRESSED_DXT1_RGBA: + case COMPRESSED_ETC1_RGB: + case COMPRESSED_ETC2_RGB: + case COMPRESSED_PVRT_RGB: + case COMPRESSED_PVRT_RGBA: byteSize /= 2; break; // 4 bpp + case COMPRESSED_ASTC_8x8_RGBA: byteSize /= 4; break;// 2 bpp + default: TraceLog(WARNING, "Image format not recognized"); break; } + newImage.data = malloc(byteSize); + if (newImage.data != NULL) { // NOTE: Size must be provided in bytes - memcpy(newImage.data, image.data, size); + memcpy(newImage.data, image.data, byteSize); newImage.width = image.width; newImage.height = image.height; @@ -1100,18 +945,18 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight) 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 xRatio = (int)((image->width << 16)/newWidth) + 1; + int yRatio = (int)((image->height << 16)/newHeight) + 1; int x2, y2; - for (int i = 0; i < newHeight; i++) + for (int y = 0; y < newHeight; y++) { - for (int j = 0; j < newWidth; j++) + for (int x = 0; x < newWidth; x++) { - x2 = ((j*x_ratio) >> 16); - y2 = ((i*y_ratio) >> 16); + x2 = ((x*xRatio) >> 16); + y2 = ((y*yRatio) >> 16); - output[(i*newWidth) + j] = pixels[(y2*image->width) + x2] ; + output[(y*newWidth) + x] = pixels[(y2*image->width) + x2] ; } } @@ -1131,7 +976,7 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight) 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; @@ -1149,15 +994,15 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) 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)) { @@ -1177,24 +1022,24 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) TraceLog(WARNING, "Destination rectangle height out of bounds, rescaled height: %i", dstRec.height); cropRequired = true; } - + if (cropRequired) { // Crop destination rectangle if out of bounds Rectangle crop = { 0, 0, dstRec.width, dstRec.height }; ImageCrop(&srcCopy, crop); } - + // 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... - + 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... + // TODO: Probably out-of-bounds blitting could be considered 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++) @@ -1202,13 +1047,13 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) // 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 } } @@ -1240,7 +1085,7 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing int length = strlen(text); int posX = 0; - Vector2 imSize = MeasureTextEx(font, text, font.size, spacing); + Vector2 imSize = MeasureTextEx(font, text, font.baseSize, spacing); // NOTE: GetTextureData() not available in OpenGL ES Image imFont = GetTextureData(font.texture); @@ -1256,7 +1101,7 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing for (int i = 0; i < length; i++) { - Rectangle letterRec = font.charRecs[(int)text[i] - 32]; + Rectangle letterRec = font.chars[(int)text[i] - 32].rec; for (int y = letterRec.y; y < (letterRec.y + letterRec.height); y++) { @@ -1516,14 +1361,15 @@ void ImageColorBrightness(Image *image, int brightness) } // Generate GPU mipmaps for a texture -void GenTextureMipmaps(Texture2D texture) +void GenTextureMipmaps(Texture2D *texture) { #if PLATFORM_WEB - int potWidth = GetNextPOT(texture.width); - int potHeight = GetNextPOT(texture.height); + // Calculate next power-of-two values + int potWidth = (int)powf(2, ceilf(logf((float)texture->width)/logf(2))); + int potHeight = (int)powf(2, ceilf(logf((float)texture->height)/logf(2))); // Check if texture is POT - if ((potWidth != texture.width) || (potHeight != texture.height)) + if ((potWidth != texture->width) || (potHeight != texture->height)) { TraceLog(WARNING, "Limited NPOT support, no mipmaps available for NPOT textures"); } @@ -1533,11 +1379,94 @@ void GenTextureMipmaps(Texture2D texture) #endif } -// Update GPU texture with new data -// NOTE: pixels data must match texture.format -void UpdateTexture(Texture2D texture, void *pixels) +// Set texture scaling filter mode +void SetTextureFilter(Texture2D texture, int filterMode) { - rlglUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels); + switch (filterMode) + { + case FILTER_POINT: + { + if (texture.mipmaps > 1) + { + // RL_FILTER_MIP_NEAREST - tex filter: POINT, mipmaps filter: POINT (sharp switching between mipmaps) + rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_MIP_NEAREST); + + // RL_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps + rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_NEAREST); + } + else + { + // RL_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps + rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_NEAREST); + rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_NEAREST); + } + } break; + case FILTER_BILINEAR: + { + if (texture.mipmaps > 1) + { + // RL_FILTER_LINEAR_MIP_NEAREST - tex filter: BILINEAR, mipmaps filter: POINT (sharp switching between mipmaps) + // Alternative: RL_FILTER_NEAREST_MIP_LINEAR - tex filter: POINT, mipmaps filter: BILINEAR (smooth transition between mipmaps) + rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR_MIP_NEAREST); + + // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); + } + else + { + // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR); + rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); + } + } break; + case FILTER_TRILINEAR: + { + if (texture.mipmaps > 1) + { + // RL_FILTER_MIP_LINEAR - tex filter: BILINEAR, mipmaps filter: BILINEAR (smooth transition between mipmaps) + rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_MIP_LINEAR); + + // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); + } + else + { + TraceLog(WARNING, "[TEX ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id); + + // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR); + rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR); + } + } break; + case FILTER_ANISOTROPIC_4X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 4); break; + case FILTER_ANISOTROPIC_8X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 8); break; + case FILTER_ANISOTROPIC_16X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 16); break; + default: break; + } +} + +// Set texture wrapping mode +void SetTextureWrap(Texture2D texture, int wrapMode) +{ + switch (wrapMode) + { + case WRAP_REPEAT: + { + rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_REPEAT); + rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_REPEAT); + } break; + case WRAP_CLAMP: + { + rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_CLAMP); + rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_CLAMP); + } break; + case WRAP_MIRROR: + { + rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_CLAMP_MIRROR); + rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_CLAMP_MIRROR); + } break; + default: break; + } } // Draw a Texture2D @@ -1584,7 +1513,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V rlEnableTexture(texture.id); rlPushMatrix(); - rlTranslatef(destRec.x, destRec.y, 0); + rlTranslatef((float)destRec.x, (float)destRec.y, 0); rlRotatef(rotation, 0, 0, 1); rlTranslatef(-origin.x, -origin.y, 0); @@ -1598,15 +1527,15 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V // Bottom-right corner for texture and quad rlTexCoord2f((float)sourceRec.x/texture.width, (float)(sourceRec.y + sourceRec.height)/texture.height); - rlVertex2f(0.0f, destRec.height); + rlVertex2f(0.0f, (float)destRec.height); // Top-right corner for texture and quad rlTexCoord2f((float)(sourceRec.x + sourceRec.width)/texture.width, (float)(sourceRec.y + sourceRec.height)/texture.height); - rlVertex2f(destRec.width, destRec.height); + rlVertex2f((float)destRec.width, (float)destRec.height); // Top-left corner for texture and quad rlTexCoord2f((float)(sourceRec.x + sourceRec.width)/texture.width, (float)sourceRec.y/texture.height); - rlVertex2f(destRec.width, 0.0f); + rlVertex2f((float)destRec.width, 0.0f); rlEnd(); rlPopMatrix(); @@ -1644,7 +1573,7 @@ static Image LoadDDS(const char *fileName) unsigned int gBitMask; unsigned int bBitMask; unsigned int aBitMask; - } ddsPixelFormat; + } DDSPixelFormat; // DDS Header (124 bytes) typedef struct { @@ -1656,13 +1585,13 @@ static Image LoadDDS(const char *fileName) unsigned int depth; unsigned int mipmapCount; unsigned int reserved1[11]; - ddsPixelFormat ddspf; + DDSPixelFormat ddspf; unsigned int caps; unsigned int caps2; unsigned int caps3; unsigned int caps4; unsigned int reserved2; - } ddsHeader; + } DDSHeader; Image image; @@ -1683,7 +1612,7 @@ static Image LoadDDS(const char *fileName) // Verify the type of file char filecode[4]; - fread(filecode, 1, 4, ddsFile); + fread(filecode, 4, 1, ddsFile); if (strncmp(filecode, "DDS ", 4) != 0) { @@ -1691,33 +1620,33 @@ static Image LoadDDS(const char *fileName) } else { - ddsHeader header; + DDSHeader ddsHeader; // Get the image header - fread(&header, sizeof(ddsHeader), 1, ddsFile); + fread(&ddsHeader, sizeof(DDSHeader), 1, ddsFile); - TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(ddsHeader)); - TraceLog(DEBUG, "[%s] DDS file pixel format size: %i", fileName, header.ddspf.size); - TraceLog(DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, header.ddspf.flags); - TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, header.ddspf.fourCC); - TraceLog(DEBUG, "[%s] DDS file bit count: 0x%x", fileName, header.ddspf.rgbBitCount); + TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(DDSHeader)); + TraceLog(DEBUG, "[%s] DDS file pixel format size: %i", fileName, ddsHeader.ddspf.size); + TraceLog(DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, ddsHeader.ddspf.flags); + TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, ddsHeader.ddspf.fourCC); + TraceLog(DEBUG, "[%s] DDS file bit count: 0x%x", fileName, ddsHeader.ddspf.rgbBitCount); - image.width = header.width; - image.height = header.height; - image.mipmaps = 1; // Default value, could be changed (header.mipmapCount) + image.width = ddsHeader.width; + image.height = ddsHeader.height; + image.mipmaps = 1; // Default value, could be changed (ddsHeader.mipmapCount) - if (header.ddspf.rgbBitCount == 16) // 16bit mode, no compressed + if (ddsHeader.ddspf.rgbBitCount == 16) // 16bit mode, no compressed { - if (header.ddspf.flags == 0x40) // no alpha channel + if (ddsHeader.ddspf.flags == 0x40) // no alpha channel { image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); image.format = UNCOMPRESSED_R5G6B5; } - else if (header.ddspf.flags == 0x41) // with alpha channel + else if (ddsHeader.ddspf.flags == 0x41) // with alpha channel { - if (header.ddspf.aBitMask == 0x8000) // 1bit alpha + if (ddsHeader.ddspf.aBitMask == 0x8000) // 1bit alpha { image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); @@ -1734,7 +1663,7 @@ static Image LoadDDS(const char *fileName) image.format = UNCOMPRESSED_R5G5B5A1; } - else if (header.ddspf.aBitMask == 0xf000) // 4bit alpha + else if (ddsHeader.ddspf.aBitMask == 0xf000) // 4bit alpha { image.data = (unsigned short *)malloc(image.width*image.height*sizeof(unsigned short)); fread(image.data, image.width*image.height*sizeof(unsigned short), 1, ddsFile); @@ -1753,7 +1682,7 @@ static Image LoadDDS(const char *fileName) } } } - if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed + if (ddsHeader.ddspf.flags == 0x40 && ddsHeader.ddspf.rgbBitCount == 24) // DDS_RGB, no compressed { // NOTE: not sure if this case exists... image.data = (unsigned char *)malloc(image.width*image.height*3*sizeof(unsigned char)); @@ -1761,7 +1690,7 @@ static Image LoadDDS(const char *fileName) image.format = UNCOMPRESSED_R8G8B8; } - else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed + else if (ddsHeader.ddspf.flags == 0x41 && ddsHeader.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); @@ -1780,27 +1709,27 @@ static Image LoadDDS(const char *fileName) image.format = UNCOMPRESSED_R8G8B8A8; } - else if (((header.ddspf.flags == 0x04) || (header.ddspf.flags == 0x05)) && (header.ddspf.fourCC > 0)) // Compressed + else if (((ddsHeader.ddspf.flags == 0x04) || (ddsHeader.ddspf.flags == 0x05)) && (ddsHeader.ddspf.fourCC > 0)) // Compressed { - int bufsize; + int size; // DDS image data size // Calculate data size, including all mipmaps - if (header.mipmapCount > 1) bufsize = header.pitchOrLinearSize*2; - else bufsize = header.pitchOrLinearSize; + if (ddsHeader.mipmapCount > 1) size = ddsHeader.pitchOrLinearSize*2; + else size = ddsHeader.pitchOrLinearSize; - TraceLog(DEBUG, "Pitch or linear size: %i", header.pitchOrLinearSize); + TraceLog(DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize); - image.data = (unsigned char*)malloc(bufsize*sizeof(unsigned char)); + image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); - fread(image.data, 1, bufsize, ddsFile); + fread(image.data, size, 1, ddsFile); - image.mipmaps = header.mipmapCount; + image.mipmaps = ddsHeader.mipmapCount; - switch (header.ddspf.fourCC) + switch (ddsHeader.ddspf.fourCC) { case FOURCC_DXT1: { - if (header.ddspf.flags == 0x04) image.format = COMPRESSED_DXT1_RGB; + if (ddsHeader.ddspf.flags == 0x04) image.format = COMPRESSED_DXT1_RGB; else image.format = COMPRESSED_DXT1_RGBA; } break; case FOURCC_DXT3: image.format = COMPRESSED_DXT3_RGBA; break; @@ -1839,7 +1768,7 @@ static Image LoadPKM(const char *fileName) unsigned short height; // Texture height (big-endian) (origHeight rounded to multiple of 4) unsigned short origWidth; // Original width (big-endian) unsigned short origHeight; // Original height (big-endian) - } pkmHeader; + } PKMHeader; // Formats list // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used) @@ -1864,42 +1793,42 @@ static Image LoadPKM(const char *fileName) } else { - pkmHeader header; + PKMHeader pkmHeader; // Get the image header - fread(&header, sizeof(pkmHeader), 1, pkmFile); + fread(&pkmHeader, sizeof(PKMHeader), 1, pkmFile); - if (strncmp(header.id, "PKM ", 4) != 0) + if (strncmp(pkmHeader.id, "PKM ", 4) != 0) { TraceLog(WARNING, "[%s] PKM file does not seem to be a valid image", fileName); } else { // NOTE: format, width and height come as big-endian, data must be swapped to little-endian - 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); + pkmHeader.format = ((pkmHeader.format & 0x00FF) << 8) | ((pkmHeader.format & 0xFF00) >> 8); + pkmHeader.width = ((pkmHeader.width & 0x00FF) << 8) | ((pkmHeader.width & 0xFF00) >> 8); + pkmHeader.height = ((pkmHeader.height & 0x00FF) << 8) | ((pkmHeader.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); + TraceLog(DEBUG, "PKM (ETC) image width: %i", pkmHeader.width); + TraceLog(DEBUG, "PKM (ETC) image height: %i", pkmHeader.height); + TraceLog(DEBUG, "PKM (ETC) image format: %i", pkmHeader.format); - image.width = header.width; - image.height = header.height; + image.width = pkmHeader.width; + image.height = pkmHeader.height; image.mipmaps = 1; int bpp = 4; - if (header.format == 3) bpp = 8; + if (pkmHeader.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)); - fread(image.data, 1, size, pkmFile); + fread(image.data, size, 1, pkmFile); - if (header.format == 0) image.format = COMPRESSED_ETC1_RGB; - else if (header.format == 1) image.format = COMPRESSED_ETC2_RGB; - else if (header.format == 3) image.format = COMPRESSED_ETC2_EAC_RGBA; + if (pkmHeader.format == 0) image.format = COMPRESSED_ETC1_RGB; + else if (pkmHeader.format == 1) image.format = COMPRESSED_ETC2_RGB; + else if (pkmHeader.format == 3) image.format = COMPRESSED_ETC2_EAC_RGBA; } fclose(pkmFile); // Close file pointer @@ -1937,7 +1866,7 @@ static Image LoadKTX(const char *fileName) unsigned int faces; // Cubemap faces, for no-cubemap = 1 unsigned int mipmapLevels; // Non-mipmapped textures = 1 unsigned int keyValueDataSize; // Used to encode any arbitrary data... - } ktxHeader; + } KTXHeader; // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize @@ -1956,31 +1885,31 @@ static Image LoadKTX(const char *fileName) } else { - ktxHeader header; + KTXHeader ktxHeader; // Get the image header - fread(&header, sizeof(ktxHeader), 1, ktxFile); + fread(&ktxHeader, sizeof(KTXHeader), 1, ktxFile); - if ((header.id[1] != 'K') || (header.id[2] != 'T') || (header.id[3] != 'X') || - (header.id[4] != ' ') || (header.id[5] != '1') || (header.id[6] != '1')) + if ((ktxHeader.id[1] != 'K') || (ktxHeader.id[2] != 'T') || (ktxHeader.id[3] != 'X') || + (ktxHeader.id[4] != ' ') || (ktxHeader.id[5] != '1') || (ktxHeader.id[6] != '1')) { 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; + image.width = ktxHeader.width; + image.height = ktxHeader.height; + image.mipmaps = ktxHeader.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); + TraceLog(DEBUG, "KTX (ETC) image width: %i", ktxHeader.width); + TraceLog(DEBUG, "KTX (ETC) image height: %i", ktxHeader.height); + TraceLog(DEBUG, "KTX (ETC) image format: 0x%x", ktxHeader.glInternalFormat); unsigned char unused; - if (header.keyValueDataSize > 0) + if (ktxHeader.keyValueDataSize > 0) { - for (int i = 0; i < header.keyValueDataSize; i++) fread(&unused, 1, 1, ktxFile); + for (int i = 0; i < ktxHeader.keyValueDataSize; i++) fread(&unused, sizeof(unsigned char), 1, ktxFile); } int dataSize; @@ -1988,11 +1917,11 @@ static Image LoadKTX(const char *fileName) image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); - fread(image.data, 1, dataSize, ktxFile); + fread(image.data, dataSize, 1, ktxFile); - if (header.glInternalFormat == 0x8D64) image.format = COMPRESSED_ETC1_RGB; - else if (header.glInternalFormat == 0x9274) image.format = COMPRESSED_ETC2_RGB; - else if (header.glInternalFormat == 0x9278) image.format = COMPRESSED_ETC2_EAC_RGBA; + if (ktxHeader.glInternalFormat == 0x8D64) image.format = COMPRESSED_ETC1_RGB; + else if (ktxHeader.glInternalFormat == 0x9274) image.format = COMPRESSED_ETC2_RGB; + else if (ktxHeader.glInternalFormat == 0x9278) image.format = COMPRESSED_ETC2_EAC_RGBA; } fclose(ktxFile); // Close file pointer @@ -2028,7 +1957,7 @@ static Image LoadPVR(const char *fileName) unsigned int bitmaskAlpha; unsigned int pvrTag; unsigned int numSurfs; - } pvrHeaderV2; + } PVRHeaderV2; #endif // PVR file v3 Header (52 bytes) @@ -2047,7 +1976,7 @@ static Image LoadPVR(const char *fileName) unsigned int numFaces; unsigned int numMipmaps; unsigned int metaDataSize; - } pvrHeaderV3; + } PVRHeaderV3; #if 0 // Not used... // Metadata (usually 15 bytes) @@ -2056,7 +1985,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; @@ -2083,44 +2012,49 @@ static Image LoadPVR(const char *fileName) // Load different PVR data formats if (pvrVersion == 0x50) { - pvrHeaderV3 header; + PVRHeaderV3 pvrHeader; // Get PVR image header - fread(&header, sizeof(pvrHeaderV3), 1, pvrFile); + fread(&pvrHeader, sizeof(PVRHeaderV3), 1, pvrFile); - if ((header.id[0] != 'P') || (header.id[1] != 'V') || (header.id[2] != 'R') || (header.id[3] != 3)) + if ((pvrHeader.id[0] != 'P') || (pvrHeader.id[1] != 'V') || (pvrHeader.id[2] != 'R') || (pvrHeader.id[3] != 3)) { TraceLog(WARNING, "[%s] PVR file does not seem to be a valid image", fileName); } else { - image.width = header.width; - image.height = header.height; - image.mipmaps = header.numMipmaps; + image.width = pvrHeader.width; + image.height = pvrHeader.height; + image.mipmaps = pvrHeader.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; - else if ((header.channels[0] == 'r') && (header.channels[1] == 'g') && (header.channels[2] == 'b')) + if (((pvrHeader.channels[0] == 'l') && (pvrHeader.channels[1] == 0)) && (pvrHeader.channelDepth[0] == 8)) + image.format = UNCOMPRESSED_GRAYSCALE; + else if (((pvrHeader.channels[0] == 'l') && (pvrHeader.channels[1] == 'a')) && ((pvrHeader.channelDepth[0] == 8) && (pvrHeader.channelDepth[1] == 8))) + image.format = UNCOMPRESSED_GRAY_ALPHA; + else if ((pvrHeader.channels[0] == 'r') && (pvrHeader.channels[1] == 'g') && (pvrHeader.channels[2] == 'b')) { - if (header.channels[3] == 'a') + if (pvrHeader.channels[3] == 'a') { - if ((header.channelDepth[0] == 5) && (header.channelDepth[1] == 5) && (header.channelDepth[2] == 5) && (header.channelDepth[3] == 1)) image.format = UNCOMPRESSED_R5G5B5A1; - else if ((header.channelDepth[0] == 4) && (header.channelDepth[1] == 4) && (header.channelDepth[2] == 4) && (header.channelDepth[3] == 4)) image.format = UNCOMPRESSED_R4G4B4A4; - else if ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8) && (header.channelDepth[2] == 8) && (header.channelDepth[3] == 8)) image.format = UNCOMPRESSED_R8G8B8A8; + if ((pvrHeader.channelDepth[0] == 5) && (pvrHeader.channelDepth[1] == 5) && (pvrHeader.channelDepth[2] == 5) && (pvrHeader.channelDepth[3] == 1)) + image.format = UNCOMPRESSED_R5G5B5A1; + else if ((pvrHeader.channelDepth[0] == 4) && (pvrHeader.channelDepth[1] == 4) && (pvrHeader.channelDepth[2] == 4) && (pvrHeader.channelDepth[3] == 4)) + image.format = UNCOMPRESSED_R4G4B4A4; + else if ((pvrHeader.channelDepth[0] == 8) && (pvrHeader.channelDepth[1] == 8) && (pvrHeader.channelDepth[2] == 8) && (pvrHeader.channelDepth[3] == 8)) + image.format = UNCOMPRESSED_R8G8B8A8; } - else if (header.channels[3] == 0) + else if (pvrHeader.channels[3] == 0) { - if ((header.channelDepth[0] == 5) && (header.channelDepth[1] == 6) && (header.channelDepth[2] == 5)) image.format = UNCOMPRESSED_R5G6B5; - else if ((header.channelDepth[0] == 8) && (header.channelDepth[1] == 8) && (header.channelDepth[2] == 8)) image.format = UNCOMPRESSED_R8G8B8; + if ((pvrHeader.channelDepth[0] == 5) && (pvrHeader.channelDepth[1] == 6) && (pvrHeader.channelDepth[2] == 5)) image.format = UNCOMPRESSED_R5G6B5; + else if ((pvrHeader.channelDepth[0] == 8) && (pvrHeader.channelDepth[1] == 8) && (pvrHeader.channelDepth[2] == 8)) image.format = UNCOMPRESSED_R8G8B8; } } - else if (header.channels[0] == 2) image.format = COMPRESSED_PVRT_RGB; - else if (header.channels[0] == 3) image.format = COMPRESSED_PVRT_RGBA; + else if (pvrHeader.channels[0] == 2) image.format = COMPRESSED_PVRT_RGB; + else if (pvrHeader.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); + for (int i = 0; i < pvrHeader.metaDataSize; i++) fread(&unused, sizeof(unsigned char), 1, pvrFile); // Calculate data size (depends on format) int bpp = 0; @@ -2174,7 +2108,7 @@ static Image LoadASTC(const char *fileName) unsigned char width[3]; // Image width in pixels (24bit value) unsigned char height[3]; // Image height in pixels (24bit value) unsigned char lenght[3]; // Image Z-size (1 for 2D images) - } astcHeader; + } ASTCHeader; Image image; @@ -2192,30 +2126,30 @@ static Image LoadASTC(const char *fileName) } else { - astcHeader header; + ASTCHeader astcHeader; // Get ASTC image header - fread(&header, sizeof(astcHeader), 1, astcFile); + fread(&astcHeader, sizeof(ASTCHeader), 1, astcFile); - if ((header.id[3] != 0x5c) || (header.id[2] != 0xa1) || (header.id[1] != 0xab) || (header.id[0] != 0x13)) + if ((astcHeader.id[3] != 0x5c) || (astcHeader.id[2] != 0xa1) || (astcHeader.id[1] != 0xab) || (astcHeader.id[0] != 0x13)) { TraceLog(WARNING, "[%s] ASTC file does not seem to be a valid image", fileName); } else { // 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]); + image.width = 0x00000000 | ((int)astcHeader.width[2] << 16) | ((int)astcHeader.width[1] << 8) | ((int)astcHeader.width[0]); + image.height = 0x00000000 | ((int)astcHeader.height[2] << 16) | ((int)astcHeader.height[1] << 8) | ((int)astcHeader.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); + TraceLog(DEBUG, "ASTC image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY); // NOTE: Each block is always stored in 128bit so we can calculate the bpp - int bpp = 128/(header.blockX*header.blockY); + int bpp = 128/(astcHeader.blockX*astcHeader.blockY); // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8 if ((bpp == 8) || (bpp == 2)) diff --git a/src/utils.c b/src/utils.c index 640c5720..9a2a723a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,16 +1,23 @@ /********************************************************************************************** * -* raylib.utils +* raylib.utils - Some common utility functions * -* Some utility functions +* CONFIGURATION: * -* External libs: -* tinfl - zlib DEFLATE algorithm decompression +* #define SUPPORT_SAVE_PNG +* Enable saving PNG fileformat +* NOTE: Requires stb_image_write library +* +* #define SUPPORT_SAVE_BMP +* +* #define DO_NOT_TRACE_DEBUG_MSGS +* Avoid showing DEBUG TraceLog() messages +* +* DEPENDENCIES: * stb_image_write - PNG writting functions * -* Module Configuration Flags: -* DO_NOT_TRACE_DEBUG_MSGS - Avoid showing DEBUG TraceLog() messages * +* LICENSE: zlib/libpng * * Copyright (c) 2014-2016 Ramon Santamaria (@raysan5) * @@ -42,16 +49,17 @@ #include <stdlib.h> // Required for: malloc(), free() #include <stdio.h> // Required for: fopen(), fclose(), fputc(), fwrite(), printf(), fprintf(), funopen() #include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end() -//#include <string.h> // Required for: strlen(), strrchr(), strcmp() +#include <string.h> // Required for: strlen(), strrchr(), strcmp() #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) #define STB_IMAGE_WRITE_IMPLEMENTATION - #include "external/stb_image_write.h" // Required for: stbi_write_png() + #include "external/stb_image_write.h" // Required for: stbi_write_bmp(), stbi_write_png() #endif -#include "external/tinfl.c" // Required for: tinfl_decompress_mem_to_mem() - // NOTE: DEFLATE algorythm data decompression +#define RRES_IMPLEMENTATION +#include "rres.h" +//#define NO_TRACELOG // Avoid TraceLog() output (any type) #define DO_NOT_TRACE_DEBUG_MSGS // Avoid DEBUG messages tracing //---------------------------------------------------------------------------------- @@ -74,143 +82,11 @@ static int android_close(void *cookie); //---------------------------------------------------------------------------------- // Module Functions Definition - Utilities //---------------------------------------------------------------------------------- - -// Data decompression function -// NOTE: Allocated data MUST be freed! -unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize) -{ - int tempUncompSize; - unsigned char *pUncomp; - - // Allocate buffer to hold decompressed data - pUncomp = (mz_uint8 *)malloc((size_t)uncompSize); - - // Check correct memory allocation - if (pUncomp == NULL) - { - TraceLog(WARNING, "Out of memory while decompressing data"); - } - else - { - // Decompress data - tempUncompSize = tinfl_decompress_mem_to_mem(pUncomp, (size_t)uncompSize, data, compSize, 1); - - if (tempUncompSize == -1) - { - TraceLog(WARNING, "Data decompression failed"); - free(pUncomp); - } - - if (uncompSize != (int)tempUncompSize) - { - TraceLog(WARNING, "Expected uncompressed size do not match, data may be corrupted"); - TraceLog(WARNING, " -- Expected uncompressed size: %i", uncompSize); - TraceLog(WARNING, " -- Returned uncompressed size: %i", tempUncompSize); - } - - TraceLog(INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize); - } - - return pUncomp; -} - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) -// Creates a bitmap (BMP) file from an array of pixel data -// NOTE: This function is not explicitly available to raylib users -void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int height) -{ - int filesize = 54 + 3*width*height; - - unsigned char bmpFileHeader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0}; // Standard BMP file header - unsigned char bmpInfoHeader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0}; // Standard BMP info header - - bmpFileHeader[2] = (unsigned char)(filesize); - bmpFileHeader[3] = (unsigned char)(filesize>>8); - bmpFileHeader[4] = (unsigned char)(filesize>>16); - bmpFileHeader[5] = (unsigned char)(filesize>>24); - - bmpInfoHeader[4] = (unsigned char)(width); - bmpInfoHeader[5] = (unsigned char)(width>>8); - bmpInfoHeader[6] = (unsigned char)(width>>16); - bmpInfoHeader[7] = (unsigned char)(width>>24); - bmpInfoHeader[8] = (unsigned char)(height); - bmpInfoHeader[9] = (unsigned char)(height>>8); - bmpInfoHeader[10] = (unsigned char)(height>>16); - bmpInfoHeader[11] = (unsigned char)(height>>24); - - FILE *bmpFile = fopen(fileName, "wb"); // Define a pointer to bitmap file and open it in write-binary mode - - if (bmpFile == NULL) - { - TraceLog(WARNING, "[%s] BMP file could not be created", fileName); - } - else - { - // NOTE: fwrite parameters are: data pointer, size in bytes of each element to be written, number of elements, file-to-write pointer - fwrite(bmpFileHeader, sizeof(unsigned char), 14, bmpFile); // Write BMP file header data - fwrite(bmpInfoHeader, sizeof(unsigned char), 40, bmpFile); // Write BMP info header data - - // Write pixel data to file - for (int y = 0; y < height ; y++) - { - for (int x = 0; x < width; x++) - { - fputc(imgData[(x*4)+2 + (y*width*4)], bmpFile); - fputc(imgData[(x*4)+1 + (y*width*4)], bmpFile); - fputc(imgData[(x*4) + (y*width*4)], bmpFile); - } - } - } - - fclose(bmpFile); // Close bitmap file -} - -// Creates a PNG image file from an array of pixel data -// NOTE: Uses stb_image_write -void WritePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize) -{ - stbi_write_png(fileName, width, height, compSize, imgData, width*compSize); -} -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) -// Outputs a trace log message (INFO, ERROR, WARNING) -// NOTE: If a file has been init, output log is written there -void TraceLog(int msgType, const char *text, ...) -{ - va_list args; - int traceDebugMsgs = 1; - -#ifdef DO_NOT_TRACE_DEBUG_MSGS - traceDebugMsgs = 0; -#endif - - switch(msgType) - { - case INFO: fprintf(stdout, "INFO: "); break; - case ERROR: fprintf(stdout, "ERROR: "); break; - case WARNING: fprintf(stdout, "WARNING: "); break; - case DEBUG: if (traceDebugMsgs) fprintf(stdout, "DEBUG: "); break; - default: break; - } - - if ((msgType != DEBUG) || ((msgType == DEBUG) && (traceDebugMsgs))) - { - va_start(args, text); - vfprintf(stdout, text, args); - va_end(args); - - fprintf(stdout, "\n"); - } - - if (msgType == ERROR) exit(1); // If ERROR message, exit program -} -#endif - -#if defined(PLATFORM_ANDROID) +// Outputs a trace log message void TraceLog(int msgType, const char *text, ...) { - static char buffer[100]; +#if !defined(NO_TRACELOG) + static char buffer[128]; int traceDebugMsgs = 1; #ifdef DO_NOT_TRACE_DEBUG_MSGS @@ -230,8 +106,9 @@ void TraceLog(int msgType, const char *text, ...) strcat(buffer, "\n"); va_list args; - va_start(args, buffer); + va_start(args, text); +#if defined(PLATFORM_ANDROID) switch(msgType) { case INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", buffer, args); break; @@ -240,12 +117,32 @@ void TraceLog(int msgType, const char *text, ...) case DEBUG: if (traceDebugMsgs) __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", buffer, args); break; default: break; } +#else + if ((msgType != DEBUG) || ((msgType == DEBUG) && (traceDebugMsgs))) vprintf(buffer, args); +#endif va_end(args); - if (msgType == ERROR) exit(1); + if (msgType == ERROR) exit(1); // If ERROR message, exit program + +#endif // NO_TRACELOG +} + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +// Creates a BMP image file from an array of pixel data +void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize) +{ + stbi_write_bmp(fileName, width, height, compSize, imgData); +} + +// Creates a PNG image file from an array of pixel data +void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize) +{ + stbi_write_png(fileName, width, height, compSize, imgData, width*compSize); } +#endif +#if defined(PLATFORM_ANDROID) // Initialize asset manager from android app void InitAssetManager(AAssetManager *manager) { @@ -283,23 +180,6 @@ const char *GetExtension(const char *fileName) return (dot + 1); } -// Calculate next power-of-two value for a given num -int GetNextPOT(int num) -{ - if (num != 0) - { - num--; - num |= (num >> 1); // Or first 2 bits - num |= (num >> 2); // Or next 2 bits - num |= (num >> 4); // Or next 4 bits - num |= (num >> 8); // Or next 8 bits - num |= (num >> 16); // Or next 16 bits - num++; - } - - return num; -} - //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/utils.h b/src/utils.h index 045b0692..3ffd025c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -31,6 +31,8 @@ #include <android/asset_manager.h> // Required for: AAssetManager #endif +#include "rres.h" + //---------------------------------------------------------------------------------- // Some basic Defines //---------------------------------------------------------------------------------- @@ -41,19 +43,8 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef enum { IMAGE = 0, SOUND, MODEL, TEXT, RAW } DataType; - typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; -// One resource info header, every resource includes this header (8 byte) -typedef struct { - unsigned short id; // Resource unique identifier (2 byte) - unsigned char type; // Resource type (1 byte) - unsigned char comp; // Data Compression and Coding (1 byte) - unsigned int size; // Data size in .rres file (compressed or not, only DATA) (4 byte) - unsigned int srcSize; // Source data size (uncompressed, only DATA) -} ResInfoHeader; - #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif @@ -66,17 +57,14 @@ extern "C" { // Prevents name mangling of functions //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- -unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize); +void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message +const char *GetExtension(const char *fileName); // Returns extension of a filename #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) -void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int height); -void WritePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize); +void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize); +void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize); #endif -void TraceLog(int msgType, const char *text, ...); // Outputs a trace log message -const char *GetExtension(const char *fileName); // Returns extension of a filename -int GetNextPOT(int num); // Calculate next power-of-two value for a given num - #if defined(PLATFORM_ANDROID) void InitAssetManager(AAssetManager *manager); // Initialize asset manager from android app FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() |
