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-2021 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: LoadFileData(), LoadFileText(), SaveFileText()
47
48 #include <stdio.h> // Required for: sprintf()
49 #include <stdlib.h> // Required for: malloc(), free()
50 #include <string.h> // Required for: memcmp(), strlen()
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 // ...
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)
111 static Model LoadOBJ(const char *fileName); // Load OBJ mesh data
112 #endif
113 #if defined(SUPPORT_FILEFORMAT_IQM)
114 static Model LoadIQM(const char *fileName); // Load IQM mesh data
115 static ModelAnimation *LoadIQMModelAnimations(const char *fileName, int *animCount); // Load IQM animation data
116 #endif
117 #if defined(SUPPORT_FILEFORMAT_GLTF)
118 static Model LoadGLTF(const char *fileName); // Load GLTF mesh data
119 static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCount); // Load GLTF animation data
120 static void LoadGLTFModelIndices(Model* model, cgltf_accessor* indexAccessor, int primitiveIndex);
121 static void BindGLTFPrimitiveToBones(Model* model, const cgltf_data* data, int primitiveIndex);
122 static void LoadGLTFBoneAttribute(Model* model, cgltf_accessor* jointsAccessor, const cgltf_data* data, int primitiveIndex);
123 static void LoadGLTFMaterial(Model* model, const char* fileName, const cgltf_data* data);
124 static void InitGLTFBones(Model* model, const cgltf_data* data);
125 #endif
126
127 //----------------------------------------------------------------------------------
128 // Module Functions Definition
129 //----------------------------------------------------------------------------------
130
131 // Draw a line in 3D world space
DrawLine3D(Vector3 startPos,Vector3 endPos,Color color)132 void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color)
133 {
134 // WARNING: Be careful with internal buffer vertex alignment
135 // when using RL_LINES or RL_TRIANGLES, data is aligned to fit
136 // lines-triangles-quads in the same indexed buffers!!!
137 rlCheckRenderBatchLimit(8);
138
139 rlBegin(RL_LINES);
140 rlColor4ub(color.r, color.g, color.b, color.a);
141 rlVertex3f(startPos.x, startPos.y, startPos.z);
142 rlVertex3f(endPos.x, endPos.y, endPos.z);
143 rlEnd();
144 }
145
146 // Draw a point in 3D space, actually a small line
DrawPoint3D(Vector3 position,Color color)147 void DrawPoint3D(Vector3 position, Color color)
148 {
149 rlCheckRenderBatchLimit(8);
150
151 rlPushMatrix();
152 rlTranslatef(position.x, position.y, position.z);
153 rlBegin(RL_LINES);
154 rlColor4ub(color.r, color.g, color.b, color.a);
155 rlVertex3f(0.0f, 0.0f, 0.0f);
156 rlVertex3f(0.0f, 0.0f, 0.1f);
157 rlEnd();
158 rlPopMatrix();
159 }
160
161 // Draw a circle in 3D world space
DrawCircle3D(Vector3 center,float radius,Vector3 rotationAxis,float rotationAngle,Color color)162 void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color)
163 {
164 rlCheckRenderBatchLimit(2*36);
165
166 rlPushMatrix();
167 rlTranslatef(center.x, center.y, center.z);
168 rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
169
170 rlBegin(RL_LINES);
171 for (int i = 0; i < 360; i += 10)
172 {
173 rlColor4ub(color.r, color.g, color.b, color.a);
174
175 rlVertex3f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius, 0.0f);
176 rlVertex3f(sinf(DEG2RAD*(i + 10))*radius, cosf(DEG2RAD*(i + 10))*radius, 0.0f);
177 }
178 rlEnd();
179 rlPopMatrix();
180 }
181
182 // Draw a color-filled triangle (vertex in counter-clockwise order!)
DrawTriangle3D(Vector3 v1,Vector3 v2,Vector3 v3,Color color)183 void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color)
184 {
185 rlCheckRenderBatchLimit(3);
186
187 rlBegin(RL_TRIANGLES);
188 rlColor4ub(color.r, color.g, color.b, color.a);
189 rlVertex3f(v1.x, v1.y, v1.z);
190 rlVertex3f(v2.x, v2.y, v2.z);
191 rlVertex3f(v3.x, v3.y, v3.z);
192 rlEnd();
193 }
194
195 // Draw a triangle strip defined by points
DrawTriangleStrip3D(Vector3 * points,int pointsCount,Color color)196 void DrawTriangleStrip3D(Vector3 *points, int pointsCount, Color color)
197 {
198 if (pointsCount >= 3)
199 {
200 rlCheckRenderBatchLimit(3*(pointsCount - 2));
201
202 rlBegin(RL_TRIANGLES);
203 rlColor4ub(color.r, color.g, color.b, color.a);
204
205 for (int i = 2; i < pointsCount; i++)
206 {
207 if ((i%2) == 0)
208 {
209 rlVertex3f(points[i].x, points[i].y, points[i].z);
210 rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z);
211 rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z);
212 }
213 else
214 {
215 rlVertex3f(points[i].x, points[i].y, points[i].z);
216 rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z);
217 rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z);
218 }
219 }
220 rlEnd();
221 }
222 }
223
224 // Draw cube
225 // NOTE: Cube position is the center position
DrawCube(Vector3 position,float width,float height,float length,Color color)226 void DrawCube(Vector3 position, float width, float height, float length, Color color)
227 {
228 float x = 0.0f;
229 float y = 0.0f;
230 float z = 0.0f;
231
232 rlCheckRenderBatchLimit(36);
233
234 rlPushMatrix();
235 // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
236 rlTranslatef(position.x, position.y, position.z);
237 //rlRotatef(45, 0, 1, 0);
238 //rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition
239
240 rlBegin(RL_TRIANGLES);
241 rlColor4ub(color.r, color.g, color.b, color.a);
242
243 // Front face
244 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
245 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
246 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
247
248 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right
249 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
250 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
251
252 // Back face
253 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left
254 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
255 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
256
257 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
258 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
259 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
260
261 // Top face
262 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
263 rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left
264 rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
265
266 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
267 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
268 rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
269
270 // Bottom face
271 rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
272 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
273 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
274
275 rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right
276 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
277 rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
278
279 // Right face
280 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
281 rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
282 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
283
284 rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left
285 rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
286 rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
287
288 // Left face
289 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
290 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
291 rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right
292
293 rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
294 rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
295 rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
296 rlEnd();
297 rlPopMatrix();
298 }
299
300 // Draw cube (Vector version)
DrawCubeV(Vector3 position,Vector3 size,Color color)301 void DrawCubeV(Vector3 position, Vector3 size, Color color)
302 {
303 DrawCube(position, size.x, size.y, size.z, color);
304 }
305
306 // Draw cube wires
DrawCubeWires(Vector3 position,float width,float height,float length,Color color)307 void DrawCubeWires(Vector3 position, float width, float height, float length, Color color)
308 {
309 float x = 0.0f;
310 float y = 0.0f;
311 float z = 0.0f;
312
313 rlCheckRenderBatchLimit(36);
314
315 rlPushMatrix();
316 rlTranslatef(position.x, position.y, position.z);
317
318 rlBegin(RL_LINES);
319 rlColor4ub(color.r, color.g, color.b, color.a);
320
321 // Front Face -----------------------------------------------------
322 // Bottom Line
323 rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
324 rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
325
326 // Left Line
327 rlVertex3f(x+width/2, y-height/2, z+length/2); // Bottom Right
328 rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right
329
330 // Top Line
331 rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right
332 rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
333
334 // Right Line
335 rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left
336 rlVertex3f(x-width/2, y-height/2, z+length/2); // Bottom Left
337
338 // Back Face ------------------------------------------------------
339 // Bottom Line
340 rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left
341 rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
342
343 // Left Line
344 rlVertex3f(x+width/2, y-height/2, z-length/2); // Bottom Right
345 rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
346
347 // Top Line
348 rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right
349 rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
350
351 // Right Line
352 rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left
353 rlVertex3f(x-width/2, y-height/2, z-length/2); // Bottom Left
354
355 // Top Face -------------------------------------------------------
356 // Left Line
357 rlVertex3f(x-width/2, y+height/2, z+length/2); // Top Left Front
358 rlVertex3f(x-width/2, y+height/2, z-length/2); // Top Left Back
359
360 // Right Line
361 rlVertex3f(x+width/2, y+height/2, z+length/2); // Top Right Front
362 rlVertex3f(x+width/2, y+height/2, z-length/2); // Top Right Back
363
364 // Bottom Face ---------------------------------------------------
365 // Left Line
366 rlVertex3f(x-width/2, y-height/2, z+length/2); // Top Left Front
367 rlVertex3f(x-width/2, y-height/2, z-length/2); // Top Left Back
368
369 // Right Line
370 rlVertex3f(x+width/2, y-height/2, z+length/2); // Top Right Front
371 rlVertex3f(x+width/2, y-height/2, z-length/2); // Top Right Back
372 rlEnd();
373 rlPopMatrix();
374 }
375
376 // Draw cube wires (vector version)
DrawCubeWiresV(Vector3 position,Vector3 size,Color color)377 void DrawCubeWiresV(Vector3 position, Vector3 size, Color color)
378 {
379 DrawCubeWires(position, size.x, size.y, size.z, color);
380 }
381
382 // Draw cube
383 // NOTE: Cube position is the center position
DrawCubeTexture(Texture2D texture,Vector3 position,float width,float height,float length,Color color)384 void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color)
385 {
386 float x = position.x;
387 float y = position.y;
388 float z = position.z;
389
390 rlCheckRenderBatchLimit(36);
391
392 rlSetTexture(texture.id);
393
394 //rlPushMatrix();
395 // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
396 //rlTranslatef(2.0f, 0.0f, 0.0f);
397 //rlRotatef(45, 0, 1, 0);
398 //rlScalef(2.0f, 2.0f, 2.0f);
399
400 rlBegin(RL_QUADS);
401 rlColor4ub(color.r, color.g, color.b, color.a);
402 // Front Face
403 rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer
404 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad
405 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad
406 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad
407 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad
408 // Back Face
409 rlNormal3f(0.0f, 0.0f, - 1.0f); // Normal Pointing Away From Viewer
410 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad
411 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad
412 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad
413 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad
414 // Top Face
415 rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
416 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad
417 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left Of The Texture and Quad
418 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right Of The Texture and Quad
419 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad
420 // Bottom Face
421 rlNormal3f(0.0f, - 1.0f, 0.0f); // Normal Pointing Down
422 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Right Of The Texture and Quad
423 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Left Of The Texture and Quad
424 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad
425 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad
426 // Right face
427 rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right
428 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad
429 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad
430 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad
431 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad
432 // Left Face
433 rlNormal3f( - 1.0f, 0.0f, 0.0f); // Normal Pointing Left
434 rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad
435 rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad
436 rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad
437 rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad
438 rlEnd();
439 //rlPopMatrix();
440
441 rlSetTexture(0);
442 }
443
444 // Draw sphere
DrawSphere(Vector3 centerPos,float radius,Color color)445 void DrawSphere(Vector3 centerPos, float radius, Color color)
446 {
447 DrawSphereEx(centerPos, radius, 16, 16, color);
448 }
449
450 // Draw sphere with extended parameters
DrawSphereEx(Vector3 centerPos,float radius,int rings,int slices,Color color)451 void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color)
452 {
453 int numVertex = (rings + 2)*slices*6;
454 rlCheckRenderBatchLimit(numVertex);
455
456 rlPushMatrix();
457 // NOTE: Transformation is applied in inverse order (scale -> translate)
458 rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
459 rlScalef(radius, radius, radius);
460
461 rlBegin(RL_TRIANGLES);
462 rlColor4ub(color.r, color.g, color.b, color.a);
463
464 for (int i = 0; i < (rings + 2); i++)
465 {
466 for (int j = 0; j < slices; j++)
467 {
468 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
469 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
470 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
471 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
472 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
473 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
474 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
475 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
476 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
477
478 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
479 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
480 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
481 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*sinf(DEG2RAD*((j+1)*360/slices)),
482 sinf(DEG2RAD*(270+(180/(rings + 1))*(i))),
483 cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*cosf(DEG2RAD*((j+1)*360/slices)));
484 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
485 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
486 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
487 }
488 }
489 rlEnd();
490 rlPopMatrix();
491 }
492
493 // Draw sphere wires
DrawSphereWires(Vector3 centerPos,float radius,int rings,int slices,Color color)494 void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color)
495 {
496 int numVertex = (rings + 2)*slices*6;
497 rlCheckRenderBatchLimit(numVertex);
498
499 rlPushMatrix();
500 // NOTE: Transformation is applied in inverse order (scale -> translate)
501 rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
502 rlScalef(radius, radius, radius);
503
504 rlBegin(RL_LINES);
505 rlColor4ub(color.r, color.g, color.b, color.a);
506
507 for (int i = 0; i < (rings + 2); i++)
508 {
509 for (int j = 0; j < slices; j++)
510 {
511 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
512 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
513 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
514 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
515 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
516 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
517
518 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
519 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
520 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
521 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
522 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
523 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
524
525 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
526 sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
527 cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
528 rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
529 sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
530 cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
531 }
532 }
533 rlEnd();
534 rlPopMatrix();
535 }
536
537 // Draw a cylinder
538 // NOTE: It could be also used for pyramid and cone
DrawCylinder(Vector3 position,float radiusTop,float radiusBottom,float height,int sides,Color color)539 void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
540 {
541 if (sides < 3) sides = 3;
542
543 int numVertex = sides*6;
544 rlCheckRenderBatchLimit(numVertex);
545
546 rlPushMatrix();
547 rlTranslatef(position.x, position.y, position.z);
548
549 rlBegin(RL_TRIANGLES);
550 rlColor4ub(color.r, color.g, color.b, color.a);
551
552 if (radiusTop > 0)
553 {
554 // Draw Body -------------------------------------------------------------------------------------
555 for (int i = 0; i < 360; i += 360/sides)
556 {
557 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left
558 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom); //Bottom Right
559 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); //Top Right
560
561 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop); //Top Left
562 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left
563 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop); //Top Right
564 }
565
566 // Draw Cap --------------------------------------------------------------------------------------
567 for (int i = 0; i < 360; i += 360/sides)
568 {
569 rlVertex3f(0, height, 0);
570 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
571 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop);
572 }
573 }
574 else
575 {
576 // Draw Cone -------------------------------------------------------------------------------------
577 for (int i = 0; i < 360; i += 360/sides)
578 {
579 rlVertex3f(0, height, 0);
580 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
581 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
582 }
583 }
584
585 // Draw Base -----------------------------------------------------------------------------------------
586 for (int i = 0; i < 360; i += 360/sides)
587 {
588 rlVertex3f(0, 0, 0);
589 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
590 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
591 }
592 rlEnd();
593 rlPopMatrix();
594 }
595
596 // Draw a wired cylinder
597 // NOTE: It could be also used for pyramid and cone
DrawCylinderWires(Vector3 position,float radiusTop,float radiusBottom,float height,int sides,Color color)598 void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
599 {
600 if (sides < 3) sides = 3;
601
602 int numVertex = sides*8;
603 rlCheckRenderBatchLimit(numVertex);
604
605 rlPushMatrix();
606 rlTranslatef(position.x, position.y, position.z);
607
608 rlBegin(RL_LINES);
609 rlColor4ub(color.r, color.g, color.b, color.a);
610
611 for (int i = 0; i < 360; i += 360/sides)
612 {
613 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
614 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
615
616 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i + 360/sides))*radiusBottom);
617 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop);
618
619 rlVertex3f(sinf(DEG2RAD*(i + 360/sides))*radiusTop, height, cosf(DEG2RAD*(i + 360/sides))*radiusTop);
620 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
621
622 rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
623 rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
624 }
625 rlEnd();
626 rlPopMatrix();
627 }
628
629 // Draw a plane
DrawPlane(Vector3 centerPos,Vector2 size,Color color)630 void DrawPlane(Vector3 centerPos, Vector2 size, Color color)
631 {
632 rlCheckRenderBatchLimit(4);
633
634 // NOTE: Plane is always created on XZ ground
635 rlPushMatrix();
636 rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
637 rlScalef(size.x, 1.0f, size.y);
638
639 rlBegin(RL_QUADS);
640 rlColor4ub(color.r, color.g, color.b, color.a);
641 rlNormal3f(0.0f, 1.0f, 0.0f);
642
643 rlVertex3f(-0.5f, 0.0f, -0.5f);
644 rlVertex3f(-0.5f, 0.0f, 0.5f);
645 rlVertex3f(0.5f, 0.0f, 0.5f);
646 rlVertex3f(0.5f, 0.0f, -0.5f);
647 rlEnd();
648 rlPopMatrix();
649 }
650
651 // Draw a ray line
DrawRay(Ray ray,Color color)652 void DrawRay(Ray ray, Color color)
653 {
654 float scale = 10000;
655
656 rlBegin(RL_LINES);
657 rlColor4ub(color.r, color.g, color.b, color.a);
658 rlColor4ub(color.r, color.g, color.b, color.a);
659
660 rlVertex3f(ray.position.x, ray.position.y, ray.position.z);
661 rlVertex3f(ray.position.x + ray.direction.x*scale, ray.position.y + ray.direction.y*scale, ray.position.z + ray.direction.z*scale);
662 rlEnd();
663 }
664
665 // Draw a grid centered at (0, 0, 0)
DrawGrid(int slices,float spacing)666 void DrawGrid(int slices, float spacing)
667 {
668 int halfSlices = slices/2;
669
670 rlCheckRenderBatchLimit((slices + 2)*4);
671
672 rlBegin(RL_LINES);
673 for (int i = -halfSlices; i <= halfSlices; i++)
674 {
675 if (i == 0)
676 {
677 rlColor3f(0.5f, 0.5f, 0.5f);
678 rlColor3f(0.5f, 0.5f, 0.5f);
679 rlColor3f(0.5f, 0.5f, 0.5f);
680 rlColor3f(0.5f, 0.5f, 0.5f);
681 }
682 else
683 {
684 rlColor3f(0.75f, 0.75f, 0.75f);
685 rlColor3f(0.75f, 0.75f, 0.75f);
686 rlColor3f(0.75f, 0.75f, 0.75f);
687 rlColor3f(0.75f, 0.75f, 0.75f);
688 }
689
690 rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing);
691 rlVertex3f((float)i*spacing, 0.0f, (float)halfSlices*spacing);
692
693 rlVertex3f((float)-halfSlices*spacing, 0.0f, (float)i*spacing);
694 rlVertex3f((float)halfSlices*spacing, 0.0f, (float)i*spacing);
695 }
696 rlEnd();
697 }
698
699 // Load model from files (mesh and material)
LoadModel(const char * fileName)700 Model LoadModel(const char *fileName)
701 {
702 Model model = { 0 };
703
704 #if defined(SUPPORT_FILEFORMAT_OBJ)
705 if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName);
706 #endif
707 #if defined(SUPPORT_FILEFORMAT_IQM)
708 if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName);
709 #endif
710 #if defined(SUPPORT_FILEFORMAT_GLTF)
711 if (IsFileExtension(fileName, ".gltf;.glb")) model = LoadGLTF(fileName);
712 #endif
713
714 // Make sure model transform is set to identity matrix!
715 model.transform = MatrixIdentity();
716
717 if (model.meshCount == 0)
718 {
719 model.meshCount = 1;
720 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
721 #if defined(SUPPORT_MESH_GENERATION)
722 TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data, default to cube mesh", fileName);
723 model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f);
724 #else
725 TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data", fileName);
726 #endif
727 }
728 else
729 {
730 // Upload vertex data to GPU (static mesh)
731 for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false);
732 }
733
734 if (model.materialCount == 0)
735 {
736 TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load material data, default to white material", fileName);
737
738 model.materialCount = 1;
739 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
740 model.materials[0] = LoadMaterialDefault();
741
742 if (model.meshMaterial == NULL) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
743 }
744
745 return model;
746 }
747
748 // Load model from generated mesh
749 // WARNING: A shallow copy of mesh is generated, passed by value,
750 // as long as struct contains pointers to data and some values, we get a copy
751 // of mesh pointing to same data as original version... be careful!
LoadModelFromMesh(Mesh mesh)752 Model LoadModelFromMesh(Mesh mesh)
753 {
754 Model model = { 0 };
755
756 model.transform = MatrixIdentity();
757
758 model.meshCount = 1;
759 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
760 model.meshes[0] = mesh;
761
762 model.materialCount = 1;
763 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
764 model.materials[0] = LoadMaterialDefault();
765
766 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
767 model.meshMaterial[0] = 0; // First material index
768
769 return model;
770 }
771
772 // Unload model (meshes/materials) from memory (RAM and/or VRAM)
773 // NOTE: This function takes care of all model elements, for a detailed control
774 // over them, use UnloadMesh() and UnloadMaterial()
UnloadModel(Model model)775 void UnloadModel(Model model)
776 {
777 // Unload meshes
778 for (int i = 0; i < model.meshCount; i++) UnloadMesh(model.meshes[i]);
779
780 // Unload materials maps
781 // NOTE: As the user could be sharing shaders and textures between models,
782 // we don't unload the material but just free it's maps,
783 // the user is responsible for freeing models shaders and textures
784 for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
785
786 // Unload arrays
787 RL_FREE(model.meshes);
788 RL_FREE(model.materials);
789 RL_FREE(model.meshMaterial);
790
791 // Unload animation data
792 RL_FREE(model.bones);
793 RL_FREE(model.bindPose);
794
795 TRACELOG(LOG_INFO, "MODEL: Unloaded model (and meshes) from RAM and VRAM");
796 }
797
798 // Unload model (but not meshes) from memory (RAM and/or VRAM)
UnloadModelKeepMeshes(Model model)799 void UnloadModelKeepMeshes(Model model)
800 {
801 // Unload materials maps
802 // NOTE: As the user could be sharing shaders and textures between models,
803 // we don't unload the material but just free it's maps,
804 // the user is responsible for freeing models shaders and textures
805 for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
806
807 // Unload arrays
808 RL_FREE(model.meshes);
809 RL_FREE(model.materials);
810 RL_FREE(model.meshMaterial);
811
812 // Unload animation data
813 RL_FREE(model.bones);
814 RL_FREE(model.bindPose);
815
816 TRACELOG(LOG_INFO, "MODEL: Unloaded model (but not meshes) from RAM and VRAM");
817 }
818
819 // Upload vertex data into a VAO (if supported) and VBO
UploadMesh(Mesh * mesh,bool dynamic)820 void UploadMesh(Mesh *mesh, bool dynamic)
821 {
822 if (mesh->vaoId > 0)
823 {
824 // Check if mesh has already been loaded in GPU
825 TRACELOG(LOG_WARNING, "VAO: [ID %i] Trying to re-load an already loaded mesh", mesh->vaoId);
826 return;
827 }
828
829 mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int));
830
831 mesh->vaoId = 0; // Vertex Array Object
832 mesh->vboId[0] = 0; // Vertex buffer: positions
833 mesh->vboId[1] = 0; // Vertex buffer: texcoords
834 mesh->vboId[2] = 0; // Vertex buffer: normals
835 mesh->vboId[3] = 0; // Vertex buffer: colors
836 mesh->vboId[4] = 0; // Vertex buffer: tangents
837 mesh->vboId[5] = 0; // Vertex buffer: texcoords2
838 mesh->vboId[6] = 0; // Vertex buffer: indices
839
840 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
841 mesh->vaoId = rlLoadVertexArray();
842 rlEnableVertexArray(mesh->vaoId);
843
844 // NOTE: Attributes must be uploaded considering default locations points
845
846 // Enable vertex attributes: position (shader-location = 0)
847 mesh->vboId[0] = rlLoadVertexBuffer(mesh->vertices, mesh->vertexCount*3*sizeof(float), dynamic);
848 rlSetVertexAttribute(0, 3, RL_FLOAT, 0, 0, 0);
849 rlEnableVertexAttribute(0);
850
851 // Enable vertex attributes: texcoords (shader-location = 1)
852 mesh->vboId[1] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic);
853 rlSetVertexAttribute(1, 2, RL_FLOAT, 0, 0, 0);
854 rlEnableVertexAttribute(1);
855
856 if (mesh->normals != NULL)
857 {
858 // Enable vertex attributes: normals (shader-location = 2)
859 mesh->vboId[2] = rlLoadVertexBuffer(mesh->normals, mesh->vertexCount*3*sizeof(float), dynamic);
860 rlSetVertexAttribute(2, 3, RL_FLOAT, 0, 0, 0);
861 rlEnableVertexAttribute(2);
862 }
863 else
864 {
865 // Default color vertex attribute set to WHITE
866 float value[3] = { 1.0f, 1.0f, 1.0f };
867 rlSetVertexAttributeDefault(2, value, SHADER_ATTRIB_VEC3, 3);
868 rlDisableVertexAttribute(2);
869 }
870
871 if (mesh->colors != NULL)
872 {
873 // Enable vertex attribute: color (shader-location = 3)
874 mesh->vboId[3] = rlLoadVertexBuffer(mesh->colors, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
875 rlSetVertexAttribute(3, 4, RL_UNSIGNED_BYTE, 1, 0, 0);
876 rlEnableVertexAttribute(3);
877 }
878 else
879 {
880 // Default color vertex attribute set to WHITE
881 float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
882 rlSetVertexAttributeDefault(3, value, SHADER_ATTRIB_VEC4, 4);
883 rlDisableVertexAttribute(3);
884 }
885
886 if (mesh->tangents != NULL)
887 {
888 // Enable vertex attribute: tangent (shader-location = 4)
889 mesh->vboId[4] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), dynamic);
890 rlSetVertexAttribute(4, 4, RL_FLOAT, 0, 0, 0);
891 rlEnableVertexAttribute(4);
892 }
893 else
894 {
895 // Default tangents vertex attribute
896 float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
897 rlSetVertexAttributeDefault(4, value, SHADER_ATTRIB_VEC4, 4);
898 rlDisableVertexAttribute(4);
899 }
900
901 if (mesh->texcoords2 != NULL)
902 {
903 // Enable vertex attribute: texcoord2 (shader-location = 5)
904 mesh->vboId[5] = rlLoadVertexBuffer(mesh->texcoords2, mesh->vertexCount*2*sizeof(float), dynamic);
905 rlSetVertexAttribute(5, 2, RL_FLOAT, 0, 0, 0);
906 rlEnableVertexAttribute(5);
907 }
908 else
909 {
910 // Default texcoord2 vertex attribute
911 float value[2] = { 0.0f, 0.0f };
912 rlSetVertexAttributeDefault(5, value, SHADER_ATTRIB_VEC2, 2);
913 rlDisableVertexAttribute(5);
914 }
915
916 if (mesh->indices != NULL)
917 {
918 mesh->vboId[6] = rlLoadVertexBufferElement(mesh->indices, mesh->triangleCount*3*sizeof(unsigned short), dynamic);
919 }
920
921 if (mesh->vaoId > 0) TRACELOG(LOG_INFO, "VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId);
922 else TRACELOG(LOG_INFO, "VBO: Mesh uploaded successfully to VRAM (GPU)");
923
924 rlDisableVertexArray();
925 #endif
926 }
927
928 // Update mesh vertex data in GPU for a specific buffer index
UpdateMeshBuffer(Mesh mesh,int index,void * data,int dataSize,int offset)929 void UpdateMeshBuffer(Mesh mesh, int index, void *data, int dataSize, int offset)
930 {
931 rlUpdateVertexBuffer(mesh.vboId[index], data, dataSize, offset);
932 }
933
934 // Draw a 3d mesh with material and transform
DrawMesh(Mesh mesh,Material material,Matrix transform)935 void DrawMesh(Mesh mesh, Material material, Matrix transform)
936 {
937 DrawMeshInstanced(mesh, material, &transform, 1);
938 }
939
940 // Draw multiple mesh instances with material and different transforms
DrawMeshInstanced(Mesh mesh,Material material,Matrix * transforms,int instances)941 void DrawMeshInstanced(Mesh mesh, Material material, Matrix *transforms, int instances)
942 {
943 #if defined(GRAPHICS_API_OPENGL_11)
944 #define GL_VERTEX_ARRAY 0x8074
945 #define GL_NORMAL_ARRAY 0x8075
946 #define GL_COLOR_ARRAY 0x8076
947 #define GL_TEXTURE_COORD_ARRAY 0x8078
948
949 rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
950
951 rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices);
952 rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords);
953 rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals);
954 rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors);
955
956 rlPushMatrix();
957 rlMultMatrixf(MatrixToFloat(transforms[0]));
958 rlColor4ub(material.maps[MATERIAL_MAP_DIFFUSE].color.r,
959 material.maps[MATERIAL_MAP_DIFFUSE].color.g,
960 material.maps[MATERIAL_MAP_DIFFUSE].color.b,
961 material.maps[MATERIAL_MAP_DIFFUSE].color.a);
962
963 if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, mesh.indices);
964 else rlDrawVertexArray(0, mesh.vertexCount);
965 rlPopMatrix();
966
967 rlDisableStatePointer(GL_VERTEX_ARRAY);
968 rlDisableStatePointer(GL_TEXTURE_COORD_ARRAY);
969 rlDisableStatePointer(GL_NORMAL_ARRAY);
970 rlDisableStatePointer(GL_COLOR_ARRAY);
971
972 rlDisableTexture();
973 #endif
974
975 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
976 // Check instancing
977 bool instancing = false;
978 if (instances < 1) return;
979 else if (instances > 1) instancing = true;
980 float16 *instanceTransforms = NULL;
981 unsigned int instancesVboId = 0;
982
983 // Bind shader program
984 rlEnableShader(material.shader.id);
985
986 // Send required data to shader (matrices, values)
987 //-----------------------------------------------------
988 // Upload to shader material.colDiffuse
989 if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1)
990 {
991 float values[4] = {
992 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f,
993 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f,
994 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f,
995 (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f
996 };
997
998 rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1);
999 }
1000
1001 // Upload to shader material.colSpecular (if location available)
1002 if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1)
1003 {
1004 float values[4] = {
1005 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.r/255.0f,
1006 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.g/255.0f,
1007 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.b/255.0f,
1008 (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.a/255.0f
1009 };
1010
1011 rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1);
1012 }
1013
1014 // Get a copy of current matrices to work with,
1015 // just in case stereo render is required and we need to modify them
1016 // NOTE: At this point the modelview matrix just contains the view matrix (camera)
1017 // That's because BeginMode3D() sets it and there is no model-drawing function
1018 // that modifies it, all use rlPushMatrix() and rlPopMatrix()
1019 Matrix matView = rlGetMatrixModelview();
1020 Matrix matModelView = matView;
1021 Matrix matProjection = rlGetMatrixProjection();
1022
1023 // Upload view and projection matrices (if locations available)
1024 if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
1025 if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
1026
1027 if (instancing)
1028 {
1029 // Create instances buffer
1030 instanceTransforms = RL_MALLOC(instances*sizeof(float16));
1031
1032 // Fill buffer with instances transformations as float16 arrays
1033 for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]);
1034
1035 // Enable mesh VAO to attach new buffer
1036 rlEnableVertexArray(mesh.vaoId);
1037
1038 // This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData().
1039 // It isn't clear which would be reliably faster in all cases and on all platforms,
1040 // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems
1041 // no faster, since we're transferring all the transform matrices anyway
1042 instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false);
1043
1044 // Instances transformation matrices are send to shader attribute location: SHADER_LOC_MATRIX_MODEL
1045 for (unsigned int i = 0; i < 4; i++)
1046 {
1047 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i);
1048 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 4, RL_FLOAT, 0, sizeof(Matrix), (void *)(i*sizeof(Vector4)));
1049 rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 1);
1050 }
1051
1052 rlDisableVertexBuffer();
1053 rlDisableVertexArray();
1054
1055 // Accumulate internal matrix transform (push/pop) and view matrix
1056 // NOTE: In this case, model instance transformation must be computed in the shader
1057 matModelView = MatrixMultiply(rlGetMatrixTransform(), matView);
1058 }
1059 else
1060 {
1061 // Model transformation matrix is send to shader uniform location: SHADER_LOC_MATRIX_MODEL
1062 if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], transforms[0]);
1063
1064 // Accumulate several transformations:
1065 // matView: rlgl internal modelview matrix (actually, just view matrix)
1066 // rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack
1067 // transform: function parameter transformation
1068 matModelView = MatrixMultiply(transforms[0], MatrixMultiply(rlGetMatrixTransform(), matView));
1069 }
1070
1071 // Upload model normal matrix (if locations available)
1072 if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModelView)));
1073 //-----------------------------------------------------
1074
1075 // Bind active texture maps (if available)
1076 for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
1077 {
1078 if (material.maps[i].texture.id > 0)
1079 {
1080 // Select current shader texture slot
1081 rlActiveTextureSlot(i);
1082
1083 // Enable texture for active slot
1084 if ((i == MATERIAL_MAP_IRRADIANCE) ||
1085 (i == MATERIAL_MAP_PREFILTER) ||
1086 (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id);
1087 else rlEnableTexture(material.maps[i].texture.id);
1088
1089 rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1);
1090 }
1091 }
1092
1093 // Try binding vertex array objects (VAO)
1094 // or use VBOs if not possible
1095 if (!rlEnableVertexArray(mesh.vaoId))
1096 {
1097 // Bind mesh VBO data: vertex position (shader-location = 0)
1098 rlEnableVertexBuffer(mesh.vboId[0]);
1099 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
1100 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
1101
1102 rlEnableVertexBuffer(mesh.vboId[0]);
1103 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
1104 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
1105
1106 // Bind mesh VBO data: vertex texcoords (shader-location = 1)
1107 rlEnableVertexBuffer(mesh.vboId[1]);
1108 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0);
1109 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]);
1110
1111 if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1)
1112 {
1113 // Bind mesh VBO data: vertex normals (shader-location = 2)
1114 rlEnableVertexBuffer(mesh.vboId[2]);
1115 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0);
1116 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]);
1117 }
1118
1119 // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
1120 if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1)
1121 {
1122 if (mesh.vboId[3] != 0)
1123 {
1124 rlEnableVertexBuffer(mesh.vboId[3]);
1125 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0);
1126 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
1127 }
1128 else
1129 {
1130 // Set default value for unused attribute
1131 // NOTE: Required when using default shader and no VAO support
1132 float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
1133 rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC2, 4);
1134 rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
1135 }
1136 }
1137
1138 // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
1139 if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1)
1140 {
1141 rlEnableVertexBuffer(mesh.vboId[4]);
1142 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0);
1143 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]);
1144 }
1145
1146 // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
1147 if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1)
1148 {
1149 rlEnableVertexBuffer(mesh.vboId[5]);
1150 rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0);
1151 rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
1152 }
1153
1154 if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[6]);
1155 }
1156
1157 int eyesCount = 1;
1158 if (rlIsStereoRenderEnabled()) eyesCount = 2;
1159
1160 for (int eye = 0; eye < eyesCount; eye++)
1161 {
1162 // Calculate model-view-projection matrix (MVP)
1163 Matrix matMVP = MatrixIdentity();
1164 if (eyesCount == 1) matMVP = MatrixMultiply(matModelView, matProjection);
1165 else
1166 {
1167 // Setup current eye viewport (half screen width)
1168 rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight());
1169 matMVP = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye));
1170 }
1171
1172 // Send combined model-view-projection matrix to shader
1173 rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matMVP);
1174
1175 if (instancing) // Draw mesh instanced
1176 {
1177 if (mesh.indices != NULL) rlDrawVertexArrayElementsInstanced(0, mesh.triangleCount*3, 0, instances);
1178 else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances);
1179 }
1180 else // Draw mesh
1181 {
1182 if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, 0);
1183 else rlDrawVertexArray(0, mesh.vertexCount);
1184 }
1185 }
1186
1187 // Unbind all binded texture maps
1188 for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
1189 {
1190 // Select current shader texture slot
1191 rlActiveTextureSlot(i);
1192
1193 // Disable texture for active slot
1194 if ((i == MATERIAL_MAP_IRRADIANCE) ||
1195 (i == MATERIAL_MAP_PREFILTER) ||
1196 (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap();
1197 else rlDisableTexture();
1198 }
1199
1200 // Disable all possible vertex array objects (or VBOs)
1201 rlDisableVertexArray();
1202 rlDisableVertexBuffer();
1203 rlDisableVertexBufferElement();
1204
1205 // Disable shader program
1206 rlDisableShader();
1207
1208 if (instancing)
1209 {
1210 // Remove instance transforms buffer
1211 rlUnloadVertexBuffer(instancesVboId);
1212 RL_FREE(instanceTransforms);
1213 }
1214 else
1215 {
1216 // Restore rlgl internal modelview and projection matrices
1217 rlSetMatrixModelview(matView);
1218 rlSetMatrixProjection(matProjection);
1219 }
1220 #endif
1221 }
1222
1223 // Unload mesh from memory (RAM and VRAM)
UnloadMesh(Mesh mesh)1224 void UnloadMesh(Mesh mesh)
1225 {
1226 // Unload rlgl mesh vboId data
1227 rlUnloadVertexArray(mesh.vaoId);
1228
1229 for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]);
1230 RL_FREE(mesh.vboId);
1231
1232 RL_FREE(mesh.vertices);
1233 RL_FREE(mesh.texcoords);
1234 RL_FREE(mesh.normals);
1235 RL_FREE(mesh.colors);
1236 RL_FREE(mesh.tangents);
1237 RL_FREE(mesh.texcoords2);
1238 RL_FREE(mesh.indices);
1239
1240 RL_FREE(mesh.animVertices);
1241 RL_FREE(mesh.animNormals);
1242 RL_FREE(mesh.boneWeights);
1243 RL_FREE(mesh.boneIds);
1244 }
1245
1246 // Export mesh data to file
ExportMesh(Mesh mesh,const char * fileName)1247 bool ExportMesh(Mesh mesh, const char *fileName)
1248 {
1249 bool success = false;
1250
1251 if (IsFileExtension(fileName, ".obj"))
1252 {
1253 // Estimated data size, it should be enough...
1254 int dataSize = mesh.vertexCount/3* (int)strlen("v 0000.00f 0000.00f 0000.00f") +
1255 mesh.vertexCount/2* (int)strlen("vt 0.000f 0.00f") +
1256 mesh.vertexCount/3* (int)strlen("vn 0.000f 0.00f 0.00f") +
1257 mesh.triangleCount/3* (int)strlen("f 00000/00000/00000 00000/00000/00000 00000/00000/00000");
1258
1259 // NOTE: Text data buffer size is estimated considering mesh data size
1260 char *txtData = (char *)RL_CALLOC(dataSize + 2000, sizeof(char));
1261
1262 int bytesCount = 0;
1263 bytesCount += sprintf(txtData + bytesCount, "# //////////////////////////////////////////////////////////////////////////////////\n");
1264 bytesCount += sprintf(txtData + bytesCount, "# // //\n");
1265 bytesCount += sprintf(txtData + bytesCount, "# // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized //\n");
1266 bytesCount += sprintf(txtData + bytesCount, "# // //\n");
1267 bytesCount += sprintf(txtData + bytesCount, "# // more info and bugs-report: github.com/raysan5/raylib //\n");
1268 bytesCount += sprintf(txtData + bytesCount, "# // feedback and support: ray[at]raylib.com //\n");
1269 bytesCount += sprintf(txtData + bytesCount, "# // //\n");
1270 bytesCount += sprintf(txtData + bytesCount, "# // Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
1271 bytesCount += sprintf(txtData + bytesCount, "# // //\n");
1272 bytesCount += sprintf(txtData + bytesCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n");
1273 bytesCount += sprintf(txtData + bytesCount, "# Vertex Count: %i\n", mesh.vertexCount);
1274 bytesCount += sprintf(txtData + bytesCount, "# Triangle Count: %i\n\n", mesh.triangleCount);
1275
1276 bytesCount += sprintf(txtData + bytesCount, "g mesh\n");
1277
1278 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
1279 {
1280 bytesCount += sprintf(txtData + bytesCount, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]);
1281 }
1282
1283 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2)
1284 {
1285 bytesCount += sprintf(txtData + bytesCount, "vt %.3f %.3f\n", mesh.texcoords[v], mesh.texcoords[v + 1]);
1286 }
1287
1288 for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
1289 {
1290 bytesCount += sprintf(txtData + bytesCount, "vn %.3f %.3f %.3f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
1291 }
1292
1293 for (int i = 0; i < mesh.triangleCount; i += 3)
1294 {
1295 bytesCount += sprintf(txtData + bytesCount, "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);
1296 }
1297
1298 bytesCount += sprintf(txtData + bytesCount, "\n");
1299
1300 // NOTE: Text data length exported is determined by '\0' (NULL) character
1301 success = SaveFileText(fileName, txtData);
1302
1303 RL_FREE(txtData);
1304 }
1305 else if (IsFileExtension(fileName, ".raw"))
1306 {
1307 // TODO: Support additional file formats to export mesh vertex data
1308 }
1309
1310 return success;
1311 }
1312
1313
1314 // Load materials from model file
LoadMaterials(const char * fileName,int * materialCount)1315 Material *LoadMaterials(const char *fileName, int *materialCount)
1316 {
1317 Material *materials = NULL;
1318 unsigned int count = 0;
1319
1320 // TODO: Support IQM and GLTF for materials parsing
1321
1322 #if defined(SUPPORT_FILEFORMAT_MTL)
1323 if (IsFileExtension(fileName, ".mtl"))
1324 {
1325 tinyobj_material_t *mats = NULL;
1326
1327 int result = tinyobj_parse_mtl_file(&mats, &count, fileName);
1328 if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName);
1329
1330 // TODO: Process materials to return
1331
1332 tinyobj_materials_free(mats, count);
1333 }
1334 #else
1335 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load material file", fileName);
1336 #endif
1337
1338 // Set materials shader to default (DIFFUSE, SPECULAR, NORMAL)
1339 if (materials != NULL)
1340 {
1341 for (unsigned int i = 0; i < count; i++) materials[i].shader = rlGetShaderDefault();
1342 }
1343
1344 *materialCount = count;
1345 return materials;
1346 }
1347
1348 // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
LoadMaterialDefault(void)1349 Material LoadMaterialDefault(void)
1350 {
1351 Material material = { 0 };
1352 material.maps = (MaterialMap *)RL_CALLOC(MAX_MATERIAL_MAPS, sizeof(MaterialMap));
1353
1354 material.shader = rlGetShaderDefault();
1355 material.maps[MATERIAL_MAP_DIFFUSE].texture = rlGetTextureDefault(); // White texture (1x1 pixel)
1356 //material.maps[MATERIAL_MAP_NORMAL].texture; // NOTE: By default, not set
1357 //material.maps[MATERIAL_MAP_SPECULAR].texture; // NOTE: By default, not set
1358
1359 material.maps[MATERIAL_MAP_DIFFUSE].color = WHITE; // Diffuse color
1360 material.maps[MATERIAL_MAP_SPECULAR].color = WHITE; // Specular color
1361
1362 return material;
1363 }
1364
1365 // Unload material from memory
UnloadMaterial(Material material)1366 void UnloadMaterial(Material material)
1367 {
1368 // Unload material shader (avoid unloading default shader, managed by raylib)
1369 if (material.shader.id != rlGetShaderDefault().id) UnloadShader(material.shader);
1370
1371 // Unload loaded texture maps (avoid unloading default texture, managed by raylib)
1372 for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
1373 {
1374 if (material.maps[i].texture.id != rlGetTextureDefault().id) rlUnloadTexture(material.maps[i].texture.id);
1375 }
1376
1377 RL_FREE(material.maps);
1378 }
1379
1380 // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
1381 // NOTE: Previous texture should be manually unloaded
SetMaterialTexture(Material * material,int mapType,Texture2D texture)1382 void SetMaterialTexture(Material *material, int mapType, Texture2D texture)
1383 {
1384 material->maps[mapType].texture = texture;
1385 }
1386
1387 // Set the material for a mesh
SetModelMeshMaterial(Model * model,int meshId,int materialId)1388 void SetModelMeshMaterial(Model *model, int meshId, int materialId)
1389 {
1390 if (meshId >= model->meshCount) TRACELOG(LOG_WARNING, "MESH: Id greater than mesh count");
1391 else if (materialId >= model->materialCount) TRACELOG(LOG_WARNING, "MATERIAL: Id greater than material count");
1392 else model->meshMaterial[meshId] = materialId;
1393 }
1394
1395 // Load model animations from file
LoadModelAnimations(const char * fileName,int * animCount)1396 ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount)
1397 {
1398 ModelAnimation *animations = NULL;
1399
1400 #if defined(SUPPORT_FILEFORMAT_IQM)
1401 if (IsFileExtension(fileName, ".iqm")) animations = LoadIQMModelAnimations(fileName, animCount);
1402 #endif
1403 #if defined(SUPPORT_FILEFORMAT_GLTF)
1404 if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadGLTFModelAnimations(fileName, animCount);
1405 #endif
1406
1407 return animations;
1408 }
1409
1410 // Update model animated vertex data (positions and normals) for a given frame
1411 // NOTE: Updated data is uploaded to GPU
UpdateModelAnimation(Model model,ModelAnimation anim,int frame)1412 void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
1413 {
1414 if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
1415 {
1416 if (frame >= anim.frameCount) frame = frame%anim.frameCount;
1417
1418 for (int m = 0; m < model.meshCount; m++)
1419 {
1420 Vector3 animVertex = { 0 };
1421 Vector3 animNormal = { 0 };
1422
1423 Vector3 inTranslation = { 0 };
1424 Quaternion inRotation = { 0 };
1425 //Vector3 inScale = { 0 }; // Not used...
1426
1427 Vector3 outTranslation = { 0 };
1428 Quaternion outRotation = { 0 };
1429 Vector3 outScale = { 0 };
1430
1431 int vCounter = 0;
1432 int boneCounter = 0;
1433 int boneId = 0;
1434 float boneWeight = 0.0;
1435
1436 for (int i = 0; i < model.meshes[m].vertexCount; i++)
1437 {
1438 model.meshes[m].animVertices[vCounter] = 0;
1439 model.meshes[m].animVertices[vCounter + 1] = 0;
1440 model.meshes[m].animVertices[vCounter + 2] = 0;
1441
1442 model.meshes[m].animNormals[vCounter] = 0;
1443 model.meshes[m].animNormals[vCounter + 1] = 0;
1444 model.meshes[m].animNormals[vCounter + 2] = 0;
1445
1446 for (int j = 0; j < 4; j++)
1447 {
1448 boneId = model.meshes[m].boneIds[boneCounter];
1449 boneWeight = model.meshes[m].boneWeights[boneCounter];
1450 inTranslation = model.bindPose[boneId].translation;
1451 inRotation = model.bindPose[boneId].rotation;
1452 //inScale = model.bindPose[boneId].scale;
1453 outTranslation = anim.framePoses[frame][boneId].translation;
1454 outRotation = anim.framePoses[frame][boneId].rotation;
1455 outScale = anim.framePoses[frame][boneId].scale;
1456
1457 // Vertices processing
1458 // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position)
1459 animVertex = (Vector3){ model.meshes[m].vertices[vCounter], model.meshes[m].vertices[vCounter + 1], model.meshes[m].vertices[vCounter + 2] };
1460 animVertex = Vector3Multiply(animVertex, outScale);
1461 animVertex = Vector3Subtract(animVertex, inTranslation);
1462 animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
1463 animVertex = Vector3Add(animVertex, outTranslation);
1464 model.meshes[m].animVertices[vCounter] += animVertex.x * boneWeight;
1465 model.meshes[m].animVertices[vCounter + 1] += animVertex.y * boneWeight;
1466 model.meshes[m].animVertices[vCounter + 2] += animVertex.z * boneWeight;
1467
1468 // Normals processing
1469 // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
1470 if (model.meshes[m].normals != NULL)
1471 {
1472 animNormal = (Vector3){ model.meshes[m].normals[vCounter], model.meshes[m].normals[vCounter + 1], model.meshes[m].normals[vCounter + 2] };
1473 animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
1474 model.meshes[m].animNormals[vCounter] += animNormal.x * boneWeight;
1475 model.meshes[m].animNormals[vCounter + 1] += animNormal.y * boneWeight;
1476 model.meshes[m].animNormals[vCounter + 2] += animNormal.z * boneWeight;
1477 }
1478 boneCounter += 1;
1479 }
1480 vCounter += 3;
1481 }
1482
1483 // Upload new vertex data to GPU for model drawing
1484 rlUpdateVertexBuffer(model.meshes[m].vboId[0], model.meshes[m].animVertices, model.meshes[m].vertexCount*3*sizeof(float), 0); // Update vertex position
1485 rlUpdateVertexBuffer(model.meshes[m].vboId[2], model.meshes[m].animNormals, model.meshes[m].vertexCount*3*sizeof(float), 0); // Update vertex normals
1486 }
1487 }
1488 }
1489
1490 // Unload animation array data
UnloadModelAnimations(ModelAnimation * animations,unsigned int count)1491 void UnloadModelAnimations(ModelAnimation* animations, unsigned int count)
1492 {
1493 for (unsigned int i = 0; i < count; i++) UnloadModelAnimation(animations[i]);
1494 RL_FREE(animations);
1495 }
1496
1497 // Unload animation data
UnloadModelAnimation(ModelAnimation anim)1498 void UnloadModelAnimation(ModelAnimation anim)
1499 {
1500 for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]);
1501
1502 RL_FREE(anim.bones);
1503 RL_FREE(anim.framePoses);
1504 }
1505
1506 // Check model animation skeleton match
1507 // NOTE: Only number of bones and parent connections are checked
IsModelAnimationValid(Model model,ModelAnimation anim)1508 bool IsModelAnimationValid(Model model, ModelAnimation anim)
1509 {
1510 int result = true;
1511
1512 if (model.boneCount != anim.boneCount) result = false;
1513 else
1514 {
1515 for (int i = 0; i < model.boneCount; i++)
1516 {
1517 if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; }
1518 }
1519 }
1520
1521 return result;
1522 }
1523
1524 #if defined(SUPPORT_MESH_GENERATION)
1525 // Generate polygonal mesh
GenMeshPoly(int sides,float radius)1526 Mesh GenMeshPoly(int sides, float radius)
1527 {
1528 Mesh mesh = { 0 };
1529
1530 if (sides < 3) return mesh;
1531
1532 int vertexCount = sides*3;
1533
1534 // Vertices definition
1535 Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1536
1537 float d = 0.0f, dStep = 360.0f/sides;
1538 for (int v = 0; v < vertexCount; v += 3)
1539 {
1540 vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f };
1541 vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius };
1542 vertices[v + 2] = (Vector3){sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius };
1543 d += dStep;
1544 }
1545
1546 // Normals definition
1547 Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1548 for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up;
1549
1550 // TexCoords definition
1551 Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
1552 for (int n = 0; n < vertexCount; n++) texcoords[n] = (Vector2){ 0.0f, 0.0f };
1553
1554 mesh.vertexCount = vertexCount;
1555 mesh.triangleCount = sides;
1556 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1557 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
1558 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1559
1560 // Mesh vertices position array
1561 for (int i = 0; i < mesh.vertexCount; i++)
1562 {
1563 mesh.vertices[3*i] = vertices[i].x;
1564 mesh.vertices[3*i + 1] = vertices[i].y;
1565 mesh.vertices[3*i + 2] = vertices[i].z;
1566 }
1567
1568 // Mesh texcoords array
1569 for (int i = 0; i < mesh.vertexCount; i++)
1570 {
1571 mesh.texcoords[2*i] = texcoords[i].x;
1572 mesh.texcoords[2*i + 1] = texcoords[i].y;
1573 }
1574
1575 // Mesh normals array
1576 for (int i = 0; i < mesh.vertexCount; i++)
1577 {
1578 mesh.normals[3*i] = normals[i].x;
1579 mesh.normals[3*i + 1] = normals[i].y;
1580 mesh.normals[3*i + 2] = normals[i].z;
1581 }
1582
1583 RL_FREE(vertices);
1584 RL_FREE(normals);
1585 RL_FREE(texcoords);
1586
1587 // Upload vertex data to GPU (static mesh)
1588 // NOTE: mesh.vboId array is allocated inside UploadMesh()
1589 UploadMesh(&mesh, false);
1590
1591 return mesh;
1592 }
1593
1594 // Generate plane mesh (with subdivisions)
GenMeshPlane(float width,float length,int resX,int resZ)1595 Mesh GenMeshPlane(float width, float length, int resX, int resZ)
1596 {
1597 Mesh mesh = { 0 };
1598
1599 #define CUSTOM_MESH_GEN_PLANE
1600 #if defined(CUSTOM_MESH_GEN_PLANE)
1601 resX++;
1602 resZ++;
1603
1604 // Vertices definition
1605 int vertexCount = resX*resZ; // vertices get reused for the faces
1606
1607 Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1608 for (int z = 0; z < resZ; z++)
1609 {
1610 // [-length/2, length/2]
1611 float zPos = ((float)z/(resZ - 1) - 0.5f)*length;
1612 for (int x = 0; x < resX; x++)
1613 {
1614 // [-width/2, width/2]
1615 float xPos = ((float)x/(resX - 1) - 0.5f)*width;
1616 vertices[x + z*resX] = (Vector3){ xPos, 0.0f, zPos };
1617 }
1618 }
1619
1620 // Normals definition
1621 Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
1622 for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f }; // Vector3.up;
1623
1624 // TexCoords definition
1625 Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
1626 for (int v = 0; v < resZ; v++)
1627 {
1628 for (int u = 0; u < resX; u++)
1629 {
1630 texcoords[u + v*resX] = (Vector2){ (float)u/(resX - 1), (float)v/(resZ - 1) };
1631 }
1632 }
1633
1634 // Triangles definition (indices)
1635 int numFaces = (resX - 1)*(resZ - 1);
1636 int *triangles = (int *)RL_MALLOC(numFaces*6*sizeof(int));
1637 int t = 0;
1638 for (int face = 0; face < numFaces; face++)
1639 {
1640 // Retrieve lower left corner from face ind
1641 int i = face % (resX - 1) + (face/(resZ - 1)*resX);
1642
1643 triangles[t++] = i + resX;
1644 triangles[t++] = i + 1;
1645 triangles[t++] = i;
1646
1647 triangles[t++] = i + resX;
1648 triangles[t++] = i + resX + 1;
1649 triangles[t++] = i + 1;
1650 }
1651
1652 mesh.vertexCount = vertexCount;
1653 mesh.triangleCount = numFaces*2;
1654 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1655 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
1656 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
1657 mesh.indices = (unsigned short *)RL_MALLOC(mesh.triangleCount*3*sizeof(unsigned short));
1658
1659 // Mesh vertices position array
1660 for (int i = 0; i < mesh.vertexCount; i++)
1661 {
1662 mesh.vertices[3*i] = vertices[i].x;
1663 mesh.vertices[3*i + 1] = vertices[i].y;
1664 mesh.vertices[3*i + 2] = vertices[i].z;
1665 }
1666
1667 // Mesh texcoords array
1668 for (int i = 0; i < mesh.vertexCount; i++)
1669 {
1670 mesh.texcoords[2*i] = texcoords[i].x;
1671 mesh.texcoords[2*i + 1] = texcoords[i].y;
1672 }
1673
1674 // Mesh normals array
1675 for (int i = 0; i < mesh.vertexCount; i++)
1676 {
1677 mesh.normals[3*i] = normals[i].x;
1678 mesh.normals[3*i + 1] = normals[i].y;
1679 mesh.normals[3*i + 2] = normals[i].z;
1680 }
1681
1682 // Mesh indices array initialization
1683 for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i];
1684
1685 RL_FREE(vertices);
1686 RL_FREE(normals);
1687 RL_FREE(texcoords);
1688 RL_FREE(triangles);
1689
1690 #else // Use par_shapes library to generate plane mesh
1691
1692 par_shapes_mesh *plane = par_shapes_create_plane(resX, resZ); // No normals/texcoords generated!!!
1693 par_shapes_scale(plane, width, length, 1.0f);
1694 par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 });
1695 par_shapes_translate(plane, -width/2, 0.0f, length/2);
1696
1697 mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
1698 mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float));
1699 mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
1700
1701 mesh.vertexCount = plane->ntriangles*3;
1702 mesh.triangleCount = plane->ntriangles;
1703
1704 for (int k = 0; k < mesh.vertexCount; k++)
1705 {
1706 mesh.vertices[k*3] = plane->points[plane->triangles[k]*3];
1707 mesh.vertices[k*3 + 1] = plane->points[plane->triangles[k]*3 + 1];
1708 mesh.vertices[k*3 + 2] = plane->points[plane->triangles[k]*3 + 2];
1709
1710 mesh.normals[k*3] = plane->normals[plane->triangles[k]*3];
1711 mesh.normals[k*3 + 1] = plane->normals[plane->triangles[k]*3 + 1];
1712 mesh.normals[k*3 + 2] = plane->normals[plane->triangles[k]*3 + 2];
1713
1714 mesh.texcoords[k*2] = plane->tcoords[plane->triangles[k]*2];
1715 mesh.texcoords[k*2 + 1] = plane->tcoords[plane->triangles[k]*2 + 1];
1716 }
1717
1718 par_shapes_free_mesh(plane);
1719 #endif
1720
1721 // Upload vertex data to GPU (static mesh)
1722 UploadMesh(&mesh, false);
1723
1724 return mesh;
1725 }
1726
1727 // Generated cuboid mesh
GenMeshCube(float width,float height,float length)1728 Mesh GenMeshCube(float width, float height, float length)
1729 {
1730 Mesh mesh = { 0 };
1731
1732 #define CUSTOM_MESH_GEN_CUBE
1733 #if defined(CUSTOM_MESH_GEN_CUBE)
1734 float vertices[] = {
1735 -width/2, -height/2, length/2,
1736 width/2, -height/2, length/2,
1737 width/2, height/2, length/2,
1738 -width/2, height/2, length/2,
1739 -width/2, -height/2, -length/2,
1740 -width/2, height/2, -length/2,
1741 width/2, height/2, -length/2,
1742 width/2, -height/2, -length/2,
1743 -width/2, height/2, -length/2,
1744 -width/2, height/2, length/2,
1745 width/2, height/2, length/2,
1746 width/2, height/2, -length/2,
1747 -width/2, -height/2, -length/2,
1748 width/2, -height/2, -length/2,
1749 width/2, -height/2, length/2,
1750 -width/2, -height/2, length/2,
1751 width/2, -height/2, -length/2,
1752 width/2, height/2, -length/2,
1753 width/2, height/2, length/2,
1754 width/2, -height/2, length/2,
1755 -width/2, -height/2, -length/2,
1756 -width/2, -height/2, length/2,
1757 -width/2, height/2, length/2,
1758 -width/2, height/2, -length/2
1759 };
1760
1761 float texcoords[] = {
1762 0.0f, 0.0f,
1763 1.0f, 0.0f,
1764 1.0f, 1.0f,
1765 0.0f, 1.0f,
1766 1.0f, 0.0f,
1767 1.0f, 1.0f,
1768 0.0f, 1.0f,
1769 0.0f, 0.0f,
1770 0.0f, 1.0f,
1771 0.0f, 0.0f,
1772 1.0f, 0.0f,
1773 1.0f, 1.0f,
1774 1.0f, 1.0f,
1775 0.0f, 1.0f,
1776 0.0f, 0.0f,
1777 1.0f, 0.0f,
1778 1.0f, 0.0f,
1779 1.0f, 1.0f,
1780 0.0f, 1.0f,
1781 0.0f, 0.0f,
1782 0.0f, 0.0f,
1783 1.0f, 0.0f,
1784 1.0f, 1.0f,
1785 0.0f, 1.0f
1786 };
1787
1788 float normals[] = {
1789 0.0f, 0.0f, 1.0f,
1790 0.0f, 0.0f, 1.0f,
1791 0.0f, 0.0f, 1.0f,
1792 0.0f, 0.0f, 1.0f,
1793 0.0f, 0.0f,-1.0f,
1794 0.0f, 0.0f,-1.0f,
1795 0.0f, 0.0f,-1.0f,
1796 0.0f, 0.0f,-1.0f,
1797 0.0f, 1.0f, 0.0f,
1798 0.0f, 1.0f, 0.0f,
1799 0.0f, 1.0f, 0.0f,
1800 0.0f, 1.0f, 0.0f,
1801 0.0f,-1.0f, 0.0f,
1802 0.0f,-1.0f, 0.0f,
1803 0.0f,-1.0f, 0.0f,
1804 0.0f,-1.0f, 0.0f,
1805 1.0f, 0.0f, 0.0f,
1806 1.0f, 0.0f, 0.0f,
1807 1.0f, 0.0f, 0.0f,
1808 1.0f, 0.0f, 0.0f,
1809 -1.0f, 0.0f, 0.0f,
1810 -1.0f, 0.0f, 0.0f,
1811 -1.0f, 0.0f, 0.0f,
1812 -1.0f, 0.0f, 0.0f
1813 };
1814
1815 mesh.vertices = (float *)RL_MALLOC(24*3*sizeof(float));
1816 memcpy(mesh.vertices, vertices, 24*3*sizeof(float));
1817
1818 mesh.texcoords = (float *)RL_MALLOC(24*2*sizeof(float));
1819 memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float));
1820
1821 mesh.normals = (float *)RL_MALLOC(24*3*sizeof(float));
1822 memcpy(mesh.normals, normals, 24*3*sizeof(float));
1823
1824 mesh.indices = (unsigned short *)RL_MALLOC(36*sizeof(unsigned short));
1825
1826 int k = 0;
1827
1828 // Indices can be initialized right now
1829 for (int i = 0; i < 36; i+=6)
1830 {
1831 mesh.indices[i] = 4*k;
1832 mesh.indices[i+1] = 4*k+1;
1833 mesh.indices[i+2] = 4*k+2;
1834 mesh.indices[i+3] = 4*k;
1835 mesh.indices[i+4] = 4*k+2;
1836 mesh.indices[i+5] = 4*k+3;
1837
1838 k++;
1839 }
1840
1841 mesh.vertexCount = 24;
1842 mesh.triangleCount = 12;
1843
1844 #else // Use par_shapes library to generate cube mesh
1845 /*
1846 // Platonic solids:
1847 par_shapes_mesh* par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid)
1848 par_shapes_mesh* par_shapes_create_cube(); // 6 sides polyhedron (cube)
1849 par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (dyamond)
1850 par_shapes_mesh* par_shapes_create_dodecahedron(); // 12 sides polyhedron
1851 par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron
1852 */
1853 // Platonic solid generation: cube (6 sides)
1854 // NOTE: No normals/texcoords generated by default
1855 par_shapes_mesh *cube = par_shapes_create_cube();
1856 cube->tcoords = PAR_MALLOC(float, 2*cube->npoints);
1857 for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f;
1858 par_shapes_scale(cube, width, height, length);
1859 par_shapes_translate(cube, -width/2, 0.0f, -length/2);
1860 par_shapes_compute_normals(cube);
1861
1862 mesh.vertices = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
1863 mesh.texcoords = (float *)RL_MALLOC(cube->ntriangles*3*2*sizeof(float));
1864 mesh.normals = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
1865
1866 mesh.vertexCount = cube->ntriangles*3;
1867 mesh.triangleCount = cube->ntriangles;
1868
1869 for (int k = 0; k < mesh.vertexCount; k++)
1870 {
1871 mesh.vertices[k*3] = cube->points[cube->triangles[k]*3];
1872 mesh.vertices[k*3 + 1] = cube->points[cube->triangles[k]*3 + 1];
1873 mesh.vertices[k*3 + 2] = cube->points[cube->triangles[k]*3 + 2];
1874
1875 mesh.normals[k*3] = cube->normals[cube->triangles[k]*3];
1876 mesh.normals[k*3 + 1] = cube->normals[cube->triangles[k]*3 + 1];
1877 mesh.normals[k*3 + 2] = cube->normals[cube->triangles[k]*3 + 2];
1878
1879 mesh.texcoords[k*2] = cube->tcoords[cube->triangles[k]*2];
1880 mesh.texcoords[k*2 + 1] = cube->tcoords[cube->triangles[k]*2 + 1];
1881 }
1882
1883 par_shapes_free_mesh(cube);
1884 #endif
1885
1886 // Upload vertex data to GPU (static mesh)
1887 UploadMesh(&mesh, false);
1888
1889 return mesh;
1890 }
1891
1892 // Generate sphere mesh (standard sphere)
GenMeshSphere(float radius,int rings,int slices)1893 Mesh GenMeshSphere(float radius, int rings, int slices)
1894 {
1895 Mesh mesh = { 0 };
1896
1897 if ((rings >= 3) && (slices >= 3))
1898 {
1899 par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings);
1900 par_shapes_scale(sphere, radius, radius, radius);
1901 // NOTE: Soft normals are computed internally
1902
1903 mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1904 mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
1905 mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1906
1907 mesh.vertexCount = sphere->ntriangles*3;
1908 mesh.triangleCount = sphere->ntriangles;
1909
1910 for (int k = 0; k < mesh.vertexCount; k++)
1911 {
1912 mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
1913 mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
1914 mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
1915
1916 mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
1917 mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
1918 mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
1919
1920 mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
1921 mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
1922 }
1923
1924 par_shapes_free_mesh(sphere);
1925
1926 // Upload vertex data to GPU (static mesh)
1927 UploadMesh(&mesh, false);
1928 }
1929 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: sphere");
1930
1931 return mesh;
1932 }
1933
1934 // Generate hemi-sphere mesh (half sphere, no bottom cap)
GenMeshHemiSphere(float radius,int rings,int slices)1935 Mesh GenMeshHemiSphere(float radius, int rings, int slices)
1936 {
1937 Mesh mesh = { 0 };
1938
1939 if ((rings >= 3) && (slices >= 3))
1940 {
1941 if (radius < 0.0f) radius = 0.0f;
1942
1943 par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings);
1944 par_shapes_scale(sphere, radius, radius, radius);
1945 // NOTE: Soft normals are computed internally
1946
1947 mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1948 mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
1949 mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
1950
1951 mesh.vertexCount = sphere->ntriangles*3;
1952 mesh.triangleCount = sphere->ntriangles;
1953
1954 for (int k = 0; k < mesh.vertexCount; k++)
1955 {
1956 mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
1957 mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
1958 mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
1959
1960 mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
1961 mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
1962 mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
1963
1964 mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
1965 mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
1966 }
1967
1968 par_shapes_free_mesh(sphere);
1969
1970 // Upload vertex data to GPU (static mesh)
1971 UploadMesh(&mesh, false);
1972 }
1973 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: hemisphere");
1974
1975 return mesh;
1976 }
1977
1978 // Generate cylinder mesh
GenMeshCylinder(float radius,float height,int slices)1979 Mesh GenMeshCylinder(float radius, float height, int slices)
1980 {
1981 Mesh mesh = { 0 };
1982
1983 if (slices >= 3)
1984 {
1985 // Instance a cylinder that sits on the Z=0 plane using the given tessellation
1986 // levels across the UV domain. Think of "slices" like a number of pizza
1987 // slices, and "stacks" like a number of stacked rings.
1988 // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
1989 par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8);
1990 par_shapes_scale(cylinder, radius, radius, height);
1991 par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 });
1992 par_shapes_rotate(cylinder, PI/2.0f, (float[]){ 0, 1, 0 });
1993
1994 // Generate an orientable disk shape (top cap)
1995 par_shapes_mesh *capTop = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, 1 });
1996 capTop->tcoords = PAR_MALLOC(float, 2*capTop->npoints);
1997 for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f;
1998 par_shapes_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 });
1999 par_shapes_translate(capTop, 0, height, 0);
2000
2001 // Generate an orientable disk shape (bottom cap)
2002 par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
2003 capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
2004 for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
2005 par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
2006
2007 par_shapes_merge_and_free(cylinder, capTop);
2008 par_shapes_merge_and_free(cylinder, capBottom);
2009
2010 mesh.vertices = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
2011 mesh.texcoords = (float *)RL_MALLOC(cylinder->ntriangles*3*2*sizeof(float));
2012 mesh.normals = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
2013
2014 mesh.vertexCount = cylinder->ntriangles*3;
2015 mesh.triangleCount = cylinder->ntriangles;
2016
2017 for (int k = 0; k < mesh.vertexCount; k++)
2018 {
2019 mesh.vertices[k*3] = cylinder->points[cylinder->triangles[k]*3];
2020 mesh.vertices[k*3 + 1] = cylinder->points[cylinder->triangles[k]*3 + 1];
2021 mesh.vertices[k*3 + 2] = cylinder->points[cylinder->triangles[k]*3 + 2];
2022
2023 mesh.normals[k*3] = cylinder->normals[cylinder->triangles[k]*3];
2024 mesh.normals[k*3 + 1] = cylinder->normals[cylinder->triangles[k]*3 + 1];
2025 mesh.normals[k*3 + 2] = cylinder->normals[cylinder->triangles[k]*3 + 2];
2026
2027 mesh.texcoords[k*2] = cylinder->tcoords[cylinder->triangles[k]*2];
2028 mesh.texcoords[k*2 + 1] = cylinder->tcoords[cylinder->triangles[k]*2 + 1];
2029 }
2030
2031 par_shapes_free_mesh(cylinder);
2032
2033 // Upload vertex data to GPU (static mesh)
2034 UploadMesh(&mesh, false);
2035 }
2036 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cylinder");
2037
2038 return mesh;
2039 }
2040
2041 // Generate torus mesh
GenMeshTorus(float radius,float size,int radSeg,int sides)2042 Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
2043 {
2044 Mesh mesh = { 0 };
2045
2046 if ((sides >= 3) && (radSeg >= 3))
2047 {
2048 if (radius > 1.0f) radius = 1.0f;
2049 else if (radius < 0.1f) radius = 0.1f;
2050
2051 // Create a donut that sits on the Z=0 plane with the specified inner radius
2052 // The outer radius can be controlled with par_shapes_scale
2053 par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius);
2054 par_shapes_scale(torus, size/2, size/2, size/2);
2055
2056 mesh.vertices = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
2057 mesh.texcoords = (float *)RL_MALLOC(torus->ntriangles*3*2*sizeof(float));
2058 mesh.normals = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
2059
2060 mesh.vertexCount = torus->ntriangles*3;
2061 mesh.triangleCount = torus->ntriangles;
2062
2063 for (int k = 0; k < mesh.vertexCount; k++)
2064 {
2065 mesh.vertices[k*3] = torus->points[torus->triangles[k]*3];
2066 mesh.vertices[k*3 + 1] = torus->points[torus->triangles[k]*3 + 1];
2067 mesh.vertices[k*3 + 2] = torus->points[torus->triangles[k]*3 + 2];
2068
2069 mesh.normals[k*3] = torus->normals[torus->triangles[k]*3];
2070 mesh.normals[k*3 + 1] = torus->normals[torus->triangles[k]*3 + 1];
2071 mesh.normals[k*3 + 2] = torus->normals[torus->triangles[k]*3 + 2];
2072
2073 mesh.texcoords[k*2] = torus->tcoords[torus->triangles[k]*2];
2074 mesh.texcoords[k*2 + 1] = torus->tcoords[torus->triangles[k]*2 + 1];
2075 }
2076
2077 par_shapes_free_mesh(torus);
2078
2079 // Upload vertex data to GPU (static mesh)
2080 UploadMesh(&mesh, false);
2081 }
2082 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: torus");
2083
2084 return mesh;
2085 }
2086
2087 // Generate trefoil knot mesh
GenMeshKnot(float radius,float size,int radSeg,int sides)2088 Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
2089 {
2090 Mesh mesh = { 0 };
2091
2092 if ((sides >= 3) && (radSeg >= 3))
2093 {
2094 if (radius > 3.0f) radius = 3.0f;
2095 else if (radius < 0.5f) radius = 0.5f;
2096
2097 par_shapes_mesh *knot = par_shapes_create_trefoil_knot(radSeg, sides, radius);
2098 par_shapes_scale(knot, size, size, size);
2099
2100 mesh.vertices = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
2101 mesh.texcoords = (float *)RL_MALLOC(knot->ntriangles*3*2*sizeof(float));
2102 mesh.normals = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
2103
2104 mesh.vertexCount = knot->ntriangles*3;
2105 mesh.triangleCount = knot->ntriangles;
2106
2107 for (int k = 0; k < mesh.vertexCount; k++)
2108 {
2109 mesh.vertices[k*3] = knot->points[knot->triangles[k]*3];
2110 mesh.vertices[k*3 + 1] = knot->points[knot->triangles[k]*3 + 1];
2111 mesh.vertices[k*3 + 2] = knot->points[knot->triangles[k]*3 + 2];
2112
2113 mesh.normals[k*3] = knot->normals[knot->triangles[k]*3];
2114 mesh.normals[k*3 + 1] = knot->normals[knot->triangles[k]*3 + 1];
2115 mesh.normals[k*3 + 2] = knot->normals[knot->triangles[k]*3 + 2];
2116
2117 mesh.texcoords[k*2] = knot->tcoords[knot->triangles[k]*2];
2118 mesh.texcoords[k*2 + 1] = knot->tcoords[knot->triangles[k]*2 + 1];
2119 }
2120
2121 par_shapes_free_mesh(knot);
2122
2123 // Upload vertex data to GPU (static mesh)
2124 UploadMesh(&mesh, false);
2125 }
2126 else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: knot");
2127
2128 return mesh;
2129 }
2130
2131 // Generate a mesh from heightmap
2132 // NOTE: Vertex data is uploaded to GPU
GenMeshHeightmap(Image heightmap,Vector3 size)2133 Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
2134 {
2135 #define GRAY_VALUE(c) ((c.r+c.g+c.b)/3)
2136
2137 Mesh mesh = { 0 };
2138
2139 int mapX = heightmap.width;
2140 int mapZ = heightmap.height;
2141
2142 Color *pixels = LoadImageColors(heightmap);
2143
2144 // NOTE: One vertex per pixel
2145 mesh.triangleCount = (mapX-1)*(mapZ-1)*2; // One quad every four pixels
2146
2147 mesh.vertexCount = mesh.triangleCount*3;
2148
2149 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
2150 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
2151 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
2152 mesh.colors = NULL;
2153
2154 int vCounter = 0; // Used to count vertices float by float
2155 int tcCounter = 0; // Used to count texcoords float by float
2156 int nCounter = 0; // Used to count normals float by float
2157
2158 int trisCounter = 0;
2159
2160 Vector3 scaleFactor = { size.x/mapX, size.y/255.0f, size.z/mapZ };
2161
2162 Vector3 vA;
2163 Vector3 vB;
2164 Vector3 vC;
2165 Vector3 vN;
2166
2167 for (int z = 0; z < mapZ-1; z++)
2168 {
2169 for (int x = 0; x < mapX-1; x++)
2170 {
2171 // Fill vertices array with data
2172 //----------------------------------------------------------
2173
2174 // one triangle - 3 vertex
2175 mesh.vertices[vCounter] = (float)x*scaleFactor.x;
2176 mesh.vertices[vCounter + 1] = (float)GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y;
2177 mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z;
2178
2179 mesh.vertices[vCounter + 3] = (float)x*scaleFactor.x;
2180 mesh.vertices[vCounter + 4] = (float)GRAY_VALUE(pixels[x + (z + 1)*mapX])*scaleFactor.y;
2181 mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z;
2182
2183 mesh.vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x;
2184 mesh.vertices[vCounter + 7] = (float)GRAY_VALUE(pixels[(x + 1) + z*mapX])*scaleFactor.y;
2185 mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z;
2186
2187 // another triangle - 3 vertex
2188 mesh.vertices[vCounter + 9] = mesh.vertices[vCounter + 6];
2189 mesh.vertices[vCounter + 10] = mesh.vertices[vCounter + 7];
2190 mesh.vertices[vCounter + 11] = mesh.vertices[vCounter + 8];
2191
2192 mesh.vertices[vCounter + 12] = mesh.vertices[vCounter + 3];
2193 mesh.vertices[vCounter + 13] = mesh.vertices[vCounter + 4];
2194 mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5];
2195
2196 mesh.vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x;
2197 mesh.vertices[vCounter + 16] = (float)GRAY_VALUE(pixels[(x + 1) + (z + 1)*mapX])*scaleFactor.y;
2198 mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z;
2199 vCounter += 18; // 6 vertex, 18 floats
2200
2201 // Fill texcoords array with data
2202 //--------------------------------------------------------------
2203 mesh.texcoords[tcCounter] = (float)x/(mapX - 1);
2204 mesh.texcoords[tcCounter + 1] = (float)z/(mapZ - 1);
2205
2206 mesh.texcoords[tcCounter + 2] = (float)x/(mapX - 1);
2207 mesh.texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1);
2208
2209 mesh.texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1);
2210 mesh.texcoords[tcCounter + 5] = (float)z/(mapZ - 1);
2211
2212 mesh.texcoords[tcCounter + 6] = mesh.texcoords[tcCounter + 4];
2213 mesh.texcoords[tcCounter + 7] = mesh.texcoords[tcCounter + 5];
2214
2215 mesh.texcoords[tcCounter + 8] = mesh.texcoords[tcCounter + 2];
2216 mesh.texcoords[tcCounter + 9] = mesh.texcoords[tcCounter + 3];
2217
2218 mesh.texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1);
2219 mesh.texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1);
2220 tcCounter += 12; // 6 texcoords, 12 floats
2221
2222 // Fill normals array with data
2223 //--------------------------------------------------------------
2224 for (int i = 0; i < 18; i += 9)
2225 {
2226 vA.x = mesh.vertices[nCounter + i];
2227 vA.y = mesh.vertices[nCounter + i + 1];
2228 vA.z = mesh.vertices[nCounter + i + 2];
2229
2230 vB.x = mesh.vertices[nCounter + i + 3];
2231 vB.y = mesh.vertices[nCounter + i + 4];
2232 vB.z = mesh.vertices[nCounter + i + 5];
2233
2234 vC.x = mesh.vertices[nCounter + i + 6];
2235 vC.y = mesh.vertices[nCounter + i + 7];
2236 vC.z = mesh.vertices[nCounter + i + 8];
2237
2238 vN = Vector3Normalize(Vector3CrossProduct(Vector3Subtract(vB, vA), Vector3Subtract(vC, vA)));
2239
2240 mesh.normals[nCounter + i] = vN.x;
2241 mesh.normals[nCounter + i + 1] = vN.y;
2242 mesh.normals[nCounter + i + 2] = vN.z;
2243
2244 mesh.normals[nCounter + i + 3] = vN.x;
2245 mesh.normals[nCounter + i + 4] = vN.y;
2246 mesh.normals[nCounter + i + 5] = vN.z;
2247
2248 mesh.normals[nCounter + i + 6] = vN.x;
2249 mesh.normals[nCounter + i + 7] = vN.y;
2250 mesh.normals[nCounter + i + 8] = vN.z;
2251 }
2252
2253 nCounter += 18; // 6 vertex, 18 floats
2254 trisCounter += 2;
2255 }
2256 }
2257
2258 UnloadImageColors(pixels); // Unload pixels color data
2259
2260 // Upload vertex data to GPU (static mesh)
2261 UploadMesh(&mesh, false);
2262
2263 return mesh;
2264 }
2265
2266 // Generate a cubes mesh from pixel data
2267 // NOTE: Vertex data is uploaded to GPU
GenMeshCubicmap(Image cubicmap,Vector3 cubeSize)2268 Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
2269 {
2270 #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
2271
2272 Mesh mesh = { 0 };
2273
2274 Color *pixels = LoadImageColors(cubicmap);
2275
2276 int mapWidth = cubicmap.width;
2277 int mapHeight = cubicmap.height;
2278
2279 // NOTE: Max possible number of triangles numCubes*(12 triangles by cube)
2280 int maxTriangles = cubicmap.width*cubicmap.height*12;
2281
2282 int vCounter = 0; // Used to count vertices
2283 int tcCounter = 0; // Used to count texcoords
2284 int nCounter = 0; // Used to count normals
2285
2286 float w = cubeSize.x;
2287 float h = cubeSize.z;
2288 float h2 = cubeSize.y;
2289
2290 Vector3 *mapVertices = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
2291 Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2));
2292 Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
2293
2294 // Define the 6 normals of the cube, we will combine them accordingly later...
2295 Vector3 n1 = { 1.0f, 0.0f, 0.0f };
2296 Vector3 n2 = { -1.0f, 0.0f, 0.0f };
2297 Vector3 n3 = { 0.0f, 1.0f, 0.0f };
2298 Vector3 n4 = { 0.0f, -1.0f, 0.0f };
2299 Vector3 n5 = { 0.0f, 0.0f, -1.0f };
2300 Vector3 n6 = { 0.0f, 0.0f, 1.0f };
2301
2302 // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6)
2303 typedef struct RectangleF {
2304 float x;
2305 float y;
2306 float width;
2307 float height;
2308 } RectangleF;
2309
2310 RectangleF rightTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
2311 RectangleF leftTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
2312 RectangleF frontTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
2313 RectangleF backTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
2314 RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f };
2315 RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f };
2316
2317 for (int z = 0; z < mapHeight; ++z)
2318 {
2319 for (int x = 0; x < mapWidth; ++x)
2320 {
2321 // Define the 8 vertex of the cube, we will combine them accordingly later...
2322 Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
2323 Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) };
2324 Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) };
2325 Vector3 v4 = { w*(x + 0.5f), h2, h*(z - 0.5f) };
2326 Vector3 v5 = { w*(x + 0.5f), 0, h*(z - 0.5f) };
2327 Vector3 v6 = { w*(x - 0.5f), 0, h*(z - 0.5f) };
2328 Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) };
2329 Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) };
2330
2331 // We check pixel color to be WHITE -> draw full cube
2332 if (COLOR_EQUAL(pixels[z*cubicmap.width + x], WHITE))
2333 {
2334 // Define triangles and checking collateral cubes
2335 //------------------------------------------------
2336
2337 // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
2338 // WARNING: Not required for a WHITE cubes, created to allow seeing the map from outside
2339 mapVertices[vCounter] = v1;
2340 mapVertices[vCounter + 1] = v2;
2341 mapVertices[vCounter + 2] = v3;
2342 mapVertices[vCounter + 3] = v1;
2343 mapVertices[vCounter + 4] = v3;
2344 mapVertices[vCounter + 5] = v4;
2345 vCounter += 6;
2346
2347 mapNormals[nCounter] = n3;
2348 mapNormals[nCounter + 1] = n3;
2349 mapNormals[nCounter + 2] = n3;
2350 mapNormals[nCounter + 3] = n3;
2351 mapNormals[nCounter + 4] = n3;
2352 mapNormals[nCounter + 5] = n3;
2353 nCounter += 6;
2354
2355 mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
2356 mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
2357 mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2358 mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
2359 mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2360 mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
2361 tcCounter += 6;
2362
2363 // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
2364 mapVertices[vCounter] = v6;
2365 mapVertices[vCounter + 1] = v8;
2366 mapVertices[vCounter + 2] = v7;
2367 mapVertices[vCounter + 3] = v6;
2368 mapVertices[vCounter + 4] = v5;
2369 mapVertices[vCounter + 5] = v8;
2370 vCounter += 6;
2371
2372 mapNormals[nCounter] = n4;
2373 mapNormals[nCounter + 1] = n4;
2374 mapNormals[nCounter + 2] = n4;
2375 mapNormals[nCounter + 3] = n4;
2376 mapNormals[nCounter + 4] = n4;
2377 mapNormals[nCounter + 5] = n4;
2378 nCounter += 6;
2379
2380 mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2381 mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2382 mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
2383 mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2384 mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
2385 mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2386 tcCounter += 6;
2387
2388 // Checking cube on bottom of current cube
2389 if (((z < cubicmap.height - 1) && COLOR_EQUAL(pixels[(z + 1)*cubicmap.width + x], BLACK)) || (z == cubicmap.height - 1))
2390 {
2391 // Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8
2392 // NOTE: Collateral occluded faces are not generated
2393 mapVertices[vCounter] = v2;
2394 mapVertices[vCounter + 1] = v7;
2395 mapVertices[vCounter + 2] = v3;
2396 mapVertices[vCounter + 3] = v3;
2397 mapVertices[vCounter + 4] = v7;
2398 mapVertices[vCounter + 5] = v8;
2399 vCounter += 6;
2400
2401 mapNormals[nCounter] = n6;
2402 mapNormals[nCounter + 1] = n6;
2403 mapNormals[nCounter + 2] = n6;
2404 mapNormals[nCounter + 3] = n6;
2405 mapNormals[nCounter + 4] = n6;
2406 mapNormals[nCounter + 5] = n6;
2407 nCounter += 6;
2408
2409 mapTexcoords[tcCounter] = (Vector2){ frontTexUV.x, frontTexUV.y };
2410 mapTexcoords[tcCounter + 1] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
2411 mapTexcoords[tcCounter + 2] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
2412 mapTexcoords[tcCounter + 3] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
2413 mapTexcoords[tcCounter + 4] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
2414 mapTexcoords[tcCounter + 5] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y + frontTexUV.height };
2415 tcCounter += 6;
2416 }
2417
2418 // Checking cube on top of current cube
2419 if (((z > 0) && COLOR_EQUAL(pixels[(z - 1)*cubicmap.width + x], BLACK)) || (z == 0))
2420 {
2421 // Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5
2422 // NOTE: Collateral occluded faces are not generated
2423 mapVertices[vCounter] = v1;
2424 mapVertices[vCounter + 1] = v5;
2425 mapVertices[vCounter + 2] = v6;
2426 mapVertices[vCounter + 3] = v1;
2427 mapVertices[vCounter + 4] = v4;
2428 mapVertices[vCounter + 5] = v5;
2429 vCounter += 6;
2430
2431 mapNormals[nCounter] = n5;
2432 mapNormals[nCounter + 1] = n5;
2433 mapNormals[nCounter + 2] = n5;
2434 mapNormals[nCounter + 3] = n5;
2435 mapNormals[nCounter + 4] = n5;
2436 mapNormals[nCounter + 5] = n5;
2437 nCounter += 6;
2438
2439 mapTexcoords[tcCounter] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
2440 mapTexcoords[tcCounter + 1] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
2441 mapTexcoords[tcCounter + 2] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y + backTexUV.height };
2442 mapTexcoords[tcCounter + 3] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
2443 mapTexcoords[tcCounter + 4] = (Vector2){ backTexUV.x, backTexUV.y };
2444 mapTexcoords[tcCounter + 5] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
2445 tcCounter += 6;
2446 }
2447
2448 // Checking cube on right of current cube
2449 if (((x < cubicmap.width - 1) && COLOR_EQUAL(pixels[z*cubicmap.width + (x + 1)], BLACK)) || (x == cubicmap.width - 1))
2450 {
2451 // Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5
2452 // NOTE: Collateral occluded faces are not generated
2453 mapVertices[vCounter] = v3;
2454 mapVertices[vCounter + 1] = v8;
2455 mapVertices[vCounter + 2] = v4;
2456 mapVertices[vCounter + 3] = v4;
2457 mapVertices[vCounter + 4] = v8;
2458 mapVertices[vCounter + 5] = v5;
2459 vCounter += 6;
2460
2461 mapNormals[nCounter] = n1;
2462 mapNormals[nCounter + 1] = n1;
2463 mapNormals[nCounter + 2] = n1;
2464 mapNormals[nCounter + 3] = n1;
2465 mapNormals[nCounter + 4] = n1;
2466 mapNormals[nCounter + 5] = n1;
2467 nCounter += 6;
2468
2469 mapTexcoords[tcCounter] = (Vector2){ rightTexUV.x, rightTexUV.y };
2470 mapTexcoords[tcCounter + 1] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
2471 mapTexcoords[tcCounter + 2] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
2472 mapTexcoords[tcCounter + 3] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
2473 mapTexcoords[tcCounter + 4] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
2474 mapTexcoords[tcCounter + 5] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y + rightTexUV.height };
2475 tcCounter += 6;
2476 }
2477
2478 // Checking cube on left of current cube
2479 if (((x > 0) && COLOR_EQUAL(pixels[z*cubicmap.width + (x - 1)], BLACK)) || (x == 0))
2480 {
2481 // Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7
2482 // NOTE: Collateral occluded faces are not generated
2483 mapVertices[vCounter] = v1;
2484 mapVertices[vCounter + 1] = v7;
2485 mapVertices[vCounter + 2] = v2;
2486 mapVertices[vCounter + 3] = v1;
2487 mapVertices[vCounter + 4] = v6;
2488 mapVertices[vCounter + 5] = v7;
2489 vCounter += 6;
2490
2491 mapNormals[nCounter] = n2;
2492 mapNormals[nCounter + 1] = n2;
2493 mapNormals[nCounter + 2] = n2;
2494 mapNormals[nCounter + 3] = n2;
2495 mapNormals[nCounter + 4] = n2;
2496 mapNormals[nCounter + 5] = n2;
2497 nCounter += 6;
2498
2499 mapTexcoords[tcCounter] = (Vector2){ leftTexUV.x, leftTexUV.y };
2500 mapTexcoords[tcCounter + 1] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
2501 mapTexcoords[tcCounter + 2] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y };
2502 mapTexcoords[tcCounter + 3] = (Vector2){ leftTexUV.x, leftTexUV.y };
2503 mapTexcoords[tcCounter + 4] = (Vector2){ leftTexUV.x, leftTexUV.y + leftTexUV.height };
2504 mapTexcoords[tcCounter + 5] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
2505 tcCounter += 6;
2506 }
2507 }
2508 // We check pixel color to be BLACK, we will only draw floor and roof
2509 else if (COLOR_EQUAL(pixels[z*cubicmap.width + x], BLACK))
2510 {
2511 // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
2512 mapVertices[vCounter] = v1;
2513 mapVertices[vCounter + 1] = v3;
2514 mapVertices[vCounter + 2] = v2;
2515 mapVertices[vCounter + 3] = v1;
2516 mapVertices[vCounter + 4] = v4;
2517 mapVertices[vCounter + 5] = v3;
2518 vCounter += 6;
2519
2520 mapNormals[nCounter] = n4;
2521 mapNormals[nCounter + 1] = n4;
2522 mapNormals[nCounter + 2] = n4;
2523 mapNormals[nCounter + 3] = n4;
2524 mapNormals[nCounter + 4] = n4;
2525 mapNormals[nCounter + 5] = n4;
2526 nCounter += 6;
2527
2528 mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
2529 mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2530 mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
2531 mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
2532 mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
2533 mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
2534 tcCounter += 6;
2535
2536 // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
2537 mapVertices[vCounter] = v6;
2538 mapVertices[vCounter + 1] = v7;
2539 mapVertices[vCounter + 2] = v8;
2540 mapVertices[vCounter + 3] = v6;
2541 mapVertices[vCounter + 4] = v8;
2542 mapVertices[vCounter + 5] = v5;
2543 vCounter += 6;
2544
2545 mapNormals[nCounter] = n3;
2546 mapNormals[nCounter + 1] = n3;
2547 mapNormals[nCounter + 2] = n3;
2548 mapNormals[nCounter + 3] = n3;
2549 mapNormals[nCounter + 4] = n3;
2550 mapNormals[nCounter + 5] = n3;
2551 nCounter += 6;
2552
2553 mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2554 mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
2555 mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2556 mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
2557 mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
2558 mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
2559 tcCounter += 6;
2560 }
2561 }
2562 }
2563
2564 // Move data from mapVertices temp arays to vertices float array
2565 mesh.vertexCount = vCounter;
2566 mesh.triangleCount = vCounter/3;
2567
2568 mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
2569 mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
2570 mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
2571 mesh.colors = NULL;
2572
2573 int fCounter = 0;
2574
2575 // Move vertices data
2576 for (int i = 0; i < vCounter; i++)
2577 {
2578 mesh.vertices[fCounter] = mapVertices[i].x;
2579 mesh.vertices[fCounter + 1] = mapVertices[i].y;
2580 mesh.vertices[fCounter + 2] = mapVertices[i].z;
2581 fCounter += 3;
2582 }
2583
2584 fCounter = 0;
2585
2586 // Move normals data
2587 for (int i = 0; i < nCounter; i++)
2588 {
2589 mesh.normals[fCounter] = mapNormals[i].x;
2590 mesh.normals[fCounter + 1] = mapNormals[i].y;
2591 mesh.normals[fCounter + 2] = mapNormals[i].z;
2592 fCounter += 3;
2593 }
2594
2595 fCounter = 0;
2596
2597 // Move texcoords data
2598 for (int i = 0; i < tcCounter; i++)
2599 {
2600 mesh.texcoords[fCounter] = mapTexcoords[i].x;
2601 mesh.texcoords[fCounter + 1] = mapTexcoords[i].y;
2602 fCounter += 2;
2603 }
2604
2605 RL_FREE(mapVertices);
2606 RL_FREE(mapNormals);
2607 RL_FREE(mapTexcoords);
2608
2609 UnloadImageColors(pixels); // Unload pixels color data
2610
2611 // Upload vertex data to GPU (static mesh)
2612 UploadMesh(&mesh, false);
2613
2614 return mesh;
2615 }
2616 #endif // SUPPORT_MESH_GENERATION
2617
2618 // Compute mesh bounding box limits
2619 // NOTE: minVertex and maxVertex should be transformed by model transform matrix
MeshBoundingBox(Mesh mesh)2620 BoundingBox MeshBoundingBox(Mesh mesh)
2621 {
2622 // Get min and max vertex to construct bounds (AABB)
2623 Vector3 minVertex = { 0 };
2624 Vector3 maxVertex = { 0 };
2625
2626 if (mesh.vertices != NULL)
2627 {
2628 minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
2629 maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
2630
2631 for (int i = 1; i < mesh.vertexCount; i++)
2632 {
2633 minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
2634 maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
2635 }
2636 }
2637
2638 // Create the bounding box
2639 BoundingBox box = { 0 };
2640 box.min = minVertex;
2641 box.max = maxVertex;
2642
2643 return box;
2644 }
2645
2646 // Compute mesh tangents
2647 // NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates
2648 // Implementation base don: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html
MeshTangents(Mesh * mesh)2649 void MeshTangents(Mesh *mesh)
2650 {
2651 if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
2652 else TRACELOG(LOG_WARNING, "MESH: Tangents data already available, re-writting");
2653
2654 Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
2655 Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
2656
2657 for (int i = 0; i < mesh->vertexCount; i += 3)
2658 {
2659 // Get triangle vertices
2660 Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] };
2661 Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] };
2662 Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] };
2663
2664 // Get triangle texcoords
2665 Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] };
2666 Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] };
2667 Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] };
2668
2669 float x1 = v2.x - v1.x;
2670 float y1 = v2.y - v1.y;
2671 float z1 = v2.z - v1.z;
2672 float x2 = v3.x - v1.x;
2673 float y2 = v3.y - v1.y;
2674 float z2 = v3.z - v1.z;
2675
2676 float s1 = uv2.x - uv1.x;
2677 float t1 = uv2.y - uv1.y;
2678 float s2 = uv3.x - uv1.x;
2679 float t2 = uv3.y - uv1.y;
2680
2681 float div = s1*t2 - s2*t1;
2682 float r = (div == 0.0f)? 0.0f : 1.0f/div;
2683
2684 Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
2685 Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
2686
2687 tan1[i + 0] = sdir;
2688 tan1[i + 1] = sdir;
2689 tan1[i + 2] = sdir;
2690
2691 tan2[i + 0] = tdir;
2692 tan2[i + 1] = tdir;
2693 tan2[i + 2] = tdir;
2694 }
2695
2696 // Compute tangents considering normals
2697 for (int i = 0; i < mesh->vertexCount; ++i)
2698 {
2699 Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
2700 Vector3 tangent = tan1[i];
2701
2702 // TODO: Review, not sure if tangent computation is right, just used reference proposed maths...
2703 #if defined(COMPUTE_TANGENTS_METHOD_01)
2704 Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
2705 tmp = Vector3Normalize(tmp);
2706 mesh->tangents[i*4 + 0] = tmp.x;
2707 mesh->tangents[i*4 + 1] = tmp.y;
2708 mesh->tangents[i*4 + 2] = tmp.z;
2709 mesh->tangents[i*4 + 3] = 1.0f;
2710 #else
2711 Vector3OrthoNormalize(&normal, &tangent);
2712 mesh->tangents[i*4 + 0] = tangent.x;
2713 mesh->tangents[i*4 + 1] = tangent.y;
2714 mesh->tangents[i*4 + 2] = tangent.z;
2715 mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f;
2716 #endif
2717 }
2718
2719 RL_FREE(tan1);
2720 RL_FREE(tan2);
2721
2722 // Load a new tangent attributes buffer
2723 mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false);
2724
2725 TRACELOG(LOG_INFO, "MESH: Tangents data computed for provided mesh");
2726 }
2727
2728 // Compute mesh binormals (aka bitangent)
MeshBinormals(Mesh * mesh)2729 void MeshBinormals(Mesh *mesh)
2730 {
2731 for (int i = 0; i < mesh->vertexCount; i++)
2732 {
2733 //Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
2734 //Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
2735 //Vector3 binormal = Vector3Scale(Vector3CrossProduct(normal, tangent), mesh->tangents[i*4 + 3]);
2736
2737 // TODO: Register computed binormal in mesh->binormal?
2738 }
2739 }
2740
2741 // Draw a model (with texture if set)
DrawModel(Model model,Vector3 position,float scale,Color tint)2742 void DrawModel(Model model, Vector3 position, float scale, Color tint)
2743 {
2744 Vector3 vScale = { scale, scale, scale };
2745 Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f };
2746
2747 DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint);
2748 }
2749
2750 // Draw a model with extended parameters
DrawModelEx(Model model,Vector3 position,Vector3 rotationAxis,float rotationAngle,Vector3 scale,Color tint)2751 void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
2752 {
2753 // Calculate transformation matrix from function parameters
2754 // Get transform matrix (rotation -> scale -> translation)
2755 Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
2756 Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
2757 Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
2758
2759 Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
2760
2761 // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
2762 model.transform = MatrixMultiply(model.transform, matTransform);
2763
2764 for (int i = 0; i < model.meshCount; i++)
2765 {
2766 Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color;
2767
2768 Color colorTint = WHITE;
2769 colorTint.r = (unsigned char)((((float)color.r/255.0)*((float)tint.r/255.0))*255.0f);
2770 colorTint.g = (unsigned char)((((float)color.g/255.0)*((float)tint.g/255.0))*255.0f);
2771 colorTint.b = (unsigned char)((((float)color.b/255.0)*((float)tint.b/255.0))*255.0f);
2772 colorTint.a = (unsigned char)((((float)color.a/255.0)*((float)tint.a/255.0))*255.0f);
2773
2774 model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint;
2775 DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
2776 model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color;
2777 }
2778 }
2779
2780 // Draw a model wires (with texture if set)
DrawModelWires(Model model,Vector3 position,float scale,Color tint)2781 void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
2782 {
2783 rlEnableWireMode();
2784
2785 DrawModel(model, position, scale, tint);
2786
2787 rlDisableWireMode();
2788 }
2789
2790 // Draw a model wires (with texture if set) with extended parameters
DrawModelWiresEx(Model model,Vector3 position,Vector3 rotationAxis,float rotationAngle,Vector3 scale,Color tint)2791 void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
2792 {
2793 rlEnableWireMode();
2794
2795 DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
2796
2797 rlDisableWireMode();
2798 }
2799
2800 // Draw a billboard
DrawBillboard(Camera camera,Texture2D texture,Vector3 center,float size,Color tint)2801 void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
2802 {
2803 Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
2804
2805 DrawBillboardRec(camera, texture, source, center, size, tint);
2806 }
2807
2808 // Draw a billboard (part of a texture defined by a rectangle)
DrawBillboardRec(Camera camera,Texture2D texture,Rectangle source,Vector3 center,float size,Color tint)2809 void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 center, float size, Color tint)
2810 {
2811 // NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width
2812 Vector2 sizeRatio = { size, size*(float)source.height/source.width };
2813
2814 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
2815
2816 Vector3 right = { matView.m0, matView.m4, matView.m8 };
2817 //Vector3 up = { matView.m1, matView.m5, matView.m9 };
2818
2819 // NOTE: Billboard locked on axis-Y
2820 Vector3 up = { 0.0f, 1.0f, 0.0f };
2821 /*
2822 a-------b
2823 | |
2824 | * |
2825 | |
2826 d-------c
2827 */
2828 right = Vector3Scale(right, sizeRatio.x/2);
2829 up = Vector3Scale(up, sizeRatio.y/2);
2830
2831 Vector3 p1 = Vector3Add(right, up);
2832 Vector3 p2 = Vector3Subtract(right, up);
2833
2834 Vector3 a = Vector3Subtract(center, p2);
2835 Vector3 b = Vector3Add(center, p1);
2836 Vector3 c = Vector3Add(center, p2);
2837 Vector3 d = Vector3Subtract(center, p1);
2838
2839 rlCheckRenderBatchLimit(4);
2840
2841 rlSetTexture(texture.id);
2842
2843 rlBegin(RL_QUADS);
2844 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
2845
2846 // Bottom-left corner for texture and quad
2847 rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height);
2848 rlVertex3f(a.x, a.y, a.z);
2849
2850 // Top-left corner for texture and quad
2851 rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height);
2852 rlVertex3f(d.x, d.y, d.z);
2853
2854 // Top-right corner for texture and quad
2855 rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height);
2856 rlVertex3f(c.x, c.y, c.z);
2857
2858 // Bottom-right corner for texture and quad
2859 rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height);
2860 rlVertex3f(b.x, b.y, b.z);
2861 rlEnd();
2862
2863 rlSetTexture(0);
2864 }
2865
2866 // Draw a bounding box with wires
DrawBoundingBox(BoundingBox box,Color color)2867 void DrawBoundingBox(BoundingBox box, Color color)
2868 {
2869 Vector3 size;
2870
2871 size.x = fabsf(box.max.x - box.min.x);
2872 size.y = fabsf(box.max.y - box.min.y);
2873 size.z = fabsf(box.max.z - box.min.z);
2874
2875 Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f };
2876
2877 DrawCubeWires(center, size.x, size.y, size.z, color);
2878 }
2879
2880 // Detect collision between two spheres
CheckCollisionSpheres(Vector3 center1,float radius1,Vector3 center2,float radius2)2881 bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2)
2882 {
2883 bool collision = false;
2884
2885 // Simple way to check for collision, just checking distance between two points
2886 // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution
2887 /*
2888 float dx = center1.x - center2.x; // X distance between centers
2889 float dy = center1.y - center2.y; // Y distance between centers
2890 float dz = center1.z - center2.z; // Z distance between centers
2891
2892 float distance = sqrtf(dx*dx + dy*dy + dz*dz); // Distance between centers
2893
2894 if (distance <= (radius1 + radius2)) collision = true;
2895 */
2896
2897 // Check for distances squared to avoid sqrtf()
2898 if (Vector3DotProduct(Vector3Subtract(center2, center1), Vector3Subtract(center2, center1)) <= (radius1 + radius2)*(radius1 + radius2)) collision = true;
2899
2900 return collision;
2901 }
2902
2903 // Detect collision between two boxes
2904 // NOTE: Boxes are defined by two points minimum and maximum
CheckCollisionBoxes(BoundingBox box1,BoundingBox box2)2905 bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2)
2906 {
2907 bool collision = true;
2908
2909 if ((box1.max.x >= box2.min.x) && (box1.min.x <= box2.max.x))
2910 {
2911 if ((box1.max.y < box2.min.y) || (box1.min.y > box2.max.y)) collision = false;
2912 if ((box1.max.z < box2.min.z) || (box1.min.z > box2.max.z)) collision = false;
2913 }
2914 else collision = false;
2915
2916 return collision;
2917 }
2918
2919 // Detect collision between box and sphere
CheckCollisionBoxSphere(BoundingBox box,Vector3 center,float radius)2920 bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius)
2921 {
2922 bool collision = false;
2923
2924 float dmin = 0;
2925
2926 if (center.x < box.min.x) dmin += powf(center.x - box.min.x, 2);
2927 else if (center.x > box.max.x) dmin += powf(center.x - box.max.x, 2);
2928
2929 if (center.y < box.min.y) dmin += powf(center.y - box.min.y, 2);
2930 else if (center.y > box.max.y) dmin += powf(center.y - box.max.y, 2);
2931
2932 if (center.z < box.min.z) dmin += powf(center.z - box.min.z, 2);
2933 else if (center.z > box.max.z) dmin += powf(center.z - box.max.z, 2);
2934
2935 if (dmin <= (radius*radius)) collision = true;
2936
2937 return collision;
2938 }
2939
2940 // Detect collision between ray and sphere
CheckCollisionRaySphere(Ray ray,Vector3 center,float radius)2941 bool CheckCollisionRaySphere(Ray ray, Vector3 center, float radius)
2942 {
2943 bool collision = false;
2944
2945 Vector3 raySpherePos = Vector3Subtract(center, ray.position);
2946 float distance = Vector3Length(raySpherePos);
2947 float vector = Vector3DotProduct(raySpherePos, ray.direction);
2948 float d = radius*radius - (distance*distance - vector*vector);
2949
2950 if (d >= 0.0f) collision = true;
2951
2952 return collision;
2953 }
2954
2955 // Detect collision between ray and sphere with extended parameters and collision point detection
CheckCollisionRaySphereEx(Ray ray,Vector3 center,float radius,Vector3 * collisionPoint)2956 bool CheckCollisionRaySphereEx(Ray ray, Vector3 center, float radius, Vector3 *collisionPoint)
2957 {
2958 bool collision = false;
2959
2960 Vector3 raySpherePos = Vector3Subtract(center, ray.position);
2961 float distance = Vector3Length(raySpherePos);
2962 float vector = Vector3DotProduct(raySpherePos, ray.direction);
2963 float d = radius*radius - (distance*distance - vector*vector);
2964
2965 if (d >= 0.0f) collision = true;
2966
2967 // Check if ray origin is inside the sphere to calculate the correct collision point
2968 float collisionDistance = 0;
2969
2970 if (distance < radius) collisionDistance = vector + sqrtf(d);
2971 else collisionDistance = vector - sqrtf(d);
2972
2973 // Calculate collision point
2974 Vector3 cPoint = Vector3Add(ray.position, Vector3Scale(ray.direction, collisionDistance));
2975
2976 collisionPoint->x = cPoint.x;
2977 collisionPoint->y = cPoint.y;
2978 collisionPoint->z = cPoint.z;
2979
2980 return collision;
2981 }
2982
2983 // Detect collision between ray and bounding box
CheckCollisionRayBox(Ray ray,BoundingBox box)2984 bool CheckCollisionRayBox(Ray ray, BoundingBox box)
2985 {
2986 bool collision = false;
2987
2988 float t[8];
2989 t[0] = (box.min.x - ray.position.x)/ray.direction.x;
2990 t[1] = (box.max.x - ray.position.x)/ray.direction.x;
2991 t[2] = (box.min.y - ray.position.y)/ray.direction.y;
2992 t[3] = (box.max.y - ray.position.y)/ray.direction.y;
2993 t[4] = (box.min.z - ray.position.z)/ray.direction.z;
2994 t[5] = (box.max.z - ray.position.z)/ray.direction.z;
2995 t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
2996 t[7] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));
2997
2998 collision = !(t[7] < 0 || t[6] > t[7]);
2999
3000 return collision;
3001 }
3002 // Get collision info between ray and mesh
GetCollisionRayMesh(Ray ray,Mesh mesh,Matrix transform)3003 RayHitInfo GetCollisionRayMesh(Ray ray, Mesh mesh, Matrix transform)
3004 {
3005 RayHitInfo result = { 0 };
3006
3007 // Check if mesh vertex data on CPU for testing
3008 if (mesh.vertices != NULL)
3009 {
3010 int triangleCount = mesh.triangleCount;
3011
3012 // Test against all triangles in mesh
3013 for (int i = 0; i < triangleCount; i++)
3014 {
3015 Vector3 a, b, c;
3016 Vector3* vertdata = (Vector3*)mesh.vertices;
3017
3018 if (mesh.indices)
3019 {
3020 a = vertdata[mesh.indices[i*3 + 0]];
3021 b = vertdata[mesh.indices[i*3 + 1]];
3022 c = vertdata[mesh.indices[i*3 + 2]];
3023 }
3024 else
3025 {
3026 a = vertdata[i*3 + 0];
3027 b = vertdata[i*3 + 1];
3028 c = vertdata[i*3 + 2];
3029 }
3030
3031 a = Vector3Transform(a, transform);
3032 b = Vector3Transform(b, transform);
3033 c = Vector3Transform(c, transform);
3034
3035 RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c);
3036
3037 if (triHitInfo.hit)
3038 {
3039 // Save the closest hit triangle
3040 if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo;
3041 }
3042 }
3043 }
3044 return result;
3045 }
3046
3047 // Get collision info between ray and model
GetCollisionRayModel(Ray ray,Model model)3048 RayHitInfo GetCollisionRayModel(Ray ray, Model model)
3049 {
3050 RayHitInfo result = { 0 };
3051
3052 for (int m = 0; m < model.meshCount; m++)
3053 {
3054 RayHitInfo meshHitInfo = GetCollisionRayMesh(ray, model.meshes[m], model.transform);
3055
3056 if (meshHitInfo.hit)
3057 {
3058 // Save the closest hit mesh
3059 if ((!result.hit) || (result.distance > meshHitInfo.distance)) result = meshHitInfo;
3060 }
3061 }
3062
3063 return result;
3064 }
3065
3066 // Get collision info between ray and triangle
3067 // NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
GetCollisionRayTriangle(Ray ray,Vector3 p1,Vector3 p2,Vector3 p3)3068 RayHitInfo GetCollisionRayTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3)
3069 {
3070 #define EPSILON 0.000001 // A small number
3071
3072 Vector3 edge1, edge2;
3073 Vector3 p, q, tv;
3074 float det, invDet, u, v, t;
3075 RayHitInfo result = {0};
3076
3077 // Find vectors for two edges sharing V1
3078 edge1 = Vector3Subtract(p2, p1);
3079 edge2 = Vector3Subtract(p3, p1);
3080
3081 // Begin calculating determinant - also used to calculate u parameter
3082 p = Vector3CrossProduct(ray.direction, edge2);
3083
3084 // If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle
3085 det = Vector3DotProduct(edge1, p);
3086
3087 // Avoid culling!
3088 if ((det > -EPSILON) && (det < EPSILON)) return result;
3089
3090 invDet = 1.0f/det;
3091
3092 // Calculate distance from V1 to ray origin
3093 tv = Vector3Subtract(ray.position, p1);
3094
3095 // Calculate u parameter and test bound
3096 u = Vector3DotProduct(tv, p)*invDet;
3097
3098 // The intersection lies outside of the triangle
3099 if ((u < 0.0f) || (u > 1.0f)) return result;
3100
3101 // Prepare to test v parameter
3102 q = Vector3CrossProduct(tv, edge1);
3103
3104 // Calculate V parameter and test bound
3105 v = Vector3DotProduct(ray.direction, q)*invDet;
3106
3107 // The intersection lies outside of the triangle
3108 if ((v < 0.0f) || ((u + v) > 1.0f)) return result;
3109
3110 t = Vector3DotProduct(edge2, q)*invDet;
3111
3112 if (t > EPSILON)
3113 {
3114 // Ray hit, get hit point and normal
3115 result.hit = true;
3116 result.distance = t;
3117 result.hit = true;
3118 result.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2));
3119 result.position = Vector3Add(ray.position, Vector3Scale(ray.direction, t));
3120 }
3121
3122 return result;
3123 }
3124
3125 // Get collision info between ray and ground plane (Y-normal plane)
GetCollisionRayGround(Ray ray,float groundHeight)3126 RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight)
3127 {
3128 #define EPSILON 0.000001 // A small number
3129
3130 RayHitInfo result = { 0 };
3131
3132 if (fabsf(ray.direction.y) > EPSILON)
3133 {
3134 float distance = (ray.position.y - groundHeight)/-ray.direction.y;
3135
3136 if (distance >= 0.0)
3137 {
3138 result.hit = true;
3139 result.distance = distance;
3140 result.normal = (Vector3){ 0.0, 1.0, 0.0 };
3141 result.position = Vector3Add(ray.position, Vector3Scale(ray.direction, distance));
3142 result.position.y = groundHeight;
3143 }
3144 }
3145
3146 return result;
3147 }
3148
3149 //----------------------------------------------------------------------------------
3150 // Module specific Functions Definition
3151 //----------------------------------------------------------------------------------
3152
3153 #if defined(SUPPORT_FILEFORMAT_OBJ)
3154 // Load OBJ mesh data
LoadOBJ(const char * fileName)3155 static Model LoadOBJ(const char *fileName)
3156 {
3157 Model model = { 0 };
3158
3159 tinyobj_attrib_t attrib = { 0 };
3160 tinyobj_shape_t *meshes = NULL;
3161 unsigned int meshCount = 0;
3162
3163 tinyobj_material_t *materials = NULL;
3164 unsigned int materialCount = 0;
3165
3166 char *fileData = LoadFileText(fileName);
3167
3168 if (fileData != NULL)
3169 {
3170 unsigned int dataSize = (unsigned int)strlen(fileData);
3171 char currentDir[1024] = { 0 };
3172 strcpy(currentDir, GetWorkingDirectory());
3173 const char *workingDir = GetDirectoryPath(fileName);
3174 if (CHDIR(workingDir) != 0)
3175 {
3176 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
3177 }
3178
3179 unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
3180 int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, fileData, dataSize, flags);
3181
3182 if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName);
3183 else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount);
3184
3185 model.meshCount = materialCount;
3186
3187 // Init model materials array
3188 if (materialCount > 0)
3189 {
3190 model.materialCount = materialCount;
3191 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
3192 TraceLog(LOG_INFO, "MODEL: model has %i material meshes", materialCount);
3193 }
3194 else
3195 {
3196 model.meshCount = 1;
3197 TraceLog(LOG_INFO, "MODEL: No materials, putting all meshes in a default material");
3198 }
3199
3200 model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
3201 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
3202
3203 // count the faces for each material
3204 int *matFaces = RL_CALLOC(meshCount, sizeof(int));
3205
3206 for (unsigned int mi = 0; mi < meshCount; mi++)
3207 {
3208 for (unsigned int fi = 0; fi < meshes[mi].length; fi++)
3209 {
3210 int idx = attrib.material_ids[meshes[mi].face_offset + fi];
3211 if (idx == -1) idx = 0; // for no material face (which could be the whole model)
3212 matFaces[idx]++;
3213 }
3214 }
3215
3216 //--------------------------------------
3217 // create the material meshes
3218
3219 // running counts / indexes for each material mesh as we are
3220 // building them at the same time
3221 int *vCount = RL_CALLOC(model.meshCount, sizeof(int));
3222 int *vtCount = RL_CALLOC(model.meshCount, sizeof(int));
3223 int *vnCount = RL_CALLOC(model.meshCount, sizeof(int));
3224 int *faceCount = RL_CALLOC(model.meshCount, sizeof(int));
3225
3226 // allocate space for each of the material meshes
3227 for (int mi = 0; mi < model.meshCount; mi++)
3228 {
3229 model.meshes[mi].vertexCount = matFaces[mi]*3;
3230 model.meshes[mi].triangleCount = matFaces[mi];
3231 model.meshes[mi].vertices = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float));
3232 model.meshes[mi].texcoords = (float *)RL_CALLOC(model.meshes[mi].vertexCount*2, sizeof(float));
3233 model.meshes[mi].normals = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float));
3234 model.meshMaterial[mi] = mi;
3235 }
3236
3237 // scan through the combined sub meshes and pick out each material mesh
3238 for (unsigned int af = 0; af < attrib.num_faces; af++)
3239 {
3240 int mm = attrib.material_ids[af]; // mesh material for this face
3241 if (mm == -1) { mm = 0; } // no material object..
3242
3243 // Get indices for the face
3244 tinyobj_vertex_index_t idx0 = attrib.faces[3*af + 0];
3245 tinyobj_vertex_index_t idx1 = attrib.faces[3*af + 1];
3246 tinyobj_vertex_index_t idx2 = attrib.faces[3*af + 2];
3247
3248 // Fill vertices buffer (float) using vertex index of the face
3249 for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount[mm] +=3;
3250 for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount[mm] +=3;
3251 for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount[mm] +=3;
3252
3253 if (attrib.num_texcoords > 0)
3254 {
3255 // Fill texcoords buffer (float) using vertex index of the face
3256 // NOTE: Y-coordinate must be flipped upside-down to account for
3257 // raylib's upside down textures...
3258 model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx0.vt_idx*2 + 0];
3259 model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount[mm] += 2;
3260 model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx1.vt_idx*2 + 0];
3261 model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount[mm] += 2;
3262 model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx2.vt_idx*2 + 0];
3263 model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount[mm] += 2;
3264 }
3265
3266 if (attrib.num_normals > 0)
3267 {
3268 // Fill normals buffer (float) using vertex index of the face
3269 for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount[mm] +=3;
3270 for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount[mm] +=3;
3271 for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount[mm] +=3;
3272 }
3273 }
3274
3275 // Init model materials
3276 for (unsigned int m = 0; m < materialCount; m++)
3277 {
3278 // Init material to default
3279 // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
3280 model.materials[m] = LoadMaterialDefault();
3281
3282 model.materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = rlGetTextureDefault(); // Get default texture, in case no texture is defined
3283
3284 if (materials[m].diffuse_texname != NULL) model.materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd
3285 else model.materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = rlGetTextureDefault();
3286
3287 model.materials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3];
3288 model.materials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
3289
3290 if (materials[m].specular_texname != NULL) model.materials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks
3291 model.materials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2]*255.0f), 255 }; //float specular[3];
3292 model.materials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f;
3293
3294 if (materials[m].bump_texname != NULL) model.materials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump
3295 model.materials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE;
3296 model.materials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess;
3297
3298 model.materials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2]*255.0f), 255 }; //float emission[3];
3299
3300 if (materials[m].displacement_texname != NULL) model.materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp
3301 }
3302
3303 tinyobj_attrib_free(&attrib);
3304 tinyobj_shapes_free(meshes, meshCount);
3305 tinyobj_materials_free(materials, materialCount);
3306
3307 RL_FREE(fileData);
3308 RL_FREE(matFaces);
3309
3310 RL_FREE(vCount);
3311 RL_FREE(vtCount);
3312 RL_FREE(vnCount);
3313 RL_FREE(faceCount);
3314
3315 if (CHDIR(currentDir) != 0)
3316 {
3317 TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);
3318 }
3319 }
3320
3321 return model;
3322 }
3323 #endif
3324
3325 #if defined(SUPPORT_FILEFORMAT_IQM)
3326 // Load IQM mesh data
LoadIQM(const char * fileName)3327 static Model LoadIQM(const char *fileName)
3328 {
3329 #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
3330 #define IQM_VERSION 2 // only IQM version 2 supported
3331
3332 #define BONE_NAME_LENGTH 32 // BoneInfo name string length
3333 #define MESH_NAME_LENGTH 32 // Mesh name string length
3334 #define MATERIAL_NAME_LENGTH 32 // Material name string length
3335
3336 unsigned int fileSize = 0;
3337 unsigned char *fileData = LoadFileData(fileName, &fileSize);
3338 unsigned char *fileDataPtr = fileData;
3339
3340 // IQM file structs
3341 //-----------------------------------------------------------------------------------
3342 typedef struct IQMHeader {
3343 char magic[16];
3344 unsigned int version;
3345 unsigned int filesize;
3346 unsigned int flags;
3347 unsigned int num_text, ofs_text;
3348 unsigned int num_meshes, ofs_meshes;
3349 unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
3350 unsigned int num_triangles, ofs_triangles, ofs_adjacency;
3351 unsigned int num_joints, ofs_joints;
3352 unsigned int num_poses, ofs_poses;
3353 unsigned int num_anims, ofs_anims;
3354 unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
3355 unsigned int num_comment, ofs_comment;
3356 unsigned int num_extensions, ofs_extensions;
3357 } IQMHeader;
3358
3359 typedef struct IQMMesh {
3360 unsigned int name;
3361 unsigned int material;
3362 unsigned int first_vertex, num_vertexes;
3363 unsigned int first_triangle, num_triangles;
3364 } IQMMesh;
3365
3366 typedef struct IQMTriangle {
3367 unsigned int vertex[3];
3368 } IQMTriangle;
3369
3370 typedef struct IQMJoint {
3371 unsigned int name;
3372 int parent;
3373 float translate[3], rotate[4], scale[3];
3374 } IQMJoint;
3375
3376 typedef struct IQMVertexArray {
3377 unsigned int type;
3378 unsigned int flags;
3379 unsigned int format;
3380 unsigned int size;
3381 unsigned int offset;
3382 } IQMVertexArray;
3383
3384 // NOTE: Below IQM structures are not used but listed for reference
3385 /*
3386 typedef struct IQMAdjacency {
3387 unsigned int triangle[3];
3388 } IQMAdjacency;
3389
3390 typedef struct IQMPose {
3391 int parent;
3392 unsigned int mask;
3393 float channeloffset[10];
3394 float channelscale[10];
3395 } IQMPose;
3396
3397 typedef struct IQMAnim {
3398 unsigned int name;
3399 unsigned int first_frame, num_frames;
3400 float framerate;
3401 unsigned int flags;
3402 } IQMAnim;
3403
3404 typedef struct IQMBounds {
3405 float bbmin[3], bbmax[3];
3406 float xyradius, radius;
3407 } IQMBounds;
3408 */
3409 //-----------------------------------------------------------------------------------
3410
3411 // IQM vertex data types
3412 enum {
3413 IQM_POSITION = 0,
3414 IQM_TEXCOORD = 1,
3415 IQM_NORMAL = 2,
3416 IQM_TANGENT = 3, // NOTE: Tangents unused by default
3417 IQM_BLENDINDEXES = 4,
3418 IQM_BLENDWEIGHTS = 5,
3419 IQM_COLOR = 6, // NOTE: Vertex colors unused by default
3420 IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default
3421 };
3422
3423 Model model = { 0 };
3424
3425 IQMMesh *imesh = NULL;
3426 IQMTriangle *tri = NULL;
3427 IQMVertexArray *va = NULL;
3428 IQMJoint *ijoint = NULL;
3429
3430 float *vertex = NULL;
3431 float *normal = NULL;
3432 float *text = NULL;
3433 char *blendi = NULL;
3434 unsigned char *blendw = NULL;
3435
3436 // In case file can not be read, return an empty model
3437 if (fileDataPtr == NULL) return model;
3438
3439 // Read IQM header
3440 IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr;
3441
3442 if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0)
3443 {
3444 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName);
3445 return model;
3446 }
3447
3448 if (iqmHeader->version != IQM_VERSION)
3449 {
3450 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version);
3451 return model;
3452 }
3453
3454 //fileDataPtr += sizeof(IQMHeader); // Move file data pointer
3455
3456 // Meshes data processing
3457 imesh = RL_MALLOC(sizeof(IQMMesh)*iqmHeader->num_meshes);
3458 //fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET);
3459 //fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile);
3460 memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh));
3461
3462 model.meshCount = iqmHeader->num_meshes;
3463 model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
3464
3465 model.materialCount = model.meshCount;
3466 model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
3467 model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
3468
3469 char name[MESH_NAME_LENGTH] = { 0 };
3470 char material[MATERIAL_NAME_LENGTH] = { 0 };
3471
3472 for (int i = 0; i < model.meshCount; i++)
3473 {
3474 //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].name, SEEK_SET);
3475 //fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile);
3476 memcpy(name, fileDataPtr + iqmHeader->ofs_text + imesh[i].name, MESH_NAME_LENGTH*sizeof(char));
3477
3478 //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].material, SEEK_SET);
3479 //fread(material, sizeof(char)*MATERIAL_NAME_LENGTH, 1, iqmFile);
3480 memcpy(material, fileDataPtr + iqmHeader->ofs_text + imesh[i].material, MATERIAL_NAME_LENGTH*sizeof(char));
3481
3482 model.materials[i] = LoadMaterialDefault();
3483
3484 TRACELOG(LOG_DEBUG, "MODEL: [%s] mesh name (%s), material (%s)", fileName, name, material);
3485
3486 model.meshes[i].vertexCount = imesh[i].num_vertexes;
3487
3488 model.meshes[i].vertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions
3489 model.meshes[i].normals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals
3490 model.meshes[i].texcoords = RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords
3491
3492 model.meshes[i].boneIds = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported!
3493 model.meshes[i].boneWeights = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported!
3494
3495 model.meshes[i].triangleCount = imesh[i].num_triangles;
3496 model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
3497
3498 // Animated verted data, what we actually process for rendering
3499 // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
3500 model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
3501 model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
3502 }
3503
3504 // Triangles data processing
3505 tri = RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle));
3506 //fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET);
3507 //fread(tri, iqmHeader->num_triangles*sizeof(IQMTriangle), 1, iqmFile);
3508 memcpy(tri, fileDataPtr + iqmHeader->ofs_triangles, iqmHeader->num_triangles*sizeof(IQMTriangle));
3509
3510 for (int m = 0; m < model.meshCount; m++)
3511 {
3512 int tcounter = 0;
3513
3514 for (unsigned int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++)
3515 {
3516 // IQM triangles indexes are stored in counter-clockwise, but raylib processes the index in linear order,
3517 // expecting they point to the counter-clockwise vertex triangle, so we need to reverse triangle indexes
3518 // NOTE: raylib renders vertex data in counter-clockwise order (standard convention) by default
3519 model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex;
3520 model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex;
3521 model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex;
3522 tcounter += 3;
3523 }
3524 }
3525
3526 // Vertex arrays data processing
3527 va = RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
3528 //fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET);
3529 //fread(va, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray), 1, iqmFile);
3530 memcpy(va, fileDataPtr + iqmHeader->ofs_vertexarrays, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
3531
3532 for (unsigned int i = 0; i < iqmHeader->num_vertexarrays; i++)
3533 {
3534 switch (va[i].type)
3535 {
3536 case IQM_POSITION:
3537 {
3538 vertex = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
3539 //fseek(iqmFile, va[i].offset, SEEK_SET);
3540 //fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
3541 memcpy(vertex, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
3542
3543 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
3544 {
3545 int vCounter = 0;
3546 for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
3547 {
3548 model.meshes[m].vertices[vCounter] = vertex[i];
3549 model.meshes[m].animVertices[vCounter] = vertex[i];
3550 vCounter++;
3551 }
3552 }
3553 } break;
3554 case IQM_NORMAL:
3555 {
3556 normal = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
3557 //fseek(iqmFile, va[i].offset, SEEK_SET);
3558 //fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
3559 memcpy(normal, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
3560
3561 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
3562 {
3563 int vCounter = 0;
3564 for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
3565 {
3566 model.meshes[m].normals[vCounter] = normal[i];
3567 model.meshes[m].animNormals[vCounter] = normal[i];
3568 vCounter++;
3569 }
3570 }
3571 } break;
3572 case IQM_TEXCOORD:
3573 {
3574 text = RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float));
3575 //fseek(iqmFile, va[i].offset, SEEK_SET);
3576 //fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile);
3577 memcpy(text, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*2*sizeof(float));
3578
3579 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
3580 {
3581 int vCounter = 0;
3582 for (unsigned int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++)
3583 {
3584 model.meshes[m].texcoords[vCounter] = text[i];
3585 vCounter++;
3586 }
3587 }
3588 } break;
3589 case IQM_BLENDINDEXES:
3590 {
3591 blendi = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char));
3592 //fseek(iqmFile, va[i].offset, SEEK_SET);
3593 //fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile);
3594 memcpy(blendi, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(char));
3595
3596 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
3597 {
3598 int boneCounter = 0;
3599 for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
3600 {
3601 model.meshes[m].boneIds[boneCounter] = blendi[i];
3602 boneCounter++;
3603 }
3604 }
3605 } break;
3606 case IQM_BLENDWEIGHTS:
3607 {
3608 blendw = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
3609 //fseek(iqmFile, va[i].offset, SEEK_SET);
3610 //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
3611 memcpy(blendw, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
3612
3613 for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
3614 {
3615 int boneCounter = 0;
3616 for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
3617 {
3618 model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f;
3619 boneCounter++;
3620 }
3621 }
3622 } break;
3623 }
3624 }
3625
3626 // Bones (joints) data processing
3627 ijoint = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
3628 //fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET);
3629 //fread(ijoint, iqmHeader->num_joints*sizeof(IQMJoint), 1, iqmFile);
3630 memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
3631
3632 model.boneCount = iqmHeader->num_joints;
3633 model.bones = RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo));
3634 model.bindPose = RL_MALLOC(iqmHeader->num_joints*sizeof(Transform));
3635
3636 for (unsigned int i = 0; i < iqmHeader->num_joints; i++)
3637 {
3638 // Bones
3639 model.bones[i].parent = ijoint[i].parent;
3640 //fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET);
3641 //fread(model.bones[i].name, BONE_NAME_LENGTH*sizeof(char), 1, iqmFile);
3642 memcpy(model.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char));
3643
3644 // Bind pose (base pose)
3645 model.bindPose[i].translation.x = ijoint[i].translate[0];
3646 model.bindPose[i].translation.y = ijoint[i].translate[1];
3647 model.bindPose[i].translation.z = ijoint[i].translate[2];
3648
3649 model.bindPose[i].rotation.x = ijoint[i].rotate[0];
3650 model.bindPose[i].rotation.y = ijoint[i].rotate[1];
3651 model.bindPose[i].rotation.z = ijoint[i].rotate[2];
3652 model.bindPose[i].rotation.w = ijoint[i].rotate[3];
3653
3654 model.bindPose[i].scale.x = ijoint[i].scale[0];
3655 model.bindPose[i].scale.y = ijoint[i].scale[1];
3656 model.bindPose[i].scale.z = ijoint[i].scale[2];
3657 }
3658
3659 // Build bind pose from parent joints
3660 for (int i = 0; i < model.boneCount; i++)
3661 {
3662 if (model.bones[i].parent >= 0)
3663 {
3664 model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation);
3665 model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation);
3666 model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation);
3667 model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale);
3668 }
3669 }
3670
3671 RL_FREE(fileData);
3672
3673 RL_FREE(imesh);
3674 RL_FREE(tri);
3675 RL_FREE(va);
3676 RL_FREE(vertex);
3677 RL_FREE(normal);
3678 RL_FREE(text);
3679 RL_FREE(blendi);
3680 RL_FREE(blendw);
3681 RL_FREE(ijoint);
3682
3683 return model;
3684 }
3685
3686 // Load IQM animation data
LoadIQMModelAnimations(const char * fileName,int * animCount)3687 static ModelAnimation* LoadIQMModelAnimations(const char* fileName, int* animCount)
3688 {
3689 #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
3690 #define IQM_VERSION 2 // only IQM version 2 supported
3691
3692 unsigned int fileSize = 0;
3693 unsigned char *fileData = LoadFileData(fileName, &fileSize);
3694 unsigned char *fileDataPtr = fileData;
3695
3696 typedef struct IQMHeader {
3697 char magic[16];
3698 unsigned int version;
3699 unsigned int filesize;
3700 unsigned int flags;
3701 unsigned int num_text, ofs_text;
3702 unsigned int num_meshes, ofs_meshes;
3703 unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
3704 unsigned int num_triangles, ofs_triangles, ofs_adjacency;
3705 unsigned int num_joints, ofs_joints;
3706 unsigned int num_poses, ofs_poses;
3707 unsigned int num_anims, ofs_anims;
3708 unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
3709 unsigned int num_comment, ofs_comment;
3710 unsigned int num_extensions, ofs_extensions;
3711 } IQMHeader;
3712
3713 typedef struct IQMPose {
3714 int parent;
3715 unsigned int mask;
3716 float channeloffset[10];
3717 float channelscale[10];
3718 } IQMPose;
3719
3720 typedef struct IQMAnim {
3721 unsigned int name;
3722 unsigned int first_frame, num_frames;
3723 float framerate;
3724 unsigned int flags;
3725 } IQMAnim;
3726
3727 // In case file can not be read, return an empty model
3728 if (fileDataPtr == NULL) return NULL;
3729
3730 // Read IQM header
3731 IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr;
3732
3733 if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0)
3734 {
3735 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName);
3736 return NULL;
3737 }
3738
3739 if (iqmHeader->version != IQM_VERSION)
3740 {
3741 TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version);
3742 return NULL;
3743 }
3744
3745 // Get bones data
3746 IQMPose *poses = RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose));
3747 //fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET);
3748 //fread(poses, iqmHeader->num_poses*sizeof(IQMPose), 1, iqmFile);
3749 memcpy(poses, fileDataPtr + iqmHeader->ofs_poses, iqmHeader->num_poses*sizeof(IQMPose));
3750
3751 // Get animations data
3752 *animCount = iqmHeader->num_anims;
3753 IQMAnim *anim = RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim));
3754 //fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET);
3755 //fread(anim, iqmHeader->num_anims*sizeof(IQMAnim), 1, iqmFile);
3756 memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim));
3757
3758 ModelAnimation *animations = RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation));
3759
3760 // frameposes
3761 unsigned short *framedata = RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
3762 //fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET);
3763 //fread(framedata, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short), 1, iqmFile);
3764 memcpy(framedata, fileDataPtr + iqmHeader->ofs_frames, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
3765
3766 for (unsigned int a = 0; a < iqmHeader->num_anims; a++)
3767 {
3768 animations[a].frameCount = anim[a].num_frames;
3769 animations[a].boneCount = iqmHeader->num_poses;
3770 animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
3771 animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
3772 // animations[a].framerate = anim.framerate; // TODO: Use framerate?
3773
3774 for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
3775 {
3776 strcpy(animations[a].bones[j].name, "ANIMJOINTNAME");
3777 animations[a].bones[j].parent = poses[j].parent;
3778 }
3779
3780 for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqmHeader->num_poses*sizeof(Transform));
3781
3782 int dcounter = anim[a].first_frame*iqmHeader->num_framechannels;
3783
3784 for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
3785 {
3786 for (unsigned int i = 0; i < iqmHeader->num_poses; i++)
3787 {
3788 animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0];
3789
3790 if (poses[i].mask & 0x01)
3791 {
3792 animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0];
3793 dcounter++;
3794 }
3795
3796 animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1];
3797
3798 if (poses[i].mask & 0x02)
3799 {
3800 animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1];
3801 dcounter++;
3802 }
3803
3804 animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2];
3805
3806 if (poses[i].mask & 0x04)
3807 {
3808 animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2];
3809 dcounter++;
3810 }
3811
3812 animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3];
3813
3814 if (poses[i].mask & 0x08)
3815 {
3816 animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3];
3817 dcounter++;
3818 }
3819
3820 animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4];
3821
3822 if (poses[i].mask & 0x10)
3823 {
3824 animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4];
3825 dcounter++;
3826 }
3827
3828 animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5];
3829
3830 if (poses[i].mask & 0x20)
3831 {
3832 animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5];
3833 dcounter++;
3834 }
3835
3836 animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6];
3837
3838 if (poses[i].mask & 0x40)
3839 {
3840 animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6];
3841 dcounter++;
3842 }
3843
3844 animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7];
3845
3846 if (poses[i].mask & 0x80)
3847 {
3848 animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7];
3849 dcounter++;
3850 }
3851
3852 animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8];
3853
3854 if (poses[i].mask & 0x100)
3855 {
3856 animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8];
3857 dcounter++;
3858 }
3859
3860 animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9];
3861
3862 if (poses[i].mask & 0x200)
3863 {
3864 animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9];
3865 dcounter++;
3866 }
3867
3868 animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation);
3869 }
3870 }
3871
3872 // Build frameposes
3873 for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
3874 {
3875 for (int i = 0; i < animations[a].boneCount; i++)
3876 {
3877 if (animations[a].bones[i].parent >= 0)
3878 {
3879 animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation);
3880 animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation);
3881 animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation);
3882 animations[a].framePoses[frame][i].scale = Vector3Multiply(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale);
3883 }
3884 }
3885 }
3886 }
3887
3888 RL_FREE(fileData);
3889
3890 RL_FREE(framedata);
3891 RL_FREE(poses);
3892 RL_FREE(anim);
3893
3894 return animations;
3895 }
3896
3897 #endif
3898
3899 #if defined(SUPPORT_FILEFORMAT_GLTF)
3900
3901 static const unsigned char base64Table[] = {
3902 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3903 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3904 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3905 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3906 0, 0, 0, 62, 0, 0, 0, 63, 52, 53,
3907 54, 55, 56, 57, 58, 59, 60, 61, 0, 0,
3908 0, 0, 0, 0, 0, 0, 1, 2, 3, 4,
3909 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
3910 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
3911 25, 0, 0, 0, 0, 0, 0, 26, 27, 28,
3912 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
3913 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
3914 49, 50, 51
3915 };
3916
GetSizeBase64(char * input)3917 static int GetSizeBase64(char *input)
3918 {
3919 int size = 0;
3920
3921 for (int i = 0; input[4*i] != 0; i++)
3922 {
3923 if (input[4*i + 3] == '=')
3924 {
3925 if (input[4*i + 2] == '=') size += 1;
3926 else size += 2;
3927 }
3928 else size += 3;
3929 }
3930
3931 return size;
3932 }
3933
DecodeBase64(char * input,int * size)3934 static unsigned char *DecodeBase64(char *input, int *size)
3935 {
3936 *size = GetSizeBase64(input);
3937
3938 unsigned char *buf = (unsigned char *)RL_MALLOC(*size);
3939 for (int i = 0; i < *size/3; i++)
3940 {
3941 unsigned char a = base64Table[(int)input[4*i]];
3942 unsigned char b = base64Table[(int)input[4*i + 1]];
3943 unsigned char c = base64Table[(int)input[4*i + 2]];
3944 unsigned char d = base64Table[(int)input[4*i + 3]];
3945
3946 buf[3*i] = (a << 2) | (b >> 4);
3947 buf[3*i + 1] = (b << 4) | (c >> 2);
3948 buf[3*i + 2] = (c << 6) | d;
3949 }
3950
3951 if (*size%3 == 1)
3952 {
3953 int n = *size/3;
3954 unsigned char a = base64Table[(int)input[4*n]];
3955 unsigned char b = base64Table[(int)input[4*n + 1]];
3956 buf[*size - 1] = (a << 2) | (b >> 4);
3957 }
3958 else if (*size%3 == 2)
3959 {
3960 int n = *size/3;
3961 unsigned char a = base64Table[(int)input[4*n]];
3962 unsigned char b = base64Table[(int)input[4*n + 1]];
3963 unsigned char c = base64Table[(int)input[4*n + 2]];
3964 buf[*size - 2] = (a << 2) | (b >> 4);
3965 buf[*size - 1] = (b << 4) | (c >> 2);
3966 }
3967 return buf;
3968 }
3969
3970 // Load texture from cgltf_image
LoadImageFromCgltfImage(cgltf_image * image,const char * texPath,Color tint)3971 static Image LoadImageFromCgltfImage(cgltf_image *image, const char *texPath, Color tint)
3972 {
3973 Image rimage = { 0 };
3974
3975 if (image->uri)
3976 {
3977 if ((strlen(image->uri) > 5) &&
3978 (image->uri[0] == 'd') &&
3979 (image->uri[1] == 'a') &&
3980 (image->uri[2] == 't') &&
3981 (image->uri[3] == 'a') &&
3982 (image->uri[4] == ':'))
3983 {
3984 // Data URI
3985 // Format: data:<mediatype>;base64,<data>
3986
3987 // Find the comma
3988 int i = 0;
3989 while ((image->uri[i] != ',') && (image->uri[i] != 0)) i++;
3990
3991 if (image->uri[i] == 0) TRACELOG(LOG_WARNING, "IMAGE: glTF data URI is not a valid image");
3992 else
3993 {
3994 int size = 0;
3995 unsigned char *data = DecodeBase64(image->uri + i + 1, &size);
3996
3997 int width, height;
3998 unsigned char *raw = stbi_load_from_memory(data, size, &width, &height, NULL, 4);
3999 RL_FREE(data);
4000
4001 rimage.data = raw;
4002 rimage.width = width;
4003 rimage.height = height;
4004 rimage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
4005 rimage.mipmaps = 1;
4006
4007 // TODO: Tint shouldn't be applied here!
4008 ImageColorTint(&rimage, tint);
4009 }
4010 }
4011 else
4012 {
4013 rimage = LoadImage(TextFormat("%s/%s", texPath, image->uri));
4014
4015 // TODO: Tint shouldn't be applied here!
4016 ImageColorTint(&rimage, tint);
4017 }
4018 }
4019 else if (image->buffer_view)
4020 {
4021 unsigned char *data = RL_MALLOC(image->buffer_view->size);
4022 int n = (int)image->buffer_view->offset;
4023 int stride = (int)image->buffer_view->stride ? (int)image->buffer_view->stride : 1;
4024
4025 for (unsigned int i = 0; i < image->buffer_view->size; i++)
4026 {
4027 data[i] = ((unsigned char *)image->buffer_view->buffer->data)[n];
4028 n += stride;
4029 }
4030
4031 int width, height;
4032 unsigned char *raw = stbi_load_from_memory(data, (int)image->buffer_view->size, &width, &height, NULL, 4);
4033 RL_FREE(data);
4034
4035 rimage.data = raw;
4036 rimage.width = width;
4037 rimage.height = height;
4038 rimage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
4039 rimage.mipmaps = 1;
4040
4041 // TODO: Tint shouldn't be applied here!
4042 ImageColorTint(&rimage, tint);
4043 }
4044 else rimage = GenImageColor(1, 1, tint);
4045
4046 return rimage;
4047 }
4048
4049
GLTFReadValue(cgltf_accessor * acc,unsigned int index,void * variable,unsigned int elements,unsigned int size)4050 static bool GLTFReadValue(cgltf_accessor* acc, unsigned int index, void *variable, unsigned int elements, unsigned int size)
4051 {
4052 if (acc->count == 2)
4053 {
4054 if (index > 1) return false;
4055
4056 memcpy(variable, index == 0 ? acc->min : acc->max, elements*size);
4057 return true;
4058 }
4059
4060 unsigned int stride = size*elements;
4061 memset(variable, 0, stride);
4062
4063 if (acc->buffer_view == NULL || acc->buffer_view->buffer == NULL || acc->buffer_view->buffer->data == NULL) return false;
4064
4065 void* readPosition = ((char *)acc->buffer_view->buffer->data) + (index*stride) + acc->buffer_view->offset + acc->offset;
4066 memcpy(variable, readPosition, stride);
4067 return true;
4068 }
4069
4070 // LoadGLTF loads in model data from given filename, supporting both .gltf and .glb
LoadGLTF(const char * fileName)4071 static Model LoadGLTF(const char *fileName)
4072 {
4073 /***********************************************************************************
4074
4075 Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend) and Hristo Stamenov(@object71)
4076
4077 Features:
4078 - Supports .gltf and .glb files
4079 - Supports embedded (base64) or external textures
4080 - Loads all raylib supported material textures, values and colors
4081 - Supports multiple mesh per model and multiple primitives per model
4082
4083 Some restrictions (not exhaustive):
4084 - Triangle-only meshes
4085 - Not supported node hierarchies or transforms
4086 - Only supports unsigned short indices (no byte/unsigned int)
4087 - Only supports float for texture coordinates (no byte/unsigned short)
4088
4089 *************************************************************************************/
4090
4091 Model model = { 0 };
4092
4093 // glTF file loading
4094 unsigned int dataSize = 0;
4095 unsigned char *fileData = LoadFileData(fileName, &dataSize);
4096
4097 if (fileData == NULL) return model;
4098
4099 // glTF data loading
4100 cgltf_options options = { 0 };
4101 cgltf_data *data = NULL;
4102 cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
4103
4104 if (result == cgltf_result_success)
4105 {
4106 TRACELOG(LOG_INFO, "MODEL: [%s] glTF meshes (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->meshes_count);
4107 TRACELOG(LOG_INFO, "MODEL: [%s] glTF materials (%s) count: %i", fileName, (data->file_type == 2)? "glb" : "gltf", data->materials_count);
4108
4109 // Read data buffers
4110 result = cgltf_load_buffers(&options, data, fileName);
4111 if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName);
4112
4113 int primitivesCount = 0;
4114
4115 for (unsigned int i = 0; i < data->meshes_count; i++)
4116 primitivesCount += (int)data->meshes[i].primitives_count;
4117
4118 // Process glTF data and map to model
4119 model.meshCount = primitivesCount;
4120 model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
4121 model.materialCount = (int)data->materials_count + 1;
4122 model.materials = RL_MALLOC(model.materialCount*sizeof(Material));
4123 model.meshMaterial = RL_MALLOC(model.meshCount*sizeof(int));
4124 model.boneCount = (int)data->nodes_count;
4125 model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo));
4126 model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform));
4127
4128 InitGLTFBones(&model, data);
4129 LoadGLTFMaterial(&model, fileName, data);
4130
4131 int primitiveIndex = 0;
4132
4133 for (unsigned int i = 0; i < data->meshes_count; i++)
4134 {
4135 for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++)
4136 {
4137 for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++)
4138 {
4139 if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position)
4140 {
4141 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
4142 model.meshes[primitiveIndex].vertexCount = (int)acc->count;
4143 int bufferSize = model.meshes[primitiveIndex].vertexCount*3*sizeof(float);
4144 model.meshes[primitiveIndex].vertices = RL_MALLOC(bufferSize);
4145 model.meshes[primitiveIndex].animVertices = RL_MALLOC(bufferSize);
4146
4147 if (acc->component_type == cgltf_component_type_r_32f)
4148 {
4149 for (int a = 0; a < acc->count; a++)
4150 {
4151 GLTFReadValue(acc, a, model.meshes[primitiveIndex].vertices + (a*3), 3, sizeof(float));
4152 }
4153 }
4154 else if (acc->component_type == cgltf_component_type_r_32u)
4155 {
4156 int readValue[3];
4157 for (int a = 0; a < acc->count; a++)
4158 {
4159 GLTFReadValue(acc, a, readValue, 3, sizeof(int));
4160 model.meshes[primitiveIndex].vertices[(a*3) + 0] = (float)readValue[0];
4161 model.meshes[primitiveIndex].vertices[(a*3) + 1] = (float)readValue[1];
4162 model.meshes[primitiveIndex].vertices[(a*3) + 2] = (float)readValue[2];
4163 }
4164 }
4165 else
4166 {
4167 // TODO: Support normalized unsigned byte/unsigned short vertices
4168 TRACELOG(LOG_WARNING, "MODEL: [%s] glTF vertices must be float or int", fileName);
4169 }
4170
4171 memcpy(model.meshes[primitiveIndex].animVertices, model.meshes[primitiveIndex].vertices, bufferSize);
4172 }
4173 else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_normal)
4174 {
4175 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
4176
4177 int bufferSize = (int)(acc->count*3*sizeof(float));
4178 model.meshes[primitiveIndex].normals = RL_MALLOC(bufferSize);
4179 model.meshes[primitiveIndex].animNormals = RL_MALLOC(bufferSize);
4180
4181 if (acc->component_type == cgltf_component_type_r_32f)
4182 {
4183 for (int a = 0; a < acc->count; a++)
4184 {
4185 GLTFReadValue(acc, a, model.meshes[primitiveIndex].normals + (a*3), 3, sizeof(float));
4186 }
4187 }
4188 else if (acc->component_type == cgltf_component_type_r_32u)
4189 {
4190 int readValue[3];
4191 for (int a = 0; a < acc->count; a++)
4192 {
4193 GLTFReadValue(acc, a, readValue, 3, sizeof(int));
4194 model.meshes[primitiveIndex].normals[(a*3) + 0] = (float)readValue[0];
4195 model.meshes[primitiveIndex].normals[(a*3) + 1] = (float)readValue[1];
4196 model.meshes[primitiveIndex].normals[(a*3) + 2] = (float)readValue[2];
4197 }
4198 }
4199 else
4200 {
4201 // TODO: Support normalized unsigned byte/unsigned short normals
4202 TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
4203 }
4204
4205 memcpy(model.meshes[primitiveIndex].animNormals, model.meshes[primitiveIndex].normals, bufferSize);
4206 }
4207 else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_texcoord)
4208 {
4209 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
4210
4211 if (acc->component_type == cgltf_component_type_r_32f)
4212 {
4213 model.meshes[primitiveIndex].texcoords = RL_MALLOC(acc->count*2*sizeof(float));
4214
4215 for (int a = 0; a < acc->count; a++)
4216 {
4217 GLTFReadValue(acc, a, model.meshes[primitiveIndex].texcoords + (a*2), 2, sizeof(float));
4218 }
4219 }
4220 else
4221 {
4222 // TODO: Support normalized unsigned byte/unsigned short texture coordinates
4223 TRACELOG(LOG_WARNING, "MODEL: [%s] glTF texture coordinates must be float", fileName);
4224 }
4225 }
4226 else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_joints)
4227 {
4228 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
4229 LoadGLTFBoneAttribute(&model, acc, data, primitiveIndex);
4230 }
4231 else if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_weights)
4232 {
4233 cgltf_accessor *acc = data->meshes[i].primitives[p].attributes[j].data;
4234
4235 model.meshes[primitiveIndex].boneWeights = RL_MALLOC(acc->count*4*sizeof(float));
4236
4237 if (acc->component_type == cgltf_component_type_r_32f)
4238 {
4239 for (int a = 0; a < acc->count; a++)
4240 {
4241 GLTFReadValue(acc, a, model.meshes[primitiveIndex].boneWeights + (a*4), 4, sizeof(float));
4242 }
4243 }
4244 else if (acc->component_type == cgltf_component_type_r_32u)
4245 {
4246 unsigned int readValue[4];
4247 for (int a = 0; a < acc->count; a++)
4248 {
4249 GLTFReadValue(acc, a, readValue, 4, sizeof(unsigned int));
4250 model.meshes[primitiveIndex].normals[(a*4) + 0] = (float)readValue[0];
4251 model.meshes[primitiveIndex].normals[(a*4) + 1] = (float)readValue[1];
4252 model.meshes[primitiveIndex].normals[(a*4) + 2] = (float)readValue[2];
4253 model.meshes[primitiveIndex].normals[(a*4) + 3] = (float)readValue[3];
4254 }
4255 }
4256 else
4257 {
4258 // TODO: Support normalized unsigned byte/unsigned short weights
4259 TRACELOG(LOG_WARNING, "MODEL: [%s] glTF normals must be float or int", fileName);
4260 }
4261 }
4262 }
4263
4264 cgltf_accessor *acc = data->meshes[i].primitives[p].indices;
4265 LoadGLTFModelIndices(&model, acc, primitiveIndex);
4266
4267 if (data->meshes[i].primitives[p].material)
4268 {
4269 // Compute the offset
4270 model.meshMaterial[primitiveIndex] = (int)(data->meshes[i].primitives[p].material - data->materials);
4271 }
4272 else
4273 {
4274 model.meshMaterial[primitiveIndex] = model.materialCount - 1;
4275 }
4276
4277 BindGLTFPrimitiveToBones(&model, data, primitiveIndex);
4278
4279 primitiveIndex++;
4280 }
4281
4282 }
4283
4284 cgltf_free(data);
4285 }
4286 else TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName);
4287
4288 RL_FREE(fileData);
4289
4290 return model;
4291 }
4292
InitGLTFBones(Model * model,const cgltf_data * data)4293 static void InitGLTFBones(Model* model, const cgltf_data* data)
4294 {
4295 for (unsigned int j = 0; j < data->nodes_count; j++)
4296 {
4297 strcpy(model->bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name);
4298 model->bones[j].parent = (data->nodes[j].parent != NULL) ? (int)(data->nodes[j].parent - data->nodes) : -1;
4299 }
4300
4301 for (unsigned int i = 0; i < data->nodes_count; i++)
4302 {
4303 if (data->nodes[i].has_translation) memcpy(&model->bindPose[i].translation, data->nodes[i].translation, 3*sizeof(float));
4304 else model->bindPose[i].translation = Vector3Zero();
4305
4306 if (data->nodes[i].has_rotation) memcpy(&model->bindPose[i].rotation, data->nodes[i].rotation, 4*sizeof(float));
4307 else model->bindPose[i].rotation = QuaternionIdentity();
4308
4309 model->bindPose[i].rotation = QuaternionNormalize(model->bindPose[i].rotation);
4310
4311 if (data->nodes[i].has_scale) memcpy(&model->bindPose[i].scale, data->nodes[i].scale, 3*sizeof(float));
4312 else model->bindPose[i].scale = Vector3One();
4313 }
4314
4315 {
4316 bool* completedBones = RL_CALLOC(model->boneCount, sizeof(bool));
4317 int numberCompletedBones = 0;
4318
4319 while (numberCompletedBones < model->boneCount) {
4320 for (int i = 0; i < model->boneCount; i++)
4321 {
4322 if (completedBones[i]) continue;
4323
4324 if (model->bones[i].parent < 0) {
4325 completedBones[i] = true;
4326 numberCompletedBones++;
4327 continue;
4328 }
4329
4330 if (!completedBones[model->bones[i].parent]) continue;
4331
4332 Transform* currentTransform = &model->bindPose[i];
4333 BoneInfo* currentBone = &model->bones[i];
4334 int root = currentBone->parent;
4335 if (root >= model->boneCount)
4336 root = 0;
4337 Transform* parentTransform = &model->bindPose[root];
4338
4339 currentTransform->rotation = QuaternionMultiply(parentTransform->rotation, currentTransform->rotation);
4340 currentTransform->translation = Vector3RotateByQuaternion(currentTransform->translation, parentTransform->rotation);
4341 currentTransform->translation = Vector3Add(currentTransform->translation, parentTransform->translation);
4342 currentTransform->scale = Vector3Multiply(currentTransform->scale, parentTransform->scale);
4343 completedBones[i] = true;
4344 numberCompletedBones++;
4345 }
4346 }
4347
4348 RL_FREE(completedBones);
4349 }
4350 }
4351
LoadGLTFMaterial(Model * model,const char * fileName,const cgltf_data * data)4352 static void LoadGLTFMaterial(Model* model, const char* fileName, const cgltf_data* data)
4353 {
4354 for (int i = 0; i < model->materialCount - 1; i++)
4355 {
4356 model->materials[i] = LoadMaterialDefault();
4357 Color tint = (Color){ 255, 255, 255, 255 };
4358 const char *texPath = GetDirectoryPath(fileName);
4359
4360 // Ensure material follows raylib support for PBR (metallic/roughness flow)
4361 if (data->materials[i].has_pbr_metallic_roughness)
4362 {
4363 tint.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255);
4364 tint.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255);
4365 tint.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255);
4366 tint.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255);
4367
4368 model->materials[i].maps[MATERIAL_MAP_ALBEDO].color = tint;
4369
4370 if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture)
4371 {
4372 Image albedo = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath, tint);
4373 model->materials[i].maps[MATERIAL_MAP_ALBEDO].texture = LoadTextureFromImage(albedo);
4374 UnloadImage(albedo);
4375 }
4376
4377 tint = WHITE; // Set tint to white after it's been used by Albedo
4378
4379 if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture)
4380 {
4381 Image metallicRoughness = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath, tint);
4382 model->materials[i].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(metallicRoughness);
4383
4384 float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor;
4385 model->materials[i].maps[MATERIAL_MAP_ROUGHNESS].value = roughness;
4386
4387 float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor;
4388 model->materials[i].maps[MATERIAL_MAP_METALNESS].value = metallic;
4389
4390 UnloadImage(metallicRoughness);
4391 }
4392
4393 if (data->materials[i].normal_texture.texture)
4394 {
4395 Image normalImage = LoadImageFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath, tint);
4396 model->materials[i].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(normalImage);
4397 UnloadImage(normalImage);
4398 }
4399
4400 if (data->materials[i].occlusion_texture.texture)
4401 {
4402 Image occulsionImage = LoadImageFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath, tint);
4403 model->materials[i].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(occulsionImage);
4404 UnloadImage(occulsionImage);
4405 }
4406
4407 if (data->materials[i].emissive_texture.texture)
4408 {
4409 Image emissiveImage = LoadImageFromCgltfImage(data->materials[i].emissive_texture.texture->image, texPath, tint);
4410 model->materials[i].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(emissiveImage);
4411 tint.r = (unsigned char)(data->materials[i].emissive_factor[0]*255);
4412 tint.g = (unsigned char)(data->materials[i].emissive_factor[1]*255);
4413 tint.b = (unsigned char)(data->materials[i].emissive_factor[2]*255);
4414 model->materials[i].maps[MATERIAL_MAP_EMISSION].color = tint;
4415 UnloadImage(emissiveImage);
4416 }
4417 }
4418 }
4419
4420 model->materials[model->materialCount - 1] = LoadMaterialDefault();
4421 }
4422
LoadGLTFBoneAttribute(Model * model,cgltf_accessor * jointsAccessor,const cgltf_data * data,int primitiveIndex)4423 static void LoadGLTFBoneAttribute(Model* model, cgltf_accessor* jointsAccessor, const cgltf_data* data, int primitiveIndex)
4424 {
4425 if (jointsAccessor->component_type == cgltf_component_type_r_16u)
4426 {
4427 model->meshes[primitiveIndex].boneIds = RL_MALLOC(sizeof(int)*jointsAccessor->count*4);
4428 short* bones = RL_MALLOC(sizeof(short)*jointsAccessor->count*4);
4429
4430 for (int a = 0; a < jointsAccessor->count; a++)
4431 {
4432 GLTFReadValue(jointsAccessor, a, bones + (a*4), 4, sizeof(short));
4433 }
4434
4435 for (unsigned int a = 0; a < jointsAccessor->count*4; a++)
4436 {
4437 cgltf_node* skinJoint = data->skins->joints[bones[a]];
4438
4439 for (unsigned int k = 0; k < data->nodes_count; k++)
4440 {
4441 if (&(data->nodes[k]) == skinJoint)
4442 {
4443 model->meshes[primitiveIndex].boneIds[a] = k;
4444 break;
4445 }
4446 }
4447 }
4448 RL_FREE(bones);
4449 }
4450 else if (jointsAccessor->component_type == cgltf_component_type_r_8u)
4451 {
4452 model->meshes[primitiveIndex].boneIds = RL_MALLOC(sizeof(int)*jointsAccessor->count*4);
4453 unsigned char* bones = RL_MALLOC(sizeof(unsigned char)*jointsAccessor->count*4);
4454
4455 for (int a = 0; a < jointsAccessor->count; a++)
4456 {
4457 GLTFReadValue(jointsAccessor, a, bones + (a*4), 4, sizeof(unsigned char));
4458 }
4459
4460 for (unsigned int a = 0; a < jointsAccessor->count*4; a++)
4461 {
4462 cgltf_node* skinJoint = data->skins->joints[bones[a]];
4463
4464 for (unsigned int k = 0; k < data->nodes_count; k++)
4465 {
4466 if (&(data->nodes[k]) == skinJoint)
4467 {
4468 model->meshes[primitiveIndex].boneIds[a] = k;
4469 break;
4470 }
4471 }
4472 }
4473 RL_FREE(bones);
4474 }
4475 else
4476 {
4477 // TODO: Support other size of bone index?
4478 TRACELOG(LOG_WARNING, "MODEL: glTF bones in unexpected format");
4479 }
4480 }
4481
BindGLTFPrimitiveToBones(Model * model,const cgltf_data * data,int primitiveIndex)4482 static void BindGLTFPrimitiveToBones(Model* model, const cgltf_data* data, int primitiveIndex)
4483 {
4484 if (model->meshes[primitiveIndex].boneIds == NULL && data->nodes_count > 0)
4485 {
4486 for (int nodeId = 0; nodeId < data->nodes_count; nodeId++)
4487 {
4488 if (data->nodes[nodeId].mesh == &(data->meshes[primitiveIndex]))
4489 {
4490 model->meshes[primitiveIndex].boneIds = RL_CALLOC(4*model->meshes[primitiveIndex].vertexCount, sizeof(int));
4491 model->meshes[primitiveIndex].boneWeights = RL_CALLOC(4*model->meshes[primitiveIndex].vertexCount, sizeof(float));
4492
4493 for (int b = 0; b < 4*model->meshes[primitiveIndex].vertexCount; b++)
4494 {
4495 if (b%4 == 0)
4496 {
4497 model->meshes[primitiveIndex].boneIds[b] = nodeId;
4498 model->meshes[primitiveIndex].boneWeights[b] = 1.0f;
4499 }
4500 else
4501 {
4502 model->meshes[primitiveIndex].boneIds[b] = 0;
4503 model->meshes[primitiveIndex].boneWeights[b] = 0.0f;
4504 }
4505
4506 }
4507
4508 Vector3 boundVertex = { 0 };
4509 Vector3 boundNormal = { 0 };
4510
4511 Vector3 outTranslation = { 0 };
4512 Quaternion outRotation = { 0 };
4513 Vector3 outScale = { 0 };
4514
4515 int vCounter = 0;
4516 int boneCounter = 0;
4517 int boneId = 0;
4518
4519 for (int i = 0; i < model->meshes[primitiveIndex].vertexCount; i++)
4520 {
4521 boneId = model->meshes[primitiveIndex].boneIds[boneCounter];
4522 outTranslation = model->bindPose[boneId].translation;
4523 outRotation = model->bindPose[boneId].rotation;
4524 outScale = model->bindPose[boneId].scale;
4525
4526 // Vertices processing
4527 boundVertex = (Vector3){ model->meshes[primitiveIndex].vertices[vCounter], model->meshes[primitiveIndex].vertices[vCounter + 1], model->meshes[primitiveIndex].vertices[vCounter + 2] };
4528 boundVertex = Vector3Multiply(boundVertex, outScale);
4529 boundVertex = Vector3RotateByQuaternion(boundVertex, outRotation);
4530 boundVertex = Vector3Add(boundVertex, outTranslation);
4531 model->meshes[primitiveIndex].vertices[vCounter] = boundVertex.x;
4532 model->meshes[primitiveIndex].vertices[vCounter + 1] = boundVertex.y;
4533 model->meshes[primitiveIndex].vertices[vCounter + 2] = boundVertex.z;
4534
4535 // Normals processing
4536 if (model->meshes[primitiveIndex].normals != NULL)
4537 {
4538 boundNormal = (Vector3){ model->meshes[primitiveIndex].normals[vCounter], model->meshes[primitiveIndex].normals[vCounter + 1], model->meshes[primitiveIndex].normals[vCounter + 2] };
4539 boundNormal = Vector3RotateByQuaternion(boundNormal, outRotation);
4540 model->meshes[primitiveIndex].normals[vCounter] = boundNormal.x;
4541 model->meshes[primitiveIndex].normals[vCounter + 1] = boundNormal.y;
4542 model->meshes[primitiveIndex].normals[vCounter + 2] = boundNormal.z;
4543 }
4544
4545 vCounter += 3;
4546 boneCounter += 4;
4547 }
4548 }
4549 }
4550 }
4551 }
4552
LoadGLTFModelIndices(Model * model,cgltf_accessor * indexAccessor,int primitiveIndex)4553 static void LoadGLTFModelIndices(Model* model, cgltf_accessor* indexAccessor, int primitiveIndex)
4554 {
4555 if (indexAccessor)
4556 {
4557 if (indexAccessor->component_type == cgltf_component_type_r_16u || indexAccessor->component_type == cgltf_component_type_r_16)
4558 {
4559 model->meshes[primitiveIndex].triangleCount = (int)indexAccessor->count/3;
4560 model->meshes[primitiveIndex].indices = RL_MALLOC(model->meshes[primitiveIndex].triangleCount*3*sizeof(unsigned short));
4561
4562 unsigned short readValue = 0;
4563 for (int a = 0; a < indexAccessor->count; a++)
4564 {
4565 GLTFReadValue(indexAccessor, a, &readValue, 1, sizeof(short));
4566 model->meshes[primitiveIndex].indices[a] = readValue;
4567 }
4568 }
4569 else if (indexAccessor->component_type == cgltf_component_type_r_8u || indexAccessor->component_type == cgltf_component_type_r_8)
4570 {
4571 model->meshes[primitiveIndex].triangleCount = (int)indexAccessor->count/3;
4572 model->meshes[primitiveIndex].indices = RL_MALLOC(model->meshes[primitiveIndex].triangleCount*3*sizeof(unsigned short));
4573
4574 unsigned char readValue = 0;
4575 for (int a = 0; a < indexAccessor->count; a++)
4576 {
4577 GLTFReadValue(indexAccessor, a, &readValue, 1, sizeof(char));
4578 model->meshes[primitiveIndex].indices[a] = (unsigned short)readValue;
4579 }
4580 }
4581 else if (indexAccessor->component_type == cgltf_component_type_r_32u)
4582 {
4583 model->meshes[primitiveIndex].triangleCount = (int)indexAccessor->count/3;
4584 model->meshes[primitiveIndex].indices = RL_MALLOC(model->meshes[primitiveIndex].triangleCount*3*sizeof(unsigned short));
4585
4586 unsigned int readValue;
4587 for (int a = 0; a < indexAccessor->count; a++)
4588 {
4589 GLTFReadValue(indexAccessor, a, &readValue, 1, sizeof(unsigned int));
4590 model->meshes[primitiveIndex].indices[a] = (unsigned short)readValue;
4591 }
4592 }
4593 }
4594 else
4595 {
4596 // Unindexed mesh
4597 model->meshes[primitiveIndex].triangleCount = model->meshes[primitiveIndex].vertexCount/3;
4598 }
4599 }
4600
4601 // LoadGLTF loads in animation data from given filename
LoadGLTFModelAnimations(const char * fileName,int * animCount)4602 static ModelAnimation *LoadGLTFModelAnimations(const char *fileName, int *animCount)
4603 {
4604 /***********************************************************************************
4605
4606 Function implemented by Hristo Stamenov (@object71)
4607
4608 Features:
4609 - Supports .gltf and .glb files
4610
4611 Some restrictions (not exhaustive):
4612 - ...
4613
4614 *************************************************************************************/
4615
4616 // glTF file loading
4617 unsigned int dataSize = 0;
4618 unsigned char *fileData = LoadFileData(fileName, &dataSize);
4619
4620 ModelAnimation *animations = NULL;
4621
4622 if (fileData == NULL) return animations;
4623
4624 // glTF data loading
4625 cgltf_options options = { 0 };
4626 cgltf_data *data = NULL;
4627 cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
4628
4629 if (result == cgltf_result_success)
4630 {
4631 TRACELOG(LOG_INFO, "MODEL: [%s] glTF animations (%s) count: %i", fileName, (data->file_type == 2)? "glb" :
4632 "gltf", data->animations_count);
4633
4634 result = cgltf_load_buffers(&options, data, fileName);
4635 if (result != cgltf_result_success) TRACELOG(LOG_WARNING, "MODEL: [%s] unable to load glTF animations data", fileName);
4636 animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation));
4637 *animCount = (int)data->animations_count;
4638
4639 for (unsigned int a = 0; a < data->animations_count; a++)
4640 {
4641 // gltf animation consists of the following structures:
4642 // - nodes - bones
4643 // - channels - single transformation type on a single bone
4644 // - node - bone
4645 // - transformation type (path) - translation, rotation, scale
4646 // - sampler - animation samples
4647 // - input - points in time this transformation happens
4648 // - output - the transformation amount at the given input points in time
4649 // - interpolation - the type of interpolation to use between the frames
4650
4651 cgltf_animation *animation = data->animations + a;
4652
4653 ModelAnimation *output = animations + a;
4654
4655 // 30 frames sampled per second
4656 const float timeStep = (1.0f/30.0f);
4657 float animationDuration = 0.0f;
4658
4659 // Getting the max animation time to consider for animation duration
4660 for (unsigned int i = 0; i < animation->channels_count; i++)
4661 {
4662 cgltf_animation_channel* channel = animation->channels + i;
4663 int frameCounts = (int)channel->sampler->input->count;
4664 float lastFrameTime = 0.0f;
4665
4666 if (GLTFReadValue(channel->sampler->input, frameCounts - 1, &lastFrameTime, 1, sizeof(float)))
4667 {
4668 animationDuration = fmaxf(lastFrameTime, animationDuration);
4669 }
4670 }
4671
4672 output->frameCount = (int)(animationDuration / timeStep);
4673 output->boneCount = (int)data->nodes_count;
4674 output->bones = RL_MALLOC(output->boneCount*sizeof(BoneInfo));
4675 output->framePoses = RL_MALLOC(output->frameCount*sizeof(Transform *));
4676 // output->framerate = // TODO: Use framerate instead of const timestep
4677
4678 // Name and parent bones
4679 for (unsigned int j = 0; j < data->nodes_count; j++)
4680 {
4681 strcpy(output->bones[j].name, data->nodes[j].name == 0 ? "ANIMJOINT" : data->nodes[j].name);
4682 output->bones[j].parent = (data->nodes[j].parent != NULL) ? (int)(data->nodes[j].parent - data->nodes) : -1;
4683 }
4684
4685 // Allocate data for frames
4686 // Initiate with zero bone translations
4687 for (int frame = 0; frame < output->frameCount; frame++)
4688 {
4689 output->framePoses[frame] = RL_MALLOC(output->frameCount*data->nodes_count*sizeof(Transform));
4690
4691 for (unsigned int i = 0; i < data->nodes_count; i++)
4692 {
4693 output->framePoses[frame][i].translation = Vector3Zero();
4694 output->framePoses[frame][i].rotation = QuaternionIdentity();
4695 output->framePoses[frame][i].rotation = QuaternionNormalize(output->framePoses[frame][i].rotation);
4696 output->framePoses[frame][i].scale = Vector3One();
4697 }
4698 }
4699
4700 // for each single transformation type on single bone
4701 for (unsigned int channelId = 0; channelId < animation->channels_count; channelId++)
4702 {
4703 cgltf_animation_channel* channel = animation->channels + channelId;
4704 cgltf_animation_sampler* sampler = channel->sampler;
4705
4706 int boneId = (int)(channel->target_node - data->nodes);
4707
4708 for (int frame = 0; frame < output->frameCount; frame++)
4709 {
4710 bool shouldSkipFurtherTransformation = true;
4711 int outputMin = 0;
4712 int outputMax = 0;
4713 float frameTime = frame*timeStep;
4714 float lerpPercent = 0.0f;
4715
4716 // For this transformation:
4717 // getting between which input values the current frame time position
4718 // and also what is the percent to use in the linear interpolation later
4719 for (unsigned int j = 0; j < sampler->input->count; j++)
4720 {
4721 float inputFrameTime;
4722 if (GLTFReadValue(sampler->input, j, &inputFrameTime, 1, sizeof(float)))
4723 {
4724 if (frameTime < inputFrameTime)
4725 {
4726 shouldSkipFurtherTransformation = false;
4727 outputMin = (j == 0) ? 0 : j - 1;
4728 outputMax = j;
4729
4730 float previousInputTime = 0.0f;
4731 if (GLTFReadValue(sampler->input, outputMin, &previousInputTime, 1, sizeof(float)))
4732 {
4733 if ((inputFrameTime - previousInputTime) != 0)
4734 {
4735 lerpPercent = (frameTime - previousInputTime)/(inputFrameTime - previousInputTime);
4736 }
4737 }
4738
4739 break;
4740 }
4741 }
4742 else break;
4743 }
4744
4745 // If the current transformation has no information for the current frame time point
4746 if (shouldSkipFurtherTransformation) continue;
4747
4748 if (channel->target_path == cgltf_animation_path_type_translation)
4749 {
4750 Vector3 translationStart;
4751 Vector3 translationEnd;
4752
4753 bool success = GLTFReadValue(sampler->output, outputMin, &translationStart, 3, sizeof(float));
4754 success = GLTFReadValue(sampler->output, outputMax, &translationEnd, 3, sizeof(float)) || success;
4755
4756 if (success) output->framePoses[frame][boneId].translation = Vector3Lerp(translationStart, translationEnd, lerpPercent);
4757 }
4758 if (channel->target_path == cgltf_animation_path_type_rotation)
4759 {
4760 Quaternion rotationStart;
4761 Quaternion rotationEnd;
4762
4763 bool success = GLTFReadValue(sampler->output, outputMin, &rotationStart, 4, sizeof(float));
4764 success = GLTFReadValue(sampler->output, outputMax, &rotationEnd, 4, sizeof(float)) || success;
4765
4766 if (success)
4767 {
4768 output->framePoses[frame][boneId].rotation = QuaternionLerp(rotationStart, rotationEnd, lerpPercent);
4769 output->framePoses[frame][boneId].rotation = QuaternionNormalize(output->framePoses[frame][boneId].rotation);
4770 }
4771 }
4772 if (channel->target_path == cgltf_animation_path_type_scale)
4773 {
4774 Vector3 scaleStart;
4775 Vector3 scaleEnd;
4776
4777 bool success = GLTFReadValue(sampler->output, outputMin, &scaleStart, 3, sizeof(float));
4778 success = GLTFReadValue(sampler->output, outputMax, &scaleEnd, 3, sizeof(float)) || success;
4779
4780 if (success) output->framePoses[frame][boneId].scale = Vector3Lerp(scaleStart, scaleEnd, lerpPercent);
4781 }
4782 }
4783 }
4784
4785 // Build frameposes
4786 for (int frame = 0; frame < output->frameCount; frame++)
4787 {
4788 bool *completedBones = RL_CALLOC(output->boneCount, sizeof(bool));
4789 int numberCompletedBones = 0;
4790
4791 while (numberCompletedBones < output->boneCount)
4792 {
4793 for (int i = 0; i < output->boneCount; i++)
4794 {
4795 if (completedBones[i]) continue;
4796
4797 if (output->bones[i].parent < 0)
4798 {
4799 completedBones[i] = true;
4800 numberCompletedBones++;
4801 continue;
4802 }
4803
4804 if (!completedBones[output->bones[i].parent]) continue;
4805
4806 output->framePoses[frame][i].rotation = QuaternionMultiply(output->framePoses[frame][output->bones[i].parent].rotation, output->framePoses[frame][i].rotation);
4807 output->framePoses[frame][i].translation = Vector3RotateByQuaternion(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].rotation);
4808 output->framePoses[frame][i].translation = Vector3Add(output->framePoses[frame][i].translation, output->framePoses[frame][output->bones[i].parent].translation);
4809 output->framePoses[frame][i].scale = Vector3Multiply(output->framePoses[frame][i].scale, output->framePoses[frame][output->bones[i].parent].scale);
4810 completedBones[i] = true;
4811 numberCompletedBones++;
4812 }
4813 }
4814
4815 RL_FREE(completedBones);
4816 }
4817
4818 }
4819
4820 cgltf_free(data);
4821 }
4822 else TRACELOG(LOG_WARNING, ": [%s] Failed to load glTF data", fileName);
4823
4824 RL_FREE(fileData);
4825
4826 return animations;
4827 }
4828
4829 #endif
4830