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/common | |
| download | blast-1.0.0-beta.tar.xz blast-1.0.0-beta.zip | |
first commitv1.0.0-beta
Diffstat (limited to 'NvBlast/tools/common')
| -rw-r--r-- | NvBlast/tools/common/BlastDataExporter.cpp | 106 | ||||
| -rw-r--r-- | NvBlast/tools/common/BlastDataExporter.h | 78 | ||||
| -rw-r--r-- | NvBlast/tools/common/FbxFileReader.cpp | 170 | ||||
| -rw-r--r-- | NvBlast/tools/common/FbxFileReader.h | 23 | ||||
| -rw-r--r-- | NvBlast/tools/common/FbxFileWriter.cpp | 645 | ||||
| -rw-r--r-- | NvBlast/tools/common/FbxFileWriter.h | 49 | ||||
| -rw-r--r-- | NvBlast/tools/common/FbxUtils.cpp | 30 | ||||
| -rw-r--r-- | NvBlast/tools/common/FbxUtils.h | 20 | ||||
| -rw-r--r-- | NvBlast/tools/common/IMeshFileReader.h | 21 | ||||
| -rw-r--r-- | NvBlast/tools/common/IMeshFileWriter.h | 41 | ||||
| -rw-r--r-- | NvBlast/tools/common/Log.cpp | 59 | ||||
| -rw-r--r-- | NvBlast/tools/common/Log.h | 122 | ||||
| -rw-r--r-- | NvBlast/tools/common/ObjFileReader.cpp | 63 | ||||
| -rw-r--r-- | NvBlast/tools/common/ObjFileReader.h | 16 | ||||
| -rw-r--r-- | NvBlast/tools/common/ObjFileWriter.cpp | 142 | ||||
| -rw-r--r-- | NvBlast/tools/common/ObjFileWriter.h | 26 | ||||
| -rw-r--r-- | NvBlast/tools/common/Utils.cpp | 166 | ||||
| -rw-r--r-- | NvBlast/tools/common/Utils.h | 113 |
18 files changed, 1890 insertions, 0 deletions
diff --git a/NvBlast/tools/common/BlastDataExporter.cpp b/NvBlast/tools/common/BlastDataExporter.cpp new file mode 100644 index 0000000..622901a --- /dev/null +++ b/NvBlast/tools/common/BlastDataExporter.cpp @@ -0,0 +1,106 @@ +#include <BlastDataExporter.h> +#include "NvBlastExtPxManager.h" +#include <NvBlastExtAuthoringCollisionBuilder.h> +#include <Log.h> +#include "PsFileBuffer.h" +#include "NvBlastExtPxAsset.h" +#include "NvBlast.h" +#include <NvBlastTkAsset.h> + +using namespace Nv::Blast; + + +ExtPxAsset* BlastDataExporter::createExtBlastAsset(std::vector<NvBlastBondDesc>& bondDescs, const std::vector<NvBlastChunkDesc>& chunkDescs, + std::vector<ExtPxAssetDesc::ChunkDesc>& physicsChunks) +{ + ExtPxAssetDesc descriptor; + descriptor.bondCount = static_cast<uint32_t>(bondDescs.size()); + descriptor.bondDescs = bondDescs.data(); + descriptor.chunkCount = static_cast<uint32_t>(chunkDescs.size()); + descriptor.chunkDescs = chunkDescs.data(); + descriptor.bondFlags = nullptr; + descriptor.pxChunks = physicsChunks.data(); + ExtPxAsset* asset = ExtPxAsset::create(descriptor, *mFramework); + return asset; +} + + + +NvBlastAsset* BlastDataExporter::createLlBlastAsset(std::vector<NvBlastBondDesc>& bondDescs, const std::vector<NvBlastChunkDesc>& chunkDescs) +{ + + NvBlastAssetDesc assetDesc; + assetDesc.bondCount = static_cast<uint32_t>(bondDescs.size()); + assetDesc.bondDescs = &bondDescs[0]; + + assetDesc.chunkCount = static_cast<uint32_t>(chunkDescs.size()); + assetDesc.chunkDescs = &chunkDescs[0]; + + + std::vector<uint8_t> scratch(static_cast<unsigned int>(NvBlastGetRequiredScratchForCreateAsset(&assetDesc, m_log))); + void* mem = _aligned_malloc(NvBlastGetAssetMemorySize(&assetDesc, m_log), 16); + NvBlastAsset* asset = NvBlastCreateAsset(mem, &assetDesc, &scratch[0], m_log); + return asset; +} + +TkAsset* BlastDataExporter::createTkBlastAsset(const std::vector<NvBlastBondDesc>& bondDescs, const std::vector<NvBlastChunkDesc>& chunkDescs) +{ + TkAssetDesc desc; + desc.bondCount = static_cast<uint32_t>(bondDescs.size()); + desc.bondDescs = bondDescs.data(); + desc.chunkCount = static_cast<uint32_t>(chunkDescs.size()); + desc.chunkDescs = chunkDescs.data(); + desc.bondFlags = nullptr; + TkAsset* asset = mFramework->createAsset(desc); + return asset; +}; + + +bool BlastDataExporter::saveBlastLLAsset(const std::string& outputFilePath, const NvBlastAsset* asset) +{ + uint32_t assetSize = NvBlastAssetGetSize(asset, m_log); + + physx::PsFileBuffer fileBuf(outputFilePath.c_str(), physx::PxFileBuf::OPEN_WRITE_ONLY); + if (!fileBuf.isOpen()) + { + NVBLAST_LOG_ERROR(m_log, "Can't open output buffer. \n"); + return false; + } + fileBuf.write(asset, sizeof(char) * assetSize); + fileBuf.close(); + return true; +} + +bool BlastDataExporter::saveBlastTkAsset(const std::string& outputFilePath, const TkAsset* asset) +{ + physx::PsFileBuffer fileBuf(outputFilePath.c_str(), physx::PxFileBuf::OPEN_WRITE_ONLY); + if (!fileBuf.isOpen()) + { + NVBLAST_LOG_ERROR(m_log, "Can't open output buffer. \n"); + return false; + } + if (!asset->serialize(fileBuf)) + { + NVBLAST_LOG_ERROR(m_log, "Serialization failed. \n"); + return false; + } + fileBuf.close(); + return true; +} + +bool BlastDataExporter::saveBlastExtAsset(const std::string& outputFilePath, const ExtPxAsset* asset) +{ + physx::PsFileBuffer fileBuf(outputFilePath.c_str(), physx::PxFileBuf::OPEN_WRITE_ONLY); + if (!fileBuf.isOpen()) + { + NVBLAST_LOG_ERROR(m_log, "Can't open output buffer. \n"); + return false; + } + if (!asset->serialize(fileBuf, *mCooking)) + { + NVBLAST_LOG_ERROR(m_log, "ExtPhysicsAsset serialization failed.\n"); + return false; + } + fileBuf.close(); + return true; +}
\ No newline at end of file diff --git a/NvBlast/tools/common/BlastDataExporter.h b/NvBlast/tools/common/BlastDataExporter.h new file mode 100644 index 0000000..3e44a7c --- /dev/null +++ b/NvBlast/tools/common/BlastDataExporter.h @@ -0,0 +1,78 @@ +#ifndef BLAST_DATA_EXPORTER +#define BLAST_DATA_EXPORTER + + +#include <NvBlastIndexFns.h> +#include <NvBlastExtAuthoringTypes.h> +#include <NvBlastExtPxAsset.h> +#include <vector> +#include <string> + +using namespace Nv::Blast; + +namespace physx +{ + class PxCooking; +} + + +struct NvBlastBondDesc; +struct NvBlastChunkDesc; + +struct NvBlastAsset; +namespace Nv +{ + namespace Blast + { + class TkAsset; + class ExtPxAsset; + } +} +/** + Tool for Blast asset creation and exporting +*/ +class BlastDataExporter +{ +public: + BlastDataExporter(TkFramework* framework, physx::PxCooking* cooking, NvBlastLog log) : mFramework(framework), mCooking(cooking), m_log(log) {}; + + /** + Creates ExtPxAsset + */ + ExtPxAsset* createExtBlastAsset(std::vector<NvBlastBondDesc>& bondDescs, const std::vector<NvBlastChunkDesc>& chunkDescs, + std::vector<ExtPxAssetDesc::ChunkDesc>& physicsChunks); + /** + Creates Low Level Blast asset + */ + NvBlastAsset* createLlBlastAsset(std::vector<NvBlastBondDesc>& bondDescs, const std::vector<NvBlastChunkDesc>& chunkDescs); + + /** + Creates Blast Toolkit Asset asset + */ + TkAsset* createTkBlastAsset(const std::vector<NvBlastBondDesc>& bondDescs, const std::vector<NvBlastChunkDesc>& chunkDescs); + + /* + Saves Blast LL asset to given path + */ + bool saveBlastLLAsset(const std::string& outputFilePath, const NvBlastAsset* asset); + + /* + Saves Blast Tk asset to given path + */ + bool saveBlastTkAsset(const std::string& outputFilePath, const TkAsset* asset); + + /* + Saves Blast BPXA asset to given path + */ + bool saveBlastExtAsset(const std::string& outputFilePath, const ExtPxAsset* asset); + +private: + TkFramework* mFramework; + physx::PxCooking* mCooking; + NvBlastLog m_log; +}; + + + + +#endif
\ No newline at end of file diff --git a/NvBlast/tools/common/FbxFileReader.cpp b/NvBlast/tools/common/FbxFileReader.cpp new file mode 100644 index 0000000..6b72223 --- /dev/null +++ b/NvBlast/tools/common/FbxFileReader.cpp @@ -0,0 +1,170 @@ +#include "FbxFileReader.h" +#include "fileio/fbxiosettings.h" +#include "fileio/fbxiosettingspath.h" +#include "core/base/fbxstringlist.h" +#include <iostream> +#include "scene/geometry/fbxmesh.h" + +#include "PxVec3.h" +#include "PxVec2.h" + +using physx::PxVec3; +using physx::PxVec2; +using Nv::Blast::Mesh; + + +FbxFileReader::FbxFileReader() +{ + setConvertToUE4(false); +} + +FbxAMatrix FbxFileReader::getTransformForNode(FbxNode* node) +{ + return node->EvaluateGlobalTransform(); +} + +std::shared_ptr<Nv::Blast::Mesh> FbxFileReader::loadFromFile(std::string filename) +{ + // Wrap in a shared ptr so that when it deallocates we get an auto destroy and all of the other assets created don't leak. + std::shared_ptr<FbxManager> sdkManager = std::shared_ptr<FbxManager>(FbxManager::Create(), [=](FbxManager* manager) + { + std::cout << "Deleting FbxManager" << std::endl; + manager->Destroy(); + }); + + FbxIOSettings* ios = FbxIOSettings::Create(sdkManager.get(), IOSROOT); + // Set some properties on the io settings + + sdkManager->SetIOSettings(ios); + + + FbxImporter* importer = FbxImporter::Create(sdkManager.get(), ""); + + bool importStatus = importer->Initialize(filename.c_str(), -1, sdkManager->GetIOSettings()); + + if (!importStatus) + { + std::cerr << "Call to FbxImporter::Initialize failed." << std::endl; + std::cerr << "Error returned: " << importer->GetStatus().GetErrorString() << std::endl; + + return nullptr; + } + + FbxScene* scene = FbxScene::Create(sdkManager.get(), "importScene"); + + importStatus = importer->Import(scene); + + if (!importStatus) + { + std::cerr << "Call to FbxImporter::Import failed." << std::endl; + std::cerr << "Error returned: " << importer->GetStatus().GetErrorString() << std::endl; + + return nullptr; + } + + if (getConvertToUE4()) + { + // Convert to UE4 + FbxAxisSystem::EFrontVector FrontVector = (FbxAxisSystem::EFrontVector) - FbxAxisSystem::eParityOdd; + const FbxAxisSystem UnrealZUp(FbxAxisSystem::eZAxis, FrontVector, FbxAxisSystem::eRightHanded); + + UnrealZUp.ConvertScene(scene); + } + + + // Recurse the fbx tree and find all meshes + std::vector<FbxNode*> meshNodes; + getFbxMeshes(scene->GetRootNode(), meshNodes); + + std::cout << "Found " << meshNodes.size() << " meshes." << std::endl; + + // Process just 0, because dumb. Fail out if more than 1? + + FbxNode* meshNode = meshNodes[0]; + FbxMesh* mesh = meshNode->GetMesh(); + + int polyCount = mesh->GetPolygonCount(); + + + bool bAllTriangles = true; + // Verify that the mesh is triangulated. + for (int i = 0; i < polyCount; i++) + { + if (mesh->GetPolygonSize(i) != 3) + { + bAllTriangles = false; + } + } + + if (!bAllTriangles) + { + std::cerr << "Mesh 0 has " << polyCount << " but not all polygons are triangles. Mesh must be triangulated." << std::endl; + return nullptr; + } + + FbxStringList uvSetNames; + + mesh->GetUVSetNames(uvSetNames); + + const char * uvSetName = uvSetNames.GetStringAt(0); + + std::vector<PxVec3> positions; + std::vector<PxVec3> normals; + std::vector<PxVec2> uv; + std::vector<uint32_t> indices; + + int* polyVertices = mesh->GetPolygonVertices(); + + uint32_t vertIndex = 0; + + FbxAMatrix trans = getTransformForNode(meshNode); + + for (int i = 0; i < polyCount; i++) + { + for (int vi = 0; vi < 3; vi++) + { + int polyCPIdx = polyVertices[i*3+vi]; + + FbxVector4 vert = mesh->GetControlPointAt(polyCPIdx); + FbxVector4 normVec; + FbxVector2 uvVec; + bool bUnmapped; + mesh->GetPolygonVertexNormal(i, vi, normVec); + mesh->GetPolygonVertexUV(i, vi, uvSetName, uvVec, bUnmapped); + + vert = trans.MultT(vert); + normVec = trans.MultT(normVec); + + positions.push_back(PxVec3((float) vert[0], (float)vert[1], (float)vert[2])); + normals.push_back(PxVec3((float)normVec[0], (float)normVec[1], (float)normVec[2])); + uv.push_back(PxVec2((float)uvVec[0], (float)uvVec[1])); + + indices.push_back(vertIndex++); + } + + } + + PxVec3* nr = (!normals.empty()) ? normals.data() : nullptr; + PxVec2* uvp = (!uv.empty()) ? uv.data() : nullptr; + + return std::make_shared<Mesh>(positions.data(), nr, uvp, static_cast<uint32_t>(positions.size()), indices.data(), static_cast<uint32_t>(indices.size())); +} + +void FbxFileReader::getFbxMeshes(FbxNode* node, std::vector<FbxNode*>& meshNodes) +{ + FbxMesh* mesh = node->GetMesh(); + + if (mesh != nullptr) + { + meshNodes.push_back(node); + } + + int childCount = node->GetChildCount(); + + for (int i = 0; i < childCount; i++) + { + FbxNode * childNode = node->GetChild(i); + + getFbxMeshes(childNode, meshNodes); + } +} diff --git a/NvBlast/tools/common/FbxFileReader.h b/NvBlast/tools/common/FbxFileReader.h new file mode 100644 index 0000000..ca288ad --- /dev/null +++ b/NvBlast/tools/common/FbxFileReader.h @@ -0,0 +1,23 @@ +#pragma once +#include "IMeshFileReader.h" +#include "fbxsdk.h" + +class FbxFileReader: public IMeshFileReader +{ +public: + FbxFileReader(); + ~FbxFileReader() = default; + + /* + Load from the specified file path, returning a mesh or nullptr if failed + */ + std::shared_ptr<Nv::Blast::Mesh> loadFromFile(std::string filename) override; + +private: + + // Should we convert the scene to UE4 coordinate system on load? + bool bConvertToUE4; + + FbxAMatrix getTransformForNode(FbxNode* node); + void getFbxMeshes(FbxNode* node, std::vector<FbxNode*>& meshNodes); +};
\ No newline at end of file diff --git a/NvBlast/tools/common/FbxFileWriter.cpp b/NvBlast/tools/common/FbxFileWriter.cpp new file mode 100644 index 0000000..a71896e --- /dev/null +++ b/NvBlast/tools/common/FbxFileWriter.cpp @@ -0,0 +1,645 @@ +#include "FbxFileWriter.h" +#include "fbxsdk.h" +#include "FbxUtils.h" +#include <iostream> +#include <sstream> +#include <iomanip> +#include "NvBlastTypes.h" +#include "NvBlastTkFramework.h" +#include "NvBlast.h" +#include "PxVec3.h" +#include "NvBlastAssert.h" +#include <unordered_set> +#include <functional> + + +FbxFileWriter::FbxFileWriter(): + bOutputFBXAscii(false) +{ + // Wrap in a shared ptr so that when it deallocates we get an auto destroy and all of the other assets created don't leak. + sdkManager = std::shared_ptr<FbxManager>(FbxManager::Create(), [=](FbxManager* manager) + { + std::cout << "Deleting FbxManager" << std::endl; + manager->Destroy(); + }); + + setConvertToUE4(false); +} + +/* + + Add the NvBlastAsset as a param + Walk the NvBlastChunk tree + -- Get the triangles for each chunk, however we do that + -- create a skin, clusters and bone node for each chunk, linked to their parent with the proper link mode + +*/ +bool FbxFileWriter::saveToFile(const NvBlastAsset* asset, std::vector<std::vector<Nv::Blast::Triangle>> chunksGeometry, std::string assetName, std::string outputPath) +{ + + FbxIOSettings* ios = FbxIOSettings::Create(sdkManager.get(), IOSROOT); + // Set some properties on the io settings + +// ios->SetBoolProp(EXP_ASCIIFBX, true); + + sdkManager->SetIOSettings(ios); + + sdkManager->GetIOSettings()->SetBoolProp(EXP_ASCIIFBX, bOutputFBXAscii); + + FbxScene* scene = FbxScene::Create(sdkManager.get(), "Export Scene"); + + if (getConvertToUE4()) + { + FbxAxisSystem::EFrontVector FrontVector = (FbxAxisSystem::EFrontVector) - FbxAxisSystem::eParityOdd; + const FbxAxisSystem UnrealZUp(FbxAxisSystem::eZAxis, FrontVector, FbxAxisSystem::eRightHanded); + + scene->GetGlobalSettings().SetAxisSystem(UnrealZUp); + } + + // Otherwise default to Maya defaults + + FbxMesh* mesh = FbxMesh::Create(sdkManager.get(), "meshgeo"); + + FbxGeometryElementNormal* geNormal = mesh->CreateElementNormal(); + geNormal->SetMappingMode(FbxGeometryElement::eByControlPoint); + geNormal->SetReferenceMode(FbxGeometryElement::eDirect); + + FbxGeometryElementUV* geUV = mesh->CreateElementUV("diffuseElement"); + geUV->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geUV->SetReferenceMode(FbxGeometryElement::eDirect); + + // Get the triangles count for all of the mesh parts + + size_t triangleCount = 0; + for (auto triangles : chunksGeometry) + { + triangleCount += triangles.size(); + } + + mesh->InitControlPoints((int)triangleCount * 3); + + FbxNode* meshNode = FbxNode::Create(scene, "meshnode"); + meshNode->SetNodeAttribute(mesh); + meshNode->SetShadingMode(FbxNode::eTextureShading); + + FbxNode* lRootNode = scene->GetRootNode(); + lRootNode->AddChild(meshNode); + + FbxSkin* skin = FbxSkin::Create(sdkManager.get(), "Skin of the thing"); + skin->SetGeometry(mesh); + + mesh->AddDeformer(skin); + + // Add a material otherwise UE4 freaks out on import + + FbxGeometryElementMaterial* matElement = mesh->CreateElementMaterial(); + matElement->SetMappingMode(FbxGeometryElement::eByPolygon); + matElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + FbxSurfacePhong* material = FbxSurfacePhong::Create(sdkManager.get(), "FirstExportMaterial"); + + material->Diffuse.Set(FbxDouble3(1.0, 1.0, 0)); + material->DiffuseFactor.Set(1.0); + + meshNode->AddMaterial(material); + + FbxSurfacePhong* material2 = FbxSurfacePhong::Create(sdkManager.get(), "SecondExportMaterial"); + + material2->Diffuse.Set(FbxDouble3(1.0, 0.0, 1.0)); + material2->DiffuseFactor.Set(1.0); + + meshNode->AddMaterial(material2); + + // Now walk the tree and create a skeleton with geometry at the same time + // Find a "root" chunk and walk the tree from there. + uint32_t chunkCount = NvBlastAssetGetChunkCount(asset, NvBlastTkFrameworkGet()->getLogFn()); + + auto chunks = NvBlastAssetGetChunks(asset, NvBlastTkFrameworkGet()->getLogFn()); + + currentDepth = 0; + uint32_t cpIdx = 0; + for (uint32_t i = 0; i < chunkCount; i++) + { + const NvBlastChunk* chunk = &chunks[i]; + + if (chunk->parentChunkIndex == UINT32_MAX) + { + uint32_t addedCps = createChunkRecursive(cpIdx, i, meshNode, lRootNode, skin, asset, chunksGeometry); + + cpIdx += addedCps; + } + } + + return finalizeFbxAndSave(scene, skin, outputPath, assetName);} + +/* + Recursive method that creates this chunk and all it's children. + + This creates a FbxNode with an FbxCluster, and all of the geometry for this chunk. + + Returns the number of added control points +*/ +uint32_t FbxFileWriter::createChunkRecursive(uint32_t currentCpIdx, uint32_t chunkIndex, FbxNode *meshNode, FbxNode* parentNode, FbxSkin* skin, const NvBlastAsset* asset, std::vector<std::vector<Nv::Blast::Triangle>> chunksGeometry) +{ + currentDepth++; + +// if (currentDepth >= 4) +// { +// return 0; +// } + + auto chunks = NvBlastAssetGetChunks(asset, NvBlastTkFrameworkGet()->getLogFn()); + const NvBlastChunk* chunk = &chunks[chunkIndex]; + auto triangles = chunksGeometry[chunkIndex]; + physx::PxVec3 centroid = physx::PxVec3(chunk->centroid[0], chunk->centroid[1], chunk->centroid[2]); + + std::ostringstream namestream; + + //mesh->InitTextureUV(triangles.size() * 3); + + std::ostringstream().swap(namestream); // Swap namestream with a default constructed ostringstream + namestream << "bone_" << chunkIndex; + std::string boneName = namestream.str(); + + FbxSkeleton* skelAttrib; + if (chunk->parentChunkIndex == UINT32_MAX) + { + skelAttrib = FbxSkeleton::Create(sdkManager.get(), "SkelRootAttrib"); + skelAttrib->SetSkeletonType(FbxSkeleton::eRoot); + + // Change the centroid to origin + centroid = physx::PxVec3(0.0f); + } + else + { + skelAttrib = FbxSkeleton::Create(sdkManager.get(), boneName.c_str()); + skelAttrib->SetSkeletonType(FbxSkeleton::eLimbNode); + } + + skelAttrib->Size.Set(1.0); // What's this for? + + + FbxNode* boneNode = FbxNode::Create(sdkManager.get(), boneName.c_str()); + boneNode->SetNodeAttribute(skelAttrib); + + auto mat = parentNode->EvaluateGlobalTransform().Inverse(); + + FbxVector4 vec(centroid.x, centroid.y, centroid.z, 0); + FbxVector4 c2 = mat.MultT(vec); + + boneNode->LclTranslation.Set(c2); + + parentNode->AddChild(boneNode); + + std::ostringstream().swap(namestream); // Swap namestream with a default constructed ostringstream + namestream << "cluster_" << std::setw(5) << std::setfill('0') << chunkIndex; + std::string clusterName = namestream.str(); + + FbxCluster* cluster = FbxCluster::Create(sdkManager.get(), clusterName.c_str()); + cluster->SetTransformMatrix(FbxAMatrix()); + cluster->SetLink(boneNode); + cluster->SetLinkMode(FbxCluster::eTotalOne); + + skin->AddCluster(cluster); + + FbxMesh* mesh = static_cast<FbxMesh*>(meshNode->GetNodeAttribute()); + + FbxVector4* controlPoints = mesh->GetControlPoints(); + auto geNormal = mesh->GetElementNormal(); + auto geUV = mesh->GetElementUV("diffuseElement"); + FbxGeometryElementMaterial* matElement = mesh->GetElementMaterial(); + + auto addVert = [&](Nv::Blast::Vertex vert, int controlPointIdx) + { + FbxVector4 vertex; + FbxVector4 normal; + FbxVector2 uv; + + FbxUtils::VertexToFbx(vert, vertex, normal, uv); + + controlPoints[controlPointIdx] = vertex; + geNormal->GetDirectArray().Add(normal); + geUV->GetDirectArray().Add(uv); + // Add this control point to the bone with weight 1.0 + cluster->AddControlPointIndex(controlPointIdx, 1.0); + }; + + uint32_t cpIdx = 0; + uint32_t polyCount = mesh->GetPolygonCount(); + for (auto tri : triangles) + { + addVert(tri.a, currentCpIdx + cpIdx + 0); + addVert(tri.b, currentCpIdx + cpIdx + 1); + addVert(tri.c, currentCpIdx + cpIdx + 2); + + mesh->BeginPolygon(); + mesh->AddPolygon(currentCpIdx + cpIdx + 0); + mesh->AddPolygon(currentCpIdx + cpIdx + 1); + mesh->AddPolygon(currentCpIdx + cpIdx + 2); + mesh->EndPolygon(); + if (tri.userInfo == 0) + { + matElement->GetIndexArray().SetAt(polyCount, 0); + } + else + { + matElement->GetIndexArray().SetAt(polyCount, 1); + } + polyCount++; + cpIdx += 3; + } + + mat = meshNode->EvaluateGlobalTransform(); + cluster->SetTransformMatrix(mat); + + mat = boneNode->EvaluateGlobalTransform(); + cluster->SetTransformLinkMatrix(mat); + + uint32_t addedCps = static_cast<uint32_t>(triangles.size() * 3); + + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) + { + addedCps += createChunkRecursive(currentCpIdx + addedCps, i, meshNode, boneNode, skin, asset, chunksGeometry); + } + + return addedCps; +} + + +uint32_t FbxFileWriter::createChunkRecursive(uint32_t currentCpIdx, uint32_t chunkIndex, FbxNode *meshNode, FbxNode* parentNode, FbxSkin* skin, const NvBlastAsset* asset, + const std::vector<std::vector<std::vector<int32_t> > >& posIndex, + const std::vector<std::vector<std::vector<int32_t> > >& normIndex, + const std::vector<std::vector<std::vector<int32_t> > >& texIndex) +{ + currentDepth++; + + // if (currentDepth >= 4) + // { + // return 0; + // } + + auto chunks = NvBlastAssetGetChunks(asset, NvBlastTkFrameworkGet()->getLogFn()); + const NvBlastChunk* chunk = &chunks[chunkIndex]; + physx::PxVec3 centroid = physx::PxVec3(chunk->centroid[0], chunk->centroid[1], chunk->centroid[2]); + + std::ostringstream namestream; + + //mesh->InitTextureUV(triangles.size() * 3); + + std::ostringstream().swap(namestream); // Swap namestream with a default constructed ostringstream + namestream << "bone_" << chunkIndex; + std::string boneName = namestream.str(); + + FbxSkeleton* skelAttrib; + if (chunk->parentChunkIndex == UINT32_MAX) + { + skelAttrib = FbxSkeleton::Create(sdkManager.get(), "SkelRootAttrib"); + skelAttrib->SetSkeletonType(FbxSkeleton::eRoot); + + // Change the centroid to origin + centroid = physx::PxVec3(0.0f); + } + else + { + skelAttrib = FbxSkeleton::Create(sdkManager.get(), boneName.c_str()); + skelAttrib->SetSkeletonType(FbxSkeleton::eLimbNode); + } + + skelAttrib->Size.Set(1.0); // What's this for? + + + FbxNode* boneNode = FbxNode::Create(sdkManager.get(), boneName.c_str()); + boneNode->SetNodeAttribute(skelAttrib); + + auto mat = parentNode->EvaluateGlobalTransform().Inverse(); + + FbxVector4 vec(centroid.x, centroid.y, centroid.z, 0); + FbxVector4 c2 = mat.MultT(vec); + + boneNode->LclTranslation.Set(c2); + + parentNode->AddChild(boneNode); + + std::ostringstream().swap(namestream); // Swap namestream with a default constructed ostringstream + namestream << "cluster_" << std::setw(5) << std::setfill('0') << chunkIndex; + std::string clusterName = namestream.str(); + + FbxCluster* cluster = FbxCluster::Create(sdkManager.get(), clusterName.c_str()); + cluster->SetTransformMatrix(FbxAMatrix()); + cluster->SetLink(boneNode); + cluster->SetLinkMode(FbxCluster::eTotalOne); + + skin->AddCluster(cluster); + + FbxMesh* mesh = static_cast<FbxMesh*>(meshNode->GetNodeAttribute()); + + auto geNormal = mesh->GetElementNormal(); + auto geUV = mesh->GetElementUV("diffuseElement"); + auto matr = mesh->GetElementMaterial(); + + + + auto posIndices = posIndex[chunkIndex]; + auto normIndices = normIndex[chunkIndex]; + auto uvIndices = texIndex[chunkIndex]; + uint32_t cPolygCount = mesh->GetPolygonCount(); + int32_t addedVertices = 0; + for (uint32_t subMesh = 0; subMesh < posIndices.size(); ++subMesh) + { + for (uint32_t tr = 0; tr < posIndices[subMesh].size(); tr += 3) + { + mesh->BeginPolygon(); + mesh->AddPolygon(posIndices[subMesh][tr + 0]); + mesh->AddPolygon(posIndices[subMesh][tr + 1]); + mesh->AddPolygon(posIndices[subMesh][tr + 2]); + mesh->EndPolygon(); + geNormal->GetIndexArray().SetAt(currentCpIdx + addedVertices, normIndices[subMesh][tr + 0]); + geNormal->GetIndexArray().SetAt(currentCpIdx + addedVertices + 1, normIndices[subMesh][tr + 1]); + geNormal->GetIndexArray().SetAt(currentCpIdx + addedVertices + 2, normIndices[subMesh][tr + 2]); + + geUV->GetIndexArray().SetAt(currentCpIdx + addedVertices, uvIndices[subMesh][tr + 0]); + geUV->GetIndexArray().SetAt(currentCpIdx + addedVertices + 1, uvIndices[subMesh][tr + 1]); + geUV->GetIndexArray().SetAt(currentCpIdx + addedVertices + 2, uvIndices[subMesh][tr + 2]); + if (subMesh == 0) + { + matr->GetIndexArray().SetAt(cPolygCount, 0); + } + else + { + matr->GetIndexArray().SetAt(cPolygCount, 1); + } + cPolygCount++; + addedVertices += 3; + + cluster->AddControlPointIndex(posIndices[subMesh][tr + 0], 1.0); + cluster->AddControlPointIndex(posIndices[subMesh][tr + 1], 1.0); + cluster->AddControlPointIndex(posIndices[subMesh][tr + 2], 1.0); + } + } + mat = meshNode->EvaluateGlobalTransform(); + cluster->SetTransformMatrix(mat); + + mat = boneNode->EvaluateGlobalTransform(); + cluster->SetTransformLinkMatrix(mat); + + + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) + { + addedVertices +=createChunkRecursive(addedVertices, i, meshNode, boneNode, skin, asset, posIndex, normIndex, texIndex); + } + + return addedVertices; + +} + +void FbxFileWriter::addControlPoints(FbxMesh* mesh, const std::vector<physx::PxVec3>& pos, std::vector<std::vector<std::vector<int32_t> > >& posIndex) +{ + std::vector<uint32_t> vertices; + std::cout << "Adding control points" << std::endl; + std::vector<int32_t> mapping(pos.size(), -1); + for (uint32_t ch = 0; ch < posIndex.size(); ++ch) + { + mapping.assign(pos.size(), -1); + for (uint32_t sb = 0; sb < posIndex[ch].size(); ++sb) + { + for (uint32_t pi = 0; pi < posIndex[ch][sb].size(); ++pi) + { + uint32_t p = posIndex[ch][sb][pi]; + if (mapping[p] == -1) + { + mapping[p] = (int)vertices.size(); + vertices.push_back(posIndex[ch][sb][pi]); + posIndex[ch][sb][pi] = mapping[p]; + } + else + { + posIndex[ch][sb][pi] = mapping[p]; + } + } + } + } + mesh->InitControlPoints((int)vertices.size()); + FbxVector4* controlPoints = mesh->GetControlPoints(); + for (auto v : vertices) + { + *controlPoints = FbxVector4(pos[v].x, pos[v].y, pos[v].z, 0); + ++controlPoints; + } + std::cout << "Adding control points: done" << std::endl; +} + +bool FbxFileWriter::finalizeFbxAndSave(FbxScene* scene, FbxSkin* skin, const std::string& outputPath, const std::string& name) +{ + // Store the bind pose + + std::unordered_set<FbxNode*> clusterNodes; + + std::function<void(FbxNode*)> addRecursively = [&](FbxNode* node) + { + if (node) + { + addRecursively(node->GetParent()); + + clusterNodes.insert(node); + } + }; + + for (uint32_t i = 0; i < (uint32_t)skin->GetClusterCount(); i++) + { + FbxNode* clusterNode = skin->GetCluster(i)->GetLink(); + + addRecursively(clusterNode); + } + + NVBLAST_ASSERT(clusterNodes.size() > 0); + + FbxPose* pose = FbxPose::Create(sdkManager.get(), "BasePose"); + pose->SetIsBindPose(true); + + for (auto node : clusterNodes) + { + FbxMatrix bindMat = node->EvaluateGlobalTransform(); + + pose->Add(node, bindMat); + } + + scene->AddPose(pose); + + FbxExporter* exporter = FbxExporter::Create(sdkManager.get(), "Scene Exporter"); + + auto path = outputPath + "\\" + name + ".fbx"; + + int lFormat; + + if (bOutputFBXAscii) + { + lFormat = sdkManager->GetIOPluginRegistry()->FindWriterIDByDescription("FBX ascii (*.fbx)"); + } + else + { + lFormat = sdkManager->GetIOPluginRegistry()->FindWriterIDByDescription("FBX binary (*.fbx)"); + } + + bool exportStatus = exporter->Initialize(path.c_str(), lFormat, sdkManager->GetIOSettings()); + + if (!exportStatus) + { + std::cerr << "Call to FbxExporter::Initialize failed" << std::endl; + std::cerr << "Error returned: " << exporter->GetStatus().GetErrorString() << std::endl; + return false; + } + + exportStatus = exporter->Export(scene); + + if (!exportStatus) + { + auto fbxStatus = exporter->GetStatus(); + + std::cerr << "Call to FbxExporter::Export failed" << std::endl; + std::cerr << "Error returned: " << fbxStatus.GetErrorString() << std::endl; + return false; + } + + return true; +} + +bool FbxFileWriter::saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, + const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& posIndexInp, + const std::vector<std::vector<std::vector<int32_t> > >& normIndex, + const std::vector<std::vector<std::vector<int32_t> > >& texIndex, + const std::vector<std::string>& texPathes, + const uint32_t submeshCount) +{ + NV_UNUSED(submeshCount); + NV_UNUSED(texPathes); + + auto posIndex = posIndexInp; + /** + Get polygon count + */ + uint32_t polygCount = 0; + for (uint32_t ch = 0; ch < posIndex.size(); ++ch) + { + for (uint32_t sbm = 0; sbm < posIndex[ch].size(); ++sbm) + { + polygCount += (uint32_t)posIndex[ch][sbm].size() / 3; + } + } + + FbxIOSettings* ios = FbxIOSettings::Create(sdkManager.get(), IOSROOT); + // Set some properties on the io settings + + // ios->SetBoolProp(EXP_ASCIIFBX, true); + + sdkManager->SetIOSettings(ios); + + sdkManager->GetIOSettings()->SetBoolProp(EXP_ASCIIFBX, bOutputFBXAscii); + + FbxScene* scene = FbxScene::Create(sdkManager.get(), "Export Scene"); + + if (getConvertToUE4()) + { + FbxAxisSystem::EFrontVector FrontVector = (FbxAxisSystem::EFrontVector) - FbxAxisSystem::eParityOdd; + const FbxAxisSystem UnrealZUp(FbxAxisSystem::eZAxis, FrontVector, FbxAxisSystem::eRightHanded); + + scene->GetGlobalSettings().SetAxisSystem(UnrealZUp); + } + + // Otherwise default to Maya defaults + + FbxMesh* mesh = FbxMesh::Create(sdkManager.get(), "meshgeo"); + + FbxGeometryElementNormal* geNormal = mesh->CreateElementNormal(); + geNormal->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geNormal->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + FbxGeometryElementUV* geUV = mesh->CreateElementUV("diffuseElement"); + geUV->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geUV->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + + FbxNode* meshNode = FbxNode::Create(scene, "meshnode"); + meshNode->SetNodeAttribute(mesh); + meshNode->SetShadingMode(FbxNode::eTextureShading); + + FbxNode* lRootNode = scene->GetRootNode(); + lRootNode->AddChild(meshNode); + + FbxSkin* skin = FbxSkin::Create(sdkManager.get(), "Skin of the thing"); + skin->SetGeometry(mesh); + + mesh->AddDeformer(skin); + + /** + Create control points, copy data to buffers + */ + addControlPoints(mesh, pos, posIndex); + + auto normalsElem = mesh->GetElementNormal(); + for (uint32_t i = 0; i < norm.size(); ++i) + { + normalsElem->GetDirectArray().Add(FbxVector4(norm[i].x, norm[i].y, norm[i].z, 0)); + } + auto uvsElem = mesh->GetElementUV("diffuseElement"); + for (uint32_t i = 0; i < uvs.size(); ++i) + { + uvsElem->GetDirectArray().Add(FbxVector2(uvs[i].x, uvs[i].y)); + } + + + // Add a material otherwise UE4 freaks out on import + + FbxGeometryElementMaterial* matElement = mesh->CreateElementMaterial(); + matElement->SetMappingMode(FbxGeometryElement::eByPolygon); + matElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + FbxSurfacePhong* material = FbxSurfacePhong::Create(sdkManager.get(), "FirstExportMaterial"); + + material->Diffuse.Set(FbxDouble3(1.0, 1.0, 0)); + material->DiffuseFactor.Set(1.0); + + meshNode->AddMaterial(material); + + FbxSurfacePhong* material2 = FbxSurfacePhong::Create(sdkManager.get(), "SecondExportMaterial"); + + material2->Diffuse.Set(FbxDouble3(1.0, 0.0, 1.0)); + material2->DiffuseFactor.Set(1.0); + + meshNode->AddMaterial(material2); + + + matElement->GetIndexArray().SetCount(polygCount); + normalsElem->GetIndexArray().SetCount(polygCount * 3); + uvsElem->GetIndexArray().SetCount(polygCount * 3); + + + std::cout << "Create chunks recursive" << std::endl; + + // Now walk the tree and create a skeleton with geometry at the same time + // Find a "root" chunk and walk the tree from there. + uint32_t chunkCount = NvBlastAssetGetChunkCount(asset, NvBlastTkFrameworkGet()->getLogFn()); + auto chunks = NvBlastAssetGetChunks(asset, NvBlastTkFrameworkGet()->getLogFn()); + currentDepth = 0; + uint32_t cpIdx = 0; + for (uint32_t i = 0; i < chunkCount; i++) + { + const NvBlastChunk* chunk = &chunks[i]; + + if (chunk->parentChunkIndex == UINT32_MAX) + { + uint32_t addedCps = createChunkRecursive(cpIdx, i, meshNode, lRootNode, skin, asset, posIndex, normIndex, texIndex); + cpIdx += addedCps; + } + } + + return finalizeFbxAndSave(scene, skin, outputPath, name); +} + +bool FbxFileWriter::saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& indices) +{ + std::vector<std::string> matnames; + matnames.push_back(""); + return saveToFile(asset, name, outputPath, pos, norm, uvs, indices, indices, indices, matnames, 1); +}
\ No newline at end of file diff --git a/NvBlast/tools/common/FbxFileWriter.h b/NvBlast/tools/common/FbxFileWriter.h new file mode 100644 index 0000000..646d1ec --- /dev/null +++ b/NvBlast/tools/common/FbxFileWriter.h @@ -0,0 +1,49 @@ +#pragma once +#include "IMeshFileWriter.h" +#include "fbxsdk.h" +#include <memory> + +struct NvBlastAsset; + +class FbxFileWriter : public IMeshFileWriter +{ +public: + + FbxFileWriter(); + ~FbxFileWriter() = default; + + virtual bool saveToFile(const NvBlastAsset* asset, std::vector<std::vector<Nv::Blast::Triangle>> chunksGeometry, std::string assetName, std::string outputPath) override; + + + + virtual bool saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, + const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& posIndex, + const std::vector<std::vector<std::vector<int32_t> > >& normIndex, + const std::vector<std::vector<std::vector<int32_t> > >& texIndex, + const std::vector<std::string>& texPathes, + const uint32_t submeshCount) override; + + virtual bool saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& indices) override; + + bool bOutputFBXAscii; + +private: + + uint32_t currentDepth; + + std::shared_ptr<FbxManager> sdkManager; + + uint32_t createChunkRecursive(uint32_t currentCpIdx, uint32_t chunkIndex, FbxNode *meshNode, FbxNode* parentNode, FbxSkin* skin, const NvBlastAsset* asset, std::vector<std::vector<Nv::Blast::Triangle>> chunksGeometry); + + uint32_t createChunkRecursive(uint32_t currentCpIdx, uint32_t chunkIndex, FbxNode *meshNode, FbxNode* parentNode, FbxSkin* skin, const NvBlastAsset* asset, + const std::vector<std::vector<std::vector<int32_t> > >& posIndex, + const std::vector<std::vector<std::vector<int32_t> > >& normIndex, + const std::vector<std::vector<std::vector<int32_t> > >& texIndex); + + void addControlPoints(FbxMesh* mesh, const std::vector<physx::PxVec3>& pos, std::vector<std::vector<std::vector<int32_t> > >& posIndex); + + bool finalizeFbxAndSave(FbxScene* scene, FbxSkin* skin, const std::string& outputPath, const std::string& name); + +}; diff --git a/NvBlast/tools/common/FbxUtils.cpp b/NvBlast/tools/common/FbxUtils.cpp new file mode 100644 index 0000000..b0bd94b --- /dev/null +++ b/NvBlast/tools/common/FbxUtils.cpp @@ -0,0 +1,30 @@ +#include "fbxsdk.h" +#include "FbxUtils.h" +#include "PxVec3.h" +#include "PxVec2.h" +#include "NvBlastExtAuthoringTypes.h" + +using physx::PxVec3; +using physx::PxVec2; + + +void FbxUtils::VertexToFbx(Nv::Blast::Vertex& vert, FbxVector4& outVertex, FbxVector4& outNormal, FbxVector2& outUV) +{ + PxVec3ToFbx(vert.p, outVertex); + PxVec3ToFbx(vert.n, outNormal); + PxVec2ToFbx(vert.uv[0], outUV); +} + +void FbxUtils::PxVec3ToFbx(physx::PxVec3& inVector, FbxVector4& outVector) +{ + outVector[0] = inVector.x; + outVector[1] = inVector.y; + outVector[2] = inVector.z; + outVector[3] = 0; +} + +void FbxUtils::PxVec2ToFbx(physx::PxVec2& inVector, FbxVector2& outVector) +{ + outVector[0] = inVector.x; + outVector[1] = inVector.y; +} diff --git a/NvBlast/tools/common/FbxUtils.h b/NvBlast/tools/common/FbxUtils.h new file mode 100644 index 0000000..902af21 --- /dev/null +++ b/NvBlast/tools/common/FbxUtils.h @@ -0,0 +1,20 @@ +#pragma once +#include "PxVec3.h" +#include "PxVec2.h" + +namespace Nv +{ + namespace Blast + { + struct Vertex; + } +} + +class FbxUtils +{ +public: + static void VertexToFbx(Nv::Blast::Vertex& vert, FbxVector4& outVertex, FbxVector4& outNormal, FbxVector2& outUV); + + static void PxVec3ToFbx(physx::PxVec3& inVector, FbxVector4& outVector); + static void PxVec2ToFbx(physx::PxVec2& inVector, FbxVector2& outVector); +}; diff --git a/NvBlast/tools/common/IMeshFileReader.h b/NvBlast/tools/common/IMeshFileReader.h new file mode 100644 index 0000000..d632a2f --- /dev/null +++ b/NvBlast/tools/common/IMeshFileReader.h @@ -0,0 +1,21 @@ +#pragma once +#include <memory> +#include <string> +#include "NvBlastExtAuthoringMesh.h" + + +class IMeshFileReader +{ +public: + + /* + Load from the specified file path, returning a mesh or nullptr if failed + */ + virtual std::shared_ptr<Nv::Blast::Mesh> loadFromFile(std::string filename) = 0; + + virtual bool getConvertToUE4() { return bConvertToUE4; } + virtual void setConvertToUE4(bool bConvert) { bConvertToUE4 = bConvert; } + +private: + bool bConvertToUE4; +};
\ No newline at end of file diff --git a/NvBlast/tools/common/IMeshFileWriter.h b/NvBlast/tools/common/IMeshFileWriter.h new file mode 100644 index 0000000..562df70 --- /dev/null +++ b/NvBlast/tools/common/IMeshFileWriter.h @@ -0,0 +1,41 @@ +#pragma once +#include "NvBlastExtAuthoringTypes.h" +#include <string> +#include <memory> +#include <vector> + +struct NvBlastAsset; + +class IMeshFileWriter +{ +public: + + /** + Input mesh geometry as triangle array + */ + virtual bool saveToFile(const NvBlastAsset* asset, std::vector<std::vector<Nv::Blast::Triangle>> chunksGeometry, std::string assetName, std::string outputPath) = 0; + + + /** + Input mesh geometry as vertex buffers with separate indices for positions, normals and uvs. Is used for compressed output to .obj file. + */ + virtual bool saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, + const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& posIndex, + const std::vector<std::vector<std::vector<int32_t> > >& normIndex, + const std::vector<std::vector<std::vector<int32_t> > >& texIndex, + const std::vector<std::string>& texPathes, + const uint32_t submeshCount) = 0; + + + /** + Input mesh geometry as vertex buffers and single index array. + */ + virtual bool saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& indices) = 0; + + virtual bool getConvertToUE4() { return bConvertToUE4; } + virtual void setConvertToUE4(bool bConvert) { bConvertToUE4 = bConvert; } +private: + bool bConvertToUE4; +}; diff --git a/NvBlast/tools/common/Log.cpp b/NvBlast/tools/common/Log.cpp new file mode 100644 index 0000000..4371a7d --- /dev/null +++ b/NvBlast/tools/common/Log.cpp @@ -0,0 +1,59 @@ +#include "Log.h" + +#include "PsString.h" + +#include <iomanip> +#include <stdarg.h> +#include <stdio.h> + +/////////////////////////////////////////////////////////////////////////// + +namespace Nv +{ +namespace Blast +{ + +void fLogf(const char* format, ...) +{ + char buf[4096], *p = buf; + va_list args; + int n; + + va_start(args, format); + //n = _vsnprintf(p, sizeof buf - 3, format, args); + n = vsprintf_s(p, sizeof(buf)-3, format, args); + va_end(args); + + p += (n < 0) ? sizeof buf - 3 : n; + + while (p > buf && isspace((unsigned char)p[-1])) + { + *--p = '\0'; + } + + *p++ = '\r'; + *p++ = '\n'; + *p = '\0'; + + fLog(buf, Log::TYPE_INFO); +} + + +////////////////////////////////////////////////////////////////////////////// + +void Log::flushDeferredMessages() +{ + if (mDeferredMessages.size() == 0) return; + + std::cout << std::endl; + for (std::vector<std::string>::iterator it = mDeferredMessages.begin(); it != mDeferredMessages.end(); ++it) + { + log(*it, mMinVerbosity); + } + mDeferredMessages.clear(); +} + + + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/tools/common/Log.h b/NvBlast/tools/common/Log.h new file mode 100644 index 0000000..528a3b9 --- /dev/null +++ b/NvBlast/tools/common/Log.h @@ -0,0 +1,122 @@ +#ifndef LOG_H +#define LOG_H + +#include "Utils.h" +#include "PxVec3.h" + +#include <sstream> + +namespace Nv +{ +namespace Blast +{ + + +////////////////////////////////////////////////////////////////////////////// + +void fLogf(const char* format, ...); + +class Log : public Singleton<Log> +{ + friend class Singleton<Log>; + +public: + + enum MessageType { + TYPE_INFO = 0, + TYPE_WARNING, + TYPE_ERROR, + TYPE_DEFERRED, + + NUM_TYPES, + MOST_VERBOSE = TYPE_INFO, + LEAST_VERBOSE = TYPE_ERROR +#if defined(_DEBUG) + , DEFAULT_VERBOSITY = MOST_VERBOSE +#else + , DEFAULT_VERBOSITY = LEAST_VERBOSE +#endif + }; + typedef MessageType Verbosity; + + /////////////////////////////////////////////////////////////////////////// + + template<typename T> + Log& log(const T& value, MessageType messageType); + + void flushDeferredMessages(); + + /////////////////////////////////////////////////////////////////////////// + + void setCurrentVerbosity(Verbosity verbosity) { mCurrentVerbosity = verbosity; } + Verbosity getCurrentVerbosity() const { return mCurrentVerbosity; } + + // Messages types below this level will be ignored + void setMinVerbosity(Verbosity verbosity) { mMinVerbosity = verbosity; } + Verbosity getMinVerbosity() const { return mMinVerbosity; } + + /////////////////////////////////////////////////////////////////////////// + +protected: + Log(MessageType verbosity = DEFAULT_VERBOSITY) + : mCurrentVerbosity(LEAST_VERBOSE), + mMinVerbosity(verbosity) { } + +private: + Verbosity mCurrentVerbosity; + Verbosity mMinVerbosity; + std::vector<std::string> mDeferredMessages; +}; + +/////////////////////////////////////////////////////////////////////////// + +PX_INLINE std::ostream& operator<< (std::ostream& stream, const physx::PxVec3& vec) +{ + return stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")"; +} + +template<typename T> +Log& Log::log(const T& value, Log::MessageType messageType) +{ + if (TYPE_DEFERRED == messageType) + { + std::stringstream ss; + ss << value; + mDeferredMessages.push_back(ss.str()); + } + else if(mMinVerbosity <= messageType) + { + std::cout << value; + } + return *this; +} + +PX_INLINE Log& lout() { return Log::instance(); } + +template <typename T> +PX_INLINE void fLog(const T& value, Log::MessageType messageType = Log::TYPE_INFO) +{ + lout().log<T>(value, messageType); +} +template <typename T> +PX_INLINE Log& operator<<(Log& logger, const T& value) +{ + return logger.log<T>(value, logger.getCurrentVerbosity()); +} +PX_INLINE Log& operator<<(Log& logger, Log::MessageType verbosity) +{ + logger.setCurrentVerbosity(verbosity); + return logger; +} +typedef std::ostream& (*ostream_manipulator)(std::ostream&); +PX_INLINE Log& operator<<(Log& logger, ostream_manipulator pf) +{ + return operator<< <ostream_manipulator> (logger, pf); +} + + +} // namespace Blast +} // namespace Nv + + +#endif diff --git a/NvBlast/tools/common/ObjFileReader.cpp b/NvBlast/tools/common/ObjFileReader.cpp new file mode 100644 index 0000000..30b4c5c --- /dev/null +++ b/NvBlast/tools/common/ObjFileReader.cpp @@ -0,0 +1,63 @@ +#include "ObjFileReader.h" + +#pragma warning(push) +#pragma warning(disable:4706) +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" +#pragma warning(pop) + + +#include <iostream> +#include "PxVec3.h" +#include "PxVec2.h" + +using physx::PxVec3; +using physx::PxVec2; +using Nv::Blast::Mesh; + + +ObjFileReader::ObjFileReader() +{ + setConvertToUE4(false); +} + +std::shared_ptr<Nv::Blast::Mesh> ObjFileReader::loadFromFile(std::string filename) +{ + std::vector<tinyobj::shape_t> shapes; + std::vector<tinyobj::material_t> mats; + std::string err; + std::string mtlPath; + bool ret = tinyobj::LoadObj(shapes, mats, err, filename.c_str()); + // can't load? + if (!ret) + return nullptr; + + if (shapes.size() > 1) + { + std::cout << "Can load only one object per mesh" << std::endl; + } + + std::vector<PxVec3> positions; + std::vector<PxVec3> normals; + std::vector<PxVec2> uv; + + auto& psVec = shapes[0].mesh.positions; + for (uint32_t i = 0; i < psVec.size() / 3; ++i) + { + positions.push_back(PxVec3(psVec[i * 3], psVec[i * 3 + 1], psVec[i * 3 + 2])); + } + auto& nmVec = shapes[0].mesh.normals; + for (uint32_t i = 0; i < nmVec.size() / 3; ++i) + { + normals.push_back(PxVec3(nmVec[i * 3], nmVec[i * 3 + 1], nmVec[i * 3 + 2])); + } + auto& txVec = shapes[0].mesh.texcoords; + for (uint32_t i = 0; i < txVec.size() / 2; ++i) + { + uv.push_back(PxVec2(txVec[i * 2], txVec[i * 2 + 1])); + } + PxVec3* nr = (!normals.empty()) ? normals.data() : 0; + PxVec2* uvp = (!uv.empty()) ? uv.data() : 0; + + return std::make_shared<Mesh>(positions.data(), nr, uvp, static_cast<uint32_t>(positions.size()), shapes[0].mesh.indices.data(), static_cast<uint32_t>(shapes[0].mesh.indices.size())); +} diff --git a/NvBlast/tools/common/ObjFileReader.h b/NvBlast/tools/common/ObjFileReader.h new file mode 100644 index 0000000..acc85ba --- /dev/null +++ b/NvBlast/tools/common/ObjFileReader.h @@ -0,0 +1,16 @@ +#pragma once +#include <memory> +#include "IMeshFileReader.h" + +class ObjFileReader: public IMeshFileReader +{ +public: + ObjFileReader(); + ~ObjFileReader() = default; + + /* + Load from the specified file path, returning a mesh or nullptr if failed + */ + std::shared_ptr<Nv::Blast::Mesh> loadFromFile(std::string filename); + +}; diff --git a/NvBlast/tools/common/ObjFileWriter.cpp b/NvBlast/tools/common/ObjFileWriter.cpp new file mode 100644 index 0000000..94aad7d --- /dev/null +++ b/NvBlast/tools/common/ObjFileWriter.cpp @@ -0,0 +1,142 @@ +#include "ObjFileWriter.h" +#include <PxVec3.h> +#include <sstream> + +using namespace physx; +using namespace Nv::Blast; + +bool ObjFileWriter::saveToFile(const NvBlastAsset* asset, std::vector<std::vector<Nv::Blast::Triangle>> chunksGeometry, std::string assetName, std::string outputPath) +{ + NV_UNUSED(asset); + + std::vector<PxVec3> pos; + std::vector<PxVec3> norm; + std::vector<PxVec2> tex; + for (uint32_t vc = 0; vc < chunksGeometry.size(); ++vc) + { + std::vector<Triangle>& chunk = chunksGeometry[vc]; + for (uint32_t i = 0; i < chunk.size(); ++i) + { + pos.push_back(chunk[i].a.p); + pos.push_back(chunk[i].b.p); + pos.push_back(chunk[i].c.p); + + norm.push_back(chunk[i].a.n); + norm.push_back(chunk[i].b.n); + norm.push_back(chunk[i].c.n); + + tex.push_back(chunk[i].a.uv[0]); + tex.push_back(chunk[i].b.uv[0]); + tex.push_back(chunk[i].c.uv[0]); + } + } + std::vector < std::vector<std::vector<int32_t> > > indices(chunksGeometry.size()); + int32_t index = 0; + for (uint32_t vc = 0; vc < chunksGeometry.size(); ++vc) + { + indices[vc].push_back(std::vector<int32_t>()); + for (uint32_t i = 0; i < chunksGeometry[vc].size() * 3; ++i) + { + indices[vc][0].push_back(index); + index++; + } + } + + return saveToFile(asset, assetName, outputPath, pos, norm, tex, indices); +} + +bool ObjFileWriter::saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, + const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& posIndex, + const std::vector<std::vector<std::vector<int32_t> > >& normIndex, + const std::vector<std::vector<std::vector<int32_t> > >& texIndex, + const std::vector<std::string>& texPathes, + const uint32_t submeshCount) +{ + NV_UNUSED(asset); + + uint32_t chunkCount = static_cast<uint32_t>(posIndex.size()); + if (posIndex.size() != normIndex.size() || normIndex.size() != texIndex.size()) + { + return false; + } + + // export materials (mtl file) + { + std::ostringstream mtlFilePath; + mtlFilePath << outputPath << "\\" << name << ".mtl"; + FILE* f = fopen(mtlFilePath.str().c_str(), "w"); + if (!f) + return false; + + for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex) + { + fprintf(f, "newmtl mat%d\n", submeshIndex); + fprintf(f, "\tmap_Kd %s\n", texPathes[submeshIndex].data()); + fprintf(f, "\n"); + } + + fclose(f); + } + + /// Export geometry to *.obj file + { + std::ostringstream objFilePath; + objFilePath << outputPath << "\\" << name << ".obj"; + FILE* f = fopen(objFilePath.str().c_str(), "w"); + if (!f) + return false; + + fprintf(f, "mtllib %s.mtl\n", name.c_str()); + fprintf(f, "o frac \n"); + + + /// Write compressed vertices + for (uint32_t i = 0; i < pos.size(); ++i) + { + fprintf(f, "v %.4f %.4f %.4f\n", pos[i].x, pos[i].y, pos[i].z); + } + for (uint32_t i = 0; i < norm.size(); ++i) + { + fprintf(f, "vn %.4f %.4f %.4f\n", norm[i].x, norm[i].y, norm[i].z); + } + for (uint32_t i = 0; i < uvs.size(); ++i) + { + fprintf(f, "vt %.4f %.4f\n", uvs[i].y, uvs[i].x); + } + + for (uint32_t chunkIndex = 0; chunkIndex < chunkCount; ++chunkIndex) + { + for (uint32_t submeshIndex = 0; submeshIndex < posIndex[chunkIndex].size(); ++submeshIndex) + { + fprintf(f, "g %d_%d \n", chunkIndex, submeshIndex); + fprintf(f, "usemtl mat%d\n", submeshIndex); + uint32_t indexCount = static_cast<uint32_t>(posIndex[chunkIndex][submeshIndex].size()); + const std::vector<int32_t>& pI = posIndex[chunkIndex][submeshIndex]; + const std::vector<int32_t>& nI = normIndex[chunkIndex][submeshIndex]; + const std::vector<int32_t>& tI = texIndex[chunkIndex][submeshIndex]; + + for (uint32_t i = 0; i < indexCount;) + { + fprintf(f, "f %d/%d/%d ", pI[i] + 1, tI[i] + 1, nI[i] + 1); + ++i; + fprintf(f, "%d/%d/%d ", pI[i] + 1, tI[i] + 1, nI[i] + 1); + ++i; + fprintf(f, "%d/%d/%d\n", pI[i] + 1, tI[i] + 1, nI[i] + 1); + ++i; + } + } + } + fclose(f); + } + return true; + +} + +bool ObjFileWriter::saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& indices) +{ + std::vector<std::string> matnames; + matnames.push_back(""); + return saveToFile(asset, name, outputPath, pos, norm, uvs, indices, indices, indices, matnames, 1); +}
\ No newline at end of file diff --git a/NvBlast/tools/common/ObjFileWriter.h b/NvBlast/tools/common/ObjFileWriter.h new file mode 100644 index 0000000..547ba62 --- /dev/null +++ b/NvBlast/tools/common/ObjFileWriter.h @@ -0,0 +1,26 @@ +#pragma once +#include "IMeshFileWriter.h" +#include <memory> + +struct NvBlastAsset; + +class ObjFileWriter : public IMeshFileWriter +{ +public: + + ObjFileWriter() {}; + ~ObjFileWriter() = default; + + virtual bool saveToFile(const NvBlastAsset* asset, std::vector<std::vector<Nv::Blast::Triangle>> chunksGeometry, std::string assetName, std::string outputPath) override; + + virtual bool saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, + const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& posIndex, + const std::vector<std::vector<std::vector<int32_t> > >& normIndex, + const std::vector<std::vector<std::vector<int32_t> > >& texIndex, + const std::vector<std::string>& texPathes, + const uint32_t submeshCount) override; + + virtual bool saveToFile(const NvBlastAsset* asset, const std::string& name, const std::string& outputPath, const std::vector<physx::PxVec3>& pos, const std::vector<physx::PxVec3>& norm, const std::vector<physx::PxVec2>& uvs, + const std::vector<std::vector<std::vector<int32_t> > >& indices) override; +}; diff --git a/NvBlast/tools/common/Utils.cpp b/NvBlast/tools/common/Utils.cpp new file mode 100644 index 0000000..736159e --- /dev/null +++ b/NvBlast/tools/common/Utils.cpp @@ -0,0 +1,166 @@ +#include "Utils.h" + +#include "Log.h" + +#include <string.h> + +#if PX_WINDOWS_FAMILY +#include <direct.h> +#define getCwd _getcwd +#else +#include <unistd.h> +#define getCwd getcwd +#endif + +/////////////////////////////////////////////////////////////////////////// + +namespace Nv +{ +namespace Blast +{ + +////////////////////////////////////////////////////////////////////////////// + +static void addSlashToPath(std::string& path) +{ + if (path[path.length() - 1] != '/' && path[path.length() - 1] != '\\') + { + path.append("/"); + } +} + +FileUtils::FileUtils() +{ + mSearchPaths.push_back(""); + char currentPathTemp[FILENAME_MAX]; + if (getCwd(currentPathTemp, sizeof(currentPathTemp))) + { + std::string currentPath(currentPathTemp); + addSlashToPath(currentPath); + addAbsolutePath(currentPath); + mCurrentPath = currentPath; + } +} + +std::string FileUtils::getDirectory(const std::string& filePath) +{ + return filePath.substr(0, filePath.find_last_of("/\\") + 1); +} + +std::string FileUtils::getFilename(const std::string& filePath, bool bWithExtension) +{ + size_t p0 = filePath.find_last_of("/\\") + 1; + if (bWithExtension) + { + return filePath.substr(p0); + } + else + { + return filePath.substr(p0, filePath.find_last_of(".") - p0); + } +} + +std::string FileUtils::getFileExtension(const std::string& filePath) +{ + std::string filename = getFilename(filePath); + size_t p0 = filename.find_last_of("."); + if (p0 != std::string::npos) + return filePath.substr(p0);// + 1); + return ""; +} + +void FileUtils::addAbsolutePath(const std::string& path) +{ + if (path.empty()) + { + return; + } + + std::string newPath = path; + addSlashToPath(newPath); + + mSearchPaths.push_back(newPath); +} + +void FileUtils::addRelativePath(const std::string& relPath) +{ + addAbsolutePath(mCurrentPath + relPath); +} + +void FileUtils::clearPaths() +{ + mSearchPaths.clear(); +} + +FILE* FileUtils::findFile(const std::string& path, bool bVerbose) +{ + FILE* file; + if (find(path, &file, NULL, bVerbose)) + { + return file; + } + else + { + return NULL; + } +} + +std::string FileUtils::findPath(const std::string& path, bool bVerbose) +{ + std::string fullPath; + if (find(path, NULL, &fullPath, bVerbose)) + { + return fullPath; + } + else + { + return path; + } +} + +bool FileUtils::find(const std::string& path, FILE** ppFile, std::string* pFullPath, bool bVerbose) +{ + if (mSearchPaths.empty() || path.empty()) + { + if (bVerbose) + { + lout() << Log::TYPE_ERROR << "Error: Invalid search path configuration."; + } + return false; + } + + std::string fullPath; + + FILE* file = NULL; + const uint32_t numSearchPaths = (uint32_t)mSearchPaths.size(); + for (uint32_t i = 0; i < numSearchPaths; ++i) + { + fullPath = mSearchPaths[i] + path; + fopen_s(&file, fullPath.c_str(), "rb"); + if (file) + { + break; + } + } + + if (!file) + { + if (bVerbose) + lout() << Log::TYPE_ERROR << std::endl << "Error: Unable to find file " << path << std::endl; + return false; + } + + if (ppFile) + *ppFile = file; + else + fclose(file); + + if (pFullPath) + *pFullPath = fullPath; + + return true; +} + + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/tools/common/Utils.h b/NvBlast/tools/common/Utils.h new file mode 100644 index 0000000..26299e8 --- /dev/null +++ b/NvBlast/tools/common/Utils.h @@ -0,0 +1,113 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "PsString.h" + +#include <string> +#include <iostream> +#include <vector> +#include <map> + +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// + +namespace Nv +{ +namespace Blast +{ + +/////////////////////////////////////////////////////////////////////////// + +template<class T> +PX_INLINE bool isNull(const T* p) { return nullptr == p; } + +template<class Releasable, class Releaser> class ScopedResource; +template<class Releasable, class Releaser> +PX_INLINE bool isNull(const ScopedResource<Releasable,Releaser>& p) { return !p; } + +PX_INLINE bool isNullString(const char* pString) +{ + return (nullptr == pString || pString[0] == '\0' || physx::shdfnd::strcmp(pString, "null") == 0); +} + +template<class T> +PX_INLINE bool isValid(const T& p) { return !isNull(p.get()); } + +PX_INLINE bool isValidString(const char* pString) { return !isNullString(pString); } + +/////////////////////////////////////////////////////////////////////////// + +// Note: This is not a thread safe singleton class +template <class T> +class Singleton +{ + // The fact that I cannot declare T a friend directly is rather absurd... + typedef T Type; + friend typename Singleton<T>::Type; + + ////////////////////////////////////////////////////////////////////////////// + +public: + static T& instance() + { + static T _instance; + return _instance; + } + + ////////////////////////////////////////////////////////////////////////////// + +private: + Singleton() { } + ~Singleton() { }; + Singleton(const Singleton&); + Singleton& operator=(const Singleton&); +}; + +////////////////////////////////////////////////////////////////////////////// + +class FileUtils : public Singleton<FileUtils> +{ + friend class Singleton<FileUtils>; + +public: + void addAbsolutePath(const std::string&); + void addRelativePath(const std::string&); + void clearPaths(); + + ////////////////////////////////////////////////////////////////////////////// + + FILE* findFile(const std::string&, bool bVerbose = true); + std::string findPath(const std::string&, bool bVerbose = true); + bool find(const std::string&, FILE**, std::string*, bool bVerbose = true); + + ////////////////////////////////////////////////////////////////////////////// + + const std::string& getCurrentPath() const + { + return mCurrentPath; + } + + ////////////////////////////////////////////////////////////////////////////// + + static std::string getDirectory(const std::string&); + static std::string getFilename(const std::string&, bool bWithExtension = true); + static std::string getFileExtension(const std::string&); + + ////////////////////////////////////////////////////////////////////////////// + +protected: + FileUtils(); + + ////////////////////////////////////////////////////////////////////////////// + + std::string mCurrentPath; + std::vector<std::string> mSearchPaths; +}; + + +} // namespace Blast +} // namespace Nv + + +#endif |