341 lines
9.4 KiB
C++
341 lines
9.4 KiB
C++
#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);
|
||
}
|
||
|
||
} |