Browse Source

Merge pull request #5 from raysan5/master

merge
Murray Campbell 7 years ago
parent
commit
c015529088

+ 1 - 1
CONTRIBUTING.md

@@ -7,7 +7,7 @@ Use your best judgement, and feel free to propose changes to this document in a
 
 
 ### raylib philosophy
 ### raylib philosophy
 
 
- - raylib is a tool to LEARN videogames programming, every single function in raylib should be a tutorial on itself.
+ - raylib is a tool to enjoy videogames programming, every single function in raylib should be a tutorial on itself.
  - raylib is SIMPLE and EASY-TO-USE, I tried to keep it compact with a small set of functions, if a function is too complex or is not clearly useful, better not to include it.
  - raylib is SIMPLE and EASY-TO-USE, I tried to keep it compact with a small set of functions, if a function is too complex or is not clearly useful, better not to include it.
  - raylib is open source and free; educators and institutions can use this tool to TEACH videogames programming completely by free.
  - raylib is open source and free; educators and institutions can use this tool to TEACH videogames programming completely by free.
  - raylib is collaborative; contribution of tutorials / code-examples / bugs-solving / code-comments are highly appreciated.
  - raylib is collaborative; contribution of tutorials / code-examples / bugs-solving / code-comments are highly appreciated.

+ 1 - 1
HELPME.md

@@ -18,7 +18,7 @@ with a small [donation](http://www.raylib.com/helpme.html) or contributing with
 raylib philosophy
 raylib philosophy
 ------------------
 ------------------
 
 
-   * raylib is a tool to LEARN videogames programming, every single function in raylib should be a tutorial on itself.
+   * raylib is a tool to enjoy videogames programming, every single function in raylib should be a tutorial on itself.
    * raylib is SIMPLE and EASY-TO-USE, I tried to keep it compact with a small set of functions, if a function is too complex or has not a clear usefulness, better not to include it.
    * raylib is SIMPLE and EASY-TO-USE, I tried to keep it compact with a small set of functions, if a function is too complex or has not a clear usefulness, better not to include it.
    * raylib is open source and free; educators and institutions can use this tool to TEACH videogames programming completely by free. 
    * raylib is open source and free; educators and institutions can use this tool to TEACH videogames programming completely by free. 
    * raylib is collaborative; contribution of tutorials / code-examples / bugs-solving / code-comments are highly appreciated.
    * raylib is collaborative; contribution of tutorials / code-examples / bugs-solving / code-comments are highly appreciated.

+ 2 - 2
README.md

@@ -1,12 +1,12 @@
 <img align="left" src="https://github.com/raysan5/raylib/blob/master/logo/raylib_256x256.png" width=256>
 <img align="left" src="https://github.com/raysan5/raylib/blob/master/logo/raylib_256x256.png" width=256>
 
 
-**raylib is a simple and easy-to-use library to learn videogames programming.**
+**raylib is a simple and easy-to-use library to enjoy videogames programming.**
 
 
 raylib is highly inspired by Borland BGI graphics lib and by XNA framework.
 raylib is highly inspired by Borland BGI graphics lib and by XNA framework.
 
 
 raylib could be useful for prototyping, tools development, graphic applications, embedded systems and education.
 raylib could be useful for prototyping, tools development, graphic applications, embedded systems and education.
 
 
-NOTE for ADVENTURERS: raylib is a programming library to learn videogames programming; 
+NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; 
 no fancy interface, no visual helpers, no auto-debugging... just coding in the most 
 no fancy interface, no visual helpers, no auto-debugging... just coding in the most 
 pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html)
 pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html)
 
 

+ 2 - 3
examples/text/text_font_sdf.c

@@ -29,19 +29,18 @@ int main()
     fontDefault.baseSize = 16;
     fontDefault.baseSize = 16;
     fontDefault.charsCount = 95;
     fontDefault.charsCount = 95;
     // Parameters > font size: 16, no chars array provided (0), chars count: 95 (autogenerate chars array)
     // Parameters > font size: 16, no chars array provided (0), chars count: 95 (autogenerate chars array)
-    fontDefault.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 95, false);
+    fontDefault.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 95, FONT_DEFAULT);
     // Parameters > chars count: 95, font size: 16, chars padding in image: 4 px, pack method: 0 (default)
     // Parameters > chars count: 95, font size: 16, chars padding in image: 4 px, pack method: 0 (default)
     Image atlas = GenImageFontAtlas(fontDefault.chars, 95, 16, 4, 0);
     Image atlas = GenImageFontAtlas(fontDefault.chars, 95, 16, 4, 0);
     fontDefault.texture = LoadTextureFromImage(atlas);
     fontDefault.texture = LoadTextureFromImage(atlas);
     UnloadImage(atlas);
     UnloadImage(atlas);
     
     
     // SDF font generation from TTF font
     // SDF font generation from TTF font
-    // NOTE: SDF chars data is generated with LoadFontData(), it's just a bool option
     Font fontSDF = { 0 };
     Font fontSDF = { 0 };
     fontSDF.baseSize = 16;
     fontSDF.baseSize = 16;
     fontSDF.charsCount = 95;
     fontSDF.charsCount = 95;
     // Parameters > font size: 16, no chars array provided (0), chars count: 0 (defaults to 95)
     // Parameters > font size: 16, no chars array provided (0), chars count: 0 (defaults to 95)
-    fontSDF.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 0, true);
+    fontSDF.chars = LoadFontData("resources/AnonymousPro-Bold.ttf", 16, 0, 0, FONT_SDF);
     // Parameters > chars count: 95, font size: 16, chars padding in image: 0 px, pack method: 1 (Skyline algorythm)
     // Parameters > chars count: 95, font size: 16, chars padding in image: 0 px, pack method: 1 (Skyline algorythm)
     atlas = GenImageFontAtlas(fontSDF.chars, 95, 16, 0, 1);
     atlas = GenImageFontAtlas(fontSDF.chars, 95, 16, 0, 1);
     fontSDF.texture = LoadTextureFromImage(atlas);
     fontSDF.texture = LoadTextureFromImage(atlas);

+ 1 - 1
examples/textures/textures_image_text.c

@@ -26,7 +26,7 @@ int main()
     Image parrots = LoadImage("resources/parrots.png"); // Load image in CPU memory (RAM)
     Image parrots = LoadImage("resources/parrots.png"); // Load image in CPU memory (RAM)
 
 
     // Draw over image using custom font
     // Draw over image using custom font
-    ImageDrawTextEx(&parrots, (Vector2){ 20.0f, 20.0f }, font, "[Parrots font drawing]", (float)font.baseSize, 0.0f, WHITE);
+    ImageDrawTextEx(&parrots, (Vector2){ 20.0f, 20.0f }, font, "[Parrots font drawing]", (float)font.baseSize, 0.0f, RED);
 
 
     Texture2D texture = LoadTextureFromImage(parrots);  // Image converted to texture, uploaded to GPU memory (VRAM)
     Texture2D texture = LoadTextureFromImage(parrots);  // Image converted to texture, uploaded to GPU memory (VRAM)
     UnloadImage(parrots);   // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM
     UnloadImage(parrots);   // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM

+ 1 - 1
games/just_do/screens/screen_logo.c

@@ -37,7 +37,7 @@ static int framesCounter;
 static int finishScreen;
 static int finishScreen;
 
 
 const char msgLogoA[64] = "A simple and easy-to-use library";
 const char msgLogoA[64] = "A simple and easy-to-use library";
-const char msgLogoB[64] = "to learn videogames programming";
+const char msgLogoB[64] = "to enjoy videogames programming";
 
 
 int logoPositionX;
 int logoPositionX;
 int logoPositionY;
 int logoPositionY;

+ 1 - 1
games/koala_seasons/screens/screen_logo.c

@@ -37,7 +37,7 @@ static int framesCounter;
 static int finishScreen;
 static int finishScreen;
 
 
 const char msgLogoA[64] = "A simple and easy-to-use library";
 const char msgLogoA[64] = "A simple and easy-to-use library";
-const char msgLogoB[64] = "to learn videogames programming";
+const char msgLogoB[64] = "to enjoy videogames programming";
 
 
 int logoPositionX;
 int logoPositionX;
 int logoPositionY;
 int logoPositionY;

+ 1 - 1
raylib.pc.in

@@ -4,7 +4,7 @@ libdir=${exec_prefix}/lib
 includedir=${prefix}/include
 includedir=${prefix}/include
 
 
 Name: raylib
 Name: raylib
-Description: Simple and easy-to-use library to learn videogames programming
+Description: Simple and easy-to-use library to enjoy videogames programming
 URL: http://github.com/raysan5/raylib
 URL: http://github.com/raysan5/raylib
 Version: @PROJECT_VERSION@
 Version: @PROJECT_VERSION@
 Libs: -L${libdir} -lraylib @PKG_CONFIG_LIBS_EXTRA@
 Libs: -L${libdir} -lraylib @PKG_CONFIG_LIBS_EXTRA@

+ 1 - 1
src/CMakeLists.txt

@@ -259,7 +259,7 @@ message(STATUS "  GRAPHICS=" ${GRAPHICS})
 
 
 # Packaging
 # Packaging
 SET(CPACK_PACKAGE_NAME "raylib")
 SET(CPACK_PACKAGE_NAME "raylib")
-SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simple and easy-to-use library to learn videogames programming")
+SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Simple and easy-to-use library to enjoy videogames programming")
 SET(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
 SET(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
 SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
 SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
 SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
 SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")

+ 1 - 2
src/CMakeOptions.txt

@@ -45,6 +45,7 @@ option(SUPPORT_FONT_TEXTURE "Draw rectangle shapes using font texture white char
 option(SUPPORT_QUADS_DRAW_MODE "Use QUADS instead of TRIANGLES for drawing when possible. Some lines-based shapes could still use lines" ON)
 option(SUPPORT_QUADS_DRAW_MODE "Use QUADS instead of TRIANGLES for drawing when possible. Some lines-based shapes could still use lines" ON)
 
 
 # textures.c
 # textures.c
+option(SUPPORT_IMAGE_EXPORT "Support image exporting to file" ON)
 option(SUPPORT_IMAGE_GENERATION "Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)" ON)
 option(SUPPORT_IMAGE_GENERATION "Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)" ON)
 option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON)
 option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON)
 option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON)
 option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON)
@@ -72,8 +73,6 @@ option(SUPPORT_FILEFORMAT_MOD  "Support loading MOD for sound" ON)
 option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF})
 option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF})
 
 
 # utils.c
 # utils.c
-option(SUPPORT_SAVE_PNG "Support saving image data in PNG file format" ON)
-option(SUPPORT_SAVE_BMP "Support saving image data in BMP file format" ${OFF})
 option(SUPPORT_TRACELOG "Show TraceLog() output messages. NOTE: By default LOG_DEBUG traces not shown" ON)
 option(SUPPORT_TRACELOG "Show TraceLog() output messages. NOTE: By default LOG_DEBUG traces not shown" ON)
 
 
 option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON)
 option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON)

+ 126 - 0
src/audio.c

@@ -221,6 +221,9 @@ static Wave LoadOGG(const char *fileName);          // Load OGG file
 #if defined(SUPPORT_FILEFORMAT_FLAC)
 #if defined(SUPPORT_FILEFORMAT_FLAC)
 static Wave LoadFLAC(const char *fileName);         // Load FLAC file
 static Wave LoadFLAC(const char *fileName);         // Load FLAC file
 #endif
 #endif
+#if defined(SUPPORT_FILEFORMAT_MP3)
+static Wave LoadMP3(const char *fileName);          // Load MP3 file
+#endif
 
 
 #if defined(AUDIO_STANDALONE)
 #if defined(AUDIO_STANDALONE)
 bool IsFileExtension(const char *fileName, const char *ext);    // Check file extension
 bool IsFileExtension(const char *fileName, const char *ext);    // Check file extension
@@ -857,6 +860,9 @@ Wave LoadWave(const char *fileName)
 #endif
 #endif
 #if defined(SUPPORT_FILEFORMAT_FLAC)
 #if defined(SUPPORT_FILEFORMAT_FLAC)
     else if (IsFileExtension(fileName, ".flac")) wave = LoadFLAC(fileName);
     else if (IsFileExtension(fileName, ".flac")) wave = LoadFLAC(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_MP3)
+    else if (IsFileExtension(fileName, ".mp3")) wave = LoadMP3(fileName);
 #endif
 #endif
     else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName);
     else TraceLog(LOG_WARNING, "[%s] Audio fileformat not supported, it can't be loaded", fileName);
 
 
@@ -1049,6 +1055,89 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
 #endif
 #endif
 }
 }
 
 
+// Export wave data to file
+void ExportWave(Wave wave, const char *fileName)
+{
+    bool success = false;
+    
+    if (IsFileExtension(fileName, ".wav"))
+    {
+        // Basic WAV headers structs
+        typedef struct {
+            char chunkID[4];
+            int chunkSize;
+            char format[4];
+        } RiffHeader;
+
+        typedef struct {
+            char subChunkID[4];
+            int subChunkSize;
+            short audioFormat;
+            short numChannels;
+            int sampleRate;
+            int byteRate;
+            short blockAlign;
+            short bitsPerSample;
+        } WaveFormat;
+
+        typedef struct {
+            char subChunkID[4];
+            int subChunkSize;
+        } WaveData;
+    
+        RiffHeader riffHeader;
+        WaveFormat waveFormat;
+        WaveData waveData;
+
+        // Fill structs with data
+        riffHeader.chunkID[0] = 'R';
+        riffHeader.chunkID[1] = 'I';
+        riffHeader.chunkID[2] = 'F';
+        riffHeader.chunkID[3] = 'F';
+        riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8;
+        riffHeader.format[0] = 'W';
+        riffHeader.format[1] = 'A';
+        riffHeader.format[2] = 'V';
+        riffHeader.format[3] = 'E';
+
+        waveFormat.subChunkID[0] = 'f';
+        waveFormat.subChunkID[1] = 'm';
+        waveFormat.subChunkID[2] = 't';
+        waveFormat.subChunkID[3] = ' ';
+        waveFormat.subChunkSize = 16;
+        waveFormat.audioFormat = 1;
+        waveFormat.numChannels = wave.channels;
+        waveFormat.sampleRate = wave.sampleRate;
+        waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8;
+        waveFormat.blockAlign = wave.sampleSize/8;
+        waveFormat.bitsPerSample = wave.sampleSize;
+
+        waveData.subChunkID[0] = 'd';
+        waveData.subChunkID[1] = 'a';
+        waveData.subChunkID[2] = 't';
+        waveData.subChunkID[3] = 'a';
+        waveData.subChunkSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
+
+        FILE *wavFile = fopen(fileName, "wb");
+        
+        if (wavFile == NULL) return;
+
+        fwrite(&riffHeader, 1, sizeof(RiffHeader), wavFile);
+        fwrite(&waveFormat, 1, sizeof(WaveFormat), wavFile);
+        fwrite(&waveData, 1, sizeof(WaveData), wavFile);
+
+        fwrite(wave.data, 1, wave.sampleCount*wave.channels*wave.sampleSize/8, wavFile);
+
+        fclose(wavFile);
+        
+        success = true;
+    }
+    else if (IsFileExtension(fileName, ".raw")) { }   // TODO: Support additional file formats to export wave sample data
+
+    if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName);
+    else TraceLog(LOG_WARNING, "Wave could not be exported.");
+}
+
 // Play a sound
 // Play a sound
 void PlaySound(Sound sound)
 void PlaySound(Sound sound)
 {
 {
@@ -1565,6 +1654,9 @@ void StopMusicStream(Music music)
 #if defined(SUPPORT_FILEFORMAT_FLAC)
 #if defined(SUPPORT_FILEFORMAT_FLAC)
         case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
         case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
 #endif
 #endif
+#if defined(SUPPORT_FILEFORMAT_MP3)
+        case MUSIC_AUDIO_MP3: /* TODO: Restart MP3 context */ break;
+#endif
 #if defined(SUPPORT_FILEFORMAT_XM)
 #if defined(SUPPORT_FILEFORMAT_XM)
         case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break;
         case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break;
 #endif
 #endif
@@ -1705,6 +1797,13 @@ void UpdateMusicStream(Music music)
 
 
                 } break;
                 } break;
             #endif
             #endif
+            #if defined(SUPPORT_FILEFORMAT_MP3)
+                case MUSIC_AUDIO_MP3:
+                {
+                    // NOTE: Returns the number of samples to process
+                    unsigned int numSamplesMp3 = (unsigned int)drmp3_read_f32(&music->ctxMp3, samplesCount*music->stream.channels, (float *)pcm);
+                } break;
+            #endif
             #if defined(SUPPORT_FILEFORMAT_XM)
             #if defined(SUPPORT_FILEFORMAT_XM)
                 case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break;
                 case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break;
             #endif
             #endif
@@ -2294,6 +2393,33 @@ static Wave LoadFLAC(const char *fileName)
 }
 }
 #endif
 #endif
 
 
+#if defined(SUPPORT_FILEFORMAT_MP3)
+// Load MP3 file into Wave structure
+// NOTE: Using dr_mp3 library
+static Wave LoadMP3(const char *fileName)
+{
+    Wave wave;
+
+    // Decode an entire MP3 file in one go
+    uint64_t totalSampleCount;
+    drmp3_config *config;
+    wave.data = drmp3_open_and_decode_file_f32(fileName, config, &totalSampleCount);
+    
+    wave.channels = config->outputChannels;
+    wave.sampleRate = config->outputSampleRate;
+    wave.sampleCount = (int)totalSampleCount/wave.channels;
+    wave.sampleSize = 16;
+
+    // NOTE: Only support up to 2 channels (mono, stereo)
+    if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] MP3 channels number (%i) not supported", fileName, wave.channels);
+
+    if (wave.data == NULL) TraceLog(LOG_WARNING, "[%s] MP3 data could not be loaded", fileName);
+    else TraceLog(LOG_INFO, "[%s] MP3 file loaded successfully (%i Hz, %i bit, %s)", fileName, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo");
+
+    return wave;
+}
+#endif
+
 // Some required functions for audio standalone module version
 // Some required functions for audio standalone module version
 #if defined(AUDIO_STANDALONE)
 #if defined(AUDIO_STANDALONE)
 // Check file extension
 // Check file extension

+ 2 - 4
src/config.h

@@ -86,6 +86,8 @@
 //#define SUPPORT_FILEFORMAT_PKM    1
 //#define SUPPORT_FILEFORMAT_PKM    1
 //#define SUPPORT_FILEFORMAT_PVR    1
 //#define SUPPORT_FILEFORMAT_PVR    1
 
 
+// Support image export functionality (.png, .bmp, .tga, .jpg)
+#define SUPPORT_IMAGE_EXPORT        1
 // Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
 // Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
 // If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
 // If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
 #define SUPPORT_IMAGE_MANIPULATION  1
 #define SUPPORT_IMAGE_MANIPULATION  1
@@ -133,10 +135,6 @@
 // Show TraceLog() output messages
 // Show TraceLog() output messages
 // NOTE: By default LOG_DEBUG traces not shown
 // NOTE: By default LOG_DEBUG traces not shown
 #define SUPPORT_TRACELOG    1
 #define SUPPORT_TRACELOG    1
-// Support saving image data fileformats 
-// NOTE: Requires stb_image_write library
-#define SUPPORT_SAVE_PNG    1
-//#define SUPPORT_SAVE_BMP  1
 
 
 
 
 #endif  //defined(RAYLIB_CMAKE)
 #endif  //defined(RAYLIB_CMAKE)

+ 3 - 0
src/config.h.in

@@ -41,6 +41,9 @@
 #cmakedefine SUPPORT_FILEFORMAT_PKM 1
 #cmakedefine SUPPORT_FILEFORMAT_PKM 1
 #cmakedefine SUPPORT_FILEFORMAT_PVR 1
 #cmakedefine SUPPORT_FILEFORMAT_PVR 1
 
 
+// Support image export functionality (.png, .bmp, .tga, .jpg)
+#define SUPPORT_IMAGE_EXPORT 1
+
 /* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() */
 /* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT() */
 #cmakedefine SUPPORT_IMAGE_MANIPULATION 1
 #cmakedefine SUPPORT_IMAGE_MANIPULATION 1
 
 

+ 4 - 2
src/core.c

@@ -886,7 +886,7 @@ void EndDrawing(void)
     // Wait for some milliseconds...
     // Wait for some milliseconds...
     if (frameTime < targetTime)
     if (frameTime < targetTime)
     {
     {
-        Wait( (float)(targetTime - frameTime)*1000.0f);
+        Wait((float)(targetTime - frameTime)*1000.0f);
 
 
         currentTime = GetTime();
         currentTime = GetTime();
         double extraTime = currentTime - previousTime;
         double extraTime = currentTime - previousTime;
@@ -1297,7 +1297,9 @@ void TakeScreenshot(const char *fileName)
 {
 {
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
     unsigned char *imgData = rlReadScreenPixels(renderWidth, renderHeight);
     unsigned char *imgData = rlReadScreenPixels(renderWidth, renderHeight);
-    SavePNG(fileName, imgData, renderWidth, renderHeight, 4); // Save image as PNG
+
+    Image image = { imgData, renderWidth, renderHeight, 1, UNCOMPRESSED_R8G8B8A8 };
+    ExportImage(image, fileName);
     free(imgData);
     free(imgData);
 
 
     TraceLog(LOG_INFO, "Screenshot taken: %s", fileName);
     TraceLog(LOG_INFO, "Screenshot taken: %s", fileName);

+ 2023 - 0
src/external/cgltf.h

@@ -0,0 +1,2023 @@
+#ifndef CGLTF_H_INCLUDED__
+#define CGLTF_H_INCLUDED__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef unsigned long cgltf_size;
+typedef float cgltf_float;
+typedef int cgltf_bool;
+
+typedef enum cgltf_file_type
+{
+	cgltf_file_type_invalid,
+	cgltf_file_type_gltf,
+	cgltf_file_type_glb,
+} cgltf_file_type;
+
+typedef struct cgltf_options
+{
+	cgltf_file_type type;
+	cgltf_size json_token_count; /* 0 == auto */
+	void* (*memory_alloc)(void* user, cgltf_size size);
+	void (*memory_free) (void* user, void* ptr);
+	void* memory_user_data;
+} cgltf_options;
+
+typedef enum cgltf_result
+{
+	cgltf_result_success,
+	cgltf_result_data_too_short,
+	cgltf_result_unknown_format,
+	cgltf_result_invalid_json,
+	cgltf_result_invalid_options,
+} cgltf_result;
+
+typedef enum cgltf_buffer_view_type
+{
+	cgltf_buffer_view_type_invalid,
+	cgltf_buffer_view_type_indices,
+	cgltf_buffer_view_type_vertices,
+} cgltf_buffer_view_type;
+
+typedef enum cgltf_attribute_type
+{
+	cgltf_attribute_type_invalid,
+	cgltf_attribute_type_position,
+	cgltf_attribute_type_normal,
+	cgltf_attribute_type_tangent,
+	cgltf_attribute_type_texcoord_0,
+	cgltf_attribute_type_texcoord_1,
+	cgltf_attribute_type_color_0,
+	cgltf_attribute_type_joints_0,
+	cgltf_attribute_type_weights_0,
+} cgltf_attribute_type;
+
+typedef enum cgltf_component_type
+{
+	cgltf_component_type_invalid,
+	cgltf_component_type_rgb_32f,
+	cgltf_component_type_rgba_32f,
+	cgltf_component_type_rg_32f,
+	cgltf_component_type_rg_8,
+	cgltf_component_type_rg_16,
+	cgltf_component_type_rgba_8,
+	cgltf_component_type_rgba_16,
+	cgltf_component_type_r_8,
+	cgltf_component_type_r_8u,
+	cgltf_component_type_r_16,
+	cgltf_component_type_r_16u,
+	cgltf_component_type_r_32u,
+	cgltf_component_type_r_32f,
+} cgltf_component_type;
+
+typedef enum cgltf_type
+{
+	cgltf_type_invalid,
+	cgltf_type_scalar,
+	cgltf_type_vec2,
+	cgltf_type_vec3,
+	cgltf_type_vec4,
+	cgltf_type_mat2,
+	cgltf_type_mat3,
+	cgltf_type_mat4,
+} cgltf_type;
+
+typedef enum cgltf_primitive_type
+{
+	cgltf_type_points,
+	cgltf_type_lines,
+	cgltf_type_line_loop,
+	cgltf_type_line_strip,
+	cgltf_type_triangles,
+	cgltf_type_triangle_strip,
+	cgltf_type_triangle_fan,
+} cgltf_primitive_type;
+
+typedef struct cgltf_buffer
+{
+	cgltf_size size;
+	char* uri;
+} cgltf_buffer;
+
+typedef struct cgltf_buffer_view
+{
+	cgltf_buffer* buffer;
+	cgltf_size offset;
+	cgltf_size size;
+	cgltf_size stride; /* 0 == automatically determined by accessor */
+	cgltf_buffer_view_type type;
+} cgltf_buffer_view;
+
+typedef struct cgltf_accessor
+{
+	cgltf_component_type component_type;
+	cgltf_type type;
+	cgltf_size offset;
+	cgltf_size count;
+	cgltf_size stride;
+	cgltf_buffer_view* buffer_view;
+} cgltf_accessor;
+
+typedef struct cgltf_attribute
+{
+	cgltf_attribute_type name;
+	cgltf_accessor* data;
+} cgltf_attribute;
+
+
+typedef struct cgltf_rgba 
+{
+	cgltf_float r;
+	cgltf_float g;
+	cgltf_float b;
+	cgltf_float a;
+} cgltf_rgba;
+
+typedef struct cgltf_image 
+{
+	char* uri;
+	cgltf_buffer_view* buffer_view;
+	char* mime_type;
+} cgltf_image;
+
+typedef struct cgltf_sampler
+{
+	cgltf_float mag_filter;
+	cgltf_float min_filter;
+	cgltf_float wrap_s;
+	cgltf_float wrap_t;
+} cgltf_sampler;
+
+typedef struct cgltf_texture
+{
+	cgltf_image* image;
+	cgltf_sampler* sampler;
+} cgltf_texture;
+
+typedef struct cgltf_texture_view
+{	
+	cgltf_texture* texture;
+	cgltf_size texcoord;
+	cgltf_float scale;
+} cgltf_texture_view;
+
+typedef struct cgltf_pbr
+{
+	cgltf_texture_view base_color_texture;
+	cgltf_texture_view metallic_roughness_texture;
+
+	cgltf_rgba base_color;
+	cgltf_float metallic_factor;
+	cgltf_float roughness_factor;
+} cgltf_pbr;
+
+typedef struct cgltf_material
+{
+	char* name;
+	cgltf_pbr pbr;
+	cgltf_rgba emissive_color;
+	cgltf_texture_view normal_texture;
+	cgltf_texture_view emissive_texture;
+	cgltf_texture_view occlusion_texture;
+	cgltf_bool double_sided;
+} cgltf_material;
+
+typedef struct cgltf_primitive {
+	cgltf_primitive_type type;
+	cgltf_accessor* indices;
+	cgltf_material* material;
+	cgltf_attribute* attributes;
+	cgltf_size attributes_count;
+} cgltf_primitive;
+
+typedef struct cgltf_mesh {
+	char* name;
+	cgltf_primitive* primitives;
+	cgltf_size primitives_count;
+} cgltf_mesh;
+
+typedef struct cgltf_data
+{
+	unsigned version;
+	cgltf_file_type file_type;
+
+	cgltf_mesh* meshes;
+	cgltf_size meshes_count;
+
+	cgltf_material* materials;
+	cgltf_size materials_count;
+
+	cgltf_accessor* accessors;
+	cgltf_size accessors_count;
+
+	cgltf_buffer_view* buffer_views;
+	cgltf_size buffer_views_count;
+
+	cgltf_buffer* buffers;
+	cgltf_size buffers_count;
+
+	cgltf_image* images;
+	cgltf_size images_count;
+
+	cgltf_texture* textures;
+	cgltf_size textures_count;
+
+	cgltf_sampler* samplers;
+	cgltf_size samplers_count;
+
+	const void* bin;
+	cgltf_size bin_size;
+
+	void (*memory_free) (void* user, void* ptr);
+	void* memory_user_data;
+} cgltf_data;
+
+cgltf_result cgltf_parse(
+		const cgltf_options* options,
+		const void* data,
+		cgltf_size size,
+		cgltf_data* out_data);
+
+void cgltf_free(cgltf_data* data);
+
+#endif /* #ifndef CGLTF_H_INCLUDED__ */
+
+/*
+ *
+ * Stop now, if you are only interested in the API.
+ * Below, you find the implementation.
+ *
+ */
+
+#ifdef __INTELLISENSE__
+/* This makes MSVC intellisense work. */
+#define CGLTF_IMPLEMENTATION
+#endif
+
+#ifdef CGLTF_IMPLEMENTATION
+
+#include <stdint.h> /* For uint8_t, uint32_t */
+#include <string.h> /* For strncpy */
+#include <stdlib.h> /* For malloc, free */
+
+
+/*
+ * -- jsmn.h start --
+ * Source: https://github.com/zserge/jsmn
+ * License: MIT
+ */
+typedef enum {
+	JSMN_UNDEFINED = 0,
+	JSMN_OBJECT = 1,
+	JSMN_ARRAY = 2,
+	JSMN_STRING = 3,
+	JSMN_PRIMITIVE = 4
+} jsmntype_t;
+enum jsmnerr {
+	/* Not enough tokens were provided */
+	JSMN_ERROR_NOMEM = -1,
+	/* Invalid character inside JSON string */
+	JSMN_ERROR_INVAL = -2,
+	/* The string is not a full JSON packet, more bytes expected */
+	JSMN_ERROR_PART = -3
+};
+typedef struct {
+	jsmntype_t type;
+	int start;
+	int end;
+	int size;
+#ifdef JSMN_PARENT_LINKS
+	int parent;
+#endif
+} jsmntok_t;
+typedef struct {
+	unsigned int pos; /* offset in the JSON string */
+	unsigned int toknext; /* next token to allocate */
+	int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+void jsmn_init(jsmn_parser *parser);
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens);
+/*
+ * -- jsmn.h end --
+ */
+
+
+static const cgltf_size GltfHeaderSize = 12;
+static const cgltf_size GltfChunkHeaderSize = 8;
+static const uint32_t GltfMagic = 0x46546C67;
+static const uint32_t GltfMagicJsonChunk = 0x4E4F534A;
+static const uint32_t GltfMagicBinChunk = 0x004E4942;
+
+static void* cgltf_mem_alloc(void* user, cgltf_size size)
+{
+	return malloc(size);
+}
+
+static void cgltf_mem_free(void* user, void* ptr)
+{
+	free(ptr);
+}
+
+static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data* out_data);
+
+cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data* out_data)
+{
+	if (size < GltfHeaderSize)
+	{
+		return cgltf_result_data_too_short;
+	}
+
+	if (options == NULL)
+	{
+		return cgltf_result_invalid_options;
+	}
+
+	cgltf_options fixed_options = *options;
+	if (fixed_options.memory_alloc == NULL)
+	{
+		fixed_options.memory_alloc = &cgltf_mem_alloc;
+	}
+	if (fixed_options.memory_free == NULL)
+	{
+		fixed_options.memory_free = &cgltf_mem_free;
+	}
+
+	uint32_t tmp;
+	// Magic
+	memcpy(&tmp, data, 4);
+	if (tmp != GltfMagic)
+	{
+		if (fixed_options.type == cgltf_file_type_invalid)
+		{
+			fixed_options.type = cgltf_file_type_gltf;
+		}
+		else
+		{
+			return cgltf_result_unknown_format;
+		}
+	}
+
+	memset(out_data, 0, sizeof(cgltf_data));
+	out_data->memory_free = fixed_options.memory_free;
+	out_data->memory_user_data = fixed_options.memory_user_data;
+
+	if (fixed_options.type == cgltf_file_type_gltf)
+	{
+		out_data->file_type = cgltf_file_type_gltf;
+		return cgltf_parse_json(&fixed_options, data, size, out_data);
+	}
+
+	const uint8_t* ptr = (const uint8_t*)data;
+	// Version
+	memcpy(&tmp, ptr + 4, 4);
+	out_data->version = tmp;
+
+	// Total length
+	memcpy(&tmp, ptr + 8, 4);
+	if (tmp > size)
+	{
+		return cgltf_result_data_too_short;
+	}
+
+	const uint8_t* json_chunk = ptr + GltfHeaderSize;
+
+	// JSON chunk: length
+	uint32_t json_length;
+	memcpy(&json_length, json_chunk, 4);
+	if (GltfHeaderSize + GltfChunkHeaderSize + json_length > size)
+	{
+		return cgltf_result_data_too_short;
+	}
+
+	// JSON chunk: magic
+	memcpy(&tmp, json_chunk + 4, 4);
+	if (tmp != GltfMagicJsonChunk)
+	{
+		return cgltf_result_unknown_format;
+	}
+
+	json_chunk += GltfChunkHeaderSize;
+	cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data);
+	if (json_result != cgltf_result_success)
+	{
+		return json_result;
+	}
+
+	out_data->file_type = cgltf_file_type_invalid;
+	if (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize <= size)
+	{
+		// We can read another chunk
+		const uint8_t* bin_chunk = json_chunk + json_length;
+
+		// Bin chunk: length
+		uint32_t bin_length;
+		memcpy(&bin_length, bin_chunk, 4);
+		if (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize + bin_length > size)
+		{
+			return cgltf_result_data_too_short;
+		}
+
+		// Bin chunk: magic
+		memcpy(&tmp, bin_chunk + 4, 4);
+		if (tmp != GltfMagicBinChunk)
+		{
+			return cgltf_result_unknown_format;
+		}
+
+		bin_chunk += GltfChunkHeaderSize;
+
+		out_data->file_type = cgltf_file_type_glb;
+		out_data->bin = bin_chunk;
+		out_data->bin_size = bin_length;
+	}
+
+	return cgltf_result_success;
+}
+
+void cgltf_free(cgltf_data* data)
+{
+	data->memory_free(data->memory_user_data, data->accessors);
+	data->memory_free(data->memory_user_data, data->buffer_views);
+
+
+	for (cgltf_size i = 0; i < data->buffers_count; ++i)
+	{
+		data->memory_free(data->memory_user_data, data->buffers[i].uri);
+	}
+	data->memory_free(data->memory_user_data, data->buffers);
+
+	for (cgltf_size i = 0; i < data->meshes_count; ++i)
+	{
+		data->memory_free(data->memory_user_data, data->meshes[i].name);
+		for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
+		{
+			data->memory_free(data->memory_user_data, data->meshes[i].primitives[j].attributes);
+		}
+		data->memory_free(data->memory_user_data, data->meshes[i].primitives);
+	}
+	data->memory_free(data->memory_user_data, data->meshes);
+
+	for (cgltf_size i = 0; i < data->materials_count; ++i)
+	{
+		data->memory_free(data->memory_user_data, data->materials[i].name);
+	}
+
+	data->memory_free(data->memory_user_data, data->materials);
+
+	for (cgltf_size i = 0; i < data->images_count; ++i) 
+	{
+		data->memory_free(data->memory_user_data, data->images[i].uri);
+		data->memory_free(data->memory_user_data, data->images[i].mime_type);
+	}
+
+	data->memory_free(data->memory_user_data, data->images);
+	data->memory_free(data->memory_user_data, data->textures);
+	data->memory_free(data->memory_user_data, data->samplers);
+}
+
+#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return -128; }
+
+static char cgltf_to_lower(char c)
+{
+	if (c >= 'A' && c <= 'Z')
+	{
+		c = 'a' + (c - 'A');
+	}
+	return c;
+}
+
+static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str)
+{
+	CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING);
+	int const str_len = strlen(str);
+	int const name_length = tok->end - tok->start;
+	if (name_length == str_len)
+	{
+		for (int i = 0; i < str_len; ++i)
+		{
+			char const a = cgltf_to_lower(*((const char*)json_chunk + tok->start + i));
+			char const b = cgltf_to_lower(*(str + i));
+			if (a < b)
+			{
+				return -1;
+			}
+			else if (a > b)
+			{
+				return 1;
+			}
+		}
+		return 0;
+	}
+	return 128;
+}
+
+static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk)
+{
+	char tmp[128];
+	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
+	int size = tok->end - tok->start;
+	strncpy(tmp,
+		(const char*)json_chunk + tok->start,
+		size);
+	tmp[size] = 0;
+	return atoi(tmp);
+}
+
+static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) {
+	char tmp[128];
+	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
+	int size = tok->end - tok->start;
+	strncpy(tmp,
+		(const char*)json_chunk + tok->start,
+		size);
+	tmp[size] = 0;
+	return atof(tmp);
+}
+
+static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) {
+	//TODO: error handling?
+	if (memcmp(json_chunk + tok->start, "true", 4) == 0)
+		return 1;
+
+	return 0;
+}
+
+static int cgltf_skip_json(jsmntok_t const* tokens, int i)
+{
+	if (tokens[i].type == JSMN_ARRAY)
+	{
+		int size = tokens[i].size;
+		++i;
+		for (int j = 0; j < size; ++j)
+		{
+			i = cgltf_skip_json(tokens, i);
+		}
+	}
+	else if (tokens[i].type == JSMN_OBJECT)
+	{
+		int size = tokens[i].size;
+		++i;
+		for (int j = 0; j < size; ++j)
+		{
+			i = cgltf_skip_json(tokens, i);
+			i = cgltf_skip_json(tokens, i);
+		}
+	}
+	else if (tokens[i].type == JSMN_PRIMITIVE
+		 || tokens[i].type == JSMN_STRING)
+	{
+		return i + 1;
+	}
+	return i;
+}
+
+
+static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i,
+				      const uint8_t* json_chunk,
+				      cgltf_primitive* out_prim)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	out_prim->indices = (void* )-1;
+	out_prim->material = NULL;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0)
+		{
+			++i;
+			out_prim->type
+					= (cgltf_primitive_type)
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0)
+		{
+			++i;
+			out_prim->indices =
+					(void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0)
+		{
+			++i;
+			out_prim->material =
+				(void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0)
+		{
+			++i;
+			if (tokens[i].type != JSMN_OBJECT)
+			{
+				return -1;
+			}
+			out_prim->attributes_count = tokens[i].size;
+			out_prim->attributes
+					= options->memory_alloc(options->memory_user_data, sizeof(cgltf_attribute) * tokens[i].size);
+			++i;
+			for (cgltf_size iattr = 0; iattr < out_prim->attributes_count; ++iattr)
+			{
+				CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING);
+				out_prim->attributes[iattr].name = cgltf_attribute_type_invalid;
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "POSITION") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_position;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "NORMAL") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_normal;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "TANGENT") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_tangent;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_0") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_0;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_1") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_1;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "COLOR_0") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_color_0;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "JOINTS_0") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_joints_0;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "WEIGHTS_0") == 0)
+				{
+					out_prim->attributes[iattr].name = cgltf_attribute_type_weights_0;
+				}
+				++i;
+				out_prim->attributes[iattr].data =
+						(void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk);
+				++i;
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i,
+				 const uint8_t* json_chunk, cgltf_size mesh_index,
+				 cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	out_data->meshes[mesh_index].name = NULL;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			++i;
+			int strsize = tokens[i].end - tokens[i].start;
+			out_data->meshes[mesh_index].name = options->memory_alloc(options->memory_user_data, strsize + 1);
+			strncpy(out_data->meshes[mesh_index].name,
+				(const char*)json_chunk + tokens[i].start,
+				strsize);
+			out_data->meshes[mesh_index].name[strsize] = 0;
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0)
+		{
+			++i;
+			if (tokens[i].type != JSMN_ARRAY)
+			{
+				return -1;
+			}
+			out_data->meshes[mesh_index].primitives_count = tokens[i].size;
+			out_data->meshes[mesh_index].primitives = options->memory_alloc(options->memory_user_data, sizeof(cgltf_primitive) * tokens[i].size);
+			++i;
+
+			for (cgltf_size prim_index = 0;
+			     prim_index < out_data->meshes[mesh_index].primitives_count;
+			     ++prim_index)
+			{
+				i = cgltf_parse_json_primitive(options, tokens, i, json_chunk,
+							       &out_data->meshes[mesh_index].primitives[prim_index]);
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->meshes_count = tokens[i].size;
+	out_data->meshes = options->memory_alloc(options->memory_user_data, sizeof(cgltf_mesh) * out_data->meshes_count);
+	++i;
+	for (cgltf_size j = 0 ; j < out_data->meshes_count; ++j)
+	{
+		i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, j, out_data);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_accessor(jsmntok_t const* tokens, int i,
+				     const uint8_t* json_chunk, cgltf_size accessor_index,
+				     cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	memset(&out_data->accessors[accessor_index], 0, sizeof(cgltf_accessor));
+	out_data->accessors[accessor_index].buffer_view = (void*)-1;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
+		{
+			++i;
+			out_data->accessors[accessor_index].buffer_view =
+					(void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
+		{
+			++i;
+			out_data->accessors[accessor_index].offset =
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0)
+		{
+			++i;
+			int type = cgltf_json_to_int(tokens+i, json_chunk);
+			switch (type)
+			{
+			case 5120:
+				type = cgltf_component_type_r_8;
+				break;
+			case 5121:
+				type = cgltf_component_type_r_8u;
+				break;
+			case 5122:
+				type = cgltf_component_type_r_16;
+				break;
+			case 5123:
+				type = cgltf_component_type_r_16u;
+				break;
+			case 5125:
+				type = cgltf_component_type_r_32u;
+				break;
+			case 5126:
+				type = cgltf_component_type_r_32f;
+				break;
+			default:
+				type = cgltf_component_type_invalid;
+				break;
+			}
+			out_data->accessors[accessor_index].component_type = type;
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
+		{
+			++i;
+			out_data->accessors[accessor_index].count =
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
+		{
+			++i;
+			if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0)
+			{
+				out_data->accessors[accessor_index].type = cgltf_type_scalar;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0)
+			{
+				out_data->accessors[accessor_index].type = cgltf_type_vec2;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0)
+			{
+				out_data->accessors[accessor_index].type = cgltf_type_vec3;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0)
+			{
+				out_data->accessors[accessor_index].type = cgltf_type_vec4;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0)
+			{
+				out_data->accessors[accessor_index].type = cgltf_type_mat2;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0)
+			{
+				out_data->accessors[accessor_index].type = cgltf_type_mat3;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0)
+			{
+				out_data->accessors[accessor_index].type = cgltf_type_mat4;
+			}
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_rgba(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_rgba* out)
+{
+	int components = tokens[i].size;
+	if (components >= 2) {
+		out->r = cgltf_json_to_float(tokens + ++i, json_chunk);
+		out->g = cgltf_json_to_float(tokens + ++i, json_chunk);
+
+		if (components > 2)
+			out->b = cgltf_json_to_float(tokens + ++i, json_chunk);
+
+		if (components > 3)
+			out->a = cgltf_json_to_float(tokens + ++i, json_chunk);
+	}
+	else {
+		out->r = cgltf_json_to_float(tokens + ++i, json_chunk);
+		out->g = out->r;
+		out->b = out->r;
+		out->a = out->r;
+	}
+
+	return ++i;
+}
+
+static int cgltf_parse_json_texture_view(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out) {
+
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0)
+		{
+			++i;
+			out->texture = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0)
+		{
+			++i;
+			out->texcoord = cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) 
+		{
+			++i;
+			out->scale = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_pbr(jsmntok_t const* tokens, int i,
+				     const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0)
+		{
+			++i;
+			out_data->materials[mat_index].pbr.metallic_factor = 
+				cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) 
+		{
+			++i;
+			out_data->materials[mat_index].pbr.roughness_factor =
+				cgltf_json_to_float(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0)
+		{
+			i = cgltf_parse_json_rgba(tokens, i + 1, json_chunk,
+					&(out_data->materials[mat_index].pbr.base_color));
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk,
+				&(out_data->materials[mat_index].pbr.base_color_texture));
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk,
+				&(out_data->materials[mat_index].pbr.metallic_roughness_texture));
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i,
+	const uint8_t* json_chunk, cgltf_size img_index, cgltf_data* out_data) 
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	memset(&out_data->images[img_index], 0, sizeof(cgltf_image));
+	int size = tokens[i].size;
+	++i;
+
+	out_data->images[img_index].buffer_view = (void*)-1;
+
+	for (int j = 0; j < size; ++j) 
+	{
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) 
+		{
+			++i;
+			int strsize = tokens[i].end - tokens[i].start;
+			out_data->images[img_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1);
+			strncpy(out_data->images[img_index].uri,
+				(const char*)json_chunk + tokens[i].start,
+				strsize);
+			out_data->images[img_index].uri[strsize] = 0;
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
+		{
+			++i;
+			out_data->images[img_index].buffer_view =
+					(void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0)
+		{
+			++i;
+			int strsize = tokens[i].end - tokens[i].start;
+			out_data->images[img_index].mime_type = options->memory_alloc(options->memory_user_data, strsize + 1);
+			strncpy(out_data->images[img_index].mime_type,
+				(const char*)json_chunk + tokens[i].start,
+				strsize);
+			out_data->images[img_index].mime_type[strsize] = 0;
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i,
+	const uint8_t* json_chunk, cgltf_size smp_index, cgltf_data* out_data) {
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	memset(&out_data->samplers[smp_index], 0, sizeof(cgltf_sampler));
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) 
+		{
+			++i;
+			out_data->samplers[smp_index].mag_filter
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0)
+		{
+			++i;
+			out_data->samplers[smp_index].min_filter
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0)
+		{
+			++i;
+			out_data->samplers[smp_index].wrap_s
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) 
+		{
+			++i;
+			out_data->samplers[smp_index].wrap_t
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+	}
+
+	return i;
+}
+
+
+static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i,
+	const uint8_t* json_chunk, cgltf_size tex_index, cgltf_data* out_data) {
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	memset(&out_data->textures[tex_index], 0, sizeof(cgltf_texture));
+	out_data->textures[tex_index].image = (void*)-1;
+	out_data->textures[tex_index].sampler = (void*)-1;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0)
+		{
+			++i;
+			out_data->textures[tex_index].sampler 
+				= (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) 
+		{
+			++i;
+			out_data->textures[tex_index].image
+				= (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i,
+				     const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	cgltf_material* material = &out_data->materials[mat_index];
+
+	memset(material, 0, sizeof(cgltf_material));
+	material->emissive_texture.texture = (void*)-1;
+	material->emissive_texture.scale = 1.0f;
+
+	material->normal_texture.texture = (void*)-1;
+	material->normal_texture.scale = 1.0f;
+
+	material->occlusion_texture.texture = (void*)-1;
+	material->occlusion_texture.scale = 1.0f;
+
+	material->pbr.base_color_texture.texture = (void*)-1;
+	material->pbr.base_color_texture.scale = 1.0f;
+
+	material->pbr.metallic_roughness_texture.texture = (void*)-1;
+	material->pbr.metallic_roughness_texture.scale = 1.0f;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			++i;
+			int strsize = tokens[i].end - tokens[i].start;
+			material->name = options->memory_alloc(options->memory_user_data, strsize + 1);
+			strncpy(material->name,
+				(const char*)json_chunk + tokens[i].start,
+				strsize);
+			material->name[strsize] = 0;
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) 
+		{
+			i = cgltf_parse_json_pbr(tokens, i+1, json_chunk, mat_index, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) 
+		{
+			i = cgltf_parse_json_rgba(tokens, i + 1, json_chunk, 
+				&(material->emissive_color));
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk,
+				&(material->normal_texture));
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk,
+				&(material->emissive_texture));
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) 
+		{
+			i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk,
+				&(material->occlusion_texture));
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) 			
+		{
+			++i;
+			material->double_sided =
+				cgltf_json_to_bool(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->accessors_count = tokens[i].size;
+	out_data->accessors = options->memory_alloc(options->memory_user_data, sizeof(cgltf_accessor) * out_data->accessors_count);
+	++i;
+	for (cgltf_size j = 0 ; j < out_data->accessors_count; ++j)
+	{
+		i = cgltf_parse_json_accessor(tokens, i, json_chunk, j, out_data);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->materials_count = tokens[i].size;
+	out_data->materials = options->memory_alloc(options->memory_user_data, sizeof(cgltf_material) * out_data->materials_count);
+	++i;
+	for (cgltf_size j = 0; j < out_data->materials_count; ++j)
+	{
+		i = cgltf_parse_json_material(options, tokens, i, json_chunk, j, out_data);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) {
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->images_count = tokens[i].size;
+	out_data->images = options->memory_alloc(options->memory_user_data, sizeof(cgltf_image) * out_data->images_count);
+	++i;
+
+	for (cgltf_size j = 0; j < out_data->images_count; ++j) {
+		i = cgltf_parse_json_image(options, tokens, i, json_chunk, j, out_data);
+		if (i < 0) {
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) {
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->textures_count = tokens[i].size;
+	out_data->textures = options->memory_alloc(options->memory_user_data, sizeof(cgltf_texture) * out_data->textures_count);
+	++i;
+
+	for (cgltf_size j = 0; j < out_data->textures_count; ++j) {
+		i = cgltf_parse_json_texture(options, tokens, i, json_chunk, j, out_data);
+		if (i < 0) {
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) {
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->samplers_count = tokens[i].size;
+	out_data->samplers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_sampler) * out_data->samplers_count);
+	++i;
+
+	for (cgltf_size j = 0; j < out_data->samplers_count; ++j) {
+		i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, j, out_data);
+		if (i < 0) {
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_buffer_view(jsmntok_t const* tokens, int i,
+					const uint8_t* json_chunk, cgltf_size buffer_view_index,
+					cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	memset(&out_data->buffer_views[buffer_view_index], 0, sizeof(cgltf_buffer_view));
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0)
+		{
+			++i;
+			out_data->buffer_views[buffer_view_index].buffer =
+					(void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
+		{
+			++i;
+			out_data->buffer_views[buffer_view_index].offset =
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0)
+		{
+			++i;
+			out_data->buffer_views[buffer_view_index].size =
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0)
+		{
+			++i;
+			out_data->buffer_views[buffer_view_index].stride =
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0)
+		{
+			++i;
+			int type = cgltf_json_to_int(tokens+i, json_chunk);
+			switch (type)
+			{
+			case 34962:
+				type = cgltf_buffer_view_type_vertices;
+				break;
+			case 34963:
+				type = cgltf_buffer_view_type_indices;
+				break;
+			default:
+				type = cgltf_buffer_view_type_invalid;
+				break;
+			}
+			out_data->buffer_views[buffer_view_index].type = type;
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->buffer_views_count = tokens[i].size;
+	out_data->buffer_views = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer_view) * out_data->buffer_views_count);
+	++i;
+	for (cgltf_size j = 0 ; j < out_data->buffer_views_count; ++j)
+	{
+		i = cgltf_parse_json_buffer_view(tokens, i, json_chunk, j, out_data);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i,
+				   const uint8_t* json_chunk, cgltf_size buffer_index,
+				   cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	out_data->buffers[buffer_index].uri = NULL;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0)
+		{
+			++i;
+			out_data->buffers[buffer_index].size =
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0)
+		{
+			++i;
+			int strsize = tokens[i].end - tokens[i].start;
+			out_data->buffers[buffer_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1);
+			strncpy(out_data->buffers[buffer_index].uri,
+				(const char*)json_chunk + tokens[i].start,
+				strsize);
+			out_data->buffers[buffer_index].uri[strsize] = 0;
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	out_data->buffers_count = tokens[i].size;
+	out_data->buffers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer) * out_data->buffers_count);
+	++i;
+	for (cgltf_size j = 0 ; j < out_data->buffers_count; ++j)
+	{
+		i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, j, out_data);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type)
+{
+	cgltf_type size = 0;
+
+	switch (component_type)
+	{
+	case cgltf_component_type_rgb_32f:
+		size = 12;
+		break;
+	case cgltf_component_type_rgba_32f:
+		size = 16;
+		break;
+	case cgltf_component_type_rg_32f:
+		size = 8;
+		break;
+	case cgltf_component_type_rg_8:
+		size = 2;
+		break;
+	case cgltf_component_type_rg_16:
+		size = 4;
+		break;
+	case cgltf_component_type_rgba_8:
+		size = 4;
+		break;
+	case cgltf_component_type_rgba_16:
+		size = 8;
+		break;
+	case cgltf_component_type_r_8:
+	case cgltf_component_type_r_8u:
+		size = 1;
+		break;
+	case cgltf_component_type_r_16:
+	case cgltf_component_type_r_16u:
+		size = 2;
+		break;
+	case cgltf_component_type_r_32u:
+	case cgltf_component_type_r_32f:
+		size = 4;
+		break;
+	case cgltf_component_type_invalid:
+	default:
+		size = 0;
+		break;
+	}
+
+	switch (type)
+	{
+	case cgltf_type_vec2:
+		size *= 2;
+		break;
+	case cgltf_type_vec3:
+		size *= 3;
+		break;
+	case cgltf_type_vec4:
+		size *= 4;
+		break;
+	case cgltf_type_mat2:
+		size *= 4;
+		break;
+	case cgltf_type_mat3:
+		size *= 9;
+		break;
+	case cgltf_type_mat4:
+		size *= 16;
+		break;
+	case cgltf_type_invalid:
+	case cgltf_type_scalar:
+	default:
+		size *= 1;
+		break;
+	}
+
+	return size;
+}
+
+cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data* out_data)
+{
+	jsmn_parser parser = {0};
+
+	if (options->json_token_count == 0)
+	{
+		options->json_token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0);
+	}
+
+	jsmntok_t* tokens = options->memory_alloc(options->memory_user_data, sizeof(jsmntok_t) * options->json_token_count);
+
+	jsmn_init(&parser);
+
+	int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count);
+
+	if (token_count < 0
+			|| tokens[0].type != JSMN_OBJECT)
+	{
+		return cgltf_result_invalid_json;
+	}
+
+	// The root is an object.
+
+	for (int i = 1; i < token_count; )
+	{
+		jsmntok_t const* tok = &tokens[i];
+		if (tok->type == JSMN_STRING
+				&& i + 1 < token_count)
+		{
+			int const name_length = tok->end - tok->start;
+			if (name_length == 6
+					&& strncmp((const char*)json_chunk + tok->start, "meshes", 6) == 0)
+			{
+				i = cgltf_parse_json_meshes(options, tokens, i+1, json_chunk, out_data);
+			}
+			else if (name_length == 9
+				 && strncmp((const char*)json_chunk + tok->start, "accessors", 9) == 0)
+			{
+				i = cgltf_parse_json_accessors(options, tokens, i+1, json_chunk, out_data);
+			}
+			else if (name_length == 11
+				 && strncmp((const char*)json_chunk + tok->start, "bufferViews", 11) == 0)
+			{
+				i = cgltf_parse_json_buffer_views(options, tokens, i+1, json_chunk, out_data);
+			}
+			else if (name_length == 7
+				 && strncmp((const char*)json_chunk + tok->start, "buffers", 7) == 0)
+			{
+				i = cgltf_parse_json_buffers(options, tokens, i+1, json_chunk, out_data);
+			}
+			else if (name_length == 9
+				&& strncmp((const char*)json_chunk + tok->start, "materials", 9) == 0) 		
+			{
+				i = cgltf_parse_json_materials(options, tokens, i+1, json_chunk, out_data);
+			}
+			else if (name_length == 6
+				&& strncmp((const char*)json_chunk + tok->start, "images", 6) == 0) 
+			{
+				i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data);
+			}
+			else if (name_length == 8
+				&& strncmp((const char*)json_chunk + tok->start, "textures", 8) == 0) 
+			{
+				i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data);
+			}
+			else if (name_length == 8
+				&& strncmp((const char*)json_chunk + tok->start, "samplers", 8) == 0)
+			{
+				i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data);
+			}
+			else
+			{
+				i = cgltf_skip_json(tokens, i+1);
+			}
+
+			if (i < 0)
+			{
+				return cgltf_result_invalid_json;
+			}
+		}
+	}
+
+	options->memory_free(options->memory_user_data, tokens);
+
+	/* Fix up pointers */
+	for (cgltf_size i = 0; i < out_data->meshes_count; ++i)
+	{
+		for (cgltf_size j = 0; j < out_data->meshes[i].primitives_count; ++j)
+		{
+			if (out_data->meshes[i].primitives[j].indices ==(void*)-1)
+			{
+				out_data->meshes[i].primitives[j].indices = NULL;
+			}
+			else
+			{
+				out_data->meshes[i].primitives[j].indices
+						= &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].indices];
+			}
+
+			for (cgltf_size k = 0; k < out_data->meshes[i].primitives[j].attributes_count; ++k)
+			{
+				out_data->meshes[i].primitives[j].attributes[k].data
+						= &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].attributes[k].data];
+			}
+		}
+	}
+
+	for (cgltf_size i = 0; i < out_data->accessors_count; ++i)
+	{
+		if (out_data->accessors[i].buffer_view == (void*)-1)
+		{
+			out_data->accessors[i].buffer_view = NULL;
+		}
+		else
+		{
+			out_data->accessors[i].buffer_view
+					= &out_data->buffer_views[(cgltf_size)out_data->accessors[i].buffer_view];
+			out_data->accessors[i].stride = 0;
+			if (out_data->accessors[i].buffer_view)
+			{
+				out_data->accessors[i].stride = out_data->accessors[i].buffer_view->stride;
+			}
+		}
+		if (out_data->accessors[i].stride == 0)
+		{
+			out_data->accessors[i].stride = cgltf_calc_size(out_data->accessors[i].type, out_data->accessors[i].component_type);
+		}
+	}
+
+	for (cgltf_size i = 0; i < out_data->textures_count; ++i) 
+	{
+		if (out_data->textures[i].image == (void*)-1) 
+		{
+			out_data->textures[i].image = NULL;
+		}
+		else
+		{
+			out_data->textures[i].image = 
+				&out_data->images[(cgltf_size)out_data->textures[i].image];
+		}
+
+		if (out_data->textures[i].sampler == (void*)-1)
+		{
+			out_data->textures[i].sampler = NULL;
+		}
+		else 
+		{
+			out_data->textures[i].sampler =
+				&out_data->samplers[(cgltf_size)out_data->textures[i].sampler];
+		}
+	}
+
+	for (cgltf_size i = 0; i < out_data->images_count; ++i)
+	{
+		if (out_data->images[i].buffer_view == (void*)-1)
+		{
+			out_data->images[i].buffer_view = NULL;
+		}
+		else
+		{
+			out_data->images[i].buffer_view
+					= &out_data->buffer_views[(cgltf_size)out_data->images[i].buffer_view];
+		}
+	}
+
+	for (cgltf_size i = 0; i < out_data->materials_count; ++i)
+	{
+		if (out_data->materials[i].emissive_texture.texture == (void*)-1)
+		{
+			out_data->materials[i].emissive_texture.texture = NULL;
+		}
+		else
+		{
+			out_data->materials[i].emissive_texture.texture =
+				&out_data->textures[(cgltf_size)out_data->materials[i].emissive_texture.texture];
+		}
+
+		if (out_data->materials[i].normal_texture.texture == (void*)-1)
+		{
+			out_data->materials[i].normal_texture.texture = NULL;
+		}
+		else 
+		{
+			out_data->materials[i].normal_texture.texture =
+				&out_data->textures[(cgltf_size)out_data->materials[i].normal_texture.texture];
+		}
+
+		if (out_data->materials[i].occlusion_texture.texture == (void*)-1)
+		{
+			out_data->materials[i].occlusion_texture.texture = NULL;
+		}
+		else
+		{
+			out_data->materials[i].occlusion_texture.texture =
+				&out_data->textures[(cgltf_size)out_data->materials[i].occlusion_texture.texture];
+		}
+
+		if (out_data->materials[i].pbr.base_color_texture.texture == (void*)-1)
+		{
+			out_data->materials[i].pbr.base_color_texture.texture = NULL;
+		}
+		else
+		{
+			out_data->materials[i].pbr.base_color_texture.texture =
+				&out_data->textures[(cgltf_size)out_data->materials[i].pbr.base_color_texture.texture];
+		}
+
+		if (out_data->materials[i].pbr.metallic_roughness_texture.texture == (void*)-1)
+		{
+			out_data->materials[i].pbr.metallic_roughness_texture.texture = NULL;
+		}
+		else 
+		{
+			out_data->materials[i].pbr.metallic_roughness_texture.texture =
+				&out_data->textures[(cgltf_size)out_data->materials[i].pbr.metallic_roughness_texture.texture];
+		}
+	}
+
+	for (cgltf_size i = 0; i < out_data->buffer_views_count; ++i)
+	{
+		out_data->buffer_views[i].buffer
+				= &out_data->buffers[(cgltf_size)out_data->buffer_views[i].buffer];
+	}
+
+	return cgltf_result_success;
+}
+
+/*
+ * -- jsmn.c start --
+ * Source: https://github.com/zserge/jsmn
+ * License: MIT
+ */
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+				   jsmntok_t *tokens, size_t num_tokens) {
+	jsmntok_t *tok;
+	if (parser->toknext >= num_tokens) {
+		return NULL;
+	}
+	tok = &tokens[parser->toknext++];
+	tok->start = tok->end = -1;
+	tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+	tok->parent = -1;
+#endif
+	return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+			    int start, int end) {
+	token->type = type;
+	token->start = start;
+	token->end = end;
+	token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+				size_t len, jsmntok_t *tokens, size_t num_tokens) {
+	jsmntok_t *token;
+	int start;
+
+	start = parser->pos;
+
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+		switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+		/* In strict mode primitive must be followed by "," or "}" or "]" */
+		case ':':
+#endif
+		case '\t' : case '\r' : case '\n' : case ' ' :
+		case ','  : case ']'  : case '}' :
+			goto found;
+		}
+		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+			parser->pos = start;
+			return JSMN_ERROR_INVAL;
+		}
+	}
+#ifdef JSMN_STRICT
+	/* In strict mode primitive must be followed by a comma/object/array */
+	parser->pos = start;
+	return JSMN_ERROR_PART;
+#endif
+
+found:
+	if (tokens == NULL) {
+		parser->pos--;
+		return 0;
+	}
+	token = jsmn_alloc_token(parser, tokens, num_tokens);
+	if (token == NULL) {
+		parser->pos = start;
+		return JSMN_ERROR_NOMEM;
+	}
+	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+	token->parent = parser->toksuper;
+#endif
+	parser->pos--;
+	return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+			     size_t len, jsmntok_t *tokens, size_t num_tokens) {
+	jsmntok_t *token;
+
+	int start = parser->pos;
+
+	parser->pos++;
+
+	/* Skip starting quote */
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+		char c = js[parser->pos];
+
+		/* Quote: end of string */
+		if (c == '\"') {
+			if (tokens == NULL) {
+				return 0;
+			}
+			token = jsmn_alloc_token(parser, tokens, num_tokens);
+			if (token == NULL) {
+				parser->pos = start;
+				return JSMN_ERROR_NOMEM;
+			}
+			jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+			token->parent = parser->toksuper;
+#endif
+			return 0;
+		}
+
+		/* Backslash: Quoted symbol expected */
+		if (c == '\\' && parser->pos + 1 < len) {
+			int i;
+			parser->pos++;
+			switch (js[parser->pos]) {
+			/* Allowed escaped symbols */
+			case '\"': case '/' : case '\\' : case 'b' :
+			case 'f' : case 'r' : case 'n'  : case 't' :
+				break;
+				/* Allows escaped symbol \uXXXX */
+			case 'u':
+				parser->pos++;
+				for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
+					/* If it isn't a hex character we have an error */
+					if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+					     (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+					     (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+						parser->pos = start;
+						return JSMN_ERROR_INVAL;
+					}
+					parser->pos++;
+				}
+				parser->pos--;
+				break;
+				/* Unexpected symbol */
+			default:
+				parser->pos = start;
+				return JSMN_ERROR_INVAL;
+			}
+		}
+	}
+	parser->pos = start;
+	return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+	       jsmntok_t *tokens, unsigned int num_tokens) {
+	int r;
+	int i;
+	jsmntok_t *token;
+	int count = parser->toknext;
+
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+		char c;
+		jsmntype_t type;
+
+		c = js[parser->pos];
+		switch (c) {
+		case '{': case '[':
+			count++;
+			if (tokens == NULL) {
+				break;
+			}
+			token = jsmn_alloc_token(parser, tokens, num_tokens);
+			if (token == NULL)
+				return JSMN_ERROR_NOMEM;
+			if (parser->toksuper != -1) {
+				tokens[parser->toksuper].size++;
+#ifdef JSMN_PARENT_LINKS
+				token->parent = parser->toksuper;
+#endif
+			}
+			token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+			token->start = parser->pos;
+			parser->toksuper = parser->toknext - 1;
+			break;
+		case '}': case ']':
+			if (tokens == NULL)
+				break;
+			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+			if (parser->toknext < 1) {
+				return JSMN_ERROR_INVAL;
+			}
+			token = &tokens[parser->toknext - 1];
+			for (;;) {
+				if (token->start != -1 && token->end == -1) {
+					if (token->type != type) {
+						return JSMN_ERROR_INVAL;
+					}
+					token->end = parser->pos + 1;
+					parser->toksuper = token->parent;
+					break;
+				}
+				if (token->parent == -1) {
+					if(token->type != type || parser->toksuper == -1) {
+						return JSMN_ERROR_INVAL;
+					}
+					break;
+				}
+				token = &tokens[token->parent];
+			}
+#else
+			for (i = parser->toknext - 1; i >= 0; i--) {
+				token = &tokens[i];
+				if (token->start != -1 && token->end == -1) {
+					if (token->type != type) {
+						return JSMN_ERROR_INVAL;
+					}
+					parser->toksuper = -1;
+					token->end = parser->pos + 1;
+					break;
+				}
+			}
+			/* Error if unmatched closing bracket */
+			if (i == -1) return JSMN_ERROR_INVAL;
+			for (; i >= 0; i--) {
+				token = &tokens[i];
+				if (token->start != -1 && token->end == -1) {
+					parser->toksuper = i;
+					break;
+				}
+			}
+#endif
+			break;
+		case '\"':
+			r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+			if (r < 0) return r;
+			count++;
+			if (parser->toksuper != -1 && tokens != NULL)
+				tokens[parser->toksuper].size++;
+			break;
+		case '\t' : case '\r' : case '\n' : case ' ':
+			break;
+		case ':':
+			parser->toksuper = parser->toknext - 1;
+			break;
+		case ',':
+			if (tokens != NULL && parser->toksuper != -1 &&
+					tokens[parser->toksuper].type != JSMN_ARRAY &&
+					tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+				parser->toksuper = tokens[parser->toksuper].parent;
+#else
+				for (i = parser->toknext - 1; i >= 0; i--) {
+					if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+						if (tokens[i].start != -1 && tokens[i].end == -1) {
+							parser->toksuper = i;
+							break;
+						}
+					}
+				}
+#endif
+			}
+			break;
+#ifdef JSMN_STRICT
+			/* In strict mode primitives are: numbers and booleans */
+		case '-': case '0': case '1' : case '2': case '3' : case '4':
+		case '5': case '6': case '7' : case '8': case '9':
+		case 't': case 'f': case 'n' :
+			/* And they must not be keys of the object */
+			if (tokens != NULL && parser->toksuper != -1) {
+				jsmntok_t *t = &tokens[parser->toksuper];
+				if (t->type == JSMN_OBJECT ||
+						(t->type == JSMN_STRING && t->size != 0)) {
+					return JSMN_ERROR_INVAL;
+				}
+			}
+#else
+			/* In non-strict mode every unquoted value is a primitive */
+		default:
+#endif
+			r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+			if (r < 0) return r;
+			count++;
+			if (parser->toksuper != -1 && tokens != NULL)
+				tokens[parser->toksuper].size++;
+			break;
+
+#ifdef JSMN_STRICT
+			/* Unexpected char in strict mode */
+		default:
+			return JSMN_ERROR_INVAL;
+#endif
+		}
+	}
+
+	if (tokens != NULL) {
+		for (i = parser->toknext - 1; i >= 0; i--) {
+			/* Unmatched opened object or array */
+			if (tokens[i].start != -1 && tokens[i].end == -1) {
+				return JSMN_ERROR_PART;
+			}
+		}
+	}
+
+	return count;
+}
+
+/**
+ * Creates a new parser based over a given  buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser) {
+	parser->pos = 0;
+	parser->toknext = 0;
+	parser->toksuper = -1;
+}
+/*
+ * -- jsmn.c end --
+ */
+
+#endif /* #ifdef CGLTF_IMPLEMENTATION */
+
+#ifdef __cplusplus
+}
+#endif

File diff suppressed because it is too large
+ 517 - 120
src/external/mini_al.h


+ 128 - 46
src/models.c

@@ -5,10 +5,10 @@
 *   CONFIGURATION:
 *   CONFIGURATION:
 *
 *
 *   #define SUPPORT_FILEFORMAT_OBJ
 *   #define SUPPORT_FILEFORMAT_OBJ
-*       Selected desired fileformats to be supported for loading.
-*
 *   #define SUPPORT_FILEFORMAT_MTL
 *   #define SUPPORT_FILEFORMAT_MTL
-*       Selected desired fileformats to be supported for loading.
+*   #define SUPPORT_FILEFORMAT_IQM
+*   #define SUPPORT_FILEFORMAT_GLTF
+*       Selected desired fileformats to be supported for model data loading.
 *
 *
 *   #define SUPPORT_MESH_GENERATION
 *   #define SUPPORT_MESH_GENERATION
 *       Support procedural mesh generation functions, uses external par_shapes.h library
 *       Support procedural mesh generation functions, uses external par_shapes.h library
@@ -48,8 +48,20 @@
 
 
 #include "rlgl.h"           // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
 #include "rlgl.h"           // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
 
 
-#define PAR_SHAPES_IMPLEMENTATION
-#include "external/par_shapes.h"    // Shapes 3d parametric generation
+#if defined(SUPPORT_FILEFORMAT_IQM)
+    #define RIQM_IMPLEMENTATION
+    #include "external/riqm.h"          // IQM file format loading
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+    #define CGLTF_IMPLEMENTATION
+    #include "external/cgltf.h"         // glTF file format loading
+#endif
+
+#if defined(SUPPORT_MESH_GENERATION)
+    #define PAR_SHAPES_IMPLEMENTATION
+    #include "external/par_shapes.h"    // Shapes 3d parametric generation
+#endif
 
 
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Defines and Macros
 // Defines and Macros
@@ -75,6 +87,12 @@ static Mesh LoadOBJ(const char *fileName);      // Load OBJ mesh data
 #if defined(SUPPORT_FILEFORMAT_MTL)
 #if defined(SUPPORT_FILEFORMAT_MTL)
 static Material LoadMTL(const char *fileName);  // Load MTL material data
 static Material LoadMTL(const char *fileName);  // Load MTL material data
 #endif
 #endif
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+static Mesh LoadIQM(const char *fileName);      // Load IQM mesh data
+#endif
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+static Mesh LoadGLTF(const char *fileName);     // Load GLTF mesh data
+#endif
 
 
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Module Functions Definition
 // Module Functions Definition
@@ -646,45 +664,54 @@ void UnloadMesh(Mesh *mesh)
     rlUnloadMesh(mesh);
     rlUnloadMesh(mesh);
 }
 }
 
 
-// Export mesh as an OBJ file
-void ExportMesh(const char *fileName, Mesh mesh)
+// Export mesh data to file
+void ExportMesh(Mesh mesh, const char *fileName)
 {
 {
-    FILE *objFile = fopen(fileName, "wt");
-    
-    fprintf(objFile, "# raylib Mesh OBJ exporter v1.0\n\n");
-    fprintf(objFile, "# Mesh exported as triangle faces and not optimized.\n");
-    fprintf(objFile, "#     Vertex Count:     %i\n", mesh.vertexCount);
-    fprintf(objFile, "#     Triangle Count:   %i\n\n", mesh.triangleCount);
-    fprintf(objFile, "# LICENSE: zlib/libpng\n");
-    fprintf(objFile, "# Copyright (c) 2018 Ramon Santamaria (@raysan5)\n\n");
-    
-    fprintf(objFile, "g mesh\n");
-    
-    for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
-    {
-        fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]);
-    }
-    
-    for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2)
-    {
-        fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]);
-    }
+    bool success = false;
     
     
-    for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
+    if (IsFileExtension(fileName, ".obj"))
     {
     {
-        fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
-    }
-    
-    for (int i = 0; i < mesh.triangleCount; i += 3)
-    {
-        fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2);
+        FILE *objFile = fopen(fileName, "wt");
+        
+        fprintf(objFile, "# raylib Mesh OBJ exporter v1.0\n\n");
+        fprintf(objFile, "# Mesh exported as triangle faces and not optimized.\n");
+        fprintf(objFile, "#     Vertex Count:     %i\n", mesh.vertexCount);
+        fprintf(objFile, "#     Triangle Count:   %i\n\n", mesh.triangleCount);
+        fprintf(objFile, "# LICENSE: zlib/libpng\n");
+        fprintf(objFile, "# Copyright (c) 2018 Ramon Santamaria (@raysan5)\n\n");
+        
+        fprintf(objFile, "g mesh\n");
+        
+        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
+        {
+            fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]);
+        }
+        
+        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2)
+        {
+            fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]);
+        }
+        
+        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
+        {
+            fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
+        }
+        
+        for (int i = 0; i < mesh.triangleCount; i += 3)
+        {
+            fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2);
+        }
+        
+        fprintf(objFile, "\n");
+        
+        fclose(objFile);
+        
+        success = true;
     }
     }
-    
-    fprintf(objFile, "\n");
-    
-    fclose(objFile);
+    else if (IsFileExtension(fileName, ".raw")) { }   // TODO: Support additional file formats to export mesh vertex data
 
 
-    TraceLog(LOG_INFO, "Mesh saved: %s", fileName);
+    if (success) TraceLog(LOG_INFO, "Mesh exported successfully: %s", fileName);
+    else TraceLog(LOG_WARNING, "Mesh could not be exported.");
 }
 }
 
 
 #if defined(SUPPORT_MESH_GENERATION)
 #if defined(SUPPORT_MESH_GENERATION)
@@ -699,7 +726,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
     resZ++;
     resZ++;
     
     
     // Vertices definition
     // Vertices definition
-    int vertexCount = resX*resZ*6;  // 6 vertex by quad
+    int vertexCount = resX*resZ; // vertices get reused for the faces
 
 
     Vector3 *vertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
     Vector3 *vertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
     for (int z = 0; z < resZ; z++)
     for (int z = 0; z < resZ; z++)
@@ -718,7 +745,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
     Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
     Vector3 *normals = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
     for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f };   // Vector3.up;
     for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f };   // Vector3.up;
 
 
-    // TexCoords definition		
+    // TexCoords definition        
     Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2));
     Vector2 *texcoords = (Vector2 *)malloc(vertexCount*sizeof(Vector2));
     for (int v = 0; v < resZ; v++)
     for (int v = 0; v < resZ; v++)
     {
     {
@@ -741,7 +768,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
         triangles[t++] = i + 1;
         triangles[t++] = i + 1;
         triangles[t++] = i;
         triangles[t++] = i;
 
 
-        triangles[t++] = i + resX;	
+        triangles[t++] = i + resX;    
         triangles[t++] = i + resX + 1;
         triangles[t++] = i + resX + 1;
         triangles[t++] = i + 1;
         triangles[t++] = i + 1;
     }
     }
@@ -2206,9 +2233,8 @@ void MeshBinormals(Mesh *mesh)
         Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
         Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
         float tangentW = mesh->tangents[i*4 + 3];
         float tangentW = mesh->tangents[i*4 + 3];
     
     
-        
         // TODO: Register computed binormal in mesh->binormal ?
         // TODO: Register computed binormal in mesh->binormal ?
-        // Vector3 binormal = Vector3Multiply( Vector3CrossProduct( normal, tangent ), tangentW );
+        // Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW);
     }
     }
 }
 }
 
 
@@ -2222,7 +2248,7 @@ static Mesh LoadOBJ(const char *fileName)
 {
 {
     Mesh mesh = { 0 };
     Mesh mesh = { 0 };
 
 
-    char dataType;
+    char dataType = 0;
     char comments[200];
     char comments[200];
 
 
     int vertexCount = 0;
     int vertexCount = 0;
@@ -2245,7 +2271,7 @@ static Mesh LoadOBJ(const char *fileName)
     // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face)
     // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face)
     while (!feof(objFile))
     while (!feof(objFile))
     {
     {
-        dataType = '\0';
+        dataType = 0;
         fscanf(objFile, "%c", &dataType);
         fscanf(objFile, "%c", &dataType);
 
 
         switch (dataType)
         switch (dataType)
@@ -2631,3 +2657,59 @@ static Material LoadMTL(const char *fileName)
     return material;
     return material;
 }
 }
 #endif
 #endif
+
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+// Load IQM mesh data
+static Mesh LoadIQM(const char *fileName)
+{
+    Mesh mesh = { 0 };
+    
+    // TODO: Load IQM file
+    
+    return mesh;
+} 
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+// Load GLTF mesh data
+static Mesh LoadGLTF(const char *fileName)
+{
+    Mesh mesh = { 0 };
+    
+    // GLTF file loading
+    FILE *gltfFile = fopen(fileName, "rb");
+    
+    if (gltfFile == NULL)
+    {
+        TraceLog(LOG_WARNING, "[%s] GLTF file could not be opened", fileName);
+        return mesh;
+    }
+
+    fseek(gltfFile, 0, SEEK_END);
+    int size = ftell(gltfFile);
+    fseek(gltfFile, 0, SEEK_SET);
+
+    void *buffer = malloc(size);
+    fread(buffer, size, 1, gltfFile);
+    
+    fclose(gltfFile);
+
+    // GLTF data loading
+    cgltf_options options = {0};
+    cgltf_data data;
+    cgltf_result result = cgltf_parse(&options, buffer, size, &data);
+
+    if (result == cgltf_result_success)
+    {
+        printf("Type: %u\n", data.file_type);
+        printf("Version: %d\n", data.version);
+        printf("Meshes: %lu\n", data.meshes_count);
+    }
+    else TraceLog(LOG_WARNING, "[%s] GLTF data could not be loaded", fileName);
+
+    free(buffer);
+    cgltf_free(&data);
+    
+    return mesh; 
+}
+#endif

+ 13 - 4
src/raylib.h

@@ -1,6 +1,6 @@
 /**********************************************************************************************
 /**********************************************************************************************
 *
 *
-*   raylib - A simple and easy-to-use library to learn videogames programming (www.raylib.com)
+*   raylib - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
 *
 *
 *   FEATURES:
 *   FEATURES:
 *       - NO external dependencies, all required libraries included with raylib
 *       - NO external dependencies, all required libraries included with raylib
@@ -442,6 +442,7 @@ typedef struct RenderTexture2D {
 // RenderTexture type, same as RenderTexture2D
 // RenderTexture type, same as RenderTexture2D
 typedef RenderTexture2D RenderTexture;
 typedef RenderTexture2D RenderTexture;
 
 
+// N-Patch layout info
 typedef struct NPatchInfo {
 typedef struct NPatchInfo {
     Rectangle sourceRec;   // Region in the texture
     Rectangle sourceRec;   // Region in the texture
     int left;              // left border offset
     int left;              // left border offset
@@ -720,6 +721,13 @@ typedef enum {
     WRAP_MIRROR
     WRAP_MIRROR
 } TextureWrapMode;
 } TextureWrapMode;
 
 
+// Font type, defines generation method
+typedef enum {
+    FONT_DEFAULT = 0,   // Default font generation, anti-aliased
+    FONT_BITMAP,        // Bitmap font generation, no anti-aliasing
+    FONT_SDF            // SDF font generation, requires external shader
+} FontType;
+
 // Color blending modes (pre-defined)
 // Color blending modes (pre-defined)
 typedef enum {
 typedef enum {
     BLEND_ALPHA = 0,
     BLEND_ALPHA = 0,
@@ -980,7 +988,7 @@ RLAPI Image LoadImage(const char *fileName);
 RLAPI Image LoadImageEx(Color *pixels, int width, int height);                                           // Load image from Color array data (RGBA - 32bit)
 RLAPI Image LoadImageEx(Color *pixels, int width, int height);                                           // Load image from Color array data (RGBA - 32bit)
 RLAPI Image LoadImagePro(void *data, int width, int height, int format);                                 // Load image from raw data with parameters
 RLAPI Image LoadImagePro(void *data, int width, int height, int format);                                 // Load image from raw data with parameters
 RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize);       // Load image from RAW file data
 RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize);       // Load image from RAW file data
-RLAPI void ExportImage(const char *fileName, Image image);                                               // Export image as a PNG file
+RLAPI void ExportImage(Image image, const char *fileName);                                               // Export image data to file
 RLAPI Texture2D LoadTexture(const char *fileName);                                                       // Load texture from file into GPU memory (VRAM)
 RLAPI Texture2D LoadTexture(const char *fileName);                                                       // Load texture from file into GPU memory (VRAM)
 RLAPI Texture2D LoadTextureFromImage(Image image);                                                       // Load texture from image data
 RLAPI Texture2D LoadTextureFromImage(Image image);                                                       // Load texture from image data
 RLAPI RenderTexture2D LoadRenderTexture(int width, int height);                                          // Load texture for rendering (framebuffer)
 RLAPI RenderTexture2D LoadRenderTexture(int width, int height);                                          // Load texture for rendering (framebuffer)
@@ -1055,7 +1063,7 @@ RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle
 RLAPI Font GetFontDefault(void);                                                            // Get the default Font
 RLAPI Font GetFontDefault(void);                                                            // Get the default Font
 RLAPI Font LoadFont(const char *fileName);                                                  // Load font from file into GPU memory (VRAM)
 RLAPI Font LoadFont(const char *fileName);                                                  // Load font from file into GPU memory (VRAM)
 RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars);  // Load font from file with extended parameters
 RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars);  // Load font from file with extended parameters
-RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, bool sdf); // Load font data for further use
+RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use
 RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod);  // Generate image font atlas using chars info
 RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod);  // Generate image font atlas using chars info
 RLAPI void UnloadFont(Font font);                                                           // Unload Font from GPU memory (VRAM)
 RLAPI void UnloadFont(Font font);                                                           // Unload Font from GPU memory (VRAM)
 
 
@@ -1105,7 +1113,7 @@ RLAPI void UnloadModel(Model model);
 // Mesh loading/unloading functions
 // Mesh loading/unloading functions
 RLAPI Mesh LoadMesh(const char *fileName);                                                              // Load mesh from file
 RLAPI Mesh LoadMesh(const char *fileName);                                                              // Load mesh from file
 RLAPI void UnloadMesh(Mesh *mesh);                                                                      // Unload mesh from memory (RAM and/or VRAM)
 RLAPI void UnloadMesh(Mesh *mesh);                                                                      // Unload mesh from memory (RAM and/or VRAM)
-RLAPI void ExportMesh(const char *fileName, Mesh mesh);                                                 // Export mesh as an OBJ file
+RLAPI void ExportMesh(Mesh mesh, const char *fileName);                                                 // Export mesh data to file
 
 
 // Mesh manipulation functions
 // Mesh manipulation functions
 RLAPI BoundingBox MeshBoundingBox(Mesh mesh);                                                           // Compute mesh bounding box limits
 RLAPI BoundingBox MeshBoundingBox(Mesh mesh);                                                           // Compute mesh bounding box limits
@@ -1213,6 +1221,7 @@ RLAPI Sound LoadSoundFromWave(Wave wave);                             // Load so
 RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data
 RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Update sound buffer with new data
 RLAPI void UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
+RLAPI void ExportWave(Wave wave, const char *fileName);               // Export wave data to file
 
 
 // Wave/Sound management functions
 // Wave/Sound management functions
 RLAPI void PlaySound(Sound sound);                                    // Play a sound
 RLAPI void PlaySound(Sound sound);                                    // Play a sound

+ 28 - 0
src/raymath.h

@@ -229,6 +229,13 @@ RMDEF Vector2 Vector2Scale(Vector2 v, float scale)
     return result;
     return result;
 }
 }
 
 
+// Multiply vector by vector
+RMDEF Vector2 Vector2MultiplyV(Vector2 v1, Vector2 v2)
+{
+	Vector2 result = { v1.x*v2.x, v1.y*v2.y };
+	return result;
+}
+
 // Negate vector
 // Negate vector
 RMDEF Vector2 Vector2Negate(Vector2 v)
 RMDEF Vector2 Vector2Negate(Vector2 v)
 {
 {
@@ -243,6 +250,13 @@ RMDEF Vector2 Vector2Divide(Vector2 v, float div)
     return result;
     return result;
 }
 }
 
 
+// Divide vector by vector
+RMDEF Vector2 Vector2DivideV(Vector2 v1, Vector2 v2)
+{
+	Vector2 result = { v1.x/v2.x, v1.y/v2.y };
+	return result;
+}
+
 // Normalize provided vector
 // Normalize provided vector
 RMDEF Vector2 Vector2Normalize(Vector2 v)
 RMDEF Vector2 Vector2Normalize(Vector2 v)
 {
 {
@@ -378,6 +392,20 @@ RMDEF Vector3 Vector3Negate(Vector3 v)
     return result;
     return result;
 }
 }
 
 
+// Divide vector by a float value
+RMDEF Vector3 Vector3Divide(Vector3 v, float div)
+{
+	Vector3 result = { v.x / div, v.y / div, v.z / div };
+	return result;
+}
+
+// Divide vector by vector
+RMDEF Vector3 Vector3DivideV(Vector3 v1, Vector3 v2)
+{
+	Vector3 result = { v1.x/v2.x, v1.y/v2.y, v1.z/v2.z };
+	return result;
+}
+
 // Normalize provided vector
 // Normalize provided vector
 RMDEF Vector3 Vector3Normalize(Vector3 v)
 RMDEF Vector3 Vector3Normalize(Vector3 v)
 {
 {

+ 27 - 19
src/text.c

@@ -86,9 +86,6 @@ static Font LoadImageFont(Image image, Color key, int firstChar); // Load a Imag
 #if defined(SUPPORT_FILEFORMAT_FNT)
 #if defined(SUPPORT_FILEFORMAT_FNT)
 static Font LoadBMFont(const char *fileName);     // Load a BMFont file (AngelCode font file)
 static Font LoadBMFont(const char *fileName);     // Load a BMFont file (AngelCode font file)
 #endif
 #endif
-#if defined(SUPPORT_FILEFORMAT_TTF)
-//static Font LoadTTF(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load spritefont from TTF data
-#endif
 
 
 #if defined(SUPPORT_DEFAULT_FONT)
 #if defined(SUPPORT_DEFAULT_FONT)
 extern void LoadDefaultFont(void);
 extern void LoadDefaultFont(void);
@@ -221,7 +218,7 @@ extern void LoadDefaultFont(void)
         defaultFont.chars[i].value = 32 + i;  // First char is 32
         defaultFont.chars[i].value = 32 + i;  // First char is 32
 
 
         defaultFont.chars[i].rec.x = (float)currentPosX;
         defaultFont.chars[i].rec.x = (float)currentPosX;
-        defaultFont.chars[i].rec.y = (float)charsDivisor + currentLine*(charsHeight + charsDivisor);
+        defaultFont.chars[i].rec.y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor));
         defaultFont.chars[i].rec.width = (float)charsWidth[i];
         defaultFont.chars[i].rec.width = (float)charsWidth[i];
         defaultFont.chars[i].rec.height = (float)charsHeight;
         defaultFont.chars[i].rec.height = (float)charsHeight;
 
 
@@ -234,7 +231,7 @@ extern void LoadDefaultFont(void)
             testPosX = currentPosX;
             testPosX = currentPosX;
 
 
             defaultFont.chars[i].rec.x = (float)charsDivisor;
             defaultFont.chars[i].rec.x = (float)charsDivisor;
-            defaultFont.chars[i].rec.y = (float)charsDivisor + currentLine*(charsHeight + charsDivisor);
+            defaultFont.chars[i].rec.y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor));
         }
         }
         else currentPosX = testPosX;
         else currentPosX = testPosX;
 
 
@@ -244,7 +241,7 @@ extern void LoadDefaultFont(void)
         defaultFont.chars[i].advanceX = 0;
         defaultFont.chars[i].advanceX = 0;
     }
     }
 
 
-    defaultFont.baseSize = (int) defaultFont.chars[0].rec.height;
+    defaultFont.baseSize = (int)defaultFont.chars[0].rec.height;
     
     
     TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id);
     TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id);
 }
 }
@@ -283,7 +280,7 @@ Font LoadFont(const char *fileName)
     {
     {
         font.baseSize = DEFAULT_TTF_FONTSIZE;
         font.baseSize = DEFAULT_TTF_FONTSIZE;
         font.charsCount = DEFAULT_TTF_NUMCHARS;
         font.charsCount = DEFAULT_TTF_NUMCHARS;
-        font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, false);
+        font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, FONT_DEFAULT);
         Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0);
         Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0);
         font.texture = LoadTextureFromImage(atlas);
         font.texture = LoadTextureFromImage(atlas);
         UnloadImage(atlas);
         UnloadImage(atlas);
@@ -319,8 +316,8 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
     
     
     font.baseSize = fontSize;
     font.baseSize = fontSize;
     font.charsCount = (charsCount > 0) ? charsCount : 95;
     font.charsCount = (charsCount > 0) ? charsCount : 95;
-    font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, false);
-    Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 0, 0);
+    font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
+    Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
     font.texture = LoadTextureFromImage(atlas);
     font.texture = LoadTextureFromImage(atlas);
     UnloadImage(atlas);
     UnloadImage(atlas);
     
     
@@ -329,7 +326,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
 
 
 // Load font data for further use
 // Load font data for further use
 // NOTE: Requires TTF font and can generate SDF data
 // NOTE: Requires TTF font and can generate SDF data
-CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, bool sdf)
+CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type)
 {
 {
     // NOTE: Using some SDF generation default values,
     // NOTE: Using some SDF generation default values,
     // trades off precision with ability to handle *smaller* sizes
     // trades off precision with ability to handle *smaller* sizes
@@ -337,6 +334,8 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
     #define SDF_ON_EDGE_VALUE         128
     #define SDF_ON_EDGE_VALUE         128
     #define SDF_PIXEL_DIST_SCALE     64.0f
     #define SDF_PIXEL_DIST_SCALE     64.0f
     
     
+    #define BITMAP_ALPHA_THRESHOLD     80
+    
     // In case no chars count provided, default to 95
     // In case no chars count provided, default to 95
     charsCount = (charsCount > 0) ? charsCount : 95;
     charsCount = (charsCount > 0) ? charsCount : 95;
     
     
@@ -367,8 +366,6 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
     // NOTE: ascent is equivalent to font baseline
     // NOTE: ascent is equivalent to font baseline
     int ascent, descent, lineGap;
     int ascent, descent, lineGap;
     stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
     stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
-    ascent *= (int) scaleFactor;
-    descent *= (int) scaleFactor;
     
     
     // Fill fontChars in case not provided externally
     // Fill fontChars in case not provided externally
     // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
     // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
@@ -392,22 +389,33 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
         //      stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be
         //      stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be
         //      stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide
         //      stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide
         
         
-        if (!sdf) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
+        if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
         else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
         else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
         
         
+        if (type == FONT_BITMAP)
+        {
+            // Aliased bitmap (black & white) font generation, avoiding anti-aliasing
+            // NOTE: For optimum results, bitmap font should be generated at base pixel size
+            for (int p = 0; p < chw*chh; p++)
+            {
+                if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0;
+                else chars[i].data[p] = 255;
+            }
+        }
+        
         chars[i].rec.width = (float)chw;
         chars[i].rec.width = (float)chw;
         chars[i].rec.height = (float)chh;
         chars[i].rec.height = (float)chh;
-        chars[i].offsetY += ascent;
+        chars[i].offsetY += (int)((float)ascent*scaleFactor);
     
     
         // Get bounding box for character (may be offset to account for chars that dip above or below the line)
         // Get bounding box for character (may be offset to account for chars that dip above or below the line)
         int chX1, chY1, chX2, chY2;
         int chX1, chY1, chX2, chY2;
         stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
         stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
         
         
         TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
         TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
-        TraceLog(LOG_DEBUG, "Character offsetY: %i", ascent + chY1);
+        TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
 
 
         stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
         stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
-        chars[i].advanceX *= (int) scaleFactor;
+        chars[i].advanceX *= scaleFactor;
     }
     }
     
     
     free(fontBuffer);
     free(fontBuffer);
@@ -660,11 +668,11 @@ const char *SubText(const char *text, int position, int length)
 
 
     for (int c = 0 ; c < length ; c++)
     for (int c = 0 ; c < length ; c++)
     {
     {
-        *(buffer+c) = *(text+position);
+        *(buffer + c) = *(text + position);
         text++;
         text++;
     }
     }
 
 
-    *(buffer+length) = '\0';
+    *(buffer + length) = '\0';
 
 
     return buffer;
     return buffer;
 }
 }
@@ -887,7 +895,7 @@ static Font LoadImageFont(Image image, Color key, int firstChar)
         spriteFont.chars[i].advanceX = 0;
         spriteFont.chars[i].advanceX = 0;
     }
     }
 
 
-    spriteFont.baseSize = (int) spriteFont.chars[0].rec.height;
+    spriteFont.baseSize = (int)spriteFont.chars[0].rec.height;
 
 
     TraceLog(LOG_INFO, "Image file loaded correctly as Font");
     TraceLog(LOG_INFO, "Image file loaded correctly as Font");
 
 

+ 38 - 7
src/textures.c

@@ -19,6 +19,9 @@
 *       Selecte desired fileformats to be supported for image data loading. Some of those formats are
 *       Selecte desired fileformats to be supported for image data loading. Some of those formats are
 *       supported by default, to remove support, just comment unrequired #define in this module
 *       supported by default, to remove support, just comment unrequired #define in this module
 *
 *
+*   #define SUPPORT_IMAGE_EXPORT
+*       Support image export in multiple file formats
+*
 *   #define SUPPORT_IMAGE_MANIPULATION
 *   #define SUPPORT_IMAGE_MANIPULATION
 *       Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
 *       Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
 *       If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
 *       If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
@@ -103,6 +106,11 @@
                                         // NOTE: Used to read image data (multiple formats support)
                                         // NOTE: Used to read image data (multiple formats support)
 #endif
 #endif
 
 
+#if defined(SUPPORT_IMAGE_EXPORT)
+    #define STB_IMAGE_WRITE_IMPLEMENTATION
+    #include "external/stb_image_write.h"   // Required for: stbi_write_*()
+#endif
+
 #if defined(SUPPORT_IMAGE_MANIPULATION)
 #if defined(SUPPORT_IMAGE_MANIPULATION)
     #define STB_IMAGE_RESIZE_IMPLEMENTATION
     #define STB_IMAGE_RESIZE_IMPLEMENTATION
     #include "external/stb_image_resize.h"  // Required for: stbir_resize_uint8()
     #include "external/stb_image_resize.h"  // Required for: stbir_resize_uint8()
@@ -706,15 +714,32 @@ void UpdateTexture(Texture2D texture, const void *pixels)
     rlUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels);
     rlUpdateTexture(texture.id, texture.width, texture.height, texture.format, pixels);
 }
 }
 
 
-// Export image as a PNG file
-void ExportImage(const char *fileName, Image image)
+// Export image data to file
+// NOTE: File format depends on fileName extension
+void ExportImage(Image image, const char *fileName)
 {
 {
+    int success = 0;
+    
     // NOTE: Getting Color array as RGBA unsigned char values
     // NOTE: Getting Color array as RGBA unsigned char values
     unsigned char *imgData = (unsigned char *)GetImageData(image);
     unsigned char *imgData = (unsigned char *)GetImageData(image);
     
     
-    // NOTE: SavePNG() not supported by some platforms: PLATFORM_WEB, PLATFORM_ANDROID
-    SavePNG(fileName, imgData, image.width, image.height, 4);
-
+    if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4);
+    else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
+    else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
+    else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80);  // Between 1 and 100
+    else if (IsFileExtension(fileName, ".raw")) 
+    {
+        // Export raw pixel data
+        // NOTE: It's up to the user to track image parameters
+        FILE *rawFile = fopen(fileName, "wb");
+        fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile);
+        fclose(rawFile);
+    }
+    else if (IsFileExtension(fileName, ".h")) { }    // TODO: Export pixel data as an array of bytes
+    
+    if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
+    else TraceLog(LOG_WARNING, "Image could not be exported.");
+    
     free(imgData);
     free(imgData);
 }
 }
 
 
@@ -1555,7 +1580,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
     // Define ImageFont struct? or include Image spritefont in Font struct?
     // Define ImageFont struct? or include Image spritefont in Font struct?
     Image imFont = GetTextureData(font.texture);
     Image imFont = GetTextureData(font.texture);
     
     
-    ImageColorTint(&imFont, tint);                    // Apply color tint to font
+    ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8);    // Make sure image format could be properly colored!
+    
+    ImageColorTint(&imFont, tint);                  // Apply color tint to font
 
 
     // Create image to store text
     // Create image to store text
     Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
     Image imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
@@ -2827,7 +2854,11 @@ static Image LoadKTX(const char *fileName)
     // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
     // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
 
 
     // KTX file Header (64 bytes)
     // KTX file Header (64 bytes)
-    // https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+    // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+    // v2.0 - http://github.khronos.org/KTX-Specification/
+    
+    // TODO: Support KTX 2.2 specs!
+    
     typedef struct {
     typedef struct {
         char id[12];                        // Identifier: "«KTX 11»\r\n\x1A\n"
         char id[12];                        // Identifier: "«KTX 11»\r\n\x1A\n"
         unsigned int endianness;            // Little endian: 0x01 0x02 0x03 0x04
         unsigned int endianness;            // Little endian: 0x01 0x02 0x03 0x04

+ 0 - 35
src/utils.c

@@ -4,21 +4,10 @@
 *
 *
 *   CONFIGURATION:
 *   CONFIGURATION:
 *
 *
-*   #define SUPPORT_SAVE_PNG (defined by default)
-*       Support saving image data as PNG fileformat
-*       NOTE: Requires stb_image_write library
-*
-*   #define SUPPORT_SAVE_BMP
-*       Support saving image data as BMP fileformat
-*       NOTE: Requires stb_image_write library
-*
 *   #define SUPPORT_TRACELOG
 *   #define SUPPORT_TRACELOG
 *       Show TraceLog() output messages
 *       Show TraceLog() output messages
 *       NOTE: By default LOG_DEBUG traces not shown
 *       NOTE: By default LOG_DEBUG traces not shown
 *
 *
-*   DEPENDENCIES:
-*       stb_image_write - BMP/PNG writting functions
-*
 *
 *
 *   LICENSE: zlib/libpng
 *   LICENSE: zlib/libpng
 *
 *
@@ -62,12 +51,6 @@ FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int),
               int (*writefn)(void *, const char *, int),
               int (*writefn)(void *, const char *, int),
               fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
               fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
 
 
-
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
-    #define STB_IMAGE_WRITE_IMPLEMENTATION
-    #include "external/stb_image_write.h"   // Required for: stbi_write_bmp(), stbi_write_png()
-#endif
-
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 // Global Variables Definition
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
@@ -160,24 +143,6 @@ void TraceLog(int msgType, const char *text, ...)
 #endif  // SUPPORT_TRACELOG
 #endif  // SUPPORT_TRACELOG
 }
 }
 
 
-// Creates a BMP image file from an array of pixel data
-void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize)
-{
-#if defined(SUPPORT_SAVE_BMP) && (defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI))
-    stbi_write_bmp(fileName, width, height, compSize, imgData);
-    TraceLog(LOG_INFO, "BMP Image saved: %s", fileName);
-#endif
-}
-
-// Creates a PNG image file from an array of pixel data
-void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize)
-{
-#if defined(SUPPORT_SAVE_PNG) && (defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI))
-    stbi_write_png(fileName, width, height, compSize, imgData, width*compSize);
-    TraceLog(LOG_INFO, "PNG Image saved: %s", fileName);
-#endif
-}
-
 // Keep track of memory allocated
 // Keep track of memory allocated
 // NOTE: mallocType defines the type of data allocated
 // NOTE: mallocType defines the type of data allocated
 /*
 /*

+ 0 - 11
src/utils.h

@@ -32,10 +32,6 @@
     #include <android/asset_manager.h>      // Required for: AAssetManager
     #include <android/asset_manager.h>      // Required for: AAssetManager
 #endif
 #endif
 
 
-#ifndef SUPPORT_SAVE_PNG
-#define SUPPORT_SAVE_PNG 1
-#endif
-
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Some basic Defines
 // Some basic Defines
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
@@ -58,13 +54,6 @@ extern "C" {            // Prevents name mangling of functions
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Module Functions Declaration
 // Module Functions Declaration
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
-#if defined(SUPPORT_SAVE_BMP)
-void SaveBMP(const char *fileName, unsigned char *imgData, int width, int height, int compSize);
-#endif
-#if defined(SUPPORT_SAVE_PNG)
-void SavePNG(const char *fileName, unsigned char *imgData, int width, int height, int compSize);
-#endif
-
 #if defined(PLATFORM_ANDROID)
 #if defined(PLATFORM_ANDROID)
 void InitAssetManager(AAssetManager *manager);  // Initialize asset manager from android app
 void InitAssetManager(AAssetManager *manager);  // Initialize asset manager from android app
 FILE *android_fopen(const char *fileName, const char *mode);    // Replacement for fopen()
 FILE *android_fopen(const char *fileName, const char *mode);    // Replacement for fopen()

Some files were not shown because too many files changed in this diff