#include <UI.h> #include <string> #include <algorithm> #include <chrono> #include <thread> #include <vector> #include "constants.h" #include "renderer.h" void UI::pushToRenderer(Renderer* renderer) { float leftX = xpos - 0.5 * (width / SCREEN_X); float rightX = xpos + 0.5 * (width / SCREEN_X); float topY = ypos + 0.5 * (height / SCREEN_Y); float bottomY = ypos - 0.5 * (height / SCREEN_Y); Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY }; Renderer::ColorRGB color(R, G, B); renderer->drawQuad(quadVertices, color, texIndex, uvs); } bool UI::isHovered(GLFWwindow* window) { double x, y; glfwGetCursorPos(window, &x, &y); float xNDC = static_cast<float>(x) / SCREEN_X * 2.0f - 1.0f; float yNDC = 1.0f - static_cast<float>(y) / SCREEN_Y * 2.0f; float leftX = xpos - 0.5 * (width / SCREEN_X); float rightX = xpos + 0.5 * (width / SCREEN_X); float topY = ypos + 0.5 * (height / SCREEN_Y); float bottomY = ypos - 0.5 * (height / SCREEN_Y); return (xNDC >= leftX && xNDC <= rightX && yNDC >= bottomY && yNDC <= topY); } bool UI::isClicked(GLFWwindow* window) { return (isHovered(window) && (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)); } Button::Button(float height, float width, float xpos, float ypos, int R, int G, int B, int texIndex = 0, const float* uvs = nullptr) { this->height = height; this->width = width; this->xpos = xpos; this->ypos = ypos; this->R = R; this->G = G; this->B = B; this->texIndex = texIndex; this->uvs = uvs; } Button::Button(float height, float width, float xpos, float ypos, int R, int G, int B, int texIndex, std::string message) { this->height = height; this->width = width; this->xpos = xpos; this->ypos = ypos; this->R = R; this->G = G; this->B = B; this->texIndex = texIndex; this->uvs = nullptr; this->message = message; } void Button::pushToRenderer(Renderer* renderer) { // Draw button background float leftX = xpos - 0.5f * (width / SCREEN_X); float rightX = xpos + 0.5f * (width / SCREEN_X); float topY = ypos + 0.5f * (height / SCREEN_Y); float bottomY = ypos - 0.5f * (height / SCREEN_Y); Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY }; Renderer::ColorRGB color(R, G, B); if (message.empty()) { renderer->drawQuad(quadVertices, color, texIndex, uvs); // default rendering image if no text } else { renderer->drawQuad(quadVertices, color, 0, nullptr); // draws blank background if we are rendering text int charNum = static_cast<int>(message.length()); float maxFontSizeByWidth = width / charNum; float fontSize = std::min(maxFontSizeByWidth, height * 0.8f); // .8 height for padding float charWidth = fontSize / SCREEN_X; float charHeight = fontSize / SCREEN_Y; float totalTextWidth = charWidth * charNum; float startX = xpos - (totalTextWidth) / 2.0f; for (int i = 0; i < charNum; i++) { char c = message[i]; int charSetIndex = std::clamp((int)c - 32, 0, 95); int col = charSetIndex % 16; int row = charSetIndex / 16; float ndcStartX = startX + i * charWidth; float ndcEndX = ndcStartX + charWidth; float ndcStartY = ypos - charHeight * 0.5f; float ndcEndY = ypos + charHeight * 0.5f; Renderer::QuadVertices textVertices = { ndcStartX, ndcEndY, ndcEndX, ndcEndY, ndcStartX, ndcStartY, ndcEndX, ndcStartY }; float uv_x = col * 0.0625f; float uv_y = row * 0.0625f; float uv_size = 0.0625f; float uvs[8] = { uv_x, uv_y + uv_size, uv_x, uv_y, uv_x + uv_size, uv_y, uv_x + uv_size, uv_y + uv_size }; renderer->drawQuad(textVertices, color, texIndex, uvs); } } } Text::Text(float width, float xpos, float ypos, int texIndex, std::string message): UI() { this->width = width; this->height = width / message.length(); this->xpos = xpos; this->ypos = ypos; this->texIndex = texIndex; this->message = message; } void Text::pushToRenderer(Renderer* renderer) { char charset[] = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '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', '[', '\\', ']', '^', '_' }; int charNum = message.length(); float maxCharSize = width / charNum; for (int i = 0; i < charNum; i++) { char c = message[i]; int charSetIndex = std::clamp((int)c - 32, 0, 95); int col = charSetIndex % 16; int row = charSetIndex / 16; float charWidth = width / charNum / (SCREEN_X); float charHeight = height / (SCREEN_Y); float ndcStartX = xpos + (i * charWidth) - (width * 0.5) / SCREEN_X; float ndcEndX = ndcStartX + charWidth; float ndcStartY = ypos - charHeight * 0.5f; float ndcEndY = ypos + charHeight * 0.5f; Renderer::QuadVertices quadVertices = { ndcStartX, ndcEndY, // BL ndcEndX, ndcEndY, // BR ndcStartX, ndcStartY, // TL ndcEndX, ndcStartY // TR }; float uv_x = col * 0.0625f; float uv_y = row * 0.0625f; float uv_size = 0.0625f; float uvs[8] = { uv_x, uv_y + uv_size, // Top-Left (x3) uv_x, uv_y, // Bottom-Left (x1) uv_x + uv_size, uv_y, // Bottom-Right (x2) uv_x + uv_size, uv_y + uv_size // Top-Right (x4) }; Renderer::ColorRGB color(255, 255, 255); renderer->drawQuad(quadVertices, color, texIndex, uvs); } } TextBox::TextBox(float width, float fontSize, float xpos, float ypos, int texIndex, std::string defaultMessage) : Text(width, xpos, ypos, texIndex, defaultMessage) { this->fontSize = fontSize; } void TextBox::pushToRenderer(Renderer* renderer) { char charset[] = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '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', '[', '\\', ']', '^', '_' }; int charNum = message.length(); float charWidth = fontSize / SCREEN_X; float charHeight = fontSize / SCREEN_Y; float totalTextWidth = charWidth * charNum; float actualCharWidth = charWidth; if (totalTextWidth > width / SCREEN_X) { actualCharWidth = (width / SCREEN_X) / charNum; } else if (totalTextWidth < (width / SCREEN_X) * 0.1f) { actualCharWidth = (width / SCREEN_X) * 0.1f / charNum; } float startX = xpos - (actualCharWidth * charNum) / 2.0f; float leftX = xpos - 0.5f * (width / SCREEN_X); float rightX = xpos + 0.5f * (width / SCREEN_X); float topY = ypos + 0.5f * (fontSize / SCREEN_Y); float bottomY = ypos - 0.5f * (fontSize / SCREEN_Y); Renderer::QuadVertices backgroundVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY }; Renderer::ColorRGB color(255, 255, 255); renderer->drawQuad(backgroundVertices, color, 0, nullptr); for (int i = 0; i < charNum; i++) { char c = message[i]; int charSetIndex = std::clamp((int)c - 32, 0, 95); int col = charSetIndex % 16; int row = charSetIndex / 16; float ndcStartX = startX + i * actualCharWidth; float ndcEndX = ndcStartX + actualCharWidth; float ndcStartY = ypos - charHeight * 0.5f; float ndcEndY = ypos + charHeight * 0.5f; Renderer::QuadVertices quadVertices = { ndcStartX, ndcEndY, // BL ndcEndX, ndcEndY, // BR ndcStartX, ndcStartY, // TL ndcEndX, ndcStartY // TR }; float uv_x = col * 0.0625f; float uv_y = row * 0.0625f; float uv_size = 0.0625f; float uvs[8] = { uv_x, uv_y + uv_size, uv_x, uv_y, uv_x + uv_size, uv_y, uv_x + uv_size, uv_y + uv_size }; renderer->drawQuad(quadVertices, color, texIndex, uvs); } } void TextBox::collectInput(GLFWwindow* window) { char allowedChars[] = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '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', '[', '\\', ']', '^', '_' }; for (int key = GLFW_KEY_SPACE; key <= GLFW_KEY_Z; ++key) { int state = glfwGetKey(window, key); if (state == GLFW_PRESS && !keyHeld[key]) { char c = static_cast<char>(key); if (std::find(std::begin(allowedChars), std::end(allowedChars), c) != std::end(allowedChars)) { message += c; } keyHeld[key] = true; } else if (state == GLFW_RELEASE) { keyHeld[key] = false; } } // Backspace logic int backspaceState = glfwGetKey(window, GLFW_KEY_BACKSPACE); if (backspaceState == GLFW_PRESS && !keyHeld[GLFW_KEY_BACKSPACE]) { if (!message.empty()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); message.pop_back(); } } if (message.empty()) { //message = " "; } } DrawingBoard::DrawingBoard(float height, float width, float xpos, float ypos, std::string saveAs) // DrawingBoard drawingBoard(300.0f, 600.0f, 0.0f, 0.2f, "DRAW CHARACTER", "playerTex.png"); : clearBtn(*new Button( height * 0.2f, width * 0.5f, xpos + width * 0.25f / SCREEN_X, ypos - 0.7f * (height / SCREEN_Y) + height * 0.2f * 0.5f / SCREEN_Y, 200, 200, 100, 4, std::string("CLEAR"))), saveBtn(*new Button( height * 0.2f, width * 0.5f, xpos - width * 0.25f / SCREEN_X, ypos - 0.7f * (height / SCREEN_Y) + height * 0.2f * 0.5f / SCREEN_Y, 200, 100, 200, 4, std::string("SAVE"))) { this->height = height; this->width = width; this->xpos = xpos; this->ypos = ypos; this->saveAs = saveAs; } void DrawingBoard::pushToRenderer(Renderer* renderer) { float leftX = xpos - 0.5f * (width / SCREEN_X); float rightX = xpos + 0.5f * (width / SCREEN_X); float topY = ypos + 0.5f * (height / SCREEN_Y); float bottomY = ypos - 0.5f * (height / SCREEN_Y); Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY }; Renderer::ColorRGB color(255, 255, 255); renderer->drawQuad(quadVertices, color, texIndex, nullptr); for (const auto& point : drawingPoints) { float pointSize = 10; float leftX = point.first - (pointSize / SCREEN_X); float rightX = point.first + (pointSize / SCREEN_X); float topY = point.second + (pointSize / SCREEN_Y); float bottomY = point.second - (pointSize / SCREEN_Y); Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY }; Renderer::ColorRGB color(0, 0, 0); renderer->drawQuad(quadVertices, color, 0, nullptr); } clearBtn.pushToRenderer(renderer); saveBtn.pushToRenderer(renderer); } bool DrawingBoard::isClicked(GLFWwindow* window) { if (this->clearBtn.isClicked(window)) { this->drawingPoints.clear(); saved = false; this->saveBtn.message = "SAVE"; } else if (saveBtn.isClicked(window)) { if (saved || saveRequested) return false; saveRequested = true; this->saveBtn.message = "SAVED!"; } else if (isHovered(window) && (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)) { double x, y; glfwGetCursorPos(window, &x, &y); float xNDC = static_cast<float>(x) / SCREEN_X * 2.0f - 1.0f; float yNDC = 1.0f - static_cast<float>(y) / SCREEN_Y * 2.0f; drawingPoints.push_back(std::make_pair(xNDC, yNDC)); //std::cout << "Added point: " << xNDC << ", " << yNDC << std::endl; saved = false; this->saveBtn.message = "SAVE"; return true; } return false; }