# Stage 1.7

- Added File to Memory and FastHashMomory
- Add Protection that only one app can exist
- Add a Trace exist Variable as GetTraceRef automatically creates a trace
- Outsource the LI::Rect to its own header
- Add a CurrentScreen func
- Use Rect for uv (to manually set all corners)
- Rect still supports to use vec4 for uv
- Add tex3ds Spritesheet support
- Add T3X Loader to Texture (if single tex)
- Integrate an autounload into Texture as in case of spritesheet the Tex needs to be unloaded manually
- Safe some performance in texture loading by combining the Loops (best thing ive ever found)
- Use the Momory Hash to only render one error icon into the TTF Texture
- Also Try loading the whole 16-Bit range
- Use GPU_A8 format for TTF rendering to save 24Bits per pixel and use the same Rendermode as System Font
- Simplify Quad Command by using modern vec api
- Integrate Text aligning
- Fix FPS displayed twice in Performance overlay
- UI7 DrawList now has its own AST system
  - TODO: do the same layering for the objects as Text uses
- Map Drawcommands with a bool that declares either bottom or top screen was active
- Add first basic Manu functions
- Fix Typos in Theme
- Add a basic UI7 Context Handler

## Extra
- Added JetBrainsMono font in Test app
## Bugs:
- Performance Overlay Freezes 3ds hardware and crashes Citra with Vulkan when System Font is used
- UI7 Menu scrolling is as scruffed as back in RenderD7 0.9.5
This commit is contained in:
2025-01-29 03:14:29 +01:00
parent d55f485b8d
commit 2914f2c8e5
33 changed files with 1198 additions and 211 deletions

View File

@ -28,6 +28,8 @@ SOFTWARE.
#include <pd/common/sys.hpp>
namespace PD {
int App::too;
void App::Run() {
this->PreInit();
this->Init();
@ -45,7 +47,10 @@ void App::Run() {
}
PD::TT::End("App_MainLoop");
PD::TT::Beg("Ovl_Update");
renderer->Layer(90);
overlay_mgr->Update(dt);
/// Messages have their own special Layer
renderer->Layer(93);
msg_mgr->Update(dt);
PD::TT::End("Ovl_Update");
renderer->Render();

26
source/common/io.cpp Normal file
View File

@ -0,0 +1,26 @@
#include <pd/common/io.hpp>
namespace PD {
namespace IO {
std::vector<u8> LoadFile2Mem(const std::string& path) {
std::ifstream iff(path, std::ios::binary);
if (!iff) {
return std::vector<u8>();
}
iff.seekg(0, std::ios::end);
size_t szs = iff.tellg();
iff.seekg(0, std::ios::beg);
std::vector<u8> res(szs, 0);
iff.read(reinterpret_cast<char*>(res.data()), res.size());
iff.close();
return res;
}
u32 HashMemory(const std::vector<u8>& data) {
u32 hash = 4477;
for (auto& it : data) {
hash = (hash * 33) + it;
}
return hash;
}
} // namespace IO
} // namespace PD

View File

@ -44,5 +44,8 @@ TT::Res::Ref& GetTraceRef(const std::string& id) {
}
return pd_sys_tm[id];
}
bool TraceExist(const std::string& id) {
return pd_sys_tm.find(id) != pd_sys_tm.end();
}
TraceMap& GetTraceMap() { return pd_sys_tm; }
} // namespace PD::Sys

View File

@ -23,6 +23,7 @@ SOFTWARE.
#include <pd/external/stb_truetype.h>
#include <pd/common/io.hpp>
#include <pd/common/strings.hpp>
#include <pd/common/sys.hpp>
#include <pd/graphics/li7_shader.hpp>
@ -45,31 +46,44 @@ void Font::LoadTTF(const std::string& path, int height) {
loader.read(reinterpret_cast<char*>(buffer), len);
loader.close();
stbtt_InitFont(&inf, buffer, 0);
std::vector<unsigned char> font_tex(quad * quad * 4);
std::vector<unsigned char> font_tex(quad * quad);
float scale = stbtt_ScaleForPixelHeight(&inf, pixel_height);
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap);
int baseline = static_cast<int>(ascent * scale);
std::map<u32, int> buf_cache;
auto tex = Texture::New();
vec2 off;
for (int i = 0; i < 255; i++) {
Codepoint c;
for (u32 ii = 0x0000; ii < 0xFFFF; ii++) {
int i = stbtt_FindGlyphIndex(&inf, ii);
if (i == 0) {
continue;
}
if (stbtt_IsGlyphEmpty(&inf, i)) {
c.cp(i);
c.tex(tex);
c.invalid(true);
cpmap[i] = c;
continue;
}
Codepoint c;
int w = 0, h = 0, xo = 0, yo = 0;
unsigned char* bitmap =
stbtt_GetCodepointBitmap(&inf, scale, scale, i, &w, &h, &xo, &yo);
int x0, y0, x1, y1;
stbtt_GetCodepointBitmapBox(&inf, i, scale, scale, &x0, &y0, &x1, &y1);
u32 hashed_map = IO::HashMemory(std::vector<u8>(bitmap, bitmap + (w * h)));
if (buf_cache.find(hashed_map) != buf_cache.end()) {
c = GetCodepoint(buf_cache[hashed_map]);
c.cp(i);
cpmap[i] = c;
free(bitmap);
continue;
} else {
buf_cache[hashed_map] = i;
}
if (off[0] + w > quad) {
off[1] += pixel_height;
off[0] = 0;
@ -88,24 +102,25 @@ void Font::LoadTTF(const std::string& path, int height) {
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
int map_pos = ((off[1] + y) * quad + (off[0] + x)) * 4;
font_tex[map_pos + 0] = 255;
font_tex[map_pos + 1] = 255;
font_tex[map_pos + 2] = 255;
font_tex[map_pos + 3] = bitmap[x + y * w];
int map_pos = ((off[1] + y) * quad + (off[0] + x));
font_tex[map_pos] = bitmap[x + y * w];
}
}
free(bitmap);
cpmap[i] = c;
// Small Patch to avoid some possible artifacts
off[0] += w + 1;
if (off[0] + w > quad) {
off[1] += pixel_height;
if (off[1] + pixel_height > quad) {
break;
}
off[0] = 0;
}
free(bitmap);
cpmap[i] = c;
}
tex->LoadPixels(font_tex, quad, quad, Texture::RGBA32, Texture::LINEAR);
tex->LoadPixels(font_tex, quad, quad, Texture::A8, Texture::LINEAR);
textures.push_back(tex);
}
@ -143,6 +158,7 @@ void Font::LoadSystemFont() {
tx->border = 0xffffffff;
tx->lodParam = 0;
stex->LoadExternal(tx, vec2(tx->width, tx->height), vec4(0, 1, 1, 0));
stex->AutoUnLoad(false);
textures[i] = stex;
}
std::vector<unsigned int> charSet;
@ -192,8 +208,10 @@ void Font::LoadSystemFont() {
codepoint.uv(vec4(dat.texcoord.left, dat.texcoord.top, dat.texcoord.right,
dat.texcoord.bottom));
if (textures.at(dat.sheetIndex) != nullptr) {
if (dat.sheetIndex < (int)textures.size()) {
codepoint.tex(textures[dat.sheetIndex]);
} else {
codepoint.invalid(true);
}
codepoint.size(vec2(dat.vtxcoord.right, dat.vtxcoord.bottom));
codepoint.off(0);
@ -250,7 +268,9 @@ void Renderer::StaticText::Setup(Renderer* ren, const vec2& pos, u32 clr,
this->pos = pos;
this->ren = ren;
this->text = StaticObject::New();
ren->TextCommand(this->text->List(), pos, clr, text, flags, box);
/// Ensure that it also renders Out of Screen i guess
ren->TextCommand(this->text->List(), pos, clr, text,
flags | LITextFlags_RenderOOS, box);
OptiCommandList(this->text->List());
}
@ -267,6 +287,8 @@ void Renderer::StaticText::SetPos(const vec2& pos) {
text->MoveIt(pos - this->pos);
}
void Renderer::StaticText::SetLayer(int layer) { text->ReLayer(layer); }
bool Renderer::InBox(const vec2& pos, const vec2& szs, const vec4& rect) {
return (pos[0] < rect[2] || pos[1] < rect[3] || pos[0] + szs[0] > rect[0] ||
pos[1] + szs[1] > rect[1]);
@ -341,18 +363,14 @@ void Renderer::SetupCommand(Command::Ref cmd) {
cmd->Index(cmd_idx++).Layer(current_layer).Tex(current_tex);
}
void Renderer::QuadCommand(Command::Ref cmd, const Rect& quad, const vec4& uv,
void Renderer::QuadCommand(Command::Ref cmd, const Rect& quad, const Rect& uv,
u32 col) {
cmd->PushIndex(0).PushIndex(1).PushIndex(2);
cmd->PushIndex(0).PushIndex(2).PushIndex(3);
cmd->PushVertex(
Vertex(vec2(quad.Bot().z(), quad.Bot().w()), vec2(uv.z(), uv.w()), col));
cmd->PushVertex(
Vertex(vec2(quad.Top().z(), quad.Top().w()), vec2(uv.z(), uv.y()), col));
cmd->PushVertex(
Vertex(vec2(quad.Top().x(), quad.Top().y()), vec2(uv.x(), uv.y()), col));
cmd->PushVertex(
Vertex(vec2(quad.Bot().x(), quad.Bot().y()), vec2(uv.x(), uv.w()), col));
cmd->PushVertex(Vertex(quad.BotRight(), uv.BotRight(), col));
cmd->PushVertex(Vertex(quad.TopRight(), uv.TopRight(), col));
cmd->PushVertex(Vertex(quad.TopLeft(), uv.TopLeft(), col));
cmd->PushVertex(Vertex(quad.BotLeft(), uv.BotLeft(), col));
}
void Renderer::TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b,
@ -364,7 +382,6 @@ void Renderer::TriangleCommand(Command::Ref cmd, const vec2& a, const vec2& b,
cmd->PushVertex(Vertex(c, vec2(1.f, 0.f), col));
}
/// TO BE REWRITTEN
void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
u32 color, const std::string& text,
LITextFlags flags, const vec2& box) {
@ -375,6 +392,23 @@ void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
float cfs = (default_font_h * text_size) / (float)font->PixelHeight();
float lh = (float)font->PixelHeight() * cfs;
vec2 td;
vec2 rpos = pos;
vec2 rbox = box;
if (flags & (LITextFlags_AlignMid | LITextFlags_AlignRight)) {
td = GetTextDimensions(text);
if (rbox[0] == 0.f) {
rbox[0] = area_size.x();
}
if (rbox[1] == 0.f) {
rbox[1] = area_size.y();
}
}
if (flags & LITextFlags_AlignMid) {
rpos = rbox * 0.5 - td * 0.5 + pos;
}
if (flags & LITextFlags_AlignRight) {
rpos[0] = rbox[0] - td[0];
}
std::vector<std::string> lines;
std::istringstream iss(text);
@ -384,10 +418,10 @@ void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
}
for (auto& it : lines) {
if (pos[1] + off[1] + lh < 0) {
if (rpos[1] + off[1] + lh < 0) {
off[1] += lh;
continue;
} else if (pos[1] + off[1] > GetViewport().w() &&
} else if (rpos[1] + off[1] > GetViewport().w() &&
!(flags & LITextFlags_RenderOOS)) {
// Break cause next lines would be out of screen
break;
@ -396,9 +430,7 @@ void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
auto cmd = Command::New();
current_tex = font->GetCodepoint(wline[0]).tex();
SetupCommand(cmd);
if (font->SystemFont()) {
cmd->Rendermode(RenderMode_SysFont);
}
cmd->Rendermode(RenderMode_Font);
for (auto& jt : wline) {
auto cp = font->GetCodepoint(jt);
if (cp.invalid() && jt != '\n' && jt != '\t') {
@ -409,9 +441,7 @@ void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
cmd = Command::New();
current_tex = cp.tex();
SetupCommand(cmd);
if (font->SystemFont()) {
cmd->Rendermode(RenderMode_SysFont);
}
cmd->Rendermode(RenderMode_Font);
}
if (jt == '\t') {
off[0] += 16 * cfs;
@ -421,13 +451,13 @@ void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
if (flags & LITextFlags_Shaddow) {
// Draw
Rect rec = CreateRect(
pos + vec2(off[0] + 1, off[1] + (cp.off() * cfs)) + 1,
rpos + vec2(off[0] + 1, off[1] + (cp.off() * cfs)) + 1,
cp.size() * cfs, 0.f);
QuadCommand(cmd, rec, cp.uv(), 0xff111111);
current_layer++;
}
// Draw
Rect rec = CreateRect(pos + off + vec2(0, (cp.off() * cfs)),
Rect rec = CreateRect(rpos + off + vec2(0, (cp.off() * cfs)),
cp.size() * cfs, 0.f);
QuadCommand(cmd, rec, cp.uv(), color);
current_layer = lr;
@ -450,7 +480,6 @@ vec4 Renderer::GetViewport() {
return vec4(0, 0, screen[0], screen[1]);
}
/// TO BE REWRITTEN
vec2 Renderer::GetTextDimensions(const std::string& text) {
if (!font) {
// No font no size (oder so)
@ -516,7 +545,8 @@ vec2 Renderer::GetTextDimensions(const std::string& text) {
void Renderer::UpdateRenderMode(const RenderMode& mode) {
C3D_TexEnv* env = C3D_GetTexEnv(0);
switch (mode) {
case RenderMode_SysFont:
case RenderMode_Font:
/// Sets Only Alpha Using the Color and Replase RGB with vertex color
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_RGB, GPU_PRIMARY_COLOR);
C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE);
@ -526,6 +556,7 @@ void Renderer::UpdateRenderMode(const RenderMode& mode) {
// Fall trough instead of defining twice
case RenderMode_RGBA:
default:
/// Use Texture for RGBA and vertexcolor for visibility
C3D_TexEnvInit(env);
C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0);
C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE);
@ -536,23 +567,17 @@ void Renderer::UpdateRenderMode(const RenderMode& mode) {
void Renderer::RenderOn(bool bot) {
C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, uLoc_projection,
(bot ? &bot_proj : &top_proj));
C3D_DepthTest(false, GPU_GREATER, GPU_WRITE_ALL);
UpdateRenderMode(RenderMode_RGBA);
int total_vertices = 0;
int total_indices = 0;
drawcalls = 0;
auto& cmds = draw_list[bot];
commands = cmds.size();
size_t index = 0;
if (flags & RenderFlags_LRS) {
OptiCommandList(cmds);
}
while (index < cmds.size()) {
C3D_Tex* tex = cmds[index]->Tex()->GetTex();
auto mode = cmds[index]->Rendermode();
@ -572,6 +597,7 @@ void Renderer::RenderOn(bool bot) {
}
index++;
}
C3D_TexBind(0, tex);
auto bufInfo = C3D_GetBufInfo();
@ -580,6 +606,7 @@ void Renderer::RenderOn(bool bot) {
C3D_DrawElements(GPU_TRIANGLES, index_idx - start_idx, C3D_UNSIGNED_SHORT,
index_buf.data() + start_idx);
drawcalls++;
total_vertices += vertex_idx - start_vtx;
total_indices += index_idx - start_idx;
@ -629,6 +656,8 @@ void Renderer::Render() {
if (Sys::GetTime() - it.second.TimeCreated() > 5) rem.push_back(it.first);
}
for (auto it : rem) tms.erase(it);
} else {
tms.clear();
}
if (flags & RenderFlags_AST) {
std::vector<u32> rem;
@ -641,11 +670,13 @@ void Renderer::Render() {
for (auto& it : rem) {
ast.erase(it);
}
} else {
ast.clear();
}
}
void Renderer::DrawRect(const vec2& pos, const vec2& size, u32 color,
const vec4& uv) {
const Rect& uv) {
if (!InBox(pos, size, GetViewport())) {
// Instand abort as it is out of screen
return;
@ -659,7 +690,7 @@ void Renderer::DrawRect(const vec2& pos, const vec2& size, u32 color,
void Renderer::DrawRectSolid(const vec2& pos, const vec2& size, u32 color) {
UseTex();
DrawRect(pos, size, color);
DrawRect(pos, size, color, vec4(0.f, 1.f, 1.f, 0.f));
}
void Renderer::DrawTriangle(const vec2& a, const vec2& b, const vec2& c,

View File

@ -0,0 +1,44 @@
#include <pd/common/io.hpp>
#include <pd/graphics/spritesheet.hpp>
namespace PD {
SpriteSheet::~SpriteSheet() { textures.clear(); }
void SpriteSheet::LoadFile(const std::string& path) {
auto file = IO::LoadFile2Mem(path);
if (file.size() == 0) {
Error("Unable to load file:\n" + path);
}
C3D_Tex* tex = new C3D_Tex;
auto t3x =
Tex3DS_TextureImport(file.data(), file.size(), tex, nullptr, false);
if (!t3x) {
Error("Unable to import:\n" + path);
}
tex->border = 0;
C3D_TexSetWrap(tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
C3D_TexSetFilter(tex, GPU_LINEAR, GPU_NEAREST);
textures.reserve(Tex3DS_GetNumSubTextures(t3x) + 1);
for (int i = 0; i < (int)Tex3DS_GetNumSubTextures(t3x); i++) {
auto t = Texture::New();
auto st = Tex3DS_GetSubTexture(t3x, i);
LI::Rect uv(vec2(st->left, st->top), vec2(st->right, st->top),
vec2(st->left, st->bottom), vec2(st->right, st->bottom));
if (st->top < st->bottom) {
uv.SwapVec2XY();
}
t->LoadExternal(tex, vec2(st->width, st->height), uv);
textures.push_back(t);
}
}
Texture::Ref SpriteSheet::Get(int idx) {
if (idx >= (int)textures.size()) {
Error("Trying to Access Texture " + std::to_string(idx + 1) + " of " +
std::to_string(NumTextures()));
}
return textures[idx];
}
int SpriteSheet::NumTextures() const { return textures.size(); }
} // namespace PD

View File

@ -24,12 +24,14 @@ SOFTWARE.
#include <3ds.h>
#include <pd/external/stb_image.h>
#include <tex3ds.h>
#include <pd/common/error.hpp>
#include <pd/common/io.hpp>
#include <pd/common/timetrace.hpp>
#include <pd/graphics/texture.hpp>
#include <pd/maths/bit_util.hpp>
#include <pd/maths/img_convert.hpp>
#include <pd/common/error.hpp>
namespace PD {
GPU_TEXCOLOR GetTexFmt(Texture::Type type) {
@ -54,36 +56,6 @@ void Texture::MakeTex(std::vector<u8>& buf, int w, int h, Texture::Type type,
Filter filter) {
// Don't check here as check done before
int bpp = GetBPP(type);
if (bpp == 4) {
// RGBA -> Abgr
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int pos = (x + y * w) * bpp;
auto r = buf[pos + 0];
auto g = buf[pos + 1];
auto b = buf[pos + 2];
auto a = buf[pos + 3];
buf[pos + 0] = a;
buf[pos + 1] = b;
buf[pos + 2] = g;
buf[pos + 3] = r;
}
}
} else if (bpp == 3) {
// RGBA -> Abgr
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int pos = (x + y * w) * bpp;
auto r = buf[pos + 0];
auto g = buf[pos + 1];
auto b = buf[pos + 2];
buf[pos + 0] = b;
buf[pos + 1] = g;
buf[pos + 2] = r;
}
}
}
vec2 tex_size(w, h);
// Pow2
if (!PD::BitUtil::IsSingleBit(w)) {
@ -95,10 +67,8 @@ void Texture::MakeTex(std::vector<u8>& buf, int w, int h, Texture::Type type,
this->size.x() = (u16)w;
this->size.y() = (u16)h;
this->uv.x() = 0.0f;
this->uv.y() = 1.0f;
this->uv.z() = ((float)w / (float)tex_size.x());
this->uv.w() = 1.0 - ((float)h / (float)tex_size.y());
this->uv = vec4(0.f, 1.f, ((float)w / (float)tex_size.x()),
1.0 - ((float)h / (float)tex_size.y()));
// Texture Setup
auto fltr = (filter == NEAREST ? GPU_NEAREST : GPU_LINEAR);
@ -109,7 +79,9 @@ void Texture::MakeTex(std::vector<u8>& buf, int w, int h, Texture::Type type,
memset(tex->data, 0, tex->size);
if (bpp == 3 || bpp == 4) {
/// Probably Remove this if statement in future
/// This are the things confirmed as working
if (bpp == 3 || bpp == 4 || bpp == 1) {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int dst_pos = ((((y >> 3) * ((int)tex_size.x() >> 3) + (x >> 3)) << 6) +
@ -117,13 +89,13 @@ void Texture::MakeTex(std::vector<u8>& buf, int w, int h, Texture::Type type,
((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) *
bpp;
int src_pos = (y * w + x) * bpp;
memcpy(&((u8*)tex->data)[dst_pos], &buf[src_pos], bpp);
/// Best idea i had
for (int i = 0; i < bpp; i++) {
((u8*)tex->data)[dst_pos + bpp - 1 - i] = buf[src_pos + i];
}
}
}
C3D_TexFlush(tex);
} else if (bpp == 1) {
C3D_TexLoadImage(tex, buf.data(), GPU_TEXFACE_2D, 0);
}
tex->border = 0x00000000;
@ -200,4 +172,23 @@ void Texture::LoadPixels(const std::vector<u8>& pixels, int w, int h, Type type,
std::vector<u8> cpy(pixels);
MakeTex(cpy, w, h, type, filter);
}
void Texture::LoadT3X(const std::string& path) {
this->Delete();
auto file = IO::LoadFile2Mem(path);
if (file.size() == 0) {
Error("Unable to load file:\n" + path);
}
this->tex = new C3D_Tex;
auto t3x =
Tex3DS_TextureImport(file.data(), file.size(), this->tex, nullptr, false);
if (!t3x) {
Error("Unable to import:\n" + path);
}
auto st = Tex3DS_GetSubTexture(t3x, 0);
this->uv = vec4(st->left, st->top, st->right, st->bottom);
this->size[0] = st->width;
this->size[1] = st->height;
Tex3DS_TextureFree(t3x);
}
} // namespace PD

View File

@ -484,8 +484,6 @@ void Keyboard::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) {
/// Process Controller Movement
Movement(inp);
/// Declare RenderLayer (10 above the latest)
ren->Layer(ren->Layer() + 10);
/// Update animations
flymgr.Update(delta);
selector.Update(delta);

View File

@ -104,8 +104,6 @@ void MessageMgr::Push(const std::string& title, const std::string& text) {
}
void MessageMgr::Update(float delta) {
// Go two layers up and Render on Top
ren->Layer(ren->Layer() + 2);
ren->OnScreen(Screen::Top);
for (size_t i = 0; i < msgs.size(); i++) {
// Update the Animation Handlers and Move older

View File

@ -12,8 +12,7 @@ void Performance::Update(float delta, LI::Renderer::Ref ren, Hid::Ref inp) {
ren->OnScreen(Screen::Top);
ren->TextScale(0.6);
vec2 pos;
Line(pos, std::format("FPS {:.1f} FPS / {:.2f}ms", 1000.f / delta, delta),
ren);
Line(pos, std::format("{:.1f} FPS / {:.2f}ms", 1000.f / delta, delta), ren);
Line(pos, "Ren [AVG]: " + TSA("LI_RenderAll"), ren);
Line(pos, "App [AVG]: " + TSA("App_MainLoop"), ren);
Line(pos, "Ovl [AVG]: " + TSA("Ovl_Update"), ren);

View File

@ -21,6 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <pd/common/strings.hpp>
#include <pd/ui7/drawlist.hpp>
namespace PD {
@ -31,11 +32,12 @@ void DrawList::AddRectangle(vec2 pos, vec2 szs, const UI7Color& clr) {
}
auto rect = ren->CreateRect(pos, szs, 0.f);
auto cmd = LI::Command::New();
ren->SetupCommand(cmd);
ren->UseTex();
ren->SetupCommand(cmd);
cmd->Layer(layer);
ren->QuadCommand(cmd, rect, vec4(0.f, 1.f, 1.f, 0.f), clr);
commands.push_back(cmd);
commands.push_back(
std::make_pair(ren->CurrentScreen() == Screen::Bottom, cmd));
}
void DrawList::AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2,
@ -44,41 +46,82 @@ void DrawList::AddTriangle(vec2 pos0, vec2 pos1, vec2 pos2,
return;
}
auto cmd = LI::Command::New();
ren->SetupCommand(cmd);
ren->UseTex();
ren->SetupCommand(cmd);
cmd->Layer(layer);
ren->TriangleCommand(cmd, pos0, pos1, pos2, clr);
commands.push_back(cmd);
commands.push_back(
std::make_pair(ren->CurrentScreen() == Screen::Bottom, cmd));
}
void DrawList::AddText(vec2 pos, const std::string& text, const UI7Color& clr,
LITextFlags flags, vec2 box) {
u32 id = Strings::FastHash(text);
auto e = static_text.find(id);
if (e == static_text.end()) {
static_text[id] = LI::Renderer::StaticText::New();
e = static_text.find(id);
}
if (!e->second->IsSetup()) {
e->second->Setup(&(*ren), pos, clr, text, flags, box);
}
e->second->SetPos(pos);
e->second->SetColor(clr);
e->second->SetLayer(base + layer);
e->second->Draw();
////// STILL LEAVING THE OLD CODE BELOW AS IT IS MAYBE NEEDED //////
////// IF STATIC TEXT SYSTEM SHOULD HAVE AN DISABLE OPTION //////
// Dont create a Command here as TextCommand has autosetup
// cause it needs to generate multiple commands if
// Font uses multiple textures
ren->TextCommand(commands, pos, clr, text, flags, box);
// Oh and Handle Layer management here as well
// int l = ren->Layer();
// ren->Layer(layer);
// std::vector<LI::Command::Ref> cmds;
// ren->TextCommand(cmds, pos, clr, text, flags, box);
// ren->Layer(l);
// for (auto c : cmds) {
// commands.push_back(
// std::make_pair(ren->CurrentScreen() == Screen::Bottom, c));
// }
}
void DrawList::AddImage(vec2 pos, Texture::Ref img) {
if (!ren->InBox(pos, img->GetSize(), ren->GetViewport())) {
void DrawList::AddImage(vec2 pos, Texture::Ref img, vec2 size) {
size = size == 0.f ? img->GetSize() : size;
if (!ren->InBox(pos, size, ren->GetViewport())) {
return;
}
auto rect = ren->CreateRect(pos, img->GetSize(), 0.f);
auto rect = ren->CreateRect(pos, size, 0.f);
auto cmd = LI::Command::New();
ren->SetupCommand(cmd);
ren->UseTex(img);
ren->SetupCommand(cmd);
cmd->Layer(layer);
ren->QuadCommand(cmd, rect, vec4(0.f, 1.f, 1.f, 0.f), 0xffffffff);
commands.push_back(cmd);
ren->QuadCommand(cmd, rect, img->GetUV(), 0xffffffff);
commands.push_back(
std::make_pair(ren->CurrentScreen() == Screen::Bottom, cmd));
}
void DrawList::Clear() { commands.clear(); }
void DrawList::Process() {
// UI7 Commands Use LI7 as default feature
ren->OptiCommandList(commands);
for (auto command : commands) {
ren->PushCommand(command);
command.second->Layer(command.second->Layer() + base);
ren->OnScreen(command.first ? Screen::Bottom : Screen::Top);
ren->PushCommand(command.second);
}
commands.clear();
layer = 0;
std::vector<u32> rem;
for (auto it : static_text) {
if (!it.second->Used()) {
rem.push_back(it.first);
}
it.second->SetUnused();
}
for (auto& it : rem) {
static_text.erase(it);
}
}
} // namespace UI7

331
source/ui7/menu.cpp Normal file
View File

@ -0,0 +1,331 @@
#include <pd/common/sys.hpp>
#include <pd/common/timetrace.hpp>
#include <pd/ui7/menu.hpp>
//////////////////////////////
//////// OBJECT SETUP ////////
////// Setup Variables ///////
///////// Move Cursor ////////
//// Check Scrolling State ///
/////// Handle Controls //////
////////// Render ////////////
//////////////////////////////
namespace PD {
namespace UI7 {
void UI7::Menu::Label(const std::string& label) {
vec2 size = this->back->GetRenderer()->GetTextDimensions(label);
vec2 pos = Cursor();
CursorMove(size - vec2(0, 4)); // Fix to make gap not to large
if (HandleScrolling(pos, size)) {
return;
}
/// To Draw a Label above the head bar you should
/// use the m->GetFrontList() instead
main->AddText(pos, label, linked_theme->Get(UI7Color_Text), 0,
vec2(view_area.z(), 20));
}
bool UI7::Menu::Button(const std::string& label) {
bool ret = false;
auto tszs = this->back->GetRenderer()->GetTextDimensions(label);
vec2 size = tszs + vec2(8, 4);
vec2 pos = Cursor();
UI7Color clr = UI7Color_Button;
CursorMove(size);
/////// SCROLLING HANDLER HERE ////////
if (HandleScrolling(pos, size)) {
return false;
}
/// CONTROLS ///
if (has_touch) {
if (inp->IsHeld(inp->Touch) &&
LI::Renderer::InBox(inp->TouchPos(), vec4(pos, size))) {
clr = UI7Color_ButtonHovered;
}
if (inp->IsUp(inp->Touch) &&
LI::Renderer::InBox(inp->TouchPosLast(), vec4(pos, size))) {
clr = UI7Color_ButtonActive;
ret = true;
}
}
/// Rendering ///
main->AddRectangle(pos, size, linked_theme->Get(clr));
main->AddText(pos + size * 0.5 - tszs * 0.5, label,
linked_theme->Get(UI7Color_Text));
return ret;
}
void UI7::Menu::Checkbox(const std::string& label, bool& v) {
vec2 pos = Cursor();
vec2 tdim = front->ren->GetTextDimensions(label);
vec2 cbs(18);
vec2 size = cbs + vec2(tdim.x() + 5, 0);
CursorMove(size);
if (HandleScrolling(pos, size)) {
return;
}
UI7Color cbbg = UI7Color_FrameBackground;
if (has_touch) {
if (inp->IsHeld(inp->Touch) &&
LI::Renderer::InBox(inp->TouchPos(), vec4(pos, size))) {
cbbg = UI7Color_FrameBackgroundHovered;
}
if (inp->IsUp(inp->Touch) &&
LI::Renderer::InBox(inp->TouchPosLast(), vec4(pos, size))) {
cbbg = UI7Color_FrameBackgroundHovered;
v = !v;
}
}
main->AddRectangle(pos, cbs, linked_theme->Get(cbbg));
if (v) {
main->AddRectangle(pos + 2, cbs - 4, linked_theme->Get(UI7Color_Checkmark));
}
main->AddText(pos + vec2(cbs.x() + 5, cbs.y() * 0.5 - tdim.y() * 0.5), label,
linked_theme->Get(UI7Color_Text));
}
void UI7::Menu::Image(Texture::Ref img, vec2 size) {
/// Variable Setup Stage ///
size = size == 0.f ? img->GetSize() : size;
vec2 pos = Cursor();
CursorMove(size);
/// Scrolling Handler ///
if (HandleScrolling(pos, size)) {
return;
}
/// Rendering Stage ///
main->AddImage(pos, img, size);
}
void UI7::Menu::DebugLabels() {
std::stringstream s;
s << "Name: " << name << " [";
s << std::hex << std::setw(8) << std::setfill('0') << id;
s << std::dec << "]";
this->Label(s.str());
this->Label(
"Pre: " +
Strings::FormatNanos(
Sys::GetTraceRef("MPRE_" + name)->GetProtocol()->GetAverage()));
this->Label(
"Post: " +
Strings::FormatNanos(
Sys::GetTraceRef("MPOS_" + name)->GetProtocol()->GetAverage()));
this->Label(
"Update: " +
Strings::FormatNanos(
Sys::GetTraceRef("MUPT_" + name)->GetProtocol()->GetAverage()));
this->Label(
"MUser: " +
Strings::FormatNanos(
Sys::GetTraceRef("MUSR_" + name)->GetProtocol()->GetAverage()));
}
void UI7::Menu::Update(float delta) {
TT::Scope st("MUPT_" + name);
this->back->BaseLayer(30);
this->back->Process();
this->main->BaseLayer(40);
this->main->Process();
this->front->BaseLayer(50);
this->front->Process();
}
void UI7::Menu::CursorMove(const vec2& size) {
last_size = size;
slcursor = cursor + vec2(size[0] + 5, 0);
if (bslpos[1]) {
cursor = vec2(5, cursor[1] + bslpos[1] + 5);
bslpos = vec2();
} else {
cursor = vec2(5, cursor[1] + size[1] + 5);
}
max = vec2(slcursor[0], cursor[1]);
}
void UI7::Menu::PreHandler(UI7MenuFlags flags) {
TT::Scope st("MPRE_" + name);
TT::Beg("MUSR_" + name);
Cursor(vec2(5, 5));
this->flags = flags;
this->scrolling[0] = flags & UI7MenuFlags_HzScrolling;
this->scrolling[1] = flags & UI7MenuFlags_VtScrolling;
has_touch = main->GetRenderer()->CurrentScreen() == Screen::Bottom;
if (!(flags & UI7MenuFlags_NoBackground)) {
back->AddRectangle(0, view_area.zw(), UI7Color_Background);
}
if (!(flags & UI7MenuFlags_NoTitlebar)) {
tbh = front->GetRenderer()->TextScale() * 30.f;
front->AddRectangle(0, vec2(view_area.z(), tbh),
linked_theme->Get(UI7Color_Header));
vec2 tpos(5, tbh * 0.5 - front->ren->GetTextDimensions(name).y() * 0.5);
LITextFlags tflags = LITextFlags_None;
if (flags & UI7MenuFlags_CenterTitle) {
tpos = 0;
tflags = LITextFlags_AlignMid;
}
front->AddText(tpos, this->name, linked_theme->Get(UI7Color_Text), tflags,
vec2(view_area.z(), tbh));
Cursor(vec2(5, tbh + 5));
}
}
void UI7::Menu::PostHandler() {
TT::Scope st("MPOS_" + name);
if (scrolling[1]) {
scroll_allowed[1] = (max[1] > 235);
scrollbar[1] = scroll_allowed[1];
if (scrollbar[1]) {
/// Setup Some Variables hare [they are self described]
int screen_w = view_area.z();
int tsp = 5 + tbh;
int slider_w = 4;
int szs = view_area.w() - tsp - 5;
/// Actually dont have a Horizontal bar yet
if (scrollbar[0]) szs -= slider_w - 2;
int lslider_h = 20; // Dont go less heigt for the drag
float slider_h = (szs - 4) * (float(szs - 4) / max[1]);
/// Visual Slider Height (How it looks in the end)
int vslider_h = std::clamp(slider_h, float(lslider_h), float(szs - 4));
/// Check if we overscroll to the bottom and Auto scroll back...
/// Probably schould use Tween ENgine here
if (scrolling_off[1] > max[1] - view_area[3] && max[1] != 0.f &&
max[1] >= view_area[3] - 5) {
scrolling_off[1] -= 3.f;
if (scrolling_off[1] < max[1] - view_area[3]) {
scrolling_off[1] = max[1] - view_area[3];
}
}
/// Do the Same as above just for Overscroll back to the top
if (scrolling_off[1] < 0) {
scrolling_off[1] += 3.f;
if (scrolling_off[1] > 0) {
scrolling_off[1] = 0;
}
}
/// Dont overscroll to much
if (scrolling_off[1] < -40 ||
scrolling_off[1] > max[1] - view_area[3] + 40) {
scroll_mod[1] = 0.f;
}
/// The pain :(
if (has_touch) {
vec2 tpos = inp->TouchPos();
if (inp->IsDown(inp->Touch)) {
mouse = tpos;
} else if (inp->IsUp(inp->Touch)) {
mouse = vec2();
}
if (inp->IsHeld(inp->Touch)) {
if (!front->ren->InBox(tpos, vec4(view_area[2] - 13, tbh + 5, 8,
view_area[3] - tbh - 10))) {
if (scrolling_off[1] < max[1] - view_area[3] + 40 &&
scrolling_off[1] > -40) {
/// Cursor Mod
float cm = mouse[1] - tpos[1];
if (scroll_mod[1] <= 4.f && scroll_mod[1] >= -4.f && cm != 0) {
scroll_mod[1] = cm;
}
}
mouse = tpos;
}
}
}
/// Effect
if (scroll_mod[1] != 0) {
scrolling_off[1] += scroll_mod[1];
}
if (scroll_mod[1] < 0.f) {
scroll_mod[1] += 0.4f;
if (scroll_mod[1] > 0.f) {
scroll_mod[1] = 0;
}
}
if (scroll_mod[1] > 0.f) {
scroll_mod[1] -= 0.4f;
if (scroll_mod[1] < 0.f) {
scroll_mod[1] = 0;
}
}
int srpos =
tsp + std::clamp(float(szs - vslider_h - 4) *
(scrolling_off[1] / (max[1] - view_area[3])),
0.f, float(szs - vslider_h - 4));
/// Rendering Stage
front->AddRectangle(vec2(screen_w - 12, tsp), vec2(slider_w * 2, szs),
linked_theme->Get(UI7Color_FrameBackground));
front->AddRectangle(vec2(screen_w - 10, tsp + 2), vec2(slider_w, szs - 4),
linked_theme->Get(UI7Color_FrameBackgroundHovered));
front->AddRectangle(vec2(screen_w - 10, srpos + 2),
vec2(slider_w, vslider_h),
linked_theme->Get(UI7Color_Button));
}
}
TT::End("MUSR_" + name);
}
void UI7::Menu::SameLine() {
bslpos = last_size;
cursor = slcursor;
}
void UI7::Menu::Separator() {
vec2 pos = Cursor();
vec2 size = vec2(view_area.z() - (scrollbar[1] ? 24 : 10), 1);
CursorMove(size);
if (HandleScrolling(pos, size)) {
return;
}
main->AddRectangle(pos, size, linked_theme->Get(UI7Color_TextDead));
}
void UI7::Menu::SeparatorText(const std::string& label) {
vec2 size = vec2(view_area.z() - (scrollbar[1] ? 24 : 10), 1);
vec2 tdim = this->back->GetRenderer()->GetTextDimensions(label);
vec2 pos = Cursor();
CursorMove(vec2(size.x(), tdim.y() - 4)); // Fix to make gap not to large
if (HandleScrolling(pos, size)) {
return;
}
/// Label pos for better overview
vec2 lpos = pos + vec2((view_area.z() - 10) * 0.5 - tdim.x() * 0.5, 0);
main->AddRectangle(pos + vec2(0, tdim.y() * 0.5),
vec2(lpos.x() - pos.x() - 5, size.y()),
linked_theme->Get(UI7Color_TextDead));
main->AddRectangle(pos + vec2(lpos.x() + tdim.x(), tdim.y() * 0.5),
vec2(size.x() - (lpos.x() + tdim.x()), size.y()),
linked_theme->Get(UI7Color_TextDead));
main->AddText(lpos, label, linked_theme->Get(UI7Color_Text), 0,
vec2(view_area.z(), 20));
}
bool UI7::Menu::HandleScrolling(vec2& pos, const vec2& size) {
if (scrolling[1]) {
vec2 p = pos;
pos -= vec2(0, scrolling_off.y());
if (pos.y() > view_area.w() ||
(pos.y() + size.y() < tbh - 5 && p.y() > tbh)) {
return true;
}
}
return false;
}
} // namespace UI7
} // namespace PD

View File

@ -10,12 +10,12 @@ void Theme::Default(Theme& theme) {
theme.Set(UI7Color_Button, Color("#111111FF"));
theme.Set(UI7Color_ButtonDead, Color("#080808FF"));
theme.Set(UI7Color_ButtonActive, Color("#2A2A2AFF"));
theme.Set(UI7Color_ButtonDisabled, Color("#222222FF"));
theme.Set(UI7Color_ButtonHovered, Color("#222222FF"));
theme.Set(UI7Color_Header, Color("#111111FF"));
theme.Set(UI7Color_Selector, Color("#222222FF"));
theme.Set(UI7Color_Checkmark, Color("#2A2A2AFF"));
theme.Set(UI7Color_FrameBackground, Color("#555555FF"));
theme.Set(UI7Color_FragmeBackgroundHovered, Color("#777777FF"));
theme.Set(UI7Color_FrameBackgroundHovered, Color("#777777FF"));
theme.Set(UI7Color_Progressbar, Color("#00FF00FF"));
theme.Set(UI7Color_ListEven, Color("#CCCCCCFF"));
theme.Set(UI7Color_ListOdd, Color("#BBBBBBFF"));

View File

@ -0,0 +1,52 @@
#include <pd/common/timetrace.hpp>
#include <pd/ui7/ui7.hpp>
namespace PD {
bool UI7::Context::BeginMenu(const ID& id, UI7MenuFlags flags) {
Assert(!this->current, "You are already in another Menu!");
Assert(std::find(amenus.begin(), amenus.end(), (u32)id) == amenus.end(),
"Menu Name Already used or\nContext::Update not called!");
auto menu = this->menus.find(id);
if (menu == this->menus.end()) {
this->menus[id] = Menu::New(id, &theme, inp);
menu = this->menus.find(id);
}
this->current = menu->second;
if (!this->current->BackList()) {
this->current->BackList(DrawList::New(ren));
}
if (!this->current->MainList()) {
this->current->MainList(DrawList::New(ren));
}
if (!this->current->FrontList()) {
this->current->FrontList(DrawList::New(ren));
}
this->current->ViewArea(this->ren->GetViewport());
this->current->PreHandler(flags);
amenus.push_back(this->current->GetID());
return true;
}
UI7::Menu::Ref UI7::Context::GetCurrentMenu() {
Assert(current != nullptr, "Not in a Menu!");
return current;
}
void UI7::Context::EndMenu() {
this->current->PostHandler();
this->current = nullptr;
}
void UI7::Context::Update(float delta) {
TT::Scope st("UI7_Update");
Assert(current == nullptr, "Still in a Menu!");
this->back->BaseLayer(10);
this->back->Process();
for (auto it : amenus) {
menus[it]->Update(delta);
}
this->front->BaseLayer(60);
this->front->Process();
this->amenus.clear();
}
} // namespace PD