Bläddra i källkod

Merge pull request #5 from raysan5/master

merge
Murray Campbell 7 år sedan
förälder
incheckning
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 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 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.

+ 1 - 1
HELPME.md

@@ -18,7 +18,7 @@ with a small [donation](http://www.raylib.com/helpme.html) or contributing with
 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 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.

+ 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>
 
-**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 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 
 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.charsCount = 95;
     // 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)
     Image atlas = GenImageFontAtlas(fontDefault.chars, 95, 16, 4, 0);
     fontDefault.texture = LoadTextureFromImage(atlas);
     UnloadImage(atlas);
     
     // SDF font generation from TTF font
-    // NOTE: SDF chars data is generated with LoadFontData(), it's just a bool option
     Font fontSDF = { 0 };
     fontSDF.baseSize = 16;
     fontSDF.charsCount = 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)
     atlas = GenImageFontAtlas(fontSDF.chars, 95, 16, 0, 1);
     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)
 
     // 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)
     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;
 
 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 logoPositionY;

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

@@ -37,7 +37,7 @@ static int framesCounter;
 static int finishScreen;
 
 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 logoPositionY;

+ 1 - 1
raylib.pc.in

@@ -4,7 +4,7 @@ libdir=${exec_prefix}/lib
 includedir=${prefix}/include
 
 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
 Version: @PROJECT_VERSION@
 Libs: -L${libdir} -lraylib @PKG_CONFIG_LIBS_EXTRA@

+ 1 - 1
src/CMakeLists.txt

@@ -259,7 +259,7 @@ message(STATUS "  GRAPHICS=" ${GRAPHICS})
 
 # Packaging
 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_MAJOR "${PROJECT_VERSION_MAJOR}")
 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)
 
 # 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_FILEFORMAT_PNG "Support loading PNG 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})
 
 # 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_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)
 static Wave LoadFLAC(const char *fileName);         // Load FLAC file
 #endif
+#if defined(SUPPORT_FILEFORMAT_MP3)
+static Wave LoadMP3(const char *fileName);          // Load MP3 file
+#endif
 
 #if defined(AUDIO_STANDALONE)
 bool IsFileExtension(const char *fileName, const char *ext);    // Check file extension
@@ -857,6 +860,9 @@ Wave LoadWave(const char *fileName)
 #endif
 #if defined(SUPPORT_FILEFORMAT_FLAC)
     else if (IsFileExtension(fileName, ".flac")) wave = LoadFLAC(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_MP3)
+    else if (IsFileExtension(fileName, ".mp3")) wave = LoadMP3(fileName);
 #endif
     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
 }
 
+// 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
 void PlaySound(Sound sound)
 {
@@ -1565,6 +1654,9 @@ void StopMusicStream(Music music)
 #if defined(SUPPORT_FILEFORMAT_FLAC)
         case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
 #endif
+#if defined(SUPPORT_FILEFORMAT_MP3)
+        case MUSIC_AUDIO_MP3: /* TODO: Restart MP3 context */ break;
+#endif
 #if defined(SUPPORT_FILEFORMAT_XM)
         case MUSIC_MODULE_XM: /* TODO: Restart XM context */ break;
 #endif
@@ -1705,6 +1797,13 @@ void UpdateMusicStream(Music music)
 
                 } break;
             #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)
                 case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break;
             #endif
@@ -2294,6 +2393,33 @@ static Wave LoadFLAC(const char *fileName)
 }
 #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
 #if defined(AUDIO_STANDALONE)
 // Check file extension

+ 2 - 4
src/config.h

@@ -86,6 +86,8 @@
 //#define SUPPORT_FILEFORMAT_PKM    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...
 // If not defined only three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
 #define SUPPORT_IMAGE_MANIPULATION  1
@@ -133,10 +135,6 @@
 // Show TraceLog() output messages
 // NOTE: By default LOG_DEBUG traces not shown
 #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)

+ 3 - 0
src/config.h.in

@@ -41,6 +41,9 @@
 #cmakedefine SUPPORT_FILEFORMAT_PKM 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() */
 #cmakedefine SUPPORT_IMAGE_MANIPULATION 1
 

+ 4 - 2
src/core.c

@@ -886,7 +886,7 @@ void EndDrawing(void)
     // Wait for some milliseconds...
     if (frameTime < targetTime)
     {
-        Wait( (float)(targetTime - frameTime)*1000.0f);
+        Wait((float)(targetTime - frameTime)*1000.0f);
 
         currentTime = GetTime();
         double extraTime = currentTime - previousTime;
@@ -1297,7 +1297,9 @@ void TakeScreenshot(const char *fileName)
 {
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI)
     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);
 
     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

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 517 - 120
src/external/mini_al.h


+ 128 - 46
src/models.c

@@ -5,10 +5,10 @@
 *   CONFIGURATION:
 *
 *   #define SUPPORT_FILEFORMAT_OBJ
-*       Selected desired fileformats to be supported for loading.
-*
 *   #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
 *       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
 
-#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
@@ -75,6 +87,12 @@ static Mesh LoadOBJ(const char *fileName);      // Load OBJ mesh data
 #if defined(SUPPORT_FILEFORMAT_MTL)
 static Material LoadMTL(const char *fileName);  // Load MTL material data
 #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
@@ -646,45 +664,54 @@ void UnloadMesh(Mesh *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)
@@ -699,7 +726,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ)
     resZ++;
     
     // 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));
     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));
     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));
     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;
 
-        triangles[t++] = i + resX;	
+        triangles[t++] = i + resX;    
         triangles[t++] = i + resX + 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] };
         float tangentW = mesh->tangents[i*4 + 3];
     
-        
         // 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 };
 
-    char dataType;
+    char dataType = 0;
     char comments[200];
 
     int vertexCount = 0;
@@ -2245,7 +2271,7 @@ static Mesh LoadOBJ(const char *fileName)
     // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face)
     while (!feof(objFile))
     {
-        dataType = '\0';
+        dataType = 0;
         fscanf(objFile, "%c", &dataType);
 
         switch (dataType)
@@ -2631,3 +2657,59 @@ static Material LoadMTL(const char *fileName)
     return material;
 }
 #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:
 *       - NO external dependencies, all required libraries included with raylib
@@ -442,6 +442,7 @@ typedef struct RenderTexture2D {
 // RenderTexture type, same as RenderTexture2D
 typedef RenderTexture2D RenderTexture;
 
+// N-Patch layout info
 typedef struct NPatchInfo {
     Rectangle sourceRec;   // Region in the texture
     int left;              // left border offset
@@ -720,6 +721,13 @@ typedef enum {
     WRAP_MIRROR
 } 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)
 typedef enum {
     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 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 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 LoadTextureFromImage(Image image);                                                       // Load texture from image data
 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 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 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 void UnloadFont(Font font);                                                           // Unload Font from GPU memory (VRAM)
 
@@ -1105,7 +1113,7 @@ RLAPI void UnloadModel(Model model);
 // Mesh loading/unloading functions
 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 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
 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 UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
+RLAPI void ExportWave(Wave wave, const char *fileName);               // Export wave data to file
 
 // Wave/Sound management functions
 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;
 }
 
+// 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
 RMDEF Vector2 Vector2Negate(Vector2 v)
 {
@@ -243,6 +250,13 @@ RMDEF Vector2 Vector2Divide(Vector2 v, float div)
     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
 RMDEF Vector2 Vector2Normalize(Vector2 v)
 {
@@ -378,6 +392,20 @@ RMDEF Vector3 Vector3Negate(Vector3 v)
     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
 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)
 static Font LoadBMFont(const char *fileName);     // Load a BMFont file (AngelCode font file)
 #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)
 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].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.height = (float)charsHeight;
 
@@ -234,7 +231,7 @@ extern void LoadDefaultFont(void)
             testPosX = currentPosX;
 
             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;
 
@@ -244,7 +241,7 @@ extern void LoadDefaultFont(void)
         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);
 }
@@ -283,7 +280,7 @@ Font LoadFont(const char *fileName)
     {
         font.baseSize = DEFAULT_TTF_FONTSIZE;
         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);
         font.texture = LoadTextureFromImage(atlas);
         UnloadImage(atlas);
@@ -319,8 +316,8 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
     
     font.baseSize = fontSize;
     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);
     UnloadImage(atlas);
     
@@ -329,7 +326,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
 
 // Load font data for further use
 // 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,
     // 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_PIXEL_DIST_SCALE     64.0f
     
+    #define BITMAP_ALPHA_THRESHOLD     80
+    
     // In case no chars count provided, default to 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
     int ascent, descent, lineGap;
     stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
-    ascent *= (int) scaleFactor;
-    descent *= (int) scaleFactor;
     
     // Fill fontChars in case not provided externally
     // 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_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);
         
+        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.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)
         int 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 offsetY: %i", ascent + chY1);
+        TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
 
         stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
-        chars[i].advanceX *= (int) scaleFactor;
+        chars[i].advanceX *= scaleFactor;
     }
     
     free(fontBuffer);
@@ -660,11 +668,11 @@ const char *SubText(const char *text, int position, int length)
 
     for (int c = 0 ; c < length ; c++)
     {
-        *(buffer+c) = *(text+position);
+        *(buffer + c) = *(text + position);
         text++;
     }
 
-    *(buffer+length) = '\0';
+    *(buffer + length) = '\0';
 
     return buffer;
 }
@@ -887,7 +895,7 @@ static Font LoadImageFont(Image image, Color key, int firstChar)
         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");
 

+ 38 - 7
src/textures.c

@@ -19,6 +19,9 @@
 *       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
 *
+*   #define SUPPORT_IMAGE_EXPORT
+*       Support image export in multiple file formats
+*
 *   #define SUPPORT_IMAGE_MANIPULATION
 *       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()
@@ -103,6 +106,11 @@
                                         // NOTE: Used to read image data (multiple formats support)
 #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)
     #define STB_IMAGE_RESIZE_IMPLEMENTATION
     #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);
 }
 
-// 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
     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);
 }
 
@@ -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?
     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
     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
 
     // 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 {
         char id[12];                        // Identifier: "«KTX 11»\r\n\x1A\n"
         unsigned int endianness;            // Little endian: 0x01 0x02 0x03 0x04

+ 0 - 35
src/utils.c

@@ -4,21 +4,10 @@
 *
 *   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
 *       Show TraceLog() output messages
 *       NOTE: By default LOG_DEBUG traces not shown
 *
-*   DEPENDENCIES:
-*       stb_image_write - BMP/PNG writting functions
-*
 *
 *   LICENSE: zlib/libpng
 *
@@ -62,12 +51,6 @@ FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int),
               int (*writefn)(void *, const char *, int),
               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
 //----------------------------------------------------------------------------------
@@ -160,24 +143,6 @@ void TraceLog(int msgType, const char *text, ...)
 #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
 // 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
 #endif
 
-#ifndef SUPPORT_SAVE_PNG
-#define SUPPORT_SAVE_PNG 1
-#endif
-
 //----------------------------------------------------------------------------------
 // Some basic Defines
 //----------------------------------------------------------------------------------
@@ -58,13 +54,6 @@ extern "C" {            // Prevents name mangling of functions
 //----------------------------------------------------------------------------------
 // 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)
 void InitAssetManager(AAssetManager *manager);  // Initialize asset manager from android app
 FILE *android_fopen(const char *fileName, const char *mode);    // Replacement for fopen()

Vissa filer visades inte eftersom för många filer har ändrats