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();
代码
这个教程中内容管道包含下列对象:
- 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;
}
}
}

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