/** * @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 #include #include #if !defined(SHIPPING) && defined(_WIN64) namespace XEngine::content { namespace { /** * @brief 关卡文件中的组件类型标签。 */ enum component_type { transform, script, count }; /** * @brief 已创建的实体缓存,用于卸载阶段统一回收。 */ utl::vector 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&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(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 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&shaders, u64& size) //{ // auto path = graphics::get_engine_shaders_path(); // return read_file(path, shaders, size); //} } #endif //!defined(SHIPPING)