feat: initial DX12 foundation framework
This commit is contained in:
102
ContentTools/ContentTools/ContentTools.vcxproj
Normal file
102
ContentTools/ContentTools/ContentTools.vcxproj
Normal 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>
|
||||
42
ContentTools/ContentTools/ContentTools.vcxproj.filters
Normal file
42
ContentTools/ContentTools/ContentTools.vcxproj.filters
Normal 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>
|
||||
352
ContentTools/ContentTools/FbxImporter.cpp
Normal file
352
ContentTools/ContentTools/FbxImporter.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
54
ContentTools/ContentTools/FbxImporter.h
Normal file
54
ContentTools/ContentTools/FbxImporter.h
Normal 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 };
|
||||
|
||||
};
|
||||
}
|
||||
677
ContentTools/ContentTools/Geometry.cpp
Normal file
677
ContentTools/ContentTools/Geometry.cpp
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
175
ContentTools/ContentTools/Geometry.h
Normal file
175
ContentTools/ContentTools/Geometry.h
Normal 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);
|
||||
}
|
||||
341
ContentTools/ContentTools/MeshPrimitives.cpp
Normal file
341
ContentTools/ContentTools/MeshPrimitives.cpp
Normal 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 坐标范围以及顶点缠绕顺序。
|
||||
* 需要注意,position,uvs记录的是顶点,在后面的indices,uv_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);
|
||||
}
|
||||
|
||||
}
|
||||
26
ContentTools/ContentTools/MeshPrimitives.h
Normal file
26
ContentTools/ContentTools/MeshPrimitives.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
||||
11
ContentTools/ContentTools/ToolsCommon.h
Normal file
11
ContentTools/ContentTools/ToolsCommon.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "CommonHeader.h"
|
||||
#include <combaseapi.h>
|
||||
|
||||
#ifndef EDITOR_INTERFACE
|
||||
#define EDITOR_INTERFACE extern "C" __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user