- Assets::Dds 解析器:中性 DdsFormat 枚举(Assets 层禁 Vulkan 头铁律), 支持 legacy fourcc(DXT1/3/5/ATI1/2)+ DX10 头(BC1..BC7/RGBA8/BGRA8) + 未压缩 32bpp + 2D mip 链;数据不解码,直接上传由硬件采样解码 - Texture:按扩展名分发(.dds → InitFromDds);渲染侧负责格式映射与 sRGB 语义(颜色图走 SRGB 变体,BC4/5/6H 数据图保持线性);view/sampler 覆盖完整 mip 链;逐 mip copyBufferToImage(紧凑排布) - Model::GetOrLoadTexture:FBX/DCC 路径反斜杠归一化(否则 Linux 全挂) - DescriptorManager 池 1000 → 4096(SunTemple 1056 材质集打爆过; 材质去重属材质系统任务) - main.cpp 临时挂 SunTemple 场景验证:48 张 DDS 全部加载(2048²、12 mips), 1056 mesh 渲染正常,validation 零警告
204 lines
6.1 KiB
C++
204 lines
6.1 KiB
C++
//
|
|
// Created by atdunbg on 2026/9/21.
|
|
//
|
|
|
|
#include <Engine/Renderer/Model.h>
|
|
#include <Engine/Renderer/Material.h>
|
|
#include <Engine/Vulkan/Device.h>
|
|
#include <Engine/Vulkan/MemoryAllocator.h>
|
|
#include <Engine/Vulkan/DescriptorManager.h>
|
|
#include <Engine/Vulkan/Texture.h>
|
|
#include <Engine/Core/Log.h>
|
|
|
|
#include <assimp/Importer.hpp>
|
|
#include <assimp/scene.h>
|
|
#include <assimp/postprocess.h>
|
|
|
|
#include <filesystem>
|
|
#include <algorithm>
|
|
|
|
namespace Engine {
|
|
|
|
Model::Model(Device& device, MemoryAllocator& allocator,
|
|
DescriptorManager& descriptorManager,
|
|
vk::DescriptorSetLayout textureLayout,
|
|
const std::string& path)
|
|
: m_Device(device)
|
|
, m_Allocator(allocator)
|
|
, m_DescriptorManager(descriptorManager)
|
|
, m_TextureLayout(textureLayout)
|
|
{
|
|
// 兜底纹理
|
|
m_DefaultTexture = CreateScope<Texture>(
|
|
m_Device, m_Allocator, "assets/textures/default.tga");
|
|
|
|
LoadModel(path);
|
|
}
|
|
|
|
Model::~Model() = default;
|
|
|
|
Model::Model(Device& device, MemoryAllocator& allocator,
|
|
DescriptorManager& descriptorManager,
|
|
vk::DescriptorSetLayout textureLayout,
|
|
ProceduralTag, f32 halfSize, const std::array<u8, 3>& color)
|
|
: m_Device(device)
|
|
, m_Allocator(allocator)
|
|
, m_DescriptorManager(descriptorManager)
|
|
, m_TextureLayout(textureLayout)
|
|
{
|
|
// 纯色 1x1 贴图
|
|
auto tex = Texture::CreateSolidColor(device, allocator, color[0], color[1], color[2]);
|
|
|
|
// XZ 平面,法线 +Y,从上往下看 CCW 绕序
|
|
const f32 h = halfSize;
|
|
std::vector<Vertex> vertices = {
|
|
{{-h, 0.0f, -h}, {0, 1, 0}, {0.0f, 0.0f}},
|
|
{{-h, 0.0f, h}, {0, 1, 0}, {0.0f, 0.0f}},
|
|
{{ h, 0.0f, h}, {0, 1, 0}, {0.0f, 0.0f}},
|
|
{{ h, 0.0f, -h}, {0, 1, 0}, {0.0f, 0.0f}},
|
|
};
|
|
const std::vector<u32> indices = {0, 1, 2, 0, 2, 3};
|
|
|
|
auto mat = CreateScope<Material>(
|
|
m_Device, m_DescriptorManager, m_TextureLayout, tex.get());
|
|
|
|
m_Meshes.push_back(CreateScope<Mesh>(
|
|
m_Device, m_Allocator, vertices, indices, std::move(mat)));
|
|
|
|
// 贴图生命周期随模型
|
|
m_DefaultTexture = std::move(tex);
|
|
}
|
|
|
|
Scope<Model> Model::CreatePlane(Device& device, MemoryAllocator& allocator,
|
|
DescriptorManager& descriptorManager,
|
|
vk::DescriptorSetLayout textureLayout,
|
|
f32 halfSize, u8 r, u8 g, u8 b)
|
|
{
|
|
return Scope<Model>(new Model(device, allocator, descriptorManager,
|
|
textureLayout, ProceduralTag{},
|
|
halfSize, std::array<u8, 3>{r, g, b}));
|
|
}
|
|
|
|
void Model::LoadModel(const std::string& path)
|
|
{
|
|
m_ModelPath = path;
|
|
|
|
Assimp::Importer importer;
|
|
const aiScene* scene = importer.ReadFile(path,
|
|
aiProcess_Triangulate |
|
|
aiProcess_FlipUVs |
|
|
aiProcess_GenSmoothNormals |
|
|
aiProcess_JoinIdenticalVertices);
|
|
|
|
if (!scene || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) || !scene->mRootNode) {
|
|
ENGINE_ERROR("Assimp error: {}", importer.GetErrorString());
|
|
return;
|
|
}
|
|
|
|
ProcessNode(scene->mRootNode, scene);
|
|
ENGINE_INFO("Model loaded: {} ({} meshes, {} textures)",
|
|
path, m_Meshes.size(), m_TextureCache.size());
|
|
}
|
|
|
|
void Model::ProcessNode(aiNode* node, const aiScene* scene)
|
|
{
|
|
for (u32 i = 0; i < node->mNumMeshes; ++i) {
|
|
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
|
m_Meshes.push_back(ProcessMesh(mesh, scene));
|
|
}
|
|
for (u32 i = 0; i < node->mNumChildren; ++i) {
|
|
ProcessNode(node->mChildren[i], scene);
|
|
}
|
|
}
|
|
|
|
Texture* Model::GetOrLoadTexture(const std::string& rawPath)
|
|
{
|
|
// FBX/DCC 导出的路径常带反斜杠(Windows 语义),统一成正斜杠
|
|
std::string path = rawPath;
|
|
std::replace(path.begin(), path.end(), '\\', '/');
|
|
|
|
auto it = m_TextureCache.find(path);
|
|
if (it != m_TextureCache.end()) {
|
|
return it->second.get();
|
|
}
|
|
|
|
// 文件不存在则用兜底纹理
|
|
if (!std::filesystem::exists(path)) {
|
|
ENGINE_WARN("Texture not found, using default: {}", path);
|
|
return m_DefaultTexture.get();
|
|
}
|
|
|
|
auto tex = CreateScope<Texture>(m_Device, m_Allocator, path);
|
|
Texture* ptr = tex.get();
|
|
m_TextureCache.emplace(path, std::move(tex));
|
|
return ptr;
|
|
}
|
|
|
|
Scope<Mesh> Model::ProcessMesh(aiMesh* mesh, const aiScene* scene)
|
|
{
|
|
std::vector<Vertex> vertices;
|
|
std::vector<u32> indices;
|
|
|
|
vertices.reserve(mesh->mNumVertices);
|
|
|
|
for (u32 i = 0; i < mesh->mNumVertices; ++i) {
|
|
Vertex vertex{};
|
|
|
|
vertex.position = {
|
|
mesh->mVertices[i].x,
|
|
mesh->mVertices[i].y,
|
|
mesh->mVertices[i].z
|
|
};
|
|
|
|
if (mesh->HasNormals()) {
|
|
vertex.normal = {
|
|
mesh->mNormals[i].x,
|
|
mesh->mNormals[i].y,
|
|
mesh->mNormals[i].z
|
|
};
|
|
}
|
|
|
|
if (mesh->mTextureCoords[0]) {
|
|
vertex.uv = {
|
|
mesh->mTextureCoords[0][i].x,
|
|
mesh->mTextureCoords[0][i].y
|
|
};
|
|
}
|
|
|
|
vertices.push_back(vertex);
|
|
}
|
|
|
|
for (u32 i = 0; i < mesh->mNumFaces; ++i) {
|
|
aiFace face = mesh->mFaces[i];
|
|
for (u32 j = 0; j < face.mNumIndices; ++j) {
|
|
indices.push_back(face.mIndices[j]);
|
|
}
|
|
}
|
|
|
|
// 读取材质纹理路径
|
|
std::string texturePath;
|
|
|
|
if (mesh->mMaterialIndex >= 0) {
|
|
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
|
|
|
|
aiString path;
|
|
if (material->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS) {
|
|
const std::filesystem::path modelDir =
|
|
std::filesystem::path(m_ModelPath).parent_path();
|
|
texturePath = (modelDir / path.C_Str()).string();
|
|
}
|
|
}
|
|
|
|
// 从缓存拿,没有才加载
|
|
Texture* tex = texturePath.empty()
|
|
? m_DefaultTexture.get()
|
|
: GetOrLoadTexture(texturePath);
|
|
|
|
auto mat = CreateScope<Material>(
|
|
m_Device, m_DescriptorManager, m_TextureLayout, tex);
|
|
|
|
return CreateScope<Mesh>(
|
|
m_Device, m_Allocator, vertices, indices, std::move(mat));
|
|
}
|
|
|
|
} // namespace Engine
|