PMX格式详解
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
]
開始行:
* PMX格式详解 [#qcc9542a]
&size(13){''PMX 2.0 / 2.1 的全部 chunk 结构与二进制规格''};
#contents
** 什么是 PMX [#t3fa80fc]
''PMX'' 是取代 PMD 的''现行标准模型格式''。
于 2011 年前后登场,如今已成为 MMD 模型事实上的标准。
|~项目|~内容|
|扩展名|.pmx|
|字节序|小端序|
|字符串编码|UTF-16LE 或 UTF-8(由 header 指定)|
|版本|2.0 / 2.1|
|设计|由 PMXEditor 的作者完成|
|特征|全部 chunk 均支持''可变长索引'',可应对大规模模型|
** 整体结构 [#t9d022a5]
PMX 由''chunk 的链式结构''构成。
#code(){{
┌─────────────────────────┐
│ Header │ ← 魔数・版本・设置
├─────────────────────────┤
│ Model Info │ ← 模型名・注释
├─────────────────────────┤
│ Vertex Data │ ← 顶点
├─────────────────────────┤
│ Face Data │ ← 面(顶点索引)
├─────────────────────────┤
│ Texture Data │ ← 贴图的路径
├─────────────────────────┤
│ Material Data │ ← 材质
├─────────────────────────┤
│ Bone Data │ ← 骨骼
├─────────────────────────┤
│ Morph Data │ ← 变形
├─────────────────────────┤
│ Display Frame Data │ ← 显示框
├─────────────────────────┤
│ Rigid Body Data │ ← 刚体
├─────────────────────────┤
│ Joint Data │ ← 关节
├─────────────────────────┤
│ Soft Body Data │ ← 软体(仅 2.1)
└─────────────────────────┘
}}
> '''注意''':PMX 的结构是''每个 chunk 的开头都带一个表示...
> 也就是说,按「顶点数 → 顶点数据…、面索引数 → 面数据…」...
> 中途哪怕偏移 1 字节,''其后全都会损坏''。
** Header [#h948c2a1]
|~字段|~类型|~内容|
|magic|4 bytes|`PMX `(0x50 0x4D 0x58 0x20)|
|version|float|2.0 或 2.1|
|globalsCount|byte|通常为 8|
|encoding|byte|0 = UTF-16LE、1 = UTF-8|
|additionalUVCount|byte|0〜4|
|vertexIndexSize|byte|1 / 2 / 4|
|textureIndexSize|byte|1 / 2 / 4|
|materialIndexSize|byte|1 / 2 / 4|
|boneIndexSize|byte|1 / 2 / 4|
|morphIndexSize|byte|1 / 2 / 4|
|rigidBodyIndexSize|byte|1 / 2 / 4|
*** 索引大小的含义 [#o13e15d7]
''索引大小''指定了该种类的索引用''多少字节表示''。
|~大小|~可表示范围|
|1|0〜255|
|2|0〜65535|
|4|0〜4294967295|
> '''设计意图''':PMD 采用''固定 2 字节''。
> 因此无法制作顶点数 65536 以上的模型。
> PMX 通过改为''可变长''撤除了这一限制。
> 实现时必须''读取 header 中的值,并据此分别读取''。
*** 有无符号 [#sc174ea2]
- ''顶点索引'' — ''无符号''
- ''贴图・材质・骨骼・变形・刚体索引'' — ''有符号''(因为...
** Model Info [#c0b3fd85]
|~字段|~内容|
|modelNameLocal|本地名(日语等)|
|modelNameUniversal|通用名(英文字母)|
|commentLocal|本地注释|
|commentUniversal|通用注释|
*** TextBuf 的结构 [#j994f8aa]
字符串以''可变长''方式存储。
#code(){{
int32 length ← 字节数(不是字符数)
byte[] text ← 实际字节序列(遵循 header 的 encoding)
}}
> '''实现的陷阱''':`length` 是''字节数''。
> 若是 UTF-16LE 则''1 字符 = 2 字节'',UTF-8 则''1 字符 =...
> ''与字符数混淆会立即损坏''。
** Vertex Data [#hd94dc34]
|~字段|~类型|~内容|
|vertexCount|int32|顶点数|
|(以下重复 vertexCount 次)|—|—|
|position|float3|坐标|
|normal|float3|法线|
|uv|float2|UV|
|additionalUV|float4 × N|附加 UV(N = header 中的值)|
|weightType|byte|权重种类|
|(权重数据)|可变|参见下文|
|edgeScale|float|轮廓边倍率|
*** 权重种类与数据 [#ff0c7522]
|~weightType|~名称|~数据|
|0|BDEF1|boneIndex × 1|
|1|BDEF2|boneIndex × 2 + weight × 1(float)|
|2|BDEF4|boneIndex × 4 + weight × 4(float)|
|3|SDEF|boneIndex × 2 + weight × 1 + C(float3) + R0(float...
|4|QDEF|boneIndex × 4 + weight × 4(float)|
> '''注意''':SDEF 是在 BDEF2 的数据上''追加 3 个 float3'...
> QDEF 与 BDEF4 是''同一结构''(只是解释方式不同)。
** Face Data [#e78ed160]
|~字段|~类型|~内容|
|faceIndexCount|int32|''索引的个数''(面数 × 3)|
|(以下重复 faceIndexCount 次)|vertexIndexSize|顶点索引...
> '''重要''':`faceIndexCount` ''并不是面数''。
> 1 个三角形有 3 个顶点,因此''面数 = faceIndexCount / 3'...
> 此处混淆会导致读取损坏。
** Texture Data [#o312d23b]
|~字段|~类型|~内容|
|textureCount|int32|贴图数|
|(以下重复 textureCount 次)|TextBuf|''相对路径''|
> '''可移植性要点''':路径是''相对于模型文件的相对路径''。
> 写入绝对路径的模型''在其他 PC 上必定损坏''。
> 发布前请确认''为相对路径''。
** Material Data [#u1d40781]
|~字段|~类型|~内容|
|nameLocal|TextBuf|材质名(本地)|
|nameUniversal|TextBuf|材质名(通用)|
|diffuse|float4|漫反射色 RGBA|
|specular|float3|高光色|
|shininess|float|高光强度|
|ambient|float3|环境色|
|drawFlag|byte|绘制标志|
|edgeColor|float4|轮廓边颜色|
|edgeSize|float|轮廓边大小|
|textureIndex|textureIndexSize|贴图(-1 = 无)|
|sphereTextureIndex|textureIndexSize|球面贴图(-1 = 无)|
|sphereMode|byte|0:无效 1:乘算 2:加算 3:减算|
|sharedToonFlag|byte|0:独立 toon 1:共享 toon|
|(toon 索引)|—|随 flag 变化|
|memo|TextBuf|备注|
|faceVertexCount|int32|''该材质使用的面索引数''|
*** drawFlag 的位 [#o9d5d0d2]
|~位|~含义|
|0x01|双面绘制|
|0x02|地面阴影|
|0x04|自阴影贴图|
|0x08|自阴影|
*** toon 索引的分支 [#i93e99b7]
#code(){{
if (sharedToonFlag == 1)
sharedToonIndex : byte // 共享 toon 的编号(...
else
toonTextureIndex : textureIndexSize // 引用贴图数组
}}
> '''注意''':`faceVertexCount` ''不是顶点数,而是面索引...
> 实际面数为 ''faceVertexCount / 3''。
** Bone Data [#l3221fa6]
|~字段|~类型|~内容|
|nameLocal|TextBuf|骨骼名|
|nameUniversal|TextBuf|骨骼名(通用)|
|position|float3|坐标|
|parentBoneIndex|boneIndexSize|父级(-1 = 无)|
|layer|int32|变形层级|
|boneFlag|uint16|标志|
|(末端位置)|—|随 flag 变化|
|(赋予)|—|随 flag 变化|
|(轴)|—|随 flag 变化|
|(局部轴)|—|随 flag 变化|
|(外部父级)|—|随 flag 变化|
|(IK 数据)|—|随 flag 变化|
*** boneFlag 的位 [#xcffdbf4]
|~位|~含义|
|0x0001|连接目标为''骨骼''(为 0 则为''相对位置'')|
|0x0002|可旋转|
|0x0004|可移动|
|0x0008|显示|
|0x0010|可操作|
|0x0020|IK|
|0x0080|局部赋予|
|0x0100|旋转赋予|
|0x0200|移动赋予|
|0x0400|轴固定|
|0x0800|局部轴|
|0x1000|物理后变形|
|0x2000|外部父级变形|
*** 条件字段 [#xd50b6fc]
#code(){{
// 末端位置
if (flag & 0x0001) tailBoneIndex : boneIndexSize
else tailPosition : float3
// 赋予
if (flag & 0x0100) or (flag & 0x0200)
grantParentBoneIndex : boneIndexSize
grantWeight : float
// 轴固定
if (flag & 0x0400) axisDirection : float3
// 局部轴
if (flag & 0x0800) localX : float3, localZ : float3
// 外部父级变形
if (flag & 0x2000) externalParentKey : int32
}}
*** IK 数据 [#d5d46772]
#code(){{
if (flag & 0x0020) {
ikTargetBoneIndex : boneIndexSize
ikLoopCount : int32
ikLimitAngle : float // 单位角(弧度)
ikLinkCount : int32
for (i = 0; i < ikLinkCount; i++) {
ikLinkBoneIndex : boneIndexSize
hasLimit : byte // 0:无限制 1:有限制
if (hasLimit) {
limitMin : float3 // 弧度
limitMax : float3 // 弧度
}
}
}
}}
> '''注意''':角度为''弧度(radian)''。
> UI 上虽以度(degree)显示,
> 但二进制中存放的是''弧度''。
** Morph Data [#j54ef09d]
|~字段|~类型|~内容|
|nameLocal|TextBuf|变形名|
|nameUniversal|TextBuf|变形名(通用)|
|panel|byte|0:系统 1:眉 2:眼 3:口 4:其他|
|morphType|byte|变形种类|
|offsetCount|int32|偏移数|
|(偏移)|可变|各类型结构不同|
*** morphType 一览 [#qa825d96]
|~值|~名称|~偏移|
|0|组|morphIndex + weight(float)|
|1|顶点|vertexIndex + offset(float3)|
|2|骨骼|boneIndex + translation(float3) + rotation(float4)|
|3|UV|vertexIndex + offset(float4)|
|4〜7|附加 UV1〜4|与 UV 同结构|
|8|材质|参见下文|
|9|翻转(2.1)|morphIndex + weight|
|10|冲量(2.1)|rigidBodyIndex + localFlag + velocity + t...
*** 材质变形的偏移 [#s165c34b]
#code(){{
materialIndex : materialIndexSize
calcMode : byte // 0:乘算 1:加算
diffuse : float4
specular : float3
shininess : float
ambient : float3
edgeColor : float4
edgeSize : float
textureCoef : float4
sphereCoef : float4
toonCoef : float4
}}
*** 骨骼变形的旋转 [#k89878ae]
以''四元数(float4)''存储。
并非欧拉角。
** Display Frame Data [#c0b328a3]
|~字段|~类型|~内容|
|nameLocal|TextBuf|框名|
|nameUniversal|TextBuf|框名(通用)|
|specialFlag|byte|0:通常框 1:特殊框(左眼・右眼)|
|elementCount|int32|元素数|
|(元素)|可变|boneIndex 或 morphIndex|
#code(){{
for (i = 0; i < elementCount; i++) {
type : byte // 0:骨骼 1:变形
index : boneIndexSize 或 morphIndexSize
}
}}
** Rigid Body Data [#ba4642ed]
|~字段|~类型|~内容|
|nameLocal|TextBuf|刚体名|
|nameUniversal|TextBuf|刚体名(通用)|
|relatedBoneIndex|boneIndexSize|关联骨骼|
|group|byte|碰撞组(0〜15)|
|noCollisionGroupFlag|uint16|非碰撞组(16 位)|
|shape|byte|0:球 1:盒 2:胶囊|
|size|float3|大小|
|position|float3|位置|
|rotation|float3|旋转(弧度)|
|mass|float|质量|
|moveAttenuation|float|移动衰减|
|rotationDamping|float|旋转衰减|
|repulsion|float|反弹力|
|friction|float|摩擦力|
|physicsMode|byte|0:骨骼跟随 1:物理运算 2:物理+骨骼对齐|
** Joint Data [#efbbeb25]
|~字段|~类型|~内容|
|nameLocal|TextBuf|关节名|
|nameUniversal|TextBuf|关节名(通用)|
|type|byte|0:弹簧 6DOF|
|rigidBodyIndexA|rigidBodyIndexSize|刚体 A|
|rigidBodyIndexB|rigidBodyIndexSize|刚体 B|
|position|float3|位置|
|rotation|float3|旋转(弧度)|
|moveLimitLower|float3|移动限制 下限|
|moveLimitUpper|float3|移动限制 上限|
|rotationLimitLower|float3|旋转限制 下限(弧度)|
|rotationLimitUpper|float3|旋转限制 上限(弧度)|
|springMove|float3|弹簧移动|
|springRotation|float3|弹簧旋转|
** Soft Body Data(仅 PMX 2.1) [#o2b22961]
PMX 2.1 中新增了''软体(Soft Body)''。
其结构复杂,特点是带有''锚点骨骼''与''顶点对应关系''。
- 可将头发和布料作为''弹簧网格''进行模拟
- 支持的工具有限,''实际应用的例子很少''
- 多数情况可用刚体+关节替代
** 与 PMD 的主要区别 [#c8d389c1]
|~项目|~PMD|~PMX|
|字符编码|Shift-JIS 固定|UTF-16LE / UTF-8|
|索引宽度|固定 2 字节|可变(1/2/4)|
|附加 UV|无|最多 4|
|权重|BDEF1/2|BDEF1/2/4、SDEF、QDEF|
|材质双面绘制|有(受限)|通过 drawFlag 详细控制|
|显示框|独有结构|灵活结构|
|软体|无|2.1 中支持|
详见 [[PMD格式详解]]。
** 实现注意事项汇总 [#i1570a5b]
#region(编写 PMX 解析器时的检查清单)
+ '''字节序为小端序''' — 部分环境需要转换
+ '''TextBuf 的长度是字节数''' — 并非字符数
+ '''索引大小从 header 读取''' — 不要假定为固定值
+ '''顶点索引无符号,其他有符号'''
+ '''faceIndexCount 是面索引数''' — 并非面数
+ '''angle 是弧度''' — 并非度
+ '''根据 boneFlag 以''可变长''读取''' — 需实现条件分支
+ '''toon 的分支通过 sharedToonFlag 判定'''
+ '''骨骼变形的旋转是四元数'''
+ '''材质变形的偏移有 22 个字段''' — 不要弄错顺序
#endregion
** 验证方法 [#uc3b48dc]
编写完 PMX 解析器后,按以下顺序测试。
+ 读取''已知的简单模型'',将顶点数・面数与 PMXEditor 的显...
+ 确认''能否完整读到全部 chunk 末尾''(剩余字节数是否为 0)
+ 分别准备''索引大小为 1/2/4 的模型''并读取
+ 读取''包含 BDEF2 / SDEF / BDEF4 的模型''
+ 读取''包含材质变形・骨骼变形的模型''
+ 将读入的数据''写回'',比较是否与原始数据一致
> '''最可靠的验证''':''读入 → 写出 → 字节比较''。
> 若完全一致,即可保证解析器与写出器均正确。
** 相关页面 [#sb5e99da]
- [[PMD格式详解]] — 与旧格式的对比
- [[VMD动作数据格式]] — 动作侧的规格
- [[数据格式速查表]] — 一览表
- [[格式转换工具链]] — 转换时的信息缺失
- [[顶点权重详解]] — 权重的含义
#comment(num=5,noname,below)
終了行:
* PMX格式详解 [#qcc9542a]
&size(13){''PMX 2.0 / 2.1 的全部 chunk 结构与二进制规格''};
#contents
** 什么是 PMX [#t3fa80fc]
''PMX'' 是取代 PMD 的''现行标准模型格式''。
于 2011 年前后登场,如今已成为 MMD 模型事实上的标准。
|~项目|~内容|
|扩展名|.pmx|
|字节序|小端序|
|字符串编码|UTF-16LE 或 UTF-8(由 header 指定)|
|版本|2.0 / 2.1|
|设计|由 PMXEditor 的作者完成|
|特征|全部 chunk 均支持''可变长索引'',可应对大规模模型|
** 整体结构 [#t9d022a5]
PMX 由''chunk 的链式结构''构成。
#code(){{
┌─────────────────────────┐
│ Header │ ← 魔数・版本・设置
├─────────────────────────┤
│ Model Info │ ← 模型名・注释
├─────────────────────────┤
│ Vertex Data │ ← 顶点
├─────────────────────────┤
│ Face Data │ ← 面(顶点索引)
├─────────────────────────┤
│ Texture Data │ ← 贴图的路径
├─────────────────────────┤
│ Material Data │ ← 材质
├─────────────────────────┤
│ Bone Data │ ← 骨骼
├─────────────────────────┤
│ Morph Data │ ← 变形
├─────────────────────────┤
│ Display Frame Data │ ← 显示框
├─────────────────────────┤
│ Rigid Body Data │ ← 刚体
├─────────────────────────┤
│ Joint Data │ ← 关节
├─────────────────────────┤
│ Soft Body Data │ ← 软体(仅 2.1)
└─────────────────────────┘
}}
> '''注意''':PMX 的结构是''每个 chunk 的开头都带一个表示...
> 也就是说,按「顶点数 → 顶点数据…、面索引数 → 面数据…」...
> 中途哪怕偏移 1 字节,''其后全都会损坏''。
** Header [#h948c2a1]
|~字段|~类型|~内容|
|magic|4 bytes|`PMX `(0x50 0x4D 0x58 0x20)|
|version|float|2.0 或 2.1|
|globalsCount|byte|通常为 8|
|encoding|byte|0 = UTF-16LE、1 = UTF-8|
|additionalUVCount|byte|0〜4|
|vertexIndexSize|byte|1 / 2 / 4|
|textureIndexSize|byte|1 / 2 / 4|
|materialIndexSize|byte|1 / 2 / 4|
|boneIndexSize|byte|1 / 2 / 4|
|morphIndexSize|byte|1 / 2 / 4|
|rigidBodyIndexSize|byte|1 / 2 / 4|
*** 索引大小的含义 [#o13e15d7]
''索引大小''指定了该种类的索引用''多少字节表示''。
|~大小|~可表示范围|
|1|0〜255|
|2|0〜65535|
|4|0〜4294967295|
> '''设计意图''':PMD 采用''固定 2 字节''。
> 因此无法制作顶点数 65536 以上的模型。
> PMX 通过改为''可变长''撤除了这一限制。
> 实现时必须''读取 header 中的值,并据此分别读取''。
*** 有无符号 [#sc174ea2]
- ''顶点索引'' — ''无符号''
- ''贴图・材质・骨骼・变形・刚体索引'' — ''有符号''(因为...
** Model Info [#c0b3fd85]
|~字段|~内容|
|modelNameLocal|本地名(日语等)|
|modelNameUniversal|通用名(英文字母)|
|commentLocal|本地注释|
|commentUniversal|通用注释|
*** TextBuf 的结构 [#j994f8aa]
字符串以''可变长''方式存储。
#code(){{
int32 length ← 字节数(不是字符数)
byte[] text ← 实际字节序列(遵循 header 的 encoding)
}}
> '''实现的陷阱''':`length` 是''字节数''。
> 若是 UTF-16LE 则''1 字符 = 2 字节'',UTF-8 则''1 字符 =...
> ''与字符数混淆会立即损坏''。
** Vertex Data [#hd94dc34]
|~字段|~类型|~内容|
|vertexCount|int32|顶点数|
|(以下重复 vertexCount 次)|—|—|
|position|float3|坐标|
|normal|float3|法线|
|uv|float2|UV|
|additionalUV|float4 × N|附加 UV(N = header 中的值)|
|weightType|byte|权重种类|
|(权重数据)|可变|参见下文|
|edgeScale|float|轮廓边倍率|
*** 权重种类与数据 [#ff0c7522]
|~weightType|~名称|~数据|
|0|BDEF1|boneIndex × 1|
|1|BDEF2|boneIndex × 2 + weight × 1(float)|
|2|BDEF4|boneIndex × 4 + weight × 4(float)|
|3|SDEF|boneIndex × 2 + weight × 1 + C(float3) + R0(float...
|4|QDEF|boneIndex × 4 + weight × 4(float)|
> '''注意''':SDEF 是在 BDEF2 的数据上''追加 3 个 float3'...
> QDEF 与 BDEF4 是''同一结构''(只是解释方式不同)。
** Face Data [#e78ed160]
|~字段|~类型|~内容|
|faceIndexCount|int32|''索引的个数''(面数 × 3)|
|(以下重复 faceIndexCount 次)|vertexIndexSize|顶点索引...
> '''重要''':`faceIndexCount` ''并不是面数''。
> 1 个三角形有 3 个顶点,因此''面数 = faceIndexCount / 3'...
> 此处混淆会导致读取损坏。
** Texture Data [#o312d23b]
|~字段|~类型|~内容|
|textureCount|int32|贴图数|
|(以下重复 textureCount 次)|TextBuf|''相对路径''|
> '''可移植性要点''':路径是''相对于模型文件的相对路径''。
> 写入绝对路径的模型''在其他 PC 上必定损坏''。
> 发布前请确认''为相对路径''。
** Material Data [#u1d40781]
|~字段|~类型|~内容|
|nameLocal|TextBuf|材质名(本地)|
|nameUniversal|TextBuf|材质名(通用)|
|diffuse|float4|漫反射色 RGBA|
|specular|float3|高光色|
|shininess|float|高光强度|
|ambient|float3|环境色|
|drawFlag|byte|绘制标志|
|edgeColor|float4|轮廓边颜色|
|edgeSize|float|轮廓边大小|
|textureIndex|textureIndexSize|贴图(-1 = 无)|
|sphereTextureIndex|textureIndexSize|球面贴图(-1 = 无)|
|sphereMode|byte|0:无效 1:乘算 2:加算 3:减算|
|sharedToonFlag|byte|0:独立 toon 1:共享 toon|
|(toon 索引)|—|随 flag 变化|
|memo|TextBuf|备注|
|faceVertexCount|int32|''该材质使用的面索引数''|
*** drawFlag 的位 [#o9d5d0d2]
|~位|~含义|
|0x01|双面绘制|
|0x02|地面阴影|
|0x04|自阴影贴图|
|0x08|自阴影|
*** toon 索引的分支 [#i93e99b7]
#code(){{
if (sharedToonFlag == 1)
sharedToonIndex : byte // 共享 toon 的编号(...
else
toonTextureIndex : textureIndexSize // 引用贴图数组
}}
> '''注意''':`faceVertexCount` ''不是顶点数,而是面索引...
> 实际面数为 ''faceVertexCount / 3''。
** Bone Data [#l3221fa6]
|~字段|~类型|~内容|
|nameLocal|TextBuf|骨骼名|
|nameUniversal|TextBuf|骨骼名(通用)|
|position|float3|坐标|
|parentBoneIndex|boneIndexSize|父级(-1 = 无)|
|layer|int32|变形层级|
|boneFlag|uint16|标志|
|(末端位置)|—|随 flag 变化|
|(赋予)|—|随 flag 变化|
|(轴)|—|随 flag 变化|
|(局部轴)|—|随 flag 变化|
|(外部父级)|—|随 flag 变化|
|(IK 数据)|—|随 flag 变化|
*** boneFlag 的位 [#xcffdbf4]
|~位|~含义|
|0x0001|连接目标为''骨骼''(为 0 则为''相对位置'')|
|0x0002|可旋转|
|0x0004|可移动|
|0x0008|显示|
|0x0010|可操作|
|0x0020|IK|
|0x0080|局部赋予|
|0x0100|旋转赋予|
|0x0200|移动赋予|
|0x0400|轴固定|
|0x0800|局部轴|
|0x1000|物理后变形|
|0x2000|外部父级变形|
*** 条件字段 [#xd50b6fc]
#code(){{
// 末端位置
if (flag & 0x0001) tailBoneIndex : boneIndexSize
else tailPosition : float3
// 赋予
if (flag & 0x0100) or (flag & 0x0200)
grantParentBoneIndex : boneIndexSize
grantWeight : float
// 轴固定
if (flag & 0x0400) axisDirection : float3
// 局部轴
if (flag & 0x0800) localX : float3, localZ : float3
// 外部父级变形
if (flag & 0x2000) externalParentKey : int32
}}
*** IK 数据 [#d5d46772]
#code(){{
if (flag & 0x0020) {
ikTargetBoneIndex : boneIndexSize
ikLoopCount : int32
ikLimitAngle : float // 单位角(弧度)
ikLinkCount : int32
for (i = 0; i < ikLinkCount; i++) {
ikLinkBoneIndex : boneIndexSize
hasLimit : byte // 0:无限制 1:有限制
if (hasLimit) {
limitMin : float3 // 弧度
limitMax : float3 // 弧度
}
}
}
}}
> '''注意''':角度为''弧度(radian)''。
> UI 上虽以度(degree)显示,
> 但二进制中存放的是''弧度''。
** Morph Data [#j54ef09d]
|~字段|~类型|~内容|
|nameLocal|TextBuf|变形名|
|nameUniversal|TextBuf|变形名(通用)|
|panel|byte|0:系统 1:眉 2:眼 3:口 4:其他|
|morphType|byte|变形种类|
|offsetCount|int32|偏移数|
|(偏移)|可变|各类型结构不同|
*** morphType 一览 [#qa825d96]
|~值|~名称|~偏移|
|0|组|morphIndex + weight(float)|
|1|顶点|vertexIndex + offset(float3)|
|2|骨骼|boneIndex + translation(float3) + rotation(float4)|
|3|UV|vertexIndex + offset(float4)|
|4〜7|附加 UV1〜4|与 UV 同结构|
|8|材质|参见下文|
|9|翻转(2.1)|morphIndex + weight|
|10|冲量(2.1)|rigidBodyIndex + localFlag + velocity + t...
*** 材质变形的偏移 [#s165c34b]
#code(){{
materialIndex : materialIndexSize
calcMode : byte // 0:乘算 1:加算
diffuse : float4
specular : float3
shininess : float
ambient : float3
edgeColor : float4
edgeSize : float
textureCoef : float4
sphereCoef : float4
toonCoef : float4
}}
*** 骨骼变形的旋转 [#k89878ae]
以''四元数(float4)''存储。
并非欧拉角。
** Display Frame Data [#c0b328a3]
|~字段|~类型|~内容|
|nameLocal|TextBuf|框名|
|nameUniversal|TextBuf|框名(通用)|
|specialFlag|byte|0:通常框 1:特殊框(左眼・右眼)|
|elementCount|int32|元素数|
|(元素)|可变|boneIndex 或 morphIndex|
#code(){{
for (i = 0; i < elementCount; i++) {
type : byte // 0:骨骼 1:变形
index : boneIndexSize 或 morphIndexSize
}
}}
** Rigid Body Data [#ba4642ed]
|~字段|~类型|~内容|
|nameLocal|TextBuf|刚体名|
|nameUniversal|TextBuf|刚体名(通用)|
|relatedBoneIndex|boneIndexSize|关联骨骼|
|group|byte|碰撞组(0〜15)|
|noCollisionGroupFlag|uint16|非碰撞组(16 位)|
|shape|byte|0:球 1:盒 2:胶囊|
|size|float3|大小|
|position|float3|位置|
|rotation|float3|旋转(弧度)|
|mass|float|质量|
|moveAttenuation|float|移动衰减|
|rotationDamping|float|旋转衰减|
|repulsion|float|反弹力|
|friction|float|摩擦力|
|physicsMode|byte|0:骨骼跟随 1:物理运算 2:物理+骨骼对齐|
** Joint Data [#efbbeb25]
|~字段|~类型|~内容|
|nameLocal|TextBuf|关节名|
|nameUniversal|TextBuf|关节名(通用)|
|type|byte|0:弹簧 6DOF|
|rigidBodyIndexA|rigidBodyIndexSize|刚体 A|
|rigidBodyIndexB|rigidBodyIndexSize|刚体 B|
|position|float3|位置|
|rotation|float3|旋转(弧度)|
|moveLimitLower|float3|移动限制 下限|
|moveLimitUpper|float3|移动限制 上限|
|rotationLimitLower|float3|旋转限制 下限(弧度)|
|rotationLimitUpper|float3|旋转限制 上限(弧度)|
|springMove|float3|弹簧移动|
|springRotation|float3|弹簧旋转|
** Soft Body Data(仅 PMX 2.1) [#o2b22961]
PMX 2.1 中新增了''软体(Soft Body)''。
其结构复杂,特点是带有''锚点骨骼''与''顶点对应关系''。
- 可将头发和布料作为''弹簧网格''进行模拟
- 支持的工具有限,''实际应用的例子很少''
- 多数情况可用刚体+关节替代
** 与 PMD 的主要区别 [#c8d389c1]
|~项目|~PMD|~PMX|
|字符编码|Shift-JIS 固定|UTF-16LE / UTF-8|
|索引宽度|固定 2 字节|可变(1/2/4)|
|附加 UV|无|最多 4|
|权重|BDEF1/2|BDEF1/2/4、SDEF、QDEF|
|材质双面绘制|有(受限)|通过 drawFlag 详细控制|
|显示框|独有结构|灵活结构|
|软体|无|2.1 中支持|
详见 [[PMD格式详解]]。
** 实现注意事项汇总 [#i1570a5b]
#region(编写 PMX 解析器时的检查清单)
+ '''字节序为小端序''' — 部分环境需要转换
+ '''TextBuf 的长度是字节数''' — 并非字符数
+ '''索引大小从 header 读取''' — 不要假定为固定值
+ '''顶点索引无符号,其他有符号'''
+ '''faceIndexCount 是面索引数''' — 并非面数
+ '''angle 是弧度''' — 并非度
+ '''根据 boneFlag 以''可变长''读取''' — 需实现条件分支
+ '''toon 的分支通过 sharedToonFlag 判定'''
+ '''骨骼变形的旋转是四元数'''
+ '''材质变形的偏移有 22 个字段''' — 不要弄错顺序
#endregion
** 验证方法 [#uc3b48dc]
编写完 PMX 解析器后,按以下顺序测试。
+ 读取''已知的简单模型'',将顶点数・面数与 PMXEditor 的显...
+ 确认''能否完整读到全部 chunk 末尾''(剩余字节数是否为 0)
+ 分别准备''索引大小为 1/2/4 的模型''并读取
+ 读取''包含 BDEF2 / SDEF / BDEF4 的模型''
+ 读取''包含材质变形・骨骼变形的模型''
+ 将读入的数据''写回'',比较是否与原始数据一致
> '''最可靠的验证''':''读入 → 写出 → 字节比较''。
> 若完全一致,即可保证解析器与写出器均正确。
** 相关页面 [#sb5e99da]
- [[PMD格式详解]] — 与旧格式的对比
- [[VMD动作数据格式]] — 动作侧的规格
- [[数据格式速查表]] — 一览表
- [[格式转换工具链]] — 转换时的信息缺失
- [[顶点权重详解]] — 权重的含义
#comment(num=5,noname,below)
ページ名:
最新の20件
2026-10-05
FrontPage
VPD姿势数据格式
VMD动作数据格式
PMX格式详解
PMXEditor详解
PMD格式详解
MME概要
MMD与新技术
MMD同人文化
MMD历史与版本
MMD快速入门
MMD概要
2022-02-12
AutoTicketLinkName
2021-12-11
FormattingRules
2021-12-08
PukiWiki/1.4/Manual/Plugin/V-Z
PukiWiki/1.4/Manual/Plugin/H-K
PukiWiki/1.4/Manual/Plugin/A-D
PukiWiki/1.4/Manual/Plugin/E-G
PukiWiki/1.4/Manual/Plugin/L-N
PukiWiki/1.4/Manual/Plugin/S-U