#include "mesh_service.hpp" #include #include #include #include #include #include #include #include namespace sdl3cpp::services::impl { MeshService::MeshService(std::shared_ptr configService) : configService_(std::move(configService)) { } bool MeshService::LoadFromFile(const std::string& requestedPath, MeshPayload& outPayload, std::string& outError) { if (!configService_) { outError = "Config service not available"; return false; } std::filesystem::path resolved(requestedPath); if (!resolved.is_absolute()) { resolved = configService_->GetScriptPath().parent_path() / resolved; } std::error_code ec; resolved = std::filesystem::weakly_canonical(resolved, ec); if (ec) { outError = "Failed to resolve mesh path: " + ec.message(); return false; } if (!std::filesystem::exists(resolved)) { outError = "Mesh file not found: " + resolved.string(); return false; } Assimp::Importer importer; const aiScene* scene = importer.ReadFile( resolved.string(), aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_PreTransformVertices); if (!scene) { outError = importer.GetErrorString() ? importer.GetErrorString() : "Assimp failed to load mesh"; return false; } if (scene->mNumMeshes == 0) { outError = "Scene contains no meshes"; return false; } const aiMesh* mesh = scene->mMeshes[0]; if (!mesh->mNumVertices) { outError = "Mesh contains no vertices"; return false; } outPayload.positions.clear(); outPayload.colors.clear(); outPayload.indices.clear(); outPayload.positions.reserve(mesh->mNumVertices); outPayload.colors.reserve(mesh->mNumVertices); outPayload.indices.reserve(mesh->mNumFaces * 3); aiColor3D defaultColor(0.6f, 0.8f, 1.0f); aiColor3D materialColor = defaultColor; if (mesh->mMaterialIndex < scene->mNumMaterials) { const aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; aiColor4D diffuse; if (material && material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS) { materialColor = aiColor3D(diffuse.r, diffuse.g, diffuse.b); } } for (unsigned i = 0; i < mesh->mNumVertices; ++i) { const aiVector3D& vertex = mesh->mVertices[i]; outPayload.positions.push_back({vertex.x, vertex.y, vertex.z}); aiColor3D color = materialColor; if (mesh->HasVertexColors(0) && mesh->mColors[0]) { const aiColor4D& vertexColor = mesh->mColors[0][i]; color = aiColor3D(vertexColor.r, vertexColor.g, vertexColor.b); } outPayload.colors.push_back({color.r, color.g, color.b}); } for (unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) { const aiFace& face = mesh->mFaces[faceIndex]; if (face.mNumIndices != 3) { continue; } outPayload.indices.push_back(face.mIndices[0]); outPayload.indices.push_back(face.mIndices[1]); outPayload.indices.push_back(face.mIndices[2]); } if (outPayload.indices.empty()) { outError = "Mesh contains no triangle faces"; return false; } return true; } void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) { lua_newtable(L); lua_newtable(L); for (size_t vertexIndex = 0; vertexIndex < payload.positions.size(); ++vertexIndex) { lua_newtable(L); lua_newtable(L); for (int component = 0; component < 3; ++component) { lua_pushnumber(L, payload.positions[vertexIndex][component]); lua_rawseti(L, -2, component + 1); } lua_setfield(L, -2, "position"); lua_newtable(L); for (int component = 0; component < 3; ++component) { lua_pushnumber(L, payload.colors[vertexIndex][component]); lua_rawseti(L, -2, component + 1); } lua_setfield(L, -2, "color"); lua_rawseti(L, -2, static_cast(vertexIndex + 1)); } lua_setfield(L, -2, "vertices"); lua_newtable(L); for (size_t index = 0; index < payload.indices.size(); ++index) { lua_pushinteger(L, static_cast(payload.indices[index]) + 1); lua_rawseti(L, -2, static_cast(index + 1)); } lua_setfield(L, -2, "indices"); } } // namespace sdl3cpp::services::impl