Skip to content

Commit 6dfb32e

Browse files
authored
Merge pull request #495 from Unity-Technologies/v3.0.0-preview
v3.0.0 preview
2 parents 85d69c9 + 0da9bcf commit 6dfb32e

File tree

8 files changed

+270
-55
lines changed

8 files changed

+270
-55
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changes in Fbx Exporter
22

3+
## [3.0.1-preview.2] - 2020-01-22
4+
### Added
5+
- Added option to export geometry when recording with the FBX recorder (in previous version geometry was always exported).
6+
- Added settings to transfer animation between two transforms in the recorded hierarchy.
7+
8+
### Fixed
9+
- It is now possible to record animated characters with the FBX recorder.
10+
311
## [3.0.0-preview.2] - 2020-01-13
412
### Added
513
- Added FBX Recorder to record animations from the Unity Recorder directly to FBX (adds dependency to Unity Recorder).
6.6 KB
Loading

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ Alternatively, the FBX Recorder can be added as a track in the Timeline.
1818

1919
| Property: | Function: |
2020
| :---------------------------- | :----------------------------------------------------------- |
21+
| __Export Geometry__ | Check this option to export the geometry of the recorded GameObject to FBX, if any. |
2122
| __File Name__ | The filename for the exported FBX. |
2223
| __Path__ | The path to export the FBX to. Can be outside of the Assets folder. |
2324
| __Take Number__ | The take number can be set and used in the filename. It automatically increments after each recording. |
2425
| __Game Object__ | Set the Game Object in the scene to record. |
2526
| __Recorded Target(s)__ | Select which components of the selected GameObject to record. |
2627
| __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: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2274,7 +2274,7 @@ private void TransferMotion(Transform source, Transform dest, float sampleRate,
22742274

22752275
for (int k = 0; k < 3; k++) {
22762276
posKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, (float)translation [k]);
2277-
rotKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, (float)rot [k]);
2277+
rotKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, rot [k]);
22782278
scaleKeyFrames [k][keyIndex] = new Keyframe(currSampleTime, (float)scale [k]);
22792279
}
22802280
keyIndex++;
@@ -2690,18 +2690,6 @@ internal int ExportAnimationOnly(
26902690
}
26912691
}
26922692

2693-
// export animation
2694-
// export default clip first
2695-
if (exportData.defaultClip != null) {
2696-
var defaultClip = exportData.defaultClip;
2697-
ExportAnimationClip (defaultClip, exportData.animationClips[defaultClip], fbxScene);
2698-
exportData.animationClips.Remove (defaultClip);
2699-
}
2700-
2701-
foreach (var animClip in exportData.animationClips) {
2702-
ExportAnimationClip (animClip.Key, animClip.Value, fbxScene);
2703-
}
2704-
27052693
return numObjectsExported;
27062694
}
27072695

@@ -2967,7 +2955,7 @@ internal static Dictionary<GameObject, IExportData> GetExportData(Object[] objec
29672955

29682956
Dictionary<GameObject, IExportData> exportData = new Dictionary<GameObject, IExportData>();
29692957

2970-
if (exportOptions.ModelAnimIncludeOption != ExportSettings.Include.Anim)
2958+
if (exportOptions.ModelAnimIncludeOption == ExportSettings.Include.Model)
29712959
{
29722960
return null;
29732961
}
@@ -3129,21 +3117,7 @@ private bool ExportComponents(FbxScene fbxScene, bool exportAnim = true)
31293117
}
31303118

31313119
ExportConstraints(unityGo, fbxScene, fbxNode);
3132-
3133-
// check if this object contains animation, keep track of it
3134-
// if it does
3135-
if (exportAnim && GameObjectHasAnimation (unityGo) ) {
3136-
animationNodes.Add (unityGo);
3137-
}
31383120
}
3139-
3140-
// export all GameObjects that have animation
3141-
if (animationNodes.Count > 0) {
3142-
foreach (var go in animationNodes) {
3143-
ExportAnimation (go, fbxScene);
3144-
}
3145-
}
3146-
31473121
return true;
31483122
}
31493123

@@ -3321,7 +3295,7 @@ internal int ExportAll (
33213295
}
33223296

33233297
try {
3324-
bool animOnly = exportData != null ;
3298+
bool animOnly = exportData != null && ExportOptions.ModelAnimIncludeOption == ExportSettings.Include.Anim;
33253299
bool status = false;
33263300
// Create the FBX manager
33273301
using (var fbxManager = FbxManager.Create ()) {
@@ -3462,6 +3436,37 @@ internal int ExportAll (
34623436
}
34633437
}
34643438

3439+
// Export animation if any
3440+
if (exportData != null)
3441+
{
3442+
foreach (var unityGo in revisedExportSet)
3443+
{
3444+
IExportData iData;
3445+
if (!exportData.TryGetValue(unityGo, out iData))
3446+
{
3447+
continue;
3448+
}
3449+
var data = iData as AnimationOnlyExportData;
3450+
if (data == null)
3451+
{
3452+
Debug.LogWarningFormat("FBX Exporter: no animation export data found for {0}", unityGo.name);
3453+
continue;
3454+
}
3455+
// export animation
3456+
// export default clip first
3457+
if (data.defaultClip != null)
3458+
{
3459+
var defaultClip = data.defaultClip;
3460+
ExportAnimationClip(defaultClip, data.animationClips[defaultClip], fbxScene);
3461+
data.animationClips.Remove(defaultClip);
3462+
}
3463+
3464+
foreach (var animClip in data.animationClips)
3465+
{
3466+
ExportAnimationClip(animClip.Key, animClip.Value, fbxScene);
3467+
}
3468+
}
3469+
}
34653470
// Set the scene's default camera.
34663471
SetDefaultCamera (fbxScene);
34673472

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

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,36 +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;
54+
if (!settings.ExportGeometry)
55+
{
56+
toInclude = ExportSettings.Include.Anim;
57+
}
6658
exportSettings.SetModelAnimIncludeOption(toInclude);
67-
ModelExporter.ExportObject(clipName, root, exportSettings);
6859

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);
6966

70-
if (hasAnimComponent)
71-
{
72-
AnimationUtility.SetAnimationClips(animator, prevAnimClips);
73-
}
74-
else
75-
{
76-
Object.DestroyImmediate(animator);
77-
}
7867
aInput.GameObjectRecorder.ResetRecording();
7968
}
8069
base.EndRecording(session);

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

Lines changed: 192 additions & 1 deletion
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;
@@ -9,6 +9,197 @@ namespace UnityEditor.Formats.Fbx.Exporter
99
[RecorderSettings(typeof(FbxRecorder), "FBX", "fbx_recorder")]
1010
internal class FbxRecorderSettings : RecorderSettings
1111
{
12+
[SerializeField] bool m_exportGeometry = true;
13+
public bool ExportGeometry
14+
{
15+
get
16+
{
17+
return m_exportGeometry;
18+
}
19+
set
20+
{
21+
m_exportGeometry = value;
22+
}
23+
}
24+
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+
12203
[SerializeField] AnimationInputSettings m_AnimationInputSettings = new AnimationInputSettings();
13204

14205
public AnimationInputSettings animationInputSettings

0 commit comments

Comments
 (0)