Search Unity

Bug Can't load the animation clips in AssetPostprocessor.OnPostprocessModel

Discussion in 'Animation' started by RomainF-Ubisoft, Dec 2, 2021.

  1. RomainF-Ubisoft

    RomainF-Ubisoft

    Joined:
    Aug 16, 2019
    Posts:
    10
    Hi!

    I don't know if it's a bug or if we were doing something illegal before.

    On Unity 2018.4, we use to be able to load the animation clips like so in AssetPostprocessor.OnPostprocessModel:

    Code (CSharp):
    1. protected void OnPostprocessModel(GameObject gameObject)
    2. {
    3.     var objects = AssetDatabase.LoadAllAssetsAtPath(assetPath);
    4.     var animationClips = objects.OfType<AnimationClip>();
    5.     Debug.Log($"Number of animation in file: {animationClips.Count()}");
    6. }
    This allowed us to create an Animator with the animation clips contained in the FBX, directly at import, during the FBX import.

    But since we upgraded to 2020.3, animationClips.Count() always returns 0 (and objects.Count() also returns 0 now, it used to return 370 objects in Unity 2018.4).

    Note: the same code, but in OnPostprocessAllAssets, works:
    Code (CSharp):
    1. protected static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    2. {
    3.     foreach (var importedAssetPath in importedAssets)
    4.     {
    5.         var objects = AssetDatabase.LoadAllAssetsAtPath(importedAssetPath);
    6.         var animationClips = objects.OfType<AnimationClip>();
    7.         Debug.Log($"Number of animation in file: {animationClips.Count()}");
    8.     }
    9. }
     
    Last edited: Dec 2, 2021
  2. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    Hey Romain,
    We should never have allowed that in the first place, and the AssetDatabase V2 starting in 2020.3 prevents that entirely.
    The idea is that when you try to load your own assets during an import, what you get are the assets from the previous import.
    This will always fail for the first import because you never imported anything, and this makes your future result depending on the previous one, which is non-deterministic, breaks the local cache system in Library, and will certainly break any project using accelerator at one point or another.

    Without any idea of what you are trying to achieve by loading the AnimationClips at that point, I would say you may want to use the clips from the OnPostprocessAnimation callback?
    If you want to share more details on what you are trying to achieve I'd be happy to help figure out a proper deterministic solution for it.

    Edit: after re-reading your message.
    If you want to create another asset during an import, this is also a non-deterministic side effect of your import, which you should not do.
    Using OnPostprocessAllAssets is definitely the best way to go if you want to populate automatically an AnimatorController asset based on the AnimationClip created at import time.
    If you want to have the AnimatorController asset linked to the Prefab, I would recommend creating a Prefab Variant from the imported Model and linking the AnimatorController here.
    Creating Variant for imported models is definitely better than using the imported model directly in any project (because you can add components, change their values, etc... and this will not be overridden by a future import and will be shared between all scenes in your project).
     
    Last edited: Dec 2, 2021
    RomainF-Ubisoft likes this.
  3. RomainF-Ubisoft

    RomainF-Ubisoft

    Joined:
    Aug 16, 2019
    Posts:
    10
    Hi Bastien!

    Thanks for the quick answer ;)

    That's what I thought. I'm not the one that did the code originally, but it was weird and it was working, so we've let that under the rug for a while.

    The goal of the whole code is to automatically import an FBX (using Unity with the command line) and create an asset bundle of it so we can load it elsewhere in a web viewer.

    I smelled that the prefab was the way to go and I reckon it does simplify the code already. There was indeed a trick in the code to force another import pass on the FBX. From what you're saying, it makes sense that they did it that way, it was to get, in the second pass, the import from the previous pass. Moving everything to OnPostprocessAllAssets already started to make things cleaner.

    I just have one question left: I know how to create a prefab in code, but not a prefab variant. Is it possible on an FBX?
     
    Last edited: Dec 2, 2021
  4. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    Not that I know of... PrefabUtility.CreateVariant does exist, but it's internal, I'm reaching out to the team to get an insight if it's possible.

    On a side note, if your tool consists of a static method called from a command line that adds an fbx file to an empty project in order to make an assetbundle so it can be loaded in an existing game, then you don't need to use any asset postprocessor or prefab variant.
    AssetPostprocessors and Prefab Variant exists so when you iterate on assets in your project across multiple users and multiple scenes everything stays consistent (guids are kept, references in scenes stay, Prefabs are updated when the fbx changes, etc...)
    If you don't need all of that because your only usage of this project is to create an assetbundle and not iterate on scenes and multiple imports of the same asset, then here is what I would do:
    - Copy the FBX file in the project and call AssetDatabase.Refresh to import it.
    - Create your AnimatorController asset using the imported AnimationClip.
    - Call GameObject.Instantiate(AssetDatabase.LoadMainAssetAtPath(myFbxPath)); to bring your fbx in the scene.
    - Add any component, set links etc...
    - Call PrefabUtility.SaveAsPrefabAsset(instance, "Assets/PrefabPath.prefab");
    - Create the assetbundle using this prefab.
     
    RomainF-Ubisoft likes this.
  5. bastien_humeau

    bastien_humeau

    Unity Technologies

    Joined:
    Jun 14, 2017
    Posts:
    191
    In order to create a Prefab Variant from code, here is what you have to do:
    Code (CSharp):
    1. var fbxRoot = AssetDatabase.LoadMainAssetAtPath("Assets/fbxPath.fbx") as GameObject;
    2. var newInstance = PrefabUtility.InstantiatePrefab(fbxRoot);
    3. PrefabUtility.SaveAsPrefabAsset(newInstance, "Assets/fbxPath Variant.prefab");
    The trick here is using PrefabUtility.InstantiatePrefab makes it a variant while doing a GameObject.Instantiate will make it a simple Prefab.
     
    RomainF-Ubisoft likes this.
  6. RomainF-Ubisoft

    RomainF-Ubisoft

    Joined:
    Aug 16, 2019
    Posts:
    10
    Thanks Bastien!

    This seems way simpler that what we do right now. I think we still need
    AssetPostprocessor
    because we want to set specific import properties (such as animation compression), but maybe I can indeed split my current Unity project in two: one to create the assetbundles and one for our web client. I'll test this solution, since we need to import multiple types of assets (not only FBX) I need to verify it could work in all the situation ;)

    Nice! Didn't know about
    PrefabUtility.InstantiatePrefab
    . I'll see if this could help us, but as you said, maybe we don't need to use variants for our simple use case, yet still interesting to know this solution exists!