为什么XNA Write / ReadObject <model>()不保留Vertex / Index Buffer数据?

时间:2015-04-24 18:08:06

标签: c# xna xna-4.0

我正在使用XNA 4.0内容管道构建一些自定义内容类型。

我的类ContentTypeWriterContentTypeReader分别有自定义TerrainModelSetContentTerrainModelSet,分别是构建时和运行时类。

地形模型集当然包括/是用于水平地形部分的模型集合,因此有一个或多个模型按顺序序列化为单个。 xnb 内容文件。

无论如何,几乎任何我可以找到的XNA文档或教程(来自微软或其他)都非常清楚XNA已经为模型提供了开箱即用的Writer和Reader。

所以我的问题是,为什么它不保留写和读之间的任何实际模型数据?它只保留了诸如BoundingSpheresPrimitiveCounts之类的无聊的额外内容 - 在顶点或索引缓冲区中没有实际几何体,它们必须保留以便远程有用。

在构建时,TerrainModelSet通过此类的Write(...)函数序列化为。 xnb 文件:

[ContentTypeWriter]
public class TerrainModelSetWriter : ContentTypeWriter<TerrainModelSetContent>
{
    protected override void Write(ContentWriter output, TerrainModelSetContent value) {
        //Write starting TerrainModelSet data...
        output.Write(value.GraphicsMeshes.Count); //value.GraphicsMeshes is a dictionary of string-keys and ModelContent-values.
        foreach (KeyValuePair<string, ModelContent> item in value.GraphicsMeshes) {
            output.Write(item.Key);
            //At this point, all geometry data is present in the Vertex and
            //Index buffers of the Model item.Value's ModelMesh's
            //ModelMeshParts, nice and neat how we would expect it. I made
            //sure if this with the debugger.
            output.WriteObject<ModelContent>(item.Value);
        }
    }
    //GetRuntimeReader(...) and GetRuntimeType(...) functions are overridden here as well.
}

TerrainModelSet当然在运行时在此相应类的Read(...)方法中反序列化:

public class TerrainModelSetReader : ContentTypeReader<TerrainModelSet>
{
    protected override TerrainModelSet Read(ContentReader input, TerrainModelSet existingInstance) {
        if (existingInstance == null)
            existingInstance = new TerrainModelSet();
        //Read starting TerrainModelSet data...
        int numItems = input.ReadInt32();
        for (int i = 0; i < numItems; i++) {
            string itemName = input.ReadString();
            Model m = input.ReadObject<Model>();
            //Here, we use the debugger again to check the state of m, and
            //find that the XNA Framework Content Pipeline has UTTERLY
            //FAILED to preserve ANY of the geomentry data.
            //All Vertex and Index buffers in any ModelMeshParts of any
            //ModelMeshes of m are null. Not even empty, just null. WTF?
            existingInstance.GraphicsMeshes.Add(itemName, m.Meshes[0]);
            existingInstance.CollisionMeshes.Add(itemName, CollisionMesh.FromModelMesh(m.Meshes[0]));
        }
    }
}

几何形状在Write调用之前就已存在 - 在适当的缓冲区中很好而且整洁。我已经使用调试器进行了检查。但是,在Read调用之后,m的网格中的所有缓冲区都为空。它们甚至都不是空的 - 只是空的。这里发生了什么?谁能开导我?

1 个答案:

答案 0 :(得分:0)

好吧,我终于发现了我的答案,它说了一些关于XNA Game Studio的丑陋。

首先,我从文档中了解到,对于任何给定类型ContentTypeWriter<T>,只能有一个ContentTypeReader<T>T。据说,为T创建第二个编写器或读取器将导致InvalidOperationException被抛出,因为管道无法决定使用哪个。这是有道理的。

这也意味着我们可以通过尝试编写一个来检查Writer或Reader是否已经存在。所以我做了。我添加了继承自以下内容的类:

  • ContentTypeWriter<Model>
  • ContentTypeReader<Model>
  • ContentTypeWriter<ModelMesh>
  • ContentTypeReader<ModelMesh>

所有内容都运行正常,表明内置的类型编写器和读取器不可用于output.WriteObject<T>(value)上的用户调用。如果我们想在我们自己的自定义/扩展内容管道中序列化这些类,我们必须自己重新编写这些编写器/读取器并重新发明轮子。我知道,非常愚蠢。

无论如何,这给我们带来了另一个问题。当我咬紧牙关并开始实现写入和读取功能时,我发现所有内置的XNA图形类 - ModelModelMeshModelMeshPartBone,等等,以及它们的构建时Content*类 - 是只读的,sealed带有internal个构造函数,这使得任何自定义用户实例化以及随后对这些类的反序列化都不可能。

该框架是PURPOSEFULLY旨在强制用户完全重写自己的图形组件框架。为什么在EARTH上有人会编写一个工具,然后使用该工具的用户应该自己重新编写工具,即使现有工具无需修改就能满足他们的需求?笨。只是愚蠢。

好的,足够的咆哮。我正在回答我的问题。我知道有问题的作家和读者存在 - 某处。毕竟,如果使用默认的XImporterModelProcessor将模型资产添加到项目而不进行任何管道自定义,(例如,我们让XNA自行序列化和反序列化我们的内容,而不使用{{1在单个文件中内联串行化多个项目)它将起作用。显然,这些作者/读者是内部的,不用于自定义操作。

我想说这是有道理的,这样用户就可以编写自己的类而不会产生冲突,除非如前所述,无论如何都是不可能的。

所以答案是这样的:我认为太愚蠢可能是真的,是真的。开箱即用的图形内容管道的唯一部分是任何实际使用的WriteObject<Model>(model)。如果您希望以任何方式自定义其功能,则所有其余部分必须由用户完全重写。

  • 您必须编写自己的XImporter课程,即使它只是内置课程的副本。
  • 您必须自己编写Model课程。
  • 您必须自己编写ModelMesh课程。
  • 您必须自己编写ModelMeshPart课程。
  • 您必须自己编写ModelContent课程。
  • 您必须自己编写ModelMeshContent课程。
  • 您必须为每个人编写自己的自定义类型编写器和读者。
  • 您必须为您可以实例化的任何内置类编写自定义Writer和Readers,例如:
    • ModelMeshPartContentTexture2DContent
    • Texture2DVertexBufferContent
    • VertexBufferIndexCollection
    • IndexBufferEffectContent / Effect
    • 等...
  • 您必须编写自己的自定义BasicEffect,将ModelProcessor的{​​{1}}输出转换为您自己的XImporter及其子内容类。
  • 我忘了什么吗?哦,是的。您必须编写自己的NodeContent函数来使用这些类。 XNA模型的内置ModelContent对你来说显然毫无用处。

截至本文发表时,我刚刚完成了这些任务,但在我测试这种方法是否有效之前还有很长的路要走。 (需要制作一些内容来测试一个,因为编写这个新系统已经使我的许多旧内容无法使用)。当(如果)我开始工作时,我会用结果编辑这个答案。

如果有人希望我在他们准备好的时候在某个地方发布最后的工作班,请发表评论。我很乐意。

PS - 在我尝试为他们编写自定义编写器/阅读器之前,框架能够完全编写/读取模型的原因是因为框架使用反射来生成编写器/阅读器对于未知类型,第一次调用Draw(...)Draw(GameTime)。但是,这仅处理属性,并丢失通过WriteObject<T>(tObj)等方法调用获得的数据。此外,ReadObject<T>()VertexBuffer.GetData(...)没有默认构造函数,因此基于反射的反序列化器无法知道如何制作它们。这就是为什么我的VertexBufferIndexBuffer都清空了(我很确定这就是为什么)。