• 处理模型——通过定义一个自定义的TypeWriter和TypeReader将多个对象存储在Tag属性中


    问题

    要让模型处理器可以将对象存储在模型中并传递到XNA项目,XNA提供了模型的Tag属性。从教程4-13,4-14和4-15中可以看到Tag属性对于存储一个相同类型的数组是很有用的,例如Vector3的数组或Triangle的数组。但很多情况中,你想传递多个对象,例如同时传递Vector3集合和模型的包围盒。

    解决方案

    定义一个自定义类,这个类存储所有你想传递的对象。在你的模型处理器中,创建这个类对象,将要传递到XNA项目中的对象存储在这个类对象中。最后在模型的Tag属性中存储这个类对象。

    因为你定义了一个自定义类,所以你必须定义一个TypeWriter (见教程4-15)才能让XNA知道如何串行化这个对象,还要定义一个TypeReader,通过这个TypeReader从二进制文件读取这个对象。

    工作原理

    因为你要扩展内容处理器并传递自定义类对象,所以你要执行教程4-15中的“使用一个自定义类对象扩展内容处理器的步骤清单”中的初始化步骤。在第1步中,我调用了新内容管道项目TagPipeline。在第4步中我调用了处理器ExtendedModelProcessor。

    namespace TagPipeline 
    {
        [ContentProcessor] 
        public class ExtendedModelProcessor : ModelProcessor 
        {
           public override ModelContent Process(NodeContent input, ContentProcessorContext context) 
           {
               return base.Process(input, context); 
           }
       }
    } 

    完成初始化后,你就做好了定义一个可以存储你想传递到XNA项目的东西的新类的准备了。本例中,你将传递一个包含模型所有Vector3的数组和全局包围盒。

    在内容管道命名空间顶部定义这个新类:

    public class TagObject 
    {
        private Vector3[] positions; 
        private BoundingBox boundingBox; 
        
        public TagObject(Vector3[] positions, BoundingBox boundingBox) 
        {
            this.positions = positions; 
            this.boundingBox = boundingBox; 
        }
        
        public Vector3[] Positions { get { return positions; } } 
        public BoundingBox GlobalBoundingBox { get { return boundingBox; } } 
    } 

    这个简单的类可以存储一个Vector3数组和包围盒。它的构造函数将这些变量传递到内部变量中,你还定义了两个getter方法,让你可以获取变量的内容。

    注意:因为这个变量不包含行为方法,你也可以使用结构数据类型替代类。

    然后编写模型处理器代码。它可以获取你想传递的数据:Vector3数组和包围盒。

    public override ModelContent Process(NodeContent input, ContentProcessorContext context) 
    {
        ModelContent usualModel = base.Process(input, context); 
        
        List<Vector3> vertices = new List<Vector3>(); 
        
        vertices = AddVerticesToList(input, vertices); 
        
        BoundingBox bBox = BoundingBox.CreateFromPoints(vertices);   
        TagObject myTagObject = new TagObject(vertices.ToArray(), bBox); 
        
        usualModel.Tag = myTagObject; 
        return usualModel; 
    } 

    模型处理器开始的操作已经在前面的教程中做过很多次了。然后,你使用AddVerticesToList方法遍历整个模型结构将所有Vector3添加到一个集合中。

    有了这个集合之后,可以使用BoundingBox. CreateFromPoints方法从这个集合生成包围盒。这个方法以Vector3集合为参数,而这个集合具有Ienumerable接口,就好像一个数组或 List。

    通过将集合转换到数组,你就拥有了创建TagObject类对象所需的所有东西!最后,将这个类对象存储在模型的Tag属性中。

    现在己经完成了这个教程的前半部分,在第二部分,你要编写TypeWriter和TypeReader。

    编写TypeWriter和TypeReader

    现在如果你运行代码,XNA会报错,这是因为它还不知道如何将TagObject类对象存储到二进制文件,所以需要在内容管道命名空间下添加自定义TypeWriter:

    [ContentTypeWriter] 
    public class TagObjectTypeWriter : ContentTypeWriter<TagObject>
    {
        protected override void Write(ContentWriter output, TagObject value) 
        { 
            output.WriteObject<Vector3[]>(value.Positions); 
            output.WriteObject<BoundingBox>(value.GlobalBoundingBox); 
        }
        
        public override string GetRuntimeReader(TargetPlatform targetPlatform) 
        { 
            return typeof(TagObjectTypeReader).AssemblyQualifiedName; 
        }
    } 

    如教程4-15中的解释,前两行代码指定这个类是一个可以串行化TagObject对象的ContentTypeWriter。同理,你也要重写两个方法:Write方法指定一个TagObject如何被写入到二进制文件中,GetRuntimeReader方法可以被XNA调用,让程序知道到哪找到对应的TypeReader,可在教程4-15见到更多信息。

    默认内容处理器知道如何串行化Vector3数组和包围盒,所以你只需要简单地让XNA为你串行化就可以了。在GetRuntimeReader方法中,你声明在相同的命名空间中编写一个叫做TagObjectReader的对应TypeReader。

    注意:如果默认您内容管道不知道如何串行化包围盒你可以自己定义。你可以调整处理TagObjects的TypeWriter,使它将包围盒中的两个Vector3保存到二进制文件中,这样ContentReader可以重新构造这个包围盒。但是,更好的方法是编写一个额外的TypeWriter和TypeReader用来串行化/反串行化一个包围盒对象,如果使用这个方法,后面的ContentWriters会知道如何串行化包围盒对象!

    下面编写TypeReader,因为已经在GetRuntimeReader方法中定义了,所以TypeReader类必须被叫做TagObjectTypeReader:

    public class TagObjectTypeReader : ContentTypeReader<TagObject> 
    {
        protected override TagObject Read(ContentReader input, TagObject existingInstance) 
        {
            Vector3[] positions = input.ReadObject<Vector3[]>(); 
            BoundingBox bBox = input.ReadObject<BoundingBox>(); 
            
            TagObject restoredTagObject = new TagObject(positions, bBox); 
            return restoredTagObject; 
        }
    } 

    在程序启动时,每个TagObject类对象都会被串行化为一个二进制文件,而TagObjectTypeReader方法可以重建这些对象。首先你从文件中读取Vector3数组并将它存储在一个变量中;然后对包围盒进行同样的处理。有了这两个对象后,就可以重建TagObject对象并把它传递到XNA程序中。

    很简单,但有一点很重要,你必须以写入文件的同样顺序读取这些对象!如果你首先读入的是包围盒,你会基于第一个Vector3数组重建包围盒!幸运的是,XNA team对此进行了保护,如果你颠倒了读取顺序,程序会报错。

    注意:如果你的解决方案无法编译,请再看一下教程4-15中的步骤清单。

    在XNA项目中访问数据现在运行程序,所有使用ExtendedModelProcessor 的模型都会在Tag属性中包含一个TagObject对象、因为Tag属性可以包含任何东西,所以首先需要将它转换为TagObject对象。现在就可以访问它的属性了:

    myModel = Content.Load<Model>("tank"); 
    modelTransforms = new Matrix[myModel.Bones.Count]; 
    TagObject modelTag = (TagObject)myModel.Tag; 
    BoundingBox modelBBox = modelTag.GlobalBoundingBox; 
    Vector3[] modelVertices = modelTag.Positions; 
    
    System.Diagnostics.Debugger.Break(); 
    代码

    这个教程中内容管道包含下列对象:

    • ExtendedModelProcessor模型处理器,包含AddVerticesToList辅助类
    • 自定义TagObject类定义 class definition
    • 自定义TypeWriter,可以串行化TagObject类对象
    • 自定义TypeReader,可以从二进制文件读取TagObject类对象

    完整代码如下:

    namespace TagPipeline
    {
        public class TagObject 
        {
            private Vector3[] positions; 
            private BoundingBox boundingBox; 
            
            public TagObject(Vector3[] positions, BoundingBox boundingBox) 
            {
                this.positions = positions; 
                this.boundingBox = boundingBox; 
            }
            
            public Vector3[] Positions { get { return positions; } } 
            public BoundingBox GlobalBoundingBox { get { return boundingBox; } } 
        } 
        
        [ContentProcessor] 
        public class ExtendedModelProcessor : ModelProcessor 
        {
            public override ModelContent Process(NodeContent input, ContentProcessorContext	context)
            {
                ModelContent usualModel = base.Process(input, context); 
                
                List<Vector3> vertices = new List<Vector3>(); 
                vertices = AddVerticesToList(input, vertices); 
                BoundingBox bBox = BoundingBox.CreateFromPoints(vertices); 
                
                TagObject myTagObject = new TagObject(vertices.ToArray(), bBox); 
                
                usualModel.Tag = myTagObject; 
                return usualModel; 
            }
            
            private List<Vector3> AddVerticesToList(NodeContent node, List<Vector3> vertList)
            {
                MeshContent mesh = node as MeshContent; 
                
                if (mesh != null) 
                {
                    Matrix absTransform = mesh.AbsoluteTransform; 
                    foreach (GeometryContent geo in mesh.Geometry) 
                    {
                        foreach (int index in geo.Indices) 
                        {
                            Vector3 vertex = geo.Vertices.Positions[index]; 
                            Vector3 transVertex = Vector3.Transform(vertex, absTransform); 
                            vertList.Add(transVertex); 
                        }
                    }
                }
                
                foreach (NodeContent child in node.Children) 
                    vertList = AddVerticesToList(child, vertList); 
                return vertList; 
           }
       }
       
       [ContentTypeWriter] 
       public class TagObjectTypeWriter : ContentTypeWriter<TagObject>
       {
           protected override void Write(ContentWriter output, TagObject value) 
           { 
               output.WriteObject<Vector3[]>(value.Positions); 
               output.WriteObject<BoundingBox>(value.GlobalBoundingBox); 
           }
           
           public override string GetRuntimeReader(TargetPlatform targetPlatform) 
           { 
               return typeof(TagObjectTypeReader).AssemblyQualifiedName; 
           }
       }
       
       public class TagObjectTypeReader : ContentTypeReader<TagObject>
       {
           protected override TagObject Read(ContentReader input, TagObject existingInstance) 
           {
               Vector3[] positions = input.ReadObject<Vector3[]>(); 
               BoundingBox bBox = input.ReadObject<BoundingBox>(); 
               TagObject restoredTagObject 	= new TagObject(positions, bBox); 
               return restoredTagObject; 
           }
       }
    } 

    1

  • 相关阅读:
    数据库内外连接以及自然连接
    Mybatis的一级二级缓存
    彻底弄懂CAS单点登录
    Tomcat部署项目的方式
    redis集群脑裂以及解决方案
    AOP分析--代理方式的选择
    线程池
    数据结构--结构体
    Python程序--选择判断
    C语言--密码问题
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120127.html
Copyright © 2020-2023  润新知