Browse Source

Add multi texture support for materials in GLTF format (#979)

* Initial commit of addition for GLTF materials.. should support loading more than just albedo map.

* Clean up

* fixed seg faults and leaks

* temp don't overwrite defuse colour when rendering

* undid something dumb!

* correctly mixed diffuse map color when rendering to preserve not overwrite it
Tyler Jessilynn Bezera 5 years ago
parent
commit
3f7fa6d6e7
2 changed files with 126 additions and 89 deletions
  1. 123 87
      src/models.c
  2. 3 2
      src/raylib.h

+ 123 - 87
src/models.c

@@ -2385,8 +2385,15 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
 
     for (int i = 0; i < model.meshCount; i++)
     {
-        model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint;
+        Color c = model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color;
+        Color s = c;
+        c.r = ((c.r/255) * (tint.r/255)) * 255;
+        c.g = ((c.g/255) * (tint.g/255)) * 255;
+        c.b = ((c.b/255) * (tint.b/255)) * 255;
+        c.a = ((c.a/255) * (tint.a/255)) * 255;
+        model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = c;
         rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
+        model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = s;
     }
 }
 
@@ -3344,6 +3351,92 @@ static unsigned char *DecodeBase64(char *input, int *size)
     return buf;
 }
 
+//static Texture LoadTextureFromCGLTFTextureView(cgltf_texture_view* view, Color tint, char* texPath)
+static Texture LoadTextureFromCGLTFTextureView(cgltf_image* image, Color tint, const char* texPath)
+{
+    Texture texture = {0};
+    //cgltf_image *image = view->texture->image;
+
+    if (image->uri)
+    {
+        if ((strlen(image->uri) > 5) &&
+            (image->uri[0] == 'd') &&
+            (image->uri[1] == 'a') &&
+            (image->uri[2] == 't') &&
+            (image->uri[3] == 'a') &&
+            (image->uri[4] == ':'))
+        {
+            // Data URI
+            // Format: data:<mediatype>;base64,<data>
+
+            // Find the comma
+            int i = 0;
+            while ((image->uri[i] != ',') && (image->uri[i] != 0))
+                i++;
+
+            if (image->uri[i] == 0)
+                TraceLog(LOG_WARNING, "Invalid data URI");
+            else
+            {
+                int size;
+                unsigned char *data = DecodeBase64(image->uri + i + 1, &size);
+
+                int w, h;
+                unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4);
+
+                Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
+                ImageColorTint(&rimage, tint);
+                texture = LoadTextureFromImage(rimage);
+                UnloadImage(rimage);
+            }
+        }
+        else
+        {
+            char *textureName = image->uri;
+            char *texturePath = RL_MALLOC(strlen(texPath) + strlen(textureName) + 2);
+            strcpy(texturePath, texPath);
+            strcat(texturePath, "/");
+            strcat(texturePath, textureName);
+
+            Image rimage = LoadImage(texturePath);
+            ImageColorTint(&rimage, tint);
+            texture = LoadTextureFromImage(rimage);
+            UnloadImage(rimage);
+            RL_FREE(texturePath);
+        }
+    }
+    else if (image->buffer_view)
+    {
+        unsigned char *data = RL_MALLOC(image->buffer_view->size);
+        int n = image->buffer_view->offset;
+        int stride = image->buffer_view->stride ? image->buffer_view->stride : 1;
+
+        for (int i = 0; i < image->buffer_view->size; i++)
+        {
+            data[i] = ((unsigned char *)image->buffer_view->buffer->data)[n];
+            n += stride;
+        }
+
+        int w, h;
+        unsigned char *raw = stbi_load_from_memory(data, image->buffer_view->size, &w, &h, NULL, 4);
+
+        Image rimage = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
+        ImageColorTint(&rimage, tint);
+        texture = LoadTextureFromImage(rimage);
+        UnloadImage(rimage);
+        free(raw);
+        free(data);
+    }
+    else
+    {
+        Image rimage = LoadImageEx(&tint, 1, 1);
+        texture = LoadTextureFromImage(rimage);
+        UnloadImage(rimage);
+    }
+
+    return texture;
+}
+
 // Load glTF mesh data
 static Model LoadGLTF(const char *fileName)
 {
@@ -3426,106 +3519,49 @@ static Model LoadGLTF(const char *fileName)
 
         for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
 
+        //For each material
         for (int i = 0; i < model.materialCount - 1; i++)
         {
-            Color tint = WHITE;
-            Texture2D texture = { 0 };
+            model.materials[i] = LoadMaterialDefault();
+            Color tint = (Color){ 255, 255, 255, 255 };
             const char *texPath = GetDirectoryPath(fileName);
 
-            if (data->materials[i].has_pbr_metallic_roughness)
-            {
-                tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255.99f);
-                tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255.99f);
-                tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255.99f);
-                tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255.99f);
-            }
-            else
-            {
-                tint.r = 1.0f;
-                tint.g = 1.0f;
-                tint.b = 1.0f;
-                tint.a = 1.0f;
-            }
+            //Ensure material follows raylib support for PBR (metallic/roughness flow)
+            if (data->materials[i].has_pbr_metallic_roughness) {
+                float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor;
+                float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor;
 
-            if (data->materials[i].has_pbr_metallic_roughness)
-            {
-                cgltf_image *img = data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image;
-
-                if (img->uri)
-                {
-                    if ((strlen(img->uri) > 5) &&
-                        (img->uri[0] == 'd') &&
-                        (img->uri[1] == 'a') &&
-                        (img->uri[2] == 't') &&
-                        (img->uri[3] == 'a') &&
-                        (img->uri[4] == ':'))
-                    {
-                        // Data URI
-                        // Format: data:<mediatype>;base64,<data>
-
-                        // Find the comma
-                        int i = 0;
-                        while ((img->uri[i] != ',') && (img->uri[i] != 0)) i++;
+                if (model.materials[i].name && data->materials[i].name) {
+                    strcpy(model.materials[i].name, data->materials[i].name);
+                }
 
-                        if (img->uri[i] == 0) TraceLog(LOG_WARNING, "[%s] Invalid data URI", fileName);
-                        else
-                        {
-                            int size;
-                            unsigned char *data = DecodeBase64(img->uri + i + 1, &size);
+                // shouldn't these be *255 ???
+                tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255);
+                tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255);
+                tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255);
+                tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255);
 
-                            int w, h;
-                            unsigned char *raw = stbi_load_from_memory(data, size, &w, &h, NULL, 4);
+                model.materials[i].maps[MAP_ROUGHNESS].color = tint;
 
-                            Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
-                            ImageColorTint(&image, tint);
-                            texture = LoadTextureFromImage(image);
-                            UnloadImage(image);
-                        }
-                    }
-                    else
-                    {
-                        char *textureName = img->uri;
-                        char *texturePath = RL_MALLOC(strlen(texPath) + strlen(textureName) + 2);
-                        strcpy(texturePath, texPath);
-                        strcat(texturePath, "/");
-                        strcat(texturePath, textureName);
-
-                        Image image = LoadImage(texturePath);
-                        ImageColorTint(&image, tint);
-                        texture = LoadTextureFromImage(image);
-                        UnloadImage(image);
-                        RL_FREE(texturePath);
-                    }
+                if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture) {
+                    model.materials[i].maps[MAP_ALBEDO].texture = LoadTextureFromCGLTFTextureView(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, tint, texPath);
                 }
-                else if (img->buffer_view)
-                {
-                    unsigned char *data = RL_MALLOC(img->buffer_view->size);
-                    int n = img->buffer_view->offset;
-                    int stride = img->buffer_view->stride ? img->buffer_view->stride : 1;
 
-                    for (int i = 0; i < img->buffer_view->size; i++)
-                    {
-                        data[i] = ((unsigned char *)img->buffer_view->buffer->data)[n];
-                        n += stride;
-                    }
+                //tint isn't need for other textures.. pass null or clear? (try full white because of mixing (multiplying * white has no effect))
+                tint = (Color){ 255, 255, 255, 255 };
 
-                    int w, h;
-                    unsigned char *raw = stbi_load_from_memory(data, img->buffer_view->size, &w, &h, NULL, 4);
+                if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture) {
+                    model.materials[i].maps[MAP_ROUGHNESS].texture = LoadTextureFromCGLTFTextureView(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, tint, texPath);
+                }
+                model.materials[i].maps[MAP_ROUGHNESS].value = roughness;
+                model.materials[i].maps[MAP_METALNESS].value = metallic;
 
-                    Image image = LoadImagePro(raw, w, h, UNCOMPRESSED_R8G8B8A8);
-                    ImageColorTint(&image, tint);
-                    texture = LoadTextureFromImage(image);
-                    UnloadImage(image);
+                if (data->materials[i].normal_texture.texture) {
+                    model.materials[i].maps[MAP_NORMAL].texture = LoadTextureFromCGLTFTextureView(data->materials[i].normal_texture.texture->image, tint, texPath);
                 }
-                else
-                {
-                    Image image = LoadImageEx(&tint, 1, 1);
-                    texture = LoadTextureFromImage(image);
-                    UnloadImage(image);
+                if (data->materials[i].occlusion_texture.texture) {
+                    model.materials[i].maps[MAP_OCCLUSION].texture = LoadTextureFromCGLTFTextureView(data->materials[i].occlusion_texture.texture->image, tint, texPath);
                 }
-
-                model.materials[i] = LoadMaterialDefault();
-                model.materials[i].maps[MAP_DIFFUSE].texture = texture;
             }
         }
 

+ 3 - 2
src/raylib.h

@@ -106,7 +106,7 @@
 #ifndef RL_FREE
     #define RL_FREE(p)          free(p)
 #endif
-
+
 // NOTE: MSC C++ compiler does not support compound literals (C99 feature)
 // Plain structures in C++ (without constructors) can be initialized from { } initializers.
 #if defined(__cplusplus)
@@ -336,6 +336,7 @@ typedef struct MaterialMap {
 
 // Material type (generic)
 typedef struct Material {
+    char* name;             // Material name
     Shader shader;          // Material shader
     MaterialMap *maps;      // Material maps array (MAX_MATERIAL_MAPS)
     float *params;          // Material generic parameters (if required)
@@ -1406,7 +1407,7 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream);                  // Check i
 RLAPI void StopAudioStream(AudioStream stream);                       // Stop audio stream
 RLAPI void SetAudioStreamVolume(AudioStream stream, float volume);    // Set volume for audio stream (1.0 is max level)
 RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch);      // Set pitch for audio stream (1.0 is base level)
-
+
 //------------------------------------------------------------------------------------
 // Network (Module: network)
 //------------------------------------------------------------------------------------