Compare commits
No commits in common. "092a28c30eb19e17a8aed7433ac53696750d1887" and "c94e43089bbf97ee9ebcc8c9fe587635b25a338c" have entirely different histories.
092a28c30e
...
c94e43089b
216
AGENTS.md
216
AGENTS.md
|
|
@ -1,216 +0,0 @@
|
||||||
# Frostbite2D Agent Guide
|
|
||||||
|
|
||||||
This guide helps agentic coding agents work effectively with the Frostbite2D 2D game engine codebase.
|
|
||||||
|
|
||||||
## Build Commands
|
|
||||||
|
|
||||||
### Basic Build
|
|
||||||
```bash
|
|
||||||
xmake build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clean Build
|
|
||||||
```bash
|
|
||||||
xmake clean
|
|
||||||
xmake build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debug Build
|
|
||||||
```bash
|
|
||||||
xmake build -m debug
|
|
||||||
```
|
|
||||||
|
|
||||||
### Release Build
|
|
||||||
```bash
|
|
||||||
xmake build -m release
|
|
||||||
```
|
|
||||||
|
|
||||||
### Platform-Specific Build
|
|
||||||
```bash
|
|
||||||
xmake build -p windows
|
|
||||||
xmake build -p linux
|
|
||||||
xmake build -p switch
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
```bash
|
|
||||||
xmake f -c # Show current configuration
|
|
||||||
xmake f # Interactive configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
Currently no automated test framework is configured. Manual testing is performed by running the built binary.
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
```
|
|
||||||
Frostbite2D/
|
|
||||||
├── include/frostbite2D/ # Public headers
|
|
||||||
│ ├── core/ # Core engine classes
|
|
||||||
│ ├── types/ # Type definitions
|
|
||||||
│ ├── utils/ # Utility classes
|
|
||||||
│ └── platform/ # Platform-specific code
|
|
||||||
└── src/frostbite2D/ # Implementation files
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Style Guidelines
|
|
||||||
|
|
||||||
### Naming Conventions
|
|
||||||
- **Classes/Structs**: PascalCase (`Application`, `Window`, `Vec2`)
|
|
||||||
- **Methods/Functions**: camelCase (`readTextFile`, `getFileName`)
|
|
||||||
- **Member Variables**: camelCase with trailing underscore (`width_`, `height_`, `running_`)
|
|
||||||
- **Constants**: UPPER_CASE (`PI_F`, `DEG_TO_RAD`) or PascalCase static methods (`Vec2::Zero()`)
|
|
||||||
- **Namespaces**: lowercase (`frostbite2D`, `math`)
|
|
||||||
- **Template Parameters**: PascalCase (`T`, `Args`, `Sig`)
|
|
||||||
- **Type Aliases**: using keyword with PascalCase (`using int8 = std::int8_t;`)
|
|
||||||
|
|
||||||
### Imports and Includes
|
|
||||||
- **Engine headers**: Use path relative to frostbite2D root
|
|
||||||
```cpp
|
|
||||||
#include <frostbite2D/core/application.h>
|
|
||||||
#include <frostbite2D/utils/asset.h>
|
|
||||||
```
|
|
||||||
- **Third-party libraries**: Use vendor prefixes
|
|
||||||
```cpp
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
```
|
|
||||||
- **Standard library**: Use angle brackets
|
|
||||||
```cpp
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Formatting
|
|
||||||
- **Indentation**: 2 spaces
|
|
||||||
- **Brace Style**: K&R (opening brace on same line)
|
|
||||||
```cpp
|
|
||||||
void myFunction() {
|
|
||||||
// body
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **Pointer/Reference Style**: Type on left, not centered
|
|
||||||
```cpp
|
|
||||||
void func(T* ptr); // Good
|
|
||||||
void func(T *ptr); // Avoid
|
|
||||||
void func(T& ref); // Good
|
|
||||||
```
|
|
||||||
- **Const Placement**: After type
|
|
||||||
```cpp
|
|
||||||
const std::string& str; // Good
|
|
||||||
std::string const& str; // Avoid
|
|
||||||
```
|
|
||||||
|
|
||||||
### Types
|
|
||||||
- **Use custom type aliases**: `int8`, `int32`, `uint8`, `uint64`, `Ptr<T>`, `SharedPtr<T>`
|
|
||||||
- **Smart pointers**: Prefer `Ptr<T>` alias over `std::shared_ptr<T>`
|
|
||||||
- **constexpr**: Use for compile-time constants
|
|
||||||
- **Struct vs Class**:
|
|
||||||
- Use `struct` for POD types, configurations, and data-only types
|
|
||||||
- Use `class` for objects with methods/encapsulation
|
|
||||||
- **Default member initialization**: Initialize at declaration
|
|
||||||
```cpp
|
|
||||||
bool running_ = false;
|
|
||||||
int width_ = 1280;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
- **Return bool for success/failure operations** (especially I/O)
|
|
||||||
- **Use SDL_Log/SDL_LogError for logging**
|
|
||||||
- **Use std::optional for optional returns**
|
|
||||||
- **Avoid exceptions** (not observed in codebase)
|
|
||||||
- **Return early on failure**: Check conditions and return false immediately
|
|
||||||
|
|
||||||
### Singletons
|
|
||||||
- Use static instance pattern with private constructor:
|
|
||||||
```cpp
|
|
||||||
static Application& get();
|
|
||||||
private:
|
|
||||||
Application() = default;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Const Correctness
|
|
||||||
- Mark methods `const` if they don't modify state
|
|
||||||
- Pass by const reference for large types: `const std::string&`
|
|
||||||
- Return const references to member data when appropriate
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
- **Doxygen-style comments** with `@brief` tag
|
|
||||||
- **Use `///` for single-line comments in headers**
|
|
||||||
- **Section separators** use `// ---------------------------------------------------------------------------`
|
|
||||||
- **Chinese comments** are present and should be preserved/maintained
|
|
||||||
```cpp
|
|
||||||
/**
|
|
||||||
* @brief 读取文本文件
|
|
||||||
* @param path 文件路径
|
|
||||||
* @param outContent 输出文件内容
|
|
||||||
* @return 读取成功返回 true
|
|
||||||
*/
|
|
||||||
bool readTextFile(const std::string& path, std::string& outContent);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Platform Handling
|
|
||||||
- Use `#ifdef` guards for platform-specific code:
|
|
||||||
```cpp
|
|
||||||
#ifdef __SWITCH__
|
|
||||||
switchInit();
|
|
||||||
#endif
|
|
||||||
```
|
|
||||||
- Supported platforms: Windows, Linux, macOS, Switch
|
|
||||||
- Platform configs in `platform/` directory
|
|
||||||
|
|
||||||
### STL Usage
|
|
||||||
- Prefer STL algorithms over manual loops
|
|
||||||
- Use `std::filesystem` for file operations (already wrapped in Asset class)
|
|
||||||
- Use `std::optional` for nullable returns
|
|
||||||
- Use `std::function` for callbacks
|
|
||||||
- Use `std::vector` for dynamic arrays
|
|
||||||
- Use `std::string` for text
|
|
||||||
|
|
||||||
### OpenGL/Graphics
|
|
||||||
- GLM library for math operations
|
|
||||||
- Glad for OpenGL function loading
|
|
||||||
- Keep OpenGL-specific code minimal and well-encapsulated
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
- **SDL2**: Windowing, input, platform abstraction
|
|
||||||
- **GLM**: Math library (vectors, matrices)
|
|
||||||
- **Glad**: OpenGL loader
|
|
||||||
- **STB**: Image/text utilities (single-header libs)
|
|
||||||
|
|
||||||
## Key Patterns
|
|
||||||
|
|
||||||
### Module Pattern
|
|
||||||
```cpp
|
|
||||||
void Application::use(Module& m) {
|
|
||||||
modules_.push_back(&m);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration Pattern
|
|
||||||
Use configuration structs with default values:
|
|
||||||
```cpp
|
|
||||||
struct WindowConfig {
|
|
||||||
uint32_t width = 640;
|
|
||||||
uint32_t height = 480;
|
|
||||||
std::string title = "Frostbite2D Game";
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Path Handling
|
|
||||||
Always use the Asset singleton for file operations - it handles platform-specific path issues:
|
|
||||||
```cpp
|
|
||||||
Asset& asset = Asset::get();
|
|
||||||
asset.setWorkingDirectory("path/to/assets");
|
|
||||||
std::string content;
|
|
||||||
if (asset.readTextFile("file.txt", content)) {
|
|
||||||
// success
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Issues
|
|
||||||
- **Path separators**: Use Asset class, it handles platform differences
|
|
||||||
- **Encoding**: Project uses UTF-8, Asset handles Windows UTF-8 paths
|
|
||||||
- **Memory leaks**: Use smart pointers, ensure proper cleanup in destructors
|
|
||||||
- **OpenGL context**: Initialize only after window creation
|
|
||||||
|
|
@ -12,27 +12,11 @@ namespace frostbite2D {
|
||||||
* 仅包含应用级别的配置项,模块配置由各模块自行管理
|
* 仅包含应用级别的配置项,模块配置由各模块自行管理
|
||||||
*/
|
*/
|
||||||
struct AppConfig {
|
struct AppConfig {
|
||||||
/**
|
|
||||||
* @brief 应用名称
|
|
||||||
*/
|
|
||||||
std::string appName = "frostbite2D App";
|
std::string appName = "frostbite2D App";
|
||||||
/**
|
|
||||||
* @brief 应用版本号
|
|
||||||
*/
|
|
||||||
std::string appVersion = "1.0.0";
|
std::string appVersion = "1.0.0";
|
||||||
/**
|
|
||||||
* @brief 组织名称
|
|
||||||
*/
|
|
||||||
std::string organization = "frostbite";
|
std::string organization = "frostbite";
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 窗口配置
|
|
||||||
*/
|
|
||||||
WindowConfig windowConfig;
|
WindowConfig windowConfig;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 目标平台
|
|
||||||
*/
|
|
||||||
PlatformType targetPlatform = PlatformType::Auto;
|
PlatformType targetPlatform = PlatformType::Auto;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,6 +73,13 @@ public:
|
||||||
*/
|
*/
|
||||||
bool init(const AppConfig& config);
|
bool init(const AppConfig& config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使用配置文件初始化
|
||||||
|
* @param configPath 配置文件路径
|
||||||
|
* @return 初始化成功返回 true
|
||||||
|
*/
|
||||||
|
bool init(const std::string& configPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 关闭应用程序
|
* @brief 关闭应用程序
|
||||||
*/
|
*/
|
||||||
|
|
@ -126,6 +117,54 @@ public:
|
||||||
*/
|
*/
|
||||||
bool isRunning() const { return running_; }
|
bool isRunning() const { return running_; }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief 获取窗口
|
||||||
|
// * @return 窗口引用
|
||||||
|
// */
|
||||||
|
// IWindow& window() { return *window_; }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief 获取渲染器
|
||||||
|
// * @return 渲染器引用
|
||||||
|
// */
|
||||||
|
// RenderBackend& renderer();
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief 获取场景服务
|
||||||
|
// * @return 场景服务共享指针
|
||||||
|
// */
|
||||||
|
// SharedPtr<class ISceneService> scenes();
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief 获取计时器服务
|
||||||
|
// * @return 计时器服务共享指针
|
||||||
|
// */
|
||||||
|
// SharedPtr<class ITimerService> timers();
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief 获取事件服务
|
||||||
|
// * @return 事件服务共享指针
|
||||||
|
// */
|
||||||
|
// SharedPtr<class IEventService> events();
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief 获取相机服务
|
||||||
|
// * @return 相机服务共享指针
|
||||||
|
// */
|
||||||
|
// SharedPtr<class ICameraService> camera();
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @brief 进入场景
|
||||||
|
// * @param scene 场景指针
|
||||||
|
// */
|
||||||
|
// void enterScene(Ptr<class Scene> scene);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取帧间隔时间
|
||||||
|
* @return 帧间隔时间(秒)
|
||||||
|
*/
|
||||||
|
float deltaTime() const { return deltaTime_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取总运行时间
|
* @brief 获取总运行时间
|
||||||
* @return 总运行时间(秒)
|
* @return 总运行时间(秒)
|
||||||
|
|
@ -144,24 +183,6 @@ public:
|
||||||
*/
|
*/
|
||||||
const AppConfig& getConfig() const;
|
const AppConfig& getConfig() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取窗口
|
|
||||||
* @return 窗口指针
|
|
||||||
*/
|
|
||||||
Window* getWindow() const { return window_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取渲染器
|
|
||||||
* @return 渲染器指针
|
|
||||||
*/
|
|
||||||
class Renderer* getRenderer() const { return renderer_; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取 delta time
|
|
||||||
* @return 帧间隔时间(秒)
|
|
||||||
*/
|
|
||||||
float deltaTime() const { return deltaTime_; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application() = default;
|
Application() = default;
|
||||||
~Application();
|
~Application();
|
||||||
|
|
@ -182,6 +203,11 @@ private:
|
||||||
*/
|
*/
|
||||||
void destroyAllModules();
|
void destroyAllModules();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注册核心服务
|
||||||
|
*/
|
||||||
|
void registerCoreServices();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 主循环
|
* @brief 主循环
|
||||||
*/
|
*/
|
||||||
|
|
@ -199,8 +225,6 @@ private:
|
||||||
|
|
||||||
std::vector<Module*> modules_;
|
std::vector<Module*> modules_;
|
||||||
Window* window_ = nullptr;
|
Window* window_ = nullptr;
|
||||||
class Renderer* renderer_ = nullptr;
|
|
||||||
AppConfig config_;
|
|
||||||
|
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
bool running_ = false;
|
bool running_ = false;
|
||||||
|
|
@ -213,7 +237,6 @@ private:
|
||||||
int frameCount_ = 0;
|
int frameCount_ = 0;
|
||||||
float fpsTimer_ = 0.0f;
|
float fpsTimer_ = 0.0f;
|
||||||
int currentFps_ = 0;
|
int currentFps_ = 0;
|
||||||
const float fpsUpdateInterval_ = 1.0f;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <frostbite2D/graphics/types.h>
|
|
||||||
#include <frostbite2D/graphics/texture.h>
|
|
||||||
#include <frostbite2D/graphics/shader.h>
|
|
||||||
#include <frostbite2D/types/type_alias.h>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
struct BatchKey {
|
|
||||||
uint32_t shaderID;
|
|
||||||
uint32_t textureID;
|
|
||||||
uint32_t blendMode;
|
|
||||||
|
|
||||||
bool operator<(const BatchKey& other) const {
|
|
||||||
if (shaderID != other.shaderID) return shaderID < other.shaderID;
|
|
||||||
if (textureID != other.textureID) return textureID < other.textureID;
|
|
||||||
return blendMode < other.blendMode;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Batch {
|
|
||||||
public:
|
|
||||||
static constexpr int MAX_QUADS = 2048;
|
|
||||||
static constexpr int MAX_VERTICES = MAX_QUADS * 4;
|
|
||||||
static constexpr int MAX_INDICES = MAX_QUADS * 6;
|
|
||||||
|
|
||||||
Batch();
|
|
||||||
~Batch();
|
|
||||||
|
|
||||||
bool init();
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
void begin();
|
|
||||||
void end();
|
|
||||||
|
|
||||||
void submitQuad(const Quad& quad, const Transform2D& transform,
|
|
||||||
Ptr<Texture> texture, Shader* shader,
|
|
||||||
BlendMode blendMode = BlendMode::Normal);
|
|
||||||
|
|
||||||
void flush();
|
|
||||||
void flushIfNeeded(const BatchKey& newKey, Shader* shader, Ptr<Texture> texture);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setupMesh();
|
|
||||||
void flushCurrentBatch();
|
|
||||||
|
|
||||||
struct BatchData {
|
|
||||||
std::vector<Vertex> vertices;
|
|
||||||
std::vector<uint16> indices;
|
|
||||||
BatchKey key;
|
|
||||||
Shader* shader = nullptr;
|
|
||||||
Ptr<Texture> texture;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t vao_ = 0;
|
|
||||||
uint32_t vbo_ = 0;
|
|
||||||
uint32_t ibo_ = 0;
|
|
||||||
|
|
||||||
BatchData currentBatch_;
|
|
||||||
std::vector<BatchData> batches_;
|
|
||||||
|
|
||||||
bool begun_ = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <frostbite2D/types/type_math.h>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
class Camera {
|
|
||||||
public:
|
|
||||||
Camera();
|
|
||||||
|
|
||||||
void setPosition(const Vec2& pos);
|
|
||||||
void setZoom(float zoom);
|
|
||||||
void setViewport(int width, int height);
|
|
||||||
void setFlipY(bool flip) { flipY_ = flip; } // 调试用:Y轴翻转开关
|
|
||||||
|
|
||||||
const Vec2& getPosition() const { return position_; }
|
|
||||||
float getZoom() const { return zoom_; }
|
|
||||||
|
|
||||||
void lookAt(const Vec2& target);
|
|
||||||
void move(const Vec2& delta);
|
|
||||||
void zoomAt(float factor, const Vec2& screenPos);
|
|
||||||
|
|
||||||
glm::mat4 getViewMatrix() const;
|
|
||||||
glm::mat4 getProjectionMatrix() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Vec2 position_;
|
|
||||||
float zoom_ = 1.0f;
|
|
||||||
int viewportWidth_ = 1280;
|
|
||||||
int viewportHeight_ = 720;
|
|
||||||
bool flipY_ = false; // 调试用:Y轴翻转开关
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <frostbite2D/graphics/types.h>
|
|
||||||
#include <frostbite2D/graphics/texture.h>
|
|
||||||
#include <frostbite2D/graphics/shader.h>
|
|
||||||
#include <frostbite2D/graphics/shader_manager.h>
|
|
||||||
#include <frostbite2D/graphics/batch.h>
|
|
||||||
#include <frostbite2D/graphics/camera.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
class Renderer {
|
|
||||||
public:
|
|
||||||
static Renderer& get();
|
|
||||||
|
|
||||||
bool init();
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
void beginFrame();
|
|
||||||
void endFrame();
|
|
||||||
void flush();
|
|
||||||
|
|
||||||
void setViewport(int x, int y, int width, int height);
|
|
||||||
void setClearColor(float r, float g, float b, float a = 1.0f);
|
|
||||||
void setClearColor(const Color& color);
|
|
||||||
void clear(uint32_t flags);
|
|
||||||
|
|
||||||
void setCamera(Camera* camera);
|
|
||||||
Camera* getCamera() { return camera_; }
|
|
||||||
|
|
||||||
void drawQuad(const Vec2& pos, const Size& size, float cr = 1.0f, float cg = 1.0f,
|
|
||||||
float cb = 1.0f, float ca = 1.0f);
|
|
||||||
void drawQuad(const Vec2& pos, const Size& size, Ptr<Texture> texture);
|
|
||||||
void drawQuad(const Rect& rect, const Color& color);
|
|
||||||
|
|
||||||
void drawSprite(const Vec2& pos, const Size& size, Ptr<Texture> texture);
|
|
||||||
void drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
|
|
||||||
Ptr<Texture> texture, const Color& color = Color(1, 1, 1, 1));
|
|
||||||
|
|
||||||
ShaderManager& getShaderManager() { return shaderManager_; }
|
|
||||||
Batch& getBatch() { return batch_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Renderer();
|
|
||||||
~Renderer();
|
|
||||||
|
|
||||||
void setupBlendMode(BlendMode mode);
|
|
||||||
void updateUniforms();
|
|
||||||
|
|
||||||
ShaderManager shaderManager_;
|
|
||||||
Batch batch_;
|
|
||||||
Camera* camera_ = nullptr;
|
|
||||||
|
|
||||||
uint32_t clearColor_[4] = {0, 0, 0, 255};
|
|
||||||
int viewportX_ = 0;
|
|
||||||
int viewportY_ = 0;
|
|
||||||
int viewportWidth_ = 1280;
|
|
||||||
int viewportHeight_ = 720;
|
|
||||||
|
|
||||||
bool initialized_ = false;
|
|
||||||
|
|
||||||
Renderer(const Renderer&) = delete;
|
|
||||||
Renderer& operator=(const Renderer&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <frostbite2D/types/type_alias.h>
|
|
||||||
#include <frostbite2D/types/type_math.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
class Shader {
|
|
||||||
public:
|
|
||||||
Shader() = default;
|
|
||||||
~Shader();
|
|
||||||
|
|
||||||
void use();
|
|
||||||
void unuse();
|
|
||||||
|
|
||||||
bool isValid() const { return programID_ != 0; }
|
|
||||||
uint32_t getID() const { return programID_; }
|
|
||||||
const std::string& getName() const { return name_; }
|
|
||||||
|
|
||||||
void setInt(const std::string& name, int value);
|
|
||||||
void setFloat(const std::string& name, float value);
|
|
||||||
void setVec2(const std::string& name, const Vec2& value);
|
|
||||||
void setVec3(const std::string& name, const glm::vec3& value);
|
|
||||||
void setVec4(const std::string& name, const glm::vec4& value);
|
|
||||||
void setMat3(const std::string& name, const glm::mat3& value);
|
|
||||||
void setMat4(const std::string& name, const glm::mat4& value);
|
|
||||||
void setTexture(const std::string& name, int slot);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Shader(const std::string& name, uint32_t programID);
|
|
||||||
|
|
||||||
friend class ShaderManager;
|
|
||||||
|
|
||||||
bool compile(uint32_t type, const std::string& source);
|
|
||||||
bool link(uint32_t vertexID, uint32_t fragmentID);
|
|
||||||
int getUniformLocation(const std::string& name);
|
|
||||||
|
|
||||||
uint32_t programID_ = 0;
|
|
||||||
std::string name_;
|
|
||||||
std::unordered_map<std::string, int> uniformLocations_;
|
|
||||||
|
|
||||||
Shader(const Shader&) = delete;
|
|
||||||
Shader& operator=(const Shader&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <frostbite2D/graphics/shader.h>
|
|
||||||
#include <frostbite2D/types/type_alias.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
class Renderer;
|
|
||||||
|
|
||||||
class ShaderManager {
|
|
||||||
public:
|
|
||||||
static ShaderManager& get();
|
|
||||||
|
|
||||||
bool init(const std::string& shadersDir);
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
Shader* getShader(const std::string& name);
|
|
||||||
bool hasShader(const std::string& name) const;
|
|
||||||
|
|
||||||
~ShaderManager();
|
|
||||||
|
|
||||||
ShaderManager() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::unordered_map<std::string, Ptr<Shader>> shaders_;
|
|
||||||
std::string shadersDir_;
|
|
||||||
|
|
||||||
bool loadShadersFromConfig(const std::string& configPath);
|
|
||||||
bool loadShader(const std::string& name, const std::string& vertPath,
|
|
||||||
const std::string& fragPath);
|
|
||||||
|
|
||||||
ShaderManager(const ShaderManager&) = delete;
|
|
||||||
ShaderManager& operator=(const ShaderManager&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <frostbite2D/graphics/types.h>
|
|
||||||
#include <frostbite2D/types/type_alias.h>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
class Texture {
|
|
||||||
public:
|
|
||||||
static Ptr<Texture> loadFromFile(const std::string& path);
|
|
||||||
static Ptr<Texture> createFromMemory(uint8* data, int width, int height, int channels);
|
|
||||||
static Ptr<Texture> createEmpty(int width, int height);
|
|
||||||
|
|
||||||
~Texture();
|
|
||||||
|
|
||||||
void bind(uint32_t slot = 0);
|
|
||||||
void unbind();
|
|
||||||
|
|
||||||
void setWrapMode(uint32_t wrapS, uint32_t wrapT);
|
|
||||||
void setFilterMode(uint32_t minFilter, uint32_t magFilter);
|
|
||||||
|
|
||||||
int getWidth() const { return width_; }
|
|
||||||
int getHeight() const { return height_; }
|
|
||||||
uint32_t getID() const { return textureID_; }
|
|
||||||
const std::string& getPath() const { return path_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Texture(int width, int height, uint32_t id);
|
|
||||||
|
|
||||||
friend class std::shared_ptr<Texture>;
|
|
||||||
template<typename T, typename... Args> friend Ptr<T> makePtr(Args &&...args);
|
|
||||||
|
|
||||||
uint32_t textureID_ = 0;
|
|
||||||
int width_ = 0;
|
|
||||||
int height_ = 0;
|
|
||||||
int channels_ = 0;
|
|
||||||
std::string path_;
|
|
||||||
|
|
||||||
Texture() = default;
|
|
||||||
Texture(const Texture&) = delete;
|
|
||||||
Texture& operator=(const Texture&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <frostbite2D/types/type_alias.h>
|
|
||||||
#include <frostbite2D/types/type_math.h>
|
|
||||||
#include <frostbite2D/types/type_color.h>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
enum class BlendMode {
|
|
||||||
None,
|
|
||||||
Normal,
|
|
||||||
Additive,
|
|
||||||
Subtractive,
|
|
||||||
Multiply
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class PrimitiveType {
|
|
||||||
Triangles,
|
|
||||||
TriangleStrip,
|
|
||||||
TriangleFan,
|
|
||||||
Quads
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Vertex {
|
|
||||||
Vec2 position;
|
|
||||||
Vec2 texCoord;
|
|
||||||
float r, g, b, a;
|
|
||||||
|
|
||||||
Vertex() = default;
|
|
||||||
Vertex(const Vec2& pos, const Vec2& uv, float cr, float cg, float cb, float ca)
|
|
||||||
: position(pos), texCoord(uv), r(cr), g(cg), b(cb), a(ca) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Quad {
|
|
||||||
Vertex vertices[4];
|
|
||||||
|
|
||||||
Quad() = default;
|
|
||||||
|
|
||||||
static Quad create(const Rect& rect, float cr = 1.0f, float cg = 1.0f,
|
|
||||||
float cb = 1.0f, float ca = 1.0f) {
|
|
||||||
Quad q;
|
|
||||||
Vec2 bl(rect.left(), rect.bottom());
|
|
||||||
Vec2 br(rect.right(), rect.bottom());
|
|
||||||
Vec2 tl(rect.left(), rect.top());
|
|
||||||
Vec2 tr(rect.right(), rect.top());
|
|
||||||
|
|
||||||
q.vertices[0] = Vertex(bl, Vec2(0, 0), cr, cg, cb, ca);
|
|
||||||
q.vertices[1] = Vertex(br, Vec2(1, 0), cr, cg, cb, ca);
|
|
||||||
q.vertices[2] = Vertex(tl, Vec2(0, 1), cr, cg, cb, ca);
|
|
||||||
q.vertices[3] = Vertex(tr, Vec2(1, 1), cr, cg, cb, ca);
|
|
||||||
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Quad createTextured(const Rect& destRect, const Rect& srcRect,
|
|
||||||
const Vec2& texSize, float cr = 1.0f,
|
|
||||||
float cg = 1.0f, float cb = 1.0f, float ca = 1.0f) {
|
|
||||||
Quad q;
|
|
||||||
|
|
||||||
Vec2 bl(destRect.left(), destRect.bottom());
|
|
||||||
Vec2 br(destRect.right(), destRect.bottom());
|
|
||||||
Vec2 tl(destRect.left(), destRect.top());
|
|
||||||
Vec2 tr(destRect.right(), destRect.top());
|
|
||||||
|
|
||||||
Vec2 uvBL(srcRect.left() / texSize.x, srcRect.bottom() / texSize.y);
|
|
||||||
Vec2 uvBR(srcRect.right() / texSize.x, srcRect.bottom() / texSize.y);
|
|
||||||
Vec2 uvTL(srcRect.left() / texSize.x, srcRect.top() / texSize.y);
|
|
||||||
Vec2 uvTR(srcRect.right() / texSize.x, srcRect.top() / texSize.y);
|
|
||||||
|
|
||||||
q.vertices[0] = Vertex(bl, uvBL, cr, cg, cb, ca);
|
|
||||||
q.vertices[1] = Vertex(br, uvBR, cr, cg, cb, ca);
|
|
||||||
q.vertices[2] = Vertex(tl, uvTL, cr, cg, cb, ca);
|
|
||||||
q.vertices[3] = Vertex(tr, uvTR, cr, cg, cb, ca);
|
|
||||||
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <frostbite2D/core/types.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
|
||||||
#include <glm/vec4.hpp>
|
#include <glm/vec4.hpp>
|
||||||
|
|
||||||
namespace frostbite2D {
|
namespace frostbite2D {
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ struct Rect {
|
||||||
constexpr Rect() = default;
|
constexpr Rect() = default;
|
||||||
constexpr Rect(float x, float y, float w, float h)
|
constexpr Rect(float x, float y, float w, float h)
|
||||||
: origin(x, y), size(w, h) {}
|
: origin(x, y), size(w, h) {}
|
||||||
constexpr Rect(const Point& o, const Size& s) : origin(o), size(s) {}
|
constexpr Rect(const Point &o, const Size &s) : origin(o), size(s) {}
|
||||||
|
|
||||||
float left() const { return origin.x; }
|
float left() const { return origin.x; }
|
||||||
float top() const { return origin.y; }
|
float top() const { return origin.y; }
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,10 +1,6 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <algorithm>
|
|
||||||
#include <frostbite2D/core/application.h>
|
#include <frostbite2D/core/application.h>
|
||||||
#include <frostbite2D/graphics/renderer.h>
|
|
||||||
#include <frostbite2D/platform/switch.h>
|
#include <frostbite2D/platform/switch.h>
|
||||||
#include <frostbite2D/types/type_math.h>
|
|
||||||
#include <frostbite2D/utils/asset.h>
|
|
||||||
namespace frostbite2D {
|
namespace frostbite2D {
|
||||||
|
|
||||||
Application &Application::get() {
|
Application &Application::get() {
|
||||||
|
|
@ -39,30 +35,18 @@ bool Application::init(const AppConfig &config) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_ = config;
|
|
||||||
|
|
||||||
// 平台相关初始化
|
// 平台相关初始化
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
switchInit();
|
switchInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 使用SDL2创建窗口
|
|
||||||
this->window_ = new Window();
|
this->window_ = new Window();
|
||||||
|
|
||||||
// 创建窗口会使用窗口配置
|
|
||||||
if (!window_->create(config.windowConfig)) {
|
if (!window_->create(config.windowConfig)) {
|
||||||
SDL_Log("Failed to create window");
|
SDL_Log("Failed to create window");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化核心模块
|
|
||||||
if (!initCoreModules()) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize core modules");
|
|
||||||
shutdown();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized_ = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,17 +54,6 @@ void Application::shutdown() {
|
||||||
if (!initialized_)
|
if (!initialized_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
running_ = false;
|
|
||||||
shouldQuit_ = true;
|
|
||||||
|
|
||||||
destroyAllModules();
|
|
||||||
|
|
||||||
// 关闭渲染器
|
|
||||||
if (renderer_) {
|
|
||||||
renderer_->shutdown();
|
|
||||||
renderer_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window_) {
|
if (window_) {
|
||||||
window_->destroy();
|
window_->destroy();
|
||||||
window_ = nullptr;
|
window_ = nullptr;
|
||||||
|
|
@ -96,184 +69,8 @@ void Application::shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
shutdown();
|
if (initialized_) {
|
||||||
}
|
shutdown();
|
||||||
|
|
||||||
bool Application::initCoreModules() {
|
|
||||||
|
|
||||||
// 初始化资产管理器
|
|
||||||
auto &asset = Asset::get();
|
|
||||||
|
|
||||||
// 平台相关 switch平台不可以获取当前工作目录
|
|
||||||
#ifndef __SWITCH__
|
|
||||||
// 获取程序工作目录
|
|
||||||
std::string workingDir = SDL_GetBasePath();
|
|
||||||
asset.setWorkingDirectory(workingDir);
|
|
||||||
SDL_Log("Asset working directory: %s", workingDir.c_str());
|
|
||||||
#else
|
|
||||||
asset.setWorkingDirectory("/switch/Frostbite2D/" + config_.appName);
|
|
||||||
SDL_Log("Asset working directory: %s", asset.getWorkingDirectory().c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 初始化渲染器
|
|
||||||
renderer_ = &Renderer::get();
|
|
||||||
if (!renderer_->init()) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize renderer");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置窗口清除颜色和视口
|
|
||||||
renderer_->setClearColor(0.0f, 0.0f, 0.0f);
|
|
||||||
renderer_->setViewport(0, 0, config_.windowConfig.width,
|
|
||||||
config_.windowConfig.height);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::setupAllModules() {
|
|
||||||
SDL_Log("Setting up modules (%zu)...", modules_.size());
|
|
||||||
|
|
||||||
for (auto* module : modules_) {
|
|
||||||
if (module && !module->isInitialized()) {
|
|
||||||
module->setupModule();
|
|
||||||
module->setInitialized(true);
|
|
||||||
SDL_Log("Module '%s' initialized", module->getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::destroyAllModules() {
|
|
||||||
SDL_Log("Destroying modules (%zu)...", modules_.size());
|
|
||||||
|
|
||||||
for (auto* module : modules_) {
|
|
||||||
if (module && module->isInitialized()) {
|
|
||||||
module->destroyModule();
|
|
||||||
module->setInitialized(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::run() {
|
|
||||||
if (!initialized_) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Application not initialized!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (running_) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Application already running!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Log("Starting application...");
|
|
||||||
running_ = true;
|
|
||||||
shouldQuit_ = false;
|
|
||||||
paused_ = false;
|
|
||||||
|
|
||||||
setupAllModules();
|
|
||||||
|
|
||||||
lastFrameTime_ = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
|
|
||||||
totalTime_ = 0.0f;
|
|
||||||
deltaTime_ = 0.0f;
|
|
||||||
frameCount_ = 0;
|
|
||||||
fpsTimer_ = 0.0f;
|
|
||||||
currentFps_ = 0;
|
|
||||||
|
|
||||||
mainLoop();
|
|
||||||
|
|
||||||
destroyAllModules();
|
|
||||||
running_ = false;
|
|
||||||
SDL_Log("Application stopped");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::quit() {
|
|
||||||
SDL_Log("Quit requested");
|
|
||||||
shouldQuit_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::pause() {
|
|
||||||
if (!paused_) {
|
|
||||||
SDL_Log("Application paused");
|
|
||||||
paused_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::resume() {
|
|
||||||
if (paused_) {
|
|
||||||
SDL_Log("Application resumed");
|
|
||||||
paused_ = false;
|
|
||||||
lastFrameTime_ = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::mainLoop() {
|
|
||||||
while (!shouldQuit_) {
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
if (event.type == SDL_QUIT) {
|
|
||||||
shouldQuit_ = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (auto* module : modules_) {
|
|
||||||
if (module && module->isInitialized()) {
|
|
||||||
module->handleEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!paused_) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::update() {
|
|
||||||
double currentTime = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
|
|
||||||
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
|
||||||
lastFrameTime_ = currentTime;
|
|
||||||
totalTime_ += deltaTime_;
|
|
||||||
|
|
||||||
for (auto* module : modules_) {
|
|
||||||
if (module && module->isInitialized()) {
|
|
||||||
module->onUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frameCount_++;
|
|
||||||
fpsTimer_ += deltaTime_;
|
|
||||||
if (fpsTimer_ >= fpsUpdateInterval_) {
|
|
||||||
currentFps_ = static_cast<int>(frameCount_ / fpsTimer_);
|
|
||||||
frameCount_ = 0;
|
|
||||||
fpsTimer_ = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::render() {
|
|
||||||
for (auto* module : modules_) {
|
|
||||||
if (module && module->isInitialized()) {
|
|
||||||
module->beforeRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* module : modules_) {
|
|
||||||
if (module && module->isInitialized()) {
|
|
||||||
module->onRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* module : modules_) {
|
|
||||||
if (module && module->isInitialized()) {
|
|
||||||
module->afterRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window_) {
|
|
||||||
window_->swap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const AppConfig& Application::getConfig() const {
|
|
||||||
return config_;
|
|
||||||
}
|
}
|
||||||
} // namespace frostbite2D
|
} // namespace frostbite2D
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include "SDL_log.h"
|
#include "SDL_log.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <frostbite2D/core/window.h>
|
#include <frostbite2D/core/window.h>
|
||||||
#include <frostbite2D/utils/asset.h>
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -81,42 +80,10 @@ bool Window::create(const WindowConfig &cfg) {
|
||||||
|
|
||||||
SDL_GL_SetSwapInterval(cfg.vsync ? 1 : 0);
|
SDL_GL_SetSwapInterval(cfg.vsync ? 1 : 0);
|
||||||
|
|
||||||
// 获取实际的可绘制大小(考虑高 DPI 和 Switch 等平台)
|
SDL_GetWindowSize(sdlWindow_, &width_, &height_);
|
||||||
int drawableWidth, drawableHeight;
|
|
||||||
SDL_GL_GetDrawableSize(sdlWindow_, &drawableWidth, &drawableHeight);
|
|
||||||
width_ = drawableWidth;
|
|
||||||
height_ = drawableHeight;
|
|
||||||
|
|
||||||
// 计算缩放比例
|
|
||||||
int windowWidth, windowHeight;
|
|
||||||
SDL_GetWindowSize(sdlWindow_, &windowWidth, &windowHeight);
|
|
||||||
if (windowWidth > 0 && windowHeight > 0) {
|
|
||||||
scaleX_ = static_cast<float>(drawableWidth) / static_cast<float>(windowWidth);
|
|
||||||
scaleY_ = static_cast<float>(drawableHeight) / static_cast<float>(windowHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||||
vsync_ = cfg.vsync;
|
vsync_ = cfg.vsync;
|
||||||
|
|
||||||
#ifndef __SWITCH__
|
|
||||||
if (!cfg.icon.file_path.empty()) {
|
|
||||||
Asset& asset = Asset::get();
|
|
||||||
std::string resolvedPath = asset.resolveAssetPath(cfg.icon.file_path);
|
|
||||||
SDL_Surface* icon = SDL_LoadBMP(resolvedPath.c_str());
|
|
||||||
if (icon) {
|
|
||||||
SDL_SetWindowIcon(sdlWindow_, icon);
|
|
||||||
SDL_FreeSurface(icon);
|
|
||||||
} else {
|
|
||||||
SDL_Log("Failed to load window icon: %s", resolvedPath.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if defined(_WIN32)
|
|
||||||
if (cfg.icon.resource_id != 0) {
|
|
||||||
SDL_Log("Loading icon from resource ID: %u", cfg.icon.resource_id);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,11 +99,7 @@ void Window::destroy() {
|
||||||
|
|
||||||
void Window::poll() {}
|
void Window::poll() {}
|
||||||
|
|
||||||
void Window::swap() {
|
void Window::swap() {}
|
||||||
if (sdlWindow_ && glContext_) {
|
|
||||||
SDL_GL_SwapWindow(sdlWindow_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::close() { shouldClose_ = true; }
|
void Window::close() { shouldClose_ = true; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,180 +0,0 @@
|
||||||
#include "SDL_log.h"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <frostbite2D/graphics/batch.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
Batch::Batch() {
|
|
||||||
currentBatch_.vertices.reserve(MAX_VERTICES);
|
|
||||||
currentBatch_.indices.reserve(MAX_INDICES);
|
|
||||||
}
|
|
||||||
|
|
||||||
Batch::~Batch() {
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Batch::init() {
|
|
||||||
glGenVertexArrays(1, &vao_);
|
|
||||||
glGenBuffers(1, &vbo_);
|
|
||||||
glGenBuffers(1, &ibo_);
|
|
||||||
|
|
||||||
setupMesh();
|
|
||||||
|
|
||||||
SDL_Log("Batch system initialized");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::shutdown() {
|
|
||||||
if (vao_ != 0) {
|
|
||||||
glDeleteVertexArrays(1, &vao_);
|
|
||||||
vao_ = 0;
|
|
||||||
}
|
|
||||||
if (vbo_ != 0) {
|
|
||||||
glDeleteBuffers(1, &vbo_);
|
|
||||||
vbo_ = 0;
|
|
||||||
}
|
|
||||||
if (ibo_ != 0) {
|
|
||||||
glDeleteBuffers(1, &ibo_);
|
|
||||||
ibo_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBatch_.vertices.clear();
|
|
||||||
currentBatch_.indices.clear();
|
|
||||||
batches_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::setupMesh() {
|
|
||||||
glBindVertexArray(vao_);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_INDICES * sizeof(uint16), nullptr, GL_DYNAMIC_DRAW);
|
|
||||||
|
|
||||||
int stride = sizeof(Vertex);
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, (void*)0);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(Vertex, texCoord));
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(2);
|
|
||||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, (void*)offsetof(Vertex, r));
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::begin() {
|
|
||||||
begun_ = true;
|
|
||||||
currentBatch_.vertices.clear();
|
|
||||||
currentBatch_.indices.clear();
|
|
||||||
currentBatch_.key = {0, 0, 0};
|
|
||||||
currentBatch_.shader = nullptr;
|
|
||||||
currentBatch_.texture.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::end() {
|
|
||||||
if (begun_) {
|
|
||||||
flush();
|
|
||||||
begun_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::submitQuad(const Quad& quad, const Transform2D& transform,
|
|
||||||
Ptr<Texture> texture, Shader* shader,
|
|
||||||
BlendMode blendMode) {
|
|
||||||
if (!begun_) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Batch not started, call begin() first");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t textureID = texture ? texture->getID() : 0;
|
|
||||||
uint32_t shaderID = shader ? shader->getID() : 0;
|
|
||||||
BatchKey newKey = {shaderID, textureID, (uint32_t)blendMode};
|
|
||||||
|
|
||||||
flushIfNeeded(newKey, shader, texture);
|
|
||||||
|
|
||||||
glm::mat4 mat = transform.matrix;
|
|
||||||
uint16_t indexOffset = static_cast<uint16_t>(currentBatch_.vertices.size());
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
Vertex v = quad.vertices[i];
|
|
||||||
glm::vec4 pos(v.position.x, v.position.y, 0.0f, 1.0f);
|
|
||||||
glm::vec4 transformed = mat * pos;
|
|
||||||
v.position.x = transformed.x;
|
|
||||||
v.position.y = transformed.y;
|
|
||||||
|
|
||||||
currentBatch_.vertices.push_back(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBatch_.indices.push_back(indexOffset);
|
|
||||||
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 1));
|
|
||||||
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 2));
|
|
||||||
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 1));
|
|
||||||
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 3));
|
|
||||||
currentBatch_.indices.push_back(static_cast<uint16_t>(indexOffset + 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::flush() {
|
|
||||||
if (!currentBatch_.vertices.empty()) {
|
|
||||||
flushCurrentBatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::flushIfNeeded(const BatchKey& newKey, Shader* shader, Ptr<Texture> texture) {
|
|
||||||
if (currentBatch_.indices.empty()) {
|
|
||||||
currentBatch_.key = newKey;
|
|
||||||
currentBatch_.shader = shader;
|
|
||||||
currentBatch_.texture = texture;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentBatch_.key.shaderID != newKey.shaderID ||
|
|
||||||
currentBatch_.key.textureID != newKey.textureID ||
|
|
||||||
currentBatch_.key.blendMode != newKey.blendMode) {
|
|
||||||
flushCurrentBatch();
|
|
||||||
currentBatch_.key = newKey;
|
|
||||||
currentBatch_.shader = shader;
|
|
||||||
currentBatch_.texture = texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Batch::flushCurrentBatch() {
|
|
||||||
if (currentBatch_.vertices.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(vao_);
|
|
||||||
|
|
||||||
// 绑定着色器和纹理
|
|
||||||
if (currentBatch_.shader) {
|
|
||||||
currentBatch_.shader->use();
|
|
||||||
}
|
|
||||||
if (currentBatch_.texture) {
|
|
||||||
currentBatch_.texture->bind();
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0,
|
|
||||||
currentBatch_.vertices.size() * sizeof(Vertex),
|
|
||||||
currentBatch_.vertices.data());
|
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
|
|
||||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0,
|
|
||||||
currentBatch_.indices.size() * sizeof(uint16),
|
|
||||||
currentBatch_.indices.data());
|
|
||||||
|
|
||||||
glDrawElements(GL_TRIANGLES,
|
|
||||||
static_cast<GLsizei>(currentBatch_.indices.size()),
|
|
||||||
GL_UNSIGNED_SHORT, 0);
|
|
||||||
|
|
||||||
currentBatch_.vertices.clear();
|
|
||||||
currentBatch_.indices.clear();
|
|
||||||
currentBatch_.shader = nullptr;
|
|
||||||
currentBatch_.texture.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
#include <frostbite2D/graphics/camera.h>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
Camera::Camera() : position_(0, 0), zoom_(1.0f) {}
|
|
||||||
|
|
||||||
void Camera::setPosition(const Vec2 &pos) { position_ = pos; }
|
|
||||||
|
|
||||||
void Camera::setZoom(float zoom) { zoom_ = zoom; }
|
|
||||||
|
|
||||||
void Camera::setViewport(int width, int height) {
|
|
||||||
viewportWidth_ = width;
|
|
||||||
viewportHeight_ = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::lookAt(const Vec2 &target) { position_ = target; }
|
|
||||||
|
|
||||||
void Camera::move(const Vec2 &delta) { position_ = position_ + delta; }
|
|
||||||
|
|
||||||
void Camera::zoomAt(float factor, const Vec2 &screenPos) {
|
|
||||||
Vec2 worldBefore = screenPos - position_;
|
|
||||||
zoom_ *= factor;
|
|
||||||
Vec2 worldAfter = screenPos - position_;
|
|
||||||
position_ = position_ + (worldBefore - worldAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 Camera::getViewMatrix() const {
|
|
||||||
glm::mat4 view = glm::mat4(1.0f);
|
|
||||||
view[3][0] = -position_.x;
|
|
||||||
view[3][1] = -position_.y;
|
|
||||||
view[0][0] = zoom_;
|
|
||||||
view[1][1] = zoom_;
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 Camera::getProjectionMatrix() const {
|
|
||||||
float left = 0.0f;
|
|
||||||
float right = static_cast<float>(viewportWidth_);
|
|
||||||
float bottom = static_cast<float>(viewportHeight_);
|
|
||||||
float top = 0.0f;
|
|
||||||
|
|
||||||
return glm::ortho(left, right, bottom, top, -1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace frostbite2D
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
||||||
#include "SDL_log.h"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <frostbite2D/graphics/renderer.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
Renderer& Renderer::get() {
|
|
||||||
static Renderer instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
Renderer::Renderer() = default;
|
|
||||||
|
|
||||||
Renderer::~Renderer() {
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Renderer::init() {
|
|
||||||
if (initialized_) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!batch_.init()) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize batch system");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//初始化着色器管理器
|
|
||||||
if (!shaderManager_.init("shaders")) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize shader manager");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
|
|
||||||
SDL_Log("Renderer initialized");
|
|
||||||
initialized_ = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::shutdown() {
|
|
||||||
batch_.shutdown();
|
|
||||||
shaderManager_.shutdown();
|
|
||||||
initialized_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::beginFrame() {
|
|
||||||
glViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
|
|
||||||
glClearColor(clearColor_[0] / 255.0f, clearColor_[1] / 255.0f,
|
|
||||||
clearColor_[2] / 255.0f, clearColor_[3] / 255.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
updateUniforms();
|
|
||||||
batch_.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::endFrame() {
|
|
||||||
batch_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::flush() {
|
|
||||||
batch_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::setViewport(int x, int y, int width, int height) {
|
|
||||||
viewportX_ = x;
|
|
||||||
viewportY_ = y;
|
|
||||||
viewportWidth_ = width;
|
|
||||||
viewportHeight_ = height;
|
|
||||||
|
|
||||||
if (camera_) {
|
|
||||||
camera_->setViewport(width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::setClearColor(float r, float g, float b, float a) {
|
|
||||||
clearColor_[0] = static_cast<uint8>(r * 255);
|
|
||||||
clearColor_[1] = static_cast<uint8>(g * 255);
|
|
||||||
clearColor_[2] = static_cast<uint8>(b * 255);
|
|
||||||
clearColor_[3] = static_cast<uint8>(a * 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::setClearColor(const Color& color) {
|
|
||||||
clearColor_[0] = color.r;
|
|
||||||
clearColor_[1] = color.g;
|
|
||||||
clearColor_[2] = color.b;
|
|
||||||
clearColor_[3] = color.a;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::clear(uint32_t flags) {
|
|
||||||
glClear(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::setCamera(Camera* camera) {
|
|
||||||
camera_ = camera;
|
|
||||||
if (camera) {
|
|
||||||
camera_->setViewport(viewportWidth_, viewportHeight_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::setupBlendMode(BlendMode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case BlendMode::None:
|
|
||||||
glDisable(GL_BLEND);
|
|
||||||
break;
|
|
||||||
case BlendMode::Normal:
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
break;
|
|
||||||
case BlendMode::Additive:
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
||||||
break;
|
|
||||||
case BlendMode::Subtractive:
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
|
||||||
break;
|
|
||||||
case BlendMode::Multiply:
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glBlendFunc(GL_DST_COLOR, GL_ZERO);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::updateUniforms() {
|
|
||||||
if (camera_) {
|
|
||||||
auto* spriteShader = shaderManager_.getShader("sprite");
|
|
||||||
if (spriteShader) {
|
|
||||||
spriteShader->use();
|
|
||||||
spriteShader->setMat4("u_view", camera_->getViewMatrix());
|
|
||||||
spriteShader->setMat4("u_projection", camera_->getProjectionMatrix());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* coloredShader = shaderManager_.getShader("colored_quad");
|
|
||||||
if (coloredShader) {
|
|
||||||
coloredShader->use();
|
|
||||||
coloredShader->setMat4("u_view", camera_->getViewMatrix());
|
|
||||||
coloredShader->setMat4("u_projection", camera_->getProjectionMatrix());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::drawQuad(const Vec2& pos, const Size& size, float cr, float cg,
|
|
||||||
float cb, float ca) {
|
|
||||||
Rect rect(pos.x, pos.y, size.width, size.height);
|
|
||||||
Quad quad = Quad::create(rect, cr, cg, cb, ca);
|
|
||||||
Transform2D transform = Transform2D::identity();
|
|
||||||
|
|
||||||
auto* shader = shaderManager_.getShader("colored_quad");
|
|
||||||
if (shader) {
|
|
||||||
shader->use();
|
|
||||||
}
|
|
||||||
|
|
||||||
batch_.submitQuad(quad, transform, nullptr, shader, BlendMode::Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::drawQuad(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
|
|
||||||
Rect rect(pos.x, pos.y, size.width, size.height);
|
|
||||||
Quad quad = Quad::createTextured(rect, Rect(0, 0, size.width, size.height),
|
|
||||||
Vec2(size.width, size.height));
|
|
||||||
Transform2D transform = Transform2D::identity();
|
|
||||||
|
|
||||||
auto* shader = shaderManager_.getShader("sprite");
|
|
||||||
if (shader) {
|
|
||||||
shader->use();
|
|
||||||
}
|
|
||||||
|
|
||||||
batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::drawQuad(const Rect& rect, const Color& color) {
|
|
||||||
drawQuad(Vec2(rect.left(), rect.top()),
|
|
||||||
Size(rect.width(), rect.height()),
|
|
||||||
static_cast<float>(color.r) / 255.0f,
|
|
||||||
static_cast<float>(color.g) / 255.0f,
|
|
||||||
static_cast<float>(color.b) / 255.0f,
|
|
||||||
static_cast<float>(color.a) / 255.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::drawSprite(const Vec2& pos, const Size& size, Ptr<Texture> texture) {
|
|
||||||
drawQuad(pos, size, texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::drawSprite(const Vec2& pos, const Rect& srcRect, const Vec2& texSize,
|
|
||||||
Ptr<Texture> texture, const Color& color) {
|
|
||||||
Rect destRect(pos.x, pos.y, srcRect.width(), srcRect.height());
|
|
||||||
Quad quad = Quad::createTextured(destRect, srcRect, texSize,
|
|
||||||
static_cast<float>(color.r) / 255.0f,
|
|
||||||
static_cast<float>(color.g) / 255.0f,
|
|
||||||
static_cast<float>(color.b) / 255.0f,
|
|
||||||
static_cast<float>(color.a) / 255.0f);
|
|
||||||
Transform2D transform = Transform2D::identity();
|
|
||||||
|
|
||||||
auto* shader = shaderManager_.getShader("sprite");
|
|
||||||
if (shader) {
|
|
||||||
shader->use();
|
|
||||||
}
|
|
||||||
|
|
||||||
batch_.submitQuad(quad, transform, texture, shader, BlendMode::Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
#include "SDL_log.h"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <frostbite2D/graphics/shader.h>
|
|
||||||
#include <frostbite2D/utils/asset.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
Shader::Shader(const std::string& name, uint32_t programID)
|
|
||||||
: name_(name), programID_(programID) {}
|
|
||||||
|
|
||||||
Shader::~Shader() {
|
|
||||||
if (programID_ != 0) {
|
|
||||||
glDeleteProgram(programID_);
|
|
||||||
programID_ = 0;
|
|
||||||
}
|
|
||||||
uniformLocations_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::use() {
|
|
||||||
glUseProgram(programID_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::unuse() {
|
|
||||||
glUseProgram(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shader::compile(uint32_t type, const std::string& source) {
|
|
||||||
uint32_t shaderID = glCreateShader(type);
|
|
||||||
const char* src = source.c_str();
|
|
||||||
glShaderSource(shaderID, 1, &src, nullptr);
|
|
||||||
glCompileShader(shaderID);
|
|
||||||
|
|
||||||
int success;
|
|
||||||
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success);
|
|
||||||
if (!success) {
|
|
||||||
int length;
|
|
||||||
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &length);
|
|
||||||
std::vector<char> log(length);
|
|
||||||
glGetShaderInfoLog(shaderID, length, &length, log.data());
|
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader compilation failed: %s", log.data());
|
|
||||||
glDeleteShader(shaderID);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shader::link(uint32_t vertexID, uint32_t fragmentID) {
|
|
||||||
programID_ = glCreateProgram();
|
|
||||||
glAttachShader(programID_, vertexID);
|
|
||||||
glAttachShader(programID_, fragmentID);
|
|
||||||
glLinkProgram(programID_);
|
|
||||||
|
|
||||||
int success;
|
|
||||||
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
|
|
||||||
if (!success) {
|
|
||||||
int length;
|
|
||||||
glGetProgramiv(programID_, GL_INFO_LOG_LENGTH, &length);
|
|
||||||
std::vector<char> log(length);
|
|
||||||
glGetProgramInfoLog(programID_, length, &length, log.data());
|
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader linking failed: %s", log.data());
|
|
||||||
glDeleteProgram(programID_);
|
|
||||||
programID_ = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glDeleteShader(vertexID);
|
|
||||||
glDeleteShader(fragmentID);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Shader::getUniformLocation(const std::string& name) {
|
|
||||||
auto it = uniformLocations_.find(name);
|
|
||||||
if (it != uniformLocations_.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
int location = glGetUniformLocation(programID_, name.c_str());
|
|
||||||
uniformLocations_[name] = location;
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setInt(const std::string& name, int value) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniform1i(location, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setFloat(const std::string& name, float value) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniform1f(location, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setVec2(const std::string& name, const Vec2& value) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniform2f(location, value.x, value.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setVec3(const std::string& name, const glm::vec3& value) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniform3f(location, value.x, value.y, value.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setVec4(const std::string& name, const glm::vec4& value) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniform4f(location, value.x, value.y, value.z, value.w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setMat3(const std::string& name, const glm::mat3& value) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniformMatrix3fv(location, 1, GL_FALSE, &value[0][0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setMat4(const std::string& name, const glm::mat4& value) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniformMatrix4fv(location, 1, GL_FALSE, &value[0][0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shader::setTexture(const std::string& name, int slot) {
|
|
||||||
int location = getUniformLocation(name);
|
|
||||||
if (location >= 0) {
|
|
||||||
glUniform1i(location, slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
#include "SDL_log.h"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <frostbite2D/graphics/shader_manager.h>
|
|
||||||
#include <frostbite2D/graphics/shader.h>
|
|
||||||
#include <frostbite2D/utils/asset.h>
|
|
||||||
#include <json/json.hpp>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
ShaderManager& ShaderManager::get() {
|
|
||||||
static ShaderManager instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderManager::~ShaderManager() {
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderManager::init(const std::string& shadersDir) {
|
|
||||||
shadersDir_ = shadersDir;
|
|
||||||
shaders_.clear();
|
|
||||||
|
|
||||||
std::string configPath = shadersDir_ + "/shaders.json";
|
|
||||||
return loadShadersFromConfig(configPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderManager::shutdown() {
|
|
||||||
shaders_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shader* ShaderManager::getShader(const std::string& name) {
|
|
||||||
auto it = shaders_.find(name);
|
|
||||||
if (it != shaders_.end()) {
|
|
||||||
return it->second.get();
|
|
||||||
}
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Shader not found: %s", name.c_str());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderManager::hasShader(const std::string& name) const {
|
|
||||||
return shaders_.find(name) != shaders_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderManager::loadShader(const std::string& name, const std::string& vertPath,
|
|
||||||
const std::string& fragPath) {
|
|
||||||
Asset& asset = Asset::get();
|
|
||||||
std::string vertSource, fragSource;
|
|
||||||
|
|
||||||
if (!asset.readTextFile(vertPath, vertSource)) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load vertex shader: %s", vertPath.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!asset.readTextFile(fragPath, fragSource)) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load fragment shader: %s", fragPath.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t vertexID = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
uint32_t fragmentID = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
|
|
||||||
const char* vertSrc = vertSource.c_str();
|
|
||||||
glShaderSource(vertexID, 1, &vertSrc, nullptr);
|
|
||||||
glCompileShader(vertexID);
|
|
||||||
|
|
||||||
const char* fragSrc = fragSource.c_str();
|
|
||||||
glShaderSource(fragmentID, 1, &fragSrc, nullptr);
|
|
||||||
glCompileShader(fragmentID);
|
|
||||||
|
|
||||||
int success;
|
|
||||||
glGetShaderiv(vertexID, GL_COMPILE_STATUS, &success);
|
|
||||||
if (!success) {
|
|
||||||
int length;
|
|
||||||
glGetShaderiv(vertexID, GL_INFO_LOG_LENGTH, &length);
|
|
||||||
std::vector<char> log(length);
|
|
||||||
glGetShaderInfoLog(vertexID, length, &length, log.data());
|
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Vertex shader compilation failed: %s", log.data());
|
|
||||||
glDeleteShader(vertexID);
|
|
||||||
glDeleteShader(fragmentID);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glGetShaderiv(fragmentID, GL_COMPILE_STATUS, &success);
|
|
||||||
if (!success) {
|
|
||||||
int length;
|
|
||||||
glGetShaderiv(fragmentID, GL_INFO_LOG_LENGTH, &length);
|
|
||||||
std::vector<char> log(length);
|
|
||||||
glGetShaderInfoLog(fragmentID, length, &length, log.data());
|
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Fragment shader compilation failed: %s", log.data());
|
|
||||||
glDeleteShader(vertexID);
|
|
||||||
glDeleteShader(fragmentID);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t programID = glCreateProgram();
|
|
||||||
glAttachShader(programID, vertexID);
|
|
||||||
glAttachShader(programID, fragmentID);
|
|
||||||
|
|
||||||
// 绑定顶点属性位置,确保和 Batch 类的设置一致
|
|
||||||
glBindAttribLocation(programID, 0, "a_position");
|
|
||||||
glBindAttribLocation(programID, 1, "a_texCoord");
|
|
||||||
glBindAttribLocation(programID, 2, "a_color");
|
|
||||||
|
|
||||||
glLinkProgram(programID);
|
|
||||||
|
|
||||||
glGetProgramiv(programID, GL_LINK_STATUS, &success);
|
|
||||||
if (!success) {
|
|
||||||
int length;
|
|
||||||
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &length);
|
|
||||||
std::vector<char> log(length);
|
|
||||||
glGetProgramInfoLog(programID, length, &length, log.data());
|
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shader linking failed: %s", log.data());
|
|
||||||
glDeleteProgram(programID);
|
|
||||||
glDeleteShader(vertexID);
|
|
||||||
glDeleteShader(fragmentID);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glDeleteShader(vertexID);
|
|
||||||
glDeleteShader(fragmentID);
|
|
||||||
|
|
||||||
auto shader = Ptr<Shader>(new Shader(name, programID));
|
|
||||||
shaders_[name] = shader;
|
|
||||||
|
|
||||||
SDL_Log("Loaded shader: %s", name.c_str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShaderManager::loadShadersFromConfig(const std::string& configPath) {
|
|
||||||
Asset& asset = Asset::get();
|
|
||||||
std::string content;
|
|
||||||
|
|
||||||
if (!asset.readTextFile(configPath, content)) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "No shaders.json found, will compile shaders on demand");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
json config = json::parse(content);
|
|
||||||
|
|
||||||
if (!config.contains("shaders")) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid shaders.json: missing 'shaders' key");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto shaders = config["shaders"];
|
|
||||||
for (auto& [key, value] : shaders.items()) {
|
|
||||||
if (!value.contains("vertex") || !value.contains("fragment")) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid shader entry: %s", key.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string vertFile = value["vertex"];
|
|
||||||
std::string fragFile = value["fragment"];
|
|
||||||
|
|
||||||
std::string vertPath = shadersDir_ + "/" + vertFile;
|
|
||||||
std::string fragPath = shadersDir_ + "/" + fragFile;
|
|
||||||
|
|
||||||
if (!loadShader(key, vertPath, fragPath)) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to load shader: %s", key.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Log("Loaded %zu shaders from config", shaders_.size());
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (const json::parse_error& e) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to parse shaders.json: %s", e.what());
|
|
||||||
return false;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error loading shaders: %s", e.what());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include "SDL_log.h"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <frostbite2D/graphics/texture.h>
|
|
||||||
#include <frostbite2D/utils/asset.h>
|
|
||||||
#include <stb/stb_image.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
namespace frostbite2D {
|
|
||||||
|
|
||||||
Texture::Texture(int width, int height, uint32_t id)
|
|
||||||
: width_(width), height_(height), textureID_(id) {}
|
|
||||||
|
|
||||||
Texture::~Texture() {
|
|
||||||
if (textureID_ != 0) {
|
|
||||||
glDeleteTextures(1, &textureID_);
|
|
||||||
textureID_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<Texture> Texture::loadFromFile(const std::string& path) {
|
|
||||||
Asset& asset = Asset::get();
|
|
||||||
std::string resolvedPath = asset.resolveAssetPath(path);
|
|
||||||
|
|
||||||
int width, height, channels;
|
|
||||||
uint8* data = stbi_load(resolvedPath.c_str(), &width, &height, &channels, 0);
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load texture: %s", resolvedPath.c_str());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t format = channels == 4 ? GL_RGBA : GL_RGB;
|
|
||||||
|
|
||||||
uint32_t textureID;
|
|
||||||
glGenTextures(1, &textureID);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
stbi_image_free(data);
|
|
||||||
|
|
||||||
auto texture = Ptr<Texture>(new Texture(width, height, textureID));
|
|
||||||
texture->path_ = resolvedPath;
|
|
||||||
texture->channels_ = channels;
|
|
||||||
|
|
||||||
SDL_Log("Loaded texture: %s (%dx%d, %d channels)", resolvedPath.c_str(), width, height, channels);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<Texture> Texture::createFromMemory(uint8* data, int width, int height, int channels) {
|
|
||||||
if (!data) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture from memory: null data");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t format = channels == 4 ? GL_RGBA : GL_RGB;
|
|
||||||
|
|
||||||
uint32_t textureID;
|
|
||||||
glGenTextures(1, &textureID);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
|
|
||||||
auto texture = Ptr<Texture>(new Texture(width, height, textureID));
|
|
||||||
texture->channels_ = channels;
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<Texture> Texture::createEmpty(int width, int height) {
|
|
||||||
uint32_t textureID;
|
|
||||||
glGenTextures(1, &textureID);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
|
|
||||||
return Ptr<Texture>(new Texture(width, height, textureID));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Texture::bind(uint32_t slot) {
|
|
||||||
glActiveTexture(GL_TEXTURE0 + slot);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Texture::unbind() {
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Texture::setWrapMode(uint32_t wrapS, uint32_t wrapT) {
|
|
||||||
bind();
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
|
|
||||||
unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Texture::setFilterMode(uint32_t minFilter, uint32_t magFilter) {
|
|
||||||
bind();
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
|
|
||||||
unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#include "SDL_log.h"
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <frostbite2D/core/application.h>
|
||||||
|
#include <frostbite2D/core/window.h>
|
||||||
|
#include <frostbite2D/utils/asset.h>
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
using namespace frostbite2D;
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
AppConfig config = AppConfig::createDefault();
|
||||||
|
config.appName = "Frostbite2D Render Test";
|
||||||
|
config.appVersion = "1.0.0";
|
||||||
|
config.windowConfig.width = 800;
|
||||||
|
config.windowConfig.height = 600;
|
||||||
|
config.windowConfig.title = "Frostbite2D - OpenGL Render Test";
|
||||||
|
|
||||||
|
Application &app = Application::get();
|
||||||
|
if (!app.init(config)) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Failed to initialize application!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Asset &asset = Asset::get();
|
||||||
|
// asset.setWorkingDirectory("I:/DOF/骑士团");
|
||||||
|
asset.setWorkingDirectory("/switch/testgame");
|
||||||
|
std::string content;
|
||||||
|
if (asset.readTextFile("test.txt", content)) {
|
||||||
|
SDL_Log("test.txt content: %s", content.c_str());
|
||||||
|
} else {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read test.txt!");
|
||||||
|
}
|
||||||
|
|
||||||
|
app.shutdown();
|
||||||
|
|
||||||
|
SDL_Log("程序正常退出");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# Icon Setup Guide
|
|
||||||
|
|
||||||
## Window Icon (运行时窗口图标)
|
|
||||||
|
|
||||||
### Windows/Linux
|
|
||||||
- Place `icon.bmp` (32x32 or larger) in `assets/icons/`
|
|
||||||
- Set in code: `config.windowConfig.icon = Icon("assets/icons/icon.bmp");`
|
|
||||||
|
|
||||||
### Switch
|
|
||||||
- Place `icon.jpg` (256x256 JPEG) in `assets/icons/icon.jpg`
|
|
||||||
- Automatically included in NRO build process
|
|
||||||
|
|
||||||
## Application Icon (程序文件图标)
|
|
||||||
|
|
||||||
### Windows (.exe 图标)
|
|
||||||
需要创建 `.ico` 文件并放置在 `assets/icons/app.ico`
|
|
||||||
|
|
||||||
#### 创建 .ico 文件的方法:
|
|
||||||
|
|
||||||
**方法 1: 使用在线工具**
|
|
||||||
1. 访问 https://icoconvert.com/
|
|
||||||
2. 上传 PNG/JPG 图标(推荐 256x256)
|
|
||||||
3.将下载的 .ico 文件保存到 `assets/icons/app.ico`
|
|
||||||
|
|
||||||
**方法 2: 使用 GIMP**
|
|
||||||
1. 打开图像
|
|
||||||
2. 调整为 256x256
|
|
||||||
3. 文件 → 导出为 → `app.ico`
|
|
||||||
4. 保存到 `assets/icons/app.ico`
|
|
||||||
|
|
||||||
**方法 3: 使用 ImageMagick**
|
|
||||||
```bash
|
|
||||||
convert icon.png -define icon:auto-resize=256,128,96,64,48,32,16 app.ico
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Linux (.desktop 图标)
|
|
||||||
- 将图标复制到 `/usr/share/icons/hicolor/256x256/apps/yourapp.png`
|
|
||||||
- 创建 `.desktop` 文件指定 Icon 字段
|
|
||||||
|
|
||||||
## Usage Example
|
|
||||||
```cpp
|
|
||||||
AppConfig config = AppConfig::createDefault();
|
|
||||||
config.windowConfig.icon = Icon("assets/icons/icon.bmp");
|
|
||||||
Application& app = Application::get();
|
|
||||||
app.init(config);
|
|
||||||
```
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
|
|
@ -1,9 +0,0 @@
|
||||||
#ifdef GL_ES
|
|
||||||
precision mediump float;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
varying vec4 v_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_FragColor = v_color;
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#ifdef GL_ES
|
|
||||||
precision mediump float;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
attribute vec2 a_position;
|
|
||||||
attribute vec2 a_texCoord;
|
|
||||||
attribute vec4 a_color;
|
|
||||||
|
|
||||||
uniform mat4 u_view;
|
|
||||||
uniform mat4 u_projection;
|
|
||||||
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
varying vec4 v_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0);
|
|
||||||
v_texCoord = a_texCoord;
|
|
||||||
v_color = a_color;
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
#ifdef GL_ES
|
|
||||||
precision mediump float;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
varying vec4 v_color;
|
|
||||||
|
|
||||||
uniform sampler2D u_texture;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 texColor = texture2D(u_texture, v_texCoord);
|
|
||||||
gl_FragColor = texColor * v_color;
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
#ifdef GL_ES
|
|
||||||
precision mediump float;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
attribute vec2 a_position;
|
|
||||||
attribute vec2 a_texCoord;
|
|
||||||
attribute vec4 a_color;
|
|
||||||
|
|
||||||
uniform mat4 u_view;
|
|
||||||
uniform mat4 u_projection;
|
|
||||||
uniform sampler2D u_texture;
|
|
||||||
|
|
||||||
varying vec2 v_texCoord;
|
|
||||||
varying vec4 v_color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0);
|
|
||||||
v_texCoord = a_texCoord;
|
|
||||||
v_color = a_color;
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
#include "SDL_log.h"
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <frostbite2D/core/application.h>
|
|
||||||
#include <frostbite2D/core/window.h>
|
|
||||||
#include <frostbite2D/graphics/renderer.h>
|
|
||||||
#include <frostbite2D/graphics/texture.h>
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
using namespace frostbite2D;
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
AppConfig config = AppConfig::createDefault();
|
|
||||||
config.appName = "Frostbite2D Test App";
|
|
||||||
config.appVersion = "1.0.0";
|
|
||||||
config.windowConfig.width = 800;
|
|
||||||
config.windowConfig.height = 600;
|
|
||||||
config.windowConfig.title = "Frostbite2D - Renderer Test";
|
|
||||||
|
|
||||||
Application& app = Application::get();
|
|
||||||
|
|
||||||
if (!app.init(config)) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize application!");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Log("Starting main loop...");
|
|
||||||
|
|
||||||
app.run();
|
|
||||||
|
|
||||||
app.shutdown();
|
|
||||||
|
|
||||||
SDL_Log("Application exited normally");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -8,16 +8,13 @@ target("Frostbite2D")
|
||||||
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
||||||
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
||||||
|
|
||||||
add_files(path.join(os.projectdir(), "Game/src/**.cpp"))
|
|
||||||
add_includedirs(path.join(os.projectdir(), "Game/include"))
|
|
||||||
|
|
||||||
add_packages("libsdl2")
|
add_packages("libsdl2")
|
||||||
add_packages("glm")
|
add_packages("glm")
|
||||||
|
|
||||||
-- 复制着色器文件到输出目录
|
-- 复制着色器文件到输出目录
|
||||||
after_build(function (target)
|
after_build(function (target)
|
||||||
-- 复制 shaders 目录
|
-- 复制 shaders 目录
|
||||||
local shaders_dir = path.join(os.projectdir(), "Game/shaders")
|
local shaders_dir = path.join(os.projectdir(), "shaders")
|
||||||
local output_dir = target:targetdir()
|
local output_dir = target:targetdir()
|
||||||
local target_shaders_dir = path.join(output_dir, "shaders")
|
local target_shaders_dir = path.join(output_dir, "shaders")
|
||||||
|
|
||||||
|
|
@ -35,20 +32,37 @@ target("Frostbite2D")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 复制图标文件到输出目录
|
-- 复制 SDL2 DLL
|
||||||
local icons_dir = path.join(os.projectdir(), "assets/icons")
|
local sdl2_lib = target:pkg("libsdl2")
|
||||||
local target_icons_dir = path.join(output_dir, "assets/icons")
|
if sdl2_lib then
|
||||||
|
local libfiles = sdl2_lib:get("libfiles")
|
||||||
|
if libfiles then
|
||||||
|
for _, libfile in ipairs(libfiles) do
|
||||||
|
-- 查找 DLL 文件
|
||||||
|
if libfile:endswith(".dll") then
|
||||||
|
local target_dll = path.join(output_dir, path.filename(libfile))
|
||||||
|
os.cp(libfile, target_dll)
|
||||||
|
print("Copy DLL: " .. path.filename(libfile))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if os.isdir(icons_dir) then
|
-- 尝试从 xmake 包目录复制 SDL2.dll
|
||||||
if not os.isdir(target_icons_dir) then
|
local sdl2_dll_paths = {
|
||||||
os.mkdir(target_icons_dir)
|
path.join(os.getenv("USERPROFILE") or "", ".xmake/packages/l/libsdl2/**/bin/SDL2.dll"),
|
||||||
|
path.join(os.getenv("USERPROFILE") or "", ".xmake/packages/l/libsdl2/**/lib/SDL2.dll"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dll_pattern in ipairs(sdl2_dll_paths) do
|
||||||
|
local dll_files = os.files(dll_pattern)
|
||||||
|
for _, dll_file in ipairs(dll_files) do
|
||||||
|
local target_dll = path.join(output_dir, "SDL2.dll")
|
||||||
|
if not os.isfile(target_dll) then
|
||||||
|
os.cp(dll_file, target_dll)
|
||||||
|
print("Copy SDL2.dll from: " .. dll_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
for _, file in ipairs(os.files(path.join(icons_dir, "*.*"))) do
|
|
||||||
local filename = path.filename(file)
|
|
||||||
local target_file = path.join(target_icons_dir, filename)
|
|
||||||
os.cp(file, target_file)
|
|
||||||
print("Copy icon: " .. filename)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
target_end()
|
target_end()
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,13 @@ target("Frostbite2D")
|
||||||
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
||||||
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
||||||
|
|
||||||
add_files(path.join(os.projectdir(), "Game/src/**.cpp"))
|
|
||||||
add_includedirs(path.join(os.projectdir(), "Game/include"))
|
|
||||||
|
|
||||||
add_packages("libsdl2")
|
add_packages("libsdl2")
|
||||||
add_packages("glm")
|
add_packages("glm")
|
||||||
|
|
||||||
-- 复制着色器文件到输出目录
|
-- 复制着色器文件到输出目录
|
||||||
after_build(function (target)
|
after_build(function (target)
|
||||||
-- 复制 shaders 目录
|
-- 复制 shaders 目录
|
||||||
local shaders_dir = path.join(os.projectdir(), "Game/shaders")
|
local shaders_dir = path.join(os.projectdir(), "shaders")
|
||||||
local output_dir = target:targetdir()
|
local output_dir = target:targetdir()
|
||||||
local target_shaders_dir = path.join(output_dir, "shaders")
|
local target_shaders_dir = path.join(output_dir, "shaders")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@ target("Frostbite2D")
|
||||||
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
||||||
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
||||||
|
|
||||||
add_files(path.join(os.projectdir(), "Game/src/**.cpp"))
|
|
||||||
add_includedirs(path.join(os.projectdir(), "Game/include"))
|
|
||||||
|
|
||||||
-- 检查 DEVKITPRO 环境变量(Windows 上使用 C:/devkitPro)
|
-- 检查 DEVKITPRO 环境变量(Windows 上使用 C:/devkitPro)
|
||||||
local devkitPro = os.getenv("DEVKITPRO") or "L:/Switch/devkitPro"
|
local devkitPro = os.getenv("DEVKITPRO") or "L:/Switch/devkitPro"
|
||||||
local devkitA64 = path.join(devkitPro, "devkitA64")
|
local devkitA64 = path.join(devkitPro, "devkitA64")
|
||||||
|
|
@ -45,9 +42,7 @@ target("Frostbite2D")
|
||||||
{public = true})
|
{public = true})
|
||||||
add_syslinks("nx", "m")
|
add_syslinks("nx", "m")
|
||||||
|
|
||||||
|
|
||||||
-- 构建后生成 NRO 文件
|
-- 构建后生成 NRO 文件
|
||||||
-- 复制着色器文件到输出目录
|
|
||||||
after_build(function (target)
|
after_build(function (target)
|
||||||
local elf_file = target:targetfile()
|
local elf_file = target:targetfile()
|
||||||
local output_dir = path.directory(elf_file)
|
local output_dir = path.directory(elf_file)
|
||||||
|
|
@ -55,37 +50,16 @@ target("Frostbite2D")
|
||||||
local nro_file = path.join(output_dir, "hello_world.nro")
|
local nro_file = path.join(output_dir, "hello_world.nro")
|
||||||
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
|
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
|
||||||
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe")
|
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe")
|
||||||
local icon_file = path.join(os.projectdir(), "assets/icons/icon.bmp")
|
|
||||||
|
|
||||||
if os.isfile(nacptool) and os.isfile(elf2nro) then
|
if os.isfile(nacptool) and os.isfile(elf2nro) then
|
||||||
os.vrunv(nacptool, {"--create", "Hello World", "Frostbite2D Team", "1.0.0", nacp_file})
|
os.vrunv(nacptool, {"--create", "Hello World", "Extra2D Team", "1.0.0", nacp_file})
|
||||||
local romfs = path.join(example_dir, "romfs")
|
local romfs = path.join(example_dir, "romfs")
|
||||||
local icon_param = os.isfile(icon_file) and ("--icon=" .. icon_file) or ""
|
|
||||||
if os.isdir(romfs) then
|
if os.isdir(romfs) then
|
||||||
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, icon_param, "--romfsdir=" .. romfs})
|
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
|
||||||
else
|
else
|
||||||
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, icon_param})
|
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file})
|
||||||
end
|
end
|
||||||
print("Generated NRO: " .. nro_file)
|
print("Generated NRO: " .. nro_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 复制 shaders 目录
|
|
||||||
local shaders_dir = path.join(os.projectdir(), "Game/shaders")
|
|
||||||
local target_shaders_dir = path.join(output_dir, "shaders")
|
|
||||||
|
|
||||||
if os.isdir(shaders_dir) then
|
|
||||||
-- 确保目标目录存在
|
|
||||||
if not os.isdir(target_shaders_dir) then
|
|
||||||
os.mkdir(target_shaders_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- 复制所有着色器文件
|
|
||||||
for _, file in ipairs(os.files(path.join(shaders_dir, "*.*"))) do
|
|
||||||
local filename = path.filename(file)
|
|
||||||
local target_file = path.join(target_shaders_dir, filename)
|
|
||||||
os.cp(file, target_file)
|
|
||||||
print("Copy shader: " .. filename)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
target_end()
|
target_end()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
-- MinGW 编译配置
|
||||||
|
set_toolchains("mingw")
|
||||||
|
|
||||||
|
add_requires("libsdl2", {configs = {shared = true}})
|
||||||
|
add_requires("glm")
|
||||||
|
|
||||||
|
target("Frostbite2D")
|
||||||
|
set_kind("binary")
|
||||||
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp"))
|
||||||
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
||||||
|
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
||||||
|
|
||||||
|
add_packages("libsdl2")
|
||||||
|
add_packages("glm")
|
||||||
|
|
||||||
|
-- 复制着色器文件到输出目录
|
||||||
|
after_build(function (target)
|
||||||
|
-- 复制 shaders 目录
|
||||||
|
local shaders_dir = path.join(os.projectdir(), "shaders")
|
||||||
|
local output_dir = target:targetdir()
|
||||||
|
local target_shaders_dir = path.join(output_dir, "shaders")
|
||||||
|
|
||||||
|
if os.isdir(shaders_dir) then
|
||||||
|
-- 确保目标目录存在
|
||||||
|
if not os.isdir(target_shaders_dir) then
|
||||||
|
os.mkdir(target_shaders_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 复制所有着色器文件
|
||||||
|
for _, file in ipairs(os.files(path.join(shaders_dir, "*.*"))) do
|
||||||
|
local filename = path.filename(file)
|
||||||
|
local target_file = path.join(target_shaders_dir, filename)
|
||||||
|
os.cp(file, target_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 复制 SDL2 DLL
|
||||||
|
local sdl2_lib = target:pkg("libsdl2")
|
||||||
|
if sdl2_lib then
|
||||||
|
local libfiles = sdl2_lib:get("libfiles")
|
||||||
|
if libfiles then
|
||||||
|
for _, libfile in ipairs(libfiles) do
|
||||||
|
-- 查找 DLL 文件
|
||||||
|
if libfile:endswith(".dll") then
|
||||||
|
local target_dll = path.join(output_dir, path.filename(libfile))
|
||||||
|
os.cp(libfile, target_dll)
|
||||||
|
print("Copy DLL: " .. path.filename(libfile))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 尝试从 xmake 包目录复制 SDL2.dll
|
||||||
|
local sdl2_dll_paths = {
|
||||||
|
path.join(os.getenv("USERPROFILE") or "", ".xmake/packages/l/libsdl2/**/bin/SDL2.dll"),
|
||||||
|
path.join(os.getenv("USERPROFILE") or "", ".xmake/packages/l/libsdl2/**/lib/SDL2.dll"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dll_pattern in ipairs(sdl2_dll_paths) do
|
||||||
|
local dll_files = os.files(dll_pattern)
|
||||||
|
for _, dll_file in ipairs(dll_files) do
|
||||||
|
local target_dll = path.join(output_dir, "SDL2.dll")
|
||||||
|
if not os.isfile(target_dll) then
|
||||||
|
os.cp(dll_file, target_dll)
|
||||||
|
print("Copy SDL2.dll from: " .. dll_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
target_end()
|
||||||
|
|
@ -10,10 +10,7 @@ add_rules("mode.debug", "mode.release")
|
||||||
|
|
||||||
local host_plat = os.host()
|
local host_plat = os.host()
|
||||||
local target_plat = get_config("plat") or host_plat
|
local target_plat = get_config("plat") or host_plat
|
||||||
if target_plat == "windows" then
|
local supported_plats = {mingw = true, windows = true, linux = true, macosx = true, switch = true}
|
||||||
target_plat = "mingw"
|
|
||||||
end
|
|
||||||
local supported_plats = {mingw = true, linux = true, macosx = true, switch = true}
|
|
||||||
|
|
||||||
-- 自动选择平台
|
-- 自动选择平台
|
||||||
if not supported_plats[target_plat] then
|
if not supported_plats[target_plat] then
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue