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
@@ -229,6 +229,12 @@ namespace Engine
// 尺寸变化走实体 Transform 缩放,勿按尺寸炸缓存
ModelRef CreateCube(f32 halfSize, u8 r, u8 g, u8 b);
// 通用程序化图元(MeshFactory 形状名 + 参数):同形状同参数同色
// 缓存去重。几何唯一源 = MeshFactory(见 MeshFactory.h)
ModelRef CreatePrimitive(const std::string& shape,
const PrimitiveDesc& desc,
u8 r, u8 g, u8 b);
// 环境资产(HDR → IBL 预积分四件套)。路径缓存去重,
// 重复 Load 同一路径零成本(动态切换场景直接反复调用)
EnvRef LoadEnvironment(const std::string& path);
@@ -45,6 +45,12 @@ namespace Engine::Assets {
// 程序化纯色立方体(halfSize 为半边长;占位几何/碎片模拟)
virtual Scope<Model> CreateCube(f32 halfSize, u8 r, u8 g, u8 b) = 0;
// 通用程序化图元(MeshFactory 形状名:cube/plane/sphere/cylinder/
// cone/torus 等):形状 + 参数 → 单 mesh 模型。非法形状返回 nullptr
virtual Scope<Model> CreatePrimitive(const std::string& shape,
const PrimitiveDesc& desc,
u8 r, u8 g, u8 b) = 0;
// 环境 HDR → IBL 预积分四件套(compute 生成,毫秒级;失败返回 nullptr)
virtual Scope<Cubemap> LoadEnvironment(const std::string& path) = 0;
@@ -0,0 +1,72 @@
//
// Created by atdunbg on 2026/10/18.
//
// MeshFactory:预制图元的 CPU 几何生成端(唯一几何源)。
//
// 定位对齐 UE 的 Engine\Source\Runtime\Engine\...(内置基础网格)思路:
// 引擎只保留一套"参数化图元 → 顶点/索引"的生成器,程序化 Model、
// 物理形状、脚本与编辑器下拉全部复用它,避免各路径各写一份立方体。
//
// 契约:
// - 所有图元中心在局部原点(平面在 y=0 的 XZ 面),尺寸为世界单位,
// 尺寸变化统一走实体 Transform 缩放,不按尺寸炸资产缓存;
// - 面法线朝外、CCW 从外正视,可被背面剔除;
// - 切线已按 UV 正交化(法线贴图 TBN 可用),退化为任一与 N 正交向量;
// - 输出 LOD0 单级索引(图元顶点少,无简化链价值)。
//
#ifndef HELLOVULKAN_MESHFACTORY_H
#define HELLOVULKAN_MESHFACTORY_H
#include <Engine/Core/Types.h>
#include <Engine/Renderer/Vertex.h>
#include <string>
#include <vector>
namespace Engine {
// 图元参数(形状无关的标量集):各形状只读取自己关心的字段。
// 默认值给"半径/半边长 0.5、高 1"的规格化尺寸,贴合单位立方体约定。
struct PrimitiveDesc {
f32 size = 0.5f; // 半边长(cube/plane)/ 半径(sphere/cylinder/cone)
f32 height = 1.0f; // 圆柱/圆锥高(沿 +Y,几何中心在原点)
f32 majorRadius = 0.5f; // 圆环主半径
f32 minorRadius = 0.15f; // 圆环管半径
u32 segments = 32; // 环绕细分(球/柱/锥/圆环主环)
u32 rings = 16; // 纵向细分(球)/ 圆环管环细分
u32 subdivisions = 1; // 平面每轴网格细分
};
// 预制图元的 CPU 几何(渲染 Mesh 与物理/脚本共用)。
struct MeshData {
std::vector<Vertex> vertices;
std::vector<u32> indices; // LOD0(三角形列表)
};
class MeshFactory {
public:
// 按名字取形状(ShapeNames 中任一);未知名字返回空 MeshData。
static MeshData Make(const std::string& shape, const PrimitiveDesc& desc);
// ---- 具名构造(编辑器下拉 / 物理形状 / 脚本可直接调用)----
static MeshData Cube(f32 halfSize);
static MeshData Plane(f32 halfSize, u32 subdivisions = 1);
static MeshData Sphere(f32 radius, u32 segments = 32, u32 rings = 16);
static MeshData Cylinder(f32 radius, f32 height, u32 segments = 32);
static MeshData Cone(f32 radius, f32 height, u32 segments = 32);
static MeshData Torus(f32 majorRadius, f32 minorRadius,
u32 majorSegments = 32, u32 minorSegments = 16);
// 已知图元名(UI 下拉 / 伪路径校验的唯一定义点)
static const std::vector<std::string>& ShapeNames();
static bool IsKnownShape(const std::string& shape);
private:
// 逐三角面积加权累积切线 → 对 N 正交化 → 归一(退化取正交兜底)
static void ComputeTangents(MeshData& mesh);
};
} // namespace Engine
#endif //HELLOVULKAN_MESHFACTORY_H
+23 -13
View File
@@ -10,6 +10,7 @@
#include <Engine/Core/Types.h>
#include <Engine/Renderer/Material.h>
#include <Engine/Renderer/Mesh.h>
#include <Engine/Renderer/MeshFactory.h> // 程序化几何唯一源(MeshData/PrimitiveDesc)
#include <glm/gtc/quaternion.hpp>
#include <vulkan/vulkan.hpp>
#include <assimp/matrix4x4.h> // 默认参数需完整类型(烘焙节点变换用)
@@ -150,9 +151,12 @@ namespace Engine
vk::DescriptorSetLayout textureLayout,
ParsedModel&& parsed);
// 程序化模型:单位法线朝上的 XZ 平面(纯色贴图),用于地面等。
// size 为半边长(平面总边长 = 2*size)。
// 程序化几何标签:几何统一来自 MeshFactory(单一几何源),
// 本类只负责把几何 + 纯色 albedo 组装成可渲染 Model
struct ProceduralTag {};
// 程序化纯色平面:单位法线朝上的 XZ 平面(纯色贴图),用于地面等。
// size 为半边长(平面总边长 = 2*size)。
static Scope<Model> CreatePlane(Device& device, MemoryAllocator& allocator,
DescriptorManager& descriptorManager,
vk::DescriptorSetLayout textureLayout,
@@ -164,15 +168,25 @@ namespace Engine
vk::DescriptorSetLayout textureLayout,
f32 halfSize, u8 r, u8 g, u8 b, u8 a);
// 程序化立方体(6 面 24 顶点纯色贴图,halfSize = 半边长):
// 程序化立方体(halfSize = 半边长,几何走 MeshFactory::Cube):
// 通用占位几何(碎片模拟的墙/块等)。尺寸变化走实体 Transform
// 缩放——同 halfSize 同色共享一份缓存模型
struct CubeTag {};
static Scope<Model> CreateCube(Device& device, MemoryAllocator& allocator,
DescriptorManager& descriptorManager,
vk::DescriptorSetLayout textureLayout,
f32 halfSize, u8 r, u8 g, u8 b);
// 通用程序化图元(MeshFactory 形状名:cube/plane/sphere/cylinder/
// cone/torus 等):几何由 MeshFactory::Make 生成,纯色 albedo。
// 非法形状名返回 nullptr(调用方回退)
static Scope<Model> CreatePrimitive(Device& device,
MemoryAllocator& allocator,
DescriptorManager& descriptorManager,
vk::DescriptorSetLayout textureLayout,
const std::string& shape,
const PrimitiveDesc& desc,
u8 r, u8 g, u8 b);
~Model();
Model(const Model&) = delete;
@@ -218,18 +232,14 @@ namespace Engine
const std::vector<ModelBone>& GetBones() const { return m_Bones; }
private:
// 程序化几何骨架:只建默认纹理 + 纯色 albedo 覆盖,mesh 顶点/索引
// 由 MeshFactory 产出(见 .cpp)。name = 编辑器材质槽显示名。
// 各具名工厂(CreatePlane/Cube/Primitive)都折到这一个 ctor
Model(Device& device, MemoryAllocator& allocator,
DescriptorManager& descriptorManager,
vk::DescriptorSetLayout textureLayout,
ProceduralTag, f32 halfSize, const std::array<u8, 4>& rgba,
bool transparent);
// 程序化立方体骨架:只建默认纹理 + 纯色 albedo 覆盖,mesh 由
// 立方体路径填(6 面,顶点法线朝外)
Model(Device& device, MemoryAllocator& allocator,
DescriptorManager& descriptorManager,
vk::DescriptorSetLayout textureLayout,
CubeTag, f32 halfSize, const std::array<u8, 4>& rgba);
ProceduralTag, MeshData&& geo, const std::array<u8, 4>& rgba,
bool transparent, const std::string& name);
// 空模型骨架(FromParsed 用):只建默认纹理,mesh 由调用方填
Model(Device& device, MemoryAllocator& allocator,
@@ -38,6 +38,9 @@ namespace Engine {
Scope<Model> CreateTransparentPlane(f32 halfSize,
u8 r, u8 g, u8 b, u8 a) override;
Scope<Model> CreateCube(f32 halfSize, u8 r, u8 g, u8 b) override;
Scope<Model> CreatePrimitive(const std::string& shape,
const PrimitiveDesc& desc,
u8 r, u8 g, u8 b) override;
Scope<Cubemap> LoadEnvironment(const std::string& path) override;
Scope<Cubemap> LoadEnvironment(const std::string& path,
DecodedEquirect&& decoded) override;