MeshFactory:程序化几何单一源 + CreatePrimitive 链路

新增 MeshFactory(Cube/Plane/Sphere/Cylinder/Cone/Torus + 切线计算),
Model 程序化几何改走 MeshFactory(收敛为单一几何源,消除重复的顶点
生成),并新增 CreatePrimitive;打通 AssetManager/IAssetUploader/
VulkanAssetUploader 的 CreatePrimitive 链路;编辑器 Procedural 弹窗
下拉 + ResolveProcedural 伪路径反解 + 场景序列化(程序化图元可存取)。
This commit is contained in:
2026-10-18 17:09:25 +08:00
parent 22309d24f0
commit f107854236
11 changed files with 654 additions and 117 deletions
+55 -13
View File
@@ -686,8 +686,13 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
}
if (ImGui::BeginPopup("##proc_model"))
{
static int shape = 0; // 0=cube 1=plane 2=transparent plane
static float half = 0.5f;
// 形状集合与 MeshFactory::ShapeNames 对齐
static int shape = 0; // 0=cube 1=plane 2=tplane
// 3=sphere 4=cylinder 5=cone 6=torus
static float half = 0.5f; // 半径 / 半边长
static float height = 1.0f; // 圆柱 / 圆锥高
static float majorR = 0.5f; // 圆环主半径
static float minorR = 0.15f; // 圆环管半径
static float col[3] = {0.55f, 0.36f, 0.28f};
static float alpha = 0.5f;
ImGui::RadioButton("Cube", &shape, 0);
@@ -695,11 +700,32 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
ImGui::RadioButton("Plane", &shape, 1);
ImGui::SameLine();
ImGui::RadioButton("Trans.Plane", &shape, 2);
ImGui::RadioButton("Sphere", &shape, 3);
ImGui::SameLine();
ImGui::RadioButton("Cylinder", &shape, 4);
ImGui::SameLine();
ImGui::RadioButton("Cone", &shape, 5);
ImGui::SameLine();
ImGui::RadioButton("Torus", &shape, 6);
// 固定宽度而非 -1:弹窗是 AlwaysAutoResize,-1(撑满可用
// 宽度)会拿上一帧宽度当基准 → 逐帧膨胀直到撑满屏幕
ImGui::SetNextItemWidth(180.0f);
ImGui::SliderFloat("half size", &half, 0.01f, 10.0f,
"%.2f");
ImGui::SliderFloat("size", &half, 0.01f, 10.0f, "%.2f");
if (shape == 4 || shape == 5)
{
ImGui::SetNextItemWidth(180.0f);
ImGui::SliderFloat("height", &height, 0.01f, 10.0f,
"%.2f");
}
if (shape == 6)
{
ImGui::SetNextItemWidth(180.0f);
ImGui::SliderFloat("major radius", &majorR, 0.01f, 10.0f,
"%.2f");
ImGui::SetNextItemWidth(180.0f);
ImGui::SliderFloat("minor radius", &minorR, 0.01f, 5.0f,
"%.2f");
}
ImGui::ColorEdit3("color", col);
if (shape == 2)
{
@@ -712,21 +738,37 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
const int r = int(col[0] * 255.0f + 0.5f);
const int g = int(col[1] * 255.0f + 0.5f);
const int b = int(col[2] * 255.0f + 0.5f);
char key[96];
if (shape == 2)
char key[128];
switch (shape)
{
const int a = int(alpha * 255.0f + 0.5f);
case 2:
std::snprintf(
key, sizeof(key),
"procedural://transparent-plane/%.3g/%d,%d,%d,%d",
half, r, g, b, a);
}
else
{
std::snprintf(
key, sizeof(key),
half, r, g, b, int(alpha * 255.0f + 0.5f));
break;
case 3:
std::snprintf(key, sizeof(key),
"procedural://sphere/%.3g/%d,%d,%d",
half, r, g, b);
break;
case 4:
case 5:
std::snprintf(key, sizeof(key),
"procedural://%s/%.3g_%.3g/%d,%d,%d",
shape == 4 ? "cylinder" : "cone",
half, height, r, g, b);
break;
case 6:
std::snprintf(key, sizeof(key),
"procedural://torus/%.3g_%.3g/%d,%d,%d",
majorR, minorR, r, g, b);
break;
default:
std::snprintf(key, sizeof(key),
"procedural://%s/%.3g/%d,%d,%d",
shape == 0 ? "cube" : "plane", half, r, g, b);
break;
}
if (auto ref = Engine::Serialization::
SceneSerializer::ResolveProcedural(