diff options
| author | Anton Novoselov <[email protected]> | 2017-08-01 12:53:38 +0300 |
|---|---|---|
| committer | Anton Novoselov <[email protected]> | 2017-08-01 12:53:38 +0300 |
| commit | 236f03c0b9a4982328ed1201978f7f69d192d9b2 (patch) | |
| tree | e486f2fa39dba203563895541e92c60ed3e25759 /sdk/extensions/exporter/source | |
| parent | Added screens to welcome page (diff) | |
| download | blast-236f03c0b9a4982328ed1201978f7f69d192d9b2.tar.xz blast-236f03c0b9a4982328ed1201978f7f69d192d9b2.zip | |
Blast 1.1 release (windows / linux)
see docs/release_notes.txt for details
Diffstat (limited to 'sdk/extensions/exporter/source')
12 files changed, 2815 insertions, 0 deletions
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporter.cpp b/sdk/extensions/exporter/source/NvBlastExtExporter.cpp new file mode 100644 index 0000000..d74a1ee --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporter.cpp @@ -0,0 +1,58 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#include "NvBlastExtExporter.h" +#include "NvBlastExtExporterFbxReader.h" +#include "NvBlastExtExporterObjReader.h" +#include "NvBlastExtExporterFbxWriter.h" +#include "NvBlastExtExporterObjWriter.h" + +using namespace Nv::Blast; + +IMeshFileReader* NvBlastExtExporterCreateObjFileReader() +{ + return new ObjFileReader; +} + +IFbxFileReader* NvBlastExtExporterCreateFbxFileReader() +{ + return new FbxFileReader; +} + +IMeshFileWriter* NvBlastExtExporterCreateObjFileWriter() +{ + return new ObjFileWriter; +} + +IMeshFileWriter* NvBlastExtExporterCreateFbxFileWriter(bool outputFBXAscii) +{ + auto ret = new FbxFileWriter; + ret->bOutputFBXAscii = outputFBXAscii; + return ret; +} + diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.cpp new file mode 100644 index 0000000..df0500e --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.cpp @@ -0,0 +1,505 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + +#include "NvBlastExtExporterFbxReader.h" +#include "NvBlastExtExporterFbxUtils.h" +#include "NvBlastGlobals.h" +#include "fileio/fbxiosettings.h" +#include "fileio/fbxiosettingspath.h" +#include "core/base/fbxstringlist.h" +#include <iostream> +#include <algorithm> +#include <cctype> +#include <sstream> +#include "scene/geometry/fbxmesh.h" + +#include "PxVec3.h" +#include "PxVec2.h" +#include "NvBlastExtAuthoringMesh.h" +#include "NvBlastExtAuthoringBondGenerator.h" +#include "NvBlastExtAuthoringCollisionBuilder.h" + +using physx::PxVec3; +using physx::PxVec2; + +using namespace Nv::Blast; + +FbxFileReader::FbxFileReader() +{ + mBoneCount = 0; +} + +void FbxFileReader::release() +{ + delete this; +} + +FbxAMatrix FbxFileReader::getTransformForNode(FbxNode* node) +{ + //The geometry transform contains the information about the pivots in the mesh node relative to the node's transform + FbxAMatrix geometryTransform(node->GetGeometricTranslation(FbxNode::eSourcePivot), + node->GetGeometricRotation(FbxNode::eSourcePivot), + node->GetGeometricScaling(FbxNode::eSourcePivot)); + FbxAMatrix nodeTransform = node->EvaluateGlobalTransform(); + + return nodeTransform * geometryTransform; +} + +void FbxFileReader::loadFromFile(const char* 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(); + }); + + mBoneCount = 0; + mCollisionNodes.clear(); + 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, -1, sdkManager->GetIOSettings()); + + if (!importStatus) + { + std::cerr << "Call to FbxImporter::Initialize failed." << std::endl; + std::cerr << "Error returned: " << importer->GetStatus().GetErrorString() << std::endl; + + return; + } + + 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; + } + + int32_t matCount = scene->GetMaterialCount(); + + for (int32_t i = 0; i < matCount; ++i) + { + mMaterialNames.push_back(std::string(scene->GetMaterial(i)->GetName())); + } + + //This removes axis and unit conversion nodes so it converts the entire scene to the header specified axis and units + FbxRootNodeUtility::RemoveAllFbxRoots(scene); + + FbxAxisSystem blastAxisSystem = FbxUtils::getBlastFBXAxisSystem(); + FbxAxisSystem sourceSetup = scene->GetGlobalSettings().GetAxisSystem(); + if (sourceSetup != blastAxisSystem) + { + std::cout << "Converting to Blast coordinates" << std::endl; + std::cout << "Existing axis: " << FbxUtils::FbxAxisSystemToString(sourceSetup) << std::endl; + blastAxisSystem.ConvertScene(scene); + } + + FbxSystemUnit blastUnits = FbxUtils::getBlastFBXUnit(); + FbxSystemUnit sourceUnits = scene->GetGlobalSettings().GetSystemUnit(); + if (sourceUnits != blastUnits) + { + std::cout << "Converting to Blast units" << std::endl; + std::cout << "Existing units: " << FbxUtils::FbxSystemUnitToString(sourceUnits) << std::endl; + blastUnits.ConvertScene(scene); + } + + FbxDisplayLayer* collisionDisplayLayer = scene->FindMember<FbxDisplayLayer>(FbxUtils::getCollisionGeometryLayerName().c_str()); + + // Recurse the fbx tree and find all meshes + std::vector<FbxNode*> meshNodes; + getFbxMeshes(collisionDisplayLayer, scene->GetRootNode(), meshNodes); + + if (isCollisionLoaded()) + { + std::cout << "Collision geometry is found."; + getCollisionInternal(); + } + + std::cout << "Found " << meshNodes.size() << " meshes." << std::endl; + + // Process just 0, because dumb. Fail out if more than 1? + if (meshNodes.size() > 1) + { + std::cerr << "Can't load more that one graphics mesh." << std::endl; + return; + } + + if (meshNodes.empty()) + { + return; + } + + 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; + } + + 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); + FbxVector4 rotation = trans.GetR(); + FbxVector4 scale = trans.GetS(); + FbxAMatrix normalTransf; + normalTransf.SetR(rotation); + normalTransf.SetS(scale); + normalTransf = normalTransf.Inverse().Transpose(); + + int32_t matElements = mesh->GetElementMaterialCount(); + if (matElements > 1) + { + std::cerr << "Mesh has more than 1 material mappings, first one will be used. " << std::endl; + } + auto matLayer = mesh->GetElementMaterial(0); + + + 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 = normalTransf.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++); + } + if (matLayer != nullptr) + { + mMaterialIds.push_back(matLayer->GetIndexArray().GetAt(i)); + } + } + + mVertexPositions = positions; + mVertexNormals = normals; + mVertexUv = uv; + mIndices = indices; + + getBoneInfluencesInternal(mesh); +} + +int32_t FbxFileReader::getMaterialCount() +{ + return mMaterialNames.size(); +} + +void FbxFileReader::getFbxMeshes(FbxDisplayLayer* collisionDisplayLayer, FbxNode* node, std::vector<FbxNode*>& meshNodes) +{ + FbxMesh* mesh = node->GetMesh(); + + if (mesh != nullptr) + { + if (collisionDisplayLayer == nullptr && node->FindProperty("ParentalChunkIndex").IsValid()) + { + //Old-style file + uint32_t chunkIndex = node->FindProperty("ParentalChunkIndex").Get<int32_t>(); + mCollisionNodes.emplace(chunkIndex, node); + } + else if (collisionDisplayLayer != nullptr && collisionDisplayLayer->IsMember(node)) + { + uint32_t chunkIndex = FbxUtils::getChunkIndexForNode(node); + if (chunkIndex != UINT32_MAX) + { + mCollisionNodes.emplace(chunkIndex, node); + } + else + { + std::cerr << "Warning: Not sure what to do about collision geo " << node->GetName() << ". No corresponding chunk." << std::endl; + } + } + else + { + meshNodes.push_back(node); + } + } + int childCount = node->GetChildCount(); + + for (int i = 0; i < childCount; i++) + { + FbxNode * childNode = node->GetChild(i); + + getFbxMeshes(collisionDisplayLayer, childNode, meshNodes); + } +} + +bool FbxFileReader::isCollisionLoaded() +{ + return !mCollisionNodes.empty(); +} + +uint32_t FbxFileReader::getCollision(uint32_t*& hullsOffset, Nv::Blast::CollisionHull** hulls) +{ + if (!isCollisionLoaded()) + { + return 0; + } + hullsOffset = new uint32_t[mMeshCount + 1]; + hulls = new Nv::Blast::CollisionHull*[mMeshCount]; + memcpy(hullsOffset, mHullsOffset.data(), sizeof(uint32_t) * (mMeshCount + 1)); + memcpy(hulls, mHulls.data(), sizeof(Nv::Blast::CollisionHull*) * mMeshCount); + return mMeshCount; +} + +struct CollisionHullImpl : public Nv::Blast::CollisionHull +{ + void release() override + { + + } +}; + +bool FbxFileReader::getCollisionInternal() +{ + for (auto hull : mHulls) + { + hull->release(); + } + int32_t maxParentIndex = 0; + + for (auto p : mCollisionNodes) + { + int32_t parentIndex = p.first; + maxParentIndex = std::max(maxParentIndex, parentIndex); + } + mMeshCount = maxParentIndex + 1; + mHullsOffset.resize(mMeshCount + 1); + mHulls.resize(mCollisionNodes.size()); + mHullsOffset[0] = 0; + + for (auto p : mCollisionNodes) // it should be sorted by chunk id + { + int32_t parentIndex = p.first; + //hulls[parentIndex].push_back(Nv::Blast::CollisionHull()); + mHulls[mHullsOffset[parentIndex]] = new CollisionHullImpl(); + Nv::Blast::CollisionHull& chull = *mHulls[mHullsOffset[parentIndex]]; + FbxMesh* meshNode = p.second->GetMesh(); + + FbxAMatrix nodeTransform = getTransformForNode(p.second); + FbxAMatrix nodeTransformNormal = nodeTransform.Inverse().Transpose(); + + chull.points = new PxVec3[meshNode->GetControlPointsCount()]; + FbxVector4* vpos = meshNode->GetControlPoints(); + /** + Copy control points from FBX. + */ + for (int32_t i = 0; i < meshNode->GetControlPointsCount(); ++i) + { + FbxVector4 worldVPos = nodeTransform.MultT(*vpos); + chull.points[i].x = (float)worldVPos[0]; + chull.points[i].y = (float)worldVPos[1]; + chull.points[i].z = (float)worldVPos[2]; + vpos++; + } + + uint32_t polyCount = meshNode->GetPolygonCount(); + chull.polygonData = new Nv::Blast::CollisionHull::HullPolygon[polyCount]; + FbxGeometryElementNormal* nrm = meshNode->GetElementNormal(); + FbxLayerElementArray& narr = nrm->GetDirectArray(); + + for (uint32_t poly = 0; poly < polyCount; ++poly) + { + int32_t vInPolyCount = meshNode->GetPolygonSize(poly); + auto& pd = chull.polygonData[poly]; + pd.mIndexBase = (uint16_t)chull.indicesCount; + pd.mNbVerts = (uint16_t)vInPolyCount; + int32_t* ind = &meshNode->GetPolygonVertices()[meshNode->GetPolygonVertexIndex(poly)]; + chull.indices = new uint32_t[vInPolyCount]; + memcpy(chull.indices, ind, sizeof(uint32_t) * vInPolyCount); + + FbxVector4 normal; + narr.GetAt(poly, &normal); + + normal = nodeTransformNormal.MultT(normal); + + pd.mPlane[0] = (float)normal[0]; + pd.mPlane[1] = (float)normal[1]; + pd.mPlane[2] = (float)normal[2]; + PxVec3 polyLastVertex = chull.points[chull.indices[vInPolyCount - 1]]; + pd.mPlane[3] = -((float)(polyLastVertex.x * normal[0] + polyLastVertex.y * normal[1] + polyLastVertex.z * normal[2])); + } + mHullsOffset[parentIndex + 1] = mHullsOffset[parentIndex] + 1; + } + + + return false; +} + + +/** + To work properly export tool should give bone names as bone_@chunkIndex (e.g. bone_1, bone_2) +**/ +bool FbxFileReader::getBoneInfluencesInternal(FbxMesh* meshNode) +{ + + if (meshNode->GetDeformerCount() != 1) + { + std::cout << "Can't create bone mapping: There is no mesh deformers...: " << std::endl; + return false; + } + mVertexToParentBoneMap.clear(); + mVertexToParentBoneMap.resize(mVertexPositions.size()); + std::vector<uint32_t> controlToParentBoneMap; + controlToParentBoneMap.resize(meshNode->GetControlPointsCount()); + FbxSkin* def = (FbxSkin *)meshNode->GetDeformer(0, FbxDeformer::EDeformerType::eSkin); + + if (def->GetClusterCount() == 0) + { + std::cout << "Can't create bone mapping: There is no vertex clusters...: " << std::endl; + return false; + } + mBoneCount = def->GetClusterCount(); + for (int32_t i = 0; i < def->GetClusterCount(); ++i) + { + FbxCluster* cls = def->GetCluster(i); + FbxNode* bone = cls->GetLink(); + int32_t parentChunk = atoi(bone->GetName() + 5); + int32_t* cpIndx = cls->GetControlPointIndices(); + for (int32_t j = 0; j < cls->GetControlPointIndicesCount(); ++j) + { + controlToParentBoneMap[*cpIndx] = parentChunk; + ++cpIndx; + } + } + int* polyVertices = meshNode->GetPolygonVertices(); + uint32_t lv = 0; + for (int i = 0; i < meshNode->GetPolygonCount(); i++) + { + for (int vi = 0; vi < 3; vi++) + { + mVertexToParentBoneMap[lv] = controlToParentBoneMap[*polyVertices]; + polyVertices++; + lv++; + } + } + return true; +}; + +physx::PxVec3* FbxFileReader::getPositionArray() +{ + return mVertexPositions.data(); +}; + +physx::PxVec3* FbxFileReader::getNormalsArray() +{ + return mVertexNormals.data(); +}; + +physx::PxVec2* FbxFileReader::getUvArray() +{ + return mVertexUv.data(); +}; + +uint32_t* FbxFileReader::getIndexArray() +{ + return mIndices.data(); +}; + +uint32_t FbxFileReader::getBoneInfluences(uint32_t*& out) +{ + out = static_cast<uint32_t*>(NVBLAST_ALLOC(sizeof(uint32_t) * mVertexToParentBoneMap.size())); + memcpy(out, mVertexToParentBoneMap.data(), sizeof(uint32_t) * mVertexToParentBoneMap.size()); + return mVertexToParentBoneMap.size(); +} + +uint32_t FbxFileReader::getBoneCount() +{ + return mBoneCount; +} + +char* FbxFileReader::getMaterialName(int32_t id) +{ + if (id < int32_t(mMaterialNames.size()) && id >= 0) + { + return &mMaterialNames[id][0]; + } + else + { + return nullptr; + } +} + +int32_t* FbxFileReader::getMaterialIds() +{ + if (mMaterialIds.empty()) + { + return nullptr; + } + return mMaterialIds.data(); +}
\ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h b/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h new file mode 100644 index 0000000..4155b25 --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h @@ -0,0 +1,144 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTERFBXREADER_H +#define NVBLASTEXTEXPORTERFBXREADER_H + +#include <memory> +#include "fbxsdk.h" +#include <vector> +#include <map> +#include "NvBlastExtExporter.h" +#include "NvBlastExtAuthoringTypes.h" + +namespace Nv +{ +namespace Blast +{ +class Mesh; + +class FbxFileReader : public IFbxFileReader +{ +public: + FbxFileReader(); + ~FbxFileReader() = default; + + virtual void release() override; + + /* + Load from the specified file path, returning a mesh or nullptr if failed + */ + virtual void loadFromFile(const char* filename) override; + + virtual uint32_t getVerticesCount() const override + { + return mVertexPositions.size(); + } + + virtual uint32_t getIdicesCount() const override + { + return mIndices.size(); + } + + /** + Check whether file contained an collision geometry + */ + virtual bool isCollisionLoaded() override; + + /** + Retrieve collision geometry if it exist + */ + virtual uint32_t getCollision(uint32_t*& hullsOffset, Nv::Blast::CollisionHull** hulls) override; + + virtual uint32_t getBoneInfluences(uint32_t*& out) override; + + virtual uint32_t getBoneCount() override; + + /** + Get loaded vertex positions + */ + virtual physx::PxVec3* getPositionArray() override; + /** + Get loaded vertex normals + */ + virtual physx::PxVec3* getNormalsArray() override; + /** + Get loaded vertex uv-coordinates + */ + virtual physx::PxVec2* getUvArray() override; + /** + Get loaded triangle indices + */ + virtual uint32_t* getIndexArray() override; + + /** + Get loaded per triangle material ids. + */ + int32_t* getMaterialIds() override; + + /** + Get loaded per triangle smoothing groups. Currently not supported. + */ + int32_t* getSmoothingGroups() override { return nullptr; }; + + /** + Get material name. + */ + char* getMaterialName(int32_t id) override; + + + int32_t getMaterialCount() override; + +private: + + uint32_t mMeshCount; + std::vector<uint32_t> mHullsOffset; + std::vector<Nv::Blast::CollisionHull*> mHulls; + std::vector<uint32_t> mVertexToParentBoneMap; + std::multimap<uint32_t, FbxNode*> mCollisionNodes; + std::vector<physx::PxVec3> mVertexPositions; + std::vector<physx::PxVec3> mVertexNormals; + std::vector<physx::PxVec2> mVertexUv; + std::vector<uint32_t> mIndices; + std::vector<int32_t> mSmoothingGroups; + std::vector<int32_t> mMaterialIds; + std::vector<std::string> mMaterialNames; + + uint32_t mBoneCount; + + FbxAMatrix getTransformForNode(FbxNode* node); + void getFbxMeshes(FbxDisplayLayer* collisionDisplayLayer, FbxNode* node, std::vector<FbxNode*>& meshNodes); + bool getCollisionInternal(); + bool getBoneInfluencesInternal(FbxMesh* meshNode); + +}; + +} +} + +#endif
\ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.cpp new file mode 100644 index 0000000..95a757f --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.cpp @@ -0,0 +1,192 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#include "fbxsdk.h" +#include "NvBlastExtExporterFbxUtils.h" +#include "PxVec3.h" +#include "PxVec2.h" +#include "NvBlastExtAuthoringTypes.h" +#include <sstream> +#include <cctype> + +using physx::PxVec3; +using physx::PxVec2; + + +void FbxUtils::VertexToFbx(const 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(const physx::PxVec3& inVector, FbxVector4& outVector) +{ + outVector[0] = inVector.x; + outVector[1] = inVector.y; + outVector[2] = inVector.z; + outVector[3] = 0; +} + +void FbxUtils::PxVec2ToFbx(const physx::PxVec2& inVector, FbxVector2& outVector) +{ + outVector[0] = inVector.x; + outVector[1] = inVector.y; +} + +FbxAxisSystem FbxUtils::getBlastFBXAxisSystem() +{ + const FbxAxisSystem::EUpVector upVector = FbxAxisSystem::eZAxis; + //From the documentation: If the up axis is Z, the remain two axes will X And Y, so the ParityEven is X, and the ParityOdd is Y + const FbxAxisSystem::EFrontVector frontVector = FbxAxisSystem::eParityOdd; + const FbxAxisSystem::ECoordSystem rightVector = FbxAxisSystem::eRightHanded; + return FbxAxisSystem(upVector, frontVector, rightVector); +} + +FbxSystemUnit FbxUtils::getBlastFBXUnit() +{ + return FbxSystemUnit::cm; +} + +std::string FbxUtils::FbxAxisSystemToString(const FbxAxisSystem& axisSystem) +{ + std::stringstream ss; + int upSign, frontSign; + FbxAxisSystem::EUpVector upVector = axisSystem.GetUpVector(upSign); + FbxAxisSystem::EFrontVector frontVector = axisSystem.GetFrontVector(frontSign); + FbxAxisSystem::ECoordSystem coordSystem = axisSystem.GetCoorSystem(); + ss << "Predefined Type: "; + if (axisSystem == FbxAxisSystem::MayaZUp) + { + ss << "MayaZUP"; + } + else if (axisSystem == FbxAxisSystem::MayaYUp) + { + ss << "MayaYUp"; + } + else if (axisSystem == FbxAxisSystem::Max) + { + ss << "Max"; + } + else if (axisSystem == FbxAxisSystem::Motionbuilder) + { + ss << "Motionbuilder"; + } + else if (axisSystem == FbxAxisSystem::OpenGL) + { + ss << "OpenGL"; + } + else if (axisSystem == FbxAxisSystem::DirectX) + { + ss << "OpenGL"; + } + else if (axisSystem == FbxAxisSystem::Lightwave) + { + ss << "OpenGL"; + } + else + { + ss << "<Other>"; + } + ss << " UpVector: " << (upSign > 0 ? "+" : "-"); + switch (upVector) + { + case FbxAxisSystem::eXAxis: ss << "eXAxis"; break; + case FbxAxisSystem::eYAxis: ss << "eYAxis"; break; + case FbxAxisSystem::eZAxis: ss << "eZAxis"; break; + default: ss << "<unknown>"; break; + } + + ss << " FrontVector: " << (frontSign > 0 ? "+" : "-"); + switch (frontVector) + { + case FbxAxisSystem::eParityEven: ss << "eParityEven"; break; + case FbxAxisSystem::eParityOdd: ss << "eParityOdd"; break; + default: ss << "<unknown>"; break; + } + + ss << " CoordSystem: "; + switch (coordSystem) + { + case FbxAxisSystem::eLeftHanded: ss << "eLeftHanded"; break; + case FbxAxisSystem::eRightHanded: ss << "eRightHanded"; break; + default: ss << "<unknown>"; break; + } + + return ss.str(); +} + +std::string FbxUtils::FbxSystemUnitToString(const FbxSystemUnit& systemUnit) +{ + return std::string(systemUnit.GetScaleFactorAsString()); +} + +const static std::string chunkPrefix = "chunk_"; + +uint32_t FbxUtils::getChunkIndexForNode(FbxNode* node, bool includeParents /*= true*/) +{ + std::string nodeName(node->GetNameOnly()); + for (char& c : nodeName) + c = (char)std::tolower(c); + + if (nodeName.substr(0, chunkPrefix.size()) == chunkPrefix) + { + std::istringstream iss(nodeName.substr(chunkPrefix.size())); + uint32_t ret = UINT32_MAX; + iss >> ret; + if (!iss.fail()) + { + return ret; + } + } + + if (includeParents && node->GetParent()) + { + return getChunkIndexForNode(node->GetParent(), true); + } + //Found nothing + return UINT32_MAX; +} + +std::string FbxUtils::getChunkNodeName(uint32_t chunkIndex) +{ + //This naming is required for the UE4 plugin to find them + std::ostringstream namestream; + namestream << chunkPrefix << chunkIndex; + return namestream.str(); +} + +std::string FbxUtils::getCollisionGeometryLayerName() +{ + return "Collision"; +} + +std::string FbxUtils::getRenderGeometryLayerName() +{ + return "Render"; +} diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.h b/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.h new file mode 100644 index 0000000..d26a9e9 --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.h @@ -0,0 +1,67 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTERFBXUTILS_H +#define NVBLASTEXTEXPORTERFBXUTILS_H + +#include "fbxsdk.h" +#include "PxVec3.h" +#include "PxVec2.h" +#include <string> + +namespace Nv +{ + namespace Blast + { + struct Vertex; + } +} + +class FbxUtils +{ +public: + static void VertexToFbx(const Nv::Blast::Vertex& vert, FbxVector4& outVertex, FbxVector4& outNormal, FbxVector2& outUV); + + static void PxVec3ToFbx(const physx::PxVec3& inVector, FbxVector4& outVector); + static void PxVec2ToFbx(const physx::PxVec2& inVector, FbxVector2& outVector); + + static FbxAxisSystem getBlastFBXAxisSystem(); + static FbxSystemUnit getBlastFBXUnit(); + + static std::string FbxAxisSystemToString(const FbxAxisSystem& axisSystem); + static std::string FbxSystemUnitToString(const FbxSystemUnit& systemUnit); + + //returns UINT32_MAX if not a chunk + static uint32_t getChunkIndexForNode(FbxNode* node, bool includeParents = true); + static std::string getChunkNodeName(uint32_t chunkIndex); + + static std::string getCollisionGeometryLayerName(); + static std::string getRenderGeometryLayerName(); +}; + +#endif //NVBLASTEXTEXPORTERFBXUTILS_H
\ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp new file mode 100644 index 0000000..b5fd04b --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp @@ -0,0 +1,1095 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#include "fbxsdk.h" +#include <iostream> +#include <sstream> +#include <iomanip> +#include "NvBlastTypes.h" +#include "NvBlastGlobals.h" +#include "NvBlastTkFramework.h" +#include "NvBlast.h" +#include "PxVec3.h" +#include "NvBlastAssert.h" +#include <unordered_set> +#include <functional> +#include "NvBlastExtExporterFbxWriter.h" +#include "NvBlastExtExporterFbxUtils.h" +#include "NvBlastExtAuthoringCollisionBuilder.h" +#include "NvBlastExtAuthoring.h" +#include "NvBlastExtAuthoringMesh.h" + +using namespace Nv::Blast; + +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(); + }); + + mScene = FbxScene::Create(sdkManager.get(), "Export Scene"); + + mScene->GetGlobalSettings().SetAxisSystem(FbxUtils::getBlastFBXAxisSystem()); + mScene->GetGlobalSettings().SetSystemUnit(FbxUtils::getBlastFBXUnit()); + mScene->GetGlobalSettings().SetOriginalUpAxis(FbxUtils::getBlastFBXAxisSystem()); + mScene->GetGlobalSettings().SetOriginalSystemUnit(FbxUtils::getBlastFBXUnit()); + + //We don't actually check for membership in this layer, but it's useful to show and hide the geo to look at the collision geo + mRenderLayer = FbxDisplayLayer::Create(mScene, FbxUtils::getRenderGeometryLayerName().c_str()); + mRenderLayer->Show.Set(true); + mRenderLayer->Color.Set(FbxDouble3(0.0f, 1.0f, 0.0f)); +} + +void FbxFileWriter::release() +{ + //sdkManager->Destroy(); + delete this; +} + +FbxScene* FbxFileWriter::getScene() +{ + return mScene; +} + + +void FbxFileWriter::createMaterials(const ExporterMeshData& aResult) +{ + mMaterials.clear(); + + for (uint32_t i = 0; i < aResult.submeshCount; ++i) + { + FbxSurfacePhong* material = FbxSurfacePhong::Create(sdkManager.get(), aResult.submeshNames[i]); + material->Diffuse.Set(FbxDouble3(float(rand()) / RAND_MAX , float(rand()) / RAND_MAX, float(rand()) / RAND_MAX)); + material->DiffuseFactor.Set(1.0); + mMaterials.push_back(material); + } +} + +void FbxFileWriter::createMaterials(const AuthoringResult& aResult) +{ + mMaterials.clear(); + for (uint32_t i = 0; i < aResult.materialCount; ++i) + { + FbxSurfacePhong* material = FbxSurfacePhong::Create(sdkManager.get(), aResult.materialNames[i]); + material->Diffuse.Set(FbxDouble3(float(rand()) / RAND_MAX, float(rand()) / RAND_MAX, float(rand()) / RAND_MAX)); + material->DiffuseFactor.Set(1.0); + mMaterials.push_back(material); + } + if (mMaterials.size() == 0) + { + FbxSurfacePhong* material = FbxSurfacePhong::Create(sdkManager.get(), "Base_mat"); + material->Diffuse.Set(FbxDouble3(0.3, 1.0, 0)); + material->DiffuseFactor.Set(1.0); + mMaterials.push_back(material); + } + FbxSurfacePhong* interiorMat = FbxSurfacePhong::Create(sdkManager.get(), "Interior_Material"); + interiorMat->Diffuse.Set(FbxDouble3(1.0, 0.0, 0.5)); + interiorMat->DiffuseFactor.Set(1.0); + mMaterials.push_back(interiorMat); +} + + +bool FbxFileWriter::appendMesh(const AuthoringResult& aResult, const char* assetName, bool nonSkinned) +{ + createMaterials(aResult); + + if (nonSkinned) + { + return appendNonSkinnedMesh(aResult, assetName); + } + std::string meshName(assetName); meshName.append("_rendermesh"); + + FbxMesh* mesh = FbxMesh::Create(sdkManager.get(), meshName.c_str()); + + FbxGeometryElementNormal* geNormal = mesh->CreateElementNormal(); + geNormal->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geNormal->SetReferenceMode(FbxGeometryElement::eDirect); + + FbxGeometryElementUV* geUV = mesh->CreateElementUV("diffuseElement"); + geUV->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geUV->SetReferenceMode(FbxGeometryElement::eDirect); + + FbxGeometryElementSmoothing* smElement = nullptr; + size_t triangleCount = aResult.geometryOffset[aResult.chunkCount]; + + for (size_t triangle = 0; triangle < triangleCount; triangle++) + { + if (aResult.geometry[triangle].smoothingGroup >= 0) + { + //Found a valid smoothing group + smElement = mesh->CreateElementSmoothing(); + smElement->SetMappingMode(FbxGeometryElement::eByPolygon); + smElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + break; + } + } + + mesh->InitControlPoints((int)triangleCount * 3); + + + FbxNode* meshNode = FbxNode::Create(mScene, assetName); + meshNode->SetNodeAttribute(mesh); + meshNode->SetShadingMode(FbxNode::eTextureShading); + + mRenderLayer->AddMember(meshNode); + + for (uint32_t i = 0; i < mMaterials.size(); ++i) + { + meshNode->AddMaterial(mMaterials[i]); + } + + FbxNode* lRootNode = mScene->GetRootNode(); + + //In order for Maya to correctly convert the axis of a skinned model there must be a common root node between the skeleton and the model + FbxNode* sceneRootNode = FbxNode::Create(sdkManager.get(), "sceneRoot"); + lRootNode->AddChild(sceneRootNode); + sceneRootNode->AddChild(meshNode); + + //UE4 cannot hide the root bone, so add a dummy chunk so chunk0 is not the root + FbxNode* skelRootNode = FbxNode::Create(sdkManager.get(), "root"); + FbxSkeleton* skelAttrib = FbxSkeleton::Create(sdkManager.get(), "SkelRootAttrib"); + skelAttrib->SetSkeletonType(FbxSkeleton::eRoot); + skelRootNode->SetNodeAttribute(skelAttrib); + + sceneRootNode->AddChild(skelRootNode); + + 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); + + // 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 = aResult.chunkCount; + auto chunks = aResult.chunkDescs; + + uint32_t cpIdx = 0; + for (uint32_t i = 0; i < chunkCount; i++) + { + auto& chunk = chunks[i]; + + if (chunk.parentChunkIndex == UINT32_MAX) + { + uint32_t addedCps = createChunkRecursive(cpIdx, i, meshNode, skelRootNode, skin, aResult); + cpIdx += addedCps; + } + } + + if (!smElement) + { + //If no smoothing groups, generate them + FbxGeometryConverter fbxConv(mesh->GetFbxManager()); + if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) + { + fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); + } + } + + if (aResult.collisionHull != nullptr) + { + return appendCollisionMesh(chunkCount, aResult.collisionHullOffset, aResult.collisionHull, assetName); + } + + return true; +}; + + +bool FbxFileWriter::appendNonSkinnedMesh(const AuthoringResult& aResult, const char* assetName) +{ + FbxNode* lRootNode = mScene->GetRootNode(); + + //UE4 cannot hide the root bone, so add a dummy chunk so chunk0 is not the root + FbxNode* skelRootNode = FbxNode::Create(sdkManager.get(), "root"); + //UE4 needs this to be a skeleton node, null node, or mesh node to get used + FbxNull* nullAttr = FbxNull::Create(sdkManager.get(), "SkelRootAttrib"); + skelRootNode->SetNodeAttribute(nullAttr); + lRootNode->AddChild(skelRootNode); + + // 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 = aResult.chunkCount; + + auto chunks = aResult.chunkDescs; + + for (uint32_t i = 0; i < chunkCount; i++) + { + auto& chunk = chunks[i]; + + if (chunk.parentChunkIndex == UINT32_MAX) + { + createChunkRecursiveNonSkinned(assetName, i, skelRootNode, mMaterials, aResult); + } + } + + if (aResult.collisionHull != nullptr) + { + return appendCollisionMesh(chunkCount, aResult.collisionHullOffset, aResult.collisionHull, assetName); + } + + return true; +} + + +bool FbxFileWriter::appendNonSkinnedMesh(const ExporterMeshData& meshData, const char* assetName) +{ + FbxNode* lRootNode = mScene->GetRootNode(); + + //UE4 cannot hide the root bone, so add a dummy chunk so chunk0 is not the root + FbxNode* skelRootNode = FbxNode::Create(sdkManager.get(), "root"); + //UE4 needs this to be a skeleton node, null node, or mesh node to get used + FbxNull* nullAttr = FbxNull::Create(sdkManager.get(), "SkelRootAttrib"); + skelRootNode->SetNodeAttribute(nullAttr); + lRootNode->AddChild(skelRootNode); + + // 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(meshData.asset, Nv::Blast::logLL); + + auto chunks = NvBlastAssetGetChunks(meshData.asset, Nv::Blast::logLL); + + for (uint32_t i = 0; i < chunkCount; i++) + { + const NvBlastChunk* chunk = &chunks[i]; + + if (chunk->parentChunkIndex == UINT32_MAX) + { + createChunkRecursiveNonSkinned("chunk", i, skelRootNode, mMaterials, meshData); + } + } + if (meshData.hulls != nullptr) + { + return appendCollisionMesh(chunkCount, meshData.hullsOffsets, meshData.hulls, assetName); + } + return true; +} + +bool FbxFileWriter::appendCollisionMesh(uint32_t meshCount, uint32_t* offsets, CollisionHull** hulls, const char* assetName) +{ + FbxDisplayLayer* displayLayer = FbxDisplayLayer::Create(mScene, FbxUtils::getCollisionGeometryLayerName().c_str()); + //Hide by default + displayLayer->Show.Set(false); + displayLayer->Color.Set(FbxDouble3(0.0f, 0.0f, 1.0f)); + + // Now walk the tree and create a skeleton with geometry at the same time + // Find a "root" chunk and walk the tree from there. + + for (uint32_t i = 0; i < meshCount; i++) + { + auto findIt = chunkNodes.find(i); + if (findIt == chunkNodes.end()) + { + std::cerr << "Warning: No chunk node for chunk " << i << ". Ignoring collision geo" << std::endl; + continue; + } + addCollisionHulls(i, displayLayer, findIt->second, offsets[i+1] - offsets[i], hulls + offsets[i]); + } + return true; +} + +/* + 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 AuthoringResult& aResult) +{ + + auto chunks = NvBlastAssetGetChunks(aResult.asset, Nv::Blast::logLL); + const NvBlastChunk* chunk = &chunks[chunkIndex]; + physx::PxVec3 centroid = physx::PxVec3(chunk->centroid[0], chunk->centroid[1], chunk->centroid[2]); + + //mesh->InitTextureUV(triangles.size() * 3); + + std::string boneName = FbxUtils::getChunkNodeName(chunkIndex); + + FbxSkeleton* skelAttrib = FbxSkeleton::Create(sdkManager.get(), boneName.c_str()); + if (chunk->parentChunkIndex == UINT32_MAX) + { + skelAttrib->SetSkeletonType(FbxSkeleton::eRoot); + + // Change the centroid to origin + centroid = physx::PxVec3(0.0f); + } + else + { + skelAttrib->SetSkeletonType(FbxSkeleton::eLimbNode); + worldChunkPivots[chunkIndex] = centroid; + } + + skelAttrib->Size.Set(1.0); // What's this for? + + + FbxNode* boneNode = FbxNode::Create(sdkManager.get(), boneName.c_str()); + boneNode->SetNodeAttribute(skelAttrib); + + chunkNodes[chunkIndex] = boneNode; + + auto mat = parentNode->EvaluateGlobalTransform().Inverse(); + + FbxVector4 vec(0, 0, 0, 0); + FbxVector4 c2 = mat.MultT(vec); + + boneNode->LclTranslation.Set(c2); + + parentNode->AddChild(boneNode); + + std::ostringstream namestream; + 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(); + FbxGeometryElementSmoothing* smElement = mesh->GetElementSmoothing(); + + 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 (uint32_t i = aResult.geometryOffset[chunkIndex]; i < aResult.geometryOffset[chunkIndex + 1]; i++) + { + Triangle& tri = aResult.geometry[i]; + 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(); + int32_t material = (tri.materialId != MATERIAL_INTERIOR) ? ((tri.materialId < int32_t(mMaterials.size() - 1)) ? tri.materialId : 0) : int32_t(mMaterials.size() - 1); + matElement->GetIndexArray().SetAt(polyCount, material); + if (smElement) + { + if (tri.userData == 0) + { + smElement->GetIndexArray().SetAt(polyCount, tri.smoothingGroup); + } + else + { + smElement->GetIndexArray().SetAt(polyCount, SMOOTHING_GROUP_INTERIOR); + } + } + + polyCount++; + cpIdx += 3; + } + + mat = meshNode->EvaluateGlobalTransform(); + cluster->SetTransformMatrix(mat); + + mat = boneNode->EvaluateGlobalTransform(); + cluster->SetTransformLinkMatrix(mat); + + uint32_t addedCps = static_cast<uint32_t>((aResult.geometryOffset[chunkIndex + 1] - aResult.geometryOffset[chunkIndex]) * 3); + + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) + { + addedCps += createChunkRecursive(currentCpIdx + addedCps, i, meshNode, boneNode, skin, aResult); + } + + return addedCps; +} + + +void FbxFileWriter::createChunkRecursiveNonSkinned(const std::string& meshName, uint32_t chunkIndex, FbxNode* parentNode, + const std::vector<FbxSurfaceMaterial*>& materials, const ExporterMeshData& meshData) +{ + auto chunks = NvBlastAssetGetChunks(meshData.asset, Nv::Blast::logLL); + const NvBlastChunk* chunk = &chunks[chunkIndex]; + physx::PxVec3 centroid = physx::PxVec3(chunk->centroid[0], chunk->centroid[1], chunk->centroid[2]); + + std::string chunkName = FbxUtils::getChunkNodeName(chunkIndex); + + FbxMesh* mesh = FbxMesh::Create(sdkManager.get(), (chunkName + "_mesh").c_str()); + + FbxNode* meshNode = FbxNode::Create(mScene, chunkName.c_str()); + meshNode->SetNodeAttribute(mesh); + meshNode->SetShadingMode(FbxNode::eTextureShading); + mRenderLayer->AddMember(meshNode); + + chunkNodes[chunkIndex] = meshNode; + + auto mat = parentNode->EvaluateGlobalTransform().Inverse(); + + FbxVector4 c2 = mat.MultT(FbxVector4(centroid.x, centroid.y, centroid.z, 1.0f)); + if (chunk->parentChunkIndex != UINT32_MAX) + { + //Don't mess with the root chunk pivot + meshNode->LclTranslation.Set(c2); + worldChunkPivots[chunkIndex] = centroid; + } + + parentNode->AddChild(meshNode); + FbxAMatrix finalXForm = meshNode->EvaluateGlobalTransform(); + + //Set the geo transform to inverse so we can use the world mesh coordinates + FbxAMatrix invFinalXForm = finalXForm.Inverse(); + meshNode->SetGeometricTranslation(FbxNode::eSourcePivot, invFinalXForm.GetT()); + meshNode->SetGeometricRotation(FbxNode::eSourcePivot, invFinalXForm.GetR()); + meshNode->SetGeometricScaling(FbxNode::eSourcePivot, invFinalXForm.GetS()); + + auto geNormal = mesh->CreateElementNormal(); + auto geUV = mesh->CreateElementUV("diffuseElement"); + auto matr = mesh->CreateElementMaterial(); + + uint32_t* firstIdx = meshData.submeshOffsets + chunkIndex * meshData.submeshCount; + uint32_t* lastIdx = meshData.submeshOffsets + (chunkIndex + 1) * meshData.submeshCount; + uint32_t cpCount = *lastIdx - *firstIdx; + mesh->InitControlPoints(cpCount); + + geNormal->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geNormal->SetReferenceMode(FbxGeometryElement::eDirect); + + geUV->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geUV->SetReferenceMode(FbxGeometryElement::eDirect); + + matr->SetMappingMode(FbxGeometryElement::eByPolygon); + matr->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + for (auto m : materials) + { + meshNode->AddMaterial(m); + } + + uint32_t cPolygCount = 0; + int32_t addedVertices = 0; + + for (uint32_t subMesh = 0; subMesh < meshData.submeshCount; ++subMesh) + { + for (uint32_t tr = *(firstIdx + subMesh); tr < *(firstIdx + subMesh + 1); tr += 3) + { + mesh->BeginPolygon(subMesh); + for (uint32_t k = 0; k < 3; ++k) + { + mesh->AddPolygon(tr - *firstIdx + k); + + FbxVector4 temp; + FbxUtils::PxVec3ToFbx(meshData.positions[meshData.posIndex[tr + k]], temp); + mesh->SetControlPointAt(temp, tr - *firstIdx + k); + + FbxUtils::PxVec3ToFbx(meshData.normals[meshData.normIndex[tr + k]], temp); + geNormal->GetDirectArray().Add(temp); + + FbxVector2 temp2; + FbxUtils::PxVec2ToFbx(meshData.uvs[meshData.texIndex[tr + k]], temp2); + geUV->GetDirectArray().Add(temp2); + } + mesh->EndPolygon(); + cPolygCount++; + addedVertices += 3; + } + } + + if (!mesh->GetElementSmoothing()) + { + //If no smoothing groups, generate them + FbxGeometryConverter fbxConv(mesh->GetFbxManager()); + if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) + { + fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); + } + } + + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) + { + createChunkRecursiveNonSkinned(meshName, i, meshNode, materials, meshData); + } +} + + +void FbxFileWriter::createChunkRecursiveNonSkinned(const std::string& meshName, uint32_t chunkIndex, FbxNode* parentNode, const std::vector<FbxSurfaceMaterial*>& materials, const AuthoringResult& aResult) +{ + auto chunks = NvBlastAssetGetChunks(aResult.asset, Nv::Blast::logLL); + const NvBlastChunk* chunk = &chunks[chunkIndex]; + physx::PxVec3 centroid = physx::PxVec3(chunk->centroid[0], chunk->centroid[1], chunk->centroid[2]); + + std::string chunkName = FbxUtils::getChunkNodeName(chunkIndex).c_str(); + + FbxMesh* mesh = FbxMesh::Create(sdkManager.get(), (chunkName + "_mesh").c_str()); + + FbxNode* meshNode = FbxNode::Create(mScene, chunkName.c_str()); + meshNode->SetNodeAttribute(mesh); + meshNode->SetShadingMode(FbxNode::eTextureShading); + mRenderLayer->AddMember(meshNode); + + chunkNodes[chunkIndex] = meshNode; + + auto mat = parentNode->EvaluateGlobalTransform().Inverse(); + + FbxVector4 c2 = mat.MultT(FbxVector4(centroid.x, centroid.y, centroid.z, 1.0f)); + + if (chunk->parentChunkIndex != UINT32_MAX) + { + //Don't mess with the root chunk pivot + meshNode->LclTranslation.Set(c2); + worldChunkPivots[chunkIndex] = centroid; + } + + parentNode->AddChild(meshNode); + FbxAMatrix finalXForm = meshNode->EvaluateGlobalTransform(); + + //Set the geo transform to inverse so we can use the world mesh coordinates + FbxAMatrix invFinalXForm = finalXForm.Inverse(); + meshNode->SetGeometricTranslation(FbxNode::eSourcePivot, invFinalXForm.GetT()); + meshNode->SetGeometricRotation(FbxNode::eSourcePivot, invFinalXForm.GetR()); + meshNode->SetGeometricScaling(FbxNode::eSourcePivot, invFinalXForm.GetS()); + + + auto geNormal = mesh->CreateElementNormal(); + auto geUV = mesh->CreateElementUV("diffuseElement"); + auto matr = mesh->CreateElementMaterial(); + + uint32_t firstIdx = aResult.geometryOffset[chunkIndex]; + uint32_t lastIdx = aResult.geometryOffset[chunkIndex + 1]; + + FbxGeometryElementSmoothing* smElement = nullptr; + for (uint32_t triangle = firstIdx; triangle < lastIdx; triangle++) + { + if (aResult.geometry[triangle].smoothingGroup >= 0) + { + //Found a valid smoothing group + smElement = mesh->CreateElementSmoothing(); + smElement->SetMappingMode(FbxGeometryElement::eByPolygon); + smElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + break; + } + } + + mesh->InitControlPoints((int)(lastIdx - firstIdx) * 3); + + geNormal->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geNormal->SetReferenceMode(FbxGeometryElement::eDirect); + + geUV->SetMappingMode(FbxGeometryElement::eByPolygonVertex); + geUV->SetReferenceMode(FbxGeometryElement::eDirect); + + matr->SetMappingMode(FbxGeometryElement::eByPolygon); + matr->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + for (auto m : materials) + { + meshNode->AddMaterial(m); + } + + FbxGeometryElementMaterial* matElement = mesh->GetElementMaterial(); + int32_t polyCount = 0; + for (uint32_t tr = firstIdx; tr < lastIdx; tr++) + { + auto& geo = aResult.geometry[tr]; + const Nv::Blast::Vertex triVerts[3] = { geo.a, geo.b, geo.c }; + mesh->BeginPolygon(); + for (uint32_t k = 0; k < 3; ++k) + { + mesh->AddPolygon(tr * 3 + k); + FbxVector4 v, n; + FbxVector2 uv; + FbxUtils::VertexToFbx(triVerts[k], v, n, uv); + mesh->SetControlPointAt(v, tr * 3 + k); + + geNormal->GetDirectArray().Add(n); + geUV->GetDirectArray().Add(uv); + } + mesh->EndPolygon(); + int32_t material = (geo.materialId != MATERIAL_INTERIOR) ? ((geo.materialId < int32_t(mMaterials.size() - 1))? geo.materialId : 0) : int32_t(mMaterials.size() - 1); + matElement->GetIndexArray().SetAt(polyCount, material); + + if (smElement) + { + if (geo.userData == 0) + { + smElement->GetIndexArray().SetAt(polyCount, geo.smoothingGroup); + } + else + { + smElement->GetIndexArray().SetAt(polyCount, SMOOTHING_GROUP_INTERIOR); + } + } + + polyCount++; + + } + + if (!smElement) + { + //If no smoothing groups, generate them + FbxGeometryConverter fbxConv(mesh->GetFbxManager()); + if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) + { + fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); + } + } + + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) + { + createChunkRecursiveNonSkinned(meshName, i, meshNode, materials, aResult); + } +} + +uint32_t FbxFileWriter::addCollisionHulls(uint32_t chunkIndex, FbxDisplayLayer* displayLayer, FbxNode* parentNode, uint32_t hullsCount, CollisionHull** hulls) +{ + for (uint32_t hullId = 0; hullId < hullsCount; ++hullId) + { + std::stringstream namestream; + namestream.clear(); + namestream << "collisionHull_" << chunkIndex << "_" << hullId; + + FbxNode* collisionNode = FbxNode::Create(sdkManager.get(), namestream.str().c_str()); + + displayLayer->AddMember(collisionNode); + + //TODO: Remove this when tools are converted over + FbxProperty::Create(collisionNode, FbxIntDT, "ParentalChunkIndex"); + collisionNode->FindProperty("ParentalChunkIndex").Set(chunkIndex); + // + + namestream.clear(); + namestream << "collisionHullGeom_" << chunkIndex << "_" << hullId; + FbxMesh* meshAttr = FbxMesh::Create(sdkManager.get(), namestream.str().c_str()); + collisionNode->SetNodeAttribute(meshAttr); + parentNode->AddChild(collisionNode); + + auto mat = parentNode->EvaluateGlobalTransform().Inverse(); + auto centroid = worldChunkPivots.find(chunkIndex); + + if (centroid != worldChunkPivots.end()) + { + FbxVector4 c2 = mat.MultT(FbxVector4(centroid->second.x, centroid->second.y, centroid->second.z, 1.0f)); + //Don't mess with the root chunk pivot + collisionNode->LclTranslation.Set(c2); + } + parentNode->AddChild(collisionNode); + FbxAMatrix finalXForm = collisionNode->EvaluateGlobalTransform(); + + //Set the geo transform to inverse so we can use the world mesh coordinates + FbxAMatrix invFinalXForm = finalXForm.Inverse(); + collisionNode->SetGeometricTranslation(FbxNode::eSourcePivot, invFinalXForm.GetT()); + collisionNode->SetGeometricRotation(FbxNode::eSourcePivot, invFinalXForm.GetR()); + collisionNode->SetGeometricScaling(FbxNode::eSourcePivot, invFinalXForm.GetS()); + + + meshAttr->InitControlPoints(hulls[hullId]->pointsCount); + meshAttr->CreateElementNormal(); + FbxVector4* controlPoints = meshAttr->GetControlPoints(); + auto geNormal = meshAttr->GetElementNormal(); + geNormal->SetMappingMode(FbxGeometryElement::eByPolygon); + geNormal->SetReferenceMode(FbxGeometryElement::eDirect); + for (uint32_t i = 0; i < hulls[hullId]->pointsCount; ++i) + { + auto& pnts = hulls[hullId]->points[i]; + controlPoints->Set(pnts.x, pnts.y, pnts.z, 0.0); + controlPoints++; + } + + for (uint32_t i = 0; i < hulls[hullId]->polygonDataCount; ++i) + { + auto& poly = hulls[hullId]->polygonData[i]; + meshAttr->BeginPolygon(); + for (uint32_t j = 0; j < poly.mNbVerts; ++j) + { + meshAttr->AddPolygon(hulls[hullId]->indices[poly.mIndexBase + j]); + } + meshAttr->EndPolygon(); + FbxVector4 plane(poly.mPlane[0], poly.mPlane[1], poly.mPlane[2], 0); + geNormal->GetDirectArray().Add(plane); + } + } + return 1; +} + +uint32_t FbxFileWriter::createChunkRecursive(uint32_t currentCpIdx, uint32_t chunkIndex, FbxNode *meshNode, FbxNode* parentNode, FbxSkin* skin, const ExporterMeshData& meshData) +{ + auto chunks = NvBlastAssetGetChunks(meshData.asset, Nv::Blast::logLL); + const NvBlastChunk* chunk = &chunks[chunkIndex]; + physx::PxVec3 centroid = physx::PxVec3(chunk->centroid[0], chunk->centroid[1], chunk->centroid[2]); + + std::string boneName = FbxUtils::getChunkNodeName(chunkIndex).c_str(); + + FbxSkeleton* skelAttrib = FbxSkeleton::Create(sdkManager.get(), boneName.c_str()); + if (chunk->parentChunkIndex == UINT32_MAX) + { + skelAttrib->SetSkeletonType(FbxSkeleton::eRoot); + + // Change the centroid to origin + centroid = physx::PxVec3(0.0f); + } + else + { + skelAttrib->SetSkeletonType(FbxSkeleton::eLimbNode); + worldChunkPivots[chunkIndex] = centroid; + } + + FbxNode* boneNode = FbxNode::Create(sdkManager.get(), boneName.c_str()); + boneNode->SetNodeAttribute(skelAttrib); + + chunkNodes[chunkIndex] = boneNode; + + auto mat = parentNode->EvaluateGlobalTransform().Inverse(); + + FbxVector4 vec(0, 0, 0, 0); + FbxVector4 c2 = mat.MultT(vec); + + boneNode->LclTranslation.Set(c2); + + parentNode->AddChild(boneNode); + + std::ostringstream namestream; + 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(); + + std::vector<bool> addedVerticesFlag(mesh->GetControlPointsCount(), false); + + uint32_t* firstIdx = meshData.submeshOffsets + chunkIndex * meshData.submeshCount; + uint32_t cPolygCount = mesh->GetPolygonCount(); + int32_t addedVertices = 0; + for (uint32_t subMesh = 0; subMesh < meshData.submeshCount; ++subMesh) + { + for (uint32_t tr = *(firstIdx + subMesh); tr < *(firstIdx + subMesh + 1); tr += 3) + { + mesh->BeginPolygon(subMesh); + mesh->AddPolygon(meshData.posIndex[tr + 0]); + mesh->AddPolygon(meshData.posIndex[tr + 1]); + mesh->AddPolygon(meshData.posIndex[tr + 2]); + mesh->EndPolygon(); + for (uint32_t k = 0; k < 3; ++k) + { + geNormal->GetIndexArray().SetAt(currentCpIdx + addedVertices + k, meshData.normIndex[tr + k]); + geUV->GetIndexArray().SetAt(currentCpIdx + addedVertices + k, meshData.texIndex[tr + k]); + } + if (subMesh == 0) + { + matr->GetIndexArray().SetAt(cPolygCount, 0); + } + else + { + matr->GetIndexArray().SetAt(cPolygCount, 1); + } + cPolygCount++; + addedVertices += 3; + for (uint32_t k = 0; k < 3; ++k) + { + if (!addedVerticesFlag[meshData.posIndex[tr + k]]) + { + cluster->AddControlPointIndex(meshData.posIndex[tr + k], 1.0); + addedVerticesFlag[meshData.posIndex[tr + k]] = true; + } + } + } + } + mat = meshNode->EvaluateGlobalTransform(); + cluster->SetTransformMatrix(mat); + + mat = boneNode->EvaluateGlobalTransform(); + cluster->SetTransformLinkMatrix(mat); + + + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) + { + addedVertices += createChunkRecursive(currentCpIdx + addedVertices, i, meshNode, boneNode, skin, meshData); + } + + return addedVertices; + +} + +void FbxFileWriter::addControlPoints(FbxMesh* mesh, const ExporterMeshData& meshData) +{ + std::vector<uint32_t> vertices; + std::cout << "Adding control points" << std::endl; + std::vector<int32_t> mapping(meshData.positionsCount, -1); + for (uint32_t ch = 0; ch < meshData.meshCount; ++ch) + { + mapping.assign(meshData.positionsCount, -1); + for (uint32_t sb = 0; sb < meshData.submeshCount; ++sb) + { + uint32_t* first = meshData.submeshOffsets + ch * meshData.submeshCount + sb; + for (uint32_t pi = *first; pi < *(first+1); ++pi) + { + uint32_t p = meshData.posIndex[pi]; + if (mapping[p] == -1) + { + mapping[p] = (int)vertices.size(); + vertices.push_back(p); + meshData.posIndex[pi] = mapping[p]; + } + else + { + meshData.posIndex[pi] = mapping[p]; + } + } + } + } + mesh->InitControlPoints((int)vertices.size()); + FbxVector4* controlPoints = mesh->GetControlPoints(); + for (auto v : vertices) + { + auto& p = meshData.positions[v]; + *controlPoints = FbxVector4(p.x, p.y, p.z, 0); + ++controlPoints; + } + std::cout << "Adding control points: done" << std::endl; +} + +void FbxFileWriter::addBindPose() +{ + // Store the bind pose + //Just add all the nodes, it doesn't seem to do any harm and it stops Maya complaining about incomplete bind poses + FbxPose* pose = FbxPose::Create(sdkManager.get(), "BindPose"); + pose->SetIsBindPose(true); + + int nodeCount = mScene->GetNodeCount(); + for (int i = 0; i < nodeCount; i++) + { + FbxNode* node = mScene->GetNode(i); + FbxMatrix bindMat = node->EvaluateGlobalTransform(); + + pose->Add(node, bindMat); + } + + mScene->AddPose(pose); +} + +bool FbxFileWriter::saveToFile(const char* assetName, const char* outputPath) +{ + + addBindPose(); + + FbxIOSettings* ios = FbxIOSettings::Create(sdkManager.get(), IOSROOT); + // Set some properties on the io settings + + sdkManager->SetIOSettings(ios); + + sdkManager->GetIOSettings()->SetBoolProp(EXP_ASCIIFBX, bOutputFBXAscii); + + + FbxExporter* exporter = FbxExporter::Create(sdkManager.get(), "Scene Exporter"); + exporter->SetFileExportVersion(FBX_2012_00_COMPATIBLE); + + int lFormat; + + if (bOutputFBXAscii) + { + lFormat = sdkManager->GetIOPluginRegistry()->FindWriterIDByDescription("FBX ascii (*.fbx)"); + } + else + { + lFormat = sdkManager->GetIOPluginRegistry()->FindWriterIDByDescription("FBX binary (*.fbx)"); + } + + auto path = std::string(outputPath) + "\\" + assetName + ".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(mScene); + + 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::appendMesh(const ExporterMeshData& meshData, const char* assetName, bool nonSkinned) +{ + createMaterials(meshData); + + if (nonSkinned) + { + return appendNonSkinnedMesh(meshData, assetName); + } + + /** + Get polygon count + */ + uint32_t polygCount = meshData.submeshOffsets[meshData.meshCount * meshData.submeshCount] / 3; + + 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(mScene, "meshnode"); + meshNode->SetNodeAttribute(mesh); + meshNode->SetShadingMode(FbxNode::eTextureShading); + + FbxNode* lRootNode = mScene->GetRootNode(); + + mRenderLayer->AddMember(meshNode); + + for (uint32_t i = 0; i < mMaterials.size(); ++i) + { + meshNode->AddMaterial(mMaterials[i]); + } + + 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, meshData); + + auto normalsElem = mesh->GetElementNormal(); + for (uint32_t i = 0; i < meshData.normalsCount; ++i) + { + auto& n = meshData.normals[i]; + normalsElem->GetDirectArray().Add(FbxVector4(n.x, n.y, n.z, 0)); + } + auto uvsElem = mesh->GetElementUV("diffuseElement"); + for (uint32_t i = 0; i < meshData.uvsCount; ++i) + { + auto& uvs = meshData.uvs[i]; + uvsElem->GetDirectArray().Add(FbxVector2(uvs.x, uvs.y)); + } + + FbxGeometryElementMaterial* matElement = mesh->CreateElementMaterial(); + matElement->SetMappingMode(FbxGeometryElement::eByPolygon); + matElement->SetReferenceMode(FbxGeometryElement::eIndexToDirect); + + + matElement->GetIndexArray().SetCount(polygCount); + normalsElem->GetIndexArray().SetCount(polygCount * 3); + uvsElem->GetIndexArray().SetCount(polygCount * 3); + + + std::cout << "Create chunks recursive" << std::endl; + + //In order for Maya to correctly convert the axis of a skinned model there must be a common root node between the skeleton and the model + FbxNode* sceneRootNode = FbxNode::Create(sdkManager.get(), "sceneRoot"); + lRootNode->AddChild(sceneRootNode); + sceneRootNode->AddChild(meshNode); + + //UE4 cannot hide the root bone, so add a dummy chunk so chunk0 is not the root + FbxNode* skelRootNode = FbxNode::Create(sdkManager.get(), "root"); + FbxSkeleton* skelAttrib = FbxSkeleton::Create(sdkManager.get(), "SkelRootAttrib"); + skelAttrib->SetSkeletonType(FbxSkeleton::eRoot); + skelRootNode->SetNodeAttribute(skelAttrib); + + sceneRootNode->AddChild(skelRootNode); + + // 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(meshData.asset, Nv::Blast::logLL); + auto chunks = NvBlastAssetGetChunks(meshData.asset, Nv::Blast::logLL); + 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, skelRootNode, skin, meshData); + cpIdx += addedCps; + } + } + + if (!mesh->GetElementSmoothing()) + { + //If no smoothing groups, generate them + FbxGeometryConverter fbxConv(mesh->GetFbxManager()); + if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) + { + fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); + } + } + + if (meshData.hulls != nullptr) + { + return appendCollisionMesh(chunkCount, meshData.hullsOffsets, meshData.hulls, assetName); + } + return true; +} + diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h new file mode 100644 index 0000000..985b32c --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h @@ -0,0 +1,134 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTERFBXWRITER_H +#define NVBLASTEXTEXPORTERFBXWRITER_H + +#include "NvBlastExtExporter.h" +#include <memory> +#include <vector> +#include <map> + +namespace fbxsdk +{ + class FbxScene; + class FbxNode; + class FbxMesh; + class FbxSkin; + class FbxManager; + class FbxSurfaceMaterial; + class FbxDisplayLayer; +} + +struct NvBlastAsset; + +namespace Nv +{ +namespace Blast +{ +class Mesh; +struct Triangle; +struct CollisionHull; + +class FbxFileWriter : public IMeshFileWriter +{ +public: + + /** + Initialize FBX sdk and create scene. + */ + FbxFileWriter(); + //~FbxFileWriter() = default; + + virtual void release() override; + + /** + Get current scene; + */ + fbxsdk::FbxScene* getScene(); + + /** + Append rendermesh to scene. Meshes constructed from arrays of triangles. + */ + virtual bool appendMesh(const AuthoringResult& aResult, const char* assetName, bool nonSkinned) override; + + /** + Append rendermesh to scene. Meshes constructed from arrays of vertex data (position, normal, uvs) and indices. + Position, normal and uv has separate index arrays. + */ + virtual bool appendMesh(const ExporterMeshData& meshData, const char* assetName, bool nonSkinned) override; + + /** + Save scene to file. + */ + virtual bool saveToFile(const char* assetName, const char* outputPath) override; + + /** + Set true if FBX should be saved in ASCII mode. + */ + bool bOutputFBXAscii; + +private: + std::vector<fbxsdk::FbxSurfaceMaterial*> mMaterials; + fbxsdk::FbxScene* mScene; + fbxsdk::FbxDisplayLayer* mRenderLayer; + + //TODO we should track for every memory allocation and deallocate it not only for sdkManager + std::shared_ptr<fbxsdk::FbxManager> sdkManager; + std::map<uint32_t, fbxsdk::FbxNode*> chunkNodes; + std::map<uint32_t, physx::PxVec3> worldChunkPivots; + + bool appendNonSkinnedMesh(const AuthoringResult& aResult, const char* assetName); + bool appendNonSkinnedMesh(const ExporterMeshData& meshData, const char* assetName); + void createMaterials(const ExporterMeshData& meshData); + void createMaterials(const AuthoringResult& aResult); + + /** + Append collision geometry to scene. Each node with collision geometry has "ParentalChunkIndex" property, which contain index of chunk + which this collision geometry belongs to. + */ + bool appendCollisionMesh(uint32_t meshCount, uint32_t* offsets, CollisionHull** hulls, const char* assetName); + + uint32_t addCollisionHulls(uint32_t chunkIndex, fbxsdk::FbxDisplayLayer* displayLayer, fbxsdk::FbxNode* parentNode, uint32_t hullsCount, CollisionHull** hulls); + uint32_t createChunkRecursive(uint32_t currentCpIdx, uint32_t chunkIndex, fbxsdk::FbxNode *meshNode, fbxsdk::FbxNode* parentNode, fbxsdk::FbxSkin* skin, const AuthoringResult& aResult); + uint32_t createChunkRecursive(uint32_t currentCpIdx, uint32_t chunkIndex, fbxsdk::FbxNode *meshNode, fbxsdk::FbxNode* parentNode, fbxsdk::FbxSkin* skin, const ExporterMeshData& meshData); + + void createChunkRecursiveNonSkinned(const std::string& meshName, uint32_t chunkIndex, fbxsdk::FbxNode* parentNode, + const std::vector<fbxsdk::FbxSurfaceMaterial*>& materials, const AuthoringResult& aResult); + + void createChunkRecursiveNonSkinned(const std::string& meshName, uint32_t chunkIndex, fbxsdk::FbxNode* parentNode, + const std::vector<fbxsdk::FbxSurfaceMaterial*>& materials, const ExporterMeshData& meshData); + + void addControlPoints(fbxsdk::FbxMesh* mesh, const ExporterMeshData& meshData); + void addBindPose(); +}; + +} +} + +#endif // NVBLASTEXTEXPORTERFBXWRITER_H
\ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterJsonCollision.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterJsonCollision.cpp new file mode 100644 index 0000000..c19c931 --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterJsonCollision.cpp @@ -0,0 +1,107 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#include "NvBlastExtExporterJsonCollision.h" +#include "NvBlastExtAuthoringTypes.h" +#include <PxVec3.h> +#include <iostream> +#include <sstream> +#include <fstream> +#include <iomanip> + +#define JS_NAME(name) "\"" << name << "\": " + +using namespace Nv::Blast; + + +void serializaHullPolygon(std::ofstream& stream, const CollisionHull::HullPolygon& p, uint32_t indent) +{ + std::string sindent(indent, '\t'); + std::string bindent(indent + 1, '\t'); + stream << sindent << "{\n" << + bindent << JS_NAME("mIndexBase") << p.mIndexBase << ",\n" << + bindent << JS_NAME("mPlane") << "[" << p.mPlane[0] << ", " << p.mPlane[1] << ", " << p.mPlane[2] << ", " << p.mPlane[3] << "],\n" << + bindent << JS_NAME("mNbVerts") << p.mNbVerts << "\n" << + sindent << "}"; +} +void serializeCollisionHull(std::ofstream& stream, const CollisionHull& hl, uint32_t indent) +{ + std::string sindent(indent, '\t'); + std::string bindent(indent + 1, '\t'); + + stream << sindent << "{\n" << bindent << JS_NAME("indices") << "["; + for (uint32_t i = 0; i < hl.indicesCount; ++i) + { + stream << hl.indices[i]; + if (i < hl.indicesCount - 1) stream << ", "; + } + stream << "],\n"; + stream << bindent << JS_NAME("points") << "["; + for (uint32_t i = 0; i < hl.pointsCount; ++i) + { + auto& p = hl.points[i]; + stream << p.x << ", " << p.y << ", " << p.z; + if (i < hl.pointsCount - 1) stream << ", "; + } + stream << "],\n"; + stream << bindent << JS_NAME("polygonData") << "[\n"; + for (uint32_t i = 0; i < hl.polygonDataCount; ++i) + { + serializaHullPolygon(stream, hl.polygonData[i], indent + 1); + if (i < hl.polygonDataCount - 1) stream << ", "; + stream << "\n"; + } + stream << bindent << "]\n"; + stream << sindent << "}"; +} + +bool JsonCollisionExporter::writeCollision(const char* path, uint32_t meshCount, const uint32_t* meshOffsets, const CollisionHull* hulls) +{ + std::ofstream stream(path, std::ios::out); + stream << std::fixed << std::setprecision(8); + if (!stream.is_open()) + { + std::cout << "Can't open output stream" << std::endl; + return false; + } + + stream << "{\n" << "\t" << JS_NAME("CollisionData") << "[\n"; + for (uint32_t i = 0; i < meshCount; ++i) + { + stream << "\t\t" << "[\n"; + for (uint32_t j = meshOffsets[i]; j < meshOffsets[i + 1]; ++j) + { + serializeCollisionHull(stream, hulls[j], 3); + stream << ((j < meshOffsets[i + 1] - 1) ? ",\n" : "\n"); + } + stream << "\t\t" << ((i < meshCount - 1) ? "], \n" : "]\n"); + } + stream << "\t]\n}"; + stream.close(); + return true; +}; diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp new file mode 100644 index 0000000..95efb3e --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp @@ -0,0 +1,130 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#include "NvBlastExtExporterObjReader.h" + +#pragma warning(push) +#pragma warning(disable:4706) +#pragma warning(disable:4702) +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" +#pragma warning(pop) + + +#include <iostream> +#include "PxVec3.h" +#include "PxVec2.h" +#include "NvBlastExtAuthoringMesh.h" + +using physx::PxVec3; +using physx::PxVec2; +using namespace Nv::Blast; + +ObjFileReader::ObjFileReader() +{ +} + +void ObjFileReader::release() +{ + delete this; +} + +void ObjFileReader::loadFromFile(const char* 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); + // can't load? + if (!ret) + { + return; + } + if (shapes.size() > 1) + { + std::cout << "Can load only one object per mesh" << std::endl; + } + + mVertexPositions.clear(); + mVertexNormals.clear(); + mVertexUv.clear(); + mIndices.clear(); + + auto& psVec = shapes[0].mesh.positions; + for (uint32_t i = 0; i < psVec.size() / 3; ++i) + { + mVertexPositions.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) + { + mVertexNormals.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) + { + mVertexUv.push_back(PxVec2(txVec[i * 2], txVec[i * 2 + 1])); + } + + mIndices = shapes[0].mesh.indices; + +} + + +bool ObjFileReader::isCollisionLoaded() +{ + return false; +}; + + +uint32_t ObjFileReader::getCollision(uint32_t*& hullsOffset, Nv::Blast::CollisionHull** hulls) +{ + NV_UNUSED(hulls); + return false; +}; + +physx::PxVec3* ObjFileReader::getPositionArray() +{ + return mVertexPositions.data(); +}; + +physx::PxVec3* ObjFileReader::getNormalsArray() +{ + return mVertexNormals.data(); +}; + +physx::PxVec2* ObjFileReader::getUvArray() +{ + return mVertexUv.data(); +}; + +uint32_t* ObjFileReader::getIndexArray() +{ + return mIndices.data(); +};
\ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h new file mode 100644 index 0000000..f80f8f9 --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h @@ -0,0 +1,122 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTEROBJREADER_H +#define NVBLASTEXTEXPORTEROBJREADER_H +#include <memory> +#include <string> +#include <vector> +#include "NvBlastExtExporter.h" + +namespace Nv +{ +namespace Blast +{ +class Mesh; + +class ObjFileReader : public IMeshFileReader +{ +public: + ObjFileReader(); + ~ObjFileReader() = default; + + virtual void release() override; + + /* + Load from the specified file path, returning a mesh or nullptr if failed + */ + virtual void loadFromFile(const char* filename) override; + + virtual uint32_t getVerticesCount() const override + { + return mVertexPositions.size(); + } + + virtual uint32_t getIdicesCount() const override + { + return mIndices.size(); + } + + /** + Check whether file contained an collision geometry + */ + virtual bool isCollisionLoaded() override; + + /** + Retrieve collision geometry if it exist + */ + virtual uint32_t getCollision(uint32_t*& hullsOffset, Nv::Blast::CollisionHull** hulls) override; + + /** + Get loaded vertex positions + */ + virtual physx::PxVec3* getPositionArray() override; + /** + Get loaded vertex normals + */ + virtual physx::PxVec3* getNormalsArray() override; + /** + Get loaded vertex uv-coordinates + */ + virtual physx::PxVec2* getUvArray() override; + /** + Get loaded triangle indices + */ + virtual uint32_t* getIndexArray() override; + + /** + Get loaded per triangle material ids. Currently not supported by OBJ. + */ + int32_t* getMaterialIds() override { return nullptr; }; + + /** + Get loaded per triangle smoothing groups. Currently not supported by OBJ. + */ + int32_t* getSmoothingGroups() override { return nullptr; }; + + /** + Get material name. Currently not supported by OBJ. + */ + char* getMaterialName(int32_t id) override { return nullptr; } + + /** + Get material count. + */ + int32_t getMaterialCount() { return 0; }; + +private: + std::vector<physx::PxVec3> mVertexPositions; + std::vector<physx::PxVec3> mVertexNormals; + std::vector<physx::PxVec2> mVertexUv; + std::vector<uint32_t> mIndices; +}; + +} +} + +#endif // NVBLASTEXTEXPORTEROBJREADER_H
\ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp new file mode 100644 index 0000000..a49e28f --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp @@ -0,0 +1,188 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#include "NvBlastExtExporterObjWriter.h" +#include <PxVec3.h> +#include <sstream> +#include "NvBlastExtAuthoringTypes.h" +#include "NvBlastExtAuthoringMesh.h" + + +using namespace physx; +using namespace Nv::Blast; + +char* gTexPath = ""; + +void ObjFileWriter::release() +{ + delete this; +} + +bool ObjFileWriter::appendMesh(const AuthoringResult& aResult, const char* /*assetName*/, bool /*nonSkinned*/) +{ + mMeshData = std::shared_ptr<ExporterMeshData>(new ExporterMeshData(), [](ExporterMeshData* md) + { + //delete[] md->hulls; + //delete[] md->hullsOffsets; + delete[] md->normals; + //delete[] md->normIndex; + delete[] md->posIndex; + delete[] md->positions; + delete[] md->submeshOffsets; + //delete[] md->texIndex; + delete[] md->submeshNames; + delete[] md->uvs; + delete md; + }); + ExporterMeshData& md = *mMeshData.get(); + uint32_t triCount = aResult.geometryOffset[aResult.chunkCount]; + md.meshCount = aResult.chunkCount; + md.submeshOffsets = new uint32_t[md.meshCount + 1]; + for (uint32_t i = 0; i < md.meshCount + 1; i++) + { + md.submeshOffsets[i] = aResult.geometryOffset[i] * 3; + } + //md.submeshOffsets = md.meshOffsets; + md.submeshCount = 1; + //md.indicesCount = triCount * 3; + md.positionsCount = triCount * 3; + md.normalsCount = md.positionsCount; + md.uvsCount = md.positionsCount; + md.positions = new PxVec3[md.positionsCount]; + md.normals = new PxVec3[md.normalsCount]; + md.uvs = new PxVec2[md.uvsCount]; + md.posIndex = new uint32_t[triCount * 3]; + md.normIndex = md.posIndex; + md.texIndex = md.posIndex; + md.submeshNames = new const char*[1]{ gTexPath }; + for (uint32_t vc = 0; vc < triCount; ++vc) + { + Triangle& tri = aResult.geometry[vc]; + uint32_t i = vc * 3; + md.positions[i+0] = tri.a.p; + md.positions[i+1] = tri.b.p; + md.positions[i+2] = tri.c.p; + + md.normals[i+0] = tri.a.n; + md.normals[i+1] = tri.b.n; + md.normals[i+2] = tri.c.n; + + md.uvs[i+0] = tri.a.uv[0]; + md.uvs[i+1] = tri.b.uv[0]; + md.uvs[i+2] = tri.c.uv[0]; + + md.posIndex[i + 0] = i + 0; + md.posIndex[i + 1] = i + 1; + md.posIndex[i + 2] = i + 2; + } + return true; +} + +bool ObjFileWriter::appendMesh(const ExporterMeshData& meshData, const char* /*assetName*/, bool /*nonSkinned*/) +{ + mMeshData = std::shared_ptr<ExporterMeshData>(new ExporterMeshData(meshData)); + return true; +} + +bool ObjFileWriter::saveToFile(const char* assetName, const char* outputPath) +{ + if (mMeshData.get() == nullptr) + { + return false; + } + const ExporterMeshData& md = *mMeshData.get(); + + uint32_t chunkCount = md.meshCount; + + // export materials (mtl file) + { + std::ostringstream mtlFilePath; + mtlFilePath << outputPath << "\\" << assetName << ".mtl"; + FILE* f = fopen(mtlFilePath.str().c_str(), "w"); + if (!f) + return false; + + for (uint32_t submeshIndex = 0; submeshIndex < md.submeshCount; ++submeshIndex) + { + fprintf(f, "newmtl mat%d\n", submeshIndex); + fprintf(f, "\tmap_Kd %s\n", md.submeshNames[submeshIndex]); + fprintf(f, "\n"); + } + + fclose(f); + } + + /// Export geometry to *.obj file + { + std::ostringstream objFilePath; + objFilePath << outputPath << "\\" << assetName << ".obj"; + FILE* f = fopen(objFilePath.str().c_str(), "w"); + if (!f) + return false; + + fprintf(f, "mtllib %s.mtl\n", assetName); + fprintf(f, "o frac \n"); + + + /// Write compressed vertices + for (uint32_t i = 0; i < md.positionsCount; ++i) + { + fprintf(f, "v %.4f %.4f %.4f\n", md.positions[i].x, md.positions[i].y, md.positions[i].z); + } + for (uint32_t i = 0; i < md.normalsCount; ++i) + { + fprintf(f, "vn %.4f %.4f %.4f\n", md.normals[i].x, md.normals[i].y, md.normals[i].z); + } + for (uint32_t i = 0; i < md.uvsCount; ++i) + { + fprintf(f, "vt %.4f %.4f\n", md.uvs[i].x, md.uvs[i].y); + } + + for (uint32_t chunkIndex = 0; chunkIndex < chunkCount; ++chunkIndex) + { + for (uint32_t submeshIndex = 0; submeshIndex < md.submeshCount; ++submeshIndex) + { + uint32_t firstIdx = md.submeshOffsets[chunkIndex * md.submeshCount + submeshIndex]; + uint32_t lastIdx = md.submeshOffsets[chunkIndex * md.submeshCount + submeshIndex + 1]; + fprintf(f, "g %d_%d \n", chunkIndex, submeshIndex); + fprintf(f, "usemtl mat%d\n", submeshIndex); + + for (uint32_t i = firstIdx; i < lastIdx; i += 3) + { + fprintf(f, "f %d/%d/%d ", md.posIndex[i] + 1, md.texIndex[i] + 1, md.normIndex[i] + 1); + fprintf(f, "%d/%d/%d ", md.posIndex[i + 1] + 1, md.texIndex[i + 1] + 1, md.normIndex[i + 1] + 1); + fprintf(f, "%d/%d/%d \n", md.posIndex[i + 2] + 1, md.texIndex[i + 2] + 1, md.normIndex[i + 2] + 1); + } + } + } + fclose(f); + } + return true; + +} + diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h new file mode 100644 index 0000000..3152d42 --- /dev/null +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h @@ -0,0 +1,73 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2017 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTEROBJWRITER_H +#define NVBLASTEXTEXPORTEROBJWRITER_H + +#include "NvBlastExtExporter.h" +#include <memory> +#include <vector> +#include <PxVec2.h> +#include <PxVec3.h> + +struct NvBlastAsset; + +namespace Nv +{ +namespace Blast +{ + +class ObjFileWriter : public IMeshFileWriter +{ +public: + + ObjFileWriter() {}; + ~ObjFileWriter() = default; + + virtual void release() override; + + virtual bool appendMesh(const AuthoringResult& aResult, const char* assetName, bool nonSkinned) override; + + /** + Append rendermesh to scene. Meshes constructed from arrays of vertices and indices + */ + virtual bool appendMesh(const ExporterMeshData& meshData, const char* assetName, bool nonSkinned) override; + + /** + Save scene to file. + */ + virtual bool saveToFile(const char* assetName, const char* outputPath) override; + +private: + std::shared_ptr<ExporterMeshData> mMeshData; +}; + +} +} + +#endif // NVBLASTEXTEXPORTEROBJWRITER_H
\ No newline at end of file |