第07章:文件 IO 与三维数据格式
Ara3D.SDK.IO(net8.0-windows)汇集了一整套三维/BIM 文件格式的读写库。它们大多零外部依赖、以高性能为目标(内存映射、非托管缓冲、零拷贝)。本章逐一介绍:BFAST、G3D、PLY、glTF(导出/导入)、VIM、STEP、GeoJSON。
平台提醒:本章库均为 Windows 目标框架,示例请在
net8.0-windows项目中运行。
1. BFAST——数组序列化二进制容器
BFAST(Binary Format for Array Serialization and Transmission)是 Ara3D 众多格式的底层容器。G3D、VIM 都是建立在 BFAST 之上的。
结构:
BFastPreamble(固定头:魔数、字节数、数组个数);BFastRange[](每个数组的 64 位 Begin/End 偏移,64 字节对齐);- 名称缓冲(以 null 分隔的 UTF-8 字符串);
- 数据缓冲(全部 64 字节对齐——利于 SIMD/AVX-512)。
魔数 0x4246415374000000(含字节序变体用于端序检测)。核心类:
| 类 | 作用 |
|---|---|
BFast(静态) |
对齐、打包、校验、写入等工具 |
BFastReader |
从内存映射文件读取 |
BFastBuilder |
增量构建以便写出 |
BFastPreamble / BFastRange |
头结构 / 偏移对 |
NamedBuffer<T> |
命名的类型化缓冲 |
读:
// 读出所有命名缓冲
BFastBuffers buffers = BFastReader.Read("file.bfast");
// 回调式(流式、内存映射)
BFastReader.Read("file.bfast", (name, view, index) =>
{
Console.WriteLine($"Buffer '{name}': {view.Length} bytes");
});
写:
var builder = new BFastBuilder();
builder.Add("positions", positionsBuffer); // IBuffer
builder.Add("indices", indicesBuffer);
builder.Write("output.bfast");
理解 BFAST 很关键:它是一个“命名字节数组的集合”。上层格式只是约定了缓冲名字和内容语义。
2. G3D——几何交换格式
G3D 是基于 BFAST 的几何交换格式。每个缓冲的名字就是一个属性描述符字符串:
g3d:<association>:<semantic>:<index>:<data_type>:<arity>
关联(association):vertex、corner、face、edge、mesh、submesh、instance、material、all、none。
预定义属性示例(CommonAttributes.cs):
g3d:vertex:position:0:float32:3 → 顶点坐标
g3d:corner:index:0:int32:1 → 面角索引
g3d:instance:transform:0:float32:16 → 实例 4×4 矩阵
g3d:instance:mesh:0:int32:1 → 实例 → 网格索引
g3d:material:color:0:float32:4 → RGBA 材质色
G3D 类把这些缓冲暴露成强类型:
public class G3D : GeometryAttributes
{
public IBuffer<Vector3> Vertices { get; }
public IBuffer<int> Indices { get; } // 面角索引
public IBuffer<Matrix4x4> InstanceTransforms { get; }
public IBuffer<int> InstanceMeshes { get; }
public IBuffer<int> MeshSubmeshOffset { get; }
public IBuffer<int> SubmeshIndexOffsets{ get; }
public IBuffer<int> SubmeshMaterials { get; }
public IBuffer<Vector4> MaterialColors { get; }
public List<G3dMesh> Meshes { get; }
public List<G3dMaterial> Materials { get; }
}
读:
G3D g3d = G3D.Read("model.g3d");
// 或从 VIM 文件的几何块读取
G3D g3d2 = G3D.ReadFromGeometryBufferIfPossible(bfastReader);
foreach (var v in g3d.Vertices) { /* ... */ }
写:构造好各 GeometryAttribute,用 BFastBuilder 把每个属性以其描述符字符串为名加入缓冲即可。
3. PLY——Stanford 网格格式
PLY 是最常见的点/网格交换格式,支持 ASCII 与二进制。
| 类 | 作用 |
|---|---|
PlyImporter(静态) |
读 PLY,返回 TriangleMesh3D 或原始缓冲 |
PlyExporter(静态) |
写 ASCII PLY |
IPlyBuffer 及其类型化子类 |
逐属性类型化缓冲 |
读:
// 高层:直接得到 TriangleMesh3D
TriangleMesh3D mesh = PlyImporter.LoadMesh("model.ply");
// 低层:得到逐属性原始缓冲
List<IPlyBuffer> buffers = PlyImporter.LoadBuffers("model.ply");
支持 ascii / binary_little_endian / binary_big_endian;属性类型覆盖 char/uchar/short/ushort/int/uint/float/double 等。
写(可带逐顶点颜色):
mesh.WritePly("output.ply"); // TriangleMesh3D 上的扩展方法
// 带颜色(每顶点 ByteRGBA)
var lines = PlyExporter.PlyStrings(mesh, colors);
File.WriteAllLines("output.ply", lines);
生成的 ASCII 头形如:
ply
format ascii 1.0
element vertex 4
property float x
property float y
property float z
element face 2
property list uchar int vertex_index
end_header
0.0 0.0 0.0
...
3 0 1 2
4. glTF 导出——GltfExporter
glTF/GLB 2.0 是 Web/实时渲染的事实标准。Ara3D.IO.GltfExporter 写二进制 .glb,依赖 Newtonsoft.Json。
| 类 | 作用 |
|---|---|
GltfBuilder |
累积网格/实例,创建 accessor/bufferView |
GltfWriter(静态) |
写出最终 GLB 二进制 |
GltfData |
根 JSON(scenes/meshes/materials/nodes/…) |
最简用法——IModel3D 一行导出:
model.WriteGlb("output.glb"); // IModel3D 上的扩展方法
手动流程:
var builder = new GltfBuilder();
builder.SetModel(model); // 转换所有网格 + 实例
var bytes = new List<byte>();
var data = builder.Build(bytes); // 填充顶点/索引二进制,创建 accessor
data.Export(bytes, "output.glb");
GLB 内部布局:Header(魔数 0x46546C67、版本 2、总长)+ JSON 块(GltfData 序列化并 4 字节对齐)+ BIN 块(float XYZ 顶点,随后 uint32 三角索引)。材质从 InstanceStruct/Material 映射到 GltfPbr(baseColorFactor 等)。
5. glTF 导入——SharpGLTF
读 glTF 用 Ara3D.IO.SharpGLTF,它 fork 自知名库 SharpGLTF(为性能移除了部分校验/守卫),程序集名仍为 SharpGLTF.Core 以保持 API 兼容。
命名空间:SharpGLTF.Schema2(主 API,入口 ModelRoot)、SharpGLTF.IO、SharpGLTF.Memory、SharpGLTF.Animations、SharpGLTF.Transforms。支持大量 KHR/EXT 扩展(KHR_materials_*、KHR_texture_transform、EXT_mesh_gpu_instancing 等)。
读:
using SharpGLTF.Schema2;
var model = ModelRoot.Load("model.gltf"); // 或 .glb
foreach (var mesh in model.LogicalMeshes)
foreach (var prim in mesh.Primitives)
{
var positions = prim.GetVertexAccessor("POSITION").AsVector3Array();
var indices = prim.GetIndexAccessor()?.AsIndicesArray();
// ... 转换成 Ara3D 的 TriangleMesh3D
}
组合技巧:用 SharpGLTF 读 glTF、用 GltfExporter 写 glTF,即可实现 glTF 的编辑/优化/重导出流水线。
6. VIM——BIM 二进制格式
VIM 是面向 AEC 的 BIM 二进制格式(BFAST 容器),把几何 + 实体表 + 字符串表打包在一起。
BFAST 缓冲结构:
| 缓冲名 | 内容 |
|---|---|
header |
版本、修订、生成器等键值对 |
strings |
全局 null 分隔 UTF-8 字符串表 |
geometry |
内嵌 BFAST,含一个 G3D 几何块 |
entities |
内嵌 BFAST,每个实体表一个子 BFAST |
assets |
命名资产字节缓冲 |
关键类:
| 类 | 作用 |
|---|---|
VimDocument |
高层:加载并解释 VIM,暴露 Elements/Categories/Nodes |
SerializableDocument |
低层:原始缓冲、字符串表、实体表、几何 |
VimSerializer(静态) |
Deserialize(path) → SerializableDocument |
VimTable / VimRow / VimColumn |
高层表/行/列访问 |
列前缀(VimConstants):int:、long:、float:、double:、byte:、string:(指向字符串表的索引)、index:(指向另一实体表的外键)。
读:
// 高层
VimDocument doc = VimDocument.Load("model.vim");
foreach (var (id, element) in doc.Elements)
Console.WriteLine($"{element.Category}/{element.Name}: {element.Parameters.Count} 参数");
// 低层
SerializableDocument raw = VimSerializer.Deserialize("model.vim");
G3D geometry = raw.Geometry; // G3D 几何块
string[] strings = raw.StringTable; // 全局字符串查找
Element 结构:
public class Element
{
public long Id { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public List<Parameter> Parameters { get; }
}
public record Parameter(string Name, string Value);
7. STEP——ISO 10303 解析器
STEP(.stp/.step,IFC 也是 STEP 物理文件)是文本型、以实体定义为基础的格式。Ara3D.IO.StepParser 用内存映射 + 非托管指针做超快、零拷贝分词。
| 类 | 作用 |
|---|---|
StepDocument |
加载并分词整个文件,产出 List<StepDefinition> |
StepDefinition |
一条实体:#id = NAME(attr1, attr2, ...) |
StepToken(unsafe struct) |
指向原始内存的 token |
StepTokenizer(静态) |
底层分词 |
StepHeader |
解析头部(FILE_DESCRIPTION/FILE_NAME/FILE_SCHEMA) |
StepToken 关键成员:
public unsafe struct StepToken
{
public readonly byte* Begin; // 指向内存映射数据
public int Length;
public StepTokenType Type { get; }
public bool IsId { get; } // 以 '#' 开头
public bool IsList { get; } // 以 '(' 开头
public bool IsString { get; }
public bool IsNumber { get; }
public double AsNumber();
public string AsString(); // 去掉外层引号
public int AsId(); // '#nnn' → int
public ReadOnlySpan<byte> Span { get; }
}
读:
using var doc = new StepDocument("file.ifc");
// doc.Header — FILE_DESCRIPTION、FILE_SCHEMA 等
foreach (var def in doc.Definitions)
{
int id = def.Id; // #1234
string name = def.Name.ToString(); // 如 "IFCWALL"
// 遍历 def 的属性 token
}
STEP 解析器是
Ara3D.IfcLoader(第 08 章)提取 IFC 属性数据的基础——几何走原生 web-ifc,属性走这个纯 C# 分词器。
8. GeoJSON 与 IMDF
Ara3D.IO.GeoJson 支持 GeoJSON(RFC 7946)及 IMDF(Indoor Mapping Data Format,室内地图)特征类型。
public abstract class GeoJsonObject { public abstract string type { get; set; } }
public abstract class GeoJsonGeometry : GeoJsonObject { }
public class GeoJsonFeature : GeoJsonObject
{
public GeoJsonGeometry? geometry { get; set; }
public Dictionary<string, object>? properties { get; set; }
public string? id { get; set; }
}
public class GeoJsonPoint : GeoJsonGeometry { public double[] coordinates; }
public class GeoJsonLineString : GeoJsonGeometry { public double[][] coordinates; }
public class GeoJsonPolygon : GeoJsonGeometry { public double[][][] coordinates; }
public class GeoJsonFeatureCollection : GeoJsonObject
{
public List<GeoJsonFeature> features { get; set; }
}
IMDF 特化(如房间单元、门洞):
public class ImdfUnit : GeoJsonFeature // 室内单元(房间/区域)
{
public const string FeatureType = "unit";
public GeoJsonPolygon geometry { get; set; }
public ImdfUnitProperties GetProperties();
}
public class ImdfOpening : GeoJsonFeature { public const string FeatureType = "opening"; /* 门/洞口 */ }
读:
// 从流
using var stream = File.OpenRead("features.geojson");
GeoJsonFeatureCollection col = stream.LoadFeatureCollection();
// 从目录(每个 .json 一个 feature,IMDF 风格)
GeoJsonFeatureCollection col2 = new DirectoryPath("imdf_dir").LoadFeatureCollection();
foreach (var f in col.features)
if (f is ImdfUnit unit)
{
var p = unit.GetProperties();
Console.WriteLine($"房间: {p.name}, 楼层: {p.level_id}");
}
序列化用 System.Text.Json + 自定义 GeoJsonFeatureConverter,根据 feature_type 反序列化成对应 IMDF 类型。
9. 选型速查
| 需求 | 用哪个 |
|---|---|
| 自定义高性能二进制数组容器 | Ara3D.IO.BFAST |
| 通用网格/实例几何交换 | Ara3D.IO.G3D |
| 点云/网格通用交换(跨软件) | Ara3D.IO.PLY |
| Web/实时渲染导出 | Ara3D.IO.GltfExporter |
| 读取/编辑 glTF | Ara3D.IO.SharpGLTF |
| BIM 场景(几何 + 实体表) | Ara3D.IO.VIM |
| IFC/STEP 文本解析 | Ara3D.IO.StepParser(+ IfcLoader 取几何) |
| GIS/室内地图 | Ara3D.IO.GeoJson |
10. 本章小结
- BFAST 是底层容器,G3D、VIM 都构建其上;
- G3D 用“描述符字符串命名缓冲”表达顶点/索引/实例/材质;
- PLY 便捷读写网格(
PlyImporter.LoadMesh/mesh.WritePly); - glTF 导出用
GltfExporter(model.WriteGlb),导入用SharpGLTF(ModelRoot.Load); - VIM 打包 BIM 场景(几何 + 实体表 + 字符串表);
- STEP 解析器零拷贝、内存映射,是 IFC 属性提取基础;
- GeoJSON/IMDF 支持 GIS 与室内地图。
下一章我们进入 SDK 最有特色的部分之一——BIM 与 IFC 数据处理,以及 BIM 开放模式(BOS)。