From ebaa922f6b53bde2ce8e0a047c72fe9670d71ef9 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Thu, 8 May 2025 09:57:31 -0700 Subject: [PATCH 01/12] Properly clean up the default font on unload, it may be reused if the window is created again --- src/rtext.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rtext.c b/src/rtext.c index 4ee9b34b6..551583b95 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -331,6 +331,9 @@ extern void UnloadFontDefault(void) if (isGpuReady) UnloadTexture(defaultFont.texture); RL_FREE(defaultFont.glyphs); RL_FREE(defaultFont.recs); + defaultFont.glyphCount = 0; + defaultFont.glyphs = NULL; + defaultFont.recs = NULL; } #endif // SUPPORT_DEFAULT_FONT From 693c9c292a876089796e7a413fdd63b15561401c Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 10 May 2025 22:45:08 +0200 Subject: [PATCH 02/12] Formatting tweaks --- src/rcore.c | 14 +++++++------- src/rmodels.c | 38 ++++++++++++++++---------------------- src/rtextures.c | 5 +++-- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index fdc4b54be..77baed957 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -523,25 +523,25 @@ const char *TextFormat(const char *text, ...); // Formatting of tex #define PLATFORM_DESKTOP_GLFW #endif -// We're using `#pragma message` because `#warning` is not adopted by MSVC. +// We're using '#pragma message' because '#warning' is not adopted by MSVC #if defined(SUPPORT_CLIPBOARD_IMAGE) #if !defined(SUPPORT_MODULE_RTEXTURES) - #pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly") + #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly") #endif // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows #if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32) - #pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows") + #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows") #endif - // From what I've tested applications on Wayland saves images on clipboard as PNG. + // From what I've tested applications on Wayland saves images on clipboard as PNG #if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32) - #pragma message ("Warning: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG") + #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG") #endif - // Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined. + // Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined // #if !defined(STBI_REQUIRED) - // #pragma message ("Warning: "STBI_REQUIRED is not defined, that means we can't load images from clipbard" + // #pragma message ("WARNING: "STBI_REQUIRED is not defined, that means we can't load images from clipbard" // #endif #endif // SUPPORT_CLIPBOARD_IMAGE diff --git a/src/rmodels.c b/src/rmodels.c index 61ac9504d..25c520685 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1429,16 +1429,12 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id); - if (mesh.animVertices) - rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices); - else - rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); + if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices); + else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); - if (mesh.normals) - rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animNormalss); - else - rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); + if (mesh.normals) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animNormalss); + else rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); @@ -5215,7 +5211,7 @@ static Model LoadGLTF(const char *fileName) /********************************************************************************************* Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend) - Transform handling implemented by Paul Melis (@paulmelis). + Transform handling implemented by Paul Melis (@paulmelis) Reviewed by Ramon Santamaria (@raysan5) FEATURES: @@ -5225,10 +5221,10 @@ static Model LoadGLTF(const char *fileName) PBR specular/glossiness flow and extended texture flows not supported - Supports multiple meshes per model (every primitives is loaded as a separate mesh) - Supports basic animations - - Transforms, including parent-child relations, are applied on the mesh data, but the - hierarchy is not kept (as it can't be represented). + - Transforms, including parent-child relations, are applied on the mesh data, + but the hierarchy is not kept (as it can't be represented) - Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes) - are turned into separate raylib Meshes. + are turned into separate raylib Meshes RESTRICTIONS: - Only triangle meshes supported @@ -5444,7 +5440,7 @@ static Model LoadGLTF(const char *fileName) // Any glTF mesh linked from more than one Node (i.e. instancing) // is turned into multiple Mesh's, as each Node will have its own // transform applied. - // Note: the code below disregards the scenes defined in the file, all nodes are used. + // NOTE: The code below disregards the scenes defined in the file, all nodes are used. //---------------------------------------------------------------------------------------------------- int meshIndex = 0; for (unsigned int i = 0; i < data->nodes_count; i++) @@ -5894,7 +5890,6 @@ static Model LoadGLTF(const char *fileName) if (attribute->type == cgltf_type_vec4) { - // TODO: Support component types: u8, u16? if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh bone weight to copy glTF attribute data @@ -5930,6 +5925,7 @@ static Model LoadGLTF(const char *fileName) // Load 4 components of float data type into mesh.boneWeights // for cgltf_attribute_type_weights we have: + // - data.meshes[0] (256 vertices) // - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16) LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights) @@ -5940,8 +5936,8 @@ static Model LoadGLTF(const char *fileName) } } - // check if we are animated, and the mesh was not given any bone assignments, but is the child of a bone node - // in this case we need to fully attach all the verts to the parent bone so it will animate with the bone. + // Check if we are animated, and the mesh was not given any bone assignments, but is the child of a bone node + // in this case we need to fully attach all the verts to the parent bone so it will animate with the bone if (data->skins_count > 0 && !hasJoints && node->parent != NULL && node->parent->mesh == NULL) { int parentBoneId = -1; @@ -5956,16 +5952,15 @@ static Model LoadGLTF(const char *fileName) if (parentBoneId >= 0) { - model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount * 4, sizeof(unsigned char)); - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount * 4, sizeof(float)); + model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); - for (int vertexIndex = 0; vertexIndex < model.meshes[meshIndex].vertexCount * 4; vertexIndex += 4) + for (int vertexIndex = 0; vertexIndex < model.meshes[meshIndex].vertexCount*4; vertexIndex += 4) { model.meshes[meshIndex].boneIds[vertexIndex] = (unsigned char)parentBoneId; model.meshes[meshIndex].boneWeights[vertexIndex] = 1.0f; } } - } // Animated vertex data @@ -5986,9 +5981,8 @@ static Model LoadGLTF(const char *fileName) model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity(); } - meshIndex++; // Move to next mesh + meshIndex++; // Move to next mesh } - } // Free all cgltf loaded data diff --git a/src/rtextures.c b/src/rtextures.c index ee116b0e5..2f5aaa871 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -832,10 +832,11 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, // Calculate how far the top-left pixel is along the gradient direction from the center of said gradient float startingPos = 0.5f - (cosDir*width/2) - (sinDir*height/2); + // With directions that lie in the first or third quadrant (i.e. from top-left to // bottom-right or vice-versa), pixel (0, 0) is the farthest point on the gradient // (i.e. the pixel which should become one of the gradient's ends color); while for - // directions that lie in the second or fourth quadrant, that point is pixel (width, 0). + // directions that lie in the second or fourth quadrant, that point is pixel (width, 0) float maxPosValue = ((signbit(sinDir) != 0) == (signbit(cosDir) != 0))? fabsf(startingPos) : fabsf(startingPos + width*cosDir); for (int i = 0; i < width; i++) { @@ -3835,7 +3836,7 @@ void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c // Calculate the inverse of the sum of the barycentric coordinates for normalization // NOTE 1: Here, we act as if we multiply by 255 the reciprocal, which avoids additional - // calculations in the loop. This is acceptable because we are only interpolating colors. + // calculations in the loop. This is acceptable because we are only interpolating colors // NOTE 2: This sum remains constant throughout the triangle float wInvSum = 255.0f/(w1Row + w2Row + w3Row); From 3083f0cd439394635dac26f76a6917d50e56b738 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 May 2025 11:03:49 +0200 Subject: [PATCH 03/12] REVIEWED: `SaveFileText()`, const input text --- src/raylib.h | 2 +- src/utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index fc949a02d..d7de006f8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1123,7 +1123,7 @@ RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() -RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success +RLAPI bool SaveFileText(const char *fileName, const char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success //------------------------------------------------------------------ // File system functions diff --git a/src/utils.c b/src/utils.c index 5c189845b..26e7a1bab 100644 --- a/src/utils.c +++ b/src/utils.c @@ -405,7 +405,7 @@ void UnloadFileText(char *text) } // Save text data to file (write), string must be '\0' terminated -bool SaveFileText(const char *fileName, char *text) +bool SaveFileText(const char *fileName, const char *text) { bool success = false; From f7d03efb4987183ccdf48031bee12a7f120a3de2 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 May 2025 11:05:25 +0200 Subject: [PATCH 04/12] REVIEWED: `DecodeDataBase64()`, follow convention: - All `char *` refer to text strings - All `unsigned char *` refer to generic byte arrays --- src/raylib.h | 2 +- src/rcore.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index d7de006f8..44a5653e4 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1154,7 +1154,7 @@ RLAPI long GetFileModTime(const char *fileName); // Get file mo RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() -RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() +RLAPI unsigned char *DecodeDataBase64(const char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes) RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes) diff --git a/src/rcore.c b/src/rcore.c index 77baed957..207e112a4 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2575,7 +2575,7 @@ char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) } // Decode Base64 string data -unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) +unsigned char *DecodeDataBase64(const char *data, int *outputSize) { static const unsigned char base64decodeTable[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, From 63b988ade906adc7264a8bec8fccec5f47befb55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 May 2025 09:05:50 +0000 Subject: [PATCH 05/12] Update raylib_api.* by CI --- parser/output/raylib_api.json | 4 ++-- parser/output/raylib_api.lua | 4 ++-- parser/output/raylib_api.txt | 4 ++-- parser/output/raylib_api.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 567f9d980..dc6fc32e2 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -4409,7 +4409,7 @@ "name": "fileName" }, { - "type": "char *", + "type": "const char *", "name": "text" } ] @@ -4707,7 +4707,7 @@ "returnType": "unsigned char *", "params": [ { - "type": "const unsigned char *", + "type": "const char *", "name": "data" }, { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 75c328da6..236f6d31a 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4006,7 +4006,7 @@ return { returnType = "bool", params = { {type = "const char *", name = "fileName"}, - {type = "char *", name = "text"} + {type = "const char *", name = "text"} } }, { @@ -4211,7 +4211,7 @@ return { description = "Decode Base64 string data, memory must be MemFree()", returnType = "unsigned char *", params = { - {type = "const unsigned char *", name = "data"}, + {type = "const char *", name = "data"}, {type = "int *", name = "outputSize"} } }, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 0b0bf86c1..7318ae045 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -1656,7 +1656,7 @@ Function 123: SaveFileText() (2 input parameters) Return type: bool Description: Save text data to file (write), string must be '\0' terminated, returns true on success Param[1]: fileName (type: const char *) - Param[2]: text (type: char *) + Param[2]: text (type: const char *) Function 124: FileExists() (1 input parameters) Name: FileExists Return type: bool @@ -1795,7 +1795,7 @@ Function 149: DecodeDataBase64() (2 input parameters) Name: DecodeDataBase64 Return type: unsigned char * Description: Decode Base64 string data, memory must be MemFree() - Param[1]: data (type: const unsigned char *) + Param[1]: data (type: const char *) Param[2]: outputSize (type: int *) Function 150: ComputeCRC32() (2 input parameters) Name: ComputeCRC32 diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index ed6880dec..9c199b6b4 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1046,7 +1046,7 @@ - + @@ -1129,7 +1129,7 @@ - + From dea6a247776dfd726ec141b6def0735d8f8287c4 Mon Sep 17 00:00:00 2001 From: Lumen Keyes Date: Mon, 12 May 2025 11:13:32 -0600 Subject: [PATCH 06/12] build.zig fix: link EGL for Android --- build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/build.zig b/build.zig index 77681e0e6..e157a5055 100644 --- a/build.zig +++ b/build.zig @@ -256,6 +256,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. raylib.root_module.linkSystemLibrary("GLESv2", .{}); raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", ""); } + raylib.root_module.linkSystemLibrary("EGL"); setDesktopPlatform(raylib, .android); } else { From 6f11e27bbee4b5e3e716e5c004c6a8632cb9136a Mon Sep 17 00:00:00 2001 From: Lumen Keyes Date: Mon, 12 May 2025 11:19:08 -0600 Subject: [PATCH 07/12] fix typo --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index e157a5055..0b086dc4d 100644 --- a/build.zig +++ b/build.zig @@ -256,7 +256,7 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. raylib.root_module.linkSystemLibrary("GLESv2", .{}); raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", ""); } - raylib.root_module.linkSystemLibrary("EGL"); + raylib.root_module.linkSystemLibrary("EGL", .{}); setDesktopPlatform(raylib, .android); } else { From d135eef462a6eecb77e032ab809c2e93bcbe010a Mon Sep 17 00:00:00 2001 From: Bigfoot71 Date: Mon, 12 May 2025 23:16:06 +0200 Subject: [PATCH 08/12] fix and improve `GenMeshTangents` --- src/rmodels.c | 137 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 41 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 25c520685..d927f4099 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3608,16 +3608,16 @@ BoundingBox GetMeshBoundingBox(Mesh mesh) } // Compute mesh tangents -// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates -// Implementation based on: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html void GenMeshTangents(Mesh *mesh) { - if ((mesh->vertices == NULL) || (mesh->texcoords == NULL)) + // Check if input mesh data is useful + if ((mesh == NULL) || (mesh->vertices == NULL) || (mesh->texcoords == NULL) || (mesh->normals == NULL)) { - TRACELOG(LOG_WARNING, "MESH: Tangents generation requires texcoord vertex attribute data"); + TRACELOG(LOG_WARNING, "MESH: Tangents generation requires vertices, texcoords and normals vertex attribute data"); return; } + // Allocate or reallocate tangents data if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float)); else { @@ -3625,26 +3625,51 @@ void GenMeshTangents(Mesh *mesh) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float)); } - Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); - Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); + // Allocate temporary arrays for tangents calculation + Vector3 *tan1 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3)); + Vector3 *tan2 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3)); - if (mesh->vertexCount % 3 != 0) + if (tan1 == NULL || tan2 == NULL) { - TRACELOG(LOG_WARNING, "MESH: vertexCount expected to be a multiple of 3. Expect uninitialized values."); + TRACELOG(LOG_WARNING, "MESH: Failed to allocate temporary memory for tangent calculation"); + if (tan1) RL_FREE(tan1); + if (tan2) RL_FREE(tan2); + return; } - for (int i = 0; i <= mesh->vertexCount - 3; i += 3) + // Process all triangles of the mesh + // 'triangleCount' must be always valid + for (int t = 0; t < mesh->triangleCount; t++) { - // Get triangle vertices - Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; - Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; - Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; + // Get triangle vertex indices + int i0, i1, i2; + + if (mesh->indices != NULL) + { + // Use indices if available + i0 = mesh->indices[t*3 + 0]; + i1 = mesh->indices[t*3 + 1]; + i2 = mesh->indices[t*3 + 2]; + } + else + { + // Sequential access for non-indexed mesh + i0 = t*3 + 0; + i1 = t*3 + 1; + i2 = t*3 + 2; + } + + // Get triangle vertices position + Vector3 v1 = { mesh->vertices[i0*3 + 0], mesh->vertices[i0*3 + 1], mesh->vertices[i0*3 + 2] }; + Vector3 v2 = { mesh->vertices[i1*3 + 0], mesh->vertices[i1*3 + 1], mesh->vertices[i1*3 + 2] }; + Vector3 v3 = { mesh->vertices[i2*3 + 0], mesh->vertices[i2*3 + 1], mesh->vertices[i2*3 + 2] }; // Get triangle texcoords - Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; - Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; - Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; + Vector2 uv1 = { mesh->texcoords[i0*2 + 0], mesh->texcoords[i0*2 + 1] }; + Vector2 uv2 = { mesh->texcoords[i1*2 + 0], mesh->texcoords[i1*2 + 1] }; + Vector2 uv3 = { mesh->texcoords[i2*2 + 0], mesh->texcoords[i2*2 + 1] }; + // Calculate triangle edges float x1 = v2.x - v1.x; float y1 = v2.y - v1.y; float z1 = v2.z - v1.z; @@ -3652,65 +3677,95 @@ void GenMeshTangents(Mesh *mesh) float y2 = v3.y - v1.y; float z2 = v3.z - v1.z; + // Calculate texture coordinate differences float s1 = uv2.x - uv1.x; float t1 = uv2.y - uv1.y; float s2 = uv3.x - uv1.x; float t2 = uv3.y - uv1.y; + // Calculate denominator and check for degenerate UV float div = s1*t2 - s2*t1; - float r = (div == 0.0f)? 0.0f : 1.0f/div; + float r = (fabsf(div) < 0.0001f)? 0.0f : 1.0f/div; + // Calculate tangent and bitangent directions Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; - tan1[i + 0] = sdir; - tan1[i + 1] = sdir; - tan1[i + 2] = sdir; + // Accumulate tangents and bitangents for each vertex of the triangle + tan1[i0] = Vector3Add(tan1[i0], sdir); + tan1[i1] = Vector3Add(tan1[i1], sdir); + tan1[i2] = Vector3Add(tan1[i2], sdir); - tan2[i + 0] = tdir; - tan2[i + 1] = tdir; - tan2[i + 2] = tdir; + tan2[i0] = Vector3Add(tan2[i0], tdir); + tan2[i1] = Vector3Add(tan2[i1], tdir); + tan2[i2] = Vector3Add(tan2[i2], tdir); } - // Compute tangents considering normals + // Calculate final tangents for each vertex for (int i = 0; i < mesh->vertexCount; i++) { Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; Vector3 tangent = tan1[i]; - // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... -#if defined(COMPUTE_TANGENTS_METHOD_01) - Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent))); - tmp = Vector3Normalize(tmp); - mesh->tangents[i*4 + 0] = tmp.x; - mesh->tangents[i*4 + 1] = tmp.y; - mesh->tangents[i*4 + 2] = tmp.z; - mesh->tangents[i*4 + 3] = 1.0f; -#else - Vector3OrthoNormalize(&normal, &tangent); - mesh->tangents[i*4 + 0] = tangent.x; - mesh->tangents[i*4 + 1] = tangent.y; - mesh->tangents[i*4 + 2] = tangent.z; - mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; -#endif + // Handle zero tangent (can happen with degenerate UVs) + if (Vector3Length(tangent) < 0.0001f) + { + // Create a tangent perpendicular to the normal + if (fabsf(normal.z) > 0.707f) tangent = (Vector3){ 1.0f, 0.0f, 0.0f }; + else tangent = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f }); + + mesh->tangents[i*4 + 0] = tangent.x; + mesh->tangents[i*4 + 1] = tangent.y; + mesh->tangents[i*4 + 2] = tangent.z; + mesh->tangents[i*4 + 3] = 1.0f; + continue; + } + + // Gram-Schmidt orthogonalization to make tangent orthogonal to normal + // T_prime = T - N * dot(N, T) + Vector3 orthogonalized = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent))); + + // Handle cases where orthogonalized vector is too small + if (Vector3Length(orthogonalized) < 0.0001f) + { + // Create a tangent perpendicular to the normal + if (fabsf(normal.z) > 0.707f) orthogonalized = (Vector3){ 1.0f, 0.0f, 0.0f }; + else orthogonalized = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f }); + } + else + { + // Normalize the orthogonalized tangent + orthogonalized = Vector3Normalize(orthogonalized); + } + + // Store the calculated tangent + mesh->tangents[i*4 + 0] = orthogonalized.x; + mesh->tangents[i*4 + 1] = orthogonalized.y; + mesh->tangents[i*4 + 2] = orthogonalized.z; + + // Calculate the handedness (w component) + mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, orthogonalized), tan2[i]) < 0.0f)? -1.0f : 1.0f; } + // Free temporary arrays RL_FREE(tan1); RL_FREE(tan2); + // Update vertex buffers if available if (mesh->vboId != NULL) { if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0) { - // Update existing vertex buffer + // Update existing tangent vertex buffer rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0); } else { - // Load a new tangent attributes buffer + // Create new tangent vertex buffer mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false); } + // Set up vertex attributes for shader rlEnableVertexArray(mesh->vaoId); rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0); rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT); From aa684a33de143b2cb9e63a93ef4db2925a6d7c48 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Mon, 12 May 2025 19:54:34 -0700 Subject: [PATCH 09/12] make save file callback match const correctness of calling function --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 44a5653e4..0cffa4272 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -954,7 +954,7 @@ typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data -typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data +typedef bool (*SaveFileTextCallback)(const char *fileName, const char *text); // FileIO: Save text data //------------------------------------------------------------------------------------ // Global Variables Definition From 4a1e9931a610489544de0535a0534839fff520a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 13 May 2025 02:55:19 +0000 Subject: [PATCH 10/12] Update raylib_api.* by CI --- parser/output/raylib_api.json | 2 +- parser/output/raylib_api.lua | 2 +- parser/output/raylib_api.txt | 2 +- parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index dc6fc32e2..e73e18fba 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -3139,7 +3139,7 @@ "name": "fileName" }, { - "type": "char *", + "type": "const char *", "name": "text" } ] diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 236f6d31a..fa92cdea7 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -3108,7 +3108,7 @@ return { returnType = "bool", params = { {type = "const char *", name = "fileName"}, - {type = "char *", name = "text"} + {type = "const char *", name = "text"} } }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 7318ae045..4aa682d78 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -985,7 +985,7 @@ Callback 005: SaveFileTextCallback() (2 input parameters) Return type: bool Description: FileIO: Save text data Param[1]: fileName (type: const char *) - Param[2]: text (type: char *) + Param[2]: text (type: const char *) Callback 006: AudioCallback() (2 input parameters) Name: AudioCallback Return type: void diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 9c199b6b4..c88a20bd1 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -672,7 +672,7 @@ - + From 9d4c31533d86d7a8f552f050f9f38d1180c629dd Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 14 May 2025 23:47:03 +0200 Subject: [PATCH 11/12] Update rtext.c --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 551583b95..70dcfc38c 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1578,7 +1578,7 @@ const char *TextSubtext(const char *text, int position, int length) } // Replace text string -// REQUIRES: strlen(), strstr(), strncpy(), strcpy() +// REQUIRES: strstr(), strncpy(), strcpy() // WARNING: Allocated memory must be manually freed char *TextReplace(const char *text, const char *replace, const char *by) { From 8c99a508c62798878af0a7030d1e42e4b4a13791 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 14 May 2025 23:49:24 +0200 Subject: [PATCH 12/12] REVIEWED: `WindowSizeCallback()`, GLFW It is called on window minification and setting internal width/height to 0, that can break things --- src/platforms/rcore_desktop_glfw.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 94c780ca3..675d09f22 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1752,6 +1752,10 @@ static void ErrorCallback(int error, const char *description) // NOTE: Window resizing not enabled by default, use SetConfigFlags() static void WindowSizeCallback(GLFWwindow *window, int width, int height) { + // WARNING: On window minimization, callback is called, + // but we don't want to change internal screen values, it breaks things + if ((width == 0) || (height == 0)) return; + // Reset viewport and projection matrix for new size SetupViewport(width, height);