FbxPropertyChannelPair.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. using Autodesk.Fbx;
  2. using System.Collections.Generic;
  3. namespace UnityEditor.Formats.Fbx.Exporter
  4. {
  5. /// <summary>
  6. /// Store FBX property name and channel name
  7. /// Default constructor added because it needs to be called before autoimplemented properties can be assigned. Otherwise we get build errors
  8. /// </summary>
  9. struct FbxPropertyChannelPair
  10. {
  11. public string Property { get; private set; }
  12. public string Channel { get; private set; }
  13. public FbxPropertyChannelPair(string p, string c) : this()
  14. {
  15. Property = p;
  16. Channel = c;
  17. }
  18. struct UnityPropertyChannelPair
  19. {
  20. public string property;
  21. public string channel;
  22. public UnityPropertyChannelPair(string p, string c)
  23. {
  24. property = p;
  25. channel = c;
  26. }
  27. }
  28. /// <summary>
  29. /// Contains the two dictionaries that map Unity property to FBX property and Unity channel to Fbx channel
  30. /// for a set of properties.
  31. /// </summary>
  32. struct PropertyChannelMap
  33. {
  34. public Dictionary<string, string> MapUnityPropToFbxProp;
  35. public Dictionary<string, string> MapUnityChannelToFbxChannel;
  36. public PropertyChannelMap(Dictionary<string,string> propertyMap, Dictionary<string, string> channelMap)
  37. {
  38. MapUnityPropToFbxProp = propertyMap;
  39. MapUnityChannelToFbxChannel = channelMap;
  40. }
  41. }
  42. // =========== Property Maps ================
  43. // These are dictionaries that map a Unity property name to it's corresponding Fbx property name.
  44. // Split up into multiple dictionaries as some are channel and object dependant.
  45. /// <summary>
  46. /// Map of Unity transform properties to their FBX equivalent.
  47. /// </summary>
  48. private static Dictionary<string, string> MapTransformPropToFbxProp = new Dictionary<string, string>()
  49. {
  50. { "m_LocalScale", "Lcl Scaling" },
  51. { "Motion S", "Lcl Scaling" },
  52. { "m_LocalPosition", "Lcl Translation" },
  53. { "Motion T", "Lcl Translation" },
  54. { "m_TranslationOffset", "Translation" },
  55. { "m_ScaleOffset", "Scaling" },
  56. { "m_RotationOffset", "Rotation" }
  57. };
  58. /// <summary>
  59. /// Map of Unity Aim constraint properties to their FBX equivalent.
  60. /// </summary>
  61. private static Dictionary<string, string> MapAimConstraintPropToFbxProp = new Dictionary<string, string>()
  62. {
  63. { "m_AimVector", "AimVector" },
  64. { "m_UpVector", "UpVector" },
  65. { "m_WorldUpVector", "WorldUpVector" },
  66. { "m_RotationOffset", "RotationOffset" }
  67. };
  68. /// <summary>
  69. /// Map of Unity color properties to their FBX equivalent.
  70. /// </summary>
  71. private static Dictionary<string, string> MapColorPropToFbxProp = new Dictionary<string, string>()
  72. {
  73. { "m_Color", "Color" }
  74. };
  75. /// <summary>
  76. /// Map of Unity properties to their FBX equivalent.
  77. /// </summary>
  78. private static Dictionary<string, string> MapPropToFbxProp = new Dictionary<string, string>()
  79. {
  80. { "m_Intensity", "Intensity" },
  81. { "field of view", "FieldOfView" },
  82. { "m_Weight", "Weight" },
  83. { "m_FocalLength", "FocalLength" },
  84. { "m_LensShift.x", "FilmOffsetX" },
  85. { "m_LensShift.y", "FilmOffsetY" }
  86. };
  87. /// <summary>
  88. /// Map of Unity constraint source property name as a regular expression to the FBX property as a string format.
  89. /// This is necessary because the Unity property contains an index in to an array, and the FBX property contains
  90. /// the name of the source object.
  91. /// </summary>
  92. private static Dictionary<string, string> MapConstraintSourcePropToFbxProp = new Dictionary<string, string>()
  93. {
  94. { @"m_Sources\.Array\.data\[(\d+)\]\.weight", "{0}.Weight" }
  95. };
  96. /// <summary>
  97. /// Map of Unity constraint source transform property name as a regular expression to the FBX property as a string format.
  98. /// This is necessary because the Unity property contains an index in to an array, and the FBX property contains
  99. /// the name of the source object.
  100. /// </summary>
  101. private static Dictionary<string, string> MapConstraintSourceTransformPropToFbxProp = new Dictionary<string, string>()
  102. {
  103. { @"m_TranslationOffsets\.Array\.data\[(\d+)\]", "{0}.Offset T" },
  104. { @"m_RotationOffsets\.Array\.data\[(\d+)\]", "{0}.Offset R" }
  105. };
  106. // ================== Channel Maps ======================
  107. /// <summary>
  108. /// Map of Unity transform channels to their FBX equivalent.
  109. /// </summary>
  110. private static Dictionary<string, string> MapTransformChannelToFbxChannel = new Dictionary<string, string>()
  111. {
  112. { "x", Globals.FBXSDK_CURVENODE_COMPONENT_X },
  113. { "y", Globals.FBXSDK_CURVENODE_COMPONENT_Y },
  114. { "z", Globals.FBXSDK_CURVENODE_COMPONENT_Z }
  115. };
  116. /// <summary>
  117. /// Map of Unity color channels to their FBX equivalent.
  118. /// </summary>
  119. private static Dictionary<string, string> MapColorChannelToFbxChannel = new Dictionary<string, string>()
  120. {
  121. { "b", Globals.FBXSDK_CURVENODE_COLOR_BLUE },
  122. { "g", Globals.FBXSDK_CURVENODE_COLOR_GREEN },
  123. { "r", Globals.FBXSDK_CURVENODE_COLOR_RED }
  124. };
  125. // =======================================================
  126. private static PropertyChannelMap TransformPropertyMap = new PropertyChannelMap(MapTransformPropToFbxProp, MapTransformChannelToFbxChannel);
  127. private static PropertyChannelMap AimConstraintPropertyMap = new PropertyChannelMap(MapAimConstraintPropToFbxProp, MapTransformChannelToFbxChannel);
  128. private static PropertyChannelMap ColorPropertyMap = new PropertyChannelMap(MapColorPropToFbxProp, MapColorChannelToFbxChannel);
  129. private static PropertyChannelMap ConstraintSourcePropertyMap = new PropertyChannelMap(MapConstraintSourcePropToFbxProp, null);
  130. private static PropertyChannelMap ConstraintSourceTransformPropertyMap = new PropertyChannelMap(MapConstraintSourceTransformPropToFbxProp, MapTransformChannelToFbxChannel);
  131. private static PropertyChannelMap OtherPropertyMap = new PropertyChannelMap(MapPropToFbxProp, null);
  132. /// <summary>
  133. /// Separates and returns the property and channel from the full Unity property name.
  134. ///
  135. /// Takes what is after the last period as the channel.
  136. /// In order to use this have to be certain that there are channels, as there are cases where what is after
  137. /// the last period is still the property name. E.g. m_Sources.Array.data[0].weight has no channel.
  138. /// </summary>
  139. /// <param name="fullPropertyName"></param>
  140. /// <returns></returns>
  141. private static UnityPropertyChannelPair GetUnityPropertyChannelPair(string fullPropertyName)
  142. {
  143. int index = fullPropertyName.LastIndexOf('.');
  144. if (index < 0)
  145. {
  146. return new UnityPropertyChannelPair(fullPropertyName, null);
  147. }
  148. var property = fullPropertyName.Substring(0, index);
  149. var channel = fullPropertyName.Substring(index + 1);
  150. return new UnityPropertyChannelPair(property, channel);
  151. }
  152. /// <summary>
  153. /// Get the Fbx property name for the given Unity property name from the given dictionary.
  154. /// </summary>
  155. /// <param name="uniProperty"></param>
  156. /// <param name="propertyMap"></param>
  157. /// <returns>The Fbx property name or null if there was no match in the dictionary</returns>
  158. private static string GetFbxProperty(string uniProperty, Dictionary<string, string> propertyMap)
  159. {
  160. string fbxProperty;
  161. if(!propertyMap.TryGetValue(uniProperty, out fbxProperty)){
  162. return null;
  163. }
  164. return fbxProperty;
  165. }
  166. /// <summary>
  167. /// Get the Fbx property name for the given Unity constraint source property name from the given dictionary.
  168. ///
  169. /// This is different from GetFbxProperty() because the Unity constraint source properties contain indices, and
  170. /// the Fbx constraint source property contains the name of the source object.
  171. /// </summary>
  172. /// <param name="uniProperty"></param>
  173. /// <param name="constraint"></param>
  174. /// <param name="propertyMap"></param>
  175. /// <returns>The Fbx property name or null if there was no match in the dictionary</returns>
  176. private static string GetFbxConstraintSourceProperty(string uniProperty, FbxConstraint constraint, Dictionary<string, string> propertyMap)
  177. {
  178. foreach (var prop in propertyMap)
  179. {
  180. var match = System.Text.RegularExpressions.Regex.Match(uniProperty, prop.Key);
  181. if (match.Success && match.Groups.Count > 0)
  182. {
  183. var matchedStr = match.Groups[1].Value;
  184. int index;
  185. if (!int.TryParse(matchedStr, out index))
  186. {
  187. continue;
  188. }
  189. var source = constraint.GetConstraintSource(index);
  190. return string.Format(prop.Value, source.GetName());
  191. }
  192. }
  193. return null;
  194. }
  195. /// <summary>
  196. /// Get the Fbx channel name for the given Unity channel from the given dictionary.
  197. /// </summary>
  198. /// <param name="uniChannel"></param>
  199. /// <param name="channelMap"></param>
  200. /// <returns>The Fbx channel name or null if there was no match in the dictionary</returns>
  201. private static string GetFbxChannel(string uniChannel, Dictionary<string, string> channelMap)
  202. {
  203. string fbxChannel;
  204. if(!channelMap.TryGetValue(uniChannel, out fbxChannel))
  205. {
  206. return null;
  207. }
  208. return fbxChannel;
  209. }
  210. /// <summary>
  211. /// Try to get the property channel pairs for the given Unity property from the given property channel mapping.
  212. /// </summary>
  213. /// <param name="uniPropertyName"></param>
  214. /// <param name="propertyChannelMap"></param>
  215. /// <param name="constraint"></param>
  216. /// <returns>The property channel pairs or null if there was no match</returns>
  217. private static FbxPropertyChannelPair[] GetChannelPairs(string uniPropertyName, PropertyChannelMap propertyChannelMap, FbxConstraint constraint = null)
  218. {
  219. // Unity property name is of the format "property.channel" or "property". Handle both cases.
  220. var possibleUniPropChannelPairs = new List<UnityPropertyChannelPair>();
  221. // could give same result as already in the list, avoid checking this case twice
  222. var propChannelPair = GetUnityPropertyChannelPair(uniPropertyName);
  223. possibleUniPropChannelPairs.Add(propChannelPair);
  224. if (propChannelPair.property != uniPropertyName)
  225. {
  226. possibleUniPropChannelPairs.Add(new UnityPropertyChannelPair(uniPropertyName, null));
  227. }
  228. foreach (var uniPropChannelPair in possibleUniPropChannelPairs)
  229. {
  230. // try to match property
  231. var fbxProperty = GetFbxProperty(uniPropChannelPair.property, propertyChannelMap.MapUnityPropToFbxProp);
  232. if (string.IsNullOrEmpty(fbxProperty) && constraint != null)
  233. {
  234. // check if it's a constraint source property
  235. fbxProperty = GetFbxConstraintSourceProperty(uniPropChannelPair.property, constraint, propertyChannelMap.MapUnityPropToFbxProp);
  236. }
  237. if (string.IsNullOrEmpty(fbxProperty))
  238. {
  239. continue;
  240. }
  241. // matched property, now try to match channel
  242. string fbxChannel = null;
  243. if(!string.IsNullOrEmpty(uniPropChannelPair.channel) && propertyChannelMap.MapUnityChannelToFbxChannel != null)
  244. {
  245. fbxChannel = GetFbxChannel(uniPropChannelPair.channel, propertyChannelMap.MapUnityChannelToFbxChannel);
  246. if (string.IsNullOrEmpty(fbxChannel))
  247. {
  248. // couldn't match the Unity channel to the fbx channel
  249. continue;
  250. }
  251. }
  252. return new FbxPropertyChannelPair[] { new FbxPropertyChannelPair(fbxProperty, fbxChannel) };
  253. }
  254. return null;
  255. }
  256. /// <summary>
  257. /// Map a Unity property name to the corresponding FBX property and
  258. /// channel names.
  259. /// </summary>
  260. public static bool TryGetValue(string uniPropertyName, out FbxPropertyChannelPair[] prop, FbxConstraint constraint = null)
  261. {
  262. prop = new FbxPropertyChannelPair[] { };
  263. // spot angle is a special case as it returns two channel pairs instead of one
  264. System.StringComparison ct = System.StringComparison.CurrentCulture;
  265. if (uniPropertyName.StartsWith("m_SpotAngle", ct))
  266. {
  267. prop = new FbxPropertyChannelPair[]{
  268. new FbxPropertyChannelPair ("OuterAngle", null),
  269. new FbxPropertyChannelPair ("InnerAngle", null)
  270. };
  271. return true;
  272. }
  273. var propertyMaps = new List<PropertyChannelMap>();
  274. // Try get constraint specific channel pairs first as we know this is a constraint
  275. if (constraint != null)
  276. {
  277. // Aim constraint shares the RotationOffset property with RotationConstraint, so make sure that the correct FBX property is returned
  278. if (constraint.GetConstraintType() == FbxConstraint.EType.eAim)
  279. {
  280. propertyMaps.Add(AimConstraintPropertyMap);
  281. }
  282. propertyMaps.Add(ConstraintSourcePropertyMap);
  283. propertyMaps.Add(ConstraintSourceTransformPropertyMap);
  284. }
  285. // Check if this is a transform, color, or other property and return the channel pairs if they match.
  286. propertyMaps.Add(TransformPropertyMap);
  287. propertyMaps.Add(ColorPropertyMap);
  288. propertyMaps.Add(OtherPropertyMap);
  289. foreach (var propMap in propertyMaps)
  290. {
  291. prop = GetChannelPairs(uniPropertyName, propMap, constraint);
  292. if (prop != null)
  293. {
  294. return true;
  295. }
  296. }
  297. return false;
  298. }
  299. }
  300. }