Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Miss_Jones · Aug 12, 2019 at 03:17 PM · c#animationfbxexport

How to export an fbx file with assimp that can be used in Unity?

I use Assimpnet to export an fbx file with a skinned mesh, skeleton and animation from at runtime. The fbx file works perfectly well when imported into Blender or Maya, but when in open Unity editor to import the file the positional values on the rig when playing the animation are super fucked up. It even crashes the animation preview panel in the inspector. Unity gives some AABB errors when playing the animation which could indicate that it cannot calculate the bounds due to NaN values, but I do not know why I have these NaN values in the first place. They do not exist in Blender or Maya.

Can anyone help me out?

This is the export code:

 public class ExportController : Singleton<ExportController>
     {
         public Assimp.ExportFormatDescription[] formatIds;
         public enum ExportFormat // Indices corresponding to Assimp formats
         {
             None = -1,
             FBX = 17,
             GLTF = 12,
             GLTF2 = 10,
             DAE = 0,
             Count
         }
         
         public int ticksPerSecond = 60;
         public float interval => 1.0f / ticksPerSecond;
 
         private readonly AssimpContext assimp = new AssimpContext();
         private Scene scene;
         private Assimp.Animation exportedAnimation;
         private int currentExportFrame;
         private bool bindPoseSet;
         
         public bool exportRequested;
         public bool exporting;
         public ExportFormat exportFormat;
         public new Animation animation;
         public float progress;
 
         private int fbxCallback;
         private int gltfCallback;
         private int gltf2Callback;
         private int daeCallback;
 
         public string outputFolder;
 
         private void Start()
         {
             formatIds = assimp.GetSupportedExportFormats();
 
             fbxCallback = EventHandler.Instance.AddListener(EventHandler.UIEvent.RequestFBXExport, () => OnRequestExport(ExportFormat.FBX));
             gltfCallback = EventHandler.Instance.AddListener(EventHandler.UIEvent.RequestGLTFExport, () => OnRequestExport(ExportFormat.GLTF));
             gltf2Callback = EventHandler.Instance.AddListener(EventHandler.UIEvent.RequestGLTF2Export, () => OnRequestExport(ExportFormat.GLTF2));
             daeCallback = EventHandler.Instance.AddListener(EventHandler.UIEvent.RequestCOLLADAExport, () => OnRequestExport(ExportFormat.DAE));
 
             outputFolder = UserSettings.Instance.modelExportPath.GetValue();
         }
 
         private void OnDisable()
         {
             if (EventHandler.Instance)
             {
                 EventHandler.Instance.RemoveListener(EventHandler.UIEvent.RequestFBXExport, fbxCallback);
                 EventHandler.Instance.RemoveListener(EventHandler.UIEvent.RequestGLTFExport, gltfCallback);
                 EventHandler.Instance.RemoveListener(EventHandler.UIEvent.RequestGLTF2Export, gltf2Callback);
                 EventHandler.Instance.RemoveListener(EventHandler.UIEvent.RequestCOLLADAExport, daeCallback);
             }
         }
 
         private void Update()
         {
             if (exportRequested)
             {
                 animation = RigReferences.Instance.effectorParent.GetComponent<Animation>();
                 InitializeExport();
                 exportRequested = false;
                 exporting = true;
             }
             else if (exporting)
             {
                 progress = animation.currentTime * ticksPerSecond / (float) exportedAnimation.DurationInTicks;
                 SampleNextFrameOfAnimation();
                 ExportFrame();
                 IncrementTick();
                 CheckIfExportFinished();
             }
         }
 
         private void OnRequestExport(ExportFormat exportFormat)
         {
             this.exportFormat = exportFormat;
             exportRequested = true;
         }
 
         private void InitializeExport()
         { 
             EventHandler.Instance.InvokeEvent(EventHandler.UIEvent.RequestExitMotionCapture);
             outputFolder = UserSettings.Instance.modelExportPath.GetValue();
             animation.playing = false;
             animation.currentTime = 0;
             currentExportFrame = 0;
             progress = 0;
 
             bindPoseSet = false;
             
             ValidateFileExists(RigReferences.Instance.importPath);
             scene = assimp.ImportFile(RigReferences.Instance.importPath, PostProcessSteps.OptimizeGraph 
                                                                          | PostProcessSteps.OptimizeMeshes 
                                                                          | PostProcessSteps.GlobalScale 
                                                                          | PostProcessSteps.LimitBoneWeights 
                                                                          | PostProcessSteps.JoinIdenticalVertices
                                                                          | PostProcessSteps.ValidateDataStructure);
             
             /* METADATA */
             scene.RootNode.Metadata["FrameRate"] = new Metadata.Entry(MetaDataType.Int32, ticksPerSecond);
             scene.RootNode.Metadata["FrontAxisSign"] = new Metadata.Entry(MetaDataType.Int32, -1);
             scene.RootNode.Metadata["OriginalUnitScaleFactor"] = new Metadata.Entry(MetaDataType.Int32, 100);
             scene.RootNode.Metadata["UnitScaleFactor"] = new Metadata.Entry(MetaDataType.Int32, 1);
             /* METADATA */
             exportedAnimation = new Assimp.Animation
             {
                 Name = "Animation clip",
                 TicksPerSecond = ticksPerSecond,
                 DurationInTicks = animation.duration * ticksPerSecond,
             };
             scene.Animations.Clear();
             scene.Animations.Add(exportedAnimation);
             
             animation.requestTPose.Invoke();
             
             EventHandler.Instance.InvokeEvent(EventHandler.SystemEvent.ExportInitialized);
         }
 
         private static void ValidateFileExists(string path)
         {
             if (!File.Exists(path))
             {
                 throw new AssimpException("File for imported animation must exist!");
             }
         }
 
         private void SampleNextFrameOfAnimation()
         {
             foreach (AnimationChannel animationChannel in animation.animationChannels)
             {
                 animationChannel.clip.SampleAnimation(animationChannel.joint.gameObject, animation.currentTime);
             }
         }
 
         private void IncrementTick()
         {
             animation.currentTime += interval;
         }
 
         private void CheckIfExportFinished()
         {
             if (animation.currentTime >= animation.duration)
             {
                 ValidateScene(scene);
                 WriteToFile();
                 EventHandler.Instance.InvokeEvent(EventHandler.SystemEvent.ExportEnded);
             }
         }
 
         private void ExportFrame()
         {
             ExportJoint(RigReferences.Instance.rootJoint);
             currentExportFrame++;
         }
 
         private void ExportJoint(Transform joint)
         {
             /*
              * For the root node (hips), set scale, world position and world rotations
              * Ignore leaves
              * For other joints set local rotation
              * Start by setting default position and scale keys
              */
             foreach (Transform child in joint.transform)
             {
                 ExportJoint(child);
             }
 
             if (joint.childCount == 0) return; // Don't animate leaves
 
             bool isRoot = joint == RigReferences.Instance.rootJoint;
 
             NodeAnimationChannel animationChannel = exportedAnimation.NodeAnimationChannels.Find(channel =>
             channel.NodeName == joint.name);
 
             if (animationChannel == null)
             {
                 Debug.Log("Setting up animation channel for: " + joint.name, joint);
                 // If we add an animation channel for a non existent node assimp crashes
                 bool existsInAssimpAnimation = false;
                 Stack<Node> unprocessedNodes = new Stack<Node>();
                 unprocessedNodes.Push(scene.RootNode);
                 while (unprocessedNodes.Count > 0 && !existsInAssimpAnimation)
                 {
                     Node node = unprocessedNodes.Pop();
 
                     if (node.Name == joint.name) existsInAssimpAnimation = true;
 
                     foreach (Node child in node.Children)
                     {
                         unprocessedNodes.Push(child);
                     }
                 }
 
                 if (!existsInAssimpAnimation)
                 {
                     Debug.LogError("Failed to find animation channel in Assimp model for " + joint.name);
                     return;
                 }
                 
                 animationChannel = new NodeAnimationChannel
                 {
                     NodeName = joint.name
                 };
                 
                 exportedAnimation.NodeAnimationChannels.Add(animationChannel);
             }
             
             Quaternion r = joint.transform.localRotation;
             Vector3 rEul = new Vector3();
             
             if (isRoot)
             {
                 // World rotation
                 r = joint.transform.rotation;
                 rEul = r.eulerAngles;
                 rEul.y *= -1;
                 rEul.z *= -1;
             }
             else
             {
                 // Local rotation
                 rEul = r.eulerAngles;
                 // LEFTHAND/RIGHTHAND FIX
                 rEul.y *= -1;
                 rEul.x *= -1;
             }
             
             r = Quaternion.Euler(rEul);
 
             QuaternionKey quaternionKey =
             new QuaternionKey(animation.currentTime, new Assimp.Quaternion(r.w, r.x, r.y, r.z));
             animationChannel.RotationKeys.Add(quaternionKey);
             
             Vector3 p = joint.transform.localPosition;
             VectorKey positionKey;
   
             if (isRoot)
             {
                 p = joint.position * IOConstants.UnityScaleFactor;
                 p.x *= -1;
                 
                 positionKey = new VectorKey(animation.currentTime, new Vector3D(p.x, p.y, p.z));
                 animationChannel.PositionKeys.Add(positionKey);
             }
             if (currentExportFrame != 0) return;
 
             if (!isRoot) // Only one position for all joints, except root
             {
                 p.z *= -1;
                 
                 positionKey = new VectorKey(animation.currentTime, new Vector3D(p.x, p.y, p.z));
                 animationChannel.PositionKeys.Add(positionKey);
             }
             
             // Only one scale key for all joints
             Vector3 s = Vector3.one;
             VectorKey scaleKey = new VectorKey(animation.currentTime, new Vector3D(s.x, s.y, s.z));
             animationChannel.ScalingKeys.Add(scaleKey);
         }
 
         private static void ValidateScene(Scene scene)
         {
             if (scene.HasAnimations || scene.AnimationCount > 0 || scene.Animations.Count > 0)
             {
                 foreach (Assimp.Animation animation in scene.Animations)
                 {
                     if (!animation.HasMeshAnimations && !animation.HasNodeAnimations)
                     {
                         throw new AssimpException("Exported Assimp animations cannot be empty!");
                     }
                 }
             }
         }
 
         private void WriteToFile()
         {
             string outputPath = RenameFilenameIfNecessary(Path.Combine(outputFolder, Path.GetFileName(RigReferences.Instance.importPath)));
             Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
             foreach (KeyValuePair<string, Metadata.Entry> keyValuePair in scene.Metadata)
             {
                 Debug.Log(keyValuePair.Key + ": " + keyValuePair.Value);
             }
             foreach (KeyValuePair<string, Metadata.Entry> keyValuePair in scene.RootNode.Metadata)
             {
                 Debug.Log(keyValuePair.Key + ": " + keyValuePair.Value);
             }
             
             assimp.ExportFile(scene, outputPath, formatIds[(int)exportFormat].FormatId);
             exporting = false;
 
             Debug.Log("Exported animation to: " + outputPath);
         }
 
         private static string RenameFilenameIfNecessary(string path)
         {
             int count = 1;
 
             string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
             string extension = Path.GetExtension(path);
             string directory = Path.GetDirectoryName(path);
             string renamedPath = path;
 
             while (File.Exists(renamedPath) && count < int.MaxValue)
             {
                 string renamedFilename = $"{filenameWithoutExtension} ({count++}){extension}";
                 renamedPath = directory == null ? renamedFilename : Path.Combine(directory, renamedFilename);
             }
 
             return renamedPath;
         }
     }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

716 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Multiple Cars not working 1 Answer

Renaming an animation from FBX format 2 Answers

Importing animation from 3DS Max into Unity problem 3 Answers

FBX export from Motionbuilder 2013 to Unity 0 Answers

FBX Exporter imcomplete anim exports in 2018.2.20 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges