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

Generated with cgit - Back to sebastiano.tronto.net