#include "mesh_service.hpp" #include #include #include #include #include #include #include #include namespace sdl3cpp::services::impl { MeshService::MeshService(std::shared_ptr configService, std::shared_ptr logger) : configService_(std::move(configService)), logger_(std::move(logger)) { if (logger_) { logger_->Trace("MeshService", "MeshService", "configService=" + std::string(configService_ ? "set" : "null")); } } bool MeshService::LoadFromFile(const std::string& requestedPath, MeshPayload& outPayload, std::string& outError) { if (logger_) { logger_->Trace("MeshService", "LoadFromFile", "requestedPath=" + requestedPath); } 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 | aiProcess_GenNormals); 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.normals.clear(); outPayload.colors.clear(); outPayload.indices.clear(); outPayload.positions.reserve(mesh->mNumVertices); outPayload.normals.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}); aiVector3D normal(0.0f, 0.0f, 1.0f); if (mesh->HasNormals()) { normal = mesh->mNormals[i]; } outPayload.normals.push_back({normal.x, normal.y, normal.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) { if (logger_) { logger_->Trace("MeshService", "PushMeshToLua", "positions.size=" + std::to_string(payload.positions.size()) + ", normals.size=" + std::to_string(payload.normals.size()) + ", colors.size=" + std::to_string(payload.colors.size()) + ", indices.size=" + std::to_string(payload.indices.size()) + ", luaStateIsNull=" + std::string(L ? "false" : "true")); } 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); std::array normal = {0.0f, 0.0f, 1.0f}; if (vertexIndex < payload.normals.size()) { normal = payload.normals[vertexIndex]; } for (int component = 0; component < 3; ++component) { lua_pushnumber(L, normal[component]); lua_rawseti(L, -2, component + 1); } lua_setfield(L, -2, "normal"); 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