GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/rmodels.c
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 2544 0.0%
Branches: 0 1166 0.0%

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