diff --git a/docs/dna.md b/docs/dna.md deleted file mode 100644 index 03504dc..0000000 --- a/docs/dna.md +++ /dev/null @@ -1,253 +0,0 @@ -## 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) diff --git a/docs/dna_api.md b/docs/dna_api.md deleted file mode 100644 index 1b35607..0000000 --- a/docs/dna_api.md +++ /dev/null @@ -1,564 +0,0 @@ -# DNA API 概述 - -以下是用于读取和写入 DNA 文件的主要方法概述。 -以下文档适用于 C++。目前尚无 Python 文档。 - -如[此处](/docs/dna.md#api-overview)所述,有一些类用于从流中读取 DNA 或将其写入流。这些类包括: -- [BinaryStreamReader](/dnacalib/DNACalib/include/dna/BinaryStreamReader.h) -- [BinaryStreamWriter](/dnacalib/DNACalib/include/dna/BinaryStreamWriter.h) -- [JSONStreamReader](/dnacalib/DNACalib/include/dna/JSONStreamReader.h) -- [JSONStreamWriter](/dnacalib/DNACalib/include/dna/JSONStreamWriter.h) - -创建读取器后,可以用它来查询 DNA 中包含的不同信息。 -创建写入器后,可以用它来在 DNA 中设置新值。 - -这是通过本页列出的方法完成的。这些方法根据 DNA 文件中的[层级](/docs/dna.md#layers)进行分组。 - -**注意**: 本页并未列出所有可用方法。有关此处列出的方法的更多详细信息以及所有可用方法的列表,请参阅相应的读取器和/或写入器[文档](/dnacalib/DNACalib/include/dna/layers)。 - -## BinaryStreamReader -包含用于创建和销毁 [BinaryStreamReader](/dnacalib/DNACalib/include/dna/BinaryStreamReader.h) 的方法。 -创建 BinaryStreamReader 时,用户可以通过指定要加载的 LOD 来过滤 DNA 文件中的数据。如[此处](/docs/dna.md#reader)所述,还可以通过指定要加载的数据层来进行过滤。 - -- `create(stream, layer = DataLayer::All, maxLOD = 0u, memRes = nullptr)` - BinaryStreamReader 的工厂方法。 - 参数: - `stream` - 将从中读取数据的源流。 - `layer` - 指定需要加载的数据层。 - `maxLOD` - 要加载的最大细节级别。值为零表示加载所有 LOD。 - `memRes` - 用于分配的内存资源。如果未提供内存资源,将使用默认分配机制。用户负责通过调用 destroy 释放返回的指针。 - -- `create(stream, layer, maxLOD, minLOD, memRes = nullptr)` - BinaryStreamReader 的工厂方法。 - 参数: - `stream` - 将从中读取数据的源流。 - `layer` - 指定需要加载的数据层。 - `maxLOD` - 要加载的最大细节级别。 - `minLOD` - 要加载的最小细节级别。maxLOD / minLOD 的范围为 [0, LOD 总数 - 1] 表示加载所有 LOD。 - `memRes` - 用于分配的内存资源。如果未提供内存资源,将使用默认分配机制。用户负责通过调用 destroy 释放返回的指针。 - -- `create(stream, layer, lods, lodCount, memRes = nullptr)` - BinaryStreamReader 的工厂方法。 - 参数: - `stream` - 将从中读取数据的源流。 - `layer` - 指定需要加载的数据层。 - `lods` - 指定要加载的具体 LOD 的数组。 - `lodCount` - lods 数组中的元素数量。 - `memRes` - 用于分配的内存资源。如果未提供内存资源,将使用默认分配机制。用户负责通过调用 destroy 释放返回的指针。 - -- `destroy(instance)` - 用于释放 BinaryStreamReader 实例的方法。 - 参数: - `instance` - 要释放的 BinaryStreamReader 实例。 - -## BinaryStreamWriter -- `create(stream, memRes = nullptr)` - BinaryStreamWriter 的工厂方法。 - 参数: - `stream` - 将向其写入数据的流。 - `memRes` - 用于分配的内存资源。如果未提供内存资源,将使用默认分配机制。用户负责通过调用 destroy 释放返回的指针。 - -- `destroy(BinaryStreamWriter* instance)` - 用于释放 BinaryStreamWriter 实例的方法。 - 参数: - `instance` - 要释放的 BinaryStreamWriter 实例。 - -## JSONStreamReader -- `create(stream, memRes = nullptr)` - JSONStreamReader 的工厂方法。 - 参数: - `stream` - 将从中读取数据的源流。 - `memRes` - 用于分配的内存资源。如果未提供内存资源,将使用默认分配机制。用户负责通过调用 destroy 释放返回的指针。 - -- `destroy(instance)` - 用于释放 JSONStreamReader 实例的方法。 - 参数: - `instance` - 要释放的 JSONStreamReader 实例。 - -## JSONStreamWriter -- `create(stream, indentWidth = 4u, memRes = nullptr)` - JSONStreamWriter 的工厂方法。 - 参数: - `stream` - 将向其写入数据的流。 - `indentWidth` - 用于缩进的空格数。 - `memRes` - 用于分配的内存资源。如果未提供内存资源,将使用默认分配机制。用户负责通过调用 destroy 释放返回的指针。 - -- `destroy(instance)` - 用于释放 JSONStreamWriter 实例的方法。 - 参数: - `instance` - 要释放的 JSONStreamWriter 实例。 - -## setFrom() 方法 -除了创建写入器的方法外,BinaryStreamReader 和 JSONStreamReader 都有一个从 [Writer](/dnacalib/DNACalib/include/dna/Writer.h) 继承的 setFrom() 方法,用于使用读取器中的数据初始化写入器。 -使用此方法时,用于初始化写入器的数据可以按数据层进行过滤。 -- `setFrom(source, layer = DataLayer::All, memRes = nullptr)` - 从给定的读取器初始化写入器。 - 此函数通过调用读取器的每个 getter 函数并将返回值传递给写入器中的匹配 setter 函数,将所有数据从给定的读取器复制到写入器实例中。 - 参数: - `source` - 需要从中复制数据的源 DNA 读取器。 - `layer` - 限制应从给定源读取器接管哪些层。 - `memRes` - 用于复制期间临时分配的可选内存资源。 - -## 读取器方法 -### DescriptorReader -包含用于访问角色和骨骼各种元数据的只读访问器。 - -- `getName()` - 角色名称。 -- `getArchetype()` - 角色原型。 -- `getGender()` - 角色性别。 -- `getAge()` - 角色年龄。 -- `getTranslationUnit()` - 使用的平移单位(厘米或米)。 -- `getRotationUnit()` - 使用的旋转单位(度或弧度)。 -- `getCoordinateSystem()` - 使用的坐标系统(x、y 和 z 轴的方向)。 -- `getLODCount()` - 可用的细节级别(例如 6 表示以下级别可用:[0,1,2,3,4,5],其中 0 是细节最高的 LOD,5 是细节最低的 LOD)。 -- `getDBMaxLOD()` - 此角色的 DNA 数据中存储的最大细节级别。该值相对于数据库中的 LOD-0。 -- `getDBComplexity()` - 用于驱动此角色骨骼的输入控制接口的名称。此参数表示角色的输入控制复杂度。 -- `getDBName()` - 角色源自的数据库的名称。来自同一数据库的所有角色必须具有相同的定义,但可能具有不同的复杂度或 LOD。 - -### DefinitionReader -包含用于表示骨骼静态数据的 DNA 属性的只读访问器。 - -- `getGUIControlCount()` - GUI 控制器的数量。 -- `getGUIControlName(index)` - 请求的 GUI 控制器的名称。 -- `getRawControlCount()` - 原始控制器的数量。 -- `getRawControlName(index)` - 请求的原始控制器的名称。 -- `getJointCount()` - 关节的数量。 -- `getJointName(index)` - 请求的关节的名称。 -- `getJointIndicesForLOD(lod)` - 指定 LOD 的关节索引列表。 -- `getJointParentIndex(index)` - 请求的关节父级的索引。 - 可以使用此函数遍历和重建关节层级。示例: - 关节名称: [A, B, C, D, E, F, G, H, I] - 层级结构: [0, 0, 0, 1, 1, 4, 2, 6, 2] - 描述了以下层级结构: - ``` - A - ├── B - │ ├── D - │ └── E - │ └── F - └── C - ├── G - │ └── H - └── I - ``` - 请求关节 5(关节名称:F)的父级索引将返回 4(关节名称:E)。 - 请求根关节的父级索引:0(关节名称:A)将返回相同的索引 0。 -- `getBlendShapeChannelCount()` - 变形通道的数量。 -- `getBlendShapeChannelName(index)` - 请求的变形通道的名称。 -- `getBlendShapeChannelIndicesForLOD(lod)` - 指定 LOD 的变形通道索引列表。 -- `getAnimatedMapCount()` - 动画贴图的数量。 -- `getAnimatedMapName(index)` - 请求的动画贴图的名称。 -- `getAnimatedMapIndicesForLOD(lod)` - 指定 LOD 的动画贴图索引列表。 -- `getMeshCount()` - 网格的数量。 -- `getMeshName(index)` - 请求的网格的名称。 -- `getMeshIndicesForLOD(lod)` - 指定 LOD 的网格索引列表。 -- `getMeshBlendShapeChannelMappingCount()` - 网格-变形通道映射项的数量。 -- `getMeshBlendShapeChannelMapping(index)` - 保存指定映射索引的网格索引和相关变形通道索引的结构。 -- `getMeshBlendShapeChannelMappingIndicesForLOD(lod)` - 指定 LOD 的网格-变形通道映射索引列表。 -- `getNeutralJointTranslation(index)` - 绑定姿态下关节的平移值(x, y, z)。 -- `getNeutralJointTranslationXs()` - 绑定姿态下所有关节的 X 轴平移值列表。 -- `getNeutralJointTranslationYs()` - 绑定姿态下所有关节的 Y 轴平移值列表。 -- `getNeutralJointTranslationZs()` - 绑定姿态下所有关节的 Z 轴平移值列表。 -- `getNeutralJointRotation(index)` - 绑定姿态下关节的旋转值(x, y, z)。 -- `getNeutralJointRotationXs()` - 绑定姿态下所有关节的 X 轴旋转值列表。 -- `getNeutralJointRotationYs()` - 绑定姿态下所有关节的 Y 轴旋转值列表。 -- `getNeutralJointRotationZs()` - 绑定姿态下所有关节的 Z 轴旋转值列表。 - -### BehaviorReader -包含用于定义骨骼评估的 DNA 属性的只读访问器。 - -- `getGUIToRawInputIndices()` - 用于将 GUI 映射到原始控制器的输入索引。 -- `getGUIToRawOutputIndices()` - 用于将 GUI 映射到原始控制器的输出索引。 -- `getGUIToRawFromValues()` - 在 GUI 到原始控制器映射期间用于决定是否应评估特定条目的过滤值(下限)。 -- `getGUIToRawToValues()` - 在 GUI 到原始控制器映射期间用于决定是否应评估特定条目的过滤值(上限)。 -- `getGUIToRawSlopeValues()` - 在 GUI 到原始控制器映射期间用于计算输出值的计算值(斜率/梯度)。 -- `getGUIToRawCutValues()` - 在 GUI 到原始控制器映射期间用于计算输出值的计算值(垂直截距)。 -- `getPSDCount()` - 不同 PSD 表情的数量。 -- `getPSDRowIndices()` - PSD(输入)索引。 -- `getPSDColumnIndices()` - 控制器(输入)索引。 -- `getPSDValues()` - 与每个 PSD 行和列对相关联的权重。 -- `getJointRowCount()` - 完整未压缩关节矩阵中的行数。 -- `getJointColumnCount()` - 完整未压缩关节矩阵中的列数。 -- `getJointVariableAttributeIndices(lod)` - 请求的 LOD 的关节属性索引(输出索引)。 -- `getJointGroupCount()` - 整个关节矩阵中存在的关节组数量。 -- `getJointGroupLODs(jointGroupIndex)` - 请求的关节组中每个细节级别的行数。 - 每个元素的位置代表级别本身,而值表示属于该级别的关节组内的行数。例如: - ``` - [12, 9, 3] - │ │ └── LOD-2 包含前 3 行 - │ └── LOD-1 包含前 9 行 - └── LOD-0 包含前 12 行 - ``` -- `getJointGroupInputIndices(jointGroupIndex)` - 请求的关节组包含的列索引。这些列索引指向完整的未压缩关节矩阵。 -- `getJointGroupOutputIndices(jointGroupIndex)` - 请求的关节组包含的行索引。这些行索引指向完整的未压缩关节矩阵。 -- `getJointGroupValues(jointGroupIndex)` - 请求的关节组包含的值。 -- `getJointGroupJointIndices(jointGroupIndex)` - 请求的关节组包含的关节索引。 -- `getBlendShapeChannelLODs()` - 变形通道每个细节级别的输入索引数量。 - 每个元素的位置代表级别本身(例如 [0,1,2,3,4,5] 值 0 是最高细节的 LOD,值 5 是最低细节的 LOD),而值表示属于该级别的输入索引数量。 -- `getBlendShapeChannelInputIndices()` - 用于索引输入向量的输入索引。 -- `getBlendShapeChannelOutputIndices()` - 指定变形通道输出值位置的输出索引。 -- `getAnimatedMapLODs()` - 动画贴图每个细节级别的行数。 - 每个元素的位置代表级别本身(例如 [0,1,2,3,4,5] 值 0 是最高细节的 LOD,值 5 是最低细节的 LOD),而值表示属于该级别的行数(在条件表中)。 -- `getAnimatedMapInputIndices()` - 用于索引输入值数组的输入索引。 -- `getAnimatedMapOutputIndices()` - 指定计算输出值位置的输出索引。 -- `getAnimatedMapFromValues()` - 用于决定是否应评估特定条目的过滤值(下限)。 -- `getAnimatedMapToValues()` - 用于决定是否应评估特定条目的过滤值(上限)。 -- `getAnimatedMapSlopeValues()` - 用于计算输出值的计算值(斜率/梯度)。 -- `getAnimatedMapCutValues()` - 用于计算输出值的计算值(垂直截距)。 - -### GeometryReader -包含与骨骼相关的几何数据的只读访问器。 - -- `getVertexPositionCount(meshIndex)` - 整个网格中顶点位置的数量。 -- `getVertexPosition(meshIndex, vertexIndex)` - 指定网格中指定顶点的位置。顶点按顶点 ID 排序。 -- `getVertexPositionXs(meshIndex)` - 引用网格的所有顶点位置 X 值列表。 -- `getVertexPositionYs(meshIndex)` - 引用网格的所有顶点位置 Y 值列表。 -- `getVertexPositionZs(meshIndex)` - 引用网格的所有顶点位置 Z 值列表。 -- `getVertexTextureCoordinateCount(meshIndex)` - 整个网格中纹理坐标的数量。 -- `getVertexTextureCoordinate(meshIndex, textureCoordinateIndex)` - 指定网格中指定索引的纹理坐标。 -- `getVertexTextureCoordinateUs(meshIndex)` - 引用网格的所有纹理坐标 U 值列表。 -- `getVertexTextureCoordinateVs(meshIndex)` - 引用网格的所有纹理坐标 V 值列表。 -- `getVertexNormalCount(meshIndex)` - 整个网格中顶点法线的数量。 -- `getVertexNormal(meshIndex, normalIndex)` - 指定网格中指定索引的顶点法线。 -- `getVertexNormalXs(meshIndex)` - 引用网格的所有法线 X 值列表。 -- `getVertexNormalYs(meshIndex)` - 引用网格的所有法线 Y 值列表。 -- `getVertexNormalZs(meshIndex)` - 引用网格的所有法线 Z 值列表。 -- `getVertexLayoutCount(meshIndex)` - 整个网格中顶点布局的数量。顶点布局是顶点属性的集合。 -- `getVertexLayout(meshIndex, layoutIndex)` - 顶点布局仅包含可用于查询与顶点关联的实际属性(如位置、纹理坐标和法线)的属性索引。布局中的索引可与上述定义的 API 一起使用。 -- `getVertexLayoutPositionIndices(meshIndex)` - 引用网格中每个顶点的位置索引。 -- `getVertexLayoutTextureCoordinateIndices(meshIndex)` - 引用网格中每个顶点的纹理坐标索引。 -- `getVertexLayoutNormalIndices(meshIndex)` - 引用网格中每个顶点的法线索引。 -- `getFaceCount(meshIndex)` - 指定网格所包含的面的数量。 -- `getFaceVertexLayoutIndices(meshIndex, faceIndex)` - 属于指定网格上某个面的顶点布局索引列表。 -- `getMaximumInfluencePerVertex(meshIndex)` - 可能影响任何单个顶点的最大关节数量。 -- `getSkinWeightsCount(meshIndex)` - 与指定网格关联的蒙皮权重数量。 -- `getSkinWeightsValues(meshIndex, vertexIndex)` - 影响请求顶点的蒙皮权重列表。 -- `getSkinWeightsJointIndices(meshIndex, vertexIndex)` - 与指定顶点的每个蒙皮权重关联的关节索引列表。关节索引的存储顺序与其关联的权重相同。 -- `getBlendShapeTargetCount(meshIndex)` - 属于指定网格的变形目标数量。 -- `getBlendShapeChannelIndex(meshIndex, blendShapeTargetIndex)` - 请求的变形目标对应的变形通道索引。 -- `getBlendShapeTargetDeltaCount(meshIndex, blendShapeTargetIndex)` - 属于指定变形目标的增量数量。 -- `getBlendShapeTargetDelta(meshIndex, blendShapeTargetIndex, deltaIndex)` - 每个受影响顶点的增量列表。 -- `getBlendShapeTargetDeltaXs(meshIndex, blendShapeTargetIndex)` - 引用变形目标的所有增量 X 值列表。 -- `getBlendShapeTargetDeltaYs(meshIndex, blendShapeTargetIndex)` - 引用变形目标的所有增量 Y 值列表。 -- `getBlendShapeTargetDeltaZs(meshIndex, blendShapeTargetIndex)` - 引用变形目标的所有增量 Z 值列表。 -- `getBlendShapeTargetVertexIndices(meshIndex, blendShapeTargetIndex)` - 受引用变形目标影响的顶点位置索引。顶点位置索引的存储顺序与其关联的增量相同。这些索引可以通过 getVertexPosition 用于查询关联的顶点本身。 - -## Writer methods - -### DescriptorWriter -包含用于访问角色和骨骼各种元数据的只写访问器。 - -- `setName(name)` - Sets character name. -- `setArchetype(archetype)` - Sets character archetype. -- `setGender(gender)` - Sets character gender. -- `setAge(age)` - Sets character age. -- `setTranslationUnit(unit)` - Sets translation unit (cm or m). -- `setRotationUnit(unit)` - Sets rotation unit (degrees or radians). -- `setCoordinateSystem(system)` - Sets coordinate system (directions of the axes). -- `setLODCount(lodCount)` - Sets available levels of detail (e.g. 6 which means the following levels are available: [0,1,2,3,4,5], where 0 is the LOD with the highest details, and 5 is the LOD with lowest details). -- `setDBMaxLOD(lod)` - Sets the maximum level of detail stored in the DNA data for this character. -- `setDBComplexity(name)` - Sets name of the input control interface used to drive this character rig. -- `setDBName(name)` - Sets name of the database from which the character originates. - -### DefinitionWriter -包含用于表示骨骼静态数据的 DNA 属性的只写访问器。 - -- `clearGUIControlNames()` - 删除所有存储的 GUI 控制器名称。 -- `setGUIControlName(index, name)` - 设置指定 GUI 控制器的名称。 -- `clearRawControlNames()` - 删除所有存储的原始控制器名称。 -- `setRawControlName(index, name)` - 设置指定原始控制器的名称。 -- `clearJointNames()` - 删除所有存储的关节名称。 -- `setJointName(index, name)` - 设置指定关节的名称。 -- `clearJointIndices()` - 删除所有存储的关节索引。 -- `setJointIndices(index, jointIndices, count)` - 将关节索引列表存储到指定索引。该索引表示整个关节索引列表的位置,而不是其单个元素的位置,即关节索引 2D 矩阵中的行索引。 -- `clearLODJointMappings()` - 删除所有存储的 LOD 到关节列表索引的映射条目。 -- `setLODJointMapping(lod, index)` - 设置哪些关节属于哪个细节级别。 -- `clearBlendShapeChannelNames()` - 删除所有存储的变形通道名称。 -- `setBlendShapeChannelName(index, name)` - 设置指定变形通道的名称。 -- `clearBlendShapeChannelIndices()` - 删除所有存储的变形通道索引。 -- `setBlendShapeChannelIndices(index, blendShapeChannelIndices, count)` - 将变形通道名称索引列表存储到指定索引。该索引表示整个变形通道索引列表的位置,而不是其单个元素的位置,即变形通道索引 2D 矩阵中的行索引。 -- `clearLODBlendShapeChannelMappings()` - 删除所有存储的 LOD 到变形通道列表索引的映射条目。 -- `setLODBlendShapeChannelMapping(lod, index)` - 设置哪些变形通道属于哪个细节级别。 -- `clearAnimatedMapNames()` - 删除所有存储的动画贴图名称。 -- `setAnimatedMapName(index, name)` - 设置指定动画贴图的名称。 -- `clearAnimatedMapIndices()` - 删除所有存储的动画贴图索引。 -- `setAnimatedMapIndices(index, animatedMapIndices, count)` - 将动画贴图名称索引列表存储到指定索引。该索引表示整个动画贴图索引列表的位置,而不是其单个元素的位置,即动画贴图索引 2D 矩阵中的行索引。 -- `clearLODAnimatedMapMappings()` - 删除所有存储的 LOD 到动画贴图列表索引的映射条目。 -- `setLODAnimatedMapMapping(lod, index)` - 设置哪些动画贴图属于哪个细节级别。 -- `clearMeshNames()` - 删除所有存储的网格名称。 -- `setMeshName(index, name)` - 设置指定网格的名称。 -- `clearMeshIndices()` - 删除所有存储的网格索引。 -- `setMeshIndices(index, meshIndices, count)` - 将网格名称索引列表存储到指定索引。该索引表示整个网格索引列表的位置,而不是其单个元素的位置,即网格索引 2D 矩阵中的行索引。 -- `clearLODMeshMappings()` - 删除所有存储的 LOD 到网格列表索引的映射条目。 -- `setLODMeshMapping(lod, index)` - 设置哪些网格属于哪个细节级别。 -- `clearMeshBlendShapeChannelMappings()` - 删除所有存储的网格到变形通道的映射条目。 -- `setMeshBlendShapeChannelMapping(index, meshIndex, blendShapeChannelIndex)` - 将变形通道与其网格关联。 -- `setJointHierarchy(jointIndices, count)` - 设置描述关节之间父子关系的简单数组。 - 示例: - 关节名称: [A, B, C, D, E, F, G, H] - 层级结构: [0, 0, 0, 1, 1, 4, 2, 2] - 描述了以下层级结构: - ``` - A - ├── B - │ ├── D - │ └── E - │ └── F - └── C - ├── G - └── H - ``` -- `setNeutralJointTranslations(translations, count)` - 设置绑定姿态下关节的平移值。 -- `setNeutralJointRotations(rotations, count)` - 设置绑定姿态下关节的旋转值。 - -### BehaviorWriter -包含用于定义骨骼评估的 DNA 属性的只写访问器。 - -- `setGUIToRawInputIndices(inputIndices, count)` - 设置用于将 GUI 映射到原始控制器的输入索引。 -- `setGUIToRawOutputIndices(outputIndices, count)` - 设置用于将 GUI 映射到原始控制器的输出索引。 -- `setGUIToRawFromValues(fromValues, count)` - 设置在 GUI 到原始控制器映射期间用于决定是否应评估特定条目的过滤值(下限)。 -- `setGUIToRawToValues(toValues, count)` - 设置在 GUI 到原始控制器映射期间用于决定是否应评估特定条目的过滤值(上限)。 -- `setGUIToRawSlopeValues(slopeValues, count)` - 设置在 GUI 到原始控制器映射期间用于计算输出值的计算值(斜率/梯度)。 -- `setGUIToRawCutValues(cutValues, count)` - 设置在 GUI 到原始控制器映射期间用于计算输出值的计算值(垂直截距)。 -- `setPSDCount(count)` - 设置不同 PSD 表情的数量。 -- `setPSDRowIndices(rowIndices, count)` - 设置将成为 PSD 矩阵行的 PSD(输入)索引。 -- `setPSDColumnIndices(columnIndices, count)` - 设置将成为 PSD 矩阵列的控制器(输入)索引。 -- `setPSDValues(weights, count)` - 设置与每个 PSD 行和列对相关联的权重。 -- `setJointRowCount(rowCount)` - 设置完整未压缩关节矩阵中的行数。 -- `setJointColumnCount(columnCount)` - 设置完整未压缩关节矩阵中的列数。 -- `clearJointGroups()` - 删除所有关节组。 -- `deleteJointGroup(jointGroupIndex)` - 删除指定的关节组。 -- `setJointGroupLODs(jointGroupIndex, lods, count)` - 设置指定关节组中每个细节级别的行数。 - 每个元素的位置代表级别本身,而值表示属于该级别的关节组内的行数。例如: - ``` - [12, 9, 3] - │ │ └── LOD-2 包含前 3 行 - │ └── LOD-1 包含前 9 行 - └── LOD-0 包含前 12 行 - ``` -- `setJointGroupInputIndices(jointGroupIndex, inputIndices, count)` - 设置指定关节组包含的列索引。这些列索引指向完整的未压缩关节矩阵。 -- `setJointGroupOutputIndices(jointGroupIndex, outputIndices, count)` - 设置指定关节组包含的行索引。这些行索引指向完整的未压缩关节矩阵。 -- `setJointGroupValues(jointGroupIndex, values, count)` - 设置指定关节组包含的值。 -- `setJointGroupJointIndices(jointGroupIndex, jointIndices, count)` - 设置指定关节组包含的关节索引。 -- `setBlendShapeChannelLODs(lods, count)` - 设置变形通道每个细节级别的输入索引数量。 - 每个元素的位置代表级别本身(例如 [0,1,2,3,4,5] 值 0 是最高细节的 LOD,值 5 是最低细节的 LOD),而值表示属于该级别的输入索引数量。 -- `setBlendShapeChannelInputIndices(inputIndices, count)` - 设置用于索引输入向量的输入索引。 -- `setBlendShapeChannelOutputIndices(outputIndices, count)` - 设置指定变形通道输出值位置的输出索引。 -- `setAnimatedMapLODs(lods, count)` - 设置动画贴图每个细节级别的行数。 - 每个元素的位置代表级别本身(例如 [0,1,2,3,4,5] 值 0 是最高细节的 LOD,值 5 是最低细节的 LOD),而值表示属于该级别的行数(在条件表中)。 -- `setAnimatedMapInputIndices(inputIndices, count)` - 设置用于索引输入值数组的输入索引。 -- `setAnimatedMapOutputIndices(outputIndices, count)` - 设置指定计算输出值位置的输出索引。 -- `setAnimatedMapFromValues(fromValues, count)` - 设置用于决定是否应评估特定条目的过滤值(下限)。 -- `setAnimatedMapToValues(toValues, count)` - 设置用于决定是否应评估特定条目的过滤值(上限)。 -- `setAnimatedMapSlopeValues(slopeValues, count)` - 设置用于计算输出值的计算值(斜率/梯度)。 -- `setAnimatedMapCutValues(cutValues, count)` - 设置用于计算输出值的计算值(垂直截距)。 - -### GeometryWriter -包含与骨骼相关的几何数据的只写访问器。 - -- `clearMeshes()` - 删除所有网格。 -- `deleteMesh(meshIndex)` - 删除指定的网格。 -- `setVertexPositions(meshIndex, positions, count)` - 设置顶点位置。 -- `setVertexTextureCoordinates(meshIndex, textureCoordinates, count)` - 设置顶点纹理坐标。 -- `setVertexNormals(meshIndex, normals, count)` - 设置顶点法线。 -- `setVertexLayouts(meshIndex, layouts, count)` - 设置属于指定网格的顶点布局。 -- `clearFaceVertexLayoutIndices(meshIndex)` - 删除指定网格的所有顶点布局索引列表。 -- `setFaceVertexLayoutIndices(meshIndex, faceIndex, layoutIndices, count)` - 设置属于指定面的顶点布局索引。布局索引指向通过 setVertexLayouts() 设置的数组。 -- `setMaximumInfluencePerVertex(meshIndex, maxInfluenceCount)` - 设置可能影响任何单个顶点的最大关节数量。 -- `clearSkinWeights(meshIndex)` - 删除指定网格的所有蒙皮权重。 -- `setSkinWeightsValues(meshIndex, vertexIndex, weights, count)` - 设置影响引用顶点的蒙皮权重。权重总和必须等于 1。 -- `setSkinWeightsJointIndices(meshIndex, vertexIndex, jointIndices, count)` - 设置与指定顶点的每个蒙皮权重关联的关节索引。关节索引必须按与其关联的权重相同的顺序存储。 -- `clearBlendShapeTargets(meshIndex)` - 删除指定网格的所有变形目标。 -- `setBlendShapeChannelIndex(meshIndex, blendShapeTargetIndex, blendShapeChannelIndex)` - 设置指定变形目标对应的变形通道索引。将网格本地变形目标索引与定义层中的绝对变形通道索引关联。 -- `setBlendShapeTargetDeltas(meshIndex, blendShapeTargetIndex, deltas, count)` - 设置每个受影响顶点的增量。 -- `setBlendShapeTargetVertexIndices(meshIndex, blendShapeTargetIndex, vertexIndices, count)` - 设置受指定变形目标影响的顶点位置索引。顶点位置索引必须按与其关联的增量相同的顺序存储。 diff --git a/docs/dna_viewer.md b/docs/dna_viewer.md deleted file mode 100644 index 45f857f..0000000 --- a/docs/dna_viewer.md +++ /dev/null @@ -1,43 +0,0 @@ -# DNAViewer -[`dna_viewer`](/dna_viewer) 包含从 DNA 文件读取和在 Maya 中创建功能性骨骼所需的所有类。 -它的组织方式使每个选项都可配置,因此您可以轻松获得想要的确切结果。 - -## 示例 -- [生成骨骼](/examples/dna_viewer_build_rig.py) -- [按 LOD 导出 FBX](/examples/dna_viewer_export_fbx.py) -- [将 Maya 场景的更改传播到 DNA](/examples/dna_viewer_grab_changes_from_scene_and_propagate_to_dna.py) -- [简单 UI](/examples/dna_viewer_run_in_maya.py) - -## 从代码使用 -有两个 [API](dna_viewer_api.md): - - [build_meshes](dna_viewer_api_build_meshes.md) - - [build_rig](dna_viewer_api_build_rig.md) - -## 在 Maya 中使用 -在 Maya 中的使用说明位于[此处](/docs/dna_viewer_maya.md) - -## 文件夹结构 - -- [builder](/dna_viewer/builder) - 包含构建器类,用于轻松添加配置选项并构建场景、配置、网格等。 -- [dnalib](/dna_viewer/dnalib) - 包含用于更好地访问 DNA 文件的 API 类。 -- [ui](/dna_viewer/ui) - 包含 Maya UI 所需的类。 - -## 工作原理 - -一般流程如下: - -![image](img/flow_general.png) - -场景构建过程的流程如下: - -![image](img/flow_scene_build.png) - -骨骼构建过程的流程如下: - -![image](img/flow_character_build.png) - -图例: -- 蓝色: 构建器相关 -- 绿色: 配置相关 -- 棕色: 模型相关 -- 紫色: 读取器相关 diff --git a/docs/dna_viewer_API.md b/docs/dna_viewer_API.md deleted file mode 100644 index 1491c59..0000000 --- a/docs/dna_viewer_API.md +++ /dev/null @@ -1,54 +0,0 @@ -# 环境设置 - -为了能够从 dna_viewer 导入,需要设置环境。这可以通过在下面提到的任何示例开头添加以下代码来完成: - -``` -from sys import path as syspath, platform -from os import environ, path as ospath - -ROOT_DIR = fr"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") # 如果使用 Maya,请使用绝对路径 -ROOT_LIB_DIR = fr"{ROOT_DIR}/lib" -if platform == "win32": - LIB_DIR = f"{ROOT_LIB_DIR}/windows" -elif platform == "linux": - LIB_DIR = f"{ROOT_LIB_DIR}/linux" -else: - raise OSError("不支持的操作系统,请编译依赖项并添加 LIB_DIR 值") - - -if "MAYA_PLUG_IN_PATH" in environ: - separator = ":" if platform == "linux" else ";" - environ["MAYA_PLUG_IN_PATH"] = separator.join([environ["MAYA_PLUG_IN_PATH"], LIB_DIR]) -else: - environ["MAYA_PLUG_IN_PATH"] = LIB_DIR - -syspath.append(ROOT_DIR) -syspath.append(LIB_DIR) -``` - -从 Maya 运行时,应将 `ROOT_DIR` 设置为仓库根目录的绝对路径。 - -# DNA - -## 加载 DNA - -加载 DNA 并返回一个 [`DNA`](../dna_viewer/dnalib/dnalib.py#L13) 对象。 - -``` -from dna_viewer import DNA - -dna_ada = DNA(DNA_PATH_ADA) -dna_taro = DNA(DNA_PATH_TARO) -``` - -这使用以下参数: -- `dna_path: str` - 要使用的 DNA 文件的路径。 -- `layers: Optional[List[Layer]]` - 要加载的 DNA 部分列表。如果未传递任何内容,将加载整个 DNA。等同于传递 Layer.all。 - -## 构建网格 - -构建网格 API 说明位于[此处](/docs/dna_viewer_api_build_meshes.md)。 - -## 构建骨骼 - -构建骨骼 API 说明位于[此处](/docs/dna_viewer_api_build_rig.md)。 \ No newline at end of file diff --git a/docs/dna_viewer_api_build_meshes.md b/docs/dna_viewer_api_build_meshes.md deleted file mode 100644 index 66fea97..0000000 --- a/docs/dna_viewer_api_build_meshes.md +++ /dev/null @@ -1,106 +0,0 @@ -# 网格工具 - -以下方法的目的是提供: -- 从给定 DNA 文件路径构建网格的简单机制 -- 返回和打印 DNA 文件中包含的网格信息 - -## 导入 - -``` -from dna_viewer import DNA, Config, build_meshes -``` - -``` -DNA_PATH_ADA = f"{ROOT_DIR}/data/dna_files/Ada.dna" -DNA_PATH_TARO = f"{ROOT_DIR}/data/dna_files/Taro.dna" -``` - -## 创建 Config 实例([`Config`](../dna_viewer/builder/config.py#35)) -创建一个将在网格构建过程中使用的配置对象。 - -``` -config = Config( - add_joints=True, - add_blend_shapes=True, - add_skin_cluster=True, - add_ctrl_attributes_on_root_joint=True, - add_animated_map_attributes_on_root_joint=True, - lod_filter=[0, 1], - mesh_filter=["head"], -) -``` - -以下是 `Config` 类的一些属性: -- `add_joints: bool` - 表示是否应添加关节的标志,默认为 `True`。 -- `add_blend_shapes: bool` - 表示是否应添加变形的标志,默认为 `True`。 -- `add_skin_cluster: bool` - 表示是否应添加蒙皮簇的标志,默认为 `True`。 -- `add_ctrl_attributes_on_root_joint: bool` - 表示是否应将控制属性作为属性添加到根关节的标志,默认为 `False`。它们在引擎中用作 Rig Logic 输入的动画曲线。 -- `add_animated_map_attributes_on_root_joint: bool` - 表示是否应将动画贴图属性添加到根关节作为属性的标志,默认为 `True`。它们在引擎中用作动画贴图的动画曲线。 - -**重要**: 某些标志值的组合可能会导致骨骼不可用或禁用某些功能! - -## 构建网格 ([`build_meshes`](../dna_viewer/api.py#L26)) - -用于构建骨骼元素(关节、网格、变形和蒙皮簇),不包含 Rig Logic。 -它返回已添加到场景中的网格的长名称。 - -``` -config = Config( - add_joints=True, - add_blend_shapes=True, - add_skin_cluster=True, - add_ctrl_attributes_on_root_joint=True, - add_animated_map_attributes_on_root_joint=True, - lod_filter=[0, 1], - mesh_filter=["head"], -) -mesh_names = build_meshes( - dna=dna_ada, - config=config -) -``` - -这使用以下参数: -- `dna: DNA` - 通过 `DNA` 获得的 DNA 实例。 -- `config: Config` - 配置实例。 - -``` -mesh_names = build_meshes(dna=dna_ada) -``` - -这默认添加 DNA 文件中的所有网格。 - -### 示例 - -**重要**: 运行此示例之前需要执行上述[环境设置](dna_viewer_api.md#environment-setup)。 - -``` -from dna_viewer import DNA, Config, build_meshes - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -# Sets DNA file path -DNA_PATH_ADA = f"{ROOT_DIR}/data/dna_files/Ada.dna" -dna_ada = DNA(DNA_PATH_ADA) - -# Starts the mesh build process with all the default values -build_meshes(dna=dna_ada) - -# Creates the options to be passed in `build_meshes` -config = Config( - add_joints=True, - add_blend_shapes=True, - add_skin_cluster=True, - add_ctrl_attributes_on_root_joint=True, - add_animated_map_attributes_on_root_joint=True, - lod_filter=[0, 1], - mesh_filter=["head"], -) - -# Starts the mesh building process with the provided parameters -# In this case it will create every mesh contained in LODs 0 and 1 with 'head` in it's name, -build_meshes( - dna=dna_ada, - config=config, -) -``` diff --git a/docs/dna_viewer_api_build_rig.md b/docs/dna_viewer_api_build_rig.md deleted file mode 100644 index b948732..0000000 --- a/docs/dna_viewer_api_build_rig.md +++ /dev/null @@ -1,42 +0,0 @@ -# 构建骨骼 ([`build_rig`](../dna_viewer/api.py#L9)) - -构建骨骼 API 用于从给定的 DNA 文件路径在 Maya 场景中轻松组装角色骨骼。 - -## 创建 RigConfig 实例([`RigConfig`](../dna_viewer/builder/config.py#35)) -创建一个将在骨骼构建过程中使用的配置对象。 - -``` -from dna_viewer import RigConfig -``` - -以下是 `RigConfig` 类的一些属性: -- `gui_path: str` - GUI 文件路径。 -- `analog_gui_path: str` - 模拟 GUI 文件路径。 -- `aas_path: str` - 附加组装脚本路径。 -- `aas_method: str` - 应从附加组装脚本调用的方法名称。 -- `add_ctrl_attributes_on_root_joint: bool` - 表示是否应在根关节上添加属性的标志,默认为 `True`。 -- `add_key_frames: bool` - 表示是否应添加关键帧的标志,默认为 `True`。 - -## 示例 - -**重要**: 运行此示例之前需要执行上述[环境设置](dna_viewer_api.md#environment-setup)。 - -``` -from dna_viewer import DNA, RigConfig, build_rig - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") - -# Sets the values that will used -DNA_PATH_ADA = f"{ROOT_DIR}/data/dna_files/Ada.dna" -dna_ada = DNA(DNA_PATH_ADA) - -config = RigConfig( - gui_path=f"{ROOT_DIR}/data/gui.ma", - analog_gui_path=f"{ROOT_DIR}/data/analog_gui.ma", - aas_path=f"{ROOT_DIR}/data/additional_assemble_script.py", -) - -# Creates the rig -build_rig(dna=dna_ada, config=config) -``` diff --git a/docs/dna_viewer_maya.md b/docs/dna_viewer_maya.md deleted file mode 100644 index 7772419..0000000 --- a/docs/dna_viewer_maya.md +++ /dev/null @@ -1,13 +0,0 @@ -# 在 Maya 中使用 - -还有一个 [Maya UI 窗口](/examples/dna_viewer_run_in_maya.py),可用于以非编程方式创建包含功能性骨骼的场景。 - -![image](img/ui.png) - -![image](img/ui_extra_options.png) - -在场景生成期间,RigLogic4 插件将自动加载,您可能会看到以下消息: - -![image](img/maya_warn.png) - -点击 `允许` 加载插件。如果启用 **应用于此位置的所有插件** 选项,Maya 将不再显示此通知。 \ No newline at end of file diff --git a/docs/dnacalib.md b/docs/dnacalib.md deleted file mode 100644 index 3dd2aab..0000000 --- a/docs/dnacalib.md +++ /dev/null @@ -1,100 +0,0 @@ -# DNACalib -这个库用于对 DNA 文件进行修改。 -它使用 C++ 编写,并且也有 Python 包装器。[SWIG](https://www.swig.org/) 库用于生成 Python 的绑定。DNACalib 可以在命令行或 Maya 中使用。 -提供了 Windows 和 Linux 的二进制文件。**如果您使用不同的架构和/或平台,您必须自行构建 DNACalib。** - -## DNACalib 文件夹结构 -- [`DNACalib`](/dnacalib/DNACalib) - 包含 DNACalib 及其依赖项的 C++ 源代码。其中有一个用于读写 DNA 文件的库,以及一些其他实用库。 -- [`PyDNACalib`](/dnacalib/PyDNACalib) - 包含用于生成 DNACalib Python 包装器的源代码。 -- [`PyDNA`](/dnacalib/PyDNA) - 包含用于生成 DNA 库 Python 包装器的源代码,该库位于包含 C++ 源代码的 DNACalib 文件夹下。 -- [`SPyUS`](/dnacalib/SPyUS) - 包含 PyDNACalib 和 PyDNA 都使用的一些通用 SWIG 接口文件。 -- [`CMakeModulesExtra`](/dnacalib/CMakeModulesExtra) - 包含在整个项目中使用的一些通用 CMake 函数,包括 C++ 和 Python 包装器。 - -## 使用 - -例如,要更改中性关节的旋转值,使用 -[`SetNeutralJointRotationsCommand`](/dnacalib/DNACalib/include/dnacalib/commands/SetNeutralJointRotationsCommand.h)。 - -下面是一个示例,它读取 DNA,将所有中性关节的旋转值更改为 `{1, 2, 3}`,并用这些新值覆写 DNA 文件。 - -``` - -// 创建 DNA 读取器 -auto inOutStream = dnac::makeScoped("example.dna", - dnac::FileStream::AccessMode::ReadWrite, - dnac::FileStream::OpenMode::Binary); -auto reader = dnac::makeScoped(inOutStream.get()); -reader->read(); - -// 检查读取 DNA 文件时是否发生错误 -if (!dnac::Status::isOk()) { - // 处理读取器错误 -} - -// 创建 DNACalib 读取器以编辑 DNA -auto dnaReader = dnac::makeScoped(reader.get()); - -std::vector rotations{dnaReader->getJointCount(), {1.0f, 2.0f, 3.0f}}; - -// 创建命令实例 -dnac::SetNeutralJointRotationsCommand cmd{dnac::ConstArrayView{rotations}}; - -// 执行命令 -cmd.run(dnaReader.get()); - -// 写入 DNA 文件 -auto writer = dnac::makeScoped(inOutStream.get()); -writer->setFrom(dnaReader.get()); -writer->write(); - -// 检查写入 DNA 文件时是否发生错误 -if (!dnac::Status::isOk()) { - // 处理写入器错误 -} -``` - -## 示例 - -### C++ -C++ 库使用示例可以在[此处](/dnacalib/DNACalib/examples)找到。 - -这些包括: -- [链接多个命令](/dnacalib/DNACalib/examples/CommandSequence.cpp) -- [重命名变形](/dnacalib/DNACalib/examples/SingleCommand.cpp) - -### Python -从 Python 使用库的示例在[此处](/examples)。 - -这些包括: -- [展示几个命令](/examples/dnacalib_demo.py) -- [重命名关节](/examples/dnacalib_rename_joint_demo.py) -- [从头开始创建一个小型 DNA](/examples/dna_demo.py) -- [通过提取特定 LOD 从现有 DNA 创建新 DNA](/examples/dnacalib_lod_demo.py) -- [读取二进制 DNA 并以人类可读格式写入](/examples/dna_binary_to_json_demo.py) -- [删除关节](/examples/dnacalib_remove_joint.py) -- [清除变形数据](/examples/dnacalib_clear_blend_shapes.py) -- [从中性网格中减去值](/examples/dnacalib_neutral_mesh_subtract.py) - -## 构建 -提供了 64 位 Windows 和 Linux 的[预构建二进制文件](/lib)。 -如果您使用不同的架构和/或平台,您必须构建 DNACalib。 - -先决条件: -- [CMake](https://cmake.org/download/) 至少 3.14 版本 -- [SWIG](https://www.swig.org/download.html) 至少 4.0.0 版本 -- [Python](https://www.python.org/downloads/) 要指定要使用的 python3 的确切版本,请设置 CMake 变量 -`PYTHON3_EXACT_VERSION`。例如,要在 Maya 2022 中使用该库,请使用 3.7 版本。对于 Maya 2023,使用 3.9 版本。 - -使用 CMake 生成构建所需的构建脚本,例如通过从 -[`MetaHuman-DNA-Calibration/dnacalib/`](/dnacalib) 目录执行以下命令: - -``` -mkdir build -cd build -cmake .. -``` - -然后,要开始构建过程: -``` -cmake --build -``` diff --git a/docs/dnacalib_api.md b/docs/dnacalib_api.md deleted file mode 100644 index 3a928eb..0000000 --- a/docs/dnacalib_api.md +++ /dev/null @@ -1,78 +0,0 @@ -# API 概述 -DNA 修改是通过可用的命令完成的。每个命令都实现了 `run(DNACalibDNAReader* output)` 方法, -该方法修改通过其参数指定的 DNA。要配置在 `run()` 中发生的修改,可以通过构造函数或特定的 setter 方法传递参数。 -以下文档适用于 C++。目前尚无 Python 文档。 - -所有可用命令列表: - -## 用于删除 DNA 特定部分的命令: - - - [`RemoveJointAnimationCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RemoveJointAnimationCommand.h) 从 DNA 中删除 -给定索引的关节动画。 - - - [`RemoveJointCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RemoveJointCommand.h) 从 DNA 中删除给定 -索引的关节。 - - - [`RemoveMeshCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RemoveMeshCommand.h) 从 DNA 中删除给定索引 -的网格。 - - - [`ClearBlendShapesCommand`](/dnacalib/DNACalib/include/dnacalib/commands/ClearBlendShapesCommand.h) 从 DNA 中清除所有变形数据。 - -## 用于重命名 DNA 特定部分的命令: - - - [`RenameAnimatedMapCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RenameAnimatedMapCommand.h) 重命名给定 -索引或之前名称的动画贴图。 - - - [`RenameBlendShapeCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RenameBlendShapeCommand.h) 重命名给定 -索引或之前名称的变形。 - - - [`RenameJointCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RenameJointCommand.h) 重命名给定 -索引或之前名称的关节。 - - - [`RenameMeshCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RenameMeshCommand.h) 重命名给定 -索引或之前名称的网格。 - -## 用于变换 DNA 的命令: - - - [`RotateCommand`](/dnacalib/DNACalib/include/dnacalib/commands/RotateCommand.h) 围绕给定原点旋转中性关节和顶点 -位置。 - - - [`ScaleCommand`](/dnacalib/DNACalib/include/dnacalib/commands/ScaleCommand.h) 按因子缩放中性关节、顶点位置 -和关节及变形增量。对于中性关节和关节增量,只缩放平移属性。 - - - [`TranslateCommand`](/dnacalib/DNACalib/include/dnacalib/commands/TranslateCommand.h) 平移中性关节和 -顶点位置。 - -## 用于修改变形的命令: - - - [`SetBlendShapeTargetDeltasCommand`](/dnacalib/DNACalib/include/dnacalib/commands/SetBlendShapeTargetDeltasCommand.h) -更改变形目标增量。 - - - [`PruneBlendShapeTargetsCommand`](/dnacalib/DNACalib/include/dnacalib/commands/PruneBlendShapeTargetsCommand.h) -修剪小于或等于指定阈值的变形目标增量。 - -## 用于更改绑定姿态的命令: - - - [`SetNeutralJointRotationsCommand`](/dnacalib/DNACalib/include/dnacalib/commands/SetNeutralJointRotationsCommand.h) -为中性关节设置新的旋转值。 - - - [`SetNeutralJointTranslationsCommand`](/dnacalib/DNACalib/include/dnacalib/commands/SetNeutralJointTranslationsCommand.h) -为中性关节设置新的平移值。 - - - [`SetVertexPositionsCommand`](/dnacalib/DNACalib/include/dnacalib/commands/SetVertexPositionsCommand.h) 更改 -顶点位置值。 - -## 执行有用计算或提供附加功能的命令: - - - [`SetLODsCommand`](/dnacalib/DNACalib/include/dnacalib/commands/SetLODsCommand.h) 过滤 DNA,使其只包含 -指定 LOD 的数据。 - - - [`CalculateMeshLowerLODsCommand`](/dnacalib/DNACalib/include/dnacalib/commands/CalculateMeshLowerLODsCommand.h) -重新计算指定网格的较低 LOD 网格的顶点位置。 - - - [`CommandSequence`](/dnacalib/DNACalib/include/dnacalib/commands/CommandSequence.h) 在指定的 DNA 上运行 -一系列命令。 - - -每个可用命令及其方法的更详细描述可以在 -[`DNACalib/include/dnacalib/commands`](/dnacalib/DNACalib/include/dnacalib/commands/)中找到。 \ No newline at end of file diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index fb15b87..0000000 --- a/docs/faq.md +++ /dev/null @@ -1,35 +0,0 @@ -# 常见问题解答 (FAQ) - -## 修复 "RuntimeError: Error loading DNA: DNA signature mismatched, expected DNA, got ver?" - -要修复此问题,您应该安装 [git-lfs](https://git-lfs.github.com/),然后重新克隆仓库。 -这样 DNA 文件就会正确下载。 -如果您无法安装 git-lfs,可以手动下载 DNA 文件。 - -## 如何分发 Maya 场景? - -如果您包含以下内容,您的场景应该可以直接使用: -- 场景文件 (`.mb` 文件) -- DNA (`.dna` 文件) -- 工作空间 (`workspace.mel` 文件) - -所有这些文件需要一起分发。如果这些文件没有打包在一起,并且您在 Maya 中遇到骨骼问题, -请尝试以下步骤: - -### 如何共享生成的文件? -如果您想将生成的 Maya 场景分发给其他用户,必须将 `.dna` 文件和 `workspace.mel` 与场景一起分发。 - -### 如何打开生成的场景? -在加载生成的场景之前,请按照以下步骤操作: -- 从主菜单中,转到 文件 > 设置项目。 -- 选择 `workspace.mel` -- 设置包含文件夹(包含生成的 maya 场景、`.dna` 文件和 `workspace.mel`)。 - -### 如何在 Maya 场景中更改 DNA 路径? -如果您想更改场景中的 DNA 路径: -- 在 `大纲视图` 中,取消选择 **仅 DAG 对象**: - - ![image](img/change_path_outliner_settings.png) -- 仍在大纲视图中,搜索 `rl4`。您会看到一个名称以 `rl4Embedded_` 开头的文件。点击此文件以选择它。 - - ![image](img/change_path_outliner.png) -- 在 `属性编辑器` 中,您可以通过 `Dna 文件路径` 更改路径: - - ![image](img/change_path_node_path.png) diff --git a/docs/img/aas.png b/docs/img/aas.png deleted file mode 100644 index 4e3723f..0000000 Binary files a/docs/img/aas.png and /dev/null differ diff --git a/docs/img/change_path_node_path.png b/docs/img/change_path_node_path.png deleted file mode 100644 index f21cac2..0000000 Binary files a/docs/img/change_path_node_path.png and /dev/null differ diff --git a/docs/img/change_path_outliner.png b/docs/img/change_path_outliner.png deleted file mode 100644 index bad1141..0000000 Binary files a/docs/img/change_path_outliner.png and /dev/null differ diff --git a/docs/img/change_path_outliner_settings.png b/docs/img/change_path_outliner_settings.png deleted file mode 100644 index dd2b3eb..0000000 Binary files a/docs/img/change_path_outliner_settings.png and /dev/null differ diff --git a/docs/img/flow_character_build.png b/docs/img/flow_character_build.png deleted file mode 100644 index 7a0efce..0000000 Binary files a/docs/img/flow_character_build.png and /dev/null differ diff --git a/docs/img/flow_general.png b/docs/img/flow_general.png deleted file mode 100644 index ecb24f8..0000000 Binary files a/docs/img/flow_general.png and /dev/null differ diff --git a/docs/img/flow_scene_build.png b/docs/img/flow_scene_build.png deleted file mode 100644 index aacac05..0000000 Binary files a/docs/img/flow_scene_build.png and /dev/null differ diff --git a/docs/img/layers.svg b/docs/img/layers.svg deleted file mode 100644 index 0b30546..0000000 --- a/docs/img/layers.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -
DNA Layers
DNA Layers
Descriptor
Descriptor
Definition
Definition
Behavior
Behavior
Geometry
Geometry
\ No newline at end of file diff --git a/docs/img/maya_warn.png b/docs/img/maya_warn.png deleted file mode 100644 index 5860825..0000000 Binary files a/docs/img/maya_warn.png and /dev/null differ diff --git a/docs/img/metahuman_008.png b/docs/img/metahuman_008.png deleted file mode 100644 index 3d69436..0000000 Binary files a/docs/img/metahuman_008.png and /dev/null differ diff --git a/docs/img/metahuman_010.png b/docs/img/metahuman_010.png deleted file mode 100644 index 9d03a8a..0000000 Binary files a/docs/img/metahuman_010.png and /dev/null differ diff --git a/docs/img/ui.png b/docs/img/ui.png deleted file mode 100644 index 9097255..0000000 Binary files a/docs/img/ui.png and /dev/null differ diff --git a/docs/img/ui_extra_options.png b/docs/img/ui_extra_options.png deleted file mode 100644 index 03e1742..0000000 Binary files a/docs/img/ui_extra_options.png and /dev/null differ diff --git a/docs/repository_organization.md b/docs/repository_organization.md deleted file mode 100644 index 1468a7a..0000000 --- a/docs/repository_organization.md +++ /dev/null @@ -1,62 +0,0 @@ -# 仓库组织结构 - -本仓库包含两个独立的组件: -1. **dnacalib C++ 库** - 用于操作 DNA 文件 -2. **dna_viewer python 代码** - 用于在 Autodesk Maya 中可视化 DNA - -# 文件夹结构 - -- [dnacalib](/dnacalib) - DNACalib 源代码 -- [dna_viewer](/dna_viewer) - dna_viewer 源代码 -- [examples](/examples) - 多个 Python 脚本,展示 dna_viewer 和 DNACalib Python 封装的基本用法 -- [lib](/lib) - DNACalib、PyDNACalib 和 PyDNA 的预编译二进制文件 -- [data](/data) - 必需的 DNA 文件和 Maya 场景文件 -- [docs](/docs) - 文档 - -## DNACalib -文档位于[此处](dnacalib.md) - -## DNAViewer -文档位于[此处](dna_viewer.md) - -## 示例 -要运行 [DNAViewer 示例](/docs/dna_viewer.md#examples),您必须安装 Maya 2022。 -要运行 [DNACalib 示例](/docs/dnacalib.md#python),您需要 Python3。 - -## Lib - -[Lib 文件夹](/lib)包含 Windows 和 Linux 平台的 DNACalib 库预编译二进制文件。此外,还提供了 Maya 的 RL4 插件。 - -### Linux 路径 -您需要为[lib](lib/Maya2022/linux)中的所有 **.so** 文件复制或创建符号链接: - -```shell -sudo ln -s ~/MetaHuman-DNA-Calibration/lib/Maya2022/linux/_py3dna.so /usr/lib/_py3dna.so - -sudo ln -s ~/MetaHuman-DNA-Calibration/lib/Maya2022/linux/libdnacalib.so /usr/lib/libdnacalib.so - -sudo ln -s ~/MetaHuman-DNA-Calibration/lib/Maya2022/linux/libdnacalib.so.6 /usr/lib/libdnacalib.so.6 - -sudo ln -s ~/MetaHuman-DNA-Calibration/lib/Maya2022/linux/libembeddedRL4.so /usr/lib/embeddedRL4.mll - -sudo ln -s ~/MetaHuman-DNA-Calibration/lib/Maya2022/linux/MayaUERBFPlugin.mll /usr/lib/MayaUERBFPlugin.mll -``` - -注意:请将路径 `~/MetaHuman-DNA-Calibration` 更改为您的 `MetaHuman-DNA-Calibration` 实际所在位置。 - -## Data - -[`data 文件夹`](/data)包含示例 DNA 文件。我们提供了两个 MetaHuman DNA 文件(我们的首个预设 Ada 和 Taro)。 - -| Ada | Taro | -|---|---| -|![image](img/metahuman_008.png)| ![image](img/metahuman_010.png) | - -此外,我们添加了在 Maya 场景组装过程中使用的[`gui`](/data/gui.ma)和[`analog_gui`](/data/analog_gui.ma) Maya 场景文件。 -另外,[`additional_assemble_script.py`](/data/additional_assemble_script.py)用于组织场景中的对象并连接控制器。理想的设置如下所示: - -![image](img/aas.png) - -MHC 2023 春季版本对骨骼定义进行了更改(增加了关节数量和表情数量)。 -为适应这些更改,我们在 `/data/mh4` 文件夹中添加了几个文件:新的[gui 场景](/data/mh4/gui.ma)、更新的[组装脚本](/data/mh4/additional_assemble_script.py)和 Ada 的[DNA 文件](data/mh4/dna_files/Ada.dna)示例。 -此外,在 lib 文件夹中我们添加了用于控制颈部表情的 Maya RBF 插件。最近对颈部设置进行了改进,添加 RBF 插件以及新的 gui 场景来使用它,我们可以获得更好的颈部变形效果。 \ No newline at end of file diff --git a/examples/dna_binary_to_json_demo.py b/examples/dna_binary_to_json_demo.py deleted file mode 100644 index d843364..0000000 --- a/examples/dna_binary_to_json_demo.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -This example demonstrates reading a DNA file in binary format and writing it in a human readable JSON format. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dna_binary_to_json_demo.py - mayapy dna_binary_to_json_demo.py -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - -Expected: Script will generate Ada_output.json in OUTPUT_DIR from original Ada.dna. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - -from os import makedirs -from os import path as ospath - -# If you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -OUTPUT_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_output.json" - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, JSONStreamWriter - - -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 - - -def save_dna(reader, path): - stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = JSONStreamWriter(stream) - # Create a writer based on the reader using all data layers (if no argument is passed, DataLayer_All is the default value) - writer.setFrom(reader) - # Alternatively, a writer can be created using only a subset of layers, - # e.g. to write only Behavior layer (Descriptor and Definition included with it), use: - # writer.setFrom(reader, DataLayer_Behavior) - # - # Available layer options and their approximate sizes for this example (Ada.dna, JSON format): - # DataLayer_Descriptor - ~ 3 KB - # DataLayer_Definition - includes Descriptor, ~ 131 KB - # DataLayer_Behavior - includes Descriptor and Definition, ~ 10 MB - # DataLayer_Geometry - includes Descriptor and Definition, ~ 191 MB - # DataLayer_GeometryWithoutBlendShapes - includes Descriptor and Definition, ~ 22 MB - # DataLayer_AllWithoutBlendShapes - includes everything except blend shapes from Geometry, ~ 32 MB - # DataLayer_All - ~ 201 MB - # - # If using one of the other layer options, be sure to add it to the import list. - # - # Beside specifying layers when creating a writer, layers to use can be specified when - # creating a reader as well. - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def create_json_dna(input_path, output_path): - dna_reader = load_dna(input_path) - save_dna(dna_reader, output_path) - print('Done.') - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - create_json_dna(CHARACTER_DNA, OUTPUT_DNA) diff --git a/examples/dna_demo.py b/examples/dna_demo.py deleted file mode 100644 index 3ad4b5f..0000000 --- a/examples/dna_demo.py +++ /dev/null @@ -1,101 +0,0 @@ -""" -This example demonstrates creating DNA from scratch. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dna_demo.py - mayapy dna_demo.py -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - -Expected: Script will generate Ada_output.dna in OUTPUT_DIR from original Ada.dna. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - - -from os import makedirs -from os import path as ospath - -# If you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -OUTPUT_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_output.dna" - - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, BinaryStreamWriter - - -def create_dna(path): - stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = BinaryStreamWriter(stream) - - # Sets a couple of parameters about in the dna that is about to be created and written to - writer.setName("rig name") - writer.setLODCount(4) - writer.setJointName(0, "spine") - writer.setJointName(1, "neck") - - writer.setMeshName(0, "head") - writer.setVertexPositions(0, [[0.0, 0.5, 0.3], [1.0, 3.0, -8.0]]) - writer.setVertexTextureCoordinates(0, [[0.25, 0.55], [1.5, 3.6]]) - - # Creates the DNA - writer.write() - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -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 - - -def print_dna_summary(dna_reader): - print(f"Name: {dna_reader.getName()}") - print(f"Joint count: {dna_reader.getJointCount()}") - joint_names = ", ".join( - dna_reader.getJointName(i) for i in range(dna_reader.getJointCount()) - ) - print(f"Joint names: {joint_names}") - - for mesh_idx in range(dna_reader.getMeshCount()): - # Get vertices one by one - for vtx_id in range(dna_reader.getVertexPositionCount(mesh_idx)): - vtx = dna_reader.getVertexPosition(mesh_idx, vtx_id) - print(f"Mesh {mesh_idx} - Vertex {vtx_id} : {vtx}") - # Get all X / Y / Z coordinates - print(dna_reader.getVertexPositionXs(mesh_idx)) - print(dna_reader.getVertexPositionYs(mesh_idx)) - print(dna_reader.getVertexPositionZs(mesh_idx)) - - for tc_idx in range(dna_reader.getVertexTextureCoordinateCount(mesh_idx)): - tex_coord = dna_reader.getVertexTextureCoordinate(mesh_idx, tc_idx) - print(f"Mesh {mesh_idx} - Texture coordinate {tc_idx} : {tex_coord}") - - -def create_new_dna(dna_path): - create_dna(dna_path) - dna_reader = load_dna(dna_path) - print_dna_summary(dna_reader) - print('Done.') - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - create_new_dna(OUTPUT_DNA) diff --git a/examples/dna_viewer_build_rig.py b/examples/dna_viewer_build_rig.py deleted file mode 100644 index 585d1d7..0000000 --- a/examples/dna_viewer_build_rig.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -This example demonstrates generating functional rig in maya scene and exporting fbx per lod. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dna_viewer_build_rig.py - mayapy dna_viewer_build_rig.py - NOTE: Script cannot be called with Python, it must be called with mayapy. - -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files -Expected: - - script will generate maya scene Ada.mb in OUTPUT_DIR - - script will generate workspace.mel in OUTPUT_DIR - - script will copy original Ada.dna file to OUTPUT_DIR - -Expected: script will generate . -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - - -from os import makedirs -from os import path as ospath -from shutil import copyfile - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" -EXAMPLES_DIR = f"{ROOT_DIR}/examples" -ROOT_LIB_DIR = f"{ROOT_DIR}/lib" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -ANALOG_GUI = f"{DATA_DIR}/analog_gui.ma" -GUI = f"{DATA_DIR}/gui.ma" -ADDITIONAL_ASSEMBLE_SCRIPT = f"{DATA_DIR}/additional_assemble_script.py" - - -from maya import cmds, mel - -from dna_viewer import DNA, RigConfig, build_rig - - -if __name__ == "__main__": - dna = DNA(CHARACTER_DNA) - - makedirs(OUTPUT_DIR, exist_ok=True) - - # This fixes warning when calling this script with headless maya Warning: line 1: Unknown object type: HIKCharacterNode - mel.eval(f"HIKCharacterControlsTool;") - - # Generate workspace.mel - mel.eval(f'setProject "{OUTPUT_DIR}";') - - config = RigConfig( - gui_path=GUI, - analog_gui_path=ANALOG_GUI, - aas_path=ADDITIONAL_ASSEMBLE_SCRIPT, - ) - - # Creates the rig - build_rig(dna=dna, config=config) - # Renames and saves the scene - cmds.file(rename=f"{OUTPUT_DIR}/{CHARACTER_NAME}.mb") - cmds.file(save=True) - # Copy dna file and workspace file alongside generated scene - copyfile(CHARACTER_DNA, f"{OUTPUT_DIR}/{CHARACTER_NAME}.dna") diff --git a/examples/dna_viewer_build_rig_with_textures.py b/examples/dna_viewer_build_rig_with_textures.py deleted file mode 100644 index 81a7200..0000000 --- a/examples/dna_viewer_build_rig_with_textures.py +++ /dev/null @@ -1,355 +0,0 @@ -""" -This example demonstrates generating functional rig based on DNA file in Maya scene with applied textures. -Maps added in data folder belong to Ada preset character. User should use source data downloaded from Quixel Bridge. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - mayapy dna_viewer_build_rig_with_textures.py - NOTE: Script cannot be called with Python, it must be called with mayapy. - -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. -""" -import time -import os - -# if you use Maya, use absolute path -ROOT_DIR = f"{os.path.dirname(os.path.abspath(__file__))}/..".replace("\\", "/") -DATA_DIR = f"{ROOT_DIR}/data" - -from maya import cmds, mel -from dna_viewer import ( - DNA, - RigConfig, - build_rig -) - -ORIENT_Y = [0.0, 0.0, 0.0] -COMMON_MAP_INFOS = [ - ("dx11_diffuseIrradiance", 1), - ("dx11_jitter", 1), - ("dx11_skinLUT", 1), - ("dx11_specularIrradiance", 1), -] - -MAP_INFOS = [ - ("head_color", 1), - ("head_cm1_color", 0), - ("head_cm2_color", 0), - ("head_cm3_color", 0), - ("head_normal", 1), - ("head_wm1_normal", 0), - ("head_wm2_normal", 0), - ("head_wm3_normal", 0), - ("head_specular", 1), - ("head_specular_16Bits", 1), - ("head_occlusion", 1), - ("head_occlusion_16Bits", 1), - ("head_cavity", 1), - ("head_cavity_16Bits", 1), - ("head_transmission", 1), - ("head_transmission_16Bits", 1), - ("head_curvature", 1), - ("head_curvature_16Bits", 1), - ("head_position", 1), - ("head_position_16Bits", 1), - ("head_worldspace", 1), - ("head_worldspace_16Bits", 1), - ("head_bentNormal", 1), - ("head_bentNormal_16Bits", 1), - ("teeth_color", 1), - ("teeth_normal", 1), - ("eyes_color", 1), - ("eyeLeft_color", 1), - ("eyeRight_color", 1), - ("eyeLeft_normal", 1), - ("eyeRight_normal", 1), - ("eyes_color_16Bits", 1), - ("eyes_normal", 1), - ("eyes_normal_16Bits", 1), - ("eyelashes_color", 1), -] - -MASKS = [ - "head_wm1_blink_L", - "head_wm1_blink_R", - "head_wm1_browsRaiseInner_L", - "head_wm1_browsRaiseInner_R", - "head_wm1_browsRaiseOuter_L", - "head_wm1_browsRaiseOuter_R", - "head_wm1_chinRaise_L", - "head_wm1_chinRaise_R", - "head_wm1_jawOpen", - "head_wm1_purse_DL", - "head_wm1_purse_DR", - "head_wm1_purse_UL", - "head_wm1_purse_UR", - "head_wm1_squintInner_L", - "head_wm1_squintInner_R", - "head_wm2_browsDown_L", - "head_wm2_browsDown_R", - "head_wm2_browsLateral_L", - "head_wm2_browsLateral_R", - "head_wm2_mouthStretch_L", - "head_wm2_mouthStretch_R", - "head_wm2_neckStretch_L", - "head_wm2_neckStretch_R", - "head_wm2_noseWrinkler_L", - "head_wm2_noseWrinkler_R", - "head_wm3_cheekRaiseInner_L", - "head_wm3_cheekRaiseInner_R", - "head_wm3_cheekRaiseOuter_L", - "head_wm3_cheekRaiseOuter_R", - "head_wm3_cheekRaiseUpper_L", - "head_wm3_cheekRaiseUpper_R", - "head_wm3_smile_L", - "head_wm3_smile_R", - "head_wm13_lips_DL", - "head_wm13_lips_DR", - "head_wm13_lips_UL", - "head_wm13_lips_UR", -] - -MESH_SHADER_MAPPING = { - "head_lod": "head_shader", - "teeth_lod": "teeth_shader", - "saliva_lod": "saliva_shader", - "eyeLeft_lod": "eyeLeft_shader", - "eyeRight_lod": "eyeRight_shader", - "eyeshell_lod": "eyeshell_shader", - "eyelashes_lod": "eyelashes_shader", - "eyelashesShadow_lod": "eyelashesShadow_shader", - "eyeEdge_lod": "eyeEdge_shader", - "cartilage_lod": "eyeEdge_shader", -} - -SHADERS = ["head_shader", "teeth_shader", "eyeLeft_shader", "eyeRight_shader"] - -SHADER_ATTRIBUTES_MAPPING = { - "FRM_WMmultipliers.head_cm2_color_head_wm2_browsDown_L": "shader_head_shader.maskWeight_00", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_browsDown_L": "shader_head_shader.maskWeight_01", - "FRM_WMmultipliers.head_cm2_color_head_wm2_browsDown_R": "shader_head_shader.maskWeight_02", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_browsDown_R": "shader_head_shader.maskWeight_03", - "FRM_WMmultipliers.head_cm2_color_head_wm2_browsLateral_L": "shader_head_shader.maskWeight_04", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_browsLateral_L": "shader_head_shader.maskWeight_05", - "FRM_WMmultipliers.head_cm2_color_head_wm2_browsLateral_R": "shader_head_shader.maskWeight_06", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_browsLateral_R": "shader_head_shader.maskWeight_07", - "FRM_WMmultipliers.head_cm1_color_head_wm1_browsRaiseInner_L": "shader_head_shader.maskWeight_08", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_browsRaiseInner_L": "shader_head_shader.maskWeight_09", - "FRM_WMmultipliers.head_cm1_color_head_wm1_browsRaiseInner_R": "shader_head_shader.maskWeight_10", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_browsRaiseInner_R": "shader_head_shader.maskWeight_11", - "FRM_WMmultipliers.head_cm1_color_head_wm1_browsRaiseOuter_L": "shader_head_shader.maskWeight_12", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_browsRaiseOuter_L": "shader_head_shader.maskWeight_13", - "FRM_WMmultipliers.head_cm1_color_head_wm1_browsRaiseOuter_R": "shader_head_shader.maskWeight_14", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_browsRaiseOuter_R": "shader_head_shader.maskWeight_15", - "FRM_WMmultipliers.head_cm1_color_head_wm1_blink_L": "shader_head_shader.maskWeight_16", - "FRM_WMmultipliers.head_cm1_color_head_wm1_squintInner_L": "shader_head_shader.maskWeight_17", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_blink_L": "shader_head_shader.maskWeight_18", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_squintInner_L": "shader_head_shader.maskWeight_19", - "FRM_WMmultipliers.head_cm1_color_head_wm1_blink_R": "shader_head_shader.maskWeight_20", - "FRM_WMmultipliers.head_cm1_color_head_wm1_squintInner_R": "shader_head_shader.maskWeight_21", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_blink_R": "shader_head_shader.maskWeight_22", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_squintInner_R": "shader_head_shader.maskWeight_23", - "FRM_WMmultipliers.head_cm3_color_head_wm3_cheekRaiseInner_L": "shader_head_shader.maskWeight_24", - "FRM_WMmultipliers.head_cm3_color_head_wm3_cheekRaiseOuter_L": "shader_head_shader.maskWeight_25", - "FRM_WMmultipliers.head_cm3_color_head_wm3_cheekRaiseUpper_L": "shader_head_shader.maskWeight_26", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_cheekRaiseInner_L": "shader_head_shader.maskWeight_27", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_cheekRaiseOuter_L": "shader_head_shader.maskWeight_28", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_cheekRaiseUpper_L": "shader_head_shader.maskWeight_29", - "FRM_WMmultipliers.head_cm3_color_head_wm3_cheekRaiseInner_R": "shader_head_shader.maskWeight_30", - "FRM_WMmultipliers.head_cm3_color_head_wm3_cheekRaiseOuter_R": "shader_head_shader.maskWeight_31", - "FRM_WMmultipliers.head_cm3_color_head_wm3_cheekRaiseUpper_R": "shader_head_shader.maskWeight_32", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_cheekRaiseInner_R": "shader_head_shader.maskWeight_33", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_cheekRaiseOuter_R": "shader_head_shader.maskWeight_34", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_cheekRaiseUpper_R": "shader_head_shader.maskWeight_35", - "FRM_WMmultipliers.head_cm2_color_head_wm2_noseWrinkler_L": "shader_head_shader.maskWeight_36", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_noseWrinkler_L": "shader_head_shader.maskWeight_37", - "FRM_WMmultipliers.head_cm2_color_head_wm2_noseWrinkler_R": "shader_head_shader.maskWeight_38", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_noseWrinkler_R": "shader_head_shader.maskWeight_39", - "FRM_WMmultipliers.head_cm3_color_head_wm3_smile_L": "shader_head_shader.maskWeight_40", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_smile_L": "shader_head_shader.maskWeight_41", - "FRM_WMmultipliers.head_cm1_color_head_wm13_lips_UL": "shader_head_shader.maskWeight_42", - "FRM_WMmultipliers.head_cm1_color_head_wm13_lips_UR": "shader_head_shader.maskWeight_43", - "FRM_WMmultipliers.head_cm1_color_head_wm13_lips_DL": "shader_head_shader.maskWeight_44", - "FRM_WMmultipliers.head_cm1_color_head_wm13_lips_DR": "shader_head_shader.maskWeight_45", - "FRM_WMmultipliers.head_wm1_normal_head_wm13_lips_UL": "shader_head_shader.maskWeight_46", - "FRM_WMmultipliers.head_wm1_normal_head_wm13_lips_UR": "shader_head_shader.maskWeight_47", - "FRM_WMmultipliers.head_wm1_normal_head_wm13_lips_DL": "shader_head_shader.maskWeight_48", - "FRM_WMmultipliers.head_wm1_normal_head_wm13_lips_DR": "shader_head_shader.maskWeight_49", - "FRM_WMmultipliers.head_cm3_color_head_wm3_smile_R": "shader_head_shader.maskWeight_50", - "FRM_WMmultipliers.head_wm3_normal_head_wm3_smile_R": "shader_head_shader.maskWeight_51", - "FRM_WMmultipliers.head_cm3_color_head_wm13_lips_UL": "shader_head_shader.maskWeight_52", - "FRM_WMmultipliers.head_cm3_color_head_wm13_lips_DL": "shader_head_shader.maskWeight_53", - "FRM_WMmultipliers.head_wm3_normal_head_wm13_lips_UL": "shader_head_shader.maskWeight_54", - "FRM_WMmultipliers.head_wm3_normal_head_wm13_lips_DL": "shader_head_shader.maskWeight_55", - "FRM_WMmultipliers.head_cm3_color_head_wm13_lips_UR": "shader_head_shader.maskWeight_56", - "FRM_WMmultipliers.head_cm3_color_head_wm13_lips_DR": "shader_head_shader.maskWeight_57", - "FRM_WMmultipliers.head_wm3_normal_head_wm13_lips_UR": "shader_head_shader.maskWeight_58", - "FRM_WMmultipliers.head_wm3_normal_head_wm13_lips_DR": "shader_head_shader.maskWeight_59", - "FRM_WMmultipliers.head_cm2_color_head_wm2_mouthStretch_L": "shader_head_shader.maskWeight_60", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_mouthStretch_L": "shader_head_shader.maskWeight_61", - "FRM_WMmultipliers.head_cm2_color_head_wm2_mouthStretch_R": "shader_head_shader.maskWeight_62", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_mouthStretch_R": "shader_head_shader.maskWeight_63", - "FRM_WMmultipliers.head_cm1_color_head_wm1_purse_UL": "shader_head_shader.maskWeight_64", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_purse_UL": "shader_head_shader.maskWeight_65", - "FRM_WMmultipliers.head_cm1_color_head_wm1_purse_UR": "shader_head_shader.maskWeight_66", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_purse_UR": "shader_head_shader.maskWeight_67", - "FRM_WMmultipliers.head_cm1_color_head_wm1_purse_DL": "shader_head_shader.maskWeight_68", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_purse_DL": "shader_head_shader.maskWeight_69", - "FRM_WMmultipliers.head_cm1_color_head_wm1_purse_DR": "shader_head_shader.maskWeight_70", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_purse_DR": "shader_head_shader.maskWeight_71", - "FRM_WMmultipliers.head_cm1_color_head_wm1_chinRaise_L": "shader_head_shader.maskWeight_72", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_chinRaise_L": "shader_head_shader.maskWeight_73", - "FRM_WMmultipliers.head_cm1_color_head_wm1_chinRaise_R": "shader_head_shader.maskWeight_74", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_chinRaise_R": "shader_head_shader.maskWeight_75", - "FRM_WMmultipliers.head_cm1_color_head_wm1_jawOpen": "shader_head_shader.maskWeight_76", - "FRM_WMmultipliers.head_wm1_normal_head_wm1_jawOpen": "shader_head_shader.maskWeight_77", - "FRM_WMmultipliers.head_cm2_color_head_wm2_neckStretch_L": "shader_head_shader.maskWeight_78", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_neckStretch_L": "shader_head_shader.maskWeight_79", - "FRM_WMmultipliers.head_cm2_color_head_wm2_neckStretch_R": "shader_head_shader.maskWeight_80", - "FRM_WMmultipliers.head_wm2_normal_head_wm2_neckStretch_R": "shader_head_shader.maskWeight_81", -} - - -# Methods -def import_head_shaders(shader_scene_path, shaders_dir_path, masks_dir_path, maps_dir_path): - import_shader(shader_scene_path, MESH_SHADER_MAPPING) - - resolve_scene_shader_paths(SHADERS, shaders_dir_path) - - set_mask_textures(MASKS, masks_dir_path) - set_map_textures(COMMON_MAP_INFOS, maps_dir_path) - set_map_textures(MAP_INFOS, maps_dir_path) - - connect_attributes_to_shader(SHADER_ATTRIBUTES_MAPPING) - - -def import_shader(shader_scene_path, mesh_shader_mapping): - print("Shader scene imported") - cmds.file(shader_scene_path, options="v=0", type="mayaAscii", i=True) - try: - items = mesh_shader_mapping.iteritems() - except: - items = mesh_shader_mapping.items() - for meshName, shaderName in items: - for lodLvl in range(0, 8): - try: - # Apply shader to all meshes based on LOD level - resolved_mesh_name = meshName + str(lodLvl) + "_mesh" - shader = "shader_" + shaderName - cmds.select(resolved_mesh_name, replace=True) - mel.eval("sets -e -forceElement " + shader + "SG") - except: - print("Skipped adding shader for mesh %s." % meshName) - - -def resolve_scene_shader_paths(shaders, folder_name): - for shader in shaders: - node_name = "shader_" + shader - if not cmds.objExists(node_name): - continue - file_shader_name = cmds.getAttr(node_name + ".shader") - shader_folder_name, shader_file_name = os.path.split(file_shader_name) - shader_folder_name = folder_name - cmds.setAttr( - node_name + ".shader", - shader_folder_name + "/" + shader_file_name, - type="string", - ) - - -def set_mask_textures(masks, folder_name): - for mask in masks: - node_name = "maskFile_" + mask - - if not cmds.objExists(node_name): - continue - - file_texture_name = cmds.getAttr(node_name + ".fileTextureName") - texture_folder_name, texture_file_name = os.path.split(file_texture_name) - texture_folder_name = folder_name - cmds.setAttr( - node_name + ".fileTextureName", - texture_folder_name + "/" + texture_file_name, - type="string", - ) - - -def set_map_textures(map_infos, folder_name): - for mapInfo in map_infos: - node_name = "mapFile_" + mapInfo[0] - if mapInfo[1]: - node_name = "baseMapFile_" + mapInfo[0] - - if not cmds.objExists(node_name): - continue - - file_texture_name = cmds.getAttr(node_name + ".fileTextureName") - texture_folder_name, texture_file_name = os.path.split(file_texture_name) - texture_folder_name = folder_name - cmds.setAttr( - node_name + ".fileTextureName", - texture_folder_name + "/" + texture_file_name, - type="string", - ) - - -def connect_attributes_to_shader(shader_attributes_mapping): - print("Connecting attributes to shader...") - try: - items = shader_attributes_mapping.iteritems() - except Exception as ex: - print(f"Error: {ex}") - items = shader_attributes_mapping.items() - - for frm_attribute, shaderAttribute in items: - if cmds.objExists(frm_attribute) and cmds.objExists(shaderAttribute): - cmds.connectAttr(frm_attribute, shaderAttribute, force=True) - - -def create_lights(lights_file_path, orient): - print("Creating lights...") - cmds.file(lights_file_path, defaultNamespace=True, i=True) - cmds.xform("Lights", ro=orient) - cmds.makeIdentity("Lights", apply=True) - - -# Define all paths -dna_path = f"{DATA_DIR}/mh4/dna_files/Ada.dna" -gui_path = f"{DATA_DIR}/mh4/gui.ma" -aas_path = f"{DATA_DIR}/mh4/additional_assemble_script.py" -ac_path = f"{DATA_DIR}/analog_gui.ma" -light_scene = f"{DATA_DIR}/lights.ma" -shader_scene = f"{DATA_DIR}/shader.ma" -shaders_dir = f"{DATA_DIR}/shaders" -masks_dir = f"{DATA_DIR}/masks" -maps_dir = f"{DATA_DIR}/maps" -output_scene = f"{ROOT_DIR}/output/Ada_rig.mb" - -try: - start_time = time.time() - - # open new scene - cmds.file(new=True, force=True) - - # import DNA data - dna = DNA(dna_path) - - config = RigConfig( - gui_path=gui_path, - analog_gui_path=ac_path, - aas_path=aas_path, - ) - build_rig(dna=dna, config=config) - import_head_shaders(shader_scene, shaders_dir, masks_dir, maps_dir) - create_lights(light_scene, ORIENT_Y) - - # save scene - cmds.file(rename=output_scene) - cmds.file(save=True, type="mayaBinary") - - print("--- Import finished in %s seconds ---" % (time.time() - start_time)) -except Exception as ex: - print("Error building scene", ex) diff --git a/examples/dna_viewer_export_fbx.py b/examples/dna_viewer_export_fbx.py deleted file mode 100644 index b5a8ac0..0000000 --- a/examples/dna_viewer_export_fbx.py +++ /dev/null @@ -1,300 +0,0 @@ -""" -This example demonstrates generating functional rig in maya scene and exporting fbx per lod. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dna_viewer_export_fbx.py - mayapy dna_viewer_export_fbx.py - NOTE: Script cannot be called with Python, it must be called with mayapy. - -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: -- change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files. If you change name to Taro, - or some other Masculine character, you need to change BODY_FILE with value f"{BODY_DIR}/masc_skeleton.ma" - - change ADD_COLOR_VERTEX to True, if you want to import fbx in Unreal Engine with painted vertices for fallowing cases: - - vertex normals that are going to be updated during import in Unreal Engine, its vertices must be painted with green color. - - for potential future GeneSplicer usage, skinwights on vertices which will need update in character mixing process, must be painted with blue color. - - change UP_AXIS in order change up axis, it can be 'z' or 'y', if put any value is put, ValueError will be raised - -Expected: - - script will generate fbx files Ada_lodX.mb where X are values from 0 to 7, in OUTPUT_DIR - - script will generate workspace.mel in OUTPUT_DIR - -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - - -from os import makedirs -from os import path as ospath -from pathlib import Path - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" -EXAMPLES_DIR = f"{ROOT_DIR}/examples" -ROOT_LIB_DIR = f"{ROOT_DIR}/lib" -DATA_DIR = f"{ROOT_DIR}/data" - - -# Setting constants that will be used -FACIAL_ROOT_NAME = "FACIAL_C_FacialRoot" -CHARACTER_NAME = "Ada" - -ADD_COLOR_VERTEX = False - -DNA_DIR = f"{DATA_DIR}/dna_files" -BODY_DIR = f"{DATA_DIR}/body" -CHARACTER_DNA = f"{DNA_DIR}/{CHARACTER_NAME}.dna" - -UP_AXIS = "z" -if UP_AXIS not in ("z", "y"): - raise ValueError("UP_AXIS can be 'z' or 'y'") - - -BODY_FILE = f"{BODY_DIR}/fem_skeleton.ma" -ADD_MESH_NAME_TO_BLEND_SHAPE_CHANNEL_NAME = True - -FACIAL_ROOT_JOINTS = ["FACIAL_C_FacialRoot", "FACIAL_C_Neck1Root", "FACIAL_C_Neck2Root"] -NECK_JOINTS = ["head", "neck_01", "neck_02"] -ROOT_JOINT = "spine_04" - -from dna import ( - BinaryStreamReader, - BinaryStreamWriter, - DataLayer_All, - FileStream, - Status, -) -from dnacalib import DNACalibDNAReader, RotateCommand -from maya import cmds, mel -from vtx_color import MESH_SHADER_MAPPING, VTX_COLOR_MESHES, VTX_COLOR_VALUES - -from dna_viewer import ( - DNA, - Config, - build_meshes, - get_skin_weights_from_scene, - set_skin_weights_to_scene, -) - - -def load_dna_reader(): - stream = FileStream( - CHARACTER_DNA, 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 - - -def save_dna(reader): - stream = FileStream( - f"{CHARACTER_DNA}.rotate.dna", - FileStream.AccessMode_Write, - FileStream.OpenMode_Binary, - ) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def prepare_rotated_dna(): - reader = load_dna_reader() - - # Copies DNA contents and will serve as input/output parameter to commands - calibrated = DNACalibDNAReader(reader) - - # Modifies calibrated DNA in-place - rotate = RotateCommand([90.0, 0.0, 0.0], [0.0, 0.0, 0.0]) - rotate.run(calibrated) - - save_dna(calibrated) - return DNA(f"{CHARACTER_DNA}.rotate.dna") - - -def get_dna(): - if UP_AXIS == "z": - return prepare_rotated_dna() - return DNA(CHARACTER_DNA) - - -def cleanup(): - path = Path(f"{CHARACTER_DNA}.rotate.dna") - if path.exists(): - path.unlink() - - -def build_meshes_for_lod(dna, lod): - # Create config - config = Config( - group_by_lod=False, - create_display_layers=False, - lod_filter=[lod], - add_mesh_name_to_blend_shape_channel_name=ADD_MESH_NAME_TO_BLEND_SHAPE_CHANNEL_NAME, - ) - - # Builds and returns the created mesh paths in the scene - return build_meshes(dna, config) - - -def create_skin_cluster(influences, mesh, skin_cluster_name, maximum_influences): - cmds.select(influences[0], replace=True) - cmds.select(mesh, add=True) - skinCluster = cmds.skinCluster( - toSelectedBones=True, - name=skin_cluster_name, - maximumInfluences=maximum_influences, - skinMethod=0, - obeyMaxInfluences=True, - ) - if len(influences) > 1: - cmds.skinCluster( - skinCluster, edit=True, addInfluence=influences[1:], weight=0.0 - ) - return skinCluster - - -def create_head_and_body_scene(mesh_names): - scene_mesh_names = [] - skinweights = [] - - for mesh_name in mesh_names: - if cmds.objExists(mesh_name): - scene_mesh_names.append(mesh_name) - skinweights.append(get_skin_weights_from_scene(mesh_name)) - cmds.delete(f"{mesh_name}_skinCluster") - - for facial_joint in FACIAL_ROOT_JOINTS: - cmds.parent(facial_joint, world=True) - cmds.delete(ROOT_JOINT) - - cmds.file(BODY_FILE, options="v=0", type="mayaAscii", i=True) - if UP_AXIS == "y": - cmds.joint("root", edit=True, orientation=[-90.0, 0.0, 0.0]) - for facial_joint, neck_joint in zip(FACIAL_ROOT_JOINTS, NECK_JOINTS): - cmds.parent(facial_joint, neck_joint) - - for mesh_name, skinweight in zip(scene_mesh_names, skinweights): - create_skin_cluster( - skinweight.joints, - mesh_name, - f"{mesh_name}_skinCluster", - skinweight.no_of_influences, - ) - set_skin_weights_to_scene(mesh_name, skinweight) - - -def set_fbx_options(): - # Executes FBX relate commands from the imported plugin - min_time = cmds.playbackOptions(minTime=True, query=True) - max_time = cmds.playbackOptions(maxTime=True, query=True) - - cmds.FBXResetExport() - mel.eval("FBXExportBakeComplexAnimation -v true") - mel.eval(f"FBXExportBakeComplexStart -v {min_time}") - mel.eval(f"FBXExportBakeComplexEnd -v {max_time}") - mel.eval("FBXExportConstraints -v true") - mel.eval("FBXExportSkeletonDefinitions -v true") - mel.eval("FBXExportInputConnections -v true") - mel.eval("FBXExportSmoothingGroups -v true") - mel.eval("FBXExportSkins -v true") - mel.eval("FBXExportShapes -v true") - mel.eval("FBXExportCameras -v false") - mel.eval("FBXExportLights -v false") - cmds.FBXExportUpAxis(UP_AXIS) - # Deselects objects in Maya - cmds.select(clear=True) - - -def create_shader(name): - cmds.shadingNode("blinn", asShader=True, name=name) - - shading_group = str( - cmds.sets( - renderable=True, - noSurfaceShader=True, - empty=True, - name=f"{name}SG", - ) - ) - cmds.connectAttr(f"{name}.outColor", f"{shading_group}.surfaceShader") - return shading_group - - -def add_shader(): - for shader_name, meshes in MESH_SHADER_MAPPING.items(): - shading_group = create_shader(shader_name) - for mesh in meshes: - try: - cmds.select(mesh, replace=True) - cmds.sets(edit=True, forceElement=shading_group) - except Exception as e: - print(f"Skipped adding shader for mesh {mesh}. Reason {e}") - - -def set_vertex_color(): - for m, meshName in enumerate(VTX_COLOR_MESHES): - try: - cmds.select(meshName) - except Exception as e: - print(f"Skipped adding vtx color for mesh {meshName}. Reason {e}") - continue - for v, rgb in enumerate(VTX_COLOR_VALUES[m]): - cmds.polyColorPerVertex(f"{meshName}.vtx[{v}]", g=rgb[1], b=rgb[2]) - - -def export_fbx(lod, meshes): - # Selects every mesh in the given lod - for item in meshes: - cmds.select(item, add=True) - # Adds facial root joint to selection - cmds.select(FACIAL_ROOT_NAME, add=True) - # Sets the file path - export_file_name = f"{OUTPUT_DIR}/{CHARACTER_NAME}_lod{lod}.fbx" - # Exports the fbx - mel.eval(f'FBXExport -f "{export_file_name}" -s true') - - -def export_fbx_for_lod(dna, lod): - # Creates the meshes for the given lod - result = build_meshes_for_lod(dna, lod) - meshes = result.get_all_meshes() - # Executes FBX relate commands from the imported plugin - create_head_and_body_scene(meshes) - set_fbx_options() - # Saves the result - if ADD_COLOR_VERTEX: - add_shader() - set_vertex_color() - export_fbx(lod, meshes) - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - - # Loads the builtin plugin needed for FBX - cmds.loadPlugin("fbxmaya.mll") - - # This fixes warning when calling this script with headless maya Warning: line 1: Unknown object type: HIKCharacterNode - mel.eval(f"HIKCharacterControlsTool;") - - # Generate workspace.mel - mel.eval(f'setProject "{OUTPUT_DIR}";') - - # Export FBX for each lod - cmds.upAxis(ax=UP_AXIS) - dna = get_dna() - for lod in range(dna.get_lod_count()): - export_fbx_for_lod(dna, lod) - cleanup() diff --git a/examples/dna_viewer_grab_changes_from_scene_and_propagate_to_dna.py b/examples/dna_viewer_grab_changes_from_scene_and_propagate_to_dna.py deleted file mode 100644 index b226f9a..0000000 --- a/examples/dna_viewer_grab_changes_from_scene_and_propagate_to_dna.py +++ /dev/null @@ -1,247 +0,0 @@ -""" -This example demonstrates how to propagate changes from maya scene to dna file. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -Follow the steps: - -1. Start Maya -2. open maya scene (do 2.1 or 2.2) -2.1. Generate new scene using build_meshes or -2.2. start DNA Viewer GUI (dna_viewer_run_in_maya.py) - - Select DNA file that you want to load and generate scene for - - Select meshes that you want to change - - Tick joints in Build Options - - Click Process - - in Maya scene rig is going to be assembled -3. Run this script to the part called "load data" - a. get current vertex positions for all meshes - -4. In the scene, make modifications to the neutral mesh and joints (important note: - if you're rotating joints, be sure to freeze transformations, so they're stored as orientations) -5. Run this script from the part called "propagate changes to dna" to the end - a. set new joints translations - b. set new joints rotations - c. move all meshes vertices to new positions - -After performing this steps, your changes in maya scene will pe propagated to dna. - -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - -Expected: - - script will generate dna file _modified.dna in OUTPUT_DIR, e.g. Ada_modified.dna - - script will generate maya scene _modified.mb in OUTPUT_DIR, e.g. Ada_modified.mb - - script will generate workspace.mel in OUTPUT_DIR - -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - -import maya.OpenMaya as om -from maya import cmds - -from os import makedirs -from os import path as ospath - -CHARACTER_NAME = "Ada" - -# If you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" -ROOT_LIB_DIR = f"{ROOT_DIR}/lib" -DATA_DIR = f"{ROOT_DIR}/data" -DNA_DIR = f"{DATA_DIR}/dna_files" -CHARACTER_DNA = f"{DNA_DIR}/{CHARACTER_NAME}.dna" -ANALOG_GUI = f"{DATA_DIR}/analog_gui.ma" -GUI = f"{DATA_DIR}/gui.ma" -ADDITIONAL_ASSEMBLE_SCRIPT = f"{DATA_DIR}/additional_assemble_script.py" -ADD_MESH_NAME_TO_BLEND_SHAPE_CHANNEL_NAME = True - -MODIFIED_CHARACTER_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_modified" - -from dna import ( - BinaryStreamReader, - BinaryStreamWriter, - DataLayer_All, - FileStream, - Status, -) -from dnacalib import ( - CommandSequence, - DNACalibDNAReader, - SetNeutralJointRotationsCommand, - SetNeutralJointTranslationsCommand, - SetVertexPositionsCommand, - VectorOperation_Add, -) - -from dna_viewer import DNA, RigConfig, build_rig, build_meshes - - -def load_dna_reader(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 - - -def save_dna(reader): - stream = FileStream( - f"{MODIFIED_CHARACTER_DNA}.dna", - FileStream.AccessMode_Write, - FileStream.OpenMode_Binary, - ) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def get_mesh_vertex_positions_from_scene(meshName): - try: - sel = om.MSelectionList() - sel.add(meshName) - - dag_path = om.MDagPath() - sel.getDagPath(0, dag_path) - - mf_mesh = om.MFnMesh(dag_path) - positions = om.MPointArray() - - mf_mesh.getPoints(positions, om.MSpace.kObject) - return [ - [positions[i].x, positions[i].y, positions[i].z] - for i in range(positions.length()) - ] - except RuntimeError: - print(f"{meshName} is missing, skipping it") - return None - - -def run_joints_command(reader, calibrated): - # Making arrays for joints' transformations and their corresponding mapping arrays - joint_translations = [] - joint_rotations = [] - - for i in range(reader.getJointCount()): - joint_name = reader.getJointName(i) - - translation = cmds.xform(joint_name, query=True, translation=True) - joint_translations.append(translation) - - rotation = cmds.joint(joint_name, query=True, orientation=True) - joint_rotations.append(rotation) - - # This is step 5 sub-step a - set_new_joints_translations = SetNeutralJointTranslationsCommand(joint_translations) - # This is step 5 sub-step b - set_new_joints_rotations = SetNeutralJointRotationsCommand(joint_rotations) - - # Abstraction to collect all commands into a sequence, and run them with only one invocation - commands = CommandSequence() - # Add vertex position deltas (NOT ABSOLUTE VALUES) onto existing vertex positions - commands.add(set_new_joints_translations) - commands.add(set_new_joints_rotations) - - commands.run(calibrated) - # Verify that everything went fine - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error run_joints_command: {status.message}") - - -def run_vertices_command( - calibrated, old_vertices_positions, new_vertices_positions, mesh_index -): - # Making deltas between old vertices positions and new one - deltas = [] - for new_vertex, old_vertex in zip(new_vertices_positions, old_vertices_positions): - delta = [] - for new, old in zip(new_vertex, old_vertex): - delta.append(new - old) - deltas.append(delta) - - # This is step 5 sub-step c - new_neutral_mesh = SetVertexPositionsCommand( - mesh_index, deltas, VectorOperation_Add - ) - commands = CommandSequence() - # Add nex vertex position deltas (NOT ABSOLUTE VALUES) onto existing vertex positions - commands.add(new_neutral_mesh) - commands.run(calibrated) - - # Verify that everything went fine - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error run_vertices_command: {status.message}") - - -def assemble_maya_scene(): - dna = DNA(f"{MODIFIED_CHARACTER_DNA}.dna") - config = RigConfig( - gui_path=f"{DATA_DIR}/gui.ma", - analog_gui_path=f"{DATA_DIR}/analog_gui.ma", - aas_path=ADDITIONAL_ASSEMBLE_SCRIPT, - ) - build_rig(dna=dna, config=config) - - cmds.file(rename=f"{MODIFIED_CHARACTER_DNA}.mb") - cmds.file(save=True) - - -makedirs(OUTPUT_DIR, exist_ok=True) - -dna = DNA(CHARACTER_DNA) -################################## -# This is step 2 sub-step 1 -# use this block only if you need to assemble of scene from step 2.1 -# config = RigConfig( -# gui_path=f"{DATA_DIR}/gui.ma", -# analog_gui_path=f"{DATA_DIR}/analog_gui.ma", -# aas_path=ADDITIONAL_ASSEMBLE_SCRIPT, -# ) -# build_meshes(dna=dna, config=config) -# This is end of step 2 sub-step 1 -################################## - - -# This is step 3 sub-step a -current_vertices_positions = {} -mesh_indices = [] -for mesh_index, name in enumerate(dna.meshes.names): - current_vertices_positions[name] = { - "mesh_index": mesh_index, - "positions": get_mesh_vertex_positions_from_scene(name), - } -# Loaded data - end of 3rd step -################################## - -################################## -# Modify rig in maya, 4th step -################################## - -################################## -# Propagate changes to dna, 5th step -reader = load_dna_reader(CHARACTER_DNA) -calibrated = DNACalibDNAReader(reader) - -run_joints_command(reader, calibrated) - -for name, item in current_vertices_positions.items(): - new_vertices_positions = get_mesh_vertex_positions_from_scene(name) - if new_vertices_positions: - run_vertices_command( - calibrated, item["positions"], new_vertices_positions, item["mesh_index"] - ) -save_dna(calibrated) -assemble_maya_scene() diff --git a/examples/dna_viewer_run_in_maya.py b/examples/dna_viewer_run_in_maya.py deleted file mode 100644 index bffb2f2..0000000 --- a/examples/dna_viewer_run_in_maya.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -This example demonstrates Maya UI Window for simple and non-programmatic creation the scene with the creating functional rig. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - NOTE: Script cannot be called with Python or mayapy, it must be called in Maya Script Editor. -- usage in Maya: - Expected: Maya will show UI. - -""" - - -# This example is intended to be used in Maya - -import dna_viewer - -dna_viewer.show() diff --git a/examples/dnacalib_clear_blend_shapes.py b/examples/dnacalib_clear_blend_shapes.py deleted file mode 100644 index 9d8f996..0000000 --- a/examples/dnacalib_clear_blend_shapes.py +++ /dev/null @@ -1,144 +0,0 @@ -""" -This example demonstrates how to remove all blend shape data from a DNA. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dnacalib_clear_blend_shapes.py - mayapy dnacalib_clear_blend_shapes.py -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - -Expected: Script will generate Ada_output.dna in OUTPUT_DIR from original Ada.dna. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - -from os import makedirs -from os import path as ospath - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -OUTPUT_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_output.dna" - - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, BinaryStreamWriter -from dnacalib import ( - DNACalibDNAReader, - ClearBlendShapesCommand -) - - -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 - - -def save_dna(reader, path): - stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def validate_geometry(dna): - mesh_count = dna.getMeshCount() - for mesh_index in range(mesh_count): - bs_tgt_count = dna.getBlendShapeTargetCount(mesh_index) - for bs_tgt_index in range(bs_tgt_count): - bs_tgt_delta_count = dna.getBlendShapeTargetDeltaCount(mesh_index, bs_tgt_index) - if bs_tgt_delta_count != 0: - raise RuntimeError("Blend shape target deltas not removed properly!") - - -def validate_animation_data(dna): - bs_channel_lods = dna.getBlendShapeChannelLODs() - bs_channel_input_indices = dna.getBlendShapeChannelInputIndices() - bs_channel_output_indices = dna.getBlendShapeChannelOutputIndices() - - if len(bs_channel_lods) != dna.getLODCount(): - raise RuntimeError("Blend shape animation data not removed properly! Number of blend shape LODs does not match LOD count!") - - for lod in bs_channel_lods: - if lod != 0: - raise RuntimeError("Blend shape animation data not removed properly!") - - if (len(bs_channel_input_indices) != 0) or (len(bs_channel_output_indices) != 0): - raise RuntimeError("Blend shape animation data not removed properly!") - - -def calibrate_dna(input_path, output_path): - dna = load_dna(input_path) - - # Copies DNA contents and will serve as input/output parameter to commands - calibrated = DNACalibDNAReader(dna) - - mesh_count = calibrated.getMeshCount() - print(f"Number of meshes: {mesh_count}") - - for mesh_index in range(mesh_count): - bs_tgt_count = calibrated.getBlendShapeTargetCount(mesh_index) - print(f"Number of blendshape targets for mesh {calibrated.getMeshName(mesh_index)}({mesh_index}): {bs_tgt_count}") - for bs_tgt_index in range(bs_tgt_count): - bs_tgt_delta_count = calibrated.getBlendShapeTargetDeltaCount(mesh_index, bs_tgt_index) - print(f"Number of blendshape target deltas for mesh {calibrated.getMeshName(mesh_index)}({mesh_index}), blend shape target {bs_tgt_index}: {bs_tgt_delta_count}") - - print(f"Blend shape channel LODs: {calibrated.getBlendShapeChannelLODs()}") - print(f"Blend shape channel input indices: {calibrated.getBlendShapeChannelInputIndices()}") - print(f"Blend shape channel output indices: {calibrated.getBlendShapeChannelOutputIndices()}") - - # Clears all blend shapes - command = ClearBlendShapesCommand() - - print("\n\nClearing blend shape data...\n\n") - # Modifies calibrated DNA in-place - command.run(calibrated) - - validate_geometry(calibrated) - validate_animation_data(calibrated) - - print(f"Number of meshes: {mesh_count}") - - for mesh_index in range(mesh_count): - bs_tgt_count = calibrated.getBlendShapeTargetCount(mesh_index) - print(f"Number of blendshape targets for mesh {calibrated.getMeshName(mesh_index)}({mesh_index}): {bs_tgt_count}") - for bs_tgt_index in range(bs_tgt_count): - bs_tgt_delta_count = calibrated.getBlendShapeTargetDeltaCount(mesh_index, bs_tgt_index) - print(f"Number of blendshape target deltas for mesh {calibrated.getMeshName(mesh_index)}({mesh_index}), blend shape target {bs_tgt_index}: {bs_tgt_delta_count}") - - bs_channel_lods = dna.getBlendShapeChannelLODs() - bs_channel_input_indices = dna.getBlendShapeChannelInputIndices() - bs_channel_output_indices = dna.getBlendShapeChannelOutputIndices() - - print(f"Blend shape channel LODs: {bs_channel_lods}") - print(f"Blend shape channel input indices: {bs_channel_input_indices}") - print(f"Blend shape channel output indices: {bs_channel_output_indices}") - - print("\n\nSuccessfully cleared blend shape data.") - - print("Saving DNA...") - save_dna(calibrated, output_path) - print("Done.") - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - calibrate_dna(CHARACTER_DNA, OUTPUT_DNA) diff --git a/examples/dnacalib_demo.py b/examples/dnacalib_demo.py deleted file mode 100644 index 9448b00..0000000 --- a/examples/dnacalib_demo.py +++ /dev/null @@ -1,154 +0,0 @@ -""" -This example demonstrates a few DNACalib's commands. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dnacalib_demo.py - mayapy dnacalib_demo.py - -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - - -Expected: Script will generate Ada_output.dna in OUTPUT_DIR from original Ada.dna. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - -from os import makedirs -from os import path as ospath - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -OUTPUT_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_output.dna" - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, BinaryStreamWriter -from dnacalib import ( - CommandSequence, - DNACalibDNAReader, - RenameJointCommand, - ScaleCommand, - SetBlendShapeTargetDeltasCommand, - SetVertexPositionsCommand, - VectorOperation_Add, - VectorOperation_Interpolate, -) - - -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 - - -def save_dna(reader, path): - stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def build_command_list(): - # Abstraction to collect all commands into a sequence, and run them with only one invocation - commands = CommandSequence() - - print("Creating a sequence of commands...") - # Instantiate command with parameters: scale-factor = 2 , origin-xyz = (0, 120, 0) - scale_by_two = ScaleCommand(2.0, [0.0, 120.0, 0.0]) - # Alternatively a command can be instantiated empty, and populated with parameters later, e.g.: - # scale_by_two = ScaleCommand() - # scale_by_two.setScale(2.0) - # scale_by_two.setOrigin([0.0, 120.0, 0.0]) - commands.add(scale_by_two) - - print("Added command to scale dna") - # Rename by joint index (faster) - commands.add(RenameJointCommand(10, "NewJointA")) - - # Rename by matching joint name (slower) - commands.add(RenameJointCommand("OldJointB", "NewJointB")) - - print("Added command to rename joint") - # Interpolate blend shape target deltas between original DNA and below specified deltas - # ¯\_(ツ)_/¯ - # Deltas in [[x, y, z], [x, y, z], [x, y, z]] format - blend_shape_target_deltas = [[0.0, 0.0, 2.0], [0.0, -1.0, 4.0], [3.0, -3.0, 8.0]] - vertex_indices = [0, 1, 2] - # Weights for interpolation between original deltas and above defined deltas - # 1.0 == take the new value completely, 0.0 means keep the old value - # Format: [Delta-0-Mask, Delta-1-Mask, Delta-2-Mask] - masks = [1.0, 0.0, 0.5] - set_blend_shapes_m0_b0 = SetBlendShapeTargetDeltasCommand( - 0, # mesh index - 0, # blend shape target index - blend_shape_target_deltas, - vertex_indices, - masks, - VectorOperation_Interpolate, - ) - commands.add(set_blend_shapes_m0_b0) - print("Added command to change blend shape target deltas") - - # Add vertex position deltas onto existing vertex positions - # Note the alternative data format, instead of using nested lists, separate all X, Y, Z - # components into distinct lists (might also be faster) - position_xs = [1.0, -4.5, 7.2] - position_ys = [2.0, -5.5, -8.3] - position_zs = [3.0, -6.5, 9.7] - # Weights to be multiplied with the above specified delta positions, before adding - # them onto the original data - # Format: [Delta-0-Weight, Delta-1-Weight, Delta-2-Weight] - masks = [1.0, 0.2, 0.4] - set_vertices_m0 = SetVertexPositionsCommand( - 0, # mesh index - position_xs, - position_ys, - position_zs, - masks, - VectorOperation_Add, - ) - commands.add(set_vertices_m0) - print("Added command to change vertex positions") - - return commands - - -def calibrate_dna(input_path, output_path): - dna = load_dna(input_path) - - # Copies DNA contents and will serve as input/output parameter to commands - calibrated = DNACalibDNAReader(dna) - - commands = build_command_list() - - print("Running command sequence...") - # Modifies calibrated DNA in-place - commands.run(calibrated) - - print("Saving DNA...") - save_dna(calibrated, output_path) - - print("Done.") - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - calibrate_dna(CHARACTER_DNA, OUTPUT_DNA) diff --git a/examples/dnacalib_lod_demo.py b/examples/dnacalib_lod_demo.py deleted file mode 100644 index 9001ecc..0000000 --- a/examples/dnacalib_lod_demo.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -This example demonstrates creation of a new DNA from an existing one by extracting specific lods. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dnacalib_lod_demo.py - mayapy dnacalib_lod_demo.py - -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - - change value of LODS to list of lods needed to be extracted - -Expected: Script will generate Ada_with_lods_1_and_3.dna in OUTPUT_DIR, from original Ada. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - - -from os import makedirs -from os import path as ospath - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, BinaryStreamWriter -from dnacalib import ( - DNACalibDNAReader, - SetLODsCommand, -) - -# Sets DNA file path -DNA = f"{ROOT_DIR}/data/dna_files/Ada.dna" -# Sets new DNA output file path -DNA_NEW = f"{OUTPUT_DIR}/Ada_with_lods_1_and_3.dna" -# Sets lods to extract -LODS = [1, 3] - - -def save_dna(reader: DNACalibDNAReader, created_dna_path: str): - # Saves the dna - stream = FileStream(created_dna_path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def run_SetLODsCommand(reader): - calibrated = DNACalibDNAReader(reader) - command = SetLODsCommand() - # Set a list of LODs that will be exported to the new file - command.setLODs(LODS) - # Runs the command that reduces LODs of the DNA - command.run(calibrated) - print("Setting new LODs...") - - if calibrated.getLODCount() != 2: - raise RuntimeError("Setting new number of LODs in DNA was unsuccessful!") - - print("\nSuccessfully changed number of LODs in ") - print("Saving ..") - # Save the newly created DNA - save_dna(calibrated, DNA_NEW) - print("Done.") - - -def load_dna_calib(dna_path: str): - # Load the DNA - stream = FileStream(dna_path, FileStream.AccessMode_Read, FileStream.OpenMode_Binary) - reader = BinaryStreamReader(stream, DataLayer_All) - reader.read() - return reader - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - reader = load_dna_calib(DNA) - run_SetLODsCommand(reader) diff --git a/examples/dnacalib_neutral_mesh_subtract.py b/examples/dnacalib_neutral_mesh_subtract.py deleted file mode 100644 index 69c8cad..0000000 --- a/examples/dnacalib_neutral_mesh_subtract.py +++ /dev/null @@ -1,139 +0,0 @@ -""" -This example demonstrates how to subtract values from a neutral mesh and transfer those changes to its lower LOD meshes. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dnacalib_neutral_mesh_subtract.py - mayapy dnacalib_neutral_mesh_subtract.py -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - -Expected: Script will generate Ada_output.dna in OUTPUT_DIR from original Ada.dna. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - -from os import makedirs -from os import path as ospath - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -OUTPUT_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_output.dna" - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, BinaryStreamWriter -from dnacalib import ( - DNACalibDNAReader, - SetVertexPositionsCommand, - VectorOperation_Subtract, - CalculateMeshLowerLODsCommand -) -from math import isclose - - -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 - - -def save_dna(reader, path): - stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def calibrate_dna(input_path, output_path): - dna = load_dna(input_path) - - # Copies DNA contents and will serve as input/output parameter to commands - calibrated = DNACalibDNAReader(dna) - - if calibrated.getMeshCount() == 0: - print("No meshes found in DNA.") - return - - mesh_index = 0 - vtx_count_mesh0 = calibrated.getVertexPositionCount(mesh_index) - xs = calibrated.getVertexPositionXs(mesh_index) - ys = calibrated.getVertexPositionYs(mesh_index) - zs = calibrated.getVertexPositionZs(mesh_index) - - # Example values to subtract from original vertex positions for mesh with index 0 - ones = [1.0] * vtx_count_mesh0 - - # Command used to subtract example values from a specified neutral mesh - subtract_command = SetVertexPositionsCommand() - subtract_command.setMeshIndex(mesh_index) - subtract_command.setOperation(VectorOperation_Subtract) - subtract_command.setPositions(ones, ones, ones) - - # Alternatively, if you wanted to do the opposite, to subtract neutral mesh values - # from example values, you could do the following: - # xs_2 = [x * 2 for x in xs] - # ys_2 = [y * 2 for y in ys] - # zs_2 = [z * 2 for z in zs] - # subtract_command.setPositions(xs_2, ys_2, zs_2) - # # After running the command, the calibrated DNA will contain -xs, -ys, -zs - # subtract_command.run(calibrated) - # - # # Add the example values, which will result in: - # # ones - xs - # # ones - ys - # # ones - zs - # add_command = SetVertexPositionsCommand() - # add_command.setMeshIndex(mesh_index) - # add_command.setOperation(VectorOperation_Add) # remember to add VectorOperation_Add to the list of imports - # add_command.setPositions(ones, ones, ones) - # add_command.run(calibrated) - - print(f"Subtracting values from neutral mesh \'{calibrated.getMeshName(mesh_index)}\'...") - # Modifies calibrated DNA in-place - subtract_command.run(calibrated) - - # Command used to recalculate vertex positions of lower LOD neutral meshes of the specified neutral mesh - calculate_lower_lods_command = CalculateMeshLowerLODsCommand() - calculate_lower_lods_command.setMeshIndex(mesh_index) - - print("Recalculating values for lower LOD meshes...") - # Modifies calibrated DNA in-place - calculate_lower_lods_command.run(calibrated) - - new_xs = calibrated.getVertexPositionXs(mesh_index) - new_ys = calibrated.getVertexPositionYs(mesh_index) - new_zs = calibrated.getVertexPositionZs(mesh_index) - - for i in range(vtx_count_mesh0): - if (not isclose(xs[i] - 1, new_xs[i], rel_tol=1e-7) or - not isclose(ys[i] - 1, new_ys[i], rel_tol=1e-7) or - not isclose(zs[i] - 1, new_zs[i], rel_tol=1e-7)): - raise RuntimeError("Vertex positions were not changed successfully!") - - print("\nSuccessfully changed vertex positions.") - - print("Saving DNA...") - save_dna(calibrated, output_path) - print("Done.") - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - calibrate_dna(CHARACTER_DNA, OUTPUT_DNA) diff --git a/examples/dnacalib_remove_joint.py b/examples/dnacalib_remove_joint.py deleted file mode 100644 index 508cb4b..0000000 --- a/examples/dnacalib_remove_joint.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -This example demonstrates a few DNACalib's commands. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python dnacalib_remove_joint.py - mayapy dnacalib_remove_joint.py -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - -Expected: Script will generate Ada_output.dna in OUTPUT_DIR from original Ada.dna. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - - -from os import makedirs -from os import path as ospath - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -OUTPUT_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_output.dna" - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, BinaryStreamWriter -from dnacalib import ( - DNACalibDNAReader, - RemoveJointCommand, -) - - -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 - - -def save_dna(reader, path): - stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -def get_joints(dna): - joints = [] - for jointIndex in range(dna.getJointCount()): - joints.append(dna.getJointName(jointIndex)) - return joints - - -def calibrate_dna(input_path, output_path): - dna = load_dna(input_path) - - # Copies DNA contents and will serve as input/output parameter to command - calibrated = DNACalibDNAReader(dna) - - original_joints = get_joints(calibrated) - - # An example joint to remove - joint_index = 314 - joint_name = calibrated.getJointName(joint_index) - - # Removes joint with specified index - command = RemoveJointCommand(joint_index) - - # Modifies calibrated DNA in-place - command.run(calibrated) - - modified_joints = get_joints(calibrated) - - if (len(modified_joints) != (len(original_joints) - 1)) or (joint_name in modified_joints): - raise RuntimeError("Joint not removed properly!") - - print(f"Successfully removed joint `{joint_name}`.") - - print("Saving DNA...") - save_dna(calibrated, output_path) - - print("Done.") - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - calibrate_dna(CHARACTER_DNA, OUTPUT_DNA) diff --git a/examples/dnacalib_rename_joint_demo.py b/examples/dnacalib_rename_joint_demo.py deleted file mode 100644 index 6fd6d3c..0000000 --- a/examples/dnacalib_rename_joint_demo.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -This example demonstrates rename of a joint. -IMPORTANT: You have to setup the environment before running this example. Please refer to the 'Environment setup' section in README.md. - -- usage in command line: - python rename_joint_demo.py - mayapy rename_joint_demo.py - - Expected: Script will generate Ada_output.dna in OUTPUT_DIR. -- usage in Maya: - 1. copy whole content of this file to Maya Script Editor - 2. change value of ROOT_DIR to absolute path of dna_calibration, e.g. `c:/dna_calibration` in Windows or `/home/user/dna_calibration`. Important: - Use `/` (forward slash), because Maya uses forward slashes in path. - -- customization: - - change CHARACTER_NAME to Taro, or the name of a custom DNA file placed in /data/dna_files - -Expected: Script will generate Ada_output.dna in OUTPUT_DIR from original Ada.dna. -NOTE: If OUTPUT_DIR does not exist, it will be created. -""" - -from os import makedirs -from os import path as ospath - -# if you use Maya, use absolute path -ROOT_DIR = f"{ospath.dirname(ospath.abspath(__file__))}/..".replace("\\", "/") -OUTPUT_DIR = f"{ROOT_DIR}/output" - -CHARACTER_NAME = "Ada" - -DATA_DIR = f"{ROOT_DIR}/data" -CHARACTER_DNA = f"{DATA_DIR}/dna_files/{CHARACTER_NAME}.dna" -OUTPUT_DNA = f"{OUTPUT_DIR}/{CHARACTER_NAME}_output.dna" - -from dna import DataLayer_All, FileStream, Status, BinaryStreamReader, BinaryStreamWriter -from dnacalib import DNACalibDNAReader, RenameJointCommand - - -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 - - -def save_dna(reader, path): - stream = FileStream(path, FileStream.AccessMode_Write, FileStream.OpenMode_Binary) - writer = BinaryStreamWriter(stream) - writer.setFrom(reader) - writer.write() - - if not Status.isOk(): - status = Status.get() - raise RuntimeError(f"Error saving DNA: {status.message}") - - -if __name__ == "__main__": - makedirs(OUTPUT_DIR, exist_ok=True) - - dna_reader = load_dna(CHARACTER_DNA) - calibrated = DNACalibDNAReader(dna_reader) - # Prints current joint name - print(calibrated.getJointName(10)) - # Creates rename command - rename = RenameJointCommand(10, "NewJointA") - # Executes command - rename.run(calibrated) - # Prints the new joint name - print(calibrated.getJointName(10)) - save_dna(calibrated, OUTPUT_DNA) - print('Done.')