crafter-build V2

This commit is contained in:
Jorijn van der Graaf 2026-04-30 01:30:08 +02:00
commit bc669b5e05
16 changed files with 830 additions and 217 deletions

View file

@ -54,7 +54,6 @@ int main() {
window.FinishInit(); window.FinishInit();
RendertargetBase<3> rendertargetBase(1280, 720);
RenderingElement2DVulkan<true, true> element( RenderingElement2DVulkan<true, true> element(
{ {
0.5, //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor 0.5, //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor
@ -73,13 +72,7 @@ int main() {
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->value[1] = {0, 1, 0, 1}; reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->value[1] = {0, 1, 0, 1};
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->FlushDevice(); reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->FlushDevice();
} }
RendertargetVulkan rendertarget(1280, 720, {&element});
InitializeRenderingElement2DVulkanBuffer();
element.UpdatePosition(rendertargetBase, rendertargetBase.transform);
renderingElement2DVulkanTransformBuffer[0].FlushDevice();
renderingElement2DVulkanTransformBuffer[1].FlushDevice();
renderingElement2DVulkanTransformBuffer[2].FlushDevice();
VkImageDescriptorInfoEXT imageInfo0 = { VkImageDescriptorInfoEXT imageInfo0 = {
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT, .sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
@ -130,7 +123,7 @@ int main() {
.size = Device::descriptorHeapProperties.imageDescriptorSize .size = Device::descriptorHeapProperties.imageDescriptorSize
}, },
WriteRenderingElement2DVulkanDescriptors(infos, ranges, 3, descriptorHeap.bufferStartOffset, descriptorHeap); rendertarget.WriteDescriptors(infos, ranges, 3, descriptorHeap.bufferStartOffset, descriptorHeap);
window.pipeline = &pipeline; window.pipeline = &pipeline;
window.descriptorHeap = &descriptorHeap; window.descriptorHeap = &descriptorHeap;

View file

@ -38,9 +38,9 @@ void main()
for (uint16_t i = 1us; i < UITransformBuffer[bufferStart].count+1; i++) { for (uint16_t i = 1us; i < UITransformBuffer[bufferStart].count+1; i++) {
if(pixel.x > UITransformBuffer[bufferStart].data[i].x && pixel.x < UITransformBuffer[bufferStart].data[i].x + UITransformBuffer[bufferStart].data[i].sizeX && pixel.y > UITransformBuffer[bufferStart].data[i].y && pixel.y < UITransformBuffer[bufferStart].data[i].y + UITransformBuffer[bufferStart].data[i].sizeY) { if(pixel.x > UITransformBuffer[bufferStart].data[i].x && pixel.x < UITransformBuffer[bufferStart].data[i].x + UITransformBuffer[bufferStart].data[i].sizeX && pixel.y > UITransformBuffer[bufferStart].data[i].y && pixel.y < UITransformBuffer[bufferStart].data[i].y + UITransformBuffer[bufferStart].data[i].sizeY) {
int16_t srcX = int16_t((pixel.x - UITransformBuffer[bufferStart].data[i].x) * UITransformBuffer[bufferStart].data[i].bufferX / UITransformBuffer[bufferStart].data[i].sizeX); int16_t srcX = int16_t(float(pixel.x - UITransformBuffer[bufferStart].data[i].x) * float(UITransformBuffer[bufferStart].data[i].bufferX) / float(UITransformBuffer[bufferStart].data[i].sizeX));
int16_t srcY = int16_t((pixel.y - UITransformBuffer[bufferStart].data[i].y) * UITransformBuffer[bufferStart].data[i].bufferY / UITransformBuffer[bufferStart].data[i].sizeY); int16_t srcY = int16_t(float(pixel.y - UITransformBuffer[bufferStart].data[i].y) * float(UITransformBuffer[bufferStart].data[i].bufferY) / float(UITransformBuffer[bufferStart].data[i].sizeY));
hitValue = vec4(UIPixelBuffer[bufferStart + UITransformBuffer[bufferStart].count].pixels[srcY * UITransformBuffer[bufferStart].data[i].bufferX + srcX]); hitValue = vec4(UIPixelBuffer[bufferStart + 1].pixels[srcY * UITransformBuffer[bufferStart].data[i].bufferX + srcX]);
} }
} }

View file

@ -211,6 +211,33 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym)
case XKB_KEY_y: return CrafterKeys::Y; case XKB_KEY_y: return CrafterKeys::Y;
case XKB_KEY_z: return CrafterKeys::Z; case XKB_KEY_z: return CrafterKeys::Z;
case XKB_KEY_A: return CrafterKeys::A;
case XKB_KEY_B: return CrafterKeys::B;
case XKB_KEY_C: return CrafterKeys::C;
case XKB_KEY_D: return CrafterKeys::D;
case XKB_KEY_E: return CrafterKeys::E;
case XKB_KEY_F: return CrafterKeys::F;
case XKB_KEY_G: return CrafterKeys::G;
case XKB_KEY_H: return CrafterKeys::H;
case XKB_KEY_I: return CrafterKeys::I;
case XKB_KEY_J: return CrafterKeys::J;
case XKB_KEY_K: return CrafterKeys::K;
case XKB_KEY_L: return CrafterKeys::L;
case XKB_KEY_M: return CrafterKeys::M;
case XKB_KEY_N: return CrafterKeys::N;
case XKB_KEY_O: return CrafterKeys::O;
case XKB_KEY_P: return CrafterKeys::P;
case XKB_KEY_Q: return CrafterKeys::Q;
case XKB_KEY_R: return CrafterKeys::R;
case XKB_KEY_S: return CrafterKeys::S;
case XKB_KEY_T: return CrafterKeys::T;
case XKB_KEY_U: return CrafterKeys::U;
case XKB_KEY_V: return CrafterKeys::V;
case XKB_KEY_W: return CrafterKeys::W;
case XKB_KEY_X: return CrafterKeys::X;
case XKB_KEY_Y: return CrafterKeys::Y;
case XKB_KEY_Z: return CrafterKeys::Z;
// Numbers // Numbers
case XKB_KEY_0: return CrafterKeys::_0; case XKB_KEY_0: return CrafterKeys::_0;
case XKB_KEY_1: return CrafterKeys::_1; case XKB_KEY_1: return CrafterKeys::_1;
@ -300,8 +327,7 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym)
case XKB_KEY_period: return CrafterKeys::period; case XKB_KEY_period: return CrafterKeys::period;
case XKB_KEY_slash: return CrafterKeys::slash; case XKB_KEY_slash: return CrafterKeys::slash;
default: default: return CrafterKeys::CrafterKeysMax;
throw std::runtime_error(std::format("Unkown XKB_KEY: {}", sym));
} }
} }
@ -461,31 +487,39 @@ void Device::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial,
} }
void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
xkb_keycode_t keycode = key + 8; xkb_keycode_t keycode = key + 8;
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode); xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode);
CrafterKeys crafterKey = keysym_to_crafter_key(keysym); CrafterKeys crafterKey = keysym_to_crafter_key(keysym);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if(Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)]) { if (focusedWindow->heldkeys[(std::uint8_t)crafterKey]) {
Device::focusedWindow->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke(); focusedWindow->onKeyHold[(std::uint8_t)crafterKey].Invoke();
Device::focusedWindow->onAnyKeyHold.Invoke(crafterKey); focusedWindow->onAnyKeyHold.Invoke(crafterKey);
} else { } else {
Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true; focusedWindow->heldkeys[(std::uint8_t)crafterKey] = true;
Device::focusedWindow->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke(); focusedWindow->onKeyDown[(std::uint8_t)crafterKey].Invoke();
Device::focusedWindow->onAnyKeyDown.Invoke(crafterKey); focusedWindow->onAnyKeyDown.Invoke(crafterKey);
}
std::string buf;
buf.resize(16);
int n = xkb_state_key_get_utf8(xkb_state, keycode, buf.data(), 16);
if (n > 0) {
if ((unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) {
buf.resize(n);
focusedWindow->onTextInput.Invoke(buf);
}
} }
} else { } else {
Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false; focusedWindow->heldkeys[(std::uint8_t)crafterKey] = false;
Device::focusedWindow->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke(); focusedWindow->onKeyUp[(std::uint8_t)crafterKey].Invoke();
Device::focusedWindow->onAnyKeyUp.Invoke(crafterKey); focusedWindow->onAnyKeyUp.Invoke(crafterKey);
} }
} }
void Device::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { void Device::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
} }
void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) { void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) {

View file

@ -57,3 +57,14 @@ Font::Font(const std::filesystem::path& fontFilePath) {
this->descent = descent; this->descent = descent;
this->lineGap = lineGap; this->lineGap = lineGap;
} }
std::uint32_t Font::GetLineWidth(const std::string_view text, float size) {
float scale = stbtt_ScaleForPixelHeight(&font, size);
std::uint32_t lineWidth = 0;
for (const char c : text) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&font, c, &advance, &lsb);
lineWidth += (int)(advance * scale);
}
return lineWidth;
}

View file

@ -0,0 +1,141 @@
/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include <vulkan/vulkan.h>
#endif
module Crafter.Graphics:Rendertarget_impl;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import :Rendertarget;
import :Window;
import :DescriptorHeapVulkan;
import :RenderingElement2DVulkan;
import std;
using namespace Crafter;
RendertargetVulkan::RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase(sizeX, sizeY) {
}
void RendertargetVulkan::UpdateElements() {
elements.clear();
std::sort(transform.children.begin(), transform.children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; });
for(Transform2D* child : transform.children) {
SetOrderResursive(child);
}
}
void RendertargetVulkan::CreateBuffer(std::uint8_t frame) {
transformBuffer[frame].Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, elements.size()+1);
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
std::uint16_t* sizePtr = reinterpret_cast<std::uint16_t*>(transformBuffer[frame].value);
*sizePtr = static_cast<std::uint16_t>(elements.size());
for(std::uint16_t i = 0; i < elements.size(); i++) {
val[i].bufferX = elements[i]->bufferX;
val[i].bufferY = elements[i]->bufferY;
}
transformBuffer[frame].FlushDevice();
}
void RendertargetVulkan::ReorderBuffer(std::uint8_t frame) {
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
for(std::uint16_t i = 0; i < elements.size(); i++) {
val[i].scaled = elements[i]->scaled;
val[i].bufferX = elements[i]->bufferX;
val[i].bufferY = elements[i]->bufferY;
}
transformBuffer[frame].FlushDevice();
}
void RendertargetVulkan::WriteDescriptors(std::span<VkResourceDescriptorInfoEXT> infos, std::span<VkHostAddressRangeEXT> ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap) {
VkDeviceAddressRangeKHR transformRanges[Window::numFrames] = {
{
.address = transformBuffer[0].address,
.size = transformBuffer[0].size
},
{
.address = transformBuffer[1].address,
.size = transformBuffer[1].size
},
{
.address = transformBuffer[2].address,
.size = transformBuffer[2].size
}
};
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
ranges[start + i] = {
.address = descriptorHeap.resourceHeap[i].value + bufferOffset,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
};
infos[start + i] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &transformRanges[i]}
};
}
start += 3;
bufferOffset += Device::descriptorHeapProperties.bufferDescriptorSize;
std::vector<VkDeviceAddressRangeKHR> bufferRanges(elements.size() * Window::numFrames);
std::uint16_t rangeOffset = 0;
for(std::uint8_t i2 = 0; i2 < Window::numFrames; i2++) {
for(std::uint16_t i = 0; i < elements.size(); i++) {
ranges[start + i] = {
.address = descriptorHeap.resourceHeap[i2].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * i,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
};
infos[start + i] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &bufferRanges[i]}
};
bufferRanges[rangeOffset + i] = {
.address = elements[i]->buffers[i2]->address,
.size = elements[i]->buffers[i2]->size
};
}
start += elements.size();
rangeOffset += elements.size();
}
Device::vkWriteResourceDescriptorsEXT(Device::device, start, infos.data(), ranges.data());
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
descriptorHeap.resourceHeap[i].FlushDevice();
}
}
void RendertargetVulkan::SetOrderResursive(Transform2D* elementTransform) {
RenderingElement2DVulkanBase* renderer = dynamic_cast<RenderingElement2DVulkanBase*>(elementTransform);
if(renderer) {
renderer->index = elements.size();
elements.push_back(renderer);
}
std::sort(elementTransform->children.begin(), elementTransform->children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; });
for(Transform2D* childTransform : elementTransform->children) {
SetOrderResursive(childTransform);
}
}
#endif

View file

@ -272,49 +272,65 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
PostQuitMessage(0); PostQuitMessage(0);
break; break;
} }
case WM_KEYDOWN:{ case WM_KEYDOWN:
if ((lParam & (1 << 30)) == 0) { // only first press case WM_SYSKEYDOWN: { // SYSKEYDOWN catches Alt combos, F10, etc.
CrafterKeys crafterKey = vk_to_crafter_key(wParam); CrafterKeys crafterKey = vk_to_crafter_key(wParam);
if(window->heldkeys[static_cast<std::uint8_t>(crafterKey)]) { bool isRepeat = (lParam & (1 << 30)) != 0;
window->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke();
if (isRepeat) {
window->onKeyHold[(uint8_t)crafterKey].Invoke();
window->onAnyKeyHold.Invoke(crafterKey); window->onAnyKeyHold.Invoke(crafterKey);
} else { } else {
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true; window->heldkeys[(uint8_t)crafterKey] = true;
window->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke(); window->onKeyDown[(uint8_t)crafterKey].Invoke();
window->onAnyKeyDown.Invoke(crafterKey); window->onAnyKeyDown.Invoke(crafterKey);
} }
}
break; break;
} }
case WM_KEYUP: {
case WM_KEYUP:
case WM_SYSKEYUP: {
CrafterKeys crafterKey = vk_to_crafter_key(wParam); CrafterKeys crafterKey = vk_to_crafter_key(wParam);
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false; window->heldkeys[(uint8_t)crafterKey] = false;
window->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke(); window->onKeyUp[(uint8_t)crafterKey].Invoke();
window->onAnyKeyUp.Invoke(crafterKey); window->onAnyKeyUp.Invoke(crafterKey);
break; break;
} }
case WM_MOUSEMOVE: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
Vector<float, 2> pos(x, y); case WM_CHAR: {
window->currentMousePos = pos; // wParam is a UTF-16 code unit. May be a surrogate — buffer until we have a pair.
window->onMouseMove.Invoke(); wchar_t wc = (wchar_t)wParam;
for(MouseElement* element : window->mouseElements) {
if(element) { // Filter control characters (backspace=0x08, tab=0x09, enter=0x0D, escape=0x1B, etc.)
if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { if (wc < 0x20 || wc == 0x7f) break;
element->onMouseMove.Invoke();
if(!element->mouseHover) { // Handle UTF-16 surrogate pairs (characters outside the BMP, e.g. emoji).
element->mouseHover = true; static wchar_t highSurrogate = 0;
element->onMouseEnter.Invoke(); wchar_t utf16[2];
int utf16Len;
if (wc >= 0xD800 && wc <= 0xDBFF) {
// High surrogate — stash it and wait for the low surrogate.
highSurrogate = wc;
break;
} else if (wc >= 0xDC00 && wc <= 0xDFFF) {
// Low surrogate — pair with the stashed high surrogate.
if (highSurrogate == 0) break; // orphaned low surrogate, ignore
utf16[0] = highSurrogate;
utf16[1] = wc;
utf16Len = 2;
highSurrogate = 0;
} else {
utf16[0] = wc;
utf16Len = 1;
} }
} else if(element->mouseHover) {
element->mouseHover = false; // Convert UTF-16 to UTF-8.
element->onMouseLeave.Invoke(); char utf8[8];
int n = WideCharToMultiByte(CP_UTF8, 0, utf16, utf16Len, utf8, sizeof(utf8), nullptr, nullptr);
if (n > 0) {
window->onTextInput.Invoke(std::string(utf8, n));
} }
}
}
window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast<MouseElement*>(nullptr)), window->mouseElements.end());
break; break;
} }
case WM_LBUTTONDOWN: { case WM_LBUTTONDOWN: {

View file

@ -34,5 +34,6 @@ namespace Crafter {
std::int_fast32_t lineGap; std::int_fast32_t lineGap;
stbtt_fontinfo font; stbtt_fontinfo font;
Font(const std::filesystem::path& font); Font(const std::filesystem::path& font);
std::uint32_t GetLineWidth(const std::string_view text, float size);
}; };
} }

View file

@ -21,9 +21,7 @@ export module Crafter.Graphics:ForwardDeclarations;
import std; import std;
export namespace Crafter { export namespace Crafter {
template<std::uint8_t Frames = 1>
struct RendertargetBase; struct RendertargetBase;
struct GridElement; struct GridElement;
struct Window; struct Window;
} }

View file

@ -33,8 +33,7 @@ export namespace Crafter {
GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) { GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) {
} }
template<std::uint8_t Frames> void UpdatePosition(RendertargetBase& window, Transform2D& parent) override {
void UpdatePositionImpl(RendertargetBase<Frames>& window, Transform2D& parent) {
ScaleElement(parent); ScaleElement(parent);
std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns; std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns;
std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows; std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows;
@ -61,11 +60,5 @@ export namespace Crafter {
} }
} }
} }
void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) override {
UpdatePositionImpl<1>(window, parent);
}
void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) override {
UpdatePositionImpl<3>(window, parent);
}
}; };
} }

View file

@ -130,7 +130,7 @@ export namespace Crafter {
} }
void UpdatePosition(RendertargetBase<Frames>& window, Transform2D& parent) override { void UpdatePosition(RendertargetBase& window, Transform2D& parent) override {
ScaleData2D oldScale = this->scaled; ScaleData2D oldScale = this->scaled;
this->ScaleElement(parent); this->ScaleElement(parent);
if constexpr(Scaling && !Rotating) { if constexpr(Scaling && !Rotating) {
@ -153,7 +153,7 @@ export namespace Crafter {
} }
} }
std::vector<std::string_view> ResizeText(RendertargetBase<Frames>& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) { std::vector<std::string_view> ResizeText(RendertargetBase& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size); float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale); int baseline = (int)(font.ascent * scale);

View file

@ -32,8 +32,8 @@ export namespace Crafter {
enum class TextVerticalAlignment { enum class TextVerticalAlignment {
Top, Top,
Middle, Center,
Down Bottom
}; };
enum class TextOverflowMode { enum class TextOverflowMode {

View file

@ -31,15 +31,9 @@ import :VulkanBuffer;
import :Types; import :Types;
import :Window; import :Window;
import :DescriptorHeapVulkan; import :DescriptorHeapVulkan;
import :Font;
export namespace Crafter { export namespace Crafter {
struct __attribute__((packed)) RenderingElement2DVulkanTransformInfo {
ScaleData2D scaled; // 0 - 8 bytes
std::uint16_t bufferX; // 8 - 2 bytes
std::uint16_t bufferY; // 10 - 2 bytes
//12 bytes total;
};
struct RenderingElement2DVulkanBase : Transform2D { struct RenderingElement2DVulkanBase : Transform2D {
std::uint16_t index; std::uint16_t index;
std::uint16_t bufferX; std::uint16_t bufferX;
@ -56,170 +50,428 @@ export namespace Crafter {
} }
}; };
std::vector<RenderingElement2DVulkanBase*> renderingElement2DVulkans; template<bool Owning, bool Mapped, bool Single = false>
VulkanBuffer<RenderingElement2DVulkanTransformInfo, true> renderingElement2DVulkanTransformBuffer[Window::numFrames];
template<bool Owning, bool Mapped>
struct RenderingElement2DVulkan : RenderingElement2DVulkanBase { struct RenderingElement2DVulkan : RenderingElement2DVulkanBase {
RenderingElement2DVulkan(Anchor2D anchor, RendertargetBase<Window::numFrames>& target, Transform2D& parent) requires(Owning) : RenderingElement2DVulkanBase(anchor) { RenderingElement2DVulkan(Anchor2D anchor) : RenderingElement2DVulkanBase(anchor) {
renderingElement2DVulkans.push_back(this);
UpdatePosition(target, parent); }
RenderingElement2DVulkan(Anchor2D anchor, RendertargetBase& target, Transform2D& parent) requires(Owning) : RenderingElement2DVulkanBase(anchor) {
GetScale(target, parent);
this->bufferX = this->scaled.size.x; this->bufferX = this->scaled.size.x;
this->bufferY = this->scaled.size.y; this->bufferY = this->scaled.size.y;
for(std::uint8_t i = 0; i < Window::numFrames; i++) { if(Single) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY) {
renderingElement2DVulkans.push_back(this);
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY, std::array<VulkanBufferBase*, Window::numFrames>&& buffers) requires(!Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY, std::move(buffers)) {
renderingElement2DVulkans.push_back(this);
}
RenderingElement2DVulkan(Anchor2D anchor, const std::filesystem::path& assetPath, bool single) requires(Owning && Mapped) : RenderingElement2DVulkanBase(anchor) {
TextureAssetInfo info = TextureAsset<_Float16>::LoadInfo(assetPath);
this->bufferX = info.sizeX;
this->bufferY = info.sizeY;
if(single) {
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>(); buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) { for(std::uint8_t i = 1; i < Window::numFrames; i++) {
buffers[i] = buffers[0]; buffers[i] = buffers[0];
} }
TextureAsset<_Float16>::LoadInfo(assetPath, buffers[0].value, this->bufferX, this->bufferY);
} else { } else {
for(std::uint8_t i = 0; i < Window::numFrames; i++) { for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>(); buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY); static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
} }
TextureAsset<_Float16>::LoadInfo(assetPath, buffers[0].value, this->bufferX, this->bufferY); }
}
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY) {
if constexpr(Single) {
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) { for(std::uint8_t i = 1; i < Window::numFrames; i++) {
std::memcpy(buffers[i].value, buffers[0].value, this->bufferX * this->bufferY * sizeof(_Float16)); buffers[i] = buffers[0];
}
} else {
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
}
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY, std::array<VulkanBufferBase*, Window::numFrames>&& buffers) requires(!Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY, std::move(buffers)) {
}
RenderingElement2DVulkan(Anchor2D anchor, const std::filesystem::path& assetPath) requires(Owning && Mapped) : RenderingElement2DVulkanBase(anchor) {
TextureAssetInfo info = TextureAsset<_Float16>::LoadInfo(assetPath);
this->bufferX = info.sizeX;
this->bufferY = info.sizeY;
if constexpr(Single) {
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
buffers[i] = buffers[0];
}
TextureAsset<Vector<_Float16, 4, 4>>::Load(assetPath, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY);
} else {
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
TextureAsset<Vector<_Float16, 4, 4>>::Load(assetPath, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
std::memcpy(static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->value, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX * this->bufferY * sizeof(_Float16));
} }
} }
} }
~RenderingElement2DVulkan() { ~RenderingElement2DVulkan() {
if constexpr(Owning) { if constexpr(Owning) {
if constexpr(Single) {
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0]);
} else {
for(VulkanBufferBase* buffer : buffers) { for(VulkanBufferBase* buffer : buffers) {
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffer); delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffer);
} }
} }
auto it = std::find(renderingElement2DVulkans.begin(), renderingElement2DVulkans.end(), this);
if (it != renderingElement2DVulkans.end()) {
renderingElement2DVulkans.erase(it);
} }
} }
RenderingElement2DVulkan(RenderingElement2DVulkan&) = delete; RenderingElement2DVulkan(RenderingElement2DVulkan&) = delete;
RenderingElement2DVulkan& operator=(RenderingElement2DVulkan&) = delete; RenderingElement2DVulkan& operator=(RenderingElement2DVulkan&) = delete;
void UpdatePosition(RendertargetBase<Window::numFrames>& window, Transform2D& parent) override { void CreateBuffer(std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) {
this->ScaleElement(parent); this->bufferX = this->scaled.size.x;
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(renderingElement2DVulkanTransformBuffer[window.frame].value) + sizeof(RenderingElement2DVulkanTransformInfo)); this->bufferY = this->scaled.size.y;
val[index].scaled = this->scaled; if constexpr(Single) {
for(Transform2D* child : this->children) { buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
child->UpdatePosition(window, *this); static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
buffers[i] = buffers[0];
} }
} } else {
};
void WriteRenderingElement2DVulkanDescriptors(std::span<VkResourceDescriptorInfoEXT> infos, std::span<VkHostAddressRangeEXT> ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap) {
VkDeviceAddressRangeKHR transformRanges[Window::numFrames] = {
{
.address = renderingElement2DVulkanTransformBuffer[0].address,
.size = renderingElement2DVulkanTransformBuffer[0].size
},
{
.address = renderingElement2DVulkanTransformBuffer[1].address,
.size = renderingElement2DVulkanTransformBuffer[1].size
},
{
.address = renderingElement2DVulkanTransformBuffer[2].address,
.size = renderingElement2DVulkanTransformBuffer[2].size
}
};
for(std::uint8_t i = 0; i < Window::numFrames; i++) { for(std::uint8_t i = 0; i < Window::numFrames; i++) {
ranges[start + i] = { buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
.address = descriptorHeap.resourceHeap[i].value + bufferOffset, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
}
void ResizeBuffer(RendertargetVulkan& window, DescriptorHeapVulkan& descriptorHeap, std::uint16_t bufferOffset, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) {
if constexpr(Single) {
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
} else {
for(VulkanBufferBase* buffer : buffers) {
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffer)->Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
}
}
this->bufferX = bufferX;
this->bufferY = bufferY;
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(window.transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
val[index].bufferX = this->bufferX;
val[index].bufferY = this->bufferY;
window.transformBuffer[frame].FlushDevice();
}
VkHostAddressRangeEXT ranges[3] = {
{
.address = descriptorHeap.resourceHeap[0].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
.size = Device::descriptorHeapProperties.bufferDescriptorSize .size = Device::descriptorHeapProperties.bufferDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[1].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
{
.address = descriptorHeap.resourceHeap[2].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
},
}; };
infos[start + i] = {
VkDeviceAddressRangeKHR bufferRanges[3] {
{
.address = buffers[0]->address,
.size = buffers[0]->size
},
{
.address = buffers[1]->address,
.size = buffers[1]->size
},
{
.address = buffers[2]->address,
.size = buffers[2]->size
},
};
VkResourceDescriptorInfoEXT infos[3] = {
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT, .sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &transformRanges[i]} .data = { .pAddressRange = &bufferRanges[0]}
}; },
} {
start += 3;
bufferOffset += Device::descriptorHeapProperties.bufferDescriptorSize;
std::vector<VkDeviceAddressRangeKHR> bufferRanges(renderingElement2DVulkans.size() * Window::numFrames);
std::uint16_t rangeOffset = 0;
for(std::uint8_t i2 = 0; i2 < Window::numFrames; i2++) {
for(std::uint16_t i = 0; i < renderingElement2DVulkans.size(); i++) {
ranges[start + i] = {
.address = descriptorHeap.resourceHeap[i2].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * i,
.size = Device::descriptorHeapProperties.bufferDescriptorSize
};
infos[start + i] = {
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT, .sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &bufferRanges[i]} .data = { .pAddressRange = &bufferRanges[1]}
},
{
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.data = { .pAddressRange = &bufferRanges[2]}
},
}; };
bufferRanges[rangeOffset + i] = {
.address = renderingElement2DVulkans[i]->buffers[i2]->address,
.size = renderingElement2DVulkans[i]->buffers[i2]->size
};
}
start += renderingElement2DVulkans.size();
rangeOffset += renderingElement2DVulkans.size();
}
Device::vkWriteResourceDescriptorsEXT(Device::device, start, infos.data(), ranges.data());
Device::vkWriteResourceDescriptorsEXT(Device::device, 3, infos, ranges);
for(std::uint8_t i = 0; i < Window::numFrames; i++) { for(std::uint8_t i = 0; i < Window::numFrames; i++) {
descriptorHeap.resourceHeap[i].FlushDevice(); descriptorHeap.resourceHeap[i].FlushDevice();
} }
} }
void InitializeRenderingElement2DVulkanBuffer() { void UpdatePosition(RendertargetBase& window2, Transform2D& parent) override {
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) { RendertargetVulkan& window = static_cast<RendertargetVulkan&>(window2);
renderingElement2DVulkanTransformBuffer[frame].Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, renderingElement2DVulkans.size()+1); this->ScaleElement(parent);
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(renderingElement2DVulkanTransformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo)); RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(window.transformBuffer[window.frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
std::uint16_t* sizePtr = reinterpret_cast<std::uint16_t*>(renderingElement2DVulkanTransformBuffer[frame].value); val[index].scaled = this->scaled;
*sizePtr = renderingElement2DVulkans.size(); for(Transform2D* child : this->children) {
for(std::uint16_t i = 0; i < renderingElement2DVulkans.size(); i++) { child->UpdatePosition(window, *this);
val[i].scaled = renderingElement2DVulkans[i]->scaled;
val[i].bufferX = renderingElement2DVulkans[i]->bufferX;
val[i].bufferY = renderingElement2DVulkans[i]->bufferY;
renderingElement2DVulkans[i]->index = i;
}
} }
} }
void UpdateRenderingElement2DVulkanBuffer(std::uint8_t frame) { void GetScale(RendertargetBase& window, Transform2D& parent) {
renderingElement2DVulkanTransformBuffer[frame].Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, renderingElement2DVulkans.size()+1); this->ScaleElement(parent);
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(renderingElement2DVulkanTransformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo)); for(Transform2D* child : this->children) {
std::uint16_t* sizePtr = reinterpret_cast<std::uint16_t*>(renderingElement2DVulkanTransformBuffer[frame].value); child->UpdatePosition(window, *this);
*sizePtr = renderingElement2DVulkans.size();
for(std::uint16_t i = 0; i < renderingElement2DVulkans.size(); i++) {
val[i].scaled = renderingElement2DVulkans[i]->scaled;
val[i].bufferX = renderingElement2DVulkans[i]->bufferX;
val[i].bufferY = renderingElement2DVulkans[i]->bufferY;
renderingElement2DVulkans[i]->index = i + 1;
} }
} }
int utf8_decode(const char* s, int* bytes_consumed) {
unsigned char c = s[0];
if (c < 0x80) {
*bytes_consumed = 1;
return c;
} else if ((c & 0xE0) == 0xC0) {
*bytes_consumed = 2;
return ((c & 0x1F) << 6) | (s[1] & 0x3F);
} else if ((c & 0xF0) == 0xE0) {
*bytes_consumed = 3;
return ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
} else if ((c & 0xF8) == 0xF0) {
*bytes_consumed = 4;
return ((c & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
}
*bytes_consumed = 1;
return 0xFFFD; // replacement char
}
void RenderText(std::span<const std::string_view> lines, float size, Vector<_Float16, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale);
std::uint32_t lineHeight = (font.ascent - font.descent) * scale;
std::uint32_t currentY = baseline;
for(std::string_view line : lines) {
std::uint32_t lineWidth = 0;
for (const char c : line) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb);
lineWidth += (int)(advance * scale);
}
std::uint32_t x = 0;
switch (alignment) {
case TextAlignment::Left:
x = 0;
break;
case TextAlignment::Center:
x = (this->scaled.size.x - lineWidth) / 2;
break;
case TextAlignment::Right:
x = this->scaled.size.x - lineWidth;
break;
}
const char* p = line.data();
const char* end = p + line.size();
while (p < end) {
int bytes;
int codepoint = utf8_decode(p, &bytes);
p += bytes;
int ax;
int lsb;
stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb);
int c_x1, c_y1, c_x2, c_y2;
stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2);
int w = c_x2 - c_x1;
int h = c_y2 - c_y1;
std::vector<unsigned char> bitmap(w * h);
stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint);
// Only render characters that fit within the scaled bounds
switch(opaque) {
case OpaqueType::FullyOpaque: {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
int bufferX = x + i + c_x1 + offsetX;
int bufferY = currentY + j + c_y1 + offsetY;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(bitmap[j * w + i])};
}
}
}
}
break;
}
case OpaqueType::SemiOpaque:
case OpaqueType::Transparent: {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
int bufferX = x + i + c_x1 + offsetX;
int bufferY = currentY + j + c_y1 + offsetY;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
std::uint8_t alpha = bitmap[j * w + i];
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
Vector<_Float16, 4, 4> dst = static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX];
_Float16 outA = srcA + dst.a * (1.0f - srcA);
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>(
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
outA
);
}
}
}
}
break;
}
}
x += (int)(ax * scale);
if (p + 1 < end) {
int next;
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
}
}
currentY += lineHeight;
}
}
void RenderText(std::span<const std::string_view> lines, float size, Vector<_Float16, 4> color, Font& font, std::uint8_t frame, TextAlignment alignment = TextAlignment::Left, TextVerticalAlignment verticalAlignment = TextVerticalAlignment::Top, std::int32_t offsetX = 0, std::int32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) {
float scale = stbtt_ScaleForPixelHeight(&font.font, size);
int baseline = (int)(font.ascent * scale);
std::uint32_t lineHeight = (font.ascent - font.descent) * scale;
std::uint32_t currentY = baseline;
std::uint32_t ogOffsetX = offsetX;
std::uint32_t ogOffsetY = offsetY;
for(std::string_view line : lines) {
offsetX = ogOffsetX;
offsetY = ogOffsetY;
std::int32_t lineWidth = 0;
for (const char c : line) {
int advance, lsb;
stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb);
lineWidth += (int)(advance * scale);
}
switch (alignment) {
case TextAlignment::Left:
break;
case TextAlignment::Center:
offsetX -= lineWidth / 2;
break;
case TextAlignment::Right:
offsetX -= lineWidth;
break;
}
switch (verticalAlignment) {
case TextVerticalAlignment::Top:
break;
case TextVerticalAlignment::Center:
offsetY += (lineHeight / 2) - (size);
break;
case TextVerticalAlignment::Bottom:
offsetY += lineHeight;
break;
}
const char* p = line.data();
const char* end = p + line.size();
while (p < end) {
int bytes;
int codepoint = utf8_decode(p, &bytes);
p += bytes;
int ax;
int lsb;
stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb);
int c_x1, c_y1, c_x2, c_y2;
stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2);
int w = c_x2 - c_x1;
int h = c_y2 - c_y1;
std::vector<unsigned char> bitmap(w * h);
stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint);
// Only render characters that fit within the scaled bounds
switch(opaque) {
case OpaqueType::FullyOpaque: {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
int bufferX = offsetX + i + c_x1;
int bufferY = currentY + j + c_y1 + offsetY;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(bitmap[j * w + i])};
}
}
}
break;
}
case OpaqueType::SemiOpaque:
case OpaqueType::Transparent: {
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
int bufferX = offsetX + i + c_x1;
int bufferY = currentY + j + c_y1 + offsetY;
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
std::uint8_t alpha = bitmap[j * w + i];
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
Vector<_Float16, 4, 4> dst = static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX];
_Float16 outA = srcA + dst.a * (1.0f - srcA);
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>(
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
outA
);
}
}
}
break;
}
}
offsetX += (int)(ax * scale);
if (p + 1 < end) {
int next;
offsetX += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
}
}
currentY += lineHeight;
}
}
};
} }
#endif #endif

View file

@ -16,6 +16,12 @@ You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
module;
#include "../lib/stb_truetype.h"
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
#include <vulkan/vulkan.h>
#endif
export module Crafter.Graphics:Rendertarget; export module Crafter.Graphics:Rendertarget;
import Crafter.Math; import Crafter.Math;
import Crafter.Asset; import Crafter.Asset;
@ -23,9 +29,12 @@ import std;
import :Types; import :Types;
import :Transform2D; import :Transform2D;
import :RenderingElement2DBase; import :RenderingElement2DBase;
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
import :Device;
import :VulkanBuffer;
#endif
export namespace Crafter { export namespace Crafter {
template<std::uint8_t Frames>
struct RendertargetBase { struct RendertargetBase {
#ifdef CRAFTER_TIMING #ifdef CRAFTER_TIMING
std::vector<std::tuple<const Transform*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>> renderTimings; std::vector<std::tuple<const Transform*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>> renderTimings;
@ -33,7 +42,6 @@ export namespace Crafter {
Transform2D transform; Transform2D transform;
std::uint16_t sizeX; std::uint16_t sizeX;
std::uint16_t sizeY; std::uint16_t sizeY;
std::uint8_t frame;
RendertargetBase() = default; RendertargetBase() = default;
RendertargetBase(std::uint16_t sizeX, std::uint16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){ RendertargetBase(std::uint16_t sizeX, std::uint16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){
transform.scaled.size.x = sizeX; transform.scaled.size.x = sizeX;
@ -43,11 +51,38 @@ export namespace Crafter {
} }
}; };
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
struct RenderingElement2DVulkanBase;
struct __attribute__((packed)) RenderingElement2DVulkanTransformInfo {
ScaleData2D scaled; // 0 - 8 bytes
std::uint16_t bufferX; // 8 - 2 bytes
std::uint16_t bufferY; // 10 - 2 bytes
//12 bytes total;
};
struct DescriptorHeapVulkan;
struct RendertargetVulkan : RendertargetBase {
std::uint8_t frame;
std::vector<RenderingElement2DVulkanBase*> elements;
VulkanBuffer<RenderingElement2DVulkanTransformInfo, true> transformBuffer[3];
RendertargetVulkan() = default;
RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY);
void UpdateElements();
void CreateBuffer(std::uint8_t frame);
void ReorderBuffer(std::uint8_t frame);
void WriteDescriptors(std::span<VkResourceDescriptorInfoEXT> infos, std::span<VkHostAddressRangeEXT> ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap);
void SetOrderResursive(Transform2D* elementTransform);
};
#endif
template<typename T, std::uint8_t Channels, std::uint8_t Alignment, std::uint8_t Frames> template<typename T, std::uint8_t Channels, std::uint8_t Alignment, std::uint8_t Frames>
struct Rendertarget : RendertargetBase<Frames> { struct Rendertarget : RendertargetBase {
Vector<T, Channels, Alignment>* buffer[Frames]; Vector<T, Channels, Alignment>* buffer[Frames];
Rendertarget() = default; Rendertarget() = default;
Rendertarget(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase<Frames>(sizeX, sizeY) { Rendertarget(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase(sizeX, sizeY) {
} }
void RenderElement(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>&& dirtyRects) { void RenderElement(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>&& dirtyRects) {
@ -148,16 +183,16 @@ export namespace Crafter {
void AddOldRects(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>& clipRects) { void AddOldRects(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>& clipRects) {
RenderingElement2DBase<T, Frames>* element = dynamic_cast<RenderingElement2DBase<T, Frames>*>(elementTransform); RenderingElement2DBase<T, Frames>* element = dynamic_cast<RenderingElement2DBase<T, Frames>*>(elementTransform);
if(element) { if(element) {
// if(element->scaled.position.x != element->oldScale[frame].position.x || element->scaled.position.y != element->oldScale[frame].position.y || element->scaled.size.x != element->oldScale[frame].size.x || element->scaled.size.y != element->oldScale[frame].size.y || element->redraw[frame]) { if(element->scaled.position.x != element->oldScale[frame].position.x || element->scaled.position.y != element->oldScale[frame].position.y || element->scaled.size.x != element->oldScale[frame].size.x || element->scaled.size.y != element->oldScale[frame].size.y || element->redraw[frame]) {
// clipRects.emplace_back(std::max(element->scaled.position.x, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.x + element->scaled.size.x), this->sizeX), std::max(element->scaled.position.y, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.y + element->scaled.size.y), this->sizeY)); clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY)));
// clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::uint16_t(0)), std::min(std::uint16_t(element->oldScale[frame].position.x + element->oldScale[frame].size.x), this->sizeX), std::max(element->oldScale[frame].position.y, std::uint16_t(0)), std::min(std::uint16_t(element->oldScale[frame].position.y + element->oldScale[frame].size.y), this->sizeY)); clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.x + element->oldScale[frame].size.x), std::int16_t(this->sizeX)), std::max(element->oldScale[frame].position.y, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.y + element->oldScale[frame].size.y), std::int16_t(this->sizeY)));
// element->oldScale[frame] = element->scaled; element->oldScale[frame] = element->scaled;
// element->redraw[frame] = false; element->redraw[frame] = false;
// } else if(element->redraw[frame]) { } else if(element->redraw[frame]) {
// clipRects.emplace_back(std::max(element->scaled.position.x, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.x + element->scaled.size.x), this->sizeX), std::max(element->scaled.position.y, std::uint16_t(0)), std::min(std::uint16_t(element->scaled.position.y + element->scaled.size.y), this->sizeY)); clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY)));
// element->oldScale[frame] = element->scaled; element->oldScale[frame] = element->scaled;
// element->redraw[frame] = false; element->redraw[frame] = false;
// } }
} }
for(Transform2D* child : elementTransform->children) { for(Transform2D* child : elementTransform->children) {
AddOldRects(child, frame, clipRects); AddOldRects(child, frame, clipRects);

View file

@ -49,14 +49,7 @@ export namespace Crafter {
Transform2D& operator=(Transform2D&) = delete; Transform2D& operator=(Transform2D&) = delete;
virtual ~Transform2D() = default; virtual ~Transform2D() = default;
virtual void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) { virtual void UpdatePosition(RendertargetBase& window, Transform2D& parent) {
ScaleElement(parent);
for(Transform2D* child : children) {
child->UpdatePosition(window, *this);
}
}
virtual void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) {
ScaleElement(parent); ScaleElement(parent);
for(Transform2D* child : children) { for(Transform2D* child : children) {
child->UpdatePosition(window, *this); child->UpdatePosition(window, *this);

View file

@ -49,7 +49,7 @@ export namespace Crafter {
std::chrono::duration<double> delta; std::chrono::duration<double> delta;
}; };
enum class CrafterKeys { enum class CrafterKeys : std::uint8_t {
// Alphabetic keys // Alphabetic keys
A, B, C, D, E, F, G, H, I, J, K, L, M, A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
@ -92,6 +92,151 @@ export namespace Crafter {
CrafterKeysMax CrafterKeysMax
}; };
constexpr std::string CrafterKeyToString(CrafterKeys key) {
switch (key) {
// Alphabetic keys
case CrafterKeys::A: return "A";
case CrafterKeys::B: return "B";
case CrafterKeys::C: return "C";
case CrafterKeys::D: return "D";
case CrafterKeys::E: return "E";
case CrafterKeys::F: return "F";
case CrafterKeys::G: return "G";
case CrafterKeys::H: return "H";
case CrafterKeys::I: return "I";
case CrafterKeys::J: return "J";
case CrafterKeys::K: return "K";
case CrafterKeys::L: return "L";
case CrafterKeys::M: return "M";
case CrafterKeys::N: return "N";
case CrafterKeys::O: return "O";
case CrafterKeys::P: return "P";
case CrafterKeys::Q: return "Q";
case CrafterKeys::R: return "R";
case CrafterKeys::S: return "S";
case CrafterKeys::T: return "T";
case CrafterKeys::U: return "U";
case CrafterKeys::V: return "V";
case CrafterKeys::W: return "W";
case CrafterKeys::X: return "X";
case CrafterKeys::Y: return "Y";
case CrafterKeys::Z: return "Z";
// Numeric keys
case CrafterKeys::_0: return "0";
case CrafterKeys::_1: return "1";
case CrafterKeys::_2: return "2";
case CrafterKeys::_3: return "3";
case CrafterKeys::_4: return "4";
case CrafterKeys::_5: return "5";
case CrafterKeys::_6: return "6";
case CrafterKeys::_7: return "7";
case CrafterKeys::_8: return "8";
case CrafterKeys::_9: return "9";
// Function keys
case CrafterKeys::F1: return "F1";
case CrafterKeys::F2: return "F2";
case CrafterKeys::F3: return "F3";
case CrafterKeys::F4: return "F4";
case CrafterKeys::F5: return "F5";
case CrafterKeys::F6: return "F6";
case CrafterKeys::F7: return "F7";
case CrafterKeys::F8: return "F8";
case CrafterKeys::F9: return "F9";
case CrafterKeys::F10: return "F10";
case CrafterKeys::F11: return "F11";
case CrafterKeys::F12: return "F12";
// Control keys
case CrafterKeys::Escape: return "Escape";
case CrafterKeys::Tab: return "Tab";
case CrafterKeys::Enter: return "Enter";
case CrafterKeys::Space: return "Space";
case CrafterKeys::Backspace: return "Backspace";
case CrafterKeys::Delete: return "Delete";
case CrafterKeys::Insert: return "Insert";
case CrafterKeys::Home: return "Home";
case CrafterKeys::End: return "End";
case CrafterKeys::PageUp: return "PageUp";
case CrafterKeys::PageDown: return "PageDown";
case CrafterKeys::CapsLock: return "CapsLock";
case CrafterKeys::NumLock: return "NumLock";
case CrafterKeys::ScrollLock: return "ScrollLock";
// Modifier keys
case CrafterKeys::LeftShift: return "LeftShift";
case CrafterKeys::RightShift: return "RightShift";
case CrafterKeys::LeftCtrl: return "LeftCtrl";
case CrafterKeys::RightCtrl: return "RightCtrl";
case CrafterKeys::LeftAlt: return "LeftAlt";
case CrafterKeys::RightAlt: return "RightAlt";
case CrafterKeys::LeftSuper: return "LeftSuper";
case CrafterKeys::RightSuper: return "RightSuper";
// Arrow keys
case CrafterKeys::Up: return "Up";
case CrafterKeys::Down: return "Down";
case CrafterKeys::Left: return "Left";
case CrafterKeys::Right: return "Right";
// Keypad keys
case CrafterKeys::keypad_0: return "Keypad0";
case CrafterKeys::keypad_1: return "Keypad1";
case CrafterKeys::keypad_2: return "Keypad2";
case CrafterKeys::keypad_3: return "Keypad3";
case CrafterKeys::keypad_4: return "Keypad4";
case CrafterKeys::keypad_5: return "Keypad5";
case CrafterKeys::keypad_6: return "Keypad6";
case CrafterKeys::keypad_7: return "Keypad7";
case CrafterKeys::keypad_8: return "Keypad8";
case CrafterKeys::keypad_9: return "Keypad9";
case CrafterKeys::keypad_enter: return "KeypadEnter";
case CrafterKeys::keypad_plus: return "KeypadPlus";
case CrafterKeys::keypad_minus: return "KeypadMinus";
case CrafterKeys::keypad_multiply: return "KeypadMultiply";
case CrafterKeys::keypad_divide: return "KeypadDivide";
case CrafterKeys::keypad_decimal: return "KeypadDecimal";
// Punctuation and special keys
case CrafterKeys::grave: return "Grave";
case CrafterKeys::minus: return "Minus";
case CrafterKeys::equal: return "Equal";
case CrafterKeys::bracket_left: return "BracketLeft";
case CrafterKeys::bracket_right: return "BracketRight";
case CrafterKeys::backslash: return "Backslash";
case CrafterKeys::semicolon: return "Semicolon";
case CrafterKeys::quote: return "Quote";
case CrafterKeys::comma: return "Comma";
case CrafterKeys::period: return "Period";
case CrafterKeys::slash: return "Slash";
case CrafterKeys::print_screen: return "PrintScreen";
case CrafterKeys::pause: return "Pause";
case CrafterKeys::menu: return "Menu";
// Additional keys
case CrafterKeys::volume_up: return "VolumeUp";
case CrafterKeys::volume_down: return "VolumeDown";
case CrafterKeys::volume_mute: return "VolumeMute";
case CrafterKeys::media_play: return "MediaPlay";
case CrafterKeys::media_stop: return "MediaStop";
case CrafterKeys::media_prev: return "MediaPrev";
case CrafterKeys::media_next: return "MediaNext";
case CrafterKeys::browser_back: return "BrowserBack";
case CrafterKeys::browser_forward: return "BrowserForward";
case CrafterKeys::browser_refresh: return "BrowserRefresh";
case CrafterKeys::browser_stop: return "BrowserStop";
case CrafterKeys::browser_search: return "BrowserSearch";
case CrafterKeys::browser_home: return "BrowserHome";
case CrafterKeys::launch_mail: return "LaunchMail";
case CrafterKeys::launch_calculator: return "LaunchCalculator";
case CrafterKeys::launch_media_player:return "LaunchMediaPlayer";
case CrafterKeys::CrafterKeysMax: return "Unknown";
}
return "Unknown";
}
template <typename T, typename T2> template <typename T, typename T2>
constexpr T AlignUp(T value, T2 alignment) { constexpr T AlignUp(T value, T2 alignment) {
return (value + alignment - 1) & ~(alignment - 1); return (value + alignment - 1) & ~(alignment - 1);

View file

@ -82,6 +82,7 @@ export namespace Crafter {
Event<CrafterKeys> onAnyKeyDown; Event<CrafterKeys> onAnyKeyDown;
Event<CrafterKeys> onAnyKeyHold; Event<CrafterKeys> onAnyKeyHold;
Event<CrafterKeys> onAnyKeyUp; Event<CrafterKeys> onAnyKeyUp;
Event<const std::string_view> onTextInput;
Event<void> onMouseRightClick; Event<void> onMouseRightClick;
Event<void> onMouseLeftClick; Event<void> onMouseLeftClick;
Event<void> onMouseRightHold; Event<void> onMouseRightHold;