SampleAnimation() is wrong???

I have just imported a character created from Blender, there are no animations other than a restPose (where all keyframes are set to their identities).

The character is already in the RestPose when imported into Unity, but I have a special need to also sample some of the animations.

AnimationClip.SampleAnimation

When I sample the RestPose I see that the character twists up slightly???

I was not expecting the character to change at all, because it already is in restpose,

Where is this twist coming from? It is causing major misalignments with my project. I can not find same inconsistency in the Blender file so this must be coming from Unity’s end.

Please help me figure this out (this has been costing days of wasted effort).

[edit]
If I change rig type to “generic” then I am able to sample any animation and they look perfectly spot on.

Why would generic/humanoid make any difference?

How can I sample my humanoid as a generic?

This was not simple to achieve at all and Unity made this extremely difficult.

But I was able to hack around the limitation with the aid of an AssetPostprocessor.

Basically as I mentioned before “If I change rig type to “generic” then I am able to sample any animation and they look perfectly spot on”. So that’s exactly what I am doing.

Here’s some sudo code taken directly from my asset postprocessor. Notice how I first import as generic rig, then sample all reference poses with SampleAnimation(), finally I set the reimportTag tag in the userData and re-import as a humanoid rig:

public class MyAssetPostprocessor : AssetPostprocessor {


    private const string reimportTag = "REIMPORT";
 
    private void OnPreprocessModel() {
        //Load in the preconfigured avatar from the resources folder

        string templateType = CheckIsTemplate (templates);
        if (!String.IsNullOrEmpty(templateType)) {
 
            var importer = (ModelImporter) assetImporter;


            if (Path.GetExtension (assetPath) != ".fbx") {
                importer.animationType = ModelImporterAnimationType.None;
                return;
            }

            if (!importer.userData.Contains (reimportTag)) {
                if (importer.animationType != ModelImporterAnimationType.Human) { //if the type is ever changed from human, then invalidate userdata so it will force everythin to be re-run
                    importer.userData = ""; //reset the user data
                    Debug.Log ("RESET USERDATA");
                }
            }

            importer.isReadable = true;

            importer.importAnimation = true;

            importer.importMaterials = true;
            importer.materialLocation = ModelImporterMaterialLocation.External;

            //Blender's normals are wrong when imported into unity causing artifacts in whole body when using blendshapes, use calculate to force unity to make the normals correct.
            importer.importNormals = ModelImporterNormals.Calculate;
            importer.importBlendShapeNormals = ModelImporterNormals.Calculate;

 
            if (importer.userData.Contains (reimportTag)) {
                importer.animationType = ModelImporterAnimationType.Human; //The rest pose has already been processed and we are re-importing as a humanoid character now
            } else {
                importer.animationType = ModelImporterAnimationType.Generic; //THIS LINE IS CRITIAL FOR SAMPLING A PROPER REST POSE. MUST FIRST IMPORT AS GENERIC!!
            }
        }
    }
 
 
    public static GameObject CreateRestPoseRig(GameObject baseRig, string assetPath) {
        var animator = baseRig.transform.root.GetComponentInChildren<Animator> ();

        var assetRepresentationsAtPath = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
        foreach (var assetRepresentation in assetRepresentationsAtPath) {
            var animationClip = assetRepresentation as AnimationClip;

            if (animationClip != null) {
                if (animationClip.name.Contains ("RestPose")) { //Found the rest pose created by the blend script, this is great!
                    animationClip.SampleAnimation (animator.gameObject, 0); //evaluate the restPose to pose this character before copying it as the restPose restPoseRig
                }
            }
        }

        GameObject restPoseRig = GameObject.Instantiate (baseRig, baseRig.transform);//Make a copy of the armature
        restPoseRig.SetActive (false); //this rig is a reference

        ...
 
        ...
        return restPoseRig;
    }

 
    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        foreach (string assetPath in importedAssets)
        {             
            if (Path.GetExtension (assetPath) == ".meta") {
                continue;
            }
 
            if (assetPath.StartsWith (processingFolder)) {//Looking for the copied .fbx file that resides in the projects processing/prefab folder

                if (!assetPath.EndsWith (".fbx", StringComparison.OrdinalIgnoreCase)) {//only allow the .fbx files in the processing folder
                    continue;
                }
                GameObject modelAsset = AssetDatabase.LoadAssetAtPath<GameObject> (assetPath); //LOADING AN ASSET
                ModelImporter modelImporter = ModelImporter.GetAtPath (assetPath) as ModelImporter;


                //ENFORCE REST-POSE SECTION (FIRST IMPORT - CHARACTER TYPE == GENERIC):

                if (!modelImporter.userData.Contains (reimportTag)) { //the reimportTag has not been set, this means to create the rest pose rig now
                    GameObject modelGeneric = (GameObject)PrefabUtility.InstantiatePrefab(modelAsset);
                    GameObject realGeneric = GameObject.Instantiate(modelGeneric); //this is a game object that we can re-arange and change parenting or objects, then save as the original prefab later on
                    var armatureGeneric = realGeneric.transform.root.GetComponentInChildren<Animator>();
                    armatureGeneric.name = modelGeneric.name; //remove "(clone) or any other discrepancies from name"

                    GameObject.DestroyImmediate (modelGeneric); //destroy the prefab as it will be overwritten by "real"

                    var restPose = CreateRestPoseRig(realGeneric, assetPath); //this is a game object that we can re-arange and change parenting or objects, then save as the original prefab later on;

                    foreach (var transform in restPose.GetComponentsInChildren<Transform>()) {
                        transform.name = "RestPoseRig_" + transform.name; //must rename each bone so that Unity's seriously bugged animator does not attempt to use them instead of the actual character!
                    }

                    string modelFileNameGeneric = Path.GetFileNameWithoutExtension( assetPath );
                    string destinationPathGeneric = Path.Combine(prefabsFolder, modelFileNameGeneric + ".prefab");

                    PrefabUtility.SaveAsPrefabAsset (restPose, destinationPathGeneric);

                    GameObject.DestroyImmediate (realGeneric);

                    modelImporter.userData = modelImporter.userData + " " + reimportTag; //Add reimportTag tag to change the character type to ModelImporterAnimationType.Human
                    modelImporter.SaveAndReimport ();
                    return;
                }

I think SampleAnimation is broken (for humanoids), Maybe if a “SampleAnimationGeneric()” method existed to avoid the twisting, it would be very useful!