refactor: 移除logger.h头文件并统一使用logger_service.h
将项目中所有对logger.h的引用替换为logger_service.h,并删除logger.h文件。同时调整了部分文件的include顺序和格式,保持代码风格一致。
This commit is contained in:
parent
6008331fc5
commit
65b143573c
|
|
@ -1,18 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture/texture.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
|
|
@ -13,13 +12,13 @@ namespace extra2d {
|
|||
* @brief 日志级别枚举
|
||||
*/
|
||||
enum class LogLevel {
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warn = 3,
|
||||
Error = 4,
|
||||
Fatal = 5,
|
||||
Off = 6
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Warn = 3,
|
||||
Error = 4,
|
||||
Fatal = 5,
|
||||
Off = 6
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -27,70 +26,70 @@ enum class LogLevel {
|
|||
*/
|
||||
class ILogger : public IService {
|
||||
public:
|
||||
virtual ~ILogger() = default;
|
||||
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
*/
|
||||
virtual void setLevel(LogLevel level) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取日志级别
|
||||
*/
|
||||
virtual LogLevel getLevel() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查日志级别是否启用
|
||||
*/
|
||||
virtual bool isEnabled(LogLevel level) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(格式化)
|
||||
*/
|
||||
virtual void log(LogLevel level, const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(字符串)
|
||||
*/
|
||||
virtual void log(LogLevel level, const std::string& msg) = 0;
|
||||
|
||||
/**
|
||||
* @brief Trace级别日志
|
||||
*/
|
||||
virtual void trace(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Debug级别日志
|
||||
*/
|
||||
virtual void debug(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Info级别日志
|
||||
*/
|
||||
virtual void info(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Warn级别日志
|
||||
*/
|
||||
virtual void warn(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Error级别日志
|
||||
*/
|
||||
virtual void error(const char* fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Fatal级别日志
|
||||
*/
|
||||
virtual void fatal(const char* fmt, ...) = 0;
|
||||
|
||||
ServiceInfo getServiceInfo() const override {
|
||||
ServiceInfo info;
|
||||
info.name = "Logger";
|
||||
info.priority = ServicePriority::Core;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
virtual ~ILogger() = default;
|
||||
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
*/
|
||||
virtual void setLevel(LogLevel level) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取日志级别
|
||||
*/
|
||||
virtual LogLevel getLevel() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查日志级别是否启用
|
||||
*/
|
||||
virtual bool isEnabled(LogLevel level) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(格式化)
|
||||
*/
|
||||
virtual void log(LogLevel level, const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(字符串)
|
||||
*/
|
||||
virtual void log(LogLevel level, const std::string &msg) = 0;
|
||||
|
||||
/**
|
||||
* @brief Trace级别日志
|
||||
*/
|
||||
virtual void trace(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Debug级别日志
|
||||
*/
|
||||
virtual void debug(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Info级别日志
|
||||
*/
|
||||
virtual void info(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Warn级别日志
|
||||
*/
|
||||
virtual void warn(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Error级别日志
|
||||
*/
|
||||
virtual void error(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Fatal级别日志
|
||||
*/
|
||||
virtual void fatal(const char *fmt, ...) = 0;
|
||||
|
||||
ServiceInfo getServiceInfo() const override {
|
||||
ServiceInfo info;
|
||||
info.name = "Logger";
|
||||
info.priority = ServicePriority::Core;
|
||||
info.enabled = true;
|
||||
return info;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -98,36 +97,36 @@ public:
|
|||
*/
|
||||
class ConsoleLogger : public ILogger {
|
||||
public:
|
||||
ConsoleLogger();
|
||||
~ConsoleLogger() override;
|
||||
|
||||
bool initialize() override;
|
||||
void shutdown() override;
|
||||
|
||||
void setLevel(LogLevel level) override;
|
||||
LogLevel getLevel() const override;
|
||||
bool isEnabled(LogLevel level) const override;
|
||||
|
||||
void log(LogLevel level, const char* fmt, ...) override;
|
||||
void log(LogLevel level, const std::string& msg) override;
|
||||
|
||||
void trace(const char* fmt, ...) override;
|
||||
void debug(const char* fmt, ...) override;
|
||||
void info(const char* fmt, ...) override;
|
||||
void warn(const char* fmt, ...) override;
|
||||
void error(const char* fmt, ...) override;
|
||||
void fatal(const char* fmt, ...) override;
|
||||
ConsoleLogger();
|
||||
~ConsoleLogger() override;
|
||||
|
||||
bool initialize() override;
|
||||
void shutdown() override;
|
||||
|
||||
void setLevel(LogLevel level) override;
|
||||
LogLevel getLevel() const override;
|
||||
bool isEnabled(LogLevel level) const override;
|
||||
|
||||
void log(LogLevel level, const char *fmt, ...) override;
|
||||
void log(LogLevel level, const std::string &msg) override;
|
||||
|
||||
void trace(const char *fmt, ...) override;
|
||||
void debug(const char *fmt, ...) override;
|
||||
void info(const char *fmt, ...) override;
|
||||
void warn(const char *fmt, ...) override;
|
||||
void error(const char *fmt, ...) override;
|
||||
void fatal(const char *fmt, ...) override;
|
||||
|
||||
private:
|
||||
void output(LogLevel level, const char* msg);
|
||||
const char* getLevelString(LogLevel level);
|
||||
void output(LogLevel level, const char *msg);
|
||||
const char *getLevelString(LogLevel level);
|
||||
|
||||
LogLevel level_;
|
||||
class Impl;
|
||||
UniquePtr<Impl> impl_;
|
||||
LogLevel level_;
|
||||
class Impl;
|
||||
UniquePtr<Impl> impl_;
|
||||
|
||||
// 服务注册元数据
|
||||
E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger);
|
||||
// 服务注册元数据
|
||||
E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -135,87 +134,80 @@ private:
|
|||
// 格式化辅助函数 - 将参数转换为字符串
|
||||
namespace extra2d {
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
std::string to_string(T&& value) {
|
||||
using Decayed = std::decay_t<T>;
|
||||
if constexpr (std::is_same_v<Decayed, std::string>) {
|
||||
return value;
|
||||
} else if constexpr (std::is_same_v<Decayed, const char*>) {
|
||||
return value ? value : "(null)";
|
||||
} else if constexpr (std::is_arithmetic_v<Decayed>) {
|
||||
if constexpr (std::is_same_v<Decayed, bool>) {
|
||||
return value ? "true" : "false";
|
||||
} else if constexpr (std::is_floating_point_v<Decayed>) {
|
||||
return std::to_string(value);
|
||||
} else {
|
||||
return std::to_string(value);
|
||||
}
|
||||
} else {
|
||||
return "<?>";
|
||||
}
|
||||
}
|
||||
|
||||
inline void format_impl(std::string& result, const char* fmt) {
|
||||
result += fmt;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void format_impl(std::string& result, const char* fmt, T&& value, Args&&... args) {
|
||||
const char* p = fmt;
|
||||
while (*p) {
|
||||
if (*p == '{' && *(p + 1) == '}') {
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p + 2, std::forward<Args>(args)...);
|
||||
return;
|
||||
}
|
||||
result += *p++;
|
||||
}
|
||||
// 没有更多的 {},追加剩余参数(不应该发生)
|
||||
result += " ";
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p, std::forward<Args>(args)...);
|
||||
template <typename T> std::string to_string(T &&value) {
|
||||
using Decayed = std::decay_t<T>;
|
||||
if constexpr (std::is_same_v<Decayed, std::string>) {
|
||||
return value;
|
||||
} else if constexpr (std::is_same_v<Decayed, const char *>) {
|
||||
return value ? value : "(null)";
|
||||
} else if constexpr (std::is_arithmetic_v<Decayed>) {
|
||||
if constexpr (std::is_same_v<Decayed, bool>) {
|
||||
return value ? "true" : "false";
|
||||
} else if constexpr (std::is_floating_point_v<Decayed>) {
|
||||
return std::to_string(value);
|
||||
} else {
|
||||
return std::to_string(value);
|
||||
}
|
||||
} else {
|
||||
return "<?>";
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
std::string format_str(const char* fmt, Args&&... args) {
|
||||
if constexpr (sizeof...(args) == 0) {
|
||||
return std::string(fmt);
|
||||
} else {
|
||||
std::string result;
|
||||
detail::format_impl(result, fmt, std::forward<Args>(args)...);
|
||||
return result;
|
||||
inline void format_impl(std::string &result, const char *fmt) { result += fmt; }
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void format_impl(std::string &result, const char *fmt, T &&value,
|
||||
Args &&...args) {
|
||||
const char *p = fmt;
|
||||
while (*p) {
|
||||
if (*p == '{' && *(p + 1) == '}') {
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p + 2, std::forward<Args>(args)...);
|
||||
return;
|
||||
}
|
||||
result += *p++;
|
||||
}
|
||||
// 没有更多的 {},追加剩余参数(不应该发生)
|
||||
result += " ";
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p, std::forward<Args>(args)...);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Args>
|
||||
std::string format_str(const char *fmt, Args &&...args) {
|
||||
if constexpr (sizeof...(args) == 0) {
|
||||
return std::string(fmt);
|
||||
} else {
|
||||
std::string result;
|
||||
detail::format_impl(result, fmt, std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
||||
// 便捷宏 - 自动获取日志服务
|
||||
#define E2D_LOG(level, ...) \
|
||||
do { \
|
||||
if (auto logService = ::extra2d::ServiceLocator::instance().tryGetService<::extra2d::ILogger>()) { \
|
||||
if (logService->isEnabled(level)) { \
|
||||
logService->log(level, ::extra2d::format_str(__VA_ARGS__)); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
#define E2D_LOG(level, ...) \
|
||||
do { \
|
||||
if (auto logService = ::extra2d::ServiceLocator::instance() \
|
||||
.tryGetService<::extra2d::ILogger>()) { \
|
||||
if (logService->isEnabled(level)) { \
|
||||
logService->log(level, ::extra2d::format_str(__VA_ARGS__)); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define E2D_LOG_TRACE(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
|
||||
#define E2D_LOG_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_DEBUG(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
|
||||
#define E2D_LOG_DEBUG(...) E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_INFO(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
|
||||
#define E2D_LOG_INFO(...) E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_WARN(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
|
||||
#define E2D_LOG_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_ERROR(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
|
||||
#define E2D_LOG_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
|
||||
|
||||
#define E2D_LOG_FATAL(...) \
|
||||
E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
|
||||
#define E2D_LOG_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
|
||||
|
||||
// 简写宏
|
||||
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
|
||||
|
|
@ -224,9 +216,9 @@ std::string format_str(const char* fmt, Args&&... args) {
|
|||
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
|
||||
|
||||
#ifdef E2D_DEBUG
|
||||
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
|
||||
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
|
||||
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__)
|
||||
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define E2D_DEBUG_LOG(...)
|
||||
#define E2D_TRACE(...)
|
||||
#define E2D_DEBUG_LOG(...)
|
||||
#define E2D_TRACE(...)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
/**
|
||||
* @file logger.h
|
||||
* @brief 日志工具头文件
|
||||
*
|
||||
* 提供便捷的日志宏定义,实际实现位于 logger_service.h
|
||||
*/
|
||||
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
|
||||
|
|
@ -1,96 +1,97 @@
|
|||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Registry& Registry::instance() {
|
||||
static Registry instance;
|
||||
return instance;
|
||||
Registry &Registry::instance() {
|
||||
static Registry instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool Registry::init() {
|
||||
auto sorted = topologicalSort();
|
||||
auto sorted = topologicalSort();
|
||||
|
||||
std::cout << "[Registry] Initializing " << sorted.size() << " modules..." << std::endl;
|
||||
std::cout << "[Registry] Initializing " << sorted.size() << " modules..."
|
||||
<< std::endl;
|
||||
|
||||
for (auto* module : sorted) {
|
||||
std::cout << "[Registry] Initializing module: " << module->name() << std::endl;
|
||||
if (!module->init()) {
|
||||
std::cerr << "[Registry] Failed to initialize module: " << module->name() << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "[Registry] Module " << module->name() << " initialized successfully" << std::endl;
|
||||
for (auto *module : sorted) {
|
||||
std::cout << "[Registry] Initializing module: " << module->name()
|
||||
<< std::endl;
|
||||
if (!module->init()) {
|
||||
std::cerr << "[Registry] Failed to initialize module: " << module->name()
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
std::cout << "[Registry] Module " << module->name()
|
||||
<< " initialized successfully" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "[Registry] All modules initialized" << std::endl;
|
||||
return true;
|
||||
std::cout << "[Registry] All modules initialized" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Registry::shutdown() {
|
||||
auto sorted = topologicalSort();
|
||||
|
||||
// 反向关闭
|
||||
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) {
|
||||
(*it)->shutdown();
|
||||
}
|
||||
auto sorted = topologicalSort();
|
||||
|
||||
// 反向关闭
|
||||
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) {
|
||||
(*it)->shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void Registry::clear() {
|
||||
shutdown();
|
||||
modules_.clear();
|
||||
shutdown();
|
||||
modules_.clear();
|
||||
}
|
||||
|
||||
std::vector<Module*> Registry::topologicalSort() {
|
||||
std::vector<Module*> result;
|
||||
std::unordered_map<Module*, int> inDegree;
|
||||
std::unordered_map<Module*, std::vector<Module*>> adj;
|
||||
|
||||
// 构建图
|
||||
for (auto& [typeIdx, module] : modules_) {
|
||||
inDegree[module.get()] = 0;
|
||||
std::vector<Module *> Registry::topologicalSort() {
|
||||
std::vector<Module *> result;
|
||||
std::unordered_map<Module *, int> inDegree;
|
||||
std::unordered_map<Module *, std::vector<Module *>> adj;
|
||||
|
||||
// 构建图
|
||||
for (auto &[typeIdx, module] : modules_) {
|
||||
inDegree[module.get()] = 0;
|
||||
}
|
||||
|
||||
for (auto &[typeIdx, module] : modules_) {
|
||||
for (auto &depType : module->deps()) {
|
||||
Module *dep = get(depType);
|
||||
if (dep) {
|
||||
adj[dep].push_back(module.get());
|
||||
inDegree[module.get()]++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& [typeIdx, module] : modules_) {
|
||||
for (auto& depType : module->deps()) {
|
||||
Module* dep = get(depType);
|
||||
if (dep) {
|
||||
adj[dep].push_back(module.get());
|
||||
inDegree[module.get()]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级队列(优先级小的先处理)
|
||||
auto cmp = [](Module *a, Module *b) { return a->priority() > b->priority(); };
|
||||
std::priority_queue<Module *, std::vector<Module *>, decltype(cmp)> pq(cmp);
|
||||
|
||||
for (auto &[mod, degree] : inDegree) {
|
||||
if (degree == 0) {
|
||||
pq.push(mod);
|
||||
}
|
||||
|
||||
// 优先级队列(优先级小的先处理)
|
||||
auto cmp = [](Module* a, Module* b) {
|
||||
return a->priority() > b->priority();
|
||||
};
|
||||
std::priority_queue<Module*, std::vector<Module*>, decltype(cmp)> pq(cmp);
|
||||
|
||||
for (auto& [mod, degree] : inDegree) {
|
||||
if (degree == 0) {
|
||||
pq.push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
// 拓扑排序
|
||||
while (!pq.empty()) {
|
||||
Module *curr = pq.top();
|
||||
pq.pop();
|
||||
result.push_back(curr);
|
||||
|
||||
for (Module *next : adj[curr]) {
|
||||
inDegree[next]--;
|
||||
if (inDegree[next] == 0) {
|
||||
pq.push(next);
|
||||
}
|
||||
}
|
||||
|
||||
// 拓扑排序
|
||||
while (!pq.empty()) {
|
||||
Module* curr = pq.top();
|
||||
pq.pop();
|
||||
result.push_back(curr);
|
||||
|
||||
for (Module* next : adj[curr]) {
|
||||
inDegree[next]--;
|
||||
if (inDegree[next] == 0) {
|
||||
pq.push(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,135 +1,142 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace graphics {
|
||||
|
||||
std::unordered_map<std::string, BackendFactory::BackendEntry>& BackendFactory::registry() {
|
||||
static std::unordered_map<std::string, BackendEntry> reg;
|
||||
return reg;
|
||||
std::unordered_map<std::string, BackendFactory::BackendEntry> &
|
||||
BackendFactory::registry() {
|
||||
static std::unordered_map<std::string, BackendEntry> reg;
|
||||
return reg;
|
||||
}
|
||||
|
||||
void BackendFactory::reg(const std::string& name, BackendFn backend,
|
||||
const std::vector<std::string>& windowBackends) {
|
||||
registry()[name] = {backend, windowBackends};
|
||||
E2D_LOG_DEBUG("Registered graphics backend: {} (window backends: {})",
|
||||
name, windowBackends.size());
|
||||
void BackendFactory::reg(const std::string &name, BackendFn backend,
|
||||
const std::vector<std::string> &windowBackends) {
|
||||
registry()[name] = {backend, windowBackends};
|
||||
E2D_LOG_DEBUG("Registered graphics backend: {} (window backends: {})", name,
|
||||
windowBackends.size());
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend> BackendFactory::createBackend(const std::string& name) {
|
||||
auto& reg = registry();
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && it->second.createFn) {
|
||||
E2D_LOG_INFO("Creating graphics backend: {}", name);
|
||||
return it->second.createFn();
|
||||
}
|
||||
E2D_LOG_ERROR("Graphics backend '{}' not found", name);
|
||||
return nullptr;
|
||||
UniquePtr<RenderBackend>
|
||||
BackendFactory::createBackend(const std::string &name) {
|
||||
auto ® = registry();
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && it->second.createFn) {
|
||||
E2D_LOG_INFO("Creating graphics backend: {}", name);
|
||||
return it->second.createFn();
|
||||
}
|
||||
E2D_LOG_ERROR("Graphics backend '{}' not found", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend> BackendFactory::createDefaultBackend() {
|
||||
std::string recommended = getRecommendedBackend();
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("No graphics backend available");
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
std::string recommended = getRecommendedBackend();
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("No graphics backend available");
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
}
|
||||
|
||||
UniquePtr<RenderBackend> BackendFactory::createBackendForWindow(const std::string& windowBackend) {
|
||||
std::string recommended = getRecommendedBackendForWindow(windowBackend);
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("No compatible graphics backend for window backend: {}", windowBackend);
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
UniquePtr<RenderBackend>
|
||||
BackendFactory::createBackendForWindow(const std::string &windowBackend) {
|
||||
std::string recommended = getRecommendedBackendForWindow(windowBackend);
|
||||
if (recommended.empty()) {
|
||||
E2D_LOG_ERROR("No compatible graphics backend for window backend: {}",
|
||||
windowBackend);
|
||||
return nullptr;
|
||||
}
|
||||
return createBackend(recommended);
|
||||
}
|
||||
|
||||
std::vector<std::string> BackendFactory::backends() {
|
||||
std::vector<std::string> result;
|
||||
for (const auto& pair : registry()) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
return result;
|
||||
std::vector<std::string> result;
|
||||
for (const auto &pair : registry()) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BackendFactory::has(const std::string& name) {
|
||||
return registry().find(name) != registry().end();
|
||||
bool BackendFactory::has(const std::string &name) {
|
||||
return registry().find(name) != registry().end();
|
||||
}
|
||||
|
||||
std::string BackendFactory::getRecommendedBackend() {
|
||||
auto& reg = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"
|
||||
};
|
||||
|
||||
for (const auto& name : priority) {
|
||||
if (reg.find(name) != reg.end()) {
|
||||
return name;
|
||||
}
|
||||
auto ® = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"};
|
||||
|
||||
for (const auto &name : priority) {
|
||||
if (reg.find(name) != reg.end()) {
|
||||
return name;
|
||||
}
|
||||
|
||||
if (!reg.empty()) {
|
||||
return reg.begin()->first;
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("No graphics backend registered");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!reg.empty()) {
|
||||
return reg.begin()->first;
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("No graphics backend registered");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string BackendFactory::getRecommendedBackendForWindow(const std::string& windowBackend) {
|
||||
auto& reg = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"
|
||||
};
|
||||
|
||||
for (const auto& name : priority) {
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && isCompatible(name, windowBackend)) {
|
||||
return name;
|
||||
}
|
||||
std::string BackendFactory::getRecommendedBackendForWindow(
|
||||
const std::string &windowBackend) {
|
||||
auto ® = registry();
|
||||
|
||||
static const std::vector<std::string> priority = {
|
||||
"vulkan", "opengl", "d3d12", "d3d11", "metal", "opengles"};
|
||||
|
||||
for (const auto &name : priority) {
|
||||
auto it = reg.find(name);
|
||||
if (it != reg.end() && isCompatible(name, windowBackend)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
for (const auto& pair : reg) {
|
||||
if (isCompatible(pair.first, windowBackend)) {
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &pair : reg) {
|
||||
if (isCompatible(pair.first, windowBackend)) {
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("No compatible graphics backend for window backend: {}", windowBackend);
|
||||
return "";
|
||||
}
|
||||
|
||||
E2D_LOG_WARN("No compatible graphics backend for window backend: {}",
|
||||
windowBackend);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool BackendFactory::isCompatible(const std::string& graphicsBackend, const std::string& windowBackend) {
|
||||
auto& reg = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it == reg.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& windowBackends = it->second.windowBackends;
|
||||
if (windowBackends.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& wb : windowBackends) {
|
||||
if (wb == windowBackend) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BackendFactory::isCompatible(const std::string &graphicsBackend,
|
||||
const std::string &windowBackend) {
|
||||
auto ® = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it == reg.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &windowBackends = it->second.windowBackends;
|
||||
if (windowBackends.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto &wb : windowBackends) {
|
||||
if (wb == windowBackend) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> BackendFactory::getSupportedWindowBackends(const std::string& graphicsBackend) {
|
||||
auto& reg = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it != reg.end()) {
|
||||
return it->second.windowBackends;
|
||||
}
|
||||
return {};
|
||||
std::vector<std::string>
|
||||
BackendFactory::getSupportedWindowBackends(const std::string &graphicsBackend) {
|
||||
auto ® = registry();
|
||||
auto it = reg.find(graphicsBackend);
|
||||
if (it != reg.end()) {
|
||||
return it->second.windowBackends;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include <cstring>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_buffer.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <cstring>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -11,161 +13,162 @@ namespace extra2d {
|
|||
|
||||
GLBuffer::GLBuffer() = default;
|
||||
|
||||
GLBuffer::~GLBuffer() {
|
||||
GLBuffer::~GLBuffer() { shutdown(); }
|
||||
|
||||
bool GLBuffer::init(const BufferDesc &desc) {
|
||||
if (bufferID_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool GLBuffer::init(const BufferDesc& desc) {
|
||||
if (bufferID_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
type_ = desc.type;
|
||||
usage_ = desc.usage;
|
||||
size_ = desc.size;
|
||||
target_ = convertType(type_);
|
||||
glUsage_ = convertUsage(usage_);
|
||||
|
||||
type_ = desc.type;
|
||||
usage_ = desc.usage;
|
||||
size_ = desc.size;
|
||||
target_ = convertType(type_);
|
||||
glUsage_ = convertUsage(usage_);
|
||||
// 生成缓冲区
|
||||
glGenBuffers(1, &bufferID_);
|
||||
if (bufferID_ == 0) {
|
||||
E2D_LOG_ERROR("Failed to generate OpenGL buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成缓冲区
|
||||
glGenBuffers(1, &bufferID_);
|
||||
if (bufferID_ == 0) {
|
||||
E2D_LOG_ERROR("Failed to generate OpenGL buffer");
|
||||
return false;
|
||||
}
|
||||
// 绑定并分配缓冲区
|
||||
glBindBuffer(target_, bufferID_);
|
||||
glBufferData(target_, static_cast<GLsizeiptr>(size_), desc.initialData,
|
||||
glUsage_);
|
||||
glBindBuffer(target_, 0);
|
||||
|
||||
// 绑定并分配缓冲区
|
||||
glBindBuffer(target_, bufferID_);
|
||||
glBufferData(target_, static_cast<GLsizeiptr>(size_), desc.initialData, glUsage_);
|
||||
glBindBuffer(target_, 0);
|
||||
// 追踪显存使用
|
||||
VRAMMgr::get().allocBuffer(size_);
|
||||
|
||||
// 追踪显存使用
|
||||
VRAMMgr::get().allocBuffer(size_);
|
||||
E2D_LOG_DEBUG("GLBuffer created: ID={}, Size={}, Type={}, Usage={}",
|
||||
bufferID_, size_, static_cast<int>(type_),
|
||||
static_cast<int>(usage_));
|
||||
|
||||
E2D_LOG_DEBUG("GLBuffer created: ID={}, Size={}, Type={}, Usage={}",
|
||||
bufferID_, size_, static_cast<int>(type_), static_cast<int>(usage_));
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLBuffer::shutdown() {
|
||||
if (bufferID_ != 0) {
|
||||
if (mapped_) {
|
||||
unmap();
|
||||
}
|
||||
// 释放显存追踪
|
||||
VRAMMgr::get().freeBuffer(size_);
|
||||
glDeleteBuffers(1, &bufferID_);
|
||||
E2D_LOG_DEBUG("GLBuffer destroyed: ID={}", bufferID_);
|
||||
bufferID_ = 0;
|
||||
if (bufferID_ != 0) {
|
||||
if (mapped_) {
|
||||
unmap();
|
||||
}
|
||||
size_ = 0;
|
||||
mapped_ = false;
|
||||
mappedPtr_ = nullptr;
|
||||
// 释放显存追踪
|
||||
VRAMMgr::get().freeBuffer(size_);
|
||||
glDeleteBuffers(1, &bufferID_);
|
||||
E2D_LOG_DEBUG("GLBuffer destroyed: ID={}", bufferID_);
|
||||
bufferID_ = 0;
|
||||
}
|
||||
size_ = 0;
|
||||
mapped_ = false;
|
||||
mappedPtr_ = nullptr;
|
||||
}
|
||||
|
||||
void GLBuffer::bind() {
|
||||
if (bufferID_ != 0) {
|
||||
glBindBuffer(target_, bufferID_);
|
||||
}
|
||||
if (bufferID_ != 0) {
|
||||
glBindBuffer(target_, bufferID_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLBuffer::unbind() {
|
||||
glBindBuffer(target_, 0);
|
||||
void GLBuffer::unbind() { glBindBuffer(target_, 0); }
|
||||
|
||||
void GLBuffer::setData(const void *data, size_t size) {
|
||||
if (bufferID_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
// 如果大小相同,使用 glBufferSubData 更高效
|
||||
if (size == size_) {
|
||||
glBufferSubData(target_, 0, static_cast<GLsizeiptr>(size), data);
|
||||
} else {
|
||||
// 大小不同,重新分配
|
||||
size_ = size;
|
||||
glBufferData(target_, static_cast<GLsizeiptr>(size_), data, glUsage_);
|
||||
}
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLBuffer::setData(const void* data, size_t size) {
|
||||
if (bufferID_ == 0) {
|
||||
return;
|
||||
}
|
||||
void GLBuffer::updateData(const void *data, size_t offset, size_t size) {
|
||||
if (bufferID_ == 0 || data == nullptr || size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
if (offset + size > size_) {
|
||||
E2D_LOG_WARN(
|
||||
"GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}",
|
||||
offset, size, size_);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果大小相同,使用 glBufferSubData 更高效
|
||||
if (size == size_) {
|
||||
glBufferSubData(target_, 0, static_cast<GLsizeiptr>(size), data);
|
||||
} else {
|
||||
// 大小不同,重新分配
|
||||
size_ = size;
|
||||
glBufferData(target_, static_cast<GLsizeiptr>(size_), data, glUsage_);
|
||||
}
|
||||
|
||||
unbind();
|
||||
bind();
|
||||
glBufferSubData(target_, static_cast<GLintptr>(offset),
|
||||
static_cast<GLsizeiptr>(size), data);
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLBuffer::updateData(const void* data, size_t offset, size_t size) {
|
||||
if (bufferID_ == 0 || data == nullptr || size == 0) {
|
||||
return;
|
||||
}
|
||||
void *GLBuffer::map() {
|
||||
if (bufferID_ == 0 || mapped_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (offset + size > size_) {
|
||||
E2D_LOG_WARN("GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}",
|
||||
offset, size, size_);
|
||||
return;
|
||||
}
|
||||
bind();
|
||||
|
||||
bind();
|
||||
glBufferSubData(target_, static_cast<GLintptr>(offset), static_cast<GLsizeiptr>(size), data);
|
||||
unbind();
|
||||
}
|
||||
// 使用 glMapBufferRange 替代 glMapBuffer,更现代且安全
|
||||
GLbitfield access = GL_MAP_WRITE_BIT;
|
||||
if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) {
|
||||
access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据
|
||||
}
|
||||
|
||||
void* GLBuffer::map() {
|
||||
if (bufferID_ == 0 || mapped_) {
|
||||
return nullptr;
|
||||
}
|
||||
mappedPtr_ =
|
||||
glMapBufferRange(target_, 0, static_cast<GLsizeiptr>(size_), access);
|
||||
if (mappedPtr_) {
|
||||
mapped_ = true;
|
||||
} else {
|
||||
E2D_LOG_ERROR("Failed to map GLBuffer");
|
||||
}
|
||||
|
||||
bind();
|
||||
|
||||
// 使用 glMapBufferRange 替代 glMapBuffer,更现代且安全
|
||||
GLbitfield access = GL_MAP_WRITE_BIT;
|
||||
if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) {
|
||||
access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据
|
||||
}
|
||||
|
||||
mappedPtr_ = glMapBufferRange(target_, 0, static_cast<GLsizeiptr>(size_), access);
|
||||
if (mappedPtr_) {
|
||||
mapped_ = true;
|
||||
} else {
|
||||
E2D_LOG_ERROR("Failed to map GLBuffer");
|
||||
}
|
||||
|
||||
return mappedPtr_;
|
||||
return mappedPtr_;
|
||||
}
|
||||
|
||||
void GLBuffer::unmap() {
|
||||
if (!mapped_ || bufferID_ == 0) {
|
||||
return;
|
||||
}
|
||||
if (!mapped_ || bufferID_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
glUnmapBuffer(target_);
|
||||
mapped_ = false;
|
||||
mappedPtr_ = nullptr;
|
||||
unbind();
|
||||
glUnmapBuffer(target_);
|
||||
mapped_ = false;
|
||||
mappedPtr_ = nullptr;
|
||||
unbind();
|
||||
}
|
||||
|
||||
GLenum GLBuffer::convertUsage(BufferUsage usage) {
|
||||
switch (usage) {
|
||||
case BufferUsage::Static:
|
||||
return GL_STATIC_DRAW;
|
||||
case BufferUsage::Dynamic:
|
||||
return GL_DYNAMIC_DRAW;
|
||||
case BufferUsage::Stream:
|
||||
return GL_STREAM_DRAW;
|
||||
default:
|
||||
return GL_STATIC_DRAW;
|
||||
}
|
||||
switch (usage) {
|
||||
case BufferUsage::Static:
|
||||
return GL_STATIC_DRAW;
|
||||
case BufferUsage::Dynamic:
|
||||
return GL_DYNAMIC_DRAW;
|
||||
case BufferUsage::Stream:
|
||||
return GL_STREAM_DRAW;
|
||||
default:
|
||||
return GL_STATIC_DRAW;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum GLBuffer::convertType(BufferType type) {
|
||||
switch (type) {
|
||||
case BufferType::Vertex:
|
||||
return GL_ARRAY_BUFFER;
|
||||
case BufferType::Index:
|
||||
return GL_ELEMENT_ARRAY_BUFFER;
|
||||
case BufferType::Uniform:
|
||||
return GL_UNIFORM_BUFFER;
|
||||
default:
|
||||
return GL_ARRAY_BUFFER;
|
||||
}
|
||||
switch (type) {
|
||||
case BufferType::Vertex:
|
||||
return GL_ARRAY_BUFFER;
|
||||
case BufferType::Index:
|
||||
return GL_ELEMENT_ARRAY_BUFFER;
|
||||
case BufferType::Uniform:
|
||||
return GL_UNIFORM_BUFFER;
|
||||
default:
|
||||
return GL_ARRAY_BUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_context.h>
|
||||
#include <extra2d/graphics/memory/gpu_context.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -10,158 +9,162 @@ namespace extra2d {
|
|||
// GLContext 实现
|
||||
// ============================================================================
|
||||
|
||||
GLContext& GLContext::get() {
|
||||
static GLContext instance;
|
||||
return instance;
|
||||
GLContext &GLContext::get() {
|
||||
static GLContext instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool GLContext::init() {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 解析 OpenGL 版本
|
||||
parseVersion();
|
||||
|
||||
// 加载扩展(GLAD 已在 glad.c 中完成)
|
||||
if (!loadExtensions()) {
|
||||
E2D_LOG_ERROR("Failed to load OpenGL extensions");
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
// 标记 GPU 上下文为有效
|
||||
GPUContext::get().markValid();
|
||||
|
||||
E2D_LOG_INFO("OpenGL Context initialized");
|
||||
E2D_LOG_INFO(" Version: {}", getVersionString());
|
||||
E2D_LOG_INFO(" Vendor: {}", getVendor());
|
||||
E2D_LOG_INFO(" Renderer: {}", getRenderer());
|
||||
E2D_LOG_INFO(" Max Texture Size: {}", getMaxTextureSize());
|
||||
E2D_LOG_INFO(" Max Texture Units: {}", getMaxTextureUnits());
|
||||
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 解析 OpenGL 版本
|
||||
parseVersion();
|
||||
|
||||
// 加载扩展(GLAD 已在 glad.c 中完成)
|
||||
if (!loadExtensions()) {
|
||||
E2D_LOG_ERROR("Failed to load OpenGL extensions");
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
// 标记 GPU 上下文为有效
|
||||
GPUContext::get().markValid();
|
||||
|
||||
E2D_LOG_INFO("OpenGL Context initialized");
|
||||
E2D_LOG_INFO(" Version: {}", getVersionString());
|
||||
E2D_LOG_INFO(" Vendor: {}", getVendor());
|
||||
E2D_LOG_INFO(" Renderer: {}", getRenderer());
|
||||
E2D_LOG_INFO(" Max Texture Size: {}", getMaxTextureSize());
|
||||
E2D_LOG_INFO(" Max Texture Units: {}", getMaxTextureUnits());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLContext::shutdown() {
|
||||
// 标记 GPU 上下文为无效
|
||||
GPUContext::get().markInvalid();
|
||||
// 标记 GPU 上下文为无效
|
||||
GPUContext::get().markInvalid();
|
||||
|
||||
initialized_ = false;
|
||||
version_ = GLVersion{};
|
||||
maxTextureSize_ = -1;
|
||||
maxTextureUnits_ = -1;
|
||||
maxVertexAttribs_ = -1;
|
||||
maxUniformBufferBindings_ = -1;
|
||||
initialized_ = false;
|
||||
version_ = GLVersion{};
|
||||
maxTextureSize_ = -1;
|
||||
maxTextureUnits_ = -1;
|
||||
maxVertexAttribs_ = -1;
|
||||
maxUniformBufferBindings_ = -1;
|
||||
}
|
||||
|
||||
std::string GLContext::getVersionString() const {
|
||||
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
return version ? version : "Unknown";
|
||||
const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
|
||||
return version ? version : "Unknown";
|
||||
}
|
||||
|
||||
std::string GLContext::getVendor() const {
|
||||
const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
return vendor ? vendor : "Unknown";
|
||||
const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
|
||||
return vendor ? vendor : "Unknown";
|
||||
}
|
||||
|
||||
std::string GLContext::getRenderer() const {
|
||||
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||
return renderer ? renderer : "Unknown";
|
||||
const char *renderer =
|
||||
reinterpret_cast<const char *>(glGetString(GL_RENDERER));
|
||||
return renderer ? renderer : "Unknown";
|
||||
}
|
||||
|
||||
bool GLContext::hasExtension(const std::string& extension) const {
|
||||
GLint numExtensions = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
|
||||
bool GLContext::hasExtension(const std::string &extension) const {
|
||||
GLint numExtensions = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
|
||||
|
||||
for (GLint i = 0; i < numExtensions; ++i) {
|
||||
const char* ext = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
|
||||
if (ext && extension == ext) {
|
||||
return true;
|
||||
}
|
||||
for (GLint i = 0; i < numExtensions; ++i) {
|
||||
const char *ext =
|
||||
reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
|
||||
if (ext && extension == ext) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int GLContext::getMaxTextureSize() const {
|
||||
if (maxTextureSize_ < 0) {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
|
||||
}
|
||||
return maxTextureSize_;
|
||||
if (maxTextureSize_ < 0) {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
|
||||
}
|
||||
return maxTextureSize_;
|
||||
}
|
||||
|
||||
int GLContext::getMaxTextureUnits() const {
|
||||
if (maxTextureUnits_ < 0) {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_);
|
||||
}
|
||||
return maxTextureUnits_;
|
||||
if (maxTextureUnits_ < 0) {
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_);
|
||||
}
|
||||
return maxTextureUnits_;
|
||||
}
|
||||
|
||||
int GLContext::getMaxVertexAttribs() const {
|
||||
if (maxVertexAttribs_ < 0) {
|
||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_);
|
||||
}
|
||||
return maxVertexAttribs_;
|
||||
if (maxVertexAttribs_ < 0) {
|
||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_);
|
||||
}
|
||||
return maxVertexAttribs_;
|
||||
}
|
||||
|
||||
int GLContext::getMaxUniformBufferBindings() const {
|
||||
if (maxUniformBufferBindings_ < 0) {
|
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_);
|
||||
}
|
||||
return maxUniformBufferBindings_;
|
||||
if (maxUniformBufferBindings_ < 0) {
|
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_);
|
||||
}
|
||||
return maxUniformBufferBindings_;
|
||||
}
|
||||
|
||||
bool GLContext::hasVAO() const {
|
||||
// OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO
|
||||
if (version_.es) {
|
||||
return version_.major >= 3;
|
||||
}
|
||||
return version_.major > 3 || (version_.major == 3 && version_.minor >= 0);
|
||||
// OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO
|
||||
if (version_.es) {
|
||||
return version_.major >= 3;
|
||||
}
|
||||
return version_.major > 3 || (version_.major == 3 && version_.minor >= 0);
|
||||
}
|
||||
|
||||
bool GLContext::hasFBO() const {
|
||||
// OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO
|
||||
if (version_.es) {
|
||||
return version_.major >= 2;
|
||||
}
|
||||
return version_.major >= 3;
|
||||
// OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO
|
||||
if (version_.es) {
|
||||
return version_.major >= 2;
|
||||
}
|
||||
return version_.major >= 3;
|
||||
}
|
||||
|
||||
bool GLContext::hasShader() const {
|
||||
// OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader
|
||||
if (version_.es) {
|
||||
return version_.major >= 2;
|
||||
}
|
||||
// OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader
|
||||
if (version_.es) {
|
||||
return version_.major >= 2;
|
||||
}
|
||||
return version_.major >= 2;
|
||||
}
|
||||
|
||||
void GLContext::parseVersion() {
|
||||
const char* versionStr = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
if (!versionStr) {
|
||||
version_ = GLVersion{0, 0, false};
|
||||
return;
|
||||
}
|
||||
const char *versionStr =
|
||||
reinterpret_cast<const char *>(glGetString(GL_VERSION));
|
||||
if (!versionStr) {
|
||||
version_ = GLVersion{0, 0, false};
|
||||
return;
|
||||
}
|
||||
|
||||
std::string version(versionStr);
|
||||
std::string version(versionStr);
|
||||
|
||||
// 检查是否为 OpenGL ES
|
||||
if (version.find("OpenGL ES") != std::string::npos) {
|
||||
version_.es = true;
|
||||
// 解析 ES 版本号,格式如 "OpenGL ES 3.0"
|
||||
std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major, &version_.minor);
|
||||
} else {
|
||||
version_.es = false;
|
||||
// 解析桌面版本号,格式如 "3.3.0 NVIDIA"
|
||||
std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor);
|
||||
}
|
||||
// 检查是否为 OpenGL ES
|
||||
if (version.find("OpenGL ES") != std::string::npos) {
|
||||
version_.es = true;
|
||||
// 解析 ES 版本号,格式如 "OpenGL ES 3.0"
|
||||
std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major,
|
||||
&version_.minor);
|
||||
} else {
|
||||
version_.es = false;
|
||||
// 解析桌面版本号,格式如 "3.3.0 NVIDIA"
|
||||
std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLContext::loadExtensions() {
|
||||
// GLAD 已经在 glad.c 中加载了所有扩展
|
||||
// 这里可以添加额外的扩展检查
|
||||
return true;
|
||||
// GLAD 已经在 glad.c 中加载了所有扩展
|
||||
// 这里可以添加额外的扩展检查
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -10,259 +12,261 @@ namespace extra2d {
|
|||
|
||||
GLFramebuffer::GLFramebuffer() = default;
|
||||
|
||||
GLFramebuffer::~GLFramebuffer() {
|
||||
GLFramebuffer::~GLFramebuffer() { shutdown(); }
|
||||
|
||||
bool GLFramebuffer::init(const FramebufferDesc &desc) {
|
||||
if (fboID_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool GLFramebuffer::init(const FramebufferDesc& desc) {
|
||||
if (fboID_ != 0) {
|
||||
shutdown();
|
||||
}
|
||||
width_ = desc.width;
|
||||
height_ = desc.height;
|
||||
numColorAttachments_ = desc.colorAttachments;
|
||||
hasDepth_ = desc.hasDepth;
|
||||
hasStencil_ = desc.hasStencil;
|
||||
|
||||
width_ = desc.width;
|
||||
height_ = desc.height;
|
||||
numColorAttachments_ = desc.colorAttachments;
|
||||
hasDepth_ = desc.hasDepth;
|
||||
hasStencil_ = desc.hasStencil;
|
||||
// 限制颜色附件数
|
||||
if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) {
|
||||
numColorAttachments_ = MAX_COLOR_ATTACHMENTS;
|
||||
}
|
||||
|
||||
// 限制颜色附件数
|
||||
if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) {
|
||||
numColorAttachments_ = MAX_COLOR_ATTACHMENTS;
|
||||
}
|
||||
// 生成 FBO
|
||||
glGenFramebuffers(1, &fboID_);
|
||||
if (fboID_ == 0) {
|
||||
E2D_LOG_ERROR("Failed to generate OpenGL framebuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成 FBO
|
||||
glGenFramebuffers(1, &fboID_);
|
||||
if (fboID_ == 0) {
|
||||
E2D_LOG_ERROR("Failed to generate OpenGL framebuffer");
|
||||
return false;
|
||||
}
|
||||
E2D_LOG_DEBUG("GLFramebuffer created: ID={}, Size={}x{}, ColorAttachments={}",
|
||||
fboID_, width_, height_, numColorAttachments_);
|
||||
|
||||
E2D_LOG_DEBUG("GLFramebuffer created: ID={}, Size={}x{}, ColorAttachments={}",
|
||||
fboID_, width_, height_, numColorAttachments_);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLFramebuffer::shutdown() {
|
||||
if (fboID_ != 0) {
|
||||
glDeleteFramebuffers(1, &fboID_);
|
||||
E2D_LOG_DEBUG("GLFramebuffer destroyed: ID={}", fboID_);
|
||||
fboID_ = 0;
|
||||
}
|
||||
if (fboID_ != 0) {
|
||||
glDeleteFramebuffers(1, &fboID_);
|
||||
E2D_LOG_DEBUG("GLFramebuffer destroyed: ID={}", fboID_);
|
||||
fboID_ = 0;
|
||||
}
|
||||
|
||||
// 清理纹理引用
|
||||
for (auto& tex : colorTextures_) {
|
||||
tex.reset();
|
||||
}
|
||||
depthTexture_.reset();
|
||||
depthStencilTexture_.reset();
|
||||
// 清理纹理引用
|
||||
for (auto &tex : colorTextures_) {
|
||||
tex.reset();
|
||||
}
|
||||
depthTexture_.reset();
|
||||
depthStencilTexture_.reset();
|
||||
|
||||
hasInternalTextures_ = false;
|
||||
hasInternalTextures_ = false;
|
||||
}
|
||||
|
||||
void GLFramebuffer::bind() {
|
||||
if (fboID_ != 0) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboID_);
|
||||
}
|
||||
if (fboID_ != 0) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fboID_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFramebuffer::unbind() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
void GLFramebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
|
||||
|
||||
void GLFramebuffer::attachColorTexture(Ptr<Texture> texture, int attachment) {
|
||||
if (fboID_ == 0 || !texture || attachment < 0 || attachment >= MAX_COLOR_ATTACHMENTS) {
|
||||
return;
|
||||
}
|
||||
if (fboID_ == 0 || !texture || attachment < 0 ||
|
||||
attachment >= MAX_COLOR_ATTACHMENTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
bind();
|
||||
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment),
|
||||
GL_TEXTURE_2D, texID, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment),
|
||||
GL_TEXTURE_2D, texID, 0);
|
||||
|
||||
colorTextures_[attachment] = texture;
|
||||
colorTextures_[attachment] = texture;
|
||||
|
||||
unbind();
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLFramebuffer::attachDepthTexture(Ptr<Texture> texture) {
|
||||
if (fboID_ == 0 || !texture) {
|
||||
return;
|
||||
}
|
||||
if (fboID_ == 0 || !texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
bind();
|
||||
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_TEXTURE_2D, texID, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
texID, 0);
|
||||
|
||||
depthTexture_ = texture;
|
||||
hasDepth_ = true;
|
||||
hasStencil_ = false;
|
||||
depthTexture_ = texture;
|
||||
hasDepth_ = true;
|
||||
hasStencil_ = false;
|
||||
|
||||
unbind();
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLFramebuffer::attachDepthStencilTexture(Ptr<Texture> texture) {
|
||||
if (fboID_ == 0 || !texture) {
|
||||
return;
|
||||
}
|
||||
if (fboID_ == 0 || !texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
bind();
|
||||
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
// 获取 OpenGL 纹理 ID
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, texID, 0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, texID, 0);
|
||||
|
||||
depthStencilTexture_ = texture;
|
||||
hasDepth_ = true;
|
||||
hasStencil_ = true;
|
||||
depthStencilTexture_ = texture;
|
||||
hasDepth_ = true;
|
||||
hasStencil_ = true;
|
||||
|
||||
unbind();
|
||||
unbind();
|
||||
}
|
||||
|
||||
bool GLFramebuffer::isComplete() {
|
||||
if (fboID_ == 0) {
|
||||
return false;
|
||||
}
|
||||
if (fboID_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bind();
|
||||
bool complete = checkStatus();
|
||||
unbind();
|
||||
bind();
|
||||
bool complete = checkStatus();
|
||||
unbind();
|
||||
|
||||
return complete;
|
||||
return complete;
|
||||
}
|
||||
|
||||
Ptr<Texture> GLFramebuffer::getColorTexture(int attachment) const {
|
||||
if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) {
|
||||
return colorTextures_[attachment];
|
||||
}
|
||||
return nullptr;
|
||||
if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) {
|
||||
return colorTextures_[attachment];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<Texture> GLFramebuffer::getDepthTexture() const {
|
||||
return depthTexture_;
|
||||
}
|
||||
Ptr<Texture> GLFramebuffer::getDepthTexture() const { return depthTexture_; }
|
||||
|
||||
void GLFramebuffer::clear(const Color& color, bool clearColor,
|
||||
bool clearDepth, bool clearStencil) {
|
||||
if (fboID_ == 0) {
|
||||
return;
|
||||
}
|
||||
void GLFramebuffer::clear(const Color &color, bool clearColor, bool clearDepth,
|
||||
bool clearStencil) {
|
||||
if (fboID_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bind();
|
||||
bind();
|
||||
|
||||
GLbitfield mask = 0;
|
||||
GLbitfield mask = 0;
|
||||
|
||||
if (clearColor) {
|
||||
mask |= GL_COLOR_BUFFER_BIT;
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
if (clearColor) {
|
||||
mask |= GL_COLOR_BUFFER_BIT;
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
if (clearDepth) {
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
glClearDepthf(1.0f);
|
||||
}
|
||||
if (clearDepth) {
|
||||
mask |= GL_DEPTH_BUFFER_BIT;
|
||||
glClearDepthf(1.0f);
|
||||
}
|
||||
|
||||
if (clearStencil) {
|
||||
mask |= GL_STENCIL_BUFFER_BIT;
|
||||
glClearStencil(0);
|
||||
}
|
||||
if (clearStencil) {
|
||||
mask |= GL_STENCIL_BUFFER_BIT;
|
||||
glClearStencil(0);
|
||||
}
|
||||
|
||||
if (mask != 0) {
|
||||
glClear(mask);
|
||||
}
|
||||
if (mask != 0) {
|
||||
glClear(mask);
|
||||
}
|
||||
|
||||
unbind();
|
||||
unbind();
|
||||
}
|
||||
|
||||
void GLFramebuffer::setViewport(int x, int y, int width, int height) {
|
||||
glViewport(x, y, width, height);
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
bool GLFramebuffer::readPixels(int x, int y, int width, int height,
|
||||
std::vector<uint8_t>& outData) {
|
||||
if (fboID_ == 0 || width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
bool GLFramebuffer::readPixels(int x, int y, int width, int height,
|
||||
std::vector<uint8_t> &outData) {
|
||||
if (fboID_ == 0 || width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算需要的缓冲区大小 (RGBA8)
|
||||
size_t dataSize = width * height * 4;
|
||||
outData.resize(dataSize);
|
||||
// 计算需要的缓冲区大小 (RGBA8)
|
||||
size_t dataSize = width * height * 4;
|
||||
outData.resize(dataSize);
|
||||
|
||||
bind();
|
||||
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data());
|
||||
unbind();
|
||||
bind();
|
||||
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data());
|
||||
unbind();
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLFramebuffer::createWithTextures(int width, int height,
|
||||
bool GLFramebuffer::createWithTextures(int width, int height,
|
||||
PixelFormat colorFormat,
|
||||
PixelFormat depthFormat) {
|
||||
FramebufferDesc desc;
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.colorAttachments = 1;
|
||||
desc.hasDepth = (depthFormat != PixelFormat::RGBA8);
|
||||
desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8);
|
||||
FramebufferDesc desc;
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.colorAttachments = 1;
|
||||
desc.hasDepth = (depthFormat != PixelFormat::RGBA8);
|
||||
desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8);
|
||||
|
||||
if (!init(desc)) {
|
||||
return false;
|
||||
}
|
||||
if (!init(desc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasInternalTextures_ = true;
|
||||
hasInternalTextures_ = true;
|
||||
|
||||
// 创建颜色纹理
|
||||
// 注意:这里简化处理,实际应该通过纹理工厂创建
|
||||
// 暂时返回 true,实际纹理创建由调用者处理
|
||||
// 创建颜色纹理
|
||||
// 注意:这里简化处理,实际应该通过纹理工厂创建
|
||||
// 暂时返回 true,实际纹理创建由调用者处理
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLFramebuffer::checkStatus() {
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
switch (status) {
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
return true;
|
||||
case GL_FRAMEBUFFER_UNDEFINED:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNDEFINED");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
||||
break;
|
||||
switch (status) {
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
return true;
|
||||
case GL_FRAMEBUFFER_UNDEFINED:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNDEFINED");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
E2D_LOG_ERROR(
|
||||
"Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
E2D_LOG_ERROR(
|
||||
"Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
||||
break;
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
||||
E2D_LOG_ERROR(
|
||||
"Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
||||
E2D_LOG_ERROR(
|
||||
"Framebuffer incomplete: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
|
||||
break;
|
||||
#endif
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNSUPPORTED");
|
||||
break;
|
||||
default:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: Unknown error {}", status);
|
||||
break;
|
||||
}
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: GL_FRAMEBUFFER_UNSUPPORTED");
|
||||
break;
|
||||
default:
|
||||
E2D_LOG_ERROR("Framebuffer incomplete: Unknown error {}", status);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
GLenum GLFramebuffer::getColorAttachment(int index) {
|
||||
return GL_COLOR_ATTACHMENT0 + index;
|
||||
return GL_COLOR_ATTACHMENT0 + index;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_pipeline.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -9,214 +11,225 @@ namespace extra2d {
|
|||
|
||||
GLPipeline::GLPipeline() = default;
|
||||
|
||||
GLPipeline::~GLPipeline() {
|
||||
GLPipeline::~GLPipeline() { shutdown(); }
|
||||
|
||||
bool GLPipeline::init(const PipelineDesc &desc) {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
blendMode_ = desc.blendMode;
|
||||
blendEnabled_ = desc.blendEnabled;
|
||||
depthTest_ = desc.depthTest;
|
||||
depthWrite_ = desc.depthWrite;
|
||||
depthFunc_ = desc.depthFunc;
|
||||
cullMode_ = desc.cullMode;
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
E2D_LOG_DEBUG(
|
||||
"GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
|
||||
static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLPipeline::init(const PipelineDesc& desc) {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
blendMode_ = desc.blendMode;
|
||||
blendEnabled_ = desc.blendEnabled;
|
||||
depthTest_ = desc.depthTest;
|
||||
depthWrite_ = desc.depthWrite;
|
||||
depthFunc_ = desc.depthFunc;
|
||||
cullMode_ = desc.cullMode;
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
E2D_LOG_DEBUG("GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
|
||||
static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLPipeline::shutdown() {
|
||||
initialized_ = false;
|
||||
}
|
||||
void GLPipeline::shutdown() { initialized_ = false; }
|
||||
|
||||
void GLPipeline::bind() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAllStates();
|
||||
applyAllStates();
|
||||
}
|
||||
|
||||
void GLPipeline::unbind() {
|
||||
// OpenGL 不需要显式解绑管线
|
||||
// OpenGL 不需要显式解绑管线
|
||||
}
|
||||
|
||||
void GLPipeline::setBlendMode(BlendMode mode) {
|
||||
blendMode_ = mode;
|
||||
applyBlendState();
|
||||
blendMode_ = mode;
|
||||
applyBlendState();
|
||||
}
|
||||
|
||||
void GLPipeline::setDepthTest(bool enabled) {
|
||||
depthTest_ = enabled;
|
||||
applyDepthState();
|
||||
depthTest_ = enabled;
|
||||
applyDepthState();
|
||||
}
|
||||
|
||||
void GLPipeline::setDepthWrite(bool enabled) {
|
||||
depthWrite_ = enabled;
|
||||
applyDepthState();
|
||||
depthWrite_ = enabled;
|
||||
applyDepthState();
|
||||
}
|
||||
|
||||
void GLPipeline::setDepthFunc(DepthFunc func) {
|
||||
depthFunc_ = func;
|
||||
applyDepthState();
|
||||
depthFunc_ = func;
|
||||
applyDepthState();
|
||||
}
|
||||
|
||||
void GLPipeline::setCullMode(CullMode mode) {
|
||||
cullMode_ = mode;
|
||||
applyCullState();
|
||||
cullMode_ = mode;
|
||||
applyCullState();
|
||||
}
|
||||
|
||||
void GLPipeline::setViewport(int x, int y, int width, int height) {
|
||||
viewportX_ = x;
|
||||
viewportY_ = y;
|
||||
viewportWidth_ = width;
|
||||
viewportHeight_ = height;
|
||||
viewportX_ = x;
|
||||
viewportY_ = y;
|
||||
viewportWidth_ = width;
|
||||
viewportHeight_ = height;
|
||||
|
||||
// 检查缓存,避免冗余调用
|
||||
if (x != cachedViewportX_ || y != cachedViewportY_ ||
|
||||
width != cachedViewportWidth_ || height != cachedViewportHeight_) {
|
||||
glViewport(x, y, width, height);
|
||||
cachedViewportX_ = x;
|
||||
cachedViewportY_ = y;
|
||||
cachedViewportWidth_ = width;
|
||||
cachedViewportHeight_ = height;
|
||||
}
|
||||
// 检查缓存,避免冗余调用
|
||||
if (x != cachedViewportX_ || y != cachedViewportY_ ||
|
||||
width != cachedViewportWidth_ || height != cachedViewportHeight_) {
|
||||
glViewport(x, y, width, height);
|
||||
cachedViewportX_ = x;
|
||||
cachedViewportY_ = y;
|
||||
cachedViewportWidth_ = width;
|
||||
cachedViewportHeight_ = height;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::getViewport(int& x, int& y, int& width, int& height) const {
|
||||
x = viewportX_;
|
||||
y = viewportY_;
|
||||
width = viewportWidth_;
|
||||
height = viewportHeight_;
|
||||
void GLPipeline::getViewport(int &x, int &y, int &width, int &height) const {
|
||||
x = viewportX_;
|
||||
y = viewportY_;
|
||||
width = viewportWidth_;
|
||||
height = viewportHeight_;
|
||||
}
|
||||
|
||||
void GLPipeline::applyAllStates() {
|
||||
applyBlendState();
|
||||
applyDepthState();
|
||||
applyCullState();
|
||||
applyBlendState();
|
||||
applyDepthState();
|
||||
applyCullState();
|
||||
|
||||
// 应用视口
|
||||
if (viewportWidth_ > 0 && viewportHeight_ > 0) {
|
||||
setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
|
||||
}
|
||||
// 应用视口
|
||||
if (viewportWidth_ > 0 && viewportHeight_ > 0) {
|
||||
setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::applyBlendState() {
|
||||
// 检查是否需要启用/禁用混合
|
||||
if (blendEnabled_ != cachedBlendEnabled_) {
|
||||
if (blendEnabled_) {
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
cachedBlendEnabled_ = blendEnabled_;
|
||||
// 检查是否需要启用/禁用混合
|
||||
if (blendEnabled_ != cachedBlendEnabled_) {
|
||||
if (blendEnabled_) {
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
cachedBlendEnabled_ = blendEnabled_;
|
||||
}
|
||||
|
||||
// 如果禁用了混合,不需要设置混合函数
|
||||
if (!blendEnabled_) {
|
||||
return;
|
||||
}
|
||||
// 如果禁用了混合,不需要设置混合函数
|
||||
if (!blendEnabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查混合模式是否改变
|
||||
if (blendMode_ != cachedBlendMode_) {
|
||||
GLenum srcFactor, dstFactor;
|
||||
getBlendFactors(blendMode_, srcFactor, dstFactor);
|
||||
glBlendFunc(srcFactor, dstFactor);
|
||||
cachedBlendMode_ = blendMode_;
|
||||
}
|
||||
// 检查混合模式是否改变
|
||||
if (blendMode_ != cachedBlendMode_) {
|
||||
GLenum srcFactor, dstFactor;
|
||||
getBlendFactors(blendMode_, srcFactor, dstFactor);
|
||||
glBlendFunc(srcFactor, dstFactor);
|
||||
cachedBlendMode_ = blendMode_;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::applyDepthState() {
|
||||
// 深度测试
|
||||
if (depthTest_ != cachedDepthTest_) {
|
||||
if (depthTest_) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
cachedDepthTest_ = depthTest_;
|
||||
// 深度测试
|
||||
if (depthTest_ != cachedDepthTest_) {
|
||||
if (depthTest_) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
cachedDepthTest_ = depthTest_;
|
||||
}
|
||||
|
||||
// 深度写入
|
||||
if (depthWrite_ != cachedDepthWrite_) {
|
||||
glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE);
|
||||
cachedDepthWrite_ = depthWrite_;
|
||||
}
|
||||
// 深度写入
|
||||
if (depthWrite_ != cachedDepthWrite_) {
|
||||
glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE);
|
||||
cachedDepthWrite_ = depthWrite_;
|
||||
}
|
||||
|
||||
// 深度函数
|
||||
if (depthFunc_ != cachedDepthFunc_) {
|
||||
glDepthFunc(convertDepthFunc(depthFunc_));
|
||||
cachedDepthFunc_ = depthFunc_;
|
||||
}
|
||||
// 深度函数
|
||||
if (depthFunc_ != cachedDepthFunc_) {
|
||||
glDepthFunc(convertDepthFunc(depthFunc_));
|
||||
cachedDepthFunc_ = depthFunc_;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::applyCullState() {
|
||||
// 检查裁剪模式是否改变
|
||||
if (cullMode_ != cachedCullMode_) {
|
||||
if (cullMode_ == CullMode::None) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(convertCullMode(cullMode_));
|
||||
}
|
||||
cachedCullMode_ = cullMode_;
|
||||
// 检查裁剪模式是否改变
|
||||
if (cullMode_ != cachedCullMode_) {
|
||||
if (cullMode_ == CullMode::None) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(convertCullMode(cullMode_));
|
||||
}
|
||||
cachedCullMode_ = cullMode_;
|
||||
}
|
||||
}
|
||||
|
||||
void GLPipeline::getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor) {
|
||||
switch (mode) {
|
||||
case BlendMode::None:
|
||||
srcFactor = GL_ONE;
|
||||
dstFactor = GL_ZERO;
|
||||
break;
|
||||
case BlendMode::Alpha:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
case BlendMode::Additive:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE;
|
||||
break;
|
||||
case BlendMode::Multiply:
|
||||
srcFactor = GL_DST_COLOR;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
default:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
void GLPipeline::getBlendFactors(BlendMode mode, GLenum &srcFactor,
|
||||
GLenum &dstFactor) {
|
||||
switch (mode) {
|
||||
case BlendMode::None:
|
||||
srcFactor = GL_ONE;
|
||||
dstFactor = GL_ZERO;
|
||||
break;
|
||||
case BlendMode::Alpha:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
case BlendMode::Additive:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE;
|
||||
break;
|
||||
case BlendMode::Multiply:
|
||||
srcFactor = GL_DST_COLOR;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
default:
|
||||
srcFactor = GL_SRC_ALPHA;
|
||||
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum GLPipeline::convertDepthFunc(DepthFunc func) {
|
||||
switch (func) {
|
||||
case DepthFunc::Never: return GL_NEVER;
|
||||
case DepthFunc::Less: return GL_LESS;
|
||||
case DepthFunc::Equal: return GL_EQUAL;
|
||||
case DepthFunc::LessEqual: return GL_LEQUAL;
|
||||
case DepthFunc::Greater: return GL_GREATER;
|
||||
case DepthFunc::NotEqual: return GL_NOTEQUAL;
|
||||
case DepthFunc::GreaterEqual: return GL_GEQUAL;
|
||||
case DepthFunc::Always: return GL_ALWAYS;
|
||||
default: return GL_LESS;
|
||||
}
|
||||
switch (func) {
|
||||
case DepthFunc::Never:
|
||||
return GL_NEVER;
|
||||
case DepthFunc::Less:
|
||||
return GL_LESS;
|
||||
case DepthFunc::Equal:
|
||||
return GL_EQUAL;
|
||||
case DepthFunc::LessEqual:
|
||||
return GL_LEQUAL;
|
||||
case DepthFunc::Greater:
|
||||
return GL_GREATER;
|
||||
case DepthFunc::NotEqual:
|
||||
return GL_NOTEQUAL;
|
||||
case DepthFunc::GreaterEqual:
|
||||
return GL_GEQUAL;
|
||||
case DepthFunc::Always:
|
||||
return GL_ALWAYS;
|
||||
default:
|
||||
return GL_LESS;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum GLPipeline::convertCullMode(CullMode mode) {
|
||||
switch (mode) {
|
||||
case CullMode::Front: return GL_FRONT;
|
||||
case CullMode::Back: return GL_BACK;
|
||||
case CullMode::Both: return GL_FRONT_AND_BACK;
|
||||
default: return GL_BACK;
|
||||
}
|
||||
switch (mode) {
|
||||
case CullMode::Front:
|
||||
return GL_FRONT;
|
||||
case CullMode::Back:
|
||||
return GL_BACK;
|
||||
case CullMode::Both:
|
||||
return GL_FRONT_AND_BACK;
|
||||
default:
|
||||
return GL_BACK;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_context.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -23,8 +23,8 @@ static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float);
|
|||
* @brief 构造函数,初始化OpenGL渲染器成员变量
|
||||
*/
|
||||
GLRenderer::GLRenderer()
|
||||
: window_(nullptr), shapeVao_(0), lineVao_(0),
|
||||
vsync_(true), shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
|
||||
: window_(nullptr), shapeVao_(0), lineVao_(0), vsync_(true),
|
||||
shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
|
||||
lineVertexCount_(0), currentLineWidth_(1.0f) {
|
||||
resetStats();
|
||||
for (auto &v : shapeVertexCache_) {
|
||||
|
|
@ -48,7 +48,8 @@ GLRenderer::~GLRenderer() { shutdown(); }
|
|||
bool GLRenderer::init(IWindow *window) {
|
||||
window_ = window;
|
||||
|
||||
// 初始化 OpenGL 上下文(Switch 平台已通过 SDL2 + EGL 初始化,GLContext 会处理兼容性)
|
||||
// 初始化 OpenGL 上下文(Switch 平台已通过 SDL2 + EGL 初始化,GLContext
|
||||
// 会处理兼容性)
|
||||
if (!GLContext::get().init()) {
|
||||
E2D_LOG_ERROR("Failed to initialize OpenGL context");
|
||||
return false;
|
||||
|
|
@ -937,7 +938,7 @@ void GLRenderer::flushLineBatch() {
|
|||
* @param desc 帧缓冲描述
|
||||
* @return 创建的帧缓冲智能指针
|
||||
*/
|
||||
Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc& desc) {
|
||||
Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc &desc) {
|
||||
auto framebuffer = makePtr<GLFramebuffer>();
|
||||
if (!framebuffer->init(desc)) {
|
||||
E2D_LOG_ERROR("Failed to create framebuffer");
|
||||
|
|
@ -950,7 +951,7 @@ Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc& desc) {
|
|||
* @brief 绑定帧缓冲(作为渲染目标)
|
||||
* @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲
|
||||
*/
|
||||
void GLRenderer::bindFramebuffer(GLFramebuffer* framebuffer) {
|
||||
void GLRenderer::bindFramebuffer(GLFramebuffer *framebuffer) {
|
||||
// 先刷新所有待处理的渲染批次
|
||||
flush();
|
||||
flushShapeBatch();
|
||||
|
|
@ -972,9 +973,7 @@ void GLRenderer::bindFramebuffer(GLFramebuffer* framebuffer) {
|
|||
/**
|
||||
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
|
||||
*/
|
||||
void GLRenderer::unbindFramebuffer() {
|
||||
bindFramebuffer(nullptr);
|
||||
}
|
||||
void GLRenderer::unbindFramebuffer() { bindFramebuffer(nullptr); }
|
||||
|
||||
/**
|
||||
* @brief 获取默认帧缓冲
|
||||
|
|
@ -996,7 +995,7 @@ Ptr<GLFramebuffer> GLRenderer::getDefaultFramebuffer() const {
|
|||
* @param clearDepth 是否清除深度缓冲
|
||||
* @param clearStencil 是否清除模板缓冲
|
||||
*/
|
||||
void GLRenderer::clearFramebuffer(const Color& color, bool clearColor,
|
||||
void GLRenderer::clearFramebuffer(const Color &color, bool clearColor,
|
||||
bool clearDepth, bool clearStencil) {
|
||||
GLbitfield mask = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,45 +1,42 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_shader.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化着色器程序ID为0
|
||||
*/
|
||||
GLShader::GLShader() : programID_(0) {
|
||||
}
|
||||
GLShader::GLShader() : programID_(0) {}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,删除OpenGL着色器程序
|
||||
*/
|
||||
GLShader::~GLShader() {
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
}
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定Shader程序
|
||||
*/
|
||||
void GLShader::bind() const {
|
||||
glUseProgram(programID_);
|
||||
}
|
||||
void GLShader::bind() const { glUseProgram(programID_); }
|
||||
|
||||
/**
|
||||
* @brief 解绑Shader程序
|
||||
*/
|
||||
void GLShader::unbind() const {
|
||||
glUseProgram(0);
|
||||
}
|
||||
void GLShader::unbind() const { glUseProgram(0); }
|
||||
|
||||
/**
|
||||
* @brief 设置布尔类型uniform变量
|
||||
* @param name uniform变量名
|
||||
* @param value 布尔值
|
||||
*/
|
||||
void GLShader::setBool(const std::string& name, bool value) {
|
||||
glUniform1i(getUniformLocation(name), value ? 1 : 0);
|
||||
void GLShader::setBool(const std::string &name, bool value) {
|
||||
glUniform1i(getUniformLocation(name), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -47,8 +44,8 @@ void GLShader::setBool(const std::string& name, bool value) {
|
|||
* @param name uniform变量名
|
||||
* @param value 整数值
|
||||
*/
|
||||
void GLShader::setInt(const std::string& name, int value) {
|
||||
glUniform1i(getUniformLocation(name), value);
|
||||
void GLShader::setInt(const std::string &name, int value) {
|
||||
glUniform1i(getUniformLocation(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,8 +53,8 @@ void GLShader::setInt(const std::string& name, int value) {
|
|||
* @param name uniform变量名
|
||||
* @param value 浮点值
|
||||
*/
|
||||
void GLShader::setFloat(const std::string& name, float value) {
|
||||
glUniform1f(getUniformLocation(name), value);
|
||||
void GLShader::setFloat(const std::string &name, float value) {
|
||||
glUniform1f(getUniformLocation(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -65,8 +62,8 @@ void GLShader::setFloat(const std::string& name, float value) {
|
|||
* @param name uniform变量名
|
||||
* @param value 二维向量值
|
||||
*/
|
||||
void GLShader::setVec2(const std::string& name, const glm::vec2& value) {
|
||||
glUniform2fv(getUniformLocation(name), 1, &value[0]);
|
||||
void GLShader::setVec2(const std::string &name, const glm::vec2 &value) {
|
||||
glUniform2fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -74,8 +71,8 @@ void GLShader::setVec2(const std::string& name, const glm::vec2& value) {
|
|||
* @param name uniform变量名
|
||||
* @param value 三维向量值
|
||||
*/
|
||||
void GLShader::setVec3(const std::string& name, const glm::vec3& value) {
|
||||
glUniform3fv(getUniformLocation(name), 1, &value[0]);
|
||||
void GLShader::setVec3(const std::string &name, const glm::vec3 &value) {
|
||||
glUniform3fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -83,8 +80,8 @@ void GLShader::setVec3(const std::string& name, const glm::vec3& value) {
|
|||
* @param name uniform变量名
|
||||
* @param value 四维向量值
|
||||
*/
|
||||
void GLShader::setVec4(const std::string& name, const glm::vec4& value) {
|
||||
glUniform4fv(getUniformLocation(name), 1, &value[0]);
|
||||
void GLShader::setVec4(const std::string &name, const glm::vec4 &value) {
|
||||
glUniform4fv(getUniformLocation(name), 1, &value[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -92,8 +89,8 @@ void GLShader::setVec4(const std::string& name, const glm::vec4& value) {
|
|||
* @param name uniform变量名
|
||||
* @param value 4x4矩阵值
|
||||
*/
|
||||
void GLShader::setMat4(const std::string& name, const glm::mat4& value) {
|
||||
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]);
|
||||
void GLShader::setMat4(const std::string &name, const glm::mat4 &value) {
|
||||
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -101,8 +98,8 @@ void GLShader::setMat4(const std::string& name, const glm::mat4& value) {
|
|||
* @param name uniform变量名
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void GLShader::setColor(const std::string& name, const Color& color) {
|
||||
glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a);
|
||||
void GLShader::setColor(const std::string &name, const Color &color) {
|
||||
glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -111,42 +108,43 @@ void GLShader::setColor(const std::string& name, const Color& color) {
|
|||
* @param fragmentSource 片段着色器源码
|
||||
* @return 编译成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::compileFromSource(const char* vertexSource, const char* fragmentSource) {
|
||||
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
|
||||
if (vertexShader == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
|
||||
if (fragmentShader == 0) {
|
||||
glDeleteShader(vertexShader);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
uniformCache_.clear();
|
||||
}
|
||||
|
||||
programID_ = glCreateProgram();
|
||||
glAttachShader(programID_, vertexShader);
|
||||
glAttachShader(programID_, fragmentShader);
|
||||
glLinkProgram(programID_);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("Shader program linking failed: {}", infoLog);
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
}
|
||||
bool GLShader::compileFromSource(const char *vertexSource,
|
||||
const char *fragmentSource) {
|
||||
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
|
||||
if (vertexShader == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
|
||||
if (fragmentShader == 0) {
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
return false;
|
||||
}
|
||||
|
||||
return success == GL_TRUE;
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
uniformCache_.clear();
|
||||
}
|
||||
|
||||
programID_ = glCreateProgram();
|
||||
glAttachShader(programID_, vertexShader);
|
||||
glAttachShader(programID_, fragmentShader);
|
||||
glLinkProgram(programID_);
|
||||
|
||||
GLint success;
|
||||
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("Shader program linking failed: {}", infoLog);
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
}
|
||||
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
|
||||
return success == GL_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -154,43 +152,45 @@ bool GLShader::compileFromSource(const char* vertexSource, const char* fragmentS
|
|||
* @param binary 二进制数据
|
||||
* @return 创建成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::compileFromBinary(const std::vector<uint8_t>& binary) {
|
||||
if (binary.empty()) {
|
||||
E2D_LOG_ERROR("Binary data is empty");
|
||||
return false;
|
||||
}
|
||||
bool GLShader::compileFromBinary(const std::vector<uint8_t> &binary) {
|
||||
if (binary.empty()) {
|
||||
E2D_LOG_ERROR("Binary data is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint numFormats = 0;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats);
|
||||
if (numFormats == 0) {
|
||||
E2D_LOG_ERROR("Program binary formats not supported");
|
||||
return false;
|
||||
}
|
||||
GLint numFormats = 0;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats);
|
||||
if (numFormats == 0) {
|
||||
E2D_LOG_ERROR("Program binary formats not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
uniformCache_.clear();
|
||||
}
|
||||
if (programID_ != 0) {
|
||||
glDeleteProgram(programID_);
|
||||
uniformCache_.clear();
|
||||
}
|
||||
|
||||
programID_ = glCreateProgram();
|
||||
programID_ = glCreateProgram();
|
||||
|
||||
GLenum binaryFormat = 0;
|
||||
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, reinterpret_cast<GLint*>(&binaryFormat));
|
||||
GLenum binaryFormat = 0;
|
||||
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS,
|
||||
reinterpret_cast<GLint *>(&binaryFormat));
|
||||
|
||||
glProgramBinary(programID_, binaryFormat, binary.data(), static_cast<GLsizei>(binary.size()));
|
||||
glProgramBinary(programID_, binaryFormat, binary.data(),
|
||||
static_cast<GLsizei>(binary.size()));
|
||||
|
||||
GLint success = 0;
|
||||
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("Failed to load shader from binary: {}", infoLog);
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
return false;
|
||||
}
|
||||
GLint success = 0;
|
||||
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("Failed to load shader from binary: {}", infoLog);
|
||||
glDeleteProgram(programID_);
|
||||
programID_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -198,47 +198,49 @@ bool GLShader::compileFromBinary(const std::vector<uint8_t>& binary) {
|
|||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShader::getBinary(std::vector<uint8_t>& outBinary) {
|
||||
if (programID_ == 0) {
|
||||
E2D_LOG_WARN("Cannot get binary: shader program is 0");
|
||||
return false;
|
||||
}
|
||||
bool GLShader::getBinary(std::vector<uint8_t> &outBinary) {
|
||||
if (programID_ == 0) {
|
||||
E2D_LOG_WARN("Cannot get binary: shader program is 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint binaryLength = 0;
|
||||
glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
||||
|
||||
E2D_LOG_DEBUG("Shader binary length: {}", binaryLength);
|
||||
|
||||
if (binaryLength <= 0) {
|
||||
E2D_LOG_WARN("Shader binary length is 0 or negative");
|
||||
return false;
|
||||
}
|
||||
GLint binaryLength = 0;
|
||||
glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
||||
|
||||
outBinary.resize(binaryLength);
|
||||
E2D_LOG_DEBUG("Shader binary length: {}", binaryLength);
|
||||
|
||||
GLenum binaryFormat = 0;
|
||||
GLsizei actualLength = 0;
|
||||
glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat, outBinary.data());
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
E2D_LOG_ERROR("glGetProgramBinary failed with error: {}", err);
|
||||
outBinary.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualLength == 0) {
|
||||
E2D_LOG_WARN("glGetProgramBinary returned 0 bytes");
|
||||
outBinary.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualLength != binaryLength) {
|
||||
outBinary.resize(actualLength);
|
||||
}
|
||||
if (binaryLength <= 0) {
|
||||
E2D_LOG_WARN("Shader binary length is 0 or negative");
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("Shader binary retrieved: {} bytes, format: {}", actualLength, binaryFormat);
|
||||
return true;
|
||||
outBinary.resize(binaryLength);
|
||||
|
||||
GLenum binaryFormat = 0;
|
||||
GLsizei actualLength = 0;
|
||||
glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat,
|
||||
outBinary.data());
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
E2D_LOG_ERROR("glGetProgramBinary failed with error: {}", err);
|
||||
outBinary.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualLength == 0) {
|
||||
E2D_LOG_WARN("glGetProgramBinary returned 0 bytes");
|
||||
outBinary.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualLength != binaryLength) {
|
||||
outBinary.resize(actualLength);
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("Shader binary retrieved: {} bytes, format: {}", actualLength,
|
||||
binaryFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -247,22 +249,22 @@ bool GLShader::getBinary(std::vector<uint8_t>& outBinary) {
|
|||
* @param source 着色器源码
|
||||
* @return 着色器ID,失败返回0
|
||||
*/
|
||||
GLuint GLShader::compileShader(GLenum type, const char* source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
GLuint GLShader::compileShader(GLenum type, const char *source) {
|
||||
GLuint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 1, &source, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("Shader compilation failed: {}", infoLog);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
GLint success;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
char infoLog[512];
|
||||
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
|
||||
E2D_LOG_ERROR("Shader compilation failed: {}", infoLog);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -270,15 +272,15 @@ GLuint GLShader::compileShader(GLenum type, const char* source) {
|
|||
* @param name uniform变量名
|
||||
* @return uniform位置
|
||||
*/
|
||||
GLint GLShader::getUniformLocation(const std::string& name) {
|
||||
auto it = uniformCache_.find(name);
|
||||
if (it != uniformCache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
GLint GLShader::getUniformLocation(const std::string &name) {
|
||||
auto it = uniformCache_.find(name);
|
||||
if (it != uniformCache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
GLint location = glGetUniformLocation(programID_, name.c_str());
|
||||
uniformCache_[name] = location;
|
||||
return location;
|
||||
GLint location = glGetUniformLocation(programID_, name.c_str());
|
||||
uniformCache_[name] = location;
|
||||
return location;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -292,20 +294,19 @@ GLint GLShader::getUniformLocation(const std::string& name) {
|
|||
* @param fragSource 片段着色器源码
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader> GLShaderFactory::createFromSource(
|
||||
const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) {
|
||||
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
shader->setName(name);
|
||||
|
||||
if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) {
|
||||
E2D_LOG_ERROR("Failed to compile shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<IShader> GLShaderFactory::createFromSource(const std::string &name,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
|
||||
return shader;
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
shader->setName(name);
|
||||
|
||||
if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) {
|
||||
E2D_LOG_ERROR("Failed to compile shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -314,19 +315,19 @@ Ptr<IShader> GLShaderFactory::createFromSource(
|
|||
* @param binary 编译后的二进制数据
|
||||
* @return 创建的Shader实例
|
||||
*/
|
||||
Ptr<IShader> GLShaderFactory::createFromBinary(
|
||||
const std::string& name,
|
||||
const std::vector<uint8_t>& binary) {
|
||||
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
shader->setName(name);
|
||||
|
||||
if (!shader->compileFromBinary(binary)) {
|
||||
E2D_LOG_ERROR("Failed to create shader from binary: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<IShader>
|
||||
GLShaderFactory::createFromBinary(const std::string &name,
|
||||
const std::vector<uint8_t> &binary) {
|
||||
|
||||
return shader;
|
||||
auto shader = std::make_shared<GLShader>();
|
||||
shader->setName(name);
|
||||
|
||||
if (!shader->compileFromBinary(binary)) {
|
||||
E2D_LOG_ERROR("Failed to create shader from binary: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -335,14 +336,15 @@ Ptr<IShader> GLShaderFactory::createFromBinary(
|
|||
* @param outBinary 输出的二进制数据
|
||||
* @return 成功返回true,失败返回false
|
||||
*/
|
||||
bool GLShaderFactory::getShaderBinary(const IShader& shader, std::vector<uint8_t>& outBinary) {
|
||||
const GLShader* glShader = dynamic_cast<const GLShader*>(&shader);
|
||||
if (!glShader) {
|
||||
E2D_LOG_ERROR("Shader is not a GLShader instance");
|
||||
return false;
|
||||
}
|
||||
bool GLShaderFactory::getShaderBinary(const IShader &shader,
|
||||
std::vector<uint8_t> &outBinary) {
|
||||
const GLShader *glShader = dynamic_cast<const GLShader *>(&shader);
|
||||
if (!glShader) {
|
||||
E2D_LOG_ERROR("Shader is not a GLShader instance");
|
||||
return false;
|
||||
}
|
||||
|
||||
return const_cast<GLShader*>(glShader)->getBinary(outBinary);
|
||||
return const_cast<GLShader *>(glShader)->getBinary(outBinary);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_sprite_batch.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -36,16 +38,19 @@ bool GLSpriteBatch::init() {
|
|||
|
||||
// 设置顶点属性
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, position)));
|
||||
glVertexAttribPointer(
|
||||
0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, position)));
|
||||
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, texCoord)));
|
||||
glVertexAttribPointer(
|
||||
1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, texCoord)));
|
||||
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, color)));
|
||||
glVertexAttribPointer(
|
||||
2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
|
||||
reinterpret_cast<void *>(offsetof(SpriteVertex, color)));
|
||||
|
||||
// 初始化 EBO(索引缓冲区)- 静态使用模式
|
||||
BufferDesc eboDesc;
|
||||
|
|
@ -172,12 +177,12 @@ void GLSpriteBatch::submitBatch() {
|
|||
// 通过传入 nullptr 进行 orphaning,告诉驱动器可以丢弃旧缓冲区并分配新内存
|
||||
// 这样可以避免 GPU 等待,提高性能
|
||||
size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex);
|
||||
vbo_.setData(nullptr, vertexDataSize); // orphaning
|
||||
vbo_.setData(nullptr, vertexDataSize); // orphaning
|
||||
vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize);
|
||||
|
||||
// 绘制
|
||||
currentTexture_->bind(0);
|
||||
|
||||
|
||||
size_t indexCount = batch_.getSpriteCount() * 6;
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indexCount),
|
||||
GL_UNSIGNED_SHORT, nullptr);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <cstring>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <fstream>
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,150 +1,150 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/vulkan/vk_renderer.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
VulkanRenderer::VulkanRenderer() = default;
|
||||
|
||||
VulkanRenderer::~VulkanRenderer() {
|
||||
shutdown();
|
||||
VulkanRenderer::~VulkanRenderer() { shutdown(); }
|
||||
|
||||
bool VulkanRenderer::init(IWindow *window) {
|
||||
E2D_LOG_WARN("Vulkan renderer is not fully implemented yet");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::init(IWindow* window) {
|
||||
E2D_LOG_WARN("Vulkan renderer is not fully implemented yet");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanRenderer::shutdown() {
|
||||
initialized_ = false;
|
||||
}
|
||||
void VulkanRenderer::shutdown() { initialized_ = false; }
|
||||
|
||||
void VulkanRenderer::beginFrame(const Color &clearColor) {
|
||||
// TODO: 实现Vulkan帧开始
|
||||
// TODO: 实现Vulkan帧开始
|
||||
}
|
||||
|
||||
void VulkanRenderer::endFrame() {
|
||||
// TODO: 实现Vulkan帧结束
|
||||
// TODO: 实现Vulkan帧结束
|
||||
}
|
||||
|
||||
void VulkanRenderer::setViewport(int x, int y, int width, int height) {
|
||||
// TODO: 实现视口设置
|
||||
// TODO: 实现视口设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::setVSync(bool enabled) {
|
||||
// TODO: 实现垂直同步设置
|
||||
// TODO: 实现垂直同步设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::setBlendMode(BlendMode mode) {
|
||||
// TODO: 实现混合模式设置
|
||||
// TODO: 实现混合模式设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::setViewProjection(const glm::mat4 &matrix) {
|
||||
// TODO: 实现视图投影矩阵设置
|
||||
// TODO: 实现视图投影矩阵设置
|
||||
}
|
||||
|
||||
void VulkanRenderer::pushTransform(const glm::mat4 &transform) {
|
||||
// TODO: 实现变换矩阵入栈
|
||||
// TODO: 实现变换矩阵入栈
|
||||
}
|
||||
|
||||
void VulkanRenderer::popTransform() {
|
||||
// TODO: 实现变换矩阵出栈
|
||||
// TODO: 实现变换矩阵出栈
|
||||
}
|
||||
|
||||
glm::mat4 VulkanRenderer::getCurrentTransform() const {
|
||||
return glm::mat4(1.0f);
|
||||
return glm::mat4(1.0f);
|
||||
}
|
||||
|
||||
Ptr<Texture> VulkanRenderer::createTexture(int width, int height, const uint8_t *pixels, int channels) {
|
||||
// TODO: 实现Vulkan纹理创建
|
||||
return nullptr;
|
||||
Ptr<Texture> VulkanRenderer::createTexture(int width, int height,
|
||||
const uint8_t *pixels,
|
||||
int channels) {
|
||||
// TODO: 实现Vulkan纹理创建
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<Texture> VulkanRenderer::loadTexture(const std::string &filepath) {
|
||||
// TODO: 实现Vulkan纹理加载
|
||||
return nullptr;
|
||||
// TODO: 实现Vulkan纹理加载
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VulkanRenderer::beginSpriteBatch() {
|
||||
// TODO: 实现精灵批处理开始
|
||||
// TODO: 实现精灵批处理开始
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint, float rotation,
|
||||
const Vec2 &anchor) {
|
||||
// TODO: 实现精灵绘制
|
||||
const Rect &srcRect, const Color &tint,
|
||||
float rotation, const Vec2 &anchor) {
|
||||
// TODO: 实现精灵绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) {
|
||||
// TODO: 实现简化精灵绘制
|
||||
const Color &tint) {
|
||||
// TODO: 实现简化精灵绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::endSpriteBatch() {
|
||||
// TODO: 实现精灵批处理结束
|
||||
// TODO: 实现精灵批处理结束
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width) {
|
||||
// TODO: 实现线条绘制
|
||||
void VulkanRenderer::drawLine(const Vec2 &start, const Vec2 &end,
|
||||
const Color &color, float width) {
|
||||
// TODO: 实现线条绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawRect(const Rect &rect, const Color &color, float width) {
|
||||
// TODO: 实现矩形边框绘制
|
||||
void VulkanRenderer::drawRect(const Rect &rect, const Color &color,
|
||||
float width) {
|
||||
// TODO: 实现矩形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillRect(const Rect &rect, const Color &color) {
|
||||
// TODO: 实现矩形填充
|
||||
// TODO: 实现矩形填充
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments, float width) {
|
||||
// TODO: 实现圆形边框绘制
|
||||
void VulkanRenderer::drawCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments, float width) {
|
||||
// TODO: 实现圆形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments) {
|
||||
// TODO: 实现圆形填充
|
||||
void VulkanRenderer::fillCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments) {
|
||||
// TODO: 实现圆形填充
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width) {
|
||||
// TODO: 实现三角形边框绘制
|
||||
void VulkanRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3, const Color &color,
|
||||
float width) {
|
||||
// TODO: 实现三角形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) {
|
||||
// TODO: 实现三角形填充
|
||||
void VulkanRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3, const Color &color) {
|
||||
// TODO: 实现三角形填充
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawPolygon(const std::vector<Vec2> &points, const Color &color,
|
||||
float width) {
|
||||
// TODO: 实现多边形边框绘制
|
||||
void VulkanRenderer::drawPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color, float width) {
|
||||
// TODO: 实现多边形边框绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) {
|
||||
// TODO: 实现多边形填充
|
||||
const Color &color) {
|
||||
// TODO: 实现多边形填充
|
||||
}
|
||||
|
||||
Ptr<FontAtlas> VulkanRenderer::createFontAtlas(const std::string &filepath, int fontSize,
|
||||
bool useSDF) {
|
||||
// TODO: 实现字体图集创建
|
||||
return nullptr;
|
||||
Ptr<FontAtlas> VulkanRenderer::createFontAtlas(const std::string &filepath,
|
||||
int fontSize, bool useSDF) {
|
||||
// TODO: 实现字体图集创建
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) {
|
||||
// TODO: 实现文本绘制
|
||||
const Vec2 &position, const Color &color) {
|
||||
// TODO: 实现文本绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text, float x,
|
||||
float y, const Color &color) {
|
||||
// TODO: 实现文本绘制
|
||||
void VulkanRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||
float x, float y, const Color &color) {
|
||||
// TODO: 实现文本绘制
|
||||
}
|
||||
|
||||
void VulkanRenderer::resetStats() {
|
||||
stats_ = Stats{};
|
||||
}
|
||||
void VulkanRenderer::resetStats() { stats_ = Stats{}; }
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/batch/sprite_batch.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
|
@ -10,196 +12,195 @@ namespace extra2d {
|
|||
// TrigLookup 实现 - 三角函数查表
|
||||
// ============================================================================
|
||||
TrigLookup::TrigLookup() {
|
||||
constexpr float PI = 3.14159265359f;
|
||||
constexpr float DEG2RAD = PI / 180.0f;
|
||||
|
||||
for (int i = 0; i < TABLE_SIZE; ++i) {
|
||||
float angle = static_cast<float>(i) * (360.0f / TABLE_SIZE) * DEG2RAD;
|
||||
sinTable_[i] = std::sin(angle);
|
||||
cosTable_[i] = std::cos(angle);
|
||||
}
|
||||
constexpr float PI = 3.14159265359f;
|
||||
constexpr float DEG2RAD = PI / 180.0f;
|
||||
|
||||
for (int i = 0; i < TABLE_SIZE; ++i) {
|
||||
float angle = static_cast<float>(i) * (360.0f / TABLE_SIZE) * DEG2RAD;
|
||||
sinTable_[i] = std::sin(angle);
|
||||
cosTable_[i] = std::cos(angle);
|
||||
}
|
||||
}
|
||||
|
||||
float TrigLookup::sin(int angle) const {
|
||||
// 规范化角度到 0-360
|
||||
angle = ((angle % 360) + 360) % 360;
|
||||
return sinTable_[angle * 4];
|
||||
// 规范化角度到 0-360
|
||||
angle = ((angle % 360) + 360) % 360;
|
||||
return sinTable_[angle * 4];
|
||||
}
|
||||
|
||||
float TrigLookup::cos(int angle) const {
|
||||
// 规范化角度到 0-360
|
||||
angle = ((angle % 360) + 360) % 360;
|
||||
return cosTable_[angle * 4];
|
||||
// 规范化角度到 0-360
|
||||
angle = ((angle % 360) + 360) % 360;
|
||||
return cosTable_[angle * 4];
|
||||
}
|
||||
|
||||
float TrigLookup::sinRad(float rad) const {
|
||||
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
|
||||
int angle = static_cast<int>(rad * RAD2DEG);
|
||||
return sin(angle);
|
||||
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
|
||||
int angle = static_cast<int>(rad * RAD2DEG);
|
||||
return sin(angle);
|
||||
}
|
||||
|
||||
float TrigLookup::cosRad(float rad) const {
|
||||
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
|
||||
int angle = static_cast<int>(rad * RAD2DEG);
|
||||
return cos(angle);
|
||||
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
|
||||
int angle = static_cast<int>(rad * RAD2DEG);
|
||||
return cos(angle);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SpriteBatch 实现
|
||||
// ============================================================================
|
||||
SpriteBatch::SpriteBatch()
|
||||
: spriteCount_(0)
|
||||
, vpDirty_(true) {
|
||||
// 预分配顶点缓冲区
|
||||
vertices_.reserve(MAX_VERTICES);
|
||||
indices_.reserve(MAX_INDICES);
|
||||
|
||||
// 生成静态索引缓冲区
|
||||
generateIndices();
|
||||
SpriteBatch::SpriteBatch() : spriteCount_(0), vpDirty_(true) {
|
||||
// 预分配顶点缓冲区
|
||||
vertices_.reserve(MAX_VERTICES);
|
||||
indices_.reserve(MAX_INDICES);
|
||||
|
||||
// 生成静态索引缓冲区
|
||||
generateIndices();
|
||||
}
|
||||
|
||||
void SpriteBatch::generateIndices() {
|
||||
indices_.clear();
|
||||
for (size_t i = 0; i < MAX_SPRITES; ++i) {
|
||||
uint16_t base = static_cast<uint16_t>(i * 4);
|
||||
// 两个三角形: (0,1,2) 和 (0,2,3)
|
||||
indices_.push_back(base + 0);
|
||||
indices_.push_back(base + 1);
|
||||
indices_.push_back(base + 2);
|
||||
indices_.push_back(base + 0);
|
||||
indices_.push_back(base + 2);
|
||||
indices_.push_back(base + 3);
|
||||
}
|
||||
indices_.clear();
|
||||
for (size_t i = 0; i < MAX_SPRITES; ++i) {
|
||||
uint16_t base = static_cast<uint16_t>(i * 4);
|
||||
// 两个三角形: (0,1,2) 和 (0,2,3)
|
||||
indices_.push_back(base + 0);
|
||||
indices_.push_back(base + 1);
|
||||
indices_.push_back(base + 2);
|
||||
indices_.push_back(base + 0);
|
||||
indices_.push_back(base + 2);
|
||||
indices_.push_back(base + 3);
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::begin(const glm::mat4& viewProjection) {
|
||||
viewProjection_ = viewProjection;
|
||||
vpDirty_ = true;
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
void SpriteBatch::begin(const glm::mat4 &viewProjection) {
|
||||
viewProjection_ = viewProjection;
|
||||
vpDirty_ = true;
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
}
|
||||
|
||||
void SpriteBatch::end() {
|
||||
// 批次结束,数据已准备好供后端使用
|
||||
// 批次结束,数据已准备好供后端使用
|
||||
}
|
||||
|
||||
void SpriteBatch::draw(const SpriteData& sprite) {
|
||||
if (spriteCount_ >= MAX_SPRITES) {
|
||||
// 缓冲区已满,需要刷新
|
||||
flush();
|
||||
void SpriteBatch::draw(const SpriteData &sprite) {
|
||||
if (spriteCount_ >= MAX_SPRITES) {
|
||||
// 缓冲区已满,需要刷新
|
||||
flush();
|
||||
}
|
||||
|
||||
generateVertices(sprite, spriteCount_ * VERTICES_PER_SPRITE);
|
||||
spriteCount_++;
|
||||
}
|
||||
|
||||
void SpriteBatch::drawBatch(const std::vector<SpriteData> &sprites) {
|
||||
size_t index = 0;
|
||||
while (index < sprites.size()) {
|
||||
// 计算剩余空间
|
||||
size_t remainingSpace = MAX_SPRITES - spriteCount_;
|
||||
size_t batchSize = std::min(remainingSpace, sprites.size() - index);
|
||||
|
||||
// 批量生成顶点
|
||||
for (size_t i = 0; i < batchSize; ++i) {
|
||||
generateVertices(sprites[index + i], spriteCount_ * VERTICES_PER_SPRITE);
|
||||
spriteCount_++;
|
||||
}
|
||||
|
||||
generateVertices(sprite, spriteCount_ * VERTICES_PER_SPRITE);
|
||||
spriteCount_++;
|
||||
}
|
||||
|
||||
void SpriteBatch::drawBatch(const std::vector<SpriteData>& sprites) {
|
||||
size_t index = 0;
|
||||
while (index < sprites.size()) {
|
||||
// 计算剩余空间
|
||||
size_t remainingSpace = MAX_SPRITES - spriteCount_;
|
||||
size_t batchSize = std::min(remainingSpace, sprites.size() - index);
|
||||
|
||||
// 批量生成顶点
|
||||
for (size_t i = 0; i < batchSize; ++i) {
|
||||
generateVertices(sprites[index + i], spriteCount_ * VERTICES_PER_SPRITE);
|
||||
spriteCount_++;
|
||||
}
|
||||
|
||||
index += batchSize;
|
||||
|
||||
// 如果缓冲区已满,需要刷新(由后端处理)
|
||||
if (spriteCount_ >= MAX_SPRITES && index < sprites.size()) {
|
||||
// 通知后端刷新,然后继续
|
||||
// 注意:这里只是准备数据,实际 GPU 提交由后端决定
|
||||
break;
|
||||
}
|
||||
index += batchSize;
|
||||
|
||||
// 如果缓冲区已满,需要刷新(由后端处理)
|
||||
if (spriteCount_ >= MAX_SPRITES && index < sprites.size()) {
|
||||
// 通知后端刷新,然后继续
|
||||
// 注意:这里只是准备数据,实际 GPU 提交由后端决定
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::drawImmediate(const SpriteData& sprite) {
|
||||
// 立即绘制模式:清空当前批次,只绘制这一个精灵
|
||||
clear();
|
||||
draw(sprite);
|
||||
void SpriteBatch::drawImmediate(const SpriteData &sprite) {
|
||||
// 立即绘制模式:清空当前批次,只绘制这一个精灵
|
||||
clear();
|
||||
draw(sprite);
|
||||
}
|
||||
|
||||
void SpriteBatch::generateVertices(const SpriteData& sprite, size_t vertexOffset) {
|
||||
// 确保顶点缓冲区足够
|
||||
if (vertices_.size() < vertexOffset + VERTICES_PER_SPRITE) {
|
||||
vertices_.resize(vertexOffset + VERTICES_PER_SPRITE);
|
||||
}
|
||||
|
||||
// 计算旋转(使用查表)
|
||||
float c = 1.0f;
|
||||
float s = 0.0f;
|
||||
void SpriteBatch::generateVertices(const SpriteData &sprite,
|
||||
size_t vertexOffset) {
|
||||
// 确保顶点缓冲区足够
|
||||
if (vertices_.size() < vertexOffset + VERTICES_PER_SPRITE) {
|
||||
vertices_.resize(vertexOffset + VERTICES_PER_SPRITE);
|
||||
}
|
||||
|
||||
// 计算旋转(使用查表)
|
||||
float c = 1.0f;
|
||||
float s = 0.0f;
|
||||
if (sprite.rotation != 0.0f) {
|
||||
c = trigLookup_.cosRad(sprite.rotation);
|
||||
s = trigLookup_.sinRad(sprite.rotation);
|
||||
}
|
||||
|
||||
// 计算精灵的四个角(相对于中心点)
|
||||
float halfWidth = sprite.size.x * 0.5f;
|
||||
float halfHeight = sprite.size.y * 0.5f;
|
||||
|
||||
// 考虑 pivot 偏移
|
||||
float pivotOffsetX = (sprite.pivot.x - 0.5f) * sprite.size.x;
|
||||
float pivotOffsetY = (sprite.pivot.y - 0.5f) * sprite.size.y;
|
||||
|
||||
// 四个角的本地坐标
|
||||
Vec2 localCorners[4] = {
|
||||
Vec2(-halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 左下
|
||||
Vec2(halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 右下
|
||||
Vec2(halfWidth - pivotOffsetX, halfHeight - pivotOffsetY), // 右上
|
||||
Vec2(-halfWidth - pivotOffsetX, halfHeight - pivotOffsetY) // 左上
|
||||
};
|
||||
|
||||
// 应用旋转和平移
|
||||
Vec2 worldPos = sprite.position;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Vec2 rotated;
|
||||
if (sprite.rotation != 0.0f) {
|
||||
c = trigLookup_.cosRad(sprite.rotation);
|
||||
s = trigLookup_.sinRad(sprite.rotation);
|
||||
}
|
||||
|
||||
// 计算精灵的四个角(相对于中心点)
|
||||
float halfWidth = sprite.size.x * 0.5f;
|
||||
float halfHeight = sprite.size.y * 0.5f;
|
||||
|
||||
// 考虑 pivot 偏移
|
||||
float pivotOffsetX = (sprite.pivot.x - 0.5f) * sprite.size.x;
|
||||
float pivotOffsetY = (sprite.pivot.y - 0.5f) * sprite.size.y;
|
||||
|
||||
// 四个角的本地坐标
|
||||
Vec2 localCorners[4] = {
|
||||
Vec2(-halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 左下
|
||||
Vec2( halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 右下
|
||||
Vec2( halfWidth - pivotOffsetX, halfHeight - pivotOffsetY), // 右上
|
||||
Vec2(-halfWidth - pivotOffsetX, halfHeight - pivotOffsetY) // 左上
|
||||
};
|
||||
|
||||
// 应用旋转和平移
|
||||
Vec2 worldPos = sprite.position;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Vec2 rotated;
|
||||
if (sprite.rotation != 0.0f) {
|
||||
rotated.x = localCorners[i].x * c - localCorners[i].y * s;
|
||||
rotated.y = localCorners[i].x * s + localCorners[i].y * c;
|
||||
} else {
|
||||
rotated = localCorners[i];
|
||||
}
|
||||
|
||||
vertices_[vertexOffset + i].position = worldPos + rotated;
|
||||
}
|
||||
|
||||
// 设置纹理坐标
|
||||
// uvRect.origin = (u0, v0) - 左下
|
||||
// uvRect.size = (width, height) - 从左上到右下的尺寸
|
||||
float u0 = sprite.uvRect.origin.x;
|
||||
float v0 = sprite.uvRect.origin.y;
|
||||
float u1 = u0 + sprite.uvRect.size.width;
|
||||
float v1 = v0 + sprite.uvRect.size.height;
|
||||
|
||||
// 顶点顺序: 左下, 右下, 右上, 左上
|
||||
// 注意: 在 gl_font_atlas 中 v0 > v1 (因为翻转了V坐标)
|
||||
// 所以 v0 对应底部,v1 对应顶部
|
||||
vertices_[vertexOffset + 0].texCoord = Vec2(u0, v0); // 左下
|
||||
vertices_[vertexOffset + 1].texCoord = Vec2(u1, v0); // 右下
|
||||
vertices_[vertexOffset + 2].texCoord = Vec2(u1, v1); // 右上
|
||||
vertices_[vertexOffset + 3].texCoord = Vec2(u0, v1); // 左上
|
||||
|
||||
// 设置颜色
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
vertices_[vertexOffset + i].color = sprite.color;
|
||||
rotated.x = localCorners[i].x * c - localCorners[i].y * s;
|
||||
rotated.y = localCorners[i].x * s + localCorners[i].y * c;
|
||||
} else {
|
||||
rotated = localCorners[i];
|
||||
}
|
||||
|
||||
vertices_[vertexOffset + i].position = worldPos + rotated;
|
||||
}
|
||||
|
||||
// 设置纹理坐标
|
||||
// uvRect.origin = (u0, v0) - 左下
|
||||
// uvRect.size = (width, height) - 从左上到右下的尺寸
|
||||
float u0 = sprite.uvRect.origin.x;
|
||||
float v0 = sprite.uvRect.origin.y;
|
||||
float u1 = u0 + sprite.uvRect.size.width;
|
||||
float v1 = v0 + sprite.uvRect.size.height;
|
||||
|
||||
// 顶点顺序: 左下, 右下, 右上, 左上
|
||||
// 注意: 在 gl_font_atlas 中 v0 > v1 (因为翻转了V坐标)
|
||||
// 所以 v0 对应底部,v1 对应顶部
|
||||
vertices_[vertexOffset + 0].texCoord = Vec2(u0, v0); // 左下
|
||||
vertices_[vertexOffset + 1].texCoord = Vec2(u1, v0); // 右下
|
||||
vertices_[vertexOffset + 2].texCoord = Vec2(u1, v1); // 右上
|
||||
vertices_[vertexOffset + 3].texCoord = Vec2(u0, v1); // 左上
|
||||
|
||||
// 设置颜色
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
vertices_[vertexOffset + i].color = sprite.color;
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteBatch::flush() {
|
||||
// 标记需要刷新 - 实际刷新由后端处理
|
||||
// 这里只是重置计数器,让后端知道需要提交当前批次
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
// 标记需要刷新 - 实际刷新由后端处理
|
||||
// 这里只是重置计数器,让后端知道需要提交当前批次
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
}
|
||||
|
||||
void SpriteBatch::clear() {
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
spriteCount_ = 0;
|
||||
vertices_.clear();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/graphics/backends/backend_factory.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/core/render_module.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <filesystem>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -19,91 +19,97 @@ void initOpenGLBackend();
|
|||
#ifdef E2D_BACKEND_VULKAN
|
||||
void initVulkanBackend();
|
||||
#endif
|
||||
}
|
||||
} // namespace graphics
|
||||
|
||||
RenderModule::RenderModule(std::function<void(RenderCfg&)> configFn) {
|
||||
configFn(cfg_);
|
||||
RenderModule::RenderModule(std::function<void(RenderCfg &)> configFn) {
|
||||
configFn(cfg_);
|
||||
}
|
||||
|
||||
RenderModule::~RenderModule() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getExecutableDir() {
|
||||
try {
|
||||
auto currentPath = std::filesystem::current_path();
|
||||
return currentPath.string() + "/";
|
||||
} catch (...) {
|
||||
return "./";
|
||||
}
|
||||
try {
|
||||
auto currentPath = std::filesystem::current_path();
|
||||
return currentPath.string() + "/";
|
||||
} catch (...) {
|
||||
return "./";
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderModule::init() {
|
||||
if (initialized_) return true;
|
||||
|
||||
auto* winMod = Registry::instance().get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) {
|
||||
E2D_LOG_ERROR("WindowModule not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化图形后端(注册到工厂)
|
||||
if (initialized_)
|
||||
return true;
|
||||
|
||||
auto *winMod = Registry::instance().get<WindowModule>();
|
||||
if (!winMod || !winMod->win()) {
|
||||
E2D_LOG_ERROR("WindowModule not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化图形后端(注册到工厂)
|
||||
#ifdef E2D_BACKEND_OPENGL
|
||||
graphics::initOpenGLBackend();
|
||||
graphics::initOpenGLBackend();
|
||||
#endif
|
||||
#ifdef E2D_BACKEND_VULKAN
|
||||
graphics::initVulkanBackend();
|
||||
graphics::initVulkanBackend();
|
||||
#endif
|
||||
|
||||
if (!ShaderManager::getInstance().isInitialized()) {
|
||||
auto factory = makeShared<GLShaderFactory>();
|
||||
std::string shaderDir = getExecutableDir() + "shaders/";
|
||||
std::string cacheDir = getExecutableDir() + "shader_cache/";
|
||||
if (!ShaderManager::getInstance().init(shaderDir, cacheDir, factory)) {
|
||||
E2D_LOG_WARN("Failed to initialize ShaderManager with dir: {}", shaderDir);
|
||||
}
|
||||
|
||||
if (!ShaderManager::getInstance().isInitialized()) {
|
||||
auto factory = makeShared<GLShaderFactory>();
|
||||
std::string shaderDir = getExecutableDir() + "shaders/";
|
||||
std::string cacheDir = getExecutableDir() + "shader_cache/";
|
||||
if (!ShaderManager::getInstance().init(shaderDir, cacheDir, factory)) {
|
||||
E2D_LOG_WARN("Failed to initialize ShaderManager with dir: {}",
|
||||
shaderDir);
|
||||
}
|
||||
|
||||
std::string windowBackend = winMod->getWindowBackend();
|
||||
|
||||
if (cfg_.backend.empty()) {
|
||||
E2D_LOG_INFO("No graphics backend specified, auto-selecting for window backend: {}", windowBackend);
|
||||
renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend);
|
||||
} else {
|
||||
if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) {
|
||||
E2D_LOG_WARN("Graphics backend '{}' is not compatible with window backend '{}'",
|
||||
cfg_.backend, windowBackend);
|
||||
}
|
||||
renderer_ = graphics::BackendFactory::createBackend(cfg_.backend);
|
||||
}
|
||||
|
||||
std::string windowBackend = winMod->getWindowBackend();
|
||||
|
||||
if (cfg_.backend.empty()) {
|
||||
E2D_LOG_INFO(
|
||||
"No graphics backend specified, auto-selecting for window backend: {}",
|
||||
windowBackend);
|
||||
renderer_ = graphics::BackendFactory::createBackendForWindow(windowBackend);
|
||||
} else {
|
||||
if (!graphics::BackendFactory::isCompatible(cfg_.backend, windowBackend)) {
|
||||
E2D_LOG_WARN(
|
||||
"Graphics backend '{}' is not compatible with window backend '{}'",
|
||||
cfg_.backend, windowBackend);
|
||||
}
|
||||
|
||||
if (!renderer_) {
|
||||
E2D_LOG_ERROR("Failed to create render backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!renderer_->init(winMod->win())) {
|
||||
E2D_LOG_ERROR("Failed to initialize render backend");
|
||||
renderer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("Render module initialized successfully");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
renderer_ = graphics::BackendFactory::createBackend(cfg_.backend);
|
||||
}
|
||||
|
||||
if (!renderer_) {
|
||||
E2D_LOG_ERROR("Failed to create render backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!renderer_->init(winMod->win())) {
|
||||
E2D_LOG_ERROR("Failed to initialize render backend");
|
||||
renderer_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("Render module initialized successfully");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderModule::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
if (renderer_) {
|
||||
renderer_->shutdown();
|
||||
renderer_.reset();
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
if (renderer_) {
|
||||
renderer_->shutdown();
|
||||
renderer_.reset();
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include <glad/glad.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/backends/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/core/render_target.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <stb/stb_image_write.h>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/memory/vram_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#include <extra2d/graphics/shader/shader_cache.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <chrono>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_cache.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
|
@ -13,9 +15,9 @@ namespace fs = std::filesystem;
|
|||
* @brief 获取单例实例
|
||||
* @return 缓存管理器实例引用
|
||||
*/
|
||||
ShaderCache& ShaderCache::getInstance() {
|
||||
static ShaderCache instance;
|
||||
return instance;
|
||||
ShaderCache &ShaderCache::getInstance() {
|
||||
static ShaderCache instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,35 +25,35 @@ ShaderCache& ShaderCache::getInstance() {
|
|||
* @param cacheDir 缓存目录路径
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::init(const std::string& cacheDir) {
|
||||
cacheDir_ = cacheDir;
|
||||
bool ShaderCache::init(const std::string &cacheDir) {
|
||||
cacheDir_ = cacheDir;
|
||||
|
||||
if (!ensureCacheDirectory()) {
|
||||
E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir);
|
||||
return false;
|
||||
}
|
||||
if (!ensureCacheDirectory()) {
|
||||
E2D_LOG_ERROR("Failed to create cache directory: {}", cacheDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadCacheIndex()) {
|
||||
E2D_LOG_WARN("Failed to load cache index, starting fresh");
|
||||
}
|
||||
if (!loadCacheIndex()) {
|
||||
E2D_LOG_WARN("Failed to load cache index, starting fresh");
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir);
|
||||
return true;
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Shader cache initialized at: {}", cacheDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭缓存系统
|
||||
*/
|
||||
void ShaderCache::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveCacheIndex();
|
||||
cacheMap_.clear();
|
||||
initialized_ = false;
|
||||
E2D_LOG_INFO("Shader cache shutdown");
|
||||
saveCacheIndex();
|
||||
cacheMap_.clear();
|
||||
initialized_ = false;
|
||||
E2D_LOG_INFO("Shader cache shutdown");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -60,13 +62,14 @@ void ShaderCache::shutdown() {
|
|||
* @param sourceHash 源码哈希值
|
||||
* @return 缓存有效返回true,否则返回false
|
||||
*/
|
||||
bool ShaderCache::hasValidCache(const std::string& name, const std::string& sourceHash) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return false;
|
||||
}
|
||||
bool ShaderCache::hasValidCache(const std::string &name,
|
||||
const std::string &sourceHash) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return it->second.sourceHash == sourceHash;
|
||||
return it->second.sourceHash == sourceHash;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -74,30 +77,30 @@ bool ShaderCache::hasValidCache(const std::string& name, const std::string& sour
|
|||
* @param name Shader名称
|
||||
* @return 缓存条目指针,不存在返回nullptr
|
||||
*/
|
||||
Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string& name) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string &name) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string cachePath = getCachePath(name);
|
||||
std::ifstream file(cachePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_WARN("Failed to open cache file: {}", cachePath);
|
||||
return nullptr;
|
||||
}
|
||||
std::string cachePath = getCachePath(name);
|
||||
std::ifstream file(cachePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_WARN("Failed to open cache file: {}", cachePath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto entry = std::make_shared<ShaderCacheEntry>(it->second);
|
||||
entry->binary.clear();
|
||||
auto entry = std::make_shared<ShaderCacheEntry>(it->second);
|
||||
entry->binary.clear();
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t fileSize = static_cast<size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t fileSize = static_cast<size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
entry->binary.resize(fileSize);
|
||||
file.read(reinterpret_cast<char*>(entry->binary.data()), fileSize);
|
||||
entry->binary.resize(fileSize);
|
||||
file.read(reinterpret_cast<char *>(entry->binary.data()), fileSize);
|
||||
|
||||
return entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,68 +108,72 @@ Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string& name) {
|
|||
* @param entry 缓存条目
|
||||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::saveCache(const ShaderCacheEntry& entry) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_WARN("ShaderCache not initialized, cannot save cache");
|
||||
return false;
|
||||
}
|
||||
bool ShaderCache::saveCache(const ShaderCacheEntry &entry) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_WARN("ShaderCache not initialized, cannot save cache");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.binary.empty()) {
|
||||
E2D_LOG_WARN("Shader binary is empty, skipping cache save for: {}", entry.name);
|
||||
return false;
|
||||
}
|
||||
if (entry.binary.empty()) {
|
||||
E2D_LOG_WARN("Shader binary is empty, skipping cache save for: {}",
|
||||
entry.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string cachePath = getCachePath(entry.name);
|
||||
E2D_LOG_DEBUG("Saving shader cache to: {} ({} bytes)", cachePath, entry.binary.size());
|
||||
|
||||
std::ofstream file(cachePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("Failed to create cache file: {}", cachePath);
|
||||
return false;
|
||||
}
|
||||
std::string cachePath = getCachePath(entry.name);
|
||||
E2D_LOG_DEBUG("Saving shader cache to: {} ({} bytes)", cachePath,
|
||||
entry.binary.size());
|
||||
|
||||
file.write(reinterpret_cast<const char*>(entry.binary.data()), entry.binary.size());
|
||||
file.close();
|
||||
std::ofstream file(cachePath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("Failed to create cache file: {}", cachePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
cacheMap_[entry.name] = entry;
|
||||
saveCacheIndex();
|
||||
file.write(reinterpret_cast<const char *>(entry.binary.data()),
|
||||
entry.binary.size());
|
||||
file.close();
|
||||
|
||||
E2D_LOG_INFO("Shader cache saved: {} ({} bytes)", entry.name, entry.binary.size());
|
||||
return true;
|
||||
cacheMap_[entry.name] = entry;
|
||||
saveCacheIndex();
|
||||
|
||||
E2D_LOG_INFO("Shader cache saved: {} ({} bytes)", entry.name,
|
||||
entry.binary.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使缓存失效
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void ShaderCache::invalidate(const std::string& name) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return;
|
||||
}
|
||||
void ShaderCache::invalidate(const std::string &name) {
|
||||
auto it = cacheMap_.find(name);
|
||||
if (it == cacheMap_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cachePath = getCachePath(name);
|
||||
fs::remove(cachePath);
|
||||
std::string cachePath = getCachePath(name);
|
||||
fs::remove(cachePath);
|
||||
|
||||
cacheMap_.erase(it);
|
||||
saveCacheIndex();
|
||||
cacheMap_.erase(it);
|
||||
saveCacheIndex();
|
||||
|
||||
E2D_LOG_DEBUG("Shader cache invalidated: {}", name);
|
||||
E2D_LOG_DEBUG("Shader cache invalidated: {}", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有缓存
|
||||
*/
|
||||
void ShaderCache::clearAll() {
|
||||
for (const auto& pair : cacheMap_) {
|
||||
std::string cachePath = getCachePath(pair.first);
|
||||
fs::remove(cachePath);
|
||||
}
|
||||
for (const auto &pair : cacheMap_) {
|
||||
std::string cachePath = getCachePath(pair.first);
|
||||
fs::remove(cachePath);
|
||||
}
|
||||
|
||||
cacheMap_.clear();
|
||||
saveCacheIndex();
|
||||
cacheMap_.clear();
|
||||
saveCacheIndex();
|
||||
|
||||
E2D_LOG_INFO("All shader caches cleared");
|
||||
E2D_LOG_INFO("All shader caches cleared");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -175,18 +182,18 @@ void ShaderCache::clearAll() {
|
|||
* @param fragSource 片段着色器源码
|
||||
* @return 哈希值字符串
|
||||
*/
|
||||
std::string ShaderCache::computeHash(const std::string& vertSource,
|
||||
const std::string& fragSource) {
|
||||
std::string combined = vertSource + fragSource;
|
||||
std::string ShaderCache::computeHash(const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
std::string combined = vertSource + fragSource;
|
||||
|
||||
uint32_t hash = 5381;
|
||||
for (char c : combined) {
|
||||
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
|
||||
}
|
||||
uint32_t hash = 5381;
|
||||
for (char c : combined) {
|
||||
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::hex << hash;
|
||||
return ss.str();
|
||||
std::stringstream ss;
|
||||
ss << std::hex << hash;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -194,43 +201,43 @@ std::string ShaderCache::computeHash(const std::string& vertSource,
|
|||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::loadCacheIndex() {
|
||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||
|
||||
if (!fs::exists(indexPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ifstream file(indexPath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pos = line.find('=');
|
||||
if (pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name = line.substr(0, pos);
|
||||
std::string hash = line.substr(pos + 1);
|
||||
|
||||
std::string cachePath = getCachePath(name);
|
||||
if (fs::exists(cachePath)) {
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = hash;
|
||||
entry.compileTime = static_cast<uint64_t>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count());
|
||||
cacheMap_[name] = entry;
|
||||
}
|
||||
}
|
||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||
|
||||
if (!fs::exists(indexPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ifstream file(indexPath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pos = line.find('=');
|
||||
if (pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string name = line.substr(0, pos);
|
||||
std::string hash = line.substr(pos + 1);
|
||||
|
||||
std::string cachePath = getCachePath(name);
|
||||
if (fs::exists(cachePath)) {
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = hash;
|
||||
entry.compileTime = static_cast<uint64_t>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count());
|
||||
cacheMap_[name] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -238,21 +245,21 @@ bool ShaderCache::loadCacheIndex() {
|
|||
* @return 保存成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderCache::saveCacheIndex() {
|
||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||
std::string indexPath = cacheDir_ + "/.cache_index";
|
||||
|
||||
std::ofstream file(indexPath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
std::ofstream file(indexPath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file << "# Extra2D Shader Cache Index\n";
|
||||
file << "# Format: name=hash\n";
|
||||
file << "# Extra2D Shader Cache Index\n";
|
||||
file << "# Format: name=hash\n";
|
||||
|
||||
for (const auto& pair : cacheMap_) {
|
||||
file << pair.first << "=" << pair.second.sourceHash << "\n";
|
||||
}
|
||||
for (const auto &pair : cacheMap_) {
|
||||
file << pair.first << "=" << pair.second.sourceHash << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -260,8 +267,8 @@ bool ShaderCache::saveCacheIndex() {
|
|||
* @param name Shader名称
|
||||
* @return 缓存文件完整路径
|
||||
*/
|
||||
std::string ShaderCache::getCachePath(const std::string& name) const {
|
||||
return cacheDir_ + "/" + name + ".cache";
|
||||
std::string ShaderCache::getCachePath(const std::string &name) const {
|
||||
return cacheDir_ + "/" + name + ".cache";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -269,18 +276,18 @@ std::string ShaderCache::getCachePath(const std::string& name) const {
|
|||
* @return 目录存在或创建成功返回true,否则返回false
|
||||
*/
|
||||
bool ShaderCache::ensureCacheDirectory() {
|
||||
if (cacheDir_.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (cacheDir_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
if (!fs::exists(cacheDir_)) {
|
||||
if (!fs::create_directories(cacheDir_, ec)) {
|
||||
return false;
|
||||
}
|
||||
std::error_code ec;
|
||||
if (!fs::exists(cacheDir_)) {
|
||||
if (!fs::create_directories(cacheDir_, ec)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#include <extra2d/graphics/shader/shader_hot_reloader.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <chrono>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_hot_reloader.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
|
@ -11,9 +13,9 @@ namespace fs = std::filesystem;
|
|||
* @brief 获取单例实例
|
||||
* @return 热重载管理器实例引用
|
||||
*/
|
||||
ShaderHotReloader& ShaderHotReloader::getInstance() {
|
||||
static ShaderHotReloader instance;
|
||||
return instance;
|
||||
ShaderHotReloader &ShaderHotReloader::getInstance() {
|
||||
static ShaderHotReloader instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -21,38 +23,38 @@ ShaderHotReloader& ShaderHotReloader::getInstance() {
|
|||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderHotReloader::init() {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
buffer_.resize(4096);
|
||||
buffer_.resize(4096);
|
||||
#endif
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Shader hot reloader initialized");
|
||||
return true;
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Shader hot reloader initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭热重载系统
|
||||
*/
|
||||
void ShaderHotReloader::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (watchHandle_ != nullptr) {
|
||||
FindCloseChangeNotification(watchHandle_);
|
||||
watchHandle_ = nullptr;
|
||||
}
|
||||
if (watchHandle_ != nullptr) {
|
||||
FindCloseChangeNotification(watchHandle_);
|
||||
watchHandle_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
watchMap_.clear();
|
||||
initialized_ = false;
|
||||
enabled_ = false;
|
||||
E2D_LOG_INFO("Shader hot reloader shutdown");
|
||||
watchMap_.clear();
|
||||
initialized_ = false;
|
||||
enabled_ = false;
|
||||
E2D_LOG_INFO("Shader hot reloader shutdown");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,47 +63,47 @@ void ShaderHotReloader::shutdown() {
|
|||
* @param filePaths 要监视的文件列表
|
||||
* @param callback 文件变化时的回调
|
||||
*/
|
||||
void ShaderHotReloader::watch(const std::string& shaderName,
|
||||
const std::vector<std::string>& filePaths,
|
||||
void ShaderHotReloader::watch(const std::string &shaderName,
|
||||
const std::vector<std::string> &filePaths,
|
||||
FileChangeCallback callback) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_WARN("Hot reloader not initialized");
|
||||
return;
|
||||
}
|
||||
if (!initialized_) {
|
||||
E2D_LOG_WARN("Hot reloader not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
WatchInfo info;
|
||||
info.filePaths = filePaths;
|
||||
info.callback = callback;
|
||||
WatchInfo info;
|
||||
info.filePaths = filePaths;
|
||||
info.callback = callback;
|
||||
|
||||
for (const auto& path : filePaths) {
|
||||
info.modifiedTimes[path] = getFileModifiedTime(path);
|
||||
}
|
||||
for (const auto &path : filePaths) {
|
||||
info.modifiedTimes[path] = getFileModifiedTime(path);
|
||||
}
|
||||
|
||||
watchMap_[shaderName] = std::move(info);
|
||||
E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size());
|
||||
watchMap_[shaderName] = std::move(info);
|
||||
E2D_LOG_DEBUG("Watching shader: {} ({} files)", shaderName, filePaths.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消监视
|
||||
* @param shaderName Shader名称
|
||||
*/
|
||||
void ShaderHotReloader::unwatch(const std::string& shaderName) {
|
||||
auto it = watchMap_.find(shaderName);
|
||||
if (it != watchMap_.end()) {
|
||||
watchMap_.erase(it);
|
||||
E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName);
|
||||
}
|
||||
void ShaderHotReloader::unwatch(const std::string &shaderName) {
|
||||
auto it = watchMap_.find(shaderName);
|
||||
if (it != watchMap_.end()) {
|
||||
watchMap_.erase(it);
|
||||
E2D_LOG_DEBUG("Stopped watching shader: {}", shaderName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新文件监视(在主循环中调用)
|
||||
*/
|
||||
void ShaderHotReloader::update() {
|
||||
if (!initialized_ || !enabled_) {
|
||||
return;
|
||||
}
|
||||
if (!initialized_ || !enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
pollChanges();
|
||||
pollChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,40 +111,41 @@ void ShaderHotReloader::update() {
|
|||
* @param enabled 是否启用
|
||||
*/
|
||||
void ShaderHotReloader::setEnabled(bool enabled) {
|
||||
enabled_ = enabled;
|
||||
E2D_LOG_DEBUG("Hot reload {}", enabled ? "enabled" : "disabled");
|
||||
enabled_ = enabled;
|
||||
E2D_LOG_DEBUG("Hot reload {}", enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 轮询检查文件变化
|
||||
*/
|
||||
void ShaderHotReloader::pollChanges() {
|
||||
auto now = static_cast<uint64_t>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count());
|
||||
auto now = static_cast<uint64_t>(
|
||||
std::chrono::system_clock::now().time_since_epoch().count());
|
||||
|
||||
for (auto& pair : watchMap_) {
|
||||
WatchInfo& info = pair.second;
|
||||
for (auto &pair : watchMap_) {
|
||||
WatchInfo &info = pair.second;
|
||||
|
||||
for (const auto& filePath : info.filePaths) {
|
||||
uint64_t currentModTime = getFileModifiedTime(filePath);
|
||||
uint64_t lastModTime = info.modifiedTimes[filePath];
|
||||
for (const auto &filePath : info.filePaths) {
|
||||
uint64_t currentModTime = getFileModifiedTime(filePath);
|
||||
uint64_t lastModTime = info.modifiedTimes[filePath];
|
||||
|
||||
if (currentModTime != 0 && lastModTime != 0 && currentModTime != lastModTime) {
|
||||
info.modifiedTimes[filePath] = currentModTime;
|
||||
if (currentModTime != 0 && lastModTime != 0 &&
|
||||
currentModTime != lastModTime) {
|
||||
info.modifiedTimes[filePath] = currentModTime;
|
||||
|
||||
FileChangeEvent event;
|
||||
event.filepath = filePath;
|
||||
event.type = FileChangeEvent::Type::Modified;
|
||||
event.timestamp = now;
|
||||
FileChangeEvent event;
|
||||
event.filepath = filePath;
|
||||
event.type = FileChangeEvent::Type::Modified;
|
||||
event.timestamp = now;
|
||||
|
||||
E2D_LOG_DEBUG("Shader file changed: {}", filePath);
|
||||
E2D_LOG_DEBUG("Shader file changed: {}", filePath);
|
||||
|
||||
if (info.callback) {
|
||||
info.callback(event);
|
||||
}
|
||||
}
|
||||
if (info.callback) {
|
||||
info.callback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -150,15 +153,16 @@ void ShaderHotReloader::pollChanges() {
|
|||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
uint64_t ShaderHotReloader::getFileModifiedTime(const std::string& filepath) {
|
||||
try {
|
||||
auto ftime = fs::last_write_time(filepath);
|
||||
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
|
||||
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t ShaderHotReloader::getFileModifiedTime(const std::string &filepath) {
|
||||
try {
|
||||
auto ftime = fs::last_write_time(filepath);
|
||||
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
ftime - fs::file_time_type::clock::now() +
|
||||
std::chrono::system_clock::now());
|
||||
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include <extra2d/graphics/shader/shader_loader.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_loader.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
|
@ -13,8 +13,7 @@ namespace fs = std::filesystem;
|
|||
/**
|
||||
* @brief 构造函数,初始化Shader加载器
|
||||
*/
|
||||
ShaderLoader::ShaderLoader() {
|
||||
}
|
||||
ShaderLoader::ShaderLoader() {}
|
||||
|
||||
/**
|
||||
* @brief 从分离文件加载Shader (.vert + .frag)
|
||||
|
|
@ -23,51 +22,53 @@ ShaderLoader::ShaderLoader() {
|
|||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult ShaderLoader::loadFromSeparateFiles(
|
||||
const std::string& name,
|
||||
const std::string& vertPath,
|
||||
const std::string& fragPath) {
|
||||
|
||||
ShaderLoadResult result;
|
||||
ShaderLoadResult
|
||||
ShaderLoader::loadFromSeparateFiles(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath) {
|
||||
|
||||
if (!fileExists(vertPath)) {
|
||||
result.errorMessage = "Vertex shader file not found: " + vertPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!fileExists(fragPath)) {
|
||||
result.errorMessage = "Fragment shader file not found: " + fragPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string vertSource = readFile(vertPath);
|
||||
std::string fragSource = readFile(fragPath);
|
||||
|
||||
if (vertSource.empty()) {
|
||||
result.errorMessage = "Failed to read vertex shader file: " + vertPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fragSource.empty()) {
|
||||
result.errorMessage = "Failed to read fragment shader file: " + fragPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::path vertDir = fs::path(vertPath).parent_path();
|
||||
fs::path fragDir = fs::path(fragPath).parent_path();
|
||||
|
||||
vertSource = processIncludes(vertSource, vertDir.string(), result.dependencies);
|
||||
fragSource = processIncludes(fragSource, fragDir.string(), result.dependencies);
|
||||
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
ShaderLoadResult result;
|
||||
|
||||
if (!fileExists(vertPath)) {
|
||||
result.errorMessage = "Vertex shader file not found: " + vertPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!fileExists(fragPath)) {
|
||||
result.errorMessage = "Fragment shader file not found: " + fragPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string vertSource = readFile(vertPath);
|
||||
std::string fragSource = readFile(fragPath);
|
||||
|
||||
if (vertSource.empty()) {
|
||||
result.errorMessage = "Failed to read vertex shader file: " + vertPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fragSource.empty()) {
|
||||
result.errorMessage = "Failed to read fragment shader file: " + fragPath;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::path vertDir = fs::path(vertPath).parent_path();
|
||||
fs::path fragDir = fs::path(fragPath).parent_path();
|
||||
|
||||
vertSource =
|
||||
processIncludes(vertSource, vertDir.string(), result.dependencies);
|
||||
fragSource =
|
||||
processIncludes(fragSource, fragDir.string(), result.dependencies);
|
||||
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,40 +76,42 @@ ShaderLoadResult ShaderLoader::loadFromSeparateFiles(
|
|||
* @param path 组合Shader文件路径
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string& path) {
|
||||
ShaderLoadResult result;
|
||||
|
||||
if (!fileExists(path)) {
|
||||
result.errorMessage = "Shader file not found: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string content = readFile(path);
|
||||
if (content.empty()) {
|
||||
result.errorMessage = "Failed to read shader file: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
ShaderMetadata metadata;
|
||||
std::string vertSource, fragSource;
|
||||
|
||||
if (!parseCombinedFile(content, vertSource, fragSource, metadata)) {
|
||||
result.errorMessage = "Failed to parse combined shader file: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::path baseDir = fs::path(path).parent_path();
|
||||
vertSource = processIncludes(vertSource, baseDir.string(), result.dependencies);
|
||||
fragSource = processIncludes(fragSource, baseDir.string(), result.dependencies);
|
||||
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string &path) {
|
||||
ShaderLoadResult result;
|
||||
|
||||
if (!fileExists(path)) {
|
||||
result.errorMessage = "Shader file not found: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string content = readFile(path);
|
||||
if (content.empty()) {
|
||||
result.errorMessage = "Failed to read shader file: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
ShaderMetadata metadata;
|
||||
std::string vertSource, fragSource;
|
||||
|
||||
if (!parseCombinedFile(content, vertSource, fragSource, metadata)) {
|
||||
result.errorMessage = "Failed to parse combined shader file: " + path;
|
||||
E2D_LOG_ERROR("{}", result.errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::path baseDir = fs::path(path).parent_path();
|
||||
vertSource =
|
||||
processIncludes(vertSource, baseDir.string(), result.dependencies);
|
||||
fragSource =
|
||||
processIncludes(fragSource, baseDir.string(), result.dependencies);
|
||||
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,15 +120,14 @@ ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string& path) {
|
|||
* @param fragSource 片段着色器源码
|
||||
* @return 加载结果
|
||||
*/
|
||||
ShaderLoadResult ShaderLoader::loadFromSource(
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) {
|
||||
|
||||
ShaderLoadResult result;
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
return result;
|
||||
ShaderLoadResult ShaderLoader::loadFromSource(const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
|
||||
ShaderLoadResult result;
|
||||
result.vertSource = vertSource;
|
||||
result.fragSource = fragSource;
|
||||
result.success = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -135,51 +137,52 @@ ShaderLoadResult ShaderLoader::loadFromSource(
|
|||
* @param outDependencies 输出依赖列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string ShaderLoader::processIncludes(
|
||||
const std::string& source,
|
||||
const std::string& baseDir,
|
||||
std::vector<std::string>& outDependencies) {
|
||||
|
||||
std::string result;
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
std::string
|
||||
ShaderLoader::processIncludes(const std::string &source,
|
||||
const std::string &baseDir,
|
||||
std::vector<std::string> &outDependencies) {
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
size_t includePos = line.find("#include");
|
||||
if (includePos != std::string::npos) {
|
||||
size_t startQuote = line.find('"', includePos);
|
||||
size_t endQuote = line.find('"', startQuote + 1);
|
||||
|
||||
if (startQuote != std::string::npos && endQuote != std::string::npos) {
|
||||
std::string includeName = line.substr(startQuote + 1, endQuote - startQuote - 1);
|
||||
std::string includePath = findIncludeFile(includeName, baseDir);
|
||||
|
||||
if (!includePath.empty()) {
|
||||
auto cacheIt = includeCache_.find(includePath);
|
||||
std::string includeContent;
|
||||
|
||||
if (cacheIt != includeCache_.end()) {
|
||||
includeContent = cacheIt->second;
|
||||
} else {
|
||||
includeContent = readFile(includePath);
|
||||
includeCache_[includePath] = includeContent;
|
||||
}
|
||||
|
||||
outDependencies.push_back(includePath);
|
||||
result += includeContent;
|
||||
result += "\n";
|
||||
continue;
|
||||
} else {
|
||||
E2D_LOG_WARN("Include file not found: {}", includeName);
|
||||
}
|
||||
}
|
||||
std::string result;
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
size_t includePos = line.find("#include");
|
||||
if (includePos != std::string::npos) {
|
||||
size_t startQuote = line.find('"', includePos);
|
||||
size_t endQuote = line.find('"', startQuote + 1);
|
||||
|
||||
if (startQuote != std::string::npos && endQuote != std::string::npos) {
|
||||
std::string includeName =
|
||||
line.substr(startQuote + 1, endQuote - startQuote - 1);
|
||||
std::string includePath = findIncludeFile(includeName, baseDir);
|
||||
|
||||
if (!includePath.empty()) {
|
||||
auto cacheIt = includeCache_.find(includePath);
|
||||
std::string includeContent;
|
||||
|
||||
if (cacheIt != includeCache_.end()) {
|
||||
includeContent = cacheIt->second;
|
||||
} else {
|
||||
includeContent = readFile(includePath);
|
||||
includeCache_[includePath] = includeContent;
|
||||
}
|
||||
|
||||
outDependencies.push_back(includePath);
|
||||
result += includeContent;
|
||||
result += "\n";
|
||||
continue;
|
||||
} else {
|
||||
E2D_LOG_WARN("Include file not found: {}", includeName);
|
||||
}
|
||||
|
||||
result += line;
|
||||
result += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
result += line;
|
||||
result += "\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -188,40 +191,40 @@ std::string ShaderLoader::processIncludes(
|
|||
* @param defines 预处理器定义列表
|
||||
* @return 处理后的源码
|
||||
*/
|
||||
std::string ShaderLoader::applyDefines(
|
||||
const std::string& source,
|
||||
const std::vector<std::string>& defines) {
|
||||
|
||||
if (defines.empty()) {
|
||||
return source;
|
||||
std::string
|
||||
ShaderLoader::applyDefines(const std::string &source,
|
||||
const std::vector<std::string> &defines) {
|
||||
|
||||
if (defines.empty()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
std::string defineBlock;
|
||||
for (const auto &def : defines) {
|
||||
defineBlock += "#define " + def + "\n";
|
||||
}
|
||||
|
||||
std::string result;
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
bool inserted = false;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
if (!inserted && (line.find("#version") != std::string::npos ||
|
||||
line.find("precision") != std::string::npos)) {
|
||||
result += line + "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string defineBlock;
|
||||
for (const auto& def : defines) {
|
||||
defineBlock += "#define " + def + "\n";
|
||||
if (!inserted) {
|
||||
result += defineBlock;
|
||||
inserted = true;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
std::istringstream stream(source);
|
||||
std::string line;
|
||||
bool inserted = false;
|
||||
result += line + "\n";
|
||||
}
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
if (!inserted && (line.find("#version") != std::string::npos ||
|
||||
line.find("precision") != std::string::npos)) {
|
||||
result += line + "\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inserted) {
|
||||
result += defineBlock;
|
||||
inserted = true;
|
||||
}
|
||||
|
||||
result += line + "\n";
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -229,30 +232,31 @@ std::string ShaderLoader::applyDefines(
|
|||
* @param path Shader文件路径
|
||||
* @return 元数据
|
||||
*/
|
||||
ShaderMetadata ShaderLoader::getMetadata(const std::string& path) {
|
||||
ShaderMetadata metadata;
|
||||
|
||||
if (!fileExists(path)) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
metadata.combinedPath = path;
|
||||
metadata.lastModified = getFileModifiedTime(path);
|
||||
|
||||
fs::path p(path);
|
||||
metadata.name = p.stem().string();
|
||||
ShaderMetadata ShaderLoader::getMetadata(const std::string &path) {
|
||||
ShaderMetadata metadata;
|
||||
|
||||
if (!fileExists(path)) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
metadata.combinedPath = path;
|
||||
metadata.lastModified = getFileModifiedTime(path);
|
||||
|
||||
fs::path p(path);
|
||||
metadata.name = p.stem().string();
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加include搜索路径
|
||||
* @param path 搜索路径
|
||||
*/
|
||||
void ShaderLoader::addIncludePath(const std::string& path) {
|
||||
if (std::find(includePaths_.begin(), includePaths_.end(), path) == includePaths_.end()) {
|
||||
includePaths_.push_back(path);
|
||||
}
|
||||
void ShaderLoader::addIncludePath(const std::string &path) {
|
||||
if (std::find(includePaths_.begin(), includePaths_.end(), path) ==
|
||||
includePaths_.end()) {
|
||||
includePaths_.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -260,15 +264,15 @@ void ShaderLoader::addIncludePath(const std::string& path) {
|
|||
* @param filepath 文件路径
|
||||
* @return 文件内容字符串
|
||||
*/
|
||||
std::string ShaderLoader::readFile(const std::string& filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return "";
|
||||
}
|
||||
std::string ShaderLoader::readFile(const std::string &filepath) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostringstream content;
|
||||
content << file.rdbuf();
|
||||
return content.str();
|
||||
std::ostringstream content;
|
||||
content << file.rdbuf();
|
||||
return content.str();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -276,19 +280,20 @@ std::string ShaderLoader::readFile(const std::string& filepath) {
|
|||
* @param filepath 文件路径
|
||||
* @return 修改时间戳
|
||||
*/
|
||||
uint64_t ShaderLoader::getFileModifiedTime(const std::string& filepath) {
|
||||
uint64_t ShaderLoader::getFileModifiedTime(const std::string &filepath) {
|
||||
#ifdef __SWITCH__
|
||||
(void)filepath;
|
||||
return 1;
|
||||
(void)filepath;
|
||||
return 1;
|
||||
#else
|
||||
try {
|
||||
auto ftime = fs::last_write_time(filepath);
|
||||
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
|
||||
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
auto ftime = fs::last_write_time(filepath);
|
||||
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
ftime - fs::file_time_type::clock::now() +
|
||||
std::chrono::system_clock::now());
|
||||
return static_cast<uint64_t>(sctp.time_since_epoch().count());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -297,8 +302,8 @@ uint64_t ShaderLoader::getFileModifiedTime(const std::string& filepath) {
|
|||
* @param filepath 文件路径
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
bool ShaderLoader::fileExists(const std::string& filepath) {
|
||||
return fs::exists(filepath);
|
||||
bool ShaderLoader::fileExists(const std::string &filepath) {
|
||||
return fs::exists(filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -309,73 +314,67 @@ bool ShaderLoader::fileExists(const std::string& filepath) {
|
|||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderLoader::parseCombinedFile(const std::string& content,
|
||||
std::string& outVert,
|
||||
std::string& outFrag,
|
||||
ShaderMetadata& outMetadata) {
|
||||
enum class Section {
|
||||
None,
|
||||
Meta,
|
||||
Vertex,
|
||||
Fragment
|
||||
};
|
||||
bool ShaderLoader::parseCombinedFile(const std::string &content,
|
||||
std::string &outVert, std::string &outFrag,
|
||||
ShaderMetadata &outMetadata) {
|
||||
enum class Section { None, Meta, Vertex, Fragment };
|
||||
|
||||
Section currentSection = Section::None;
|
||||
std::string metaContent;
|
||||
std::string vertContent;
|
||||
std::string fragContent;
|
||||
Section currentSection = Section::None;
|
||||
std::string metaContent;
|
||||
std::string vertContent;
|
||||
std::string fragContent;
|
||||
|
||||
std::istringstream stream(content);
|
||||
std::string line;
|
||||
std::istringstream stream(content);
|
||||
std::string line;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
std::string trimmedLine = line;
|
||||
size_t start = trimmedLine.find_first_not_of(" \t\r\n");
|
||||
if (start != std::string::npos) {
|
||||
trimmedLine = trimmedLine.substr(start);
|
||||
}
|
||||
size_t end = trimmedLine.find_last_not_of(" \t\r\n");
|
||||
if (end != std::string::npos) {
|
||||
trimmedLine = trimmedLine.substr(0, end + 1);
|
||||
}
|
||||
|
||||
if (trimmedLine == "#meta") {
|
||||
currentSection = Section::Meta;
|
||||
continue;
|
||||
} else if (trimmedLine == "#vertex") {
|
||||
currentSection = Section::Vertex;
|
||||
continue;
|
||||
} else if (trimmedLine == "#fragment") {
|
||||
currentSection = Section::Fragment;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (currentSection) {
|
||||
case Section::Meta:
|
||||
metaContent += line + "\n";
|
||||
break;
|
||||
case Section::Vertex:
|
||||
vertContent += line + "\n";
|
||||
break;
|
||||
case Section::Fragment:
|
||||
fragContent += line + "\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
while (std::getline(stream, line)) {
|
||||
std::string trimmedLine = line;
|
||||
size_t start = trimmedLine.find_first_not_of(" \t\r\n");
|
||||
if (start != std::string::npos) {
|
||||
trimmedLine = trimmedLine.substr(start);
|
||||
}
|
||||
size_t end = trimmedLine.find_last_not_of(" \t\r\n");
|
||||
if (end != std::string::npos) {
|
||||
trimmedLine = trimmedLine.substr(0, end + 1);
|
||||
}
|
||||
|
||||
if (vertContent.empty() || fragContent.empty()) {
|
||||
return false;
|
||||
if (trimmedLine == "#meta") {
|
||||
currentSection = Section::Meta;
|
||||
continue;
|
||||
} else if (trimmedLine == "#vertex") {
|
||||
currentSection = Section::Vertex;
|
||||
continue;
|
||||
} else if (trimmedLine == "#fragment") {
|
||||
currentSection = Section::Fragment;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!metaContent.empty()) {
|
||||
parseMetadata(metaContent, outMetadata);
|
||||
switch (currentSection) {
|
||||
case Section::Meta:
|
||||
metaContent += line + "\n";
|
||||
break;
|
||||
case Section::Vertex:
|
||||
vertContent += line + "\n";
|
||||
break;
|
||||
case Section::Fragment:
|
||||
fragContent += line + "\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outVert = vertContent;
|
||||
outFrag = fragContent;
|
||||
return true;
|
||||
if (vertContent.empty() || fragContent.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!metaContent.empty()) {
|
||||
parseMetadata(metaContent, outMetadata);
|
||||
}
|
||||
|
||||
outVert = vertContent;
|
||||
outFrag = fragContent;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -384,44 +383,45 @@ bool ShaderLoader::parseCombinedFile(const std::string& content,
|
|||
* @param outMetadata 输出元数据
|
||||
* @return 解析成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderLoader::parseMetadata(const std::string& jsonContent, ShaderMetadata& outMetadata) {
|
||||
std::string content = jsonContent;
|
||||
|
||||
size_t start = content.find('{');
|
||||
size_t end = content.rfind('}');
|
||||
if (start == std::string::npos || end == std::string::npos || end <= start) {
|
||||
return false;
|
||||
bool ShaderLoader::parseMetadata(const std::string &jsonContent,
|
||||
ShaderMetadata &outMetadata) {
|
||||
std::string content = jsonContent;
|
||||
|
||||
size_t start = content.find('{');
|
||||
size_t end = content.rfind('}');
|
||||
if (start == std::string::npos || end == std::string::npos || end <= start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
content = content.substr(start, end - start + 1);
|
||||
|
||||
auto extractString = [&content](const std::string &key) -> std::string {
|
||||
std::string searchKey = "\"" + key + "\"";
|
||||
size_t keyPos = content.find(searchKey);
|
||||
if (keyPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
content = content.substr(start, end - start + 1);
|
||||
|
||||
auto extractString = [&content](const std::string& key) -> std::string {
|
||||
std::string searchKey = "\"" + key + "\"";
|
||||
size_t keyPos = content.find(searchKey);
|
||||
if (keyPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t colonPos = content.find(':', keyPos);
|
||||
if (colonPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t quoteStart = content.find('"', colonPos);
|
||||
if (quoteStart == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t quoteEnd = content.find('"', quoteStart + 1);
|
||||
if (quoteEnd == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1);
|
||||
};
|
||||
size_t colonPos = content.find(':', keyPos);
|
||||
if (colonPos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
outMetadata.name = extractString("name");
|
||||
return true;
|
||||
size_t quoteStart = content.find('"', colonPos);
|
||||
if (quoteStart == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
size_t quoteEnd = content.find('"', quoteStart + 1);
|
||||
if (quoteEnd == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1);
|
||||
};
|
||||
|
||||
outMetadata.name = extractString("name");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -430,22 +430,23 @@ bool ShaderLoader::parseMetadata(const std::string& jsonContent, ShaderMetadata&
|
|||
* @param baseDir 基础目录
|
||||
* @return 找到的完整路径,未找到返回空字符串
|
||||
*/
|
||||
std::string ShaderLoader::findIncludeFile(const std::string& includeName, const std::string& baseDir) {
|
||||
fs::path basePath(baseDir);
|
||||
fs::path includePath = basePath / includeName;
|
||||
|
||||
std::string ShaderLoader::findIncludeFile(const std::string &includeName,
|
||||
const std::string &baseDir) {
|
||||
fs::path basePath(baseDir);
|
||||
fs::path includePath = basePath / includeName;
|
||||
|
||||
if (fs::exists(includePath)) {
|
||||
return includePath.string();
|
||||
}
|
||||
|
||||
for (const auto &searchPath : includePaths_) {
|
||||
includePath = fs::path(searchPath) / includeName;
|
||||
if (fs::exists(includePath)) {
|
||||
return includePath.string();
|
||||
return includePath.string();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& searchPath : includePaths_) {
|
||||
includePath = fs::path(searchPath) / includeName;
|
||||
if (fs::exists(includePath)) {
|
||||
return includePath.string();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
|
@ -11,9 +13,9 @@ namespace extra2d {
|
|||
* @brief 获取单例实例
|
||||
* @return Shader管理器实例引用
|
||||
*/
|
||||
ShaderManager& ShaderManager::getInstance() {
|
||||
static ShaderManager instance;
|
||||
return instance;
|
||||
ShaderManager &ShaderManager::getInstance() {
|
||||
static ShaderManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -23,22 +25,23 @@ ShaderManager& ShaderManager::getInstance() {
|
|||
* @param appName 应用名称(用于缓存目录)
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::init(Ptr<IShaderFactory> factory, const std::string& appName) {
|
||||
// 使用相对路径作为Shader目录
|
||||
std::string shaderDir = "shaders/";
|
||||
std::string cacheDir = "cache/shaders/";
|
||||
bool ShaderManager::init(Ptr<IShaderFactory> factory,
|
||||
const std::string &appName) {
|
||||
// 使用相对路径作为Shader目录
|
||||
std::string shaderDir = "shaders/";
|
||||
std::string cacheDir = "cache/shaders/";
|
||||
|
||||
// 非Switch平台支持热重载
|
||||
// 非Switch平台支持热重载
|
||||
#ifndef __SWITCH__
|
||||
hotReloadSupported_ = true;
|
||||
hotReloadSupported_ = true;
|
||||
#else
|
||||
hotReloadSupported_ = false;
|
||||
hotReloadSupported_ = false;
|
||||
#endif
|
||||
|
||||
E2D_LOG_INFO("ShaderManager init (HotReload: {})",
|
||||
hotReloadSupported_ ? "supported" : "not supported");
|
||||
E2D_LOG_INFO("ShaderManager init (HotReload: {})",
|
||||
hotReloadSupported_ ? "supported" : "not supported");
|
||||
|
||||
return init(shaderDir, cacheDir, factory);
|
||||
return init(shaderDir, cacheDir, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -48,75 +51,76 @@ bool ShaderManager::init(Ptr<IShaderFactory> factory, const std::string& appName
|
|||
* @param factory 渲染后端Shader工厂
|
||||
* @return 初始化成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::init(const std::string& shaderDir,
|
||||
const std::string& cacheDir,
|
||||
bool ShaderManager::init(const std::string &shaderDir,
|
||||
const std::string &cacheDir,
|
||||
Ptr<IShaderFactory> factory) {
|
||||
if (initialized_) {
|
||||
E2D_LOG_WARN("ShaderManager already initialized");
|
||||
return true;
|
||||
}
|
||||
if (initialized_) {
|
||||
E2D_LOG_WARN("ShaderManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!factory) {
|
||||
E2D_LOG_ERROR("Shader factory is null");
|
||||
return false;
|
||||
}
|
||||
if (!factory) {
|
||||
E2D_LOG_ERROR("Shader factory is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
shaderDir_ = shaderDir;
|
||||
cacheDir_ = cacheDir;
|
||||
factory_ = factory;
|
||||
shaderDir_ = shaderDir;
|
||||
cacheDir_ = cacheDir;
|
||||
factory_ = factory;
|
||||
|
||||
// 非Switch平台支持热重载
|
||||
// 非Switch平台支持热重载
|
||||
#ifndef __SWITCH__
|
||||
hotReloadSupported_ = true;
|
||||
hotReloadSupported_ = true;
|
||||
#else
|
||||
hotReloadSupported_ = false;
|
||||
hotReloadSupported_ = false;
|
||||
#endif
|
||||
|
||||
#ifdef __SWITCH__
|
||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||
E2D_LOG_WARN("Failed to initialize shader cache on Switch");
|
||||
}
|
||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||
E2D_LOG_WARN("Failed to initialize shader cache on Switch");
|
||||
}
|
||||
#else
|
||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||
E2D_LOG_WARN("Failed to initialize shader cache, caching disabled");
|
||||
}
|
||||
if (!ShaderCache::getInstance().init(cacheDir_)) {
|
||||
E2D_LOG_WARN("Failed to initialize shader cache, caching disabled");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (hotReloadSupported_) {
|
||||
if (!ShaderHotReloader::getInstance().init()) {
|
||||
E2D_LOG_WARN("Failed to initialize hot reloader");
|
||||
}
|
||||
if (hotReloadSupported_) {
|
||||
if (!ShaderHotReloader::getInstance().init()) {
|
||||
E2D_LOG_WARN("Failed to initialize hot reloader");
|
||||
}
|
||||
}
|
||||
|
||||
loader_.addIncludePath(shaderDir_ + "common");
|
||||
loader_.addIncludePath(shaderDir_ + "common");
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("ShaderManager initialized");
|
||||
E2D_LOG_INFO(" Shader directory: {}", shaderDir_);
|
||||
E2D_LOG_INFO(" Cache directory: {}", cacheDir_);
|
||||
E2D_LOG_INFO(" Hot reload: {}", hotReloadSupported_ ? "supported" : "not supported");
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("ShaderManager initialized");
|
||||
E2D_LOG_INFO(" Shader directory: {}", shaderDir_);
|
||||
E2D_LOG_INFO(" Cache directory: {}", cacheDir_);
|
||||
E2D_LOG_INFO(" Hot reload: {}",
|
||||
hotReloadSupported_ ? "supported" : "not supported");
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭Shader系统
|
||||
*/
|
||||
void ShaderManager::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hotReloadSupported_) {
|
||||
ShaderHotReloader::getInstance().shutdown();
|
||||
}
|
||||
ShaderCache::getInstance().shutdown();
|
||||
if (hotReloadSupported_) {
|
||||
ShaderHotReloader::getInstance().shutdown();
|
||||
}
|
||||
ShaderCache::getInstance().shutdown();
|
||||
|
||||
shaders_.clear();
|
||||
factory_.reset();
|
||||
initialized_ = false;
|
||||
shaders_.clear();
|
||||
factory_.reset();
|
||||
initialized_ = false;
|
||||
|
||||
E2D_LOG_INFO("ShaderManager shutdown");
|
||||
E2D_LOG_INFO("ShaderManager shutdown");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,72 +130,79 @@ void ShaderManager::shutdown() {
|
|||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name,
|
||||
const std::string& vertPath,
|
||||
const std::string& fragPath) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<IShader> ShaderManager::loadFromFiles(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
ShaderLoadResult result = loader_.loadFromSeparateFiles(name, vertPath, fragPath);
|
||||
if (!result.success) {
|
||||
E2D_LOG_ERROR("Failed to load shader files: {} - {}", vertPath, fragPath);
|
||||
return nullptr;
|
||||
}
|
||||
ShaderLoadResult result =
|
||||
loader_.loadFromSeparateFiles(name, vertPath, fragPath);
|
||||
if (!result.success) {
|
||||
E2D_LOG_ERROR("Failed to load shader files: {} - {}", vertPath, fragPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource);
|
||||
Ptr<IShader> shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
||||
std::string sourceHash =
|
||||
ShaderCache::computeHash(result.vertSource, result.fragSource);
|
||||
Ptr<IShader> shader =
|
||||
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
||||
|
||||
if (!shader) {
|
||||
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}",
|
||||
name);
|
||||
shader =
|
||||
factory_->createFromSource(name, result.vertSource, result.fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name);
|
||||
shader = factory_->createFromSource(name, result.vertSource, result.fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> binary;
|
||||
if (factory_->getShaderBinary(*shader, binary)) {
|
||||
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = sourceHash;
|
||||
entry.binary = binary;
|
||||
entry.dependencies = result.dependencies;
|
||||
ShaderCache::getInstance().saveCache(entry);
|
||||
} else {
|
||||
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
|
||||
}
|
||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = result.vertSource;
|
||||
info.fragSource = result.fragSource;
|
||||
info.filePaths = {vertPath, fragPath};
|
||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end());
|
||||
|
||||
info.metadata.name = name;
|
||||
info.metadata.vertPath = vertPath;
|
||||
info.metadata.fragPath = fragPath;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
auto callback = [this, name](const FileChangeEvent& event) {
|
||||
this->handleFileChange(name, event);
|
||||
};
|
||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback);
|
||||
std::vector<uint8_t> binary;
|
||||
if (factory_->getShaderBinary(*shader, binary)) {
|
||||
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = sourceHash;
|
||||
entry.binary = binary;
|
||||
entry.dependencies = result.dependencies;
|
||||
ShaderCache::getInstance().saveCache(entry);
|
||||
} else {
|
||||
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("Shader loaded: {}", name);
|
||||
return shader;
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = result.vertSource;
|
||||
info.fragSource = result.fragSource;
|
||||
info.filePaths = {vertPath, fragPath};
|
||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
|
||||
result.dependencies.end());
|
||||
|
||||
info.metadata.name = name;
|
||||
info.metadata.vertPath = vertPath;
|
||||
info.metadata.fragPath = fragPath;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
auto callback = [this, name](const FileChangeEvent &event) {
|
||||
this->handleFileChange(name, event);
|
||||
};
|
||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
|
||||
callback);
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("Shader loaded: {}", name);
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -199,70 +210,76 @@ Ptr<IShader> ShaderManager::loadFromFiles(const std::string& name,
|
|||
* @param path 组合Shader文件路径
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string& path) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string &path) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ShaderMetadata metadata = loader_.getMetadata(path);
|
||||
std::string name = metadata.name.empty() ? path : metadata.name;
|
||||
ShaderMetadata metadata = loader_.getMetadata(path);
|
||||
std::string name = metadata.name.empty() ? path : metadata.name;
|
||||
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
ShaderLoadResult result = loader_.loadFromCombinedFile(path);
|
||||
if (!result.success) {
|
||||
E2D_LOG_ERROR("Failed to load combined shader file: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
ShaderLoadResult result = loader_.loadFromCombinedFile(path);
|
||||
if (!result.success) {
|
||||
E2D_LOG_ERROR("Failed to load combined shader file: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string sourceHash = ShaderCache::computeHash(result.vertSource, result.fragSource);
|
||||
Ptr<IShader> shader = loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
||||
std::string sourceHash =
|
||||
ShaderCache::computeHash(result.vertSource, result.fragSource);
|
||||
Ptr<IShader> shader =
|
||||
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
|
||||
|
||||
if (!shader) {
|
||||
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}",
|
||||
name);
|
||||
shader =
|
||||
factory_->createFromSource(name, result.vertSource, result.fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}", name);
|
||||
shader = factory_->createFromSource(name, result.vertSource, result.fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> binary;
|
||||
if (factory_->getShaderBinary(*shader, binary)) {
|
||||
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = sourceHash;
|
||||
entry.binary = binary;
|
||||
entry.dependencies = result.dependencies;
|
||||
ShaderCache::getInstance().saveCache(entry);
|
||||
} else {
|
||||
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
|
||||
}
|
||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = result.vertSource;
|
||||
info.fragSource = result.fragSource;
|
||||
info.filePaths = {path};
|
||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(), result.dependencies.end());
|
||||
info.metadata = metadata;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
auto callback = [this, name](const FileChangeEvent& event) {
|
||||
this->handleFileChange(name, event);
|
||||
};
|
||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths, callback);
|
||||
std::vector<uint8_t> binary;
|
||||
if (factory_->getShaderBinary(*shader, binary)) {
|
||||
E2D_LOG_DEBUG("Got shader binary, size: {} bytes", binary.size());
|
||||
ShaderCacheEntry entry;
|
||||
entry.name = name;
|
||||
entry.sourceHash = sourceHash;
|
||||
entry.binary = binary;
|
||||
entry.dependencies = result.dependencies;
|
||||
ShaderCache::getInstance().saveCache(entry);
|
||||
} else {
|
||||
E2D_LOG_WARN("Failed to get shader binary for: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("Shader loaded from combined file: {}", name);
|
||||
return shader;
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = result.vertSource;
|
||||
info.fragSource = result.fragSource;
|
||||
info.filePaths = {path};
|
||||
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
|
||||
result.dependencies.end());
|
||||
info.metadata = metadata;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
auto callback = [this, name](const FileChangeEvent &event) {
|
||||
this->handleFileChange(name, event);
|
||||
};
|
||||
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
|
||||
callback);
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("Shader loaded from combined file: {}", name);
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -272,35 +289,36 @@ Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string& path) {
|
|||
* @param fragSource 片段着色器源码
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromSource(const std::string& name,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<IShader> ShaderManager::loadFromSource(const std::string &name,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
Ptr<IShader> shader = factory_->createFromSource(name, vertSource, fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<IShader> shader =
|
||||
factory_->createFromSource(name, vertSource, fragSource);
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("Failed to create shader from source: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = vertSource;
|
||||
info.fragSource = fragSource;
|
||||
info.metadata.name = name;
|
||||
ShaderInfo info;
|
||||
info.shader = shader;
|
||||
info.vertSource = vertSource;
|
||||
info.fragSource = fragSource;
|
||||
info.metadata.name = name;
|
||||
|
||||
shaders_[name] = std::move(info);
|
||||
shaders_[name] = std::move(info);
|
||||
|
||||
E2D_LOG_DEBUG("Shader loaded from source: {}", name);
|
||||
return shader;
|
||||
E2D_LOG_DEBUG("Shader loaded from source: {}", name);
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -308,12 +326,12 @@ Ptr<IShader> ShaderManager::loadFromSource(const std::string& name,
|
|||
* @param name Shader名称
|
||||
* @return Shader实例,不存在返回nullptr
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::get(const std::string& name) const {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
return nullptr;
|
||||
Ptr<IShader> ShaderManager::get(const std::string &name) const {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -321,34 +339,34 @@ Ptr<IShader> ShaderManager::get(const std::string& name) const {
|
|||
* @param name Shader名称
|
||||
* @return 存在返回true,否则返回false
|
||||
*/
|
||||
bool ShaderManager::has(const std::string& name) const {
|
||||
return shaders_.find(name) != shaders_.end();
|
||||
bool ShaderManager::has(const std::string &name) const {
|
||||
return shaders_.find(name) != shaders_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除Shader
|
||||
* @param name Shader名称
|
||||
*/
|
||||
void ShaderManager::remove(const std::string& name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
ShaderHotReloader::getInstance().unwatch(name);
|
||||
shaders_.erase(it);
|
||||
E2D_LOG_DEBUG("Shader removed: {}", name);
|
||||
}
|
||||
void ShaderManager::remove(const std::string &name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
ShaderHotReloader::getInstance().unwatch(name);
|
||||
shaders_.erase(it);
|
||||
E2D_LOG_DEBUG("Shader removed: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有Shader
|
||||
*/
|
||||
void ShaderManager::clear() {
|
||||
if (hotReloadSupported_) {
|
||||
for (const auto& pair : shaders_) {
|
||||
ShaderHotReloader::getInstance().unwatch(pair.first);
|
||||
}
|
||||
if (hotReloadSupported_) {
|
||||
for (const auto &pair : shaders_) {
|
||||
ShaderHotReloader::getInstance().unwatch(pair.first);
|
||||
}
|
||||
shaders_.clear();
|
||||
E2D_LOG_DEBUG("All shaders cleared");
|
||||
}
|
||||
shaders_.clear();
|
||||
E2D_LOG_DEBUG("All shaders cleared");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -356,11 +374,12 @@ void ShaderManager::clear() {
|
|||
* @param name Shader名称
|
||||
* @param callback 重载回调函数
|
||||
*/
|
||||
void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallback callback) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
it->second.reloadCallback = callback;
|
||||
}
|
||||
void ShaderManager::setReloadCallback(const std::string &name,
|
||||
ShaderReloadCallback callback) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
it->second.reloadCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -368,13 +387,13 @@ void ShaderManager::setReloadCallback(const std::string& name, ShaderReloadCallb
|
|||
* @param enabled 是否启用
|
||||
*/
|
||||
void ShaderManager::setHotReloadEnabled(bool enabled) {
|
||||
if (!hotReloadSupported_) {
|
||||
E2D_LOG_WARN("Hot reload not supported on this platform");
|
||||
return;
|
||||
}
|
||||
hotReloadEnabled_ = enabled;
|
||||
ShaderHotReloader::getInstance().setEnabled(enabled);
|
||||
E2D_LOG_INFO("Hot reload {}", enabled ? "enabled" : "disabled");
|
||||
if (!hotReloadSupported_) {
|
||||
E2D_LOG_WARN("Hot reload not supported on this platform");
|
||||
return;
|
||||
}
|
||||
hotReloadEnabled_ = enabled;
|
||||
ShaderHotReloader::getInstance().setEnabled(enabled);
|
||||
E2D_LOG_INFO("Hot reload {}", enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -382,16 +401,16 @@ void ShaderManager::setHotReloadEnabled(bool enabled) {
|
|||
* @return 启用返回true,否则返回false
|
||||
*/
|
||||
bool ShaderManager::isHotReloadEnabled() const {
|
||||
return hotReloadEnabled_ && hotReloadSupported_;
|
||||
return hotReloadEnabled_ && hotReloadSupported_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新热重载系统(主循环调用)
|
||||
*/
|
||||
void ShaderManager::update() {
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
ShaderHotReloader::getInstance().update();
|
||||
}
|
||||
if (hotReloadEnabled_ && hotReloadSupported_) {
|
||||
ShaderHotReloader::getInstance().update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -399,48 +418,51 @@ void ShaderManager::update() {
|
|||
* @param name Shader名称
|
||||
* @return 重载成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::reload(const std::string& name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it == shaders_.end()) {
|
||||
E2D_LOG_WARN("Shader not found for reload: {}", name);
|
||||
return false;
|
||||
bool ShaderManager::reload(const std::string &name) {
|
||||
auto it = shaders_.find(name);
|
||||
if (it == shaders_.end()) {
|
||||
E2D_LOG_WARN("Shader not found for reload: {}", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
ShaderInfo &info = it->second;
|
||||
|
||||
std::string vertSource = info.vertSource;
|
||||
std::string fragSource = info.fragSource;
|
||||
|
||||
if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) {
|
||||
ShaderLoadResult result = loader_.loadFromSeparateFiles(
|
||||
name, info.metadata.vertPath, info.metadata.fragPath);
|
||||
if (result.success) {
|
||||
vertSource = result.vertSource;
|
||||
fragSource = result.fragSource;
|
||||
}
|
||||
|
||||
ShaderInfo& info = it->second;
|
||||
|
||||
std::string vertSource = info.vertSource;
|
||||
std::string fragSource = info.fragSource;
|
||||
|
||||
if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) {
|
||||
ShaderLoadResult result = loader_.loadFromSeparateFiles(name, info.metadata.vertPath, info.metadata.fragPath);
|
||||
if (result.success) {
|
||||
vertSource = result.vertSource;
|
||||
fragSource = result.fragSource;
|
||||
}
|
||||
} else if (!info.metadata.combinedPath.empty()) {
|
||||
ShaderLoadResult result = loader_.loadFromCombinedFile(info.metadata.combinedPath);
|
||||
if (result.success) {
|
||||
vertSource = result.vertSource;
|
||||
fragSource = result.fragSource;
|
||||
}
|
||||
} else if (!info.metadata.combinedPath.empty()) {
|
||||
ShaderLoadResult result =
|
||||
loader_.loadFromCombinedFile(info.metadata.combinedPath);
|
||||
if (result.success) {
|
||||
vertSource = result.vertSource;
|
||||
fragSource = result.fragSource;
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<IShader> newShader = factory_->createFromSource(name, vertSource, fragSource);
|
||||
if (!newShader) {
|
||||
E2D_LOG_ERROR("Failed to reload shader: {}", name);
|
||||
return false;
|
||||
}
|
||||
Ptr<IShader> newShader =
|
||||
factory_->createFromSource(name, vertSource, fragSource);
|
||||
if (!newShader) {
|
||||
E2D_LOG_ERROR("Failed to reload shader: {}", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
info.shader = newShader;
|
||||
info.vertSource = vertSource;
|
||||
info.fragSource = fragSource;
|
||||
info.shader = newShader;
|
||||
info.vertSource = vertSource;
|
||||
info.fragSource = fragSource;
|
||||
|
||||
if (info.reloadCallback) {
|
||||
info.reloadCallback(newShader);
|
||||
}
|
||||
if (info.reloadCallback) {
|
||||
info.reloadCallback(newShader);
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("Shader reloaded: {}", name);
|
||||
return true;
|
||||
E2D_LOG_INFO("Shader reloaded: {}", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -448,21 +470,21 @@ bool ShaderManager::reload(const std::string& name) {
|
|||
* @param name 内置Shader名称
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) {
|
||||
Ptr<IShader> shader = get(name);
|
||||
if (shader) {
|
||||
return shader;
|
||||
}
|
||||
Ptr<IShader> ShaderManager::getBuiltin(const std::string &name) {
|
||||
Ptr<IShader> shader = get(name);
|
||||
if (shader) {
|
||||
return shader;
|
||||
}
|
||||
|
||||
// 尝试从新的多后端JSON元数据加载
|
||||
std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json";
|
||||
if (loader_.fileExists(jsonPath)) {
|
||||
return loadFromMetadata(jsonPath, name);
|
||||
}
|
||||
// 尝试从新的多后端JSON元数据加载
|
||||
std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json";
|
||||
if (loader_.fileExists(jsonPath)) {
|
||||
return loadFromMetadata(jsonPath, name);
|
||||
}
|
||||
|
||||
// 回退到旧的组合文件格式
|
||||
std::string path = shaderDir_ + "builtin/" + name + ".shader";
|
||||
return loadFromCombinedFile(path);
|
||||
// 回退到旧的组合文件格式
|
||||
std::string path = shaderDir_ + "builtin/" + name + ".shader";
|
||||
return loadFromCombinedFile(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -471,57 +493,60 @@ Ptr<IShader> ShaderManager::getBuiltin(const std::string& name) {
|
|||
* @param name Shader名称
|
||||
* @return 加载的Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromMetadata(const std::string& jsonPath, const std::string& name) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
Ptr<IShader> ShaderManager::loadFromMetadata(const std::string &jsonPath,
|
||||
const std::string &name) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 检查是否已加载
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
}
|
||||
|
||||
// 读取JSON文件
|
||||
std::string jsonContent = loader_.readFile(jsonPath);
|
||||
if (jsonContent.empty()) {
|
||||
E2D_LOG_ERROR("Failed to read shader metadata: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用nlohmann/json解析
|
||||
nl::json j = nl::json::parse(jsonContent);
|
||||
|
||||
// 获取OpenGL后端路径
|
||||
if (!j.contains("backends") || !j["backends"].contains("opengl")) {
|
||||
E2D_LOG_ERROR("No OpenGL backend found in shader metadata: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 检查是否已加载
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
return it->second.shader;
|
||||
auto &opengl = j["backends"]["opengl"];
|
||||
if (!opengl.contains("vertex") || !opengl.contains("fragment")) {
|
||||
E2D_LOG_ERROR("Missing vertex or fragment path in shader metadata: {}",
|
||||
jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 读取JSON文件
|
||||
std::string jsonContent = loader_.readFile(jsonPath);
|
||||
if (jsonContent.empty()) {
|
||||
E2D_LOG_ERROR("Failed to read shader metadata: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
std::string vertRelativePath = opengl["vertex"].get<std::string>();
|
||||
std::string fragRelativePath = opengl["fragment"].get<std::string>();
|
||||
|
||||
try {
|
||||
// 使用nlohmann/json解析
|
||||
nl::json j = nl::json::parse(jsonContent);
|
||||
// 构建完整路径
|
||||
std::string vertPath = shaderDir_ + vertRelativePath;
|
||||
std::string fragPath = shaderDir_ + fragRelativePath;
|
||||
|
||||
// 获取OpenGL后端路径
|
||||
if (!j.contains("backends") || !j["backends"].contains("opengl")) {
|
||||
E2D_LOG_ERROR("No OpenGL backend found in shader metadata: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
E2D_LOG_DEBUG("Loading shader from metadata: {} -> vert: {}, frag: {}",
|
||||
name, vertPath, fragPath);
|
||||
|
||||
auto& opengl = j["backends"]["opengl"];
|
||||
if (!opengl.contains("vertex") || !opengl.contains("fragment")) {
|
||||
E2D_LOG_ERROR("Missing vertex or fragment path in shader metadata: {}", jsonPath);
|
||||
return nullptr;
|
||||
}
|
||||
// 使用分离文件加载
|
||||
return loadFromFiles(name, vertPath, fragPath);
|
||||
|
||||
std::string vertRelativePath = opengl["vertex"].get<std::string>();
|
||||
std::string fragRelativePath = opengl["fragment"].get<std::string>();
|
||||
|
||||
// 构建完整路径
|
||||
std::string vertPath = shaderDir_ + vertRelativePath;
|
||||
std::string fragPath = shaderDir_ + fragRelativePath;
|
||||
|
||||
E2D_LOG_DEBUG("Loading shader from metadata: {} -> vert: {}, frag: {}", name, vertPath, fragPath);
|
||||
|
||||
// 使用分离文件加载
|
||||
return loadFromFiles(name, vertPath, fragPath);
|
||||
|
||||
} catch (const nl::json::exception& e) {
|
||||
E2D_LOG_ERROR("Failed to parse shader metadata {}: {}", jsonPath, e.what());
|
||||
return nullptr;
|
||||
}
|
||||
} catch (const nl::json::exception &e) {
|
||||
E2D_LOG_ERROR("Failed to parse shader metadata {}: {}", jsonPath, e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -529,52 +554,47 @@ Ptr<IShader> ShaderManager::loadFromMetadata(const std::string& jsonPath, const
|
|||
* @return 加载成功返回true,失败返回false
|
||||
*/
|
||||
bool ShaderManager::loadBuiltinShaders() {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return false;
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("ShaderManager not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool allSuccess = true;
|
||||
|
||||
const char *builtinNames[] = {"sprite", "particle", "shape", "postprocess",
|
||||
"font"};
|
||||
|
||||
for (const char *name : builtinNames) {
|
||||
// 首先尝试新的多后端JSON格式
|
||||
std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json";
|
||||
std::string shaderName = std::string("builtin_") + name;
|
||||
|
||||
Ptr<IShader> shader = nullptr;
|
||||
if (loader_.fileExists(jsonPath)) {
|
||||
shader = loadFromMetadata(jsonPath, name);
|
||||
} else {
|
||||
// 回退到旧的组合文件格式
|
||||
std::string path = shaderDir_ + "builtin/" + name + ".shader";
|
||||
shader = loadFromCombinedFile(path);
|
||||
}
|
||||
|
||||
bool allSuccess = true;
|
||||
|
||||
const char* builtinNames[] = {
|
||||
"sprite",
|
||||
"particle",
|
||||
"shape",
|
||||
"postprocess",
|
||||
"font"
|
||||
};
|
||||
|
||||
for (const char* name : builtinNames) {
|
||||
// 首先尝试新的多后端JSON格式
|
||||
std::string jsonPath = shaderDir_ + "shared/builtin/" + name + ".json";
|
||||
std::string shaderName = std::string("builtin_") + name;
|
||||
|
||||
Ptr<IShader> shader = nullptr;
|
||||
if (loader_.fileExists(jsonPath)) {
|
||||
shader = loadFromMetadata(jsonPath, name);
|
||||
} else {
|
||||
// 回退到旧的组合文件格式
|
||||
std::string path = shaderDir_ + "builtin/" + name + ".shader";
|
||||
shader = loadFromCombinedFile(path);
|
||||
}
|
||||
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("Failed to load builtin {} shader", name);
|
||||
allSuccess = false;
|
||||
} else {
|
||||
// 同时注册带 builtin_ 前缀的名称
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
shaders_[shaderName] = it->second;
|
||||
}
|
||||
}
|
||||
if (!shader) {
|
||||
E2D_LOG_ERROR("Failed to load builtin {} shader", name);
|
||||
allSuccess = false;
|
||||
} else {
|
||||
// 同时注册带 builtin_ 前缀的名称
|
||||
auto it = shaders_.find(name);
|
||||
if (it != shaders_.end()) {
|
||||
shaders_[shaderName] = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allSuccess) {
|
||||
E2D_LOG_INFO("All builtin shaders loaded");
|
||||
}
|
||||
if (allSuccess) {
|
||||
E2D_LOG_INFO("All builtin shaders loaded");
|
||||
}
|
||||
|
||||
return allSuccess;
|
||||
return allSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -585,29 +605,29 @@ bool ShaderManager::loadBuiltinShaders() {
|
|||
* @param fragSource 片段着色器源码
|
||||
* @return Shader实例
|
||||
*/
|
||||
Ptr<IShader> ShaderManager::loadFromCache(const std::string& name,
|
||||
const std::string& sourceHash,
|
||||
const std::string& vertSource,
|
||||
const std::string& fragSource) {
|
||||
if (!ShaderCache::getInstance().isInitialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<IShader> ShaderManager::loadFromCache(const std::string &name,
|
||||
const std::string &sourceHash,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource) {
|
||||
if (!ShaderCache::getInstance().isInitialized()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<ShaderCacheEntry> entry = ShaderCache::getInstance().loadCache(name);
|
||||
if (!entry || entry->binary.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
Ptr<ShaderCacheEntry> entry = ShaderCache::getInstance().loadCache(name);
|
||||
if (!entry || entry->binary.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ptr<IShader> shader = factory_->createFromBinary(name, entry->binary);
|
||||
if (shader) {
|
||||
E2D_LOG_DEBUG("Shader loaded from cache: {}", name);
|
||||
}
|
||||
Ptr<IShader> shader = factory_->createFromBinary(name, entry->binary);
|
||||
if (shader) {
|
||||
E2D_LOG_DEBUG("Shader loaded from cache: {}", name);
|
||||
}
|
||||
|
||||
return shader;
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -615,9 +635,10 @@ Ptr<IShader> ShaderManager::loadFromCache(const std::string& name,
|
|||
* @param shaderName Shader名称
|
||||
* @param event 文件变化事件
|
||||
*/
|
||||
void ShaderManager::handleFileChange(const std::string& shaderName, const FileChangeEvent& event) {
|
||||
E2D_LOG_DEBUG("Shader file changed: {} -> {}", shaderName, event.filepath);
|
||||
reload(shaderName);
|
||||
void ShaderManager::handleFileChange(const std::string &shaderName,
|
||||
const FileChangeEvent &event) {
|
||||
E2D_LOG_DEBUG("Shader file changed: {} -> {}", shaderName, event.filepath);
|
||||
reload(shaderName);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/shader/shader_manager.h>
|
||||
#include <extra2d/graphics/shader/shader_preset.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/texture/texture_atlas.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -21,10 +20,10 @@ TextureAtlasPage::TextureAtlasPage(int width, int height)
|
|||
// 创建空白纹理
|
||||
std::vector<uint8_t> emptyData(width * height * 4, 0);
|
||||
texture_ = makePtr<GLTexture>(width, height, emptyData.data(), 4);
|
||||
|
||||
|
||||
// 初始化矩形打包根节点
|
||||
root_ = std::make_unique<PackNode>(0, 0, width, height);
|
||||
|
||||
|
||||
E2D_LOG_INFO("Created texture atlas page: {}x{}", width, height);
|
||||
}
|
||||
|
||||
|
|
@ -46,53 +45,56 @@ TextureAtlasPage::~TextureAtlasPage() = default;
|
|||
*
|
||||
* 尝试将纹理添加到图集页面中,使用矩形打包算法找到合适位置
|
||||
*/
|
||||
bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int texHeight,
|
||||
const uint8_t* pixels, Rect& outUvRect) {
|
||||
bool TextureAtlasPage::tryAddTexture(const std::string &name, int texWidth,
|
||||
int texHeight, const uint8_t *pixels,
|
||||
Rect &outUvRect) {
|
||||
if (isFull_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 添加边距
|
||||
int paddedWidth = texWidth + 2 * PADDING;
|
||||
int paddedHeight = texHeight + 2 * PADDING;
|
||||
|
||||
|
||||
// 如果纹理太大,无法放入
|
||||
if (paddedWidth > width_ || paddedHeight > height_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 尝试插入
|
||||
PackNode* node = insert(root_.get(), paddedWidth, paddedHeight);
|
||||
PackNode *node = insert(root_.get(), paddedWidth, paddedHeight);
|
||||
if (node == nullptr) {
|
||||
// 无法放入,标记为满
|
||||
isFull_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 写入像素数据(跳过边距区域)
|
||||
writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight, pixels);
|
||||
|
||||
writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight,
|
||||
pixels);
|
||||
|
||||
// 创建条目
|
||||
AtlasEntry entry;
|
||||
entry.name = name;
|
||||
entry.originalSize = Vec2(static_cast<float>(texWidth), static_cast<float>(texHeight));
|
||||
entry.originalSize =
|
||||
Vec2(static_cast<float>(texWidth), static_cast<float>(texHeight));
|
||||
entry.padding = PADDING;
|
||||
|
||||
|
||||
// 计算 UV 坐标(考虑边距)
|
||||
float u1 = static_cast<float>(node->x + PADDING) / width_;
|
||||
float v1 = static_cast<float>(node->y + PADDING) / height_;
|
||||
float u2 = static_cast<float>(node->x + PADDING + texWidth) / width_;
|
||||
float v2 = static_cast<float>(node->y + PADDING + texHeight) / height_;
|
||||
|
||||
|
||||
entry.uvRect = Rect(u1, v1, u2 - u1, v2 - v1);
|
||||
outUvRect = entry.uvRect;
|
||||
|
||||
|
||||
entries_[name] = std::move(entry);
|
||||
usedArea_ += paddedWidth * paddedHeight;
|
||||
|
||||
E2D_LOG_DEBUG("Added texture '{}' to atlas: {}x{} at ({}, {})",
|
||||
name, texWidth, texHeight, node->x, node->y);
|
||||
|
||||
|
||||
E2D_LOG_DEBUG("Added texture '{}' to atlas: {}x{} at ({}, {})", name,
|
||||
texWidth, texHeight, node->x, node->y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -105,45 +107,50 @@ bool TextureAtlasPage::tryAddTexture(const std::string& name, int texWidth, int
|
|||
*
|
||||
* 使用二叉树算法递归查找合适的空间位置
|
||||
*/
|
||||
TextureAtlasPage::PackNode* TextureAtlasPage::insert(PackNode* node, int width, int height) {
|
||||
TextureAtlasPage::PackNode *TextureAtlasPage::insert(PackNode *node, int width,
|
||||
int height) {
|
||||
if (node == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// 如果节点已被使用,尝试子节点
|
||||
if (node->used) {
|
||||
PackNode* result = insert(node->left.get(), width, height);
|
||||
PackNode *result = insert(node->left.get(), width, height);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
return insert(node->right.get(), width, height);
|
||||
}
|
||||
|
||||
|
||||
// 检查是否适合
|
||||
if (width > node->width || height > node->height) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// 如果刚好合适,使用此节点
|
||||
if (width == node->width && height == node->height) {
|
||||
node->used = true;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
// 需要分割节点
|
||||
int dw = node->width - width;
|
||||
int dh = node->height - height;
|
||||
|
||||
|
||||
if (dw > dh) {
|
||||
// 水平分割
|
||||
node->left = std::make_unique<PackNode>(node->x, node->y, width, node->height);
|
||||
node->right = std::make_unique<PackNode>(node->x + width, node->y, dw, node->height);
|
||||
node->left =
|
||||
std::make_unique<PackNode>(node->x, node->y, width, node->height);
|
||||
node->right =
|
||||
std::make_unique<PackNode>(node->x + width, node->y, dw, node->height);
|
||||
} else {
|
||||
// 垂直分割
|
||||
node->left = std::make_unique<PackNode>(node->x, node->y, node->width, height);
|
||||
node->right = std::make_unique<PackNode>(node->x, node->y + height, node->width, dh);
|
||||
node->left =
|
||||
std::make_unique<PackNode>(node->x, node->y, node->width, height);
|
||||
node->right =
|
||||
std::make_unique<PackNode>(node->x, node->y + height, node->width, dh);
|
||||
}
|
||||
|
||||
|
||||
// 递归插入到左子节点
|
||||
return insert(node->left.get(), width, height);
|
||||
}
|
||||
|
|
@ -158,17 +165,19 @@ TextureAtlasPage::PackNode* TextureAtlasPage::insert(PackNode* node, int width,
|
|||
*
|
||||
* 使用glTexSubImage2D更新纹理的指定区域
|
||||
*/
|
||||
void TextureAtlasPage::writePixels(int x, int y, int w, int h, const uint8_t* pixels) {
|
||||
void TextureAtlasPage::writePixels(int x, int y, int w, int h,
|
||||
const uint8_t *pixels) {
|
||||
if (texture_ == nullptr || pixels == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 使用 glTexSubImage2D 更新纹理数据
|
||||
GLuint texID = static_cast<GLuint>(
|
||||
reinterpret_cast<uintptr_t>(texture_->getNativeHandle()));
|
||||
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texID);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
pixels);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +186,7 @@ void TextureAtlasPage::writePixels(int x, int y, int w, int h, const uint8_t* pi
|
|||
* @param name 纹理名称
|
||||
* @return 找到返回条目指针,未找到返回nullptr
|
||||
*/
|
||||
const AtlasEntry* TextureAtlasPage::getEntry(const std::string& name) const {
|
||||
const AtlasEntry *TextureAtlasPage::getEntry(const std::string &name) const {
|
||||
auto it = entries_.find(name);
|
||||
if (it != entries_.end()) {
|
||||
return &it->second;
|
||||
|
|
@ -205,11 +214,8 @@ float TextureAtlasPage::getUsageRatio() const {
|
|||
* 创建一个使用默认页面大小的纹理图集
|
||||
*/
|
||||
TextureAtlas::TextureAtlas()
|
||||
: pageSize_(TextureAtlasPage::DEFAULT_SIZE),
|
||||
sizeThreshold_(256),
|
||||
enabled_(true),
|
||||
initialized_(false) {
|
||||
}
|
||||
: pageSize_(TextureAtlasPage::DEFAULT_SIZE), sizeThreshold_(256),
|
||||
enabled_(true), initialized_(false) {}
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
|
|
@ -240,33 +246,33 @@ void TextureAtlas::init(int pageSize) {
|
|||
*
|
||||
* 尝试将纹理添加到现有页面,如空间不足则创建新页面
|
||||
*/
|
||||
bool TextureAtlas::addTexture(const std::string& name, int width, int height,
|
||||
const uint8_t* pixels) {
|
||||
bool TextureAtlas::addTexture(const std::string &name, int width, int height,
|
||||
const uint8_t *pixels) {
|
||||
if (!enabled_ || !initialized_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查是否已存在
|
||||
if (contains(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 检查纹理大小
|
||||
if (width > sizeThreshold_ || height > sizeThreshold_) {
|
||||
E2D_LOG_DEBUG("Texture '{}' too large for atlas ({}x{} > {}), skipping",
|
||||
E2D_LOG_DEBUG("Texture '{}' too large for atlas ({}x{} > {}), skipping",
|
||||
name, width, height, sizeThreshold_);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 尝试添加到现有页面
|
||||
Rect uvRect;
|
||||
for (auto& page : pages_) {
|
||||
for (auto &page : pages_) {
|
||||
if (page->tryAddTexture(name, width, height, pixels, uvRect)) {
|
||||
entryToPage_[name] = page.get();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 创建新页面
|
||||
auto newPage = std::make_unique<TextureAtlasPage>(pageSize_, pageSize_);
|
||||
if (newPage->tryAddTexture(name, width, height, pixels, uvRect)) {
|
||||
|
|
@ -274,7 +280,7 @@ bool TextureAtlas::addTexture(const std::string& name, int width, int height,
|
|||
pages_.push_back(std::move(newPage));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
E2D_LOG_WARN("Failed to add texture '{}' to atlas", name);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -284,7 +290,7 @@ bool TextureAtlas::addTexture(const std::string& name, int width, int height,
|
|||
* @param name 纹理名称
|
||||
* @return 存在返回true,不存在返回false
|
||||
*/
|
||||
bool TextureAtlas::contains(const std::string& name) const {
|
||||
bool TextureAtlas::contains(const std::string &name) const {
|
||||
return entryToPage_.find(name) != entryToPage_.end();
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +299,7 @@ bool TextureAtlas::contains(const std::string& name) const {
|
|||
* @param name 纹理名称
|
||||
* @return 找到返回纹理指针,未找到返回nullptr
|
||||
*/
|
||||
const Texture* TextureAtlas::getAtlasTexture(const std::string& name) const {
|
||||
const Texture *TextureAtlas::getAtlasTexture(const std::string &name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
return it->second->getTexture().get();
|
||||
|
|
@ -306,10 +312,10 @@ const Texture* TextureAtlas::getAtlasTexture(const std::string& name) const {
|
|||
* @param name 纹理名称
|
||||
* @return UV坐标矩形,未找到返回默认值
|
||||
*/
|
||||
Rect TextureAtlas::getUVRect(const std::string& name) const {
|
||||
Rect TextureAtlas::getUVRect(const std::string &name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
const AtlasEntry* entry = it->second->getEntry(name);
|
||||
const AtlasEntry *entry = it->second->getEntry(name);
|
||||
if (entry != nullptr) {
|
||||
return entry->uvRect;
|
||||
}
|
||||
|
|
@ -322,10 +328,10 @@ Rect TextureAtlas::getUVRect(const std::string& name) const {
|
|||
* @param name 纹理名称
|
||||
* @return 原始尺寸,未找到返回零向量
|
||||
*/
|
||||
Vec2 TextureAtlas::getOriginalSize(const std::string& name) const {
|
||||
Vec2 TextureAtlas::getOriginalSize(const std::string &name) const {
|
||||
auto it = entryToPage_.find(name);
|
||||
if (it != entryToPage_.end()) {
|
||||
const AtlasEntry* entry = it->second->getEntry(name);
|
||||
const AtlasEntry *entry = it->second->getEntry(name);
|
||||
if (entry != nullptr) {
|
||||
return entry->originalSize;
|
||||
}
|
||||
|
|
@ -343,9 +349,9 @@ float TextureAtlas::getTotalUsageRatio() const {
|
|||
if (pages_.empty()) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
float total = 0.0f;
|
||||
for (const auto& page : pages_) {
|
||||
for (const auto &page : pages_) {
|
||||
total += page->getUsageRatio();
|
||||
}
|
||||
return total / pages_.size();
|
||||
|
|
@ -372,7 +378,7 @@ void TextureAtlas::clear() {
|
|||
*
|
||||
* 使用静态局部变量实现线程安全的单例模式
|
||||
*/
|
||||
TextureAtlasMgr& TextureAtlasMgr::get() {
|
||||
TextureAtlasMgr &TextureAtlasMgr::get() {
|
||||
static TextureAtlasMgr instance;
|
||||
return instance;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,426 +1,492 @@
|
|||
#include "glfw_input.h"
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <cmath>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// GLFW 按键到引擎按键的映射
|
||||
static Key glfwToKey(int glfwKey) {
|
||||
switch (glfwKey) {
|
||||
// 字母键
|
||||
case GLFW_KEY_A: return Key::A;
|
||||
case GLFW_KEY_B: return Key::B;
|
||||
case GLFW_KEY_C: return Key::C;
|
||||
case GLFW_KEY_D: return Key::D;
|
||||
case GLFW_KEY_E: return Key::E;
|
||||
case GLFW_KEY_F: return Key::F;
|
||||
case GLFW_KEY_G: return Key::G;
|
||||
case GLFW_KEY_H: return Key::H;
|
||||
case GLFW_KEY_I: return Key::I;
|
||||
case GLFW_KEY_J: return Key::J;
|
||||
case GLFW_KEY_K: return Key::K;
|
||||
case GLFW_KEY_L: return Key::L;
|
||||
case GLFW_KEY_M: return Key::M;
|
||||
case GLFW_KEY_N: return Key::N;
|
||||
case GLFW_KEY_O: return Key::O;
|
||||
case GLFW_KEY_P: return Key::P;
|
||||
case GLFW_KEY_Q: return Key::Q;
|
||||
case GLFW_KEY_R: return Key::R;
|
||||
case GLFW_KEY_S: return Key::S;
|
||||
case GLFW_KEY_T: return Key::T;
|
||||
case GLFW_KEY_U: return Key::U;
|
||||
case GLFW_KEY_V: return Key::V;
|
||||
case GLFW_KEY_W: return Key::W;
|
||||
case GLFW_KEY_X: return Key::X;
|
||||
case GLFW_KEY_Y: return Key::Y;
|
||||
case GLFW_KEY_Z: return Key::Z;
|
||||
|
||||
// 数字键
|
||||
case GLFW_KEY_0: return Key::Num0;
|
||||
case GLFW_KEY_1: return Key::Num1;
|
||||
case GLFW_KEY_2: return Key::Num2;
|
||||
case GLFW_KEY_3: return Key::Num3;
|
||||
case GLFW_KEY_4: return Key::Num4;
|
||||
case GLFW_KEY_5: return Key::Num5;
|
||||
case GLFW_KEY_6: return Key::Num6;
|
||||
case GLFW_KEY_7: return Key::Num7;
|
||||
case GLFW_KEY_8: return Key::Num8;
|
||||
case GLFW_KEY_9: return Key::Num9;
|
||||
|
||||
// 功能键
|
||||
case GLFW_KEY_F1: return Key::F1;
|
||||
case GLFW_KEY_F2: return Key::F2;
|
||||
case GLFW_KEY_F3: return Key::F3;
|
||||
case GLFW_KEY_F4: return Key::F4;
|
||||
case GLFW_KEY_F5: return Key::F5;
|
||||
case GLFW_KEY_F6: return Key::F6;
|
||||
case GLFW_KEY_F7: return Key::F7;
|
||||
case GLFW_KEY_F8: return Key::F8;
|
||||
case GLFW_KEY_F9: return Key::F9;
|
||||
case GLFW_KEY_F10: return Key::F10;
|
||||
case GLFW_KEY_F11: return Key::F11;
|
||||
case GLFW_KEY_F12: return Key::F12;
|
||||
|
||||
// 特殊键
|
||||
case GLFW_KEY_SPACE: return Key::Space;
|
||||
case GLFW_KEY_ENTER: return Key::Enter;
|
||||
case GLFW_KEY_ESCAPE: return Key::Escape;
|
||||
case GLFW_KEY_TAB: return Key::Tab;
|
||||
case GLFW_KEY_BACKSPACE: return Key::Backspace;
|
||||
case GLFW_KEY_INSERT: return Key::Insert;
|
||||
case GLFW_KEY_DELETE: return Key::Delete;
|
||||
case GLFW_KEY_HOME: return Key::Home;
|
||||
case GLFW_KEY_END: return Key::End;
|
||||
case GLFW_KEY_PAGE_UP: return Key::PageUp;
|
||||
case GLFW_KEY_PAGE_DOWN: return Key::PageDown;
|
||||
|
||||
// 方向键
|
||||
case GLFW_KEY_UP: return Key::Up;
|
||||
case GLFW_KEY_DOWN: return Key::Down;
|
||||
case GLFW_KEY_LEFT: return Key::Left;
|
||||
case GLFW_KEY_RIGHT: return Key::Right;
|
||||
|
||||
// 修饰键
|
||||
case GLFW_KEY_LEFT_SHIFT: return Key::LShift;
|
||||
case GLFW_KEY_RIGHT_SHIFT: return Key::RShift;
|
||||
case GLFW_KEY_LEFT_CONTROL: return Key::LCtrl;
|
||||
case GLFW_KEY_RIGHT_CONTROL: return Key::RCtrl;
|
||||
case GLFW_KEY_LEFT_ALT: return Key::LAlt;
|
||||
case GLFW_KEY_RIGHT_ALT: return Key::RAlt;
|
||||
|
||||
// 锁定键
|
||||
case GLFW_KEY_CAPS_LOCK: return Key::CapsLock;
|
||||
case GLFW_KEY_NUM_LOCK: return Key::NumLock;
|
||||
case GLFW_KEY_SCROLL_LOCK: return Key::ScrollLock;
|
||||
|
||||
default: return Key::None;
|
||||
}
|
||||
switch (glfwKey) {
|
||||
// 字母键
|
||||
case GLFW_KEY_A:
|
||||
return Key::A;
|
||||
case GLFW_KEY_B:
|
||||
return Key::B;
|
||||
case GLFW_KEY_C:
|
||||
return Key::C;
|
||||
case GLFW_KEY_D:
|
||||
return Key::D;
|
||||
case GLFW_KEY_E:
|
||||
return Key::E;
|
||||
case GLFW_KEY_F:
|
||||
return Key::F;
|
||||
case GLFW_KEY_G:
|
||||
return Key::G;
|
||||
case GLFW_KEY_H:
|
||||
return Key::H;
|
||||
case GLFW_KEY_I:
|
||||
return Key::I;
|
||||
case GLFW_KEY_J:
|
||||
return Key::J;
|
||||
case GLFW_KEY_K:
|
||||
return Key::K;
|
||||
case GLFW_KEY_L:
|
||||
return Key::L;
|
||||
case GLFW_KEY_M:
|
||||
return Key::M;
|
||||
case GLFW_KEY_N:
|
||||
return Key::N;
|
||||
case GLFW_KEY_O:
|
||||
return Key::O;
|
||||
case GLFW_KEY_P:
|
||||
return Key::P;
|
||||
case GLFW_KEY_Q:
|
||||
return Key::Q;
|
||||
case GLFW_KEY_R:
|
||||
return Key::R;
|
||||
case GLFW_KEY_S:
|
||||
return Key::S;
|
||||
case GLFW_KEY_T:
|
||||
return Key::T;
|
||||
case GLFW_KEY_U:
|
||||
return Key::U;
|
||||
case GLFW_KEY_V:
|
||||
return Key::V;
|
||||
case GLFW_KEY_W:
|
||||
return Key::W;
|
||||
case GLFW_KEY_X:
|
||||
return Key::X;
|
||||
case GLFW_KEY_Y:
|
||||
return Key::Y;
|
||||
case GLFW_KEY_Z:
|
||||
return Key::Z;
|
||||
|
||||
// 数字键
|
||||
case GLFW_KEY_0:
|
||||
return Key::Num0;
|
||||
case GLFW_KEY_1:
|
||||
return Key::Num1;
|
||||
case GLFW_KEY_2:
|
||||
return Key::Num2;
|
||||
case GLFW_KEY_3:
|
||||
return Key::Num3;
|
||||
case GLFW_KEY_4:
|
||||
return Key::Num4;
|
||||
case GLFW_KEY_5:
|
||||
return Key::Num5;
|
||||
case GLFW_KEY_6:
|
||||
return Key::Num6;
|
||||
case GLFW_KEY_7:
|
||||
return Key::Num7;
|
||||
case GLFW_KEY_8:
|
||||
return Key::Num8;
|
||||
case GLFW_KEY_9:
|
||||
return Key::Num9;
|
||||
|
||||
// 功能键
|
||||
case GLFW_KEY_F1:
|
||||
return Key::F1;
|
||||
case GLFW_KEY_F2:
|
||||
return Key::F2;
|
||||
case GLFW_KEY_F3:
|
||||
return Key::F3;
|
||||
case GLFW_KEY_F4:
|
||||
return Key::F4;
|
||||
case GLFW_KEY_F5:
|
||||
return Key::F5;
|
||||
case GLFW_KEY_F6:
|
||||
return Key::F6;
|
||||
case GLFW_KEY_F7:
|
||||
return Key::F7;
|
||||
case GLFW_KEY_F8:
|
||||
return Key::F8;
|
||||
case GLFW_KEY_F9:
|
||||
return Key::F9;
|
||||
case GLFW_KEY_F10:
|
||||
return Key::F10;
|
||||
case GLFW_KEY_F11:
|
||||
return Key::F11;
|
||||
case GLFW_KEY_F12:
|
||||
return Key::F12;
|
||||
|
||||
// 特殊键
|
||||
case GLFW_KEY_SPACE:
|
||||
return Key::Space;
|
||||
case GLFW_KEY_ENTER:
|
||||
return Key::Enter;
|
||||
case GLFW_KEY_ESCAPE:
|
||||
return Key::Escape;
|
||||
case GLFW_KEY_TAB:
|
||||
return Key::Tab;
|
||||
case GLFW_KEY_BACKSPACE:
|
||||
return Key::Backspace;
|
||||
case GLFW_KEY_INSERT:
|
||||
return Key::Insert;
|
||||
case GLFW_KEY_DELETE:
|
||||
return Key::Delete;
|
||||
case GLFW_KEY_HOME:
|
||||
return Key::Home;
|
||||
case GLFW_KEY_END:
|
||||
return Key::End;
|
||||
case GLFW_KEY_PAGE_UP:
|
||||
return Key::PageUp;
|
||||
case GLFW_KEY_PAGE_DOWN:
|
||||
return Key::PageDown;
|
||||
|
||||
// 方向键
|
||||
case GLFW_KEY_UP:
|
||||
return Key::Up;
|
||||
case GLFW_KEY_DOWN:
|
||||
return Key::Down;
|
||||
case GLFW_KEY_LEFT:
|
||||
return Key::Left;
|
||||
case GLFW_KEY_RIGHT:
|
||||
return Key::Right;
|
||||
|
||||
// 修饰键
|
||||
case GLFW_KEY_LEFT_SHIFT:
|
||||
return Key::LShift;
|
||||
case GLFW_KEY_RIGHT_SHIFT:
|
||||
return Key::RShift;
|
||||
case GLFW_KEY_LEFT_CONTROL:
|
||||
return Key::LCtrl;
|
||||
case GLFW_KEY_RIGHT_CONTROL:
|
||||
return Key::RCtrl;
|
||||
case GLFW_KEY_LEFT_ALT:
|
||||
return Key::LAlt;
|
||||
case GLFW_KEY_RIGHT_ALT:
|
||||
return Key::RAlt;
|
||||
|
||||
// 锁定键
|
||||
case GLFW_KEY_CAPS_LOCK:
|
||||
return Key::CapsLock;
|
||||
case GLFW_KEY_NUM_LOCK:
|
||||
return Key::NumLock;
|
||||
case GLFW_KEY_SCROLL_LOCK:
|
||||
return Key::ScrollLock;
|
||||
|
||||
default:
|
||||
return Key::None;
|
||||
}
|
||||
}
|
||||
|
||||
GLFWInput::GLFWInput() {
|
||||
keyCurrent_.fill(false);
|
||||
keyPrevious_.fill(false);
|
||||
mouseCurrent_.fill(false);
|
||||
mousePrevious_.fill(false);
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
keyCurrent_.fill(false);
|
||||
keyPrevious_.fill(false);
|
||||
mouseCurrent_.fill(false);
|
||||
mousePrevious_.fill(false);
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
}
|
||||
|
||||
GLFWInput::~GLFWInput() {
|
||||
shutdown();
|
||||
}
|
||||
GLFWInput::~GLFWInput() { shutdown(); }
|
||||
|
||||
void GLFWInput::init() {
|
||||
E2D_LOG_INFO("GLFWInput initialized");
|
||||
openGamepad();
|
||||
E2D_LOG_INFO("GLFWInput initialized");
|
||||
openGamepad();
|
||||
}
|
||||
|
||||
void GLFWInput::shutdown() {
|
||||
closeGamepad();
|
||||
E2D_LOG_INFO("GLFWInput shutdown");
|
||||
closeGamepad();
|
||||
E2D_LOG_INFO("GLFWInput shutdown");
|
||||
}
|
||||
|
||||
void GLFWInput::update() {
|
||||
// 保存上一帧状态
|
||||
keyPrevious_ = keyCurrent_;
|
||||
mousePrevious_ = mouseCurrent_;
|
||||
gamepadPrevious_ = gamepadCurrent_;
|
||||
|
||||
// 重置增量
|
||||
scrollDelta_ = 0.0f;
|
||||
mouseDelta_ = Vec2{0.0f, 0.0f};
|
||||
|
||||
// 更新游戏手柄
|
||||
updateGamepad();
|
||||
|
||||
// 更新键盘状态(通过轮询 GLFW)
|
||||
if (window_) {
|
||||
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; ++i) {
|
||||
Key key = glfwToKey(i);
|
||||
if (key != Key::None) {
|
||||
int state = glfwGetKey(window_, i);
|
||||
keyCurrent_[static_cast<size_t>(key)] = (state == GLFW_PRESS);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新鼠标按钮状态
|
||||
for (int i = 0; i < static_cast<int>(Mouse::Count); ++i) {
|
||||
int glfwButton = GLFW_MOUSE_BUTTON_1 + i;
|
||||
if (glfwButton <= GLFW_MOUSE_BUTTON_LAST) {
|
||||
int state = glfwGetMouseButton(window_, glfwButton);
|
||||
mouseCurrent_[i] = (state == GLFW_PRESS);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取鼠标位置
|
||||
double x, y;
|
||||
glfwGetCursorPos(window_, &x, &y);
|
||||
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
|
||||
// 保存上一帧状态
|
||||
keyPrevious_ = keyCurrent_;
|
||||
mousePrevious_ = mouseCurrent_;
|
||||
gamepadPrevious_ = gamepadCurrent_;
|
||||
|
||||
// 重置增量
|
||||
scrollDelta_ = 0.0f;
|
||||
mouseDelta_ = Vec2{0.0f, 0.0f};
|
||||
|
||||
// 更新游戏手柄
|
||||
updateGamepad();
|
||||
|
||||
// 更新键盘状态(通过轮询 GLFW)
|
||||
if (window_) {
|
||||
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; ++i) {
|
||||
Key key = glfwToKey(i);
|
||||
if (key != Key::None) {
|
||||
int state = glfwGetKey(window_, i);
|
||||
keyCurrent_[static_cast<size_t>(key)] = (state == GLFW_PRESS);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新鼠标按钮状态
|
||||
for (int i = 0; i < static_cast<int>(Mouse::Count); ++i) {
|
||||
int glfwButton = GLFW_MOUSE_BUTTON_1 + i;
|
||||
if (glfwButton <= GLFW_MOUSE_BUTTON_LAST) {
|
||||
int state = glfwGetMouseButton(window_, glfwButton);
|
||||
mouseCurrent_[i] = (state == GLFW_PRESS);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取鼠标位置
|
||||
double x, y;
|
||||
glfwGetCursorPos(window_, &x, &y);
|
||||
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
|
||||
}
|
||||
}
|
||||
|
||||
bool GLFWInput::down(Key key) const {
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLFWInput::pressed(Key key) const {
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx] && !keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx] && !keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLFWInput::released(Key key) const {
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return !keyCurrent_[idx] && keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return !keyCurrent_[idx] && keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLFWInput::down(Mouse btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLFWInput::pressed(Mouse btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx] && !mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx] && !mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLFWInput::released(Mouse btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return !mouseCurrent_[idx] && mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return !mouseCurrent_[idx] && mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2 GLFWInput::mouse() const {
|
||||
return mousePos_;
|
||||
Vec2 GLFWInput::mouse() const { return mousePos_; }
|
||||
|
||||
Vec2 GLFWInput::mouseDelta() const { return mouseDelta_; }
|
||||
|
||||
float GLFWInput::scroll() const { return scroll_; }
|
||||
|
||||
float GLFWInput::scrollDelta() const { return scrollDelta_; }
|
||||
|
||||
void GLFWInput::setMouse(const Vec2 &pos) {
|
||||
if (window_) {
|
||||
glfwSetCursorPos(window_, pos.x, pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
Vec2 GLFWInput::mouseDelta() const {
|
||||
return mouseDelta_;
|
||||
}
|
||||
|
||||
float GLFWInput::scroll() const {
|
||||
return scroll_;
|
||||
}
|
||||
|
||||
float GLFWInput::scrollDelta() const {
|
||||
return scrollDelta_;
|
||||
}
|
||||
|
||||
void GLFWInput::setMouse(const Vec2& pos) {
|
||||
if (window_) {
|
||||
glfwSetCursorPos(window_, pos.x, pos.y);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLFWInput::gamepad() const {
|
||||
return gamepadId_ != -1;
|
||||
}
|
||||
bool GLFWInput::gamepad() const { return gamepadId_ != -1; }
|
||||
|
||||
bool GLFWInput::down(Gamepad btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLFWInput::pressed(Gamepad btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx] && !gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx] && !gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLFWInput::released(Gamepad btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return !gamepadCurrent_[idx] && gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return !gamepadCurrent_[idx] && gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2 GLFWInput::leftStick() const {
|
||||
return leftStick_;
|
||||
}
|
||||
Vec2 GLFWInput::leftStick() const { return leftStick_; }
|
||||
|
||||
Vec2 GLFWInput::rightStick() const {
|
||||
return rightStick_;
|
||||
}
|
||||
Vec2 GLFWInput::rightStick() const { return rightStick_; }
|
||||
|
||||
float GLFWInput::leftTrigger() const {
|
||||
return leftTrigger_;
|
||||
}
|
||||
float GLFWInput::leftTrigger() const { return leftTrigger_; }
|
||||
|
||||
float GLFWInput::rightTrigger() const {
|
||||
return rightTrigger_;
|
||||
}
|
||||
float GLFWInput::rightTrigger() const { return rightTrigger_; }
|
||||
|
||||
void GLFWInput::vibrate(float left, float right) {
|
||||
// GLFW 本身不支持震动,需要平台特定的代码
|
||||
// 这里可以扩展为使用平台特定的 API
|
||||
(void)left;
|
||||
(void)right;
|
||||
// GLFW 本身不支持震动,需要平台特定的代码
|
||||
// 这里可以扩展为使用平台特定的 API
|
||||
(void)left;
|
||||
(void)right;
|
||||
}
|
||||
|
||||
bool GLFWInput::touching() const {
|
||||
return false;
|
||||
}
|
||||
bool GLFWInput::touching() const { return false; }
|
||||
|
||||
int GLFWInput::touchCount() const {
|
||||
return 0;
|
||||
}
|
||||
int GLFWInput::touchCount() const { return 0; }
|
||||
|
||||
Vec2 GLFWInput::touch(int index) const {
|
||||
(void)index;
|
||||
return Vec2{0.0f, 0.0f};
|
||||
(void)index;
|
||||
return Vec2{0.0f, 0.0f};
|
||||
}
|
||||
|
||||
TouchPoint GLFWInput::touchPoint(int index) const {
|
||||
(void)index;
|
||||
return TouchPoint{};
|
||||
(void)index;
|
||||
return TouchPoint{};
|
||||
}
|
||||
|
||||
// 事件处理函数
|
||||
void GLFWInput::handleKeyEvent(int key, int scancode, int action, int mods) {
|
||||
(void)scancode;
|
||||
(void)mods;
|
||||
|
||||
Key eKey = glfwToKey(key);
|
||||
if (eKey != Key::None) {
|
||||
size_t idx = static_cast<size_t>(eKey);
|
||||
if (action == GLFW_PRESS) {
|
||||
keyCurrent_[idx] = true;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
keyCurrent_[idx] = false;
|
||||
}
|
||||
(void)scancode;
|
||||
(void)mods;
|
||||
|
||||
Key eKey = glfwToKey(key);
|
||||
if (eKey != Key::None) {
|
||||
size_t idx = static_cast<size_t>(eKey);
|
||||
if (action == GLFW_PRESS) {
|
||||
keyCurrent_[idx] = true;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
keyCurrent_[idx] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWInput::handleMouseButtonEvent(int button, int action, int mods) {
|
||||
(void)mods;
|
||||
|
||||
if (button >= GLFW_MOUSE_BUTTON_1 && button <= GLFW_MOUSE_BUTTON_LAST) {
|
||||
size_t idx = static_cast<size_t>(button - GLFW_MOUSE_BUTTON_1);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
if (action == GLFW_PRESS) {
|
||||
mouseCurrent_[idx] = true;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
mouseCurrent_[idx] = false;
|
||||
}
|
||||
}
|
||||
(void)mods;
|
||||
|
||||
if (button >= GLFW_MOUSE_BUTTON_1 && button <= GLFW_MOUSE_BUTTON_LAST) {
|
||||
size_t idx = static_cast<size_t>(button - GLFW_MOUSE_BUTTON_1);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
if (action == GLFW_PRESS) {
|
||||
mouseCurrent_[idx] = true;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
mouseCurrent_[idx] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWInput::handleCursorPosEvent(double xpos, double ypos) {
|
||||
Vec2 newPos{static_cast<float>(xpos), static_cast<float>(ypos)};
|
||||
mouseDelta_ = newPos - mousePos_;
|
||||
mousePos_ = newPos;
|
||||
Vec2 newPos{static_cast<float>(xpos), static_cast<float>(ypos)};
|
||||
mouseDelta_ = newPos - mousePos_;
|
||||
mousePos_ = newPos;
|
||||
}
|
||||
|
||||
void GLFWInput::handleScrollEvent(double xoffset, double yoffset) {
|
||||
(void)xoffset;
|
||||
scroll_ += static_cast<float>(yoffset);
|
||||
scrollDelta_ += static_cast<float>(yoffset);
|
||||
(void)xoffset;
|
||||
scroll_ += static_cast<float>(yoffset);
|
||||
scrollDelta_ += static_cast<float>(yoffset);
|
||||
}
|
||||
|
||||
void GLFWInput::handleJoystickEvent(int jid, int event) {
|
||||
if (event == GLFW_CONNECTED) {
|
||||
E2D_LOG_INFO("Gamepad connected: {}", jid);
|
||||
if (gamepadId_ == -1) {
|
||||
openGamepad();
|
||||
}
|
||||
} else if (event == GLFW_DISCONNECTED) {
|
||||
if (jid == gamepadId_) {
|
||||
E2D_LOG_INFO("Gamepad disconnected: {}", jid);
|
||||
closeGamepad();
|
||||
}
|
||||
if (event == GLFW_CONNECTED) {
|
||||
E2D_LOG_INFO("Gamepad connected: {}", jid);
|
||||
if (gamepadId_ == -1) {
|
||||
openGamepad();
|
||||
}
|
||||
} else if (event == GLFW_DISCONNECTED) {
|
||||
if (jid == gamepadId_) {
|
||||
E2D_LOG_INFO("Gamepad disconnected: {}", jid);
|
||||
closeGamepad();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWInput::updateGamepad() {
|
||||
if (gamepadId_ == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLFWgamepadstate state;
|
||||
if (!glfwGetGamepadState(gamepadId_, &state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新按钮状态
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::A)] = state.buttons[GLFW_GAMEPAD_BUTTON_A] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::B)] = state.buttons[GLFW_GAMEPAD_BUTTON_B] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::X)] = state.buttons[GLFW_GAMEPAD_BUTTON_X] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Y)] = state.buttons[GLFW_GAMEPAD_BUTTON_Y] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Back)] = state.buttons[GLFW_GAMEPAD_BUTTON_BACK] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Start)] = state.buttons[GLFW_GAMEPAD_BUTTON_START] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::LStick)] = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::RStick)] = state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::LB)] = state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::RB)] = state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DUp)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DDown)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DLeft)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DRight)] = state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Guide)] = state.buttons[GLFW_GAMEPAD_BUTTON_GUIDE] == GLFW_PRESS;
|
||||
|
||||
// 更新摇杆值(应用死区)
|
||||
leftStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]);
|
||||
leftStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
|
||||
rightStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
|
||||
rightStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
|
||||
|
||||
// 更新扳机值(范围 [0, 1])
|
||||
leftTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) * 0.5f;
|
||||
rightTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) * 0.5f;
|
||||
if (gamepadId_ == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLFWgamepadstate state;
|
||||
if (!glfwGetGamepadState(gamepadId_, &state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新按钮状态
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::A)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_A] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::B)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_B] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::X)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_X] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Y)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_Y] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Back)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_BACK] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Start)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_START] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::LStick)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::RStick)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::LB)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::RB)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DUp)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DDown)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DLeft)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::DRight)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] == GLFW_PRESS;
|
||||
gamepadCurrent_[static_cast<size_t>(Gamepad::Guide)] =
|
||||
state.buttons[GLFW_GAMEPAD_BUTTON_GUIDE] == GLFW_PRESS;
|
||||
|
||||
// 更新摇杆值(应用死区)
|
||||
leftStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X]);
|
||||
leftStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
|
||||
rightStick_.x = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]);
|
||||
rightStick_.y = applyDeadzone(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
|
||||
|
||||
// 更新扳机值(范围 [0, 1])
|
||||
leftTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] + 1.0f) * 0.5f;
|
||||
rightTrigger_ = (state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + 1.0f) * 0.5f;
|
||||
}
|
||||
|
||||
void GLFWInput::openGamepad() {
|
||||
for (int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; ++jid) {
|
||||
if (glfwJoystickPresent(jid) && glfwJoystickIsGamepad(jid)) {
|
||||
gamepadId_ = jid;
|
||||
E2D_LOG_INFO("Gamepad opened: {}", glfwGetGamepadName(jid));
|
||||
return;
|
||||
}
|
||||
for (int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; ++jid) {
|
||||
if (glfwJoystickPresent(jid) && glfwJoystickIsGamepad(jid)) {
|
||||
gamepadId_ = jid;
|
||||
E2D_LOG_INFO("Gamepad opened: {}", glfwGetGamepadName(jid));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWInput::closeGamepad() {
|
||||
if (gamepadId_ != -1) {
|
||||
gamepadId_ = -1;
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
leftStick_ = Vec2{0.0f, 0.0f};
|
||||
rightStick_ = Vec2{0.0f, 0.0f};
|
||||
leftTrigger_ = 0.0f;
|
||||
rightTrigger_ = 0.0f;
|
||||
}
|
||||
if (gamepadId_ != -1) {
|
||||
gamepadId_ = -1;
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
leftStick_ = Vec2{0.0f, 0.0f};
|
||||
rightStick_ = Vec2{0.0f, 0.0f};
|
||||
leftTrigger_ = 0.0f;
|
||||
rightTrigger_ = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float GLFWInput::applyDeadzone(float value) const {
|
||||
if (std::abs(value) < deadzone_) {
|
||||
return 0.0f;
|
||||
}
|
||||
float sign = value >= 0.0f ? 1.0f : -1.0f;
|
||||
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
|
||||
if (std::abs(value) < deadzone_) {
|
||||
return 0.0f;
|
||||
}
|
||||
float sign = value >= 0.0f ? 1.0f : -1.0f;
|
||||
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,449 +1,441 @@
|
|||
#include "glfw_window.h"
|
||||
#include "glfw_input.h"
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
GLFWWindow::GLFWWindow() {}
|
||||
|
||||
GLFWWindow::~GLFWWindow() {
|
||||
destroy();
|
||||
}
|
||||
GLFWWindow::~GLFWWindow() { destroy(); }
|
||||
|
||||
bool GLFWWindow::create(const std::string& title, int width, int height, bool vsync) {
|
||||
if (!initGLFW()) {
|
||||
return false;
|
||||
}
|
||||
bool GLFWWindow::create(const std::string &title, int width, int height,
|
||||
bool vsync) {
|
||||
if (!initGLFW()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_DEPTH_BITS, 24);
|
||||
glfwWindowHint(GLFW_STENCIL_BITS, 8);
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_DEPTH_BITS, 24);
|
||||
glfwWindowHint(GLFW_STENCIL_BITS, 8);
|
||||
|
||||
#ifdef __SWITCH__
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
||||
fullscreen_ = true;
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
|
||||
fullscreen_ = true;
|
||||
#else
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
#endif
|
||||
|
||||
GLFWmonitor* monitor = nullptr;
|
||||
GLFWmonitor *monitor = nullptr;
|
||||
#ifdef __SWITCH__
|
||||
monitor = glfwGetPrimaryMonitor();
|
||||
monitor = glfwGetPrimaryMonitor();
|
||||
#endif
|
||||
|
||||
glfwWindow_ = glfwCreateWindow(
|
||||
width, height,
|
||||
title.c_str(),
|
||||
monitor,
|
||||
nullptr
|
||||
);
|
||||
glfwWindow_ =
|
||||
glfwCreateWindow(width, height, title.c_str(), monitor, nullptr);
|
||||
|
||||
if (!glfwWindow_) {
|
||||
E2D_LOG_ERROR("Failed to create GLFW window");
|
||||
deinitGLFW();
|
||||
return false;
|
||||
}
|
||||
if (!glfwWindow_) {
|
||||
E2D_LOG_ERROR("Failed to create GLFW window");
|
||||
deinitGLFW();
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef __SWITCH__
|
||||
if (!fullscreen_ && !monitor) {
|
||||
GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
|
||||
if (primaryMonitor) {
|
||||
const GLFWvidmode* mode = glfwGetVideoMode(primaryMonitor);
|
||||
if (mode) {
|
||||
int screenWidth = mode->width;
|
||||
int screenHeight = mode->height;
|
||||
int windowX = (screenWidth - width) / 2;
|
||||
int windowY = (screenHeight - height) / 2;
|
||||
glfwSetWindowPos(glfwWindow_, windowX, windowY);
|
||||
}
|
||||
}
|
||||
if (!fullscreen_ && !monitor) {
|
||||
GLFWmonitor *primaryMonitor = glfwGetPrimaryMonitor();
|
||||
if (primaryMonitor) {
|
||||
const GLFWvidmode *mode = glfwGetVideoMode(primaryMonitor);
|
||||
if (mode) {
|
||||
int screenWidth = mode->width;
|
||||
int screenHeight = mode->height;
|
||||
int windowX = (screenWidth - width) / 2;
|
||||
int windowY = (screenHeight - height) / 2;
|
||||
glfwSetWindowPos(glfwWindow_, windowX, windowY);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
glfwMakeContextCurrent(glfwWindow_);
|
||||
glfwMakeContextCurrent(glfwWindow_);
|
||||
|
||||
if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) {
|
||||
E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
|
||||
glfwDestroyWindow(glfwWindow_);
|
||||
glfwWindow_ = nullptr;
|
||||
deinitGLFW();
|
||||
return false;
|
||||
}
|
||||
if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) {
|
||||
E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
|
||||
glfwDestroyWindow(glfwWindow_);
|
||||
glfwWindow_ = nullptr;
|
||||
deinitGLFW();
|
||||
return false;
|
||||
}
|
||||
|
||||
glfwSwapInterval(vsync ? 1 : 0);
|
||||
vsync_ = vsync;
|
||||
glfwSwapInterval(vsync ? 1 : 0);
|
||||
vsync_ = vsync;
|
||||
|
||||
glfwGetWindowSize(glfwWindow_, &width_, &height_);
|
||||
updateContentScale();
|
||||
glfwGetWindowSize(glfwWindow_, &width_, &height_);
|
||||
updateContentScale();
|
||||
|
||||
glfwSetWindowUserPointer(glfwWindow_, this);
|
||||
glfwSetFramebufferSizeCallback(glfwWindow_, framebufferSizeCallback);
|
||||
glfwSetWindowCloseCallback(glfwWindow_, windowCloseCallback);
|
||||
glfwSetWindowFocusCallback(glfwWindow_, windowFocusCallback);
|
||||
glfwSetWindowIconifyCallback(glfwWindow_, windowIconifyCallback);
|
||||
glfwSetCursorPosCallback(glfwWindow_, cursorPosCallback);
|
||||
glfwSetMouseButtonCallback(glfwWindow_, mouseButtonCallback);
|
||||
glfwSetScrollCallback(glfwWindow_, scrollCallback);
|
||||
glfwSetKeyCallback(glfwWindow_, keyCallback);
|
||||
glfwSetJoystickCallback(joystickCallback);
|
||||
glfwSetWindowUserPointer(glfwWindow_, this);
|
||||
glfwSetFramebufferSizeCallback(glfwWindow_, framebufferSizeCallback);
|
||||
glfwSetWindowCloseCallback(glfwWindow_, windowCloseCallback);
|
||||
glfwSetWindowFocusCallback(glfwWindow_, windowFocusCallback);
|
||||
glfwSetWindowIconifyCallback(glfwWindow_, windowIconifyCallback);
|
||||
glfwSetCursorPosCallback(glfwWindow_, cursorPosCallback);
|
||||
glfwSetMouseButtonCallback(glfwWindow_, mouseButtonCallback);
|
||||
glfwSetScrollCallback(glfwWindow_, scrollCallback);
|
||||
glfwSetKeyCallback(glfwWindow_, keyCallback);
|
||||
glfwSetJoystickCallback(joystickCallback);
|
||||
|
||||
input_ = makeUnique<GLFWInput>();
|
||||
input_->setWindow(glfwWindow_);
|
||||
input_->init();
|
||||
input_ = makeUnique<GLFWInput>();
|
||||
input_->setWindow(glfwWindow_);
|
||||
input_->init();
|
||||
|
||||
E2D_LOG_INFO("GLFW window created: {}x{}", width_, height_);
|
||||
E2D_LOG_INFO(" Platform: OpenGL ES 3.2");
|
||||
return true;
|
||||
E2D_LOG_INFO("GLFW window created: {}x{}", width_, height_);
|
||||
E2D_LOG_INFO(" Platform: OpenGL ES 3.2");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLFWWindow::destroy() {
|
||||
if (input_) {
|
||||
input_->shutdown();
|
||||
input_.reset();
|
||||
}
|
||||
if (input_) {
|
||||
input_->shutdown();
|
||||
input_.reset();
|
||||
}
|
||||
|
||||
if (glfwWindow_) {
|
||||
glfwDestroyWindow(glfwWindow_);
|
||||
glfwWindow_ = nullptr;
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwDestroyWindow(glfwWindow_);
|
||||
glfwWindow_ = nullptr;
|
||||
}
|
||||
|
||||
deinitGLFW();
|
||||
deinitGLFW();
|
||||
}
|
||||
|
||||
void GLFWWindow::poll() {
|
||||
if (!glfwWindow_) return;
|
||||
if (!glfwWindow_)
|
||||
return;
|
||||
|
||||
if (input_) {
|
||||
input_->update();
|
||||
}
|
||||
if (input_) {
|
||||
input_->update();
|
||||
}
|
||||
|
||||
glfwPollEvents();
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
void GLFWWindow::swap() {
|
||||
if (glfwWindow_) {
|
||||
glfwSwapBuffers(glfwWindow_);
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwSwapBuffers(glfwWindow_);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLFWWindow::shouldClose() const {
|
||||
if (!glfwWindow_) return true;
|
||||
return shouldClose_ || glfwWindowShouldClose(glfwWindow_);
|
||||
if (!glfwWindow_)
|
||||
return true;
|
||||
return shouldClose_ || glfwWindowShouldClose(glfwWindow_);
|
||||
}
|
||||
|
||||
void GLFWWindow::close() {
|
||||
shouldClose_ = true;
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowShouldClose(glfwWindow_, GLFW_TRUE);
|
||||
}
|
||||
shouldClose_ = true;
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowShouldClose(glfwWindow_, GLFW_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::setTitle(const std::string& title) {
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowTitle(glfwWindow_, title.c_str());
|
||||
}
|
||||
void GLFWWindow::setTitle(const std::string &title) {
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowTitle(glfwWindow_, title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::setSize(int w, int h) {
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowSize(glfwWindow_, w, h);
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowSize(glfwWindow_, w, h);
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::setPos(int x, int y) {
|
||||
#ifndef __SWITCH__
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowPos(glfwWindow_, x, y);
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwSetWindowPos(glfwWindow_, x, y);
|
||||
}
|
||||
#else
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)x;
|
||||
(void)y;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLFWWindow::setFullscreen(bool fs) {
|
||||
#ifndef __SWITCH__
|
||||
if (!glfwWindow_) return;
|
||||
|
||||
if (fs == fullscreen_) return;
|
||||
|
||||
if (fs) {
|
||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||
glfwSetWindowMonitor(glfwWindow_, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
|
||||
} else {
|
||||
glfwSetWindowMonitor(glfwWindow_, nullptr, 100, 100, 1280, 720, 0);
|
||||
}
|
||||
fullscreen_ = fs;
|
||||
glfwGetWindowSize(glfwWindow_, &width_, &height_);
|
||||
updateContentScale();
|
||||
if (!glfwWindow_)
|
||||
return;
|
||||
|
||||
if (fs == fullscreen_)
|
||||
return;
|
||||
|
||||
if (fs) {
|
||||
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
||||
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
|
||||
glfwSetWindowMonitor(glfwWindow_, monitor, 0, 0, mode->width, mode->height,
|
||||
mode->refreshRate);
|
||||
} else {
|
||||
glfwSetWindowMonitor(glfwWindow_, nullptr, 100, 100, 1280, 720, 0);
|
||||
}
|
||||
fullscreen_ = fs;
|
||||
glfwGetWindowSize(glfwWindow_, &width_, &height_);
|
||||
updateContentScale();
|
||||
#else
|
||||
(void)fs;
|
||||
(void)fs;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLFWWindow::setVSync(bool vsync) {
|
||||
if (glfwWindow_) {
|
||||
glfwSwapInterval(vsync ? 1 : 0);
|
||||
vsync_ = vsync;
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwSwapInterval(vsync ? 1 : 0);
|
||||
vsync_ = vsync;
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::setVisible(bool visible) {
|
||||
#ifndef __SWITCH__
|
||||
if (glfwWindow_) {
|
||||
if (visible) {
|
||||
glfwShowWindow(glfwWindow_);
|
||||
} else {
|
||||
glfwHideWindow(glfwWindow_);
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
if (visible) {
|
||||
glfwShowWindow(glfwWindow_);
|
||||
} else {
|
||||
glfwHideWindow(glfwWindow_);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)visible;
|
||||
(void)visible;
|
||||
#endif
|
||||
}
|
||||
|
||||
int GLFWWindow::width() const {
|
||||
return width_;
|
||||
}
|
||||
int GLFWWindow::width() const { return width_; }
|
||||
|
||||
int GLFWWindow::height() const {
|
||||
return height_;
|
||||
}
|
||||
int GLFWWindow::height() const { return height_; }
|
||||
|
||||
Size GLFWWindow::size() const {
|
||||
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||
}
|
||||
|
||||
Vec2 GLFWWindow::pos() const {
|
||||
int x = 0, y = 0;
|
||||
int x = 0, y = 0;
|
||||
#ifndef __SWITCH__
|
||||
if (glfwWindow_) {
|
||||
glfwGetWindowPos(glfwWindow_, &x, &y);
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwGetWindowPos(glfwWindow_, &x, &y);
|
||||
}
|
||||
#endif
|
||||
return Vec2(static_cast<float>(x), static_cast<float>(y));
|
||||
return Vec2(static_cast<float>(x), static_cast<float>(y));
|
||||
}
|
||||
|
||||
bool GLFWWindow::fullscreen() const {
|
||||
return fullscreen_;
|
||||
}
|
||||
bool GLFWWindow::fullscreen() const { return fullscreen_; }
|
||||
|
||||
bool GLFWWindow::vsync() const {
|
||||
return vsync_;
|
||||
}
|
||||
bool GLFWWindow::vsync() const { return vsync_; }
|
||||
|
||||
bool GLFWWindow::focused() const {
|
||||
return focused_;
|
||||
}
|
||||
bool GLFWWindow::focused() const { return focused_; }
|
||||
|
||||
bool GLFWWindow::minimized() const {
|
||||
return minimized_;
|
||||
}
|
||||
bool GLFWWindow::minimized() const { return minimized_; }
|
||||
|
||||
float GLFWWindow::scaleX() const {
|
||||
return scaleX_;
|
||||
}
|
||||
float GLFWWindow::scaleX() const { return scaleX_; }
|
||||
|
||||
float GLFWWindow::scaleY() const {
|
||||
return scaleY_;
|
||||
}
|
||||
float GLFWWindow::scaleY() const { return scaleY_; }
|
||||
|
||||
void GLFWWindow::setCursor(Cursor cursor) {
|
||||
#ifndef __SWITCH__
|
||||
if (!glfwWindow_) return;
|
||||
if (!glfwWindow_)
|
||||
return;
|
||||
|
||||
if (cursor == Cursor::Hidden) {
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
return;
|
||||
}
|
||||
if (cursor == Cursor::Hidden) {
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
|
||||
GLFWcursor* glfwCursor = nullptr;
|
||||
switch (cursor) {
|
||||
case Cursor::Arrow:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
break;
|
||||
case Cursor::IBeam:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
|
||||
break;
|
||||
case Cursor::Crosshair:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR);
|
||||
break;
|
||||
case Cursor::Hand:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
|
||||
break;
|
||||
case Cursor::HResize:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
|
||||
break;
|
||||
case Cursor::VResize:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
|
||||
break;
|
||||
default:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
break;
|
||||
}
|
||||
GLFWcursor *glfwCursor = nullptr;
|
||||
switch (cursor) {
|
||||
case Cursor::Arrow:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
break;
|
||||
case Cursor::IBeam:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
|
||||
break;
|
||||
case Cursor::Crosshair:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_CROSSHAIR_CURSOR);
|
||||
break;
|
||||
case Cursor::Hand:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
|
||||
break;
|
||||
case Cursor::HResize:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
|
||||
break;
|
||||
case Cursor::VResize:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
|
||||
break;
|
||||
default:
|
||||
glfwCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (glfwCursor) {
|
||||
glfwSetCursor(glfwWindow_, glfwCursor);
|
||||
}
|
||||
if (glfwCursor) {
|
||||
glfwSetCursor(glfwWindow_, glfwCursor);
|
||||
}
|
||||
#else
|
||||
(void)cursor;
|
||||
(void)cursor;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLFWWindow::showCursor(bool show) {
|
||||
#ifndef __SWITCH__
|
||||
if (glfwWindow_) {
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, show ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
|
||||
cursorVisible_ = show;
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR,
|
||||
show ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
|
||||
cursorVisible_ = show;
|
||||
}
|
||||
#else
|
||||
(void)show;
|
||||
(void)show;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLFWWindow::lockCursor(bool lock) {
|
||||
#ifndef __SWITCH__
|
||||
if (glfwWindow_) {
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR, lock ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
|
||||
cursorLocked_ = lock;
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
glfwSetInputMode(glfwWindow_, GLFW_CURSOR,
|
||||
lock ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
|
||||
cursorLocked_ = lock;
|
||||
}
|
||||
#else
|
||||
(void)lock;
|
||||
(void)lock;
|
||||
#endif
|
||||
}
|
||||
|
||||
IInput* GLFWWindow::input() const {
|
||||
return input_.get();
|
||||
}
|
||||
IInput *GLFWWindow::input() const { return input_.get(); }
|
||||
|
||||
void GLFWWindow::onResize(ResizeCb cb) {
|
||||
resizeCb_ = cb;
|
||||
}
|
||||
void GLFWWindow::onResize(ResizeCb cb) { resizeCb_ = cb; }
|
||||
|
||||
void GLFWWindow::onClose(CloseCb cb) {
|
||||
closeCb_ = cb;
|
||||
}
|
||||
void GLFWWindow::onClose(CloseCb cb) { closeCb_ = cb; }
|
||||
|
||||
void GLFWWindow::onFocus(FocusCb cb) {
|
||||
focusCb_ = cb;
|
||||
}
|
||||
void GLFWWindow::onFocus(FocusCb cb) { focusCb_ = cb; }
|
||||
|
||||
void* GLFWWindow::native() const {
|
||||
return glfwWindow_;
|
||||
}
|
||||
void *GLFWWindow::native() const { return glfwWindow_; }
|
||||
|
||||
bool GLFWWindow::initGLFW() {
|
||||
static int glfwInitCount = 0;
|
||||
if (glfwInitCount == 0) {
|
||||
if (!glfwInit()) {
|
||||
E2D_LOG_ERROR("Failed to initialize GLFW");
|
||||
return false;
|
||||
}
|
||||
glfwInitCount++;
|
||||
static int glfwInitCount = 0;
|
||||
if (glfwInitCount == 0) {
|
||||
if (!glfwInit()) {
|
||||
E2D_LOG_ERROR("Failed to initialize GLFW");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
glfwInitCount++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLFWWindow::deinitGLFW() {
|
||||
static int glfwInitCount = 1;
|
||||
glfwInitCount--;
|
||||
if (glfwInitCount == 0) {
|
||||
glfwTerminate();
|
||||
}
|
||||
static int glfwInitCount = 1;
|
||||
glfwInitCount--;
|
||||
if (glfwInitCount == 0) {
|
||||
glfwTerminate();
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::updateContentScale() {
|
||||
if (glfwWindow_) {
|
||||
int fbWidth, fbHeight;
|
||||
glfwGetFramebufferSize(glfwWindow_, &fbWidth, &fbHeight);
|
||||
scaleX_ = fbWidth > 0 ? static_cast<float>(fbWidth) / width_ : 1.0f;
|
||||
scaleY_ = fbHeight > 0 ? static_cast<float>(fbHeight) / height_ : 1.0f;
|
||||
}
|
||||
if (glfwWindow_) {
|
||||
int fbWidth, fbHeight;
|
||||
glfwGetFramebufferSize(glfwWindow_, &fbWidth, &fbHeight);
|
||||
scaleX_ = fbWidth > 0 ? static_cast<float>(fbWidth) / width_ : 1.0f;
|
||||
scaleY_ = fbHeight > 0 ? static_cast<float>(fbHeight) / height_ : 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// 静态回调函数
|
||||
void GLFWWindow::framebufferSizeCallback(GLFWwindow* window, int width, int height) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->width_ = width;
|
||||
self->height_ = height;
|
||||
self->updateContentScale();
|
||||
if (self->resizeCb_) {
|
||||
self->resizeCb_(width, height);
|
||||
}
|
||||
void GLFWWindow::framebufferSizeCallback(GLFWwindow *window, int width,
|
||||
int height) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->width_ = width;
|
||||
self->height_ = height;
|
||||
self->updateContentScale();
|
||||
if (self->resizeCb_) {
|
||||
self->resizeCb_(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::windowCloseCallback(GLFWwindow* window) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->shouldClose_ = true;
|
||||
if (self->closeCb_) {
|
||||
self->closeCb_();
|
||||
}
|
||||
void GLFWWindow::windowCloseCallback(GLFWwindow *window) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->shouldClose_ = true;
|
||||
if (self->closeCb_) {
|
||||
self->closeCb_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::windowFocusCallback(GLFWwindow* window, int focused) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->focused_ = (focused == GLFW_TRUE);
|
||||
if (self->focusCb_) {
|
||||
self->focusCb_(self->focused_);
|
||||
}
|
||||
void GLFWWindow::windowFocusCallback(GLFWwindow *window, int focused) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->focused_ = (focused == GLFW_TRUE);
|
||||
if (self->focusCb_) {
|
||||
self->focusCb_(self->focused_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::windowIconifyCallback(GLFWwindow* window, int iconified) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->minimized_ = (iconified == GLFW_TRUE);
|
||||
}
|
||||
void GLFWWindow::windowIconifyCallback(GLFWwindow *window, int iconified) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self) {
|
||||
self->minimized_ = (iconified == GLFW_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::cursorPosCallback(GLFWwindow* window, double xpos, double ypos) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleCursorPosEvent(xpos, ypos);
|
||||
}
|
||||
void GLFWWindow::cursorPosCallback(GLFWwindow *window, double xpos,
|
||||
double ypos) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleCursorPosEvent(xpos, ypos);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleMouseButtonEvent(button, action, mods);
|
||||
}
|
||||
void GLFWWindow::mouseButtonCallback(GLFWwindow *window, int button, int action,
|
||||
int mods) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleMouseButtonEvent(button, action, mods);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::scrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleScrollEvent(xoffset, yoffset);
|
||||
}
|
||||
void GLFWWindow::scrollCallback(GLFWwindow *window, double xoffset,
|
||||
double yoffset) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleScrollEvent(xoffset, yoffset);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
|
||||
GLFWWindow* self = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleKeyEvent(key, scancode, action, mods);
|
||||
}
|
||||
void GLFWWindow::keyCallback(GLFWwindow *window, int key, int scancode,
|
||||
int action, int mods) {
|
||||
GLFWWindow *self =
|
||||
static_cast<GLFWWindow *>(glfwGetWindowUserPointer(window));
|
||||
if (self && self->input_) {
|
||||
self->input_->handleKeyEvent(key, scancode, action, mods);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFWWindow::joystickCallback(int jid, int event) {
|
||||
// 通过全局回调找到对应的窗口实例
|
||||
// 由于 GLFW 的 joystick 回调没有窗口参数,我们需要其他方式处理
|
||||
// 这里简化处理,让输入系统在 update() 中轮询
|
||||
// 通过全局回调找到对应的窗口实例
|
||||
// 由于 GLFW 的 joystick 回调没有窗口参数,我们需要其他方式处理
|
||||
// 这里简化处理,让输入系统在 update() 中轮询
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,451 +1,454 @@
|
|||
#include "sdl2_input.h"
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <cmath>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
SDL2Input::SDL2Input() {
|
||||
keyCurrent_.fill(false);
|
||||
keyPrevious_.fill(false);
|
||||
mouseCurrent_.fill(false);
|
||||
mousePrevious_.fill(false);
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
keyCurrent_.fill(false);
|
||||
keyPrevious_.fill(false);
|
||||
mouseCurrent_.fill(false);
|
||||
mousePrevious_.fill(false);
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
}
|
||||
|
||||
SDL2Input::~SDL2Input() {
|
||||
shutdown();
|
||||
}
|
||||
SDL2Input::~SDL2Input() { shutdown(); }
|
||||
|
||||
void SDL2Input::init() {
|
||||
E2D_LOG_INFO("SDL2Input initialized");
|
||||
|
||||
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
E2D_LOG_WARN("Failed to init gamecontroller subsystem: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
openGamepad();
|
||||
E2D_LOG_INFO("SDL2Input initialized");
|
||||
|
||||
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
E2D_LOG_WARN("Failed to init gamecontroller subsystem: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
openGamepad();
|
||||
}
|
||||
|
||||
void SDL2Input::shutdown() {
|
||||
closeGamepad();
|
||||
E2D_LOG_INFO("SDL2Input shutdown");
|
||||
closeGamepad();
|
||||
E2D_LOG_INFO("SDL2Input shutdown");
|
||||
}
|
||||
|
||||
void SDL2Input::update() {
|
||||
keyPrevious_ = keyCurrent_;
|
||||
mousePrevious_ = mouseCurrent_;
|
||||
gamepadPrevious_ = gamepadCurrent_;
|
||||
|
||||
scrollDelta_ = 0.0f;
|
||||
mouseDelta_ = Vec2{0.0f, 0.0f};
|
||||
|
||||
updateGamepad();
|
||||
keyPrevious_ = keyCurrent_;
|
||||
mousePrevious_ = mouseCurrent_;
|
||||
gamepadPrevious_ = gamepadCurrent_;
|
||||
|
||||
scrollDelta_ = 0.0f;
|
||||
mouseDelta_ = Vec2{0.0f, 0.0f};
|
||||
|
||||
updateGamepad();
|
||||
}
|
||||
|
||||
void SDL2Input::setEventCallback(EventCallback callback) {
|
||||
eventCallback_ = std::move(callback);
|
||||
eventCallback_ = std::move(callback);
|
||||
}
|
||||
|
||||
void SDL2Input::handleSDLEvent(const SDL_Event& event) {
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN: {
|
||||
int key = event.key.keysym.scancode;
|
||||
if (key >= 0 && key < static_cast<int>(Key::Count)) {
|
||||
if (!keyCurrent_[key]) {
|
||||
keyCurrent_[key] = true;
|
||||
|
||||
Event e = Event::createKeyPress(
|
||||
event.key.keysym.sym,
|
||||
event.key.keysym.scancode,
|
||||
event.key.keysym.mod
|
||||
);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_KEYUP: {
|
||||
int key = event.key.keysym.scancode;
|
||||
if (key >= 0 && key < static_cast<int>(Key::Count)) {
|
||||
keyCurrent_[key] = false;
|
||||
|
||||
Event e = Event::createKeyRelease(
|
||||
event.key.keysym.sym,
|
||||
event.key.keysym.scancode,
|
||||
event.key.keysym.mod
|
||||
);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
int btn = event.button.button - 1;
|
||||
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
|
||||
mouseCurrent_[btn] = true;
|
||||
|
||||
Vec2 pos{static_cast<float>(event.button.x),
|
||||
static_cast<float>(event.button.y)};
|
||||
Event e = Event::createMouseButtonPress(btn, 0, pos);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
int btn = event.button.button - 1;
|
||||
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
|
||||
mouseCurrent_[btn] = false;
|
||||
|
||||
Vec2 pos{static_cast<float>(event.button.x),
|
||||
static_cast<float>(event.button.y)};
|
||||
Event e = Event::createMouseButtonRelease(btn, 0, pos);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEMOTION: {
|
||||
Vec2 newPos{static_cast<float>(event.motion.x),
|
||||
static_cast<float>(event.motion.y)};
|
||||
Vec2 delta{static_cast<float>(event.motion.xrel),
|
||||
static_cast<float>(event.motion.yrel)};
|
||||
|
||||
mouseDelta_ = mouseDelta_ + delta;
|
||||
mousePos_ = newPos;
|
||||
|
||||
Event e = Event::createMouseMove(newPos, delta);
|
||||
dispatchEvent(e);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEWHEEL: {
|
||||
Vec2 offset{static_cast<float>(event.wheel.x),
|
||||
static_cast<float>(event.wheel.y)};
|
||||
Vec2 pos = mousePos_;
|
||||
|
||||
scroll_ += event.wheel.y;
|
||||
scrollDelta_ += event.wheel.y;
|
||||
|
||||
Event e = Event::createMouseScroll(offset, pos);
|
||||
dispatchEvent(e);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
E2D_LOG_INFO("Gamepad connected: index {}", event.cdevice.which);
|
||||
openGamepad();
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
if (gamepad_ && event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad_))) {
|
||||
E2D_LOG_INFO("Gamepad disconnected");
|
||||
closeGamepad();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
if (gamepad_) {
|
||||
int btn = event.cbutton.button;
|
||||
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
|
||||
gamepadCurrent_[btn] = true;
|
||||
|
||||
GamepadButtonEvent btnEvent;
|
||||
btnEvent.gamepadId = gamepadIndex_;
|
||||
btnEvent.button = btn;
|
||||
|
||||
Event e;
|
||||
e.type = EventType::GamepadButtonPressed;
|
||||
e.data = btnEvent;
|
||||
dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
if (gamepad_) {
|
||||
int btn = event.cbutton.button;
|
||||
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
|
||||
gamepadCurrent_[btn] = false;
|
||||
|
||||
GamepadButtonEvent btnEvent;
|
||||
btnEvent.gamepadId = gamepadIndex_;
|
||||
btnEvent.button = btn;
|
||||
|
||||
Event e;
|
||||
e.type = EventType::GamepadButtonReleased;
|
||||
e.data = btnEvent;
|
||||
dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
void SDL2Input::handleSDLEvent(const SDL_Event &event) {
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN: {
|
||||
int key = event.key.keysym.scancode;
|
||||
if (key >= 0 && key < static_cast<int>(Key::Count)) {
|
||||
if (!keyCurrent_[key]) {
|
||||
keyCurrent_[key] = true;
|
||||
|
||||
Event e = Event::createKeyPress(event.key.keysym.sym,
|
||||
event.key.keysym.scancode,
|
||||
event.key.keysym.mod);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_KEYUP: {
|
||||
int key = event.key.keysym.scancode;
|
||||
if (key >= 0 && key < static_cast<int>(Key::Count)) {
|
||||
keyCurrent_[key] = false;
|
||||
|
||||
Event e = Event::createKeyRelease(event.key.keysym.sym,
|
||||
event.key.keysym.scancode,
|
||||
event.key.keysym.mod);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
int btn = event.button.button - 1;
|
||||
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
|
||||
mouseCurrent_[btn] = true;
|
||||
|
||||
Vec2 pos{static_cast<float>(event.button.x),
|
||||
static_cast<float>(event.button.y)};
|
||||
Event e = Event::createMouseButtonPress(btn, 0, pos);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
int btn = event.button.button - 1;
|
||||
if (btn >= 0 && btn < static_cast<int>(Mouse::Count)) {
|
||||
mouseCurrent_[btn] = false;
|
||||
|
||||
Vec2 pos{static_cast<float>(event.button.x),
|
||||
static_cast<float>(event.button.y)};
|
||||
Event e = Event::createMouseButtonRelease(btn, 0, pos);
|
||||
dispatchEvent(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEMOTION: {
|
||||
Vec2 newPos{static_cast<float>(event.motion.x),
|
||||
static_cast<float>(event.motion.y)};
|
||||
Vec2 delta{static_cast<float>(event.motion.xrel),
|
||||
static_cast<float>(event.motion.yrel)};
|
||||
|
||||
mouseDelta_ = mouseDelta_ + delta;
|
||||
mousePos_ = newPos;
|
||||
|
||||
Event e = Event::createMouseMove(newPos, delta);
|
||||
dispatchEvent(e);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEWHEEL: {
|
||||
Vec2 offset{static_cast<float>(event.wheel.x),
|
||||
static_cast<float>(event.wheel.y)};
|
||||
Vec2 pos = mousePos_;
|
||||
|
||||
scroll_ += event.wheel.y;
|
||||
scrollDelta_ += event.wheel.y;
|
||||
|
||||
Event e = Event::createMouseScroll(offset, pos);
|
||||
dispatchEvent(e);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
E2D_LOG_INFO("Gamepad connected: index {}", event.cdevice.which);
|
||||
openGamepad();
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
if (gamepad_ &&
|
||||
event.cdevice.which ==
|
||||
SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad_))) {
|
||||
E2D_LOG_INFO("Gamepad disconnected");
|
||||
closeGamepad();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
if (gamepad_) {
|
||||
int btn = event.cbutton.button;
|
||||
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
|
||||
gamepadCurrent_[btn] = true;
|
||||
|
||||
GamepadButtonEvent btnEvent;
|
||||
btnEvent.gamepadId = gamepadIndex_;
|
||||
btnEvent.button = btn;
|
||||
|
||||
Event e;
|
||||
e.type = EventType::GamepadButtonPressed;
|
||||
e.data = btnEvent;
|
||||
dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
if (gamepad_) {
|
||||
int btn = event.cbutton.button;
|
||||
if (btn >= 0 && btn < static_cast<int>(Gamepad::Count)) {
|
||||
gamepadCurrent_[btn] = false;
|
||||
|
||||
GamepadButtonEvent btnEvent;
|
||||
btnEvent.gamepadId = gamepadIndex_;
|
||||
btnEvent.button = btn;
|
||||
|
||||
Event e;
|
||||
e.type = EventType::GamepadButtonReleased;
|
||||
e.data = btnEvent;
|
||||
dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2Input::dispatchEvent(const Event& event) {
|
||||
if (eventCallback_) {
|
||||
eventCallback_(event);
|
||||
}
|
||||
void SDL2Input::dispatchEvent(const Event &event) {
|
||||
if (eventCallback_) {
|
||||
eventCallback_(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL2Input::down(Key key) const {
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL2Input::pressed(Key key) const {
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx] && !keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return keyCurrent_[idx] && !keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL2Input::released(Key key) const {
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return !keyCurrent_[idx] && keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(key);
|
||||
if (idx < keyCurrent_.size()) {
|
||||
return !keyCurrent_[idx] && keyPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL2Input::down(Mouse btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL2Input::pressed(Mouse btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx] && !mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return mouseCurrent_[idx] && !mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL2Input::released(Mouse btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return !mouseCurrent_[idx] && mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < mouseCurrent_.size()) {
|
||||
return !mouseCurrent_[idx] && mousePrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2 SDL2Input::mouse() const {
|
||||
return mousePos_;
|
||||
Vec2 SDL2Input::mouse() const { return mousePos_; }
|
||||
|
||||
Vec2 SDL2Input::mouseDelta() const { return mouseDelta_; }
|
||||
|
||||
float SDL2Input::scroll() const { return scroll_; }
|
||||
|
||||
float SDL2Input::scrollDelta() const { return scrollDelta_; }
|
||||
|
||||
void SDL2Input::setMouse(const Vec2 &pos) {
|
||||
SDL_WarpMouseInWindow(nullptr, static_cast<int>(pos.x),
|
||||
static_cast<int>(pos.y));
|
||||
}
|
||||
|
||||
Vec2 SDL2Input::mouseDelta() const {
|
||||
return mouseDelta_;
|
||||
}
|
||||
|
||||
float SDL2Input::scroll() const {
|
||||
return scroll_;
|
||||
}
|
||||
|
||||
float SDL2Input::scrollDelta() const {
|
||||
return scrollDelta_;
|
||||
}
|
||||
|
||||
void SDL2Input::setMouse(const Vec2& pos) {
|
||||
SDL_WarpMouseInWindow(nullptr, static_cast<int>(pos.x), static_cast<int>(pos.y));
|
||||
}
|
||||
|
||||
bool SDL2Input::gamepad() const {
|
||||
return gamepad_ != nullptr;
|
||||
}
|
||||
bool SDL2Input::gamepad() const { return gamepad_ != nullptr; }
|
||||
|
||||
bool SDL2Input::down(Gamepad btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL2Input::pressed(Gamepad btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx] && !gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return gamepadCurrent_[idx] && !gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDL2Input::released(Gamepad btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return !gamepadCurrent_[idx] && gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx < gamepadCurrent_.size()) {
|
||||
return !gamepadCurrent_[idx] && gamepadPrevious_[idx];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2 SDL2Input::leftStick() const {
|
||||
return leftStick_;
|
||||
}
|
||||
Vec2 SDL2Input::leftStick() const { return leftStick_; }
|
||||
|
||||
Vec2 SDL2Input::rightStick() const {
|
||||
return rightStick_;
|
||||
}
|
||||
Vec2 SDL2Input::rightStick() const { return rightStick_; }
|
||||
|
||||
float SDL2Input::leftTrigger() const {
|
||||
return leftTrigger_;
|
||||
}
|
||||
float SDL2Input::leftTrigger() const { return leftTrigger_; }
|
||||
|
||||
float SDL2Input::rightTrigger() const {
|
||||
return rightTrigger_;
|
||||
}
|
||||
float SDL2Input::rightTrigger() const { return rightTrigger_; }
|
||||
|
||||
void SDL2Input::vibrate(float left, float right) {
|
||||
if (gamepad_) {
|
||||
Uint16 lowFreq = static_cast<Uint16>(left * 65535.0f);
|
||||
Uint16 highFreq = static_cast<Uint16>(right * 65535.0f);
|
||||
SDL_GameControllerRumble(gamepad_, lowFreq, highFreq, 500);
|
||||
}
|
||||
if (gamepad_) {
|
||||
Uint16 lowFreq = static_cast<Uint16>(left * 65535.0f);
|
||||
Uint16 highFreq = static_cast<Uint16>(right * 65535.0f);
|
||||
SDL_GameControllerRumble(gamepad_, lowFreq, highFreq, 500);
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL2Input::touching() const {
|
||||
return false;
|
||||
}
|
||||
bool SDL2Input::touching() const { return false; }
|
||||
|
||||
int SDL2Input::touchCount() const {
|
||||
return 0;
|
||||
}
|
||||
int SDL2Input::touchCount() const { return 0; }
|
||||
|
||||
Vec2 SDL2Input::touch(int index) const {
|
||||
(void)index;
|
||||
return Vec2{0.0f, 0.0f};
|
||||
(void)index;
|
||||
return Vec2{0.0f, 0.0f};
|
||||
}
|
||||
|
||||
TouchPoint SDL2Input::touchPoint(int index) const {
|
||||
(void)index;
|
||||
return TouchPoint{};
|
||||
(void)index;
|
||||
return TouchPoint{};
|
||||
}
|
||||
|
||||
void SDL2Input::updateKeyboard() {
|
||||
}
|
||||
void SDL2Input::updateKeyboard() {}
|
||||
|
||||
void SDL2Input::updateMouse() {
|
||||
int x = 0, y = 0;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
|
||||
int x = 0, y = 0;
|
||||
SDL_GetMouseState(&x, &y);
|
||||
mousePos_ = Vec2{static_cast<float>(x), static_cast<float>(y)};
|
||||
}
|
||||
|
||||
void SDL2Input::updateGamepad() {
|
||||
if (!gamepad_) {
|
||||
return;
|
||||
if (!gamepad_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto applyDeadzone = [this](float value) -> float {
|
||||
if (std::abs(value) < deadzone_) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
auto applyDeadzone = [this](float value) -> float {
|
||||
if (std::abs(value) < deadzone_) {
|
||||
return 0.0f;
|
||||
}
|
||||
float sign = value >= 0.0f ? 1.0f : -1.0f;
|
||||
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
|
||||
};
|
||||
|
||||
int lx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
int ly = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
int rx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
int ry = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
|
||||
leftStick_.x = applyDeadzone(lx / 32767.0f);
|
||||
leftStick_.y = applyDeadzone(ly / 32767.0f);
|
||||
rightStick_.x = applyDeadzone(rx / 32767.0f);
|
||||
rightStick_.y = applyDeadzone(ry / 32767.0f);
|
||||
|
||||
int lt = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||
int rt = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||
|
||||
leftTrigger_ = lt / 32767.0f;
|
||||
rightTrigger_ = rt / 32767.0f;
|
||||
float sign = value >= 0.0f ? 1.0f : -1.0f;
|
||||
return sign * (std::abs(value) - deadzone_) / (1.0f - deadzone_);
|
||||
};
|
||||
|
||||
int lx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
int ly = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
int rx = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
int ry = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
|
||||
leftStick_.x = applyDeadzone(lx / 32767.0f);
|
||||
leftStick_.y = applyDeadzone(ly / 32767.0f);
|
||||
rightStick_.x = applyDeadzone(rx / 32767.0f);
|
||||
rightStick_.y = applyDeadzone(ry / 32767.0f);
|
||||
|
||||
int lt = SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
|
||||
int rt =
|
||||
SDL_GameControllerGetAxis(gamepad_, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
|
||||
|
||||
leftTrigger_ = lt / 32767.0f;
|
||||
rightTrigger_ = rt / 32767.0f;
|
||||
}
|
||||
|
||||
void SDL2Input::openGamepad() {
|
||||
int numJoysticks = SDL_NumJoysticks();
|
||||
for (int i = 0; i < numJoysticks; ++i) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
gamepad_ = SDL_GameControllerOpen(i);
|
||||
if (gamepad_) {
|
||||
gamepadIndex_ = i;
|
||||
E2D_LOG_INFO("Gamepad opened: {}", SDL_GameControllerName(gamepad_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
int numJoysticks = SDL_NumJoysticks();
|
||||
for (int i = 0; i < numJoysticks; ++i) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
gamepad_ = SDL_GameControllerOpen(i);
|
||||
if (gamepad_) {
|
||||
gamepadIndex_ = i;
|
||||
E2D_LOG_INFO("Gamepad opened: {}", SDL_GameControllerName(gamepad_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2Input::closeGamepad() {
|
||||
if (gamepad_) {
|
||||
SDL_GameControllerClose(gamepad_);
|
||||
gamepad_ = nullptr;
|
||||
gamepadIndex_ = -1;
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
}
|
||||
if (gamepad_) {
|
||||
SDL_GameControllerClose(gamepad_);
|
||||
gamepad_ = nullptr;
|
||||
gamepadIndex_ = -1;
|
||||
gamepadCurrent_.fill(false);
|
||||
gamepadPrevious_.fill(false);
|
||||
}
|
||||
}
|
||||
|
||||
int SDL2Input::keyToSDL(Key key) {
|
||||
return static_cast<int>(key);
|
||||
}
|
||||
int SDL2Input::keyToSDL(Key key) { return static_cast<int>(key); }
|
||||
|
||||
int SDL2Input::mouseToSDL(Mouse btn) {
|
||||
switch (btn) {
|
||||
case Mouse::Left: return SDL_BUTTON_LEFT;
|
||||
case Mouse::Middle: return SDL_BUTTON_MIDDLE;
|
||||
case Mouse::Right: return SDL_BUTTON_RIGHT;
|
||||
case Mouse::X1: return SDL_BUTTON_X1;
|
||||
case Mouse::X2: return SDL_BUTTON_X2;
|
||||
default: return 0;
|
||||
}
|
||||
switch (btn) {
|
||||
case Mouse::Left:
|
||||
return SDL_BUTTON_LEFT;
|
||||
case Mouse::Middle:
|
||||
return SDL_BUTTON_MIDDLE;
|
||||
case Mouse::Right:
|
||||
return SDL_BUTTON_RIGHT;
|
||||
case Mouse::X1:
|
||||
return SDL_BUTTON_X1;
|
||||
case Mouse::X2:
|
||||
return SDL_BUTTON_X2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int SDL2Input::gamepadToSDL(Gamepad btn) {
|
||||
switch (btn) {
|
||||
case Gamepad::A: return SDL_CONTROLLER_BUTTON_A;
|
||||
case Gamepad::B: return SDL_CONTROLLER_BUTTON_B;
|
||||
case Gamepad::X: return SDL_CONTROLLER_BUTTON_X;
|
||||
case Gamepad::Y: return SDL_CONTROLLER_BUTTON_Y;
|
||||
case Gamepad::Back: return SDL_CONTROLLER_BUTTON_BACK;
|
||||
case Gamepad::Start: return SDL_CONTROLLER_BUTTON_START;
|
||||
case Gamepad::LStick: return SDL_CONTROLLER_BUTTON_LEFTSTICK;
|
||||
case Gamepad::RStick: return SDL_CONTROLLER_BUTTON_RIGHTSTICK;
|
||||
case Gamepad::LB: return SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
|
||||
case Gamepad::RB: return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
|
||||
case Gamepad::DUp: return SDL_CONTROLLER_BUTTON_DPAD_UP;
|
||||
case Gamepad::DDown: return SDL_CONTROLLER_BUTTON_DPAD_DOWN;
|
||||
case Gamepad::DLeft: return SDL_CONTROLLER_BUTTON_DPAD_LEFT;
|
||||
case Gamepad::DRight: return SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
|
||||
case Gamepad::Guide: return SDL_CONTROLLER_BUTTON_GUIDE;
|
||||
default: return 0;
|
||||
}
|
||||
switch (btn) {
|
||||
case Gamepad::A:
|
||||
return SDL_CONTROLLER_BUTTON_A;
|
||||
case Gamepad::B:
|
||||
return SDL_CONTROLLER_BUTTON_B;
|
||||
case Gamepad::X:
|
||||
return SDL_CONTROLLER_BUTTON_X;
|
||||
case Gamepad::Y:
|
||||
return SDL_CONTROLLER_BUTTON_Y;
|
||||
case Gamepad::Back:
|
||||
return SDL_CONTROLLER_BUTTON_BACK;
|
||||
case Gamepad::Start:
|
||||
return SDL_CONTROLLER_BUTTON_START;
|
||||
case Gamepad::LStick:
|
||||
return SDL_CONTROLLER_BUTTON_LEFTSTICK;
|
||||
case Gamepad::RStick:
|
||||
return SDL_CONTROLLER_BUTTON_RIGHTSTICK;
|
||||
case Gamepad::LB:
|
||||
return SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
|
||||
case Gamepad::RB:
|
||||
return SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
|
||||
case Gamepad::DUp:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_UP;
|
||||
case Gamepad::DDown:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_DOWN;
|
||||
case Gamepad::DLeft:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_LEFT;
|
||||
case Gamepad::DRight:
|
||||
return SDL_CONTROLLER_BUTTON_DPAD_RIGHT;
|
||||
case Gamepad::Guide:
|
||||
return SDL_CONTROLLER_BUTTON_GUIDE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Key SDL2Input::sdlToKey(int sdlKey) {
|
||||
if (sdlKey >= 0 && sdlKey < static_cast<int>(Key::Count)) {
|
||||
return static_cast<Key>(sdlKey);
|
||||
}
|
||||
return Key::None;
|
||||
if (sdlKey >= 0 && sdlKey < static_cast<int>(Key::Count)) {
|
||||
return static_cast<Key>(sdlKey);
|
||||
}
|
||||
return Key::None;
|
||||
}
|
||||
|
||||
Mouse SDL2Input::sdlToMouse(int sdlButton) {
|
||||
switch (sdlButton) {
|
||||
case SDL_BUTTON_LEFT: return Mouse::Left;
|
||||
case SDL_BUTTON_MIDDLE: return Mouse::Middle;
|
||||
case SDL_BUTTON_RIGHT: return Mouse::Right;
|
||||
case SDL_BUTTON_X1: return Mouse::X1;
|
||||
case SDL_BUTTON_X2: return Mouse::X2;
|
||||
default: return Mouse::Count;
|
||||
}
|
||||
switch (sdlButton) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
return Mouse::Left;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
return Mouse::Middle;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
return Mouse::Right;
|
||||
case SDL_BUTTON_X1:
|
||||
return Mouse::X1;
|
||||
case SDL_BUTTON_X2:
|
||||
return Mouse::X2;
|
||||
default:
|
||||
return Mouse::Count;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,400 +1,373 @@
|
|||
#include "sdl2_window.h"
|
||||
#include "sdl2_input.h"
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <glad/glad.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
SDL2Window::SDL2Window() {
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
sdlCursors_[i] = nullptr;
|
||||
}
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
sdlCursors_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SDL2Window::~SDL2Window() {
|
||||
destroy();
|
||||
}
|
||||
SDL2Window::~SDL2Window() { destroy(); }
|
||||
|
||||
bool SDL2Window::create(const std::string& title, int width, int height, bool vsync) {
|
||||
if (!initSDL()) {
|
||||
return false;
|
||||
}
|
||||
bool SDL2Window::create(const std::string &title, int width, int height,
|
||||
bool vsync) {
|
||||
if (!initSDL()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
|
||||
|
||||
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
|
||||
|
||||
#ifdef __SWITCH__
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
#endif
|
||||
|
||||
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_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);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
sdlWindow_ = SDL_CreateWindow(
|
||||
title.c_str(),
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
width, height,
|
||||
flags
|
||||
);
|
||||
sdlWindow_ = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, width, height, flags);
|
||||
|
||||
if (!sdlWindow_) {
|
||||
E2D_LOG_ERROR("Failed to create SDL window: {}", SDL_GetError());
|
||||
deinitSDL();
|
||||
return false;
|
||||
}
|
||||
if (!sdlWindow_) {
|
||||
E2D_LOG_ERROR("Failed to create SDL window: {}", SDL_GetError());
|
||||
deinitSDL();
|
||||
return false;
|
||||
}
|
||||
|
||||
glContext_ = SDL_GL_CreateContext(sdlWindow_);
|
||||
if (!glContext_) {
|
||||
E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError());
|
||||
SDL_DestroyWindow(sdlWindow_);
|
||||
sdlWindow_ = nullptr;
|
||||
deinitSDL();
|
||||
return false;
|
||||
}
|
||||
glContext_ = SDL_GL_CreateContext(sdlWindow_);
|
||||
if (!glContext_) {
|
||||
E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError());
|
||||
SDL_DestroyWindow(sdlWindow_);
|
||||
sdlWindow_ = nullptr;
|
||||
deinitSDL();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
||||
E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
|
||||
SDL_GL_DeleteContext(glContext_);
|
||||
glContext_ = nullptr;
|
||||
SDL_DestroyWindow(sdlWindow_);
|
||||
sdlWindow_ = nullptr;
|
||||
deinitSDL();
|
||||
return false;
|
||||
}
|
||||
if (!gladLoadGLES2Loader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
||||
E2D_LOG_ERROR("Failed to initialize GLAD GLES2");
|
||||
SDL_GL_DeleteContext(glContext_);
|
||||
glContext_ = nullptr;
|
||||
SDL_DestroyWindow(sdlWindow_);
|
||||
sdlWindow_ = nullptr;
|
||||
deinitSDL();
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
||||
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
||||
|
||||
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
|
||||
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||
vsync_ = vsync;
|
||||
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
|
||||
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||
vsync_ = vsync;
|
||||
|
||||
#ifndef __SWITCH__
|
||||
initCursors();
|
||||
initCursors();
|
||||
#endif
|
||||
updateContentScale();
|
||||
updateContentScale();
|
||||
|
||||
input_ = makeUnique<SDL2Input>();
|
||||
input_->init();
|
||||
input_ = makeUnique<SDL2Input>();
|
||||
input_->init();
|
||||
|
||||
E2D_LOG_INFO("SDL2 window created: {}x{}", width_, height_);
|
||||
E2D_LOG_INFO(" Platform: OpenGL ES 3.2");
|
||||
return true;
|
||||
E2D_LOG_INFO("SDL2 window created: {}x{}", width_, height_);
|
||||
E2D_LOG_INFO(" Platform: OpenGL ES 3.2");
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL2Window::destroy() {
|
||||
if (input_) {
|
||||
input_->shutdown();
|
||||
input_.reset();
|
||||
}
|
||||
if (input_) {
|
||||
input_->shutdown();
|
||||
input_.reset();
|
||||
}
|
||||
|
||||
#ifndef __SWITCH__
|
||||
deinitCursors();
|
||||
deinitCursors();
|
||||
#endif
|
||||
|
||||
if (glContext_) {
|
||||
SDL_GL_DeleteContext(glContext_);
|
||||
glContext_ = nullptr;
|
||||
}
|
||||
if (glContext_) {
|
||||
SDL_GL_DeleteContext(glContext_);
|
||||
glContext_ = nullptr;
|
||||
}
|
||||
|
||||
if (sdlWindow_) {
|
||||
SDL_DestroyWindow(sdlWindow_);
|
||||
sdlWindow_ = nullptr;
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_DestroyWindow(sdlWindow_);
|
||||
sdlWindow_ = nullptr;
|
||||
}
|
||||
|
||||
deinitSDL();
|
||||
deinitSDL();
|
||||
}
|
||||
|
||||
void SDL2Window::poll() {
|
||||
if (!sdlWindow_) return;
|
||||
if (!sdlWindow_)
|
||||
return;
|
||||
|
||||
if (input_) {
|
||||
input_->update();
|
||||
}
|
||||
if (input_) {
|
||||
input_->update();
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
handleEvent(event);
|
||||
}
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2Window::swap() {
|
||||
if (sdlWindow_ && glContext_) {
|
||||
SDL_GL_SwapWindow(sdlWindow_);
|
||||
}
|
||||
if (sdlWindow_ && glContext_) {
|
||||
SDL_GL_SwapWindow(sdlWindow_);
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL2Window::shouldClose() const {
|
||||
return shouldClose_;
|
||||
}
|
||||
bool SDL2Window::shouldClose() const { return shouldClose_; }
|
||||
|
||||
void SDL2Window::close() {
|
||||
shouldClose_ = true;
|
||||
}
|
||||
void SDL2Window::close() { shouldClose_ = true; }
|
||||
|
||||
void SDL2Window::setTitle(const std::string& title) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowTitle(sdlWindow_, title.c_str());
|
||||
}
|
||||
void SDL2Window::setTitle(const std::string &title) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowTitle(sdlWindow_, title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2Window::setSize(int w, int h) {
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowSize(sdlWindow_, w, h);
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowSize(sdlWindow_, w, h);
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2Window::setPos(int x, int y) {
|
||||
#ifndef __SWITCH__
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowPosition(sdlWindow_, x, y);
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowPosition(sdlWindow_, x, y);
|
||||
}
|
||||
#else
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)x;
|
||||
(void)y;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL2Window::setFullscreen(bool fs) {
|
||||
#ifndef __SWITCH__
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowFullscreen(sdlWindow_, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
fullscreen_ = fs;
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_SetWindowFullscreen(sdlWindow_, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
fullscreen_ = fs;
|
||||
}
|
||||
#else
|
||||
(void)fs;
|
||||
(void)fs;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL2Window::setVSync(bool vsync) {
|
||||
if (glContext_) {
|
||||
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
||||
vsync_ = vsync;
|
||||
}
|
||||
if (glContext_) {
|
||||
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
||||
vsync_ = vsync;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2Window::setVisible(bool visible) {
|
||||
#ifndef __SWITCH__
|
||||
if (sdlWindow_) {
|
||||
if (visible) {
|
||||
SDL_ShowWindow(sdlWindow_);
|
||||
} else {
|
||||
SDL_HideWindow(sdlWindow_);
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
if (visible) {
|
||||
SDL_ShowWindow(sdlWindow_);
|
||||
} else {
|
||||
SDL_HideWindow(sdlWindow_);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)visible;
|
||||
(void)visible;
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL2Window::width() const {
|
||||
return width_;
|
||||
}
|
||||
int SDL2Window::width() const { return width_; }
|
||||
|
||||
int SDL2Window::height() const {
|
||||
return height_;
|
||||
}
|
||||
int SDL2Window::height() const { return height_; }
|
||||
|
||||
Size SDL2Window::size() const {
|
||||
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||
}
|
||||
|
||||
Vec2 SDL2Window::pos() const {
|
||||
int x = 0, y = 0;
|
||||
int x = 0, y = 0;
|
||||
#ifndef __SWITCH__
|
||||
if (sdlWindow_) {
|
||||
SDL_GetWindowPosition(sdlWindow_, &x, &y);
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_GetWindowPosition(sdlWindow_, &x, &y);
|
||||
}
|
||||
#endif
|
||||
return Vec2(static_cast<float>(x), static_cast<float>(y));
|
||||
return Vec2(static_cast<float>(x), static_cast<float>(y));
|
||||
}
|
||||
|
||||
bool SDL2Window::fullscreen() const {
|
||||
return fullscreen_;
|
||||
}
|
||||
bool SDL2Window::fullscreen() const { return fullscreen_; }
|
||||
|
||||
bool SDL2Window::vsync() const {
|
||||
return vsync_;
|
||||
}
|
||||
bool SDL2Window::vsync() const { return vsync_; }
|
||||
|
||||
bool SDL2Window::focused() const {
|
||||
return focused_;
|
||||
}
|
||||
bool SDL2Window::focused() const { return focused_; }
|
||||
|
||||
bool SDL2Window::minimized() const {
|
||||
return minimized_;
|
||||
}
|
||||
bool SDL2Window::minimized() const { return minimized_; }
|
||||
|
||||
float SDL2Window::scaleX() const {
|
||||
return scaleX_;
|
||||
}
|
||||
float SDL2Window::scaleX() const { return scaleX_; }
|
||||
|
||||
float SDL2Window::scaleY() const {
|
||||
return scaleY_;
|
||||
}
|
||||
float SDL2Window::scaleY() const { return scaleY_; }
|
||||
|
||||
void SDL2Window::setCursor(Cursor cursor) {
|
||||
#ifndef __SWITCH__
|
||||
if (cursor == Cursor::Hidden) {
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
return;
|
||||
}
|
||||
if (cursor == Cursor::Hidden) {
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
|
||||
int idx = static_cast<int>(cursor);
|
||||
if (idx >= 0 && idx < 7 && sdlCursors_[idx]) {
|
||||
SDL_SetCursor(sdlCursors_[idx]);
|
||||
currentCursor_ = idx;
|
||||
}
|
||||
int idx = static_cast<int>(cursor);
|
||||
if (idx >= 0 && idx < 7 && sdlCursors_[idx]) {
|
||||
SDL_SetCursor(sdlCursors_[idx]);
|
||||
currentCursor_ = idx;
|
||||
}
|
||||
#else
|
||||
(void)cursor;
|
||||
(void)cursor;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL2Window::showCursor(bool show) {
|
||||
#ifndef __SWITCH__
|
||||
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
|
||||
cursorVisible_ = show;
|
||||
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
|
||||
cursorVisible_ = show;
|
||||
#else
|
||||
(void)show;
|
||||
(void)show;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL2Window::lockCursor(bool lock) {
|
||||
#ifndef __SWITCH__
|
||||
if (sdlWindow_) {
|
||||
SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE);
|
||||
cursorLocked_ = lock;
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_SetRelativeMouseMode(lock ? SDL_TRUE : SDL_FALSE);
|
||||
cursorLocked_ = lock;
|
||||
}
|
||||
#else
|
||||
(void)lock;
|
||||
(void)lock;
|
||||
#endif
|
||||
}
|
||||
|
||||
IInput* SDL2Window::input() const {
|
||||
return input_.get();
|
||||
}
|
||||
IInput *SDL2Window::input() const { return input_.get(); }
|
||||
|
||||
void SDL2Window::onResize(ResizeCb cb) {
|
||||
resizeCb_ = cb;
|
||||
}
|
||||
void SDL2Window::onResize(ResizeCb cb) { resizeCb_ = cb; }
|
||||
|
||||
void SDL2Window::onClose(CloseCb cb) {
|
||||
closeCb_ = cb;
|
||||
}
|
||||
void SDL2Window::onClose(CloseCb cb) { closeCb_ = cb; }
|
||||
|
||||
void SDL2Window::onFocus(FocusCb cb) {
|
||||
focusCb_ = cb;
|
||||
}
|
||||
void SDL2Window::onFocus(FocusCb cb) { focusCb_ = cb; }
|
||||
|
||||
void* SDL2Window::native() const {
|
||||
return sdlWindow_;
|
||||
}
|
||||
void *SDL2Window::native() const { return sdlWindow_; }
|
||||
|
||||
bool SDL2Window::initSDL() {
|
||||
static int sdlInitCount = 0;
|
||||
if (sdlInitCount == 0) {
|
||||
Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER;
|
||||
if (SDL_Init(initFlags) != 0) {
|
||||
E2D_LOG_ERROR("Failed to initialize SDL: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
sdlInitCount++;
|
||||
static int sdlInitCount = 0;
|
||||
if (sdlInitCount == 0) {
|
||||
Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER;
|
||||
if (SDL_Init(initFlags) != 0) {
|
||||
E2D_LOG_ERROR("Failed to initialize SDL: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
sdlInitCount++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL2Window::deinitSDL() {
|
||||
static int sdlInitCount = 1;
|
||||
sdlInitCount--;
|
||||
if (sdlInitCount == 0) {
|
||||
SDL_Quit();
|
||||
}
|
||||
static int sdlInitCount = 1;
|
||||
sdlInitCount--;
|
||||
if (sdlInitCount == 0) {
|
||||
SDL_Quit();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __SWITCH__
|
||||
void SDL2Window::initCursors() {
|
||||
sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||
sdlCursors_[2] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
|
||||
sdlCursors_[3] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
sdlCursors_[4] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
|
||||
sdlCursors_[5] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
|
||||
sdlCursors_[6] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||
sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||
sdlCursors_[2] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
|
||||
sdlCursors_[3] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
sdlCursors_[4] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
|
||||
sdlCursors_[5] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
|
||||
sdlCursors_[6] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||
}
|
||||
|
||||
void SDL2Window::deinitCursors() {
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
if (sdlCursors_[i]) {
|
||||
SDL_FreeCursor(sdlCursors_[i]);
|
||||
sdlCursors_[i] = nullptr;
|
||||
}
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
if (sdlCursors_[i]) {
|
||||
SDL_FreeCursor(sdlCursors_[i]);
|
||||
sdlCursors_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SDL2Window::updateContentScale() {
|
||||
if (sdlWindow_) {
|
||||
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
|
||||
int dw, dh;
|
||||
SDL_GL_GetDrawableSize(sdlWindow_, &dw, &dh);
|
||||
scaleX_ = dw > 0 ? static_cast<float>(dw) / width_ : 1.0f;
|
||||
scaleY_ = dh > 0 ? static_cast<float>(dh) / height_ : 1.0f;
|
||||
}
|
||||
if (sdlWindow_) {
|
||||
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
|
||||
int dw, dh;
|
||||
SDL_GL_GetDrawableSize(sdlWindow_, &dw, &dh);
|
||||
scaleX_ = dw > 0 ? static_cast<float>(dw) / width_ : 1.0f;
|
||||
scaleY_ = dh > 0 ? static_cast<float>(dh) / height_ : 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL2Window::handleEvent(const SDL_Event& event) {
|
||||
if (input_) {
|
||||
input_->handleSDLEvent(event);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
shouldClose_ = true;
|
||||
if (closeCb_) closeCb_();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
width_ = event.window.data1;
|
||||
height_ = event.window.data2;
|
||||
updateContentScale();
|
||||
if (resizeCb_) resizeCb_(width_, height_);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
focused_ = true;
|
||||
if (focusCb_) focusCb_(true);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
focused_ = false;
|
||||
if (focusCb_) focusCb_(false);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
minimized_ = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
minimized_ = false;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
shouldClose_ = true;
|
||||
if (closeCb_) closeCb_();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
void SDL2Window::handleEvent(const SDL_Event &event) {
|
||||
if (input_) {
|
||||
input_->handleSDLEvent(event);
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
shouldClose_ = true;
|
||||
if (closeCb_)
|
||||
closeCb_();
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
width_ = event.window.data1;
|
||||
height_ = event.window.data2;
|
||||
updateContentScale();
|
||||
if (resizeCb_)
|
||||
resizeCb_(width_, height_);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
focused_ = true;
|
||||
if (focusCb_)
|
||||
focusCb_(true);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
focused_ = false;
|
||||
if (focusCb_)
|
||||
focusCb_(false);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
minimized_ = true;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
minimized_ = false;
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
shouldClose_ = true;
|
||||
if (closeCb_)
|
||||
closeCb_();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/platform/backend_factory.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/platform/backend_factory.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
|
|
@ -16,61 +16,63 @@ void initSDL2Backend();
|
|||
#elif defined(E2D_BACKEND_GLFW)
|
||||
void initGLFWBackend();
|
||||
#endif
|
||||
}
|
||||
} // namespace platform
|
||||
|
||||
WindowModule::WindowModule(std::function<void(WindowCfg&)> configFn) {
|
||||
configFn(cfg_);
|
||||
WindowModule::WindowModule(std::function<void(WindowCfg &)> configFn) {
|
||||
configFn(cfg_);
|
||||
}
|
||||
|
||||
WindowModule::~WindowModule() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowModule::init() {
|
||||
if (initialized_) return true;
|
||||
if (initialized_)
|
||||
return true;
|
||||
|
||||
// 初始化后端(注册到工厂)
|
||||
// 初始化后端(注册到工厂)
|
||||
#if defined(E2D_BACKEND_SDL2)
|
||||
platform::initSDL2Backend();
|
||||
platform::initSDL2Backend();
|
||||
#elif defined(E2D_BACKEND_GLFW)
|
||||
platform::initGLFWBackend();
|
||||
platform::initGLFWBackend();
|
||||
#else
|
||||
#error "No window backend defined"
|
||||
#error "No window backend defined"
|
||||
#endif
|
||||
|
||||
E2D_LOG_INFO("Window backend initialized");
|
||||
E2D_LOG_INFO("Window backend initialized");
|
||||
|
||||
E2D_LOG_INFO("Creating window with size {}x{}", cfg_.w, cfg_.h);
|
||||
E2D_LOG_INFO("Creating window with size {}x{}", cfg_.w, cfg_.h);
|
||||
|
||||
// 创建窗口(使用配置的后端)
|
||||
win_ = platform::BackendFactory::createWindow(cfg_.backend);
|
||||
if (!win_) {
|
||||
E2D_LOG_ERROR("Failed to create window backend: {}", cfg_.backend);
|
||||
return false;
|
||||
}
|
||||
// 创建窗口(使用配置的后端)
|
||||
win_ = platform::BackendFactory::createWindow(cfg_.backend);
|
||||
if (!win_) {
|
||||
E2D_LOG_ERROR("Failed to create window backend: {}", cfg_.backend);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!win_->create(cfg_.title, cfg_.w, cfg_.h, cfg_.vsync)) {
|
||||
E2D_LOG_ERROR("Failed to create window");
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
if (!win_->create(cfg_.title, cfg_.w, cfg_.h, cfg_.vsync)) {
|
||||
E2D_LOG_ERROR("Failed to create window");
|
||||
shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_INFO("Window created successfully");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
E2D_LOG_INFO("Window created successfully");
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowModule::shutdown() {
|
||||
if (!initialized_) return;
|
||||
|
||||
if (win_) {
|
||||
win_->destroy();
|
||||
win_.reset();
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
if (win_) {
|
||||
win_->destroy();
|
||||
win_.reset();
|
||||
}
|
||||
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -521,13 +523,13 @@ void Node::onRender(RenderBackend &renderer) {
|
|||
return;
|
||||
|
||||
renderer.pushTransform(getLocalTransform());
|
||||
|
||||
|
||||
onDraw(renderer);
|
||||
|
||||
for (auto &child : children_) {
|
||||
child->onRender(renderer);
|
||||
}
|
||||
|
||||
|
||||
renderer.popTransform();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <algorithm>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/graphics/core/render_command.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
|
|
@ -10,7 +11,8 @@
|
|||
#include <extra2d/scene/transition_scale_scene.h>
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <extra2d/scene/transition_slide_scene.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -748,7 +750,8 @@ void SceneManager::finishTransition() {
|
|||
*/
|
||||
void SceneManager::dispatchPointerEvents(Scene &scene) {
|
||||
auto *input = Application::get().input();
|
||||
if (!input) return;
|
||||
if (!input)
|
||||
return;
|
||||
Vec2 screenPos = input->mouse();
|
||||
|
||||
Vec2 worldPos = screenPos;
|
||||
|
|
@ -832,7 +835,6 @@ void SceneManager::dispatchPointerEvents(Scene &scene) {
|
|||
lastPointerWorld_ = worldPos;
|
||||
}
|
||||
|
||||
void SceneManager::doSceneSwitch() {
|
||||
}
|
||||
void SceneManager::doSceneSwitch() {}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#include <extra2d/scene/transition_fade_scene.h>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <algorithm>
|
||||
#include <extra2d/scene/transition_fade_scene.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/graphics/core/render_backend.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue