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,102 @@
<?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="ReleaseEditor|x64">
<Configuration>ReleaseEditor</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="FbxImporter.h" />
<ClInclude Include="Geometry.h" />
<ClInclude Include="MeshPrimitives.h" />
<ClInclude Include="ToolsCommon.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="FbxImporter.cpp" />
<ClCompile Include="Geometry.cpp" />
<ClCompile Include="MeshPrimitives.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{d98da990-9d7c-4064-ab98-6f6c405c10f0}</ProjectGuid>
<RootNamespace>ContentTools</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</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)'=='DebugEditor|x64'">
<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)'=='ReleaseEditor|x64'">
<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)'=='DebugEditor|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;CONTENTTOOLS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)Engine;$(SolutionDir)Engine\Common;C:\Program Files\Autodesk\FBX\FBX SDK\2020.3.2\include</AdditionalIncludeDirectories>
<AdditionalOptions>/Ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;CONTENTTOOLS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>
</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)Engine;$(SolutionDir)Engine\Common;</AdditionalIncludeDirectories>
<AdditionalOptions>/Ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ToolsCommon.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="MeshPrimitives.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="Geometry.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="FbxImporter.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="MeshPrimitives.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="Geometry.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="FbxImporter.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,352 @@
#include "FbxImporter.h"
#include "Geometry.h"
#if _DEBUG
#pragma comment (lib, "C:\\Program Files\\Autodesk\\FBX\\FBX SDK\\2020.3.2\\lib\\vs2019\\x64\\debug\\libfbxsdk-md.lib")
#pragma comment (lib, "C:\\Program Files\\Autodesk\\FBX\\FBX SDK\\2020.3.2\\lib\\vs2019\\x64\\debug\\libxml2-md.lib")
#pragma comment (lib, "C:\\Program Files\\Autodesk\\FBX\\FBX SDK\\2020.3.2\\lib\\vs2019\\x64\\debug\\zlib-md.lib")
#else
#pragma comment (lib, "C:\\Program Files\\Autodesk\\FBX\\FBX SDK\\2020.3.2\\lib\\vs2019\\x64\\release\\libfbxsdk-md.lib")
#pragma comment (lib, "C:\\Program Files\\Autodesk\\FBX\\FBX SDK\\2020.3.2\\lib\\vs2019\\x64\\release\\libxml2-md.lib")
#pragma comment (lib, "C:\\Program Files\\Autodesk\\FBX\\FBX SDK\\2020.3.2\\lib\\vs2019\\x64\\release\\zlib-md.lib")
#endif
namespace XEngine::tools {
namespace {
std::mutex fbx_mutex{};
} // namespace
bool
fbx_context::initialize_fbx()
{
assert(!is_valid());
_fbx_manager = FbxManager::Create();
if (!_fbx_manager)
{
return false;
}
FbxIOSettings* ios{ FbxIOSettings::Create(_fbx_manager, IOSROOT) };
assert(ios);
_fbx_manager->SetIOSettings(ios);
return true;
}
void
fbx_context::load_fbx_file(const char* file)
{
assert(_fbx_manager && !_fbx_scene);
_fbx_scene = FbxScene::Create(_fbx_manager, "Importer Scene");
if (!_fbx_scene)
{
return;
}
FbxImporter* importer{ FbxImporter::Create(_fbx_manager, "Importer") };
if (!(importer &&
importer->Initialize(file, -1, _fbx_manager->GetIOSettings()) &&
importer->Import(_fbx_scene)))
{
return;
}
importer->Destroy();
_scene_scale = (f32)_fbx_scene->GetGlobalSettings().GetSystemUnit().GetConversionFactorTo(FbxSystemUnit::m);
}
void
fbx_context::get_scene(FbxNode* root /* = nullptr*/)
{
assert(is_valid());
if (!root)
{
root = _fbx_scene->GetRootNode();
if (!root)return;
}
const s32 num_nodes{ root->GetChildCount() };
for (s32 i{ 0 }; i < num_nodes; ++i)
{
FbxNode* node{ root->GetChild(i) };
if (!node) continue;
lod_group lod{};
get_meshes(node, lod.meshes, 0, -1);
if (lod.meshes.size())
{
lod.name = lod.meshes[0].name;
_scene->lod_groups.emplace_back(lod);
}
}
}
void
fbx_context::get_meshes(FbxNode* node, utl::vector<mesh>& meshes, u32 lod_id, f32 lod_threshold)
{
assert(node && lod_id != u32_invalid_id);
bool is_lod_group{ false };
if (const s32 num_attributes{ node->GetNodeAttributeCount() })
{
for (s32 i{ 0 }; i < num_attributes; ++i)
{
FbxNodeAttribute* attribute{ node->GetNodeAttributeByIndex(i) };
const FbxNodeAttribute::EType attribute_type{ attribute->GetAttributeType() };
if (attribute_type == FbxNodeAttribute::eMesh)
{
get_mesh(attribute, meshes, lod_id, lod_threshold);
}
else
{
get_lod_group(attribute);
is_lod_group = true;
}
}
}
if (!is_lod_group)
{
if (const s32 num_children{ node->GetChildCount() })
{
for (s32 i{ 0 }; i < num_children; ++i)
{
get_meshes(node->GetChild(i), meshes, lod_id, lod_threshold);
}
}
}
}
void
fbx_context::get_mesh(FbxNodeAttribute* attribute, utl::vector<mesh>& meshes, u32 lod_id, f32 lod_threshold)
{
assert(attribute);
FbxMesh * fbx_mesh{ (FbxMesh*)attribute };
if (fbx_mesh->RemoveBadPolygons() < 0) return;
FbxGeometryConverter gc{ _fbx_manager };
fbx_mesh = (FbxMesh*)(gc.Triangulate(fbx_mesh, true));
if (!fbx_mesh || fbx_mesh->RemoveBadPolygons() < 0)return;
FbxNode *const node{ fbx_mesh->GetNode() };
mesh m{};
m.lod_id = lod_id;
m.lod_threshold = lod_threshold;
m.name = (node->GetName()[0] != '\0') ? node->GetName() : fbx_mesh->GetName();
if (get_mesh_data(fbx_mesh, m))
{
meshes.emplace_back(m);
}
}
bool
fbx_context::get_mesh_data(FbxMesh* fbx_mesh, mesh& m)
{
assert(fbx_mesh);
FbxNode *const node{ fbx_mesh->GetNode() };
FbxAMatrix geometricTransform;
geometricTransform.SetT(node->GetGeometricTranslation(FbxNode::eSourcePivot));
geometricTransform.SetR(node->GetGeometricRotation(FbxNode::eSourcePivot));
geometricTransform.SetS(node->GetGeometricScaling(FbxNode::eSourcePivot));
FbxAMatrix transform{ node->EvaluateGlobalTransform() * geometricTransform };
FbxAMatrix inverse_transpose{ transform.Inverse().Transpose() };
const s32 num_polys{ fbx_mesh->GetPolygonCount() };
if (num_polys <= 0) return false;
const s32 num_vertices{ fbx_mesh->GetControlPointsCount() };
FbxVector4* vertices{ fbx_mesh->GetControlPoints() };
const s32 num_indices{ fbx_mesh->GetPolygonVertexCount() };
s32* indices{ fbx_mesh->GetPolygonVertices() };
assert(num_vertices > 0 && vertices && num_indices > 0 && indices);
if (!(num_vertices > 0 && vertices && num_indices > 0 && indices)) return false;
m.raw_indices.resize(num_indices);
utl::vector vertex_ref(num_vertices, u32_invalid_id);
for (s32 i{ 0 }; i < num_indices; ++i)
{
const u32 v_idx{ (u32)indices[i] };
if (vertex_ref[v_idx] != u32_invalid_id)
{
m.raw_indices[i] = vertex_ref[v_idx];
}
else
{
FbxVector4 v = transform.MultT( vertices[v_idx]) * _scene_scale;
m.raw_indices[i] = (u32)m.positions.size();
vertex_ref[v_idx] = m.raw_indices[i];
m.positions.emplace_back((f32)v[0], (f32)v[1], (f32)v[2]);
}
}
assert(m.raw_indices.size() % 3 == 0);
FbxLayerElementArrayTemplate<s32>* mtl_indices;
if (fbx_mesh->GetMaterialIndices(&mtl_indices))
{
for (s32 i{ 0 }; i < num_polys; ++i)
{
const s32 mtl_index{ mtl_indices->GetAt(i) };
assert(mtl_index >= 0);
m.material_indices.emplace_back((u32)mtl_index);
if (std::find(m.material_used.begin(), m.material_used.end(), (u32)mtl_index) == m.material_used.end())
{
m.material_used.emplace_back((u32)mtl_index);
}
}
}
// Importing normals in ON by default
const bool import_normals{ !_scene_data->settings.calculate_normals };
// Importing normals in ON by default
const bool import_tangents{ !_scene_data->settings.calculate_tangents };
if (import_normals)
{
FbxArray<FbxVector4> normals;
if (fbx_mesh->GenerateNormals() &&
fbx_mesh->GetPolygonVertexNormals(normals) && normals.Size() > 0)
{
const s32 num_normals{ normals.Size() };
for (s32 i{ 0 }; i < num_normals; ++i)
{
FbxVector4 n{ inverse_transpose.MultT(normals[i]) };
n.Normalize();
m.normals.emplace_back((f32)n[0], (f32)n[1], (f32)n[2]);
}
}
else
{
_scene_data->settings.calculate_normals = true;
}
}
if (import_tangents)
{
FbxLayerElementArrayTemplate<FbxVector4>* tangents{ nullptr };
if (fbx_mesh->GenerateTangentsData() &&
fbx_mesh->GetTangents(&tangents) &&
tangents && tangents->GetCount() > 0)
{
const s32 num_tangent{ tangents->GetCount() };
for (s32 i{ 0 }; i < num_tangent; ++i)
{
FbxVector4 t{ tangents->GetAt(i) };
const f32 handedness{ (f32)t[3] };
t[3] = 0.0;
t = transform.MultT(t);
t.Normalize();
m.tangents.emplace_back((f32)t[0], (f32)t[1], (f32)t[2], handedness);
}
}
else
{
_scene_data->settings.calculate_tangents = true;
}
}
FbxStringList uv_names;
fbx_mesh->GetUVSetNames(uv_names);
const s32 uv_set_count{ uv_names.GetCount() };
m.uv_sets.resize(uv_set_count);
for (s32 i{ 0 }; i < uv_set_count; ++i)
{
FbxArray<FbxVector2> uvs;
if (fbx_mesh->GetPolygonVertexUVs(uv_names.GetStringAt(i), uvs))
{
const s32 num_uvs{ uvs.Size() };
for (s32 j{ 0 }; j < num_uvs; ++j)
{
m.uv_sets[i].emplace_back((f32)uvs[j][0], (f32)uvs[j][1]);
}
}
}
return true;
}
void
fbx_context::get_lod_group(FbxNodeAttribute* attribute)
{
assert(attribute);
FbxLODGroup * lod_grp{ (FbxLODGroup*)attribute };
FbxNode *const node{ lod_grp->GetNode() };
lod_group lod{};
lod.name = (node->GetName()[0] != '\0') ? node->GetName() : lod_grp->GetName();
const s32 num_nodes{ node->GetChildCount() };
assert(num_nodes > 0 && lod_grp->GetNumThresholds() == (num_nodes - 1));
for (s32 i{ 0 }; i < num_nodes; ++i)
{
f32 lod_threshold{ -1.f };
if (i>0)
{
FbxDistance threshold;
lod_grp->GetThreshold(i-1, threshold);
lod_threshold = threshold.value() * _scene_scale;
}
get_meshes(node->GetChild(i), lod.meshes, (u32)lod.meshes.size(), lod_threshold);
}
if (lod.meshes.size()) _scene->lod_groups.emplace_back(lod);
}
EDITOR_INTERFACE void
ImportFbx(const char* file, scene_data* data)
{
assert(file && data);
scene scene{};
// 注意: 任何调用此函数都需要使用单线程
{
std::lock_guard lock{ fbx_mutex };
fbx_context fbx_context{ file, &scene, data };
if (fbx_context.is_valid())
{
fbx_context.get_scene();
}
else
{
return;
}
}
process_scene(scene, data->settings);
pack_data(scene, *data);
}
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include "ToolsCommon.h"
#include <fbxsdk.h>
namespace XEngine::tools {
struct scene_data;
struct scene;
struct mesh;
struct geometry_import_settings;
class fbx_context {
public:
fbx_context(const char* file, scene* scene, scene_data* data)
:_scene_data{ data }, _scene{ scene }
{
assert(file && _scene && _scene_data);
if (initialize_fbx())
{
load_fbx_file(file);
assert(is_valid());
}
}
~fbx_context()
{
_fbx_scene->Destroy();
_fbx_manager->Destroy();
ZeroMemory(this, sizeof(fbx_context));
}
void get_scene(FbxNode* root = nullptr);
constexpr bool is_valid() const { return _fbx_manager && _fbx_scene; }
constexpr f32 scene_scale() const { return _scene_scale; }
private:
bool initialize_fbx();
void load_fbx_file(const char* file);
void get_meshes(FbxNode* node, utl::vector<mesh>& meshes, u32 lod_id, f32 lod_threshold);
void get_mesh(FbxNodeAttribute* attribute, utl::vector<mesh>& meshes, u32 lod_id, f32 lod_threshold);
void get_lod_group(FbxNodeAttribute* attribute);
bool get_mesh_data(FbxMesh* fbx_mesh, mesh& m);
scene* _scene{ nullptr };
scene_data* _scene_data{ nullptr };
FbxManager* _fbx_manager{ nullptr };
FbxScene* _fbx_scene{ nullptr };
f32 _scene_scale{ 1.0f };
};
}

View File

@@ -0,0 +1,677 @@
#include "Geometry.h"
#include "..\Engine\Utilities\IOStream.h"
namespace XEngine::tools {
namespace {
using namespace math;
using namespace DirectX;
void
recalculate_normals(mesh& m)
{
const u32 num_indeices{ (u32)m.raw_indices.size() };
m.normals.resize(num_indeices);
for (u32 i{ 0 }; i < num_indeices; ++i)
{
const u32 i0{ m.raw_indices[i] };
const u32 i1{ m.raw_indices[++i] };
const u32 i2{ m.raw_indices[++i] };
vector v0{ XMLoadFloat3(&m.positions[i0]) };
vector v1{ XMLoadFloat3(&m.positions[i1]) };
vector v2{ XMLoadFloat3(&m.positions[i2]) };
vector e0{ v1 - v0 };
vector e1{ v2 - v0 };
vector n{ XMVector3Normalize(XMVector3Cross(e0,e1)) };
XMStoreFloat3(&m.normals[i], n);
m.normals[i - 1] = m.normals[i];
m.normals[i - 2] = m.normals[i];
}
}
/**
* @brief 处理网格的法线,实现平滑或硬边效果。
*
* 该函数根据给定的平滑角度smoothing_angle处理网格mesh的法线。
* 如果平滑角度接近 180 度,则生成硬边法线;如果接近 0 度,则生成软边法线;否则,根据角度阈值进行平滑。
*
* @param m 要处理的网格对象,包含原始索引、顶点位置和法线。
* @param smoothing_angle 法线平滑角度,单位为度。
*/
void
process_normals(mesh& m, f32 smoothing_angle)
{
// 计算角度的余弦值,作为平滑阈值
const f32 cos_alpha{ XMScalarCos(pi - smoothing_angle * pi / 180.f) };
// 判断是否为硬边或软边
const bool is_hard_edge{ XMScalarNearEqual(smoothing_angle, 180.f, epsilon) };
const bool is_soft_edge{ XMScalarNearEqual(smoothing_angle, 0.f, epsilon) };
// 获取索引和顶点数量
const u32 num_idices{ (u32)m.raw_indices.size() };
const u32 num_vertices{ (u32)m.positions.size() };
// 确保索引和顶点数量有效
assert(num_idices && num_vertices);
// 调整索引缓冲区的大小,用于存储新的顶点索引
m.indices.resize(num_idices);
// 创建一个索引引用表,用于存储每个顶点索引对应的原始索引列表
// 这里是每一个vertice对应4个point参考houdini
utl::vector<utl::vector<u32>> idx_ref(num_vertices);
for (u32 i{ 0 }; i < num_idices; ++i)
idx_ref[m.raw_indices[i]].emplace_back(i);
// 遍历每个vertice
for (u32 i{ 0 }; i < num_vertices; ++i)
{
// 获取当前顶点的原始索引列表
auto& refs{ idx_ref[i] };
u32 num_refs{ (u32)refs.size() };
// 遍历当前顶点的每个原始索引
for (u32 j{ 0 }; j < num_refs; ++j)
{
m.indices[refs[j]] = (u32)m.vertices.size();
vertex& v{ m.vertices.emplace_back() };
v.position = m.positions[m.raw_indices[refs[j]]];
vector n1{ XMLoadFloat3(&m.normals[refs[j]]) };
// 如果不是硬边,则尝试平滑法线
if (!is_hard_edge)
{
// 遍历剩余的原始索引
for (u32 k{ j + 1 }; k < num_refs; ++k)
{
f32 cos_theta{ 0.f };
vector n2{ XMLoadFloat3(&m.normals[refs[k]]) };
// 如果不是软边,则计算法线之间的点积
// 这里的n2是已经normalize过的所有他的长度为1这里就没有进行计算长度
// 这里的读取道德n1虽然也已经是normalize过的但是他可能会在后续改变
// 以需要计算长度
// cos(angle) = dot(n1,n2) / (||n1||*||n2||)
if (!is_soft_edge)
{
XMStoreFloat(&cos_theta, XMVector3Dot(n1, n2) * XMVector3ReciprocalLength(n1));
}
if (is_soft_edge || cos_theta >= cos_alpha)
{
n1 += n2;
m.indices[refs[k]] = m.indices[refs[j]];
refs.erase(refs.begin() + k);
--num_refs;
--k;
}
}
}
// 归一化合并后的法线,并存储到新的顶点中
XMStoreFloat3(&v.normal, XMVector3Normalize(n1));
}
}
}
void
process_uvs(mesh& m)
{
utl::vector<vertex> old_vertices;
old_vertices.swap(m.vertices);
utl::vector<u32> old_indices(m.indices.size());
old_indices.swap(m.indices);
const u32 num_vertices{ (u32)old_vertices.size() };
const u32 num_indices{ (u32)old_indices.size() };
assert(num_vertices && num_indices);
utl::vector<utl::vector<u32>> idx_ref(num_vertices);
for (u32 i{ 0 }; i < num_indices; ++i)
idx_ref[old_indices[i]].emplace_back(i);
for (u32 i{ 0 }; i < num_vertices; ++i)
{
auto& refs{ idx_ref[i] };
u32 num_refs{ (u32)refs.size() };
for (u32 j{ 0 }; j < num_refs; ++j)
{
m.indices[refs[j]] = (u32)m.vertices.size();
vertex& v{ old_vertices[old_indices[refs[j]]] };
v.uv = m.uv_sets[0][refs[j]];
m.vertices.emplace_back(v);
for (u32 k{ j + 1 }; k < num_refs; ++k)
{
v2& uv1{ m.uv_sets[0][refs[k]] };
if (XMScalarNearEqual(v.uv.x, uv1.x, epsilon) &&
XMScalarNearEqual(v.uv.y, uv1.y, epsilon))
{
m.indices[refs[k]] = m.indices[refs[j]];
refs.erase(refs.begin() + k);
--num_refs;
--k;
}
}
}
}
}
u64
get_vertex_element_size(elements::element_type::type elements_type)
{
using namespace elements;
switch (elements_type)
{
case element_type::static_normal: return sizeof(static_normal);
case element_type::static_normal_texture: return sizeof(static_normal_texture);
case element_type::static_color: return sizeof(static_color);
case element_type::skeletal: return sizeof(skeletal);
case element_type::skeletal_color: return sizeof(skeletal_color);
case element_type::skeletal_normal: return sizeof(skeletal_normal);
case element_type::skeletal_normal_color: return sizeof(skeletal_normal_color);
case element_type::skeletal_normal_texture: return sizeof(skeletal_normal_texture);
case element_type::skeletal_normal_texture_color: return sizeof(skeletal_normal_texture_color);
}
return 0;
}
void
pack_vertices(mesh& m)
{
const u32 num_vertices{ (u32)m.vertices.size() };
assert(num_vertices);
m.position_buffer.resize(sizeof(math::v3) * num_vertices);
math::v3 *const position_buffer{ (math::v3 *const)m.position_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
position_buffer[i] = m.vertices[i].position;
}
struct u16v2 { u16 x, y; };
struct u8v3 { u8 x, y, z; };
utl::vector<u8> t_signs(num_vertices);
utl::vector<u16v2> normals(num_vertices);
utl::vector<u16v2> tangents(num_vertices);
utl::vector<u8v3> joint_weights(num_vertices);
if (m.elements_type & elements::element_type::static_normal)
{
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
t_signs[i] = { (u8)((v.normal.z > 0.f) << 1) };
normals[i] = {
(u16)pack_float<16>(v.normal.x, -1.f, 1.f),
(u16)pack_float<16>(v.normal.y, -1.f, 1.f)
};
}
if (m.elements_type & elements::element_type::static_normal_texture) {
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
t_signs[i] |= (u8)((v.tangent.w > 0.f) && (v.normal.z > 0.f));
tangents[i] = {
(u16)pack_float<16>(v.tangent.x, -1.f, 1.f),
(u16)pack_float<16>(v.tangent.y, -1.f, 1.f)
};
}
}
}
if (m.elements_type & elements::element_type::skeletal) {
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
//pack [0,1] to [0,255]
joint_weights[i] = {
(u8)pack_unit_float<8>(v.joint_weights.x),
(u8)pack_unit_float<8>(v.joint_weights.y),
(u8)pack_unit_float<8>(v.joint_weights.z)
};
}
}
m.element_buffer.resize(get_vertex_element_size(m.elements_type) * num_vertices);
using namespace elements;
switch (m.elements_type)
{
case element_type::static_color:
{
static_color *const element_buffer{ (static_color *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
element_buffer[i] = { {v.red, v.gree, v.blue}, {} };
}
}
break;
case element_type::static_normal:
{
static_normal *const element_buffer{ (static_normal *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
element_buffer[i] = { {v.red, v.gree, v.blue}, t_signs[i], {normals[i].x, normals[i].y} };
}
}
break;
case element_type::static_normal_texture:
{
static_normal_texture *const element_buffer{ (static_normal_texture *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
element_buffer[i] = { {v.red, v.gree, v.blue}, t_signs[i],
{normals[i].x, normals[i].y}, {tangents[i].x, tangents[i].y},
v.uv };
}
}
break;
case element_type::skeletal:
{
skeletal *const element_buffer{ (skeletal *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
const u16 indices[4]{ (u16)v.joint_indices.x, (u16)v.joint_indices.y, (u16)v.joint_indices.z,(u16)v.joint_indices.w };
element_buffer[i] = { {joint_weights[i].x,joint_weights[i].y,joint_weights[i].z},{},
{indices[0],indices[1],indices[2],indices[3]}
};
}
}
break;
case element_type::skeletal_color:
{
skeletal_color *const element_buffer{ (skeletal_color *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
const u16 indices[4]{ (u16)v.joint_indices.x, (u16)v.joint_indices.y, (u16)v.joint_indices.z,(u16)v.joint_indices.w };
element_buffer[i] = { {joint_weights[i].x,joint_weights[i].y,joint_weights[i].z},{},
{indices[0],indices[1],indices[2],indices[3]},
{v.red, v.gree, v.blue},{}
};
}
}
break;
case element_type::skeletal_normal:
{
skeletal_normal *const element_buffer{ (skeletal_normal *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
const u16 indices[4]{ (u16)v.joint_indices.x, (u16)v.joint_indices.y, (u16)v.joint_indices.z,(u16)v.joint_indices.w };
element_buffer[i] = { {joint_weights[i].x,joint_weights[i].y,joint_weights[i].z},t_signs[i],
{indices[0],indices[1],indices[2],indices[3]},
{normals[i].x, normals[i].y}
};
}
}
break;
case element_type::skeletal_normal_color:
{
skeletal_normal_color *const element_buffer{ (skeletal_normal_color *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
const u16 indices[4]{ (u16)v.joint_indices.x, (u16)v.joint_indices.y, (u16)v.joint_indices.z,(u16)v.joint_indices.w };
element_buffer[i] = { {joint_weights[i].x,joint_weights[i].y,joint_weights[i].z},t_signs[i],
{indices[0],indices[1],indices[2],indices[3]},
{normals[i].x, normals[i].y},{v.red, v.gree, v.blue},{}
};
}
}
break;
case element_type::skeletal_normal_texture:
{
skeletal_normal_texture *const element_buffer{ (skeletal_normal_texture *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
const u16 indices[4]{ (u16)v.joint_indices.x, (u16)v.joint_indices.y, (u16)v.joint_indices.z,(u16)v.joint_indices.w };
element_buffer[i] = { {joint_weights[i].x,joint_weights[i].y,joint_weights[i].z},t_signs[i],
{indices[0],indices[1],indices[2],indices[3]},
{normals[i].x, normals[i].y},{tangents[i].x, tangents[i].y},v.uv
};
}
}
break;
case element_type::skeletal_normal_texture_color:
{
skeletal_normal_texture_color *const element_buffer{ (skeletal_normal_texture_color *const)m.element_buffer.data() };
for (u32 i{ 0 }; i < num_vertices; ++i)
{
vertex& v{ m.vertices[i] };
const u16 indices[4]{ (u16)v.joint_indices.x, (u16)v.joint_indices.y, (u16)v.joint_indices.z,(u16)v.joint_indices.w };
element_buffer[i] = { {joint_weights[i].x,joint_weights[i].y,joint_weights[i].z},t_signs[i],
{indices[0],indices[1],indices[2],indices[3]},
{normals[i].x, normals[i].y},{tangents[i].x, tangents[i].y},v.uv,
{v.red, v.gree, v.blue},{}
};
}
}
break;
}
}
void
determin_elements_type(mesh&m)
{
using namespace elements;
if (m.normals.size())
{
if (m.uv_sets.size() && m.uv_sets[0].size())
{
m.elements_type = element_type::static_normal_texture;
}
else
{
m.elements_type = element_type::static_normal;
}
}
else if (m.colors.size())
{
m.elements_type = element_type::static_color;
}
}
void
process_vertices(mesh & m, const geometry_import_settings & settings)
{
assert((m.raw_indices.size() % 3) == 0);
if (settings.calculate_normals || m.normals.empty())
{
recalculate_normals(m);
}
process_normals(m, settings.smoothing_angle);
if (!m.uv_sets.empty())
{
process_uvs(m);
}
determin_elements_type(m);
pack_vertices(m);
}
u64
get_mesh_size(const mesh & m)
{
const u64 num_vertices{ m.vertices.size() };
const u64 position_buffer_size{ m.position_buffer.size() };
assert(position_buffer_size == sizeof(math::v3) * num_vertices);
const u64 element_buffer_size{ m.element_buffer.size() };
assert(element_buffer_size == get_vertex_element_size(m.elements_type) * num_vertices);
const u64 index_size{ (num_vertices < (1 << 16)) ? sizeof(u16) : sizeof(u32) };
const u64 index_buffer_size{ index_size * m.indices.size() };
constexpr u64 su32{ sizeof(u32) };
const u64 size{
su32 + m.name.size() + // mesh name length ad room for mesh name string
su32 + // lod id
su32 + // vertex size
su32 + // element type
su32 + // number of vertices
su32 + // index size( 16 or 32 )
su32 + // number of indices
sizeof(f32) + // LOD threshold
position_buffer_size + //room for vertices positions
element_buffer_size + //room for vertex elements
index_buffer_size // room for indices
};
return size;
}
u64
get_scene_size(const scene & scene)
{
constexpr u64 su32(sizeof(u32));
u64 size
{
su32 + // name
scene.name.size() + // room for scene name string
su32 // number of LODS
};
for (auto& lod : scene.lod_groups)
{
u64 lod_size
{
su32 + lod.name.size() + // LOD name length and room for LPD name string
su32 // number of meshes in this LOD
};
for (auto& m : lod.meshes)
{
lod_size += get_mesh_size(m);
}
size += lod_size;
}
return size;
}
void
pack_mesh_data(const mesh & m, utl::blob_stream_writer& blob)
{
// mesh name
blob.write((u32)m.name.size());
blob.write(m.name.c_str(), m.name.size());
// lod id
blob.write(m.lod_id);
// vertex element size
const u32 elements_size{ (u32)get_vertex_element_size(m.elements_type) };
blob.write(elements_size);
// elements type enumeration
blob.write((u32)m.elements_type);
// number of vertices
const u32 num_vertices{ (u32)m.vertices.size() };
blob.write(num_vertices);
// index size (16 or 32)
const u32 index_size{ (num_vertices < (1 << 16)) ? sizeof(u16) : sizeof(u32) };
blob.write(index_size);
// number of indices
const u32 num_indices{ (u32)m.indices.size() };
blob.write(num_indices);
// LOD threshold
blob.write(m.lod_threshold);
// position buffer
assert(m.position_buffer.size() == sizeof(math::v3) * num_vertices);
blob.write(m.position_buffer.data(), m.position_buffer.size());
// element buffer
assert(m.element_buffer.size() == elements_size * num_vertices);
blob.write(m.element_buffer.data(), m.element_buffer.size());
// index data
const u32 index_buffer_size{ index_size * num_indices };
const u8* data{ (const u8*)m.indices.data() };
utl::vector<u16> indices;
if (index_size == sizeof(u16))
{
indices.resize(num_indices);
for (u32 i{ 0 }; i < num_indices; ++i) indices[i] = (u16)m.indices[i];
data = (const u8*)indices.data();
}
blob.write(data, index_buffer_size);
}
bool
split_meshes_by_material(u32 material_idx, const mesh & m, mesh & submesh)
{
submesh.name = m.name;
submesh.lod_threshold = m.lod_threshold;
submesh.lod_id = m.lod_id;
submesh.material_used.emplace_back(material_idx);
submesh.uv_sets.resize(m.uv_sets.size());
const u32 num_polys{ (u32)m.raw_indices.size() / 3 };
utl::vector<u32> vertex_ref(m.positions.size(), u32_invalid_id);
for (u32 i{ 0 }; i < num_polys; ++i)
{
const u32 mtl_idx{ m.material_indices[i] };
if (mtl_idx != material_idx) continue;
const u32 index{ i * 3 };
for (u32 j = index; j < index + 3; ++j)
{
const u32 v_idx{ m.raw_indices[i] };
if (vertex_ref[v_idx] != u32_invalid_id)
{
submesh.raw_indices.emplace_back(vertex_ref[v_idx]);
}
else
{
submesh.raw_indices.emplace_back((u32)submesh.positions.size());
vertex_ref[v_idx] = submesh.raw_indices.back();
submesh.positions.emplace_back(m.positions[v_idx]);
}
if (m.normals.size())
{
submesh.normals.emplace_back(m.normals[j]);
}
if (m.tangents.size())
{
submesh.tangents.emplace_back(m.tangents[j]);
}
for (u32 k{ 0 }; k < m.uv_sets.size(); ++k)
{
if (m.uv_sets[k].size())
{
submesh.uv_sets[k].emplace_back(m.uv_sets[k][j]);
}
}
}
}
assert((submesh.raw_indices.size() % 3) == 0);
return !submesh.raw_indices.empty();
}
void
split_meshes_by_material(scene & scene)
{
for (auto& lod : scene.lod_groups)
{
utl::vector<mesh> new_meshes;
for (auto& m : lod.meshes)
{
const u32 num_materials{ (u32)m.material_used.size() };
if (num_materials > 1)
{
for (u32 i{ 0 }; i < num_materials; ++i)
{
mesh submesh{};
if (split_meshes_by_material(m.material_used[i], m, submesh))
{
new_meshes.emplace_back(submesh);
}
}
}
else
{
new_meshes.emplace_back(m);
}
}
new_meshes.swap(lod.meshes);
}
}
} // namespace
void
process_scene(scene& scene, const geometry_import_settings& settings)
{
split_meshes_by_material(scene);
for (auto& lod : scene.lod_groups)
for (auto& m : lod.meshes)
{
process_vertices(m, settings);
}
}
void
pack_data(const scene& scene, scene_data& data)
{
const u64 scene_size{ get_scene_size(scene) };
data.buffer_size = (u32)scene_size;
data.buffer = (u8*)CoTaskMemAlloc(scene_size);
assert(data.buffer);
utl::blob_stream_writer blob{ data.buffer, data.buffer_size };
// scene name
blob.write((u32)scene.name.size());
blob.write(scene.name.c_str(), scene.name.size());
// number of LODS
blob.write((u32)scene.lod_groups.size());
for (auto& lod : scene.lod_groups)
{
// LOD name
blob.write((u32)lod.name.size());
blob.write(lod.name.c_str(), lod.name.size());
// Number of meshes in this LOD
blob.write((u32)lod.meshes.size());
for (auto& m : lod.meshes)
{
pack_mesh_data(m, blob);
}
}
assert(scene_size == blob.offset());
}
}

View File

@@ -0,0 +1,175 @@
#pragma once
#include "ToolsCommon.h"
namespace XEngine::tools {
namespace elements {
struct element_type {
enum type :u32 {
position_only = 0x00,
static_normal = 0x01,
static_normal_texture = 0x03,
static_color = 0x04,
skeletal = 0x08,
skeletal_color = skeletal | static_color,
skeletal_normal = skeletal | static_normal,
skeletal_normal_color = skeletal_normal | skeletal_color,
skeletal_normal_texture = skeletal | static_normal_texture,
skeletal_normal_texture_color = skeletal_normal_texture | static_color
};
};
struct static_color
{
u8 color[3];
u8 pad;
};
struct static_normal
{
u8 color[3];
u8 t_sign; //bit 0 : tangent handedness * (tangent.z sign), bit 1 : normal.z sign (0 means -1, 1 means +1).
u16 normal[2];
};
struct static_normal_texture
{
u8 color[3];
u8 t_sign; //bit 0 : tangent handedness * (tangent.z sign), bit 1 : normal.z sign (0 means -1, 1 means +1).
u16 normal[2];
u16 tangent[2];
math::v2 uv;
};
struct skeletal
{
u8 joint_weights[3];
u8 pad;
u16 joint_indices[4];
};
struct skeletal_color
{
u8 joint_weights[3];
u8 pad;
u16 joint_indices[4];
u8 color[3];
u8 pad2;
};
struct skeletal_normal
{
u8 joint_weights[3];
u8 t_sign; //bit 0 : tangent handedness * (tangent.z sign), bit 1 : normal.z sign (0 means -1, 1 means +1).
u16 joint_indices[4];
u16 normal[2];
};
struct skeletal_normal_color
{
u8 joint_weights[3];
u8 t_sign; //bit 0 : tangent handedness * (tangent.z sign), bit 1 : normal.z sign (0 means -1, 1 means +1).
u16 joint_indices[4];
u16 normal[2];
u8 color[3];
u8 pad;
};
struct skeletal_normal_texture
{
u8 joint_weights[3];
u8 t_sign; //bit 0 : tangent handedness * (tangent.z sign), bit 1 : normal.z sign (0 means -1, 1 means +1).
u16 joint_indices[4];
u16 normal[2];
u16 tangent[2];
math::v2 uv;
};
struct skeletal_normal_texture_color
{
u8 joint_weights[3];
u8 t_sign; //bit 0 : tangent handedness * (tangent.z sign), bit 1 : normal.z sign (0 means -1, 1 means +1).
u16 joint_indices[4];
u16 normal[2];
u16 tangent[2];
math::v2 uv;
u8 color[3];
u8 pad;
};
} // namespace elements
struct vertex
{
math::v4 tangent{};
math::v4 joint_weights{};
math::u32v4 joint_indices{ u32_invalid_id,u32_invalid_id ,u32_invalid_id ,u32_invalid_id };
math::v3 position{};
math::v3 normal{};
math::v2 uv{};
u8 red{}, gree{}, blue{};
u8 pad;
};
struct mesh
{
utl::vector<math::v3> positions;
utl::vector<math::v3> normals;
utl::vector<math::v3> colors;
utl::vector<math::v4> tangents;
utl::vector<utl::vector<math::v2>> uv_sets;
utl::vector<u32> material_indices;
utl::vector<u32> material_used;
utl::vector<u32> raw_indices;
// Intermediate data
utl::vector<vertex> vertices;
utl::vector<u32> indices;
// Output data
std::string name;
elements::element_type::type elements_type;
utl::vector<u8> position_buffer;
utl::vector<u8> element_buffer;
f32 lod_threshold{ -1.f };
u32 lod_id{ u32_invalid_id };
};
struct lod_group
{
std::string name;
utl::vector<mesh> meshes;
};
struct scene
{
std::string name;
utl::vector<lod_group> lod_groups;
};
struct geometry_import_settings
{
f32 smoothing_angle;
u8 calculate_normals;
u8 calculate_tangents;
u8 reverse_handedness;
u8 import_embeded_textures;
u8 import_animations;
};
struct scene_data
{
u8* buffer;
u32 buffer_size;
geometry_import_settings settings;
};
void process_scene(scene& scene, const geometry_import_settings& settings);
void pack_data(const scene& scene, scene_data& data);
}

View File

@@ -0,0 +1,341 @@
#include "MeshPrimitives.h"
#include "Geometry.h"
namespace XEngine::tools
{
using namespace math;
using namespace DirectX;
namespace {
using primitive_mesh_creator = void(*)(scene&, const primitive_init_info& info);
void create_plane(scene& scene, const primitive_init_info& info);
void create_cube(scene& scene, const primitive_init_info& info);
void create_uv_sphere(scene& scene, const primitive_init_info& info);
void create_ico_sphere(scene& scene, const primitive_init_info& info);
void create_cylinder(scene& scene, const primitive_init_info& info);
void create_capsule(scene& scene, const primitive_init_info& info);
primitive_mesh_creator creators[]
{
create_plane,
create_cube,
create_uv_sphere,
create_ico_sphere,
create_cylinder,
create_capsule,
};
struct axis
{
enum : u32 {
x = 0,
y = 1,
z = 2,
};
};
/**
* @brief 创建一个平面网格mesh对象。
*
* 该平面网格可以通过参数定制其尺寸、分段、方向、UV 坐标范围以及顶点缠绕顺序。
* 需要注意positionuvs记录的是顶点在后面的indicesuv_set中细节记录点和uv的分配。
*
* @param info 包含平面尺寸和分段信息的结构体。
* @param horizontal_index 水平方向的轴索引 (0: x, 1: y, 2: z),默认为 axis::x。
* @param vertical_index 垂直方向的轴索引 (0: x, 1: y, 2: z),默认为 axis::z。
* @param flip_winding 是否翻转顶点缠绕顺序,默认为 false。
* @param offset 平面的初始位置偏移,默认为 {-0.5f, 0.f, -0.5f}。
* @param u_range U 坐标的范围,默认为 {0.f, 1.f}。
* @param v_range V 坐标的范围,默认为 {0.f, 1.f}。
* @return 创建的平面网格对象。
*/
mesh
create_plane(const primitive_init_info& info,
u32 horizontal_index = axis::x, u32 vertical_index = axis::z, bool flip_winding = false,
v3 offset = { -0.5f,0.f,-0.5f }, v2 u_range = { 0.f,1.f }, v2 v_range = { 0.f, 1.f })
{
// 参数合法性检查
assert(horizontal_index < 3 && vertical_index < 3);
assert(horizontal_index != vertical_index);
// 计算水平和垂直方向的分段数,并限制在合理范围内
const u32 horizontal_count{ clamp(info.segments[horizontal_index], 1u ,10u) };
const u32 vertical_count{ clamp(info.segments[vertical_index], 1u ,10u) };
// 计算水平和垂直方向的步长
const f32 horizontal_step{ 1.f / horizontal_count };
const f32 vertical_step{ 1.f / vertical_count };
// 计算 UV 坐标的步长
const f32 u_step{ (u_range.y - u_range.x) / horizontal_count };
const f32 v_step{ (v_range.y - v_range.x) / vertical_count };
// 创建网格对象和 UV 坐标向量
mesh m{};
m.name = "plane";
utl::vector<v2> uvs;
// 生成顶点和 UV 坐标
for (u32 j{ 0 }; j <= vertical_count; ++j)
for (u32 i{ 0 }; i <= horizontal_count; ++i)
{
// 计算顶点位置
v3 position{ offset };
f32* const as_array{ &position.x };
as_array[horizontal_index] += i * horizontal_step;
as_array[vertical_index] += j * vertical_step;
// 添加顶点位置到网格对象
m.positions.emplace_back(position.x * info.size.x, position.y * info.size.y, position.z * info.size.z);
// 计算 UV 坐标
v2 uv{ u_range.x, 1.f - v_range.x };
uv.x += i * u_step;
uv.y -= j * v_step;
// 添加 UV 坐标到向量
uvs.emplace_back(uv);
}
// 顶点数量检查
assert(m.positions.size() == (((u64)horizontal_count + 1) * ((u64)vertical_count + 1)));
// 计算每行顶点数
const u32 row_length{ horizontal_count + 1 };
// 生成三角形索引
for (u32 j{ 0 }; j < vertical_count; ++j)
{
for (u32 i{ 0 }; i < horizontal_count; ++i)
{
// 计算四边形四个顶点的索引
const u32 index[4]
{
i + j * row_length,
i + (j + 1) * row_length,
(i + 1) + j * row_length,
(i + 1) + (j + 1) * row_length,
};
// 添加三角形索引到网格对象,并根据 flip_winding 参数调整顶点顺序
// 这里索引是对应到position的序号。
m.raw_indices.emplace_back(index[0]);
m.raw_indices.emplace_back(index[flip_winding ? 2 : 1]);
m.raw_indices.emplace_back(index[flip_winding ? 1 : 2]);
m.raw_indices.emplace_back(index[2]);
m.raw_indices.emplace_back(index[flip_winding ? 3 : 1]);
m.raw_indices.emplace_back(index[flip_winding ? 1 : 3]);
}
}
// 索引数量检查
const u32 num_indices{ 3 * 2 * horizontal_count * vertical_count };
assert(m.raw_indices.size() == num_indices);
m.uv_sets.resize(1);
// 根据索引分配 UV 坐标
// 对应的是每个顶点。
for (u32 i{ 0 }; i < num_indices; ++i)
{
m.uv_sets[0].emplace_back(uvs[m.raw_indices[i]]);
}
return m;
}
mesh
create_uv_sphere(const primitive_init_info& info)
{
const u32 phi_count{ clamp(info.segments[axis::x], 3u, 64u) };
const u32 theta_count{ clamp(info.segments[axis::y], 2u, 64u) };
const f32 theta_step{ pi / theta_count };
const f32 phi_step{ two_pi / phi_count };
const u32 num_vertices{ 2 + phi_count * (theta_count - 1) };
const u32 num_indices{ 2 * 3 * phi_count + 2 * 3 * phi_count * (theta_count - 2) };
mesh m{};
m.name = "uv_sphere";
m.positions.resize(num_vertices);
// add the top vertex
u32 c{ 0 };
m.positions[c++] = { 0.f, info.size.y, 0.f };
for (u32 j{ 1 }; j <= (theta_count - 1); ++j)
{
const f32 theta{ j * theta_step };
for (u32 i{ 0 }; i < phi_count; ++i)
{
const f32 phi{ i * phi_step };
m.positions[c++] = {
info.size.x * XMScalarSin(theta) * XMScalarCos(phi),
info.size.y * XMScalarCos(theta),
-info.size.z * XMScalarSin(theta) * XMScalarSin(phi)
};
}
}
// add bottom vertex
m.positions[c++] = { 0.f, -info.size.y,0.f };
assert(c == num_vertices);
c = 0;
m.raw_indices.resize(num_indices);
utl::vector<v2> uvs(num_indices);
const f32 inv_theta_count{ 1.f / theta_count };
const f32 inv_phi_count{ 1.f / phi_count };
// indices for the top cap
for (u32 i{ 0 }; i < phi_count - 1; ++i)
{
uvs[c] = { (2 * i + 1) * 0.5f * inv_phi_count, 1.f };
m.raw_indices[c++] = 0;
uvs[c] = { i * inv_phi_count, 1.f - inv_theta_count };
m.raw_indices[c++] = i + 1;
uvs[c] = { (i + 1) * inv_phi_count, 1.f - inv_theta_count };
m.raw_indices[c++] = i + 2;
}
uvs[c] = { 1.f - 0.5f * inv_phi_count, 1.f };
m.raw_indices[c++] = 0;
uvs[c] = { 1.f - inv_phi_count, 1.f - inv_theta_count };
m.raw_indices[c++] = phi_count;
uvs[c] = { 1.f, 1.f - inv_theta_count };
m.raw_indices[c++] = 1;
// indices for the section
for (u32 j{ 0 }; j < (theta_count - 2); ++j)
{
for (u32 i{ 0 }; i < (phi_count - 1); ++i)
{
const u32 index[4]{
1 + i + j * phi_count,
1 + i + (j + 1) * phi_count,
1 + (i + 1) + (j + 1) * phi_count,
1 + (i + 1) + j * phi_count,
};
uvs[c] = { (i)*inv_phi_count, 1.f - (j + 1) * inv_theta_count };
m.raw_indices[c++] = index[0];
uvs[c] = { (i)*inv_phi_count, 1.f - (j + 2) * inv_theta_count };
m.raw_indices[c++] = index[1];
uvs[c] = { (i + 1) * inv_phi_count, 1.f - (j + 2) * inv_theta_count };
m.raw_indices[c++] = index[2];
uvs[c] = { (i)*inv_phi_count, 1.f - (j + 1) * inv_theta_count };
m.raw_indices[c++] = index[0];
uvs[c] = { (i + 1) * inv_phi_count, 1.f - (j + 2) * inv_theta_count };
m.raw_indices[c++] = index[2];
uvs[c] = { (i + 1) * inv_phi_count, 1.f - (j + 1) * inv_theta_count };
m.raw_indices[c++] = index[3];
}
const u32 index[4]{
phi_count + j * phi_count,
phi_count + (j + 1) * phi_count,
1 + (j + 1) * phi_count,
1 + j * phi_count
};
uvs[c] = { (1.f) - inv_phi_count, 1.f - (j + 1) * inv_theta_count };
m.raw_indices[c++] = index[0];
uvs[c] = { (1.f) - inv_phi_count, 1.f - (j + 2) * inv_theta_count };
m.raw_indices[c++] = index[1];
uvs[c] = { (1.f), 1.f - (j + 2) * inv_theta_count };
m.raw_indices[c++] = index[2];
uvs[c] = { (1.f) - inv_phi_count, 1.f - (j + 1) * inv_theta_count };
m.raw_indices[c++] = index[0];
uvs[c] = { (1.f), 1.f - (j + 2) * inv_theta_count };
m.raw_indices[c++] = index[2];
uvs[c] = { (1.f), 1.f - (j + 1) * inv_theta_count };
m.raw_indices[c++] = index[3];
}
// indices for the bottom
const u32 south_pole_index{ (u32)m.positions.size() - 1 };
for (u32 i{ 0 }; i < (phi_count - 1); ++i)
{
uvs[c] = { (2 * i + 1) * 0.5f * inv_phi_count, 0.f };
m.raw_indices[c++] = south_pole_index;
uvs[c] = { (i + 1) * inv_phi_count, inv_theta_count };
m.raw_indices[c++] = south_pole_index - phi_count + i + 1;
uvs[c] = { (i)*inv_phi_count, inv_theta_count };
m.raw_indices[c++] = south_pole_index - phi_count + i;
}
uvs[c] = { 1.f - 0.5f * inv_phi_count, 0.f };
m.raw_indices[c++] = south_pole_index;
uvs[c] = { 1.f, inv_theta_count };
m.raw_indices[c++] = south_pole_index - phi_count;
uvs[c] = { 1.f - inv_phi_count, inv_theta_count };
m.raw_indices[c++] = south_pole_index - 1;
m.uv_sets.emplace_back(uvs);
return m;
}
void
create_plane(scene& scene, const primitive_init_info& info)
{
lod_group lod{};
lod.name = "plane";
lod.meshes.emplace_back(create_plane(info));
scene.lod_groups.emplace_back(lod);
}
void
create_cube(scene& scene, const primitive_init_info& info)
{
}
void
create_uv_sphere(scene& scene, const primitive_init_info& info)
{
lod_group lod{};
lod.name = "uv_sphere";
lod.meshes.emplace_back(create_uv_sphere(info));
scene.lod_groups.emplace_back(lod);
}
void
create_ico_sphere(scene& scene, const primitive_init_info& info)
{
}
void
create_cylinder(scene& scene, const primitive_init_info& info)
{
}
void
create_capsule(scene& scene, const primitive_init_info& info)
{
}
static_assert(_countof(creators) == primitive_mesh_type::count);
} // namespace
EDITOR_INTERFACE void
CreatePrimitiveMesh(scene_data* data, primitive_init_info* info)
{
assert(data && info);
assert(info->type < primitive_mesh_type::count);
scene scene{};
creators[info->type](scene, *info);
data->settings.calculate_normals = 1;
process_scene(scene, data->settings);
pack_data(scene, *data);
}
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "ToolsCommon.h"
namespace XEngine::tools {
enum primitive_mesh_type : u32
{
plane,
cube,
uv_sphere,
ico_sphere,
cylinder,
capsule,
count
};
struct primitive_init_info
{
primitive_mesh_type type;
u32 segments[3]{ 1,1,1 };
math::v3 size{ 1,1,1 };
u32 lod{ 0 };
};
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "CommonHeader.h"
#include <combaseapi.h>
#ifndef EDITOR_INTERFACE
#define EDITOR_INTERFACE extern "C" __declspec(dllexport)
#endif