Skip to content

Commit b1ad33d

Browse files
author
Benoit Hudson
committed
Fix two bugs found by tests (and fix tests).
- Ignore the root transform values, we don't want to drive them. - Don't store the FbxPrefab, we don't want it driven by the FBX. - Comparing FbxRepresentation is much more complicated now, because the Transform isn't exact after the roundtrip. Only affects the test. - Added some debug logs in the test.
1 parent 4be9458 commit b1ad33d

File tree

2 files changed

+96
-36
lines changed

2 files changed

+96
-36
lines changed

Assets/FbxExporters/Editor/UnitTests/FbxPrefabTest.cs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using UnityEditor;
33
using UnityEngine.TestTools;
44
using NUnit.Framework;
5+
using System.Collections.Generic;
56

67
namespace FbxExporters.UnitTests
78
{
@@ -17,7 +18,61 @@ public class FbxPrefabTest : ExporterTestBase
1718
public static void AssertAreIdentical(
1819
FbxPrefab.FbxRepresentation a,
1920
FbxPrefab.FbxRepresentation b) {
20-
Assert.AreEqual(a.ToJson(), b.ToJson());
21+
// A bit of a laborious comparison scheme. This is due to the
22+
// round-trip through FBX causing tiny errors in the transforms.
23+
var astack = new List<FbxPrefab.FbxRepresentation> ();
24+
astack.Add(a);
25+
var bstack = new List<FbxPrefab.FbxRepresentation> ();
26+
bstack.Add(b);
27+
28+
var aDummy = new GameObject("aDummy").transform;
29+
var bDummy = new GameObject("bDummy").transform;
30+
while (astack.Count > 0) {
31+
Assert.AreEqual(astack.Count, bstack.Count); // should never fail
32+
a = astack[astack.Count - 1]; astack.RemoveAt(astack.Count - 1);
33+
b = bstack[bstack.Count - 1]; bstack.RemoveAt(bstack.Count - 1);
34+
35+
// Verify that they have the same children (by name).
36+
var achildren = a.ChildNames;
37+
var bchildren = b.ChildNames;
38+
Assert.That(achildren, Is.EquivalentTo(bchildren));
39+
40+
// Add the children to each stack.
41+
foreach(var child in achildren) {
42+
astack.Add(a.GetChild(child));
43+
bstack.Add(b.GetChild(child));
44+
}
45+
46+
// Verify that they have the same components.
47+
var atypes = a.ComponentTypes;
48+
var btypes = b.ComponentTypes;
49+
Assert.That(atypes, Is.EquivalentTo(btypes));
50+
51+
foreach(var t in atypes) {
52+
var avalues = a.GetComponentValues(t);
53+
var bvalues = b.GetComponentValues(t);
54+
Assert.AreEqual(avalues.Count, bvalues.Count);
55+
56+
if (t != "UnityEngine.Transform") {
57+
Assert.AreEqual(avalues, bvalues);
58+
} else {
59+
// Verify that the transforms are nearly (but don't require bitwise) equal.
60+
EditorJsonUtility.FromJsonOverwrite(avalues[0], aDummy);
61+
EditorJsonUtility.FromJsonOverwrite(bvalues[0], bDummy);
62+
var dist = Vector3.Distance(aDummy.localPosition, bDummy.localPosition);
63+
Assert.That(dist, Is.LessThan(1e-6), () => string.Format("position {0} vs {1} dist {2}",
64+
aDummy.localPosition, bDummy.localPosition, dist));
65+
66+
dist = Vector3.Distance(aDummy.localScale, bDummy.localScale);
67+
Assert.That(dist, Is.LessThan(1e-6), () => string.Format("scale {0} vs {1} dist {2}",
68+
aDummy.localScale, bDummy.localScale, dist));
69+
70+
dist = Quaternion.Angle(aDummy.localRotation, bDummy.localRotation);
71+
Assert.That(dist, Is.LessThan(1e-6), () => string.Format("rotation {0} vs {1} angle {2}",
72+
aDummy.localRotation.eulerAngles, bDummy.localRotation.eulerAngles, dist));
73+
}
74+
}
75+
}
2176
}
2277

2378
public static void AssertAreDifferent(
@@ -72,7 +127,6 @@ FbxPrefab.FbxRepresentation History(GameObject go) {
72127

73128
GameObject ModifySourceFbx()
74129
{
75-
76130
// Modify the source fbx file:
77131
// - delete parent1
78132
// - add parent3
@@ -101,6 +155,7 @@ public void BasicTest() {
101155
AssertAreIdentical(m_originalRep, History(m_manualPrefab));
102156
AssertAreIdentical(m_originalRep, History(m_autoPrefab));
103157

158+
Debug.Log("Testing auto update");
104159
var newHierarchy = Rep(ModifySourceFbx());
105160
AssertAreDifferent(m_originalRep, newHierarchy);
106161

@@ -117,6 +172,7 @@ public void BasicTest() {
117172
AssertAreIdentical(m_originalRep, History(m_manualPrefab));
118173

119174
// Manual update, make sure it updated.
175+
Debug.Log("Testing manual update");
120176
var manualPrefabComponent = m_manualPrefab.GetComponent<FbxPrefab>();
121177
manualPrefabComponent.SyncPrefab();
122178
AssertAreIdentical(newHierarchy, Rep(m_manualPrefab));
@@ -128,14 +184,17 @@ public void BasicTest() {
128184
// Illegal to set the source model to something that isn't an
129185
// asset.
130186
var go = CreateGameObject("foo");
187+
Debug.Log("Testing SetSourceModel to scene object");
131188
Assert.That( () => manualPrefabComponent.SetSourceModel(go), Throws.Exception );
132189

133190
// Illegal to set the source model to something that isn't an fbx
134191
// asset (it's a prefab).
192+
Debug.Log("Testing SetSourceModel to prefab");
135193
Assert.That( () => manualPrefabComponent.SetSourceModel(m_autoPrefab), Throws.Exception );
136194

137195
// Legal to set the source model to null. It doesn't change the
138196
// hierarchy or anything.
197+
Debug.Log("Testing SetSourceModel to null");
139198
Assert.That( () => manualPrefabComponent.SetSourceModel(null), Throws.Nothing );
140199
Assert.IsNull(manualPrefabComponent.GetFbxAsset());
141200
AssertAreIdentical(newHierarchy, Rep(m_manualPrefab));
@@ -151,6 +210,7 @@ public void BasicTest() {
151210
GetRandomFbxFilePath(), m_original);
152211
var newSource = AssetDatabase.LoadMainAssetAtPath(fbxAsset) as GameObject;
153212
Assert.IsTrue(newSource);
213+
Debug.Log("Testing SetSourceModel relink");
154214
manualPrefabComponent.SetSourceModel(newSource);
155215
AssertAreIdentical(m_originalRep, Rep(m_manualPrefab));
156216
AssertAreIdentical(m_originalRep, History(m_manualPrefab));

Assets/FbxExporters/FbxPrefab.cs

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -145,25 +145,34 @@ public class FbxRepresentation
145145
/// The key is the name, which is assumed to be unique.
146146
/// The value is, recursively, the representation of that subtree.
147147
/// </summary>
148-
Dictionary<string, FbxRepresentation> m_children;
148+
Dictionary<string, FbxRepresentation> m_children = new Dictionary<string, FbxRepresentation>();
149149

150150
/// <summary>
151-
/// Children of this node.
151+
/// Components of this node.
152152
/// The key is the name of the type of the Component. We accept that there may be several.
153153
/// The value is the json for the component, to be decoded with EditorJsonUtility.
154+
///
155+
/// Note that we skip the FbxPrefab component because we never want to update that
156+
/// automatically.
154157
/// </summary>
155-
Dictionary<string, List<string>> m_components;
158+
Dictionary<string, List<string>> m_components = new Dictionary<string, List<string>>();
156159

157160
/// <summary>
158161
/// Build a hierarchical representation based on a transform.
159162
/// </summary>
160-
public FbxRepresentation(Transform xfo)
163+
public FbxRepresentation(Transform xfo, bool isRoot = true)
161164
{
162165
m_children = new Dictionary<string, FbxRepresentation>();
163166
foreach(Transform child in xfo) {
164-
m_children.Add(child.name, new FbxRepresentation(child));
167+
m_children.Add(child.name, new FbxRepresentation(child, isRoot: false));
165168
}
166169
foreach(var component in xfo.GetComponents<Component>()) {
170+
// Don't save the prefab link, to avoid a logic loop.
171+
if (component is FbxPrefab) { continue; }
172+
173+
// Don't save the root transform, to allow importing at a place other than zero.
174+
if (isRoot && component is Transform) { continue; }
175+
167176
var typeName = component.GetType().ToString();
168177
var jsonValue = UnityEditor.EditorJsonUtility.ToJson(component);
169178
Append(ref m_components, typeName, jsonValue);
@@ -309,32 +318,22 @@ public string ToJson() {
309318
return builder.ToString();
310319
}
311320

312-
public static bool IsLeaf(FbxRepresentation rep) {
313-
return rep == null || rep.m_children == null;
314-
}
315-
316-
public static HashSet<string> GetChildren(FbxRepresentation rep) {
317-
if (IsLeaf(rep)) {
318-
return new HashSet<string>();
319-
} else {
320-
return new HashSet<string>(rep.m_children.Keys);
321-
}
322-
}
321+
public HashSet<string> ChildNames { get { return new HashSet<string> (m_children.Keys); } }
323322

324-
public static IEnumerable<KeyValuePair<string, List<string>>> GetComponents(FbxRepresentation rep) {
325-
if (rep == null || rep.m_components == null) {
326-
return new KeyValuePair<string, List<string>>[0];
327-
} else {
328-
return rep.m_components;
323+
public FbxRepresentation GetChild(string childName) {
324+
FbxRepresentation child;
325+
if (m_children.TryGetValue(childName, out child)) {
326+
return child;
329327
}
328+
return null;
330329
}
331330

332-
public static FbxRepresentation Find(FbxRepresentation rep, string key) {
333-
if (IsLeaf(rep)) { return null; }
331+
public HashSet<string> ComponentTypes { get { return new HashSet<string> (m_components.Keys); } }
334332

335-
FbxRepresentation child;
336-
if (rep.m_children.TryGetValue(key, out child)) {
337-
return child;
333+
public List<string> GetComponentValues(string componentType) {
334+
List<string> jsonValues;
335+
if (m_components.TryGetValue(componentType, out jsonValues)) {
336+
return jsonValues;
338337
}
339338
return null;
340339
}
@@ -475,22 +474,23 @@ public static HashSet<string> GetAllNames(params Data [] data) {
475474
/// </summary>
476475
Dictionary<string, List<Component>> m_componentsToUpdate;
477476

478-
static void SetupDataHelper(Data data, FbxRepresentation fbxrep, string parent)
477+
static void SetupDataHelper(Data data, FbxRepresentation fbxrep, string nodeName)
479478
{
480-
foreach(var kvp in FbxRepresentation.GetComponents(fbxrep)) {
481-
var typename = kvp.Key;
482-
var jsonValues = kvp.Value;
483-
data.AddComponents(parent, typename, jsonValues);
479+
foreach(var typename in fbxrep.ComponentTypes) {
480+
var jsonValues = fbxrep.GetComponentValues(typename);
481+
data.AddComponents(nodeName, typename, jsonValues);
484482
}
485-
foreach(var child in FbxRepresentation.GetChildren(fbxrep)) {
486-
data.AddNode(child, parent);
487-
SetupDataHelper(data, FbxRepresentation.Find(fbxrep, child), child);
483+
foreach(var child in fbxrep.ChildNames) {
484+
data.AddNode(child, nodeName);
485+
SetupDataHelper(data, fbxrep.GetChild(child), child);
488486
}
489487
}
490488

491489
static void SetupData(ref Data data, FbxRepresentation fbxrep)
492490
{
493491
Initialize(ref data);
492+
493+
// The root node has no name
494494
SetupDataHelper(data, fbxrep, "");
495495
}
496496

0 commit comments

Comments
 (0)