/// <summary>
        /// find a custom property which contains a given name
        /// </summary>
        /// <param name="bId">material id</param>
        /// <param name="propertyName">search key</param>
        /// <returns></returns>
        private CustomProperty _FindCustomProperty(BlendValueCapsule bId, string propertyName)
        {
            var bTopIdProperty  = bId.GetMember("properties").GetRawValue <BlendAddress>().DereferenceOne();
            var bNextIdProperty = bTopIdProperty.GetMember("data").GetMember("group").GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();

            while (bNextIdProperty != null)
            {
                // found
                var name = bNextIdProperty.GetMember("name").GetAllValueAsString();
                if (propertyName == name)
                {
                    var prop = new CustomProperty()
                    {
                        Name  = name,
                        Value = bNextIdProperty.GetMember("data").GetMember("val").GetRawValue <int>()
                    };
                    return(prop);
                }

                bNextIdProperty = bNextIdProperty.GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
            }

            // not fund
            return(null);
        }
Example #2
0
        private bool _LoadMesh(BlendTypeRepository repository, BlendValueCapsule meshObj)
        {
            var    mesh     = meshObj.GetMember("data").GetRawValue <BlendAddress>().DereferenceOne();
            string meshName = meshObj.GetMember("id").GetMember("name").GetAllValueAsString();
            int    mtlCount = meshObj.GetMember("totcol").GetRawValue <int>();

            // Build Armature
            DrawSystem.BoneData[] boneArray  = null;
            int[] deformGroupIndex2BoneIndex = null;
            AnimType.AnimationData animData;
            if (!_LoadArmature(repository, meshObj, out boneArray, out deformGroupIndex2BoneIndex, out animData))
            {
                // igrnore
            }

            // Build original vertices
            var originVertices = _CreateOriginalVertices(repository, mesh, deformGroupIndex2BoneIndex);

            // Build materials
            var pmtlType = new BlendPointerType(repository.Find("Material"));
            var mtls     = mesh.GetMember("mat").GetRawValue <BlendAddress>().DereferenceAll(pmtlType);

            Debug.Assert(mtls.Count == mtlCount, "material count is unmatched");

            for (int mtlIndex = 0; mtlIndex < mtlCount; ++mtlIndex)
            {
                // Build a vertex buffer(s)
                var vertices = originVertices
                               .Where(v => v.MaterialIndex == mtlIndex)
                               .ToArray();


                var bMaterial = mtls[mtlIndex].GetRawValue <BlendAddress>().DereferenceOne();
                if (bMaterial != null)
                {
                    Dictionary <DrawSystem.TextureTypes, TextureInfo> textureInfos = null;
                    MaterialBase material;
                    if (!_LoadMaterial(repository, bMaterial, out textureInfos, out material))
                    {
                        continue;
                    }

                    var node = new SceneNode()
                    {
                        Name         = meshName,
                        Vertics      = vertices,
                        MaterialData = material,
                        TextureInfos = textureInfos,
                        BoneArray    = mtlIndex == 0 ? boneArray : null,                     // set boneArray for the first node
                        Animation    = animData.Actions != null ? (AnimType.AnimationData?)animData : null,
                    };
                    m_nodeList.Add(node);
                }
            }

            return(m_nodeList.Count != 0);
        }
        private bool _LoadMaterial(BlendTypeRepository repository, BlendValueCapsule bMaterial, out Dictionary <DrawSystem.TextureTypes, TextureInfo> outTextureInfos, out MaterialBase outMaterial)
        {
            string mtlName = bMaterial.GetMember("id").GetMember("name").GetAllValueAsString();

            Console.WriteLine("    found material : " + mtlName);

            var    texInfos         = new Dictionary <DrawSystem.TextureTypes, TextureInfo>();
            string materialTypeName = Path.GetExtension(mtlName);

            switch (materialTypeName)
            {
            case "":
            case ".std":
                _LoadTextures(repository, bMaterial, ref texInfos,
                              new DrawSystem.TextureTypes[] { DrawSystem.TextureTypes.Diffuse0, DrawSystem.TextureTypes.Bump0 });
                outMaterial     = new StandardMaterial();
                outTextureInfos = texInfos;
                return(true);

            case ".map":
                _LoadTextures(repository, bMaterial, ref texInfos,
                              new DrawSystem.TextureTypes[] { DrawSystem.TextureTypes.Diffuse0 });
                outMaterial = new MinimapMaterial();
                texInfos.Add(DrawSystem.TextureTypes.MinimapRoute, new TextureInfo {
                    Name = "route.png", UvScale = new Vector2(1, 1)
                });                                                                                                                                             // add a special texture
                outTextureInfos = texInfos;
                return(true);

            case ".mark":
            {
                var prop = _FindCustomProperty(bMaterial.GetMember("id"), "id");
                if (prop == null)
                {
                    Debug.Fail("marker material must have id property");
                    break;
                }

                outMaterial     = MarkerMaterial.Create(prop.Value);
                outTextureInfos = texInfos;
            }
                return(true);

            default:
                Debug.Fail("unknown material type : " + materialTypeName);
                break;
            }

            outMaterial     = null;
            outTextureInfos = texInfos;
            return(false);
        }
        private void _LoadTextures(BlendTypeRepository repository, BlendValueCapsule bMaterial, ref Dictionary <DrawSystem.TextureTypes, TextureInfo> outTextureInfos, DrawSystem.TextureTypes[] supportTypes)
        {
            var mtexs     = bMaterial.GetMember("mtex");
            var mtexsType = mtexs.Type as BlendArrayType;

            for (int mtexIndex = 0; mtexIndex < mtexsType.GetLength(0); ++mtexIndex)
            {
                // Build textures
                var mtex = mtexs.GetAt(mtexIndex).GetRawValue <BlendAddress>().DereferenceOne();
                if (mtex != null)
                {
                    float scaleU = mtex.GetMember("size").GetAt(0).GetRawValue <float>();
                    float scaleV = mtex.GetMember("size").GetAt(1).GetRawValue <float>();
                    var   tex    = mtex.GetMember("tex").GetRawValue <BlendAddress>().DereferenceOne();
                    if (tex != null)
                    {
                        var ima = tex.GetMember("ima").GetRawValue <BlendAddress>().DereferenceOne();
                        if (ima != null)
                        {
                            var typename = tex.GetMember("id").GetMember("name").GetAllValueAsString();
                            typename = Path.GetFileNameWithoutExtension(typename);                            //  TEdiffuse.001 => TEdiffuse
                            var path = Path.GetFileName(ima.GetMember("name").GetAllValueAsString());

                            Console.WriteLine("    found texture : " + path);

                            DrawSystem.TextureTypes type = DrawSystem.TextureTypes.Diffuse0;
                            switch (typename)
                            {
                            case "TEdiffuse":
                                type = DrawSystem.TextureTypes.Diffuse0;
                                break;

                            case "TEnormal":
                                type = DrawSystem.TextureTypes.Bump0;
                                break;

                            default:
                                Debug.Fail("unsupported texture typename " + typename);
                                break;
                            }

                            outTextureInfos.Add(type, new TextureInfo {
                                Name = path, UvScale = new Vector2(scaleU, scaleV)
                            });
                        }
                    }
                }
            }
        }
Example #5
0
        private void _LoadBone(BlendTypeRepository repository, BlendValueCapsule bone, int parentBoneIndex, ref List <DrawSystem.BoneData> outList)
        {
            if (bone != null)
            {
                // make bone data
                var   name   = bone.GetMember("name").GetAllValueAsString();
                float length = bone.GetMember("length").GetRawValue <float>();
                var   offset = new Vector3()
                {
                    X = bone.GetMember("head").GetAt(0).GetRawValue <float>(),
                    Y = bone.GetMember("head").GetAt(1).GetRawValue <float>(),
                    Z = bone.GetMember("head").GetAt(2).GetRawValue <float>(),
                };
                offset = BlenderUtil.ChangeCoordsSystem(offset);

                var elements = new float[16];
                for (int i = 0; i < 4; ++i)
                {
                    for (int j = 0; j < 4; ++j)
                    {
                        elements[i * 4 + j] = bone.GetMember("arm_mat").GetAt(i, j).GetRawValue <float>();
                    }
                }
                var modelTrans = new Matrix(elements);
                modelTrans = BlenderUtil.ChangeCoordsSystem(modelTrans);

                var result = new DrawSystem.BoneData()
                {
                    Name          = name,
                    Parent        = parentBoneIndex,
                    BoneTransform = modelTrans,                    // convert local bone transformation after
                    BoneOffset    = Matrix.Invert(modelTrans),
                    Length        = length,
                };

                outList.Add(result);
                parentBoneIndex = outList.Count() - 1;
                //Console.WriteLine("    found bone : " + name);

                // call for children
                var childBone = bone.GetMember("childbase").GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();
                while (childBone != null)
                {
                    _LoadBone(repository, childBone, parentBoneIndex, ref outList);
                    childBone = childBone.GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
                }
            }
        }
Example #6
0
        private bool _LoadArmature(BlendTypeRepository repository, BlendValueCapsule meshObj, out DrawSystem.BoneData[] outBoneArray, out int[] outDeformGroupIndex2BoneIndex, out AnimType.AnimationData outAnimData)
        {
            BlendValueCapsule bArmature = null;
            BlendValueCapsule bAnimData = null;

            // find blend value
            {
                var mod = meshObj.GetMember("modifiers").GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();
                while (mod != null)
                {
                    if (mod.Type.Equals(repository.Find("ArmatureModifierData")))
                    {
                        // animation modifier
                        var armatureObj = mod.GetMember("object").GetRawValue <BlendAddress>().DereferenceOne();
                        if (armatureObj != null)
                        {
                            bArmature = armatureObj.GetMember("data").GetRawValue <BlendAddress>().DereferenceOne();
                            bAnimData = armatureObj.GetMember("adt").GetRawValue <BlendAddress>().DereferenceOne();
                            break;
                        }
                    }

                    mod = mod.GetMember("modifier").GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
                }
            }

            // build boneList from armature
            var boneList = new List <DrawSystem.BoneData>();

            if (bArmature != null)
            {
                var firstBone   = bArmature.GetMember("bonebase").GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();
                var tmpBoneList = new List <DrawSystem.BoneData>();
                _LoadBone(repository, firstBone, -1, ref tmpBoneList);

                // compute local bone matrix
                for (int boneIndex = 0; boneIndex < tmpBoneList.Count(); ++boneIndex)
                {
                    var tmp = tmpBoneList[boneIndex];
                    if (!tmp.IsMasterBone())
                    {
                        tmp.BoneTransform = tmp.BoneTransform * Matrix.Invert(tmpBoneList[tmp.Parent].BoneTransform);
                    }
                    boneList.Add(tmp);
                }
            }

            // build anime data from animAction
            var animActionList = new List <AnimType.ActionData>();

            if (bAnimData != null)
            {
                var bAnimAction = bAnimData.GetMember("action").GetRawValue <BlendAddress>().DereferenceOne();

                // seek the top of action
                while (bAnimAction != null)
                {
                    var bTmpAnimAction = bAnimAction.GetMember("id").GetMember("prev").GetRawValue <BlendAddress>().DereferenceOne();
                    if (bTmpAnimAction == null)
                    {
                        break;
                    }
                    bAnimAction = bTmpAnimAction;
                }

                // load action
                while (bAnimAction != null)
                {
                    _LoadAction(repository, bAnimAction, ref animActionList);
                    bAnimAction = bAnimAction.GetMember("id").GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
                }
            }

            var deformGroupNameList = new List <string>();
            var deformGroup         = meshObj.GetMember("defbase").GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();

            while (deformGroup != null)
            {
                var groupName = deformGroup.GetMember("name").GetAllValueAsString();
                //Console.WriteLine("    found deform group : " + groupName);
                deformGroupNameList.Add(groupName);
                deformGroup = deformGroup.GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
            }

            if (deformGroupNameList.Count != 0 && boneList.Count != 0)
            {
                // sort boneArray by deform group
                outDeformGroupIndex2BoneIndex = new int[boneList.Count()];
                int nextIndex = 0;
                foreach (var defName in deformGroupNameList)
                {
                    var bone = boneList.Select((n, index) => new { n, index }).FirstOrDefault(ni => ni.n.Name == defName);
                    if (bone != null)
                    {
                        outDeformGroupIndex2BoneIndex[nextIndex] = bone.index;
                    }
                    nextIndex++;
                }
                outBoneArray = boneList.ToArray();
                if (animActionList.Count == 0)
                {
                    outAnimData = new AnimType.AnimationData();
                }
                else
                {
                    outAnimData         = new AnimType.AnimationData();
                    outAnimData.Actions = animActionList.ToArray();
                }
                return(true);
            }
            else
            {
                outDeformGroupIndex2BoneIndex = new int[0];
                outBoneArray = new DrawSystem.BoneData[0];
                outAnimData  = new AnimType.AnimationData();
                return(false);
            }
        }
Example #7
0
        private void _LoadAction(BlendTypeRepository repository, BlendValueCapsule bAnimAction, ref List <AnimType.ActionData> animActionList)
        {
            var groupList = new List <AnimType.ActionGroupData>();
            var bGroup    = bAnimAction.GetMember("groups").GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();

            while (bGroup != null)
            {
                var groupData = new AnimType.ActionGroupData();
                groupData.BoneName = bGroup.GetMember("name").GetAllValueAsString();
                groupData.Location = AnimType.ChannelData <Vector3> .Empty();

                groupData.Rotation = AnimType.ChannelData <Quaternion> .Empty();

                groupData.Scale = AnimType.ChannelData <Vector3> .Empty();

                //Console.WriteLine("    found anim action group : " + groupData.BoneName);

                var channelList = new List <AnimType.ChannelData <float> >();
                var bChannel    = bGroup.GetMember("channels").GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();
                while (bChannel != null)
                {
                    string boneName     = "";
                    string propertyName = "";
                    var    bRnaPath     = bChannel.GetMember("rna_path").GetRawValue <BlendAddress>().DereferenceAll(Blender.BlendPrimitiveType.Char());
                    string rnaPath      = Blender.ConvertUtil.CharArray2String(bRnaPath.Select(c => (object)c.GetRawValue <char>()));
                    if (!BlenderUtil.ParseRnaPath(rnaPath, ref boneName, ref propertyName))
                    {
                        Debug.Fail("Failed to parse rna path(" + rnaPath + ")");
                        return;
                    }
                    int arrayIndex = bChannel.GetMember("array_index").GetRawValue <int>();

                    if (boneName == groupData.BoneName)
                    {
                        //Console.WriteLine(String.Format("        {0}.{1}[{2}]", boneName, propertyName, arrayIndex));

                        var bBeztList = bChannel.GetMember("bezt").GetRawValue <BlendAddress>().DereferenceAll();
                        var channel   = new AnimType.ChannelData <float>();
                        channel.KeyFrames = new AnimType.KeyData <float> [bBeztList.Count()];

                        foreach (var bBezt in bBeztList.Select((value, index) => new { value, index }))
                        {
                            float frame = bBezt.value.GetMember("vec").GetAt(1, 0).GetRawValue <float>();
                            float value = bBezt.value.GetMember("vec").GetAt(1, 1).GetRawValue <float>();

                            channel.KeyFrames[bBezt.index] = new AnimType.KeyData <float>((int)frame, value);
                        }

                        channelList.Add(channel);
                    }

                    bChannel = bChannel.GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
                }                       // while

                if (channelList.Count() == 10)
                {
                    // channel type convertion
                    // location : floatx3 to Vector3
                    // rotation : floatx4 to Quatanion
                    // scale : floatx3 to Vector3
                    groupData.Location.KeyFrames
                        = channelList[0].KeyFrames
                          .Select((key, index) => new AnimType.KeyData <Vector3>(key.Frame, new Vector3(key.Value, channelList[1].KeyFrames[index].Value, channelList[2].KeyFrames[index].Value)))
                          .Select(key => { key.Value = BlenderUtil.ChangeCoordsSystem(key.Value); return(key); })
                          //.Select(key => { key.Frame--; return key; })	// blender frame index starts from 1
                          .ToArray();
                    groupData.Rotation.KeyFrames
                        = channelList[3].KeyFrames
                          .Select((key, index) => new AnimType.KeyData <Quaternion>(key.Frame, new Quaternion(channelList[4].KeyFrames[index].Value, channelList[5].KeyFrames[index].Value, channelList[6].KeyFrames[index].Value, key.Value)))
                          .Select(key => { key.Value = BlenderUtil.ChangeCoordsSystem(key.Value); return(key); })
                          //.Select(key => { key.Frame--; return key; })	// blender frame index starts from 1
                          .ToArray();
                    groupData.Scale.KeyFrames
                        = channelList[7].KeyFrames
                          .Select((key, index) => new AnimType.KeyData <Vector3>(key.Frame, new Vector3(key.Value, channelList[8].KeyFrames[index].Value, channelList[9].KeyFrames[index].Value)))
                          .Select(key => { key.Value = BlenderUtil.ChangeCoordsSystem(key.Value); return(key); })
                          //.Select(key => { key.Frame--; return key; })	// blender frame index starts from 1
                          .ToArray();
                    groupList.Add(groupData);

                    bGroup = bGroup.GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
                }
                else
                {
                    Debug.Fail("unexpected the number of channels.");
                    return;
                }
            }

            if (groupList.Count != 0)
            {
                var actionData = new AnimType.ActionData();
                var actionName = bAnimAction.GetMember("id").GetMember("name").GetAllValueAsString();
                actionName = actionName.Substring(2, actionName.Length - 2);                //  ACArmatureAction => ArmatureAction

                actionData.Name   = actionName;
                actionData.Groups = groupList.ToArray();
                animActionList.Add(actionData);
            }
        }
Example #8
0
        private bool _LoadScene(BlendTypeRepository repository, List <BlockHeaderEntity> entityList)
        {
            // find root
            BlendValueCapsule root = entityList.Where(e => e.Name == "GLOB").Select(e => e.Children[0].Value).First();

            if (root == null)
            {
                return(false);
            }

            var scene    = root.GetMember("curscene").GetRawValue <BlendAddress>().DereferenceOne();
            var listBase = scene.GetMember("base");
            var nextBase = listBase.GetMember("first").GetRawValue <BlendAddress>().DereferenceOne();

            // load mesh
            while (nextBase != null)
            {
                var obj = nextBase.GetMember("object").GetRawValue <BlendAddress>().DereferenceOne();
                if (obj != null)
                {
                    string name         = obj.GetMember("id").GetMember("name").GetAllValueAsString();
                    int    restrictFlag = obj.GetMember("restrictflag").GetRawValue <char>();
                    if ((restrictFlag & 1) != 0)
                    {
                        // invisible object
                    }

                    var data = obj.GetMember("data").GetRawValue <BlendAddress>().DereferenceOne();
                    if (data != null && data.Type.Name == "Mesh")
                    {
                        // mesh object
                        Console.WriteLine("found mesh : " + name);
                        if (!_LoadMesh(repository, obj))
                        {
                            return(false);
                        }
                    }

                    var groupId = obj.GetMember("dup_group").GetRawValue <BlendAddress>().DereferenceOne();
                    if (groupId != null)
                    {
                        // link object
                        Console.WriteLine("found link obj: " + name);

                        var lib  = groupId.GetMember("lib").GetRawValue <BlendAddress>().DereferenceOne();
                        var path = lib.GetMember("filepath").GetAllValueAsString();

                        // make layout matrix
                        var elements = new float[16];
                        for (int i = 0; i < 4; ++i)
                        {
                            for (int j = 0; j < 4; ++j)
                            {
                                elements[i * 4 + j] = obj.GetMember("obmat").GetAt(i, j).GetRawValue <float>();
                            }
                        }
                        var layoutTrans = new Matrix(elements);
                        layoutTrans = BlenderUtil.ChangeCoordsSystem(layoutTrans);

                        var node = new LinkNode()
                        {
                            Name           = name,
                            Layout         = layoutTrans,
                            TargetFileName = Path.GetFileName(path),
                        };
                        m_linkList.Add(node);
                    }
                }

                nextBase = nextBase.GetMember("next").GetRawValue <BlendAddress>().DereferenceOne();
            }

            return(true);
        }
Example #9
0
        private VertexOriginal[] _CreateOriginalVertices(BlendTypeRepository repository, BlendValueCapsule mesh, int[] deformGroupIndex2BoneIndex)
        {
            var mpolyList   = mesh.GetMember("mpoly").GetRawValue <BlendAddress>().DereferenceAll();
            var mloopList   = mesh.GetMember("mloop").GetRawValue <BlendAddress>().DereferenceAll();
            var mloopuvList = mesh.GetMember("mloopuv").GetRawValue <BlendAddress>().DereferenceAll();
            var mvertList   = mesh.GetMember("mvert").GetRawValue <BlendAddress>().DereferenceAll();
            var dvertList   = mesh.GetMember("dvert").GetRawValue <BlendAddress>().DereferenceAll();

            int capacity = mpolyList.Count() * 6;            // assume that all polygons is square.
            var vertices = new List <VertexOriginal>(capacity);

            foreach (var mpoly in mpolyList)
            {
                int   offset        = mpoly.GetMember("loopstart").GetRawValue <int>();
                int   count         = mpoly.GetMember("totloop").GetRawValue <int>();
                short materialIndex = mpoly.GetMember("mat_nr").GetRawValue <short>();
                Debug.Assert(count >= 0, "negative totloop is here!");                // todo: ref previous loop

                int[] plan = null;
                switch (count)
                {
                case 3:
                    plan = new int[] { offset + 2, offset + 1, offset + 0 };
                    break;

                case 4:
                    // triangulation
                    plan = new int[] { offset + 2, offset + 1, offset, offset + 3, offset + 2, offset };
                    break;

                default:
                    Debug.Fail("tutloop must be 3 or 4");                            // todo: ref previous loop
                    break;
                }

                if (plan == null)
                {
                    continue;
                }

                foreach (int i in plan)
                {
                    int vIndex   = mloopList[i].GetMember("v").GetRawValue <int>();
                    var position = mvertList[vIndex].GetMember("co");
                    var normal   = mvertList[vIndex].GetMember("no");

                    VertexOriginal vertex;
                    vertex.Position.X = position.GetAt(0).GetRawValue <float>();
                    vertex.Position.Y = position.GetAt(1).GetRawValue <float>();
                    vertex.Position.Z = position.GetAt(2).GetRawValue <float>();
                    vertex.Position.W = 1;
                    vertex.Position   = BlenderUtil.ChangeCoordsSystem(vertex.Position);

                    vertex.Normal.X = normal.GetAt(0).GetRawValue <short>();
                    vertex.Normal.Y = normal.GetAt(1).GetRawValue <short>();
                    vertex.Normal.Z = normal.GetAt(2).GetRawValue <short>();
                    vertex.Normal   = BlenderUtil.ChangeCoordsSystem(vertex.Normal);
                    vertex.Normal.Normalize();

                    var uv = mloopuvList[i].GetMember("uv");
                    vertex.Texcoord.X = uv.GetAt(0).GetRawValue <float>();
                    vertex.Texcoord.Y = 1 - uv.GetAt(1).GetRawValue <float>();

                    vertex.Tangent  = Vector3.Zero;
                    vertex.Binormal = Vector3.Zero;

                    vertex.MaterialIndex = materialIndex;

                    var weights = dvertList == null
                                                ? null
                                                : dvertList[vIndex].GetMember("dw").GetRawValue <BlendAddress>().DereferenceAll();
                    if (weights == null || deformGroupIndex2BoneIndex == null)
                    {
                        vertex.BoneWeights = null;
                        vertex.BoneIndices = null;
                    }
                    else
                    {
                        // load bone weights
                        // bone weight can be stored 0, so we ignore this case.
                        //int maxWeightCount = dvertList[vIndex].GetMember("totweight").GetRawValue<int>();
                        var noneZeroWeightList =
                            weights.Select(w => new Tuple <float, int>(w.GetMember("weight").GetRawValue <float>(), w.GetMember("def_nr").GetRawValue <int>()))
                            .OrderByDescending(tuple => tuple.Item1)    // sort by descending
                            .Where(tuple => tuple.Item1 > 0.0f);        // ignore zero value too
                        int weightCount = noneZeroWeightList.Count();

                        vertex.BoneWeights = new float[weightCount];
                        vertex.BoneIndices = new uint[weightCount];

                        int wIndex = 0;
                        foreach (var tuple in noneZeroWeightList)
                        {
                            float weight           = tuple.Item1;
                            int   deformGroupIndex = tuple.Item2;
                            vertex.BoneWeights[wIndex] = weight;

                            // def_nr is NOT index of bones, but index of deform group
                            // we must replace to index of bones using bone-deform mapping.
                            vertex.BoneIndices[wIndex] = (uint)deformGroupIndex2BoneIndex[deformGroupIndex];

                            wIndex++;
                        }
                    }

                    vertices.Add(vertex);
                }
            }

            // compute tangent and binormal
            int polyCount = vertices.Count / 3;

            for (int polyIndex = 0; polyIndex < polyCount; ++polyIndex)
            {
                var posArray = new Vector4[]
                {
                    vertices[polyIndex * 3].Position,
                    vertices[polyIndex * 3 + 1].Position,
                    vertices[polyIndex * 3 + 2].Position,
                };

                var normalArray = new Vector3[]
                {
                    vertices[polyIndex * 3].Normal,
                    vertices[polyIndex * 3 + 1].Normal,
                    vertices[polyIndex * 3 + 2].Normal,
                };

                var uvArray = new Vector2[]
                {
                    vertices[polyIndex * 3].Texcoord,
                    vertices[polyIndex * 3 + 1].Texcoord,
                    vertices[polyIndex * 3 + 2].Texcoord,
                };

                var faceTangent = MathUtil.ComputeFaceTangent(posArray[0], posArray[1], posArray[2], uvArray[0], uvArray[1], uvArray[2]);

                for (int vIndex = 0; vIndex < 3; ++vIndex)
                {
                    // calc tangent
                    var normal  = normalArray[vIndex];
                    var tangent = faceTangent;
                    MathUtil.Orthonormalize(ref normal, ref tangent);

                    // calc binormal
                    var binormal = Vector3.Cross(normalArray[vIndex], tangent);
                    //binormal.Normalize();

                    var oldVertex = vertices[polyIndex * 3 + vIndex];
                    oldVertex.Tangent  = tangent;
                    oldVertex.Binormal = binormal;
                    vertices[polyIndex * 3 + vIndex] = oldVertex;
                }
            }

            return(vertices.ToArray());
        }