From 7115f60b91b5717d90f643fd692010905c7004db Mon Sep 17 00:00:00 2001 From: Bryan Galdrikian Date: Thu, 31 May 2018 11:36:08 -0700 Subject: Blast 1.1.3. See docs/release_notes.txt. --- .../exporter/include/NvBlastExtExporter.h | 534 ++-- .../include/NvBlastExtExporterJsonCollision.h | 134 +- .../exporter/source/NvBlastExtExporter.cpp | 116 +- .../source/NvBlastExtExporterFbxReader.cpp | 1272 ++++----- .../exporter/source/NvBlastExtExporterFbxReader.h | 446 +-- .../exporter/source/NvBlastExtExporterFbxUtils.cpp | 416 +-- .../exporter/source/NvBlastExtExporterFbxUtils.h | 136 +- .../source/NvBlastExtExporterFbxWriter.cpp | 2880 ++++++++++---------- .../exporter/source/NvBlastExtExporterFbxWriter.h | 286 +- .../source/NvBlastExtExporterJsonCollision.cpp | 244 +- .../source/NvBlastExtExporterObjReader.cpp | 322 +-- .../exporter/source/NvBlastExtExporterObjReader.h | 250 +- .../source/NvBlastExtExporterObjWriter.cpp | 530 ++-- .../exporter/source/NvBlastExtExporterObjWriter.h | 158 +- 14 files changed, 3882 insertions(+), 3842 deletions(-) mode change 100644 => 100755 sdk/extensions/exporter/include/NvBlastExtExporter.h mode change 100644 => 100755 sdk/extensions/exporter/include/NvBlastExtExporterJsonCollision.h mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporter.cpp mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.cpp mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.cpp mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.h mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterJsonCollision.cpp mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp mode change 100644 => 100755 sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h (limited to 'sdk/extensions/exporter') diff --git a/sdk/extensions/exporter/include/NvBlastExtExporter.h b/sdk/extensions/exporter/include/NvBlastExtExporter.h old mode 100644 new mode 100755 index 62ba04d..2718f3c --- a/sdk/extensions/exporter/include/NvBlastExtExporter.h +++ b/sdk/extensions/exporter/include/NvBlastExtExporter.h @@ -1,268 +1,268 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#ifndef NVBLASTEXTEXPORTER_H -#define NVBLASTEXTEXPORTER_H - -#include "NvBlastTypes.h" - -struct NvBlastAsset; - -namespace physx -{ -class PxVec2; -class PxVec3; -} - -namespace Nv -{ -namespace Blast -{ -struct AuthoringResult; -struct CollisionHull; - -struct Material -{ - const char* name; - const char* diffuse_tex; -}; - -struct ExporterMeshData -{ - NvBlastAsset* asset; //Blast asset - - uint32_t positionsCount; //Number of positions - - uint32_t normalsCount; //Number of normals - - uint32_t uvsCount; //Number of textures uv - - physx::PxVec3* positions; //Array of positions - - physx::PxVec3* normals; //Array of normals - - physx::PxVec2* uvs; //Array of textures uv - - uint32_t meshCount; //Number of meshes (chunks) - - uint32_t submeshCount; //Number of submeshes - - Material* submeshMats; - - - /** - Indices offsets for posIndex, normIndex and texIndex - First position index: posIndex[submeshOffsets[meshId * submeshCount + submeshId]] - Total number of indices: submeshOffsets[meshCount * submeshCount] - */ - uint32_t* submeshOffsets; - - uint32_t* posIndex; //Array of position indices - - uint32_t* normIndex; //Array of normals indices - - uint32_t* texIndex; //Array of texture indices - - - /** - Hull offsets. Contains meshCount + 1 element. - First hull for i-th mesh: hulls[hullsOffsets[i]] - hullsOffsets[meshCount+1] is total number of hulls - */ - uint32_t* hullsOffsets; - - CollisionHull** hulls; //Array of pointers to hull for all meshes -}; - -/** - An interface for Blast mesh file reader -*/ -class IMeshFileReader -{ -public: - - /** - Delete this object - */ - virtual void release() = 0; - - /* - Load from the specified file path - */ - virtual void loadFromFile(const char* filename) = 0; - - /** - Number of loaded vertices - */ - virtual uint32_t getVerticesCount() const = 0; - - /** - Number of loaded indices - */ - virtual uint32_t getIndicesCount() const = 0; - - /** - Get loaded vertex positions - */ - virtual physx::PxVec3* getPositionArray() = 0; - - /** - Get loaded vertex normals - */ - virtual physx::PxVec3* getNormalsArray() = 0; - - /** - Get loaded vertex uv-coordinates - */ - virtual physx::PxVec2* getUvArray() = 0; - - /** - Get loaded per triangle material ids. - */ - virtual int32_t* getMaterialIds() = 0; - - /** - Get loaded per triangle smoothing groups. - */ - virtual int32_t* getSmoothingGroups() = 0; - - /** - Get material name. - */ - virtual const char* getMaterialName(int32_t id) = 0; - - /** - Get material count. - */ - virtual int32_t getMaterialCount() = 0; - - - - /** - Get loaded triangle indices - */ - virtual uint32_t* getIndexArray() = 0; - - - /** - Check whether file contained an collision geometry - */ - virtual bool isCollisionLoaded() = 0; - - /** - Retrieve collision geometry if it exist - \note User should call NVBLAST_FREE for hulls and hullsOffset when it not needed anymore - - \param[out] hullsOffset Array of hull offsets for hulls array. The size is meshCount + 1. - \param[out] hulls Array of hull. The first i-th mesh hull: hulls[hullsOffset[i]]. The size is written to hullsOffset[meshCount] - \return Number of meshes (meshCount) - */ - virtual uint32_t getCollision(uint32_t*& hullsOffset, CollisionHull**& hulls) = 0; - -}; - -/** - An interface for fbx file reader -*/ -class IFbxFileReader : public IMeshFileReader -{ -public: - /** - Retrieve bone influence if it exist, this is a bone index for each vertex in the mesh - \note User should call NVBLAST_FREE for out when it not needed anymore - - \param[out] out Array of bone influences. - \return Number of bones influences (boneCount) - */ - virtual uint32_t getBoneInfluences(uint32_t*& out) = 0; - - /** - Return number of bones in fbx file - */ - virtual uint32_t getBoneCount() = 0; -}; - -/** - An interface for Blast mesh file writer -*/ -class IMeshFileWriter -{ -public: - - /** - Delete this object - */ - virtual void release() = 0; - - /** - Append rendermesh to scene. Meshes constructed from arrays of triangles. - */ - virtual bool appendMesh(const AuthoringResult& aResult, const char* assetName, bool nonSkinned = false) = 0; - - /** - Append rendermesh to scene. Meshes constructed from arrays of vertices and indices - */ - virtual bool appendMesh(const ExporterMeshData& meshData, const char* assetName, bool nonSkinned = false) = 0; - - /** - Save scene to file. - */ - virtual bool saveToFile(const char* assetName, const char* outputPath) = 0; - - /** - Set material index for interior surface. By default new material will be created; - */ - virtual void setInteriorIndex(int32_t index) = 0; -}; - -} -} - -/** - Creates an instance of IMeshFileReader for reading obj file. -*/ -NVBLAST_API Nv::Blast::IMeshFileReader* NvBlastExtExporterCreateObjFileReader(); - -/** - Creates an instance of IFbxFileReader for reading fbx file. -*/ -NVBLAST_API Nv::Blast::IFbxFileReader* NvBlastExtExporterCreateFbxFileReader(); - -/** - Creates an instance of IMeshFileWriter for writing obj file. -*/ -NVBLAST_API Nv::Blast::IMeshFileWriter* NvBlastExtExporterCreateObjFileWriter(); - -/** - Creates an instance of IMeshFileWriter for writing fbx file. - - \param[in] outputFBXAscii If true writes fbx in ascii format otherwise write in binary. -*/ -NVBLAST_API Nv::Blast::IMeshFileWriter* NvBlastExtExporterCreateFbxFileWriter(bool outputFBXAscii = false); - +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTER_H +#define NVBLASTEXTEXPORTER_H + +#include "NvBlastTypes.h" + +struct NvBlastAsset; + +namespace physx +{ +class PxVec2; +class PxVec3; +} + +namespace Nv +{ +namespace Blast +{ +struct AuthoringResult; +struct CollisionHull; + +struct Material +{ + const char* name; + const char* diffuse_tex; +}; + +struct ExporterMeshData +{ + NvBlastAsset* asset; //Blast asset + + uint32_t positionsCount; //Number of positions + + uint32_t normalsCount; //Number of normals + + uint32_t uvsCount; //Number of textures uv + + physx::PxVec3* positions; //Array of positions + + physx::PxVec3* normals; //Array of normals + + physx::PxVec2* uvs; //Array of textures uv + + uint32_t meshCount; //Number of meshes (chunks) + + uint32_t submeshCount; //Number of submeshes + + Material* submeshMats; + + + /** + Indices offsets for posIndex, normIndex and texIndex + First position index: posIndex[submeshOffsets[meshId * submeshCount + submeshId]] + Total number of indices: submeshOffsets[meshCount * submeshCount] + */ + uint32_t* submeshOffsets; + + uint32_t* posIndex; //Array of position indices + + uint32_t* normIndex; //Array of normals indices + + uint32_t* texIndex; //Array of texture indices + + + /** + Hull offsets. Contains meshCount + 1 element. + First hull for i-th mesh: hulls[hullsOffsets[i]] + hullsOffsets[meshCount+1] is total number of hulls + */ + uint32_t* hullsOffsets; + + CollisionHull** hulls; //Array of pointers to hull for all meshes +}; + +/** + An interface for Blast mesh file reader +*/ +class IMeshFileReader +{ +public: + + /** + Delete this object + */ + virtual void release() = 0; + + /* + Load from the specified file path + */ + virtual void loadFromFile(const char* filename) = 0; + + /** + Number of loaded vertices + */ + virtual uint32_t getVerticesCount() const = 0; + + /** + Number of loaded indices + */ + virtual uint32_t getIndicesCount() const = 0; + + /** + Get loaded vertex positions + */ + virtual physx::PxVec3* getPositionArray() = 0; + + /** + Get loaded vertex normals + */ + virtual physx::PxVec3* getNormalsArray() = 0; + + /** + Get loaded vertex uv-coordinates + */ + virtual physx::PxVec2* getUvArray() = 0; + + /** + Get loaded per triangle material ids. + */ + virtual int32_t* getMaterialIds() = 0; + + /** + Get loaded per triangle smoothing groups. + */ + virtual int32_t* getSmoothingGroups() = 0; + + /** + Get material name. + */ + virtual const char* getMaterialName(int32_t id) = 0; + + /** + Get material count. + */ + virtual int32_t getMaterialCount() = 0; + + + + /** + Get loaded triangle indices + */ + virtual uint32_t* getIndexArray() = 0; + + + /** + Check whether file contained an collision geometry + */ + virtual bool isCollisionLoaded() = 0; + + /** + Retrieve collision geometry if it exist + \note User should call NVBLAST_FREE for hulls and hullsOffset when it not needed anymore + + \param[out] hullsOffset Array of hull offsets for hulls array. The size is meshCount + 1. + \param[out] hulls Array of hull. The first i-th mesh hull: hulls[hullsOffset[i]]. The size is written to hullsOffset[meshCount] + \return Number of meshes (meshCount) + */ + virtual uint32_t getCollision(uint32_t*& hullsOffset, CollisionHull**& hulls) = 0; + +}; + +/** + An interface for fbx file reader +*/ +class IFbxFileReader : public IMeshFileReader +{ +public: + /** + Retrieve bone influence if it exist, this is a bone index for each vertex in the mesh + \note User should call NVBLAST_FREE for out when it not needed anymore + + \param[out] out Array of bone influences. + \return Number of bones influences (boneCount) + */ + virtual uint32_t getBoneInfluences(uint32_t*& out) = 0; + + /** + Return number of bones in fbx file + */ + virtual uint32_t getBoneCount() = 0; +}; + +/** + An interface for Blast mesh file writer +*/ +class IMeshFileWriter +{ +public: + + /** + Delete this object + */ + virtual void release() = 0; + + /** + Append rendermesh to scene. Meshes constructed from arrays of triangles. + */ + virtual bool appendMesh(const AuthoringResult& aResult, const char* assetName, bool nonSkinned = false) = 0; + + /** + Append rendermesh to scene. Meshes constructed from arrays of vertices and indices + */ + virtual bool appendMesh(const ExporterMeshData& meshData, const char* assetName, bool nonSkinned = false) = 0; + + /** + Save scene to file. + */ + virtual bool saveToFile(const char* assetName, const char* outputPath) = 0; + + /** + Set material index for interior surface. By default new material will be created; + */ + virtual void setInteriorIndex(int32_t index) = 0; +}; + +} +} + +/** + Creates an instance of IMeshFileReader for reading obj file. +*/ +NVBLAST_API Nv::Blast::IMeshFileReader* NvBlastExtExporterCreateObjFileReader(); + +/** + Creates an instance of IFbxFileReader for reading fbx file. +*/ +NVBLAST_API Nv::Blast::IFbxFileReader* NvBlastExtExporterCreateFbxFileReader(); + +/** + Creates an instance of IMeshFileWriter for writing obj file. +*/ +NVBLAST_API Nv::Blast::IMeshFileWriter* NvBlastExtExporterCreateObjFileWriter(); + +/** + Creates an instance of IMeshFileWriter for writing fbx file. + + \param[in] outputFBXAscii If true writes fbx in ascii format otherwise write in binary. +*/ +NVBLAST_API Nv::Blast::IMeshFileWriter* NvBlastExtExporterCreateFbxFileWriter(bool outputFBXAscii = false); + #endif //NVBLASTEXTEXPORTER_H \ No newline at end of file diff --git a/sdk/extensions/exporter/include/NvBlastExtExporterJsonCollision.h b/sdk/extensions/exporter/include/NvBlastExtExporterJsonCollision.h old mode 100644 new mode 100755 index 08fe3ac..65ebb93 --- a/sdk/extensions/exporter/include/NvBlastExtExporterJsonCollision.h +++ b/sdk/extensions/exporter/include/NvBlastExtExporterJsonCollision.h @@ -1,62 +1,72 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#ifndef NVBLASTEXTEXPORTERJSONCOLLISION_H -#define NVBLASTEXTEXPORTERJSONCOLLISION_H - -#include "NvBlastTypes.h" - -namespace Nv -{ -namespace Blast -{ - - -struct CollisionHull; - -/** - Serializes collision geometry to JSON format. -*/ -class JsonCollisionExporter -{ -public: - JsonCollisionExporter(){}; - - /** - Method creates file with given path and serializes given array of arrays of convex hulls to it in JSON format. - \param[in] path Output file path - \param[in] hulls Array of arrays of convex hull descriptors. Each array contain array of convex hulls for chunk (hulls[0] - convexes for chunk 0, etc.) - */ - bool writeCollision(const char* path, uint32_t meshCount, const uint32_t* meshOffsets, const CollisionHull* hulls); -}; - - -} // namespace Blast -} // namespace Nv - -#endif //NVBLASTEXTEXPORTERJSONCOLLISION_H \ No newline at end of file +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTERJSONCOLLISION_H +#define NVBLASTEXTEXPORTERJSONCOLLISION_H + +#include "NvBlastTypes.h" + +namespace Nv +{ +namespace Blast +{ + +struct CollisionHull; + +/** + Interface to object which serializes collision geometry to JSON format. +*/ +class IJsonCollisionExporter +{ +public: + /** + Delete this object + */ + virtual void release() = 0; + + /** + Method creates file with given path and serializes given array of arrays of convex hulls to it in JSON format. + \param[in] path Output file path. + \param[in] chunkCount The number of chunks, may be less than the number of collision hulls. + \param[in] hullOffsets Collision hull offsets. Contains chunkCount + 1 element. First collision hull for i-th chunk: hull[hullOffsets[i]]. hullOffsets[chunkCount+1] is total number of hulls. + \param[in] hulls Array of pointers to convex hull descriptors, contiguously grouped for chunk[0], chunk[1], etc. + */ + virtual bool writeCollision(const char* path, uint32_t chunkCount, const uint32_t* hullOffsets, const CollisionHull* const * hulls) = 0; +}; + +} // namespace Blast +} // namespace Nv + + +/** +Creates an instance of IMeshFileWriter for writing obj file. +*/ +NVBLAST_API Nv::Blast::IJsonCollisionExporter* NvBlastExtExporterCreateJsonCollisionExporter(); + + +#endif //NVBLASTEXTEXPORTERJSONCOLLISION_H diff --git a/sdk/extensions/exporter/source/NvBlastExtExporter.cpp b/sdk/extensions/exporter/source/NvBlastExtExporter.cpp old mode 100644 new mode 100755 index 405c4a4..790d408 --- a/sdk/extensions/exporter/source/NvBlastExtExporter.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporter.cpp @@ -1,58 +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) 2018 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; -} - +// 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) 2018 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 old mode 100644 new mode 100755 index edf6c16..99600fd --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.cpp @@ -1,636 +1,636 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - -#include "NvBlastExtExporterFbxReader.h" -#include "NvBlastExtExporterFbxUtils.h" -#include "NvBlastGlobals.h" -#include -#include -#include -#include -#include -#include - -#include "PxVec3.h" -#include "PxVec2.h" -#include "PxPlane.h" -#include "NvBlastExtAuthoringMesh.h" -#include "NvBlastExtAuthoringBondGenerator.h" -#include "NvBlastExtAuthoringCollisionBuilder.h" - -using physx::PxVec3; -using physx::PxVec2; - -using namespace Nv::Blast; - -FbxFileReader::FbxFileReader() -{ - mMeshCount = 0; - mChunkCount = 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 sdkManager = std::shared_ptr(FbxManager::Create(), [=](FbxManager* manager) - { - manager->Destroy(); - }); - - mChunkCount = 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); - } - - FbxGeometryConverter geoConverter(sdkManager.get()); - FbxDisplayLayer* collisionDisplayLayer = scene->FindMember(FbxUtils::getCollisionGeometryLayerName().c_str()); - - // Recurse the fbx tree and find all meshes - std::vector meshNodes; - getFbxMeshes(collisionDisplayLayer, scene->GetRootNode(), meshNodes); - - if (isCollisionLoaded()) - { - std::cout << "Collision geometry is found."; - getCollisionInternal(); - } - - std::cout << "Found " << meshNodes.size() << " meshes." << std::endl; - - if (meshNodes.size() > 1) - { - FbxArray tempMeshArray; - tempMeshArray.Resize((int)meshNodes.size()); - for (size_t i = 0; i < meshNodes.size(); i++) - { - FbxMesh* mesh = meshNodes[i]->GetMesh(); - if (mesh->GetDeformerCount(FbxDeformer::eSkin) != 0) - { - std::cerr << "Multi-part mesh " << meshNodes[i]->GetName() << " is already skinned, not sure what to do" << std::endl; - return; - } - //Add a one-bone skin so later when we merge meshes the connection to the chunk transform will stick, this handles the non-skinned layout of the FBX file - FbxSkin* skin = FbxSkin::Create(scene, (std::string(meshNodes[i]->GetName()) + "_skin").c_str()); - mesh->AddDeformer(skin); - FbxCluster* cluster = FbxCluster::Create(skin, (std::string(meshNodes[i]->GetName()) + "_cluster").c_str()); - skin->AddCluster(cluster); - cluster->SetLink(meshNodes[i]); - const int cpCount = mesh->GetControlPointsCount(); - cluster->SetControlPointIWCount(cpCount); - //Fully weight to the one bone - int* cpIdx = cluster->GetControlPointIndices(); - double* cpWeights = cluster->GetControlPointWeights(); - for (int cp = 0; cp < cpCount; cp++) - { - cpIdx[cp] = cp; - cpWeights[cp] = 1.0; - } - tempMeshArray.SetAt(int(i), meshNodes[i]); - } - meshNodes.resize(1); - meshNodes[0] = geoConverter.MergeMeshes(tempMeshArray, "MergedMesh", scene); - } - - if (meshNodes.empty()) - { - return; - } - - FbxNode* meshNode = meshNodes[0]; - FbxMesh* mesh = meshNode->GetMesh(); - - // Verify that the mesh is triangulated. - bool bAllTriangles = mesh->IsTriangleMesh(); - if (!bAllTriangles) - { - //try letting the FBX SDK triangulate it - geoConverter.Triangulate(mesh, true); - bAllTriangles = mesh->IsTriangleMesh(); - } - - int polyCount = mesh->GetPolygonCount(); - 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 positions; - std::vector normals; - std::vector uv; - std::vector indices; - - int* polyVertices = mesh->GetPolygonVertices(); - - uint32_t vertIndex = 0; - - FbxAMatrix trans = getTransformForNode(meshNode); - FbxAMatrix normalTransf = trans.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); - auto smLayer = mesh->GetElementSmoothing(); - - const int triangleIndexMappingUnflipped[3] = { 0, 1, 2 }; - const int triangleIndexMappingFlipped[3] = { 2, 1, 0 }; - const int* triangleIndexMapping = trans.Determinant() < 0 ? triangleIndexMappingFlipped : triangleIndexMappingUnflipped; - - for (int i = 0; i < polyCount; i++) - { - for (int vi = 0; vi < 3; vi++) - { - int polyCPIdx = polyVertices[i*3+ triangleIndexMapping[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)); - } - if (smLayer != nullptr) - { - mSmoothingGroups.push_back(smLayer->GetDirectArray().GetAt(i)); - } - } - - mVertexPositions = positions; - mVertexNormals = normals; - mVertexUv = uv; - mIndices = indices; - - getBoneInfluencesInternal(mesh); -} - -int32_t* FbxFileReader::getSmoothingGroups() -{ - if (!mSmoothingGroups.empty()) - { - return mSmoothingGroups.data(); - } - else - { - return nullptr; - } -} - -int32_t FbxFileReader::getMaterialCount() -{ - return mMaterialNames.size(); -} - -void FbxFileReader::getFbxMeshes(FbxDisplayLayer* collisionDisplayLayer, FbxNode* node, std::vector& 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(); - 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()) - { - hullsOffset = nullptr; - hulls = nullptr; - return 0; - } - hullsOffset = (uint32_t*)NVBLAST_ALLOC(sizeof(uint32_t) * mHullsOffset.size()); - memcpy(hullsOffset, mHullsOffset.data(), sizeof(uint32_t) * mHullsOffset.size()); - - - hulls = (Nv::Blast::CollisionHull**)NVBLAST_ALLOC(sizeof(Nv::Blast::CollisionHull*) * mHulls.size()); - for (size_t i = 0; i < mHulls.size(); i++) - { - //This deep-copies the data inside - hulls[i] = new CollisionHullImpl(mHulls[i]); - } - return mMeshCount; -} - -bool FbxFileReader::getCollisionInternal() -{ - mHulls.clear(); - 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()); - - uint32_t currentHullCount = 0; - uint32_t prevParentIndex = 0; - mHullsOffset[0] = currentHullCount; - - for (auto p : mCollisionNodes) // it should be sorted by chunk id - { - uint32_t parentIndex = p.first; - if (prevParentIndex != parentIndex) - { - for (uint32_t m = prevParentIndex + 1; m < parentIndex; m++) - { - //copy these if there were no collision meshes - mHullsOffset[m] = mHullsOffset[prevParentIndex]; - } - mHullsOffset[parentIndex] = currentHullCount; - prevParentIndex = parentIndex; - } - Nv::Blast::CollisionHull& chull = mHulls[currentHullCount]; - currentHullCount++; - - FbxMesh* meshNode = p.second->GetMesh(); - - FbxAMatrix nodeTransform = getTransformForNode(p.second); - FbxAMatrix nodeTransformNormal = nodeTransform.Inverse().Transpose(); - - //PhysX seems to care about having welding verticies. - //Probably doing a dumb search is fast enough since how big could the convex hulls possibly be? - std::vector uniqueCPValues; - uniqueCPValues.reserve(meshNode->GetControlPointsCount()); - std::vector originalToNewCPMapping(meshNode->GetControlPointsCount(), ~0U); - - FbxVector4* vpos = meshNode->GetControlPoints(); - for (int32_t i = 0; i < meshNode->GetControlPointsCount(); ++i) - { - FbxVector4 worldVPos = nodeTransform.MultT(*vpos); - bool found = false; - for (size_t j = 0; j < uniqueCPValues.size(); j++) - { - if (uniqueCPValues[j] == worldVPos) - { - originalToNewCPMapping[i] = uint32_t(j); - found = true; - break; - } - } - if (!found) - { - originalToNewCPMapping[i] = uint32_t(uniqueCPValues.size()); - uniqueCPValues.push_back(worldVPos); - } - vpos++; - } - - chull.points = new PxVec3[uniqueCPValues.size()]; - chull.pointsCount = uint32_t(uniqueCPValues.size()); - - physx::PxVec3 hullCentroid(0.0f); - - for (uint32_t i = 0; i < chull.pointsCount; ++i) - { - const FbxVector4& worldVPos = uniqueCPValues[i]; - chull.points[i].x = (float)worldVPos[0]; - chull.points[i].y = (float)worldVPos[1]; - chull.points[i].z = (float)worldVPos[2]; - hullCentroid += chull.points[i]; - } - - if (chull.pointsCount) - { - hullCentroid /= (float)chull.pointsCount; - } - - uint32_t polyCount = meshNode->GetPolygonCount(); - chull.polygonData = new Nv::Blast::CollisionHull::HullPolygon[polyCount]; - chull.polygonDataCount = polyCount; - - chull.indicesCount = meshNode->GetPolygonVertexCount(); - chull.indices = new uint32_t[chull.indicesCount]; - uint32_t curIndexCount = 0; - - for (uint32_t poly = 0; poly < polyCount; ++poly) - { - int32_t vInPolyCount = meshNode->GetPolygonSize(poly); - auto& pd = chull.polygonData[poly]; - pd.mIndexBase = (uint16_t)curIndexCount; - pd.mNbVerts = (uint16_t)vInPolyCount; - int32_t* ind = &meshNode->GetPolygonVertices()[meshNode->GetPolygonVertexIndex(poly)]; - uint32_t* destInd = chull.indices + curIndexCount; - for (int32_t v = 0; v < vInPolyCount; v++) - { - destInd[v] = originalToNewCPMapping[ind[v]]; - } - curIndexCount += vInPolyCount; - - //Don't depend on the normals to create the plane normal, they could be wrong - PxVec3 lastThreeVerts[3] = { - chull.points[chull.indices[curIndexCount - 1]], - chull.points[chull.indices[curIndexCount - 2]], - chull.points[chull.indices[curIndexCount - 3]] - }; - - physx::PxPlane plane(lastThreeVerts[0], lastThreeVerts[1], lastThreeVerts[2]); - plane.normalize(); - - const float s = plane.n.dot(lastThreeVerts[0] - hullCentroid) >= 0.0f ? 1.0f : -1.0f; - - pd.mPlane[0] = s*plane.n.x; - pd.mPlane[1] = s*plane.n.y; - pd.mPlane[2] = s*plane.n.z; - pd.mPlane[3] = s*plane.d; - } - } - - //Set the end marker - for (uint32_t m = prevParentIndex + 1; m <= mMeshCount; m++) - { - //copy these if there were no collision meshes - mHullsOffset[m] = currentHullCount; - } - - 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) -{ - std::unordered_map boneToChunkIndex; - - if (meshNode->GetDeformerCount() != 1) - { - std::cout << "Can't create bone mapping: There is no mesh deformers...: " << std::endl; - return false; - } - mVertexToContainingChunkMap.clear(); - mVertexToContainingChunkMap.resize(mVertexPositions.size()); - std::vector controlToParentChunkMap; - controlToParentChunkMap.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; - } - //We want the number of chunks not the bones in the FBX file - mChunkCount = 0; - - for (int32_t i = 0; i < def->GetClusterCount(); ++i) - { - FbxCluster* cls = def->GetCluster(i); - FbxNode* bone = cls->GetLink(); - - uint32_t myChunkIndex; - auto findIt = boneToChunkIndex.find(bone); - if (findIt != boneToChunkIndex.end()) - { - myChunkIndex = findIt->second; - } - else - { - myChunkIndex = FbxUtils::getChunkIndexForNode(bone); - if (myChunkIndex == UINT32_MAX) - { - //maybe an old file? - myChunkIndex = FbxUtils::getChunkIndexForNodeBackwardsCompatible(bone); - } - - if (myChunkIndex == UINT32_MAX) - { - std::cerr << "Not sure what to do with node " << bone->GetName() << ". is this a chunk?" << std::endl; - } - - boneToChunkIndex.emplace(bone, myChunkIndex); - if (myChunkIndex >= mChunkCount) - { - mChunkCount = myChunkIndex + 1; - } - } - - int32_t* cpIndx = cls->GetControlPointIndices(); - for (int32_t j = 0; j < cls->GetControlPointIndicesCount(); ++j) - { - controlToParentChunkMap[*cpIndx] = myChunkIndex; - ++cpIndx; - } - } - - int* polyVertices = meshNode->GetPolygonVertices(); - uint32_t lv = 0; - for (int i = 0; i < meshNode->GetPolygonCount(); i++) - { - for (int vi = 0; vi < 3; vi++) - { - mVertexToContainingChunkMap[lv] = controlToParentChunkMap[*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(NVBLAST_ALLOC(sizeof(uint32_t) * mVertexToContainingChunkMap.size())); - memcpy(out, mVertexToContainingChunkMap.data(), sizeof(uint32_t) * mVertexToContainingChunkMap.size()); - return mVertexToContainingChunkMap.size(); -} - -uint32_t FbxFileReader::getBoneCount() -{ - return mChunkCount; -} - -const char* FbxFileReader::getMaterialName(int32_t id) -{ - if (id < int32_t(mMaterialNames.size()) && id >= 0) - { - return mMaterialNames[id].c_str(); - } - else - { - return nullptr; - } -} - -int32_t* FbxFileReader::getMaterialIds() -{ - if (mMaterialIds.empty()) - { - return nullptr; - } - return mMaterialIds.data(); -} +// 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) 2018 NVIDIA Corporation. All rights reserved. + +#include "NvBlastExtExporterFbxReader.h" +#include "NvBlastExtExporterFbxUtils.h" +#include "NvBlastGlobals.h" +#include +#include +#include +#include +#include +#include + +#include "PxVec3.h" +#include "PxVec2.h" +#include "PxPlane.h" +#include "NvBlastExtAuthoringMesh.h" +#include "NvBlastExtAuthoringBondGenerator.h" +#include "NvBlastExtAuthoringCollisionBuilder.h" + +using physx::PxVec3; +using physx::PxVec2; + +using namespace Nv::Blast; + +FbxFileReader::FbxFileReader() +{ + mMeshCount = 0; + mChunkCount = 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 sdkManager = std::shared_ptr(FbxManager::Create(), [=](FbxManager* manager) + { + manager->Destroy(); + }); + + mChunkCount = 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); + } + + FbxGeometryConverter geoConverter(sdkManager.get()); + FbxDisplayLayer* collisionDisplayLayer = scene->FindMember(FbxUtils::getCollisionGeometryLayerName().c_str()); + + // Recurse the fbx tree and find all meshes + std::vector meshNodes; + getFbxMeshes(collisionDisplayLayer, scene->GetRootNode(), meshNodes); + + if (isCollisionLoaded()) + { + std::cout << "Collision geometry is found."; + getCollisionInternal(); + } + + std::cout << "Found " << meshNodes.size() << " meshes." << std::endl; + + if (meshNodes.size() > 1) + { + FbxArray tempMeshArray; + tempMeshArray.Resize((int)meshNodes.size()); + for (size_t i = 0; i < meshNodes.size(); i++) + { + FbxMesh* mesh = meshNodes[i]->GetMesh(); + if (mesh->GetDeformerCount(FbxDeformer::eSkin) != 0) + { + std::cerr << "Multi-part mesh " << meshNodes[i]->GetName() << " is already skinned, not sure what to do" << std::endl; + return; + } + //Add a one-bone skin so later when we merge meshes the connection to the chunk transform will stick, this handles the non-skinned layout of the FBX file + FbxSkin* skin = FbxSkin::Create(scene, (std::string(meshNodes[i]->GetName()) + "_skin").c_str()); + mesh->AddDeformer(skin); + FbxCluster* cluster = FbxCluster::Create(skin, (std::string(meshNodes[i]->GetName()) + "_cluster").c_str()); + skin->AddCluster(cluster); + cluster->SetLink(meshNodes[i]); + const int cpCount = mesh->GetControlPointsCount(); + cluster->SetControlPointIWCount(cpCount); + //Fully weight to the one bone + int* cpIdx = cluster->GetControlPointIndices(); + double* cpWeights = cluster->GetControlPointWeights(); + for (int cp = 0; cp < cpCount; cp++) + { + cpIdx[cp] = cp; + cpWeights[cp] = 1.0; + } + tempMeshArray.SetAt(int(i), meshNodes[i]); + } + meshNodes.resize(1); + meshNodes[0] = geoConverter.MergeMeshes(tempMeshArray, "MergedMesh", scene); + } + + if (meshNodes.empty()) + { + return; + } + + FbxNode* meshNode = meshNodes[0]; + FbxMesh* mesh = meshNode->GetMesh(); + + // Verify that the mesh is triangulated. + bool bAllTriangles = mesh->IsTriangleMesh(); + if (!bAllTriangles) + { + //try letting the FBX SDK triangulate it + geoConverter.Triangulate(mesh, true); + bAllTriangles = mesh->IsTriangleMesh(); + } + + int polyCount = mesh->GetPolygonCount(); + 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 positions; + std::vector normals; + std::vector uv; + std::vector indices; + + int* polyVertices = mesh->GetPolygonVertices(); + + uint32_t vertIndex = 0; + + FbxAMatrix trans = getTransformForNode(meshNode); + FbxAMatrix normalTransf = trans.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); + auto smLayer = mesh->GetElementSmoothing(); + + const int triangleIndexMappingUnflipped[3] = { 0, 1, 2 }; + const int triangleIndexMappingFlipped[3] = { 2, 1, 0 }; + const int* triangleIndexMapping = trans.Determinant() < 0 ? triangleIndexMappingFlipped : triangleIndexMappingUnflipped; + + for (int i = 0; i < polyCount; i++) + { + for (int vi = 0; vi < 3; vi++) + { + int polyCPIdx = polyVertices[i*3+ triangleIndexMapping[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)); + } + if (smLayer != nullptr) + { + mSmoothingGroups.push_back(smLayer->GetDirectArray().GetAt(i)); + } + } + + mVertexPositions = positions; + mVertexNormals = normals; + mVertexUv = uv; + mIndices = indices; + + getBoneInfluencesInternal(mesh); +} + +int32_t* FbxFileReader::getSmoothingGroups() +{ + if (!mSmoothingGroups.empty()) + { + return mSmoothingGroups.data(); + } + else + { + return nullptr; + } +} + +int32_t FbxFileReader::getMaterialCount() +{ + return mMaterialNames.size(); +} + +void FbxFileReader::getFbxMeshes(FbxDisplayLayer* collisionDisplayLayer, FbxNode* node, std::vector& 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(); + 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()) + { + hullsOffset = nullptr; + hulls = nullptr; + return 0; + } + hullsOffset = (uint32_t*)NVBLAST_ALLOC(sizeof(uint32_t) * mHullsOffset.size()); + memcpy(hullsOffset, mHullsOffset.data(), sizeof(uint32_t) * mHullsOffset.size()); + + + hulls = (Nv::Blast::CollisionHull**)NVBLAST_ALLOC(sizeof(Nv::Blast::CollisionHull*) * mHulls.size()); + for (size_t i = 0; i < mHulls.size(); i++) + { + //This deep-copies the data inside + hulls[i] = new CollisionHullImpl(mHulls[i]); + } + return mMeshCount; +} + +bool FbxFileReader::getCollisionInternal() +{ + mHulls.clear(); + 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()); + + uint32_t currentHullCount = 0; + uint32_t prevParentIndex = 0; + mHullsOffset[0] = currentHullCount; + + for (auto p : mCollisionNodes) // it should be sorted by chunk id + { + uint32_t parentIndex = p.first; + if (prevParentIndex != parentIndex) + { + for (uint32_t m = prevParentIndex + 1; m < parentIndex; m++) + { + //copy these if there were no collision meshes + mHullsOffset[m] = mHullsOffset[prevParentIndex]; + } + mHullsOffset[parentIndex] = currentHullCount; + prevParentIndex = parentIndex; + } + Nv::Blast::CollisionHull& chull = mHulls[currentHullCount]; + currentHullCount++; + + FbxMesh* meshNode = p.second->GetMesh(); + + FbxAMatrix nodeTransform = getTransformForNode(p.second); + FbxAMatrix nodeTransformNormal = nodeTransform.Inverse().Transpose(); + + //PhysX seems to care about having welding verticies. + //Probably doing a dumb search is fast enough since how big could the convex hulls possibly be? + std::vector uniqueCPValues; + uniqueCPValues.reserve(meshNode->GetControlPointsCount()); + std::vector originalToNewCPMapping(meshNode->GetControlPointsCount(), ~0U); + + FbxVector4* vpos = meshNode->GetControlPoints(); + for (int32_t i = 0; i < meshNode->GetControlPointsCount(); ++i) + { + FbxVector4 worldVPos = nodeTransform.MultT(*vpos); + bool found = false; + for (size_t j = 0; j < uniqueCPValues.size(); j++) + { + if (uniqueCPValues[j] == worldVPos) + { + originalToNewCPMapping[i] = uint32_t(j); + found = true; + break; + } + } + if (!found) + { + originalToNewCPMapping[i] = uint32_t(uniqueCPValues.size()); + uniqueCPValues.push_back(worldVPos); + } + vpos++; + } + + chull.points = new PxVec3[uniqueCPValues.size()]; + chull.pointsCount = uint32_t(uniqueCPValues.size()); + + physx::PxVec3 hullCentroid(0.0f); + + for (uint32_t i = 0; i < chull.pointsCount; ++i) + { + const FbxVector4& worldVPos = uniqueCPValues[i]; + chull.points[i].x = (float)worldVPos[0]; + chull.points[i].y = (float)worldVPos[1]; + chull.points[i].z = (float)worldVPos[2]; + hullCentroid += chull.points[i]; + } + + if (chull.pointsCount) + { + hullCentroid /= (float)chull.pointsCount; + } + + uint32_t polyCount = meshNode->GetPolygonCount(); + chull.polygonData = new Nv::Blast::CollisionHull::HullPolygon[polyCount]; + chull.polygonDataCount = polyCount; + + chull.indicesCount = meshNode->GetPolygonVertexCount(); + chull.indices = new uint32_t[chull.indicesCount]; + uint32_t curIndexCount = 0; + + for (uint32_t poly = 0; poly < polyCount; ++poly) + { + int32_t vInPolyCount = meshNode->GetPolygonSize(poly); + auto& pd = chull.polygonData[poly]; + pd.mIndexBase = (uint16_t)curIndexCount; + pd.mNbVerts = (uint16_t)vInPolyCount; + int32_t* ind = &meshNode->GetPolygonVertices()[meshNode->GetPolygonVertexIndex(poly)]; + uint32_t* destInd = chull.indices + curIndexCount; + for (int32_t v = 0; v < vInPolyCount; v++) + { + destInd[v] = originalToNewCPMapping[ind[v]]; + } + curIndexCount += vInPolyCount; + + //Don't depend on the normals to create the plane normal, they could be wrong + PxVec3 lastThreeVerts[3] = { + chull.points[chull.indices[curIndexCount - 1]], + chull.points[chull.indices[curIndexCount - 2]], + chull.points[chull.indices[curIndexCount - 3]] + }; + + physx::PxPlane plane(lastThreeVerts[0], lastThreeVerts[1], lastThreeVerts[2]); + plane.normalize(); + + const float s = plane.n.dot(lastThreeVerts[0] - hullCentroid) >= 0.0f ? 1.0f : -1.0f; + + pd.mPlane[0] = s*plane.n.x; + pd.mPlane[1] = s*plane.n.y; + pd.mPlane[2] = s*plane.n.z; + pd.mPlane[3] = s*plane.d; + } + } + + //Set the end marker + for (uint32_t m = prevParentIndex + 1; m <= mMeshCount; m++) + { + //copy these if there were no collision meshes + mHullsOffset[m] = currentHullCount; + } + + 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) +{ + std::unordered_map boneToChunkIndex; + + if (meshNode->GetDeformerCount() != 1) + { + std::cout << "Can't create bone mapping: There is no mesh deformers...: " << std::endl; + return false; + } + mVertexToContainingChunkMap.clear(); + mVertexToContainingChunkMap.resize(mVertexPositions.size()); + std::vector controlToParentChunkMap; + controlToParentChunkMap.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; + } + //We want the number of chunks not the bones in the FBX file + mChunkCount = 0; + + for (int32_t i = 0; i < def->GetClusterCount(); ++i) + { + FbxCluster* cls = def->GetCluster(i); + FbxNode* bone = cls->GetLink(); + + uint32_t myChunkIndex; + auto findIt = boneToChunkIndex.find(bone); + if (findIt != boneToChunkIndex.end()) + { + myChunkIndex = findIt->second; + } + else + { + myChunkIndex = FbxUtils::getChunkIndexForNode(bone); + if (myChunkIndex == UINT32_MAX) + { + //maybe an old file? + myChunkIndex = FbxUtils::getChunkIndexForNodeBackwardsCompatible(bone); + } + + if (myChunkIndex == UINT32_MAX) + { + std::cerr << "Not sure what to do with node " << bone->GetName() << ". is this a chunk?" << std::endl; + } + + boneToChunkIndex.emplace(bone, myChunkIndex); + if (myChunkIndex >= mChunkCount) + { + mChunkCount = myChunkIndex + 1; + } + } + + int32_t* cpIndx = cls->GetControlPointIndices(); + for (int32_t j = 0; j < cls->GetControlPointIndicesCount(); ++j) + { + controlToParentChunkMap[*cpIndx] = myChunkIndex; + ++cpIndx; + } + } + + int* polyVertices = meshNode->GetPolygonVertices(); + uint32_t lv = 0; + for (int i = 0; i < meshNode->GetPolygonCount(); i++) + { + for (int vi = 0; vi < 3; vi++) + { + mVertexToContainingChunkMap[lv] = controlToParentChunkMap[*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(NVBLAST_ALLOC(sizeof(uint32_t) * mVertexToContainingChunkMap.size())); + memcpy(out, mVertexToContainingChunkMap.data(), sizeof(uint32_t) * mVertexToContainingChunkMap.size()); + return mVertexToContainingChunkMap.size(); +} + +uint32_t FbxFileReader::getBoneCount() +{ + return mChunkCount; +} + +const char* FbxFileReader::getMaterialName(int32_t id) +{ + if (id < int32_t(mMaterialNames.size()) && id >= 0) + { + return mMaterialNames[id].c_str(); + } + else + { + return nullptr; + } +} + +int32_t* FbxFileReader::getMaterialIds() +{ + if (mMaterialIds.empty()) + { + return nullptr; + } + return mMaterialIds.data(); +} diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h b/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h old mode 100644 new mode 100755 index ce1d008..0509700 --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxReader.h @@ -1,224 +1,224 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#ifndef NVBLASTEXTEXPORTERFBXREADER_H -#define NVBLASTEXTEXPORTERFBXREADER_H - -#include -#include "fbxsdk.h" -#include -#include -#include "NvBlastExtExporter.h" -#include "NvBlastExtAuthoringTypes.h" - -namespace Nv -{ -namespace Blast -{ -class Mesh; - -class FbxFileReader : public IFbxFileReader -{ - struct CollisionHullImpl : public Nv::Blast::CollisionHull - { - void release() override - { - delete this; - } - - //copy from existing - CollisionHullImpl(const CollisionHullImpl& other) : CollisionHullImpl() - { - copyFrom(other); - } - - CollisionHullImpl() - { - pointsCount = 0; - indicesCount = 0; - polygonDataCount = 0; - points = nullptr; - indices = nullptr; - polygonData = nullptr; - } - - CollisionHullImpl(CollisionHullImpl&& other) - { - operator=(std::move(other)); - } - - CollisionHullImpl& operator=(const CollisionHullImpl& other) - { - if (&other != this) - { - release(); - copyFrom(other); - } - return *this; - } - - CollisionHullImpl& operator=(CollisionHullImpl&& other) - { - if (&other != this) - { - pointsCount = other.pointsCount; - indicesCount = other.indicesCount; - polygonDataCount = other.polygonDataCount; - points = other.points; - indices = other.indices; - polygonData = other.polygonData; - - other.pointsCount = 0; - other.indicesCount = 0; - other.polygonDataCount = 0; - other.points = nullptr; - other.indices = nullptr; - other.polygonData = nullptr; - } - return *this; - } - - virtual ~CollisionHullImpl() - { - delete[] points; - delete[] indices; - delete[] polygonData; - } - private: - - void copyFrom(const CollisionHullImpl& other) - { - pointsCount = other.pointsCount; - indicesCount = other.indicesCount; - polygonDataCount = other.polygonDataCount; - points = new physx::PxVec3[pointsCount]; - indices = new uint32_t[indicesCount]; - polygonData = new Nv::Blast::CollisionHull::HullPolygon[polygonDataCount]; - memcpy(points, other.points, sizeof(points[0]) * pointsCount); - memcpy(indices, other.indices, sizeof(indices[0]) * indicesCount); - memcpy(polygonData, other.polygonData, sizeof(polygonData[0]) * polygonDataCount); - } - }; - -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 getIndicesCount() 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; - - /** - Get material name. - */ - const char* getMaterialName(int32_t id) override; - - - int32_t getMaterialCount() override; - -private: - - uint32_t mMeshCount; - uint32_t mChunkCount; - std::vector mHullsOffset; - std::vector mHulls; - std::vector mVertexToContainingChunkMap; - std::multimap mCollisionNodes; - std::vector mVertexPositions; - std::vector mVertexNormals; - std::vector mVertexUv; - std::vector mIndices; - std::vector mSmoothingGroups; - std::vector mMaterialIds; - std::vector mMaterialNames; - - FbxAMatrix getTransformForNode(FbxNode* node); - void getFbxMeshes(FbxDisplayLayer* collisionDisplayLayer, FbxNode* node, std::vector& meshNodes); - bool getCollisionInternal(); - bool getBoneInfluencesInternal(FbxMesh* meshNode); - -}; - -} -} - +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTERFBXREADER_H +#define NVBLASTEXTEXPORTERFBXREADER_H + +#include +#include "fbxsdk.h" +#include +#include +#include "NvBlastExtExporter.h" +#include "NvBlastExtAuthoringTypes.h" + +namespace Nv +{ +namespace Blast +{ +class Mesh; + +class FbxFileReader : public IFbxFileReader +{ + struct CollisionHullImpl : public Nv::Blast::CollisionHull + { + void release() override + { + delete this; + } + + //copy from existing + CollisionHullImpl(const CollisionHullImpl& other) : CollisionHullImpl() + { + copyFrom(other); + } + + CollisionHullImpl() + { + pointsCount = 0; + indicesCount = 0; + polygonDataCount = 0; + points = nullptr; + indices = nullptr; + polygonData = nullptr; + } + + CollisionHullImpl(CollisionHullImpl&& other) + { + operator=(std::move(other)); + } + + CollisionHullImpl& operator=(const CollisionHullImpl& other) + { + if (&other != this) + { + release(); + copyFrom(other); + } + return *this; + } + + CollisionHullImpl& operator=(CollisionHullImpl&& other) + { + if (&other != this) + { + pointsCount = other.pointsCount; + indicesCount = other.indicesCount; + polygonDataCount = other.polygonDataCount; + points = other.points; + indices = other.indices; + polygonData = other.polygonData; + + other.pointsCount = 0; + other.indicesCount = 0; + other.polygonDataCount = 0; + other.points = nullptr; + other.indices = nullptr; + other.polygonData = nullptr; + } + return *this; + } + + virtual ~CollisionHullImpl() + { + delete[] points; + delete[] indices; + delete[] polygonData; + } + private: + + void copyFrom(const CollisionHullImpl& other) + { + pointsCount = other.pointsCount; + indicesCount = other.indicesCount; + polygonDataCount = other.polygonDataCount; + points = new physx::PxVec3[pointsCount]; + indices = new uint32_t[indicesCount]; + polygonData = new Nv::Blast::CollisionHull::HullPolygon[polygonDataCount]; + memcpy(points, other.points, sizeof(points[0]) * pointsCount); + memcpy(indices, other.indices, sizeof(indices[0]) * indicesCount); + memcpy(polygonData, other.polygonData, sizeof(polygonData[0]) * polygonDataCount); + } + }; + +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 getIndicesCount() 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; + + /** + Get material name. + */ + const char* getMaterialName(int32_t id) override; + + + int32_t getMaterialCount() override; + +private: + + uint32_t mMeshCount; + uint32_t mChunkCount; + std::vector mHullsOffset; + std::vector mHulls; + std::vector mVertexToContainingChunkMap; + std::multimap mCollisionNodes; + std::vector mVertexPositions; + std::vector mVertexNormals; + std::vector mVertexUv; + std::vector mIndices; + std::vector mSmoothingGroups; + std::vector mMaterialIds; + std::vector mMaterialNames; + + FbxAMatrix getTransformForNode(FbxNode* node); + void getFbxMeshes(FbxDisplayLayer* collisionDisplayLayer, FbxNode* node, std::vector& 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 old mode 100644 new mode 100755 index adf4092..f135436 --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.cpp @@ -1,208 +1,208 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#include "fbxsdk.h" -#include "NvBlastExtExporterFbxUtils.h" -#include "PxVec3.h" -#include "PxVec2.h" -#include "NvBlastExtAuthoringTypes.h" -#include -#include - -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 << ""; - } - 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 << ""; break; - } - - ss << " FrontVector: " << (frontSign > 0 ? "+" : "-"); - switch (frontVector) - { - case FbxAxisSystem::eParityEven: ss << "eParityEven"; break; - case FbxAxisSystem::eParityOdd: ss << "eParityOdd"; break; - default: ss << ""; break; - } - - ss << " CoordSystem: "; - switch (coordSystem) - { - case FbxAxisSystem::eLeftHanded: ss << "eLeftHanded"; break; - case FbxAxisSystem::eRightHanded: ss << "eRightHanded"; break; - default: ss << ""; break; - } - - return ss.str(); -} - -std::string FbxUtils::FbxSystemUnitToString(const FbxSystemUnit& systemUnit) -{ - return std::string(systemUnit.GetScaleFactorAsString()); -} - -const static std::string currentChunkPrefix = "chunk_"; -const static std::string oldChunkPrefix = "bone_"; - -static uint32_t getChunkIndexForNodeInternal(const std::string& chunkPrefix, FbxNode* node, uint32_t* outParentChunkIndex /*=nullptr*/) -{ - if (!node) - { - //Found nothing - return UINT32_MAX; - } - - 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()) - { - if (outParentChunkIndex) - { - *outParentChunkIndex = getChunkIndexForNodeInternal(chunkPrefix, node->GetParent(), nullptr); - } - return ret; - } - } - - return getChunkIndexForNodeInternal(chunkPrefix, node->GetParent(), outParentChunkIndex); -} - -uint32_t FbxUtils::getChunkIndexForNode(FbxNode* node, uint32_t* outParentChunkIndex /*=nullptr*/) -{ - return getChunkIndexForNodeInternal(currentChunkPrefix, node, outParentChunkIndex); -} - -uint32_t FbxUtils::getChunkIndexForNodeBackwardsCompatible(FbxNode* node, uint32_t* outParentChunkIndex /*= nullptr*/) -{ - return getChunkIndexForNodeInternal(oldChunkPrefix, node, outParentChunkIndex); -} - -std::string FbxUtils::getChunkNodeName(uint32_t chunkIndex) -{ - //This naming is required for the UE4 plugin to find them - std::ostringstream namestream; - namestream << currentChunkPrefix << chunkIndex; - return namestream.str(); -} - -std::string FbxUtils::getCollisionGeometryLayerName() -{ - return "Collision"; -} - -std::string FbxUtils::getRenderGeometryLayerName() -{ - return "Render"; -} +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#include "fbxsdk.h" +#include "NvBlastExtExporterFbxUtils.h" +#include "PxVec3.h" +#include "PxVec2.h" +#include "NvBlastExtAuthoringTypes.h" +#include +#include + +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 << ""; + } + 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 << ""; break; + } + + ss << " FrontVector: " << (frontSign > 0 ? "+" : "-"); + switch (frontVector) + { + case FbxAxisSystem::eParityEven: ss << "eParityEven"; break; + case FbxAxisSystem::eParityOdd: ss << "eParityOdd"; break; + default: ss << ""; break; + } + + ss << " CoordSystem: "; + switch (coordSystem) + { + case FbxAxisSystem::eLeftHanded: ss << "eLeftHanded"; break; + case FbxAxisSystem::eRightHanded: ss << "eRightHanded"; break; + default: ss << ""; break; + } + + return ss.str(); +} + +std::string FbxUtils::FbxSystemUnitToString(const FbxSystemUnit& systemUnit) +{ + return std::string(systemUnit.GetScaleFactorAsString()); +} + +const static std::string currentChunkPrefix = "chunk_"; +const static std::string oldChunkPrefix = "bone_"; + +static uint32_t getChunkIndexForNodeInternal(const std::string& chunkPrefix, FbxNode* node, uint32_t* outParentChunkIndex /*=nullptr*/) +{ + if (!node) + { + //Found nothing + return UINT32_MAX; + } + + 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()) + { + if (outParentChunkIndex) + { + *outParentChunkIndex = getChunkIndexForNodeInternal(chunkPrefix, node->GetParent(), nullptr); + } + return ret; + } + } + + return getChunkIndexForNodeInternal(chunkPrefix, node->GetParent(), outParentChunkIndex); +} + +uint32_t FbxUtils::getChunkIndexForNode(FbxNode* node, uint32_t* outParentChunkIndex /*=nullptr*/) +{ + return getChunkIndexForNodeInternal(currentChunkPrefix, node, outParentChunkIndex); +} + +uint32_t FbxUtils::getChunkIndexForNodeBackwardsCompatible(FbxNode* node, uint32_t* outParentChunkIndex /*= nullptr*/) +{ + return getChunkIndexForNodeInternal(oldChunkPrefix, node, outParentChunkIndex); +} + +std::string FbxUtils::getChunkNodeName(uint32_t chunkIndex) +{ + //This naming is required for the UE4 plugin to find them + std::ostringstream namestream; + namestream << currentChunkPrefix << 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 old mode 100644 new mode 100755 index 47cedd7..431d63a --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.h +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxUtils.h @@ -1,69 +1,69 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#ifndef NVBLASTEXTEXPORTERFBXUTILS_H -#define NVBLASTEXTEXPORTERFBXUTILS_H - -#include "fbxsdk.h" -#include "PxVec3.h" -#include "PxVec2.h" -#include - -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, uint32_t* outParentChunkIndex = nullptr); - //Search using the old naming - static uint32_t getChunkIndexForNodeBackwardsCompatible(FbxNode* node, uint32_t* outParentChunkIndex = nullptr); - static std::string getChunkNodeName(uint32_t chunkIndex); - - static std::string getCollisionGeometryLayerName(); - static std::string getRenderGeometryLayerName(); -}; - +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTERFBXUTILS_H +#define NVBLASTEXTEXPORTERFBXUTILS_H + +#include "fbxsdk.h" +#include "PxVec3.h" +#include "PxVec2.h" +#include + +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, uint32_t* outParentChunkIndex = nullptr); + //Search using the old naming + static uint32_t getChunkIndexForNodeBackwardsCompatible(FbxNode* node, uint32_t* outParentChunkIndex = nullptr); + 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 old mode 100644 new mode 100755 index c3c4b37..a0de9d5 --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp @@ -1,1441 +1,1441 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#include "fbxsdk.h" -#include -#include -#include -#include -#include -#include -#include "NvBlastTypes.h" -#include "NvBlastGlobals.h" -#include "NvBlastTkFramework.h" -#include "NvBlast.h" -#include "PxVec3.h" -#include "NvBlastAssert.h" -#include -#include -#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::Create(), [=](FbxManager* manager) - { - 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)); - - mInteriorIndex = -1; -} - -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.submeshMats[i].name); - 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::setInteriorIndex(int32_t index) -{ - mInteriorIndex = index; -} - - -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); - } - if (mInteriorIndex == -1) // No material setted. Create new one. - { - 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); - } - else - { - if (mInteriorIndex < 0) mInteriorIndex = 0; - if (static_cast(mInteriorIndex) >= mMaterials.size()) mInteriorIndex = 0; - } - -} - - -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::eDirect); - 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 = NvBlastAssetGetChunkCount(aResult.asset, Nv::Blast::logLL); - auto chunks = NvBlastAssetGetChunks(aResult.asset, Nv::Blast::logLL); - - 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 - generateSmoothingGroups(mesh, skin); - } - - removeDuplicateControlPoints(mesh, skin); - - 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 = NvBlastAssetGetChunkCount(aResult.asset, Nv::Blast::logLL); - auto chunks = NvBlastAssetGetChunks(aResult.asset, Nv::Blast::logLL); - - 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(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())) ? tri.materialId : 0) : ((mInteriorIndex == -1) ? int32_t(mMaterials.size() - 1): mInteriorIndex); - matElement->GetIndexArray().SetAt(polyCount, material); - if (smElement) - { - if (tri.userData == 0) - { - smElement->GetDirectArray().Add(tri.smoothingGroup); - } - else - { - smElement->GetDirectArray().Add(SMOOTHING_GROUP_INTERIOR); - } - } - - polyCount++; - cpIdx += 3; - } - - mat = meshNode->EvaluateGlobalTransform(); - cluster->SetTransformMatrix(mat); - - mat = boneNode->EvaluateGlobalTransform(); - cluster->SetTransformLinkMatrix(mat); - - uint32_t addedCps = static_cast((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& 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 - generateSmoothingGroups(mesh, nullptr); - } - - removeDuplicateControlPoints(mesh, nullptr); - - 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& 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::eDirect); - 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()))? geo.materialId : 0) : ((mInteriorIndex == -1)? int32_t(mMaterials.size() - 1) : mInteriorIndex); - matElement->GetIndexArray().SetAt(polyCount, material); - - if (smElement) - { - if (geo.userData == 0) - { - smElement->GetDirectArray().Add(geo.smoothingGroup); - } - else - { - smElement->GetDirectArray().Add(SMOOTHING_GROUP_INTERIOR); - } - } - - polyCount++; - - } - - if (!smElement) - { - //If no smoothing groups, generate them - generateSmoothingGroups(mesh, nullptr); - - } - - removeDuplicateControlPoints(mesh, nullptr); - - 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(meshNode->GetNodeAttribute()); - - auto geNormal = mesh->GetElementNormal(); - auto geUV = mesh->GetElementUV("diffuseElement"); - auto matr = mesh->GetElementMaterial(); - - std::vector 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 vertices; - std::cout << "Adding control points" << std::endl; - std::vector 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 - generateSmoothingGroups(mesh, skin); - } - - removeDuplicateControlPoints(mesh, skin); - - if (meshData.hulls != nullptr) - { - return appendCollisionMesh(chunkCount, meshData.hullsOffsets, meshData.hulls, assetName); - } - return true; -} - -void FbxFileWriter::generateSmoothingGroups(fbxsdk::FbxMesh* mesh, FbxSkin* skin) -{ - if (mesh->GetElementSmoothing(0) || !mesh->IsTriangleMesh()) - { - //they already exist or we can't make it - return; - } - - const FbxGeometryElementNormal* geNormal = mesh->GetElementNormal(); - if (!geNormal || geNormal->GetMappingMode() != FbxGeometryElement::eByPolygonVertex || geNormal->GetReferenceMode() != FbxGeometryElement::eDirect) - { - //We just set this up, but just incase - return; - } - - int clusterCount = 0; - std::vector> cpsPerCluster; - if (skin) - { - clusterCount = skin->GetClusterCount(); - cpsPerCluster.resize(clusterCount); - for (int c = 0; c < clusterCount; c++) - { - FbxCluster* cluster = skin->GetCluster(c); - int* clusterCPList = cluster->GetControlPointIndices(); - const int clusterCPListLength = cluster->GetControlPointIndicesCount(); - - cpsPerCluster[c].resize(clusterCPListLength); - memcpy(cpsPerCluster[c].data(), clusterCPList, sizeof(int) * clusterCPListLength); - std::sort(cpsPerCluster[c].begin(), cpsPerCluster[c].end()); - } - } - - auto smElement = mesh->CreateElementSmoothing(); - smElement->SetMappingMode(FbxGeometryElement::eByPolygon); - smElement->SetReferenceMode(FbxGeometryElement::eDirect); - - FbxVector4* cpList = mesh->GetControlPoints(); - const int cpCount = mesh->GetControlPointsCount(); - - const int triangleCount = mesh->GetPolygonCount(); - const int cornerCount = triangleCount * 3; - - int* polygonCPList = mesh->GetPolygonVertices(); - const auto& normalByCornerList = geNormal->GetDirectArray(); - - std::multimap overlappingCorners; - //sort them by z for faster overlap checking - std::vector> cornerIndexesByZ(cornerCount); - for (int c = 0; c < cornerCount; c++) - { - cornerIndexesByZ[c] = std::pair(cpList[polygonCPList[c]][2], c); - } - std::sort(cornerIndexesByZ.begin(), cornerIndexesByZ.end()); - - for (int i = 0; i < cornerCount; i++) - { - const int cornerA = cornerIndexesByZ[i].second; - const int cpiA = polygonCPList[cornerA]; - FbxVector4 cpA = cpList[cpiA]; - cpA[3] = 0; - - int clusterIndexA = -1; - for (int c = 0; c < clusterCount; c++) - { - if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiA)) - { - clusterIndexA = c; - break; - } - } - - for (int j = i + 1; j < cornerCount; j++) - { - if (std::abs(cornerIndexesByZ[j].first - cornerIndexesByZ[i].first) > FBXSDK_TOLERANCE) - { - break; // if the z's don't match other values don't matter - } - const int cornerB = cornerIndexesByZ[j].second; - const int cpiB = polygonCPList[cornerB]; - FbxVector4 cpB = cpList[cpiB]; - - cpB[3] = 0; - - //uses FBXSDK_TOLERANCE - if (cpA == cpB) - { - int clusterIndexB = -1; - for (int c = 0; c < clusterCount; c++) - { - if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiB)) - { - clusterIndexB = c; - break; - } - } - - if (clusterIndexA == clusterIndexB) - { - overlappingCorners.emplace(cornerA, cornerB); - overlappingCorners.emplace(cornerB, cornerA); - } - } - } - } - - auto& smoothingGroupByTri = smElement->GetDirectArray(); - for (int i = 0; i < triangleCount; i++) - { - smoothingGroupByTri.Add(0); - } - //first one - smoothingGroupByTri.SetAt(0, 1); - - for (int i = 1; i < triangleCount; i++) - { - int sharedMask = 0, unsharedMask = 0; - for (int c = 0; c < 3; c++) - { - int myCorner = i * 3 + c; - FbxVector4 myNormal = normalByCornerList.GetAt(myCorner); - myNormal.Normalize(); - myNormal[3] = 0; - - auto otherCornersRangeBegin = overlappingCorners.lower_bound(myCorner); - auto otherCornersRangeEnd = overlappingCorners.upper_bound(myCorner); - for (auto it = otherCornersRangeBegin; it != otherCornersRangeEnd; it++) - { - int otherCorner = it->second; - FbxVector4 otherNormal = normalByCornerList.GetAt(otherCorner); - otherNormal.Normalize(); - otherNormal[3] = 0; - if (otherNormal == myNormal) - { - sharedMask |= smoothingGroupByTri[otherCorner / 3]; - } - else - { - unsharedMask |= smoothingGroupByTri[otherCorner / 3]; - } - } - } - - //Easy case, no overlap - if ((sharedMask & unsharedMask) == 0 && sharedMask != 0) - { - smoothingGroupByTri.SetAt(i, sharedMask); - } - else - { - for (int sm = 0; sm < 32; sm++) - { - int val = 1 << sm; - if (((val & sharedMask) == sharedMask) && !(val & unsharedMask)) - { - smoothingGroupByTri.SetAt(i, val); - break; - } - } - } - } - -} - -namespace -{ - //These methods have different names for some reason - inline double* getControlPointBlendWeights(FbxSkin* skin) - { - return skin->GetControlPointBlendWeights(); - } - - inline double* getControlPointBlendWeights(FbxCluster* cluster) - { - return cluster->GetControlPointWeights(); - } - - template - void remapCPsAndRemoveDuplicates(const int newCPCount, const std::vector& oldToNewCPMapping, T* skinOrCluster) - { - //Need to avoid duplicate entires since UE doesn't seem to normalize this correctly - std::vector addedCP(newCPCount, false); - std::vector> newCPsAndWeights; - newCPsAndWeights.reserve(newCPCount); - - int* skinCPList = skinOrCluster->GetControlPointIndices(); - double* skinCPWeights = getControlPointBlendWeights(skinOrCluster); - const int skinCPListLength = skinOrCluster->GetControlPointIndicesCount(); - - for (int bw = 0; bw < skinCPListLength; bw++) - { - int newCPIdx = oldToNewCPMapping[skinCPList[bw]]; - if (!addedCP[newCPIdx]) - { - addedCP[newCPIdx] = true; - newCPsAndWeights.emplace_back(newCPIdx, skinCPWeights[bw]); - } - } - skinOrCluster->SetControlPointIWCount(newCPsAndWeights.size()); - skinCPList = skinOrCluster->GetControlPointIndices(); - skinCPWeights = getControlPointBlendWeights(skinOrCluster); - for (size_t bw = 0; bw < newCPsAndWeights.size(); bw++) - { - skinCPList[bw] = newCPsAndWeights[bw].first; - skinCPWeights[bw] = newCPsAndWeights[bw].second; - } - } -} - -//Do this otherwise Maya shows the mesh as faceted due to not being welded -void FbxFileWriter::removeDuplicateControlPoints(fbxsdk::FbxMesh* mesh, FbxSkin* skin) -{ - FbxVector4* cpList = mesh->GetControlPoints(); - const int cpCount = mesh->GetControlPointsCount(); - - std::vector oldToNewCPMapping(cpCount, -1); - //sort them by z for faster overlap checking - std::vector> cpIndexesByZ(cpCount); - for (int cp = 0; cp < cpCount; cp++) - { - cpIndexesByZ[cp] = std::pair(cpList[cp][2], cp); - } - std::sort(cpIndexesByZ.begin(), cpIndexesByZ.end()); - - int clusterCount = 0; - std::vector> cpsPerCluster; - if (skin) - { - clusterCount = skin->GetClusterCount(); - cpsPerCluster.resize(clusterCount); - for (int c = 0; c < clusterCount; c++) - { - FbxCluster* cluster = skin->GetCluster(c); - int* clusterCPList = cluster->GetControlPointIndices(); - const int clusterCPListLength = cluster->GetControlPointIndicesCount(); - - cpsPerCluster[c].resize(clusterCPListLength); - memcpy(cpsPerCluster[c].data(), clusterCPList, sizeof(int) * clusterCPListLength); - std::sort(cpsPerCluster[c].begin(), cpsPerCluster[c].end()); - } - } - - std::vector uniqueCPs; - uniqueCPs.reserve(cpCount); - - for (int i = 0; i < cpCount; i++) - { - const int cpiA = cpIndexesByZ[i].second; - FbxVector4 cpA = cpList[cpiA]; - if (!(oldToNewCPMapping[cpiA] < 0)) - { - //already culled this one - continue; - } - const int newIdx = int(uniqueCPs.size()); - oldToNewCPMapping[cpiA] = newIdx; - uniqueCPs.push_back(cpA); - - int clusterIndexA = -1; - for (int c = 0; c < clusterCount; c++) - { - if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiA)) - { - clusterIndexA = c; - break; - } - } - - for (int j = i + 1; j < cpCount; j++) - { - if (std::abs(cpIndexesByZ[j].first - cpIndexesByZ[i].first) > FBXSDK_TOLERANCE) - { - break; // if the z's don't match other values don't matter - } - - const int cpiB = cpIndexesByZ[j].second; - FbxVector4 cpB = cpList[cpiB]; - - //uses FBXSDK_TOLERANCE - if (cpA == cpB) - { - int clusterIndexB = -1; - for (int c = 0; c < clusterCount; c++) - { - if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiB)) - { - clusterIndexB = c; - break; - } - } - - //don't merge unless they share the same clusters - if (clusterIndexA == clusterIndexB) - { - oldToNewCPMapping[cpiB] = newIdx; - } - } - } - } - - const int originalCPCount = cpCount; - const int newCPCount = int(uniqueCPs.size()); - - if (newCPCount == cpCount) - { - //don't bother, it will just scramble it for no reason - return; - } - - mesh->InitControlPoints(newCPCount); - cpList = mesh->GetControlPoints(); - - for (int cp = 0; cp < newCPCount; cp++) - { - cpList[cp] = uniqueCPs[cp]; - } - - int* polygonCPList = mesh->GetPolygonVertices(); - const int polygonCPListLength = mesh->GetPolygonVertexCount(); - for (int pv = 0; pv < polygonCPListLength; pv++) - { - polygonCPList[pv] = oldToNewCPMapping[polygonCPList[pv]]; - } - - if (skin) - { - remapCPsAndRemoveDuplicates(newCPCount, oldToNewCPMapping, skin); - for (int c = 0; c < skin->GetClusterCount(); c++) - { - FbxCluster* cluster = skin->GetCluster(c); - remapCPsAndRemoveDuplicates(newCPCount, oldToNewCPMapping, cluster); - } - - } +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#include "fbxsdk.h" +#include +#include +#include +#include +#include +#include +#include "NvBlastTypes.h" +#include "NvBlastGlobals.h" +#include "NvBlastTkFramework.h" +#include "NvBlast.h" +#include "PxVec3.h" +#include "NvBlastAssert.h" +#include +#include +#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::Create(), [=](FbxManager* manager) + { + 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)); + + mInteriorIndex = -1; +} + +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.submeshMats[i].name); + 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::setInteriorIndex(int32_t index) +{ + mInteriorIndex = index; +} + + +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); + } + if (mInteriorIndex == -1) // No material setted. Create new one. + { + 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); + } + else + { + if (mInteriorIndex < 0) mInteriorIndex = 0; + if (static_cast(mInteriorIndex) >= mMaterials.size()) mInteriorIndex = 0; + } + +} + + +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::eDirect); + 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 = NvBlastAssetGetChunkCount(aResult.asset, Nv::Blast::logLL); + auto chunks = NvBlastAssetGetChunks(aResult.asset, Nv::Blast::logLL); + + 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 + generateSmoothingGroups(mesh, skin); + } + + removeDuplicateControlPoints(mesh, skin); + + 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 = NvBlastAssetGetChunkCount(aResult.asset, Nv::Blast::logLL); + auto chunks = NvBlastAssetGetChunks(aResult.asset, Nv::Blast::logLL); + + 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(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())) ? tri.materialId : 0) : ((mInteriorIndex == -1) ? int32_t(mMaterials.size() - 1): mInteriorIndex); + matElement->GetIndexArray().SetAt(polyCount, material); + if (smElement) + { + if (tri.userData == 0) + { + smElement->GetDirectArray().Add(tri.smoothingGroup); + } + else + { + smElement->GetDirectArray().Add(SMOOTHING_GROUP_INTERIOR); + } + } + + polyCount++; + cpIdx += 3; + } + + mat = meshNode->EvaluateGlobalTransform(); + cluster->SetTransformMatrix(mat); + + mat = boneNode->EvaluateGlobalTransform(); + cluster->SetTransformLinkMatrix(mat); + + uint32_t addedCps = static_cast((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& 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 + generateSmoothingGroups(mesh, nullptr); + } + + removeDuplicateControlPoints(mesh, nullptr); + + 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& 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::eDirect); + 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()))? geo.materialId : 0) : ((mInteriorIndex == -1)? int32_t(mMaterials.size() - 1) : mInteriorIndex); + matElement->GetIndexArray().SetAt(polyCount, material); + + if (smElement) + { + if (geo.userData == 0) + { + smElement->GetDirectArray().Add(geo.smoothingGroup); + } + else + { + smElement->GetDirectArray().Add(SMOOTHING_GROUP_INTERIOR); + } + } + + polyCount++; + + } + + if (!smElement) + { + //If no smoothing groups, generate them + generateSmoothingGroups(mesh, nullptr); + + } + + removeDuplicateControlPoints(mesh, nullptr); + + 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(meshNode->GetNodeAttribute()); + + auto geNormal = mesh->GetElementNormal(); + auto geUV = mesh->GetElementUV("diffuseElement"); + auto matr = mesh->GetElementMaterial(); + + std::vector 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 vertices; + std::cout << "Adding control points" << std::endl; + std::vector 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 + generateSmoothingGroups(mesh, skin); + } + + removeDuplicateControlPoints(mesh, skin); + + if (meshData.hulls != nullptr) + { + return appendCollisionMesh(chunkCount, meshData.hullsOffsets, meshData.hulls, assetName); + } + return true; +} + +void FbxFileWriter::generateSmoothingGroups(fbxsdk::FbxMesh* mesh, FbxSkin* skin) +{ + if (mesh->GetElementSmoothing(0) || !mesh->IsTriangleMesh()) + { + //they already exist or we can't make it + return; + } + + const FbxGeometryElementNormal* geNormal = mesh->GetElementNormal(); + if (!geNormal || geNormal->GetMappingMode() != FbxGeometryElement::eByPolygonVertex || geNormal->GetReferenceMode() != FbxGeometryElement::eDirect) + { + //We just set this up, but just incase + return; + } + + int clusterCount = 0; + std::vector> cpsPerCluster; + if (skin) + { + clusterCount = skin->GetClusterCount(); + cpsPerCluster.resize(clusterCount); + for (int c = 0; c < clusterCount; c++) + { + FbxCluster* cluster = skin->GetCluster(c); + int* clusterCPList = cluster->GetControlPointIndices(); + const int clusterCPListLength = cluster->GetControlPointIndicesCount(); + + cpsPerCluster[c].resize(clusterCPListLength); + memcpy(cpsPerCluster[c].data(), clusterCPList, sizeof(int) * clusterCPListLength); + std::sort(cpsPerCluster[c].begin(), cpsPerCluster[c].end()); + } + } + + auto smElement = mesh->CreateElementSmoothing(); + smElement->SetMappingMode(FbxGeometryElement::eByPolygon); + smElement->SetReferenceMode(FbxGeometryElement::eDirect); + + FbxVector4* cpList = mesh->GetControlPoints(); + const int cpCount = mesh->GetControlPointsCount(); + + const int triangleCount = mesh->GetPolygonCount(); + const int cornerCount = triangleCount * 3; + + int* polygonCPList = mesh->GetPolygonVertices(); + const auto& normalByCornerList = geNormal->GetDirectArray(); + + std::multimap overlappingCorners; + //sort them by z for faster overlap checking + std::vector> cornerIndexesByZ(cornerCount); + for (int c = 0; c < cornerCount; c++) + { + cornerIndexesByZ[c] = std::pair(cpList[polygonCPList[c]][2], c); + } + std::sort(cornerIndexesByZ.begin(), cornerIndexesByZ.end()); + + for (int i = 0; i < cornerCount; i++) + { + const int cornerA = cornerIndexesByZ[i].second; + const int cpiA = polygonCPList[cornerA]; + FbxVector4 cpA = cpList[cpiA]; + cpA[3] = 0; + + int clusterIndexA = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiA)) + { + clusterIndexA = c; + break; + } + } + + for (int j = i + 1; j < cornerCount; j++) + { + if (std::abs(cornerIndexesByZ[j].first - cornerIndexesByZ[i].first) > FBXSDK_TOLERANCE) + { + break; // if the z's don't match other values don't matter + } + const int cornerB = cornerIndexesByZ[j].second; + const int cpiB = polygonCPList[cornerB]; + FbxVector4 cpB = cpList[cpiB]; + + cpB[3] = 0; + + //uses FBXSDK_TOLERANCE + if (cpA == cpB) + { + int clusterIndexB = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiB)) + { + clusterIndexB = c; + break; + } + } + + if (clusterIndexA == clusterIndexB) + { + overlappingCorners.emplace(cornerA, cornerB); + overlappingCorners.emplace(cornerB, cornerA); + } + } + } + } + + auto& smoothingGroupByTri = smElement->GetDirectArray(); + for (int i = 0; i < triangleCount; i++) + { + smoothingGroupByTri.Add(0); + } + //first one + smoothingGroupByTri.SetAt(0, 1); + + for (int i = 1; i < triangleCount; i++) + { + int sharedMask = 0, unsharedMask = 0; + for (int c = 0; c < 3; c++) + { + int myCorner = i * 3 + c; + FbxVector4 myNormal = normalByCornerList.GetAt(myCorner); + myNormal.Normalize(); + myNormal[3] = 0; + + auto otherCornersRangeBegin = overlappingCorners.lower_bound(myCorner); + auto otherCornersRangeEnd = overlappingCorners.upper_bound(myCorner); + for (auto it = otherCornersRangeBegin; it != otherCornersRangeEnd; it++) + { + int otherCorner = it->second; + FbxVector4 otherNormal = normalByCornerList.GetAt(otherCorner); + otherNormal.Normalize(); + otherNormal[3] = 0; + if (otherNormal == myNormal) + { + sharedMask |= smoothingGroupByTri[otherCorner / 3]; + } + else + { + unsharedMask |= smoothingGroupByTri[otherCorner / 3]; + } + } + } + + //Easy case, no overlap + if ((sharedMask & unsharedMask) == 0 && sharedMask != 0) + { + smoothingGroupByTri.SetAt(i, sharedMask); + } + else + { + for (int sm = 0; sm < 32; sm++) + { + int val = 1 << sm; + if (((val & sharedMask) == sharedMask) && !(val & unsharedMask)) + { + smoothingGroupByTri.SetAt(i, val); + break; + } + } + } + } + +} + +namespace +{ + //These methods have different names for some reason + inline double* getControlPointBlendWeights(FbxSkin* skin) + { + return skin->GetControlPointBlendWeights(); + } + + inline double* getControlPointBlendWeights(FbxCluster* cluster) + { + return cluster->GetControlPointWeights(); + } + + template + void remapCPsAndRemoveDuplicates(const int newCPCount, const std::vector& oldToNewCPMapping, T* skinOrCluster) + { + //Need to avoid duplicate entires since UE doesn't seem to normalize this correctly + std::vector addedCP(newCPCount, false); + std::vector> newCPsAndWeights; + newCPsAndWeights.reserve(newCPCount); + + int* skinCPList = skinOrCluster->GetControlPointIndices(); + double* skinCPWeights = getControlPointBlendWeights(skinOrCluster); + const int skinCPListLength = skinOrCluster->GetControlPointIndicesCount(); + + for (int bw = 0; bw < skinCPListLength; bw++) + { + int newCPIdx = oldToNewCPMapping[skinCPList[bw]]; + if (!addedCP[newCPIdx]) + { + addedCP[newCPIdx] = true; + newCPsAndWeights.emplace_back(newCPIdx, skinCPWeights[bw]); + } + } + skinOrCluster->SetControlPointIWCount(newCPsAndWeights.size()); + skinCPList = skinOrCluster->GetControlPointIndices(); + skinCPWeights = getControlPointBlendWeights(skinOrCluster); + for (size_t bw = 0; bw < newCPsAndWeights.size(); bw++) + { + skinCPList[bw] = newCPsAndWeights[bw].first; + skinCPWeights[bw] = newCPsAndWeights[bw].second; + } + } +} + +//Do this otherwise Maya shows the mesh as faceted due to not being welded +void FbxFileWriter::removeDuplicateControlPoints(fbxsdk::FbxMesh* mesh, FbxSkin* skin) +{ + FbxVector4* cpList = mesh->GetControlPoints(); + const int cpCount = mesh->GetControlPointsCount(); + + std::vector oldToNewCPMapping(cpCount, -1); + //sort them by z for faster overlap checking + std::vector> cpIndexesByZ(cpCount); + for (int cp = 0; cp < cpCount; cp++) + { + cpIndexesByZ[cp] = std::pair(cpList[cp][2], cp); + } + std::sort(cpIndexesByZ.begin(), cpIndexesByZ.end()); + + int clusterCount = 0; + std::vector> cpsPerCluster; + if (skin) + { + clusterCount = skin->GetClusterCount(); + cpsPerCluster.resize(clusterCount); + for (int c = 0; c < clusterCount; c++) + { + FbxCluster* cluster = skin->GetCluster(c); + int* clusterCPList = cluster->GetControlPointIndices(); + const int clusterCPListLength = cluster->GetControlPointIndicesCount(); + + cpsPerCluster[c].resize(clusterCPListLength); + memcpy(cpsPerCluster[c].data(), clusterCPList, sizeof(int) * clusterCPListLength); + std::sort(cpsPerCluster[c].begin(), cpsPerCluster[c].end()); + } + } + + std::vector uniqueCPs; + uniqueCPs.reserve(cpCount); + + for (int i = 0; i < cpCount; i++) + { + const int cpiA = cpIndexesByZ[i].second; + FbxVector4 cpA = cpList[cpiA]; + if (!(oldToNewCPMapping[cpiA] < 0)) + { + //already culled this one + continue; + } + const int newIdx = int(uniqueCPs.size()); + oldToNewCPMapping[cpiA] = newIdx; + uniqueCPs.push_back(cpA); + + int clusterIndexA = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiA)) + { + clusterIndexA = c; + break; + } + } + + for (int j = i + 1; j < cpCount; j++) + { + if (std::abs(cpIndexesByZ[j].first - cpIndexesByZ[i].first) > FBXSDK_TOLERANCE) + { + break; // if the z's don't match other values don't matter + } + + const int cpiB = cpIndexesByZ[j].second; + FbxVector4 cpB = cpList[cpiB]; + + //uses FBXSDK_TOLERANCE + if (cpA == cpB) + { + int clusterIndexB = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiB)) + { + clusterIndexB = c; + break; + } + } + + //don't merge unless they share the same clusters + if (clusterIndexA == clusterIndexB) + { + oldToNewCPMapping[cpiB] = newIdx; + } + } + } + } + + const int originalCPCount = cpCount; + const int newCPCount = int(uniqueCPs.size()); + + if (newCPCount == cpCount) + { + //don't bother, it will just scramble it for no reason + return; + } + + mesh->InitControlPoints(newCPCount); + cpList = mesh->GetControlPoints(); + + for (int cp = 0; cp < newCPCount; cp++) + { + cpList[cp] = uniqueCPs[cp]; + } + + int* polygonCPList = mesh->GetPolygonVertices(); + const int polygonCPListLength = mesh->GetPolygonVertexCount(); + for (int pv = 0; pv < polygonCPListLength; pv++) + { + polygonCPList[pv] = oldToNewCPMapping[polygonCPList[pv]]; + } + + if (skin) + { + remapCPsAndRemoveDuplicates(newCPCount, oldToNewCPMapping, skin); + for (int c = 0; c < skin->GetClusterCount(); c++) + { + FbxCluster* cluster = skin->GetCluster(c); + remapCPsAndRemoveDuplicates(newCPCount, oldToNewCPMapping, cluster); + } + + } } \ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h old mode 100644 new mode 100755 index f66ce6b..75b4843 --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h @@ -1,144 +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) 2018 NVIDIA Corporation. All rights reserved. - - -#ifndef NVBLASTEXTEXPORTERFBXWRITER_H -#define NVBLASTEXTEXPORTERFBXWRITER_H - -#include "NvBlastExtExporter.h" -#include -#include -#include - -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 interior material index. - */ - virtual void setInteriorIndex(int32_t index) override; - - /** - Set true if FBX should be saved in ASCII mode. - */ - bool bOutputFBXAscii; - -private: - std::vector 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 sdkManager; - std::map chunkNodes; - std::map 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& materials, const AuthoringResult& aResult); - - void createChunkRecursiveNonSkinned(const std::string& meshName, uint32_t chunkIndex, fbxsdk::FbxNode* parentNode, - const std::vector& materials, const ExporterMeshData& meshData); - - void addControlPoints(fbxsdk::FbxMesh* mesh, const ExporterMeshData& meshData); - void addBindPose(); - - void generateSmoothingGroups(fbxsdk::FbxMesh* mesh, FbxSkin* skin); - void removeDuplicateControlPoints(fbxsdk::FbxMesh* mesh, FbxSkin* skin); - - int32_t mInteriorIndex; -}; - -} -} - +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTERFBXWRITER_H +#define NVBLASTEXTEXPORTERFBXWRITER_H + +#include "NvBlastExtExporter.h" +#include +#include +#include + +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 interior material index. + */ + virtual void setInteriorIndex(int32_t index) override; + + /** + Set true if FBX should be saved in ASCII mode. + */ + bool bOutputFBXAscii; + +private: + std::vector 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 sdkManager; + std::map chunkNodes; + std::map 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& materials, const AuthoringResult& aResult); + + void createChunkRecursiveNonSkinned(const std::string& meshName, uint32_t chunkIndex, fbxsdk::FbxNode* parentNode, + const std::vector& materials, const ExporterMeshData& meshData); + + void addControlPoints(fbxsdk::FbxMesh* mesh, const ExporterMeshData& meshData); + void addBindPose(); + + void generateSmoothingGroups(fbxsdk::FbxMesh* mesh, FbxSkin* skin); + void removeDuplicateControlPoints(fbxsdk::FbxMesh* mesh, FbxSkin* skin); + + int32_t mInteriorIndex; +}; + +} +} + #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 old mode 100644 new mode 100755 index e4e2746..7b63461 --- a/sdk/extensions/exporter/source/NvBlastExtExporterJsonCollision.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporterJsonCollision.cpp @@ -1,107 +1,137 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#include "NvBlastExtExporterJsonCollision.h" -#include "NvBlastExtAuthoringTypes.h" -#include -#include -#include -#include -#include - -#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; -}; +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#include "NvBlastExtExporterJsonCollision.h" +#include "NvBlastExtAuthoringTypes.h" +#include +#include +#include +#include +#include + +#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 << "}"; +} + + +/** +Implementation of object which serializes collision geometry to JSON format. +*/ +class JsonCollisionExporter : public IJsonCollisionExporter +{ +public: + JsonCollisionExporter() {} + ~JsonCollisionExporter() = default; + + virtual void release() override; + + virtual bool writeCollision(const char* path, uint32_t chunkCount, const uint32_t* hullOffsets, const CollisionHull* const * hulls) override; +}; + + +void +JsonCollisionExporter::release() +{ + delete this; +} + + +bool +JsonCollisionExporter::JsonCollisionExporter::writeCollision(const char* path, uint32_t chunkCount, const uint32_t* hullOffsets, const CollisionHull* const * 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 < chunkCount; ++i) + { + stream << "\t\t" << "[\n"; + for (uint32_t j = hullOffsets[i]; j < hullOffsets[i + 1]; ++j) + { + serializeCollisionHull(stream, *hulls[j], 3); + stream << ((j < hullOffsets[i + 1] - 1) ? ",\n" : "\n"); + } + stream << "\t\t" << ((i < chunkCount - 1) ? "], \n" : "]\n"); + } + stream << "\t]\n}"; + stream.close(); + return true; +}; + + +IJsonCollisionExporter* NvBlastExtExporterCreateJsonCollisionExporter() +{ + return new JsonCollisionExporter; +} diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp old mode 100644 new mode 100755 index 16da7d6..f188912 --- a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp @@ -1,162 +1,162 @@ -// 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) 2018 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 -#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 shapes; - std::vector mats; - std::string err; - std::string mtlPath; - - int32_t lastDelimeter = strlen(filename); - - while (lastDelimeter > 0 && filename[lastDelimeter] != '/' && filename[lastDelimeter] != '\\') - { - lastDelimeter--; - } - mtlPath = std::string(filename, filename + lastDelimeter); - mtlPath += '/'; - - bool ret = tinyobj::LoadObj(shapes, mats, err, filename, mtlPath.c_str()); - - // can't load? - if (!ret) - { - return; - } - if (shapes.size() > 1) - { - std::cout << "Can load only one object per mesh" << std::endl; - } - - if (!mats.empty()) - { - if (mats.size() == 1 && mats[0].name == "") - { - mats[0].name = "Default"; - } - for (uint32_t i = 0; i < mats.size(); ++i) - { - mMaterialNames.push_back(mats[i].name); - } - } - - 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; - mPerFaceMatId = shapes[0].mesh.material_ids; - for (uint32_t i = 0; i < mPerFaceMatId.size(); ++i) - { - if (mPerFaceMatId[i] == -1) // TinyOBJ loader sets ID to -1 when .mtl file not found. Set to default 0 material. - { - mPerFaceMatId[i] = 0; - } - } - -} - - -bool ObjFileReader::isCollisionLoaded() -{ - return false; -}; - - -uint32_t ObjFileReader::getCollision(uint32_t*& hullsOffset, Nv::Blast::CollisionHull**& hulls) -{ - hullsOffset = nullptr; - hulls = nullptr; - return 0; -}; - -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(); +// 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) 2018 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 +#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 shapes; + std::vector mats; + std::string err; + std::string mtlPath; + + int32_t lastDelimeter = strlen(filename); + + while (lastDelimeter > 0 && filename[lastDelimeter] != '/' && filename[lastDelimeter] != '\\') + { + lastDelimeter--; + } + mtlPath = std::string(filename, filename + lastDelimeter); + mtlPath += '/'; + + bool ret = tinyobj::LoadObj(shapes, mats, err, filename, mtlPath.c_str()); + + // can't load? + if (!ret) + { + return; + } + if (shapes.size() > 1) + { + std::cout << "Can load only one object per mesh" << std::endl; + } + + if (!mats.empty()) + { + if (mats.size() == 1 && mats[0].name == "") + { + mats[0].name = "Default"; + } + for (uint32_t i = 0; i < mats.size(); ++i) + { + mMaterialNames.push_back(mats[i].name); + } + } + + 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; + mPerFaceMatId = shapes[0].mesh.material_ids; + for (uint32_t i = 0; i < mPerFaceMatId.size(); ++i) + { + if (mPerFaceMatId[i] == -1) // TinyOBJ loader sets ID to -1 when .mtl file not found. Set to default 0 material. + { + mPerFaceMatId[i] = 0; + } + } + +} + + +bool ObjFileReader::isCollisionLoaded() +{ + return false; +}; + + +uint32_t ObjFileReader::getCollision(uint32_t*& hullsOffset, Nv::Blast::CollisionHull**& hulls) +{ + hullsOffset = nullptr; + hulls = nullptr; + return 0; +}; + +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 old mode 100644 new mode 100755 index c1210ab..7657be3 --- a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h @@ -1,126 +1,126 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#ifndef NVBLASTEXTEXPORTEROBJREADER_H -#define NVBLASTEXTEXPORTEROBJREADER_H -#include -#include -#include -#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 getIndicesCount() 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. - */ - int32_t* getMaterialIds() override { return mPerFaceMatId.data(); }; - - /** - Get loaded per triangle smoothing groups. Currently not supported by OBJ. - */ - int32_t* getSmoothingGroups() override { return nullptr; }; - - /** - Get material name. - */ - const char* getMaterialName(int32_t id) override { return mMaterialNames[id].c_str(); } - - /** - Get material count. - */ - int32_t getMaterialCount() { return mMaterialNames.size(); }; - -private: - std::vector mVertexPositions; - std::vector mVertexNormals; - std::vector mVertexUv; - std::vector mIndices; - - std::vector mMaterialNames; - std::vector mPerFaceMatId; - -}; - -} -} - +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTEROBJREADER_H +#define NVBLASTEXTEXPORTEROBJREADER_H +#include +#include +#include +#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 getIndicesCount() 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. + */ + int32_t* getMaterialIds() override { return mPerFaceMatId.data(); }; + + /** + Get loaded per triangle smoothing groups. Currently not supported by OBJ. + */ + int32_t* getSmoothingGroups() override { return nullptr; }; + + /** + Get material name. + */ + const char* getMaterialName(int32_t id) override { return mMaterialNames[id].c_str(); } + + /** + Get material count. + */ + int32_t getMaterialCount() { return mMaterialNames.size(); }; + +private: + std::vector mVertexPositions; + std::vector mVertexNormals; + std::vector mVertexUv; + std::vector mIndices; + + std::vector mMaterialNames; + std::vector mPerFaceMatId; + +}; + +} +} + #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 old mode 100644 new mode 100755 index 722a258..e6ec276 --- a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp @@ -1,265 +1,265 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#include "NvBlastExtExporterObjWriter.h" -#include -#include -#include "NvBlastExtAuthoringTypes.h" -#include "NvBlastExtAuthoringMesh.h" -#include - - -using namespace physx; -using namespace Nv::Blast; - -char* gTexPath = ""; - -void ObjFileWriter::release() -{ - delete this; -} - -void ObjFileWriter::setInteriorIndex(int32_t index) -{ - mIntSurfaceMatIndex = index; -} - -bool CompByMaterial(const Triangle& a, const Triangle& b) -{ - return a.materialId < b.materialId; -} - -bool ObjFileWriter::appendMesh(const AuthoringResult& aResult, const char* /*assetName*/, bool /*nonSkinned*/) -{ - mMeshData = std::shared_ptr(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->submeshMats; - delete[] md->uvs; - delete md; - }); - - - ExporterMeshData& md = *mMeshData.get(); - uint32_t triCount = aResult.geometryOffset[aResult.chunkCount]; - md.meshCount = aResult.chunkCount; - md.submeshCount = aResult.materialCount; - - int32_t additionalMats = 0; - - if (mIntSurfaceMatIndex == -1 || mIntSurfaceMatIndex >= (int32_t)md.submeshCount) - { - md.submeshCount += 1; - mIntSurfaceMatIndex = md.submeshCount - 1; - additionalMats = 1; - } - - md.submeshOffsets = new uint32_t[md.meshCount * md.submeshCount + 1]; - md.submeshMats = new Material[md.submeshCount]; - - for (uint32_t i = 0; i < md.submeshCount - additionalMats; ++i) - { - md.submeshMats[i].name = aResult.materialNames[i]; - md.submeshMats[i].diffuse_tex = nullptr; - } - - if (additionalMats) - { - md.submeshMats[mIntSurfaceMatIndex].name = interiorNameStr.c_str(); - md.submeshMats[mIntSurfaceMatIndex].diffuse_tex = nullptr; - } - 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; - - - - /** - Now we need to sort input trianles chunk they belong to, then by material; - */ - std::vector sorted; - sorted.reserve(triCount); - - - int32_t perChunkOffset = 0; - for (uint32_t i = 0; i < md.meshCount; ++i) - { - std::vector perMaterialCount(md.submeshCount); - - uint32_t first = aResult.geometryOffset[i]; - uint32_t last = aResult.geometryOffset[i + 1]; - uint32_t firstInSorted = sorted.size(); - for (uint32_t t = first; t < last; ++t) - { - sorted.push_back(aResult.geometry[t]); - int32_t cmat = sorted.back().materialId; - if (cmat == MATERIAL_INTERIOR) - { - cmat = mIntSurfaceMatIndex; - } - perMaterialCount[cmat]++; - } - for (uint32_t mof = 0; mof < md.submeshCount; ++mof) - { - md.submeshOffsets[i * md.submeshCount + mof] = perChunkOffset * 3; - perChunkOffset += perMaterialCount[mof]; - } - std::sort(sorted.begin() + firstInSorted, sorted.end(), CompByMaterial); - } - md.submeshOffsets[md.meshCount * md.submeshCount] = perChunkOffset * 3; - - for (uint32_t vc = 0; vc < triCount; ++vc) - { - Triangle& tri = sorted[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(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 %s\n", md.submeshMats[submeshIndex].name); - if (md.submeshMats[submeshIndex].diffuse_tex != nullptr) - { - fprintf(f, "\tmap_Kd %s\n", md.submeshMats[submeshIndex].diffuse_tex); - } - else - { - fprintf(f, "\tKd %f %f %f\n", float(rand()) / RAND_MAX, float(rand()) / RAND_MAX, float(rand()) / RAND_MAX); - } - 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) - { - fprintf(f, "g %d \n", 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]; - if (firstIdx == lastIdx) // There is no trianlges in this submesh. - { - continue; - } - fprintf(f, "usemtl %s\n", md.submeshMats[submeshIndex].name); - 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; - -} - +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#include "NvBlastExtExporterObjWriter.h" +#include +#include +#include "NvBlastExtAuthoringTypes.h" +#include "NvBlastExtAuthoringMesh.h" +#include + + +using namespace physx; +using namespace Nv::Blast; + +char* gTexPath = ""; + +void ObjFileWriter::release() +{ + delete this; +} + +void ObjFileWriter::setInteriorIndex(int32_t index) +{ + mIntSurfaceMatIndex = index; +} + +bool CompByMaterial(const Triangle& a, const Triangle& b) +{ + return a.materialId < b.materialId; +} + +bool ObjFileWriter::appendMesh(const AuthoringResult& aResult, const char* /*assetName*/, bool /*nonSkinned*/) +{ + mMeshData = std::shared_ptr(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->submeshMats; + delete[] md->uvs; + delete md; + }); + + + ExporterMeshData& md = *mMeshData.get(); + uint32_t triCount = aResult.geometryOffset[aResult.chunkCount]; + md.meshCount = aResult.chunkCount; + md.submeshCount = aResult.materialCount; + + int32_t additionalMats = 0; + + if (mIntSurfaceMatIndex == -1 || mIntSurfaceMatIndex >= (int32_t)md.submeshCount) + { + md.submeshCount += 1; + mIntSurfaceMatIndex = md.submeshCount - 1; + additionalMats = 1; + } + + md.submeshOffsets = new uint32_t[md.meshCount * md.submeshCount + 1]; + md.submeshMats = new Material[md.submeshCount]; + + for (uint32_t i = 0; i < md.submeshCount - additionalMats; ++i) + { + md.submeshMats[i].name = aResult.materialNames[i]; + md.submeshMats[i].diffuse_tex = nullptr; + } + + if (additionalMats) + { + md.submeshMats[mIntSurfaceMatIndex].name = interiorNameStr.c_str(); + md.submeshMats[mIntSurfaceMatIndex].diffuse_tex = nullptr; + } + 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; + + + + /** + Now we need to sort input trianles chunk they belong to, then by material; + */ + std::vector sorted; + sorted.reserve(triCount); + + + int32_t perChunkOffset = 0; + for (uint32_t i = 0; i < md.meshCount; ++i) + { + std::vector perMaterialCount(md.submeshCount); + + uint32_t first = aResult.geometryOffset[i]; + uint32_t last = aResult.geometryOffset[i + 1]; + uint32_t firstInSorted = sorted.size(); + for (uint32_t t = first; t < last; ++t) + { + sorted.push_back(aResult.geometry[t]); + int32_t cmat = sorted.back().materialId; + if (cmat == MATERIAL_INTERIOR) + { + cmat = mIntSurfaceMatIndex; + } + perMaterialCount[cmat]++; + } + for (uint32_t mof = 0; mof < md.submeshCount; ++mof) + { + md.submeshOffsets[i * md.submeshCount + mof] = perChunkOffset * 3; + perChunkOffset += perMaterialCount[mof]; + } + std::sort(sorted.begin() + firstInSorted, sorted.end(), CompByMaterial); + } + md.submeshOffsets[md.meshCount * md.submeshCount] = perChunkOffset * 3; + + for (uint32_t vc = 0; vc < triCount; ++vc) + { + Triangle& tri = sorted[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(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 %s\n", md.submeshMats[submeshIndex].name); + if (md.submeshMats[submeshIndex].diffuse_tex != nullptr) + { + fprintf(f, "\tmap_Kd %s\n", md.submeshMats[submeshIndex].diffuse_tex); + } + else + { + fprintf(f, "\tKd %f %f %f\n", float(rand()) / RAND_MAX, float(rand()) / RAND_MAX, float(rand()) / RAND_MAX); + } + 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) + { + fprintf(f, "g %d \n", 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]; + if (firstIdx == lastIdx) // There is no trianlges in this submesh. + { + continue; + } + fprintf(f, "usemtl %s\n", md.submeshMats[submeshIndex].name); + 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 old mode 100644 new mode 100755 index 68e025d..7685ea2 --- a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h +++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h @@ -1,80 +1,80 @@ -// 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) 2018 NVIDIA Corporation. All rights reserved. - - -#ifndef NVBLASTEXTEXPORTEROBJWRITER_H -#define NVBLASTEXTEXPORTEROBJWRITER_H - -#include "NvBlastExtExporter.h" -#include -#include -#include -#include -#include -struct NvBlastAsset; - -namespace Nv -{ -namespace Blast -{ - -class ObjFileWriter : public IMeshFileWriter -{ -public: - - ObjFileWriter(): mIntSurfaceMatIndex(-1), interiorNameStr("INTERIOR_MATERIAL") { }; - ~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; - - /** - Set interior material index. Not supported in OBJ since AuthoringTool doesn't created OBJ with materials currently. - */ - virtual void setInteriorIndex(int32_t index) override; - -private: - std::shared_ptr mMeshData; - int32_t mIntSurfaceMatIndex; - std::string interiorNameStr; -}; - -} -} - +// 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) 2018 NVIDIA Corporation. All rights reserved. + + +#ifndef NVBLASTEXTEXPORTEROBJWRITER_H +#define NVBLASTEXTEXPORTEROBJWRITER_H + +#include "NvBlastExtExporter.h" +#include +#include +#include +#include +#include +struct NvBlastAsset; + +namespace Nv +{ +namespace Blast +{ + +class ObjFileWriter : public IMeshFileWriter +{ +public: + + ObjFileWriter(): mIntSurfaceMatIndex(-1), interiorNameStr("INTERIOR_MATERIAL") { }; + ~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; + + /** + Set interior material index. Not supported in OBJ since AuthoringTool doesn't created OBJ with materials currently. + */ + virtual void setInteriorIndex(int32_t index) override; + +private: + std::shared_ptr mMeshData; + int32_t mIntSurfaceMatIndex; + std::string interiorNameStr; +}; + +} +} + #endif // NVBLASTEXTEXPORTEROBJWRITER_H \ No newline at end of file -- cgit v1.2.3