WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
FBXExporter.cpp
Go to the documentation of this file.
1/*----------------------------------------------------------------------*\
2| This file is part of WoW Model Viewer |
3| |
4| WoW Model Viewer is free software: you can redistribute it and/or |
5| modify it under the terms of the GNU General Public License as |
6| published by the Free Software Foundation, either version 3 of the |
7| License, or (at your option) any later version. |
8| |
9| WoW Model Viewer is distributed in the hope that it will be useful, |
10| but WITHOUT ANY WARRANTY; without even the implied warranty of |
11| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12| GNU General Public License for more details. |
13| |
14| You should have received a copy of the GNU General Public License |
15| along with WoW Model Viewer. |
16| If not, see <http://www.gnu.org/licenses/>. |
17\*----------------------------------------------------------------------*/
18
19/*
20 * FBXExporter.cpp
21 *
22 * Created on: 13 june 2015
23 * Copyright: 2015 , WoW Model Viewer (http://wowmodelviewer.net)
24 */
25
26#include "FBXExporter.h"
27
28#ifdef _WIN32
29#include <windows.h>
30#endif
31#include <filesystem>
32#include <mutex>
33
34#include "FBXHeaders.h"
35#include "FBXAnimExporter.h"
36#include "ModelRenderPass.h"
37#include "WoWModel.h"
38
39#include "GlobalSettings.h"
40
41FBXExporter::FBXExporter() : m_p_manager(nullptr), m_p_scene(nullptr), m_p_model(nullptr),
42 m_p_meshNode(nullptr), m_p_skeletonNode(nullptr)
43{
45}
46
47std::wstring FBXExporter::menuLabel() const
48{
49 return L"FBX...";
50}
51
52std::wstring FBXExporter::fileSaveTitle() const
53{
54 return L"Save FBX file";
55}
56
57std::wstring FBXExporter::fileSaveFilter() const
58{
59 return L"FBX files (*.fbx)|*.fbx";
60}
61
62bool FBXExporter::exportModel(Model* model, std::wstring target)
63{
64 reset();
65
66 m_p_model = dynamic_cast<WoWModel*>(model);
67
68 if (!m_p_model)
69 return false;
70
71 m_filename = target;
72 m_p_manager = FbxManager::Create();
73 if (!m_p_manager)
74 {
75 LOG_ERROR << "Unable to create the FBX SDK manager";
76 return false;
77 }
78
79 FbxIOSettings* ios = FbxIOSettings::Create(m_p_manager, IOSROOT);
80 m_p_manager->SetIOSettings(ios);
81
82 // ensure that textures are embed in final fbx file
83 ios->SetBoolProp(EXP_FBX_MATERIAL, true);
84 ios->SetBoolProp(EXP_FBX_TEXTURE, true);
85 ios->SetBoolProp(EXP_FBX_EMBEDDED, true);
86 ios->SetBoolProp(EXP_FBX_SHAPE, true);
87 ios->SetBoolProp(EXP_FBX_GOBO, true);
88 ios->SetBoolProp(EXP_FBX_ANIMATION, false);
89 ios->SetBoolProp(EXP_FBX_GLOBAL_SETTINGS, true);
90
91 FbxExporter* exporter = nullptr;
92
93 m_fileVersion = FBX_2014_00_COMPATIBLE;
94
95 std::string filenameUtf8;
96 if (!m_filename.empty())
97 {
98 int n = WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), nullptr, 0, nullptr, nullptr);
99 filenameUtf8.resize(n);
100 WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), filenameUtf8.data(), n, nullptr, nullptr);
101 }
102 if (FBXHeaders::createFBXHeaders(m_fileVersion, filenameUtf8, m_p_manager, exporter,
103 m_p_scene) == false)
104 return false;
105
106 // add some info to exported scene
107 FbxDocumentInfo* sceneInfo = FbxDocumentInfo::Create(m_p_manager, "SceneInfo");
108 {
109 std::string modelName = m_p_model->name();
110 auto sp = modelName.rfind('/');
111 if (sp != std::string::npos)
112 modelName = modelName.substr(sp + 1);
113 sceneInfo->mTitle = modelName.c_str();
114 }
115 {
116 std::wstring appName = GLOBALSETTINGS.appName();
117 if (!appName.empty())
118 {
119 int n = WideCharToMultiByte(CP_UTF8, 0, appName.c_str(), static_cast<int>(appName.size()), nullptr, 0, nullptr, nullptr);
120 std::string s(n, '\0');
121 WideCharToMultiByte(CP_UTF8, 0, appName.c_str(), static_cast<int>(appName.size()), s.data(), n, nullptr, nullptr);
122 sceneInfo->mAuthor = s.c_str();
123 }
124 }
125 {
126 std::wstring appVer = GLOBALSETTINGS.appVersion();
127 if (!appVer.empty())
128 {
129 int n = WideCharToMultiByte(CP_UTF8, 0, appVer.c_str(), static_cast<int>(appVer.size()), nullptr, 0, nullptr, nullptr);
130 std::string s(n, '\0');
131 WideCharToMultiByte(CP_UTF8, 0, appVer.c_str(), static_cast<int>(appVer.size()), s.data(), n, nullptr, nullptr);
132 sceneInfo->mRevision = s.c_str();
133 }
134 }
135 m_p_scene->SetSceneInfo(sceneInfo);
136
137 // export main model mesh
138 // follow FBX SDK example (ExportScene01) for organization
139 try
140 {
141 createMeshes();
142 LOG_INFO << "Meshes successfully created";
143
145 LOG_INFO << "Materials successfully created";
146
148 LOG_INFO << "Skeleton successfully created";
149
151
153 //FBXHeaders::storeRestPose(m_p_scene, m_p_skeletonNode);
154
155 for (const auto it : *m_p_model)
156 {
157 std::map<POSITION_SLOTS, WoWModel*> itemModels = it->models();
158 if (!itemModels.empty())
159 {
160 for (const auto& itemModel : itemModels)
161 {
162 if (m_attachBoneClusters.find(itemModel.first) != m_attachBoneClusters.end() && m_attachMeshNodes.
163 find(itemModel.first) != m_attachMeshNodes.end())
165 m_attachMeshNodes[itemModel.first]);
166 //if (m_attachSkeletonNode.find(it->first) != m_attachSkeletonNode.end())
167 //FBXHeaders::storeRestPose(m_p_scene, m_attachSkeletonNode[it->first]);
168 }
169 }
170 }
171 }
172 catch (const std::exception& ex)
173 {
174 LOG_ERROR << "Error during export:" << ex.what();
175 return false;
176 }
177
178 if (!exporter->Export(m_p_scene))
179 {
180 LOG_ERROR << "Unable to export FBX scene";
181 return false;
182 }
183 LOG_INFO << "Model successfully created";
184
185 // delete texture files created during export
186 for (auto it : m_texturesToExport)
187 _wremove((it.first).c_str());
188
189 // Export bulk animations
190 if (m_animsToExport.size() > 0)
191 {
192 try
193 {
195 LOG_INFO << "Animations successfully created";
196 }
197 catch (const std::exception& ex)
198 {
199 LOG_ERROR << "Error during bulk animation export:" << ex.what();
200 return false;
201 }
202 }
203
204 LOG_INFO << "FBX scene successfully exported";
205 return true;
206}
207
209{
211
212 FbxNode* root_node = m_p_scene->GetRootNode();
213 root_node->AddChild(m_p_meshNode);
214
215 for (const auto it : *m_p_model)
216 {
217 std::map<POSITION_SLOTS, WoWModel*> itemModels = it->models();
218 if (!itemModels.empty())
219 {
220 for (const auto& It : itemModels)
221 {
222 WoWModel* itemModel = It.second;
223 LOG_INFO << "Found attached item:" << itemModel->modelname.c_str();
224
225 const int l = m_p_model->attLookup[It.first];
226 glm::mat4 m;
227 if (l > -1)
228 {
229 m = m_p_model->bones[m_p_model->atts[l].bone].mat;
230 }
231
232 FbxNode* itemMeshNode = FBXHeaders::createMesh(m_p_manager, m_p_scene, itemModel, m);
233 m_attachMeshNodes[It.first] = itemMeshNode;
234
235 root_node->AddChild(itemMeshNode);
236 }
237 }
238 }
239}
240
242{
244
245 FbxNode* root_node = m_p_scene->GetRootNode();
246 root_node->AddChild(m_p_skeletonNode);
247
248 for (const auto it : *m_p_model)
249 {
250 std::map<POSITION_SLOTS, WoWModel*> itemModels = it->models();
251 if (!itemModels.empty())
252 {
253 for (const auto& It : itemModels)
254 {
255 WoWModel* itemModel = It.second;
256 if (itemModel->animated == false || itemModel->bones.size() < 2)
257 continue;
258
259 FbxNode* itemSkeleton;
260 std::map<int, FbxNode*> itemboneNodes;
261
262 FBXHeaders::createSkeleton(itemModel, m_p_scene, itemSkeleton, itemboneNodes);
263
264 m_attachSkeletonNode[It.first] = itemSkeleton;
265 m_attachBoneNodes[It.first] = itemboneNodes;
266 root_node->AddChild(itemSkeleton);
267 }
268 }
269 }
270}
271
273{
274 // create clusters
275 for (const auto it : m_boneNodes)
276 {
277 FbxCluster* cluster = FbxCluster::Create(m_p_scene, "");
278 m_boneClusters.push_back(cluster);
279 cluster->SetLink(it.second);
280 cluster->SetLinkMode(FbxCluster::ELinkMode::eNormalize);
281 }
282
283 // define control points
284 int i = 0;
285 for (const auto& it : m_p_model->origVertices)
286 {
287 for (size_t j = 0; j < 4; j++)
288 {
289 if (it.weights[j] > 0)
290 m_boneClusters[it.bones[j]]->AddControlPointIndex((int)i, static_cast<double>(it.weights[j]) / 255.0);
291 }
292 i++;
293 }
294
295 // set initial matrices
296 FbxAMatrix matrix = m_p_meshNode->EvaluateGlobalTransform();
297 for (const auto it : m_boneClusters)
298 {
299 it->SetTransformMatrix(matrix);
300 }
301
302 // set link matrices
303 std::vector<FbxCluster*>::iterator clusterIt = m_boneClusters.begin();
304 for (const auto it : m_boneNodes)
305 {
306 matrix = it.second->EvaluateGlobalTransform();
307 (*clusterIt)->SetTransformLinkMatrix(matrix);
308 ++clusterIt;
309 }
310
311 // add cluster to skin
312 FbxGeometry* lMeshAttribute = static_cast<FbxGeometry*>(m_p_meshNode->GetNodeAttribute());
313 FbxSkin* skin = FbxSkin::Create(m_p_scene, "");
314
315 for (const auto it : m_boneClusters)
316 skin->AddCluster(it);
317
318 lMeshAttribute->AddDeformer(skin);
319}
320
322{
323 if (m_boneNodes.empty())
324 {
325 LOG_ERROR << "No bone in skeleton, so no animation will be exported";
326 return;
327 }
328
329 LOG_INFO << "Num animations to export:" << m_animsToExport.size();
330
332 {
333 LOG_ERROR << "An error occured while exporting Animation files.";
334 return;
335 }
336}
337
338// Creates separate FBX files for each animation, in a folder with the original FBX file's name.
340{
341 //int maxThreads = QThread::idealThreadCount();
342 // Get the ideal number of threads we can run at once. This usually equals the total number of threads in a CPU.
343 std::map<int, std::wstring> animsMap = m_p_model->getAnimsMap();
344
345 /**** NOTICE! ****//*
346
347 While the QThreadPool code DOES start the FBX animation exporting in threads, the FBX SDK itself is not thread-safe!
348
349 We will have to develop our own threaded wrapper for FBX, or copy the FBX data, or find some other method to make the SDK work in a thread-safe manner before we can re-enable multi-threaded animation exporting.
350 Without doing this, WMV will crash while exporting animations, as the data becomes cross-contaminated, and thus, invalidated.
351
352 */ /**** ****/
353
354 // If we allow users to set the number of threads WMV can use, we can limit it here. I would recommend using no more than 3/4ths of the total thread count! (Gotta leave some for normal CPU usage...)
355 //int allocatedThreads = 5;
356 //if (maxThreads > allocatedThreads)
357 // maxThreads = allocatedThreads;
358
359 //LOG_INFO << "Exporting animations with" << maxThreads << "threads...";
360 //QThreadPool::globalInstance()->setMaxThreadCount(maxThreads); // Use to limit the number of threads we can run at once.
361
362 for (const auto it : m_animsToExport)
363 {
364 std::lock_guard<std::mutex> locker(m_mutex);
365 const ModelAnimation curAnimation = m_p_model->anims[it];
366 FBXAnimExporter* exporter = new FBXAnimExporter();
367 std::string fnUtf8, anUtf8;
368 if (!m_filename.empty())
369 {
370 int n = WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), nullptr, 0, nullptr, nullptr);
371 fnUtf8.resize(n);
372 WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), fnUtf8.data(), n, nullptr, nullptr);
373 }
374 {
375 const std::wstring& wAnim = animsMap[curAnimation.animID];
376 if (!wAnim.empty())
377 {
378 int n = WideCharToMultiByte(CP_UTF8, 0, wAnim.c_str(), static_cast<int>(wAnim.size()), nullptr, 0, nullptr, nullptr);
379 anUtf8.resize(n);
380 WideCharToMultiByte(CP_UTF8, 0, wAnim.c_str(), static_cast<int>(wAnim.size()), anUtf8.data(), n, nullptr, nullptr);
381 }
382 }
383 exporter->setValues(m_fileVersion, fnUtf8, anUtf8, m_p_model, m_boneClusters,
384 m_p_meshNode, it);
385 exporter->run();
386 delete exporter;
387 }
388
389 //QThreadPool::globalInstance()->waitForDone(); // Don't finish until all the threads have been processed.
390 return true;
391}
392
393// Create materials.
395{
396 for (unsigned int i = 0; i < m_p_model->passes.size(); i++)
397 {
398 ModelRenderPass* pass = m_p_model->passes[i];
399 if (pass->init())
400 {
401 // Build material name.
402 std::string mn = m_p_model->name();
403 auto sp = mn.rfind('/');
404 if (sp != std::string::npos)
405 mn = mn.substr(sp + 1);
406 FbxString mtrl_name = mn.c_str();
407 mtrl_name.Append("_", 1);
408 char tmp[32];
409 _itoa(static_cast<int>(i), tmp, 10);
410 mtrl_name.Append(tmp, strlen(tmp));
411
412 // Create material.
413 FbxString shading_name = "Phong";
414 FbxSurfacePhong* material = FbxSurfacePhong::Create(m_p_manager, mtrl_name.Buffer());
415 material->Ambient.Set(FbxDouble3(0.7, 0.7, 0.7));
416
417 std::string tex = m_p_model->getNameForTex(pass->tex);
418
419 std::string tex_name = tex;
420 auto slashPos = tex_name.rfind('/');
421 if (slashPos != std::string::npos)
422 tex_name = tex_name.substr(slashPos + 1);
423 if (auto blpPos = tex_name.find(".blp"); blpPos != std::string::npos)
424 tex_name.replace(blpPos, 4, ".png");
425
426 std::string filenameNarrow;
427 if (!m_filename.empty())
428 {
429 int n = WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), nullptr, 0, nullptr, nullptr);
430 filenameNarrow.resize(n);
431 WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), filenameNarrow.data(), n, nullptr, nullptr);
432 }
433 std::string tex_fullpath = filenameNarrow;
434 auto bsPos = tex_fullpath.rfind('\\');
435 if (bsPos != std::string::npos)
436 tex_fullpath = tex_fullpath.substr(0, bsPos + 1) + tex_name;
437
438 m_texturesToExport[std::wstring(tex_fullpath.begin(), tex_fullpath.end())] = m_p_model->getGLTexture(pass->tex);
439
440 FbxFileTexture* texture = FbxFileTexture::Create(m_p_manager, tex_name.c_str());
441 texture->SetFileName(tex_fullpath.c_str());
442 texture->SetTextureUse(FbxTexture::eStandard);
443 texture->SetMappingType(FbxTexture::eUV);
444 texture->SetMaterialUse(FbxFileTexture::eModelMaterial);
445 texture->SetSwapUV(false);
446 texture->SetTranslation(0.0, 0.0);
447 texture->SetScale(1.0, 1.0);
448 texture->SetRotation(0.0, 0.0);
449 texture->UVSet.Set(FbxString("DiffuseUV"));
450 material->Diffuse.ConnectSrcObject(texture);
451
452 // Add material to the scene.
453 m_p_meshNode->AddMaterial(material);
454 }
455 }
456
457 for (const auto it : *m_p_model)
458 {
459 std::map<POSITION_SLOTS, WoWModel*> itemModels = it->models();
460 if (!itemModels.empty())
461 {
462 for (const auto& itemModel : itemModels)
463 {
464 WoWModel* model = itemModel.second;
465 for (unsigned int i = 0; i < model->passes.size(); i++)
466 {
467 ModelRenderPass* pass = model->passes[i];
468 if (pass->init())
469 {
470 // Build material name.
471 std::string mn2 = model->name();
472 auto sp2 = mn2.rfind('/');
473 if (sp2 != std::string::npos)
474 mn2 = mn2.substr(sp2 + 1);
475 FbxString mtrl_name = mn2.c_str();
476 mtrl_name.Append("_", 1);
477 char tmp[32];
478 _itoa(static_cast<int>(i), tmp, 10);
479 mtrl_name.Append(tmp, strlen(tmp));
480
481 // Create material.
482 FbxString shading_name = "Phong";
483 FbxSurfacePhong* material = FbxSurfacePhong::Create(m_p_manager, mtrl_name.Buffer());
484 material->Ambient.Set(FbxDouble3(0.7, 0.7, 0.7));
485
486 std::string tex = model->getNameForTex(pass->tex);
487
488 std::string tex_name = tex;
489 auto slashPos2 = tex_name.rfind('/');
490 if (slashPos2 != std::string::npos)
491 tex_name = tex_name.substr(slashPos2 + 1);
492 if (auto blpPos = tex_name.find(".blp"); blpPos != std::string::npos)
493 tex_name.replace(blpPos, 4, ".png");
494
495 std::string filenameNarrow2;
496 if (!m_filename.empty())
497 {
498 int n = WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), nullptr, 0, nullptr, nullptr);
499 filenameNarrow2.resize(n);
500 WideCharToMultiByte(CP_UTF8, 0, m_filename.c_str(), static_cast<int>(m_filename.size()), filenameNarrow2.data(), n, nullptr, nullptr);
501 }
502 std::string tex_fullpath2 = filenameNarrow2;
503 auto bsPos2 = tex_fullpath2.rfind('\\');
504 if (bsPos2 != std::string::npos)
505 tex_fullpath2 = tex_fullpath2.substr(0, bsPos2 + 1) + tex_name;
506
507 m_texturesToExport[std::wstring(tex_fullpath2.begin(), tex_fullpath2.end())] = model->getGLTexture(pass->tex);
508
509 FbxFileTexture* texture = FbxFileTexture::Create(m_p_manager, tex_name.c_str());
510 texture->SetFileName(tex_fullpath2.c_str());
511 texture->SetTextureUse(FbxTexture::eStandard);
512 texture->SetMappingType(FbxTexture::eUV);
513 texture->SetMaterialUse(FbxFileTexture::eModelMaterial);
514 texture->SetSwapUV(false);
515 texture->SetTranslation(0.0, 0.0);
516 texture->SetScale(1.0, 1.0);
517 texture->SetRotation(0.0, 0.0);
518 texture->UVSet.Set(FbxString("DiffuseUV"));
519 material->Diffuse.ConnectSrcObject(texture);
520
521 // Add material to the scene.
522 m_attachMeshNodes[itemModel.first]->AddMaterial(material);
523 }
524 }
525 }
526 }
527 }
528
529 for (const auto it : m_texturesToExport)
530 exportGLTexture(it.second, it.first);
531}
532
534{
535 if (m_p_manager)
536 m_p_manager->Destroy();
537
538 m_p_manager = nullptr;
539
540 // scene is destroyed by manager's destroy call
541 m_p_scene = nullptr;
542
543 m_p_model = nullptr;
544 m_p_meshNode = nullptr;
545 m_p_skeletonNode = nullptr;
546
547 m_filename = L"";
548
549 m_boneNodes.clear();
550 m_texturesToExport.clear();
551 m_boneClusters.clear();
552}
#define GLOBALSETTINGS
#define LOG_ERROR
Definition Logger.h:11
#define LOG_INFO
Definition Logger.h:10
std::string name() const
Definition Component.cpp:52
void exportGLTexture(GLuint id, std::wstring filename) const
std::vector< int > m_animsToExport
Exports individual animation sequences from a WoW model into separate FBX files.
void run()
Execute the animation export.
void setValues(FbxString fileVersion, std::string fn, std::string an, WoWModel *m, std::vector< FbxCluster * > bc, FbxNode *&meshnode, int aID, bool uan=false)
Configure the exporter with source model, animation, and output settings.
FbxNode * m_p_skeletonNode
Definition FBXExporter.h:68
bool exportModel(Model *, std::wstring file)
FbxString m_fileVersion
Definition FBXExporter.h:73
std::wstring fileSaveFilter() const
void createAnimations()
void createMeshes()
WoWModel * m_p_model
Definition FBXExporter.h:66
std::map< int, FbxNode * > m_boneNodes
Definition FBXExporter.h:75
std::map< std::wstring, GLuint > m_texturesToExport
Definition FBXExporter.h:83
FbxNode * m_p_meshNode
Definition FBXExporter.h:67
std::wstring fileSaveTitle() const
std::map< int, std::vector< FbxCluster * > > m_attachBoneClusters
Definition FBXExporter.h:81
void linkMeshAndSkeleton()
std::wstring m_filename
Definition FBXExporter.h:74
std::mutex m_mutex
Definition FBXExporter.h:71
std::wstring menuLabel() const
FbxScene * m_p_scene
Definition FBXExporter.h:65
void createSkeletons()
std::map< int, FbxNode * > m_attachSkeletonNode
Definition FBXExporter.h:78
FbxManager * m_p_manager
Definition FBXExporter.h:64
std::vector< FbxCluster * > m_boneClusters
Definition FBXExporter.h:76
std::map< int, std::map< int, FbxNode * > > m_attachBoneNodes
Definition FBXExporter.h:80
bool createAnimationFiles()
std::map< int, FbxNode * > m_attachMeshNodes
Definition FBXExporter.h:79
void createMaterials()
Represents a single render pass (material + geometry) for an M2 model geoset.
uint16 tex
Texture index.
bool init()
Initialise render state from the model's material data.
Abstract base interface for all 3D model types.
Definition Model.h:5
Core WoW .m2 model: geometry, animation, textures, and character data.
Definition WoWModel.h:50
std::vector< ModelAnimation > anims
Definition WoWModel.h:178
GLuint getGLTexture(uint16 tex) const
int16 attLookup[ATT_MAX]
Definition WoWModel.h:209
std::map< int, std::wstring > getAnimsMap()
bool animated
Definition WoWModel.h:172
std::vector< Bone > bones
Definition WoWModel.h:181
std::string modelname
Definition WoWModel.h:145
std::vector< ModelRenderPass * > passes
Definition WoWModel.h:148
std::string getNameForTex(uint16 tex)
std::vector< ModelAttachment > atts
Definition WoWModel.h:207
std::vector< ModelVertex > origVertices
Definition WoWModel.h:132
FbxNode * createMesh(FbxManager *&l_manager, FbxScene *&l_scene, WoWModel *model, const glm::mat4 &matrix=glm::mat4(1.0f), const glm::vec3 &offset=glm::vec3(0.0f))
void createSkeleton(WoWModel *l_model, FbxScene *&l_scene, FbxNode *&l_skeletonNode, std::map< int, FbxNode * > &l_boneNodes)
void storeBindPose(FbxScene *&l_scene, std::vector< FbxCluster * > l_boneClusters, FbxNode *l_meshNode)
bool createFBXHeaders(FbxString fileVersion, std::string l_FileName, FbxManager *&l_Manager, FbxExporter *&l_Exporter, FbxScene *&l_Scene)
An animation sequence entry in the M2 model (block B).