diff options
| author | victorfisac <[email protected]> | 2017-08-27 12:48:51 +0200 |
|---|---|---|
| committer | victorfisac <[email protected]> | 2017-08-27 12:48:51 +0200 |
| commit | 353912b2153b848b270ac15ba97a156b7d336d84 (patch) | |
| tree | 63fdca2144cd13f6a537e76d6a3f8712ae106ead /src | |
| parent | eb7b5e59bbaec0f556c4fde21cd0bf20819a8108 (diff) | |
| parent | c074783861994fb9f3bcc618b776a41dc57b50d0 (diff) | |
| download | raylib-353912b2153b848b270ac15ba97a156b7d336d84.tar.gz raylib-353912b2153b848b270ac15ba97a156b7d336d84.zip | |
Merge branch 'master' of https://github.com/raysan5/raylib
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 27 | ||||
| -rw-r--r-- | src/audio.c | 145 | ||||
| -rw-r--r-- | src/core.c | 258 | ||||
| -rw-r--r-- | src/external/gif.h | 282 | ||||
| -rw-r--r-- | src/external/jar_xm.h | 4 | ||||
| -rw-r--r-- | src/external/stb_image.h | 184 | ||||
| -rw-r--r-- | src/external/stb_image_resize.h | 13 | ||||
| -rw-r--r-- | src/external/stb_image_write.h | 378 | ||||
| -rw-r--r-- | src/external/stb_perlin.h | 316 | ||||
| -rw-r--r-- | src/external/stb_rect_pack.h | 11 | ||||
| -rw-r--r-- | src/external/stb_truetype.h | 529 | ||||
| -rw-r--r-- | src/external/stb_vorbis.c | 1 | ||||
| -rw-r--r-- | src/external/stb_vorbis.h | 5 | ||||
| -rw-r--r-- | src/gestures.h | 5 | ||||
| -rw-r--r-- | src/meson.build | 22 | ||||
| -rw-r--r-- | src/models.c | 656 | ||||
| -rw-r--r-- | src/physac.h | 7 | ||||
| -rw-r--r-- | src/raylib.h | 200 | ||||
| -rw-r--r-- | src/raymath.h | 327 | ||||
| -rw-r--r-- | src/resources (renamed from src/raylib_icon) | bin | 107260 -> 107260 bytes | |||
| -rw-r--r-- | src/rlgl.c | 1356 | ||||
| -rw-r--r-- | src/rlgl.h | 232 | ||||
| -rw-r--r-- | src/rres.h | 43 | ||||
| -rw-r--r-- | src/shapes.c | 12 | ||||
| -rw-r--r-- | src/text.c | 55 | ||||
| -rw-r--r-- | src/textures.c | 448 | ||||
| -rw-r--r-- | src/utils.c | 30 |
27 files changed, 3774 insertions, 1772 deletions
diff --git a/src/Makefile b/src/Makefile index 1eac71f6..6d0318c1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -145,6 +145,14 @@ endif # default gcc compiler CC = gcc + +# For OS X +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM_OS),OSX) + CC = clang + endif +endif + # Android toolchain compiler ifeq ($(PLATFORM),PLATFORM_ANDROID) ifeq ($(ANDROID_ARCH),ARM) @@ -198,7 +206,7 @@ endif # -fgnu89-inline declaring inline functions support (GCC optimized) # -Wno-missing-braces ignore invalid warning (GCC bug 53119) # -D_DEFAULT_SOURCE use with -std=c99 -CFLAGS = -O1 -Wall -std=c99 -D_DEFAULT_SOURCE -fgnu89-inline -Wno-missing-braces +CFLAGS += -O1 -Wall -std=c99 -D_DEFAULT_SOURCE -fgnu89-inline -Wno-missing-braces ifeq ($(PLATFORM),PLATFORM_WEB) CFLAGS += -s USE_GLFW=3 -s ASSERTIONS=1 --profiling --preload-file resources @@ -215,7 +223,6 @@ endif ifeq ($(SHARED_RAYLIB),YES) CFLAGS += -fPIC SHAREDFLAG = BUILDING_DLL - SHAREDLIBS = -Lexternal/glfw3/lib/win32 -Lexternal/openal_soft/lib/win32 -lglfw3 -lgdi32 else SHAREDFLAG = BUILDING_STATIC endif @@ -317,15 +324,21 @@ ifeq ($(PLATFORM),PLATFORM_WEB) @echo "libraylib.bc generated (web version)!" else ifeq ($(SHARED_RAYLIB),YES) + # NOTE: If using OpenAL Soft as static library, all its dependencies must be also linked in the shared library + ifeq ($(PLATFORM_OS),WINDOWS) + $(CC) -shared -o $(OUTPUT_PATH)/raylib.dll $(OBJS) $(SHAREDLIBS) -Lexternal/glfw3/lib/win32 -Lexternal/openal_soft/lib/win32 -lglfw3 -lgdi32 -Wl,--out-implib,$(OUTPUT_PATH)/libraylibdll.a + @echo "raylib dynamic library (raylib.dll) and import library (libraylibdll.a) generated!" + endif ifeq ($(PLATFORM_OS),LINUX) # compile raylib to shared library version for GNU/Linux. # WARNING: you should type "make clean" before doing this target - $(CC) -shared -o $(OUTPUT_PATH)/libraylib.so $(OBJS) - @echo "raylib shared library (libraylib.so) generated!" + $(CC) -shared -o $(OUTPUT_PATH)/libraylib.so $(OBJS) -lglfw3 -lGL -lopenal -lm -lpthread -ldl + @echo "raylib shared library generated (libraylib.so)!" endif - ifeq ($(PLATFORM_OS),WINDOWS) - $(CC) -shared -o $(OUTPUT_PATH)/raylib.dll $(OBJS) $(SHAREDLIBS) -Wl,--out-implib,$(OUTPUT_PATH)/libraylibdll.a - @echo "raylib dynamic library (raylib.dll) and import library (libraylibdll.a) generated!" + ifeq ($(PLATFORM_OS),OSX) + $(CC) -dynamiclib -o $(OUTPUT_PATH)/libraylib.dylib $(OBJS) -L/usr/local/Cellar/glfw/3.2.1/lib -lglfw -framework OpenGL -framework OpenAL -framework Cocoa + install_name_tool -id "libraylib.dylib" $(OUTPUT_PATH)/libraylib.dylib + @echo "raylib shared library generated (libraylib.dylib)!" endif ifeq ($(PLATFORM),PLATFORM_ANDROID) $(CC) -shared -o $(OUTPUT_PATH)/libraylib.so $(OBJS) diff --git a/src/audio.c b/src/audio.c index b81d5572..1b340377 100644 --- a/src/audio.c +++ b/src/audio.c @@ -170,7 +170,7 @@ typedef struct MusicData { } MusicData; #if defined(AUDIO_STANDALONE) -typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; +typedef enum { LOG_INFO = 0, LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_OTHER } TraceLogType; #endif //---------------------------------------------------------------------------------- @@ -193,7 +193,7 @@ static Wave LoadFLAC(const char *fileName); // Load FLAC file #if defined(AUDIO_STANDALONE) bool IsFileExtension(const char *fileName, const char *ext); // Check file extension -void TraceLog(int msgType, const char *text, ...); // Outputs trace log message (INFO, ERROR, WARNING) +void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) #endif //---------------------------------------------------------------------------------- @@ -206,7 +206,7 @@ void InitAudioDevice(void) // Open and initialize a device with default settings ALCdevice *device = alcOpenDevice(NULL); - if (!device) TraceLog(ERROR, "Audio device could not be opened"); + if (!device) TraceLog(LOG_ERROR, "Audio device could not be opened"); else { ALCcontext *context = alcCreateContext(device, NULL); @@ -217,11 +217,11 @@ void InitAudioDevice(void) alcCloseDevice(device); - TraceLog(ERROR, "Could not initialize audio context"); + TraceLog(LOG_ERROR, "Could not initialize audio context"); } else { - TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); + TraceLog(LOG_INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); // Listener definition (just for 2D) alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); @@ -239,7 +239,7 @@ void CloseAudioDevice(void) ALCdevice *device; ALCcontext *context = alcGetCurrentContext(); - if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing"); + if (context == NULL) TraceLog(LOG_WARNING, "Could not get current audio context for closing"); device = alcGetContextsDevice(context); @@ -247,7 +247,7 @@ void CloseAudioDevice(void) alcDestroyContext(context); alcCloseDevice(device); - TraceLog(INFO, "Audio device closed successfully"); + TraceLog(LOG_INFO, "Audio device closed successfully"); } // Check if device has been initialized successfully @@ -298,12 +298,12 @@ Wave LoadWave(const char *fileName) // 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); + else TraceLog(LOG_WARNING, "[%s] Resource file does not contain wave data", fileName); UnloadResource(rres); } #endif - else TraceLog(WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); + else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); return wave; } @@ -358,7 +358,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; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; + default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; } } else if (wave.channels == 2) @@ -368,10 +368,10 @@ 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; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; + default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break; } } - else TraceLog(WARNING, "Wave number of channels not supported: %i", wave.channels); + else TraceLog(LOG_WARNING, "Wave number of channels not supported: %i", wave.channels); // Create an audio source ALuint source; @@ -396,7 +396,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 (%i Hz, %i bit, %s)", source, buffer, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); + TraceLog(LOG_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; @@ -411,7 +411,7 @@ void UnloadWave(Wave wave) { if (wave.data != NULL) free(wave.data); - TraceLog(INFO, "Unloaded wave data from RAM"); + TraceLog(LOG_INFO, "Unloaded wave data from RAM"); } // Unload sound @@ -422,7 +422,7 @@ void UnloadSound(Sound sound) alDeleteSources(1, &sound.source); alDeleteBuffers(1, &sound.buffer); - TraceLog(INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer); + TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer); } // Update sound buffer with new data @@ -434,9 +434,9 @@ void UpdateSound(Sound sound, const void *data, int samplesCount) 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); + TraceLog(LOG_DEBUG, "UpdateSound() : AL_FREQUENCY: %i", sampleRate); + TraceLog(LOG_DEBUG, "UpdateSound() : AL_BITS: %i", sampleSize); + TraceLog(LOG_DEBUG, "UpdateSound() : AL_CHANNELS: %i", channels); unsigned int dataSize = samplesCount*channels*sampleSize/8; // Size of data in bytes @@ -457,7 +457,7 @@ void PlaySound(Sound sound) { alSourcePlay(sound.source); // Play the sound - //TraceLog(INFO, "Playing sound"); + //TraceLog(LOG_INFO, "Playing sound"); // Find the current position of the sound being played // NOTE: Only work when the entire file is in a single buffer @@ -639,7 +639,7 @@ void WaveCrop(Wave *wave, int initSample, int finalSample) free(wave->data); wave->data = data; } - else TraceLog(WARNING, "Wave crop range out of bounds"); + else TraceLog(LOG_WARNING, "Wave crop range out of bounds"); } // Get samples data from wave as a floats array @@ -675,7 +675,7 @@ Music LoadMusicStream(const char *fileName) // Open ogg audio stream music->ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL); - if (music->ctxOgg == NULL) TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName); + if (music->ctxOgg == NULL) TraceLog(LOG_WARNING, "[%s] OGG audio file could not be opened", fileName); else { stb_vorbis_info info = stb_vorbis_get_info(music->ctxOgg); // Get Ogg file info @@ -687,10 +687,10 @@ Music LoadMusicStream(const char *fileName) music->ctxType = MUSIC_AUDIO_OGG; 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); + TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music->totalSamples); + TraceLog(LOG_DEBUG, "[%s] OGG sample rate: %i", fileName, info.sample_rate); + TraceLog(LOG_DEBUG, "[%s] OGG channels: %i", fileName, info.channels); + TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required); } } #if defined(SUPPORT_FILEFORMAT_FLAC) @@ -698,7 +698,7 @@ Music LoadMusicStream(const char *fileName) { music->ctxFlac = drflac_open_file(fileName); - if (music->ctxFlac == NULL) TraceLog(WARNING, "[%s] FLAC audio file could not be opened", fileName); + if (music->ctxFlac == NULL) TraceLog(LOG_WARNING, "[%s] FLAC audio file could not be opened", fileName); else { music->stream = InitAudioStream(music->ctxFlac->sampleRate, music->ctxFlac->bitsPerSample, music->ctxFlac->channels); @@ -707,10 +707,10 @@ Music LoadMusicStream(const char *fileName) music->ctxType = MUSIC_AUDIO_FLAC; 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); + TraceLog(LOG_DEBUG, "[%s] FLAC total samples: %i", fileName, music->totalSamples); + TraceLog(LOG_DEBUG, "[%s] FLAC sample rate: %i", fileName, music->ctxFlac->sampleRate); + TraceLog(LOG_DEBUG, "[%s] FLAC bits per sample: %i", fileName, music->ctxFlac->bitsPerSample); + TraceLog(LOG_DEBUG, "[%s] FLAC channels: %i", fileName, music->ctxFlac->channels); } } #endif @@ -730,10 +730,10 @@ Music LoadMusicStream(const char *fileName) music->ctxType = MUSIC_MODULE_XM; 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); + TraceLog(LOG_DEBUG, "[%s] XM number of samples: %i", fileName, music->totalSamples); + TraceLog(LOG_DEBUG, "[%s] XM track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); } - else TraceLog(WARNING, "[%s] XM file could not be opened", fileName); + else TraceLog(LOG_WARNING, "[%s] XM file could not be opened", fileName); } #endif #if defined(SUPPORT_FILEFORMAT_MOD) @@ -749,13 +749,13 @@ Music LoadMusicStream(const char *fileName) music->ctxType = MUSIC_MODULE_MOD; music->loopCount = -1; // Infinite loop by default - 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); + TraceLog(LOG_DEBUG, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); + TraceLog(LOG_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); + else TraceLog(LOG_WARNING, "[%s] MOD file could not be opened", fileName); } #endif - else TraceLog(WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); + else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName); return music; } @@ -799,7 +799,7 @@ void ResumeMusicStream(Music music) if (state == AL_PAUSED) { - TraceLog(INFO, "[AUD ID %i] Resume music stream playing", music->stream.source); + TraceLog(LOG_INFO, "[AUD ID %i] Resume music stream playing", music->stream.source); alSourcePlay(music->stream.source); } } @@ -992,7 +992,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un if ((channels > 0) && (channels < 3)) stream.channels = channels; else { - TraceLog(WARNING, "Init audio stream: Number of channels not supported: %i", channels); + TraceLog(LOG_WARNING, "Init audio stream: Number of channels not supported: %i", channels); stream.channels = 1; // Fallback to mono channel } @@ -1004,7 +1004,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un 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; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; + default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; } } else if (stream.channels == 2) @@ -1014,7 +1014,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un 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; // Requires OpenAL extension: AL_EXT_FLOAT32 - default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; + default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; } } @@ -1041,7 +1041,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); - 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"); + TraceLog(LOG_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; } @@ -1068,7 +1068,7 @@ void CloseAudioStream(AudioStream stream) alDeleteSources(1, &stream.source); alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers); - TraceLog(INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); + TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); } // Update audio stream buffers with data @@ -1085,7 +1085,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) alBufferData(buffer, stream.format, data, samplesCount*stream.sampleSize/8*stream.channels, stream.sampleRate); alSourceQueueBuffers(stream.source, 1, &buffer); } - else TraceLog(WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source); + else TraceLog(LOG_WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source); } // Check if any audio stream buffers requires refill @@ -1168,7 +1168,7 @@ static Wave LoadWAV(const char *fileName) if (wavFile == NULL) { - TraceLog(WARNING, "[%s] WAV file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] WAV file could not be opened", fileName); wave.data = NULL; } else @@ -1180,7 +1180,7 @@ static Wave LoadWAV(const char *fileName) if (strncmp(wavRiffHeader.chunkID, "RIFF", 4) || strncmp(wavRiffHeader.format, "WAVE", 4)) { - TraceLog(WARNING, "[%s] Invalid RIFF or WAVE Header", fileName); + TraceLog(LOG_WARNING, "[%s] Invalid RIFF or WAVE Header", fileName); } else { @@ -1191,7 +1191,7 @@ static Wave LoadWAV(const char *fileName) if ((wavFormat.subChunkID[0] != 'f') || (wavFormat.subChunkID[1] != 'm') || (wavFormat.subChunkID[2] != 't') || (wavFormat.subChunkID[3] != ' ')) { - TraceLog(WARNING, "[%s] Invalid Wave format", fileName); + TraceLog(LOG_WARNING, "[%s] Invalid Wave format", fileName); } else { @@ -1205,7 +1205,7 @@ static Wave LoadWAV(const char *fileName) if ((wavData.subChunkID[0] != 'd') || (wavData.subChunkID[1] != 'a') || (wavData.subChunkID[2] != 't') || (wavData.subChunkID[3] != 'a')) { - TraceLog(WARNING, "[%s] Invalid data header", fileName); + TraceLog(LOG_WARNING, "[%s] Invalid data header", fileName); } else { @@ -1223,7 +1223,7 @@ static Wave LoadWAV(const char *fileName) // 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); + TraceLog(LOG_WARNING, "[%s] WAV sample size (%ibit) not supported, converted to 16bit", fileName, wave.sampleSize); WaveFormat(&wave, wave.sampleRate, 16, wave.channels); } @@ -1231,13 +1231,13 @@ static Wave LoadWAV(const char *fileName) 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); + TraceLog(LOG_WARNING, "[%s] WAV channels number (%i) not supported, converted to 2 channels", fileName, wave.channels); } // 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 (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); + TraceLog(LOG_INFO, "[%s] WAV file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); } } } @@ -1258,7 +1258,7 @@ static Wave LoadOGG(const char *fileName) stb_vorbis *oggFile = stb_vorbis_open_filename(fileName, NULL, NULL); - if (oggFile == NULL) TraceLog(WARNING, "[%s] OGG file could not be opened", fileName); + if (oggFile == NULL) TraceLog(LOG_WARNING, "[%s] OGG file could not be opened", fileName); else { stb_vorbis_info info = stb_vorbis_get_info(oggFile); @@ -1269,16 +1269,16 @@ static Wave LoadOGG(const char *fileName) wave.sampleCount = (int)stb_vorbis_stream_length_in_samples(oggFile); // Independent by channel float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile); - if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio length is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds); + if (totalSeconds > 10) TraceLog(LOG_WARNING, "[%s] Ogg audio length is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds); wave.data = (short *)malloc(wave.sampleCount*wave.channels*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); - TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, numSamplesOgg); + TraceLog(LOG_DEBUG, "[%s] Samples obtained: %i", fileName, numSamplesOgg); - TraceLog(INFO, "[%s] OGG file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); + TraceLog(LOG_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); } @@ -1302,10 +1302,10 @@ static Wave LoadFLAC(const char *fileName) 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.channels > 2) TraceLog(LOG_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"); + if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] FLAC data could not be loaded", fileName); + else TraceLog(LOG_INFO, "[%s] FLAC file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo"); return wave; } @@ -1327,35 +1327,26 @@ bool IsFileExtension(const char *fileName, const char *ext) return result; } -// Outputs a trace log message (INFO, ERROR, WARNING) -// NOTE: If a file has been init, output log is written there +// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) void TraceLog(int msgType, const char *text, ...) { va_list args; - int traceDebugMsgs = 0; - -#if defined(DO_NOT_TRACE_DEBUG_MSGS) - traceDebugMsgs = 0; -#endif + va_start(args, text); 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; + case LOG_INFO: fprintf(stdout, "INFO: "); break; + case LOG_ERROR: fprintf(stdout, "ERROR: "); break; + case LOG_WARNING: fprintf(stdout, "WARNING: "); break; + case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; default: break; } - if ((msgType != DEBUG) || ((msgType == DEBUG) && (traceDebugMsgs))) - { - va_start(args, text); - vfprintf(stdout, text, args); - va_end(args); + vfprintf(stdout, text, args); + fprintf(stdout, "\n"); - fprintf(stdout, "\n"); - } + va_end(args); - if (msgType == ERROR) exit(1); // If ERROR message, exit program + if (msgType == LOG_ERROR) exit(1); } #endif @@ -109,6 +109,10 @@ #include "external/gif.h" // Support GIF recording #endif +#if defined(__linux__) || defined(PLATFORM_WEB) + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif + #include <stdio.h> // Standard input / output lib #include <stdlib.h> // Required for: malloc(), free(), rand(), atexit() #include <stdint.h> // Required for: typedef unsigned long long int uint64_t, used by hi-res timer @@ -328,7 +332,6 @@ static char configFlags = 0; // Configuration flags (bit based) static bool showLogo = false; // Track if showing logo at init is enabled #if defined(SUPPORT_GIF_RECORDING) -static GifWriter gifWriter; static int gifFramesCounter = 0; static bool gifRecording = false; #endif @@ -409,7 +412,7 @@ static void *GamepadThread(void *arg); // Mouse reading thread // Initialize window and OpenGL context void InitWindow(int width, int height, const char *title) { - TraceLog(INFO, "Initializing raylib (v1.7.0)"); + TraceLog(LOG_INFO, "Initializing raylib (v1.8.0)"); // Store window title (could be useful...) windowTitle = title; @@ -472,7 +475,7 @@ void InitWindow(int width, int height, const char *title) // Initialize Android activity void InitWindow(int width, int height, void *state) { - TraceLog(INFO, "Initializing raylib (v1.7.0)"); + TraceLog(LOG_INFO, "Initializing raylib (v1.8.0)"); app_dummy(); @@ -488,19 +491,19 @@ void InitWindow(int width, int height, void *state) int orientation = AConfiguration_getOrientation(app->config); - if (orientation == ACONFIGURATION_ORIENTATION_PORT) TraceLog(INFO, "PORTRAIT window orientation"); - else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TraceLog(INFO, "LANDSCAPE window orientation"); + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TraceLog(LOG_INFO, "PORTRAIT window orientation"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TraceLog(LOG_INFO, "LANDSCAPE window orientation"); // TODO: Automatic orientation doesn't seem to work if (width <= height) { AConfiguration_setOrientation(app->config, ACONFIGURATION_ORIENTATION_PORT); - TraceLog(WARNING, "Window set to portraid mode"); + TraceLog(LOG_WARNING, "Window set to portraid mode"); } else { AConfiguration_setOrientation(app->config, ACONFIGURATION_ORIENTATION_LAND); - TraceLog(WARNING, "Window set to landscape mode"); + TraceLog(LOG_WARNING, "Window set to landscape mode"); } //AConfiguration_getDensity(app->config); @@ -514,7 +517,7 @@ void InitWindow(int width, int height, void *state) InitAssetManager(app->activity->assetManager); - TraceLog(INFO, "Android app initialized successfully"); + TraceLog(LOG_INFO, "Android app initialized successfully"); // Wait for window to be initialized (display and context) while (!windowReady) @@ -538,7 +541,7 @@ void CloseWindow(void) #if defined(SUPPORT_GIF_RECORDING) if (gifRecording) { - GifEnd(&gifWriter); + GifEnd(); gifRecording = false; } #endif @@ -593,7 +596,7 @@ void CloseWindow(void) pthread_join(gamepadThreadId, NULL); #endif - TraceLog(INFO, "Window closed successfully"); + TraceLog(LOG_INFO, "Window closed successfully"); } // Check if KEY_ESCAPE pressed or Close icon pressed @@ -633,7 +636,7 @@ void ToggleFullscreen(void) #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) - TraceLog(WARNING, "Could not toggle to windowed mode"); + TraceLog(LOG_WARNING, "Could not toggle to windowed mode"); #endif } @@ -674,9 +677,9 @@ void SetWindowMonitor(int monitor) 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])); + TraceLog(LOG_INFO, "Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); } - else TraceLog(WARNING, "Selected monitor not found"); + else TraceLog(LOG_WARNING, "Selected monitor not found"); #endif } @@ -806,8 +809,8 @@ void EndDrawing(void) { // Get image data for the current frame (from backbuffer) // NOTE: This process is very slow... :( - unsigned char *screenData = rlglReadScreenPixels(screenWidth, screenHeight); - GifWriteFrame(&gifWriter, screenData, screenWidth, screenHeight, 10, 8, false); + unsigned char *screenData = rlReadScreenPixels(screenWidth, screenHeight); + GifWriteFrame(screenData, screenWidth, screenHeight, 10, 8, false); free(screenData); // Free image data } @@ -882,7 +885,7 @@ void Begin3dMode(Camera camera) // Setup perspective projection float aspect = (float)screenWidth/(float)screenHeight; - double top = 0.01*tan(camera.fovy*PI/360.0); + double top = 0.01*tan(camera.fovy*0.5*DEG2RAD); double right = top*aspect; // NOTE: zNear and zFar values are important when computing depth buffer values @@ -892,8 +895,8 @@ void Begin3dMode(Camera camera) rlLoadIdentity(); // Reset current matrix (MODELVIEW) // Setup Camera view - Matrix cameraView = MatrixLookAt(camera.position, camera.target, camera.up); - rlMultMatrixf(MatrixToFloat(cameraView)); // Multiply MODELVIEW matrix by view matrix (camera) + Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); + rlMultMatrixf(MatrixToFloat(matView)); // Multiply MODELVIEW matrix by view matrix (camera) rlEnableDepthTest(); // Enable DEPTH_TEST for 3D } @@ -909,8 +912,6 @@ void End3dMode(void) rlMatrixMode(RL_MODELVIEW); // Get back to modelview matrix rlLoadIdentity(); // Reset current matrix (MODELVIEW) - //rlTranslatef(0.375, 0.375, 0); // HACK to ensure pixel-perfect drawing on OpenGL (after exiting 3D mode) - rlDisableDepthTest(); // Disable DEPTH_TEST for 2D } @@ -974,50 +975,21 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) // Store values in a vector Vector3 deviceCoords = { x, y, z }; - TraceLog(DEBUG, "Device coordinates: (%f, %f, %f)", deviceCoords.x, deviceCoords.y, deviceCoords.z); + TraceLog(LOG_DEBUG, "Device coordinates: (%f, %f, %f)", deviceCoords.x, deviceCoords.y, deviceCoords.z); - // Calculate projection matrix (from perspective instead of frustum) - Matrix matProj = MatrixPerspective(camera.fovy, ((double)GetScreenWidth()/(double)GetScreenHeight()), 0.01, 1000.0); + // Calculate projection matrix from perspective + Matrix matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), 0.01, 1000.0); // Calculate view matrix from camera look at Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); - // Do I need to transpose it? It seems that yes... - // NOTE: matrix order may be incorrect... In OpenGL to get world position from - // camera view it just needs to get inverted, but here we need to transpose it too. - // For example, if you get view matrix, transpose and inverted and you transform it - // to a vector, you will get its 3d world position coordinates (camera.position). - // If you don't transpose, final position will be wrong. - MatrixTranspose(&matView); - -//#define USE_RLGL_UNPROJECT -#if defined(USE_RLGL_UNPROJECT) // OPTION 1: Use rlglUnproject() - - Vector3 nearPoint = rlglUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView); - Vector3 farPoint = rlglUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView); - -#else // OPTION 2: Compute unprojection directly here - - // Calculate unproject matrix (multiply projection matrix and view matrix) and invert it - Matrix matProjView = MatrixMultiply(matProj, matView); - MatrixInvert(&matProjView); - - // Calculate far and near points - Quaternion qNear = { deviceCoords.x, deviceCoords.y, 0.0f, 1.0f }; - Quaternion qFar = { deviceCoords.x, deviceCoords.y, 1.0f, 1.0f }; - - // Multiply points by unproject matrix - QuaternionTransform(&qNear, matProjView); - QuaternionTransform(&qFar, matProjView); - - // Calculate normalized world points in vectors - Vector3 nearPoint = { qNear.x/qNear.w, qNear.y/qNear.w, qNear.z/qNear.w}; - Vector3 farPoint = { qFar.x/qFar.w, qFar.y/qFar.w, qFar.z/qFar.w}; -#endif + // Unproject far/near points + Vector3 nearPoint = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView); + Vector3 farPoint = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView); // Calculate normalized direction vector - Vector3 direction = VectorSubtract(farPoint, nearPoint); - VectorNormalize(&direction); + Vector3 direction = Vector3Subtract(farPoint, nearPoint); + Vector3Normalize(&direction); // Apply calculated vectors to ray ray.position = camera.position; @@ -1030,11 +1002,10 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera) Vector2 GetWorldToScreen(Vector3 position, Camera camera) { // Calculate projection matrix (from perspective instead of frustum - Matrix matProj = MatrixPerspective(camera.fovy, (double)GetScreenWidth()/(double)GetScreenHeight(), 0.01, 1000.0); + Matrix matProj = MatrixPerspective(camera.fovy*DEG2RAD, (double)GetScreenWidth()/(double)GetScreenHeight(), 0.01, 1000.0); // Calculate view matrix from camera look at (and transpose it) Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); - MatrixTranspose(&matView); // Convert world position vector to quaternion Quaternion worldPos = { position.x, position.y, position.z, 1.0f }; @@ -1066,7 +1037,7 @@ void SetTargetFPS(int fps) if (fps < 1) targetTime = 0.0; else targetTime = 1.0/(double)fps; - TraceLog(INFO, "Target time per frame: %02.03f milliseconds", (float)targetTime*1000); + TraceLog(LOG_INFO, "Target time per frame: %02.03f milliseconds", (float)targetTime*1000); } // Returns current FPS @@ -1095,45 +1066,6 @@ float *ColorToFloat(Color color) return buffer; } -// Converts Vector3 to float array -float *VectorToFloat(Vector3 vec) -{ - static float buffer[3]; - - buffer[0] = vec.x; - buffer[1] = vec.y; - buffer[2] = vec.z; - - return buffer; -} - -// NOTE: Returned vector is a transposed version of the Matrix struct, -// it should be this way because, despite raymath use OpenGL column-major convention, -// Matrix struct memory alignment and variables naming are not coherent -float *MatrixToFloat(Matrix mat) -{ - static float buffer[16]; - - buffer[0] = mat.m0; - buffer[1] = mat.m4; - buffer[2] = mat.m8; - buffer[3] = mat.m12; - buffer[4] = mat.m1; - buffer[5] = mat.m5; - buffer[6] = mat.m9; - buffer[7] = mat.m13; - buffer[8] = mat.m2; - buffer[9] = mat.m6; - buffer[10] = mat.m10; - buffer[11] = mat.m14; - buffer[12] = mat.m3; - buffer[13] = mat.m7; - buffer[14] = mat.m11; - buffer[15] = mat.m15; - - return buffer; -} - // Returns a Color struct from hexadecimal value Color GetColor(int hexValue) { @@ -1198,11 +1130,11 @@ void SetConfigFlags(char flags) void TakeScreenshot(const char *fileName) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) - unsigned char *imgData = rlglReadScreenPixels(renderWidth, renderHeight); + unsigned char *imgData = rlReadScreenPixels(renderWidth, renderHeight); SavePNG(fileName, imgData, renderWidth, renderHeight, 4); // Save image as PNG free(imgData); - TraceLog(INFO, "Screenshot taken: %s", fileName); + TraceLog(LOG_INFO, "Screenshot taken: %s", fileName); #endif } @@ -1301,7 +1233,7 @@ void StorageSaveValue(int position, int value) // If file doesn't exist, create a new storage data file if (!storageFile) storageFile = fopen(path, "wb"); - if (!storageFile) TraceLog(WARNING, "Storage data file could not be created"); + if (!storageFile) TraceLog(LOG_WARNING, "Storage data file could not be created"); else { // Get file size @@ -1309,7 +1241,7 @@ void StorageSaveValue(int position, int value) int fileSize = ftell(storageFile); // Size in bytes fseek(storageFile, 0, SEEK_SET); - if (fileSize < (position*4)) TraceLog(WARNING, "Storage position could not be found"); + if (fileSize < (position*4)) TraceLog(LOG_WARNING, "Storage position could not be found"); else { fseek(storageFile, (position*4), SEEK_SET); @@ -1338,7 +1270,7 @@ int StorageLoadValue(int position) // Try open existing file to append data FILE *storageFile = fopen(path, "rb"); - if (!storageFile) TraceLog(WARNING, "Storage data file could not be found"); + if (!storageFile) TraceLog(LOG_WARNING, "Storage data file could not be found"); else { // Get file size @@ -1346,7 +1278,7 @@ int StorageLoadValue(int position) int fileSize = ftell(storageFile); // Size in bytes rewind(storageFile); - if (fileSize < (position*4)) TraceLog(WARNING, "Storage position could not be found"); + if (fileSize < (position*4)) TraceLog(LOG_WARNING, "Storage position could not be found"); else { fseek(storageFile, (position*4), SEEK_SET); @@ -1672,7 +1604,7 @@ Vector2 GetTouchPosition(int index) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) if (index < MAX_TOUCH_POINTS) position = touchPosition[index]; - else TraceLog(WARNING, "Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); + else TraceLog(LOG_WARNING, "Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) { @@ -1713,7 +1645,7 @@ static void InitGraphicsDevice(int width, int height) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSetErrorCallback(ErrorCallback); - if (!glfwInit()) TraceLog(ERROR, "Failed to initialize GLFW"); + if (!glfwInit()) TraceLog(LOG_ERROR, "Failed to initialize GLFW"); // NOTE: Getting video modes is not implemented in emscripten GLFW3 version #if defined(PLATFORM_DESKTOP) @@ -1749,7 +1681,7 @@ static void InitGraphicsDevice(int width, int height) if (configFlags & FLAG_MSAA_4X_HINT) { glfwWindowHint(GLFW_SAMPLES, 4); // Enables multisampling x4 (MSAA), default is 0 - TraceLog(INFO, "Trying to enable MSAA x4"); + TraceLog(LOG_INFO, "Trying to enable MSAA x4"); } //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits @@ -1802,7 +1734,7 @@ static void InitGraphicsDevice(int width, int height) } } - TraceLog(WARNING, "Closest fullscreen videomode: %i x %i", displayWidth, displayHeight); + TraceLog(LOG_WARNING, "Closest fullscreen videomode: %i x %i", displayWidth, displayHeight); // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), @@ -1841,17 +1773,17 @@ static void InitGraphicsDevice(int width, int height) if (!window) { glfwTerminate(); - TraceLog(ERROR, "GLFW Failed to initialize Window"); + TraceLog(LOG_ERROR, "GLFW Failed to initialize Window"); } else { - TraceLog(INFO, "Display device initialized successfully"); + TraceLog(LOG_INFO, "Display device initialized successfully"); #if defined(PLATFORM_DESKTOP) - TraceLog(INFO, "Display size: %i x %i", displayWidth, displayHeight); + TraceLog(LOG_INFO, "Display size: %i x %i", displayWidth, displayHeight); #endif - TraceLog(INFO, "Render size: %i x %i", renderWidth, renderHeight); - TraceLog(INFO, "Screen size: %i x %i", screenWidth, screenHeight); - TraceLog(INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); + TraceLog(LOG_INFO, "Render size: %i x %i", renderWidth, renderHeight); + TraceLog(LOG_INFO, "Screen size: %i x %i", screenWidth, screenHeight); + TraceLog(LOG_INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); } glfwSetWindowSizeCallback(window, WindowSizeCallback); // NOTE: Resizing not allowed by default! @@ -1875,7 +1807,7 @@ static void InitGraphicsDevice(int width, int height) #if defined(PLATFORM_DESKTOP) // Load OpenGL 3.3 extensions // NOTE: GLFW loader function is passed as parameter - rlglLoadExtensions(glfwGetProcAddress); + rlLoadExtensions(glfwGetProcAddress); #endif // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -1883,7 +1815,7 @@ static void InitGraphicsDevice(int width, int height) if (configFlags & FLAG_VSYNC_HINT) { glfwSwapInterval(1); - TraceLog(INFO, "Trying to enable VSYNC"); + TraceLog(LOG_INFO, "Trying to enable VSYNC"); } #endif // defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) @@ -1911,7 +1843,7 @@ static void InitGraphicsDevice(int width, int height) { samples = 4; sampleBuffer = 1; - TraceLog(INFO, "Trying to enable MSAA x4"); + TraceLog(LOG_INFO, "Trying to enable MSAA x4"); } const EGLint framebufferAttribs[] = @@ -2019,7 +1951,7 @@ static void InitGraphicsDevice(int width, int height) if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { - TraceLog(ERROR, "Unable to attach EGL rendering context to EGL surface"); + TraceLog(LOG_ERROR, "Unable to attach EGL rendering context to EGL surface"); } else { @@ -2027,11 +1959,11 @@ static void InitGraphicsDevice(int width, int height) //eglQuerySurface(display, surface, EGL_WIDTH, &renderWidth); //eglQuerySurface(display, surface, EGL_HEIGHT, &renderHeight); - TraceLog(INFO, "Display device initialized successfully"); - TraceLog(INFO, "Display size: %i x %i", displayWidth, displayHeight); - TraceLog(INFO, "Render size: %i x %i", renderWidth, renderHeight); - TraceLog(INFO, "Screen size: %i x %i", screenWidth, screenHeight); - TraceLog(INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); + TraceLog(LOG_INFO, "Display device initialized successfully"); + TraceLog(LOG_INFO, "Display size: %i x %i", displayWidth, displayHeight); + TraceLog(LOG_INFO, "Render size: %i x %i", renderWidth, renderHeight); + TraceLog(LOG_INFO, "Screen size: %i x %i", screenWidth, screenHeight); + TraceLog(LOG_INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); } #endif // defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) @@ -2082,7 +2014,7 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) // Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var) if ((screenWidth > displayWidth) || (screenHeight > displayHeight)) { - TraceLog(WARNING, "DOWNSCALING: Required screen size (%ix%i) is bigger than display size (%ix%i)", screenWidth, screenHeight, displayWidth, displayHeight); + TraceLog(LOG_WARNING, "DOWNSCALING: Required screen size (%ix%i) is bigger than display size (%ix%i)", screenWidth, screenHeight, displayWidth, displayHeight); // Downscaling to fit display with border-bars float widthRatio = (float)displayWidth/(float)screenWidth; @@ -2113,12 +2045,12 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight) renderWidth = displayWidth; renderHeight = displayHeight; - TraceLog(WARNING, "Downscale matrix generated, content will be rendered at: %i x %i", renderWidth, renderHeight); + TraceLog(LOG_WARNING, "Downscale matrix generated, content will be rendered at: %i x %i", renderWidth, renderHeight); } else if ((screenWidth < displayWidth) || (screenHeight < displayHeight)) { // Required screen size is smaller than display size - TraceLog(INFO, "UPSCALING: Required screen size: %i x %i -> Display size: %i x %i", screenWidth, screenHeight, displayWidth, displayHeight); + TraceLog(LOG_INFO, "UPSCALING: Required screen size: %i x %i -> Display size: %i x %i", screenWidth, screenHeight, displayWidth, displayHeight); // Upscaling to fit display with border-bars float displayRatio = (float)displayWidth/(float)displayHeight; @@ -2164,7 +2096,7 @@ static void InitTimer(void) { baseTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; } - else TraceLog(WARNING, "No hi-resolution timer available"); + else TraceLog(LOG_WARNING, "No hi-resolution timer available"); #endif previousTime = GetTime(); // Get time as double @@ -2394,7 +2326,7 @@ static void PollInputEvents(void) // NOTE: Never close window, native activity is controlled by the system! if (app->destroyRequested != 0) { - //TraceLog(INFO, "Closing Window..."); + //TraceLog(LOG_INFO, "Closing Window..."); //windowShouldClose = true; //ANativeActivity_finish(app->activity); } @@ -2428,7 +2360,7 @@ static void SwapBuffers(void) // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { - TraceLog(WARNING, "[GLFW3 Error] Code: %i Decription: %s", error, description); + TraceLog(LOG_WARNING, "[GLFW3 Error] Code: %i Decription: %s", error, description); } // GLFW3 Srolling Callback, runs on mouse wheel @@ -2454,10 +2386,10 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i { if (gifRecording) { - GifEnd(&gifWriter); + GifEnd(); gifRecording = false; - TraceLog(INFO, "End animated GIF recording"); + TraceLog(LOG_INFO, "End animated GIF recording"); } else { @@ -2466,10 +2398,10 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // NOTE: delay represents the time between frames in the gif, if we capture a gif frame every // 10 game frames and each frame trakes 16.6ms (60fps), delay between gif frames should be ~16.6*10. - GifBegin(&gifWriter, FormatText("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false); + GifBegin(FormatText("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false); screenshotCounter++; - TraceLog(INFO, "Begin animated GIF recording: %s", FormatText("screenrec%03i.gif", screenshotCounter)); + TraceLog(LOG_INFO, "Begin animated GIF recording: %s", FormatText("screenrec%03i.gif", screenshotCounter)); } } else @@ -2554,7 +2486,7 @@ static void CharCallback(GLFWwindow *window, unsigned int key) { lastKeyPressed = key; - //TraceLog(INFO, "Char Callback Key pressed: %i\n", key); + //TraceLog(LOG_INFO, "Char Callback Key pressed: %i\n", key); } // GLFW3 CursorEnter Callback, when cursor enters the window @@ -2623,15 +2555,15 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_START: { //rendering = true; - TraceLog(INFO, "APP_CMD_START"); + TraceLog(LOG_INFO, "APP_CMD_START"); } break; case APP_CMD_RESUME: { - TraceLog(INFO, "APP_CMD_RESUME"); + TraceLog(LOG_INFO, "APP_CMD_RESUME"); } break; case APP_CMD_INIT_WINDOW: { - TraceLog(INFO, "APP_CMD_INIT_WINDOW"); + TraceLog(LOG_INFO, "APP_CMD_INIT_WINDOW"); if (app->window != NULL) { @@ -2688,18 +2620,18 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } break; case APP_CMD_GAINED_FOCUS: { - TraceLog(INFO, "APP_CMD_GAINED_FOCUS"); + TraceLog(LOG_INFO, "APP_CMD_GAINED_FOCUS"); appEnabled = true; //ResumeMusicStream(); } break; case APP_CMD_PAUSE: { - TraceLog(INFO, "APP_CMD_PAUSE"); + TraceLog(LOG_INFO, "APP_CMD_PAUSE"); } break; case APP_CMD_LOST_FOCUS: { //DrawFrame(); - TraceLog(INFO, "APP_CMD_LOST_FOCUS"); + TraceLog(LOG_INFO, "APP_CMD_LOST_FOCUS"); appEnabled = false; //PauseMusicStream(); } break; @@ -2713,22 +2645,22 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) contextRebindRequired = true; - TraceLog(INFO, "APP_CMD_TERM_WINDOW"); + TraceLog(LOG_INFO, "APP_CMD_TERM_WINDOW"); } break; case APP_CMD_SAVE_STATE: { - TraceLog(INFO, "APP_CMD_SAVE_STATE"); + TraceLog(LOG_INFO, "APP_CMD_SAVE_STATE"); } break; case APP_CMD_STOP: { - TraceLog(INFO, "APP_CMD_STOP"); + TraceLog(LOG_INFO, "APP_CMD_STOP"); } break; case APP_CMD_DESTROY: { // TODO: Finish activity? //ANativeActivity_finish(app->activity); - TraceLog(INFO, "APP_CMD_DESTROY"); + TraceLog(LOG_INFO, "APP_CMD_DESTROY"); } break; case APP_CMD_CONFIG_CHANGED: { @@ -2737,7 +2669,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Check screen orientation here! - TraceLog(INFO, "APP_CMD_CONFIG_CHANGED"); + TraceLog(LOG_INFO, "APP_CMD_CONFIG_CHANGED"); } break; default: break; } @@ -2845,11 +2777,11 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte if (e->isFullscreen) { - TraceLog(INFO, "Canvas scaled to fullscreen. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight); + TraceLog(LOG_INFO, "Canvas scaled to fullscreen. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight); } else { - TraceLog(INFO, "Canvas scaled to windowed. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight); + TraceLog(LOG_INFO, "Canvas scaled to windowed. ElementSize: (%ix%i), ScreenSize(%ix%i)", e->elementWidth, e->elementHeight, e->screenWidth, e->screenHeight); } // TODO: Depending on scaling factor (screen vs element), calculate factor to scale mouse/touch input @@ -2882,7 +2814,7 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent { emscripten_exit_pointerlock(); emscripten_get_pointerlock_status(&plce); - //if (plce.isActive) TraceLog(WARNING, "Pointer lock exit did not work!"); + //if (plce.isActive) TraceLog(LOG_WARNING, "Pointer lock exit did not work!"); } toggleCursorLock = false; @@ -3008,7 +2940,7 @@ static void InitKeyboard(void) if (ioctl(STDIN_FILENO, KDGKBMODE, &defaultKeyboardMode) < 0) { // NOTE: It could mean we are using a remote keyboard through ssh! - TraceLog(WARNING, "Could not change keyboard mode (SSH keyboard?)"); + TraceLog(LOG_WARNING, "Could not change keyboard mode (SSH keyboard?)"); } else { @@ -3043,7 +2975,7 @@ static void ProcessKeyboard(void) // Fill all read bytes (looking for keys) for (int i = 0; i < bufferByteCount; i++) { - TraceLog(DEBUG, "Bytes on keysBuffer: %i", bufferByteCount); + TraceLog(LOG_DEBUG, "Bytes on keysBuffer: %i", bufferByteCount); //printf("Key(s) bytes: "); //for (int i = 0; i < bufferByteCount; i++) printf("0x%02x ", keysBuffer[i]); @@ -3104,7 +3036,7 @@ static void ProcessKeyboard(void) else if (keysBuffer[i] == 0x7f) currentKeyState[259] = 1; // raylib KEY_BACKSPACE else { - TraceLog(DEBUG, "Pressed key (ASCII): 0x%02x", keysBuffer[i]); + TraceLog(LOG_DEBUG, "Pressed key (ASCII): 0x%02x", keysBuffer[i]); // Translate lowercase a-z letters to A-Z if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122)) @@ -3142,7 +3074,7 @@ static void InitMouse(void) // NOTE: We can use /dev/input/mice to read from all available mice if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0) { - TraceLog(WARNING, "Mouse device could not be opened, no mouse available"); + TraceLog(LOG_WARNING, "Mouse device could not be opened, no mouse available"); } else { @@ -3150,8 +3082,8 @@ static void InitMouse(void) int error = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL); - if (error != 0) TraceLog(WARNING, "Error creating mouse input event thread"); - else TraceLog(INFO, "Mouse device initialized successfully"); + if (error != 0) TraceLog(LOG_WARNING, "Error creating mouse input event thread"); + else TraceLog(LOG_INFO, "Mouse device initialized successfully"); } } @@ -3221,7 +3153,7 @@ 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"); + TraceLog(LOG_WARNING, "Touch device could not be opened, no touchscreen available"); } else { @@ -3229,8 +3161,8 @@ static void InitTouch(void) 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"); + if (error != 0) TraceLog(LOG_WARNING, "Error creating touch input event thread"); + else TraceLog(LOG_INFO, "Touch device initialized successfully"); } } @@ -3332,7 +3264,7 @@ static void InitGamepad(void) if ((gamepadStream[i] = open(gamepadDev, O_RDONLY|O_NONBLOCK)) < 0) { // NOTE: Only show message for first gamepad - if (i == 0) TraceLog(WARNING, "Gamepad device could not be opened, no gamepad available"); + if (i == 0) TraceLog(LOG_WARNING, "Gamepad device could not be opened, no gamepad available"); } else { @@ -3343,8 +3275,8 @@ static void InitGamepad(void) { int error = pthread_create(&gamepadThreadId, NULL, &GamepadThread, NULL); - if (error != 0) TraceLog(WARNING, "Error creating gamepad input event thread"); - else TraceLog(INFO, "Gamepad device initialized successfully"); + if (error != 0) TraceLog(LOG_WARNING, "Error creating gamepad input event thread"); + else TraceLog(LOG_INFO, "Gamepad device initialized successfully"); } } } @@ -3378,7 +3310,7 @@ static void *GamepadThread(void *arg) // Process gamepad events by type if (gamepadEvent.type == JS_EVENT_BUTTON) { - TraceLog(DEBUG, "Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + TraceLog(LOG_DEBUG, "Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS) { @@ -3391,7 +3323,7 @@ static void *GamepadThread(void *arg) } else if (gamepadEvent.type == JS_EVENT_AXIS) { - TraceLog(DEBUG, "Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + TraceLog(LOG_DEBUG, "Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); if (gamepadEvent.number < MAX_GAMEPAD_AXIS) { diff --git a/src/external/gif.h b/src/external/gif.h index 1c0aeb18..c38cddb7 100644 --- a/src/external/gif.h +++ b/src/external/gif.h @@ -55,7 +55,6 @@ #define GIF_H #include <stdio.h> // Required for: FILE -#include <stdint.h> // Required for for integer typedefs //#define GIF_STATIC #ifdef GIF_STATIC @@ -69,32 +68,13 @@ #endif //---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -//#define MAX_RESOURCES_SUPPORTED 256 - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -typedef struct GifWriter { - FILE *f; - uint8_t *oldImage; - bool firstFrame; -} GifWriter; - -//---------------------------------------------------------------------------------- -// Global variables -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- // NOTE: By default use bitDepth = 8, dither = false -GIFDEF bool GifBegin(GifWriter *writer, const char *filename, uint32_t width, uint32_t height, uint32_t delay, int32_t bitDepth, bool dither); -GIFDEF bool GifWriteFrame(GifWriter *writer, const uint8_t *image, uint32_t width, uint32_t height, uint32_t delay, int bitDepth, bool dither); -GIFDEF bool GifEnd(GifWriter *writer); +GIFDEF bool GifBegin(const char *filename, unsigned int width, unsigned int height, unsigned int delay, unsigned int bitDepth, bool dither); +GIFDEF bool GifWriteFrame(const unsigned char *image, unsigned int width, unsigned int height, unsigned int delay, int bitDepth, bool dither); +GIFDEF bool GifEnd(); #endif // GIF_H @@ -109,7 +89,6 @@ GIFDEF bool GifEnd(GifWriter *writer); #include <stdio.h> // Required for: FILE, fopen(), fclose() #include <string.h> // Required for: memcpy() -#include <stdint.h> // Required for for integer typedefs // Define these macros to hook into a custom memory allocator. // GIF_TEMP_MALLOC and GIF_TEMP_FREE will only be called in stack fashion - frees in the reverse order of mallocs @@ -142,30 +121,30 @@ GIFDEF bool GifEnd(GifWriter *writer); // Types and Structures Definition //---------------------------------------------------------------------------------- -//Gif palette structure +// Gif palette structure typedef struct GifPalette { int bitDepth; - uint8_t r[256]; - uint8_t g[256]; - uint8_t b[256]; + unsigned char r[256]; + unsigned char g[256]; + unsigned char b[256]; // k-d tree over RGB space, organized in heap fashion // i.e. left child of node i is node i*2, right child is node i*2 + 1 // nodes 256-511 are implicitly the leaves, containing a color - uint8_t treeSplitElt[255]; - uint8_t treeSplit[255]; + unsigned char treeSplitElt[255]; + unsigned char treeSplit[255]; } GifPalette; // Simple structure to write out the LZW-compressed // portion of the imageone bit at a time typedef struct GifBitStatus { - uint8_t bitIndex; // how many bits in the partial byte written so far - uint8_t byte; // current partial byte + unsigned char bitIndex; // how many bits in the partial byte written so far + unsigned char byte; // current partial byte - uint32_t chunkIndex; - uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file + unsigned int chunkIndex; + unsigned char chunk[256]; // bytes are written in here until we have 256 of them, then written to the file } GifBitStatus; // The LZW dictionary is a 256-ary tree constructed @@ -177,109 +156,111 @@ typedef struct GifLzwNode { //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -const int kGifTransIndex = 0; +const int gifTransparentIndex = 0; // Transparent color index + +static FILE *gifFile; +unsigned char *gifFrame; //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- static void GifGetClosestPaletteColor(GifPalette *pPal, int r, int g, int b, int *bestInd, int *bestDiff, int treeRoot); -static void GifSwapPixels(uint8_t *image, int pixA, int pixB); -static int GifPartition(uint8_t *image, const int left, const int right, const int elt, int pivotIndex); -static void GifPartitionByMedian(uint8_t *image, int left, int right, int com, int neededCenter); -static void GifSplitPalette(uint8_t *image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, int treeNode, bool buildForDither, GifPalette *pal); -static int GifPickChangedPixels(const uint8_t *lastFrame, uint8_t *frame, int numPixels); -static void GifMakePalette(const uint8_t *lastFrame, const uint8_t *nextFrame, uint32_t width, uint32_t height, int bitDepth, bool buildForDither, GifPalette *pPal); -static void GifDitherImage(const uint8_t *lastFrame, const uint8_t *nextFrame, uint8_t *outFrame, uint32_t width, uint32_t height, GifPalette *pPal); -static void GifThresholdImage(const uint8_t *lastFrame, const uint8_t *nextFrame, uint8_t *outFrame, uint32_t width, uint32_t height, GifPalette *pPal); -static void GifWriteBit(GifBitStatus *stat, uint32_t bit); +static void GifSwapPixels(unsigned char *image, int pixA, int pixB); +static int GifPartition(unsigned char *image, const int left, const int right, const int elt, int pivotIndex); +static void GifPartitionByMedian(unsigned char *image, int left, int right, int com, int neededCenter); +static void GifSplitPalette(unsigned char *image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, int treeNode, bool buildForDither, GifPalette *pal); +static int GifPickChangedPixels(const unsigned char *lastFrame, unsigned char *frame, int numPixels); +static void GifMakePalette(const unsigned char *lastFrame, const unsigned char *nextFrame, unsigned int width, unsigned int height, int bitDepth, bool buildForDither, GifPalette *pPal); +static void GifDitherImage(const unsigned char *lastFrame, const unsigned char *nextFrame, unsigned char *outFrame, unsigned int width, unsigned int height, GifPalette *pPal); +static void GifThresholdImage(const unsigned char *lastFrame, const unsigned char *nextFrame, unsigned char *outFrame, unsigned int width, unsigned int height, GifPalette *pPal); +static void GifWriteBit(GifBitStatus *stat, unsigned int bit); static void GifWriteChunk(FILE *f, GifBitStatus *stat); -static void GifWriteCode(FILE *f, GifBitStatus *stat, uint32_t code, uint32_t length); +static void GifWriteCode(FILE *f, GifBitStatus *stat, unsigned int code, unsigned int length); static void GifWritePalette(const GifPalette *pPal, FILE *f); -static void GifWriteLzwImage(FILE *f, uint8_t *image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette *pPal); +static void GifWriteLzwImage(FILE *f, unsigned char *image, unsigned int left, unsigned int top, unsigned int width, unsigned int height, unsigned int delay, GifPalette *pPal); //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- -// Creates a gif file. -// The input GIFWriter is assumed to be uninitialized. +// Creates a gif file +// NOTE: Initializes internal file pointer (only one gif recording at a time) // The delay value is the time between frames in hundredths of a second - note that not all viewers pay much attention to this value. -GIFDEF bool GifBegin(GifWriter *writer, const char *filename, uint32_t width, uint32_t height, uint32_t delay, int32_t bitDepth, bool dither) +GIFDEF bool GifBegin(const char *filename, unsigned int width, unsigned int height, unsigned int delay, unsigned int bitDepth, bool dither) { #if _MSC_VER >= 1400 - writer->f = 0; - fopen_s(&writer->f, filename, "wb"); + gifFile = 0; + fopen_s(&gifFile, filename, "wb"); #else - writer->f = fopen(filename, "wb"); + gifFile = fopen(filename, "wb"); #endif - if (!writer->f) return false; + + if (!gifFile) return false; - writer->firstFrame = true; + // Allocate space for one gif frame + gifFrame = (unsigned char *)GIF_MALLOC(width*height*4); - // allocate - writer->oldImage = (uint8_t*)GIF_MALLOC(width*height*4); + // GIF Header + fputs("GIF89a",gifFile); - fputs("GIF89a", writer->f); + // Reference: http://www.onicos.com/staff/iz/formats/gif.html - // screen descriptor - fputc(width & 0xff, writer->f); - fputc((width >> 8) & 0xff, writer->f); - fputc(height & 0xff, writer->f); - fputc((height >> 8) & 0xff, writer->f); + // GIF Screen Descriptor + fputc(width & 0xff, gifFile); + fputc((width >> 8) & 0xff, gifFile); // Screen width (2 byte) + fputc(height & 0xff, gifFile); + fputc((height >> 8) & 0xff, gifFile); // Screen height (2 byte) - fputc(0xf0, writer->f); // there is an unsorted global color table of 2 entries - fputc(0, writer->f); // background color - fputc(0, writer->f); // pixels are square (we need to specify this because it's 1989) + fputc(0xf0, gifFile); // Color table flags: unsorted global color table of 2 entries (1 byte, bit-flags) + fputc(0, gifFile); // Background color index + fputc(0, gifFile); // Pixel Aspect Ratio (square, we need to specify this because it's 1989) - // now the "global" palette (really just a dummy palette) - // color 0: black - fputc(0, writer->f); - fputc(0, writer->f); - fputc(0, writer->f); - // color 1: also black - fputc(0, writer->f); - fputc(0, writer->f); - fputc(0, writer->f); + // GIF Global Color table (just a dummy palette) + // Color 0: black + fputc(0, gifFile); + fputc(0, gifFile); + fputc(0, gifFile); + // Color 1: also black + fputc(0, gifFile); + fputc(0, gifFile); + fputc(0, gifFile); if (delay != 0) { - // animation header - fputc(0x21, writer->f); // extension - fputc(0xff, writer->f); // application specific - fputc(11, writer->f); // length 11 - fputs("NETSCAPE2.0", writer->f); // yes, really - fputc(3, writer->f); // 3 bytes of NETSCAPE2.0 data + // Application Extension Block (19 bytes long) + fputc(0x21, gifFile); // GIF Extension code + fputc(0xff, gifFile); // Application Extension Label + fputc(11, gifFile); // Length of Application Block (11 byte) + fputs("NETSCAPE2.0", gifFile); // Application Identifier (Netscape 2.0 block) - fputc(1, writer->f); // JUST BECAUSE - fputc(0, writer->f); // loop infinitely (byte 0) - fputc(0, writer->f); // loop infinitely (byte 1) + fputc(0x03, gifFile); // Length of Data Sub-Block (3 bytes) + fputc(0x01, gifFile); // 0x01 + fputc(0x00, gifFile); // This specifies the number of times, + fputc(0x00, gifFile); // the loop should be executed (infinitely) - fputc(0, writer->f); // block terminator + fputc(0x00, gifFile); // Data Sub-Block Terminator. } return true; } // Writes out a new frame to a GIF in progress. -// The GIFWriter should have been created by GIFBegin. +// NOTE: gifFile should have been initialized with GifBegin() // AFAIK, it is legal to use different bit depths for different frames of an image - // this may be handy to save bits in animations that don't change much. -GIFDEF bool GifWriteFrame(GifWriter *writer, const uint8_t *image, uint32_t width, uint32_t height, uint32_t delay, int bitDepth, bool dither) +GIFDEF bool GifWriteFrame(const unsigned char *image, unsigned int width, unsigned int height, unsigned int delay, int bitDepth, bool dither) { - if (!writer->f) return false; + if (!gifFile) return false; - const uint8_t *oldImage = writer->firstFrame? NULL : writer->oldImage; - writer->firstFrame = false; + const unsigned char *oldImage = gifFrame; GifPalette pal; - GifMakePalette((dither? NULL : oldImage), image, width, height, bitDepth, dither, &pal); + GifMakePalette((dither ? NULL : oldImage), image, width, height, bitDepth, dither, &pal); - if (dither) - GifDitherImage(oldImage, image, writer->oldImage, width, height, &pal); - else - GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal); + if (dither) GifDitherImage(oldImage, image, gifFrame, width, height, &pal); + else GifThresholdImage(oldImage, image, gifFrame, width, height, &pal); - GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal); + GifWriteLzwImage(gifFile, gifFrame, 0, 0, width, height, delay, &pal); return true; } @@ -287,16 +268,17 @@ GIFDEF bool GifWriteFrame(GifWriter *writer, const uint8_t *image, uint32_t widt // Writes the EOF code, closes the file handle, and frees temp memory used by a GIF. // Many if not most viewers will still display a GIF properly if the EOF code is missing, // but it's still a good idea to write it out. -GIFDEF bool GifEnd(GifWriter *writer) +GIFDEF bool GifEnd() { - if (!writer->f) return false; + if (!gifFile) return false; + + fputc(0x3b, gifFile); // Trailer (end of file) + fclose(gifFile); - fputc(0x3b, writer->f); // end of file - fclose(writer->f); - GIF_FREE(writer->oldImage); + GIF_FREE(gifFrame); - writer->f = NULL; - writer->oldImage = NULL; + gifFile = NULL; + gifFrame = NULL; return true; } @@ -314,7 +296,7 @@ static void GifGetClosestPaletteColor(GifPalette *pPal, int r, int g, int b, int if (treeRoot > (1<<pPal->bitDepth)-1) { int ind = treeRoot-(1<<pPal->bitDepth); - if (ind == kGifTransIndex) return; + if (ind == gifTransparentIndex) return; // check whether this color is better than the current winner int r_err = r - ((int32_t)pPal->r[ind]); @@ -358,17 +340,17 @@ static void GifGetClosestPaletteColor(GifPalette *pPal, int r, int g, int b, int } } -static void GifSwapPixels(uint8_t *image, int pixA, int pixB) +static void GifSwapPixels(unsigned char *image, int pixA, int pixB) { - uint8_t rA = image[pixA*4]; - uint8_t gA = image[pixA*4 + 1]; - uint8_t bA = image[pixA*4+2]; - uint8_t aA = image[pixA*4+3]; + unsigned char rA = image[pixA*4]; + unsigned char gA = image[pixA*4 + 1]; + unsigned char bA = image[pixA*4+2]; + unsigned char aA = image[pixA*4+3]; - uint8_t rB = image[pixB*4]; - uint8_t gB = image[pixB*4 + 1]; - uint8_t bB = image[pixB*4+2]; - uint8_t aB = image[pixA*4+3]; + unsigned char rB = image[pixB*4]; + unsigned char gB = image[pixB*4 + 1]; + unsigned char bB = image[pixB*4+2]; + unsigned char aB = image[pixA*4+3]; image[pixA*4] = rB; image[pixA*4 + 1] = gB; @@ -382,7 +364,7 @@ static void GifSwapPixels(uint8_t *image, int pixA, int pixB) } // just the partition operation from quicksort -static int GifPartition(uint8_t *image, const int left, const int right, const int elt, int pivotIndex) +static int GifPartition(unsigned char *image, const int left, const int right, const int elt, int pivotIndex) { const int pivotValue = image[(pivotIndex)*4+elt]; GifSwapPixels(image, pivotIndex, right-1); @@ -411,7 +393,7 @@ static int GifPartition(uint8_t *image, const int left, const int right, const i } // Perform an incomplete sort, finding all elements above and below the desired median -static void GifPartitionByMedian(uint8_t *image, int left, int right, int com, int neededCenter) +static void GifPartitionByMedian(unsigned char *image, int left, int right, int com, int neededCenter) { if (left < right-1) { @@ -429,7 +411,7 @@ static void GifPartitionByMedian(uint8_t *image, int left, int right, int com, i } // Builds a palette by creating a balanced k-d tree of all pixels in the image -static void GifSplitPalette(uint8_t *image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, +static void GifSplitPalette(unsigned char *image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, int treeNode, bool buildForDither, GifPalette *pal) { if (lastElt <= firstElt || numPixels == 0) @@ -446,7 +428,7 @@ static void GifSplitPalette(uint8_t *image, int numPixels, int firstElt, int las if (firstElt == 1) { // special case: the darkest color in the image - uint32_t r=255, g=255, b=255; + unsigned int r=255, g=255, b=255; for (int ii=0; ii<numPixels; ++ii) { r = GIFMIN(r, image[ii*4+0]); @@ -464,7 +446,7 @@ static void GifSplitPalette(uint8_t *image, int numPixels, int firstElt, int las if (firstElt == (1 << pal->bitDepth)-1) { // special case: the lightest color in the image - uint32_t r=0, g=0, b=0; + unsigned int r=0, g=0, b=0; for (int ii=0; ii<numPixels; ++ii) { r = GIFMAX(r, image[ii*4+0]); @@ -497,9 +479,9 @@ static void GifSplitPalette(uint8_t *image, int numPixels, int firstElt, int las g /= numPixels; b /= numPixels; - pal->r[firstElt] = (uint8_t)r; - pal->g[firstElt] = (uint8_t)g; - pal->b[firstElt] = (uint8_t)b; + pal->r[firstElt] = (unsigned char)r; + pal->g[firstElt] = (unsigned char)g; + pal->b[firstElt] = (unsigned char)b; return; } @@ -549,10 +531,10 @@ static void GifSplitPalette(uint8_t *image, int numPixels, int firstElt, int las // moves them to the fromt of th buffer. // This allows us to build a palette optimized for the colors of the // changed pixels only. -static int GifPickChangedPixels(const uint8_t *lastFrame, uint8_t *frame, int numPixels) +static int GifPickChangedPixels(const unsigned char *lastFrame, unsigned char *frame, int numPixels) { int numChanged = 0; - uint8_t *writeIter = frame; + unsigned char *writeIter = frame; for (int ii=0; ii<numPixels; ++ii) { @@ -575,14 +557,14 @@ static int GifPickChangedPixels(const uint8_t *lastFrame, uint8_t *frame, int nu // Creates a palette by placing all the image pixels in a k-d tree and then averaging the blocks at the bottom. // This is known as the "modified median split" technique -static void GifMakePalette(const uint8_t *lastFrame, const uint8_t *nextFrame, uint32_t width, uint32_t height, int bitDepth, bool buildForDither, GifPalette *pPal) +static void GifMakePalette(const unsigned char *lastFrame, const unsigned char *nextFrame, unsigned int width, unsigned int height, int bitDepth, bool buildForDither, GifPalette *pPal) { pPal->bitDepth = bitDepth; // SplitPalette is destructive (it sorts the pixels by color) so // we must create a copy of the image for it to destroy - int imageSize = width*height*4*sizeof(uint8_t); - uint8_t *destroyableImage = (uint8_t*)GIF_TEMP_MALLOC(imageSize); + int imageSize = width*height*4*sizeof(unsigned char); + unsigned char *destroyableImage = (unsigned char*)GIF_TEMP_MALLOC(imageSize); memcpy(destroyableImage, nextFrame, imageSize); int numPixels = width*height; @@ -605,7 +587,7 @@ static void GifMakePalette(const uint8_t *lastFrame, const uint8_t *nextFrame, u } // Implements Floyd-Steinberg dithering, writes palette value to alpha -static void GifDitherImage(const uint8_t *lastFrame, const uint8_t *nextFrame, uint8_t *outFrame, uint32_t width, uint32_t height, GifPalette *pPal) +static void GifDitherImage(const unsigned char *lastFrame, const unsigned char *nextFrame, unsigned char *outFrame, unsigned int width, unsigned int height, GifPalette *pPal) { int numPixels = width*height; @@ -616,17 +598,17 @@ static void GifDitherImage(const uint8_t *lastFrame, const uint8_t *nextFrame, u for (int ii=0; ii<numPixels*4; ++ii) { - uint8_t pix = nextFrame[ii]; + unsigned char pix = nextFrame[ii]; int32_t pix16 = (int32_t)pix*256; quantPixels[ii] = pix16; } - for (uint32_t yy=0; yy<height; ++yy) + for (unsigned int yy=0; yy<height; ++yy) { - for (uint32_t xx=0; xx<width; ++xx) + for (unsigned int xx=0; xx<width; ++xx) { int32_t *nextPix = quantPixels + 4*(yy*width+xx); - const uint8_t *lastPix = lastFrame? lastFrame + 4*(yy*width+xx) : NULL; + const unsigned char *lastPix = lastFrame? lastFrame + 4*(yy*width+xx) : NULL; // Compute the colors we want (rounding to nearest) int32_t rr = (nextPix[0] + 127) / 256; @@ -643,12 +625,12 @@ static void GifDitherImage(const uint8_t *lastFrame, const uint8_t *nextFrame, u nextPix[0] = rr; nextPix[1] = gg; nextPix[2] = bb; - nextPix[3] = kGifTransIndex; + nextPix[3] = gifTransparentIndex; continue; } int32_t bestDiff = 1000000; - int32_t bestInd = kGifTransIndex; + int32_t bestInd = gifTransparentIndex; // Search the palete GifGetClosestPaletteColor(pPal, rr, gg, bb, &bestInd, &bestDiff, 1); @@ -714,10 +696,10 @@ static void GifDitherImage(const uint8_t *lastFrame, const uint8_t *nextFrame, u } // Picks palette colors for the image using simple thresholding, no dithering -static void GifThresholdImage(const uint8_t *lastFrame, const uint8_t *nextFrame, uint8_t *outFrame, uint32_t width, uint32_t height, GifPalette *pPal) +static void GifThresholdImage(const unsigned char *lastFrame, const unsigned char *nextFrame, unsigned char *outFrame, unsigned int width, unsigned int height, GifPalette *pPal) { - uint32_t numPixels = width*height; - for (uint32_t ii=0; ii<numPixels; ++ii) + unsigned int numPixels = width*height; + for (unsigned int ii=0; ii<numPixels; ++ii) { // if a previous color is available, and it matches the current color, // set the pixel to transparent @@ -729,7 +711,7 @@ static void GifThresholdImage(const uint8_t *lastFrame, const uint8_t *nextFrame outFrame[0] = lastFrame[0]; outFrame[1] = lastFrame[1]; outFrame[2] = lastFrame[2]; - outFrame[3] = kGifTransIndex; + outFrame[3] = gifTransparentIndex; } else { @@ -753,7 +735,7 @@ static void GifThresholdImage(const uint8_t *lastFrame, const uint8_t *nextFrame // insert a single bit -static void GifWriteBit(GifBitStatus *stat, uint32_t bit) +static void GifWriteBit(GifBitStatus *stat, unsigned int bit) { bit = bit & 1; bit = bit << stat->bitIndex; @@ -781,9 +763,9 @@ static void GifWriteChunk(FILE *f, GifBitStatus *stat) stat->chunkIndex = 0; } -static void GifWriteCode(FILE *f, GifBitStatus *stat, uint32_t code, uint32_t length) +static void GifWriteCode(FILE *f, GifBitStatus *stat, unsigned int code, unsigned int length) { - for (uint32_t ii=0; ii<length; ++ii) + for (unsigned int ii=0; ii<length; ++ii) { GifWriteBit(stat, code); code = code >> 1; @@ -804,9 +786,9 @@ static void GifWritePalette(const GifPalette *pPal, FILE *f) for (int ii=1; ii<(1 << pPal->bitDepth); ++ii) { - uint32_t r = pPal->r[ii]; - uint32_t g = pPal->g[ii]; - uint32_t b = pPal->b[ii]; + unsigned int r = pPal->r[ii]; + unsigned int g = pPal->g[ii]; + unsigned int b = pPal->b[ii]; fputc(r, f); fputc(g, f); @@ -815,7 +797,7 @@ static void GifWritePalette(const GifPalette *pPal, FILE *f) } // write the image header, LZW-compress and write out the image -static void GifWriteLzwImage(FILE *f, uint8_t *image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette *pPal) +static void GifWriteLzwImage(FILE *f, unsigned char *image, unsigned int left, unsigned int top, unsigned int width, unsigned int height, unsigned int delay, GifPalette *pPal) { // graphics control extension fputc(0x21, f); @@ -824,7 +806,7 @@ static void GifWriteLzwImage(FILE *f, uint8_t *image, uint32_t left, uint32_t to fputc(0x05, f); // leave prev frame in place, this frame has transparency fputc(delay & 0xff, f); fputc((delay >> 8) & 0xff, f); - fputc(kGifTransIndex, f); // transparent color index + fputc(gifTransparentIndex, f); // transparent color index fputc(0, f); fputc(0x2c, f); // image descriptor block @@ -846,7 +828,7 @@ static void GifWriteLzwImage(FILE *f, uint8_t *image, uint32_t left, uint32_t to GifWritePalette(pPal, f); const int minCodeSize = pPal->bitDepth; - const uint32_t clearCode = 1 << pPal->bitDepth; + const unsigned int clearCode = 1 << pPal->bitDepth; fputc(minCodeSize, f); // min code size 8 bits @@ -854,8 +836,8 @@ static void GifWriteLzwImage(FILE *f, uint8_t *image, uint32_t left, uint32_t to memset(codetree, 0, sizeof(GifLzwNode)*4096); int32_t curCode = -1; - uint32_t codeSize = minCodeSize + 1; - uint32_t maxCode = clearCode + 1; + unsigned int codeSize = minCodeSize + 1; + unsigned int maxCode = clearCode + 1; GifBitStatus stat; stat.byte = 0; @@ -864,11 +846,11 @@ static void GifWriteLzwImage(FILE *f, uint8_t *image, uint32_t left, uint32_t to GifWriteCode(f, &stat, clearCode, codeSize); // start with a fresh LZW dictionary - for (uint32_t yy=0; yy<height; ++yy) + for (unsigned int yy=0; yy<height; ++yy) { - for (uint32_t xx=0; xx<width; ++xx) + for (unsigned int xx=0; xx<width; ++xx) { - uint8_t nextValue = image[(yy*width+xx)*4+3]; + unsigned char nextValue = image[(yy*width+xx)*4+3]; // "loser mode" - no compression, every single code is followed immediately by a clear //WriteCode(f, stat, nextValue, codeSize); diff --git a/src/external/jar_xm.h b/src/external/jar_xm.h index 7f0517df..15b31fd0 100644 --- a/src/external/jar_xm.h +++ b/src/external/jar_xm.h @@ -308,7 +308,7 @@ uint64_t jar_xm_get_remaining_samples(jar_xm_context_t* ctx); #include <math.h> #include <string.h> -#ifdef JAR_XM_DEBUG +#if JAR_XM_DEBUG //JAR_XM_DEBUG defined as 0 #include <stdio.h> #define DEBUG(fmt, ...) do { \ fprintf(stderr, "%s(): " fmt "\n", __func__, __VA_ARGS__); \ @@ -782,7 +782,7 @@ uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t* ctx, uint16_t ch #define READ_U32(offset) ((uint32_t)READ_U16(offset) | ((uint32_t)READ_U16((offset) + 2) << 16)) #define READ_MEMCPY(ptr, offset, length) memcpy_pad(ptr, length, moddata, moddata_length, offset) -static inline void memcpy_pad(void* dst, size_t dst_len, const void* src, size_t src_len, size_t offset) { +static void memcpy_pad(void* dst, size_t dst_len, const void* src, size_t src_len, size_t offset) { uint8_t* dst_c = dst; const uint8_t* src_c = src; diff --git a/src/external/stb_image.h b/src/external/stb_image.h index ae2ada6a..a056138d 100644 --- a/src/external/stb_image.h +++ b/src/external/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.15 - public domain image loader - http://nothings.org/stb_image.h +/* stb_image - v2.16 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: @@ -48,6 +48,7 @@ LICENSE RECENT REVISION HISTORY: + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes @@ -58,10 +59,6 @@ RECENT REVISION HISTORY: correct channel count for PNG & BMP 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) partial animated GIF support - limited 16-bit PSD support - minor bugs, code cleanup, and compiler warnings See end of file for full revision history. @@ -83,6 +80,7 @@ RECENT REVISION HISTORY: Optimizations & bugfixes Fabian "ryg" Giesen Arseny Kapoulkine + John-Mark Allen Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko @@ -98,7 +96,7 @@ RECENT REVISION HISTORY: Michaelangel007@github Philipp Wiesemann Dale Weiler github:grim210 Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:sammyhw Blazej Dariusz Roszkowski Gregory Mullen github:phprus - + Christian Floisand Kevin Schmidt github:poppolopoppo */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -134,11 +132,12 @@ RECENT REVISION HISTORY: // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to see if it's trivially opaque -// because e.g. there were only 3 channels in the source image. +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: @@ -150,10 +149,10 @@ RECENT REVISION HISTORY: // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. @@ -310,7 +309,7 @@ RECENT REVISION HISTORY: enum { - STBI_default = 0, // only used for req_comp + STBI_default = 0, // only used for desired_channels STBI_grey = 1, STBI_grey_alpha = 2, @@ -352,12 +351,12 @@ typedef struct // 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 *channels_in_file, int desired_channels); +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_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 @@ -366,22 +365,24 @@ STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_i // 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); +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_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_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); 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 *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 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif @@ -639,7 +640,7 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name -static int stbi__sse2_available() +static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; @@ -647,7 +648,7 @@ static int stbi__sse2_available() #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -static int stbi__sse2_available() +static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means // -msse2 is on, which means the compiler is allowed to use SSE2 @@ -1029,6 +1030,30 @@ static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int chan return enlarged; } +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; @@ -1046,21 +1071,8 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, // @TODO: move stbi__convert_format to here if (stbi__vertically_flip_on_load) { - int w = *x, h = *y; int channels = req_comp ? req_comp : *comp; - int row,col,z; - 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 < 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; - } - } - } + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); } return (unsigned char *) result; @@ -1084,21 +1096,8 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, // @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; - } - } - } + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); } return (stbi__uint16 *) result; @@ -1108,21 +1107,8 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - float temp; - - // @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; - } - } - } + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); } } #endif @@ -1191,6 +1177,20 @@ STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, i #endif //!STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + 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; @@ -2806,7 +2806,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = sixteen ? stbi__get16be(z->s) : stbi__get8(z->s); + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); L -= (sixteen ? 129 : 65); } return L==0; @@ -3611,20 +3611,20 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } else if (z->s->img_n == 4) { if (z->app14_color_transform == 0) { // CMYK for (i=0; i < z->s->img_x; ++i) { - stbi_uc k = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], k); - out[1] = stbi__blinn_8x8(coutput[1][i], k); - out[2] = stbi__blinn_8x8(coutput[2][i], k); + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); out[3] = 255; out += n; } } else if (z->app14_color_transform == 2) { // YCCK z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); for (i=0; i < z->s->img_x; ++i) { - stbi_uc k = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], k); - out[1] = stbi__blinn_8x8(255 - out[1], k); - out[2] = stbi__blinn_8x8(255 - out[2], k); + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); out += n; } } else { // YCbCr + alpha? Ignore the fourth channel for now @@ -3649,10 +3649,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp } } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { for (i=0; i < z->s->img_x; ++i) { - stbi_uc k = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], k); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], k); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], k); + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); out[0] = stbi__compute_y(r, g, b); out[1] = 255; out += n; @@ -4297,11 +4297,10 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; @@ -4654,9 +4653,10 @@ static void stbi__de_iphone(stbi__png *z) stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; } else { p[0] = p[2]; p[2] = t; @@ -4819,6 +4819,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; return 1; @@ -6966,6 +6969,13 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int /* revision history: + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; warning fixes; disable run-time SSE detection on gcc; uniform handling of optional "return" values; diff --git a/src/external/stb_image_resize.h b/src/external/stb_image_resize.h index b507e049..031ca99d 100644 --- a/src/external/stb_image_resize.h +++ b/src/external/stb_image_resize.h @@ -1,4 +1,4 @@ -/* stb_image_resize - v0.94 - public domain image resizing +/* stb_image_resize - v0.95 - public domain image resizing by Jorge L Rodriguez (@VinoBS) - 2014 http://github.com/nothings/stb @@ -156,8 +156,10 @@ Jorge L Rodriguez: Implementation Sean Barrett: API design, optimizations Aras Pranckevicius: bugfix - + Nathan Reed: warning fixes + REVISIONS + 0.95 (2017-07-23) fixed warnings 0.94 (2017-03-18) fixed warnings 0.93 (2017-03-03) fixed bug with certain combinations of heights 0.92 (2017-01-02) fix integer overflow on large (>2GB) images @@ -393,8 +395,9 @@ STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int #ifndef STBIR_MALLOC #include <stdlib.h> -#define STBIR_MALLOC(size,c) malloc(size) -#define STBIR_FREE(ptr,c) free(ptr) +// use comma operator to evaluate c, to avoid "unused parameter" warnings +#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) +#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) #endif #ifndef _MSC_VER @@ -983,7 +986,7 @@ static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) return (m); } - return n; // NOTREACHED + // NOTREACHED default: STBIR_ASSERT(!"Unimplemented edge type"); diff --git a/src/external/stb_image_write.h b/src/external/stb_image_write.h index df623393..9d553e0d 100644 --- a/src/external/stb_image_write.h +++ b/src/external/stb_image_write.h @@ -1,5 +1,5 @@ -/* stb_image_write - v1.05 - public domain - http://nothings.org/stb/stb_image_write.h - writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 +/* stb_image_write - v1.07 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, @@ -35,6 +35,7 @@ USAGE: int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const float *data); There are also four equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: @@ -43,6 +44,7 @@ USAGE: int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); @@ -79,6 +81,10 @@ USAGE: TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). CREDITS: @@ -94,6 +100,9 @@ CREDITS: Alan Hickman initial file IO callback implementation Emmanuel Julien + JPEG + Jon Olick (original jo_jpeg.cpp code) + Daniel Gibson bugfixes: github:Chribba Guillaume Chereau @@ -131,6 +140,7 @@ STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #endif typedef void stbi_write_func(void *context, void *data, int size); @@ -139,6 +149,7 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); #ifdef __cplusplus } @@ -277,6 +288,11 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) va_end(v); } +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; @@ -450,7 +466,7 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v return 1; } -int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); @@ -458,7 +474,7 @@ int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, i } #ifndef STBI_WRITE_NO_STDIO -int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { @@ -620,7 +636,7 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f } } -int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); @@ -628,7 +644,7 @@ int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, i } #ifndef STBI_WRITE_NO_STDIO -int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { @@ -1013,9 +1029,359 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, return 1; } + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<<bits[1])-1); +} + +static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) { + const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] }; + const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] }; + int dataOff, i, diff, end0pos; + int DU[64]; + + // DCT rows + for(dataOff=0; dataOff<64; dataOff+=8) { + stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]); + } + // DCT columns + for(dataOff=0; dataOff<8; ++dataOff) { + stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+8], &CDU[dataOff+16], &CDU[dataOff+24], &CDU[dataOff+32], &CDU[dataOff+40], &CDU[dataOff+48], &CDU[dataOff+56]); + } + // Quantize/descale/zigzag the coefficients + for(i=0; i<64; ++i) { + float v = CDU[i]*fdtbl[i]; + // DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); + // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway? + DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); + } + + // Encode DC + diff = DU[0] - DC; + if (diff == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]); + } else { + unsigned short bits[2]; + stbiw__jpg_calcBits(diff, bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + // Encode ACs + end0pos = 63; + for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + for(col = x; col < x+8; ++col, ++pos) { + int p = row*width*comp + col*comp; + float r, g, b; + if(row >= height) { + p -= width*comp*(row+1 - height); + } + if(col >= width) { + p -= comp*(col+1 - width); + } + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? 1.04 (2017-03-03) monochrome BMP expansion 1.03 ??? diff --git a/src/external/stb_perlin.h b/src/external/stb_perlin.h new file mode 100644 index 00000000..5d762220 --- /dev/null +++ b/src/external/stb_perlin.h @@ -0,0 +1,316 @@ +// stb_perlin.h - v0.3 - perlin noise +// public domain single-file C implementation by Sean Barrett +// +// LICENSE +// +// See end of file. +// +// +// to create the implementation, +// #define STB_PERLIN_IMPLEMENTATION +// in *one* C/CPP file that includes this file. +// +// +// Documentation: +// +// float stb_perlin_noise3( float x, +// float y, +// float z, +// int x_wrap=0, +// int y_wrap=0, +// int z_wrap=0) +// +// This function computes a random value at the coordinate (x,y,z). +// Adjacent random values are continuous but the noise fluctuates +// its randomness with period 1, i.e. takes on wholly unrelated values +// at integer points. Specifically, this implements Ken Perlin's +// revised noise function from 2002. +// +// The "wrap" parameters can be used to create wraparound noise that +// wraps at powers of two. The numbers MUST be powers of two. Specify +// 0 to mean "don't care". (The noise always wraps every 256 due +// details of the implementation, even if you ask for larger or no +// wrapping.) +// +// Fractal Noise: +// +// Three common fractal noise functions are included, which produce +// a wide variety of nice effects depending on the parameters +// provided. Note that each function will call stb_perlin_noise3 +// 'octaves' times, so this parameter will affect runtime. +// +// float stb_perlin_ridge_noise3(float x, float y, float z, +// float lacunarity, float gain, float offset, int octaves, +// int x_wrap, int y_wrap, int z_wrap); +// +// float stb_perlin_fbm_noise3(float x, float y, float z, +// float lacunarity, float gain, int octaves, +// int x_wrap, int y_wrap, int z_wrap); +// +// float stb_perlin_turbulence_noise3(float x, float y, float z, +// float lacunarity, float gain,int octaves, +// int x_wrap, int y_wrap, int z_wrap); +// +// Typical values to start playing with: +// octaves = 6 -- number of "octaves" of noise3() to sum +// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) +// gain = 0.5 -- relative weighting applied to each successive octave +// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure +// +// +// Contributors: +// Jack Mott - additional noise functions +// + + +#ifdef __cplusplus +extern "C" { +#endif +extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap); +extern float stb_perlin_ridge_noise3(float x, float y, float z,float lacunarity, float gain, float offset, int octaves,int x_wrap, int y_wrap, int z_wrap); +extern float stb_perlin_fbm_noise3(float x, float y, float z,float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap); +extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap); +#ifdef __cplusplus +} +#endif + +#ifdef STB_PERLIN_IMPLEMENTATION + +// not same permutation table as Perlin's reference to avoid copyright issues; +// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/ +// @OPTIMIZE: should this be unsigned char instead of int for cache? +static unsigned char stb__perlin_randtab[512] = +{ + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, + + // and a second copy so we don't need an extra mask or static initializer + 23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123, + 152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72, + 175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240, + 8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57, + 225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233, + 94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172, + 165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243, + 65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122, + 26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76, + 250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246, + 132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3, + 91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231, + 38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221, + 131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62, + 27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135, + 61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5, +}; + +static float stb__perlin_lerp(float a, float b, float t) +{ + return a + (b-a) * t; +} + +static int stb__perlin_fastfloor(float a) +{ + int ai = (int) a; + return (a < ai) ? ai-1 : ai; +} + +// different grad function from Perlin's, but easy to modify to match reference +static float stb__perlin_grad(int hash, float x, float y, float z) +{ + static float basis[12][4] = + { + { 1, 1, 0 }, + { -1, 1, 0 }, + { 1,-1, 0 }, + { -1,-1, 0 }, + { 1, 0, 1 }, + { -1, 0, 1 }, + { 1, 0,-1 }, + { -1, 0,-1 }, + { 0, 1, 1 }, + { 0,-1, 1 }, + { 0, 1,-1 }, + { 0,-1,-1 }, + }; + + // perlin's gradient has 12 cases so some get used 1/16th of the time + // and some 2/16ths. We reduce bias by changing those fractions + // to 5/64ths and 6/64ths, and the same 4 cases get the extra weight. + static unsigned char indices[64] = + { + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,9,1,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + 0,1,2,3,4,5,6,7,8,9,10,11, + }; + + // if you use reference permutation table, change 63 below to 15 to match reference + // (this is why the ordering of the table above is funky) + float *grad = basis[indices[hash & 63]]; + return grad[0]*x + grad[1]*y + grad[2]*z; +} + +float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap) +{ + float u,v,w; + float n000,n001,n010,n011,n100,n101,n110,n111; + float n00,n01,n10,n11; + float n0,n1; + + unsigned int x_mask = (x_wrap-1) & 255; + unsigned int y_mask = (y_wrap-1) & 255; + unsigned int z_mask = (z_wrap-1) & 255; + int px = stb__perlin_fastfloor(x); + int py = stb__perlin_fastfloor(y); + int pz = stb__perlin_fastfloor(z); + int x0 = px & x_mask, x1 = (px+1) & x_mask; + int y0 = py & y_mask, y1 = (py+1) & y_mask; + int z0 = pz & z_mask, z1 = (pz+1) & z_mask; + int r0,r1, r00,r01,r10,r11; + + #define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a) + + x -= px; u = stb__perlin_ease(x); + y -= py; v = stb__perlin_ease(y); + z -= pz; w = stb__perlin_ease(z); + + r0 = stb__perlin_randtab[x0]; + r1 = stb__perlin_randtab[x1]; + + r00 = stb__perlin_randtab[r0+y0]; + r01 = stb__perlin_randtab[r0+y1]; + r10 = stb__perlin_randtab[r1+y0]; + r11 = stb__perlin_randtab[r1+y1]; + + n000 = stb__perlin_grad(stb__perlin_randtab[r00+z0], x , y , z ); + n001 = stb__perlin_grad(stb__perlin_randtab[r00+z1], x , y , z-1 ); + n010 = stb__perlin_grad(stb__perlin_randtab[r01+z0], x , y-1, z ); + n011 = stb__perlin_grad(stb__perlin_randtab[r01+z1], x , y-1, z-1 ); + n100 = stb__perlin_grad(stb__perlin_randtab[r10+z0], x-1, y , z ); + n101 = stb__perlin_grad(stb__perlin_randtab[r10+z1], x-1, y , z-1 ); + n110 = stb__perlin_grad(stb__perlin_randtab[r11+z0], x-1, y-1, z ); + n111 = stb__perlin_grad(stb__perlin_randtab[r11+z1], x-1, y-1, z-1 ); + + n00 = stb__perlin_lerp(n000,n001,w); + n01 = stb__perlin_lerp(n010,n011,w); + n10 = stb__perlin_lerp(n100,n101,w); + n11 = stb__perlin_lerp(n110,n111,w); + + n0 = stb__perlin_lerp(n00,n01,v); + n1 = stb__perlin_lerp(n10,n11,v); + + return stb__perlin_lerp(n0,n1,u); +} + +float stb_perlin_ridge_noise3(float x, float y, float z,float lacunarity, float gain, float offset, int octaves,int x_wrap, int y_wrap, int z_wrap) +{ + int i; + float frequency = 1.0f; + float prev = 1.0f; + float amplitude = 0.5f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = (float)(stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)); + r = r<0 ? -r : r; // fabs() + r = offset - r; + r = r*r; + sum += r*amplitude*prev; + prev = r; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_fbm_noise3(float x, float y, float z,float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + sum += stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves,int x_wrap, int y_wrap, int z_wrap) +{ + int i; + float frequency = 1.0f; + float amplitude = 1.0f; + float sum = 0.0f; + + for (i = 0; i < octaves; i++) { + float r = stb_perlin_noise3(x*frequency,y*frequency,z*frequency,x_wrap,y_wrap,z_wrap)*amplitude; + r = r<0 ? -r : r; // fabs() + sum += r; + frequency *= lacunarity; + amplitude *= gain; + } + return sum; +} + +#endif // STB_PERLIN_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/external/stb_rect_pack.h b/src/external/stb_rect_pack.h index f5eb8d33..9faf5783 100644 --- a/src/external/stb_rect_pack.h +++ b/src/external/stb_rect_pack.h @@ -524,17 +524,6 @@ static int rect_height_compare(const void *a, const void *b) return (p->w > q->w) ? -1 : (p->w < q->w); } -static int rect_width_compare(const void *a, const void *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) - return 1; - return (p->h > q->h) ? -1 : (p->h < q->h); -} - static int rect_original_order(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; diff --git a/src/external/stb_truetype.h b/src/external/stb_truetype.h index fc5b9782..cec24254 100644 --- a/src/external/stb_truetype.h +++ b/src/external/stb_truetype.h @@ -1,4 +1,4 @@ -// stb_truetype.h - v1.15 - public domain +// stb_truetype.h - v1.17 - public domain // authored from 2009-2016 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -6,6 +6,7 @@ // extract glyph metrics // extract glyph shapes // render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) // // Todo: // non-MS cmaps @@ -26,9 +27,10 @@ // Ryan Gordon // Simon Glass // github:IntellectualKitty +// Imanol Celaya // // Bug/warning reports/fixes: -// "Zer" on mollyrocket (with fix) +// "Zer" on mollyrocket // Cass Everitt // stoiko (Haemimont Games) // Brian Hook @@ -51,9 +53,12 @@ // Thomas Fields // Derek Vinyard // Cort Stratton +// github:oyvindjam // // VERSION HISTORY // +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support // 1.15 (2017-03-03) make more arguments const // 1.14 (2017-01-16) num-fonts-in-TTC function // 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts @@ -92,7 +97,7 @@ // Improved 3D API (more shippable): // #include "stb_rect_pack.h" -- optional, but you really want it // stbtt_PackBegin() -// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackSetOversampling() -- for improved quality on small fonts // stbtt_PackFontRanges() -- pack and renders // stbtt_PackEnd() // stbtt_GetPackedQuad() @@ -110,6 +115,7 @@ // Character advance/positioning // stbtt_GetCodepointHMetrics() // stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() // stbtt_GetCodepointKernAdvance() // // Starting with version 1.06, the rasterizer was replaced with a new, @@ -407,6 +413,18 @@ int main(int arg, char **argv) #ifndef STBTT_sqrt #include <math.h> #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_cos + #include <math.h> + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include <math.h> + #define STBTT_fabs(x) fabs(x) #endif #ifndef STBTT_fabs @@ -432,7 +450,7 @@ int main(int arg, char **argv) #endif #ifndef STBTT_memcpy - #include <memory.h> + #include <string.h> #define STBTT_memcpy memcpy #define STBTT_memset memset #endif @@ -548,7 +566,7 @@ STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); #define STBTT_POINT_SIZE(x) (-(x)) -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); // Creates character bitmaps from the font_index'th font found in fontdata (use // font_index=0 if you don't know what that is). It creates num_chars_in_range @@ -573,7 +591,7 @@ typedef struct unsigned char h_oversample, v_oversample; // don't set these, they're used internally } stbtt_pack_range; -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); // Creates character bitmaps from multiple ranges of characters stored in // ranges. This will usually create a better-packed bitmap than multiple // calls to stbtt_PackFontRange. Note that you can call this multiple @@ -715,6 +733,12 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in // these are expressed in unscaled coordinates, so you must multiply by // the scale factor for a given size +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); // the bounding box around all possible characters @@ -809,6 +833,10 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel // shift for the character +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); // get the bbox of the bitmap centered around the glyph origin; so the // bitmap width is ix1-ix0, height is iy1-iy0, and location to place @@ -826,6 +854,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); @@ -850,6 +879,64 @@ STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap ////////////////////////////////////////////////////////////////////////////// // +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshhold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// // Finding the right font... // // You should really just solve this offline, keep your own tables @@ -2201,6 +2288,17 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); } +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) { *x0 = ttSHORT(info->data + info->head + 36); @@ -2693,19 +2791,18 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, // from the other y segment, and it might ignored as an empty segment. to avoid // that, we need to explicitly produce segments based on x positions. - // rename variables to clear pairs + // rename variables to clearly-defined pairs float y0 = y_top; float x1 = (float) (x); float x2 = (float) (x+1); float x3 = xb; float y3 = y_bottom; - float y1,y2; // x = e->x + e->dx * (y-y_top) // (y-y_top) = (x - e->x) / e->dx // y = (x - e->x) / e->dx + y_top - y1 = (x - x0) / dx + y_top; - y2 = (x+1 - x0) / dx + y_top; + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; if (x0 < x1 && x3 > x2) { // three segments descending down-right stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); @@ -3600,6 +3697,29 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb return k; } +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + // rects array must be big enough to accommodate all characters in the given ranges STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { @@ -3688,7 +3808,7 @@ STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); } -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) { stbtt_fontinfo info; int i,j,n, return_value = 1; @@ -3724,7 +3844,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd return return_value; } -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) { stbtt_pack_range range; @@ -3763,6 +3883,387 @@ STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int *xpos += b->xadvance; } +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + orig[0] = x; + orig[1] = y; + + // make sure y never passes through a vertex of the shape + y_frac = (float) fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + c*x^2 + b*x + a = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + // if one scale is 0, use same scale for both + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; // if both scales are 0, return NULL + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3],px,py,t,it; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} ////////////////////////////////////////////////////////////////////////////// // @@ -3970,6 +4471,10 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const // FULL VERSION HISTORY // +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts // 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 diff --git a/src/external/stb_vorbis.c b/src/external/stb_vorbis.c index 21638bcd..5f2e0814 100644 --- a/src/external/stb_vorbis.c +++ b/src/external/stb_vorbis.c @@ -193,6 +193,7 @@ #undef __forceinline #endif #define __forceinline + #define alloca __builtin_alloca #elif !defined(_MSC_VER) #if __GNUC__ #define __forceinline inline diff --git a/src/external/stb_vorbis.h b/src/external/stb_vorbis.h index 9394e813..1cdca652 100644 --- a/src/external/stb_vorbis.h +++ b/src/external/stb_vorbis.h @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.10 - public domain +// Ogg Vorbis audio decoder - v1.11 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -29,9 +29,10 @@ // Bernhard Wodo Evan Balster alxprd@github // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// manxorist@github saga musix +// manxorist@github saga musix github:infatum // // Partial history: +// 1.11 - 2017/07/23 - fix MinGW compilation // 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory // 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version // 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame diff --git a/src/gestures.h b/src/gestures.h index f04bf091..f4d38dfb 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -148,8 +148,9 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount); int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency); #elif defined(__linux__) - #include <sys/time.h> // Required for: timespec - #include <time.h> // Required for: clock_gettime() + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. + #include <sys/time.h> // Required for: timespec + #include <time.h> // Required for: clock_gettime() #endif //---------------------------------------------------------------------------------- diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..bdffb98e --- /dev/null +++ b/src/meson.build @@ -0,0 +1,22 @@ +install_headers('raylib.h') + +source_c = [ + 'audio.c', + 'core.c', + 'models.c', + 'rlgl.c', + 'shapes.c', + 'text.c', + 'textures.c', + 'utils.c', + 'external/stb_vorbis.c', +] + +# use 'meson --default-library=static builddir' to build as static, if no builddir yet exists +# use 'mesonconf -Ddefault_library=static builddir' to change the type +raylib = library('raylib', + source_c, + dependencies : [ glfw_dep, gl_dep, openal_dep, m_dep, x11_dep], + install : true, + version : '1.7.0') + diff --git a/src/models.c b/src/models.c index 0a692f5c..315b51d4 100644 --- a/src/models.c +++ b/src/models.c @@ -76,9 +76,6 @@ static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data static Material LoadMTL(const char *fileName); // Load MTL material data #endif -static Mesh GenMeshHeightmap(Image image, Vector3 size); -static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); - //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- @@ -121,68 +118,67 @@ void DrawCube(Vector3 position, float width, float height, float length, Color c float z = 0.0f; rlPushMatrix(); - - // NOTE: Be careful! Function order matters (rotate -> scale -> translate) + // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) rlTranslatef(position.x, position.y, position.z); - //rlScalef(2.0f, 2.0f, 2.0f); //rlRotatef(45, 0, 1, 0); + //rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); - // Front Face ----------------------------------------------------- - rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left - rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right - rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left + // Front face + rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left - rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right - rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left - rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right + rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right - // Back Face ------------------------------------------------------ - rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left - rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left - rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right + // Back face + rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right - rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right - rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right - rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left + rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left - // Top Face ------------------------------------------------------- - rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left - rlVertex3f(x-width/2, y+height/2, z+length/2); // Bottom Left - rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right + // Top face + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left + rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left + rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right - rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right - rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left - rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right + rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left + rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right - // Bottom Face ---------------------------------------------------- - rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left - rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right - rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left - - rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Right - rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right - rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left - - // Right face ----------------------------------------------------- - rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right - rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right - rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left + // Bottom face + rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right + rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left - rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left - rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right - rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left - - // Left Face ------------------------------------------------------ - rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Right - rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left - rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Right + rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right + rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left - rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left - rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left - rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Right + // Right face + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right + rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left + + rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left + rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left + + // Left face + rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left + rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right + + rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left + rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left + rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right rlEnd(); rlPopMatrix(); } @@ -201,9 +197,7 @@ void DrawCubeWires(Vector3 position, float width, float height, float length, Co float z = 0.0f; rlPushMatrix(); - rlTranslatef(position.x, position.y, position.z); - //rlRotatef(45, 0, 1, 0); rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -274,49 +268,49 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei rlEnableTexture(texture.id); //rlPushMatrix(); - // NOTE: Be careful! Function order matters (scale, translate, rotate) - //rlScalef(2.0f, 2.0f, 2.0f); + // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) //rlTranslatef(2.0f, 0.0f, 0.0f); //rlRotatef(45, 0, 1, 0); + //rlScalef(2.0f, 2.0f, 2.0f); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); // Front Face rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer - rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad // Back Face - rlNormal3f(0.0f, 0.0f,-1.0f); // Normal Pointing Away From Viewer - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Left Of The Texture and Quad + rlNormal3f(0.0f, 0.0f, - 1.0f); // Normal Pointing Away From Viewer + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad // Top Face rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up - rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad // Bottom Face - rlNormal3f(0.0f,-1.0f, 0.0f); // Normal Pointing Down - rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Right Of The Texture and Quad + rlNormal3f(0.0f, - 1.0f, 0.0f); // Normal Pointing Down + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad // Right face rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Left Of The Texture and Quad - rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad // Left Face - rlNormal3f(-1.0f, 0.0f, 0.0f); // Normal Pointing Left - rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left Of The Texture and Quad - rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Right Of The Texture and Quad - rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Right Of The Texture and Quad - rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Of The Texture and Quad + rlNormal3f( - 1.0f, 0.0f, 0.0f); // Normal Pointing Left + rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad rlEnd(); //rlPopMatrix(); @@ -333,6 +327,7 @@ void DrawSphere(Vector3 centerPos, float radius, Color color) void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) { rlPushMatrix(); + // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(radius, radius, radius); @@ -372,6 +367,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color) { rlPushMatrix(); + // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); rlScalef(radius, radius, radius); @@ -426,12 +422,12 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom); //Bottom Right - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop); //Top Right + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom); //Bottom Right + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); //Top Right rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop); //Top Left rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop); //Top Right + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); //Top Right } // Draw Cap -------------------------------------------------------------------------------------- @@ -439,7 +435,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h { rlVertex3f(0, height, 0); rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop); - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop); + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); } } else @@ -449,7 +445,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h { rlVertex3f(0, height, 0); rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom); + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom); } } @@ -457,7 +453,7 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(0, 0, 0); - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom); + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom); rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); } rlEnd(); @@ -479,12 +475,12 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl for (int i = 0; i < 360; i += 360/sides) { rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom); + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom); - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom); - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop); + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom); + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); - rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop); + rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop); rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop); @@ -571,7 +567,6 @@ void DrawGizmo(Vector3 position) rlPushMatrix(); rlTranslatef(position.x, position.y, position.z); - //rlRotatef(rotation, 0, 1, 0); rlScalef(length, length, length); rlBegin(RL_LINES); @@ -587,162 +582,193 @@ void DrawGizmo(Vector3 position) rlPopMatrix(); } -// Load mesh from file -Mesh LoadMesh(const char *fileName) -{ - Mesh mesh = { 0 }; - -#if defined(SUPPORT_FILEFORMAT_OBJ) - if (IsFileExtension(fileName, ".obj")) mesh = LoadOBJ(fileName); -#else - TraceLog(WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName); -#endif - - if (mesh.vertexCount == 0) TraceLog(WARNING, "Mesh could not be loaded"); - else rlglLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) - - // TODO: Initialize default mesh data in case loading fails, maybe a cube? - - return mesh; -} - -// Load mesh from vertex data -// NOTE: All vertex data arrays must be same size: vertexCount -Mesh LoadMeshEx(int vertexCount, float *vData, float *vtData, float *vnData, Color *cData) -{ - Mesh mesh = { 0 }; - - mesh.vertexCount = vertexCount; - mesh.triangleCount = vertexCount/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 model from file +// Load model from files (mesh and material) Model LoadModel(const char *fileName) { Model model = { 0 }; model.mesh = LoadMesh(fileName); model.transform = MatrixIdentity(); - model.material = LoadDefaultMaterial(); - - return model; -} - -// Load model from mesh data -Model LoadModelFromMesh(Mesh data, bool dynamic) -{ - Model model = { 0 }; - - model.mesh = data; - - rlglLoadMesh(&model.mesh, dynamic); // Upload vertex data to GPU - - model.transform = MatrixIdentity(); - model.material = LoadDefaultMaterial(); - - return model; -} - -// Load heightmap model from image data -// NOTE: model map size is defined in generic units -Model LoadHeightmap(Image heightmap, Vector3 size) -{ - Model model = { 0 }; - - model.mesh = GenMeshHeightmap(heightmap, size); - - rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model) - - model.transform = MatrixIdentity(); - model.material = LoadDefaultMaterial(); + model.material = LoadMaterialDefault(); return model; } -// Load cubes-based map model from image data -Model LoadCubicmap(Image cubicmap) +// Load model from generated mesh +// WARNING: A shallow copy of mesh is generated, passed by value, +// as long as struct contains pointers to data and some values, we get a copy +// of mesh pointing to same data as original version... be careful! +Model LoadModelFromMesh(Mesh mesh) { Model model = { 0 }; - - model.mesh = GenMeshCubicmap(cubicmap, (Vector3){ 1.0f, 1.5f, 1.0f }); - - rlglLoadMesh(&model.mesh, false); // Upload vertex data to GPU (static model) - + + model.mesh = mesh; model.transform = MatrixIdentity(); - model.material = LoadDefaultMaterial(); + model.material = LoadMaterialDefault(); return model; } -// Unload mesh from memory (RAM and/or VRAM) -void UnloadMesh(Mesh *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 (mesh and material) from RAM and VRAM"); + TraceLog(LOG_INFO, "Unloaded model data (mesh and material) from RAM and VRAM"); } -// Load material data (from file) -Material LoadMaterial(const char *fileName) +// Load mesh from file +// NOTE: Mesh data loaded in CPU and GPU +Mesh LoadMesh(const char *fileName) { - Material material = { 0 }; + Mesh mesh = { 0 }; -#if defined(SUPPORT_FILEFORMAT_MTL) - if (IsFileExtension(fileName, ".mtl")) material = LoadMTL(fileName); +#if defined(SUPPORT_FILEFORMAT_OBJ) + if (IsFileExtension(fileName, ".obj")) mesh = LoadOBJ(fileName); #else - TraceLog(WARNING, "[%s] Material fileformat not supported, it can't be loaded", fileName); + TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName); #endif - return material; + if (mesh.vertexCount == 0) TraceLog(LOG_WARNING, "Mesh could not be loaded"); + else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) + + // TODO: Initialize default mesh data in case loading fails, maybe a cube? + + return mesh; } -// Load default material (uses default models shader) -Material LoadDefaultMaterial(void) +// Unload mesh from memory (RAM and/or VRAM) +void UnloadMesh(Mesh *mesh) { - Material material = { 0 }; - - material.shader = GetDefaultShader(); - material.texDiffuse = GetDefaultTexture(); // White texture (1x1 pixel) - //material.texNormal; // NOTE: By default, not set - //material.texSpecular; // NOTE: By default, not set + rlUnloadMesh(mesh); +} - material.colDiffuse = WHITE; // Diffuse color - material.colAmbient = WHITE; // Ambient color - material.colSpecular = WHITE; // Specular color +// Generated cuboid mesh +// NOTE: Vertex data is uploaded to GPU +Mesh GenMeshCube(float width, float height, float length) +{ + Mesh mesh = { 0 }; - material.glossiness = 100.0f; // Glossiness level + float vertices[] = { + -width/2, -height/2, length/2, + width/2, -height/2, length/2, + width/2, height/2, length/2, + -width/2, height/2, length/2, + -width/2, -height/2, -length/2, + -width/2, height/2, -length/2, + width/2, height/2, -length/2, + width/2, -height/2, -length/2, + -width/2, height/2, -length/2, + -width/2, height/2, length/2, + width/2, height/2, length/2, + width/2, height/2, -length/2, + -width/2, -height/2, -length/2, + width/2, -height/2, -length/2, + width/2, -height/2, length/2, + -width/2, -height/2, length/2, + width/2, -height/2, -length/2, + width/2, height/2, -length/2, + width/2, height/2, length/2, + width/2, -height/2, length/2, + -width/2, -height/2, -length/2, + -width/2, -height/2, length/2, + -width/2, height/2, length/2, + -width/2, height/2, -length/2 + }; + + float texcoords[] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f + }; + + float normals[] = { + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f,-1.0f, + 0.0f, 0.0f,-1.0f, + 0.0f, 0.0f,-1.0f, + 0.0f, 0.0f,-1.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f,-1.0f, 0.0f, + 0.0f,-1.0f, 0.0f, + 0.0f,-1.0f, 0.0f, + 0.0f,-1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f + }; + + mesh.vertices = (float *)malloc(24*3*sizeof(float)); + memcpy(mesh.vertices, vertices, 24*3*sizeof(float)); + + mesh.texcoords = (float *)malloc(24*2*sizeof(float)); + memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float)); + + mesh.normals = (float *)malloc(24*3*sizeof(float)); + memcpy(mesh.normals, normals, 24*3*sizeof(float)); + + mesh.indices = (unsigned short *)malloc(36*sizeof(unsigned short)); + + int k = 0; - return material; -} + // Indices can be initialized right now + for (int i = 0; i < 36; i+=6) + { + mesh.indices[i] = 4*k; + mesh.indices[i+1] = 4*k+1; + mesh.indices[i+2] = 4*k+2; + mesh.indices[i+3] = 4*k; + mesh.indices[i+4] = 4*k+2; + mesh.indices[i+5] = 4*k+3; + + k++; + } + + mesh.vertexCount = 24; + mesh.triangleCount = 12; + + // Upload vertex data to GPU (static mesh) + rlLoadMesh(&mesh, false); -// Unload material from memory -void UnloadMaterial(Material material) -{ - rlDeleteTextures(material.texDiffuse.id); - rlDeleteTextures(material.texNormal.id); - rlDeleteTextures(material.texSpecular.id); + return mesh; } // Generate a mesh from heightmap -static Mesh GenMeshHeightmap(Image heightmap, Vector3 size) +// NOTE: Vertex data is uploaded to GPU +Mesh GenMeshHeightmap(Image heightmap, Vector3 size) { #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3) @@ -843,11 +869,16 @@ static Mesh GenMeshHeightmap(Image heightmap, Vector3 size) } free(pixels); + + // Upload vertex data to GPU (static mesh) + rlLoadMesh(&mesh, false); return mesh; } -static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) +// Generate a cubes mesh from pixel data +// NOTE: Vertex data is uploaded to GPU +Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) { Mesh mesh = { 0 }; @@ -1197,10 +1228,60 @@ static Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) free(mapTexcoords); free(cubicmapPixels); // Free image pixel data + + // Upload vertex data to GPU (static mesh) + rlLoadMesh(&mesh, false); return mesh; } +// Load material data (from file) +Material LoadMaterial(const char *fileName) +{ + Material material = { 0 }; + +#if defined(SUPPORT_FILEFORMAT_MTL) + if (IsFileExtension(fileName, ".mtl")) material = LoadMTL(fileName); +#else + TraceLog(LOG_WARNING, "[%s] Material fileformat not supported, it can't be loaded", fileName); +#endif + + // Our material uses the default shader (DIFFUSE, SPECULAR, NORMAL) + material.shader = GetShaderDefault(); + + return material; +} + +// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) +Material LoadMaterialDefault(void) +{ + Material material = { 0 }; + + material.shader = GetShaderDefault(); + material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel) + //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set + //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set + + material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color + material.maps[MAP_SPECULAR].color = WHITE; // Specular color + + return material; +} + +// Unload material from memory +void UnloadMaterial(Material material) +{ + // Unload material shader + UnloadShader(material.shader); + + // Unload loaded texture maps + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) + { + // NOTE: We already check for (tex.id > 0) inside function + rlDeleteTextures(material.maps[i].texture.id); + } +} + // Draw a model (with texture if set) void DrawModel(Model model, Vector3 position, float scale, Color tint) { @@ -1215,8 +1296,8 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota { // Calculate transformation matrix from function parameters // Get transform matrix (rotation -> scale -> translation) - Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); + Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); @@ -1225,9 +1306,9 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates model.transform = MatrixMultiply(model.transform, matTransform); - model.material.colDiffuse = tint; // TODO: Multiply tint color by diffuse color? + model.material.maps[MAP_DIFFUSE].color = tint; // TODO: Multiply tint color by diffuse color? - rlglDrawMesh(model.mesh, model.material, model.transform); + rlDrawMesh(model.mesh, model.material, model.transform); } // Draw a model wires (with texture if set) @@ -1264,11 +1345,10 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec // NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width Vector2 sizeRatio = { size, size*(float)sourceRec.height/sourceRec.width }; - Matrix viewMatrix = MatrixLookAt(camera.position, camera.target, camera.up); - MatrixTranspose(&viewMatrix); + Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); - Vector3 right = { viewMatrix.m0, viewMatrix.m4, viewMatrix.m8 }; - //Vector3 up = { viewMatrix.m1, viewMatrix.m5, viewMatrix.m9 }; + Vector3 right = { matView.m0, matView.m4, matView.m8 }; + //Vector3 up = { matView.m1, matView.m5, matView.m9 }; // NOTE: Billboard locked on axis-Y Vector3 up = { 0.0f, 1.0f, 0.0f }; @@ -1279,16 +1359,16 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec | | d-------c */ - VectorScale(&right, sizeRatio.x/2); - VectorScale(&up, sizeRatio.y/2); + Vector3Scale(&right, sizeRatio.x/2); + Vector3Scale(&up, sizeRatio.y/2); - Vector3 p1 = VectorAdd(right, up); - Vector3 p2 = VectorSubtract(right, up); + Vector3 p1 = Vector3Add(right, up); + Vector3 p2 = Vector3Subtract(right, up); - Vector3 a = VectorSubtract(center, p2); - Vector3 b = VectorAdd(center, p1); - Vector3 c = VectorAdd(center, p2); - Vector3 d = VectorSubtract(center, p1); + Vector3 a = Vector3Subtract(center, p2); + Vector3 b = Vector3Add(center, p1); + Vector3 c = Vector3Add(center, p2); + Vector3 d = Vector3Subtract(center, p1); rlEnableTexture(texture.id); @@ -1387,9 +1467,9 @@ bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius { bool collision = false; - Vector3 raySpherePos = VectorSubtract(spherePosition, ray.position); - float distance = VectorLength(raySpherePos); - float vector = VectorDotProduct(raySpherePos, ray.direction); + Vector3 raySpherePos = Vector3Subtract(spherePosition, ray.position); + float distance = Vector3Length(raySpherePos); + float vector = Vector3DotProduct(raySpherePos, ray.direction); float d = sphereRadius*sphereRadius - (distance*distance - vector*vector); if (d >= 0.0f) collision = true; @@ -1402,9 +1482,9 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi { bool collision = false; - Vector3 raySpherePos = VectorSubtract(spherePosition, ray.position); - float distance = VectorLength(raySpherePos); - float vector = VectorDotProduct(raySpherePos, ray.direction); + Vector3 raySpherePos = Vector3Subtract(spherePosition, ray.position); + float distance = Vector3Length(raySpherePos); + float vector = Vector3DotProduct(raySpherePos, ray.direction); float d = sphereRadius*sphereRadius - (distance*distance - vector*vector); if (d >= 0.0f) collision = true; @@ -1417,8 +1497,8 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi if (distance < sphereRadius) collisionDistance = vector + sqrtf(d); else collisionDistance = vector - sqrtf(d); - VectorScale(&offset, collisionDistance); - Vector3 cPoint = VectorAdd(ray.position, offset); + Vector3Scale(&offset, collisionDistance); + Vector3 cPoint = Vector3Add(ray.position, offset); collisionPoint->x = cPoint.x; collisionPoint->y = cPoint.y; @@ -1501,14 +1581,14 @@ RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) RayHitInfo result = {0}; // Find vectors for two edges sharing V1 - edge1 = VectorSubtract(p2, p1); - edge2 = VectorSubtract(p3, p1); + edge1 = Vector3Subtract(p2, p1); + edge2 = Vector3Subtract(p3, p1); // Begin calculating determinant - also used to calculate u parameter - p = VectorCrossProduct(ray.direction, edge2); + p = Vector3CrossProduct(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); + det = Vector3DotProduct(edge1, p); // Avoid culling! if ((det > -EPSILON) && (det < EPSILON)) return result; @@ -1516,24 +1596,24 @@ RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) invDet = 1.0f/det; // Calculate distance from V1 to ray origin - tv = VectorSubtract(ray.position, p1); + tv = Vector3Subtract(ray.position, p1); // Calculate u parameter and test bound - u = VectorDotProduct(tv, p)*invDet; + u = Vector3DotProduct(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); + q = Vector3CrossProduct(tv, edge1); // Calculate V parameter and test bound - v = VectorDotProduct(ray.direction, q)*invDet; + v = Vector3DotProduct(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; + t = Vector3DotProduct(edge2, q)*invDet; if (t > EPSILON) { @@ -1541,11 +1621,11 @@ RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) result.hit = true; result.distance = t; result.hit = true; - result.normal = VectorCrossProduct(edge1, edge2); - VectorNormalize(&result.normal); + result.normal = Vector3CrossProduct(edge1, edge2); + Vector3Normalize(&result.normal); Vector3 rayDir = ray.direction; - VectorScale(&rayDir, t); - result.position = VectorAdd(ray.position, rayDir); + Vector3Scale(&rayDir, t); + result.position = Vector3Add(ray.position, rayDir); } return result; @@ -1565,11 +1645,11 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) if (t >= 0.0) { Vector3 rayDir = ray.direction; - VectorScale(&rayDir, t); + Vector3Scale(&rayDir, t); result.hit = true; result.distance = t; result.normal = (Vector3){ 0.0, 1.0, 0.0 }; - result.position = VectorAdd(ray.position, rayDir); + result.position = Vector3Add(ray.position, rayDir); } } @@ -1577,7 +1657,7 @@ RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight) } // Calculate mesh bounding box limits -// NOTE: minVertex and maxVertex should be transformed by model transform matrix (position, scale, rotate) +// NOTE: minVertex and maxVertex should be transformed by model transform matrix BoundingBox CalculateBoundingBox(Mesh mesh) { // Get min and max vertex to construct bounds (AABB) @@ -1591,8 +1671,8 @@ BoundingBox CalculateBoundingBox(Mesh mesh) for (int i = 1; i < mesh.vertexCount; i++) { - minVertex = VectorMin(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); - maxVertex = VectorMax(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); + maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); } } @@ -1628,7 +1708,7 @@ static Mesh LoadOBJ(const char *fileName) if (objFile == NULL) { - TraceLog(WARNING, "[%s] OBJ file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] OBJ file could not be opened", fileName); return mesh; } @@ -1637,6 +1717,7 @@ static Mesh LoadOBJ(const char *fileName) // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face) while (!feof(objFile)) { + dataType = '\0'; fscanf(objFile, "%c", &dataType); switch (dataType) @@ -1679,10 +1760,10 @@ static Mesh LoadOBJ(const char *fileName) } } - TraceLog(DEBUG, "[%s] Model vertices: %i", fileName, vertexCount); - TraceLog(DEBUG, "[%s] Model texcoords: %i", fileName, texcoordCount); - TraceLog(DEBUG, "[%s] Model normals: %i", fileName, normalCount); - TraceLog(DEBUG, "[%s] Model triangles: %i", fileName, triangleCount); + TraceLog(LOG_DEBUG, "[%s] Model vertices: %i", fileName, vertexCount); + TraceLog(LOG_DEBUG, "[%s] Model texcoords: %i", fileName, texcoordCount); + TraceLog(LOG_DEBUG, "[%s] Model normals: %i", fileName, normalCount); + TraceLog(LOG_DEBUG, "[%s] Model triangles: %i", fileName, triangleCount); // Once we know the number of vertices to store, we create required arrays Vector3 *midVertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3)); @@ -1756,7 +1837,7 @@ static Mesh LoadOBJ(const char *fileName) rewind(objFile); // Return to the beginning of the file, to read again - if (normalCount == 0) TraceLog(INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName); + if (normalCount == 0) TraceLog(LOG_INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName); // Third reading pass: Get faces (triangles) data and fill VertexArray while (!feof(objFile)) @@ -1806,8 +1887,8 @@ static Mesh LoadOBJ(const char *fileName) else { // If normals not defined, they are calculated from the 3 vertices [N = (V2 - V1) x (V3 - V1)] - Vector3 norm = VectorCrossProduct(VectorSubtract(midVertices[vCount[1]-1], midVertices[vCount[0]-1]), VectorSubtract(midVertices[vCount[2]-1], midVertices[vCount[0]-1])); - VectorNormalize(&norm); + Vector3 norm = Vector3CrossProduct(Vector3Subtract(midVertices[vCount[1]-1], midVertices[vCount[0]-1]), Vector3Subtract(midVertices[vCount[2]-1], midVertices[vCount[0]-1])); + Vector3Normalize(&norm); mesh.normals[nCounter] = norm.x; mesh.normals[nCounter + 1] = norm.y; @@ -1867,8 +1948,8 @@ static Mesh LoadOBJ(const char *fileName) Vector2 uv2 = { mesh.texcoords[uvCount + 4], mesh.texcoords[uvCount + 5] }; // Calculate edges of the triangle (position delta) - Vector3 deltaPos1 = VectorSubtract(v1, v0); - Vector3 deltaPos2 = VectorSubtract(v2, v0); + Vector3 deltaPos1 = Vector3Subtract(v1, v0); + Vector3 deltaPos2 = Vector3Subtract(v2, v0); // UV delta Vector2 deltaUV1 = { uv1.x - uv0.x, uv1.y - uv0.y }; @@ -1881,8 +1962,8 @@ static Mesh LoadOBJ(const char *fileName) // Vector3 b2 = { deltaPos1.x*deltaUV2.x, deltaPos1.y*deltaUV2.x, deltaPos1.z*deltaUV2.x }; // Calculate vertex tangent - Vector3 tangent = VectorSubtract(t1, t2); - VectorScale(&tangent, r); + Vector3 tangent = Vector3Subtract(t1, t2); + Vector3Scale(&tangent, r); // Apply calculated tangents data to mesh struct mesh.tangents[vCount + 0] = tangent.x; @@ -1897,8 +1978,8 @@ static Mesh LoadOBJ(const char *fileName) // TODO: add binormals to mesh struct and assign buffers id and locations properly /* // Calculate vertex binormal - Vector3 binormal = VectorSubtract(b1, b2); - VectorScale(&binormal, r); + Vector3 binormal = Vector3Subtract(b1, b2); + Vector3Scale(&binormal, r); // Apply calculated binormals data to mesh struct mesh.binormals[vCount + 0] = binormal.x; @@ -1923,7 +2004,7 @@ static Mesh LoadOBJ(const char *fileName) free(midTexCoords); // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct - TraceLog(INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); + TraceLog(LOG_INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); return mesh; } @@ -1936,7 +2017,7 @@ static Material LoadMTL(const char *fileName) { #define MAX_BUFFER_SIZE 128 - Material material = { 0 }; // LoadDefaultMaterial(); + Material material = { 0 }; char buffer[MAX_BUFFER_SIZE]; Vector3 color = { 1.0f, 1.0f, 1.0f }; @@ -1949,7 +2030,7 @@ static Material LoadMTL(const char *fileName) if (mtlFile == NULL) { - TraceLog(WARNING, "[%s] MTL file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] MTL file could not be opened", fileName); return material; } @@ -1964,7 +2045,7 @@ static Material LoadMTL(const char *fileName) // TODO: Support multiple materials in a single .mtl sscanf(buffer, "newmtl %s", mapFileName); - TraceLog(INFO, "[%s] Loading material...", mapFileName); + TraceLog(LOG_INFO, "[%s] Loading material...", mapFileName); } case 'i': // illum int Illumination model { @@ -1979,23 +2060,24 @@ static Material LoadMTL(const char *fileName) case 'a': // Ka float float float Ambient color (RGB) { sscanf(buffer, "Ka %f %f %f", &color.x, &color.y, &color.z); - material.colAmbient.r = (unsigned char)(color.x*255); - material.colAmbient.g = (unsigned char)(color.y*255); - material.colAmbient.b = (unsigned char)(color.z*255); + // TODO: Support ambient color + //material.colAmbient.r = (unsigned char)(color.x*255); + //material.colAmbient.g = (unsigned char)(color.y*255); + //material.colAmbient.b = (unsigned char)(color.z*255); } break; case 'd': // Kd float float float Diffuse color (RGB) { sscanf(buffer, "Kd %f %f %f", &color.x, &color.y, &color.z); - material.colDiffuse.r = (unsigned char)(color.x*255); - material.colDiffuse.g = (unsigned char)(color.y*255); - material.colDiffuse.b = (unsigned char)(color.z*255); + material.maps[MAP_DIFFUSE].color.r = (unsigned char)(color.x*255); + material.maps[MAP_DIFFUSE].color.g = (unsigned char)(color.y*255); + material.maps[MAP_DIFFUSE].color.b = (unsigned char)(color.z*255); } break; case 's': // Ks float float float Specular color (RGB) { sscanf(buffer, "Ks %f %f %f", &color.x, &color.y, &color.z); - material.colSpecular.r = (unsigned char)(color.x*255); - material.colSpecular.g = (unsigned char)(color.y*255); - material.colSpecular.b = (unsigned char)(color.z*255); + material.maps[MAP_SPECULAR].color.r = (unsigned char)(color.x*255); + material.maps[MAP_SPECULAR].color.g = (unsigned char)(color.y*255); + material.maps[MAP_SPECULAR].color.b = (unsigned char)(color.z*255); } break; case 'e': // Ke float float float Emmisive color (RGB) { @@ -2011,7 +2093,7 @@ static Material LoadMTL(const char *fileName) int shininess = 0; sscanf(buffer, "Ns %i", &shininess); - material.glossiness = (float)shininess; + //material.params[PARAM_GLOSSINES] = (float)shininess; } else if (buffer[1] == 'i') // Ni int Refraction index. { @@ -2027,12 +2109,12 @@ static Material LoadMTL(const char *fileName) if (buffer[5] == 'd') // map_Kd string Diffuse color texture map. { result = sscanf(buffer, "map_Kd %s", mapFileName); - if (result != EOF) material.texDiffuse = LoadTexture(mapFileName); + if (result != EOF) material.maps[MAP_DIFFUSE].texture = LoadTexture(mapFileName); } else if (buffer[5] == 's') // map_Ks string Specular color texture map. { result = sscanf(buffer, "map_Ks %s", mapFileName); - if (result != EOF) material.texSpecular = LoadTexture(mapFileName); + if (result != EOF) material.maps[MAP_SPECULAR].texture = LoadTexture(mapFileName); } else if (buffer[5] == 'a') // map_Ka string Ambient color texture map. { @@ -2042,12 +2124,12 @@ static Material LoadMTL(const char *fileName) case 'B': // map_Bump string Bump texture map. { result = sscanf(buffer, "map_Bump %s", mapFileName); - if (result != EOF) material.texNormal = LoadTexture(mapFileName); + if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); } break; case 'b': // map_bump string Bump texture map. { result = sscanf(buffer, "map_bump %s", mapFileName); - if (result != EOF) material.texNormal = LoadTexture(mapFileName); + if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); } break; case 'd': // map_d string Opacity texture map. { @@ -2062,7 +2144,7 @@ static Material LoadMTL(const char *fileName) { float alpha = 1.0f; sscanf(buffer, "d %f", &alpha); - material.colDiffuse.a = (unsigned char)(alpha*255); + material.maps[MAP_DIFFUSE].color.a = (unsigned char)(alpha*255); } else if (buffer[1] == 'i') // disp string Displacement map { @@ -2072,13 +2154,13 @@ static Material LoadMTL(const char *fileName) case 'b': // bump string Bump texture map { result = sscanf(buffer, "bump %s", mapFileName); - if (result != EOF) material.texNormal = LoadTexture(mapFileName); + if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName); } break; case 'T': // Tr float Transparency Tr (alpha). Tr is inverse of d { float ialpha = 0.0f; sscanf(buffer, "Tr %f", &ialpha); - material.colDiffuse.a = (unsigned char)((1.0f - ialpha)*255); + material.maps[MAP_DIFFUSE].color.a = (unsigned char)((1.0f - ialpha)*255); } break; case 'r': // refl string Reflection texture map @@ -2089,7 +2171,7 @@ static Material LoadMTL(const char *fileName) fclose(mtlFile); // NOTE: At this point we have all material data - TraceLog(INFO, "[%s] Material loaded successfully", fileName); + TraceLog(LOG_INFO, "[%s] Material loaded successfully", fileName); return material; } diff --git a/src/physac.h b/src/physac.h index d3fdaca4..5ecd2815 100644 --- a/src/physac.h +++ b/src/physac.h @@ -252,7 +252,8 @@ 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__) || defined(PLATFORM_WEB) +#elif defined(__linux__) || defined(__APPLE__) || defined(PLATFORM_WEB) + #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext. //#define _DEFAULT_SOURCE // Enables BSD function definitions and C99 POSIX compliance #include <sys/time.h> // Required for: timespec #include <time.h> // Required for: clock_gettime() @@ -281,7 +282,7 @@ static pthread_t physicsThreadId; // Physics thread id 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) || defined(__linux__) || defined(PLATFORM_WEB) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(__linux__) || defined(__APPLE__) || defined(PLATFORM_WEB) static double baseTime = 0; // Android and RPI platforms base time #endif static double startTime = 0; // Start time in milliseconds @@ -1906,7 +1907,7 @@ static double GetCurrentTime(void) time = (double)((double)currentTime/clockFrequency)*1000; #endif - #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(__linux__) || defined(PLATFORM_WEB) + #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(__linux__) || defined(__APPLE__) || 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 0b1a6b19..85499eb1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ -/********************************************************************************************** +/********************************************************************************************** * -* raylib v1.7.0 +* raylib v1.8.0 * * A simple and easy-to-use library to learn videogames programming (www.raylib.com) * @@ -291,14 +291,17 @@ #define MAGENTA CLITERAL{ 255, 0, 255, 255 } // Magenta #define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo) +// Shader and material limits +#define MAX_SHADER_LOCATIONS 32 // Maximum number of predefined locations stored in shader struct +#define MAX_MATERIAL_MAPS 12 // Maximum number of texture maps stored in shader struct + //---------------------------------------------------------------------------------- // Structures Definition //---------------------------------------------------------------------------------- #ifndef __cplusplus // Boolean type - #if !defined(_STDBOOL_H) + #if !defined(_STDBOOL_H) || !defined(__STDBOOL_H) // CLang uses second form typedef enum { false, true } bool; - #define _STDBOOL_H #endif #endif @@ -401,63 +404,46 @@ typedef struct Camera2D { // Bounding box type typedef struct BoundingBox { - Vector3 min; // minimum vertex box-corner - Vector3 max; // maximum vertex box-corner + Vector3 min; // Minimum vertex box-corner + Vector3 max; // Maximum vertex box-corner } BoundingBox; // Vertex data definning a mesh +// NOTE: Data stored in CPU memory (and GPU) typedef struct Mesh { - int vertexCount; // number of vertices stored in arrays - int triangleCount; // number of triangles stored (indexed or not) - float *vertices; // vertex position (XYZ - 3 components per vertex) (shader-location = 0) - float *texcoords; // vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - float *texcoords2; // vertex second texture coordinates (useful for lightmaps) (shader-location = 5) - float *normals; // vertex normals (XYZ - 3 components per vertex) (shader-location = 2) - float *tangents; // vertex tangents (XYZ - 3 components per vertex) (shader-location = 4) - unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - unsigned short *indices;// vertex indices (in case vertex data comes indexed) + int vertexCount; // Number of vertices stored in arrays + int triangleCount; // Number of triangles stored (indexed or not) + + float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + float *texcoords2; // Vertex second texture coordinates (useful for lightmaps) (shader-location = 5) + float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) + float *tangents; // Vertex tangents (XYZ - 3 components per vertex) (shader-location = 4) + unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + unsigned short *indices;// Vertex indices (in case vertex data comes indexed) unsigned int vaoId; // OpenGL Vertex Array Object id unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data) } Mesh; -// Shader type (generic shader) +// Shader type (generic) typedef struct Shader { - unsigned int id; // Shader program id - - // Vertex attributes locations (default locations) - int vertexLoc; // Vertex attribute location point (default-location = 0) - int texcoordLoc; // Texcoord attribute location point (default-location = 1) - int texcoord2Loc; // Texcoord2 attribute location point (default-location = 5) - int normalLoc; // Normal attribute location point (default-location = 2) - int tangentLoc; // Tangent attribute location point (default-location = 4) - int colorLoc; // Color attibute location point (default-location = 3) - - // Uniform locations - int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader) - int colDiffuseLoc; // Diffuse color uniform location point (fragment shader) - int colAmbientLoc; // Ambient color uniform location point (fragment shader) - int colSpecularLoc; // Specular color uniform location point (fragment shader) - - // Texture map locations (generic for any kind of map) - int mapTexture0Loc; // Map texture uniform location point (default-texture-unit = 0) - int mapTexture1Loc; // Map texture uniform location point (default-texture-unit = 1) - int mapTexture2Loc; // Map texture uniform location point (default-texture-unit = 2) + unsigned int id; // Shader program id + int locs[MAX_SHADER_LOCATIONS]; // Shader locations array } Shader; -// Material type -typedef struct Material { - Shader shader; // Standard shader (supports 3 map textures) - - Texture2D texDiffuse; // Diffuse texture (binded to shader mapTexture0Loc) - Texture2D texNormal; // Normal texture (binded to shader mapTexture1Loc) - Texture2D texSpecular; // Specular texture (binded to shader mapTexture2Loc) - - Color colDiffuse; // Diffuse color - Color colAmbient; // Ambient color - Color colSpecular; // Specular color +// Material texture map +typedef struct MaterialMap { + Texture2D texture; // Material map texture + Color color; // Material map color + float value; // Material map value +} MaterialMap; - float glossiness; // Glossiness level (Ranges from 0 to 1000) +// Material type (generic) +typedef struct Material { + Shader shader; // Material shader + MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps + float *params; // Material generic parameters (if required) } Material; // Model type @@ -534,13 +520,63 @@ typedef struct RRESData *RRES; //---------------------------------------------------------------------------------- // Trace log type typedef enum { - INFO = 0, - WARNING, - ERROR, - DEBUG, - OTHER + LOG_INFO = 0, + LOG_WARNING, + LOG_ERROR, + LOG_DEBUG, + LOG_OTHER } LogType; +// Shader location point type +typedef enum { + LOC_VERTEX_POSITION = 0, + LOC_VERTEX_TEXCOORD01, + LOC_VERTEX_TEXCOORD02, + LOC_VERTEX_NORMAL, + LOC_VERTEX_TANGENT, + LOC_VERTEX_COLOR, + LOC_MATRIX_MVP, + LOC_MATRIX_MODEL, + LOC_MATRIX_VIEW, + LOC_MATRIX_PROJECTION, + LOC_VECTOR_VIEW, + LOC_COLOR_DIFFUSE, + LOC_COLOR_SPECULAR, + LOC_COLOR_AMBIENT, + LOC_MAP_ALBEDO, // LOC_MAP_DIFFUSE + LOC_MAP_METALNESS, // LOC_MAP_SPECULAR + LOC_MAP_NORMAL, + LOC_MAP_ROUGHNESS, + LOC_MAP_OCCUSION, + LOC_MAP_EMISSION, + LOC_MAP_HEIGHT, + LOC_MAP_CUBEMAP, + LOC_MAP_IRRADIANCE, + LOC_MAP_PREFILTER, + LOC_MAP_BRDF +} ShaderLocationIndex; + +#define LOC_MAP_DIFFUSE LOC_MAP_ALBEDO +#define LOC_MAP_SPECULAR LOC_MAP_METALNESS + +// Material map type +typedef enum { + MAP_ALBEDO = 0, // MAP_DIFFUSE + MAP_METALNESS = 1, // MAP_SPECULAR + MAP_NORMAL = 2, + MAP_ROUGHNESS = 3, + MAP_OCCLUSION, + MAP_EMISSION, + MAP_HEIGHT, + MAP_CUBEMAP, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_BRDF +} TexmapIndex; + +#define MAP_DIFFUSE MAP_ALBEDO +#define MAP_SPECULAR MAP_METALNESS + // Texture formats // NOTE: Support depends on OpenGL version and platform typedef enum { @@ -706,17 +742,21 @@ RLAPI int GetHexValue(Color color); // Returns hex RLAPI Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value RLAPI Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f RLAPI float *ColorToFloat(Color color); // Converts Color to float array and normalizes -RLAPI float *VectorToFloat(Vector3 vec); // Converts Vector3 to float array -RLAPI float *MatrixToFloat(Matrix mat); // Converts Matrix to float array + +// Math useful functions (available from raymath.h) +RLAPI float *VectorToFloat(Vector3 vec); // Returns Vector3 as float array +RLAPI float *MatrixToFloat(Matrix mat); // Returns Matrix as float array +RLAPI Vector3 Vector3Zero(void); // Vector with components value 0.0f +RLAPI Vector3 Vector3One(void); // Vector with components value 1.0f +RLAPI Matrix MatrixIdentity(void); // Returns identity matrix // Misc. functions RLAPI void ShowLogo(void); // Activate raylib logo at startup (can be done with flags) RLAPI void SetConfigFlags(char flags); // Setup window configuration flags (view FLAGS) -RLAPI void TraceLog(int logType, const char *text, ...); // Show trace log messages (INFO, WARNING, ERROR, DEBUG) +RLAPI void TraceLog(int logType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (saved a .png) RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) - // Files management functions RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension RLAPI const char *GetDirectoryPath(const char *fileName); // Get directory for a given fileName (with path) @@ -836,7 +876,7 @@ RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Ve // Texture Loading and Drawing Functions (Module: textures) //------------------------------------------------------------------------------------ -// Image/Texture2D data loading/unloading functions +// Image/Texture2D data loading/unloading/saving functions 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 @@ -850,6 +890,7 @@ RLAPI void UnloadRenderTexture(RenderTexture2D target); 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, const void *pixels); // Update GPU texture with new data +RLAPI void SaveImageAs(const char *fileName, Image image); // Save image to a PNG file // Image manipulation functions RLAPI void ImageToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two) @@ -874,6 +915,15 @@ RLAPI void ImageColorGrayscale(Image *image); 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) +// Image generation functions +RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient +RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient +RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient +RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked +RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise +RLAPI Image GenImagePerlinNoise(int width, int height, float scale); // Generate image: perlin noise +RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm. Bigger tileSize means bigger cells + // Texture2D configuration functions RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture RLAPI void SetTextureFilter(Texture2D texture, int filterMode); // Set texture scaling filter mode @@ -902,6 +952,7 @@ RLAPI void DrawFPS(int posX, int posY); RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) RLAPI void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, // Draw text using SpriteFont and additional parameters float fontSize, int spacing, Color tint); +RLAPI void DrawRectangleT(int posX, int posY, int width, int height, Color color); // Draw rectangle using text character // Text misc. functions RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font @@ -936,18 +987,26 @@ RLAPI void DrawGizmo(Vector3 position); //------------------------------------------------------------------------------------ // Model loading/unloading functions +RLAPI Model LoadModel(const char *fileName); // Load model from files (mesh and material) +RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh +RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) + +// Mesh loading/unloading functions 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 Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with desired subdivisions) +RLAPI Mesh GenMeshCube(float width, float height, float length); // Generate cuboid mesh +//RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere) +//RLAPI Mesh GenMeshCylinder(float radiusTop, float radiusBottom, float height, int slices); // Generate cylinder mesh +//RLAPI Mesh GenMeshTorus(float radius1, float radius2, int radSeg, int sides); // Generate torus mesh +//RLAPI Mesh GenMeshTube(float radius1, float radius2, float height, int sides); // Generate tube mesh +RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data +RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data // Material loading/unloading functions RLAPI Material LoadMaterial(const char *fileName); // Load material from file -RLAPI Material LoadDefaultMaterial(void); // Load default material (uses default models shader) +RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) // Model drawing functions @@ -985,8 +1044,8 @@ RLAPI char *LoadText(const char *fileName); // Loa 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 Texture2D GetDefaultTexture(void); // Get default texture +RLAPI Shader GetShaderDefault(void); // Get default shader +RLAPI Texture2D GetTextureDefault(void); // Get default texture // Shader configuration functions RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location @@ -996,6 +1055,13 @@ RLAPI void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); RLAPI void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) RLAPI void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) +// Texture maps generation (PBR) +// NOTE: Required shaders should be provided +RLAPI Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture +RLAPI Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data +RLAPI Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data +RLAPI Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size); // Generate BRDF texture using cubemap data + // Shading begin/end functions RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) diff --git a/src/raymath.h b/src/raymath.h index 1dbebc07..fe0b8947 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raymath v1.0 - Math functions to work with Vector3, Matrix and Quaternions +* raymath v1.1 - Math functions to work with Vector3, Matrix and Quaternions * * CONFIGURATION: * @@ -19,7 +19,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-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. @@ -124,7 +124,7 @@ RMDEF Vector2 Vector2Zero(void); // Vector with c RMDEF Vector2 Vector2One(void); // Vector with components value 1.0f RMDEF Vector2 Vector2Add(Vector2 v1, Vector2 v2); // Add two vectors (v1 + v2) RMDEF Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); // Subtract two vectors (v1 - v2) -RMDEF float Vector2Lenght(Vector2 v); // Calculate vector lenght +RMDEF float Vector2Length(Vector2 v); // Calculate vector length RMDEF float Vector2DotProduct(Vector2 v1, Vector2 v2); // Calculate two vectors dot product RMDEF float Vector2Distance(Vector2 v1, Vector2 v2); // Calculate distance between two vectors RMDEF float Vector2Angle(Vector2 v1, Vector2 v2); // Calculate angle between two vectors in X-axis @@ -136,24 +136,27 @@ RMDEF void Vector2Normalize(Vector2 *v); // Normalize pro //------------------------------------------------------------------------------------ // Functions Declaration to work with Vector3 //------------------------------------------------------------------------------------ -RMDEF Vector3 VectorZero(void); // Vector with components value 0.0f -RMDEF Vector3 VectorOne(void); // Vector with components value 1.0f -RMDEF Vector3 VectorAdd(Vector3 v1, Vector3 v2); // Add two vectors -RMDEF Vector3 VectorSubtract(Vector3 v1, Vector3 v2); // Substract two vectors -RMDEF Vector3 VectorCrossProduct(Vector3 v1, Vector3 v2); // Calculate two vectors cross product -RMDEF Vector3 VectorPerpendicular(Vector3 v); // Calculate one vector perpendicular vector -RMDEF float VectorLength(const Vector3 v); // Calculate vector lenght -RMDEF float VectorDotProduct(Vector3 v1, Vector3 v2); // Calculate two vectors dot product -RMDEF float VectorDistance(Vector3 v1, Vector3 v2); // Calculate distance between two points -RMDEF void VectorScale(Vector3 *v, float scale); // Scale provided vector -RMDEF void VectorNegate(Vector3 *v); // Negate provided vector (invert direction) -RMDEF void VectorNormalize(Vector3 *v); // Normalize provided vector -RMDEF void VectorTransform(Vector3 *v, Matrix mat); // Transforms a Vector3 by a given Matrix -RMDEF Vector3 VectorLerp(Vector3 v1, Vector3 v2, float amount); // Calculate linear interpolation between two vectors -RMDEF Vector3 VectorReflect(Vector3 vector, Vector3 normal); // Calculate reflected vector to normal -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 VectorBarycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c); // Barycenter coords for p in triangle abc +RMDEF Vector3 Vector3Zero(void); // Vector with components value 0.0f +RMDEF Vector3 Vector3One(void); // Vector with components value 1.0f +RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2); // Add two vectors +RMDEF Vector3 Vector3Multiply(Vector3 v, float scalar); // Multiply vector by scalar +RMDEF Vector3 Vector3MultiplyV(Vector3 v1, Vector3 v2); // Multiply vector by vector +RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2); // Substract two vectors +RMDEF Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2); // Calculate two vectors cross product +RMDEF Vector3 Vector3Perpendicular(Vector3 v); // Calculate one vector perpendicular vector +RMDEF float Vector3Length(const Vector3 v); // Calculate vector length +RMDEF float Vector3DotProduct(Vector3 v1, Vector3 v2); // Calculate two vectors dot product +RMDEF float Vector3Distance(Vector3 v1, Vector3 v2); // Calculate distance between two points +RMDEF void Vector3Scale(Vector3 *v, float scale); // Scale provided vector +RMDEF void Vector3Negate(Vector3 *v); // Negate provided vector (invert direction) +RMDEF void Vector3Normalize(Vector3 *v); // Normalize provided vector +RMDEF void Vector3Transform(Vector3 *v, Matrix mat); // Transforms a Vector3 by a given Matrix +RMDEF Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount); // Calculate linear interpolation between two vectors +RMDEF Vector3 Vector3Reflect(Vector3 vector, Vector3 normal); // Calculate reflected vector to normal +RMDEF Vector3 Vector3Min(Vector3 vec1, Vector3 vec2); // Return min value for each pair of components +RMDEF Vector3 Vector3Max(Vector3 vec1, Vector3 vec2); // Return max value for each pair of components +RMDEF Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c); // Barycenter coords for p in triangle abc +RMDEF float *Vector3ToFloat(Vector3 vec); // Returns Vector3 as float array //------------------------------------------------------------------------------------ // Functions Declaration to work with Matrix @@ -177,15 +180,20 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far); // Returns perspective projection matrix RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far); // Returns orthographic projection matrix RMDEF Matrix MatrixLookAt(Vector3 position, Vector3 target, Vector3 up); // Returns camera look-at matrix (view matrix) +RMDEF float *MatrixToFloat(Matrix mat); // Returns float array of Matrix data //------------------------------------------------------------------------------------ // Functions Declaration to work with Quaternions //------------------------------------------------------------------------------------ +RMDEF Quaternion QuaternionIdentity(void); // Returns identity quaternion RMDEF float QuaternionLength(Quaternion quat); // Compute the length of a quaternion RMDEF void QuaternionNormalize(Quaternion *q); // Normalize provided quaternion RMDEF void QuaternionInvert(Quaternion *quat); // Invert provided quaternion RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2); // Calculate two quaternion multiplication -RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float slerp); // Calculates spherical linear interpolation between two quaternions +RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount); // Calculate linear interpolation between two quaternions +RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount); // Calculates spherical linear interpolation between two quaternions +RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount); // Calculate slerp-optimized interpolation between two quaternions +RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to); // Calculate quaternion based on the rotation from one vector to another RMDEF Quaternion QuaternionFromMatrix(Matrix matrix); // Returns a quaternion for a given rotation matrix RMDEF Matrix QuaternionToMatrix(Quaternion q); // Returns a matrix for a given quaternion RMDEF Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle); // Returns rotation quaternion for an angle and axis @@ -236,8 +244,8 @@ RMDEF Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) return (Vector2){ v1.x - v2.x, v1.y - v2.y }; } -// Calculate vector lenght -RMDEF float Vector2Lenght(Vector2 v) +// Calculate vector length +RMDEF float Vector2Length(Vector2 v) { return sqrtf((v.x*v.x) + (v.y*v.y)); } @@ -287,7 +295,7 @@ RMDEF void Vector2Divide(Vector2 *v, float div) // Normalize provided vector RMDEF void Vector2Normalize(Vector2 *v) { - Vector2Divide(v, Vector2Lenght(*v)); + Vector2Divide(v, Vector2Length(*v)); } //---------------------------------------------------------------------------------- @@ -295,25 +303,47 @@ RMDEF void Vector2Normalize(Vector2 *v) //---------------------------------------------------------------------------------- // Vector with components value 0.0f -RMDEF Vector3 VectorZero(void) { return (Vector3){ 0.0f, 0.0f, 0.0f }; } +RMDEF Vector3 Vector3Zero(void) { return (Vector3){ 0.0f, 0.0f, 0.0f }; } // Vector with components value 1.0f -RMDEF Vector3 VectorOne(void) { return (Vector3){ 1.0f, 1.0f, 1.0f }; } +RMDEF Vector3 Vector3One(void) { return (Vector3){ 1.0f, 1.0f, 1.0f }; } // Add two vectors -RMDEF Vector3 VectorAdd(Vector3 v1, Vector3 v2) +RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2) { return (Vector3){ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; } // Substract two vectors -RMDEF Vector3 VectorSubtract(Vector3 v1, Vector3 v2) +RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) { return (Vector3){ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; } +// Multiply vector by scalar +RMDEF Vector3 Vector3Multiply(Vector3 v, float scalar) +{ + v.x *= scalar; + v.y *= scalar; + v.z *= scalar; + + return v; +} + +// Multiply vector by vector +RMDEF Vector3 Vector3MultiplyV(Vector3 v1, Vector3 v2) +{ + Vector3 result; + + result.x = v1.x * v2.x; + result.y = v1.y * v2.y; + result.z = v1.z * v2.z; + + return result; +} + // Calculate two vectors cross product -RMDEF Vector3 VectorCrossProduct(Vector3 v1, Vector3 v2) +RMDEF Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2) { Vector3 result; @@ -325,7 +355,7 @@ RMDEF Vector3 VectorCrossProduct(Vector3 v1, Vector3 v2) } // Calculate one vector perpendicular vector -RMDEF Vector3 VectorPerpendicular(Vector3 v) +RMDEF Vector3 Vector3Perpendicular(Vector3 v) { Vector3 result; @@ -343,25 +373,25 @@ RMDEF Vector3 VectorPerpendicular(Vector3 v) cardinalAxis = (Vector3){0.0f, 0.0f, 1.0f}; } - result = VectorCrossProduct(v, cardinalAxis); + result = Vector3CrossProduct(v, cardinalAxis); return result; } -// Calculate vector lenght -RMDEF float VectorLength(const Vector3 v) +// Calculate vector length +RMDEF float Vector3Length(const Vector3 v) { return sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); } // Calculate two vectors dot product -RMDEF float VectorDotProduct(Vector3 v1, Vector3 v2) +RMDEF float Vector3DotProduct(Vector3 v1, Vector3 v2) { return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); } // Calculate distance between two vectors -RMDEF float VectorDistance(Vector3 v1, Vector3 v2) +RMDEF float Vector3Distance(Vector3 v1, Vector3 v2) { float dx = v2.x - v1.x; float dy = v2.y - v1.y; @@ -371,7 +401,7 @@ RMDEF float VectorDistance(Vector3 v1, Vector3 v2) } // Scale provided vector -RMDEF void VectorScale(Vector3 *v, float scale) +RMDEF void Vector3Scale(Vector3 *v, float scale) { v->x *= scale; v->y *= scale; @@ -379,7 +409,7 @@ RMDEF void VectorScale(Vector3 *v, float scale) } // Negate provided vector (invert direction) -RMDEF void VectorNegate(Vector3 *v) +RMDEF void Vector3Negate(Vector3 *v) { v->x = -v->x; v->y = -v->y; @@ -387,11 +417,11 @@ RMDEF void VectorNegate(Vector3 *v) } // Normalize provided vector -RMDEF void VectorNormalize(Vector3 *v) +RMDEF void Vector3Normalize(Vector3 *v) { float length, ilength; - length = VectorLength(*v); + length = Vector3Length(*v); if (length == 0.0f) length = 1.0f; @@ -403,8 +433,7 @@ RMDEF void VectorNormalize(Vector3 *v) } // Transforms a Vector3 by a given Matrix -// TODO: Review math (matrix transpose required?) -RMDEF void VectorTransform(Vector3 *v, Matrix mat) +RMDEF void Vector3Transform(Vector3 *v, Matrix mat) { float x = v->x; float y = v->y; @@ -416,7 +445,7 @@ RMDEF void VectorTransform(Vector3 *v, Matrix mat) }; // Calculate linear interpolation between two vectors -RMDEF Vector3 VectorLerp(Vector3 v1, Vector3 v2, float amount) +RMDEF Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount) { Vector3 result; @@ -428,7 +457,7 @@ RMDEF Vector3 VectorLerp(Vector3 v1, Vector3 v2, float amount) } // Calculate reflected vector to normal -RMDEF Vector3 VectorReflect(Vector3 vector, Vector3 normal) +RMDEF Vector3 Vector3Reflect(Vector3 vector, Vector3 normal) { // I is the original vector // N is the normal of the incident plane @@ -436,7 +465,7 @@ RMDEF Vector3 VectorReflect(Vector3 vector, Vector3 normal) Vector3 result; - float dotProduct = VectorDotProduct(vector, normal); + float dotProduct = Vector3DotProduct(vector, normal); result.x = vector.x - (2.0f*normal.x)*dotProduct; result.y = vector.y - (2.0f*normal.y)*dotProduct; @@ -446,7 +475,7 @@ RMDEF Vector3 VectorReflect(Vector3 vector, Vector3 normal) } // Return min value for each pair of components -RMDEF Vector3 VectorMin(Vector3 vec1, Vector3 vec2) +RMDEF Vector3 Vector3Min(Vector3 vec1, Vector3 vec2) { Vector3 result; @@ -458,7 +487,7 @@ RMDEF Vector3 VectorMin(Vector3 vec1, Vector3 vec2) } // Return max value for each pair of components -RMDEF Vector3 VectorMax(Vector3 vec1, Vector3 vec2) +RMDEF Vector3 Vector3Max(Vector3 vec1, Vector3 vec2) { Vector3 result; @@ -471,18 +500,18 @@ RMDEF Vector3 VectorMax(Vector3 vec1, Vector3 vec2) // 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 VectorBarycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) +RMDEF Vector3 Vector3Barycenter(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); + Vector3 v0 = Vector3Subtract(b, a); + Vector3 v1 = Vector3Subtract(c, a); + Vector3 v2 = Vector3Subtract(p, a); + float d00 = Vector3DotProduct(v0, v0); + float d01 = Vector3DotProduct(v0, v1); + float d11 = Vector3DotProduct(v1, v1); + float d20 = Vector3DotProduct(v2, v0); + float d21 = Vector3DotProduct(v2, v1); float denom = d00*d11 - d01*d01; @@ -495,6 +524,18 @@ RMDEF Vector3 VectorBarycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c) return result; } +// Returns Vector3 as float array +RMDEF float *Vector3ToFloat(Vector3 vec) +{ + static float buffer[3]; + + buffer[0] = vec.x; + buffer[1] = vec.y; + buffer[2] = vec.z; + + return buffer; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix math //---------------------------------------------------------------------------------- @@ -685,10 +726,10 @@ RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) // Returns translation matrix RMDEF Matrix MatrixTranslate(float x, float y, float z) { - Matrix result = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - x, y, z, 1.0f }; + Matrix result = { 1.0f, 0.0f, 0.0f, x, + 0.0f, 1.0f, 0.0f, y, + 0.0f, 0.0f, 1.0f, z, + 0.0f, 0.0f, 0.0f, 1.0f }; return result; } @@ -813,22 +854,22 @@ RMDEF Matrix MatrixMultiply(Matrix left, Matrix right) { Matrix result; - result.m0 = right.m0*left.m0 + right.m1*left.m4 + right.m2*left.m8 + right.m3*left.m12; - result.m1 = right.m0*left.m1 + right.m1*left.m5 + right.m2*left.m9 + right.m3*left.m13; - result.m2 = right.m0*left.m2 + right.m1*left.m6 + right.m2*left.m10 + right.m3*left.m14; - result.m3 = right.m0*left.m3 + right.m1*left.m7 + right.m2*left.m11 + right.m3*left.m15; - result.m4 = right.m4*left.m0 + right.m5*left.m4 + right.m6*left.m8 + right.m7*left.m12; - result.m5 = right.m4*left.m1 + right.m5*left.m5 + right.m6*left.m9 + right.m7*left.m13; - result.m6 = right.m4*left.m2 + right.m5*left.m6 + right.m6*left.m10 + right.m7*left.m14; - result.m7 = right.m4*left.m3 + right.m5*left.m7 + right.m6*left.m11 + right.m7*left.m15; - result.m8 = right.m8*left.m0 + right.m9*left.m4 + right.m10*left.m8 + right.m11*left.m12; - result.m9 = right.m8*left.m1 + right.m9*left.m5 + right.m10*left.m9 + right.m11*left.m13; - result.m10 = right.m8*left.m2 + right.m9*left.m6 + right.m10*left.m10 + right.m11*left.m14; - result.m11 = right.m8*left.m3 + right.m9*left.m7 + right.m10*left.m11 + right.m11*left.m15; - result.m12 = right.m12*left.m0 + right.m13*left.m4 + right.m14*left.m8 + right.m15*left.m12; - result.m13 = right.m12*left.m1 + right.m13*left.m5 + right.m14*left.m9 + right.m15*left.m13; - result.m14 = right.m12*left.m2 + right.m13*left.m6 + right.m14*left.m10 + right.m15*left.m14; - result.m15 = right.m12*left.m3 + right.m13*left.m7 + right.m14*left.m11 + right.m15*left.m15; + result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12; + result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13; + result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14; + result.m3 = left.m0*right.m3 + left.m1*right.m7 + left.m2*right.m11 + left.m3*right.m15; + result.m4 = left.m4*right.m0 + left.m5*right.m4 + left.m6*right.m8 + left.m7*right.m12; + result.m5 = left.m4*right.m1 + left.m5*right.m5 + left.m6*right.m9 + left.m7*right.m13; + result.m6 = left.m4*right.m2 + left.m5*right.m6 + left.m6*right.m10 + left.m7*right.m14; + result.m7 = left.m4*right.m3 + left.m5*right.m7 + left.m6*right.m11 + left.m7*right.m15; + result.m8 = left.m8*right.m0 + left.m9*right.m4 + left.m10*right.m8 + left.m11*right.m12; + result.m9 = left.m8*right.m1 + left.m9*right.m5 + left.m10*right.m9 + left.m11*right.m13; + result.m10 = left.m8*right.m2 + left.m9*right.m6 + left.m10*right.m10 + left.m11*right.m14; + result.m11 = left.m8*right.m3 + left.m9*right.m7 + left.m10*right.m11 + left.m11*right.m15; + result.m12 = left.m12*right.m0 + left.m13*right.m4 + left.m14*right.m8 + left.m15*right.m12; + result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13; + result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14; + result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15; return result; } @@ -866,9 +907,10 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top, } // Returns perspective projection matrix +// NOTE: Angle should be provided in radians RMDEF Matrix MatrixPerspective(double fovy, double aspect, double near, double far) { - double top = near*tan(fovy*PI/360.0); + double top = near*tan(fovy*0.5); double right = top*aspect; return MatrixFrustum(-right, right, -top, top, near, far); @@ -908,37 +950,70 @@ RMDEF Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up) { Matrix result; - Vector3 z = VectorSubtract(eye, target); - VectorNormalize(&z); - Vector3 x = VectorCrossProduct(up, z); - VectorNormalize(&x); - Vector3 y = VectorCrossProduct(z, x); - VectorNormalize(&y); - + Vector3 z = Vector3Subtract(eye, target); + Vector3Normalize(&z); + Vector3 x = Vector3CrossProduct(up, z); + Vector3Normalize(&x); + Vector3 y = Vector3CrossProduct(z, x); + Vector3Normalize(&y); + result.m0 = x.x; result.m1 = x.y; result.m2 = x.z; - result.m3 = -((x.x*eye.x) + (x.y*eye.y) + (x.z*eye.z)); + result.m3 = 0.0f; result.m4 = y.x; result.m5 = y.y; result.m6 = y.z; - result.m7 = -((y.x*eye.x) + (y.y*eye.y) + (y.z*eye.z)); + result.m7 = 0.0f; result.m8 = z.x; result.m9 = z.y; result.m10 = z.z; - result.m11 = -((z.x*eye.x) + (z.y*eye.y) + (z.z*eye.z)); - result.m12 = 0.0f; - result.m13 = 0.0f; - result.m14 = 0.0f; + result.m11 = 0.0f; + result.m12 = eye.x; + result.m13 = eye.y; + result.m14 = eye.z; result.m15 = 1.0f; + MatrixInvert(&result); + return result; } +// Returns float array of matrix data +RMDEF float *MatrixToFloat(Matrix mat) +{ + static float buffer[16]; + + buffer[0] = mat.m0; + buffer[1] = mat.m1; + buffer[2] = mat.m2; + buffer[3] = mat.m3; + buffer[4] = mat.m4; + buffer[5] = mat.m5; + buffer[6] = mat.m6; + buffer[7] = mat.m7; + buffer[8] = mat.m8; + buffer[9] = mat.m9; + buffer[10] = mat.m10; + buffer[11] = mat.m11; + buffer[12] = mat.m12; + buffer[13] = mat.m13; + buffer[14] = mat.m14; + buffer[15] = mat.m15; + + return buffer; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Quaternion math //---------------------------------------------------------------------------------- +// Returns identity quaternion +RMDEF Quaternion QuaternionIdentity(void) +{ + return (Quaternion){ 0.0f, 0.0f, 0.0f, 1.0f }; +} + // Computes the length of a quaternion RMDEF float QuaternionLength(Quaternion quat) { @@ -995,6 +1070,19 @@ RMDEF Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2) return result; } +// Calculate linear interpolation between two quaternions +RMDEF Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result; + + result.x = q1.x + amount*(q2.x - q1.x); + result.y = q1.y + amount*(q2.y - q1.y); + result.z = q1.z + amount*(q2.z - q1.z); + result.w = q1.w + amount*(q2.w - q1.w); + + return result; +} + // Calculates spherical linear interpolation between two quaternions RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) { @@ -1003,6 +1091,7 @@ RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; if (fabs(cosHalfTheta) >= 1.0f) result = q1; + else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount); else { float halfTheta = acos(cosHalfTheta); @@ -1030,6 +1119,37 @@ RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) return result; } +// Calculate slerp-optimized interpolation between two quaternions +RMDEF Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount) +{ + Quaternion result = QuaternionLerp(q1, q2, amount); + QuaternionNormalize(&result); + + return result; +} + +// Calculate quaternion based on the rotation from one vector to another +RMDEF Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to) +{ + Quaternion q = { 0 }; + + float cos2Theta = Vector3DotProduct(from, to); + Vector3 cross = Vector3CrossProduct(from, to); + + q.x = cross.x; + q.y = cross.y; + q.z = cross.y; + q.w = 1.0f + cos2Theta; // NOTE: Added QuaternioIdentity() + + // Normalize to essentially nlerp the original and identity to 0.5 + QuaternionNormalize(&q); + + // Above lines are equivalent to: + //Quaternion result = QuaternionNlerp(q, QuaternionIdentity(), 0.5f); + + return q; +} + // Returns a quaternion for a given rotation matrix RMDEF Quaternion QuaternionFromMatrix(Matrix matrix) { @@ -1096,18 +1216,21 @@ RMDEF Matrix QuaternionToMatrix(Quaternion q) float x2 = x + x; float y2 = y + y; float z2 = z + z; + + float length = QuaternionLength(q); + float lengthSquared = length*length; - float xx = x*x2; - float xy = x*y2; - float xz = x*z2; + float xx = x*x2/lengthSquared; + float xy = x*y2/lengthSquared; + float xz = x*z2/lengthSquared; - float yy = y*y2; - float yz = y*z2; - float zz = z*z2; + float yy = y*y2/lengthSquared; + float yz = y*z2/lengthSquared; + float zz = z*z2/lengthSquared; - float wx = w*x2; - float wy = w*y2; - float wz = w*z2; + float wx = w*x2/lengthSquared; + float wy = w*y2/lengthSquared; + float wz = w*z2/lengthSquared; result.m0 = 1.0f - (yy + zz); result.m1 = xy - wz; @@ -1135,11 +1258,11 @@ RMDEF Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle) { Quaternion result = { 0.0f, 0.0f, 0.0f, 1.0f }; - if (VectorLength(axis) != 0.0f) + if (Vector3Length(axis) != 0.0f) angle *= 0.5f; - VectorNormalize(&axis); + Vector3Normalize(&axis); float sinres = sinf(angle); float cosres = cosf(angle); @@ -1203,7 +1326,7 @@ RMDEF Quaternion QuaternionFromEuler(float roll, float pitch, float yaw) } // Return the Euler angles equivalent to quaternion (roll, pitch, yaw) -// NOTE: Angles are returned in a Vector3 struct and in degrees +// NOTE: Angles are returned in a Vector3 struct in degrees RMDEF Vector3 QuaternionToEuler(Quaternion q) { Vector3 v = { 0 }; diff --git a/src/raylib_icon b/src/resources Binary files differindex 92ccf3a6..92ccf3a6 100644 --- a/src/raylib_icon +++ b/src/resources @@ -87,12 +87,12 @@ #if defined(__APPLE__) #include <OpenGL/gl3.h> // OpenGL 3 library for OSX #else - #define GLAD_IMPLEMENTATION - #if defined(RLGL_STANDALONE) - #include "glad.h" // GLAD extensions loading library, includes OpenGL headers - #else - #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers - #endif + #define GLAD_IMPLEMENTATION + #if defined(RLGL_STANDALONE) + #include "glad.h" // GLAD extensions loading library, includes OpenGL headers + #else + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers + #endif #endif #endif @@ -317,7 +317,7 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; // Compressed textures support flags static bool texCompDXTSupported = false; // DDS texture compression support -static bool texNPOTSupported = false; // NPOT textures full support +static bool texNPOTSupported = false; // NPOT textures full support static bool texFloatSupported = false; // float textures support (32 bit per channel) static int blendMode = 0; // Track current blending mode @@ -333,17 +333,20 @@ static int screenHeight; // Default framebuffer height // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) -static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat); +static void LoadTextureCompressed(unsigned char *data, int width, int height, int compressedFormat, int mipmapCount); 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 void LoadDefaultShaderLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) -static void UnloadDefaultShader(void); // Unload default shader +static Shader LoadShaderDefault(void); // Load default shader (just vertex positioning and texture coloring) +static void SetShaderDefaultLocations(Shader *shader); // Bind default shader locations (attributes and uniforms) +static void UnloadShaderDefault(void); // Unload default 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 -static void DrawDefaultBuffers(void); // Draw default internal buffers vertex data -static void UnloadDefaultBuffers(void); // Unload default internal buffers vertex data from CPU and GPU +static void LoadBuffersDefault(void); // Load default internal buffers (lines, triangles, quads) +static void UpdateBuffersDefault(void); // Update default internal buffers (VAOs/VBOs) with vertex data +static void DrawBuffersDefault(void); // Draw default internal buffers vertex data +static void UnloadBuffersDefault(void); // Unload default internal buffers vertex data from CPU and GPU + +static void GenDrawCube(void); // Generate and draw cube +static void GenDrawQuad(void); // Generate and draw quad #if defined(SUPPORT_VR_SIMULATOR) static void SetStereoConfig(VrDeviceInfo info); // Configure stereo rendering (including distortion shader) with HMD device parameters @@ -357,10 +360,6 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight); static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight); #endif -#if defined(RLGL_STANDALONE) -float *MatrixToFloat(Matrix mat); // Converts Matrix to float array -#endif - //---------------------------------------------------------------------------------- // Module Functions Definition - Matrix operations //---------------------------------------------------------------------------------- @@ -396,7 +395,7 @@ void rlLoadIdentity(void) { glLoadIdentity(); } void rlTranslatef(float x, float y, float z) { glTranslatef(x, y, z); } void rlRotatef(float angleDeg, float x, float y, float z) { glRotatef(angleDeg, x, y, z); } void rlScalef(float x, float y, float z) { glScalef(x, y, z); } -void rlMultMatrixf(float *mat) { glMultMatrixf(mat); } +void rlMultMatrixf(float *matf) { glMultMatrixf(matf); } #elif defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -415,7 +414,7 @@ void rlPushMatrix(void) { if (stackCounter == MATRIX_STACK_SIZE - 1) { - TraceLog(ERROR, "Stack Buffer Overflow (MAX %i Matrix)", MATRIX_STACK_SIZE); + TraceLog(LOG_ERROR, "Stack Buffer Overflow (MAX %i Matrix)", MATRIX_STACK_SIZE); } stack[stackCounter] = *currentMatrix; @@ -446,9 +445,9 @@ void rlLoadIdentity(void) void rlTranslatef(float x, float y, float z) { Matrix matTranslation = MatrixTranslate(x, y, z); - MatrixTranspose(&matTranslation); - *currentMatrix = MatrixMultiply(*currentMatrix, matTranslation); + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matTranslation, *currentMatrix); } // Multiply the current matrix by a rotation matrix @@ -457,30 +456,30 @@ void rlRotatef(float angleDeg, float x, float y, float z) Matrix matRotation = MatrixIdentity(); Vector3 axis = (Vector3){ x, y, z }; - VectorNormalize(&axis); + Vector3Normalize(&axis); matRotation = MatrixRotate(axis, angleDeg*DEG2RAD); - MatrixTranspose(&matRotation); - *currentMatrix = MatrixMultiply(*currentMatrix, matRotation); + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matRotation, *currentMatrix); } // Multiply the current matrix by a scaling matrix void rlScalef(float x, float y, float z) { Matrix matScale = MatrixScale(x, y, z); - MatrixTranspose(&matScale); - *currentMatrix = MatrixMultiply(*currentMatrix, matScale); + // NOTE: We transpose matrix with multiplication order + *currentMatrix = MatrixMultiply(matScale, *currentMatrix); } // Multiply the current matrix by another matrix -void rlMultMatrixf(float *m) +void rlMultMatrixf(float *matf) { // Matrix creation from array - Matrix mat = { m[0], m[1], m[2], m[3], - m[4], m[5], m[6], m[7], - m[8], m[9], m[10], m[11], - m[12], m[13], m[14], m[15] }; + Matrix mat = { matf[0], matf[4], matf[8], matf[12], + matf[1], matf[5], matf[9], matf[13], + matf[2], matf[6], matf[10], matf[14], + matf[3], matf[7], matf[11], matf[15] }; *currentMatrix = MatrixMultiply(*currentMatrix, mat); } @@ -489,7 +488,6 @@ void rlMultMatrixf(float *m) void rlFrustum(double left, double right, double bottom, double top, double near, double far) { Matrix matPerps = MatrixFrustum(left, right, bottom, top, near, far); - MatrixTranspose(&matPerps); *currentMatrix = MatrixMultiply(*currentMatrix, matPerps); } @@ -498,7 +496,6 @@ void rlFrustum(double left, double right, double bottom, double top, double near void rlOrtho(double left, double right, double bottom, double top, double near, double far) { Matrix matOrtho = MatrixOrtho(left, right, bottom, top, near, far); - MatrixTranspose(&matOrtho); *currentMatrix = MatrixMultiply(*currentMatrix, matOrtho); } @@ -559,7 +556,7 @@ void rlEnd(void) // This way, rlTranslatef(), rlRotatef()... behaviour is the same than OpenGL 1.1 // Apply transformation matrix to all temp vertices - for (int i = 0; i < tempBufferCount; i++) VectorTransform(&tempBuffer[i], *currentMatrix); + for (int i = 0; i < tempBufferCount; i++) Vector3Transform(&tempBuffer[i], *currentMatrix); // Deactivate tempBuffer usage to allow rlVertex3f do its job useTempBuffer = false; @@ -678,7 +675,7 @@ void rlVertex3f(float x, float y, float z) lines.vCounter++; } - else TraceLog(ERROR, "MAX_LINES_BATCH overflow"); + else TraceLog(LOG_ERROR, "MAX_LINES_BATCH overflow"); } break; case RL_TRIANGLES: @@ -692,7 +689,7 @@ void rlVertex3f(float x, float y, float z) triangles.vCounter++; } - else TraceLog(ERROR, "MAX_TRIANGLES_BATCH overflow"); + else TraceLog(LOG_ERROR, "MAX_TRIANGLES_BATCH overflow"); } break; case RL_QUADS: @@ -708,7 +705,7 @@ void rlVertex3f(float x, float y, float z) draws[drawsCounter - 1].vertexCount++; } - else TraceLog(ERROR, "MAX_QUADS_BATCH overflow"); + else TraceLog(LOG_ERROR, "MAX_QUADS_BATCH overflow"); } break; default: break; @@ -847,7 +844,7 @@ void rlTextureParameters(unsigned int id, int param, int value) case RL_TEXTURE_WRAP_S: case RL_TEXTURE_WRAP_T: { - if ((value == RL_WRAP_CLAMP_MIRROR) && !texClampMirrorSupported) TraceLog(WARNING, "Clamp mirror wrap mode not supported"); + if ((value == RL_WRAP_CLAMP_MIRROR) && !texClampMirrorSupported) TraceLog(LOG_WARNING, "Clamp mirror wrap mode not supported"); else glTexParameteri(GL_TEXTURE_2D, param, value); } break; case RL_TEXTURE_MAG_FILTER: @@ -857,10 +854,10 @@ void rlTextureParameters(unsigned int id, int param, int 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); + TraceLog(LOG_WARNING, "[TEX ID %i] Maximum anisotropic filter level supported is %iX", id, maxAnisotropicLevel); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value); } - else TraceLog(WARNING, "Anisotropic filtering not supported"); + else TraceLog(LOG_WARNING, "Anisotropic filtering not supported"); } break; default: break; } @@ -923,18 +920,18 @@ void rlDisableWireMode(void) // Unload texture from GPU memory void rlDeleteTextures(unsigned int id) { - if (id != 0) glDeleteTextures(1, &id); + if (id > 0) glDeleteTextures(1, &id); } // Unload render texture from GPU memory void rlDeleteRenderTextures(RenderTexture2D target) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - 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); + 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); + TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); #endif } @@ -953,7 +950,7 @@ void rlDeleteVertexArrays(unsigned int id) if (vaoSupported) { if (id != 0) glDeleteVertexArrays(1, &id); - TraceLog(INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", id); + TraceLog(LOG_INFO, "[VAO ID %i] Unloaded model data from VRAM (GPU)", id); } #endif } @@ -965,7 +962,7 @@ void rlDeleteBuffers(unsigned int id) if (id != 0) { glDeleteBuffers(1, &id); - if (!vaoSupported) TraceLog(INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id); + if (!vaoSupported) TraceLog(LOG_INFO, "[VBO ID %i] Unloaded model vertex data from VRAM (GPU)", id); } #endif } @@ -989,20 +986,6 @@ void rlClearScreenBuffers(void) //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... } -// Returns current OpenGL version -int rlGetVersion(void) -{ -#if defined(GRAPHICS_API_OPENGL_11) - return OPENGL_11; -#elif defined(GRAPHICS_API_OPENGL_21) - return OPENGL_21; -#elif defined(GRAPHICS_API_OPENGL_33) - return OPENGL_33; -#elif defined(GRAPHICS_API_OPENGL_ES2) - return OPENGL_ES_20; -#endif -} - //---------------------------------------------------------------------------------- // Module Functions Definition - rlgl Functions //---------------------------------------------------------------------------------- @@ -1014,33 +997,33 @@ void rlglInit(int width, int height) //------------------------------------------------------------------------------ // Print current OpenGL and GLSL version - TraceLog(INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); - TraceLog(INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); - TraceLog(INFO, "GPU: Version: %s", glGetString(GL_VERSION)); - TraceLog(INFO, "GPU: GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + TraceLog(LOG_INFO, "GPU: Vendor: %s", glGetString(GL_VENDOR)); + TraceLog(LOG_INFO, "GPU: Renderer: %s", glGetString(GL_RENDERER)); + TraceLog(LOG_INFO, "GPU: Version: %s", glGetString(GL_VERSION)); + TraceLog(LOG_INFO, "GPU: GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); // NOTE: We can get a bunch of extra information about GPU capabilities (glGet*) //int maxTexSize; //glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); - //TraceLog(INFO, "GL_MAX_TEXTURE_SIZE: %i", maxTexSize); + //TraceLog(LOG_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); + //TraceLog(LOG_INFO, "GL_AUX_BUFFERS: %i", numAuxBuffers); //GLint numComp = 0; //GLint format[32] = { 0 }; //glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numComp); //glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, format); - //for (int i = 0; i < numComp; i++) TraceLog(INFO, "Supported compressed format: 0x%x", format[i]); + //for (int i = 0; i < numComp; i++) TraceLog(LOG_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"); + //TraceLog(LOG_INFO, "OpenGL 1.1 (or driver default) profile initialized"); #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -1096,10 +1079,10 @@ void rlglInit(int width, int height) numExt -= 1; #endif - TraceLog(INFO, "Number of supported extensions: %i", numExt); + TraceLog(LOG_INFO, "Number of supported extensions: %i", numExt); // Show supported extensions - //for (int i = 0; i < numExt; i++) TraceLog(INFO, "Supported extension: %s", extList[i]); + //for (int i = 0; i < numExt; i++) TraceLog(LOG_INFO, "Supported extension: %s", extList[i]); // Check required extensions for (int i = 0; i < numExt; i++) @@ -1161,21 +1144,21 @@ void rlglInit(int width, int height) #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 (vaoSupported) TraceLog(LOG_INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully"); + else TraceLog(LOG_WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); - if (texNPOTSupported) 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)"); + if (texNPOTSupported) TraceLog(LOG_INFO, "[EXTENSION] NPOT textures extension detected, full NPOT textures supported"); + else TraceLog(LOG_WARNING, "[EXTENSION] NPOT textures extension not found, limited NPOT support (no-mipmaps, no-repeat)"); #endif - if (texCompDXTSupported) TraceLog(INFO, "[EXTENSION] DXT compressed textures supported"); - if (texCompETC1Supported) TraceLog(INFO, "[EXTENSION] ETC1 compressed textures supported"); - 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 (texCompDXTSupported) TraceLog(LOG_INFO, "[EXTENSION] DXT compressed textures supported"); + if (texCompETC1Supported) TraceLog(LOG_INFO, "[EXTENSION] ETC1 compressed textures supported"); + if (texCompETC2Supported) TraceLog(LOG_INFO, "[EXTENSION] ETC2/EAC compressed textures supported"); + if (texCompPVRTSupported) TraceLog(LOG_INFO, "[EXTENSION] PVRT compressed textures supported"); + if (texCompASTCSupported) TraceLog(LOG_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"); + if (texAnisotropicFilterSupported) TraceLog(LOG_INFO, "[EXTENSION] Anisotropic textures filtering supported (max: %.0fX)", maxAnisotropicLevel); + if (texClampMirrorSupported) TraceLog(LOG_INFO, "[EXTENSION] Clamp mirror wrap texture mode supported"); // Initialize buffers, default shaders and default textures //---------------------------------------------------------- @@ -1183,22 +1166,22 @@ void rlglInit(int width, int height) // Init default white texture unsigned char pixels[4] = { 255, 255, 255, 255 }; // 1 pixel RGBA (4 bytes) - whiteTexture = rlglLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); + whiteTexture = rlLoadTexture(pixels, 1, 1, UNCOMPRESSED_R8G8B8A8, 1); - if (whiteTexture != 0) TraceLog(INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); - else TraceLog(WARNING, "Base white texture could not be loaded"); + if (whiteTexture != 0) TraceLog(LOG_INFO, "[TEX ID %i] Base white texture loaded successfully", whiteTexture); + else TraceLog(LOG_WARNING, "Base white texture could not be loaded"); // Init default Shader (customized for GL 3.3 and ES2) - defaultShader = LoadDefaultShader(); + defaultShader = LoadShaderDefault(); currentShader = defaultShader; // Init default vertex arrays buffers (lines, triangles, quads) - LoadDefaultBuffers(); + LoadBuffersDefault(); // Init temp vertex buffer, used when transformation required (translate, rotate, scale) tempBuffer = (Vector3 *)malloc(sizeof(Vector3)*TEMP_VERTEX_BUFFER_SIZE); - for (int i = 0; i < TEMP_VERTEX_BUFFER_SIZE; i++) tempBuffer[i] = VectorZero(); + for (int i = 0; i < TEMP_VERTEX_BUFFER_SIZE; i++) tempBuffer[i] = Vector3Zero(); // Init draw calls tracking system draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE); @@ -1254,19 +1237,18 @@ void rlglInit(int width, int height) screenWidth = width; screenHeight = height; - TraceLog(INFO, "OpenGL default states initialized successfully"); + TraceLog(LOG_INFO, "OpenGL default states initialized successfully"); } // Vertex Buffer Object deinitialization (memory free) void rlglClose(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - UnloadDefaultShader(); - UnloadDefaultBuffers(); - - // Delete default white texture - glDeleteTextures(1, &whiteTexture); - TraceLog(INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); + UnloadShaderDefault(); // Unload default shader + UnloadBuffersDefault(); // Unload default buffers (lines, triangles, quads) + glDeleteTextures(1, &whiteTexture); // Unload default texture + + TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data (base white texture) from VRAM", whiteTexture); free(draws); #endif @@ -1277,29 +1259,47 @@ void rlglDraw(void) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // NOTE: In a future version, models could be stored in a stack... - //for (int i = 0; i < modelsCount; i++) rlglDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform); + //for (int i = 0; i < modelsCount; i++) rlDrawMesh(models[i]->mesh, models[i]->material, models[i]->transform); // NOTE: Default buffers upload and draw - UpdateDefaultBuffers(); - DrawDefaultBuffers(); // NOTE: Stereo rendering is checked inside + UpdateBuffersDefault(); + DrawBuffersDefault(); // NOTE: Stereo rendering is checked inside +#endif +} + +// Returns current OpenGL version +int rlGetVersion(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) + return OPENGL_11; +#elif defined(GRAPHICS_API_OPENGL_21) + #if defined(__APPLE__) + return OPENGL_33; // NOTE: Force OpenGL 3.3 on OSX + #else + return OPENGL_21; + #endif +#elif defined(GRAPHICS_API_OPENGL_33) + return OPENGL_33; +#elif defined(GRAPHICS_API_OPENGL_ES2) + return OPENGL_ES_20; #endif } // Load OpenGL extensions // NOTE: External loader function could be passed as a pointer -void rlglLoadExtensions(void *loader) +void rlLoadExtensions(void *loader) { #if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_33) // NOTE: glad is generated and contains only required OpenGL 3.3 Core extensions (and lower versions) #if !defined(__APPLE__) - if (!gladLoadGLLoader((GLADloadproc)loader)) TraceLog(WARNING, "GLAD: Cannot load OpenGL extensions"); - else TraceLog(INFO, "GLAD: OpenGL extensions loaded successfully"); + if (!gladLoadGLLoader((GLADloadproc)loader)) TraceLog(LOG_WARNING, "GLAD: Cannot load OpenGL extensions"); + else TraceLog(LOG_INFO, "GLAD: OpenGL extensions loaded successfully"); #if defined(GRAPHICS_API_OPENGL_21) - if (GLAD_GL_VERSION_2_1) TraceLog(INFO, "OpenGL 2.1 profile supported"); + if (GLAD_GL_VERSION_2_1) TraceLog(LOG_INFO, "OpenGL 2.1 profile supported"); #elif defined(GRAPHICS_API_OPENGL_33) - if(GLAD_GL_VERSION_3_3) TraceLog(INFO, "OpenGL 3.3 Core profile supported"); - else TraceLog(ERROR, "OpenGL 3.3 Core profile not supported"); + if(GLAD_GL_VERSION_3_3) TraceLog(LOG_INFO, "OpenGL 3.3 Core profile supported"); + else TraceLog(LOG_ERROR, "OpenGL 3.3 Core profile not supported"); #endif #endif @@ -1309,19 +1309,19 @@ void rlglLoadExtensions(void *loader) } // Get world coordinates from screen coordinates -Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) +Vector3 rlUnproject(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); + // Calculate unproject matrix (multiply view patrix by projection matrix) and invert it + Matrix matViewProj = MatrixMultiply(view, proj); + MatrixInvert(&matViewProj); // Create quaternion from source point Quaternion quat = { source.x, source.y, source.z, 1.0f }; // Multiply quat point by unproject matrix - QuaternionTransform(&quat, matProjView); + QuaternionTransform(&quat, matViewProj); // Normalized world points in vectors result.x = quat.x/quat.w; @@ -1332,7 +1332,7 @@ Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view) } // Convert image data to OpenGL texture (returns OpenGL valid Id) -unsigned int rlglLoadTexture(void *data, int width, int height, int format, int mipmapCount) +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount) { glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding @@ -1342,7 +1342,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int format, int #if defined(GRAPHICS_API_OPENGL_11) if (format >= COMPRESSED_DXT1_RGB) { - TraceLog(WARNING, "OpenGL 1.1 does not support GPU compressed texture formats"); + TraceLog(LOG_WARNING, "OpenGL 1.1 does not support GPU compressed texture formats"); return id; } #endif @@ -1350,31 +1350,31 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int format, int if ((!texCompDXTSupported) && ((format == COMPRESSED_DXT1_RGB) || (format == COMPRESSED_DXT1_RGBA) || (format == COMPRESSED_DXT3_RGBA) || (format == COMPRESSED_DXT5_RGBA))) { - TraceLog(WARNING, "DXT compressed texture format not supported"); + TraceLog(LOG_WARNING, "DXT compressed texture format not supported"); return id; } #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if ((!texCompETC1Supported) && (format == COMPRESSED_ETC1_RGB)) { - TraceLog(WARNING, "ETC1 compressed texture format not supported"); + TraceLog(LOG_WARNING, "ETC1 compressed texture format not supported"); return id; } if ((!texCompETC2Supported) && ((format == COMPRESSED_ETC2_RGB) || (format == COMPRESSED_ETC2_EAC_RGBA))) { - TraceLog(WARNING, "ETC2 compressed texture format not supported"); + TraceLog(LOG_WARNING, "ETC2 compressed texture format not supported"); return id; } if ((!texCompPVRTSupported) && ((format == COMPRESSED_PVRT_RGB) || (format == COMPRESSED_PVRT_RGBA))) { - TraceLog(WARNING, "PVRT compressed texture format not supported"); + TraceLog(LOG_WARNING, "PVRT compressed texture format not supported"); return id; } if ((!texCompASTCSupported) && ((format == COMPRESSED_ASTC_4x4_RGBA) || (format == COMPRESSED_ASTC_8x8_RGBA))) { - TraceLog(WARNING, "ASTC compressed texture format not supported"); + TraceLog(LOG_WARNING, "ASTC compressed texture format not supported"); return id; } #endif @@ -1411,7 +1411,7 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int format, int 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); + TraceLog(LOG_INFO, "[TEX ID %i] Grayscale texture loaded and swizzled", id); } break; case UNCOMPRESSED_GRAY_ALPHA: { @@ -1427,18 +1427,18 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int format, int case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; case UNCOMPRESSED_R32G32B32: if (texFloatSupported) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_FLOAT, (float *)data); break; - case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; - case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; - case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - default: TraceLog(WARNING, "Texture format not recognized"); break; + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, mipmapCount); break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, mipmapCount); break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, mipmapCount); break; + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, mipmapCount); break; + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) LoadTextureCompressed((unsigned char *)data, width, height, GL_ETC1_RGB8_OES, mipmapCount); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGB8_ETC2, mipmapCount); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA8_ETC2_EAC, mipmapCount); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, mipmapCount); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, mipmapCount); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_ASTC_4x4_KHR, mipmapCount); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_ASTC_8x8_KHR, mipmapCount); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + default: TraceLog(LOG_WARNING, "Texture format not recognized"); break; } #elif defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_ES2) // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA @@ -1452,20 +1452,20 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int format, int case UNCOMPRESSED_R4G4B4A4: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; case UNCOMPRESSED_R8G8B8A8: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; #if defined(GRAPHICS_API_OPENGL_ES2) - case UNCOMPRESSED_R32G32B32: if (texFloatSupported) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, (float *)data); break; // Requries extension OES_texture_float - case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); break; - case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); break; // NOTE: Not supported by WebGL - case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_ETC1_RGB8_OES); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 - case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB8_ETC2); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA8_ETC2_EAC); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 - case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); break; // NOTE: Requires PowerVR GPU - case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_4x4_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 - case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) LoadCompressedTexture((unsigned char *)data, width, height, mipmapCount, GL_COMPRESSED_RGBA_ASTC_8x8_KHR); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case UNCOMPRESSED_R32G32B32: if (texFloatSupported) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, (float *)data); break; // NOTE: Requires extension OES_texture_float + case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, mipmapCount); break; + case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, mipmapCount); break; + case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, mipmapCount); break; // NOTE: Not supported by WebGL + case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, mipmapCount); break; // NOTE: Not supported by WebGL + case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) LoadTextureCompressed((unsigned char *)data, width, height, GL_ETC1_RGB8_OES, mipmapCount); break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3 + case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGB8_ETC2, mipmapCount); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA8_ETC2_EAC, mipmapCount); break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3 + case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, mipmapCount); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, mipmapCount); break; // NOTE: Requires PowerVR GPU + case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_ASTC_4x4_KHR, mipmapCount); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 + case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) LoadTextureCompressed((unsigned char *)data, width, height, GL_COMPRESSED_RGBA_ASTC_8x8_KHR, mipmapCount); break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3 #endif - default: TraceLog(WARNING, "Texture format not supported"); break; + default: TraceLog(LOG_WARNING, "Texture format not supported"); break; } #endif @@ -1508,14 +1508,54 @@ unsigned int rlglLoadTexture(void *data, int width, int height, int format, int // Unbind current texture glBindTexture(GL_TEXTURE_2D, 0); - if (id > 0) TraceLog(INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height); - else TraceLog(WARNING, "Texture could not be created"); + if (id > 0) TraceLog(LOG_INFO, "[TEX ID %i] Texture created successfully (%ix%i)", id, width, height); + else TraceLog(LOG_WARNING, "Texture could not be created"); return id; } +// Update already loaded texture in GPU with new data +void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data) +{ + glBindTexture(GL_TEXTURE_2D, id); + +#if defined(GRAPHICS_API_OPENGL_33) + switch (format) + { + case UNCOMPRESSED_GRAYSCALE: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_GRAY_ALPHA: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G6B5: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G5B5A1: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; + case UNCOMPRESSED_R4G4B4A4: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8A8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + default: TraceLog(LOG_WARNING, "Texture format updating not supported"); break; + } +#elif defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA + switch (format) + { + case UNCOMPRESSED_GRAYSCALE: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_GRAY_ALPHA: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G6B5: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + case UNCOMPRESSED_R5G5B5A1: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; + case UNCOMPRESSED_R4G4B4A4: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; + case UNCOMPRESSED_R8G8B8A8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; + default: TraceLog(LOG_WARNING, "Texture format updating not supported"); break; + } +#endif +} + +// Unload texture from GPU memory +void rlUnloadTexture(unsigned int id) +{ + if (id > 0) glDeleteTextures(1, &id); +} + + // Load a texture to be used for rendering (fbo with color and depth attachments) -RenderTexture2D rlglLoadRenderTexture(int width, int height) +RenderTexture2D rlLoadRenderTexture(int width, int height) { RenderTexture2D target; @@ -1584,16 +1624,16 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height) if (status != GL_FRAMEBUFFER_COMPLETE) { - TraceLog(WARNING, "Framebuffer object could not be created..."); + TraceLog(LOG_WARNING, "Framebuffer object could not be created..."); switch (status) { - case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(WARNING, "Framebuffer is unsupported"); break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete attachment"); break; + case GL_FRAMEBUFFER_UNSUPPORTED: TraceLog(LOG_WARNING, "Framebuffer is unsupported"); break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: TraceLog(LOG_WARNING, "Framebuffer incomplete attachment"); break; #if defined(GRAPHICS_API_OPENGL_ES2) - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(WARNING, "Framebuffer incomplete dimensions"); break; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: TraceLog(LOG_WARNING, "Framebuffer incomplete dimensions"); break; #endif - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(WARNING, "Framebuffer incomplete missing attachment"); break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: TraceLog(LOG_WARNING, "Framebuffer incomplete missing attachment"); break; default: break; } @@ -1601,7 +1641,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height) glDeleteTextures(1, &target.depth.id); glDeleteFramebuffers(1, &target.id); } - else TraceLog(INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); + else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif @@ -1609,41 +1649,8 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height) return target; } -// Update already loaded texture in GPU with new data -void rlglUpdateTexture(unsigned int id, int width, int height, int format, const void *data) -{ - glBindTexture(GL_TEXTURE_2D, id); - -#if defined(GRAPHICS_API_OPENGL_33) - switch (format) - { - case UNCOMPRESSED_GRAYSCALE: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case UNCOMPRESSED_GRAY_ALPHA: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RG, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case UNCOMPRESSED_R5G6B5: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; - case UNCOMPRESSED_R8G8B8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case UNCOMPRESSED_R5G5B5A1: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; - case UNCOMPRESSED_R4G4B4A4: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; - case UNCOMPRESSED_R8G8B8A8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - default: TraceLog(WARNING, "Texture format updating not supported"); break; - } -#elif defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA - switch (format) - { - case UNCOMPRESSED_GRAYSCALE: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case UNCOMPRESSED_GRAY_ALPHA: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case UNCOMPRESSED_R5G6B5: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (unsigned short *)data); break; - case UNCOMPRESSED_R8G8B8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - case UNCOMPRESSED_R5G5B5A1: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, (unsigned short *)data); break; - case UNCOMPRESSED_R4G4B4A4: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, (unsigned short *)data); break; - case UNCOMPRESSED_R8G8B8A8: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data); break; - default: TraceLog(WARNING, "Texture format updating not supported"); break; - } -#endif -} - // Generate mipmap data for selected texture -void rlglGenerateMipmaps(Texture2D *texture) +void rlGenerateMipmaps(Texture2D *texture) { glBindTexture(GL_TEXTURE_2D, texture->id); @@ -1657,7 +1664,7 @@ void rlglGenerateMipmaps(Texture2D *texture) { #if defined(GRAPHICS_API_OPENGL_11) // Compute required mipmaps - void *data = rlglReadTexturePixels(*texture); + void *data = rlReadTexturePixels(*texture); // NOTE: data size is reallocated to fit mipmaps data // NOTE: CPU mipmap generation only supports RGBA 32bit data @@ -1681,7 +1688,7 @@ void rlglGenerateMipmaps(Texture2D *texture) mipHeight /= 2; } - TraceLog(WARNING, "[TEX ID %i] Mipmaps generated manually on CPU side", texture->id); + TraceLog(LOG_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); @@ -1692,7 +1699,7 @@ void rlglGenerateMipmaps(Texture2D *texture) #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(LOG_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 @@ -1703,13 +1710,14 @@ void rlglGenerateMipmaps(Texture2D *texture) 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(LOG_WARNING, "[TEX ID %i] Mipmaps can not be generated", texture->id); glBindTexture(GL_TEXTURE_2D, 0); } // Upload vertex data into a VAO (if supported) and VBO -void rlglLoadMesh(Mesh *mesh, bool dynamic) +// TODO: Check if mesh has already been loaded in GPU +void rlLoadMesh(Mesh *mesh, bool dynamic) { mesh->vaoId = 0; // Vertex Array Object mesh->vboId[0] = 0; // Vertex positions VBO @@ -1724,28 +1732,25 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) int drawHint = GL_STATIC_DRAW; if (dynamic) drawHint = GL_DYNAMIC_DRAW; - GLuint vaoId = 0; // Vertex Array Objects (VAO) - GLuint vboId[7] = { 0 }; // Vertex Buffer Objects (VBOs) - if (vaoSupported) { // Initialize Quads VAO (Buffer A) - glGenVertexArrays(1, &vaoId); - glBindVertexArray(vaoId); + glGenVertexArrays(1, &mesh->vaoId); + glBindVertexArray(mesh->vaoId); } // NOTE: Attributes must be uploaded considering default locations points // Enable vertex attributes: position (shader-location = 0) - glGenBuffers(1, &vboId[0]); - glBindBuffer(GL_ARRAY_BUFFER, vboId[0]); + glGenBuffers(1, &mesh->vboId[0]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->vertices, drawHint); glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(0); // Enable vertex attributes: texcoords (shader-location = 1) - glGenBuffers(1, &vboId[1]); - glBindBuffer(GL_ARRAY_BUFFER, vboId[1]); + glGenBuffers(1, &mesh->vboId[1]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords, drawHint); glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(1); @@ -1753,8 +1758,8 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) // Enable vertex attributes: normals (shader-location = 2) if (mesh->normals != NULL) { - glGenBuffers(1, &vboId[2]); - glBindBuffer(GL_ARRAY_BUFFER, vboId[2]); + glGenBuffers(1, &mesh->vboId[2]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->normals, drawHint); glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(2); @@ -1769,8 +1774,8 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) // Default color vertex attribute (shader-location = 3) if (mesh->colors != NULL) { - glGenBuffers(1, &vboId[3]); - glBindBuffer(GL_ARRAY_BUFFER, vboId[3]); + glGenBuffers(1, &mesh->vboId[3]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[3]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*mesh->vertexCount, mesh->colors, drawHint); glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); glEnableVertexAttribArray(3); @@ -1785,8 +1790,8 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) // Default tangent vertex attribute (shader-location = 4) if (mesh->tangents != NULL) { - glGenBuffers(1, &vboId[4]); - glBindBuffer(GL_ARRAY_BUFFER, vboId[4]); + glGenBuffers(1, &mesh->vboId[4]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[4]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh->vertexCount, mesh->tangents, drawHint); glVertexAttribPointer(4, 3, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(4); @@ -1801,8 +1806,8 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) // Default texcoord2 vertex attribute (shader-location = 5) if (mesh->texcoords2 != NULL) { - glGenBuffers(1, &vboId[5]); - glBindBuffer(GL_ARRAY_BUFFER, vboId[5]); + glGenBuffers(1, &mesh->vboId[5]); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId[5]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh->vertexCount, mesh->texcoords2, drawHint); glVertexAttribPointer(5, 2, GL_FLOAT, 0, 0, 0); glEnableVertexAttribArray(5); @@ -1816,37 +1821,25 @@ void rlglLoadMesh(Mesh *mesh, bool dynamic) if (mesh->indices != NULL) { - glGenBuffers(1, &vboId[6]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[6]); + glGenBuffers(1, &mesh->vboId[6]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vboId[6]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*mesh->triangleCount*3, mesh->indices, GL_STATIC_DRAW); } - mesh->vboId[0] = vboId[0]; // Vertex position VBO - mesh->vboId[1] = vboId[1]; // Texcoords VBO - mesh->vboId[2] = vboId[2]; // Normals VBO - mesh->vboId[3] = vboId[3]; // Colors VBO - mesh->vboId[4] = vboId[4]; // Tangents VBO - mesh->vboId[5] = vboId[5]; // Texcoords2 VBO - mesh->vboId[6] = vboId[6]; // Indices VBO - if (vaoSupported) { - if (vaoId > 0) - { - mesh->vaoId = vaoId; - TraceLog(INFO, "[VAO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); - } - else TraceLog(WARNING, "Mesh could not be uploaded to VRAM (GPU)"); + if (mesh->vaoId > 0) TraceLog(LOG_INFO, "[VAO ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); + else TraceLog(LOG_WARNING, "Mesh could not be uploaded to VRAM (GPU)"); } else { - TraceLog(INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)"); + TraceLog(LOG_INFO, "[VBOs] Mesh uploaded successfully to VRAM (GPU)"); } #endif } // Update vertex data on GPU (upload new data to one buffer) -void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex) +void rlUpdateMesh(Mesh mesh, int buffer, int numVertex) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Activate mesh VAO @@ -1908,11 +1901,11 @@ void rlglUpdateMesh(Mesh mesh, int buffer, int numVertex) } // Draw a 3d mesh with material and transform -void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) +void rlDrawMesh(Mesh mesh, Material material, Matrix transform) { #if defined(GRAPHICS_API_OPENGL_11) glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); + glBindTexture(GL_TEXTURE_2D, material.maps[MAP_DIFFUSE].texture.id); // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array @@ -1927,7 +1920,7 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) rlPushMatrix(); rlMultMatrixf(MatrixToFloat(transform)); - rlColor4ub(material.colDiffuse.r, material.colDiffuse.g, material.colDiffuse.b, material.colDiffuse.a); + rlColor4ub(material.maps[MAP_DIFFUSE].color.r, material.maps[MAP_DIFFUSE].color.g, material.maps[MAP_DIFFUSE].color.b, material.maps[MAP_DIFFUSE].color.a); if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, mesh.indices); else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); @@ -1943,16 +1936,30 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) #endif #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glUseProgram(material.shader.id); + // Bind shader program + glUseProgram(material.shader.id); + // Matrices and other values required by shader + //----------------------------------------------------- + // Calculate and send to shader model matrix (used by PBR shader) + SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_MODEL], transform); + // 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); + if (material.shader.locs[LOC_COLOR_DIFFUSE] != -1) + glUniform4f(material.shader.locs[LOC_COLOR_DIFFUSE], (float)material.maps[MAP_DIFFUSE].color.r/255, + (float)material.maps[MAP_DIFFUSE].color.g/255, + (float)material.maps[MAP_DIFFUSE].color.b/255, + (float)material.maps[MAP_DIFFUSE].color.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); + if (material.shader.locs[LOC_COLOR_SPECULAR] != -1) + glUniform4f(material.shader.locs[LOC_COLOR_SPECULAR], (float)material.maps[MAP_SPECULAR].color.r/255, + (float)material.maps[MAP_SPECULAR].color.g/255, + (float)material.maps[MAP_SPECULAR].color.b/255, + (float)material.maps[MAP_SPECULAR].color.a/255); + + if (material.shader.locs[LOC_MATRIX_VIEW] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_VIEW], modelview); + if (material.shader.locs[LOC_MATRIX_PROJECTION] != -1) SetShaderValueMatrix(material.shader, material.shader.locs[LOC_MATRIX_PROJECTION], projection); // At this point the modelview matrix just contains the view matrix (camera) // That's because Begin3dMode() sets it an no model-drawing function modifies it, all use rlPushMatrix() and rlPopMatrix() @@ -1961,118 +1968,80 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) // Calculate model-view matrix combining matModel and matView Matrix matModelView = MatrixMultiply(transform, matView); // Transform to camera-space coordinates + //----------------------------------------------------- - // If not using default shader, we check for some additional location points - // NOTE: This method is quite inefficient... it's a temporal solution while looking for a better one - if (material.shader.id != defaultShader.id) + // Bind active texture maps (if available) + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) { - // Check if model matrix is located in shader and upload value - int modelMatrixLoc = glGetUniformLocation(material.shader.id, "modelMatrix"); - if (modelMatrixLoc != -1) + if (material.maps[i].texture.id > 0) { - // Transpose and inverse model transformations matrix for fragment normal calculations - Matrix transInvTransform = transform; - MatrixTranspose(&transInvTransform); - MatrixInvert(&transInvTransform); + glActiveTexture(GL_TEXTURE0 + i); + if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, material.maps[i].texture.id); + else glBindTexture(GL_TEXTURE_2D, material.maps[i].texture.id); - // Send model transformations matrix to shader - glUniformMatrix4fv(modelMatrixLoc, 1, false, MatrixToFloat(transInvTransform)); + glUniform1i(material.shader.locs[LOC_MAP_DIFFUSE + i], i); } - - // Check if view direction is located in shader and upload value - // NOTE: View matrix values m8, m9 and m10 are view direction vector axis (target - position) - int viewDirLoc = glGetUniformLocation(material.shader.id, "viewDir"); - if (viewDirLoc != -1) glUniform3f(viewDirLoc, matView.m8, matView.m9, matView.m10); - - // Check if glossiness is located in shader and upload value - int glossinessLoc = glGetUniformLocation(material.shader.id, "glossiness"); - if (glossinessLoc != -1) glUniform1f(glossinessLoc, material.glossiness); - } - - // Set shader textures (diffuse, normal, specular) - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, material.texDiffuse.id); - glUniform1i(material.shader.mapTexture0Loc, 0); // Diffuse texture fits in active texture unit 0 - - if ((material.texNormal.id != 0) && (material.shader.mapTexture1Loc != -1)) - { - // Upload to shader specular map flag - glUniform1i(glGetUniformLocation(material.shader.id, "useNormal"), 1); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, material.texNormal.id); - glUniform1i(material.shader.mapTexture1Loc, 1); // Normal texture fits in active texture unit 1 } - if ((material.texSpecular.id != 0) && (material.shader.mapTexture2Loc != -1)) - { - // Upload to shader specular map flag - glUniform1i(glGetUniformLocation(material.shader.id, "useSpecular"), 1); - - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, material.texSpecular.id); - glUniform1i(material.shader.mapTexture2Loc, 2); // Specular texture fits in active texture unit 2 - } - - if (vaoSupported) - { - glBindVertexArray(mesh.vaoId); - } + // Bind vertex array objects (or VBOs) + if (vaoSupported) glBindVertexArray(mesh.vaoId); else { + // TODO: Simplify VBO binding into a for loop + // Bind mesh VBO data: vertex position (shader-location = 0) glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[0]); - glVertexAttribPointer(material.shader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.vertexLoc); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_POSITION]); // Bind mesh VBO data: vertex texcoords (shader-location = 1) glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[1]); - glVertexAttribPointer(material.shader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.texcoordLoc); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD01]); // Bind mesh VBO data: vertex normals (shader-location = 2, if available) - if (material.shader.normalLoc != -1) + if (material.shader.locs[LOC_VERTEX_NORMAL] != -1) { glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[2]); - glVertexAttribPointer(material.shader.normalLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.normalLoc); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_NORMAL], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_NORMAL]); } // Bind mesh VBO data: vertex colors (shader-location = 3, if available) - if (material.shader.colorLoc != -1) + if (material.shader.locs[LOC_VERTEX_COLOR] != -1) { if (mesh.vboId[3] != 0) { glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[3]); - glVertexAttribPointer(material.shader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(material.shader.colorLoc); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]); } else { // Set default value for unused attribute // NOTE: Required when using default shader and no VAO support - glVertexAttrib4f(material.shader.colorLoc, 1.0f, 1.0f, 1.0f, 1.0f); - glDisableVertexAttribArray(material.shader.colorLoc); + glVertexAttrib4f(material.shader.locs[LOC_VERTEX_COLOR], 1.0f, 1.0f, 1.0f, 1.0f); + glDisableVertexAttribArray(material.shader.locs[LOC_VERTEX_COLOR]); } } // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) - if (material.shader.tangentLoc != -1) + if (material.shader.locs[LOC_VERTEX_TANGENT] != -1) { glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[4]); - glVertexAttribPointer(material.shader.tangentLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.tangentLoc); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TANGENT], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TANGENT]); } // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) - if (material.shader.texcoord2Loc != -1) + if (material.shader.locs[LOC_VERTEX_TEXCOORD02] != -1) { glBindBuffer(GL_ARRAY_BUFFER, mesh.vboId[5]); - glVertexAttribPointer(material.shader.texcoord2Loc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(material.shader.texcoord2Loc); + glVertexAttribPointer(material.shader.locs[LOC_VERTEX_TEXCOORD02], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(material.shader.locs[LOC_VERTEX_TEXCOORD02]); } - if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); + if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.vboId[6]); } int eyesCount = 1; @@ -2091,45 +2060,41 @@ void rlglDrawMesh(Mesh mesh, Material material, Matrix transform) Matrix matMVP = MatrixMultiply(modelview, projection); // Transform to screen-space coordinates // Send combined model-view-projection matrix to shader - glUniformMatrix4fv(material.shader.mvpLoc, 1, false, MatrixToFloat(matMVP)); + glUniformMatrix4fv(material.shader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); // Draw call! if (mesh.indices != NULL) glDrawElements(GL_TRIANGLES, mesh.triangleCount*3, GL_UNSIGNED_SHORT, 0); // Indexed vertices draw else glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); } - - if (material.texNormal.id != 0) - { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - } - - if (material.texSpecular.id != 0) + + // Unbind all binded texture maps + for (int i = 0; i < MAX_MATERIAL_MAPS; i++) { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0 + i); // Set shader active texture + if ((i == MAP_IRRADIANCE) || (i == MAP_PREFILTER) || (i == MAP_CUBEMAP)) glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + else glBindTexture(GL_TEXTURE_2D, 0); // Unbind current active texture } - glActiveTexture(GL_TEXTURE0); // Set shader active texture to default 0 - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures - - if (vaoSupported) glBindVertexArray(0); // Unbind VAO + // Unind vertex array objects (or VBOs) + if (vaoSupported) glBindVertexArray(0); else { - glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBOs + glBindBuffer(GL_ARRAY_BUFFER, 0); if (mesh.indices != NULL) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - glUseProgram(0); // Unbind shader program + // Unbind shader program + glUseProgram(0); // Restore projection/modelview matrices + // NOTE: In stereo rendering matrices are being modified to fit every eye projection = matProjection; modelview = matView; #endif } // Unload mesh data from CPU and GPU -void rlglUnloadMesh(Mesh *mesh) +void rlUnloadMesh(Mesh *mesh) { if (mesh->vertices != NULL) free(mesh->vertices); if (mesh->texcoords != NULL) free(mesh->texcoords); @@ -2151,7 +2116,7 @@ void rlglUnloadMesh(Mesh *mesh) } // Read screen pixel data (color buffer) -unsigned char *rlglReadScreenPixels(int width, int height) +unsigned char *rlReadScreenPixels(int width, int height) { unsigned char *screenData = (unsigned char *)calloc(width*height*4, sizeof(unsigned char)); @@ -2182,7 +2147,7 @@ unsigned char *rlglReadScreenPixels(int width, int height) // Read texture pixel data // NOTE: glGetTexImage() is not available on OpenGL ES 2.0 // Texture2D width and height are required on OpenGL ES 2.0. There is no way to get it from texture id. -void *rlglReadTexturePixels(Texture2D texture) +void *rlReadTexturePixels(Texture2D texture) { void *pixels = NULL; @@ -2219,7 +2184,7 @@ void *rlglReadTexturePixels(Texture2D texture) case UNCOMPRESSED_R5G5B5A1: pixels = (unsigned short *)malloc(size); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_5_5_5_1; break; // 16 bpp (1 bit alpha) case UNCOMPRESSED_R4G4B4A4: pixels = (unsigned short *)malloc(size); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_4_4_4_4; break; // 16 bpp (4 bit alpha) 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; + default: TraceLog(LOG_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. @@ -2235,7 +2200,7 @@ void *rlglReadTexturePixels(Texture2D texture) #if defined(GRAPHICS_API_OPENGL_ES2) - RenderTexture2D fbo = rlglLoadRenderTexture(texture.width, texture.height); + RenderTexture2D fbo = rlLoadRenderTexture(texture.width, texture.height); // NOTE: Two possible Options: // 1 - Bind texture to color fbo attachment and glReadPixels() @@ -2306,7 +2271,7 @@ void *rlglReadTexturePixels(Texture2D texture) /* // TODO: Record draw calls to be processed in batch // NOTE: Global state must be kept -void rlglRecordDraw(void) +void rlRecordDraw(void) { // TODO: Before adding a new draw, check if anything changed from last stored draw #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -2328,7 +2293,7 @@ void rlglRecordDraw(void) //---------------------------------------------------------------------------------- // Get default internal texture (white texture) -Texture2D GetDefaultTexture(void) +Texture2D GetTextureDefault(void) { Texture2D texture; @@ -2341,6 +2306,17 @@ Texture2D GetDefaultTexture(void) return texture; } +// Get default shader +Shader GetShaderDefault(void) +{ +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) + return defaultShader; +#else + Shader shader = { 0 }; + return shader; +#endif +} + // Load text data from file // NOTE: text chars array should be freed manually char *LoadText(const char *fileName) @@ -2369,7 +2345,7 @@ char *LoadText(const char *fileName) fclose(textFile); } - else TraceLog(WARNING, "[%s] Text file could not be opened", fileName); + else TraceLog(LOG_WARNING, "[%s] Text file could not be opened", fileName); } return text; @@ -2389,8 +2365,8 @@ Shader LoadShader(char *vsFileName, char *fsFileName) { shader.id = LoadShaderProgram(vShaderStr, fShaderStr); - // After shader loading, we try to load default location names - if (shader.id != 0) LoadDefaultShaderLocations(&shader); + // After shader loading, we TRY to set default location names + if (shader.id > 0) SetShaderDefaultLocations(&shader); // Shader strings must be freed free(vShaderStr); @@ -2399,9 +2375,35 @@ Shader LoadShader(char *vsFileName, char *fsFileName) if (shader.id == 0) { - TraceLog(WARNING, "Custom shader could not be loaded"); + TraceLog(LOG_WARNING, "Custom shader could not be loaded"); shader = defaultShader; } + + + // Get available shader uniforms + // NOTE: This information is useful for debug... + int uniformCount = -1; + + glGetProgramiv(shader.id, GL_ACTIVE_UNIFORMS, &uniformCount); + + for(int i = 0; i < uniformCount; i++) + { + int namelen = -1; + int num = -1; + char name[256]; // Assume no variable names longer than 256 + GLenum type = GL_ZERO; + + // Get the name of the uniforms + glGetActiveUniform(shader.id, i,sizeof(name) - 1, &namelen, &num, &type, name); + + name[namelen] = 0; + + // Get the location of the named uniform + GLuint location = glGetUniformLocation(shader.id, name); + + TraceLog(LOG_DEBUG, "[SHDR ID %i] Active uniform [%s] set at location: %i", shader.id, name, location); + } + #endif return shader; @@ -2410,10 +2412,10 @@ Shader LoadShader(char *vsFileName, char *fsFileName) // Unload shader from GPU memory (VRAM) void UnloadShader(Shader shader) { - if (shader.id != 0) + if (shader.id > 0) { rlDeleteShader(shader.id); - TraceLog(INFO, "[SHDR ID %i] Unloaded shader program data", shader.id); + TraceLog(LOG_INFO, "[SHDR ID %i] Unloaded shader program data", shader.id); } } @@ -2437,17 +2439,6 @@ void EndShaderMode(void) #endif } -// Get default shader -Shader GetDefaultShader(void) -{ -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - return defaultShader; -#else - Shader shader = { 0 }; - return shader; -#endif -} - // Get shader uniform location int GetShaderLocation(Shader shader, const char *uniformName) { @@ -2455,7 +2446,8 @@ int GetShaderLocation(Shader shader, const char *uniformName) #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); + if (location == -1) TraceLog(LOG_WARNING, "[SHDR ID %i] Shader uniform [%s] COULD NOT BE FOUND", shader.id, uniformName); + else TraceLog(LOG_INFO, "[SHDR ID %i] Shader uniform [%s] set at location: %i", shader.id, uniformName, location); #endif return location; } @@ -2470,7 +2462,7 @@ void SetShaderValue(Shader shader, int uniformLoc, float *value, int size) else if (size == 2) glUniform2fv(uniformLoc, 1, value); // Shader uniform type: vec2 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"); + else TraceLog(LOG_WARNING, "Shader value float array size not supported"); //glUseProgram(0); // Avoid reseting current shader program, in case other uniforms are set #endif @@ -2486,7 +2478,7 @@ void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size) else if (size == 2) glUniform2iv(uniformLoc, 1, value); // Shader uniform type: ivec2 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"); + else TraceLog(LOG_WARNING, "Shader value int array size not supported"); //glUseProgram(0); #endif @@ -2520,6 +2512,292 @@ void SetMatrixModelview(Matrix view) #endif } +// Generate cubemap texture from HDR texture +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) +{ + Texture2D cubemap = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + + // Set up depth face culling and cubemap seamless + glDisable(GL_CULL_FACE); +#if defined(GRAPHICS_API_OPENGL_33) + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Flag not supported on OpenGL ES 2.0 +#endif + + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Set up cubemap to render and attach to framebuffer + // NOTE: faces are stored with 16 bit floating point values + glGenTextures(1, &cubemap.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + for (unsigned int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#if defined(GRAPHICS_API_OPENGL_33) + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Flag not supported on OpenGL ES 2.0 +#endif + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + //MatrixTranspose(&fboProjection); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Convert HDR equirectangular environment map to cubemap equivalent + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, skyHDR.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + // Note: don't forget to configure the viewport to the capture dimensions + glViewport(0, 0, size, size); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + for (unsigned int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap.id, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + //glEnable(GL_CULL_FACE); + + cubemap.width = size; + cubemap.height = size; +#endif + return cubemap; +} + +// Generate irradiance texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size) +{ + Texture2D irradiance = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Create an irradiance cubemap, and re-scale capture FBO to irradiance scale + glGenTextures(1, &irradiance.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance.id); + for (unsigned int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + //MatrixTranspose(&fboProjection); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Solve diffuse integral by convolution to create an irradiance cubemap + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + // Note: don't forget to configure the viewport to the capture dimensions + glViewport(0, 0, size, size); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + for (unsigned int i = 0; i < 6; i++) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradiance.id, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + + irradiance.width = size; + irradiance.height = size; +#endif + return irradiance; +} + +// Generate prefilter texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) +{ + Texture2D prefilter = { 0 }; + +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader + // Other locations should be setup externally in shader before calling the function + // TODO: Locations should be taken out of this function... too shader dependant... + int roughnessLoc = GetShaderLocation(shader, "roughness"); + + // Setup framebuffer + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); + + // Create a prefiltered HDR environment map + glGenTextures(1, &prefilter.id); + glBindTexture(GL_TEXTURE_CUBE_MAP, prefilter.id); + for (unsigned int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Generate mipmaps for the prefiltered HDR texture + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + + // Create projection (transposed) and different views for each face + Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); + //MatrixTranspose(&fboProjection); + Matrix fboViews[6] = { + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, 1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), + MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, 0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }) + }; + + // Prefilter HDR and store data into mipmap levels + glUseProgram(shader.id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_PROJECTION], fboProjection); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + #define MAX_MIPMAP_LEVELS 5 // Max number of prefilter texture mipmaps + + for (unsigned int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++) + { + // Resize framebuffer according to mip-level size. + unsigned int mipWidth = size*powf(0.5f, mip); + unsigned int mipHeight = size*powf(0.5f, mip); + + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); + glViewport(0, 0, mipWidth, mipHeight); + + float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1); + glUniform1f(roughnessLoc, roughness); + + for (unsigned int i = 0; i < 6; ++i) + { + SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilter.id, mip); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawCube(); + } + } + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + + prefilter.width = size; + prefilter.height = size; +#endif + return prefilter; +} + +// Generate BRDF texture using cubemap data +// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size) +{ + Texture2D brdf = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) + // Generate BRDF convolution texture + glGenTextures(1, &brdf.id); + glBindTexture(GL_TEXTURE_2D, brdf.id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, size, size, 0, GL_RG, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Render BRDF LUT into a quad using FBO + unsigned int fbo, rbo; + glGenFramebuffers(1, &fbo); + glGenRenderbuffers(1, &rbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdf.id, 0); + + glViewport(0, 0, size, size); + glUseProgram(shader.id); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GenDrawQuad(); + + // Unbind framebuffer and textures + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Reset viewport dimensions to default + glViewport(0, 0, screenWidth, screenHeight); + + brdf.width = size; + brdf.height = size; +#endif + return brdf; +} + // Begin blending mode (alpha, additive, multiplied) // NOTE: Only 3 blending modes supported, default blend mode is alpha void BeginBlendMode(int mode) @@ -2572,7 +2850,7 @@ void InitVrSimulator(int vrDevice) hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - TraceLog(INFO, "Initializing VR Simulator (Oculus Rift DK2)"); + TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift DK2)"); } else if ((vrDevice == HMD_DEFAULT_DEVICE) || (vrDevice == HMD_OCULUS_RIFT_CV1)) { @@ -2599,22 +2877,22 @@ void InitVrSimulator(int vrDevice) hmd.chromaAbCorrection[2] = 1.014f; // HMD chromatic aberration correction parameter 2 hmd.chromaAbCorrection[3] = 0.0f; // HMD chromatic aberration correction parameter 3 - TraceLog(INFO, "Initializing VR Simulator (Oculus Rift CV1)"); + TraceLog(LOG_INFO, "Initializing VR Simulator (Oculus Rift CV1)"); } else { - TraceLog(WARNING, "VR Simulator doesn't support selected device parameters,"); - TraceLog(WARNING, "using default VR Simulator parameters"); + TraceLog(LOG_WARNING, "VR Simulator doesn't support selected device parameters,"); + TraceLog(LOG_WARNING, "using default VR Simulator parameters"); } // Initialize framebuffer and textures for stereo rendering // NOTE: screen size should match HMD aspect ratio - vrConfig.stereoFbo = rlglLoadRenderTexture(screenWidth, screenHeight); + vrConfig.stereoFbo = rlLoadRenderTexture(screenWidth, screenHeight); #if defined(SUPPORT_DISTORTION_SHADER) // 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); + if (vrConfig.distortionShader.id > 0) SetShaderDefaultLocations(&vrConfig.distortionShader); #endif SetStereoConfig(hmd); @@ -2623,7 +2901,7 @@ void InitVrSimulator(int vrDevice) #endif #if defined(GRAPHICS_API_OPENGL_11) - TraceLog(WARNING, "VR Simulator not supported on OpenGL 1.1"); + TraceLog(LOG_WARNING, "VR Simulator not supported on OpenGL 1.1"); #endif } @@ -2664,7 +2942,6 @@ void ToggleVrMode(void) // Reset viewport and default projection-modelview matrices rlViewport(0, 0, screenWidth, screenHeight); projection = MatrixOrtho(0, screenWidth, screenHeight, 0, 0.0f, 1.0f); - MatrixTranspose(&projection); modelview = MatrixIdentity(); } else vrStereoRender = true; @@ -2727,7 +3004,7 @@ void EndVrDrawing(void) // Draw RenderTexture (stereoFbo) using distortion shader currentShader = vrConfig.distortionShader; #else - currentShader = GetDefaultShader(); + currentShader = GetShaderDefault(); #endif rlEnableTexture(vrConfig.stereoFbo.texture.id); @@ -2758,8 +3035,8 @@ void EndVrDrawing(void) rlDisableTexture(); // Update and draw render texture fbo with distortion to backbuffer - UpdateDefaultBuffers(); - DrawDefaultBuffers(); + UpdateBuffersDefault(); + DrawBuffersDefault(); // Restore defaultShader currentShader = defaultShader; @@ -2767,7 +3044,6 @@ void EndVrDrawing(void) // Reset viewport and default projection-modelview matrices rlViewport(0, 0, screenWidth, screenHeight); projection = MatrixOrtho(0, screenWidth, screenHeight, 0, 0.0f, 1.0f); - MatrixTranspose(&projection); modelview = MatrixIdentity(); rlDisableDepthTest(); @@ -2783,7 +3059,7 @@ void EndVrDrawing(void) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Convert image data to OpenGL texture (returns OpenGL valid Id) // NOTE: Expected compressed image data and POT image -static void LoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compressedFormat) +static void LoadTextureCompressed(unsigned char *data, int width, int height, int compressedFormat, int mipmapCount) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); @@ -2843,7 +3119,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade if (success != GL_TRUE) { - TraceLog(WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader); + TraceLog(LOG_WARNING, "[VSHDR ID %i] Failed to compile vertex shader...", vertexShader); int maxLength = 0; int length; @@ -2857,13 +3133,13 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade #endif glGetShaderInfoLog(vertexShader, maxLength, &length, log); - TraceLog(INFO, "%s", log); + TraceLog(LOG_INFO, "%s", log); #ifdef _MSC_VER free(log); #endif } - else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); + else TraceLog(LOG_INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); glCompileShader(fragmentShader); @@ -2871,7 +3147,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade if (success != GL_TRUE) { - TraceLog(WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader); + TraceLog(LOG_WARNING, "[FSHDR ID %i] Failed to compile fragment shader...", fragmentShader); int maxLength = 0; int length; @@ -2885,13 +3161,13 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade #endif glGetShaderInfoLog(fragmentShader, maxLength, &length, log); - TraceLog(INFO, "%s", log); + TraceLog(LOG_INFO, "%s", log); #ifdef _MSC_VER free(log); #endif } - else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); + else TraceLog(LOG_INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); program = glCreateProgram(); @@ -2916,7 +3192,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade if (success == GL_FALSE) { - TraceLog(WARNING, "[SHDR ID %i] Failed to link shader program...", program); + TraceLog(LOG_WARNING, "[SHDR ID %i] Failed to link shader program...", program); int maxLength = 0; int length; @@ -2930,7 +3206,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade #endif glGetProgramInfoLog(program, maxLength, &length, log); - TraceLog(INFO, "%s", log); + TraceLog(LOG_INFO, "%s", log); #ifdef _MSC_VER free(log); @@ -2939,7 +3215,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade program = 0; } - else TraceLog(INFO, "[SHDR ID %i] Shader program loaded successfully", program); + else TraceLog(LOG_INFO, "[SHDR ID %i] Shader program loaded successfully", program); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); @@ -2950,7 +3226,7 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade // Load default shader (just vertex positioning and texture coloring) // NOTE: This shader program is used for batch buffers (lines, triangles, quads) -static Shader LoadDefaultShader(void) +static Shader LoadShaderDefault(void) { Shader shader; @@ -2975,12 +3251,12 @@ static Shader LoadDefaultShader(void) "out vec2 fragTexCoord; \n" "out vec4 fragColor; \n" #endif - "uniform mat4 mvpMatrix; \n" + "uniform mat4 mvp; \n" "void main() \n" "{ \n" " fragTexCoord = vertexTexCoord; \n" " fragColor = vertexColor; \n" - " gl_Position = mvpMatrix*vec4(vertexPosition, 1.0); \n" + " gl_Position = mvp*vec4(vertexPosition, 1.0); \n" "} \n"; // Fragment shader directly defined, no external file required @@ -3015,17 +3291,29 @@ static Shader LoadDefaultShader(void) shader.id = LoadShaderProgram(vDefaultShaderStr, fDefaultShaderStr); - if (shader.id != 0) TraceLog(INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); - else TraceLog(WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); - - if (shader.id != 0) LoadDefaultShaderLocations(&shader); + if (shader.id > 0) + { + TraceLog(LOG_INFO, "[SHDR ID %i] Default shader loaded successfully", shader.id); + + // Set default shader locations + // Get handles to GLSL input attibute locations + shader.locs[LOC_VERTEX_POSITION] = glGetAttribLocation(shader.id, "vertexPosition"); + shader.locs[LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader.id, "vertexTexCoord"); + shader.locs[LOC_VERTEX_COLOR] = glGetAttribLocation(shader.id, "vertexColor"); + + // Get handles to GLSL uniform locations + shader.locs[LOC_MATRIX_MVP] = glGetUniformLocation(shader.id, "mvp"); + shader.locs[LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader.id, "colDiffuse"); + shader.locs[LOC_MAP_DIFFUSE] = glGetUniformLocation(shader.id, "texture0"); + } + else TraceLog(LOG_WARNING, "[SHDR ID %i] Default shader could not be loaded", shader.id); 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) +static void SetShaderDefaultLocations(Shader *shader) { // NOTE: Default shader attrib locations have been fixed before linking: // vertex position location = 0 @@ -3036,30 +3324,29 @@ static void LoadDefaultShaderLocations(Shader *shader) // vertex texcoord2 location = 5 // Get handles to GLSL input attibute locations - shader->vertexLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME); - shader->texcoordLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME); - shader->texcoord2Loc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD2_NAME); - shader->normalLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_NORMAL_NAME); - shader->tangentLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TANGENT_NAME); - shader->colorLoc = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_COLOR_NAME); + shader->locs[LOC_VERTEX_POSITION] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_POSITION_NAME); + shader->locs[LOC_VERTEX_TEXCOORD01] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD_NAME); + shader->locs[LOC_VERTEX_TEXCOORD02] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TEXCOORD2_NAME); + shader->locs[LOC_VERTEX_NORMAL] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_NORMAL_NAME); + shader->locs[LOC_VERTEX_TANGENT] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_TANGENT_NAME); + shader->locs[LOC_VERTEX_COLOR] = glGetAttribLocation(shader->id, DEFAULT_ATTRIB_COLOR_NAME); // Get handles to GLSL uniform locations (vertex shader) - shader->mvpLoc = glGetUniformLocation(shader->id, "mvpMatrix"); + shader->locs[LOC_MATRIX_MVP] = glGetUniformLocation(shader->id, "mvp"); + shader->locs[LOC_MATRIX_PROJECTION] = glGetUniformLocation(shader->id, "projection"); + shader->locs[LOC_MATRIX_VIEW] = glGetUniformLocation(shader->id, "view"); // Get handles to GLSL uniform locations (fragment 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"); + shader->locs[LOC_COLOR_DIFFUSE] = glGetUniformLocation(shader->id, "colDiffuse"); + shader->locs[LOC_MAP_DIFFUSE] = glGetUniformLocation(shader->id, "texture0"); + shader->locs[LOC_MAP_NORMAL] = glGetUniformLocation(shader->id, "texture1"); + shader->locs[LOC_MAP_SPECULAR] = glGetUniformLocation(shader->id, "texture2"); // TODO: Try to find all expected/recognized shader locations (predefined names, must be documented) } // Unload default shader -static void UnloadDefaultShader(void) +static void UnloadShaderDefault(void) { glUseProgram(0); @@ -3071,7 +3358,7 @@ static void UnloadDefaultShader(void) } // Load default internal buffers (lines, triangles, quads) -static void LoadDefaultBuffers(void) +static void LoadBuffersDefault(void) { // [CPU] Allocate and initialize float array buffers to store vertex data (lines, triangles, quads) //-------------------------------------------------------------------------------------------- @@ -3135,7 +3422,7 @@ static void LoadDefaultBuffers(void) quads.tcCounter = 0; quads.cCounter = 0; - TraceLog(INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); + TraceLog(LOG_INFO, "[CPU] Default buffers initialized successfully (lines, triangles, quads)"); //-------------------------------------------------------------------------------------------- // [GPU] Upload vertex data and initialize VAOs/VBOs (lines, triangles, quads) @@ -3155,18 +3442,18 @@ static void LoadDefaultBuffers(void) glGenBuffers(2, &lines.vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*2*MAX_LINES_BATCH, lines.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.vertexLoc); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); // Vertex color buffer (shader-location = 3) glGenBuffers(2, &lines.vboId[1]); glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.colorLoc); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", lines.vaoId); - else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", lines.vboId[0], lines.vboId[1]); + if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (lines)", lines.vaoId); + else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (lines)", lines.vboId[0], lines.vboId[1]); // Upload and link triangles vertex buffers if (vaoSupported) @@ -3181,18 +3468,18 @@ static void LoadDefaultBuffers(void) glGenBuffers(1, &triangles.vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*3*MAX_TRIANGLES_BATCH, triangles.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.vertexLoc); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); // Vertex color buffer (shader-location = 3) glGenBuffers(1, &triangles.vboId[1]); glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.colorLoc); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", triangles.vaoId); - else TraceLog(INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (triangles)", triangles.vboId[0], triangles.vboId[1]); + if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (triangles)", triangles.vaoId); + else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (triangles)", triangles.vboId[0], triangles.vboId[1]); // Upload and link quads vertex buffers if (vaoSupported) @@ -3207,22 +3494,22 @@ static void LoadDefaultBuffers(void) glGenBuffers(1, &quads.vboId[0]); glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*4*MAX_QUADS_BATCH, quads.vertices, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.vertexLoc); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); // Vertex texcoord buffer (shader-location = 1) glGenBuffers(1, &quads.vboId[1]); glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*4*MAX_QUADS_BATCH, quads.texcoords, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.texcoordLoc); - glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); // Vertex color buffer (shader-location = 3) glGenBuffers(1, &quads.vboId[2]); glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); glBufferData(GL_ARRAY_BUFFER, sizeof(unsigned char)*4*4*MAX_QUADS_BATCH, quads.colors, GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(currentShader.colorLoc); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); // Fill index buffer glGenBuffers(1, &quads.vboId[3]); @@ -3233,8 +3520,8 @@ static void LoadDefaultBuffers(void) glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(short)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW); #endif - if (vaoSupported) TraceLog(INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", quads.vaoId); - else TraceLog(INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quads.vboId[0], quads.vboId[1], quads.vboId[2], quads.vboId[3]); + if (vaoSupported) TraceLog(LOG_INFO, "[VAO ID %i] Default buffers VAO initialized successfully (quads)", quads.vaoId); + else TraceLog(LOG_INFO, "[VBO ID %i][VBO ID %i][VBO ID %i][VBO ID %i] Default buffers VBOs initialized successfully (quads)", quads.vboId[0], quads.vboId[1], quads.vboId[2], quads.vboId[3]); // Unbind the current VAO if (vaoSupported) glBindVertexArray(0); @@ -3244,7 +3531,7 @@ static void LoadDefaultBuffers(void) // Update default internal buffers (VAOs/VBOs) with vertex array data // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) -static void UpdateDefaultBuffers(void) +static void UpdateBuffersDefault(void) { // Update lines vertex buffers if (lines.vCounter > 0) @@ -3314,7 +3601,7 @@ static void UpdateDefaultBuffers(void) // Draw default internal buffers vertex data // NOTE: We draw in this order: lines, triangles, quads -static void DrawDefaultBuffers() +static void DrawBuffersDefault(void) { Matrix matProjection = projection; Matrix matModelView = modelview; @@ -3338,9 +3625,9 @@ static void DrawDefaultBuffers() // 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); + glUniformMatrix4fv(currentShader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(matMVP)); + glUniform4f(currentShader.locs[LOC_COLOR_DIFFUSE], 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1i(currentShader.locs[LOC_MAP_DIFFUSE], 0); // NOTE: Additional map textures not considered for default buffers drawing } @@ -3348,6 +3635,7 @@ static void DrawDefaultBuffers() // Draw lines buffers if (lines.vCounter > 0) { + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, whiteTexture); if (vaoSupported) @@ -3358,13 +3646,13 @@ static void DrawDefaultBuffers() { // Bind vertex attrib: position (shader-location = 0) glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[0]); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.vertexLoc); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); // Bind vertex attrib: color (shader-location = 3) glBindBuffer(GL_ARRAY_BUFFER, lines.vboId[1]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); } glDrawArrays(GL_LINES, 0, lines.vCounter); @@ -3376,6 +3664,7 @@ static void DrawDefaultBuffers() // Draw triangles buffers if (triangles.vCounter > 0) { + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, whiteTexture); if (vaoSupported) @@ -3386,13 +3675,13 @@ static void DrawDefaultBuffers() { // Bind vertex attrib: position (shader-location = 0) glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[0]); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.vertexLoc); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); // Bind vertex attrib: color (shader-location = 3) glBindBuffer(GL_ARRAY_BUFFER, triangles.vboId[1]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); } glDrawArrays(GL_TRIANGLES, 0, triangles.vCounter); @@ -3416,31 +3705,32 @@ static void DrawDefaultBuffers() { // Bind vertex attrib: position (shader-location = 0) glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[0]); - glVertexAttribPointer(currentShader.vertexLoc, 3, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.vertexLoc); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_POSITION], 3, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_POSITION]); // Bind vertex attrib: texcoord (shader-location = 1) glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[1]); - glVertexAttribPointer(currentShader.texcoordLoc, 2, GL_FLOAT, 0, 0, 0); - glEnableVertexAttribArray(currentShader.texcoordLoc); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_TEXCOORD01], 2, GL_FLOAT, 0, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_TEXCOORD01]); // Bind vertex attrib: color (shader-location = 3) glBindBuffer(GL_ARRAY_BUFFER, quads.vboId[2]); - glVertexAttribPointer(currentShader.colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); - glEnableVertexAttribArray(currentShader.colorLoc); + glVertexAttribPointer(currentShader.locs[LOC_VERTEX_COLOR], 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0); + glEnableVertexAttribArray(currentShader.locs[LOC_VERTEX_COLOR]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quads.vboId[3]); } - //TraceLog(DEBUG, "Draws required per frame: %i", drawsCounter); + //TraceLog(LOG_DEBUG, "Draws required per frame: %i", drawsCounter); for (int i = 0; i < drawsCounter; i++) { quadsCount = draws[i].vertexCount/4; numIndicesToProcess = quadsCount*6; // Get number of Quads*6 index by Quad - //TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount); + //TraceLog(LOG_DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, draws[i].textureId); // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process @@ -3450,7 +3740,7 @@ static void DrawDefaultBuffers() glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_SHORT, (GLvoid *)(sizeof(GLushort)*indicesOffset)); #endif //GLenum err; - //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! + //if ((err = glGetError()) != GL_NO_ERROR) TraceLog(LOG_INFO, "OpenGL error: %i", (int)err); //GL_INVALID_ENUM! indicesOffset += draws[i].vertexCount/4*6; } @@ -3461,10 +3751,10 @@ static void DrawDefaultBuffers() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures + glBindTexture(GL_TEXTURE_2D, 0); // Unbind textures } - if (vaoSupported) glBindVertexArray(0); // Unbind VAO + if (vaoSupported) glBindVertexArray(0); // Unbind VAO glUseProgram(0); // Unbind shader program } @@ -3492,7 +3782,7 @@ static void DrawDefaultBuffers() } // Unload default internal buffers vertex data from CPU and GPU -static void UnloadDefaultBuffers(void) +static void UnloadBuffersDefault(void) { // Unbind everything if (vaoSupported) glBindVertexArray(0); @@ -3534,6 +3824,117 @@ static void UnloadDefaultBuffers(void) free(quads.indices); } +// Renders a 1x1 XY quad in NDC +static void GenDrawQuad(void) +{ + unsigned int quadVAO = 0; + unsigned int quadVBO = 0; + + float vertices[] = { + // Positions // Texture Coords + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + }; + + // Set up plane VAO + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW); + + // Link vertex attributes + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)(3*sizeof(float))); + + // Draw quad + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + glDeleteBuffers(1, &quadVBO); + glDeleteVertexArrays(1, &quadVAO); +} + +// Renders a 1x1 3D cube in NDC +static void GenDrawCube(void) +{ + unsigned int cubeVAO = 0; + unsigned int cubeVBO = 0; + + float vertices[] = { + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f + }; + + // Set up cube VAO + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &cubeVBO); + + // Fill buffer + glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + // Link vertex attributes + glBindVertexArray(cubeVAO); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(3*sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *)(6*sizeof(float))); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + // Draw cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + + glDeleteBuffers(1, &cubeVBO); + glDeleteVertexArrays(1, &cubeVAO); +} + #if defined(SUPPORT_VR_SIMULATOR) // Configure stereo rendering (including distortion shader) with HMD device parameters static void SetStereoConfig(VrDeviceInfo hmd) @@ -3557,17 +3958,17 @@ static void SetStereoConfig(VrDeviceInfo hmd) hmd.distortionK[2]*lensRadiusSq*lensRadiusSq + hmd.distortionK[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq; - TraceLog(DEBUG, "VR: Distortion Scale: %f", distortionScale); + TraceLog(LOG_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]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: Scale = { %f, %f }", scale[0], scale[1]); + TraceLog(LOG_DEBUG, "VR: Distortion Shader: ScaleIn = { %f, %f }", scaleIn[0], scaleIn[1]); #if defined(SUPPORT_DISTORTION_SHADER) // Update distortion shader with lens and distortion-scale parameters @@ -3582,10 +3983,10 @@ static void SetStereoConfig(VrDeviceInfo hmd) SetShaderValue(vrConfig.distortionShader, GetShaderLocation(vrConfig.distortionShader, "chromaAbParam"), hmd.chromaAbCorrection, 4); #endif - // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance)*RAD2DEG + // Fovy is normally computed with: 2*atan2(hmd.vScreenSize, 2*hmd.eyeToScreenDistance) // ...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; + //float fovy = 2.0f*atan2(hmd.vScreenSize*0.5f*distortionScale, hmd.eyeToScreenDistance); // Really need distortionScale? + float fovy = 2.0f*(float)atan2(hmd.vScreenSize*0.5f, hmd.eyeToScreenDistance); // Compute camera projection matrices float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1] @@ -3593,10 +3994,6 @@ static void SetStereoConfig(VrDeviceInfo hmd) 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 @@ -3645,20 +4042,20 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) if (width != 1) width /= 2; if (height != 1) height /= 2; - TraceLog(DEBUG, "Next mipmap size: %i x %i", width, height); + TraceLog(LOG_DEBUG, "Next mipmap size: %i x %i", width, height); mipmapCount++; size += (width*height*4); // Add mipmap size (in bytes) } - TraceLog(DEBUG, "Total mipmaps required: %i", mipmapCount); - TraceLog(DEBUG, "Total size of data required: %i", size); + TraceLog(LOG_DEBUG, "Total mipmaps required: %i", mipmapCount); + TraceLog(LOG_DEBUG, "Total size of data required: %i", size); unsigned char *temp = realloc(data, size); if (temp != NULL) data = temp; - else TraceLog(WARNING, "Mipmaps required memory could not be allocated"); + else TraceLog(LOG_WARNING, "Mipmaps required memory could not be allocated"); width = baseWidth; height = baseHeight; @@ -3680,7 +4077,7 @@ static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight) j++; } - TraceLog(DEBUG, "Mipmap base (%ix%i)", width, height); + TraceLog(LOG_DEBUG, "Mipmap base (%ix%i)", width, height); for (int mip = 1; mip < mipmapCount; mip++) { @@ -3751,15 +4148,14 @@ static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight) } } - TraceLog(DEBUG, "Mipmap generated successfully (%ix%i)", width, height); + TraceLog(LOG_DEBUG, "Mipmap generated successfully (%ix%i)", width, height); return mipmap; } #endif #if defined(RLGL_STANDALONE) -// Output a trace log message -// NOTE: Expected msgType: (0)Info, (1)Error, (2)Warning +// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) void TraceLog(int msgType, const char *text, ...) { va_list args; @@ -3767,10 +4163,10 @@ void TraceLog(int msgType, const char *text, ...) switch (msgType) { - case INFO: fprintf(stdout, "INFO: "); break; - case ERROR: fprintf(stdout, "ERROR: "); break; - case WARNING: fprintf(stdout, "WARNING: "); break; - case DEBUG: fprintf(stdout, "DEBUG: "); break; + case LOG_INFO: fprintf(stdout, "INFO: "); break; + case LOG_ERROR: fprintf(stdout, "ERROR: "); break; + case LOG_WARNING: fprintf(stdout, "WARNING: "); break; + case LOG_DEBUG: fprintf(stdout, "DEBUG: "); break; default: break; } @@ -3779,34 +4175,6 @@ void TraceLog(int msgType, const char *text, ...) va_end(args); - if (msgType == ERROR) exit(1); -} - -// Converts Matrix to float array -// NOTE: Returned vector is a transposed version of the Matrix struct, -// it should be this way because, despite raymath use OpenGL column-major convention, -// Matrix struct memory alignment and variables naming are not coherent -float *MatrixToFloat(Matrix mat) -{ - static float buffer[16]; - - buffer[0] = mat.m0; - buffer[1] = mat.m4; - buffer[2] = mat.m8; - buffer[3] = mat.m12; - buffer[4] = mat.m1; - buffer[5] = mat.m5; - buffer[6] = mat.m9; - buffer[7] = mat.m13; - buffer[8] = mat.m2; - buffer[9] = mat.m6; - buffer[10] = mat.m10; - buffer[11] = mat.m14; - buffer[12] = mat.m3; - buffer[13] = mat.m7; - buffer[14] = mat.m11; - buffer[15] = mat.m15; - - return buffer; + if (msgType == LOG_ERROR) exit(1); } #endif @@ -145,6 +145,56 @@ typedef unsigned char byte; // Boolean type typedef enum { false, true } bool; #endif + + // Shader location point type + typedef enum { + LOC_VERTEX_POSITION = 0, + LOC_VERTEX_TEXCOORD01, + LOC_VERTEX_TEXCOORD02, + LOC_VERTEX_NORMAL, + LOC_VERTEX_TANGENT, + LOC_VERTEX_COLOR, + LOC_MATRIX_MVP, + LOC_MATRIX_MODEL, + LOC_MATRIX_VIEW, + LOC_MATRIX_PROJECTION, + LOC_VECTOR_VIEW, + LOC_COLOR_DIFFUSE, + LOC_COLOR_SPECULAR, + LOC_COLOR_AMBIENT, + LOC_MAP_ALBEDO, // LOC_MAP_DIFFUSE + LOC_MAP_METALNESS, // LOC_MAP_SPECULAR + LOC_MAP_NORMAL, + LOC_MAP_ROUGHNESS, + LOC_MAP_OCCUSION, + LOC_MAP_EMISSION, + LOC_MAP_HEIGHT, + LOC_MAP_CUBEMAP, + LOC_MAP_IRRADIANCE, + LOC_MAP_PREFILTER, + LOC_MAP_BRDF + } ShaderLocationIndex; + + #define LOC_MAP_DIFFUSE LOC_MAP_ALBEDO + #define LOC_MAP_SPECULAR LOC_MAP_METALNESS + + // Material map type + typedef enum { + MAP_ALBEDO = 0, // MAP_DIFFUSE + MAP_METALNESS = 1, // MAP_SPECULAR + MAP_NORMAL = 2, + MAP_ROUGHNESS = 3, + MAP_OCCLUSION, + MAP_EMISSION, + MAP_HEIGHT, + MAP_CUBEMAP, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP + MAP_BRDF + } TexmapIndex; + + #define MAP_DIFFUSE MAP_ALBEDO + #define MAP_SPECULAR MAP_METALNESS // Color type, RGBA (32bit) typedef struct Color { @@ -186,44 +236,29 @@ typedef unsigned char byte; unsigned int vaoId; // OpenGL Vertex Array Object id unsigned int vboId[7]; // OpenGL Vertex Buffer Objects id (7 types of vertex data) } Mesh; - - // Shader type (generic shader) + + // Shader and material limits + #define MAX_SHADER_LOCATIONS 32 + #define MAX_MATERIAL_MAPS 12 + + // Shader type (generic) typedef struct Shader { - unsigned int id; // Shader program id - - // Vertex attributes locations (default locations) - int vertexLoc; // Vertex attribute location point (default-location = 0) - int texcoordLoc; // Texcoord attribute location point (default-location = 1) - int normalLoc; // Normal attribute location point (default-location = 2) - int colorLoc; // Color attibute location point (default-location = 3) - int tangentLoc; // Tangent attribute location point (default-location = 4) - int texcoord2Loc; // Texcoord2 attribute location point (default-location = 5) - - // Uniform locations - int mvpLoc; // ModelView-Projection matrix uniform location point (vertex shader) - int colDiffuseLoc; // Color uniform location point (fragment shader) - int colAmbientLoc; // Ambient color uniform location point (fragment shader) - int colSpecularLoc; // Specular color uniform location point (fragment shader) - - // Texture map locations (generic for any kind of map) - int mapTexture0Loc; // Map texture uniform location point (default-texture-unit = 0) - int mapTexture1Loc; // Map texture uniform location point (default-texture-unit = 1) - int mapTexture2Loc; // Map texture uniform location point (default-texture-unit = 2) + unsigned int id; // Shader program id + int locs[MAX_SHADER_LOCATIONS]; // Shader locations array } Shader; - // Material type - typedef struct Material { - Shader shader; // Standard shader (supports 3 map types: diffuse, normal, specular) - - Texture2D texDiffuse; // Diffuse texture - Texture2D texNormal; // Normal texture - Texture2D texSpecular; // Specular texture + // Material texture map + typedef struct MaterialMap { + Texture2D texture; // Material map texture + Color color; // Material map color + float value; // Material map value + } MaterialMap; - Color colDiffuse; // Diffuse color - Color colAmbient; // Ambient color - Color colSpecular; // Specular color - - float glossiness; // Glossiness level (Ranges from 0 to 1000) + // Material type (generic) + typedef struct Material { + Shader shader; // Material shader + MaterialMap maps[MAX_MATERIAL_MAPS]; // Material maps + float *params; // Material generic parameters (if required) } Material; // Camera type, defines a camera position/orientation in 3d space @@ -236,11 +271,11 @@ typedef unsigned char byte; // TraceLog message types typedef enum { - INFO = 0, - ERROR, - WARNING, - DEBUG, - OTHER + LOG_INFO = 0, + LOG_ERROR, + LOG_WARNING, + LOG_DEBUG, + LOG_OTHER } TraceLogType; // Texture formats (support depends on OpenGL version) @@ -320,7 +355,7 @@ void rlLoadIdentity(void); // Reset current matrix to ident void rlTranslatef(float x, float y, float z); // Multiply the current matrix by a translation matrix void rlRotatef(float angleDeg, float x, float y, float z); // Multiply the current matrix by a rotation matrix void rlScalef(float x, float y, float z); // Multiply the current matrix by a scaling matrix -void rlMultMatrixf(float *mat); // Multiply the current matrix by another matrix +void rlMultMatrixf(float *matf); // Multiply the current matrix by another matrix void rlFrustum(double left, double right, double bottom, double top, double near, double far); void rlOrtho(double left, double right, double bottom, double top, double near, double far); void rlViewport(int x, int y, int width, int height); // Set the viewport area @@ -343,50 +378,48 @@ void rlColor4f(float x, float y, float z, float w); // Define one vertex (color) // Functions Declaration - OpenGL equivalent functions (common to 1.1, 3.3+, ES2) // NOTE: This functions are used to completely abstract raylib code from OpenGL layer //------------------------------------------------------------------------------------ -void rlEnableTexture(unsigned int id); // Enable texture usage -void rlDisableTexture(void); // Disable texture usage +void rlEnableTexture(unsigned int id); // Enable texture usage +void rlDisableTexture(void); // Disable texture usage void rlTextureParameters(unsigned int id, int param, int value); // Set texture parameters (filter, wrap) -void rlEnableRenderTexture(unsigned int id); // Enable render texture (fbo) -void rlDisableRenderTexture(void); // Disable render texture (fbo), return to default framebuffer -void rlEnableDepthTest(void); // Enable depth test -void rlDisableDepthTest(void); // Disable depth test -void rlEnableWireMode(void); // Enable wire mode -void rlDisableWireMode(void); // Disable wire mode -void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU +void rlEnableRenderTexture(unsigned int id); // Enable render texture (fbo) +void rlDisableRenderTexture(void); // Disable render texture (fbo), return to default framebuffer +void rlEnableDepthTest(void); // Enable depth test +void rlDisableDepthTest(void); // Disable depth test +void rlEnableWireMode(void); // Enable wire mode +void rlDisableWireMode(void); // Disable wire mode +void rlDeleteTextures(unsigned int id); // Delete OpenGL texture from GPU void rlDeleteRenderTextures(RenderTexture2D target); // Delete render textures (fbo) from GPU -void rlDeleteShader(unsigned int id); // Delete OpenGL shader program from GPU -void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (VAO) from GPU memory -void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory -void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color -void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) -int rlGetVersion(void); // Returns current OpenGL version +void rlDeleteShader(unsigned int id); // Delete OpenGL shader program from GPU +void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (VAO) from GPU memory +void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory +void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color +void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ void rlglInit(int width, int height); // Initialize rlgl (buffers, shaders, textures, states) -void rlglClose(void); // De-init rlgl -void rlglDraw(void); // Draw VAO/VBO -void rlglLoadExtensions(void *loader); // Load OpenGL extensions - -unsigned int rlglLoadTexture(void *data, int width, int height, int format, 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, 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) -void rlglDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform -void rlglUnloadMesh(Mesh *mesh); // Unload mesh data from CPU and GPU - -Vector3 rlglUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates - -unsigned char *rlglReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) -void *rlglReadTexturePixels(Texture2D texture); // Read texture pixel data +void rlglClose(void); // De-inititialize rlgl (buffers, shaders, textures) +void rlglDraw(void); // Update and Draw default buffers (lines, triangles, quads) -// VR functions exposed to core module but not to raylib users -void BeginVrDrawing(void); // Begin VR drawing configuration -void EndVrDrawing(void); // End VR drawing process (and desktop mirror) +int rlGetVersion(void); // Returns current OpenGL version +void rlLoadExtensions(void *loader); // Load OpenGL extensions +Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates + +// Textures data management +unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU +void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data +void rlUnloadTexture(unsigned int id); +void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture +void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data +unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer) +RenderTexture2D rlLoadRenderTexture(int width, int height); // Load a texture to be used for rendering (fbo with color and depth attachments) + +// Vertex data management +void rlLoadMesh(Mesh *mesh, bool dynamic); // Upload vertex data into GPU and provided VAO/VBO ids +void rlUpdateMesh(Mesh mesh, int buffer, int numVertex); // Update vertex data on GPU (upload new data to one buffer) +void rlDrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform +void rlUnloadMesh(Mesh *mesh); // Unload mesh data from CPU and GPU // NOTE: There is a set of shader related functions that are available to end user, // to avoid creating function wrappers through core module, they have been directly declared in raylib.h @@ -396,35 +429,42 @@ void EndVrDrawing(void); // End VR drawing process (and deskt // Shaders System Functions (Module: rlgl) // NOTE: This functions are useless when using OpenGL 1.1 //------------------------------------------------------------------------------------ -Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations -void UnloadShader(Shader shader); // Unload a custom shader from memory +Shader LoadShader(char *vsFileName, char *fsFileName); // Load a custom shader and bind default locations +void UnloadShader(Shader shader); // Unload a custom shader from memory -Shader GetDefaultShader(void); // Get default shader -Shader GetStandardShader(void); // Get default shader -Texture2D GetDefaultTexture(void); // Get default texture +Shader GetShaderDefault(void); // Get default shader +Texture2D GetTextureDefault(void); // Get default texture +// Shader configuration functions int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location void SetShaderValue(Shader shader, int uniformLoc, float *value, int size); // Set shader uniform value (float) void SetShaderValuei(Shader shader, int uniformLoc, int *value, int size); // Set shader uniform value (int) void SetShaderValueMatrix(Shader shader, int uniformLoc, Matrix mat); // Set shader uniform value (matrix 4x4) - void SetMatrixProjection(Matrix proj); // Set a custom projection matrix (replaces internal projection matrix) void SetMatrixModelview(Matrix view); // Set a custom modelview matrix (replaces internal modelview matrix) -void BeginShaderMode(Shader shader); // Begin custom shader drawing -void EndShaderMode(void); // End custom shader drawing (use default shader) -void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) -void EndBlendMode(void); // End blending mode (reset to default: alpha blending) - -void TraceLog(int msgType, const char *text, ...); -float *MatrixToFloat(Matrix mat); - -void InitVrSimulator(int vrDevice); // Init VR simulator for selected device -void CloseVrSimulator(void); // Close VR simulator for current device -void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera -void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator) -void BeginVrDrawing(void); // Begin VR stereo rendering -void EndVrDrawing(void); // End VR stereo rendering +// Texture maps generation (PBR) +// NOTE: Required shaders should be provided +Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture +Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data +Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data +Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size); // Generate BRDF texture using cubemap data + +// Shading and blending +void BeginShaderMode(Shader shader); // Begin custom shader drawing +void EndShaderMode(void); // End custom shader drawing (use default shader) +void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied) +void EndBlendMode(void); // End blending mode (reset to default: alpha blending) + +// VR simulator functionality +void InitVrSimulator(int vrDevice); // Init VR simulator for selected device +void CloseVrSimulator(void); // Close VR simulator for current device +void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera +void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator) +void BeginVrDrawing(void); // Begin VR stereo rendering +void EndVrDrawing(void); // End VR stereo rendering + +void TraceLog(int msgType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) #endif #ifdef __cplusplus @@ -252,7 +252,7 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId) FILE *rresFile = fopen(fileName, "rb"); - if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", fileName); + if (rresFile == NULL) TraceLog(LOG_WARNING, "[%s] rRES raylib resource file could not be opened", fileName); else { // Read rres file info header @@ -266,7 +266,7 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId) // 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); + TraceLog(LOG_WARNING, "[%s] This is not a valid raylib resource file", fileName); } else { @@ -305,7 +305,7 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId) } else rres[k].data = data; - if (rres[k].data != NULL) TraceLog(INFO, "[%s][ID %i] Resource data loaded successfully", fileName, (int)infoHeader.id); + if (rres[k].data != NULL) TraceLog(LOG_INFO, "[%s][ID %i] Resource data loaded successfully", fileName, (int)infoHeader.id); // Read next part fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile); @@ -318,7 +318,7 @@ RRESDEF RRES LoadResource(const char *fileName, int rresId) } } - if (rres[0].data == NULL) TraceLog(WARNING, "[%s][ID %i] Requested resource could not be found", fileName, (int)rresId); + if (rres[0].data == NULL) TraceLog(LOG_WARNING, "[%s][ID %i] Requested resource could not be found", fileName, (int)rresId); } fclose(rresFile); @@ -349,7 +349,7 @@ static void *DecompressData(const unsigned char *data, unsigned long compSize, i // Check correct memory allocation if (uncompData == NULL) { - TraceLog(WARNING, "Out of memory while decompressing data"); + TraceLog(LOG_WARNING, "Out of memory while decompressing data"); } else { @@ -358,18 +358,18 @@ static void *DecompressData(const unsigned char *data, unsigned long compSize, i if (tempUncompSize == -1) { - TraceLog(WARNING, "Data decompression failed"); + TraceLog(LOG_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(LOG_WARNING, "Expected uncompressed size do not match, data may be corrupted"); + TraceLog(LOG_WARNING, " -- Expected uncompressed size: %i", uncompSize); + TraceLog(LOG_WARNING, " -- Returned uncompressed size: %i", tempUncompSize); } - TraceLog(INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize); + TraceLog(LOG_INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize); } return uncompData; @@ -377,36 +377,27 @@ static void *DecompressData(const unsigned char *data, unsigned long compSize, i // 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 +// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) void TraceLog(int logType, const char *text, ...) { va_list args; - int traceDebugMsgs = 0; - -#ifdef DO_NOT_TRACE_DEBUG_MSGS - traceDebugMsgs = 0; -#endif + va_start(args, text); 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; + case LOG_DEBUG: 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); + vfprintf(stdout, text, args); + fprintf(stdout, "\n"); - fprintf(stdout, "\n"); - } + va_end(args); - if (msgType == ERROR) exit(1); // If ERROR message, exit program + if (msgType == LOG_ERROR) exit(1); } #endif diff --git a/src/shapes.c b/src/shapes.c index 2a924476..0e544718 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -119,11 +119,11 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) float d = sqrtf(dx*dx + dy*dy); float angle = asinf(dy/d); - rlEnableTexture(GetDefaultTexture().id); + rlEnableTexture(GetTextureDefault().id); rlPushMatrix(); rlTranslatef((float)startPos.x, (float)startPos.y, 0); - rlRotatef(-RAD2DEG*angle, 0, 0, 1); + rlRotatef(RAD2DEG*angle, 0, 0, 1); rlTranslatef(0, -thick/2.0f, 0); rlBegin(RL_QUADS); @@ -203,7 +203,7 @@ void DrawCircleV(Vector2 center, float radius, Color color) } else if ((rlGetVersion() == OPENGL_21) || (rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20)) { - rlEnableTexture(GetDefaultTexture().id); // Default white texture + rlEnableTexture(GetTextureDefault().id); // Default white texture rlBegin(RL_QUADS); for (int i = 0; i < 360; i += 20) @@ -253,7 +253,7 @@ void DrawRectangleRec(Rectangle rec, Color color) void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color) { - rlEnableTexture(GetDefaultTexture().id); + rlEnableTexture(GetTextureDefault().id); rlPushMatrix(); rlTranslatef((float)rec.x, (float)rec.y, 0); @@ -309,7 +309,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color) } else if ((rlGetVersion() == OPENGL_21) || (rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20)) { - rlEnableTexture(GetDefaultTexture().id); // Default white texture + rlEnableTexture(GetTextureDefault().id); // Default white texture rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); @@ -376,7 +376,7 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) } else if ((rlGetVersion() == OPENGL_21) || (rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20)) { - rlEnableTexture(GetDefaultTexture().id); // Default white texture + rlEnableTexture(GetTextureDefault().id); // Default white texture rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); @@ -139,10 +139,10 @@ extern void LoadDefaultFont(void) 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820, 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0, 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x06000000, 0x24000000, 0x00000901, 0x00000000, 0x09108000, - 0x24fa28a2, 0x00000901, 0x00000000, 0x013e0000, 0x2242252a, 0x00000952, 0x00000000, 0x038a8000, 0x2422222a, 0x00000929, 0x00000000, 0x010a8000, - 0x2412252a, 0x00000901, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000901, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000, - 0x0003e000, 0x00000000, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03, + 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000f01, 0x00000000, 0x06000000, 0x24000000, 0x00000f01, 0x00000000, 0x09108000, + 0x24fa28a2, 0x00000f01, 0x00000000, 0x013e0000, 0x2242252a, 0x00000f52, 0x00000000, 0x038a8000, 0x2422222a, 0x00000f29, 0x00000000, 0x010a8000, + 0x2412252a, 0x00000f01, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000f01, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000, + 0x0003e000, 0x00000f00, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03, 0x02bea080, 0xf0000020, 0xc452208a, 0x04202b02, 0xf8029122, 0x07f0003b, 0xe44b388e, 0x02203a02, 0x081e8a1c, 0x0411e92a, 0xf4420be0, 0x01248202, 0xe8140414, 0x05d104ba, 0xe7c3b880, 0x00893a0a, 0x283c0e1c, 0x04500902, 0xc4400080, 0x00448002, 0xe8208422, 0x04500002, 0x80400000, 0x05200002, 0x083e8e00, 0x04100002, 0x804003e0, 0x07000042, 0xf8008400, 0x07f00003, 0x80400000, 0x04000022, 0x00000000, 0x00000000, 0x80400000, 0x04000002, @@ -255,8 +255,8 @@ extern void LoadDefaultFont(void) } defaultFont.baseSize = defaultFont.chars[0].rec.height; - - TraceLog(INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id); + + TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id); } // Unload raylib default font @@ -330,7 +330,7 @@ SpriteFont LoadSpriteFont(const char *fileName) if (spriteFont.texture.id == 0) { - TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName); + TraceLog(LOG_WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName); spriteFont = GetDefaultFont(); } else SetTextureFilter(spriteFont.texture, FILTER_POINT); // By default we set point filter (best performance) @@ -364,7 +364,7 @@ SpriteFont LoadSpriteFontEx(const char *fileName, int fontSize, int charsCount, if (spriteFont.texture.id == 0) { - TraceLog(WARNING, "[%s] SpriteFont could not be generated, using default font", fileName); + TraceLog(LOG_WARNING, "[%s] SpriteFont could not be generated, using default font", fileName); spriteFont = GetDefaultFont(); } @@ -380,7 +380,7 @@ void UnloadSpriteFont(SpriteFont spriteFont) UnloadTexture(spriteFont.texture); free(spriteFont.chars); - TraceLog(DEBUG, "Unloaded sprite font data"); + TraceLog(LOG_DEBUG, "Unloaded sprite font data"); } } @@ -457,6 +457,14 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float } } +// Draw rectangle using text character (char: 127) +// NOTE: Useful to avoid changing to default white texture +void DrawRectangleT(int posX, int posY, int width, int height, Color color) +{ + DrawTexturePro(GetDefaultFont().texture, GetDefaultFont().chars[95].rec, + (Rectangle){ posX, posY, width, height }, (Vector2){ 0, 0 }, 0.0f, color); +} + // Formatting of text with variables to 'embed' const char *FormatText(const char *text, ...) { @@ -632,6 +640,7 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar) { if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break; } + if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break; } @@ -677,7 +686,7 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar) xPosToRead = charSpacing; } - TraceLog(DEBUG, "SpriteFont data parsed correctly from image"); + TraceLog(LOG_DEBUG, "SpriteFont data parsed correctly from image"); // NOTE: We need to remove key color borders from image to avoid weird // artifacts on texture scaling when using FILTER_BILINEAR or FILTER_TRILINEAR @@ -713,7 +722,7 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar) spriteFont.baseSize = spriteFont.chars[0].rec.height; - TraceLog(INFO, "Image file loaded correctly as SpriteFont"); + TraceLog(LOG_INFO, "Image file loaded correctly as SpriteFont"); return spriteFont; } @@ -743,7 +752,7 @@ static SpriteFont LoadBMFont(const char *fileName) if (fntFile == NULL) { - TraceLog(WARNING, "[%s] FNT file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] FNT file could not be opened", fileName); return font; } @@ -756,20 +765,20 @@ static SpriteFont LoadBMFont(const char *fileName) searchPoint = strstr(buffer, "lineHeight"); sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &texWidth, &texHeight); - TraceLog(DEBUG, "[%s] Font size: %i", fileName, fontSize); - TraceLog(DEBUG, "[%s] Font texture scale: %ix%i", fileName, texWidth, texHeight); + TraceLog(LOG_DEBUG, "[%s] Font size: %i", fileName, fontSize); + TraceLog(LOG_DEBUG, "[%s] Font texture scale: %ix%i", fileName, texWidth, texHeight); fgets(buffer, MAX_BUFFER_SIZE, fntFile); searchPoint = strstr(buffer, "file"); sscanf(searchPoint, "file=\"%128[^\"]\"", texFileName); - TraceLog(DEBUG, "[%s] Font texture filename: %s", fileName, texFileName); + TraceLog(LOG_DEBUG, "[%s] Font texture filename: %s", fileName, texFileName); fgets(buffer, MAX_BUFFER_SIZE, fntFile); searchPoint = strstr(buffer, "count"); sscanf(searchPoint, "count=%i", &charsCount); - TraceLog(DEBUG, "[%s] Font num chars: %i", fileName, charsCount); + TraceLog(LOG_DEBUG, "[%s] Font num chars: %i", fileName, charsCount); // Compose correct path using route of .fnt file (fileName) and texFileName char *texPath = NULL; @@ -785,7 +794,7 @@ static SpriteFont LoadBMFont(const char *fileName) strncat(texPath, fileName, strlen(fileName) - strlen(lastSlash) + 1); strncat(texPath, texFileName, strlen(texFileName)); - TraceLog(DEBUG, "[%s] Font texture loading path: %s", fileName, texPath); + TraceLog(LOG_DEBUG, "[%s] Font texture loading path: %s", fileName, texPath); Image imFont = LoadImage(texPath); @@ -832,7 +841,7 @@ static SpriteFont LoadBMFont(const char *fileName) UnloadSpriteFont(font); font = GetDefaultFont(); } - else TraceLog(INFO, "[%s] SpriteFont loaded successfully", fileName); + else TraceLog(LOG_INFO, "[%s] SpriteFont loaded successfully", fileName); return font; } @@ -853,7 +862,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int charsCount, in 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); + TraceLog(LOG_INFO, "TTF spritefont loading: Predicted texture size: %ix%i", textureSize, textureSize); 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! @@ -865,22 +874,22 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int charsCount, in if (ttfFile == NULL) { - TraceLog(WARNING, "[%s] TTF file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName); return font; } // 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"); + if (fontChars[0] != 32) TraceLog(LOG_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], 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"); + //if (result > 0) TraceLog(LOG_INFO, "TTF spritefont loading: first unused row of generated bitmap: %i", result); + if (result < 0) TraceLog(LOG_WARNING, "TTF spritefont loading: Not all the characters fit in the font"); free(ttfBuffer); diff --git a/src/textures.c b/src/textures.c index 81d36ef8..9322004b 100644 --- a/src/textures.c +++ b/src/textures.c @@ -53,6 +53,7 @@ // Default configuration flags (supported features) //------------------------------------------------- #define SUPPORT_FILEFORMAT_PNG +#define SUPPORT_FILEFORMAT_DDS #define SUPPORT_FILEFORMAT_HDR #define SUPPORT_IMAGE_MANIPULATION //------------------------------------------------- @@ -63,11 +64,14 @@ #include <string.h> // Required for: strcmp(), strrchr(), strncmp() #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 - // Required for: rlglLoadTexture() rlDeleteTextures(), - // rlglGenerateMipmaps(), some funcs for DrawTexturePro() + // Required for: rlLoadTexture() rlDeleteTextures(), + // rlGenerateMipmaps(), some funcs for DrawTexturePro() #include "utils.h" // Required for: fopen() Android mapping +#define STB_PERLIN_IMPLEMENTATION +#include "external/stb_perlin.h"// Required for: stb_perlin_fbm_noise3 + // Support only desired texture formats on stb_image #if !defined(SUPPORT_FILEFORMAT_BMP) #define STBI_NO_BMP @@ -164,7 +168,7 @@ Image LoadImage(const char *fileName) // 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); + else TraceLog(LOG_WARNING, "[%s] Resource file does not contain image data", fileName); UnloadResource(rres); } @@ -191,20 +195,23 @@ Image LoadImage(const char *fileName) int imgBpp = 0; FILE *imFile = fopen(fileName, "rb"); - - // NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...) - image.data = stbi_load_from_file(imFile, &imgWidth, &imgHeight, &imgBpp, 0); - fclose(imFile); + if (imFile != NULL) + { + // NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...) + image.data = stbi_load_from_file(imFile, &imgWidth, &imgHeight, &imgBpp, 0); + + fclose(imFile); - image.width = imgWidth; - image.height = imgHeight; - image.mipmaps = 1; + image.width = imgWidth; + image.height = imgHeight; + image.mipmaps = 1; - if (imgBpp == 1) image.format = UNCOMPRESSED_GRAYSCALE; - else if (imgBpp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA; - else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8; - else if (imgBpp == 4) image.format = UNCOMPRESSED_R8G8B8A8; + if (imgBpp == 1) image.format = UNCOMPRESSED_GRAYSCALE; + else if (imgBpp == 2) image.format = UNCOMPRESSED_GRAY_ALPHA; + else if (imgBpp == 3) image.format = UNCOMPRESSED_R8G8B8; + else if (imgBpp == 4) image.format = UNCOMPRESSED_R8G8B8A8; + } } #if defined(SUPPORT_FILEFORMAT_HDR) else if (IsFileExtension(fileName, ".hdr")) @@ -228,7 +235,7 @@ Image LoadImage(const char *fileName) else { // TODO: Support different number of channels at 32 bit float - TraceLog(WARNING, "[%s] Image fileformat not supported (only 3 channel 32 bit floats)", fileName); + TraceLog(LOG_WARNING, "[%s] Image fileformat not supported (only 3 channel 32 bit floats)", fileName); UnloadImage(image); } } @@ -248,10 +255,10 @@ Image LoadImage(const char *fileName) #if defined(SUPPORT_FILEFORMAT_ASTC) else if (IsFileExtension(fileName, ".astc")) image = LoadASTC(fileName); #endif - else TraceLog(WARNING, "[%s] Image fileformat not supported", fileName); + else TraceLog(LOG_WARNING, "[%s] Image fileformat not supported", 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); + if (image.data != NULL) TraceLog(LOG_INFO, "[%s] Image loaded successfully (%ix%i)", fileName, image.width, image.height); + else TraceLog(LOG_WARNING, "[%s] Image could not be loaded", fileName); return image; } @@ -309,7 +316,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int if (rawFile == NULL) { - TraceLog(WARNING, "[%s] RAW image file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] RAW image file could not be opened", fileName); } else { @@ -327,7 +334,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int case UNCOMPRESSED_R4G4B4A4: image.data = (unsigned short *)malloc(size); break; // 16 bpp (4 bit alpha) case UNCOMPRESSED_R8G8B8A8: image.data = (unsigned char *)malloc(size*4); size *= 4; break; // 32 bpp case UNCOMPRESSED_R32G32B32: image.data = (float *)malloc(size*12); size *= 12; break; // 4 byte per channel (12 byte) - default: TraceLog(WARNING, "Image format not suported"); break; + default: TraceLog(LOG_WARNING, "Image format not suported"); break; } // NOTE: fread() returns num read elements instead of bytes, @@ -337,7 +344,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int // Check if data has been read successfully if (bytes < size) { - TraceLog(WARNING, "[%s] RAW image data can not be read, wrong requested format or size", fileName); + TraceLog(LOG_WARNING, "[%s] RAW image data can not be read, wrong requested format or size", fileName); if (image.data != NULL) free(image.data); } @@ -345,7 +352,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int { image.width = width; image.height = height; - image.mipmaps = 0; + image.mipmaps = 1; image.format = format; } @@ -367,7 +374,7 @@ Texture2D LoadTexture(const char *fileName) texture = LoadTextureFromImage(image); UnloadImage(image); } - else TraceLog(WARNING, "Texture could not be created"); + else TraceLog(LOG_WARNING, "Texture could not be created"); return texture; } @@ -378,14 +385,14 @@ Texture2D LoadTextureFromImage(Image image) { Texture2D texture = { 0 }; - texture.id = rlglLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); + texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); texture.width = image.width; texture.height = image.height; texture.mipmaps = image.mipmaps; texture.format = image.format; - TraceLog(DEBUG, "[TEX ID %i] Parameters: %ix%i, %i mips, format %i", texture.id, texture.width, texture.height, texture.mipmaps, texture.format); + TraceLog(LOG_DEBUG, "[TEX ID %i] Parameters: %ix%i, %i mips, format %i", texture.id, texture.width, texture.height, texture.mipmaps, texture.format); return texture; } @@ -393,7 +400,7 @@ Texture2D LoadTextureFromImage(Image image) // Load texture for rendering (framebuffer) RenderTexture2D LoadRenderTexture(int width, int height) { - RenderTexture2D target = rlglLoadRenderTexture(width, height); + RenderTexture2D target = rlLoadRenderTexture(width, height); return target; } @@ -401,27 +408,27 @@ RenderTexture2D LoadRenderTexture(int width, int height) // Unload image from CPU memory (RAM) void UnloadImage(Image image) { - free(image.data); + if (image.data != NULL) free(image.data); // NOTE: It becomes anoying every time a texture is loaded - //TraceLog(INFO, "Unloaded image data"); + //TraceLog(LOG_INFO, "Unloaded image data"); } // Unload texture from GPU memory (VRAM) void UnloadTexture(Texture2D texture) { - if (texture.id != 0) + if (texture.id > 0) { rlDeleteTextures(texture.id); - TraceLog(INFO, "[TEX ID %i] Unloaded texture data from VRAM (GPU)", texture.id); + TraceLog(LOG_INFO, "[TEX ID %i] Unloaded texture data from VRAM (GPU)", texture.id); } } // Unload render texture from GPU memory (VRAM) void UnloadRenderTexture(RenderTexture2D target) { - if (target.id != 0) rlDeleteRenderTextures(target); + if (target.id > 0) rlDeleteRenderTextures(target); } // Get pixel data from image in the form of Color struct array @@ -504,7 +511,7 @@ Color *GetImageData(Image image) k += 3; } break; - default: TraceLog(WARNING, "Format not supported for pixel data retrieval"); break; + default: TraceLog(LOG_WARNING, "Format not supported for pixel data retrieval"); break; } } @@ -519,7 +526,7 @@ Image GetTextureData(Texture2D texture) if (texture.format < 8) { - image.data = rlglReadTexturePixels(texture); + image.data = rlReadTexturePixels(texture); if (image.data != NULL) { @@ -534,11 +541,11 @@ Image GetTextureData(Texture2D texture) } else image.format = texture.format; - TraceLog(INFO, "Texture pixel data obtained successfully"); + TraceLog(LOG_INFO, "Texture pixel data obtained successfully"); } - else TraceLog(WARNING, "Texture pixel data could not be obtained"); + else TraceLog(LOG_WARNING, "Texture pixel data could not be obtained"); } - else TraceLog(WARNING, "Compressed texture data could not be obtained"); + else TraceLog(LOG_WARNING, "Compressed texture data could not be obtained"); return image; } @@ -547,7 +554,19 @@ Image GetTextureData(Texture2D texture) // NOTE: pixels data must match texture.format void UpdateTexture(Texture2D texture, const void *pixels) { - rlglUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels); + rlUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels); +} + +// Save image to a PNG file +void SaveImageAs(const char* fileName, Image image) +{ +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) + unsigned char* imgData = (unsigned char*)GetImageData(image); // this works since Color is just a container for the RGBA values + SavePNG(fileName, imgData, image.width, image.height, 4); + free(imgData); + + TraceLog(LOG_INFO, "Image saved: %s", fileName); +#endif } // Convert image data to desired format @@ -679,7 +698,7 @@ void ImageFormat(Image *image, int newFormat) free(pixels); } - else TraceLog(WARNING, "Image data format is compressed, can not be converted"); + else TraceLog(LOG_WARNING, "Image data format is compressed, can not be converted"); } } @@ -690,11 +709,11 @@ void ImageAlphaMask(Image *image, Image alphaMask) { if ((image->width != alphaMask.width) || (image->height != alphaMask.height)) { - TraceLog(WARNING, "Alpha mask must be same size as image"); + TraceLog(LOG_WARNING, "Alpha mask must be same size as image"); } else if (image->format >= COMPRESSED_DXT1_RGB) { - TraceLog(WARNING, "Alpha mask can not be applied to compressed data formats"); + TraceLog(LOG_WARNING, "Alpha mask can not be applied to compressed data formats"); } else { @@ -757,7 +776,7 @@ void ImageToPOT(Image *image, Color fillColor) } } - TraceLog(WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight); + TraceLog(LOG_WARNING, "Image converted to POT: (%ix%i) -> (%ix%i)", image->width, image->height, potWidth, potHeight); free(pixels); // Free pixels data free(image->data); // Free old image data @@ -803,7 +822,7 @@ Image ImageCopy(Image image) 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; + default: TraceLog(LOG_WARNING, "Image format not recognized"); break; } newImage.data = malloc(byteSize); @@ -830,13 +849,13 @@ void ImageCrop(Image *image, Rectangle crop) if ((crop.x + crop.width) > image->width) { crop.width = image->width - crop.x; - TraceLog(WARNING, "Crop rectangle width out of bounds, rescaled crop width: %i", crop.width); + TraceLog(LOG_WARNING, "Crop rectangle width out of bounds, rescaled crop width: %i", crop.width); } if ((crop.y + crop.height) > image->height) { crop.height = image->height - crop.y; - TraceLog(WARNING, "Crop rectangle height out of bounds, rescaled crop height: %i", crop.height); + TraceLog(LOG_WARNING, "Crop rectangle height out of bounds, rescaled crop height: %i", crop.height); } if ((crop.x < image->width) && (crop.y < image->height)) @@ -868,7 +887,7 @@ void ImageCrop(Image *image, Rectangle crop) } else { - TraceLog(WARNING, "Image can not be cropped, crop rectangle out of bounds"); + TraceLog(LOG_WARNING, "Image can not be cropped, crop rectangle out of bounds"); } } @@ -943,13 +962,13 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) if ((srcRec.x + srcRec.width) > src.width) { srcRec.width = src.width - srcRec.x; - TraceLog(WARNING, "Source rectangle width out of bounds, rescaled width: %i", srcRec.width); + TraceLog(LOG_WARNING, "Source rectangle width out of bounds, rescaled width: %i", srcRec.width); } if ((srcRec.y + srcRec.height) > src.height) { srcRec.height = src.height - srcRec.y; - TraceLog(WARNING, "Source rectangle height out of bounds, rescaled height: %i", srcRec.height); + TraceLog(LOG_WARNING, "Source rectangle height out of bounds, rescaled height: %i", srcRec.height); cropRequired = true; } @@ -970,14 +989,14 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) if ((dstRec.x + dstRec.width) > dst->width) { dstRec.width = dst->width - dstRec.x; - TraceLog(WARNING, "Destination rectangle width out of bounds, rescaled width: %i", dstRec.width); + TraceLog(LOG_WARNING, "Destination rectangle width out of bounds, rescaled width: %i", dstRec.width); cropRequired = true; } if ((dstRec.y + dstRec.height) > dst->height) { dstRec.height = dst->height - dstRec.y; - TraceLog(WARNING, "Destination rectangle height out of bounds, rescaled height: %i", dstRec.height); + TraceLog(LOG_WARNING, "Destination rectangle height out of bounds, rescaled height: %i", dstRec.height); cropRequired = true; } @@ -1080,7 +1099,7 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing if (fontSize > imSize.y) { float scaleFactor = fontSize/imSize.y; - TraceLog(INFO, "Scalefactor: %f", scaleFactor); + TraceLog(LOG_INFO, "Scalefactor: %f", scaleFactor); // Using nearest-neighbor scaling algorithm for default font if (font.texture.id == GetDefaultFont().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); @@ -1168,13 +1187,13 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) { if (image->format >= COMPRESSED_DXT1_RGB) { - TraceLog(WARNING, "Compressed data formats can not be dithered"); + TraceLog(LOG_WARNING, "Compressed data formats can not be dithered"); return; } if ((rBpp+gBpp+bBpp+aBpp) > 16) { - TraceLog(WARNING, "Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp)); + TraceLog(LOG_WARNING, "Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp)); } else { @@ -1184,7 +1203,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) if ((image->format != UNCOMPRESSED_R8G8B8) && (image->format != UNCOMPRESSED_R8G8B8A8)) { - TraceLog(WARNING, "Image format is already 16bpp or lower, dithering could have no effect"); + TraceLog(LOG_WARNING, "Image format is already 16bpp or lower, dithering could have no effect"); } // Define new image format, check if desired bpp match internal known format @@ -1194,7 +1213,7 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) else { image->format = 0; - TraceLog(WARNING, "Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp); + TraceLog(LOG_WARNING, "Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp); } // NOTE: We will store the dithered data as unsigned short (16bpp) @@ -1428,6 +1447,207 @@ void ImageColorBrightness(Image *image, int brightness) } #endif // SUPPORT_IMAGE_MANIPULATION +// Generate image: vertical gradient +Image GenImageGradientV(int width, int height, Color top, Color bottom) +{ + Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + + for (int j = 0; j < height; j++) + { + float factor = (float)j/(float)height; + for (int i = 0; i < width; i++) + { + pixels[j*width + i].r = (int)((float)bottom.r*factor + (float)top.r*(1.f - factor)); + pixels[j*width + i].g = (int)((float)bottom.g*factor + (float)top.g*(1.f - factor)); + pixels[j*width + i].b = (int)((float)bottom.b*factor + (float)top.b*(1.f - factor)); + pixels[j*width + i].a = (int)((float)bottom.a*factor + (float)top.a*(1.f - factor)); + } + } + + Image image = LoadImageEx(pixels, width, height); + free(pixels); + + return image; +} + +// Generate image: horizontal gradient +Image GenImageGradientH(int width, int height, Color left, Color right) +{ + Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + + for (int i = 0; i < width; i++) + { + float factor = (float)i/(float)width; + for (int j = 0; j < height; j++) + { + pixels[j*width + i].r = (int)((float)right.r*factor + (float)left.r*(1.f - factor)); + pixels[j*width + i].g = (int)((float)right.g*factor + (float)left.g*(1.f - factor)); + pixels[j*width + i].b = (int)((float)right.b*factor + (float)left.b*(1.f - factor)); + pixels[j*width + i].a = (int)((float)right.a*factor + (float)left.a*(1.f - factor)); + } + } + + Image image = LoadImageEx(pixels, width, height); + free(pixels); + + return image; +} + +// Generate image: radial gradient +Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer) +{ + Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + float radius = (width < height) ? (float)width/2.0f : (float)height/2.0f; + + float centerX = (float)width/2.0f; + float centerY = (float)height/2.0f; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + float dist = hypotf((float)x - centerX, (float)y - centerY); + float factor = (dist - radius*density)/(radius*(1.0f - density)); + + factor = fmax(factor, 0.f); + factor = fmin(factor, 1.f); // dist can be bigger than radius so we have to check + + pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); + pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor)); + pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor)); + pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor)); + } + } + + Image image = LoadImageEx(pixels, width, height); + free(pixels); + + return image; +} + +// Generate image: checked +Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2) +{ + Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if ((x/checksX + y/checksY)%2 == 0) pixels[y*width + x] = col1; + else pixels[y*width + x] = col2; + } + } + + Image image = LoadImageEx(pixels, width, height); + free(pixels); + + return image; +} + +// Generate image: white noise +Image GenImageWhiteNoise(int width, int height, float factor) +{ + Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + + for (int i = 0; i < width*height; i++) + { + if (GetRandomValue(0, 99) < (int)(factor*100.0f)) pixels[i] = WHITE; + else pixels[i] = BLACK; + } + + Image image = LoadImageEx(pixels, width, height); + free(pixels); + + return image; +} + +// Generate image: perlin noise +Image GenImagePerlinNoise(int width, int height, float scale) +{ + Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + float nx = (float)x*scale/(float)width; + float ny = (float)y*scale/(float)height; + + // we need to translate the data from [-1; 1] to [0; 1] + float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6, 0, 0, 0) + 1.0f) / 2.0f; + + int intensity = (int)(p * 255.0f); + pixels[y*width + x] = (Color){intensity, intensity, intensity, 255}; + } + } + + Image image = LoadImageEx(pixels, width, height); + free(pixels); + + return image; +} + +// Generate image: cellular algorithm. Bigger tileSize means bigger cells +Image GenImageCellular(int width, int height, int tileSize) +{ + Color *pixels = (Color *)malloc(width*height*sizeof(Color)); + + int seedsPerRow = width/tileSize; + int seedsPerCol = height/tileSize; + int seedsCount = seedsPerRow * seedsPerCol; + + Vector2 *seeds = (Vector2 *)malloc(seedsCount*sizeof(Vector2)); + + for (int i = 0; i < seedsCount; i++) + { + int y = (i/seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1); + int x = (i%seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1); + seeds[i] = (Vector2){x, y}; + } + + for (int y = 0; y < height; y++) + { + int tileY = y/tileSize; + + for (int x = 0; x < width; x++) + { + int tileX = x/tileSize; + + float minDistance = strtod("Inf", NULL); + + // Check all adjacent tiles + for (int i = -1; i < 2; i++) + { + if ((tileX + i < 0) || (tileX + i >= seedsPerRow)) continue; + + for (int j = -1; j < 2; j++) + { + if ((tileY + j < 0) || (tileY + j >= seedsPerCol)) continue; + + Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i]; + + float dist = hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y); + minDistance = fmin(minDistance, dist); + } + } + + // I made this up but it seems to give good results at all tile sizes + int intensity = (int)(minDistance*256.0f/tileSize); + if (intensity > 255) intensity = 255; + + pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 }; + } + } + + free(seeds); + + Image image = LoadImageEx(pixels, width, height); + free(pixels); + + return image; +} + // Generate GPU mipmaps for a texture void GenTextureMipmaps(Texture2D *texture) { @@ -1439,11 +1659,11 @@ void GenTextureMipmaps(Texture2D *texture) // Check if texture is POT if ((potWidth != texture->width) || (potHeight != texture->height)) { - TraceLog(WARNING, "Limited NPOT support, no mipmaps available for NPOT textures"); + TraceLog(LOG_WARNING, "Limited NPOT support, no mipmaps available for NPOT textures"); } - else rlglGenerateMipmaps(texture); + else rlGenerateMipmaps(texture); #else - rlglGenerateMipmaps(texture); + rlGenerateMipmaps(texture); #endif } @@ -1499,7 +1719,7 @@ void SetTextureFilter(Texture2D texture, int filterMode) } else { - TraceLog(WARNING, "[TEX ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id); + TraceLog(LOG_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); @@ -1540,7 +1760,7 @@ void SetTextureWrap(Texture2D texture, int wrapMode) // Draw a Texture2D void DrawTexture(Texture2D texture, int posX, int posY, Color tint) { - DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0, 1.0f, tint); + DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0.0f, 1.0f, tint); } // Draw a Texture2D with position defined as Vector2 @@ -1573,7 +1793,7 @@ void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Co void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint) { // Check if texture is valid - if (texture.id != 0) + if (texture.id > 0) { if (sourceRec.width < 0) sourceRec.x -= sourceRec.width; if (sourceRec.height < 0) sourceRec.y -= sourceRec.height; @@ -1662,19 +1882,13 @@ static Image LoadDDS(const char *fileName) unsigned int reserved2; } DDSHeader; - Image image; - - image.data = NULL; - image.width = 0; - image.height = 0; - image.mipmaps = 0; - image.format = 0; + Image image = { 0 }; FILE *ddsFile = fopen(fileName, "rb"); if (ddsFile == NULL) { - TraceLog(WARNING, "[%s] DDS file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] DDS file could not be opened", fileName); } else { @@ -1685,7 +1899,7 @@ static Image LoadDDS(const char *fileName) if (strncmp(filecode, "DDS ", 4) != 0) { - TraceLog(WARNING, "[%s] DDS file does not seem to be a valid image", fileName); + TraceLog(LOG_WARNING, "[%s] DDS file does not seem to be a valid image", fileName); } else { @@ -1694,15 +1908,17 @@ static Image LoadDDS(const char *fileName) // Get the image header 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, 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); + TraceLog(LOG_DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(DDSHeader)); + TraceLog(LOG_DEBUG, "[%s] DDS file pixel format size: %i", fileName, ddsHeader.ddspf.size); + TraceLog(LOG_DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, ddsHeader.ddspf.flags); + TraceLog(LOG_DEBUG, "[%s] DDS file format: 0x%x", fileName, ddsHeader.ddspf.fourCC); + TraceLog(LOG_DEBUG, "[%s] DDS file bit count: 0x%x", fileName, ddsHeader.ddspf.rgbBitCount); image.width = ddsHeader.width; image.height = ddsHeader.height; - image.mipmaps = 1; // Default value, could be changed (ddsHeader.mipmapCount) + + if (ddsHeader.mipmapCount == 0) image.mipmaps = 1; // Parameter not used + else image.mipmaps = ddsHeader.mipmapCount; if (ddsHeader.ddspf.rgbBitCount == 16) // 16bit mode, no compressed { @@ -1786,14 +2002,12 @@ static Image LoadDDS(const char *fileName) if (ddsHeader.mipmapCount > 1) size = ddsHeader.pitchOrLinearSize*2; else size = ddsHeader.pitchOrLinearSize; - TraceLog(DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize); + TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize); image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); fread(image.data, size, 1, ddsFile); - image.mipmaps = ddsHeader.mipmapCount; - switch (ddsHeader.ddspf.fourCC) { case FOURCC_DXT1: @@ -1848,19 +2062,13 @@ static Image LoadPKM(const char *fileName) // NOTE: The extended width and height are the widths rounded up to a multiple of 4. // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels) - Image image; - - image.data = NULL; - image.width = 0; - image.height = 0; - image.mipmaps = 0; - image.format = 0; + Image image = { 0 }; FILE *pkmFile = fopen(fileName, "rb"); if (pkmFile == NULL) { - TraceLog(WARNING, "[%s] PKM file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] PKM file could not be opened", fileName); } else { @@ -1871,7 +2079,7 @@ static Image LoadPKM(const char *fileName) if (strncmp(pkmHeader.id, "PKM ", 4) != 0) { - TraceLog(WARNING, "[%s] PKM file does not seem to be a valid image", fileName); + TraceLog(LOG_WARNING, "[%s] PKM file does not seem to be a valid image", fileName); } else { @@ -1880,9 +2088,9 @@ static Image LoadPKM(const char *fileName) 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", pkmHeader.width); - TraceLog(DEBUG, "PKM (ETC) image height: %i", pkmHeader.height); - TraceLog(DEBUG, "PKM (ETC) image format: %i", pkmHeader.format); + TraceLog(LOG_DEBUG, "PKM (ETC) image width: %i", pkmHeader.width); + TraceLog(LOG_DEBUG, "PKM (ETC) image height: %i", pkmHeader.height); + TraceLog(LOG_DEBUG, "PKM (ETC) image format: %i", pkmHeader.format); image.width = pkmHeader.width; image.height = pkmHeader.height; @@ -1943,18 +2151,13 @@ static Image LoadKTX(const char *fileName) // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize - Image image; - - image.width = 0; - image.height = 0; - image.mipmaps = 0; - image.format = 0; + Image image = { 0 }; FILE *ktxFile = fopen(fileName, "rb"); if (ktxFile == NULL) { - TraceLog(WARNING, "[%s] KTX image file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] KTX image file could not be opened", fileName); } else { @@ -1966,7 +2169,7 @@ static Image LoadKTX(const char *fileName) 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); + TraceLog(LOG_WARNING, "[%s] KTX file does not seem to be a valid file", fileName); } else { @@ -1974,9 +2177,9 @@ static Image LoadKTX(const char *fileName) image.height = ktxHeader.height; image.mipmaps = ktxHeader.mipmapLevels; - 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); + TraceLog(LOG_DEBUG, "KTX (ETC) image width: %i", ktxHeader.width); + TraceLog(LOG_DEBUG, "KTX (ETC) image height: %i", ktxHeader.height); + TraceLog(LOG_DEBUG, "KTX (ETC) image format: 0x%x", ktxHeader.glInternalFormat); unsigned char unused; @@ -2063,19 +2266,13 @@ static Image LoadPVR(const char *fileName) } PVRMetadata; #endif - Image image; - - image.data = NULL; - image.width = 0; - image.height = 0; - image.mipmaps = 0; - image.format = 0; + Image image = { 0 }; FILE *pvrFile = fopen(fileName, "rb"); if (pvrFile == NULL) { - TraceLog(WARNING, "[%s] PVR file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] PVR file could not be opened", fileName); } else { @@ -2094,7 +2291,7 @@ static Image LoadPVR(const char *fileName) 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); + TraceLog(LOG_WARNING, "[%s] PVR file does not seem to be a valid image", fileName); } else { @@ -2155,7 +2352,7 @@ static Image LoadPVR(const char *fileName) fread(image.data, dataSize, 1, pvrFile); } } - else if (pvrVersion == 52) TraceLog(INFO, "PVR v2 not supported, update your files to PVR v3"); + else if (pvrVersion == 52) TraceLog(LOG_INFO, "PVR v2 not supported, update your files to PVR v3"); fclose(pvrFile); // Close file pointer } @@ -2184,22 +2381,16 @@ static Image LoadASTC(const char *fileName) unsigned char blockZ; // Block Z dimensions (1 for 2D images) 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) + unsigned char length[3]; // Image Z-size (1 for 2D images) } ASTCHeader; - Image image; - - image.data = NULL; - image.width = 0; - image.height = 0; - image.mipmaps = 0; - image.format = 0; + Image image = { 0 }; FILE *astcFile = fopen(fileName, "rb"); if (astcFile == NULL) { - TraceLog(WARNING, "[%s] ASTC file could not be opened", fileName); + TraceLog(LOG_WARNING, "[%s] ASTC file could not be opened", fileName); } else { @@ -2210,7 +2401,7 @@ static Image LoadASTC(const char *fileName) 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); + TraceLog(LOG_WARNING, "[%s] ASTC file does not seem to be a valid image", fileName); } else { @@ -2218,12 +2409,11 @@ static Image LoadASTC(const char *fileName) 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", astcHeader.blockX, astcHeader.blockY); + TraceLog(LOG_DEBUG, "ASTC image width: %i", image.width); + TraceLog(LOG_DEBUG, "ASTC image height: %i", image.height); + TraceLog(LOG_DEBUG, "ASTC image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY); + + image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level // NOTE: Each block is always stored in 128bit so we can calculate the bpp int bpp = 128/(astcHeader.blockX*astcHeader.blockY); @@ -2239,7 +2429,7 @@ static Image LoadASTC(const char *fileName) if (bpp == 8) image.format = COMPRESSED_ASTC_4x4_RGBA; else if (bpp == 2) image.format = COMPRESSED_ASTC_4x4_RGBA; } - else TraceLog(WARNING, "[%s] ASTC block size configuration not supported", fileName); + else TraceLog(LOG_WARNING, "[%s] ASTC block size configuration not supported", fileName); } fclose(astcFile); @@ -2247,4 +2437,4 @@ static Image LoadASTC(const char *fileName) return image; } -#endif
\ No newline at end of file +#endif diff --git a/src/utils.c b/src/utils.c index c86c9c82..cd4638af 100644 --- a/src/utils.c +++ b/src/utils.c @@ -14,10 +14,10 @@ * * #define SUPPORT_TRACELOG * Show TraceLog() output messages -* NOTE: By default DEBUG traces not shown +* NOTE: By default LOG_DEBUG traces not shown * * #define SUPPORT_TRACELOG_DEBUG -* Show TraceLog() DEBUG messages +* Show TraceLog() LOG_DEBUG messages * * DEPENDENCIES: * stb_image_write - BMP/PNG writting functions @@ -45,7 +45,7 @@ **********************************************************************************************/ #define SUPPORT_TRACELOG // Output tracelog messages -//#define SUPPORT_TRACELOG_DEBUG // Avoid DEBUG messages tracing +//#define SUPPORT_TRACELOG_DEBUG // Avoid LOG_DEBUG messages tracing #include "utils.h" @@ -89,7 +89,7 @@ static int android_close(void *cookie); // Module Functions Definition - Utilities //---------------------------------------------------------------------------------- -// Output trace log messages +// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) void TraceLog(int msgType, const char *text, ...) { #if defined(SUPPORT_TRACELOG) @@ -102,10 +102,10 @@ void TraceLog(int msgType, const char *text, ...) switch(msgType) { - case INFO: strcpy(buffer, "INFO: "); break; - case ERROR: strcpy(buffer, "ERROR: "); break; - case WARNING: strcpy(buffer, "WARNING: "); break; - case DEBUG: strcpy(buffer, "DEBUG: "); break; + case LOG_INFO: strcpy(buffer, "INFO: "); break; + case LOG_ERROR: strcpy(buffer, "ERROR: "); break; + case LOG_WARNING: strcpy(buffer, "WARNING: "); break; + case LOG_DEBUG: strcpy(buffer, "DEBUG: "); break; default: break; } @@ -118,19 +118,19 @@ void TraceLog(int msgType, const char *text, ...) #if defined(PLATFORM_ANDROID) switch(msgType) { - case INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", buffer, args); break; - case ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", buffer, args); break; - case WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", buffer, args); break; - case DEBUG: if (traceDebugMsgs) __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", buffer, args); break; + case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", buffer, args); break; + case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", buffer, args); break; + case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", buffer, args); break; + case LOG_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); + if ((msgType != LOG_DEBUG) || ((msgType == LOG_DEBUG) && (traceDebugMsgs))) vprintf(buffer, args); #endif va_end(args); - if (msgType == ERROR) exit(1); // If ERROR message, exit program + if (msgType == LOG_ERROR) exit(1); // If LOG_ERROR message, exit program #endif // SUPPORT_TRACELOG } @@ -195,7 +195,7 @@ static int android_read(void *cookie, char *buf, int size) static int android_write(void *cookie, const char *buf, int size) { - TraceLog(ERROR, "Can't provide write access to the APK"); + TraceLog(LOG_ERROR, "Can't provide write access to the APK"); return EACCES; } |
