feat(渲染): 添加虚拟分辨率支持并重构相机系统
实现虚拟分辨率渲染系统,支持不同缩放模式 重构相机控制器以使用虚拟分辨率计算可见区域 移除硬编码的屏幕尺寸,改为动态获取 添加分辨率状态管理及相关工具函数 更新窗口和渲染器以处理分辨率变化
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <frostbite2D/core/window.h>
|
||||
#include <frostbite2D/graphics/camera.h>
|
||||
#include <frostbite2D/graphics/render_resolution.h>
|
||||
#include <frostbite2D/types/type_alias.h>
|
||||
#include <frostbite2D/event/event.h>
|
||||
#include <string>
|
||||
@@ -37,6 +38,26 @@ struct AppConfig {
|
||||
*/
|
||||
PlatformType targetPlatform = PlatformType::Auto;
|
||||
|
||||
/**
|
||||
* @brief 是否启用虚拟分辨率
|
||||
*/
|
||||
bool useVirtualResolution = false;
|
||||
|
||||
/**
|
||||
* @brief 虚拟分辨率宽度
|
||||
*/
|
||||
int virtualWidth = 0;
|
||||
|
||||
/**
|
||||
* @brief 虚拟分辨率高度
|
||||
*/
|
||||
int virtualHeight = 0;
|
||||
|
||||
/**
|
||||
* @brief 虚拟分辨率缩放模式
|
||||
*/
|
||||
ResolutionScaleMode resolutionMode = ResolutionScaleMode::Fit;
|
||||
|
||||
/**
|
||||
* @brief 创建默认配置
|
||||
* @return 默认的应用配置实例
|
||||
|
||||
@@ -8,30 +8,22 @@
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 鼠标指针类型
|
||||
*/
|
||||
enum class CursorType {
|
||||
Arrow, ///< 指针
|
||||
TextInput, ///< 文本
|
||||
Hand, ///< 手
|
||||
SizeAll, ///< 指向四个方向的箭头
|
||||
SizeWE, ///< 指向左右方向的箭头
|
||||
SizeNS, ///< 指向上下方向的箭头
|
||||
SizeNESW, ///< 指向左下到右上方向的箭头
|
||||
SizeNWSE, ///< 指向左上到右下方向的箭头
|
||||
No, ///< 禁止
|
||||
Arrow,
|
||||
TextInput,
|
||||
Hand,
|
||||
SizeAll,
|
||||
SizeWE,
|
||||
SizeNS,
|
||||
SizeNESW,
|
||||
SizeNWSE,
|
||||
No,
|
||||
};
|
||||
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 分辨率
|
||||
*/
|
||||
struct Resolution {
|
||||
uint32_t width = 0; ///< 分辨率宽度
|
||||
uint32_t height = 0; ///< 分辨率高度
|
||||
uint32_t refresh_rate = 0; ///< 刷新率
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t refresh_rate = 0;
|
||||
|
||||
Resolution() = default;
|
||||
|
||||
@@ -39,41 +31,33 @@ struct Resolution {
|
||||
: width(width), height(height), refresh_rate(refresh_rate) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 图标
|
||||
*/
|
||||
struct Icon {
|
||||
Icon() = default;
|
||||
|
||||
Icon(std::string file_path) : file_path(file_path) {}
|
||||
|
||||
std::string file_path; ///< 文件路径
|
||||
std::string file_path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
uint32_t resource_id = 0; ///< 资源ID,仅在windows上生效
|
||||
uint32_t resource_id = 0;
|
||||
|
||||
Icon(uint32_t resource_id) : resource_id(resource_id) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 窗口设置
|
||||
*/
|
||||
struct WindowConfig {
|
||||
uint32_t width = 640; ///< 窗口宽度
|
||||
uint32_t height = 480; ///< 窗口高度
|
||||
std::string title = "frostbite2D Game"; ///< 窗口标题
|
||||
Icon icon; ///< 窗口图标
|
||||
bool resizable = false; ///< 窗口大小可调整
|
||||
bool fullscreen = false; ///< 窗口全屏
|
||||
bool borderless = false; ///< 无边框窗口
|
||||
bool decorated = true; ///< 窗口装饰
|
||||
int multisamples = 0; ///< 多重采样数
|
||||
bool centered = true; ///< 窗口是否居中
|
||||
bool vsync = true; ///< 是否启用垂直同步
|
||||
bool showCursor = true; ///< 是否显示光标
|
||||
uint32_t width = 640;
|
||||
uint32_t height = 480;
|
||||
std::string title = "frostbite2D Game";
|
||||
Icon icon;
|
||||
bool resizable = false;
|
||||
bool fullscreen = false;
|
||||
bool borderless = false;
|
||||
bool decorated = true;
|
||||
int multisamples = 0;
|
||||
bool centered = true;
|
||||
bool vsync = true;
|
||||
bool showCursor = true;
|
||||
};
|
||||
|
||||
class Window {
|
||||
@@ -81,179 +65,55 @@ public:
|
||||
Window() = default;
|
||||
~Window() = default;
|
||||
|
||||
/**
|
||||
* @brief 创建窗口
|
||||
* @param cfg 窗口配置
|
||||
* @return 创建是否成功
|
||||
*/
|
||||
virtual bool create(const WindowConfig &cfg);
|
||||
|
||||
/**
|
||||
* @brief 销毁窗口
|
||||
*/
|
||||
virtual bool create(const WindowConfig& cfg);
|
||||
virtual void destroy();
|
||||
|
||||
/**
|
||||
* @brief 轮询事件
|
||||
*/
|
||||
virtual void poll();
|
||||
|
||||
/**
|
||||
* @brief 交换缓冲区
|
||||
*/
|
||||
virtual void swap();
|
||||
|
||||
/**
|
||||
* @brief 设置窗口关闭标志
|
||||
*/
|
||||
virtual void close();
|
||||
|
||||
/**
|
||||
* @brief 设置窗口标题
|
||||
*/
|
||||
virtual void setTitle(const std::string &title);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口大小
|
||||
*/
|
||||
virtual void setTitle(const std::string& title);
|
||||
virtual void setSize(int w, int h);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口位置
|
||||
*/
|
||||
virtual void setPos(int x, int y);
|
||||
|
||||
/**
|
||||
* @brief 设置全屏模式
|
||||
*/
|
||||
virtual void setFullscreen(bool fs);
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*/
|
||||
virtual void setVSync(bool vsync);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口可见性
|
||||
*/
|
||||
virtual void setVisible(bool visible);
|
||||
|
||||
/**
|
||||
* @brief 获取窗口宽度
|
||||
*/
|
||||
virtual int width() const;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口高度
|
||||
*/
|
||||
virtual int height() const;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口大小
|
||||
*/
|
||||
virtual int drawableWidth() const;
|
||||
virtual int drawableHeight() const;
|
||||
virtual Size size() const;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
*/
|
||||
virtual Vec2 pos() const;
|
||||
|
||||
/**
|
||||
* @brief 是否全屏
|
||||
*/
|
||||
virtual bool fullscreen() const;
|
||||
|
||||
/**
|
||||
* @brief 是否启用垂直同步
|
||||
*/
|
||||
virtual bool vsync() const;
|
||||
|
||||
/**
|
||||
* @brief 窗口是否获得焦点
|
||||
*/
|
||||
virtual bool focused() const;
|
||||
|
||||
/**
|
||||
* @brief 窗口是否最小化
|
||||
*/
|
||||
virtual bool minimized() const;
|
||||
|
||||
/**
|
||||
* @brief 获取内容缩放X
|
||||
*/
|
||||
virtual float scaleX() const;
|
||||
|
||||
/**
|
||||
* @brief 获取内容缩放Y
|
||||
*/
|
||||
virtual float scaleY() const;
|
||||
|
||||
/**
|
||||
* @brief 设置光标形状
|
||||
*/
|
||||
virtual bool refreshMetrics(bool emitResize = false);
|
||||
virtual void setCursor(CursorType cursor);
|
||||
|
||||
/**
|
||||
* @brief 显示/隐藏光标
|
||||
*/
|
||||
virtual void showCursor(bool show);
|
||||
|
||||
/**
|
||||
* @brief 锁定/解锁光标
|
||||
*/
|
||||
virtual void lockCursor(bool lock);
|
||||
|
||||
/**
|
||||
* @brief 窗口大小改变回调
|
||||
*/
|
||||
using ResizeCb = std::function<void(int, int)>;
|
||||
|
||||
/**
|
||||
* @brief 窗口关闭回调
|
||||
*/
|
||||
using CloseCb = std::function<void()>;
|
||||
|
||||
/**
|
||||
* @brief 窗口焦点改变回调
|
||||
*/
|
||||
using FocusCb = std::function<void(bool)>;
|
||||
|
||||
/**
|
||||
* @brief 设置大小改变回调
|
||||
*/
|
||||
virtual void onResize(ResizeCb cb);
|
||||
|
||||
/**
|
||||
* @brief 设置关闭回调
|
||||
*/
|
||||
virtual void onClose(CloseCb cb);
|
||||
|
||||
/**
|
||||
* @brief 设置焦点改变回调
|
||||
*/
|
||||
virtual void onFocus(FocusCb cb);
|
||||
virtual void* native() const;
|
||||
|
||||
/**
|
||||
* @brief 获取原生窗口句柄
|
||||
*/
|
||||
virtual void *native() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 SDL 窗口句柄
|
||||
*/
|
||||
SDL_Window *sdlWindow() const { return sdlWindow_; }
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 上下文
|
||||
*/
|
||||
SDL_Window* sdlWindow() const { return sdlWindow_; }
|
||||
SDL_GLContext glContext() const { return glContext_; }
|
||||
|
||||
private:
|
||||
SDL_Window *sdlWindow_ = nullptr;
|
||||
SDL_Window* sdlWindow_ = nullptr;
|
||||
SDL_GLContext glContext_ = nullptr;
|
||||
|
||||
int width_ = 1280;
|
||||
int height_ = 720;
|
||||
int drawableWidth_ = 1280;
|
||||
int drawableHeight_ = 720;
|
||||
bool fullscreen_ = false;
|
||||
bool vsync_ = true;
|
||||
bool focused_ = true;
|
||||
@@ -269,4 +129,4 @@ private:
|
||||
FocusCb focusCb_;
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
} // namespace frostbite2D
|
||||
|
||||
@@ -17,6 +17,8 @@ public:
|
||||
float getZoom() const { return zoom_; }
|
||||
int getViewportWidth() const { return viewportWidth_; }
|
||||
int getViewportHeight() const { return viewportHeight_; }
|
||||
float getVisibleWidth() const;
|
||||
float getVisibleHeight() const;
|
||||
|
||||
void lookAt(const Vec2& target);
|
||||
void move(const Vec2& delta);
|
||||
|
||||
43
Frostbite2D/include/frostbite2D/graphics/render_resolution.h
Normal file
43
Frostbite2D/include/frostbite2D/graphics/render_resolution.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <frostbite2D/types/type_math.h>
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
enum class ResolutionScaleMode {
|
||||
Disabled,
|
||||
Fit,
|
||||
FitHeight,
|
||||
};
|
||||
|
||||
struct RenderResolutionState {
|
||||
int windowWidth = 1280;
|
||||
int windowHeight = 720;
|
||||
|
||||
int drawableWidth = 1280;
|
||||
int drawableHeight = 720;
|
||||
|
||||
int windowViewportX = 0;
|
||||
int windowViewportY = 0;
|
||||
int windowViewportWidth = 1280;
|
||||
int windowViewportHeight = 720;
|
||||
|
||||
int viewportX = 0;
|
||||
int viewportY = 0;
|
||||
int viewportWidth = 1280;
|
||||
int viewportHeight = 720;
|
||||
|
||||
float contentScaleX = 1.0f;
|
||||
float contentScaleY = 1.0f;
|
||||
float scaleX = 1.0f;
|
||||
float scaleY = 1.0f;
|
||||
|
||||
Rect getWindowViewportRect() const {
|
||||
return Rect(static_cast<float>(windowViewportX),
|
||||
static_cast<float>(windowViewportY),
|
||||
static_cast<float>(windowViewportWidth),
|
||||
static_cast<float>(windowViewportHeight));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace frostbite2D
|
||||
@@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <frostbite2D/graphics/types.h>
|
||||
#include <frostbite2D/graphics/texture.h>
|
||||
#include <frostbite2D/graphics/shader.h>
|
||||
#include <frostbite2D/graphics/shader_manager.h>
|
||||
#include <frostbite2D/graphics/batch.h>
|
||||
#include <frostbite2D/graphics/camera.h>
|
||||
#include <frostbite2D/graphics/render_resolution.h>
|
||||
#include <frostbite2D/graphics/shader.h>
|
||||
#include <frostbite2D/graphics/shader_manager.h>
|
||||
#include <frostbite2D/graphics/texture.h>
|
||||
#include <frostbite2D/graphics/types.h>
|
||||
#include <string>
|
||||
|
||||
namespace frostbite2D {
|
||||
@@ -13,55 +14,71 @@ namespace frostbite2D {
|
||||
class Renderer {
|
||||
public:
|
||||
static Renderer& get();
|
||||
|
||||
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
|
||||
void beginFrame();
|
||||
void endFrame();
|
||||
void flush();
|
||||
|
||||
|
||||
void setViewport(int x, int y, int width, int height);
|
||||
void setWindowSize(int width, int height, float contentScaleX = 1.0f,
|
||||
float contentScaleY = 1.0f);
|
||||
void setVirtualResolutionEnabled(bool enabled);
|
||||
void setVirtualResolution(int width, int height);
|
||||
void setResolutionScaleMode(ResolutionScaleMode mode);
|
||||
void setClearColor(float r, float g, float b, float a = 1.0f);
|
||||
void setClearColor(const Color& color);
|
||||
void clear(uint32_t flags);
|
||||
|
||||
|
||||
void setCamera(Camera* camera);
|
||||
Camera* getCamera() { return camera_; }
|
||||
|
||||
void drawQuad(const Vec2& pos, const Size& size, float cr = 1.0f, float cg = 1.0f,
|
||||
float cb = 1.0f, float ca = 1.0f);
|
||||
int getVirtualWidth() const;
|
||||
int getVirtualHeight() const;
|
||||
Rect getViewportRect() const;
|
||||
const RenderResolutionState& getResolutionState() const {
|
||||
return resolutionState_;
|
||||
}
|
||||
Vec2 screenToVirtual(const Vec2& screenPos) const;
|
||||
Vec2 virtualToScreen(const Vec2& virtualPos) const;
|
||||
|
||||
void drawQuad(const Vec2& pos, const Size& size, float cr = 1.0f,
|
||||
float cg = 1.0f, float cb = 1.0f, float ca = 1.0f);
|
||||
void drawQuad(const Vec2& pos, const Size& size, Ptr<Texture> texture);
|
||||
void drawQuad(const Rect& rect, const Color& color);
|
||||
|
||||
|
||||
void drawSprite(const Vec2& pos, const Size& size, Ptr<Texture> texture);
|
||||
void drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
|
||||
Ptr<Texture> texture, const Color& color = Color(1, 1, 1, 1));
|
||||
void drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
|
||||
Ptr<Texture> texture,
|
||||
const Color& color = Color(1, 1, 1, 1));
|
||||
void setupBlendMode(BlendMode mode);
|
||||
|
||||
|
||||
ShaderManager& getShaderManager() { return shaderManager_; }
|
||||
Batch& getBatch() { return batch_; }
|
||||
|
||||
|
||||
private:
|
||||
Renderer();
|
||||
// ~Renderer() 在 shutdown() 中手动调用销毁
|
||||
|
||||
|
||||
void updateUniforms();
|
||||
|
||||
void recalculateResolutionState();
|
||||
void syncCameraViewport();
|
||||
|
||||
ShaderManager shaderManager_;
|
||||
Batch batch_;
|
||||
Camera* camera_ = nullptr;
|
||||
|
||||
|
||||
uint32_t clearColor_[4] = {0, 0, 0, 255};
|
||||
int viewportX_ = 0;
|
||||
int viewportY_ = 0;
|
||||
int viewportWidth_ = 1280;
|
||||
int viewportHeight_ = 720;
|
||||
|
||||
bool useVirtualResolution_ = false;
|
||||
int virtualWidth_ = 1280;
|
||||
int virtualHeight_ = 720;
|
||||
ResolutionScaleMode resolutionMode_ = ResolutionScaleMode::Fit;
|
||||
RenderResolutionState resolutionState_;
|
||||
|
||||
bool initialized_ = false;
|
||||
|
||||
|
||||
Renderer(const Renderer&) = delete;
|
||||
Renderer& operator=(const Renderer&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace frostbite2D
|
||||
|
||||
@@ -180,9 +180,13 @@ bool Application::initCoreModules() {
|
||||
}
|
||||
|
||||
window_->onResize([this](int width, int height) {
|
||||
renderer_->setViewport(0, 0, width, height);
|
||||
camera_->setViewport(width, height);
|
||||
SDL_Log("Window resized to %dx%d", width, height);
|
||||
if (!renderer_ || !window_) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderer_->setWindowSize(width, height, window_->scaleX(), window_->scaleY());
|
||||
SDL_Log("Window resized to %dx%d (drawable %dx%d)", width, height,
|
||||
window_->drawableWidth(), window_->drawableHeight());
|
||||
});
|
||||
|
||||
// 初始化渲染器
|
||||
@@ -197,13 +201,19 @@ bool Application::initCoreModules() {
|
||||
|
||||
// 设置窗口清除颜色和视口
|
||||
renderer_->setClearColor(0.0f, 0.0f, 0.0f);
|
||||
renderer_->setViewport(0, 0, config_.windowConfig.width, config_.windowConfig.height);
|
||||
renderer_->setVirtualResolutionEnabled(config_.useVirtualResolution);
|
||||
renderer_->setResolutionScaleMode(config_.resolutionMode);
|
||||
if (config_.virtualWidth > 0 && config_.virtualHeight > 0) {
|
||||
renderer_->setVirtualResolution(config_.virtualWidth, config_.virtualHeight);
|
||||
}
|
||||
renderer_->setWindowSize(window_->width(), window_->height(), window_->scaleX(),
|
||||
window_->scaleY());
|
||||
|
||||
// 创建并设置相机
|
||||
{
|
||||
ScopedStartupTrace stageTrace("Camera setup");
|
||||
camera_ = new Camera();
|
||||
camera_->setViewport(config_.windowConfig.width, config_.windowConfig.height);
|
||||
camera_->setViewport(renderer_->getVirtualWidth(), renderer_->getVirtualHeight());
|
||||
camera_->setFlipY(true); // 启用 Y 轴翻转,(0,0) 在左上角
|
||||
renderer_->setCamera(camera_);
|
||||
}
|
||||
@@ -282,6 +292,17 @@ void Application::mainLoop() {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sdlEvent.type == SDL_WINDOWEVENT && window_) {
|
||||
switch (sdlEvent.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
window_->refreshMetrics(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto event = convertSDLEvent(sdlEvent);
|
||||
if (event) {
|
||||
dispatchEvent(*event);
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
#include <frostbite2D/resource/asset.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
bool Window::create(const WindowConfig &cfg) {
|
||||
|
||||
bool Window::create(const WindowConfig& cfg) {
|
||||
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||
|
||||
|
||||
#ifndef __SWITCH__
|
||||
if (cfg.fullscreen) {
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
@@ -28,7 +26,6 @@ bool Window::create(const WindowConfig &cfg) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
@@ -38,17 +35,11 @@ bool Window::create(const WindowConfig &cfg) {
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, cfg.multisamples);
|
||||
}
|
||||
|
||||
int x = SDL_WINDOWPOS_CENTERED;
|
||||
int y = SDL_WINDOWPOS_CENTERED;
|
||||
if (!cfg.centered) {
|
||||
x = SDL_WINDOWPOS_UNDEFINED;
|
||||
y = SDL_WINDOWPOS_UNDEFINED;
|
||||
}
|
||||
|
||||
// 创建窗口
|
||||
sdlWindow_ =
|
||||
SDL_CreateWindow(cfg.title.c_str(), x, y, cfg.width, cfg.height, flags);
|
||||
int x = cfg.centered ? SDL_WINDOWPOS_CENTERED : SDL_WINDOWPOS_UNDEFINED;
|
||||
int y = cfg.centered ? SDL_WINDOWPOS_CENTERED : SDL_WINDOWPOS_UNDEFINED;
|
||||
|
||||
sdlWindow_ = SDL_CreateWindow(cfg.title.c_str(), x, y, cfg.width, cfg.height,
|
||||
flags);
|
||||
if (!sdlWindow_) {
|
||||
SDL_Log("Failed to create window: %s", SDL_GetError());
|
||||
return false;
|
||||
@@ -72,23 +63,12 @@ bool Window::create(const WindowConfig &cfg) {
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(cfg.vsync ? 1 : 0);
|
||||
refreshMetrics(false);
|
||||
|
||||
// 获取实际的可绘制大小(考虑高 DPI 和 Switch 等平台)
|
||||
int drawableWidth, drawableHeight;
|
||||
SDL_GL_GetDrawableSize(sdlWindow_, &drawableWidth, &drawableHeight);
|
||||
width_ = drawableWidth;
|
||||
height_ = drawableHeight;
|
||||
|
||||
// 计算缩放比例
|
||||
int windowWidth, windowHeight;
|
||||
SDL_GetWindowSize(sdlWindow_, &windowWidth, &windowHeight);
|
||||
if (windowWidth > 0 && windowHeight > 0) {
|
||||
scaleX_ = static_cast<float>(drawableWidth) / static_cast<float>(windowWidth);
|
||||
scaleY_ = static_cast<float>(drawableHeight) / static_cast<float>(windowHeight);
|
||||
}
|
||||
|
||||
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||
vsync_ = cfg.vsync;
|
||||
cursorVisible_ = cfg.showCursor;
|
||||
showCursor(cfg.showCursor);
|
||||
|
||||
#ifndef __SWITCH__
|
||||
if (!cfg.icon.file_path.empty()) {
|
||||
@@ -113,9 +93,11 @@ bool Window::create(const WindowConfig &cfg) {
|
||||
}
|
||||
|
||||
void Window::destroy() {
|
||||
if (sdlWindow_) {
|
||||
if (glContext_) {
|
||||
SDL_GL_DeleteContext(glContext_);
|
||||
glContext_ = nullptr;
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_DestroyWindow(sdlWindow_);
|
||||
sdlWindow_ = nullptr;
|
||||
}
|
||||
@@ -129,16 +111,32 @@ void Window::swap() {
|
||||
}
|
||||
}
|
||||
|
||||
void Window::close() { shouldClose_ = true; }
|
||||
void Window::close() {
|
||||
shouldClose_ = true;
|
||||
if (closeCb_) {
|
||||
closeCb_();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setTitle(const std::string &title) {}
|
||||
void Window::setTitle(const std::string& title) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowTitle(sdlWindow_, title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setSize(int w, int h) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowSize(sdlWindow_, w, h);
|
||||
refreshMetrics(true);
|
||||
return;
|
||||
}
|
||||
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
drawableWidth_ = w;
|
||||
drawableHeight_ = h;
|
||||
scaleX_ = 1.0f;
|
||||
scaleY_ = 1.0f;
|
||||
if (resizeCb_) {
|
||||
resizeCb_(w, h);
|
||||
}
|
||||
@@ -150,23 +148,54 @@ void Window::setPos(int x, int y) {
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setFullscreen(bool fs) { fullscreen_ = fs; }
|
||||
void Window::setFullscreen(bool fs) {
|
||||
fullscreen_ = fs;
|
||||
if (!sdlWindow_) {
|
||||
return;
|
||||
}
|
||||
|
||||
void Window::setVSync(bool vsync) { vsync_ = vsync; }
|
||||
#ifndef __SWITCH__
|
||||
Uint32 flags = fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
|
||||
SDL_SetWindowFullscreen(sdlWindow_, flags);
|
||||
refreshMetrics(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Window::setVisible(bool visible) {}
|
||||
void Window::setVSync(bool vsync) {
|
||||
vsync_ = vsync;
|
||||
if (glContext_) {
|
||||
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setVisible(bool visible) {
|
||||
if (!sdlWindow_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
SDL_ShowWindow(sdlWindow_);
|
||||
} else {
|
||||
SDL_HideWindow(sdlWindow_);
|
||||
}
|
||||
}
|
||||
|
||||
int Window::width() const { return width_; }
|
||||
|
||||
int Window::height() const { return height_; }
|
||||
|
||||
int Window::drawableWidth() const { return drawableWidth_; }
|
||||
|
||||
int Window::drawableHeight() const { return drawableHeight_; }
|
||||
|
||||
Size Window::size() const {
|
||||
return Size{static_cast<float>(width_), static_cast<float>(height_)};
|
||||
}
|
||||
|
||||
Vec2 Window::pos() const {
|
||||
if (sdlWindow_) {
|
||||
int x, y;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
SDL_GetWindowPosition(sdlWindow_, &x, &y);
|
||||
return Vec2{static_cast<float>(x), static_cast<float>(y)};
|
||||
}
|
||||
@@ -185,11 +214,90 @@ float Window::scaleX() const { return scaleX_; }
|
||||
|
||||
float Window::scaleY() const { return scaleY_; }
|
||||
|
||||
void Window::setCursor(CursorType cursor) {}
|
||||
bool Window::refreshMetrics(bool emitResize) {
|
||||
if (!sdlWindow_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Window::showCursor(bool show) { cursorVisible_ = show; }
|
||||
int prevWidth = width_;
|
||||
int prevHeight = height_;
|
||||
int prevDrawableWidth = drawableWidth_;
|
||||
int prevDrawableHeight = drawableHeight_;
|
||||
|
||||
void Window::lockCursor(bool lock) { cursorLocked_ = lock; }
|
||||
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
|
||||
SDL_GL_GetDrawableSize(sdlWindow_, &drawableWidth_, &drawableHeight_);
|
||||
|
||||
if (width_ > 0 && height_ > 0) {
|
||||
scaleX_ = static_cast<float>(drawableWidth_) / static_cast<float>(width_);
|
||||
scaleY_ = static_cast<float>(drawableHeight_) / static_cast<float>(height_);
|
||||
} else {
|
||||
scaleX_ = 1.0f;
|
||||
scaleY_ = 1.0f;
|
||||
}
|
||||
|
||||
Uint32 windowFlags = SDL_GetWindowFlags(sdlWindow_);
|
||||
focused_ = (windowFlags & SDL_WINDOW_INPUT_FOCUS) != 0;
|
||||
minimized_ = (windowFlags & SDL_WINDOW_MINIMIZED) != 0;
|
||||
fullscreen_ = (windowFlags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0 ||
|
||||
(windowFlags & SDL_WINDOW_FULLSCREEN) != 0;
|
||||
|
||||
bool changed = prevWidth != width_ || prevHeight != height_ ||
|
||||
prevDrawableWidth != drawableWidth_ ||
|
||||
prevDrawableHeight != drawableHeight_;
|
||||
if (emitResize && changed && resizeCb_) {
|
||||
resizeCb_(width_, height_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Window::setCursor(CursorType cursor) {
|
||||
SDL_SystemCursor systemCursor = SDL_SYSTEM_CURSOR_ARROW;
|
||||
switch (cursor) {
|
||||
case CursorType::Arrow:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_ARROW;
|
||||
break;
|
||||
case CursorType::TextInput:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_IBEAM;
|
||||
break;
|
||||
case CursorType::Hand:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_HAND;
|
||||
break;
|
||||
case CursorType::SizeAll:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_SIZEALL;
|
||||
break;
|
||||
case CursorType::SizeWE:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_SIZEWE;
|
||||
break;
|
||||
case CursorType::SizeNS:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_SIZENS;
|
||||
break;
|
||||
case CursorType::SizeNESW:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_SIZENESW;
|
||||
break;
|
||||
case CursorType::SizeNWSE:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_SIZENWSE;
|
||||
break;
|
||||
case CursorType::No:
|
||||
systemCursor = SDL_SYSTEM_CURSOR_ARROW;
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Cursor* sdlCursor = SDL_CreateSystemCursor(systemCursor);
|
||||
if (sdlCursor) {
|
||||
SDL_SetCursor(sdlCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::showCursor(bool show) {
|
||||
cursorVisible_ = show;
|
||||
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
|
||||
}
|
||||
|
||||
void Window::lockCursor(bool lock) {
|
||||
cursorLocked_ = lock;
|
||||
SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
|
||||
void Window::onResize(ResizeCb cb) { resizeCb_ = cb; }
|
||||
|
||||
@@ -197,6 +305,6 @@ void Window::onClose(CloseCb cb) { closeCb_ = cb; }
|
||||
|
||||
void Window::onFocus(FocusCb cb) { focusCb_ = cb; }
|
||||
|
||||
void *Window::native() const { return nullptr; }
|
||||
void* Window::native() const { return nullptr; }
|
||||
|
||||
} // namespace frostbite2D
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <frostbite2D/graphics/camera.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
@@ -14,6 +15,16 @@ void Camera::setViewport(int width, int height) {
|
||||
viewportHeight_ = height;
|
||||
}
|
||||
|
||||
float Camera::getVisibleWidth() const {
|
||||
float safeZoom = std::max(zoom_, 0.01f);
|
||||
return static_cast<float>(viewportWidth_) / safeZoom;
|
||||
}
|
||||
|
||||
float Camera::getVisibleHeight() const {
|
||||
float safeZoom = std::max(zoom_, 0.01f);
|
||||
return static_cast<float>(viewportHeight_) / safeZoom;
|
||||
}
|
||||
|
||||
void Camera::lookAt(const Vec2 &target) { position_ = target; }
|
||||
|
||||
void Camera::move(const Vec2 &delta) { position_ = position_ + delta; }
|
||||
@@ -54,4 +65,4 @@ glm::mat4 Camera::getProjectionMatrix() const {
|
||||
return glm::ortho(left, right, bottom, top, -1.0f, 1.0f);
|
||||
}
|
||||
|
||||
} // namespace frostbite2D
|
||||
} // namespace frostbite2D
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
#include "SDL_log.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <frostbite2D/graphics/renderer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float kMinContentScale = 0.01f;
|
||||
|
||||
int roundToInt(float value) {
|
||||
return static_cast<int>(std::lround(value));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Renderer& Renderer::get() {
|
||||
static Renderer instance;
|
||||
return instance;
|
||||
@@ -13,29 +25,29 @@ Renderer& Renderer::get() {
|
||||
|
||||
Renderer::Renderer() = default;
|
||||
|
||||
// 移除析构函数中的自动销毁,改为在 Application::shutdown() 中手动调用
|
||||
|
||||
bool Renderer::init() {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!batch_.init()) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize batch system");
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to initialize batch system");
|
||||
return false;
|
||||
}
|
||||
|
||||
//初始化着色器管理器
|
||||
if (!shaderManager_.init("assets/shaders")) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize shader manager");
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to initialize shader manager");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
|
||||
SDL_Log("Renderer initialized");
|
||||
initialized_ = true;
|
||||
recalculateResolutionState();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -46,11 +58,12 @@ void Renderer::shutdown() {
|
||||
}
|
||||
|
||||
void Renderer::beginFrame() {
|
||||
glViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
|
||||
glClearColor(clearColor_[0] / 255.0f, clearColor_[1] / 255.0f,
|
||||
clearColor_[2] / 255.0f, clearColor_[3] / 255.0f);
|
||||
glViewport(resolutionState_.viewportX, resolutionState_.viewportY,
|
||||
resolutionState_.viewportWidth, resolutionState_.viewportHeight);
|
||||
glClearColor(clearColor_[0] / 255.0f, clearColor_[1] / 255.0f,
|
||||
clearColor_[2] / 255.0f, clearColor_[3] / 255.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
updateUniforms();
|
||||
batch_.begin();
|
||||
}
|
||||
@@ -64,14 +77,66 @@ void Renderer::flush() {
|
||||
}
|
||||
|
||||
void Renderer::setViewport(int x, int y, int width, int height) {
|
||||
viewportX_ = x;
|
||||
viewportY_ = y;
|
||||
viewportWidth_ = width;
|
||||
viewportHeight_ = height;
|
||||
|
||||
if (camera_) {
|
||||
camera_->setViewport(width, height);
|
||||
}
|
||||
resolutionState_.viewportX = x;
|
||||
resolutionState_.viewportY = y;
|
||||
resolutionState_.viewportWidth = std::max(width, 1);
|
||||
resolutionState_.viewportHeight = std::max(height, 1);
|
||||
|
||||
float safeScaleX = std::max(resolutionState_.contentScaleX, kMinContentScale);
|
||||
float safeScaleY = std::max(resolutionState_.contentScaleY, kMinContentScale);
|
||||
|
||||
resolutionState_.windowViewportX = roundToInt(
|
||||
static_cast<float>(resolutionState_.viewportX) / safeScaleX);
|
||||
resolutionState_.windowViewportY = roundToInt(
|
||||
static_cast<float>(resolutionState_.viewportY) / safeScaleY);
|
||||
resolutionState_.windowViewportWidth = roundToInt(
|
||||
static_cast<float>(resolutionState_.viewportWidth) / safeScaleX);
|
||||
resolutionState_.windowViewportHeight = roundToInt(
|
||||
static_cast<float>(resolutionState_.viewportHeight) / safeScaleY);
|
||||
|
||||
int logicalWidth = std::max(getVirtualWidth(), 1);
|
||||
int logicalHeight = std::max(getVirtualHeight(), 1);
|
||||
resolutionState_.scaleX =
|
||||
static_cast<float>(resolutionState_.windowViewportWidth) /
|
||||
static_cast<float>(logicalWidth);
|
||||
resolutionState_.scaleY =
|
||||
static_cast<float>(resolutionState_.windowViewportHeight) /
|
||||
static_cast<float>(logicalHeight);
|
||||
|
||||
syncCameraViewport();
|
||||
}
|
||||
|
||||
void Renderer::setWindowSize(int width, int height, float contentScaleX,
|
||||
float contentScaleY) {
|
||||
resolutionState_.windowWidth = std::max(width, 1);
|
||||
resolutionState_.windowHeight = std::max(height, 1);
|
||||
resolutionState_.contentScaleX = std::max(contentScaleX, kMinContentScale);
|
||||
resolutionState_.contentScaleY = std::max(contentScaleY, kMinContentScale);
|
||||
resolutionState_.drawableWidth = std::max(
|
||||
roundToInt(static_cast<float>(resolutionState_.windowWidth) *
|
||||
resolutionState_.contentScaleX),
|
||||
1);
|
||||
resolutionState_.drawableHeight = std::max(
|
||||
roundToInt(static_cast<float>(resolutionState_.windowHeight) *
|
||||
resolutionState_.contentScaleY),
|
||||
1);
|
||||
recalculateResolutionState();
|
||||
}
|
||||
|
||||
void Renderer::setVirtualResolutionEnabled(bool enabled) {
|
||||
useVirtualResolution_ = enabled;
|
||||
recalculateResolutionState();
|
||||
}
|
||||
|
||||
void Renderer::setVirtualResolution(int width, int height) {
|
||||
virtualWidth_ = std::max(width, 1);
|
||||
virtualHeight_ = std::max(height, 1);
|
||||
recalculateResolutionState();
|
||||
}
|
||||
|
||||
void Renderer::setResolutionScaleMode(ResolutionScaleMode mode) {
|
||||
resolutionMode_ = mode;
|
||||
recalculateResolutionState();
|
||||
}
|
||||
|
||||
void Renderer::setClearColor(float r, float g, float b, float a) {
|
||||
@@ -98,15 +163,169 @@ void Renderer::setCamera(Camera* camera) {
|
||||
}
|
||||
|
||||
camera_ = camera;
|
||||
if (camera) {
|
||||
camera_->setViewport(viewportWidth_, viewportHeight_);
|
||||
}
|
||||
syncCameraViewport();
|
||||
|
||||
if (initialized_) {
|
||||
updateUniforms();
|
||||
}
|
||||
}
|
||||
|
||||
int Renderer::getVirtualWidth() const {
|
||||
if (useVirtualResolution_) {
|
||||
return std::max(virtualWidth_, 1);
|
||||
}
|
||||
return std::max(resolutionState_.windowWidth, 1);
|
||||
}
|
||||
|
||||
int Renderer::getVirtualHeight() const {
|
||||
if (useVirtualResolution_) {
|
||||
return std::max(virtualHeight_, 1);
|
||||
}
|
||||
return std::max(resolutionState_.windowHeight, 1);
|
||||
}
|
||||
|
||||
Rect Renderer::getViewportRect() const {
|
||||
return resolutionState_.getWindowViewportRect();
|
||||
}
|
||||
|
||||
Vec2 Renderer::screenToVirtual(const Vec2& screenPos) const {
|
||||
Rect viewport = getViewportRect();
|
||||
if (viewport.width() <= 0.0f || viewport.height() <= 0.0f) {
|
||||
return screenPos;
|
||||
}
|
||||
|
||||
float localX = screenPos.x - viewport.left();
|
||||
float localY = screenPos.y - viewport.top();
|
||||
return Vec2(localX * static_cast<float>(getVirtualWidth()) / viewport.width(),
|
||||
localY * static_cast<float>(getVirtualHeight()) /
|
||||
viewport.height());
|
||||
}
|
||||
|
||||
Vec2 Renderer::virtualToScreen(const Vec2& virtualPos) const {
|
||||
Rect viewport = getViewportRect();
|
||||
float logicalWidth = static_cast<float>(std::max(getVirtualWidth(), 1));
|
||||
float logicalHeight = static_cast<float>(std::max(getVirtualHeight(), 1));
|
||||
|
||||
return Vec2(viewport.left() + virtualPos.x * viewport.width() / logicalWidth,
|
||||
viewport.top() + virtualPos.y * viewport.height() /
|
||||
logicalHeight);
|
||||
}
|
||||
|
||||
void Renderer::recalculateResolutionState() {
|
||||
resolutionState_.windowWidth = std::max(resolutionState_.windowWidth, 1);
|
||||
resolutionState_.windowHeight = std::max(resolutionState_.windowHeight, 1);
|
||||
resolutionState_.contentScaleX =
|
||||
std::max(resolutionState_.contentScaleX, kMinContentScale);
|
||||
resolutionState_.contentScaleY =
|
||||
std::max(resolutionState_.contentScaleY, kMinContentScale);
|
||||
resolutionState_.drawableWidth = std::max(
|
||||
roundToInt(static_cast<float>(resolutionState_.windowWidth) *
|
||||
resolutionState_.contentScaleX),
|
||||
1);
|
||||
resolutionState_.drawableHeight = std::max(
|
||||
roundToInt(static_cast<float>(resolutionState_.windowHeight) *
|
||||
resolutionState_.contentScaleY),
|
||||
1);
|
||||
|
||||
int windowViewportX = 0;
|
||||
int windowViewportY = 0;
|
||||
int windowViewportWidth = resolutionState_.windowWidth;
|
||||
int windowViewportHeight = resolutionState_.windowHeight;
|
||||
|
||||
if (useVirtualResolution_) {
|
||||
float widthScale = static_cast<float>(resolutionState_.windowWidth) /
|
||||
static_cast<float>(std::max(virtualWidth_, 1));
|
||||
float heightScale = static_cast<float>(resolutionState_.windowHeight) /
|
||||
static_cast<float>(std::max(virtualHeight_, 1));
|
||||
|
||||
switch (resolutionMode_) {
|
||||
case ResolutionScaleMode::Disabled:
|
||||
resolutionState_.scaleX = widthScale;
|
||||
resolutionState_.scaleY = heightScale;
|
||||
break;
|
||||
case ResolutionScaleMode::FitHeight: {
|
||||
float uniformScale = heightScale;
|
||||
float fittedWidth = static_cast<float>(virtualWidth_) * uniformScale;
|
||||
if (fittedWidth > static_cast<float>(resolutionState_.windowWidth)) {
|
||||
uniformScale = std::min(widthScale, heightScale);
|
||||
}
|
||||
|
||||
windowViewportWidth = std::clamp(
|
||||
roundToInt(static_cast<float>(virtualWidth_) * uniformScale), 1,
|
||||
resolutionState_.windowWidth);
|
||||
windowViewportHeight = std::clamp(
|
||||
roundToInt(static_cast<float>(virtualHeight_) * uniformScale), 1,
|
||||
resolutionState_.windowHeight);
|
||||
windowViewportX =
|
||||
(resolutionState_.windowWidth - windowViewportWidth) / 2;
|
||||
windowViewportY =
|
||||
(resolutionState_.windowHeight - windowViewportHeight) / 2;
|
||||
resolutionState_.scaleX =
|
||||
static_cast<float>(windowViewportWidth) /
|
||||
static_cast<float>(std::max(virtualWidth_, 1));
|
||||
resolutionState_.scaleY =
|
||||
static_cast<float>(windowViewportHeight) /
|
||||
static_cast<float>(std::max(virtualHeight_, 1));
|
||||
break;
|
||||
}
|
||||
case ResolutionScaleMode::Fit:
|
||||
default: {
|
||||
float uniformScale = std::min(widthScale, heightScale);
|
||||
windowViewportWidth = std::clamp(
|
||||
roundToInt(static_cast<float>(virtualWidth_) * uniformScale), 1,
|
||||
resolutionState_.windowWidth);
|
||||
windowViewportHeight = std::clamp(
|
||||
roundToInt(static_cast<float>(virtualHeight_) * uniformScale), 1,
|
||||
resolutionState_.windowHeight);
|
||||
windowViewportX =
|
||||
(resolutionState_.windowWidth - windowViewportWidth) / 2;
|
||||
windowViewportY =
|
||||
(resolutionState_.windowHeight - windowViewportHeight) / 2;
|
||||
resolutionState_.scaleX =
|
||||
static_cast<float>(windowViewportWidth) /
|
||||
static_cast<float>(std::max(virtualWidth_, 1));
|
||||
resolutionState_.scaleY =
|
||||
static_cast<float>(windowViewportHeight) /
|
||||
static_cast<float>(std::max(virtualHeight_, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolutionState_.scaleX = 1.0f;
|
||||
resolutionState_.scaleY = 1.0f;
|
||||
}
|
||||
|
||||
resolutionState_.windowViewportX = windowViewportX;
|
||||
resolutionState_.windowViewportY = windowViewportY;
|
||||
resolutionState_.windowViewportWidth = windowViewportWidth;
|
||||
resolutionState_.windowViewportHeight = windowViewportHeight;
|
||||
resolutionState_.viewportX = roundToInt(
|
||||
static_cast<float>(windowViewportX) * resolutionState_.contentScaleX);
|
||||
resolutionState_.viewportY = roundToInt(
|
||||
static_cast<float>(windowViewportY) * resolutionState_.contentScaleY);
|
||||
resolutionState_.viewportWidth = std::max(
|
||||
roundToInt(static_cast<float>(windowViewportWidth) *
|
||||
resolutionState_.contentScaleX),
|
||||
1);
|
||||
resolutionState_.viewportHeight = std::max(
|
||||
roundToInt(static_cast<float>(windowViewportHeight) *
|
||||
resolutionState_.contentScaleY),
|
||||
1);
|
||||
|
||||
syncCameraViewport();
|
||||
if (initialized_) {
|
||||
updateUniforms();
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::syncCameraViewport() {
|
||||
if (!camera_) {
|
||||
return;
|
||||
}
|
||||
|
||||
camera_->setViewport(getVirtualWidth(), getVirtualHeight());
|
||||
}
|
||||
|
||||
void Renderer::setupBlendMode(BlendMode mode) {
|
||||
switch (mode) {
|
||||
case BlendMode::None:
|
||||
@@ -116,38 +335,37 @@ void Renderer::setupBlendMode(BlendMode mode) {
|
||||
case BlendMode::Normal:
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
|
||||
GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
case BlendMode::Additive:
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE,
|
||||
GL_ONE, GL_ONE);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE);
|
||||
break;
|
||||
case BlendMode::Screen:
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR,
|
||||
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ONE,
|
||||
GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
case BlendMode::Premultiplied:
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
|
||||
GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
case BlendMode::Subtractive:
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
|
||||
GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
||||
break;
|
||||
case BlendMode::Multiply:
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
|
||||
GL_ONE_MINUS_SRC_ALPHA);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -160,7 +378,7 @@ void Renderer::updateUniforms() {
|
||||
spriteShader->setMat4("u_view", camera_->getViewMatrix());
|
||||
spriteShader->setMat4("u_projection", camera_->getProjectionMatrix());
|
||||
}
|
||||
|
||||
|
||||
auto* coloredShader = shaderManager_.getShader("colored_quad");
|
||||
if (coloredShader) {
|
||||
coloredShader->use();
|
||||
@@ -177,57 +395,57 @@ void Renderer::updateUniforms() {
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::drawQuad(const Vec2& pos, const Size& size, float cr, float cg,
|
||||
float cb, float ca) {
|
||||
void Renderer::drawQuad(const Vec2& pos, const Size& size, float cr, float cg,
|
||||
float cb, float ca) {
|
||||
Rect rect(pos.x, pos.y, size.width, size.height);
|
||||
Quad quad = Quad::create(rect, cr, cg, cb, ca);
|
||||
Transform2D transform = Transform2D::identity();
|
||||
|
||||
|
||||
auto* shader = shaderManager_.getShader("colored_quad");
|
||||
if (shader) {
|
||||
shader->use();
|
||||
}
|
||||
|
||||
|
||||
batch_.submitQuad(quad, transform, nullptr, shader, BlendMode::Normal);
|
||||
}
|
||||
|
||||
void Renderer::drawQuad(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
|
||||
Rect rect(pos.x, pos.y, size.width, size.height);
|
||||
Quad quad = Quad::createTextured(rect, Rect(0, 0, size.width, size.height),
|
||||
Vec2(size.width, size.height));
|
||||
Vec2(size.width, size.height));
|
||||
Transform2D transform = Transform2D::identity();
|
||||
|
||||
|
||||
auto* shader = shaderManager_.getShader("sprite");
|
||||
if (shader) {
|
||||
shader->use();
|
||||
}
|
||||
|
||||
|
||||
batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
|
||||
}
|
||||
|
||||
void Renderer::drawQuad(const Rect& rect, const Color& color) {
|
||||
drawQuad(Vec2(rect.left(), rect.top()),
|
||||
Size(rect.width(), rect.height()),
|
||||
color.r, color.g, color.b, color.a);
|
||||
drawQuad(Vec2(rect.left(), rect.top()), Size(rect.width(), rect.height()),
|
||||
color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
void Renderer::drawSprite(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
|
||||
drawQuad(pos, size, texture);
|
||||
}
|
||||
|
||||
void Renderer::drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
|
||||
Ptr<Texture> texture, const Color& color) {
|
||||
void Renderer::drawSprite(const Vec2& pos, const Rect& srcRect,
|
||||
const Vec2& texSize, Ptr<Texture> texture,
|
||||
const Color& color) {
|
||||
Rect destRect(pos.x, pos.y, srcRect.width(), srcRect.height());
|
||||
Quad quad = Quad::createTextured(destRect, srcRect, texSize,
|
||||
color.r, color.g, color.b, color.a);
|
||||
Quad quad = Quad::createTextured(destRect, srcRect, texSize, color.r, color.g,
|
||||
color.b, color.a);
|
||||
Transform2D transform = Transform2D::identity();
|
||||
|
||||
|
||||
auto* shader = shaderManager_.getShader("sprite");
|
||||
if (shader) {
|
||||
shader->use();
|
||||
}
|
||||
|
||||
|
||||
batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace frostbite2D
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include <frostbite2D/graphics/renderer.h>
|
||||
#include <frostbite2D/scene/ui_scene.h>
|
||||
|
||||
namespace frostbite2D {
|
||||
|
||||
UIScene::UIScene() {
|
||||
Renderer& renderer = Renderer::get();
|
||||
camera_.setViewport(renderer.getVirtualWidth(), renderer.getVirtualHeight());
|
||||
camera_.setFlipY(true);
|
||||
camera_.setZoom(1.0f);
|
||||
camera_.setPosition(Vec2::Zero());
|
||||
|
||||
@@ -45,7 +45,7 @@ private:
|
||||
bool debugEnabled_ = false;
|
||||
bool initialized_ = false;
|
||||
|
||||
float zoom_ = 1.2f;
|
||||
float zoom_ = 1.0f;
|
||||
float debugMoveSpeed_ = 800.0f;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,9 +90,9 @@ private:
|
||||
int backgroundRepeatWidth_ = 0;
|
||||
/// 地图配置里的整体 Y 偏移;既影响层位置,也影响地板校准。
|
||||
int mapOffsetY_ = 0;
|
||||
bool debugMode_ = true;
|
||||
bool debugMode_ = false;
|
||||
/// 硬编码调试开关:关闭后忽略可行走区域检测,允许角色自由移动。
|
||||
bool movableAreaCheckEnabled_ = false;
|
||||
bool movableAreaCheckEnabled_ = true;
|
||||
/// 当前地图正在播放的背景音乐。
|
||||
Ptr<Music> currentMusic_;
|
||||
};
|
||||
|
||||
@@ -34,9 +34,18 @@ int main(int argc, char **argv) {
|
||||
AppConfig config = AppConfig::createDefault();
|
||||
config.appName = "Frostbite2D Test App";
|
||||
config.appVersion = "1.0.0";
|
||||
#ifdef SWITCH
|
||||
config.windowConfig.width = 1280;
|
||||
config.windowConfig.height = 720;
|
||||
#else
|
||||
config.windowConfig.width = 1066;
|
||||
config.windowConfig.height = 600;
|
||||
#endif
|
||||
config.windowConfig.title = "Frostbite2D - Async Init Demo";
|
||||
config.useVirtualResolution = true;
|
||||
config.virtualWidth = 1066;
|
||||
config.virtualHeight = 600;
|
||||
config.resolutionMode = ResolutionScaleMode::FitHeight;
|
||||
|
||||
Application &app = Application::get();
|
||||
{
|
||||
@@ -76,19 +85,20 @@ int main(int argc, char **argv) {
|
||||
auto LoadingScene = MakePtr<Scene>();
|
||||
SceneManager::get().PushScene(LoadingScene);
|
||||
|
||||
auto Background = Sprite::createFromFile("assets/ImagePacks2/Loading0.jpg");
|
||||
Background->SetSize(1280, 720);
|
||||
auto Background =
|
||||
Sprite::createFromFile("assets/ImagePacks2/Loading0.png");
|
||||
Background->SetSize(1066, 600);
|
||||
LoadingScene->AddChild(Background);
|
||||
|
||||
auto BackgroundBar =
|
||||
Sprite::createFromFile("assets/ImagePacks2/Loading1.png");
|
||||
BackgroundBar->SetPosition(0, 686);
|
||||
BackgroundBar->SetPosition(0 - 107, 566);
|
||||
LoadingScene->AddChild(BackgroundBar);
|
||||
|
||||
auto LoadCircleSp =
|
||||
Sprite::createFromFile("assets/ImagePacks2/Loading2.png");
|
||||
LoadCircleSp->SetAnchor(Vec2(0.5f, 0.5f));
|
||||
LoadCircleSp->SetPosition(1280 / 2.0f, 686 - 60);
|
||||
LoadCircleSp->SetPosition(1066 / 2.0f, 566 - 60);
|
||||
LoadCircleSp->SetBlendMode(BlendMode::Additive);
|
||||
LoadCircleSp->AddUpdateListener([](Actor &self, float dt) {
|
||||
auto rotation = self.GetRotation();
|
||||
|
||||
@@ -10,8 +10,6 @@ namespace frostbite2D {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float kScreenWidth = 1280.0f;
|
||||
constexpr float kScreenHeight = 720.0f;
|
||||
constexpr int kDebugStickDeadzone = 8000;
|
||||
|
||||
float normalizeControllerAxis(int16 value) {
|
||||
@@ -180,10 +178,10 @@ void GameCameraController::applyFocus() const {
|
||||
return;
|
||||
}
|
||||
|
||||
float halfWidth = kScreenWidth * 0.5f / zoom_;
|
||||
float halfHeight = kScreenHeight * 0.5f / zoom_;
|
||||
Vec2 cameraPos(focus_.x - halfWidth, focus_.y - halfHeight);
|
||||
camera->setZoom(zoom_);
|
||||
float halfWidth = camera->getVisibleWidth() * 0.5f;
|
||||
float halfHeight = camera->getVisibleHeight() * 0.5f;
|
||||
Vec2 cameraPos(focus_.x - halfWidth, focus_.y - halfHeight);
|
||||
camera->setPosition(cameraPos);
|
||||
map_->ApplyCameraFocus(focus_);
|
||||
}
|
||||
|
||||
@@ -43,14 +43,14 @@ void GameMapTestScene::onEnter() {
|
||||
|
||||
{
|
||||
ScopedStartupTrace stageTrace("GameMapTestScene character construction");
|
||||
character_ = MakePtr<CharacterObject>();
|
||||
if (!character_->Construction(0)) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
character_ = MakePtr<CharacterObject>();
|
||||
if (!character_->Construction(0)) {
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||
"GameMapTestScene: failed to construct default character");
|
||||
character_.Reset();
|
||||
} else {
|
||||
Vec2 spawnPos =
|
||||
map_->ClampCameraFocus(map_->GetDefaultCameraFocus(), 1.2f);
|
||||
map_->ClampCameraFocus(map_->GetDefaultCameraFocus(), 1.0f);
|
||||
character_->SetCharacterPosition(spawnPos);
|
||||
character_->EnableEventReceive();
|
||||
character_->SetEventPriority(-100);
|
||||
@@ -59,7 +59,6 @@ void GameMapTestScene::onEnter() {
|
||||
}
|
||||
|
||||
cameraController_.SetMap(map_.Get());
|
||||
// cameraController_.SetZoom(1.2f);
|
||||
cameraController_.SetTarget(character_.Get());
|
||||
cameraController_.SetDebugEnabled(false);
|
||||
if (character_) {
|
||||
|
||||
@@ -117,7 +117,6 @@ void GameTown::AddCharacter(RefPtr<Actor> actor, int areaIndex) {
|
||||
|
||||
AddChild(mapIt->map);
|
||||
cameraController_.SetMap(mapIt->map.Get());
|
||||
cameraController_.SetZoom(1.2f);
|
||||
cameraController_.SetTarget(actor.Get());
|
||||
cameraController_.SnapToDefaultFocus();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user