// // Created by atdunbg on 2026/9/21. // #include #include #include #include #include #include #include #include #include #include #include #include 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( 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& 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 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 indices = {0, 1, 2, 0, 2, 3}; auto mat = CreateScope( m_Device, m_DescriptorManager, m_TextureLayout, tex.get()); m_Meshes.push_back(CreateScope( m_Device, m_Allocator, vertices, indices, std::move(mat))); // 贴图生命周期随模型 m_DefaultTexture = std::move(tex); } Scope Model::CreatePlane(Device& device, MemoryAllocator& allocator, DescriptorManager& descriptorManager, vk::DescriptorSetLayout textureLayout, f32 halfSize, u8 r, u8 g, u8 b) { return Scope(new Model(device, allocator, descriptorManager, textureLayout, ProceduralTag{}, halfSize, std::array{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(m_Device, m_Allocator, path); Texture* ptr = tex.get(); m_TextureCache.emplace(path, std::move(tex)); return ptr; } Scope Model::ProcessMesh(aiMesh* mesh, const aiScene* scene) { std::vector vertices; std::vector 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( m_Device, m_DescriptorManager, m_TextureLayout, tex); return CreateScope( m_Device, m_Allocator, vertices, indices, std::move(mat)); } } // namespace Engine