处理模型——通过定义一个自定义的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

你可能感兴趣的:(Writer)