znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第07章:文件 IO 与三维数据格式

Ara3D.SDK.IOnet8.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 之上的。

结构:

  1. BFastPreamble(固定头:魔数、字节数、数组个数);
  2. BFastRange[](每个数组的 64 位 Begin/End 偏移,64 字节对齐);
  3. 名称缓冲(以 null 分隔的 UTF-8 字符串);
  4. 数据缓冲(全部 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):vertexcornerfaceedgemeshsubmeshinstancematerialallnone

预定义属性示例(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.IOSharpGLTF.MemorySharpGLTF.AnimationsSharpGLTF.Transforms。支持大量 KHR/EXT 扩展(KHR_materials_*KHR_texture_transformEXT_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 导出用 GltfExportermodel.WriteGlb),导入用 SharpGLTFModelRoot.Load);
  • VIM 打包 BIM 场景(几何 + 实体表 + 字符串表);
  • STEP 解析器零拷贝、内存映射,是 IFC 属性提取基础;
  • GeoJSON/IMDF 支持 GIS 与室内地图。

下一章我们进入 SDK 最有特色的部分之一——BIM 与 IFC 数据处理,以及 BIM 开放模式(BOS)。


← 上一章 目录 下一章 →