// // Created by atdunbg on 2026/9/21. // #include #include #include #include #include #include #include #include #include "Engine/Core/Stats.h" namespace Engine { namespace { // 全局连续 mesh 槽位:SceneRenderer 的 batch 查找用数组寻址 //(每实例每视图一次 hash 的代价在大场景下是 5ms 级) std::atomic s_nextMeshSlot{0}; } Mesh::Mesh(Device& device, MemoryAllocator& allocator, const std::vector& vertices, const std::vector>& lods, Material* material, const std::vector* cpuPositions) : m_Material(material) , m_Slot(s_nextMeshSlot.fetch_add(1, std::memory_order_relaxed)) { if (vertices.empty() || lods.empty() || lods[0].empty()) { ENGINE_ERROR("Mesh: empty vertices or LOD0 indices"); return; } // 位置取数:蒙皮 mesh 用绑定姿态渲染空间副本(与视觉同源), // 否则用原始顶点位置 const bool useBind = cpuPositions != nullptr && cpuPositions->size() == vertices.size(); auto posAt = [&](size_t i) -> const glm::vec3& { return useBind ? (*cpuPositions)[i] : vertices[i].position; }; // CPU 位置副本:物理 MeshShape/ConvexHull 数据源(GPU 上传后 // 完整 Vertex 不再保留,见 Mesh.h) m_CpuPositions.reserve(vertices.size()); for (size_t i = 0; i < vertices.size(); ++i) { m_CpuPositions.push_back(posAt(i)); } m_CpuIndices = lods[0]; u64 vbSize = sizeof(Vertex) * vertices.size(); m_VertexBuffer = CreateScope( device, allocator, vbSize, vk::BufferUsageFlagBits::eVertexBuffer, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); m_VertexBuffer->Upload(vertices.data(), vbSize); // 各级索引连续拼进同一 index buffer(共享顶点,只换索引区间) std::vector allIndices; for (const auto& idx : lods) { allIndices.insert(allIndices.end(), idx.begin(), idx.end()); } u64 ibSize = sizeof(u32) * allIndices.size(); m_IndexBuffer = CreateScope( device, allocator, ibSize, vk::BufferUsageFlagBits::eIndexBuffer, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); m_IndexBuffer->Upload(allIndices.data(), ibSize); // 记录各级区间。LOD 不足的级复用最后一级的"已有区间" //(first 不得继续累加——buffer 里没有重复数据,累加必越界) u32 first = 0; for (u32 lod = 0; lod < kMeshLodCount; ++lod) { if (lod < lods.size()) { m_Lods[lod] = {first, static_cast(lods[lod].size())}; first += m_Lods[lod].indexCount; } else { m_Lods[lod] = m_Lods[lods.size() - 1]; } } // 局部空间包围球:顶点包围盒 → 中心 + 最大半径(视锥剔除用) // 蒙皮 mesh 用绑定姿态副本(原始空间包围球大一个数量级 = 剔除/拾取失真) { glm::vec3 mn(std::numeric_limits::max()); glm::vec3 mx(std::numeric_limits::lowest()); for (size_t i = 0; i < vertices.size(); ++i) { mn = glm::min(mn, posAt(i)); mx = glm::max(mx, posAt(i)); } m_Bounds.center = (mn + mx) * 0.5f; m_Bounds.radius = 0.0f; for (size_t i = 0; i < vertices.size(); ++i) { m_Bounds.radius = glm::max(m_Bounds.radius, glm::length(posAt(i) - m_Bounds.center)); } } ENGINE_INFO("Mesh created: {} vertices, LOD0 {} indices", vertices.size(), lods[0].size()); m_VertexBytes = sizeof(Vertex) * vertices.size(); for (const auto& idx : lods) { m_IndexBytes += sizeof(u32) * idx.size(); } STATS.AddMesh(m_VertexBytes, m_IndexBytes); } Mesh::~Mesh() { STATS.RemoveMesh(m_VertexBytes, m_IndexBytes); } void Mesh::Draw(vk::CommandBuffer cmd, u32 instanceCount, u32 firstInstance, u32 lod) const { const LodRange& range = m_Lods[lod < kMeshLodCount ? lod : kMeshLodCount - 1]; cmd.bindVertexBuffers(0, m_VertexBuffer->Get(), {0}); cmd.bindIndexBuffer(m_IndexBuffer->Get(), 0, vk::IndexType::eUint32); cmd.drawIndexed(range.indexCount, instanceCount, range.firstIndex, 0, firstInstance); } } // namespace Engine