#include "model.h" #include #include #include #include #include #ifdef IOS_REF #undef IOS_REF #define IOS_REF (*(pManager->GetIOSettings())) #endif cl::opt text_fbx("t", cl::desc("Produce ascii .fbx")); extern cl::opt all_formats; bool CreateScene(const model &m, const std::string &name, FbxManager* pSdkManager, FbxScene* pScene); void InitializeSdkObjects(FbxManager*& pManager, FbxScene*& pScene) { //The first thing to do is to create the FBX Manager which is the object allocator for almost all the classes in the SDK pManager = FbxManager::Create(); if (!pManager) { FBXSDK_printf("Error: Unable to create FBX Manager!\n"); exit(1); } //Create an IOSettings object. This object holds all import/export settings. FbxIOSettings* ios = FbxIOSettings::Create(pManager, IOSROOT); pManager->SetIOSettings(ios); //Load plugins from the executable directory (optional) FbxString lPath = FbxGetApplicationDirectory(); pManager->LoadPluginsDirectory(lPath.Buffer()); //Create an FBX scene. This object holds most objects imported/exported from/to files. pScene = FbxScene::Create(pManager, "myScene"); if (!pScene) { FBXSDK_printf("Error: Unable to create FBX scene!\n"); exit(1); } pScene->GetSceneInfo()->LastSaved_ApplicationVendor.Set("lzwdgc's"); pScene->GetSceneInfo()->LastSaved_ApplicationName.Set("mod_converter"); pScene->GetSceneInfo()->LastSaved_ApplicationVersion.Set(version().c_str()); } void DestroySdkObjects(FbxManager* pManager, bool pExitStatus) { //Delete the FBX Manager. All the objects that have been allocated using the FBX Manager and that haven't been explicitly destroyed are also automatically destroyed. if (pManager) pManager->Destroy(); if (!pExitStatus) FBXSDK_printf("Error: Unable to destroy FBX Manager!\n"); } bool SaveScene(FbxManager* pManager, FbxDocument* pScene, const char* pFilename, bool blender = false) { //int lMajor, lMinor, lRevision; bool lStatus = true; // Create an exporter. FbxExporter* lExporter = FbxExporter::Create(pManager, ""); auto pFileFormat = pManager->GetIOPluginRegistry()->GetNativeWriterFormat(); if (text_fbx) pFileFormat = 1; // 1 for ascii FbxString lDesc = pManager->GetIOPluginRegistry()->GetWriterFormatDescription(pFileFormat); // Set the export states. By default, the export states are always set to // true except for the option eEXPORT_TEXTURE_AS_EMBEDDED. The code below // shows how to change these states. /*IOS_REF.SetBoolProp(EXP_FBX_MATERIAL, true); IOS_REF.SetBoolProp(EXP_FBX_TEXTURE, true); IOS_REF.SetBoolProp(EXP_FBX_EMBEDDED, pEmbedMedia); IOS_REF.SetBoolProp(EXP_FBX_SHAPE, true); IOS_REF.SetBoolProp(EXP_FBX_GOBO, true); IOS_REF.SetBoolProp(EXP_FBX_ANIMATION, true); IOS_REF.SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true);*/ // Initialize the exporter by providing a filename. if (lExporter->Initialize(pFilename, pFileFormat, pManager->GetIOSettings()) == false) { FBXSDK_printf("Call to FbxExporter::Initialize() failed.\n"); FBXSDK_printf("Error returned: %s\n\n", lExporter->GetStatus().GetErrorString()); return false; } // Export the scene. // this one for blender if (blender) lExporter->SetFileExportVersion(FbxString("FBX201400"), FbxSceneRenamer::eNone); lStatus = lExporter->Export(pScene); // Destroy the exporter. lExporter->Destroy(); return lStatus; } bool LoadScene(FbxManager* pManager, FbxDocument* pScene, const char* pFilename) { int lFileMajor, lFileMinor, lFileRevision; int lSDKMajor, lSDKMinor, lSDKRevision; //int lFileFormat = -1; int i, lAnimStackCount; bool lStatus; char lPassword[1024]; // Get the file version number generate by the FBX SDK. FbxManager::GetFileFormatVersion(lSDKMajor, lSDKMinor, lSDKRevision); // Create an importer. FbxImporter* lImporter = FbxImporter::Create(pManager, ""); // Initialize the importer by providing a filename. const bool lImportStatus = lImporter->Initialize(pFilename, -1, pManager->GetIOSettings()); lImporter->GetFileVersion(lFileMajor, lFileMinor, lFileRevision); if (!lImportStatus) { FbxString error = lImporter->GetStatus().GetErrorString(); FBXSDK_printf("Call to FbxImporter::Initialize() failed.\n"); FBXSDK_printf("Error returned: %s\n\n", error.Buffer()); if (lImporter->GetStatus().GetCode() == FbxStatus::eInvalidFileVersion) { FBXSDK_printf("FBX file format version for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision); FBXSDK_printf("FBX file format version for file '%s' is %d.%d.%d\n\n", pFilename, lFileMajor, lFileMinor, lFileRevision); } return false; } FBXSDK_printf("FBX file format version for this FBX SDK is %d.%d.%d\n", lSDKMajor, lSDKMinor, lSDKRevision); if (lImporter->IsFBX()) { FBXSDK_printf("FBX file format version for file '%s' is %d.%d.%d\n\n", pFilename, lFileMajor, lFileMinor, lFileRevision); // From this point, it is possible to access animation stack information without // the expense of loading the entire file. FBXSDK_printf("Animation Stack Information\n"); lAnimStackCount = lImporter->GetAnimStackCount(); FBXSDK_printf(" Number of Animation Stacks: %d\n", lAnimStackCount); FBXSDK_printf(" Current Animation Stack: \"%s\"\n", lImporter->GetActiveAnimStackName().Buffer()); FBXSDK_printf("\n"); for (i = 0; i < lAnimStackCount; i++) { FbxTakeInfo* lTakeInfo = lImporter->GetTakeInfo(i); FBXSDK_printf(" Animation Stack %d\n", i); FBXSDK_printf(" Name: \"%s\"\n", lTakeInfo->mName.Buffer()); FBXSDK_printf(" Description: \"%s\"\n", lTakeInfo->mDescription.Buffer()); // Change the value of the import name if the animation stack should be imported // under a different name. FBXSDK_printf(" Import Name: \"%s\"\n", lTakeInfo->mImportName.Buffer()); // Set the value of the import state to false if the animation stack should be not // be imported. FBXSDK_printf(" Import State: %s\n", lTakeInfo->mSelect ? "true" : "false"); FBXSDK_printf("\n"); } // Set the import states. By default, the import states are always set to // true. The code below shows how to change these states. IOS_REF.SetBoolProp(IMP_FBX_MATERIAL, true); IOS_REF.SetBoolProp(IMP_FBX_TEXTURE, true); IOS_REF.SetBoolProp(IMP_FBX_LINK, true); IOS_REF.SetBoolProp(IMP_FBX_SHAPE, true); IOS_REF.SetBoolProp(IMP_FBX_GOBO, true); IOS_REF.SetBoolProp(IMP_FBX_ANIMATION, true); IOS_REF.SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, true); } // Import the scene. lStatus = lImporter->Import(pScene); if (lStatus == false && lImporter->GetStatus().GetCode() == FbxStatus::ePasswordError) { FBXSDK_printf("Please enter password: "); lPassword[0] = '\0'; FBXSDK_CRT_SECURE_NO_WARNING_BEGIN scanf("%s", lPassword); FBXSDK_CRT_SECURE_NO_WARNING_END FbxString lString(lPassword); IOS_REF.SetStringProp(IMP_FBX_PASSWORD, lString); IOS_REF.SetBoolProp(IMP_FBX_PASSWORD_ENABLE, true); lStatus = lImporter->Import(pScene); if (lStatus == false && lImporter->GetStatus().GetCode() == FbxStatus::ePasswordError) { FBXSDK_printf("\nPassword is wrong, import aborted.\n"); } } // Destroy the importer. lImporter->Destroy(); return lStatus; } void model::printFbx(const std::string &fn) const { FbxManager* lSdkManager = NULL; FbxScene* lScene = NULL; // Prepare the FBX SDK. InitializeSdkObjects(lSdkManager, lScene); // default is ??? //FbxAxisSystem::MayaZUp.ConvertScene(lScene); // Create the scene. CreateScene(*this, fn, lSdkManager, lScene); SaveScene(lSdkManager, lScene, (fn + ".fbx").c_str()); //SaveScene(lSdkManager, lScene, (fn + "_ue4.fbx").c_str()); //if (all_formats) //SaveScene(lSdkManager, lScene, (fn + "_blender.fbx").c_str(), true); // Destroy all objects created by the FBX SDK. DestroySdkObjects(lSdkManager, true); } bool CreateScene(const model &model, const std::string &name, FbxManager* pSdkManager, FbxScene* pScene) { static const char* gDiffuseElementName = "DiffuseUV"; static const char* gAmbientElementName = "AmbientUV"; static const char* gSpecularElementName = "SpecularUV"; auto create_socket_named = [&pScene](const std::string &name) { FbxNode *socket = FbxNode::Create(pScene, ("SOCKET_" + name).c_str()); auto n = FbxNull::Create(pScene, ""); socket->SetNodeAttribute(n); pScene->GetRootNode()->AddChild(socket); return socket; }; auto create_socket = [&create_socket_named](const auto &b, const std::string &name, bool mirror_x = false) { FbxVector4 c; for (auto &v : b.vertices) { FbxVector4 x; x.Set(v.coordinates.x * scale_mult(), v.coordinates.y * scale_mult(), v.coordinates.z * scale_mult(), v.coordinates.w); c += x; } c /= b.vertices.size(); auto s = create_socket_named(name); // here we must mirror 'first' number (original x) // it depends on what order was during loading if (mirror_x) c.mData[get_x_coordinate_id()] = -c.mData[get_x_coordinate_id()]; s->LclTranslation.Set(c); //s->LclScaling.Set(FbxDouble3(scale_mult(), scale_mult(), scale_mult())); }; int engine_id = 0; int fx_id = 0; for (auto &b : model.blocks) { // if (b.isEngineFx()) { create_socket(b, "EngineFx_" + std::to_string(engine_id++)); continue; } else if (b.h.name == boost::to_lower_copy("LIGHTGUN"s)) { create_socket(b, "WeaponLight_0"); create_socket(b, "WeaponLight_1", true); continue; } else if (b.h.name == boost::to_lower_copy("HEAVYGUN"s)) { create_socket(b, "WeaponHeavy"); continue; } else if (b.h.name == boost::to_lower_copy("ROCKET"s)) { create_socket(b, "WeaponRocket"); continue; } else if (b.h.name.find(boost::to_lower_copy("FX"s)) == 0) { create_socket(b, "Fx_" + std::to_string(fx_id++)); continue; } if (!b.canPrint()) continue; //auto block_name = name + "/" + b.h.name; const auto block_name = b.h.name; // mesh auto m = FbxMesh::Create(pScene, block_name.c_str()); // node FbxNode *node = FbxNode::Create(pScene, block_name.c_str()); node->SetNodeAttribute(m); node->SetShadingMode(FbxNode::eFullShading); // change?! was texture sh // vertices m->InitControlPoints(b.vertices.size()); // normals auto normal = m->CreateElementNormal(); normal->SetMappingMode(FbxGeometryElement::eByControlPoint); normal->SetReferenceMode(FbxGeometryElement::eDirect); // uv auto create_uv = [&m](auto &name) { auto uv1 = m->CreateElementUV(name); uv1->SetMappingMode(FbxGeometryElement::eByControlPoint); uv1->SetReferenceMode(FbxGeometryElement::eDirect); return uv1; }; auto d_uv = create_uv(gDiffuseElementName); auto a_uv = create_uv(gAmbientElementName); auto s_uv = create_uv(gSpecularElementName); // add vertices, normals, uvs for (const auto &[i,v] : enumerate(b.vertices)) { FbxVector4 x(v.coordinates.x * scale_mult(), v.coordinates.y * scale_mult(), v.coordinates.z * scale_mult(), v.coordinates.w); FbxVector4 n(v.normal.x, v.normal.y, v.normal.z); m->SetControlPointAt(x, n, i); float f; auto uc = modf(fabs(v.texture_coordinates.u), &f); auto vc = modf(fabs(v.texture_coordinates.v), &f); d_uv->GetDirectArray().Add(FbxVector2(uc, 1 - vc)); a_uv->GetDirectArray().Add(FbxVector2(uc, 1 - vc)); s_uv->GetDirectArray().Add(FbxVector2(uc, 1 - vc)); } // faces for (auto &v : b.faces) { // Set the control point indices of the bottom side of the pyramid m->BeginPolygon(); m->AddPolygon(v.x); m->AddPolygon(v.y); m->AddPolygon(v.z); m->EndPolygon(); } m->BuildMeshEdgeArray(); //if (m->GenerateNormals(true)) //throw SW_RUNTIME_ERROR("Cannot generate normals"); // mats auto lMaterial = node->GetSrcObject(0); if (!lMaterial) { FbxString lMaterialName = block_name.c_str(); FbxString lShadingName = "Phong"; FbxDouble3 lAmbientColor(b.mat.ambient.r, b.mat.ambient.g, b.mat.ambient.b); FbxDouble3 lSpecularColor(b.mat.specular.r, b.mat.specular.g, b.mat.specular.b); FbxDouble3 lDiffuseColor(b.mat.diffuse.r, b.mat.diffuse.g, b.mat.diffuse.b); FbxDouble3 lEmissiveColor(b.mat.emissive.r, b.mat.emissive.g, b.mat.emissive.b); FbxLayer* lLayer = m->GetLayer(0); // Create a layer element material to handle proper mapping. FbxLayerElementMaterial* lLayerElementMaterial = FbxLayerElementMaterial::Create(m, lMaterialName.Buffer()); // This allows us to control where the materials are mapped. Using eAllSame // means that all faces/polygons of the mesh will be assigned the same material. lLayerElementMaterial->SetMappingMode(FbxLayerElement::eAllSame); lLayerElementMaterial->SetReferenceMode(FbxLayerElement::eDirect); // Save the material on the layer lLayer->SetMaterials(lLayerElementMaterial); // Add an index to the lLayerElementMaterial. Since we have only one, and are using eAllSame mapping mode, // we only need to add one. lLayerElementMaterial->GetIndexArray().Add(0); lMaterial = FbxSurfacePhong::Create(pScene, lMaterialName.Buffer()); // Generate primary and secondary colors. lMaterial->Emissive.Set(lEmissiveColor); lMaterial->EmissiveFactor.Set(b.mat.emissive.r); lMaterial->Ambient.Set(lAmbientColor); lMaterial->AmbientFactor.Set(b.mat.ambient.r); // Add texture for diffuse channel lMaterial->Diffuse.Set(lDiffuseColor); lMaterial->DiffuseFactor.Set(b.mat.diffuse.r); //lMaterial->TransparencyFactor.Set(0.4); lMaterial->ShadingModel.Set(lShadingName); //lMaterial->Shininess.Set(0.5); lMaterial->Specular.Set(lSpecularColor); lMaterial->SpecularFactor.Set(b.mat.power); node->AddMaterial(lMaterial); } if (b.mat_type != MaterialType::MaterialOnly) { FbxFileTexture* lTexture; // Set texture properties. lTexture = FbxFileTexture::Create(pScene, "Diffuse Texture"); lTexture->SetFileName((b.h.mask.name + texture_extension).c_str()); // Resource file is in current directory. lTexture->SetTextureUse(FbxTexture::eStandard); lTexture->SetMappingType(FbxTexture::eUV); lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial); lTexture->UVSet.Set(gDiffuseElementName); if (lMaterial) lMaterial->Diffuse.ConnectSrcObject(lTexture); // Set texture properties. lTexture = FbxFileTexture::Create(pScene, "Ambient Texture"); lTexture->SetFileName((b.h.mask.name + texture_extension).c_str()); // Resource file is in current directory. lTexture->SetTextureUse(FbxTexture::eStandard); lTexture->SetMappingType(FbxTexture::eUV); lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial); lTexture->UVSet.Set(gAmbientElementName); if (lMaterial) lMaterial->Ambient.ConnectSrcObject(lTexture); // Set texture properties. lTexture = FbxFileTexture::Create(pScene, "Specular Texture"); lTexture->SetFileName((b.h.spec.name + texture_extension).c_str()); // Resource file is in current directory. lTexture->SetTextureUse(FbxTexture::eStandard); lTexture->SetMappingType(FbxTexture::eUV); lTexture->SetMaterialUse(FbxFileTexture::eModelMaterial); lTexture->UVSet.Set(gSpecularElementName); if (lMaterial) lMaterial->Specular.ConnectSrcObject(lTexture); } // add smoothing groups FbxGeometryConverter lGeometryConverter(pSdkManager); lGeometryConverter.ComputeEdgeSmoothingFromNormals(m); //convert soft/hard edge info to smoothing group info lGeometryConverter.ComputePolygonSmoothingFromEdgeSmoothing(m); // scale //node->LclScaling.Set(FbxDouble3(scale_mult(), scale_mult(), scale_mult())); // pScene->GetRootNode()->AddChild(node); } return true; }