diff --git a/core/client/include/pragma/asset/c_util_model.hpp b/core/client/include/pragma/asset/c_util_model.hpp index 12d51033d..784ed0a6a 100644 --- a/core/client/include/pragma/asset/c_util_model.hpp +++ b/core/client/include/pragma/asset/c_util_model.hpp @@ -98,6 +98,8 @@ namespace pragma::asset { DLLCLIENT bool import_texture(std::unique_ptr &&f, const TextureImportInfo &texInfo, const std::string &outputPath, std::string &outErrMsg); DLLCLIENT bool import_texture(prosper::IImage &img, const TextureImportInfo &texInfo, const std::string &outputPath, std::string &outErrMsg); + DLLCLIENT void assign_texture(CMaterial &mat, const std::string &textureRootPath, const std::string &matIdentifier, const std::string &texName, prosper::IImage &img, bool greyScale, bool normalMap, AlphaMode alphaMode = AlphaMode::Opaque); + DLLCLIENT bool export_model(Model &model, const ModelExportInfo &exportInfo, std::string &outErrMsg, const std::optional &modelName = {}, std::string *optOutPath = nullptr); DLLCLIENT bool export_animation(Model &model, const std::string &animName, const ModelExportInfo &exportInfo, std::string &outErrMsg, const std::optional &modelName = {}); DLLCLIENT bool export_map(const std::string &mapName, const ModelExportInfo &exportInfo, std::string &outErrMsg, const std::optional &mapExportInfo = {}); diff --git a/core/client/src/asset/c_util_model.cpp b/core/client/src/asset/c_util_model.cpp index 5ee864497..ee0cd1085 100644 --- a/core/client/src/asset/c_util_model.cpp +++ b/core/client/src/asset/c_util_model.cpp @@ -292,6 +292,16 @@ uimg::TextureInfo pragma::asset::get_texture_info(bool isGreyScale, bool isNorma return texInfo; } +void pragma::asset::assign_texture(CMaterial &mat, const std::string &textureRootPath, const std::string &matIdentifier, const std::string &texName, prosper::IImage &img, bool greyScale, bool normalMap, AlphaMode alphaMode) +{ + auto texInfo = pragma::asset::get_texture_info(greyScale, normalMap, alphaMode); + c_game->SaveImage(img, textureRootPath + texName, texInfo); + + auto path = util::FilePath(texName); + path.PopFront(); + mat.SetTexture(matIdentifier, path.GetString()); +} + static std::optional import_model(ufile::IFile *optFile, const std::string &optFileName, std::string &outErrMsg, const util::Path &outputPath, bool importAsMap) { auto scale = static_cast(pragma::metres_to_units(1.f)); @@ -446,15 +456,8 @@ static std::optional import_model(ufile::IFile *optFile, const std:: dataBlock->AddValue("int", "alpha_mode", std::to_string(umath::to_integral(alphaMode))); dataBlock->AddValue("float", "alpha_cutoff", std::to_string(gltfMat.alphaCutoff)); - std::string textureRootPath = "addons/converted/"; - - auto fWriteImage = [cmat, &textureRootPath](const std::string &matIdentifier, const std::string &texName, prosper::IImage &img, bool greyScale, bool normalMap, AlphaMode alphaMode = AlphaMode::Opaque) { - auto texInfo = pragma::asset::get_texture_info(greyScale, normalMap, alphaMode); - c_game->SaveImage(img, textureRootPath + texName, texInfo); - - util::Path albedoPath {texName}; - albedoPath.PopFront(); - cmat->SetTexture(matIdentifier, albedoPath.GetString()); + auto fWriteImage = [cmat](const std::string &matIdentifier, const std::string &texName, prosper::IImage &img, bool greyScale, bool normalMap, AlphaMode alphaMode = AlphaMode::Opaque) { + pragma::asset::assign_texture(*cmat, ::util::CONVERT_PATH, matIdentifier, texName, img, greyScale, normalMap, alphaMode); }; auto isHandled = false; diff --git a/core/client/src/asset/fbx_loader.cpp b/core/client/src/asset/fbx_loader.cpp index f8f1d5480..e5c81ee40 100644 --- a/core/client/src/asset/fbx_loader.cpp +++ b/core/client/src/asset/fbx_loader.cpp @@ -14,19 +14,56 @@ #include #include #include +#include "pragma/rendering/shaders/util/c_shader_specular_glossiness_to_metalness_roughness.hpp" +#include "pragma/rendering/shaders/util/c_shader_compose_rma.hpp" +#include "pragma/rendering/shaders/util/c_shader_combine_image_channels.hpp" #include #include #include #include +#include #include #include "pragma/asset/c_util_model.hpp" +#include +#include using namespace pragma::asset::fbx; extern DLLCLIENT CGame *c_game; extern DLLCLIENT ClientState *client; +extern DLLCLIENT CEngine *c_engine; static std::string_view to_string_view(ofbx::DataView data) { return std::string_view {(const char *)data.begin, (const char *)data.end}; } +static std::shared_ptr load_texture_image(const std::string &mdlPath, const std::string &mdlName, const std::string &texPath, bool isAbsPath) +{ + auto fullTexFilePath = util::FilePath(texPath); + if(!isAbsPath) + fullTexFilePath = mdlPath + fullTexFilePath; + auto ext = fullTexFilePath.GetFileExtension(); + if(ext && (*ext == "dds" || *ext == "ktx")) { + auto &texManager = static_cast(client->GetMaterialManager()).GetTextureManager(); + auto tex = texManager.LoadAsset(fullTexFilePath.GetString(), util::AssetLoadFlags::AbsolutePath | util::AssetLoadFlags::DontCache | util::AssetLoadFlags::IgnoreCache); + if(!tex) + return nullptr; + return tex->GetVkTexture(); + } + auto f = filemanager::open_file(fullTexFilePath.GetString(), filemanager::FileMode::Read | filemanager::FileMode::Binary); + if(!f) + f = filemanager::open_system_file(fullTexFilePath.GetString(), filemanager::FileMode::Read | filemanager::FileMode::Binary); + if(!f) + return nullptr; + fsys::File file {f}; + auto imgBuf = uimg::load_image(file); + if(!imgBuf) + return nullptr; + imgBuf->SwapChannels(uimg::Channel::Red, uimg::Channel::Blue); + auto img = c_engine->GetRenderContext().CreateImage(*imgBuf); + prosper::util::TextureCreateInfo texCreateInfo {}; + prosper::util::ImageViewCreateInfo imgViewCreateInfo {}; + prosper::util::SamplerCreateInfo samplerCreateInfo {}; + return c_engine->GetRenderContext().CreateTexture(texCreateInfo, *img, imgViewCreateInfo, samplerCreateInfo); +} + static std::optional import_texture(const std::string &mdlPath, const std::string &mdlName, const std::string &texPath, bool isAbsPath) { auto fullTexFilePath = util::FilePath(texPath); @@ -57,6 +94,8 @@ static std::optional import_texture(const std::string &mdlPath, con auto img = uimg::load_image(file); if(!img) return {}; + img->SwapChannels(uimg::Channel::Red, uimg::Channel::Blue); + // TODO auto greyScale = false; auto normalMap = false; auto alphaMode = AlphaMode::Opaque; @@ -120,10 +159,10 @@ static Quat fix_orientation(const Quat &v, pragma::asset::fbx::UpVector orientat FbxImporter::~FbxImporter() { m_fbxScene->destroy(); } -bool FbxImporter::LoadMaterial(const ofbx::Material &mat, std::string &outErr) +std::optional FbxImporter::LoadMaterial(const ofbx::Material &fbxMat, uint32_t partitionIdx, std::string &outErr) { - auto importTexture = [this, &mat](ofbx::Texture::TextureType etex) -> std::optional { - auto *tex = mat.getTexture(etex); + auto importTexture = [this, &fbxMat](ofbx::Texture::TextureType etex) -> std::optional { + auto *tex = fbxMat.getTexture(etex); if(!tex) return {}; ofbx::DataView filename = tex->getRelativeFileName(); @@ -135,34 +174,103 @@ bool FbxImporter::LoadMaterial(const ofbx::Material &mat, std::string &outErr) auto path = util::FilePath(std::string {to_string_view(filename)}); return ::import_texture(m_mdlPath, m_mdlName, path.GetString(), isAbsPath); }; - auto texPath = importTexture(ofbx::Texture::DIFFUSE); - if(texPath) { - std::string fbxMatName = mat.name; - auto matPath = *texPath; - if(!fbxMatName.empty()) { - matPath = ufile::get_path_from_filename(matPath); - matPath += fbxMatName; + auto loadTexture = [this, &fbxMat](ofbx::Texture::TextureType etex) -> std::shared_ptr { + auto *tex = fbxMat.getTexture(etex); + if(!tex) + return {}; + ofbx::DataView filename = tex->getRelativeFileName(); + auto isAbsPath = false; + if(filename == "") { + filename = tex->getFileName(); + isAbsPath = true; } - else - ufile::remove_extension_from_filename(matPath, pragma::asset::get_supported_extensions(pragma::asset::Type::Texture, pragma::asset::FormatType::All)); - auto mat = client->CreateMaterial(matPath, "pbr"); - auto *cmat = static_cast(mat.get()); + auto path = util::FilePath(std::string {to_string_view(filename)}); + return ::load_texture_image(m_mdlPath, m_mdlName, path.GetString(), isAbsPath); + }; + + auto matFilePath = util::DirPath(::util::CONVERT_PATH, pragma::asset::get_asset_root_directory(pragma::asset::Type::Material), "models", m_mdlName); + std::string fbxMatName = fbxMat.name; + if(!fbxMatName.empty()) + matFilePath += fbxMatName; + else + matFilePath += "material" + std::to_string(partitionIdx); - auto &dataBlock = mat->GetDataBlock(); + auto relMatPath = matFilePath; + relMatPath.MakeRelative(util::CONVERT_PATH); + relMatPath.MakeRelative(std::string {pragma::asset::get_asset_root_directory(pragma::asset::Type::Material)}); + auto mat = client->CreateMaterial(relMatPath.GetString(), "pbr"); + auto *cmat = static_cast(mat.get()); + auto &dataBlock = mat->GetDataBlock(); + + auto importAndAssignTexture = [&importTexture, cmat](ofbx::Texture::TextureType etex, const std::string &matIdentifier) -> std::optional { + auto texPath = importTexture(etex); + if(!texPath) + return {}; auto relTexPath = util::FilePath(*texPath); relTexPath.MakeRelative(util::CONVERT_PATH); relTexPath.MakeRelative(std::string {pragma::asset::get_asset_root_directory(pragma::asset::Type::Material)}); - cmat->SetTexture(Material::ALBEDO_MAP_IDENTIFIER, relTexPath.GetString()); + cmat->SetTexture(matIdentifier, relTexPath.GetString()); + return texPath; + }; + importAndAssignTexture(ofbx::Texture::TextureType::DIFFUSE, Material::ALBEDO_MAP_IDENTIFIER); + importAndAssignTexture(ofbx::Texture::TextureType::NORMAL, Material::NORMAL_MAP_IDENTIFIER); + importAndAssignTexture(ofbx::Texture::TextureType::EMISSIVE, Material::EMISSION_MAP_IDENTIFIER); + + auto applyColorFactor = [&fbxMat, &dataBlock](const ofbx::Color &fbxColor, double factor, const std::string &matProp) { + Vector3 colorFactor {fbxColor.r, fbxColor.g, fbxColor.b}; + colorFactor *= factor; + if(umath::abs(1.f - colorFactor.x) > 0.001f || umath::abs(1.f - colorFactor.y) > 0.001f || umath::abs(1.f - colorFactor.z) > 0.001f) + dataBlock->AddValue("vector", matProp, std::to_string(colorFactor.x) + ' ' + std::to_string(colorFactor.y) + ' ' + std::to_string(colorFactor.z)); + }; + applyColorFactor(fbxMat.getDiffuseColor(), fbxMat.getDiffuseFactor(), "color_factor"); + applyColorFactor(fbxMat.getEmissiveColor(), fbxMat.getEmissiveFactor(), "emission_factor"); + + // TODO + //dataBlock->AddValue("float", "roughness_factor", std::to_string(pbrMetallicRoughness.roughnessFactor)); + //dataBlock->AddValue("float", "metalness_factor", std::to_string(pbrMetallicRoughness.metallicFactor)); + + + auto *combine = static_cast(c_engine->GetShader("combine_image_channels").get()); + auto *rma = static_cast(c_engine->GetShader("specular_glossiness_to_metalness_roughness").get()); + if(combine && rma) { + auto specularMap = loadTexture(ofbx::Texture::TextureType::SPECULAR); + auto shininessMap = loadTexture(ofbx::Texture::TextureType::SHININESS); + auto ambientMap = loadTexture(ofbx::Texture::TextureType::AMBIENT); + auto reflectionMap = loadTexture(ofbx::Texture::TextureType::REFLECTION); // TODO + + if(specularMap || shininessMap || ambientMap) { + auto tex = static_cast(client->GetMaterialManager()).GetTextureManager().LoadAsset("white"); + if(tex) { + auto whiteMap = tex->GetVkTexture(); + if(!specularMap) + specularMap = whiteMap; + if(!shininessMap) + shininessMap = whiteMap; + + auto &context = c_engine->GetRenderContext(); + pragma::ShaderCombineImageChannels::PushConstants pushConstants {}; + pushConstants.alphaChannel = umath::to_integral(uimg::Channel::Red); + auto specularGlossinessMap = combine->CombineImageChannels(context, *specularMap, *specularMap, *specularMap, *shininessMap, pushConstants); + if(specularGlossinessMap) { + pragma::ShaderSpecularGlossinessToMetalnessRoughness::PushConstants pushConstants {}; + auto metallicRoughnessSet = rma->ConvertToMetalnessRoughness(c_engine->GetRenderContext(), nullptr, specularGlossinessMap.get(), pushConstants, ambientMap.get()); + if(metallicRoughnessSet.has_value()) { + auto texPath = matFilePath; + texPath.MakeRelative(util::CONVERT_PATH); + pragma::asset::assign_texture(*cmat, ::util::CONVERT_PATH, Material::RMA_MAP_IDENTIFIER, texPath.GetString() + "_rma", *metallicRoughnessSet->rmaMap, false, false, ambientMap ? AlphaMode::Blend : AlphaMode::Opaque); + } + } + } + } + } - mat->UpdateTextures(); + mat->UpdateTextures(); - std::string err; - mat->Save(matPath, err, true); + std::string err; + mat->Save(matFilePath.GetString(), err, true); - auto idx = m_model->AddMaterial(0, mat.get()); - } - return true; + return m_model->AddMaterial(0, mat.get()); } std::optional FbxImporter::Finalize(std::string &outErr) @@ -395,20 +503,6 @@ bool FbxImporter::LoadMeshes(std::string &outErr) auto meshBone = AddBone(fbxMesh); FillSkinInfo(vertWeights, &fbxMesh, meshBone->ID); - auto numMats = fbxMesh.getMaterialCount(); - //if(numMats == 0) - { - - m_model->AddMaterial(0, client->LoadMaterial("white")); - } - for(auto i = decltype(numMats) {0u}; i < numMats; ++i) { - auto *fbxMat = fbxMesh.getMaterial(i); - if(!fbxMat) - continue; - std::string err; - LoadMaterial(*fbxMat, err); - } - auto geoMatrix = to_pragma_matrix(fbxMesh.getGeometricMatrix()); auto transformMatrix = to_pragma_matrix(fbxMesh.getGlobalTransform()) * geoMatrix; umath::ScaledTransform pose {transformMatrix}; @@ -417,11 +511,18 @@ bool FbxImporter::LoadMeshes(std::string &outErr) auto mesh = c_game->CreateModelMesh(); for(int partition_idx = 0; partition_idx < geom.getPartitionCount(); ++partition_idx) { const ofbx::GeometryPartition &partition = geom.getPartition(partition_idx); - auto *mat = (fbxMesh.getMaterialCount() > 0) ? fbxMesh.getMaterial(partition_idx) : nullptr; auto subMesh = c_game->CreateModelSubMesh(); auto &verts = subMesh->GetVertices(); + auto *mat = (fbxMesh.getMaterialCount() > 0) ? fbxMesh.getMaterial(partition_idx) : nullptr; + if(mat) { + std::string err; + auto matIdx = LoadMaterial(*mat, partition_idx, err); + if(matIdx) + subMesh->SetSkinTextureIndex(*matIdx); + } + std::unordered_map fbxIndexToPragmaIndex; for(int polygon_idx = 0; polygon_idx < partition.polygon_count; ++polygon_idx) { const ofbx::GeometryPartition::Polygon &polygon = partition.polygons[polygon_idx]; @@ -442,7 +543,7 @@ bool FbxImporter::LoadMeshes(std::string &outErr) } if(uvs.values != nullptr) { ofbx::Vec2 uv = uvs.get(idx); - v.uv = {uv.x, uv.y}; + v.uv = {uv.x, 1.f - uv.y}; } if(tangents.values != nullptr) { ofbx::Vec3 t = tangents.get(idx); @@ -506,12 +607,6 @@ bool FbxImporter::LoadMeshes(std::string &outErr) static double fbxTimeToSeconds(ofbx::i64 value) { return double(value) / 46186158000L; } -bool FbxImporter::LoadMorphTargets(std::string &outErr) -{ - // - return true; -} - static void normalizeScale(Mat4 &m) { Vector3 scale = {1 / length(Vector3(m[0].x, m[1].x, m[2].x)), 1 / length(Vector3(m[0].y, m[1].y, m[2].y)), 1 / length(Vector3(m[0].z, m[1].z, m[2].z))}; @@ -961,7 +1056,7 @@ std::optional FbxImporter::Load(std::string &o m_fbxScale = m_fbxScene->getGlobalSettings()->UnitScaleFactor * pragma::metres_to_units(1.0) * 0.01f; m_upVector = static_cast(m_fbxScene->getGlobalSettings()->UpAxis); - if(!LoadMeshes(outErr) || !LoadAnimations(outErr) || !LoadMorphTargets(outErr)) + if(!LoadMeshes(outErr) || !LoadAnimations(outErr)) return {}; auto mdlPath = Finalize(outErr); if(!mdlPath) diff --git a/core/client/src/asset/fbx_loader.hpp b/core/client/src/asset/fbx_loader.hpp index 7d7912e70..096467141 100644 --- a/core/client/src/asset/fbx_loader.hpp +++ b/core/client/src/asset/fbx_loader.hpp @@ -79,10 +79,9 @@ namespace pragma::asset::fbx { void SortBones(bool force_skinned); void InsertHierarchy(std::vector &bones, const ofbx::Object *node); void FillSkinInfo(std::vector &skinning, const ofbx::Mesh *mesh, int32_t boneId); - bool LoadMaterial(const ofbx::Material &mat, std::string &outErr); + std::optional LoadMaterial(const ofbx::Material &mat, uint32_t partitionIdx, std::string &outErr); bool LoadMeshes(std::string &outErr); bool LoadAnimations(std::string &outErr); - bool LoadMorphTargets(std::string &outErr); std::optional Finalize(std::string &outErr); std::shared_ptr AddBone(const ofbx::Object &o); };