private aiMatrix4x4 InterpolateRotation(double time, NodeAnimationChannel channel)
        {
            aiQuaternion rotation;

            if (channel.RotationKeyCount == 1)
            {
                rotation = channel.RotationKeys[0].Value;
            }
            else
            {
                uint frameIndex = 0;
                for (uint i = 0; i < channel.RotationKeyCount - 1; i++)
                {
                    if (time < (float)channel.RotationKeys[(int)(i + 1)].Time)
                    {
                        frameIndex = i;
                        break;
                    }
                }

                QuaternionKey currentFrame = channel.RotationKeys[(int)frameIndex];
                QuaternionKey nextFrame    = channel.RotationKeys[(int)((frameIndex + 1) % channel.RotationKeyCount)];

                double delta = (time - (float)currentFrame.Time) / (float)(nextFrame.Time - currentFrame.Time);

                aiQuaternion start = currentFrame.Value;
                aiQuaternion end   = nextFrame.Value;
                rotation = aiQuaternion.Slerp(start, end, (float)delta);
                rotation.Normalize();
            }

            return(rotation.GetMatrix());
        }
Пример #2
0
        Matrix4x4 FindRotation(NodeAnimationChannel n)
        {
            Matrix4x4 result = Matrix4x4.Identity;

            for (int c = 0; c < n.RotationKeyCount; c++)
            {
                QuaternionKey q = n.RotationKeys[c];

                q.Value = new Assimp.Quaternion(q.Value.W, q.Value.X, q.Value.Y, q.Value.Z);

                VectorKey pk = n.PositionKeys[c];
                VectorKey ps = n.ScalingKeys[c];

                if (_animationController.FrameCounter <= q.Time)
                {
                    result =
                        (Matrix4x4)q.Value.GetMatrix()
                        * (Matrix4x4.FromTranslation(pk.Value))
                        * (Matrix4x4.FromScaling(ps.Value))
                    ;
                    break;
                }
            }
            return(result);
        }
Пример #3
0
        static NumMatrix4x4 GetTransformForFrame(NodeAnimationChannel Ch, int Frame)
        {
            VectorKey PosKey = Ch.PositionKeys[Ch.PositionKeys.Count - 1];

            if (Frame < Ch.PositionKeys.Count)
            {
                PosKey = Ch.PositionKeys[Frame];
            }

            QuaternionKey RotKey = Ch.RotationKeys[Ch.RotationKeys.Count - 1];

            if (Frame < Ch.RotationKeys.Count)
            {
                RotKey = Ch.RotationKeys[Frame];
            }

            VectorKey SclKey = Ch.ScalingKeys[Ch.ScalingKeys.Count - 1];

            if (Frame < Ch.ScalingKeys.Count)
            {
                SclKey = Ch.ScalingKeys[Frame];
            }


            NumMatrix4x4 Rot = NumMatrix4x4.CreateFromQuaternion(ConvertQuat(RotKey.Value));
            NumMatrix4x4 Pos = NumMatrix4x4.CreateTranslation(ConvertVec(PosKey.Value));
            NumMatrix4x4 Scl = NumMatrix4x4.CreateScale(ConvertVec(SclKey.Value));

            //return Pos * Rot * Scl;
            return(Scl * Rot * Pos);
        }
Пример #4
0
        /// <summary>
        /// Converts a <see cref="QuaternionKey"/> to a <see cref="DomainQuaternionKeyFrame"/>.
        /// </summary>
        /// <param name="assimpQuaternionKey">The source <see cref="QuaternionKey"/>.</param>
        /// <param name="ticksPerSecond">The number of ticks per second for the animation.</param>
        /// <returns>The resulting <see cref="DomainQuaternionKeyFrame"/>.</returns>
        private static DomainQuaternionKeyFrame GetDomainQuaternionKeyFrame(QuaternionKey assimpQuaternionKey, double ticksPerSecond)
        {
            var assimpQuaternion = assimpQuaternionKey.Value;

            assimpQuaternion.Normalize();

            var quaternion = new NumericsQuaternion(assimpQuaternion.X, assimpQuaternion.Y, assimpQuaternion.Z, assimpQuaternion.W);

            return(new DomainQuaternionKeyFrame(assimpQuaternionKey.Time / ticksPerSecond, quaternion));
        }
Пример #5
0
        static void Main(string[] args)
        {
            string[] strs = Directory.GetFiles(Environment.CurrentDirectory, "*.dae", SearchOption.TopDirectoryOnly);
            foreach (string path in strs)
            {
                try
                {
                    using (AssimpContext ACont = new AssimpContext())
                    {
                        ACont.SetConfig(new NormalSmoothingAngleConfig(66f));
                        Scene scene = ACont.ImportFile(path, PostProcessSteps.Triangulate);
                        if (!scene.HasAnimations)
                        {
                            Console.WriteLine("Skipping " + path);
                            continue;
                        }
                        Animation anim = scene.Animations[0];
                        if (!anim.HasNodeAnimations)
                        {
                            Console.WriteLine("Invalid animation in " + path);
                            continue;
                        }
                        StringBuilder sb = new StringBuilder();
                        sb.Append("// mcmonkey's animation details file format v0.2\n");
                        sb.Append("general\n");
                        sb.Append("{\n");
                        sb.Append("\tlength: ").Append((anim.DurationInTicks / anim.TicksPerSecond).ToString()).Append(";\n");
                        sb.Append("}\n");
                        for (int i = 0; i < anim.NodeAnimationChannelCount; i++)
                        {
                            NodeAnimationChannel node = anim.NodeAnimationChannels[i];
                            sb.Append(node.NodeName).Append('\n');
                            sb.Append("{\n");
                            Node   found = LookForMatch(scene.RootNode, node.NodeName);
                            string pNode = "<none>";
                            if (found != null)
                            {
                                pNode = found.Parent.Name;
                            }
                            sb.Append("\tparent: " + pNode + ";\n");

                            /*Bone bfound = LookForBone(scene, node.NodeName);
                             * Matrix4x4 mat = Matrix4x4.Identity;
                             * if (bfound != null)
                             * {
                             *  mat = bfound.OffsetMatrix;
                             *  //bpos = bfound.OffsetMatrix.C1 + "=" + bfound.OffsetMatrix.C2 + "=" + bfound.OffsetMatrix.C3;
                             *  //bpos = bfound.OffsetMatrix.A4 + "=" + bfound.OffsetMatrix.B4 + "=" + bfound.OffsetMatrix.C4;
                             * }*/
                            /*sb.Append("\toffset: " + mat.A1 + "=" + mat.A2 + "=" + mat.A3 + "=" + mat.A4 + "=" +
                             *  mat.B1 + "=" + mat.B2 + "=" + mat.B3 + "=" + mat.B4 + "=" +
                             *  mat.C1 + "=" + mat.C2 + "=" + mat.C3 + "=" + mat.C4 + "=" +
                             *  mat.D1 + "=" + mat.D2 + "=" + mat.D3 + "=" + mat.D4 +";\n");*/
                            if (node.HasPositionKeys)
                            {
                                sb.Append("\tpositions: ");
                                for (int x = 0; x < node.PositionKeyCount; x++)
                                {
                                    VectorKey key = node.PositionKeys[x];
                                    sb.Append(key.Time.ToString() + "=" + key.Value.X.ToString() + "=" + key.Value.Y.ToString() + "=" + key.Value.Z.ToString() + " ");
                                }
                                sb.Append(";\n");
                            }
                            if (node.HasRotationKeys)
                            {
                                sb.Append("\trotations: ");
                                for (int x = 0; x < node.RotationKeyCount; x++)
                                {
                                    QuaternionKey key = node.RotationKeys[x];
                                    sb.Append(key.Time.ToString() + "=" + key.Value.X.ToString() + "=" + key.Value.Y.ToString() + "=" + key.Value.Z.ToString() + "=" + key.Value.W + " ");
                                }
                                sb.Append(";\n");
                            }
                            // TODO: Should scaling matter? Probably not.
                            sb.Append("}\n");
                        }
                        File.WriteAllText(path + ".anim", sb.ToString());
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Processing " + path + ": " + ex.ToString());
                }
            }
        }
Пример #6
0
    bool ListQuaternionKey(ref List <QuaternionKey> list, int totalFrames)
    {
        bool hasChanges = false;

        for (int i = 0; i < list.Count; i++)
        {
            if (list[i] != list[GetNextIndex(list, i)])
            {
                hasChanges = true;
                break;
            }
        }

        if (!hasChanges)
        {
            return(false);
        }

        List <QuaternionKey> interpolated = new List <QuaternionKey>(totalFrames);

        for (int i = 0; i < totalFrames; i++)
        {
            interpolated.Add(new QuaternionKey());
        }

        QuaternionKey first = list[0];
        QuaternionKey from;
        QuaternionKey to;

        for (int i = 0; i < list.Count; i++)
        {
            from = list[i];
            to   = list[GetNextIndex(list, i)];

            if (Mathf.Abs(((int)to.Time - (int)from.Time)) == 1)
            {
                continue;
            }

            bool aNan = (float.IsNaN(from.Value.X) || float.IsNaN(from.Value.Y) || float.IsNaN(from.Value.Z) || float.IsNaN(from.Value.W));
            bool bNan = (float.IsNaN(to.Value.X) || float.IsNaN(to.Value.Y) || float.IsNaN(to.Value.Z) || float.IsNaN(to.Value.W));

            UnityEngine.Quaternion a = aNan ? UnityEngine.Quaternion.identity : new UnityEngine.Quaternion(from.Value.X, from.Value.Y, from.Value.Z, from.Value.W);
            UnityEngine.Quaternion b = bNan ? UnityEngine.Quaternion.identity : new UnityEngine.Quaternion(to.Value.X, to.Value.Y, to.Value.Z, to.Value.W);

            int   stepCount = Mathf.Abs((int)to.Time - (int)from.Time);
            float step      = 1f / stepCount;
            if (to.Time < from.Time)
            {
                step = 1f / (float)(totalFrames - (int)from.Time + (int)to.Time);
            }
            float t = 0f;

            for (int j = (int)from.Time; j != (int)to.Time; j = GetNextIndex(interpolated, j))
            {
                QuaternionKey ik = interpolated[j];
                ik.Time = j;
                UnityEngine.Quaternion ab = UnityEngine.Quaternion.Lerp(a, b, t);
                ik.Value = new Assimp.Quaternion(ab.w, ab.x, ab.y, ab.z);
                //print(ik.Value);
                interpolated[j] = ik;

                t += step;

                if (stepCount-- < 0)
                {
                    break;
                }
            }

            if (to == first)
            {
                break;
            }
        }

        list = interpolated;

        return(true);
    }
Пример #7
0
        /**
         * Kinect Joint Conversion to NodeAnim
         */
        private void KinectJointToNode(Scene mainScene, Joint joint, NodeAnimationChannel matchedNode, double time)
        {
            VectorKey keyframePos = new VectorKey();
            keyframePos.Value.X = joint.Position.X;
            keyframePos.Value.Y = joint.Position.Y;
            keyframePos.Value.Z = joint.Position.Z;
            keyframePos.Time = time;

            QuaternionKey keyframeRot = new QuaternionKey();
            if (joint.JointType == JointType.HipCenter)
            {
                keyframeRot.Value = new Quaternion(0, (float)3, (float)1.3);
            }
            else
            {
                keyframeRot.Value.X = 0;
                keyframeRot.Value.Y = 0;
                keyframeRot.Value.Z = 0;
            }
            keyframeRot.Time = time;

            //We set last_assimp_joint node that we will reuse for calculate localPosition/Rotation the next step
            string prevNodeName = mainScene.RootNode.FindNode(matchedNode.NodeName).Parent.Name;

            VectorKey prevKeyframePos = new VectorKey();
            bool find = false;
            /*
             * TODO: We could refactor that code to avoid to go through all joint each time
             * we need to look for a specific joint
             */
            foreach (Joint j in currentJoinCol)
            {
                if (prevNodeName == "")
                    find = true;
                if (j.JointType.ToString() == prevNodeName)
                {
                    prevKeyframePos.Value.X = j.Position.X;
                    prevKeyframePos.Value.Y = j.Position.Y;
                    prevKeyframePos.Value.Z = j.Position.Z;
                    find = true;
                    break;
                }
            }

            if (find == false)
                throw new Exception();
            if (prevNodeName != "")
                keyframePos.Value -= prevKeyframePos.Value;

            keyframePos.Value.X *= 17;
            keyframePos.Value.Y *= 17;
            keyframePos.Value.Z *= 17;

            matchedNode.PositionKeys.Add(keyframePos);
            matchedNode.RotationKeys.Add(keyframeRot);
        }
Пример #8
0
        private Tuple<VectorKey, QuaternionKey> VectorToNode(Vector posVect, double time)
        {
            VectorKey keyframePos = new VectorKey();
            keyframePos.Value.X = posVect.x / HAND_SCALE_RATIO;
            keyframePos.Value.Y = posVect.y / HAND_SCALE_RATIO;
            keyframePos.Value.Z = posVect.z / HAND_SCALE_RATIO;
            keyframePos.Time = time;

            QuaternionKey keyframeRot = new QuaternionKey();
            keyframeRot.Value.X = 0;
            keyframeRot.Value.Y = 0;
            keyframeRot.Value.Z = 0;
            keyframeRot.Time = time;

            return new Tuple<VectorKey, QuaternionKey>(keyframePos, keyframeRot);
        }
Пример #9
0
        public static SkeletalAnimation[] LoadAnimations(string fileName, Vector3 multiplier)
        {
            AssimpContext importer = new AssimpContext();
            Scene         scene    = importer.ImportFile(fileName);

            List <SkeletalAnimation> skeletalAnimations = new List <SkeletalAnimation>();

            if (scene.HasAnimations)
            {
                foreach (Animation animation in scene.Animations)
                {
                    if (animation.HasNodeAnimations)
                    {
                        SkeletalAnimation skeletalAnimation = new SkeletalAnimation(animation.Name, (float)animation.TicksPerSecond);
                        foreach (NodeAnimationChannel channel in animation.NodeAnimationChannels)
                        {
                            Vector3           lastPosition = Vector3.Zero;
                            OpenTK.Quaternion lastRotation = OpenTK.Quaternion.Identity;
                            Vector3           lastScale    = Vector3.One;
                            string            nodeName     = SanitizeBoneName(channel.NodeName);
                            if (channel.NodeName.Contains("_$AssimpFbx$_Translation"))
                            {
                                foreach (VectorKey key in channel.PositionKeys)
                                {
                                    lastPosition = new Vector3(key.Value.X, key.Value.Y, key.Value.Z) * multiplier;
                                    skeletalAnimation.AddKeyFrame(nodeName, (float)key.Time, lastPosition, lastRotation, lastScale);
                                }
                            }
                            else if (channel.NodeName.Contains("_$AssimpFbx$_Rotation"))
                            {
                                foreach (QuaternionKey key in channel.RotationKeys)
                                {
                                    lastRotation = new OpenTK.Quaternion(key.Value.X, key.Value.Y, key.Value.Z, key.Value.W);
                                    skeletalAnimation.AddKeyFrame(nodeName, (float)key.Time, lastPosition, lastRotation, lastScale);
                                }
                            }
                            else if (channel.NodeName.Contains("_$AssimpFbx$_Scaling"))
                            {
                                foreach (VectorKey key in channel.ScalingKeys)
                                {
                                    lastScale = new Vector3(key.Value.X, key.Value.Y, key.Value.Z);
                                    skeletalAnimation.AddKeyFrame(nodeName, (float)key.Time, lastPosition, lastRotation, lastScale);
                                }
                            }
                            else
                            {
                                for (int i = 0; i < channel.PositionKeyCount; i++)
                                {
                                    VectorKey     keyT = channel.PositionKeys[i];
                                    QuaternionKey keyR = channel.RotationKeys[i];
                                    VectorKey     keyS = channel.ScalingKeys[i];

                                    lastPosition = new Vector3(keyT.Value.X, keyT.Value.Y, keyT.Value.Z) * multiplier;

                                    lastRotation = new OpenTK.Quaternion(keyR.Value.X, keyR.Value.Y, keyR.Value.Z, keyR.Value.W);

                                    lastScale = new Vector3(keyS.Value.X, keyS.Value.Y, keyS.Value.Z);

                                    skeletalAnimation.AddKeyFrame(nodeName, (float)keyT.Time, lastPosition, lastRotation, lastScale);
                                }
                            }
                        }
                        skeletalAnimations.Add(skeletalAnimation);
                    }
                }
            }

            return(skeletalAnimations.ToArray());
        }
Пример #10
0
    private Tuple<VectorKey, QuaternionKey> BoneToNode(Bone bone)
    {
        VectorKey keyframePos = new VectorKey();
        keyframePos.Value.X = joint.Position.X;
        keyframePos.Value.Y = joint.Position.Y;
        keyframePos.Value.Z = joint.Position.Z;
        keyframePos.Time = this.current_frametime;

        QuaternionKey keyframeRot = new QuaternionKey();
        keyframeRot.Value.X = 0;
        keyframeRot.Value.Y = 0;
        keyframeRot.Value.Z = 0;
        keyframeRot.Time = this.current_frametime;

        return new Tuple<VectorKey, QuaternionKey>(keyframePos, keyframeRot);
    }
Пример #11
0
        public bool Load(String fileName)
        {
            // @TODO - change the file path for asset folder in the future currently, set as executable path
            String path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Assets\\");

            m_FileName = path + fileName;

            // setting assimp context
            AssimpContext importer = new AssimpContext();

            importer.SetConfig(new NormalSmoothingAngleConfig(66.0f));

            // loading scene
            Scene scene = null;

            try
            {
                scene = importer.ImportFile(m_FileName, PostProcessPreset.TargetRealTimeMaximumQuality | PostProcessSteps.Triangulate | PostProcessSteps.FlipUVs | PostProcessSteps.FlipWindingOrder);
            }
            catch (Exception e) // handling assimp exception
            {
                if (e.Source != null)
                {
                    Console.WriteLine("Error: {0}", e.Message);
                }
            }

            asset = new H1AssetContext();
            H1ModelContext convertedModel = asset.AddModel();

            // extract the meshes
            if (scene.HasMeshes)
            {
                Int32 meshCount = scene.Meshes.Count;
                List <H1MeshContext> meshContexts = new List <H1MeshContext>();

                ExtractMeshData(scene, ref meshContexts);

                foreach (H1MeshContext meshContext in meshContexts)
                {
                    convertedModel.Meshes.Add(meshContext);
                }
            }

            // extract skeletal data

            // what I learned from this failed algorithm
            // 1. root bone nodes can be multiple
            // 2. for example, blabla_Hub(containing null meshes, only space infos) have null information for weighted vertices, but it should not be considered as leaf node!
            #region Disabled Extracting skeletal mesh data methods

            /*if (scene.HasMeshes)
             * {
             *  // collect all bones from meshes
             *  List<Bone> bones = new List<Bone>();
             *  foreach (Mesh mesh in scene.Meshes)
             *  {
             *      if (mesh.HasBones)
             *      {
             *          bones.AddRange(mesh.Bones);
             *      }
             *  }
             *
             *  // find root node for all meshes
             *  Node boneRootNode = FindRootBone(bones, scene.RootNode);
             *
             *  H1SkeletalContext skeletalContext = null;
             *  skeletalContext = new H1SkeletalContext();
             *  //@TODO - need to optimize for deep copies
             *  ExtractSkeletalMeshData(boneRootNode, bones, ref skeletalContext);
             *
             *  convertedModel.SkeletalContexts.Add(skeletalContext);
             * }*/
            #endregion

            // what I learned from this failed algorithm
            // 1. the counts of bones and bone nodes (joint nodes) can be different
            // 2. bone node can be used for just transforming spaces
            // 3. bone node could have empty meshes (just includes space information)
            // 4. bones are subsidiary for bone nodes
            // 5. don't consider aiBone as bone node in hierarchy rather regards node(aiNode) as bone node in hierarchy
            // 6. consider aiBone as node bone data containing all necessary data
            #region Failed Algorithm
            // new algorithms to handle multiple root node (like pelvis and head) more efficient way

            /*if (scene.HasMeshes)
             * {
             *  // 1. extracted all bones for all meshes in the scene
             *  // NOTICE - multiple meshes in file are considered in same body but chunked several parts
             *  Dictionary<String, Bone> extractedBones = new Dictionary<String, Bone>();
             *  Dictionary<String, H1SkeletalContext.JointNode> jointNodes = new Dictionary<string, H1SkeletalContext.JointNode>();
             *  foreach (Mesh mesh in scene.Meshes)
             *  {
             *      if (mesh.HasBones)
             *      {
             *          foreach (Bone bone in mesh.Bones)
             *          {
             *              String boneName = bone.Name;
             *              if (!extractedBones.ContainsKey(boneName))
             *              {
             *                  extractedBones.Add(boneName, bone);
             *
             *                  H1SkeletalContext.JointNode newJointNode = new H1SkeletalContext.JointNode();
             *                  newJointNode.JointData.JointName = boneName;
             *                  jointNodes.Add(boneName, newJointNode);
             *              }
             *          }
             *      }
             *  }
             *
             *  // 2. process extracted bones
             *  // @TODO - naive algorithm need to optimize by searching tree and set the bones parent
             *  foreach (KeyValuePair<String, Bone> extractedBone in extractedBones)
             *  {
             *      Node node = scene.RootNode.FindNode(extractedBone.Key);
             *      H1SkeletalContext.JointNode jointNode = jointNodes[node.Name];
             *      H1SkeletalContext.JointNode parentJointNode = jointNodes[node.Parent.Name];
             *
             *      // set proper properties
             *      jointNode.Parent = parentJointNode;
             *      foreach (Node child in node.Children)
             *      {
             *          H1SkeletalContext.JointNode childJointNode = null;
             *          if (jointNodes.TryGetValue(child.Name, out childJointNode))
             *          {
             *              jointNode.Children.Add(childJointNode);
             *          }
             *      }
             *      ExtractBone(extractedBone.Value, ref jointNode.JointData);
             *  }
             *
             *  // 3. find root node(s) and set them at the front
             * }*/
            #endregion

            if (scene.HasMeshes)
            {
                H1SkeletalContext skeletalContext = null;
                skeletalContext = new H1SkeletalContext();

                // prepare data for BFS search
                List <H1SkeletalContext.JointNode> jointNodes = skeletalContext.JointNodes;
                Dictionary <String, Int32>         jointNameToJointNodeIndex = new Dictionary <String, Int32>();

                // extractedBones could have same bone name and multiple bone data
                Dictionary <String, List <Bone> > extractedBones = new Dictionary <String, List <Bone> >();
                // boneToMeshContexIndex should be mirrored same as extractedBones (index of list should be same)
                Dictionary <String, List <Int32> > boneToMeshContextIndex = new Dictionary <String, List <Int32> >();

                #region Debug Validation Weight Vertex Counts
#if DEBUG
                List <Boolean> taggedVertexIndex       = new List <Boolean>(); // verification to tag all needed vertices
                List <float>   taggedVertexWeights     = new List <float>();   // verification to tag all vertex weight for sum of each vertex
                List <Int32>   taggedVertexIndexOffset = new List <Int32>();

                // pre-test for validation
                foreach (Mesh mesh in scene.Meshes)
                {
                    if (mesh.HasVertices)
                    {
                        foreach (Vector3D vertex in mesh.Vertices)
                        {
                            taggedVertexIndex.Add(false);
                        }
                    }

                    if (mesh.HasBones)
                    {
                        foreach (Bone bone in mesh.Bones)
                        {
                            if (bone.HasVertexWeights)
                            {
                                foreach (VertexWeight vertexWeight in bone.VertexWeights)
                                {
                                    taggedVertexIndex[vertexWeight.VertexID] = true;
                                }
                            }
                        }
                    }

                    List <Int32> notTaggedVertexIndex0 = new List <Int32>();
                    Int32        currVertexIndex0      = 0;
                    foreach (Boolean isTagged in taggedVertexIndex)
                    {
                        if (isTagged == false)
                        {
                            notTaggedVertexIndex0.Add(currVertexIndex0);
                        }
                        currVertexIndex0++;
                    }

                    if (notTaggedVertexIndex0.Count > 0)
                    {
                        return(false);
                    }

                    taggedVertexIndex.Clear();
                }
#endif
                #endregion

                Int32 validMeshContextIndex = 0;
                foreach (Mesh mesh in scene.Meshes)
                {
                    if (mesh.HasVertices &&
                        mesh.HasNormals &&
                        mesh.HasTangentBasis &&
                        mesh.TextureCoordinateChannels[0].Count > 0 &&
                        mesh.HasFaces) // process vertices in the mesh
                    {
                        if (mesh.HasBones)
                        {
                            foreach (Bone bone in mesh.Bones)
                            {
                                String boneName = bone.Name;
                                if (!extractedBones.ContainsKey(boneName))
                                {
                                    // add new list of bone list
                                    extractedBones.Add(boneName, new List <Bone>());
                                    extractedBones[boneName].Add(bone);
                                    // add new list of mesh context index
                                    boneToMeshContextIndex.Add(boneName, new List <Int32>());
                                    boneToMeshContextIndex[boneName].Add(validMeshContextIndex);
                                }
                                else // same bone name, but different bone data exists
                                {
                                    // no need to create list of data, just add new item
                                    // bone data could have different set of weighted vertices, but with same bone name
                                    extractedBones[boneName].Add(bone);
                                    boneToMeshContextIndex[boneName].Add(validMeshContextIndex);
                                }
                            }

                            validMeshContextIndex++;

                            #region Debug Validation Weight Vertex Count
#if DEBUG
                            taggedVertexIndexOffset.Add(taggedVertexIndex.Count);
                            for (Int32 i = 0; i < mesh.VertexCount; ++i)
                            {
                                taggedVertexIndex.Add(false);
                                taggedVertexWeights.Add(0.0f);
                            }
#endif
                            #endregion
                        }
                    }
                }

                // BFS search to construct bone data
                Stack <Node> nodes = new Stack <Node>();
                nodes.Push(scene.RootNode);

                while (nodes.Count != 0)
                {
                    Node currNode = nodes.Pop();
                    H1SkeletalContext.JointNode jointNode = new H1SkeletalContext.JointNode();

                    H1SkeletalContext.JointNode parentJointNode = jointNodes.Find(x => (x.JointName == currNode.Parent.Name));
                    jointNode.Parent = parentJointNode;
                    ExtractNodeSpace(currNode, ref jointNode);

                    List <Bone>  boneDataList         = null;
                    List <Int32> meshContextIndexList = null;
                    if (extractedBones.TryGetValue(jointNode.JointName, out boneDataList))
                    {
                        meshContextIndexList = boneToMeshContextIndex[jointNode.JointName];

                        // looping bone data list, extract bone data
                        Int32 currBoneIndex = 0;
                        foreach (Bone bone in boneDataList)
                        {
                            H1SkeletalContext.Joint newJointData = new H1SkeletalContext.Joint();
                            ExtractBone(bone, ref newJointData);

                            newJointData.MeshContextIndex = meshContextIndexList[currBoneIndex];
                            currBoneIndex++;

                            // store mesh context local-to-global H1Transform for later transformation of offsetMatrix for animation
                            newJointData.MeshContextLocalToGlobal = convertedModel.Meshes[newJointData.MeshContextIndex].LocalToGlobalTransform;

                            // add new joint data
                            jointNode.JointDataList.Add(newJointData);
                        }

                        // mark this node is bone-space
                        jointNode.MarkedAsBoneSpace = true;

                        // tag its parent until it reaches the state that 'MarkedAsBoneSpace' is true
                        H1SkeletalContext.JointNode markNode = jointNode.Parent;
                        while (markNode != null && markNode.MarkedAsBoneSpace != true)
                        {
                            markNode.MarkedAsBoneSpace = true;
                            markNode = markNode.Parent;
                        }
                    }
                    else
                    {
                        // for debugging
                    }

                    foreach (Node child in currNode.Children)
                    {
                        nodes.Push(child);
                        // tag child names to process child nodes after BFS search
                        jointNode.ChildNodeNames.Add(child.Name);
                    }

                    // add new joint node
                    Int32 newJointNodeIndex = jointNodes.Count;
                    jointNodes.Add(jointNode);
                    jointNameToJointNodeIndex.Add(jointNode.JointName, newJointNodeIndex);
                }

                // process tagged child nodes
                foreach (H1SkeletalContext.JointNode node in jointNodes)
                {
                    foreach (String childName in node.ChildNodeNames)
                    {
                        node.Children.Add(jointNodes[jointNameToJointNodeIndex[childName]]);
                    }
                }

                #region Debug Validation for total weight value of weighted vertices & vertex counts
#if DEBUG
                foreach (H1SkeletalContext.JointNode node in jointNodes)
                {
                    foreach (H1SkeletalContext.Joint jointData in node.JointDataList)
                    {
                        foreach (H1SkeletalContext.WeightedVertex weightedVertex in jointData.WeightedVertices)
                        {
                            // confirm all vertices in Mesh are tagged by weighted vertices
                            taggedVertexIndex[taggedVertexIndexOffset[jointData.MeshContextIndex] + weightedVertex.VertexIndex] = true;
                            // add vertex weight to verify
                            taggedVertexWeights[taggedVertexIndexOffset[jointData.MeshContextIndex] + weightedVertex.VertexIndex] += weightedVertex.Weight;
                        }
                    }
                }

                // verification code to extract not tagged vertex index
                List <Int32> notTaggedVertexIndex = new List <Int32>();
                Int32        currVertexIndex      = 0;
                foreach (Boolean isTagged in taggedVertexIndex)
                {
                    if (isTagged == false)
                    {
                        notTaggedVertexIndex.Add(currVertexIndex);
                    }
                    currVertexIndex++;
                }
                if (notTaggedVertexIndex.Count > 0)
                {
                    return(false);
                }

                // verification code to extract vertex which has invalid vertex weight value
                List <Int32> invalidVertexWeightVertexIndex = new List <Int32>();
                currVertexIndex = 0;
                foreach (float currVertexWeights in taggedVertexWeights)
                {
                    if (currVertexWeights < 0.99f)
                    {
                        invalidVertexWeightVertexIndex.Add(currVertexIndex);
                    }
                }
                if (invalidVertexWeightVertexIndex.Count > 0)
                {
                    return(false);
                }
#endif
                #endregion

                convertedModel.SkeletalContexts.Add(skeletalContext);
            }

            // extract the animations
            if (scene.HasAnimations)
            {
                H1AnimationContext newAnimContext = new H1AnimationContext();
                for (Int32 animIndex = 0; animIndex < scene.AnimationCount; ++animIndex)
                {
                    Animation currAnimation = scene.Animations[animIndex];
                    if (currAnimation.HasNodeAnimations) // we only handle node animations (not vertex animation)
                    {
                        // create new animation sequence
                        H1AnimationContext.AnimSequence newAnimSeq = new H1AnimationContext.AnimSequence();
                        newAnimSeq.AnimSeqName    = currAnimation.Name;
                        newAnimSeq.Duration       = currAnimation.DurationInTicks;
                        newAnimSeq.TicksPerSecond = currAnimation.TicksPerSecond;

                        foreach (NodeAnimationChannel animChannel in currAnimation.NodeAnimationChannels)
                        {
                            H1AnimationContext.JointAnimation newJointAnim = new H1AnimationContext.JointAnimation();
                            newJointAnim.BoneName = animChannel.NodeName;

                            // calculate maximum number of keys to fill up the special case (only holding one key)
                            Int32 maxNumKeys = Math.Max(Math.Max(animChannel.ScalingKeyCount, animChannel.RotationKeyCount), animChannel.PositionKeyCount);

                            if (animChannel.HasPositionKeys)
                            {
                                if (animChannel.PositionKeyCount == 1) // special handling (only holding one key)
                                {
                                    VectorKey positionKey = animChannel.PositionKeys[0];
                                    for (Int32 numKey = 0; numKey < maxNumKeys; ++numKey)
                                    {
                                        H1AnimationContext.PositionKey newPosKey = new H1AnimationContext.PositionKey();
                                        newPosKey.Time  = positionKey.Time;
                                        newPosKey.Value = new Vector3(positionKey.Value.X, positionKey.Value.Y, positionKey.Value.Z);

                                        newJointAnim.PosKeys.Add(newPosKey);
                                    }
                                }

                                else
                                {
                                    foreach (VectorKey positionKey in animChannel.PositionKeys)
                                    {
                                        H1AnimationContext.PositionKey newPosKey = new H1AnimationContext.PositionKey();
                                        newPosKey.Time  = positionKey.Time;
                                        newPosKey.Value = new Vector3(positionKey.Value.X, positionKey.Value.Y, positionKey.Value.Z);

                                        newJointAnim.PosKeys.Add(newPosKey);
                                    }
                                }
                            }

                            if (animChannel.HasRotationKeys)
                            {
                                if (animChannel.RotationKeyCount == 1) // special handling (only holding one key)
                                {
                                    QuaternionKey quatKey = animChannel.RotationKeys[0];
                                    for (Int32 numKey = 0; numKey < maxNumKeys; ++numKey)
                                    {
                                        H1AnimationContext.QuaternionKey newQuatKey = new H1AnimationContext.QuaternionKey();
                                        newQuatKey.Time  = quatKey.Time;
                                        newQuatKey.Value = new SharpDX.Quaternion(quatKey.Value.X, quatKey.Value.Y, quatKey.Value.Z, quatKey.Value.W);

                                        newJointAnim.RotKeys.Add(newQuatKey);
                                    }
                                }

                                else
                                {
                                    foreach (QuaternionKey quatKey in animChannel.RotationKeys)
                                    {
                                        H1AnimationContext.QuaternionKey newQuatKey = new H1AnimationContext.QuaternionKey();
                                        newQuatKey.Time  = quatKey.Time;
                                        newQuatKey.Value = new SharpDX.Quaternion(quatKey.Value.X, quatKey.Value.Y, quatKey.Value.Z, quatKey.Value.W);

                                        newJointAnim.RotKeys.Add(newQuatKey);
                                    }
                                }
                            }

                            if (animChannel.HasScalingKeys)
                            {
                                if (animChannel.ScalingKeyCount == 1) // special handling (only holding one key)
                                {
                                    VectorKey scalingKey = animChannel.ScalingKeys[0];
                                    for (Int32 numKey = 0; numKey < maxNumKeys; ++numKey)
                                    {
                                        H1AnimationContext.ScalingKey newScalingKey = new H1AnimationContext.ScalingKey();
                                        newScalingKey.Time  = scalingKey.Time;
                                        newScalingKey.Value = new Vector3(scalingKey.Value.X, scalingKey.Value.Y, scalingKey.Value.Z);

                                        newJointAnim.ScaleKeys.Add(newScalingKey);
                                    }
                                }

                                else
                                {
                                    foreach (VectorKey scalingKey in animChannel.ScalingKeys)
                                    {
                                        H1AnimationContext.ScalingKey newScalingKey = new H1AnimationContext.ScalingKey();
                                        newScalingKey.Time  = scalingKey.Time;
                                        newScalingKey.Value = new Vector3(scalingKey.Value.X, scalingKey.Value.Y, scalingKey.Value.Z);

                                        newJointAnim.ScaleKeys.Add(newScalingKey);
                                    }
                                }
                            }

                            newAnimSeq.BoneAnimations.Add(newJointAnim);
                        }

                        newAnimContext.AnimSequences.Add(newAnimSeq);
                    }
                }

                // set the animation context
                convertedModel.AnimationContext = newAnimContext;
            }

            return(true);
        }