## DNA 库 DNA 库作为 DNACalib 的依赖项之一捆绑在本仓库中。 它提供了读取和写入 MetaHuman DNA 文件的核心功能。 它使用户能够查询和更改其中包含的信息。 DNACalib 提供了一组用于编辑 MetaHuman DNA 文件的实用命令。在底层,它使用 DNA 库。一些命令只是封装了对 DNA 库的几个调用,而其他命令则包含额外的逻辑。 虽然用户可以仅使用 DNA 库自己完成所有这些操作,但这些命令旨在让他们的工作更轻松。 ## MetaHuman DNA MetaHuman DNA 是一种文件格式,旨在存储 3D 对象的骨骼和几何体的完整描述。 仅依靠 MetaHuman DNA 文件,就可以重建对象的完整网格并使其完全装配好骨骼,准备好进行动画制作。在实践中,MetaHuman DNA 文件仅用于存储人物角色的面部。 ### 层级 MetaHuman DNA 文件中的数据分为几个逻辑层。这些层通过松散的层级结构连接在一起,其中 MetaHuman DNA 文件中的每个后续层都依赖于其上层中存储的数据。 ![MetaHuman DNA 层级](img/layers.svg "MetaHuman DNA 层级") 可以选择性地只加载 MetaHuman DNA 到指定层。如层级组织图所示,行为层和几何层彼此不依赖。这种独立性对于只需要使用 MetaHuman DNA 文件来驱动骨骼(使用行为层进行运行时评估)而不需要访问几何数据的用例至关重要。 #### 描述符 描述符层包含关于骨骼的基本元数据,例如: - 角色名称 - 年龄 - 面部原型 - 以键/值对形式的任意字符串元数据 - 所需的兼容性参数(与高级系统相关,例如用于混合 MetaHuman DNA 文件) #### 定义 定义层包含骨骼的静态数据,例如: - 控制器、关节、变形、动画贴图和网格的名称 - 关节、变形、动画贴图和网格到各个 LOD 的映射 - 关节层级 - 绑定姿态(如 T 姿态)中的关节变换 该层包含基于所选 LOD 在后续层中进行过滤所需的信息。 #### 行为 行为层包含骨骼的动态数据,用于: - 将 GUI 控制器映射到原始控制值 - 计算修正表情 - 计算关节变换 - 计算变形通道权重 - 计算动画贴图权重 #### 几何 几何层包含重建角色网格所需的所有数据,以及其蒙皮权重和变形目标增量。网格信息本身的结构类似于 OBJ 格式。 ### API 概述 在使用 MetaHuman DNA 文件时,使用的两个主要接口是: - [`BinaryStreamReader`](/dnacalib/DNACalib/include/dna/BinaryStreamReader.h) - [`BinaryStreamWriter`](/dnacalib/DNACalib/include/dna/BinaryStreamWriter.h) 它们用于从二进制流读取数据或将数据写入二进制流。例如,使用 [`FileStream`](/dnacalib/DNACalib/include/trio/streams/FileStream.h) 处理文件时。 这里我们将展示一些展示库基本用法的代码片段。 DNA 库的一般 API 概述可以在[此处](/docs/dna_api.md)找到。 与 DNACalib 一样,DNA 库是用 C++ 编写的,但有一个 Python 包装器,因此可以从 C++ 和 Python 中使用它。 #### 创建读取器/写入器 ##### 读取器 从文件读取二进制 DNA 的函数示例: ``` def load_dna(path): stream = FileStream(path, FileStream.AccessMode_Read, FileStream.OpenMode_Binary) reader = BinaryStreamReader(stream, DataLayer_All) reader.read() if not Status.isOk(): status = Status.get() raise RuntimeError(f"Error loading DNA: {status.message}") return reader ``` 创建读取器时,除了流参数外,还可以指定要加载的数据层。在此示例中,将加载所有层,因为使用了 ```DataLayer_All```,但您可以指定以下任何一项: ``` DataLayer_Descriptor DataLayer_Definition - 包括描述符和定义 DataLayer_Behavior - 包括描述符、定义和行为 DataLayer_Geometry - 包括描述符、定义和几何 DataLayer_GeometryWithoutBlendShapes - 包括描述符、定义和不含变形的几何 DataLayer_AllWithoutBlendShapes - 包括除几何中的变形之外的所有内容 DataLayer_All ``` 例如,如果您只想加载行为层(包括定义和描述符),请使用: ``` stream = FileStream(path, FileStream.AccessMode_Read, FileStream.OpenMode_Binary) reader = BinaryStreamReader(stream, DataLayer_Behavior) reader.read() if not Status.isOk(): status = Status.get() raise RuntimeError(f"Error loading DNA: {status.message}") ``` ##### 写入器 从文件写入二进制 DNA 的函数示例: ``` def save_dna(reader, path): stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) writer = BinaryStreamWriter(stream) # 使用所有数据层基于读取器创建写入器(如果没有传递参数给 setFrom(),DataLayer_All 是默认值) writer.setFrom(reader) # 例如,要仅使用几何层(包括定义和描述符)创建写入器,请使用: # writer.setFrom(reader, DataLayer_Geometry) writer.write() if not Status.isOk(): status = Status.get() raise RuntimeError(f"Error saving DNA: {status.message}") ``` 除了在创建读取器时指定层外,还可以在创建写入器时指定要使用的层(作为 ```setFrom()``` 方法的参数)。 ```load_dna``` 和 ```save_dna``` 函数在大多数[`示例`](/examples/)中都有使用。 **注意**: 还有 [`JSONStreamReader`](/dnacalib/DNACalib/include/dna/JSONStreamReader.h) 和 [`JSONStreamWriter`](/dnacalib/DNACalib/include/dna/JSONStreamWriter.h),用于处理 JSON 格式而不是二进制格式的 MetaHuman DNA 文件。但是应该注意,JSON 变体仅用于调试等目的。与二进制读取器和写入器不同,它们的 JSON 对应项不能执行过滤,并且通常会生成更大的文件。 存储 DNA 文件的推荐格式是二进制格式。 **已知问题**: 目前读取 JSON MetaHuman DNA 文件会失败。此问题将在未来版本中解决。 #### 示例 以下是使用该库的一些示例片段。 ##### 示例 1: 读取指定网格的中性顶点位置 ``` dna = load_dna(input_path) if dna.getMeshCount() == 0: print("No meshes found in DNA.") return mesh_index = 0 xs = dna.getVertexPositionXs(mesh_index) ys = dna.getVertexPositionYs(mesh_index) zs = dna.getVertexPositionZs(mesh_index) ``` ##### 示例 2: 读取中性关节坐标和关节方向值 ``` dna = load_dna(input_path) # 读取关节坐标 neutral_joint_translation_xs = dna.getNeutralJointTranslationXs() neutral_joint_translation_ys = dna.getNeutralJointTranslationYs() neutral_joint_translation_zs = dna.getNeutralJointTranslationZs() # 读取关节方向 neutral_joint_orient_xs = dna.getNeutralJointRotationXs() neutral_joint_orient_ys = dna.getNeutralJointRotationYs() neutral_joint_orient_zs = dna.getNeutralJointRotationZs() ``` ##### 示例 3: 读取表情的变形目标增量并更改它们 ``` def read_blend_shape_target_deltas(reader, mesh_index, blend_shape_target_index): """ 读取变形目标增量和相应的顶点索引。 """ vertex_indices = reader.getBlendShapeTargetVertexIndices( mesh_index, blend_shape_target_index ) blend_shape_target_delta_count = reader.getBlendShapeTargetDeltaCount( mesh_index, blend_shape_target_index ) deltas = [] for delta_index in range(blend_shape_target_delta_count): x, y, z = reader.getBlendShapeTargetDelta( mesh_index, blend_shape_target_index, delta_index ) deltas.append([x, y, z]) return vertex_indices, deltas # 读取然后更改表情 "jaw_open" 的变形目标增量,网格为 "head_lod0_mesh" input_dna = load_dna(input_path) mesh_name = "head_lod0_mesh" mesh_count = input_dna.getMeshCount() head_mesh_index = 0 for mesh_index in range(mesh_count): if input_dna.getMeshName(mesh_index) == mesh_name: head_mesh_index = mesh_index break bs_target_count = input_dna.getBlendShapeTargetCount(head_mesh_index) expr_name = "jaw_open" # 获取指定表情的变形目标索引 for i in range(bs_target_count): bs_channel_index = input_dna.getBlendShapeChannelIndex(head_mesh_index, i) bs_name = input_dna.getBlendShapeChannelName(bs_channel_index) if bs_name == expr_name: bs_target_index = i break vertex_indices, deltas = read_blend_shape_target_deltas(input_dna, head_mesh_index, bs_target_index) # 修改增量(在本例中,只是给每个增量加 1.0) for i in range(len(deltas)): deltas[i][0] += 1.0 deltas[i][1] += 1.0 deltas[i][2] += 1.0 # 从输入 DNA 创建写入器 DNA output_stream = dna.FileStream(outputPath, dna.FileStream.AccessMode_Write, dna.FileStream.OpenMode_Binary) # 在此示例中,为了调试目的,以 JSON 格式写入 DNA,以快速查看变形增量是否已更改 output_dna = dna.JSONStreamWriter(output_stream) output_dna.setFrom(input_dna) # 为表情写入新的变形增量值 output_dna.setBlendShapeTargetDeltas(mesh_index, bs_target_index, deltas) # 如果您以删除或添加某些增量的方式修改了增量, # 那么您还必须设置与新增量对应的新顶点索引: # output_dna.setBlendShapeTargetVertexIndices(mesh_index, bs_target_index, new_vertex_indices) # 写入具有修改值的 DNA output_dna.write() if not dna.Status.isOk(): status = dna.Status.get() raise RuntimeError("Error saving DNA: {}".format(status.message)) ``` ### 使用 有一些简短的[`示例`](/examples)涵盖了用户在使用 MetaHuman DNA 时可能遇到的一些情况。 其中包括: - [`将二进制 DNA 文件内容写入 JSON 格式以供检查`](/examples/dna_binary_to_json_demo.py) - [`从 DNA 中清除所有变形数据`](/examples/dnacalib_clear_blend_shapes.py) - [`从 DNA 中删除某些 LOD`](/examples/dnacalib_lod_demo.py)