diff --git a/src/config.h b/src/config.h index f73804223..71908b0c0 100644 --- a/src/config.h +++ b/src/config.h @@ -92,6 +92,7 @@ #define DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) #define MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack +#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh #define MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported #define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported diff --git a/src/models.c b/src/models.c index f4c6a9c97..5c54902a9 100644 --- a/src/models.c +++ b/src/models.c @@ -92,9 +92,7 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#ifndef DEFAULT_MESH_VERTEX_BUFFERS - #define DEFAULT_MESH_VERTEX_BUFFERS 7 // Number of vertex buffers (VBO) per mesh -#endif +// ... //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -860,8 +858,21 @@ void UploadMesh(Mesh *mesh) // Unload mesh from memory (RAM and/or VRAM) void UnloadMesh(Mesh mesh) { + // Unload rlgl mesh vboId data rlUnloadMesh(mesh); - RL_FREE(mesh.vboId); + + RL_FREE(mesh.vertices); + RL_FREE(mesh.texcoords); + RL_FREE(mesh.normals); + RL_FREE(mesh.colors); + RL_FREE(mesh.tangents); + RL_FREE(mesh.texcoords2); + RL_FREE(mesh.indices); + + RL_FREE(mesh.animVertices); + RL_FREE(mesh.animNormals); + RL_FREE(mesh.boneWeights); + RL_FREE(mesh.boneIds); } // Export mesh data to file @@ -1128,8 +1139,6 @@ Mesh GenMeshPoly(int sides, float radius) if (sides < 3) return mesh; - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - int vertexCount = sides*3; // Vertices definition @@ -1186,6 +1195,7 @@ Mesh GenMeshPoly(int sides, float radius) RL_FREE(texcoords); // Upload vertex data to GPU (static mesh) + // NOTE: mesh.vboId array is allocated inside rlLoadMesh() rlLoadMesh(&mesh, false); return mesh; @@ -1195,7 +1205,6 @@ Mesh GenMeshPoly(int sides, float radius) Mesh GenMeshPlane(float width, float length, int resX, int resZ) { Mesh mesh = { 0 }; - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); #define CUSTOM_MESH_GEN_PLANE #if defined(CUSTOM_MESH_GEN_PLANE) @@ -1329,7 +1338,6 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) Mesh GenMeshCube(float width, float height, float length) { Mesh mesh = { 0 }; - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); #define CUSTOM_MESH_GEN_CUBE #if defined(CUSTOM_MESH_GEN_CUBE) @@ -1498,8 +1506,6 @@ RLAPI Mesh GenMeshSphere(float radius, int rings, int slices) if ((rings >= 3) && (slices >= 3)) { - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings); par_shapes_scale(sphere, radius, radius, radius); // NOTE: Soft normals are computed internally @@ -1544,8 +1550,6 @@ RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices) { if (radius < 0.0f) radius = 0.0f; - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings); par_shapes_scale(sphere, radius, radius, radius); // NOTE: Soft normals are computed internally @@ -1588,8 +1592,6 @@ Mesh GenMeshCylinder(float radius, float height, int slices) if (slices >= 3) { - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - // Instance a cylinder that sits on the Z=0 plane using the given tessellation // levels across the UV domain. Think of "slices" like a number of pizza // slices, and "stacks" like a number of stacked rings. @@ -1653,8 +1655,6 @@ Mesh GenMeshTorus(float radius, float size, int radSeg, int sides) if ((sides >= 3) && (radSeg >= 3)) { - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - if (radius > 1.0f) radius = 1.0f; else if (radius < 0.1f) radius = 0.1f; @@ -1701,8 +1701,6 @@ Mesh GenMeshKnot(float radius, float size, int radSeg, int sides) if ((sides >= 3) && (radSeg >= 3)) { - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - if (radius > 3.0f) radius = 3.0f; else if (radius < 0.5f) radius = 0.5f; @@ -1747,7 +1745,6 @@ Mesh GenMeshHeightmap(Image heightmap, Vector3 size) #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3) Mesh mesh = { 0 }; - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); int mapX = heightmap.width; int mapZ = heightmap.height; @@ -1883,7 +1880,6 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a)) Mesh mesh = { 0 }; - mesh.vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); Color *pixels = LoadImageColors(cubicmap); @@ -2842,7 +2838,6 @@ static Model LoadOBJ(const char *fileName) model.meshes[mi].vertices = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); model.meshes[mi].texcoords = (float *)RL_CALLOC(model.meshes[mi].vertexCount*2, sizeof(float)); model.meshes[mi].normals = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshes[mi].vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); model.meshMaterial[mi] = mi; } @@ -3108,8 +3103,6 @@ static Model LoadIQM(const char *fileName) // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); - - model.meshes[i].vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); } // Triangles data processing @@ -3726,10 +3719,7 @@ static Model LoadGLTF(const char *fileName) model.boneCount = (int)data->nodes_count; model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo)); model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform)); - - for (int i = 0; i < model.meshCount; i++) - model.meshes[i].vboId = (unsigned int *)RL_CALLOC(DEFAULT_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); - + for (unsigned int j = 0; j < data->nodes_count; j++) { strcpy(model.bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name); @@ -3738,43 +3728,25 @@ static Model LoadGLTF(const char *fileName) for (unsigned int i = 0; i < data->nodes_count; i++) { - if (data->nodes[i].has_translation) - { - memcpy(&model.bindPose[i].translation, data->nodes[i].translation, 3 * sizeof(float)); - } - else - { - model.bindPose[i].translation = Vector3Zero(); - } - - if (data->nodes[i].has_rotation) - { - memcpy(&model.bindPose[i].rotation, data->nodes[i].rotation, 4 * sizeof(float)); - } - else - { - model.bindPose[i].rotation = QuaternionIdentity(); - } + if (data->nodes[i].has_translation) memcpy(&model.bindPose[i].translation, data->nodes[i].translation, 3 * sizeof(float)); + else model.bindPose[i].translation = Vector3Zero(); + + if (data->nodes[i].has_rotation) memcpy(&model.bindPose[i].rotation, data->nodes[i].rotation, 4 * sizeof(float)); + else model.bindPose[i].rotation = QuaternionIdentity(); + model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation); - if (data->nodes[i].has_scale) - { - memcpy(&model.bindPose[i].scale, data->nodes[i].scale, 3 * sizeof(float)); - } - else - { - model.bindPose[i].scale = Vector3One(); - } + if (data->nodes[i].has_scale) memcpy(&model.bindPose[i].scale, data->nodes[i].scale, 3 * sizeof(float)); + else model.bindPose[i].scale = Vector3One(); } for (int i = 0; i < model.boneCount; i++) { - Transform* currentTransform = model.bindPose + i; - BoneInfo* currentBone = model.bones + i; + Transform *currentTransform = model.bindPose + i; + BoneInfo *currentBone = model.bones + i; int root = currentBone->parent; - if (root >= model.boneCount) - root = 0; - Transform* parentTransform = model.bindPose + root; + if (root >= model.boneCount) root = 0; + Transform *parentTransform = model.bindPose + root; if (currentBone->parent >= 0) { @@ -3791,7 +3763,7 @@ static Model LoadGLTF(const char *fileName) Color tint = (Color){ 255, 255, 255, 255 }; const char *texPath = GetDirectoryPath(fileName); - //Ensure material follows raylib support for PBR (metallic/roughness flow) + // Ensure material follows raylib support for PBR (metallic/roughness flow) if (data->materials[i].has_pbr_metallic_roughness) { tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0] * 255); diff --git a/src/rlgl.h b/src/rlgl.h index 3bd79a363..a543c40f8 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -158,6 +158,11 @@ #define MAX_MATRIX_STACK_SIZE 32 // Maximum size of Matrix stack #endif +// Vertex buffers id limit +#ifndef MAX_MESH_VERTEX_BUFFERS + #define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh +#endif + // Shader and material limits #ifndef MAX_SHADER_LOCATIONS #define MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported @@ -2420,9 +2425,9 @@ void rlGenerateMipmaps(Texture2D *texture) if (texture->format == UNCOMPRESSED_R8G8B8A8) { // Retrieve texture data from VRAM - void *data = rlReadTexturePixels(*texture); + void *texData = rlReadTexturePixels(*texture); - // NOTE: data size is reallocated to fit mipmaps data + // NOTE: Texture data size is reallocated to fit mipmaps data // NOTE: CPU mipmap generation only supports RGBA 32bit data int mipmapCount = GenerateMipmaps(data, texture->width, texture->height); @@ -2435,7 +2440,7 @@ void rlGenerateMipmaps(Texture2D *texture) // Load the mipmaps for (int level = 1; level < mipmapCount; level++) { - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)data + offset); + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, mipWidth, mipHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)texData + offset); size = mipWidth*mipHeight*4; offset += size; @@ -2445,7 +2450,7 @@ void rlGenerateMipmaps(Texture2D *texture) } texture->mipmaps = mipmapCount + 1; - RL_FREE(data); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data + RL_FREE(texData); // Once mipmaps have been generated and data has been uploaded to GPU VRAM, we can discard RAM data TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Mipmaps generated manually on CPU side, total: %i", texture->id, texture->mipmaps); } @@ -2481,6 +2486,8 @@ void rlLoadMesh(Mesh *mesh, bool dynamic) TRACELOG(LOG_WARNING, "VAO: [ID %i] Trying to re-load an already loaded mesh", mesh->vaoId); return; } + + mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); mesh->vaoId = 0; // Vertex Array Object mesh->vboId[0] = 0; // Vertex positions VBO @@ -2952,9 +2959,9 @@ void rlDrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int c glUniformMatrix4fv(material.shader.locs[LOC_MATRIX_MVP], 1, false, MatrixToFloat(MatrixMultiply(MatrixMultiply(RLGL.State.transform, RLGL.State.modelview), RLGL.State.projection))); - float16* instances = RL_MALLOC(count*sizeof(float16)); + float16* instanceTransforms = RL_MALLOC(count*sizeof(float16)); - for (int i = 0; i < count; i++) instances[i] = MatrixToFloatV(transforms[i]); + for (int i = 0; i < count; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); // This could alternatively use a static VBO and either glMapBuffer or glBufferSubData. // It isn't clear which would be reliably faster in all cases and on all platforms, and @@ -2963,7 +2970,7 @@ void rlDrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int c unsigned int instancesB = 0; glGenBuffers(1, &instancesB); glBindBuffer(GL_ARRAY_BUFFER, instancesB); - glBufferData(GL_ARRAY_BUFFER, count*sizeof(float16), instances, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, count*sizeof(float16), instanceTransforms, GL_STATIC_DRAW); // Instances are put in LOC_MATRIX_MODEL attribute location with space for 4x Vector4, eg: // layout (location = 12) in mat4 instance; @@ -2983,7 +2990,7 @@ void rlDrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int c else glDrawArraysInstanced(GL_TRIANGLES, 0, mesh.vertexCount, count); glDeleteBuffers(1, &instancesB); - RL_FREE(instances); + RL_FREE(instanceTransforms); // Unbind all binded texture maps for (int i = 0; i < MAX_MATERIAL_MAPS; i++) @@ -3007,19 +3014,6 @@ void rlDrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int c // Unload mesh data from CPU and GPU void rlUnloadMesh(Mesh mesh) { - RL_FREE(mesh.vertices); - RL_FREE(mesh.texcoords); - RL_FREE(mesh.normals); - RL_FREE(mesh.colors); - RL_FREE(mesh.tangents); - RL_FREE(mesh.texcoords2); - RL_FREE(mesh.indices); - - RL_FREE(mesh.animVertices); - RL_FREE(mesh.animNormals); - RL_FREE(mesh.boneWeights); - RL_FREE(mesh.boneIds); - #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) for (int i = 0; i < 7; i++) glDeleteBuffers(1, &mesh.vboId[i]); // DEFAULT_MESH_VERTEX_BUFFERS (model.c) if (RLGL.ExtSupported.vao) @@ -3030,6 +3024,8 @@ void rlUnloadMesh(Mesh mesh) } else TRACELOG(LOG_INFO, "VBO: Unloaded vertex data from VRAM (GPU)"); #endif + + RL_FREE(mesh.vboId); } // Read screen pixel data (color buffer)