feat: initial DX12 foundation framework

This commit is contained in:
SpecialX
2026-03-19 18:27:49 +08:00
commit 60f73b525d
70 changed files with 8993 additions and 0 deletions

View 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
View 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
}

View 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;

View 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 {
}

View 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];
}
}

View 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);
}
}

View 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
* 返回值为 LPSAFEARRAYSAFEARRAY*),元素类型为 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

View 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);
}

View 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)];
}
}

View 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);
}

View 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)

View 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)

View 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]);
}
}
}
}

View 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
View 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
View 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
View 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>

View 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
View 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 };
};
}

View 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
View 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 };
};
}

View 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;
};
}

View 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;
};
}

View 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

View 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);
}

View 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

View 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
View 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
View 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
View 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
View 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
View 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;
}
}

View 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)
}

View 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
View 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 };
};
}