Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3319 lines
109 KiB

  1. /* = NULL */
  2. //
  3. //=============================================================================
  4. // Valve includes
  5. #include "filesystem.h"
  6. #include "fbxsystem/ifbxsystem.h"
  7. #include "fbxutils/dmfbxserializer.h"
  8. #include "movieobjects/dmedccmakefile.h"
  9. #include "movieobjects/dmeanimationlist.h"
  10. #include "movieobjects/dmeaxissystem.h"
  11. #include "movieobjects/dmechannel.h"
  12. #include "movieobjects/dmeclip.h"
  13. #include "movieobjects/dmecombinationoperator.h"
  14. #include "movieobjects/dmedag.h"
  15. #include "movieobjects/dmeexporttags.h"
  16. #include "movieobjects/dmefaceset.h"
  17. #include "movieobjects/dmelog.h"
  18. #include "movieobjects/dmematerial.h"
  19. #include "movieobjects/dmemesh.h"
  20. #include "movieobjects/dmemodel.h"
  21. #include "resourcefile/resourcedictionary.h"
  22. #ifdef SOURCE2
  23. #include "resourcesystem/resourcehandletypes.h"
  24. #endif
  25. #include "tier1/fmtstr.h"
  26. // Last include
  27. #include "tier0/memdbgon.h"
  28. //-----------------------------------------------------------------------------
  29. //
  30. //-----------------------------------------------------------------------------
  31. class CDmeFbxAxisSystem : public FbxAxisSystem
  32. {
  33. public:
  34. CDmeFbxAxisSystem(
  35. CDmeAxisSystem::Axis_t eUpAxis,
  36. CDmeAxisSystem::ForwardParity_t eForwardParity,
  37. CDmeAxisSystem::CoordSys_t eCoordSys )
  38. : FbxAxisSystem(
  39. static_cast< EUpVector >( eUpAxis ),
  40. static_cast< EFrontVector >( eForwardParity ),
  41. static_cast< ECoordSystem >( eCoordSys ) )
  42. {
  43. }
  44. CDmeFbxAxisSystem( EUpVector eUpVector, EFrontVector eFrontVector, ECoordSystem eCoordSystem )
  45. : FbxAxisSystem( eUpVector, eFrontVector, eCoordSystem )
  46. {
  47. }
  48. CDmeFbxAxisSystem( const FbxAxisSystem &rhs )
  49. {
  50. FbxAxisSystem::operator=( rhs );
  51. }
  52. CDmeFbxAxisSystem &operator=( const FbxAxisSystem &rhs )
  53. {
  54. FbxAxisSystem::operator=( rhs );
  55. return *this;
  56. }
  57. CUtlString GetAxes() const
  58. {
  59. int nUSign = 0;
  60. const int nU = GetUpAxis( nUSign );
  61. int nFSign = 0;
  62. const int nF = GetFrontAxis( nFSign );
  63. int nLSign = 0;
  64. const int nL = GetLeftAxis( nLSign );
  65. const char *szAxis[] = { "X", "Y", "Z" };
  66. return CUtlString( CFmtStr( "U: %s%s F: %s%s L: %s%s",
  67. nUSign < 0 ? "-" : " ", szAxis[nU],
  68. nFSign < 0 ? "-" : " ", szAxis[nF],
  69. nLSign < 0 ? "-" : " ", szAxis[nL] ).Get() );
  70. }
  71. CUtlString PrintRot( const FbxAxisSystem &from ) const
  72. {
  73. FbxMatrix mFbx;
  74. GetConversionMatrix( from, mFbx );
  75. FbxVector4 vT;
  76. FbxQuaternion qR;
  77. FbxVector4 vSh;
  78. FbxVector4 vSc;
  79. double flSign;
  80. mFbx.GetElements( vT, qR, vSh, vSc, flSign );
  81. FbxVector4 vR = qR.DecomposeSphericalXYZ();
  82. mFbx = mFbx.Inverse();
  83. mFbx.GetElements( vT, qR, vSh, vSc, flSign );
  84. FbxVector4 vI = qR.DecomposeSphericalXYZ();
  85. return CUtlString( CFmtStr( "F < %6.2f %6.2f %6.2f > I < %6.2f %6.2f %6.2f >\n",
  86. RAD2DEG( vR[0] ),
  87. RAD2DEG( vR[1] ),
  88. RAD2DEG( vR[2] ),
  89. RAD2DEG( vI[0] ),
  90. RAD2DEG( vI[1] ),
  91. RAD2DEG( vI[2] ) ).Get() );
  92. }
  93. void GetConversionRotation( RadianEuler &e, const FbxAxisSystem &from )
  94. {
  95. FbxMatrix mFbx;
  96. GetConversionMatrix( from, mFbx );
  97. mFbx = mFbx.Inverse();
  98. FbxVector4 vT;
  99. FbxQuaternion qR;
  100. FbxVector4 vSh;
  101. FbxVector4 vSc;
  102. double flSign;
  103. mFbx.GetElements( vT, qR, vSh, vSc, flSign );
  104. FbxVector4 vR = qR.DecomposeSphericalXYZ();
  105. e.x = vR[0];
  106. e.y = vR[1];
  107. e.z = vR[2];
  108. }
  109. int GetUpAxis( int &nSign ) const { nSign = mUpVector.mSign; return mUpVector.mAxis; };
  110. int GetFrontAxis( int &nSign ) const { nSign = mFrontVector.mSign; return mFrontVector.mAxis; };
  111. int GetLeftAxis( int &nSign ) const { nSign = mCoorSystem.mSign; return mCoorSystem.mAxis; };
  112. // static void Validate();
  113. };
  114. #if 0
  115. //-----------------------------------------------------------------------------
  116. //
  117. //-----------------------------------------------------------------------------
  118. void CDmeFbxAxisSystem::Validate()
  119. {
  120. const int fbxUp[] = { -FbxAxisSystem::eXAxis, -FbxAxisSystem::eYAxis, -FbxAxisSystem::eZAxis, FbxAxisSystem::eXAxis, FbxAxisSystem::eYAxis, FbxAxisSystem::eZAxis };
  121. const int fbxParity[] = { -FbxAxisSystem::eParityEven, -FbxAxisSystem::eParityOdd, FbxAxisSystem::eParityEven, FbxAxisSystem::eParityOdd };
  122. const int dmxUp[] = { CDmeAxisSystem::AS_AXIS_NX, CDmeAxisSystem::AS_AXIS_NY, CDmeAxisSystem::AS_AXIS_NZ, CDmeAxisSystem::AS_AXIS_X, CDmeAxisSystem::AS_AXIS_Y, CDmeAxisSystem::AS_AXIS_Z };
  123. const int dmxParity[] = { CDmeAxisSystem::AS_PARITY_NEVEN, CDmeAxisSystem::AS_PARITY_NODD, CDmeAxisSystem::AS_PARITY_EVEN, CDmeAxisSystem::AS_PARITY_ODD };
  124. COMPILE_TIME_ASSERT( ARRAYSIZE( fbxUp ) == ARRAYSIZE( dmxUp ) );
  125. COMPILE_TIME_ASSERT( ARRAYSIZE( fbxParity ) == ARRAYSIZE( dmxParity ) );
  126. int nOkCount = 0;
  127. int nBadCount = 0;
  128. for ( int i = 0; i < ARRAYSIZE( fbxUp ); ++i )
  129. {
  130. const int nFbxAU = fbxUp[i];
  131. const int nDmxAU = dmxUp[i];
  132. Assert( nFbxAU == nDmxAU );
  133. for ( int j = 0; j < ARRAYSIZE( fbxParity ); ++j )
  134. {
  135. const int nFbxAP = fbxParity[j];
  136. const int nDmxAP = dmxParity[j];
  137. Assert( nFbxAP == nDmxAP );
  138. // A = FROM
  139. CDmeFbxAxisSystem fA(
  140. static_cast< FbxAxisSystem::EUpVector >( nFbxAU ),
  141. static_cast< FbxAxisSystem::EFrontVector >( nFbxAP ),
  142. FbxAxisSystem::eRightHanded );
  143. const CDmeAxisSystem::Axis_t eAU = static_cast< CDmeAxisSystem::Axis_t >( nDmxAU );
  144. const CDmeAxisSystem::ForwardParity_t eAF = static_cast< CDmeAxisSystem::ForwardParity_t >( nDmxAP );
  145. const CDmeAxisSystem::CoordSys_t eAC = CDmeAxisSystem::AS_RIGHT_HANDED;
  146. for ( int k = 0; k < ARRAYSIZE( fbxUp ); ++k )
  147. {
  148. const int nFbxBU = fbxUp[k];
  149. const int nDmxBU = dmxUp[k];
  150. Assert( nFbxBU == nDmxBU );
  151. for ( int l = 0; l < ARRAYSIZE( fbxParity ); ++l )
  152. {
  153. const int nFbxBP = fbxParity[l];
  154. const int nDmxBP = dmxParity[l];
  155. Assert( nFbxBP == nDmxBP );
  156. // B = TO
  157. CDmeFbxAxisSystem fB(
  158. static_cast< FbxAxisSystem::EUpVector >( nFbxBU ),
  159. static_cast< FbxAxisSystem::EFrontVector >( nFbxBP ),
  160. FbxAxisSystem::eRightHanded );
  161. const CDmeAxisSystem::Axis_t eBU = static_cast< CDmeAxisSystem::Axis_t >( nDmxBU );
  162. const CDmeAxisSystem::ForwardParity_t eBF = static_cast< CDmeAxisSystem::ForwardParity_t >( nDmxBP );
  163. const CDmeAxisSystem::CoordSys_t eBC = CDmeAxisSystem::AS_RIGHT_HANDED;
  164. RadianEuler eFbx;
  165. fB.GetConversionRotation( eFbx, fA );
  166. if ( i == k && j == l )
  167. {
  168. Assert( RadianEulersAreEqual( eFbx, RadianEuler( 0.0f, 0.0f, 0.0f ), 1.0e-6 ) );
  169. }
  170. matrix3x4a_t mDmx;
  171. CDmeAxisSystem::GetConversionMatrix( mDmx,
  172. eBU, eBF, eBC,
  173. eAU, eAF, eAC );
  174. RadianEuler eDmx;
  175. MatrixAngles( mDmx, eDmx );
  176. if ( i == k && j == l )
  177. {
  178. Assert( RadianEulersAreEqual( eDmx, RadianEuler( 0.0f, 0.0f, 0.0f ), 1.0e-6 ) );
  179. }
  180. // Account for FBX/DMX differences in converting matrix to Euler
  181. Quaternion qFbx = eFbx;
  182. QuaternionNormalize( qFbx );
  183. Quaternion qDmx = eDmx;
  184. QuaternionNormalize( qDmx );
  185. eFbx = qFbx;
  186. eDmx = qDmx;
  187. if ( !QuaternionsAreEqual( qFbx, qDmx, 1.0e-6 ) )
  188. {
  189. Msg( " * FBX \"%s\" -> \"%s\": %6.2f %6.2f %6.2f\n", fA.GetAxes().Get(), fB.GetAxes().Get(),
  190. RAD2DEG( eFbx.x ),
  191. RAD2DEG( eFbx.y ),
  192. RAD2DEG( eFbx.z ) );
  193. Msg( " * DMX \"%s\" -> \"%s\": %6.2f %6.2f %6.2f\n",
  194. CDmeAxisSystem::GetAxisString( eAU, eAF, eAC ).Get(),
  195. CDmeAxisSystem::GetAxisString( eBU, eBF, eBC ).Get(),
  196. RAD2DEG( eDmx.x ),
  197. RAD2DEG( eDmx.y ),
  198. RAD2DEG( eDmx.z ) );
  199. Msg( "\n" );
  200. ++nBadCount;
  201. }
  202. else
  203. {
  204. ++nOkCount;
  205. }
  206. }
  207. }
  208. }
  209. }
  210. Msg( " * OK: %4d Bad: %d\n", nOkCount, nBadCount );
  211. }
  212. #endif // 0
  213. //-----------------------------------------------------------------------------
  214. //
  215. //-----------------------------------------------------------------------------
  216. const char *FbxImporterGetErrorString( FbxImporter *pFbxImporter )
  217. {
  218. #if FBXSDK_VERSION_MAJOR >= 2014
  219. return pFbxImporter->GetStatus().GetErrorString();
  220. #else // FBXSDK_VERSION_MAJOR
  221. return pFbxImporter->GetLastErrorString();
  222. #endif // FBXSDK_VERSION_MAJOR
  223. }
  224. //=============================================================================
  225. //
  226. // CDmFbxSerializer
  227. //
  228. //=============================================================================
  229. //-----------------------------------------------------------------------------
  230. //
  231. //-----------------------------------------------------------------------------
  232. CDmFbxSerializer::CDmFbxSerializer()
  233. : m_nOptVerbosity( 0 )
  234. , m_bOptUnderscoreForCorrectors( false )
  235. , m_bAnimation( false )
  236. , m_bReturnDmeModel( false )
  237. , m_flOptScale( 1.0f )
  238. {
  239. // Initialize Axis System To Maya Y Up
  240. CDmeAxisSystem::GetPredefinedAxisSystem( m_eOptUpAxis, m_eOptForwardParity, m_eCoordSys, CDmeAxisSystem::AS_MAYA_YUP );
  241. }
  242. //-----------------------------------------------------------------------------
  243. //
  244. //-----------------------------------------------------------------------------
  245. CDmFbxSerializer::~CDmFbxSerializer()
  246. {
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Common function both ReadFBX & Unserialize can call
  250. //-----------------------------------------------------------------------------
  251. CDmElement *CDmFbxSerializer::ReadFBX( const char *pszFilename )
  252. {
  253. FbxManager *pFbxManager = GetFbxManager();
  254. if ( !pFbxManager )
  255. return NULL;
  256. DmFileId_t nDmFileId = g_pDataModel->FindOrCreateFileId( pszFilename );
  257. if ( nDmFileId == DMFILEID_INVALID )
  258. {
  259. Warning( "Warning! Couldn't create DmFileId_t for \"%s\"\n", pszFilename );
  260. return NULL;
  261. }
  262. FbxTime::EMode eFbxTimeMode = FbxTime::eFrames30;
  263. FbxScene *pFbxScene = LoadFbxScene( eFbxTimeMode, pszFilename );
  264. if ( !pFbxScene )
  265. return NULL;
  266. if ( !FloatsAreEqual( m_flOptScale, 1.0f, 1.0e-4 ) )
  267. {
  268. FbxSystemUnit fbxSystemUnit( 1.0 / m_flOptScale );
  269. fbxSystemUnit.ConvertScene( pFbxScene );
  270. }
  271. CDmeFbxAxisSystem fromAxisSystem = pFbxScene->GetGlobalSettings().GetAxisSystem();
  272. CDmeFbxAxisSystem toAxisSystem( m_eOptUpAxis, m_eOptForwardParity, m_eCoordSys );
  273. toAxisSystem.ConvertScene( pFbxScene );
  274. CDmeAxisSystem::Axis_t eUpAxis;
  275. CDmeAxisSystem::ForwardParity_t eForwardParity;
  276. CDmeAxisSystem::CoordSys_t eCoordSys;
  277. {
  278. // nU [ -1, 1 ], eU [ 1, 2, 3 ]
  279. int nU = 0;
  280. const int eU = fromAxisSystem.GetUpVector( nU );
  281. eUpAxis = static_cast< CDmeAxisSystem::Axis_t >( nU * eU );
  282. // nF [ -1, 1 ], eF [ 1, 2 ]
  283. int nF = 0;
  284. const int eF = fromAxisSystem.GetFrontVector( nF );
  285. eForwardParity = static_cast< CDmeAxisSystem::ForwardParity_t >( nF * eF );
  286. // GetCoordSys() [ 0, 1 ]
  287. eCoordSys = static_cast< CDmeAxisSystem::CoordSys_t >( fromAxisSystem.GetCoorSystem() );
  288. }
  289. char szFileBase[ MAX_PATH ] = "";
  290. V_FileBase( pszFilename, szFileBase, ARRAYSIZE( szFileBase ) );
  291. CDmElement *pDmeRoot = NULL;
  292. CDmeModel *pDmeModel = CreateElement< CDmeModel >( szFileBase, nDmFileId );
  293. #ifdef SOURCE2
  294. pDmeModel->SetAxisSystem( m_eOptUpAxis, m_eOptForwardParity, m_eCoordSys );
  295. #endif
  296. if ( !m_bAnimation && m_bReturnDmeModel )
  297. {
  298. pDmeRoot = pDmeModel;
  299. }
  300. else
  301. {
  302. pDmeRoot = CreateElement< CDmElement >( "root", nDmFileId );
  303. pDmeRoot->SetValue( "skeleton", pDmeModel );
  304. if ( !m_bAnimation )
  305. {
  306. // Don't set "model" if animation only... Studiomdl can't handle it
  307. pDmeRoot->SetValue( "model", pDmeModel );
  308. }
  309. }
  310. g_pDataModel->SetFileRoot( nDmFileId, pDmeRoot->GetHandle() );
  311. CDmeDCCMakefile *pDmeMakefile = CreateElement< CDmeDCCMakefile >( "makefile", nDmFileId );
  312. pDmeRoot->SetValue( pDmeMakefile->GetName(), pDmeMakefile );
  313. // DMX sources are relative to the DMX file
  314. char szFullPath[ MAX_PATH ];
  315. if ( g_pFullFileSystem->RelativePathToFullPath( pszFilename, NULL, szFullPath, ARRAYSIZE( szFullPath ) ) )
  316. {
  317. pDmeMakefile->AddSource< CDmeSource >( szFullPath );
  318. }
  319. else
  320. {
  321. pDmeMakefile->AddSource< CDmeSource >( pszFilename );
  322. }
  323. FbxDocumentInfo *pFbxDocumentInfo = pFbxScene->GetSceneInfo();
  324. if ( pFbxDocumentInfo )
  325. {
  326. FbxString sUrl = pFbxDocumentInfo->LastSavedUrl.Get();
  327. FbxString sOrigFilename = pFbxDocumentInfo->Original_FileName.Get();
  328. char szUrl[MAX_PATH] = {};
  329. char szOrigFilename[MAX_PATH] = {};
  330. V_FixupPathName( szUrl, ARRAYSIZE( szUrl ), sUrl.Buffer() );
  331. V_FixSlashes( szUrl, '/' );
  332. V_FixupPathName( szOrigFilename, ARRAYSIZE( szOrigFilename ), sOrigFilename.Buffer() );
  333. V_FixSlashes( szOrigFilename, '/' );
  334. if ( V_strcmp( szUrl, szOrigFilename ) != 0 )
  335. {
  336. #ifdef SOURCE2
  337. char szRelativePath[MAX_PATH] = {};
  338. GenerateResourceNameFromFileName( sOrigFilename.Buffer(), szRelativePath, ARRAYSIZE( szRelativePath ) );
  339. if ( V_strlen( szRelativePath ) > 0 && GenerateStandardFullPathForResourceName( szRelativePath, RESOURCE_PATH_CONTENT, szFullPath, ARRAYSIZE( szFullPath ) ) )
  340. {
  341. pDmeMakefile->AddSource< CDmeSource >( szFullPath );
  342. }
  343. else
  344. #endif
  345. {
  346. pDmeMakefile->AddSource< CDmeSource >( sOrigFilename.Buffer() );
  347. }
  348. }
  349. }
  350. CDmeExportTags *pDmeExportTags = CreateElement< CDmeExportTags >( "exportTags", nDmFileId );
  351. pDmeExportTags->Init( "fbx2dmx", g_pFbx->GetFbxManager()->GetVersion() );
  352. pDmeExportTags->SetValue( "cmdLine", CommandLine()->GetCmdLine() );
  353. {
  354. char szCurrentDirectory[ MAX_PATH ];
  355. Plat_getwd( szCurrentDirectory, ARRAYSIZE( szCurrentDirectory ) );
  356. V_FixSlashes( szCurrentDirectory, '/' );
  357. pDmeExportTags->SetValue( "pwd", szCurrentDirectory );
  358. }
  359. pDmeRoot->SetValue( pDmeExportTags->GetName(), pDmeExportTags );
  360. CDmAttribute *pRootAttr = pDmeModel->AddAttribute( "__rootElement", AT_ELEMENT );
  361. if ( pRootAttr )
  362. {
  363. pRootAttr->AddFlag( FATTRIB_DONTSAVE );
  364. pRootAttr->SetValue( pDmeRoot );
  365. }
  366. FbxToDmxMap_t fbxToDmxMap( CDefOps< FbxToDmxMap_t::KeyType_t >::LessFunc );
  367. // Don't create a DmeDag for the root node
  368. FbxNode *pFbxRootNode = pFbxScene->GetRootNode();
  369. if ( Verbose2() )
  370. {
  371. Msg( " * Skeleton\n" );
  372. }
  373. for ( int i = 0; i < pFbxRootNode->GetChildCount(); ++i )
  374. {
  375. LoadModelAndSkeleton_R( fbxToDmxMap, pDmeModel, pDmeModel, pFbxRootNode->GetChild( i ), m_bAnimation, 0 );
  376. }
  377. pDmeModel->CaptureJointsToBaseState( "bind" );
  378. if ( m_bAnimation )
  379. {
  380. LoadAnimation( pDmeRoot, pDmeModel, fbxToDmxMap, pFbxScene, pFbxRootNode, eFbxTimeMode );
  381. }
  382. else
  383. {
  384. for ( int i = 0; i < pFbxRootNode->GetChildCount(); ++i )
  385. {
  386. SkinMeshes_R( fbxToDmxMap, pDmeModel, pFbxRootNode->GetChild( i ) );
  387. AddBlendShapes_R( fbxToDmxMap, pDmeRoot, pFbxRootNode->GetChild( i ) );
  388. }
  389. }
  390. pFbxScene->Destroy();
  391. pDmeModel->ConvertToAxisSystem( CDmeAxisSystem::AS_VALVE_ENGINE );
  392. return pDmeRoot;
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Feed the CDmElement returned by ReadFBX to see if there were non-fatal conversion errors which the user should be informed about
  396. //-----------------------------------------------------------------------------
  397. bool CDmFbxSerializer::HasConversionErrors( CDmElement *pDmRoot )
  398. {
  399. if ( !pDmRoot )
  400. return false;
  401. CDmAttribute *pConversionErrorsAttr = pDmRoot->GetAttribute( "conversionErrors", AT_STRING_ARRAY );
  402. if ( !pConversionErrorsAttr )
  403. return false;
  404. return ( CDmrStringArrayConst( pConversionErrorsAttr ).Count() > 0 );
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Feed the CDmElement returned by ReadFBX to see if there were non-fatal conversion errors which the user should be informed about, they are stored in pConversionErrors, if no errors, pConversionErrors is not touched
  408. //-----------------------------------------------------------------------------
  409. void CDmFbxSerializer::GetConversionErrors( CDmElement *pDmRoot, CUtlVector< CUtlString > *pConversionErrors )
  410. {
  411. if ( !pDmRoot || !pConversionErrors || !HasConversionErrors( pDmRoot ) )
  412. return;
  413. CDmAttribute *pConversionErrorsAttr = pDmRoot->GetAttribute( "conversionErrors", AT_STRING_ARRAY );
  414. if ( !pConversionErrorsAttr )
  415. return;
  416. CDmrStringArrayConst conversionErrors( pConversionErrorsAttr );
  417. for ( int i = 0; i < conversionErrors.Count(); ++i )
  418. {
  419. pConversionErrors->AddToTail( conversionErrors[i] );
  420. }
  421. }
  422. //-----------------------------------------------------------------------------
  423. //
  424. //-----------------------------------------------------------------------------
  425. FbxScene *CDmFbxSerializer::LoadFbxScene( FbxTime::EMode &eFbxTimeMode, const char *pszFilename )
  426. {
  427. FbxManager *pFbxManager = GetFbxManager();
  428. if ( !pFbxManager )
  429. return NULL;
  430. if ( Verbose1() )
  431. {
  432. Msg( "Reading FBX: %s\n", pszFilename );
  433. }
  434. // Get the file version number generate by the FBX SDK.
  435. int nSDKMajor = 0;
  436. int nSDKMinor = 0;
  437. int nSDKRevision = 0;
  438. FbxManager::GetFileFormatVersion( nSDKMajor, nSDKMinor, nSDKRevision );
  439. if ( Verbose2() )
  440. {
  441. Msg( "FBX file version %d.%d.%d - SDK\n", nSDKMajor, nSDKMinor, nSDKRevision );
  442. }
  443. FbxIOSettings *pFbxIOSettings = FbxIOSettings::Create( pFbxManager, IOSROOT );
  444. FbxImporter *pFbxImporter = FbxImporter::Create( pFbxManager, "" );
  445. pFbxImporter->ParseForGlobalSettings( true );
  446. // Initialize the importer by providing a filename.
  447. const bool bImportStatus = pFbxImporter->Initialize( pszFilename, -1, pFbxIOSettings );
  448. int nFileMajor = 0;
  449. int nFileMinor = 0;
  450. int nFileRevision = 0;
  451. pFbxImporter->GetFileVersion( nFileMajor, nFileMinor, nFileRevision );
  452. if ( !bImportStatus )
  453. {
  454. Warning( "Warning! Couldn't import specified file as FBX \"%s\": %s\n",
  455. pszFilename, pFbxImporter->GetStatus().GetErrorString() );
  456. if ( pFbxImporter->GetStatus().GetCode() == FbxStatus::eInvalidFileVersion )
  457. {
  458. Warning( "Warning! FBX file version mismatch SDK %d.%d.%d vs File %d.%d.%d\n",
  459. nSDKMajor, nSDKMinor, nSDKRevision,
  460. nFileMajor, nFileMinor, nFileRevision );
  461. }
  462. return NULL;
  463. }
  464. if ( pFbxImporter->IsFBX() )
  465. {
  466. if ( Verbose2() )
  467. {
  468. Msg( "FBX file version %d.%d.%d - %s\n", nFileMajor, nFileMinor, nFileRevision, pszFilename );
  469. // From this point, it is possible to access animation stack information without
  470. // the expense of loading the entire file.
  471. Msg( "Animation Stack Information\n");
  472. const int nAnimStackCount = pFbxImporter->GetAnimStackCount();
  473. Msg( " Number of Animation Stacks: %d\n", nAnimStackCount );
  474. Msg( " Current Animation Stack: \"%s\"\n", pFbxImporter->GetActiveAnimStackName().Buffer() );
  475. Msg( "\n" );
  476. for ( int i = 0; i < nAnimStackCount; ++i )
  477. {
  478. FbxTakeInfo *pFbxTakeInfo = pFbxImporter->GetTakeInfo( i );
  479. Msg( " Animation Stack %d\n", i );
  480. Msg( " Name: \"%s\"\n", pFbxTakeInfo->mName.Buffer() );
  481. Msg( " Description: \"%s\"\n", pFbxTakeInfo->mDescription.Buffer() );
  482. // Change the value of the import name if the animation stack should be imported
  483. // under a different name.
  484. Msg( " Import Name: \"%s\"\n", pFbxTakeInfo->mImportName.Buffer() );
  485. // Set the value of the import state to false if the animation stack should be not
  486. // be imported.
  487. Msg( " Import State: %s\n", pFbxTakeInfo->mSelect ? "true" : "false" );
  488. Msg( "\n");
  489. }
  490. }
  491. // Set the import states. By default, the import states are always set to
  492. // true. The code below shows how to change these states.
  493. pFbxIOSettings->SetBoolProp( IMP_FBX_MATERIAL, true );
  494. pFbxIOSettings->SetBoolProp( IMP_FBX_TEXTURE, true );
  495. pFbxIOSettings->SetBoolProp( IMP_FBX_LINK, true );
  496. pFbxIOSettings->SetBoolProp( IMP_FBX_SHAPE, true );
  497. pFbxIOSettings->SetBoolProp( IMP_FBX_GOBO, true );
  498. pFbxIOSettings->SetBoolProp( IMP_FBX_ANIMATION, true );
  499. pFbxIOSettings->SetBoolProp( IMP_FBX_GLOBAL_SETTINGS, true );
  500. }
  501. FbxScene *pFbxScene = FbxScene::Create( pFbxManager, "" );
  502. if ( pFbxScene )
  503. {
  504. // Import the scene.
  505. bool bStatus = pFbxImporter->Import( pFbxScene );
  506. if ( bStatus == false && pFbxImporter->GetStatus().GetCode() == FbxStatus::ePasswordError )
  507. {
  508. Warning( "Warning! Password protected FBX files unsupported\n" );
  509. pFbxScene->Destroy();
  510. pFbxScene = NULL;
  511. /* TODO: Handle password protected FBX files...
  512. Msg( "Please enter password: ");
  513. char szPassword[1024];
  514. szPassword[0] = '\0';
  515. FBXSDK_CRT_SECURE_NO_WARNING_BEGIN
  516. scanf( "%s", szPassword );
  517. FBXSDK_CRT_SECURE_NO_WARNING_END
  518. FbxString lString(szPassword);
  519. pFbxIOSettings->SetStringProp( IMP_FBX_PASSWORD, lString );
  520. pFbxIOSettings->SetBoolProp( IMP_FBX_PASSWORD_ENABLE, true );
  521. bStatus = pFbxImporter->Import( pFbxScene );
  522. if ( bStatus == false && pFbxImporter->GetLastErrorID() == FbxIOBase::ePasswordError )
  523. {
  524. Msg( "\nPassword is wrong, import aborted.\n" );
  525. pFbxScene->Destroy();
  526. pFbxScene = NULL;
  527. }
  528. */
  529. }
  530. eFbxTimeMode = FbxTime::eFrames30;
  531. if ( !pFbxImporter->GetFrameRate( eFbxTimeMode ) )
  532. {
  533. eFbxTimeMode = FbxTime::eFrames30;
  534. }
  535. // Destroy the importer.
  536. pFbxImporter->Destroy();
  537. }
  538. else
  539. {
  540. Warning( "Warning! Couldn't create FbxScene\n" );
  541. }
  542. return pFbxScene;
  543. }
  544. //-----------------------------------------------------------------------------
  545. //
  546. //-----------------------------------------------------------------------------
  547. void CDmFbxSerializer::LoadModelAndSkeleton_R(
  548. FbxToDmxMap_t &fbxToDmxMap, CDmeModel *pDmeModel, CDmeDag *pDmeDagParent, FbxNode *pFbxNode, bool bAnimation, int nDepth ) const
  549. {
  550. FbxString sIndent;
  551. for ( int i = 0; i < nDepth; ++i )
  552. {
  553. sIndent += " ";
  554. }
  555. CDmeDag *pDmeDag = NULL;
  556. const char *pszFbxType = NULL;
  557. const char *pszDmeType = NULL;
  558. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  559. if ( pFbxNodeAttribute )
  560. {
  561. const FbxNodeAttribute::EType nAttributeType = pFbxNodeAttribute->GetAttributeType();
  562. switch ( nAttributeType )
  563. {
  564. case FbxNodeAttribute::eNull:
  565. pszFbxType = "Transform";
  566. if ( pFbxNode->GetDstObjectCount( FbxCluster::ClassId ) > 0 )
  567. {
  568. pDmeDag = FbxNodeToDmeDag( pDmeDagParent, pFbxNode, "DmeJoint" );
  569. }
  570. else
  571. {
  572. pDmeDag = FbxNodeToDmeDag( pDmeDagParent, pFbxNode, "DmeDag" );
  573. }
  574. break;
  575. case FbxNodeAttribute::eSkeleton:
  576. pszFbxType = "Skeleton";
  577. pDmeDag = FbxNodeToDmeDag( pDmeDagParent, pFbxNode, "DmeJoint" );
  578. break;
  579. case FbxNodeAttribute::eMesh:
  580. {
  581. pszFbxType = "Mesh";
  582. pszDmeType = "DmeMesh";
  583. FbxMatrix mScale;
  584. pDmeDag = FbxNodeToDmeDag( pDmeDagParent, pFbxNode, "DmeDag", &mScale );
  585. if ( !bAnimation )
  586. {
  587. FbxShapeToDmeMesh( pDmeDag, pFbxNode, mScale );
  588. }
  589. }
  590. break;
  591. default:
  592. Warning( "Warning! Ignoring Unsupported FBX Node Attribute Type: %s.%s(%s)\n",
  593. pFbxNode->GetName(), pFbxNodeAttribute->GetName(), pFbxNodeAttribute->GetTypeName() );
  594. break;
  595. }
  596. }
  597. if ( Verbose2() && pDmeDag && pszFbxType )
  598. {
  599. Msg( "%s + %-8s %s\n",
  600. sIndent.Buffer(), pszDmeType ? pszDmeType : pDmeDag->GetTypeString(), pDmeDag->GetName() );
  601. }
  602. if ( pDmeDag )
  603. {
  604. pDmeModel->AddJoint( pDmeDag );
  605. fbxToDmxMap.Insert( pFbxNode, pDmeDag );
  606. for ( int i = 0; i < pFbxNode->GetChildCount(); ++i )
  607. {
  608. LoadModelAndSkeleton_R( fbxToDmxMap, pDmeModel, pDmeDag, pFbxNode->GetChild( i ), bAnimation, nDepth + 1 );
  609. }
  610. }
  611. }
  612. //-----------------------------------------------------------------------------
  613. // Converts an FbxMatrix to a Valve matrix3x4_t (transpose)
  614. //-----------------------------------------------------------------------------
  615. inline void MatrixFbxToValve( matrix3x4_t &mValve, const FbxMatrix &mFbx )
  616. {
  617. mValve[0][0] = mFbx[0][0]; mValve[0][1] = mFbx[1][0]; mValve[0][2] = mFbx[2][0]; mValve[0][3] = mFbx[3][0];
  618. mValve[1][0] = mFbx[0][1]; mValve[1][1] = mFbx[1][1]; mValve[1][2] = mFbx[2][1]; mValve[1][3] = mFbx[3][1];
  619. mValve[2][0] = mFbx[0][2]; mValve[2][1] = mFbx[1][2]; mValve[2][2] = mFbx[2][2]; mValve[2][3] = mFbx[3][2];
  620. }
  621. //-----------------------------------------------------------------------------
  622. // Converts a Valve matrix3x4_t to an FbxMatrix (transpose)
  623. //-----------------------------------------------------------------------------
  624. inline void MatrixValveToFbx( FbxMatrix &mFbx, const matrix3x4_t &mValve )
  625. {
  626. mFbx[0][0] = mValve[0][0]; mFbx[0][1] = mValve[1][0]; mFbx[0][2] = mValve[2][0]; mFbx[3][0] = 0.0;
  627. mFbx[1][0] = mValve[0][1]; mFbx[1][1] = mValve[1][1]; mFbx[1][2] = mValve[2][1]; mFbx[3][1] = 0.0;
  628. mFbx[2][0] = mValve[0][2]; mFbx[2][1] = mValve[1][2]; mFbx[2][2] = mValve[2][2]; mFbx[3][2] = 0.0;
  629. mFbx[3][0] = mValve[0][3]; mFbx[3][1] = mValve[1][3]; mFbx[3][2] = mValve[2][3]; mFbx[3][3] = 1.0;
  630. }
  631. //-----------------------------------------------------------------------------
  632. //
  633. //-----------------------------------------------------------------------------
  634. CDmeDag *CDmFbxSerializer::FbxNodeToDmeDag( CDmeDag *pDmeDagParent, FbxNode *pFbxNode, const char *pszDmeType, FbxMatrix *pmOutScale /* = NULL */ ) const
  635. {
  636. const bool bRoot = pDmeDagParent->IsA( CDmeModel::GetStaticTypeSymbol() );
  637. const FbxAMatrix &mFbxWorld = pFbxNode->EvaluateGlobalTransform();
  638. const FbxVector4 vFbxTranslate = mFbxWorld.GetT();
  639. const FbxQuaternion qFbxRotate = mFbxWorld.GetQ();
  640. Assert( vFbxTranslate[3] == 0.0 || vFbxTranslate[3] == 1.0 );
  641. CUtlString sName;
  642. GetName( sName, pFbxNode );
  643. Vector vDmeTranslate( vFbxTranslate[0], vFbxTranslate[1], vFbxTranslate[2] );
  644. Quaternion qDmeRotate( qFbxRotate[0], qFbxRotate[1], qFbxRotate[2], qFbxRotate[3] );
  645. DmElementHandle_t hElement = g_pDataModel->CreateElement( pszDmeType, sName.String(), pDmeDagParent->GetFileId() );
  646. CDmeDag *pDmeDag = CastElement< CDmeDag >( g_pDataModel->GetElement( hElement ) );
  647. CDmeTransform *pDmeTransform = pDmeDag->GetTransform();
  648. pDmeTransform->SetName( pDmeDag->GetName() );
  649. if ( !bRoot )
  650. {
  651. matrix3x4_t mDmeParentWorld;
  652. pDmeDagParent->GetAbsTransform( mDmeParentWorld );
  653. matrix3x4_t mDmeParentWorldInverse;
  654. MatrixInvert( mDmeParentWorld, mDmeParentWorldInverse );
  655. matrix3x4_t mDmeWorld;
  656. AngleMatrix( RadianEuler( qDmeRotate ), vDmeTranslate, mDmeWorld );
  657. matrix3x4_t mDmeLocal;
  658. MatrixMultiply( mDmeParentWorldInverse, mDmeWorld, mDmeLocal );
  659. MatrixAngles( mDmeLocal, qDmeRotate, vDmeTranslate );
  660. }
  661. else
  662. {
  663. CDmAttribute *pDmeRootNodeAttr = pDmeDag->AddAttribute( "__rootNode", AT_BOOL );
  664. pDmeRootNodeAttr->AddFlag( FATTRIB_DONTSAVE );
  665. pDmeRootNodeAttr->SetValue( bRoot );
  666. }
  667. pDmeTransform->SetOrientation( qDmeRotate );
  668. pDmeTransform->SetPosition( vDmeTranslate );
  669. pDmeDagParent->AddChild( pDmeDag );
  670. if ( pmOutScale )
  671. {
  672. FbxMatrix mfWf = mFbxWorld;
  673. FbxMatrix mfWv;
  674. {
  675. matrix3x4_t mvWv; // mvWv = World Matrix3x4 Valve
  676. pDmeDag->GetAbsTransform( mvWv );
  677. MatrixValveToFbx( mfWv, mvWv );
  678. }
  679. const FbxMatrix mfWv_ = mfWv.Inverse();
  680. *pmOutScale = mfWv_ * mfWf;
  681. }
  682. return pDmeDag;
  683. }
  684. //-----------------------------------------------------------------------------
  685. //
  686. //-----------------------------------------------------------------------------
  687. bool AddPositionData( CDmeVertexData *pDmeVertexData, const CUtlVector< int > &nIndices, const FbxMesh *pFbxMesh, const FbxMatrix &mScale )
  688. {
  689. if ( !pFbxMesh || nIndices.Count() <= 0 )
  690. return false;
  691. const FbxVector4 *pvFbxData = pFbxMesh->GetControlPoints();
  692. const int nDataCount = pFbxMesh->GetControlPointsCount();
  693. CUtlVector< Vector > dmxData;
  694. dmxData.SetCount( nDataCount );
  695. for ( int i = 0; i < dmxData.Count(); ++i )
  696. {
  697. Vector &vDmxData = dmxData[i];
  698. const FbxVector4 vFbxData = mScale.MultNormalize( pvFbxData[i] );
  699. vDmxData.x = vFbxData[0];
  700. vDmxData.y = vFbxData[1];
  701. vDmxData.z = vFbxData[2];
  702. Assert( vFbxData[3] == 0 || vFbxData[3] == 1 );
  703. }
  704. const FieldIndex_t nFieldIndex = pDmeVertexData->CreateField( CDmeVertexDataBase::FIELD_POSITION );
  705. pDmeVertexData->AddVertexData( nFieldIndex, dmxData.Count() );
  706. pDmeVertexData->SetVertexData( nFieldIndex, 0, dmxData.Count(), AT_VECTOR3, dmxData.Base() );
  707. pDmeVertexData->AddVertexIndices( nIndices.Count() );
  708. pDmeVertexData->SetVertexIndices( nFieldIndex, 0, nIndices.Count(), nIndices.Base() );
  709. return true;
  710. }
  711. //-----------------------------------------------------------------------------
  712. //
  713. //-----------------------------------------------------------------------------
  714. void AddUVData( CDmeVertexData *pDmeVertexData, const CUtlVector< int > &nIndices, const FbxGeometryElementUV *pFbxElement, int nSemanticIndex )
  715. {
  716. if ( !pFbxElement || nIndices.Count() <= 0 )
  717. return;
  718. const FbxLayerElementArrayTemplate< FbxVector2 > &fbxData = pFbxElement->GetDirectArray();
  719. const int nDataCount = fbxData.GetCount();
  720. CUtlVector< Vector2D > dmxData;
  721. dmxData.SetCount( nDataCount );
  722. for ( int i = 0; i < dmxData.Count(); ++i )
  723. {
  724. Vector2D &vDmxData = dmxData[i];
  725. const FbxVector2 vFbxData = fbxData[i];
  726. vDmxData.x = vFbxData[0];
  727. vDmxData.y = vFbxData[1];
  728. }
  729. FieldIndex_t nFieldIndex = -1;
  730. if ( nSemanticIndex == 0 )
  731. {
  732. nFieldIndex = pDmeVertexData->CreateField( CDmeVertexDataBase::FIELD_TEXCOORD );
  733. }
  734. #ifdef SOURCE2
  735. else if ( nSemanticIndex == 1 )
  736. {
  737. nFieldIndex = pDmeVertexData->CreateField( CDmeVertexDataBase::FIELD_TEXCOORD2 );
  738. }
  739. #endif
  740. else
  741. {
  742. // No FIELD_TEXCOORD3, no method to get the standard field name without the semantic index
  743. // so magically we know it will be "texcoord$#" where # is the semantic index
  744. nFieldIndex = pDmeVertexData->CreateField< Vector2D >( CFmtStr( "texcoord$%d", nSemanticIndex ).Get() );
  745. }
  746. pDmeVertexData->AddVertexData( nFieldIndex, dmxData.Count() );
  747. pDmeVertexData->SetVertexData( nFieldIndex, 0, dmxData.Count(), AT_VECTOR2, dmxData.Base() );
  748. pDmeVertexData->SetVertexIndices( nFieldIndex, 0, nIndices.Count(), nIndices.Base() );
  749. }
  750. //-----------------------------------------------------------------------------
  751. //
  752. //-----------------------------------------------------------------------------
  753. void AddNormalData( CDmeVertexData *pDmeVertexData, const CUtlVector< int > &nIndices, const FbxGeometryElementNormal *pFbxElement, const FbxMatrix &mScale )
  754. {
  755. if ( !pFbxElement || nIndices.Count() <= 0 )
  756. return;
  757. // Normals being FbxVector4's are a little weird but just using x, y, z is fine.
  758. // There seems to be breakage when loading an FBX 2014 file in FBX 2013 SDK because they
  759. // added a NormalsW layer which isn't actually put into the W component but this gets the
  760. // right x, y, z values regardless if w is just ignored
  761. const FbxLayerElementArrayTemplate< FbxVector4 > &fbxData = pFbxElement->GetDirectArray();
  762. const int nDataCount = fbxData.GetCount();
  763. CUtlVector< Vector > dmxData;
  764. dmxData.SetCount( nDataCount );
  765. if ( mScale == FbxMatrix() )
  766. {
  767. // If the scale is the identity matrix
  768. for ( int i = 0; i < dmxData.Count(); ++i )
  769. {
  770. Vector &vDmxData = dmxData[i];
  771. const FbxVector4 vFbxData = fbxData[i];
  772. vDmxData.x = vFbxData[0];
  773. vDmxData.y = vFbxData[1];
  774. vDmxData.z = vFbxData[2];
  775. }
  776. }
  777. else
  778. {
  779. const FbxMatrix mInvTranspose = mScale.Inverse().Transpose();
  780. for ( int i = 0; i < dmxData.Count(); ++i )
  781. {
  782. Vector &vDmxData = dmxData[i];
  783. FbxVector4 vFbxData = mInvTranspose.MultNormalize( fbxData[i] );
  784. vFbxData.Normalize();
  785. vDmxData.x = vFbxData[0];
  786. vDmxData.y = vFbxData[1];
  787. vDmxData.z = vFbxData[2];
  788. }
  789. }
  790. const FieldIndex_t nFieldIndex = pDmeVertexData->CreateField( CDmeVertexDataBase::FIELD_NORMAL );
  791. pDmeVertexData->AddVertexData( nFieldIndex, dmxData.Count() );
  792. pDmeVertexData->SetVertexData( nFieldIndex, 0, dmxData.Count(), AT_VECTOR3, dmxData.Base() );
  793. pDmeVertexData->SetVertexIndices( nFieldIndex, 0, nIndices.Count(), nIndices.Base() );
  794. }
  795. //-----------------------------------------------------------------------------
  796. //
  797. //-----------------------------------------------------------------------------
  798. void CDmFbxSerializer::AddColorData( CDmeVertexData *pDmeVertexData, const CUtlVector< int > &nIndices, const FbxGeometryElementVertexColor *pFbxElement ) const
  799. {
  800. if ( !pFbxElement || nIndices.Count() <= 0 )
  801. return;
  802. const FbxLayerElementArrayTemplate< FbxColor > &fbxData = pFbxElement->GetDirectArray();
  803. const int nDataCount = fbxData.GetCount();
  804. if ( nDataCount <= 0 )
  805. return;
  806. const char *pszName = pFbxElement->GetName();
  807. const char *pszChannelName = pszName;
  808. bool bUseChannelName = false;
  809. DmAttributeType_t dmAttributeType = AT_COLOR;
  810. if ( StringHasPrefix( pszName, "export_" ) )
  811. {
  812. pszChannelName = StringAfterPrefix( pszName, "export_" );
  813. if ( V_strlen( pszChannelName ) > 0 )
  814. {
  815. bUseChannelName = true;
  816. // Currently "foliageanimation" is a special case which gets converted to a Vector3
  817. if ( !V_stricmp( pszChannelName, "foliageanimation" ) )
  818. {
  819. dmAttributeType = AT_VECTOR3;
  820. pszChannelName = "foliageanimation"; // Make it lowercase for certain
  821. }
  822. }
  823. else
  824. {
  825. pszChannelName = pszName;
  826. }
  827. }
  828. FieldIndex_t nFieldIndex = -1;
  829. if ( bUseChannelName )
  830. {
  831. nFieldIndex = pDmeVertexData->CreateField( pszChannelName, ValueTypeToArrayType( dmAttributeType ) );
  832. }
  833. else
  834. {
  835. CFmtStr sFieldName;
  836. int nSemanticIndex = 0;
  837. for ( ;; )
  838. {
  839. sFieldName.sprintf( "color$%d", nSemanticIndex );
  840. FieldIndex_t nTmpField = pDmeVertexData->FindFieldIndex( sFieldName.Get() );
  841. if ( nTmpField < 0 )
  842. break;
  843. ++nSemanticIndex;
  844. }
  845. nFieldIndex = pDmeVertexData->CreateField( sFieldName.Get(), ValueTypeToArrayType( dmAttributeType ) );
  846. }
  847. if ( nFieldIndex < 0 )
  848. {
  849. AddConversionError( pDmeVertexData->GetFileId(), CFmtStr( "Couldn't convert color field: %s\n", pszName ).Get() );
  850. return;
  851. }
  852. if ( dmAttributeType == AT_VECTOR3 )
  853. {
  854. CUtlVector< Vector > dmxData;
  855. dmxData.SetCount( nDataCount );
  856. for ( int i = 0; i < dmxData.Count(); ++i )
  857. {
  858. Vector &vDmxData = dmxData[i];
  859. const FbxColor vFbxData = fbxData[i];
  860. vDmxData[0] = vFbxData[0];
  861. vDmxData[1] = vFbxData[1];
  862. vDmxData[2] = vFbxData[2];
  863. // Alpha gets discarded when converting to Vector3, all FBX color channels are RGBA
  864. }
  865. pDmeVertexData->AddVertexData( nFieldIndex, dmxData.Count() );
  866. pDmeVertexData->SetVertexData( nFieldIndex, 0, dmxData.Count(), dmAttributeType, dmxData.Base() );
  867. }
  868. else
  869. {
  870. Assert( dmAttributeType == AT_COLOR );
  871. CUtlVector< Color > dmxData;
  872. dmxData.SetCount( nDataCount );
  873. for ( int i = 0; i < dmxData.Count(); ++i )
  874. {
  875. Color &vDmxData = dmxData[i];
  876. const FbxColor vFbxData = fbxData[i];
  877. vDmxData.SetColor(
  878. clamp( static_cast< uint8 >( floor( vFbxData[0] * 255.0 ) ), 0, 255 ),
  879. clamp( static_cast< uint8 >( floor( vFbxData[1] * 255.0 ) ), 0, 255 ),
  880. clamp( static_cast< uint8 >( floor( vFbxData[2] * 255.0 ) ), 0, 255 ),
  881. clamp( static_cast< uint8 >( floor( vFbxData[3] * 255.0 ) ), 0, 255 ) );
  882. }
  883. pDmeVertexData->AddVertexData( nFieldIndex, dmxData.Count() );
  884. pDmeVertexData->SetVertexData( nFieldIndex, 0, dmxData.Count(), dmAttributeType, dmxData.Base() );
  885. }
  886. pDmeVertexData->SetVertexIndices( nFieldIndex, 0, nIndices.Count(), nIndices.Base() );
  887. }
  888. //-----------------------------------------------------------------------------
  889. // UV Color data is a special case for 3DSMax
  890. //-----------------------------------------------------------------------------
  891. void CDmFbxSerializer::AddUVColorData( CDmeVertexData *pDmeVertexData, const UVColorChannelData_t &uvColorData ) const
  892. {
  893. // Normally UVColor data is exported as a VECTOR3 data in DMX, set this to true to also export as a color map
  894. const bool bExportColor = false;
  895. for ( int i = 0; i < ARRAYSIZE( uvColorData.m_uvChannelData ); ++i )
  896. {
  897. if ( !uvColorData.m_uvChannelData[i].m_pFbxElementUV || uvColorData.m_uvChannelData[i].m_nIndices.IsEmpty() )
  898. return;
  899. }
  900. CUtlVector< Color > dmxColorData;
  901. CUtlVector< Vector > dmxData;
  902. CUtlVector< int > indices;
  903. for ( int i = 0; i < ARRAYSIZE( uvColorData.m_uvChannelData ); ++i )
  904. {
  905. const CUtlVector< int > &srcIndices = uvColorData.m_uvChannelData[i].m_nIndices;
  906. if ( indices.IsEmpty() )
  907. {
  908. const int nIndexCount = srcIndices.Count();
  909. indices.SetCount( nIndexCount );
  910. dmxColorData.SetCount( nIndexCount );
  911. dmxData.SetCount( nIndexCount );
  912. for ( int j = 0; j < nIndexCount; ++j )
  913. {
  914. indices[j] = j;
  915. }
  916. if ( bExportColor )
  917. {
  918. for ( int j = 0; j < nIndexCount; ++j )
  919. {
  920. dmxColorData[j] = Color( 0, 0, 0, 255 );
  921. }
  922. }
  923. }
  924. else
  925. {
  926. if ( indices.Count() != srcIndices.Count() )
  927. {
  928. Warning( "Warning! Fbx 3DS Max index mismatch for channel \"%s\", got %d expected %d\n", uvColorData.m_uvChannelData[i].m_pFbxElementUV->GetName(), srcIndices.Count(), indices.Count() );
  929. return;
  930. }
  931. }
  932. }
  933. for ( int i = 0; i < ARRAYSIZE( uvColorData.m_uvChannelData ); ++i )
  934. {
  935. const FbxLayerElementArrayTemplate< FbxVector2 > &fbxData = uvColorData.m_uvChannelData[i].m_pFbxElementUV->GetDirectArray();
  936. const int nDataCount = fbxData.GetCount();
  937. if ( nDataCount <= 0 )
  938. return;
  939. const CUtlVector< int > &fbxIndices = uvColorData.m_uvChannelData[i].m_nIndices;
  940. for ( int j = 0; j < indices.Count(); ++j )
  941. {
  942. const int nDmxDataIndex = indices[j]; Assert( nDmxDataIndex == j );
  943. const int nFbxDataIndex = fbxIndices[nDmxDataIndex];
  944. const FbxVector2 &vFbxData = fbxData[nFbxDataIndex];
  945. Vector &vDmxData = dmxData[nDmxDataIndex];
  946. vDmxData[i] = vFbxData[0]; // Always use value 0
  947. if ( bExportColor )
  948. {
  949. Color &vDmxColorData = dmxColorData[nDmxDataIndex];
  950. vDmxColorData[i] = clamp( static_cast< uint8 >( floor( vFbxData[0] * 255.0 ) ), 0, 255 ); // Always use value 0
  951. }
  952. }
  953. }
  954. {
  955. const DmAttributeType_t dmAttributeType = AT_VECTOR3;
  956. const FieldIndex_t nFieldIndex = pDmeVertexData->CreateField( uvColorData.m_sChannelName.Get(), ValueTypeToArrayType( dmAttributeType ) );
  957. pDmeVertexData->AddVertexData( nFieldIndex, dmxData.Count() );
  958. pDmeVertexData->SetVertexData( nFieldIndex, 0, dmxData.Count(), dmAttributeType, dmxData.Base() );
  959. pDmeVertexData->SetVertexIndices( nFieldIndex, 0, indices.Count(), indices.Base() );
  960. pDmeVertexData->Resolve();
  961. }
  962. if ( bExportColor )
  963. {
  964. CFmtStr sFieldName;
  965. int nSemanticIndex = 0;
  966. for ( ;; )
  967. {
  968. sFieldName.sprintf( "color$%d", nSemanticIndex );
  969. FieldIndex_t nTmpField = pDmeVertexData->FindFieldIndex( sFieldName.Get() );
  970. if ( nTmpField < 0 )
  971. break;
  972. ++nSemanticIndex;
  973. }
  974. const DmAttributeType_t dmAttributeType = AT_COLOR;
  975. const FieldIndex_t nFieldIndex = pDmeVertexData->CreateField( sFieldName.Get(), ValueTypeToArrayType( dmAttributeType ) );
  976. pDmeVertexData->AddVertexData( nFieldIndex, dmxColorData.Count() );
  977. pDmeVertexData->SetVertexData( nFieldIndex, 0, dmxColorData.Count(), dmAttributeType, dmxColorData.Base() );
  978. pDmeVertexData->SetVertexIndices( nFieldIndex, 0, indices.Count(), indices.Base() );
  979. pDmeVertexData->Resolve();
  980. }
  981. }
  982. //-----------------------------------------------------------------------------
  983. //
  984. //-----------------------------------------------------------------------------
  985. template < FbxLayerElement::EMappingMode M, FbxLayerElement::EReferenceMode R >
  986. void HandleUV( CUtlVector< int > &uvIndices, FbxMesh *pFbxMesh, FbxGeometryElementUV *peUV, int nControlPointIndex, int nPolygonVertexIndex )
  987. {
  988. // Do Nothing
  989. }
  990. //-----------------------------------------------------------------------------
  991. //
  992. //-----------------------------------------------------------------------------
  993. template <>
  994. void HandleUV< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eDirect >(
  995. CUtlVector< int > &uvIndices, FbxMesh *pFbxMesh, FbxGeometryElementUV *peUV, int nControlPointIndex, int nPolygonVertexIndex )
  996. {
  997. uvIndices.AddToTail( nControlPointIndex );
  998. }
  999. //-----------------------------------------------------------------------------
  1000. //
  1001. //-----------------------------------------------------------------------------
  1002. template <>
  1003. void HandleUV< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eIndexToDirect >(
  1004. CUtlVector< int > &uvIndices, FbxMesh *pFbxMesh, FbxGeometryElementUV *peUV, int nControlPointIndex, int nPolygonVertexIndex )
  1005. {
  1006. const int nUVId = peUV->GetIndexArray().GetAt( nControlPointIndex );
  1007. uvIndices.AddToTail( nUVId );
  1008. }
  1009. //-----------------------------------------------------------------------------
  1010. //
  1011. //-----------------------------------------------------------------------------
  1012. template <>
  1013. void HandleUV< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eDirect >(
  1014. CUtlVector< int > &uvIndices, FbxMesh *pFbxMesh, FbxGeometryElementUV *peUV, int nControlPointIndex, int nPolygonVertexIndex )
  1015. {
  1016. uvIndices.AddToTail( nPolygonVertexIndex );
  1017. }
  1018. //-----------------------------------------------------------------------------
  1019. //
  1020. //-----------------------------------------------------------------------------
  1021. template <>
  1022. void HandleUV< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eIndexToDirect >(
  1023. CUtlVector< int > &uvIndices, FbxMesh *pFbxMesh, FbxGeometryElementUV *peUV, int nControlPointIndex, int nPolygonVertexIndex )
  1024. {
  1025. const int nUVId = peUV->GetIndexArray().GetAt( nPolygonVertexIndex );
  1026. uvIndices.AddToTail( nUVId );
  1027. }
  1028. //-----------------------------------------------------------------------------
  1029. //
  1030. //-----------------------------------------------------------------------------
  1031. CDmFbxSerializer::HandleUVFunc_t GetHandleUVFunc( FbxGeometryElementUV *pFbxElementUV )
  1032. {
  1033. if ( pFbxElementUV )
  1034. {
  1035. const FbxLayerElement::EMappingMode nMappingMode = pFbxElementUV->GetMappingMode();
  1036. const FbxLayerElement::EReferenceMode nReferenceMode = pFbxElementUV->GetReferenceMode();
  1037. if ( nMappingMode == FbxGeometryElement::eByControlPoint && nReferenceMode == FbxGeometryElement::eDirect )
  1038. {
  1039. return HandleUV< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eDirect >;
  1040. }
  1041. else if ( nMappingMode == FbxGeometryElement::eByControlPoint && nReferenceMode == FbxGeometryElement::eIndexToDirect )
  1042. {
  1043. return HandleUV< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eIndexToDirect >;
  1044. }
  1045. else if ( nMappingMode == FbxGeometryElement::eByPolygonVertex && nReferenceMode == FbxGeometryElement::eDirect )
  1046. {
  1047. return HandleUV< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eDirect >;
  1048. }
  1049. else if ( nMappingMode == FbxGeometryElement::eByPolygonVertex && nReferenceMode == FbxGeometryElement::eIndexToDirect )
  1050. {
  1051. return HandleUV < FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eIndexToDirect > ;
  1052. }
  1053. else
  1054. {
  1055. Warning( "Warning! Unsupported FBX UV Mapping/Reference Mode %d/%d\n", nMappingMode, nReferenceMode );
  1056. }
  1057. }
  1058. return HandleUV< FbxGeometryElement::eNone, FbxGeometryElement::eDirect >;
  1059. }
  1060. //-----------------------------------------------------------------------------
  1061. //
  1062. //-----------------------------------------------------------------------------
  1063. template < FbxLayerElement::EMappingMode M, FbxLayerElement::EReferenceMode R >
  1064. void HandleNormal(
  1065. CUtlVector< int > &nNormalIndices, FbxMesh *pFbxMesh, FbxGeometryElementNormal *pFbxElementNormal, int nControlPointIndex, int nPolygonVertexIndex )
  1066. {
  1067. // Do Nothing
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. //
  1071. //-----------------------------------------------------------------------------
  1072. template <>
  1073. void HandleNormal< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eDirect >(
  1074. CUtlVector< int > &nNormalIndices, FbxMesh *pFbxMesh, FbxGeometryElementNormal *pFbxElementNormal, int nControlPointIndex, int nPolygonVertexIndex )
  1075. {
  1076. nNormalIndices.AddToTail( nControlPointIndex );
  1077. }
  1078. //-----------------------------------------------------------------------------
  1079. //
  1080. //-----------------------------------------------------------------------------
  1081. template <>
  1082. void HandleNormal< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eDirect >(
  1083. CUtlVector< int > &nNormalIndices, FbxMesh *pFbxMesh, FbxGeometryElementNormal *pFbxElementNormal, int nControlPointIndex, int nPolygonVertexIndex )
  1084. {
  1085. nNormalIndices.AddToTail( nPolygonVertexIndex );
  1086. }
  1087. //-----------------------------------------------------------------------------
  1088. //
  1089. //-----------------------------------------------------------------------------
  1090. template <>
  1091. void HandleNormal< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eIndexToDirect >(
  1092. CUtlVector< int > &nNormalIndices, FbxMesh *pFbxMesh, FbxGeometryElementNormal *pFbxElementNormal, int nControlPointIndex, int nPolygonVertexIndex )
  1093. {
  1094. const int nNormalIndex = pFbxElementNormal->GetIndexArray().GetAt( nPolygonVertexIndex );
  1095. nNormalIndices.AddToTail( nNormalIndex );
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. //
  1099. //-----------------------------------------------------------------------------
  1100. typedef void (*HandleNormalFunc_t)( CUtlVector< int > &, FbxMesh *, FbxGeometryElementNormal *, int, int );
  1101. //-----------------------------------------------------------------------------
  1102. //
  1103. //-----------------------------------------------------------------------------
  1104. HandleNormalFunc_t GetHandleNormalFunc( FbxGeometryElementNormal *pFbxElementNormal )
  1105. {
  1106. if ( pFbxElementNormal )
  1107. {
  1108. const FbxLayerElement::EMappingMode nMappingMode = pFbxElementNormal->GetMappingMode();
  1109. const FbxLayerElement::EReferenceMode nReferenceMode = pFbxElementNormal->GetReferenceMode();
  1110. if ( nMappingMode == FbxGeometryElement::eByControlPoint && nReferenceMode == FbxGeometryElement::eDirect )
  1111. {
  1112. return HandleNormal< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eDirect >;
  1113. }
  1114. else if ( nMappingMode == FbxGeometryElement::eByPolygonVertex && nReferenceMode == FbxGeometryElement::eDirect )
  1115. {
  1116. return HandleNormal< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eDirect >;
  1117. }
  1118. else if ( nMappingMode == FbxGeometryElement::eByPolygonVertex && nReferenceMode == FbxGeometryElement::eIndexToDirect )
  1119. {
  1120. return HandleNormal< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eIndexToDirect >;
  1121. }
  1122. else
  1123. {
  1124. Warning( "Warning! Unsupported FBX Normal Mapping/Reference Mode %d/%d\n", nMappingMode, nReferenceMode );
  1125. }
  1126. }
  1127. return HandleNormal< FbxGeometryElement::eNone, FbxGeometryElement::eDirect >;
  1128. }
  1129. //-----------------------------------------------------------------------------
  1130. //
  1131. //-----------------------------------------------------------------------------
  1132. template < FbxLayerElement::EMappingMode M, FbxLayerElement::EReferenceMode R >
  1133. void HandleVertexColor(
  1134. CUtlVector< int > &vertexColorIndices, FbxMesh *pFbxMesh, FbxGeometryElementVertexColor *peVertexColor, int nControlPointIndex, int nPolygonVertexIndex )
  1135. {
  1136. // Do Nothing
  1137. }
  1138. //-----------------------------------------------------------------------------
  1139. //
  1140. //-----------------------------------------------------------------------------
  1141. template <>
  1142. void HandleVertexColor< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eDirect >(
  1143. CUtlVector< int > &vertexColorIndices, FbxMesh *pFbxMesh, FbxGeometryElementVertexColor *peVertexColor, int nControlPointIndex, int nPolygonVertexIndex )
  1144. {
  1145. vertexColorIndices.AddToTail( nControlPointIndex );
  1146. }
  1147. //-----------------------------------------------------------------------------
  1148. //
  1149. //-----------------------------------------------------------------------------
  1150. template <>
  1151. void HandleVertexColor< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eIndexToDirect >(
  1152. CUtlVector< int > &vertexColorIndices, FbxMesh *pFbxMesh, FbxGeometryElementVertexColor *peVertexColor, int nControlPointIndex, int nPolygonVertexIndex )
  1153. {
  1154. const int nVertexColorId = peVertexColor->GetIndexArray().GetAt( nControlPointIndex );
  1155. vertexColorIndices.AddToTail( nVertexColorId );
  1156. }
  1157. //-----------------------------------------------------------------------------
  1158. //
  1159. //-----------------------------------------------------------------------------
  1160. template <>
  1161. void HandleVertexColor< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eDirect >(
  1162. CUtlVector< int > &vertexColorIndices, FbxMesh *pFbxMesh, FbxGeometryElementVertexColor *peVertexColor, int nControlPointIndex, int nPolygonVertexIndex )
  1163. {
  1164. vertexColorIndices.AddToTail( nPolygonVertexIndex );
  1165. }
  1166. //-----------------------------------------------------------------------------
  1167. //
  1168. //-----------------------------------------------------------------------------
  1169. template <>
  1170. void HandleVertexColor< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eIndexToDirect >(
  1171. CUtlVector< int > &vertexColorIndices, FbxMesh *pFbxMesh, FbxGeometryElementVertexColor *peVertexColor, int nControlPointIndex, int nPolygonVertexIndex )
  1172. {
  1173. const int nVertexColorId = peVertexColor->GetIndexArray().GetAt( nPolygonVertexIndex );
  1174. vertexColorIndices.AddToTail( nVertexColorId );
  1175. }
  1176. //-----------------------------------------------------------------------------
  1177. //
  1178. //-----------------------------------------------------------------------------
  1179. typedef void (*HandleVertexColorFunc_t)( CUtlVector< int > &, FbxMesh *, FbxGeometryElementVertexColor *, int, int );
  1180. //-----------------------------------------------------------------------------
  1181. //
  1182. //-----------------------------------------------------------------------------
  1183. HandleVertexColorFunc_t GetHandleVertexColorFunc( FbxGeometryElementVertexColor *pFbxElementVertexColor )
  1184. {
  1185. if ( pFbxElementVertexColor )
  1186. {
  1187. const FbxLayerElement::EMappingMode nMappingMode = pFbxElementVertexColor->GetMappingMode();
  1188. const FbxLayerElement::EReferenceMode nReferenceMode = pFbxElementVertexColor->GetReferenceMode();
  1189. if ( nMappingMode == FbxGeometryElement::eByControlPoint && nReferenceMode == FbxGeometryElement::eDirect )
  1190. {
  1191. return HandleVertexColor< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eDirect >;
  1192. }
  1193. else if ( nMappingMode == FbxGeometryElement::eByControlPoint && nReferenceMode == FbxGeometryElement::eIndexToDirect )
  1194. {
  1195. return HandleVertexColor< FbxGeometryElement::eByControlPoint, FbxGeometryElement::eIndexToDirect >;
  1196. }
  1197. else if ( nMappingMode == FbxGeometryElement::eByPolygonVertex && nReferenceMode == FbxGeometryElement::eDirect )
  1198. {
  1199. return HandleVertexColor< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eDirect >;
  1200. }
  1201. else if ( nMappingMode == FbxGeometryElement::eByPolygonVertex && nReferenceMode == FbxGeometryElement::eIndexToDirect )
  1202. {
  1203. return HandleVertexColor< FbxGeometryElement::eByPolygonVertex, FbxGeometryElement::eIndexToDirect >;
  1204. }
  1205. else
  1206. {
  1207. Warning( "Warning! Unsupported FBX VertexColor Mapping/Reference Mode %d/%d\n", nMappingMode, nReferenceMode );
  1208. }
  1209. }
  1210. return HandleVertexColor< FbxGeometryElement::eNone, FbxGeometryElement::eDirect >;
  1211. }
  1212. //-----------------------------------------------------------------------------
  1213. //
  1214. //-----------------------------------------------------------------------------
  1215. struct ColorChannelData_t
  1216. {
  1217. public:
  1218. ColorChannelData_t()
  1219. : m_pFbxElementVertexColor( NULL )
  1220. , m_pFunc( NULL )
  1221. {
  1222. }
  1223. FbxGeometryElementVertexColor *m_pFbxElementVertexColor;
  1224. HandleVertexColorFunc_t m_pFunc;
  1225. CUtlVector< int > m_nIndices;
  1226. };
  1227. //-----------------------------------------------------------------------------
  1228. //
  1229. // This converts an FbxMesh into a DmeMesh where the vertex data indices for
  1230. // the DmeMesh will be full expanded to be per polygon per vertex values
  1231. //
  1232. //-----------------------------------------------------------------------------
  1233. CDmeMesh *CDmFbxSerializer::FbxShapeToDmeMesh( CDmeDag *pDmeDag, FbxNode *pFbxNode, const FbxMatrix &mScale ) const
  1234. {
  1235. bool bReverse = false;
  1236. {
  1237. FbxVector4 vTTmp;
  1238. FbxQuaternion qRTmp;
  1239. FbxVector4 vShTmp;
  1240. FbxVector4 vScTmp;
  1241. double flSign = 1.0; // Sign of the determinant of the scale matrix, if negative, odd number of scales and faces need reversing
  1242. mScale.GetElements( vTTmp, qRTmp, vShTmp, vScTmp, flSign );
  1243. bReverse = ( flSign < 0.0 );
  1244. }
  1245. if ( !pDmeDag || !pFbxNode )
  1246. return NULL;
  1247. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  1248. if ( !pFbxNodeAttribute || pFbxNodeAttribute->GetAttributeType() != FbxNodeAttribute::eMesh )
  1249. return NULL;
  1250. FbxMesh *pFbxMesh = reinterpret_cast< FbxMesh * >( pFbxNodeAttribute );
  1251. if ( !pFbxMesh )
  1252. return NULL;
  1253. CUtlString sName;
  1254. GetName( sName, pFbxNode );
  1255. CDmeMesh *pDmeMesh = CreateElement< CDmeMesh >( sName.String(), pDmeDag->GetFileId() );
  1256. if ( !pDmeMesh )
  1257. {
  1258. Warning( "Warning! Couldn't create DmeMesh for FbxMesh \"%s\"\n", pFbxMesh->GetName() );
  1259. return NULL;
  1260. }
  1261. CUtlVector< int > nPolygonToFaceSetMap;
  1262. FbxMeshToDmeFaceSets( pDmeDag, pDmeMesh, pFbxMesh, nPolygonToFaceSetMap );
  1263. const int nNormalCount = pFbxMesh->GetElementNormalCount();
  1264. FbxGeometryElementNormal *pFbxElementNormal = nNormalCount > 0 ? pFbxMesh->GetElementNormal( 0 ) : NULL;
  1265. HandleNormalFunc_t pHandleNormalFunc = GetHandleNormalFunc( pFbxElementNormal );
  1266. CUtlVector< int > nPositionIndices;
  1267. CUtlVector< int > nNormalIndices;
  1268. CUtlVector< ColorChannelData_t > colorChannelDataList;
  1269. const int nColorCount = pFbxMesh->GetElementVertexColorCount();
  1270. for ( int nColor = 0; nColor < nColorCount; ++nColor )
  1271. {
  1272. FbxGeometryElementVertexColor *pFbxElementVertexColor = pFbxMesh->GetElementVertexColor( nColor );
  1273. if ( !pFbxElementVertexColor )
  1274. continue;
  1275. ColorChannelData_t &colorChannelData = colorChannelDataList[colorChannelDataList.AddToTail()];
  1276. colorChannelData.m_pFbxElementVertexColor = pFbxElementVertexColor;
  1277. colorChannelData.m_pFunc = GetHandleVertexColorFunc( pFbxElementVertexColor );
  1278. }
  1279. CUtlVector< UVColorChannelData_t > uvColorChannelDataList;
  1280. int nUVElementCount = pFbxMesh->GetElementUVCount();
  1281. CUtlVector< UVChannelData_t > uvChannelList;
  1282. if ( nUVElementCount > 0 )
  1283. {
  1284. CUtlVector< FbxGeometryElementUV * > uvElementList;
  1285. uvElementList.SetCount( nUVElementCount );
  1286. for ( int nUV = 0; nUV < nUVElementCount; ++nUV )
  1287. {
  1288. uvElementList[nUV] = pFbxMesh->GetElementUV( nUV );
  1289. }
  1290. if ( nUVElementCount >= 4 )
  1291. {
  1292. // For 3DS Max, there seems to be a bug in how 3DS Max exports extra vertex color information, it's sticking it into UVs
  1293. // See if there are three UV channels:
  1294. // "export_foliageanimation_r",
  1295. // "export_foliageanimation_g",
  1296. // "export_foliageanimation_b"
  1297. const char *szChannelNames[3] = { "r", "g", "b" };
  1298. FbxGeometryElementUV *pUVs[3] = { NULL, NULL, NULL };
  1299. int nUVFoundCount = 0;
  1300. bool bValid = true;
  1301. COMPILE_TIME_ASSERT( ARRAYSIZE( szChannelNames ) == ARRAYSIZE( pUVs ) );
  1302. const char *pszPrefix = "export_foliageanimation_";
  1303. const char *pszChannelName = "foliageanimation";
  1304. for ( int nUV = 0; bValid && nUV < nUVElementCount; ++nUV )
  1305. {
  1306. FbxGeometryElementUV *pUV = pFbxMesh->GetElementUV( nUV );
  1307. const char *pszUVName = pUV->GetName();
  1308. if ( StringHasPrefix( pszUVName, pszPrefix ) )
  1309. {
  1310. const char *pszChannel = StringAfterPrefix( pszUVName, pszPrefix );
  1311. bool bUVFound = false;
  1312. for ( int i = 0; i < ARRAYSIZE( szChannelNames ); ++i )
  1313. {
  1314. if ( !V_stricmp( pszChannel, szChannelNames[i] ) )
  1315. {
  1316. bUVFound = true;
  1317. if ( pUVs[i] == NULL )
  1318. {
  1319. pUVs[i] = pUV;
  1320. ++nUVFoundCount;
  1321. }
  1322. else
  1323. {
  1324. Warning( "Warning! Fbx 3DSMax UV encoded VertexPaint channel \"%s\", specified multiple times on mesh \"%s\"\n", pszUVName, pFbxMesh->GetName() );
  1325. }
  1326. break;
  1327. }
  1328. }
  1329. if ( bValid && !bUVFound )
  1330. {
  1331. Warning( "Warning! Fbx 3DSMax UV encoded VertexPaint channel \"%s\", unexpected, expecting ending of [ r, g or b ], on mesh \"%s\"\n", pszUVName, pFbxMesh->GetName() );
  1332. }
  1333. }
  1334. }
  1335. if ( bValid && nUVFoundCount == 3 )
  1336. {
  1337. UVColorChannelData_t &uvEncodedColorChannelData = uvColorChannelDataList[uvColorChannelDataList.AddToTail()];
  1338. uvEncodedColorChannelData.m_sChannelName = pszChannelName;
  1339. for ( int i = 0; i < ARRAYSIZE( pUVs ); ++i )
  1340. {
  1341. uvEncodedColorChannelData.m_uvChannelData[i].m_pFbxElementUV = pUVs[i];
  1342. uvEncodedColorChannelData.m_uvChannelData[i].m_pFunc = GetHandleUVFunc( pUVs[i] );
  1343. uvElementList.FindAndRemove( pUVs[i] ); // Remove these funky 3ds max channels from the normal UV channels
  1344. }
  1345. }
  1346. else if ( nUVFoundCount > 0 )
  1347. {
  1348. Warning( "Warning! Fbx 3DSMax UV encoded VertexPaint \"%s\" invalid, expected %llu maps, found %d, on mesh \"%s\"\n", pszPrefix, static_cast< uint64 >( ARRAYSIZE( szChannelNames ) ), nUVFoundCount, pFbxMesh->GetName() );
  1349. }
  1350. }
  1351. // Add the non-3DsMax UV encoded color channels to the actual UV channel list
  1352. nUVElementCount = uvElementList.Count();
  1353. for ( int nUV = 0; nUV < nUVElementCount; ++nUV )
  1354. {
  1355. FbxGeometryElementUV *pFbxElementUV = pFbxMesh->GetElementUV( nUV );
  1356. if ( !pFbxElementUV )
  1357. continue;
  1358. HandleUVFunc_t pHandleUVFunc = GetHandleUVFunc( pFbxElementUV );
  1359. if ( !pHandleUVFunc )
  1360. continue;
  1361. UVChannelData_t &uvChannelData = uvChannelList[uvChannelList.AddToTail()];
  1362. uvChannelData.m_pFbxElementUV = pFbxElementUV;
  1363. uvChannelData.m_pFunc = pHandleUVFunc;
  1364. }
  1365. nUVElementCount = uvChannelList.Count();
  1366. }
  1367. const int nUVColorCount = uvColorChannelDataList.Count();
  1368. CUtlVector< int > nFaceSetIndices;
  1369. const int nPolygonCount = pFbxMesh->GetPolygonCount();
  1370. int nPolygonVertexIndex = 0;
  1371. for ( int nPolygon = 0; nPolygon < nPolygonCount; ++nPolygon )
  1372. {
  1373. const int nFaceSet = nPolygonToFaceSetMap[nPolygon];
  1374. CDmeFaceSet *pDmeFaceSet = pDmeMesh->GetFaceSet( nFaceSet );
  1375. const int nPolygonVertCount = pFbxMesh->GetPolygonSize( nPolygon );
  1376. nFaceSetIndices.SetCount( nPolygonVertCount + 1 );
  1377. for ( int nPolygonVert = 0; nPolygonVert < nPolygonVertCount; ++nPolygonVert )
  1378. {
  1379. const int nControlPointIndex = pFbxMesh->GetPolygonVertex( nPolygon, nPolygonVert );
  1380. nFaceSetIndices[nPolygonVert] = nPositionIndices.Count();
  1381. nPositionIndices.AddToTail( nControlPointIndex );
  1382. for ( int nUV = 0; nUV < nUVElementCount; ++nUV )
  1383. {
  1384. UVChannelData_t &uvChannelData = uvChannelList[nUV];
  1385. ( *( uvChannelData.m_pFunc ) )( uvChannelData.m_nIndices, pFbxMesh, uvChannelData.m_pFbxElementUV, nControlPointIndex, nPolygonVertexIndex );
  1386. }
  1387. ( *pHandleNormalFunc )( nNormalIndices, pFbxMesh, pFbxElementNormal, nControlPointIndex, nPolygonVertexIndex );
  1388. for ( int nColor = 0; nColor < nColorCount; ++nColor )
  1389. {
  1390. ColorChannelData_t &colorChannelData = colorChannelDataList[nColor];
  1391. (*colorChannelData.m_pFunc)( colorChannelData.m_nIndices, pFbxMesh, colorChannelData.m_pFbxElementVertexColor, nControlPointIndex, nPolygonVertexIndex );
  1392. }
  1393. for ( int nUVColor = 0; nUVColor < nUVColorCount; ++nUVColor )
  1394. {
  1395. UVColorChannelData_t &uvColorChannelData = uvColorChannelDataList[nUVColor];
  1396. for ( int nUVChannel = 0; nUVChannel < ARRAYSIZE( uvColorChannelData.m_uvChannelData ); ++nUVChannel )
  1397. {
  1398. ( *( uvColorChannelData.m_uvChannelData[nUVChannel].m_pFunc ) )( uvColorChannelData.m_uvChannelData[nUVChannel].m_nIndices, pFbxMesh, uvColorChannelData.m_uvChannelData[nUVChannel].m_pFbxElementUV, nControlPointIndex, nPolygonVertexIndex );
  1399. }
  1400. }
  1401. ++nPolygonVertexIndex;
  1402. }
  1403. nFaceSetIndices[nPolygonVertCount] = -1;
  1404. const int nStartIndex = pDmeFaceSet->NumIndices();
  1405. pDmeFaceSet->AddIndices( nFaceSetIndices.Count() );
  1406. if ( bReverse )
  1407. {
  1408. int nCurrentIndex = nStartIndex;
  1409. // Add indices for this face in reverse order, leaving the -1 as the last index (face terminator)
  1410. for ( int nReverseIndex = nFaceSetIndices.Count() - 2; nReverseIndex >= 0; --nReverseIndex )
  1411. {
  1412. pDmeFaceSet->SetIndex( nCurrentIndex, nFaceSetIndices[nReverseIndex] );
  1413. ++nCurrentIndex;
  1414. }
  1415. pDmeFaceSet->SetIndex( nCurrentIndex, nFaceSetIndices.Tail() ); // Add the -1
  1416. }
  1417. else
  1418. {
  1419. pDmeFaceSet->SetIndices( nStartIndex, nFaceSetIndices.Count(), nFaceSetIndices.Base() );
  1420. }
  1421. }
  1422. CDmeVertexData *pDmeVertexData = pDmeMesh->FindOrCreateBaseState( "bind" );
  1423. pDmeVertexData->FlipVCoordinate( true );
  1424. pDmeMesh->SetBindBaseState( pDmeVertexData );
  1425. pDmeMesh->SetCurrentBaseState( "bind" );
  1426. if ( !AddPositionData( pDmeVertexData, nPositionIndices, pFbxMesh, mScale ) )
  1427. {
  1428. Warning( "Warning! Couldn't convert FbxMesh \"%s\"\n", pFbxMesh->GetName() );
  1429. g_pDataModel->DestroyElement( pDmeMesh->GetHandle() );
  1430. return NULL;
  1431. }
  1432. for ( int nUV = 0; nUV < nUVElementCount; ++nUV )
  1433. {
  1434. const UVChannelData_t &uvChannelData = uvChannelList[nUV];
  1435. AddUVData( pDmeVertexData, uvChannelData.m_nIndices, uvChannelData.m_pFbxElementUV, nUV );
  1436. }
  1437. AddNormalData( pDmeVertexData, nNormalIndices, pFbxElementNormal, mScale );
  1438. for ( int nColor = 0; nColor < nColorCount; ++nColor )
  1439. {
  1440. ColorChannelData_t &colorChannelData = colorChannelDataList[nColor];
  1441. AddColorData( pDmeVertexData, colorChannelData.m_nIndices, colorChannelData.m_pFbxElementVertexColor );
  1442. }
  1443. for ( int nUVColor = 0; nUVColor < uvColorChannelDataList.Count(); ++nUVColor )
  1444. {
  1445. UVColorChannelData_t &uvColorChannelData = uvColorChannelDataList[nUVColor];
  1446. AddUVColorData( pDmeVertexData, uvColorChannelData );
  1447. }
  1448. pDmeDag->SetShape( pDmeMesh );
  1449. return pDmeMesh;
  1450. }
  1451. //-----------------------------------------------------------------------------
  1452. //
  1453. //-----------------------------------------------------------------------------
  1454. bool CDmFbxSerializer::FbxMeshToDmeFaceSets( CDmeDag *pDmeDag, CDmeMesh *pDmeMesh, FbxMesh *pFbxMesh, CUtlVector< int > &nPolygonToFaceSetMap ) const
  1455. {
  1456. const int nPolygonCount = pFbxMesh->GetPolygonCount();
  1457. const int nMaterialCount = pFbxMesh->GetNode()->GetMaterialCount();
  1458. const int nElementMaterialCount = pFbxMesh->GetElementMaterialCount();
  1459. FbxGeometryElementMaterial *pMatElement = NULL;
  1460. if ( nMaterialCount > 0 && nElementMaterialCount > 0 )
  1461. {
  1462. pMatElement = pFbxMesh->GetElementMaterial( 0 );
  1463. }
  1464. CUtlString sFallbackMaterialPath;
  1465. #ifdef SOURCE2
  1466. {
  1467. // Generate a fallback material path based on the mesh name, only used if material cannot be located
  1468. char szResourceName[MAX_PATH] = {};
  1469. if ( !FixupResourceName( pDmeMesh->GetName(), RESOURCE_TYPE_MATERIAL, szResourceName, ARRAYSIZE( szResourceName ) ) )
  1470. {
  1471. SetResourceFileNameExtension( pDmeMesh->GetName(), RESOURCE_TYPE_MATERIAL, szResourceName, ARRAYSIZE( szResourceName ) );
  1472. }
  1473. sFallbackMaterialPath = szResourceName;
  1474. }
  1475. #endif
  1476. CUtlString sMaterialPath = sFallbackMaterialPath;
  1477. // Looking for UserData on a mesh ending in .vmat
  1478. static const char szSuffix[] = "_vmat";
  1479. const int nSuffixCount = ARRAYSIZE( szSuffix ) - 1;
  1480. COMPILE_TIME_ASSERT( nSuffixCount == 5 );
  1481. typedef FbxMap< FbxString, FbxLayerElementArrayTemplate< void * > * > HoudiniMatDataMap_t; // data to figure out material assignments from houdini
  1482. HoudiniMatDataMap_t houdiniMatData;
  1483. for ( int i = 0; i < pFbxMesh->GetElementUserDataCount(); ++i )
  1484. {
  1485. FbxGeometryElementUserData *pFbxUserData = pFbxMesh->GetElementUserData( i );;
  1486. for ( int j = 0; j < pFbxUserData->GetDirectArrayCount(); ++j )
  1487. {
  1488. if ( pFbxUserData->GetDataType( j ).GetType() == eFbxInt )
  1489. {
  1490. const char *pszDataName = pFbxUserData->GetDataName( j );
  1491. FbxString sDataName( pszDataName );
  1492. // Look if string ends in "_vmat"
  1493. if ( sDataName.FindAndReplace( szSuffix, ".vmat", sDataName.GetLen() - nSuffixCount ) )
  1494. {
  1495. bool bArray = false;
  1496. FbxLayerElementArrayTemplate< void * > *pDirectArrayVoid = pFbxUserData->GetDirectArrayVoid( j, &bArray );
  1497. if ( bArray && nPolygonCount == pDirectArrayVoid->GetCount() )
  1498. {
  1499. houdiniMatData.Insert( sDataName, pDirectArrayVoid );
  1500. }
  1501. }
  1502. }
  1503. }
  1504. }
  1505. if ( pMatElement )
  1506. {
  1507. if ( pMatElement->GetMappingMode() == FbxGeometryElement::eByPolygon )
  1508. {
  1509. nPolygonToFaceSetMap.RemoveAll();
  1510. nPolygonToFaceSetMap.EnsureCapacity( nPolygonCount );
  1511. CUtlVector< FbxString > materialList;
  1512. materialList.SetCount( nMaterialCount );
  1513. if ( houdiniMatData.Empty() )
  1514. {
  1515. for ( int i = 0; i < nPolygonCount; ++i )
  1516. {
  1517. nPolygonToFaceSetMap.AddToTail( pMatElement->GetIndexArray().GetAt( i ) );
  1518. }
  1519. }
  1520. else
  1521. {
  1522. for ( int i = 0; i < nPolygonCount; ++i )
  1523. {
  1524. const int nPolygonMaterialIndex = pMatElement->GetIndexArray().GetAt( i );
  1525. nPolygonToFaceSetMap.AddToTail( nPolygonMaterialIndex );
  1526. if ( materialList[nPolygonMaterialIndex].IsEmpty() )
  1527. {
  1528. bool bFoundMat = false;
  1529. for ( HoudiniMatDataMap_t::Iterator hmdIt = houdiniMatData.Begin(); hmdIt != houdiniMatData.End(); ++hmdIt )
  1530. {
  1531. FbxLayerElementArrayTemplate< void * > *pDirectArrayVoid = hmdIt->GetValue();
  1532. int *pDirectArrayInt = NULL;
  1533. pDirectArrayInt = pDirectArrayVoid->GetLocked( pDirectArrayInt, FbxLayerElementArray::eReadLock );
  1534. if ( pDirectArrayInt[i] != 0 )
  1535. {
  1536. materialList[nPolygonMaterialIndex] = hmdIt->GetKey();
  1537. bFoundMat = true;
  1538. }
  1539. pDirectArrayVoid->Release( reinterpret_cast< void ** >( &pDirectArrayInt ) );
  1540. if ( bFoundMat )
  1541. break;
  1542. }
  1543. }
  1544. }
  1545. }
  1546. for ( int i = 0; i < nMaterialCount; ++i )
  1547. {
  1548. CUtlString sByPolygonMaterialPath;
  1549. CUtlVector< CUtlString > materialSearchErrorList;
  1550. if ( materialList[i].IsEmpty() )
  1551. {
  1552. FbxSurfaceMaterial *pFbxMaterial = pFbxMesh->GetNode()->GetMaterial( i );
  1553. // If there's actually a material, change the fallback to the material name instead of the mesh name
  1554. if ( pFbxMaterial )
  1555. {
  1556. sByPolygonMaterialPath = pFbxMaterial->GetName();
  1557. }
  1558. else
  1559. {
  1560. sByPolygonMaterialPath = sFallbackMaterialPath;
  1561. }
  1562. if ( !GetFbxMaterialPath( sByPolygonMaterialPath, pFbxMaterial, materialSearchErrorList ) )
  1563. {
  1564. #ifdef SOURCE2
  1565. AddConversionError( pDmeMesh->GetFileId(), CFmtStrMax( "GetFbxMaterialPath Failed! FbxMesh: %s FbxMaterial[%d]: %s, using: %s", pDmeMesh->GetName(), i, pFbxMaterial ? pFbxMaterial->GetName() : "nil", sByPolygonMaterialPath.Get() ).Get() );
  1566. if ( !materialSearchErrorList.IsEmpty() )
  1567. {
  1568. AddConversionError( pDmeMesh->GetFileId(), " + Searched:" );
  1569. for ( int e = 0; e < materialSearchErrorList.Count(); ++e )
  1570. {
  1571. AddConversionError( pDmeMesh->GetFileId(), CFmtStr( " + %s", materialSearchErrorList[e].Get() ).Get() );
  1572. }
  1573. }
  1574. #endif
  1575. }
  1576. }
  1577. else
  1578. {
  1579. sByPolygonMaterialPath = materialList[i].Buffer();
  1580. }
  1581. const CUtlString sByPolygonMaterialName = sByPolygonMaterialPath.GetBaseFilename();
  1582. CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( sByPolygonMaterialName.String(), pDmeMesh->GetFileId() );
  1583. CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( sByPolygonMaterialName.String(), pDmeMesh->GetFileId() );
  1584. pDmeMaterial->SetMaterial( sByPolygonMaterialPath.Get() );
  1585. pDmeFaceSet->SetMaterial( pDmeMaterial );
  1586. pDmeMesh->AddFaceSet( pDmeFaceSet );
  1587. }
  1588. return true;
  1589. }
  1590. else if ( pMatElement->GetMappingMode() == FbxGeometryElement::eAllSame )
  1591. {
  1592. CUtlVector< CUtlString > materialSearchErrorList;
  1593. FbxSurfaceMaterial *pFbxMaterial = pFbxMesh->GetNode()->GetMaterial( 0 );
  1594. if ( pFbxMaterial )
  1595. {
  1596. // If there's actually a material, change the fallback to the material name instead of the mesh name
  1597. sMaterialPath = pFbxMaterial->GetName();
  1598. }
  1599. if ( !GetFbxMaterialPath( sMaterialPath, pFbxMaterial, materialSearchErrorList ) )
  1600. {
  1601. #ifdef SOURCE2
  1602. AddConversionError( pDmeMesh->GetFileId(), CFmtStrMax( "GetFbxMaterialPath Failed! FbxMesh: %s FbxMaterial: %s, using: %s", pDmeMesh->GetName(), pFbxMaterial ? pFbxMaterial->GetName() : "nil", sMaterialPath.Get() ).Get() );
  1603. if ( !materialSearchErrorList.IsEmpty() )
  1604. {
  1605. AddConversionError( pDmeMesh->GetFileId(), " + Searched:" );
  1606. for ( int e = 0; e < materialSearchErrorList.Count(); ++e )
  1607. {
  1608. AddConversionError( pDmeMesh->GetFileId(), CFmtStr( " + %s", materialSearchErrorList[e].Get() ).Get() );
  1609. }
  1610. }
  1611. #endif
  1612. }
  1613. if ( houdiniMatData.GetSize() == 1 )
  1614. {
  1615. sMaterialPath = houdiniMatData.Begin()->GetKey();
  1616. }
  1617. }
  1618. else
  1619. {
  1620. AddConversionError( pDmeMesh->GetFileId(), CFmtStrMax( "GetFbxMaterialPath Failed! FbxMesh: %s, Unsupported material mapping mode, using: %s", pDmeMesh->GetName(), sMaterialPath.Get() ).Get() );
  1621. }
  1622. }
  1623. else
  1624. {
  1625. AddConversionError( pDmeMesh->GetFileId(), CFmtStrMax( "GetFbxMaterialPath Failed! FbxMesh: %s, No FbxMaterial assigned, using: %s", pDmeMesh->GetName(), sMaterialPath.Get() ).Get() );
  1626. }
  1627. // Either a single material or no material on the mesh
  1628. static const int nZero = 0;
  1629. nPolygonToFaceSetMap.SetCount( nPolygonCount );
  1630. nPolygonToFaceSetMap.FillWithValue( nZero );
  1631. const CUtlString sMaterialName = sMaterialPath.GetBaseFilename();
  1632. CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( sMaterialName.String(), pDmeMesh->GetFileId() );
  1633. CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( sMaterialName.String(), pDmeMesh->GetFileId() );
  1634. pDmeMaterial->SetMaterial( sMaterialPath.String() );
  1635. pDmeFaceSet->SetMaterial( pDmeMaterial );
  1636. pDmeMesh->AddFaceSet( pDmeFaceSet );
  1637. return true;
  1638. }
  1639. //-----------------------------------------------------------------------------
  1640. //
  1641. // Search for a material by name by generating resource names and seeing if
  1642. // the resource exists on disk in content and then game and then on user
  1643. // specified material search paths
  1644. //
  1645. // If a material file cannot be found, the original name is copied and false
  1646. // is returned
  1647. //
  1648. //-----------------------------------------------------------------------------
  1649. bool CDmFbxSerializer::FindMaterialResource( CUtlString &sOutMaterialPath, const char *pszInMaterialName, CUtlVector< CUtlString > &materialSearchErrorList ) const
  1650. {
  1651. #ifdef SOURCE2
  1652. char szResourceName[MAX_PATH] = { 0 };
  1653. char szResourceFullPath[MAX_PATH] = { 0 };
  1654. char szMaterialPath[MAX_PATH] = { 0 };
  1655. ResourcePathGenerationType_t pSearchPaths[] =
  1656. {
  1657. RESOURCE_PATH_CONTENT,
  1658. RESOURCE_PATH_GAME
  1659. };
  1660. const char *szSearchPaths[] =
  1661. {
  1662. "CONTENT",
  1663. "GAME"
  1664. };
  1665. COMPILE_TIME_ASSERT( ARRAYSIZE( pSearchPaths ) == ARRAYSIZE( szSearchPaths ) );
  1666. FixupResourceName( pszInMaterialName, RESOURCE_TYPE_MATERIAL, szResourceName, ARRAYSIZE( szResourceName ) );
  1667. for ( int s = 0; s < ARRAYSIZE( pSearchPaths ); ++s )
  1668. {
  1669. // Check if current material is valid
  1670. if ( GenerateStandardFullPathForResourceName( szResourceName, pSearchPaths[s], szResourceFullPath, ARRAYSIZE( szResourceFullPath ) ) )
  1671. {
  1672. sOutMaterialPath = szResourceName;
  1673. return true;
  1674. }
  1675. else
  1676. {
  1677. materialSearchErrorList.AddToTail( CFmtStr( "%-7s %s", szSearchPaths[s], szResourceName ).Get() );
  1678. }
  1679. }
  1680. // Loop through material search paths and try to find the material
  1681. for ( int s = 0; s < ARRAYSIZE( pSearchPaths ); s++ )
  1682. {
  1683. for ( int i = 0; i < m_sOptMaterialSearchPathList.Count(); ++i )
  1684. {
  1685. V_ComposeFileName( m_sOptMaterialSearchPathList[i].Get(), pszInMaterialName, szMaterialPath, ARRAYSIZE( szMaterialPath ) );
  1686. FixupResourceName( szMaterialPath, RESOURCE_TYPE_MATERIAL, szResourceName, ARRAYSIZE( szResourceName ) );
  1687. if ( GenerateStandardFullPathForResourceName( szResourceName, pSearchPaths[s], szResourceFullPath, ARRAYSIZE( szResourceFullPath ) ) )
  1688. {
  1689. sOutMaterialPath = szResourceName;
  1690. return true;
  1691. }
  1692. else
  1693. {
  1694. materialSearchErrorList.AddToTail( CFmtStr( "%-7s %s", szSearchPaths[s], szResourceName ).Get() );
  1695. }
  1696. }
  1697. }
  1698. return false;
  1699. #else
  1700. //sOutMaterialPath = CUtlString( pszInMaterialName ).UnqualifiedFilename();
  1701. return false;
  1702. #endif
  1703. }
  1704. //-----------------------------------------------------------------------------
  1705. //
  1706. //-----------------------------------------------------------------------------
  1707. bool CDmFbxSerializer::GetFbxMaterialPathFromFbxFileTexture( CUtlString &sMaterialPath, FbxFileTexture *pFileTexture, CUtlVector< CUtlString > &materialSearchErrorList ) const
  1708. {
  1709. const char *pszFileName = pFileTexture->GetFileName();
  1710. if ( !pszFileName || *pszFileName == '\0' )
  1711. return false;
  1712. #ifdef SOURCE2
  1713. char szResourceName[ MAX_PATH ];
  1714. GenerateResourceNameFromFileName( pszFileName, RESOURCE_TYPE_MATERIAL, szResourceName, ARRAYSIZE( szResourceName ) );
  1715. #else
  1716. sMaterialPath = CUtlString( pszFileName ).UnqualifiedFilename();
  1717. return true;
  1718. const char *szResourceName;
  1719. szResourceName = pszFileName;
  1720. #endif
  1721. if ( FindMaterialResource( sMaterialPath, szResourceName, materialSearchErrorList ) )
  1722. return true;
  1723. // See if the texture ends in any well known suffixes
  1724. const char *szSuffixes[] = { "_color." };
  1725. FbxString sTmpResourceName = szResourceName;
  1726. for ( int i = 0; i < ARRAYSIZE( szSuffixes ); ++i )
  1727. {
  1728. if ( sTmpResourceName.FindAndReplace( szSuffixes[ i ], "." ) )
  1729. {
  1730. if ( FindMaterialResource( sMaterialPath, sTmpResourceName.Buffer(), materialSearchErrorList ) )
  1731. return true;
  1732. if ( !V_strcmp( szSuffixes[i], "_color." ) )
  1733. {
  1734. // If we got here, probably just use this one, likely the file wasn't put onto disk yet
  1735. sMaterialPath = sTmpResourceName.Buffer();
  1736. }
  1737. }
  1738. }
  1739. return false;
  1740. }
  1741. //-----------------------------------------------------------------------------
  1742. //
  1743. //-----------------------------------------------------------------------------
  1744. bool CDmFbxSerializer::GetFbxMaterialPath( CUtlString &sMaterialPath, FbxSurfaceMaterial *pFbxMat, CUtlVector< CUtlString > &materialSearchErrorList ) const
  1745. {
  1746. if ( !pFbxMat )
  1747. {
  1748. materialSearchErrorList.AddToTail( CFmtStr( "GetFbxMaterialPath Failed: No Fbx material" ).Get() );
  1749. return false;
  1750. }
  1751. // See if FBX_vmatPath is explicitly set, if it is, then just use it and return
  1752. static const char *szExplitPaths[] = { "vmatPath", "FBX_vmatPath" };
  1753. for ( int i = 0; i < ARRAYSIZE( szExplitPaths ); ++i )
  1754. {
  1755. const char *pszExplicitPath = szExplitPaths[i];
  1756. FbxProperty fbx_vmatPath = pFbxMat->FindProperty( pszExplicitPath );
  1757. if ( fbx_vmatPath.IsValid() )
  1758. {
  1759. const EFbxType eFbxType = fbx_vmatPath.GetPropertyDataType().GetType();
  1760. if ( eFbxType == eFbxString )
  1761. {
  1762. const FbxString sVMatPath = fbx_vmatPath.Get< FbxString >();
  1763. if ( !sVMatPath.IsEmpty() )
  1764. {
  1765. sMaterialPath = sVMatPath.Buffer();
  1766. return true;
  1767. }
  1768. else
  1769. {
  1770. materialSearchErrorList.AddToTail( CFmtStr( "FBXSurfaceMaterial( %s ).FBX_vmatPath found but empty", pFbxMat->GetName() ).Get() );
  1771. }
  1772. }
  1773. else
  1774. {
  1775. materialSearchErrorList.AddToTail( CFmtStr( "FBXSurfaceMaterial( %s ).FBX_vmatPath found but not a string attribute", pFbxMat->GetName() ).Get() );
  1776. }
  1777. }
  1778. }
  1779. // See if the name of the material maps directly to a material file
  1780. if ( FindMaterialResource( sMaterialPath, pFbxMat->GetNameOnly(), materialSearchErrorList ) )
  1781. return true;
  1782. // Look to see if a texture can map to a .vmat
  1783. FbxProperty fbxProperty = pFbxMat->FindProperty( FbxSurfaceMaterial::sDiffuse );
  1784. const int nLayeredTextureCount = fbxProperty.GetSrcObjectCount( FbxLayeredTexture::ClassId );
  1785. if ( nLayeredTextureCount > 0 )
  1786. {
  1787. for ( int j = 0; j < nLayeredTextureCount; ++j )
  1788. {
  1789. FbxLayeredTexture *pLayeredTexture = FbxCast< FbxLayeredTexture >( fbxProperty.GetSrcObject( FbxLayeredTexture::ClassId, j ) );
  1790. const int nTexCount = pLayeredTexture->GetSrcObjectCount( FbxTexture::ClassId );
  1791. for ( int k = 0; k < nTexCount; ++k )
  1792. {
  1793. FbxFileTexture *pFileTexture = FbxCast< FbxFileTexture >( pLayeredTexture->GetSrcObject( FbxTexture::ClassId, k ) );
  1794. if ( pFileTexture )
  1795. {
  1796. if ( GetFbxMaterialPathFromFbxFileTexture( sMaterialPath, pFileTexture, materialSearchErrorList ) )
  1797. return true;
  1798. }
  1799. }
  1800. }
  1801. }
  1802. else
  1803. {
  1804. //no layered texture simply get on the property
  1805. const int nTexCount = fbxProperty.GetSrcObjectCount( FbxTexture::ClassId );
  1806. if ( nTexCount > 0 )
  1807. {
  1808. for ( int j = 0; j < nTexCount; ++j )
  1809. {
  1810. FbxFileTexture *pFileTexture = FbxCast< FbxFileTexture >( fbxProperty.GetSrcObject( FbxTexture::ClassId, j ) );
  1811. if ( pFileTexture )
  1812. {
  1813. if ( GetFbxMaterialPathFromFbxFileTexture( sMaterialPath, pFileTexture, materialSearchErrorList ) )
  1814. return true;
  1815. }
  1816. }
  1817. }
  1818. }
  1819. return false;
  1820. }
  1821. //-----------------------------------------------------------------------------
  1822. //
  1823. //-----------------------------------------------------------------------------
  1824. void CDmFbxSerializer::SkinMeshes_R(
  1825. const FbxToDmxMap_t &fbxToDmxMap, CDmeModel *pDmeModel, FbxNode *pFbxNode ) const
  1826. {
  1827. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  1828. if ( pFbxNodeAttribute )
  1829. {
  1830. const FbxNodeAttribute::EType nAttributeType = pFbxNodeAttribute->GetAttributeType();
  1831. if ( nAttributeType == FbxNodeAttribute::eMesh )
  1832. {
  1833. FbxToDmxMap_t::IndexType_t nDmxIndex = fbxToDmxMap.Find( pFbxNode );
  1834. if ( fbxToDmxMap.IsValidIndex( nDmxIndex ) )
  1835. {
  1836. SkinMesh( fbxToDmxMap.Element( nDmxIndex ), fbxToDmxMap, pDmeModel, pFbxNode );
  1837. }
  1838. }
  1839. }
  1840. for ( int i = 0; i < pFbxNode->GetChildCount(); ++i )
  1841. {
  1842. SkinMeshes_R( fbxToDmxMap, pDmeModel, pFbxNode->GetChild( i ) );
  1843. }
  1844. }
  1845. //=============================================================================
  1846. //
  1847. //=============================================================================
  1848. class CVertexSkin
  1849. {
  1850. public:
  1851. enum
  1852. {
  1853. kMaxWeights = 3
  1854. };
  1855. enum SkinError_t
  1856. {
  1857. kSkinErrorNone = 0,
  1858. kSkinErrorTooManyWeights,
  1859. kSkinErrorZeroWeight,
  1860. kSkinErrorNegativeWeight
  1861. };
  1862. bool AddWeight( int nJointIndex, float flJointWeight )
  1863. {
  1864. // Ignore 0 weights
  1865. if ( flJointWeight < m_flEps )
  1866. return false;
  1867. IndexWeightPair_t &indexWeightPair = m_weights[ m_weights.AddToTail() ];
  1868. indexWeightPair.m_nIndex = nJointIndex;
  1869. indexWeightPair.m_flWeight = flJointWeight;
  1870. return true;
  1871. }
  1872. int GetIndex( int nIndex ) const
  1873. {
  1874. if ( nIndex >= kMaxWeights || nIndex >= m_weights.Count() )
  1875. return -1;
  1876. return m_weights[nIndex].m_nIndex;
  1877. }
  1878. float GetWeight( int nIndex ) const
  1879. {
  1880. if ( nIndex >= kMaxWeights || nIndex >= m_weights.Count() )
  1881. return 0.0f;
  1882. return m_weights[nIndex].m_flWeight;
  1883. }
  1884. SkinError_t Renormalize();
  1885. protected:
  1886. static int VertexWeightLessFunc( const void *pLhs, const void *pRhs );
  1887. static const float m_flEps;
  1888. struct IndexWeightPair_t
  1889. {
  1890. int m_nIndex;
  1891. float m_flWeight;
  1892. };
  1893. CUtlVector< IndexWeightPair_t > m_weights;
  1894. };
  1895. //-----------------------------------------------------------------------------
  1896. //
  1897. //-----------------------------------------------------------------------------
  1898. const float CVertexSkin::m_flEps = 1.0e-4;
  1899. //-----------------------------------------------------------------------------
  1900. // Used by HandleVertexWeights, sorts vertex weights by weight
  1901. //-----------------------------------------------------------------------------
  1902. int CVertexSkin::VertexWeightLessFunc( const void *pLhs, const void *pRhs )
  1903. {
  1904. const IndexWeightPair_t *pVertexWeightL = reinterpret_cast< const IndexWeightPair_t * >( pLhs );
  1905. const IndexWeightPair_t *pVertexWeightR = reinterpret_cast< const IndexWeightPair_t * >( pRhs );
  1906. if ( pVertexWeightL->m_nIndex < 0 )
  1907. {
  1908. if ( pVertexWeightR->m_nIndex < 0 )
  1909. {
  1910. return 0;
  1911. }
  1912. else
  1913. {
  1914. return 1;
  1915. }
  1916. }
  1917. else if ( pVertexWeightR->m_nIndex < 0 )
  1918. {
  1919. return -1;
  1920. }
  1921. if ( pVertexWeightL->m_flWeight > pVertexWeightR->m_flWeight )
  1922. {
  1923. return -1;
  1924. }
  1925. else if ( pVertexWeightL->m_flWeight < pVertexWeightR->m_flWeight )
  1926. {
  1927. return 1;
  1928. }
  1929. return 0;
  1930. }
  1931. //-----------------------------------------------------------------------------
  1932. //
  1933. // Returns: kSkinErrorNone if all ok
  1934. // kSkinErrorTooManyWeights if more than kMaxWeights
  1935. // kSkinErrorZeroWeight if total weight is 0 for this vertex
  1936. // kSkinErrorNegativeWeight if total weight is < 0 for this vertex
  1937. //
  1938. //-----------------------------------------------------------------------------
  1939. CVertexSkin::SkinError_t CVertexSkin::Renormalize()
  1940. {
  1941. SkinError_t nErr = kSkinErrorNone;
  1942. // Sort by weight, largest weights first
  1943. qsort( m_weights.Base(), m_weights.Count(), sizeof( IndexWeightPair_t ), VertexWeightLessFunc );
  1944. // Remove any weights > kMaxWeights
  1945. if ( m_weights.Count() > kMaxWeights )
  1946. {
  1947. m_weights.RemoveMultipleFromTail( m_weights.Count() - kMaxWeights );
  1948. nErr = kSkinErrorTooManyWeights;
  1949. }
  1950. float flTotalWeight = 0.0f;
  1951. for ( int i = 0; i < m_weights.Count(); ++i )
  1952. {
  1953. flTotalWeight += m_weights[i].m_flWeight;
  1954. }
  1955. if ( fabs( flTotalWeight ) < m_flEps )
  1956. {
  1957. nErr = kSkinErrorZeroWeight;
  1958. }
  1959. else
  1960. {
  1961. if ( flTotalWeight < 0.0f )
  1962. {
  1963. nErr = kSkinErrorNegativeWeight;
  1964. }
  1965. if ( fabs( fabs( flTotalWeight ) - 1.0f ) > m_flEps )
  1966. {
  1967. for ( int i = 0; i < m_weights.Count(); ++i )
  1968. {
  1969. m_weights[i].m_flWeight /= flTotalWeight;
  1970. }
  1971. }
  1972. }
  1973. return nErr;
  1974. }
  1975. //-----------------------------------------------------------------------------
  1976. //
  1977. //-----------------------------------------------------------------------------
  1978. void CDmFbxSerializer::SkinMesh( CDmeDag *pDmeDag, const FbxToDmxMap_t &fbxToDmxMap, CDmeModel *pDmeModel, FbxNode *pFbxNode ) const
  1979. {
  1980. static const char *szClusterModes[] = { "Normalize", "Additive", "Total1" };
  1981. if ( !pDmeDag )
  1982. return;
  1983. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  1984. if ( !pFbxNodeAttribute || pFbxNodeAttribute->GetAttributeType() != FbxNodeAttribute::eMesh )
  1985. return;
  1986. FbxMesh *pFbxMesh = reinterpret_cast< FbxMesh * >( pFbxNodeAttribute );
  1987. if ( !pFbxMesh )
  1988. return;
  1989. CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( pDmeDag->GetShape() );
  1990. if ( !pDmeMesh )
  1991. return;
  1992. CDmeVertexData *pDmeVertexData = pDmeMesh->GetBindBaseState();
  1993. if ( !pDmeVertexData )
  1994. {
  1995. Warning( "Warning! No DmeVertexData on DmeMesh (%s)\n", pDmeMesh->GetName() );
  1996. return;
  1997. }
  1998. const int nPositionCount = pDmeVertexData->GetPositionData().Count();
  1999. if ( nPositionCount <= 0 )
  2000. {
  2001. Warning( "Warning! Invalid position count (%d) on DmeMesh (%s)\n", nPositionCount, pDmeMesh->GetName() );
  2002. return;
  2003. }
  2004. const int nDeformerCount = pFbxMesh->GetDeformerCount( FbxDeformer::eSkin );
  2005. if ( nDeformerCount <= 0 )
  2006. return;
  2007. if ( Verbose2() )
  2008. {
  2009. Msg( " * Skinning Mesh: %s\n", pDmeDag->GetName() );
  2010. }
  2011. int nJointCount = 0;
  2012. CUtlVector< CVertexSkin > vertexSkinWeights;
  2013. vertexSkinWeights.SetCount( nPositionCount );
  2014. CDmElement *pDmRoot = pDmeModel->GetValueElement< CDmElement >( "__rootElement" );
  2015. for ( int i = 0; i < nDeformerCount; ++i )
  2016. {
  2017. FbxSkin *pFbxSkin = FbxCast< FbxSkin >( pFbxMesh->GetDeformer( i, FbxDeformer::eSkin ) );
  2018. if ( !pFbxSkin )
  2019. continue;
  2020. const int nClusterCount = pFbxSkin->GetClusterCount();
  2021. for ( int j = 0; j < nClusterCount; ++j )
  2022. {
  2023. FbxCluster *pFbxCluster = pFbxSkin->GetCluster( j );
  2024. FbxNode *pFbxLinkNode = pFbxCluster->GetLink();
  2025. if ( !pFbxLinkNode )
  2026. continue;
  2027. const FbxToDmxMap_t::IndexType_t nDmxIndex = fbxToDmxMap.Find( pFbxLinkNode );
  2028. if ( !fbxToDmxMap.IsValidIndex( nDmxIndex ) )
  2029. {
  2030. Warning( "Warning! FBX mesh (%s) skinned to unmapped node (%s)\n", pFbxNode->GetName(), pFbxLinkNode->GetName() );
  2031. continue;
  2032. }
  2033. CDmeJoint *pDmeJoint = CastElement< CDmeJoint >( fbxToDmxMap.Element( nDmxIndex ) );
  2034. if ( !pDmeJoint )
  2035. {
  2036. Warning( "Warning! FBX mesh (%s) skinned to non-joint (%s)\n", pFbxNode->GetName(), pFbxLinkNode->GetName() );
  2037. continue;
  2038. }
  2039. const int nJointIndex = pDmeModel->GetJointIndex( pDmeJoint );
  2040. if ( nJointIndex < 0 )
  2041. {
  2042. Warning( "Warning! FBX mesh (%s) skinned to joint (%s) which isn't in DmeModel\n", pFbxNode->GetName(), pDmeJoint->GetName() );
  2043. continue;
  2044. }
  2045. const FbxCluster::ELinkMode nLinkMode = pFbxCluster->GetLinkMode();
  2046. if ( nLinkMode != FbxCluster::eNormalize && nLinkMode != FbxCluster::eTotalOne )
  2047. {
  2048. Warning( "Warning! FBX mesh (%s) skinned to joint (%s) but mode %s isn't supported, only %s & %s\n", pFbxNode->GetName(), pDmeJoint->GetName(), szClusterModes[nLinkMode], szClusterModes[FbxCluster::eNormalize], szClusterModes[FbxCluster::eTotalOne] );
  2049. continue;
  2050. }
  2051. const int nIndexCount = pFbxCluster->GetControlPointIndicesCount();
  2052. const int *pnIndices = pFbxCluster->GetControlPointIndices();
  2053. const double *pfWeights = pFbxCluster->GetControlPointWeights();
  2054. if ( nIndexCount > 0 )
  2055. {
  2056. ++nJointCount;
  2057. if ( Verbose2() )
  2058. {
  2059. Msg( " + DmeJoint[%3d] %5d Vertices - %s\n",
  2060. nJointIndex, nIndexCount, pDmeJoint->GetName() );
  2061. }
  2062. }
  2063. for ( int k = 0; k < nIndexCount; ++k )
  2064. {
  2065. const int nVertexIndex = pnIndices[k];
  2066. Assert( nVertexIndex >= 0 && nVertexIndex <= vertexSkinWeights.Count() );
  2067. CVertexSkin &vertexSkinWeight = vertexSkinWeights[ nVertexIndex ];
  2068. vertexSkinWeight.AddWeight( nJointIndex, pfWeights[k] );
  2069. }
  2070. }
  2071. CUtlVector< CVertexSkin::SkinError_t > errorsAdded;
  2072. // Clean up skin weights
  2073. for ( int j = 0; j < vertexSkinWeights.Count(); ++j )
  2074. {
  2075. const CVertexSkin::SkinError_t nErr = vertexSkinWeights[j].Renormalize();
  2076. if ( nErr == CVertexSkin::kSkinErrorNone )
  2077. continue;
  2078. // Do a linear search, maximum of 3 elements and exit early if this error is already added, AddConversionError does a string compare to an array
  2079. if ( errorsAdded.HasElement( nErr ) )
  2080. continue;
  2081. errorsAdded.AddToTail( nErr );
  2082. switch ( nErr )
  2083. {
  2084. case CVertexSkin::kSkinErrorTooManyWeights:
  2085. AddConversionError( pDmRoot->GetFileId(), CFmtStr( "Too many skin weights on some vertices, maximum is %d weights per vertex, will renormalize and discard smallest weights", CVertexSkin::kMaxWeights ).Access() );
  2086. break;
  2087. case CVertexSkin::kSkinErrorZeroWeight:
  2088. AddConversionError( pDmRoot->GetFileId(), "Zero skin weights on one or more vertices" );
  2089. break;
  2090. case CVertexSkin::kSkinErrorNegativeWeight:
  2091. AddConversionError( pDmRoot->GetFileId(), "Negative total skin weights on one or more vertices" );
  2092. break;
  2093. }
  2094. }
  2095. CUtlVector< int > jointIndices;
  2096. jointIndices.EnsureCapacity( CVertexSkin::kMaxWeights * nPositionCount );
  2097. CUtlVector< float > jointWeights;
  2098. jointWeights.EnsureCapacity( CVertexSkin::kMaxWeights * nPositionCount );
  2099. for ( int j = 0; j < vertexSkinWeights.Count(); ++j )
  2100. {
  2101. const CVertexSkin &vertexSkinWeight = vertexSkinWeights[ j ];
  2102. for ( int k = 0; k < CVertexSkin::kMaxWeights; ++k )
  2103. {
  2104. jointIndices.AddToTail( vertexSkinWeight.GetIndex( k ) );
  2105. jointWeights.AddToTail( vertexSkinWeight.GetWeight( k ) );
  2106. }
  2107. }
  2108. FieldIndex_t nJointWeightsIndex;
  2109. FieldIndex_t nJointIndicesIndex;
  2110. pDmeVertexData->CreateJointWeightsAndIndices( CVertexSkin::kMaxWeights, &nJointWeightsIndex, &nJointIndicesIndex );
  2111. Assert( jointIndices.Count() == CVertexSkin::kMaxWeights * nPositionCount );
  2112. Assert( jointWeights.Count() == CVertexSkin::kMaxWeights * nPositionCount );
  2113. pDmeVertexData->AddVertexData( nJointIndicesIndex, jointIndices.Count() );
  2114. pDmeVertexData->SetVertexData( nJointIndicesIndex, 0, jointIndices.Count(), AT_INT, jointIndices.Base() );
  2115. pDmeVertexData->AddVertexData( nJointWeightsIndex, jointWeights.Count() );
  2116. pDmeVertexData->SetVertexData( nJointWeightsIndex, 0, jointWeights.Count(), AT_FLOAT, jointWeights.Base() );
  2117. break; // Only do the first skin deformer
  2118. }
  2119. if ( Verbose1() && !Verbose2() && nJointCount )
  2120. {
  2121. Msg( " * Skinning Mesh Joints %3d: %s\n", nJointCount, pDmeDag->GetName() );
  2122. }
  2123. }
  2124. //-----------------------------------------------------------------------------
  2125. //
  2126. //-----------------------------------------------------------------------------
  2127. void CDmFbxSerializer::AddBlendShapes_R( const FbxToDmxMap_t &fbxToDmxMap, CDmElement *pDmeRoot, FbxNode *pFbxNode ) const
  2128. {
  2129. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  2130. if ( pFbxNodeAttribute )
  2131. {
  2132. const FbxNodeAttribute::EType nAttributeType = pFbxNodeAttribute->GetAttributeType();
  2133. if ( nAttributeType == FbxNodeAttribute::eMesh )
  2134. {
  2135. FbxToDmxMap_t::IndexType_t nDmxIndex = fbxToDmxMap.Find( pFbxNode );
  2136. if ( fbxToDmxMap.IsValidIndex( nDmxIndex ) )
  2137. {
  2138. AddBlendShape( fbxToDmxMap.Element( nDmxIndex ), fbxToDmxMap, pDmeRoot, pFbxNode );
  2139. }
  2140. }
  2141. }
  2142. for ( int i = 0; i < pFbxNode->GetChildCount(); ++i )
  2143. {
  2144. AddBlendShapes_R( fbxToDmxMap, pDmeRoot, pFbxNode->GetChild( i ) );
  2145. }
  2146. }
  2147. //-----------------------------------------------------------------------------
  2148. //
  2149. //-----------------------------------------------------------------------------
  2150. void CDmFbxSerializer::AddBlendShape( CDmeDag *pDmeDag, const FbxToDmxMap_t &fbxToDmxMap, CDmElement *pDmeRoot, FbxNode *pFbxNode ) const
  2151. {
  2152. if ( !pDmeDag )
  2153. return;
  2154. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  2155. if ( !pFbxNodeAttribute || pFbxNodeAttribute->GetAttributeType() != FbxNodeAttribute::eMesh )
  2156. return;
  2157. FbxMesh *pFbxMesh = reinterpret_cast< FbxMesh * >( pFbxNodeAttribute );
  2158. if ( !pFbxMesh )
  2159. return;
  2160. CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( pDmeDag->GetShape() );
  2161. if ( !pDmeMesh )
  2162. return;
  2163. CDmeVertexData *pDmeVertexData = pDmeMesh->GetBindBaseState();
  2164. if ( !pDmeVertexData )
  2165. {
  2166. Warning( "Warning! No DmeVertexData on DmeMesh (%s)\n", pDmeMesh->GetName() );
  2167. return;
  2168. }
  2169. const CUtlVector< Vector > &positionData = pDmeVertexData->GetPositionData();
  2170. const int nPositionCount = positionData.Count();
  2171. if ( nPositionCount <= 0 )
  2172. {
  2173. Warning( "Warning! Invalid position count (%d) on DmeMesh (%s)\n", nPositionCount, pDmeMesh->GetName() );
  2174. return;
  2175. }
  2176. const int nBlendShapeCount = pFbxMesh->GetDeformerCount( FbxDeformer::eBlendShape );
  2177. if ( nBlendShapeCount <= 0 )
  2178. return;
  2179. CUtlVector< Vector > vDeltas;
  2180. CUtlVector< int > nDeltaIndices;
  2181. CDmeCombinationOperator *pDmeComboOp = FindOrCreateComboOp( pDmeRoot );
  2182. if ( !pDmeComboOp )
  2183. {
  2184. Warning( "Warning! Couldn't create DmeComboOp for FbxMesh %s\n", pFbxNode->GetName() );
  2185. return;
  2186. }
  2187. for ( int nBlendShapeIndex = 0; nBlendShapeIndex < nBlendShapeCount; ++nBlendShapeIndex )
  2188. {
  2189. FbxBlendShape *pFbxBlendShape = FbxCast< FbxBlendShape >( pFbxMesh->GetDeformer(nBlendShapeIndex, FbxDeformer::eBlendShape) );
  2190. const int nBlendShapeChannelCount = pFbxBlendShape->GetBlendShapeChannelCount();
  2191. if ( Verbose1() )
  2192. {
  2193. if ( nBlendShapeCount > 1 )
  2194. {
  2195. Msg( " * BlendShape Mesh: Shapes %3d - BlendShape %3d/%3d - %s\n",
  2196. nBlendShapeChannelCount, nBlendShapeIndex + 1, nBlendShapeCount, pDmeMesh->GetName() );
  2197. }
  2198. else
  2199. {
  2200. Msg( " * BlendShape Mesh: Shapes %3d - %s\n",
  2201. nBlendShapeChannelCount, pDmeMesh->GetName() );
  2202. }
  2203. }
  2204. for ( int nBlendShapeChannelIndex = 0; nBlendShapeChannelIndex < nBlendShapeChannelCount; ++nBlendShapeChannelIndex )
  2205. {
  2206. FbxBlendShapeChannel *pFbxBlendShapeChannel = pFbxBlendShape->GetBlendShapeChannel( nBlendShapeChannelIndex );
  2207. const char *pszDeltaName = pFbxBlendShapeChannel->GetName();
  2208. const char *pszExt = V_GetFileExtension( pszDeltaName ); // FBX will probably name this blendShape.<THING> we just want <THING>
  2209. if ( pszExt )
  2210. {
  2211. pszDeltaName = pszExt;
  2212. }
  2213. CUtlString sDeltaName;
  2214. CleanupName( sDeltaName, pszDeltaName );
  2215. const int nTargetShapeCount = pFbxBlendShapeChannel->GetTargetShapeCount();
  2216. for ( int nTargetShapeIndex = 0; nTargetShapeIndex < nTargetShapeCount; ++nTargetShapeIndex )
  2217. {
  2218. FbxShape *pFbxShape = pFbxBlendShapeChannel->GetTargetShape( nTargetShapeIndex );
  2219. const int nControlPointsCount = pFbxShape->GetControlPointsCount();
  2220. const FbxVector4 *pvControlPoints = pFbxShape->GetControlPoints();
  2221. const int nControlPointIndicesCount = pFbxShape->GetControlPointIndicesCount();
  2222. const int *pnControlPointIndices = pFbxShape->GetControlPointIndices();
  2223. // pvControlPoints has as many values as the original mesh but only the vertices indexed by pnControlPointIndices are deltas
  2224. Assert( nControlPointsCount == nPositionCount );
  2225. if ( nControlPointsCount == nPositionCount )
  2226. {
  2227. vDeltas.SetCount( nControlPointIndicesCount );
  2228. nDeltaIndices.SetCount( nControlPointIndicesCount );
  2229. for ( int i = 0; i < nControlPointIndicesCount; ++i )
  2230. {
  2231. const int nDeltaIndex = pnControlPointIndices[i];
  2232. const Vector &vDme = positionData[ nDeltaIndex ];
  2233. const FbxVector4 &vFbxSrc = pvControlPoints[ nDeltaIndex ];
  2234. vDeltas[i] = Vector( vFbxSrc[0] - vDme.x, vFbxSrc[1] - vDme.y, vFbxSrc[2] - vDme.z );
  2235. nDeltaIndices[i] = nDeltaIndex;
  2236. }
  2237. }
  2238. else
  2239. {
  2240. Warning( "Warning! FbxMesh %s Has Unhandled FbxBlendShapeChannel %s\n", pFbxNode->GetName(), pszDeltaName );
  2241. }
  2242. CDmeVertexDeltaData *pDmeVertexDeltaData = pDmeMesh->FindOrCreateDeltaState( pszDeltaName, m_bOptUnderscoreForCorrectors );
  2243. pszDeltaName = pDmeVertexDeltaData->GetName(); // The delta name could be changed if m_bOptUnderscoreForCorrectors is on, i.e. B_A becomes A_B
  2244. pDmeVertexDeltaData->FlipVCoordinate( true );
  2245. FieldIndex_t nDeltaPosIndex = pDmeVertexDeltaData->CreateField( CDmeVertexDeltaData::FIELD_POSITION );
  2246. pDmeVertexDeltaData->AddVertexData( nDeltaPosIndex, vDeltas.Count() );
  2247. pDmeVertexDeltaData->SetVertexData( nDeltaPosIndex, 0, vDeltas.Count(), AT_VECTOR3, vDeltas.Base() );
  2248. pDmeVertexDeltaData->SetVertexIndices( nDeltaPosIndex, 0, nDeltaIndices.Count(), nDeltaIndices.Base() );
  2249. if ( FindOrCreateControl( pDmeComboOp, pszDeltaName ) )
  2250. {
  2251. pDmeVertexDeltaData->SetValue( "corrected", true );
  2252. }
  2253. // TODO: See if FBX handle exporting expressions? Face rules?
  2254. // TODO: Handle normals
  2255. if ( Verbose2() )
  2256. {
  2257. Msg( " + Shape %3d Deltas: %5d - %s\n",
  2258. nBlendShapeChannelIndex, nControlPointIndicesCount, sDeltaName.String() );
  2259. }
  2260. AssertMsg( nTargetShapeCount == 1, "TODO: Handle multiple target shapes?" );
  2261. break;
  2262. }
  2263. }
  2264. }
  2265. pDmeComboOp->AddTarget( pDmeMesh );
  2266. }
  2267. //-----------------------------------------------------------------------------
  2268. //
  2269. //-----------------------------------------------------------------------------
  2270. CDmeCombinationOperator * CDmFbxSerializer::FindOrCreateComboOp( CDmElement *pDmeRoot ) const
  2271. {
  2272. CDmeCombinationOperator *pDmeComboOp = pDmeRoot->GetValueElement< CDmeCombinationOperator >( "combinationOperator" );
  2273. if ( !pDmeComboOp )
  2274. {
  2275. pDmeComboOp = CreateElement< CDmeCombinationOperator >( "combinationOperator", pDmeRoot->GetFileId() );
  2276. pDmeRoot->SetValue( "combinationOperator", pDmeComboOp );
  2277. }
  2278. return pDmeComboOp;
  2279. }
  2280. //-----------------------------------------------------------------------------
  2281. //
  2282. //-----------------------------------------------------------------------------
  2283. bool CDmFbxSerializer::FindOrCreateControl( CDmeCombinationOperator *pDmeComboOp, const char *pszName ) const
  2284. {
  2285. if ( m_bOptUnderscoreForCorrectors )
  2286. {
  2287. if ( strchr( pszName, '_' ) ) // Don't create a control if m_bOptUnderscoreForCorrectors is true and name has '_' in it
  2288. return false;
  2289. }
  2290. const ControlIndex_t nControlIndex = pDmeComboOp->FindOrCreateControl( pszName, false, true );
  2291. return nControlIndex >= 0;
  2292. }
  2293. //-----------------------------------------------------------------------------
  2294. //
  2295. //-----------------------------------------------------------------------------
  2296. CUtlString CleanupName( const char *pszName )
  2297. {
  2298. CUtlString sCleanName( pszName );
  2299. const int nNameLen = V_strlen( pszName );
  2300. if ( nNameLen > 1 )
  2301. {
  2302. const char *pszColon = V_strrchr( pszName, ':' );
  2303. if ( pszColon && nNameLen > ( pszColon - pszName + 1 ) && *( pszColon + 1 ) )
  2304. {
  2305. sCleanName.Set( pszColon + 1 );
  2306. }
  2307. }
  2308. return sCleanName;
  2309. }
  2310. //-----------------------------------------------------------------------------
  2311. //
  2312. //-----------------------------------------------------------------------------
  2313. CUtlString GetName( const FbxNode *pFbxNode )
  2314. {
  2315. const FbxString sName = pFbxNode->GetNameOnly();
  2316. const char *pszName = sName.Buffer();
  2317. return CleanupName( pszName );
  2318. }
  2319. //-----------------------------------------------------------------------------
  2320. //
  2321. //-----------------------------------------------------------------------------
  2322. void CDmFbxSerializer::GetName( CUtlString &sCleanName, const FbxNode *pFbxNode ) const
  2323. {
  2324. FbxString sName = pFbxNode->GetNameOnly();
  2325. const char *pszName = sName.Buffer();
  2326. sCleanName = ::CleanupName( pszName );
  2327. }
  2328. //-----------------------------------------------------------------------------
  2329. //
  2330. //-----------------------------------------------------------------------------
  2331. void CDmFbxSerializer::CleanupName( CUtlString &sCleanName, const char *pszName ) const
  2332. {
  2333. sCleanName = ::CleanupName( pszName );
  2334. }
  2335. //-----------------------------------------------------------------------------
  2336. //
  2337. //-----------------------------------------------------------------------------
  2338. struct FbxDmxAnimData_t
  2339. {
  2340. FbxDmxAnimData_t()
  2341. : m_pFbxNode( nullptr )
  2342. , m_pDmePosLog( nullptr )
  2343. , m_pDmeRotLog( nullptr )
  2344. , m_pParent( nullptr )
  2345. , m_pFbxAnimCurve( nullptr )
  2346. , m_pDmeFloatLog( nullptr )
  2347. , m_flMin( 0.0f )
  2348. , m_flMax( 1.0f )
  2349. , m_bNormalize( false )
  2350. {
  2351. SetIdentityMatrix( m_mWorldInverse );
  2352. }
  2353. void SetWorldMatrix( const matrix3x4_t &mWorld )
  2354. {
  2355. MatrixInvert( mWorld, m_mWorldInverse );
  2356. }
  2357. FbxNode *m_pFbxNode;
  2358. CDmeVector3Log *m_pDmePosLog;
  2359. CDmeQuaternionLog *m_pDmeRotLog;
  2360. FbxDmxAnimData_t *m_pParent;
  2361. matrix3x4_t m_mWorldInverse;
  2362. FbxProperty m_fbxProperty;
  2363. FbxAnimCurve *m_pFbxAnimCurve;
  2364. CDmeFloatLog *m_pDmeFloatLog;
  2365. bool m_bNormalize;
  2366. float m_flMin;
  2367. float m_flMax;
  2368. };
  2369. //-----------------------------------------------------------------------------
  2370. //
  2371. //-----------------------------------------------------------------------------
  2372. static CDmeFloatLog *CreateMorphChannel( const char *pszChannelName, CDmeChannelsClip *pDmeChannelsClip )
  2373. {
  2374. CDmeChannel *pDmeFloatChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_flex_channel", pszChannelName ).Access(), pDmeChannelsClip->GetFileId() );
  2375. pDmeFloatChannel->SetMode( CM_PLAY );
  2376. CDmElement *pDmeToElement = CreateElement< CDmElement >( pszChannelName, pDmeFloatChannel->GetFileId() );
  2377. pDmeToElement->SetValue( "flexWeight", 0.0f );
  2378. pDmeFloatChannel->SetOutput( pDmeToElement, "flexWeight" );
  2379. CDmeFloatLog *pDmeFloatLog = pDmeFloatChannel->CreateLog< float >();
  2380. pDmeFloatLog->SetValueThreshold( 1.0e-6 );
  2381. pDmeFloatChannel->SetValue( "channelType", "morph" );
  2382. pDmeChannelsClip->m_Channels.AddToTail( pDmeFloatChannel );
  2383. return pDmeFloatLog;
  2384. }
  2385. //-----------------------------------------------------------------------------
  2386. //
  2387. //-----------------------------------------------------------------------------
  2388. void CDmFbxSerializer::ComputeVstFlexSliderAnimDataList_R(
  2389. FbxAnimLayer *pFbxAnimLayer,
  2390. CUtlVector< FbxDmxAnimData_t * > &animDataList,
  2391. CDmeChannelsClip *pDmeChannelsClip,
  2392. const CDmFbxSerializer::FbxToDmxMap_t &fbxToDmxMap,
  2393. FbxNode *pFbxNode ) const
  2394. {
  2395. CDmFbxSerializer::FbxToDmxMap_t::IndexType_t nIndex = fbxToDmxMap.Find( pFbxNode );
  2396. if ( !fbxToDmxMap.IsValidIndex( nIndex ) )
  2397. return;
  2398. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  2399. if ( pFbxNodeAttribute )
  2400. {
  2401. const FbxNodeAttribute::EType nAttributeType = pFbxNodeAttribute->GetAttributeType();
  2402. if ( nAttributeType == FbxNodeAttribute::eNull )
  2403. {
  2404. const CUtlString sNodeName = ::GetName( pFbxNode );
  2405. if ( sNodeName == "vstFlexSlider" || pFbxNode->FindProperty( "vstFlexSlider", false ).IsValid() )
  2406. {
  2407. for ( FbxProperty fbxProperty = pFbxNode->GetFirstProperty(); fbxProperty.IsValid(); fbxProperty = pFbxNode->GetNextProperty( fbxProperty ) )
  2408. {
  2409. const FbxDataType fbxDataType = fbxProperty.GetPropertyDataType();
  2410. const EFbxType eFbxType = fbxDataType.GetType();
  2411. if ( fbxProperty.GetFlag( FbxPropertyFlags::eUserDefined ) && ( eFbxType == eFbxDouble || eFbxType == eFbxFloat ) )
  2412. {
  2413. const char *pszChannelName = fbxProperty.GetNameAsCStr();
  2414. FbxDmxAnimData_t *pAnimData = animDataList[ animDataList.AddToTail( new FbxDmxAnimData_t ) ];
  2415. animDataList.AddToTail( pAnimData );
  2416. pAnimData->m_fbxProperty = fbxProperty;
  2417. if ( fbxProperty.HasMinLimit() && fbxProperty.HasMaxLimit() )
  2418. {
  2419. pAnimData->m_bNormalize = true;
  2420. pAnimData->m_flMin = fbxProperty.GetMinLimit();
  2421. pAnimData->m_flMax = fbxProperty.GetMaxLimit();
  2422. }
  2423. pAnimData->m_pDmeFloatLog = CreateMorphChannel( pszChannelName, pDmeChannelsClip );
  2424. if ( Verbose1() )
  2425. {
  2426. Msg( " - vstFlexSlider FlexControl Channel: %s\n", pszChannelName );
  2427. }
  2428. }
  2429. }
  2430. }
  2431. }
  2432. }
  2433. for ( int i = 0; i < pFbxNode->GetChildCount(); ++i )
  2434. {
  2435. ComputeVstFlexSliderAnimDataList_R( pFbxAnimLayer, animDataList, pDmeChannelsClip, fbxToDmxMap, pFbxNode->GetChild( i ) );
  2436. }
  2437. }
  2438. //-----------------------------------------------------------------------------
  2439. //
  2440. //-----------------------------------------------------------------------------
  2441. void CDmFbxSerializer::ComputeAnimDataList_R(
  2442. FbxAnimLayer *pFbxAnimLayer,
  2443. CUtlVector< FbxDmxAnimData_t * > &animDataList,
  2444. CDmeChannelsClip *pDmeChannelsClip,
  2445. const CDmFbxSerializer::FbxToDmxMap_t &fbxToDmxMap,
  2446. FbxNode *pFbxNode,
  2447. FbxDmxAnimData_t *pAnimDataParent ) const
  2448. {
  2449. CDmFbxSerializer::FbxToDmxMap_t::IndexType_t nIndex = fbxToDmxMap.Find( pFbxNode );
  2450. if ( !fbxToDmxMap.IsValidIndex( nIndex ) )
  2451. return;
  2452. FbxNodeAttribute *pFbxNodeAttribute = pFbxNode->GetNodeAttribute();
  2453. if ( pFbxNodeAttribute && pFbxNodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh )
  2454. {
  2455. FbxGeometry *pFbxGeometry = static_cast< FbxGeometry * >( pFbxNodeAttribute );
  2456. const int nBlendShapeDeformerCount = pFbxGeometry->GetDeformerCount( FbxDeformer::eBlendShape );
  2457. for ( int lBlendShapeIndex = 0; lBlendShapeIndex < nBlendShapeDeformerCount; ++lBlendShapeIndex )
  2458. {
  2459. FbxBlendShape *pFbxBlendShape = static_cast< FbxBlendShape * >( pFbxGeometry->GetDeformer( lBlendShapeIndex, FbxDeformer::eBlendShape ) );
  2460. if ( pFbxBlendShape )
  2461. {
  2462. const int nBlendShapeChannelCount = pFbxBlendShape->GetBlendShapeChannelCount();
  2463. for ( int nChannelIndex = 0; nChannelIndex < nBlendShapeChannelCount; ++nChannelIndex )
  2464. {
  2465. FbxBlendShapeChannel *pFbxBlendShapeChannel = pFbxBlendShape->GetBlendShapeChannel( nChannelIndex );
  2466. const CUtlString sChannelName = ::CleanupName( pFbxBlendShapeChannel->GetName() );
  2467. const char *pszChannelName = sChannelName.Get();
  2468. const char *pszExt = V_GetFileExtension( pszChannelName ); // FBX will probably name this blendShape.<THING> we just want <THING>
  2469. if ( pszExt )
  2470. {
  2471. pszChannelName = pszExt;
  2472. }
  2473. FbxAnimCurve *pFbxAnimCurve = pFbxGeometry->GetShapeChannel( lBlendShapeIndex, nChannelIndex, pFbxAnimLayer, true );
  2474. if ( pFbxAnimCurve )
  2475. {
  2476. bool bDefined = false;
  2477. FOR_EACH_VEC( animDataList, aIt )
  2478. {
  2479. const FbxDmxAnimData_t *pFbxDmxAnimData = animDataList[aIt];
  2480. if ( pFbxDmxAnimData->m_pDmeFloatLog && pFbxDmxAnimData->m_fbxProperty.IsValid() && !V_stricmp( pFbxDmxAnimData->m_fbxProperty.GetName(), pszChannelName ) )
  2481. {
  2482. if ( Verbose1() )
  2483. {
  2484. Warning( " - vstFlexSlider Already defined FlexControl Channel: %s, ignoring FbxBlendShapeChannel with same name\n", pszChannelName );
  2485. }
  2486. bDefined = true;
  2487. break;
  2488. }
  2489. }
  2490. if ( !bDefined )
  2491. {
  2492. if ( Verbose1() )
  2493. {
  2494. Msg( " - FbxBlendShapeChannel: %s\n", pszChannelName );
  2495. }
  2496. FbxDmxAnimData_t *pAnimData = animDataList[ animDataList.AddToTail( new FbxDmxAnimData_t ) ];
  2497. animDataList.AddToTail( pAnimData );
  2498. pAnimData->m_pFbxAnimCurve = pFbxAnimCurve;
  2499. pAnimData->m_pDmeFloatLog = CreateMorphChannel( pszChannelName, pDmeChannelsClip );
  2500. }
  2501. }
  2502. }
  2503. }
  2504. }
  2505. }
  2506. FbxDmxAnimData_t *pAnimData = new FbxDmxAnimData_t;
  2507. animDataList.AddToTail( pAnimData );
  2508. CDmeDag *pDmeDag = fbxToDmxMap.Element( nIndex );
  2509. const bool bIsRoot = pDmeDag->GetValue( "__rootNode", false );
  2510. pAnimData->m_pFbxNode = pFbxNode;
  2511. pAnimData->m_pParent = bIsRoot ? NULL : pAnimDataParent;
  2512. CDmeTransform *pDmeTransform = pDmeDag->GetTransform();
  2513. CDmeChannel *pDmePosChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_p", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() );
  2514. pDmePosChannel->SetMode( CM_PLAY );
  2515. pDmePosChannel->SetOutput( pDmeTransform, "position" );
  2516. CDmeVector3Log *pDmePosLog = pDmePosChannel->CreateLog< Vector >();
  2517. pDmePosLog->SetValueThreshold( 1.0e-6 );
  2518. pDmeChannelsClip->m_Channels.AddToTail( pDmePosChannel );
  2519. CDmeChannel *pDmeRotChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_o", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() );
  2520. pDmeRotChannel->SetMode( CM_PLAY );
  2521. pDmeRotChannel->SetOutput( pDmeTransform, "orientation" );
  2522. CDmeQuaternionLog *pDmeRotLog = pDmeRotChannel->CreateLog< Quaternion >();
  2523. pDmeRotLog->SetValueThreshold( 1.0e-6 );
  2524. pDmeChannelsClip->m_Channels.AddToTail( pDmeRotChannel );
  2525. pAnimData->m_pDmePosLog = pDmePosLog;
  2526. pAnimData->m_pDmeRotLog = pDmeRotLog;
  2527. for ( int i = 0; i < pFbxNode->GetChildCount(); ++i )
  2528. {
  2529. ComputeAnimDataList_R( pFbxAnimLayer, animDataList, pDmeChannelsClip, fbxToDmxMap, pFbxNode->GetChild( i ), pAnimData );
  2530. }
  2531. }
  2532. //-----------------------------------------------------------------------------
  2533. //
  2534. //-----------------------------------------------------------------------------
  2535. void CDmFbxSerializer::LoadAnimation(
  2536. CDmElement *pDmeRoot, CDmeModel *pDmeModel, const FbxToDmxMap_t &fbxToDmxMap, FbxScene *pFbxScene, FbxNode *pFbxRootNode, FbxTime::EMode eFbxTimeMode ) const
  2537. {
  2538. // Only process the first animation layer of the first animation stack
  2539. FbxAnimStack *pFbxAnimStack = pFbxScene->GetSrcObject<FbxAnimStack>();
  2540. if ( !pFbxAnimStack )
  2541. return;
  2542. FbxAnimLayer *pFbxAnimLayer = pFbxAnimStack->GetMember<FbxAnimLayer >();
  2543. if ( !pFbxAnimLayer )
  2544. return;
  2545. FbxArray< FbxString * > mAnimStackNameArray;
  2546. pFbxScene->FillAnimStackNameArray( mAnimStackNameArray );
  2547. if ( mAnimStackNameArray.GetCount() <= 0 )
  2548. return;
  2549. pFbxScene->SetCurrentAnimationStack( pFbxAnimStack );
  2550. FbxTakeInfo *pFbxTakeInfo = pFbxScene->GetTakeInfo( *( mAnimStackNameArray[0] ) );
  2551. if ( !pFbxTakeInfo )
  2552. return;
  2553. const FbxTime tFbxStart = pFbxTakeInfo->mLocalTimeSpan.GetStart();
  2554. const FbxLongLong nFbxStart = tFbxStart.GetFrameCount( eFbxTimeMode );
  2555. const FbxTime tFbxEnd = pFbxTakeInfo->mLocalTimeSpan.GetStop();
  2556. const FbxLongLong nFbxEnd = tFbxEnd.GetFrameCount( eFbxTimeMode );
  2557. const double flFrameRate = FbxTime::GetFrameRate( eFbxTimeMode );
  2558. const DmeFramerate_t dmeFrameRate( static_cast< float >( flFrameRate ) );
  2559. if ( Verbose1() )
  2560. {
  2561. Msg( " * Animation Stack %s Layer %s - %s\n", pFbxAnimStack->GetName(), pFbxAnimLayer->GetName(), mAnimStackNameArray[0]->Buffer() );
  2562. Msg( " + Fbx Time Start: %6.2f End: %6.2f Duration: %6.2f @ %6.2f fps\n",
  2563. tFbxStart.GetSecondDouble(),
  2564. tFbxEnd.GetSecondDouble(),
  2565. pFbxTakeInfo->mLocalTimeSpan.GetDuration().GetSecondDouble(),
  2566. flFrameRate );
  2567. Msg( " + Fbx Frame Start: %6lld End: %6lld Duration: %6lld @ %6.2f fps\n",
  2568. nFbxStart,
  2569. nFbxEnd,
  2570. pFbxTakeInfo->mLocalTimeSpan.GetDuration().GetFrameCount( eFbxTimeMode ),
  2571. flFrameRate );
  2572. }
  2573. const char *pszAnimName = "anim"; // TODO: Get this value, name of file?
  2574. DmFileId_t nFileId = pDmeRoot->GetFileId();
  2575. CDmeAnimationList *pDmeAnimationList = CreateElement< CDmeAnimationList >( pszAnimName, nFileId );
  2576. CDmeChannelsClip *pDmeChannelsClip = CreateElement< CDmeChannelsClip >( pszAnimName, nFileId );
  2577. pDmeAnimationList->AddAnimation( pDmeChannelsClip );
  2578. pDmeRoot->SetValue( "animationList", pDmeAnimationList );
  2579. pDmeModel->SetValue( "animationList", pDmeAnimationList );
  2580. pDmeChannelsClip->SetStartTime( DmeTime_t( tFbxStart.GetSecondDouble() ) );
  2581. pDmeChannelsClip->SetTimeOffset( pDmeChannelsClip->GetStartTime() );
  2582. pDmeChannelsClip->SetValue( "frameRate", static_cast< int >( dmeFrameRate.GetFramesPerSecond() ) );
  2583. CUtlVector< FbxDmxAnimData_t * > animList;
  2584. // Look for flex animation on vstFlexSlider nodes first
  2585. for ( int i = 0; i < pFbxRootNode->GetChildCount(); ++i )
  2586. {
  2587. ComputeVstFlexSliderAnimDataList_R( pFbxAnimLayer, animList, pDmeChannelsClip, fbxToDmxMap, pFbxRootNode->GetChild( i ) );
  2588. }
  2589. for ( int i = 0; i < pFbxRootNode->GetChildCount(); ++i )
  2590. {
  2591. ComputeAnimDataList_R( pFbxAnimLayer, animList, pDmeChannelsClip, fbxToDmxMap, pFbxRootNode->GetChild( i ), NULL );
  2592. }
  2593. matrix3x4_t mDmeWorld;
  2594. matrix3x4_t mDmeLocal;
  2595. FbxLongLong nFbxCurrent;
  2596. FbxTime tFbxCurrent;
  2597. for ( nFbxCurrent = nFbxStart; nFbxCurrent <= nFbxEnd; nFbxCurrent += 1 )
  2598. {
  2599. tFbxCurrent.SetFrame( nFbxCurrent, eFbxTimeMode );
  2600. const DmeTime_t tDmeCurrent( tFbxCurrent.GetSecondDouble() );
  2601. if ( Verbose2() )
  2602. {
  2603. Msg( " * Fbx Time: %6.2f Fbx Frame: %6lld Dmx Time: %6.2f Dmx Frame: %d\n",
  2604. tFbxCurrent.GetSecondDouble(), tFbxCurrent.GetFrameCount( eFbxTimeMode ),
  2605. tDmeCurrent.GetSeconds(), tDmeCurrent.CurrentFrame( dmeFrameRate ) );
  2606. }
  2607. for ( int i = 0; i < animList.Count(); ++i )
  2608. {
  2609. FbxDmxAnimData_t *pAnimData = animList[ i ];
  2610. if ( pAnimData->m_pDmeFloatLog )
  2611. {
  2612. if ( pAnimData->m_fbxProperty.IsValid() )
  2613. {
  2614. float flFloatValue = pAnimData->m_fbxProperty.EvaluateValue< float >( tFbxCurrent );
  2615. if ( pAnimData->m_bNormalize )
  2616. {
  2617. flFloatValue = RemapVal( flFloatValue, pAnimData->m_flMin, pAnimData->m_flMax, 0.0f, 1.0f );
  2618. }
  2619. pAnimData->m_pDmeFloatLog->SetKey( tDmeCurrent, flFloatValue );
  2620. }
  2621. else if ( pAnimData->m_pFbxAnimCurve )
  2622. {
  2623. const float flFloatValue = pAnimData->m_pFbxAnimCurve->Evaluate( tFbxCurrent );
  2624. pAnimData->m_pDmeFloatLog->SetKey( tDmeCurrent, flFloatValue );
  2625. }
  2626. continue;
  2627. }
  2628. FbxNode *pFbxNode = pAnimData->m_pFbxNode;
  2629. const FbxAMatrix &mFbxWorld = pFbxNode->EvaluateGlobalTransform( tFbxCurrent );
  2630. const FbxVector4 vFbxTranslate = mFbxWorld.GetT();
  2631. const FbxQuaternion qFbxRotate = mFbxWorld.GetQ();
  2632. Assert( vFbxTranslate[3] == 0.0 || vFbxTranslate[3] == 1.0 );
  2633. Vector vDmeTranslate( vFbxTranslate[0], vFbxTranslate[1], vFbxTranslate[2] );
  2634. Quaternion qDmeRotate( qFbxRotate[0], qFbxRotate[1], qFbxRotate[2], qFbxRotate[3] );
  2635. AngleMatrix( RadianEuler( qDmeRotate ), vDmeTranslate, mDmeWorld );
  2636. pAnimData->SetWorldMatrix( mDmeWorld );
  2637. if ( pAnimData->m_pParent )
  2638. {
  2639. MatrixMultiply( pAnimData->m_pParent->m_mWorldInverse, mDmeWorld, mDmeLocal );
  2640. MatrixAngles( mDmeLocal, qDmeRotate, vDmeTranslate );
  2641. }
  2642. pAnimData->m_pDmePosLog->SetKey( tDmeCurrent, vDmeTranslate );
  2643. pAnimData->m_pDmeRotLog->SetKey( tDmeCurrent, qDmeRotate );
  2644. }
  2645. }
  2646. pDmeChannelsClip->SetDuration( DmeTime_t( pFbxTakeInfo->mLocalTimeSpan.GetDuration().GetSecondDouble() ) );
  2647. if ( Verbose1() )
  2648. {
  2649. Msg( " + Dme Time Start: %6.2f End: %6.2f Duration: %6.2f @ %6.2f fps\n",
  2650. pDmeChannelsClip->GetStartTime().GetSeconds(),
  2651. pDmeChannelsClip->GetEndTime().GetSeconds(),
  2652. pDmeChannelsClip->GetDuration().GetSeconds(),
  2653. flFrameRate );
  2654. Msg( " + Dme Frame Start: %6d End: %6d Duration: %6d @ %6.2f fps\n",
  2655. pDmeChannelsClip->GetStartTime().CurrentFrame( dmeFrameRate ),
  2656. pDmeChannelsClip->GetEndTime().CurrentFrame( dmeFrameRate ),
  2657. pDmeChannelsClip->GetDuration().CurrentFrame( dmeFrameRate ),
  2658. flFrameRate );
  2659. }
  2660. animList.PurgeAndDeleteElements();
  2661. }
  2662. //-----------------------------------------------------------------------------
  2663. //
  2664. //-----------------------------------------------------------------------------
  2665. FbxManager *CDmFbxSerializer::GetFbxManager()
  2666. {
  2667. static bool bWarned = false;
  2668. if ( !g_pFbx )
  2669. {
  2670. if ( !bWarned )
  2671. {
  2672. Log_Warning( LOG_FBX_SYSTEM, "Warning! FBX system not initialized\n" );
  2673. bWarned = true;
  2674. }
  2675. return NULL;
  2676. }
  2677. return g_pFbx->GetFbxManager();
  2678. }
  2679. //-----------------------------------------------------------------------------
  2680. //
  2681. //-----------------------------------------------------------------------------
  2682. void CDmFbxSerializer::AddConversionError( DmFileId_t nDmFileId, const char *pszErrorMsg )
  2683. {
  2684. if ( !pszErrorMsg )
  2685. return;
  2686. CDmElement *pDmRoot = g_pDataModel->GetElement( g_pDataModel->GetFileRoot( nDmFileId ) );
  2687. if ( !pDmRoot )
  2688. return;
  2689. CDmAttribute *pConversionErrorsAttr = pDmRoot->AddAttribute( "conversionErrors", AT_STRING_ARRAY );
  2690. if ( pConversionErrorsAttr )
  2691. {
  2692. CDmrStringArray conversionErrors( pConversionErrorsAttr );
  2693. for ( int i = 0; i < conversionErrors.Count(); ++i )
  2694. {
  2695. if ( !V_stricmp( conversionErrors[i], pszErrorMsg ) )
  2696. return;
  2697. }
  2698. conversionErrors.AddToTail( pszErrorMsg );
  2699. Warning( "%s\n", pszErrorMsg );
  2700. }
  2701. }