models_decals.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /*******************************************************************************************
  2. *
  3. * raylib [models] example - decals
  4. *
  5. * Example complexity rating: [★★★★] 4/4
  6. *
  7. * Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
  8. *
  9. * Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5)
  10. * Based on previous work by @mrdoob
  11. *
  12. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  13. * BSD-like license that allows static linking with closed source software
  14. *
  15. * Copyright (c) 2025 JP Mortiboys (@themushroompirates) and Ramon Santamaria (@raysan5)
  16. *
  17. ********************************************************************************************/
  18. #include "raylib.h"
  19. #include "raymath.h"
  20. #include <string.h> // Required for: memcpy()
  21. #undef FLT_MAX
  22. #define FLT_MAX 340282346638528859811704183484516925440.0f // Maximum value of a float, from bit pattern 01111111011111111111111111111111
  23. #define MAX_DECALS 256
  24. //----------------------------------------------------------------------------------
  25. // Types and Structures Definition
  26. //----------------------------------------------------------------------------------
  27. typedef struct MeshBuilder {
  28. int vertexCount;
  29. int vertexCapacity;
  30. Vector3 *vertices;
  31. Vector2 *uvs;
  32. } MeshBuilder;
  33. //------------------------------------------------------------------------------------
  34. // Module Functions Declaration
  35. //------------------------------------------------------------------------------------
  36. static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 vertices[3]);
  37. static void FreeMeshBuilder(MeshBuilder *mb);
  38. static Mesh BuildMesh(MeshBuilder *mb);
  39. static Mesh GenMeshDecal(Model inputModel, Matrix projection, float decalSize, float decalOffset);
  40. static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s);
  41. #define FreeDecalMeshData() GenMeshDecal((Model){ .meshCount = -1.0f }, (Matrix){ 0 }, 0.0f, 0.0f)
  42. static bool GuiButton(Rectangle rec, const char *label);
  43. //------------------------------------------------------------------------------------
  44. // Program main entry point
  45. //------------------------------------------------------------------------------------
  46. int main(void)
  47. {
  48. // Initialization
  49. //--------------------------------------------------------------------------------------
  50. const int screenWidth = 800;
  51. const int screenHeight = 450;
  52. SetConfigFlags(FLAG_MSAA_4X_HINT);
  53. InitWindow(screenWidth, screenHeight, "raylib [models] example - decals");
  54. // Define the camera to look into our 3d world
  55. Camera camera = { 0 };
  56. camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
  57. camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point
  58. camera.up = (Vector3){ 0.0f, 1.6f, 0.0f }; // Camera up vector (rotation towards target)
  59. camera.fovy = 45.0f; // Camera field-of-view Y
  60. camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
  61. // Load character model
  62. Model model = LoadModel("resources/models/obj/character.obj");
  63. // Apply character skin
  64. Texture2D modelTexture = LoadTexture("resources/models/obj/character_diffuse.png");
  65. SetTextureFilter(modelTexture, TEXTURE_FILTER_BILINEAR);
  66. model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = modelTexture;
  67. BoundingBox modelBBox = GetMeshBoundingBox(model.meshes[0]); // Get mesh bounding box
  68. camera.target = Vector3Lerp(modelBBox.min, modelBBox.max, 0.5f);
  69. camera.position = Vector3Scale(modelBBox.max, 1.0f);
  70. camera.position.x *= 0.1f;
  71. float modelSize = fminf(
  72. fminf(fabsf(modelBBox.max.x - modelBBox.min.x), fabsf(modelBBox.max.y - modelBBox.min.y)),
  73. fabsf(modelBBox.max.z - modelBBox.min.z));
  74. camera.position = (Vector3){ 0.0f, modelBBox.max.y*1.2f, modelSize*3.0f };
  75. float decalSize = modelSize*0.25f;
  76. float decalOffset = 0.01f;
  77. Model placementCube = LoadModelFromMesh(GenMeshCube(decalSize, decalSize, decalSize));
  78. placementCube.materials[0].maps[0].color = LIME;
  79. Material decalMaterial = LoadMaterialDefault();
  80. decalMaterial.maps[0].color = YELLOW;
  81. Image decalImage = LoadImage("resources/raylib_logo.png");
  82. ImageResizeNN(&decalImage, decalImage.width/4, decalImage.height/4);
  83. Texture decalTexture = LoadTextureFromImage(decalImage);
  84. UnloadImage(decalImage);
  85. SetTextureFilter(decalTexture, TEXTURE_FILTER_BILINEAR);
  86. decalMaterial.maps[MATERIAL_MAP_DIFFUSE].texture = decalTexture;
  87. decalMaterial.maps[MATERIAL_MAP_DIFFUSE].color = RAYWHITE;
  88. bool showModel = true;
  89. Model decalModels[MAX_DECALS] = { 0 };
  90. int decalCount = 0;
  91. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  92. //--------------------------------------------------------------------------------------
  93. // Main game loop
  94. while (!WindowShouldClose()) // Detect window close button or ESC key
  95. {
  96. // Update
  97. //----------------------------------------------------------------------------------
  98. if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) UpdateCamera(&camera, CAMERA_THIRD_PERSON);
  99. // Display information about closest hit
  100. RayCollision collision = { 0 };
  101. collision.distance = FLT_MAX;
  102. collision.hit = false;
  103. // Get mouse ray
  104. Ray ray = GetScreenToWorldRay(GetMousePosition(), camera);
  105. // Check ray collision against bounding box first, before trying the full ray-mesh test
  106. RayCollision boxHitInfo = GetRayCollisionBox(ray, modelBBox);
  107. if ((boxHitInfo.hit) && (decalCount < MAX_DECALS))
  108. {
  109. // Check ray collision against model meshes
  110. RayCollision meshHitInfo = { 0 };
  111. for (int m = 0; m < model.meshCount; m++)
  112. {
  113. // NOTE: We consider the model.transform for the collision check but
  114. // it can be checked against any transform Matrix, used when checking against same
  115. // model drawn multiple times with multiple transforms
  116. meshHitInfo = GetRayCollisionMesh(ray, model.meshes[m], model.transform);
  117. if (meshHitInfo.hit)
  118. {
  119. // Save the closest hit mesh
  120. if (!collision.hit || (collision.distance > meshHitInfo.distance)) collision = meshHitInfo;
  121. }
  122. }
  123. if (meshHitInfo.hit) collision = meshHitInfo;
  124. }
  125. // Add decal to mesh on hit point
  126. if (collision.hit && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (decalCount < MAX_DECALS))
  127. {
  128. // Create the transformation to project the decal
  129. Vector3 origin = Vector3Add(collision.point, Vector3Scale(collision.normal, 1.0f));
  130. Matrix splat = MatrixLookAt(collision.point, origin, (Vector3){ 0.0f, 1.0f, 0.0f });
  131. // Spin the placement around a bit
  132. splat = MatrixMultiply(splat, MatrixRotateZ(DEG2RAD*((float)GetRandomValue(-180, 180))));
  133. Mesh decalMesh = GenMeshDecal(model, splat, decalSize, decalOffset);
  134. if (decalMesh.vertexCount > 0)
  135. {
  136. int decalIndex = decalCount++;
  137. decalModels[decalIndex] = LoadModelFromMesh(decalMesh);
  138. decalModels[decalIndex].materials[0].maps[0] = decalMaterial.maps[0];
  139. }
  140. }
  141. //----------------------------------------------------------------------------------
  142. // Draw
  143. //----------------------------------------------------------------------------------
  144. BeginDrawing();
  145. ClearBackground(RAYWHITE);
  146. BeginMode3D(camera);
  147. // Draw the model at the origin and default scale
  148. if (showModel) DrawModel(model, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE);
  149. // Draw the decal models
  150. for (int i = 0; i < decalCount; i++) DrawModel(decalModels[i], (Vector3){0}, 1.0f, WHITE);
  151. // If we hit the mesh, draw the box for the decal
  152. if (collision.hit)
  153. {
  154. Vector3 origin = Vector3Add(collision.point, Vector3Scale(collision.normal, 1.0f));
  155. Matrix splat = MatrixLookAt(collision.point, origin, (Vector3){0,1,0});
  156. placementCube.transform = MatrixInvert(splat);
  157. DrawModel(placementCube, (Vector3){0}, 1.0f, Fade(WHITE, 0.5f));
  158. }
  159. DrawGrid(10, 10.0f);
  160. EndMode3D();
  161. float yPos = 10;
  162. float x0 = GetScreenWidth() - 300;
  163. float x1 = x0 + 100;
  164. float x2 = x1 + 100;
  165. DrawText("Vertices", x1, yPos, 10, LIME);
  166. DrawText("Triangles", x2, yPos, 10, LIME);
  167. yPos += 15;
  168. int vertexCount = 0;
  169. int triangleCount = 0;
  170. for (int i = 0; i < model.meshCount; i++)
  171. {
  172. vertexCount += model.meshes[i].vertexCount;
  173. triangleCount += model.meshes[i].triangleCount;
  174. }
  175. DrawText("Main model", x0, yPos, 10, LIME);
  176. DrawText(TextFormat("%d", vertexCount), x1, yPos, 10, LIME);
  177. DrawText(TextFormat("%d", triangleCount), x2, yPos, 10, LIME);
  178. yPos += 15;
  179. for (int i = 0; i < decalCount; i++)
  180. {
  181. if (i == 20)
  182. {
  183. DrawText("...", x0, yPos, 10, LIME);
  184. yPos += 15;
  185. }
  186. if (i < 20)
  187. {
  188. DrawText(TextFormat("Decal #%d", i+1), x0, yPos, 10, LIME);
  189. DrawText(TextFormat("%d", decalModels[i].meshes[0].vertexCount), x1, yPos, 10, LIME);
  190. DrawText(TextFormat("%d", decalModels[i].meshes[0].triangleCount), x2, yPos, 10, LIME);
  191. yPos += 15;
  192. }
  193. vertexCount += decalModels[i].meshes[0].vertexCount;
  194. triangleCount += decalModels[i].meshes[0].triangleCount;
  195. }
  196. DrawText("TOTAL", x0, yPos, 10, LIME);
  197. DrawText(TextFormat("%d", vertexCount), x1, yPos, 10, LIME);
  198. DrawText(TextFormat("%d", triangleCount), x2, yPos, 10, LIME);
  199. yPos += 15;
  200. DrawText("Hold RMB to move camera", 10, 430, 10, GRAY);
  201. DrawText("(c) Character model and texture from kenney.nl", screenWidth - 260, screenHeight - 20, 10, GRAY);
  202. // UI elements
  203. if (GuiButton((Rectangle){ 10, screenHeight - 100, 100, 60 }, showModel ? "Hide Model" : "Show Model")) showModel = !showModel;
  204. if (GuiButton((Rectangle){ 10 + 110, screenHeight - 100, 100, 60 }, "Clear Decals"))
  205. {
  206. // Clear decals, unload all decal models
  207. for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]);
  208. decalCount = 0;
  209. }
  210. DrawFPS(10, 10);
  211. EndDrawing();
  212. //----------------------------------------------------------------------------------
  213. }
  214. // De-Initialization
  215. //--------------------------------------------------------------------------------------
  216. UnloadModel(model);
  217. UnloadTexture(modelTexture);
  218. // Unload decal models
  219. for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]);
  220. UnloadTexture(decalTexture);
  221. FreeDecalMeshData(); // Free the data for decal generation
  222. CloseWindow(); // Close window and OpenGL context
  223. //--------------------------------------------------------------------------------------
  224. return 0;
  225. }
  226. //----------------------------------------------------------------------------------
  227. // Module Functions Definition
  228. //----------------------------------------------------------------------------------
  229. // Add triangles to mesh builder (dynamic array manager)
  230. static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 vertices[3])
  231. {
  232. // Reallocate and copy if we need to
  233. if (mb->vertexCapacity <= (mb->vertexCount + 3))
  234. {
  235. int newVertexCapacity = (1 + (mb->vertexCapacity/256))*256;
  236. Vector3 *newVertices = (Vector3 *)MemAlloc(newVertexCapacity*sizeof(Vector3));
  237. if (mb->vertexCapacity > 0)
  238. {
  239. memcpy(newVertices, mb->vertices, mb->vertexCount*sizeof(Vector3));
  240. MemFree(mb->vertices);
  241. }
  242. mb->vertices = newVertices;
  243. mb->vertexCapacity = newVertexCapacity;
  244. }
  245. // Add 3 vertices
  246. int index = mb->vertexCount;
  247. mb->vertexCount += 3;
  248. for (int i = 0; i < 3; i++) mb->vertices[index+i] = vertices[i];
  249. }
  250. // Free mesh builder
  251. static void FreeMeshBuilder(MeshBuilder *mb)
  252. {
  253. MemFree(mb->vertices);
  254. if (mb->uvs) MemFree(mb->uvs);
  255. *mb = (MeshBuilder){ 0 };
  256. }
  257. // Build a Mesh from MeshBuilder data
  258. static Mesh BuildMesh(MeshBuilder *mb)
  259. {
  260. Mesh outMesh = { 0 };
  261. outMesh.vertexCount = mb->vertexCount;
  262. outMesh.triangleCount = mb->vertexCount/3;
  263. outMesh.vertices = MemAlloc(outMesh.vertexCount*3*sizeof(float));
  264. if (mb->uvs) outMesh.texcoords = MemAlloc(outMesh.vertexCount*2*sizeof(float));
  265. for (int i = 0; i < mb->vertexCount; i++)
  266. {
  267. outMesh.vertices[3*i+0] = mb->vertices[i].x;
  268. outMesh.vertices[3*i+1] = mb->vertices[i].y;
  269. outMesh.vertices[3*i+2] = mb->vertices[i].z;
  270. if (mb->uvs)
  271. {
  272. outMesh.texcoords[2*i+0] = mb->uvs[i].x;
  273. outMesh.texcoords[2*i+1] = mb->uvs[i].y;
  274. }
  275. }
  276. UploadMesh(&outMesh, false);
  277. return outMesh;
  278. }
  279. // Clip segment
  280. static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s)
  281. {
  282. float d0 = Vector3DotProduct(v0, p) - s;
  283. float d1 = Vector3DotProduct(v1, p) - s;
  284. float s0 = d0/(d0 - d1);
  285. Vector3 position = Vector3Lerp(v0, v1, s0);
  286. return position;
  287. }
  288. // Generate mesh decals for provided model
  289. static Mesh GenMeshDecal(Model target, Matrix projection, float decalSize, float decalOffset)
  290. {
  291. // We're going to use these to build up our decal meshes
  292. // They'll resize automatically as we go, we'll free them at the end
  293. static MeshBuilder meshBuilders[2] = { 0 };
  294. // Ugly way of telling us to free the static MeshBuilder data
  295. if (target.meshCount == -1)
  296. {
  297. FreeMeshBuilder(&meshBuilders[0]);
  298. FreeMeshBuilder(&meshBuilders[1]);
  299. return (Mesh){ 0 };
  300. }
  301. // We're going to need the inverse matrix
  302. Matrix invProj = MatrixInvert(projection);
  303. // Reset the mesh builders
  304. meshBuilders[0].vertexCount = 0;
  305. meshBuilders[1].vertexCount = 0;
  306. // We'll be flip-flopping between the two mesh builders
  307. // Reading from one and writing to the other, then swapping
  308. int mbIndex = 0;
  309. // First pass, just get any triangle inside the bounding box (for each mesh of the model)
  310. for (int meshIndex = 0; meshIndex < target.meshCount; meshIndex++)
  311. {
  312. Mesh mesh = target.meshes[meshIndex];
  313. for (int tri = 0; tri < mesh.triangleCount; tri++)
  314. {
  315. Vector3 vertices[3] = { 0 };
  316. // The way we calculate the vertices of the mesh triangle
  317. // depend on whether the mesh vertices are indexed or not
  318. if (mesh.indices == 0)
  319. {
  320. for (int v = 0; v < 3; v++)
  321. {
  322. vertices[v] = (Vector3){
  323. mesh.vertices[3*3*tri + 3*v + 0],
  324. mesh.vertices[3*3*tri + 3*v + 1],
  325. mesh.vertices[3*3*tri + 3*v + 2]
  326. };
  327. }
  328. }
  329. else
  330. {
  331. for (int v = 0; v < 3; v++)
  332. {
  333. vertices[v] = (Vector3){
  334. mesh.vertices[ 3*mesh.indices[3*tri+0] + v],
  335. mesh.vertices[ 3*mesh.indices[3*tri+1] + v],
  336. mesh.vertices[ 3*mesh.indices[3*tri+2] + v]
  337. };
  338. }
  339. }
  340. // Transform all 3 vertices of the triangle
  341. // and check if they are inside our decal box
  342. int insideCount = 0;
  343. for (int i = 0; i < 3; i++)
  344. {
  345. // To projection space
  346. Vector3 v = Vector3Transform(vertices[i], projection);
  347. if ((fabsf(v.x) < decalSize) || (fabsf(v.y) <= decalSize) || (fabsf(v.z) <= decalSize)) insideCount++;
  348. // We need to keep the transformed vertex
  349. vertices[i] = v;
  350. }
  351. // If any of them are inside, we add the triangle - we'll clip it later
  352. if (insideCount > 0) AddTriangleToMeshBuilder(&meshBuilders[mbIndex], vertices);
  353. }
  354. }
  355. // Clipping time! We need to clip against all 6 directions
  356. Vector3 planes[6] = {
  357. { 1, 0, 0 },
  358. { -1, 0, 0 },
  359. { 0, 1, 0 },
  360. { 0, -1, 0 },
  361. { 0, 0, 1 },
  362. { 0, 0, -1 }
  363. };
  364. for (int face = 0; face < 6; face++)
  365. {
  366. // Swap current model builder (so we read from the one we just wrote to)
  367. mbIndex = 1 - mbIndex;
  368. MeshBuilder *inMesh = &meshBuilders[1 - mbIndex];
  369. MeshBuilder *outMesh = &meshBuilders[mbIndex];
  370. // Reset write builder
  371. outMesh->vertexCount = 0;
  372. float s = 0.5f*decalSize;
  373. for (int i = 0; i < inMesh->vertexCount; i += 3)
  374. {
  375. Vector3 nV1, nV2, nV3, nV4;
  376. float d1 = Vector3DotProduct(inMesh->vertices[ i + 0 ], planes[face] ) - s;
  377. float d2 = Vector3DotProduct(inMesh->vertices[ i + 1 ], planes[face] ) - s;
  378. float d3 = Vector3DotProduct(inMesh->vertices[ i + 2 ], planes[face] ) - s;
  379. int v1Out = (d1 > 0);
  380. int v2Out = (d2 > 0);
  381. int v3Out = (d3 > 0);
  382. // Calculate, how many vertices of the face lie outside of the clipping plane
  383. int total = v1Out + v2Out + v3Out;
  384. switch (total)
  385. {
  386. case 0:
  387. {
  388. // The entire face lies inside of the plane, no clipping needed
  389. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){inMesh->vertices[i], inMesh->vertices[i+1], inMesh->vertices[i+2]});
  390. } break;
  391. case 1:
  392. {
  393. // One vertex lies outside of the plane, perform clipping
  394. if (v1Out)
  395. {
  396. nV1 = inMesh->vertices[i + 1];
  397. nV2 = inMesh->vertices[i + 2];
  398. nV3 = ClipSegment(inMesh->vertices[i], nV1, planes[face], s);
  399. nV4 = ClipSegment(inMesh->vertices[i], nV2, planes[face], s);
  400. }
  401. if (v2Out)
  402. {
  403. nV1 = inMesh->vertices[i];
  404. nV2 = inMesh->vertices[i + 2];
  405. nV3 = ClipSegment(inMesh->vertices[i + 1], nV1, planes[face], s);
  406. nV4 = ClipSegment(inMesh->vertices[i + 1], nV2, planes[face], s);
  407. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV3, nV2, nV1});
  408. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV2, nV3, nV4});
  409. break;
  410. }
  411. if (v3Out)
  412. {
  413. nV1 = inMesh->vertices[i];
  414. nV2 = inMesh->vertices[i + 1];
  415. nV3 = ClipSegment(inMesh->vertices[i + 2], nV1, planes[face], s);
  416. nV4 = ClipSegment(inMesh->vertices[i + 2], nV2, planes[face], s);
  417. }
  418. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
  419. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV4, nV3, nV2});
  420. } break;
  421. case 2:
  422. {
  423. // Two vertices lies outside of the plane, perform clipping
  424. if (!v1Out)
  425. {
  426. nV1 = inMesh->vertices[i];
  427. nV2 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
  428. nV3 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
  429. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
  430. }
  431. if (!v2Out)
  432. {
  433. nV1 = inMesh->vertices[i + 1];
  434. nV2 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
  435. nV3 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
  436. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
  437. }
  438. if (!v3Out)
  439. {
  440. nV1 = inMesh->vertices[i + 2];
  441. nV2 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
  442. nV3 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
  443. AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
  444. }
  445. } break;
  446. case 3: // The entire face lies outside of the plane, so let's discard the corresponding vertices
  447. default: break;
  448. }
  449. }
  450. }
  451. // Now we just need to re-transform the vertices
  452. MeshBuilder *theMesh = &meshBuilders[mbIndex];
  453. // Allocate room for UVs
  454. if (theMesh->vertexCount > 0)
  455. {
  456. theMesh->uvs = (Vector2 *)MemAlloc(sizeof(Vector2)*theMesh->vertexCount);
  457. for (int i = 0; i < theMesh->vertexCount; i++)
  458. {
  459. // Calculate the UVs based on the projected coords
  460. // They are clipped to (-decalSize .. decalSize) and we want them (0..1)
  461. theMesh->uvs[i].x = (theMesh->vertices[i].x/decalSize + 0.5f);
  462. theMesh->uvs[i].y = (theMesh->vertices[i].y/decalSize + 0.5f);
  463. // Tiny nudge in the normal direction so it renders properly over the mesh
  464. theMesh->vertices[i].z -= decalOffset;
  465. // From projection space to world space
  466. theMesh->vertices[i] = Vector3Transform(theMesh->vertices[i], invProj);
  467. }
  468. // Decal model data ready, create the mesh and return it
  469. return BuildMesh(theMesh);
  470. }
  471. else
  472. {
  473. // Return a blank mesh as there's nothing to add
  474. return (Mesh){ 0 };
  475. }
  476. }
  477. // Button UI element
  478. static bool GuiButton(Rectangle rec, const char *label)
  479. {
  480. Color bgColor = GRAY;
  481. bool pressed = false;
  482. if (CheckCollisionPointRec(GetMousePosition(), rec))
  483. {
  484. bgColor = LIGHTGRAY;
  485. if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) pressed = true;
  486. }
  487. DrawRectangleRec(rec, bgColor);
  488. DrawRectangleLinesEx(rec, 2.0f, DARKGRAY);
  489. float fontSize = 10.0f;
  490. float textWidth = MeasureText(label, fontSize);
  491. DrawText(label, (int)(rec.x + rec.width*0.5f - textWidth*0.5f), (int)(rec.y + rec.height*0.5f - fontSize*0.5f), fontSize, DARKGRAY);
  492. return pressed;
  493. }