mirror of
https://github.com/coah80/LegacyVulkEdition.git
synced 2026-06-18 07:11:52 +00:00
556 lines
19 KiB
C++
556 lines
19 KiB
C++
#include "stb_truetype.h"
|
|
#include "stb_image.h"
|
|
|
|
#include "vui_internal.h"
|
|
|
|
namespace vui {
|
|
|
|
Renderer::Renderer() : impl(new Impl) {}
|
|
Renderer::~Renderer() { delete impl; }
|
|
|
|
void Renderer::init(VkDevice device, VkPhysicalDevice physDevice,
|
|
VkQueue graphicsQueue, uint32_t queueFamily,
|
|
VkFormat swapchainFormat, uint32_t imageCount) {
|
|
impl->device = device;
|
|
impl->physDevice = physDevice;
|
|
impl->graphicsQueue = graphicsQueue;
|
|
impl->queueFamily = queueFamily;
|
|
impl->swapchainFormat = swapchainFormat;
|
|
impl->imageCount = imageCount;
|
|
|
|
impl->initRenderPass();
|
|
impl->initSampler();
|
|
impl->initDescriptorPool();
|
|
impl->initPipeline();
|
|
impl->initVertexBuffer(INITIAL_VERTEX_COUNT);
|
|
impl->createWhiteTexture();
|
|
|
|
impl->currentTransform = Mat3::identity();
|
|
memset(&impl->input, 0, sizeof(impl->input));
|
|
}
|
|
|
|
void Renderer::shutdown() {
|
|
vkDeviceWaitIdle(impl->device);
|
|
|
|
for (auto &tex : impl->textures) {
|
|
if (!tex.active) continue;
|
|
vkDestroyImageView(impl->device, tex.view, nullptr);
|
|
vkDestroyImage(impl->device, tex.image, nullptr);
|
|
vkFreeMemory(impl->device, tex.memory, nullptr);
|
|
}
|
|
impl->textures.clear();
|
|
impl->fonts.clear();
|
|
|
|
if (impl->vertexBuffer) {
|
|
vkDestroyBuffer(impl->device, impl->vertexBuffer, nullptr);
|
|
vkFreeMemory(impl->device, impl->vertexMemory, nullptr);
|
|
}
|
|
|
|
for (auto fb : impl->overlayFramebuffers)
|
|
vkDestroyFramebuffer(impl->device, fb, nullptr);
|
|
impl->overlayFramebuffers.clear();
|
|
|
|
if (impl->pipeline) vkDestroyPipeline(impl->device, impl->pipeline, nullptr);
|
|
if (impl->pipelineLayout) vkDestroyPipelineLayout(impl->device, impl->pipelineLayout, nullptr);
|
|
if (impl->descriptorPool) vkDestroyDescriptorPool(impl->device, impl->descriptorPool, nullptr);
|
|
if (impl->descriptorSetLayout) vkDestroyDescriptorSetLayout(impl->device, impl->descriptorSetLayout, nullptr);
|
|
if (impl->sampler) vkDestroySampler(impl->device, impl->sampler, nullptr);
|
|
if (impl->renderPass) vkDestroyRenderPass(impl->device, impl->renderPass, nullptr);
|
|
}
|
|
|
|
void Renderer::setOverlayFramebuffers(VkImageView *views, uint32_t count, uint32_t width, uint32_t height) {
|
|
for (auto fb : impl->overlayFramebuffers)
|
|
vkDestroyFramebuffer(impl->device, fb, nullptr);
|
|
impl->overlayFramebuffers.clear();
|
|
|
|
impl->overlayWidth = width;
|
|
impl->overlayHeight = height;
|
|
impl->overlayFramebuffers.resize(count);
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
VkFramebufferCreateInfo fci{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
|
|
fci.renderPass = impl->renderPass;
|
|
fci.attachmentCount = 1;
|
|
fci.pAttachments = &views[i];
|
|
fci.width = width;
|
|
fci.height = height;
|
|
fci.layers = 1;
|
|
vkCreateFramebuffer(impl->device, &fci, nullptr, &impl->overlayFramebuffers[i]);
|
|
}
|
|
}
|
|
|
|
VkRenderPass Renderer::getRenderPass() const {
|
|
return impl->renderPass;
|
|
}
|
|
|
|
void Renderer::beginFrame(int windowW, int windowH, int framebufferW, int framebufferH) {
|
|
impl->winW = windowW;
|
|
impl->winH = windowH;
|
|
impl->fbW = (framebufferW > 0) ? framebufferW : windowW;
|
|
impl->fbH = (framebufferH > 0) ? framebufferH : windowH;
|
|
impl->vertices.clear();
|
|
impl->drawCmds.clear();
|
|
impl->transformStack.clear();
|
|
impl->scissorStack.clear();
|
|
impl->currentTransform = Mat3::identity();
|
|
impl->frameActive = true;
|
|
|
|
memcpy(impl->input.prevMouseButtons, impl->input.mouseButtons, sizeof(impl->input.mouseButtons));
|
|
memcpy(impl->input.prevKeys, impl->input.keys, sizeof(impl->input.keys));
|
|
memset(impl->input.keyClicked, 0, sizeof(impl->input.keyClicked));
|
|
memset(impl->input.mouseClicked, 0, sizeof(impl->input.mouseClicked));
|
|
impl->input.scrollX = 0;
|
|
impl->input.scrollY = 0;
|
|
impl->input.charCount = 0;
|
|
}
|
|
|
|
void Renderer::endFrame(VkCommandBuffer cmd, uint32_t imageIndex) {
|
|
impl->frameActive = false;
|
|
|
|
if (impl->vertices.empty()) return;
|
|
|
|
if (impl->vertices.size() > impl->vertexBufferCapacity) {
|
|
uint32_t newCap = (uint32_t)impl->vertices.size() * 2;
|
|
impl->initVertexBuffer(newCap);
|
|
}
|
|
|
|
void *mapped;
|
|
vkMapMemory(impl->device, impl->vertexMemory, 0,
|
|
sizeof(Vertex) * impl->vertices.size(), 0, &mapped);
|
|
memcpy(mapped, impl->vertices.data(), sizeof(Vertex) * impl->vertices.size());
|
|
vkUnmapMemory(impl->device, impl->vertexMemory);
|
|
|
|
bool ownPass = !impl->overlayFramebuffers.empty() &&
|
|
imageIndex < (uint32_t)impl->overlayFramebuffers.size();
|
|
|
|
if (ownPass) {
|
|
VkRenderPassBeginInfo rpbi{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
|
|
rpbi.renderPass = impl->renderPass;
|
|
rpbi.framebuffer = impl->overlayFramebuffers[imageIndex];
|
|
rpbi.renderArea.extent = {impl->overlayWidth, impl->overlayHeight};
|
|
vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
}
|
|
|
|
float L = 0, R = (float)impl->winW, T = 0, B = (float)impl->winH;
|
|
float projection[16] = {
|
|
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
|
|
0.0f, 2.0f/(B-T), 0.0f, 0.0f,
|
|
0.0f, 0.0f, -1.0f, 0.0f,
|
|
-(R+L)/(R-L), -(B+T)/(B-T), 0.0f, 1.0f,
|
|
};
|
|
|
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, impl->pipeline);
|
|
vkCmdPushConstants(cmd, impl->pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT,
|
|
0, sizeof(projection), projection);
|
|
|
|
VkViewport viewport{0, 0, (float)impl->fbW, (float)impl->fbH, 0.0f, 1.0f};
|
|
vkCmdSetViewport(cmd, 0, 1, &viewport);
|
|
|
|
VkRect2D fullScissor{{0,0}, {(uint32_t)impl->fbW, (uint32_t)impl->fbH}};
|
|
vkCmdSetScissor(cmd, 0, 1, &fullScissor);
|
|
|
|
VkDeviceSize offset = 0;
|
|
vkCmdBindVertexBuffers(cmd, 0, 1, &impl->vertexBuffer, &offset);
|
|
|
|
float scaleX = (float)impl->fbW / (float)impl->winW;
|
|
float scaleY = (float)impl->fbH / (float)impl->winH;
|
|
|
|
int lastTexId = -1;
|
|
for (auto &dc : impl->drawCmds) {
|
|
if (dc.textureId != lastTexId) {
|
|
if (dc.textureId >= 0 && dc.textureId < (int)impl->textures.size()) {
|
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
impl->pipelineLayout, 0, 1,
|
|
&impl->textures[dc.textureId].descriptorSet,
|
|
0, nullptr);
|
|
}
|
|
lastTexId = dc.textureId;
|
|
}
|
|
|
|
if (dc.hasScissor) {
|
|
VkRect2D sc;
|
|
sc.offset.x = std::max(0, (int)(dc.scissorX * scaleX));
|
|
sc.offset.y = std::max(0, (int)(dc.scissorY * scaleY));
|
|
sc.extent.width = (uint32_t)std::max(0.0f, dc.scissorW * scaleX);
|
|
sc.extent.height = (uint32_t)std::max(0.0f, dc.scissorH * scaleY);
|
|
vkCmdSetScissor(cmd, 0, 1, &sc);
|
|
} else {
|
|
vkCmdSetScissor(cmd, 0, 1, &fullScissor);
|
|
}
|
|
|
|
vkCmdDraw(cmd, dc.vertexCount, 1, dc.vertexOffset, 0);
|
|
}
|
|
|
|
if (ownPass) {
|
|
vkCmdEndRenderPass(cmd);
|
|
}
|
|
}
|
|
|
|
void Renderer::drawRect(float x, float y, float w, float h, Color color) {
|
|
impl->addQuad(WHITE_TEXTURE_ID, x, y, x+w, y+h, 0, 0, 1, 1, color);
|
|
}
|
|
|
|
void Renderer::drawRectOutline(float x, float y, float w, float h, float t, Color color) {
|
|
drawRect(x, y, w, t, color);
|
|
drawRect(x, y+h-t, w, t, color);
|
|
drawRect(x, y+t, t, h-2*t, color);
|
|
drawRect(x+w-t, y+t, t, h-2*t, color);
|
|
}
|
|
|
|
void Renderer::drawImage(int texId, float x, float y, float w, float h, Color tint) {
|
|
impl->addQuad(texId, x, y, x+w, y+h, 0, 0, 1, 1, tint);
|
|
}
|
|
|
|
void Renderer::drawImageRegion(int texId, float x, float y, float w, float h,
|
|
float u0, float v0, float u1, float v1, Color tint) {
|
|
impl->addQuad(texId, x, y, x+w, y+h, u0, v0, u1, v1, tint);
|
|
}
|
|
|
|
void Renderer::drawImage9Slice(int texId, float x, float y, float w, float h,
|
|
float bL, float bR, float bT, float bB,
|
|
int texW, int texH, Color tint) {
|
|
float uL = bL / texW, uR = 1.0f - bR / texW;
|
|
float vT = bT / texH, vB = 1.0f - bB / texH;
|
|
float midW = w - bL - bR, midH = h - bT - bB;
|
|
|
|
drawImageRegion(texId, x, y, bL, bT, 0, 0, uL, vT, tint);
|
|
drawImageRegion(texId, x+bL, y, midW, bT, uL, 0, uR, vT, tint);
|
|
drawImageRegion(texId, x+bL+midW, y, bR, bT, uR, 0, 1, vT, tint);
|
|
|
|
drawImageRegion(texId, x, y+bT, bL, midH, 0, vT, uL, vB, tint);
|
|
drawImageRegion(texId, x+bL, y+bT, midW, midH, uL, vT, uR, vB, tint);
|
|
drawImageRegion(texId, x+bL+midW, y+bT, bR, midH, uR, vT, 1, vB, tint);
|
|
|
|
drawImageRegion(texId, x, y+bT+midH, bL, bB, 0, vB, uL, 1, tint);
|
|
drawImageRegion(texId, x+bL, y+bT+midH, midW, bB, uL, vB, uR, 1, tint);
|
|
drawImageRegion(texId, x+bL+midW, y+bT+midH, bR, bB, uR, vB, 1, 1, tint);
|
|
}
|
|
|
|
void Renderer::drawText(const char *text, float x, float y, Color color, int fontId) {
|
|
if (fontId < 0 || fontId >= (int)impl->fonts.size() || !impl->fonts[fontId].active) return;
|
|
auto &font = impl->fonts[fontId];
|
|
float cx = x;
|
|
float cy = y + font.ascent;
|
|
|
|
for (const char *p = text; *p; p++) {
|
|
int ch = (unsigned char)*p;
|
|
if (ch >= 128) continue;
|
|
auto &g = font.glyphs[ch];
|
|
if (g.x1 > g.x0) {
|
|
float gx = cx + g.xOff;
|
|
float gy = cy + g.yOff;
|
|
impl->addQuad(font.textureId,
|
|
gx, gy, gx + (g.x1 - g.x0), gy + (g.y1 - g.y0),
|
|
g.u0, g.v0, g.u1, g.v1, color);
|
|
}
|
|
cx += g.xAdvance;
|
|
}
|
|
}
|
|
|
|
void Renderer::drawTextCentered(const char *text, float x, float y, float w, float h,
|
|
Color color, int fontId) {
|
|
float tw = measureText(text, fontId);
|
|
float th = fontHeight(fontId);
|
|
float tx = x + (w - tw) * 0.5f;
|
|
float ty = y + (h - th) * 0.5f;
|
|
drawText(text, tx, ty, color, fontId);
|
|
}
|
|
|
|
void Renderer::drawTextWrapped(const char *text, float x, float y, float maxWidth,
|
|
Color color, int fontId) {
|
|
if (fontId < 0 || fontId >= (int)impl->fonts.size() || !impl->fonts[fontId].active) return;
|
|
auto &font = impl->fonts[fontId];
|
|
|
|
float cx = x;
|
|
float cy = y;
|
|
float lineH = font.ascent - font.descent + font.lineGap;
|
|
const char *wordStart = text;
|
|
|
|
while (*wordStart) {
|
|
while (*wordStart == ' ') {
|
|
int ch = (unsigned char)*wordStart;
|
|
if (ch < 128) cx += font.glyphs[ch].xAdvance;
|
|
wordStart++;
|
|
}
|
|
if (!*wordStart) break;
|
|
|
|
const char *wordEnd = wordStart;
|
|
float wordW = 0;
|
|
while (*wordEnd && *wordEnd != ' ') {
|
|
int ch = (unsigned char)*wordEnd;
|
|
if (ch < 128) wordW += font.glyphs[ch].xAdvance;
|
|
wordEnd++;
|
|
}
|
|
|
|
if (cx + wordW > x + maxWidth && cx > x) {
|
|
cx = x;
|
|
cy += lineH;
|
|
}
|
|
|
|
for (const char *p = wordStart; p < wordEnd; p++) {
|
|
int ch = (unsigned char)*p;
|
|
if (ch < 128) {
|
|
auto &g = font.glyphs[ch];
|
|
if (g.x1 > g.x0) {
|
|
float gx = cx + g.xOff;
|
|
float gy = cy + font.ascent + g.yOff;
|
|
impl->addQuad(font.textureId,
|
|
gx, gy, gx + (g.x1 - g.x0), gy + (g.y1 - g.y0),
|
|
g.u0, g.v0, g.u1, g.v1, color);
|
|
}
|
|
cx += g.xAdvance;
|
|
}
|
|
}
|
|
wordStart = wordEnd;
|
|
}
|
|
}
|
|
|
|
float Renderer::measureText(const char *text, int fontId) {
|
|
if (fontId < 0 || fontId >= (int)impl->fonts.size() || !impl->fonts[fontId].active) return 0;
|
|
auto &font = impl->fonts[fontId];
|
|
float w = 0;
|
|
for (const char *p = text; *p; p++) {
|
|
int ch = (unsigned char)*p;
|
|
if (ch < 128) w += font.glyphs[ch].xAdvance;
|
|
}
|
|
return w;
|
|
}
|
|
|
|
float Renderer::fontHeight(int fontId) {
|
|
if (fontId < 0 || fontId >= (int)impl->fonts.size() || !impl->fonts[fontId].active) return 0;
|
|
auto &f = impl->fonts[fontId];
|
|
return f.ascent - f.descent;
|
|
}
|
|
|
|
int Renderer::createTexture(int width, int height, const void *rgba) {
|
|
return impl->allocTexture(width, height, rgba);
|
|
}
|
|
|
|
void Renderer::updateTexture(int id, int x, int y, int w, int h, const void *rgba) {
|
|
if (id < 0 || id >= (int)impl->textures.size() || !impl->textures[id].active) return;
|
|
impl->uploadTextureData(impl->textures[id], x, y, w, h, rgba);
|
|
}
|
|
|
|
void Renderer::destroyTexture(int id) {
|
|
if (id < 0 || id >= (int)impl->textures.size() || !impl->textures[id].active) return;
|
|
auto &tex = impl->textures[id];
|
|
vkDeviceWaitIdle(impl->device);
|
|
vkDestroyImageView(impl->device, tex.view, nullptr);
|
|
vkDestroyImage(impl->device, tex.image, nullptr);
|
|
vkFreeMemory(impl->device, tex.memory, nullptr);
|
|
tex.active = false;
|
|
}
|
|
|
|
int Renderer::loadTexture(const char *path) {
|
|
int w, h, channels;
|
|
unsigned char *data = stbi_load(path, &w, &h, &channels, 4);
|
|
if (!data) {
|
|
fprintf(stderr, "vui: failed to load texture '%s'\n", path);
|
|
return -1;
|
|
}
|
|
int id = createTexture(w, h, data);
|
|
stbi_image_free(data);
|
|
return id;
|
|
}
|
|
|
|
int Renderer::loadFont(const char *path, float size) {
|
|
FILE *f = fopen(path, "rb");
|
|
if (!f) {
|
|
fprintf(stderr, "vui: failed to open font '%s'\n", path);
|
|
return -1;
|
|
}
|
|
fseek(f, 0, SEEK_END);
|
|
long len = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
std::vector<unsigned char> buf(len);
|
|
fread(buf.data(), 1, len, f);
|
|
fclose(f);
|
|
return loadFontFromMemory(buf.data(), (int)len, size);
|
|
}
|
|
|
|
int Renderer::loadFontFromMemory(const void *data, int dataSize, float size) {
|
|
(void)dataSize;
|
|
stbtt_fontinfo info;
|
|
if (!stbtt_InitFont(&info, (const unsigned char *)data, 0)) {
|
|
fprintf(stderr, "vui: failed to init font\n");
|
|
return -1;
|
|
}
|
|
|
|
float fontScale = stbtt_ScaleForPixelHeight(&info, size);
|
|
int ascent, descent, lineGap;
|
|
stbtt_GetFontVMetrics(&info, &ascent, &descent, &lineGap);
|
|
|
|
int atlasW = 512, atlasH = 512;
|
|
std::vector<unsigned char> bitmap(atlasW * atlasH, 0);
|
|
|
|
stbtt_bakedchar cdata[128];
|
|
stbtt_BakeFontBitmap((const unsigned char *)data, 0, size,
|
|
bitmap.data(), atlasW, atlasH, 0, 128, cdata);
|
|
|
|
std::vector<uint32_t> rgba(atlasW * atlasH);
|
|
for (int i = 0; i < atlasW * atlasH; i++) {
|
|
uint8_t a = bitmap[i];
|
|
rgba[i] = (uint32_t)a << 24 | 0x00FFFFFF;
|
|
}
|
|
|
|
int texId = createTexture(atlasW, atlasH, rgba.data());
|
|
|
|
FontEntry fe{};
|
|
fe.textureId = texId;
|
|
fe.size = size;
|
|
fe.ascent = ascent * fontScale;
|
|
fe.descent = descent * fontScale;
|
|
fe.lineGap = lineGap * fontScale;
|
|
fe.active = true;
|
|
|
|
for (int i = 0; i < 128; i++) {
|
|
auto &bc = cdata[i];
|
|
fe.glyphs[i].x0 = (float)bc.x0;
|
|
fe.glyphs[i].y0 = (float)bc.y0;
|
|
fe.glyphs[i].x1 = (float)bc.x1;
|
|
fe.glyphs[i].y1 = (float)bc.y1;
|
|
fe.glyphs[i].u0 = (float)bc.x0 / atlasW;
|
|
fe.glyphs[i].v0 = (float)bc.y0 / atlasH;
|
|
fe.glyphs[i].u1 = (float)bc.x1 / atlasW;
|
|
fe.glyphs[i].v1 = (float)bc.y1 / atlasH;
|
|
fe.glyphs[i].xAdvance = bc.xadvance;
|
|
fe.glyphs[i].xOff = bc.xoff;
|
|
fe.glyphs[i].yOff = bc.yoff;
|
|
}
|
|
|
|
int id = (int)impl->fonts.size();
|
|
impl->fonts.push_back(fe);
|
|
return id;
|
|
}
|
|
|
|
void Renderer::pushScissor(float x, float y, float w, float h) {
|
|
impl->scissorStack.push_back({x, y, w, h});
|
|
}
|
|
|
|
void Renderer::popScissor() {
|
|
if (!impl->scissorStack.empty())
|
|
impl->scissorStack.pop_back();
|
|
}
|
|
|
|
void Renderer::pushTransform() {
|
|
impl->transformStack.push_back(impl->currentTransform);
|
|
}
|
|
|
|
void Renderer::popTransform() {
|
|
if (!impl->transformStack.empty()) {
|
|
impl->currentTransform = impl->transformStack.back();
|
|
impl->transformStack.pop_back();
|
|
}
|
|
}
|
|
|
|
void Renderer::translate(float x, float y) {
|
|
Mat3 t;
|
|
t.m[2] = x;
|
|
t.m[5] = y;
|
|
impl->currentTransform = impl->currentTransform * t;
|
|
}
|
|
|
|
void Renderer::rotate(float angleDeg) {
|
|
float rad = angleDeg * 3.14159265f / 180.0f;
|
|
float c = cosf(rad), s = sinf(rad);
|
|
Mat3 r;
|
|
r.m[0] = c; r.m[1] = -s;
|
|
r.m[3] = s; r.m[4] = c;
|
|
impl->currentTransform = impl->currentTransform * r;
|
|
}
|
|
|
|
void Renderer::scale(float sx, float sy) {
|
|
Mat3 s;
|
|
s.m[0] = sx;
|
|
s.m[4] = sy;
|
|
impl->currentTransform = impl->currentTransform * s;
|
|
}
|
|
|
|
void Renderer::feedMousePosition(float x, float y) {
|
|
impl->input.mouseX = x;
|
|
impl->input.mouseY = y;
|
|
}
|
|
|
|
void Renderer::feedMouseButton(int button, bool pressed) {
|
|
if (button >= 0 && button < 5) {
|
|
if (pressed && !impl->input.mouseButtons[button])
|
|
impl->input.mouseClicked[button] = true;
|
|
impl->input.mouseButtons[button] = pressed;
|
|
}
|
|
}
|
|
|
|
void Renderer::feedMouseScroll(float dx, float dy) {
|
|
impl->input.scrollX += dx;
|
|
impl->input.scrollY += dy;
|
|
}
|
|
|
|
void Renderer::feedKey(int key, bool pressed) {
|
|
if (key >= 0 && key < 512) {
|
|
if (pressed && !impl->input.keys[key])
|
|
impl->input.keyClicked[key] = true;
|
|
impl->input.keys[key] = pressed;
|
|
}
|
|
}
|
|
|
|
void Renderer::feedChar(unsigned int codepoint) {
|
|
if (impl->input.charCount < 32)
|
|
impl->input.charBuffer[impl->input.charCount++] = codepoint;
|
|
}
|
|
|
|
bool Renderer::isMouseOver(float x, float y, float w, float h) {
|
|
return impl->input.mouseX >= x && impl->input.mouseX < x + w &&
|
|
impl->input.mouseY >= y && impl->input.mouseY < y + h;
|
|
}
|
|
|
|
bool Renderer::isMouseClicked(float x, float y, float w, float h) {
|
|
return isMouseOver(x, y, w, h) && impl->input.mouseClicked[0];
|
|
}
|
|
|
|
bool Renderer::isMouseDown(float x, float y, float w, float h) {
|
|
return isMouseOver(x, y, w, h) && impl->input.mouseButtons[0];
|
|
}
|
|
|
|
bool Renderer::isKeyPressed(int key) {
|
|
if (key < 0 || key >= 512) return false;
|
|
return impl->input.keys[key];
|
|
}
|
|
|
|
bool Renderer::isKeyClicked(int key) {
|
|
if (key < 0 || key >= 512) return false;
|
|
return impl->input.keyClicked[key];
|
|
}
|
|
|
|
bool Renderer::isMouseDownAnywhere() { return impl->input.mouseButtons[0]; }
|
|
bool Renderer::isMouseClickedAnywhere() {
|
|
return impl->input.mouseClicked[0];
|
|
}
|
|
|
|
float Renderer::mouseX() { return impl->input.mouseX; }
|
|
float Renderer::mouseY() { return impl->input.mouseY; }
|
|
float Renderer::scrollDeltaX() { return impl->input.scrollX; }
|
|
float Renderer::scrollDeltaY() { return impl->input.scrollY; }
|
|
|
|
int Renderer::pollChar() {
|
|
if (impl->input.charCount <= 0) return -1;
|
|
unsigned int c = impl->input.charBuffer[0];
|
|
for (int i = 1; i < impl->input.charCount; i++)
|
|
impl->input.charBuffer[i - 1] = impl->input.charBuffer[i];
|
|
impl->input.charCount--;
|
|
return static_cast<int>(c);
|
|
}
|
|
|
|
int Renderer::windowW() const { return impl->winW; }
|
|
int Renderer::windowH() const { return impl->winH; }
|
|
|
|
int Renderer::textureWidth(int id) const {
|
|
if (id < 0 || id >= (int)impl->textures.size() || !impl->textures[id].active) return 0;
|
|
return impl->textures[id].width;
|
|
}
|
|
|
|
int Renderer::textureHeight(int id) const {
|
|
if (id < 0 || id >= (int)impl->textures.size() || !impl->textures[id].active) return 0;
|
|
return impl->textures[id].height;
|
|
}
|
|
|
|
} // namespace vui
|