Files
DX12/Engine/Content/ContentLoader.cpp
2026-03-19 18:27:49 +08:00

218 lines
5.4 KiB
C++

/**
* @file ContentLoader.cpp
* @brief 关卡内容反序列化与实体创建流程实现。
* @details
* 主要流程为:读取二进制关卡文件 -> 解析组件块 -> 创建实体并绑定脚本/变换。
* 同时提供运行期清理逻辑,确保测试场景退出时能回收创建的实体与资源。
* 本实现仅在非 SHIPPING 且 Win64 条件下启用。
*/
#include "ContentLoader.h"
#include "..\Components\Script.h"
#include "..\EngineAPI\GameEntity.h"
#include "..\EngineAPI\TransformComponent.h"
#include "..\EngineAPI\ScriptComponent.h"
#include "..\Components\Entity.h"
#include "Graphics\Renderer.h"
#include <fstream>
#include <filesystem>
#include <Windows.h>
#if !defined(SHIPPING) && defined(_WIN64)
namespace XEngine::content {
namespace {
/**
* @brief 关卡文件中的组件类型标签。
*/
enum component_type
{
transform,
script,
count
};
/**
* @brief 已创建的实体缓存,用于卸载阶段统一回收。
*/
utl::vector<game_entity::entity> entities;
/**
* @brief 解析变换组件时复用的临时结构。
*/
transform::init_info transform_info{};
/**
* @brief 解析脚本组件时复用的临时结构。
*/
script::init_info script_info{};
/**
* @brief 从二进制流读取变换组件数据。
* @param data 当前读取游标,函数返回后移动到组件末尾。
* @param info 目标实体初始化信息。
* @return 读取成功返回 true。
*/
bool
read_transform(const u8*& data, game_entity::entity_info& info)
{
using namespace DirectX;
f32 rotation[3];
assert(!info.transform);
memcpy(&transform_info.position[0], data, sizeof(transform_info.position)); data += sizeof(transform_info.position);
memcpy(&rotation[0], data, sizeof(rotation)); data += sizeof(rotation);
memcpy(&transform_info.scale[0], data, sizeof(transform_info.scale)); data += sizeof(transform_info.scale);
XMFLOAT3A rot{ &rotation[0] };
XMVECTOR quat{ XMQuaternionRotationRollPitchYawFromVector(XMLoadFloat3A(&rot)) };
XMFLOAT4A rot_quat{};
XMStoreFloat4A(&rot_quat, quat);
memcpy(&transform_info.rotation[0], &rot_quat.x, sizeof(transform_info.rotation));
info.transform = &transform_info;
return true;
}
/**
* @brief 从二进制流读取脚本组件并查找脚本工厂。
* @param data 当前读取游标,函数返回后移动到组件末尾。
* @param info 目标实体初始化信息。
* @return 解析并找到脚本工厂返回 true。
*/
bool
read_script(const u8*& data, game_entity::entity_info& info)
{
assert(!info.script);
const u32 name_length{ *data }; data += sizeof(u32);
if (!name_length) return false;
assert(name_length < 256);
char script_name[256];
memcpy(&script_name, data, name_length); data += name_length;
script_name[name_length] = 0;
script_info.script_creator = script::detail::get_script_creator(script::detail::string_hash()(script_name));
info.script = &script_info;
return script_info.script_creator != nullptr;
}
using compoent_reader = bool(*)(const u8*&, game_entity::entity_info&);
compoent_reader component_readers[]
{
read_transform,
read_script,
};
static_assert(_countof(component_readers) == component_type::count);
/**
* @brief 读取完整二进制文件到内存缓冲区。
* @param path 文件路径。
* @param data 输出缓冲区。
* @param size 输出字节数。
* @return 成功返回 true。
*/
bool
read_file(std::filesystem::path path, std::unique_ptr<u8[]>&data, u64& size)
{
if (!std::filesystem::exists(path)) return false;
size = std::filesystem::file_size(path);
assert(size);
if (!size) return false;
data = std::make_unique<u8[]>(size);
std::ifstream file{ path, std::ios::in | std::ios::binary };
if (!file || !file.read((char*)data.get(), size))
{
file.close();
return false;
}
file.close();
return true;
}
} // namespace
/**
* @brief 加载 game.bin 并批量创建实体。
* @return 全部实体创建成功返回 true。
*/
bool
load_games()
{
std::unique_ptr<u8[]> game_data{};
u64 size{ 0 };
if (!read_file("game.bin", game_data, size)) return false;
assert(game_data.get());
const u8* at{ game_data.get() };
constexpr u32 su32{ sizeof(u32) };
const u32 num_entities{ *at }; at += su32;
if (!num_entities) return false;
for (u32 entity_index{ 0 }; entity_index < num_entities; ++entity_index)
{
game_entity::entity_info info{};
const u32 entity_type{ *at }; at += su32;
const u32 num_components{ *at }; at += su32;
if (!num_components)return false;
for (u32 component_index{ 0 }; component_index < num_components; ++component_index)
{
const u32 component_type{ *at }; at += su32;
assert(component_type < component_type::count);
if (!component_readers[component_type](at, info)) return false;
}
assert(info.transform);
game_entity::entity entity{ game_entity::create(info) };
if (!entity.is_valid()) return false;
entities.emplace_back(entity);
}
assert(at == game_data.get() + size);
return true;
}
/**
* @brief 回收 load_games 创建的实体对象。
*/
void
unload_game()
{
for (auto entity : entities)
{
game_entity::remove(entity.get_id());
}
}
/**
* @brief 加载引擎内置着色器二进制文件。
* @param shaders 输出字节缓冲区。
* @param size 输出字节大小。
* @return 加载成功返回 true。
*/
//bool
//load_engine_shaders(std::unique_ptr<u8[]>&shaders, u64& size)
//{
// auto path = graphics::get_engine_shaders_path();
// return read_file(path, shaders, size);
//}
}
#endif //!defined(SHIPPING)