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