Initial Cross Platform Work
This commit is contained in:
147
source/lithium/drawlist.cpp
Normal file
147
source/lithium/drawlist.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
#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 Command::Ref DrawList::PreGenerateCmd() {
|
||||
Command::Ref cmd = Command::New();
|
||||
cmd->Layer = Layer;
|
||||
cmd->Index = pDrawList.Size();
|
||||
cmd->Tex = CurrentTex;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
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::PathRect(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 {
|
||||
PathArcToN(vec2(a.x + rounding, a.y + rounding), rounding, 4 * 6, 4 * 9,
|
||||
21);
|
||||
PathArcToN(vec2(b.x - rounding, a.y + rounding), rounding, 4 * 9, 4 * 12,
|
||||
21);
|
||||
PathArcToN(vec2(b.x - rounding, b.y - rounding), rounding, 4 * 0, 4 * 3,
|
||||
21);
|
||||
PathArcToN(vec2(a.x + rounding, b.y - rounding), rounding, 4 * 3, 4 * 6,
|
||||
21);
|
||||
}
|
||||
}
|
||||
|
||||
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& center, 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);
|
||||
}
|
||||
DrawSolid(); // Only Solid Color Supported
|
||||
PathStroke(color, thickness, (1 << 0));
|
||||
}
|
||||
|
||||
PD_LITHIUM_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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
CurrentTex = WhitePixel;
|
||||
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, line, vec4(0.f, 1.f, 1.f, 0.f), clr);
|
||||
}
|
||||
}
|
||||
AddCommand(cmd);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API void DrawList::DrawConvexPolyFilled(const Vec<fvec2>& points,
|
||||
u32 clr) {
|
||||
if (points.Size() < 3) {
|
||||
return; // Need at least three points
|
||||
}
|
||||
auto cmd = PreGenerateCmd();
|
||||
Renderer::CmdConvexPolyFilled(cmd, points, clr, CurrentTex);
|
||||
AddCommand(cmd);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API void DrawList::DrawText(const fvec2& pos,
|
||||
const std::string& text, u32 color) {
|
||||
if (!pCurrentFont) {
|
||||
return;
|
||||
}
|
||||
PD::Vec<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(cmds[i]);
|
||||
}
|
||||
}
|
||||
} // namespace LI
|
||||
} // namespace PD
|
@ -1,239 +1,278 @@
|
||||
/*
|
||||
MIT License
|
||||
Copyright (c) 2024 - 2025 René Amthor (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 <3ds.h>
|
||||
#include <pd/external/stb_truetype.h>
|
||||
|
||||
#include <pd/core/bit_util.hpp>
|
||||
#include <pd/core/io.hpp>
|
||||
#include <pd/core/strings.hpp>
|
||||
#include <pd/core/sys.hpp>
|
||||
#include <pd/lithium/font.hpp>
|
||||
|
||||
namespace PD {
|
||||
namespace LI {
|
||||
void Font::LoadTTF(const std::string &path, int height) {
|
||||
sysfont = false; // Not using System Font
|
||||
TT::Scope st("LI_LoadTTF_" + path);
|
||||
pixel_height = height; // Set internel pixel height
|
||||
// Use NextPow2 to be able to use sizes between for example 16 and 32
|
||||
// before it only was possible to use 8, 16, 32, 64 as size
|
||||
int texszs = BitUtil::GetPow2(height * 16);
|
||||
// Load stbtt
|
||||
stbtt_fontinfo inf;
|
||||
std::ifstream loader(path, std::ios::binary);
|
||||
if (!loader.is_open()) return;
|
||||
loader.seekg(0, std::ios::end);
|
||||
size_t len = loader.tellg();
|
||||
loader.seekg(0, std::ios::beg);
|
||||
unsigned char *buffer = new unsigned char[len];
|
||||
loader.read(reinterpret_cast<char *>(buffer), len);
|
||||
loader.close();
|
||||
stbtt_InitFont(&inf, buffer, 0);
|
||||
std::vector<unsigned char> font_tex(texszs * texszs); // Create font Texture
|
||||
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; // Cache to not render same codepoint tex twice
|
||||
|
||||
/// Load Codepoints
|
||||
auto tex = Texture::New();
|
||||
vec2 off;
|
||||
for (u32 ii = 0x0000; ii < 0xFFFF; ii++) {
|
||||
int i = stbtt_FindGlyphIndex(&inf, ii);
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
if (stbtt_IsGlyphEmpty(&inf, i)) {
|
||||
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);
|
||||
|
||||
// Check if Codepoint exists as hash and if it is use its already written
|
||||
// data
|
||||
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 > texszs) {
|
||||
off[1] += pixel_height;
|
||||
off[0] = 0;
|
||||
}
|
||||
|
||||
// Set UV Data
|
||||
vec4 uvs;
|
||||
uvs[0] = static_cast<float>(off.x() / (float)texszs);
|
||||
uvs[1] = static_cast<float>(1.f - (off.y() / (float)texszs));
|
||||
uvs[2] = static_cast<float>((float)(off.x() + w) / (float)texszs);
|
||||
uvs[3] = static_cast<float>(1.f - (float)(off.y() + h) / (float)texszs);
|
||||
c.uv(uvs);
|
||||
|
||||
c.tex(tex);
|
||||
c.size(vec2(w, h));
|
||||
c.off(baseline + yo);
|
||||
|
||||
// Render glyph
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
int map_pos = ((off[1] + y) * texszs + (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 > texszs) {
|
||||
off[1] += pixel_height;
|
||||
if (off[1] + pixel_height > texszs) {
|
||||
break;
|
||||
}
|
||||
off[0] = 0;
|
||||
}
|
||||
}
|
||||
// Load the Texture and append to list
|
||||
tex->LoadPixels(font_tex, texszs, texszs, Texture::A8, Texture::LINEAR);
|
||||
textures.push_back(tex);
|
||||
}
|
||||
|
||||
Font::Codepoint &Font::GetCodepoint(u32 cp) {
|
||||
// Check if codepoijt exist or return a static invalid one
|
||||
auto res = cpmap.find(cp);
|
||||
if (res == cpmap.end()) {
|
||||
static Codepoint invalid;
|
||||
return invalid.invalid(true);
|
||||
}
|
||||
return res->second;
|
||||
}
|
||||
|
||||
void Font::LoadSystemFont() {
|
||||
TT::Scope st("LI_SystemFont"); // Trace loading time
|
||||
sysfont = true; // Set as System Font
|
||||
fontEnsureMapped(); // Call this to be sure the font is mapped
|
||||
// Get some const references for system font loading
|
||||
const auto fnt = fontGetSystemFont();
|
||||
const auto fnt_info = fontGetInfo(fnt);
|
||||
const auto glyph_info = fontGetGlyphInfo(fnt);
|
||||
// Resize the Texture list by the num of sysfont textures
|
||||
this->textures.resize(glyph_info->nSheets + 1);
|
||||
/// Modify the Pixel Height by 1.1f to fit the
|
||||
/// Size og ttf font Rendering
|
||||
pixel_height = glyph_info->cellHeight * 1.1f;
|
||||
// Load the Textures and make sure they don't auto unload
|
||||
for (size_t i = 0; i < glyph_info->nSheets; i++) {
|
||||
auto stex = Texture::New();
|
||||
auto tx = new C3D_Tex;
|
||||
tx->data = fontGetGlyphSheetTex(fnt, i);
|
||||
tx->fmt = (GPU_TEXCOLOR)glyph_info->sheetFmt;
|
||||
tx->size = glyph_info->sheetSize;
|
||||
tx->width = glyph_info->sheetWidth;
|
||||
tx->height = glyph_info->sheetHeight;
|
||||
tx->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) |
|
||||
GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) |
|
||||
GPU_TEXTURE_WRAP_S(GPU_REPEAT) | GPU_TEXTURE_WRAP_T(GPU_REPEAT);
|
||||
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;
|
||||
// Write the Charset into a vector
|
||||
for (auto cmap = fnt_info->cmap; cmap; cmap = cmap->next) {
|
||||
if (cmap->mappingMethod == CMAP_TYPE_DIRECT) {
|
||||
if (cmap->codeEnd >= cmap->codeBegin) {
|
||||
charSet.reserve(charSet.size() + cmap->codeEnd - cmap->codeBegin + 1);
|
||||
for (auto i = cmap->codeBegin; i <= cmap->codeEnd; ++i) {
|
||||
if (cmap->indexOffset + (i - cmap->codeBegin) == 0xFFFF) break;
|
||||
charSet.emplace_back(i);
|
||||
}
|
||||
}
|
||||
} else if (cmap->mappingMethod == CMAP_TYPE_TABLE) {
|
||||
if (cmap->codeEnd >= cmap->codeBegin) {
|
||||
charSet.reserve(charSet.size() + cmap->codeEnd - cmap->codeBegin + 1);
|
||||
for (auto i = cmap->codeBegin; i <= cmap->codeEnd; ++i) {
|
||||
if (cmap->indexTable[i - cmap->codeBegin] == 0xFFFF) continue;
|
||||
charSet.emplace_back(i);
|
||||
}
|
||||
}
|
||||
} else if (cmap->mappingMethod == CMAP_TYPE_SCAN) {
|
||||
charSet.reserve(charSet.size() + cmap->nScanEntries);
|
||||
for (unsigned i = 0; i < cmap->nScanEntries; ++i) {
|
||||
if (cmap->scanEntries[i].code >= cmap->codeBegin &&
|
||||
cmap->scanEntries[i].code <= cmap->codeEnd) {
|
||||
if (cmap->scanEntries[i].glyphIndex != 0xFFFF) {
|
||||
charSet.emplace_back(cmap->scanEntries[i].code);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the charset and make sure all values are unique
|
||||
std::sort(charSet.begin(), charSet.end());
|
||||
charSet.erase(std::unique(charSet.begin(), charSet.end()));
|
||||
|
||||
// Setup the Codepoint map by the charset
|
||||
for (auto cp : charSet) {
|
||||
int gidx = fontGlyphIndexFromCodePoint(fnt, cp);
|
||||
if (gidx >= 0xFFFF) continue;
|
||||
Codepoint codepoint;
|
||||
fontGlyphPos_s dat;
|
||||
fontCalcGlyphPos(&dat, fnt, gidx, GLYPH_POS_CALC_VTXCOORD, 1.f, 1.f);
|
||||
|
||||
codepoint.cp(cp);
|
||||
codepoint.uv(vec4(dat.texcoord.left, dat.texcoord.top, dat.texcoord.right,
|
||||
dat.texcoord.bottom));
|
||||
|
||||
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);
|
||||
cpmap[cp] = codepoint;
|
||||
}
|
||||
}
|
||||
} // namespace LI
|
||||
} // namespace PD
|
||||
/*
|
||||
MIT License
|
||||
Copyright (c) 2024 - 2025 René Amthor (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.
|
||||
*/
|
||||
|
||||
#ifdef PD_LITHIUM_BUILD_SHARED
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#endif
|
||||
#include <pd/external/stb_truetype.h>
|
||||
|
||||
#include <pd/lithium/font.hpp>
|
||||
#include <pd/lithium/renderer.hpp>
|
||||
|
||||
namespace PD {
|
||||
namespace LI {
|
||||
PD_LITHIUM_API void Font::LoadTTF(const std::string &path, int height) {
|
||||
TT::Scope st("LI_LoadTTF_" + path);
|
||||
PixelHeight = height; // Set internel pixel height
|
||||
// Use NextPow2 to be able to use sizes between for example 16 and 32
|
||||
// before it only was possible to use 8, 16, 32, 64 as size
|
||||
int texszs = BitUtil::GetPow2(height * 16);
|
||||
// Load stbtt
|
||||
stbtt_fontinfo inf;
|
||||
std::ifstream loader(path, std::ios::binary);
|
||||
if (!loader.is_open()) return;
|
||||
loader.seekg(0, std::ios::end);
|
||||
size_t len = loader.tellg();
|
||||
loader.seekg(0, std::ios::beg);
|
||||
unsigned char *buffer = new unsigned char[len];
|
||||
loader.read(reinterpret_cast<char *>(buffer), len);
|
||||
loader.close();
|
||||
stbtt_InitFont(&inf, buffer, 0);
|
||||
// clang-format off
|
||||
// Disable clang here cause dont want a garbage looking line
|
||||
std::vector<PD::u8> font_tex(texszs * texszs * 4); // Create font Texture
|
||||
// clang-format on
|
||||
float scale = stbtt_ScaleForPixelHeight(&inf, PixelHeight);
|
||||
|
||||
int ascent, descent, lineGap;
|
||||
stbtt_GetFontVMetrics(&inf, &ascent, &descent, &lineGap);
|
||||
int baseline = static_cast<int>(ascent * scale);
|
||||
|
||||
std::map<u32, int> buf_cache; // Cache to not render same codepoint tex twice
|
||||
|
||||
/// Load Codepoints
|
||||
auto tex = Texture::New();
|
||||
fvec2 off;
|
||||
for (u32 ii = 0x0000; ii < 0xFFFF; ii++) {
|
||||
int i = stbtt_FindGlyphIndex(&inf, ii);
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
if (stbtt_IsGlyphEmpty(&inf, i)) {
|
||||
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);
|
||||
|
||||
// Check if Codepoint exists as hash and if it is use its already written
|
||||
// data
|
||||
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.pCodepoint = i;
|
||||
CodeMap[i] = c;
|
||||
free(bitmap);
|
||||
continue;
|
||||
} else {
|
||||
buf_cache[hashed_map] = i;
|
||||
}
|
||||
|
||||
if (off.x + w > texszs) {
|
||||
off.y += PixelHeight;
|
||||
off.x = 0;
|
||||
}
|
||||
|
||||
// Set UV Data
|
||||
fvec4 uvs;
|
||||
uvs.x = static_cast<float>(off.x) / texszs;
|
||||
uvs.y = static_cast<float>(off.y) / texszs;
|
||||
uvs.z = static_cast<float>((off.x + w) / texszs);
|
||||
uvs.w = static_cast<float>((off.y + h) / texszs);
|
||||
if (pBackend->Flags & LIBackendFlags_FlipUV_Y) {
|
||||
uvs.y = 1.f - uvs.y;
|
||||
uvs.w = 1.f - uvs.w;
|
||||
}
|
||||
c.SimpleUV = uvs;
|
||||
c.Tex = tex;
|
||||
c.Size = fvec2(w, h);
|
||||
c.Offset = baseline + yo;
|
||||
|
||||
// Render glyph
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
int map_pos = (((off.y + y) * texszs + (off.x + 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];
|
||||
}
|
||||
}
|
||||
|
||||
free(bitmap);
|
||||
CodeMap[i] = c;
|
||||
|
||||
// Small Patch to avoid some possible artifacts
|
||||
off.x += w + 1;
|
||||
if (off.x + w > texszs) {
|
||||
off.y += PixelHeight;
|
||||
if (off.y + PixelHeight > texszs) {
|
||||
break;
|
||||
}
|
||||
off.x = 0;
|
||||
}
|
||||
}
|
||||
// Load the Texture and append to list
|
||||
{
|
||||
auto t = pBackend->LoadTexture(font_tex, texszs, texszs, Texture::RGBA32,
|
||||
Texture::LINEAR);
|
||||
tex->CopyOther(t);
|
||||
}
|
||||
Textures.push_back(tex);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API Font::Codepoint &Font::GetCodepoint(u32 cp) {
|
||||
// Check if codepoijt exist or return a static invalid one
|
||||
auto res = CodeMap.find(cp);
|
||||
if (res == CodeMap.end()) {
|
||||
static Codepoint invalid;
|
||||
invalid.pInvalid = true;
|
||||
return invalid;
|
||||
}
|
||||
return res->second;
|
||||
}
|
||||
|
||||
PD_LITHIUM_API fvec2 Font::GetTextBounds(const std::string &text, float scale) {
|
||||
// Use wstring for exemple for german äöü
|
||||
auto wtext = Strings::MakeWstring(text);
|
||||
// Create a temp position and offset as [0, 0]
|
||||
fvec2 res;
|
||||
float x = 0;
|
||||
// Curent Font Scale
|
||||
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
|
||||
float lh = (float)PixelHeight * cfs;
|
||||
size_t index = 0;
|
||||
for (auto &it : wtext) {
|
||||
if (it == '\0') {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
auto cp = GetCodepoint(it);
|
||||
if (cp.pInvalid && it != '\n' && it != '\t' && it != ' ') {
|
||||
continue;
|
||||
}
|
||||
switch (it) {
|
||||
case '\n':
|
||||
res.y += lh;
|
||||
res.x = std::max(res.x, x);
|
||||
x = 0.f;
|
||||
break;
|
||||
case '\t':
|
||||
x += 16 * cfs;
|
||||
break;
|
||||
case ' ':
|
||||
x += 2 * cfs;
|
||||
// Fall trough here to get the same result as in
|
||||
// TextCommand if/else Section
|
||||
default:
|
||||
x += cp.Size.x * cfs;
|
||||
if (index != wtext.size()) {
|
||||
x += 2 * cfs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
res.x = std::max(res.x, x);
|
||||
res.y += lh;
|
||||
return res;
|
||||
}
|
||||
|
||||
PD_LITHIUM_API void Font::CmdTextEx(Vec<Command::Ref> &cmds, const fvec2 &pos,
|
||||
u32 color, float scale,
|
||||
const std::string &text, LITextFlags flags,
|
||||
const fvec2 &box) {
|
||||
fvec2 off;
|
||||
float cfs = (DefaultPixelHeight * scale) / (float)PixelHeight;
|
||||
float lh = (float)PixelHeight * cfs;
|
||||
fvec2 td;
|
||||
fvec2 rpos = pos;
|
||||
fvec2 rbox = box;
|
||||
if (flags & (LITextFlags_AlignMid | LITextFlags_AlignRight)) {
|
||||
td = GetTextBounds(text, scale);
|
||||
}
|
||||
if (flags & LITextFlags_AlignMid) {
|
||||
rpos = rbox * 0.5 - td * 0.5 + pos;
|
||||
}
|
||||
if (flags & LITextFlags_AlignRight) {
|
||||
rpos.x = rpos.x - td.x;
|
||||
}
|
||||
|
||||
std::vector<std::string> lines;
|
||||
std::istringstream iss(text);
|
||||
std::string tmp;
|
||||
while (std::getline(iss, tmp)) {
|
||||
lines.push_back(tmp);
|
||||
}
|
||||
|
||||
for (auto &it : lines) {
|
||||
/*if (flags & LITextFlags_Short) {
|
||||
fvec2 tmp_dim;
|
||||
it = ShortText(it, box.x() - pos.x(), tmp_dim);
|
||||
}*/
|
||||
auto wline = Strings::MakeWstring(it);
|
||||
auto cmd = Command::New();
|
||||
auto Tex = GetCodepoint(wline[0]).Tex;
|
||||
cmd->Tex = Tex;
|
||||
for (auto &jt : wline) {
|
||||
auto cp = GetCodepoint(jt);
|
||||
if (cp.pInvalid && jt != '\n' && jt != '\t') {
|
||||
continue;
|
||||
}
|
||||
if (Tex != cp.Tex) {
|
||||
cmds.Add(cmd);
|
||||
cmd = Command::New();
|
||||
Tex = cp.Tex;
|
||||
cmd->Tex = Tex;
|
||||
}
|
||||
if (jt == '\t') {
|
||||
off.x += 16 * cfs;
|
||||
} else {
|
||||
if (jt != ' ') {
|
||||
if (flags & LITextFlags_Shaddow) {
|
||||
// Draw
|
||||
Rect rec = Renderer::PrimRect(
|
||||
rpos + vec2(off.x + 1, off.x + (cp.Offset * cfs)) + 1,
|
||||
cp.Size * cfs, 0.f);
|
||||
Renderer::CmdQuad(cmd, rec, cp.SimpleUV, 0xff111111);
|
||||
}
|
||||
// Draw
|
||||
Rect rec = Renderer::PrimRect(
|
||||
rpos + off + fvec2(0, (cp.Offset * cfs)), cp.Size * cfs, 0.f);
|
||||
Renderer::CmdQuad(cmd, rec, cp.SimpleUV, color);
|
||||
} else {
|
||||
off.x += 2 * cfs;
|
||||
}
|
||||
off.x += cp.Size.x * cfs + 2 * cfs;
|
||||
}
|
||||
}
|
||||
cmds.Add(cmd);
|
||||
off.y += lh;
|
||||
off.x = 0;
|
||||
}
|
||||
}
|
||||
} // namespace LI
|
||||
} // namespace PD
|
||||
|
@ -1,34 +0,0 @@
|
||||
// THIS FILE WAS GENERATED BY build_shaders.py!!!
|
||||
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 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/li7_shader.hpp>
|
||||
|
||||
// clang-format off
|
||||
unsigned char li7_shader[] = {
|
||||
0x44, 0x56, 0x4c, 0x42, 0x1, 0x0, 0x0, 0x0, 0xa4, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x50, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4e, 0x1, 0xf0, 0x7, 0x4e, 0x2, 0x8, 0x2, 0x8, 0x3, 0x18, 0x2, 0x8, 0x4, 0x28, 0x2, 0x8, 0x5, 0x38, 0x2, 0x8, 0x6, 0x10, 0x40, 0x4c, 0x7, 0xf1, 0x27, 0x22, 0x8, 0x10, 0x21, 0x4c, 0x0, 0x0, 0x0, 0x88, 0xac, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa1, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x61, 0xc3, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaf, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xd5, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x56, 0x4c, 0x45, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x54, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x2, 0x0, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x1, 0x1, 0x37, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0xf, 0x0, 0x0, 0x0, 0x3, 0x0, 0x2, 0x0, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x13, 0x0, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x0,
|
||||
};
|
||||
// clang-format on
|
||||
size_t li7_shader_size = 0x124;
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
Copyright (c) 2024 - 2025 René Amthor (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/external/stb_truetype.h>
|
||||
|
||||
#include <pd/core/io.hpp>
|
||||
#include <pd/core/strings.hpp>
|
||||
#include <pd/core/sys.hpp>
|
||||
#include <pd/lithium/font.hpp>
|
||||
#include <pd/lithium/renderer.hpp>
|
||||
|
||||
namespace PD {
|
||||
namespace LI {
|
||||
void StaticText::Setup(Renderer* ren, const vec2& pos, u32 clr,
|
||||
const std::string& text, LITextFlags flags,
|
||||
const vec2& box) {
|
||||
this->tdim = ren->GetTextDimensions(text);
|
||||
this->pos = pos;
|
||||
this->ren = ren;
|
||||
this->text = StaticObject::New();
|
||||
/// Ensure that it also renders Out of Screen i guess
|
||||
ren->TextCommand(this->text->List(), pos, clr, text,
|
||||
flags | LITextFlags_RenderOOS, box);
|
||||
Renderer::OptiCommandList(this->text->List());
|
||||
// Make sure to bring the text in edit mode
|
||||
// Fixes flickering problems in ui7
|
||||
this->text->ReCopy();
|
||||
}
|
||||
|
||||
void StaticText::Draw() {
|
||||
used = true;
|
||||
for (auto& it : text->List()) {
|
||||
ren->PushCommand(it);
|
||||
}
|
||||
text->ReCopy();
|
||||
}
|
||||
|
||||
void StaticText::SetColor(u32 col) { text->ReColor(col); }
|
||||
void StaticText::SetPos(const vec2& pos) { text->MoveIt(pos - this->pos); }
|
||||
|
||||
void StaticText::SetLayer(int layer) { text->ReLayer(layer); }
|
||||
} // namespace LI
|
||||
} // namespace PD
|
@ -1,601 +1,152 @@
|
||||
/*
|
||||
MIT License
|
||||
Copyright (c) 2024 - 2025 René Amthor (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 <3ds.h>
|
||||
#include <pd/external/stb_truetype.h>
|
||||
|
||||
#include <pd/core/io.hpp>
|
||||
#include <pd/core/strings.hpp>
|
||||
#include <pd/core/sys.hpp>
|
||||
#include <pd/lithium/font.hpp>
|
||||
#include <pd/lithium/li7_shader.hpp>
|
||||
#include <pd/lithium/renderer.hpp>
|
||||
|
||||
namespace PD {
|
||||
namespace LI {
|
||||
Renderer::Renderer(LIRenderFlags flags) {
|
||||
vertex_buf.resize(4 * 4096, Vertex());
|
||||
index_buf.resize(6 * 4096, 0);
|
||||
|
||||
/// Use 3ds u32 here
|
||||
dvlb = DVLB_ParseFile((uint32_t*)li7_shader, li7_shader_size);
|
||||
shaderProgramInit(&shader);
|
||||
shaderProgramSetVsh(&shader, &dvlb->DVLE[0]);
|
||||
uLoc_projection =
|
||||
shaderInstanceGetUniformLocation(shader.vertexShader, "projection");
|
||||
|
||||
AttrInfo_Init(&attr);
|
||||
AttrInfo_AddLoader(&attr, 0, GPU_FLOAT, 2);
|
||||
AttrInfo_AddLoader(&attr, 1, GPU_FLOAT, 2);
|
||||
AttrInfo_AddLoader(&attr, 2, GPU_UNSIGNED_BYTE, 4);
|
||||
|
||||
// Precalculate Projection (Never changes)
|
||||
Mtx_OrthoTilt(&top_proj, 0.f, 400.f, 240.f, 0.f, 1.f, -1.f, false);
|
||||
Mtx_OrthoTilt(&bot_proj, 0.f, 320.f, 240.f, 0.f, 1.f, -1.f, false);
|
||||
|
||||
std::vector<u8> pixels(16 * 16 * 4, 255);
|
||||
white = Texture::New(pixels, 16, 16);
|
||||
UseTex(white);
|
||||
|
||||
// Not Loading as Systemfont is freezing
|
||||
// font = Font::New();
|
||||
// font->LoadSystemFont();
|
||||
}
|
||||
Renderer::~Renderer() {
|
||||
shaderProgramFree(&shader);
|
||||
DVLB_Free(dvlb);
|
||||
}
|
||||
|
||||
bool Renderer::InBox(const vec2& pos, const vec2& szs, const vec4& rect) {
|
||||
return (pos[0] + szs[0] >= rect[0] && pos[1] + szs[1] >= rect[1] &&
|
||||
pos[0] <= rect[2] && pos[1] <= rect[3]);
|
||||
}
|
||||
|
||||
bool Renderer::InBox(const vec2& pos, const vec4& rect) {
|
||||
return (pos.x() > rect.x() && pos.x() < rect.x() + rect.z() &&
|
||||
pos.y() > rect.y() && pos.y() < rect.y() + rect.w());
|
||||
}
|
||||
|
||||
bool Renderer::InBox(const vec2& alpha, const vec2& bravo, const vec2& charlie,
|
||||
const vec4& rect) {
|
||||
return ((alpha[0] < rect[2] && bravo[0] < rect[2] && charlie[0] < rect[2]) ||
|
||||
(alpha[1] < rect[3] && bravo[1] < rect[3] && charlie[1] < rect[3]) ||
|
||||
(alpha[0] > 0 && bravo[0] > 0 && charlie[0] > 0) ||
|
||||
(alpha[1] > 0 && bravo[1] > 0 && charlie[1] > 0));
|
||||
}
|
||||
|
||||
void Renderer::RotateCorner(vec2& v, float s, float c) {
|
||||
float x = v[0] * c - v[1] * s;
|
||||
float y = v[1] * c + v[0] * s;
|
||||
v = vec2(x, y);
|
||||
}
|
||||
|
||||
Rect Renderer::CreateRect(const vec2& pos, const vec2& size, float angle) {
|
||||
vec2 c = size * 0.5f; // Center
|
||||
vec2 corner[4] = {
|
||||
vec2(-c[0], -c[1]),
|
||||
vec2(-c[0] + size[0], -c[1]),
|
||||
vec2(-c[0], -c[1] + size[1]),
|
||||
vec2(-c[0] + size[0], -c[1] + size[1]),
|
||||
};
|
||||
|
||||
// Only rotate if required
|
||||
if (angle != 0.f) {
|
||||
float s = std::sin(angle);
|
||||
float co = std::cos(angle);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
RotateCorner(corner[i], s, co);
|
||||
}
|
||||
}
|
||||
|
||||
// Return Result
|
||||
return Rect(corner[0] + pos + c, corner[1] + pos + c, corner[2] + pos + c,
|
||||
corner[3] + pos + c);
|
||||
}
|
||||
|
||||
Rect Renderer::CreateLine(const vec2& a, const vec2& b, int t) {
|
||||
// Usin g th evec maths api makes the code as short as it is
|
||||
vec2 dir = a - b;
|
||||
float len = dir.len();
|
||||
vec2 unit_dir = dir / len;
|
||||
vec2 perpendicular(-unit_dir.y(), unit_dir.x());
|
||||
vec2 off = perpendicular * ((float)t * 0.5f);
|
||||
|
||||
return Rect(a + off, b + off, a - off, b - off);
|
||||
}
|
||||
|
||||
void Renderer::OptiCommandList(std::vector<Command::Ref>& list) {
|
||||
std::sort(list.begin(), list.end(), [](Command::Ref a, Command::Ref b) {
|
||||
if (a->Layer() == b->Layer()) {
|
||||
if (a->Tex() == b->Tex()) {
|
||||
return a->Index() < b->Index();
|
||||
}
|
||||
return a->Tex() < b->Tex(); // else
|
||||
}
|
||||
return a->Layer() < b->Layer(); // else
|
||||
});
|
||||
}
|
||||
|
||||
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 Rect& uv,
|
||||
u32 col) {
|
||||
cmd->PushIndex(0).PushIndex(1).PushIndex(2);
|
||||
cmd->PushIndex(0).PushIndex(2).PushIndex(3);
|
||||
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,
|
||||
const vec2& c, u32 col) {
|
||||
cmd->PushIndex(2).PushIndex(1).PushIndex(0);
|
||||
cmd->PushVertex(Vertex(a, vec2(0.f, 1.f), col));
|
||||
cmd->PushVertex(Vertex(b, vec2(1.f, 1.f), col));
|
||||
cmd->PushVertex(Vertex(c, vec2(1.f, 0.f), col));
|
||||
}
|
||||
|
||||
void Renderer::TextCommand(std::vector<Command::Ref>& cmds, const vec2& pos,
|
||||
u32 color, const std::string& text,
|
||||
LITextFlags flags, const vec2& box) {
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
vec2 off;
|
||||
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] = rpos[0] - td[0];
|
||||
}
|
||||
|
||||
std::vector<std::string> lines;
|
||||
std::istringstream iss(text);
|
||||
std::string tmp;
|
||||
while (std::getline(iss, tmp)) {
|
||||
lines.push_back(tmp);
|
||||
}
|
||||
|
||||
for (auto& it : lines) {
|
||||
if (flags & LITextFlags_Short) {
|
||||
vec2 tmp_dim;
|
||||
it = ShortText(it, box.x() - pos.x(), tmp_dim);
|
||||
}
|
||||
/// Well support OOS Rendering here as well
|
||||
/// Fixes UI7 Scroll back up bug
|
||||
if (rpos[1] + off[1] + lh < 0 && !(flags & LITextFlags_RenderOOS)) {
|
||||
off[1] += lh;
|
||||
continue;
|
||||
} else if (rpos[1] + off[1] > GetViewport().w() &&
|
||||
!(flags & LITextFlags_RenderOOS)) {
|
||||
// Break cause next lines would be out of screen
|
||||
break;
|
||||
}
|
||||
auto wline = Strings::MakeWstring(it);
|
||||
auto cmd = Command::New();
|
||||
current_tex = font->GetCodepoint(wline[0]).tex();
|
||||
SetupCommand(cmd);
|
||||
cmd->Rendermode(RenderMode_Font);
|
||||
for (auto& jt : wline) {
|
||||
auto cp = font->GetCodepoint(jt);
|
||||
if (cp.invalid() && jt != '\n' && jt != '\t') {
|
||||
continue;
|
||||
}
|
||||
if (current_tex != cp.tex()) {
|
||||
cmds.push_back(cmd);
|
||||
cmd = Command::New();
|
||||
current_tex = cp.tex();
|
||||
SetupCommand(cmd);
|
||||
cmd->Rendermode(RenderMode_Font);
|
||||
}
|
||||
if (jt == '\t') {
|
||||
off[0] += 16 * cfs;
|
||||
} else {
|
||||
if (jt != ' ') {
|
||||
int lr = current_layer;
|
||||
if (flags & LITextFlags_Shaddow) {
|
||||
// Draw
|
||||
Rect rec = CreateRect(
|
||||
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(rpos + off + vec2(0, (cp.off() * cfs)),
|
||||
cp.size() * cfs, 0.f);
|
||||
QuadCommand(cmd, rec, cp.uv(), color);
|
||||
current_layer = lr;
|
||||
} else {
|
||||
if (!font->SystemFont()) {
|
||||
off[0] += 2 * cfs;
|
||||
}
|
||||
}
|
||||
off[0] += cp.size().x() * cfs + 2 * cfs;
|
||||
}
|
||||
}
|
||||
cmds.push_back(cmd);
|
||||
off[1] += lh;
|
||||
off[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vec4 Renderer::GetViewport() { return vec4(vec2(), screen->GetSize()); }
|
||||
|
||||
std::string Renderer::ShortText(const std::string& text, int maxlen,
|
||||
vec2& newsize) {
|
||||
vec2 cdim;
|
||||
if (flags & LIRenderFlags_TMS) {
|
||||
auto e = tms.find(text);
|
||||
if (e != tms.end()) {
|
||||
e->second.TimeCreated(Sys::GetTime());
|
||||
if (e->second.Optional()) {
|
||||
return e->second.Text();
|
||||
}
|
||||
cdim = e->second.Size();
|
||||
}
|
||||
}
|
||||
cdim = this->GetTextDimensions(text);
|
||||
if (cdim[0] < (float)maxlen) {
|
||||
return text;
|
||||
}
|
||||
std::string ext;
|
||||
/// Forgot why i called this var ending cause
|
||||
/// Its more a placeholder for removed content
|
||||
std::string ending = "...";
|
||||
std::string cpy = text;
|
||||
std::string res;
|
||||
size_t extension = text.find_last_of('.');
|
||||
if (extension != text.npos) {
|
||||
ext = text.substr(extension);
|
||||
cpy = text.substr(0, extension);
|
||||
maxlen -= GetTextDimensions(ext).x();
|
||||
}
|
||||
maxlen -= GetTextDimensions(ending).x();
|
||||
for (auto& it : cpy) {
|
||||
if (GetTextDimensions(res).x() > (float)maxlen) {
|
||||
res += ending;
|
||||
res += ext;
|
||||
newsize = GetTextDimensions(res);
|
||||
if (flags & LIRenderFlags_TMS) {
|
||||
auto& tmp = tms[text];
|
||||
tmp.Text(res);
|
||||
tmp.Size(newsize);
|
||||
tmp.TimeCreated(Sys::GetTime());
|
||||
tmp.Optional(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
res += it;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Renderer::WrapText(const std::string& text, int maxlen,
|
||||
vec2& newsize) {}
|
||||
|
||||
vec2 Renderer::GetTextDimensions(const std::string& text) {
|
||||
if (!font) {
|
||||
// No font no size (oder so)
|
||||
return vec2();
|
||||
}
|
||||
// Handle TextMapSystem
|
||||
if (flags & LIRenderFlags_TMS) {
|
||||
auto ref = tms.find(text);
|
||||
if (ref != tms.end()) {
|
||||
ref->second.TimeCreated(Sys::GetTime());
|
||||
return ref->second.Size();
|
||||
}
|
||||
}
|
||||
// Use wstring for exemple for german äöü
|
||||
auto wtext = Strings::MakeWstring(text);
|
||||
// Create a temp position and offset as [0, 0]
|
||||
vec2 res;
|
||||
float x = 0;
|
||||
// Curent Font Scale
|
||||
float cfs = (default_font_h * text_size) / (float)font->PixelHeight();
|
||||
float lh = (float)font->PixelHeight() * cfs;
|
||||
size_t index = 0;
|
||||
for (auto& it : wtext) {
|
||||
if (it == '\0') {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
auto cp = font->GetCodepoint(it);
|
||||
if (cp.invalid() && it != '\n' && it != '\t' && it != ' ') {
|
||||
continue;
|
||||
}
|
||||
switch (it) {
|
||||
case '\n':
|
||||
res[1] += lh;
|
||||
res[0] = std::max(res[0], x);
|
||||
x = 0.f;
|
||||
break;
|
||||
case '\t':
|
||||
x += 16 * cfs;
|
||||
break;
|
||||
case ' ':
|
||||
if (!font->SystemFont()) {
|
||||
x += 2 * cfs;
|
||||
}
|
||||
// Fall trough here to get the same result as in
|
||||
// TextCommand if/else Section
|
||||
default:
|
||||
x += cp.size().x() * cfs;
|
||||
if (index != wtext.size()) {
|
||||
x += 2 * cfs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
res[0] = std::max(res[0], x);
|
||||
res[1] += lh;
|
||||
if (flags & LIRenderFlags_TMS) {
|
||||
tms[text] = TextBox(res, Sys::GetTime());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void Renderer::UpdateRenderMode(const RenderMode& mode) {
|
||||
C3D_TexEnv* env = C3D_GetTexEnv(0);
|
||||
switch (mode) {
|
||||
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);
|
||||
C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0);
|
||||
C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE);
|
||||
break;
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::PrepareRender() {
|
||||
if (font_update) {
|
||||
tms.clear();
|
||||
font_update = false;
|
||||
}
|
||||
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
|
||||
TT::Beg("LI_RenderAll");
|
||||
vertex_idx = 0;
|
||||
index_idx = 0;
|
||||
vertices = 0;
|
||||
indices = 0;
|
||||
commands = 0;
|
||||
drawcalls = 0;
|
||||
C3D_BindProgram(&shader);
|
||||
C3D_SetAttrInfo(&attr);
|
||||
}
|
||||
|
||||
void Renderer::FinalizeRender() {
|
||||
C3D_FrameEnd(0);
|
||||
TT::End("LI_RenderAll");
|
||||
current_layer = 0;
|
||||
cmd_idx = 0;
|
||||
rot = 0.f;
|
||||
UseTex();
|
||||
if (flags & LIRenderFlags_TMS) {
|
||||
std::vector<std::string> rem;
|
||||
for (auto& it : tms) {
|
||||
if (Sys::GetTime() - it.second.TimeCreated() > 5) rem.push_back(it.first);
|
||||
}
|
||||
for (auto it : rem) tms.erase(it);
|
||||
} else {
|
||||
tms.clear();
|
||||
}
|
||||
if (flags & LIRenderFlags_AST) {
|
||||
std::vector<u32> rem;
|
||||
for (auto it : ast) {
|
||||
if (!it.second->Used()) {
|
||||
rem.push_back(it.first);
|
||||
}
|
||||
it.second->SetUnused();
|
||||
}
|
||||
for (auto& it : rem) {
|
||||
ast.erase(it);
|
||||
}
|
||||
} else {
|
||||
ast.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::Render(Screen::Ref s) {
|
||||
Assert(s.get(), "Expected Screen Address but got nullptr!");
|
||||
s->Clear();
|
||||
s->Use();
|
||||
bool bot = s->ScreenType() == Screen::Bottom;
|
||||
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;
|
||||
auto& cmds = draw_list[Screen32(s)];
|
||||
commands += cmds.size();
|
||||
size_t index = 0;
|
||||
if (flags & LIRenderFlags_LRS) {
|
||||
OptiCommandList(cmds);
|
||||
}
|
||||
while (index < cmds.size()) {
|
||||
C3D_Tex* tex = cmds[index]->Tex()->GetTex();
|
||||
auto mode = cmds[index]->Rendermode();
|
||||
auto smode = cmds[index]->GetScissorMode();
|
||||
auto spos = cmds[index]->ScissorRect();
|
||||
C3D_SetScissor((GPU_SCISSORMODE)smode, s->GetSize().y() - spos.w(),
|
||||
s->GetSize().x() - spos.z(), s->GetSize().y() - spos.y(),
|
||||
s->GetSize().x() - spos.x());
|
||||
UpdateRenderMode(mode);
|
||||
u32 start_vtx = vertex_idx;
|
||||
u32 start_idx = index_idx;
|
||||
while (index < cmds.size() && cmds[index]->Tex()->GetTex() == tex &&
|
||||
cmds[index]->Rendermode() == mode &&
|
||||
cmds[index]->GetScissorMode() == smode) {
|
||||
auto c = cmds[index];
|
||||
// Indices
|
||||
for (size_t i = 0; i < c->IndexList().size(); i++) {
|
||||
index_buf[index_idx++] = vertex_idx + c->IndexList().at(i);
|
||||
}
|
||||
// Vertices
|
||||
for (size_t i = 0; i < c->VertexList().size(); i++) {
|
||||
vertex_buf[vertex_idx++] = c->VertexList().at(i);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
C3D_TexBind(0, tex);
|
||||
|
||||
auto bufInfo = C3D_GetBufInfo();
|
||||
BufInfo_Init(bufInfo);
|
||||
BufInfo_Add(bufInfo, vertex_buf.data(), sizeof(Vertex), 3, 0x210);
|
||||
|
||||
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;
|
||||
}
|
||||
cmds.clear();
|
||||
C3D_DepthTest(true, GPU_GREATER, GPU_WRITE_ALL);
|
||||
vertices += total_vertices;
|
||||
indices += total_indices;
|
||||
}
|
||||
|
||||
void Renderer::DrawRect(const vec2& pos, const vec2& size, u32 color,
|
||||
const Rect& uv) {
|
||||
if (!InBox(pos, size, GetViewport())) {
|
||||
// Instand abort as it is out of screen
|
||||
return;
|
||||
}
|
||||
Rect rec = CreateRect(pos, size, rot);
|
||||
auto cmd = Command::New();
|
||||
SetupCommand(cmd);
|
||||
QuadCommand(cmd, rec, uv, color);
|
||||
draw_list[Screen32(screen)].push_back(cmd);
|
||||
}
|
||||
|
||||
void Renderer::DrawRectSolid(const vec2& pos, const vec2& size, u32 color) {
|
||||
UseTex();
|
||||
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,
|
||||
u32 color) {
|
||||
if (!InBox(a, b, c, GetViewport())) {
|
||||
return;
|
||||
}
|
||||
UseTex();
|
||||
auto cmd = Command::New();
|
||||
SetupCommand(cmd);
|
||||
TriangleCommand(cmd, a, b, c, color);
|
||||
draw_list[Screen32(screen)].push_back(cmd);
|
||||
}
|
||||
|
||||
void Renderer::DrawCircle(const vec2& center_pos, float r, u32 color,
|
||||
int segments) {
|
||||
if (segments < 3) {
|
||||
return;
|
||||
}
|
||||
auto cmd = Command::New();
|
||||
cmd->Index(cmd_idx++).Layer(current_layer).Tex(current_tex);
|
||||
for (int i = 1; i < segments - 1; i++) {
|
||||
cmd->PushIndex(0);
|
||||
cmd->PushIndex(i + 1).PushIndex(i);
|
||||
}
|
||||
float as = 2.f * M_PI / segments;
|
||||
for (int i = 0; i < segments; i++) {
|
||||
float a = i * as;
|
||||
float x = center_pos.x() + r * std::cos(a);
|
||||
float y = center_pos.y() + r * std::sin(a);
|
||||
cmd->PushVertex(Vertex(
|
||||
vec2(x, y), vec2((std::cos(a) + 1.f) / 2.f, (std::sin(a) + 1.f) / 2.f),
|
||||
color));
|
||||
}
|
||||
draw_list[Screen32(screen)].push_back(cmd);
|
||||
}
|
||||
|
||||
void Renderer::DrawLine(const vec2& a, const vec2& b, u32 color, int t) {
|
||||
UseTex();
|
||||
Rect line = CreateLine(a, b, t);
|
||||
|
||||
auto cmd = Command::New();
|
||||
SetupCommand(cmd);
|
||||
QuadCommand(cmd, line, vec4(0.f, 1.f, 1.f, 0.f), color);
|
||||
draw_list[Screen32(screen)].push_back(cmd);
|
||||
}
|
||||
|
||||
void Renderer::DrawImage(const vec2& pos, Texture::Ref tex, const vec2& scale) {
|
||||
UseTex(tex);
|
||||
DrawRect(pos, tex->GetSize() * scale, 0xffffffff, tex->GetUV());
|
||||
}
|
||||
|
||||
void Renderer::DrawText(const vec2& pos, u32 color, const std::string& text,
|
||||
u32 flags, const vec2& ap) {
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
if (this->flags & LIRenderFlags_AST) {
|
||||
u32 id = Strings::FastHash(text);
|
||||
auto e = ast.find(id);
|
||||
if (e == ast.end()) {
|
||||
ast[id] = StaticText::New();
|
||||
e = ast.find(id);
|
||||
}
|
||||
if (!e->second->IsSetup() || e->second->Font() != font) {
|
||||
e->second->Setup(this, pos, color, text, flags, ap);
|
||||
e->second->Font(font);
|
||||
}
|
||||
e->second->SetPos(pos);
|
||||
e->second->SetColor(color);
|
||||
e->second->Draw();
|
||||
return;
|
||||
}
|
||||
TextCommand(draw_list[Screen32(screen)], pos, color, text, flags, ap);
|
||||
}
|
||||
} // namespace LI
|
||||
#include <pd/lithium/renderer.hpp>
|
||||
|
||||
namespace PD {
|
||||
namespace LI {
|
||||
PD_LITHIUM_API Renderer::Renderer(Backend::Ref backend) {
|
||||
pBackend = backend;
|
||||
std::vector<PD::u8> white(16 * 16 * 4, 0xff);
|
||||
WhitePixel = pBackend->LoadTexture(white, 16, 16);
|
||||
CurrentTex = WhitePixel; // Make sure to have a texture set
|
||||
}
|
||||
|
||||
PD_LITHIUM_API void Renderer::Render() {
|
||||
pBackend->NewFrame();
|
||||
pBackend->RenderDrawData(DrawList);
|
||||
DrawList.Clear();
|
||||
for (auto it = pDrawLists.Begin(); it != pDrawLists.End(); it++) {
|
||||
pBackend->RenderDrawData((*it)->pDrawList);
|
||||
(*it)->Clear();
|
||||
}
|
||||
pDrawLists.Clear();
|
||||
}
|
||||
|
||||
PD_LITHIUM_API bool Renderer::InBox(const fvec2& pos, const fvec2& szs,
|
||||
const fvec4& rect) {
|
||||
return (pos.x + szs.x >= rect.x && pos.y + szs.y >= rect.y &&
|
||||
pos.x <= rect.z && pos.y <= rect.w);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API bool Renderer::InBox(const fvec2& pos, const fvec4& rect) {
|
||||
return (pos.x > rect.x && pos.x < rect.x + rect.z && pos.y > rect.y &&
|
||||
pos.y < rect.y + rect.w);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API bool Renderer::InBox(const fvec2& alpha, const fvec2& bravo,
|
||||
const fvec2& charlie, const fvec4& rect) {
|
||||
return ((alpha.x < rect.z && bravo.x < rect.z && charlie.x < rect.z) ||
|
||||
(alpha.y < rect.w && bravo.y < rect.w && charlie.y < rect.w) ||
|
||||
(alpha.x > 0 && bravo.x > 0 && charlie.x > 0) ||
|
||||
(alpha.y > 0 && bravo.y > 0 && charlie.y > 0));
|
||||
}
|
||||
|
||||
PD_LITHIUM_API void Renderer::RotateCorner(fvec2& pos, float sinus,
|
||||
float cosinus) {
|
||||
float x = pos.x * cosinus - pos.y * sinus;
|
||||
float y = pos.y * cosinus - pos.x * sinus;
|
||||
pos = fvec2(x, y);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API Rect Renderer::PrimRect(const fvec2& pos, const fvec2& size,
|
||||
float angle) {
|
||||
fvec2 c = size * 0.5f; // Center
|
||||
fvec2 corner[4] = {
|
||||
fvec2(-c.x, -c.y),
|
||||
fvec2(-c.x + size.x, -c.y),
|
||||
fvec2(-c.x, -c.y + size.y),
|
||||
fvec2(-c.x + size.x, -c.y + size.y),
|
||||
};
|
||||
|
||||
// Only rotate if required
|
||||
if (angle != 0.f) {
|
||||
float s = std::sin(angle);
|
||||
float co = std::cos(angle);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
RotateCorner(corner[i], s, co);
|
||||
}
|
||||
}
|
||||
|
||||
// Return Result
|
||||
return Rect(corner[0] + pos + c, corner[1] + pos + c, corner[2] + pos + c,
|
||||
corner[3] + pos + c);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API Rect Renderer::PrimLine(const fvec2& a, const fvec2& b,
|
||||
int thickness) {
|
||||
// Using the vec maths api makes the code as short as it is
|
||||
vec2 dir = a - b;
|
||||
float len = dir.Len();
|
||||
vec2 unit_dir = dir / len;
|
||||
vec2 perpendicular(-unit_dir.y, unit_dir.x);
|
||||
vec2 off = perpendicular * ((float)thickness * 0.5f);
|
||||
|
||||
return Rect(a + off, b + off, a - off, b - off);
|
||||
}
|
||||
|
||||
PD_LITHIUM_API void Renderer::CmdQuad(Command::Ref cmd, const Rect& quad,
|
||||
const Rect& uv, u32 color) {
|
||||
cmd->AppendIndex(0).AppendIndex(1).AppendIndex(2);
|
||||
cmd->AppendIndex(0).AppendIndex(2).AppendIndex(3);
|
||||
cmd->AppendVertex(Vertex(quad.BotRight(), uv.BotRight(), color));
|
||||
cmd->AppendVertex(Vertex(quad.TopRight(), uv.TopRight(), color));
|
||||
cmd->AppendVertex(Vertex(quad.TopLeft(), uv.TopLeft(), color));
|
||||
cmd->AppendVertex(Vertex(quad.BotLeft(), uv.BotLeft(), color));
|
||||
}
|
||||
|
||||
PD_LITHIUM_API void Renderer::CmdTriangle(Command::Ref cmd, const fvec2 a,
|
||||
const fvec2 b, const fvec2 c,
|
||||
u32 clr) {
|
||||
cmd->AppendIndex(2).AppendIndex(1).AppendIndex(0);
|
||||
cmd->AppendVertex(Vertex(a, vec2(0.f, 1.f), clr));
|
||||
cmd->AppendVertex(Vertex(b, vec2(1.f, 1.f), clr));
|
||||
cmd->AppendVertex(Vertex(c, vec2(1.f, 0.f), clr));
|
||||
}
|
||||
|
||||
PD_LITHIUM_API Command::Ref Renderer::PreGenerateCmd() {
|
||||
Command::Ref res = Command::New();
|
||||
res->Index = DrawList.Size();
|
||||
res->Layer = Layer;
|
||||
res->Tex = CurrentTex;
|
||||
return res;
|
||||
}
|
||||
|
||||
// TODO: Don't render OOS (Probably make it with a define as it
|
||||
// would probably be faster to render out of screen than checking if
|
||||
// it could be skipped)
|
||||
PD_LITHIUM_API void Renderer::CmdConvexPolyFilled(Command::Ref cmd,
|
||||
const Vec<fvec2>& points,
|
||||
u32 clr, Texture::Ref tex) {
|
||||
if (points.Size() < 3 || tex == nullptr) {
|
||||
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 (auto it = points.Begin(); it != points.End(); it++) {
|
||||
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 = tex->UV.TopLeft();
|
||||
auto uv_tr = tex->UV.TopRight();
|
||||
auto uv_bl = tex->UV.BotLeft();
|
||||
|
||||
// Render
|
||||
for (int i = 2; i < (int)points.Size(); i++) {
|
||||
cmd->AppendIndex(0).AppendIndex(i).AppendIndex(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->AppendVertex(LI::Vertex(points[i], fvec2(u, v), clr));
|
||||
}
|
||||
}
|
||||
} // namespace LI
|
||||
} // namespace PD
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
Copyright (c) 2024 - 2025 René Amthor (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/core/io.hpp>
|
||||
#include <pd/app/error.hpp>
|
||||
#include <pd/lithium/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
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
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 <3ds.h>
|
||||
#include <pd/external/stb_image.h>
|
||||
#include <tex3ds.h>
|
||||
|
||||
#include <pd/app/error.hpp>
|
||||
#include <pd/core/bit_util.hpp>
|
||||
#include <pd/core/io.hpp>
|
||||
#include <pd/core/timetrace.hpp>
|
||||
#include <pd/image/image.hpp>
|
||||
#include <pd/image/img_convert.hpp>
|
||||
#include <pd/lithium/texture.hpp>
|
||||
|
||||
namespace PD {
|
||||
GPU_TEXCOLOR GetTexFmt(Texture::Type type) {
|
||||
if (type == Texture::RGBA32)
|
||||
return GPU_RGBA8;
|
||||
else if (type == Texture::RGB24)
|
||||
return GPU_RGB8;
|
||||
else if (type == Texture::A8)
|
||||
return GPU_A8;
|
||||
return GPU_RGBA8; // Default
|
||||
}
|
||||
int GetBPP(Texture::Type type) {
|
||||
if (type == Texture::RGBA32)
|
||||
return 4;
|
||||
else if (type == Texture::RGB24)
|
||||
return 3;
|
||||
else if (type == Texture::A8)
|
||||
return 1;
|
||||
return 0; // Error
|
||||
}
|
||||
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);
|
||||
vec2 tex_size(w, h);
|
||||
// Pow2
|
||||
if (!PD::BitUtil::IsSingleBit(w)) {
|
||||
tex_size.x() = PD::BitUtil::GetPow2((unsigned int)w);
|
||||
}
|
||||
if (!PD::BitUtil::IsSingleBit(h)) {
|
||||
tex_size.y() = PD::BitUtil::GetPow2((unsigned int)h);
|
||||
}
|
||||
|
||||
this->size.x() = (u16)w;
|
||||
this->size.y() = (u16)h;
|
||||
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);
|
||||
auto tex_fmt = GetTexFmt(type);
|
||||
tex = new C3D_Tex;
|
||||
C3D_TexInit(tex, (u16)tex_size.x(), (u16)tex_size.y(), tex_fmt);
|
||||
C3D_TexSetFilter(tex, fltr, fltr);
|
||||
|
||||
memset(tex->data, 0, tex->size);
|
||||
|
||||
/// 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) +
|
||||
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) |
|
||||
((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3))) *
|
||||
bpp;
|
||||
int src_pos = (y * w + x) * 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);
|
||||
}
|
||||
|
||||
tex->border = 0x00000000;
|
||||
C3D_TexSetWrap(tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER);
|
||||
}
|
||||
|
||||
void Texture::Delete() {
|
||||
if (tex) {
|
||||
C3D_TexDelete(tex);
|
||||
delete tex;
|
||||
tex = nullptr;
|
||||
size = vec2();
|
||||
uv = vec4(0.f, 1.f, 1.f, 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::LoadFile(const std::string& path) {
|
||||
PD::TT::Scope st("texldr-" + path);
|
||||
Delete();
|
||||
PD::Image img(path);
|
||||
PD::Assert(img.GetBuffer().size(), "Unable to load image: " + path);
|
||||
if (img.Width() > 1024 || img.Height() > 1024) {
|
||||
PD::Error("Width or heigt is > 1024");
|
||||
return;
|
||||
}
|
||||
MakeTex(img, img.Width(), img.Height());
|
||||
}
|
||||
|
||||
void Texture::LoadMemory(const std::vector<u8>& data) {
|
||||
Delete();
|
||||
PD::Image img(data);
|
||||
PD::Assert(img.GetBuffer().size(), "Unable to load image from Memory!");
|
||||
if (img.Width() > 1024 || img.Height() > 1024) {
|
||||
PD::Error("Width or heigt is > 1024");
|
||||
return;
|
||||
}
|
||||
MakeTex(img, img.Width(), img.Height());
|
||||
}
|
||||
|
||||
void Texture::LoadPixels(const std::vector<u8>& pixels, int w, int h, Type type,
|
||||
Filter filter) {
|
||||
Delete();
|
||||
int bpp = GetBPP(type);
|
||||
if (w * h * bpp != (int)pixels.size()) {
|
||||
return;
|
||||
}
|
||||
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
|
Reference in New Issue
Block a user