diff options
| author | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
|---|---|---|
| committer | Bryan Galdrikian <[email protected]> | 2017-02-21 12:07:59 -0800 |
| commit | 446ce137c6823ba9eff273bdafdaf266287c7c98 (patch) | |
| tree | d20aab3e2ed08d7b3ca71c2f40db6a93ea00c459 /NvBlast/tools/ApexImporter/src | |
| download | blast-1.0.0-beta.tar.xz blast-1.0.0-beta.zip | |
first commitv1.0.0-beta
Diffstat (limited to 'NvBlast/tools/ApexImporter/src')
3 files changed, 659 insertions, 0 deletions
diff --git a/NvBlast/tools/ApexImporter/src/ApexDestructibleObjExporter.cpp b/NvBlast/tools/ApexImporter/src/ApexDestructibleObjExporter.cpp new file mode 100644 index 0000000..0c90ac1 --- /dev/null +++ b/NvBlast/tools/ApexImporter/src/ApexDestructibleObjExporter.cpp @@ -0,0 +1,314 @@ +#include "ApexDestructibleObjExporter.h" + +#include "Log.h" + +#include "PsFastXml.h" +#include "PsFileBuffer.h" +#include "PxInputDataFromPxFileBuf.h" +#include <PxVec2.h> +#include <PxVec3.h> +#include <map> +#include <FbxFileWriter.h> +#include <ObjFileWriter.h> + + +using namespace nvidia; +using namespace Nv::Blast; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Material Parser +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const float VEC_EPS = 1e-4; + +class MaterialXmlParser : public physx::shdfnd::FastXml::Callback +{ +public: + std::string textureFile; + +protected: + // encountered a comment in the XML + virtual bool processComment(const char* /*comment*/) + { + return true; + } + + virtual bool processClose(const char* /*element*/, unsigned int /*depth*/, bool& /*isError*/) + { + return true; + } + + // return true to continue processing the XML document, false to skip. + virtual bool processElement(const char* elementName, // name of the element + const char* elementData, // element data, null if none + const physx::shdfnd::FastXml::AttributePairs& attr, + int /*lineno*/) // line number in the source XML file + { + PX_UNUSED(attr); + if (::strcmp(elementName, "sampler2D") == 0) + { + int nameIndex = -1; + for (int i = 0; i < attr.getNbAttr(); i += 2) + { + if (::strcmp(attr.getKey(i), "name") == 0) + { + nameIndex = i; + break; + } + } + + if (::strcmp(attr.getValue(nameIndex), "diffuseTexture") == 0) + { + textureFile = elementData; + } + } + + return true; + } +}; + +std::string getTextureFromMaterial(const char* materialPath) +{ + PsFileBuffer fileBuffer(materialPath, general_PxIOStream2::PxFileBuf::OPEN_READ_ONLY); + PxInputDataFromPxFileBuf inputData(fileBuffer); + MaterialXmlParser parser; + physx::shdfnd::FastXml* xml = physx::shdfnd::createFastXml(&parser); + xml->processXml(inputData, false); + + xml->release(); + + // trim folders + std::string textureFile = parser.textureFile.substr(parser.textureFile.find_last_of("/\\") + 1); + + return textureFile; +} + + +bool ApexDestructibleGeometryExporter::exportToFile(NvBlastAsset* asset, const DestructibleAsset& apexAsset, const std::string& name, const std::vector<uint32_t>& chunkReorderInvMap, bool toFbx, bool toObj, bool fbxascii, bool toUe4) +{ + const RenderMeshAsset* rAsset = apexAsset.getRenderMeshAsset(); + uint32_t submeshCount = rAsset->getSubmeshCount(); + std::vector<std::string> materialPathes; + // gather materials + { + for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex) + { + const char* materialName = rAsset->getMaterialName(submeshIndex); + std::ostringstream materialPath; + materialPath << m_materialsDir << "\\" << materialName; + std::string texturePath = getTextureFromMaterial(materialPath.str().c_str()); + materialPathes.push_back(texturePath); + } + } + struct vc3Comp + { + bool operator()(const PxVec3& a, const PxVec3& b) const + { + if (a.x + VEC_EPS < b.x) return true; + if (a.x - VEC_EPS > b.x) return false; + if (a.y + VEC_EPS < b.y) return true; + if (a.y - VEC_EPS > b.y) return false; + if (a.z + VEC_EPS < b.z) return true; + return false; + } + }; + struct vc2Comp + { + bool operator()(const PxVec2& a, const PxVec2& b) const + { + if (a.x + VEC_EPS < b.x) return true; + if (a.x - VEC_EPS > b.x) return false; + if (a.y + VEC_EPS < b.y) return true; + return false; + } + }; + + std::vector<PxVec3> compressedPositions; + std::vector<PxVec3> compressedNormals; + std::vector<PxVec2> compressedTextures; + + std::vector<uint32_t> positionsMapping; + std::vector<uint32_t> normalsMapping; + std::vector<uint32_t> texturesMapping; + + std::map<PxVec3, uint32_t, vc3Comp> posMap; + std::map<PxVec3, uint32_t, vc3Comp> normMap; + std::map<PxVec2, uint32_t, vc2Comp> texMap; + + + // gather data for export + { + for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex) + { + const RenderSubmesh& currentSubmesh = rAsset->getSubmesh(submeshIndex); + uint32_t indexCount = currentSubmesh.getVertexBuffer().getVertexCount(); + const VertexFormat& fmt = currentSubmesh.getVertexBuffer().getFormat(); + // Find position buffer index + uint32_t bufferId = 0; + { + for (; bufferId < fmt.getBufferCount(); ++bufferId) + { + if (fmt.getBufferSemantic(bufferId) != RenderVertexSemantic::POSITION) + continue; + else + break; + } + if (bufferId == fmt.getBufferCount()) + { + lout() << "Can't find positions buffer" << std::endl; + return false; + } + } + const PxVec3* posistions = reinterpret_cast<const PxVec3*>(currentSubmesh.getVertexBuffer().getBuffer(bufferId)); + uint32_t oldSize = (uint32_t)positionsMapping.size(); + positionsMapping.resize(oldSize + indexCount); + for (uint32_t i = 0; i < indexCount; ++i) + { + auto it = posMap.find(posistions[i]); + if (it == posMap.end()) + { + posMap[posistions[i]] = (uint32_t)compressedPositions.size(); + positionsMapping[oldSize + i] = (uint32_t)compressedPositions.size(); + compressedPositions.push_back(posistions[i]); + } + else + { + positionsMapping[oldSize + i] = it->second; + } + } + } + + for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex) + { + const RenderSubmesh& currentSubmesh = rAsset->getSubmesh(submeshIndex); + uint32_t indexCount = currentSubmesh.getVertexBuffer().getVertexCount(); + const VertexFormat& fmt = currentSubmesh.getVertexBuffer().getFormat(); + // Find normal buffer index + uint32_t bufferId = 0; + { + for (; bufferId < fmt.getBufferCount(); ++bufferId) + { + if (fmt.getBufferSemantic(bufferId) != RenderVertexSemantic::NORMAL) + continue; + else + break; + } + if (bufferId == fmt.getBufferCount()) + { + lout() << "Can't find positions buffer" << std::endl; + return false; + } + } + const PxVec3* normals = reinterpret_cast<const PxVec3*>(currentSubmesh.getVertexBuffer().getBuffer(bufferId)); + uint32_t oldSize = (uint32_t)normalsMapping.size(); + normalsMapping.resize(oldSize + indexCount); + for (uint32_t i = 0; i < indexCount; ++i) + { + auto it = normMap.find(normals[i]); + if (it == normMap.end()) + { + normMap[normals[i]] = (uint32_t)compressedNormals.size(); + normalsMapping[oldSize + i] = (uint32_t)compressedNormals.size(); + compressedNormals.push_back(normals[i]); + } + else + { + normalsMapping[oldSize + i] = it->second; + } + } + + } + for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex) + { + const RenderSubmesh& currentSubmesh = rAsset->getSubmesh(submeshIndex); + uint32_t indexCount = currentSubmesh.getVertexBuffer().getVertexCount(); + const VertexFormat& fmt = currentSubmesh.getVertexBuffer().getFormat(); + + // Find texture coords buffer index + uint32_t bufferId = 0; + { + for (; bufferId < fmt.getBufferCount(); ++bufferId) + { + if (fmt.getBufferSemantic(bufferId) != RenderVertexSemantic::TEXCOORD0) + continue; + else + break; + } + if (bufferId == fmt.getBufferCount()) + { + lout() << "Can't find positions buffer" << std::endl; + return false; + } + } + const PxVec2* texCoord = reinterpret_cast<const PxVec2*>(currentSubmesh.getVertexBuffer().getBuffer(bufferId)); + uint32_t oldSize = (uint32_t)texturesMapping.size(); + texturesMapping.resize(oldSize + indexCount); + for (uint32_t i = 0; i < indexCount; ++i) + { + auto it = texMap.find(texCoord[i]); + if (it == texMap.end()) + { + texMap[texCoord[i]] = (uint32_t)compressedTextures.size(); + texturesMapping[oldSize + i] = (uint32_t)compressedTextures.size(); + compressedTextures.push_back(texCoord[i]); + } + else + { + texturesMapping[oldSize + i] = it->second; + } + } + + } + + uint32_t apexChunkCount = apexAsset.getChunkCount(); + uint32_t chunkCount = static_cast<uint32_t>(chunkReorderInvMap.size()); + + std::vector<std::vector<std::vector<int32_t> > > pInd(chunkCount); + std::vector<std::vector<std::vector<int32_t> > > tInd(chunkCount); + std::vector<std::vector<std::vector<int32_t> > > nInd(chunkCount); + + + for (uint32_t chunkIndex = 0; chunkIndex < chunkCount; ++chunkIndex) + { + uint32_t apexChunkIndex = chunkReorderInvMap[chunkIndex]; + if (apexChunkIndex >= apexChunkCount) + { + continue; + } + uint32_t part = apexAsset.getPartIndex(apexChunkIndex); + uint32_t offset = 0; + for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex) + { + const RenderSubmesh& currentSubmesh = rAsset->getSubmesh(submeshIndex); + const uint32_t* indexArray = currentSubmesh.getIndexBuffer(part); + uint32_t indexCount = currentSubmesh.getIndexCount(part); + pInd[chunkIndex].push_back(std::vector<int32_t>()); + tInd[chunkIndex].push_back(std::vector<int32_t>()); + nInd[chunkIndex].push_back(std::vector<int32_t>()); + + for (uint32_t i = 0; i < indexCount;++i) + { + pInd[chunkIndex].back().push_back(positionsMapping[indexArray[i] + offset]); + tInd[chunkIndex].back().push_back(texturesMapping[indexArray[i] + offset]); + nInd[chunkIndex].back().push_back(normalsMapping[indexArray[i] + offset]); + } + offset += currentSubmesh.getVertexBuffer().getVertexCount(); + } + } + + if (toObj) + { + ObjFileWriter writer; + writer.saveToFile(asset, name, m_exportDir, compressedPositions, compressedNormals, compressedTextures, pInd, nInd, tInd, materialPathes, submeshCount); + } + if (toFbx) + { + FbxFileWriter writer; + writer.bOutputFBXAscii = fbxascii; + writer.setConvertToUE4(toUe4); + writer.saveToFile(asset, name, m_exportDir, compressedPositions, compressedNormals, compressedTextures, pInd, nInd, tInd, materialPathes, submeshCount); + } + } + + return true; +} diff --git a/NvBlast/tools/ApexImporter/src/ApexDestructibleObjExporter.h b/NvBlast/tools/ApexImporter/src/ApexDestructibleObjExporter.h new file mode 100644 index 0000000..cd6f238 --- /dev/null +++ b/NvBlast/tools/ApexImporter/src/ApexDestructibleObjExporter.h @@ -0,0 +1,47 @@ +#ifndef OBJ_APEX_EXPORTER_H +#define OBJ_APEX_EXPORTER_H + +#include <string> +#include <vector> +#include "DestructibleAsset.h" + + + + +namespace physx +{ + class PxVec3; + class PxVec2; +} + +struct NvBlastAsset; + +namespace Nv +{ +namespace Blast +{ + +class ApexDestructibleGeometryExporter +{ +public: + ApexDestructibleGeometryExporter(std::string materialsDir, std::string exportDir) : m_materialsDir(materialsDir), m_exportDir(exportDir) + {} + + bool exportToFile(NvBlastAsset* asset, const nvidia::apex::DestructibleAsset& apexAsset, const std::string& name, const std::vector<uint32_t>& chunkReorderInvMap, bool toFbx, bool toObj, bool fbxascii, bool toUe4); + +private: + ApexDestructibleGeometryExporter& operator=(const ApexDestructibleGeometryExporter&) + { + return *this; + } + const std::string m_materialsDir; + const std::string m_exportDir; + +}; + + +} // namespace Blast +} // namespace Nv + + +#endif //OBJ_EXPORTER_H diff --git a/NvBlast/tools/ApexImporter/src/Main.cpp b/NvBlast/tools/ApexImporter/src/Main.cpp new file mode 100644 index 0000000..157e923 --- /dev/null +++ b/NvBlast/tools/ApexImporter/src/Main.cpp @@ -0,0 +1,298 @@ + +#if NV_VC +#pragma warning(push) +#pragma warning(disable: 4996) // 'fopen' unsafe warning, from NxFileBuffer.h +#endif + +#include "PxPhysicsAPI.h" +#include "Apex.h" +#include <ModuleDestructible.h> +#include <DestructibleAsset.h> +#include "NullRenderer.h" +#include "NvBlastExtApexImportTool.h" +#include "Log.h" +#include <string> +#include <iostream> +#include "tclap/CmdLine.h" +#include "ApexDestructibleObjExporter.h" +#include "NvBlastTkFramework.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlastExtPxManager.h" +#include <BlastDataExporter.h> +#include "windows.h" +#include <NvBlastTkAsset.h> +#define DEFAULT_INPUT_FILE "../../../tools/ApexImporter/resources/assets/table.apb" +#define DEFAULT_OUTPUT_DIR "C:/TestFracturer/" +#define DEFAULT_ASSET_NAME "table" + +using namespace Nv::Blast; +using namespace Nv::Blast::ApexImporter; + +void loggingCallback(int type, const char* msg, const char* file, int line) +{ + if (type == NvBlastMessage::Info) + lout() << Log::TYPE_INFO << msg << " FILE:" << file << " Line: " << line << "\n"; + else + lout() << Log::TYPE_ERROR << msg << " FILE:" << file << " Line: " << line << "\n"; +} + +bool isDirectoryExist(std::string path) +{ + DWORD attributes = GetFileAttributesA(path.c_str()); + if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) + { + return true; + } + return false; +} + +bool mkDirRecursively(std::string path) +{ + if (isDirectoryExist(path)) + { + return true; + } + auto indx = path.find_first_of("\\/"); + while (indx != std::string::npos) + { + std::string subfolder = path.substr(0, indx); + CreateDirectory(subfolder.c_str(), NULL); + indx = path.find_first_of("\\/", indx + 1); + } + return isDirectoryExist(path); +} + +// Create an Asset from the APEX destructible asset +void run(const std::string& inFilepath, const std::string& outDir, const std::string& assetName, uint32_t mode, bool llFlag, bool tkFlag, bool extPxFlag, bool obj, bool fbx, bool fbxascii, bool ue4) +{ + std::string inputDir = inFilepath.substr(0, inFilepath.find_last_of("/\\")); + + // load APEX + ApexImportTool blast(loggingCallback); + + lout() << Log::TYPE_INFO << "ApexImportTool initialization" << std::endl; + blast.initialize(); + if (!blast.isValid()) + { + lout() << Log::TYPE_ERROR << "Failed to create BlastSDK" << std::endl; + return; + } + + // load asset + lout() << Log::TYPE_INFO << "Loading asset: " << inFilepath << std::endl; + physx::PxFileBuf* apexAssetStream = nvidia::apex::GetApexSDK()->createStream(inFilepath.c_str(), physx::PxFileBuf::OPEN_READ_ONLY); + nvidia::apex::DestructibleAsset* apexAsset = blast.loadAssetFromFile(apexAssetStream); + if (!apexAsset) + { + return; + } + apexAssetStream->release(); + + ApexImporterConfig config; + config.infSearchMode = static_cast<ApexImporterConfig::InterfaceSearchMode>(mode); + + std::vector<uint32_t> chunkReorderInvMap; + TkFrameworkDesc frameworkDesc = + { + nvidia::apex::GetApexSDK()->getErrorCallback(), + nvidia::apex::GetApexSDK()->getAllocator() + }; + TkFramework* framework = NvBlastTkFrameworkCreate(frameworkDesc); + if (framework == nullptr) + { + lout() << Log::TYPE_ERROR << "Failed to create TkFramework" << std::endl; + return; + } + + std::vector<NvBlastChunkDesc> chunkDesc; + std::vector<NvBlastBondDesc> bondDescs; + std::vector<uint32_t> flags; + + std::vector<ExtPxAssetDesc::ChunkDesc> physicsChunks; + std::vector<ExtPxAssetDesc::SubchunkDesc> physicsSubchunks; + + bool result = blast.importApexAsset(chunkReorderInvMap, apexAsset, chunkDesc, bondDescs, flags, config); + if (!result) + { + lout() << Log::TYPE_ERROR << "Failed to build Blast asset data" << std::endl; + return; + }; + + result = blast.getCollisionGeometry(apexAsset, static_cast<uint32_t>(chunkDesc.size()), chunkReorderInvMap, flags, physicsChunks, physicsSubchunks); + if (!result) + { + lout() << Log::TYPE_ERROR << "Failed to build physics data" << std::endl; + return; + }; + + + // save asset + lout() << Log::TYPE_INFO << "Saving blast asset: " << outDir << std::endl; + BlastDataExporter blExpr(framework, nvidia::apex::GetApexSDK()->getCookingInterface(), loggingCallback); + NvBlastAsset* llAsset = blExpr.createLlBlastAsset(bondDescs, chunkDesc); + + std::cout <<"Chunk count: " << NvBlastAssetGetChunkCount(llAsset, NULL) << std::endl; + + if (llAsset == nullptr && (llFlag || fbx)) + { + lout() << Log::TYPE_ERROR << "Failed to build low-level asset" << std::endl; + return; + } + + if (llFlag) + { + std::ostringstream outBlastFilePathStream; + outBlastFilePathStream << outDir << "/" << assetName << ".llasset"; + std::string outBlastFilePath = outBlastFilePathStream.str(); + blExpr.saveBlastLLAsset(outBlastFilePath, llAsset); + std::cout << "Wrote NvBlastAsset to " << outBlastFilePath << std::endl; + } + if (tkFlag) + { + TkAsset* tkAsset = blExpr.createTkBlastAsset(bondDescs, chunkDesc); + std::ostringstream outBlastFilePathStream; + outBlastFilePathStream << outDir << "/" << assetName << ".tkasset"; + std::string outBlastFilePath = outBlastFilePathStream.str(); + blExpr.saveBlastTkAsset(outBlastFilePath, tkAsset); + std::cout << "Wrote TkAsset to " << outBlastFilePath << std::endl; + tkAsset->release(); + } + if (extPxFlag) + { + ExtPxAsset* pxAsset = blExpr.createExtBlastAsset(bondDescs, chunkDesc, physicsChunks); + std::ostringstream outBlastFilePathStream; + outBlastFilePathStream << outDir << "/" << assetName << ".bpxa"; + std::string outBlastFilePath = outBlastFilePathStream.str(); + blExpr.saveBlastExtAsset(outBlastFilePath, pxAsset); + std::cout << "Wrote ExtPxAsset to " << outBlastFilePath << std::endl; + pxAsset->release(); + } + + lout() << Log::TYPE_INFO << "Saving model file: " << outDir << std::endl; + ApexDestructibleGeometryExporter objSaver(inputDir, outDir); + objSaver.exportToFile(llAsset, *apexAsset, assetName, chunkReorderInvMap, fbx, obj, fbxascii, ue4); + _aligned_free(llAsset); +} + + +int main(int argc, const char* const* argv) +{ + try + { + // setup cmd line + TCLAP::CmdLine cmd("Blast SDK: APEX Importer", ' ', "0.1"); + + TCLAP::ValueArg<std::string> infileArg("f", "file", "File to load", true, DEFAULT_INPUT_FILE, "infile"); + cmd.add(infileArg); + + TCLAP::ValueArg<std::string> outDirArg("o", "outputDir", "Output directory", false, DEFAULT_OUTPUT_DIR, "output directory"); + cmd.add(outDirArg); + + TCLAP::ValueArg<std::string> outAssetName("n", "outAssetName", "Output asset name", true, DEFAULT_ASSET_NAME, "output asset name"); + cmd.add(outAssetName); + + + TCLAP::ValueArg<uint32_t> interfaceSearchMode("m", "mode", "Interface search mode", false, 0, "0 - EXACT, 1 - FORCED, for detailed description see docs."); + cmd.add(interfaceSearchMode); + + TCLAP::SwitchArg debugSwitch("d", "debug", "Print debug output", cmd, false); + + TCLAP::SwitchArg bpxaOutputArg("", "bpxa", "Output ExtPxAsset to the output directory (ext: bpxa)", false); + cmd.add(bpxaOutputArg); + + TCLAP::SwitchArg tkOutputArg("", "tk", "Output TkAsset to the output directory (ext: tkasset)", false); + cmd.add(tkOutputArg); + + TCLAP::SwitchArg llOutputArg("", "ll", "Output LL Blast asset to the output directory (ext: llasset)", false); + cmd.add(llOutputArg); + + TCLAP::SwitchArg ue4OutputArg("", "ue4", "Output FBX with UE4 coordinate system", false); + cmd.add(ue4OutputArg); + + TCLAP::SwitchArg fbxAsciiArg("", "fbxascii", "Output FBX as an ascii file (defaults to binary output)", false); + cmd.add(fbxAsciiArg); + + TCLAP::SwitchArg objOutputArg("", "obj", "Output a OBJ mesh to the output directory", false); + cmd.add(objOutputArg); + + TCLAP::SwitchArg fbxOutputArg("", "fbx", "Output a FBX mesh to the output directory", false); + cmd.add(fbxOutputArg); + + + // parse cmd input + cmd.parse(argc, argv); + + bool bOutputBPXA = bpxaOutputArg.getValue(); + bool bOutputTK = tkOutputArg.getValue(); + bool bOutputLL = llOutputArg.getValue(); + + bool bUE4CoordSystem = ue4OutputArg.isSet(); + bool bOutputFBXAscii = fbxAsciiArg.isSet(); + + bool bOutputObjFile = objOutputArg.isSet(); + bool bOutputFbxFile = fbxOutputArg.isSet(); + + + // Did we specify no output formats? + if (!bpxaOutputArg.isSet() && !tkOutputArg.isSet() && !llOutputArg.isSet()) + { + std::cout << "Didn't specify an output format on the command line, so defaulting to outputting BPXA" << std::endl; + bOutputBPXA = true; + } + // Did we specify no geometry output formats? + if (!bOutputObjFile && !bOutputFbxFile) + { + std::cout << "Didn't specify an output geometry format on the command line, so defaulting to outputting .OBJ" << std::endl; + bOutputObjFile = true; + } + + // get cmd parse results + std::string infile = infileArg.getValue(); + std::string outDir; + std::string assetName = outAssetName.getValue(); + + /** + Set output dir, make sure that it exist. + */ + if (outDirArg.isSet()) + { + outDir = outDirArg.getValue(); + std::string temp = outDir + '/'; + if (!isDirectoryExist(outDir.data())) + { + std::cout << "Output directory doesn't exist. It will be created." << std::endl; + if (!mkDirRecursively(temp.data())) + { + std::cout << "Directory creation failed!" << std::endl; + return -1; + } + } + } + else + { + auto idx = infile.find_last_of("/\\"); + if (idx == 0 || idx == std::string::npos) + { + outDir = "."; + } + else + { + outDir = infile.substr(0, idx); + } + } + + bool debug = debugSwitch.getValue(); + int mode = interfaceSearchMode.getValue(); + // do stuff + if (debug) + lout().setMinVerbosity(Log::MOST_VERBOSE); + + run(infile, outDir, assetName, mode, bOutputLL, bOutputTK, bOutputBPXA, bOutputObjFile, bOutputFbxFile, bOutputFBXAscii, bUE4CoordSystem); + } + catch (TCLAP::ArgException &e) // catch any exceptions + { + lout() << Log::TYPE_ERROR << "error: " << e.error() << " for arg " << e.argId() << std::endl; + } + + return 0; +} |