Logo

index : raylib-jai

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/raylib-jai.git/html/Raylib/raylib/src/rmodels.c blob: 665b941473d51754eddabcff9e4605d54cb7ca59 [raw] [clear marker]

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