Skip to content

Commit 84ed33e

Browse files
authored
Merge pull request #494 from Unity-Technologies/UT-3147-add-transfer-anim-to-recorder
Ut 3147 add transfer anim to recorder
2 parents 2c7944d + 3508fd6 commit 84ed33e

File tree

6 files changed

+238
-55
lines changed

6 files changed

+238
-55
lines changed
-439 Bytes
Loading

com.unity.formats.fbx/Documentation~/recorder.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ Alternatively, the FBX Recorder can be added as a track in the Timeline.
2525
| __Game Object__ | Set the Game Object in the scene to record. |
2626
| __Recorded Target(s)__ | Select which components of the selected GameObject to record. |
2727
| __Recorded Hierarchy__ | Check to record other objects in the hierarchy of the GameObject in addition to the selected GameObject. |
28+
| __Source__ | Transfer the transform animation from this object to the __Destination__ transform. <br/><br/>**NOTES:**<br/> - __Source__ must be an ancestor of __Destination__<br/> - __Source__ may be an ancestor of the selected object. |
29+
| __Destination__ | Which object to transfer the transform animation to.<br/><br/>This object receives the transform animation on objects between __Source__ and __Destination__ as well as the animation on the Source itself. |

com.unity.formats.fbx/Editor/FbxExporter.cs

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,7 +2323,7 @@ private void TransferMotion(Transform source, Transform dest, float sampleRate,
23232323

23242324
for (int k = 0; k < 3; k++) {
23252325
posKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, (float)translation [k]);
2326-
rotKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, (float)rot [k]);
2326+
rotKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, rot [k]);
23272327
scaleKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, (float)scale [k]);
23282328
}
23292329
keyIndex++;
@@ -2734,18 +2734,6 @@ internal int ExportAnimationOnly(
27342734
}
27352735
}
27362736

2737-
// export animation
2738-
// export default clip first
2739-
if (exportData.defaultClip != null) {
2740-
var defaultClip = exportData.defaultClip;
2741-
ExportAnimationClip (defaultClip, exportData.animationClips[defaultClip], fbxScene);
2742-
exportData.animationClips.Remove (defaultClip);
2743-
}
2744-
2745-
foreach (var animClip in exportData.animationClips) {
2746-
ExportAnimationClip (animClip.Key, animClip.Value, fbxScene);
2747-
}
2748-
27492737
return numObjectsExported;
27502738
}
27512739

@@ -3012,7 +3000,7 @@ internal static Dictionary<GameObject, IExportData> GetExportData(Object[] objec
30123000

30133001
Dictionary<GameObject, IExportData> exportData = new Dictionary<GameObject, IExportData>();
30143002

3015-
if (exportOptions.ModelAnimIncludeOption != ExportSettings.Include.Anim)
3003+
if (exportOptions.ModelAnimIncludeOption == ExportSettings.Include.Model)
30163004
{
30173005
return null;
30183006
}
@@ -3174,21 +3162,7 @@ private bool ExportComponents(FbxScene fbxScene, bool exportAnim = true)
31743162
}
31753163

31763164
ExportConstraints(unityGo, fbxScene, fbxNode);
3177-
3178-
// check if this object contains animation, keep track of it
3179-
// if it does
3180-
if (exportAnim && GameObjectHasAnimation (unityGo) ) {
3181-
animationNodes.Add (unityGo);
3182-
}
31833165
}
3184-
3185-
// export all GameObjects that have animation
3186-
if (animationNodes.Count > 0) {
3187-
foreach (var go in animationNodes) {
3188-
ExportAnimation (go, fbxScene);
3189-
}
3190-
}
3191-
31923166
return true;
31933167
}
31943168

@@ -3366,7 +3340,7 @@ internal int ExportAll (
33663340
}
33673341

33683342
try {
3369-
bool animOnly = exportData != null ;
3343+
bool animOnly = exportData != null && ExportOptions.ModelAnimIncludeOption == ExportSettings.Include.Anim;
33703344
bool status = false;
33713345
// Create the FBX manager
33723346
using (var fbxManager = FbxManager.Create ()) {
@@ -3508,6 +3482,33 @@ internal int ExportAll (
35083482
}
35093483
}
35103484

3485+
foreach (var unityGo in revisedExportSet)
3486+
{
3487+
IExportData iData;
3488+
if(!exportData.TryGetValue(unityGo, out iData))
3489+
{
3490+
continue;
3491+
}
3492+
var data = iData as AnimationOnlyExportData;
3493+
if(data == null)
3494+
{
3495+
Debug.LogWarningFormat("FBX Exporter: no animation export data found for {0}", unityGo.name);
3496+
continue;
3497+
}
3498+
// export animation
3499+
// export default clip first
3500+
if (data.defaultClip != null)
3501+
{
3502+
var defaultClip = data.defaultClip;
3503+
ExportAnimationClip(defaultClip, data.animationClips[defaultClip], fbxScene);
3504+
data.animationClips.Remove(defaultClip);
3505+
}
3506+
3507+
foreach (var animClip in data.animationClips)
3508+
{
3509+
ExportAnimationClip(animClip.Key, animClip.Value, fbxScene);
3510+
}
3511+
}
35113512
// Set the scene's default camera.
35123513
SetDefaultCamera (fbxScene);
35133514

com.unity.formats.fbx/Editor/Sources/Recorders/FbxRecorder/FbxRecorder.cs

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,40 +45,25 @@ protected override void EndRecording(RecordingSession session)
4545
#endif
4646
var root = ((AnimationInputSettings)aInput.settings).gameObject;
4747
clip.name = "recorded_clip";
48-
clip.legacy = true;
49-
Animation animator = root.GetComponent<Animation>();
50-
bool hasAnimComponent = true;
51-
if (!animator)
52-
{
53-
animator = root.AddComponent<Animation>();
54-
hasAnimComponent = false;
55-
}
56-
57-
AnimationClip[] prevAnimClips = null;
58-
if (hasAnimComponent)
59-
{
60-
prevAnimClips = AnimationUtility.GetAnimationClips(root);
61-
}
6248

63-
AnimationUtility.SetAnimationClips(animator, new AnimationClip[] { clip });
6449
var exportSettings = new ExportModelSettingsSerialize();
50+
exportSettings.SetAnimationSource(settings.TransferAnimationSource);
51+
exportSettings.SetAnimationDest(settings.TransferAnimationDest);
52+
exportSettings.SetObjectPosition(ExportSettings.ObjectPosition.WorldAbsolute);
6553
var toInclude = ExportSettings.Include.ModelAndAnim;
6654
if (!settings.ExportGeometry)
6755
{
6856
toInclude = ExportSettings.Include.Anim;
6957
}
7058
exportSettings.SetModelAnimIncludeOption(toInclude);
71-
ModelExporter.ExportObject(clipName, root, exportSettings);
7259

60+
var exportData = new AnimationOnlyExportData();
61+
exportData.CollectDependencies(clip, root, exportSettings);
62+
var exportDataContainer = new Dictionary<GameObject, IExportData>();
63+
exportDataContainer.Add(root, exportData);
64+
65+
ModelExporter.ExportObjects(clipName, new UnityEngine.Object[] { root }, exportSettings, exportDataContainer);
7366

74-
if (hasAnimComponent)
75-
{
76-
AnimationUtility.SetAnimationClips(animator, prevAnimClips);
77-
}
78-
else
79-
{
80-
Object.DestroyImmediate(animator);
81-
}
8267
aInput.GameObjectRecorder.ResetRecording();
8368
}
8469
base.EndRecording(session);

com.unity.formats.fbx/Editor/Sources/Recorders/FbxRecorder/FbxRecorderSettings.cs

Lines changed: 180 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections;
1+
using System;
22
using System.Collections.Generic;
33
using UnityEngine;
44
using UnityEditor.Recorder;
@@ -7,7 +7,7 @@
77
namespace UnityEditor.Formats.Fbx.Exporter
88
{
99
[RecorderSettings(typeof(FbxRecorder), "FBX", "fbx_recorder")]
10-
public class FbxRecorderSettings : RecorderSettings
10+
internal class FbxRecorderSettings : RecorderSettings
1111
{
1212
[SerializeField] bool m_exportGeometry = true;
1313
public bool ExportGeometry
@@ -22,6 +22,184 @@ public bool ExportGeometry
2222
}
2323
}
2424

25+
[SerializeField]
26+
private string m_animSourceBindingId;
27+
[SerializeField]
28+
private string m_animDestBindingId;
29+
30+
public Transform TransferAnimationSource
31+
{
32+
get
33+
{
34+
if (string.IsNullOrEmpty(m_animSourceBindingId))
35+
return null;
36+
37+
return GetBinding(m_animSourceBindingId);
38+
}
39+
40+
set
41+
{
42+
if (!TransferAnimationSourceIsValid(value))
43+
{
44+
return;
45+
}
46+
if (string.IsNullOrEmpty(m_animSourceBindingId))
47+
m_animSourceBindingId = GenerateBindingId();
48+
49+
SetBinding(m_animSourceBindingId, value);
50+
}
51+
}
52+
53+
public Transform TransferAnimationDest
54+
{
55+
get
56+
{
57+
if (string.IsNullOrEmpty(m_animDestBindingId))
58+
return null;
59+
60+
return GetBinding(m_animDestBindingId);
61+
}
62+
63+
set
64+
{
65+
if (!TransferAnimationDestIsValid(value))
66+
{
67+
return;
68+
}
69+
if (string.IsNullOrEmpty(m_animDestBindingId))
70+
m_animDestBindingId = GenerateBindingId();
71+
72+
SetBinding(m_animDestBindingId, value);
73+
}
74+
}
75+
76+
/// <summary>
77+
/// Get a binding ID for the transform, so that the reference survives domain reload. Maintaining a direct reference would not work
78+
/// as all scene objects are destroyed and recreated on reload (e.g. when entering/exiting playmode).
79+
/// </summary>
80+
/// <returns>Binding ID</returns>
81+
static string GenerateBindingId()
82+
{
83+
return GUID.Generate().ToString();
84+
}
85+
86+
/// <summary>
87+
/// Get the Unity object (in this case transform), associated with the given binding ID.
88+
/// </summary>
89+
/// <param name="id">Binding ID</param>
90+
/// <returns>Transform associated with binding ID</returns>
91+
static Transform GetBinding(string id)
92+
{
93+
// return BindingManager.Get(m_BindingId) as GameObject;
94+
var method = Type.GetType("UnityEditor.Recorder.BindingManager,Unity.Recorder.Editor").GetMethod("Get", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
95+
return method.Invoke(null, new object[] { id }) as Transform;
96+
}
97+
98+
/// <summary>
99+
/// Set the binding ID to be associated with the given Unity object.
100+
/// This information will be saved on domain reload, so that the object can still be found
101+
/// with the binding ID.
102+
/// </summary>
103+
/// <param name="id">Binding ID</param>
104+
/// <param name="obj">Unity Object</param>
105+
static void SetBinding(string id, UnityEngine.Object obj)
106+
{
107+
// BindingManager.Set(m_BindingId, value);
108+
var method = Type.GetType("UnityEditor.Recorder.BindingManager,Unity.Recorder.Editor").GetMethod("Set", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
109+
method.Invoke(null, new object[] { id, obj });
110+
}
111+
112+
/// <summary>
113+
/// Determines whether p is an ancestor to t.
114+
/// </summary>
115+
/// <returns><c>true</c> if p is ancestor to t; otherwise, <c>false</c>.</returns>
116+
/// <param name="p">P.</param>
117+
/// <param name="t">T.</param>
118+
protected bool IsAncestor(Transform p, Transform t)
119+
{
120+
var curr = t;
121+
while (curr != null)
122+
{
123+
if (curr == p)
124+
{
125+
return true;
126+
}
127+
curr = curr.parent;
128+
}
129+
return false;
130+
}
131+
132+
/// <summary>
133+
/// Determines whether t1 and t2 are in the same hierarchy.
134+
/// </summary>
135+
/// <returns><c>true</c> if t1 is in same hierarchy as t2; otherwise, <c>false</c>.</returns>
136+
/// <param name="t1">T1.</param>
137+
/// <param name="t2">T2.</param>
138+
protected bool IsInSameHierarchy(Transform t1, Transform t2)
139+
{
140+
return (IsAncestor(t1, t2) || IsAncestor(t2, t1));
141+
}
142+
143+
protected bool TransferAnimationSourceIsValid(Transform newValue)
144+
{
145+
if (!newValue)
146+
{
147+
return true;
148+
}
149+
150+
var selectedGO = m_AnimationInputSettings.gameObject;
151+
152+
if (!selectedGO)
153+
{
154+
Debug.LogWarning("FbxRecorderSettings: no Objects selected for export, can't transfer animation");
155+
return false;
156+
}
157+
158+
// source must be ancestor to dest
159+
if (TransferAnimationDest && !IsAncestor(newValue, TransferAnimationDest))
160+
{
161+
Debug.LogWarningFormat("FbxRecorderSettings: Source {0} must be an ancestor of {1}", newValue.name, TransferAnimationDest.name);
162+
return false;
163+
}
164+
// must be in same hierarchy as selected GO
165+
if (!selectedGO || !IsInSameHierarchy(newValue, selectedGO.transform))
166+
{
167+
Debug.LogWarningFormat("FbxRecorderSettings: Source {0} must be in the same hierarchy as {1}", newValue.name, selectedGO ? selectedGO.name : "the selected object");
168+
return false;
169+
}
170+
return true;
171+
}
172+
173+
protected bool TransferAnimationDestIsValid(Transform newValue)
174+
{
175+
if (!newValue)
176+
{
177+
return true;
178+
}
179+
180+
var selectedGO = m_AnimationInputSettings.gameObject;
181+
182+
if (!selectedGO)
183+
{
184+
Debug.LogWarning("FbxRecorderSettings: no Objects selected for export, can't transfer animation");
185+
return false;
186+
}
187+
188+
// source must be ancestor to dest
189+
if (TransferAnimationSource && !IsAncestor(TransferAnimationSource, newValue))
190+
{
191+
Debug.LogWarningFormat("FbxRecorderSettings: Destination {0} must be a descendant of {1}", newValue.name, TransferAnimationSource.name);
192+
return false;
193+
}
194+
// must be in same hierarchy as selected GO
195+
if (!selectedGO || !IsInSameHierarchy(newValue, selectedGO.transform))
196+
{
197+
Debug.LogWarningFormat("FbxRecorderSettings: Destination {0} must be in the same hierarchy as {1}", newValue.name, selectedGO ? selectedGO.name : "the selected object");
198+
return false;
199+
}
200+
return true;
201+
}
202+
25203
[SerializeField] AnimationInputSettings m_AnimationInputSettings = new AnimationInputSettings();
26204

27205
public AnimationInputSettings animationInputSettings

com.unity.formats.fbx/Editor/Sources/Recorders/FbxRecorder/FbxRecorderSettingsEditor.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,22 @@ protected override void FileTypeAndFormatGUI()
1717

1818
settings.ExportGeometry = EditorGUILayout.Toggle("Export Geometry", settings.ExportGeometry);
1919
}
20+
21+
protected override void OnEncodingGui()
22+
{
23+
base.OnEncodingGui();
24+
25+
DrawSeparator();
26+
27+
EditorGUILayout.LabelField(new GUIContent(
28+
"Transfer Animation",
29+
"Transfer transform animation from source to destination. Animation on objects between source and destination will also be transferred to destination."
30+
));
31+
32+
FbxRecorderSettings settings = target as FbxRecorderSettings;
33+
34+
settings.TransferAnimationSource = EditorGUILayout.ObjectField("Source", settings.TransferAnimationSource, typeof(Transform), allowSceneObjects: true) as Transform;
35+
settings.TransferAnimationDest = EditorGUILayout.ObjectField("Destination", settings.TransferAnimationDest, typeof(Transform), allowSceneObjects: true) as Transform;
36+
}
2037
}
2138
}

0 commit comments

Comments
 (0)