Unity
EXPORT
FBX

Unity export fbx in Edit Mode

More than 1 year has passed since last update.

Editor上で、[ExecuteInEditMode]を使って、Scene上に作成したColorやBoneWeight等の
情報を含むMeshを、一旦Assets/に保存して、ParticleSystem等で利用するという事がしたくて、作成。
自分仕様なので、中身は見づらいかも知れないが、一旦exportすることにより、
UnityのFbx ImporterのEdgeやTangents等をCalculateする機能が使えて、結構便利なので共有します。

使い方
・このComponentをAttachしたGameObjectを作成し、Inspector上で操作する。
・保存するMesh Rendererを含むGameObjectのRootをInspector/exportObjectにDrop。
・Inspector/zoom は大抵のfbx fileはそうだったので、defaultは100にしている。
・Inspector/exportFbx をtrueにするとexport実行。
・Inspector/exportTexture はRendererのmaterialsに含まれるテクスチャをpngで保存。

問題点
・ファイル仕様のバージョンが古いので、3Dアプリケーションなどからは読めない。
・AnimationClipをPlotして吐き出す事もやってみたが、あまりうまく行かない。

#if UNITY_EDITOR
using UnityEngine;
using System.Collections;
using System.IO;
using UnityEditor;

[ExecuteInEditMode]
public class FBXExporter2 : MonoBehaviour
{
    public GameObject exportObject = null;
    public string filePath = "";
    public float zoom = 100.0f;
    public int rotationPlotQuarity = 120;
    public bool exportFbx = false;
    public bool exportTexture = false;

    private GameObject lstObj;
    private string lstName;

    private string expTxt0 = "";
    private string expTxt1 = "";
    private Transform[] tfs = new Transform[0];
    enum Type
    {
        Null,
        Mesh,
        Skin
    }
    private Type[] types = new Type[0];
    private AnimationClip clip;

    private bool isSkin = false;
    private int mdlCnt = 0;
    private int dfmCnt = 0;
    private int pseCnt = 0;

    void Update ()
    {
        if ( exportObject != lstObj ) {
            lstObj = exportObject;
            filePath = "";
        } else if ( exportObject != null && exportObject.name != lstName ) {
            lstName = exportObject.name;
            filePath = "";
        }
        if ( exportFbx ) {
            exportFbx = false;
            ExportMain();
        }
        if ( exportTexture ) {
            exportTexture = false;
            ExportTexture();
        }
    }

    void ExportMain ()
    {
        if ( !exportObject ) { return; }
        if ( filePath == "" ) { filePath = exportObject.name; }

        SubMeshCheck();

        exportObject.transform.parent = null;
        int num;
        num = ObjCount(exportObject.transform, 0);
        tfs = new Transform[num];
        types = new Type[num];


        Dig(exportObject.transform, 0);

        expTxt0 = "";
        expTxt1 = "";

        //Body//
        AddExpTxt1("; Object properties");
        AddExpTxt1(";------------------------------------------------------------------");
        AddExpTxt1("");
        AddExpTxt1("Objects: {");
        ObjectsTxt(num);
        if ( isSkin ) { PoseTxt(num); }
        if ( isSkin ) { DeformerTxt(num); }
        AddExpTxt1("}");
        AddExpTxt1("");
        ConnectTxt(num);

        //Header//
        AddExpTxt0("; F 6.1 project file");
        AddExpTxt0("");
        AddExpTxt0("; Object definitions");
        AddExpTxt0(";------------------------------------------------------------------");
        AddExpTxt0("");
        AddExpTxt0("Definitions: {");
        AddExpTxt0("    Count: " + (mdlCnt + dfmCnt + pseCnt));
        if ( mdlCnt > 0 ) {
            AddExpTxt0("    ObjectType: \"Model\" {");
            AddExpTxt0("        Count: " + mdlCnt);
            AddExpTxt0("    }");
        }
        if ( dfmCnt > 0 ) {
            AddExpTxt0("    ObjectType: \"Deformer\" {");
            AddExpTxt0("        Count: " + dfmCnt);
            AddExpTxt0("    }");
        }
        if ( pseCnt > 0 ) {
            AddExpTxt0("    ObjectType: \"Pose\" {");
            AddExpTxt0("        Count: " + pseCnt);
            AddExpTxt0("    }");
        }
        AddExpTxt0("}");
        AddExpTxt0("");

        GetAnimationClip();
        if ( clip != null ) { AnimationText(); }

        Export("Assets/" + filePath + ".fbx", expTxt0 + expTxt1);

        SubMeshEndProcess();
    }

    //Number of objects in the hierarchy//
    int ObjCount (Transform tf, int num)
    {
        num++;

        int i;
        for ( i = 0; i < tf.childCount; i++ ) {
            num = ObjCount(tf.GetChild(i), num);
        }

        return num;
    }

    //Hierarchy expansion//
    int Dig (Transform tf, int no)
    {
        MeshRenderer mr = tf.GetComponent("MeshRenderer") as MeshRenderer;
        SkinnedMeshRenderer smr = tf.GetComponent("SkinnedMeshRenderer") as SkinnedMeshRenderer;

        tfs[no] = tf;
        if ( mr ) {
            types[no] = Type.Mesh;
        } else if ( smr ) {
            types[no] = Type.Skin;
            isSkin = true;
        } else {
            types[no] = Type.Null;
        }

        no++;

        int i;
        for ( i = 0; i < tf.childCount; i++ ) {
            no = Dig(tf.GetChild(i), no);
        }

        return no;
    }

    //Add 1 line of text//
    void AddExpTxt0 (string txt)
    {
        expTxt0 += txt + "\n";
    }
    void AddExpTxt1 (string txt)
    {
        expTxt1 += txt + "\n";
    }

    //Object definition in scene//
    void ObjectsTxt (int num)
    {
        int i;
        for ( i = 0; i < num; i++ ) {
            if ( types[i] == Type.Null ) {
                NullTxt(i);
            } else if ( types[i] == Type.Mesh ) {
                MeshTxt(i, types[i]);
            } else if ( types[i] == Type.Skin ) {
                MeshTxt(i, types[i]);
            }
        }
    }

    //Empty GameObject definition text//
    void NullTxt (int no)
    {
        Vector3 pos = VectorExponential(tfs[no].localPosition);
        pos.x *= -1;
        pos *= zoom;
        //回転軸順を調整 unity:zxy FBX:xyz//
        Quaternion rot = tfs[no].localRotation;
        Vector3 eul = VectorExponential(GetEulerXYZ(rot));
        eul.y = -eul.y;
        eul.z = -eul.z;
        Vector3 scl = VectorExponential(tfs[no].localScale);

        AddExpTxt1("    Model: \"Model::" + tfs[no].name + "\",\"Null\" {");
        AddExpTxt1("        Properties60: {");
        AddExpTxt1("            Property: \"Lcl Translation\", \"Lcl Translation\", \"A+\"," + VectorTxt(pos));
        AddExpTxt1("            Property: \"Lcl Rotation\", \"Lcl Rotation\", \"A+\"," + VectorTxt(eul));
        AddExpTxt1("            Property: \"Lcl Scaling\", \"Lcl Scaling\", \"A+\"," + VectorTxt(scl));
        AddExpTxt1("        }");
        AddExpTxt1("    }");

        mdlCnt++;
    }

    //Mesh definition text//
    void MeshTxt (int no, Type type)
    {
        Vector3 pos = VectorExponential(tfs[no].localPosition);
        pos.x = -pos.x;
        pos *= zoom;
        //回転軸順を調整 unity:zxy FBX:xyz//
        Quaternion rot = tfs[no].localRotation;
        Vector3 eul = VectorExponential(GetEulerXYZ(rot));
        eul.y *= -1;
        eul.z *= -1;
        Vector3 siz = VectorExponential(tfs[no].localScale);

        AddExpTxt1("    Model: \"Model::" + tfs[no].name + "\",\"Mesh\" {");
        AddExpTxt1("        Properties60: {");
        AddExpTxt1("            Property: \"Lcl Translation\", \"Lcl Translation\", \"A+\"," + VectorTxt(pos));
        AddExpTxt1("            Property: \"Lcl Rotation\", \"Lcl Rotation\", \"A+\"," + VectorTxt(eul));
        AddExpTxt1("            Property: \"Lcl Scaling\", \"Lcl Scaling\", \"A+\"," + VectorTxt(siz));
        AddExpTxt1("        }");

        int i;
        Mesh mesh = null;
        if ( type == Type.Mesh ) {
            MeshFilter meshf = tfs[no].GetComponent("MeshFilter") as MeshFilter;
            mesh = meshf.sharedMesh;
        } else if ( type == Type.Skin ) {
            SkinnedMeshRenderer smr = tfs[no].GetComponent("SkinnedMeshRenderer") as SkinnedMeshRenderer;
            mesh = smr.sharedMesh;
        }

        //Vertices//
        Vector3[] vrts = mesh.vertices;
        string vrtTxt = "";
        for ( i = 0; i < mesh.vertexCount; i++ ) {
            Vector3 vrt = VectorExponential(vrts[i]);
            vrt.x = -vrt.x;
            vrt *= zoom;
            if ( i > 0 )
                vrtTxt += ",";
            vrtTxt += VectorTxt(vrt);
        }
        AddExpTxt1("        Vertices: " + vrtTxt);

        //Triangles//
        int[] tris = mesh.triangles;
        string triTxt = "";
        for ( i = 0; i < tris.Length / 3; i++ ) {
            if ( i > 0 )
                triTxt += ",";
            triTxt += tris[i * 3 + 2];
            triTxt += ",";
            triTxt += tris[i * 3 + 1];
            triTxt += ",";
            triTxt += -(tris[i * 3 + 0] + 1);
        }
        AddExpTxt1("        PolygonVertexIndex: " + triTxt);

        //Normals//
        Vector3[] nrms = mesh.normals;
        string nrmTxt = "";
        for ( i = 0; i < nrms.Length; i++ ) {
            Vector3 nrm = nrms[i];
            nrm.x *= -1;
            if ( i > 0 )
                nrmTxt += ",";
            nrmTxt += VectorTxt(nrm);
        }
        AddExpTxt1("        LayerElementNormal: 0 {");
        AddExpTxt1("            MappingInformationType: \"ByVertice\"");
        AddExpTxt1("            ReferenceInformationType: \"Direct\"");
        AddExpTxt1("            Normals: " + nrmTxt);
        AddExpTxt1("        }");

        //UV//
        Vector2[] uvs = mesh.uv;
        if ( uvs.Length > 0 ) {
            string uvTxt = "";
            string uviTxt = "";
            for ( i = 0; i < uvs.Length; i++ ) {
                Vector2 uv = uvs[i];
                if ( i > 0 )
                    uvTxt += ",";
                uvTxt += FloatTxt(uv.x) + "," + FloatTxt(uv.y);

                if ( i > 0 )
                    uviTxt += ",";
                uviTxt += i;
            }
            AddExpTxt1("        LayerElementUV: 0 {");
            AddExpTxt1("            MappingInformationType: \"ByVertice\"");
            AddExpTxt1("            ReferenceInformationType: \"IndexToDirect\"");
            AddExpTxt1("            UV: " + uvTxt);
            AddExpTxt1("            UVIndex: " + uviTxt);
            AddExpTxt1("        }");
        }

        Vector2[] uv2s = mesh.uv2;
        if ( uv2s.Length > 0 ) {
            if ( uv2s.Length > 0 ) {
                string uvTxt = "";
                string uviTxt = "";
                for ( i = 0; i < uv2s.Length; i++ ) {
                    Vector2 uv = uv2s[i];
                    if ( i > 0 )
                        uvTxt += ",";
                    uvTxt += FloatTxt(uv.x) + "," + FloatTxt(uv.y);

                    if ( i > 0 )
                        uviTxt += ",";
                    uviTxt += i;
                }
                AddExpTxt1("        LayerElementUV: 1 {");
                AddExpTxt1("            MappingInformationType: \"ByVertice\"");
                AddExpTxt1("            ReferenceInformationType: \"IndexToDirect\"");
                AddExpTxt1("            UV: " + uvTxt);
                AddExpTxt1("            UVIndex: " + uviTxt);
                AddExpTxt1("        }");
            }
        }

        //Vertex Colors//
        Color[] colrs = mesh.colors;
        if ( colrs.Length > 0 ) {
            string colrTxt = "";
            string colriTxt = "";
            for ( i = 0; i < colrs.Length; i++ ) {
                Color clr = colrs[i];
                if ( i > 0 )
                    colrTxt += ",";
                colrTxt += clr.r + "," + clr.g + "," + clr.b + "," + clr.a;

                if ( i > 0 )
                    colriTxt += ",";
                colriTxt += i;
            }

            AddExpTxt1("        LayerElementColor: 0 {");
            AddExpTxt1("            MappingInformationType: \"ByVertice\"");
            AddExpTxt1("            ReferenceInformationType: \"IndexToDirect\"");
            AddExpTxt1("            Colors: " + colrTxt);
            AddExpTxt1("            ColorIndex: " + colriTxt);
            AddExpTxt1("        }");

        }

        //Layer infomation//
        AddExpTxt1("        Layer: 0 {");
        AddExpTxt1("            LayerElement: {");
        AddExpTxt1("                Type: \"LayerElementNormal\"");
        AddExpTxt1("                TypedIndex: 0");
        AddExpTxt1("            }");
        if ( uvs.Length > 0 ) {
            AddExpTxt1("            LayerElement: {");
            AddExpTxt1("                Type: \"LayerElementUV\"");
            AddExpTxt1("                TypedIndex: 0");
            AddExpTxt1("            }");
        }
        if ( colrs.Length > 0 ) {
            AddExpTxt1("            LayerElement: {");
            AddExpTxt1("                Type: \"LayerElementColor\"");
            AddExpTxt1("                TypedIndex: 0");
            AddExpTxt1("            }");
        }
        AddExpTxt1("        }");
        if ( uv2s.Length > 0 ) {
            AddExpTxt1("        Layer: 1 {");
            AddExpTxt1("            LayerElement: {");
            AddExpTxt1("                Type: \"LayerElementUV\"");
            AddExpTxt1("                TypedIndex: 1");
            AddExpTxt1("            }");
            AddExpTxt1("        }");
        }
        //AddExpTxt1("      NodeAttributeName: \"Geometry::" + exportObject.name + "\"");
        AddExpTxt1("    }");

        mdlCnt++;
    }

    void PoseTxt (int num)
    {
        int i;
        AddExpTxt1("    Pose: \"Pose::BIND_POSES\", \"BindPose\" {");
        AddExpTxt1("        NbPoseNodes: " + num);

        for ( i = 0; i < num; i++ ) {
            Transform tf = tfs[i];
            Transform pr = tf.parent;
            if ( !pr )
                pr = tf;
            Matrix4x4 mtx = exportObject.transform.localToWorldMatrix;
            Vector3 mtxVec = tf.position - exportObject.transform.position;
            mtxVec.x *= -1;
            mtx[0, 3] = mtxVec.x;
            mtx[1, 3] = mtxVec.y;
            mtx[2, 3] = mtxVec.z;

            mtx = MatrixExponential(mtx);
            string mtxTxt = MatrixTxt(mtx);

            AddExpTxt1("        PoseNode: {");
            AddExpTxt1("            Node: \"Model::" + tf.name + "\"");
            AddExpTxt1("            Matrix: " + mtxTxt);
            AddExpTxt1("        }");
        }
        AddExpTxt1("    }");
        pseCnt++;
    }

    //Deformer definition//
    void DeformerTxt (int num)
    {
        int i, j, k;

        Transform[] bnes = new Transform[0];
        BoneWeight[] wits = new BoneWeight[0];
        for ( i = 0; i < num; i++ ) {
            if ( types[i] == Type.Skin ) {
                SkinnedMeshRenderer smr = tfs[i].GetComponent("SkinnedMeshRenderer") as SkinnedMeshRenderer;
                bnes = smr.bones;
                Mesh mesh = smr.sharedMesh;
                wits = mesh.boneWeights;

                AddExpTxt1("    Deformer: \"Deformer::Skin " + tfs[i].name + "\", \"Skin\" {");
                AddExpTxt1("        Type: \"Skin\"");
                AddExpTxt1("    }");
                dfmCnt++;

                for ( j = 0; j < bnes.Length; j++ ) {
                    Transform bne = bnes[j];
                    string idxTxt = "";
                    string witTxt = "";
                    for ( k = 0; k < wits.Length; k++ ) {
                        BoneWeight wit = wits[k];
                        if ( wit.boneIndex0 == j && wit.weight0 > 0 ) {
                            if ( idxTxt.Length > 0 )
                                idxTxt += ",";
                            if ( witTxt.Length > 0 )
                                witTxt += ",";
                            idxTxt += k;
                            witTxt += FloatExponential(wit.weight0);
                        } else if ( wit.boneIndex1 == j && wit.weight1 > 0 ) {
                            if ( idxTxt.Length > 0 )
                                idxTxt += ",";
                            if ( witTxt.Length > 0 )
                                witTxt += ",";
                            idxTxt += k;
                            witTxt += FloatExponential(wit.weight1);
                        } else if ( wit.boneIndex2 == j && wit.weight2 > 0 ) {
                            if ( idxTxt.Length > 0 )
                                idxTxt += ",";
                            if ( witTxt.Length > 0 )
                                witTxt += ",";
                            idxTxt += k;
                            witTxt += FloatExponential(wit.weight2);
                        } else if ( wit.boneIndex3 == j && wit.weight3 > 0 ) {
                            if ( idxTxt.Length > 0 )
                                idxTxt += ",";
                            if ( witTxt.Length > 0 )
                                witTxt += ",";
                            idxTxt += k;
                            witTxt += FloatExponential(wit.weight3);
                        }
                    }


                    //Create bone rotation matrix//
                    Transform parent = bne.parent;
                    if ( !parent )
                        parent = bne;

                    Quaternion bRot = bne.rotation;
                    Vector3 eulr = bRot.eulerAngles;
                    eulr.y *= -1;
                    eulr.z *= -1;
                    bne.rotation = Quaternion.Euler(eulr);

                    Matrix4x4 mtx0 = tfs[i].transform.localToWorldMatrix * bne.worldToLocalMatrix;
                    //mtx0[0, 3] *= -1;//FBXとX方向が逆

                    Vector3 mtxVec = bne.position;
                    mtxVec.y *= -1;
                    mtxVec.z *= -1;
                    mtxVec = Quaternion.Inverse(bne.rotation) * mtxVec;
                    mtx0[0, 3] = mtxVec.x;
                    mtx0[1, 3] = mtxVec.y;
                    mtx0[2, 3] = mtxVec.z;

                    mtx0 = MatrixExponential(mtx0);
                    string tfTxt = MatrixTxt(mtx0);

                    bne.rotation = bRot;

                    Matrix4x4 mtx1 = parent.localToWorldMatrix * bne.worldToLocalMatrix;
                    mtx1 = MatrixExponential(mtx1);
                    string tflTxt = MatrixTxt(mtx1);

                    AddExpTxt1("    Deformer: \"SubDeformer::Cluster " + bne.name + "_" + tfs[i].name + "\", \"Cluster\" {");
                    AddExpTxt1("        Type: \"Cluster\"");
                    AddExpTxt1("        Indexes: " + idxTxt);
                    AddExpTxt1("        Weights: " + witTxt);
                    AddExpTxt1("        Transform: " + tfTxt);
                    AddExpTxt1("        TransformLink: " + tflTxt);
                    AddExpTxt1("    }");

                    dfmCnt++;
                }
            }
        }

    }

    //Connection definition//
    void ConnectTxt (int num)
    {
        AddExpTxt1("; Object connections");
        AddExpTxt1(";------------------------------------------------------------------");
        AddExpTxt1("");
        AddExpTxt1("Connections: {");

        int i, j;
        string name = "";
        string pname = "";
        Transform tf = null;
        Transform prt = null;
        for ( i = 0; i < num; i++ ) {
            tf = tfs[i];
            prt = tf.parent;
            if ( prt ) {
                pname = prt.name;
            } else {
                pname = "Scene";
            }
            name = tfs[i].name;

            AddExpTxt1("    Connect: \"OO\", \"Model::" + name + "\", \"Model::" + pname + "\"");
        }

        Transform[] bnes = new Transform[0];
        int skinCount = 0;
        if ( isSkin ) {
            for ( i = 0; i < num; i++ ) {
                if ( types[i] == Type.Skin ) {
                    tf = tfs[i];
                    SkinnedMeshRenderer smr = tf.GetComponent("SkinnedMeshRenderer") as SkinnedMeshRenderer;
                    if ( !smr )
                        continue;

                    skinCount++;
                    string skinName = "Skin " + tf.name;
                    AddExpTxt1("    Connect: \"OO\", \"Deformer::" + skinName + "\", \"Model::" + tf.name + "\"");

                    bnes = smr.bones;
                    for ( j = 0; j < bnes.Length; j++ ) {
                        Transform bne = bnes[j];
                        AddExpTxt1("    Connect: \"OO\", \"SubDeformer::Cluster " + bne.name + "_" + tf.name + "\", \"Deformer::" + skinName + "\"");
                        AddExpTxt1("    Connect: \"OO\", \"Model::" + bne.name + "\", \"SubDeformer::Cluster " + bne.name + "_" + tf.name + "\"");
                    }
                }
            }
        }
        AddExpTxt1("}");
    }

    //Animation definition//
    void AnimationText ()
    {
        AddExpTxt1(";Takes and animation section");
        AddExpTxt1(";------------------------------------------------------------------");
        AddExpTxt1("");
        AddExpTxt1("Takes:  {");
        AddExpTxt1("    Current: \"" + clip.name + "\"");
        AddExpTxt1("    Take: \"" + clip.name + "\" {");
        AddExpTxt1("        FileName: \"" + clip.name + ".tak\"");
        AddExpTxt1("        LocalTime: 0," + FloatExponential(clip.length));
        AddExpTxt1("        ReferenceTime: 0," + FloatExponential(clip.length));
        AddExpTxt1("");
        AddExpTxt1("        ;Models animation");
        AddExpTxt1("        ;----------------------------------------------------");
        AnimationModelText();
        AddExpTxt1("    }");
        AddExpTxt1("}");
    }

    public AnimationData[] animationDatas;
    void AnimationModelText ()
    {
        GetAnimationDatas();
        for ( int i = 0; i < animationDatas.Length; i++ ) {
            var animData = animationDatas[i];
            var anmTf = exportObject.transform.Find(animData.path);
            string name = animData.name;
            AddExpTxt1("        Model: \"Model::" + name + "\" {");
            AddExpTxt1("            Version: 1.1");
            AddExpTxt1("            Channel: \"Transform\" {");
            if ( animData.positionX.length > 0 && animData.positionY.length > 0 && animData.positionZ.length > 0 ) {
                var defaultPos = Vector3.zero;
                if ( anmTf != null ) {
                    defaultPos = anmTf.localPosition;
                }
                AddExpTxt1("                Channel: \"T\" {");
                AddExpTxt1("                    Channel: \"X\" {");
                AddExpTxt1("                        Default: " + FloatExponential(defaultPos.x * zoom));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.positionX.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.positionX.keys, zoom));
                AddExpTxt1("                        Color: 1,0,0");
                AddExpTxt1("                    }");
                AddExpTxt1("                    Channel: \"Y\" {");
                AddExpTxt1("                        Default: " + FloatExponential(defaultPos.y * zoom));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.positionY.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.positionY.keys, zoom));
                AddExpTxt1("                        Color: 0,1,0");
                AddExpTxt1("                    }");
                AddExpTxt1("                    Channel: \"Z\" {");
                AddExpTxt1("                        Default: " + FloatExponential(defaultPos.z * zoom));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.positionZ.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.positionZ.keys, zoom));
                AddExpTxt1("                        Color: 0,0,1");
                AddExpTxt1("                    }");
                AddExpTxt1("                    LayerType: 1");
                AddExpTxt1("                }");
            }
            if ( animData.eulerX.length > 0 && animData.eulerY.length > 0 && animData.eulerZ.length > 0 ) {
                var eul = Vector3.zero;
                if ( anmTf != null ) {
                    eul = GetEulerXYZ(anmTf.localRotation);
                    eul.y = -eul.y;
                    eul.z = -eul.z;
                }
                AddExpTxt1("                Channel: \"R\" {");
                AddExpTxt1("                    Channel: \"X\" {");
                AddExpTxt1("                        Default: " + FloatExponential(eul.x));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.eulerX.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.eulerX.keys, 1));
                AddExpTxt1("                        Color: 1,0,0");
                AddExpTxt1("                    }");
                AddExpTxt1("                    Channel: \"Y\" {");
                AddExpTxt1("                        Default: " + FloatExponential(eul.y));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.eulerY.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.eulerY.keys, 1));
                AddExpTxt1("                        Color: 0,1,0");
                AddExpTxt1("                    }");
                AddExpTxt1("                    Channel: \"Z\" {");
                AddExpTxt1("                        Default: " + FloatExponential(eul.z));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.eulerZ.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.eulerZ.keys, 1));
                AddExpTxt1("                        Color: 0,0,1");
                AddExpTxt1("                    }");
                AddExpTxt1("                    LayerType: 2");
                AddExpTxt1("                }");
            }
            if ( animData.scaleX.length > 0 && animData.scaleY.length > 0 && animData.scaleZ.length > 0 ) {
                var defaultScl = Vector3.zero;
                if ( anmTf != null ) {
                    defaultScl = anmTf.localPosition;
                }
                AddExpTxt1("                Channel: \"S\" {");
                AddExpTxt1("                    Channel: \"X\" {");
                AddExpTxt1("                        Default: " + FloatExponential(defaultScl.x));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.scaleX.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.scaleX.keys, 1));
                AddExpTxt1("                        Color: 1,0,0");
                AddExpTxt1("                    }");
                AddExpTxt1("                    Channel: \"Y\" {");
                AddExpTxt1("                        Default: " + FloatExponential(defaultScl.y));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.scaleY.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.scaleY.keys, 1));
                AddExpTxt1("                        Color: 0,1,0");
                AddExpTxt1("                    }");
                AddExpTxt1("                    Channel: \"Z\" {");
                AddExpTxt1("                        Default: " + FloatExponential(defaultScl.z));
                AddExpTxt1("                        KeyVer: 4005");
                AddExpTxt1("                        KeyCount: " + animData.scaleZ.keys.Length);
                AddExpTxt1("                        Key:" + KeyText(animData.scaleZ.keys, 1));
                AddExpTxt1("                        Color: 0,0,1");
                AddExpTxt1("                    }");
                AddExpTxt1("                    LayerType: 3");
                AddExpTxt1("                }");
                AddExpTxt1("            }");
            }
            AddExpTxt1("            Channel: \"Visibility\" {");
            AddExpTxt1("                Default: 1");
            AddExpTxt1("                Color: 0.75,0,0");
            AddExpTxt1("                LayerType: 1");
            AddExpTxt1("            }");
            AddExpTxt1("        }");
        }
    }

    string KeyText (Keyframe[] keys, float _zoom)
    {
        string text = "";
        int num = keys.Length;
        for ( int i = 0; i < num; i++ ) {
            text += ((double)keys[i].time * 46232344158).ToString();//FBXの謎定数46232344158
            text += ",";
            text += FloatExponential(keys[i].value * _zoom);
            text += ",U,s,";
            text += FloatExponential(keys[i].outTangent * _zoom);
            text += ",";
            if ( i < num - 1 ) {
                text += FloatExponential(keys[i + 1].inTangent * _zoom);
            } else {
                text += "0";
            }
            text += ",n";
            if ( i < num - 1 ) {
                text += ",";
            }
        }
        return text;
    }

    [System.Serializable]
    public class AnimationData
    {
        public string name;
        public string path;
        public AnimationCurve positionX;
        public AnimationCurve positionY;
        public AnimationCurve positionZ;
        public AnimationCurve eulerX;
        public AnimationCurve eulerY;
        public AnimationCurve eulerZ;
        public AnimationCurve rotationX;
        public AnimationCurve rotationY;
        public AnimationCurve rotationZ;
        public AnimationCurve rotationW;
        public AnimationCurve scaleX;
        public AnimationCurve scaleY;
        public AnimationCurve scaleZ;
        public AnimationData (string _path)
        {
            name = System.IO.Path.GetFileName(_path);
            path = _path;
            positionX = new AnimationCurve();
            positionY = new AnimationCurve();
            positionZ = new AnimationCurve();
            eulerX = new AnimationCurve();
            eulerY = new AnimationCurve();
            eulerZ = new AnimationCurve();
            rotationX = new AnimationCurve();
            rotationY = new AnimationCurve();
            rotationZ = new AnimationCurve();
            rotationW = new AnimationCurve();
            scaleX = new AnimationCurve();
            scaleY = new AnimationCurve();
            scaleZ = new AnimationCurve();
        }
    }

    void GetAnimationDatas ()
    {
        AnimationData[] _animDatas = new AnimationData[100];
        int pt = 0;
        var curveBindings = AnimationUtility.GetCurveBindings(clip);
        for ( int i = 0; i < curveBindings.Length; i++ ) {
            var binding = curveBindings[i];
            AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, binding);


            if ( binding.type != typeof(Transform) ) { continue; }
            var datPath = binding.path;
            int hitPt = -1;
            for ( int j = 0; j < pt; j++ ) {
                if ( _animDatas[j].path == datPath ) {
                    hitPt = j;
                    break;
                }
            }
            if ( hitPt == -1 ) {
                hitPt = pt;
                pt++;
                _animDatas[hitPt] = new AnimationData(datPath);
            }
            var propertyName = binding.propertyName;
            if ( propertyName == "m_LocalPosition.x" ) {
                _animDatas[hitPt].positionX = CurveReverse(curve);
            } else if ( propertyName == "m_LocalPosition.y" ) {
                _animDatas[hitPt].positionY = curve;
            } else if ( propertyName == "m_LocalPosition.z" ) {
                _animDatas[hitPt].positionZ = curve;
            } else if ( propertyName == "localEulerAnglesRaw.x" ) {
                _animDatas[hitPt].eulerX = curve;
            } else if ( propertyName == "localEulerAnglesRaw.y" ) {
                _animDatas[hitPt].eulerY = curve;
            } else if ( propertyName == "localEulerAnglesRaw.z" ) {
                _animDatas[hitPt].eulerZ = curve;
            } else if ( propertyName == "m_LocalRotation.x" ) {
                _animDatas[hitPt].rotationX = curve;
            } else if ( propertyName == "m_LocalRotation.y" ) {
                _animDatas[hitPt].rotationY = curve;
            } else if ( propertyName == "m_LocalRotation.z" ) {
                _animDatas[hitPt].rotationZ = curve;
            } else if ( propertyName == "m_LocalRotation.w" ) {
                _animDatas[hitPt].rotationW = curve;
            } else if ( propertyName == "m_LocalScale.x" ) {
                _animDatas[hitPt].scaleX = curve;
            } else if ( propertyName == "m_LocalScale.y" ) {
                _animDatas[hitPt].scaleY = curve;
            } else if ( propertyName == "m_LocalScale.z" ) {
                _animDatas[hitPt].scaleZ = curve;
            } else {
                Debug.Log("AnimationPropertyName = " + propertyName);
            }
        }
        animationDatas = new AnimationData[pt];
        for ( int i = 0; i < pt; i++ ) {
            animationDatas[i] = _animDatas[i];
        }

        //Plot rotation once
        for ( int i = 0; i < pt; i++ ) {
            var anmData = animationDatas[i];
            if ( anmData.eulerX.length > 0 && anmData.eulerY.length > 0 && anmData.eulerZ.length > 0 ) {
                float anmLength = animationDatas[i].eulerX.keys[animationDatas[i].eulerX.keys.Length - 1].time;
                int numFrame = Mathf.FloorToInt(anmLength * rotationPlotQuarity) + 1;
                var keysX = new Keyframe[numFrame];
                var keysY = new Keyframe[numFrame];
                var keysZ = new Keyframe[numFrame];
                for ( int j = 0; j < numFrame; j++ ) {
                    float t = anmLength * j / (numFrame - 1);
                    var rot = Quaternion.Euler(anmData.eulerX.Evaluate(t), anmData.eulerY.Evaluate(t), anmData.eulerZ.Evaluate(t));
                    var eul = GetEulerXYZ(rot);
                    eul.y = -eul.y;
                    eul.z = -eul.z;
                    keysX[j] = new Keyframe();
                    keysX[j].time = t;
                    keysX[j].value = eul.x;
                    keysY[j] = new Keyframe();
                    keysY[j].time = t;
                    keysY[j].value = eul.y;
                    keysZ[j] = new Keyframe();
                    keysZ[j].time = t;
                    keysZ[j].value = eul.z;
                }
                anmData.eulerX = new AnimationCurve(Softkeys(keysX));
                anmData.eulerY = new AnimationCurve(Softkeys(keysY));
                anmData.eulerZ = new AnimationCurve(Softkeys(keysZ));
            } else if ( anmData.rotationX.length > 0 && anmData.rotationY.length > 0 && anmData.rotationZ.length > 0 && anmData.rotationW.length > 0 ) {
                float anmLength = animationDatas[i].rotationX.keys[animationDatas[i].rotationX.keys.Length - 1].time;
                int numFrame = Mathf.FloorToInt(anmLength * rotationPlotQuarity) + 1;
                var keysX = new Keyframe[numFrame];
                var keysY = new Keyframe[numFrame];
                var keysZ = new Keyframe[numFrame];
                for ( int j = 0; j < numFrame; j++ ) {
                    float t = anmLength * j / (numFrame - 1);
                    var rot = new Quaternion(anmData.rotationX.Evaluate(t), anmData.rotationY.Evaluate(t), anmData.rotationZ.Evaluate(t), anmData.rotationW.Evaluate(t));
                    var eul = GetEulerXYZ(rot);
                    eul.y = -eul.y;
                    eul.z = -eul.z;
                    keysX[j] = new Keyframe();
                    keysX[j].time = t;
                    keysX[j].value = eul.x;
                    keysY[j] = new Keyframe();
                    keysY[j].time = t;
                    keysY[j].value = eul.y;
                    keysZ[j] = new Keyframe();
                    keysZ[j].time = t;
                    keysZ[j].value = eul.z;
                }
                anmData.eulerX = new AnimationCurve(Softkeys(keysX));
                anmData.eulerY = new AnimationCurve(Softkeys(keysY));
                anmData.eulerZ = new AnimationCurve(Softkeys(keysZ));
            }
        }
    }

    //Soften key frame slope
    public Keyframe[] Softkeys (Keyframe[] keys)
    {
        int numKeys = keys.Length;
        for ( int i = 0; i < numKeys; i++ ) {
            float bt, bv, at, av;
            if ( i == 0 ) { bt = keys[i].time; bv = keys[i].value; } else { bt = keys[i - 1].time; bv = keys[i - 1].value; }
            if ( i < numKeys - 1 ) { at = keys[i + 1].time; av = keys[i + 1].value; } else { at = keys[i].time; av = keys[i].value; }
            float frameLength = at - bt;
            float tan;
            if ( frameLength != 0 ) { tan = (av - bv) / frameLength; } else { tan = 0f; }
            keys[i].inTangent = tan;
            keys[i].outTangent = tan;
        }
        return keys;
    }

    AnimationCurve CurveReverse (AnimationCurve curve)
    {
        var keys = curve.keys;
        for ( int i = 0; i < keys.Length; i++ ) {
            keys[i].value = -keys[i].value;
            keys[i].inTangent = -keys[i].inTangent;
            keys[i].outTangent = -keys[i].outTangent;
        }
        curve.keys = keys;
        return curve;
    }

    Vector3 VectorExponential (Vector3 vec)
    {
        return new Vector3(FloatExponential(vec.x), FloatExponential(vec.y), FloatExponential(vec.z));
    }

    Matrix4x4 MatrixExponential (Matrix4x4 mtx)
    {
        Vector4 mc0 = mtx.GetColumn(0);
        Vector4 mc1 = mtx.GetColumn(1);
        Vector4 mc2 = mtx.GetColumn(2);
        Vector4 mc3 = mtx.GetColumn(3);
        mc0[0] = FloatExponential(mc0[0]);
        mc0[1] = FloatExponential(mc0[1]);
        mc0[2] = FloatExponential(mc0[2]);
        mc0[3] = FloatExponential(mc0[3]);
        mc1[0] = FloatExponential(mc1[0]);
        mc1[1] = FloatExponential(mc1[1]);
        mc1[2] = FloatExponential(mc1[2]);
        mc1[3] = FloatExponential(mc1[3]);
        mc2[0] = FloatExponential(mc2[0]);
        mc2[1] = FloatExponential(mc2[1]);
        mc2[2] = FloatExponential(mc2[2]);
        mc2[3] = FloatExponential(mc2[3]);
        mc3[0] = FloatExponential(mc3[0]);
        mc3[1] = FloatExponential(mc3[1]);
        mc3[2] = FloatExponential(mc3[2]);
        mc3[3] = FloatExponential(mc3[3]);
        mtx.SetColumn(0, mc0);
        mtx.SetColumn(1, mc1);
        mtx.SetColumn(2, mc2);
        mtx.SetColumn(3, mc3);
        return mtx;
    }

    float FloatExponential (float f)
    {
        string s0 = "" + f;
        int len = s0.Length;
        int i, j;
        for ( i = 0; i < len; i++ ) {
            if ( s0.Substring(i, 1) == "E" ) {
                int exp;
                if ( s0.Substring(i + 1, 1) == "+" ) {
                    exp = System.Int32.Parse(s0.Substring(i + 2, len - i - 2));
                    for ( j = 0; j < exp; j++ ) {
                        f *= 10;
                    }
                    i = len;
                } else {
                    exp = System.Int32.Parse(s0.Substring(i + 2, len - i - 2));
                    for ( j = 0; j < exp; j++ ) {
                        f /= 10;
                    }
                    i = len;
                }
            }
        }

        if ( Mathf.Abs(f % 1) < 0.0001f || Mathf.Abs(f % 1) > 0.9999f ) {
            f = Mathf.Round(f);
        }

        return f;
    }

    string VectorTxt (Vector3 vec)
    {
        string vecTxt = FloatTxt(vec.x) + "," + FloatTxt(vec.y) + "," + FloatTxt(vec.z);
        return vecTxt;
    }

    string MatrixTxt (Matrix4x4 mtx)
    {
        Vector4 mc0 = mtx.GetColumn(0);
        Vector4 mc1 = mtx.GetColumn(1);
        Vector4 mc2 = mtx.GetColumn(2);
        Vector4 mc3 = mtx.GetColumn(3);

        string mtxTxt = FloatTxt(mc0[0]) + "," + FloatTxt(mc0[1]) + "," + FloatTxt(mc0[2]) + "," + FloatTxt(mc0[3]) +
            "," + FloatTxt(mc1[0]) + "," + FloatTxt(mc1[1]) + "," + FloatTxt(mc1[2]) + "," + FloatTxt(mc1[3]) +
            "," + FloatTxt(mc2[0]) + "," + FloatTxt(mc2[1]) + "," + FloatTxt(mc2[2]) + "," + FloatTxt(mc2[3]) +
            "," + FloatTxt(mc3[0] * zoom) + "," + FloatTxt(mc3[1] * zoom) + "," + FloatTxt(mc3[2] * zoom) + "," + FloatTxt(mc3[3]);
        return mtxTxt;
    }

    string FloatTxt (float f)
    {
        return "" + FloatExponential(f);
    }

    //Sub mesh process--------------------------------------------------------------------------------
    class DivisionSubMeshState
    {
        public Transform baseObj;
        public Transform parent;
        public Transform[] divisionObjs;
        public Vector3 position;
        public Quaternion rotation;
        public Vector3 scale;
        public DivisionSubMeshState (Transform _baseObj)
        {
            baseObj = _baseObj;
            parent = _baseObj.parent;
            divisionObjs = new Transform[0];
            position = _baseObj.transform.localPosition;
            rotation = _baseObj.transform.localRotation;
            scale = _baseObj.transform.localScale;
        }
    }
    private DivisionSubMeshState[] divStates;

    //Split objects including submeshes into multiple mesh objects
    void SubMeshCheck ()
    {
        divStates = new DivisionSubMeshState[0];
        var mfilts = exportObject.GetComponentsInChildren<MeshFilter>();
        if ( mfilts.Length > 0 ) {
            divStates = new DivisionSubMeshState[mfilts.Length];
            for ( int i = 0; i < mfilts.Length; i++ ) {
                var mesh = mfilts[i].sharedMesh;
                if ( mesh == null ) { continue; }
                if ( mesh.subMeshCount > 1 ) {
                    divStates[i] = DivisionSubMesh(mfilts[i].gameObject);
                    //サブメッシュオブジェをWorldRootに置き、分割オブジェを代わりに置く
                    if ( divStates[i].divisionObjs.Length > 0 ) {
                        for ( int j = 0; j < divStates[i].divisionObjs.Length; j++ ) {
                            divStates[i].divisionObjs[j].SetParent(divStates[i].parent);
                            divStates[i].divisionObjs[j].localPosition = divStates[i].position;
                            divStates[i].divisionObjs[j].localRotation = divStates[i].rotation;
                            divStates[i].divisionObjs[j].localScale = divStates[i].scale;
                        }
                        divStates[i].baseObj.SetParent(null);
                    }
                }
            }
            return;
        }
        var srends = exportObject.GetComponentsInChildren<SkinnedMeshRenderer>();
        if ( srends.Length > 0 ) {
            divStates = new DivisionSubMeshState[srends.Length];
            for ( int i = 0; i < srends.Length; i++ ) {
                var mesh = srends[i].sharedMesh;
                if ( mesh == null ) { continue; }
                if ( mesh.subMeshCount > 1 ) {
                    divStates[i] = DivisionSubMesh(srends[i].gameObject);
                    //サブメッシュオブジェをWorldRootに置き、分割オブジェを代わりに置く
                    if ( divStates[i].divisionObjs.Length > 0 ) {
                        for ( int j = 0; j < divStates[i].divisionObjs.Length; j++ ) {
                            divStates[i].divisionObjs[j].SetParent(divStates[i].parent);
                            divStates[i].divisionObjs[j].localPosition = divStates[i].position;
                            divStates[i].divisionObjs[j].localRotation = divStates[i].rotation;
                            divStates[i].divisionObjs[j].localScale = divStates[i].scale;
                        }
                        divStates[i].baseObj.SetParent(null);
                    }
                }
            }
        }
    }

    //Sub mesh object split
    DivisionSubMeshState DivisionSubMesh (GameObject meshObject)
    {
        var ret = new DivisionSubMeshState(meshObject.transform);
        if ( meshObject == null ) { return ret; }
        var _mrend = meshObject.GetComponent<MeshRenderer>();
        var _mfilt = meshObject.GetComponent<MeshFilter>();
        var _srend = meshObject.GetComponent<SkinnedMeshRenderer>();
        Material[] mats = new Material[0];
        Type type;
        Mesh _mesh = null;
        if ( _mfilt != null ) {
            if ( _mrend == null ) { return ret; }
            _mesh = _mfilt.sharedMesh;
            mats = _mrend.sharedMaterials;
            type = Type.Mesh;
        } else {
            if ( _srend == null ) { return ret; }
            _mesh = _srend.sharedMesh;
            mats = _srend.sharedMaterials;
            type = Type.Skin;
        }
        if ( _mesh == null ) { return ret; }
        int meshCount = _mesh.subMeshCount;
        if ( meshCount <= 1 ) { return ret; }
        ret.divisionObjs = new Transform[meshCount];

        for ( int i = 0; i < meshCount; i++ ) {
            Mesh mesh = new Mesh();
            mesh.name = _mesh.name + " " + i;
            GameObject gameObj = new GameObject(meshObject.name + " " + i);
            if ( type == Type.Mesh ) {
                var mrend = gameObj.AddComponent<MeshRenderer>();
                if ( mats.Length <= i + 1 ) {
                    mrend.sharedMaterial = mats[i];
                } else if ( mats.Length > 0 ) {
                    mrend.sharedMaterial = mats[0];
                }
                var mfilt = gameObj.AddComponent<MeshFilter>();
                mfilt.sharedMesh = mesh;
            } else if ( type == Type.Skin ) {
                var srend = gameObj.AddComponent<SkinnedMeshRenderer>();
                if ( mats.Length <= i + 1 ) {
                    srend.sharedMaterial = mats[i];
                } else if ( mats.Length > 0 ) {
                    srend.sharedMaterial = mats[0];
                }
                srend.bones = _srend.bones;
                srend.rootBone = _srend.rootBone;
                srend.sharedMesh = mesh;
            }

            Vector3[] _verts = _mesh.vertices;
            Vector2[] _uvs = _mesh.uv;
            Vector2[] _uv2s = _mesh.uv2;
            Vector2[] _uv3s = _mesh.uv3;
            Vector2[] _uv4s = _mesh.uv4;
            Vector3[] _norms = _mesh.normals;
            Color32[] _colrs = _mesh.colors32;
            Vector4[] _tans = _mesh.tangents;
            BoneWeight[] _weits = _mesh.boneWeights;
            Matrix4x4[] _poses = _mesh.bindposes;
            int[] tris = _mesh.GetTriangles(i);
            //使っている頂点を調べる
            bool[] uses = new bool[_mesh.vertexCount];
            for ( int j = 0; j < tris.Length; j++ ) {
                uses[tris[j]] = true;
            }
            //使わない頂点を省いたindex
            int[] idxs = new int[_mesh.vertexCount];
            int pt = 0;
            for ( int j = 0; j < _mesh.vertexCount; j++ ) {
                if ( uses[j] ) {
                    idxs[j] = pt;
                    pt++;
                }
            }
            //Reconstruct only with vertices used
            Vector3[] verts = new Vector3[pt];
            Vector2[] uvs = new Vector2[pt];
            Vector2[] uv2s = new Vector2[0];
            Vector2[] uv3s = new Vector2[0];
            Vector2[] uv4s = new Vector2[0];
            if ( _uv2s.Length > 0 ) { uv2s = new Vector2[pt]; }
            if ( _uv3s.Length > 0 ) { uv3s = new Vector2[pt]; }
            if ( _uv4s.Length > 0 ) { uv4s = new Vector2[pt]; }
            Vector3[] norms = new Vector3[0];
            if ( _norms.Length > 0 ) { norms = new Vector3[pt]; }
            Color32[] colrs = new Color32[0];
            if ( _colrs.Length > 0 ) { colrs = new Color32[pt]; }
            Vector4[] tans = new Vector4[0];
            if ( _tans.Length > 0 ) { tans = new Vector4[pt]; }
            BoneWeight[] weits = new BoneWeight[0];
            if ( _weits.Length > 0 ) { weits = new BoneWeight[pt]; }

            for ( int j = 0; j < tris.Length; j++ ) {
                tris[j] = idxs[tris[j]];
            }
            for ( int j = 0; j < _mesh.vertexCount; j++ ) {
                if ( uses[j] ) {
                    verts[idxs[j]] = _verts[j];
                    uvs[idxs[j]] = _uvs[j];
                    if ( _uv2s.Length > 0 ) { uv2s[idxs[j]] = _uv2s[j]; }
                    if ( _uv3s.Length > 0 ) { uv3s[idxs[j]] = _uv3s[j]; }
                    if ( _uv4s.Length > 0 ) { uv4s[idxs[j]] = _uv4s[j]; }
                    if ( _norms.Length > 0 ) { norms[idxs[j]] = _norms[j]; }
                    if ( _colrs.Length > 0 ) { colrs[idxs[j]] = _colrs[j]; }
                    if ( _tans.Length > 0 ) { tans[idxs[j]] = _tans[j]; }
                    if ( _weits.Length > 0 ) { weits[idxs[j]] = _weits[j]; }
                }
            }

            mesh.vertices = verts;
            mesh.uv = uvs;
            mesh.triangles = tris;
            if ( uv2s.Length > 0 ) { mesh.uv2 = uv2s; }
            if ( uv3s.Length > 0 ) { mesh.uv3 = uv3s; }
            if ( uv4s.Length > 0 ) { mesh.uv4 = uv4s; }
            if ( norms.Length > 0 ) { mesh.normals = norms; }
            if ( colrs.Length > 0 ) { mesh.colors32 = colrs; }
            if ( tans.Length > 0 ) { mesh.tangents = tans; }
            if ( weits.Length > 0 ) { mesh.boneWeights = weits; }
            if ( _poses.Length > 0 ) { mesh.bindposes = _poses; }
            ret.divisionObjs[i] = gameObj.transform;
        }
        return ret;
    }

    //Place the exploded object in WorldRoot and return the original submesh object
    void SubMeshEndProcess ()
    {
        if ( divStates != null ) {
            for ( int i = 0; i < divStates.Length; i++ ) {
                if ( divStates[i] != null && divStates[i].divisionObjs.Length > 0 ) {
                    for ( int j = 0; j < divStates[i].divisionObjs.Length; j++ ) {
                        divStates[i].divisionObjs[j].SetParent(null);
                    }
                    divStates[i].baseObj.SetParent(divStates[i].parent);
                }
            }
        }
    }

    //Euler of rotation axis XYZ//
    public Vector3 GetEulerXYZ (Quaternion rot)
    {
        if ( rot == Quaternion.identity )
            return Vector3.zero;
        Vector3 vec = rot * Vector3.right;
        float x = vec.x;
        float y = vec.y;
        float z = vec.z;
        float zRad = 0f;
        if ( Mathf.Abs(x) > Mathf.Abs(y) ) {
            if ( x > 0.00001f ) {
                zRad = Mathf.Atan(y / x);
            } else if ( x < -0.00001f ) {
                zRad = Mathf.Atan(y / x) + 3.14159265f;
                if ( zRad > 3.14159265f )
                    zRad -= 6.2831853f;
            } else if ( y > 0.00001f ) {
                zRad = -1.5707963f;
            } else {
                zRad = -1.5707963f;
            }
        } else {
            if ( y > 0.00001f ) {
                zRad = -Mathf.Atan(x / y) + 1.5707963f;
            } else if ( y < -0.00001f ) {
                zRad = -Mathf.Atan(x / y) - 1.5707963f;
            }
        }

        x = x * Mathf.Cos(-zRad) - y * Mathf.Sin(-zRad);

        float yRad = 0f;
        if ( Mathf.Abs(x) > Mathf.Abs(z) ) {
            if ( x > 0.00001f ) {
                yRad = -Mathf.Atan(z / x);
            } else if ( x < -0.00001f ) {
                yRad = Mathf.Atan(z / x) + 3.14159265f;
                if ( yRad > 3.14159265f )
                    yRad -= 6.2831853f;
            }
        } else {
            if ( z > 0.00001f ) {
                yRad = Mathf.Atan(x / z) - 1.5707963f;
            } else if ( z < -0.00001f ) {
                yRad = Mathf.Atan(x / z) + 1.5707963f;
            }
        }

        float yAng = yRad * 57.2957795f;
        float zAng = zRad * 57.2957795f;
        Quaternion xRot = Quaternion.Inverse(Quaternion.Euler(0, 0, zAng) * Quaternion.Euler(0, yAng, 0)) * rot;
        Vector3 xEul = xRot.eulerAngles;
        float xAng = xEul.x;
        if ( Mathf.Abs(Mathf.DeltaAngle(xEul.y, 0)) > 136 && Mathf.Abs(Mathf.DeltaAngle(xEul.z, 0)) > 136 )
            xAng = xAng * -1 + 180;

        if ( xAng < -180 )
            xAng = xAng % 360 + 360;
        if ( xAng > 180 )
            xAng = xAng % 360 - 360;
        if ( yAng < -180 )
            yAng = yAng % 360 + 360;
        if ( yAng > 180 )
            yAng = yAng % 360 - 360;
        if ( zAng < -180 )
            zAng = zAng % 360 + 360;
        if ( zAng > 180 )
            zAng = zAng % 360 - 360;

        return new Vector3(xAng, yAng, zAng);
    }

    //Extract clip from Animator or Animation on Root
    void GetAnimationClip ()
    {
        clip = null;
        var animator = exportObject.GetComponent<Animator>();
        if ( animator != null ) {
            //var info = animator.GetCurrentAnimatorStateInfo(0);
            if ( animator.runtimeAnimatorController != null ) {
                var clips = animator.runtimeAnimatorController.animationClips;
                if ( clips.Length > 0 ) {
                    clip = clips[0];
                }
            }
            return;
        }
        var animation = exportObject.GetComponent<Animation>();
        if ( animation != null ) {
            clip = animation.clip;
        }
    }

    void ExportTexture ()
    {
        if ( !exportObject ) { return; }
        if ( filePath == "" ) { filePath = exportObject.name; }
        var directory = System.IO.Path.GetDirectoryName(filePath);
        if ( directory.Length != 0 ) { directory += "/"; }
        var rends = exportObject.GetComponents<Renderer>();
        for ( int i = 0; i < rends.Length; i++ ) {
            var mats = rends[i].sharedMaterials;
            for ( int j = 0; j < mats.Length; j++ ) {
                if ( mats[i].HasProperty("_MainTex") ) {
                    Texture2D tex = (Texture2D)mats[i].GetTexture("_MainTex");
                    if ( tex != null ) {
                        var path = "Assets/" + System.IO.Path.GetDirectoryName(filePath) + tex.name + ".png";
                        Texture2D tex2 = TextreClone(tex);
                        Debug.Log(path);
                        System.IO.File.WriteAllBytes(path, tex2.EncodeToPNG());
                    }
                }
                if ( mats[i].HasProperty("_DetailTex") ) {
                    Texture2D tex = (Texture2D)mats[i].GetTexture("_DetailTex");
                    if ( tex != null ) {
                        var path = "Assets/" + System.IO.Path.GetDirectoryName(filePath) + tex.name + ".png";
                        Texture2D tex2 = TextreClone(tex);
                        System.IO.File.WriteAllBytes(path, tex2.EncodeToPNG());
                    }
                }
                if ( mats[i].HasProperty("_Texture") ) {
                    Texture2D tex = (Texture2D)mats[i].GetTexture("_Texture");
                    if ( tex != null ) {
                        var path = "Assets/" + System.IO.Path.GetDirectoryName(filePath) + tex.name + ".png";
                        Texture2D tex2 = TextreClone(tex);
                        System.IO.File.WriteAllBytes(path, tex2.EncodeToPNG());
                    }
                }
            }
        }
    }

    Texture2D TextreClone (Texture2D tex)
    {
        var temp = RenderTexture.GetTemporary(tex.width, tex.height);
        Graphics.Blit(tex, temp);
        var copy = new Texture2D(tex.width, tex.height);
        copy.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
        copy.Apply();
        RenderTexture.ReleaseTemporary(temp);
        return copy;
    }

    void Export (string path, string str)
    {
        AssetDatabase.DeleteAsset(path);
        StreamWriter sw;
        sw = new StreamWriter(path, true);
        sw.Write(str);
        sw.Close();
    }
}
#endif