Update
Update
253
docs/dna.md
@ -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 到指定层。如层级组织图所示,行为层和几何层彼此不依赖。这种独立性对于只需要使用 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)
|
|
564
docs/dna_api.md
@ -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)`
|
|
||||||
设置受指定变形目标影响的顶点位置索引。顶点位置索引必须按与其关联的增量相同的顺序存储。
|
|
@ -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 所需的类。
|
|
||||||
|
|
||||||
## 工作原理
|
|
||||||
|
|
||||||
一般流程如下:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
场景构建过程的流程如下:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
骨骼构建过程的流程如下:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
图例:
|
|
||||||
- <span style="color:blue">蓝色: 构建器相关</span>
|
|
||||||
- <span style="color:green">绿色: 配置相关</span>
|
|
||||||
- <span style="color:brown">棕色: 模型相关</span>
|
|
||||||
- <span style="color:purple">紫色: 读取器相关</span>
|
|
@ -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)。
|
|
@ -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,
|
|
||||||
)
|
|
||||||
```
|
|
@ -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)
|
|
||||||
```
|
|
@ -1,13 +0,0 @@
|
|||||||
# 在 Maya 中使用
|
|
||||||
|
|
||||||
还有一个 [Maya UI 窗口](/examples/dna_viewer_run_in_maya.py),可用于以非编程方式创建包含功能性骨骼的场景。
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
在场景生成期间,RigLogic4 插件将自动加载,您可能会看到以下消息:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
点击 `允许` 加载插件。如果启用 **应用于此位置的所有插件** 选项,Maya 将不再显示此通知。
|
|
100
docs/dnacalib.md
@ -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<dnac::FileStream>("example.dna",
|
|
||||||
dnac::FileStream::AccessMode::ReadWrite,
|
|
||||||
dnac::FileStream::OpenMode::Binary);
|
|
||||||
auto reader = dnac::makeScoped<dnac::BinaryStreamReader>(inOutStream.get());
|
|
||||||
reader->read();
|
|
||||||
|
|
||||||
// 检查读取 DNA 文件时是否发生错误
|
|
||||||
if (!dnac::Status::isOk()) {
|
|
||||||
// 处理读取器错误
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 DNACalib 读取器以编辑 DNA
|
|
||||||
auto dnaReader = dnac::makeScoped<dnac::DNACalibDNAReader>(reader.get());
|
|
||||||
|
|
||||||
std::vector<dnac::Vector3> rotations{dnaReader->getJointCount(), {1.0f, 2.0f, 3.0f}};
|
|
||||||
|
|
||||||
// 创建命令实例
|
|
||||||
dnac::SetNeutralJointRotationsCommand cmd{dnac::ConstArrayView<dnac::Vector3>{rotations}};
|
|
||||||
|
|
||||||
// 执行命令
|
|
||||||
cmd.run(dnaReader.get());
|
|
||||||
|
|
||||||
// 写入 DNA 文件
|
|
||||||
auto writer = dnac::makeScoped<dnac::BinaryStreamWriter>(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
|
|
||||||
```
|
|
@ -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/)中找到。
|
|
35
docs/faq.md
@ -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 对象**:
|
|
||||||
- 
|
|
||||||
- 仍在大纲视图中,搜索 `rl4`。您会看到一个名称以 `rl4Embedded_` 开头的文件。点击此文件以选择它。
|
|
||||||
- 
|
|
||||||
- 在 `属性编辑器` 中,您可以通过 `Dna 文件路径` 更改路径:
|
|
||||||
- 
|
|
BIN
docs/img/aas.png
Before Width: | Height: | Size: 517 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 209 KiB |
Before Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 1.7 MiB |
BIN
docs/img/ui.png
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB |
@ -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 |
|
|
||||||
|---|---|
|
|
||||||
||  |
|
|
||||||
|
|
||||||
此外,我们添加了在 Maya 场景组装过程中使用的[`gui`](/data/gui.ma)和[`analog_gui`](/data/analog_gui.ma) Maya 场景文件。
|
|
||||||
另外,[`additional_assemble_script.py`](/data/additional_assemble_script.py)用于组织场景中的对象并连接控制器。理想的设置如下所示:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
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 场景来使用它,我们可以获得更好的颈部变形效果。
|
|
@ -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)
|
|
@ -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)
|
|
@ -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 <PATH TO NEW DNA FILE>.
|
|
||||||
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")
|
|
@ -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)
|
|
@ -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()
|
|
@ -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 <CHARACTER_NAME>_modified.dna in OUTPUT_DIR, e.g. Ada_modified.dna
|
|
||||||
- script will generate maya scene <CHARACTER_NAME>_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()
|
|
@ -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()
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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.')
|
|