299 lines
8.2 KiB
C++
299 lines
8.2 KiB
C++
#include "RenderManager.h"
|
|
#include <SDL3/SDL.h>
|
|
#include <algorithm>
|
|
|
|
RenderManager::RenderManager() = default;
|
|
|
|
RenderManager::~RenderManager() {
|
|
if (m_initialized) {
|
|
shutdown();
|
|
}
|
|
}
|
|
|
|
bool RenderManager::initialize(int width, int height, const std::string& title) {
|
|
if (m_initialized) {
|
|
SDL_LogWarn(SDL_LOG_CATEGORY_RENDER, "RenderManager already initialized");
|
|
return true;
|
|
}
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Initializing RenderManager (%dx%d)", width, height);
|
|
|
|
// Create window
|
|
m_window = SDL_CreateWindow(
|
|
title.c_str(),
|
|
width, height,
|
|
SDL_WINDOW_RESIZABLE
|
|
);
|
|
|
|
if (!m_window) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to create window: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
// Create renderer
|
|
m_renderer = SDL_CreateRenderer(m_window, nullptr);
|
|
if (!m_renderer) {
|
|
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to create renderer: %s", SDL_GetError());
|
|
SDL_DestroyWindow(m_window);
|
|
m_window = nullptr;
|
|
return false;
|
|
}
|
|
|
|
// Enable VSync
|
|
SDL_SetRenderVSync(m_renderer, 1);
|
|
|
|
// Store window dimensions
|
|
m_windowWidth = width;
|
|
m_windowHeight = height;
|
|
m_logicalWidth = width;
|
|
m_logicalHeight = height;
|
|
|
|
// Set initial logical size
|
|
setLogicalSize(width, height);
|
|
|
|
m_initialized = true;
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "RenderManager initialized successfully");
|
|
return true;
|
|
}
|
|
|
|
void RenderManager::shutdown() {
|
|
if (!m_initialized) {
|
|
return;
|
|
}
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Shutting down RenderManager");
|
|
|
|
if (m_renderer) {
|
|
SDL_DestroyRenderer(m_renderer);
|
|
m_renderer = nullptr;
|
|
}
|
|
|
|
if (m_window) {
|
|
SDL_DestroyWindow(m_window);
|
|
m_window = nullptr;
|
|
}
|
|
|
|
m_initialized = false;
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "RenderManager shutdown complete");
|
|
}
|
|
|
|
void RenderManager::beginFrame() {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
// Trace beginFrame entry
|
|
{
|
|
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::beginFrame entry\n"); fclose(f); }
|
|
}
|
|
|
|
// Clear the screen (wrapped with trace)
|
|
clear(12, 12, 16, 255); // Dark background similar to original
|
|
|
|
// Trace after clear
|
|
{
|
|
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::beginFrame after clear\n"); fclose(f); }
|
|
}
|
|
}
|
|
|
|
void RenderManager::endFrame() {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
// Trace before present
|
|
{
|
|
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::endFrame before present\n"); fclose(f); }
|
|
}
|
|
|
|
SDL_RenderPresent(m_renderer);
|
|
|
|
// Trace after present
|
|
{
|
|
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::endFrame after present\n"); fclose(f); }
|
|
}
|
|
}
|
|
|
|
void RenderManager::setLogicalSize(int width, int height) {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
m_logicalWidth = width;
|
|
m_logicalHeight = height;
|
|
updateLogicalScale();
|
|
}
|
|
|
|
void RenderManager::setViewport(int x, int y, int width, int height) {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
SDL_Rect viewport = { x, y, width, height };
|
|
SDL_SetRenderViewport(m_renderer, &viewport);
|
|
// Keep cached logical viewport in sync if this matches our computed logical scale
|
|
m_logicalVP = viewport;
|
|
}
|
|
|
|
void RenderManager::setScale(float scaleX, float scaleY) {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
m_scaleX = scaleX;
|
|
m_scaleY = scaleY;
|
|
SDL_SetRenderScale(m_renderer, scaleX, scaleY);
|
|
}
|
|
|
|
void RenderManager::resetViewport() {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
// Reset to full window viewport and recompute logical scale/viewport
|
|
SDL_SetRenderViewport(m_renderer, nullptr);
|
|
updateLogicalScale();
|
|
}
|
|
|
|
void RenderManager::clear(Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
|
|
SDL_RenderClear(m_renderer);
|
|
}
|
|
|
|
void RenderManager::renderTexture(SDL_Texture* texture, const SDL_FRect* src, const SDL_FRect* dst) {
|
|
if (!m_initialized || !m_renderer || !texture) {
|
|
return;
|
|
}
|
|
|
|
// Trace renderTexture usage
|
|
{
|
|
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::renderTexture entry tex=%llu src=%p dst=%p\n", (unsigned long long)(uintptr_t)texture, (void*)src, (void*)dst); fclose(f); }
|
|
}
|
|
SDL_RenderTexture(m_renderer, texture, src, dst);
|
|
{
|
|
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::renderTexture after SDL_RenderTexture tex=%llu\n", (unsigned long long)(uintptr_t)texture); fclose(f); }
|
|
}
|
|
}
|
|
|
|
void RenderManager::renderTexture(SDL_Texture* texture, float x, float y) {
|
|
if (!texture) {
|
|
return;
|
|
}
|
|
|
|
float w, h;
|
|
SDL_GetTextureSize(texture, &w, &h);
|
|
SDL_FRect dst = { x, y, w, h };
|
|
renderTexture(texture, nullptr, &dst);
|
|
}
|
|
|
|
void RenderManager::renderTexture(SDL_Texture* texture, float x, float y, float w, float h) {
|
|
if (!texture) {
|
|
return;
|
|
}
|
|
|
|
SDL_FRect dst = { x, y, w, h };
|
|
renderTexture(texture, nullptr, &dst);
|
|
}
|
|
|
|
void RenderManager::renderRect(const SDL_FRect& rect, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
|
|
SDL_RenderFillRect(m_renderer, &rect);
|
|
}
|
|
|
|
void RenderManager::renderLine(float x1, float y1, float x2, float y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
|
|
SDL_RenderLine(m_renderer, x1, y1, x2, y2);
|
|
}
|
|
|
|
void RenderManager::renderPoint(float x, float y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(m_renderer, r, g, b, a);
|
|
SDL_RenderPoint(m_renderer, x, y);
|
|
}
|
|
|
|
void RenderManager::handleWindowResize(int newWidth, int newHeight) {
|
|
if (!m_initialized) {
|
|
return;
|
|
}
|
|
|
|
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Window resized to %dx%d", newWidth, newHeight);
|
|
|
|
m_windowWidth = newWidth;
|
|
m_windowHeight = newHeight;
|
|
updateLogicalScale();
|
|
}
|
|
|
|
void RenderManager::setFullscreen(bool fullscreen) {
|
|
if (!m_initialized || !m_window) {
|
|
return;
|
|
}
|
|
|
|
if (m_isFullscreen == fullscreen) {
|
|
return;
|
|
}
|
|
|
|
SDL_SetWindowFullscreen(m_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
|
|
m_isFullscreen = fullscreen;
|
|
|
|
// Update window size after fullscreen change
|
|
SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight);
|
|
updateLogicalScale();
|
|
}
|
|
|
|
void RenderManager::getWindowSize(int& width, int& height) const {
|
|
width = m_windowWidth;
|
|
height = m_windowHeight;
|
|
}
|
|
|
|
void RenderManager::getTextureSize(SDL_Texture* tex, int& w, int& h) const {
|
|
if (!tex) { w = 0; h = 0; return; }
|
|
// SDL3 provides SDL_GetTextureSize which accepts float or int pointers depending on overloads
|
|
float fw = 0.0f, fh = 0.0f;
|
|
SDL_GetTextureSize(tex, &fw, &fh);
|
|
w = int(fw + 0.5f);
|
|
h = int(fh + 0.5f);
|
|
}
|
|
|
|
void RenderManager::updateLogicalScale() {
|
|
if (!m_initialized || !m_renderer) {
|
|
return;
|
|
}
|
|
|
|
// Calculate scale to fit logical size into window
|
|
float scaleX = static_cast<float>(m_windowWidth) / static_cast<float>(m_logicalWidth);
|
|
float scaleY = static_cast<float>(m_windowHeight) / static_cast<float>(m_logicalHeight);
|
|
|
|
// Use uniform scaling to maintain aspect ratio
|
|
float scale = std::min(scaleX, scaleY);
|
|
if (scale <= 0.0f) {
|
|
scale = 1.0f;
|
|
}
|
|
|
|
setScale(scale, scale);
|
|
// Compute centered logical viewport that preserves aspect ratio and is centered in window
|
|
int vpW = static_cast<int>(m_logicalWidth * scale);
|
|
int vpH = static_cast<int>(m_logicalHeight * scale);
|
|
int vpX = (m_windowWidth - vpW) / 2;
|
|
int vpY = (m_windowHeight - vpH) / 2;
|
|
SDL_Rect vp{ vpX, vpY, vpW, vpH };
|
|
SDL_SetRenderViewport(m_renderer, &vp);
|
|
|
|
// Cache logical viewport and scale for callers
|
|
m_logicalVP = vp;
|
|
m_logicalScale = scale;
|
|
}
|