using Autodesk.Fbx;
using System.Collections.Generic;
namespace UnityEditor.Formats.Fbx.Exporter
{
///
/// Store FBX property name and channel name
/// Default constructor added because it needs to be called before autoimplemented properties can be assigned. Otherwise we get build errors
///
struct FbxPropertyChannelPair
{
public string Property { get; private set; }
public string Channel { get; private set; }
public FbxPropertyChannelPair(string p, string c) : this()
{
Property = p;
Channel = c;
}
struct UnityPropertyChannelPair
{
public string property;
public string channel;
public UnityPropertyChannelPair(string p, string c)
{
property = p;
channel = c;
}
}
///
/// Contains the two dictionaries that map Unity property to FBX property and Unity channel to Fbx channel
/// for a set of properties.
///
struct PropertyChannelMap
{
public Dictionary MapUnityPropToFbxProp;
public Dictionary MapUnityChannelToFbxChannel;
public PropertyChannelMap(Dictionary propertyMap, Dictionary channelMap)
{
MapUnityPropToFbxProp = propertyMap;
MapUnityChannelToFbxChannel = channelMap;
}
}
// =========== Property Maps ================
// These are dictionaries that map a Unity property name to it's corresponding Fbx property name.
// Split up into multiple dictionaries as some are channel and object dependant.
///
/// Map of Unity transform properties to their FBX equivalent.
///
private static Dictionary MapTransformPropToFbxProp = new Dictionary()
{
{ "m_LocalScale", "Lcl Scaling" },
{ "Motion S", "Lcl Scaling" },
{ "m_LocalPosition", "Lcl Translation" },
{ "Motion T", "Lcl Translation" },
{ "m_TranslationOffset", "Translation" },
{ "m_ScaleOffset", "Scaling" },
{ "m_RotationOffset", "Rotation" }
};
///
/// Map of Unity Aim constraint properties to their FBX equivalent.
///
private static Dictionary MapAimConstraintPropToFbxProp = new Dictionary()
{
{ "m_AimVector", "AimVector" },
{ "m_UpVector", "UpVector" },
{ "m_WorldUpVector", "WorldUpVector" },
{ "m_RotationOffset", "RotationOffset" }
};
///
/// Map of Unity color properties to their FBX equivalent.
///
private static Dictionary MapColorPropToFbxProp = new Dictionary()
{
{ "m_Color", "Color" }
};
///
/// Map of Unity properties to their FBX equivalent.
///
private static Dictionary MapPropToFbxProp = new Dictionary()
{
{ "m_Intensity", "Intensity" },
{ "field of view", "FieldOfView" },
{ "m_Weight", "Weight" },
{ "m_FocalLength", "FocalLength" },
{ "m_LensShift.x", "FilmOffsetX" },
{ "m_LensShift.y", "FilmOffsetY" }
};
///
/// Map of Unity constraint source property name as a regular expression to the FBX property as a string format.
/// This is necessary because the Unity property contains an index in to an array, and the FBX property contains
/// the name of the source object.
///
private static Dictionary MapConstraintSourcePropToFbxProp = new Dictionary()
{
{ @"m_Sources\.Array\.data\[(\d+)\]\.weight", "{0}.Weight" }
};
///
/// Map of Unity constraint source transform property name as a regular expression to the FBX property as a string format.
/// This is necessary because the Unity property contains an index in to an array, and the FBX property contains
/// the name of the source object.
///
private static Dictionary MapConstraintSourceTransformPropToFbxProp = new Dictionary()
{
{ @"m_TranslationOffsets\.Array\.data\[(\d+)\]", "{0}.Offset T" },
{ @"m_RotationOffsets\.Array\.data\[(\d+)\]", "{0}.Offset R" }
};
// ================== Channel Maps ======================
///
/// Map of Unity transform channels to their FBX equivalent.
///
private static Dictionary MapTransformChannelToFbxChannel = new Dictionary()
{
{ "x", Globals.FBXSDK_CURVENODE_COMPONENT_X },
{ "y", Globals.FBXSDK_CURVENODE_COMPONENT_Y },
{ "z", Globals.FBXSDK_CURVENODE_COMPONENT_Z }
};
///
/// Map of Unity color channels to their FBX equivalent.
///
private static Dictionary MapColorChannelToFbxChannel = new Dictionary()
{
{ "b", Globals.FBXSDK_CURVENODE_COLOR_BLUE },
{ "g", Globals.FBXSDK_CURVENODE_COLOR_GREEN },
{ "r", Globals.FBXSDK_CURVENODE_COLOR_RED }
};
// =======================================================
private static PropertyChannelMap TransformPropertyMap = new PropertyChannelMap(MapTransformPropToFbxProp, MapTransformChannelToFbxChannel);
private static PropertyChannelMap AimConstraintPropertyMap = new PropertyChannelMap(MapAimConstraintPropToFbxProp, MapTransformChannelToFbxChannel);
private static PropertyChannelMap ColorPropertyMap = new PropertyChannelMap(MapColorPropToFbxProp, MapColorChannelToFbxChannel);
private static PropertyChannelMap ConstraintSourcePropertyMap = new PropertyChannelMap(MapConstraintSourcePropToFbxProp, null);
private static PropertyChannelMap ConstraintSourceTransformPropertyMap = new PropertyChannelMap(MapConstraintSourceTransformPropToFbxProp, MapTransformChannelToFbxChannel);
private static PropertyChannelMap OtherPropertyMap = new PropertyChannelMap(MapPropToFbxProp, null);
///
/// Separates and returns the property and channel from the full Unity property name.
///
/// Takes what is after the last period as the channel.
/// In order to use this have to be certain that there are channels, as there are cases where what is after
/// the last period is still the property name. E.g. m_Sources.Array.data[0].weight has no channel.
///
///
///
private static UnityPropertyChannelPair GetUnityPropertyChannelPair(string fullPropertyName)
{
int index = fullPropertyName.LastIndexOf('.');
if (index < 0)
{
return new UnityPropertyChannelPair(fullPropertyName, null);
}
var property = fullPropertyName.Substring(0, index);
var channel = fullPropertyName.Substring(index + 1);
return new UnityPropertyChannelPair(property, channel);
}
///
/// Get the Fbx property name for the given Unity property name from the given dictionary.
///
///
///
/// The Fbx property name or null if there was no match in the dictionary
private static string GetFbxProperty(string uniProperty, Dictionary propertyMap)
{
string fbxProperty;
if(!propertyMap.TryGetValue(uniProperty, out fbxProperty)){
return null;
}
return fbxProperty;
}
///
/// Get the Fbx property name for the given Unity constraint source property name from the given dictionary.
///
/// This is different from GetFbxProperty() because the Unity constraint source properties contain indices, and
/// the Fbx constraint source property contains the name of the source object.
///
///
///
///
/// The Fbx property name or null if there was no match in the dictionary
private static string GetFbxConstraintSourceProperty(string uniProperty, FbxConstraint constraint, Dictionary propertyMap)
{
foreach (var prop in propertyMap)
{
var match = System.Text.RegularExpressions.Regex.Match(uniProperty, prop.Key);
if (match.Success && match.Groups.Count > 0)
{
var matchedStr = match.Groups[1].Value;
int index;
if (!int.TryParse(matchedStr, out index))
{
continue;
}
var source = constraint.GetConstraintSource(index);
return string.Format(prop.Value, source.GetName());
}
}
return null;
}
///
/// Get the Fbx channel name for the given Unity channel from the given dictionary.
///
///
///
/// The Fbx channel name or null if there was no match in the dictionary
private static string GetFbxChannel(string uniChannel, Dictionary channelMap)
{
string fbxChannel;
if(!channelMap.TryGetValue(uniChannel, out fbxChannel))
{
return null;
}
return fbxChannel;
}
///
/// Try to get the property channel pairs for the given Unity property from the given property channel mapping.
///
///
///
///
/// The property channel pairs or null if there was no match
private static FbxPropertyChannelPair[] GetChannelPairs(string uniPropertyName, PropertyChannelMap propertyChannelMap, FbxConstraint constraint = null)
{
// Unity property name is of the format "property.channel" or "property". Handle both cases.
var possibleUniPropChannelPairs = new List();
// could give same result as already in the list, avoid checking this case twice
var propChannelPair = GetUnityPropertyChannelPair(uniPropertyName);
possibleUniPropChannelPairs.Add(propChannelPair);
if (propChannelPair.property != uniPropertyName)
{
possibleUniPropChannelPairs.Add(new UnityPropertyChannelPair(uniPropertyName, null));
}
foreach (var uniPropChannelPair in possibleUniPropChannelPairs)
{
// try to match property
var fbxProperty = GetFbxProperty(uniPropChannelPair.property, propertyChannelMap.MapUnityPropToFbxProp);
if (string.IsNullOrEmpty(fbxProperty) && constraint != null)
{
// check if it's a constraint source property
fbxProperty = GetFbxConstraintSourceProperty(uniPropChannelPair.property, constraint, propertyChannelMap.MapUnityPropToFbxProp);
}
if (string.IsNullOrEmpty(fbxProperty))
{
continue;
}
// matched property, now try to match channel
string fbxChannel = null;
if(!string.IsNullOrEmpty(uniPropChannelPair.channel) && propertyChannelMap.MapUnityChannelToFbxChannel != null)
{
fbxChannel = GetFbxChannel(uniPropChannelPair.channel, propertyChannelMap.MapUnityChannelToFbxChannel);
if (string.IsNullOrEmpty(fbxChannel))
{
// couldn't match the Unity channel to the fbx channel
continue;
}
}
return new FbxPropertyChannelPair[] { new FbxPropertyChannelPair(fbxProperty, fbxChannel) };
}
return null;
}
///
/// Map a Unity property name to the corresponding FBX property and
/// channel names.
///
public static bool TryGetValue(string uniPropertyName, out FbxPropertyChannelPair[] prop, FbxConstraint constraint = null)
{
prop = new FbxPropertyChannelPair[] { };
// spot angle is a special case as it returns two channel pairs instead of one
System.StringComparison ct = System.StringComparison.CurrentCulture;
if (uniPropertyName.StartsWith("m_SpotAngle", ct))
{
prop = new FbxPropertyChannelPair[]{
new FbxPropertyChannelPair ("OuterAngle", null),
new FbxPropertyChannelPair ("InnerAngle", null)
};
return true;
}
var propertyMaps = new List();
// Try get constraint specific channel pairs first as we know this is a constraint
if (constraint != null)
{
// Aim constraint shares the RotationOffset property with RotationConstraint, so make sure that the correct FBX property is returned
if (constraint.GetConstraintType() == FbxConstraint.EType.eAim)
{
propertyMaps.Add(AimConstraintPropertyMap);
}
propertyMaps.Add(ConstraintSourcePropertyMap);
propertyMaps.Add(ConstraintSourceTransformPropertyMap);
}
// Check if this is a transform, color, or other property and return the channel pairs if they match.
propertyMaps.Add(TransformPropertyMap);
propertyMaps.Add(ColorPropertyMap);
propertyMaps.Add(OtherPropertyMap);
foreach (var propMap in propertyMaps)
{
prop = GetChannelPairs(uniPropertyName, propMap, constraint);
if (prop != null)
{
return true;
}
}
return false;
}
}
}