当前位置: 首页>>代码示例>>C#>>正文


C# ContentProcessorContext.BuildAndLoadAsset方法代码示例

本文整理汇总了C#中ContentProcessorContext.BuildAndLoadAsset方法的典型用法代码示例。如果您正苦于以下问题:C# ContentProcessorContext.BuildAndLoadAsset方法的具体用法?C# ContentProcessorContext.BuildAndLoadAsset怎么用?C# ContentProcessorContext.BuildAndLoadAsset使用的例子?那么, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在ContentProcessorContext的用法示例。


在下文中一共展示了ContentProcessorContext.BuildAndLoadAsset方法的10个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C#代码示例。

示例1: LoadFragmentContent

		protected FragmentContent LoadFragmentContent(ContentProcessorContext context, string fileName, ContentIdentity relativeToContent = null)
		{
			ExternalReference<FragmentContent> externalReference = (relativeToContent != null)
				? new ExternalReference<FragmentContent>(fileName, relativeToContent)
				: new ExternalReference<FragmentContent>(fileName);
			return context.BuildAndLoadAsset<FragmentContent, FragmentContent>(externalReference, null);
		}
开发者ID:modulexcite,项目名称:stitchup,代码行数:7,代码来源:FragmentSource.cs

示例2: Import

        /// <summary>
        /// Imports the asset.
        /// </summary>
        /// <param name="context">Contains any required custom process parameters.</param>
        public void Import(ContentProcessorContext context)
        {
            if (context == null)
            throw new ArgumentNullException("context");
              if (string.IsNullOrWhiteSpace(ModelDescription.FileName))
            throw new InvalidContentException("The attribute 'File' is not set in the model description (.drmdl file).", Identity);

              var fileName = ContentHelper.FindFile(ModelDescription.FileName, Identity);
              var asset = new ExternalReference<NodeContent>(fileName);
              var node = context.BuildAndLoadAsset<NodeContent, NodeContent>(asset, null, null, ModelDescription.Importer);

              // BuildAndLoadAsset does not return root node in MonoGame.
              while (node.Parent != null)
            node = node.Parent;

              if (node.GetType() == typeof(NodeContent))
              {
            // Root node is of type NodeContent.
            // --> Copy root node content and children.
            Name = node.Name;
            Transform = node.Transform;
            Animations.AddRange(node.Animations);
            OpaqueData.AddRange(node.OpaqueData);

            var children = node.Children.ToArray();
            node.Children.Clear(); // Clear parents.
            Children.AddRange(children);
              }
              else
              {
            // Root node is a derived type.
            // --> Add node as child.
            Children.Add(node);
              }
        }
开发者ID:Zolniu,项目名称:DigitalRune,代码行数:39,代码来源:DeferredNodeContent.cs

示例3: MergeAnimation

        private static void MergeAnimation(string animationFilePath, AnimationContentDictionary animationDictionary,
                                       ContentIdentity contentIdentity, ContentProcessorContext context)
        {
            // Use content pipeline to build the asset.
              NodeContent mergeModel = context.BuildAndLoadAsset<NodeContent, NodeContent>(new ExternalReference<NodeContent>(animationFilePath), null);

              // Find the skeleton.
              BoneContent mergeRoot = MeshHelper.FindSkeleton(mergeModel);
              if (mergeRoot == null)
              {
            context.Logger.LogWarning(null, contentIdentity, "Animation model file '{0}' has no root bone. Cannot merge animations.", animationFilePath);
            return;
              }

              // Merge all animations of the skeleton root node.
              foreach (string animationName in mergeRoot.Animations.Keys)
              {
            if (animationDictionary.ContainsKey(animationName))
            {
              context.Logger.LogWarning(null, contentIdentity,
              "Replacing animation '{0}' from '{1}' with merged animation.",
              animationName, animationFilePath);

              animationDictionary[animationName] = mergeRoot.Animations[animationName];
            }
            else
            {
              context.Logger.LogImportantMessage("Merging animation '{0}' from '{1}'.", animationName, animationFilePath);

              animationDictionary.Add(animationName, mergeRoot.Animations[animationName]);
            }
              }
        }
开发者ID:HATtrick-games,项目名称:ICT309,代码行数:33,代码来源:AnimationMerger.cs

示例4: ProcessAnimations

        /// <summary>
        /// Entry point for animation processing. 
        /// </summary>
        /// <param name="model"></param>
        /// <param name="input"></param>
        /// <param name="context"></param>
        private void ProcessAnimations(ModelContent model, NodeContent input, ContentProcessorContext context, ContentIdentity sourceIdentity)
        {
            // First build a lookup table so we can determine the
            // index into the list of bones from a bone name.
            for (int i = 0; i < model.Bones.Count; i++)
            {
                bones[model.Bones[i].Name] = i;
            }

            // For saving the bone transforms
            boneTransforms = new Matrix[model.Bones.Count];

            //
            // Collect up all of the animation data
            //

            ProcessAnimationsRecursive(input);

            // Check to see if there's an animation clip definition
            // Here, we're checking for a file with the _Anims suffix.
            // So, if your model is named dude.fbx, we'd add dude_Anims.xml in the same folder
            // and the pipeline will see the file and use it to override the animations in the
            // original model file.
            string SourceModelFile = sourceIdentity.SourceFilename;
            string SourcePath = Path.GetDirectoryName(SourceModelFile);
            string AnimFilename = Path.GetFileNameWithoutExtension(SourceModelFile);
            AnimFilename += "_Anims.xml";
            string AnimPath = Path.Combine(SourcePath, AnimFilename);
            if (File.Exists(AnimPath))
            {
                // Add the filename as a dependency, so if it changes, the model is rebuilt
                context.AddDependency(AnimPath);

                // Load the animation definition from the XML file
                AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null);

                if (modelExtra.Clips.Count > 0) //if there are some animations in our model
                {
                    foreach (AnimationDefinition.ClipPart Part in AnimDef.ClipParts)
                    {
                        // Grab the main clip that we are using and copy to MainClip

                        AnimationClip MainClip = new AnimationClip();

                        float StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.OriginalFrameCount, modelExtra.Clips[AnimDef.OriginalClipName].Duration);
                        float EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.OriginalFrameCount, modelExtra.Clips[AnimDef.OriginalClipName].Duration);

                        MainClip.Duration = EndTime-StartTime;
                        MainClip.Name = modelExtra.Clips[AnimDef.OriginalClipName].Name;

                        // Process each of our new animation clip parts
                        for (int i = 0; i < modelExtra.Clips[AnimDef.OriginalClipName].Bones.Count; i++)
                        {
                            AnimationClip.Bone clipBone = new AnimationClip.Bone();
                            clipBone.Name = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Name;
                            LinkedList<AnimationClip.Keyframe> keyframes = new LinkedList<AnimationClip.Keyframe>();

                            if (modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes.Count != 0)
                            {

                                for (int j = 0; j < modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes.Count; j++)
                                {

                                    if ((modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time >= StartTime) && (modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time <= EndTime))
                                    {
                                        AnimationClip.Keyframe frame = new AnimationClip.Keyframe();
                                        frame.Rotation = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Rotation;
                                        frame.Time = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Time - StartTime;
                                        frame.Translation = modelExtra.Clips[AnimDef.OriginalClipName].Bones[i].Keyframes[j].Translation;
                                        keyframes.AddLast(frame);
                                       //clipBone.Keyframes.Add(frame);
                                    }

                                }
                            }
                           // LinearKeyframeReduction(keyframes);
                            clipBone.Keyframes = keyframes.ToList<AnimationClip.Keyframe>();
                            MainClip.Bones.Add(clipBone);

                        }
                        modelExtra.Clips.Add(Part.ClipName, MainClip);

                    }
                }
            }

            // Ensure there is always a clip, even if none is included in the FBX
            // That way we can create poses using FBX files as one-frame
            // animation clips
            if (modelExtra.Clips.Count == 0)
            {
                AnimationClip clip = new AnimationClip();
                modelExtra.Clips.Add("Take 001",clip);

//.........这里部分代码省略.........
开发者ID:patrykos91,项目名称:Laikos,代码行数:101,代码来源:AnimationProcessor.cs

示例5: GetV2DContent

        public V2DContentHolder GetV2DContent(ContentProcessorContext context)
        {
            V2DContentHolder result = new V2DContentHolder();
            result.v2dWorld = genV2d.v2dWorld;// V2DWorld.CreateFromXml(genV2d.path);

            //XmlSerializer xs = new XmlSerializer(typeof(V2DWorld));
            //StringWriter sw = new StringWriter();
            //xs.Serialize(sw, result.v2dWorld);

            result.contentTextures.Clear();
            foreach (string s in usedImages.Keys)
            {
                ExternalReference<TextureContent> tr = new ExternalReference<TextureContent>(s);
                Texture2DContent texture = context.BuildAndLoadAsset<TextureContent, Texture2DContent>(tr, null);
                result.contentTextures.Add(Path.GetFileNameWithoutExtension(s), texture);
            }

            return result;
        }
开发者ID:Hamsand,项目名称:Swf2XNA,代码行数:19,代码来源:VexTree.cs

示例6: ConvertMaterial

        // At the moment we are not displaying any animations so we can keep basic effect
        /*
        /// <summary>
        /// Changes all the materials to use our model effect.
        /// </summary>
        protected override MaterialContent ConvertMaterial(MaterialContent material,
                                                        ContentProcessorContext context)
        {
            BasicMaterialContent basicMaterial = material as BasicMaterialContent;

            if (basicMaterial == null)
            {
                throw new InvalidContentException(string.Format(
                    "AnimatedModelProcessor only supports BasicMaterialContent, " +
                    "but input mesh uses {0}.", material.GetType()));
            }

            EffectMaterialContent effectMaterial = new EffectMaterialContent();

            // Store a reference to our skinned mesh effect.
            string effectPath = Path.GetFullPath("../Content/Effects/Animated.fx");

            effectMaterial.Effect = new ExternalReference<EffectContent>(effectPath);

            // Copy texture settings from the input
            // BasicMaterialContent over to our new material.
            // Typically the Texture is named "Texture" in the model FBX file
            // so the parameter in the effect file should be named the same.
            if (basicMaterial.Texture != null)
                effectMaterial.Textures.Add("Texture", basicMaterial.Texture);

            // Chain to the base ModelProcessor converter.
            return base.ConvertMaterial(effectMaterial, context);
        }
         * */
        void MergeAnimation(NodeContent input, ContentProcessorContext context, string mergeFile)
        {
            NodeContent mergeModel = context.BuildAndLoadAsset<NodeContent, NodeContent>(
                                                new ExternalReference<NodeContent>(mergeFile), null);

            BoneContent rootBone = MeshHelper.FindSkeleton(input);

            if (rootBone == null)
            {
                context.Logger.LogWarning(null, input.Identity, "Source model has no root bone.");
                return;
            }

            BoneContent mergeRoot = MeshHelper.FindSkeleton(mergeModel);

            if (mergeRoot == null)
            {
                context.Logger.LogWarning(null, input.Identity, "Merge model '{0}' has no root bone.", mergeFile);
                return;
            }

            foreach (string animationName in mergeRoot.Animations.Keys)
            {
                if (rootBone.Animations.ContainsKey(animationName))
                {
                    context.Logger.LogWarning(null, input.Identity,
                        "Cannot merge animation '{0}' from '{1}', because this animation already exists.",
                        animationName, mergeFile);

                    continue;
                }

                context.Logger.LogImportantMessage("Merging animation '{0}' from '{1}'.", animationName, mergeFile);

                rootBone.Animations.Add(animationName, mergeRoot.Animations[animationName]);
            }
        }
开发者ID:gcode-mirror,项目名称:3d-model-prep,代码行数:72,代码来源:MergeAnimationsProcessor.cs

示例7: ProcessAnimations

        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContentDictionary
        /// object to our runtime AnimationClip format.
        /// </summary>
        static Dictionary<string, AnimationClip> ProcessAnimations(
            AnimationContentDictionary animations, IList<BoneContent> bones,
            ContentProcessorContext context, ContentIdentity sourceIdentity)
        {
            // Build up a table mapping bone names to indices.
            Dictionary<string, int> boneMap = new Dictionary<string, int>();

            for (int i = 0; i < bones.Count; i++)
            {
                string boneName = bones[i].Name;

                if (!string.IsNullOrEmpty(boneName))
                    boneMap.Add(boneName, i);
            }

            // Convert each animation in turn.
            Dictionary<string, AnimationClip> animationClips;
            animationClips = new Dictionary<string, AnimationClip>();

            foreach (KeyValuePair<string, AnimationContent> animation in animations)
            {
                AnimationClip processed = ProcessAnimation(animation.Value, boneMap, animation.Key);

                animationClips.Add(animation.Key, processed);
            }

            // Check to see if there's an animation clip definition
            // Here, we're checking for a file with the _Anims suffix.
            // So, if your model is named dude.fbx, we'd add dude_Anims.xml in the same folder
            // and the pipeline will see the file and use it to override the animations in the
            // original model file.
            string SourceModelFile = sourceIdentity.SourceFilename;
            string SourcePath = Path.GetDirectoryName(SourceModelFile);
            string AnimFilename = Path.GetFileNameWithoutExtension(SourceModelFile);
            AnimFilename += "_Anims.xml";
            string AnimPath = Path.Combine(SourcePath, AnimFilename);
            if (File.Exists(AnimPath))
            {
                context.AddDependency(AnimPath);

                AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null);

                //breaks up original animation clips into new clips
                if (animationClips.ContainsKey(AnimDef.originalClipName))
                {
                    //graps main clip
                    AnimationClip MainClip = animationClips[AnimDef.originalClipName];
                    //remove original clip from animations
                    animationClips.Remove(AnimDef.originalClipName);

                    foreach (AnimationDefinition.clipPart Part in AnimDef.ClipParts)
                    {
                        //calculate frame times
                        TimeSpan StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.originalFrameCount, MainClip.Duration.Ticks);
                        TimeSpan EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.originalFrameCount, MainClip.Duration.Ticks);

                        //get keyframes for animation clip thats in start and end time
                        List<Keyframe> Keyframes = new List<Keyframe>();
                        foreach (Keyframe AnimFrame in MainClip.Keyframes)
                        {
                            if ((AnimFrame.Time >= StartTime) && (AnimFrame.Time <= EndTime))
                            {
                                Keyframe NewFrame = new Keyframe(AnimFrame.Bone, AnimFrame.Time - StartTime, AnimFrame.Transform);
                                Keyframes.Add(NewFrame);
                            }
                        }

                        //process events
                        /*List<AnimationEvent> Events = new List<AnimationEvent>();
                        if (Part.Events != null)
                        {
                            foreach (AnimationDefinition.clipPart.Event Event in Part.Events)
                            {
                                TimeSpan EventTime = GetTimeSpanForFrame(Event.Keyframe, AnimDef.originalFrameCount, MainClip.Duration.Ticks);
                                EventTime -= StartTime;

                                AnimationEvent newEvent = new AnimationEvent();
                                newEvent.EventTime = EventTime;
                                newEvent.EventName = Event.Name;
                                Events.Add(newEvent);
                            }
                        }*/

                        AnimationClip newClip = new AnimationClip(EndTime - StartTime, Keyframes, Part.ClipName);
                        animationClips[Part.ClipName] = newClip;
                    }
                }
            }

               /* if (animationClips.Count == 0)
            {
                throw new InvalidContentException(
                            "Input file does not contain any animations.");
            }*/

            return animationClips;
//.........这里部分代码省略.........
开发者ID:unk1nd,项目名称:SkinningSample_4_0_Test,代码行数:101,代码来源:SkinnedModelProcessor.cs

示例8: GetV2DContent

        public V2DContent GetV2DContent(ContentProcessorContext context)
        {
            V2DContent result = new V2DContent();
            result.v2dWorld = v2dWorld;

            //XmlSerializer xs = new XmlSerializer(typeof(V2DWorld));
            //StringWriter sw = new StringWriter();
            //xs.Serialize(sw, result.v2dWorld);

            result.contentTextures.Clear();
            foreach (string s in paths.Values)
            {
                ExternalReference<TextureContent> tr = new ExternalReference<TextureContent>(s);
                Texture2DContent texture = context.BuildAndLoadAsset<TextureContent, Texture2DContent>(tr, null);
                result.contentTextures.Add(Path.GetFileNameWithoutExtension(s), texture);
            }

            return result;
        }
开发者ID:Hamsand,项目名称:Swf2XNA,代码行数:19,代码来源:VexToScreen.cs

示例9: ProcessAnimations

        /// <summary>
        /// Converts an intermediate format content pipeline AnimationContentDictionary
        /// object to our runtime AnimationClip format.
        /// </summary>
        static Dictionary<string, AnimationClip> ProcessAnimations(
            AnimationContentDictionary animations, IList<BoneContent> bones,
            ContentProcessorContext context, ContentIdentity sourceIdentity)
        {
            // Build up a table mapping bone names to indices.
            Dictionary<string, int> boneMap = new Dictionary<string, int>();

            for (int i = 0; i < bones.Count; i++)
            {
                string boneName = bones[i].Name;

                if (!string.IsNullOrEmpty(boneName))
                    boneMap.Add(boneName, i);
            }

            // Convert each animation in turn.
            Dictionary<string, AnimationClip> animationClips;
            animationClips = new Dictionary<string, AnimationClip>();

            // We process the original animation first, so we can use their keyframes
            foreach (KeyValuePair<string, AnimationContent> animation in animations)
            {
                AnimationClip processed = ProcessAnimation(animation.Value, boneMap, animation.Key);

                animationClips.Add(animation.Key, processed);
            }

            // Check to see if there's an animation clip definition
            // Here, we're checking for a file with the _Anims suffix.
            // So, if your model is named dude.fbx, we'd add dude_Anims.xml in the same folder
            // and the pipeline will see the file and use it to override the animations in the
            // original model file.
            string SourceModelFile = sourceIdentity.SourceFilename;
            string SourcePath = Path.GetDirectoryName(SourceModelFile);
            string AnimFilename = Path.GetFileNameWithoutExtension(SourceModelFile);
            AnimFilename += "_Anims.xml";
            string AnimPath = Path.Combine(SourcePath, AnimFilename);
            if (File.Exists(AnimPath))
            {
                // Add the filename as a dependency, so if it changes, the model is rebuilt
                context.AddDependency(AnimPath);

                // Load the animation definition from the XML file
                AnimationDefinition AnimDef = context.BuildAndLoadAsset<XmlImporter, AnimationDefinition>(new ExternalReference<XmlImporter>(AnimPath), null);

                // Break up the original animation clips into our new clips
                // First, we check if the clips contains our clip to break up
                if (animationClips.ContainsKey(AnimDef.OriginalClipName))
                {
                    // Grab the main clip that we are using
                    AnimationClip MainClip = animationClips[AnimDef.OriginalClipName];

                    // Now remove the original clip from our animations
                    animationClips.Remove(AnimDef.OriginalClipName);

                    // Process each of our new animation clip parts
                    foreach (AnimationDefinition.ClipPart Part in AnimDef.ClipParts)
                    {
                        // Calculate the frame times
                        TimeSpan StartTime = GetTimeSpanForFrame(Part.StartFrame, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks);
                        TimeSpan EndTime = GetTimeSpanForFrame(Part.EndFrame, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks);

                        // Get all the keyframes for the animation clip
                        // that fall within the start and end time
                        List<Keyframe> Keyframes = new List<Keyframe>();
                        foreach (Keyframe AnimFrame in MainClip.Keyframes)
                        {
                            if ((AnimFrame.Time >= StartTime) && (AnimFrame.Time <= EndTime))
                            {
                                Keyframe NewFrame = new Keyframe(AnimFrame.Bone, AnimFrame.Time - StartTime, AnimFrame.Transform);
                                Keyframes.Add(NewFrame);
                            }
                        }

                        // Process the events
                        List<AnimationEvent> Events = new List<AnimationEvent>();
                        if (Part.Events != null)
                        {
                            // Process each event
                            foreach (AnimationDefinition.ClipPart.Event Event in Part.Events)
                            {
                                // Get the event time within the animation
                                TimeSpan EventTime = GetTimeSpanForFrame(Event.Keyframe, AnimDef.OriginalFrameCount, MainClip.Duration.Ticks);

                                // Offset the event time so it is relative to the start of the animation
                                EventTime -= StartTime;

                                // Create the event
                                AnimationEvent NewEvent = new AnimationEvent();
                                NewEvent.EventTime = EventTime;
                                NewEvent.EventName = Event.Name;
                                Events.Add(NewEvent);
                            }
                        }

                        // Create the clip
//.........这里部分代码省略.........
开发者ID:nguoihocnghe,项目名称:chessbreaklaw,代码行数:101,代码来源:SkinnedModelProcessor.cs

示例10: MergeAnimation

        private void MergeAnimation(NodeContent input, ContentProcessorContext context, string mergeFile)
        {
            NodeContent mergeModel = context.BuildAndLoadAsset<NodeContent, NodeContent>(new ExternalReference<NodeContent>(mergeFile), null);

            // Rotate merge file
            RotateAll(mergeModel, DegreesX, DegreesY, DegreesZ);

            BoneContent rootBone = MeshHelper.FindSkeleton(input);

            if (rootBone == null)
            {
                context.Logger.LogWarning(null, input.Identity, "Source model has no root bone.");

                return;
            }

            BoneContent mergeRoot = MeshHelper.FindSkeleton(mergeModel);

            if (mergeRoot == null)
            {
                context.Logger.LogWarning(null, input.Identity, "Merge model '{0}' has no root bone.");

                return;
            }

            int preEndOfFile = mergeFile.LastIndexOf("/");
            String animationKey = "";

            context.Logger.LogImportantMessage("MERGEFILE '{0}'", mergeFile);
            context.Logger.LogImportantMessage("PREENDOFFILE '{0}'", preEndOfFile);

            if (preEndOfFile > -1)
            {
                animationKey = mergeFile.Substring(preEndOfFile + 1);

                // Find the animation name
                int endOfFile = animationKey.IndexOf(".");

                if (endOfFile > -1)
                {
                    animationKey = animationKey.Substring(0, endOfFile);
                }
            }


            foreach (string animationName in mergeRoot.Animations.Keys)
            {
                if (rootBone.Animations.ContainsKey(animationName))
                {
                    context.Logger.LogWarning(null, input.Identity, "CANNOT MERGE ANIAMTION '{0}' FROM '{1}', BECAUSE THIS ANIMATION ALREADY EXISTS.", animationName, mergeFile);

                    //continue;
                }

                context.Logger.LogImportantMessage("MERGING ANIMATION '{0}' FROM '{1}'.", animationName, mergeFile);
                context.Logger.LogImportantMessage("ANIMATION KEY '{0}'.", animationKey);

                if (!rootBone.Animations.ContainsKey(animationKey))
                {
                    rootBone.Animations.Add(animationKey, mergeRoot.Animations[animationName]);

                    context.Logger.LogImportantMessage("ANIMATION COUNT '{0}'.", rootBone.Animations.Count);

                    // Log animation merge
                    context.Logger.LogImportantMessage("ANIMATION '{0}' ADDED!", animationKey);
                }
            }

        }
开发者ID:Imortilize,项目名称:Psynergy-Engine,代码行数:69,代码来源:DeferredRendererModel.cs


注:本文中的ContentProcessorContext.BuildAndLoadAsset方法示例由纯净天空整理自Github/MSDocs等开源代码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。