Quellcode durchsuchen

Lots of changes, most of them under testing-review

Added a Tracing/Log system
Added OGG stream music support (DOESN'T WORK)
Added Compressed textures support
* This update is probably very buggy...
raysan5 vor 11 Jahren
Ursprung
Commit
e6b82cb111
12 geänderte Dateien mit 1141 neuen und 523 gelöschten Zeilen
  1. 415 194
      src/audio.c
  2. 51 37
      src/core.c
  3. 36 46
      src/models.c
  4. 51 27
      src/raylib.h
  5. 146 52
      src/rlgl.c
  6. 11 7
      src/rlgl.h
  7. 4 0
      src/stb_vorbis.c
  8. 2 7
      src/stb_vorbis.h
  9. 21 13
      src/text.c
  10. 269 118
      src/textures.c
  11. 127 21
      src/utils.c
  12. 8 1
      src/utils.h

+ 415 - 194
src/audio.c

@@ -37,7 +37,7 @@
 
 #include "utils.h"           // rRES data decompression utility function
 
-//#include "stb_vorbis.h"    // TODO: OGG loading functions
+//#include "stb_vorbis.h"      // OGG loading functions
 
 //----------------------------------------------------------------------------------
 // Defines and Macros
@@ -47,6 +47,29 @@
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
+// Sound source type (all file loaded in memory)
+/*
+struct Sound {
+    unsigned int source;
+    unsigned int buffer;
+};
+
+// Music type (file streamming from memory)
+// NOTE: Anything longer than ~10 seconds should be Music...
+struct Music {
+    stb_vorbis* stream;
+	stb_vorbis_info info;
+    
+    ALuint id; 
+	ALuint buffers[2];
+	ALuint source;
+	ALenum format;
+ 
+	int bufferSize;
+	int totalSamplesLeft;
+	bool loop;
+};
+*/
 
 // Wave file data
 typedef struct Wave {
@@ -60,7 +83,8 @@ typedef struct Wave {
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-// Nop...
+static bool musicIsPlaying;
+static Music *currentMusic;
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -68,6 +92,10 @@ typedef struct Wave {
 static Wave LoadWAV(char *fileName);
 static void UnloadWAV(Wave wave);
 //static Ogg LoadOGG(char *fileName);
+static bool MusicStream(Music music, ALuint buffer);
+
+extern bool MusicStreamUpdate();
+extern void PlayCurrentMusic();
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Window and OpenGL Context Functions
@@ -79,30 +107,27 @@ void InitAudioDevice()
     // Open and initialize a device with default settings
     ALCdevice *device = alcOpenDevice(NULL);
     
-    if(!device)
-    {
-        fprintf(stderr, "Could not open a device!\n");
-        exit(1);
-    }
+    if(!device) TraceLog(ERROR, "Could not open audio device");
 
     ALCcontext *context = alcCreateContext(device, NULL);
     
     if(context == NULL || alcMakeContextCurrent(context) == ALC_FALSE)
     {
-        if(context != NULL)    alcDestroyContext(context);
+        if(context != NULL) alcDestroyContext(context);
         
         alcCloseDevice(device);
         
-        fprintf(stderr, "Could not set a context!\n");
-        exit(1);
+        TraceLog(ERROR, "Could not setup audio context");
     }
 
-    printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
+    TraceLog(INFO, "Audio device and context initialized: %s\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
     
     // Listener definition (just for 2D)
     alListener3f(AL_POSITION, 0, 0, 0);
     alListener3f(AL_VELOCITY, 0, 0, 0);
     alListener3f(AL_ORIENTATION, 0, 0, -1);
+    
+    musicIsPlaying = false;
 }
 
 // Close the audio device for the current context, and destroys the context
@@ -111,7 +136,7 @@ void CloseAudioDevice()
     ALCdevice *device;
     ALCcontext *context = alcGetCurrentContext();
     
-    if (context == NULL) return;
+    if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing");
 
     device = alcGetContextsDevice(context);
 
@@ -169,10 +194,8 @@ Sound LoadSound(char *fileName)
     // Unallocate WAV data
     UnloadWAV(wave);
     
-    printf("Sample rate: %i\n", wave.sampleRate);
-    printf("Channels: %i\n", wave.channels);
-    
-    printf("Audio file loaded...!\n");
+    TraceLog(INFO, "[%s] Sound file loaded successfully", fileName);  
+    TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels);
     
     sound.source = source;
     sound.buffer = buffer;
@@ -196,139 +219,136 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
     
     FILE *rresFile = fopen(rresName, "rb");
 
-    if (!rresFile) printf("Error opening raylib Resource file\n");
-    
-    // Read rres file (basic file check - id)
-    fread(&id[0], sizeof(char), 1, rresFile);
-    fread(&id[1], sizeof(char), 1, rresFile);
-    fread(&id[2], sizeof(char), 1, rresFile);
-    fread(&id[3], sizeof(char), 1, rresFile);
-    fread(&version, sizeof(char), 1, rresFile);
-    fread(&useless, sizeof(char), 1, rresFile);
-    
-    if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
+    if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName);
+    else
     {
-        printf("This is not a valid raylib Resource file!\n");
-        exit(1);
-    }
-    
-    // Read number of resources embedded
-    fread(&numRes, sizeof(short), 1, rresFile);
-    
-    for (int i = 0; i < numRes; i++)
-    {
-        fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile);
+        // Read rres file (basic file check - id)
+        fread(&id[0], sizeof(char), 1, rresFile);
+        fread(&id[1], sizeof(char), 1, rresFile);
+        fread(&id[2], sizeof(char), 1, rresFile);
+        fread(&id[3], sizeof(char), 1, rresFile);
+        fread(&version, sizeof(char), 1, rresFile);
+        fread(&useless, sizeof(char), 1, rresFile);
         
-        if (infoHeader.id == resId)
+        if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
         {
-            found = true;
-
-            // Check data is of valid SOUND type
-            if (infoHeader.type == 1)   // SOUND data type
-            {
-                // TODO: Check data compression type
-                // NOTE: We suppose compression type 2 (DEFLATE - default)
-                
-                // Reading SOUND parameters
-                Wave wave;
-                short sampleRate, bps;
-                char channels, reserved;
+            TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName);
+        }
+        else
+        {
+            // Read number of resources embedded
+            fread(&numRes, sizeof(short), 1, rresFile);
             
-                fread(&sampleRate, sizeof(short), 1, rresFile); // Sample rate (frequency)
-                fread(&bps, sizeof(short), 1, rresFile);        // Bits per sample
-                fread(&channels, 1, 1, rresFile);               // Channels (1 - mono, 2 - stereo)
-                fread(&reserved, 1, 1, rresFile);               // <reserved>
-        
-                printf("Sample rate: %i\n", (int)sampleRate);
-                printf("Bits per sample: %i\n", (int)bps);
-                printf("Channels: %i\n", (int)channels);
-                
-                wave.sampleRate = sampleRate;
-                wave.dataSize = infoHeader.srcSize;
-                wave.bitsPerSample = bps;
-                wave.channels = (short)channels;
-                
-                unsigned char *data = malloc(infoHeader.size);
-
-                fread(data, infoHeader.size, 1, rresFile);
-                
-                wave.data = DecompressData(data, infoHeader.size, infoHeader.srcSize);
-                
-                free(data);
-                
-                // Convert wave to Sound (OpenAL)
-                ALenum format = 0;
+            for (int i = 0; i < numRes; i++)
+            {
+                fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile);
                 
-                // The OpenAL format is worked out by looking at the number of channels and the bits per sample
-                if (wave.channels == 1) 
+                if (infoHeader.id == resId)
                 {
-                    if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
-                    else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
-                } 
-                else if (wave.channels == 2) 
-                {
-                    if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
-                    else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
+                    found = true;
+
+                    // Check data is of valid SOUND type
+                    if (infoHeader.type == 1)   // SOUND data type
+                    {
+                        // TODO: Check data compression type
+                        // NOTE: We suppose compression type 2 (DEFLATE - default)
+                        
+                        // Reading SOUND parameters
+                        Wave wave;
+                        short sampleRate, bps;
+                        char channels, reserved;
+                    
+                        fread(&sampleRate, sizeof(short), 1, rresFile); // Sample rate (frequency)
+                        fread(&bps, sizeof(short), 1, rresFile);        // Bits per sample
+                        fread(&channels, 1, 1, rresFile);               // Channels (1 - mono, 2 - stereo)
+                        fread(&reserved, 1, 1, rresFile);               // <reserved>
+                                
+                        wave.sampleRate = sampleRate;
+                        wave.dataSize = infoHeader.srcSize;
+                        wave.bitsPerSample = bps;
+                        wave.channels = (short)channels;
+                        
+                        unsigned char *data = malloc(infoHeader.size);
+
+                        fread(data, infoHeader.size, 1, rresFile);
+                        
+                        wave.data = DecompressData(data, infoHeader.size, infoHeader.srcSize);
+                        
+                        free(data);
+                        
+                        // Convert wave to Sound (OpenAL)
+                        ALenum format = 0;
+                        
+                        // The OpenAL format is worked out by looking at the number of channels and the bits per sample
+                        if (wave.channels == 1) 
+                        {
+                            if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
+                            else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
+                        } 
+                        else if (wave.channels == 2) 
+                        {
+                            if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
+                            else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
+                        }
+                        
+                        
+                        // Create an audio source
+                        ALuint source;
+                        alGenSources(1, &source);            // Generate pointer to audio source
+
+                        alSourcef(source, AL_PITCH, 1);    
+                        alSourcef(source, AL_GAIN, 1);
+                        alSource3f(source, AL_POSITION, 0, 0, 0);
+                        alSource3f(source, AL_VELOCITY, 0, 0, 0);
+                        alSourcei(source, AL_LOOPING, AL_FALSE);
+                        
+                        // Convert loaded data to OpenAL buffer
+                        //----------------------------------------
+                        ALuint buffer;
+                        alGenBuffers(1, &buffer);            // Generate pointer to buffer
+
+                        // Upload sound data to buffer
+                        alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate);
+
+                        // Attach sound buffer to source
+                        alSourcei(source, AL_BUFFER, buffer);
+                        
+                        // Unallocate WAV data
+                        UnloadWAV(wave);
+
+                        TraceLog(INFO, "[%s] Sound loaded successfully from resource, sample rate: %i", rresName, (int)sampleRate);
+                        
+                        sound.source = source;
+                        sound.buffer = buffer;
+                    }
+                    else
+                    {
+                        TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName);
+                    }
                 }
-                
-                
-                // Create an audio source
-                ALuint source;
-                alGenSources(1, &source);            // Generate pointer to audio source
-
-                alSourcef(source, AL_PITCH, 1);    
-                alSourcef(source, AL_GAIN, 1);
-                alSource3f(source, AL_POSITION, 0, 0, 0);
-                alSource3f(source, AL_VELOCITY, 0, 0, 0);
-                alSourcei(source, AL_LOOPING, AL_FALSE);
-                
-                // Convert loaded data to OpenAL buffer
-                //----------------------------------------
-                ALuint buffer;
-                alGenBuffers(1, &buffer);            // Generate pointer to buffer
-
-                // Upload sound data to buffer
-                alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate);
-
-                // Attach sound buffer to source
-                alSourcei(source, AL_BUFFER, buffer);
-                
-                // Unallocate WAV data
-                UnloadWAV(wave);
-
-                printf("Audio file loaded...!\n");
-                
-                sound.source = source;
-                sound.buffer = buffer;
-            }
-            else
-            {
-
-                printf("Required resource do not seem to be a valid IMAGE resource\n");
-                exit(2);
+                else
+                {
+                    // Depending on type, skip the right amount of parameters
+                    switch (infoHeader.type)
+                    {
+                        case 0: fseek(rresFile, 6, SEEK_CUR); break;   // IMAGE: Jump 6 bytes of parameters
+                        case 1: fseek(rresFile, 6, SEEK_CUR); break;   // SOUND: Jump 6 bytes of parameters
+                        case 2: fseek(rresFile, 5, SEEK_CUR); break;   // MODEL: Jump 5 bytes of parameters (TODO: Review)
+                        case 3: break;   // TEXT: No parameters
+                        case 4: break;   // RAW: No parameters
+                        default: break;
+                    }
+                    
+                    // Jump DATA to read next infoHeader
+                    fseek(rresFile, infoHeader.size, SEEK_CUR);
+                }    
             }
         }
-        else
-        {
-            // Depending on type, skip the right amount of parameters
-            switch (infoHeader.type)
-            {
-                case 0: fseek(rresFile, 6, SEEK_CUR); break;   // IMAGE: Jump 6 bytes of parameters
-                case 1: fseek(rresFile, 6, SEEK_CUR); break;   // SOUND: Jump 6 bytes of parameters
-                case 2: fseek(rresFile, 5, SEEK_CUR); break;   // MODEL: Jump 5 bytes of parameters (TODO: Review)
-                case 3: break;   // TEXT: No parameters
-                case 4: break;   // RAW: No parameters
-                default: break;
-            }
-            
-            // Jump DATA to read next infoHeader
-            fseek(rresFile, infoHeader.size, SEEK_CUR);
-        }    
+        
+        fclose(rresFile);
     }
     
-    fclose(rresFile);
-    
-    if (!found) printf("Required resource id could not be found in the raylib Resource file!\n");
+    if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId);
     
     return sound;
 }
@@ -345,7 +365,7 @@ void PlaySound(Sound sound)
 {
     alSourcePlay(sound.source);        // Play the sound
     
-    printf("Playing sound!\n");
+    TraceLog(INFO, "Playing sound");
 
     // Find the current position of the sound being played
     // NOTE: Only work when the entire file is in a single buffer
@@ -390,7 +410,7 @@ void StopSound(Sound sound)
 }
 
 // Check if a sound is playing
-bool IsPlaying(Sound sound)
+bool SoundIsPlaying(Sound sound)
 {
     bool playing = false;
     ALint state;
@@ -401,6 +421,16 @@ bool IsPlaying(Sound sound)
     return playing;
 }
 
+// Check if music is playing
+bool MusicIsPlaying(Music music)
+{
+    ALenum state;
+    
+    alGetSourcei(music.source, AL_SOURCE_STATE, &state);
+    
+    return (state == AL_PLAYING);
+}
+
 // Set volume for a sound
 void SetVolume(Sound sound, float volume)
 {
@@ -450,61 +480,65 @@ static Wave LoadWAV(char *fileName)
     
     if (!wavFile)
     {
-        printf("Could not open WAV file.\n");
-        exit(1);
+        TraceLog(WARNING, "[%s] Could not open WAV file", fileName);
     }
-   
-    // Read in the first chunk into the struct
-    fread(&riffHeader, sizeof(RiffHeader), 1, wavFile);
- 
-    // Check for RIFF and WAVE tags
-    if ((riffHeader.chunkID[0] != 'R' ||
-         riffHeader.chunkID[1] != 'I' ||
-         riffHeader.chunkID[2] != 'F' ||
-         riffHeader.chunkID[3] != 'F') ||
-        (riffHeader.format[0] != 'W' ||
-         riffHeader.format[1] != 'A' ||
-         riffHeader.format[2] != 'V' ||
-         riffHeader.format[3] != 'E'))
-            printf("Invalid RIFF or WAVE Header");
- 
-    // Read in the 2nd chunk for the wave info
-    fread(&waveFormat, sizeof(WaveFormat), 1, wavFile);
-    
-    // Check for fmt tag
-    if (waveFormat.subChunkID[0] != 'f' ||
-        waveFormat.subChunkID[1] != 'm' ||
-        waveFormat.subChunkID[2] != 't' ||
-        waveFormat.subChunkID[3] != ' ')
-            printf("Invalid Wave Format");
- 
-    // Check for extra parameters;
-    if (waveFormat.subChunkSize > 16)
-        fseek(wavFile, sizeof(short), SEEK_CUR);
- 
-    // Read in the the last byte of data before the sound file
-    fread(&waveData, sizeof(WaveData), 1, wavFile);
-    
-    // Check for data tag
-    if (waveData.subChunkID[0] != 'd' ||
-        waveData.subChunkID[1] != 'a' ||
-        waveData.subChunkID[2] != 't' ||
-        waveData.subChunkID[3] != 'a')
-            printf("Invalid data header");
- 
-    // Allocate memory for data
-    wave.data = (unsigned char *)malloc(sizeof(unsigned char) * waveData.subChunkSize); 
- 
-    // Read in the sound data into the soundData variable
-    fread(wave.data, waveData.subChunkSize, 1, wavFile);
-    
-    // Now we set the variables that we need later
-    wave.dataSize = waveData.subChunkSize;
-    wave.sampleRate = waveFormat.sampleRate;
-    wave.channels = waveFormat.numChannels;
-    wave.bitsPerSample = waveFormat.bitsPerSample;  
+    else
+    {
+        // Read in the first chunk into the struct
+        fread(&riffHeader, sizeof(RiffHeader), 1, wavFile);
+     
+        // Check for RIFF and WAVE tags
+        if (((riffHeader.chunkID[0] != 'R') || (riffHeader.chunkID[1] != 'I') || (riffHeader.chunkID[2] != 'F') || (riffHeader.chunkID[3] != 'F')) ||
+            ((riffHeader.format[0] != 'W') || (riffHeader.format[1] != 'A') || (riffHeader.format[2] != 'V') || (riffHeader.format[3] != 'E')))
+        {
+                TraceLog(WARNING, "[%s] Invalid RIFF or WAVE Header", fileName);
+        }
+        else
+        {
+            // Read in the 2nd chunk for the wave info
+            fread(&waveFormat, sizeof(WaveFormat), 1, wavFile);
+            
+            // Check for fmt tag
+            if ((waveFormat.subChunkID[0] != 'f') || (waveFormat.subChunkID[1] != 'm') ||
+                (waveFormat.subChunkID[2] != 't') || (waveFormat.subChunkID[3] != ' '))
+            {
+                TraceLog(WARNING, "[%s] Invalid Wave format", fileName);
+            }
+            else
+            {
+                // Check for extra parameters;
+                if (waveFormat.subChunkSize > 16) fseek(wavFile, sizeof(short), SEEK_CUR);
+             
+                // Read in the the last byte of data before the sound file
+                fread(&waveData, sizeof(WaveData), 1, wavFile);
+                
+                // Check for data tag
+                if ((waveData.subChunkID[0] != 'd') || (waveData.subChunkID[1] != 'a') ||
+                    (waveData.subChunkID[2] != 't') || (waveData.subChunkID[3] != 'a'))
+                {
+                    TraceLog(WARNING, "[%s] Invalid data header", fileName);
+                }
+                else
+                {
+                    // Allocate memory for data
+                    wave.data = (unsigned char *)malloc(sizeof(unsigned char) * waveData.subChunkSize); 
+                 
+                    // Read in the sound data into the soundData variable
+                    fread(wave.data, waveData.subChunkSize, 1, wavFile);
+                    
+                    // Now we set the variables that we need later
+                    wave.dataSize = waveData.subChunkSize;
+                    wave.sampleRate = waveFormat.sampleRate;
+                    wave.channels = waveFormat.numChannels;
+                    wave.bitsPerSample = waveFormat.bitsPerSample;
+                    
+                    TraceLog(INFO, "[%s] Wave file loaded successfully", fileName);
+                }
+            }
+        }
 
-    fclose(wavFile);
+        fclose(wavFile);
+    }
     
     return wave;
 }
@@ -516,5 +550,192 @@ static void UnloadWAV(Wave wave)
 }
 
 // TODO: Ogg data loading
-//static Ogg LoadOGG(char *fileName) { }
+Music LoadMusic(char *fileName)
+{
+    Music music;
+    
+    // Open audio stream
+    music.stream = stb_vorbis_open_filename(fileName, NULL, NULL);
+    
+	if (music.stream == NULL) TraceLog(WARNING, "Could not open ogg audio file");
+    else
+    {
+        // Get file info
+        music.info = stb_vorbis_get_info(music.stream);
+        
+        printf("Ogg sample rate: %i\n", music.info.sample_rate);
+        printf("Ogg channels: %i\n", music.info.channels);
+        printf("Temp memory required: %i\n", music.info.temp_memory_required);
+        
+        if (music.info.channels == 2) music.format = AL_FORMAT_STEREO16;
+        else music.format = AL_FORMAT_MONO16;
+        
+        music.bufferSize = 4096*8;
+        music.loop = true;          // We loop by default
+        
+        // Create an audio source
+        alGenSources(1, &music.source);             // Generate pointer to audio source
+
+        alSourcef(music.source, AL_PITCH, 1);    
+        alSourcef(music.source, AL_GAIN, 1);
+        alSource3f(music.source, AL_POSITION, 0, 0, 0);
+        alSource3f(music.source, AL_VELOCITY, 0, 0, 0);
+        alSourcei(music.source, AL_LOOPING, AL_TRUE);     // We loop by default
+        
+        // Convert loaded data to OpenAL buffers
+        alGenBuffers(2, music.buffers);
+    /*
+        if (!MusicStream(music, music.buffers[0])) exit(1);
+        if (!MusicStream(music, music.buffers[1])) exit(1);
+        
+        alSourceQueueBuffers(music.source, 2, music.buffers);
+     
+        PlayMusic(music);
+    */ 
+        music.totalSamplesLeft = stb_vorbis_stream_length_in_samples(music.stream) * music.info.channels;
+     
+        currentMusic = &music;
+    }
+    
+    return music;
+}
+
+void UnloadMusic(Music music)
+{
+    StopMusic(music);
+
+    alDeleteSources(1, &music.source);
+	alDeleteBuffers(2, music.buffers);
+    
+	stb_vorbis_close(music.stream);
+}
+
+void PlayMusic(Music music)
+{
+    //if (MusicIsPlaying(music)) return true;
+
+    if (!MusicStream(music, music.buffers[0])) TraceLog(WARNING, "MusicStream returned 0");
+    if (!MusicStream(music, music.buffers[1])) TraceLog(WARNING, "MusicStream returned 0");
+    
+    alSourceQueueBuffers(music.source, 2, music.buffers);
+    alSourcePlay(music.source);
+
+    TraceLog(INFO, "Playing music");
+}
+
+extern void PlayCurrentMusic()
+{
+    if (!MusicStream(*currentMusic, currentMusic->buffers[0])) TraceLog(WARNING, "MusicStream returned 0");
+    if (!MusicStream(*currentMusic, currentMusic->buffers[1])) TraceLog(WARNING, "MusicStream returned 0");
+    
+    alSourceQueueBuffers(currentMusic->source, 2, currentMusic->buffers);
+    alSourcePlay(currentMusic->source);
+}
+
+// Stop reproducing music
+void StopMusic(Music music)
+{
+    alSourceStop(music.source);
+    
+    musicIsPlaying = false;
+}
+
+static bool MusicStream(Music music, ALuint buffer)
+{
+	//Uncomment this to avoid VLAs
+	//#define BUFFER_SIZE 4096*32
+	#ifndef BUFFER_SIZE//VLAs ftw
+	#define BUFFER_SIZE (music.bufferSize)
+	#endif
+	ALshort pcm[BUFFER_SIZE];
+    
+	int  size = 0;
+	int  result = 0;
+ 
+	while (size < BUFFER_SIZE)
+    {
+		result = stb_vorbis_get_samples_short_interleaved(music.stream, music.info.channels, pcm+size, BUFFER_SIZE-size);
+        
+		if (result > 0) size += (result*music.info.channels);
+		else break;
+	}
+ 
+	if (size == 0) return false;
+ 
+	alBufferData(buffer, music.format, pcm, size*sizeof(ALshort), music.info.sample_rate);
+    
+	music.totalSamplesLeft -= size;
+	
+    #undef BUFFER_SIZE
+ 
+	return true;
+}
+/*
+extern bool MusicStreamUpdate()
+{
+	ALint processed = 0;
+ 
+    alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed);
+ 
+    while (processed--)
+    {
+        ALuint buffer = 0;
+        
+        alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
+ 
+		if (!MusicStream(*currentMusic, buffer))
+        {
+			bool shouldExit = true;
+ 
+			if (currentMusic->loop)
+            {
+				stb_vorbis_seek_start(currentMusic->stream);
+				currentMusic->totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic->stream) * currentMusic->info.channels;
+				
+                shouldExit = !MusicStream(*currentMusic, buffer);
+			}
+ 
+			if (shouldExit) return false;
+		}
+        
+		alSourceQueueBuffers(currentMusic->source, 1, &buffer);
+	}
+ 
+	return true;
+}
+*/
+extern bool MusicStreamUpdate()
+{
+    int processed;
+    bool active = true;
+ 
+    alGetSourcei(currentMusic->source, AL_BUFFERS_PROCESSED, &processed);
+    
+    printf("Data processed: %i\n", processed);
+ 
+    while (processed--)
+    {
+        ALuint buffer = 0;
+        
+        alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
+
+        active = MusicStream(*currentMusic, buffer);
+ 
+        alSourceQueueBuffers(currentMusic->source, 1, &buffer);
+    }
+ 
+    return active;
+}
 
+void MusicStreamEmpty()
+{
+    int queued;
+    
+    alGetSourcei(currentMusic->source, AL_BUFFERS_QUEUED, &queued);
+    
+    while(queued--)
+    {
+        ALuint buffer;  
+        alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
+    }
+}

+ 51 - 37
src/core.c

@@ -92,6 +92,9 @@ static Color background = { 0, 0, 0, 0 };   // Screen background color
 extern void LoadDefaultFont();               // [Module: text] Loads default font on InitWindow()
 extern void UnloadDefaultFont();             // [Module: text] Unloads default font from GPU memory
 
+extern bool MusicStreamUpdate();             // [Module: audio] Updates buffers for music streamming
+extern void PlayCurrentMusic();              // [Module: audio] Plays current music stream
+
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
@@ -117,7 +120,7 @@ void InitWindowEx(int width, int height, const char* title, bool resizable, cons
 {
     glfwSetErrorCallback(ErrorCallback);
     
-    if (!glfwInit()) exit(1);
+    if (!glfwInit()) TraceLog(ERROR, "Failed to initialize GLFW");
     
     //glfwDefaultWindowHints()                  // Set default windows hints
     
@@ -140,7 +143,7 @@ void InitWindowEx(int width, int height, const char* title, bool resizable, cons
     if (!window)
     {
         glfwTerminate();
-        exit(1);
+        TraceLog(ERROR, "Failed to initialize Window");
     }
     
     glfwSetWindowSizeCallback(window, WindowSizeCallback);
@@ -154,7 +157,7 @@ void InitWindowEx(int width, int height, const char* title, bool resizable, cons
                                     // Framerate can be setup using SetTargetFPS()
 
     //------------------------------------------------------ 
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     rlglInit();                     // Init rlgl
 #endif
     //------------------------------------------------------
@@ -183,7 +186,7 @@ void CloseWindow()
     UnloadDefaultFont();
     
     //------------------------------------------------------
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     rlglClose();                    // De-init rlgl
 #endif
     //------------------------------------------------------
@@ -230,13 +233,21 @@ void ToggleFullscreen()
         // TODO: WARNING! All loaded resources are lost, we loose Context!
 
         // NOTE: Window aspect ratio is always windowWidth / windowHeight
-        if (fullscreen) window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, glfwGetPrimaryMonitor(), NULL);    // Fullscreen mode
+        if (fullscreen)
+        {
+            // TODO: Get desktop window size and adapt aspect-ratio (?)
+            //const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
+            //windowWidth = mode->width;
+            //windowHeight = mode->height;
+            
+            window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, glfwGetPrimaryMonitor(), NULL);    // Fullscreen mode
+        }
         else window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, NULL, NULL);
-    
+
         if (!window)
         {
             glfwTerminate();
-            exit(1);
+            TraceLog(ERROR, "Failed to initialize Window when switching fullscreen mode");
         }
         
         glfwMakeContextCurrent(window);
@@ -285,7 +296,7 @@ void EndDrawing()
     if (customCursor && cursorOnScreen) DrawTexture(cursor, GetMouseX(), GetMouseY(), WHITE);
 
     //------------------------------------------------------
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     rlglDraw();                         //  Draw Buffers
 #endif
     //------------------------------------------------------
@@ -293,6 +304,10 @@ void EndDrawing()
     glfwSwapBuffers(window);            // Swap back and front buffers
     glfwPollEvents();                   // Register keyboard/mouse events
     
+    //MusicStreamUpdate();
+    //if (!MusicIsPlaying()) 
+    //PlayCurrentMusic();
+    
     currentTime = glfwGetTime();
     drawTime = currentTime - previousTime;
     previousTime = currentTime;
@@ -315,7 +330,7 @@ void EndDrawing()
 void Begin3dMode(Camera camera)
 {
     //------------------------------------------------------
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     rlglDraw();                         //  Draw Buffers
 #endif
     //------------------------------------------------------
@@ -344,7 +359,7 @@ void Begin3dMode(Camera camera)
 void End3dMode()
 {
     //------------------------------------------------------
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     rlglDraw();                         //  Draw Buffers
 #endif
     //------------------------------------------------------
@@ -363,7 +378,7 @@ void SetTargetFPS(int fps)
 {
     targetTime = 1 / (float)fps;
     
-    printf("TargetTime per Frame: %f seconds\n", (float)targetTime);
+    TraceLog(INFO, "Target time per frame: %02.03f milliseconds", (float)targetTime*1000);
 }
 
 // Returns current FPS
@@ -431,18 +446,18 @@ Color Fade(Color color, float alpha)
 // Detect if a key has been pressed once
 bool IsKeyPressed(int key)
 {   
-    bool ret = false;
+    bool pressed = false;
 
     currentKeyState[key] = IsKeyDown(key);
 
     if (currentKeyState[key] != previousKeyState[key])
     {
-        if (currentKeyState[key]) ret = true;
+        if (currentKeyState[key]) pressed = true;
         previousKeyState[key] = currentKeyState[key];
     }
-    else ret = false;
+    else pressed = false;
     
-    return ret;
+    return pressed;
 }
 
 // Detect if a key is being pressed (key held down)
@@ -455,18 +470,18 @@ bool IsKeyDown(int key)
 // Detect if a key has been released once
 bool IsKeyReleased(int key)
 {   
-    bool ret = false;
+    bool released = false;
     
     currentKeyState[key] = IsKeyUp(key);
 
     if (currentKeyState[key] != previousKeyState[key])
     {
-        if (currentKeyState[key]) ret = true;
+        if (currentKeyState[key]) released = true;
         previousKeyState[key] = currentKeyState[key];
     }
-    else ret = false;
+    else released = false;
     
-    return ret;
+    return released;
 }
 
 // Detect if a key is NOT being pressed (key not held down)
@@ -479,18 +494,18 @@ bool IsKeyUp(int key)
 // Detect if a mouse button has been pressed once
 bool IsMouseButtonPressed(int button)
 {
-    bool ret = false;
+    bool pressed = false;
 
     currentMouseState[button] = IsMouseButtonDown(button);
 
     if (currentMouseState[button] != previousMouseState[button])
     {
-        if (currentMouseState[button]) ret = true;
+        if (currentMouseState[button]) pressed = true;
         previousMouseState[button] = currentMouseState[button];
     }
-    else ret = false;
+    else pressed = false;
     
-    return ret;
+    return pressed;
 }
 
 // Detect if a mouse button is being pressed
@@ -503,18 +518,18 @@ bool IsMouseButtonDown(int button)
 // Detect if a mouse button has been released once
 bool IsMouseButtonReleased(int button)
 {
-    bool ret = false;
+    bool released = false;
 
     currentMouseState[button] = IsMouseButtonUp(button);
 
     if (currentMouseState[button] != previousMouseState[button])
     {
-        if (currentMouseState[button]) ret = true;
+        if (currentMouseState[button]) released = true;
         previousMouseState[button] = currentMouseState[button];
     }
-    else ret = false;
+    else released = false;
     
-    return ret;
+    return released;
 }
 
 // Detect if a mouse button is NOT being pressed
@@ -603,18 +618,18 @@ Vector2 GetGamepadMovement(int gamepad)
 // Detect if a gamepad button is being pressed
 bool IsGamepadButtonPressed(int gamepad, int button)
 {
-    bool ret = false;
+    bool pressed = false;
 
     currentGamepadState[button] = IsGamepadButtonDown(gamepad, button);
 
     if (currentGamepadState[button] != previousGamepadState[button])
     {
-        if (currentGamepadState[button]) ret = true;
+        if (currentGamepadState[button]) pressed = true;
         previousGamepadState[button] = currentGamepadState[button];
     }
-    else ret = false;
+    else pressed = false;
     
-    return ret;
+    return pressed;
 }
 
 bool IsGamepadButtonDown(int gamepad, int button)
@@ -634,18 +649,18 @@ bool IsGamepadButtonDown(int gamepad, int button)
 // Detect if a gamepad button is NOT being pressed
 bool IsGamepadButtonReleased(int gamepad, int button)
 {
-    bool ret = false;
+    bool released = false;
 
     currentGamepadState[button] = IsGamepadButtonUp(gamepad, button);
 
     if (currentGamepadState[button] != previousGamepadState[button])
     {
-        if (currentGamepadState[button]) ret = true;
+        if (currentGamepadState[button]) released = true;
         previousGamepadState[button] = currentGamepadState[button];
     }
-    else ret = false;
+    else released = false;
     
-    return ret;
+    return released;
 }
 
 bool IsGamepadButtonUp(int gamepad, int button)
@@ -669,8 +684,7 @@ bool IsGamepadButtonUp(int gamepad, int button)
 // GLFW3 Error Callback, runs on GLFW3 error
 static void ErrorCallback(int error, const char *description)
 {
-    printf(description);
-    //fprintf(stderr, description);
+    TraceLog(WARNING, "GLFW3 Error: %s", description);
 }
 
 // GLFW3 Srolling Callback, runs on mouse wheel

+ 36 - 46
src/models.c

@@ -568,53 +568,48 @@ void DrawGizmo(Vector3 position)
     rlPopMatrix();
 }
 
-void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits)
+void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits)
 {
+    static float rotation = 0;
     // NOTE: RGB = XYZ
     rlPushMatrix();
         rlTranslatef(position.x, position.y, position.z);
+        rlRotatef(rotation, 0, 1, 0);
         rlScalef(scale, scale, scale);
-        
-        if(rotation.x) rlRotatef(rotation.x, 1, 0, 0);
-        if(rotation.y) rlRotatef(rotation.y, 0, 1, 0);
-        if(rotation.z) rlRotatef(rotation.z, 0, 0, 1);
-    
+
         rlBegin(RL_LINES);
             // X Axis
-            rlColor4ub(200, 0, 0, 255); // RED
-            rlVertex3f(position.x, position.y, position.z);
-            rlVertex3f(position.x + 1, position.y, position.z);
+            rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x, position.y, position.z);
+            rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1, position.y, position.z);
             
             // ArrowX
-            rlVertex3f(position.x + 1.1, position.y, position.z);
-            rlVertex3f(position.x + .9, position.y, position.z + .1);
+            rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z);
+            rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z + .1);
             
-            rlVertex3f(position.x + 1.1, position.y, position.z);
-            rlVertex3f(position.x + .9, position.y, position.z - .1);
+            rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + 1.1, position.y, position.z);
+            rlColor4ub(200, 0, 0, 255); rlVertex3f(position.x + .9, position.y, position.z - .1);
             
             // Y Axis
-            rlColor4ub(0, 200, 0, 255); // GREEN
-            rlVertex3f(position.x, position.y, position.z);
-            rlVertex3f(position.x, position.y + 1, position.z);
+            rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y, position.z);
+            rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1, position.z);
             
             // ArrowY
-            rlVertex3f(position.x, position.y + 1.1, position.z);
-            rlVertex3f(position.x + .1, position.y + .9, position.z);
+            rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z);
+            rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + .1, position.y + .9, position.z);
             
-            rlVertex3f(position.x, position.y + 1.1, position.z);
-            rlVertex3f(position.x - .1, position.y + .9, position.z);
+            rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x, position.y + 1.1, position.z);
+            rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x - .1, position.y + .9, position.z);
             
             // Z Axis
-            rlColor4ub(0, 0, 200, 255); // BLUE
-            rlVertex3f(position.x, position.y, position.z);
-            rlVertex3f(position.x, position.y, position.z - 1);
+            rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z);
+            rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1);
             
             // ArrowZ
-            rlVertex3f(position.x, position.y, position.z - 1.1);
-            rlVertex3f(position.x + .1, position.y, position.z - .9);
+            rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1);
+            rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + .1, position.y, position.z - .9);
             
-            rlVertex3f(position.x, position.y, position.z - 1.1);
-            rlVertex3f(position.x - .1, position.y, position.z - .9);
+            rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x, position.y, position.z - 1.1);
+            rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x - .1, position.y, position.z - .9);
             
             // Extra
             if(orbits)
@@ -622,31 +617,30 @@ void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits)
                 int n = 3;
                 
                 // X Axis
-                rlColor4ub(200, 0, 0, 255); // RED
-                for (int i=0; i < 360; i++)
+                for (int i=0; i < 360; i += 6)
                 {
-                    rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n);
-                    rlVertex3f(0,position.x + sin(DEG2RAD*(i+1)) * scale/n, position.y + cos(DEG2RAD*(i+1)) * scale/n);
+                    rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n);
+                    rlColor4ub(200, 0, 0, 255); rlVertex3f(0, position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n);
                 }
                 
                 // Y Axis
-                rlColor4ub(0, 200, 0, 255); // GREEN
-                for (int i=0; i < 360; i++)
+                for (int i=0; i < 360; i += 6)
                 {
-                    rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n);
-                    rlVertex3f(position.x + sin(DEG2RAD*(i+1)) * scale/n, 0, position.y + cos(DEG2RAD*(i+1)) * scale/n);
+                    rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, 0, position.y + cos(DEG2RAD*i) * scale/n);
+                    rlColor4ub(0, 200, 0, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, 0, position.y + cos(DEG2RAD*(i+6)) * scale/n);
                 }
                 
                 // Z Axis
-                rlColor4ub(0, 0, 200, 255); // BLUE
-                for (int i=0; i < 360; i++)
+                for (int i=0; i < 360; i += 6)
                 {
-                    rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0);
-                    rlVertex3f(position.x + sin(DEG2RAD*(i+1)) * scale/n, position.y + cos(DEG2RAD*(i+1)) * scale/n, 0);
+                    rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*i) * scale/n, position.y + cos(DEG2RAD*i) * scale/n, 0);
+                    rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x + sin(DEG2RAD*(i+6)) * scale/n, position.y + cos(DEG2RAD*(i+6)) * scale/n, 0);
                 }
             }
         rlEnd();
     rlPopMatrix();
+    
+    rotation += 0.1f;
 }
 
 // Load a 3d model (.OBJ)
@@ -696,7 +690,6 @@ Model LoadModel(const char *fileName)
                     {
                         fscanf(objFile, "%i", &numTexCoords);
                     }
-                    else printf("Ouch! Something was wrong...");
                     
                     fgets(comments, 200, objFile);
                 }
@@ -715,7 +708,6 @@ Model LoadModel(const char *fileName)
                     {
                         fscanf(objFile, "%i", &numNormals);
                     }
-                    else printf("Ouch! Something was wrong...");
                 
                     fgets(comments, 200, objFile);
                 }
@@ -734,7 +726,6 @@ Model LoadModel(const char *fileName)
                     {
                         fscanf(objFile, "%i", &numVertex);
                     }
-                    else printf("Ouch! Something was wrong...");
                     
                     fgets(comments, 200, objFile);
                 }
@@ -754,7 +745,6 @@ Model LoadModel(const char *fileName)
                 {
                     fscanf(objFile, "%i", &numTriangles);
                 }
-                else printf("Ouch! Something was wrong...");
                 
                 fgets(comments, 200, objFile);
             
@@ -1024,7 +1014,7 @@ void UnloadModel(Model model)
     free(model.data.normals);
 #endif
 
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     rlDeleteVertexArrays(model.vaoId);
 #endif
 }
@@ -1032,7 +1022,7 @@ void UnloadModel(Model model)
 // Draw a model
 void DrawModel(Model model, Vector3 position, float scale, Color color)
 {
-    rlglDrawModel(model, false);
+    rlglDrawModel(model, position, scale, false);
 }
 
 // Draw a textured model
@@ -1048,7 +1038,7 @@ void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale,
 // Draw a model wires
 void DrawModelWires(Model model, Vector3 position, float scale, Color color)
 {
-    rlglDrawModel(model, true);
+    rlglDrawModel(model, position, scale, true);
 }
 
 // Draw a billboard

+ 51 - 27
src/raylib.h

@@ -54,6 +54,8 @@
 #ifndef RAYLIB_H
 #define RAYLIB_H 
 
+#include "stb_vorbis.h"
+
 //----------------------------------------------------------------------------------
 // Some basic Defines
 //----------------------------------------------------------------------------------
@@ -153,6 +155,19 @@
 // Boolean type
 typedef enum { false, true } bool;
 
+// Vector2 type
+typedef struct Vector2 {
+    float x;
+    float y;
+} Vector2;
+
+// Vector3 type
+typedef struct Vector3 {
+    float x;
+    float y;
+    float z;
+} Vector3;
+
 // Color type, RGBA (32bit)
 typedef struct Color {
     unsigned char r;
@@ -185,45 +200,49 @@ typedef struct Texture2D {
     int height;
 } Texture2D;
 
-// SpriteFont one Character (Glyph) data, defined in text module
+// Camera type, defines a camera position/orientation in 3d space
+typedef struct Camera {
+    Vector3 position;
+    Vector3 target;
+    Vector3 up;
+} Camera;
+
 typedef struct Character Character;
 
-// SpriteFont type, includes texture and charSet array data
+// SpriteFont type
 typedef struct SpriteFont {
     Texture2D texture;
     int numChars;
     Character *charSet;
 } SpriteFont;
 
-// Vector2 type
-typedef struct Vector2 {
-    float x;
-    float y;
-} Vector2;
-
-// Vector3 type
-typedef struct Vector3 {
-    float x;
-    float y;
-    float z;
-} Vector3;
-
-// Camera type, defines a camera position/orientation in 3d space
-typedef struct Camera {
-    Vector3 position;
-    Vector3 target;
-    Vector3 up;
-} Camera;
+// 3d Model type
+// NOTE: If using OpenGL 1.1 loaded in CPU; if OpenGL 3.3+ loaded in GPU
+typedef struct Model Model; // Defined in module: rlgl
 
-// Basic 3d Model type
-typedef struct Model Model;
-
-// Basic Sound source and buffer
+// Sound source type
 typedef struct Sound {
     unsigned int source;
     unsigned int buffer;
 } Sound;
 
+typedef struct OggStream OggStream;
+
+// Music type (streamming)
+typedef struct Music {
+    stb_vorbis *stream;
+	stb_vorbis_info info;
+    
+    unsigned int source;
+	unsigned int buffers[2];
+
+	int format;
+ 
+	int bufferSize;
+	int totalSamplesLeft;
+	bool loop;
+} Music;
+
 #ifdef __cplusplus
 extern "C" {            // Prevents name mangling of functions
 #endif
@@ -365,7 +384,7 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color);
 void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions
 void DrawGrid(int slices, float spacing);                                                          // Draw a grid (centered at (0, 0, 0))
 void DrawGizmo(Vector3 position);                                                                  // Draw simple gizmo
-void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale, bool orbits);                    // Draw gizmo with extended parameters
+void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits);                    // Draw gizmo with extended parameters
 //DrawTorus(), DrawTeapot() are useless...
 
 //------------------------------------------------------------------------------------
@@ -389,13 +408,18 @@ void CloseAudioDevice();                                        // Close the aud
 Sound LoadSound(char *fileName);                                // Load sound to memory
 Sound LoadSoundFromRES(const char *rresName, int resId);        // Load sound to memory from rRES file (raylib Resource)
 void UnloadSound(Sound sound);                                  // Unload sound
+Music LoadMusic(char *fileName);
+void UnloadMusic(Music music);
 
 void PlaySound(Sound sound);                                    // Play a sound
 void PauseSound(Sound sound);                                   // Pause a sound
 void StopSound(Sound sound);                                    // Stop playing a sound
-bool IsPlaying(Sound sound);                                    // Check if a sound is currently playing
+bool SoundIsPlaying(Sound sound);                               // Check if a sound is currently playing
 void SetVolume(Sound sound, float volume);                      // Set volume for a sound (1.0 is base level)
 void SetPitch(Sound sound, float pitch);                        // Set pitch for a sound (1.0 is base level)
+void PlayMusic(Music music);
+void StopMusic(Music music);
+bool MusicIsPlaying();
 
 #ifdef __cplusplus
 }

+ 146 - 52
src/rlgl.c

@@ -31,7 +31,7 @@
 #include <stdio.h>          // Standard input / output lib
 #include <stdlib.h>         // Declares malloc() and free() for memory management, rand()
 
-#include "raymath.h"        // Required for data type Matrix and Matrix functions
+// TODO: Security check in case multiple USE_OPENGL_* defined
 
 #ifdef USE_OPENGL_11
     #include <GL/gl.h>      // Extensions loading lib
@@ -51,7 +51,7 @@
 //----------------------------------------------------------------------------------
 #define MATRIX_STACK_SIZE          16   // Matrix stack max size
 #define MAX_DRAWS_BY_TEXTURE      256   // Draws are organized by texture changes
-#define TEMP_VERTEX_BUFFER_SIZE  1024   // Temporal Vertex Buffer (required for vertex-transformations)
+#define TEMP_VERTEX_BUFFER_SIZE  4096   // Temporal Vertex Buffer (required for vertex-transformations)
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
@@ -226,9 +226,7 @@ void rlPushMatrix()
 {
     if (stackCounter == MATRIX_STACK_SIZE - 1)
     {
-        printf("ERROR: Stack Buffer Overflow! (MAX 16 MATRIX)");
-        
-        exit(1);
+        TraceLog(ERROR, "Stack Buffer Overflow (MAX %i Matrix)", MATRIX_STACK_SIZE);
     }
 
     stack[stackCounter] = *currentMatrix;
@@ -667,17 +665,22 @@ void rlglInit()
     
     if (error != GLEW_OK) 
     {
-        fprintf(stderr, "Failed to initialize GLEW - Error: %s\n", glewGetErrorString(error));
-        exit(1);
+        TraceLog(ERROR, "Failed to initialize GLEW - Error Code: %s\n", glewGetErrorString(error));
     }
 
-    if (glewIsSupported("GL_VERSION_3_3")) printf("OpenGL 3.3 initialized\n"); 
+    if (glewIsSupported("GL_VERSION_3_3")) TraceLog(INFO, "OpenGL 3.3 initialized\n");
+    
+    // Print OpenGL and GLSL version
+    TraceLog(INFO, "Vendor:   %s", glGetString(GL_VENDOR));
+    TraceLog(INFO, "Renderer: %s", glGetString(GL_RENDERER));
+    TraceLog(INFO, "Version:  %s", glGetString(GL_VERSION));
+    TraceLog(INFO, "GLSL:     %s\n", glGetString(0x8B8C));  //GL_SHADING_LANGUAGE_VERSION
+    
 /*
     // TODO: GLEW is a big library that loads ALL extensions, maybe using glad we can only load required ones...
     if (!gladLoadGL()) 
     {
-        fprintf(stderr, printf("Failed to initialize glad.\n");
-        exit(1);
+        TraceLog("ERROR: Failed to initialize glad\n");
     }
 */    
     // Set default draw mode
@@ -707,13 +710,7 @@ void rlglInit()
     // Get handles to GLSL uniform vars locations (fragment-shader)
     textureLoc  = glGetUniformLocation(shaderProgram, "texture0");
     
-    printf("Default shaders loaded\n\n");
-    
-    // Print OpenGL and GLSL version
-    printf("Vendor:   %s\n", glGetString(GL_VENDOR));
-    printf("Renderer: %s\n", glGetString(GL_RENDERER));
-    printf("Version:  %s\n", glGetString(GL_VERSION));
-    printf("GLSL:     %s\n\n", glGetString(0x8B8C));  //GL_SHADING_LANGUAGE_VERSION
+    TraceLog(INFO, "Default shader loaded");
     
     InitializeBuffers();    // Init vertex arrays
     InitializeVAOs();       // Init VBO and VAO
@@ -726,7 +723,10 @@ void rlglInit()
     // Create default white texture for plain colors (required by shader)
     unsigned char pixels[4] = { 255, 255, 255, 255 };   // 1 pixel RGBA (4 bytes)
     
-    whiteTexture = rlglLoadTexture(1, 1, pixels);     
+    whiteTexture = rlglLoadTexture(1, 1, pixels);
+    
+    if (whiteTexture != 0) TraceLog(INFO, "Base white texture successfully created, id: %i", whiteTexture);
+    else TraceLog(WARNING, "Base white texture could not be created");
     
     // Init draw calls tracking system
     draws = (DrawCall *)malloc(sizeof(DrawCall)*MAX_DRAWS_BY_TEXTURE);
@@ -836,16 +836,14 @@ void rlglDraw()
             glBindVertexArray(vaoQuads);
         }
         
-        //printf("\nRequired Draws: %i\n", drawsCounter);
+        //TraceLog(INFO, "Draws required per frame: %i", drawsCounter);
      
         for (int i = 0; i < drawsCounter; i++)
         {
             numQuads = draws[i].vCount/4;
             numIndicesToProcess = numQuads*6;  // Get number of Quads * 6 index by Quad
             
-            //printf("Quads to render: %i - ", numQuads);
-            //printf("Vertex Count: %i - ", draws[i].vCount);
-            //printf("Binding texture: %i\n", draws[i].texId);
+            //TraceLog(INFO, "Quads to render: %i - Vertex Count: %i", numQuads, draws[i].vCount);
 
             glBindTexture(GL_TEXTURE_2D, draws[i].texId);
             
@@ -882,7 +880,10 @@ void rlglDraw()
 #endif
 }
 
-void rlglDrawModel(Model model, bool wires)
+#endif      // End for OpenGL 3.3+ and ES2 only functions
+
+// Draw a 3d model
+void rlglDrawModel(Model model, Vector3 position, float scale, bool wires)
 {
     if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
     
@@ -892,19 +893,19 @@ void rlglDrawModel(Model model, bool wires)
     glEnableClientState(GL_TEXTURE_COORD_ARRAY);              // Enable texture coords array
     glEnableClientState(GL_NORMAL_ARRAY);                     // Enable normals array
         
-    glVertexPointer(3, GL_FLOAT, 0, model.vertices);          // Pointer to vertex coords array
-    glTexCoordPointer(2, GL_FLOAT, 0, model.texcoords);       // Pointer to texture coords array
-    glNormalPointer(GL_FLOAT, 0, model.normals);              // Pointer to normals array
+    glVertexPointer(3, GL_FLOAT, 0, model.data.vertices);     // Pointer to vertex coords array
+    glTexCoordPointer(2, GL_FLOAT, 0, model.data.texcoords);  // Pointer to texture coords array
+    glNormalPointer(GL_FLOAT, 0, model.data.normals);         // Pointer to normals array
     //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors);   // Pointer to colors array (NOT USED)
         
     rlPushMatrix();
         rlTranslatef(position.x, position.y, position.z);
-        //glRotatef(rotation * GetFrameTime(), 0, 1, 0);
+        //rlRotatef(rotation * GetFrameTime(), 0, 1, 0);
         rlScalef(scale, scale, scale);
         
-        rlColor4ub(color.r, color.g, color.b, color.a);
+        rlColor4ub(1.0f, 1.0f, 1.0f, 1.0f);
 
-        glDrawArrays(GL_TRIANGLES, 0, model.numVertices);
+        glDrawArrays(GL_TRIANGLES, 0, model.data.numVertices);
     rlPopMatrix();
     
     glDisableClientState(GL_VERTEX_ARRAY);                     // Disable vertex array
@@ -912,7 +913,7 @@ void rlglDrawModel(Model model, bool wires)
     glDisableClientState(GL_NORMAL_ARRAY);                     // Disable normals array
 #endif
 
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     glUseProgram(shaderProgram);        // Use our shader
     
     Matrix modelview2 = MatrixMultiply(model.transform, modelview);
@@ -934,8 +935,6 @@ void rlglDrawModel(Model model, bool wires)
     if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 }
 
-#endif
-
 // Initialize Graphics Device (OpenGL stuff)
 void rlglInitGraphicsDevice(int fbWidth, int fbHeight)
 {
@@ -968,7 +967,7 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight)
     rlMatrixMode(RL_MODELVIEW);                 // Switch back to MODELVIEW matrix
     rlLoadIdentity();                           // Reset current matrix (MODELVIEW)
     
-    // TODO: Review all shapes/models are drawn CCW and enable backface culling
+    // NOTE: All shapes/models triangles are drawn CCW
 
     glEnable(GL_CULL_FACE);       // Enable backface culling (Disabled by default)
     //glCullFace(GL_BACK);        // Cull the Back face (default)
@@ -978,11 +977,13 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight)
     glShadeModel(GL_SMOOTH);      // Smooth shading between vertex (vertex colors interpolation) (Deprecated on OpenGL 3.3+)
                                   // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation)
 #endif
+
+    TraceLog(INFO, "OpenGL graphics device initialized");
 }
 
 // Convert image data to OpenGL texture (returns OpenGL valid Id)
 // NOTE: Image is not unloaded, it should be done manually...
-unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels)
+unsigned int rlglLoadTexture(int width, int height, unsigned char *data)
 {
     glBindTexture(GL_TEXTURE_2D,0); // Free any old binding
 
@@ -998,7 +999,7 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels)
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  // Filter for pixel-perfect drawing, alternative: GL_LINEAR 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  // Filter for pixel-perfect drawing, alternative: GL_LINEAR
  
-#ifdef USE_OPENGL_33 
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     // Trilinear filtering
     //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);   // Activate use of mipmaps (must be available)
@@ -1008,20 +1009,84 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels)
     // NOTE: Not using mipmappings (texture for 2D drawing)
     // At this point we have the image converted to texture and uploaded to GPU
     
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
     
     // At this point we have the image converted to texture and uploaded to GPU
     
     // Unbind current texture
     glBindTexture(GL_TEXTURE_2D, 0);
     
-    printf("New texture created, id: %i (%i x %i)\n", id, width, height);
+    TraceLog(INFO, "New texture created, id: %i (%i x %i)", id, width, height);
     
     return id;
 }
 
-#ifdef USE_OPENGL_33 
-unsigned int rlglLoadModel(VertexData data)
+
+#ifdef USE_OPENGL_33
+
+#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
+#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
+#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
+
+// Convert image data to OpenGL texture (returns OpenGL valid Id)
+// NOTE: Expected compressed data from DDS file
+unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format)
+{
+    // Create one OpenGL texture
+    GLuint id;
+    int compFormat = 0;
+    
+    TraceLog(DEBUG, "Compressed texture width: %i", width);
+    TraceLog(DEBUG, "Compressed texture height: %i", height);
+    TraceLog(DEBUG, "Compressed texture mipmap levels: %i", mipmapCount);
+    TraceLog(DEBUG, "Compressed texture format: 0x%x", format);
+    
+    switch(format) 
+    { 
+        case FOURCC_DXT1: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; 
+        case FOURCC_DXT3: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; 
+        case FOURCC_DXT5: compFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; 
+        default: compFormat = -1; break;
+    }
+    
+    if (compFormat == -1)
+    {
+        TraceLog(WARNING, "Texture compressed format not recognized");
+        id = 0;
+    }
+    else
+    {
+        glGenTextures(1, &id);
+
+        // Bind the texture
+        glBindTexture(GL_TEXTURE_2D, id);
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    
+        unsigned int blockSize = (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16; 
+        unsigned int offset = 0;
+        
+        // Load the mipmaps 
+        for (int level = 0; level < mipmapCount && (width || height); level++) 
+        { 
+            unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
+            
+            glCompressedTexImage2D(GL_TEXTURE_2D, level, compFormat, width, height, 0, size, data + offset); 
+         
+            offset += size;
+            width  /= 2; 
+            height /= 2; 
+
+            // Security check for NPOT textures
+            if (width < 1) width = 1;
+            if (height < 1) height = 1;
+        }
+    }
+
+    return id;
+}
+
+// Load vertex data into a VAO
+unsigned int rlglLoadModel(VertexData mesh)
 {
     GLuint vaoModel;            // Vertex Array Objects (VAO)
     GLuint vertexBuffer[3];     // Vertex Buffer Objects (VBO)
@@ -1035,17 +1100,17 @@ unsigned int rlglLoadModel(VertexData data)
  
     // Enable vertex attributes
     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*data.numVertices, data.vertices, GL_STATIC_DRAW);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.numVertices, mesh.vertices, GL_STATIC_DRAW);
     glEnableVertexAttribArray(vertexLoc);
     glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0);
     
     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*data.numVertices, data.texcoords, GL_STATIC_DRAW);      
+    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.numVertices, mesh.texcoords, GL_STATIC_DRAW);      
     glEnableVertexAttribArray(texcoordLoc);
     glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, 0, 0, 0);
     
     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*data.numVertices, data.normals, GL_STATIC_DRAW);   
+    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.numVertices, mesh.normals, GL_STATIC_DRAW);   
     //glEnableVertexAttribArray(normalLoc);
     //glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0);
     
@@ -1077,7 +1142,7 @@ unsigned char *rlglReadScreenPixels(int width, int height)
     return imgData;     // NOTE: image data should be freed
 }
 
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
 
 void PrintProjectionMatrix()
 {
@@ -1154,7 +1219,7 @@ static GLuint LoadDefaultShaders()
     glDeleteShader(vertexShader);
     glDeleteShader(fragmentShader);
  
-    return(program);
+    return program;
 }
 
 
@@ -1191,14 +1256,14 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName)
     glDeleteShader(vertexShader);
     glDeleteShader(fragmentShader);
  
-    return(program);
+    return program;
 }
 
 // Read shader text file
 static char *TextFileRead(char *fn) 
 {
     FILE *fp;
-    char *content = NULL;
+    char *text = NULL;
 
     int count=0;
 
@@ -1214,15 +1279,15 @@ static char *TextFileRead(char *fn)
 
             if (count > 0) 
             {
-                content = (char *)malloc(sizeof(char) * (count+1));
-                count = fread(content, sizeof(char), count, fp);
-                content[count] = '\0';
+                text = (char *)malloc(sizeof(char) * (count+1));
+                count = fread(text, sizeof(char), count, fp);
+                text[count] = '\0';
             }
             
             fclose(fp);
         }
     }
-    return content;
+    return text;
 }
 
 // Allocate and initialize float array buffers to store vertex data (lines, triangles, quads)
@@ -1377,10 +1442,10 @@ static void InitializeVAOs()
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBufferB[3]);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW);
     
-    printf("Using VBO double buffering\n");
+    TraceLog(INFO, "Using VBO double buffering");
 #endif
  
-    printf("Vertex buffers initialized (lines, triangles, quads)\n\n");
+    TraceLog(INFO, "Vertex buffers successfully initialized (lines, triangles, quads)\n");
  
     // Unbind the current VAO
     glBindVertexArray(0);
@@ -1475,4 +1540,33 @@ static void UpdateBuffers()
     glBindVertexArray(0);
 }
 
+#endif
+
+#ifdef RLGL_STANDALONE
+
+typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
+
+// Output a trace log message
+// NOTE: Expected msgType: (0)Info, (1)Error, (2)Warning
+void TraceLog(int msgType, const char *text, ...)
+{
+    va_list args;
+    va_start(args, text);
+    
+    switch(msgType)
+    {
+        case 0: fprintf(stdout, "INFO: "); break;
+        case 1: fprintf(stdout, "ERROR: "); break;
+        case 2: fprintf(stdout, "WARNING: "); break;
+        case 3: fprintf(logstream, "DEBUG: "); break;
+        default: break;
+    }
+    
+    vfprintf(stdout, text, args);
+    fprintf(stdout, "\n");
+    
+    va_end(args);
+    
+    if (msgType == 1) exit(1);
+}
 #endif

+ 11 - 7
src/rlgl.h

@@ -33,17 +33,20 @@
 
 #ifndef RLGL_STANDALONE
     #include "raylib.h"         // Required for typedef: Model
+    #include "utils.h"          // Required for function TraceLog()
 #endif
 
+#include "raymath.h"        // Required for data type Matrix and Matrix functions
+
 // Select desired OpenGL version
-#define USE_OPENGL_11
-//#define USE_OPENGL_33
+//#define USE_OPENGL_11
+#define USE_OPENGL_33
 //#define USE_OPENGL_ES2
 
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-#define MAX_LINES_BATCH         1024
+#define MAX_LINES_BATCH         8192    // 1024
 #define MAX_TRIANGLES_BATCH     2048
 #define MAX_QUADS_BATCH         8192
 
@@ -57,7 +60,7 @@ typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode;
 typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode;
 
 #ifdef RLGL_STANDALONE
-    typedef struct Model Model;
+typedef struct Model Model;
 #endif
 
 typedef struct {
@@ -125,19 +128,20 @@ void rlClearScreenBuffers();                // Clear used screen buffers (color
 //------------------------------------------------------------------------------------
 // Functions Declaration - rlgl functionality
 //------------------------------------------------------------------------------------
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
 void rlglInit();                                // Initialize rlgl (shaders, VAO, VBO...)
 void rlglClose();                               // De-init rlgl
 void rlglDraw();                                // Draw VAOs
 unsigned int rlglLoadModel(VertexData data);
+unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format);
 #endif
 
-void rlglDrawModel(Model model, bool wires);    // Draw model
+void rlglDrawModel(Model model, Vector3 position, float scale, bool wires);   // Draw model
 void rlglInitGraphicsDevice(int fbWidth, int fbHeight);  // Initialize Graphics Device (OpenGL stuff)
 unsigned int rlglLoadTexture(int width, int height, unsigned char *pixels); // Load in GPU OpenGL texture
 byte *rlglReadScreenPixels(int width, int height);    // Read screen pixel data (color buffer)
 
-#ifdef USE_OPENGL_33
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
 void PrintProjectionMatrix();       // DEBUG: Print projection matrix
 void PrintModelviewMatrix();        // DEBUG: Print modelview matrix
 #endif

+ 4 - 0
src/stb_vorbis.c

@@ -11,6 +11,7 @@
 // Get the latest version and other information at:
 //     http://nothings.org/stb_vorbis/
 
+
 // Todo:
 //
 //   - seeking (note you can seek yourself using the pushdata API)
@@ -25,6 +26,9 @@
 // 
 // All of these limitations may be removed in future versions.
 
+
+#include "stb_vorbis.h"
+
 #ifndef STB_VORBIS_HEADER_ONLY
 
 // global configuration settings (e.g. set these in the project/makefile),

+ 2 - 7
src/stb_vorbis.h

@@ -11,6 +11,7 @@
 // Get the latest version and other information at:
 //     http://nothings.org/stb_vorbis/
 
+
 // Todo:
 //
 //   - seeking (note you can seek yourself using the pushdata API)
@@ -25,12 +26,6 @@
 // 
 // All of these limitations may be removed in future versions.
 
-
-//////////////////////////////////////////////////////////////////////////////
-//
-//  HEADER BEGINS HERE
-//
-
 #ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
 #define STB_VORBIS_INCLUDE_STB_VORBIS_H
 
@@ -349,4 +344,4 @@ enum STBVorbisError
 }
 #endif
 
-#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
+#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H

+ 21 - 13
src/text.c

@@ -50,13 +50,22 @@
 typedef unsigned char byte;
 
 // SpriteFont one Character (Glyph) data
-struct Character {
+typedef struct Character {
     int value;        //char value = ' '; (int)value = 32;
     int x;
     int y;
     int w;
     int h;
+} Character;
+
+// SpriteFont type, includes texture and charSet array data
+/*
+struct SpriteFont {
+    Texture2D texture;
+    int numChars;
+    Character *charSet;
 };
+*/
 
 //----------------------------------------------------------------------------------
 // Global variables
@@ -177,6 +186,8 @@ extern void LoadDefaultFont()
         }
         else currentPosX = testPosX;
     }
+    
+    TraceLog(INFO, "Default font loaded successfully");
 }
 
 extern void UnloadDefaultFont()
@@ -232,8 +243,8 @@ SpriteFont LoadSpriteFont(const char* fileName)
         // spriteFont.charSet data is filled inside the function and memory is allocated!
         int numChars = ParseImageData(imgDataPixel, imgWidth, imgHeight, &spriteFont.charSet);
         
-        fprintf(stderr, "SpriteFont data parsed correctly!\n");
-        fprintf(stderr, "SpriteFont num chars: %i\n", numChars);
+        TraceLog(INFO, "[%s] SpriteFont data parsed correctly", fileName);
+        TraceLog(INFO, "[%s] SpriteFont num chars detected: %i", numChars);
         
         spriteFont.numChars = numChars;
         
@@ -257,7 +268,7 @@ SpriteFont LoadSpriteFont(const char* fileName)
                 }
             }
             
-            fprintf(stderr, "SpriteFont texture converted to POT: %i %i\n", potWidth, potHeight);
+            TraceLog(WARNING, "SpriteFont texture converted to POT: %ix%i", potWidth, potHeight);
         }
         
         free(imgDataPixel);
@@ -347,7 +358,7 @@ const char *FormatText(const char *text, ...)
     
     va_list args;
     va_start(args, text);
-    vsprintf(buffer, text, args);        // NOTE: We use vsprintf() defined in <stdarg.h>
+    vsprintf(buffer, text, args);
     va_end(args);
 
     return buffer;
@@ -547,7 +558,7 @@ static SpriteFont LoadRBMF(const char *fileName)
 
     fread(&rbmfHeader, sizeof(rbmfInfoHeader), 1, rbmfFile);
     
-    //printf("rBMF info: %i %i %i %i\n", rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight);
+    TraceLog(INFO, "[%s] Loading rBMF file, size: %ix%i, numChars: %i, charHeight: %i", fileName, rbmfHeader.imgWidth, rbmfHeader.imgHeight, rbmfHeader.numChars, rbmfHeader.charHeight);
     
     spriteFont.numChars = (int)rbmfHeader.numChars;
     
@@ -564,8 +575,6 @@ static SpriteFont LoadRBMF(const char *fileName)
     
     for(int i = 0; i < spriteFont.numChars; i++) fread(&rbmfCharWidthData[i], sizeof(unsigned char), 1, rbmfFile);
     
-    printf("Just read image data and width data... Starting image reconstruction...");
-    
     // Re-construct image from rbmfFileData
     //-----------------------------------------
     image.pixels = (Color *)malloc(image.width * image.height * sizeof(Color));
@@ -585,13 +594,13 @@ static SpriteFont LoadRBMF(const char *fileName)
         counter++;
     }
     
-    printf("Image reconstructed correctly... now converting it to texture...");
+    TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
     
     spriteFont.texture = CreateTexture(image);
     
     UnloadImage(image);     // Unload image data
     
-    printf("Starting charSet reconstruction...\n");
+    TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName);
     
     // Reconstruct charSet using rbmfCharWidthData, rbmfHeader.charHeight, charsDivisor, rbmfHeader.numChars
     spriteFont.charSet = (Character *)malloc(spriteFont.numChars * sizeof(Character));     // Allocate space for our character data
@@ -620,11 +629,9 @@ static SpriteFont LoadRBMF(const char *fileName)
             spriteFont.charSet[i].y = charsDivisor + currentLine * (rbmfHeader.charHeight + charsDivisor);
         }
         else currentPosX = testPosX;
-        
-        //printf("Char %i data: %i %i %i %i\n", spriteFont.charSet[i].value, spriteFont.charSet[i].x, spriteFont.charSet[i].y, spriteFont.charSet[i].w, spriteFont.charSet[i].h); 
     }
     
-    printf("CharSet reconstructed correctly... Data should be ready...\n");
+    TraceLog(INFO, "[%s] rBMF file loaded correctly as SpriteFont", fileName);
     
     fclose(rbmfFile);
     
@@ -634,6 +641,7 @@ static SpriteFont LoadRBMF(const char *fileName)
     return spriteFont;
 }
 
+// Get the extension for a filename
 static const char *GetExtension(const char *fileName) 
 {
     const char *dot = strrchr(fileName, '.');

+ 269 - 118
src/textures.c

@@ -29,8 +29,8 @@
 #include "raylib.h"
 
 #include <stdlib.h>          // Declares malloc() and free() for memory management
+#include <string.h>          // Required for strcmp(), strrchr(), strncmp()
 #include "stb_image.h"       // Used to read image data (multiple formats support)
-
 #include "utils.h"           // rRES data decompression utility function
 
 #include "rlgl.h"            // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
@@ -45,6 +45,14 @@
 //----------------------------------------------------------------------------------
 typedef unsigned char byte;
 
+typedef struct {
+    unsigned char *data;
+    int width;
+    int height;
+    int mipmaps;
+    int format;
+} ImageDDS;
+
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
@@ -58,7 +66,8 @@ typedef unsigned char byte;
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
-// No private (static) functions in this module (.c file)
+static const char *GetExtension(const char *fileName);
+static ImageDDS LoadDDS(const char *fileName);
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition
@@ -69,32 +78,44 @@ Image LoadImage(const char *fileName)
 {
     Image image;
     
-    int imgWidth;
-    int imgHeight;
-    int imgBpp;
-    
-    // NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...)
-    // Force loading to 4 components (RGBA)
-    byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4);    
-    
-    // Convert array to pixel array for working convenience
-    image.pixels = (Color *)malloc(imgWidth * imgHeight * sizeof(Color));
-    
-    int pix = 0;
-    
-    for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4)
-    {
-        image.pixels[pix].r = imgData[i];
-        image.pixels[pix].g = imgData[i+1];
-        image.pixels[pix].b = imgData[i+2];
-        image.pixels[pix].a = imgData[i+3];
-        pix++;
+    if ((strcmp(GetExtension(fileName),"png") == 0) ||
+        (strcmp(GetExtension(fileName),"bmp") == 0) ||
+        (strcmp(GetExtension(fileName),"tga") == 0) ||
+        (strcmp(GetExtension(fileName),"jpg") == 0) ||
+        (strcmp(GetExtension(fileName),"gif") == 0) ||
+        (strcmp(GetExtension(fileName),"psd") == 0) ||
+        (strcmp(GetExtension(fileName),"pic") == 0))
+    {       
+        int imgWidth;
+        int imgHeight;
+        int imgBpp;
+        
+        // NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...)
+        // Force loading to 4 components (RGBA)
+        byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4);    
+        
+        // Convert array to pixel array for working convenience
+        image.pixels = (Color *)malloc(imgWidth * imgHeight * sizeof(Color));
+        
+        int pix = 0;
+        
+        for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4)
+        {
+            image.pixels[pix].r = imgData[i];
+            image.pixels[pix].g = imgData[i+1];
+            image.pixels[pix].b = imgData[i+2];
+            image.pixels[pix].a = imgData[i+3];
+            pix++;
+        }
+        
+        stbi_image_free(imgData);
+        
+        image.width = imgWidth;
+        image.height = imgHeight;
+        
+        TraceLog(INFO, "[%s] Image loaded successfully", fileName);
     }
-    
-    stbi_image_free(imgData);
-    
-    image.width = imgWidth;
-    image.height = imgHeight;
+    else TraceLog(WARNING, "[%s] Image extension not recognized, it can't be loaded", fileName);
     
     // ALTERNATIVE: We can load pixel data directly into Color struct pixels array, 
     // to do that struct data alignment should be the right one (4 byte); it is.
@@ -119,103 +140,104 @@ Image LoadImageFromRES(const char *rresName, int resId)
     
     FILE *rresFile = fopen(rresName, "rb");
 
-    if (!rresFile) printf("Error opening raylib Resource file\n");
-    
-    // Read rres file (basic file check - id)
-    fread(&id[0], sizeof(char), 1, rresFile);
-    fread(&id[1], sizeof(char), 1, rresFile);
-    fread(&id[2], sizeof(char), 1, rresFile);
-    fread(&id[3], sizeof(char), 1, rresFile);
-    fread(&version, sizeof(char), 1, rresFile);
-    fread(&useless, sizeof(char), 1, rresFile);
-    
-    if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
-    {
-        printf("This is not a valid raylib Resource file!\n");
-        exit(1);
-    }
-    
-    // Read number of resources embedded
-    fread(&numRes, sizeof(short), 1, rresFile);
-    
-    for (int i = 0; i < numRes; i++)
+    if (!rresFile) TraceLog(WARNING, "[%s] Could not open raylib resource file", rresName);
+    else
     {
-        fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile);
+        // Read rres file (basic file check - id)
+        fread(&id[0], sizeof(char), 1, rresFile);
+        fread(&id[1], sizeof(char), 1, rresFile);
+        fread(&id[2], sizeof(char), 1, rresFile);
+        fread(&id[3], sizeof(char), 1, rresFile);
+        fread(&version, sizeof(char), 1, rresFile);
+        fread(&useless, sizeof(char), 1, rresFile);
         
-        if (infoHeader.id == resId)
+        if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S'))
+        {
+            TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName);
+        }
+        else
         {
-            found = true;
+            // Read number of resources embedded
+            fread(&numRes, sizeof(short), 1, rresFile);
             
-            // Check data is of valid IMAGE type
-            if (infoHeader.type == 0)   // IMAGE data type
+            for (int i = 0; i < numRes; i++)
             {
-                // TODO: Check data compression type
-                
-                // NOTE: We suppose compression type 2 (DEFLATE - default)
-                short imgWidth, imgHeight;
-                char colorFormat, mipmaps;
-            
-                fread(&imgWidth, sizeof(short), 1, rresFile);   // Image width
-                fread(&imgHeight, sizeof(short), 1, rresFile);  // Image height
-                fread(&colorFormat, 1, 1, rresFile);            // Image data color format (default: RGBA 32 bit)
-                fread(&mipmaps, 1, 1, rresFile);                // Mipmap images included (default: 0)
-        
-                printf("Image width: %i\n", (int)imgWidth);
-                printf("Image height: %i\n", (int)imgHeight);
+                fread(&infoHeader, sizeof(ResInfoHeader), 1, rresFile);
                 
-                image.width = (int)imgWidth;
-                image.height = (int)imgHeight;
+                if (infoHeader.id == resId)
+                {
+                    found = true;
+                    
+                    // Check data is of valid IMAGE type
+                    if (infoHeader.type == 0)   // IMAGE data type
+                    {
+                        // TODO: Check data compression type
+                        
+                        // NOTE: We suppose compression type 2 (DEFLATE - default)
+                        short imgWidth, imgHeight;
+                        char colorFormat, mipmaps;
+                    
+                        fread(&imgWidth, sizeof(short), 1, rresFile);   // Image width
+                        fread(&imgHeight, sizeof(short), 1, rresFile);  // Image height
+                        fread(&colorFormat, 1, 1, rresFile);            // Image data color format (default: RGBA 32 bit)
+                        fread(&mipmaps, 1, 1, rresFile);                // Mipmap images included (default: 0)
                 
-                unsigned char *data = malloc(infoHeader.size);
+                        image.width = (int)imgWidth;
+                        image.height = (int)imgHeight;
+                        
+                        unsigned char *data = malloc(infoHeader.size);
 
-                fread(data, infoHeader.size, 1, rresFile);
-                
-                unsigned char *imgData = DecompressData(data, infoHeader.size, infoHeader.srcSize);
-                
-                image.pixels = (Color *)malloc(sizeof(Color)*imgWidth*imgHeight);
-                
-                int pix = 0;
-                
-                for (int i = 0; i < (imgWidth*imgHeight*4); i += 4)
+                        fread(data, infoHeader.size, 1, rresFile);
+                        
+                        unsigned char *imgData = DecompressData(data, infoHeader.size, infoHeader.srcSize);
+                        
+                        image.pixels = (Color *)malloc(sizeof(Color)*imgWidth*imgHeight);
+                        
+                        int pix = 0;
+                        
+                        for (int i = 0; i < (imgWidth*imgHeight*4); i += 4)
+                        {
+                            image.pixels[pix].r = imgData[i];
+                            image.pixels[pix].g = imgData[i+1];
+                            image.pixels[pix].b = imgData[i+2];
+                            image.pixels[pix].a = imgData[i+3];
+                            pix++;
+                        }
+                        
+                        free(imgData);
+                     
+                        free(data);
+                        
+                        TraceLog(INFO, "[%s] Image loaded successfully from resource, size: %ix%i", rresName, image.width, image.height);
+                    }
+                    else
+                    {
+                        TraceLog(WARNING, "[%s] Required resource do not seem to be a valid IMAGE resource", rresName);
+                    }
+                }
+                else
                 {
-                    image.pixels[pix].r = imgData[i];
-                    image.pixels[pix].g = imgData[i+1];
-                    image.pixels[pix].b = imgData[i+2];
-                    image.pixels[pix].a = imgData[i+3];
-                    pix++;
+                    // Depending on type, skip the right amount of parameters
+                    switch (infoHeader.type)
+                    {
+                        case 0: fseek(rresFile, 6, SEEK_CUR); break;   // IMAGE: Jump 6 bytes of parameters
+                        case 1: fseek(rresFile, 6, SEEK_CUR); break;   // SOUND: Jump 6 bytes of parameters
+                        case 2: fseek(rresFile, 5, SEEK_CUR); break;   // MODEL: Jump 5 bytes of parameters (TODO: Review)
+                        case 3: break;   // TEXT: No parameters
+                        case 4: break;   // RAW: No parameters
+                        default: break;
+                    }
+                    
+                    // Jump DATA to read next infoHeader
+                    fseek(rresFile, infoHeader.size, SEEK_CUR);
                 }
-                
-                free(imgData);
-             
-                free(data);
-            }
-            else
-            {
-                printf("Required resource do not seem to be a valid IMAGE resource\n");
-                exit(2);
             }
         }
-        else
-        {
-            // Depending on type, skip the right amount of parameters
-            switch (infoHeader.type)
-            {
-                case 0: fseek(rresFile, 6, SEEK_CUR); break;   // IMAGE: Jump 6 bytes of parameters
-                case 1: fseek(rresFile, 6, SEEK_CUR); break;   // SOUND: Jump 6 bytes of parameters
-                case 2: fseek(rresFile, 5, SEEK_CUR); break;   // MODEL: Jump 5 bytes of parameters (TODO: Review)
-                case 3: break;   // TEXT: No parameters
-                case 4: break;   // RAW: No parameters
-                default: break;
-            }
-            
-            // Jump DATA to read next infoHeader
-            fseek(rresFile, infoHeader.size, SEEK_CUR);
-        }    
+        
+        fclose(rresFile);
     }
     
-    fclose(rresFile);
-    
-    if (!found) printf("Required resource id could not be found in the raylib Resource file!\n");
+    if (!found) TraceLog(WARNING, "[%s] Required resource id [%i] could not be found in the raylib resource file", rresName, resId);
     
     return image;
 }
@@ -224,11 +246,33 @@ Image LoadImageFromRES(const char *rresName, int resId)
 Texture2D LoadTexture(const char *fileName)
 {
     Texture2D texture;
-    Image image;
-    
-    image = LoadImage(fileName);
-    texture = CreateTexture(image);
-    UnloadImage(image);
+
+    if (strcmp(GetExtension(fileName),"dds") == 0)
+    {
+#ifdef USE_OPENGL_11 
+        TraceLog(WARNING, "[%s] DDS file loading requires OpenGL 3.2+ or ES 2.0", fileName);
+#else
+        ImageDDS image = LoadDDS(fileName);
+        
+        texture.glId = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.format);
+
+        texture.width = image.width;
+        texture.height = image.height;
+        
+        if (texture.glId == 0) TraceLog(WARNING, "Compressed texture could not be loaded");
+        else TraceLog(INFO, "Compressed texture loaded succesfully");
+#endif
+    }
+    else
+    {
+        Image image = LoadImage(fileName);
+        
+        if (image.pixels != NULL)
+        {
+            texture = CreateTexture(image);
+            UnloadImage(image);
+        }
+    }
     
     return texture;
 }
@@ -259,7 +303,7 @@ void UnloadTexture(Texture2D texture)
 // 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, 1.0f, tint);
 }
 
 // Draw a Texture2D with position defined as Vector2
@@ -276,9 +320,9 @@ void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float sc
     // NOTE: Rotation is applied before translation and scaling, even being called in inverse order...
     // NOTE: Rotation point is upper-left corner    
     rlPushMatrix();
-        rlTranslatef(position.x, position.y, 0.0);
-        rlScalef(scale, scale, 1.0f);
+        //rlTranslatef(position.x, position.y, 0.0);
         rlRotatef(rotation, 0, 0, 1);
+        rlScalef(scale, scale, 1.0f);
     
         rlBegin(RL_QUADS);
             rlColor4ub(tint.r, tint.g, tint.b, tint.a);
@@ -390,7 +434,114 @@ Texture2D CreateTexture(Image image)
     texture.width = image.width;
     texture.height = image.height;
     
+    TraceLog(INFO, "Texture created succesfully");
+    
     free(img);
     
     return texture;
+}
+
+// Get the extension for a filename
+static const char *GetExtension(const char *fileName) 
+{
+    const char *dot = strrchr(fileName, '.');
+    if(!dot || dot == fileName) return "";
+    return (dot + 1);
+}
+
+// Loading DDS image compressed data 
+ImageDDS LoadDDS(const char *fileName)
+{   
+    // TODO: Review and expand DDS file loading to support uncompressed formats and new formats
+
+    // DDS Pixel Format
+    typedef struct {
+        unsigned int size;
+        unsigned int flags;
+        unsigned int fourCC;
+        unsigned int rgbBitCount;
+        unsigned int rBitMask;
+        unsigned int gBitMask;
+        unsigned int bitMask;
+        unsigned int aBitMask;
+    } ddsPixelFormat;
+    
+    // DDS Header (124 bytes)
+    typedef struct {
+        unsigned int size;
+        unsigned int flags;
+        unsigned int height;
+        unsigned int width;
+        unsigned int pitchOrLinearSize;
+        unsigned int depth;
+        unsigned int mipMapCount;
+        unsigned int reserved1[11];
+        ddsPixelFormat ddspf;
+        unsigned int caps;
+        unsigned int caps2;
+        unsigned int caps3;
+        unsigned int caps4;
+        unsigned int reserved2;
+    } ddsHeader;
+    
+    ImageDDS image;
+    ddsHeader header;
+
+    FILE *ddsFile = fopen(fileName, "rb");
+    
+    if (ddsFile == NULL)
+    {
+        TraceLog(WARNING, "DDS File could not be opened");
+    }
+    else
+    {
+        // Verify the type of file
+        char filecode[4];
+        
+        fread(filecode, 1, 4, ddsFile);
+        
+        if (strncmp(filecode, "DDS ", 4) != 0) 
+        { 
+            TraceLog(WARNING, "DDS File does not seem to be valid");
+            fclose(ddsFile);
+        }
+        else
+        {       
+            // Get the surface descriptor
+            fread(&header, sizeof(ddsHeader), 1, ddsFile);
+
+            int height = header.height;
+            int width = header.width;
+            int linearSize = header.pitchOrLinearSize;
+            int mipMapCount = header.mipMapCount;
+            int fourCC = header.ddspf.fourCC;
+            
+            TraceLog(DEBUG, "[%s] DDS file header size: %i", fileName, sizeof(ddsHeader));
+            
+            TraceLog(DEBUG, "[%s] DDS file pixel format size: %i", fileName, header.ddspf.size);
+            TraceLog(DEBUG, "[%s] DDS file pixel format flags: 0x%x", fileName, header.ddspf.flags);
+            TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, fourCC);
+            
+            int bufsize;
+            
+            // Calculate data size, including all mipmaps 
+            bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize; 
+            
+            image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); 
+            
+            fread(image.data, 1, bufsize, ddsFile); 
+            
+            // Close file pointer
+            fclose(ddsFile);
+
+            //int components = (fourCC == FOURCC_DXT1) ? 3 : 4; // Not required
+            
+            image.width = width;
+            image.height = height;
+            image.mipmaps = mipMapCount;
+            image.format = fourCC;
+        }
+    }
+    
+    return image;
 }

+ 127 - 21
src/utils.c

@@ -29,7 +29,8 @@
 #include "utils.h"
 
 #include <stdlib.h>         // malloc(), free()
-#include <stdio.h>          // printf()
+#include <stdio.h>          // printf(), fprintf()
+#include <stdarg.h>         // Used for functions with variable number of parameters (TraceLog())
 //#include <string.h>       // String management functions: strlen(), strrchr(), strcmp()
 
 #define STB_IMAGE_WRITE_IMPLEMENTATION
@@ -37,6 +38,15 @@
 #include "stb_image_write.h"    // Create PNG file
 #include "tinfl.c"
 
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static FILE *logstream = NULL;
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Utilities
+//----------------------------------------------------------------------------------
+
 // Data decompression function
 // NOTE: Allocated data MUST be freed!
 unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize)
@@ -50,28 +60,28 @@ unsigned char *DecompressData(const unsigned char *data, unsigned long compSize,
     // Check correct memory allocation
     if (!pUncomp)
     {
-        printf("Out of memory!\n");
-        return NULL;
+        TraceLog(WARNING, "Out of memory while decompressing data");
     }
-    
-    // Decompress data
-    tempUncompSize = tinfl_decompress_mem_to_mem(pUncomp, (size_t)uncompSize, data, compSize, 1);
-    
-    if (tempUncompSize == -1)
+    else
     {
-        printf("Decompression failed!\n");
-        free(pUncomp);
-        return NULL;
-    }
-    
-    if (uncompSize != (int)tempUncompSize)
-    {
-        printf("WARNING! Expected uncompressed size do not match! Data may be corrupted!\n");
-        printf(" -- Expected uncompressed size: %i\n", uncompSize);
-        printf(" -- Returned uncompressed size: %i\n", tempUncompSize);
-    }
+        // Decompress data
+        tempUncompSize = tinfl_decompress_mem_to_mem(pUncomp, (size_t)uncompSize, data, compSize, 1);
+        
+        if (tempUncompSize == -1)
+        {
+            TraceLog(WARNING, "Data decompression failed");
+            free(pUncomp);
+        }
+        
+        if (uncompSize != (int)tempUncompSize)
+        {
+            TraceLog(WARNING, "Expected uncompressed size do not match, data may be corrupted");
+            TraceLog(WARNING, " -- Expected uncompressed size: %i", uncompSize);
+            TraceLog(WARNING, " -- Returned uncompressed size: %i", tempUncompSize);
+        }
 
-    printf("Decompressed from %u bytes to %u bytes\n", (mz_uint32)compSize, (mz_uint32)tempUncompSize);
+        TraceLog(INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize);
+    }
     
     return pUncomp;
 }
@@ -124,4 +134,100 @@ void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int he
 void WritePNG(const char *fileName, unsigned char *imgData, int width, int height)
 {
     stbi_write_png(fileName, width, height, 4, imgData, width*4); // It WORKS!!!
-}
+}
+
+// Outputs a trace log message (INFO, ERROR, WARNING)
+// NOTE: If a file has been init, output log is written there
+void TraceLog(int msgType, const char *text, ...)
+{
+    // TODO: This function requires some refactoring...
+
+    // NOTE: If trace log file has been set, stdout is being redirected to a file
+    va_list args;
+    int traceDebugMsgs = 1;
+    
+#ifdef DO_NOT_TRACE_DEBUG_MSGS
+    traceDebugMsgs = 0;
+#endif
+    
+    if (logstream != NULL)
+    {
+        switch(msgType)
+        {
+            case 0: fprintf(logstream, "INFO: "); break;
+            case 1: fprintf(logstream, "ERROR: "); break;
+            case 2: fprintf(logstream, "WARNING: "); break;
+            case 3: if (traceDebugMsgs) fprintf(logstream, "DEBUG: "); break;
+            default: break;
+        }
+        
+        if (msgType == 3)
+        {
+            if (traceDebugMsgs)
+            {
+                va_start(args, text);
+                vfprintf(logstream, text, args);
+                va_end(args);
+                
+                fprintf(logstream, "\n");
+            }
+        }
+        else
+        {
+            va_start(args, text);
+            vfprintf(logstream, text, args);
+            va_end(args);
+            
+            fprintf(logstream, "\n");
+        }
+    }
+    else
+    {   
+        switch(msgType)
+        {
+            case 0: fprintf(stdout, "INFO: "); break;
+            case 1: fprintf(stdout, "ERROR: "); break;
+            case 2: fprintf(stdout, "WARNING: "); break;
+            case 3: if (traceDebugMsgs) fprintf(stdout, "DEBUG: "); break;
+            default: break;
+        }
+        
+        if (msgType == 3)
+        {
+            if (traceDebugMsgs)
+            {
+                va_start(args, text);
+                vfprintf(stdout, text, args);
+                va_end(args);
+                
+                fprintf(stdout, "\n");
+            }
+        }
+        else
+        {
+            va_start(args, text);
+            vfprintf(stdout, text, args);
+            va_end(args);
+            
+            fprintf(stdout, "\n");
+        }
+    }
+    
+    if (msgType == 1) exit(1);      // If ERROR message, exit program
+}
+
+// Inits a trace log file
+void InitTraceLogFile(const char *logFileName)
+{
+    // stdout redirected to stream file
+    FILE *logstream = fopen(logFileName, "w");
+
+    if (logstream == NULL) TraceLog(WARNING, "Unable to open log file");
+}
+
+// Closes the trace log file
+void CloseTraceLogFile()
+{
+    if (logstream != NULL) fclose(logstream);
+}
+

+ 8 - 1
src/utils.h

@@ -32,13 +32,15 @@
 //----------------------------------------------------------------------------------
 // Some basic Defines
 //----------------------------------------------------------------------------------
-//...
+//#define DO_NOT_TRACE_DEBUG_MSGS   // Use this define to avoid DEBUG tracing
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
 typedef enum { IMAGE, SOUND, MODEL, TEXT, RAW } DataType;
 
+typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
+
 // One resource info header, every resource includes this header (8 byte)
 typedef struct {
     unsigned short id;      // Resource unique identifier (2 byte)
@@ -61,9 +63,14 @@ extern "C" {            // Prevents name mangling of functions
 // Module Functions Declaration
 //----------------------------------------------------------------------------------
 unsigned char *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize);
+
 void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int height);
 void WritePNG(const char *fileName, unsigned char *imgData, int width, int height);
 
+void TraceLog(int msgType, const char *text, ...);  // Outputs a trace log message
+void InitTraceLogFile(const char *logFileName);     // Inits a trace log file
+void CloseTraceLogFile();                           // Closes the trace log file
+
 #ifdef __cplusplus
 }
 #endif