Pārlūkot izejas kodu

raylib 1.1

View CHANGELOG for a detailed list of changes
raysan5 11 gadi atpakaļ
vecāks
revīzija
f06a15ac8b
17 mainītis faili ar 1541 papildinājumiem un 1082 dzēšanām
  1. 34 1
      CHANGELOG
  2. 2 2
      HELPME.md
  3. 52 19
      README.md
  4. 6 6
      ROADMAP.md
  5. 346 284
      src/audio.c
  6. 7 8
      src/core.c
  7. 343 304
      src/models.c
  8. 56 48
      src/raylib.h
  9. 20 2
      src/raymath.c
  10. 1 0
      src/raymath.h
  11. 359 114
      src/rlgl.c
  12. 26 31
      src/rlgl.h
  13. 18 21
      src/shapes.c
  14. 17 26
      src/text.c
  15. 204 137
      src/textures.c
  16. 44 72
      src/utils.c
  17. 6 7
      src/utils.h

+ 34 - 1
CHANGELOG

@@ -1,11 +1,44 @@
 changelog
 ---------
 
-Current Release:    raylib 1.0.6 (March 2014)
+Current Release:    raylib 1.1.0 (April 2014)
 
 NOTE: Only versions marked as 'Release' are available on release folder, updates are only available as source.
 NOTE: Current Release includes all previous updates.
 
+-----------------------------------------------
+Release:     raylib 1.1.0 (19 April 2014)
+-----------------------------------------------
+NOTE: 
+  This version supposed a complete internal redesign of the library to support OpenGL 3.3+ and OpenGL ES 2.0.
+  New module [rlgl] has been added to 'translate' immediate mode style functions (i.e. rlVertex3f()) to GL 1.1, 3.3+ or ES2.
+  Another new module [raymath] has also been added with lot of useful 3D math vector-matrix-quaternion functions.
+
+[rlgl] New module, abstracts OpenGL rendering (multiple versions support)
+[raymath] New module, useful 3D math vector-matrix-quaternion functions
+[core] Adapt all OpenGL code (initialization, drawing) to use [rlgl]
+[shapes] Rewrite all shapes drawing functions to use [rlgl]
+[textures] Adapt texture GPU loading to use [rlgl]
+[textures] Added support for DDS images (compressed and uncompressed)
+[textures] CreateTexture() - Redesigned to add mipmap automatic generation
+[textures] DrawTexturePro() - Redesigned and corrected bugs
+[models] Rewrite all 3d-shapes drawing functions to use [rlgl]
+[models] Adapt model loading and drawing to use [rlgl]
+[models] Model struct updated to include texture id
+[models] SetModelTexture() - Added, link a texture to a model
+[models] DrawModelEx() - Redesigned with extended parameters
+[audio] Added music streaming support (OGG files)
+[audio] Added support for OGG files as Sound
+[audio] PlayMusicStream() - Added, open a new music stream and play it
+[audio] StopMusicStream() - Added, stop music stream playing and close stream
+[audio] PauseMusicStream() - Added, pause music stream playing
+[audio] MusicIsPlaying() - Added, to check if music is playing
+[audio] SetMusicVolume() - Added, set volume for music
+[audio] GetMusicTimeLength() - Added, get current music time length (in seconds)
+[audio] GetMusicTimePlayed() - Added, get current music time played (in seconds)
+[utils] Added log tracing functionality - TraceLog(), TraceLogOpen(), TraceLogClose()
+[*] Log tracing messages all around the code
+
 -----------------------------------------------
 Release:     raylib 1.0.6 (16 March 2014)
 -----------------------------------------------

+ 2 - 2
HELPME.md

@@ -10,7 +10,7 @@ The following help is highly appreciated:
 	- Translators / Localizators - Can you translate raylib to another language?
 	- Documentation / Tutorials / Example writters - Can you write some tutorial / example?
 	- Web Development - Can you help with the web? Can you setup a forum?
-	- Porting to Linux and OSX - Can you compile and test raylib on another OS?
+	- Porting to Linux, OSX... - Can you compile and test raylib on another OS?
 	- Testers of current features and multiple systems - Can you find some bug on raylib?
 
 If you can not help on any of the above points but you still want to contribute in some way... please, consider helping 
@@ -34,4 +34,4 @@ contact
    * Facebook: [http://www.facebook.com/raylibgames](http://www.facebook.com/raylibgames)
 
    
-[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San"
+[raysan5]: mailto:raysan5@gmail.com "Ramon Santamaria - Ray San"

+ 52 - 19
README.md

@@ -23,29 +23,51 @@ a simple PONG and some of them even a BREAKOUT!
 
 But WinBGI was not the clearer and most organized lib. There were a lot of things I found useless and 
 confusing and some function names were not clear enough for most of the students; not to mention points 
-like no transparencies support or no hardware acceleration. 
+like no transparencies support or no hardware acceleration.
 
 So, I decided to create my own lib, hardware accelerated, clear function names, quite organized, well structured, 
 plain C coding and, the most important, primarily intended to LEARN videogames programming.
 
-I've coded quite a lot in C# and XNA and I really love it (in fact, my students learn C# with XNA after C), 
+I've coded quite a lot in C# and XNA and I really love it (in fact, my students learn C# after C), 
 so, I decided to use C# language notation and XNA naming conventions. That way, students can jump from 
-raylib to XNA (or MonoGame) extremely easily.
+raylib to XNA, MonoGame or similar libs extremely easily.
 
-raylib started as a weekend project and after three months of hard work, here it is the first version. 
+raylib started as a weekend project and after three months of hard work, first version was published.
+
+Enjoy it.
+
+notes on raylib 1.1
+-------------------
+
+On April 2014, after 6 month of first raylib release, raybil 1.1 has been released. This new version presents a
+complete internal redesign of the library to support OpenGL 1.1, OpenGL 3.3+ and OpenGL ES 2.0.
+
+A new module named [rlgl] (https://github.com/raysan5/raylib/blob/master/src/rlgl.h) has been added to the library. This new module translate raylib-OpenGL-style 
+immediate mode functions (i.e. rlVertex3f(), rlBegin(), ...) to different versions of OpenGL (1.1, 3.3+, ES2), selectable by one define.
+
+[rlgl] (https://github.com/raysan5/raylib/blob/master/src/rlgl.h) also comes with a second new module named [raymath] (https://github.com/raysan5/raylib/blob/master/src/raymath.h), which includes
+a bunch of useful functions for 3d-math with vectors, matrices and quaternions.
+
+Some other big changes of this new version have been the support for OGG files loading and stream playing, and the
+support of DDS texture files (compressed and uncompressed) along with mipmaps support.
+
+Lots of code changes and lot of testing have concluded in this amazing new raylib 1.1.
 
 Enjoy it.
 
 features
 --------
-
-   * Written in plain C code (C99)
-   * Uses C# PascalCase/camelCase notation
-   * Hardware accelerated using OpenGL 1.1
-   * Transparencies support (RGBA Colors)
-   * Custom color palette for better use on white background
-   * Basic 3D Support (camera, basic models, OBJ models, etc)
-   * Powerful Text module with SpriteFonts support
+ 
+   *  Written in plain C code (C99)
+   *  Uses C# PascalCase/camelCase notation
+   *  Hardware accelerated with OpenGL (1.1, 3.3+ or ES2)
+   *  Unique OpenGL abstraction layer [rlgl]
+   *  Powerful fonts module with SpriteFonts support
+   *  Multiple textures support, including DDS and mipmaps generation
+   *  Basic 3d support for Shapes, Models, Heightmaps and Billboards
+   *  Powerful math module for Vector and Matrix operations [raymath]
+   *  Audio loading and playing with streaming support
+   *  Custom color palette for fancy visuals on raywhite background
 
 raylib uses on its core module the outstanding [GLFW3] (http://www.glfw.org/) library. The best option by far I found for 
 window/context and input management (clean, focused, great license, well documented, modern, ...). 
@@ -75,19 +97,30 @@ raylib could be build with the following command lines (Using GCC compiler):
 	gcc -c core.c -std=c99 -Wall
 	gcc -c shapes.c -std=c99 -Wall
 	gcc -c textures.c -std=c99 -Wall
-	gcc -c stb_image.c -std=c99 -Wall
 	gcc -c text.c -std=c99 -Wall
 	gcc -c models.c -std=c99 -Wall
-	gcc -c vector3.c -std=c99 -Wall
+	gcc -c raymath.c -std=c99 -Wall
+    gcc -c rlgl.c -std=c99 -Wall
 	gcc -c audio.c -std=c99 -Wall
     gcc -c utils.c -std=c99 -Wall
-	ar rcs raylib.a core.o shapes.o textures.o stb_image.o text.o models.o vector3.o utils.o audio.o
+    gcc -c stb_image.c -std=c99 -Wall
+    gcc -c stb_vorbis.c -std=c99 -Wall
+    
+	ar rcs libraylib.a core.o shapes.o textures.o stb_image.o text.o models.o raymath.o rlgl.o utils.o stb_vorbis.o audio.o
+
+To compile examples, make sure raylib.h is placed in the include path and the following libraries are placed in the libraries path:
 
-To compile examples, make sure raylib.h is placed in include path and libraries raylib (libraylib.a) and glfw3 (libglfw3.a) 
-are placed in the libraries path. It's also recommended to link with file icon.o for fancy raylib icon usage.
+    libraylib.a   - raylib
+    libglfw3.a    - GLFW3 (static version)
+    libglew32.a   - GLEW, OpenGL extension loading, only required if using OpenGL 3.3+ or ES2
+    libopenal32.a - OpenAL, audio device management
+    
+It's also recommended to link with file icon.o for fancy raylib icon usage. Linking command:
 
 	cd raylib/examples
-	gcc -o test_code.exe test_code.c icon.o -lraylib -lglfw3 -lopengl32 -lgdi32 -std=c99 -Wl,--subsystem,windows
+	gcc -o test_code.exe test_code.c icon.o -lraylib -lglfw3 -lglew32 -lopenal32 -lopengl32 -lgdi32 -std=c99 -Wl,--subsystem,windows
+    
+If you have any doubt, [let me know][raysan5].
 
 contact
 -------
@@ -109,4 +142,4 @@ The following people have contributed in some way to make raylib project a reali
  - [Elendow](http://www.elendow.com)
 
 	
-[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San"
+[raysan5]: mailto:raysan5@gmail.com "Ramon Santamaria - Ray San"

+ 6 - 6
ROADMAP.md

@@ -1,7 +1,7 @@
 roadmap
 -------
 
-First version of raylib is quite complete and functional but there is still a lot of things I would like to improve.
+Current version of raylib is quite complete and functional but there is still a lot of things I would like to improve.
 Here it is a list of features I would like to add and functions to improve.
 
 Around the source code there are some TODO points with pending revisions/bugs and here it is a list of features I would like to add.
@@ -10,13 +10,13 @@ raylib v1.x
 
    - [DONE] Review Billboard Drawing functions
    - [DONE] Review Heightmap Loading and Drawing functions - Load Heightmap directly as a Model
-   - Lighting support (only 3d mode) - CreateLight()
+   - Lighting support (only 3d mode)
    - [DONE] Simple Collision Detection functions
    - Default scene Camera controls (zoom, pan, rotate)   
-   - Basic Procedural Texture / Image generation (Gradient, Checked, Spot, Noise, Cellular)
-   - Software mipmapping generation and POT conversion (custom implementation)
-   - Comments / Functions translation (?)
+   - Basic Procedural Image Generation (Gradient, Checked, Spot, Noise, Cellular)
+   - [DONE] Software mipmapping generation and POT conversion (custom implementation)
+   - TTF fonts support
    
 Any feature missing? Do you have a request? [Let me know!][raysan5]
 
-[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San"
+[raysan5]: mailto:raysan5@gmail.com "Ramon Santamaria - Ray San"

+ 346 - 284
src/audio.c

@@ -6,7 +6,7 @@
 *    
 *   Uses external lib:    
 *       OpenAL - Audio device management lib
-*       TODO: stb_vorbis - Ogg audio files loading
+*       stb_vorbis - Ogg audio files loading
 *       
 *   Copyright (c) 2013 Ramon Santamaria (Ray San - [email protected])
 *    
@@ -32,50 +32,45 @@
 #include <AL/al.h>           // OpenAL basic header
 #include <AL/alc.h>          // OpenAL context header (like OpenGL, OpenAL requires a context to work)
 
-#include <stdlib.h>          // To use exit() function
+#include <stdlib.h>          // Declares malloc() and free() for memory management
+#include <string.h>          // Required for strcmp()
 #include <stdio.h>           // Used for .WAV loading
 
 #include "utils.h"           // rRES data decompression utility function
 
-//#include "stb_vorbis.h"      // OGG loading functions
+#include "stb_vorbis.h"      // OGG loading functions
 
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-// Nop...
+#define MUSIC_STREAM_BUFFERS        2
+#define MUSIC_BUFFER_SIZE      4096*8   //4096*32
 
 //----------------------------------------------------------------------------------
 // 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];
+
+// Music type (file streaming from memory)
+// NOTE: Anything longer than ~10 seconds should be streamed...
+typedef struct Music {
+    stb_vorbis *stream;
+    
+	ALuint buffers[MUSIC_STREAM_BUFFERS];
 	ALuint source;
 	ALenum format;
  
-	int bufferSize;
+    int channels;
+    int sampleRate;
 	int totalSamplesLeft;
 	bool loop;
-};
-*/
+    
+} Music;
 
 // Wave file data
 typedef struct Wave {
-    unsigned char *data;      // Buffer data pointer
+    void *data;                 // Buffer data pointer
+    unsigned int dataSize;      // Data size in bytes
     unsigned int sampleRate;
-    unsigned int dataSize;
     short bitsPerSample;
     short channels;  
 } Wave;
@@ -83,22 +78,23 @@ typedef struct Wave {
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
-static bool musicIsPlaying;
-static Music *currentMusic;
+bool musicEnabled = false;
+static Music currentMusic;      // Current music loaded
+                                // NOTE: Only one music file playing at a time
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
-static Wave LoadWAV(char *fileName);
-static void UnloadWAV(Wave wave);
-//static Ogg LoadOGG(char *fileName);
-static bool MusicStream(Music music, ALuint buffer);
+static Wave LoadWAV(const char *fileName);
+static Wave LoadOGG(char *fileName);
+static void UnloadWave(Wave wave);
 
-extern bool MusicStreamUpdate();
-extern void PlayCurrentMusic();
+static bool BufferMusicStream(ALuint buffer);   // Fill music buffers with data
+static void EmptyMusicStream();                 // Empty music buffers
+extern void UpdateMusicStream();                // Updates buffers (refill) for music streaming
 
 //----------------------------------------------------------------------------------
-// Module Functions Definition - Window and OpenGL Context Functions
+// Module Functions Definition - Audio Device initialization and Closing
 //----------------------------------------------------------------------------------
 
 // Initialize audio device and context
@@ -126,13 +122,13 @@ void InitAudioDevice()
     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
 void CloseAudioDevice()
 {
+    StopMusicStream();      // Stop music streaming and close current stream
+
     ALCdevice *device;
     ALCcontext *context = alcGetCurrentContext();
     
@@ -145,60 +141,70 @@ void CloseAudioDevice()
     alcCloseDevice(device);
 }
 
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Sounds loading and playing (.WAV)
+//----------------------------------------------------------------------------------
+
 // Load sound to memory
 Sound LoadSound(char *fileName)
 {
     Sound sound;
+    Wave wave;
     
     // NOTE: The entire file is loaded to memory to play it all at once (no-streaming)
     
-    // WAV file loading
-    // NOTE: Buffer space is allocated inside LoadWAV, Wave must be freed
-    Wave wave = LoadWAV(fileName);
-    
-    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;
-    }
-    
+    // Audio file loading
+    // NOTE: Buffer space is allocated inside function, Wave must be freed
     
-    // 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);
+    if (strcmp(GetExtension(fileName),"wav") == 0) wave = LoadWAV(fileName);
+    else if (strcmp(GetExtension(fileName),"ogg") == 0) wave = LoadOGG(fileName);
+    else TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName);
     
-    // Convert loaded data to OpenAL buffer
-    //----------------------------------------
-    ALuint buffer;
-    alGenBuffers(1, &buffer);            // Generate pointer to buffer
+    if (wave.data != NULL)
+    {
+        ALenum format = 0;
+        // The OpenAL format is worked out by looking at the number of channels and the bits per sample
+        if (wave.channels == 1) 
+        {
+            if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8;
+            else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16;
+        } 
+        else if (wave.channels == 2) 
+        {
+            if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8;
+            else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16;
+        }
+        
+        // Create an audio source
+        ALuint source;
+        alGenSources(1, &source);            // Generate pointer to audio source
+
+        alSourcef(source, AL_PITCH, 1);    
+        alSourcef(source, AL_GAIN, 1);
+        alSource3f(source, AL_POSITION, 0, 0, 0);
+        alSource3f(source, AL_VELOCITY, 0, 0, 0);
+        alSourcei(source, AL_LOOPING, AL_FALSE);
+        
+        // Convert loaded data to OpenAL buffer
+        //----------------------------------------
+        ALuint buffer;
+        alGenBuffers(1, &buffer);            // Generate pointer to buffer
 
-    // Upload sound data to buffer
-    alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate);
+        // Upload sound data to buffer
+        alBufferData(buffer, format, 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 file loaded successfully", fileName);  
-    TraceLog(INFO, "[%s] Sample rate: %i - Channels: %i", fileName, wave.sampleRate, wave.channels);
-    
-    sound.source = source;
-    sound.buffer = buffer;
+        // Attach sound buffer to source
+        alSourcei(source, AL_BUFFER, buffer);
+        
+        // Unallocate WAV data
+        UnloadWave(wave);
+        
+        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;
+    }
     
     return sound;
 }
@@ -314,7 +320,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId)
                         alSourcei(source, AL_BUFFER, buffer);
                         
                         // Unallocate WAV data
-                        UnloadWAV(wave);
+                        UnloadWave(wave);
 
                         TraceLog(INFO, "[%s] Sound loaded successfully from resource, sample rate: %i", rresName, (int)sampleRate);
                         
@@ -381,22 +387,6 @@ void PlaySound(Sound sound)
     //alGetSourcef(sound.source, AL_SEC_OFFSET, &result);   // AL_SAMPLE_OFFSET
 }
 
-// Play a sound with extended options
-// TODO: This function should be reviewed...
-void PlaySoundEx(Sound sound, float timePosition, bool loop)
-{
-    // TODO: Review
-    
-    // Change the current position (e.g. skip some part of the sound)
-    // NOTE: Only work when the entire file is in a single buffer
-    //alSourcei(sound.source, AL_BYTE_OFFSET, int(position * sampleRate));
-
-    alSourcePlay(sound.source);        // Play the sound
-    
-    if (loop) alSourcei(sound.source, AL_LOOPING, AL_TRUE);
-    else alSourcei(sound.source, AL_LOOPING, AL_FALSE);
-}
-
 // Pause a sound
 void PauseSound(Sound sound)
 {
@@ -421,30 +411,250 @@ bool SoundIsPlaying(Sound sound)
     return playing;
 }
 
+// Set volume for a sound
+void SetSoundVolume(Sound sound, float volume)
+{
+    alSourcef(sound.source, AL_GAIN, volume);
+}
+
+// Set pitch for a sound
+void SetSoundPitch(Sound sound, float pitch)
+{
+    alSourcef(sound.source, AL_PITCH, pitch);
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Music loading and stream playing (.OGG)
+//----------------------------------------------------------------------------------
+
+// Start music playing (open stream)
+void PlayMusicStream(char *fileName)
+{
+    if (strcmp(GetExtension(fileName),"ogg") == 0)
+    {
+        // Stop current music, clean buffers, unload current stream
+        StopMusicStream();
+    
+        // Open audio stream
+        currentMusic.stream = stb_vorbis_open_filename(fileName, NULL, NULL);
+        
+        if (currentMusic.stream == NULL) TraceLog(WARNING, "[%s] Could not open ogg audio file", fileName);
+        else
+        {
+            // Get file info
+            stb_vorbis_info info = stb_vorbis_get_info(currentMusic.stream);
+            
+            currentMusic.channels = info.channels;
+            currentMusic.sampleRate = info.sample_rate;
+            
+            TraceLog(INFO, "[%s] Ogg sample rate: %i", fileName, info.sample_rate);
+            TraceLog(INFO, "[%s] Ogg channels: %i", fileName, info.channels);
+            TraceLog(INFO, "[%s] Temp memory required: %i", fileName, info.temp_memory_required);
+            
+            if (info.channels == 2) currentMusic.format = AL_FORMAT_STEREO16;
+            else currentMusic.format = AL_FORMAT_MONO16;
+            
+            currentMusic.loop = true;                  // We loop by default
+            musicEnabled = true;
+            
+            // Create an audio source
+            alGenSources(1, &currentMusic.source);     // Generate pointer to audio source
+
+            alSourcef(currentMusic.source, AL_PITCH, 1);    
+            alSourcef(currentMusic.source, AL_GAIN, 1);
+            alSource3f(currentMusic.source, AL_POSITION, 0, 0, 0);
+            alSource3f(currentMusic.source, AL_VELOCITY, 0, 0, 0);
+            //alSourcei(currentMusic.source, AL_LOOPING, AL_TRUE);     // ERROR: Buffers do not queue!
+            
+            // Generate two OpenAL buffers
+            alGenBuffers(2, currentMusic.buffers);
+
+            // Fill buffers with music...
+            BufferMusicStream(currentMusic.buffers[0]);
+            BufferMusicStream(currentMusic.buffers[1]);
+            
+            // Queue buffers and start playing
+            alSourceQueueBuffers(currentMusic.source, 2, currentMusic.buffers);
+            alSourcePlay(currentMusic.source);
+            
+            // NOTE: Regularly, we must check if a buffer has been processed and refill it: MusicStreamUpdate()
+
+            currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
+        }
+    }
+    else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName);
+}
+
+// Stop music playing (close stream)
+void StopMusicStream()
+{
+    if (musicEnabled)
+    {
+        alSourceStop(currentMusic.source);
+        
+        EmptyMusicStream();     // Empty music buffers
+        
+        alDeleteSources(1, &currentMusic.source);
+        alDeleteBuffers(2, currentMusic.buffers);
+        
+        stb_vorbis_close(currentMusic.stream);
+    }
+    
+    musicEnabled = false;
+}
+
+// Pause music playing
+void PauseMusicStream()
+{
+    // TODO: Record music is paused or check if music available!
+    alSourcePause(currentMusic.source);
+}
+
 // Check if music is playing
-bool MusicIsPlaying(Music music)
+bool MusicIsPlaying()
 {
     ALenum state;
     
-    alGetSourcei(music.source, AL_SOURCE_STATE, &state);
+    alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
     
     return (state == AL_PLAYING);
 }
 
-// Set volume for a sound
-void SetVolume(Sound sound, float volume)
+// Set volume for music
+void SetMusicVolume(float volume)
 {
-    alSourcef(sound.source, AL_GAIN, volume);
+    alSourcef(currentMusic.source, AL_GAIN, volume);
 }
 
-// Set pitch for a sound
-void SetPitch(Sound sound, float pitch)
+// Get current music time length (in seconds)
+float GetMusicTimeLength()
 {
-    alSourcef(sound.source, AL_PITCH, pitch);
+    float totalSeconds = stb_vorbis_stream_length_in_seconds(currentMusic.stream);
+    
+    return totalSeconds;
+}
+
+// Get current music time played (in seconds)
+float GetMusicTimePlayed()
+{
+    int totalSamples = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
+    
+    int samplesPlayed = totalSamples - currentMusic.totalSamplesLeft;
+    
+    float secondsPlayed = (float)samplesPlayed / (currentMusic.sampleRate * currentMusic.channels);
+    
+    return secondsPlayed;
+}
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+
+// Fill music buffers with new data from music stream
+static bool BufferMusicStream(ALuint buffer)
+{
+	short pcm[MUSIC_BUFFER_SIZE];
+    
+	int  size = 0;              // Total size of data steamed (in bytes)
+	int  streamedBytes = 0;     // Bytes of data obtained in one samples get
+    
+    bool active = true;         // We can get more data from stream (not finished)
+    
+    if (musicEnabled)
+    {
+        while (size < MUSIC_BUFFER_SIZE)
+        {
+            streamedBytes = stb_vorbis_get_samples_short_interleaved(currentMusic.stream, currentMusic.channels, pcm + size, MUSIC_BUFFER_SIZE - size);
+            
+            if (streamedBytes > 0) size += (streamedBytes*currentMusic.channels);
+            else break;
+        }
+        
+        TraceLog(DEBUG, "Streaming music data to buffer. Bytes streamed: %i", size);
+    }
+    
+	if (size > 0)
+    {
+        alBufferData(buffer, currentMusic.format, pcm, size*sizeof(short), currentMusic.sampleRate);
+        
+        currentMusic.totalSamplesLeft -= size;
+    }
+    else
+    {
+        active = false;
+        TraceLog(WARNING, "No more data obtained from stream");
+    }
+
+	return active;
+}
+
+// Empty music buffers
+static void EmptyMusicStream()
+{
+    ALuint buffer = 0; 
+    int queued = 0;
+    
+    alGetSourcei(currentMusic.source, AL_BUFFERS_QUEUED, &queued);
+    
+    while(queued > 0)
+    {
+        alSourceUnqueueBuffers(currentMusic.source, 1, &buffer);
+        
+        queued--;
+    }
+}
+
+// Update (re-fill) music buffers if data already processed
+extern void UpdateMusicStream()
+{
+    ALuint buffer = 0;
+    ALint processed = 0;
+    bool active = true;
+    
+    if (musicEnabled)
+    {
+        // Get the number of already processed buffers (if any)
+        alGetSourcei(currentMusic.source, AL_BUFFERS_PROCESSED, &processed);
+        
+        while (processed > 0)
+        {
+            // Recover processed buffer for refill
+            alSourceUnqueueBuffers(currentMusic.source, 1, &buffer);
+
+            // Refill buffer
+            active = BufferMusicStream(buffer);
+            
+            // If no more data to stream, restart music (if loop)
+            if ((!active) && (currentMusic.loop))   
+            {
+                if (currentMusic.loop)
+                {
+                    stb_vorbis_seek_start(currentMusic.stream);
+                    currentMusic.totalSamplesLeft = stb_vorbis_stream_length_in_samples(currentMusic.stream) * currentMusic.channels;
+                    
+                    active = BufferMusicStream(buffer);
+                }
+            }
+            
+            // Add refilled buffer to queue again... don't let the music stop!
+            alSourceQueueBuffers(currentMusic.source, 1, &buffer);
+            
+            if(alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Ogg playing, error buffering data...");
+            
+            processed--;
+        }
+        
+        ALenum state;
+        alGetSourcei(currentMusic.source, AL_SOURCE_STATE, &state);
+        
+        if ((state != AL_PLAYING) && active) alSourcePlay(currentMusic.source);
+        
+        if (!active) StopMusicStream();
+    }
 }
 
 // Load WAV file into Wave structure
-static Wave LoadWAV(char *fileName) 
+static Wave LoadWAV(const char *fileName)
 {
     // Basic WAV headers structs
     typedef struct {
@@ -543,199 +753,51 @@ static Wave LoadWAV(char *fileName)
     return wave;
 }
 
-// Unload WAV file data
-static void UnloadWAV(Wave wave)
+// Load OGG file into Wave structure
+static Wave LoadOGG(char *fileName)
 {
-    free(wave.data);
-}
-
-// TODO: Ogg data loading
-Music LoadMusic(char *fileName)
-{
-    Music music;
+    Wave wave;
     
-    // Open audio stream
-    music.stream = stb_vorbis_open_filename(fileName, NULL, NULL);
+    stb_vorbis *oggFile = stb_vorbis_open_filename(fileName, NULL, NULL);
+    stb_vorbis_info info = stb_vorbis_get_info(oggFile);
     
-	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;
-    }
+    wave.sampleRate = info.sample_rate;
+    wave.bitsPerSample = 16;
+    wave.channels = info.channels;
     
-    return music;
-}
+    TraceLog(DEBUG, "[%s] Ogg sample rate: %i", fileName, info.sample_rate);
+    TraceLog(DEBUG, "[%s] Ogg channels: %i", fileName, info.channels);
 
-void UnloadMusic(Music music)
-{
-    StopMusic(music);
-
-    alDeleteSources(1, &music.source);
-	alDeleteBuffers(2, music.buffers);
+    int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile) * info.channels);
     
-	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");
+    wave.dataSize = totalSamplesLength*sizeof(short);   // Size must be in bytes
     
-    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");
+    TraceLog(DEBUG, "[%s] Samples length: %i", fileName, totalSamplesLength);
     
-    alSourceQueueBuffers(currentMusic->source, 2, currentMusic->buffers);
-    alSourcePlay(currentMusic->source);
-}
-
-// Stop reproducing music
-void StopMusic(Music music)
-{
-    alSourceStop(music.source);
+    float totalSeconds = stb_vorbis_stream_length_in_seconds(oggFile);
     
-    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);
+    TraceLog(DEBUG, "[%s] Total seconds: %f", fileName, totalSeconds);
     
-	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);
+    if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio lenght is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds);
     
-    printf("Data processed: %i\n", processed);
- 
-    while (processed--)
-    {
-        ALuint buffer = 0;
-        
-        alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
+    int totalSamples = totalSeconds*info.sample_rate*info.channels;
+   
+    TraceLog(DEBUG, "[%s] Total samples calculated: %i", fileName, totalSamples);
+    
+    //short *data 
+    wave.data = malloc(sizeof(short)*totalSamplesLength);
 
-        active = MusicStream(*currentMusic, buffer);
- 
-        alSourceQueueBuffers(currentMusic->source, 1, &buffer);
-    }
- 
-    return active;
+    int samplesObtained = stb_vorbis_get_samples_short_interleaved(oggFile, info.channels, wave.data, totalSamplesLength);
+    
+    TraceLog(DEBUG, "[%s] Samples obtained: %i", fileName, samplesObtained);
+
+    stb_vorbis_close(oggFile);
+    
+    return wave;
 }
 
-void MusicStreamEmpty()
+// Unload Wave data
+static void UnloadWave(Wave wave)
 {
-    int queued;
-    
-    alGetSourcei(currentMusic->source, AL_BUFFERS_QUEUED, &queued);
-    
-    while(queued--)
-    {
-        ALuint buffer;  
-        alSourceUnqueueBuffers(currentMusic->source, 1, &buffer);
-    }
+    free(wave.data);
 }

+ 7 - 8
src/core.c

@@ -89,11 +89,10 @@ static Color background = { 0, 0, 0, 0 };   // Screen background color
 //----------------------------------------------------------------------------------
 // Other Modules Functions Declaration (required by core)
 //----------------------------------------------------------------------------------
-extern void LoadDefaultFont();               // [Module: text] Loads default font on InitWindow()
-extern void UnloadDefaultFont();             // [Module: text] Unloads default font from GPU memory
+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
+extern void UpdateMusicStream();            // [Module: audio] Updates buffers for music streaming
 
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
@@ -103,7 +102,7 @@ static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, i
 static void ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);            // GLFW3 Srolling Callback, runs on mouse wheel
 static void CursorEnterCallback(GLFWwindow* window, int enter);                            // GLFW3 Cursor Enter Callback, cursor enters client area
 static void WindowSizeCallback(GLFWwindow* window, int width, int height);                 // GLFW3 WindowSize Callback, runs when window is resized
-static void TakeScreenshot();                                                              // Takes a bitmap (BMP) screenshot and saves it in the same folder as executable
+static void TakeScreenshot();                                                              // Takes a screenshot and saves it in the same folder as executable
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Window and OpenGL Context Functions
@@ -304,9 +303,7 @@ void EndDrawing()
     glfwSwapBuffers(window);            // Swap back and front buffers
     glfwPollEvents();                   // Register keyboard/mouse events
     
-    //MusicStreamUpdate();
-    //if (!MusicIsPlaying()) 
-    //PlayCurrentMusic();
+    UpdateMusicStream();        // NOTE: Function checks if music is enabled
     
     currentTime = glfwGetTime();
     drawTime = currentTime - previousTime;
@@ -748,4 +745,6 @@ static void TakeScreenshot()
     free(imgData);
 
     shotNum++;
+    
+    TraceLog(INFO, "[%s] Screenshot taken!", buffer);
 }

+ 343 - 304
src/models.c

@@ -25,9 +25,9 @@
 
 #include "raylib.h"
 
-#include <GL/gl.h>       // OpenGL functions
 #include <stdio.h>       // Standard input/output functions, used to read model files data
 #include <stdlib.h>      // Declares malloc() and free() for memory management
+#include <string.h>      // Required for strcmp()
 #include <math.h>        // Used for sin, cos, tan
 
 #include "raymath.h"     // Required for data type Matrix and Matrix functions
@@ -52,6 +52,7 @@
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
 static float GetHeightValue(Color pixel);
+static VertexData LoadOBJ(const char *fileName);
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition
@@ -67,9 +68,9 @@ void DrawCube(Vector3 position, float width, float height, float lenght, Color c
 
     rlPushMatrix();
 
-        // NOTE: Be careful! Function order matters (scale, translate, rotate)
-        //rlScalef(2.0f, 2.0f, 2.0f);
+        // NOTE: Be careful! Function order matters (rotate -> scale -> translate)
         //rlTranslatef(0.0f, 0.0f, 0.0f);
+        //rlScalef(2.0f, 2.0f, 2.0f);
         //rlRotatef(45, 0, 1, 0);
     
         rlBegin(RL_TRIANGLES);
@@ -215,9 +216,9 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei
     float y = position.y;
     float z = position.z;
 
-    rlEnableTexture(texture.glId);
+    rlEnableTexture(texture.id);
     
-    rlPushMatrix();      
+    //rlPushMatrix();      
         // NOTE: Be careful! Function order matters (scale, translate, rotate)
         //rlScalef(2.0f, 2.0f, 2.0f);
         //rlTranslatef(2.0f, 0.0f, 0.0f);
@@ -262,7 +263,7 @@ void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float hei
             rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z+lenght/2);  // Top Right Of The Texture and Quad
             rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x-width/2, y+height/2, z-lenght/2);  // Top Left Of The Texture and Quad
         rlEnd();
-    rlPopMatrix();
+    //rlPopMatrix();
     
     rlDisableTexture();
 }
@@ -278,13 +279,13 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
 {
     rlPushMatrix();
         rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
-        //rlRotatef(rotation, 0, 1, 0);
         rlScalef(radius, radius, radius);
+        //rlRotatef(rotation, 0, 1, 0);
         
         rlBegin(RL_TRIANGLES);
             rlColor4ub(color.r, color.g, color.b, color.a);
             
-            for(int i = 0; i < 2 * rings + 1; i ++)
+            for(int i = 0; i < 2 * rings + 1; i++)
             {
                 for(int j = 0; j < slices; j++)
                 {
@@ -317,14 +318,14 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
 void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color)
 {
     rlPushMatrix();
-        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
-        //rlRotatef(rotation, 0, 1, 0);
+        //rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
         rlScalef(radius, radius, radius);
+        //rlRotatef(rotation, 0, 1, 0);
         
         rlBegin(RL_LINES);
             rlColor4ub(color.r, color.g, color.b, color.a);
             
-            for(int i = 0; i < 2 * rings + 1; i ++)
+            for(int i = 0; i < 2 * rings + 1; i++)
             {
                 for(int j = 0; j < slices; j++)
                 {
@@ -447,12 +448,12 @@ void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color)
     // NOTE: Plane is always created on XZ ground and then rotated
     rlPushMatrix();
         rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
+        rlScalef(size.x, 1.0f, size.y);
         
         // TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions...
         rlRotatef(rotation.x, 1, 0, 0);
         rlRotatef(rotation.y, 0, 1, 0);
         rlRotatef(rotation.z, 0, 0, 1);
-        rlScalef(size.x, 1.0f, size.y);
     
         rlBegin(RL_QUADS);
             rlColor4ub(color.r, color.g, color.b, color.a);
@@ -568,14 +569,13 @@ void DrawGizmo(Vector3 position)
     rlPopMatrix();
 }
 
-void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits)
-{
-    static float rotation = 0;
+void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale)
+{   
     // NOTE: RGB = XYZ
     rlPushMatrix();
         rlTranslatef(position.x, position.y, position.z);
-        rlRotatef(rotation, 0, 1, 0);
         rlScalef(scale, scale, scale);
+        rlRotatef(rotation.y, 0, 1, 0);
 
         rlBegin(RL_LINES);
             // X Axis
@@ -612,279 +612,53 @@ void DrawGizmoEx(Vector3 position, Vector3 rot, float scale, bool orbits)
             rlColor4ub(0, 0, 200, 255); rlVertex3f(position.x - .1, position.y, position.z - .9);
             
             // Extra
-            if(orbits)
+            int n = 3;
+            
+            // X Axis
+            for (int i=0; i < 360; i += 6)
             {
-                int n = 3;
-                
-                // X Axis
-                for (int i=0; i < 360; i += 6)
-                {
-                    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
-                for (int i=0; i < 360; i += 6)
-                {
-                    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
-                for (int i=0; i < 360; i += 6)
-                {
-                    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);
-                }
+                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
+            for (int i=0; i < 360; i += 6)
+            {
+                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
+            for (int i=0; i < 360; i += 6)
+            {
+                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)
-// TODO: Add comments explaining this function process
+// Load a 3d model
 Model LoadModel(const char *fileName)                                    
 {
     VertexData vData;
     
-    char dataType;
-    char comments[200];
-    
-    int numVertex = 0;
-    int numNormals = 0;
-    int numTexCoords = 0;
-    int numTriangles = 0;
-
-    FILE* objFile;
-
-    objFile = fopen(fileName, "rt");
-    
-    while(!feof(objFile))
-    {
-        fscanf(objFile, "%c", &dataType);
-        
-        switch(dataType)
-        {
-            case '#':         // It's a comment
-            {
-                fgets(comments, 200, objFile);                
-            } break;
-            case 'v': 
-            {
-                fscanf(objFile, "%c", &dataType);
-                
-                if (dataType == 't')    // Read texCoord
-                {
-                    fgets(comments, 200, objFile);
-                    fscanf(objFile, "%c", &dataType);
-                
-                    while (dataType == 'v')
-                    {
-                        fgets(comments, 200, objFile);
-                        fscanf(objFile, "%c", &dataType);
-                    }
-                    
-                    if (dataType == '#')
-                    {
-                        fscanf(objFile, "%i", &numTexCoords);
-                    }
-                    
-                    fgets(comments, 200, objFile);
-                }
-                else if (dataType == 'n')    // Read normals
-                {
-                    fgets(comments, 200, objFile);
-                    fscanf(objFile, "%c", &dataType);
-                
-                    while (dataType == 'v')
-                    {
-                        fgets(comments, 200, objFile);
-                        fscanf(objFile, "%c", &dataType);
-                    }
-                    
-                    if (dataType == '#')
-                    {
-                        fscanf(objFile, "%i", &numNormals);
-                    }
-                
-                    fgets(comments, 200, objFile);
-                }
-                else    // Read vertex
-                {
-                    fgets(comments, 200, objFile);
-                    fscanf(objFile, "%c", &dataType);
-                
-                    while (dataType == 'v')
-                    {
-                        fgets(comments, 200, objFile);
-                        fscanf(objFile, "%c", &dataType);
-                    }
-                    
-                    if (dataType == '#')
-                    {
-                        fscanf(objFile, "%i", &numVertex);
-                    }
-                    
-                    fgets(comments, 200, objFile);
-                }
-            } break;
-            case 'f':
-            {
-                fgets(comments, 200, objFile);
-                fscanf(objFile, "%c", &dataType);
-            
-                while (dataType == 'f')
-                {
-                    fgets(comments, 200, objFile);
-                    fscanf(objFile, "%c", &dataType);
-                }
-                
-                if (dataType == '#')
-                {
-                    fscanf(objFile, "%i", &numTriangles);
-                }
-                
-                fgets(comments, 200, objFile);
-            
-            } break;
-            default: break;
-        }
-    }
-    
-    Vector3 midVertices[numVertex];
-    Vector3 midNormals[numNormals];
-    Vector2 midTexCoords[numTexCoords];
-    
-    vData.numVertices = numTriangles*3;
-    
-    vData.vertices = (float *)malloc(vData.numVertices * 3 * sizeof(float));
-    vData.texcoords = (float *)malloc(vData.numVertices * 2 * sizeof(float));
-    vData.normals = (float *)malloc(vData.numVertices * 3 * sizeof(float));
-    
-    int countVertex = 0;
-    int countNormals = 0;
-    int countTexCoords = 0;
-    
-    int vCounter = 0;       // Used to count vertices float by float
-    int tcCounter = 0;      // Used to count texcoords float by float
-    int nCounter = 0;       // Used to count normals float by float
-    
-    rewind(objFile);
-    
-    while(!feof(objFile))
-    {
-        fscanf(objFile, "%c", &dataType);
-        
-        switch(dataType)
-        {
-            case '#': 
-            {
-                fgets(comments, 200, objFile);                
-            } break;
-            case 'v': 
-            {
-                fscanf(objFile, "%c", &dataType);
-                
-                if (dataType == 't')    // Read texCoord
-                {
-                    float useless = 0;
-                
-                    fscanf(objFile, "%f %f %f", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y, &useless);
-                    countTexCoords++;
-                    
-                    fscanf(objFile, "%c", &dataType);
-                }
-                else if (dataType == 'n')    // Read normals
-                {
-                    fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z );
-                    countNormals++;
-                    
-                    fscanf(objFile, "%c", &dataType);
-                }
-                else    // Read vertex
-                {
-                    fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z );
-                    countVertex++;
-                    
-                    fscanf(objFile, "%c", &dataType);
-                }
-            } break;
-            case 'f':
-            {
-                // At this point all vertex data (v, vt, vn) have been gathered on midVertices, midTexCoords, midNormals
-                // Now we can organize that data into our VertexData struct
-            
-                int vNum, vtNum, vnNum;
-                fscanf(objFile, "%c", &dataType);
-                fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
-                
-                vData.vertices[vCounter] = midVertices[vNum-1].x;
-                vData.vertices[vCounter + 1] = midVertices[vNum-1].y;
-                vData.vertices[vCounter + 2] = midVertices[vNum-1].z;
-                vCounter += 3;
-                
-                vData.normals[nCounter] = midNormals[vnNum-1].x;
-                vData.normals[nCounter + 1] = midNormals[vnNum-1].y;
-                vData.normals[nCounter + 2] = midNormals[vnNum-1].z;
-                nCounter += 3;
-                
-                vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x;
-                vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y;
-                tcCounter += 2;
-
-                fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
-                
-                vData.vertices[vCounter] = midVertices[vNum-1].x;
-                vData.vertices[vCounter + 1] = midVertices[vNum-1].y;
-                vData.vertices[vCounter + 2] = midVertices[vNum-1].z;
-                vCounter += 3;
-                
-                vData.normals[nCounter] = midNormals[vnNum-1].x;
-                vData.normals[nCounter + 1] = midNormals[vnNum-1].y;
-                vData.normals[nCounter + 2] = midNormals[vnNum-1].z;
-                nCounter += 3;
-                
-                vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x;
-                vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y;
-                tcCounter += 2;
-                
-                fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
-                
-                vData.vertices[vCounter] = midVertices[vNum-1].x;
-                vData.vertices[vCounter + 1] = midVertices[vNum-1].y;
-                vData.vertices[vCounter + 2] = midVertices[vNum-1].z;
-                vCounter += 3;
-                
-                vData.normals[nCounter] = midNormals[vnNum-1].x;
-                vData.normals[nCounter + 1] = midNormals[vnNum-1].y;
-                vData.normals[nCounter + 2] = midNormals[vnNum-1].z;
-                nCounter += 3;
-                
-                vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x;
-                vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y;
-                tcCounter += 2;
-            } break;
-            default: break;
-        }
-    }
-    
-    fclose(objFile);
-    
-    // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct
+    if (strcmp(GetExtension(fileName),"obj") == 0) vData = LoadOBJ(fileName);
+    else TraceLog(WARNING, "[%s] Model extension not recognized, it can't be loaded", fileName); 
 
     Model model;
 
-#ifdef USE_OPENGL_11
-    model.data = vData;      // model data is vertex data  
-#else   
-    model.vaoId = rlglLoadModel(vData);     // Use loaded data to generate VAO
+    model.mesh = vData;                     // Model mesh is vertex data
+    model.textureId = 0;
     
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
+    model.vaoId = rlglLoadModel(vData);     // Use loaded data to generate VAO
+    model.textureId = 1;                    // Default whiteTexture
+
     // Now that vertex data is uploaded to GPU, we can free arrays
-    free(vData.vertices);
-    free(vData.texcoords);
-    free(vData.normals);
+    //free(vData.vertices);
+    //free(vData.texcoords);
+    //free(vData.normals);
 #endif
 
     return model;
@@ -902,11 +676,11 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
     // TODO: Consider resolution when generating model data?
     int numTriangles = (mapX-1)*(mapZ-1)*2;    // One quad every four pixels
   
-    vData.numVertices = numTriangles*3;
+    vData.vertexCount = numTriangles*3;
 
-    vData.vertices = (float *)malloc(vData.numVertices * 3 * sizeof(float));
-    vData.normals = (float *)malloc(vData.numVertices * 3 * sizeof(float));
-    vData.texcoords = (float *)malloc(vData.numVertices * 2 * sizeof(float));
+    vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
+    vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
+    vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float));
     
     int vCounter = 0;       // Used to count vertices float by float
     int tcCounter = 0;      // Used to count texcoords float by float
@@ -973,7 +747,7 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
             
             // Fill normals array with data
             //--------------------------------------------------------------
-            // TODO: Review normals calculation
+            // NOTE: Current Model implementation doe not use normals! 
             for (int i = 0; i < 18; i += 3)
             {
                 vData.normals[nCounter + i] = 0.0f;
@@ -981,6 +755,8 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
                 vData.normals[nCounter + i + 2] = 0.0f;
             }
             
+            // TODO: Calculate normals in an efficient way
+            
             nCounter += 18;     // 6 vertex, 18 floats
             
             trisCounter += 2;
@@ -991,15 +767,17 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
 
     Model model;
 
-#ifdef USE_OPENGL_11
-    model.data = vData;      // model data is vertex data  
-#else   
+    model.mesh = vData;                     // Model mesh is vertex data
+    model.textureId = 0;
+    
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     model.vaoId = rlglLoadModel(vData);     // Use loaded data to generate VAO
+    model.textureId = 1;                    // Default whiteTexture
 
     // Now that vertex data is uploaded to GPU, we can free arrays
-    free(vData.vertices);
-    free(vData.texcoords);
-    free(vData.normals);
+    //free(vData.vertices);
+    //free(vData.texcoords);
+    //free(vData.normals);
 #endif
 
     return model;
@@ -1008,37 +786,43 @@ Model LoadHeightmap(Image heightmap, float maxHeight)
 // Unload 3d model from memory
 void UnloadModel(Model model)
 {
-#ifdef USE_OPENGL_11
-    free(model.data.vertices);
-    free(model.data.texcoords);
-    free(model.data.normals);
-#endif
+    free(model.mesh.vertices);
+    free(model.mesh.texcoords);
+    free(model.mesh.normals);
 
 #if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     rlDeleteVertexArrays(model.vaoId);
 #endif
 }
 
-// Draw a model
-void DrawModel(Model model, Vector3 position, float scale, Color color)
+void SetModelTexture(Model *model, Texture2D texture)
 {
-    rlglDrawModel(model, position, scale, false);
+    if (texture.id <= 0) model->textureId = 1;  // Default white texture (use mesh color)
+    else model->textureId = texture.id;
 }
 
-// Draw a textured model
-void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, Color tint)
+// Draw a model (with texture if set)
+void DrawModel(Model model, Vector3 position, float scale, Color tint)
 {
-    rlEnableTexture(texture.glId);
-    
-    DrawModel(model, position, scale, tint);
-    
-    rlDisableTexture();
+    Vector3 vScale = { scale, scale, scale };
+    Vector3 rotation = { 0, 0, 0 };
+
+    rlglDrawModel(model, position, rotation, vScale, tint, false);
 }
 
-// Draw a model wires
+// Draw a model with extended parameters
+void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint)
+{
+    rlglDrawModel(model, position, rotation, scale, tint, false);
+}
+
+// Draw a model wires (with texture if set)
 void DrawModelWires(Model model, Vector3 position, float scale, Color color)
 {
-    rlglDrawModel(model, position, scale, true);
+    Vector3 vScale = { scale, scale, scale };
+    Vector3 rotation = { 0, 0, 0 };
+
+    rlglDrawModel(model, position, rotation, vScale, color, true);
 }
 
 // Draw a billboard
@@ -1070,7 +854,7 @@ void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size,
     Vector3 c = VectorAdd(center, p2);
     Vector3 d = VectorSubtract(center, p1);
     
-    rlEnableTexture(texture.glId);
+    rlEnableTexture(texture.id);
       
     rlBegin(RL_QUADS);
         rlColor4ub(tint.r, tint.g, tint.b, tint.a);
@@ -1113,7 +897,7 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
     Vector3 c = VectorAdd(center, p2);
     Vector3 d = VectorSubtract(center, p1);
     
-    rlEnableTexture(texture.glId);
+    rlEnableTexture(texture.id);
     
     rlBegin(RL_QUADS);
         rlColor4ub(tint.r, tint.g, tint.b, tint.a);
@@ -1142,4 +926,259 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
 static float GetHeightValue(Color pixel)
 {
     return (((float)pixel.r + (float)pixel.g + (float)pixel.b)/3);
+}
+
+// Load OBJ mesh data
+static VertexData LoadOBJ(const char *fileName)
+{
+    VertexData vData;
+    
+    char dataType;
+    char comments[200];
+    
+    int numVertex = 0;
+    int numNormals = 0;
+    int numTexCoords = 0;
+    int numTriangles = 0;
+
+    FILE* objFile;
+
+    objFile = fopen(fileName, "rt");
+    
+    // First pass over all file to get numVertex, numNormals, numTexCoords, numTriangles
+    // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition)
+    while(!feof(objFile))
+    {
+        fscanf(objFile, "%c", &dataType);
+        
+        switch(dataType)
+        {
+            case '#':         // It's a comment
+            {
+                fgets(comments, 200, objFile);                
+            } break;
+            case 'o':         // New object
+            {
+                // TODO: Read multiple objects, we need to know numMeshes + verticesPerMesh
+                
+                // NOTE: One OBJ file can contain multible meshes defined, one after every 'o'
+                
+            } break;
+            case 'v':
+            {
+                fscanf(objFile, "%c", &dataType);
+                
+                if (dataType == 't')    // Read texCoord
+                {
+                    fgets(comments, 200, objFile);
+                    fscanf(objFile, "%c", &dataType);
+                
+                    while (dataType == 'v')
+                    {
+                        fgets(comments, 200, objFile);
+                        fscanf(objFile, "%c", &dataType);
+                    }
+                    
+                    if (dataType == '#')
+                    {
+                        fscanf(objFile, "%i", &numTexCoords);
+                    }
+                    
+                    fgets(comments, 200, objFile);
+                }
+                else if (dataType == 'n')    // Read normals
+                {
+                    fgets(comments, 200, objFile);
+                    fscanf(objFile, "%c", &dataType);
+                
+                    while (dataType == 'v')
+                    {
+                        fgets(comments, 200, objFile);
+                        fscanf(objFile, "%c", &dataType);
+                    }
+                    
+                    if (dataType == '#')
+                    {
+                        fscanf(objFile, "%i", &numNormals);
+                    }
+                
+                    fgets(comments, 200, objFile);
+                }
+                else    // Read vertex
+                {
+                    fgets(comments, 200, objFile);
+                    fscanf(objFile, "%c", &dataType);
+                
+                    while (dataType == 'v')
+                    {
+                        fgets(comments, 200, objFile);
+                        fscanf(objFile, "%c", &dataType);
+                    }
+                    
+                    if (dataType == '#')
+                    {
+                        fscanf(objFile, "%i", &numVertex);
+                    }
+                    
+                    fgets(comments, 200, objFile);
+                }
+            } break;
+            case 'f':
+            {
+                fgets(comments, 200, objFile);
+                fscanf(objFile, "%c", &dataType);
+            
+                while (dataType == 'f')
+                {
+                    fgets(comments, 200, objFile);
+                    fscanf(objFile, "%c", &dataType);
+                }
+                
+                if (dataType == '#')
+                {
+                    fscanf(objFile, "%i", &numTriangles);
+                }
+                
+                fgets(comments, 200, objFile);
+            
+            } break;
+            default: break;
+        }
+    }
+    
+    // Once we know the number of vertices to store, we create required arrays
+    Vector3 *midVertices = (Vector3 *)malloc(numVertex*sizeof(Vector3));
+    Vector3 *midNormals = (Vector3 *)malloc(numNormals*sizeof(Vector3));
+    Vector2 *midTexCoords = (Vector2 *)malloc(numTexCoords*sizeof(Vector2));
+    
+    vData.vertexCount = numTriangles*3;
+    
+    // Additional arrays to store vertex data as floats
+    vData.vertices = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
+    vData.texcoords = (float *)malloc(vData.vertexCount * 2 * sizeof(float));
+    vData.normals = (float *)malloc(vData.vertexCount * 3 * sizeof(float));
+    vData.colors = (float *)malloc(vData.vertexCount * 4 * sizeof(float));
+    
+    int countVertex = 0;
+    int countNormals = 0;
+    int countTexCoords = 0;
+    
+    int vCounter = 0;       // Used to count vertices float by float
+    int tcCounter = 0;      // Used to count texcoords float by float
+    int nCounter = 0;       // Used to count normals float by float
+    
+    rewind(objFile);        // Return to the beginning of the file, to read again
+    
+    // Reading again file to get vertex data
+    while(!feof(objFile))
+    {
+        fscanf(objFile, "%c", &dataType);
+        
+        switch(dataType)
+        {
+            case '#': 
+            {
+                fgets(comments, 200, objFile);                
+            } break;
+            case 'v': 
+            {
+                fscanf(objFile, "%c", &dataType);
+                
+                if (dataType == 't')    // Read texCoord
+                {
+                    float useless = 0;
+                
+                    fscanf(objFile, "%f %f %f", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y, &useless);
+                    countTexCoords++;
+                    
+                    fscanf(objFile, "%c", &dataType);
+                }
+                else if (dataType == 'n')    // Read normals
+                {
+                    fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z );
+                    countNormals++;
+                    
+                    fscanf(objFile, "%c", &dataType);
+                }
+                else    // Read vertex
+                {
+                    fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z );
+                    countVertex++;
+                    
+                    fscanf(objFile, "%c", &dataType);
+                }
+            } break;
+            case 'f':
+            {
+                // At this point all vertex data (v, vt, vn) have been gathered on midVertices, midTexCoords, midNormals
+                // Now we can organize that data into our VertexData struct
+            
+                int vNum, vtNum, vnNum;
+                fscanf(objFile, "%c", &dataType);
+                fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
+                
+                vData.vertices[vCounter] = midVertices[vNum-1].x;
+                vData.vertices[vCounter + 1] = midVertices[vNum-1].y;
+                vData.vertices[vCounter + 2] = midVertices[vNum-1].z;
+                vCounter += 3;
+                
+                vData.normals[nCounter] = midNormals[vnNum-1].x;
+                vData.normals[nCounter + 1] = midNormals[vnNum-1].y;
+                vData.normals[nCounter + 2] = midNormals[vnNum-1].z;
+                nCounter += 3;
+                
+                vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x;
+                vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y;
+                tcCounter += 2;
+
+                fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
+                
+                vData.vertices[vCounter] = midVertices[vNum-1].x;
+                vData.vertices[vCounter + 1] = midVertices[vNum-1].y;
+                vData.vertices[vCounter + 2] = midVertices[vNum-1].z;
+                vCounter += 3;
+                
+                vData.normals[nCounter] = midNormals[vnNum-1].x;
+                vData.normals[nCounter + 1] = midNormals[vnNum-1].y;
+                vData.normals[nCounter + 2] = midNormals[vnNum-1].z;
+                nCounter += 3;
+                
+                vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x;
+                vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y;
+                tcCounter += 2;
+                
+                fscanf(objFile, "%i/%i/%i", &vNum, &vtNum, &vnNum);
+                
+                vData.vertices[vCounter] = midVertices[vNum-1].x;
+                vData.vertices[vCounter + 1] = midVertices[vNum-1].y;
+                vData.vertices[vCounter + 2] = midVertices[vNum-1].z;
+                vCounter += 3;
+                
+                vData.normals[nCounter] = midNormals[vnNum-1].x;
+                vData.normals[nCounter + 1] = midNormals[vnNum-1].y;
+                vData.normals[nCounter + 2] = midNormals[vnNum-1].z;
+                nCounter += 3;
+                
+                vData.texcoords[tcCounter] = midTexCoords[vtNum-1].x;
+                vData.texcoords[tcCounter + 1] = -midTexCoords[vtNum-1].y;
+                tcCounter += 2;
+            } break;
+            default: break;
+        }
+    }
+    
+    fclose(objFile);
+    
+    // NOTE: We set all vertex colors to white
+    for (int i = 0; i < (4*vData.vertexCount); i++) vData.colors[i] = 1.0f;
+    
+    // Now we can free temp mid* arrays
+    free(midVertices);
+    free(midNormals);
+    free(midTexCoords);
+    
+    // NOTE: At this point we have all vertex, texcoord, normal data for the model in vData struct
+    TraceLog(INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName);
+    
+    return vData;
 }

+ 56 - 48
src/raylib.h

@@ -10,13 +10,17 @@
 *     Hardware accelerated with OpenGL (1.1, 3.3+ or ES2)
 *     Unique OpenGL abstraction layer [rlgl]
 *     Powerful fonts module with SpriteFonts support
+*     Multiple textures support, including DDS and mipmaps generation
 *     Basic 3d support for Shapes, Models, Heightmaps and Billboards
 *     Powerful math module for Vector and Matrix operations [raymath]
-*     Audio loading and playing
+*     Audio loading and playing with streaming support
 *    
 *   Used external libs:
 *     GLFW3 (www.glfw.org) for window/context management and input
+*     GLEW for OpenGL extensions loading (3.3+ and ES2)
 *     stb_image (Sean Barret) for images loading (JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC)
+*     stb_image_write (Sean Barret) for image writting (PNG)
+*     stb_vorbis (Sean Barret) for ogg audio loading
 *     OpenAL Soft for audio device/context management
 *     tinfl for data decompression (DEFLATE algorithm)
 *
@@ -25,9 +29,9 @@
 *     32bit Textures - All loaded images are converted automatically to RGBA textures
 *     SpriteFonts - All loaded sprite-font images are converted to RGBA and POT textures
 *     One custom default font is loaded automatically when InitWindow()
-*     If using OpenGL 3.3+, one default shader is loaded automatically (internally defined)
+*     If using OpenGL 3.3+ or ES2, one default shader is loaded automatically (internally defined)
 *
-*   -- LICENSE (raylib v1.1, March 2014) --
+*   -- LICENSE (raylib v1.1, April 2014) --
 *
 *   raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, 
 *   BSD-like license that allows static linking with closed source software:
@@ -52,9 +56,7 @@
 **********************************************************************************************/
 
 #ifndef RAYLIB_H
-#define RAYLIB_H 
-
-#include "stb_vorbis.h"
+#define RAYLIB_H
 
 //----------------------------------------------------------------------------------
 // Some basic Defines
@@ -195,30 +197,46 @@ typedef struct Image {
 // Texture2D type, bpp always RGBA (32bit)
 // NOTE: Data stored in GPU memory
 typedef struct Texture2D {
-    unsigned int glId;
+    unsigned int id;        // OpenGL id
     int width;
     int height;
 } Texture2D;
 
-// Camera type, defines a camera position/orientation in 3d space
-typedef struct Camera {
-    Vector3 position;
-    Vector3 target;
-    Vector3 up;
-} Camera;
-
+// Character type (one font glyph)
+// NOTE: Defined in module: text
 typedef struct Character Character;
 
-// SpriteFont type
+// SpriteFont type, includes texture and charSet array data
 typedef struct SpriteFont {
     Texture2D texture;
     int numChars;
     Character *charSet;
 } SpriteFont;
 
+// Camera type, defines a camera position/orientation in 3d space
+typedef struct Camera {
+    Vector3 position;
+    Vector3 target;
+    Vector3 up;
+} Camera;
+
+// Vertex data definning a mesh
+typedef struct {
+    int vertexCount;
+    float *vertices;            // 3 components per vertex
+    float *texcoords;           // 2 components per vertex
+    float *normals;             // 3 components per vertex
+    float *colors;              // 4 components per vertex
+} VertexData;
+
 // 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
+// NOTE: If using OpenGL 1.1 loaded in CPU (mesh); if OpenGL 3.3+ loaded in GPU (vaoId)
+typedef struct Model {
+    VertexData mesh;
+    unsigned int vaoId;
+    unsigned int textureId;
+    //Matrix transform;
+} Model;
 
 // Sound source type
 typedef struct Sound {
@@ -226,23 +244,6 @@ typedef struct Sound {
     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
@@ -342,7 +343,7 @@ Image LoadImage(const char *fileName);
 Image LoadImageFromRES(const char *rresName, int resId);                                           // Load an image from rRES file (raylib Resource)
 Texture2D LoadTexture(const char *fileName);                                                       // Load an image as texture into GPU memory
 Texture2D LoadTextureFromRES(const char *rresName, int resId);                                     // Load an image as texture from rRES file (raylib Resource)
-Texture2D CreateTexture(Image image);                                                              // Create a Texture2D from Image data
+Texture2D CreateTexture(Image image, bool genMipmaps);                                             // Create a Texture2D from Image data (and generate mipmaps)
 void UnloadImage(Image image);                                                                     // Unload image from CPU memory (RAM)
 void UnloadTexture(Texture2D texture);                                                             // Unload texture from GPU memory
 
@@ -359,6 +360,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
 SpriteFont GetDefaultFont();                                                                       // Get the default SpriteFont
 SpriteFont LoadSpriteFont(const char *fileName);                                                   // Load a SpriteFont image into GPU memory
 void UnloadSpriteFont(SpriteFont spriteFont);                                                      // Unload SpriteFont from GPU memory
+
 void DrawText(const char *text, int posX, int posY, int fontSize, Color color);                    // Draw text (using default font)
 void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position,                         // Draw text using SpriteFont and additional parameters
                 int fontSize, int spacing, Color tint);
@@ -384,7 +386,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 rot, float scale, bool orbits);                    // Draw gizmo with extended parameters
+void DrawGizmoEx(Vector3 position, Vector3 rotation, float scale);                                 // Draw gizmo with extended parameters
 //DrawTorus(), DrawTeapot() are useless...
 
 //------------------------------------------------------------------------------------
@@ -394,9 +396,12 @@ Model LoadModel(const char *fileName);
 //Model LoadModelFromRES(const char *rresName, int resId);                                         // TODO: Load a 3d model from rRES file (raylib Resource)
 Model LoadHeightmap(Image heightmap, float maxHeight);                                             // Load a heightmap image as a 3d model
 void UnloadModel(Model model);                                                                     // Unload 3d model from memory
-void DrawModel(Model model, Vector3 position, float scale, Color color);                           // Draw a model
-void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, Color tint);       // Draw a textured model
-void DrawModelWires(Model model, Vector3 position, float scale, Color color);                      // Draw a model wires
+void SetModelTexture(Model *model, Texture2D texture);                                             // Link a texture to a model
+
+void DrawModel(Model model, Vector3 position, float scale, Color tint);                            // Draw a model (with texture if set)
+void DrawModelEx(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color tint);      // Draw a model with extended parameters
+void DrawModelWires(Model model, Vector3 position, float scale, Color color);                      // Draw a model wires (with texture if set)
+
 void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint);                         // Draw a billboard texture
 void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint); // Draw a billboard texture defined by sourceRec 
 
@@ -404,22 +409,25 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vec
 // Audio Loading and Playing Functions (Module: audio)
 //------------------------------------------------------------------------------------
 void InitAudioDevice();                                         // Initialize audio device and context
-void CloseAudioDevice();                                        // Close the audio device and context
+void CloseAudioDevice();                                        // Close the audio device and context (and music stream)
+
 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 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();
+void SetSoundVolume(Sound sound, float volume);                 // Set volume for a sound (1.0 is max level)
+void SetSoundPitch(Sound sound, float pitch);                   // Set pitch for a sound (1.0 is base level)
+
+void PlayMusicStream(char *fileName);                           // Start music playing (open stream)
+void StopMusicStream();                                         // Stop music playing (close stream)
+void PauseMusicStream();                                        // Pause music playing
+bool MusicIsPlaying();                                          // Check if music is playing
+void SetMusicVolume(float volume);                              // Set volume for music (1.0 is max level)
+float GetMusicTimeLength();                                     // Get current music time length (in seconds)
+float GetMusicTimePlayed();                                     // Get current music time played (in seconds)
 
 #ifdef __cplusplus
 }

+ 20 - 2
src/raymath.c

@@ -435,7 +435,7 @@ Matrix MatrixSubstract(Matrix left, Matrix right)
 }
 
 // Returns translation matrix
-// TODO: REVIEW
+// TODO: Review this function
 Matrix MatrixTranslate(float x, float y, float z)
 {
 /*
@@ -478,6 +478,7 @@ Matrix MatrixTranslate(float x, float y, float z)
 }
 
 // Returns rotation matrix
+// TODO: Review this function
 Matrix MatrixRotate(float angleX, float angleY, float angleZ)
 {
     Matrix result;
@@ -492,6 +493,7 @@ Matrix MatrixRotate(float angleX, float angleY, float angleZ)
 }
 
 // Create rotation matrix from axis and angle
+// TODO: Test this function
 Matrix MatrixFromAxisAngle(Vector3 axis, float angle) 
 {
     Matrix result;
@@ -545,7 +547,8 @@ Matrix MatrixFromAxisAngle(Vector3 axis, float angle)
     return result;
 };
 
-// Create rotation matrix from axis and angle
+// Create rotation matrix from axis and angle (version 2)
+// TODO: Test this function
 Matrix MatrixFromAxisAngle2(Vector3 axis, float angle)
 {
     Matrix result;
@@ -661,6 +664,21 @@ Matrix MatrixScale(float x, float y, float z)
     return result;
 }
 
+// Returns transformation matrix for a given translation, rotation and scale
+// NOTE: Transformation order is rotation -> scale -> translation
+Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale)
+{
+    Matrix result = MatrixIdentity();
+    
+    Matrix mRotation = MatrixRotate(rotation.x, rotation.y, rotation.z);
+    Matrix mScale = MatrixScale(scale.x, scale.y, scale.z);
+    Matrix mTranslate = MatrixTranslate(translation.x, translation.y, translation.z);
+
+    result = MatrixMultiply(MatrixMultiply(mRotation, mScale), mTranslate);
+
+    return result;
+}
+
 // Returns two matrix multiplication
 // NOTE: When multiplying matrices... the order matters!
 Matrix MatrixMultiply(Matrix left, Matrix right)

+ 1 - 0
src/raymath.h

@@ -115,6 +115,7 @@ Matrix MatrixRotateX(float angle);                      // Returns x-rotation ma
 Matrix MatrixRotateY(float angle);                      // Returns y-rotation matrix (angle in radians)
 Matrix MatrixRotateZ(float angle);                      // Returns z-rotation matrix (angle in radians)
 Matrix MatrixScale(float x, float y, float z);          // Returns scaling matrix
+Matrix MatrixTransform(Vector3 translation, Vector3 rotation, Vector3 scale);   // Returns transformation matrix for a given translation, rotation and scale
 Matrix MatrixMultiply(Matrix left, Matrix right);       // Returns two matrix multiplication
 Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far);  // Returns perspective projection matrix
 Matrix MatrixPerspective(double fovy, double aspect, double near, double far);                        // Returns perspective projection matrix

+ 359 - 114
src/rlgl.c

@@ -31,10 +31,24 @@
 #include <stdio.h>          // Standard input / output lib
 #include <stdlib.h>         // Declares malloc() and free() for memory management, rand()
 
-// TODO: Security check in case multiple USE_OPENGL_* defined
+// Security check in case no USE_OPENGL_* defined
+#if !defined(USE_OPENGL_11) && !defined(USE_OPENGL_33) && !defined(USE_OPENGL_ES2)
+    #define USE_OPENGL_11
+#endif
+
+// Security check in case multiple USE_OPENGL_* defined
+#ifdef USE_OPENGL_11
+    #ifdef USE_OPENGL_33
+        #undef USE_OPENGL_33
+    #endif
+    
+    #ifdef USE_OPENGL_ES2
+        #undef USE_OPENGL_ES2
+    #endif   
+#endif
 
 #ifdef USE_OPENGL_11
-    #include <GL/gl.h>      // Extensions loading lib
+    #include <GL/gl.h>      // Basic OpenGL include
 #endif
 
 #ifdef USE_OPENGL_33
@@ -42,7 +56,7 @@
     #include <GL/glew.h>    // Extensions loading lib
 #endif
 
-//#include "glad.h"         // Extensions loading lib? --> REVIEW
+//#include "glad.h"         // Other extensions loading lib? --> REVIEW
 
 #define USE_VBO_DOUBLE_BUFFERS    // Enable VBO double buffers usage --> REVIEW!
 
@@ -56,18 +70,18 @@
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-typedef struct {
-    int numQuads;
-    int texId;
-} QuadsByTexture;
 
+// Vertex buffer (position + color arrays)
+// NOTE: Used for lines and triangles VAOs
 typedef struct {
     int vCounter;
     int cCounter;
     float *vertices;            // 3 components per vertex
     float *colors;              // 4 components per vertex
 } VertexPositionColorBuffer;
-/*
+
+// Vertex buffer (position + texcoords + color arrays)
+// NOTE: Not used
 typedef struct {
     int vCounter;
     int tcCounter;
@@ -76,8 +90,9 @@ typedef struct {
     float *texcoords;           // 2 components per vertex
     float *colors;              // 4 components per vertex
 } VertexPositionColorTextureBuffer;
-*/
-/*
+
+// Vertex buffer (position + texcoords + normals arrays)
+// NOTE: Not used
 typedef struct {
     int vCounter;
     int tcCounter;
@@ -86,7 +101,9 @@ typedef struct {
     float *texcoords;           // 2 components per vertex
     float *normals;             // 3 components per vertex
 } VertexPositionTextureNormalBuffer;
-*/
+
+// Vertex buffer (position + texcoords + colors + indices arrays)
+// NOTE: Used for quads VAO
 typedef struct {
     int vCounter;
     int tcCounter;
@@ -97,12 +114,22 @@ typedef struct {
     unsigned int *indices;      // 6 indices per quad
 } VertexPositionColorTextureIndexBuffer;
 
+// Draw call type
+// NOTE: Used to track required draw-calls, organized by texture
 typedef struct {
-    GLuint texId;
-    int firstVertex;    // Actually, when using glDrawElements, this parameter is useless..
-    int vCount;
+    GLuint textureId;
+    int vertexCount;
 } DrawCall;
 
+// pixel type (same as Color type)
+// NOTE: Used exclusively in mipmap generation functions
+typedef struct {
+    unsigned char r;
+    unsigned char g;
+    unsigned char b;
+    unsigned char a;
+} pixel;
+
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
@@ -140,7 +167,6 @@ static GLuint quadsBuffer[4];
 
 #ifdef USE_VBO_DOUBLE_BUFFERS
 // Double buffering
-// TODO: REVIEW -> Not getting any performance improvement... why?
 static GLuint vaoQuadsB;
 static GLuint quadsBufferB[4];
 static bool useBufferB = false;
@@ -172,6 +198,11 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName);
 static char *TextFileRead(char *fn);
 #endif
 
+#ifdef USE_OPENGL_11
+static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight);
+static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight);
+#endif
+
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Matrix operations
 //----------------------------------------------------------------------------------
@@ -216,7 +247,7 @@ void rlMatrixMode(int mode)
 {
     if (mode == RL_PROJECTION) currentMatrix = &projection;
     else if (mode == RL_MODELVIEW) currentMatrix = &modelview;
-    //else if (mode == GL_TEXTURE) TODO: NEVER USED!
+    //else if (mode == RL_TEXTURE) // Not supported
     
     currentMatrixMode = mode;
 }
@@ -257,6 +288,7 @@ void rlLoadIdentity()
 void rlTranslatef(float x, float y, float z)
 {
     Matrix mat = MatrixTranslate(x, y, z);
+    MatrixTranspose(&mat);
     
     *currentMatrix = MatrixMultiply(*currentMatrix, mat);
 }
@@ -264,13 +296,15 @@ void rlTranslatef(float x, float y, float z)
 // Multiply the current matrix by a rotation matrix
 void rlRotatef(float angleDeg, float x, float y, float z)
 {
-    // TODO: Rotation matrix --> REVIEW!
+    // TODO: Support rotation in multiple axes
     Matrix rot = MatrixIdentity();
     
     if (x == 1) rot = MatrixRotateX(angleDeg*DEG2RAD);
     else if (y == 1) rot = MatrixRotateY(angleDeg*DEG2RAD);
     else if (z == 1) rot = MatrixRotateZ(angleDeg*DEG2RAD);
     
+    MatrixTranspose(&rot);
+    
     *currentMatrix = MatrixMultiply(*currentMatrix, rot);
 }
 
@@ -278,6 +312,7 @@ void rlRotatef(float angleDeg, float x, float y, float z)
 void rlScalef(float x, float y, float z)
 {
     Matrix mat = MatrixScale(x, y, z);
+    MatrixTranspose(&mat);
     
     *currentMatrix = MatrixMultiply(*currentMatrix, mat);
 }
@@ -356,12 +391,12 @@ void rlEnd()
 {
     if (useTempBuffer)
     {
-        // IT WORKS!!! --> Refactor...
-        Matrix mat = *currentMatrix;
-        MatrixTranspose(&mat);
+        // NOTE: In this case, *currentMatrix is already transposed because transposing has been applied
+        // independently to translation-scale-rotation matrices -> t(M1 x M2) = t(M2) x t(M1)
+        // This way, rlTranslatef(), rlRotatef()... behaviour is the same than OpenGL 1.1
 
         // Apply transformation matrix to all temp vertices
-        for (int i = 0; i < tempBufferCount; i++) VectorTransform(&tempBuffer[i], mat);
+        for (int i = 0; i < tempBufferCount; i++) VectorTransform(&tempBuffer[i], *currentMatrix);
         
         // Deactivate tempBuffer usage to allow rlVertex3f do its job
         useTempBuffer = false;
@@ -373,7 +408,7 @@ void rlEnd()
         tempBufferCount = 0;
     }
 
-    // Make sure vertexCounter is the same for vertices-texcoords-normals-colors
+    // Make sure vertexCount is the same for vertices-texcoords-normals-colors
     // NOTE: In OpenGL 1.1, one glColor call can be made for all the subsequent glVertex calls.
     switch (currentDrawMode)
     {
@@ -490,7 +525,7 @@ void rlVertex3f(float x, float y, float z)
                 
                 quads.vCounter++;
                 
-                draws[drawsCounter - 1].vCount++;
+                draws[drawsCounter - 1].vertexCount++;
                 
             } break;
             default: break;
@@ -596,13 +631,12 @@ void rlEnableTexture(unsigned int id)
 #endif
 
 #if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
-    if (draws[drawsCounter - 1].texId != id)
+    if (draws[drawsCounter - 1].textureId != id)
     {
-        if (draws[drawsCounter - 1].vCount > 0) drawsCounter++;
+        if (draws[drawsCounter - 1].vertexCount > 0) drawsCounter++;
         
-        draws[drawsCounter - 1].texId = id;
-        draws[drawsCounter - 1].firstVertex = draws[drawsCounter - 2].vCount;
-        draws[drawsCounter - 1].vCount = 0;
+        draws[drawsCounter - 1].textureId = id;
+        draws[drawsCounter - 1].vertexCount = 0;
     }
 #endif
 }
@@ -708,9 +742,7 @@ void rlglInit()
     projectionMatrixLoc = glGetUniformLocation(shaderProgram, "projectionMatrix");
     
     // Get handles to GLSL uniform vars locations (fragment-shader)
-    textureLoc  = glGetUniformLocation(shaderProgram, "texture0");
-    
-    TraceLog(INFO, "Default shader loaded");
+    textureLoc = glGetUniformLocation(shaderProgram, "texture0");
     
     InitializeBuffers();    // Init vertex arrays
     InitializeVAOs();       // Init VBO and VAO
@@ -723,9 +755,9 @@ 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(pixels, 1, 1, false);
     
-    if (whiteTexture != 0) TraceLog(INFO, "Base white texture successfully created, id: %i", whiteTexture);
+    if (whiteTexture != 0) TraceLog(INFO, "[ID %i] Base white texture created successfully", whiteTexture);
     else TraceLog(WARNING, "Base white texture could not be created");
     
     // Init draw calls tracking system
@@ -733,13 +765,12 @@ void rlglInit()
     
     for (int i = 0; i < MAX_DRAWS_BY_TEXTURE; i++)
     {
-        draws[i].texId = 0;
-        draws[i].firstVertex = 0;
-        draws[i].vCount = 0;
+        draws[i].textureId = 0;
+        draws[i].vertexCount = 0;
     }
     
     drawsCounter = 1;
-    draws[drawsCounter - 1].texId = whiteTexture;
+    draws[drawsCounter - 1].textureId = whiteTexture;
 }
 
 // Vertex Buffer Object deinitialization (memory free)
@@ -789,6 +820,8 @@ void rlglClose()
     
     // Free GPU texture
     glDeleteTextures(1, &whiteTexture);
+    
+    free(draws);
 }
 
 void rlglDraw()
@@ -823,7 +856,7 @@ void rlglDraw()
     
     if (quads.vCounter > 0)
     {
-        int numQuads = 0;
+        int quadsCount = 0;
         int numIndicesToProcess = 0;
         int indicesOffset = 0;
 
@@ -836,21 +869,21 @@ void rlglDraw()
             glBindVertexArray(vaoQuads);
         }
         
-        //TraceLog(INFO, "Draws required per frame: %i", drawsCounter);
+        //TraceLog(DEBUG, "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
+            quadsCount = draws[i].vertexCount/4;
+            numIndicesToProcess = quadsCount*6;  // Get number of Quads * 6 index by Quad
             
-            //TraceLog(INFO, "Quads to render: %i - Vertex Count: %i", numQuads, draws[i].vCount);
+            //TraceLog(DEBUG, "Quads to render: %i - Vertex Count: %i", quadsCount, draws[i].vertexCount);
 
-            glBindTexture(GL_TEXTURE_2D, draws[i].texId);
+            glBindTexture(GL_TEXTURE_2D, draws[i].textureId);
             
             // NOTE: The final parameter tells the GPU the offset in bytes from the start of the index buffer to the location of the first index to process
             glDrawElements(GL_TRIANGLES, numIndicesToProcess, GL_UNSIGNED_INT, (GLvoid*) (sizeof(GLuint) * indicesOffset));
 
-            indicesOffset += draws[i].vCount/4*6;
+            indicesOffset += draws[i].vertexCount/4*6;
         }
     }
     
@@ -859,9 +892,8 @@ void rlglDraw()
     
     // Reset draws counter
     drawsCounter = 1;
-    draws[0].texId = whiteTexture;
-    draws[0].firstVertex = 0;
-    draws[0].vCount = 0;
+    draws[0].textureId = whiteTexture;
+    draws[0].vertexCount = 0;
     
     // Reset vertex counters for next frame
     lines.vCounter = 0;
@@ -883,52 +915,71 @@ void rlglDraw()
 #endif      // End for OpenGL 3.3+ and ES2 only functions
 
 // Draw a 3d model
-void rlglDrawModel(Model model, Vector3 position, float scale, bool wires)
+void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires)
 {
     if (wires) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
     
 #ifdef USE_OPENGL_11
-    // NOTE: For models we use Vertex Arrays (OpenGL 1.1)
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, model.textureId);
+
+    // NOTE: On OpenGL 1.1 we use Vertex Arrays to draw model
     glEnableClientState(GL_VERTEX_ARRAY);                     // Enable vertex array
     glEnableClientState(GL_TEXTURE_COORD_ARRAY);              // Enable texture coords array
     glEnableClientState(GL_NORMAL_ARRAY);                     // Enable 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
+    glVertexPointer(3, GL_FLOAT, 0, model.mesh.vertices);     // Pointer to vertex coords array
+    glTexCoordPointer(2, GL_FLOAT, 0, model.mesh.texcoords);  // Pointer to texture coords array
+    glNormalPointer(GL_FLOAT, 0, model.mesh.normals);         // Pointer to normals array
     //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors);   // Pointer to colors array (NOT USED)
-        
+    
+    //TraceLog(DEBUG, "Drawing model.mesh, VertexCount: %i", model.mesh.vertexCount);
+    
     rlPushMatrix();
         rlTranslatef(position.x, position.y, position.z);
-        //rlRotatef(rotation * GetFrameTime(), 0, 1, 0);
-        rlScalef(scale, scale, scale);
+        rlScalef(scale.x, scale.y, scale.z);
+        //rlRotatef(rotation, 0, 1, 0);
         
-        rlColor4ub(1.0f, 1.0f, 1.0f, 1.0f);
+        // TODO: If rotate in multiple axis, get rotation matrix and use rlMultMatrix()
+
+        rlColor4ub(color.r, color.g, color.b, color.a);
 
-        glDrawArrays(GL_TRIANGLES, 0, model.data.numVertices);
+        glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount);
     rlPopMatrix();
     
     glDisableClientState(GL_VERTEX_ARRAY);                     // Disable vertex array
     glDisableClientState(GL_TEXTURE_COORD_ARRAY);              // Disable texture coords array
     glDisableClientState(GL_NORMAL_ARRAY);                     // Disable normals array
+    
+    glDisable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, 0);
 #endif
 
 #if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
     glUseProgram(shaderProgram);        // Use our shader
     
-    Matrix modelview2 = MatrixMultiply(model.transform, modelview);
+    // Get transform matrix (rotation -> scale -> translation)
+    Matrix transform = MatrixTransform(position, rotation, scale);
+    Matrix modelviewworld = MatrixMultiply(transform, modelview);
     
     // NOTE: Drawing in OpenGL 3.3+, transform is passed to shader
     glUniformMatrix4fv(projectionMatrixLoc, 1, false, GetMatrixVector(projection));
-    glUniformMatrix4fv(modelviewMatrixLoc, 1, false, GetMatrixVector(modelview2));
+    glUniformMatrix4fv(modelviewMatrixLoc, 1, false, GetMatrixVector(modelviewworld));
     glUniform1i(textureLoc, 0);
+    
+    //TraceLog(DEBUG, "ShaderProgram: %i, VAO ID: %i, VertexCount: %i", shaderProgram, model.vaoId, model.mesh.vertexCount);
    
     glBindVertexArray(model.vaoId);
-    //glBindTexture(GL_TEXTURE_2D, model.textureId);
+    
+    // TODO: Update vertex color
+    glBindBuffer(GL_ARRAY_BUFFER, linesBuffer[1]);
+    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*4*model.mesh.vertexCount, model.mesh.colors);
+    
+    glBindTexture(GL_TEXTURE_2D, model.textureId);
 
-    glDrawArrays(GL_TRIANGLES, 0, model.numVertices);
+    glDrawArrays(GL_TRIANGLES, 0, model.mesh.vertexCount);
 
-    //glBindTexture(GL_TEXTURE_2D, 0);    // Unbind textures
+    glBindTexture(GL_TEXTURE_2D, 0);    // Unbind textures
     glBindVertexArray(0);               // Unbind VAO
 #endif
 
@@ -982,8 +1033,7 @@ void rlglInitGraphicsDevice(int fbWidth, int fbHeight)
 }
 
 // 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 *data)
+unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps)
 {
     glBindTexture(GL_TEXTURE_2D,0); // Free any old binding
 
@@ -996,27 +1046,82 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *data)
     // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used!
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);       // Set texture to repead on x-axis
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);       // Set texture to repead on y-axis
-    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
- 
-#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)
-    //glGenerateMipmap(GL_TEXTURE_2D);    // OpenGL 3.3!
-#endif  
 
-    // NOTE: Not using mipmappings (texture for 2D drawing)
-    // At this point we have the image converted to texture and uploaded to GPU
+    bool texIsPOT = false;
     
+    // Check if width and height are power-of-two (POT)
+    if (((width > 0) && ((width & (width - 1)) == 0)) && ((height > 0) && ((height & (height - 1)) == 0))) texIsPOT = true;
+    
+    if (!texIsPOT)
+    {
+        TraceLog(WARNING, "[ID %i] Texture is not power-of-two, mipmaps can not be generated", id);
+        
+        genMipmaps = false;
+    }
+    
+    // If mipmaps are being used, we configure mag-min filters accordingly
+    if (genMipmaps)
+    {
+        // Trilinear filtering with mipmaps
+        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)
+    }
+    else
+    {
+        // Not using mipmappings
+        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_11
+    if (genMipmaps)
+    {
+        TraceLog(WARNING, "[ID %i] Mipmaps generated manually on CPU side", id);
+
+        // Compute required mipmaps
+        // NOTE: data size is reallocated to fit mipmaps data
+        int mipmapCount = GenerateMipmaps(data, width, height);
+
+        int offset = 0;
+        int size = 0;
+        
+        int mipWidth = width;
+        int mipHeight = height;
+        
+        // Load the mipmaps       
+        for (int level = 0; level < mipmapCount; level++)
+        {
+            glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + offset);
+            
+            size = mipWidth*mipHeight*4;
+            offset += size;
+            
+            mipWidth /= 2;
+            mipHeight /= 2;
+        }
+    }
+    else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+#endif
+
+
+#if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
+
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
     
+    if (genMipmaps)
+    {
+        glGenerateMipmap(GL_TEXTURE_2D);  // Generate mipmaps automatically
+        TraceLog(INFO, "[ID %i] Mipmaps generated automatically for new texture", id);
+    }
+    
+#endif
+
     // At this point we have the image converted to texture and uploaded to GPU
     
     // Unbind current texture
     glBindTexture(GL_TEXTURE_2D, 0);
     
-    TraceLog(INFO, "New texture created, id: %i (%i x %i)", id, width, height);
+    TraceLog(INFO, "[ID %i] New texture created (%i x %i)", id, width, height);
     
     return id;
 }
@@ -1024,51 +1129,42 @@ unsigned int rlglLoadTexture(int width, int height, unsigned char *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)
+// NOTE: Expected compressed image data and POT image
+unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int compFormat)
 {
     // Create one OpenGL texture
     GLuint id;
-    int compFormat = 0;
+    
+    glGenTextures(1, &id);
     
     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(DEBUG, "Compressed texture format: 0x%x", compFormat);
+        
+    if (compFormat == 0)
     {
-        TraceLog(WARNING, "Texture compressed format not recognized");
+        TraceLog(WARNING, "[ID %i] Texture compressed format not recognized", id);
         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;
+        int blockSize = 0;
+        int offset = 0;
+        
+        if (compFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) blockSize = 8;
+        else blockSize = 16;
         
         // Load the mipmaps 
         for (int level = 0; level < mipmapCount && (width || height); level++) 
         { 
-            unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
+            // NOTE: size specifies the number of bytes of image data (S3TC/DXTC)
+            unsigned int size = ((width + 3)/4)*((height + 3)/4)*blockSize;
             
             glCompressedTexImage2D(GL_TEXTURE_2D, level, compFormat, width, height, 0, size, data + offset); 
          
@@ -1100,20 +1196,28 @@ unsigned int rlglLoadModel(VertexData mesh)
  
     // Enable vertex attributes
     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.numVertices, mesh.vertices, GL_STATIC_DRAW);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, 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*mesh.numVertices, mesh.texcoords, GL_STATIC_DRAW);      
+    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*mesh.vertexCount, 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*mesh.numVertices, mesh.normals, GL_STATIC_DRAW);   
+    //glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
+    //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*mesh.vertexCount, mesh.normals, GL_STATIC_DRAW);   
     //glEnableVertexAttribArray(normalLoc);
     //glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0);
     
+    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*mesh.vertexCount, mesh.colors, GL_STATIC_DRAW);   
+    glEnableVertexAttribArray(colorLoc);
+    glVertexAttribPointer(colorLoc, 4, GL_FLOAT, 0, 0, 0);
+    
+    if (vaoModel > 0) TraceLog(INFO, "[ID %i] Model uploaded successfully to VRAM (GPU)", vaoModel);
+    else TraceLog(WARNING, "Model could not be uploaded to VRAM (GPU)");
+    
     return vaoModel;
 }
 #endif
@@ -1208,6 +1312,9 @@ static GLuint LoadDefaultShaders()
 
     glCompileShader(vertexShader);
     glCompileShader(fragmentShader);
+    
+    TraceLog(INFO, "[ID %i] Default vertex shader compiled succesfully", vertexShader);
+    TraceLog(INFO, "[ID %i] Default fragment shader compiled succesfully", fragmentShader);
  
     program = glCreateProgram();
     
@@ -1218,6 +1325,8 @@ static GLuint LoadDefaultShaders()
  
     glDeleteShader(vertexShader);
     glDeleteShader(fragmentShader);
+    
+    TraceLog(INFO, "[ID %i] Default shader program loaded succesfully", program);
  
     return program;
 }
@@ -1245,6 +1354,9 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName)
 
     glCompileShader(vertexShader);
     glCompileShader(fragmentShader);
+    
+    TraceLog(INFO, "[ID %i] Vertex shader compiled succesfully", vertexShader);
+    TraceLog(INFO, "[ID %i] Fragment shader compiled succesfully", fragmentShader);
  
     program = glCreateProgram();
     
@@ -1255,6 +1367,8 @@ static GLuint LoadShaders(char *vertexFileName, char *fragmentFileName)
  
     glDeleteShader(vertexShader);
     glDeleteShader(fragmentShader);
+    
+    TraceLog(INFO, "[ID %i] Shader program loaded succesfully", program);
  
     return program;
 }
@@ -1364,7 +1478,8 @@ static void InitializeVAOs()
     glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*2*MAX_LINES_BATCH, lines.colors, GL_DYNAMIC_DRAW);
     glEnableVertexAttribArray(colorLoc);
     glVertexAttribPointer(colorLoc, 4, GL_FLOAT, 0, 0, 0);
-
+    
+    TraceLog(INFO, "[ID %i] Lines VAO successfully initialized", vaoLines);
     //-------------------------------------------------------------- 
     
     // Initialize Triangles VAO
@@ -1384,7 +1499,8 @@ static void InitializeVAOs()
     glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*3*MAX_TRIANGLES_BATCH, triangles.colors, GL_DYNAMIC_DRAW);
     glEnableVertexAttribArray(colorLoc);
     glVertexAttribPointer(colorLoc, 4, GL_FLOAT, 0, 0, 0);
-
+    
+    TraceLog(INFO, "[ID %i] Triangles VAO successfully initialized", vaoTriangles);
     //-------------------------------------------------------------- 
     
     // Initialize Quads VAO (Buffer A)
@@ -1414,6 +1530,8 @@ static void InitializeVAOs()
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadsBuffer[3]);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*6*MAX_QUADS_BATCH, quads.indices, GL_STATIC_DRAW);
     
+    TraceLog(INFO, "[ID %i] Quads VAO successfully initialized", vaoQuads);
+    
 #ifdef USE_VBO_DOUBLE_BUFFERS
     // Initialize Quads VAO (Buffer B)
     glGenVertexArrays(1, &vaoQuadsB);
@@ -1442,11 +1560,9 @@ 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);
     
-    TraceLog(INFO, "Using VBO double buffering");
+    TraceLog(INFO, "[ID %i] Second Quads VAO successfully initilized (double buffering)", vaoQuadsB);
 #endif
  
-    TraceLog(INFO, "Vertex buffers successfully initialized (lines, triangles, quads)\n");
- 
     // Unbind the current VAO
     glBindVertexArray(0);
 }
@@ -1540,6 +1656,135 @@ static void UpdateBuffers()
     glBindVertexArray(0);
 }
 
+#endif //defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)
+
+#ifdef USE_OPENGL_11
+
+// Mipmaps data is generated after image data
+static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight)
+{
+    int mipmapCount = 1;                // Required mipmap levels count (including base level)
+    int width = baseWidth;
+    int height = baseHeight;
+    int size = baseWidth*baseHeight*4;  // Size in bytes (will include mipmaps...)
+
+    // Count mipmap levels required
+    while ((width != 1) && (height != 1))
+    {
+        if (width != 1) width /= 2;
+        if (height != 1) height /= 2;
+        
+        TraceLog(DEBUG, "Next mipmap size: %i x %i", width, height);
+        
+        mipmapCount++;
+        
+        size += (width*height*4);       // Add mipmap size (in bytes)
+    }
+    
+    TraceLog(DEBUG, "Total mipmaps required: %i", mipmapCount);
+    TraceLog(DEBUG, "Total size of data required: %i", size);
+    
+    unsigned char *temp = realloc(data, size);
+    
+    if (temp != NULL) data = temp;
+    else TraceLog(WARNING, "Mipmaps required memory could not be allocated");
+    
+    width = baseWidth;
+    height = baseHeight;
+    size = (width*height*4);
+    
+    // Generate mipmaps
+    // NOTE: Every mipmap data is stored after data
+    pixel *image = (pixel *)malloc(width*height*sizeof(pixel));
+    pixel *mipmap = NULL;
+    int offset = 0;
+    int j = 0;
+
+    for (int i = 0; i < size; i += 4)
+    {
+        image[j].r = data[i];
+        image[j].g = data[i + 1];
+        image[j].b = data[i + 2];
+        image[j].a = data[i + 3];
+        j++;
+    }
+    
+    TraceLog(DEBUG, "Mipmap base (%i, %i)", width, height);
+    
+    for (int mip = 1; mip < mipmapCount; mip++)
+    {
+        mipmap = GenNextMipmap(image, width, height);
+        
+        offset += (width*height*4); // Size of last mipmap
+        j = 0;
+        
+        width /= 2;
+        height /= 2;
+        size = (width*height*4);    // Mipmap size to store after offset
+
+        // Add mipmap to data
+        for (int i = 0; i < size; i += 4)
+        {
+            data[offset + i] = mipmap[j].r;
+            data[offset + i + 1] = mipmap[j].g;
+            data[offset + i + 2] = mipmap[j].b;
+            data[offset + i + 3] = mipmap[j].a; 
+            j++;
+        }
+
+        free(image);
+        
+        image = mipmap;
+        mipmap = NULL;
+    }
+    
+    free(mipmap);       // free mipmap data
+    
+    return mipmapCount;
+}
+
+// Manual mipmap generation (basic scaling algorithm)
+static pixel *GenNextMipmap(pixel *srcData, int srcWidth, int srcHeight)
+{
+    int x2, y2;
+    pixel prow, pcol;
+
+    int width = srcWidth / 2;
+    int height = srcHeight / 2;
+
+    pixel *mipmap = (pixel *)malloc(width*height*sizeof(pixel));
+
+    // Scaling algorithm works perfectly (box-filter)
+    for (int y = 0; y < height; y++) 
+    {
+        y2 = 2 * y;
+
+        for (int x = 0; x < width; x++) 
+        {
+            x2 = 2 * x;
+
+            prow.r = (srcData[y2*srcWidth + x2].r + srcData[y2*srcWidth + x2 + 1].r)/2;
+            prow.g = (srcData[y2*srcWidth + x2].g + srcData[y2*srcWidth + x2 + 1].g)/2;
+            prow.b = (srcData[y2*srcWidth + x2].b + srcData[y2*srcWidth + x2 + 1].b)/2;
+            prow.a = (srcData[y2*srcWidth + x2].a + srcData[y2*srcWidth + x2 + 1].a)/2;
+
+            pcol.r = (srcData[(y2+1)*srcWidth + x2].r + srcData[(y2+1)*srcWidth + x2 + 1].r)/2;
+            pcol.g = (srcData[(y2+1)*srcWidth + x2].g + srcData[(y2+1)*srcWidth + x2 + 1].g)/2;
+            pcol.b = (srcData[(y2+1)*srcWidth + x2].b + srcData[(y2+1)*srcWidth + x2 + 1].b)/2;
+            pcol.a = (srcData[(y2+1)*srcWidth + x2].a + srcData[(y2+1)*srcWidth + x2 + 1].a)/2;
+
+            mipmap[y*width + x].r = (prow.r + pcol.r)/2;
+            mipmap[y*width + x].g = (prow.g + pcol.g)/2;
+            mipmap[y*width + x].b = (prow.b + pcol.b)/2;
+            mipmap[y*width + x].a = (prow.a + pcol.a)/2;
+        }
+    }
+    
+    TraceLog(DEBUG, "Mipmap generated successfully (%i, %i)", width, height);
+
+    return mipmap;
+}
+
 #endif
 
 #ifdef RLGL_STANDALONE
@@ -1555,10 +1800,10 @@ void TraceLog(int msgType, const char *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;
+        case INFO: fprintf(stdout, "INFO: "); break;
+        case ERROR: fprintf(stdout, "ERROR: "); break;
+        case WARNING: fprintf(stdout, "WARNING: "); break;
+        case DEBUG: fprintf(stdout, "DEBUG: "); break;
         default: break;
     }
     
@@ -1567,6 +1812,6 @@ void TraceLog(int msgType, const char *text, ...)
     
     va_end(args);
     
-    if (msgType == 1) exit(1);
+    if (msgType == ERROR) exit(1);
 }
 #endif

+ 26 - 31
src/rlgl.h

@@ -36,19 +36,19 @@
     #include "utils.h"          // Required for function TraceLog()
 #endif
 
-#include "raymath.h"        // Required for data type Matrix and Matrix functions
+#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         8192    // 1024
-#define MAX_TRIANGLES_BATCH     2048
-#define MAX_QUADS_BATCH         8192
+#define MAX_LINES_BATCH         8192    // NOTE: Be careful with limits!
+#define MAX_TRIANGLES_BATCH     4096    // NOTE: Be careful with limits!
+#define MAX_QUADS_BATCH         8192    // NOTE: Be careful with limits!
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
@@ -60,26 +60,20 @@ 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;
-#endif
-
-typedef struct {
-    int numVertices;
-    float *vertices;            // 3 components per vertex
-    float *texcoords;           // 2 components per vertex
-    float *normals;             // 3 components per vertex
-} VertexData;
-
-#ifdef USE_OPENGL_11
-struct Model {
-    VertexData data;
-};
-#else
-struct Model {
-    unsigned int vaoId;
-    Matrix transform;
-    int numVertices;
-};
+    typedef struct {
+        int vertexCount;
+        float *vertices;            // 3 components per vertex
+        float *texcoords;           // 2 components per vertex
+        float *normals;             // 3 components per vertex
+        float *colors;
+    } VertexData;
+
+    typedef struct Model {
+        VertexData mesh;
+        unsigned int vaoId;
+        unsigned int textureId;
+        //Matrix transform;
+    } Model;
 #endif
 
 #ifdef __cplusplus
@@ -90,8 +84,8 @@ extern "C" {            // Prevents name mangling of functions
 // Functions Declaration - Matrix operations
 //------------------------------------------------------------------------------------
 void rlMatrixMode(int mode);                    // Choose the current matrix to be transformed
-void rlPushMatrix();                            // TODO: REVIEW: Required? - Push the current matrix to stack
-void rlPopMatrix();                             // TODO: REVIEW: Required? - Pop lattest inserted matrix from stack
+void rlPushMatrix();                            // Push the current matrix to stack
+void rlPopMatrix();                             // Pop lattest inserted matrix from stack
 void rlLoadIdentity();                          // Reset current matrix to identity matrix
 void rlTranslatef(float x, float y, float z);   // Multiply the current matrix by a translation matrix
 void rlRotatef(float angleDeg, float x, float y, float z);  // Multiply the current matrix by a rotation matrix
@@ -132,13 +126,14 @@ void rlClearScreenBuffers();                // Clear used screen buffers (color
 void rlglInit();                                // Initialize rlgl (shaders, VAO, VBO...)
 void rlglClose();                               // De-init rlgl
 void rlglDraw();                                // Draw VAOs
-unsigned int rlglLoadModel(VertexData data);
+unsigned int rlglLoadModel(VertexData mesh);
 unsigned int rlglLoadCompressedTexture(unsigned char *data, int width, int height, int mipmapCount, int format);
 #endif
 
-void rlglDrawModel(Model model, Vector3 position, float scale, bool wires);   // Draw model
+void rlglDrawModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale, Color color, bool wires);
+
 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
+unsigned int rlglLoadTexture(unsigned char *data, int width, int height, bool genMipmaps); // Load in GPU OpenGL texture
 byte *rlglReadScreenPixels(int width, int height);    // Read screen pixel data (color buffer)
 
 #if defined(USE_OPENGL_33) || defined(USE_OPENGL_ES2)

+ 18 - 21
src/shapes.c

@@ -112,6 +112,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
             rlVertex2i(centerX, centerY);
             rlColor4ub(color2.r, color2.g, color2.b, color2.a);
             rlVertex2f(centerX + sin(DEG2RAD*i) * radius, centerY + cos(DEG2RAD*i) * radius);
+            rlColor4ub(color2.r, color2.g, color2.b, color2.a);
             rlVertex2f(centerX + sin(DEG2RAD*(i+2)) * radius, centerY + cos(DEG2RAD*(i+2)) * radius);
         }
     rlEnd();
@@ -149,17 +150,10 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color)
 // Draw a color-filled rectangle
 void DrawRectangle(int posX, int posY, int width, int height, Color color)
 {
-    rlBegin(RL_QUADS);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-        rlTexCoord2f(0.0f, 0.0f);
-        rlVertex2i(posX, posY);
-        rlTexCoord2f(0.0f, 1.0f); 
-        rlVertex2i(posX, posY + height);
-        rlTexCoord2f(1.0f, 1.0f); 
-        rlVertex2i(posX + width, posY + height);
-        rlTexCoord2f(1.0f, 0.0f);
-        rlVertex2i(posX + width, posY);
-    rlEnd();
+    Vector2 position = { (float)posX, (float)posY };
+    Vector2 size = { (float)width, (float)height };
+
+    DrawRectangleV(position, size, color);
 }
 
 // Draw a color-filled rectangle
@@ -172,26 +166,29 @@ void DrawRectangleRec(Rectangle rec, Color color)
 // NOTE: Gradient goes from bottom (color1) to top (color2)
 void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2)
 {
-    rlBegin(RL_QUADS);
-        rlColor4ub(color1.r, color1.g, color1.b, color1.a);
-        rlVertex2i(posX, posY);
-        rlColor4ub(color1.r, color1.g, color1.b, color1.a);
-        rlVertex2i(posX, posY + height);
-        rlColor4ub(color2.r, color2.g, color2.b, color2.a);
-        rlVertex2i(posX + width, posY + height);
-        rlColor4ub(color2.r, color2.g, color2.b, color2.a);
-        rlVertex2i(posX + width, posY);
+    rlBegin(RL_TRIANGLES);
+        rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(posX, posY);
+        rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2i(posX, posY + height);
+        rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2i(posX + width, posY + height);
+        
+        rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(posX, posY);
+        rlColor4ub(color2.r, color2.g, color2.b, color2.a); rlVertex2i(posX + width, posY + height);        
+        rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2i(posX + width, posY);
     rlEnd();
 }
 
 // Draw a color-filled rectangle (Vector version)
 void DrawRectangleV(Vector2 position, Vector2 size, Color color)
 {
-    rlBegin(RL_QUADS);
+    rlBegin(RL_TRIANGLES);
         rlColor4ub(color.r, color.g, color.b, color.a);
+        
         rlVertex2i(position.x, position.y);
         rlVertex2i(position.x, position.y + size.y);
         rlVertex2i(position.x + size.x, position.y + size.y);
+        
+        rlVertex2i(position.x, position.y);
+        rlVertex2i(position.x + size.x, position.y + size.y);        
         rlVertex2i(position.x + size.x, position.y);
     rlEnd();
 }

+ 17 - 26
src/text.c

@@ -35,6 +35,8 @@
 
 #include "rlgl.h"         // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
 
+#include "utils.h"        // Required for function GetExtendion()
+
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
@@ -58,15 +60,6 @@ typedef struct Character {
     int h;
 } Character;
 
-// SpriteFont type, includes texture and charSet array data
-/*
-struct SpriteFont {
-    Texture2D texture;
-    int numChars;
-    Character *charSet;
-};
-*/
-
 //----------------------------------------------------------------------------------
 // Global variables
 //----------------------------------------------------------------------------------
@@ -85,7 +78,6 @@ static bool PixelIsMagenta(Color p);                // Check if a pixel is magen
 static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet);    // Parse image pixel data to obtain character set measures
 static int GetNextPOT(int num);                     // Calculate next power-of-two value for a given value
 static SpriteFont LoadRBMF(const char *fileName);   // Load a rBMF font file (raylib BitMap Font)
-static const char *GetExtension(const char *fileName);
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition
@@ -153,8 +145,7 @@ extern void LoadDefaultFont()
         if (counter > 256) counter = 0;         // Security check...
     }
     
-    defaultFont.texture = CreateTexture(image); // Convert loaded image to OpenGL texture
-
+    defaultFont.texture = CreateTexture(image, false); // Convert loaded image to OpenGL texture
     UnloadImage(image);
     
     // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars
@@ -192,7 +183,7 @@ extern void LoadDefaultFont()
 
 extern void UnloadDefaultFont()
 {
-    rlDeleteTextures(defaultFont.texture.glId);
+    rlDeleteTextures(defaultFont.texture.id);
     free(defaultFont.charSet);
 }
 
@@ -277,8 +268,7 @@ SpriteFont LoadSpriteFont(const char* fileName)
         image.width = potWidth;
         image.height = potHeight;
         
-        spriteFont.texture = CreateTexture(image); // Convert loaded image to OpenGL texture
-
+        spriteFont.texture = CreateTexture(image, false); // Convert loaded image to OpenGL texture
         UnloadImage(image);
     }
     
@@ -288,7 +278,7 @@ SpriteFont LoadSpriteFont(const char* fileName)
 // Unload SpriteFont from GPU memory
 void UnloadSpriteFont(SpriteFont spriteFont)
 {
-    rlDeleteTextures(spriteFont.texture.glId);
+    rlDeleteTextures(spriteFont.texture.id);
     free(spriteFont.charSet);
 }
 
@@ -322,7 +312,7 @@ void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, int f
     if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f;
     else scaleFactor = (float)fontSize / spriteFont.charSet[0].h;
 
-    rlEnableTexture(spriteFont.texture.glId);
+    rlEnableTexture(spriteFont.texture.id);
 
     rlBegin(RL_QUADS);
         for(int i = 0; i < length; i++)
@@ -513,11 +503,11 @@ static int GetNextPOT(int num)
     if (num != 0)
     {
         num--;
-        num |= (num >> 1);    // Or first 2 bits
+        num |= (num >> 1);     // Or first 2 bits
         num |= (num >> 2);     // Or next 2 bits
         num |= (num >> 4);     // Or next 4 bits
         num |= (num >> 8);     // Or next 8 bits
-        num |= (num >> 16); // Or next 16 bits
+        num |= (num >> 16);    // Or next 16 bits
         num++;
     }
 
@@ -596,8 +586,7 @@ static SpriteFont LoadRBMF(const char *fileName)
     
     TraceLog(INFO, "[%s] Image reconstructed correctly, now converting it to texture", fileName);
     
-    spriteFont.texture = CreateTexture(image);
-    
+    spriteFont.texture = CreateTexture(image, false);
     UnloadImage(image);     // Unload image data
     
     TraceLog(INFO, "[%s] Starting charSet reconstruction", fileName);
@@ -641,10 +630,12 @@ static SpriteFont LoadRBMF(const char *fileName)
     return spriteFont;
 }
 
-// Get the extension for a filename
-static const char *GetExtension(const char *fileName) 
+// Generate a sprite font from TTF data (font size required)
+static SpriteFont GenerateFromTTF(const char *fileName, int fontSize)
 {
-    const char *dot = strrchr(fileName, '.');
-    if(!dot || dot == fileName) return "";
-    return dot + 1;
+    SpriteFont font;
+    
+    // TODO: Load TTF and generate bitmap font and chars data
+    
+    return font;
 }

+ 204 - 137
src/textures.c

@@ -46,12 +46,14 @@
 typedef unsigned char byte;
 
 typedef struct {
-    unsigned char *data;
-    int width;
-    int height;
-    int mipmaps;
-    int format;
-} ImageDDS;
+    unsigned char *data;    // Image raw data
+    int width;              // Image base width
+    int height;             // Image base height
+    //int bpp;              // bytes per pixel
+    //int components;       // num color components
+    int mipmaps;            // Mipmap levels, 1 by default
+    int compFormat;         // Compressed data format, 0 if no compression
+} ImageEx;
 
 //----------------------------------------------------------------------------------
 // Global Variables Definition
@@ -66,8 +68,7 @@ typedef struct {
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 //----------------------------------------------------------------------------------
-static const char *GetExtension(const char *fileName);
-static ImageDDS LoadDDS(const char *fileName);
+static ImageEx LoadDDS(const char *fileName);
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition
@@ -78,6 +79,11 @@ Image LoadImage(const char *fileName)
 {
     Image image;
     
+    // Initial values
+    image.pixels = NULL;
+    image.width = 0;
+    image.height = 0;
+    
     if ((strcmp(GetExtension(fileName),"png") == 0) ||
         (strcmp(GetExtension(fileName),"bmp") == 0) ||
         (strcmp(GetExtension(fileName),"tga") == 0) ||
@@ -115,6 +121,35 @@ Image LoadImage(const char *fileName)
         
         TraceLog(INFO, "[%s] Image loaded successfully", fileName);
     }
+    else if (strcmp(GetExtension(fileName),"dds") == 0)
+    {
+        // NOTE: DDS uncompressed images can also be loaded (discarding mipmaps...)
+        
+        ImageEx imageDDS = LoadDDS(fileName);
+        
+        if (imageDDS.compFormat == 0)
+        {
+            image.pixels = (Color *)malloc(imageDDS.width * imageDDS.height * sizeof(Color));
+            image.width = imageDDS.width;
+            image.height = imageDDS.height;
+            
+            int pix = 0;
+        
+            for (int i = 0; i < (image.width * image.height * 4); i += 4)
+            {
+                image.pixels[pix].r = imageDDS.data[i];
+                image.pixels[pix].g = imageDDS.data[i+1];
+                image.pixels[pix].b = imageDDS.data[i+2];
+                image.pixels[pix].a = imageDDS.data[i+3];
+                pix++;
+            }
+            
+            free(imageDDS.data);
+            
+            TraceLog(INFO, "[%s] Image loaded successfully", fileName);
+        }
+        else TraceLog(WARNING, "[%s] Compressed image data could not be loaded", fileName);    
+    }
     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, 
@@ -127,7 +162,8 @@ Image LoadImage(const char *fileName)
 // Load an image from rRES file (raylib Resource)
 Image LoadImageFromRES(const char *rresName, int resId)
 {
-    // NOTE: rresName could be directly a char array with all the data!!! ---> TODO!
+    // TODO: rresName could be directly a char array with all the data! --> support it! :P
+    
     Image image;
     bool found = false;
 
@@ -172,8 +208,8 @@ Image LoadImageFromRES(const char *rresName, int resId)
                     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;
                     
@@ -220,11 +256,11 @@ Image LoadImageFromRES(const char *rresName, int resId)
                     // 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
+                        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;
                     }
                     
@@ -249,19 +285,26 @@ Texture2D LoadTexture(const char *fileName)
 
     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);
+        ImageEx image = LoadDDS(fileName);
+        
+        if (image.compFormat == 0)
+        {
+            texture.id = rlglLoadTexture(image.data, image.width, image.height, false);
+        }
+        else
+        {
+#ifdef USE_OPENGL_33
+            texture.id = rlglLoadCompressedTexture(image.data, image.width, image.height, image.mipmaps, image.compFormat);
+#endif
+        }
         
-        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
+        if (texture.id == 0) TraceLog(WARNING, "[%s] DDS texture could not be loaded", fileName);
+        else TraceLog(INFO, "[%s] DDS texture loaded succesfully", fileName);
+        
+        free(image.data);
     }
     else
     {
@@ -269,7 +312,7 @@ Texture2D LoadTexture(const char *fileName)
         
         if (image.pixels != NULL)
         {
-            texture = CreateTexture(image);
+            texture = CreateTexture(image, false);
             UnloadImage(image);
         }
     }
@@ -283,7 +326,8 @@ Texture2D LoadTextureFromRES(const char *rresName, int resId)
     Texture2D texture;
 
     Image image = LoadImageFromRES(rresName, resId);
-    texture = CreateTexture(image);
+    texture = CreateTexture(image, false);
+    UnloadImage(image);
     
     return texture;
 }
@@ -297,7 +341,7 @@ void UnloadImage(Image image)
 // Unload texture from GPU memory
 void UnloadTexture(Texture2D texture)
 {
-    rlDeleteTextures(texture.glId);
+    rlDeleteTextures(texture.id);
 }
 
 // Draw a Texture2D
@@ -315,76 +359,32 @@ void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
 // Draw a Texture2D with extended parameters
 void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
 {
-    rlEnableTexture(texture.glId);
+    Rectangle sourceRec = { 0, 0, texture.width, texture.height };
+    Rectangle destRec = { (int)position.x, (int)position.y, texture.width*scale, texture.height*scale };
+    Vector2 origin = { 0, 0 };
     
-    // 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);
-        rlRotatef(rotation, 0, 0, 1);
-        rlScalef(scale, scale, 1.0f);
-    
-        rlBegin(RL_QUADS);
-            rlColor4ub(tint.r, tint.g, tint.b, tint.a);
-            rlNormal3f(0.0f, 0.0f, 1.0f);                               // Normal vector pointing towards viewer
-            
-            rlTexCoord2f(0.0f, 0.0f);
-            rlVertex2f(position.x, position.y);                         // Bottom-left corner for texture and quad
-            
-            rlTexCoord2f(0.0f, 1.0f); 
-            rlVertex2f(position.x, position.y + texture.height);        // Bottom-right corner for texture and quad
-            
-            rlTexCoord2f(1.0f, 1.0f); 
-            rlVertex2f(position.x + texture.width, position.y + texture.height);  // Top-right corner for texture and quad
-            
-            rlTexCoord2f(1.0f, 0.0f); 
-            rlVertex2f(position.x + texture.width, position.y);         // Top-left corner for texture and quad
-        rlEnd();
-    rlPopMatrix();
-    
-    rlDisableTexture();
+    DrawTexturePro(texture, sourceRec, destRec, origin, rotation, tint);
 }
 
 // Draw a part of a texture (defined by a rectangle)
 void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint)
 {
-    rlEnableTexture(texture.glId);
-    
-    rlBegin(RL_QUADS);
-        rlColor4ub(tint.r, tint.g, tint.b, tint.a);
-        rlNormal3f(0.0f, 0.0f, 1.0f);                          // Normal vector pointing towards viewer
-        
-        // Bottom-left corner for texture and quad
-        rlTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height); 
-        rlVertex2f(position.x, position.y);
-        
-        // Bottom-right corner for texture and quad
-        rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
-        rlVertex2f(position.x, position.y + sourceRec.height);
-        
-        // Top-right corner for texture and quad
-        rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); 
-        rlVertex2f(position.x + sourceRec.width, position.y + sourceRec.height);
-        
-        // Top-left corner for texture and quad 
-        rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
-        rlVertex2f(position.x + sourceRec.width, position.y);
-    rlEnd();
+    Rectangle destRec = { (int)position.x, (int)position.y, sourceRec.width, sourceRec.height };
+    Vector2 origin = { 0, 0 };
     
-    rlDisableTexture();
+    DrawTexturePro(texture, sourceRec, destRec, origin, 0, tint);
 }
 
 // Draw a part of a texture (defined by a rectangle) with 'pro' parameters
-// TODO: Test this function...
+// NOTE: origin is relative to destination rectangle size
 void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint)
 {
-    rlEnableTexture(texture.glId);
+    rlEnableTexture(texture.id);
     
-    // NOTE: First we translate texture to origin to apply rotation and translation from there
     rlPushMatrix();
-        rlTranslatef(-origin.x, -origin.y, 0);  
+        rlTranslatef(destRec.x, destRec.y, 0);
         rlRotatef(rotation, 0, 0, 1);
-        rlTranslatef(destRec.x + origin.x, destRec.y + origin.y, 0);
+        rlTranslatef(-origin.x, -origin.y, 0);
             
         rlBegin(RL_QUADS);
             rlColor4ub(tint.r, tint.g, tint.b, tint.a);
@@ -395,65 +395,84 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
             rlVertex2f(0.0f, 0.0f);
             
             // Bottom-right corner for texture and quad
-            rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
-            rlVertex2f(destRec.width, 0.0f);
+            rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
+            rlVertex2f(0.0f, destRec.height);
             
             // Top-right corner for texture and quad
             rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); 
             rlVertex2f(destRec.width, destRec.height);
             
             // Top-left corner for texture and quad
-            rlTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height);
-            rlVertex2f(0.0f, destRec.height);
+            rlTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height);
+            rlVertex2f(destRec.width, 0.0f);
         rlEnd();
     rlPopMatrix();
     
     rlDisableTexture();
 }
 
-Texture2D CreateTexture(Image image)
+// Create a texture from an image
+// NOTE: image is not unloaded, iot must be done manually
+Texture2D CreateTexture(Image image, bool genMipmaps)
 {
     Texture2D texture;
     
-    unsigned char *img = malloc(image.width * image.height * 4);
-    
-    int j = 0;
+    // Init texture to default values
+    texture.id = 0;
+    texture.width = 0;
+    texture.height = 0;
     
-    for (int i = 0; i < image.width * image.height * 4; i += 4)
+    if (image.pixels != NULL)
     {
-        img[i] = image.pixels[j].r;
-        img[i+1] = image.pixels[j].g;
-        img[i+2] = image.pixels[j].b;
-        img[i+3] = image.pixels[j].a;
+        unsigned char *imgData = malloc(image.width * image.height * 4);
         
-        j++;
-    }
+        int j = 0;
+        
+        for (int i = 0; i < image.width * image.height * 4; i += 4)
+        {
+            imgData[i] = image.pixels[j].r;
+            imgData[i+1] = image.pixels[j].g;
+            imgData[i+2] = image.pixels[j].b;
+            imgData[i+3] = image.pixels[j].a;
+            
+            j++;
+        }
 
-    texture.glId = rlglLoadTexture(image.width, image.height, img);
+        // NOTE: rlglLoadTexture() can generate mipmaps (POT image required)
+        texture.id = rlglLoadTexture(imgData, image.width, image.height, genMipmaps);
 
-    texture.width = image.width;
-    texture.height = image.height;
-    
-    TraceLog(INFO, "Texture created succesfully");
-    
-    free(img);
+        texture.width = image.width;
+        texture.height = image.height;
+        
+        TraceLog(INFO, "[ID %i] Texture created succesfully", texture.id);
+        
+        free(imgData);
+    }
+    else TraceLog(WARNING, "Texture could not be created, image data is not valid");
     
     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)
+// Loading DDS image data (compressed or uncompressed)
+// NOTE: Compressed data loading not supported on OpenGL 1.1
+ImageEx LoadDDS(const char *fileName)
 {   
-    // TODO: Review and expand DDS file loading to support uncompressed formats and new formats
+    #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
+    
+    #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
+    #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+    #endif
 
+    #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
+    #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+    #endif
+
+    #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
+    #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+    #endif
+    
     // DDS Pixel Format
     typedef struct {
         unsigned int size;
@@ -484,7 +503,7 @@ ImageDDS LoadDDS(const char *fileName)
         unsigned int reserved2;
     } ddsHeader;
     
-    ImageDDS image;
+    ImageEx image;
     ddsHeader header;
 
     FILE *ddsFile = fopen(fileName, "rb");
@@ -510,36 +529,84 @@ ImageDDS LoadDDS(const char *fileName)
             // 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);
+            TraceLog(DEBUG, "[%s] DDS file format: 0x%x", fileName, header.ddspf.fourCC);
             
-            int bufsize;
+            image.width = header.width;
+            image.height = header.height;
+            image.mipmaps = 1;
+            image.compFormat = 0;
             
-            // Calculate data size, including all mipmaps 
-            bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize; 
+            if (header.ddspf.flags == 0x40 && header.ddspf.rgbBitCount == 24)   // DDS_RGB, no compressed
+            {
+                image.data = (unsigned char *)malloc(header.width * header.height * 4);
+                unsigned char *buffer = (unsigned char *)malloc(header.width * header.height * 3);
             
-            image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); 
+                fread(buffer, image.width*image.height*3, 1, ddsFile);
+                
+                unsigned char *src = buffer;
+                unsigned char *dest = image.data;
+                
+                for(int y = 0; y < image.height; y++) 
+                {
+                    for(int x = 0; x < image.width; x++) 
+                    {
+                        *dest++ = *src++;
+                        *dest++ = *src++;
+                        *dest++ = *src++;
+                        *dest++ = 255;
+                    }
+                }
+                
+                free(buffer);
+            }
+            else if (header.ddspf.flags == 0x41 && header.ddspf.rgbBitCount == 32) // DDS_RGBA, no compressed
+            {
+                image.data = (unsigned char *)malloc(header.width * header.height * 4);
             
-            fread(image.data, 1, bufsize, ddsFile); 
+                fread(image.data, image.width*image.height*4, 1, ddsFile);
             
-            // Close file pointer
-            fclose(ddsFile);
-
-            //int components = (fourCC == FOURCC_DXT1) ? 3 : 4; // Not required
+                image.mipmaps = 1;
+                image.compFormat = 0;
+            }
+            else if ((header.ddspf.flags == 0x04) && (header.ddspf.fourCC > 0))
+            {
+#ifdef USE_OPENGL_11 
+                TraceLog(WARNING, "[%s] DDS image uses compression, not supported by current OpenGL version", fileName);
+                TraceLog(WARNING, "[%s] DDS compressed files require OpenGL 3.2+ or ES 2.0", fileName);
+                fclose(ddsFile);
+#else
+                int bufsize;
+                
+                // Calculate data size, including all mipmaps
+                if (header.mipMapCount > 1) bufsize = header.pitchOrLinearSize * 2;
+                else bufsize = header.pitchOrLinearSize; 
+                
+                image.data = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); 
+                
+                fread(image.data, 1, bufsize, ddsFile); 
+                
+                // Close file pointer
+                fclose(ddsFile);
+                
+                image.mipmaps = header.mipMapCount;
+                image.compFormat = 0;
+           
+                switch(header.ddspf.fourCC)
+                { 
+                    case FOURCC_DXT1: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; 
+                    case FOURCC_DXT3: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; 
+                    case FOURCC_DXT5: image.compFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; 
+                    default: break;
+                }
             
-            image.width = width;
-            image.height = height;
-            image.mipmaps = mipMapCount;
-            image.format = fourCC;
+                // NOTE: Image num color components not required... for now...
+                //if (fourCC == FOURCC_DXT1) image.components = 3;
+                //else image.components = 4;
+#endif
+            }
         }
     }
     

+ 44 - 72
src/utils.c

@@ -4,8 +4,9 @@
 *
 *   Utils Functions Definitions
 *    
-*   Uses external lib:    
+*   Uses external libs:    
 *       tinfl - zlib DEFLATE algorithm decompression lib
+*       stb_image_write - PNG writting functions
 *       
 *   Copyright (c) 2013 Ramon Santamaria (Ray San - [email protected])
 *    
@@ -28,14 +29,14 @@
 
 #include "utils.h"
 
-#include <stdlib.h>         // malloc(), free()
-#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()
+#include <stdlib.h>             // malloc(), free()
+#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
-
 #include "stb_image_write.h"    // Create PNG file
+
 #include "tinfl.c"
 
 //----------------------------------------------------------------------------------
@@ -140,84 +141,39 @@ void WritePNG(const char *fileName, unsigned char *imgData, int width, int heigh
 // 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
+
+    // NOTE: If trace log file not set, output redirected to stdout 
+    if (logstream == NULL) logstream = stdout;
     
-    if (logstream != NULL)
+    switch(msgType)
     {
-        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");
-        }
+        case INFO: fprintf(logstream, "INFO: "); break;
+        case ERROR: fprintf(logstream, "ERROR: "); break;
+        case WARNING: fprintf(logstream, "WARNING: "); break;
+        case DEBUG: if (traceDebugMsgs) fprintf(logstream, "DEBUG: "); break;
+        default: break;
     }
-    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 != DEBUG) || ((msgType == DEBUG) && (traceDebugMsgs)))
+    {
+        va_start(args, text);
+        vfprintf(logstream, text, args);
+        va_end(args);
         
-        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");
-        }
+        fprintf(logstream, "\n");
     }
     
-    if (msgType == 1) exit(1);      // If ERROR message, exit program
+    if (msgType == ERROR) exit(1);      // If ERROR message, exit program
 }
 
-// Inits a trace log file
-void InitTraceLogFile(const char *logFileName)
+// Open a trace log file (if desired)
+void TraceLogOpen(const char *logFileName)
 {
     // stdout redirected to stream file
     FILE *logstream = fopen(logFileName, "w");
@@ -225,9 +181,25 @@ void InitTraceLogFile(const char *logFileName)
     if (logstream == NULL) TraceLog(WARNING, "Unable to open log file");
 }
 
-// Closes the trace log file
-void CloseTraceLogFile()
+// Close the trace log file
+void TraceLogClose()
 {
     if (logstream != NULL) fclose(logstream);
 }
 
+// Keep track of memory allocated
+// NOTE: mallocType defines the type of data allocated
+void RecordMalloc(int mallocType, int mallocSize, const char *msg)
+{
+    // TODO: Investigate how to record memory allocation data...
+    // Maybe creating my own malloc function...
+}
+
+// Get the extension for a filename
+const char *GetExtension(const char *fileName) 
+{
+    const char *dot = strrchr(fileName, '.');
+    if(!dot || dot == fileName) return "";
+    return (dot + 1);
+}
+

+ 6 - 7
src/utils.h

@@ -3,9 +3,6 @@
 *   raylib.utils
 *
 *   Some utility functions: rRES files data decompression
-*    
-*   Uses external lib:    
-*       tinfl - zlib DEFLATE algorithm decompression lib
 *       
 *   Copyright (c) 2013 Ramon Santamaria (Ray San - [email protected])
 *    
@@ -32,12 +29,12 @@
 //----------------------------------------------------------------------------------
 // Some basic Defines
 //----------------------------------------------------------------------------------
-//#define DO_NOT_TRACE_DEBUG_MSGS   // Use this define to avoid DEBUG tracing
+#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 { IMAGE = 0, SOUND, MODEL, TEXT, RAW } DataType;
 
 typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType;
 
@@ -68,8 +65,10 @@ void WriteBitmap(const char *fileName, unsigned char *imgData, int width, int he
 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
+void TraceLogOpen(const char *logFileName);         // Open a trace log file (if desired)
+void TraceLogClose();                               // Close the trace log file
+
+const char *GetExtension(const char *fileName);
 
 #ifdef __cplusplus
 }