285 lines
8.8 KiB
C++
285 lines
8.8 KiB
C++
|
|
#include <pd/drivers/gfx.hpp>
|
||
|
|
#include <pd/lithium/drawlist.hpp>
|
||
|
|
#include <pd/lithium/math.hpp>
|
||
|
|
|
||
|
|
namespace PD {
|
||
|
|
namespace Li {
|
||
|
|
PD_API Drawlist::Drawlist() { Clear(); }
|
||
|
|
|
||
|
|
PD_API Drawlist::~Drawlist() { Clear(); }
|
||
|
|
|
||
|
|
PD_API void Drawlist::Merge(Drawlist& other) {}
|
||
|
|
|
||
|
|
PD_API void Drawlist::Copy(Drawlist& other) {}
|
||
|
|
|
||
|
|
PD_API void Drawlist::Optimize() {}
|
||
|
|
|
||
|
|
PD_API void Drawlist::Clear() {
|
||
|
|
UnbindTexture();
|
||
|
|
pPath.Reset();
|
||
|
|
pVertices.Reset();
|
||
|
|
pIndices.Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Command Allocation */
|
||
|
|
PD_API Command& Drawlist::NewCommand() {
|
||
|
|
auto cmd = pCommands.Allocate(1);
|
||
|
|
cmd->Tex = pCurrentTexture.GetID();
|
||
|
|
return *cmd;
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::BindTexture(const Texture& tex) { pCurrentTexture = tex; }
|
||
|
|
|
||
|
|
/** Path API */
|
||
|
|
PD_API void Drawlist::PathStroke(u32 color, int t, LiDrawFlags flags) {
|
||
|
|
DrawPolyLine(pPath, color, flags, t);
|
||
|
|
PathClear();
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::PathFill(u32 color) {
|
||
|
|
DrawConvexPolyFilled(pPath, color);
|
||
|
|
PathClear();
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::PathArcToN(const fvec2& c, float r, float amin,
|
||
|
|
float amax, int s) {
|
||
|
|
// Path.push_back(c);
|
||
|
|
PathReserve(s + 1);
|
||
|
|
for (int i = 0; i < s; i++) {
|
||
|
|
float a = amin + ((float)i / (float)s) * (amax - amin);
|
||
|
|
PathAdd(vec2(c.x + std::cos(a) * r, c.y + std::sin(a) * r));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_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_API void Drawlist::PathRect(const fvec2& tl, const fvec2& br, float r) {
|
||
|
|
if (r == 0.f) {
|
||
|
|
PathAdd(tl);
|
||
|
|
PathAdd(vec2(br.x, tl.y));
|
||
|
|
PathAdd(br);
|
||
|
|
PathAdd(vec2(tl.x, br.y));
|
||
|
|
} else {
|
||
|
|
float r = std::min({r, (br.x - tl.x) * 0.5f, (br.y - tl.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(tl.x + r, tl.y));
|
||
|
|
PathFastArcToN(vec2(br.x - r, tl.y + r), r, -M_PI / 2.0f, 0.0f, segments);
|
||
|
|
/** Top Right */
|
||
|
|
PathAdd(vec2(br.x, br.y - r));
|
||
|
|
PathFastArcToN(vec2(br.x - r, br.y - r), r, 0.0f, M_PI / 2.0f, segments);
|
||
|
|
/** Bottom Right */
|
||
|
|
PathAdd(vec2(tl.x + r, br.y));
|
||
|
|
PathFastArcToN(vec2(tl.x + r, br.y - r), r, M_PI / 2.0f, M_PI, segments);
|
||
|
|
/** Bottom Left */
|
||
|
|
PathAdd(vec2(tl.x, tl.y + r));
|
||
|
|
PathFastArcToN(vec2(tl.x + r, tl.y + r), r, M_PI, 3.0f * M_PI / 2.0f,
|
||
|
|
segments);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::PathRectEx(const fvec2& tl, const fvec2& br, float r,
|
||
|
|
LiPathRectFlags flags) {
|
||
|
|
if (r == 0.f) {
|
||
|
|
PathAdd(tl);
|
||
|
|
PathAdd(vec2(br.x, tl.y));
|
||
|
|
PathAdd(br);
|
||
|
|
PathAdd(vec2(tl.x, br.y));
|
||
|
|
} else {
|
||
|
|
float r = std::min({r, (br.x - tl.x) * 0.5f, (br.y - tl.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(tl);
|
||
|
|
} else {
|
||
|
|
PathAdd(vec2(tl.x + r, tl.y));
|
||
|
|
PathFastArcToN(vec2(br.x - r, tl.y + r), r, -M_PI / 2.0f, 0.0f, segments);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Top Right */
|
||
|
|
if (flags & LiPathRectFlags_KeepTopRight) {
|
||
|
|
PathAdd(vec2(br.x, tl.y));
|
||
|
|
} else {
|
||
|
|
PathAdd(vec2(br.x, br.y - r));
|
||
|
|
PathFastArcToN(vec2(br.x - r, br.y - r), r, 0.0f, M_PI / 2.0f, segments);
|
||
|
|
}
|
||
|
|
/** Bottom Right */
|
||
|
|
if (flags & LiPathRectFlags_KeepBotRight) {
|
||
|
|
PathAdd(br);
|
||
|
|
} else {
|
||
|
|
PathAdd(vec2(tl.x + r, br.y));
|
||
|
|
PathFastArcToN(vec2(tl.x + r, br.y - r), r, M_PI / 2.0f, M_PI, segments);
|
||
|
|
}
|
||
|
|
/** Bottom Left */
|
||
|
|
if (flags & LiPathRectFlags_KeepBotLeft) {
|
||
|
|
PathAdd(vec2(tl.x, br.y));
|
||
|
|
} else {
|
||
|
|
PathAdd(vec2(tl.x, tl.y + r));
|
||
|
|
PathFastArcToN(vec2(tl.x + r, tl.y + r), r, M_PI, 3.0f * M_PI / 2.0f,
|
||
|
|
segments);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Drawing functions */
|
||
|
|
PD_API void Drawlist::DrawRect(const fvec2& pos, const fvec2& size, u32 color,
|
||
|
|
int t) {
|
||
|
|
PathRect(pos, pos + size);
|
||
|
|
PathStroke(color, t, LiDrawFlags_Close);
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawRectFilled(const fvec2& pos, const fvec2& size,
|
||
|
|
u32 color) {
|
||
|
|
PathRect(pos, pos + size);
|
||
|
|
PathFill(color);
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawTriangle(const fvec2& a, const fvec2& b,
|
||
|
|
const fvec2& c, u32 color, int t) {
|
||
|
|
PathAdd(a);
|
||
|
|
PathAdd(b);
|
||
|
|
PathAdd(c);
|
||
|
|
PathStroke(color, t, LiDrawFlags_Close);
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawTriangleFilled(const fvec2& a, const fvec2& b,
|
||
|
|
const fvec2& c, u32 color) {
|
||
|
|
PathAdd(a);
|
||
|
|
PathAdd(b);
|
||
|
|
PathAdd(c);
|
||
|
|
PathFill(color);
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawCircle(const fvec2& center, float rad, u32 color,
|
||
|
|
int num_segments, int t) {
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
UnbindTexture(); // Only Solid Color Supported
|
||
|
|
PathStroke(color, t, LiDrawFlags_Close);
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawCircleFilled(const fvec2& center, 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);
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawText(const fvec2& p, const char* text, u32 color) {}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawTextEx(const fvec2& p, const char* text, u32 color,
|
||
|
|
LiTextFlags flags, const fvec2& box) {}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawPolyLine(const Pool<fvec2>& points, u32 color,
|
||
|
|
LiDrawFlags flags, int t) {
|
||
|
|
if (points.size() < 2) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
UnbindTexture();
|
||
|
|
auto& cmd = NewCommand();
|
||
|
|
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 = Math::PrimLine(points[i], points[j], t);
|
||
|
|
this->PrimQuad(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), color);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::DrawConvexPolyFilled(const Pool<fvec2>& points,
|
||
|
|
u32 color) {
|
||
|
|
if (points.size() < 3) {
|
||
|
|
return; // Need at least three points
|
||
|
|
}
|
||
|
|
|
||
|
|
// Support for Custom Textures (UV calculation)
|
||
|
|
float minX = points[0].x, minY = points[0].y;
|
||
|
|
float maxX = minX, maxY = minY;
|
||
|
|
// Check for the max and min Positions
|
||
|
|
for (const auto& it : points) {
|
||
|
|
if (it.x < minX) minX = it.x;
|
||
|
|
if (it.y < minY) minY = it.y;
|
||
|
|
if (it.x > maxX) maxX = it.x;
|
||
|
|
if (it.y > maxY) maxY = it.y;
|
||
|
|
}
|
||
|
|
// Get Short defines for UV
|
||
|
|
// (Bottom Right is not required)
|
||
|
|
auto uv_tl = pCurrentTexture.GetUV().TopLeft();
|
||
|
|
auto uv_tr = pCurrentTexture.GetUV().TopRight();
|
||
|
|
auto uv_bl = pCurrentTexture.GetUV().BotLeft();
|
||
|
|
|
||
|
|
auto& cmd = NewCommand();
|
||
|
|
cmd.Reserve(points.size(), (points.size() - 2) * 3);
|
||
|
|
// Render
|
||
|
|
for (int i = 2; i < (int)points.size(); i++) {
|
||
|
|
cmd.Add(0, i, i - 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 0; i < (int)points.size(); i++) {
|
||
|
|
// Calculate U and V coords
|
||
|
|
float u =
|
||
|
|
uv_tl.x + ((points[i].x - minX) / (maxX - minX)) * (uv_tr.x - uv_tl.x);
|
||
|
|
float v =
|
||
|
|
uv_tl.y + ((points[i].y - minY) / (maxY - minY)) * (uv_bl.y - uv_tl.y);
|
||
|
|
cmd.Add(Vertex(points[i], fvec2(u, v), color));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::PrimQuad(Command& cmd, const Rect& quad, const Rect& uv,
|
||
|
|
u32 color) {
|
||
|
|
cmd.Add(2, 1, 0);
|
||
|
|
cmd.Add(3, 2, 0);
|
||
|
|
cmd.Add(Vertex(quad.TopLeft(), uv.TopLeft(), color));
|
||
|
|
cmd.Add(Vertex(quad.TopRight(), uv.TopRight(), color));
|
||
|
|
cmd.Add(Vertex(quad.BotRight(), uv.BotRight(), color));
|
||
|
|
cmd.Add(Vertex(quad.BotLeft(), uv.BotLeft(), color));
|
||
|
|
}
|
||
|
|
|
||
|
|
PD_API void Drawlist::PrimTriangle(Command& cmd, const fvec2& a, const fvec2& b,
|
||
|
|
const fvec2& c, u32 color) {
|
||
|
|
cmd.Add(2, 1, 0);
|
||
|
|
cmd.Add(Vertex(a, vec2(0.f, 1.f), color));
|
||
|
|
cmd.Add(Vertex(b, vec2(1.f, 1.f), color));
|
||
|
|
cmd.Add(Vertex(c, vec2(1.f, 0.f), color));
|
||
|
|
}
|
||
|
|
} // namespace Li
|
||
|
|
} // namespace PD
|