Files
HelloVulkan/Engine/src/Renderer/Mesh.cpp
T
atdunbg d5a86c170e fix(renderer): mesh collider source positions in render space for skinned meshes
Skinned meshes keep vertices in raw mesh space (bakeTransform=false);
rendering transforms them via bind palette (global(bone) x invBind, folds
FBX unit scale). Physics MeshShape/ConvexHull and the mesh bounding sphere
consumed the raw positions directly -> M1911 hull measured ~10x the visual
size. ParseFromDisk now bakes bind-palette-skinned positions per vertex
(same formula as the skinning shader) into ParsedMesh::cpuPositions; Mesh
uses them for m_CpuPositions and bounds. GPU vertex data and rendering are
untouched.
2026-10-03 17:43:55 +08:00

131 lines
5.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by atdunbg on 2026/9/21.
//
#include <Engine/Renderer/Mesh.h>
#include <Engine/Renderer/Material.h>
#include <Engine/Vulkan/Device.h>
#include <Engine/Vulkan/MemoryAllocator.h>
#include <Engine/Vulkan/Buffer.h>
#include <Engine/Core/Log.h>
#include <limits>
#include <atomic>
#include "Engine/Core/Stats.h"
namespace Engine
{
namespace {
// 全局连续 mesh 槽位:SceneRenderer 的 batch 查找用数组寻址
//(每实例每视图一次 hash 的代价在大场景下是 5ms 级)
std::atomic<u32> s_nextMeshSlot{0};
}
Mesh::Mesh(Device& device, MemoryAllocator& allocator,
const std::vector<Vertex>& vertices,
const std::vector<std::vector<u32>>& lods,
Material* material,
const std::vector<glm::vec3>* 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<Buffer>(
device, allocator, vbSize,
vk::BufferUsageFlagBits::eVertexBuffer,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent);
m_VertexBuffer->Upload(vertices.data(), vbSize);
// 各级索引连续拼进同一 index buffer(共享顶点,只换索引区间)
std::vector<u32> allIndices;
for (const auto& idx : lods) {
allIndices.insert(allIndices.end(), idx.begin(), idx.end());
}
u64 ibSize = sizeof(u32) * allIndices.size();
m_IndexBuffer = CreateScope<Buffer>(
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<u32>(lods[lod].size())};
first += m_Lods[lod].indexCount;
} else {
m_Lods[lod] = m_Lods[lods.size() - 1];
}
}
// 局部空间包围球:顶点包围盒 → 中心 + 最大半径(视锥剔除用)
// 蒙皮 mesh 用绑定姿态副本(原始空间包围球大一个数量级 = 剔除/拾取失真)
{
glm::vec3 mn(std::numeric_limits<f32>::max());
glm::vec3 mx(std::numeric_limits<f32>::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