rmodels.c (307329B)
1 /********************************************************************************************** 2 * 3 * rmodels - Basic functions to draw 3d shapes and load and draw 3d models 4 * 5 * CONFIGURATION: 6 * #define SUPPORT_MODULE_RMODELS 7 * rmodels module is included in the build 8 * 9 * #define SUPPORT_FILEFORMAT_OBJ 10 * #define SUPPORT_FILEFORMAT_MTL 11 * #define SUPPORT_FILEFORMAT_IQM 12 * #define SUPPORT_FILEFORMAT_GLTF 13 * #define SUPPORT_FILEFORMAT_VOX 14 * #define SUPPORT_FILEFORMAT_M3D 15 * Selected desired fileformats to be supported for model data loading. 16 * 17 * #define SUPPORT_MESH_GENERATION 18 * Support procedural mesh generation functions, uses external par_shapes.h library 19 * NOTE: Some generated meshes DO NOT include generated texture coordinates 20 * 21 * 22 * LICENSE: zlib/libpng 23 * 24 * Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) 25 * 26 * This software is provided "as-is", without any express or implied warranty. In no event 27 * will the authors be held liable for any damages arising from the use of this software. 28 * 29 * Permission is granted to anyone to use this software for any purpose, including commercial 30 * applications, and to alter it and redistribute it freely, subject to the following restrictions: 31 * 32 * 1. The origin of this software must not be misrepresented; you must not claim that you 33 * wrote the original software. If you use this software in a product, an acknowledgment 34 * in the product documentation would be appreciated but is not required. 35 * 36 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 37 * as being the original software. 38 * 39 * 3. This notice may not be removed or altered from any source distribution. 40 * 41 **********************************************************************************************/ 42 43 #include "raylib.h" // Declares module functions 44 45 // Check if config flags have been externally provided on compilation line 46 #if !defined(EXTERNAL_CONFIG_FLAGS) 47 #include "config.h" // Defines module configuration flags 48 #endif 49 50 #if defined(SUPPORT_MODULE_RMODELS) 51 52 #include "utils.h" // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText() 53 #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 54 #include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality 55 56 #include <stdio.h> // Required for: sprintf() 57 #include <stdlib.h> // Required for: malloc(), calloc(), free() 58 #include <string.h> // Required for: memcmp(), strlen(), strncpy() 59 #include <math.h> // Required for: sinf(), cosf(), sqrtf(), fabsf() 60 61 #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) 62 #define TINYOBJ_MALLOC RL_MALLOC 63 #define TINYOBJ_CALLOC RL_CALLOC 64 #define TINYOBJ_REALLOC RL_REALLOC 65 #define TINYOBJ_FREE RL_FREE 66 67 #define TINYOBJ_LOADER_C_IMPLEMENTATION 68 #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading 69 #endif 70 71 #if defined(SUPPORT_FILEFORMAT_GLTF) 72 #define CGLTF_MALLOC RL_MALLOC 73 #define CGLTF_FREE RL_FREE 74 75 #define CGLTF_IMPLEMENTATION 76 #include "external/cgltf.h" // glTF file format loading 77 #endif 78 79 #if defined(SUPPORT_FILEFORMAT_VOX) 80 #define VOX_MALLOC RL_MALLOC 81 #define VOX_CALLOC RL_CALLOC 82 #define VOX_REALLOC RL_REALLOC 83 #define VOX_FREE RL_FREE 84 85 #define VOX_LOADER_IMPLEMENTATION 86 #include "external/vox_loader.h" // VOX file format loading (MagikaVoxel) 87 #endif 88 89 #if defined(SUPPORT_FILEFORMAT_M3D) 90 #define M3D_MALLOC RL_MALLOC 91 #define M3D_REALLOC RL_REALLOC 92 #define M3D_FREE RL_FREE 93 94 #define M3D_IMPLEMENTATION 95 #include "external/m3d.h" // Model3D file format loading 96 #endif 97 98 #if defined(SUPPORT_MESH_GENERATION) 99 #define PAR_MALLOC(T, N) ((T*)RL_MALLOC(N*sizeof(T))) 100 #define PAR_CALLOC(T, N) ((T*)RL_CALLOC(N*sizeof(T), 1)) 101 #define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N))) 102 #define PAR_FREE RL_FREE 103 104 #if defined(_MSC_VER) // Disable some MSVC warning 105 #pragma warning(push) 106 #pragma warning(disable : 4244) 107 #pragma warning(disable : 4305) 108 #endif 109 110 #define PAR_SHAPES_IMPLEMENTATION 111 #include "external/par_shapes.h" // Shapes 3d parametric generation 112 113 #if defined(_MSC_VER) 114 #pragma warning(pop) // Disable MSVC warning suppression 115 #endif 116 #endif 117 118 #if defined(_WIN32) 119 #include <direct.h> // Required for: _chdir() [Used in LoadOBJ()] 120 #define CHDIR _chdir 121 #else 122 #include <unistd.h> // Required for: chdir() (POSIX) [Used in LoadOBJ()] 123 #define CHDIR chdir 124 #endif 125 126 //---------------------------------------------------------------------------------- 127 // Defines and Macros 128 //---------------------------------------------------------------------------------- 129 #ifndef MAX_MATERIAL_MAPS 130 #define MAX_MATERIAL_MAPS 12 // Maximum number of maps supported 131 #endif 132 #ifndef MAX_MESH_VERTEX_BUFFERS 133 #define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh 134 #endif 135 136 //---------------------------------------------------------------------------------- 137 // Types and Structures Definition 138 //---------------------------------------------------------------------------------- 139 // ... 140 141 //---------------------------------------------------------------------------------- 142 // Global Variables Definition 143 //---------------------------------------------------------------------------------- 144 // ... 145 146 //---------------------------------------------------------------------------------- 147 // Module specific Functions Declaration 148 //---------------------------------------------------------------------------------- 149 #if defined(SUPPORT_FILEFORMAT_OBJ) 150 static Model LoadOBJ(const char *fileName); // Load OBJ mesh data 151 #endif 152 #if defined(SUPPORT_FILEFORMAT_IQM) 153 static Model LoadIQM(const char *fileName); // Load IQM mesh data 154 static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount); // Load IQM animation data 155 #endif 156 #if defined(SUPPORT_FILEFORMAT_GLTF) 157 static Model LoadGLTF(const char *fileName); // Load GLTF mesh data 158 static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount); // Load GLTF animation data 159 #endif 160 #if defined(SUPPORT_FILEFORMAT_VOX) 161 static Model LoadVOX(const char *filename); // Load VOX mesh data 162 #endif 163 #if defined(SUPPORT_FILEFORMAT_M3D) 164 static Model LoadM3D(const char *filename); // Load M3D mesh data 165 static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount); // Load M3D animation data 166 #endif 167 #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) 168 static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount); // Process obj materials 169 #endif 170 171 //---------------------------------------------------------------------------------- 172 // Module Functions Definition 173 //---------------------------------------------------------------------------------- 174 175 // Draw a line in 3D world space 176 void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color) 177 { 178 rlBegin(RL_LINES); 179 rlColor4ub(color.r, color.g, color.b, color.a); 180 rlVertex3f(startPos.x, startPos.y, startPos.z); 181 rlVertex3f(endPos.x, endPos.y, endPos.z); 182 rlEnd(); 183 } 184 185 // Draw a point in 3D space, actually a small line 186 void DrawPoint3D(Vector3 position, Color color) 187 { 188 rlPushMatrix(); 189 rlTranslatef(position.x, position.y, position.z); 190 rlBegin(RL_LINES); 191 rlColor4ub(color.r, color.g, color.b, color.a); 192 rlVertex3f(0.0f, 0.0f, 0.0f); 193 rlVertex3f(0.0f, 0.0f, 0.1f); 194 rlEnd(); 195 rlPopMatrix(); 196 } 197 198 // Draw a circle in 3D world space 199 void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color) 200 { 201 rlPushMatrix(); 202 rlTranslatef(center.x, center.y, center.z); 203 rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z); 204 205 rlBegin(RL_LINES); 206 for (int i = 0; i < 360; i += 10) 207 { 208 rlColor4ub(color.r, color.g, color.b, color.a); 209 210 rlVertex3f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius, 0.0f); 211 rlVertex3f(sinf(DEG2RAD*(i + 10))*radius, cosf(DEG2RAD*(i + 10))*radius, 0.0f); 212 } 213 rlEnd(); 214 rlPopMatrix(); 215 } 216 217 // Draw a color-filled triangle (vertex in counter-clockwise order!) 218 void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color) 219 { 220 rlBegin(RL_TRIANGLES); 221 rlColor4ub(color.r, color.g, color.b, color.a); 222 rlVertex3f(v1.x, v1.y, v1.z); 223 rlVertex3f(v2.x, v2.y, v2.z); 224 rlVertex3f(v3.x, v3.y, v3.z); 225 rlEnd(); 226 } 227 228 // Draw a triangle strip defined by points 229 void DrawTriangleStrip3D(const Vector3 *points, int pointCount, Color color) 230 { 231 if (pointCount < 3) return; // Security check 232 233 rlBegin(RL_TRIANGLES); 234 rlColor4ub(color.r, color.g, color.b, color.a); 235 236 for (int i = 2; i < pointCount; i++) 237 { 238 if ((i%2) == 0) 239 { 240 rlVertex3f(points[i].x, points[i].y, points[i].z); 241 rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z); 242 rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z); 243 } 244 else 245 { 246 rlVertex3f(points[i].x, points[i].y, points[i].z); 247 rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z); 248 rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z); 249 } 250 } 251 rlEnd(); 252 } 253 254 // Draw cube 255 // NOTE: Cube position is the center position 256 void DrawCube(Vector3 position, float width, float height, float length, Color color) 257 { 258 float x = 0.0f; 259 float y = 0.0f; 260 float z = 0.0f; 261 262 rlPushMatrix(); 263 // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) 264 rlTranslatef(position.x, position.y, position.z); 265 //rlRotatef(45, 0, 1, 0); 266 //rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition 267 268 rlBegin(RL_TRIANGLES); 269 rlColor4ub(color.r, color.g, color.b, color.a); 270 271 // Front face 272 rlNormal3f(0.0f, 0.0f, 1.0f); 273 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left 274 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right 275 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left 276 277 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right 278 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left 279 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right 280 281 // Back face 282 rlNormal3f(0.0f, 0.0f, -1.0f); 283 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left 284 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left 285 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right 286 287 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right 288 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right 289 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left 290 291 // Top face 292 rlNormal3f(0.0f, 1.0f, 0.0f); 293 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left 294 rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left 295 rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right 296 297 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right 298 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left 299 rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right 300 301 // Bottom face 302 rlNormal3f(0.0f, -1.0f, 0.0f); 303 rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left 304 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right 305 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left 306 307 rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right 308 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right 309 rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left 310 311 // Right face 312 rlNormal3f(1.0f, 0.0f, 0.0f); 313 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right 314 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right 315 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left 316 317 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left 318 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right 319 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left 320 321 // Left face 322 rlNormal3f(-1.0f, 0.0f, 0.0f); 323 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right 324 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left 325 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right 326 327 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left 328 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left 329 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right 330 rlEnd(); 331 rlPopMatrix(); 332 } 333 334 // Draw cube (Vector version) 335 void DrawCubeV(Vector3 position, Vector3 size, Color color) 336 { 337 DrawCube(position, size.x, size.y, size.z, color); 338 } 339 340 // Draw cube wires 341 void DrawCubeWires(Vector3 position, float width, float height, float length, Color color) 342 { 343 float x = 0.0f; 344 float y = 0.0f; 345 float z = 0.0f; 346 347 rlPushMatrix(); 348 rlTranslatef(position.x, position.y, position.z); 349 350 rlBegin(RL_LINES); 351 rlColor4ub(color.r, color.g, color.b, color.a); 352 353 // Front face 354 //------------------------------------------------------------------ 355 // Bottom line 356 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom left 357 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom right 358 359 // Left line 360 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom right 361 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top right 362 363 // Top line 364 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top right 365 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top left 366 367 // Right line 368 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top left 369 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom left 370 371 // Back face 372 //------------------------------------------------------------------ 373 // Bottom line 374 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom left 375 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom right 376 377 // Left line 378 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom right 379 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top right 380 381 // Top line 382 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top right 383 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top left 384 385 // Right line 386 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top left 387 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom left 388 389 // Top face 390 //------------------------------------------------------------------ 391 // Left line 392 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top left front 393 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top left back 394 395 // Right line 396 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top right front 397 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top right back 398 399 // Bottom face 400 //------------------------------------------------------------------ 401 // Left line 402 rlVertex3f(x - width/2, y - height/2, z + length/2); // Top left front 403 rlVertex3f(x - width/2, y - height/2, z - length/2); // Top left back 404 405 // Right line 406 rlVertex3f(x + width/2, y - height/2, z + length/2); // Top right front 407 rlVertex3f(x + width/2, y - height/2, z - length/2); // Top right back 408 rlEnd(); 409 rlPopMatrix(); 410 } 411 412 // Draw cube wires (vector version) 413 void DrawCubeWiresV(Vector3 position, Vector3 size, Color color) 414 { 415 DrawCubeWires(position, size.x, size.y, size.z, color); 416 } 417 418 // Draw sphere 419 void DrawSphere(Vector3 centerPos, float radius, Color color) 420 { 421 DrawSphereEx(centerPos, radius, 16, 16, color); 422 } 423 424 // Draw sphere with extended parameters 425 void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) 426 { 427 #if 0 428 // Basic implementation, do not use it! 429 // For a sphere with 16 rings and 16 slices it requires 8640 cos()/sin() function calls! 430 // New optimized version below only requires 4 cos()/sin() calls 431 432 rlPushMatrix(); 433 // NOTE: Transformation is applied in inverse order (scale -> translate) 434 rlTranslatef(centerPos.x, centerPos.y, centerPos.z); 435 rlScalef(radius, radius, radius); 436 437 rlBegin(RL_TRIANGLES); 438 rlColor4ub(color.r, color.g, color.b, color.a); 439 440 for (int i = 0; i < (rings + 2); i++) 441 { 442 for (int j = 0; j < slices; j++) 443 { 444 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), 445 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), 446 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); 447 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), 448 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), 449 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); 450 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)), 451 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), 452 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices))); 453 454 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), 455 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), 456 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); 457 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), 458 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i))), 459 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); 460 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), 461 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), 462 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); 463 } 464 } 465 rlEnd(); 466 rlPopMatrix(); 467 #endif 468 469 rlPushMatrix(); 470 // NOTE: Transformation is applied in inverse order (scale -> translate) 471 rlTranslatef(centerPos.x, centerPos.y, centerPos.z); 472 rlScalef(radius, radius, radius); 473 474 rlBegin(RL_TRIANGLES); 475 rlColor4ub(color.r, color.g, color.b, color.a); 476 477 float ringangle = DEG2RAD*(180.0f/(rings + 1)); // Angle between latitudinal parallels 478 float sliceangle = DEG2RAD*(360.0f/slices); // Angle between longitudinal meridians 479 480 float cosring = cosf(ringangle); 481 float sinring = sinf(ringangle); 482 float cosslice = cosf(sliceangle); 483 float sinslice = sinf(sliceangle); 484 485 Vector3 vertices[4] = { 0 }; // Required to store face vertices 486 vertices[2] = (Vector3){ 0, 1, 0 }; 487 vertices[3] = (Vector3){ sinring, cosring, 0 }; 488 489 for (int i = 0; i < rings + 1; i++) 490 { 491 for (int j = 0; j < slices; j++) 492 { 493 vertices[0] = vertices[2]; // Rotate around y axis to set up vertices for next face 494 vertices[1] = vertices[3]; 495 vertices[2] = (Vector3){ cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z }; // Rotation matrix around y axis 496 vertices[3] = (Vector3){ cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z }; 497 498 rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); 499 rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); 500 rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z); 501 502 rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); 503 rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z); 504 rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); 505 } 506 507 vertices[2] = vertices[3]; // Rotate around z axis to set up starting vertices for next ring 508 vertices[3] = (Vector3){ cosring*vertices[3].x + sinring*vertices[3].y, -sinring*vertices[3].x + cosring*vertices[3].y, vertices[3].z }; // Rotation matrix around z axis 509 } 510 rlEnd(); 511 rlPopMatrix(); 512 } 513 514 // Draw sphere wires 515 void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color) 516 { 517 rlPushMatrix(); 518 // NOTE: Transformation is applied in inverse order (scale -> translate) 519 rlTranslatef(centerPos.x, centerPos.y, centerPos.z); 520 rlScalef(radius, radius, radius); 521 522 rlBegin(RL_LINES); 523 rlColor4ub(color.r, color.g, color.b, color.a); 524 525 for (int i = 0; i < (rings + 2); i++) 526 { 527 for (int j = 0; j < slices; j++) 528 { 529 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), 530 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), 531 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); 532 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), 533 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), 534 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); 535 536 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), 537 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), 538 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); 539 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)), 540 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), 541 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices))); 542 543 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)), 544 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), 545 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices))); 546 rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), 547 sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), 548 cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); 549 } 550 } 551 rlEnd(); 552 rlPopMatrix(); 553 } 554 555 // Draw a cylinder 556 // NOTE: It could be also used for pyramid and cone 557 void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) 558 { 559 if (sides < 3) sides = 3; 560 561 const float angleStep = 360.0f/sides; 562 563 rlPushMatrix(); 564 rlTranslatef(position.x, position.y, position.z); 565 566 rlBegin(RL_TRIANGLES); 567 rlColor4ub(color.r, color.g, color.b, color.a); 568 569 if (radiusTop > 0) 570 { 571 // Draw Body ------------------------------------------------------------------------------------- 572 for (int i = 0; i < sides; i++) 573 { 574 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); //Bottom Left 575 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom); //Bottom Right 576 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); //Top Right 577 578 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop); //Top Left 579 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); //Bottom Left 580 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); //Top Right 581 } 582 583 // Draw Cap -------------------------------------------------------------------------------------- 584 for (int i = 0; i < sides; i++) 585 { 586 rlVertex3f(0, height, 0); 587 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop); 588 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); 589 } 590 } 591 else 592 { 593 // Draw Cone ------------------------------------------------------------------------------------- 594 for (int i = 0; i < sides; i++) 595 { 596 rlVertex3f(0, height, 0); 597 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); 598 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom); 599 } 600 } 601 602 // Draw Base ----------------------------------------------------------------------------------------- 603 for (int i = 0; i < sides; i++) 604 { 605 rlVertex3f(0, 0, 0); 606 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom); 607 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); 608 } 609 610 rlEnd(); 611 rlPopMatrix(); 612 } 613 614 // Draw a cylinder with base at startPos and top at endPos 615 // NOTE: It could be also used for pyramid and cone 616 void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color) 617 { 618 if (sides < 3) sides = 3; 619 620 Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z }; 621 if ((direction.x == 0) && (direction.y == 0) && (direction.z == 0)) return; // Security check 622 623 // Construct a basis of the base and the top face: 624 Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction)); 625 Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction)); 626 627 float baseAngle = (2.0f*PI)/sides; 628 629 rlBegin(RL_TRIANGLES); 630 rlColor4ub(color.r, color.g, color.b, color.a); 631 632 for (int i = 0; i < sides; i++) 633 { 634 // Compute the four vertices 635 float s1 = sinf(baseAngle*(i + 0))*startRadius; 636 float c1 = cosf(baseAngle*(i + 0))*startRadius; 637 Vector3 w1 = { startPos.x + s1*b1.x + c1*b2.x, startPos.y + s1*b1.y + c1*b2.y, startPos.z + s1*b1.z + c1*b2.z }; 638 float s2 = sinf(baseAngle*(i + 1))*startRadius; 639 float c2 = cosf(baseAngle*(i + 1))*startRadius; 640 Vector3 w2 = { startPos.x + s2*b1.x + c2*b2.x, startPos.y + s2*b1.y + c2*b2.y, startPos.z + s2*b1.z + c2*b2.z }; 641 float s3 = sinf(baseAngle*(i + 0))*endRadius; 642 float c3 = cosf(baseAngle*(i + 0))*endRadius; 643 Vector3 w3 = { endPos.x + s3*b1.x + c3*b2.x, endPos.y + s3*b1.y + c3*b2.y, endPos.z + s3*b1.z + c3*b2.z }; 644 float s4 = sinf(baseAngle*(i + 1))*endRadius; 645 float c4 = cosf(baseAngle*(i + 1))*endRadius; 646 Vector3 w4 = { endPos.x + s4*b1.x + c4*b2.x, endPos.y + s4*b1.y + c4*b2.y, endPos.z + s4*b1.z + c4*b2.z }; 647 648 if (startRadius > 0) 649 { 650 rlVertex3f(startPos.x, startPos.y, startPos.z); // | 651 rlVertex3f(w2.x, w2.y, w2.z); // T0 652 rlVertex3f(w1.x, w1.y, w1.z); // | 653 } 654 // w2 x.-----------x startPos 655 rlVertex3f(w1.x, w1.y, w1.z); // | |\'. T0 / 656 rlVertex3f(w2.x, w2.y, w2.z); // T1 | \ '. / 657 rlVertex3f(w3.x, w3.y, w3.z); // | |T \ '. / 658 // | 2 \ T 'x w1 659 rlVertex3f(w2.x, w2.y, w2.z); // | w4 x.---\-1-|---x endPos 660 rlVertex3f(w4.x, w4.y, w4.z); // T2 '. \ |T3/ 661 rlVertex3f(w3.x, w3.y, w3.z); // | '. \ | / 662 // '.\|/ 663 if (endRadius > 0) // 'x w3 664 { 665 rlVertex3f(endPos.x, endPos.y, endPos.z); // | 666 rlVertex3f(w3.x, w3.y, w3.z); // T3 667 rlVertex3f(w4.x, w4.y, w4.z); // | 668 } // 669 } 670 rlEnd(); 671 } 672 673 // Draw a wired cylinder 674 // NOTE: It could be also used for pyramid and cone 675 void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color) 676 { 677 if (sides < 3) sides = 3; 678 679 const float angleStep = 360.0f/sides; 680 681 rlPushMatrix(); 682 rlTranslatef(position.x, position.y, position.z); 683 684 rlBegin(RL_LINES); 685 rlColor4ub(color.r, color.g, color.b, color.a); 686 687 for (int i = 0; i < sides; i++) 688 { 689 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); 690 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom); 691 692 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom); 693 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); 694 695 rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); 696 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop); 697 698 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop); 699 rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); 700 } 701 rlEnd(); 702 rlPopMatrix(); 703 } 704 705 // Draw a wired cylinder with base at startPos and top at endPos 706 // NOTE: It could be also used for pyramid and cone 707 void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color) 708 { 709 if (sides < 3) sides = 3; 710 711 Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z }; 712 if ((direction.x == 0) && (direction.y == 0) && (direction.z == 0)) return; // Security check 713 714 // Construct a basis of the base and the top face: 715 Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction)); 716 Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction)); 717 718 float baseAngle = (2.0f*PI)/sides; 719 720 rlBegin(RL_LINES); 721 rlColor4ub(color.r, color.g, color.b, color.a); 722 723 for (int i = 0; i < sides; i++) 724 { 725 // Compute the four vertices 726 float s1 = sinf(baseAngle*(i + 0))*startRadius; 727 float c1 = cosf(baseAngle*(i + 0))*startRadius; 728 Vector3 w1 = { startPos.x + s1*b1.x + c1*b2.x, startPos.y + s1*b1.y + c1*b2.y, startPos.z + s1*b1.z + c1*b2.z }; 729 float s2 = sinf(baseAngle*(i + 1))*startRadius; 730 float c2 = cosf(baseAngle*(i + 1))*startRadius; 731 Vector3 w2 = { startPos.x + s2*b1.x + c2*b2.x, startPos.y + s2*b1.y + c2*b2.y, startPos.z + s2*b1.z + c2*b2.z }; 732 float s3 = sinf(baseAngle*(i + 0))*endRadius; 733 float c3 = cosf(baseAngle*(i + 0))*endRadius; 734 Vector3 w3 = { endPos.x + s3*b1.x + c3*b2.x, endPos.y + s3*b1.y + c3*b2.y, endPos.z + s3*b1.z + c3*b2.z }; 735 float s4 = sinf(baseAngle*(i + 1))*endRadius; 736 float c4 = cosf(baseAngle*(i + 1))*endRadius; 737 Vector3 w4 = { endPos.x + s4*b1.x + c4*b2.x, endPos.y + s4*b1.y + c4*b2.y, endPos.z + s4*b1.z + c4*b2.z }; 738 739 rlVertex3f(w1.x, w1.y, w1.z); 740 rlVertex3f(w2.x, w2.y, w2.z); 741 742 rlVertex3f(w1.x, w1.y, w1.z); 743 rlVertex3f(w3.x, w3.y, w3.z); 744 745 rlVertex3f(w3.x, w3.y, w3.z); 746 rlVertex3f(w4.x, w4.y, w4.z); 747 } 748 rlEnd(); 749 } 750 751 // Draw a capsule with the center of its sphere caps at startPos and endPos 752 void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color) 753 { 754 if (slices < 3) slices = 3; 755 756 Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z }; 757 758 // draw a sphere if start and end points are the same 759 bool sphereCase = (direction.x == 0) && (direction.y == 0) && (direction.z == 0); 760 if (sphereCase) direction = (Vector3){0.0f, 1.0f, 0.0f}; 761 762 // Construct a basis of the base and the caps: 763 Vector3 b0 = Vector3Normalize(direction); 764 Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction)); 765 Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction)); 766 Vector3 capCenter = endPos; 767 768 float baseSliceAngle = (2.0f*PI)/slices; 769 float baseRingAngle = PI*0.5f/rings; 770 771 rlBegin(RL_TRIANGLES); 772 rlColor4ub(color.r, color.g, color.b, color.a); 773 774 // render both caps 775 for (int c = 0; c < 2; c++) 776 { 777 for (int i = 0; i < rings; i++) 778 { 779 for (int j = 0; j < slices; j++) 780 { 781 782 // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier 783 784 // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i)) 785 // as we iterate through the rings they must get smaller by the cos(angle(i)) 786 787 // compute the four vertices 788 float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); 789 float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); 790 Vector3 w1 = (Vector3){ 791 capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin1*b1.x + ringCos1*b2.x)*radius, 792 capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin1*b1.y + ringCos1*b2.y)*radius, 793 capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin1*b1.z + ringCos1*b2.z)*radius 794 }; 795 float ringSin2 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 )); 796 float ringCos2 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 )); 797 Vector3 w2 = (Vector3){ 798 capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin2*b1.x + ringCos2*b2.x)*radius, 799 capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin2*b1.y + ringCos2*b2.y)*radius, 800 capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin2*b1.z + ringCos2*b2.z)*radius 801 }; 802 803 float ringSin3 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 )); 804 float ringCos3 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 )); 805 Vector3 w3 = (Vector3){ 806 capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin3*b1.x + ringCos3*b2.x)*radius, 807 capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin3*b1.y + ringCos3*b2.y)*radius, 808 capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin3*b1.z + ringCos3*b2.z)*radius 809 }; 810 float ringSin4 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 )); 811 float ringCos4 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 )); 812 Vector3 w4 = (Vector3){ 813 capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin4*b1.x + ringCos4*b2.x)*radius, 814 capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin4*b1.y + ringCos4*b2.y)*radius, 815 capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin4*b1.z + ringCos4*b2.z)*radius 816 }; 817 818 // Make sure cap triangle normals are facing outwards 819 if (c == 0) 820 { 821 rlVertex3f(w1.x, w1.y, w1.z); 822 rlVertex3f(w2.x, w2.y, w2.z); 823 rlVertex3f(w3.x, w3.y, w3.z); 824 825 rlVertex3f(w2.x, w2.y, w2.z); 826 rlVertex3f(w4.x, w4.y, w4.z); 827 rlVertex3f(w3.x, w3.y, w3.z); 828 } 829 else 830 { 831 rlVertex3f(w1.x, w1.y, w1.z); 832 rlVertex3f(w3.x, w3.y, w3.z); 833 rlVertex3f(w2.x, w2.y, w2.z); 834 835 rlVertex3f(w2.x, w2.y, w2.z); 836 rlVertex3f(w3.x, w3.y, w3.z); 837 rlVertex3f(w4.x, w4.y, w4.z); 838 } 839 } 840 } 841 capCenter = startPos; 842 b0 = Vector3Scale(b0, -1.0f); 843 } 844 // render middle 845 if (!sphereCase) 846 { 847 for (int j = 0; j < slices; j++) 848 { 849 // compute the four vertices 850 float ringSin1 = sinf(baseSliceAngle*(j + 0))*radius; 851 float ringCos1 = cosf(baseSliceAngle*(j + 0))*radius; 852 Vector3 w1 = { 853 startPos.x + ringSin1*b1.x + ringCos1*b2.x, 854 startPos.y + ringSin1*b1.y + ringCos1*b2.y, 855 startPos.z + ringSin1*b1.z + ringCos1*b2.z 856 }; 857 float ringSin2 = sinf(baseSliceAngle*(j + 1))*radius; 858 float ringCos2 = cosf(baseSliceAngle*(j + 1))*radius; 859 Vector3 w2 = { 860 startPos.x + ringSin2*b1.x + ringCos2*b2.x, 861 startPos.y + ringSin2*b1.y + ringCos2*b2.y, 862 startPos.z + ringSin2*b1.z + ringCos2*b2.z 863 }; 864 865 float ringSin3 = sinf(baseSliceAngle*(j + 0))*radius; 866 float ringCos3 = cosf(baseSliceAngle*(j + 0))*radius; 867 Vector3 w3 = { 868 endPos.x + ringSin3*b1.x + ringCos3*b2.x, 869 endPos.y + ringSin3*b1.y + ringCos3*b2.y, 870 endPos.z + ringSin3*b1.z + ringCos3*b2.z 871 }; 872 float ringSin4 = sinf(baseSliceAngle*(j + 1))*radius; 873 float ringCos4 = cosf(baseSliceAngle*(j + 1))*radius; 874 Vector3 w4 = { 875 endPos.x + ringSin4*b1.x + ringCos4*b2.x, 876 endPos.y + ringSin4*b1.y + ringCos4*b2.y, 877 endPos.z + ringSin4*b1.z + ringCos4*b2.z 878 }; 879 // w2 x.-----------x startPos 880 rlVertex3f(w1.x, w1.y, w1.z); // | |\'. T0 / 881 rlVertex3f(w2.x, w2.y, w2.z); // T1 | \ '. / 882 rlVertex3f(w3.x, w3.y, w3.z); // | |T \ '. / 883 // | 2 \ T 'x w1 884 rlVertex3f(w2.x, w2.y, w2.z); // | w4 x.---\-1-|---x endPos 885 rlVertex3f(w4.x, w4.y, w4.z); // T2 '. \ |T3/ 886 rlVertex3f(w3.x, w3.y, w3.z); // | '. \ | / 887 // '.\|/ 888 // 'x w3 889 } 890 } 891 rlEnd(); 892 } 893 894 // Draw capsule wires with the center of its sphere caps at startPos and endPos 895 void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color) 896 { 897 if (slices < 3) slices = 3; 898 899 Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z }; 900 901 // draw a sphere if start and end points are the same 902 bool sphereCase = (direction.x == 0) && (direction.y == 0) && (direction.z == 0); 903 if (sphereCase) direction = (Vector3){0.0f, 1.0f, 0.0f}; 904 905 // Construct a basis of the base and the caps: 906 Vector3 b0 = Vector3Normalize(direction); 907 Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction)); 908 Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction)); 909 Vector3 capCenter = endPos; 910 911 float baseSliceAngle = (2.0f*PI)/slices; 912 float baseRingAngle = PI*0.5f/rings; 913 914 rlBegin(RL_LINES); 915 rlColor4ub(color.r, color.g, color.b, color.a); 916 917 // render both caps 918 for (int c = 0; c < 2; c++) 919 { 920 for (int i = 0; i < rings; i++) 921 { 922 for (int j = 0; j < slices; j++) 923 { 924 925 // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier 926 927 // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i)) 928 // as we iterate through the rings they must get smaller by the cos(angle(i)) 929 930 // compute the four vertices 931 float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); 932 float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 )); 933 Vector3 w1 = (Vector3){ 934 capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin1*b1.x + ringCos1*b2.x)*radius, 935 capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin1*b1.y + ringCos1*b2.y)*radius, 936 capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin1*b1.z + ringCos1*b2.z)*radius 937 }; 938 float ringSin2 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 )); 939 float ringCos2 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 )); 940 Vector3 w2 = (Vector3){ 941 capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin2*b1.x + ringCos2*b2.x)*radius, 942 capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin2*b1.y + ringCos2*b2.y)*radius, 943 capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin2*b1.z + ringCos2*b2.z)*radius 944 }; 945 946 float ringSin3 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 )); 947 float ringCos3 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 )); 948 Vector3 w3 = (Vector3){ 949 capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin3*b1.x + ringCos3*b2.x)*radius, 950 capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin3*b1.y + ringCos3*b2.y)*radius, 951 capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin3*b1.z + ringCos3*b2.z)*radius 952 }; 953 float ringSin4 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 )); 954 float ringCos4 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 )); 955 Vector3 w4 = (Vector3){ 956 capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin4*b1.x + ringCos4*b2.x)*radius, 957 capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin4*b1.y + ringCos4*b2.y)*radius, 958 capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin4*b1.z + ringCos4*b2.z)*radius 959 }; 960 961 rlVertex3f(w1.x, w1.y, w1.z); 962 rlVertex3f(w2.x, w2.y, w2.z); 963 964 rlVertex3f(w2.x, w2.y, w2.z); 965 rlVertex3f(w3.x, w3.y, w3.z); 966 967 rlVertex3f(w1.x, w1.y, w1.z); 968 rlVertex3f(w3.x, w3.y, w3.z); 969 970 rlVertex3f(w2.x, w2.y, w2.z); 971 rlVertex3f(w4.x, w4.y, w4.z); 972 973 rlVertex3f(w3.x, w3.y, w3.z); 974 rlVertex3f(w4.x, w4.y, w4.z); 975 } 976 } 977 capCenter = startPos; 978 b0 = Vector3Scale(b0, -1.0f); 979 } 980 // render middle 981 if (!sphereCase) 982 { 983 for (int j = 0; j < slices; j++) 984 { 985 // compute the four vertices 986 float ringSin1 = sinf(baseSliceAngle*(j + 0))*radius; 987 float ringCos1 = cosf(baseSliceAngle*(j + 0))*radius; 988 Vector3 w1 = { 989 startPos.x + ringSin1*b1.x + ringCos1*b2.x, 990 startPos.y + ringSin1*b1.y + ringCos1*b2.y, 991 startPos.z + ringSin1*b1.z + ringCos1*b2.z 992 }; 993 float ringSin2 = sinf(baseSliceAngle*(j + 1))*radius; 994 float ringCos2 = cosf(baseSliceAngle*(j + 1))*radius; 995 Vector3 w2 = { 996 startPos.x + ringSin2*b1.x + ringCos2*b2.x, 997 startPos.y + ringSin2*b1.y + ringCos2*b2.y, 998 startPos.z + ringSin2*b1.z + ringCos2*b2.z 999 }; 1000 1001 float ringSin3 = sinf(baseSliceAngle*(j + 0))*radius; 1002 float ringCos3 = cosf(baseSliceAngle*(j + 0))*radius; 1003 Vector3 w3 = { 1004 endPos.x + ringSin3*b1.x + ringCos3*b2.x, 1005 endPos.y + ringSin3*b1.y + ringCos3*b2.y, 1006 endPos.z + ringSin3*b1.z + ringCos3*b2.z 1007 }; 1008 float ringSin4 = sinf(baseSliceAngle*(j + 1))*radius; 1009 float ringCos4 = cosf(baseSliceAngle*(j + 1))*radius; 1010 Vector3 w4 = { 1011 endPos.x + ringSin4*b1.x + ringCos4*b2.x, 1012 endPos.y + ringSin4*b1.y + ringCos4*b2.y, 1013 endPos.z + ringSin4*b1.z + ringCos4*b2.z 1014 }; 1015 1016 rlVertex3f(w1.x, w1.y, w1.z); 1017 rlVertex3f(w3.x, w3.y, w3.z); 1018 1019 rlVertex3f(w2.x, w2.y, w2.z); 1020 rlVertex3f(w4.x, w4.y, w4.z); 1021 1022 rlVertex3f(w2.x, w2.y, w2.z); 1023 rlVertex3f(w3.x, w3.y, w3.z); 1024 } 1025 } 1026 rlEnd(); 1027 } 1028 1029 // Draw a plane 1030 void DrawPlane(Vector3 centerPos, Vector2 size, Color color) 1031 { 1032 // NOTE: Plane is always created on XZ ground 1033 rlPushMatrix(); 1034 rlTranslatef(centerPos.x, centerPos.y, centerPos.z); 1035 rlScalef(size.x, 1.0f, size.y); 1036 1037 rlBegin(RL_QUADS); 1038 rlColor4ub(color.r, color.g, color.b, color.a); 1039 rlNormal3f(0.0f, 1.0f, 0.0f); 1040 1041 rlVertex3f(-0.5f, 0.0f, -0.5f); 1042 rlVertex3f(-0.5f, 0.0f, 0.5f); 1043 rlVertex3f(0.5f, 0.0f, 0.5f); 1044 rlVertex3f(0.5f, 0.0f, -0.5f); 1045 rlEnd(); 1046 rlPopMatrix(); 1047 } 1048 1049 // Draw a ray line 1050 void DrawRay(Ray ray, Color color) 1051 { 1052 float scale = 10000; 1053 1054 rlBegin(RL_LINES); 1055 rlColor4ub(color.r, color.g, color.b, color.a); 1056 rlColor4ub(color.r, color.g, color.b, color.a); 1057 1058 rlVertex3f(ray.position.x, ray.position.y, ray.position.z); 1059 rlVertex3f(ray.position.x + ray.direction.x*scale, ray.position.y + ray.direction.y*scale, ray.position.z + ray.direction.z*scale); 1060 rlEnd(); 1061 } 1062 1063 // Draw a grid centered at (0, 0, 0) 1064 void DrawGrid(int slices, float spacing) 1065 { 1066 int halfSlices = slices/2; 1067 1068 rlBegin(RL_LINES); 1069 for (int i = -halfSlices; i <= halfSlices; i++) 1070 { 1071 if (i == 0) 1072 { 1073 rlColor3f(0.5f, 0.5f, 0.5f); 1074 } 1075 else 1076 { 1077 rlColor3f(0.75f, 0.75f, 0.75f); 1078 } 1079 1080 rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing); 1081 rlVertex3f((float)i*spacing, 0.0f, (float)halfSlices*spacing); 1082 1083 rlVertex3f((float)-halfSlices*spacing, 0.0f, (float)i*spacing); 1084 rlVertex3f((float)halfSlices*spacing, 0.0f, (float)i*spacing); 1085 } 1086 rlEnd(); 1087 } 1088 1089 // Load model from files (mesh and material) 1090 Model LoadModel(const char *fileName) 1091 { 1092 Model model = { 0 }; 1093 1094 #if defined(SUPPORT_FILEFORMAT_OBJ) 1095 if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName); 1096 #endif 1097 #if defined(SUPPORT_FILEFORMAT_IQM) 1098 if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); 1099 #endif 1100 #if defined(SUPPORT_FILEFORMAT_GLTF) 1101 if (IsFileExtension(fileName, ".gltf") || IsFileExtension(fileName, ".glb")) model = LoadGLTF(fileName); 1102 #endif 1103 #if defined(SUPPORT_FILEFORMAT_VOX) 1104 if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName); 1105 #endif 1106 #if defined(SUPPORT_FILEFORMAT_M3D) 1107 if (IsFileExtension(fileName, ".m3d")) model = LoadM3D(fileName); 1108 #endif 1109 1110 // Make sure model transform is set to identity matrix! 1111 model.transform = MatrixIdentity(); 1112 1113 if ((model.meshCount != 0) && (model.meshes != NULL)) 1114 { 1115 // Upload vertex data to GPU (static meshes) 1116 for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false); 1117 } 1118 else TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load model mesh(es) data", fileName); 1119 1120 if (model.materialCount == 0) 1121 { 1122 TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load model material data, default to white material", fileName); 1123 1124 model.materialCount = 1; 1125 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); 1126 model.materials[0] = LoadMaterialDefault(); 1127 1128 if (model.meshMaterial == NULL) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); 1129 } 1130 1131 return model; 1132 } 1133 1134 // Load model from generated mesh 1135 // WARNING: A shallow copy of mesh is generated, passed by value, 1136 // as long as struct contains pointers to data and some values, we get a copy 1137 // of mesh pointing to same data as original version... be careful! 1138 Model LoadModelFromMesh(Mesh mesh) 1139 { 1140 Model model = { 0 }; 1141 1142 model.transform = MatrixIdentity(); 1143 1144 model.meshCount = 1; 1145 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); 1146 model.meshes[0] = mesh; 1147 1148 model.materialCount = 1; 1149 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); 1150 model.materials[0] = LoadMaterialDefault(); 1151 1152 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); 1153 model.meshMaterial[0] = 0; // First material index 1154 1155 return model; 1156 } 1157 1158 // Check if a model is valid (loaded in GPU, VAO/VBOs) 1159 bool IsModelValid(Model model) 1160 { 1161 bool result = false; 1162 1163 if ((model.meshes != NULL) && // Validate model contains some mesh 1164 (model.materials != NULL) && // Validate model contains some material (at least default one) 1165 (model.meshMaterial != NULL) && // Validate mesh-material linkage 1166 (model.meshCount > 0) && // Validate mesh count 1167 (model.materialCount > 0)) result = true; // Validate material count 1168 1169 // NOTE: Many elements could be validated from a model, including every model mesh VAO/VBOs 1170 // but some VBOs could not be used, it depends on Mesh vertex data 1171 for (int i = 0; i < model.meshCount; i++) 1172 { 1173 if ((model.meshes[i].vertices != NULL) && (model.meshes[i].vboId[0] == 0)) { result = false; break; } // Vertex position buffer not uploaded to GPU 1174 if ((model.meshes[i].texcoords != NULL) && (model.meshes[i].vboId[1] == 0)) { result = false; break; } // Vertex textcoords buffer not uploaded to GPU 1175 if ((model.meshes[i].normals != NULL) && (model.meshes[i].vboId[2] == 0)) { result = false; break; } // Vertex normals buffer not uploaded to GPU 1176 if ((model.meshes[i].colors != NULL) && (model.meshes[i].vboId[3] == 0)) { result = false; break; } // Vertex colors buffer not uploaded to GPU 1177 if ((model.meshes[i].tangents != NULL) && (model.meshes[i].vboId[4] == 0)) { result = false; break; } // Vertex tangents buffer not uploaded to GPU 1178 if ((model.meshes[i].texcoords2 != NULL) && (model.meshes[i].vboId[5] == 0)) { result = false; break; } // Vertex texcoords2 buffer not uploaded to GPU 1179 if ((model.meshes[i].indices != NULL) && (model.meshes[i].vboId[6] == 0)) { result = false; break; } // Vertex indices buffer not uploaded to GPU 1180 if ((model.meshes[i].boneIds != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; } // Vertex boneIds buffer not uploaded to GPU 1181 if ((model.meshes[i].boneWeights != NULL) && (model.meshes[i].vboId[8] == 0)) { result = false; break; } // Vertex boneWeights buffer not uploaded to GPU 1182 1183 // NOTE: Some OpenGL versions do not support VAO, so we don't check it 1184 //if (model.meshes[i].vaoId == 0) { result = false; break } 1185 } 1186 1187 return result; 1188 } 1189 1190 // Unload model (meshes/materials) from memory (RAM and/or VRAM) 1191 // NOTE: This function takes care of all model elements, for a detailed control 1192 // over them, use UnloadMesh() and UnloadMaterial() 1193 void UnloadModel(Model model) 1194 { 1195 // Unload meshes 1196 for (int i = 0; i < model.meshCount; i++) UnloadMesh(model.meshes[i]); 1197 1198 // Unload materials maps 1199 // NOTE: As the user could be sharing shaders and textures between models, 1200 // we don't unload the material but just free its maps, 1201 // the user is responsible for freeing models shaders and textures 1202 for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps); 1203 1204 // Unload arrays 1205 RL_FREE(model.meshes); 1206 RL_FREE(model.materials); 1207 RL_FREE(model.meshMaterial); 1208 1209 // Unload animation data 1210 RL_FREE(model.bones); 1211 RL_FREE(model.bindPose); 1212 1213 TRACELOG(LOG_INFO, "MODEL: Unloaded model (and meshes) from RAM and VRAM"); 1214 } 1215 1216 // Compute model bounding box limits (considers all meshes) 1217 BoundingBox GetModelBoundingBox(Model model) 1218 { 1219 BoundingBox bounds = { 0 }; 1220 1221 if (model.meshCount > 0) 1222 { 1223 Vector3 temp = { 0 }; 1224 bounds = GetMeshBoundingBox(model.meshes[0]); 1225 1226 for (int i = 1; i < model.meshCount; i++) 1227 { 1228 BoundingBox tempBounds = GetMeshBoundingBox(model.meshes[i]); 1229 1230 temp.x = (bounds.min.x < tempBounds.min.x)? bounds.min.x : tempBounds.min.x; 1231 temp.y = (bounds.min.y < tempBounds.min.y)? bounds.min.y : tempBounds.min.y; 1232 temp.z = (bounds.min.z < tempBounds.min.z)? bounds.min.z : tempBounds.min.z; 1233 bounds.min = temp; 1234 1235 temp.x = (bounds.max.x > tempBounds.max.x)? bounds.max.x : tempBounds.max.x; 1236 temp.y = (bounds.max.y > tempBounds.max.y)? bounds.max.y : tempBounds.max.y; 1237 temp.z = (bounds.max.z > tempBounds.max.z)? bounds.max.z : tempBounds.max.z; 1238 bounds.max = temp; 1239 } 1240 } 1241 1242 // Apply model.transform to bounding box 1243 // WARNING: Current BoundingBox structure design does not support rotation transformations, 1244 // in those cases is up to the user to calculate the proper box bounds (8 vertices transformed) 1245 bounds.min = Vector3Transform(bounds.min, model.transform); 1246 bounds.max = Vector3Transform(bounds.max, model.transform); 1247 1248 return bounds; 1249 } 1250 1251 // Upload vertex data into a VAO (if supported) and VBO 1252 void UploadMesh(Mesh *mesh, bool dynamic) 1253 { 1254 if (mesh->vaoId > 0) 1255 { 1256 // Check if mesh has already been loaded in GPU 1257 TRACELOG(LOG_WARNING, "VAO: [ID %i] Trying to re-load an already loaded mesh", mesh->vaoId); 1258 return; 1259 } 1260 1261 mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int)); 1262 1263 mesh->vaoId = 0; // Vertex Array Object 1264 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = 0; // Vertex buffer: positions 1265 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = 0; // Vertex buffer: texcoords 1266 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = 0; // Vertex buffer: normals 1267 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = 0; // Vertex buffer: colors 1268 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0; // Vertex buffer: tangents 1269 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0; // Vertex buffer: texcoords2 1270 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0; // Vertex buffer: indices 1271 1272 #ifdef RL_SUPPORT_MESH_GPU_SKINNING 1273 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = 0; // Vertex buffer: boneIds 1274 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0; // Vertex buffer: boneWeights 1275 #endif 1276 1277 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) 1278 mesh->vaoId = rlLoadVertexArray(); 1279 rlEnableVertexArray(mesh->vaoId); 1280 1281 // NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data 1282 1283 // Enable vertex attributes: position (shader-location = 0) 1284 void *vertices = (mesh->animVertices != NULL)? mesh->animVertices : mesh->vertices; 1285 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic); 1286 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, RL_FLOAT, 0, 0, 0); 1287 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION); 1288 1289 // Enable vertex attributes: texcoords (shader-location = 1) 1290 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic); 1291 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0); 1292 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); 1293 1294 // WARNING: When setting default vertex attribute values, the values for each generic vertex attribute 1295 // is part of current state, and it is maintained even if a different program object is used 1296 1297 if (mesh->normals != NULL) 1298 { 1299 // Enable vertex attributes: normals (shader-location = 2) 1300 void *normals = (mesh->animNormals != NULL)? mesh->animNormals : mesh->normals; 1301 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic); 1302 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, 3, RL_FLOAT, 0, 0, 0); 1303 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL); 1304 } 1305 else 1306 { 1307 // Default vertex attribute: normal 1308 // WARNING: Default value provided to shader if location available 1309 float value[3] = { 1.0f, 1.0f, 1.0f }; 1310 rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, value, SHADER_ATTRIB_VEC3, 3); 1311 rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL); 1312 } 1313 1314 if (mesh->colors != NULL) 1315 { 1316 // Enable vertex attribute: color (shader-location = 3) 1317 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = rlLoadVertexBuffer(mesh->colors, mesh->vertexCount*4*sizeof(unsigned char), dynamic); 1318 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, 4, RL_UNSIGNED_BYTE, 1, 0, 0); 1319 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR); 1320 } 1321 else 1322 { 1323 // Default vertex attribute: color 1324 // WARNING: Default value provided to shader if location available 1325 float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // WHITE 1326 rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, value, SHADER_ATTRIB_VEC4, 4); 1327 rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR); 1328 } 1329 1330 if (mesh->tangents != NULL) 1331 { 1332 // Enable vertex attribute: tangent (shader-location = 4) 1333 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), dynamic); 1334 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0); 1335 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT); 1336 } 1337 else 1338 { 1339 // Default vertex attribute: tangent 1340 // WARNING: Default value provided to shader if location available 1341 float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; 1342 rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, value, SHADER_ATTRIB_VEC4, 4); 1343 rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT); 1344 } 1345 1346 if (mesh->texcoords2 != NULL) 1347 { 1348 // Enable vertex attribute: texcoord2 (shader-location = 5) 1349 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = rlLoadVertexBuffer(mesh->texcoords2, mesh->vertexCount*2*sizeof(float), dynamic); 1350 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, 2, RL_FLOAT, 0, 0, 0); 1351 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2); 1352 } 1353 else 1354 { 1355 // Default vertex attribute: texcoord2 1356 // WARNING: Default value provided to shader if location available 1357 float value[2] = { 0.0f, 0.0f }; 1358 rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, value, SHADER_ATTRIB_VEC2, 2); 1359 rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2); 1360 } 1361 1362 #ifdef RL_SUPPORT_MESH_GPU_SKINNING 1363 if (mesh->boneIds != NULL) 1364 { 1365 // Enable vertex attribute: boneIds (shader-location = 7) 1366 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = rlLoadVertexBuffer(mesh->boneIds, mesh->vertexCount*4*sizeof(unsigned char), dynamic); 1367 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, 4, RL_UNSIGNED_BYTE, 0, 0, 0); 1368 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS); 1369 } 1370 else 1371 { 1372 // Default vertex attribute: boneIds 1373 // WARNING: Default value provided to shader if location available 1374 float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; 1375 rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, value, SHADER_ATTRIB_VEC4, 4); 1376 rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS); 1377 } 1378 1379 if (mesh->boneWeights != NULL) 1380 { 1381 // Enable vertex attribute: boneWeights (shader-location = 8) 1382 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = rlLoadVertexBuffer(mesh->boneWeights, mesh->vertexCount*4*sizeof(float), dynamic); 1383 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, 4, RL_FLOAT, 0, 0, 0); 1384 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS); 1385 } 1386 else 1387 { 1388 // Default vertex attribute: boneWeights 1389 // WARNING: Default value provided to shader if location available 1390 float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; 1391 rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, value, SHADER_ATTRIB_VEC4, 2); 1392 rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS); 1393 } 1394 #endif 1395 1396 if (mesh->indices != NULL) 1397 { 1398 mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = rlLoadVertexBufferElement(mesh->indices, mesh->triangleCount*3*sizeof(unsigned short), dynamic); 1399 } 1400 1401 if (mesh->vaoId > 0) TRACELOG(LOG_INFO, "VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId); 1402 else TRACELOG(LOG_INFO, "VBO: Mesh uploaded successfully to VRAM (GPU)"); 1403 1404 rlDisableVertexArray(); 1405 #endif 1406 } 1407 1408 // Update mesh vertex data in GPU for a specific buffer index 1409 void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset) 1410 { 1411 rlUpdateVertexBuffer(mesh.vboId[index], data, dataSize, offset); 1412 } 1413 1414 // Draw a 3d mesh with material and transform 1415 void DrawMesh(Mesh mesh, Material material, Matrix transform) 1416 { 1417 #if defined(GRAPHICS_API_OPENGL_11) 1418 #define GL_VERTEX_ARRAY 0x8074 1419 #define GL_NORMAL_ARRAY 0x8075 1420 #define GL_COLOR_ARRAY 0x8076 1421 #define GL_TEXTURE_COORD_ARRAY 0x8078 1422 1423 rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id); 1424 1425 rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); 1426 rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); 1427 rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); 1428 rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); 1429 1430 rlPushMatrix(); 1431 rlMultMatrixf(MatrixToFloat(transform)); 1432 rlColor4ub(material.maps[MATERIAL_MAP_DIFFUSE].color.r, 1433 material.maps[MATERIAL_MAP_DIFFUSE].color.g, 1434 material.maps[MATERIAL_MAP_DIFFUSE].color.b, 1435 material.maps[MATERIAL_MAP_DIFFUSE].color.a); 1436 1437 if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, mesh.indices); 1438 else rlDrawVertexArray(0, mesh.vertexCount); 1439 rlPopMatrix(); 1440 1441 rlDisableStatePointer(GL_VERTEX_ARRAY); 1442 rlDisableStatePointer(GL_TEXTURE_COORD_ARRAY); 1443 rlDisableStatePointer(GL_NORMAL_ARRAY); 1444 rlDisableStatePointer(GL_COLOR_ARRAY); 1445 1446 rlDisableTexture(); 1447 #endif 1448 1449 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) 1450 // Bind shader program 1451 rlEnableShader(material.shader.id); 1452 1453 // Send required data to shader (matrices, values) 1454 //----------------------------------------------------- 1455 // Upload to shader material.colDiffuse 1456 if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) 1457 { 1458 float values[4] = { 1459 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, 1460 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, 1461 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, 1462 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f 1463 }; 1464 1465 rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1); 1466 } 1467 1468 // Upload to shader material.colSpecular (if location available) 1469 if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) 1470 { 1471 float values[4] = { 1472 (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f, 1473 (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f, 1474 (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f, 1475 (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f 1476 }; 1477 1478 rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1); 1479 } 1480 1481 // Get a copy of current matrices to work with, 1482 // just in case stereo render is required, and we need to modify them 1483 // NOTE: At this point the modelview matrix just contains the view matrix (camera) 1484 // That's because BeginMode3D() sets it and there is no model-drawing function 1485 // that modifies it, all use rlPushMatrix() and rlPopMatrix() 1486 Matrix matModel = MatrixIdentity(); 1487 Matrix matView = rlGetMatrixModelview(); 1488 Matrix matModelView = MatrixIdentity(); 1489 Matrix matProjection = rlGetMatrixProjection(); 1490 1491 // Upload view and projection matrices (if locations available) 1492 if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView); 1493 if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection); 1494 1495 // Accumulate several model transformations: 1496 // transform: model transformation provided (includes DrawModel() params combined with model.transform) 1497 // rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack 1498 matModel = MatrixMultiply(transform, rlGetMatrixTransform()); 1499 1500 // Model transformation matrix is sent to shader uniform location: SHADER_LOC_MATRIX_MODEL 1501 if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], matModel); 1502 1503 // Get model-view matrix 1504 matModelView = MatrixMultiply(matModel, matView); 1505 1506 // Upload model normal matrix (if locations available) 1507 if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); 1508 1509 #ifdef RL_SUPPORT_MESH_GPU_SKINNING 1510 // Upload Bone Transforms 1511 if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices) 1512 { 1513 rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount); 1514 } 1515 #endif 1516 //----------------------------------------------------- 1517 1518 // Bind active texture maps (if available) 1519 for (int i = 0; i < MAX_MATERIAL_MAPS; i++) 1520 { 1521 if (material.maps[i].texture.id > 0) 1522 { 1523 // Select current shader texture slot 1524 rlActiveTextureSlot(i); 1525 1526 // Enable texture for active slot 1527 if ((i == MATERIAL_MAP_IRRADIANCE) || 1528 (i == MATERIAL_MAP_PREFILTER) || 1529 (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id); 1530 else rlEnableTexture(material.maps[i].texture.id); 1531 1532 rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1); 1533 } 1534 } 1535 1536 // Try binding vertex array objects (VAO) or use VBOs if not possible 1537 // WARNING: UploadMesh() enables all vertex attributes available in mesh and sets default attribute values 1538 // for shader expected vertex attributes that are not provided by the mesh (i.e. colors) 1539 // This could be a dangerous approach because different meshes with different shaders can enable/disable some attributes 1540 if (!rlEnableVertexArray(mesh.vaoId)) 1541 { 1542 // Bind mesh VBO data: vertex position (shader-location = 0) 1543 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]); 1544 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0); 1545 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]); 1546 1547 // Bind mesh VBO data: vertex texcoords (shader-location = 1) 1548 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]); 1549 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0); 1550 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); 1551 1552 if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1) 1553 { 1554 // Bind mesh VBO data: vertex normals (shader-location = 2) 1555 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]); 1556 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0); 1557 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]); 1558 } 1559 1560 // Bind mesh VBO data: vertex colors (shader-location = 3, if available) 1561 if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) 1562 { 1563 if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0) 1564 { 1565 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]); 1566 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0); 1567 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); 1568 } 1569 else 1570 { 1571 // Set default value for defined vertex attribute in shader but not provided by mesh 1572 // WARNING: It could result in GPU undefined behaviour 1573 float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; 1574 rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4); 1575 rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); 1576 } 1577 } 1578 1579 // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) 1580 if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1) 1581 { 1582 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]); 1583 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0); 1584 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]); 1585 } 1586 1587 // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) 1588 if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1) 1589 { 1590 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]); 1591 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0); 1592 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); 1593 } 1594 1595 #ifdef RL_SUPPORT_MESH_GPU_SKINNING 1596 // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available) 1597 if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1) 1598 { 1599 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]); 1600 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0); 1601 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]); 1602 } 1603 1604 // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available) 1605 if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1) 1606 { 1607 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]); 1608 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0); 1609 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]); 1610 } 1611 #endif 1612 1613 if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]); 1614 } 1615 1616 int eyeCount = 1; 1617 if (rlIsStereoRenderEnabled()) eyeCount = 2; 1618 1619 for (int eye = 0; eye < eyeCount; eye++) 1620 { 1621 // Calculate model-view-projection matrix (MVP) 1622 Matrix matModelViewProjection = MatrixIdentity(); 1623 if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection); 1624 else 1625 { 1626 // Setup current eye viewport (half screen width) 1627 rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight()); 1628 matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye)); 1629 } 1630 1631 // Send combined model-view-projection matrix to shader 1632 rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection); 1633 1634 // Draw mesh 1635 if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, 0); 1636 else rlDrawVertexArray(0, mesh.vertexCount); 1637 } 1638 1639 // Unbind all bound texture maps 1640 for (int i = 0; i < MAX_MATERIAL_MAPS; i++) 1641 { 1642 if (material.maps[i].texture.id > 0) 1643 { 1644 // Select current shader texture slot 1645 rlActiveTextureSlot(i); 1646 1647 // Disable texture for active slot 1648 if ((i == MATERIAL_MAP_IRRADIANCE) || 1649 (i == MATERIAL_MAP_PREFILTER) || 1650 (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); 1651 else rlDisableTexture(); 1652 } 1653 } 1654 1655 // Disable all possible vertex array objects (or VBOs) 1656 rlDisableVertexArray(); 1657 rlDisableVertexBuffer(); 1658 rlDisableVertexBufferElement(); 1659 1660 // Disable shader program 1661 rlDisableShader(); 1662 1663 // Restore rlgl internal modelview and projection matrices 1664 rlSetMatrixModelview(matView); 1665 rlSetMatrixProjection(matProjection); 1666 #endif 1667 } 1668 1669 // Draw multiple mesh instances with material and different transforms 1670 void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances) 1671 { 1672 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) 1673 // Instancing required variables 1674 float16 *instanceTransforms = NULL; 1675 unsigned int instancesVboId = 0; 1676 1677 // Bind shader program 1678 rlEnableShader(material.shader.id); 1679 1680 // Send required data to shader (matrices, values) 1681 //----------------------------------------------------- 1682 // Upload to shader material.colDiffuse 1683 if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1) 1684 { 1685 float values[4] = { 1686 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f, 1687 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f, 1688 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f, 1689 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f 1690 }; 1691 1692 rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1); 1693 } 1694 1695 // Upload to shader material.colSpecular (if location available) 1696 if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1) 1697 { 1698 float values[4] = { 1699 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.r/255.0f, 1700 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.g/255.0f, 1701 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.b/255.0f, 1702 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.a/255.0f 1703 }; 1704 1705 rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1); 1706 } 1707 1708 // Get a copy of current matrices to work with, 1709 // just in case stereo render is required, and we need to modify them 1710 // NOTE: At this point the modelview matrix just contains the view matrix (camera) 1711 // That's because BeginMode3D() sets it and there is no model-drawing function 1712 // that modifies it, all use rlPushMatrix() and rlPopMatrix() 1713 Matrix matModel = MatrixIdentity(); 1714 Matrix matView = rlGetMatrixModelview(); 1715 Matrix matModelView = MatrixIdentity(); 1716 Matrix matProjection = rlGetMatrixProjection(); 1717 1718 // Upload view and projection matrices (if locations available) 1719 if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView); 1720 if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection); 1721 1722 // Create instances buffer 1723 instanceTransforms = (float16 *)RL_MALLOC(instances*sizeof(float16)); 1724 1725 // Fill buffer with instances transformations as float16 arrays 1726 for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]); 1727 1728 // Enable mesh VAO to attach new buffer 1729 rlEnableVertexArray(mesh.vaoId); 1730 1731 // This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData() 1732 // It isn't clear which would be reliably faster in all cases and on all platforms, 1733 // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems 1734 // no faster, since we're transferring all the transform matrices anyway 1735 instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false); 1736 1737 // Instances transformation matrices are send to shader attribute location: SHADER_LOC_MATRIX_MODEL 1738 for (unsigned int i = 0; i < 4; i++) 1739 { 1740 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i); 1741 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); 1742 rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 1); 1743 } 1744 1745 rlDisableVertexBuffer(); 1746 rlDisableVertexArray(); 1747 1748 // Accumulate internal matrix transform (push/pop) and view matrix 1749 // NOTE: In this case, model instance transformation must be computed in the shader 1750 matModelView = MatrixMultiply(rlGetMatrixTransform(), matView); 1751 1752 // Upload model normal matrix (if locations available) 1753 if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel))); 1754 1755 #ifdef RL_SUPPORT_MESH_GPU_SKINNING 1756 // Upload Bone Transforms 1757 if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices) 1758 { 1759 rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount); 1760 } 1761 #endif 1762 1763 //----------------------------------------------------- 1764 1765 // Bind active texture maps (if available) 1766 for (int i = 0; i < MAX_MATERIAL_MAPS; i++) 1767 { 1768 if (material.maps[i].texture.id > 0) 1769 { 1770 // Select current shader texture slot 1771 rlActiveTextureSlot(i); 1772 1773 // Enable texture for active slot 1774 if ((i == MATERIAL_MAP_IRRADIANCE) || 1775 (i == MATERIAL_MAP_PREFILTER) || 1776 (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id); 1777 else rlEnableTexture(material.maps[i].texture.id); 1778 1779 rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1); 1780 } 1781 } 1782 1783 // Try binding vertex array objects (VAO) 1784 // or use VBOs if not possible 1785 if (!rlEnableVertexArray(mesh.vaoId)) 1786 { 1787 // Bind mesh VBO data: vertex position (shader-location = 0) 1788 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]); 1789 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0); 1790 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]); 1791 1792 // Bind mesh VBO data: vertex texcoords (shader-location = 1) 1793 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]); 1794 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0); 1795 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]); 1796 1797 if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1) 1798 { 1799 // Bind mesh VBO data: vertex normals (shader-location = 2) 1800 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]); 1801 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0); 1802 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]); 1803 } 1804 1805 // Bind mesh VBO data: vertex colors (shader-location = 3, if available) 1806 if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1) 1807 { 1808 if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0) 1809 { 1810 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]); 1811 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0); 1812 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); 1813 } 1814 else 1815 { 1816 // Set default value for unused attribute 1817 // NOTE: Required when using default shader and no VAO support 1818 float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; 1819 rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4); 1820 rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]); 1821 } 1822 } 1823 1824 // Bind mesh VBO data: vertex tangents (shader-location = 4, if available) 1825 if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1) 1826 { 1827 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]); 1828 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0); 1829 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]); 1830 } 1831 1832 // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available) 1833 if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1) 1834 { 1835 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]); 1836 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0); 1837 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]); 1838 } 1839 1840 #ifdef RL_SUPPORT_MESH_GPU_SKINNING 1841 // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available) 1842 if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1) 1843 { 1844 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]); 1845 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0); 1846 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]); 1847 } 1848 1849 // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available) 1850 if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1) 1851 { 1852 rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]); 1853 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0); 1854 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]); 1855 } 1856 #endif 1857 1858 if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]); 1859 } 1860 1861 int eyeCount = 1; 1862 if (rlIsStereoRenderEnabled()) eyeCount = 2; 1863 1864 for (int eye = 0; eye < eyeCount; eye++) 1865 { 1866 // Calculate model-view-projection matrix (MVP) 1867 Matrix matModelViewProjection = MatrixIdentity(); 1868 if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection); 1869 else 1870 { 1871 // Setup current eye viewport (half screen width) 1872 rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight()); 1873 matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye)); 1874 } 1875 1876 // Send combined model-view-projection matrix to shader 1877 rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection); 1878 1879 // Draw mesh instanced 1880 if (mesh.indices != NULL) rlDrawVertexArrayElementsInstanced(0, mesh.triangleCount*3, 0, instances); 1881 else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances); 1882 } 1883 1884 // Unbind all bound texture maps 1885 for (int i = 0; i < MAX_MATERIAL_MAPS; i++) 1886 { 1887 if (material.maps[i].texture.id > 0) 1888 { 1889 // Select current shader texture slot 1890 rlActiveTextureSlot(i); 1891 1892 // Disable texture for active slot 1893 if ((i == MATERIAL_MAP_IRRADIANCE) || 1894 (i == MATERIAL_MAP_PREFILTER) || 1895 (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap(); 1896 else rlDisableTexture(); 1897 } 1898 } 1899 1900 // Disable all possible vertex array objects (or VBOs) 1901 rlDisableVertexArray(); 1902 rlDisableVertexBuffer(); 1903 rlDisableVertexBufferElement(); 1904 1905 // Disable shader program 1906 rlDisableShader(); 1907 1908 // Remove instance transforms buffer 1909 rlUnloadVertexBuffer(instancesVboId); 1910 RL_FREE(instanceTransforms); 1911 #endif 1912 } 1913 1914 // Unload mesh from memory (RAM and VRAM) 1915 void UnloadMesh(Mesh mesh) 1916 { 1917 // Unload rlgl mesh vboId data 1918 rlUnloadVertexArray(mesh.vaoId); 1919 1920 if (mesh.vboId != NULL) for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]); 1921 RL_FREE(mesh.vboId); 1922 1923 RL_FREE(mesh.vertices); 1924 RL_FREE(mesh.texcoords); 1925 RL_FREE(mesh.normals); 1926 RL_FREE(mesh.colors); 1927 RL_FREE(mesh.tangents); 1928 RL_FREE(mesh.texcoords2); 1929 RL_FREE(mesh.indices); 1930 1931 RL_FREE(mesh.animVertices); 1932 RL_FREE(mesh.animNormals); 1933 RL_FREE(mesh.boneWeights); 1934 RL_FREE(mesh.boneIds); 1935 RL_FREE(mesh.boneMatrices); 1936 } 1937 1938 // Export mesh data to file 1939 bool ExportMesh(Mesh mesh, const char *fileName) 1940 { 1941 bool success = false; 1942 1943 if (IsFileExtension(fileName, ".obj")) 1944 { 1945 // Estimated data size, it should be enough... 1946 int dataSize = mesh.vertexCount*(int)strlen("v 0000.00f 0000.00f 0000.00f") + 1947 mesh.vertexCount*(int)strlen("vt 0.000f 0.00f") + 1948 mesh.vertexCount*(int)strlen("vn 0.000f 0.00f 0.00f") + 1949 mesh.triangleCount*(int)strlen("f 00000/00000/00000 00000/00000/00000 00000/00000/00000"); 1950 1951 // NOTE: Text data buffer size is estimated considering mesh data size 1952 char *txtData = (char *)RL_CALLOC(dataSize*2 + 2000, sizeof(char)); 1953 1954 int byteCount = 0; 1955 byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n"); 1956 byteCount += sprintf(txtData + byteCount, "# // //\n"); 1957 byteCount += sprintf(txtData + byteCount, "# // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized //\n"); 1958 byteCount += sprintf(txtData + byteCount, "# // //\n"); 1959 byteCount += sprintf(txtData + byteCount, "# // more info and bugs-report: github.com/raysan5/raylib //\n"); 1960 byteCount += sprintf(txtData + byteCount, "# // feedback and support: ray[at]raylib.com //\n"); 1961 byteCount += sprintf(txtData + byteCount, "# // //\n"); 1962 byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n"); 1963 byteCount += sprintf(txtData + byteCount, "# // //\n"); 1964 byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n"); 1965 byteCount += sprintf(txtData + byteCount, "# Vertex Count: %i\n", mesh.vertexCount); 1966 byteCount += sprintf(txtData + byteCount, "# Triangle Count: %i\n\n", mesh.triangleCount); 1967 1968 byteCount += sprintf(txtData + byteCount, "g mesh\n"); 1969 1970 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) 1971 { 1972 byteCount += sprintf(txtData + byteCount, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]); 1973 } 1974 1975 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2) 1976 { 1977 byteCount += sprintf(txtData + byteCount, "vt %.3f %.3f\n", mesh.texcoords[v], mesh.texcoords[v + 1]); 1978 } 1979 1980 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) 1981 { 1982 byteCount += sprintf(txtData + byteCount, "vn %.3f %.3f %.3f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); 1983 } 1984 1985 if (mesh.indices != NULL) 1986 { 1987 for (int i = 0, v = 0; i < mesh.triangleCount; i++, v += 3) 1988 { 1989 byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", 1990 mesh.indices[v] + 1, mesh.indices[v] + 1, mesh.indices[v] + 1, 1991 mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1, 1992 mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1); 1993 } 1994 } 1995 else 1996 { 1997 for (int i = 0, v = 1; i < mesh.triangleCount; i++, v += 3) 1998 { 1999 byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", v, v, v, v + 1, v + 1, v + 1, v + 2, v + 2, v + 2); 2000 } 2001 } 2002 2003 byteCount += sprintf(txtData + byteCount, "\n"); 2004 2005 // NOTE: Text data length exported is determined by '\0' (NULL) character 2006 success = SaveFileText(fileName, txtData); 2007 2008 RL_FREE(txtData); 2009 } 2010 else if (IsFileExtension(fileName, ".raw")) 2011 { 2012 // TODO: Support additional file formats to export mesh vertex data 2013 } 2014 2015 return success; 2016 } 2017 2018 // Export mesh as code file (.h) defining multiple arrays of vertex attributes 2019 bool ExportMeshAsCode(Mesh mesh, const char *fileName) 2020 { 2021 bool success = false; 2022 2023 #ifndef TEXT_BYTES_PER_LINE 2024 #define TEXT_BYTES_PER_LINE 20 2025 #endif 2026 2027 // NOTE: Text data buffer size is fixed to 64MB 2028 char *txtData = (char *)RL_CALLOC(64*1024*1024, sizeof(char)); // 64 MB 2029 2030 int byteCount = 0; 2031 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); 2032 byteCount += sprintf(txtData + byteCount, "// //\n"); 2033 byteCount += sprintf(txtData + byteCount, "// MeshAsCode exporter v1.0 - Mesh vertex data exported as arrays //\n"); 2034 byteCount += sprintf(txtData + byteCount, "// //\n"); 2035 byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); 2036 byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); 2037 byteCount += sprintf(txtData + byteCount, "// //\n"); 2038 byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2023 Ramon Santamaria (@raysan5) //\n"); 2039 byteCount += sprintf(txtData + byteCount, "// //\n"); 2040 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); 2041 2042 // Get file name from path and convert variable name to uppercase 2043 char varFileName[256] = { 0 }; 2044 strcpy(varFileName, GetFileNameWithoutExt(fileName)); 2045 for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } 2046 2047 // Add image information 2048 byteCount += sprintf(txtData + byteCount, "// Mesh basic information\n"); 2049 byteCount += sprintf(txtData + byteCount, "#define %s_VERTEX_COUNT %i\n", varFileName, mesh.vertexCount); 2050 byteCount += sprintf(txtData + byteCount, "#define %s_TRIANGLE_COUNT %i\n\n", varFileName, mesh.triangleCount); 2051 2052 // Define vertex attributes data as separate arrays 2053 //----------------------------------------------------------------------------------------- 2054 if (mesh.vertices != NULL) // Vertex position (XYZ - 3 components per vertex - float) 2055 { 2056 byteCount += sprintf(txtData + byteCount, "static float %s_VERTEX_DATA[%i] = { ", varFileName, mesh.vertexCount*3); 2057 for (int i = 0; i < mesh.vertexCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.vertices[i]); 2058 byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.vertices[mesh.vertexCount*3 - 1]); 2059 } 2060 2061 if (mesh.texcoords != NULL) // Vertex texture coordinates (UV - 2 components per vertex - float) 2062 { 2063 byteCount += sprintf(txtData + byteCount, "static float %s_TEXCOORD_DATA[%i] = { ", varFileName, mesh.vertexCount*2); 2064 for (int i = 0; i < mesh.vertexCount*2 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.texcoords[i]); 2065 byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.texcoords[mesh.vertexCount*2 - 1]); 2066 } 2067 2068 if (mesh.texcoords2 != NULL) // Vertex texture coordinates (UV - 2 components per vertex - float) 2069 { 2070 byteCount += sprintf(txtData + byteCount, "static float %s_TEXCOORD2_DATA[%i] = { ", varFileName, mesh.vertexCount*2); 2071 for (int i = 0; i < mesh.vertexCount*2 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.texcoords2[i]); 2072 byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.texcoords2[mesh.vertexCount*2 - 1]); 2073 } 2074 2075 if (mesh.normals != NULL) // Vertex normals (XYZ - 3 components per vertex - float) 2076 { 2077 byteCount += sprintf(txtData + byteCount, "static float %s_NORMAL_DATA[%i] = { ", varFileName, mesh.vertexCount*3); 2078 for (int i = 0; i < mesh.vertexCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.normals[i]); 2079 byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.normals[mesh.vertexCount*3 - 1]); 2080 } 2081 2082 if (mesh.tangents != NULL) // Vertex tangents (XYZW - 4 components per vertex - float) 2083 { 2084 byteCount += sprintf(txtData + byteCount, "static float %s_TANGENT_DATA[%i] = { ", varFileName, mesh.vertexCount*4); 2085 for (int i = 0; i < mesh.vertexCount*4 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.tangents[i]); 2086 byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.tangents[mesh.vertexCount*4 - 1]); 2087 } 2088 2089 if (mesh.colors != NULL) // Vertex colors (RGBA - 4 components per vertex - unsigned char) 2090 { 2091 byteCount += sprintf(txtData + byteCount, "static unsigned char %s_COLOR_DATA[%i] = { ", varFileName, mesh.vertexCount*4); 2092 for (int i = 0; i < mesh.vertexCount*4 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), mesh.colors[i]); 2093 byteCount += sprintf(txtData + byteCount, "0x%x };\n\n", mesh.colors[mesh.vertexCount*4 - 1]); 2094 } 2095 2096 if (mesh.indices != NULL) // Vertex indices (3 index per triangle - unsigned short) 2097 { 2098 byteCount += sprintf(txtData + byteCount, "static unsigned short %s_INDEX_DATA[%i] = { ", varFileName, mesh.triangleCount*3); 2099 for (int i = 0; i < mesh.triangleCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%i,\n" : "%i, "), mesh.indices[i]); 2100 byteCount += sprintf(txtData + byteCount, "%i };\n", mesh.indices[mesh.triangleCount*3 - 1]); 2101 } 2102 //----------------------------------------------------------------------------------------- 2103 2104 // NOTE: Text data size exported is determined by '\0' (NULL) character 2105 success = SaveFileText(fileName, txtData); 2106 2107 RL_FREE(txtData); 2108 2109 //if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName); 2110 //else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName); 2111 2112 return success; 2113 } 2114 2115 #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) 2116 // Process obj materials 2117 static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount) 2118 { 2119 // Init model mats 2120 for (int m = 0; m < materialCount; m++) 2121 { 2122 // Init material to default 2123 // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE 2124 materials[m] = LoadMaterialDefault(); 2125 2126 if (mats == NULL) continue; 2127 2128 // Get default texture, in case no texture is defined 2129 // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 2130 materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; 2131 2132 if (mats[m].diffuse_texname != NULL) materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(mats[m].diffuse_texname); //char *diffuse_texname; // map_Kd 2133 else materials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(mats[m].diffuse[0]*255.0f), (unsigned char)(mats[m].diffuse[1]*255.0f), (unsigned char)(mats[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; 2134 materials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; 2135 2136 if (mats[m].specular_texname != NULL) materials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(mats[m].specular_texname); //char *specular_texname; // map_Ks 2137 materials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(mats[m].specular[0]*255.0f), (unsigned char)(mats[m].specular[1]*255.0f), (unsigned char)(mats[m].specular[2]*255.0f), 255 }; //float specular[3]; 2138 materials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; 2139 2140 if (mats[m].bump_texname != NULL) materials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(mats[m].bump_texname); //char *bump_texname; // map_bump, bump 2141 materials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; 2142 materials[m].maps[MATERIAL_MAP_NORMAL].value = mats[m].shininess; 2143 2144 materials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(mats[m].emission[0]*255.0f), (unsigned char)(mats[m].emission[1]*255.0f), (unsigned char)(mats[m].emission[2]*255.0f), 255 }; //float emission[3]; 2145 2146 if (mats[m].displacement_texname != NULL) materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(mats[m].displacement_texname); //char *displacement_texname; // disp 2147 } 2148 } 2149 #endif 2150 2151 // Load materials from model file 2152 Material *LoadMaterials(const char *fileName, int *materialCount) 2153 { 2154 Material *materials = NULL; 2155 unsigned int count = 0; 2156 2157 // TODO: Support IQM and GLTF for materials parsing 2158 2159 #if defined(SUPPORT_FILEFORMAT_MTL) 2160 if (IsFileExtension(fileName, ".mtl")) 2161 { 2162 tinyobj_material_t *mats = NULL; 2163 2164 int result = tinyobj_parse_mtl_file(&mats, &count, fileName); 2165 if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName); 2166 2167 materials = RL_MALLOC(count*sizeof(Material)); 2168 ProcessMaterialsOBJ(materials, mats, count); 2169 2170 tinyobj_materials_free(mats, count); 2171 } 2172 #else 2173 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load material file", fileName); 2174 #endif 2175 2176 *materialCount = count; 2177 return materials; 2178 } 2179 2180 // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) 2181 Material LoadMaterialDefault(void) 2182 { 2183 Material material = { 0 }; 2184 material.maps = (MaterialMap *)RL_CALLOC(MAX_MATERIAL_MAPS, sizeof(MaterialMap)); 2185 2186 // Using rlgl default shader 2187 material.shader.id = rlGetShaderIdDefault(); 2188 material.shader.locs = rlGetShaderLocsDefault(); 2189 2190 // Using rlgl default texture (1x1 pixel, UNCOMPRESSED_R8G8B8A8, 1 mipmap) 2191 material.maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; 2192 //material.maps[MATERIAL_MAP_NORMAL].texture; // NOTE: By default, not set 2193 //material.maps[MATERIAL_MAP_SPECULAR].texture; // NOTE: By default, not set 2194 2195 material.maps[MATERIAL_MAP_DIFFUSE].color = WHITE; // Diffuse color 2196 material.maps[MATERIAL_MAP_SPECULAR].color = WHITE; // Specular color 2197 2198 return material; 2199 } 2200 2201 // Check if a material is valid (map textures loaded in GPU) 2202 bool IsMaterialValid(Material material) 2203 { 2204 bool result = false; 2205 2206 if ((material.maps != NULL) && // Validate material contain some map 2207 (material.shader.id > 0)) result = true; // Validate material shader is valid 2208 2209 // TODO: Check if available maps contain loaded textures 2210 2211 return result; 2212 } 2213 2214 // Unload material from memory 2215 void UnloadMaterial(Material material) 2216 { 2217 // Unload material shader (avoid unloading default shader, managed by raylib) 2218 if (material.shader.id != rlGetShaderIdDefault()) UnloadShader(material.shader); 2219 2220 // Unload loaded texture maps (avoid unloading default texture, managed by raylib) 2221 if (material.maps != NULL) 2222 { 2223 for (int i = 0; i < MAX_MATERIAL_MAPS; i++) 2224 { 2225 if (material.maps[i].texture.id != rlGetTextureIdDefault()) rlUnloadTexture(material.maps[i].texture.id); 2226 } 2227 } 2228 2229 RL_FREE(material.maps); 2230 } 2231 2232 // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) 2233 // NOTE: Previous texture should be manually unloaded 2234 void SetMaterialTexture(Material *material, int mapType, Texture2D texture) 2235 { 2236 material->maps[mapType].texture = texture; 2237 } 2238 2239 // Set the material for a mesh 2240 void SetModelMeshMaterial(Model *model, int meshId, int materialId) 2241 { 2242 if (meshId >= model->meshCount) TRACELOG(LOG_WARNING, "MESH: Id greater than mesh count"); 2243 else if (materialId >= model->materialCount) TRACELOG(LOG_WARNING, "MATERIAL: Id greater than material count"); 2244 else model->meshMaterial[meshId] = materialId; 2245 } 2246 2247 // Load model animations from file 2248 ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount) 2249 { 2250 ModelAnimation *animations = NULL; 2251 2252 #if defined(SUPPORT_FILEFORMAT_IQM) 2253 if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount); 2254 #endif 2255 #if defined(SUPPORT_FILEFORMAT_M3D) 2256 if (IsFileExtension(fileName, ".m3d")) animations = LoadModelAnimationsM3D(fileName, animCount); 2257 #endif 2258 #if defined(SUPPORT_FILEFORMAT_GLTF) 2259 if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationsGLTF(fileName, animCount); 2260 #endif 2261 2262 return animations; 2263 } 2264 2265 // Update model animated bones transform matrices for a given frame 2266 // NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId], 2267 // to be uploaded to shader at drawing, in case GPU skinning is enabled 2268 void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) 2269 { 2270 if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL)) 2271 { 2272 if (frame >= anim.frameCount) frame = frame%anim.frameCount; 2273 2274 for (int i = 0; i < model.meshCount; i++) 2275 { 2276 if (model.meshes[i].boneMatrices) 2277 { 2278 assert(model.meshes[i].boneCount == anim.boneCount); 2279 2280 for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++) 2281 { 2282 Vector3 inTranslation = model.bindPose[boneId].translation; 2283 Quaternion inRotation = model.bindPose[boneId].rotation; 2284 Vector3 inScale = model.bindPose[boneId].scale; 2285 2286 Vector3 outTranslation = anim.framePoses[frame][boneId].translation; 2287 Quaternion outRotation = anim.framePoses[frame][boneId].rotation; 2288 Vector3 outScale = anim.framePoses[frame][boneId].scale; 2289 2290 Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation)); 2291 Quaternion invRotation = QuaternionInvert(inRotation); 2292 Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale); 2293 2294 Vector3 boneTranslation = Vector3Add( 2295 Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation), 2296 outRotation), outTranslation); 2297 Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation); 2298 Vector3 boneScale = Vector3Multiply(outScale, invScale); 2299 2300 Matrix boneMatrix = MatrixMultiply(MatrixMultiply( 2301 QuaternionToMatrix(boneRotation), 2302 MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)), 2303 MatrixScale(boneScale.x, boneScale.y, boneScale.z)); 2304 2305 model.meshes[i].boneMatrices[boneId] = boneMatrix; 2306 } 2307 } 2308 } 2309 } 2310 } 2311 2312 // at least 2x speed up vs the old method 2313 // Update model animated vertex data (positions and normals) for a given frame 2314 // NOTE: Updated data is uploaded to GPU 2315 void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) 2316 { 2317 UpdateModelAnimationBones(model,anim,frame); 2318 for (int m = 0; m < model.meshCount; m++) 2319 { 2320 Mesh mesh = model.meshes[m]; 2321 Vector3 animVertex = { 0 }; 2322 Vector3 animNormal = { 0 }; 2323 int boneId = 0; 2324 int boneCounter = 0; 2325 float boneWeight = 0.0; 2326 bool updated = false; // Flag to check when anim vertex information is updated 2327 const int vValues = mesh.vertexCount*3; 2328 for (int vCounter = 0; vCounter < vValues; vCounter += 3) 2329 { 2330 mesh.animVertices[vCounter] = 0; 2331 mesh.animVertices[vCounter + 1] = 0; 2332 mesh.animVertices[vCounter + 2] = 0; 2333 if (mesh.animNormals != NULL) 2334 { 2335 mesh.animNormals[vCounter] = 0; 2336 mesh.animNormals[vCounter + 1] = 0; 2337 mesh.animNormals[vCounter + 2] = 0; 2338 } 2339 // Iterates over 4 bones per vertex 2340 for (int j = 0; j < 4; j++, boneCounter++) 2341 { 2342 boneWeight = mesh.boneWeights[boneCounter]; 2343 boneId = mesh.boneIds[boneCounter]; 2344 // Early stop when no transformation will be applied 2345 if (boneWeight == 0.0f) continue; 2346 animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] }; 2347 animVertex = Vector3Transform(animVertex,model.meshes[m].boneMatrices[boneId]); 2348 mesh.animVertices[vCounter] += animVertex.x * boneWeight; 2349 mesh.animVertices[vCounter+1] += animVertex.y * boneWeight; 2350 mesh.animVertices[vCounter+2] += animVertex.z * boneWeight; 2351 updated = true; 2352 // Normals processing 2353 // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) 2354 if (mesh.normals != NULL) 2355 { 2356 animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] }; 2357 animNormal = Vector3Transform(animNormal,model.meshes[m].boneMatrices[boneId]); 2358 mesh.animNormals[vCounter] += animNormal.x*boneWeight; 2359 mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight; 2360 mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight; 2361 } 2362 } 2363 } 2364 if (updated) 2365 { 2366 rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position 2367 rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals 2368 } 2369 } 2370 } 2371 2372 // Unload animation array data 2373 void UnloadModelAnimations(ModelAnimation *animations, int animCount) 2374 { 2375 for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]); 2376 RL_FREE(animations); 2377 } 2378 2379 // Unload animation data 2380 void UnloadModelAnimation(ModelAnimation anim) 2381 { 2382 for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]); 2383 2384 RL_FREE(anim.bones); 2385 RL_FREE(anim.framePoses); 2386 } 2387 2388 // Check model animation skeleton match 2389 // NOTE: Only number of bones and parent connections are checked 2390 bool IsModelAnimationValid(Model model, ModelAnimation anim) 2391 { 2392 int result = true; 2393 2394 if (model.boneCount != anim.boneCount) result = false; 2395 else 2396 { 2397 for (int i = 0; i < model.boneCount; i++) 2398 { 2399 if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; } 2400 } 2401 } 2402 2403 return result; 2404 } 2405 2406 #if defined(SUPPORT_MESH_GENERATION) 2407 // Generate polygonal mesh 2408 Mesh GenMeshPoly(int sides, float radius) 2409 { 2410 Mesh mesh = { 0 }; 2411 2412 if (sides < 3) return mesh; // Security check 2413 2414 int vertexCount = sides*3; 2415 2416 // Vertices definition 2417 Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); 2418 2419 float d = 0.0f, dStep = 360.0f/sides; 2420 for (int v = 0; v < vertexCount - 2; v += 3) 2421 { 2422 vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f }; 2423 vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius }; 2424 vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius }; 2425 d += dStep; 2426 } 2427 2428 // Normals definition 2429 Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); 2430 for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up; 2431 2432 // TexCoords definition 2433 Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2)); 2434 for (int n = 0; n < vertexCount; n++) texcoords[n] = (Vector2){ 0.0f, 0.0f }; 2435 2436 mesh.vertexCount = vertexCount; 2437 mesh.triangleCount = sides; 2438 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 2439 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); 2440 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 2441 2442 // Mesh vertices position array 2443 for (int i = 0; i < mesh.vertexCount; i++) 2444 { 2445 mesh.vertices[3*i] = vertices[i].x; 2446 mesh.vertices[3*i + 1] = vertices[i].y; 2447 mesh.vertices[3*i + 2] = vertices[i].z; 2448 } 2449 2450 // Mesh texcoords array 2451 for (int i = 0; i < mesh.vertexCount; i++) 2452 { 2453 mesh.texcoords[2*i] = texcoords[i].x; 2454 mesh.texcoords[2*i + 1] = texcoords[i].y; 2455 } 2456 2457 // Mesh normals array 2458 for (int i = 0; i < mesh.vertexCount; i++) 2459 { 2460 mesh.normals[3*i] = normals[i].x; 2461 mesh.normals[3*i + 1] = normals[i].y; 2462 mesh.normals[3*i + 2] = normals[i].z; 2463 } 2464 2465 RL_FREE(vertices); 2466 RL_FREE(normals); 2467 RL_FREE(texcoords); 2468 2469 // Upload vertex data to GPU (static mesh) 2470 // NOTE: mesh.vboId array is allocated inside UploadMesh() 2471 UploadMesh(&mesh, false); 2472 2473 return mesh; 2474 } 2475 2476 // Generate plane mesh (with subdivisions) 2477 Mesh GenMeshPlane(float width, float length, int resX, int resZ) 2478 { 2479 Mesh mesh = { 0 }; 2480 2481 #define CUSTOM_MESH_GEN_PLANE 2482 #if defined(CUSTOM_MESH_GEN_PLANE) 2483 resX++; 2484 resZ++; 2485 2486 // Vertices definition 2487 int vertexCount = resX*resZ; // vertices get reused for the faces 2488 2489 Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); 2490 for (int z = 0; z < resZ; z++) 2491 { 2492 // [-length/2, length/2] 2493 float zPos = ((float)z/(resZ - 1) - 0.5f)*length; 2494 for (int x = 0; x < resX; x++) 2495 { 2496 // [-width/2, width/2] 2497 float xPos = ((float)x/(resX - 1) - 0.5f)*width; 2498 vertices[x + z*resX] = (Vector3){ xPos, 0.0f, zPos }; 2499 } 2500 } 2501 2502 // Normals definition 2503 Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); 2504 for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up; 2505 2506 // TexCoords definition 2507 Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2)); 2508 for (int v = 0; v < resZ; v++) 2509 { 2510 for (int u = 0; u < resX; u++) 2511 { 2512 texcoords[u + v*resX] = (Vector2){ (float)u/(resX - 1), (float)v/(resZ - 1) }; 2513 } 2514 } 2515 2516 // Triangles definition (indices) 2517 int numFaces = (resX - 1)*(resZ - 1); 2518 int *triangles = (int *)RL_MALLOC(numFaces*6*sizeof(int)); 2519 int t = 0; 2520 for (int face = 0; face < numFaces; face++) 2521 { 2522 // Retrieve lower left corner from face ind 2523 int i = face + face/(resX - 1); 2524 2525 triangles[t++] = i + resX; 2526 triangles[t++] = i + 1; 2527 triangles[t++] = i; 2528 2529 triangles[t++] = i + resX; 2530 triangles[t++] = i + resX + 1; 2531 triangles[t++] = i + 1; 2532 } 2533 2534 mesh.vertexCount = vertexCount; 2535 mesh.triangleCount = numFaces*2; 2536 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 2537 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); 2538 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 2539 mesh.indices = (unsigned short *)RL_MALLOC(mesh.triangleCount*3*sizeof(unsigned short)); 2540 2541 // Mesh vertices position array 2542 for (int i = 0; i < mesh.vertexCount; i++) 2543 { 2544 mesh.vertices[3*i] = vertices[i].x; 2545 mesh.vertices[3*i + 1] = vertices[i].y; 2546 mesh.vertices[3*i + 2] = vertices[i].z; 2547 } 2548 2549 // Mesh texcoords array 2550 for (int i = 0; i < mesh.vertexCount; i++) 2551 { 2552 mesh.texcoords[2*i] = texcoords[i].x; 2553 mesh.texcoords[2*i + 1] = texcoords[i].y; 2554 } 2555 2556 // Mesh normals array 2557 for (int i = 0; i < mesh.vertexCount; i++) 2558 { 2559 mesh.normals[3*i] = normals[i].x; 2560 mesh.normals[3*i + 1] = normals[i].y; 2561 mesh.normals[3*i + 2] = normals[i].z; 2562 } 2563 2564 // Mesh indices array initialization 2565 for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i]; 2566 2567 RL_FREE(vertices); 2568 RL_FREE(normals); 2569 RL_FREE(texcoords); 2570 RL_FREE(triangles); 2571 2572 #else // Use par_shapes library to generate plane mesh 2573 2574 par_shapes_mesh *plane = par_shapes_create_plane(resX, resZ); // No normals/texcoords generated!!! 2575 par_shapes_scale(plane, width, length, 1.0f); 2576 par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 }); 2577 par_shapes_translate(plane, -width/2, 0.0f, length/2); 2578 2579 mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); 2580 mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float)); 2581 mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float)); 2582 2583 mesh.vertexCount = plane->ntriangles*3; 2584 mesh.triangleCount = plane->ntriangles; 2585 2586 for (int k = 0; k < mesh.vertexCount; k++) 2587 { 2588 mesh.vertices[k*3] = plane->points[plane->triangles[k]*3]; 2589 mesh.vertices[k*3 + 1] = plane->points[plane->triangles[k]*3 + 1]; 2590 mesh.vertices[k*3 + 2] = plane->points[plane->triangles[k]*3 + 2]; 2591 2592 mesh.normals[k*3] = plane->normals[plane->triangles[k]*3]; 2593 mesh.normals[k*3 + 1] = plane->normals[plane->triangles[k]*3 + 1]; 2594 mesh.normals[k*3 + 2] = plane->normals[plane->triangles[k]*3 + 2]; 2595 2596 mesh.texcoords[k*2] = plane->tcoords[plane->triangles[k]*2]; 2597 mesh.texcoords[k*2 + 1] = plane->tcoords[plane->triangles[k]*2 + 1]; 2598 } 2599 2600 par_shapes_free_mesh(plane); 2601 #endif 2602 2603 // Upload vertex data to GPU (static mesh) 2604 UploadMesh(&mesh, false); 2605 2606 return mesh; 2607 } 2608 2609 // Generated cuboid mesh 2610 Mesh GenMeshCube(float width, float height, float length) 2611 { 2612 Mesh mesh = { 0 }; 2613 2614 #define CUSTOM_MESH_GEN_CUBE 2615 #if defined(CUSTOM_MESH_GEN_CUBE) 2616 float vertices[] = { 2617 -width/2, -height/2, length/2, 2618 width/2, -height/2, length/2, 2619 width/2, height/2, length/2, 2620 -width/2, height/2, length/2, 2621 -width/2, -height/2, -length/2, 2622 -width/2, height/2, -length/2, 2623 width/2, height/2, -length/2, 2624 width/2, -height/2, -length/2, 2625 -width/2, height/2, -length/2, 2626 -width/2, height/2, length/2, 2627 width/2, height/2, length/2, 2628 width/2, height/2, -length/2, 2629 -width/2, -height/2, -length/2, 2630 width/2, -height/2, -length/2, 2631 width/2, -height/2, length/2, 2632 -width/2, -height/2, length/2, 2633 width/2, -height/2, -length/2, 2634 width/2, height/2, -length/2, 2635 width/2, height/2, length/2, 2636 width/2, -height/2, length/2, 2637 -width/2, -height/2, -length/2, 2638 -width/2, -height/2, length/2, 2639 -width/2, height/2, length/2, 2640 -width/2, height/2, -length/2 2641 }; 2642 2643 float texcoords[] = { 2644 0.0f, 0.0f, 2645 1.0f, 0.0f, 2646 1.0f, 1.0f, 2647 0.0f, 1.0f, 2648 1.0f, 0.0f, 2649 1.0f, 1.0f, 2650 0.0f, 1.0f, 2651 0.0f, 0.0f, 2652 0.0f, 1.0f, 2653 0.0f, 0.0f, 2654 1.0f, 0.0f, 2655 1.0f, 1.0f, 2656 1.0f, 1.0f, 2657 0.0f, 1.0f, 2658 0.0f, 0.0f, 2659 1.0f, 0.0f, 2660 1.0f, 0.0f, 2661 1.0f, 1.0f, 2662 0.0f, 1.0f, 2663 0.0f, 0.0f, 2664 0.0f, 0.0f, 2665 1.0f, 0.0f, 2666 1.0f, 1.0f, 2667 0.0f, 1.0f 2668 }; 2669 2670 float normals[] = { 2671 0.0f, 0.0f, 1.0f, 2672 0.0f, 0.0f, 1.0f, 2673 0.0f, 0.0f, 1.0f, 2674 0.0f, 0.0f, 1.0f, 2675 0.0f, 0.0f,-1.0f, 2676 0.0f, 0.0f,-1.0f, 2677 0.0f, 0.0f,-1.0f, 2678 0.0f, 0.0f,-1.0f, 2679 0.0f, 1.0f, 0.0f, 2680 0.0f, 1.0f, 0.0f, 2681 0.0f, 1.0f, 0.0f, 2682 0.0f, 1.0f, 0.0f, 2683 0.0f,-1.0f, 0.0f, 2684 0.0f,-1.0f, 0.0f, 2685 0.0f,-1.0f, 0.0f, 2686 0.0f,-1.0f, 0.0f, 2687 1.0f, 0.0f, 0.0f, 2688 1.0f, 0.0f, 0.0f, 2689 1.0f, 0.0f, 0.0f, 2690 1.0f, 0.0f, 0.0f, 2691 -1.0f, 0.0f, 0.0f, 2692 -1.0f, 0.0f, 0.0f, 2693 -1.0f, 0.0f, 0.0f, 2694 -1.0f, 0.0f, 0.0f 2695 }; 2696 2697 mesh.vertices = (float *)RL_MALLOC(24*3*sizeof(float)); 2698 memcpy(mesh.vertices, vertices, 24*3*sizeof(float)); 2699 2700 mesh.texcoords = (float *)RL_MALLOC(24*2*sizeof(float)); 2701 memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float)); 2702 2703 mesh.normals = (float *)RL_MALLOC(24*3*sizeof(float)); 2704 memcpy(mesh.normals, normals, 24*3*sizeof(float)); 2705 2706 mesh.indices = (unsigned short *)RL_MALLOC(36*sizeof(unsigned short)); 2707 2708 int k = 0; 2709 2710 // Indices can be initialized right now 2711 for (int i = 0; i < 36; i += 6) 2712 { 2713 mesh.indices[i] = 4*k; 2714 mesh.indices[i + 1] = 4*k + 1; 2715 mesh.indices[i + 2] = 4*k + 2; 2716 mesh.indices[i + 3] = 4*k; 2717 mesh.indices[i + 4] = 4*k + 2; 2718 mesh.indices[i + 5] = 4*k + 3; 2719 2720 k++; 2721 } 2722 2723 mesh.vertexCount = 24; 2724 mesh.triangleCount = 12; 2725 2726 #else // Use par_shapes library to generate cube mesh 2727 /* 2728 // Platonic solids: 2729 par_shapes_mesh* par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid) 2730 par_shapes_mesh* par_shapes_create_cube(); // 6 sides polyhedron (cube) 2731 par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (diamond) 2732 par_shapes_mesh* par_shapes_create_dodecahedron(); // 12 sides polyhedron 2733 par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron 2734 */ 2735 // Platonic solid generation: cube (6 sides) 2736 // NOTE: No normals/texcoords generated by default 2737 par_shapes_mesh *cube = par_shapes_create_cube(); 2738 cube->tcoords = PAR_MALLOC(float, 2*cube->npoints); 2739 for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f; 2740 par_shapes_scale(cube, width, height, length); 2741 par_shapes_translate(cube, -width/2, 0.0f, -length/2); 2742 par_shapes_compute_normals(cube); 2743 2744 mesh.vertices = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float)); 2745 mesh.texcoords = (float *)RL_MALLOC(cube->ntriangles*3*2*sizeof(float)); 2746 mesh.normals = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float)); 2747 2748 mesh.vertexCount = cube->ntriangles*3; 2749 mesh.triangleCount = cube->ntriangles; 2750 2751 for (int k = 0; k < mesh.vertexCount; k++) 2752 { 2753 mesh.vertices[k*3] = cube->points[cube->triangles[k]*3]; 2754 mesh.vertices[k*3 + 1] = cube->points[cube->triangles[k]*3 + 1]; 2755 mesh.vertices[k*3 + 2] = cube->points[cube->triangles[k]*3 + 2]; 2756 2757 mesh.normals[k*3] = cube->normals[cube->triangles[k]*3]; 2758 mesh.normals[k*3 + 1] = cube->normals[cube->triangles[k]*3 + 1]; 2759 mesh.normals[k*3 + 2] = cube->normals[cube->triangles[k]*3 + 2]; 2760 2761 mesh.texcoords[k*2] = cube->tcoords[cube->triangles[k]*2]; 2762 mesh.texcoords[k*2 + 1] = cube->tcoords[cube->triangles[k]*2 + 1]; 2763 } 2764 2765 par_shapes_free_mesh(cube); 2766 #endif 2767 2768 // Upload vertex data to GPU (static mesh) 2769 UploadMesh(&mesh, false); 2770 2771 return mesh; 2772 } 2773 2774 // Generate sphere mesh (standard sphere) 2775 Mesh GenMeshSphere(float radius, int rings, int slices) 2776 { 2777 Mesh mesh = { 0 }; 2778 2779 if ((rings >= 3) && (slices >= 3)) 2780 { 2781 par_shapes_set_epsilon_degenerate_sphere(0.0); 2782 par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings); 2783 par_shapes_scale(sphere, radius, radius, radius); 2784 // NOTE: Soft normals are computed internally 2785 2786 mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); 2787 mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float)); 2788 mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); 2789 2790 mesh.vertexCount = sphere->ntriangles*3; 2791 mesh.triangleCount = sphere->ntriangles; 2792 2793 for (int k = 0; k < mesh.vertexCount; k++) 2794 { 2795 mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3]; 2796 mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1]; 2797 mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2]; 2798 2799 mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3]; 2800 mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1]; 2801 mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2]; 2802 2803 mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2]; 2804 mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1]; 2805 } 2806 2807 par_shapes_free_mesh(sphere); 2808 2809 // Upload vertex data to GPU (static mesh) 2810 UploadMesh(&mesh, false); 2811 } 2812 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: sphere"); 2813 2814 return mesh; 2815 } 2816 2817 // Generate hemisphere mesh (half sphere, no bottom cap) 2818 Mesh GenMeshHemiSphere(float radius, int rings, int slices) 2819 { 2820 Mesh mesh = { 0 }; 2821 2822 if ((rings >= 3) && (slices >= 3)) 2823 { 2824 if (radius < 0.0f) radius = 0.0f; 2825 2826 par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings); 2827 par_shapes_scale(sphere, radius, radius, radius); 2828 // NOTE: Soft normals are computed internally 2829 2830 mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); 2831 mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float)); 2832 mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float)); 2833 2834 mesh.vertexCount = sphere->ntriangles*3; 2835 mesh.triangleCount = sphere->ntriangles; 2836 2837 for (int k = 0; k < mesh.vertexCount; k++) 2838 { 2839 mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3]; 2840 mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1]; 2841 mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2]; 2842 2843 mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3]; 2844 mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1]; 2845 mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2]; 2846 2847 mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2]; 2848 mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1]; 2849 } 2850 2851 par_shapes_free_mesh(sphere); 2852 2853 // Upload vertex data to GPU (static mesh) 2854 UploadMesh(&mesh, false); 2855 } 2856 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: hemisphere"); 2857 2858 return mesh; 2859 } 2860 2861 // Generate cylinder mesh 2862 Mesh GenMeshCylinder(float radius, float height, int slices) 2863 { 2864 Mesh mesh = { 0 }; 2865 2866 if (slices >= 3) 2867 { 2868 // Instance a cylinder that sits on the Z=0 plane using the given tessellation 2869 // levels across the UV domain. Think of "slices" like a number of pizza 2870 // slices, and "stacks" like a number of stacked rings 2871 // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale 2872 par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8); 2873 par_shapes_scale(cylinder, radius, radius, height); 2874 par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 }); 2875 2876 // Generate an orientable disk shape (top cap) 2877 par_shapes_mesh *capTop = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, 1 }); 2878 capTop->tcoords = PAR_MALLOC(float, 2*capTop->npoints); 2879 for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f; 2880 par_shapes_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 }); 2881 par_shapes_rotate(capTop, 90*DEG2RAD, (float[]){ 0, 1, 0 }); 2882 par_shapes_translate(capTop, 0, height, 0); 2883 2884 // Generate an orientable disk shape (bottom cap) 2885 par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 }); 2886 capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints); 2887 for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f; 2888 par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 }); 2889 par_shapes_rotate(capBottom, -90*DEG2RAD, (float[]){ 0, 1, 0 }); 2890 2891 par_shapes_merge_and_free(cylinder, capTop); 2892 par_shapes_merge_and_free(cylinder, capBottom); 2893 2894 mesh.vertices = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float)); 2895 mesh.texcoords = (float *)RL_MALLOC(cylinder->ntriangles*3*2*sizeof(float)); 2896 mesh.normals = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float)); 2897 2898 mesh.vertexCount = cylinder->ntriangles*3; 2899 mesh.triangleCount = cylinder->ntriangles; 2900 2901 for (int k = 0; k < mesh.vertexCount; k++) 2902 { 2903 mesh.vertices[k*3] = cylinder->points[cylinder->triangles[k]*3]; 2904 mesh.vertices[k*3 + 1] = cylinder->points[cylinder->triangles[k]*3 + 1]; 2905 mesh.vertices[k*3 + 2] = cylinder->points[cylinder->triangles[k]*3 + 2]; 2906 2907 mesh.normals[k*3] = cylinder->normals[cylinder->triangles[k]*3]; 2908 mesh.normals[k*3 + 1] = cylinder->normals[cylinder->triangles[k]*3 + 1]; 2909 mesh.normals[k*3 + 2] = cylinder->normals[cylinder->triangles[k]*3 + 2]; 2910 2911 mesh.texcoords[k*2] = cylinder->tcoords[cylinder->triangles[k]*2]; 2912 mesh.texcoords[k*2 + 1] = cylinder->tcoords[cylinder->triangles[k]*2 + 1]; 2913 } 2914 2915 par_shapes_free_mesh(cylinder); 2916 2917 // Upload vertex data to GPU (static mesh) 2918 UploadMesh(&mesh, false); 2919 } 2920 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cylinder"); 2921 2922 return mesh; 2923 } 2924 2925 // Generate cone/pyramid mesh 2926 Mesh GenMeshCone(float radius, float height, int slices) 2927 { 2928 Mesh mesh = { 0 }; 2929 2930 if (slices >= 3) 2931 { 2932 // Instance a cone that sits on the Z=0 plane using the given tessellation 2933 // levels across the UV domain. Think of "slices" like a number of pizza 2934 // slices, and "stacks" like a number of stacked rings 2935 // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale 2936 par_shapes_mesh *cone = par_shapes_create_cone(slices, 8); 2937 par_shapes_scale(cone, radius, radius, height); 2938 par_shapes_rotate(cone, -PI/2.0f, (float[]){ 1, 0, 0 }); 2939 par_shapes_rotate(cone, PI/2.0f, (float[]){ 0, 1, 0 }); 2940 2941 // Generate an orientable disk shape (bottom cap) 2942 par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 }); 2943 capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints); 2944 for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f; 2945 par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 }); 2946 2947 par_shapes_merge_and_free(cone, capBottom); 2948 2949 mesh.vertices = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float)); 2950 mesh.texcoords = (float *)RL_MALLOC(cone->ntriangles*3*2*sizeof(float)); 2951 mesh.normals = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float)); 2952 2953 mesh.vertexCount = cone->ntriangles*3; 2954 mesh.triangleCount = cone->ntriangles; 2955 2956 for (int k = 0; k < mesh.vertexCount; k++) 2957 { 2958 mesh.vertices[k*3] = cone->points[cone->triangles[k]*3]; 2959 mesh.vertices[k*3 + 1] = cone->points[cone->triangles[k]*3 + 1]; 2960 mesh.vertices[k*3 + 2] = cone->points[cone->triangles[k]*3 + 2]; 2961 2962 mesh.normals[k*3] = cone->normals[cone->triangles[k]*3]; 2963 mesh.normals[k*3 + 1] = cone->normals[cone->triangles[k]*3 + 1]; 2964 mesh.normals[k*3 + 2] = cone->normals[cone->triangles[k]*3 + 2]; 2965 2966 mesh.texcoords[k*2] = cone->tcoords[cone->triangles[k]*2]; 2967 mesh.texcoords[k*2 + 1] = cone->tcoords[cone->triangles[k]*2 + 1]; 2968 } 2969 2970 par_shapes_free_mesh(cone); 2971 2972 // Upload vertex data to GPU (static mesh) 2973 UploadMesh(&mesh, false); 2974 } 2975 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cone"); 2976 2977 return mesh; 2978 } 2979 2980 // Generate torus mesh 2981 Mesh GenMeshTorus(float radius, float size, int radSeg, int sides) 2982 { 2983 Mesh mesh = { 0 }; 2984 2985 if ((sides >= 3) && (radSeg >= 3)) 2986 { 2987 if (radius > 1.0f) radius = 1.0f; 2988 else if (radius < 0.1f) radius = 0.1f; 2989 2990 // Create a donut that sits on the Z=0 plane with the specified inner radius 2991 // The outer radius can be controlled with par_shapes_scale 2992 par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius); 2993 par_shapes_scale(torus, size/2, size/2, size/2); 2994 2995 mesh.vertices = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float)); 2996 mesh.texcoords = (float *)RL_MALLOC(torus->ntriangles*3*2*sizeof(float)); 2997 mesh.normals = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float)); 2998 2999 mesh.vertexCount = torus->ntriangles*3; 3000 mesh.triangleCount = torus->ntriangles; 3001 3002 for (int k = 0; k < mesh.vertexCount; k++) 3003 { 3004 mesh.vertices[k*3] = torus->points[torus->triangles[k]*3]; 3005 mesh.vertices[k*3 + 1] = torus->points[torus->triangles[k]*3 + 1]; 3006 mesh.vertices[k*3 + 2] = torus->points[torus->triangles[k]*3 + 2]; 3007 3008 mesh.normals[k*3] = torus->normals[torus->triangles[k]*3]; 3009 mesh.normals[k*3 + 1] = torus->normals[torus->triangles[k]*3 + 1]; 3010 mesh.normals[k*3 + 2] = torus->normals[torus->triangles[k]*3 + 2]; 3011 3012 mesh.texcoords[k*2] = torus->tcoords[torus->triangles[k]*2]; 3013 mesh.texcoords[k*2 + 1] = torus->tcoords[torus->triangles[k]*2 + 1]; 3014 } 3015 3016 par_shapes_free_mesh(torus); 3017 3018 // Upload vertex data to GPU (static mesh) 3019 UploadMesh(&mesh, false); 3020 } 3021 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: torus"); 3022 3023 return mesh; 3024 } 3025 3026 // Generate trefoil knot mesh 3027 Mesh GenMeshKnot(float radius, float size, int radSeg, int sides) 3028 { 3029 Mesh mesh = { 0 }; 3030 3031 if ((sides >= 3) && (radSeg >= 3)) 3032 { 3033 if (radius > 3.0f) radius = 3.0f; 3034 else if (radius < 0.5f) radius = 0.5f; 3035 3036 par_shapes_mesh *knot = par_shapes_create_trefoil_knot(radSeg, sides, radius); 3037 par_shapes_scale(knot, size, size, size); 3038 3039 mesh.vertices = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float)); 3040 mesh.texcoords = (float *)RL_MALLOC(knot->ntriangles*3*2*sizeof(float)); 3041 mesh.normals = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float)); 3042 3043 mesh.vertexCount = knot->ntriangles*3; 3044 mesh.triangleCount = knot->ntriangles; 3045 3046 for (int k = 0; k < mesh.vertexCount; k++) 3047 { 3048 mesh.vertices[k*3] = knot->points[knot->triangles[k]*3]; 3049 mesh.vertices[k*3 + 1] = knot->points[knot->triangles[k]*3 + 1]; 3050 mesh.vertices[k*3 + 2] = knot->points[knot->triangles[k]*3 + 2]; 3051 3052 mesh.normals[k*3] = knot->normals[knot->triangles[k]*3]; 3053 mesh.normals[k*3 + 1] = knot->normals[knot->triangles[k]*3 + 1]; 3054 mesh.normals[k*3 + 2] = knot->normals[knot->triangles[k]*3 + 2]; 3055 3056 mesh.texcoords[k*2] = knot->tcoords[knot->triangles[k]*2]; 3057 mesh.texcoords[k*2 + 1] = knot->tcoords[knot->triangles[k]*2 + 1]; 3058 } 3059 3060 par_shapes_free_mesh(knot); 3061 3062 // Upload vertex data to GPU (static mesh) 3063 UploadMesh(&mesh, false); 3064 } 3065 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: knot"); 3066 3067 return mesh; 3068 } 3069 3070 // Generate a mesh from heightmap 3071 // NOTE: Vertex data is uploaded to GPU 3072 Mesh GenMeshHeightmap(Image heightmap, Vector3 size) 3073 { 3074 #define GRAY_VALUE(c) ((float)(c.r + c.g + c.b)/3.0f) 3075 3076 Mesh mesh = { 0 }; 3077 3078 int mapX = heightmap.width; 3079 int mapZ = heightmap.height; 3080 3081 Color *pixels = LoadImageColors(heightmap); 3082 3083 // NOTE: One vertex per pixel 3084 mesh.triangleCount = (mapX - 1)*(mapZ - 1)*2; // One quad every four pixels 3085 3086 mesh.vertexCount = mesh.triangleCount*3; 3087 3088 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 3089 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 3090 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); 3091 mesh.colors = NULL; 3092 3093 int vCounter = 0; // Used to count vertices float by float 3094 int tcCounter = 0; // Used to count texcoords float by float 3095 int nCounter = 0; // Used to count normals float by float 3096 3097 Vector3 scaleFactor = { size.x/(mapX - 1), size.y/255.0f, size.z/(mapZ - 1) }; 3098 3099 Vector3 vA = { 0 }; 3100 Vector3 vB = { 0 }; 3101 Vector3 vC = { 0 }; 3102 Vector3 vN = { 0 }; 3103 3104 for (int z = 0; z < mapZ-1; z++) 3105 { 3106 for (int x = 0; x < mapX-1; x++) 3107 { 3108 // Fill vertices array with data 3109 //---------------------------------------------------------- 3110 3111 // one triangle - 3 vertex 3112 mesh.vertices[vCounter] = (float)x*scaleFactor.x; 3113 mesh.vertices[vCounter + 1] = GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y; 3114 mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z; 3115 3116 mesh.vertices[vCounter + 3] = (float)x*scaleFactor.x; 3117 mesh.vertices[vCounter + 4] = GRAY_VALUE(pixels[x + (z + 1)*mapX])*scaleFactor.y; 3118 mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z; 3119 3120 mesh.vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x; 3121 mesh.vertices[vCounter + 7] = GRAY_VALUE(pixels[(x + 1) + z*mapX])*scaleFactor.y; 3122 mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z; 3123 3124 // Another triangle - 3 vertex 3125 mesh.vertices[vCounter + 9] = mesh.vertices[vCounter + 6]; 3126 mesh.vertices[vCounter + 10] = mesh.vertices[vCounter + 7]; 3127 mesh.vertices[vCounter + 11] = mesh.vertices[vCounter + 8]; 3128 3129 mesh.vertices[vCounter + 12] = mesh.vertices[vCounter + 3]; 3130 mesh.vertices[vCounter + 13] = mesh.vertices[vCounter + 4]; 3131 mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5]; 3132 3133 mesh.vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x; 3134 mesh.vertices[vCounter + 16] = GRAY_VALUE(pixels[(x + 1) + (z + 1)*mapX])*scaleFactor.y; 3135 mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z; 3136 vCounter += 18; // 6 vertex, 18 floats 3137 3138 // Fill texcoords array with data 3139 //-------------------------------------------------------------- 3140 mesh.texcoords[tcCounter] = (float)x/(mapX - 1); 3141 mesh.texcoords[tcCounter + 1] = (float)z/(mapZ - 1); 3142 3143 mesh.texcoords[tcCounter + 2] = (float)x/(mapX - 1); 3144 mesh.texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1); 3145 3146 mesh.texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1); 3147 mesh.texcoords[tcCounter + 5] = (float)z/(mapZ - 1); 3148 3149 mesh.texcoords[tcCounter + 6] = mesh.texcoords[tcCounter + 4]; 3150 mesh.texcoords[tcCounter + 7] = mesh.texcoords[tcCounter + 5]; 3151 3152 mesh.texcoords[tcCounter + 8] = mesh.texcoords[tcCounter + 2]; 3153 mesh.texcoords[tcCounter + 9] = mesh.texcoords[tcCounter + 3]; 3154 3155 mesh.texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1); 3156 mesh.texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1); 3157 tcCounter += 12; // 6 texcoords, 12 floats 3158 3159 // Fill normals array with data 3160 //-------------------------------------------------------------- 3161 for (int i = 0; i < 18; i += 9) 3162 { 3163 vA.x = mesh.vertices[nCounter + i]; 3164 vA.y = mesh.vertices[nCounter + i + 1]; 3165 vA.z = mesh.vertices[nCounter + i + 2]; 3166 3167 vB.x = mesh.vertices[nCounter + i + 3]; 3168 vB.y = mesh.vertices[nCounter + i + 4]; 3169 vB.z = mesh.vertices[nCounter + i + 5]; 3170 3171 vC.x = mesh.vertices[nCounter + i + 6]; 3172 vC.y = mesh.vertices[nCounter + i + 7]; 3173 vC.z = mesh.vertices[nCounter + i + 8]; 3174 3175 vN = Vector3Normalize(Vector3CrossProduct(Vector3Subtract(vB, vA), Vector3Subtract(vC, vA))); 3176 3177 mesh.normals[nCounter + i] = vN.x; 3178 mesh.normals[nCounter + i + 1] = vN.y; 3179 mesh.normals[nCounter + i + 2] = vN.z; 3180 3181 mesh.normals[nCounter + i + 3] = vN.x; 3182 mesh.normals[nCounter + i + 4] = vN.y; 3183 mesh.normals[nCounter + i + 5] = vN.z; 3184 3185 mesh.normals[nCounter + i + 6] = vN.x; 3186 mesh.normals[nCounter + i + 7] = vN.y; 3187 mesh.normals[nCounter + i + 8] = vN.z; 3188 } 3189 3190 nCounter += 18; // 6 vertex, 18 floats 3191 } 3192 } 3193 3194 UnloadImageColors(pixels); // Unload pixels color data 3195 3196 // Upload vertex data to GPU (static mesh) 3197 UploadMesh(&mesh, false); 3198 3199 return mesh; 3200 } 3201 3202 // Generate a cubes mesh from pixel data 3203 // NOTE: Vertex data is uploaded to GPU 3204 Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) 3205 { 3206 #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a)) 3207 3208 Mesh mesh = { 0 }; 3209 3210 Color *pixels = LoadImageColors(cubicmap); 3211 3212 // NOTE: Max possible number of triangles numCubes*(12 triangles by cube) 3213 int maxTriangles = cubicmap.width*cubicmap.height*12; 3214 3215 int vCounter = 0; // Used to count vertices 3216 int tcCounter = 0; // Used to count texcoords 3217 int nCounter = 0; // Used to count normals 3218 3219 float w = cubeSize.x; 3220 float h = cubeSize.z; 3221 float h2 = cubeSize.y; 3222 3223 Vector3 *mapVertices = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3)); 3224 Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2)); 3225 Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3)); 3226 3227 // Define the 6 normals of the cube, we will combine them accordingly later... 3228 Vector3 n1 = { 1.0f, 0.0f, 0.0f }; 3229 Vector3 n2 = { -1.0f, 0.0f, 0.0f }; 3230 Vector3 n3 = { 0.0f, 1.0f, 0.0f }; 3231 Vector3 n4 = { 0.0f, -1.0f, 0.0f }; 3232 Vector3 n5 = { 0.0f, 0.0f, -1.0f }; 3233 Vector3 n6 = { 0.0f, 0.0f, 1.0f }; 3234 3235 // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6) 3236 typedef struct RectangleF { 3237 float x; 3238 float y; 3239 float width; 3240 float height; 3241 } RectangleF; 3242 3243 RectangleF rightTexUV = { 0.0f, 0.0f, 0.5f, 0.5f }; 3244 RectangleF leftTexUV = { 0.5f, 0.0f, 0.5f, 0.5f }; 3245 RectangleF frontTexUV = { 0.0f, 0.0f, 0.5f, 0.5f }; 3246 RectangleF backTexUV = { 0.5f, 0.0f, 0.5f, 0.5f }; 3247 RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f }; 3248 RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f }; 3249 3250 for (int z = 0; z < cubicmap.height; ++z) 3251 { 3252 for (int x = 0; x < cubicmap.width; ++x) 3253 { 3254 // Define the 8 vertex of the cube, we will combine them accordingly later... 3255 Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) }; 3256 Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) }; 3257 Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) }; 3258 Vector3 v4 = { w*(x + 0.5f), h2, h*(z - 0.5f) }; 3259 Vector3 v5 = { w*(x + 0.5f), 0, h*(z - 0.5f) }; 3260 Vector3 v6 = { w*(x - 0.5f), 0, h*(z - 0.5f) }; 3261 Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) }; 3262 Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) }; 3263 3264 // We check pixel color to be WHITE -> draw full cube 3265 if (COLOR_EQUAL(pixels[z*cubicmap.width + x], WHITE)) 3266 { 3267 // Define triangles and checking collateral cubes 3268 //------------------------------------------------ 3269 3270 // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4) 3271 // WARNING: Not required for a WHITE cubes, created to allow seeing the map from outside 3272 mapVertices[vCounter] = v1; 3273 mapVertices[vCounter + 1] = v2; 3274 mapVertices[vCounter + 2] = v3; 3275 mapVertices[vCounter + 3] = v1; 3276 mapVertices[vCounter + 4] = v3; 3277 mapVertices[vCounter + 5] = v4; 3278 vCounter += 6; 3279 3280 mapNormals[nCounter] = n3; 3281 mapNormals[nCounter + 1] = n3; 3282 mapNormals[nCounter + 2] = n3; 3283 mapNormals[nCounter + 3] = n3; 3284 mapNormals[nCounter + 4] = n3; 3285 mapNormals[nCounter + 5] = n3; 3286 nCounter += 6; 3287 3288 mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y }; 3289 mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height }; 3290 mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; 3291 mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y }; 3292 mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; 3293 mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y }; 3294 tcCounter += 6; 3295 3296 // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8) 3297 mapVertices[vCounter] = v6; 3298 mapVertices[vCounter + 1] = v8; 3299 mapVertices[vCounter + 2] = v7; 3300 mapVertices[vCounter + 3] = v6; 3301 mapVertices[vCounter + 4] = v5; 3302 mapVertices[vCounter + 5] = v8; 3303 vCounter += 6; 3304 3305 mapNormals[nCounter] = n4; 3306 mapNormals[nCounter + 1] = n4; 3307 mapNormals[nCounter + 2] = n4; 3308 mapNormals[nCounter + 3] = n4; 3309 mapNormals[nCounter + 4] = n4; 3310 mapNormals[nCounter + 5] = n4; 3311 nCounter += 6; 3312 3313 mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; 3314 mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; 3315 mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height }; 3316 mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; 3317 mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y }; 3318 mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; 3319 tcCounter += 6; 3320 3321 // Checking cube on bottom of current cube 3322 if (((z < cubicmap.height - 1) && COLOR_EQUAL(pixels[(z + 1)*cubicmap.width + x], BLACK)) || (z == cubicmap.height - 1)) 3323 { 3324 // Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8 3325 // NOTE: Collateral occluded faces are not generated 3326 mapVertices[vCounter] = v2; 3327 mapVertices[vCounter + 1] = v7; 3328 mapVertices[vCounter + 2] = v3; 3329 mapVertices[vCounter + 3] = v3; 3330 mapVertices[vCounter + 4] = v7; 3331 mapVertices[vCounter + 5] = v8; 3332 vCounter += 6; 3333 3334 mapNormals[nCounter] = n6; 3335 mapNormals[nCounter + 1] = n6; 3336 mapNormals[nCounter + 2] = n6; 3337 mapNormals[nCounter + 3] = n6; 3338 mapNormals[nCounter + 4] = n6; 3339 mapNormals[nCounter + 5] = n6; 3340 nCounter += 6; 3341 3342 mapTexcoords[tcCounter] = (Vector2){ frontTexUV.x, frontTexUV.y }; 3343 mapTexcoords[tcCounter + 1] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height }; 3344 mapTexcoords[tcCounter + 2] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y }; 3345 mapTexcoords[tcCounter + 3] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y }; 3346 mapTexcoords[tcCounter + 4] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height }; 3347 mapTexcoords[tcCounter + 5] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y + frontTexUV.height }; 3348 tcCounter += 6; 3349 } 3350 3351 // Checking cube on top of current cube 3352 if (((z > 0) && COLOR_EQUAL(pixels[(z - 1)*cubicmap.width + x], BLACK)) || (z == 0)) 3353 { 3354 // Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5 3355 // NOTE: Collateral occluded faces are not generated 3356 mapVertices[vCounter] = v1; 3357 mapVertices[vCounter + 1] = v5; 3358 mapVertices[vCounter + 2] = v6; 3359 mapVertices[vCounter + 3] = v1; 3360 mapVertices[vCounter + 4] = v4; 3361 mapVertices[vCounter + 5] = v5; 3362 vCounter += 6; 3363 3364 mapNormals[nCounter] = n5; 3365 mapNormals[nCounter + 1] = n5; 3366 mapNormals[nCounter + 2] = n5; 3367 mapNormals[nCounter + 3] = n5; 3368 mapNormals[nCounter + 4] = n5; 3369 mapNormals[nCounter + 5] = n5; 3370 nCounter += 6; 3371 3372 mapTexcoords[tcCounter] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y }; 3373 mapTexcoords[tcCounter + 1] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height }; 3374 mapTexcoords[tcCounter + 2] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y + backTexUV.height }; 3375 mapTexcoords[tcCounter + 3] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y }; 3376 mapTexcoords[tcCounter + 4] = (Vector2){ backTexUV.x, backTexUV.y }; 3377 mapTexcoords[tcCounter + 5] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height }; 3378 tcCounter += 6; 3379 } 3380 3381 // Checking cube on right of current cube 3382 if (((x < cubicmap.width - 1) && COLOR_EQUAL(pixels[z*cubicmap.width + (x + 1)], BLACK)) || (x == cubicmap.width - 1)) 3383 { 3384 // Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5 3385 // NOTE: Collateral occluded faces are not generated 3386 mapVertices[vCounter] = v3; 3387 mapVertices[vCounter + 1] = v8; 3388 mapVertices[vCounter + 2] = v4; 3389 mapVertices[vCounter + 3] = v4; 3390 mapVertices[vCounter + 4] = v8; 3391 mapVertices[vCounter + 5] = v5; 3392 vCounter += 6; 3393 3394 mapNormals[nCounter] = n1; 3395 mapNormals[nCounter + 1] = n1; 3396 mapNormals[nCounter + 2] = n1; 3397 mapNormals[nCounter + 3] = n1; 3398 mapNormals[nCounter + 4] = n1; 3399 mapNormals[nCounter + 5] = n1; 3400 nCounter += 6; 3401 3402 mapTexcoords[tcCounter] = (Vector2){ rightTexUV.x, rightTexUV.y }; 3403 mapTexcoords[tcCounter + 1] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height }; 3404 mapTexcoords[tcCounter + 2] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y }; 3405 mapTexcoords[tcCounter + 3] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y }; 3406 mapTexcoords[tcCounter + 4] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height }; 3407 mapTexcoords[tcCounter + 5] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y + rightTexUV.height }; 3408 tcCounter += 6; 3409 } 3410 3411 // Checking cube on left of current cube 3412 if (((x > 0) && COLOR_EQUAL(pixels[z*cubicmap.width + (x - 1)], BLACK)) || (x == 0)) 3413 { 3414 // Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7 3415 // NOTE: Collateral occluded faces are not generated 3416 mapVertices[vCounter] = v1; 3417 mapVertices[vCounter + 1] = v7; 3418 mapVertices[vCounter + 2] = v2; 3419 mapVertices[vCounter + 3] = v1; 3420 mapVertices[vCounter + 4] = v6; 3421 mapVertices[vCounter + 5] = v7; 3422 vCounter += 6; 3423 3424 mapNormals[nCounter] = n2; 3425 mapNormals[nCounter + 1] = n2; 3426 mapNormals[nCounter + 2] = n2; 3427 mapNormals[nCounter + 3] = n2; 3428 mapNormals[nCounter + 4] = n2; 3429 mapNormals[nCounter + 5] = n2; 3430 nCounter += 6; 3431 3432 mapTexcoords[tcCounter] = (Vector2){ leftTexUV.x, leftTexUV.y }; 3433 mapTexcoords[tcCounter + 1] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height }; 3434 mapTexcoords[tcCounter + 2] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y }; 3435 mapTexcoords[tcCounter + 3] = (Vector2){ leftTexUV.x, leftTexUV.y }; 3436 mapTexcoords[tcCounter + 4] = (Vector2){ leftTexUV.x, leftTexUV.y + leftTexUV.height }; 3437 mapTexcoords[tcCounter + 5] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height }; 3438 tcCounter += 6; 3439 } 3440 } 3441 // We check pixel color to be BLACK, we will only draw floor and roof 3442 else if (COLOR_EQUAL(pixels[z*cubicmap.width + x], BLACK)) 3443 { 3444 // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4) 3445 mapVertices[vCounter] = v1; 3446 mapVertices[vCounter + 1] = v3; 3447 mapVertices[vCounter + 2] = v2; 3448 mapVertices[vCounter + 3] = v1; 3449 mapVertices[vCounter + 4] = v4; 3450 mapVertices[vCounter + 5] = v3; 3451 vCounter += 6; 3452 3453 mapNormals[nCounter] = n4; 3454 mapNormals[nCounter + 1] = n4; 3455 mapNormals[nCounter + 2] = n4; 3456 mapNormals[nCounter + 3] = n4; 3457 mapNormals[nCounter + 4] = n4; 3458 mapNormals[nCounter + 5] = n4; 3459 nCounter += 6; 3460 3461 mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y }; 3462 mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; 3463 mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height }; 3464 mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y }; 3465 mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y }; 3466 mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height }; 3467 tcCounter += 6; 3468 3469 // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8) 3470 mapVertices[vCounter] = v6; 3471 mapVertices[vCounter + 1] = v7; 3472 mapVertices[vCounter + 2] = v8; 3473 mapVertices[vCounter + 3] = v6; 3474 mapVertices[vCounter + 4] = v8; 3475 mapVertices[vCounter + 5] = v5; 3476 vCounter += 6; 3477 3478 mapNormals[nCounter] = n3; 3479 mapNormals[nCounter + 1] = n3; 3480 mapNormals[nCounter + 2] = n3; 3481 mapNormals[nCounter + 3] = n3; 3482 mapNormals[nCounter + 4] = n3; 3483 mapNormals[nCounter + 5] = n3; 3484 nCounter += 6; 3485 3486 mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; 3487 mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height }; 3488 mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; 3489 mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y }; 3490 mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height }; 3491 mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y }; 3492 tcCounter += 6; 3493 } 3494 } 3495 } 3496 3497 // Move data from mapVertices temp arrays to vertices float array 3498 mesh.vertexCount = vCounter; 3499 mesh.triangleCount = vCounter/3; 3500 3501 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 3502 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float)); 3503 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); 3504 mesh.colors = NULL; 3505 3506 int fCounter = 0; 3507 3508 // Move vertices data 3509 for (int i = 0; i < vCounter; i++) 3510 { 3511 mesh.vertices[fCounter] = mapVertices[i].x; 3512 mesh.vertices[fCounter + 1] = mapVertices[i].y; 3513 mesh.vertices[fCounter + 2] = mapVertices[i].z; 3514 fCounter += 3; 3515 } 3516 3517 fCounter = 0; 3518 3519 // Move normals data 3520 for (int i = 0; i < nCounter; i++) 3521 { 3522 mesh.normals[fCounter] = mapNormals[i].x; 3523 mesh.normals[fCounter + 1] = mapNormals[i].y; 3524 mesh.normals[fCounter + 2] = mapNormals[i].z; 3525 fCounter += 3; 3526 } 3527 3528 fCounter = 0; 3529 3530 // Move texcoords data 3531 for (int i = 0; i < tcCounter; i++) 3532 { 3533 mesh.texcoords[fCounter] = mapTexcoords[i].x; 3534 mesh.texcoords[fCounter + 1] = mapTexcoords[i].y; 3535 fCounter += 2; 3536 } 3537 3538 RL_FREE(mapVertices); 3539 RL_FREE(mapNormals); 3540 RL_FREE(mapTexcoords); 3541 3542 UnloadImageColors(pixels); // Unload pixels color data 3543 3544 // Upload vertex data to GPU (static mesh) 3545 UploadMesh(&mesh, false); 3546 3547 return mesh; 3548 } 3549 #endif // SUPPORT_MESH_GENERATION 3550 3551 // Compute mesh bounding box limits 3552 // NOTE: minVertex and maxVertex should be transformed by model transform matrix 3553 BoundingBox GetMeshBoundingBox(Mesh mesh) 3554 { 3555 // Get min and max vertex to construct bounds (AABB) 3556 Vector3 minVertex = { 0 }; 3557 Vector3 maxVertex = { 0 }; 3558 3559 if (mesh.vertices != NULL) 3560 { 3561 minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; 3562 maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] }; 3563 3564 for (int i = 1; i < mesh.vertexCount; i++) 3565 { 3566 minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); 3567 maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] }); 3568 } 3569 } 3570 3571 // Create the bounding box 3572 BoundingBox box = { 0 }; 3573 box.min = minVertex; 3574 box.max = maxVertex; 3575 3576 return box; 3577 } 3578 3579 // Compute mesh tangents 3580 // NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates 3581 // Implementation based on: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html 3582 void GenMeshTangents(Mesh *mesh) 3583 { 3584 if ((mesh->vertices == NULL) || (mesh->texcoords == NULL)) 3585 { 3586 TRACELOG(LOG_WARNING, "MESH: Tangents generation requires texcoord vertex attribute data"); 3587 return; 3588 } 3589 3590 if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float)); 3591 else 3592 { 3593 RL_FREE(mesh->tangents); 3594 mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float)); 3595 } 3596 3597 Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); 3598 Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); 3599 3600 if (mesh->vertexCount % 3 != 0) 3601 { 3602 TRACELOG(LOG_WARNING, "MESH: vertexCount expected to be a multiple of 3. Expect uninitialized values."); 3603 } 3604 3605 for (int i = 0; i <= mesh->vertexCount - 3; i += 3) 3606 { 3607 // Get triangle vertices 3608 Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; 3609 Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; 3610 Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; 3611 3612 // Get triangle texcoords 3613 Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; 3614 Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; 3615 Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; 3616 3617 float x1 = v2.x - v1.x; 3618 float y1 = v2.y - v1.y; 3619 float z1 = v2.z - v1.z; 3620 float x2 = v3.x - v1.x; 3621 float y2 = v3.y - v1.y; 3622 float z2 = v3.z - v1.z; 3623 3624 float s1 = uv2.x - uv1.x; 3625 float t1 = uv2.y - uv1.y; 3626 float s2 = uv3.x - uv1.x; 3627 float t2 = uv3.y - uv1.y; 3628 3629 float div = s1*t2 - s2*t1; 3630 float r = (div == 0.0f)? 0.0f : 1.0f/div; 3631 3632 Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; 3633 Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; 3634 3635 tan1[i + 0] = sdir; 3636 tan1[i + 1] = sdir; 3637 tan1[i + 2] = sdir; 3638 3639 tan2[i + 0] = tdir; 3640 tan2[i + 1] = tdir; 3641 tan2[i + 2] = tdir; 3642 } 3643 3644 // Compute tangents considering normals 3645 for (int i = 0; i < mesh->vertexCount; i++) 3646 { 3647 Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; 3648 Vector3 tangent = tan1[i]; 3649 3650 // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... 3651 #if defined(COMPUTE_TANGENTS_METHOD_01) 3652 Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent))); 3653 tmp = Vector3Normalize(tmp); 3654 mesh->tangents[i*4 + 0] = tmp.x; 3655 mesh->tangents[i*4 + 1] = tmp.y; 3656 mesh->tangents[i*4 + 2] = tmp.z; 3657 mesh->tangents[i*4 + 3] = 1.0f; 3658 #else 3659 Vector3OrthoNormalize(&normal, &tangent); 3660 mesh->tangents[i*4 + 0] = tangent.x; 3661 mesh->tangents[i*4 + 1] = tangent.y; 3662 mesh->tangents[i*4 + 2] = tangent.z; 3663 mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; 3664 #endif 3665 } 3666 3667 RL_FREE(tan1); 3668 RL_FREE(tan2); 3669 3670 if (mesh->vboId != NULL) 3671 { 3672 if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0) 3673 { 3674 // Update existing vertex buffer 3675 rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0); 3676 } 3677 else 3678 { 3679 // Load a new tangent attributes buffer 3680 mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false); 3681 } 3682 3683 rlEnableVertexArray(mesh->vaoId); 3684 rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0); 3685 rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT); 3686 rlDisableVertexArray(); 3687 } 3688 3689 TRACELOG(LOG_INFO, "MESH: Tangents data computed and uploaded for provided mesh"); 3690 } 3691 3692 // Draw a model (with texture if set) 3693 void DrawModel(Model model, Vector3 position, float scale, Color tint) 3694 { 3695 Vector3 vScale = { scale, scale, scale }; 3696 Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f }; 3697 3698 DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint); 3699 } 3700 3701 // Draw a model with extended parameters 3702 void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) 3703 { 3704 // Calculate transformation matrix from function parameters 3705 // Get transform matrix (rotation -> scale -> translation) 3706 Matrix matScale = MatrixScale(scale.x, scale.y, scale.z); 3707 Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD); 3708 Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z); 3709 3710 Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); 3711 3712 // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) 3713 model.transform = MatrixMultiply(model.transform, matTransform); 3714 3715 for (int i = 0; i < model.meshCount; i++) 3716 { 3717 Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color; 3718 3719 Color colorTint = WHITE; 3720 colorTint.r = (unsigned char)(((int)color.r*(int)tint.r)/255); 3721 colorTint.g = (unsigned char)(((int)color.g*(int)tint.g)/255); 3722 colorTint.b = (unsigned char)(((int)color.b*(int)tint.b)/255); 3723 colorTint.a = (unsigned char)(((int)color.a*(int)tint.a)/255); 3724 3725 model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint; 3726 DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform); 3727 model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color; 3728 } 3729 } 3730 3731 // Draw a model wires (with texture if set) 3732 void DrawModelWires(Model model, Vector3 position, float scale, Color tint) 3733 { 3734 rlEnableWireMode(); 3735 3736 DrawModel(model, position, scale, tint); 3737 3738 rlDisableWireMode(); 3739 } 3740 3741 // Draw a model wires (with texture if set) with extended parameters 3742 void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) 3743 { 3744 rlEnableWireMode(); 3745 3746 DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint); 3747 3748 rlDisableWireMode(); 3749 } 3750 3751 // Draw a model points 3752 void DrawModelPoints(Model model, Vector3 position, float scale, Color tint) 3753 { 3754 rlEnablePointMode(); 3755 rlDisableBackfaceCulling(); 3756 3757 DrawModel(model, position, scale, tint); 3758 3759 rlEnableBackfaceCulling(); 3760 rlDisableWireMode(); 3761 } 3762 3763 // Draw a model points 3764 void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) 3765 { 3766 rlEnablePointMode(); 3767 rlDisableBackfaceCulling(); 3768 3769 DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint); 3770 3771 rlEnableBackfaceCulling(); 3772 rlDisableWireMode(); 3773 } 3774 3775 // Draw a billboard 3776 void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint) 3777 { 3778 Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height }; 3779 3780 DrawBillboardRec(camera, texture, source, position, (Vector2) { scale*fabsf((float)source.width/source.height), scale }, tint); 3781 } 3782 3783 // Draw a billboard (part of a texture defined by a rectangle) 3784 void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint) 3785 { 3786 // NOTE: Billboard locked on axis-Y 3787 Vector3 up = { 0.0f, 1.0f, 0.0f }; 3788 3789 DrawBillboardPro(camera, texture, source, position, up, size, Vector2Scale(size, 0.5), 0.0f, tint); 3790 } 3791 3792 // Draw a billboard with additional parameters 3793 void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint) 3794 { 3795 // Compute the up vector and the right vector 3796 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); 3797 Vector3 right = { matView.m0, matView.m4, matView.m8 }; 3798 right = Vector3Scale(right, size.x); 3799 up = Vector3Scale(up, size.y); 3800 3801 // Flip the content of the billboard while maintaining the counterclockwise edge rendering order 3802 if (size.x < 0.0f) 3803 { 3804 source.x += size.x; 3805 source.width *= -1.0; 3806 right = Vector3Negate(right); 3807 origin.x *= -1.0f; 3808 } 3809 if (size.y < 0.0f) 3810 { 3811 source.y += size.y; 3812 source.height *= -1.0; 3813 up = Vector3Negate(up); 3814 origin.y *= -1.0f; 3815 } 3816 3817 // Draw the texture region described by source on the following rectangle in 3D space: 3818 // 3819 // size.x <--. 3820 // 3 ^---------------------------+ 2 \ rotation 3821 // | | / 3822 // | | 3823 // | origin.x position | 3824 // up |.............. | size.y 3825 // | . | 3826 // | . origin.y | 3827 // | . | 3828 // 0 +---------------------------> 1 3829 // right 3830 Vector3 forward; 3831 if (rotation != 0.0) forward = Vector3CrossProduct(right, up); 3832 3833 Vector3 origin3D = Vector3Add(Vector3Scale(Vector3Normalize(right), origin.x), Vector3Scale(Vector3Normalize(up), origin.y)); 3834 3835 Vector3 points[4]; 3836 points[0] = Vector3Zero(); 3837 points[1] = right; 3838 points[2] = Vector3Add(up, right); 3839 points[3] = up; 3840 3841 for (int i = 0; i < 4; i++) 3842 { 3843 points[i] = Vector3Subtract(points[i], origin3D); 3844 if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation * DEG2RAD); 3845 points[i] = Vector3Add(points[i], position); 3846 } 3847 3848 Vector2 texcoords[4]; 3849 texcoords[0] = (Vector2) { (float)source.x/texture.width, (float)(source.y + source.height)/texture.height }; 3850 texcoords[1] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height }; 3851 texcoords[2] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)source.y/texture.height }; 3852 texcoords[3] = (Vector2) { (float)source.x/texture.width, (float)source.y/texture.height }; 3853 3854 rlSetTexture(texture.id); 3855 rlBegin(RL_QUADS); 3856 3857 rlColor4ub(tint.r, tint.g, tint.b, tint.a); 3858 for (int i = 0; i < 4; i++) 3859 { 3860 rlTexCoord2f(texcoords[i].x, texcoords[i].y); 3861 rlVertex3f(points[i].x, points[i].y, points[i].z); 3862 } 3863 3864 rlEnd(); 3865 rlSetTexture(0); 3866 } 3867 3868 // Draw a bounding box with wires 3869 void DrawBoundingBox(BoundingBox box, Color color) 3870 { 3871 Vector3 size = { 0 }; 3872 3873 size.x = fabsf(box.max.x - box.min.x); 3874 size.y = fabsf(box.max.y - box.min.y); 3875 size.z = fabsf(box.max.z - box.min.z); 3876 3877 Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f }; 3878 3879 DrawCubeWires(center, size.x, size.y, size.z, color); 3880 } 3881 3882 // Check collision between two spheres 3883 bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2) 3884 { 3885 bool collision = false; 3886 3887 // Simple way to check for collision, just checking distance between two points 3888 // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution 3889 /* 3890 float dx = center1.x - center2.x; // X distance between centers 3891 float dy = center1.y - center2.y; // Y distance between centers 3892 float dz = center1.z - center2.z; // Z distance between centers 3893 3894 float distance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance between centers 3895 3896 if (distance <= (radius1 + radius2)) collision = true; 3897 */ 3898 3899 // Check for distances squared to avoid sqrtf() 3900 if (Vector3DotProduct(Vector3Subtract(center2, center1), Vector3Subtract(center2, center1)) <= (radius1 + radius2)*(radius1 + radius2)) collision = true; 3901 3902 return collision; 3903 } 3904 3905 // Check collision between two boxes 3906 // NOTE: Boxes are defined by two points minimum and maximum 3907 bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2) 3908 { 3909 bool collision = true; 3910 3911 if ((box1.max.x >= box2.min.x) && (box1.min.x <= box2.max.x)) 3912 { 3913 if ((box1.max.y < box2.min.y) || (box1.min.y > box2.max.y)) collision = false; 3914 if ((box1.max.z < box2.min.z) || (box1.min.z > box2.max.z)) collision = false; 3915 } 3916 else collision = false; 3917 3918 return collision; 3919 } 3920 3921 // Check collision between box and sphere 3922 bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius) 3923 { 3924 bool collision = false; 3925 3926 float dmin = 0; 3927 3928 if (center.x < box.min.x) dmin += powf(center.x - box.min.x, 2); 3929 else if (center.x > box.max.x) dmin += powf(center.x - box.max.x, 2); 3930 3931 if (center.y < box.min.y) dmin += powf(center.y - box.min.y, 2); 3932 else if (center.y > box.max.y) dmin += powf(center.y - box.max.y, 2); 3933 3934 if (center.z < box.min.z) dmin += powf(center.z - box.min.z, 2); 3935 else if (center.z > box.max.z) dmin += powf(center.z - box.max.z, 2); 3936 3937 if (dmin <= (radius*radius)) collision = true; 3938 3939 return collision; 3940 } 3941 3942 // Get collision info between ray and sphere 3943 RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius) 3944 { 3945 RayCollision collision = { 0 }; 3946 3947 Vector3 raySpherePos = Vector3Subtract(center, ray.position); 3948 float vector = Vector3DotProduct(raySpherePos, ray.direction); 3949 float distance = Vector3Length(raySpherePos); 3950 float d = radius*radius - (distance*distance - vector*vector); 3951 3952 collision.hit = d >= 0.0f; 3953 3954 // Check if ray origin is inside the sphere to calculate the correct collision point 3955 if (distance < radius) 3956 { 3957 collision.distance = vector + sqrtf(d); 3958 3959 // Calculate collision point 3960 collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance)); 3961 3962 // Calculate collision normal (pointing outwards) 3963 collision.normal = Vector3Negate(Vector3Normalize(Vector3Subtract(collision.point, center))); 3964 } 3965 else 3966 { 3967 collision.distance = vector - sqrtf(d); 3968 3969 // Calculate collision point 3970 collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance)); 3971 3972 // Calculate collision normal (pointing inwards) 3973 collision.normal = Vector3Normalize(Vector3Subtract(collision.point, center)); 3974 } 3975 3976 return collision; 3977 } 3978 3979 // Get collision info between ray and box 3980 RayCollision GetRayCollisionBox(Ray ray, BoundingBox box) 3981 { 3982 RayCollision collision = { 0 }; 3983 3984 // Note: If ray.position is inside the box, the distance is negative (as if the ray was reversed) 3985 // Reversing ray.direction will give use the correct result 3986 bool insideBox = (ray.position.x > box.min.x) && (ray.position.x < box.max.x) && 3987 (ray.position.y > box.min.y) && (ray.position.y < box.max.y) && 3988 (ray.position.z > box.min.z) && (ray.position.z < box.max.z); 3989 3990 if (insideBox) ray.direction = Vector3Negate(ray.direction); 3991 3992 float t[11] = { 0 }; 3993 3994 t[8] = 1.0f/ray.direction.x; 3995 t[9] = 1.0f/ray.direction.y; 3996 t[10] = 1.0f/ray.direction.z; 3997 3998 t[0] = (box.min.x - ray.position.x)*t[8]; 3999 t[1] = (box.max.x - ray.position.x)*t[8]; 4000 t[2] = (box.min.y - ray.position.y)*t[9]; 4001 t[3] = (box.max.y - ray.position.y)*t[9]; 4002 t[4] = (box.min.z - ray.position.z)*t[10]; 4003 t[5] = (box.max.z - ray.position.z)*t[10]; 4004 t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5])); 4005 t[7] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5])); 4006 4007 collision.hit = !((t[7] < 0) || (t[6] > t[7])); 4008 collision.distance = t[6]; 4009 collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance)); 4010 4011 // Get box center point 4012 collision.normal = Vector3Lerp(box.min, box.max, 0.5f); 4013 // Get vector center point->hit point 4014 collision.normal = Vector3Subtract(collision.point, collision.normal); 4015 // Scale vector to unit cube 4016 // NOTE: We use an additional .01 to fix numerical errors 4017 collision.normal = Vector3Scale(collision.normal, 2.01f); 4018 collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min)); 4019 // The relevant elements of the vector are now slightly larger than 1.0f (or smaller than -1.0f) 4020 // and the others are somewhere between -1.0 and 1.0 casting to int is exactly our wanted normal! 4021 collision.normal.x = (float)((int)collision.normal.x); 4022 collision.normal.y = (float)((int)collision.normal.y); 4023 collision.normal.z = (float)((int)collision.normal.z); 4024 4025 collision.normal = Vector3Normalize(collision.normal); 4026 4027 if (insideBox) 4028 { 4029 // Reset ray.direction 4030 ray.direction = Vector3Negate(ray.direction); 4031 // Fix result 4032 collision.distance *= -1.0f; 4033 collision.normal = Vector3Negate(collision.normal); 4034 } 4035 4036 return collision; 4037 } 4038 4039 // Get collision info between ray and mesh 4040 RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform) 4041 { 4042 RayCollision collision = { 0 }; 4043 4044 // Check if mesh vertex data on CPU for testing 4045 if (mesh.vertices != NULL) 4046 { 4047 int triangleCount = mesh.triangleCount; 4048 4049 // Test against all triangles in mesh 4050 for (int i = 0; i < triangleCount; i++) 4051 { 4052 Vector3 a, b, c; 4053 Vector3* vertdata = (Vector3*)mesh.vertices; 4054 4055 if (mesh.indices) 4056 { 4057 a = vertdata[mesh.indices[i*3 + 0]]; 4058 b = vertdata[mesh.indices[i*3 + 1]]; 4059 c = vertdata[mesh.indices[i*3 + 2]]; 4060 } 4061 else 4062 { 4063 a = vertdata[i*3 + 0]; 4064 b = vertdata[i*3 + 1]; 4065 c = vertdata[i*3 + 2]; 4066 } 4067 4068 a = Vector3Transform(a, transform); 4069 b = Vector3Transform(b, transform); 4070 c = Vector3Transform(c, transform); 4071 4072 RayCollision triHitInfo = GetRayCollisionTriangle(ray, a, b, c); 4073 4074 if (triHitInfo.hit) 4075 { 4076 // Save the closest hit triangle 4077 if ((!collision.hit) || (collision.distance > triHitInfo.distance)) collision = triHitInfo; 4078 } 4079 } 4080 } 4081 4082 return collision; 4083 } 4084 4085 // Get collision info between ray and triangle 4086 // NOTE: The points are expected to be in counter-clockwise winding 4087 // NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm 4088 RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3) 4089 { 4090 #define EPSILON 0.000001f // A small number 4091 4092 RayCollision collision = { 0 }; 4093 Vector3 edge1 = { 0 }; 4094 Vector3 edge2 = { 0 }; 4095 Vector3 p, q, tv; 4096 float det, invDet, u, v, t; 4097 4098 // Find vectors for two edges sharing V1 4099 edge1 = Vector3Subtract(p2, p1); 4100 edge2 = Vector3Subtract(p3, p1); 4101 4102 // Begin calculating determinant - also used to calculate u parameter 4103 p = Vector3CrossProduct(ray.direction, edge2); 4104 4105 // If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle 4106 det = Vector3DotProduct(edge1, p); 4107 4108 // Avoid culling! 4109 if ((det > -EPSILON) && (det < EPSILON)) return collision; 4110 4111 invDet = 1.0f/det; 4112 4113 // Calculate distance from V1 to ray origin 4114 tv = Vector3Subtract(ray.position, p1); 4115 4116 // Calculate u parameter and test bound 4117 u = Vector3DotProduct(tv, p)*invDet; 4118 4119 // The intersection lies outside the triangle 4120 if ((u < 0.0f) || (u > 1.0f)) return collision; 4121 4122 // Prepare to test v parameter 4123 q = Vector3CrossProduct(tv, edge1); 4124 4125 // Calculate V parameter and test bound 4126 v = Vector3DotProduct(ray.direction, q)*invDet; 4127 4128 // The intersection lies outside the triangle 4129 if ((v < 0.0f) || ((u + v) > 1.0f)) return collision; 4130 4131 t = Vector3DotProduct(edge2, q)*invDet; 4132 4133 if (t > EPSILON) 4134 { 4135 // Ray hit, get hit point and normal 4136 collision.hit = true; 4137 collision.distance = t; 4138 collision.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2)); 4139 collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, t)); 4140 } 4141 4142 return collision; 4143 } 4144 4145 // Get collision info between ray and quad 4146 // NOTE: The points are expected to be in counter-clockwise winding 4147 RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4) 4148 { 4149 RayCollision collision = { 0 }; 4150 4151 collision = GetRayCollisionTriangle(ray, p1, p2, p4); 4152 4153 if (!collision.hit) collision = GetRayCollisionTriangle(ray, p2, p3, p4); 4154 4155 return collision; 4156 } 4157 4158 //---------------------------------------------------------------------------------- 4159 // Module specific Functions Definition 4160 //---------------------------------------------------------------------------------- 4161 #if defined(SUPPORT_FILEFORMAT_IQM) || defined(SUPPORT_FILEFORMAT_GLTF) 4162 // Build pose from parent joints 4163 // NOTE: Required for animations loading (required by IQM and GLTF) 4164 static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform *transforms) 4165 { 4166 for (int i = 0; i < boneCount; i++) 4167 { 4168 if (bones[i].parent >= 0) 4169 { 4170 if (bones[i].parent > i) 4171 { 4172 TRACELOG(LOG_WARNING, "Assumes bones are toplogically sorted, but bone %d has parent %d. Skipping.", i, bones[i].parent); 4173 continue; 4174 } 4175 transforms[i].rotation = QuaternionMultiply(transforms[bones[i].parent].rotation, transforms[i].rotation); 4176 transforms[i].translation = Vector3RotateByQuaternion(transforms[i].translation, transforms[bones[i].parent].rotation); 4177 transforms[i].translation = Vector3Add(transforms[i].translation, transforms[bones[i].parent].translation); 4178 transforms[i].scale = Vector3Multiply(transforms[i].scale, transforms[bones[i].parent].scale); 4179 } 4180 } 4181 } 4182 #endif 4183 4184 #if defined(SUPPORT_FILEFORMAT_OBJ) 4185 // Load OBJ mesh data 4186 // 4187 // Keep the following information in mind when reading this 4188 // - A mesh is created for every material present in the obj file 4189 // - the model.meshCount is therefore the materialCount returned from tinyobj 4190 // - the mesh is automatically triangulated by tinyobj 4191 static Model LoadOBJ(const char *fileName) 4192 { 4193 tinyobj_attrib_t objAttributes = { 0 }; 4194 tinyobj_shape_t* objShapes = NULL; 4195 unsigned int objShapeCount = 0; 4196 4197 tinyobj_material_t* objMaterials = NULL; 4198 unsigned int objMaterialCount = 0; 4199 4200 Model model = { 0 }; 4201 model.transform = MatrixIdentity(); 4202 4203 char* fileText = LoadFileText(fileName); 4204 4205 if (fileText == NULL) 4206 { 4207 TRACELOG(LOG_ERROR, "MODEL Unable to read obj file %s", fileName); 4208 return model; 4209 } 4210 4211 char currentDir[1024] = { 0 }; 4212 strcpy(currentDir, GetWorkingDirectory()); // Save current working directory 4213 const char* workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness 4214 if (CHDIR(workingDir) != 0) 4215 { 4216 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); 4217 } 4218 4219 unsigned int dataSize = (unsigned int)strlen(fileText); 4220 4221 unsigned int flags = TINYOBJ_FLAG_TRIANGULATE; 4222 int ret = tinyobj_parse_obj(&objAttributes, &objShapes, &objShapeCount, &objMaterials, &objMaterialCount, fileText, dataSize, flags); 4223 4224 if (ret != TINYOBJ_SUCCESS) 4225 { 4226 TRACELOG(LOG_ERROR, "MODEL Unable to read obj data %s", fileName); 4227 return model; 4228 } 4229 4230 UnloadFileText(fileText); 4231 4232 unsigned int faceVertIndex = 0; 4233 unsigned int nextShape = 1; 4234 int lastMaterial = -1; 4235 unsigned int meshIndex = 0; 4236 4237 // count meshes 4238 unsigned int nextShapeEnd = objAttributes.num_face_num_verts; 4239 4240 // see how many verts till the next shape 4241 4242 if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset; 4243 4244 // walk all the faces 4245 for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++) 4246 { 4247 if (faceId >= nextShapeEnd) 4248 { 4249 // try to find the last vert in the next shape 4250 nextShape++; 4251 if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset; 4252 else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces 4253 meshIndex++; 4254 } 4255 else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) 4256 { 4257 meshIndex++;// if this is a new material, we need to allocate a new mesh 4258 } 4259 4260 lastMaterial = objAttributes.material_ids[faceId]; 4261 faceVertIndex += objAttributes.face_num_verts[faceId]; 4262 } 4263 4264 // allocate the base meshes and materials 4265 model.meshCount = meshIndex + 1; 4266 model.meshes = (Mesh*)MemAlloc(sizeof(Mesh) * model.meshCount); 4267 4268 if (objMaterialCount > 0) 4269 { 4270 model.materialCount = objMaterialCount; 4271 model.materials = (Material*)MemAlloc(sizeof(Material) * objMaterialCount); 4272 } 4273 else // we must allocate at least one material 4274 { 4275 model.materialCount = 1; 4276 model.materials = (Material*)MemAlloc(sizeof(Material) * 1); 4277 } 4278 4279 model.meshMaterial = (int*)MemAlloc(sizeof(int) * model.meshCount); 4280 4281 // see how many verts are in each mesh 4282 unsigned int* localMeshVertexCounts = (unsigned int*)MemAlloc(sizeof(unsigned int) * model.meshCount); 4283 4284 faceVertIndex = 0; 4285 nextShapeEnd = objAttributes.num_face_num_verts; 4286 lastMaterial = -1; 4287 meshIndex = 0; 4288 unsigned int localMeshVertexCount = 0; 4289 4290 nextShape = 1; 4291 if (objShapeCount > 1) 4292 nextShapeEnd = objShapes[nextShape].face_offset; 4293 4294 // walk all the faces 4295 for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++) 4296 { 4297 bool newMesh = false; // do we need a new mesh? 4298 if (faceId >= nextShapeEnd) 4299 { 4300 // try to find the last vert in the next shape 4301 nextShape++; 4302 if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset; 4303 else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces 4304 4305 newMesh = true; 4306 } 4307 else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) 4308 { 4309 newMesh = true; 4310 } 4311 4312 lastMaterial = objAttributes.material_ids[faceId]; 4313 4314 if (newMesh) 4315 { 4316 localMeshVertexCounts[meshIndex] = localMeshVertexCount; 4317 4318 localMeshVertexCount = 0; 4319 meshIndex++; 4320 } 4321 4322 faceVertIndex += objAttributes.face_num_verts[faceId]; 4323 localMeshVertexCount += objAttributes.face_num_verts[faceId]; 4324 } 4325 localMeshVertexCounts[meshIndex] = localMeshVertexCount; 4326 4327 for (int i = 0; i < model.meshCount; i++) 4328 { 4329 // allocate the buffers for each mesh 4330 unsigned int vertexCount = localMeshVertexCounts[i]; 4331 4332 model.meshes[i].vertexCount = vertexCount; 4333 model.meshes[i].triangleCount = vertexCount / 3; 4334 4335 model.meshes[i].vertices = (float*)MemAlloc(sizeof(float) * vertexCount * 3); 4336 model.meshes[i].normals = (float*)MemAlloc(sizeof(float) * vertexCount * 3); 4337 model.meshes[i].texcoords = (float*)MemAlloc(sizeof(float) * vertexCount * 2); 4338 model.meshes[i].colors = (unsigned char*)MemAlloc(sizeof(unsigned char) * vertexCount * 4); 4339 } 4340 4341 MemFree(localMeshVertexCounts); 4342 localMeshVertexCounts = NULL; 4343 4344 // fill meshes 4345 faceVertIndex = 0; 4346 4347 nextShapeEnd = objAttributes.num_face_num_verts; 4348 4349 // see how many verts till the next shape 4350 nextShape = 1; 4351 if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset; 4352 lastMaterial = -1; 4353 meshIndex = 0; 4354 localMeshVertexCount = 0; 4355 4356 // walk all the faces 4357 for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++) 4358 { 4359 bool newMesh = false; // do we need a new mesh? 4360 if (faceId >= nextShapeEnd) 4361 { 4362 // try to find the last vert in the next shape 4363 nextShape++; 4364 if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset; 4365 else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces 4366 newMesh = true; 4367 } 4368 // if this is a new material, we need to allocate a new mesh 4369 if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) newMesh = true; 4370 lastMaterial = objAttributes.material_ids[faceId]; 4371 4372 if (newMesh) 4373 { 4374 localMeshVertexCount = 0; 4375 meshIndex++; 4376 } 4377 4378 int matId = 0; 4379 if (lastMaterial >= 0 && lastMaterial < (int)objMaterialCount) 4380 matId = lastMaterial; 4381 4382 model.meshMaterial[meshIndex] = matId; 4383 4384 for (int f = 0; f < objAttributes.face_num_verts[faceId]; f++) 4385 { 4386 int vertIndex = objAttributes.faces[faceVertIndex].v_idx; 4387 int normalIndex = objAttributes.faces[faceVertIndex].vn_idx; 4388 int texcordIndex = objAttributes.faces[faceVertIndex].vt_idx; 4389 4390 for (int i = 0; i < 3; i++) 4391 model.meshes[meshIndex].vertices[localMeshVertexCount * 3 + i] = objAttributes.vertices[vertIndex * 3 + i]; 4392 4393 for (int i = 0; i < 3; i++) 4394 model.meshes[meshIndex].normals[localMeshVertexCount * 3 + i] = objAttributes.normals[normalIndex * 3 + i]; 4395 4396 for (int i = 0; i < 2; i++) 4397 model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + i] = objAttributes.texcoords[texcordIndex * 2 + i]; 4398 4399 model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1]; 4400 4401 for (int i = 0; i < 4; i++) 4402 model.meshes[meshIndex].colors[localMeshVertexCount * 4 + i] = 255; 4403 4404 faceVertIndex++; 4405 localMeshVertexCount++; 4406 } 4407 } 4408 4409 if (objMaterialCount > 0) ProcessMaterialsOBJ(model.materials, objMaterials, objMaterialCount); 4410 else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh 4411 4412 tinyobj_attrib_free(&objAttributes); 4413 tinyobj_shapes_free(objShapes, objShapeCount); 4414 tinyobj_materials_free(objMaterials, objMaterialCount); 4415 4416 for (int i = 0; i < model.meshCount; i++) 4417 UploadMesh(model.meshes + i, true); 4418 4419 // Restore current working directory 4420 if (CHDIR(currentDir) != 0) 4421 { 4422 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir); 4423 } 4424 4425 return model; 4426 } 4427 #endif 4428 4429 #if defined(SUPPORT_FILEFORMAT_IQM) 4430 // Load IQM mesh data 4431 static Model LoadIQM(const char *fileName) 4432 { 4433 #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number 4434 #define IQM_VERSION 2 // only IQM version 2 supported 4435 4436 #define BONE_NAME_LENGTH 32 // BoneInfo name string length 4437 #define MESH_NAME_LENGTH 32 // Mesh name string length 4438 #define MATERIAL_NAME_LENGTH 32 // Material name string length 4439 4440 int dataSize = 0; 4441 unsigned char *fileData = LoadFileData(fileName, &dataSize); 4442 unsigned char *fileDataPtr = fileData; 4443 4444 // IQM file structs 4445 //----------------------------------------------------------------------------------- 4446 typedef struct IQMHeader { 4447 char magic[16]; 4448 unsigned int version; 4449 unsigned int dataSize; 4450 unsigned int flags; 4451 unsigned int num_text, ofs_text; 4452 unsigned int num_meshes, ofs_meshes; 4453 unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; 4454 unsigned int num_triangles, ofs_triangles, ofs_adjacency; 4455 unsigned int num_joints, ofs_joints; 4456 unsigned int num_poses, ofs_poses; 4457 unsigned int num_anims, ofs_anims; 4458 unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; 4459 unsigned int num_comment, ofs_comment; 4460 unsigned int num_extensions, ofs_extensions; 4461 } IQMHeader; 4462 4463 typedef struct IQMMesh { 4464 unsigned int name; 4465 unsigned int material; 4466 unsigned int first_vertex, num_vertexes; 4467 unsigned int first_triangle, num_triangles; 4468 } IQMMesh; 4469 4470 typedef struct IQMTriangle { 4471 unsigned int vertex[3]; 4472 } IQMTriangle; 4473 4474 typedef struct IQMJoint { 4475 unsigned int name; 4476 int parent; 4477 float translate[3], rotate[4], scale[3]; 4478 } IQMJoint; 4479 4480 typedef struct IQMVertexArray { 4481 unsigned int type; 4482 unsigned int flags; 4483 unsigned int format; 4484 unsigned int size; 4485 unsigned int offset; 4486 } IQMVertexArray; 4487 4488 // NOTE: Below IQM structures are not used but listed for reference 4489 /* 4490 typedef struct IQMAdjacency { 4491 unsigned int triangle[3]; 4492 } IQMAdjacency; 4493 4494 typedef struct IQMPose { 4495 int parent; 4496 unsigned int mask; 4497 float channeloffset[10]; 4498 float channelscale[10]; 4499 } IQMPose; 4500 4501 typedef struct IQMAnim { 4502 unsigned int name; 4503 unsigned int first_frame, num_frames; 4504 float framerate; 4505 unsigned int flags; 4506 } IQMAnim; 4507 4508 typedef struct IQMBounds { 4509 float bbmin[3], bbmax[3]; 4510 float xyradius, radius; 4511 } IQMBounds; 4512 */ 4513 //----------------------------------------------------------------------------------- 4514 4515 // IQM vertex data types 4516 enum { 4517 IQM_POSITION = 0, 4518 IQM_TEXCOORD = 1, 4519 IQM_NORMAL = 2, 4520 IQM_TANGENT = 3, // NOTE: Tangents unused by default 4521 IQM_BLENDINDEXES = 4, 4522 IQM_BLENDWEIGHTS = 5, 4523 IQM_COLOR = 6, 4524 IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default 4525 }; 4526 4527 Model model = { 0 }; 4528 4529 IQMMesh *imesh = NULL; 4530 IQMTriangle *tri = NULL; 4531 IQMVertexArray *va = NULL; 4532 IQMJoint *ijoint = NULL; 4533 4534 float *vertex = NULL; 4535 float *normal = NULL; 4536 float *text = NULL; 4537 char *blendi = NULL; 4538 unsigned char *blendw = NULL; 4539 unsigned char *color = NULL; 4540 4541 // In case file can not be read, return an empty model 4542 if (fileDataPtr == NULL) return model; 4543 4544 const char *basePath = GetDirectoryPath(fileName); 4545 4546 // Read IQM header 4547 IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr; 4548 4549 if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0) 4550 { 4551 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName); 4552 return model; 4553 } 4554 4555 if (iqmHeader->version != IQM_VERSION) 4556 { 4557 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version); 4558 return model; 4559 } 4560 4561 //fileDataPtr += sizeof(IQMHeader); // Move file data pointer 4562 4563 // Meshes data processing 4564 imesh = RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh)); 4565 //fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET); 4566 //fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile); 4567 memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh)); 4568 4569 model.meshCount = iqmHeader->num_meshes; 4570 model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); 4571 4572 model.materialCount = model.meshCount; 4573 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); 4574 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); 4575 4576 char name[MESH_NAME_LENGTH] = { 0 }; 4577 char material[MATERIAL_NAME_LENGTH] = { 0 }; 4578 4579 for (int i = 0; i < model.meshCount; i++) 4580 { 4581 //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].name, SEEK_SET); 4582 //fread(name, sizeof(char), MESH_NAME_LENGTH, iqmFile); 4583 memcpy(name, fileDataPtr + iqmHeader->ofs_text + imesh[i].name, MESH_NAME_LENGTH*sizeof(char)); 4584 4585 //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].material, SEEK_SET); 4586 //fread(material, sizeof(char), MATERIAL_NAME_LENGTH, iqmFile); 4587 memcpy(material, fileDataPtr + iqmHeader->ofs_text + imesh[i].material, MATERIAL_NAME_LENGTH*sizeof(char)); 4588 4589 model.materials[i] = LoadMaterialDefault(); 4590 model.materials[i].maps[MATERIAL_MAP_ALBEDO].texture = LoadTexture(TextFormat("%s/%s", basePath, material)); 4591 4592 model.meshMaterial[i] = i; 4593 4594 TRACELOG(LOG_DEBUG, "MODEL: [%s] mesh name (%s), material (%s)", fileName, name, material); 4595 4596 model.meshes[i].vertexCount = imesh[i].num_vertexes; 4597 4598 model.meshes[i].vertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions 4599 model.meshes[i].normals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals 4600 model.meshes[i].texcoords = RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords 4601 4602 model.meshes[i].boneIds = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported! 4603 model.meshes[i].boneWeights = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported! 4604 4605 model.meshes[i].triangleCount = imesh[i].num_triangles; 4606 model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); 4607 4608 // Animated vertex data, what we actually process for rendering 4609 // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) 4610 model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); 4611 model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); 4612 } 4613 4614 // Triangles data processing 4615 tri = RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle)); 4616 //fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET); 4617 //fread(tri, sizeof(IQMTriangle), iqmHeader->num_triangles, iqmFile); 4618 memcpy(tri, fileDataPtr + iqmHeader->ofs_triangles, iqmHeader->num_triangles*sizeof(IQMTriangle)); 4619 4620 for (int m = 0; m < model.meshCount; m++) 4621 { 4622 int tcounter = 0; 4623 4624 for (unsigned int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++) 4625 { 4626 // IQM triangles indexes are stored in counter-clockwise, but raylib processes the index in linear order, 4627 // expecting they point to the counter-clockwise vertex triangle, so we need to reverse triangle indexes 4628 // NOTE: raylib renders vertex data in counter-clockwise order (standard convention) by default 4629 model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex; 4630 model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex; 4631 model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; 4632 tcounter += 3; 4633 } 4634 } 4635 4636 // Vertex arrays data processing 4637 va = RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); 4638 //fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET); 4639 //fread(va, sizeof(IQMVertexArray), iqmHeader->num_vertexarrays, iqmFile); 4640 memcpy(va, fileDataPtr + iqmHeader->ofs_vertexarrays, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); 4641 4642 for (unsigned int i = 0; i < iqmHeader->num_vertexarrays; i++) 4643 { 4644 switch (va[i].type) 4645 { 4646 case IQM_POSITION: 4647 { 4648 vertex = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); 4649 //fseek(iqmFile, va[i].offset, SEEK_SET); 4650 //fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile); 4651 memcpy(vertex, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float)); 4652 4653 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) 4654 { 4655 int vCounter = 0; 4656 for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) 4657 { 4658 model.meshes[m].vertices[vCounter] = vertex[i]; 4659 model.meshes[m].animVertices[vCounter] = vertex[i]; 4660 vCounter++; 4661 } 4662 } 4663 } break; 4664 case IQM_NORMAL: 4665 { 4666 normal = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); 4667 //fseek(iqmFile, va[i].offset, SEEK_SET); 4668 //fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile); 4669 memcpy(normal, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float)); 4670 4671 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) 4672 { 4673 int vCounter = 0; 4674 for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) 4675 { 4676 model.meshes[m].normals[vCounter] = normal[i]; 4677 model.meshes[m].animNormals[vCounter] = normal[i]; 4678 vCounter++; 4679 } 4680 } 4681 } break; 4682 case IQM_TEXCOORD: 4683 { 4684 text = RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float)); 4685 //fseek(iqmFile, va[i].offset, SEEK_SET); 4686 //fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile); 4687 memcpy(text, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*2*sizeof(float)); 4688 4689 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) 4690 { 4691 int vCounter = 0; 4692 for (unsigned int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++) 4693 { 4694 model.meshes[m].texcoords[vCounter] = text[i]; 4695 vCounter++; 4696 } 4697 } 4698 } break; 4699 case IQM_BLENDINDEXES: 4700 { 4701 blendi = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char)); 4702 //fseek(iqmFile, va[i].offset, SEEK_SET); 4703 //fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile); 4704 memcpy(blendi, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(char)); 4705 4706 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) 4707 { 4708 int boneCounter = 0; 4709 for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) 4710 { 4711 model.meshes[m].boneIds[boneCounter] = blendi[i]; 4712 boneCounter++; 4713 } 4714 } 4715 } break; 4716 case IQM_BLENDWEIGHTS: 4717 { 4718 blendw = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); 4719 //fseek(iqmFile, va[i].offset, SEEK_SET); 4720 //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile); 4721 memcpy(blendw, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char)); 4722 4723 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) 4724 { 4725 int boneCounter = 0; 4726 for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) 4727 { 4728 model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f; 4729 boneCounter++; 4730 } 4731 } 4732 } break; 4733 case IQM_COLOR: 4734 { 4735 color = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); 4736 //fseek(iqmFile, va[i].offset, SEEK_SET); 4737 //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile); 4738 memcpy(color, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char)); 4739 4740 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) 4741 { 4742 model.meshes[m].colors = RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char)); 4743 4744 int vCounter = 0; 4745 for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) 4746 { 4747 model.meshes[m].colors[vCounter] = color[i]; 4748 vCounter++; 4749 } 4750 } 4751 } break; 4752 } 4753 } 4754 4755 // Bones (joints) data processing 4756 ijoint = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); 4757 //fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET); 4758 //fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile); 4759 memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint)); 4760 4761 model.boneCount = iqmHeader->num_joints; 4762 model.bones = RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo)); 4763 model.bindPose = RL_MALLOC(iqmHeader->num_joints*sizeof(Transform)); 4764 4765 for (unsigned int i = 0; i < iqmHeader->num_joints; i++) 4766 { 4767 // Bones 4768 model.bones[i].parent = ijoint[i].parent; 4769 //fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET); 4770 //fread(model.bones[i].name, sizeof(char), BONE_NAME_LENGTH, iqmFile); 4771 memcpy(model.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char)); 4772 4773 // Bind pose (base pose) 4774 model.bindPose[i].translation.x = ijoint[i].translate[0]; 4775 model.bindPose[i].translation.y = ijoint[i].translate[1]; 4776 model.bindPose[i].translation.z = ijoint[i].translate[2]; 4777 4778 model.bindPose[i].rotation.x = ijoint[i].rotate[0]; 4779 model.bindPose[i].rotation.y = ijoint[i].rotate[1]; 4780 model.bindPose[i].rotation.z = ijoint[i].rotate[2]; 4781 model.bindPose[i].rotation.w = ijoint[i].rotate[3]; 4782 4783 model.bindPose[i].scale.x = ijoint[i].scale[0]; 4784 model.bindPose[i].scale.y = ijoint[i].scale[1]; 4785 model.bindPose[i].scale.z = ijoint[i].scale[2]; 4786 } 4787 4788 BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose); 4789 4790 for (int i = 0; i < model.meshCount; i++) 4791 { 4792 model.meshes[i].boneCount = model.boneCount; 4793 model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); 4794 4795 for (int j = 0; j < model.meshes[i].boneCount; j++) 4796 { 4797 model.meshes[i].boneMatrices[j] = MatrixIdentity(); 4798 } 4799 } 4800 4801 UnloadFileData(fileData); 4802 4803 RL_FREE(imesh); 4804 RL_FREE(tri); 4805 RL_FREE(va); 4806 RL_FREE(vertex); 4807 RL_FREE(normal); 4808 RL_FREE(text); 4809 RL_FREE(blendi); 4810 RL_FREE(blendw); 4811 RL_FREE(ijoint); 4812 RL_FREE(color); 4813 4814 return model; 4815 } 4816 4817 // Load IQM animation data 4818 static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount) 4819 { 4820 #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number 4821 #define IQM_VERSION 2 // only IQM version 2 supported 4822 4823 int dataSize = 0; 4824 unsigned char *fileData = LoadFileData(fileName, &dataSize); 4825 unsigned char *fileDataPtr = fileData; 4826 4827 typedef struct IQMHeader { 4828 char magic[16]; 4829 unsigned int version; 4830 unsigned int dataSize; 4831 unsigned int flags; 4832 unsigned int num_text, ofs_text; 4833 unsigned int num_meshes, ofs_meshes; 4834 unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; 4835 unsigned int num_triangles, ofs_triangles, ofs_adjacency; 4836 unsigned int num_joints, ofs_joints; 4837 unsigned int num_poses, ofs_poses; 4838 unsigned int num_anims, ofs_anims; 4839 unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; 4840 unsigned int num_comment, ofs_comment; 4841 unsigned int num_extensions, ofs_extensions; 4842 } IQMHeader; 4843 4844 typedef struct IQMJoint { 4845 unsigned int name; 4846 int parent; 4847 float translate[3], rotate[4], scale[3]; 4848 } IQMJoint; 4849 4850 typedef struct IQMPose { 4851 int parent; 4852 unsigned int mask; 4853 float channeloffset[10]; 4854 float channelscale[10]; 4855 } IQMPose; 4856 4857 typedef struct IQMAnim { 4858 unsigned int name; 4859 unsigned int first_frame, num_frames; 4860 float framerate; 4861 unsigned int flags; 4862 } IQMAnim; 4863 4864 // In case file can not be read, return an empty model 4865 if (fileDataPtr == NULL) return NULL; 4866 4867 // Read IQM header 4868 IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr; 4869 4870 if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0) 4871 { 4872 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName); 4873 return NULL; 4874 } 4875 4876 if (iqmHeader->version != IQM_VERSION) 4877 { 4878 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version); 4879 return NULL; 4880 } 4881 4882 // Get bones data 4883 IQMPose *poses = RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose)); 4884 //fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET); 4885 //fread(poses, sizeof(IQMPose), iqmHeader->num_poses, iqmFile); 4886 memcpy(poses, fileDataPtr + iqmHeader->ofs_poses, iqmHeader->num_poses*sizeof(IQMPose)); 4887 4888 // Get animations data 4889 *animCount = iqmHeader->num_anims; 4890 IQMAnim *anim = RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim)); 4891 //fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET); 4892 //fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile); 4893 memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim)); 4894 4895 ModelAnimation *animations = RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation)); 4896 4897 // frameposes 4898 unsigned short *framedata = RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); 4899 //fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET); 4900 //fread(framedata, sizeof(unsigned short), iqmHeader->num_frames*iqmHeader->num_framechannels, iqmFile); 4901 memcpy(framedata, fileDataPtr + iqmHeader->ofs_frames, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); 4902 4903 // joints 4904 IQMJoint *joints = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); 4905 memcpy(joints, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint)); 4906 4907 for (unsigned int a = 0; a < iqmHeader->num_anims; a++) 4908 { 4909 animations[a].frameCount = anim[a].num_frames; 4910 animations[a].boneCount = iqmHeader->num_poses; 4911 animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); 4912 animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); 4913 memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); // I don't like this 32 here 4914 TraceLog(LOG_INFO, "IQM Anim %s", animations[a].name); 4915 // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data? 4916 4917 for (unsigned int j = 0; j < iqmHeader->num_poses; j++) 4918 { 4919 // If animations and skeleton are in the same file, copy bone names to anim 4920 if (iqmHeader->num_joints > 0) 4921 memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char)); 4922 else 4923 strcpy(animations[a].bones[j].name, "ANIMJOINTNAME"); // default bone name otherwise 4924 animations[a].bones[j].parent = poses[j].parent; 4925 } 4926 4927 for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); 4928 4929 int dcounter = anim[a].first_frame*iqmHeader->num_framechannels; 4930 4931 for (unsigned int frame = 0; frame < anim[a].num_frames; frame++) 4932 { 4933 for (unsigned int i = 0; i < iqmHeader->num_poses; i++) 4934 { 4935 animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0]; 4936 4937 if (poses[i].mask & 0x01) 4938 { 4939 animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0]; 4940 dcounter++; 4941 } 4942 4943 animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1]; 4944 4945 if (poses[i].mask & 0x02) 4946 { 4947 animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1]; 4948 dcounter++; 4949 } 4950 4951 animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2]; 4952 4953 if (poses[i].mask & 0x04) 4954 { 4955 animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2]; 4956 dcounter++; 4957 } 4958 4959 animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3]; 4960 4961 if (poses[i].mask & 0x08) 4962 { 4963 animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3]; 4964 dcounter++; 4965 } 4966 4967 animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4]; 4968 4969 if (poses[i].mask & 0x10) 4970 { 4971 animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4]; 4972 dcounter++; 4973 } 4974 4975 animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5]; 4976 4977 if (poses[i].mask & 0x20) 4978 { 4979 animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5]; 4980 dcounter++; 4981 } 4982 4983 animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6]; 4984 4985 if (poses[i].mask & 0x40) 4986 { 4987 animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6]; 4988 dcounter++; 4989 } 4990 4991 animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7]; 4992 4993 if (poses[i].mask & 0x80) 4994 { 4995 animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7]; 4996 dcounter++; 4997 } 4998 4999 animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8]; 5000 5001 if (poses[i].mask & 0x100) 5002 { 5003 animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8]; 5004 dcounter++; 5005 } 5006 5007 animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9]; 5008 5009 if (poses[i].mask & 0x200) 5010 { 5011 animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9]; 5012 dcounter++; 5013 } 5014 5015 animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation); 5016 } 5017 } 5018 5019 // Build frameposes 5020 for (unsigned int frame = 0; frame < anim[a].num_frames; frame++) 5021 { 5022 for (int i = 0; i < animations[a].boneCount; i++) 5023 { 5024 if (animations[a].bones[i].parent >= 0) 5025 { 5026 animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation); 5027 animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation); 5028 animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation); 5029 animations[a].framePoses[frame][i].scale = Vector3Multiply(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale); 5030 } 5031 } 5032 } 5033 } 5034 5035 UnloadFileData(fileData); 5036 5037 RL_FREE(joints); 5038 RL_FREE(framedata); 5039 RL_FREE(poses); 5040 RL_FREE(anim); 5041 5042 return animations; 5043 } 5044 5045 #endif 5046 5047 #if defined(SUPPORT_FILEFORMAT_GLTF) 5048 // Load file data callback for cgltf 5049 static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, const char *path, cgltf_size *size, void **data) 5050 { 5051 int filesize; 5052 unsigned char *filedata = LoadFileData(path, &filesize); 5053 5054 if (filedata == NULL) return cgltf_result_io_error; 5055 5056 *size = filesize; 5057 *data = filedata; 5058 5059 return cgltf_result_success; 5060 } 5061 5062 // Release file data callback for cgltf 5063 static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data) 5064 { 5065 UnloadFileData(data); 5066 } 5067 5068 // Load image from different glTF provided methods (uri, path, buffer_view) 5069 static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPath) 5070 { 5071 Image image = { 0 }; 5072 5073 if (cgltfImage->uri != NULL) // Check if image data is provided as an uri (base64 or path) 5074 { 5075 if ((strlen(cgltfImage->uri) > 5) && 5076 (cgltfImage->uri[0] == 'd') && 5077 (cgltfImage->uri[1] == 'a') && 5078 (cgltfImage->uri[2] == 't') && 5079 (cgltfImage->uri[3] == 'a') && 5080 (cgltfImage->uri[4] == ':')) // Check if image is provided as base64 text data 5081 { 5082 // Data URI Format: data:<mediatype>;base64,<data> 5083 5084 // Find the comma 5085 int i = 0; 5086 while ((cgltfImage->uri[i] != ',') && (cgltfImage->uri[i] != 0)) i++; 5087 5088 if (cgltfImage->uri[i] == 0) TRACELOG(LOG_WARNING, "IMAGE: glTF data URI is not a valid image"); 5089 else 5090 { 5091 int base64Size = (int)strlen(cgltfImage->uri + i + 1); 5092 while (cgltfImage->uri[i + base64Size] == '=') base64Size--; // Ignore optional paddings 5093 int numberOfEncodedBits = base64Size*6 - (base64Size*6) % 8 ; // Encoded bits minus extra bits, so it becomes a multiple of 8 bits 5094 int outSize = numberOfEncodedBits/8 ; // Actual encoded bytes 5095 void *data = NULL; 5096 5097 cgltf_options options = { 0 }; 5098 options.file.read = LoadFileGLTFCallback; 5099 options.file.release = ReleaseFileGLTFCallback; 5100 cgltf_result result = cgltf_load_buffer_base64(&options, outSize, cgltfImage->uri + i + 1, &data); 5101 5102 if (result == cgltf_result_success) 5103 { 5104 image = LoadImageFromMemory(".png", (unsigned char *)data, outSize); 5105 RL_FREE(data); 5106 } 5107 } 5108 } 5109 else // Check if image is provided as image path 5110 { 5111 image = LoadImage(TextFormat("%s/%s", texPath, cgltfImage->uri)); 5112 } 5113 } 5114 else if (cgltfImage->buffer_view->buffer->data != NULL) // Check if image is provided as data buffer 5115 { 5116 unsigned char *data = RL_MALLOC(cgltfImage->buffer_view->size); 5117 int offset = (int)cgltfImage->buffer_view->offset; 5118 int stride = (int)cgltfImage->buffer_view->stride? (int)cgltfImage->buffer_view->stride : 1; 5119 5120 // Copy buffer data to memory for loading 5121 for (unsigned int i = 0; i < cgltfImage->buffer_view->size; i++) 5122 { 5123 data[i] = ((unsigned char *)cgltfImage->buffer_view->buffer->data)[offset]; 5124 offset += stride; 5125 } 5126 5127 // Check mime_type for image: (cgltfImage->mime_type == "image/png") 5128 // NOTE: Detected that some models define mime_type as "image\\/png" 5129 if ((strcmp(cgltfImage->mime_type, "image\\/png") == 0) || 5130 (strcmp(cgltfImage->mime_type, "image/png") == 0)) image = LoadImageFromMemory(".png", data, (int)cgltfImage->buffer_view->size); 5131 else if ((strcmp(cgltfImage->mime_type, "image\\/jpeg") == 0) || 5132 (strcmp(cgltfImage->mime_type, "image/jpeg") == 0)) image = LoadImageFromMemory(".jpg", data, (int)cgltfImage->buffer_view->size); 5133 else TRACELOG(LOG_WARNING, "MODEL: glTF image data MIME type not recognized", TextFormat("%s/%s", texPath, cgltfImage->uri)); 5134 5135 RL_FREE(data); 5136 } 5137 5138 return image; 5139 } 5140 5141 // Load bone info from GLTF skin data 5142 static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount) 5143 { 5144 *boneCount = (int)skin.joints_count; 5145 BoneInfo *bones = RL_MALLOC(skin.joints_count*sizeof(BoneInfo)); 5146 5147 for (unsigned int i = 0; i < skin.joints_count; i++) 5148 { 5149 cgltf_node node = *skin.joints[i]; 5150 if (node.name != NULL) 5151 { 5152 strncpy(bones[i].name, node.name, sizeof(bones[i].name)); 5153 bones[i].name[sizeof(bones[i].name) - 1] = '\0'; 5154 } 5155 5156 // Find parent bone index 5157 int parentIndex = -1; 5158 5159 for (unsigned int j = 0; j < skin.joints_count; j++) 5160 { 5161 if (skin.joints[j] == node.parent) 5162 { 5163 parentIndex = (int)j; 5164 break; 5165 } 5166 } 5167 5168 bones[i].parent = parentIndex; 5169 } 5170 5171 return bones; 5172 } 5173 5174 // Load glTF file into model struct, .gltf and .glb supported 5175 static Model LoadGLTF(const char *fileName) 5176 { 5177 /********************************************************************************************* 5178 5179 Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend) 5180 Transform handling implemented by Paul Melis (@paulmelis). 5181 Reviewed by Ramon Santamaria (@raysan5) 5182 5183 FEATURES: 5184 - Supports .gltf and .glb files 5185 - Supports embedded (base64) or external textures 5186 - Supports PBR metallic/roughness flow, loads material textures, values and colors 5187 PBR specular/glossiness flow and extended texture flows not supported 5188 - Supports multiple meshes per model (every primitives is loaded as a separate mesh) 5189 - Supports basic animations 5190 - Transforms, including parent-child relations, are applied on the mesh data, but the 5191 hierarchy is not kept (as it can't be represented). 5192 - Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes) 5193 are turned into separate raylib Meshes. 5194 5195 RESTRICTIONS: 5196 - Only triangle meshes supported 5197 - Vertex attribute types and formats supported: 5198 > Vertices (position): vec3: float 5199 > Normals: vec3: float 5200 > Texcoords: vec2: float 5201 > Colors: vec4: u8, u16, f32 (normalized) 5202 > Indices: u16, u32 (truncated to u16) 5203 - Scenes defined in the glTF file are ignored. All nodes in the file 5204 are used. 5205 5206 ***********************************************************************************************/ 5207 5208 // Macro to simplify attributes loading code 5209 #define LOAD_ATTRIBUTE(accesor, numComp, srcType, dstPtr) LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, srcType) 5210 5211 #define LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, dstType) \ 5212 { \ 5213 int n = 0; \ 5214 srcType *buffer = (srcType *)accesor->buffer_view->buffer->data + accesor->buffer_view->offset/sizeof(srcType) + accesor->offset/sizeof(srcType); \ 5215 for (unsigned int k = 0; k < accesor->count; k++) \ 5216 {\ 5217 for (int l = 0; l < numComp; l++) \ 5218 {\ 5219 dstPtr[numComp*k + l] = (dstType)buffer[n + l];\ 5220 }\ 5221 n += (int)(accesor->stride/sizeof(srcType));\ 5222 }\ 5223 } 5224 5225 Model model = { 0 }; 5226 5227 // glTF file loading 5228 int dataSize = 0; 5229 unsigned char *fileData = LoadFileData(fileName, &dataSize); 5230 5231 if (fileData == NULL) return model; 5232 5233 // glTF data loading 5234 cgltf_options options = { 0 }; 5235 options.file.read = LoadFileGLTFCallback; 5236 options.file.release = ReleaseFileGLTFCallback; 5237 cgltf_data *data = NULL; 5238 cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data); 5239 5240 if (result == cgltf_result_success) 5241 { 5242 if (data->file_type == cgltf_file_type_glb) TRACELOG(LOG_INFO, "MODEL: [%s] Model basic data (glb) loaded successfully", fileName); 5243 else if (data->file_type == cgltf_file_type_gltf) TRACELOG(LOG_INFO, "MODEL: [%s] Model basic data (glTF) loaded successfully", fileName); 5244 else TRACELOG(LOG_WARNING, "MODEL: [%s] Model format not recognized", fileName); 5245 5246 TRACELOG(LOG_INFO, " > Meshes count: %i", data->meshes_count); 5247 TRACELOG(LOG_INFO, " > Materials count: %i (+1 default)", data->materials_count); 5248 TRACELOG(LOG_DEBUG, " > Buffers count: %i", data->buffers_count); 5249 TRACELOG(LOG_DEBUG, " > Images count: %i", data->images_count); 5250 TRACELOG(LOG_DEBUG, " > Textures count: %i", data->textures_count); 5251 5252 // Force reading data buffers (fills buffer_view->buffer->data) 5253 // NOTE: If an uri is defined to base64 data or external path, it's automatically loaded 5254 result = cgltf_load_buffers(&options, data, fileName); 5255 if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName); 5256 5257 int primitivesCount = 0; 5258 // NOTE: We will load every primitive in the glTF as a separate raylib Mesh. 5259 // Determine total number of meshes needed from the node hierarchy. 5260 for (unsigned int i = 0; i < data->nodes_count; i++) 5261 { 5262 cgltf_node *node = &(data->nodes[i]); 5263 cgltf_mesh *mesh = node->mesh; 5264 if (!mesh) 5265 continue; 5266 5267 for (unsigned int p = 0; p < mesh->primitives_count; p++) 5268 { 5269 if (mesh->primitives[p].type == cgltf_primitive_type_triangles) 5270 primitivesCount++; 5271 } 5272 } 5273 TRACELOG(LOG_DEBUG, " > Primitives (triangles only) count based on hierarchy : %i", primitivesCount); 5274 5275 // Load our model data: meshes and materials 5276 model.meshCount = primitivesCount; 5277 model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); 5278 5279 // NOTE: We keep an extra slot for default material, in case some mesh requires it 5280 model.materialCount = (int)data->materials_count + 1; 5281 model.materials = RL_CALLOC(model.materialCount, sizeof(Material)); 5282 model.materials[0] = LoadMaterialDefault(); // Load default material (index: 0) 5283 5284 // Load mesh-material indices, by default all meshes are mapped to material index: 0 5285 model.meshMaterial = RL_CALLOC(model.meshCount, sizeof(int)); 5286 5287 // Load materials data 5288 //---------------------------------------------------------------------------------------------------- 5289 for (unsigned int i = 0, j = 1; i < data->materials_count; i++, j++) 5290 { 5291 model.materials[j] = LoadMaterialDefault(); 5292 const char *texPath = GetDirectoryPath(fileName); 5293 5294 // Check glTF material flow: PBR metallic/roughness flow 5295 // NOTE: Alternatively, materials can follow PBR specular/glossiness flow 5296 if (data->materials[i].has_pbr_metallic_roughness) 5297 { 5298 // Load base color texture (albedo) 5299 if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture) 5300 { 5301 Image imAlbedo = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath); 5302 if (imAlbedo.data != NULL) 5303 { 5304 model.materials[j].maps[MATERIAL_MAP_ALBEDO].texture = LoadTextureFromImage(imAlbedo); 5305 UnloadImage(imAlbedo); 5306 } 5307 } 5308 // Load base color factor (tint) 5309 model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255); 5310 model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255); 5311 model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255); 5312 model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255); 5313 5314 // Load metallic/roughness texture 5315 if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture) 5316 { 5317 Image imMetallicRoughness = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath); 5318 if (imMetallicRoughness.data != NULL) 5319 { 5320 model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(imMetallicRoughness); 5321 UnloadImage(imMetallicRoughness); 5322 } 5323 5324 // Load metallic/roughness material properties 5325 float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor; 5326 model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].value = roughness; 5327 5328 float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor; 5329 model.materials[j].maps[MATERIAL_MAP_METALNESS].value = metallic; 5330 } 5331 5332 // Load normal texture 5333 if (data->materials[i].normal_texture.texture) 5334 { 5335 Image imNormal = LoadImageFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath); 5336 if (imNormal.data != NULL) 5337 { 5338 model.materials[j].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(imNormal); 5339 UnloadImage(imNormal); 5340 } 5341 } 5342 5343 // Load ambient occlusion texture 5344 if (data->materials[i].occlusion_texture.texture) 5345 { 5346 Image imOcclusion = LoadImageFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath); 5347 if (imOcclusion.data != NULL) 5348 { 5349 model.materials[j].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(imOcclusion); 5350 UnloadImage(imOcclusion); 5351 } 5352 } 5353 5354 // Load emissive texture 5355 if (data->materials[i].emissive_texture.texture) 5356 { 5357 Image imEmissive = LoadImageFromCgltfImage(data->materials[i].emissive_texture.texture->image, texPath); 5358 if (imEmissive.data != NULL) 5359 { 5360 model.materials[j].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(imEmissive); 5361 UnloadImage(imEmissive); 5362 } 5363 5364 // Load emissive color factor 5365 model.materials[j].maps[MATERIAL_MAP_EMISSION].color.r = (unsigned char)(data->materials[i].emissive_factor[0]*255); 5366 model.materials[j].maps[MATERIAL_MAP_EMISSION].color.g = (unsigned char)(data->materials[i].emissive_factor[1]*255); 5367 model.materials[j].maps[MATERIAL_MAP_EMISSION].color.b = (unsigned char)(data->materials[i].emissive_factor[2]*255); 5368 model.materials[j].maps[MATERIAL_MAP_EMISSION].color.a = 255; 5369 } 5370 } 5371 5372 // Other possible materials not supported by raylib pipeline: 5373 // has_clearcoat, has_transmission, has_volume, has_ior, has specular, has_sheen 5374 } 5375 5376 // Visit each node in the hierarchy and process any mesh linked from it. 5377 // Each primitive within a glTF node becomes a Raylib Mesh. 5378 // The local-to-world transform of each node is used to transform the 5379 // points/normals/tangents of the created Mesh(es). 5380 // Any glTF mesh linked from more than one Node (i.e. instancing) 5381 // is turned into multiple Mesh's, as each Node will have its own 5382 // transform applied. 5383 // Note: the code below disregards the scenes defined in the file, all nodes are used. 5384 //---------------------------------------------------------------------------------------------------- 5385 int meshIndex = 0; 5386 for (unsigned int i = 0; i < data->nodes_count; i++) 5387 { 5388 cgltf_node *node = &(data->nodes[i]); 5389 5390 cgltf_mesh *mesh = node->mesh; 5391 if (!mesh) 5392 continue; 5393 5394 cgltf_float worldTransform[16]; 5395 cgltf_node_transform_world(node, worldTransform); 5396 5397 Matrix worldMatrix = { 5398 worldTransform[0], worldTransform[4], worldTransform[8], worldTransform[12], 5399 worldTransform[1], worldTransform[5], worldTransform[9], worldTransform[13], 5400 worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14], 5401 worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15] 5402 }; 5403 5404 Matrix worldMatrixNormals = MatrixTranspose(MatrixInvert(worldMatrix)); 5405 5406 for (unsigned int p = 0; p < mesh->primitives_count; p++) 5407 { 5408 // NOTE: We only support primitives defined by triangles 5409 // Other alternatives: points, lines, line_strip, triangle_strip 5410 if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue; 5411 5412 // NOTE: Attributes data could be provided in several data formats (8, 8u, 16u, 32...), 5413 // Only some formats for each attribute type are supported, read info at the top of this function! 5414 5415 for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++) 5416 { 5417 // Check the different attributes for every primitive 5418 if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_position) // POSITION, vec3, float 5419 { 5420 cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; 5421 5422 // WARNING: SPECS: POSITION accessor MUST have its min and max properties defined 5423 5424 if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f)) 5425 { 5426 // Init raylib mesh vertices to copy glTF attribute data 5427 model.meshes[meshIndex].vertexCount = (int)attribute->count; 5428 model.meshes[meshIndex].vertices = RL_MALLOC(attribute->count*3*sizeof(float)); 5429 5430 // Load 3 components of float data type into mesh.vertices 5431 LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].vertices) 5432 5433 // Transform the vertices 5434 float *vertices = model.meshes[meshIndex].vertices; 5435 for (unsigned int k = 0; k < attribute->count; k++) 5436 { 5437 Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k+1], vertices[3*k+2] }, worldMatrix); 5438 vertices[3*k] = vt.x; 5439 vertices[3*k+1] = vt.y; 5440 vertices[3*k+2] = vt.z; 5441 } 5442 } 5443 else TRACELOG(LOG_WARNING, "MODEL: [%s] Vertices attribute data format not supported, use vec3 float", fileName); 5444 } 5445 else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_normal) // NORMAL, vec3, float 5446 { 5447 cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; 5448 5449 if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f)) 5450 { 5451 // Init raylib mesh normals to copy glTF attribute data 5452 model.meshes[meshIndex].normals = RL_MALLOC(attribute->count*3*sizeof(float)); 5453 5454 // Load 3 components of float data type into mesh.normals 5455 LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].normals) 5456 5457 // Transform the normals 5458 float *normals = model.meshes[meshIndex].normals; 5459 for (unsigned int k = 0; k < attribute->count; k++) 5460 { 5461 Vector3 nt = Vector3Transform((Vector3){ normals[3*k], normals[3*k+1], normals[3*k+2] }, worldMatrixNormals); 5462 normals[3*k] = nt.x; 5463 normals[3*k+1] = nt.y; 5464 normals[3*k+2] = nt.z; 5465 } 5466 } 5467 else TRACELOG(LOG_WARNING, "MODEL: [%s] Normal attribute data format not supported, use vec3 float", fileName); 5468 } 5469 else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_tangent) // TANGENT, vec3, float 5470 { 5471 cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; 5472 5473 if ((attribute->type == cgltf_type_vec4) && (attribute->component_type == cgltf_component_type_r_32f)) 5474 { 5475 // Init raylib mesh tangent to copy glTF attribute data 5476 model.meshes[meshIndex].tangents = RL_MALLOC(attribute->count*4*sizeof(float)); 5477 5478 // Load 4 components of float data type into mesh.tangents 5479 LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].tangents) 5480 5481 // Transform the tangents 5482 float *tangents = model.meshes[meshIndex].tangents; 5483 for (unsigned int k = 0; k < attribute->count; k++) 5484 { 5485 Vector3 tt = Vector3Transform((Vector3){ tangents[3*k], tangents[3*k+1], tangents[3*k+2] }, worldMatrix); 5486 tangents[3*k] = tt.x; 5487 tangents[3*k+1] = tt.y; 5488 tangents[3*k+2] = tt.z; 5489 } 5490 } 5491 else TRACELOG(LOG_WARNING, "MODEL: [%s] Tangent attribute data format not supported, use vec4 float", fileName); 5492 } 5493 else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_texcoord) // TEXCOORD_n, vec2, float/u8n/u16n 5494 { 5495 // Support up to 2 texture coordinates attributes 5496 float *texcoordPtr = NULL; 5497 5498 cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; 5499 5500 if (attribute->type == cgltf_type_vec2) 5501 { 5502 if (attribute->component_type == cgltf_component_type_r_32f) // vec2, float 5503 { 5504 // Init raylib mesh texcoords to copy glTF attribute data 5505 texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float)); 5506 5507 // Load 3 components of float data type into mesh.texcoords 5508 LOAD_ATTRIBUTE(attribute, 2, float, texcoordPtr) 5509 } 5510 else if (attribute->component_type == cgltf_component_type_r_8u) // vec2, u8n 5511 { 5512 // Init raylib mesh texcoords to copy glTF attribute data 5513 texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float)); 5514 5515 // Load data into a temp buffer to be converted to raylib data type 5516 unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*2*sizeof(unsigned char)); 5517 LOAD_ATTRIBUTE(attribute, 2, unsigned char, temp); 5518 5519 // Convert data to raylib texcoord data type (float) 5520 for (unsigned int t = 0; t < attribute->count*2; t++) texcoordPtr[t] = (float)temp[t]/255.0f; 5521 5522 RL_FREE(temp); 5523 } 5524 else if (attribute->component_type == cgltf_component_type_r_16u) // vec2, u16n 5525 { 5526 // Init raylib mesh texcoords to copy glTF attribute data 5527 texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float)); 5528 5529 // Load data into a temp buffer to be converted to raylib data type 5530 unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*2*sizeof(unsigned short)); 5531 LOAD_ATTRIBUTE(attribute, 2, unsigned short, temp); 5532 5533 // Convert data to raylib texcoord data type (float) 5534 for (unsigned int t = 0; t < attribute->count*2; t++) texcoordPtr[t] = (float)temp[t]/65535.0f; 5535 5536 RL_FREE(temp); 5537 } 5538 else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported", fileName); 5539 } 5540 else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported, use vec2 float", fileName); 5541 5542 int index = mesh->primitives[p].attributes[j].index; 5543 if (index == 0) model.meshes[meshIndex].texcoords = texcoordPtr; 5544 else if (index == 1) model.meshes[meshIndex].texcoords2 = texcoordPtr; 5545 else 5546 { 5547 TRACELOG(LOG_WARNING, "MODEL: [%s] No more than 2 texture coordinates attributes supported", fileName); 5548 if (texcoordPtr != NULL) RL_FREE(texcoordPtr); 5549 } 5550 } 5551 else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_color) // COLOR_n, vec3/vec4, float/u8n/u16n 5552 { 5553 cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; 5554 5555 // WARNING: SPECS: All components of each COLOR_n accessor element MUST be clamped to [0.0, 1.0] range 5556 5557 if (attribute->type == cgltf_type_vec3) // RGB 5558 { 5559 if (attribute->component_type == cgltf_component_type_r_8u) 5560 { 5561 // Init raylib mesh color to copy glTF attribute data 5562 model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); 5563 5564 // Load data into a temp buffer to be converted to raylib data type 5565 unsigned char *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned char)); 5566 LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp); 5567 5568 // Convert data to raylib color data type (4 bytes) 5569 for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3) 5570 { 5571 model.meshes[meshIndex].colors[c] = temp[k]; 5572 model.meshes[meshIndex].colors[c + 1] = temp[k + 1]; 5573 model.meshes[meshIndex].colors[c + 2] = temp[k + 2]; 5574 model.meshes[meshIndex].colors[c + 3] = 255; 5575 } 5576 5577 RL_FREE(temp); 5578 } 5579 else if (attribute->component_type == cgltf_component_type_r_16u) 5580 { 5581 // Init raylib mesh color to copy glTF attribute data 5582 model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); 5583 5584 // Load data into a temp buffer to be converted to raylib data type 5585 unsigned short *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned short)); 5586 LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp); 5587 5588 // Convert data to raylib color data type (4 bytes) 5589 for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3) 5590 { 5591 model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[k]/65535.0f)*255.0f); 5592 model.meshes[meshIndex].colors[c + 1] = (unsigned char)(((float)temp[k + 1]/65535.0f)*255.0f); 5593 model.meshes[meshIndex].colors[c + 2] = (unsigned char)(((float)temp[k + 2]/65535.0f)*255.0f); 5594 model.meshes[meshIndex].colors[c + 3] = 255; 5595 } 5596 5597 RL_FREE(temp); 5598 } 5599 else if (attribute->component_type == cgltf_component_type_r_32f) 5600 { 5601 // Init raylib mesh color to copy glTF attribute data 5602 model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); 5603 5604 // Load data into a temp buffer to be converted to raylib data type 5605 float *temp = RL_MALLOC(attribute->count*3*sizeof(float)); 5606 LOAD_ATTRIBUTE(attribute, 3, float, temp); 5607 5608 // Convert data to raylib color data type (4 bytes) 5609 for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3) 5610 { 5611 model.meshes[meshIndex].colors[c] = (unsigned char)(temp[k]*255.0f); 5612 model.meshes[meshIndex].colors[c + 1] = (unsigned char)(temp[k + 1]*255.0f); 5613 model.meshes[meshIndex].colors[c + 2] = (unsigned char)(temp[k + 2]*255.0f); 5614 model.meshes[meshIndex].colors[c + 3] = 255; 5615 } 5616 5617 RL_FREE(temp); 5618 } 5619 else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName); 5620 } 5621 else if (attribute->type == cgltf_type_vec4) // RGBA 5622 { 5623 if (attribute->component_type == cgltf_component_type_r_8u) 5624 { 5625 // Init raylib mesh color to copy glTF attribute data 5626 model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); 5627 5628 // Load 4 components of unsigned char data type into mesh.colors 5629 LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].colors) 5630 } 5631 else if (attribute->component_type == cgltf_component_type_r_16u) 5632 { 5633 // Init raylib mesh color to copy glTF attribute data 5634 model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); 5635 5636 // Load data into a temp buffer to be converted to raylib data type 5637 unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short)); 5638 LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); 5639 5640 // Convert data to raylib color data type (4 bytes) 5641 for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[c]/65535.0f)*255.0f); 5642 5643 RL_FREE(temp); 5644 } 5645 else if (attribute->component_type == cgltf_component_type_r_32f) 5646 { 5647 // Init raylib mesh color to copy glTF attribute data 5648 model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); 5649 5650 // Load data into a temp buffer to be converted to raylib data type 5651 float *temp = RL_MALLOC(attribute->count*4*sizeof(float)); 5652 LOAD_ATTRIBUTE(attribute, 4, float, temp); 5653 5654 // Convert data to raylib color data type (4 bytes), we expect the color data normalized 5655 for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(temp[c]*255.0f); 5656 5657 RL_FREE(temp); 5658 } 5659 else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName); 5660 } 5661 else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName); 5662 } 5663 5664 // NOTE: Attributes related to animations are processed separately 5665 } 5666 5667 // Load primitive indices data (if provided) 5668 if (mesh->primitives[p].indices != NULL) 5669 { 5670 cgltf_accessor *attribute = mesh->primitives[p].indices; 5671 5672 model.meshes[meshIndex].triangleCount = (int)attribute->count/3; 5673 5674 if (attribute->component_type == cgltf_component_type_r_16u) 5675 { 5676 // Init raylib mesh indices to copy glTF attribute data 5677 model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); 5678 5679 // Load unsigned short data type into mesh.indices 5680 LOAD_ATTRIBUTE(attribute, 1, unsigned short, model.meshes[meshIndex].indices) 5681 } 5682 else if (attribute->component_type == cgltf_component_type_r_8u) 5683 { 5684 // Init raylib mesh indices to copy glTF attribute data 5685 model.meshes[meshIndex].indices = RL_MALLOC(attribute->count * sizeof(unsigned short)); 5686 LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned char, model.meshes[meshIndex].indices, unsigned short) 5687 5688 } 5689 else if (attribute->component_type == cgltf_component_type_r_32u) 5690 { 5691 // Init raylib mesh indices to copy glTF attribute data 5692 model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); 5693 LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned int, model.meshes[meshIndex].indices, unsigned short); 5694 5695 TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName); 5696 } 5697 else 5698 { 5699 TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data format not supported, use u16", fileName); 5700 } 5701 } 5702 else model.meshes[meshIndex].triangleCount = model.meshes[meshIndex].vertexCount/3; // Unindexed mesh 5703 5704 // Assign to the primitive mesh the corresponding material index 5705 // NOTE: If no material defined, mesh uses the already assigned default material (index: 0) 5706 for (unsigned int m = 0; m < data->materials_count; m++) 5707 { 5708 // The primitive actually keeps the pointer to the corresponding material, 5709 // raylib instead assigns to the mesh the by its index, as loaded in model.materials array 5710 // To get the index, we check if material pointers match, and we assign the corresponding index, 5711 // skipping index 0, the default material 5712 if (&data->materials[m] == mesh->primitives[p].material) 5713 { 5714 model.meshMaterial[meshIndex] = m + 1; 5715 break; 5716 } 5717 } 5718 5719 meshIndex++; // Move to next mesh 5720 } 5721 } 5722 5723 // Load glTF meshes animation data 5724 // REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skins 5725 // REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skinned-mesh-attributes 5726 // 5727 // LIMITATIONS: 5728 // - Only supports 1 armature per file, and skips loading it if there are multiple armatures 5729 // - Only supports linear interpolation (default method in Blender when checked "Always Sample Animations" when exporting a GLTF file) 5730 // - Only supports translation/rotation/scale animation channel.path, weights not considered (i.e. morph targets) 5731 //---------------------------------------------------------------------------------------------------- 5732 if (data->skins_count > 0) 5733 { 5734 cgltf_skin skin = data->skins[0]; 5735 model.bones = LoadBoneInfoGLTF(skin, &model.boneCount); 5736 model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform)); 5737 5738 for (int i = 0; i < model.boneCount; i++) 5739 { 5740 cgltf_node* node = skin.joints[i]; 5741 cgltf_float worldTransform[16]; 5742 cgltf_node_transform_world(node, worldTransform); 5743 Matrix worldMatrix = { 5744 worldTransform[0], worldTransform[4], worldTransform[8], worldTransform[12], 5745 worldTransform[1], worldTransform[5], worldTransform[9], worldTransform[13], 5746 worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14], 5747 worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15] 5748 }; 5749 MatrixDecompose(worldMatrix, &(model.bindPose[i].translation), &(model.bindPose[i].rotation), &(model.bindPose[i].scale)); 5750 } 5751 } 5752 if (data->skins_count > 1) 5753 { 5754 TRACELOG(LOG_WARNING, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count); 5755 } 5756 5757 meshIndex = 0; 5758 for (unsigned int i = 0; i < data->nodes_count; i++) 5759 { 5760 cgltf_node *node = &(data->nodes[i]); 5761 5762 cgltf_mesh *mesh = node->mesh; 5763 if (!mesh) 5764 continue; 5765 5766 for (unsigned int p = 0; p < mesh->primitives_count; p++) 5767 { 5768 // NOTE: We only support primitives defined by triangles 5769 if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue; 5770 5771 for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++) 5772 { 5773 // NOTE: JOINTS_1 + WEIGHT_1 will be used for +4 joints influencing a vertex -> Not supported by raylib 5774 5775 if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_joints) // JOINTS_n (vec4: 4 bones max per vertex / u8, u16) 5776 { 5777 cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; 5778 5779 // NOTE: JOINTS_n can only be vec4 and u8/u16 5780 // SPECS: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview 5781 5782 // WARNING: raylib only supports model.meshes[].boneIds as u8 (unsigned char), 5783 // if data is provided in any other format, it is converted to supported format but 5784 // it could imply data loss (a warning message is issued in that case) 5785 5786 if (attribute->type == cgltf_type_vec4) 5787 { 5788 if (attribute->component_type == cgltf_component_type_r_8u) 5789 { 5790 // Init raylib mesh boneIds to copy glTF attribute data 5791 model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); 5792 5793 // Load attribute: vec4, u8 (unsigned char) 5794 LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIds) 5795 } 5796 else if (attribute->component_type == cgltf_component_type_r_16u) 5797 { 5798 // Init raylib mesh boneIds to copy glTF attribute data 5799 model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); 5800 5801 // Load data into a temp buffer to be converted to raylib data type 5802 unsigned short *temp = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short)); 5803 LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); 5804 5805 // Convert data to raylib color data type (4 bytes) 5806 bool boneIdOverflowWarning = false; 5807 for (int b = 0; b < model.meshes[meshIndex].vertexCount*4; b++) 5808 { 5809 if ((temp[b] > 255) && !boneIdOverflowWarning) 5810 { 5811 TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format (u16) overflow", fileName); 5812 boneIdOverflowWarning = true; 5813 } 5814 5815 // Despite the possible overflow, we convert data to unsigned char 5816 model.meshes[meshIndex].boneIds[b] = (unsigned char)temp[b]; 5817 } 5818 5819 RL_FREE(temp); 5820 } 5821 else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format not supported", fileName); 5822 } 5823 else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format not supported", fileName); 5824 } 5825 else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_weights) // WEIGHTS_n (vec4, u8n/u16n/f32) 5826 { 5827 cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; 5828 5829 if (attribute->type == cgltf_type_vec4) 5830 { 5831 // TODO: Support component types: u8, u16? 5832 if (attribute->component_type == cgltf_component_type_r_8u) 5833 { 5834 // Init raylib mesh bone weight to copy glTF attribute data 5835 model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); 5836 5837 // Load data into a temp buffer to be converted to raylib data type 5838 unsigned char *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); 5839 LOAD_ATTRIBUTE(attribute, 4, unsigned char, temp); 5840 5841 // Convert data to raylib bone weight data type (4 bytes) 5842 for (unsigned int b = 0; b < attribute->count*4; b++) model.meshes[meshIndex].boneWeights[b] = (float)temp[b]/255.0f; 5843 5844 RL_FREE(temp); 5845 } 5846 else if (attribute->component_type == cgltf_component_type_r_16u) 5847 { 5848 // Init raylib mesh bone weight to copy glTF attribute data 5849 model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); 5850 5851 // Load data into a temp buffer to be converted to raylib data type 5852 unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short)); 5853 LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); 5854 5855 // Convert data to raylib bone weight data type 5856 for (unsigned int b = 0; b < attribute->count*4; b++) model.meshes[meshIndex].boneWeights[b] = (float)temp[b]/65535.0f; 5857 5858 RL_FREE(temp); 5859 } 5860 else if (attribute->component_type == cgltf_component_type_r_32f) 5861 { 5862 // Init raylib mesh bone weight to copy glTF attribute data 5863 model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); 5864 5865 // Load 4 components of float data type into mesh.boneWeights 5866 // for cgltf_attribute_type_weights we have: 5867 // - data.meshes[0] (256 vertices) 5868 // - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16) 5869 LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights) 5870 } 5871 else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint weight attribute data format not supported, use vec4 float", fileName); 5872 } 5873 else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint weight attribute data format not supported, use vec4 float", fileName); 5874 } 5875 } 5876 5877 // Animated vertex data 5878 model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); 5879 memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float)); 5880 model.meshes[meshIndex].animNormals = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); 5881 if (model.meshes[meshIndex].normals != NULL) 5882 { 5883 memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float)); 5884 } 5885 5886 // Bone Transform Matrices 5887 model.meshes[meshIndex].boneCount = model.boneCount; 5888 model.meshes[meshIndex].boneMatrices = RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix)); 5889 5890 for (int j = 0; j < model.meshes[meshIndex].boneCount; j++) 5891 { 5892 model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity(); 5893 } 5894 5895 meshIndex++; // Move to next mesh 5896 } 5897 5898 } 5899 5900 // Free all cgltf loaded data 5901 cgltf_free(data); 5902 } 5903 else TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName); 5904 5905 // WARNING: cgltf requires the file pointer available while reading data 5906 UnloadFileData(fileData); 5907 5908 return model; 5909 } 5910 5911 // Get interpolated pose for bone sampler at a specific time. Returns true on success 5912 static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_accessor *input, cgltf_accessor *output, float time, void *data) 5913 { 5914 if (interpolationType >= cgltf_interpolation_type_max_enum) return false; 5915 5916 // Input and output should have the same count 5917 float tstart = 0.0f; 5918 float tend = 0.0f; 5919 int keyframe = 0; // Defaults to first pose 5920 5921 for (int i = 0; i < (int)input->count - 1; i++) 5922 { 5923 cgltf_bool r1 = cgltf_accessor_read_float(input, i, &tstart, 1); 5924 if (!r1) return false; 5925 5926 cgltf_bool r2 = cgltf_accessor_read_float(input, i + 1, &tend, 1); 5927 if (!r2) return false; 5928 5929 if ((tstart <= time) && (time < tend)) 5930 { 5931 keyframe = i; 5932 break; 5933 } 5934 } 5935 5936 // Constant animation, no need to interpolate 5937 if (FloatEquals(tend, tstart)) return true; 5938 5939 float duration = fmaxf((tend - tstart), EPSILON); 5940 float t = (time - tstart)/duration; 5941 t = (t < 0.0f)? 0.0f : t; 5942 t = (t > 1.0f)? 1.0f : t; 5943 5944 if (output->component_type != cgltf_component_type_r_32f) return false; 5945 5946 if (output->type == cgltf_type_vec3) 5947 { 5948 switch (interpolationType) 5949 { 5950 case cgltf_interpolation_type_step: 5951 { 5952 float tmp[3] = { 0.0f }; 5953 cgltf_accessor_read_float(output, keyframe, tmp, 3); 5954 Vector3 v1 = {tmp[0], tmp[1], tmp[2]}; 5955 Vector3 *r = data; 5956 5957 *r = v1; 5958 } break; 5959 case cgltf_interpolation_type_linear: 5960 { 5961 float tmp[3] = { 0.0f }; 5962 cgltf_accessor_read_float(output, keyframe, tmp, 3); 5963 Vector3 v1 = {tmp[0], tmp[1], tmp[2]}; 5964 cgltf_accessor_read_float(output, keyframe+1, tmp, 3); 5965 Vector3 v2 = {tmp[0], tmp[1], tmp[2]}; 5966 Vector3 *r = data; 5967 5968 *r = Vector3Lerp(v1, v2, t); 5969 } break; 5970 case cgltf_interpolation_type_cubic_spline: 5971 { 5972 float tmp[3] = { 0.0f }; 5973 cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 3); 5974 Vector3 v1 = {tmp[0], tmp[1], tmp[2]}; 5975 cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 3); 5976 Vector3 tangent1 = {tmp[0], tmp[1], tmp[2]}; 5977 cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 3); 5978 Vector3 v2 = {tmp[0], tmp[1], tmp[2]}; 5979 cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 3); 5980 Vector3 tangent2 = {tmp[0], tmp[1], tmp[2]}; 5981 Vector3 *r = data; 5982 5983 *r = Vector3CubicHermite(v1, tangent1, v2, tangent2, t); 5984 } break; 5985 default: break; 5986 } 5987 } 5988 else if (output->type == cgltf_type_vec4) 5989 { 5990 // Only v4 is for rotations, so we know it's a quaternion 5991 switch (interpolationType) 5992 { 5993 case cgltf_interpolation_type_step: 5994 { 5995 float tmp[4] = { 0.0f }; 5996 cgltf_accessor_read_float(output, keyframe, tmp, 4); 5997 Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]}; 5998 Vector4 *r = data; 5999 6000 *r = v1; 6001 } break; 6002 case cgltf_interpolation_type_linear: 6003 { 6004 float tmp[4] = { 0.0f }; 6005 cgltf_accessor_read_float(output, keyframe, tmp, 4); 6006 Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]}; 6007 cgltf_accessor_read_float(output, keyframe+1, tmp, 4); 6008 Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; 6009 Vector4 *r = data; 6010 6011 *r = QuaternionSlerp(v1, v2, t); 6012 } break; 6013 case cgltf_interpolation_type_cubic_spline: 6014 { 6015 float tmp[4] = { 0.0f }; 6016 cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 4); 6017 Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]}; 6018 cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 4); 6019 Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2], 0.0f}; 6020 cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 4); 6021 Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; 6022 cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 4); 6023 Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2], 0.0f}; 6024 Vector4 *r = data; 6025 6026 v1 = QuaternionNormalize(v1); 6027 v2 = QuaternionNormalize(v2); 6028 6029 if (Vector4DotProduct(v1, v2) < 0.0f) 6030 { 6031 v2 = Vector4Negate(v2); 6032 } 6033 6034 outTangent1 = Vector4Scale(outTangent1, duration); 6035 inTangent2 = Vector4Scale(inTangent2, duration); 6036 6037 *r = QuaternionCubicHermiteSpline(v1, outTangent1, v2, inTangent2, t); 6038 } break; 6039 default: break; 6040 } 6041 } 6042 6043 return true; 6044 } 6045 6046 #define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) 6047 6048 static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount) 6049 { 6050 // glTF file loading 6051 int dataSize = 0; 6052 unsigned char *fileData = LoadFileData(fileName, &dataSize); 6053 6054 ModelAnimation *animations = NULL; 6055 6056 // glTF data loading 6057 cgltf_options options = { 0 }; 6058 options.file.read = LoadFileGLTFCallback; 6059 options.file.release = ReleaseFileGLTFCallback; 6060 cgltf_data *data = NULL; 6061 cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data); 6062 6063 if (result != cgltf_result_success) 6064 { 6065 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName); 6066 *animCount = 0; 6067 return NULL; 6068 } 6069 6070 result = cgltf_load_buffers(&options, data, fileName); 6071 if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load animation buffers", fileName); 6072 6073 if (result == cgltf_result_success) 6074 { 6075 if (data->skins_count > 0) 6076 { 6077 cgltf_skin skin = data->skins[0]; 6078 *animCount = (int)data->animations_count; 6079 animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); 6080 6081 for (unsigned int i = 0; i < data->animations_count; i++) 6082 { 6083 animations[i].bones = LoadBoneInfoGLTF(skin, &animations[i].boneCount); 6084 6085 cgltf_animation animData = data->animations[i]; 6086 6087 struct Channels { 6088 cgltf_animation_channel *translate; 6089 cgltf_animation_channel *rotate; 6090 cgltf_animation_channel *scale; 6091 cgltf_interpolation_type interpolationType; 6092 }; 6093 6094 struct Channels *boneChannels = RL_CALLOC(animations[i].boneCount, sizeof(struct Channels)); 6095 float animDuration = 0.0f; 6096 6097 for (unsigned int j = 0; j < animData.channels_count; j++) 6098 { 6099 cgltf_animation_channel channel = animData.channels[j]; 6100 int boneIndex = -1; 6101 6102 for (unsigned int k = 0; k < skin.joints_count; k++) 6103 { 6104 if (animData.channels[j].target_node == skin.joints[k]) 6105 { 6106 boneIndex = k; 6107 break; 6108 } 6109 } 6110 6111 if (boneIndex == -1) 6112 { 6113 // Animation channel for a node not in the armature 6114 continue; 6115 } 6116 6117 boneChannels[boneIndex].interpolationType = animData.channels[j].sampler->interpolation; 6118 6119 if (animData.channels[j].sampler->interpolation != cgltf_interpolation_type_max_enum) 6120 { 6121 if (channel.target_path == cgltf_animation_path_type_translation) 6122 { 6123 boneChannels[boneIndex].translate = &animData.channels[j]; 6124 } 6125 else if (channel.target_path == cgltf_animation_path_type_rotation) 6126 { 6127 boneChannels[boneIndex].rotate = &animData.channels[j]; 6128 } 6129 else if (channel.target_path == cgltf_animation_path_type_scale) 6130 { 6131 boneChannels[boneIndex].scale = &animData.channels[j]; 6132 } 6133 else 6134 { 6135 TRACELOG(LOG_WARNING, "MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping.", fileName, j, i); 6136 } 6137 } 6138 else TRACELOG(LOG_WARNING, "MODEL: [%s] Invalid interpolation curve encountered for GLTF animation.", fileName); 6139 6140 float t = 0.0f; 6141 cgltf_bool r = cgltf_accessor_read_float(channel.sampler->input, channel.sampler->input->count - 1, &t, 1); 6142 6143 if (!r) 6144 { 6145 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load input time", fileName); 6146 continue; 6147 } 6148 6149 animDuration = (t > animDuration)? t : animDuration; 6150 } 6151 6152 if (animData.name != NULL) 6153 { 6154 strncpy(animations[i].name, animData.name, sizeof(animations[i].name)); 6155 animations[i].name[sizeof(animations[i].name) - 1] = '\0'; 6156 } 6157 6158 animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY) + 1; 6159 animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); 6160 6161 for (int j = 0; j < animations[i].frameCount; j++) 6162 { 6163 animations[i].framePoses[j] = RL_MALLOC(animations[i].boneCount*sizeof(Transform)); 6164 float time = ((float) j*GLTF_ANIMDELAY)/1000.0f; 6165 6166 for (int k = 0; k < animations[i].boneCount; k++) 6167 { 6168 Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]}; 6169 Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]}; 6170 Vector3 scale = {skin.joints[k]->scale[0], skin.joints[k]->scale[1], skin.joints[k]->scale[2]}; 6171 6172 if (boneChannels[k].translate) 6173 { 6174 if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].translate->sampler->input, boneChannels[k].translate->sampler->output, time, &translation)) 6175 { 6176 TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load translate pose data for bone %s", fileName, animations[i].bones[k].name); 6177 } 6178 } 6179 6180 if (boneChannels[k].rotate) 6181 { 6182 if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].rotate->sampler->input, boneChannels[k].rotate->sampler->output, time, &rotation)) 6183 { 6184 TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load rotate pose data for bone %s", fileName, animations[i].bones[k].name); 6185 } 6186 } 6187 6188 if (boneChannels[k].scale) 6189 { 6190 if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].scale->sampler->input, boneChannels[k].scale->sampler->output, time, &scale)) 6191 { 6192 TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load scale pose data for bone %s", fileName, animations[i].bones[k].name); 6193 } 6194 } 6195 6196 animations[i].framePoses[j][k] = (Transform){ 6197 .translation = translation, 6198 .rotation = rotation, 6199 .scale = scale 6200 }; 6201 } 6202 6203 BuildPoseFromParentJoints(animations[i].bones, animations[i].boneCount, animations[i].framePoses[j]); 6204 } 6205 6206 TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s (%d frames, %fs)", fileName, (animData.name != NULL)? animData.name : "NULL", animations[i].frameCount, animDuration); 6207 RL_FREE(boneChannels); 6208 } 6209 } 6210 6211 if (data->skins_count > 1) 6212 { 6213 TRACELOG(LOG_WARNING, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count); 6214 } 6215 6216 cgltf_free(data); 6217 } 6218 UnloadFileData(fileData); 6219 return animations; 6220 } 6221 #endif 6222 6223 #if defined(SUPPORT_FILEFORMAT_VOX) 6224 // Load VOX (MagicaVoxel) mesh data 6225 static Model LoadVOX(const char *fileName) 6226 { 6227 Model model = { 0 }; 6228 6229 int nbvertices = 0; 6230 int meshescount = 0; 6231 6232 // Read vox file into buffer 6233 int dataSize = 0; 6234 unsigned char *fileData = LoadFileData(fileName, &dataSize); 6235 6236 if (fileData == 0) 6237 { 6238 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName); 6239 return model; 6240 } 6241 6242 // Read and build voxarray description 6243 VoxArray3D voxarray = { 0 }; 6244 int ret = Vox_LoadFromMemory(fileData, dataSize, &voxarray); 6245 6246 if (ret != VOX_SUCCESS) 6247 { 6248 // Error 6249 UnloadFileData(fileData); 6250 6251 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX data", fileName); 6252 return model; 6253 } 6254 else 6255 { 6256 // Success: Compute meshes count 6257 nbvertices = voxarray.vertices.used; 6258 meshescount = 1 + (nbvertices/65536); 6259 6260 TRACELOG(LOG_INFO, "MODEL: [%s] VOX data loaded successfully : %i vertices/%i meshes", fileName, nbvertices, meshescount); 6261 } 6262 6263 // Build models from meshes 6264 model.transform = MatrixIdentity(); 6265 6266 model.meshCount = meshescount; 6267 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); 6268 6269 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); 6270 6271 model.materialCount = 1; 6272 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); 6273 model.materials[0] = LoadMaterialDefault(); 6274 6275 // Init model meshes 6276 int verticesRemain = voxarray.vertices.used; 6277 int verticesMax = 65532; // 5461 voxels x 12 vertices per voxel -> 65532 (must be inf 65536) 6278 6279 // 6*4 = 12 vertices per voxel 6280 Vector3 *pvertices = (Vector3 *)voxarray.vertices.array; 6281 Vector3 *pnormals = (Vector3 *)voxarray.normals.array; 6282 Color *pcolors = (Color *)voxarray.colors.array; 6283 6284 unsigned short *pindices = voxarray.indices.array; // 5461*6*6 = 196596 indices max per mesh 6285 6286 int size = 0; 6287 6288 for (int i = 0; i < meshescount; i++) 6289 { 6290 Mesh *pmesh = &model.meshes[i]; 6291 memset(pmesh, 0, sizeof(Mesh)); 6292 6293 // Copy vertices 6294 pmesh->vertexCount = (int)fmin(verticesMax, verticesRemain); 6295 6296 size = pmesh->vertexCount*sizeof(float)*3; 6297 pmesh->vertices = (float *)RL_MALLOC(size); 6298 memcpy(pmesh->vertices, pvertices, size); 6299 6300 // Copy normals 6301 pmesh->normals = (float *)RL_MALLOC(size); 6302 memcpy(pmesh->normals, pnormals, size); 6303 6304 // Copy indices 6305 size = voxarray.indices.used*sizeof(unsigned short); 6306 pmesh->indices = (unsigned short *)RL_MALLOC(size); 6307 memcpy(pmesh->indices, pindices, size); 6308 6309 pmesh->triangleCount = (pmesh->vertexCount/4)*2; 6310 6311 // Copy colors 6312 size = pmesh->vertexCount*sizeof(Color); 6313 pmesh->colors = RL_MALLOC(size); 6314 memcpy(pmesh->colors, pcolors, size); 6315 6316 // First material index 6317 model.meshMaterial[i] = 0; 6318 6319 verticesRemain -= verticesMax; 6320 pvertices += verticesMax; 6321 pnormals += verticesMax; 6322 pcolors += verticesMax; 6323 } 6324 6325 // Free buffers 6326 Vox_FreeArrays(&voxarray); 6327 UnloadFileData(fileData); 6328 6329 return model; 6330 } 6331 #endif 6332 6333 #if defined(SUPPORT_FILEFORMAT_M3D) 6334 // Hook LoadFileData()/UnloadFileData() calls to M3D loaders 6335 unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); } 6336 void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); } 6337 6338 // Load M3D mesh data 6339 static Model LoadM3D(const char *fileName) 6340 { 6341 Model model = { 0 }; 6342 6343 m3d_t *m3d = NULL; 6344 m3dp_t *prop = NULL; 6345 int i, j, k, l, n, mi = -2, vcolor = 0; 6346 6347 int dataSize = 0; 6348 unsigned char *fileData = LoadFileData(fileName, &dataSize); 6349 6350 if (fileData != NULL) 6351 { 6352 m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL); 6353 6354 if (!m3d || M3D_ERR_ISFATAL(m3d->errcode)) 6355 { 6356 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2); 6357 if (m3d) m3d_free(m3d); 6358 UnloadFileData(fileData); 6359 return model; 6360 } 6361 else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial); 6362 6363 // no face? this is probably just a material library 6364 if (!m3d->numface) 6365 { 6366 m3d_free(m3d); 6367 UnloadFileData(fileData); 6368 return model; 6369 } 6370 6371 if (m3d->nummaterial > 0) 6372 { 6373 model.meshCount = model.materialCount = m3d->nummaterial; 6374 TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", model.materialCount); 6375 } 6376 else 6377 { 6378 model.meshCount = 1; model.materialCount = 0; 6379 TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); 6380 } 6381 6382 // We always need a default material, so we add +1 6383 model.materialCount++; 6384 6385 // Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise 6386 // WARNING: Sorting is not needed, valid M3D model files should already be sorted 6387 // Just keeping the sorting function for reference (Check PR #3363 #3385) 6388 /* 6389 for (i = 1; i < m3d->numface; i++) 6390 { 6391 if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue; 6392 6393 // face[i-1] > face[i]. slide face[i] lower 6394 m3df_t slider = m3d->face[i]; 6395 j = i-1; 6396 6397 do 6398 { // face[j] > slider, face[j+1] is svailable vacant gap 6399 m3d->face[j+1] = m3d->face[j]; 6400 j = j-1; 6401 } 6402 while (j >= 0 && m3d->face[j].materialid > slider.materialid); 6403 6404 m3d->face[j+1] = slider; 6405 } 6406 */ 6407 6408 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); 6409 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); 6410 model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material)); 6411 6412 // Map no material to index 0 with default shader, everything else materialid + 1 6413 model.materials[0] = LoadMaterialDefault(); 6414 6415 for (i = l = 0, k = -1; i < (int)m3d->numface; i++, l++) 6416 { 6417 // Materials are grouped together 6418 if (mi != m3d->face[i].materialid) 6419 { 6420 // there should be only one material switch per material kind, but be bulletproof for non-optimal model files 6421 if (k + 1 >= model.meshCount) 6422 { 6423 model.meshCount++; 6424 model.meshes = (Mesh *)RL_REALLOC(model.meshes, model.meshCount*sizeof(Mesh)); 6425 memset(&model.meshes[model.meshCount - 1], 0, sizeof(Mesh)); 6426 model.meshMaterial = (int *)RL_REALLOC(model.meshMaterial, model.meshCount*sizeof(int)); 6427 } 6428 6429 k++; 6430 mi = m3d->face[i].materialid; 6431 6432 // Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch 6433 // if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors 6434 for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++) 6435 { 6436 if (!m3d->vertex[m3d->face[j].vertex[0]].color || 6437 !m3d->vertex[m3d->face[j].vertex[1]].color || 6438 !m3d->vertex[m3d->face[j].vertex[2]].color) vcolor = 1; 6439 } 6440 6441 model.meshes[k].vertexCount = l*3; 6442 model.meshes[k].triangleCount = l; 6443 model.meshes[k].vertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); 6444 model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float)); 6445 model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); 6446 6447 // If no map is provided, or we have colors defined, we allocate storage for vertex colors 6448 // M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors 6449 if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); 6450 6451 // If no map is provided and we allocated vertex colors, set them to white 6452 if ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL)) 6453 { 6454 for (int c = 0; c < model.meshes[k].vertexCount*4; c++) model.meshes[k].colors[c] = 255; 6455 } 6456 6457 if (m3d->numbone && m3d->numskin) 6458 { 6459 model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); 6460 model.meshes[k].boneWeights = (float *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(float)); 6461 model.meshes[k].animVertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); 6462 model.meshes[k].animNormals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); 6463 } 6464 6465 model.meshMaterial[k] = mi + 1; 6466 l = 0; 6467 } 6468 6469 // Process meshes per material, add triangles 6470 model.meshes[k].vertices[l*9 + 0] = m3d->vertex[m3d->face[i].vertex[0]].x*m3d->scale; 6471 model.meshes[k].vertices[l*9 + 1] = m3d->vertex[m3d->face[i].vertex[0]].y*m3d->scale; 6472 model.meshes[k].vertices[l*9 + 2] = m3d->vertex[m3d->face[i].vertex[0]].z*m3d->scale; 6473 model.meshes[k].vertices[l*9 + 3] = m3d->vertex[m3d->face[i].vertex[1]].x*m3d->scale; 6474 model.meshes[k].vertices[l*9 + 4] = m3d->vertex[m3d->face[i].vertex[1]].y*m3d->scale; 6475 model.meshes[k].vertices[l*9 + 5] = m3d->vertex[m3d->face[i].vertex[1]].z*m3d->scale; 6476 model.meshes[k].vertices[l*9 + 6] = m3d->vertex[m3d->face[i].vertex[2]].x*m3d->scale; 6477 model.meshes[k].vertices[l*9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*m3d->scale; 6478 model.meshes[k].vertices[l*9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale; 6479 6480 // Without vertex color (full transparency), we use the default color 6481 if (model.meshes[k].colors != NULL) 6482 { 6483 if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000) 6484 memcpy(&model.meshes[k].colors[l*12 + 0], &m3d->vertex[m3d->face[i].vertex[0]].color, 4); 6485 if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xFF000000) 6486 memcpy(&model.meshes[k].colors[l*12 + 4], &m3d->vertex[m3d->face[i].vertex[1]].color, 4); 6487 if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xFF000000) 6488 memcpy(&model.meshes[k].colors[l*12 + 8], &m3d->vertex[m3d->face[i].vertex[2]].color, 4); 6489 } 6490 6491 if (m3d->face[i].texcoord[0] != M3D_UNDEF) 6492 { 6493 model.meshes[k].texcoords[l*6 + 0] = m3d->tmap[m3d->face[i].texcoord[0]].u; 6494 model.meshes[k].texcoords[l*6 + 1] = 1.0f - m3d->tmap[m3d->face[i].texcoord[0]].v; 6495 model.meshes[k].texcoords[l*6 + 2] = m3d->tmap[m3d->face[i].texcoord[1]].u; 6496 model.meshes[k].texcoords[l*6 + 3] = 1.0f - m3d->tmap[m3d->face[i].texcoord[1]].v; 6497 model.meshes[k].texcoords[l*6 + 4] = m3d->tmap[m3d->face[i].texcoord[2]].u; 6498 model.meshes[k].texcoords[l*6 + 5] = 1.0f - m3d->tmap[m3d->face[i].texcoord[2]].v; 6499 } 6500 6501 if (m3d->face[i].normal[0] != M3D_UNDEF) 6502 { 6503 model.meshes[k].normals[l*9 + 0] = m3d->vertex[m3d->face[i].normal[0]].x; 6504 model.meshes[k].normals[l*9 + 1] = m3d->vertex[m3d->face[i].normal[0]].y; 6505 model.meshes[k].normals[l*9 + 2] = m3d->vertex[m3d->face[i].normal[0]].z; 6506 model.meshes[k].normals[l*9 + 3] = m3d->vertex[m3d->face[i].normal[1]].x; 6507 model.meshes[k].normals[l*9 + 4] = m3d->vertex[m3d->face[i].normal[1]].y; 6508 model.meshes[k].normals[l*9 + 5] = m3d->vertex[m3d->face[i].normal[1]].z; 6509 model.meshes[k].normals[l*9 + 6] = m3d->vertex[m3d->face[i].normal[2]].x; 6510 model.meshes[k].normals[l*9 + 7] = m3d->vertex[m3d->face[i].normal[2]].y; 6511 model.meshes[k].normals[l*9 + 8] = m3d->vertex[m3d->face[i].normal[2]].z; 6512 } 6513 6514 // Add skin (vertex / bone weight pairs) 6515 if (m3d->numbone && m3d->numskin) 6516 { 6517 for (n = 0; n < 3; n++) 6518 { 6519 int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid; 6520 6521 // Check if there is a skin for this mesh, should be, just failsafe 6522 if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin)) 6523 { 6524 for (j = 0; j < 4; j++) 6525 { 6526 model.meshes[k].boneIds[l*12 + n*4 + j] = m3d->skin[skinid].boneid[j]; 6527 model.meshes[k].boneWeights[l*12 + n*4 + j] = m3d->skin[skinid].weight[j]; 6528 } 6529 } 6530 else 6531 { 6532 // raylib does not handle boneless meshes with skeletal animations, so 6533 // we put all vertices without a bone into a special "no bone" bone 6534 model.meshes[k].boneIds[l*12 + n*4] = m3d->numbone; 6535 model.meshes[k].boneWeights[l*12 + n*4] = 1.0f; 6536 } 6537 } 6538 } 6539 } 6540 6541 // Load materials 6542 for (i = 0; i < (int)m3d->nummaterial; i++) 6543 { 6544 model.materials[i + 1] = LoadMaterialDefault(); 6545 6546 for (j = 0; j < m3d->material[i].numprop; j++) 6547 { 6548 prop = &m3d->material[i].prop[j]; 6549 6550 switch (prop->type) 6551 { 6552 case m3dp_Kd: 6553 { 6554 memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].color, &prop->value.color, 4); 6555 model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; 6556 } break; 6557 case m3dp_Ks: 6558 { 6559 memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].color, &prop->value.color, 4); 6560 } break; 6561 case m3dp_Ns: 6562 { 6563 model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].value = prop->value.fnum; 6564 } break; 6565 case m3dp_Ke: 6566 { 6567 memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].color, &prop->value.color, 4); 6568 model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].value = 0.0f; 6569 } break; 6570 case m3dp_Pm: 6571 { 6572 model.materials[i + 1].maps[MATERIAL_MAP_METALNESS].value = prop->value.fnum; 6573 } break; 6574 case m3dp_Pr: 6575 { 6576 model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].value = prop->value.fnum; 6577 } break; 6578 case m3dp_Ps: 6579 { 6580 model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].color = WHITE; 6581 model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].value = prop->value.fnum; 6582 } break; 6583 default: 6584 { 6585 if (prop->type >= 128) 6586 { 6587 Image image = { 0 }; 6588 image.data = m3d->texture[prop->value.textureid].d; 6589 image.width = m3d->texture[prop->value.textureid].w; 6590 image.height = m3d->texture[prop->value.textureid].h; 6591 image.mipmaps = 1; 6592 image.format = (m3d->texture[prop->value.textureid].f == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : 6593 ((m3d->texture[prop->value.textureid].f == 3)? PIXELFORMAT_UNCOMPRESSED_R8G8B8 : 6594 ((m3d->texture[prop->value.textureid].f == 2)? PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA : PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)); 6595 6596 switch (prop->type) 6597 { 6598 case m3dp_map_Kd: model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTextureFromImage(image); break; 6599 case m3dp_map_Ks: model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].texture = LoadTextureFromImage(image); break; 6600 case m3dp_map_Ke: model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(image); break; 6601 case m3dp_map_Km: model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(image); break; 6602 case m3dp_map_Ka: model.materials[i + 1].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(image); break; 6603 case m3dp_map_Pm: model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(image); break; 6604 default: break; 6605 } 6606 } 6607 } break; 6608 } 6609 } 6610 } 6611 6612 // Load bones 6613 if (m3d->numbone) 6614 { 6615 model.boneCount = m3d->numbone + 1; 6616 model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo)); 6617 model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform)); 6618 6619 for (i = 0; i < (int)m3d->numbone; i++) 6620 { 6621 model.bones[i].parent = m3d->bone[i].parent; 6622 strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name)); 6623 model.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale; 6624 model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale; 6625 model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale; 6626 model.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x; 6627 model.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y; 6628 model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z; 6629 model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w; 6630 6631 // TODO: If the orientation quaternion is not normalized, then that's encoding scaling 6632 model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation); 6633 model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f; 6634 6635 // Child bones are stored in parent bone relative space, convert that into model space 6636 if (model.bones[i].parent >= 0) 6637 { 6638 model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation); 6639 model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation); 6640 model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation); 6641 model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale); 6642 } 6643 } 6644 6645 // Add a special "no bone" bone 6646 model.bones[i].parent = -1; 6647 strcpy(model.bones[i].name, "NO BONE"); 6648 model.bindPose[i].translation.x = 0.0f; 6649 model.bindPose[i].translation.y = 0.0f; 6650 model.bindPose[i].translation.z = 0.0f; 6651 model.bindPose[i].rotation.x = 0.0f; 6652 model.bindPose[i].rotation.y = 0.0f; 6653 model.bindPose[i].rotation.z = 0.0f; 6654 model.bindPose[i].rotation.w = 1.0f; 6655 model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f; 6656 } 6657 6658 // Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets 6659 // called, but not before, however DrawMesh uses these if they exist (so not good if they are left empty) 6660 if (m3d->numbone && m3d->numskin) 6661 { 6662 for (i = 0; i < model.meshCount; i++) 6663 { 6664 memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float)); 6665 memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float)); 6666 6667 model.meshes[i].boneCount = model.boneCount; 6668 model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); 6669 for (j = 0; j < model.meshes[i].boneCount; j++) 6670 { 6671 model.meshes[i].boneMatrices[j] = MatrixIdentity(); 6672 } 6673 } 6674 } 6675 6676 m3d_free(m3d); 6677 UnloadFileData(fileData); 6678 } 6679 6680 return model; 6681 } 6682 6683 #define M3D_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) 6684 6685 // Load M3D animation data 6686 static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount) 6687 { 6688 ModelAnimation *animations = NULL; 6689 6690 m3d_t *m3d = NULL; 6691 int i = 0, j = 0; 6692 *animCount = 0; 6693 6694 int dataSize = 0; 6695 unsigned char *fileData = LoadFileData(fileName, &dataSize); 6696 6697 if (fileData != NULL) 6698 { 6699 m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL); 6700 6701 if (!m3d || M3D_ERR_ISFATAL(m3d->errcode)) 6702 { 6703 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2); 6704 UnloadFileData(fileData); 6705 return NULL; 6706 } 6707 else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName, 6708 m3d->numaction, m3d->numbone, m3d->numskin); 6709 6710 // No animation or bone+skin? 6711 if (!m3d->numaction || !m3d->numbone || !m3d->numskin) 6712 { 6713 m3d_free(m3d); 6714 UnloadFileData(fileData); 6715 return NULL; 6716 } 6717 6718 animations = RL_MALLOC(m3d->numaction*sizeof(ModelAnimation)); 6719 *animCount = m3d->numaction; 6720 6721 for (unsigned int a = 0; a < m3d->numaction; a++) 6722 { 6723 animations[a].frameCount = m3d->action[a].durationmsec/M3D_ANIMDELAY; 6724 animations[a].boneCount = m3d->numbone + 1; 6725 animations[a].bones = RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo)); 6726 animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *)); 6727 strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name)); 6728 animations[a].name[sizeof(animations[a].name) - 1] = '\0'; 6729 6730 TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount); 6731 6732 for (i = 0; i < (int)m3d->numbone; i++) 6733 { 6734 animations[a].bones[i].parent = m3d->bone[i].parent; 6735 strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name)); 6736 } 6737 6738 // A special, never transformed "no bone" bone, used for boneless vertices 6739 animations[a].bones[i].parent = -1; 6740 strcpy(animations[a].bones[i].name, "NO BONE"); 6741 6742 // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at 6743 // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones 6744 for (i = 0; i < animations[a].frameCount; i++) 6745 { 6746 animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); 6747 6748 m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY); 6749 6750 if (pose != NULL) 6751 { 6752 for (j = 0; j < (int)m3d->numbone; j++) 6753 { 6754 animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x*m3d->scale; 6755 animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y*m3d->scale; 6756 animations[a].framePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z*m3d->scale; 6757 animations[a].framePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x; 6758 animations[a].framePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y; 6759 animations[a].framePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z; 6760 animations[a].framePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w; 6761 animations[a].framePoses[i][j].rotation = QuaternionNormalize(animations[a].framePoses[i][j].rotation); 6762 animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; 6763 6764 // Child bones are stored in parent bone relative space, convert that into model space 6765 if (animations[a].bones[j].parent >= 0) 6766 { 6767 animations[a].framePoses[i][j].rotation = QuaternionMultiply(animations[a].framePoses[i][animations[a].bones[j].parent].rotation, animations[a].framePoses[i][j].rotation); 6768 animations[a].framePoses[i][j].translation = Vector3RotateByQuaternion(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].rotation); 6769 animations[a].framePoses[i][j].translation = Vector3Add(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].translation); 6770 animations[a].framePoses[i][j].scale = Vector3Multiply(animations[a].framePoses[i][j].scale, animations[a].framePoses[i][animations[a].bones[j].parent].scale); 6771 } 6772 } 6773 6774 // Default transform for the "no bone" bone 6775 animations[a].framePoses[i][j].translation.x = 0.0f; 6776 animations[a].framePoses[i][j].translation.y = 0.0f; 6777 animations[a].framePoses[i][j].translation.z = 0.0f; 6778 animations[a].framePoses[i][j].rotation.x = 0.0f; 6779 animations[a].framePoses[i][j].rotation.y = 0.0f; 6780 animations[a].framePoses[i][j].rotation.z = 0.0f; 6781 animations[a].framePoses[i][j].rotation.w = 1.0f; 6782 animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f; 6783 RL_FREE(pose); 6784 } 6785 } 6786 } 6787 6788 m3d_free(m3d); 6789 UnloadFileData(fileData); 6790 } 6791 6792 return animations; 6793 } 6794 #endif 6795 6796 #endif // SUPPORT_MODULE_RMODELS