218 lines
5.4 KiB
C++
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)
|