1/**********************************************************************************************
2*
3* raylib.models - Basic functions to deal with 3d shapes and 3d models
4*
5* CONFIGURATION:
6*
7* #define SUPPORT_FILEFORMAT_OBJ
8* #define SUPPORT_FILEFORMAT_MTL
9* #define SUPPORT_FILEFORMAT_IQM
10* #define SUPPORT_FILEFORMAT_GLTF
11* Selected desired fileformats to be supported for model data loading.
12*
13* #define SUPPORT_MESH_GENERATION
14* Support procedural mesh generation functions, uses external par_shapes.h library
15* NOTE: Some generated meshes DO NOT include generated texture coordinates
16*
17*
18* LICENSE: zlib/libpng
19*
20* Copyright (c) 2013-2020 Ramon Santamaria (@raysan5)
21*
22* This software is provided "as-is", without any express or implied warranty. In no event
23* will the authors be held liable for any damages arising from the use of this software.
24*
25* Permission is granted to anyone to use this software for any purpose, including commercial
26* applications, and to alter it and redistribute it freely, subject to the following restrictions:
27*
28* 1. The origin of this software must not be misrepresented; you must not claim that you
29* wrote the original software. If you use this software in a product, an acknowledgment
30* in the product documentation would be appreciated but is not required.
31*
32* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
33* as being the original software.
34*
35* 3. This notice may not be removed or altered from any source distribution.
36*
37**********************************************************************************************/
38
39#include "raylib.h" // Declares module functions
40
41// Check if config flags have been externally provided on compilation line
42#if !defined(EXTERNAL_CONFIG_FLAGS)
43 #include "config.h" // Defines module configuration flags
44#endif
45
46#include "utils.h" // Required for: fopen() Android mapping
47
48#include <stdlib.h> // Required for: malloc(), free()
49#include <stdio.h> // Required for: FILE, fopen(), fclose()
50#include <string.h> // Required for: strncmp() [Used in LoadModelAnimations()], strlen() [Used in LoadTextureFromCgltfImage()]
51#include <math.h> // Required for: sinf(), cosf(), sqrtf(), fabsf()
52
53#if defined(_WIN32)
54 #include <direct.h> // Required for: _chdir() [Used in LoadOBJ()]
55 #define CHDIR _chdir
56#else
57 #include <unistd.h> // Required for: chdir() (POSIX) [Used in LoadOBJ()]
58 #define CHDIR chdir
59#endif
60
61#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
62
63#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
64 #define TINYOBJ_MALLOC RL_MALLOC
65 #define TINYOBJ_CALLOC RL_CALLOC
66 #define TINYOBJ_REALLOC RL_REALLOC
67 #define TINYOBJ_FREE RL_FREE
68
69 #define TINYOBJ_LOADER_C_IMPLEMENTATION
70 #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading
71#endif
72
73#if defined(SUPPORT_FILEFORMAT_GLTF)
74 #define CGLTF_MALLOC RL_MALLOC
75 #define CGLTF_FREE RL_FREE
76
77 #define CGLTF_IMPLEMENTATION
78 #include "external/cgltf.h" // glTF file format loading
79 #include "external/stb_image.h" // glTF texture images loading
80#endif
81
82#if defined(SUPPORT_MESH_GENERATION)
83 #define PAR_MALLOC(T, N) ((T*)RL_MALLOC(N*sizeof(T)))
84 #define PAR_CALLOC(T, N) ((T*)RL_CALLOC(N*sizeof(T), 1))
85 #define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N)))
86 #define PAR_FREE RL_FREE
87
88 #define PAR_SHAPES_IMPLEMENTATION
89 #include "external/par_shapes.h" // Shapes 3d parametric generation
90#endif
91
92//----------------------------------------------------------------------------------
93// Defines and Macros
94//----------------------------------------------------------------------------------
95#define MAX_MESH_VBO 7 // Maximum number of vbo per mesh
96
97//----------------------------------------------------------------------------------
98// Types and Structures Definition
99//----------------------------------------------------------------------------------
100// ...
101
102//----------------------------------------------------------------------------------
103// Global Variables Definition
104//----------------------------------------------------------------------------------
105// ...
106
107//----------------------------------------------------------------------------------
108// Module specific Functions Declaration
109//----------------------------------------------------------------------------------
110#if defined(SUPPORT_FILEFORMAT_OBJ)
111static Model LoadOBJ(const char *fileName); // Load OBJ mesh data
112#endif
113#if defined(SUPPORT_FILEFORMAT_IQM)
114static Model LoadIQM(const char *fileName); // Load IQM mesh data
115#endif
116#if defined(SUPPORT_FILEFORMAT_GLTF)
117static Model LoadGLTF(const char *fileName); // Load GLTF mesh data
118#endif
119
120//----------------------------------------------------------------------------------
121// Module Functions Definition
122//----------------------------------------------------------------------------------
123
124// Draw a line in 3D world space
125void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color)
126{
127 rlBegin(RL_LINES);
128 rlColor4ub(color.r, color.g, color.b, color.a);
129 rlVertex3f(startPos.x, startPos.y, startPos.z);
130 rlVertex3f(endPos.x, endPos.y, endPos.z);
131 rlEnd();
132}
133
134// Draw a point in 3D space, actually a small line
135void DrawPoint3D(Vector3 position, Color color)
136{
137 if (rlCheckBufferLimit(8)) rlglDraw();
138
139 rlPushMatrix();
140 rlTranslatef(position.x, position.y, position.z);
141 rlBegin(RL_LINES);
142 rlColor4ub(color.r, color.g, color.b, color.a);
143 rlVertex3f(0.0,0.0,0.0);
144 rlVertex3f(0.0,0.0,0.1);
145 rlEnd();
146 rlPopMatrix();
147}
148
149// Draw a circle in 3D world space
150void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color)
151{
152 if (rlCheckBufferLimit(2*36)) rlglDraw();
153
154 rlPushMatrix();
155 rlTranslatef(center.x, center.y, center.z);
156 rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
157
158 rlBegin(RL_LINES);
159 for (int i = 0; i < 360; i += 10)
160 {
161 rlColor4ub(color.r, color.g, color.b, color.a);
162
163 rlVertex3f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius, 0.0f);
164 rlVertex3f(sinf(DEG2RAD*(i + 10))*radius, cosf(DEG2RAD*(i + 10))*radius, 0.0f);
165 }
166 rlEnd();
167 rlPopMatrix();
168}
169
170// Draw cube
171// NOTE: Cube position is the center position
172void DrawCube(Vector3 position, float width, float height, float length, Color color)
173{
174 float x = 0.0f;
175 float y = 0.0f;
176 float z = 0.0f;
177
178 if (rlCheckBufferLimit(36)) rlglDraw();
179
180 rlPushMatrix();
181 // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
182 rlTranslatef(position.x, position.y, position.z);
183 //rlRotatef(45, 0, 1, 0);
184 //rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition
185
186 rlBegin(RL_TRIANGLES);
187 rlColor4ub(color.r, color.g, color.b, color.a);
188
189 // Front face
190 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
191 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
192 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
193
194 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right
195 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
196 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
197
198 // Back face
199 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left
200 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
201 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
202
203 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
204 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
205 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
206
207 // Top face
208 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
209 rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left
210 rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
211
212 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
213 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
214 rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
215
216 // Bottom face
217 rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
218 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
219 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
220
221 rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right
222 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
223 rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
224
225 // Right face
226 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
227 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
228 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
229
230 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left
231 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
232 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
233
234 // Left face
235 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
236 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
237 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right
238
239 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
240 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
241 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
242 rlEnd();
243 rlPopMatrix();
244}
245
246// Draw cube (Vector version)
247void DrawCubeV(Vector3 position, Vector3 size, Color color)
248{
249 DrawCube(position, size.x, size.y, size.z, color);
250}
251
252// Draw cube wires
253void DrawCubeWires(Vector3 position, float width, float height, float length, Color color)
254{
255 float x = 0.0f;
256 float y = 0.0f;
257 float z = 0.0f;
258
259 if (rlCheckBufferLimit(36)) rlglDraw();
260
261 rlPushMatrix();
262 rlTranslatef(position.x, position.y, position.z);
263
264 rlBegin(RL_LINES);
265 rlColor4ub(color.r, color.g, color.b, color.a);
266
267 // Front Face -----------------------------------------------------
268 // Bottom Line
269 rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
270 rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
271
272 // Left Line
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 Right
275
276 // Top Line
277 rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right
278 rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
279
280 // Right Line
281 rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
282 rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
283
284 // Back Face ------------------------------------------------------
285 // Bottom Line
286 rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left
287 rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
288
289 // Left Line
290 rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
291 rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
292
293 // Top Line
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
297 // Right Line
298 rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
299 rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left
300
301 // Top Face -------------------------------------------------------
302 // Left Line
303 rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left Front
304 rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Back
305
306 // Right Line
307 rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right Front
308 rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Back
309
310 // Bottom Face ---------------------------------------------------
311 // Left Line
312 rlVertex3f(x-width/2, y-height/2, z+length/2); // Top Left Front
313 rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left Back
314
315 // Right Line
316 rlVertex3f(x+width/2, y-height/2, z+length/2); // Top Right Front
317 rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Right Back
318 rlEnd();
319 rlPopMatrix();
320}
321
322// Draw cube wires (vector version)
323void DrawCubeWiresV(Vector3 position, Vector3 size, Color color)
324{
325 DrawCubeWires(position, size.x, size.y, size.z, color);
326}
327
328// Draw cube
329// NOTE: Cube position is the center position
330void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color)
331{
332 float x = position.x;
333 float y = position.y;
334 float z = position.z;
335
336 if (rlCheckBufferLimit(36)) rlglDraw();
337
338 rlEnableTexture(texture.id);
339
340 //rlPushMatrix();
341 // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
342 //rlTranslatef(2.0f, 0.0f, 0.0f);
343 //rlRotatef(45, 0, 1, 0);
344 //rlScalef(2.0f, 2.0f, 2.0f);
345
346 rlBegin(RL_QUADS);
347 rlColor4ub(color.r, color.g, color.b, color.a);
348 // Front Face
349 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer
350 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad
351 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad
352 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad
353 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad
354 // Back Face
355 rlNormal3f(0.0f, 0.0f, - 1.0f); // Normal Pointing Away From Viewer
356 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad
357 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad
358 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad
359 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad
360 // Top Face
361 rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
362 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad
363 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left Of The Texture and Quad
364 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right Of The Texture and Quad
365 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad
366 // Bottom Face
367 rlNormal3f(0.0f, - 1.0f, 0.0f); // Normal Pointing Down
368 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Right Of The Texture and Quad
369 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Left Of The Texture and Quad
370 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad
371 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad
372 // Right face
373 rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right
374 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad
375 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad
376 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad
377 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad
378 // Left Face
379 rlNormal3f( - 1.0f, 0.0f, 0.0f); // Normal Pointing Left
380 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad
381 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad
382 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad
383 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad
384 rlEnd();
385 //rlPopMatrix();
386
387 rlDisableTexture();
388}
389
390// Draw sphere
391void DrawSphere(Vector3 centerPos, float radius, Color color)
392{
393 DrawSphereEx(centerPos, radius, 16, 16, color);
394}
395
396// Draw sphere with extended parameters
397void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color)
398{
399 int numVertex = (rings + 2)*slices*6;
400 if (rlCheckBufferLimit(numVertex)) rlglDraw();
401
402 rlPushMatrix();
403 // NOTE: Transformation is applied in inverse order (scale -> translate)
404 rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
405 rlScalef(radius, radius, radius);
406
407 rlBegin(RL_TRIANGLES);
408 rlColor4ub(color.r, color.g, color.b, color.a);
409
410 for (int i = 0; i < (rings + 2); i++)
411 {
412 for (int j = 0; j < slices; j++)
413 {
414 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
415 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
416 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
417 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
418 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
419 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
420 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
421 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
422 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
423
424 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
425 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
426 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
427 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*sinf(DEG2RAD*((j+1)*360/slices)),
428 sinf(DEG2RAD*(270+(180/(rings + 1))*(i))),
429 cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*cosf(DEG2RAD*((j+1)*360/slices)));
430 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
431 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
432 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
433 }
434 }
435 rlEnd();
436 rlPopMatrix();
437}
438
439// Draw sphere wires
440void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color)
441{
442 int numVertex = (rings + 2)*slices*6;
443 if (rlCheckBufferLimit(numVertex)) rlglDraw();
444
445 rlPushMatrix();
446 // NOTE: Transformation is applied in inverse order (scale -> translate)
447 rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
448 rlScalef(radius, radius, radius);
449
450 rlBegin(RL_LINES);
451 rlColor4ub(color.r, color.g, color.b, color.a);
452
453 for (int i = 0; i < (rings + 2); i++)
454 {
455 for (int j = 0; j < slices; j++)
456 {
457 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
458 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
459 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
460 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
461 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
462 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
463
464 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
465 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
466 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
467 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
468 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
469 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
470
471 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
472 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
473 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
474 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
475 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
476 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
477 }
478 }
479 rlEnd();
480 rlPopMatrix();
481}
482
483// Draw a cylinder
484// NOTE: It could be also used for pyramid and cone
485void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
486{
487 if (sides < 3) sides = 3;
488
489 int numVertex = sides*6;
490 if (rlCheckBufferLimit(numVertex)) rlglDraw();
491
492 rlPushMatrix();
493 rlTranslatef(position.x, position.y, position.z);
494
495 rlBegin(RL_TRIANGLES);
496 rlColor4ub(color.r, color.g, color.b, color.a);
497
498 if (radiusTop > 0)
499 {
500 // Draw Body -------------------------------------------------------------------------------------
501 for (int i = 0; i < 360; i += 360/sides)
502 {
503 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left
504 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom); //Bottom Right
505 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); //Top Right
506
507 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop); //Top Left
508 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left
509 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); //Top Right
510 }
511
512 // Draw Cap --------------------------------------------------------------------------------------
513 for (int i = 0; i < 360; i += 360/sides)
514 {
515 rlVertex3f(0, height, 0);
516 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
517 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop);
518 }
519 }
520 else
521 {
522 // Draw Cone -------------------------------------------------------------------------------------
523 for (int i = 0; i < 360; i += 360/sides)
524 {
525 rlVertex3f(0, height, 0);
526 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
527 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
528 }
529 }
530
531 // Draw Base -----------------------------------------------------------------------------------------
532 for (int i = 0; i < 360; i += 360/sides)
533 {
534 rlVertex3f(0, 0, 0);
535 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
536 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
537 }
538 rlEnd();
539 rlPopMatrix();
540}
541
542// Draw a wired cylinder
543// NOTE: It could be also used for pyramid and cone
544void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
545{
546 if (sides < 3) sides = 3;
547
548 int numVertex = sides*8;
549 if (rlCheckBufferLimit(numVertex)) rlglDraw();
550
551 rlPushMatrix();
552 rlTranslatef(position.x, position.y, position.z);
553
554 rlBegin(RL_LINES);
555 rlColor4ub(color.r, color.g, color.b, color.a);
556
557 for (int i = 0; i < 360; i += 360/sides)
558 {
559 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
560 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
561
562 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
563 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop);
564
565 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop);
566 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
567
568 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
569 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
570 }
571 rlEnd();
572 rlPopMatrix();
573}
574
575// Draw a plane
576void DrawPlane(Vector3 centerPos, Vector2 size, Color color)
577{
578 if (rlCheckBufferLimit(4)) rlglDraw();
579
580 // NOTE: Plane is always created on XZ ground
581 rlPushMatrix();
582 rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
583 rlScalef(size.x, 1.0f, size.y);
584
585 rlBegin(RL_QUADS);
586 rlColor4ub(color.r, color.g, color.b, color.a);
587 rlNormal3f(0.0f, 1.0f, 0.0f);
588
589 rlVertex3f(-0.5f, 0.0f, -0.5f);
590 rlVertex3f(-0.5f, 0.0f, 0.5f);
591 rlVertex3f(0.5f, 0.0f, 0.5f);
592 rlVertex3f(0.5f, 0.0f, -0.5f);
593 rlEnd();
594 rlPopMatrix();
595}
596
597// Draw a ray line
598void DrawRay(Ray ray, Color color)
599{
600 float scale = 10000;
601
602 rlBegin(RL_LINES);
603 rlColor4ub(color.r, color.g, color.b, color.a);
604 rlColor4ub(color.r, color.g, color.b, color.a);
605
606 rlVertex3f(ray.position.x, ray.position.y, ray.position.z);
607 rlVertex3f(ray.position.x + ray.direction.x*scale, ray.position.y + ray.direction.y*scale, ray.position.z + ray.direction.z*scale);
608 rlEnd();
609}
610
611// Draw a grid centered at (0, 0, 0)
612void DrawGrid(int slices, float spacing)
613{
614 int halfSlices = slices/2;
615
616 if (rlCheckBufferLimit(slices*4)) rlglDraw();
617
618 rlBegin(RL_LINES);
619 for (int i = -halfSlices; i <= halfSlices; i++)
620 {
621 if (i == 0)
622 {
623 rlColor3f(0.5f, 0.5f, 0.5f);
624 rlColor3f(0.5f, 0.5f, 0.5f);
625 rlColor3f(0.5f, 0.5f, 0.5f);
626 rlColor3f(0.5f, 0.5f, 0.5f);
627 }
628 else
629 {
630 rlColor3f(0.75f, 0.75f, 0.75f);
631 rlColor3f(0.75f, 0.75f, 0.75f);
632 rlColor3f(0.75f, 0.75f, 0.75f);
633 rlColor3f(0.75f, 0.75f, 0.75f);
634 }
635
636 rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing);
637 rlVertex3f((float)i*spacing, 0.0f, (float)halfSlices*spacing);
638
639 rlVertex3f((float)-halfSlices*spacing, 0.0f, (float)i*spacing);
640 rlVertex3f((float)halfSlices*spacing, 0.0f, (float)i*spacing);
641 }
642 rlEnd();
643}
644
645// Draw gizmo
646void DrawGizmo(Vector3 position)
647{
648 // NOTE: RGB = XYZ
649 float length = 1.0f;
650
651 rlPushMatrix();
652 rlTranslatef(position.x, position.y, position.z);
653 rlScalef(length, length, length);
654
655 rlBegin(RL_LINES);
656 rlColor3f(1.0f, 0.0f, 0.0f); rlVertex3f(0.0f, 0.0f, 0.0f);
657 rlColor3f(1.0f, 0.0f, 0.0f); rlVertex3f(1.0f, 0.0f, 0.0f);
658
659 rlColor3f(0.0f, 1.0f, 0.0f); rlVertex3f(0.0f, 0.0f, 0.0f);
660 rlColor3f(0.0f, 1.0f, 0.0f); rlVertex3f(0.0f, 1.0f, 0.0f);
661
662 rlColor3f(0.0f, 0.0f, 1.0f); rlVertex3f(0.0f, 0.0f, 0.0f);
663 rlColor3f(0.0f, 0.0f, 1.0f); rlVertex3f(0.0f, 0.0f, 1.0f);
664 rlEnd();
665 rlPopMatrix();
666}
667
668// Load model from files (mesh and material)
669Model LoadModel(const char *fileName)
670{
671 Model model = { 0 };
672
673#if defined(SUPPORT_FILEFORMAT_OBJ)
674 if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName);
675#endif
676#if defined(SUPPORT_FILEFORMAT_IQM)
677 if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName);
678#endif
679#if defined(SUPPORT_FILEFORMAT_GLTF)
680 if (IsFileExtension(fileName, ".gltf") || IsFileExtension(fileName, ".glb")) model = LoadGLTF(fileName);
681#endif
682
683 // Make sure model transform is set to identity matrix!
684 model.transform = MatrixIdentity();
685
686 if (model.meshCount == 0)
687 {
688 model.meshCount = 1;
689 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
690#if defined(SUPPORT_MESH_GENERATION)
691 TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data, default to cube mesh", fileName);
692 model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f);
693#else
694 TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data", fileName);
695#endif
696 }
697 else
698 {
699 // Upload vertex data to GPU (static mesh)
700 for (int i = 0; i < model.meshCount; i++) rlLoadMesh(&model.meshes[i], false);
701 }
702
703 if (model.materialCount == 0)
704 {
705 TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load material data, default to white material", fileName);
706
707 model.materialCount = 1;
708 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
709 model.materials[0] = LoadMaterialDefault();
710
711 if (model.meshMaterial == NULL) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
712 }
713
714 return model;
715}
716
717// Load model from generated mesh
718// WARNING: A shallow copy of mesh is generated, passed by value,
719// as long as struct contains pointers to data and some values, we get a copy
720// of mesh pointing to same data as original version... be careful!
721Model LoadModelFromMesh(Mesh mesh)
722{
723 Model model = { 0 };
724
725 model.transform = MatrixIdentity();
726
727 model.meshCount = 1;
728 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
729 model.meshes[0] = mesh;
730
731 model.materialCount = 1;
732 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
733 model.materials[0] = LoadMaterialDefault();
734
735 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
736 model.meshMaterial[0] = 0; // First material index
737
738 return model;
739}
740
741// Unload model from memory (RAM and/or VRAM)
742void UnloadModel(Model model)
743{
744 for (int i = 0; i < model.meshCount; i++) UnloadMesh(model.meshes[i]);
745
746 // As the user could be sharing shaders and textures between models,
747 // we don't unload the material but just free it's maps, the user
748 // is responsible for freeing models shaders and textures
749 for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
750
751 RL_FREE(model.meshes);
752 RL_FREE(model.materials);
753 RL_FREE(model.meshMaterial);
754
755 // Unload animation data
756 RL_FREE(model.bones);
757 RL_FREE(model.bindPose);
758
759 TRACELOG(LOG_INFO, "MODEL: Unloaded model from RAM and VRAM");
760}
761
762// Load meshes from model file
763Mesh *LoadMeshes(const char *fileName, int *meshCount)
764{
765 Mesh *meshes = NULL;
766 int count = 0;
767
768 // TODO: Load meshes from file (OBJ, IQM, GLTF)
769
770 *meshCount = count;
771 return meshes;
772}
773
774// Unload mesh from memory (RAM and/or VRAM)
775void UnloadMesh(Mesh mesh)
776{
777 rlUnloadMesh(mesh);
778 RL_FREE(mesh.vboId);
779}
780
781// Export mesh data to file
782void ExportMesh(Mesh mesh, const char *fileName)
783{
784 bool success = false;
785
786 if (IsFileExtension(fileName, ".obj"))
787 {
788 FILE *objFile = fopen(fileName, "wt");
789
790 fprintf(objFile, "# //////////////////////////////////////////////////////////////////////////////////\n");
791 fprintf(objFile, "# // //\n");
792 fprintf(objFile, "# // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized //\n");
793 fprintf(objFile, "# // //\n");
794 fprintf(objFile, "# // more info and bugs-report: github.com/raysan5/raylib //\n");
795 fprintf(objFile, "# // feedback and support: ray[at]raylib.com //\n");
796 fprintf(objFile, "# // //\n");
797 fprintf(objFile, "# // Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
798 fprintf(objFile, "# // //\n");
799 fprintf(objFile, "# //////////////////////////////////////////////////////////////////////////////////\n\n");
800 fprintf(objFile, "# Vertex Count: %i\n", mesh.vertexCount);
801 fprintf(objFile, "# Triangle Count: %i\n\n", mesh.triangleCount);
802
803 fprintf(objFile, "g mesh\n");
804
805 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
806 {
807 fprintf(objFile, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]);
808 }
809
810 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2)
811 {
812 fprintf(objFile, "vt %.2f %.2f\n", mesh.texcoords[v], mesh.texcoords[v + 1]);
813 }
814
815 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
816 {
817 fprintf(objFile, "vn %.2f %.2f %.2f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
818 }
819
820 for (int i = 0; i < mesh.triangleCount; i += 3)
821 {
822 fprintf(objFile, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", i, i, i, i + 1, i + 1, i + 1, i + 2, i + 2, i + 2);
823 }
824
825 fprintf(objFile, "\n");
826
827 fclose(objFile);
828
829 success = true;
830 }
831 else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export mesh vertex data
832
833 if (success) TRACELOG(LOG_INFO, "FILEIO: [%s] Mesh exported successfully", fileName);
834 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export mesh data", fileName);
835}
836
837// Load materials from model file
838Material *LoadMaterials(const char *fileName, int *materialCount)
839{
840 Material *materials = NULL;
841 unsigned int count = 0;
842
843 // TODO: Support IQM and GLTF for materials parsing
844
845#if defined(SUPPORT_FILEFORMAT_MTL)
846 if (IsFileExtension(fileName, ".mtl"))
847 {
848 tinyobj_material_t *mats;
849
850 int result = tinyobj_parse_mtl_file(&mats, &count, fileName);
851 if (result != TINYOBJ_SUCCESS) {
852 TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName);
853 }
854
855 // TODO: Process materials to return
856
857 tinyobj_materials_free(mats, count);
858 }
859#else
860 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load material file", fileName);
861#endif
862
863 // Set materials shader to default (DIFFUSE, SPECULAR, NORMAL)
864 for (int i = 0; i < count; i++) materials[i].shader = GetShaderDefault();
865
866 *materialCount = count;
867 return materials;
868}
869
870// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
871Material LoadMaterialDefault(void)
872{
873 Material material = { 0 };
874 material.maps = (MaterialMap *)RL_CALLOC(MAX_MATERIAL_MAPS, sizeof(MaterialMap));
875
876 material.shader = GetShaderDefault();
877 material.maps[MAP_DIFFUSE].texture = GetTextureDefault(); // White texture (1x1 pixel)
878 //material.maps[MAP_NORMAL].texture; // NOTE: By default, not set
879 //material.maps[MAP_SPECULAR].texture; // NOTE: By default, not set
880
881 material.maps[MAP_DIFFUSE].color = WHITE; // Diffuse color
882 material.maps[MAP_SPECULAR].color = WHITE; // Specular color
883
884 return material;
885}
886
887// Unload material from memory
888void UnloadMaterial(Material material)
889{
890 // Unload material shader (avoid unloading default shader, managed by raylib)
891 if (material.shader.id != GetShaderDefault().id) UnloadShader(material.shader);
892
893 // Unload loaded texture maps (avoid unloading default texture, managed by raylib)
894 for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
895 {
896 if (material.maps[i].texture.id != GetTextureDefault().id) rlDeleteTextures(material.maps[i].texture.id);
897 }
898
899 RL_FREE(material.maps);
900}
901
902// Set texture for a material map type (MAP_DIFFUSE, MAP_SPECULAR...)
903// NOTE: Previous texture should be manually unloaded
904void SetMaterialTexture(Material *material, int mapType, Texture2D texture)
905{
906 material->maps[mapType].texture = texture;
907}
908
909// Set the material for a mesh
910void SetModelMeshMaterial(Model *model, int meshId, int materialId)
911{
912 if (meshId >= model->meshCount) TRACELOG(LOG_WARNING, "MESH: Id greater than mesh count");
913 else if (materialId >= model->materialCount) TRACELOG(LOG_WARNING, "MATERIAL: Id greater than material count");
914 else model->meshMaterial[meshId] = materialId;
915}
916
917// Load model animations from file
918ModelAnimation *LoadModelAnimations(const char *filename, int *animCount)
919{
920 #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
921 #define IQM_VERSION 2 // only IQM version 2 supported
922
923 typedef struct IQMHeader {
924 char magic[16];
925 unsigned int version;
926 unsigned int filesize;
927 unsigned int flags;
928 unsigned int num_text, ofs_text;
929 unsigned int num_meshes, ofs_meshes;
930 unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
931 unsigned int num_triangles, ofs_triangles, ofs_adjacency;
932 unsigned int num_joints, ofs_joints;
933 unsigned int num_poses, ofs_poses;
934 unsigned int num_anims, ofs_anims;
935 unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
936 unsigned int num_comment, ofs_comment;
937 unsigned int num_extensions, ofs_extensions;
938 } IQMHeader;
939
940 typedef struct IQMPose {
941 int parent;
942 unsigned int mask;
943 float channeloffset[10];
944 float channelscale[10];
945 } IQMPose;
946
947 typedef struct IQMAnim {
948 unsigned int name;
949 unsigned int first_frame, num_frames;
950 float framerate;
951 unsigned int flags;
952 } IQMAnim;
953
954 FILE *iqmFile = NULL;
955 IQMHeader iqm;
956
957 iqmFile = fopen(filename,"rb");
958
959 if (!iqmFile)
960 {
961 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", filename);
962 return NULL;
963 }
964
965 // Read IQM header
966 fread(&iqm, sizeof(IQMHeader), 1, iqmFile);
967
968 if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC)))
969 {
970 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", filename);
971 fclose(iqmFile);
972 return NULL;
973 }
974
975 if (iqm.version != IQM_VERSION)
976 {
977 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version incorrect", filename);
978 fclose(iqmFile);
979 return NULL;
980 }
981
982 // Get bones data
983 IQMPose *poses = RL_MALLOC(iqm.num_poses*sizeof(IQMPose));
984 fseek(iqmFile, iqm.ofs_poses, SEEK_SET);
985 fread(poses, iqm.num_poses*sizeof(IQMPose), 1, iqmFile);
986
987 // Get animations data
988 *animCount = iqm.num_anims;
989 IQMAnim *anim = RL_MALLOC(iqm.num_anims*sizeof(IQMAnim));
990 fseek(iqmFile, iqm.ofs_anims, SEEK_SET);
991 fread(anim, iqm.num_anims*sizeof(IQMAnim), 1, iqmFile);
992 ModelAnimation *animations = RL_MALLOC(iqm.num_anims*sizeof(ModelAnimation));
993
994 // frameposes
995 unsigned short *framedata = RL_MALLOC(iqm.num_frames*iqm.num_framechannels*sizeof(unsigned short));
996 fseek(iqmFile, iqm.ofs_frames, SEEK_SET);
997 fread(framedata, iqm.num_frames*iqm.num_framechannels*sizeof(unsigned short), 1, iqmFile);
998
999 for (int a = 0; a < iqm.num_anims; a++)
1000 {
1001 animations[a].frameCount = anim[a].num_frames;
1002 animations[a].boneCount = iqm.num_poses;
1003 animations[a].bones = RL_MALLOC(iqm.num_poses*sizeof(BoneInfo));
1004 animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
1005 //animations[a].framerate = anim.framerate; // TODO: Use framerate?
1006
1007 for (int j = 0; j < iqm.num_poses; j++)
1008 {
1009 strcpy(animations[a].bones[j].name, "ANIMJOINTNAME");
1010 animations[a].bones[j].parent = poses[j].parent;
1011 }
1012
1013 for (int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqm.num_poses*sizeof(Transform));
1014
1015 int dcounter = anim[a].first_frame*iqm.num_framechannels;
1016
1017 for (int frame = 0; frame < anim[a].num_frames; frame++)
1018 {
1019 for (int i = 0; i < iqm.num_poses; i++)
1020 {
1021 animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0];
1022
1023 if (poses[i].mask & 0x01)
1024 {
1025 animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0];
1026 dcounter++;
1027 }
1028
1029 animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1];
1030
1031 if (poses[i].mask & 0x02)
1032 {
1033 animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1];
1034 dcounter++;
1035 }
1036
1037 animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2];
1038
1039 if (poses[i].mask & 0x04)
1040 {
1041 animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2];
1042 dcounter++;
1043 }
1044
1045 animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3];
1046
1047 if (poses[i].mask & 0x08)
1048 {
1049 animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3];
1050 dcounter++;
1051 }
1052
1053 animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4];
1054
1055 if (poses[i].mask & 0x10)
1056 {
1057 animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4];
1058 dcounter++;
1059 }
1060
1061 animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5];
1062
1063 if (poses[i].mask & 0x20)
1064 {
1065 animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5];
1066 dcounter++;
1067 }
1068
1069 animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6];
1070
1071 if (poses[i].mask & 0x40)
1072 {
1073 animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6];
1074 dcounter++;
1075 }
1076
1077 animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7];
1078
1079 if (poses[i].mask & 0x80)
1080 {
1081 animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7];
1082 dcounter++;
1083 }
1084
1085 animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8];
1086
1087 if (poses[i].mask & 0x100)
1088 {
1089 animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8];
1090 dcounter++;
1091 }
1092
1093 animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9];
1094
1095 if (poses[i].mask & 0x200)
1096 {
1097 animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9];
1098 dcounter++;
1099 }
1100
1101 animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation);
1102 }
1103 }
1104
1105 // Build frameposes
1106 for (int frame = 0; frame < anim[a].num_frames; frame++)
1107 {
1108 for (int i = 0; i < animations[a].boneCount; i++)
1109 {
1110 if (animations[a].bones[i].parent >= 0)
1111 {
1112 animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation);
1113 animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation);
1114 animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation);
1115 animations[a].framePoses[frame][i].scale = Vector3Multiply(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale);
1116 }
1117 }
1118 }
1119 }
1120
1121 RL_FREE(framedata);
1122 RL_FREE(poses);
1123 RL_FREE(anim);
1124
1125 fclose(iqmFile);
1126
1127 return animations;
1128}
1129
1130// Update model animated vertex data (positions and normals) for a given frame
1131// NOTE: Updated data is uploaded to GPU
1132void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
1133{
1134 if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
1135 {
1136 if (frame >= anim.frameCount) frame = frame%anim.frameCount;
1137
1138 for (int m = 0; m < model.meshCount; m++)
1139 {
1140 Vector3 animVertex = { 0 };
1141 Vector3 animNormal = { 0 };
1142
1143 Vector3 inTranslation = { 0 };
1144 Quaternion inRotation = { 0 };
1145 //Vector3 inScale = { 0 }; // Not used...
1146
1147 Vector3 outTranslation = { 0 };
1148 Quaternion outRotation = { 0 };
1149 Vector3 outScale = { 0 };
1150
1151 int vCounter = 0;
1152 int boneCounter = 0;
1153 int boneId = 0;
1154
1155 for (int i = 0; i < model.meshes[m].vertexCount; i++)
1156 {
1157 boneId = model.meshes[m].boneIds[boneCounter];
1158 inTranslation = model.bindPose[boneId].translation;
1159 inRotation = model.bindPose[boneId].rotation;
1160 //inScale = model.bindPose[boneId].scale;
1161 outTranslation = anim.framePoses[frame][boneId].translation;
1162 outRotation = anim.framePoses[frame][boneId].rotation;
1163 outScale = anim.framePoses[frame][boneId].scale;
1164
1165 // Vertices processing
1166 // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position)
1167 animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] };
1168 animVertex = Vector3Multiply(animVertex, outScale);
1169 animVertex = Vector3Subtract(animVertex, inTranslation);
1170 animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
1171 animVertex = Vector3Add(animVertex, outTranslation);
1172 model.meshes[m].animVertices[vCounter] = animVertex.x;
1173 model.meshes[m].animVertices[vCounter + 1] = animVertex.y;
1174 model.meshes[m].animVertices[vCounter + 2] = animVertex.z;
1175
1176 // Normals processing
1177 // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
1178 animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] };
1179 animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
1180 model.meshes[m].animNormals[vCounter] = animNormal.x;
1181 model.meshes[m].animNormals[vCounter + 1] = animNormal.y;
1182 model.meshes[m].animNormals[vCounter + 2] = animNormal.z;
1183 vCounter += 3;
1184
1185 boneCounter += 4;
1186 }
1187
1188 // Upload new vertex data to GPU for model drawing
1189 rlUpdateBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex position
1190 rlUpdateBuffer(model.meshes[m].vboId[2], model.meshes[m].animNormals, model.meshes[m].vertexCount*3*sizeof(float)); // Update vertex normals
1191 }
1192 }
1193}
1194
1195// Unload animation data
1196void UnloadModelAnimation(ModelAnimation anim)
1197{
1198 for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]);
1199
1200 RL_FREE(anim.bones);
1201 RL_FREE(anim.framePoses);
1202}
1203
1204// Check model animation skeleton match
1205// NOTE: Only number of bones and parent connections are checked
1206bool IsModelAnimationValid(Model model, ModelAnimation anim)
1207{
1208 int result = true;
1209
1210 if (model.boneCount != anim.boneCount) result = false;
1211 else
1212 {
1213 for (int i = 0; i < model.boneCount; i++)
1214 {
1215 if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; }
1216 }
1217 }
1218
1219 return result;
1220}
1221
1222#if defined(SUPPORT_MESH_GENERATION)
1223// Generate polygonal mesh
1224Mesh GenMeshPoly(int sides, float radius)
1225{
1226 Mesh mesh = { 0 };
1227 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1228 int vertexCount = sides*3;
1229
1230 // Vertices definition
1231 Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1232 for (int i = 0, v = 0; i < 360; i += 360/sides, v += 3)
1233 {
1234 vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f };
1235 vertices[v + 1] = (Vector3){ sinf(DEG2RAD*i)*radius, 0.0f, cosf(DEG2RAD*i)*radius };
1236 vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(i + 360/sides))*radius, 0.0f, cosf(DEG2RAD*(i + 360/sides))*radius };
1237 }
1238
1239 // Normals definition
1240 Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1241 for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up;
1242
1243 // TexCoords definition
1244 Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
1245 for (int n = 0; n < vertexCount; n++) texcoords[n] = (Vector2){ 0.0f, 0.0f };
1246
1247 mesh.vertexCount = vertexCount;
1248 mesh.triangleCount = sides;
1249 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1250 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
1251 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1252
1253 // Mesh vertices position array
1254 for (int i = 0; i < mesh.vertexCount; i++)
1255 {
1256 mesh.vertices[3*i] = vertices[i].x;
1257 mesh.vertices[3*i + 1] = vertices[i].y;
1258 mesh.vertices[3*i + 2] = vertices[i].z;
1259 }
1260
1261 // Mesh texcoords array
1262 for (int i = 0; i < mesh.vertexCount; i++)
1263 {
1264 mesh.texcoords[2*i] = texcoords[i].x;
1265 mesh.texcoords[2*i + 1] = texcoords[i].y;
1266 }
1267
1268 // Mesh normals array
1269 for (int i = 0; i < mesh.vertexCount; i++)
1270 {
1271 mesh.normals[3*i] = normals[i].x;
1272 mesh.normals[3*i + 1] = normals[i].y;
1273 mesh.normals[3*i + 2] = normals[i].z;
1274 }
1275
1276 RL_FREE(vertices);
1277 RL_FREE(normals);
1278 RL_FREE(texcoords);
1279
1280 // Upload vertex data to GPU (static mesh)
1281 rlLoadMesh(&mesh, false);
1282
1283 return mesh;
1284}
1285
1286// Generate plane mesh (with subdivisions)
1287Mesh GenMeshPlane(float width, float length, int resX, int resZ)
1288{
1289 Mesh mesh = { 0 };
1290 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1291
1292#define CUSTOM_MESH_GEN_PLANE
1293#if defined(CUSTOM_MESH_GEN_PLANE)
1294 resX++;
1295 resZ++;
1296
1297 // Vertices definition
1298 int vertexCount = resX*resZ; // vertices get reused for the faces
1299
1300 Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1301 for (int z = 0; z < resZ; z++)
1302 {
1303 // [-length/2, length/2]
1304 float zPos = ((float)z/(resZ - 1) - 0.5f)*length;
1305 for (int x = 0; x < resX; x++)
1306 {
1307 // [-width/2, width/2]
1308 float xPos = ((float)x/(resX - 1) - 0.5f)*width;
1309 vertices[x + z*resX] = (Vector3){ xPos, 0.0f, zPos };
1310 }
1311 }
1312
1313 // Normals definition
1314 Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1315 for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up;
1316
1317 // TexCoords definition
1318 Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
1319 for (int v = 0; v < resZ; v++)
1320 {
1321 for (int u = 0; u < resX; u++)
1322 {
1323 texcoords[u + v*resX] = (Vector2){ (float)u/(resX - 1), (float)v/(resZ - 1) };
1324 }
1325 }
1326
1327 // Triangles definition (indices)
1328 int numFaces = (resX - 1)*(resZ - 1);
1329 int *triangles = (int *)RL_MALLOC(numFaces*6*sizeof(int));
1330 int t = 0;
1331 for (int face = 0; face < numFaces; face++)
1332 {
1333 // Retrieve lower left corner from face ind
1334 int i = face % (resX - 1) + (face/(resZ - 1)*resX);
1335
1336 triangles[t++] = i + resX;
1337 triangles[t++] = i + 1;
1338 triangles[t++] = i;
1339
1340 triangles[t++] = i + resX;
1341 triangles[t++] = i + resX + 1;
1342 triangles[t++] = i + 1;
1343 }
1344
1345 mesh.vertexCount = vertexCount;
1346 mesh.triangleCount = numFaces*2;
1347 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1348 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
1349 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1350 mesh.indices = (unsigned short *)RL_MALLOC(mesh.triangleCount*3*sizeof(unsigned short));
1351
1352 // Mesh vertices position array
1353 for (int i = 0; i < mesh.vertexCount; i++)
1354 {
1355 mesh.vertices[3*i] = vertices[i].x;
1356 mesh.vertices[3*i + 1] = vertices[i].y;
1357 mesh.vertices[3*i + 2] = vertices[i].z;
1358 }
1359
1360 // Mesh texcoords array
1361 for (int i = 0; i < mesh.vertexCount; i++)
1362 {
1363 mesh.texcoords[2*i] = texcoords[i].x;
1364 mesh.texcoords[2*i + 1] = texcoords[i].y;
1365 }
1366
1367 // Mesh normals array
1368 for (int i = 0; i < mesh.vertexCount; i++)
1369 {
1370 mesh.normals[3*i] = normals[i].x;
1371 mesh.normals[3*i + 1] = normals[i].y;
1372 mesh.normals[3*i + 2] = normals[i].z;
1373 }
1374
1375 // Mesh indices array initialization
1376 for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i];
1377
1378 RL_FREE(vertices);
1379 RL_FREE(normals);
1380 RL_FREE(texcoords);
1381 RL_FREE(triangles);
1382
1383#else // Use par_shapes library to generate plane mesh
1384
1385 par_shapes_mesh *plane = par_shapes_create_plane(resX, resZ); // No normals/texcoords generated!!!
1386 par_shapes_scale(plane, width, length, 1.0f);
1387 par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 });
1388 par_shapes_translate(plane, -width/2, 0.0f, length/2);
1389
1390 mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
1391 mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float));
1392 mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
1393 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1394
1395 mesh.vertexCount = plane->ntriangles*3;
1396 mesh.triangleCount = plane->ntriangles;
1397
1398 for (int k = 0; k < mesh.vertexCount; k++)
1399 {
1400 mesh.vertices[k*3] = plane->points[plane->triangles[k]*3];
1401 mesh.vertices[k*3 + 1] = plane->points[plane->triangles[k]*3 + 1];
1402 mesh.vertices[k*3 + 2] = plane->points[plane->triangles[k]*3 + 2];
1403
1404 mesh.normals[k*3] = plane->normals[plane->triangles[k]*3];
1405 mesh.normals[k*3 + 1] = plane->normals[plane->triangles[k]*3 + 1];
1406 mesh.normals[k*3 + 2] = plane->normals[plane->triangles[k]*3 + 2];
1407
1408 mesh.texcoords[k*2] = plane->tcoords[plane->triangles[k]*2];
1409 mesh.texcoords[k*2 + 1] = plane->tcoords[plane->triangles[k]*2 + 1];
1410 }
1411
1412 par_shapes_free_mesh(plane);
1413#endif
1414
1415 // Upload vertex data to GPU (static mesh)
1416 rlLoadMesh(&mesh, false);
1417
1418 return mesh;
1419}
1420
1421// Generated cuboid mesh
1422Mesh GenMeshCube(float width, float height, float length)
1423{
1424 Mesh mesh = { 0 };
1425 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1426
1427#define CUSTOM_MESH_GEN_CUBE
1428#if defined(CUSTOM_MESH_GEN_CUBE)
1429 float vertices[] = {
1430 -width/2, -height/2, length/2,
1431 width/2, -height/2, length/2,
1432 width/2, height/2, length/2,
1433 -width/2, height/2, length/2,
1434 -width/2, -height/2, -length/2,
1435 -width/2, height/2, -length/2,
1436 width/2, height/2, -length/2,
1437 width/2, -height/2, -length/2,
1438 -width/2, height/2, -length/2,
1439 -width/2, height/2, length/2,
1440 width/2, height/2, length/2,
1441 width/2, height/2, -length/2,
1442 -width/2, -height/2, -length/2,
1443 width/2, -height/2, -length/2,
1444 width/2, -height/2, length/2,
1445 -width/2, -height/2, length/2,
1446 width/2, -height/2, -length/2,
1447 width/2, height/2, -length/2,
1448 width/2, height/2, length/2,
1449 width/2, -height/2, length/2,
1450 -width/2, -height/2, -length/2,
1451 -width/2, -height/2, length/2,
1452 -width/2, height/2, length/2,
1453 -width/2, height/2, -length/2
1454 };
1455
1456 float texcoords[] = {
1457 0.0f, 0.0f,
1458 1.0f, 0.0f,
1459 1.0f, 1.0f,
1460 0.0f, 1.0f,
1461 1.0f, 0.0f,
1462 1.0f, 1.0f,
1463 0.0f, 1.0f,
1464 0.0f, 0.0f,
1465 0.0f, 1.0f,
1466 0.0f, 0.0f,
1467 1.0f, 0.0f,
1468 1.0f, 1.0f,
1469 1.0f, 1.0f,
1470 0.0f, 1.0f,
1471 0.0f, 0.0f,
1472 1.0f, 0.0f,
1473 1.0f, 0.0f,
1474 1.0f, 1.0f,
1475 0.0f, 1.0f,
1476 0.0f, 0.0f,
1477 0.0f, 0.0f,
1478 1.0f, 0.0f,
1479 1.0f, 1.0f,
1480 0.0f, 1.0f
1481 };
1482
1483 float normals[] = {
1484 0.0f, 0.0f, 1.0f,
1485 0.0f, 0.0f, 1.0f,
1486 0.0f, 0.0f, 1.0f,
1487 0.0f, 0.0f, 1.0f,
1488 0.0f, 0.0f,-1.0f,
1489 0.0f, 0.0f,-1.0f,
1490 0.0f, 0.0f,-1.0f,
1491 0.0f, 0.0f,-1.0f,
1492 0.0f, 1.0f, 0.0f,
1493 0.0f, 1.0f, 0.0f,
1494 0.0f, 1.0f, 0.0f,
1495 0.0f, 1.0f, 0.0f,
1496 0.0f,-1.0f, 0.0f,
1497 0.0f,-1.0f, 0.0f,
1498 0.0f,-1.0f, 0.0f,
1499 0.0f,-1.0f, 0.0f,
1500 1.0f, 0.0f, 0.0f,
1501 1.0f, 0.0f, 0.0f,
1502 1.0f, 0.0f, 0.0f,
1503 1.0f, 0.0f, 0.0f,
1504 -1.0f, 0.0f, 0.0f,
1505 -1.0f, 0.0f, 0.0f,
1506 -1.0f, 0.0f, 0.0f,
1507 -1.0f, 0.0f, 0.0f
1508 };
1509
1510 mesh.vertices = (float *)RL_MALLOC(24*3*sizeof(float));
1511 memcpy(mesh.vertices, vertices, 24*3*sizeof(float));
1512
1513 mesh.texcoords = (float *)RL_MALLOC(24*2*sizeof(float));
1514 memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float));
1515
1516 mesh.normals = (float *)RL_MALLOC(24*3*sizeof(float));
1517 memcpy(mesh.normals, normals, 24*3*sizeof(float));
1518
1519 mesh.indices = (unsigned short *)RL_MALLOC(36*sizeof(unsigned short));
1520
1521 int k = 0;
1522
1523 // Indices can be initialized right now
1524 for (int i = 0; i < 36; i+=6)
1525 {
1526 mesh.indices[i] = 4*k;
1527 mesh.indices[i+1] = 4*k+1;
1528 mesh.indices[i+2] = 4*k+2;
1529 mesh.indices[i+3] = 4*k;
1530 mesh.indices[i+4] = 4*k+2;
1531 mesh.indices[i+5] = 4*k+3;
1532
1533 k++;
1534 }
1535
1536 mesh.vertexCount = 24;
1537 mesh.triangleCount = 12;
1538
1539#else // Use par_shapes library to generate cube mesh
1540/*
1541// Platonic solids:
1542par_shapes_mesh* par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid)
1543par_shapes_mesh* par_shapes_create_cube(); // 6 sides polyhedron (cube)
1544par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (dyamond)
1545par_shapes_mesh* par_shapes_create_dodecahedron(); // 12 sides polyhedron
1546par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron
1547*/
1548 // Platonic solid generation: cube (6 sides)
1549 // NOTE: No normals/texcoords generated by default
1550 par_shapes_mesh *cube = par_shapes_create_cube();
1551 cube->tcoords = PAR_MALLOC(float, 2*cube->npoints);
1552 for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f;
1553 par_shapes_scale(cube, width, height, length);
1554 par_shapes_translate(cube, -width/2, 0.0f, -length/2);
1555 par_shapes_compute_normals(cube);
1556
1557 mesh.vertices = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
1558 mesh.texcoords = (float *)RL_MALLOC(cube->ntriangles*3*2*sizeof(float));
1559 mesh.normals = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
1560
1561 mesh.vertexCount = cube->ntriangles*3;
1562 mesh.triangleCount = cube->ntriangles;
1563
1564 for (int k = 0; k < mesh.vertexCount; k++)
1565 {
1566 mesh.vertices[k*3] = cube->points[cube->triangles[k]*3];
1567 mesh.vertices[k*3 + 1] = cube->points[cube->triangles[k]*3 + 1];
1568 mesh.vertices[k*3 + 2] = cube->points[cube->triangles[k]*3 + 2];
1569
1570 mesh.normals[k*3] = cube->normals[cube->triangles[k]*3];
1571 mesh.normals[k*3 + 1] = cube->normals[cube->triangles[k]*3 + 1];
1572 mesh.normals[k*3 + 2] = cube->normals[cube->triangles[k]*3 + 2];
1573
1574 mesh.texcoords[k*2] = cube->tcoords[cube->triangles[k]*2];
1575 mesh.texcoords[k*2 + 1] = cube->tcoords[cube->triangles[k]*2 + 1];
1576 }
1577
1578 par_shapes_free_mesh(cube);
1579#endif
1580
1581 // Upload vertex data to GPU (static mesh)
1582 rlLoadMesh(&mesh, false);
1583
1584 return mesh;
1585}
1586
1587// Generate sphere mesh (standard sphere)
1588RLAPI Mesh GenMeshSphere(float radius, int rings, int slices)
1589{
1590 Mesh mesh = { 0 };
1591 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1592
1593 par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings);
1594 par_shapes_scale(sphere, radius, radius, radius);
1595 // NOTE: Soft normals are computed internally
1596
1597 mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1598 mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
1599 mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1600
1601 mesh.vertexCount = sphere->ntriangles*3;
1602 mesh.triangleCount = sphere->ntriangles;
1603
1604 for (int k = 0; k < mesh.vertexCount; k++)
1605 {
1606 mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
1607 mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
1608 mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
1609
1610 mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
1611 mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
1612 mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
1613
1614 mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
1615 mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
1616 }
1617
1618 par_shapes_free_mesh(sphere);
1619
1620 // Upload vertex data to GPU (static mesh)
1621 rlLoadMesh(&mesh, false);
1622
1623 return mesh;
1624}
1625
1626// Generate hemi-sphere mesh (half sphere, no bottom cap)
1627RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices)
1628{
1629 Mesh mesh = { 0 };
1630 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1631
1632 par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings);
1633 par_shapes_scale(sphere, radius, radius, radius);
1634 // NOTE: Soft normals are computed internally
1635
1636 mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1637 mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
1638 mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1639
1640 mesh.vertexCount = sphere->ntriangles*3;
1641 mesh.triangleCount = sphere->ntriangles;
1642
1643 for (int k = 0; k < mesh.vertexCount; k++)
1644 {
1645 mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
1646 mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
1647 mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
1648
1649 mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
1650 mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
1651 mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
1652
1653 mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
1654 mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
1655 }
1656
1657 par_shapes_free_mesh(sphere);
1658
1659 // Upload vertex data to GPU (static mesh)
1660 rlLoadMesh(&mesh, false);
1661
1662 return mesh;
1663}
1664
1665// Generate cylinder mesh
1666Mesh GenMeshCylinder(float radius, float height, int slices)
1667{
1668 Mesh mesh = { 0 };
1669 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1670
1671 // Instance a cylinder that sits on the Z=0 plane using the given tessellation
1672 // levels across the UV domain. Think of "slices" like a number of pizza
1673 // slices, and "stacks" like a number of stacked rings.
1674 // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
1675 par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8);
1676 par_shapes_scale(cylinder, radius, radius, height);
1677 par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 });
1678 par_shapes_rotate(cylinder, PI/2.0f, (float[]){ 0, 1, 0 });
1679
1680 // Generate an orientable disk shape (top cap)
1681 par_shapes_mesh *capTop = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, 1 });
1682 capTop->tcoords = PAR_MALLOC(float, 2*capTop->npoints);
1683 for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f;
1684 par_shapes_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 });
1685 par_shapes_translate(capTop, 0, height, 0);
1686
1687 // Generate an orientable disk shape (bottom cap)
1688 par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
1689 capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
1690 for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
1691 par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
1692
1693 par_shapes_merge_and_free(cylinder, capTop);
1694 par_shapes_merge_and_free(cylinder, capBottom);
1695
1696 mesh.vertices = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
1697 mesh.texcoords = (float *)RL_MALLOC(cylinder->ntriangles*3*2*sizeof(float));
1698 mesh.normals = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
1699
1700 mesh.vertexCount = cylinder->ntriangles*3;
1701 mesh.triangleCount = cylinder->ntriangles;
1702
1703 for (int k = 0; k < mesh.vertexCount; k++)
1704 {
1705 mesh.vertices[k*3] = cylinder->points[cylinder->triangles[k]*3];
1706 mesh.vertices[k*3 + 1] = cylinder->points[cylinder->triangles[k]*3 + 1];
1707 mesh.vertices[k*3 + 2] = cylinder->points[cylinder->triangles[k]*3 + 2];
1708
1709 mesh.normals[k*3] = cylinder->normals[cylinder->triangles[k]*3];
1710 mesh.normals[k*3 + 1] = cylinder->normals[cylinder->triangles[k]*3 + 1];
1711 mesh.normals[k*3 + 2] = cylinder->normals[cylinder->triangles[k]*3 + 2];
1712
1713 mesh.texcoords[k*2] = cylinder->tcoords[cylinder->triangles[k]*2];
1714 mesh.texcoords[k*2 + 1] = cylinder->tcoords[cylinder->triangles[k]*2 + 1];
1715 }
1716
1717 par_shapes_free_mesh(cylinder);
1718
1719 // Upload vertex data to GPU (static mesh)
1720 rlLoadMesh(&mesh, false);
1721
1722 return mesh;
1723}
1724
1725// Generate torus mesh
1726Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
1727{
1728 Mesh mesh = { 0 };
1729 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1730
1731 if (radius > 1.0f) radius = 1.0f;
1732 else if (radius < 0.1f) radius = 0.1f;
1733
1734 // Create a donut that sits on the Z=0 plane with the specified inner radius
1735 // The outer radius can be controlled with par_shapes_scale
1736 par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius);
1737 par_shapes_scale(torus, size/2, size/2, size/2);
1738
1739 mesh.vertices = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
1740 mesh.texcoords = (float *)RL_MALLOC(torus->ntriangles*3*2*sizeof(float));
1741 mesh.normals = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
1742
1743 mesh.vertexCount = torus->ntriangles*3;
1744 mesh.triangleCount = torus->ntriangles;
1745
1746 for (int k = 0; k < mesh.vertexCount; k++)
1747 {
1748 mesh.vertices[k*3] = torus->points[torus->triangles[k]*3];
1749 mesh.vertices[k*3 + 1] = torus->points[torus->triangles[k]*3 + 1];
1750 mesh.vertices[k*3 + 2] = torus->points[torus->triangles[k]*3 + 2];
1751
1752 mesh.normals[k*3] = torus->normals[torus->triangles[k]*3];
1753 mesh.normals[k*3 + 1] = torus->normals[torus->triangles[k]*3 + 1];
1754 mesh.normals[k*3 + 2] = torus->normals[torus->triangles[k]*3 + 2];
1755
1756 mesh.texcoords[k*2] = torus->tcoords[torus->triangles[k]*2];
1757 mesh.texcoords[k*2 + 1] = torus->tcoords[torus->triangles[k]*2 + 1];
1758 }
1759
1760 par_shapes_free_mesh(torus);
1761
1762 // Upload vertex data to GPU (static mesh)
1763 rlLoadMesh(&mesh, false);
1764
1765 return mesh;
1766}
1767
1768// Generate trefoil knot mesh
1769Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
1770{
1771 Mesh mesh = { 0 };
1772 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1773
1774 if (radius > 3.0f) radius = 3.0f;
1775 else if (radius < 0.5f) radius = 0.5f;
1776
1777 par_shapes_mesh *knot = par_shapes_create_trefoil_knot(radSeg, sides, radius);
1778 par_shapes_scale(knot, size, size, size);
1779
1780 mesh.vertices = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
1781 mesh.texcoords = (float *)RL_MALLOC(knot->ntriangles*3*2*sizeof(float));
1782 mesh.normals = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
1783
1784 mesh.vertexCount = knot->ntriangles*3;
1785 mesh.triangleCount = knot->ntriangles;
1786
1787 for (int k = 0; k < mesh.vertexCount; k++)
1788 {
1789 mesh.vertices[k*3] = knot->points[knot->triangles[k]*3];
1790 mesh.vertices[k*3 + 1] = knot->points[knot->triangles[k]*3 + 1];
1791 mesh.vertices[k*3 + 2] = knot->points[knot->triangles[k]*3 + 2];
1792
1793 mesh.normals[k*3] = knot->normals[knot->triangles[k]*3];
1794 mesh.normals[k*3 + 1] = knot->normals[knot->triangles[k]*3 + 1];
1795 mesh.normals[k*3 + 2] = knot->normals[knot->triangles[k]*3 + 2];
1796
1797 mesh.texcoords[k*2] = knot->tcoords[knot->triangles[k]*2];
1798 mesh.texcoords[k*2 + 1] = knot->tcoords[knot->triangles[k]*2 + 1];
1799 }
1800
1801 par_shapes_free_mesh(knot);
1802
1803 // Upload vertex data to GPU (static mesh)
1804 rlLoadMesh(&mesh, false);
1805
1806 return mesh;
1807}
1808
1809// Generate a mesh from heightmap
1810// NOTE: Vertex data is uploaded to GPU
1811Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
1812{
1813 #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3)
1814
1815 Mesh mesh = { 0 };
1816 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1817
1818 int mapX = heightmap.width;
1819 int mapZ = heightmap.height;
1820
1821 Color *pixels = GetImageData(heightmap);
1822
1823 // NOTE: One vertex per pixel
1824 mesh.triangleCount = (mapX-1)*(mapZ-1)*2; // One quad every four pixels
1825
1826 mesh.vertexCount = mesh.triangleCount*3;
1827
1828 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1829 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1830 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
1831 mesh.colors = NULL;
1832
1833 int vCounter = 0; // Used to count vertices float by float
1834 int tcCounter = 0; // Used to count texcoords float by float
1835 int nCounter = 0; // Used to count normals float by float
1836
1837 int trisCounter = 0;
1838
1839 Vector3 scaleFactor = { size.x/mapX, size.y/255.0f, size.z/mapZ };
1840
1841 Vector3 vA;
1842 Vector3 vB;
1843 Vector3 vC;
1844 Vector3 vN;
1845
1846 for (int z = 0; z < mapZ-1; z++)
1847 {
1848 for (int x = 0; x < mapX-1; x++)
1849 {
1850 // Fill vertices array with data
1851 //----------------------------------------------------------
1852
1853 // one triangle - 3 vertex
1854 mesh.vertices[vCounter] = (float)x*scaleFactor.x;
1855 mesh.vertices[vCounter + 1] = (float)GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y;
1856 mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z;
1857
1858 mesh.vertices[vCounter + 3] = (float)x*scaleFactor.x;
1859 mesh.vertices[vCounter + 4] = (float)GRAY_VALUE(pixels[x + (z + 1)*mapX])*scaleFactor.y;
1860 mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z;
1861
1862 mesh.vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x;
1863 mesh.vertices[vCounter + 7] = (float)GRAY_VALUE(pixels[(x + 1) + z*mapX])*scaleFactor.y;
1864 mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z;
1865
1866 // another triangle - 3 vertex
1867 mesh.vertices[vCounter + 9] = mesh.vertices[vCounter + 6];
1868 mesh.vertices[vCounter + 10] = mesh.vertices[vCounter + 7];
1869 mesh.vertices[vCounter + 11] = mesh.vertices[vCounter + 8];
1870
1871 mesh.vertices[vCounter + 12] = mesh.vertices[vCounter + 3];
1872 mesh.vertices[vCounter + 13] = mesh.vertices[vCounter + 4];
1873 mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5];
1874
1875 mesh.vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x;
1876 mesh.vertices[vCounter + 16] = (float)GRAY_VALUE(pixels[(x + 1) + (z + 1)*mapX])*scaleFactor.y;
1877 mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z;
1878 vCounter += 18; // 6 vertex, 18 floats
1879
1880 // Fill texcoords array with data
1881 //--------------------------------------------------------------
1882 mesh.texcoords[tcCounter] = (float)x/(mapX - 1);
1883 mesh.texcoords[tcCounter + 1] = (float)z/(mapZ - 1);
1884
1885 mesh.texcoords[tcCounter + 2] = (float)x/(mapX - 1);
1886 mesh.texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1);
1887
1888 mesh.texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1);
1889 mesh.texcoords[tcCounter + 5] = (float)z/(mapZ - 1);
1890
1891 mesh.texcoords[tcCounter + 6] = mesh.texcoords[tcCounter + 4];
1892 mesh.texcoords[tcCounter + 7] = mesh.texcoords[tcCounter + 5];
1893
1894 mesh.texcoords[tcCounter + 8] = mesh.texcoords[tcCounter + 2];
1895 mesh.texcoords[tcCounter + 9] = mesh.texcoords[tcCounter + 3];
1896
1897 mesh.texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1);
1898 mesh.texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1);
1899 tcCounter += 12; // 6 texcoords, 12 floats
1900
1901 // Fill normals array with data
1902 //--------------------------------------------------------------
1903 for (int i = 0; i < 18; i += 9)
1904 {
1905 vA.x = mesh.vertices[nCounter + i];
1906 vA.y = mesh.vertices[nCounter + i + 1];
1907 vA.z = mesh.vertices[nCounter + i + 2];
1908
1909 vB.x = mesh.vertices[nCounter + i + 3];
1910 vB.y = mesh.vertices[nCounter + i + 4];
1911 vB.z = mesh.vertices[nCounter + i + 5];
1912
1913 vC.x = mesh.vertices[nCounter + i + 6];
1914 vC.y = mesh.vertices[nCounter + i + 7];
1915 vC.z = mesh.vertices[nCounter + i + 8];
1916
1917 vN = Vector3Normalize(Vector3CrossProduct(Vector3Subtract(vB, vA), Vector3Subtract(vC, vA)));
1918
1919 mesh.normals[nCounter + i] = vN.x;
1920 mesh.normals[nCounter + i + 1] = vN.y;
1921 mesh.normals[nCounter + i + 2] = vN.z;
1922
1923 mesh.normals[nCounter + i + 3] = vN.x;
1924 mesh.normals[nCounter + i + 4] = vN.y;
1925 mesh.normals[nCounter + i + 5] = vN.z;
1926
1927 mesh.normals[nCounter + i + 6] = vN.x;
1928 mesh.normals[nCounter + i + 7] = vN.y;
1929 mesh.normals[nCounter + i + 8] = vN.z;
1930 }
1931
1932 nCounter += 18; // 6 vertex, 18 floats
1933 trisCounter += 2;
1934 }
1935 }
1936
1937 RL_FREE(pixels);
1938
1939 // Upload vertex data to GPU (static mesh)
1940 rlLoadMesh(&mesh, false);
1941
1942 return mesh;
1943}
1944
1945// Generate a cubes mesh from pixel data
1946// NOTE: Vertex data is uploaded to GPU
1947Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
1948{
1949 Mesh mesh = { 0 };
1950 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
1951
1952 Color *cubicmapPixels = GetImageData(cubicmap);
1953
1954 int mapWidth = cubicmap.width;
1955 int mapHeight = cubicmap.height;
1956
1957 // NOTE: Max possible number of triangles numCubes*(12 triangles by cube)
1958 int maxTriangles = cubicmap.width*cubicmap.height*12;
1959
1960 int vCounter = 0; // Used to count vertices
1961 int tcCounter = 0; // Used to count texcoords
1962 int nCounter = 0; // Used to count normals
1963
1964 float w = cubeSize.x;
1965 float h = cubeSize.z;
1966 float h2 = cubeSize.y;
1967
1968 Vector3 *mapVertices = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
1969 Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2));
1970 Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
1971
1972 // Define the 6 normals of the cube, we will combine them accordingly later...
1973 Vector3 n1 = { 1.0f, 0.0f, 0.0f };
1974 Vector3 n2 = { -1.0f, 0.0f, 0.0f };
1975 Vector3 n3 = { 0.0f, 1.0f, 0.0f };
1976 Vector3 n4 = { 0.0f, -1.0f, 0.0f };
1977 Vector3 n5 = { 0.0f, 0.0f, 1.0f };
1978 Vector3 n6 = { 0.0f, 0.0f, -1.0f };
1979
1980 // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6)
1981 typedef struct RectangleF {
1982 float x;
1983 float y;
1984 float width;
1985 float height;
1986 } RectangleF;
1987
1988 RectangleF rightTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
1989 RectangleF leftTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
1990 RectangleF frontTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
1991 RectangleF backTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
1992 RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f };
1993 RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f };
1994
1995 for (int z = 0; z < mapHeight; ++z)
1996 {
1997 for (int x = 0; x < mapWidth; ++x)
1998 {
1999 // Define the 8 vertex of the cube, we will combine them accordingly later...
2000 Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
2001 Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) };
2002 Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) };
2003 Vector3 v4 = { w*(x + 0.5f), h2, h*(z - 0.5f) };
2004 Vector3 v5 = { w*(x + 0.5f), 0, h*(z - 0.5f) };
2005 Vector3 v6 = { w*(x - 0.5f), 0, h*(z - 0.5f) };
2006 Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) };
2007 Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) };
2008
2009 // We check pixel color to be WHITE, we will full cubes
2010 if ((cubicmapPixels[z*cubicmap.width + x].r == 255) &&
2011 (cubicmapPixels[z*cubicmap.width + x].g == 255) &&
2012 (cubicmapPixels[z*cubicmap.width + x].b == 255))
2013 {
2014 // Define triangles (Checking Collateral Cubes!)
2015 //----------------------------------------------
2016
2017 // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
2018 mapVertices[vCounter] = v1;
2019 mapVertices[vCounter + 1] = v2;
2020 mapVertices[vCounter + 2] = v3;
2021 mapVertices[vCounter + 3] = v1;
2022 mapVertices[vCounter + 4] = v3;
2023 mapVertices[vCounter + 5] = v4;
2024 vCounter += 6;
2025
2026 mapNormals[nCounter] = n3;
2027 mapNormals[nCounter + 1] = n3;
2028 mapNormals[nCounter + 2] = n3;
2029 mapNormals[nCounter + 3] = n3;
2030 mapNormals[nCounter + 4] = n3;
2031 mapNormals[nCounter + 5] = n3;
2032 nCounter += 6;
2033
2034 mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
2035 mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
2036 mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2037 mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
2038 mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2039 mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
2040 tcCounter += 6;
2041
2042 // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
2043 mapVertices[vCounter] = v6;
2044 mapVertices[vCounter + 1] = v8;
2045 mapVertices[vCounter + 2] = v7;
2046 mapVertices[vCounter + 3] = v6;
2047 mapVertices[vCounter + 4] = v5;
2048 mapVertices[vCounter + 5] = v8;
2049 vCounter += 6;
2050
2051 mapNormals[nCounter] = n4;
2052 mapNormals[nCounter + 1] = n4;
2053 mapNormals[nCounter + 2] = n4;
2054 mapNormals[nCounter + 3] = n4;
2055 mapNormals[nCounter + 4] = n4;
2056 mapNormals[nCounter + 5] = n4;
2057 nCounter += 6;
2058
2059 mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2060 mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2061 mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
2062 mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2063 mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
2064 mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2065 tcCounter += 6;
2066
2067 if (((z < cubicmap.height - 1) &&
2068 (cubicmapPixels[(z + 1)*cubicmap.width + x].r == 0) &&
2069 (cubicmapPixels[(z + 1)*cubicmap.width + x].g == 0) &&
2070 (cubicmapPixels[(z + 1)*cubicmap.width + x].b == 0)) || (z == cubicmap.height - 1))
2071 {
2072 // Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8
2073 // NOTE: Collateral occluded faces are not generated
2074 mapVertices[vCounter] = v2;
2075 mapVertices[vCounter + 1] = v7;
2076 mapVertices[vCounter + 2] = v3;
2077 mapVertices[vCounter + 3] = v3;
2078 mapVertices[vCounter + 4] = v7;
2079 mapVertices[vCounter + 5] = v8;
2080 vCounter += 6;
2081
2082 mapNormals[nCounter] = n6;
2083 mapNormals[nCounter + 1] = n6;
2084 mapNormals[nCounter + 2] = n6;
2085 mapNormals[nCounter + 3] = n6;
2086 mapNormals[nCounter + 4] = n6;
2087 mapNormals[nCounter + 5] = n6;
2088 nCounter += 6;
2089
2090 mapTexcoords[tcCounter] = (Vector2){ frontTexUV.x, frontTexUV.y };
2091 mapTexcoords[tcCounter + 1] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
2092 mapTexcoords[tcCounter + 2] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
2093 mapTexcoords[tcCounter + 3] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
2094 mapTexcoords[tcCounter + 4] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
2095 mapTexcoords[tcCounter + 5] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y + frontTexUV.height };
2096 tcCounter += 6;
2097 }
2098
2099 if (((z > 0) &&
2100 (cubicmapPixels[(z - 1)*cubicmap.width + x].r == 0) &&
2101 (cubicmapPixels[(z - 1)*cubicmap.width + x].g == 0) &&
2102 (cubicmapPixels[(z - 1)*cubicmap.width + x].b == 0)) || (z == 0))
2103 {
2104 // Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5
2105 // NOTE: Collateral occluded faces are not generated
2106 mapVertices[vCounter] = v1;
2107 mapVertices[vCounter + 1] = v5;
2108 mapVertices[vCounter + 2] = v6;
2109 mapVertices[vCounter + 3] = v1;
2110 mapVertices[vCounter + 4] = v4;
2111 mapVertices[vCounter + 5] = v5;
2112 vCounter += 6;
2113
2114 mapNormals[nCounter] = n5;
2115 mapNormals[nCounter + 1] = n5;
2116 mapNormals[nCounter + 2] = n5;
2117 mapNormals[nCounter + 3] = n5;
2118 mapNormals[nCounter + 4] = n5;
2119 mapNormals[nCounter + 5] = n5;
2120 nCounter += 6;
2121
2122 mapTexcoords[tcCounter] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
2123 mapTexcoords[tcCounter + 1] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
2124 mapTexcoords[tcCounter + 2] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y + backTexUV.height };
2125 mapTexcoords[tcCounter + 3] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
2126 mapTexcoords[tcCounter + 4] = (Vector2){ backTexUV.x, backTexUV.y };
2127 mapTexcoords[tcCounter + 5] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
2128 tcCounter += 6;
2129 }
2130
2131 if (((x < cubicmap.width - 1) &&
2132 (cubicmapPixels[z*cubicmap.width + (x + 1)].r == 0) &&
2133 (cubicmapPixels[z*cubicmap.width + (x + 1)].g == 0) &&
2134 (cubicmapPixels[z*cubicmap.width + (x + 1)].b == 0)) || (x == cubicmap.width - 1))
2135 {
2136 // Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5
2137 // NOTE: Collateral occluded faces are not generated
2138 mapVertices[vCounter] = v3;
2139 mapVertices[vCounter + 1] = v8;
2140 mapVertices[vCounter + 2] = v4;
2141 mapVertices[vCounter + 3] = v4;
2142 mapVertices[vCounter + 4] = v8;
2143 mapVertices[vCounter + 5] = v5;
2144 vCounter += 6;
2145
2146 mapNormals[nCounter] = n1;
2147 mapNormals[nCounter + 1] = n1;
2148 mapNormals[nCounter + 2] = n1;
2149 mapNormals[nCounter + 3] = n1;
2150 mapNormals[nCounter + 4] = n1;
2151 mapNormals[nCounter + 5] = n1;
2152 nCounter += 6;
2153
2154 mapTexcoords[tcCounter] = (Vector2){ rightTexUV.x, rightTexUV.y };
2155 mapTexcoords[tcCounter + 1] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
2156 mapTexcoords[tcCounter + 2] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
2157 mapTexcoords[tcCounter + 3] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
2158 mapTexcoords[tcCounter + 4] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
2159 mapTexcoords[tcCounter + 5] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y + rightTexUV.height };
2160 tcCounter += 6;
2161 }
2162
2163 if (((x > 0) &&
2164 (cubicmapPixels[z*cubicmap.width + (x - 1)].r == 0) &&
2165 (cubicmapPixels[z*cubicmap.width + (x - 1)].g == 0) &&
2166 (cubicmapPixels[z*cubicmap.width + (x - 1)].b == 0)) || (x == 0))
2167 {
2168 // Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7
2169 // NOTE: Collateral occluded faces are not generated
2170 mapVertices[vCounter] = v1;
2171 mapVertices[vCounter + 1] = v7;
2172 mapVertices[vCounter + 2] = v2;
2173 mapVertices[vCounter + 3] = v1;
2174 mapVertices[vCounter + 4] = v6;
2175 mapVertices[vCounter + 5] = v7;
2176 vCounter += 6;
2177
2178 mapNormals[nCounter] = n2;
2179 mapNormals[nCounter + 1] = n2;
2180 mapNormals[nCounter + 2] = n2;
2181 mapNormals[nCounter + 3] = n2;
2182 mapNormals[nCounter + 4] = n2;
2183 mapNormals[nCounter + 5] = n2;
2184 nCounter += 6;
2185
2186 mapTexcoords[tcCounter] = (Vector2){ leftTexUV.x, leftTexUV.y };
2187 mapTexcoords[tcCounter + 1] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
2188 mapTexcoords[tcCounter + 2] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y };
2189 mapTexcoords[tcCounter + 3] = (Vector2){ leftTexUV.x, leftTexUV.y };
2190 mapTexcoords[tcCounter + 4] = (Vector2){ leftTexUV.x, leftTexUV.y + leftTexUV.height };
2191 mapTexcoords[tcCounter + 5] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
2192 tcCounter += 6;
2193 }
2194 }
2195 // We check pixel color to be BLACK, we will only draw floor and roof
2196 else if ((cubicmapPixels[z*cubicmap.width + x].r == 0) &&
2197 (cubicmapPixels[z*cubicmap.width + x].g == 0) &&
2198 (cubicmapPixels[z*cubicmap.width + x].b == 0))
2199 {
2200 // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
2201 mapVertices[vCounter] = v1;
2202 mapVertices[vCounter + 1] = v3;
2203 mapVertices[vCounter + 2] = v2;
2204 mapVertices[vCounter + 3] = v1;
2205 mapVertices[vCounter + 4] = v4;
2206 mapVertices[vCounter + 5] = v3;
2207 vCounter += 6;
2208
2209 mapNormals[nCounter] = n4;
2210 mapNormals[nCounter + 1] = n4;
2211 mapNormals[nCounter + 2] = n4;
2212 mapNormals[nCounter + 3] = n4;
2213 mapNormals[nCounter + 4] = n4;
2214 mapNormals[nCounter + 5] = n4;
2215 nCounter += 6;
2216
2217 mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
2218 mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2219 mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
2220 mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
2221 mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
2222 mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2223 tcCounter += 6;
2224
2225 // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
2226 mapVertices[vCounter] = v6;
2227 mapVertices[vCounter + 1] = v7;
2228 mapVertices[vCounter + 2] = v8;
2229 mapVertices[vCounter + 3] = v6;
2230 mapVertices[vCounter + 4] = v8;
2231 mapVertices[vCounter + 5] = v5;
2232 vCounter += 6;
2233
2234 mapNormals[nCounter] = n3;
2235 mapNormals[nCounter + 1] = n3;
2236 mapNormals[nCounter + 2] = n3;
2237 mapNormals[nCounter + 3] = n3;
2238 mapNormals[nCounter + 4] = n3;
2239 mapNormals[nCounter + 5] = n3;
2240 nCounter += 6;
2241
2242 mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2243 mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
2244 mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2245 mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2246 mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2247 mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
2248 tcCounter += 6;
2249 }
2250 }
2251 }
2252
2253 // Move data from mapVertices temp arays to vertices float array
2254 mesh.vertexCount = vCounter;
2255 mesh.triangleCount = vCounter/3;
2256
2257 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
2258 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
2259 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
2260 mesh.colors = NULL;
2261
2262 int fCounter = 0;
2263
2264 // Move vertices data
2265 for (int i = 0; i < vCounter; i++)
2266 {
2267 mesh.vertices[fCounter] = mapVertices[i].x;
2268 mesh.vertices[fCounter + 1] = mapVertices[i].y;
2269 mesh.vertices[fCounter + 2] = mapVertices[i].z;
2270 fCounter += 3;
2271 }
2272
2273 fCounter = 0;
2274
2275 // Move normals data
2276 for (int i = 0; i < nCounter; i++)
2277 {
2278 mesh.normals[fCounter] = mapNormals[i].x;
2279 mesh.normals[fCounter + 1] = mapNormals[i].y;
2280 mesh.normals[fCounter + 2] = mapNormals[i].z;
2281 fCounter += 3;
2282 }
2283
2284 fCounter = 0;
2285
2286 // Move texcoords data
2287 for (int i = 0; i < tcCounter; i++)
2288 {
2289 mesh.texcoords[fCounter] = mapTexcoords[i].x;
2290 mesh.texcoords[fCounter + 1] = mapTexcoords[i].y;
2291 fCounter += 2;
2292 }
2293
2294 RL_FREE(mapVertices);
2295 RL_FREE(mapNormals);
2296 RL_FREE(mapTexcoords);
2297
2298 RL_FREE(cubicmapPixels); // Free image pixel data
2299
2300 // Upload vertex data to GPU (static mesh)
2301 rlLoadMesh(&mesh, false);
2302
2303 return mesh;
2304}
2305#endif // SUPPORT_MESH_GENERATION
2306
2307// Compute mesh bounding box limits
2308// NOTE: minVertex and maxVertex should be transformed by model transform matrix
2309BoundingBox MeshBoundingBox(Mesh mesh)
2310{
2311 // Get min and max vertex to construct bounds (AABB)
2312 Vector3 minVertex = { 0 };
2313 Vector3 maxVertex = { 0 };
2314
2315 if (mesh.vertices != NULL)
2316 {
2317 minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
2318 maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
2319
2320 for (int i = 1; i < mesh.vertexCount; i++)
2321 {
2322 minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
2323 maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
2324 }
2325 }
2326
2327 // Create the bounding box
2328 BoundingBox box = { 0 };
2329 box.min = minVertex;
2330 box.max = maxVertex;
2331
2332 return box;
2333}
2334
2335// Compute mesh tangents
2336// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates
2337// Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html
2338void MeshTangents(Mesh *mesh)
2339{
2340 if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
2341 else TRACELOG(LOG_WARNING, "MESH: Tangents data already available, re-writting");
2342
2343 Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
2344 Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
2345
2346 for (int i = 0; i < mesh->vertexCount; i += 3)
2347 {
2348 // Get triangle vertices
2349 Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] };
2350 Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] };
2351 Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] };
2352
2353 // Get triangle texcoords
2354 Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] };
2355 Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] };
2356 Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] };
2357
2358 float x1 = v2.x - v1.x;
2359 float y1 = v2.y - v1.y;
2360 float z1 = v2.z - v1.z;
2361 float x2 = v3.x - v1.x;
2362 float y2 = v3.y - v1.y;
2363 float z2 = v3.z - v1.z;
2364
2365 float s1 = uv2.x - uv1.x;
2366 float t1 = uv2.y - uv1.y;
2367 float s2 = uv3.x - uv1.x;
2368 float t2 = uv3.y - uv1.y;
2369
2370 float div = s1*t2 - s2*t1;
2371 float r = (div == 0.0f)? 0.0f : 1.0f/div;
2372
2373 Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
2374 Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
2375
2376 tan1[i + 0] = sdir;
2377 tan1[i + 1] = sdir;
2378 tan1[i + 2] = sdir;
2379
2380 tan2[i + 0] = tdir;
2381 tan2[i + 1] = tdir;
2382 tan2[i + 2] = tdir;
2383 }
2384
2385 // Compute tangents considering normals
2386 for (int i = 0; i < mesh->vertexCount; ++i)
2387 {
2388 Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
2389 Vector3 tangent = tan1[i];
2390
2391 // TODO: Review, not sure if tangent computation is right, just used reference proposed maths...
2392 #if defined(COMPUTE_TANGENTS_METHOD_01)
2393 Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
2394 tmp = Vector3Normalize(tmp);
2395 mesh->tangents[i*4 + 0] = tmp.x;
2396 mesh->tangents[i*4 + 1] = tmp.y;
2397 mesh->tangents[i*4 + 2] = tmp.z;
2398 mesh->tangents[i*4 + 3] = 1.0f;
2399 #else
2400 Vector3OrthoNormalize(&normal, &tangent);
2401 mesh->tangents[i*4 + 0] = tangent.x;
2402 mesh->tangents[i*4 + 1] = tangent.y;
2403 mesh->tangents[i*4 + 2] = tangent.z;
2404 mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f;
2405 #endif
2406 }
2407
2408 RL_FREE(tan1);
2409 RL_FREE(tan2);
2410
2411 // Load a new tangent attributes buffer
2412 mesh->vboId[LOC_VERTEX_TANGENT] = rlLoadAttribBuffer(mesh->vaoId, LOC_VERTEX_TANGENT, mesh->tangents, mesh->vertexCount*4*sizeof(float), false);
2413
2414 TRACELOG(LOG_INFO, "MESH: Tangents data computed for provided mesh");
2415}
2416
2417// Compute mesh binormals (aka bitangent)
2418void MeshBinormals(Mesh *mesh)
2419{
2420 for (int i = 0; i < mesh->vertexCount; i++)
2421 {
2422 //Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
2423 //Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
2424 //Vector3 binormal = Vector3Scale(Vector3CrossProduct(normal, tangent), mesh->tangents[i*4 + 3]);
2425
2426 // TODO: Register computed binormal in mesh->binormal?
2427 }
2428}
2429
2430// Draw a model (with texture if set)
2431void DrawModel(Model model, Vector3 position, float scale, Color tint)
2432{
2433 Vector3 vScale = { scale, scale, scale };
2434 Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f };
2435
2436 DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint);
2437}
2438
2439// Draw a model with extended parameters
2440void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
2441{
2442 // Calculate transformation matrix from function parameters
2443 // Get transform matrix (rotation -> scale -> translation)
2444 Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
2445 Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
2446 Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
2447
2448 Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
2449
2450 // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
2451 model.transform = MatrixMultiply(model.transform, matTransform);
2452
2453 for (int i = 0; i < model.meshCount; i++)
2454 {
2455 // TODO: Review color + tint premultiplication mechanism
2456 Color color = model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color;
2457
2458 Color colorTint = WHITE;
2459 colorTint.r = (((float)color.r/255.0)*((float)tint.r/255.0))*255;
2460 colorTint.g = (((float)color.g/255.0)*((float)tint.g/255.0))*255;
2461 colorTint.b = (((float)color.b/255.0)*((float)tint.b/255.0))*255;
2462 colorTint.a = (((float)color.a/255.0)*((float)tint.a/255.0))*255;
2463
2464 model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = colorTint;
2465 rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
2466 model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = color;
2467 }
2468}
2469
2470// Draw a model wires (with texture if set)
2471void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
2472{
2473 rlEnableWireMode();
2474
2475 DrawModel(model, position, scale, tint);
2476
2477 rlDisableWireMode();
2478}
2479
2480// Draw a model wires (with texture if set) with extended parameters
2481void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
2482{
2483 rlEnableWireMode();
2484
2485 DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
2486
2487 rlDisableWireMode();
2488}
2489
2490// Draw a billboard
2491void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
2492{
2493 Rectangle sourceRec = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
2494
2495 DrawBillboardRec(camera, texture, sourceRec, center, size, tint);
2496}
2497
2498// Draw a billboard (part of a texture defined by a rectangle)
2499void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 center, float size, Color tint)
2500{
2501 // NOTE: Billboard size will maintain sourceRec aspect ratio, size will represent billboard width
2502 Vector2 sizeRatio = { size, size*(float)sourceRec.height/sourceRec.width };
2503
2504 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
2505
2506 Vector3 right = { matView.m0, matView.m4, matView.m8 };
2507 //Vector3 up = { matView.m1, matView.m5, matView.m9 };
2508
2509 // NOTE: Billboard locked on axis-Y
2510 Vector3 up = { 0.0f, 1.0f, 0.0f };
2511/*
2512 a-------b
2513 | |
2514 | * |
2515 | |
2516 d-------c
2517*/
2518 right = Vector3Scale(right, sizeRatio.x/2);
2519 up = Vector3Scale(up, sizeRatio.y/2);
2520
2521 Vector3 p1 = Vector3Add(right, up);
2522 Vector3 p2 = Vector3Subtract(right, up);
2523
2524 Vector3 a = Vector3Subtract(center, p2);
2525 Vector3 b = Vector3Add(center, p1);
2526 Vector3 c = Vector3Add(center, p2);
2527 Vector3 d = Vector3Subtract(center, p1);
2528
2529 if (rlCheckBufferLimit(4)) rlglDraw();
2530
2531 rlEnableTexture(texture.id);
2532
2533 rlBegin(RL_QUADS);
2534 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
2535
2536 // Bottom-left corner for texture and quad
2537 rlTexCoord2f((float)sourceRec.x/texture.width, (float)sourceRec.y/texture.height);
2538 rlVertex3f(a.x, a.y, a.z);
2539
2540 // Top-left corner for texture and quad
2541 rlTexCoord2f((float)sourceRec.x/texture.width, (float)(sourceRec.y + sourceRec.height)/texture.height);
2542 rlVertex3f(d.x, d.y, d.z);
2543
2544 // Top-right corner for texture and quad
2545 rlTexCoord2f((float)(sourceRec.x + sourceRec.width)/texture.width, (float)(sourceRec.y + sourceRec.height)/texture.height);
2546 rlVertex3f(c.x, c.y, c.z);
2547
2548 // Bottom-right corner for texture and quad
2549 rlTexCoord2f((float)(sourceRec.x + sourceRec.width)/texture.width, (float)sourceRec.y/texture.height);
2550 rlVertex3f(b.x, b.y, b.z);
2551 rlEnd();
2552
2553 rlDisableTexture();
2554}
2555
2556// Draw a bounding box with wires
2557void DrawBoundingBox(BoundingBox box, Color color)
2558{
2559 Vector3 size;
2560
2561 size.x = fabsf(box.max.x - box.min.x);
2562 size.y = fabsf(box.max.y - box.min.y);
2563 size.z = fabsf(box.max.z - box.min.z);
2564
2565 Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f };
2566
2567 DrawCubeWires(center, size.x, size.y, size.z, color);
2568}
2569
2570// Detect collision between two spheres
2571bool CheckCollisionSpheres(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB)
2572{
2573 bool collision = false;
2574
2575 // Simple way to check for collision, just checking distance between two points
2576 // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution
2577 /*
2578 float dx = centerA.x - centerB.x; // X distance between centers
2579 float dy = centerA.y - centerB.y; // Y distance between centers
2580 float dz = centerA.z - centerB.z; // Z distance between centers
2581
2582 float distance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance between centers
2583
2584 if (distance <= (radiusA + radiusB)) collision = true;
2585 */
2586
2587 // Check for distances squared to avoid sqrtf()
2588 if (Vector3DotProduct(Vector3Subtract(centerB, centerA), Vector3Subtract(centerB, centerA)) <= (radiusA + radiusB)*(radiusA + radiusB)) collision = true;
2589
2590 return collision;
2591}
2592
2593// Detect collision between two boxes
2594// NOTE: Boxes are defined by two points minimum and maximum
2595bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2)
2596{
2597 bool collision = true;
2598
2599 if ((box1.max.x >= box2.min.x) && (box1.min.x <= box2.max.x))
2600 {
2601 if ((box1.max.y < box2.min.y) || (box1.min.y > box2.max.y)) collision = false;
2602 if ((box1.max.z < box2.min.z) || (box1.min.z > box2.max.z)) collision = false;
2603 }
2604 else collision = false;
2605
2606 return collision;
2607}
2608
2609// Detect collision between box and sphere
2610bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius)
2611{
2612 bool collision = false;
2613
2614 float dmin = 0;
2615
2616 if (center.x < box.min.x) dmin += powf(center.x - box.min.x, 2);
2617 else if (center.x > box.max.x) dmin += powf(center.x - box.max.x, 2);
2618
2619 if (center.y < box.min.y) dmin += powf(center.y - box.min.y, 2);
2620 else if (center.y > box.max.y) dmin += powf(center.y - box.max.y, 2);
2621
2622 if (center.z < box.min.z) dmin += powf(center.z - box.min.z, 2);
2623 else if (center.z > box.max.z) dmin += powf(center.z - box.max.z, 2);
2624
2625 if (dmin <= (radius*radius)) collision = true;
2626
2627 return collision;
2628}
2629
2630// Detect collision between ray and sphere
2631bool CheckCollisionRaySphere(Ray ray, Vector3 center, float radius)
2632{
2633 bool collision = false;
2634
2635 Vector3 raySpherePos = Vector3Subtract(center, ray.position);
2636 float distance = Vector3Length(raySpherePos);
2637 float vector = Vector3DotProduct(raySpherePos, ray.direction);
2638 float d = radius*radius - (distance*distance - vector*vector);
2639
2640 if (d >= 0.0f) collision = true;
2641
2642 return collision;
2643}
2644
2645// Detect collision between ray and sphere with extended parameters and collision point detection
2646bool CheckCollisionRaySphereEx(Ray ray, Vector3 center, float radius, Vector3 *collisionPoint)
2647{
2648 bool collision = false;
2649
2650 Vector3 raySpherePos = Vector3Subtract(center, ray.position);
2651 float distance = Vector3Length(raySpherePos);
2652 float vector = Vector3DotProduct(raySpherePos, ray.direction);
2653 float d = radius*radius - (distance*distance - vector*vector);
2654
2655 if (d >= 0.0f) collision = true;
2656
2657 // Check if ray origin is inside the sphere to calculate the correct collision point
2658 float collisionDistance = 0;
2659
2660 if (distance < radius) collisionDistance = vector + sqrtf(d);
2661 else collisionDistance = vector - sqrtf(d);
2662
2663 // Calculate collision point
2664 Vector3 cPoint = Vector3Add(ray.position, Vector3Scale(ray.direction, collisionDistance));
2665
2666 collisionPoint->x = cPoint.x;
2667 collisionPoint->y = cPoint.y;
2668 collisionPoint->z = cPoint.z;
2669
2670 return collision;
2671}
2672
2673// Detect collision between ray and bounding box
2674bool CheckCollisionRayBox(Ray ray, BoundingBox box)
2675{
2676 bool collision = false;
2677
2678 float t[8];
2679 t[0] = (box.min.x - ray.position.x)/ray.direction.x;
2680 t[1] = (box.max.x - ray.position.x)/ray.direction.x;
2681 t[2] = (box.min.y - ray.position.y)/ray.direction.y;
2682 t[3] = (box.max.y - ray.position.y)/ray.direction.y;
2683 t[4] = (box.min.z - ray.position.z)/ray.direction.z;
2684 t[5] = (box.max.z - ray.position.z)/ray.direction.z;
2685 t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
2686 t[7] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));
2687
2688 collision = !(t[7] < 0 || t[6] > t[7]);
2689
2690 return collision;
2691}
2692
2693// Get collision info between ray and model
2694RayHitInfo GetCollisionRayModel(Ray ray, Model model)
2695{
2696 RayHitInfo result = { 0 };
2697
2698 for (int m = 0; m < model.meshCount; m++)
2699 {
2700 // Check if meshhas vertex data on CPU for testing
2701 if (model.meshes[m].vertices != NULL)
2702 {
2703 // model->mesh.triangleCount may not be set, vertexCount is more reliable
2704 int triangleCount = model.meshes[m].vertexCount/3;
2705
2706 // Test against all triangles in mesh
2707 for (int i = 0; i < triangleCount; i++)
2708 {
2709 Vector3 a, b, c;
2710 Vector3 *vertdata = (Vector3 *)model.meshes[m].vertices;
2711
2712 if (model.meshes[m].indices)
2713 {
2714 a = vertdata[model.meshes[m].indices[i*3 + 0]];
2715 b = vertdata[model.meshes[m].indices[i*3 + 1]];
2716 c = vertdata[model.meshes[m].indices[i*3 + 2]];
2717 }
2718 else
2719 {
2720 a = vertdata[i*3 + 0];
2721 b = vertdata[i*3 + 1];
2722 c = vertdata[i*3 + 2];
2723 }
2724
2725 a = Vector3Transform(a, model.transform);
2726 b = Vector3Transform(b, model.transform);
2727 c = Vector3Transform(c, model.transform);
2728
2729 RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c);
2730
2731 if (triHitInfo.hit)
2732 {
2733 // Save the closest hit triangle
2734 if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo;
2735 }
2736 }
2737 }
2738 }
2739
2740 return result;
2741}
2742
2743// Get collision info between ray and triangle
2744// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
2745RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3)
2746{
2747 #define EPSILON 0.000001 // A small number
2748
2749 Vector3 edge1, edge2;
2750 Vector3 p, q, tv;
2751 float det, invDet, u, v, t;
2752 RayHitInfo result = {0};
2753
2754 // Find vectors for two edges sharing V1
2755 edge1 = Vector3Subtract(p2, p1);
2756 edge2 = Vector3Subtract(p3, p1);
2757
2758 // Begin calculating determinant - also used to calculate u parameter
2759 p = Vector3CrossProduct(ray.direction, edge2);
2760
2761 // If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle
2762 det = Vector3DotProduct(edge1, p);
2763
2764 // Avoid culling!
2765 if ((det > -EPSILON) && (det < EPSILON)) return result;
2766
2767 invDet = 1.0f/det;
2768
2769 // Calculate distance from V1 to ray origin
2770 tv = Vector3Subtract(ray.position, p1);
2771
2772 // Calculate u parameter and test bound
2773 u = Vector3DotProduct(tv, p)*invDet;
2774
2775 // The intersection lies outside of the triangle
2776 if ((u < 0.0f) || (u > 1.0f)) return result;
2777
2778 // Prepare to test v parameter
2779 q = Vector3CrossProduct(tv, edge1);
2780
2781 // Calculate V parameter and test bound
2782 v = Vector3DotProduct(ray.direction, q)*invDet;
2783
2784 // The intersection lies outside of the triangle
2785 if ((v < 0.0f) || ((u + v) > 1.0f)) return result;
2786
2787 t = Vector3DotProduct(edge2, q)*invDet;
2788
2789 if (t > EPSILON)
2790 {
2791 // Ray hit, get hit point and normal
2792 result.hit = true;
2793 result.distance = t;
2794 result.hit = true;
2795 result.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2));
2796 result.position = Vector3Add(ray.position, Vector3Scale(ray.direction, t));
2797 }
2798
2799 return result;
2800}
2801
2802// Get collision info between ray and ground plane (Y-normal plane)
2803RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight)
2804{
2805 #define EPSILON 0.000001 // A small number
2806
2807 RayHitInfo result = { 0 };
2808
2809 if (fabsf(ray.direction.y) > EPSILON)
2810 {
2811 float distance = (ray.position.y - groundHeight)/-ray.direction.y;
2812
2813 if (distance >= 0.0)
2814 {
2815 result.hit = true;
2816 result.distance = distance;
2817 result.normal = (Vector3){ 0.0, 1.0, 0.0 };
2818 result.position = Vector3Add(ray.position, Vector3Scale(ray.direction, distance));
2819 }
2820 }
2821
2822 return result;
2823}
2824
2825//----------------------------------------------------------------------------------
2826// Module specific Functions Definition
2827//----------------------------------------------------------------------------------
2828
2829#if defined(SUPPORT_FILEFORMAT_OBJ)
2830// Load OBJ mesh data
2831static Model LoadOBJ(const char *fileName)
2832{
2833 Model model = { 0 };
2834
2835 tinyobj_attrib_t attrib;
2836 tinyobj_shape_t *meshes = NULL;
2837 unsigned int meshCount = 0;
2838
2839 tinyobj_material_t *materials = NULL;
2840 unsigned int materialCount = 0;
2841
2842 char *fileData = LoadFileText(fileName);
2843
2844 if (fileData != NULL)
2845 {
2846 int dataSize = strlen(fileData);
2847 char currentDir[1024] = { 0 };
2848 strcpy(currentDir, GetWorkingDirectory());
2849 chdir(GetDirectoryPath(fileName));
2850
2851 unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
2852 int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, fileData, dataSize, flags);
2853
2854 if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName);
2855 else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount);
2856
2857 // Init model meshes array
2858 // TODO: Support multiple meshes... in the meantime, only one mesh is returned
2859 //model.meshCount = meshCount;
2860 model.meshCount = 1;
2861 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
2862
2863 // Init model materials array
2864 if (materialCount > 0)
2865 {
2866 model.materialCount = materialCount;
2867 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
2868 }
2869
2870 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
2871
2872 /*
2873 // Multiple meshes data reference
2874 // NOTE: They are provided as a faces offset
2875 typedef struct {
2876 char *name; // group name or object name
2877 unsigned int face_offset;
2878 unsigned int length;
2879 } tinyobj_shape_t;
2880 */
2881
2882 // Init model meshes
2883 for (int m = 0; m < 1; m++)
2884 {
2885 Mesh mesh = { 0 };
2886 memset(&mesh, 0, sizeof(Mesh));
2887 mesh.vertexCount = attrib.num_faces*3;
2888 mesh.triangleCount = attrib.num_faces;
2889 mesh.vertices = (float *)RL_CALLOC(mesh.vertexCount*3, sizeof(float));
2890 mesh.texcoords = (float *)RL_CALLOC(mesh.vertexCount*2, sizeof(float));
2891 mesh.normals = (float *)RL_CALLOC(mesh.vertexCount*3, sizeof(float));
2892 mesh.vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
2893
2894 int vCount = 0;
2895 int vtCount = 0;
2896 int vnCount = 0;
2897
2898 for (int f = 0; f < attrib.num_faces; f++)
2899 {
2900 // Get indices for the face
2901 tinyobj_vertex_index_t idx0 = attrib.faces[3*f + 0];
2902 tinyobj_vertex_index_t idx1 = attrib.faces[3*f + 1];
2903 tinyobj_vertex_index_t idx2 = attrib.faces[3*f + 2];
2904
2905 // Fill vertices buffer (float) using vertex index of the face
2906 for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount +=3;
2907 for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount +=3;
2908 for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount +=3;
2909
2910 // Fill texcoords buffer (float) using vertex index of the face
2911 // NOTE: Y-coordinate must be flipped upside-down
2912 mesh.texcoords[vtCount + 0] = attrib.texcoords[idx0.vt_idx*2 + 0];
2913 mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount += 2;
2914 mesh.texcoords[vtCount + 0] = attrib.texcoords[idx1.vt_idx*2 + 0];
2915 mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount += 2;
2916 mesh.texcoords[vtCount + 0] = attrib.texcoords[idx2.vt_idx*2 + 0];
2917 mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount += 2;
2918
2919 // Fill normals buffer (float) using vertex index of the face
2920 for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount +=3;
2921 for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount +=3;
2922 for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount +=3;
2923 }
2924
2925 model.meshes[m] = mesh; // Assign mesh data to model
2926
2927 // Assign mesh material for current mesh
2928 model.meshMaterial[m] = attrib.material_ids[m];
2929
2930 // Set unfound materials to default
2931 if (model.meshMaterial[m] == -1) model.meshMaterial[m] = 0;
2932 }
2933
2934 // Init model materials
2935 for (int m = 0; m < materialCount; m++)
2936 {
2937 // Init material to default
2938 // NOTE: Uses default shader, only MAP_DIFFUSE supported
2939 model.materials[m] = LoadMaterialDefault();
2940
2941 /*
2942 typedef struct {
2943 char *name;
2944
2945 float ambient[3];
2946 float diffuse[3];
2947 float specular[3];
2948 float transmittance[3];
2949 float emission[3];
2950 float shininess;
2951 float ior; // index of refraction
2952 float dissolve; // 1 == opaque; 0 == fully transparent
2953 // illumination model (Ref: http://www.fileformat.info/format/material/)
2954 int illum;
2955
2956 int pad0;
2957
2958 char *ambient_texname; // map_Ka
2959 char *diffuse_texname; // map_Kd
2960 char *specular_texname; // map_Ks
2961 char *specular_highlight_texname; // map_Ns
2962 char *bump_texname; // map_bump, bump
2963 char *displacement_texname; // disp
2964 char *alpha_texname; // map_d
2965 } tinyobj_material_t;
2966 */
2967
2968 model.materials[m].maps[MAP_DIFFUSE].texture = GetTextureDefault(); // Get default texture, in case no texture is defined
2969
2970 if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd
2971 model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3];
2972 model.materials[m].maps[MAP_DIFFUSE].value = 0.0f;
2973
2974 if (materials[m].specular_texname != NULL) model.materials[m].maps[MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks
2975 model.materials[m].maps[MAP_SPECULAR].color = (Color){ (float)(materials[m].specular[0]*255.0f), (float)(materials[m].specular[1]*255.0f), (float)(materials[m].specular[2]*255.0f), 255 }; //float specular[3];
2976 model.materials[m].maps[MAP_SPECULAR].value = 0.0f;
2977
2978 if (materials[m].bump_texname != NULL) model.materials[m].maps[MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump
2979 model.materials[m].maps[MAP_NORMAL].color = WHITE;
2980 model.materials[m].maps[MAP_NORMAL].value = materials[m].shininess;
2981
2982 model.materials[m].maps[MAP_EMISSION].color = (Color){ (float)(materials[m].emission[0]*255.0f), (float)(materials[m].emission[1]*255.0f), (float)(materials[m].emission[2]*255.0f), 255 }; //float emission[3];
2983
2984 if (materials[m].displacement_texname != NULL) model.materials[m].maps[MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp
2985 }
2986
2987 tinyobj_attrib_free(&attrib);
2988 tinyobj_shapes_free(meshes, meshCount);
2989 tinyobj_materials_free(materials, materialCount);
2990
2991 RL_FREE(fileData);
2992
2993 chdir(currentDir);
2994 }
2995
2996 return model;
2997}
2998#endif
2999
3000#if defined(SUPPORT_FILEFORMAT_IQM)
3001// Load IQM mesh data
3002static Model LoadIQM(const char *fileName)
3003{
3004 #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
3005 #define IQM_VERSION 2 // only IQM version 2 supported
3006
3007 #define BONE_NAME_LENGTH 32 // BoneInfo name string length
3008 #define MESH_NAME_LENGTH 32 // Mesh name string length
3009
3010 // IQM file structs
3011 //-----------------------------------------------------------------------------------
3012 typedef struct IQMHeader {
3013 char magic[16];
3014 unsigned int version;
3015 unsigned int filesize;
3016 unsigned int flags;
3017 unsigned int num_text, ofs_text;
3018 unsigned int num_meshes, ofs_meshes;
3019 unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
3020 unsigned int num_triangles, ofs_triangles, ofs_adjacency;
3021 unsigned int num_joints, ofs_joints;
3022 unsigned int num_poses, ofs_poses;
3023 unsigned int num_anims, ofs_anims;
3024 unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
3025 unsigned int num_comment, ofs_comment;
3026 unsigned int num_extensions, ofs_extensions;
3027 } IQMHeader;
3028
3029 typedef struct IQMMesh {
3030 unsigned int name;
3031 unsigned int material;
3032 unsigned int first_vertex, num_vertexes;
3033 unsigned int first_triangle, num_triangles;
3034 } IQMMesh;
3035
3036 typedef struct IQMTriangle {
3037 unsigned int vertex[3];
3038 } IQMTriangle;
3039
3040 typedef struct IQMJoint {
3041 unsigned int name;
3042 int parent;
3043 float translate[3], rotate[4], scale[3];
3044 } IQMJoint;
3045
3046 typedef struct IQMVertexArray {
3047 unsigned int type;
3048 unsigned int flags;
3049 unsigned int format;
3050 unsigned int size;
3051 unsigned int offset;
3052 } IQMVertexArray;
3053
3054 // NOTE: Below IQM structures are not used but listed for reference
3055 /*
3056 typedef struct IQMAdjacency {
3057 unsigned int triangle[3];
3058 } IQMAdjacency;
3059
3060 typedef struct IQMPose {
3061 int parent;
3062 unsigned int mask;
3063 float channeloffset[10];
3064 float channelscale[10];
3065 } IQMPose;
3066
3067 typedef struct IQMAnim {
3068 unsigned int name;
3069 unsigned int first_frame, num_frames;
3070 float framerate;
3071 unsigned int flags;
3072 } IQMAnim;
3073
3074 typedef struct IQMBounds {
3075 float bbmin[3], bbmax[3];
3076 float xyradius, radius;
3077 } IQMBounds;
3078 */
3079 //-----------------------------------------------------------------------------------
3080
3081 // IQM vertex data types
3082 enum {
3083 IQM_POSITION = 0,
3084 IQM_TEXCOORD = 1,
3085 IQM_NORMAL = 2,
3086 IQM_TANGENT = 3, // NOTE: Tangents unused by default
3087 IQM_BLENDINDEXES = 4,
3088 IQM_BLENDWEIGHTS = 5,
3089 IQM_COLOR = 6, // NOTE: Vertex colors unused by default
3090 IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default
3091 };
3092
3093 Model model = { 0 };
3094
3095 FILE *iqmFile = NULL;
3096 IQMHeader iqm;
3097
3098 IQMMesh *imesh;
3099 IQMTriangle *tri;
3100 IQMVertexArray *va;
3101 IQMJoint *ijoint;
3102
3103 float *vertex = NULL;
3104 float *normal = NULL;
3105 float *text = NULL;
3106 char *blendi = NULL;
3107 unsigned char *blendw = NULL;
3108
3109 iqmFile = fopen(fileName, "rb");
3110
3111 if (iqmFile == NULL)
3112 {
3113 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open IQM file", fileName);
3114 return model;
3115 }
3116
3117 fread(&iqm, sizeof(IQMHeader), 1, iqmFile); // Read IQM header
3118
3119 if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC)))
3120 {
3121 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName);
3122 fclose(iqmFile);
3123 return model;
3124 }
3125
3126 if (iqm.version != IQM_VERSION)
3127 {
3128 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqm.version);
3129 fclose(iqmFile);
3130 return model;
3131 }
3132
3133 // Meshes data processing
3134 imesh = RL_MALLOC(sizeof(IQMMesh)*iqm.num_meshes);
3135 fseek(iqmFile, iqm.ofs_meshes, SEEK_SET);
3136 fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile);
3137
3138 model.meshCount = iqm.num_meshes;
3139 model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
3140
3141 char name[MESH_NAME_LENGTH] = { 0 };
3142
3143 for (int i = 0; i < model.meshCount; i++)
3144 {
3145 fseek(iqmFile, iqm.ofs_text + imesh[i].name, SEEK_SET);
3146 fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used...
3147 model.meshes[i].vertexCount = imesh[i].num_vertexes;
3148
3149 model.meshes[i].vertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions
3150 model.meshes[i].normals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals
3151 model.meshes[i].texcoords = RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords
3152
3153 model.meshes[i].boneIds = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported!
3154 model.meshes[i].boneWeights = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported!
3155
3156 model.meshes[i].triangleCount = imesh[i].num_triangles;
3157 model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
3158
3159 // Animated verted data, what we actually process for rendering
3160 // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
3161 model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
3162 model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
3163
3164 model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
3165 }
3166
3167 // Triangles data processing
3168 tri = RL_MALLOC(iqm.num_triangles*sizeof(IQMTriangle));
3169 fseek(iqmFile, iqm.ofs_triangles, SEEK_SET);
3170 fread(tri, iqm.num_triangles*sizeof(IQMTriangle), 1, iqmFile);
3171
3172 for (int m = 0; m < model.meshCount; m++)
3173 {
3174 int tcounter = 0;
3175
3176 for (int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++)
3177 {
3178 // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around
3179 model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex;
3180 model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex;
3181 model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex;
3182 tcounter += 3;
3183 }
3184 }
3185
3186 // Vertex arrays data processing
3187 va = RL_MALLOC(iqm.num_vertexarrays*sizeof(IQMVertexArray));
3188 fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET);
3189 fread(va, iqm.num_vertexarrays*sizeof(IQMVertexArray), 1, iqmFile);
3190
3191 for (int i = 0; i < iqm.num_vertexarrays; i++)
3192 {
3193 switch (va[i].type)
3194 {
3195 case IQM_POSITION:
3196 {
3197 vertex = RL_MALLOC(iqm.num_vertexes*3*sizeof(float));
3198 fseek(iqmFile, va[i].offset, SEEK_SET);
3199 fread(vertex, iqm.num_vertexes*3*sizeof(float), 1, iqmFile);
3200
3201 for (int m = 0; m < iqm.num_meshes; m++)
3202 {
3203 int vCounter = 0;
3204 for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
3205 {
3206 model.meshes[m].vertices[vCounter] = vertex[i];
3207 model.meshes[m].animVertices[vCounter] = vertex[i];
3208 vCounter++;
3209 }
3210 }
3211 } break;
3212 case IQM_NORMAL:
3213 {
3214 normal = RL_MALLOC(iqm.num_vertexes*3*sizeof(float));
3215 fseek(iqmFile, va[i].offset, SEEK_SET);
3216 fread(normal, iqm.num_vertexes*3*sizeof(float), 1, iqmFile);
3217
3218 for (int m = 0; m < iqm.num_meshes; m++)
3219 {
3220 int vCounter = 0;
3221 for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
3222 {
3223 model.meshes[m].normals[vCounter] = normal[i];
3224 model.meshes[m].animNormals[vCounter] = normal[i];
3225 vCounter++;
3226 }
3227 }
3228 } break;
3229 case IQM_TEXCOORD:
3230 {
3231 text = RL_MALLOC(iqm.num_vertexes*2*sizeof(float));
3232 fseek(iqmFile, va[i].offset, SEEK_SET);
3233 fread(text, iqm.num_vertexes*2*sizeof(float), 1, iqmFile);
3234
3235 for (int m = 0; m < iqm.num_meshes; m++)
3236 {
3237 int vCounter = 0;
3238 for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++)
3239 {
3240 model.meshes[m].texcoords[vCounter] = text[i];
3241 vCounter++;
3242 }
3243 }
3244 } break;
3245 case IQM_BLENDINDEXES:
3246 {
3247 blendi = RL_MALLOC(iqm.num_vertexes*4*sizeof(char));
3248 fseek(iqmFile, va[i].offset, SEEK_SET);
3249 fread(blendi, iqm.num_vertexes*4*sizeof(char), 1, iqmFile);
3250
3251 for (int m = 0; m < iqm.num_meshes; m++)
3252 {
3253 int boneCounter = 0;
3254 for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
3255 {
3256 model.meshes[m].boneIds[boneCounter] = blendi[i];
3257 boneCounter++;
3258 }
3259 }
3260 } break;
3261 case IQM_BLENDWEIGHTS:
3262 {
3263 blendw = RL_MALLOC(iqm.num_vertexes*4*sizeof(unsigned char));
3264 fseek(iqmFile, va[i].offset, SEEK_SET);
3265 fread(blendw, iqm.num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
3266
3267 for (int m = 0; m < iqm.num_meshes; m++)
3268 {
3269 int boneCounter = 0;
3270 for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
3271 {
3272 model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f;
3273 boneCounter++;
3274 }
3275 }
3276 } break;
3277 }
3278 }
3279
3280 // Bones (joints) data processing
3281 ijoint = RL_MALLOC(iqm.num_joints*sizeof(IQMJoint));
3282 fseek(iqmFile, iqm.ofs_joints, SEEK_SET);
3283 fread(ijoint, iqm.num_joints*sizeof(IQMJoint), 1, iqmFile);
3284
3285 model.boneCount = iqm.num_joints;
3286 model.bones = RL_MALLOC(iqm.num_joints*sizeof(BoneInfo));
3287 model.bindPose = RL_MALLOC(iqm.num_joints*sizeof(Transform));
3288
3289 for (int i = 0; i < iqm.num_joints; i++)
3290 {
3291 // Bones
3292 model.bones[i].parent = ijoint[i].parent;
3293 fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET);
3294 fread(model.bones[i].name, BONE_NAME_LENGTH*sizeof(char), 1, iqmFile);
3295
3296 // Bind pose (base pose)
3297 model.bindPose[i].translation.x = ijoint[i].translate[0];
3298 model.bindPose[i].translation.y = ijoint[i].translate[1];
3299 model.bindPose[i].translation.z = ijoint[i].translate[2];
3300
3301 model.bindPose[i].rotation.x = ijoint[i].rotate[0];
3302 model.bindPose[i].rotation.y = ijoint[i].rotate[1];
3303 model.bindPose[i].rotation.z = ijoint[i].rotate[2];
3304 model.bindPose[i].rotation.w = ijoint[i].rotate[3];
3305
3306 model.bindPose[i].scale.x = ijoint[i].scale[0];
3307 model.bindPose[i].scale.y = ijoint[i].scale[1];
3308 model.bindPose[i].scale.z = ijoint[i].scale[2];
3309 }
3310
3311 // Build bind pose from parent joints
3312 for (int i = 0; i < model.boneCount; i++)
3313 {
3314 if (model.bones[i].parent >= 0)
3315 {
3316 model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation);
3317 model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation);
3318 model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation);
3319 model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale);
3320 }
3321 }
3322
3323 fclose(iqmFile);
3324 RL_FREE(imesh);
3325 RL_FREE(tri);
3326 RL_FREE(va);
3327 RL_FREE(vertex);
3328 RL_FREE(normal);
3329 RL_FREE(text);
3330 RL_FREE(blendi);
3331 RL_FREE(blendw);
3332 RL_FREE(ijoint);
3333
3334 return model;
3335}
3336#endif
3337
3338#if defined(SUPPORT_FILEFORMAT_GLTF)
3339
3340static const unsigned char base64Table[] = {
3341 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3342 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3343 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3344 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3345 0, 0, 0, 62, 0, 0, 0, 63, 52, 53,
3346 54, 55, 56, 57, 58, 59, 60, 61, 0, 0,
3347 0, 0, 0, 0, 0, 0, 1, 2, 3, 4,
3348 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
3349 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
3350 25, 0, 0, 0, 0, 0, 0, 26, 27, 28,
3351 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
3352 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
3353 49, 50, 51
3354};
3355
3356static int GetSizeBase64(char *input)
3357{
3358 int size = 0;
3359
3360 for (int i = 0; input[4*i] != 0; i++)
3361 {
3362 if (input[4*i + 3] == '=')
3363 {
3364 if (input[4*i + 2] == '=') size += 1;
3365 else size += 2;
3366 }
3367 else size += 3;
3368 }
3369
3370 return size;
3371}
3372
3373static unsigned char *DecodeBase64(char *input, int *size)
3374{
3375 *size = GetSizeBase64(input);
3376
3377 unsigned char *buf = (unsigned char *)RL_MALLOC(*size);
3378 for (int i = 0; i < *size/3; i++)
3379 {
3380 unsigned char a = base64Table[(int)input[4*i]];
3381 unsigned char b = base64Table[(int)input[4*i + 1]];
3382 unsigned char c = base64Table[(int)input[4*i + 2]];
3383 unsigned char d = base64Table[(int)input[4*i + 3]];
3384
3385 buf[3*i] = (a << 2) | (b >> 4);
3386 buf[3*i + 1] = (b << 4) | (c >> 2);
3387 buf[3*i + 2] = (c << 6) | d;
3388 }
3389
3390 if (*size%3 == 1)
3391 {
3392 int n = *size/3;
3393 unsigned char a = base64Table[(int)input[4*n]];
3394 unsigned char b = base64Table[(int)input[4*n + 1]];
3395 buf[*size - 1] = (a << 2) | (b >> 4);
3396 }
3397 else if (*size%3 == 2)
3398 {
3399 int n = *size/3;
3400 unsigned char a = base64Table[(int)input[4*n]];
3401 unsigned char b = base64Table[(int)input[4*n + 1]];
3402 unsigned char c = base64Table[(int)input[4*n + 2]];
3403 buf[*size - 2] = (a << 2) | (b >> 4);
3404 buf[*size - 1] = (b << 4) | (c >> 2);
3405 }
3406 return buf;
3407}
3408
3409// Load texture from cgltf_image
3410static Image LoadImageFromCgltfImage(cgltf_image *image, const char *texPath, Color tint)
3411{
3412 Image rimage = { 0 };
3413
3414 if (image->uri)
3415 {
3416 if ((strlen(image->uri) > 5) &&
3417 (image->uri[0] == 'd') &&
3418 (image->uri[1] == 'a') &&
3419 (image->uri[2] == 't') &&
3420 (image->uri[3] == 'a') &&
3421 (image->uri[4] == ':'))
3422 {
3423 // Data URI
3424 // Format: data:<mediatype>;base64,<data>
3425
3426 // Find the comma
3427 int i = 0;
3428 while ((image->uri[i] != ',') && (image->uri[i] != 0)) i++;
3429
3430 if (image->uri[i] == 0) TRACELOG(LOG_WARNING, "IMAGE: glTF data URI is not a valid image");
3431 else
3432 {
3433 int size = 0;
3434 unsigned char *data = DecodeBase64(image->uri + i + 1, &size);
3435
3436 int width, height;
3437 unsigned char *raw = stbi_load_from_memory(data, size, &width, &height, NULL, 4);
3438 free(data);
3439
3440 rimage.data = raw;
3441 rimage.width = width;
3442 rimage.height = height;
3443 rimage.format = UNCOMPRESSED_R8G8B8A8;
3444 rimage.mipmaps = 1;
3445
3446 // TODO: Tint shouldn't be applied here!
3447 ImageColorTint(&rimage, tint);
3448 }
3449 }
3450 else
3451 {
3452 rimage = LoadImage(TextFormat("%s/%s", texPath, image->uri));
3453
3454 // TODO: Tint shouldn't be applied here!
3455 ImageColorTint(&rimage, tint);
3456 }
3457 }
3458 else if (image->buffer_view)
3459 {
3460 unsigned char *data = RL_MALLOC(image->buffer_view->size);
3461 int n = image->buffer_view->offset;
3462 int stride = image->buffer_view->stride ? image->buffer_view->stride : 1;
3463
3464 for (int i = 0; i < image->buffer_view->size; i++)
3465 {
3466 data[i] = ((unsigned char *)image->buffer_view->buffer->data)[n];
3467 n += stride;
3468 }
3469
3470 int width, height;
3471 unsigned char *raw = stbi_load_from_memory(data, image->buffer_view->size, &width, &height, NULL, 4);
3472 free(data);
3473
3474 rimage.data = raw;
3475 rimage.width = width;
3476 rimage.height = height;
3477 rimage.format = UNCOMPRESSED_R8G8B8A8;
3478 rimage.mipmaps = 1;
3479
3480 // TODO: Tint shouldn't be applied here!
3481 ImageColorTint(&rimage, tint);
3482 }
3483 else rimage = GenImageColor(1, 1, tint);
3484
3485 return rimage;
3486}
3487
3488// LoadGLTF loads in model data from given filename, supporting both .gltf and .glb
3489static Model LoadGLTF(const char *fileName)
3490{
3491 /***********************************************************************************
3492
3493 Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend)
3494
3495 Features:
3496 - Supports .gltf and .glb files
3497 - Supports embedded (base64) or external textures
3498 - Loads all raylib supported material textures, values and colors
3499 - Supports multiple mesh per model and multiple primitives per model
3500
3501 Some restrictions (not exhaustive):
3502 - Triangle-only meshes
3503 - Not supported node hierarchies or transforms
3504 - Only supports unsigned short indices (no byte/unsigned int)
3505 - Only supports float for texture coordinates (no byte/unsigned short)
3506
3507 *************************************************************************************/
3508
3509 #define LOAD_ACCESSOR(type, nbcomp, acc, dst) \
3510 { \
3511 int n = 0; \
3512 type* buf = (type*)acc->buffer_view->buffer->data+acc->buffer_view->offset/sizeof(type)+acc->offset/sizeof(type); \
3513 for (int k = 0; k < acc->count; k++) {\
3514 for (int l = 0; l < nbcomp; l++) {\
3515 dst[nbcomp*k+l] = buf[n+l];\
3516 }\
3517 n += acc->stride/sizeof(type);\
3518 }\
3519 }
3520
3521 Model model = { 0 };
3522
3523 // glTF file loading
3524 FILE *gltfFile = fopen(fileName, "rb");
3525
3526 if (gltfFile == NULL)
3527 {
3528 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open glTF file", fileName);
3529 return model;
3530 }
3531
3532 fseek(gltfFile, 0, SEEK_END);
3533 int size = ftell(gltfFile);
3534 fseek(gltfFile, 0, SEEK_SET);
3535
3536 void *buffer = RL_MALLOC(size);
3537 fread(buffer, size, 1, gltfFile);
3538
3539 fclose(gltfFile);
3540
3541 // glTF data loading
3542 cgltf_options options = { 0 };
3543 cgltf_data *data = NULL;
3544 cgltf_result result = cgltf_parse(&options, buffer, size, &data);
3545
3546 if (result == cgltf_result_success)
3547 {
3548 TRACELOG(LOG_INFO, "MODEL: [%s] glTF meshes (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->meshes_count, data->materials_count);
3549 TRACELOG(LOG_INFO, "MODEL: [%s] glTF materials (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->meshes_count, data->materials_count);
3550
3551 // Read data buffers
3552 result = cgltf_load_buffers(&options, data, fileName);
3553 if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName);
3554
3555 int primitivesCount = 0;
3556
3557 for (int i = 0; i < data->meshes_count; i++) primitivesCount += (int)data->meshes[i].primitives_count;
3558
3559 // Process glTF data and map to model
3560 model.meshCount = primitivesCount;
3561 model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
3562 model.materialCount = data->materials_count + 1;
3563 model.materials = RL_MALLOC(model.materialCount*sizeof(Material));
3564 model.meshMaterial = RL_MALLOC(model.meshCount*sizeof(int));
3565
3566 for (int i = 0; i < model.meshCount; i++) model.meshes[i].vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VBO, sizeof(unsigned int));
3567
3568 for (int i = 0; i < model.materialCount - 1; i++)
3569 {
3570 model.materials[i] = LoadMaterialDefault();
3571 Color tint = (Color){ 255, 255, 255, 255 };
3572 const char *texPath = GetDirectoryPath(fileName);
3573
3574 //Ensure material follows raylib support for PBR (metallic/roughness flow)
3575 if (data->materials[i].has_pbr_metallic_roughness)
3576 {
3577 tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0] * 255);
3578 tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1] * 255);
3579 tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2] * 255);
3580 tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3] * 255);
3581
3582 model.materials[i].maps[MAP_ALBEDO].color = tint;
3583
3584 if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture)
3585 {
3586 Image albedo = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath, tint);
3587 model.materials[i].maps[MAP_ALBEDO].texture = LoadTextureFromImage(albedo);
3588 UnloadImage(albedo);
3589 }
3590
3591 tint = WHITE; // Set tint to white after it's been used by Albedo
3592
3593 if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture)
3594 {
3595 Image metallicRoughness = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath, tint);
3596 model.materials[i].maps[MAP_ROUGHNESS].texture = LoadTextureFromImage(metallicRoughness);
3597
3598 float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor;
3599 model.materials[i].maps[MAP_ROUGHNESS].value = roughness;
3600
3601 float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor;
3602 model.materials[i].maps[MAP_METALNESS].value = metallic;
3603
3604 UnloadImage(metallicRoughness);
3605 }
3606
3607 if (data->materials[i].normal_texture.texture)
3608 {
3609 Image normalImage = LoadImageFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath, tint);
3610 model.materials[i].maps[MAP_NORMAL].texture = LoadTextureFromImage(normalImage);
3611 UnloadImage(normalImage);
3612 }
3613
3614 if (data->materials[i].occlusion_texture.texture)
3615 {
3616 Image occulsionImage = LoadImageFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath, tint);
3617 model.materials[i].maps[MAP_OCCLUSION].texture = LoadTextureFromImage(occulsionImage);
3618 UnloadImage(occulsionImage);
3619 }
3620
3621 if (data->materials[i].emissive_texture.texture)
3622 {
3623 Image emissiveImage = LoadImageFromCgltfImage(data->materials[i].emissive_texture.texture->image, texPath, tint);
3624 model.materials[i].maps[MAP_EMISSION].texture = LoadTextureFromImage(emissiveImage);
3625 tint.r = (unsigned char)(data->materials[i].emissive_factor[0]*255);
3626 tint.g = (unsigned char)(data->materials[i].emissive_factor[1]*255);
3627 tint.b = (unsigned char)(data->materials[i].emissive_factor[2]*255);
3628 model.materials[i].maps[MAP_EMISSION].color = tint;
3629 UnloadImage(emissiveImage);
3630 }
3631 }
3632 }
3633
3634 model.materials[model.materialCount - 1] = LoadMaterialDefault();
3635
3636 int primitiveIndex = 0;
3637
3638 for (int i = 0; i < data->meshes_count; i++)
3639 {
3640 for (int p = 0; p < data->meshes[i].primitives_count; p++)
3641 {
3642 for (int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++)
3643 {
3644 if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position)
3645 {
3646 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
3647 model.meshes[primitiveIndex].vertexCount = acc->count;
3648 model.meshes[primitiveIndex].vertices = RL_MALLOC(sizeof(float)*model.meshes[primitiveIndex].vertexCount*3);
3649
3650 LOAD_ACCESSOR(float, 3, acc, model.meshes[primitiveIndex].vertices)
3651 }
3652 else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_normal)
3653 {
3654 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
3655 model.meshes[primitiveIndex].normals = RL_MALLOC(sizeof(float)*acc->count*3);
3656
3657 LOAD_ACCESSOR(float, 3, acc, model.meshes[primitiveIndex].normals)
3658 }
3659 else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_texcoord)
3660 {
3661 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
3662
3663 if (acc->component_type == cgltf_component_type_r_32f)
3664 {
3665 model.meshes[primitiveIndex].texcoords = RL_MALLOC(sizeof(float)*acc->count*2);
3666 LOAD_ACCESSOR(float, 2, acc, model.meshes[primitiveIndex].texcoords)
3667 }
3668 else
3669 {
3670 // TODO: Support normalized unsigned byte/unsigned short texture coordinates
3671 TRACELOG(LOG_WARNING, "MODEL: [%s] glTF texture coordinates must be float", fileName);
3672 }
3673 }
3674 }
3675
3676 cgltf_accessor *acc = data->meshes[i].primitives[p].indices;
3677
3678 if (acc)
3679 {
3680 if (acc->component_type == cgltf_component_type_r_16u)
3681 {
3682 model.meshes[primitiveIndex].triangleCount = acc->count/3;
3683 model.meshes[primitiveIndex].indices = RL_MALLOC(sizeof(unsigned short)*model.meshes[primitiveIndex].triangleCount*3);
3684 LOAD_ACCESSOR(unsigned short, 1, acc, model.meshes[primitiveIndex].indices)
3685 }
3686 else
3687 {
3688 // TODO: Support unsigned byte/unsigned int
3689 TRACELOG(LOG_WARNING, "MODEL: [%s] glTF index data must be unsigned short", fileName);
3690 }
3691 }
3692 else
3693 {
3694 // Unindexed mesh
3695 model.meshes[primitiveIndex].triangleCount = model.meshes[primitiveIndex].vertexCount/3;
3696 }
3697
3698 if (data->meshes[i].primitives[p].material)
3699 {
3700 // Compute the offset
3701 model.meshMaterial[primitiveIndex] = data->meshes[i].primitives[p].material - data->materials;
3702 }
3703 else
3704 {
3705 model.meshMaterial[primitiveIndex] = model.materialCount - 1;;
3706 }
3707
3708 primitiveIndex++;
3709 }
3710 }
3711
3712 cgltf_free(data);
3713 }
3714 else TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName);
3715
3716 RL_FREE(buffer);
3717
3718 return model;
3719}
3720#endif
3721