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