feat: initial DX12 foundation framework
This commit is contained in:
55
Engine/Common/CommonHeader.h
Normal file
55
Engine/Common/CommonHeader.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN64
|
||||
#pragma warning(disable: 4530)
|
||||
#endif // _WIN64
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <typeinfo>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
|
||||
//#define USESTDFUNC
|
||||
#if defined USESTDFUNC
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN64)
|
||||
#include <DirectXMath.h>
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_COPY
|
||||
#define DISABLE_COPY(T) \
|
||||
explicit T(const T&) = delete; \
|
||||
T& operator=(const T&) = delete;
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_MOVE
|
||||
#define DISABLE_MOVE(T) \
|
||||
explicit T(T&&) = delete; \
|
||||
T& operator=(T&&) = delete
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_COPY_AND_MOVE
|
||||
#define DISABLE_COPY_AND_MOVE(T) DISABLE_COPY(T) DISABLE_MOVE(T);
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_OP(x) x
|
||||
#else
|
||||
#define DEBUG_OP(x)
|
||||
#endif
|
||||
|
||||
//Common Header
|
||||
#include "XEnginType.h"
|
||||
#include "..\Utilities\Math.h"
|
||||
#include "..\Utilities\MathTypes.h"
|
||||
#include "..\Utilities\Utilities.h"
|
||||
#include "Id.h"
|
||||
|
||||
143
Engine/Common/Id.h
Normal file
143
Engine/Common/Id.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @file Id.h
|
||||
* @brief 使用“索引 + 代数”打包方案的 32 位标识符工具。
|
||||
* @details
|
||||
* 位布局:
|
||||
* - 低位:索引
|
||||
* - 高位:代数
|
||||
*
|
||||
* 当索引被复用时,通过递增代数降低悬挂句柄误用风险。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
|
||||
namespace XEngine::id
|
||||
{
|
||||
/**
|
||||
* @brief 标识符相关类型与常量。
|
||||
*/
|
||||
using id_type = u32;
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* @brief 高位代数字段的位宽。
|
||||
*/
|
||||
constexpr u32 generation_bits{ 8 };
|
||||
/**
|
||||
* @brief 低位索引字段的位宽。
|
||||
*/
|
||||
constexpr u32 index_bit{ sizeof(id_type) * 8 - generation_bits };
|
||||
/**
|
||||
* @brief 用于提取索引字段的掩码。
|
||||
*/
|
||||
constexpr id_type index_mask{ (id_type{1} << index_bit) - 1 };
|
||||
/**
|
||||
* @brief 右移后用于提取代数字段的掩码。
|
||||
*/
|
||||
constexpr id_type generation_mask{ (id_type{1} << generation_bits) - 1 };
|
||||
}
|
||||
/**
|
||||
* @brief 表示无效标识符的哨兵值。
|
||||
*/
|
||||
constexpr id_type invalid_id{ (id_type)-1 };
|
||||
/**
|
||||
* @brief 延迟回收删除项时的触发阈值。
|
||||
*/
|
||||
constexpr u32 min_deleted_elements{ 1024 };
|
||||
|
||||
/**
|
||||
* @brief 可容纳代数字段的最小无符号整型。
|
||||
*/
|
||||
using generation_type = std::conditional_t<detail::generation_bits <= 16, std::conditional_t<detail::generation_bits <= 8, u8, u16>, u32>;
|
||||
static_assert(sizeof(generation_type) * 8 >= detail::generation_bits);
|
||||
static_assert((sizeof(id_type) - sizeof(generation_type)) > 0);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 判断标识符是否有效。
|
||||
* @param id 打包后的标识符。
|
||||
* @return 当 @p id 不是无效哨兵值时返回 true。
|
||||
*/
|
||||
constexpr bool
|
||||
is_valid(id_type id)
|
||||
{
|
||||
return id != invalid_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从打包标识符中提取索引字段。
|
||||
* @param id 打包后的标识符。
|
||||
* @return 低位索引字段。
|
||||
* @pre 提取后的索引不能等于保留的全 1 索引值。
|
||||
*/
|
||||
constexpr id_type
|
||||
index(id_type id)
|
||||
{
|
||||
id_type index{ id & detail::index_mask };
|
||||
assert(index != detail::index_mask);
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从打包标识符中提取代数字段。
|
||||
* @param id 打包后的标识符。
|
||||
* @return 高位代数字段。
|
||||
*/
|
||||
constexpr id_type
|
||||
generation(id_type id)
|
||||
{
|
||||
return (id >> detail::index_bit) & detail::generation_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 为同一索引生成下一代标识符。
|
||||
* @param id 当前打包标识符。
|
||||
* @return 索引不变且代数递增后的标识符。
|
||||
* @pre 代数递增后不能溢出到保留范围。
|
||||
*/
|
||||
constexpr id_type
|
||||
new_generation(id_type id)
|
||||
{
|
||||
const id_type generation{ id::generation(id) + 1 };
|
||||
assert(generation < (((u64)1 << detail::generation_bits) - 1));
|
||||
return index(id) | (generation << detail::index_bit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if _DEBUG
|
||||
namespace detail {
|
||||
struct id_base
|
||||
{
|
||||
constexpr explicit id_base(id_type id) : _id(id) {}
|
||||
constexpr operator id_type() const { return _id; }
|
||||
private:
|
||||
id_type _id;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在调试构建下声明强类型标识符。
|
||||
* @details
|
||||
* 调试构建使用派生自 id_base 的包装类型。
|
||||
* 发布构建退化为 id_type 别名以保持零开销抽象。
|
||||
*/
|
||||
#define DEFINE_TYPED_ID(name) \
|
||||
struct name final : id::detail::id_base \
|
||||
{ \
|
||||
constexpr explicit name(id::id_type id) \
|
||||
: id_base{id}{} \
|
||||
constexpr name() : id_base{ 0 }{} \
|
||||
};
|
||||
#else
|
||||
/**
|
||||
* @brief 发布模式下的零运行时开销类型别名。
|
||||
*/
|
||||
#define DEFINE_TYPED_ID(name) using name = id::id_type;
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
23
Engine/Common/XEnginType.h
Normal file
23
Engine/Common/XEnginType.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
|
||||
using s8 = int8_t;
|
||||
using s16 = int16_t;
|
||||
using s32 = int32_t;
|
||||
using s64 = int64_t;
|
||||
|
||||
|
||||
|
||||
constexpr u64 u64_invalid_id{ 0xffffffffffffffff };
|
||||
constexpr u32 u32_invalid_id{ 0xffffffff };
|
||||
constexpr u16 u16_invalid_id{ 0xffff };
|
||||
constexpr u8 u8_invalid_id { 0xff };
|
||||
|
||||
|
||||
using f32 = float;
|
||||
13
Engine/Components/ComponentsCommon.h
Normal file
13
Engine/Components/ComponentsCommon.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @file ComponentsCommon.h
|
||||
* @brief Components 模块公共依赖头。
|
||||
*/
|
||||
#pragma once
|
||||
#include "..\Common\CommonHeader.h"
|
||||
#include "..\EngineAPI\GameEntity.h"
|
||||
#include "..\EngineAPI\TransformComponent.h"
|
||||
#include "..\EngineAPI\ScriptComponent.h"
|
||||
|
||||
namespace XEngine::game_entity {
|
||||
|
||||
}
|
||||
108
Engine/Components/Entity.cpp
Normal file
108
Engine/Components/Entity.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "Entity.h"
|
||||
#include "Script.h"
|
||||
#include "Transform.h"
|
||||
|
||||
namespace XEngine::game_entity {
|
||||
|
||||
namespace {
|
||||
|
||||
utl::vector<transform::component> transforms;
|
||||
utl::vector<script::component> scripts;
|
||||
|
||||
|
||||
utl::vector<id::generation_type> generations;
|
||||
utl::deque<entity_id> free_ids;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
entity
|
||||
create(entity_info info)
|
||||
{
|
||||
assert(info.transform);
|
||||
if (!info.transform) return entity{ };
|
||||
|
||||
entity_id id;
|
||||
|
||||
if (free_ids.size() > id::min_deleted_elements)
|
||||
{
|
||||
id = free_ids.front();
|
||||
assert(!is_alive( id ));
|
||||
free_ids.pop_front();
|
||||
id = entity_id{ id::new_generation(id) };
|
||||
++generations[id::index(id)];
|
||||
}
|
||||
else
|
||||
{
|
||||
id = entity_id{ (id::id_type)generations.size() };
|
||||
generations.push_back(0);
|
||||
|
||||
transforms.emplace_back();
|
||||
scripts.emplace_back();
|
||||
}
|
||||
|
||||
const entity new_entity{ id };
|
||||
const id::id_type index{ id::index(id) };
|
||||
|
||||
|
||||
//Create Transform Component
|
||||
assert(!transforms[index].is_valid());
|
||||
transforms[index] = transform::create(*info.transform, new_entity);
|
||||
if (!transforms[index].is_valid()) return{ };
|
||||
|
||||
|
||||
//Create Script Component
|
||||
if (info.script && info.script->script_creator)
|
||||
{
|
||||
assert(!scripts[index].is_valid());
|
||||
scripts[index] = script::create(*info.script, new_entity);
|
||||
assert(scripts[index].is_valid());
|
||||
}
|
||||
|
||||
|
||||
return new_entity;
|
||||
|
||||
}
|
||||
void
|
||||
remove(entity_id id)
|
||||
{
|
||||
const id::id_type index{ id::index(id) };
|
||||
assert(is_alive(id));
|
||||
if (scripts[index].is_valid())
|
||||
{
|
||||
script::remove(scripts[index]);
|
||||
scripts[index] = {};
|
||||
}
|
||||
transform::remove(transforms[index]);
|
||||
transforms[index] = {};
|
||||
free_ids.push_back(id);
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
is_alive(entity_id id)
|
||||
{
|
||||
assert(id::is_valid(id));
|
||||
const id::id_type index{ id::index(id) };
|
||||
assert(index < generations.size());
|
||||
return (generations[index] == id::generation(id) && transforms[index].is_valid());
|
||||
}
|
||||
|
||||
transform::component
|
||||
entity::transform() const
|
||||
{
|
||||
assert(is_alive(_id));
|
||||
const id::id_type index{ id::index(_id) };
|
||||
return transforms[index];
|
||||
}
|
||||
|
||||
|
||||
script::component
|
||||
entity::script() const
|
||||
{
|
||||
assert(is_alive(_id));
|
||||
const id::id_type index{ id::index(_id) };
|
||||
return scripts[index];
|
||||
}
|
||||
|
||||
}
|
||||
51
Engine/Components/Entity.h
Normal file
51
Engine/Components/Entity.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file Entity.h
|
||||
* @brief 实体组件生命周期管理接口。
|
||||
*/
|
||||
#pragma once
|
||||
#include "ComponentsCommon.h"
|
||||
#include "Transform.h"
|
||||
|
||||
|
||||
namespace XEngine {
|
||||
|
||||
/**
|
||||
* @brief 前置声明组件初始化参数结构。
|
||||
*/
|
||||
#define INIT_INFO(component) namespace component { struct init_info; }
|
||||
|
||||
INIT_INFO(transform);
|
||||
INIT_INFO(script);
|
||||
|
||||
#undef INIT_INFO
|
||||
|
||||
|
||||
namespace game_entity {
|
||||
/**
|
||||
* @brief 创建实体时可选的组件初始化信息。
|
||||
*/
|
||||
struct entity_info
|
||||
{
|
||||
transform::init_info* transform{ nullptr };
|
||||
script::init_info* script{ nullptr };
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 创建实体并按需附加组件。
|
||||
* @param info 实体初始化信息。
|
||||
* @return 新建实体句柄。
|
||||
*/
|
||||
entity create(entity_info info);
|
||||
/**
|
||||
* @brief 删除实体及其关联组件。
|
||||
* @param id 实体标识符。
|
||||
*/
|
||||
void remove(entity_id id);
|
||||
/**
|
||||
* @brief 判断实体是否仍然存活。
|
||||
* @param id 实体标识符。
|
||||
* @return 存活返回 true,否则返回 false。
|
||||
*/
|
||||
bool is_alive(entity_id id);
|
||||
}
|
||||
}
|
||||
367
Engine/Components/Script.cpp
Normal file
367
Engine/Components/Script.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* @file Script.cpp
|
||||
* @brief 脚本组件存储、脚本注册表与逐帧更新实现。
|
||||
* @details
|
||||
* 该文件负责脚本系统运行时核心流程:
|
||||
* - 维护脚本实例数组与 script_id 到实例下标映射;
|
||||
* - 维护脚本工厂注册表,支持按哈希查询创建函数;
|
||||
* - 在脚本回调中缓存变换修改,并在帧末批量提交到 Transform 系统。
|
||||
*/
|
||||
#include "Script.h"
|
||||
#include "Entity.h"
|
||||
#include "Transform.h"
|
||||
|
||||
#define USE_TRANSFORM_CACHE_MAP 1
|
||||
|
||||
|
||||
namespace XEngine::script {
|
||||
namespace {
|
||||
/**
|
||||
* @brief 活跃脚本实例数组。
|
||||
*/
|
||||
utl::vector<detail::script_ptr> entity_scripts;
|
||||
/**
|
||||
* @brief script_id 索引到实例数组下标的映射。
|
||||
*/
|
||||
utl::vector<id::id_type> id_mapping;
|
||||
|
||||
/**
|
||||
* @brief script_id 代数数组。
|
||||
*/
|
||||
utl::vector<id::generation_type> generations;
|
||||
/**
|
||||
* @brief 可复用 script_id 队列。
|
||||
*/
|
||||
utl::deque<script_id> free_ids;
|
||||
|
||||
/**
|
||||
* @brief 脚本对 Transform 的延迟修改缓存。
|
||||
*/
|
||||
utl::vector<transform::component_cache> transform_cache;
|
||||
|
||||
#if USE_TRANSFORM_CACHE_MAP
|
||||
std::unordered_map<id::id_type, u32> cache_map;
|
||||
#endif
|
||||
|
||||
|
||||
using script_registry = std::unordered_map<size_t, detail::script_creator>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取全局脚本工厂注册表。
|
||||
* @return 注册表引用。
|
||||
*/
|
||||
script_registry&
|
||||
registery()
|
||||
{
|
||||
static script_registry reg;
|
||||
return reg;
|
||||
}
|
||||
#ifdef USE_WITH_EDITOR
|
||||
/**
|
||||
* @brief 获取编辑器脚本名集合。
|
||||
* @return 脚本名数组引用。
|
||||
*/
|
||||
utl::vector<std::string>&
|
||||
script_names()
|
||||
{
|
||||
static utl::vector<std::string> names;
|
||||
return names;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 判断脚本组件是否存在且有效。
|
||||
* @param id 脚本组件 ID。
|
||||
* @return 存在返回 true。
|
||||
*/
|
||||
bool
|
||||
exists(script_id id)
|
||||
{
|
||||
assert(id::is_valid(id));
|
||||
const id::id_type index{ id::index(id) };
|
||||
assert(index < generations.size() && id_mapping[index] < entity_scripts.size());
|
||||
assert(generations[index] == id::generation(id));
|
||||
return (generations[index] == id::generation(id) &&
|
||||
entity_scripts[id_mapping[index]] &&
|
||||
entity_scripts[id_mapping[index]]->is_valid());
|
||||
}
|
||||
|
||||
#if USE_TRANSFORM_CACHE_MAP
|
||||
/**
|
||||
* @brief 获取实体对应的 Transform 缓存项。
|
||||
* @param entity 目标实体。
|
||||
* @return 缓存项指针。
|
||||
*/
|
||||
transform::component_cache *const
|
||||
get_cache_ptr(const game_entity::entity *const entity)
|
||||
{
|
||||
assert(game_entity::is_alive((*entity).get_id()));
|
||||
const transform::transform_id id{ (*entity).transform().get_id() };
|
||||
|
||||
u32 index{ u32_invalid_id };
|
||||
auto pair = cache_map.try_emplace(id, id::invalid_id);
|
||||
|
||||
if (pair.second)
|
||||
{
|
||||
index = (u32)transform_cache.size();
|
||||
transform_cache.emplace_back();
|
||||
transform_cache.back().id = id;
|
||||
cache_map[id] = index;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = cache_map[id];
|
||||
}
|
||||
|
||||
assert(index < transform_cache.size());
|
||||
return &transform_cache[index];
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @brief 线性查找方式获取 Transform 缓存项。
|
||||
* @param entity 目标实体。
|
||||
* @return 缓存项指针。
|
||||
*/
|
||||
transform::component_cache *const
|
||||
get_cache_ptr(const game_entity::entity *const entity)
|
||||
{
|
||||
assert(game_entity::is_alive((*entity).get_id()));
|
||||
const transform::transform_id id{ (*entity).transform().get_id() };
|
||||
|
||||
for (auto& cache : transform_cache)
|
||||
{
|
||||
if (cache.id == id)
|
||||
{
|
||||
return &cache;
|
||||
}
|
||||
}
|
||||
|
||||
transform_cache.emplace_back();
|
||||
transform_cache.back().id = id;
|
||||
|
||||
return &transform_cache.back();
|
||||
}
|
||||
#endif
|
||||
}// namespace
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* @brief 注册脚本工厂函数。
|
||||
* @param tag 脚本名哈希。
|
||||
* @param func 工厂函数。
|
||||
* @return 注册成功返回 1。
|
||||
*/
|
||||
u8
|
||||
register_script(size_t tag, script_creator func)
|
||||
{
|
||||
bool result{ registery().insert(script_registry::value_type{tag, func}).second };
|
||||
assert(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按哈希获取脚本工厂函数。
|
||||
* @param tag 脚本名哈希。
|
||||
* @return 脚本工厂函数。
|
||||
*/
|
||||
script_creator
|
||||
get_script_creator(size_t tag)
|
||||
{
|
||||
auto script = XEngine::script::registery().find(tag);
|
||||
assert(script != XEngine::script::registery().end() && script->first == tag);
|
||||
return script->second;
|
||||
}
|
||||
|
||||
#ifdef USE_WITH_EDITOR
|
||||
/**
|
||||
* @brief 记录脚本名称供编辑器使用。
|
||||
* @param name 脚本类名。
|
||||
* @return 写入成功返回 1。
|
||||
*/
|
||||
u8
|
||||
add_script_name(const char* name)
|
||||
{
|
||||
script_names().emplace_back(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}// namespace detail
|
||||
|
||||
|
||||
/**
|
||||
* @brief 创建脚本组件并实例化脚本对象。
|
||||
* @param info 脚本初始化参数。
|
||||
* @param entity 绑定实体。
|
||||
* @return 脚本组件句柄。
|
||||
*/
|
||||
component
|
||||
create(init_info info, game_entity::entity entity)
|
||||
{
|
||||
assert(entity.is_valid());
|
||||
assert(info.script_creator);
|
||||
|
||||
script_id id{};
|
||||
if (free_ids.size() > id::min_deleted_elements)
|
||||
{
|
||||
id = free_ids.front();
|
||||
assert(!exists(id));
|
||||
free_ids.pop_front();
|
||||
id = script_id{ id::new_generation(id) };
|
||||
++generations[id::index(id)];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
id = script_id{ (id::id_type)id_mapping.size() };
|
||||
id_mapping.emplace_back();
|
||||
generations.push_back(0);
|
||||
}
|
||||
assert(id::is_valid(id));
|
||||
const id::id_type index{ (id::id_type)entity_scripts.size() };
|
||||
entity_scripts.emplace_back(info.script_creator(entity));
|
||||
assert(entity_scripts.back()->get_id() == entity.get_id());
|
||||
id_mapping[id::index(id)] = index;
|
||||
|
||||
return component{ id };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 删除脚本组件并更新映射关系。
|
||||
* @param c 脚本组件句柄。
|
||||
*/
|
||||
void
|
||||
remove(component c)
|
||||
{
|
||||
assert(c.is_valid() && exists(c.get_id()));
|
||||
const script_id id{ c.get_id() };
|
||||
const id::id_type index{ id::index(id) };
|
||||
const script_id last_id{ entity_scripts.back()->script().get_id() };
|
||||
utl::erase_unordered(entity_scripts, index);
|
||||
id_mapping[id::index(last_id)] = index;
|
||||
id_mapping[id::index(id)] = id::invalid_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行脚本逐帧更新并提交变换缓存。
|
||||
* @param dt 帧时间步长。
|
||||
*/
|
||||
void
|
||||
update(float dt) {
|
||||
for (auto& ptr : entity_scripts)
|
||||
{
|
||||
ptr->update(dt);
|
||||
}
|
||||
|
||||
if (transform_cache.size())
|
||||
{
|
||||
transform::update(transform_cache.data(), (u32)transform_cache.size());
|
||||
transform_cache.clear();
|
||||
|
||||
#if USE_TRANSFORM_CACHE_MAP
|
||||
cache_map.clear();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 缓存脚本写入的旋转变更。
|
||||
* @param entity 目标实体。
|
||||
* @param rotation_quaternion 旋转四元数。
|
||||
*/
|
||||
void
|
||||
entity_script::set_rotation(const game_entity::entity *const entity, math::v4 rotation_quaternion)
|
||||
{
|
||||
transform::component_cache& cache{ *get_cache_ptr(entity) };
|
||||
cache.flags |= transform::component_flags::rotation;
|
||||
cache.rotation = rotation_quaternion;
|
||||
}
|
||||
/**
|
||||
* @brief 缓存脚本写入的朝向变更。
|
||||
* @param entity 目标实体。
|
||||
* @param orientation_vector 朝向向量。
|
||||
*/
|
||||
void
|
||||
entity_script::set_orientation(const game_entity::entity *const entity, math::v3 orientation_vector)
|
||||
{
|
||||
transform::component_cache& cache{ *get_cache_ptr(entity) };
|
||||
cache.flags |= transform::component_flags::orientation;
|
||||
cache.orientation = orientation_vector;
|
||||
}
|
||||
/**
|
||||
* @brief 缓存脚本写入的位置变更。
|
||||
* @param entity 目标实体。
|
||||
* @param position 位置向量。
|
||||
*/
|
||||
void
|
||||
entity_script::set_position(const game_entity::entity *const entity, math::v3 position)
|
||||
{
|
||||
transform::component_cache& cache{ *get_cache_ptr(entity) };
|
||||
cache.flags |= transform::component_flags::position;
|
||||
cache.position = position;
|
||||
}
|
||||
/**
|
||||
* @brief 缓存脚本写入的缩放变更。
|
||||
* @param entity 目标实体。
|
||||
* @param scale 缩放向量。
|
||||
*/
|
||||
void
|
||||
entity_script::set_scale(const game_entity::entity *const entity, math::v3 scale)
|
||||
{
|
||||
transform::component_cache& cache{ *get_cache_ptr(entity) };
|
||||
cache.flags |= transform::component_flags::scale;
|
||||
cache.scale = scale;
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
|
||||
#ifdef USE_WITH_EDITOR
|
||||
|
||||
/**
|
||||
* @brief ATL 的 SAFEARRAY 封装头。
|
||||
* @details
|
||||
* 该头文件提供 CComSafeArray,用于在编辑器导出路径中构造 COM 安全数组,
|
||||
* 以便把脚本名列表跨 DLL 边界返回给外部调用方。
|
||||
*/
|
||||
#include <atlsafe.h>
|
||||
|
||||
/**
|
||||
* @brief 导出脚本名列表给编辑器端。
|
||||
* @details
|
||||
* 返回值为 LPSAFEARRAY(SAFEARRAY*),元素类型为 BSTR。
|
||||
* 内部流程:
|
||||
* - 从 script_names() 读取脚本名数量;
|
||||
* - 为空时返回 nullptr;
|
||||
* - 使用 CComSafeArray<BSTR> 分配数组;
|
||||
* - 逐项把 std::string 转成 BSTR 填入数组;
|
||||
* - 通过 Detach 转移 SAFEARRAY 所有权给调用方。
|
||||
* 调用方在不再使用后应负责释放 SAFEARRAY。
|
||||
*/
|
||||
extern "C" __declspec(dllexport)
|
||||
LPSAFEARRAY
|
||||
get_script_names()
|
||||
{
|
||||
// 读取当前已注册脚本名数量。
|
||||
const u32 size{ (u32)XEngine::script::script_names().size() };
|
||||
|
||||
// 没有脚本时返回空指针,避免创建空 SAFEARRAY。
|
||||
if (!size) return nullptr;
|
||||
|
||||
// 创建固定长度的 BSTR 安全数组。
|
||||
CComSafeArray<BSTR> names(size);
|
||||
|
||||
// 将脚本名逐项转换为 BSTR 并写入数组。
|
||||
for (u32 i{ 0 }; i < size; ++i)
|
||||
{
|
||||
names.SetAt(i, A2BSTR_EX(XEngine::script::script_names()[i].c_str()), false);
|
||||
}
|
||||
// 释放 CComSafeArray 对底层 SAFEARRAY 的管理并返回给调用方。
|
||||
return names.Detach();
|
||||
}
|
||||
#endif
|
||||
35
Engine/Components/Script.h
Normal file
35
Engine/Components/Script.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @file Script.h
|
||||
* @brief 脚本组件创建、销毁与更新接口。
|
||||
*/
|
||||
#pragma once
|
||||
#include "ComponentsCommon.h"
|
||||
|
||||
namespace XEngine::script {
|
||||
|
||||
/**
|
||||
* @brief 脚本组件初始化信息。
|
||||
*/
|
||||
struct init_info
|
||||
{
|
||||
detail::script_creator script_creator;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 为实体创建脚本组件。
|
||||
* @param info 脚本初始化信息。
|
||||
* @param entity 目标实体。
|
||||
* @return 创建后的脚本组件句柄。
|
||||
*/
|
||||
component create(init_info info, game_entity::entity entity);
|
||||
/**
|
||||
* @brief 移除脚本组件。
|
||||
* @param c 脚本组件句柄。
|
||||
*/
|
||||
void remove(component c);
|
||||
/**
|
||||
* @brief 更新所有脚本组件。
|
||||
* @param dt 帧时间间隔(秒)。
|
||||
*/
|
||||
void update(float dt);
|
||||
}
|
||||
213
Engine/Components/Transform.cpp
Normal file
213
Engine/Components/Transform.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
#include "Transform.h"
|
||||
#include "Entity.h"
|
||||
|
||||
namespace XEngine::transform {
|
||||
|
||||
namespace {
|
||||
|
||||
utl::vector<math::m4x4> to_world;
|
||||
utl::vector<math::m4x4> inv_world;
|
||||
utl::vector<math::v3> positions;
|
||||
utl::vector<math::v3> orientations;
|
||||
utl::vector<math::v4> rotations;
|
||||
utl::vector<math::v3> scales;
|
||||
utl::vector<u8> has_transform;
|
||||
utl::vector<u8> changes_from_previous_frame;
|
||||
u8 read_write_flag;
|
||||
|
||||
void
|
||||
calculate_transform_matrices(id::id_type index)
|
||||
{
|
||||
assert(rotations.size() >= index);
|
||||
assert(positions.size() >= index);
|
||||
assert(scales.size() >= index);
|
||||
|
||||
using namespace DirectX;
|
||||
XMVECTOR r{ XMLoadFloat4(&rotations[index]) };
|
||||
XMVECTOR t{ XMLoadFloat3(&positions[index]) };
|
||||
XMVECTOR s{ XMLoadFloat3(&scales[index]) };
|
||||
|
||||
XMMATRIX world{ XMMatrixAffineTransformation(s, XMQuaternionIdentity(), r, t) };
|
||||
XMStoreFloat4x4(&to_world[index], world);
|
||||
|
||||
world.r[3] = XMVectorSet(0.f, 0.f, 0.f, 1.f);
|
||||
XMMATRIX inverse_world{ XMMatrixInverse(nullptr,world) };
|
||||
XMStoreFloat4x4(&inv_world[index], inverse_world);
|
||||
|
||||
has_transform[index] = 1;
|
||||
}
|
||||
|
||||
math::v3
|
||||
calculate_orientation(math::v4 rotation)
|
||||
{
|
||||
using namespace DirectX;
|
||||
XMVECTOR rotation_quat{ XMLoadFloat4(&rotation) };
|
||||
XMVECTOR front{ XMVectorSet(0.f,0.f,1.f,0.f) };
|
||||
math::v3 orientation;
|
||||
XMStoreFloat3(&orientation, XMVector3Rotate(front, rotation_quat));
|
||||
return orientation;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
set_rotation(transform_id id, const math::v4& rotation_quaternion)
|
||||
{
|
||||
const u32 index{ id::index(id) };
|
||||
rotations[index] = rotation_quaternion;
|
||||
orientations[index] = calculate_orientation(rotation_quaternion);
|
||||
has_transform[index] = 0;
|
||||
changes_from_previous_frame[index] |= component_flags::rotation;
|
||||
}
|
||||
void
|
||||
set_orientation(transform_id id, const math::v3& rotation_quaternion)
|
||||
{
|
||||
|
||||
}
|
||||
void
|
||||
set_position(transform_id id, const math::v3& position)
|
||||
{
|
||||
const u32 index{ id::index(id) };
|
||||
positions[index] = position;
|
||||
has_transform[index] = 0;
|
||||
changes_from_previous_frame[index] |= component_flags::position;
|
||||
}
|
||||
void
|
||||
set_scale(transform_id id, const math::v3& scale)
|
||||
{
|
||||
const u32 index{ id::index(id) };
|
||||
scales[index] = scale;
|
||||
has_transform[index] = 0;
|
||||
changes_from_previous_frame[index] |= component_flags::scale;
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
component
|
||||
create(init_info info, game_entity::entity entity)
|
||||
{
|
||||
assert(entity.is_valid());
|
||||
const id::id_type entity_index{ id::index(entity.get_id()) };
|
||||
|
||||
if (positions.size() > entity_index)
|
||||
{
|
||||
math::v4 rotation{ info.rotation };
|
||||
rotations[entity_index] = rotation;
|
||||
orientations[entity_index] = calculate_orientation(rotation);
|
||||
positions[entity_index] = math::v3{ info.position };
|
||||
scales[entity_index] = math::v3{ info.scale };
|
||||
has_transform[entity_index] = 0;
|
||||
changes_from_previous_frame[entity_index] = (u8)component_flags::all;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(positions.size() == entity_index);
|
||||
rotations.emplace_back(info.rotation);
|
||||
orientations.emplace_back(calculate_orientation(math::v4{ info.rotation }));
|
||||
positions.emplace_back(info.position);
|
||||
scales.emplace_back(info.scale);
|
||||
has_transform.emplace_back((u8)0);
|
||||
to_world.emplace_back();
|
||||
inv_world.emplace_back();
|
||||
changes_from_previous_frame.emplace_back((u8)component_flags::all);
|
||||
}
|
||||
|
||||
return component{ transform_id{ entity.get_id() } };
|
||||
}
|
||||
void
|
||||
remove([[maybe_unused]] component c)
|
||||
{
|
||||
assert(c.is_valid());
|
||||
}
|
||||
|
||||
void
|
||||
get_transform_matrices(const game_entity::entity_id id, math::m4x4& world, math::m4x4& inverse_world)
|
||||
{
|
||||
assert(game_entity::entity{ id }.is_valid());
|
||||
|
||||
const id::id_type entity_index{ id::index(id) };
|
||||
if (!has_transform[entity_index])
|
||||
{
|
||||
calculate_transform_matrices(entity_index);
|
||||
}
|
||||
|
||||
world = to_world[entity_index];
|
||||
inverse_world = inv_world[entity_index];
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
get_update_component_flags(const game_entity::entity_id *const ids, u32 count, u8 *const flags)
|
||||
{
|
||||
assert(ids && count && flags);
|
||||
read_write_flag = 1;
|
||||
|
||||
for (u32 i{ 0 }; i < count; ++i)
|
||||
{
|
||||
assert(game_entity::entity{ ids[i] }.is_valid());
|
||||
flags[i] = changes_from_previous_frame[id::index(ids[i])];
|
||||
}
|
||||
}
|
||||
void
|
||||
update(const component_cache *const cache, u32 count)
|
||||
{
|
||||
assert(cache && count);
|
||||
|
||||
if (read_write_flag)
|
||||
{
|
||||
memset(changes_from_previous_frame.data(), 0, changes_from_previous_frame.size());
|
||||
read_write_flag = 0;
|
||||
}
|
||||
|
||||
for (u32 i{ 0 }; i < count; ++i)
|
||||
{
|
||||
const component_cache& c{ cache[i] };
|
||||
assert(component{ c.id }.is_valid());
|
||||
if (c.flags & component_flags::rotation)
|
||||
{
|
||||
set_rotation(c.id, c.rotation);
|
||||
}
|
||||
if (c.flags & component_flags::orientation)
|
||||
{
|
||||
set_orientation(c.id, c.orientation);
|
||||
}
|
||||
if (c.flags & component_flags::position)
|
||||
{
|
||||
set_position(c.id, c.position);
|
||||
}
|
||||
if (c.flags & component_flags::scale)
|
||||
{
|
||||
set_scale(c.id, c.scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
math::v3
|
||||
component::orientation() const
|
||||
{
|
||||
assert(is_valid());
|
||||
return orientations[id::index(_id)];
|
||||
}
|
||||
|
||||
math::v3
|
||||
component::position() const
|
||||
{
|
||||
assert(is_valid());
|
||||
return positions[id::index(_id)];
|
||||
}
|
||||
math::v3
|
||||
component::scale() const
|
||||
{
|
||||
assert(is_valid());
|
||||
return scales[id::index(_id)];
|
||||
|
||||
}
|
||||
math::v4
|
||||
component::rotation() const
|
||||
{
|
||||
assert(is_valid());
|
||||
return rotations[id::index(_id)];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
83
Engine/Components/Transform.h
Normal file
83
Engine/Components/Transform.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @file Transform.h
|
||||
* @brief 变换组件数据结构与更新接口。
|
||||
*/
|
||||
#pragma once
|
||||
#include "ComponentsCommon.h"
|
||||
|
||||
namespace XEngine::transform {
|
||||
|
||||
/**
|
||||
* @brief 变换组件初始化参数。
|
||||
*/
|
||||
struct init_info
|
||||
{
|
||||
f32 position[3]{};
|
||||
f32 rotation[4]{};
|
||||
f32 scale[3]{1.f, 1.f, 1.f};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 变换组件缓存更新标记位。
|
||||
*/
|
||||
struct component_flags
|
||||
{
|
||||
enum flags :u32 {
|
||||
rotation = 0x01,
|
||||
orientation = 0x02,
|
||||
position = 0x04,
|
||||
scale = 0x08,
|
||||
|
||||
|
||||
all = rotation | orientation | position| scale
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 变换组件批量更新缓存项。
|
||||
*/
|
||||
struct component_cache
|
||||
{
|
||||
math::v4 rotation;
|
||||
math::v3 orientation;
|
||||
math::v3 position;
|
||||
math::v3 scale;
|
||||
|
||||
transform_id id;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取实体的世界矩阵与逆世界矩阵。
|
||||
* @param id 实体标识符。
|
||||
* @param world 输出世界矩阵。
|
||||
* @param inverse_world 输出逆世界矩阵。
|
||||
*/
|
||||
void get_transform_matrices(const game_entity::entity_id id, math::m4x4& world, math::m4x4& inverse_world);
|
||||
/**
|
||||
* @brief 为实体创建变换组件。
|
||||
* @param info 变换初始化参数。
|
||||
* @param entity 目标实体。
|
||||
* @return 创建后的变换组件句柄。
|
||||
*/
|
||||
component create( init_info info, game_entity::entity entity);
|
||||
/**
|
||||
* @brief 移除变换组件。
|
||||
* @param c 变换组件句柄。
|
||||
*/
|
||||
void remove(component c);
|
||||
/**
|
||||
* @brief 查询指定实体集合的变换更新标记。
|
||||
* @param ids 实体标识符数组。
|
||||
* @param count 实体数量。
|
||||
* @param flags 输出标记数组。
|
||||
*/
|
||||
void get_update_component_flags(const game_entity::entity_id *const ids, u32 count, u8 *const flags);
|
||||
/**
|
||||
* @brief 批量提交变换缓存更新。
|
||||
* @param cache 缓存数组首地址。
|
||||
* @param count 缓存项数量。
|
||||
*/
|
||||
void update(const component_cache *const cache, u32 count);
|
||||
|
||||
}
|
||||
217
Engine/Content/ContentLoader.cpp
Normal file
217
Engine/Content/ContentLoader.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* @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)
|
||||
35
Engine/Content/ContentLoader.h
Normal file
35
Engine/Content/ContentLoader.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @file ContentLoader.h
|
||||
* @brief 编辑器/测试构建下的关卡内容加载入口声明。
|
||||
* @details
|
||||
* 本文件定义内容系统对外暴露的高层加载接口,负责:
|
||||
* - 从磁盘加载测试关卡与实体描述;
|
||||
* - 卸载已创建的实体与运行时资源;
|
||||
* - 读取并返回引擎着色器二进制数据供渲染模块初始化。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
#if !defined(SHIPPING)
|
||||
namespace XEngine::content {
|
||||
/**
|
||||
* @brief 加载测试游戏内容并创建对应运行时对象。
|
||||
* @return 加载成功返回 true,否则返回 false。
|
||||
*/
|
||||
bool load_games();
|
||||
/**
|
||||
* @brief 卸载由 load_games 创建的内容与对象。
|
||||
*/
|
||||
void unload_game();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 从内容目录读取引擎着色器二进制文件。
|
||||
* @param shader 输出的字节缓冲区。
|
||||
* @param size 输出的缓冲区大小(字节)。
|
||||
* @return 读取成功返回 true,否则返回 false。
|
||||
*/
|
||||
bool load_engine_shaders(std::unique_ptr<u8[]>&shader, u64 size);
|
||||
}
|
||||
|
||||
#endif //!defined(SHIPPING)
|
||||
528
Engine/Content/ContentToEngine.cpp
Normal file
528
Engine/Content/ContentToEngine.cpp
Normal file
@@ -0,0 +1,528 @@
|
||||
/**
|
||||
* @file ContentToEngine.cpp
|
||||
* @brief 内容二进制数据到渲染/资源句柄的转换实现。
|
||||
* @details
|
||||
* 本文件完成导入数据的运行时落地,包含:
|
||||
* - 网格层级(LOD)结构重排与子网格 GPU 资源创建;
|
||||
* - 着色器分组缓存与 key 索引查询;
|
||||
* - 材质/纹理/几何资源的统一创建与释放入口。
|
||||
*/
|
||||
#include "ContentToEngine.h"
|
||||
#include "Graphics\Renderer.h"
|
||||
#include "Utilities\IOStream.h"
|
||||
|
||||
namespace XEngine::content {
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief 几何层级缓冲区的视图封装。
|
||||
* @details
|
||||
* 对连续内存中的几何层级布局进行字段映射,提供阈值、LOD 偏移与 GPU ID 访问。
|
||||
*/
|
||||
class geometry_hierarchy_stream
|
||||
{
|
||||
public:
|
||||
DISABLE_COPY_AND_MOVE(geometry_hierarchy_stream);
|
||||
|
||||
geometry_hierarchy_stream(u8 *const buffer, u32 lods = u32_invalid_id)
|
||||
:_buffer{ buffer }
|
||||
{
|
||||
assert(buffer && lods);
|
||||
if (lods != u32_invalid_id)
|
||||
{
|
||||
*((u32*)buffer) = lods;
|
||||
}
|
||||
|
||||
_lod_count = *((u32*)buffer);
|
||||
_thresholds = (f32*)(&buffer[sizeof(u32)]);
|
||||
_lod_offsets = (lod_offset*)(&_thresholds[_lod_count]);
|
||||
_gpu_ids = (id::id_type*)(&_lod_offsets[_lod_count]);
|
||||
}
|
||||
|
||||
void gpu_ids(u32 lod, id::id_type*& ids, u32& id_count)
|
||||
{
|
||||
assert(lod < _lod_count);
|
||||
ids = &_gpu_ids[_lod_offsets[lod].offset];
|
||||
id_count = _lod_offsets[lod].count;
|
||||
}
|
||||
|
||||
u32 lod_from_threshold(f32 threshold)
|
||||
{
|
||||
assert(threshold > 0);
|
||||
if (_lod_count == 1) return 0;
|
||||
|
||||
for (u32 i{ _lod_count - 1 }; i > 0; --i)
|
||||
{
|
||||
if (_thresholds[i] <= threshold) return i;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 lod_count() const { return _lod_count; }
|
||||
[[nodiscard]] constexpr f32* thresholds() const { return _thresholds; }
|
||||
[[nodiscard]] constexpr lod_offset* lod_offsets() const { return _lod_offsets; }
|
||||
[[nodiscard]] constexpr id::id_type* gpu_ids() const { return _gpu_ids; }
|
||||
private:
|
||||
u8 *const _buffer;
|
||||
f32* _thresholds;
|
||||
lod_offset* _lod_offsets;
|
||||
id::id_type* _gpu_ids;
|
||||
u32 _lod_count;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief 支持 noexcept 移动的着色器组缓存容器。
|
||||
*/
|
||||
struct noexcept_map {
|
||||
std::unordered_map<u32, std::unique_ptr<u8[]>> map;
|
||||
noexcept_map() = default;
|
||||
noexcept_map(const noexcept_map&) = default;
|
||||
noexcept_map(noexcept_map&&) noexcept = default;
|
||||
noexcept_map& operator=(const noexcept_map&) = default;
|
||||
noexcept_map& operator=(noexcept_map&&) noexcept = default;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 单子网格资源的指针标记位。
|
||||
*/
|
||||
constexpr uintptr_t single_mesh_marker{ (uintptr_t)0x01 };
|
||||
/**
|
||||
* @brief 几何层级资源池。
|
||||
*/
|
||||
utl::free_list<u8*> geometry_hierarchies;
|
||||
/**
|
||||
* @brief 几何资源互斥锁。
|
||||
*/
|
||||
std::mutex geometry_mutex;
|
||||
|
||||
/**
|
||||
* @brief 着色器组资源池。
|
||||
*/
|
||||
utl::free_list<noexcept_map> shader_groups;
|
||||
/**
|
||||
* @brief 着色器资源互斥锁。
|
||||
*/
|
||||
std::mutex shader_mutex;
|
||||
|
||||
|
||||
/**
|
||||
* @brief 预估并计算几何层级缓冲区所需字节数。
|
||||
* @param data 几何二进制输入数据。
|
||||
* @return 所需缓冲区大小(字节)。
|
||||
*/
|
||||
u32
|
||||
get_geometry_hierarchy_buffer_size(const void *const data)
|
||||
{
|
||||
assert(data);
|
||||
utl::blob_stream_reader blob{ (const u8*)data };
|
||||
const u32 lod_count{ blob.reader<u32>() };
|
||||
assert(lod_count);
|
||||
constexpr u32 su32{ sizeof(u32) };
|
||||
|
||||
u32 size{ su32 + (sizeof(f32) + sizeof(lod_offset)) * lod_count };
|
||||
|
||||
for (u32 lod_idx{ 0 }; lod_idx < lod_count; ++lod_idx)
|
||||
{
|
||||
// skip threshold
|
||||
blob.skip(sizeof(f32));
|
||||
// add size of gpu_ids
|
||||
size += sizeof(id::id_type) * blob.reader<u32>();
|
||||
// skip submesh data and goto next LOD
|
||||
blob.skip(blob.reader<u32>());
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建多 LOD 网格层级资源。
|
||||
* @param data 几何二进制输入数据。
|
||||
* @return 几何内容资源 ID。
|
||||
*/
|
||||
//id::id_type
|
||||
//create_mesh_hierarchy(const void *const data)
|
||||
//{
|
||||
// assert(data);
|
||||
//
|
||||
// const u32 size{ get_geometry_hierarchy_buffer_size(data) };
|
||||
// u8 *const hierarchy_buffer{ (u8 *const)malloc(size) };
|
||||
//
|
||||
// utl::blob_stream_reader blob{ (const u8*)data };
|
||||
// const u32 lod_count{ blob.reader<u32>() };
|
||||
// assert(lod_count);
|
||||
// geometry_hierarchy_stream stream{ hierarchy_buffer, lod_count };
|
||||
// u32 submesh_index{ 0 };
|
||||
// id::id_type *const gpu_ids{ stream.gpu_ids() };
|
||||
//
|
||||
// for (u32 lod_idx{ 0 }; lod_idx < lod_count; ++lod_idx)
|
||||
// {
|
||||
// stream.thresholds()[lod_idx] = blob.reader<f32>();
|
||||
// const u32 id_count{ blob.reader<u32>() };
|
||||
// assert(id_count < (1 << 16));
|
||||
// stream.lod_offsets()[lod_idx] = { (u16)submesh_index, (u16)id_count };
|
||||
// blob.skip(sizeof(u32)); // skip sizeof submeshes
|
||||
// for (u32 id_idx{ 0 }; id_idx < id_count; ++id_idx)
|
||||
// {
|
||||
// const u8* at{ blob.position() };
|
||||
// gpu_ids[submesh_index++] = graphics::add_submesh(at);
|
||||
// blob.skip((u32)(at - blob.position()));
|
||||
// assert(submesh_index < (1 << 16));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// assert([&]()
|
||||
// {
|
||||
// f32 previous_theshold{ stream.thresholds()[0] };
|
||||
// for (u32 i{ 1 }; i < lod_count; ++i)
|
||||
// {
|
||||
// if (stream.thresholds()[i] <= previous_theshold) return false;
|
||||
// previous_theshold = stream.thresholds()[i];
|
||||
// }
|
||||
// return true;
|
||||
// }());
|
||||
//
|
||||
// static_assert(alignof(void*) > 2, "We need the least significant bit for th single mesh marker.");
|
||||
// std::lock_guard lock{ geometry_mutex };
|
||||
// return geometry_hierarchies.add(hierarchy_buffer);
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief 判断几何数据是否为单 LOD 单子网格。
|
||||
* @param data 几何二进制输入数据。
|
||||
* @return 单子网格返回 true。
|
||||
*/
|
||||
bool
|
||||
is_single_mesh(const void *const data)
|
||||
{
|
||||
assert(data);
|
||||
utl::blob_stream_reader blob{ (const u8*)data };
|
||||
const u32 lod_count{ blob.reader<u32>() };
|
||||
assert(lod_count);
|
||||
if (lod_count > 1) return false;
|
||||
|
||||
|
||||
blob.skip(sizeof(f32)); // skip threhold
|
||||
const u32 submesh_count{ blob.reader<u32>() };
|
||||
assert(submesh_count);
|
||||
return submesh_count == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建单子网格资源并编码为伪指针句柄。
|
||||
* @param data 几何二进制输入数据。
|
||||
* @return 几何内容资源 ID。
|
||||
*/
|
||||
//id::id_type
|
||||
//create_single_submesh(const void *const data)
|
||||
//{
|
||||
// assert(data);
|
||||
// utl::blob_stream_reader blob{ (const u8*)data };
|
||||
// // skip lod_count, lod_threshold, submesh_count, and sizeof_submeshes
|
||||
// blob.skip(sizeof(u32) + sizeof(f32) + sizeof(u32) + sizeof(u32));
|
||||
// const u8* at{ blob.position() };
|
||||
// const id::id_type gpu_id{ graphics::add_submesh(at) };
|
||||
//
|
||||
// // create a fake pointer and put it in the geometry_hierarchies.
|
||||
// static_assert(sizeof(uintptr_t) > sizeof(id::id_type));
|
||||
// constexpr u8 shift_bits{ (sizeof(uintptr_t) - sizeof(id::id_type)) << 3 };
|
||||
// u8 *const fake_pointer{ (u8 *const)((((uintptr_t)gpu_id) << shift_bits) | single_mesh_marker) };
|
||||
// std::lock_guard lock{ geometry_mutex };
|
||||
// return geometry_hierarchies.add(fake_pointer);
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief 从单子网格伪指针解码 GPU 资源 ID。
|
||||
* @param pointer 编码后的伪指针。
|
||||
* @return GPU 子网格 ID。
|
||||
*/
|
||||
constexpr id::id_type
|
||||
gpu_id_from_fake_pointer(u8 *const pointer)
|
||||
{
|
||||
assert((uintptr_t)pointer & single_mesh_marker);
|
||||
static_assert(sizeof(uintptr_t) > sizeof(id::id_type));
|
||||
constexpr u8 shift_bits{ (sizeof(uintptr_t) - sizeof(id::id_type)) << 3 };
|
||||
return (((uintptr_t)pointer) >> shift_bits) & (uintptr_t)id::invalid_id;
|
||||
}
|
||||
//
|
||||
// data contain:
|
||||
//struct{
|
||||
// u32 lod_count,
|
||||
// struct{
|
||||
// f32 lod_threshold,
|
||||
// u32 submesh_count,
|
||||
// u32 sizeof_submeshes,
|
||||
// struct{
|
||||
// u32 element_size, u32 vertex_count,
|
||||
// u32 index_count, u32 elements_type, u32 primitive_topology
|
||||
// u8 positions[sizeof(f32) * 3 * vertex_count],
|
||||
// u8 elements[sizeof(element_size) * vertex_count],
|
||||
// u8 indices[index_size * index_count],
|
||||
// } submeshes[submesh_count]
|
||||
// }mesh_lods[lod_count]
|
||||
// } geometry;
|
||||
//
|
||||
// Output format
|
||||
//
|
||||
// struct{
|
||||
// u32 lod_count,
|
||||
// f32 thresholds[lod_count]
|
||||
// struct{
|
||||
// u16 offset,
|
||||
// u16 count
|
||||
// }lod_offsets[lod_count],
|
||||
// id::idtype gpu_ids[total_number_of_submeshes]
|
||||
//}geometry_hierarchy
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
/**
|
||||
* @brief 按输入几何形态创建对应资源。
|
||||
* @param data 几何二进制输入数据。
|
||||
* @return 几何内容资源 ID。
|
||||
*/
|
||||
//id::id_type
|
||||
//create_geometry_resource(const void *const data)
|
||||
//{
|
||||
// assert(data);
|
||||
// return is_single_mesh(data) ? create_single_submesh(data) : create_mesh_hierarchy(data);
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief 销毁几何资源并释放关联子网格。
|
||||
* @param id 几何内容资源 ID。
|
||||
*/
|
||||
//void
|
||||
//destory_geometry_resource(id::id_type id)
|
||||
//{
|
||||
// std::lock_guard lock{ geometry_mutex };
|
||||
// u8 *const pointer{ geometry_hierarchies[id] };
|
||||
// if ((uintptr_t)pointer & single_mesh_marker)
|
||||
// {
|
||||
// graphics::remove_submesh(gpu_id_from_fake_pointer(pointer));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// geometry_hierarchy_stream stream{ pointer };
|
||||
// const u32 lod_count{ stream.lod_count() };
|
||||
// u32 id_index{ 0 };
|
||||
// for (u32 lod{ 0 }; lod < lod_count; ++lod)
|
||||
// {
|
||||
// for (u32 i{ 0 }; i < stream.lod_offsets()[lod].count; ++i)
|
||||
// {
|
||||
// graphics::remove_submesh(stream.gpu_ids()[id_index++]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// free(pointer);
|
||||
// }
|
||||
//
|
||||
// geometry_hierarchies.remove(id);
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief 创建材质资源。
|
||||
* @param data 材质初始化数据。
|
||||
* @return 材质资源 ID。
|
||||
*/
|
||||
//id::id_type
|
||||
//create_material_resource(const void *const data)
|
||||
//{
|
||||
// assert(data);
|
||||
// return graphics::add_material(*(const graphics::material_init_info *const)data);
|
||||
//}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* @brief 销毁材质资源。
|
||||
* @param id 材质资源 ID。
|
||||
*/
|
||||
//void
|
||||
//destory_material_resource(id::id_type id)
|
||||
//{
|
||||
// graphics::remove_material(id);
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief 统一资源创建入口。
|
||||
* @param data 资源二进制数据。
|
||||
* @param type 资源类型。
|
||||
* @return 资源 ID。
|
||||
*/
|
||||
//id::id_type
|
||||
//create_resource(const void *const data, asset_type::type type)
|
||||
//{
|
||||
// assert(data);
|
||||
// id::id_type id{ id::invalid_id };
|
||||
//
|
||||
// switch (type)
|
||||
// {
|
||||
// case XEngine::content::asset_type::unknown: break;
|
||||
// case XEngine::content::asset_type::animation: break;
|
||||
// case XEngine::content::asset_type::audio: break;
|
||||
// case XEngine::content::asset_type::material: id = create_material_resource(data); break;
|
||||
// case XEngine::content::asset_type::mesh: id = create_geometry_resource(data); break;
|
||||
// case XEngine::content::asset_type::skeleton: break;
|
||||
// case XEngine::content::asset_type::texture: break;
|
||||
// }
|
||||
//
|
||||
// assert(id::is_valid(id));
|
||||
// return id;
|
||||
//
|
||||
//}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 统一资源销毁入口。
|
||||
* @param id 资源 ID。
|
||||
* @param type 资源类型。
|
||||
*/
|
||||
//void
|
||||
//destroy_resource(id::id_type id, asset_type::type type)
|
||||
//{
|
||||
// assert(id::is_valid(id));
|
||||
// switch (type)
|
||||
// {
|
||||
// case XEngine::content::asset_type::unknown: break;
|
||||
// case XEngine::content::asset_type::animation: break;
|
||||
// case XEngine::content::asset_type::audio: break;
|
||||
// case XEngine::content::asset_type::material: destory_material_resource(id); break;
|
||||
// case XEngine::content::asset_type::mesh: destory_geometry_resource(id); break;
|
||||
// case XEngine::content::asset_type::skeleton: break;
|
||||
// case XEngine::content::asset_type::texture: break;
|
||||
// default:
|
||||
// assert(false);
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
/**
|
||||
* @brief 注册着色器组并复制其二进制数据。
|
||||
* @param shaders 着色器数组。
|
||||
* @param num_shaders 着色器数量。
|
||||
* @param keys 查询键数组。
|
||||
* @return 着色器组 ID。
|
||||
*/
|
||||
id::id_type
|
||||
add_shader_group(const u8 *const * shaders, u32 num_shaders, const u32 *const keys)
|
||||
{
|
||||
assert(shaders && num_shaders && keys);
|
||||
noexcept_map group;
|
||||
for (u32 i{ 0 }; i < num_shaders; ++i)
|
||||
{
|
||||
assert(shaders[i]);
|
||||
const compiled_shader_ptr shader_ptr{ (const compiled_shader_ptr)shaders[i] };
|
||||
const u64 size{ shader_ptr->buffer_size()};
|
||||
std::unique_ptr<u8[]> shader{ std::make_unique<u8[]>(size) };
|
||||
memcpy(shader.get(), shaders[i], size);
|
||||
group.map[keys[i]] = std::move(shader);
|
||||
}
|
||||
std::lock_guard lock{ shader_mutex };
|
||||
return shader_groups.add(std::move(group));
|
||||
}
|
||||
/**
|
||||
* @brief 删除着色器组。
|
||||
* @param id 着色器组 ID。
|
||||
*/
|
||||
void
|
||||
remove_shader_group(id::id_type id)
|
||||
{
|
||||
std::lock_guard lock{ shader_mutex };
|
||||
assert(id::is_valid(id));
|
||||
|
||||
shader_groups[id].map.clear();
|
||||
shader_groups.remove(id);
|
||||
}
|
||||
/**
|
||||
* @brief 在着色器组内按键查找着色器。
|
||||
* @param id 着色器组 ID。
|
||||
* @param shader_key 查询键。
|
||||
* @return 命中时返回着色器指针。
|
||||
*/
|
||||
compiled_shader_ptr
|
||||
get_shader(id::id_type id, u32 shader_key)
|
||||
{
|
||||
std::lock_guard lock{ shader_mutex };
|
||||
assert(id::is_valid(id));
|
||||
for (const auto& [key, value] : shader_groups[id].map)
|
||||
{
|
||||
if (key == shader_key)
|
||||
{
|
||||
return (const compiled_shader_ptr)value.get();
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取几何资源包含的全部子网格 GPU ID。
|
||||
* @param geometry_content_id 几何内容资源 ID。
|
||||
* @param id_count 输出数组长度。
|
||||
* @param gpu_ids 输出数组。
|
||||
*/
|
||||
void
|
||||
get_submesh_gpu_id(id::id_type geometry_content_id, u32 id_count, id::id_type *const gpu_ids)
|
||||
{
|
||||
std::lock_guard lock{ geometry_mutex };
|
||||
u8 *const pointer{ geometry_hierarchies[geometry_content_id] };
|
||||
if ((uintptr_t)pointer & single_mesh_marker)
|
||||
{
|
||||
assert(id_count == 1);
|
||||
*gpu_ids = gpu_id_from_fake_pointer(pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry_hierarchy_stream stream{ pointer };
|
||||
|
||||
assert([&]() {
|
||||
const u32 lod_count{ stream.lod_count() };
|
||||
const lod_offset lod_offset{ stream.lod_offsets()[lod_count - 1] };
|
||||
const u32 gpu_id_count{ (u32)lod_offset.offset + (u32)lod_offset.count };
|
||||
return gpu_id_count == id_count;
|
||||
}());
|
||||
|
||||
|
||||
memcpy(gpu_ids, stream.gpu_ids(), sizeof(id::id_type) * id_count);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按阈值查询各几何实例使用的 LOD 偏移。
|
||||
* @param geometry_ids 几何资源 ID 数组。
|
||||
* @param thresholds 每个实例的阈值数组。
|
||||
* @param id_count 查询数量。
|
||||
* @param offsets 输出 LOD 偏移数组。
|
||||
*/
|
||||
void
|
||||
get_lod_offset(const id::id_type *const geometry_ids, const f32 *const thresholds, u32 id_count, utl::vector<lod_offset>& offsets)
|
||||
{
|
||||
assert(geometry_ids && thresholds && id_count);
|
||||
assert(offsets.empty());
|
||||
|
||||
std::lock_guard lock{ geometry_mutex };
|
||||
|
||||
for (u32 i{ 0 }; i < id_count; ++i)
|
||||
{
|
||||
u8 *const pointer{ geometry_hierarchies[geometry_ids[i]] };
|
||||
if ((uintptr_t)pointer & single_mesh_marker)
|
||||
{
|
||||
assert(id_count == 1);
|
||||
offsets.emplace_back(lod_offset{ 0,1 });
|
||||
}
|
||||
else
|
||||
{
|
||||
geometry_hierarchy_stream stream{ pointer };
|
||||
const u32 lod{ stream.lod_from_threshold(thresholds[i]) };
|
||||
offsets.emplace_back(stream.lod_offsets()[lod]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Engine/Content/ContentToEngine.h
Normal file
133
Engine/Content/ContentToEngine.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @file ContentToEngine.h
|
||||
* @brief 内容系统到运行时资源系统的数据桥接接口。
|
||||
* @details
|
||||
* 该文件描述资源导入后的统一输入格式与运行时句柄接口,核心能力包括:
|
||||
* - 依据 asset_type 创建/销毁网格、材质、纹理等资源;
|
||||
* - 管理变体着色器组并按 key 查询编译结果;
|
||||
* - 提供几何 LOD 偏移与 GPU 子网格 ID 的查询能力。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
|
||||
namespace XEngine::content {
|
||||
|
||||
/**
|
||||
* @brief 资源类型枚举容器。
|
||||
*/
|
||||
struct asset_type {
|
||||
/**
|
||||
* @brief 运行时可识别的内容资源类型。
|
||||
*/
|
||||
enum type : u32
|
||||
{
|
||||
unknown = 0,
|
||||
animation,
|
||||
audio,
|
||||
material,
|
||||
mesh,
|
||||
skeleton,
|
||||
texture,
|
||||
|
||||
count
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 指定某个 LOD 在子网格数组中的起始偏移与数量。
|
||||
*/
|
||||
struct lod_offset
|
||||
{
|
||||
u16 offset;
|
||||
u16 count;
|
||||
};
|
||||
|
||||
|
||||
typedef struct compiled_shader {
|
||||
|
||||
/**
|
||||
* @brief 着色器哈希的固定字节数。
|
||||
*/
|
||||
static constexpr u32 hash_length{ 16 };
|
||||
/**
|
||||
* @brief 获取字节码长度。
|
||||
*/
|
||||
constexpr u64 byte_code_size() const { return _byte_code_size; }
|
||||
/**
|
||||
* @brief 获取哈希数据首地址。
|
||||
*/
|
||||
constexpr const u8 *const hash() const { return &_hash[0]; }
|
||||
/**
|
||||
* @brief 获取字节码首地址。
|
||||
*/
|
||||
constexpr const u8 *const byte_code() const { return &_byte_code; }
|
||||
/**
|
||||
* @brief 获取当前对象对应的总缓冲区长度。
|
||||
*/
|
||||
constexpr const u64 buffer_size() const { return sizeof(_byte_code_size ) + hash_length + _byte_code_size; }
|
||||
/**
|
||||
* @brief 根据字节码大小计算总缓冲区长度。
|
||||
*/
|
||||
constexpr static u64 buffer_size(u64 size) { return sizeof(_byte_code_size ) + hash_length + size; }
|
||||
|
||||
private:
|
||||
u64 _byte_code_size;
|
||||
u8 _hash[hash_length];
|
||||
u8 _byte_code;
|
||||
}const *compiled_shader_ptr;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 根据二进制内容创建对应运行时资源。
|
||||
* @param data 编译后的资源二进制数据。
|
||||
* @param type 资源类型。
|
||||
* @return 新创建资源的 ID。
|
||||
*/
|
||||
id::id_type create_resource(const void *const data, asset_type::type type);
|
||||
/**
|
||||
* @brief 销毁指定资源类型下的运行时资源。
|
||||
* @param id 资源 ID。
|
||||
* @param type 资源类型。
|
||||
*/
|
||||
void destroy_resource(id::id_type id, asset_type::type type);
|
||||
|
||||
/**
|
||||
* @brief 注册一组着色器变体。
|
||||
* @param shaders 着色器二进制列表。
|
||||
* @param num_shaders 着色器数量。
|
||||
* @param keys 每个着色器对应的查询键。
|
||||
* @return 着色器组 ID。
|
||||
*/
|
||||
id::id_type add_shader_group(const u8 *const * shaders, u32 num_shaders, const u32 *const keys);
|
||||
/**
|
||||
* @brief 删除着色器组及其缓存数据。
|
||||
* @param id 着色器组 ID。
|
||||
*/
|
||||
void remove_shader_group(id::id_type id);
|
||||
/**
|
||||
* @brief 按组 ID 与键查询编译后着色器。
|
||||
* @param id 着色器组 ID。
|
||||
* @param shader_key 着色器键值。
|
||||
* @return 命中时返回着色器描述,否则返回空。
|
||||
*/
|
||||
compiled_shader_ptr get_shader(id::id_type id, u32 shader_key);
|
||||
|
||||
/**
|
||||
* @brief 根据几何内容 ID 批量获取子网格 GPU 资源 ID。
|
||||
* @param geometry_content_id 几何内容资源 ID。
|
||||
* @param id_count 输出数组长度。
|
||||
* @param gpu_ids 输出的 GPU ID 数组。
|
||||
*/
|
||||
void get_submesh_gpu_id(id::id_type geometry_content_id, u32 id_count, id::id_type *const gpu_ids);
|
||||
/**
|
||||
* @brief 按阈值批量查询几何资源对应的 LOD 偏移信息。
|
||||
* @param geometry_ids 几何资源 ID 数组。
|
||||
* @param thresholds 每个几何对应的 LOD 阈值。
|
||||
* @param id_count 查询数量。
|
||||
* @param offsets 输出的 LOD 偏移信息。
|
||||
*/
|
||||
void get_lod_offset(const id::id_type *const geometry_ids, const f32 *const thresholds, u32 id_count, utl::vector<lod_offset>& offsets);
|
||||
}
|
||||
103
Engine/Core/EngineWin32.cpp
Normal file
103
Engine/Core/EngineWin32.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @file EngineWin32.cpp
|
||||
* @brief Win32 运行模式下的引擎生命周期实现。
|
||||
* @details
|
||||
* 该文件负责连接平台消息循环与引擎帧循环:
|
||||
* - 初始化阶段加载内容并创建主窗口;
|
||||
* - 更新阶段驱动脚本系统与帧间休眠;
|
||||
* - 关闭阶段销毁窗口并卸载内容资源。
|
||||
*/
|
||||
|
||||
#if !defined(SHIPPING) && defined(_WIN64)
|
||||
#include "..\Content\ContentLoader.h"
|
||||
#include "..\Components\Script.h"
|
||||
#include "..\Platform\PlatformTypes.h"
|
||||
#include "..\Platform\Platform.h"
|
||||
#include "..\Graphics\Renderer.h"
|
||||
#include <thread>
|
||||
|
||||
using namespace XEngine;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief 游戏窗口渲染表面实例。
|
||||
*/
|
||||
graphics::render_surface game_window{};
|
||||
|
||||
/**
|
||||
* @brief 主窗口消息回调。
|
||||
* @param hwnd 窗口句柄。
|
||||
* @param msg 消息类型。
|
||||
* @param wparam 消息参数。
|
||||
* @param lparam 消息参数。
|
||||
* @return 消息处理结果。
|
||||
*/
|
||||
LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
{
|
||||
if (game_window.window.is_closed())
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case WM_SYSCHAR:
|
||||
if (wparam == VK_RETURN && (HIWORD(lparam) & KF_ALTDOWN))
|
||||
{
|
||||
game_window.window.set_fullscreen(!game_window.window.is_fullscreen());
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化引擎运行环境。
|
||||
* @return 初始化成功返回 true。
|
||||
*/
|
||||
bool
|
||||
engine_initialize()
|
||||
{
|
||||
if (!XEngine::content::load_games()) return false;
|
||||
platform::window_init_info info
|
||||
{
|
||||
&win_proc, nullptr, L"XGame"
|
||||
};
|
||||
|
||||
game_window.window = platform::create_window(&info);
|
||||
if (!game_window.window.is_valid()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief 执行单帧更新。
|
||||
*/
|
||||
void
|
||||
engine_update()
|
||||
{
|
||||
XEngine::script::update(10.f);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
/**
|
||||
* @brief 关闭引擎并释放运行时资源。
|
||||
*/
|
||||
void
|
||||
engine_shutdown()
|
||||
{
|
||||
platform::remove_window(game_window.window.get_id());
|
||||
XEngine::content::unload_game();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //!defined(SHIPPING)
|
||||
77
Engine/Core/MainWin32.cpp
Normal file
77
Engine/Core/MainWin32.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @file MainWin32.cpp
|
||||
* @brief Win32 可执行程序入口与消息泵实现。
|
||||
* @details
|
||||
* 负责设置工作目录、初始化调试内存检测,并驱动主循环:
|
||||
* - 轮询并分发 Windows 消息;
|
||||
* - 在消息空闲阶段调用 engine_update;
|
||||
* - 退出时由 engine_shutdown 完成资源收尾。
|
||||
*/
|
||||
#ifdef _WIN64
|
||||
#include "CommonHeader.h"
|
||||
#include <filesystem>
|
||||
#ifndef WIN32_MEAN_AND_LEAN
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#endif
|
||||
|
||||
#include <Windows.h>
|
||||
#include <crtdbg.h>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief 将当前工作目录切换到可执行文件所在目录。
|
||||
* @return 切换后的当前目录;失败时返回空路径。
|
||||
*/
|
||||
std::filesystem::path
|
||||
set_current_directory_to_executable_path()
|
||||
{
|
||||
wchar_t path[MAX_PATH]{};
|
||||
const uint32_t length{ GetModuleFileName(0, &path[0], MAX_PATH) };
|
||||
if (!length || GetLastError() == ERROR_INSUFFICIENT_BUFFER) return {};
|
||||
std::filesystem::path p{ path };
|
||||
std::filesystem::current_path(p.parent_path());
|
||||
return std::filesystem::current_path();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifndef USE_WITH_EDITOR
|
||||
|
||||
extern bool engine_initialize();
|
||||
extern void engine_update();
|
||||
extern void engine_shutdown();
|
||||
|
||||
/**
|
||||
* @brief Win32 程序入口函数。
|
||||
* @return 进程退出码。
|
||||
*/
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
|
||||
#if _DEBUG
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
#endif
|
||||
|
||||
set_current_directory_to_executable_path();
|
||||
|
||||
if (engine_initialize())
|
||||
{
|
||||
MSG msg{};
|
||||
bool is_running{ true };
|
||||
while (is_running)
|
||||
{
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
is_running &= (msg.message != WM_QUIT);
|
||||
}
|
||||
|
||||
engine_update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_WITH_EDITOR
|
||||
#endif // _WIN64
|
||||
227
Engine/Engine.vcxproj
Normal file
227
Engine/Engine.vcxproj
Normal file
@@ -0,0 +1,227 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="DebugEditor|x64">
|
||||
<Configuration>DebugEditor</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="ReleaseEditor|x64">
|
||||
<Configuration>ReleaseEditor</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Common\CommonHeader.h" />
|
||||
<ClInclude Include="Common\XEnginType.h" />
|
||||
<ClInclude Include="Common\Id.h" />
|
||||
<ClInclude Include="Components\ComponentsCommon.h" />
|
||||
<ClInclude Include="Components\Entity.h" />
|
||||
<ClInclude Include="Components\Script.h" />
|
||||
<ClCompile Include="Components\Transform.h" />
|
||||
<ClInclude Include="Content\ContentLoader.h" />
|
||||
<ClInclude Include="Content\ContentToEngine.h" />
|
||||
<ClInclude Include="EngineAPI\Camera.h" />
|
||||
<ClInclude Include="EngineAPI\GameEntity.h" />
|
||||
<ClInclude Include="EngineAPI\ScriptComponent.h" />
|
||||
<ClInclude Include="EngineAPI\TransformComponent.h" />
|
||||
<ClInclude Include="EngineAPI\Light.h" />
|
||||
<ClInclude Include="Graphics\Renderer.h" />
|
||||
<ClInclude Include="Platform\IncludeWindowCpp.h" />
|
||||
<ClInclude Include="Platform\Platform.h" />
|
||||
<ClInclude Include="Platform\PlatformTypes.h" />
|
||||
<ClInclude Include="Platform\Window.h" />
|
||||
<ClInclude Include="Utilities\FreeList.h" />
|
||||
<ClInclude Include="Utilities\IOStream.h" />
|
||||
<ClInclude Include="Utilities\Math.h" />
|
||||
<ClInclude Include="Utilities\MathTypes.h" />
|
||||
<ClInclude Include="Utilities\Utilities.h" />
|
||||
<ClInclude Include="Utilities\Vector.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Components\Entity.cpp" />
|
||||
<ClCompile Include="Components\Script.cpp" />
|
||||
<ClCompile Include="Components\Transform.cpp" />
|
||||
<ClCompile Include="Content\ContentLoader.cpp" />
|
||||
<ClCompile Include="Content\ContentToEngine.cpp" />
|
||||
<ClCompile Include="Core\EngineWin32.cpp" />
|
||||
<ClCompile Include="Core\MainWin32.cpp" />
|
||||
<ClCompile Include="Graphics\Renderer.cpp" />
|
||||
<ClCompile Include="Platform\PlatformWin32.cpp" />
|
||||
<ClCompile Include="Platform\Window.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{838f1eae-48a4-40ba-9b8f-332d37ea66cd}</ProjectGuid>
|
||||
<RootNamespace>Engine</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>
|
||||
</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<CallingConvention>FastCall</CallingConvention>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)/Common</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>
|
||||
</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;USE_WITH_EDITOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<CallingConvention>FastCall</CallingConvention>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)/Common</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>
|
||||
</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<ControlFlowGuard>false</ControlFlowGuard>
|
||||
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<CallingConvention>FastCall</CallingConvention>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)/Common</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>
|
||||
</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;USE_WITH_EDITOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<ControlFlowGuard>false</ControlFlowGuard>
|
||||
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<CallingConvention>FastCall</CallingConvention>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)/Common</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
41
Engine/Engine.vcxproj.filters
Normal file
41
Engine/Engine.vcxproj.filters
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Common\CommonHeader.h" />
|
||||
<ClInclude Include="Common\XEnginType.h" />
|
||||
<ClInclude Include="Common\Id.h" />
|
||||
<ClInclude Include="Components\Entity.h" />
|
||||
<ClInclude Include="Components\ComponentsCommon.h" />
|
||||
<ClInclude Include="Utilities\Utilities.h" />
|
||||
<ClInclude Include="EngineAPI\GameEntity.h" />
|
||||
<ClInclude Include="EngineAPI\TransformComponent.h" />
|
||||
<ClInclude Include="Utilities\MathTypes.h" />
|
||||
<ClInclude Include="EngineAPI\ScriptComponent.h" />
|
||||
<ClInclude Include="Components\Script.h" />
|
||||
<ClInclude Include="Content\ContentLoader.h" />
|
||||
<ClInclude Include="Platform\Window.h" />
|
||||
<ClInclude Include="Platform\Platform.h" />
|
||||
<ClInclude Include="Platform\PlatformTypes.h" />
|
||||
<ClInclude Include="Utilities\Math.h" />
|
||||
<ClInclude Include="Utilities\FreeList.h" />
|
||||
<ClInclude Include="Utilities\Vector.h" />
|
||||
<ClInclude Include="Platform\IncludeWindowCpp.h" />
|
||||
<ClInclude Include="Utilities\IOStream.h" />
|
||||
<ClInclude Include="Content\ContentToEngine.h" />
|
||||
<ClInclude Include="EngineAPI\Camera.h" />
|
||||
<ClInclude Include="EngineAPI\Light.h" />
|
||||
<ClInclude Include="Graphics\Renderer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Components\Entity.cpp" />
|
||||
<ClCompile Include="Components\Transform.h" />
|
||||
<ClCompile Include="Components\Transform.cpp" />
|
||||
<ClCompile Include="Components\Script.cpp" />
|
||||
<ClCompile Include="Core\MainWin32.cpp" />
|
||||
<ClCompile Include="Core\EngineWin32.cpp" />
|
||||
<ClCompile Include="Content\ContentLoader.cpp" />
|
||||
<ClCompile Include="Platform\PlatformWin32.cpp" />
|
||||
<ClCompile Include="Platform\Window.cpp" />
|
||||
<ClCompile Include="Content\ContentToEngine.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
128
Engine/EngineAPI/Camera.h
Normal file
128
Engine/EngineAPI/Camera.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* @file Camera.h
|
||||
* @brief 相机句柄与视图/投影参数接口定义。
|
||||
* @details
|
||||
* camera 提供对渲染相机实例的统一访问,覆盖:
|
||||
* - 透视/正交两类投影参数设置;
|
||||
* - 视图矩阵、投影矩阵及其逆矩阵查询;
|
||||
* - 近远裁剪面、宽高与绑定实体 ID 读取。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
namespace XEngine::graphics {
|
||||
|
||||
|
||||
/**
|
||||
* @brief 相机强类型标识符。
|
||||
*/
|
||||
DEFINE_TYPED_ID(camera_id);
|
||||
|
||||
/**
|
||||
* @brief 渲染相机句柄对象。
|
||||
*/
|
||||
class camera
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 投影模式枚举。
|
||||
*/
|
||||
enum type :u32
|
||||
{
|
||||
perspective,
|
||||
orthographic,
|
||||
};
|
||||
|
||||
constexpr explicit camera(camera_id id) : _id{ id } {}
|
||||
constexpr camera() = default;
|
||||
constexpr camera_id get_id() const { return _id; }
|
||||
constexpr bool is_valid() const { return id::is_valid(_id); }
|
||||
|
||||
/**
|
||||
* @brief 设置相机上方向。
|
||||
*/
|
||||
void up(math::v3 up) const;
|
||||
/**
|
||||
* @brief 设置透视投影视场角。
|
||||
*/
|
||||
void field_of_view(f32 fov) const;
|
||||
/**
|
||||
* @brief 设置视口宽高比。
|
||||
*/
|
||||
void aspect_ratio(f32 aspect_ratio) const;
|
||||
/**
|
||||
* @brief 设置正交视口宽度。
|
||||
*/
|
||||
void view_width(f32 width) const;
|
||||
/**
|
||||
* @brief 设置正交视口高度。
|
||||
*/
|
||||
void view_height(f32 height) const;
|
||||
/**
|
||||
* @brief 设置近远裁剪面。
|
||||
*/
|
||||
void range(f32 near_z, f32 far_z) const;
|
||||
|
||||
/**
|
||||
* @brief 获取视图矩阵。
|
||||
*/
|
||||
math::m4x4 view() const;
|
||||
/**
|
||||
* @brief 获取投影矩阵。
|
||||
*/
|
||||
math::m4x4 projection() const;
|
||||
/**
|
||||
* @brief 获取投影矩阵逆矩阵。
|
||||
*/
|
||||
math::m4x4 inverse_projection() const;
|
||||
/**
|
||||
* @brief 获取视图投影矩阵。
|
||||
*/
|
||||
math::m4x4 view_projection() const;
|
||||
/**
|
||||
* @brief 获取视图投影逆矩阵。
|
||||
*/
|
||||
math::m4x4 inverse_view_projection() const;
|
||||
/**
|
||||
* @brief 获取上方向向量。
|
||||
*/
|
||||
math::v3 up() const;
|
||||
|
||||
/**
|
||||
* @brief 获取近裁剪面。
|
||||
*/
|
||||
f32 near_z() const;
|
||||
/**
|
||||
* @brief 获取远裁剪面。
|
||||
*/
|
||||
f32 far_z() const;
|
||||
/**
|
||||
* @brief 获取视场角。
|
||||
*/
|
||||
f32 field_of_view() const;
|
||||
/**
|
||||
* @brief 获取宽高比。
|
||||
*/
|
||||
f32 aspect_ratio() const;
|
||||
/**
|
||||
* @brief 获取视口宽度。
|
||||
*/
|
||||
f32 view_width() const;
|
||||
/**
|
||||
* @brief 获取视口高度。
|
||||
*/
|
||||
f32 view_height() const;
|
||||
/**
|
||||
* @brief 获取投影类型。
|
||||
*/
|
||||
type projection_type() const;
|
||||
/**
|
||||
* @brief 获取绑定实体 ID。
|
||||
*/
|
||||
id::id_type entity_id() const;
|
||||
private:
|
||||
camera_id _id{ id::invalid_id };
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
191
Engine/EngineAPI/GameEntity.h
Normal file
191
Engine/EngineAPI/GameEntity.h
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @file GameEntity.h
|
||||
* @brief 实体句柄、脚本基类与脚本注册机制定义。
|
||||
* @details
|
||||
* 该文件是引擎 ECS 对外脚本接口的核心入口:
|
||||
* - game_entity::entity 提供实体句柄及常用变换访问;
|
||||
* - script::entity_script 定义脚本生命周期与变换写接口;
|
||||
* - script::detail 提供脚本工厂注册、按名称哈希检索与宏封装。
|
||||
*/
|
||||
#pragma once
|
||||
#include "..\Components\ComponentsCommon.h"
|
||||
#include "TransformComponent.h"
|
||||
#include "ScriptComponent.h"
|
||||
|
||||
namespace XEngine{
|
||||
namespace game_entity {
|
||||
|
||||
/**
|
||||
* @brief 实体强类型标识符。
|
||||
*/
|
||||
DEFINE_TYPED_ID(entity_id);
|
||||
|
||||
/**
|
||||
* @brief 运行时实体句柄封装。
|
||||
*/
|
||||
class entity {
|
||||
public:
|
||||
/**
|
||||
* @brief 由实体 ID 构造句柄。
|
||||
*/
|
||||
constexpr explicit entity(entity_id id) : _id{ id } {}
|
||||
/**
|
||||
* @brief 构造无效实体句柄。
|
||||
*/
|
||||
constexpr entity() : _id{ id::invalid_id } {}
|
||||
/**
|
||||
* @brief 获取实体 ID。
|
||||
*/
|
||||
[[nodiscard]] constexpr entity_id get_id() const { return _id; }
|
||||
/**
|
||||
* @brief 判断实体句柄是否有效。
|
||||
*/
|
||||
[[nodiscard]] constexpr bool is_valid() const { return id::is_valid(_id); }
|
||||
|
||||
/**
|
||||
* @brief 获取实体变换组件句柄。
|
||||
*/
|
||||
[[nodiscard]] transform::component transform() const;
|
||||
/**
|
||||
* @brief 获取实体脚本组件句柄。
|
||||
*/
|
||||
[[nodiscard]] script::component script() const;
|
||||
|
||||
[[nodiscard]] math::v4 rotation() const { return transform().rotation(); }
|
||||
[[nodiscard]] math::v3 orientation() const { return transform().orientation(); }
|
||||
[[nodiscard]] math::v3 position() const { return transform().position(); }
|
||||
[[nodiscard]] math::v3 scale() const { return transform().scale(); }
|
||||
private:
|
||||
entity_id _id;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
namespace script {
|
||||
/**
|
||||
* @brief 实体脚本基类。
|
||||
* @details
|
||||
* 派生类可覆盖 begin_play/update,并通过受保护接口修改实体变换。
|
||||
*/
|
||||
class entity_script : public game_entity::entity {
|
||||
public:
|
||||
/**
|
||||
* @brief 析构脚本实例。
|
||||
*/
|
||||
virtual ~entity_script() = default;
|
||||
/**
|
||||
* @brief 脚本启动回调。
|
||||
*/
|
||||
virtual void begin_play() {}
|
||||
/**
|
||||
* @brief 脚本逐帧更新回调。
|
||||
* @param dt 帧时间步长。
|
||||
*/
|
||||
virtual void update(float) {}
|
||||
|
||||
protected:
|
||||
constexpr explicit entity_script(game_entity::entity entity)
|
||||
: game_entity::entity{ entity.get_id() }{}
|
||||
|
||||
void set_rotation(math::v4 rotation_quaternion) const {set_rotation(this, rotation_quaternion);}
|
||||
void set_orientation(math::v3 orientation_vector) const {set_orientation(this, orientation_vector);}
|
||||
void set_position(math::v3 position) const {set_position(this, position);}
|
||||
void set_scale(math::v3 scale) const { set_scale(this, scale); }
|
||||
|
||||
static void set_rotation(const game_entity::entity *const entity, math::v4 rotation_quaternion);
|
||||
static void set_orientation(const game_entity::entity *const entity, math::v3 orientation_vector);
|
||||
static void set_position(const game_entity::entity *const entity, math::v3 position);
|
||||
static void set_scale(const game_entity::entity *const entity, math::v3 scale);
|
||||
|
||||
};
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* @brief 脚本实例智能指针类型。
|
||||
*/
|
||||
using script_ptr = std::unique_ptr<entity_script>;
|
||||
#ifdef USESTDFUNC
|
||||
/**
|
||||
* @brief 基于 std::function 的脚本工厂签名。
|
||||
*/
|
||||
using script_creator = std::function<script_ptr(game_entity::entity entity)>;
|
||||
#else
|
||||
/**
|
||||
* @brief 基于函数指针的脚本工厂签名。
|
||||
*/
|
||||
using script_creator = script_ptr(*)(game_entity::entity entity);
|
||||
#endif // USESTDFUNC
|
||||
/**
|
||||
* @brief 脚本名哈希器类型。
|
||||
*/
|
||||
using string_hash = std::hash<std::string>;
|
||||
|
||||
/**
|
||||
* @brief 注册脚本工厂到脚本注册表。
|
||||
* @param tag 脚本哈希标签。
|
||||
* @param creator 脚本工厂函数。
|
||||
* @return 注册结果码。
|
||||
*/
|
||||
u8 register_script(size_t tag, script_creator creator);
|
||||
#ifdef USE_WITH_EDITOR
|
||||
extern "C" __declspec(dllexport)
|
||||
#endif
|
||||
/**
|
||||
* @brief 按哈希标签查询脚本工厂。
|
||||
* @param tag 脚本名哈希。
|
||||
* @return 对应脚本工厂函数。
|
||||
*/
|
||||
script_creator get_script_creator(size_t tag);
|
||||
|
||||
/**
|
||||
* @brief 默认脚本工厂模板函数。
|
||||
* @tparam script_class 脚本类型。
|
||||
* @param entity 绑定实体。
|
||||
* @return 新创建脚本实例。
|
||||
*/
|
||||
template<class script_class>
|
||||
script_ptr create_script(game_entity::entity entity)
|
||||
{
|
||||
assert(entity.is_valid());
|
||||
return std::make_unique<script_class>(entity);
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_WITH_EDITOR
|
||||
/**
|
||||
* @brief 向编辑器导出脚本名称。
|
||||
* @param name 脚本类名。
|
||||
* @return 写入结果码。
|
||||
*/
|
||||
u8 add_script_name(const char* name);
|
||||
|
||||
#define REGISTER_SCRIPT(TYPE) \
|
||||
namespace { \
|
||||
u8 _reg_##TYPE{ \
|
||||
XEngine::script::detail::register_script( \
|
||||
XEngine::script::detail::string_hash()(#TYPE), \
|
||||
&XEngine::script::detail::create_script<TYPE>) }; \
|
||||
const u8 _name_##TYPE \
|
||||
{ XEngine::script::detail::add_script_name(#TYPE) }; \
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#define REGISTER_SCRIPT(TYPE) \
|
||||
class TYPE; \
|
||||
namespace { \
|
||||
u8 _reg_##TYPE{ \
|
||||
XEngine::script::detail::register_script( \
|
||||
XEngine::script::detail::string_hash()(#TYPE), \
|
||||
&XEngine::script::detail::create_script<TYPE>) }; \
|
||||
}
|
||||
|
||||
#endif // USE_WITH_EDITOR
|
||||
|
||||
|
||||
}//namespace detail
|
||||
}//namespace script
|
||||
|
||||
}
|
||||
97
Engine/EngineAPI/Light.h
Normal file
97
Engine/EngineAPI/Light.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @file Light.h
|
||||
* @brief 光源句柄与光照参数访问接口定义。
|
||||
* @details
|
||||
* light 封装渲染层光源实例的轻量句柄语义,支持:
|
||||
* - 启停、强度、颜色等常用光照属性读写;
|
||||
* - 查询光源类型与绑定实体;
|
||||
* - 通过 light_set_key 区分不同光照集合。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
namespace XEngine::graphics {
|
||||
|
||||
|
||||
/**
|
||||
* @brief 光源强类型标识符。
|
||||
*/
|
||||
DEFINE_TYPED_ID(light_id);
|
||||
|
||||
/**
|
||||
* @brief 光源句柄对象。
|
||||
*/
|
||||
class light
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 光源类型枚举。
|
||||
*/
|
||||
enum type :u32
|
||||
{
|
||||
directioinal,
|
||||
point,
|
||||
spot,
|
||||
|
||||
count
|
||||
};
|
||||
|
||||
constexpr explicit light(light_id id, u64 light_set_key)
|
||||
:_light_set_key{ light_set_key }, _id { id } {}
|
||||
constexpr light() = default;
|
||||
constexpr light_id get_id() const { return _id; }
|
||||
constexpr u64 get_light_set_key() const { return _light_set_key; }
|
||||
constexpr bool is_valid() const { return id::is_valid(_id); }
|
||||
|
||||
/**
|
||||
* @brief 设置光源启用状态。
|
||||
* @param is_enabled true 表示启用。
|
||||
*/
|
||||
void is_enabled(bool is_enabled) const;
|
||||
/**
|
||||
* @brief 设置光照强度。
|
||||
* @param intensity 光照强度值。
|
||||
*/
|
||||
void intensity(f32 intensity) const;
|
||||
/**
|
||||
* @brief 设置光照颜色。
|
||||
* @param color RGB 颜色。
|
||||
*/
|
||||
void color(math::v3 color) const;
|
||||
|
||||
/**
|
||||
* @brief 查询光源启用状态。
|
||||
* @return 启用返回 true。
|
||||
*/
|
||||
bool is_enabled( ) const;
|
||||
/**
|
||||
* @brief 查询光照强度。
|
||||
* @return 当前强度值。
|
||||
*/
|
||||
f32 intensity( ) const;
|
||||
/**
|
||||
* @brief 查询光照颜色。
|
||||
* @return 当前 RGB 颜色。
|
||||
*/
|
||||
math::v3 color( ) const;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 查询光源类型。
|
||||
* @return 光源类型枚举值。
|
||||
*/
|
||||
type light_type() const;
|
||||
/**
|
||||
* @brief 查询绑定实体 ID。
|
||||
* @return 实体 ID。
|
||||
*/
|
||||
id::id_type entity_id() const;
|
||||
|
||||
private:
|
||||
u64 _light_set_key{ 0 };
|
||||
light_id _id{ id::invalid_id };
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
51
Engine/EngineAPI/ScriptComponent.h
Normal file
51
Engine/EngineAPI/ScriptComponent.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file ScriptComponent.h
|
||||
* @brief 脚本组件句柄定义。
|
||||
* @details
|
||||
* 该文件提供脚本组件在 ECS 中的强类型 ID 封装,用于:
|
||||
* - 标识实体绑定的脚本组件实例;
|
||||
* - 与组件系统统一的有效性检查;
|
||||
* - 在不暴露内部存储结构的前提下传递脚本组件引用。
|
||||
*/
|
||||
#pragma once
|
||||
#include "..\Components\ComponentsCommon.h"
|
||||
|
||||
|
||||
namespace XEngine::script {
|
||||
/**
|
||||
* @brief 脚本组件强类型标识符。
|
||||
*/
|
||||
DEFINE_TYPED_ID(script_id);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 脚本组件句柄对象。
|
||||
*/
|
||||
class component final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 由脚本组件 ID 构造句柄。
|
||||
* @param id 脚本组件 ID。
|
||||
*/
|
||||
constexpr explicit component(script_id id) : _id{ id } {}
|
||||
/**
|
||||
* @brief 构造无效脚本组件句柄。
|
||||
*/
|
||||
constexpr component() : _id{ id::invalid_id } {}
|
||||
/**
|
||||
* @brief 获取脚本组件 ID。
|
||||
* @return 组件 ID。
|
||||
*/
|
||||
constexpr script_id get_id() const { return _id; }
|
||||
/**
|
||||
* @brief 判断句柄是否有效。
|
||||
* @return 有效返回 true。
|
||||
*/
|
||||
constexpr bool is_valid() const { return id::is_valid(_id); }
|
||||
|
||||
private:
|
||||
script_id _id;
|
||||
|
||||
};
|
||||
}
|
||||
53
Engine/EngineAPI/TransformComponent.h
Normal file
53
Engine/EngineAPI/TransformComponent.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file TransformComponent.h
|
||||
* @brief 变换组件句柄与只读查询接口定义。
|
||||
* @details
|
||||
* component 以轻量句柄方式访问 ECS 中的变换数据,提供:
|
||||
* - 位置(position)、朝向(orientation)、缩放(scale)查询;
|
||||
* - 四元数旋转(rotation)读取;
|
||||
* - 与 id 系统一致的有效性判断语义。
|
||||
*/
|
||||
#pragma once
|
||||
#include "..\Components\ComponentsCommon.h"
|
||||
|
||||
|
||||
namespace XEngine::transform {
|
||||
/**
|
||||
* @brief 变换组件强类型标识符。
|
||||
*/
|
||||
DEFINE_TYPED_ID(transform_id);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 变换组件句柄对象。
|
||||
*/
|
||||
class component final
|
||||
{
|
||||
public:
|
||||
constexpr explicit component(transform_id id) : _id{ id } {}
|
||||
constexpr component() : _id{ id::invalid_id } {}
|
||||
constexpr transform_id get_id() const { return _id; }
|
||||
constexpr bool is_valid() const { return id::is_valid(_id); }
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取世界空间位置。
|
||||
*/
|
||||
math::v3 position() const;
|
||||
/**
|
||||
* @brief 获取朝向向量。
|
||||
*/
|
||||
math::v3 orientation() const;
|
||||
/**
|
||||
* @brief 获取缩放向量。
|
||||
*/
|
||||
math::v3 scale() const;
|
||||
/**
|
||||
* @brief 获取旋转四元数。
|
||||
*/
|
||||
math::v4 rotation() const;
|
||||
private:
|
||||
transform_id _id;
|
||||
|
||||
};
|
||||
}
|
||||
20
Engine/Platform/IncludeWindowCpp.h
Normal file
20
Engine/Platform/IncludeWindowCpp.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @file IncludeWindowCpp.h
|
||||
* @brief 以单编译单元方式引入 Window.cpp 的桥接头。
|
||||
* @details
|
||||
* 某些构建路径通过宏控制将 Window.cpp 以内联包含方式编译。
|
||||
* 该头文件负责:
|
||||
* - 定义 INCLUDE_WINDOW_CPP 触发实现体暴露;
|
||||
* - 包含 Window.cpp;
|
||||
* - 立即取消宏定义以避免污染后续编译单元。
|
||||
*/
|
||||
#pragma once
|
||||
/**
|
||||
* @brief 启用 Window.cpp 头内实现编译模式。
|
||||
*/
|
||||
#define INCLUDE_WINDOW_CPP 1
|
||||
#include "Window.cpp"
|
||||
/**
|
||||
* @brief 关闭 Window.cpp 头内实现编译模式。
|
||||
*/
|
||||
#undef INCLUDE_WINDOW_CPP
|
||||
33
Engine/Platform/Platform.h
Normal file
33
Engine/Platform/Platform.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file Platform.h
|
||||
* @brief 平台窗口生命周期管理接口。
|
||||
* @details
|
||||
* 对外提供窗口创建与销毁能力:
|
||||
* - create_window 根据可选初始化参数构建窗口;
|
||||
* - remove_window 根据 window_id 释放窗口资源;
|
||||
* - 保持与平台无关上层模块之间的最小依赖面。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
#include "Window.h"
|
||||
|
||||
|
||||
namespace XEngine::platform {
|
||||
|
||||
struct window_init_info;
|
||||
|
||||
/**
|
||||
* @brief 创建平台窗口。
|
||||
* @param init_info 可选初始化参数。
|
||||
* @return 创建后的窗口句柄对象。
|
||||
*/
|
||||
window create_window(const window_init_info *const init_info = nullptr);
|
||||
/**
|
||||
* @brief 销毁平台窗口。
|
||||
* @param id 目标窗口 ID。
|
||||
*/
|
||||
void remove_window(window_id id);
|
||||
|
||||
|
||||
|
||||
}
|
||||
47
Engine/Platform/PlatformTypes.h
Normal file
47
Engine/Platform/PlatformTypes.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file PlatformTypes.h
|
||||
* @brief 平台窗口系统公共类型定义。
|
||||
* @details
|
||||
* 该文件集中声明平台窗口创建所需的数据结构,包括:
|
||||
* - window_proc:应用层窗口消息回调签名;
|
||||
* - window_handle:原生窗口句柄别名;
|
||||
* - window_init_info:窗口创建参数(父窗口、标题、位置、尺寸)。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
|
||||
#ifdef _WIN64
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
namespace XEngine::platform {
|
||||
|
||||
/**
|
||||
* @brief 窗口消息回调函数签名。
|
||||
*/
|
||||
using window_proc = LRESULT(*)(HWND, UINT, WPARAM, LPARAM);
|
||||
/**
|
||||
* @brief 原生窗口句柄类型别名。
|
||||
*/
|
||||
using window_handle = HWND;
|
||||
|
||||
/**
|
||||
* @brief 窗口初始化参数。
|
||||
*/
|
||||
struct window_init_info
|
||||
{
|
||||
window_proc callback{ nullptr };
|
||||
window_handle parent{ nullptr };
|
||||
const wchar_t* caption{ nullptr };
|
||||
s32 left{ 0 };
|
||||
s32 top{ 0 };
|
||||
s32 width{ 0 };
|
||||
s32 height{ 0 };
|
||||
};
|
||||
}
|
||||
|
||||
#endif // _WIN64
|
||||
372
Engine/Platform/PlatformWin32.cpp
Normal file
372
Engine/Platform/PlatformWin32.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @file PlatformWin32.cpp
|
||||
* @brief Win32 平台窗口系统实现。
|
||||
* @details
|
||||
* 该文件实现平台窗口后端的完整行为,包括:
|
||||
* - Win32 窗口类注册与窗口创建;
|
||||
* - 窗口 ID 与 HWND 的双向关联管理;
|
||||
* - 大小变化、销毁、全屏切换等消息处理;
|
||||
* - 对 Platform.h / Window.h 暴露接口的底层支撑。
|
||||
*/
|
||||
#ifdef _WIN64
|
||||
#include "Platform.h"
|
||||
#include "PlatformTypes.h"
|
||||
|
||||
namespace XEngine::platform {
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* @brief Win32 窗口实例运行时状态。
|
||||
*/
|
||||
struct window_info
|
||||
{
|
||||
HWND hwnd{ nullptr };
|
||||
RECT client_area{ 0,0,1920,1080 };
|
||||
RECT fullscreen_area{};
|
||||
POINT top_left{ 0,0 };
|
||||
DWORD style{ WS_VISIBLE };
|
||||
bool is_fullscreen{ false };
|
||||
bool is_closed{ false };
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口对象存储池。
|
||||
*/
|
||||
utl::free_list<window_info> windows;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 根据窗口 ID 获取窗口状态对象。
|
||||
* @param id 窗口 ID。
|
||||
* @return 对应窗口状态引用。
|
||||
*/
|
||||
window_info&
|
||||
get_from_id(window_id id)
|
||||
{
|
||||
assert(windows[id].hwnd);
|
||||
return windows[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据 HWND 获取窗口状态对象。
|
||||
* @param handle 原生窗口句柄。
|
||||
* @return 对应窗口状态引用。
|
||||
*/
|
||||
window_info&
|
||||
get_from_handle(window_handle handle)
|
||||
{
|
||||
const window_id id{ (id::id_type)GetWindowLongPtr(handle, GWLP_USERDATA) };
|
||||
return get_from_id(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 标记窗口是否发生了可见尺寸变更。
|
||||
*/
|
||||
bool resized{ false };
|
||||
|
||||
/**
|
||||
* @brief 按目标客户区更新窗口尺寸与位置。
|
||||
* @param info 窗口状态。
|
||||
* @param area 目标客户区矩形。
|
||||
*/
|
||||
void
|
||||
resize_window(const window_info& info, const RECT& area)
|
||||
{
|
||||
RECT window_rect{ area };
|
||||
AdjustWindowRect(&window_rect, info.style, FALSE);
|
||||
|
||||
const s32 width{ window_rect.right - window_rect.left };
|
||||
const s32 height{ window_rect.bottom - window_rect.top };
|
||||
|
||||
MoveWindow(info.hwnd, info.top_left.x, info.top_left.y, width, height, true);
|
||||
}
|
||||
|
||||
/// <Window parameter implementation>
|
||||
|
||||
|
||||
/**
|
||||
* @brief 查询窗口是否处于全屏状态。
|
||||
* @param id 窗口 ID。
|
||||
* @return 全屏返回 true。
|
||||
*/
|
||||
bool
|
||||
is_window_fullscreen(window_id id)
|
||||
{
|
||||
assert(id != u32_invalid_id);
|
||||
return get_from_id(id).is_fullscreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询窗口原生句柄。
|
||||
* @param id 窗口 ID。
|
||||
* @return HWND 句柄。
|
||||
*/
|
||||
window_handle
|
||||
get_window_handle(window_id id)
|
||||
{
|
||||
return get_from_id(id).hwnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置窗口标题文本。
|
||||
* @param id 窗口 ID。
|
||||
* @param caption 标题文本。
|
||||
*/
|
||||
void
|
||||
set_window_caption(window_id id, const wchar_t* caption)
|
||||
{
|
||||
window_info& info{ get_from_id(id) };
|
||||
SetWindowText(info.hwnd, caption);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询窗口矩形(客户区或全屏区)。
|
||||
* @param id 窗口 ID。
|
||||
* @return 左上右下坐标向量。
|
||||
*/
|
||||
math::u32v4
|
||||
get_window_size(window_id id)
|
||||
{
|
||||
window_info& info{ get_from_id(id) };
|
||||
RECT& rect{ info.is_fullscreen ? info.fullscreen_area : info.client_area };
|
||||
return { (u32)rect.left,(u32)rect.top,(u32)rect.right,(u32)rect.bottom };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整窗口客户区尺寸。
|
||||
* @param id 窗口 ID。
|
||||
* @param width 新宽度。
|
||||
* @param height 新高度。
|
||||
*/
|
||||
void
|
||||
resize_window(window_id id, u32 width, u32 height)
|
||||
{
|
||||
window_info& info{ get_from_id(id) };
|
||||
// when we host the window in the level editor we just update
|
||||
// the internal data.
|
||||
if (info.style & WS_CHILD)
|
||||
{
|
||||
GetClientRect(info.hwnd, &info.client_area);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
RECT& area{ info.is_fullscreen ? info.fullscreen_area : info.client_area };
|
||||
area.bottom = area.top + height;
|
||||
area.right = area.left + width;
|
||||
|
||||
resize_window(info, area);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询窗口是否已被关闭。
|
||||
* @param id 窗口 ID。
|
||||
* @return 已关闭返回 true。
|
||||
*/
|
||||
bool
|
||||
is_window_close(window_id id)
|
||||
{
|
||||
return get_from_id(id).is_closed;
|
||||
}
|
||||
|
||||
/// <Window parameter implementation>
|
||||
|
||||
/**
|
||||
* @brief 平台内部默认窗口过程。
|
||||
* @param hwnd 窗口句柄。
|
||||
* @param msg 消息类型。
|
||||
* @param wparam 消息参数。
|
||||
* @param lparam 消息参数。
|
||||
* @return 消息处理结果。
|
||||
*/
|
||||
LRESULT CALLBACK internal_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
case WM_NCCREATE:
|
||||
{
|
||||
|
||||
DEBUG_OP(SetLastError(0));
|
||||
const window_id id{ windows.add() };
|
||||
windows[id].hwnd = hwnd;
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)id);
|
||||
assert(GetLastError() == 0);
|
||||
}
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
|
||||
get_from_handle(hwnd).is_closed = true;
|
||||
|
||||
break;
|
||||
|
||||
case WM_SIZE:
|
||||
resized = (wparam != SIZE_MINIMIZED);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (resized && GetAsyncKeyState(VK_LBUTTON) >= 0)
|
||||
{
|
||||
window_info& info{ get_from_handle(hwnd) };
|
||||
assert(info.hwnd);
|
||||
GetClientRect(info.hwnd, info.is_fullscreen ? &info.fullscreen_area : &info.client_area);
|
||||
resized = false;
|
||||
}
|
||||
|
||||
LONG_PTR long_ptr{ GetWindowLongPtr(hwnd, 0) };
|
||||
return long_ptr
|
||||
? ((window_proc)long_ptr)(hwnd, msg, wparam, lparam)
|
||||
: DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
|
||||
assert(GetLastError() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换窗口全屏状态。
|
||||
* @param id 窗口 ID。
|
||||
* @param is_fullscreen 目标全屏状态。
|
||||
*/
|
||||
void
|
||||
set_window_fullscreen(window_id id, bool is_fullscreen)
|
||||
{
|
||||
window_info& info{ windows[id] };
|
||||
if (info.is_fullscreen != is_fullscreen)
|
||||
{
|
||||
info.is_fullscreen = is_fullscreen;
|
||||
|
||||
if (is_fullscreen)
|
||||
{
|
||||
GetClientRect(info.hwnd, &info.client_area);
|
||||
RECT rect;
|
||||
GetWindowRect(info.hwnd, &rect);
|
||||
info.top_left.x = rect.left;
|
||||
info.top_left.y = rect.top;
|
||||
SetWindowLongPtr(info.hwnd, GWL_STYLE, info.style);
|
||||
ShowWindow(info.hwnd, SW_MAXIMIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowLongPtr(info.hwnd, GWL_STYLE, info.style);
|
||||
resize_window(info, info.client_area);
|
||||
ShowWindow(info.hwnd, SW_SHOWNORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* @brief 创建 Win32 窗口并注册到窗口池。
|
||||
* @param init_info 可选窗口初始化参数。
|
||||
* @return 创建成功的窗口句柄对象。
|
||||
*/
|
||||
window
|
||||
create_window(const window_init_info* init_info /* = nullptr */)
|
||||
{
|
||||
window_proc callback{ init_info ? init_info->callback : nullptr }; // 回调窗口过程:由初始化参数提供;未提供时为 nullptr。
|
||||
window_handle parent{ init_info ? init_info->parent : nullptr }; // 父窗口句柄:用于子窗口挂载;未提供时创建顶层窗口。
|
||||
|
||||
|
||||
WNDCLASSEX wc;
|
||||
ZeroMemory(&wc, sizeof(WNDCLASSEX));
|
||||
|
||||
// 初始化 WNDCLASSEX 字段。
|
||||
wc.cbSize = sizeof(WNDCLASSEX); // 结构体大小。
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW; // 水平/垂直尺寸变化时重绘。
|
||||
wc.lpfnWndProc = internal_window_proc; // 默认窗口过程入口。
|
||||
wc.cbClsExtra = 0; // 类额外字节数。
|
||||
wc.cbWndExtra = callback ? sizeof(callback) : 0; // 实例额外字节数:有回调时用于存储回调指针。
|
||||
wc.hInstance = 0; // 当前模块实例句柄(使用默认)。
|
||||
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 大图标。
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 光标。
|
||||
wc.hbrBackground = CreateSolidBrush(RGB(26, 48, 76)); // 背景画刷。
|
||||
wc.lpszMenuName = NULL; // 菜单名(无)。
|
||||
wc.lpszClassName = L"XWindow"; // 窗口类名。
|
||||
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // 小图标。
|
||||
|
||||
|
||||
// 注册窗口类。
|
||||
if (!RegisterClassEx(&wc))
|
||||
{
|
||||
// 若类已注册或注册失败,后续创建窗口时由系统返回结果。
|
||||
}
|
||||
|
||||
window_info info{};
|
||||
info.client_area.right = (init_info && init_info->width) ? info.client_area.left + init_info->width : info.client_area.right;
|
||||
info.client_area.bottom = (init_info && init_info->height) ? info.client_area.top + init_info->height : info.client_area.bottom;
|
||||
info.style |= parent ? WS_CHILD : WS_OVERLAPPEDWINDOW;
|
||||
|
||||
RECT rect{ info.client_area };
|
||||
AdjustWindowRect(&rect, info.style, FALSE);
|
||||
|
||||
const wchar_t* caption{ (init_info && init_info->caption) ? init_info->caption : L"XGame" };
|
||||
const s32 left{ (init_info) ? init_info->left : info.top_left.x };
|
||||
const s32 top{ (init_info) ? init_info->top : info.top_left.y };
|
||||
|
||||
const s32 width{ rect.right - rect.left };
|
||||
const s32 height{ rect.bottom - rect.top };
|
||||
|
||||
|
||||
// 创建窗口实例。
|
||||
|
||||
info.hwnd = CreateWindowEx(
|
||||
0, // 扩展样式。
|
||||
wc.lpszClassName, // 窗口类名。
|
||||
caption, // 窗口标题。
|
||||
info.style, // 窗口样式。
|
||||
|
||||
// 尺寸与位置。
|
||||
left, top, width, height,
|
||||
|
||||
parent, // 父窗口句柄(nullptr 表示顶层窗口)。
|
||||
nullptr, // 菜单句柄(未使用)。
|
||||
nullptr, // 实例句柄(未显式传入)。
|
||||
nullptr // 创建参数(未使用)。
|
||||
);
|
||||
|
||||
if (info.hwnd)
|
||||
{
|
||||
DEBUG_OP(SetLastError(0));
|
||||
|
||||
if (callback) SetWindowLongPtr(info.hwnd, 0, (LONG_PTR)callback);
|
||||
assert(GetLastError() == 0);
|
||||
|
||||
ShowWindow(info.hwnd, SW_SHOWNORMAL);
|
||||
UpdateWindow(info.hwnd);
|
||||
|
||||
window_id id{ (id::id_type)GetWindowLongPtr(info.hwnd, GWLP_USERDATA) };
|
||||
windows[id] = info;
|
||||
return window{ id };
|
||||
}
|
||||
|
||||
return {}; // 创建失败时返回无效窗口句柄。
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁窗口并从窗口池移除。
|
||||
* @param id 窗口 ID。
|
||||
*/
|
||||
void
|
||||
remove_window(window_id id)
|
||||
{
|
||||
window_info& info{ get_from_id(id) };
|
||||
DestroyWindow(info.hwnd);
|
||||
windows.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
#include "IncludeWindowCpp.h"
|
||||
|
||||
|
||||
#endif // _WIN64
|
||||
116
Engine/Platform/Window.cpp
Normal file
116
Engine/Platform/Window.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @file Window.cpp
|
||||
* @brief window 句柄对象成员函数实现。
|
||||
* @details
|
||||
* 本文件将 window 类的高层接口转发到平台后端函数:
|
||||
* - full screen、caption、resize 等操作;
|
||||
* - 原生句柄与尺寸查询;
|
||||
* - 窗口关闭状态查询。
|
||||
* 通过 INCLUDE_WINDOW_CPP 宏控制其编译包含方式。
|
||||
*/
|
||||
#if INCLUDE_WINDOW_CPP
|
||||
#include "Window.h"
|
||||
|
||||
namespace XEngine::platform {
|
||||
|
||||
|
||||
/**
|
||||
* @brief 设置窗口全屏状态。
|
||||
* @param is_fullscreen 目标全屏状态。
|
||||
*/
|
||||
void
|
||||
window::set_fullscreen(bool is_fullscreen) const
|
||||
{
|
||||
assert(is_valid());
|
||||
set_window_fullscreen(_id, is_fullscreen);
|
||||
}
|
||||
/**
|
||||
* @brief 查询窗口是否处于全屏状态。
|
||||
* @return 全屏返回 true。
|
||||
*/
|
||||
bool
|
||||
window::is_fullscreen() const
|
||||
{
|
||||
assert(is_valid());
|
||||
return is_window_fullscreen(_id);
|
||||
}
|
||||
/**
|
||||
* @brief 获取原生窗口句柄。
|
||||
* @return 平台相关窗口句柄指针。
|
||||
*/
|
||||
void*
|
||||
window::handle() const
|
||||
{
|
||||
assert(is_valid());
|
||||
return get_window_handle(_id);
|
||||
}
|
||||
/**
|
||||
* @brief 设置窗口标题。
|
||||
* @param caption 标题文本。
|
||||
*/
|
||||
void
|
||||
window::set_caption(const wchar_t* caption) const
|
||||
{
|
||||
assert(is_valid());
|
||||
set_window_caption(_id, caption);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取窗口矩形坐标。
|
||||
* @return 左上右下坐标向量。
|
||||
*/
|
||||
math::u32v4 window::size() const
|
||||
{
|
||||
assert(is_valid());
|
||||
return get_window_size(_id);
|
||||
}
|
||||
/**
|
||||
* @brief 调整窗口客户区尺寸。
|
||||
* @param width 目标宽度。
|
||||
* @param height 目标高度。
|
||||
*/
|
||||
void
|
||||
window::resize(u32 width, u32 height)const
|
||||
{
|
||||
assert(is_valid());
|
||||
resize_window(_id, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取窗口当前宽度。
|
||||
* @return 宽度像素值。
|
||||
*/
|
||||
u32
|
||||
window::width()const
|
||||
{
|
||||
assert(is_valid());
|
||||
math::u32v4 s{ size() };
|
||||
return s.z - s.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取窗口当前高度。
|
||||
* @return 高度像素值。
|
||||
*/
|
||||
u32
|
||||
window::height()const
|
||||
{
|
||||
assert(is_valid());
|
||||
math::u32v4 s{ size() };
|
||||
return s.w - s.y;
|
||||
}
|
||||
/**
|
||||
* @brief 查询窗口是否已关闭。
|
||||
* @return 已关闭返回 true。
|
||||
*/
|
||||
bool
|
||||
window::is_closed()const
|
||||
{
|
||||
assert(is_valid());
|
||||
return is_window_close(_id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //INCLUDE_WINDOW_CPP
|
||||
99
Engine/Platform/Window.h
Normal file
99
Engine/Platform/Window.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @file Window.h
|
||||
* @brief 跨模块窗口句柄对象与基础窗口操作接口。
|
||||
* @details
|
||||
* window 是平台层窗口对象的轻量包装,向上层提供:
|
||||
* - 全屏切换、标题设置、尺寸调整;
|
||||
* - 原生窗口句柄访问;
|
||||
* - 关闭状态与尺寸查询。
|
||||
* 具体平台行为由 PlatformWin32.cpp 等实现体完成。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
|
||||
namespace XEngine::platform {
|
||||
/**
|
||||
* @brief 窗口强类型标识符。
|
||||
*/
|
||||
DEFINE_TYPED_ID(window_id);
|
||||
|
||||
/**
|
||||
* @brief 平台窗口句柄对象。
|
||||
*/
|
||||
class window
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 由窗口 ID 构造句柄。
|
||||
* @param id 窗口 ID。
|
||||
*/
|
||||
constexpr explicit window(window_id id) : _id{ id } {}
|
||||
/**
|
||||
* @brief 构造无效窗口句柄。
|
||||
*/
|
||||
constexpr window() = default;
|
||||
/**
|
||||
* @brief 获取窗口 ID。
|
||||
* @return 窗口 ID。
|
||||
*/
|
||||
constexpr window_id get_id() const { return _id; }
|
||||
/**
|
||||
* @brief 判断窗口句柄是否有效。
|
||||
* @return 有效返回 true。
|
||||
*/
|
||||
constexpr bool is_valid() const { return id::is_valid(_id); }
|
||||
|
||||
|
||||
/**
|
||||
* @brief 设置全屏状态。
|
||||
* @param is_fullscreen true 表示全屏。
|
||||
*/
|
||||
void set_fullscreen(bool is_fullscreen) const;
|
||||
/**
|
||||
* @brief 查询是否全屏。
|
||||
* @return 全屏返回 true。
|
||||
*/
|
||||
bool is_fullscreen() const;
|
||||
/**
|
||||
* @brief 获取原生窗口句柄。
|
||||
* @return 平台句柄指针。
|
||||
*/
|
||||
void* handle() const;
|
||||
/**
|
||||
* @brief 设置窗口标题文本。
|
||||
* @param caption 标题字符串。
|
||||
*/
|
||||
void set_caption(const wchar_t* caption) const;
|
||||
/**
|
||||
* @brief 获取窗口矩形坐标。
|
||||
* @return 左上右下坐标向量。
|
||||
*/
|
||||
math::u32v4 size() const;
|
||||
/**
|
||||
* @brief 调整窗口大小。
|
||||
* @param width 目标宽度。
|
||||
* @param height 目标高度。
|
||||
*/
|
||||
void resize(u32 width, u32 height)const;
|
||||
/**
|
||||
* @brief 获取窗口宽度。
|
||||
* @return 宽度像素值。
|
||||
*/
|
||||
u32 width()const;
|
||||
/**
|
||||
* @brief 获取窗口高度。
|
||||
* @return 高度像素值。
|
||||
*/
|
||||
u32 height()const;
|
||||
/**
|
||||
* @brief 查询窗口是否已关闭。
|
||||
* @return 已关闭返回 true。
|
||||
*/
|
||||
bool is_closed()const;
|
||||
|
||||
private:
|
||||
window_id _id{ id::invalid_id };
|
||||
|
||||
};
|
||||
}
|
||||
169
Engine/Utilities/FreeList.h
Normal file
169
Engine/Utilities/FreeList.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @file FreeList.h
|
||||
* @brief 带复用槽位的稀疏对象容器模板。
|
||||
* @details
|
||||
* free_list 维护“已使用槽位 + 空闲链”结构,支持:
|
||||
* - O(1) 近似开销的新增与删除;
|
||||
* - 删除后槽位复用,降低频繁分配释放成本;
|
||||
* - 以索引作为外部句柄,与 id 系统协同使用。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
namespace XEngine::utl {
|
||||
|
||||
#if USE_STL_VECTOR
|
||||
#pragma message("WARNING: using utl::free_list with std::vector result in duplicate calls to class constructor!");
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
class free_list
|
||||
{
|
||||
static_assert(sizeof(T) >= sizeof(u32));
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造空容器。
|
||||
*/
|
||||
free_list() = default;
|
||||
|
||||
/**
|
||||
* @brief 预留底层存储容量。
|
||||
* @param count 预留元素数量。
|
||||
*/
|
||||
explicit free_list(u32 count)
|
||||
{
|
||||
_array.reserve(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁容器并校验无存活元素。
|
||||
*/
|
||||
~free_list()
|
||||
{
|
||||
assert(!_size);
|
||||
|
||||
#if USE_STL_VECTOR
|
||||
memset(_array.data(), 0, _array.size() * sizeof(T));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 新增一个元素并返回槽位 ID。
|
||||
* @tparam params 构造参数类型。
|
||||
* @param p 构造参数。
|
||||
* @return 新元素 ID。
|
||||
*/
|
||||
template<class ... params>
|
||||
constexpr u32 add(params&&... p)
|
||||
{
|
||||
u32 id{ u32_invalid_id };
|
||||
if (_next_free_index == u32_invalid_id)
|
||||
{
|
||||
id = (u32)_array.size();
|
||||
_array.emplace_back(std::forward<params>(p)...);
|
||||
}
|
||||
else
|
||||
{
|
||||
id = _next_free_index;
|
||||
assert(id < _array.size() && already_removed(id));
|
||||
_next_free_index = *(const u32 *const)std::addressof(_array[id]);
|
||||
new (std::addressof(_array[id])) T(std::forward<params>(p)...);
|
||||
}
|
||||
++_size;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除指定 ID 的元素并回收到空闲链。
|
||||
* @param id 元素 ID。
|
||||
*/
|
||||
constexpr void remove(u32 id)
|
||||
{
|
||||
assert(id < _array.size() && !already_removed(id));
|
||||
T& item{ _array[id] };
|
||||
item.~T();
|
||||
DEBUG_OP(memset(std::addressof(_array[id]), 0xcc, sizeof(T)));
|
||||
*(u32 *const)std::addressof(_array[id]) = _next_free_index;
|
||||
_next_free_index = id;
|
||||
--_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前有效元素数量。
|
||||
* @return 元素数量。
|
||||
*/
|
||||
constexpr u32 size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取当前已分配槽位总数。
|
||||
* @return 容量值。
|
||||
*/
|
||||
constexpr u32 capacity() const
|
||||
{
|
||||
return _array.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 判断容器是否为空。
|
||||
* @return 空返回 true。
|
||||
*/
|
||||
constexpr bool empty() const
|
||||
{
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按 ID 访问元素。
|
||||
* @param id 元素 ID。
|
||||
* @return 元素引用。
|
||||
*/
|
||||
[[nodiscard]] constexpr T& operator[](u32 id)
|
||||
{
|
||||
assert(id < _array.size() && !already_removed(id));
|
||||
return _array[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按 ID 访问常量元素。
|
||||
* @param id 元素 ID。
|
||||
* @return 常量元素引用。
|
||||
*/
|
||||
[[nodiscard]] constexpr const T& operator[](u32 id) const
|
||||
{
|
||||
assert(id < _array.size() && !already_removed(id));
|
||||
return _array[id];
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr bool already_removed(u32 id) const
|
||||
{
|
||||
if constexpr (sizeof(T) > sizeof(u32))
|
||||
{
|
||||
u32 i{ sizeof(u32) };
|
||||
const u8 *const p{ (const u8 *const)std::addressof(_array[id]) };
|
||||
while ((p[i] == 0xcc) && (i < sizeof(T))) ++i;
|
||||
return i == sizeof(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_STL_VECTOR
|
||||
utl::vector<T> _array;
|
||||
#else
|
||||
utl::vector<T, false> _array;
|
||||
#endif
|
||||
u32 _next_free_index{ u32_invalid_id };
|
||||
u32 _size{ 0 };
|
||||
};
|
||||
|
||||
}
|
||||
154
Engine/Utilities/IOStream.h
Normal file
154
Engine/Utilities/IOStream.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @file IOStream.h
|
||||
* @brief 面向内存缓冲区的二进制流读写工具。
|
||||
* @details
|
||||
* 定义 reader/writer 两个轻量类,用于在连续内存上顺序序列化:
|
||||
* - 支持算术类型按字节写入与读取;
|
||||
* - 支持原始块拷贝与位置跳过;
|
||||
* - 通过边界断言降低越界读写风险。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
namespace XEngine::utl {
|
||||
|
||||
/**
|
||||
* @brief 只读内存二进制流。
|
||||
*/
|
||||
class blob_stream_reader
|
||||
{
|
||||
public:
|
||||
DISABLE_COPY_AND_MOVE(blob_stream_reader);
|
||||
/**
|
||||
* @brief 绑定外部只读缓冲区。
|
||||
* @param buffer 缓冲区首地址。
|
||||
*/
|
||||
explicit blob_stream_reader(const u8* buffer)
|
||||
:_buffer{buffer}, _position{buffer}
|
||||
{
|
||||
assert(buffer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 按类型读取一个值并推进游标。
|
||||
* @tparam T 算术类型。
|
||||
* @return 读取到的值。
|
||||
*/
|
||||
template<typename T>
|
||||
[[nodiscard]] T reader()
|
||||
{
|
||||
static_assert(std::is_arithmetic_v<T>, "Template argument should be a primitive type.");
|
||||
T value{ *((T*)_position) };
|
||||
_position += sizeof(T);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取原始字节块并推进游标。
|
||||
* @param buffer 输出缓冲区。
|
||||
* @param length 读取字节数。
|
||||
*/
|
||||
void read(u8* buffer, size_t length)
|
||||
{
|
||||
memcpy(buffer, _position, length);
|
||||
_position += length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跳过指定字节数。
|
||||
* @param offset 偏移字节数。
|
||||
*/
|
||||
void skip(size_t offset)
|
||||
{
|
||||
_position += offset;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const u8 *const buffer_start() const { return _buffer; }
|
||||
[[nodiscard]] constexpr const u8 *const position() const { return _position; }
|
||||
[[nodiscard]] constexpr size_t offset() const { return _position - _buffer; }
|
||||
|
||||
private:
|
||||
const u8 *const _buffer;
|
||||
const u8* _position;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief 可写内存二进制流。
|
||||
*/
|
||||
class blob_stream_writer
|
||||
{
|
||||
public:
|
||||
DISABLE_COPY_AND_MOVE(blob_stream_writer);
|
||||
/**
|
||||
* @brief 绑定外部可写缓冲区。
|
||||
* @param buffer 缓冲区首地址。
|
||||
* @param buffer_size 缓冲区大小。
|
||||
*/
|
||||
explicit blob_stream_writer(u8* buffer, size_t buffer_size)
|
||||
:_buffer{ buffer }, _position{ buffer }, _buffer_size(buffer_size)
|
||||
{
|
||||
assert(buffer && buffer_size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 写入一个算术类型值并推进游标。
|
||||
* @tparam T 算术类型。
|
||||
* @param value 待写入值。
|
||||
*/
|
||||
template<typename T>
|
||||
void write(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic_v<T>, "Template argument should be a primitive type.");
|
||||
assert(&_position[sizeof(T)] <= &_buffer[_buffer_size]);
|
||||
*((T*)_position) = value;
|
||||
_position += sizeof(T);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写入字符缓冲区。
|
||||
* @param buffer 输入缓冲区。
|
||||
* @param length 写入字节数。
|
||||
*/
|
||||
void write(const char* buffer, size_t length)
|
||||
{
|
||||
assert(&_position[length] <= &_buffer[_buffer_size]);
|
||||
memcpy(_position, buffer, length);
|
||||
_position += length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写入字节缓冲区。
|
||||
* @param buffer 输入缓冲区。
|
||||
* @param length 写入字节数。
|
||||
*/
|
||||
void write(const u8* buffer, size_t length)
|
||||
{
|
||||
assert(&_position[length] <= &_buffer[_buffer_size]);
|
||||
memcpy(_position, buffer, length);
|
||||
_position += length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跳过指定字节数。
|
||||
* @param offset 偏移字节数。
|
||||
*/
|
||||
void skip(size_t offset)
|
||||
{
|
||||
assert(&_position[offset] <= &_buffer[_buffer_size]);
|
||||
_position += offset;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const u8 *const buffer_start() const { return _buffer; }
|
||||
[[nodiscard]] constexpr const u8 *const buffer_end() const { return &_buffer[_buffer_size]; }
|
||||
[[nodiscard]] constexpr const u8 *const position() const { return _position; }
|
||||
[[nodiscard]] constexpr size_t offset() const { return _position - _buffer; }
|
||||
|
||||
private:
|
||||
u8 *const _buffer;
|
||||
u8* _position;
|
||||
size_t _buffer_size;
|
||||
};
|
||||
}
|
||||
185
Engine/Utilities/Math.h
Normal file
185
Engine/Utilities/Math.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @file Math.h
|
||||
* @brief 数学工具函数与二进制对齐辅助函数。
|
||||
* @details
|
||||
* 提供模板化的数值打包/解包与内存对齐能力,包括:
|
||||
* - clamp 与归一化浮点量化打包;
|
||||
* - 固定/动态对齐粒度下的向上与向下对齐;
|
||||
* - 基于 SIMD 指令的 CRC32 快速计算辅助。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
#include "MathTypes.h"
|
||||
|
||||
namespace XEngine::math {
|
||||
|
||||
/**
|
||||
* @brief 将值限制在给定区间内。
|
||||
* @tparam T 标量类型。
|
||||
* @param value 输入值。
|
||||
* @param min 下界。
|
||||
* @param max 上界。
|
||||
* @return 限制后的值。
|
||||
*/
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr T
|
||||
clamp(T value, T min, T max)
|
||||
{
|
||||
return (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 将 [0,1] 浮点值量化到指定位宽的无符号整数。
|
||||
* @tparam bits 量化位宽。
|
||||
* @param i 归一化输入值。
|
||||
* @return 量化结果。
|
||||
*/
|
||||
template<u32 bits>
|
||||
[[nodiscard]] constexpr u32
|
||||
pack_unit_float(f32 i)
|
||||
{
|
||||
static_assert(bits <= sizeof(u32) * 8);
|
||||
assert(i >= 0.f && i <= 1.f);
|
||||
constexpr f32 intervals{ (f32)(((u32)1 << bits) - 1) };
|
||||
return (u32)(intervals * i + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将 [min,max] 浮点值量化到指定位宽。
|
||||
* @tparam bits 量化位宽。
|
||||
* @param i 输入值。
|
||||
* @param min 下界。
|
||||
* @param max 上界。
|
||||
* @return 量化结果。
|
||||
*/
|
||||
template<u32 bits>
|
||||
[[nodiscard]] constexpr u32
|
||||
pack_float(f32 i, f32 min, f32 max)
|
||||
{
|
||||
assert(min < max);
|
||||
assert(i <= max && i >= min);
|
||||
const f32 distance{ (i - min) / (max - min) };
|
||||
return pack_unit_float<bits>(distance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 将量化整数还原为 [0,1] 浮点值。
|
||||
* @tparam bits 量化位宽。
|
||||
* @param i 量化输入值。
|
||||
* @return 反量化结果。
|
||||
*/
|
||||
template<u32 bits>
|
||||
[[nodiscard]] constexpr f32
|
||||
unpack_unit_float(u32 i)
|
||||
{
|
||||
static_assert(bits <= sizeof(u32) * 8);
|
||||
assert(i < ((u32)i << bits));
|
||||
constexpr f32 intervals{ (f32)(((u32)1 << bits) - 1) };
|
||||
return (f32)i / intervals;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 将量化值还原到 [min,max] 区间。
|
||||
* @tparam bits 量化位宽。
|
||||
* @param i 量化输入值。
|
||||
* @param min 下界。
|
||||
* @param max 上界。
|
||||
* @return 反量化结果。
|
||||
*/
|
||||
template<u32 bits>
|
||||
[[nodiscard]] constexpr f32
|
||||
unpack_float(f32 i, f32 min, f32 max)
|
||||
{
|
||||
assert(min < max);
|
||||
return unpack_unit_float<bits>(i) * (max - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按固定对齐值向上对齐。
|
||||
* @tparam alignment 对齐粒度(2 的幂)。
|
||||
* @param size 原始大小。
|
||||
* @return 向上对齐后的大小。
|
||||
*/
|
||||
template<u64 alignment>
|
||||
[[nodiscard]] constexpr u64
|
||||
align_size_up(u64 size)
|
||||
{
|
||||
static_assert(alignment, "Alignment must be non-zero.");
|
||||
constexpr u64 mask{ alignment - 1 };
|
||||
static_assert(!(alignment & mask), "Alignment should be a power of 2.");
|
||||
return ((size + mask) & ~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按固定对齐值向下对齐。
|
||||
* @tparam alignment 对齐粒度(2 的幂)。
|
||||
* @param size 原始大小。
|
||||
* @return 向下对齐后的大小。
|
||||
*/
|
||||
template<u64 alignment>
|
||||
[[nodiscard]] constexpr u64
|
||||
align_size_down(u64 size)
|
||||
{
|
||||
static_assert(alignment, "Alignment must be non-zero.");
|
||||
constexpr u64 mask{ alignment - 1 };
|
||||
static_assert(!(alignment & mask), "Alignment should be a power of 2.");
|
||||
return (size & ~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按运行时对齐值向上对齐。
|
||||
* @param size 原始大小。
|
||||
* @param alignment 对齐粒度(2 的幂)。
|
||||
* @return 向上对齐后的大小。
|
||||
*/
|
||||
[[nodiscard]] constexpr u64
|
||||
align_size_up(u64 size, u64 alignment)
|
||||
{
|
||||
assert(alignment && "Alignment must be non-zero.");
|
||||
const u64 mask{ alignment - 1 };
|
||||
assert(!(alignment & mask) && "Alignment should be a power of 2.");
|
||||
return ((size + mask) & ~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按运行时对齐值向下对齐。
|
||||
* @param size 原始大小。
|
||||
* @param alignment 对齐粒度(2 的幂)。
|
||||
* @return 向下对齐后的大小。
|
||||
*/
|
||||
[[nodiscard]] constexpr u64
|
||||
align_size_down(u64 size, u64 alignment)
|
||||
{
|
||||
assert(alignment && "Alignment must be non-zero.");
|
||||
const u64 mask{ alignment - 1 };
|
||||
assert(!(alignment & mask) && "Alignment should be a power of 2.");
|
||||
return (size & ~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算数据块的 64 位 CRC32 累积值。
|
||||
* @param data 输入字节流。
|
||||
* @param size 输入长度。
|
||||
* @return CRC32 累积结果。
|
||||
*/
|
||||
[[nodiscard]] constexpr u64
|
||||
calcect_crc32_u64(const u8 *const data, u64 size)
|
||||
{
|
||||
assert(size >= sizeof(u64));
|
||||
u64 crc{ 0 };
|
||||
const u8* at{ data };
|
||||
const u8 *const end{ data + align_size_down<sizeof(u64)>(size) };
|
||||
while (at < end)
|
||||
{
|
||||
crc = _mm_crc32_u64(crc, *((const u64*)at));
|
||||
at += sizeof(u64);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
}
|
||||
80
Engine/Utilities/MathTypes.h
Normal file
80
Engine/Utilities/MathTypes.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @file MathTypes.h
|
||||
* @brief 数学常量与 DirectX 数学类型别名集合。
|
||||
* @details
|
||||
* 统一定义引擎常用数学基础元素:
|
||||
* - 常量:pi、two_pi、epsilon;
|
||||
* - 向量/矩阵别名:v2/v3/v4、m3x3/m4x4 等;
|
||||
* - 与 Win64 下 DirectXMath 类型的映射与对齐变体。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
namespace XEngine::math {
|
||||
|
||||
//constexpr long double pi = 3.141592653589793238462643383279L;
|
||||
/**
|
||||
* @brief 圆周率常量。
|
||||
*/
|
||||
constexpr float pi = 3.1415927f;
|
||||
/**
|
||||
* @brief 两倍圆周率常量。
|
||||
*/
|
||||
constexpr float two_pi = 6.2831853f;
|
||||
|
||||
/**
|
||||
* @brief 浮点比较容差常量。
|
||||
*/
|
||||
constexpr float epsilon = 1e-5f;
|
||||
|
||||
#if defined(_WIN64)
|
||||
|
||||
/**
|
||||
* @brief DirectXMath 向量类型别名。
|
||||
* @details
|
||||
* - 无 A 后缀类型使用常规布局;
|
||||
* - A 后缀类型要求 16 字节对齐,适合 SIMD 对齐访问场景。
|
||||
*/
|
||||
using v2 = DirectX::XMFLOAT2;
|
||||
using v2a = DirectX::XMFLOAT2A;
|
||||
using v3 = DirectX::XMFLOAT3;
|
||||
using v3a = DirectX::XMFLOAT3A;
|
||||
using v4 = DirectX::XMFLOAT4;
|
||||
using v4a = DirectX::XMFLOAT4A;
|
||||
|
||||
/**
|
||||
* @brief DirectXMath 整型向量别名。
|
||||
*/
|
||||
using u32v2 = DirectX::XMUINT2;
|
||||
using u32v3 = DirectX::XMUINT3;
|
||||
using u32v4 = DirectX::XMUINT4;
|
||||
using s32v2 = DirectX::XMINT2;
|
||||
using s32v3 = DirectX::XMINT3;
|
||||
using s32v4 = DirectX::XMINT4;
|
||||
|
||||
/**
|
||||
* @brief 标量浮点向量别名。
|
||||
*/
|
||||
using f32v2 = DirectX::XMFLOAT2;
|
||||
using f32v3 = DirectX::XMFLOAT3;
|
||||
using f32v4 = DirectX::XMFLOAT4;
|
||||
|
||||
/**
|
||||
* @brief DirectXMath 矩阵与寄存器向量别名。
|
||||
*/
|
||||
using Vec4 = DirectX::XMVECTOR;
|
||||
using Mat4 = DirectX::XMMATRIX;
|
||||
using Mat3 = DirectX::XMFLOAT3X3;
|
||||
using Mat4f = DirectX::XMFLOAT4X4;
|
||||
using m3x3 = DirectX::XMFLOAT3X3;
|
||||
using m4x4 = DirectX::XMFLOAT4X4;
|
||||
using m4x4a = DirectX::XMFLOAT4X4A;
|
||||
using vector4 = DirectX::XMVECTOR;
|
||||
using MAT4 = DirectX::XMMATRIX;
|
||||
using vector = DirectX::XMVECTOR;
|
||||
using Quat = DirectX::XMVECTOR;
|
||||
|
||||
#endif // (_WIN64)
|
||||
|
||||
|
||||
}
|
||||
86
Engine/Utilities/Utilities.h
Normal file
86
Engine/Utilities/Utilities.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @file Utilities.h
|
||||
* @brief 公共容器与工具模块聚合入口。
|
||||
* @details
|
||||
* 通过编译开关统一切换 STL 与自定义容器实现,并集中导出:
|
||||
* - vector/deque 等基础容器别名;
|
||||
* - erase_unordered 辅助函数;
|
||||
* - FreeList 等常用数据结构头文件。
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
/**
|
||||
* @brief 是否使用 STL vector 作为基础容器。
|
||||
*/
|
||||
#define USE_STL_VECTOR 0
|
||||
/**
|
||||
* @brief 是否启用 STL deque 别名。
|
||||
*/
|
||||
#define USE_STL_DRQUE 1
|
||||
|
||||
#if USE_STL_VECTOR
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
namespace XEngine::utl {
|
||||
template<typename T>
|
||||
using vector = std::vector<T>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief 通过交换尾元素方式无序删除。
|
||||
* @tparam T 容器类型。
|
||||
* @param v 目标容器。
|
||||
* @param index 删除下标。
|
||||
*/
|
||||
template<typename T>
|
||||
void erase_unordered(T& v, size_t index)
|
||||
{
|
||||
if (v.size() > 1)
|
||||
{
|
||||
std::iter_swap(v.begin() + index, v.end() - 1);
|
||||
v.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
v.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
#include "Vector.h"
|
||||
|
||||
namespace XEngine::utl {
|
||||
/**
|
||||
* @brief 调用自定义容器的无序删除接口。
|
||||
* @tparam T 容器类型。
|
||||
* @param v 目标容器。
|
||||
* @param index 删除下标。
|
||||
*/
|
||||
template<typename T>
|
||||
void erase_unordered(T& v, size_t index)
|
||||
{
|
||||
v.erase_unordered(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if USE_STL_DRQUE
|
||||
#include <deque>
|
||||
namespace XEngine::utl {
|
||||
template<typename T>
|
||||
using deque = std::deque<T>;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
namespace XEngine::utl {
|
||||
}
|
||||
|
||||
#include "FreeList.h"
|
||||
463
Engine/Utilities/Vector.h
Normal file
463
Engine/Utilities/Vector.h
Normal file
@@ -0,0 +1,463 @@
|
||||
/**
|
||||
* @file Vector.h
|
||||
* @brief 自定义动态数组容器实现。
|
||||
* @details
|
||||
* 该容器提供接近 std::vector 的常用能力,并支持可选析构策略:
|
||||
* - 动态扩容、插入、删除与无序删除;
|
||||
* - 连续内存访问与迭代器接口;
|
||||
* - 在性能敏感场景下复用底层内存分配行为。
|
||||
*/
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
|
||||
namespace XEngine::utl {
|
||||
|
||||
/**
|
||||
* @brief 自定义连续内存动态数组。
|
||||
* @tparam T 元素类型。
|
||||
* @tparam destruct 是否在删除时调用析构。
|
||||
*/
|
||||
template<typename T, bool destruct = true>
|
||||
class vector
|
||||
{
|
||||
|
||||
public:
|
||||
// Default constructor. Doesn~t allocate memory.
|
||||
/**
|
||||
* @brief 构造空容器。
|
||||
*/
|
||||
vector() = default;
|
||||
|
||||
/**
|
||||
* @brief 构造并调整到指定元素数量。
|
||||
* @param count 目标元素数量。
|
||||
*/
|
||||
constexpr explicit vector(u64 count)
|
||||
{
|
||||
resize(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 构造并填充指定数量元素。
|
||||
* @param count 目标元素数量。
|
||||
* @param value 填充值。
|
||||
*/
|
||||
constexpr explicit vector(u64 count, const T& value)
|
||||
{
|
||||
resize(count, value);
|
||||
}
|
||||
|
||||
constexpr vector(const vector& o)
|
||||
{
|
||||
*this = o;
|
||||
}
|
||||
|
||||
constexpr vector(vector&& o)
|
||||
:_capacity{o._capacity},
|
||||
_size{o._size},
|
||||
_data{o._data}
|
||||
{
|
||||
o.reset();
|
||||
}
|
||||
|
||||
|
||||
constexpr vector& operator=(const vector& o)
|
||||
{
|
||||
assert(this != std::addressof(o));
|
||||
if (this != std::addressof(o))
|
||||
{
|
||||
clear();
|
||||
reserve(o._size);
|
||||
for (auto& item : o)
|
||||
{
|
||||
emplace_back(item);
|
||||
}
|
||||
assert(_size == o._size);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr vector& operator=(vector&& o)
|
||||
{
|
||||
assert(this != std::addressof(o));
|
||||
if (this != std::addressof(o))
|
||||
{
|
||||
destroy();
|
||||
move(o);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~vector() { destroy(); }
|
||||
|
||||
/**
|
||||
* @brief 追加拷贝元素。
|
||||
* @param value 元素值。
|
||||
*/
|
||||
constexpr void push_back(const T& value)
|
||||
{
|
||||
emplace_back(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 追加右值元素。
|
||||
* @param value 元素值。
|
||||
*/
|
||||
constexpr void push_back(const T&& value)
|
||||
{
|
||||
emplace_back(std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 原位构造并追加元素。
|
||||
* @tparam params 构造参数类型。
|
||||
* @param p 构造参数。
|
||||
* @return 新元素引用。
|
||||
*/
|
||||
template<typename... params>
|
||||
constexpr decltype(auto) emplace_back(params&&... p)
|
||||
{
|
||||
if (_size == _capacity)
|
||||
{
|
||||
reserve(((_capacity + 1) * 3) >> 1); // reserve 50% more
|
||||
}
|
||||
assert(_size < _capacity);
|
||||
|
||||
T *const item{ new (std::addressof(_data[_size])) T(std::forward<params>(p)...) };
|
||||
++_size;
|
||||
return *item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整元素数量,新增元素默认构造。
|
||||
* @param new_size 新大小。
|
||||
*/
|
||||
constexpr void resize(u64 new_size)
|
||||
{
|
||||
static_assert(std::is_default_constructible<T>::value,
|
||||
"Type must be default-constructible.");
|
||||
|
||||
if (new_size > _size)
|
||||
{
|
||||
reserve(new_size);
|
||||
while (_size < new_size)
|
||||
{
|
||||
emplace_back();
|
||||
}
|
||||
}
|
||||
else if (new_size < _size)
|
||||
{
|
||||
if constexpr (destruct)
|
||||
{
|
||||
destruct_range(new_size, _size);
|
||||
}
|
||||
|
||||
_size = new_size;
|
||||
}
|
||||
|
||||
|
||||
assert(new_size == _size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 调整元素数量,新增元素使用给定值填充。
|
||||
* @param new_size 新大小。
|
||||
* @param value 填充值。
|
||||
*/
|
||||
constexpr void resize(u64 new_size, const T& value)
|
||||
{
|
||||
static_assert(std::is_copy_constructible<T>::value,
|
||||
"Type must be copy-constructible.");
|
||||
|
||||
if (new_size > _size)
|
||||
{
|
||||
reserve(new_size);
|
||||
while (_size < new_size)
|
||||
{
|
||||
emplace_back(value);
|
||||
}
|
||||
}
|
||||
else if (new_size < _size)
|
||||
{
|
||||
if constexpr (destruct)
|
||||
{
|
||||
destruct_range(new_size, _size);
|
||||
}
|
||||
|
||||
_size = new_size;
|
||||
}
|
||||
|
||||
|
||||
assert(new_size == _size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 预留最小容量。
|
||||
* @param new_capacity 目标容量。
|
||||
*/
|
||||
constexpr void reserve(u64 new_capacity)
|
||||
{
|
||||
if (new_capacity > _capacity)
|
||||
{
|
||||
// NOTE: realoc() will automatically copy the data in the buffer
|
||||
// if a new region of memory iss allocated.
|
||||
void* new_buffer{ realloc(_data, new_capacity * sizeof(T)) };
|
||||
assert(new_buffer);
|
||||
if (new_buffer)
|
||||
{
|
||||
_data = static_cast<T*>(new_buffer);
|
||||
_capacity = new_capacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除指定下标元素并保持顺序。
|
||||
* @param index 删除下标。
|
||||
* @return 指向删除位置的指针。
|
||||
*/
|
||||
constexpr T *const erase(u64 index)
|
||||
{
|
||||
assert(_data && index < _size);
|
||||
return erase(std::addressof(_data[index]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除指定位置元素并保持顺序。
|
||||
* @param item 待删除元素指针。
|
||||
* @return 指向删除位置的指针。
|
||||
*/
|
||||
constexpr T *const erase(T *const item)
|
||||
{
|
||||
assert(_data && item >= std::addressof(_data[0]) &&
|
||||
item < std::addressof(_data[_size]));
|
||||
|
||||
if constexpr (destruct) item->~T();
|
||||
--_size;
|
||||
if (item < std::addressof(_data[_size]))
|
||||
{
|
||||
memcpy(item, item + 1, (std::addressof(_data[_size]) - item) * sizeof(T));
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 无序删除指定下标元素。
|
||||
* @param index 删除下标。
|
||||
* @return 指向删除位置的指针。
|
||||
*/
|
||||
constexpr T *const erase_unordered(u64 index)
|
||||
{
|
||||
assert(_data && index < _size);
|
||||
return erase_unordered(std::addressof(_data[index]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 无序删除指定位置元素。
|
||||
* @param item 待删除元素指针。
|
||||
* @return 指向删除位置的指针。
|
||||
*/
|
||||
constexpr T *const erase_unordered(T *const item)
|
||||
{
|
||||
assert(_data && item >= std::addressof(_data[0]) &&
|
||||
item < std::addressof(_data[_size]));
|
||||
|
||||
if constexpr (destruct) item->~T();
|
||||
--_size;
|
||||
|
||||
if (item < std::addressof(_data[_size]))
|
||||
{
|
||||
memcpy(item, std::addressof(_data[_size]), sizeof(T));
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空容器中的有效元素。
|
||||
*/
|
||||
constexpr void clear()
|
||||
{
|
||||
if constexpr (destruct)
|
||||
{
|
||||
destruct_range(0, _size);
|
||||
}
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 与另一个容器交换内容。
|
||||
* @param o 目标容器。
|
||||
*/
|
||||
constexpr void swap(vector& o)
|
||||
{
|
||||
if (this != std::addressof(o))
|
||||
{
|
||||
auto temp(std::move(o));
|
||||
o.move(*this);
|
||||
move(temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 返回底层数据指针。
|
||||
* @return 数据首地址。
|
||||
*/
|
||||
[[nodiscard]] constexpr T* data()
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 返回只读底层数据指针。
|
||||
* @return 数据首地址。
|
||||
*/
|
||||
[[nodiscard]] constexpr T *const data() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断容器是否为空。
|
||||
* @return 空返回 true。
|
||||
*/
|
||||
[[nodiscard]] constexpr bool empty() const
|
||||
{
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取元素个数。
|
||||
* @return 元素数量。
|
||||
*/
|
||||
[[nodiscard]] constexpr u64 size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取当前容量。
|
||||
* @return 容量值。
|
||||
*/
|
||||
[[nodiscard]] constexpr u64 capacity() const
|
||||
{
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] constexpr T& operator [](u64 index)
|
||||
{
|
||||
assert(_data && index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator [](u64 index) const
|
||||
{
|
||||
assert(_data && index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& front()
|
||||
{
|
||||
assert(_data && _size);
|
||||
return _data[0];
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] constexpr const T& front() const
|
||||
{
|
||||
assert(_data && _size);
|
||||
return _data[0];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& back()
|
||||
{
|
||||
assert(_data && _size);
|
||||
return _data[_size -1];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& back() const
|
||||
{
|
||||
assert(_data && _size);
|
||||
return _data[_size - 1];
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] constexpr T* begin()
|
||||
{
|
||||
return std::addressof(_data[0]);
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] constexpr const T* begin() const
|
||||
{
|
||||
return std::addressof(_data[0]);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* end()
|
||||
{
|
||||
assert(!(_data == nullptr && _size > 0));
|
||||
return std::addressof(_data[_size]);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* end() const
|
||||
{
|
||||
assert(!(_data == nullptr && _size > 0));
|
||||
return std::addressof(_data[_size]);
|
||||
}
|
||||
private:
|
||||
|
||||
constexpr void move(vector& o)
|
||||
{
|
||||
_capacity = o._capacity;
|
||||
_size = o._size;
|
||||
_data = o._data;
|
||||
o.reset();
|
||||
}
|
||||
|
||||
constexpr void reset()
|
||||
{
|
||||
_capacity = 0;
|
||||
_size = 0;
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
constexpr void destruct_range(u64 first, u64 last)
|
||||
{
|
||||
assert(destruct);
|
||||
assert(first <= _size && last <= _size && first <= last);
|
||||
if (_data)
|
||||
{
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
_data[first].~T();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void destroy()
|
||||
{
|
||||
assert([&] {return _capacity ? _data != nullptr : _data == nullptr; }());
|
||||
clear();
|
||||
_capacity = 0;
|
||||
if (_data) free(_data);
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
u64 _capacity{ 0 };
|
||||
u64 _size{ 0 };
|
||||
T* _data{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user