Browse Source

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 11 năm trước cách đây
mục cha
commit
e6b82cb111
12 tập tin đã thay đổi với 1141 bổ sung523 xóa
  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