Skip to content

Commit bc7bd37

Browse files
author
Benoit Hudson
committed
Fix bug with components of the prefab root, and add regression test.
Oh but the regression test doesn't work because ConvertToModel regressed ;(
1 parent 8211ecf commit bc7bd37

File tree

4 files changed

+87
-18
lines changed

4 files changed

+87
-18
lines changed

Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ namespace FbxExporters.UnitTests
1616
{
1717
public abstract class ExporterTestBase
1818
{
19+
/// <summary>
20+
/// Sleep an amount of time (in ms) so we can safely assume that the timestamp on an fbx will change.
21+
/// </summary>
22+
public void SleepForFileTimestamp() {
23+
System.Threading.Thread.Sleep(1000);
24+
}
25+
1926
private string _testDirectory;
2027
protected string filePath {
2128
get {

Assets/FbxExporters/Editor/UnitTests/FbxPrefabAutoUpdaterTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public void ReplaceTest ()
7474
// Export it to the same fbx path. But first, wait one second so
7575
// that its timestamp differs enough for Unity to notice it
7676
// changed.
77-
System.Threading.Thread.Sleep(1000);
77+
SleepForFileTimestamp();
7878
FbxExporters.Editor.ModelExporter.ExportObject(m_fbxPath, newHierarchy);
7979
AssetDatabase.Refresh();
8080

@@ -103,7 +103,7 @@ public void ExpensivePerformanceTest ()
103103
var stopwatch = new System.Diagnostics.Stopwatch ();
104104
stopwatch.Start();
105105

106-
// Create 1000 fbx models and 1000 prefabs.
106+
// Create N fbx models and N/2 prefabs.
107107
// Each prefab points to an fbx model.
108108
//
109109
// Then modify one fbx model. Shouldn't take longer than 1s.

Assets/FbxExporters/Editor/UnitTests/FbxPrefabTest.cs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ GameObject ModifySourceFbx()
138138
// Sleep one second first to make sure the timestamp differs
139139
// enough, so the asset database knows to reload it. I was getting
140140
// test failures otherwise.
141-
System.Threading.Thread.Sleep(1000);
141+
SleepForFileTimestamp();
142142
FbxExporters.Editor.ModelExporter.ExportObjects (
143143
AssetDatabase.GetAssetPath(m_source),
144144
new Object[] { newModel } );
@@ -233,6 +233,54 @@ public void ManualToAuto() {
233233
}
234234
}
235235

236+
public class FbxPrefabRegressions : ExporterTestBase
237+
{
238+
[Ignore("ConvertToModel return value is messed up.")]
239+
[Test]
240+
public void TestCubeAtRoot()
241+
{
242+
// vkovec found a bug when removing a mesh at the root.
243+
// bhudson fixed it, let's make sure it stays fixed:
244+
// 1. Make a cube
245+
// 2. Convert to model
246+
// 3. In Maya, make a null named 'Cube' and put it at the root.
247+
// 4. Update => meshfilter and meshrenderer should be gone
248+
249+
// Make a cube.
250+
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
251+
cube.name = "Cube";
252+
var cubeAssetPath = GetRandomFbxFilePath();
253+
var autoPrefab = FbxExporters.Editor.ConvertToModel.CreateInstantiatedModelPrefab(
254+
new GameObject[] { cube }, path: cubeAssetPath)[0];
255+
Assert.IsTrue(autoPrefab);
256+
257+
// Make a maya locator.
258+
var locator = new GameObject("Cube");
259+
var locatorAssetPath = FbxExporters.Editor.ModelExporter.ExportObject(
260+
GetRandomFbxFilePath(), locator);
261+
262+
// Check the prefab has all the default stuff it should have.
263+
Assert.IsNotNull(autoPrefab.GetComponent<MeshFilter>());
264+
Assert.IsNotNull(autoPrefab.GetComponent<MeshRenderer>());
265+
Assert.IsNotNull(autoPrefab.GetComponent<BoxCollider>());
266+
267+
// Now copy the locator over and refresh.
268+
SleepForFileTimestamp();
269+
System.IO.File.Copy(locatorAssetPath, cubeAssetPath, overwrite: true);
270+
AssetDatabase.Refresh();
271+
272+
// Check the prefab lost its mesh filter and renderer.
273+
Assert.IsNull(autoPrefab.GetComponent<MeshFilter>());
274+
Assert.IsNull(autoPrefab.GetComponent<MeshRenderer>());
275+
276+
// The box collider is controversial: it got generated
277+
// automatically, so shouldn't it be deleted automatically? But
278+
// right now it doesn't get deleted, so let's test to make sure a
279+
// change in behaviour isn't accidental.
280+
Assert.IsNotNull(autoPrefab.GetComponent<BoxCollider>());
281+
}
282+
}
283+
236284
public class FbxPrefabHelpersTest
237285
{
238286
class AClass { }

Assets/FbxExporters/FbxPrefab.cs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ void InitHelper(FbxRepresentation fbxrep, string nodeName)
424424
}
425425

426426
public Data(FbxRepresentation fbxrep) {
427+
m_parents.Add("", ""); // the root points to itself
427428
InitHelper(fbxrep, "");
428429
}
429430

@@ -597,8 +598,12 @@ void ClassifyReparenting()
597598
// x a b => conflict! switch to a for now (todo!)
598599
// x x a => no action. Todo: what if a is being destroyed? conflict!
599600
foreach(var name in m_prefab.NodeNames) {
601+
if (name == "") {
602+
// Don't reparent the root.
603+
continue;
604+
}
600605
if (m_nodesToDestroy.Contains(name)) {
601-
// Reparent to null. This is to avoid the nuisance of
606+
// Reparent to the root. This is to avoid the nuisance of
602607
// trying to destroy objects that are already destroyed
603608
// because a parent got there first. Maybe there's a
604609
// faster way to do it, but performance seems OK.
@@ -758,10 +763,18 @@ public bool NeedsUpdates() {
758763
/// </summary>
759764
public void ImplementUpdates(FbxPrefab prefabInstance)
760765
{
761-
// Gather up all the nodes in the prefab.
766+
Log("{0}: performing updates", prefabInstance.name);
767+
768+
// Gather up all the nodes in the prefab so we can look up
769+
// nodes. We use the empty string for the root node.
770+
var prefabRoot = prefabInstance.transform;
762771
var prefabNodes = new Dictionary<string, Transform>();
763772
foreach(var node in prefabInstance.GetComponentsInChildren<Transform>()) {
764-
prefabNodes.Add(node.name, node);
773+
if (node == prefabRoot) {
774+
prefabNodes[""] = node;
775+
} else {
776+
prefabNodes.Add(node.name, node);
777+
}
765778
}
766779

767780
// Create new nodes.
@@ -786,7 +799,7 @@ public void ImplementUpdates(FbxPrefab prefabInstance)
786799
var parent = kvp.Value;
787800
Transform parentNode;
788801
if (string.IsNullOrEmpty(parent)) {
789-
parentNode = prefabInstance.transform;
802+
parentNode = prefabRoot;
790803
} else {
791804
parentNode = prefabNodes[parent];
792805
}
@@ -801,17 +814,17 @@ public void ImplementUpdates(FbxPrefab prefabInstance)
801814
}
802815

803816
// Destroy the old components.
804-
foreach(var kvp in prefabNodes) {
805-
var name = kvp.Key;
806-
var xfo = kvp.Value;
807-
List<System.Type> typesToDestroy;
808-
if (m_componentsToDestroy.TryGetValue(name, out typesToDestroy)) {
809-
foreach(var componentType in typesToDestroy) {
810-
var component = xfo.GetComponent(componentType);
811-
if (component != null) {
812-
Object.DestroyImmediate(component);
813-
Log("destroyed component {0}:{1}", xfo.name, componentType);
814-
}
817+
foreach(var kvp in m_componentsToDestroy) {
818+
Log("destroying components on {0}", kvp.Key);
819+
var nodeName = kvp.Key;
820+
var typesToDestroy = kvp.Value;
821+
var prefabXfo = prefabNodes[nodeName];
822+
823+
foreach(var componentType in typesToDestroy) {
824+
var component = prefabXfo.GetComponent(componentType);
825+
if (component != null) {
826+
Object.DestroyImmediate(component);
827+
Log("destroyed component {0}:{1}", nodeName, componentType);
815828
}
816829
}
817830
}
@@ -880,6 +893,7 @@ void CompareAndUpdate()
880893

881894
// If we don't need to do anything, jump out now.
882895
if (!updates.NeedsUpdates()) {
896+
Log("{0}: no updates needed", transform.name);
883897
return;
884898
}
885899

0 commit comments

Comments
 (0)