mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-25 06:04:57 +00:00
184 lines
6.1 KiB
C++
184 lines
6.1 KiB
C++
#include "mesh_service.hpp"
|
|
#include <utility>
|
|
|
|
#include <assimp/Importer.hpp>
|
|
#include <assimp/material.h>
|
|
#include <assimp/postprocess.h>
|
|
#include <assimp/scene.h>
|
|
#include <filesystem>
|
|
#include <lua.hpp>
|
|
#include <system_error>
|
|
|
|
namespace sdl3cpp::services::impl {
|
|
|
|
MeshService::MeshService(std::shared_ptr<IConfigService> configService,
|
|
std::shared_ptr<ILogger> 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<float, 3> 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<int>(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<lua_Integer>(payload.indices[index]) + 1);
|
|
lua_rawseti(L, -2, static_cast<int>(index + 1));
|
|
}
|
|
lua_setfield(L, -2, "indices");
|
|
}
|
|
|
|
} // namespace sdl3cpp::services::impl
|