| 
									
										
										
										
											2025-06-22 21:05:09 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  | MIT License | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Copyright (c) 2024 - 2025 tobid7 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  | of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  | in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  | copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  | furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The above copyright notice and this permission notice shall be included in all | 
					
						
							|  |  |  | copies or substantial portions of the Software. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
					
						
							|  |  |  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
					
						
							|  |  |  | SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <pd/lithium/drawlist.hpp>
 | 
					
						
							|  |  |  | #include <pd/lithium/renderer.hpp>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef M_PI
 | 
					
						
							|  |  |  | #define M_PI 3.14159265358979323846
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace PD { | 
					
						
							|  |  |  | namespace Li { | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawSolid() { CurrentTex = Gfx::GetSolidTex(); } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-14 20:37:55 +02:00
										 |  |  | PD_LITHIUM_API void DrawList::Clear() { | 
					
						
							|  |  |  |   pNumIndices = 0; | 
					
						
							|  |  |  |   pNumVertices = 0; | 
					
						
							|  |  |  |   pDrawList.clear(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::AddCommand(Command::Ref v) { | 
					
						
							|  |  |  |   pNumIndices += v->IndexBuffer.Size(); | 
					
						
							|  |  |  |   pNumVertices += v->VertexBuffer.Size(); | 
					
						
							|  |  |  |   pDrawList.push_back(std::move(v)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 21:43:35 +02:00
										 |  |  | PD_LITHIUM_API void DrawList::Merge(DrawList::Ref list) { | 
					
						
							|  |  |  |   for (size_t i = 0; i < list->pDrawList.size(); i++) { | 
					
						
							| 
									
										
										
										
											2025-08-14 20:37:55 +02:00
										 |  |  |     pNumIndices += list->pDrawList[i]->IndexBuffer.Size(); | 
					
						
							|  |  |  |     pNumVertices += list->pDrawList[i]->VertexBuffer.Size(); | 
					
						
							| 
									
										
										
										
											2025-07-01 21:43:35 +02:00
										 |  |  |     pDrawList.push_back(std::move(list->pDrawList[i])); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /** Make sure The list gets cleared */ | 
					
						
							|  |  |  |   list->Clear(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-22 21:05:09 +02:00
										 |  |  | PD_LITHIUM_API Command::Ref DrawList::PreGenerateCmd() { | 
					
						
							|  |  |  |   Command::Ref cmd = Command::New(); | 
					
						
							|  |  |  |   cmd->Layer = Layer; | 
					
						
							|  |  |  |   cmd->Index = pDrawList.size(); | 
					
						
							|  |  |  |   cmd->Tex = CurrentTex; | 
					
						
							|  |  |  |   pClipCmd(cmd.get()); | 
					
						
							| 
									
										
										
										
											2025-07-01 21:43:35 +02:00
										 |  |  |   return cmd; | 
					
						
							| 
									
										
										
										
											2025-06-22 21:05:09 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::pClipCmd(Command *cmd) { | 
					
						
							|  |  |  |   if (!pClipRects.IsEmpty()) { | 
					
						
							|  |  |  |     cmd->ScissorOn = true; | 
					
						
							|  |  |  |     cmd->ScissorRect = ivec4(pClipRects.Top()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::PathArcToN(const fvec2 &c, float radius, | 
					
						
							|  |  |  |                                          float a_min, float a_max, | 
					
						
							|  |  |  |                                          int segments) { | 
					
						
							|  |  |  |   // Path.push_back(c);
 | 
					
						
							|  |  |  |   PathReserve(segments + 1); | 
					
						
							|  |  |  |   for (int i = 0; i < segments; i++) { | 
					
						
							|  |  |  |     float a = a_min + ((float)i / (float)segments) * (a_max - a_min); | 
					
						
							|  |  |  |     PathAdd(vec2(c.x + std::cos(a) * radius, c.y + std::sin(a) * radius)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::PathFastArcToN(const fvec2 &c, float r, | 
					
						
							|  |  |  |                                              float amin, float amax, int s) { | 
					
						
							|  |  |  |   /**
 | 
					
						
							|  |  |  |    * Funcion with less division overhead | 
					
						
							|  |  |  |    * Usefull for stuff where a lot of calculations are required | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   float d = (amax - amin) / s; | 
					
						
							|  |  |  |   PathReserve(s + 1); | 
					
						
							|  |  |  |   for (int i = 0; i <= s; i++) { | 
					
						
							|  |  |  |     float a = amin + i * d; | 
					
						
							|  |  |  |     PathAdd(fvec2(c.x + std::cos(a) * r, c.y + std::sin(a) * r)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::PathRect(fvec2 a, fvec2 b, float rounding) { | 
					
						
							|  |  |  |   if (rounding == 0.f) { | 
					
						
							|  |  |  |     PathAdd(a); | 
					
						
							|  |  |  |     PathAdd(vec2(b.x, a.y)); | 
					
						
							|  |  |  |     PathAdd(b); | 
					
						
							|  |  |  |     PathAdd(vec2(a.x, b.y)); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     float r = std::min({rounding, (b.x - a.x) * 0.5f, (b.y - a.y) * 0.5f}); | 
					
						
							|  |  |  |     /** Calculate Optimal segment count automatically */ | 
					
						
							|  |  |  |     float corner = M_PI * 0.5f; | 
					
						
							|  |  |  |     int segments = std::max(3, int(std::ceil(corner / (6.0f * M_PI / 180.0f)))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * To Correctly render filled shapes with Paths API | 
					
						
							|  |  |  |      * The Commands need to be setup clockwise | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     /** Top Left */ | 
					
						
							|  |  |  |     PathAdd(vec2(a.x + r, a.y)); | 
					
						
							|  |  |  |     PathFastArcToN(vec2(b.x - r, a.y + r), r, -M_PI / 2.0f, 0.0f, segments); | 
					
						
							|  |  |  |     /** Top Right */ | 
					
						
							|  |  |  |     PathAdd(vec2(b.x, b.y - r)); | 
					
						
							|  |  |  |     PathFastArcToN(vec2(b.x - r, b.y - r), r, 0.0f, M_PI / 2.0f, segments); | 
					
						
							|  |  |  |     /** Bottom Right */ | 
					
						
							|  |  |  |     PathAdd(vec2(a.x + r, b.y)); | 
					
						
							|  |  |  |     PathFastArcToN(vec2(a.x + r, b.y - r), r, M_PI / 2.0f, M_PI, segments); | 
					
						
							|  |  |  |     /** Bottom Left */ | 
					
						
							|  |  |  |     PathAdd(vec2(a.x, a.y + r)); | 
					
						
							|  |  |  |     PathFastArcToN(vec2(a.x + r, a.y + r), r, M_PI, 3.0f * M_PI / 2.0f, | 
					
						
							|  |  |  |                    segments); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::PathRectEx(fvec2 a, fvec2 b, float rounding, | 
					
						
							|  |  |  |                                          u32 flags) { | 
					
						
							|  |  |  |   if (rounding == 0.f) { | 
					
						
							|  |  |  |     PathAdd(a); | 
					
						
							|  |  |  |     PathAdd(vec2(b.x, a.y)); | 
					
						
							|  |  |  |     PathAdd(b); | 
					
						
							|  |  |  |     PathAdd(vec2(a.x, b.y)); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     float r = std::min({rounding, (b.x - a.x) * 0.5f, (b.y - a.y) * 0.5f}); | 
					
						
							|  |  |  |     /** Calculate Optimal segment count automatically */ | 
					
						
							|  |  |  |     float corner = M_PI * 0.5f; | 
					
						
							|  |  |  |     int segments = std::max(3, int(std::ceil(corner / (6.0f * M_PI / 180.0f)))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /**
 | 
					
						
							|  |  |  |      * To Correctly render filled shapes with Paths API | 
					
						
							|  |  |  |      * The Commands need to be setup clockwise | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     /** Top Left */ | 
					
						
							|  |  |  |     if (flags & LiPathRectFlags_KeepTopLeft) { | 
					
						
							|  |  |  |       PathAdd(a); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       PathAdd(vec2(a.x + r, a.y)); | 
					
						
							|  |  |  |       PathFastArcToN(vec2(b.x - r, a.y + r), r, -M_PI / 2.0f, 0.0f, segments); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** Top Right */ | 
					
						
							|  |  |  |     if (flags & LiPathRectFlags_KeepTopRight) { | 
					
						
							|  |  |  |       PathAdd(vec2(b.x, a.y)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       PathAdd(vec2(b.x, b.y - r)); | 
					
						
							|  |  |  |       PathFastArcToN(vec2(b.x - r, b.y - r), r, 0.0f, M_PI / 2.0f, segments); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /** Bottom Right */ | 
					
						
							|  |  |  |     if (flags & LiPathRectFlags_KeepBotRight) { | 
					
						
							|  |  |  |       PathAdd(b); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       PathAdd(vec2(a.x + r, b.y)); | 
					
						
							|  |  |  |       PathFastArcToN(vec2(a.x + r, b.y - r), r, M_PI / 2.0f, M_PI, segments); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /** Bottom Left */ | 
					
						
							|  |  |  |     if (flags & LiPathRectFlags_KeepBotLeft) { | 
					
						
							|  |  |  |       PathAdd(vec2(a.x, b.y)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       PathAdd(vec2(a.x, a.y + r)); | 
					
						
							|  |  |  |       PathFastArcToN(vec2(a.x + r, a.y + r), r, M_PI, 3.0f * M_PI / 2.0f, | 
					
						
							|  |  |  |                      segments); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawRect(const fvec2 &pos, const fvec2 &size, | 
					
						
							|  |  |  |                                        u32 color, int thickness) { | 
					
						
							|  |  |  |   PathRect(pos, pos + size); | 
					
						
							|  |  |  |   // Flags is currently hardcoded (1 = close)
 | 
					
						
							|  |  |  |   PathStroke(color, thickness, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | void DrawList::DrawRectFilled(const fvec2 &pos, const fvec2 &size, u32 color) { | 
					
						
							|  |  |  |   PathRect(pos, pos + size); | 
					
						
							|  |  |  |   PathFill(color); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawTriangle(const fvec2 &a, const fvec2 &b, | 
					
						
							|  |  |  |                                            const fvec2 &c, u32 color, | 
					
						
							|  |  |  |                                            int thickness) { | 
					
						
							|  |  |  |   PathAdd(a); | 
					
						
							|  |  |  |   PathAdd(b); | 
					
						
							|  |  |  |   PathAdd(c); | 
					
						
							|  |  |  |   PathStroke(color, thickness, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawTriangleFilled(const fvec2 &a, const fvec2 &b, | 
					
						
							|  |  |  |                                                  const fvec2 &c, u32 color) { | 
					
						
							|  |  |  |   PathAdd(a); | 
					
						
							|  |  |  |   PathAdd(b); | 
					
						
							|  |  |  |   PathAdd(c); | 
					
						
							|  |  |  |   PathFill(color); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawCircle(const fvec2 ¢er, float rad, | 
					
						
							|  |  |  |                                          u32 color, int num_segments, | 
					
						
							|  |  |  |                                          int thickness) { | 
					
						
							|  |  |  |   if (num_segments <= 0) { | 
					
						
							|  |  |  |     // Auto Segment
 | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments; | 
					
						
							|  |  |  |     PathArcToN(center, rad, 0.f, am, num_segments); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-07-01 21:43:35 +02:00
										 |  |  |   DrawSolid();  // Only Solid Color Supported
 | 
					
						
							| 
									
										
										
										
											2025-06-22 21:05:09 +02:00
										 |  |  |   PathStroke(color, thickness, (1 << 0)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawCircleFilled(const fvec2 ¢er, float rad, | 
					
						
							|  |  |  |                                                u32 color, int num_segments) { | 
					
						
							|  |  |  |   if (num_segments <= 0) { | 
					
						
							|  |  |  |     // Auto Segment
 | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     float am = (M_PI * 2.0f) * ((float)num_segments) / (float)num_segments; | 
					
						
							|  |  |  |     PathArcToN(center, rad, 0.f, am, num_segments); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   PathFill(color); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODO: Don't render OOS
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawPolyLine(const Vec<fvec2> &points, u32 clr, | 
					
						
							|  |  |  |                                            u32 flags, int thickness) { | 
					
						
							|  |  |  |   if (points.Size() < 2) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   DrawSolid(); | 
					
						
							|  |  |  |   auto cmd = PreGenerateCmd(); | 
					
						
							|  |  |  |   bool close = (flags & (1 << 0)); | 
					
						
							|  |  |  |   int num_points = close ? (int)points.Size() : (int)points.Size() - 1; | 
					
						
							|  |  |  |   if (flags & (1 << 1)) { | 
					
						
							|  |  |  |     // TODO: Find a way to draw less garbage looking lines
 | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     // Non antialiased lines look awful when rendering with thickness != 1
 | 
					
						
							|  |  |  |     for (int i = 0; i < num_points; i++) { | 
					
						
							|  |  |  |       int j = (i + 1) == (int)points.Size() ? 0 : (i + 1); | 
					
						
							|  |  |  |       auto line = Renderer::PrimLine(points[i], points[j], thickness); | 
					
						
							|  |  |  |       Renderer::CmdQuad(cmd.get(), line, vec4(0.f, 1.f, 1.f, 0.f), clr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   AddCommand(std::move(cmd)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawConvexPolyFilled(const Vec<fvec2> &points, | 
					
						
							|  |  |  |                                                    u32 clr) { | 
					
						
							|  |  |  |   if (points.Size() < 3) { | 
					
						
							| 
									
										
										
										
											2025-07-01 21:43:35 +02:00
										 |  |  |     return;  // Need at least three points
 | 
					
						
							| 
									
										
										
										
											2025-06-22 21:05:09 +02:00
										 |  |  |   } | 
					
						
							|  |  |  |   auto cmd = PreGenerateCmd(); | 
					
						
							|  |  |  |   Renderer::CmdConvexPolyFilled(cmd.get(), points, clr, CurrentTex); | 
					
						
							|  |  |  |   AddCommand(std::move(cmd)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawText(const fvec2 &pos, | 
					
						
							|  |  |  |                                        const std::string &text, u32 color) { | 
					
						
							|  |  |  |   if (!pCurrentFont) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   std::vector<Command::Ref> cmds; | 
					
						
							|  |  |  |   pCurrentFont->CmdTextEx(cmds, pos, color, pFontScale, text); | 
					
						
							|  |  |  |   for (size_t i = 0; i < cmds.size(); i++) { | 
					
						
							|  |  |  |     cmds[i]->Index = pDrawList.size(); | 
					
						
							|  |  |  |     cmds[i]->Layer = Layer; | 
					
						
							|  |  |  |     AddCommand(std::move(cmds[i])); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawTextEx(const fvec2 &p, | 
					
						
							|  |  |  |                                          const std::string &text, u32 color, | 
					
						
							|  |  |  |                                          LiTextFlags flags, fvec2 box) { | 
					
						
							|  |  |  |   if (!pCurrentFont) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   std::vector<Command::Ref> cmds; | 
					
						
							|  |  |  |   pCurrentFont->CmdTextEx(cmds, p, color, pFontScale, text, flags, box); | 
					
						
							|  |  |  |   for (size_t i = 0; i < cmds.size(); i++) { | 
					
						
							|  |  |  |     cmds[i]->Index = pDrawList.size(); | 
					
						
							|  |  |  |     cmds[i]->Layer = Layer; | 
					
						
							|  |  |  |     AddCommand(std::move(cmds[i])); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PD_LITHIUM_API void DrawList::DrawLine(const fvec2 &a, const fvec2 &b, | 
					
						
							|  |  |  |                                        u32 color, int t) { | 
					
						
							|  |  |  |   PathAdd(a); | 
					
						
							|  |  |  |   PathAdd(b); | 
					
						
							|  |  |  |   PathStroke(color, t); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-01 21:43:35 +02:00
										 |  |  | }  // namespace Li
 | 
					
						
							|  |  |  | }  // namespace PD
 |