diff --git a/WickedEngine/wiApplication.cpp b/WickedEngine/wiApplication.cpp index 72209eddc..c6786cff9 100644 --- a/WickedEngine/wiApplication.cpp +++ b/WickedEngine/wiApplication.cpp @@ -86,25 +86,13 @@ namespace wi // Until engine is not loaded, present initialization screen... CommandList cmd = graphicsDevice->BeginCommandList(); graphicsDevice->RenderPassBegin(&swapChain, cmd); - wi::image::SetCanvas(canvas); Viewport viewport; viewport.width = (float)swapChain.desc.width; viewport.height = (float)swapChain.desc.height; graphicsDevice->BindViewports(1, &viewport, cmd); if (wi::initializer::IsInitializeFinished(wi::initializer::INITIALIZED_SYSTEM_FONT)) { - wi::font::SetCanvas(canvas); - wi::font::Params params; - params.posX = 5.f; - params.posY = 5.f; - std::string text = wi::backlog::getText(); - float textheight = wi::font::TextHeight(text, params); - float screenheight = canvas.GetLogicalHeight(); - if (textheight > screenheight) - { - params.posY = screenheight - textheight; - } - wi::font::Draw(text, params, cmd); + wi::backlog::DrawOutputText(canvas, cmd); } graphicsDevice->RenderPassEnd(cmd); graphicsDevice->SubmitCommandLists(); diff --git a/WickedEngine/wiBacklog.cpp b/WickedEngine/wiBacklog.cpp index 82e851235..780f36111 100644 --- a/WickedEngine/wiBacklog.cpp +++ b/WickedEngine/wiBacklog.cpp @@ -31,7 +31,7 @@ namespace wi::backlog std::deque history; const float speed = 4000.0f; const size_t deletefromline = 500; - float pos = std::numeric_limits::lowest(); + float pos = 5; float scroll = 0; std::string inputArea; int historyPos = 0; @@ -44,6 +44,15 @@ namespace wi::backlog bool blockLuaExec = false; LogLevel logLevel = LogLevel::Default; + std::string getTextWithoutLock() + { + std::string retval; + for (auto& x : entries) + { + retval += x.text; + } + return retval; + } void write_logfile() { std::string filename = wi::helper::GetTempDirectoryPath() + "wiBacklog.txt"; @@ -142,46 +151,15 @@ namespace wi::backlog params.shadow_softness = 1; wi::font::Draw(inputArea, params, cmd); - font_params.cursor = XMFLOAT2(0, 0); - params.shadowColor = 0x10000000u; - params.shadow_offset_x = 2; - params.shadow_offset_y = 2; - params.shadow_softness = 1; - if (refitscroll) - { - refitscroll = false; - float textheight = wi::font::TextHeight(getText(), font_params); - float limit = canvas.GetLogicalHeight() * 0.9f; - if (scroll + textheight > limit) - { - scroll = limit - textheight; - } - } - font_params.posX = 50; - font_params.posY = pos + scroll; - font_params.h_wrap = canvas.GetLogicalWidth() - font_params.posX; Rect rect; rect.left = 0; rect.right = (int32_t)canvas.GetPhysicalWidth(); rect.top = 0; rect.bottom = int32_t(canvas.LogicalToPhysical(canvas.GetLogicalHeight() - 35)); wi::graphics::GetDevice()->BindScissorRects(1, &rect, cmd); - for (auto& x : entries) - { - switch (x.level) - { - case LogLevel::Warning: - font_params.color = 0xFF66FFFF; // light yellow - break; - case LogLevel::Error: - font_params.color = 0xFF6666FF; // light red - break; - default: - font_params.color = wi::Color::White(); - break; - } - font_params.cursor = wi::font::Draw(x.text, font_params, cmd); - } + + DrawOutputText(canvas, cmd); + rect.left = -std::numeric_limits::max(); rect.right = std::numeric_limits::max(); rect.top = -std::numeric_limits::max(); @@ -190,16 +168,46 @@ namespace wi::backlog } } + void DrawOutputText(const wi::Canvas& canvas, wi::graphics::CommandList cmd) + { + std::scoped_lock lock(logLock); + wi::font::SetCanvas(canvas); // always set here as it can be called from outside... + font_params.cursor = {}; + if (refitscroll) + { + float textheight = wi::font::TextHeight(getTextWithoutLock(), font_params); + float limit = canvas.GetLogicalHeight() - 35; + if (scroll + textheight > limit) + { + scroll = limit - textheight; + } + refitscroll = false; + } + font_params.posX = 5; + font_params.posY = pos + scroll; + font_params.h_wrap = canvas.GetLogicalWidth() - font_params.posX; + for (auto& x : entries) + { + switch (x.level) + { + case LogLevel::Warning: + font_params.color = 0xFF66FFFF; // light yellow + break; + case LogLevel::Error: + font_params.color = 0xFF6666FF; // light red + break; + default: + font_params.color = wi::Color::White(); + break; + } + font_params.cursor = wi::font::Draw(x.text, font_params, cmd); + } + } std::string getText() { std::scoped_lock lock(logLock); - std::string retval; - for (auto& x : entries) - { - retval += x.text; - } - return retval; + return getTextWithoutLock(); } void clear() { diff --git a/WickedEngine/wiBacklog.h b/WickedEngine/wiBacklog.h index 12280935f..1d9ccb31d 100644 --- a/WickedEngine/wiBacklog.h +++ b/WickedEngine/wiBacklog.h @@ -20,6 +20,7 @@ namespace wi::backlog void Scroll(int direction); void Update(const wi::Canvas& canvas, float dt = 1.0f / 60.0f); void Draw(const wi::Canvas& canvas, wi::graphics::CommandList cmd); + void DrawOutputText(const wi::Canvas& canvas, wi::graphics::CommandList cmd); std::string getText(); void clear(); diff --git a/WickedEngine/wiFont.cpp b/WickedEngine/wiFont.cpp index d2721da06..29a2ad981 100644 --- a/WickedEngine/wiFont.cpp +++ b/WickedEngine/wiFont.cpp @@ -111,10 +111,9 @@ namespace wi::font }; static wi::vector fontStyles; - struct Cursor + struct ParseStatus { - XMFLOAT2 pos = {}; - XMFLOAT2 size = {}; + Cursor cursor; uint32_t quadCount = 0; size_t last_word_begin = 0; bool start_new_word = false; @@ -123,32 +122,32 @@ namespace wi::font static thread_local wi::vector vertexList; template - Cursor ParseText(const T* text, size_t text_length, Params params) + ParseStatus ParseText(const T* text, size_t text_length, Params params) { - Cursor cursor; - cursor.pos = params.cursor; + ParseStatus status; + status.cursor = params.cursor; const FontStyle& fontStyle = fontStyles[params.style]; const float fontScale = stbtt_ScaleForPixelHeight(&fontStyle.fontInfo, (float)params.size); vertexList.clear(); auto word_wrap = [&] { - cursor.start_new_word = true; - if (cursor.last_word_begin > 0 && params.h_wrap >= 0 && cursor.pos.x >= params.h_wrap - 1) + status.start_new_word = true; + if (status.last_word_begin > 0 && params.h_wrap >= 0 && status.cursor.position.x >= params.h_wrap - 1) { // Word ended and wrap detected, push down last word by one line: - float word_offset = vertexList[cursor.last_word_begin].pos.x + WHITESPACE_SIZE; - for (size_t i = cursor.last_word_begin; i < cursor.quadCount * 4; ++i) + float word_offset = vertexList[status.last_word_begin].pos.x + WHITESPACE_SIZE; + for (size_t i = status.last_word_begin; i < status.quadCount * 4; ++i) { vertexList[i].pos.x -= word_offset; vertexList[i].pos.y += LINEBREAK_SIZE; } - cursor.pos.x -= word_offset; - cursor.pos.y += LINEBREAK_SIZE; + status.cursor.position.x -= word_offset; + status.cursor.position.y += LINEBREAK_SIZE; } }; - cursor.size.y = cursor.pos.y + LINEBREAK_SIZE; + status.cursor.size.y = status.cursor.position.y + LINEBREAK_SIZE; for (size_t i = 0; i < text_length; ++i) { T character = text[i]; @@ -166,18 +165,18 @@ namespace wi::font if (code == '\n') { word_wrap(); - cursor.pos.x = 0; - cursor.pos.y += LINEBREAK_SIZE; + status.cursor.position.x = 0; + status.cursor.position.y += LINEBREAK_SIZE; } else if (code == ' ') { word_wrap(); - cursor.pos.x += WHITESPACE_SIZE; + status.cursor.position.x += WHITESPACE_SIZE; } else if (code == '\t') { word_wrap(); - cursor.pos.x += TAB_SIZE; + status.cursor.position.x += TAB_SIZE; } else { @@ -187,19 +186,19 @@ namespace wi::font const float glyphOffsetX = glyph.x * params.scaling; const float glyphOffsetY = glyph.y * params.scaling; - const size_t vertexID = size_t(cursor.quadCount) * 4; + const size_t vertexID = size_t(status.quadCount) * 4; vertexList.resize(vertexID + 4); - cursor.quadCount++; + status.quadCount++; - if (cursor.start_new_word) + if (status.start_new_word) { - cursor.last_word_begin = vertexID; + status.last_word_begin = vertexID; } - cursor.start_new_word = false; + status.start_new_word = false; - const float left = cursor.pos.x + glyphOffsetX; + const float left = status.cursor.position.x + glyphOffsetX; const float right = left + glyphWidth; - const float top = cursor.pos.y + glyphOffsetY; + const float top = status.cursor.position.y + glyphOffsetY; const float bottom = top + glyphHeight; vertexList[vertexID + 0].pos = float2(left, top); @@ -214,25 +213,25 @@ namespace wi::font int advance, lsb; stbtt_GetCodepointHMetrics(&fontStyle.fontInfo, code, &advance, &lsb); - cursor.pos.x += advance * fontScale * params.scaling; + status.cursor.position.x += advance * fontScale * params.scaling; - cursor.pos.x += params.spacingX; + status.cursor.position.x += params.spacingX; if (text_length > 1 && i < text_length - 1 && text[i + 1]) { int code_next = (int)text[i + 1]; int kern = stbtt_GetCodepointKernAdvance(&fontStyle.fontInfo, code, code_next); - cursor.pos.x += kern * fontScale; + status.cursor.position.x += kern * fontScale; } } - cursor.size.x = std::max(cursor.size.x, cursor.pos.x); - cursor.size.y = std::max(cursor.size.y, cursor.pos.y + LINEBREAK_SIZE); + status.cursor.size.x = std::max(status.cursor.size.x, status.cursor.position.x); + status.cursor.size.y = std::max(status.cursor.size.y, status.cursor.position.y + LINEBREAK_SIZE); } word_wrap(); - return cursor; + return status; } void CommitText(void* vertexList_GPU) { @@ -433,32 +432,31 @@ namespace wi::font } template - XMFLOAT2 Draw_internal(const T* text, size_t text_length, const Params& params_in, CommandList cmd) + Cursor Draw_internal(const T* text, size_t text_length, const Params& params_in, CommandList cmd) { if (text_length <= 0) { - return XMFLOAT2(0, 0); + return Cursor(); } - Cursor cursor = ParseText(text, text_length, params_in); + ParseStatus status = ParseText(text, text_length, params_in); Params params = params_in; if (params.h_align == WIFALIGN_CENTER) - params.posX -= cursor.size.x / 2; + params.posX -= status.cursor.size.x / 2; else if (params.h_align == WIFALIGN_RIGHT) - params.posX -= cursor.size.x; + params.posX -= status.cursor.size.x; if (params.v_align == WIFALIGN_CENTER) - params.posY -= cursor.size.y / 2; + params.posY -= status.cursor.size.y / 2; else if (params.v_align == WIFALIGN_BOTTOM) - params.posY -= cursor.size.y; + params.posY -= status.cursor.size.y; - - if (cursor.quadCount > 0) + if (status.quadCount > 0) { GraphicsDevice* device = wi::graphics::GetDevice(); - GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(FontVertex) * cursor.quadCount * 4, cmd); + GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(FontVertex) * status.quadCount * 4, cmd); if (!mem.IsValid()) { - return cursor.pos; + return status.cursor; } CommitText(mem.data); @@ -490,7 +488,7 @@ namespace wi::font font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, params.shadow_softness)); device->BindDynamicConstantBuffer(font, CBSLOT_FONT, cmd); - device->DrawInstanced(4, cursor.quadCount, 0, 0, cmd); + device->DrawInstanced(4, status.quadCount, 0, 0, cmd); } // font base render: @@ -503,12 +501,12 @@ namespace wi::font font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, params.softness)); device->BindDynamicConstantBuffer(font, CBSLOT_FONT, cmd); - device->DrawInstanced(4, cursor.quadCount, 0, 0, cmd); + device->DrawInstanced(4, status.quadCount, 0, 0, cmd); device->EventEnd(cmd); } - return cursor.pos; + return status.cursor; } void SetCanvas(const wi::Canvas& current_canvas) @@ -516,29 +514,29 @@ namespace wi::font canvas = current_canvas; } - XMFLOAT2 Draw(const char* text, const Params& params, CommandList cmd) + Cursor Draw(const char* text, const Params& params, CommandList cmd) { size_t text_length = strlen(text); if (text_length == 0) { - return XMFLOAT2(0, 0); + return Cursor(); } return Draw_internal(text, text_length, params, cmd); } - XMFLOAT2 Draw(const wchar_t* text, const Params& params, CommandList cmd) + Cursor Draw(const wchar_t* text, const Params& params, CommandList cmd) { size_t text_length = wcslen(text); if (text_length == 0) { - return XMFLOAT2(0, 0); + return Cursor(); } return Draw_internal(text, text_length, params, cmd); } - XMFLOAT2 Draw(const std::string& text, const Params& params, CommandList cmd) + Cursor Draw(const std::string& text, const Params& params, CommandList cmd) { return Draw_internal(text.c_str(), text.length(), params, cmd); } - XMFLOAT2 Draw(const std::wstring& text, const Params& params, CommandList cmd) + Cursor Draw(const std::wstring& text, const Params& params, CommandList cmd) { return Draw_internal(text.c_str(), text.length(), params, cmd); } @@ -550,7 +548,7 @@ namespace wi::font { return XMFLOAT2(0, 0); } - return ParseText(text, text_length, params).size; + return ParseText(text, text_length, params).cursor.size; } XMFLOAT2 TextSize(const wchar_t* text, const Params& params) { @@ -559,7 +557,7 @@ namespace wi::font { return XMFLOAT2(0, 0); } - return ParseText(text, text_length, params).size; + return ParseText(text, text_length, params).cursor.size; } XMFLOAT2 TextSize(const std::string& text, const Params& params) { @@ -567,7 +565,7 @@ namespace wi::font { return XMFLOAT2(0, 0); } - return ParseText(text.c_str(), text.length(), params).size; + return ParseText(text.c_str(), text.length(), params).cursor.size; } XMFLOAT2 TextSize(const std::wstring& text, const Params& params) { @@ -575,7 +573,7 @@ namespace wi::font { return XMFLOAT2(0, 0); } - return ParseText(text.c_str(), text.length(), params).size; + return ParseText(text.c_str(), text.length(), params).cursor.size; } float TextWidth(const char* text, const Params& params) diff --git a/WickedEngine/wiFont.h b/WickedEngine/wiFont.h index cd8b0407c..ab0e386d6 100644 --- a/WickedEngine/wiFont.h +++ b/WickedEngine/wiFont.h @@ -12,34 +12,41 @@ namespace wi::font // Do not alter order because it is bound to lua manually enum Alignment { - WIFALIGN_LEFT, - WIFALIGN_CENTER, - WIFALIGN_RIGHT, - WIFALIGN_TOP, - WIFALIGN_BOTTOM + WIFALIGN_LEFT, // left alignment (horizontal) + WIFALIGN_CENTER, // center alignment (horizontal or vertical) + WIFALIGN_RIGHT, // right alignment (horizontal) + WIFALIGN_TOP, // top alignment (vertical) + WIFALIGN_BOTTOM // bottom alignment (vertical) }; static constexpr int WIFONTSIZE_DEFAULT = 16; + struct Cursor + { + XMFLOAT2 position = {}; // the next character's position offset from the first character (logical canvas units) + XMFLOAT2 size = {}; // the written text's measurements from the first character (logical canvas units) + }; + struct Params { - float posX = 0; - float posY = 0; - int size = WIFONTSIZE_DEFAULT; // line height in DPI scaled units - float scaling = 1; - float spacingX = 0, spacingY = 0; // minimum spacing between characters - Alignment h_align, v_align; - wi::Color color; - wi::Color shadowColor; - float h_wrap = -1; // wrap start width (-1 default for no wrap) - int style = 0; + float posX = 0; // position in horizontal direction (logical canvas units) + float posY = 0; // position in vertical direction (logical canvas units) + int size = WIFONTSIZE_DEFAULT; // line height (logical canvas units) + float scaling = 1; // this will apply upscaling to the text while keeping the same resolution (size) of the font + float spacingX = 0, spacingY = 0; // minimum spacing between characters (logical canvas units) + Alignment h_align = WIFALIGN_LEFT; // horizontal alignment + Alignment v_align = WIFALIGN_TOP; // vertical alignment + wi::Color color; // base color of the text characters + wi::Color shadowColor; // transparent disables, any other color enables shadow under text + float h_wrap = -1; // wrap start width (-1 default for no wrap) (logical canvas units) + int style = 0; // 0: use default font style, other values can be taken from the wi::font::AddFontStyle() funtion's return value float softness = 0.1f; // value in [0,1] range float bolden = 0; // value in [0,1] range float shadow_softness = 0.5f; // value in [0,1] range float shadow_bolden = 0.1f; // value in [0,1] range float shadow_offset_x = 0; // offset for shadow under the text in logical canvas coordinates float shadow_offset_y = 0; // offset for shadow under the text in logical canvas coordinates - XMFLOAT2 cursor = XMFLOAT2(0, 0); // cursor offset, it can be used to continue text drawing by taking the Draw's return value (optional) + Cursor cursor; // cursor can be used to continue text drawing by taking the Draw's return value (optional) Params( float posX = 0, @@ -86,21 +93,28 @@ namespace wi::font void UpdateAtlas(); // Draw text with specified parameters and return cursor for last word - XMFLOAT2 Draw(const char* text, const Params& params, wi::graphics::CommandList cmd); - XMFLOAT2 Draw(const wchar_t* text, const Params& params, wi::graphics::CommandList cmd); - XMFLOAT2 Draw(const std::string& text, const Params& params, wi::graphics::CommandList cmd); - XMFLOAT2 Draw(const std::wstring& text, const Params& params, wi::graphics::CommandList cmd); + // The next Draw() can continue from where this left off by using the return value of this function + // in wi::font::Params::cursor + Cursor Draw(const char* text, const Params& params, wi::graphics::CommandList cmd); + Cursor Draw(const wchar_t* text, const Params& params, wi::graphics::CommandList cmd); + Cursor Draw(const std::string& text, const Params& params, wi::graphics::CommandList cmd); + Cursor Draw(const std::wstring& text, const Params& params, wi::graphics::CommandList cmd); + // Computes the text's size measurements in logical canvas coordinates XMFLOAT2 TextSize(const char* text, const Params& params); XMFLOAT2 TextSize(const wchar_t* text, const Params& params); XMFLOAT2 TextSize(const std::string& text, const Params& params); XMFLOAT2 TextSize(const std::wstring& text, const Params& params); + // Computes the text's width in logical canvas coordinates + // Avoid calling TextWidth() and TextHeight() both, instead use TextSize() if you need both measurements! float TextWidth(const char* text, const Params& params); float TextWidth(const wchar_t* text, const Params& params); float TextWidth(const std::string& text, const Params& params); float TextWidth(const std::wstring& text, const Params& params); + // Computes the text's height in logical canvas coordinates + // Avoid calling TextWidth() and TextHeight() both, instead use TextSize() if you need both measurements! float TextHeight(const char* text, const Params& params); float TextHeight(const wchar_t* text, const Params& params); float TextHeight(const std::string& text, const Params& params); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 5f293f06a..71654fa23 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 60; // minor bug fixes, alterations, refactors, updates - const int revision = 78; + const int revision = 79; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);