FBX.cpp 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262
  1. /******************************************************************************
  2. Node is a bone when:
  3. -node type is FbxNodeAttribute::eSkeleton
  4. -node has any skinning (vertexes attached to the node)
  5. -node has animation keyframes
  6. /******************************************************************************/
  7. #ifndef EE_PRIVATE
  8. #define EE_PRIVATE 1
  9. #endif
  10. #include "../../Engine/H/EsenthelEngine.h"
  11. #include "../begin.h"
  12. //#define FBXSDK_SHARED
  13. #include <fbxsdk.h>
  14. #include "../end.h"
  15. #define KEY_SAFETY 0 // not needed since time's are represented using integer values, and they will always be filled out
  16. /******************************************************************************/
  17. namespace EE{
  18. static SyncLock Lock; // FBX SDK is not thread-safe and crashes upon importing multiple files at the same time
  19. /******************************************************************************/
  20. struct FBX
  21. {
  22. static Color COLOR (C FbxColor &c) {return Color(Vec4(c.mRed, c.mGreen, c.mBlue, c.mAlpha));}
  23. static Vec2 VEC (C FbxVector2 &v) {return Vec2(v[0], v[1] );}
  24. static Vec4 VEC (C FbxVector4 &v) {return Vec4(v[0], v[1], v[2], v[3]);}
  25. static MatrixD MATRIX(C FbxAMatrix &f)
  26. {
  27. MatrixD m;
  28. m.x .set(f.Get(0, 0), f.Get(0, 1), f.Get(0, 2));
  29. m.y .set(f.Get(1, 0), f.Get(1, 1), f.Get(1, 2));
  30. m.z .set(f.Get(2, 0), f.Get(2, 1), f.Get(2, 2));
  31. m.pos.set(f.Get(3, 0), f.Get(3, 1), f.Get(3, 2));
  32. #if DEBUG
  33. if(f.Get(0, 3)!=0 || f.Get(1, 3)!=0 || f.Get(2, 3)!=0 || f.Get(3, 3)!=1)Exit("FBX Matrix");
  34. #endif
  35. return m;
  36. }
  37. static Int CompareTime(C FbxTime &a, C FbxTime &b) {if(a<b)return -1; if(a>b)return +1; return 0;}
  38. struct Node
  39. {
  40. Node *parent;
  41. Memc<Node*> children;
  42. FbxNode *node;
  43. Bool bone, mesh, has_skin;
  44. Str full_name, ee_name;
  45. MatrixD local, global;
  46. Int bone_index, // points to the bone index in the skeleton
  47. nearest_bone_index; // this points to the nearest bone index in the skeleton (for example, if this node doesn't have a corresponding bone in the skeleton, then it will point to the bone index of one of its parents)
  48. Node()
  49. {
  50. bone =mesh=has_skin=false;
  51. parent=null;
  52. node =null;
  53. local .identity();
  54. global.identity();
  55. bone_index=nearest_bone_index=-1;
  56. }
  57. Bool hasAnimFast(FbxAnimLayer *anim_layer)C
  58. {
  59. if(FbxAnimCurve *curve=node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  60. if(FbxAnimCurve *curve=node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  61. if(FbxAnimCurve *curve=node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  62. if(FbxAnimCurve *curve=node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  63. if(FbxAnimCurve *curve=node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  64. if(FbxAnimCurve *curve=node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  65. if(FbxAnimCurve *curve=node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  66. if(FbxAnimCurve *curve=node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  67. if(FbxAnimCurve *curve=node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z)){Flt v=curve->EvaluateIndex(0); REP(curve->KeyGetCount())if(!Equal(curve->EvaluateIndex(i), v))return true;}
  68. return false;
  69. }
  70. Bool hasAnimFast()C // !! assumes that 'SetCurrentAnimationStack' was already called !!
  71. {
  72. return !Equal(local, MATRIX(node->EvaluateLocalTransform(0)), (Dbl)EPS_ANIM_ANGLE, EPSD); // use 'EPSD' instead of 'EPS_ANIM_POS' because mesh can be scaled
  73. }
  74. Bool hasAnim(FbxAnimLayer *anim_layer)C // !! assumes that 'SetCurrentAnimationStack' was already called !!
  75. {
  76. return hasAnimFast(anim_layer) || hasAnimFast();
  77. }
  78. Bool hasAnim(FBX &fbx)C
  79. {
  80. REP(fbx.scene->GetSrcObjectCount<FbxAnimStack>())
  81. if(FbxAnimStack *anim_stack=fbx.scene->GetSrcObject<FbxAnimStack>(i))
  82. {
  83. // first do a fast check if any of the keyframes are different
  84. Int anim_layers=anim_stack->GetMemberCount<FbxAnimLayer>();
  85. REPD(layer, anim_layers)if(FbxAnimLayer *anim_layer=anim_stack->GetMember<FbxAnimLayer>(layer))if(hasAnimFast(anim_layer))return true;
  86. // now check if the first keyframe is not as local transform
  87. fbx.scene->SetCurrentAnimationStack(anim_stack);
  88. if(hasAnimFast())return true;
  89. }
  90. return false;
  91. }
  92. /*Bool setNodesAsBoneIfAnimAffectsBoneOrMesh(FBX &fbx)
  93. {
  94. Bool child_is_bone_or_mesh=false; FREPA(children)child_is_bone_or_mesh|=children[i]->setNodesAsBoneIfAnimAffectsBoneOrMesh(fbx);
  95. child_is_bone_or_mesh|=mesh; // include from self
  96. if(!bone && child_is_bone_or_mesh && hasAnim(fbx))bone=true;
  97. return child_is_bone_or_mesh || bone;
  98. }*/
  99. void adjustBoneIndex(Int bone_index)
  100. {
  101. if(bone_index>=0xFF)bone_index=-1;
  102. T.bone_index=bone_index;
  103. if(bone_index<0)bone=false; // if we're clearing bone index, then it means we don't want this as a bone
  104. }
  105. };
  106. FbxManager *manager;
  107. FbxScene *scene;
  108. FbxImporter *importer;
  109. FbxGlobalSettings *global_settings;
  110. Int sdk_major, sdk_minor, sdk_revision;
  111. Memc<Node> nodes;
  112. Memc<FbxSurfaceMaterial*> ee_mtrl_to_fbx_mtrl; // this will point from EE Material 'materials' container index to FBX Material pointer
  113. Flt scale;
  114. Str app_name;
  115. Int findNodeI(FbxNode *node)C {REPA(nodes)if(nodes[i].node==node)return i; return -1;}
  116. Node* findNode (FbxNode *node) {return nodes.addr(findNodeI(node));}
  117. Int findMaterial(FbxSurfaceMaterial *mtrl)C {return ee_mtrl_to_fbx_mtrl.find(mtrl);}
  118. ~FBX()
  119. {
  120. SyncLocker locker(Lock);
  121. if(importer){importer->Destroy(); importer=null;}
  122. if(scene ){scene ->Destroy(); scene =null;}
  123. if(manager ){manager ->Destroy(); manager =null;}
  124. }
  125. FBX()
  126. {
  127. //SyncLocker locker(Lock); not needed because we don't use FBX SDK here
  128. manager =null;
  129. scene =null;
  130. importer =null;
  131. global_settings=null;
  132. scale =1.0f;
  133. sdk_major=sdk_minor=sdk_revision=0;
  134. }
  135. Bool init()
  136. {
  137. SyncLocker locker(Lock);
  138. FbxManager::GetFileFormatVersion(sdk_major, sdk_minor, sdk_revision); // get versions
  139. if(!manager )manager =FbxManager ::Create( ); if(!manager )return false;
  140. if(!scene )scene =FbxScene ::Create(manager, ""); if(!scene )return false;
  141. if(!importer)importer=FbxImporter::Create(manager, ""); if(!importer)return false;
  142. return true;
  143. }
  144. Bool load(C Str &name, Bool all_nodes_as_bones, C Str8 &password)
  145. {
  146. SyncLocker locker(Lock);
  147. if(importer)
  148. {
  149. Bool status=importer->Initialize((WINDOWS ? UTF8 : UnixPathUTF8)(name), -1, manager->GetIOSettings());
  150. Int file_major, file_minor, file_revision; importer->GetFileVersion(file_major, file_minor, file_revision);
  151. if(status)
  152. {
  153. status=importer->Import(scene);
  154. if(!status && importer->GetStatus()==FbxStatus::ePasswordError && password.is())
  155. {
  156. if(FbxIOSettings *io_settings=importer->GetIOSettings())
  157. {
  158. io_settings->SetStringProp(IMP_FBX_PASSWORD , password());
  159. io_settings->SetBoolProp (IMP_FBX_PASSWORD_ENABLE, true );
  160. status=importer->Import(scene);
  161. if(!status && importer->GetStatus()==FbxStatus::ePasswordError){} // wrong password
  162. }
  163. }
  164. if(status)
  165. {
  166. global_settings=&scene->GetGlobalSettings(); if(!global_settings)return false;
  167. if(FbxDocumentInfo *doc_info=scene->GetDocumentInfo())
  168. {
  169. app_name=doc_info->LastSaved_ApplicationName.Get();
  170. }
  171. // Convert Axis System
  172. FbxAxisSystem scene_axis_system=global_settings->GetAxisSystem(),
  173. //desired_axis_system(FbxAxisSystem::eYAxis, FbxAxisSystem::eParityOdd, FbxAxisSystem:: eLeftHanded); should be this (the same as FbxAxisSystem::eDirectX), however it's broken (Y axis gets flipped somehow)
  174. desired_axis_system(FbxAxisSystem::eYAxis, FbxAxisSystem::eParityOdd, FbxAxisSystem::eRightHanded); // instead use 'eRightHanded' and call 'mirrorX'
  175. if(scene_axis_system!=desired_axis_system)desired_axis_system.ConvertScene(scene);
  176. // Convert Unit System
  177. FbxSystemUnit scene_system_unit=global_settings->GetSystemUnit(),
  178. desired_system_unit=FbxSystemUnit::m; // meters
  179. if(scene_system_unit!=desired_system_unit)
  180. #if 0 // broken in latest SDK
  181. desired_system_unit.ConvertScene(scene);
  182. #else
  183. scale=scene_system_unit.GetScaleFactor()/desired_system_unit.GetScaleFactor();
  184. #endif
  185. // set nodes
  186. nodes.setNum(scene->GetNodeCount()); // create all nodes up-front !! this is important because we're using 'Memc' !!
  187. REPAO(nodes).node=scene->GetNode(i); // set node link
  188. REPA (nodes)
  189. {
  190. Node &node=nodes[i];
  191. node.ee_name=node.full_name=FromUTF8(node.node->GetName());
  192. // type
  193. if(FbxNodeAttribute *attrib=node.node->GetNodeAttribute())
  194. switch(attrib->GetAttributeType())
  195. {
  196. case FbxNodeAttribute::eNull : node.bone=all_nodes_as_bones; break;
  197. case FbxNodeAttribute::eSkeleton: node.bone= true; break;
  198. case FbxNodeAttribute::eMesh : node.mesh= true; break;
  199. }
  200. // matrix
  201. node.local =MATRIX(node.node->EvaluateLocalTransform ());
  202. node.global=MATRIX(node.node->EvaluateGlobalTransform());
  203. // link with parent
  204. if(node.parent=findNode(node.node->GetParent()))node.parent->children.add(&node);
  205. }
  206. return true;
  207. }
  208. }
  209. }
  210. return false;
  211. }
  212. void ignoreBones(C MemPtr<Str> &bone_names)
  213. {
  214. REPA(nodes)
  215. {
  216. Node &node=nodes[i]; if(node.bone)
  217. {
  218. REPA(bone_names)if(node.full_name==bone_names[i])
  219. {
  220. node.bone=false;
  221. break;
  222. }
  223. }
  224. }
  225. }
  226. void makeBoneNamesUnique()
  227. {
  228. FREPA(nodes)
  229. {
  230. Node &node=nodes[i]; if(node.bone)
  231. {
  232. if(!node.ee_name.is())node.ee_name="Root"; // we don't allow empty names because 'findBone' methods may skip searching if the name parameter is empty
  233. again:
  234. REPD(j, i)
  235. {
  236. C Node &test=nodes[j]; if(test.bone && node.ee_name==test.ee_name)
  237. {
  238. Int index=1;
  239. Char separator='@';
  240. REPA(node.ee_name)if(node.ee_name[i]==separator) // find if this name already has a separator
  241. {index=TextInt(node.ee_name()+i+1)+1; node.ee_name.clip(i); break;} // get the index value, increase by one, and remove the separator
  242. node.ee_name+=separator;
  243. node.ee_name+=index;
  244. goto again;
  245. }
  246. }
  247. }
  248. }
  249. }
  250. void shortenBoneNames(C MemPtr<Str> &start)
  251. {
  252. REPA(nodes)
  253. {
  254. Node &node=nodes[i]; if(node.bone)
  255. {
  256. Str &name=node.ee_name;
  257. REPA(start)name=SkipStart(name, start[i]);
  258. }
  259. }
  260. }
  261. void shortenBoneNames()
  262. {
  263. Int max_name_length=MEMBER_ELMS(SkelBone, name)-1, name_length=0;
  264. REPA(nodes)
  265. {
  266. C Node &node=nodes[i]; if(node.bone)MAX(name_length, node.ee_name.length());
  267. }
  268. if(name_length>max_name_length) // if name length exceeds allowed limit, then shorten names by removing the shared start
  269. {
  270. REPA(nodes)
  271. {
  272. C Node &node_i=nodes[i]; if(node_i.bone)
  273. {
  274. C Str &name_i=node_i.ee_name;
  275. REPD(j, i)
  276. {
  277. C Node &node_j=nodes[j]; if(node_j.bone)
  278. {
  279. C Str &name_j=node_j.ee_name;
  280. FREPA(name_i)if(name_i[i]!=name_j[i]){MIN(name_length, i); break;}
  281. }
  282. }
  283. }
  284. }
  285. REPA(nodes)
  286. {
  287. Str &name=nodes[i].ee_name;
  288. if(name_length)name.remove(0, name_length+(name[name_length]==' ')); // if this name has a space after shared start, then remove that space too
  289. // even after removing shared start, bone name can still be too long
  290. if(name.length()>max_name_length)name.clip(max_name_length-1-2); // leave room for 1 char separator + 2 char index
  291. }
  292. makeBoneNamesUnique(); // we've changed names, so we need to make sure that they are unique
  293. }
  294. }
  295. void setNodesAsBoneFromSkin()
  296. {
  297. FREPA(nodes)
  298. if(nodes[i].mesh)
  299. if(FbxMesh *mesh=nodes[i].node->GetMesh()) // access fbx mesh
  300. {
  301. Int polys=mesh->GetPolygonCount();
  302. if( polys>0)
  303. REPD(s, mesh->GetDeformerCount(FbxDeformer::eSkin))if(FbxSkin *skin =(FbxSkin*)mesh->GetDeformer(s, FbxDeformer::eSkin))
  304. REPD(b, skin-> GetClusterCount( ))if(FbxCluster *cluster=skin->GetCluster(b))
  305. {
  306. Int index_count=cluster->GetControlPointIndicesCount();
  307. if( index_count>0)
  308. if(Node *bone=findNode(cluster->GetLink()))
  309. {
  310. bone->bone =true;
  311. bone->has_skin=true;
  312. }
  313. }
  314. }
  315. }
  316. /*void setNodeHasAnim()
  317. {
  318. FREP(scene->GetSrcObjectCount<FbxAnimStack>())
  319. if(FbxAnimStack *anim_stack=scene->GetSrcObject<FbxAnimStack>(i))
  320. {
  321. Int anim_layers=anim_stack->GetMemberCount<FbxAnimLayer>();
  322. REPD(layer, anim_layers)
  323. if(FbxAnimLayer *anim_layer=anim_stack->GetMember<FbxAnimLayer>(layer))
  324. {
  325. FREPAD(n, nodes)
  326. {
  327. Node &node=nodes[n];
  328. if( !node.has_anim)
  329. {
  330. Bool has_pos=false;
  331. if(FbxAnimCurve *curve=node.node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X))if(curve->KeyGetCount())has_pos=true;
  332. if(FbxAnimCurve *curve=node.node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y))if(curve->KeyGetCount())has_pos=true;
  333. if(FbxAnimCurve *curve=node.node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z))if(curve->KeyGetCount())has_pos=true;
  334. Bool has_rot=false;
  335. if(FbxAnimCurve *curve=node.node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X))if(curve->KeyGetCount())has_rot=true;
  336. if(FbxAnimCurve *curve=node.node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y))if(curve->KeyGetCount())has_rot=true;
  337. if(FbxAnimCurve *curve=node.node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z))if(curve->KeyGetCount())has_rot=true;
  338. Bool has_scale=false;
  339. if(FbxAnimCurve *curve=node.node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X))if(curve->KeyGetCount())has_scale=true;
  340. if(FbxAnimCurve *curve=node.node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y))if(curve->KeyGetCount())has_scale=true;
  341. if(FbxAnimCurve *curve=node.node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z))if(curve->KeyGetCount())has_scale=true;
  342. if(has_pos || has_rot || has_scale)node.has_anim=true;
  343. }
  344. }
  345. }
  346. }
  347. }*/
  348. void setNodesAsBoneFromAnim()
  349. {
  350. FREPA(nodes)
  351. {
  352. Node &node=nodes[i];
  353. if(!node.bone && node.hasAnim(T))node.bone=true;
  354. }
  355. scene->SetCurrentAnimationStack(null); // reset context which may have been changed in 'hasAnim'
  356. }
  357. static Str TextureFile(C FbxTexture &texture)
  358. {
  359. if(C FbxFileTexture *file_texture=FbxCast<FbxFileTexture>(&texture))
  360. {
  361. Str file=FromUTF8(file_texture->GetFileName());
  362. if( file.is())return file;
  363. }else
  364. if(C FbxLayeredTexture *layered_texture=FbxCast<FbxLayeredTexture>(&texture))
  365. {
  366. Str file=TextureFile(SCAST(C FbxObject, *layered_texture));
  367. if( file.is())return file;
  368. }
  369. return S;
  370. }
  371. static Str TextureFile(C FbxObject &obj)
  372. {
  373. Int textures=obj.GetSrcObjectCount<FbxTexture>();
  374. FREP(textures)
  375. if(FbxTexture *texture=obj.GetSrcObject<FbxTexture>(i))
  376. {
  377. Str file=TextureFile(*texture);
  378. if( file.is())return file;
  379. }
  380. return S;
  381. }
  382. static Str TextureFile(C FbxProperty &prop)
  383. {
  384. Int textures=prop.GetSrcObjectCount<FbxTexture>();
  385. FREP(textures)
  386. if(FbxTexture *texture=prop.GetSrcObject<FbxTexture>(i))
  387. {
  388. Str file=TextureFile(*texture);
  389. if( file.is())return file;
  390. }
  391. return S;
  392. }
  393. static Str TextureFile(C FbxSurfaceMaterial &mtrl, char const *type)
  394. {
  395. return TextureFile(mtrl.FindProperty(type));
  396. }
  397. void set(MemPtr<XMaterial> materials)
  398. {
  399. if(materials)
  400. {
  401. Bool flip_normal_y=Contains(app_name, "Maya", false, true);
  402. Int mtrls=scene->GetMaterialCount();
  403. FREP(mtrls)if(FbxSurfaceMaterial *mtrl=scene->GetMaterial(i))
  404. {
  405. XMaterial *xm=null;
  406. // params
  407. if(FbxSurfacePhong *phong=FbxCast<FbxSurfacePhong>(mtrl))
  408. {
  409. xm=&materials.New(); ee_mtrl_to_fbx_mtrl.add(mtrl);
  410. FbxDouble3 amb =phong->Ambient ; //xm->ambient .set(amb[0], amb[1], amb[2]);
  411. FbxDouble3 dif =phong->Diffuse ; xm->color.xyz.set(dif[0], dif[1], dif[2]);
  412. FbxDouble alpha=phong->TransparencyFactor; xm->color.w=1-alpha;
  413. FbxDouble3 spec =phong->Specular ; //xm->color.xyz.set(dif[0], dif[1], dif[2]);
  414. FbxDouble shin =phong->Shininess ; //xm->color.w=alpha;
  415. FbxDouble3 glow =phong->Emissive ; //xm->glow=Max(glow[0], glow[1], glow[2]);
  416. FbxDouble refl =phong->ReflectionFactor ; //xm->reflection=refl;
  417. FbxDouble bump =phong->DisplacementFactor; //xm->bump=bump;
  418. FbxDouble rough=phong->BumpFactor ; //xm->roughness=rough;
  419. }else
  420. if(FbxSurfaceLambert *lambert=FbxCast<FbxSurfaceLambert>(mtrl))
  421. {
  422. xm=&materials.New(); ee_mtrl_to_fbx_mtrl.add(mtrl);
  423. FbxDouble3 amb =lambert->Ambient ; //xm->ambient .set(amb[0], amb[1], amb[2]);
  424. FbxDouble3 dif =lambert->Diffuse ; xm->color.xyz.set(dif[0], dif[1], dif[2]);
  425. FbxDouble alpha=lambert->TransparencyFactor; xm->color.w=1-alpha;
  426. FbxDouble3 glow =lambert->Emissive ; //xm->glow=Max(glow[0], glow[1], glow[2]);
  427. FbxDouble bump =lambert->DisplacementFactor; //xm->bump=bump;
  428. FbxDouble rough=lambert->BumpFactor ; //xm->roughness=rough;
  429. }else continue;
  430. // name & props
  431. xm->name=FromUTF8(mtrl->GetName());
  432. xm->flip_normal_y=flip_normal_y;
  433. // textures
  434. #if 1
  435. Str tex;
  436. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sDiffuseFactor ); if(tex.is())xm-> color_map=tex;
  437. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sDiffuse ); if(tex.is())xm-> color_map=tex;
  438. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sTransparencyFactor); if(tex.is())xm-> alpha_map=tex;
  439. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sTransparentColor ); if(tex.is())xm-> alpha_map=tex;
  440. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sShininess ); if(tex.is())xm-> specular_map=tex;
  441. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sSpecularFactor ); if(tex.is())xm-> specular_map=tex;
  442. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sSpecular ); if(tex.is())xm-> specular_map=tex;
  443. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sAmbientFactor ); if(tex.is())xm-> light_map=tex;
  444. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sAmbient ); if(tex.is())xm-> light_map=tex;
  445. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sReflectionFactor ); if(tex.is())xm->reflection_map=tex;
  446. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sReflection ); if(tex.is())xm->reflection_map=tex;
  447. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sDisplacementFactor); if(tex.is())xm-> bump_map=tex;
  448. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sDisplacementColor ); if(tex.is())xm-> bump_map=tex;
  449. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sBumpFactor ); if(tex.is())xm-> normal_map=tex;
  450. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sBump ); if(tex.is())xm-> normal_map=tex;
  451. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sNormalMap ); if(tex.is())xm-> normal_map=tex;
  452. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sEmissive ); if(tex.is())xm-> glow_map=tex;
  453. tex=TextureFile(*mtrl, FbxSurfaceMaterial::sEmissiveFactor ); if(tex.is())xm-> glow_map=tex;
  454. #else
  455. Int TextureIndex;
  456. FOR_EACH_TEXTURE(TextureIndex)
  457. {
  458. FbxProperty Property=mtrl->FindProperty(FbxLayerElement::TEXTURE_CHANNEL_NAMES[TextureIndex]);
  459. if(Property.IsValid())
  460. {
  461. Int TextureCount=Property.GetSrcObjectCount(FbxTexture::ClassId);
  462. for(Int j=0; j<TextureCount; ++j)
  463. {
  464. /*if(FbxLayeredTexture *LayeredTexture=FbxCast<FbxLayeredTexture>(Property.GetSrcObject(FbxLayeredTexture::ClassId, j)))
  465. {
  466. // layered texture
  467. }else*/
  468. if(FbxTexture *Texture=FbxCast<FbxTexture>(Property.GetSrcObject(FbxTexture::ClassId, j)))
  469. if(FbxFileTexture *FileTexture=FbxCast<FbxFileTexture>(Texture))
  470. {
  471. Str file=FromUTF8(FileTexture->GetFileName()),
  472. type=FromUTF8(Property.GetName());
  473. if( type=="DiffuseColor" )xm-> color_map=file;else
  474. if( type=="SpecularColor" )xm->specular_map=file;else
  475. if( type=="SpecularFactor")xm->specular_map=file;else
  476. if( type=="NormalMap" )xm-> normal_map=file;
  477. }
  478. }
  479. }
  480. }
  481. #endif
  482. }
  483. }
  484. }
  485. void boneRemap(C MemPtrN<Byte, 256> &old_to_new)
  486. {
  487. REPA(nodes)
  488. {
  489. Node &node=nodes[i]; node.adjustBoneIndex(InRange(node.bone_index, old_to_new) ? old_to_new[node.bone_index] : 0xFF);
  490. }
  491. }
  492. void set(Skeleton *skeleton, MemPtr<Str> bone_names)
  493. {
  494. if(skeleton)
  495. {
  496. MemtN<Byte, 256> old_to_new;
  497. // create bones
  498. FREPA(nodes)
  499. {
  500. Node &node=nodes[i]; if(node.bone)
  501. {
  502. node.bone_index=skeleton->bones.elms();
  503. SkelBone &sbon=skeleton->bones.New ();
  504. Set(sbon.name, node.ee_name);
  505. sbon.pos =node.global.pos;
  506. sbon.dir =node.global.x;
  507. sbon.perp=node.global.y;
  508. sbon.fix();
  509. }
  510. }
  511. // set parents
  512. FREPA(nodes)
  513. {
  514. Node &node=nodes[i];
  515. if(InRange(node.bone_index, skeleton->bones)) // if this node is a bone
  516. for(Node *parent=node.parent; parent; parent=parent->parent) // iterate all parents
  517. if(InRange(parent->bone_index, skeleton->bones)) // if the parent is a bone
  518. {
  519. skeleton->bones[node.bone_index].parent=parent->bone_index; // set skel bone parent
  520. break; // stop searching
  521. }
  522. }
  523. // sort bones and set children
  524. skeleton->sortBones(old_to_new); // 'sortBones' needs to be called before any 'SetSkin'
  525. boneRemap(old_to_new);
  526. // set bone lengths, before removing nub-bones (so we can use the nubs for proper lengths of their parents)
  527. skeleton->setBoneLengths();
  528. // after bone lengths, remove nub-bones
  529. FREPA(nodes)
  530. {
  531. Node &node=nodes[i];
  532. if(node.bone && !node.children.elms() && !node.has_skin && Ends(node.full_name, "Nub"))
  533. {
  534. skeleton->removeBone(node.bone_index, old_to_new);
  535. node.adjustBoneIndex(-1); // !! adjust manually after remap, because remap will point to the parent of removed bone instead, this is important so that we won't apply this NUB bone animations to its parent !!
  536. boneRemap(old_to_new);
  537. }
  538. }
  539. // after skeleton bones are ready, link nearest bone and setup original full names
  540. FREPA(nodes)
  541. {
  542. Node &node=nodes[i];
  543. for(Node *parent=&node; parent; parent=parent->parent) // iterate all parents, starting from this node inclusive
  544. {
  545. if(InRange(parent-> bone_index, skeleton->bones)){node.nearest_bone_index=parent-> bone_index; break;}
  546. if(InRange(parent->nearest_bone_index, skeleton->bones)){node.nearest_bone_index=parent->nearest_bone_index; break;}
  547. }
  548. }
  549. if(bone_names)
  550. {
  551. bone_names.setNum(skeleton->bones.elms());
  552. FREPA(nodes)
  553. {
  554. C Node &node=nodes[i]; if(InRange(node.bone_index, bone_names))bone_names[node.bone_index]=node.full_name;
  555. }
  556. }
  557. }
  558. }
  559. static FbxAMatrix GetPoseMatrix(C FbxPose &pose, int node_index)
  560. {
  561. FbxMatrix matrix=pose.GetMatrix(node_index);
  562. FbxAMatrix amatrix; SCAST(FbxDouble4x4, amatrix)=matrix;
  563. return amatrix;
  564. }
  565. static FbxAMatrix GetGeometryOffset(FbxNode *node)
  566. {
  567. return FbxAMatrix(node->GetGeometricTranslation(FbxNode::eSourcePivot),
  568. node->GetGeometricRotation (FbxNode::eSourcePivot),
  569. node->GetGeometricScaling (FbxNode::eSourcePivot));
  570. }
  571. static FbxAMatrix GetGlobalPosition(FbxNode *node, const FbxTime &time=FBXSDK_TIME_INFINITE, FbxPose *pose=null, FbxAMatrix *parent_global_position=null)
  572. {
  573. if(pose)
  574. {
  575. Int node_index=pose->Find(node); if(node_index>=0)
  576. {
  577. // The bind pose is always a global matrix. If we have a rest pose, we need to check if it is stored in global or local space.
  578. if(pose->IsBindPose() || !pose->IsLocalMatrix(node_index))return GetPoseMatrix(*pose, node_index);
  579. // We have a local matrix, we need to convert it to a global space matrix.
  580. FbxAMatrix parent;
  581. if(parent_global_position)parent=*parent_global_position;else
  582. if(node->GetParent() )parent= GetGlobalPosition(node->GetParent(), time, pose);
  583. FbxAMatrix local_position=GetPoseMatrix(*pose, node_index);
  584. return parent*local_position;
  585. }
  586. }
  587. // There is no pose entry for that node, get the current global position instead.
  588. // Ideally this would use parent global position and local position to compute the global position.
  589. // Unfortunately the equation "parent_global_position*local_position"
  590. // does not hold when inheritance type is other than "Parent" (RSrs).
  591. // To compute the parent rotation and scaling is tricky in the RrSs and Rrs cases.
  592. return node->EvaluateGlobalTransform(time);
  593. }
  594. void set(Mesh *mesh, Skeleton *skeleton, MemPtr<Int> part_material_index)
  595. {
  596. if(mesh)
  597. {
  598. MemtN<Int, 128> poly_vtx;
  599. FREPA(nodes)if(nodes[i].mesh)
  600. {
  601. Node &node=nodes[i];
  602. //FbxGeometryConverter(manager).Triangulate(node.node->GetNodeAttribute(), true); // triangulate, don't do this, because we want to preserve quads
  603. if(FbxMesh *fbx_mesh=node.node->GetMesh()) // access fbx mesh
  604. {
  605. Int control_points=fbx_mesh->GetControlPointsCount();
  606. FbxVector4 *control_point =fbx_mesh->GetControlPoints ();
  607. Int polys =fbx_mesh->GetPolygonCount ();
  608. if(polys>0)
  609. {
  610. MeshBase base;
  611. Int texs=Mid(fbx_mesh->GetElementUVCount(), 0, 3); // EE supports only up to 3
  612. Bool one_material=true;
  613. REP(fbx_mesh->GetElementMaterialCount())
  614. if(FbxGeometryElementMaterial *mtrl=fbx_mesh->GetElementMaterial(i))
  615. if(mtrl->GetMappingMode()==FbxGeometryElement::eByPolygon){one_material=false; break;}
  616. MatrixD matrix_d=MATRIX(GetGeometryOffset(node.node))*node.global;
  617. Matrix matrix=matrix_d;
  618. // mesh needs to be transformed by "VertexTransformMatrix*matrix"
  619. // skin
  620. MemtN< Matrix, 256 > bone_matrix;
  621. Memc < Memc<IndexWeight> > vtx_skin;
  622. VecB4 vtx_matrix=0, vtx_blend(255, 0, 0, 0); // !! sum must be equal to 255 !! defaults used for vertexes with no skin specified
  623. Bool force_skin=false, animate=false;
  624. if(skeleton && skeleton->bones.elms())
  625. {
  626. REP(skeleton->bones.elms()+VIRTUAL_ROOT_BONE)bone_matrix.add(matrix);
  627. Int skin_count=fbx_mesh->GetDeformerCount(FbxDeformer::eSkin);
  628. FREPD(s, skin_count)if(FbxSkin *skin=(FbxSkin*)fbx_mesh->GetDeformer(s, FbxDeformer::eSkin))
  629. {
  630. Int bones=skin->GetClusterCount();
  631. FREPD(b, bones)if(FbxCluster *cluster=skin->GetCluster(b))
  632. {
  633. if(Node *bone=findNode(cluster->GetLink()))
  634. {
  635. if(InRange(bone->nearest_bone_index, skeleton->bones)) // use 'nearest_bone_index' in case this node was disabled as a bone
  636. {
  637. if(Int *indices=cluster->GetControlPointIndices())
  638. if(Dbl *weights=cluster->GetControlPointWeights())
  639. {
  640. Int index_count=cluster->GetControlPointIndicesCount(); FREP(index_count)
  641. {
  642. Int point =indices[i];
  643. Flt weight=weights[i];
  644. if(InRange(point, control_points))vtx_skin(point).New().set(bone->nearest_bone_index+VIRTUAL_ROOT_BONE, weight);
  645. }
  646. }
  647. }
  648. if(InRange(bone->bone_index , skeleton->bones)
  649. && InRange(bone->bone_index+VIRTUAL_ROOT_BONE, bone_matrix))
  650. {
  651. /*FbxAMatrix temp1, temp2;
  652. MatrixD transform_matrix =MATRIX(cluster->GetTransformMatrix (temp1)),
  653. transform_link_matrix=MATRIX(cluster->GetTransformLinkMatrix(temp2));*/
  654. // matrixes that calculate conversion from mesh to bind pose (code taken from FBX SDK tutorials)
  655. FbxTime time=FBXSDK_TIME_INFINITE; FbxPose *pose=null;
  656. FbxAMatrix GlobalPosition=GetGlobalPosition(node.node, time, pose);
  657. FbxAMatrix GeometryOffset=GetGeometryOffset(node.node);
  658. FbxAMatrix GlobalOffPosition=GlobalPosition*GeometryOffset;
  659. FbxAMatrix ReferenceGlobalInitPosition; cluster->GetTransformMatrix(ReferenceGlobalInitPosition);
  660. FbxAMatrix ReferenceGlobalCurrentPosition=GlobalOffPosition;
  661. FbxAMatrix ReferenceGeometry=GetGeometryOffset(node.node);
  662. ReferenceGlobalInitPosition*=ReferenceGeometry;
  663. FbxAMatrix ClusterGlobalInitPosition; cluster->GetTransformLinkMatrix(ClusterGlobalInitPosition);
  664. FbxAMatrix ClusterGlobalCurrentPosition=GetGlobalPosition(cluster->GetLink(), time, pose);
  665. FbxAMatrix ClusterRelativeInitPosition=ClusterGlobalInitPosition.Inverse()*ReferenceGlobalInitPosition;
  666. FbxAMatrix ClusterRelativeCurrentPositionInverse=ReferenceGlobalCurrentPosition.Inverse()*ClusterGlobalCurrentPosition;
  667. FbxAMatrix VertexTransformMatrix=ClusterRelativeCurrentPositionInverse*ClusterRelativeInitPosition;
  668. bone_matrix[bone->bone_index+VIRTUAL_ROOT_BONE]=MATRIX(VertexTransformMatrix)*matrix_d;
  669. animate=true;
  670. }else
  671. {
  672. #if DEBUG
  673. Exit("Cluster Node is not a bone");
  674. #endif
  675. }
  676. }else
  677. {
  678. #if DEBUG
  679. Exit("Cluster Node not found");
  680. #endif
  681. }
  682. }
  683. }
  684. for(Node *cur=&node; cur; cur=cur->parent)if(InRange(cur->bone_index, skeleton->bones)) // find first parent that's a bone
  685. {
  686. force_skin=true; // if any parent is a bone, then always set skinning
  687. vtx_matrix.x=cur->bone_index+VIRTUAL_ROOT_BONE;
  688. break;
  689. }
  690. }
  691. Int tris=0, quads=0;
  692. REP(polys)
  693. {
  694. Int poly_vtxs=fbx_mesh->GetPolygonSize(i);
  695. if( poly_vtxs==3) tris++;else
  696. if( poly_vtxs==4)quads++;else
  697. if( poly_vtxs>=5) tris+=poly_vtxs-2;
  698. }
  699. // set mesh
  700. base.create(tris*3 + quads*4, 0, tris, quads, (fbx_mesh->GetElementVertexColorCount() ? VTX_COLOR : 0) | ((texs>=1)?VTX_TEX0:0) | ((texs>=2)?VTX_TEX1:0) | ((texs>=3)?VTX_TEX2:0) | (fbx_mesh->GetElementNormalCount()?VTX_NRM:0) | ((force_skin || vtx_skin.elms())?VTX_SKIN:0) | (one_material?0:FACE_ID));
  701. Bool reverse=node.global.mirrored();
  702. // polys
  703. Int vtxs=0; tris=quads=0; FREPD(poly, polys) // polygons
  704. {
  705. Int poly_vtxs =fbx_mesh->GetPolygonSize(poly);
  706. if( poly_vtxs>=3)
  707. {
  708. Int vtx_ofs=vtxs;
  709. // vertexes
  710. FREPD(vtx, poly_vtxs) // vertexes in poly
  711. {
  712. Int control_point_index=fbx_mesh->GetPolygonVertex(poly, vtx);
  713. // pos
  714. base.vtx.pos(vtxs)=(InRange(control_point_index, control_points) ? VEC(control_point[control_point_index]).xyz : VecZero);
  715. // skin
  716. if(base.vtx.matrix())
  717. {
  718. if(InRange(control_point_index, vtx_skin))
  719. {
  720. Memc<IndexWeight> &vs=vtx_skin[control_point_index]; if(vs.elms())
  721. {
  722. SetSkin(vs, base.vtx.matrix(vtxs), base.vtx.blend(vtxs), skeleton);
  723. goto skin_set;
  724. }
  725. }
  726. // if vtx had no skinning set, then set default
  727. base.vtx.matrix(vtxs)=vtx_matrix;
  728. base.vtx.blend (vtxs)=vtx_blend ;
  729. skin_set:;
  730. }
  731. // color
  732. if(base.vtx.color())
  733. {
  734. Color color=WHITE;
  735. if(FbxGeometryElementVertexColor *leVtxc=fbx_mesh->GetElementVertexColor(0))
  736. switch(leVtxc->GetMappingMode())
  737. {
  738. case FbxGeometryElement::eByControlPoint: switch(leVtxc->GetReferenceMode())
  739. {
  740. case FbxGeometryElement::eDirect : color=COLOR(leVtxc->GetDirectArray().GetAt(control_point_index)); break;
  741. case FbxGeometryElement::eIndexToDirect: color=COLOR(leVtxc->GetDirectArray().GetAt(leVtxc->GetIndexArray().GetAt(control_point_index))); break;
  742. }break;
  743. case FbxGeometryElement::eByPolygonVertex: switch(leVtxc->GetReferenceMode())
  744. {
  745. case FbxGeometryElement::eDirect : color=COLOR(leVtxc->GetDirectArray().GetAt(vtxs)); break;
  746. case FbxGeometryElement::eIndexToDirect: color=COLOR(leVtxc->GetDirectArray().GetAt(leVtxc->GetIndexArray().GetAt(vtxs))); break;
  747. }break;
  748. }
  749. base.vtx.color(vtxs)=color;
  750. }
  751. // normal
  752. if(base.vtx.nrm())
  753. {
  754. Vec nrm=0;
  755. if(FbxGeometryElementNormal *leNormal=fbx_mesh->GetElementNormal(0))
  756. switch(leNormal->GetMappingMode())
  757. {
  758. case FbxGeometryElement::eByControlPoint: switch(leNormal->GetReferenceMode())
  759. {
  760. case FbxGeometryElement::eDirect : nrm=VEC(leNormal->GetDirectArray().GetAt(control_point_index)).xyz; break;
  761. case FbxGeometryElement::eIndexToDirect: nrm=VEC(leNormal->GetDirectArray().GetAt(leNormal->GetIndexArray().GetAt(control_point_index))).xyz; break;
  762. }break;
  763. case FbxGeometryElement::eByPolygonVertex: switch(leNormal->GetReferenceMode())
  764. {
  765. case FbxGeometryElement::eDirect : nrm=VEC(leNormal->GetDirectArray().GetAt(vtxs)).xyz; break;
  766. case FbxGeometryElement::eIndexToDirect: nrm=VEC(leNormal->GetDirectArray().GetAt(leNormal->GetIndexArray().GetAt(vtxs))).xyz; break;
  767. }break;
  768. }
  769. base.vtx.nrm(vtxs)=nrm;
  770. }
  771. // tex
  772. REPD(tex, texs)
  773. {
  774. Vec2 uv=0;
  775. if(FbxGeometryElementUV *leUV=fbx_mesh->GetElementUV(tex))
  776. switch(leUV->GetMappingMode())
  777. {
  778. case FbxGeometryElement::eByControlPoint: switch(leUV->GetReferenceMode())
  779. {
  780. case FbxGeometryElement::eDirect : uv=VEC(leUV->GetDirectArray().GetAt(control_point_index)); break;
  781. case FbxGeometryElement::eIndexToDirect: uv=VEC(leUV->GetDirectArray().GetAt(leUV->GetIndexArray().GetAt(control_point_index))); break;
  782. }break;
  783. case FbxGeometryElement::eByPolygonVertex:
  784. {
  785. #if 0 // this code is from FBX SDK "DisplayMesh.cxx", but it's faulty
  786. Int TextureUVIndex=fbx_mesh->GetTextureUVIndex(poly, vtx);
  787. switch(leUV->GetReferenceMode())
  788. {
  789. case FbxGeometryElement::eDirect :
  790. case FbxGeometryElement::eIndexToDirect: uv=VEC(leUV->GetDirectArray().GetAt(TextureUVIndex)); break;
  791. }
  792. #else // this code is based on other vertex components
  793. switch(leUV->GetReferenceMode())
  794. {
  795. case FbxGeometryElement::eDirect : uv=VEC(leUV->GetDirectArray().GetAt(vtxs)); break;
  796. case FbxGeometryElement::eIndexToDirect: uv=VEC(leUV->GetDirectArray().GetAt(leUV->GetIndexArray().GetAt(vtxs))); break;
  797. }
  798. #endif
  799. }break;
  800. }
  801. uv.y=1-uv.y; // FBX uses mirrored Y
  802. if(tex==0)base.vtx.tex0(vtxs)=uv;else
  803. if(tex==1)base.vtx.tex1(vtxs)=uv;else
  804. if(tex==2)base.vtx.tex2(vtxs)=uv;
  805. }
  806. // counter
  807. vtxs++;
  808. }
  809. // face
  810. if(!one_material) // material
  811. {
  812. FbxSurfaceMaterial *mtrl=null;
  813. FREPD(m, fbx_mesh->GetElementMaterialCount())
  814. if(FbxGeometryElementMaterial *mtrl_elm=fbx_mesh->GetElementMaterial(m))
  815. if(mtrl=node.node->GetMaterial(mtrl_elm->GetIndexArray().GetAt(poly)))break;
  816. Int mtrl_id=findMaterial(mtrl)+1; // +1 so not found material (-1) will have ID=0, materials with 0 index will have ID=1, ..
  817. if (poly_vtxs==4)base.quad.id( quads)=mtrl_id;else
  818. REP(poly_vtxs -2)base.tri .id(i+tris)=mtrl_id;
  819. }
  820. // vertex index
  821. if(poly_vtxs==3) // triangle
  822. {
  823. base.tri.ind(tris).set(vtx_ofs, vtx_ofs+1, vtx_ofs+2); if(reverse)base.tri.ind(tris).reverse();
  824. tris++;
  825. }else
  826. if(poly_vtxs==4) // quad
  827. {
  828. VecI4 &ind=base.quad.ind(quads++).set(vtx_ofs, vtx_ofs+1, vtx_ofs+2, vtx_ofs+3); if(reverse)ind.swapXZ(); // use 'swapXZ' to preserve the same triangles being generated, but flipped, this is important, because 'reverse' would cause different triangle combination
  829. // there are 2 possibilities to choose a quad, default: 013+123, rotated: 012+230, to determine which one, we calculate normals for 2 tris in those 2 quads (4 tris total) and select the quad which tris are the most flat
  830. C Vec &p0=base.vtx.pos(ind.x), &p1=base.vtx.pos(ind.y), &p2=base.vtx.pos(ind.z), &p3=base.vtx.pos(ind.w),
  831. qn0=GetNormal(p0, p1, p3), qn1=GetNormal(p1, p2, p3), // quad normals
  832. rqn0=GetNormal(p0, p1, p2), rqn1=GetNormal(p2, p3, p0); // rotated quad normals
  833. if(Dot(rqn0, rqn1)>Dot(qn0, qn1))ind.rotateOrder();
  834. }else // triangulate a polygon
  835. {
  836. // get average normal
  837. Vec normal=0; REP(poly_vtxs)
  838. {
  839. VecI2 ind(i, (i+1)%poly_vtxs); ind+=vtx_ofs;
  840. normal+=GetNormalEdge(base.vtx.pos(ind.x), base.vtx.pos(ind.y));
  841. }
  842. // add triangles
  843. poly_vtx.setNum(poly_vtxs); REPAO(poly_vtx)=vtx_ofs+i;
  844. for(; poly_vtx.elms()>=3; )
  845. {
  846. Bool added=false; // if added a face in this step
  847. REP(poly_vtx.elms()-2)
  848. {
  849. VecI &ind=base.tri.ind(tris); ind.set(poly_vtx[i], poly_vtx[i+1], poly_vtx[i+2]);
  850. Tri tri(base.vtx.pos(ind.x), base.vtx.pos(ind.y), base.vtx.pos(ind.z));
  851. if(Dot(normal, tri.n)>0) // if normal of this triangle is correct
  852. {
  853. Vec cross[3]={Cross(tri.n, tri.p[0]-tri.p[1]), Cross(tri.n, tri.p[1]-tri.p[2]), Cross(tri.n, tri.p[2]-tri.p[0])};
  854. REPAD(t, poly_vtx)if(t<i || t>i+2)if(Cuts(base.vtx.pos(poly_vtx[t]), tri, cross))goto cuts; // if any other vertex intersects with this triangle, then continue
  855. if(reverse)ind.reverse();
  856. tris++;
  857. poly_vtx.remove(i+1, true);
  858. added=true;
  859. cuts:;
  860. }
  861. }
  862. if(!added) // add all remaining
  863. {
  864. REP(poly_vtx.elms()-2)
  865. {
  866. VecI &ind=base.tri.ind(tris); ind.set(poly_vtx[i], poly_vtx[i+1], poly_vtx[i+2]); if(reverse)ind.reverse();
  867. tris++;
  868. poly_vtx.remove(i+1, true);
  869. }
  870. break;
  871. }
  872. }
  873. }
  874. }
  875. }
  876. // finalize base
  877. base.weldVtx(VTX_ALL, EPSD, EPS_COL_COS, -1); // use small epsilon in case mesh is scaled down
  878. if(animate)base.animate(bone_matrix);else base.transform(matrix); // 'bone_matrix' was already transformed by 'matrix'
  879. if(!base.vtx.nrm())base.setNormals();
  880. // put to mesh
  881. if(one_material)
  882. {
  883. MeshPart &part=mesh->parts.New(); Set(part.name, node.full_name);
  884. Swap(part.base, base);
  885. if(part_material_index)
  886. {
  887. Int ee_mtrl=-1;
  888. FREPD(m, fbx_mesh->GetElementMaterialCount())
  889. if(FbxGeometryElementMaterial *mtrl_elm=fbx_mesh->GetElementMaterial(m))
  890. if(mtrl_elm->GetMappingMode()==FbxGeometryElement::eAllSame)
  891. if(FbxSurfaceMaterial *mtrl=node.node->GetMaterial(mtrl_elm->GetIndexArray().GetAt(0)))
  892. {ee_mtrl=findMaterial(mtrl); break;}
  893. part_material_index.add(ee_mtrl);
  894. }
  895. }else
  896. {
  897. MeshLod lod;
  898. base.copyId(lod, ~ID_ALL); // copy to 'lod', ID's are no longer needed
  899. FREPA(lod)
  900. {
  901. MeshPart &src=lod.parts[i];
  902. if(src.is())
  903. {
  904. MeshPart &dest=mesh->parts.New(); Swap(src, dest); Set(dest.name, node.full_name);
  905. if(part_material_index)part_material_index.add(i-1); // ID's were created with +1
  906. }
  907. }
  908. }
  909. }
  910. }
  911. }
  912. }
  913. }
  914. void set(Skeleton *skeleton, MemPtr<XAnimation> animations)
  915. {
  916. if(skeleton && animations)
  917. FREP(scene->GetSrcObjectCount<FbxAnimStack>())
  918. if(FbxAnimStack *anim_stack=scene->GetSrcObject<FbxAnimStack>(i))
  919. {
  920. scene->SetCurrentAnimationStack(anim_stack);
  921. Int anim_layers=anim_stack->GetMemberCount<FbxAnimLayer>();
  922. if( anim_layers>0)
  923. {
  924. FbxTimeSpan time_span=anim_stack->GetLocalTimeSpan(); Dbl time_start=time_span.GetStart().GetSecondDouble(), time_stop=time_span.GetStop().GetSecondDouble(), fps=FbxTime::GetFrameRate(global_settings->GetTimeMode()); // alternative is GetReferenceTimeSpan()
  925. XAnimation &xanim =animations.New();
  926. xanim.fps =fps;
  927. xanim.start=time_start;
  928. xanim.name =FromUTF8(anim_stack->GetName());
  929. xanim.anim.length(time_stop-time_start, false);
  930. if(anim_layers>1)
  931. {
  932. Bool ok=anim_stack->BakeLayers(null, 0, time_stop, 1.0/fps);
  933. anim_layers=1;
  934. }
  935. REPD(layer, anim_layers)
  936. if(FbxAnimLayer *anim_layer=anim_stack->GetMember<FbxAnimLayer>(layer))
  937. {
  938. FREPAD(n, nodes)
  939. {
  940. Node &node=nodes[n]; if(InRange(node.bone_index, skeleton->bones))
  941. {
  942. Memc<FbxTime> rot_times, pos_times, scale_times;
  943. Node *animated_node_ancestor=&node; // there's following node hierarchy starting from root: null <-> root <-> .. <-> bone_node_parent (nearest node that is a bone) <-> .. (some bones) <-> animated_node_ancestor (first node that has some animations, in most cases this is 'node' however if there were some parent bones that are ignored, then this could point to one of them) <-> .. <-> node (currently processed node), at start set to "&node" so we don't have to call 'hasAnim' below, this is never set to another node that is a bone (only 'node' can be set or another nodes that have animations but aren't bones)
  944. for(Node *cur=&node; ; )
  945. {
  946. if(cur!=&node && cur->hasAnim(anim_layer))animated_node_ancestor=cur; // remember the last encountered node that has some animations !! 'hasAnim' assumes that 'SetCurrentAnimationStack' was already called !!
  947. if(FbxAnimCurve *curve=cur->node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X))FREP(curve->KeyGetCount())rot_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  948. if(FbxAnimCurve *curve=cur->node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y))FREP(curve->KeyGetCount())rot_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  949. if(FbxAnimCurve *curve=cur->node->LclRotation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z))FREP(curve->KeyGetCount())rot_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  950. if(FbxAnimCurve *curve=cur->node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X))FREP(curve->KeyGetCount())pos_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  951. if(FbxAnimCurve *curve=cur->node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y))FREP(curve->KeyGetCount())pos_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  952. if(FbxAnimCurve *curve=cur->node->LclTranslation.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z))FREP(curve->KeyGetCount())pos_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  953. if(FbxAnimCurve *curve=cur->node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_X))FREP(curve->KeyGetCount())scale_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  954. if(FbxAnimCurve *curve=cur->node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Y))FREP(curve->KeyGetCount())scale_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  955. if(FbxAnimCurve *curve=cur->node->LclScaling.GetCurve(anim_layer, FBXSDK_CURVENODE_COMPONENT_Z))FREP(curve->KeyGetCount())scale_times.binaryInclude(curve->KeyGetTime(i), CompareTime);
  956. cur=cur->parent; if(!cur || InRange(cur->bone_index, skeleton->bones))break; // if there's no parent, or it's a bone (its animations are alread stored in it so we can skip them), then break
  957. }
  958. if(rot_times.elms() || pos_times.elms() || scale_times.elms())
  959. {
  960. Memc<FbxTime> times;
  961. FREPA( rot_times)times.binaryInclude( rot_times[i], CompareTime);
  962. FREPA( pos_times)times.binaryInclude( pos_times[i], CompareTime);
  963. FREPA(scale_times)times.binaryInclude(scale_times[i], CompareTime);
  964. #if DEBUG
  965. //if(!node.bone)Exit("Animated Node is not a Bone");
  966. #endif
  967. SkelBone &sbon=skeleton->bones[node.bone_index];
  968. AnimBone &abon=xanim.anim.bones.New(); abon.set(sbon.name);
  969. MatrixD3 parent_matrix_inv; if(sbon.parent!=0xFF)skeleton->bones[sbon.parent].inverse(parent_matrix_inv);
  970. MatrixD3 local_to_world; animated_node_ancestor->local.orn().inverseNonOrthogonal(local_to_world); local_to_world*=animated_node_ancestor->global.orn(); // GetTransform(animated_node_ancestor->local.orn(), animated_node_ancestor->global.orn());
  971. abon.orns .setNum( rot_times.elms());
  972. abon.poss .setNum( pos_times.elms());
  973. abon.scales.setNum(scale_times.elms());
  974. #if KEY_SAFETY
  975. REPAO(abon.orns ).time=NAN;
  976. REPAO(abon.poss ).time=NAN;
  977. REPAO(abon.scales).time=NAN;
  978. #endif
  979. FREPA(times)
  980. {
  981. Int index;
  982. C FbxTime &time =times[i];
  983. Flt t =time.GetSecondDouble()-time_start;
  984. FbxAMatrix transform =node.node->EvaluateLocalTransform(time);
  985. MatrixD node_local=node.local;
  986. for(Node *n=&node; n!=animated_node_ancestor; ) // gather all transforms starting from this 'node' to 'animated_node_ancestor' inclusive
  987. {
  988. n=n->parent;
  989. transform=n->node->EvaluateLocalTransform(time)*transform; // FBX multiply order is reversed compared to EE
  990. node_local*=n->local;
  991. }
  992. MatrixD anim_matrix=MATRIX(transform);
  993. // orientation
  994. if(rot_times.binarySearch(time, index, CompareTime))
  995. {
  996. MatrixD3 anim;
  997. anim.x=anim_matrix.z;
  998. anim.y=anim_matrix.y;
  999. anim.z=anim_matrix.x;
  1000. anim*=local_to_world;
  1001. if(sbon.parent!=0xFF)anim*=parent_matrix_inv;
  1002. OrientD o=anim; o.fix();
  1003. AnimKeys::Orn &orn=abon.orns[index]; orn.time=t; orn.orn=o;
  1004. }
  1005. // position
  1006. if(pos_times.binarySearch(time, index, CompareTime))
  1007. {
  1008. VecD p=anim_matrix.pos-node_local.pos;
  1009. if(Node *parent=animated_node_ancestor->parent)p*=parent->global.orn();
  1010. if(sbon.parent!=0xFF)p*=parent_matrix_inv;
  1011. AnimKeys::Pos &pos=abon.poss[index]; pos.time=t; pos.pos=p;
  1012. }
  1013. // scale
  1014. if(scale_times.binarySearch(time, index, CompareTime))
  1015. {
  1016. AnimKeys::Scale &scale=abon.scales[index]; scale.time=t;
  1017. scale.scale.x=ScaleFactorR(anim_matrix.z.length()/node_local.z.length());
  1018. scale.scale.y=ScaleFactorR(anim_matrix.y.length()/node_local.y.length());
  1019. scale.scale.z=ScaleFactorR(anim_matrix.x.length()/node_local.x.length());
  1020. }
  1021. }
  1022. #if KEY_SAFETY
  1023. REPA(abon.orns )if(NaN(abon.orns [i].time))abon.orns .remove(i, true);
  1024. REPA(abon.poss )if(NaN(abon.poss [i].time))abon.poss .remove(i, true);
  1025. REPA(abon.scales)if(NaN(abon.scales[i].time))abon.scales.remove(i, true);
  1026. #endif
  1027. abon.sortFrames(); // sort just in case FbxTime is weird somehow
  1028. }
  1029. }
  1030. }
  1031. }
  1032. xanim.anim.setTangents().setRootMatrix();
  1033. }
  1034. }
  1035. }
  1036. };
  1037. /******************************************************************************/
  1038. Bool _ImportFBX(C Str &name, Mesh *mesh, Skeleton *skeleton, MemPtr<XAnimation> animations, MemPtr<XMaterial> materials, MemPtr<Int> part_material_index, MemPtr<Str> bone_names, Bool all_nodes_as_bones, C Str8 &password)
  1039. {
  1040. if(mesh )mesh ->del();
  1041. if(skeleton)skeleton->del();
  1042. animations .clear();
  1043. materials .clear();
  1044. part_material_index.clear();
  1045. bone_names .clear();
  1046. FBX fbx; if(fbx.init() && fbx.load(name, all_nodes_as_bones, password))
  1047. {
  1048. Skeleton temp, *skel=(skeleton ? skeleton : (mesh || animations) ? &temp : null); // if skel not specified, but we want mesh or animations, then we have to process it (mesh requires it for skinning: bone names and types, animations require for skeleton bone transforms, parents, etc.)
  1049. {
  1050. SyncLocker locker(Lock);
  1051. fbx.set(materials); // !! call before creating meshes to setup 'ee_mtrl_to_fbx_mtrl' !!
  1052. if(skel)
  1053. {
  1054. fbx.setNodesAsBoneFromSkin();
  1055. fbx.setNodesAsBoneFromAnim();
  1056. #if 0
  1057. Memc<Str> ignore;
  1058. #if 0 // InfinityPBR - Medusa 256 bone limit
  1059. ignore.add("Root");
  1060. ignore.add("LeftWrist");
  1061. ignore.add("RightWrist");
  1062. ignore.add("Jaw_1");
  1063. ignore.add("Tongue_1");
  1064. ignore.add("Tongue_3");
  1065. ignore.add("Tongue_5");
  1066. ignore.add("L_weapon");
  1067. ignore.add("Snakehead_8");
  1068. ignore.add("Snakehead_13");
  1069. ignore.add("Snakehead_23");
  1070. ignore.add("Snakehead_33");
  1071. ignore.add("Snakehead_18");
  1072. ignore.add("Snakehead_28");
  1073. ignore.add("L_Snakehead_68");
  1074. ignore.add("L_Snakehead_73");
  1075. ignore.add("L_Snakehead_78");
  1076. //ignore.add("L_Snakehead_83");
  1077. ignore.add("L_Snakehead_93");
  1078. ignore.add("L_Snakehead_98");
  1079. ignore.add("L_Snakehead_103");
  1080. ignore.add("L_Snakehead_108");
  1081. ignore.add("L_Snakehead_113");
  1082. ignore.add("L_Snakehead_118");
  1083. ignore.add("L_Snakehead_123");
  1084. ignore.add("L_Snakehead_128");
  1085. ignore.add("R_Snakehead_68");
  1086. ignore.add("R_Snakehead_73");
  1087. ignore.add("R_Snakehead_78");
  1088. //ignore.add("R_Snakehead_83");
  1089. ignore.add("R_Snakehead_93");
  1090. ignore.add("R_Snakehead_98");
  1091. ignore.add("R_Snakehead_103");
  1092. ignore.add("R_Snakehead_108");
  1093. ignore.add("R_Snakehead_113");
  1094. ignore.add("R_Snakehead_118");
  1095. ignore.add("R_Snakehead_123");
  1096. ignore.add("R_Snakehead_128");
  1097. ignore.add("Tail02");
  1098. ignore.add("Tail04");
  1099. ignore.add("Tail06");
  1100. ignore.add("Tail08");
  1101. ignore.add("Tail10");
  1102. ignore.add("Tail12");
  1103. ignore.add("Tail14");
  1104. ignore.add("Tail16");
  1105. #endif
  1106. fbx.ignoreBones(ignore);
  1107. #endif
  1108. //Memc<Str> shorten; shorten.add(); fbx.shortenBoneNames(shorten);
  1109. fbx.makeBoneNamesUnique();
  1110. fbx.shortenBoneNames();
  1111. fbx.set(skel, bone_names);
  1112. fbx.set(mesh, skel, part_material_index);
  1113. fbx.set(skel, animations);
  1114. }
  1115. }
  1116. REPAO(materials ).fixPath(GetPath(name));
  1117. if(skel ) skel->mirrorX().setBoneTypes(); // call 'setBoneTypes' after 'mirrorX' !!
  1118. if(mesh ){mesh->mirrorX().skeleton(skel).skeleton(null).setBox(); CleanMesh(*mesh);} // !! link with skeleton after calling 'setBoneTypes' !!
  1119. REPAO(animations).anim .mirrorX().setBoneTypeIndexesFromSkeleton(*skel); // !! call this after 'setBoneTypes' !!
  1120. if(skeleton ){skel->setBoneShapes(); if(skeleton!=skel)Swap(*skeleton, *skel);} // !! 'skel' is now invalid !!
  1121. if(!Equal(fbx.scale, 1))
  1122. {
  1123. if(mesh )mesh ->scale(fbx.scale);
  1124. if(skeleton )skeleton->scale(fbx.scale);
  1125. REPAO(animations).anim .scale(fbx.scale);
  1126. }
  1127. return true;
  1128. }
  1129. return false;
  1130. }
  1131. /******************************************************************************/
  1132. }
  1133. #include "FBX Shared.h"
  1134. #if FBX_LINK_TYPE==FBX_LINK_DLL
  1135. /******************************************************************************
  1136. Memory allocated by the DLL MUST also be free'd by the DLL (otherwise errors happen), so:
  1137. -allocate all FBX data on the DLL
  1138. -return pointer to that data to the EXE
  1139. -copy data on the EXE
  1140. -free data in the DLL
  1141. /******************************************************************************/
  1142. extern "C" __declspec(dllexport) Int __cdecl ImportFBXVer ( ) {return 0;}
  1143. extern "C" __declspec(dllexport) void __cdecl ImportFBXFree(CPtr data ) {Free(data);}
  1144. extern "C" __declspec(dllexport) CPtr __cdecl ImportFBXData(CChar *name, Int &size, UInt flag)
  1145. {
  1146. Mesh mesh ;
  1147. Skeleton skel ;
  1148. Memc<XAnimation> anims; MemPtr<XAnimation> a; if(flag&FBX_ANIM )a.point(anims);
  1149. Memc<XMaterial > mtrls; MemPtr<XMaterial > m; if(flag&FBX_MTRL )m.point(mtrls);
  1150. Memc<Int > pmi ; MemPtr<Int > p; if(flag&FBX_PMI )p.point(pmi );
  1151. Memc<Str > names; MemPtr<Str > n; if(flag&FBX_BONE_NAMES)n.point(names);
  1152. if(_ImportFBX(name, (flag&FBX_MESH) ? &mesh : null, (flag&FBX_SKEL) ? &skel : null, a, m, p, n, FlagTest(flag, FBX_ALL_NODES_AS_BONES), S8))
  1153. {
  1154. File f; f.writeMem(); SaveFBXData(f, mesh, skel, anims, mtrls, pmi, names); // save data to file
  1155. Ptr data=Alloc(f.size()); f.pos(0); f.get(data, size=f.size()); // copy file to memory
  1156. return data; // return that memory
  1157. }
  1158. return null;
  1159. }
  1160. /******************************************************************************/
  1161. void InitPre() {}
  1162. Bool Init () {return false;}
  1163. void Shut () {}
  1164. Bool Update () {return false;}
  1165. void Draw () {}
  1166. /******************************************************************************/
  1167. #endif
  1168. /******************************************************************************/