介绍

本文档基于Jeroen Bakker撰写的文档 《The Bletery of the Blend》,略有编辑。本文通过逐步介绍以下主题来描述blend-file-format的基础知识:

  • 首先,我们描述Blender如何使用混合文件。

  • 我们来看一下混合文件(文件头和文件块)的全局文件结构。

  • 在解释之后,我们深入研究混合文件的核心 - DNA结构。它们保存了混合文件的蓝图和理解混合文件的关键资产。

  • 完成后,我们可以使用这些DNA结构从混合文件中的其他位置读取信息。

作为一个工作示例,我们将使用Blender 2.48中的默认混合文件,目标是从Scene中读取输出分辨率。本文被编写为独立于编程语言。

在Blender中加载和保存

在Blender中保存和加载复杂场景几秒钟内完成。Blender通过将内存中的数据保存到磁盘而无需任何转换或转换来实现此目的。Blender仅向此数据添加文件块标头

一个文件块的报头包含有关如何解释数据的线索。在数据之后,存储所有内部Blender结构。当Blender加载文件时,这些结构充当蓝图。

Blender3D FreeTip.gif混合文件格式是自我描述的
存储在不同的硬件平台或Blender版本上时,混合文件可能会有所不同。没有努力使混合文件二进制文件相同。在将混合文件加载到Blender期间,保持向后和向前兼容性。

当Blender加载一个混合文件时,它首先会创建一个DNA结构的目录(进一步了解DNA结构是什么)。然后,此目录用于确保加载数据与Blender内部结构的向后和向前兼容性。在Blender源代码中,您可以找到将Blender版本使用的每个结构转换为您正在使用的版本的逻辑,请参阅/source/blender/blenloader/intern/readfile.c。版本之间的差异越大,执行的逻辑越多。

blend-file-format与内部使用的结构没有区别,文件是自包含的和自我解释的(在您阅读本文之后)。

全局文件结构

混合文件始终以文件头开头,后跟一个或多个文件块。每个文件块都有一个文件块头数据

旁注:Blender 2.48的默认混合文件包含400多个文件块。

文件头

每个混合文件的前12个字节是文件头。文件头包含有关Blender(版本号)和保存混合文件的PC的信息(指针大小和字节序)。这是必需的,因为blend-file中的所有数据都以这种方式排序,因为在保存期间不进行转换或转换。下表描述了文件头中的信息:

参考结构体类型抵消尺寸
识别码炭[7]文件标识符(始终为’BLENDER’)07
指针大小烧焦指针的大小 文件中的所有指针都以这种格式存储 ‘_'(下划线)表示4个字节或32位 ' - ‘(减号)表示8个字节或64位。71
字节顺序(*)烧焦使用的字节排序类型 ‘v’表示小尾数 ‘V’表示大端。81
版本号炭[3]该文件创建于的Blender版本 例如'248’表示版本2.4893

(*)Endianness以字节序列的形式处理值的排序方式[ref:http//en.wikipedia.org/wiki/Endianness ]。Blender支持little-endian和big-endian。在大端序排列中,值的最大部分放在第一个字节上,值的最低部分放在最后一个字节上。在小端序排列中,值的最大部分放在最后一个字节上,值的最小部分放在第一个字节上。示例:写入整数0x4A3B2C1Dh,将以Big endian的形式排序为0x4Ah,0x3Bh,0x2Ch,0x1Dh,并以小端顺序排列为0x1Dh,0x2Ch,0x3Bh,0x4Ah。

Blender3D FreeTip.gif混合文件和您使用的PC之间的字节顺序可能不同
当它们不同时,Blender会将其更改为PC的字节顺序。如今,little-endian是最常用的。

下一个十六进制转储描述了在小端硬件上使用blender 2.48创建的文件头,其指针长度为32位:

1
2
0000 0000:[42 4C 45 4E 44 45 52] [5F] [76] [32 34 38](二进制数据)
0000 0000 BLENDER _ v 2 4 8(可读形式)

文件块

文件块包含文件块头和数据。文件块的开头始终以4字节的方式对齐。file-block-header描述数据的总长度,存储在文件块中的信息类型,此信息的项目数以及数据写入磁盘时的旧存储器指针。

根据存储在文件头中的指针大小,文件块头可以是20或24字节长。下表描述了如何构造文件块头:

参考结构体类型抵消尺寸
炭[4]文件块的标识符04
尺寸整数file-block-header之后 的数据总长度44
旧记忆地址无效*内存地址 指针指向 写入磁盘时结构所在的位置8指针大小(4/8)
SDNA指数整数SDNA结构的索引8 +指针尺寸4
计数整数位于 此文件块中 的结构数12 +指针尺寸4

代码:描述不同类型的文件块。代码确定必须读取数据的逻辑。这些代码还允许快速查找诸如库,场景,对象或材料之类的数据,因为它们都具有特定代码。

size:包含file-block-header之后的数据总长度。在数据之后,新的文件块开始。文件中的最后一个文件块的代码为“ENDB”。

旧内存地址:包含上次存储结构时的内存地址。加载文件时,结构可以放在不同的内存地址上。Blender将指向这些结构的指针更新为新的内存地址。

SDNA索引:包含读取此文件块数据时要使用的DNA结构中的索引。有关此主题的更多信息将在阅读场景信息部分中进行说明。

Count:告诉我们在数据中可以找到特定SDNA结构的元素数量

下一节是文件块头的示例。

1
2
3
4
5
6
7
           代码:size:old mem:SDNA:
0000 4420:53 43 00 00 60 05 00 00 A0 2F 04 0A 8B 00 00 00(二进制数据)
            SC。。`。。。。/。。。。。。(可读形式)
           计数:

0000 4430:01 00 00 00 xx xx xx xx xx xx xx xx xx xx xx xx(二进制数据)
            。。。。xxxxxxxxxxxx(可读形式)
  • 代码(前4个字节)‘SC'+ 0x00h表示它是一个场景。
  • 大小(接下来的4字节大端):0x60h + 0x05h * 256 = 96 + 1280 = 1376字节
  • 旧指针是:0x0A042FA0h
  • SDNA索引是:0X8B = 8 * 16 + 11 = 139。
  • count:这个例子中的1表示:该部分包含一个场景。

在我们解释这个文件块的数据之前,我们首先要阅读文件中的DNA结构。部分结构DNA将显示如何做到这一点:

结构DNA

结构DNA存储在具有代码’DNA1’的文件块中。它可以在’ENDB’文件块之前。它包含创建文件的Blender版本的所有内部结构。此文件块中的数据必须按照本节中的描述进行解释。

在使用Blender 2.48a创建的混合文件中,此部分长度为43468字节,包含309个结构。这些结构可以描述为C结构。它们可以保存字段,数组和指向其他结构的指针,就像普通的C结构一样:

1
2
3
4
5
6
7
8
结构场景{ 
    ID id; // 52个字节(ID是不同的结构)
    Object * camera; // 4个字节(指向Object结构的指针)
    World * world; // 4个字节(指向World结构的指针)
    ... 
    float cursor [3]; // 12个字节(3个浮点数组)
    ... 
}

下一节将介绍如何在’DNA1’文件块的数据中对此信息进行排序。

重复条件名称类型长度描述
识别码炭[4]4‘SDNA’
名称标识符炭[4]4‘名称’
名称
#names整数4名称数量如下
对于(#names)名称炭[]零终止的名称字符串 可能包含指针和 简单数组定义, 例如:'* vertex [3] \ 0’
类型标识符炭[4]4‘TYPE’(此字段以4个字节对齐)
类型
#Types广告整数4类型数如下
对于(#Types广告)类型炭[]零终止的字符串类型(例如’int \ 0’)
长度标识符炭[4]4‘TLEN’(此字段以4个字节对齐)
长度
对于(#Types广告)长度2类型的字节长度(例如4)
结构标识符炭[4]4‘STRC’(此字段以4个字节对齐)
结构
#structures整数4结构数量如下
对于(#structures)结构类型2包含结构名称的类型中的索引
..#fields2此结构中的字段数
..对于(#field)字段类型2索引类型
为了结束为了结束字段名称2名称索引

如您所见,结构存储在4个数组中:名称,类型,长度和结构。

每个结构还包含一个字段数组。字段是类型和名称的组合。根据这些信息,可以构建所有结构的目录。

名称存储为C-developer如何定义它们。这意味着该名称还定义了指针和数组:

  • 如果名称以’*‘开头,则将其用作指针。
  • 如果名称包含例如’[3]',则它用作长度为3的数组。

在类型中你会找到简单类型(如:‘integer’,‘char’,‘float’),还有复杂类型,如’Scene’和’MetaBall’。‘TLEN’部分描述了类型的长度。‘char’是1个字节,‘integer’是4个字节,‘Scene’是1376个字节长。

注意在阅读DNA时,你会遇到一些奇怪的名字,比如’(* doit)()'。这些是方法指针,Blender将它们更新为正确的方法。

注意字段的“类型标识符”,“长度标识符”和“结构标识符”以4个字节对齐。

Blender 2.48混合文件中的DNA结构可以在http://www.atmind.nl/blender/blender-sdna.html找到。如果我们理解文件的DNA部分,现在可以从其他部分文件块中读取信息。下一节将告诉我们如何。

阅读场景信息

让我们看一下前面看到的文件块。代码是’SC'+ 0x00h,SDNA索引是139.DNA中的第139个结构是’Scene’类型的结构。关联类型(‘Scene’)的长度为1376字节。这与文件块中的数据完全相同。我们可以将场景结构映射到文件块的数据上。但在我们做到这一点之前,我们必须平整场景结构。

Scene-structure中的第一个字段是’ID’类型,名称为’id’。在DNA结构列表中,存在为类型“ID”(结构索引17)定义的结构。此结构中的第一个字段的类型为“void”,名称为“* next”。查看结构列表,没有为“void”类型定义结构。它是一种简单类型,因此应该读取数据。'* next’描述了一个指针。数据的前4个字节可以映射到’id.next’。使用此方法,我们将结构映射到其数据。如果我们想要读取特定字段,我们知道它所在数据的偏移量以及所需的空间。

下一个表格显示了场景结构某些部分的展平过程的输出。并非所有行都在表中描述,因为Scene-structure中有很多信息。扁平SDNA结构139:场景

参考结构体类型名称抵消尺寸描述
id.nextID空虚*下一个04指的是下一个场景
id.prevID空虚*上一个44指前一个场景
id.newidIDID* NEWID84
id.libID图书馆* LIB124
id.nameID烧焦名称[24]1624‘SC'+ Blender中显示的场景名称
id.usID我们402
id.flagID422
id.icon_idIDINTicon_id444
id.propertiesIDIDProperty*性质484
相机现场宾语*相机524指向当前相机的指针
世界现场世界*世界564指向当前世界的指针
现场现场*组604指向当前集的指针
跳过的行
r.sfra的RenderDataINTSFRA2484开始场景的框架
r.efra的RenderDataINTEFRA2524场景的结束帧
跳过的行
r.xsch的RenderDataxsch3262以100%渲染时输出的X分辨率
r.ysch的RenderDataysch3282以100%渲染时输出的Y分辨率
r.xparts的RenderDataxparts3302渲染器使用的x部分数
r.yparts的RenderDatayparts3322渲染器使用的y部分数
跳过的行
sculptdata.axislockSculptData烧焦axislock13651
sculptdata.padSculptData烧焦垫[2]13662
frame_step现场INTframe_step13684
现场INT13724

我们现在可以读取场景的X和Y分辨率。X分辨率位于文件块数据的偏移326上,必须作为短路读取。Y分辨率位于偏移328并且也是短的。

注意一系列字符可能意味着两件事。该字段包含可读文本或包含标志数组(不可读)。

注意包含列表的文件块是指DNA结构并且计数大于1.例如,Vertexes和Faces以这种方式存储。

下一步

在Blender中保存的实现很容易,但加载很困难。在自定义工具中实现加载和保存混合文件时,困难恰恰相反。在自定义工具中加载混合文件很容易,并且保存混合文件很困难。如果你想保存混合文件,我建议首先了解全局文件结构并解析文件的DNA部分。完成此操作后,应该很容易从现有的混合文件中读取信息,如场景数据,材质和网格。当您对此熟悉时,可以使用特定版本的内部Blender结构开始创建混合库。如果您不想深入了解Blender源代码,可以在http://www.atmind.nl/blender/blender-sdna.html找到它们。

在Blender中有一个支持基于XML的导入/导出系统的功能请求。我不支持该请求,但看看如何实现这一点很有意思。可以轻松实现XML导出,因为XSD可以用作DNA结构,数据可以写入XML [请参阅http://www.atmind.nl/blender/blender-file.zip下载JAVA示例,包括源代码]。实现XML导入系统使用大量内存和CPU。如果你真的想要实现它,我希望最简单的方法是将XML文件转换回普通的混合文件,然后使用当前的实现加载它。一个真正的缺点是,解析基于XML的混合文件会占用大量内存和CPU,并且文件会变得非常大。

此时我正在自动渲染管道中使用此信息。渲染管道围绕Web服务器和SVN构建。当一个艺术家在SVN中提交一个新的混合文件时,它会被网络服务器拾取,它将从混合文件中提取分辨率,帧场景和库。此信息与SVN中的其他文件匹配,混合文件将放置在渲染管道中。