A collection of Bone objects used to animate a skinned mesh.
Skeletal animation works by having a collection of 'bones' which are actually just joints with a position and orientation, arranged in a tree structure. For example, the wrist joint is a child of the elbow joint, which in turn is a child of the shoulder joint. Rotating the shoulder automatically moves the elbow and wrist as well due to this hierarchy.

So how does this animate a mesh? Well every vertex in a mesh is assigned to one or more bones which affects it's position when the bone is moved. If a vertex is assigned to more than one bone, then weights must be assigned to determine how much each bone affects the vertex (actually a weight of 1.0 is used for single bone assignments). Weighted vertex assignments are especially useful around the joints themselves to avoid 'pinching' of the mesh in this region.

Therefore by moving the skeleton using preset animations, we can animate the mesh. The advantage of using skeletal animation is that you store less animation data, especially as vertex counts increase. In addition, you are able to blend multiple animations together (e.g. walking and looking around, running and shooting) and provide smooth transitions between animations without incurring as much of an overhead as would be involved if you did this on the core vertex data.

Skeleton definitions are loaded from datafiles, namely the .xsf file format. They are loaded on demand, especially when referenced by a Mesh.

Inheritance: Axiom.Core.Resource
 public void ExportSkeleton(Skeleton skeleton, string fileName)
 {
     this.skeleton = skeleton;
     FileStream stream = new FileStream(fileName, FileMode.Create);
     try {
         BinaryWriter writer = new BinaryWriter(stream);
         WriteFileHeader(writer, version);
         WriteSkeleton(writer);
     } finally {
         if (stream != null)
             stream.Close();
     }
 }
		public void ImportSkeleton( Stream stream, Skeleton skeleton )
		{
			// store a local reference to the mesh for modification
			this.skeleton = skeleton;

			var reader = new BinaryReader( stream, System.Text.Encoding.UTF8 );

			// start off by taking a look at the header
			ReadFileHeader( reader );

			SkeletonChunkID chunkID = 0;

			while ( !IsEOF( reader ) )
			{
				chunkID = ReadChunk( reader );

				switch ( chunkID )
				{
					case SkeletonChunkID.Bone:
						ReadBone( reader );
						break;

					case SkeletonChunkID.BoneParent:
						ReadBoneParent( reader );
						break;

					case SkeletonChunkID.Animation:
						ReadAnimation( reader );
						break;

					case SkeletonChunkID.AttachmentPoint:
						ReadAttachmentPoint( reader );
						break;

					default:
						LogManager.Instance.Write(
							"Can only parse bones, parents, and animations at the top level during skeleton loading." );
						LogManager.Instance.Write( "Unexpected chunk: " + chunkID.ToString() );
						break;
				} // switch
			} // while

			// assume bones are stored in binding pose
			skeleton.SetBindingPose();
		}
        public void Import(Skeleton skeleton)
        {
            // store a local reference to the skeleton for modification
            this.skeleton = skeleton;

            XmlDocument document = new XmlDocument();
            document.Load(stream);
            foreach (XmlNode childNode in document.ChildNodes) {
                switch (childNode.Name) {
                    case "skeleton":
                        ReadSkeleton(childNode);
                        break;
                    default:
                        DebugMessage(childNode);
                        break;
                }
            }
            skeleton.SetBindingPose();
        }
Exemple #4
0
		/// <summary>
		///		Constructor.
		/// </summary>
		/// <param name="handle">Handle to use.</param>
		/// <param name="creator">Skeleton who created this tagpoint.</param>
		public TagPoint( ushort handle, Skeleton creator )
			: base( handle, creator )
		{
			suppressUpdateEvent = true;
			InheritParentEntityOrientation = true;
			InheritParentEntityScale = true;
		}
Exemple #5
0
		public void Apply( Skeleton skeleton, float time, float weight, bool accumulate, float scale )
		{
			// loop through tracks and update them all with current time
			foreach ( KeyValuePair<ushort, NodeAnimationTrack> pair in nodeTrackList )
			{
				NodeAnimationTrack track = pair.Value;
				Bone bone = skeleton.GetBone( pair.Key );
				track.ApplyToNode( bone, time, weight, accumulate, scale );
			}
		}
		/// <summary>
		///		Constructor, don't call directly, this will be created automatically
		///		when you create an <see cref="Entity"/> based on a skeletally animated Mesh.
		/// </summary>
		/// <param name="masterCopy"></param>
		public SkeletonInstance( Skeleton masterCopy )
			: base()
		{
			this.skeleton = masterCopy;
		}
        private void SetUpInitialPose( Skeleton skeleton )
        {
            // Set up the initial pose for this model
            if( skeleton != null &&
                0 < BoneParents.Count )
            {
                // Ok, now we have a skeleton, so we can go ahead and rig the model
                // and animate it.
                foreach( GeometryInstance geoInstance in GeoInstances )
                {
                    SkinController skinController = geoInstance.controller as SkinController;

                    if( null != skinController )
                    {
                        m_Log.Debug( "Skinning: " + geoInstance.name );
                        geoInstance.SkinGeometry( m_AxiomMesh, skeleton, NodeSidMap, skinController );

                        foreach( PoseInfo info in m_PoseInfoCatalog.TargetInfos( geoInstance.geoSet.Name ) )
                        {
                            geoInstance.SkinMorphTarget( info.TargetGeometry, m_AxiomMesh, skeleton, NodeSidMap, skinController );
                        }
                    }
                    else
                    {
                        // If there is no controller for this geometry, then it probably is a rigging
                        // artifact.  We need to remove it from the Axiom.Mesh, or Axiom will choke because
                        // it will be missing skinning bindings for the mesh.
                        //
                        // I've added this flag to allow disabling this culling because I'm worried that
                        // the culling will break in some unaccounted-for condition.  What, me worry?
                        if( ! NoRiggingCulling )
                        {
                            try
                            {
                                // Make sure it's not any kind of controller--morph, for example.
                                if( null == geoInstance.controller )
                                {
                                    m_Log.Debug( "Removing: " + geoInstance.name );
                                    // remove (maybe conditionally) submeshes for the unskinned instance
                                    foreach( MeshGeometry mesh in geoInstance.geoSet )
                                    {
                                        m_Log.Debug( "    ==> " + mesh.Id );
                                        m_AxiomMesh.RemoveSubMesh( mesh.Id );
                                    }
                                }
                            }
                            catch( Exception ex )
                            {
                                // There is no query on Axiom.Mesh to see if it contains a given submesh,
                                // so it throws an exception if you try to remove an unknown submesh. It
                                // is not an error in this case, so we catch and swallow the exception.
                                m_Log.Debug( "Cannot remove submesh; " + Environment.NewLine + ex.Message );
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        ///   Import into the mesh, using the skeleton provided, and 
        ///   assigning the animation data to a new animation.
        /// </summary>
        /// <param name="transform">the world transform to apply to this object</param>
        /// <param name="mesh">the mesh we will populate</param>
        /// <param name="skeleton">the skeleton to which we will add animations (or null if we are creating one)</param>
        /// <param name="animationName">the name that will be used for the animation</param>
        /// <param name="materialNamespace">namespace used for generation of material names</param>
        public void Import( Matrix4 transform, Mesh mesh, Skeleton skeleton, string animationName, string materialNamespace )
        {
            ColladaMeshReader reader = null;

            XmlDocument document = new XmlDocument();

            document.Load( m_Stream );

            XmlElement rootElement = document.DocumentElement;

            // This is slightly weird. The client calls this method on this object,
            // but then we determine which version of collada we're actually looking
            // at, and create a new instances of a derived collada reader.  As an
            // outcome, we have to copy fields from the factory-created instance
            // back to this instance.
            // TODO: Need a static factory method on the base class to create the
            // collada reader instance, then call that instance from the client;
            // that way we'll only have one instance in the first place.
            reader = GetColladaParser( rootElement );

            reader.m_ColladaRootNode = rootElement;
            reader.m_Document = document;

            reader.m_MaterialBuilder = new MaterialScriptBuilder( materialNamespace );

            ColladaMeshInfo meshInfo = new ColladaMeshInfo( mesh );

            reader.ReadCollada( rootElement, meshInfo );

            meshInfo.NoRiggingCulling = NoRiggingCulling;

            meshInfo.Process( transform, skeleton, m_BaseFile, animationName );

            this.m_MaterialBuilder = reader.MaterialBuilder;
        }
Exemple #9
0
		/// <summary>
		///    Internal notification, used to tell the Mesh which Skeleton to use without loading it.
		/// </summary>
		/// <remarks>
		///    This is only here for unusual situation where you want to manually set up a
		///    Skeleton. Best to let the engine deal with this, don't call it yourself unless you
		///    really know what you're doing.
		/// </remarks>
		/// <param name="skeleton"></param>
		public void NotifySkeleton( Skeleton skeleton )
		{
			this._skeleton = skeleton;
			this._skeletonName = skeleton.Name;
		}
        protected void TransformAnimation(Matrix4 exportTransform, 
										  Animation newAnim, Animation anim,
										  Skeleton newSkeleton)
        {
            foreach (NodeAnimationTrack track in anim.NodeTracks.Values) {
                NodeAnimationTrack newTrack = newAnim.CreateNodeTrack(track.Handle);
                Bone targetBone = (Bone)track.TargetNode;
                newTrack.TargetNode = newSkeleton.GetBone(targetBone.Handle);
                TransformTrack(exportTransform, newTrack, track, targetBone);
            }
        }
        protected void TransformAnimation(Matrix4 unscaledTransform, float scale,
										  Animation newAnim, Animation anim,
										  Skeleton newSkeleton)
        {
            // With the new idea I had for transforming these, I need the tracks
            // set up for the parent bones before I can handle the child bones.
            for (int i = 0; i < anim.Tracks.Count; ++i) {
                AnimationTrack track = anim.Tracks[i];
                AnimationTrack newTrack = newAnim.CreateTrack(track.Handle);
                Bone targetBone = (Bone)track.TargetNode;
                newTrack.TargetNode = newSkeleton.GetBone(targetBone.Handle);
            }
            // This gets the ordered bone list, and transforms the tracks in
            // that order instead.
            List<Bone> orderedBoneList = GetOrderedBoneList(null, newSkeleton);
            foreach (Bone bone in orderedBoneList)
                TransformTrack(unscaledTransform, scale, newAnim, anim, bone);
        }
        public static void CleanupAnimation(Skeleton skel, Animation anim)
        {
            Animation newAnim = skel.CreateAnimation("_replacement", anim.Length);
            Animation tmpAnim = skel.CreateAnimation("_temporary", anim.Length);
            foreach (NodeAnimationTrack track in anim.NodeTracks.Values) {
                Bone bone = skel.GetBone((ushort)track.Handle);
                NodeAnimationTrack newTrack = newAnim.CreateNodeTrack(track.Handle, bone);
                NodeAnimationTrack tmpTrack = tmpAnim.CreateNodeTrack(track.Handle, bone);
                int maxFrame = track.KeyFrames.Count;
                int lastKeyFrame = -1;
                for (int keyFrameIndex = 0; keyFrameIndex < track.KeyFrames.Count; ++keyFrameIndex) {
                    if (anim.InterpolationMode == InterpolationMode.Linear) {
                        // Linear is based on one point before and one after.
                        TransformKeyFrame cur = track.GetTransformKeyFrame(keyFrameIndex);
                        if (keyFrameIndex == 0 ||
                            keyFrameIndex == (track.KeyFrames.Count - 1)) {
                            // Add the key frame if it is the first or last keyframe.
                            lastKeyFrame = keyFrameIndex;
                            DuplicateKeyFrame(newTrack, cur);
                        } else {
                            // Make sure tmpTrack is clean.. we just use it for interpolation
                            tmpTrack.RemoveAllKeyFrames();
                            TransformKeyFrame prior = track.GetTransformKeyFrame(lastKeyFrame);
                            TransformKeyFrame next = track.GetTransformKeyFrame(keyFrameIndex + 1);
                            DuplicateKeyFrame(tmpTrack, prior);
                            DuplicateKeyFrame(tmpTrack, next);
                            // Check to see if removing this last keyframe will throw off
                            // any of the other keyframes that were considered redundant.
                            bool needKeyFrame = false;
                            for (int i = lastKeyFrame + 1; i <= keyFrameIndex; ++i) {
                                TransformKeyFrame orig = track.GetTransformKeyFrame(i);
                                TransformKeyFrame interp = new TransformKeyFrame(tmpTrack, orig.Time);
                                tmpTrack.GetInterpolatedKeyFrame(orig.Time, interp);
                                // Is this interpolated frame useful or redundant?
                                if (!CompareKeyFrames(interp, cur)) {
                                    needKeyFrame = true;
                                    break;
                                }
                            }
                            if (needKeyFrame) {
                                lastKeyFrame = keyFrameIndex;
                                DuplicateKeyFrame(newTrack, cur);
                            }
                        }
                    } else if (anim.InterpolationMode == InterpolationMode.Spline) {
                        // Spline is based on two points before and two after.
                        TransformKeyFrame cur = track.GetTransformKeyFrame(keyFrameIndex);
            #if DISABLED_CODE
                        if (keyFrameIndex == 0 ||
                            keyFrameIndex == 1 ||
                            keyFrameIndex == (track.KeyFrames.Count - 1) ||
                            keyFrameIndex == (track.KeyFrames.Count - 2)) {
                            // Add the key frame if it is the first, second, last or second to last keyframe.
                            DuplicateKeyFrame(newTrack, cur);
                        } else {
                            // Make sure tmpTrack is clean.. we just use it for interpolation
                            tmpTrack.RemoveAllKeyFrames();
                            TransformKeyFrame prior1 = track.GetTransformKeyFrame(keyFrameIndex - 2);
                            TransformKeyFrame prior2 = track.GetTransformKeyFrame(keyFrameIndex - 1);
                            TransformKeyFrame next1 = track.GetTransformKeyFrame(keyFrameIndex + 1);
                            TransformKeyFrame next2 = track.GetTransformKeyFrame(keyFrameIndex + 2);
                            DuplicateKeyFrame(tmpTrack, prior1);
                            DuplicateKeyFrame(tmpTrack, prior2);
                            DuplicateKeyFrame(tmpTrack, next1);
                            DuplicateKeyFrame(tmpTrack, next2);
                            TransformKeyFrame interp = new TransformKeyFrame(tmpTrack, cur.Time);
                            tmpTrack.GetInterpolatedKeyFrame(cur.Time, interp);
                            // Is this interpolated frame useful or redundant?
                            if (!CompareKeyFrames(interp, cur))
                                DuplicateKeyFrame(newTrack, cur);
                        }
            #else
                        DuplicateKeyFrame(newTrack, cur);
            #endif
                    } else {
                        System.Diagnostics.Debug.Assert(false, "Invalid InterpolationMode: " + anim.InterpolationMode);
                    }
                }
            }
            skel.RemoveAnimation(tmpAnim.Name);
            skel.RemoveAnimation(newAnim.Name);
            skel.RemoveAnimation(anim.Name);

            // Recreate the animation with the proper name (awkward)
            anim = skel.CreateAnimation(anim.Name, anim.Length);
            foreach (NodeAnimationTrack track in newAnim.NodeTracks.Values) {
                Bone bone = skel.GetBone((ushort)track.Handle);
                NodeAnimationTrack newTrack = anim.CreateNodeTrack(track.Handle, bone);
                foreach (KeyFrame keyFrame in track.KeyFrames)
                    DuplicateKeyFrame(newTrack, (TransformKeyFrame)keyFrame);
            }
        }
 public void Export(Skeleton skeleton, Matrix4 exportTransform)
 {
     this.exportTransform = exportTransform;
     float det = exportTransform.Determinant;
     this.exportScale = (float)Math.Pow(det, 1 / 3.0f);
     Export(skeleton);
 }
 public static void CleanupAnimation(Skeleton skel, string anim_name)
 {
     Animation anim = skel.GetAnimation(anim_name);
     CleanupAnimation(skel, anim);
 }
        private Skeleton SynthesizeSkeletonFromBones( Matrix4 transform, string baseFile )
        {
            Skeleton skeleton = null;

            if( BoneParents.Count != 0 )
            {
                skeleton = new Skeleton( baseFile + ".skeleton" );
                m_AxiomMesh.NotifySkeleton( skeleton );
                BuildSkeletonAtBindPose( transform, skeleton );
            }

            return skeleton;
        }
        public void SetSkeleton(SkeletonInstance skel)
        {
            Bone bone = skel.RootBone;
            TreeNode node = new TreeNode();
            node.Name = bone.Name;
            node.Text = bone.Name;
            boneTreeView.Nodes.Add(node);
            AddChildBones(node, bone);

            skeleton = skel;
            string skeletonName = skel.MasterSkeleton.Name;
            helperSkeleton = SkeletonManager.Instance.Load(skeletonName);
        }
        /// <summary>
        ///   Build the bind pose for the skeleton based on the bind pose of the 
        ///   various Controllers (skin clusters).
        ///   This will also set up bones for the tag points.
        /// </summary>
        /// <param name="transform">the transform matrix to convert from the 
        ///                         system used by the modeling tool to the 
        ///                         system that will be used by multiverse</param>
        /// <param name="skeleton">Skeleton that will be built by this call</param>
        protected void BuildSkeletonAtBindPose( Matrix4 transform,
                                    Skeleton skeleton )
        {
            // Construct a list of bones that are of interest to us
            // This will be every bone that influences the skin, including the
            // parent bones (since they indirectly influence the skin).
            List<string> boneNames = new List<string>();
            foreach( Controller controller in Controllers.Values )
            {
                SkinController skinController = controller as SkinController;
                if( skinController == null )
                    continue;
                foreach( string key in skinController.InverseBindMatrices.Keys )
                {
                    string boneName = key;
                    // The entries in the skin controller may use sid.  If so,
                    // change these to use the bone name.
                    if( NodeSidMap.ContainsKey( boneName ) )
                        boneName = NodeSidMap[ boneName ];
                    if( boneNames.Contains( boneName ) )
                        continue;
                    boneNames.Add( boneName );
                    // Add all the parent bones
                    string currentBone = boneName;
                    while( BoneParents.ContainsKey( currentBone ) )
                    {
                        string parentBone = BoneParents[ currentBone ];
                        if( parentBone == null )
                            break;
                        if( !boneNames.Contains( parentBone ) )
                            boneNames.Add( parentBone );
                        currentBone = parentBone;
                    }
                }
            }
            Matrix4 unitConversionMatrix = Matrix4.Identity;
            unitConversionMatrix.Scale = new Vector3( m_UnitConversion, m_UnitConversion, m_UnitConversion );
            Matrix4 worldTransform = transform * unitConversionMatrix;

            // Build the invBindMatrices with the inverse bind matrices for
            // the bones
            Dictionary<string, Matrix4> invBindMatrices =
                new Dictionary<string, Matrix4>();
            Quaternion worldRotate;
            Vector3 worldScale, worldTranslate;
            Matrix4.DecomposeMatrix( ref worldTransform, out worldTranslate, out worldRotate, out worldScale );
            foreach( string boneName in boneNames )
            {
                Matrix4 bonePoseTransform = GetBonePoseTransform( boneName );

                Quaternion bonePoseRotate;
                Vector3 bonePoseTranslate, bonePoseScale;
                Matrix4.DecomposeMatrix( ref bonePoseTransform, out bonePoseTranslate, out bonePoseRotate, out bonePoseScale );

                Vector3 boneTranslate = worldTranslate + worldRotate * MathHelpers.ScaleVector( worldScale, bonePoseTranslate );
                Quaternion boneOrient = worldRotate * bonePoseRotate;

                Matrix4 boneTransform = Multiverse.MathLib.MathUtil.GetTransform( boneOrient, boneTranslate );
                invBindMatrices[ boneName ] = boneTransform.Inverse();
            }
            ProcessBones( invBindMatrices, skeleton, null );
            skeleton.SetBindingPose();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="transform">the transform to apply to this object</param>
        /// <param name="skeleton">the skeleton to which we will add animations, or null to create one</param>
        /// <param name="baseFile">the prefix to be used for the generated skeleton</param>
        /// <param name="animationName">the name to be used for the animation data</param>
        public void Process( Matrix4 transform, Skeleton skeleton, string baseFile, string animationName )
        {
            m_AxiomMesh.BoundingBox = new AxisAlignedBox();

            if( null == skeleton )
            {
                skeleton = SynthesizeSkeletonFromBones( transform * m_UpAxisConversion, baseFile );
            }
            else
            {
                m_AxiomMesh.NotifySkeleton( skeleton );
            }

            // Put any attachment point data in the skeleton or mesh as neeeded
            ProcessAttachmentPoints( transform * m_UpAxisConversion, skeleton, m_AxiomMesh);

            Matrix4 unitConversionMatrix = Matrix4.Identity;
            unitConversionMatrix.Scale = new Vector3( m_UnitConversion, m_UnitConversion, m_UnitConversion );
            Matrix4 worldTransform = transform * m_UpAxisConversion * unitConversionMatrix;

            foreach( GeometryInstance geoInstance in GeoInstances )
            {
                // TODO: This is stuffing yet another transform matrix into the instance
                // (alongside "transfom" and "localTransform").  There ought to be a way
                // to merge this global transform into one of those (likely "transform"),
                // but first I need to understand all the places that touch the transforms.
                geoInstance.GlobalTransform = worldTransform;

                // Removing empty sets prevents the hardware buffer allocation from breaking.
                // TODO: I'm not sure yet if this can happen after BuildVertexBuffers() and
                // SetUpIntialPose(), but it would be nice if it could happen at allocation time.
                geoInstance.RemoveEmptyGeometrySets();

                // This is actually semantically relevant to instances!  You can have more
                // than one instance of a given source geometry, each with its own material
                // binding.  This resolves the binding.
                geoInstance.ApplyMaterialBindings();
            }

            GeneratePoseList();

            BuildVertexBuffers( worldTransform );

            SetUpInitialPose( skeleton );

            foreach( GeometryInstance geoInstance in GeoInstances )
            {
                foreach( MeshGeometry geometry in geoInstance.geoSet )
                {
                    HWBuffer.AllocateBuffer( geometry.VertexData, 0, geometry.VertexDataEntries );
                    ColladaMeshInfo.BuildBoundingBox( m_AxiomMesh, geometry );
                }
            }

            // Build any poses that we need to support morph/pose style animation
            BuildPoses();

            if( skeleton != null && animationName != null )
            {
                Animation anim = CreateAnimation( animationName );
                if( anim != null )
                {
                    // Build skeletal animations
                    ProcessChannels( worldTransform, anim, skeleton );
                    // Remove redundant linear keyframes
                    AnimationHelper.CleanupAnimation( skeleton, animationName );
                }
            }

            // Handle morph (and perhaps pose) animation
            ProcessVertexAnimationChannels();

            float len = 0;
            foreach( Vector3 corner in m_AxiomMesh.BoundingBox.Corners )
            {
                len = Math.Max( len, corner.LengthSquared );
            }
            m_AxiomMesh.BoundingSphereRadius = (float) Math.Sqrt( len );
        }
 public void Export(Skeleton skeleton)
 {
     // store a local reference to the mesh for modification
     this.skeleton = skeleton;
     this.document = new XmlDocument();
     XmlNode skeletonNode = WriteSkeleton();
     document.AppendChild(skeletonNode);
     document.Save(stream);
 }
Exemple #20
0
		public void Apply( Skeleton skeleton, float time, float weight, bool accumulate, float scale )
		{
			// loop through tracks and update them all with current time
			foreach ( var pair in this.nodeTrackList )
			{
				var track = pair.Value;
				var bone = skeleton.GetBone( pair.Key );
				track.ApplyToNode( bone, time, weight, accumulate, scale );
			}
		}
 public List<Bone> GetOrderedBoneList(Bone parentBone, Skeleton skel)
 {
     List<Bone> rv = new List<Bone>();
     // Get all the bones that are my children
     List<Bone> childBones = new List<Bone>();
     for (ushort i = 0; i < skel.BoneCount; ++i) {
         Bone bone = skel.GetBone(i);
         if (bone.Parent == parentBone)
             childBones.Add(bone);
     }
     rv.AddRange(childBones);
     // For each of those bones, get their descendents
     foreach (Bone childBone in childBones) {
         List<Bone> bones = GetOrderedBoneList(childBone, skel);
         rv.AddRange(bones);
     }
     return rv;
 }
 /// <summary>
 ///   Utility method to add attachment points to an existing skeleton
 /// </summary>
 /// <param name="dstDir">the directory to which the modified skeleton will be saved</param>
 /// <param name="skelFile">the name of the file to which the modified skeleton will be written</param>
 /// <param name="skeleton">the original skeleton</param>
 /// <param name="attachPoints">the list of attachment points</param>
 private static void AddAttachments( string dstDir,
                                    string skelFile,
                                    Skeleton skeleton,
                                    List<AttachmentPointNode> attachPoints )
 {
     if( skeleton.AttachmentPoints.Count > 0 &&
         attachPoints.Count != skeleton.AttachmentPoints.Count )
         log.WarnFormat( "Skeleton attachment points count ({0}) does not match new count ({1})",
                         skeleton.AttachmentPoints.Count, attachPoints.Count );
     foreach( AttachmentPointNode attachPoint in attachPoints )
     {
         Quaternion rotate;
         Vector3 translate, scale;
         Matrix4 transform = attachPoint.Transform;
         Matrix4.DecomposeMatrix( ref transform, out translate, out rotate, out scale );
         Bone parentBone = skeleton.GetBone( attachPoint.ParentBone );
         bool isDup = false;
         foreach( AttachmentPoint tmp in skeleton.AttachmentPoints )
             if( tmp.Name == attachPoint.Name )
                 isDup = true;
         if( isDup )
             continue;
         skeleton.CreateAttachmentPoint( attachPoint.Name, parentBone.Handle, rotate, translate );
     }
     OgreSkeletonSerializer skelWriter = new OgreSkeletonSerializer();
     skelWriter.ExportSkeleton( skeleton, dstDir + skelFile );
 }
 protected void TransformSkeleton(Matrix4 unscaledTransform, float scale)
 {
     Matrix4 invExportTransform = unscaledTransform.Inverse();
     Dictionary<string, Matrix4> fullInverseBoneTransforms = new Dictionary<string, Matrix4>();
     Skeleton newSkeleton = new Skeleton(skeleton.Name);
     // Construct new versions of the bones, and build
     // the inverse bind matrix that will be needed.
     for (ushort i = 0; i < skeleton.BoneCount; ++i) {
         Bone bone = skeleton.GetBone(i);
         Bone newBone = newSkeleton.CreateBone(bone.Name, bone.Handle);
         fullInverseBoneTransforms[bone.Name] =
             bone.BindDerivedInverseTransform * invExportTransform;
     }
     //  Build the parenting relationship for the new skeleton
     for (ushort i = 0; i < skeleton.BoneCount; ++i) {
         Bone bone = skeleton.GetBone(i);
         Bone newBone = newSkeleton.GetBone(i);
         Bone parentBone = (Bone)bone.Parent;
         if (parentBone != null) {
             Bone newParentBone = newSkeleton.GetBone(parentBone.Handle);
             newParentBone.AddChild(newBone);
         }
     }
     // Set the orientation and position for the various bones
     for (ushort i = 0; i < newSkeleton.BoneCount; ++i) {
         Bone bone = skeleton.GetBone(i);
         string boneName = bone.Name;
         string parentName = (bone.Parent == null) ? null : bone.Parent.Name;
         Matrix4 transform = GetLocalBindMatrix(fullInverseBoneTransforms, boneName, parentName, true);
         Quaternion orientation = GetRotation(transform);
         Bone newBone = newSkeleton.GetBone(i);
         newBone.Orientation = orientation;
         // newBone.Scale = transform.Scale;
         newBone.Position = scale * transform.Translation;
     }
     newSkeleton.SetBindingPose();
     for (int i = 0; i < skeleton.AnimationCount; ++i) {
         Animation anim = skeleton.GetAnimation(i);
         Animation newAnim = newSkeleton.CreateAnimation(anim.Name, anim.Length);
         TransformAnimation(unscaledTransform, scale, newAnim, anim, newSkeleton);
     }
     skeleton = newSkeleton;
 }
 private static Skeleton ReadSkeleton( Matrix4 transform, string srcDir, string skelFile )
 {
     Stream skelData = new FileStream( srcDir + skelFile, FileMode.Open );
     Skeleton skeleton = new Skeleton( skelFile );
     if( skelFile.EndsWith( ".skeleton" ) )
     {
         OgreSkeletonSerializer skelReader = new OgreSkeletonSerializer();
         skelReader.ImportSkeleton( skelData, skeleton );
     }
     else if( skelFile.EndsWith( ".skeleton.xml" ) )
     {
         OgreXmlSkeletonReader skelReader = new OgreXmlSkeletonReader( skelData );
         skelReader.Import( skeleton );
     }
     else
     {
         skelData.Close();
         string extension = Path.GetExtension( skelFile );
         throw new AxiomException( "Unsupported skeleton format '{0}'", extension );
     }
     skelData.Close();
     return skeleton;
 }
 /// <summary>
 ///		Constructor.
 /// </summary>
 /// <param name="handle">Handle to use.</param>
 /// <param name="creator">Skeleton who created this tagpoint.</param>
 public TagPoint(ushort handle, Skeleton creator)
     : base(handle, creator)
 {
     suppressUpdateEvent = true;
 }
        /// <summary>
        ///   Utility method to merge animations from other files into a single skeleton
        /// </summary>
        /// <param name="srcDir">the directory from which the new animations will be loaded</param>
        /// <param name="dstDir">the directory to which the modified skeleton will be saved</param>
        /// <param name="skelFile">the name of the file to which the modified skeleton will be written</param>
        /// <param name="transform">the transform to apply to the skeleton and animations</param>
        /// <param name="skeleton">the original skeleton</param>
        /// <param name="animations">the list of animations</param>
        private static void AddAnimations( string srcDir, string dstDir,
                                        string skelFile,
                                        Matrix4 transform, Skeleton skeleton,
                                        List<AnimationEntry> animations )
        {
            // mesh loading stats
            int before, after;

            // get the tick count before loading the mesh
            before = Environment.TickCount;

            foreach( AnimationEntry entry in animations )
            {
                Mesh mesh = new Mesh( "Mesh" );
                Stream data = new FileStream( srcDir + entry.animation_file, FileMode.Open );
                ColladaMeshReader meshReader = new ColladaMeshReader( data, null );
                // import the .dae file
                meshReader.Import( transform, mesh, skeleton, entry.animation_name, null );
                // close the stream (we don't need to leave it open here)
                data.Close();
            }

            // get the tick count after loading the mesh
            after = Environment.TickCount;

            // record the time elapsed while loading the mesh
            log.InfoFormat( "Mesh: took {0}ms", (after - before) );

            //// prepare the mesh for a shadow volume?
            //if (MeshManager.Instance.PrepareAllMeshesForShadowVolumes) {
            //    if (edgeListsBuilt || autoBuildEdgeLists) {
            //        PrepareForShadowVolume();
            //    }
            //    if (!edgeListsBuilt && autoBuildEdgeLists) {
            //        BuildEdgeList();
            //    }
            //}

            OgreSkeletonSerializer skelWriter = new OgreSkeletonSerializer();
            skelWriter.ExportSkeleton( skeleton, dstDir + skelFile );
        }
Exemple #27
0
		/// <summary>
		///		Unloads the mesh data.
		/// </summary>
		protected override void unload()
		{
			// Dispose managed resources.
			if ( this._skeleton != null )
			{
				if ( !Skeleton.IsDisposed )
				{
					this._skeleton.Dispose();
				}

				this._skeleton = null;
			}

			foreach ( var subMesh in this._subMeshList )
			{
				if ( !subMesh.IsDisposed )
				{
					subMesh.Dispose();
				}
			}
			this._subMeshList.Clear();

			if ( this._sharedVertexData != null )
			{
				if ( !this._sharedVertexData.IsDisposed )
				{
					this._sharedVertexData.Dispose();
				}

				this._sharedVertexData = null;
			}

			this._isPreparedForShadowVolumes = false;

			//// TODO: SubMeshNameCount
			//// TODO: Remove LOD levels
		}
		public InstancedGeometry( SceneManager owner, String name )
		{
			mOwner = owner;
			mName = name;
			mBuilt = false;
			mUpperDistance = 0.0f;
			mSquaredUpperDistance = 0.0f;
			mCastShadows = false;
			mBatchInstanceDimensions = new Vector3( 1000, 1000, 1000 );
			mHalfBatchInstanceDimensions = new Vector3( 500, 500, 500 );
			mOrigin = new Vector3( 0, 0, 0 );
			mVisible = true;
			mRenderQueueID = (byte)RenderQueueGroupID.Main;
			mRenderQueueIDSet = false;
			mObjectCount = 0;
			mInstancedGeometryInstance = null;
			mSkeletonInstance = null;
			mBaseSkeleton = null;
		}
        private LineRenderable CreateBoneLines(Skeleton skeleton)
        {
            List<Vector3> points = new List<Vector3>();
            List<ColorEx> colors = new List<ColorEx>();
            List<VertexBoneAssignment> vbas = new List<VertexBoneAssignment>();
            int vertexIndex = 0;
            VertexBoneAssignment vba;
            foreach (Bone bone in skeleton.Bones) {
                Matrix4 bindTransform = bone.BindDerivedInverseTransform.Inverse();
                Vector3 bonePosition = bindTransform.Translation;
                Bone parentBone = null;
                if (bone.Parent != null)
                    parentBone = bone.Parent as Bone;
                // If we have a parent bone, draw a line to the parent bone
                if (parentBone != null) {
                    points.Add(parentBone.BindDerivedInverseTransform.Inverse().Translation);
                    points.Add(bonePosition);
                    colors.Add(ColorEx.Cyan);
                    colors.Add(ColorEx.Cyan);
                    // Set up the vba for the bone base
                    vba = new VertexBoneAssignment();
                    vba.vertexIndex = vertexIndex++;
                    vba.weight = 1.0f;
                    vba.boneIndex = parentBone.Handle;
                    vbas.Add(vba);
                    // Set up the vba for the bone end
                    vba = new VertexBoneAssignment();
                    vba.vertexIndex = vertexIndex++;
                    vba.weight = 1.0f;
                    vba.boneIndex = bone.Handle;
                    vbas.Add(vba);
                }
                // Set up axis lines for this entry
                // X axis line
                points.Add(bonePosition);
                points.Add(bindTransform * (Vector3.UnitX * boneAxisLength));
                colors.Add(ColorEx.Red);
                colors.Add(ColorEx.Red);
                vba = new VertexBoneAssignment();
                vba.vertexIndex = vertexIndex++;
                vba.weight = 1.0f;
                vba.boneIndex = bone.Handle;
                vbas.Add(vba);
                vba = new VertexBoneAssignment();
                vba.vertexIndex = vertexIndex++;
                vba.weight = 1.0f;
                vba.boneIndex = bone.Handle;
                vbas.Add(vba);
                // Y axis line
                points.Add(bonePosition);
                points.Add(bindTransform * (Vector3.UnitY * boneAxisLength));
                colors.Add(ColorEx.Blue);
                colors.Add(ColorEx.Blue);
                vba = new VertexBoneAssignment();
                vba.vertexIndex = vertexIndex++;
                vba.weight = 1.0f;
                vba.boneIndex = bone.Handle;
                vbas.Add(vba);
                vba = new VertexBoneAssignment();
                vba.vertexIndex = vertexIndex++;
                vba.weight = 1.0f;
                vba.boneIndex = bone.Handle;
                vbas.Add(vba);
                // Z axis line
                points.Add(bonePosition);
                points.Add(bindTransform * (Vector3.UnitZ * boneAxisLength));
                colors.Add(ColorEx.Lime);
                colors.Add(ColorEx.Lime);
                vba = new VertexBoneAssignment();
                vba.vertexIndex = vertexIndex++;
                vba.weight = 1.0f;
                vba.boneIndex = bone.Handle;
                vbas.Add(vba);
                vba = new VertexBoneAssignment();
                vba.vertexIndex = vertexIndex++;
                vba.weight = 1.0f;
                vba.boneIndex = bone.Handle;
                vbas.Add(vba);
            }

            LineRenderable lines = new LineRenderable();
            lines.SetPoints(points, colors, vbas);
            lines.MaterialName = "MVSkinnedLines";
            lines.Skeleton = skeleton;
            return lines;
        }
		///<summary>
		///  Adds an Entity to the static geometry.
		///  <remarks>
		///    This method takes an existing Entity and adds its details to the list of elements to include when building. Note that the Entity itself is not copied or referenced in this method; an Entity is passed simply so that you can change the materials of attached SubEntity objects if you want. You can add the same Entity instance multiple times with different material settings completely safely, and destroy the Entity before destroying this InstancedGeometry if you like. The Entity passed in is simply Must be called before 'Build'.
		///  </remarks>
		///</summary>
		///<param name="ent"> The Entity to use as a definition (the Mesh and Materials referenced will be recorded for the build call). </param>
		///<param name="position"> The world position at which to add this Entity </param>
		///<param name="orientation"> The world orientation at which to add this Entity </param>
		///<param name="scale"> </param>
		public virtual void AddEntity( Entity ent, Vector3 position, Quaternion orientation, Vector3 scale )
		{
			Mesh msh = ent.Mesh;

			// Validate
			if ( msh.IsLodManual )
			{
				LogManager.Instance.Write(
					"(InstancedGeometry): Manual LOD is not supported. Using only highest LOD level for mesh " + msh.Name );
			}

			//get the skeleton of the entity, if that's not already done
			if ( ent.Mesh.Skeleton != null && mBaseSkeleton == null )
			{
				mBaseSkeleton = ent.Mesh.Skeleton;
				mSkeletonInstance = new SkeletonInstance( mBaseSkeleton );
				mSkeletonInstance.Load();
				mAnimationState = ent.GetAllAnimationStates();
			}

			BoundingBox sharedWorldBounds;
			// queue this entities submeshes and choice of material
			// also build the lists of geometry to be used for the source of lods


			for ( int i = 0; i < ent.SubEntityCount; ++i )
			{
				SubEntity se = ent.GetSubEntity( i );
				var q = new QueuedSubMesh();

				// Get the geometry for this SubMesh
				q.submesh = se.SubMesh;
				q.geometryLodList = DetermineGeometry( q.submesh );
				q.materialName = se.MaterialName;
				q.orientation = orientation;
				q.position = position;
				q.scale = scale;
				q.ID = mObjectCount;
			}

			mObjectCount++;
		}