跳到主要内容

第 1 章:快速开始

规范状态: Draft
本章范围: 本章说明 VFP 的典型工作流、VF 到 VFP 的编译关系、最小可用文件与标准加载流程。它不替代后续章节对二进制字段、区段 Payload 和校验算法的精确定义;如本章与后续字段定义冲突,以后续字段定义为准。

VFP(Voxel Format Package)是面向单体体素资产的二进制包格式。它将资产的权威体素数据、编辑加速数据、运行时渲染缓存和展示附加信息放入同一个可随机访问的容器中,使同一资产既可被准确编辑,也可被高效加载和渲染。

VFP 1.3 的根本原则是:

VOX0 是唯一权威体素源。MSH0VBUFPMSHRND0ANM0BORDPRVWTHMB 都是可删除、可重新生成的派生数据。

因此,VFP 不是仅用于预览的网格格式,也不是只方便编辑的逐体素文本格式;它是两者之间的资产交付层。


1.1 VFP 工作流

1.1.1 资产生命周期

一个 VFP 资产通常经历创作、编译、分发、加载、编辑和重新保存六个阶段:

各阶段的职责如下:

阶段输入输出主要责任
创作图片、AI 结果、VF、模型导入或编辑器内部数据原始体素描述表达“哪些体素存在、各自是什么颜色或属性”
规范化原始体素描述规范化资产数据统一坐标、颜色、空体素和边界表达
编译规范化资产数据.vfp生成权威区段、目录、校验和可选缓存
分发.vfp本地文件、资源包或网络对象保存、传输、版本管理与按需获取
加载.vfp内存中的体素资产和渲染资源验证、解析、选择可用缓存并渲染
编辑已加载资产修改后的权威数据和失效缓存只更新受影响体素、Chunk 与缓存

1.1.2 数据层次

VFP 将同一资产拆分为不同可信度和不同生命周期的数据层。实现者必须明确区分这些层,避免把缓存误当作源数据。

数据层内容主要区段可否作为资产真实性来源缺失时的处理
容器层文件版本、区段目录、偏移、长度和校验Header、DIR0、Footer无法定位资产,应拒绝文件
权威资产层尺寸、调色板、分块和体素内容METAPAL0CHIXVOX0无法正确还原资产,应拒绝文件
编辑缓存层面计划、贪心合面、局部编辑辅助数据MSH0VOX0 重建
表现缓存层顶点、索引、渲染批次、描边、动画和预览VBUFPMSHRND0ANM0BORDPRVWTHMB忽略、降级或重建

读取器可以根据自身能力选择性加载缓存。例如文件列表可优先读取 THMB,移动端渲染器可优先读取 VBUF,通用编辑器可优先读取 MSH0,不支持体素渲染的兼容工具可以读取 PRVW。但无论选择哪条性能路径,资产语义必须由权威资产层确定。

1.1.3 VFP 的典型使用场景

VFP 1.3 适合以下场景:

  • AI 将图像、文字描述或多视角输入生成体素资产后,输出可编辑、可交付的文件;
  • 用户在体素编辑器中制作角色、宠物、家具、道具或建筑组件;
  • 应用希望在首次加载后直接复用已生成的网格或 GPU 数据,减少逐体素合面开销;
  • 资源系统希望按 Chunk 局部读取、局部编辑和局部更新模型;
  • 不同平台或引擎之间需要交换原始体素资产,同时允许携带各自适用的表现缓存;
  • 内容平台希望保存可靠的资产源,并在缓存版本过期时可重新构建预览和渲染数据。

VFP 1.3 不用于直接存储大型开放世界或无限地形。它的定位是“一个可独立使用的体素资产包”。由多个资产组成的场景或世界应在 VFP 之外通过场景系统、资源索引或未来的 VWFP 格式进行组织。

1.1.4 Chunk 工作方式

VFP 将资产逻辑网格切分为多个空间 Chunk。每个 Chunk 对应一个局部体素区域,并可拥有独立的 VOX0MSH0VBUFPMSH 区段。

Chunk 的价值在于:

  • 按需加载: 读取器只读取当前需要显示或编辑的空间区域;
  • 局部编辑: 修改一个体素时,只重建所在 Chunk 及其受边界影响的邻接 Chunk;
  • 局部缓存: 不必为整个资产一次性保存或更新所有网格数据;
  • 增量存储: 写入器可以在追加式保存时只新增受影响区段与新的 Directory。

CHIX 负责描述 Chunk 的编号、坐标、尺寸和存在关系;VOX0 负责存储每个 Chunk 中的权威体素数据。Chunk 的默认推荐尺寸为 16 × 16 × 16,但最终可用尺寸、边缘 Chunk 规则和坐标映射以后续 METACHIX 章节为准。

1.1.5 编辑后的更新原则

编辑器修改资产时,应遵循“先更新权威数据,再更新缓存”的顺序:

  1. 修改目标体素的存在性、颜色索引或其他权威属性;
  2. 更新对应 VOX0 Chunk;
  3. 更新必要的 META 统计信息与 Source Hash;
  4. 标记受影响的 MSH0VBUFPMSHRND0BORD 为失效;
  5. 立即重建、延迟重建或删除这些缓存;
  6. 写入新的 Directory 与 Footer,使新版本成为文件当前有效状态。

编辑器不得只修改网格或 GPU Buffer 后保存为 VFP。这样会导致显示结果与 VOX0 不一致,并破坏其他编辑器、验证器和运行时的兼容性。


1.2 VF 到 VFP 编译流程

1.2.1 VF 与 VFP 的职责边界

VF(Voxel Format)是推荐的上游体素描述格式。VF 通常为易读、易生成、易修改的 JSON 文档,适合 AI 生成、脚本处理、创作存档和跨工具交换。VFP 则是编译后的二进制资产包,适合加载、渲染、缓存和分发。

两者关系如下:

对比项VFVFP
主要用途创作、生成、交换、调试交付、运行时加载、编辑加速、分发
常见编码JSON / UTF-8二进制容器
可读性低,需要解析器
运行时性能通常需要重新解析和编译可直接读取权威数据和可选缓存
是否必须携带缓存可选,但支持携带
是否唯一来源否,VFP 可来自其他输入否,VFP 不要求必须由 VF 生成

VF 是推荐输入,而非 VFP 的唯一合法来源。扫描、程序化生成、第三方模型导入、编辑器内部状态或网络服务输出,都可以直接编译为合规 VFP。

1.2.2 编译目标

VFP 编译器的最低目标是生成一个能被最小 VFP 读取器加载的资产包。编译输出必须包含:

  • 文件 Header;
  • META
  • PAL0
  • CHIX
  • 一个或多个 VOX0
  • DIR0
  • Footer。

编译器可按目标平台、性能档位和资产用途额外生成 SCNEMSH0VBUFPMSHRND0ANM0BORDPRVWTHMB 等区段。

1.2.3 编译步骤

推荐的编译流程如下:

各步骤的要求如下。

第一步:解析与语义校验

编译器读取输入后,必须确认至少存在以下语义信息:

  • 资产的三维体素网格尺寸;
  • 每个非空体素的位置;
  • 每个非空体素对应的颜色或可映射到颜色的值;
  • 坐标系或能够转换为 VFP 坐标系的信息。

输入中的越界体素、重复体素、非法颜色、非有限数值或无法确定尺寸的数据,必须在编译阶段被拒绝或以明确规则修正。编译器不应把不确定的解释留给 VFP 读取器。

第二步:规范化体素网格

编译器必须将输入转换到 VFP 的统一体素坐标语义:右手坐标系、单位立方体体素、原点与尺寸由资产元数据定义。体素坐标必须落在资产网格范围内。

VFP 1.3 的单轴逻辑尺寸不得超过 255。若输入超过此范围,编译器必须拒绝、拆分为多个资产,或改用不属于本规范的世界/大型资产流程;不得静默截断坐标或将其取模。

第三步:构建调色板

VFP 的 VOX0 不直接保存 RGBA,而是保存 uint8 调色板索引。编译器必须:

  1. 保留索引 0 用于空体素;
  2. 将所有非空体素映射到 1..255 的颜色索引;
  3. 生成对应的 PAL0 颜色条目;
  4. 在超过 255 种可引用颜色时,执行明确的量化、拆分或拒绝策略;
  5. 确保任何 VOX0 中出现的非零索引都在 PAL0 中有定义。

为了获得可重复输出,编译器应采用稳定的调色板顺序,例如按首次出现顺序、固定色彩空间排序或明确的输入索引排序。不能依赖哈希表遍历顺序。

第四步:切分 Chunk

编译器根据目标 chunkSize 将逻辑网格划分为 Chunk。每个 Chunk 必须拥有稳定的 Chunk ID 与空间坐标;同一规范化输入在相同参数下应产生相同的 Chunk 切分和编号。

空 Chunk 是否写入 CHIX 由后续 CHIX 章节定义。推荐做法是仅为包含非空体素或承担明确逻辑作用的 Chunk 建立条目,以降低文件体积和目录数量。

第五步:编码 VOX0

对每个输出 Chunk,编译器按规定的线性顺序将每个体素写为 Palette Index。VFP 1.3 的基本线性顺序是:X 最快变化、随后 Y、最后 Z。

编译器可选择 RAW8RLE8

  • RAW8 直接写入每个体素的 uint8 Palette Index,结构简单、随机访问友好;
  • RLE8 对连续相同索引进行游程编码,通常适合大量空体素或大色块的 Chunk。

所有 VOX0 编码选择必须由 Directory Entry 的 Codec 明确标识。编译器不得使用未声明的私有压缩方式。

第六步:生成 META、CHIX 与 Source Hash

META 应记录资产的基础信息,例如逻辑尺寸、坐标系、体素统计、Chunk 尺寸、资产标识和生成信息。CHIX 应记录每一个 Chunk 的索引信息。

Source Hash 必须在权威数据最终确定后计算。它用于确认缓存对应的是否是当前 METAPAL0CHIXVOX0。任何会改变资产真实体素内容的修改,都必须导致 Source Hash 改变。

第七步:生成可选缓存

缓存不是最小编译目标的必要条件。编译器应按产品需求选择生成:

缓存适合的编译目标
MSH0需要高频体素编辑、局部合面和选取的编辑器
VBUF需要快速 GPU 上传的特定运行时
PMSH需要向通用网格管线输出的环境
RND0有固定渲染器、批次策略或 LOD 策略的产品
BORD需要描边、轮廓或特定像素风表现的产品
PRVW需要缩略图或不支持体素渲染的兼容预览环境

每个缓存必须记录或可关联到生成它时的 Source Hash。缓存与当前 Source Hash 不匹配时,读取器必须将其视为失效。

第八步:写入容器

写入器先写 Header 和全部区段 Payload,再写 DIR0,最后写 Footer。Directory 记录所有区段的 Tag、Codec、Chunk ID、Flags、Offset、Length、Raw Length 和 CRC32。Footer 记录当前有效 Directory 的位置与长度。

写入器必须保证所有 Offset、Length、对齐、CRC32 和保留字段符合本规范。文件完成后,应使用独立验证器或等价校验逻辑回读一次,确认输出是合规 VFP。

1.2.4 编译档位

产品可定义编译档位,但所有档位均必须保留完整权威数据。

档位输出内容适合场景
Source仅最小权威区段云端归档、版本管理、后续再编译
Edit权威区段 + MSH0编辑器、局部修改、高频创作
Runtime权威区段 + 运行时所需缓存移动端、游戏、实时展示
Distribution权威区段 + 编辑/运行缓存 + PRVW跨平台分发、内容社区、离线资源包

“Runtime”或“Distribution”档位不能省略 VOX0 后只交付网格。那会将文件降级为预烘焙表现数据,失去 VFP 的可编辑和可恢复特性。


1.3 最小 VFP 文件

1.3.1 最小文件的定义

最小 VFP 文件是指:在不携带任何编辑缓存、运行时缓存或预览数据的情况下,仍能被合规最小读取器验证、解析并还原资产的 .vfp 文件。

它必须包含:

顺序要求组成部分是否必须作用
固定Header识别文件类型与基础版本
任意META解释资产尺寸、坐标与基础属性
任意PAL0定义 Palette Index 所指向的颜色
任意CHIX定义资产包含哪些 Chunk
任意VOX0是,一个或多个保存每个 Chunk 的权威体素内容
接近结尾DIR0定位以上所有区段
文件结尾Footer定位当前有效 Directory

Data Section 的物理顺序并不表达语义。上表的“任意”表示 METAPAL0CHIXVOX0 可以以任何顺序存在于数据区;读取器必须通过 Footer 和 DIR0 定位它们。

1.3.2 最小文件布局

┌─────────────────────────────────────────┐
│ Header │
├─────────────────────────────────────────┤
│ META Payload │
├─────────────────────────────────────────┤
│ PAL0 Payload │
├─────────────────────────────────────────┤
│ CHIX Payload │
├─────────────────────────────────────────┤
│ VOX0 Payload for Chunk 0 │
├─────────────────────────────────────────┤
│ VOX0 Payload for Chunk N (可选) │
├─────────────────────────────────────────┤
│ DIR0 Header + Directory Entries │
├─────────────────────────────────────────┤
│ Footer │
└─────────────────────────────────────────┘

实际文件允许在区段之间插入对齐填充,并允许存在更多 VOX0 Chunk。Footer 必须位于最后 64 字节;读取器不得通过扫描整个文件猜测 Directory 的位置。

1.3.3 最小资产示例

以下概念示例描述一个边长为 16 的单 Chunk 资产:

  • 逻辑网格尺寸:16 × 16 × 16
  • Chunk 尺寸:16 × 16 × 16
  • Chunk 数量:1
  • 调色板:索引 0 为空,索引 1 为红色,索引 2 为蓝色;
  • VOX0:保存 4096 个 Palette Index,或保存能够无损解码出该 4096 个值的 RLE8 数据;
  • 不携带 MSH0VBUFPMSHRND0PRVW
  • Directory 中恰好包含 METAPAL0CHIXVOX0 四个数据区段;
  • Footer 指向该 Directory。

这个文件虽然不含任何网格缓存,但任何最小读取器都可以:读取 VOX0、查找 PAL0、根据 METACHIX 恢复体素网格,并自行生成渲染网格。

1.3.4 最小文件的约束

最小 VFP 文件必须同时满足以下条件:

  1. Header 和 Footer 的 Magic、版本、固定长度和保留字段有效;
  2. Footer 指向一个有效 DIR0
  3. DIR0 的每个 Directory Entry 指向有效且互不重叠的 Payload 范围;
  4. METAPAL0CHIX 均恰好存在一次;
  5. 每个 CHIX 中声明的 Chunk 均恰好对应一个 VOX0
  6. 每个 VOX0 的 Chunk ID 与对应 CHIX 项目一致;
  7. 所有非零 Palette Index 都在 PAL0 中有定义;
  8. VOX0 解码后的体素数量、Chunk 尺寸和坐标范围一致;
  9. 每个区段的 CRC32 可验证;
  10. 缓存区段即使完全不存在,也不影响资产可用性。

1.3.5 最小读取器要求

要称为 VFP 1.3 最小读取器,软件至少必须能够:

  • 读取 Header、Footer 和 DIR0
  • 验证区段范围、区段 CRC32 与必需区段存在性;
  • 解析 METAPAL0CHIXVOX0
  • 支持 RAW8 编码的 VOX0
  • 将 Palette Index 0 解释为空体素;
  • 忽略它不支持的可选缓存区段;
  • 在权威数据缺失、损坏、越界或互相冲突时拒绝资产,而不是用缓存猜测结果。

最小读取器不要求支持预览图、动画、描边、GPU Buffer 或指定渲染器的派生数据。


1.4 加载流程

1.4.1 加载原则

VFP 的读取入口是 Footer,而不是 Header 后的第一个区段。读取器必须先从文件结尾定位有效 Directory,再按 Directory 按需读取数据。

这种设计使 VFP 可以支持:

  • 区段任意排序;
  • 缓存按需加载;
  • 追加式保存;
  • 旧 Directory 保留;
  • 不重写整个文件的局部更新;
  • 对未知可选区段的安全忽略。

1.4.2 标准加载步骤

合规读取器应按以下顺序执行:

步骤 1:打开文件并进行基础检查

读取器应首先取得文件实际长度,并拒绝短于 Header 加 Footer 最小长度的文件。随后读取开头固定长度的 Header,检查:

  • Magic 是否为 VFPK
  • 主版本是否受支持;
  • Header Size 是否符合当前版本;
  • 未定义的强制特性位是否被设置;
  • 保留字段是否符合规则。

Header 只用于识别文件和进行早期版本判断,不能用于定位数据区段。

读取器必须读取文件最后固定长度的 Footer,检查:

  • Magic 是否为 VFPF
  • Footer Size 是否正确;
  • Footer 中声明的文件长度是否等于实际文件长度;
  • directoryOffsetdirectoryLength 是否位于 Header 之后、Footer 之前;
  • Directory 范围计算是否发生整数溢出;
  • Footer 保留字段是否符合规则。

若文件曾经被追加式保存,文件中可能存在更早的 Directory 和 Footer;读取器只能使用最后一个 Footer 所指向的 Directory。

步骤 3:读取并验证 Directory

读取器根据 Footer 读取 DIR0,并验证:

  • DIR0 Magic、版本、Header Size 和 Entry Size;
  • Directory 总长度是否等于 Header 与 Entry 数量所推导的长度;
  • Directory 的 CRC32;
  • 每个 Entry 的 Tag、Codec、Chunk ID、Flags、Offset、Length、Raw Length 和 CRC32 字段;
  • 所有 Payload 范围是否位于数据区,且不与彼此、Header、Directory 或 Footer 交叠;
  • 必需全局区段是否存在且唯一;
  • Chunk 级区段是否具有有效的 Chunk ID。

此阶段不必立即读取所有 Payload。读取器可以只建立 Tag 与 Chunk ID 到 Directory Entry 的索引。

步骤 4:加载权威全局数据

读取器必须先加载 METAPAL0CHIX,并执行区段级 CRC32、Codec 和长度验证。

此时读取器应获得:

  • 资产的逻辑网格尺寸与坐标系统;
  • 调色板条目与其属性;
  • Chunk 尺寸、Chunk 数量、Chunk ID 与空间位置;
  • 当前 Source Hash 或计算它所需的信息。

如果三者之间存在无法解释的矛盾,例如 CHIX 声明的 Chunk 坐标超出 META 网格、Palette 数量与 VOX0 索引约束不一致,读取器必须拒绝资产。

步骤 5:按需加载 VOX0

读取器根据场景需要选择要加载的 Chunk。例如:

  • 预览器可先加载全部 Chunk;
  • 编辑器可加载当前选择区域与相邻 Chunk;
  • 流式运行时可只加载可见 Chunk;
  • 校验器必须检查所有 Chunk。

对每个 VOX0,读取器必须验证该 Entry 的 Chunk ID 存在于 CHIX,然后使用 Entry 的 Codec 解码 Payload。解码后必须检查:

  • 输出长度是否等于 rawLength
  • 体素数量是否符合该 Chunk 的实际尺寸;
  • 每个 Palette Index 是否在合法范围内;
  • 边缘 Chunk 的无效空间是否未被误解释为有效体素;
  • 线性体素顺序是否按规范解释。

读取器不得从 PMSHVBUFPRVW 反推出缺失的 VOX0。如果所需 Chunk 的 VOX0 无法验证,资产或该 Chunk 必须被报告为不可用。

步骤 6:选择缓存路径

权威数据可用后,读取器可选择最适合自身的缓存:

读取器类型优先数据路径回退路径
体素编辑器MSH0 + VOX0VOX0 重新合面
通用实时渲染器VBUF / RND0VOX0MSH0 构建网格
网格导出工具PMSHVOX0 生成面片
兼容预览器PRVW使用权威数据生成简化预览
验证器全部权威区段及可检查缓存不以缓存作为验证成功依据

在使用任何缓存之前,读取器应确认其格式版本、Chunk 关联和 Source Hash 与当前权威资产一致。缓存校验失败时,读取器应丢弃缓存并回退,而不应拒绝原本有效的权威资产。

步骤 7:建立运行时对象

完成权威数据与可选缓存加载后,读取器可建立应用内对象,例如:

  • 体素 Chunk 存储;
  • 调色板和材质表;
  • 可编辑体素选择和修改接口;
  • GPU 顶点/索引资源;
  • 默认相机、光照或姿态;
  • 预览缩略图、动画和描边表现。

这些运行时对象属于实现内部状态,不是 VFP 文件本身的额外权威语义。实现可以采用不同的数据结构,但对同一个有效 VFP 文件必须得出相同的权威体素结果。

1.4.3 错误与降级原则

加载过程中应按数据的重要性处理错误:

问题推荐行为
Header、Footer 或 Directory 无效拒绝整个文件
METAPAL0CHIX 或所需 VOX0 缺失/损坏拒绝资产或标记相应 Chunk 不可用
未知且必需的 Codec 或 Flag拒绝受影响资产
未知的可选 Tag忽略并保留兼容性提示
缓存 CRC32 错误或 Source Hash 不匹配丢弃该缓存并从权威数据重建或降级
PRVW 无法读取不影响资产加载;可不显示预览
表现特性不支持使用实现的默认表现,不改变体素内容

读取器不得为了“尽量展示”而把损坏或不匹配的缓存当作权威结果。展示错误的资产通常比明确报告不可用更危险,尤其是在编辑、同步或资产再导出流程中。

1.4.4 最小加载伪代码

function loadVfp(file):
header = readAt(file, 0, 64)
validateHeader(header)

footer = readAt(file, file.length - 64, 64)
validateFooter(footer, file.length)

directoryBytes = readAt(file, footer.directoryOffset, footer.directoryLength)
validateCrc32(directoryBytes, footer.directoryCrc32)
directory = parseDirectory(directoryBytes)
validateDirectory(directory, file.length)

meta = readAndDecodeRequired(directory, "META", -1)
palette = readAndDecodeRequired(directory, "PAL0", -1)
chunkIndex = readAndDecodeRequired(directory, "CHIX", -1)
validateAuthoritativeGlobals(meta, palette, chunkIndex)

asset = createAsset(meta, palette, chunkIndex)
for each required chunk in requestedChunks(asset):
voxels = readAndDecodeRequired(directory, "VOX0", chunk.id)
validateVoxels(voxels, chunk, palette)
asset.attachChunk(chunk.id, voxels)

cache = findValidCompatibleCache(directory, asset.sourceHash)
if cache exists:
asset.attachCache(cache)
else:
asset.rebuildCacheIfNeeded()

return asset

该伪代码刻意省略了具体字段布局、压缩实现、资源上限和错误代码;这些由后续文件结构、区段、验证器和错误处理章节规定。


本章总结

VFP 的使用方式可概括为:先将可创作、可生成的体素输入规范化;再把权威 VOX0 与必要的元数据写入 VFP;最后按目标平台附加可重建缓存。

对于所有实现,以下顺序不可改变:

权威数据决定资产内容;Source Hash 判断缓存是否仍有效;缓存只负责提升性能;Footer 和 Directory 决定数据位置。

下一章将定义文件容器、Header、Footer、Directory、Section 与数据一致性的精确二进制规则。