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