为什么 XNA Write/ReadObject<Model>() 不保留 Vertex/Index 缓冲区数据?
Why is XNA Write/ReadObject<Model>() not preserving Vertex/Index Buffer data?
我正在使用 XNA 4.0 内容管道构建一些自定义内容类型。
我的 classes TerrainModelSetContent
和 TerrainModelSet
分别有自定义的 ContentTypeWriter
和 ContentTypeReader
,分别是构建时间和 运行 时间 classes.
当然是地形模型集 includes/is 用于关卡地形部分的模型集合,因此有一个或多个模型按顺序序列化为单个 .xnb 内容文件。
无论如何,几乎所有我能找到的 XNA 文档或教程(来自 Microsoft 或其他地方)都清楚地表明 XNA 已经带有开箱即用的 Writer 和 Reader对于模型。
所以我的问题是,为什么它不保留写入和读取之间的任何实际模型数据?它只保留顶点或索引缓冲区中实际几何体的 BoundingSpheres
和 PrimitiveCounts
--none 等琐碎的额外内容,为了远程使用,它必须保留这些内容.
在构建时,TerrainModelSet
通过此 class 的 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
当然在 运行 时间反序列化在 class:
的 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 的网格中的所有缓冲区都为空。它们甚至不是空的——只是空的。这里发生了什么?谁能赐教一下?
好吧,我终于找到了我的答案,它说了一些关于 XNA Game Studio 的丑闻。
首先,我从文档中了解到任何给定类型 T
只能有一个 ContentTypeWriter<T>
或 ContentTypeReader<T>
。据推测,为 T
创建第二个编写器或 reader 将导致 InvalidOperationException
被抛出,因为管道无法决定使用哪个。这是有道理的。
这也意味着我们可以通过尝试写入一个来检查 Writer 或 Reader 是否已经存在。所以我做了。我添加了继承自以下内容的 classes:
ContentTypeWriter<Model>
ContentTypeReader<Model>
ContentTypeWriter<ModelMesh>
ContentTypeReader<ModelMesh>
一切 运行 都没有错误,表明内置类型编写器和 Reader 对 output.WriteObject<T>(value)
上的用户调用不可用。如果我们想在我们自己的custom/extended内容管道中序列化这些class,我们必须自己重新编写那些writers/readers并重新发明轮子。很蠢,我知道。
无论如何,这给我们带来了另一个问题。当我咬紧牙关开始实现写入和读取功能时,我发现所有内置的 XNA 图形 classes--Model
、ModelMesh
、ModelMeshPart
、Bone
,等等,以及它们的构建时间 Content*
classes-- 是只读的并且 sealed
带有 internal
构造函数,使任何自定义用户实例化和后续这些 class 的反序列化是不可能的。
该框架的设计目的是强制用户完全重写他们自己的图形组件框架。为什么在 EARTH 上有人会编写一个工具,然后使该工具的用户应该自己重新编写该工具,即使现有工具无需修改即可满足他们的需求?愚蠢的。只是愚蠢。
好的,够了运行。我正在引导我的答案。我知道有问题的 Writers 和 Readers 存在于某个地方。毕竟,如果您使用默认 XImporter
和 ModelProcessor
向项目添加模型资产而不进行任何管道自定义,(例如,我们让 XNA 自行序列化和反序列化我们的内容而不使用 WriteObject<Model>(model)
在单个文件中将多个项目一起内联序列化)它会起作用。显然,这些 writers/readers 是内部的,不用于自定义操作。
我会说这是有道理的,这样用户就可以编写自己的 classes 而不会产生冲突,除了如前所述,无论如何都不可能做到这一点。
所以答案是这样的:我认为愚蠢到不可能是真的,是真的。开箱即用的图形内容管道中唯一有任何实际用途的部分是 XImporter
。如果您想以任何方式自定义其功能,则所有其余部分都必须由用户完全重写。
- 您必须编写自己的
Model
class,即使它只是内置的副本。
- 你必须自己写
ModelMesh
class.
- 你必须自己写
ModelMeshPart
class.
- 你必须自己写
ModelContent
class.
- 你必须自己写
ModelMeshContent
class.
- 你必须自己写
ModelMeshPartContent
class.
- 您必须编写自己的自定义类型编写器和 Reader 每个。
- 您还必须为任何您正在使用的可以实例化的内置 classes 编写自定义编写器和 Readers,例如:
Texture2DContent
和 Texture2D
VertexBufferContent
和 VertexBuffer
IndexCollection
和 IndexBuffer
EffectContent
和 Effect
/BasicEffect
- 等...
- 您必须编写自己的自定义
ModelProcessor
以将 XImporter
的 NodeContent
输出转换为您自己的 ModelContent
及其子内容 class es.
- 我是不是忘记了什么?哦是的。您必须编写自己的
Draw(...)
函数才能使用这些 classes。 XNA 模型的内置 Draw(GameTime)
显然对您没用。
截至撰写本 post 时,我已经差不多完成了这些任务,但在我测试此方法是否有效之前还有很长的路要走。 (需要制作一些内容来测试一个,因为编写这个新系统使我的很多旧内容无法使用)。当(如果)我让它工作时,我将用结果编辑这个答案。
如果有人希望我 post 最终工作 classes 在某处作为教程,当他们准备好时,请发表评论。我会非常乐意。
P.S. -- 在我尝试编写自定义 writers/readers 之前框架能够 write/read 模型的原因它们,是因为框架在第一次调用 WriteObject<T>(tObj)
或 ReadObject<T>()
时使用反射为未知类型生成 writer/reader。然而,这只会处理属性,并且会丢失通过 VertexBuffer.GetData(...)
等方法调用获得的数据。此外,VertexBuffer
和 IndexBuffer
没有默认构造函数,这使得基于反射的反序列化器无法知道如何创建它们。这就是为什么我的 VertexBuffers
和 IndexBuffers
都是空的(我很确定这就是原因)。
我正在使用 XNA 4.0 内容管道构建一些自定义内容类型。
我的 classes TerrainModelSetContent
和 TerrainModelSet
分别有自定义的 ContentTypeWriter
和 ContentTypeReader
,分别是构建时间和 运行 时间 classes.
当然是地形模型集 includes/is 用于关卡地形部分的模型集合,因此有一个或多个模型按顺序序列化为单个 .xnb 内容文件。
无论如何,几乎所有我能找到的 XNA 文档或教程(来自 Microsoft 或其他地方)都清楚地表明 XNA 已经带有开箱即用的 Writer 和 Reader对于模型。
所以我的问题是,为什么它不保留写入和读取之间的任何实际模型数据?它只保留顶点或索引缓冲区中实际几何体的 BoundingSpheres
和 PrimitiveCounts
--none 等琐碎的额外内容,为了远程使用,它必须保留这些内容.
在构建时,TerrainModelSet
通过此 class 的 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
当然在 运行 时间反序列化在 class:
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 的网格中的所有缓冲区都为空。它们甚至不是空的——只是空的。这里发生了什么?谁能赐教一下?
好吧,我终于找到了我的答案,它说了一些关于 XNA Game Studio 的丑闻。
首先,我从文档中了解到任何给定类型 T
只能有一个 ContentTypeWriter<T>
或 ContentTypeReader<T>
。据推测,为 T
创建第二个编写器或 reader 将导致 InvalidOperationException
被抛出,因为管道无法决定使用哪个。这是有道理的。
这也意味着我们可以通过尝试写入一个来检查 Writer 或 Reader 是否已经存在。所以我做了。我添加了继承自以下内容的 classes:
ContentTypeWriter<Model>
ContentTypeReader<Model>
ContentTypeWriter<ModelMesh>
ContentTypeReader<ModelMesh>
一切 运行 都没有错误,表明内置类型编写器和 Reader 对 output.WriteObject<T>(value)
上的用户调用不可用。如果我们想在我们自己的custom/extended内容管道中序列化这些class,我们必须自己重新编写那些writers/readers并重新发明轮子。很蠢,我知道。
无论如何,这给我们带来了另一个问题。当我咬紧牙关开始实现写入和读取功能时,我发现所有内置的 XNA 图形 classes--Model
、ModelMesh
、ModelMeshPart
、Bone
,等等,以及它们的构建时间 Content*
classes-- 是只读的并且 sealed
带有 internal
构造函数,使任何自定义用户实例化和后续这些 class 的反序列化是不可能的。
该框架的设计目的是强制用户完全重写他们自己的图形组件框架。为什么在 EARTH 上有人会编写一个工具,然后使该工具的用户应该自己重新编写该工具,即使现有工具无需修改即可满足他们的需求?愚蠢的。只是愚蠢。
好的,够了运行。我正在引导我的答案。我知道有问题的 Writers 和 Readers 存在于某个地方。毕竟,如果您使用默认 XImporter
和 ModelProcessor
向项目添加模型资产而不进行任何管道自定义,(例如,我们让 XNA 自行序列化和反序列化我们的内容而不使用 WriteObject<Model>(model)
在单个文件中将多个项目一起内联序列化)它会起作用。显然,这些 writers/readers 是内部的,不用于自定义操作。
我会说这是有道理的,这样用户就可以编写自己的 classes 而不会产生冲突,除了如前所述,无论如何都不可能做到这一点。
所以答案是这样的:我认为愚蠢到不可能是真的,是真的。开箱即用的图形内容管道中唯一有任何实际用途的部分是 XImporter
。如果您想以任何方式自定义其功能,则所有其余部分都必须由用户完全重写。
- 您必须编写自己的
Model
class,即使它只是内置的副本。 - 你必须自己写
ModelMesh
class. - 你必须自己写
ModelMeshPart
class. - 你必须自己写
ModelContent
class. - 你必须自己写
ModelMeshContent
class. - 你必须自己写
ModelMeshPartContent
class. - 您必须编写自己的自定义类型编写器和 Reader 每个。
- 您还必须为任何您正在使用的可以实例化的内置 classes 编写自定义编写器和 Readers,例如:
Texture2DContent
和Texture2D
VertexBufferContent
和VertexBuffer
IndexCollection
和IndexBuffer
EffectContent
和Effect
/BasicEffect
- 等...
- 您必须编写自己的自定义
ModelProcessor
以将XImporter
的NodeContent
输出转换为您自己的ModelContent
及其子内容 class es. - 我是不是忘记了什么?哦是的。您必须编写自己的
Draw(...)
函数才能使用这些 classes。 XNA 模型的内置Draw(GameTime)
显然对您没用。
截至撰写本 post 时,我已经差不多完成了这些任务,但在我测试此方法是否有效之前还有很长的路要走。 (需要制作一些内容来测试一个,因为编写这个新系统使我的很多旧内容无法使用)。当(如果)我让它工作时,我将用结果编辑这个答案。
如果有人希望我 post 最终工作 classes 在某处作为教程,当他们准备好时,请发表评论。我会非常乐意。
P.S. -- 在我尝试编写自定义 writers/readers 之前框架能够 write/read 模型的原因它们,是因为框架在第一次调用 WriteObject<T>(tObj)
或 ReadObject<T>()
时使用反射为未知类型生成 writer/reader。然而,这只会处理属性,并且会丢失通过 VertexBuffer.GetData(...)
等方法调用获得的数据。此外,VertexBuffer
和 IndexBuffer
没有默认构造函数,这使得基于反射的反序列化器无法知道如何创建它们。这就是为什么我的 VertexBuffers
和 IndexBuffers
都是空的(我很确定这就是原因)。