feat(assets): 添加字体和音乐资源支持
- 新增 Font 类,使用 stb_truetype 加载 TTF/OTF 字体并计算度量信息 - 新增 Music 类,基于 SDL_mixer 实现音频播放、暂停、停止等功能 - 为 AssetSystem 和 AssetsModule 添加字体和音乐的存储、加载器注册及缓存支持 - 在 hello_world 示例中演示字体和音乐的加载与使用 - 更新 extra2d.h 头文件以包含新增的资产模块头文件
This commit is contained in:
parent
1097aeae6c
commit
81bc7ae030
|
|
@ -1,8 +1,52 @@
|
||||||
#include <extra2d.h>
|
#include <extra2d.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fstream>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using namespace extra2d;
|
using namespace extra2d;
|
||||||
|
|
||||||
|
static bool writeTestWav(const std::string &path) {
|
||||||
|
const int sampleRate = 22050;
|
||||||
|
const int channels = 1;
|
||||||
|
const int bitsPerSample = 16;
|
||||||
|
const int durationMs = 250;
|
||||||
|
const int sampleCount = sampleRate * durationMs / 1000;
|
||||||
|
const int byteRate = sampleRate * channels * bitsPerSample / 8;
|
||||||
|
const int blockAlign = channels * bitsPerSample / 8;
|
||||||
|
const int dataSize = sampleCount * blockAlign;
|
||||||
|
const int riffSize = 36 + dataSize;
|
||||||
|
|
||||||
|
std::ofstream file(path, std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write("RIFF", 4);
|
||||||
|
file.write(reinterpret_cast<const char *>(&riffSize), 4);
|
||||||
|
file.write("WAVE", 4);
|
||||||
|
file.write("fmt ", 4);
|
||||||
|
const int fmtChunkSize = 16;
|
||||||
|
const short audioFormat = 1;
|
||||||
|
const short numChannels = static_cast<short>(channels);
|
||||||
|
const short bps = static_cast<short>(bitsPerSample);
|
||||||
|
file.write(reinterpret_cast<const char *>(&fmtChunkSize), 4);
|
||||||
|
file.write(reinterpret_cast<const char *>(&audioFormat), 2);
|
||||||
|
file.write(reinterpret_cast<const char *>(&numChannels), 2);
|
||||||
|
file.write(reinterpret_cast<const char *>(&sampleRate), 4);
|
||||||
|
file.write(reinterpret_cast<const char *>(&byteRate), 4);
|
||||||
|
const short ba = static_cast<short>(blockAlign);
|
||||||
|
file.write(reinterpret_cast<const char *>(&ba), 2);
|
||||||
|
file.write(reinterpret_cast<const char *>(&bps), 2);
|
||||||
|
file.write("data", 4);
|
||||||
|
file.write(reinterpret_cast<const char *>(&dataSize), 4);
|
||||||
|
|
||||||
|
std::vector<int16_t> samples(static_cast<size_t>(sampleCount), 0);
|
||||||
|
file.write(reinterpret_cast<const char *>(samples.data()), dataSize);
|
||||||
|
return file.good();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
// 创建应用(自动创建窗口)
|
// 创建应用(自动创建窗口)
|
||||||
auto app = Application::create();
|
auto app = Application::create();
|
||||||
|
|
@ -19,6 +63,30 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
printf("窗口: %dx%d\n", app->getWindowWidth(), app->getWindowHeight());
|
printf("窗口: %dx%d\n", app->getWindowWidth(), app->getWindowHeight());
|
||||||
|
|
||||||
|
AssetsModule *assets = getAssets();
|
||||||
|
if (assets) {
|
||||||
|
Handle<Font> font = assets->load<Font>("assets/font.ttf");
|
||||||
|
if (font.isValid()) {
|
||||||
|
Font *fontPtr = assets->get(font);
|
||||||
|
float lineHeight = fontPtr ? fontPtr->lineHeight(32.0f) : 0.0f;
|
||||||
|
printf("字体加载成功,32px 行高: %.2f\n", lineHeight);
|
||||||
|
} else {
|
||||||
|
printf("字体加载失败\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string testMusicPath = "assets/generated_test.wav";
|
||||||
|
if (writeTestWav(testMusicPath)) {
|
||||||
|
Handle<Music> music = assets->load<Music>(testMusicPath);
|
||||||
|
if (music.isValid()) {
|
||||||
|
printf("音乐加载成功: %s\n", testMusicPath.c_str());
|
||||||
|
} else {
|
||||||
|
printf("音乐加载失败: %s\n", testMusicPath.c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("测试音频文件生成失败\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 运行应用
|
// 运行应用
|
||||||
app->run();
|
app->run();
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 644 KiB |
|
|
@ -3,7 +3,9 @@
|
||||||
#include <assets/asset_storage.h>
|
#include <assets/asset_storage.h>
|
||||||
#include <assets/core/asset_system.h>
|
#include <assets/core/asset_system.h>
|
||||||
#include <assets/asset_loader.h>
|
#include <assets/asset_loader.h>
|
||||||
|
#include <assets/font.h>
|
||||||
#include <assets/handle.h>
|
#include <assets/handle.h>
|
||||||
|
#include <assets/music.h>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -240,6 +242,8 @@ public:
|
||||||
struct Stats {
|
struct Stats {
|
||||||
size_t textureCount = 0;
|
size_t textureCount = 0;
|
||||||
size_t shaderCount = 0;
|
size_t shaderCount = 0;
|
||||||
|
size_t fontCount = 0;
|
||||||
|
size_t musicCount = 0;
|
||||||
size_t materialCount = 0;
|
size_t materialCount = 0;
|
||||||
size_t meshCount = 0;
|
size_t meshCount = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -250,12 +254,16 @@ private:
|
||||||
// 资源存储
|
// 资源存储
|
||||||
AssetStorage<Texture> textures_;
|
AssetStorage<Texture> textures_;
|
||||||
AssetStorage<Shader> shaders_;
|
AssetStorage<Shader> shaders_;
|
||||||
|
AssetStorage<Font> fonts_;
|
||||||
|
AssetStorage<Music> musics_;
|
||||||
AssetStorage<Material> materials_;
|
AssetStorage<Material> materials_;
|
||||||
AssetStorage<Mesh> meshes_;
|
AssetStorage<Mesh> meshes_;
|
||||||
|
|
||||||
// 加载器
|
// 加载器
|
||||||
std::unique_ptr<AssetLoader<Texture>> textureLoader_;
|
std::unique_ptr<AssetLoader<Texture>> textureLoader_;
|
||||||
std::unique_ptr<AssetLoader<Shader>> shaderLoader_;
|
std::unique_ptr<AssetLoader<Shader>> shaderLoader_;
|
||||||
|
std::unique_ptr<AssetLoader<Font>> fontLoader_;
|
||||||
|
std::unique_ptr<AssetLoader<Music>> musicLoader_;
|
||||||
|
|
||||||
// 事件监听器
|
// 事件监听器
|
||||||
std::unique_ptr<events::OnShow::Listener> onShowListener_;
|
std::unique_ptr<events::OnShow::Listener> onShowListener_;
|
||||||
|
|
@ -346,6 +354,10 @@ Handle<Texture> AssetsModule::load<Texture>(const std::string &path);
|
||||||
|
|
||||||
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path);
|
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path);
|
||||||
|
|
||||||
|
template <> Handle<Font> AssetsModule::load<Font>(const std::string &path);
|
||||||
|
|
||||||
|
template <> Handle<Music> AssetsModule::load<Music>(const std::string &path);
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||||||
const std::string &fragPath);
|
const std::string &fragPath);
|
||||||
|
|
@ -361,6 +373,11 @@ Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Handle<Font> AssetsModule::loadFromMemory<Font>(const std::string &key,
|
||||||
|
const uint8_t *data,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
template <typename T> T *AssetsModule::get(Handle<T> handle) { return nullptr; }
|
template <typename T> T *AssetsModule::get(Handle<T> handle) { return nullptr; }
|
||||||
|
|
||||||
template <> inline Texture *AssetsModule::get<Texture>(Handle<Texture> handle) {
|
template <> inline Texture *AssetsModule::get<Texture>(Handle<Texture> handle) {
|
||||||
|
|
@ -371,6 +388,14 @@ template <> inline Shader *AssetsModule::get<Shader>(Handle<Shader> handle) {
|
||||||
return shaders_.get(handle);
|
return shaders_.get(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> inline Font *AssetsModule::get<Font>(Handle<Font> handle) {
|
||||||
|
return fonts_.get(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline Music *AssetsModule::get<Music>(Handle<Music> handle) {
|
||||||
|
return musics_.get(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline Material *AssetsModule::get<Material>(Handle<Material> handle) {
|
inline Material *AssetsModule::get<Material>(Handle<Material> handle) {
|
||||||
return materials_.get(handle);
|
return materials_.get(handle);
|
||||||
|
|
@ -394,6 +419,15 @@ inline Ptr<Shader> AssetsModule::getPtr<Shader>(Handle<Shader> handle) {
|
||||||
return shaders_.getPtr(handle);
|
return shaders_.getPtr(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> inline Ptr<Font> AssetsModule::getPtr<Font>(Handle<Font> handle) {
|
||||||
|
return fonts_.getPtr(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline Ptr<Music> AssetsModule::getPtr<Music>(Handle<Music> handle) {
|
||||||
|
return musics_.getPtr(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline Ptr<Material> AssetsModule::getPtr<Material>(Handle<Material> handle) {
|
inline Ptr<Material> AssetsModule::getPtr<Material>(Handle<Material> handle) {
|
||||||
return materials_.getPtr(handle);
|
return materials_.getPtr(handle);
|
||||||
|
|
@ -417,6 +451,15 @@ inline bool AssetsModule::isValid<Shader>(Handle<Shader> handle) const {
|
||||||
return shaders_.isValid(handle);
|
return shaders_.isValid(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> inline bool AssetsModule::isValid<Font>(Handle<Font> handle) const {
|
||||||
|
return fonts_.isValid(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool AssetsModule::isValid<Music>(Handle<Music> handle) const {
|
||||||
|
return musics_.isValid(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool AssetsModule::isValid<Material>(Handle<Material> handle) const {
|
inline bool AssetsModule::isValid<Material>(Handle<Material> handle) const {
|
||||||
return materials_.isValid(handle);
|
return materials_.isValid(handle);
|
||||||
|
|
@ -440,6 +483,16 @@ inline bool AssetsModule::isLoading<Shader>(Handle<Shader> handle) const {
|
||||||
return shaders_.isLoading(handle);
|
return shaders_.isLoading(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool AssetsModule::isLoading<Font>(Handle<Font> handle) const {
|
||||||
|
return fonts_.isLoading(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool AssetsModule::isLoading<Music>(Handle<Music> handle) const {
|
||||||
|
return musics_.isLoading(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool AssetsModule::isLoading<Material>(Handle<Material> handle) const {
|
inline bool AssetsModule::isLoading<Material>(Handle<Material> handle) const {
|
||||||
return materials_.isLoading(handle);
|
return materials_.isLoading(handle);
|
||||||
|
|
@ -463,6 +516,16 @@ inline bool AssetsModule::isLoaded<Shader>(Handle<Shader> handle) const {
|
||||||
return shaders_.isLoaded(handle);
|
return shaders_.isLoaded(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool AssetsModule::isLoaded<Font>(Handle<Font> handle) const {
|
||||||
|
return fonts_.isLoaded(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool AssetsModule::isLoaded<Music>(Handle<Music> handle) const {
|
||||||
|
return musics_.isLoaded(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool AssetsModule::isLoaded<Material>(Handle<Material> handle) const {
|
inline bool AssetsModule::isLoaded<Material>(Handle<Material> handle) const {
|
||||||
return materials_.isLoaded(handle);
|
return materials_.isLoaded(handle);
|
||||||
|
|
@ -486,6 +549,16 @@ inline AssetLoadState AssetsModule::getLoadState<Shader>(Handle<Shader> handle)
|
||||||
return shaders_.getLoadState(handle);
|
return shaders_.getLoadState(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline AssetLoadState AssetsModule::getLoadState<Font>(Handle<Font> handle) const {
|
||||||
|
return fonts_.getLoadState(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline AssetLoadState AssetsModule::getLoadState<Music>(Handle<Music> handle) const {
|
||||||
|
return musics_.getLoadState(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline AssetLoadState AssetsModule::getLoadState<Material>(Handle<Material> handle) const {
|
inline AssetLoadState AssetsModule::getLoadState<Material>(Handle<Material> handle) const {
|
||||||
return materials_.getLoadState(handle);
|
return materials_.getLoadState(handle);
|
||||||
|
|
@ -505,6 +578,14 @@ template <> inline void AssetsModule::remove<Shader>(Handle<Shader> handle) {
|
||||||
shaders_.remove(handle);
|
shaders_.remove(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> inline void AssetsModule::remove<Font>(Handle<Font> handle) {
|
||||||
|
fonts_.remove(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline void AssetsModule::remove<Music>(Handle<Music> handle) {
|
||||||
|
musics_.remove(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void AssetsModule::remove<Material>(Handle<Material> handle) {
|
inline void AssetsModule::remove<Material>(Handle<Material> handle) {
|
||||||
materials_.remove(handle);
|
materials_.remove(handle);
|
||||||
|
|
@ -528,6 +609,16 @@ inline uint32_t AssetsModule::getRefCount<Shader>(Handle<Shader> handle) const {
|
||||||
return shaders_.getRefCount(handle);
|
return shaders_.getRefCount(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline uint32_t AssetsModule::getRefCount<Font>(Handle<Font> handle) const {
|
||||||
|
return fonts_.getRefCount(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline uint32_t AssetsModule::getRefCount<Music>(Handle<Music> handle) const {
|
||||||
|
return musics_.getRefCount(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline uint32_t AssetsModule::getRefCount<Material>(Handle<Material> handle) const {
|
inline uint32_t AssetsModule::getRefCount<Material>(Handle<Material> handle) const {
|
||||||
return materials_.getRefCount(handle);
|
return materials_.getRefCount(handle);
|
||||||
|
|
@ -555,6 +646,18 @@ AssetsModule::findUnloadableResources<Shader>(int maxAgeSeconds) const {
|
||||||
return shaders_.findUnloadableResources(maxAgeSeconds);
|
return shaders_.findUnloadableResources(maxAgeSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::vector<Handle<Font>>
|
||||||
|
AssetsModule::findUnloadableResources<Font>(int maxAgeSeconds) const {
|
||||||
|
return fonts_.findUnloadableResources(maxAgeSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::vector<Handle<Music>>
|
||||||
|
AssetsModule::findUnloadableResources<Music>(int maxAgeSeconds) const {
|
||||||
|
return musics_.findUnloadableResources(maxAgeSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline std::vector<Handle<Material>>
|
inline std::vector<Handle<Material>>
|
||||||
AssetsModule::findUnloadableResources<Material>(int maxAgeSeconds) const {
|
AssetsModule::findUnloadableResources<Material>(int maxAgeSeconds) const {
|
||||||
|
|
@ -581,6 +684,16 @@ inline bool AssetsModule::unloadResource<Shader>(Handle<Shader> handle) {
|
||||||
return shaders_.unloadResource(handle);
|
return shaders_.unloadResource(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool AssetsModule::unloadResource<Font>(Handle<Font> handle) {
|
||||||
|
return fonts_.unloadResource(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool AssetsModule::unloadResource<Music>(Handle<Music> handle) {
|
||||||
|
return musics_.unloadResource(handle);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool AssetsModule::unloadResource<Material>(Handle<Material> handle) {
|
inline bool AssetsModule::unloadResource<Material>(Handle<Material> handle) {
|
||||||
return materials_.unloadResource(handle);
|
return materials_.unloadResource(handle);
|
||||||
|
|
@ -606,6 +719,16 @@ inline size_t AssetsModule::unloadOldResources<Shader>(int maxCount, int maxAgeS
|
||||||
return shaders_.unloadOldResources(maxCount, maxAgeSeconds);
|
return shaders_.unloadOldResources(maxCount, maxAgeSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline size_t AssetsModule::unloadOldResources<Font>(int maxCount, int maxAgeSeconds) {
|
||||||
|
return fonts_.unloadOldResources(maxCount, maxAgeSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline size_t AssetsModule::unloadOldResources<Music>(int maxCount, int maxAgeSeconds) {
|
||||||
|
return musics_.unloadOldResources(maxCount, maxAgeSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline size_t AssetsModule::unloadOldResources<Material>(int maxCount, int maxAgeSeconds) {
|
inline size_t AssetsModule::unloadOldResources<Material>(int maxCount, int maxAgeSeconds) {
|
||||||
return materials_.unloadOldResources(maxCount, maxAgeSeconds);
|
return materials_.unloadOldResources(maxCount, maxAgeSeconds);
|
||||||
|
|
@ -651,4 +774,12 @@ template <>
|
||||||
void AssetsModule::registerLoader<Shader>(
|
void AssetsModule::registerLoader<Shader>(
|
||||||
std::unique_ptr<AssetLoader<Shader>> loader);
|
std::unique_ptr<AssetLoader<Shader>> loader);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void AssetsModule::registerLoader<Font>(
|
||||||
|
std::unique_ptr<AssetLoader<Font>> loader);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void AssetsModule::registerLoader<Music>(
|
||||||
|
std::unique_ptr<AssetLoader<Music>> loader);
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
#include <assets/builtin/builtin_asset_factory.h>
|
#include <assets/builtin/builtin_asset_factory.h>
|
||||||
#include <assets/cache/asset_cache.h>
|
#include <assets/cache/asset_cache.h>
|
||||||
#include <assets/dependency/asset_dependency_graph.h>
|
#include <assets/dependency/asset_dependency_graph.h>
|
||||||
|
#include <assets/font.h>
|
||||||
#include <assets/io/asset_file_system.h>
|
#include <assets/io/asset_file_system.h>
|
||||||
|
#include <assets/music.h>
|
||||||
#include <assets/runtime/asset_async_runtime.h>
|
#include <assets/runtime/asset_async_runtime.h>
|
||||||
#include <assets/runtime/asset_hot_reload_runtime.h>
|
#include <assets/runtime/asset_hot_reload_runtime.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
@ -22,14 +24,19 @@ public:
|
||||||
struct Stats {
|
struct Stats {
|
||||||
size_t textureCount = 0;
|
size_t textureCount = 0;
|
||||||
size_t shaderCount = 0;
|
size_t shaderCount = 0;
|
||||||
|
size_t fontCount = 0;
|
||||||
|
size_t musicCount = 0;
|
||||||
size_t materialCount = 0;
|
size_t materialCount = 0;
|
||||||
size_t meshCount = 0;
|
size_t meshCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
AssetSystem(AssetStorage<Texture> &textures, AssetStorage<Shader> &shaders,
|
AssetSystem(AssetStorage<Texture> &textures, AssetStorage<Shader> &shaders,
|
||||||
|
AssetStorage<Font> &fonts, AssetStorage<Music> &musics,
|
||||||
AssetStorage<Material> &materials, AssetStorage<Mesh> &meshes,
|
AssetStorage<Material> &materials, AssetStorage<Mesh> &meshes,
|
||||||
std::unique_ptr<AssetLoader<Texture>> &textureLoader,
|
std::unique_ptr<AssetLoader<Texture>> &textureLoader,
|
||||||
std::unique_ptr<AssetLoader<Shader>> &shaderLoader);
|
std::unique_ptr<AssetLoader<Shader>> &shaderLoader,
|
||||||
|
std::unique_ptr<AssetLoader<Font>> &fontLoader,
|
||||||
|
std::unique_ptr<AssetLoader<Music>> &musicLoader);
|
||||||
|
|
||||||
Handle<Texture> loadTexture(const std::string &path);
|
Handle<Texture> loadTexture(const std::string &path);
|
||||||
Handle<Texture> loadTextureFromMemory(const std::string &key,
|
Handle<Texture> loadTextureFromMemory(const std::string &key,
|
||||||
|
|
@ -40,6 +47,10 @@ public:
|
||||||
Handle<Shader> loadShader(const std::string &path);
|
Handle<Shader> loadShader(const std::string &path);
|
||||||
Handle<Shader> loadShader(const std::string &vertPath,
|
Handle<Shader> loadShader(const std::string &vertPath,
|
||||||
const std::string &fragPath);
|
const std::string &fragPath);
|
||||||
|
Handle<Font> loadFont(const std::string &path);
|
||||||
|
Handle<Font> loadFontFromMemory(const std::string &key, const uint8_t *data,
|
||||||
|
size_t size);
|
||||||
|
Handle<Music> loadMusic(const std::string &path);
|
||||||
|
|
||||||
void loadTextureAsync(const std::string &path,
|
void loadTextureAsync(const std::string &path,
|
||||||
std::function<void(Handle<Texture>)> callback);
|
std::function<void(Handle<Texture>)> callback);
|
||||||
|
|
@ -51,6 +62,8 @@ public:
|
||||||
|
|
||||||
void registerTextureLoader(std::unique_ptr<AssetLoader<Texture>> loader);
|
void registerTextureLoader(std::unique_ptr<AssetLoader<Texture>> loader);
|
||||||
void registerShaderLoader(std::unique_ptr<AssetLoader<Shader>> loader);
|
void registerShaderLoader(std::unique_ptr<AssetLoader<Shader>> loader);
|
||||||
|
void registerFontLoader(std::unique_ptr<AssetLoader<Font>> loader);
|
||||||
|
void registerMusicLoader(std::unique_ptr<AssetLoader<Music>> loader);
|
||||||
|
|
||||||
bool createDefaults();
|
bool createDefaults();
|
||||||
void destroyDefaults();
|
void destroyDefaults();
|
||||||
|
|
@ -77,14 +90,20 @@ private:
|
||||||
|
|
||||||
AssetStorage<Texture> &textures_;
|
AssetStorage<Texture> &textures_;
|
||||||
AssetStorage<Shader> &shaders_;
|
AssetStorage<Shader> &shaders_;
|
||||||
|
AssetStorage<Font> &fonts_;
|
||||||
|
AssetStorage<Music> &musics_;
|
||||||
AssetStorage<Material> &materials_;
|
AssetStorage<Material> &materials_;
|
||||||
AssetStorage<Mesh> &meshes_;
|
AssetStorage<Mesh> &meshes_;
|
||||||
|
|
||||||
std::unique_ptr<AssetLoader<Texture>> &textureLoader_;
|
std::unique_ptr<AssetLoader<Texture>> &textureLoader_;
|
||||||
std::unique_ptr<AssetLoader<Shader>> &shaderLoader_;
|
std::unique_ptr<AssetLoader<Shader>> &shaderLoader_;
|
||||||
|
std::unique_ptr<AssetLoader<Font>> &fontLoader_;
|
||||||
|
std::unique_ptr<AssetLoader<Music>> &musicLoader_;
|
||||||
|
|
||||||
AssetCache<Texture> textureCache_;
|
AssetCache<Texture> textureCache_;
|
||||||
AssetCache<Shader> shaderCache_;
|
AssetCache<Shader> shaderCache_;
|
||||||
|
AssetCache<Font> fontCache_;
|
||||||
|
AssetCache<Music> musicCache_;
|
||||||
AssetDependencyGraph dependencyGraph_;
|
AssetDependencyGraph dependencyGraph_;
|
||||||
AssetAsyncRuntime asyncRuntime_;
|
AssetAsyncRuntime asyncRuntime_;
|
||||||
AssetHotReloadRuntime hotReloadRuntime_;
|
AssetHotReloadRuntime hotReloadRuntime_;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <types/ptr/ref_counted.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class Font : public RefCounted {
|
||||||
|
public:
|
||||||
|
Font();
|
||||||
|
~Font() override;
|
||||||
|
|
||||||
|
bool loadFromFile(const std::string &path);
|
||||||
|
bool loadFromMemory(const uint8_t *data, size_t size);
|
||||||
|
|
||||||
|
bool isLoaded() const { return loaded_; }
|
||||||
|
int ascent() const { return ascent_; }
|
||||||
|
int descent() const { return descent_; }
|
||||||
|
int lineGap() const { return lineGap_; }
|
||||||
|
float scaleForPixelHeight(float pixelHeight) const;
|
||||||
|
float lineHeight(float pixelHeight) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8_t> data_;
|
||||||
|
bool loaded_ = false;
|
||||||
|
int ascent_ = 0;
|
||||||
|
int descent_ = 0;
|
||||||
|
int lineGap_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/asset_loader.h>
|
||||||
|
#include <assets/font.h>
|
||||||
|
#include <assets/io/asset_file_system.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class FontLoader : public AssetLoader<Font> {
|
||||||
|
public:
|
||||||
|
FontLoader() = default;
|
||||||
|
explicit FontLoader(AssetFileSystem fileSystem);
|
||||||
|
|
||||||
|
Ptr<Font> load(const std::string &path) override;
|
||||||
|
Ptr<Font> loadFromMemory(const uint8_t *data, size_t size) override;
|
||||||
|
std::vector<std::string> getExtensions() const override {
|
||||||
|
return {".ttf", ".otf", ".ttc"};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AssetFileSystem fileSystem_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/asset_loader.h>
|
||||||
|
#include <assets/io/asset_file_system.h>
|
||||||
|
#include <assets/music.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class MusicLoader : public AssetLoader<Music> {
|
||||||
|
public:
|
||||||
|
MusicLoader() = default;
|
||||||
|
explicit MusicLoader(AssetFileSystem fileSystem);
|
||||||
|
|
||||||
|
Ptr<Music> load(const std::string &path) override;
|
||||||
|
Ptr<Music> loadFromMemory(const uint8_t *data, size_t size) override;
|
||||||
|
std::vector<std::string> getExtensions() const override {
|
||||||
|
return {".mp3", ".ogg", ".wav", ".flac", ".mod"};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AssetFileSystem fileSystem_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <types/ptr/ref_counted.h>
|
||||||
|
|
||||||
|
struct Mix_Music;
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class Music : public RefCounted {
|
||||||
|
public:
|
||||||
|
Music();
|
||||||
|
~Music() override;
|
||||||
|
|
||||||
|
bool loadFromFile(const std::string &path);
|
||||||
|
void unload();
|
||||||
|
|
||||||
|
bool play(int loops = -1);
|
||||||
|
void stop();
|
||||||
|
void pause();
|
||||||
|
void resume();
|
||||||
|
|
||||||
|
bool isLoaded() const { return music_ != nullptr; }
|
||||||
|
bool isPlaying() const;
|
||||||
|
|
||||||
|
static bool initAudio();
|
||||||
|
static void shutdownAudio();
|
||||||
|
static bool isAudioReady();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mix_Music *music_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -42,6 +42,9 @@
|
||||||
#include <config/window_config.h>
|
#include <config/window_config.h>
|
||||||
|
|
||||||
// Renderer System
|
// Renderer System
|
||||||
|
#include <assets/assets_module.h>
|
||||||
|
#include <assets/font.h>
|
||||||
|
#include <assets/music.h>
|
||||||
#include <renderer/render_types.h>
|
#include <renderer/render_types.h>
|
||||||
#include <renderer/renderer_module.h>
|
#include <renderer/renderer_module.h>
|
||||||
#include <renderer/shader.h>
|
#include <renderer/shader.h>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#include <assets/assets_module.h>
|
#include <assets/assets_module.h>
|
||||||
|
#include <assets/loaders/font_loader.h>
|
||||||
|
#include <assets/loaders/music_loader.h>
|
||||||
#include <assets/loaders/shader_loader.h>
|
#include <assets/loaders/shader_loader.h>
|
||||||
#include <assets/loaders/texture_loader.h>
|
#include <assets/loaders/texture_loader.h>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
|
|
@ -20,8 +22,11 @@ bool AssetsModule::init() {
|
||||||
E2D_INFO("资源模块正在初始化...");
|
E2D_INFO("资源模块正在初始化...");
|
||||||
textureLoader_ = std::make_unique<TextureLoader>();
|
textureLoader_ = std::make_unique<TextureLoader>();
|
||||||
shaderLoader_ = std::make_unique<ShaderLoader>();
|
shaderLoader_ = std::make_unique<ShaderLoader>();
|
||||||
system_ = std::make_unique<AssetSystem>(textures_, shaders_, materials_, meshes_,
|
fontLoader_ = std::make_unique<FontLoader>();
|
||||||
textureLoader_, shaderLoader_);
|
musicLoader_ = std::make_unique<MusicLoader>();
|
||||||
|
system_ = std::make_unique<AssetSystem>(
|
||||||
|
textures_, shaders_, fonts_, musics_, materials_, meshes_, textureLoader_,
|
||||||
|
shaderLoader_, fontLoader_, musicLoader_);
|
||||||
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
||||||
onShowListener_->bind([this]() { this->onGLContextReady(); });
|
onShowListener_->bind([this]() { this->onGLContextReady(); });
|
||||||
E2D_INFO("资源模块初始化成功 (V2)");
|
E2D_INFO("资源模块初始化成功 (V2)");
|
||||||
|
|
@ -37,10 +42,15 @@ void AssetsModule::shutdown() {
|
||||||
}
|
}
|
||||||
textures_.clear();
|
textures_.clear();
|
||||||
shaders_.clear();
|
shaders_.clear();
|
||||||
|
fonts_.clear();
|
||||||
|
musics_.clear();
|
||||||
materials_.clear();
|
materials_.clear();
|
||||||
meshes_.clear();
|
meshes_.clear();
|
||||||
textureLoader_.reset();
|
textureLoader_.reset();
|
||||||
shaderLoader_.reset();
|
shaderLoader_.reset();
|
||||||
|
fontLoader_.reset();
|
||||||
|
musicLoader_.reset();
|
||||||
|
Music::shutdownAudio();
|
||||||
defaultResourcesCreated_ = false;
|
defaultResourcesCreated_ = false;
|
||||||
E2D_INFO("资源模块关闭完成");
|
E2D_INFO("资源模块关闭完成");
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +92,20 @@ template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
||||||
return system_->loadShader(path);
|
return system_->loadShader(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> Handle<Font> AssetsModule::load<Font>(const std::string &path) {
|
||||||
|
if (!system_) {
|
||||||
|
return Handle<Font>::invalid();
|
||||||
|
}
|
||||||
|
return system_->loadFont(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> Handle<Music> AssetsModule::load<Music>(const std::string &path) {
|
||||||
|
if (!system_) {
|
||||||
|
return Handle<Music>::invalid();
|
||||||
|
}
|
||||||
|
return system_->loadMusic(path);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||||||
const std::string &fragPath) {
|
const std::string &fragPath) {
|
||||||
|
|
@ -91,6 +115,16 @@ Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||||||
return system_->loadShader(vertPath, fragPath);
|
return system_->loadShader(vertPath, fragPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Handle<Font> AssetsModule::loadFromMemory<Font>(const std::string &key,
|
||||||
|
const uint8_t *data,
|
||||||
|
size_t size) {
|
||||||
|
if (!system_) {
|
||||||
|
return Handle<Font>::invalid();
|
||||||
|
}
|
||||||
|
return system_->loadFontFromMemory(key, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::vector<Handle<Texture>>
|
std::vector<Handle<Texture>>
|
||||||
AssetsModule::loadDir<Texture>(const std::string &directory,
|
AssetsModule::loadDir<Texture>(const std::string &directory,
|
||||||
|
|
@ -140,6 +174,25 @@ void AssetsModule::registerLoader<Shader>(
|
||||||
system_->registerShaderLoader(std::move(loader));
|
system_->registerShaderLoader(std::move(loader));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void AssetsModule::registerLoader<Font>(std::unique_ptr<AssetLoader<Font>> loader) {
|
||||||
|
if (!system_) {
|
||||||
|
fontLoader_ = std::move(loader);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
system_->registerFontLoader(std::move(loader));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void AssetsModule::registerLoader<Music>(
|
||||||
|
std::unique_ptr<AssetLoader<Music>> loader) {
|
||||||
|
if (!system_) {
|
||||||
|
musicLoader_ = std::move(loader);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
system_->registerMusicLoader(std::move(loader));
|
||||||
|
}
|
||||||
|
|
||||||
bool AssetsModule::createDefaultResources() {
|
bool AssetsModule::createDefaultResources() {
|
||||||
if (!system_) {
|
if (!system_) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -271,10 +324,11 @@ AssetsModule::Stats AssetsModule::getStats() const {
|
||||||
AssetSystem::Stats systemStats = system_->stats();
|
AssetSystem::Stats systemStats = system_->stats();
|
||||||
stats.textureCount = systemStats.textureCount;
|
stats.textureCount = systemStats.textureCount;
|
||||||
stats.shaderCount = systemStats.shaderCount;
|
stats.shaderCount = systemStats.shaderCount;
|
||||||
|
stats.fontCount = systemStats.fontCount;
|
||||||
|
stats.musicCount = systemStats.musicCount;
|
||||||
stats.materialCount = systemStats.materialCount;
|
stats.materialCount = systemStats.materialCount;
|
||||||
stats.meshCount = systemStats.meshCount;
|
stats.meshCount = systemStats.meshCount;
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,18 @@ namespace extra2d {
|
||||||
|
|
||||||
AssetSystem::AssetSystem(AssetStorage<Texture> &textures,
|
AssetSystem::AssetSystem(AssetStorage<Texture> &textures,
|
||||||
AssetStorage<Shader> &shaders,
|
AssetStorage<Shader> &shaders,
|
||||||
|
AssetStorage<Font> &fonts,
|
||||||
|
AssetStorage<Music> &musics,
|
||||||
AssetStorage<Material> &materials,
|
AssetStorage<Material> &materials,
|
||||||
AssetStorage<Mesh> &meshes,
|
AssetStorage<Mesh> &meshes,
|
||||||
std::unique_ptr<AssetLoader<Texture>> &textureLoader,
|
std::unique_ptr<AssetLoader<Texture>> &textureLoader,
|
||||||
std::unique_ptr<AssetLoader<Shader>> &shaderLoader)
|
std::unique_ptr<AssetLoader<Shader>> &shaderLoader,
|
||||||
: textures_(textures), shaders_(shaders), materials_(materials), meshes_(meshes),
|
std::unique_ptr<AssetLoader<Font>> &fontLoader,
|
||||||
textureLoader_(textureLoader), shaderLoader_(shaderLoader),
|
std::unique_ptr<AssetLoader<Music>> &musicLoader)
|
||||||
|
: textures_(textures), shaders_(shaders), fonts_(fonts), musics_(musics),
|
||||||
|
materials_(materials), meshes_(meshes), textureLoader_(textureLoader),
|
||||||
|
shaderLoader_(shaderLoader), fontLoader_(fontLoader),
|
||||||
|
musicLoader_(musicLoader),
|
||||||
hotReloadRuntime_(fileSystem_),
|
hotReloadRuntime_(fileSystem_),
|
||||||
builtinFactory_(textures, shaders, materials, meshes, fileSystem_) {}
|
builtinFactory_(textures, shaders, materials, meshes, fileSystem_) {}
|
||||||
|
|
||||||
|
|
@ -139,6 +145,58 @@ Handle<Shader> AssetSystem::loadShader(const std::string &vertPath,
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle<Font> AssetSystem::loadFont(const std::string &path) {
|
||||||
|
Handle<Font> cached = fontCache_.find(path);
|
||||||
|
if (cached.isValid() && fonts_.isValid(cached)) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
if (!fontLoader_) {
|
||||||
|
return Handle<Font>::invalid();
|
||||||
|
}
|
||||||
|
Ptr<Font> font = fontLoader_->load(path);
|
||||||
|
if (!font) {
|
||||||
|
return Handle<Font>::invalid();
|
||||||
|
}
|
||||||
|
Handle<Font> handle = fonts_.insert(font);
|
||||||
|
fontCache_.set(path, handle);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Font> AssetSystem::loadFontFromMemory(const std::string &key,
|
||||||
|
const uint8_t *data, size_t size) {
|
||||||
|
Handle<Font> cached = fontCache_.find(key);
|
||||||
|
if (cached.isValid() && fonts_.isValid(cached)) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
if (!fontLoader_) {
|
||||||
|
return Handle<Font>::invalid();
|
||||||
|
}
|
||||||
|
Ptr<Font> font = fontLoader_->loadFromMemory(data, size);
|
||||||
|
if (!font) {
|
||||||
|
return Handle<Font>::invalid();
|
||||||
|
}
|
||||||
|
Handle<Font> handle = fonts_.insert(font);
|
||||||
|
fontCache_.set(key, handle);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Music> AssetSystem::loadMusic(const std::string &path) {
|
||||||
|
Handle<Music> cached = musicCache_.find(path);
|
||||||
|
if (cached.isValid() && musics_.isValid(cached)) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
if (!musicLoader_) {
|
||||||
|
return Handle<Music>::invalid();
|
||||||
|
}
|
||||||
|
Ptr<Music> music = musicLoader_->load(path);
|
||||||
|
if (!music) {
|
||||||
|
return Handle<Music>::invalid();
|
||||||
|
}
|
||||||
|
Handle<Music> handle = musics_.insert(music);
|
||||||
|
musicCache_.set(path, handle);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
void AssetSystem::initAsync(uint32_t threadCount) {
|
void AssetSystem::initAsync(uint32_t threadCount) {
|
||||||
if (asyncRuntime_.running()) {
|
if (asyncRuntime_.running()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -198,6 +256,14 @@ void AssetSystem::registerShaderLoader(std::unique_ptr<AssetLoader<Shader>> load
|
||||||
shaderLoader_ = std::move(loader);
|
shaderLoader_ = std::move(loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssetSystem::registerFontLoader(std::unique_ptr<AssetLoader<Font>> loader) {
|
||||||
|
fontLoader_ = std::move(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetSystem::registerMusicLoader(std::unique_ptr<AssetLoader<Music>> loader) {
|
||||||
|
musicLoader_ = std::move(loader);
|
||||||
|
}
|
||||||
|
|
||||||
bool AssetSystem::createDefaults() { return builtinFactory_.create(); }
|
bool AssetSystem::createDefaults() { return builtinFactory_.create(); }
|
||||||
|
|
||||||
void AssetSystem::destroyDefaults() { builtinFactory_.destroy(); }
|
void AssetSystem::destroyDefaults() { builtinFactory_.destroy(); }
|
||||||
|
|
@ -299,12 +365,16 @@ void AssetSystem::clear() {
|
||||||
dependencyGraph_.clear();
|
dependencyGraph_.clear();
|
||||||
textureCache_.clear();
|
textureCache_.clear();
|
||||||
shaderCache_.clear();
|
shaderCache_.clear();
|
||||||
|
fontCache_.clear();
|
||||||
|
musicCache_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetSystem::Stats AssetSystem::stats() const {
|
AssetSystem::Stats AssetSystem::stats() const {
|
||||||
Stats s;
|
Stats s;
|
||||||
s.textureCount = textures_.count();
|
s.textureCount = textures_.count();
|
||||||
s.shaderCount = shaders_.count();
|
s.shaderCount = shaders_.count();
|
||||||
|
s.fontCount = fonts_.count();
|
||||||
|
s.musicCount = musics_.count();
|
||||||
s.materialCount = materials_.count();
|
s.materialCount = materials_.count();
|
||||||
s.meshCount = meshes_.count();
|
s.meshCount = meshes_.count();
|
||||||
return s;
|
return s;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
#include <assets/font.h>
|
||||||
|
#include <fstream>
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include <stb/stb_truetype.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
Font::Font() = default;
|
||||||
|
|
||||||
|
Font::~Font() = default;
|
||||||
|
|
||||||
|
bool Font::loadFromFile(const std::string &path) {
|
||||||
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
E2D_ERROR("Font: 打开字体文件失败: {}", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
if (size <= 0) {
|
||||||
|
E2D_ERROR("Font: 字体文件为空: {}", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> buffer(static_cast<size_t>(size));
|
||||||
|
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
|
||||||
|
E2D_ERROR("Font: 读取字体文件失败: {}", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadFromMemory(buffer.data(), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Font::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
|
if (!data || size == 0) {
|
||||||
|
E2D_ERROR("Font: 无效的内存数据");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stbtt_fontinfo info;
|
||||||
|
if (!stbtt_InitFont(&info, data, stbtt_GetFontOffsetForIndex(data, 0))) {
|
||||||
|
E2D_ERROR("Font: 初始化字体失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ascent = 0;
|
||||||
|
int descent = 0;
|
||||||
|
int lineGap = 0;
|
||||||
|
stbtt_GetFontVMetrics(&info, &ascent, &descent, &lineGap);
|
||||||
|
|
||||||
|
data_.assign(data, data + size);
|
||||||
|
loaded_ = true;
|
||||||
|
ascent_ = ascent;
|
||||||
|
descent_ = descent;
|
||||||
|
lineGap_ = lineGap;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Font::scaleForPixelHeight(float pixelHeight) const {
|
||||||
|
if (!loaded_ || pixelHeight <= 0.0f) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
stbtt_fontinfo info;
|
||||||
|
if (!stbtt_InitFont(&info, data_.data(),
|
||||||
|
stbtt_GetFontOffsetForIndex(data_.data(), 0))) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
return stbtt_ScaleForPixelHeight(&info, pixelHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Font::lineHeight(float pixelHeight) const {
|
||||||
|
float scale = scaleForPixelHeight(pixelHeight);
|
||||||
|
if (scale <= 0.0f) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
return static_cast<float>(ascent_ - descent_ + lineGap_) * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include <assets/loaders/font_loader.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
FontLoader::FontLoader(AssetFileSystem fileSystem)
|
||||||
|
: fileSystem_(std::move(fileSystem)) {}
|
||||||
|
|
||||||
|
Ptr<Font> FontLoader::load(const std::string &path) {
|
||||||
|
std::string resolved = fileSystem_.assetPath(path);
|
||||||
|
Ptr<Font> font = makePtr<Font>();
|
||||||
|
if (!font->loadFromFile(resolved)) {
|
||||||
|
E2D_ERROR("FontLoader: 加载字体失败: {}", resolved);
|
||||||
|
return Ptr<Font>();
|
||||||
|
}
|
||||||
|
E2D_DEBUG("FontLoader: 已加载字体 {}", resolved);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Font> FontLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
|
Ptr<Font> font = makePtr<Font>();
|
||||||
|
if (!font->loadFromMemory(data, size)) {
|
||||||
|
E2D_ERROR("FontLoader: 从内存加载字体失败");
|
||||||
|
return Ptr<Font>();
|
||||||
|
}
|
||||||
|
E2D_DEBUG("FontLoader: 已从内存加载字体");
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include <assets/loaders/music_loader.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
MusicLoader::MusicLoader(AssetFileSystem fileSystem)
|
||||||
|
: fileSystem_(std::move(fileSystem)) {}
|
||||||
|
|
||||||
|
Ptr<Music> MusicLoader::load(const std::string &path) {
|
||||||
|
std::string resolved = fileSystem_.assetPath(path);
|
||||||
|
Ptr<Music> music = makePtr<Music>();
|
||||||
|
if (!music->loadFromFile(resolved)) {
|
||||||
|
E2D_ERROR("MusicLoader: 加载音乐失败: {}", resolved);
|
||||||
|
return Ptr<Music>();
|
||||||
|
}
|
||||||
|
E2D_DEBUG("MusicLoader: 已加载音乐 {}", resolved);
|
||||||
|
return music;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Music> MusicLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
|
(void)data;
|
||||||
|
(void)size;
|
||||||
|
E2D_ERROR("MusicLoader: loadFromMemory 暂不支持");
|
||||||
|
return Ptr<Music>();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
#include <assets/music.h>
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_mixer.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
namespace {
|
||||||
|
std::mutex g_audioMutex;
|
||||||
|
bool g_audioReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Music::Music() = default;
|
||||||
|
|
||||||
|
Music::~Music() { unload(); }
|
||||||
|
|
||||||
|
bool Music::initAudio() {
|
||||||
|
std::lock_guard<std::mutex> lock(g_audioMutex);
|
||||||
|
if (g_audioReady) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((SDL_WasInit(SDL_INIT_AUDIO) & SDL_INIT_AUDIO) == 0) {
|
||||||
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
|
||||||
|
E2D_ERROR("Music: 初始化 SDL 音频子系统失败: {}", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int initFlags = MIX_INIT_OGG | MIX_INIT_MP3 | MIX_INIT_FLAC | MIX_INIT_MOD;
|
||||||
|
Mix_Init(initFlags);
|
||||||
|
|
||||||
|
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
|
||||||
|
E2D_ERROR("Music: 打开音频设备失败: {}", Mix_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_audioReady = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Music::shutdownAudio() {
|
||||||
|
std::lock_guard<std::mutex> lock(g_audioMutex);
|
||||||
|
if (!g_audioReady) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Mix_CloseAudio();
|
||||||
|
Mix_Quit();
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
g_audioReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Music::isAudioReady() {
|
||||||
|
std::lock_guard<std::mutex> lock(g_audioMutex);
|
||||||
|
return g_audioReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Music::loadFromFile(const std::string &path) {
|
||||||
|
unload();
|
||||||
|
if (!initAudio()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
music_ = Mix_LoadMUS(path.c_str());
|
||||||
|
if (!music_) {
|
||||||
|
E2D_ERROR("Music: 加载音乐失败: {} ({})", path, Mix_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Music::unload() {
|
||||||
|
if (music_) {
|
||||||
|
Mix_FreeMusic(music_);
|
||||||
|
music_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Music::play(int loops) {
|
||||||
|
if (!music_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Mix_PlayMusic(music_, loops) == -1) {
|
||||||
|
E2D_ERROR("Music: 播放失败: {}", Mix_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Music::stop() { Mix_HaltMusic(); }
|
||||||
|
|
||||||
|
void Music::pause() { Mix_PauseMusic(); }
|
||||||
|
|
||||||
|
void Music::resume() { Mix_ResumeMusic(); }
|
||||||
|
|
||||||
|
bool Music::isPlaying() const { return Mix_PlayingMusic() != 0; }
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
Loading…
Reference in New Issue