4.16 通过定义一个自定义的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(); 

代码

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

完整代码如下:

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; 
       }
   }
} 

程序截图


发布时间:2009/6/26 上午9:38:35  阅读次数:5310

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号