.mv1 ファイル形式仕様書対象: DxLib 3.24f の MV1 モデルファイル (Ver.1)
出典: DxModel.cpp / DxArchive_.cpp (loader) + DxModelFile.h / DxLib.h / DxModel.h (バイナリ構造体と enum)
文字コード表記: リトルエンディアン、C 構造体の直接シリアライズ (padding は明示)
MV1MODEL_FILEHEADER_F1MV1_FRAME_F1MV1_MESH_F1MV1_MATERIAL_F1MV1_TEXTURE_F1MV1_TRIANGLE_LIST_F1MV1_SKIN_BONE_F1MV1_LIGHT_F1★印 = 拡充版で追加された章
.mv1 は DxLib 独自の 3D モデルファイル形式で、他形式 (.mqo / .pmd / .pmx / .x / .fbx / .vmd) を読み込んだ後のランタイム構造体を丸ごとバイナリ化したキャッシュ的なフォーマット。利点:
MV1LoadModel で即座にハンドル化保存は MV1SaveModelToMV1File() で、読み込みは他形式と同じ MV1LoadModel() の裏で自動判定される (マジック "MV11" で分岐)。
+-------+-----------------------------+
| off | 内容 |
+-------+-----------------------------+
| 0x00 | Magic "MV11" (4 bytes) | ← MV1MODEL_FILEHEADER_F1.CheckID[4] と同一のバイト列
+-------+-----------------------------+
| 0x04 | DXA 圧縮ブロック |
| | 伸長すると |
| | MV1MODEL_FILEHEADER_F1 の |
| | Version 以降 + 可変長 |
| | データ群 |
+-------+-----------------------------+
M V 1 1。これ以外は即 -1 エラー。この 4 byte は「MV1MODEL_FILEHEADER_F1 の CheckID[4]」でもある — つまり CheckID は DXA 圧縮に含まれず、ファイル magic をそのまま FHeader の先頭 4 byte に流用する 設計 (DxModel.cpp L14921: DXA_Decode( file+4, (BYTE*)FHeader + 4 ))。DXA_Decode(src + 4, NULL) で伸長後サイズを取得、バッファ確保、再 DXA_Decode で FHeader+4 以降に実データ展開。アルゴリズムは DxLib 独自の LZSS 派生 (本文参照: DxArchive_.cpp)。Version は 0 (DxLib 3.24f 時点)。古い記述の「Version = 1」は誤り。Magic の '1' はバージョンではなく形式識別子。伸長されたデータは「MV1MODEL_FILEHEADER_F1 の Version フィールド (offset 4) 以降」としてメモリに貼られ、同じバッファ内の後続オフセットに各種配列やポインタ先が連続配置される。従って パーサ実装側は decode 結果の先頭に "MV11" 4 byte を自前で補って FHeader として reinterpret する のが簡単 (mv1conv はこの方式)。構造体メンバー内の ポインタ相当の DWORD は「このバッファ (CheckID 込み) 先頭からのバイトオフセット」を格納 (ロード時にポインタへ変換される)。ポインタ値 0 は null。
出典: DxArchive_.cpp の DXA_Encode / DXA_Decode。純粋 LZSS (Huffman 併用なし)。.mv1 の magic "MV11" 直後から始まるバイト列がこの形式。
| offset | type | 意味 |
|---|---|---|
| 0 | DWORD (LE) | OriginalSize 元のバイト数 |
| 4 | DWORD (LE) | CompressedSize 圧縮後バイト数 (この 9 byte ヘッダを含む) |
| 8 | BYTE | KeyCode マッチ開始マーカ (圧縮元データに存在しないバイト値が選ばれる) |
リテラル byte と backref の混在列。どちらかの判別は「現バイトが KeyCode か否か」で行う:
byte != KeyCode → そのまま出力KeyCode そのもの): KeyCode KeyCode → 1 個の KeyCode を出力KeyCode <flags> [<extlen>] <addr...> → 過去データからコピーKeyCode の直後のバイトを flags と呼ぶ (値が KeyCode 以上の場合は デコード時に -1 する 補正あり):
bit 7-3: (match_length - 4) の下位 5 bit (0..31)
bit 2 : 拡張長フラグ (1 なら次 1 byte で長さ bit 8-5 を追加)
bit 1-0: アドレス byte 数
00 = 1 byte (1..256 back)
01 = 2 byte (1..65536 back)
10 = 3 byte (1..16777216 back)
flags の次は:
extlen (1 byte): これで length |= (extlen << 5) し、合計 13 bit の長さを扱える| 名称 | 値 | 意味 |
|---|---|---|
MIN_COMPRESS |
4 | これ以上のマッチ長のみ圧縮に使う |
MAX_COPYSIZE |
8195 (5bit + 8bit + MIN_COMPRESS) |
最大マッチ長 (decoder は 13bit しか読まない) |
MAX_POSITION |
16 MB (1<<24) | 最大 lookback 距離 |
注: 旧 spec に
0x7FFF (32,767)と書いてあったのは誤り。実 decoder はlength = ((flags>>3) & 0x1F) | (ext_byte << 5); length += 4で組み立てるので、 最大長は0x1FFF + 4 = 8195byte。mv1convの encoder も 8195 で cap している。
uint32_t DXA_Decode_Size( const uint8_t *src )
{
return read_u32_le( src + 0 ) ; // OriginalSize
}
int DXA_Decode( const uint8_t *src, uint8_t *dst )
{
uint32_t origSize = read_u32_le( src + 0 ) ;
uint32_t compSize = read_u32_le( src + 4 ) ;
uint8_t key = src[ 8 ] ;
const uint8_t *sp = src + 9 ;
const uint8_t *spEnd = src + compSize ;
uint8_t *dp = dst ;
while ( sp < spEnd ) {
if ( *sp != key ) { /* リテラル */
*dp++ = *sp++ ;
continue ;
}
if ( sp[ 1 ] == key ) { /* エスケープ済 KeyCode */
*dp++ = key ;
sp += 2 ;
continue ;
}
/* マッチ参照 */
uint8_t flags = sp[ 1 ] ;
if ( flags > key ) flags-- ; /* デコード補正 */
sp += 2 ;
uint32_t length = ( flags >> 3 ) & 0x1F ;
if ( flags & 0x04 ) { /* 拡張長 */
length |= ( uint32_t )( *sp++ ) << 5 ;
}
length += 4 ; /* MIN_COMPRESS 加算 */
uint32_t distance = 0 ;
switch ( flags & 0x03 ) {
case 0 : distance = *sp++ ; break ;
case 1 : distance = read_u16_le( sp ) ; sp += 2 ; break ;
case 2 : distance = read_u16_le( sp ) | ( sp[ 2 ] << 16 ) ; sp += 3 ; break ;
}
distance += 1 ; /* デコード補正 */
/* 自己参照コピー (distance < length) の場合、少しずつ展開 */
if ( distance < length ) {
uint32_t part = distance ;
while ( length > part ) {
memcpy( dp, dp - part, part ) ;
dp += part ;
length -= part ;
part += part ; /* 2 倍ずつ広げる */
}
memcpy( dp, dp - distance * 2, length ) ; /* 残り */
dp += length ;
} else {
memcpy( dp, dp - distance, length ) ;
dp += length ;
}
}
return ( int )origSize ;
}
.mv1 ファイルから DXA ブロックを取り出すuint32_t get_dxa_size( const uint8_t *file, size_t fileLen )
{
if ( fileLen < 4 + 9 ) return 0 ;
if ( memcmp( file, "MV11", 4 ) != 0 ) return 0 ;
return read_u32_le( file + 4 + 4 ) ; /* DXA ヘッダの CompressedSize */
}
4 (magic) + CompressedSize (DXA 圧縮部分含む)OriginalSize (DXA ヘッダの先頭 4 byte)KeyCode の選定 = 入力データ中に最も少ないバイト値を 0..255 から線形探索 (DXA_GetKeyCode)。稀な値ほど stream が短くなる。match_length が MIN_COMPRESS 未満 or match が見つからない場合はリテラル書き出し。本セクションで扱う DXA_Encode/Decode は ファイル内部圧縮 専用で、.dxa アーカイブの鍵付き暗号化 (DXArchive クラス内蔵) とは 別物。.mv1 には暗号化はない。
MV1MODEL_FILEHEADER_F1正確に 304 バイト (0x130) の固定長ヘッダ (4 byte align)。主要フィールド:
| offset | type | name | 意味 |
|---|---|---|---|
| 0x00 | BYTE[4] | CheckID[4] |
"MV11" — ファイル magic を流用、DXA ブロックには含まれない |
| 0x04 | DWORD | Version |
0 (DxLib 3.24f 現行。旧仕様書の「1」は誤り) |
| 0x08 | int | RightHandType |
TRUE=右手系 / FALSE=左手系 |
| 0x0C | int | AutoCreateNormal |
法線自動計算使用 |
| 0x10 | int | ChangeDrawMaterialTableSize |
描画マテリアル変更管理用ビット数 |
| 0x14 | int | ChangeMatrixTableSize |
行列変更管理用ビット数 |
| 0x18 | DWORD | ChangeDrawMaterialTable (ptr) |
描画用マテリアル変更確認用ビットデータ |
| 0x1C | DWORD | ChangeMatrixTable (ptr) |
行列変更確認用ビットデータ |
| 0x20 | int | FrameNum |
階層データ (フレーム) 数 |
| 0x24 | DWORD | Frame (ptr) |
MV1_FRAME_F1[] の先頭 |
| 0x28 | int | TopFrameNum |
ルート階層 (親なし) フレーム数 |
| 0x2C | DWORD | FirstTopFrame (ptr) |
最初のルートフレーム |
| 0x30 | DWORD | LastTopFrame (ptr) |
最後のルートフレーム |
| 0x34 | int | FrameUseSkinBoneNum |
フレームが使用するボーン総数 |
| 0x38 | DWORD | FrameUseSkinBone (ptr) |
MV1_SKIN_BONE_F1*[] の先頭 |
| 0x3C | int | MaterialNum |
マテリアル数 |
| 0x40 | DWORD | Material (ptr) |
MV1_MATERIAL_F1[] |
| 0x44 | int | TextureNum |
テクスチャ数 |
| 0x48 | DWORD | Texture (ptr) |
MV1_TEXTURE_F1[] |
| 0x4C | int | MeshNum |
メッシュ数 |
| 0x50 | DWORD | Mesh (ptr) |
MV1_MESH_F1[] |
| 0x54 | int | LightNum |
ライト数 |
| 0x58 | DWORD | Light (ptr) |
MV1_LIGHT_F1[] |
| 0x5C | int | SkinBoneNum |
スキンボーン数 |
| 0x60 | DWORD | SkinBone (ptr) |
MV1_SKIN_BONE_F1[] |
| 0x64 | int | SkinBoneUseFrameNum |
ボーンを使用するフレーム数 |
| 0x68 | DWORD | SkinBoneUseFrame (ptr) |
MV1_SKIN_BONE_USE_FRAME_F1*[] |
| 0x6C | int | TriangleListNum |
トライアングルリスト数 |
| 0x70 | DWORD | TriangleList (ptr) |
MV1_TRIANGLE_LIST_F1[] |
| 0x74 | DWORD | VertexData (ptr) |
頂点データブロック先頭 |
| 0x78 | DWORD | VertexDataSize |
頂点データブロックの合計バイト数 |
| ... | int×10 | TriangleListNormalPositionNum ... TriangleListIndexNum |
ボーン別頂点総数カウンタ群 |
| ... | int | TriangleNum |
モデル全体の三角形数 |
| ... | int | TriangleListVertexNum |
トライアングルリストの頂点総数 |
| ... | int | StringSize |
文字列データサイズ |
| ... | DWORD | StringBuffer (ptr) |
文字列プール先頭 (null 終端連結) |
| ... | int | OriginalAnimKeyDataSize |
アニメキー元サイズ |
| ... | int | AnimKeyDataSize |
アニメキーデータサイズ (圧縮後) |
| ... | DWORD | AnimKeyData (ptr) |
アニメキー可変長データ |
| ... | int | AnimKeySetNum |
アニメキーセット数 |
| ... | int | AnimKeySetUnitSize |
MV1_ANIM_KEYSET_F1 サイズ |
| ... | DWORD | AnimKeySet (ptr) |
アニメキーセット配列 |
| ... | int | AnimNum |
アニメ数 |
| ... | int | AnimUnitSize |
MV1_ANIM_F1 サイズ |
| ... | DWORD | Anim (ptr) |
アニメ配列 |
| ... | int | AnimSetNum |
アニメセット数 |
| ... | DWORD | AnimSet (ptr) |
アニメセット配列 |
| ... | DWORD[4] | UserData[4] |
ユーザー任意 |
| ... | DWORD | Shape (ptr) |
MV1_FILEHEAD_SHAPE_F1*、なければ NULL |
| ... | DWORD | Physics (ptr) |
MV1_FILEHEAD_PHYSICS_F1*、なければ NULL |
| ... | BYTE | MaterialNumberOrderDraw |
マテリアル番号昇順で描画するか |
| ... | BYTE | IsStringUTF8 |
StringBuffer が UTF-8 か (1) / Shift-JIS か (0) |
| ... | BYTE[2] | Padding1[2] |
0 |
| ... | DWORD[13] | Padding2[13] |
0 (将来拡張用) |
ヘッダ全長: 304 byte (0x130)。mv1conv の static_assert(sizeof(MV1MODEL_FILEHEADER_F1) == 304) で確認可能。
検証済サンプル (mv1conv reader 通過):
SimpleModel.mv1/SimpleModelVertexColor.mv1/SimplePillarStage.mv1/ColTestStage.mv1/DxChara.mv1(DxLib_VC3_24f 同梱)。全てで magicMV11/Version=0/IsStringUTF8=1/MaterialNumberOrderDraw=0を確認。
MV1_FRAME_F1モデルの骨格 (ボーン) + メッシュコンテナ。親子ツリー構造:
| 役割 | フィールド |
|---|---|
| ツリー連結 | DimPrev / DimNext (配列順) / Parent / FirstChild / LastChild / Prev / Next |
| 基本情報 | Name / Index / Flag (MV1_FRAMEFLAG_VISIBLE 等) |
| 変換 | Translate (VECTOR) / Scale / Rotate + RotateOrder / Quaternion (FLOAT4) / PreRotate / PostRotate |
| 統計 | TotalMeshNum / TotalChildNum / TriangleNum / VertexNum |
| メッシュ | MeshNum / Mesh (ptr to MV1_MESH_F1[]) |
| スキンボーン | SkinBoneNum / SkinBone / UseSkinBoneNum / UseSkinBone (ボーンリスト) |
| 法線 | SmoothingAngle (ラジアン) / AutoCreateNormal |
| 座標/法線データ | PositionNum / NormalNum / PositionAndNormalData (ptr) |
| シェイプ | FrameShape (ptr, NULL 可) |
| 変更管理 | ChangeDrawMaterialInfo / ChangeMatrixInfo (各 MV1_CHANGE_F1) |
| その他 | Light (ptr、ライト本体) / VertFlag / MaxBoneBlendNum |
PositionAndNormalData の構造VertFlag ビット (MV1_FRAME_VERT_FLAG_* 群) に応じて可変レイアウト。ヘッダで要素タイプを切替:
[MV1_FRAME_VERT_FLAG_POSITION_B16 が立っている場合]
MV1_POSITION_16BIT_SUBINFO_F1 x ; // float Min; float Width;
MV1_POSITION_16BIT_SUBINFO_F1 y ;
MV1_POSITION_16BIT_SUBINFO_F1 z ;
[PositionNum 回繰返し: 座標]
POSITION_B16 立っていれば WORD×3、立っていなければ float×3
[PositionNum 回繰返し: スキニング情報 (MATRIX_WEIGHT_NONE でない場合)]
MaxBoneBlendNum 分、又はインデックス=-1 で終端
インデックス値 (MATRIX_INDEX_MASK 指定型: u8 / u16)
ウェイト値 (MATRIX_WEIGHT_MASK 指定型: u8 / u16)
[法線: MV1_FRAME_NORMAL_TYPE_NONE でない場合]
NormalNum 回繰返し: NORMAL_TYPE_MASK 指定型 (s8 / s16 / float)
法線型フラグ (MV1_FRAME_NORMAL_TYPE_*):
NONE (0) — 法線なし (自動計算)S8 (1) — 各成分 signed char (-127..127 で [-1..1])S16 (2) — 各成分 signed shortF32 (3) — float 3 成分MATRIX_WEIGHT_TYPE_U8 / U16 で頂点ウェイトを 8/16 bit に圧縮可能 (生 float なら 4 byte/weight)。
MV1_MESH_F1フレームに属する描画単位。1 マテリアル = 1 メッシュが基本。
| 役割 | フィールド |
|---|---|
| 親 | Container (ptr to MV1_FRAME_F1) |
| マテリアル | Material (ptr) |
| 頂点色 | UseVertexDiffuseColor / UseVertexSpecularColor / NotOneDiffuseAlpha |
| 形状 | Shape (1=シェイプメッシュ) |
| 描画制御 | Visible / BackCulling (0=なし, 1=表, 2=裏) |
| UV | UVSetUnitNum (UV ペア数) / UVUnitNum (UV 成分数) |
| 頂点フラグ | VertFlag (MV1_MESH_VERT_FLAG_*) |
| 頂点数/面数 | VertexNum / FaceNum |
| 頂点データ | VertexData (ptr) |
| トライアングルリスト | TriangleListNum / TriangleList (ptr) |
VertexData 構造[先頭に一度だけ: VertFlag に MV1_MESH_VERT_FLAG_COMMON_COLOR が立っている場合]
COLOR_U8 DiffuseColor ; // 全頂点共通色
COLOR_U8 SpecularColor ;
[VertexNum 回繰返し: 座標インデックス]
POS_IND_TYPE_MASK 指定型 (none / u8 / u16 / u32)
[VertexNum 回繰返し: 法線インデックス]
NRM_IND_TYPE_MASK 指定型
[VertexNum 回繰返し: 頂点カラー (COMMON_COLOR でない場合)]
COLOR_U8 diffuse + COLOR_U8 specular
[VertexNum 回繰返し: UV 値]
UVUnitNum * UVSetUnitNum 個、float×2 又は u16×2 (UV_U16 指定時、65535=1.0)
[VertexNum 回繰返し: トゥーン輪郭抑止情報 (NON_TOON_OUTLINE 立っている場合のみ)]
1 頂点 1 ビット、余り 8 bit 未満はパディング
MV1_MATERIAL_F1| フィールド | 意味 |
|---|---|
Name / Index |
名前 / インデックス |
Diffuse / Ambient / Specular / Emissive |
COLOR_F (float×4) |
Power |
スペキュラパワー |
Alpha |
不透明度 |
DiffuseLayerNum / DiffuseLayer[8] |
MV1_MATERIAL_LAYER_F1 の 8 段配列 (1 段目以降は BlendType 有効) |
SpecularLayerNum / SpecularLayer[8] |
スペキュラマップ |
NormalLayerNum / NormalLayer[8] |
法線マップ |
UseAlphaTest / AlphaFunc / AlphaRef |
アルファテスト |
DrawBlendMode / DrawBlendParam |
出力時ブレンド |
ToonInfo (ptr or NULL) |
MV1_MATERIAL_TOON_F1 へのオフセット |
MV1_MATERIAL_LAYER_F1struct MV1_MATERIAL_LAYER_F1 {
int Texture ; // MV1_TEXTURE_F1 のインデックス
int BlendType ; // DX_MATERIAL_BLENDTYPE_ADDITIVE など
DWORD Padding[4] ;
} ;
MV1_MATERIAL_TOON_F1struct MV1_MATERIAL_TOON_F1 {
int Type ; // DX_MATERIAL_TYPE_TOON など
int DiffuseGradTexture ; // ディフューズグラデ texture index (-1=デフォルト)
int SpecularGradTexture ; // スペキュラグラデ
int DiffuseGradBlendType ; // ブレンドタイプ
int SpecularGradBlendType ;
float OutLineWidth ; // 輪郭線幅 (0..1)
COLOR_F OutLineColor ;
float OutLineDotWidth ; // ドット単位幅
BYTE EnableSphereMap ; // スフィアマップ有効
BYTE SphereMapBlendType ;
short SphereMapTexture ;
DWORD Padding[2] ;
} ;
MV1_TEXTURE_F1| フィールド | 意味 |
|---|---|
Name / Index |
名前 / インデックス |
ColorFilePath |
カラーチャンネル画像の相対パス (文字列プールへのオフセット) |
AlphaFilePath |
アルファチャンネル別画像の相対パス (単独使用時) |
BumpImageFlag / BumpImageNextPixelLength |
バンプマップ情報 |
AddressModeU / AddressModeV |
MV1_TEXTURE_ADDRESS_MODE_WRAP / MIRROR / CLAMP / BORDER |
FilterMode |
MV1_TEXTURE_FILTER_MODE_POINT / LINEAR など |
Flag |
MV1_TEXTURE_FLAG_REVERSE / BMP32_ALL_ZERO_ALPHA_TO_XRGB8 / VALID_SCALE_UV |
ScaleU / ScaleV |
UV スケール (VALID_SCALE_UV 時のみ) |
MV1_TRIANGLE_LIST_F1struct MV1_TRIANGLE_LIST_F1 {
DWORD DimPrev, DimNext ;
int Index ;
DWORD Container ; // ptr to MV1_MESH_F1
WORD VertexType ; // MV1_VERTEX_TYPE_NORMAL / SKIN_4BONE / SKIN_8BONE / SKIN_FREEBONE
WORD Flag ; // MV1_TRIANGLE_LIST_FLAG_* (インデックス型 + 法線型)
WORD VertexNum ;
WORD IndexNum ;
DWORD MeshVertexIndexAndIndexData ;
DWORD Padding[2] ;
} ;
MeshVertexIndexAndIndexData 構造[VertexType == SKIN_4BONE / SKIN_8BONE の場合]
WORD UseBoneNum ; // 使用ボーン数
WORD BoneIndices[UseBoneNum] ;
[VertexType == SKIN_FREEBONE の場合]
WORD MaxBoneNum ; // 最大使用ボーン数のみ
[VertexNum 個のメッシュ頂点インデックス]
Flag & MV1_TRIANGLE_LIST_FLAG_MVERT_INDEX_MASK に応じた型 (u8/u16/u32)
[IndexNum 個の頂点インデックス]
Flag & MV1_TRIANGLE_LIST_FLAG_INDEX_MASK に応じた型
MV1_SKIN_BONE_F1struct MV1_SKIN_BONE_F1 {
DWORD DimPrev, DimNext ;
int Index ;
int BoneFrame ; // ボーンとして使うフレーム index
MATRIX_4X4CT_F ModelLocalMatrix ; // 4x4 行 3 行形式 (省略 4 行目)
int ModelLocalMatrixIsTranslateOnly ; // 並進のみ=1
int UseFrameNum ;
DWORD UseFrame ; // MV1_SKIN_BONE_USE_FRAME_F1[]
DWORD Padding[2] ;
} ;
MV1_LIGHT_F1MV1_LIGHT_TYPE_POINT / DIRECTIONAL / SPOT。色 3 種 (Diffuse/Specular/Ambient)、減衰係数 3 つ (Attenuation0..2)、スポット角 2 つ (Theta/Phi)、フォールオフ、有効距離。
3 層構造:
MV1_ANIMSET_F1 — 「歩行」「攻撃」等のアニメーション集合。Name で名前アクセス可。MV1_ANIM_F1 — セット内の各アニメ。対象フレーム・回転オーダー・キーセット配列を持つ。MV1_ANIM_KEYSET_F1 — キーフレーム列本体。Type (MV1_ANIMKEY_TYPE_*) × DataType (ROTATE / TRANSLATE / SCALE / MATRIX / SHAPE 等) の組合せでキー格納形式が変わる。Flag ビット (MV1_ANIM_KEYSET_FLAG_*) で時間・キー値の圧縮形態を切替:
KEY_ONE — キー数は 1 (時間軸無し、静的)KEYNUM_B / KEYNUM_W — キー数を 1 byte / 2 byte で格納TIME_BIT16 / TIME_UNIT / TIME_UNIT_ST_W / _ST_Z / _UN_W — 時間軸を 16bit / 固定間隔 (start+unit) / 0 開始 等で圧縮KEY_BIT16 — キー値を 16bit 化 (Min + Unit * 16bit_index の形式)KeyData ブロックの可変長レイアウトは DxModelFile.h 130-175 行のコメントに詳細記述あり。
MV1_ANIM_KEY_MATRIX4X4C_F1 — float[4][3] (4 列目 (0,0,0,1) 固定)MV1_ANIM_KEY_MATRIX3X3_F1 — float[3][3]MV1_ANIM_KEY_MATRIX3X3_B16_F1 — WORD[3][3] (16bit 圧縮)MV1_ROTATE_F1 union の Qt 型)MV1_ANIM_KEY_16BIT_F1struct MV1_ANIM_KEY_16BIT_F1 {
BYTE Min ; // bit7: zero指標 / bit6: 符号 / bit5: 指数符号 / bit4-0: 指数 (10^-15 ... 10^15)
BYTE Unit ; // bit7: 指数符号 / bit6-4: 指数 (10^-7 ... 10^7) / bit3-0: 乗算整数値 (0-15)
} ;
→ 実値 = Min * 10^sMin + unit16bit * Unit * 10^sUnit * mulInt。16bit インデックスを用いて小さなダイナミックレンジを表現。
MV1_FILEHEAD_SHAPE_F1顔表情等のモーフターゲット。MV1_SHAPE_F1 は複数の MV1_SHAPE_MESH_F1 を持ち、各々が MV1_SHAPE_VERTEX_F1[] を保持。シェイプ頂点は base からの差分を保存 (Position/Normal)。IsVertexPress=1 で頂点データ圧縮対応。
MV1_FILEHEAD_PHYSICS_F1Bullet Physics 用の剛体 + ジョイント情報。
MV1_PHYSICS_RIGIDBODY_F1: ShapeType (0=球 / 1=箱 / 2=カプセル) + W/H/D、位置/回転、重さ、摩擦、弾性、タイプ (Bone 追従 / 自由 / Bone 位置揃え)MV1_PHYSICS_JOINT_F1: 2 剛体間の bound、位置/回転制限、スプリング定数| フィールド | 意味 |
|---|---|
RightHandType |
TRUE で右手系 (OpenGL 系) / FALSE で左手系 (DirectX 系) |
| 頂点 UV V | 下向きが正 (画像メモリ順と一致) |
loader 側で座標系変換を行うかどうかは呼び出し側次第。
StringBuffer全 Name / ColorFilePath 等は "文字列プール" に連結格納された null 終端文字列の先頭からのバイトオフセット。IsStringUTF8 フラグで UTF-8 / Shift-JIS を選択。
現行は Version = 1 のみ。将来の "MV12" 等の拡張に備え、loader は CheckID == "MV11" のみ受理する設計。
DXA_Encode 経由でのみ生成。MV1_MESH_VERT_FLAG_NON_TOON_OUTLINE フラグで頂点単位に ON/OFF 指定。MV1_LIGHT_F1) の MV1_LIGHT_F1 は MV1_FRAME_F1.Light 経由でフレームに属する (独立ライトリストは Light / LightNum 参照)。ChangeMatrixTable / ChangeDrawMaterialTable は 0 にしてはいけない — 詳細は Writer の落とし穴 を参照。DxLib 互換の .mv1 を自作 writer で生成する際、loader で 必ず crash する罠を以下に列挙。
ChangeMatrixTable / ChangeDrawMaterialTable は必ず非 0 の buffer を指すべき| フィールド | 型 | 意味 |
|---|---|---|
ChangeDrawMaterialTableSize | int | 描画マテリアル変更追跡用 bit-flag table のバイト数 |
ChangeMatrixTableSize | int | 行列変更追跡用 bit-flag table のバイト数 |
ChangeDrawMaterialTable | DWORD (offset) | 上記 bit-flag table の実データ位置 |
ChangeMatrixTable | DWORD (offset) | 同上 |
これらを 0 / 空にしてはいけない。DxLib は frame / mesh / material の状態変化を 1 bit / element で追跡する runtime テーブルを持ち、load 時にこの領域を _MEMCPY して MBase->ChangeMatrixTable 等のポインタに設定する。サイズ 0 で確保すると runtime の bit 操作で境界外 write が発生し、debug allocator の MagicID tag 破壊 → GetAllocSize Error : メモリタグの MagicID が不正です → crash に至る。
データ依存で再現する 厄介な症状で、frame 数が閾値 (5 前後、buffer 配置次第) を超えると初めて crash する。試行錯誤で原因特定が非常に困難。
max(FrameNum, MeshNum, MaterialNum) bit 分の DWORD 整列を確保。ChangeMatrixTable / ChangeDrawMaterialTable は上記 buffer 先頭へのファイルオフセットを指す。両者 別々 の buffer にすること。// Writer 実装例 (C++)
const int32_t changeTableSize = 256; // 2048 bit、frame 数に応じて可変でも可
uint32_t offChangeDrawMatTable = append_zero_bytes(changeTableSize); // 全 0
uint32_t offChangeMatTable = append_zero_bytes(changeTableSize); // 全 0
hdr.ChangeDrawMaterialTableSize = changeTableSize;
hdr.ChangeDrawMaterialTable = offChangeDrawMatTable;
hdr.ChangeMatrixTableSize = changeTableSize;
hdr.ChangeMatrixTable = offChangeMatTable;
各 MV1_FRAME_F1 / MV1_MESH_F1 の ChangeInfo (MV1_CHANGE_F1) フィールドはすべて 0 で OK (runtime で table への offset を逆算するため、Target / Fill = 0 のままで一貫している)。
NON_TOON_OUTLINE フラグを立てたら bit data を忘れずにMV1_MESH_F1.VertFlag bit 6 (MV1_MESH_VERT_FLAG_NON_TOON_OUTLINE) を立てた場合、VertexData blob の末尾に VertexNum ビット分のデータ を書く必要がある (各頂点の ToonOutLineScale > 0 か否か 1 bit)。省略するとフラグが立っているのに bit data が無いとして load 時に overrun する。
全頂点を「輪郭なし」にするなら 0xFF で埋める (bit=1 = ToonOutLineScale=0)。その後 4 byte 整列までゼロ padding。
TriangleListNormalPositionNum は per-corner 合計であるべき非 skin の MV1_VERTEX_TYPE_NORMAL な triangle list では、この値は 全 triangle list の VertexNum 合計 でなければならない (DxLib は MV1_TLIST_NORMAL_POS[N] のメモリを N 個分確保し、各 TL が自分の VertexNum 分をスライス消費する)。ずれると 16 byte align 再計算で末尾の TL が overflow する。
TriangleListSkinPosition4BNum に per-corner 合計を入れる (非 skin は 0)MV1_VERTEX_TYPE_SKIN_4BONE の triangle list を含む場合、DxLib は runtime の MV1_TLIST_SKIN_POS_4B[N] 配列を確保する。カウンタは TriangleListSkinPosition4BNum。非 skin 用の TriangleListNormalPositionNum はこのとき 0 でなければならない (両方同時に立てると allocation 境界で overlap → crash)。
| VertexType | TriangleListNormalPositionNum | TriangleListSkinPosition4BNum |
|---|---|---|
MV1_VERTEX_TYPE_NORMAL | sum(TL.VertexNum) | 0 |
MV1_VERTEX_TYPE_SKIN_4BONE | 0 | sum(TL.VertexNum) |
MV1_VERTEX_TYPE_SKIN_8BONE | 0 | 0 (SkinPosition8BNum を使う) |
MeshPositionSize = Σ(PositionNum × 44)、非 skin は × 12DxLib の runtime では MV1_MESH_POSITION 構造体サイズは MaxBoneBlendNum 依存で可変:
PosUnitSize = sizeof(MV1_MESH_POSITION:44) + (MaxBoneBlendNum - 4) × sizeof(MV1_SKINBONE_BLEND:8)
| Mode | MaxBoneBlendNum | PosUnitSize | 内訳 |
|---|---|---|---|
| 非 skin | 0 | 12 | Position (VECTOR=12) のみ、BoneWeight スロット無し |
| 4 ボーン skin | 4 | 44 | Position (12) + BoneWeight[4] (32) |
| 8 ボーン skin | 8 | 76 | Position (12) + BoneWeight[8] (64) |
Header の MeshPositionSize は全フレーム分の合計: Σ(Frame.PositionNum × PosUnitSize)。非 skin と同じ × 12 で書くと、skin 時は allocation が小さすぎて隣接バッファを上書きしてクラッシュ。
TopFrameNum=M の top-level 兄弟にして Prev / Next で連結する。TopFrameNum=1。空の root を挟むと一部の FirstChild / LastChild 走査で特定条件下に crash。
1. "MV11" マジック確認
2. DXA_Decode で伸長後サイズ取得 → 確保 → 再 DXA_Decode
3. MV1MODEL_FILEHEADER_F1 を解析
4. オフセット → ポインタ変換 (全ての配列とネスト構造)
5. 文字列プールから Name / FilePath 等を wchar_t にコピー
6. MV1_MODEL_BASE 構造体 (ランタイム表現) を malloc 一括確保してフィールド単位コピー
7. テクスチャファイルを LoadGraph で順次ロード (ColorFilePath / AlphaFilePath)
8. ハンドル値を返却 (DX_HANDLETYPE_MODEL)
┌─────────────────────────────────────────────┐
│ Magic "MV11" (4 bytes) │
├─────────────────────────────────────────────┤
│ DXA compressed block │
│ ╭─────────────────────────────────╮ │
│ │ MV1MODEL_FILEHEADER_F1 (~128B) │ │
│ │ CheckID / Version │ │
│ │ FrameNum + Frame ptr │────┐ │
│ │ MaterialNum + Material ptr │──┐ │ │
│ │ TextureNum + Texture ptr │┐ │ │ │
│ │ MeshNum + Mesh ptr │┼─┐ │ │
│ │ TriangleListNum + TL ptr │┼│┼─┐ │
│ │ SkinBoneNum + SkinBone ptr │┼│┼│┼─┐ │
│ │ VertexData ptr │┼│┼│┼│┼─┐
│ │ StringBuffer ptr │┼│┼│┼│┼│┼─┐
│ │ AnimSetNum + AnimSet ptr │┼│┼│┼│┼│┼│┼─┐
│ │ Shape ptr / Physics ptr │┼│┼│┼│┼│┼│┼│┼─┐
│ ╰─────────────────────────────────╯│ │ │ │ │ │ │
│ ╭──────────────────────────────────┘ │ │ │ │ │ │
│ │ MV1_TEXTURE_F1 [TextureNum] │ │ │ │ │ │
│ ╰────────────────────────────────────┘ │ │ │ │ │
│ ╭──────────────────────────────────────┘ │ │ │ │
│ │ MV1_MATERIAL_F1 [MaterialNum] │ │ │ │
│ ╰────────────────────────────────────────┘ │ │ │
│ ╭──────────────────────────────────────────┘ │ │
│ │ MV1_FRAME_F1 [FrameNum] │ │
│ │ with PositionAndNormalData inline │ │
│ ╰────────────────────────────────────────────┘ │
│ ╭──────────────────────────────────────────────┘
│ │ MV1_MESH_F1 [MeshNum]
│ │ with VertexData inline
│ ╰────────────────────────────────────────────
│ ...
│ ╭────────────────────────────────────────────
│ │ StringBuffer (UTF-8 or Shift-JIS, null-concat)
│ ╰────────────────────────────────────────────
│ ╭────────────────────────────────────────────
│ │ AnimKeyData (可変長、フラグで圧縮形式切替)
│ ╰────────────────────────────────────────────
└─────────────────────────────────────────────┘
MV1SaveModelToMV1File() の引数:
MHandle — 保存対象モデルハンドルFileName — 出力パスSaveType — 0=アニメ込み / 1=ジオメトリのみ / 2=アニメのみAnimMHandle — 別モデルのアニメを借りる場合AnimNameCheck — アニメ名照合モードNormal8BitFlag — 法線 8bit 圧縮Position16BitFlag — 座標 16bit 圧縮Weight8BitFlag — スキンウェイト 8bit 圧縮Anim16BitFlag — アニメキー 16bit 圧縮圧縮フラグを立てるとファイル容量は 40〜60% 削減されるが精度低下に注意。
このドキュメントは DxLib 3.24f 時点の .mv1 フォーマット (Version 1, "MV11" magic) を対象とする。将来のバージョン拡張には未対応。
本文で参照する定数の実数値。出典: DxLib.h / DxModel.h / DxModelFile.h。
MV1_VERTEX_TYPE_* — 頂点型 (MV1_TRIANGLE_LIST_F1.VertexType)| 名称 | 値 | 意味 |
|---|---|---|
MV1_VERTEX_TYPE_NORMAL |
0 | 通常メッシュ (スキンなし) |
MV1_VERTEX_TYPE_SKIN_4BONE |
1 | スキンメッシュ 1〜4 ボーン |
MV1_VERTEX_TYPE_SKIN_8BONE |
2 | スキンメッシュ 5〜8 ボーン |
MV1_VERTEX_TYPE_SKIN_FREEBONE |
3 | 9 ボーン以上の可変長スキン |
MV1_ROTATE_TYPE_* — 回転表現| 名称 | 値 | 意味 |
|---|---|---|
MV1_ROTATE_TYPE_XYZROT |
0 | Euler 角 (XYZ 軸) |
MV1_ROTATE_TYPE_QUATERNION |
1 | クォータニオン |
MV1_ROTATE_TYPE_MATRIX |
2 | 行列 (MATRIX_4X4CT_F) |
MV1_ROTATE_TYPE_ZAXIS |
3 | Z 軸 + ローカルベクトル + スケール |
MV1_ROTATE_ORDER_* — Euler 角オーダー| 名称 | 値 |
|---|---|
MV1_ROTATE_ORDER_XYZ |
0 |
MV1_ROTATE_ORDER_XZY |
1 |
MV1_ROTATE_ORDER_YZX |
2 |
MV1_ROTATE_ORDER_YXZ |
3 |
MV1_ROTATE_ORDER_ZXY |
4 |
MV1_ROTATE_ORDER_ZYX |
5 |
MV1_ANIMKEY_TYPE_* — アニメキーのデータエンコード| 名称 | 値 | 意味 |
|---|---|---|
MV1_ANIMKEY_TYPE_QUATERNION_X |
0 | クォータニオン (X ファイル形式) |
MV1_ANIMKEY_TYPE_VECTOR |
1 | ベクトル |
MV1_ANIMKEY_TYPE_MATRIX4X4C |
2 | 4x4 行列 (4 行目固定) |
MV1_ANIMKEY_TYPE_MATRIX3X3 |
3 | 3x3 行列 |
MV1_ANIMKEY_TYPE_FLAT |
4 | 定数補間 (階段関数) |
MV1_ANIMKEY_TYPE_LINEAR |
5 | 線形補間 |
MV1_ANIMKEY_TYPE_BLEND |
6 | スプライン補間 |
MV1_ANIMKEY_TYPE_QUATERNION_VMD |
7 | クォータニオン (VMD 形式) |
MV1_ANIMKEY_DATATYPE_* — アニメキーの対象| 名称 | 値 | 意味 |
|---|---|---|
MV1_ANIMKEY_DATATYPE_ROTATE |
0 | 全軸回転 |
MV1_ANIMKEY_DATATYPE_ROTATE_X |
1 | X 軸回転 |
MV1_ANIMKEY_DATATYPE_ROTATE_Y |
2 | Y 軸回転 |
MV1_ANIMKEY_DATATYPE_ROTATE_Z |
3 | Z 軸回転 |
MV1_ANIMKEY_DATATYPE_SCALE |
5 | 全軸スケール |
MV1_ANIMKEY_DATATYPE_SCALE_X |
6 | X スケール |
MV1_ANIMKEY_DATATYPE_SCALE_Y |
7 | Y スケール |
MV1_ANIMKEY_DATATYPE_SCALE_Z |
8 | Z スケール |
MV1_ANIMKEY_DATATYPE_TRANSLATE |
10 | 全軸並進 |
MV1_ANIMKEY_DATATYPE_TRANSLATE_X |
11 | X 並進 |
MV1_ANIMKEY_DATATYPE_TRANSLATE_Y |
12 | Y 並進 |
MV1_ANIMKEY_DATATYPE_TRANSLATE_Z |
13 | Z 並進 |
MV1_ANIMKEY_DATATYPE_MATRIX4X4C |
15 | 4x4 行列 |
MV1_ANIMKEY_DATATYPE_MATRIX3X3 |
17 | 3x3 行列 |
MV1_ANIMKEY_DATATYPE_SHAPE |
18 | シェイプ (モーフ) ウェイト |
MV1_ANIMKEY_DATATYPE_OTHRE |
20 | その他 (typo は原文ママ: OTHER ではなく OTHRE) |
MV1_LIGHT_TYPE_*| 名称 | 値 |
|---|---|
MV1_LIGHT_TYPE_POINT |
0 |
MV1_LIGHT_TYPE_SPOT |
1 |
MV1_LIGHT_TYPE_DIRECTIONAL |
2 |
MV1_FRAMEFLAG_* (ビットフラグ)| 名称 | bit | 意味 |
|---|---|---|
MV1_FRAMEFLAG_VISIBLE |
0x0001 | 表示 |
MV1_FRAMEFLAG_IGNOREPARENTTRANS |
0x0002 | 親の変換を無視 |
MV1_FRAMEFLAG_PREROTATE |
0x0004 | 前回転 (PreRotate) 有効 |
MV1_FRAMEFLAG_POSTROTATE |
0x0008 | 後回転 (PostRotate) 有効 |
MV1_FRAMEFLAG_TANGENT_BINORMAL |
0x0010 | 接線 / 従法線データあり |
DX_MATERIAL_TYPE_* (MV1_MATERIAL_TOON_F1.Type)| 名称 | 値 | 意味 |
|---|---|---|
DX_MATERIAL_TYPE_NORMAL |
0 | 通常 |
DX_MATERIAL_TYPE_TOON |
1 | トゥーン |
DX_MATERIAL_TYPE_TOON_2 |
2 | トゥーン (MMD 互換) |
DX_MATERIAL_TYPE_MAT_SPEC_LUMINANCE_UNORM |
3 | スペキュラ輝度 (0..1 正規化) |
DX_MATERIAL_TYPE_MAT_SPEC_LUMINANCE_CLIP_UNORM |
4 | 同 (クリップ付き) |
DX_MATERIAL_TYPE_MAT_SPEC_LUMINANCE_CMP_GREATEREQUAL |
5 | 同 (≥ 比較) |
DX_MATERIAL_TYPE_MAT_SPEC_POWER_UNORM |
6 | スペキュラ強度 |
DX_MATERIAL_TYPE_MAT_SPEC_POWER_CLIP_UNORM |
7 | 同 (クリップ付き) |
DX_MATERIAL_TYPE_MAT_SPEC_POWER_CMP_GREATEREQUAL |
8 | 同 (≥ 比較) |
DX_MATERIAL_BLENDTYPE_* (レイヤブレンド種別)| 名称 | 値 |
|---|---|
DX_MATERIAL_BLENDTYPE_TRANSLUCENT |
0 |
DX_MATERIAL_BLENDTYPE_ADDITIVE |
1 |
DX_MATERIAL_BLENDTYPE_MODULATE |
2 |
DX_MATERIAL_BLENDTYPE_NONE |
3 |
MV1_LAYERBLEND_TYPE_*| 名称 | 値 |
|---|---|
MV1_LAYERBLEND_TYPE_TRANSLUCENT |
0 |
MV1_LAYERBLEND_TYPE_ADDITIVE |
1 |
MV1_LAYERBLEND_TYPE_MODULATE |
2 |
MV1_LAYERBLEND_TYPE_MODULATE2 |
3 |
DX_BLENDMODE_* (描画時ブレンド、MV1_MATERIAL_F1.DrawBlendMode)主要値のみ (全 38 種):
| 名称 | 値 |
|---|---|
DX_BLENDMODE_NOBLEND |
0 |
DX_BLENDMODE_ALPHA |
1 |
DX_BLENDMODE_ADD |
2 |
DX_BLENDMODE_SUB |
3 |
DX_BLENDMODE_MUL |
4 |
DX_BLENDMODE_INVSRC |
10 |
DX_BLENDMODE_PMA_ALPHA |
17 |
DX_BLENDMODE_PMA_ADD |
18 |
DX_BLENDMODE_CUSTOM |
32 |
MV1_TEXTURE_ADDRESS_MODE_*| 名称 | 値 |
|---|---|
MV1_TEXTURE_ADDRESS_MODE_WRAP |
0 |
MV1_TEXTURE_ADDRESS_MODE_MIRROR |
1 |
MV1_TEXTURE_ADDRESS_MODE_CLAMP |
2 |
MV1_TEXTURE_FILTER_MODE_*| 名称 | 値 |
|---|---|
MV1_TEXTURE_FILTER_MODE_POINT |
0 |
MV1_TEXTURE_FILTER_MODE_LINEAR |
1 |
MV1_TEXTURE_FILTER_MODE_ANISOTROPIC |
2 |
MV1_ANIM_KEYSET_FLAG_* (16bit ビットフラグ)| 名称 | bit |
|---|---|
MV1_ANIM_KEYSET_FLAG_KEY_ONE |
0x0001 |
MV1_ANIM_KEYSET_FLAG_KEYNUM_B |
0x0002 |
MV1_ANIM_KEYSET_FLAG_KEYNUM_W |
0x0004 |
MV1_ANIM_KEYSET_FLAG_TIME_UNIT |
0x0008 |
MV1_ANIM_KEYSET_FLAG_TIME_UNIT_ST_W |
0x0010 |
MV1_ANIM_KEYSET_FLAG_TIME_UNIT_ST_Z |
0x0020 |
MV1_ANIM_KEYSET_FLAG_TIME_UNIT_UN_W |
0x0040 |
MV1_ANIM_KEYSET_FLAG_TIME_BIT16 |
0x0080 |
MV1_ANIM_KEYSET_FLAG_KEY_BIT16 |
0x0100 |
MV1_ANIM_KEYSET_FLAG_KEY_MP_PP |
0x0200 |
MV1_ANIM_KEYSET_FLAG_KEY_Z_TP |
0x0400 |
MV1_FRAME_VERT_FLAG_*| 名称 | bit |
|---|---|
MV1_FRAME_VERT_FLAG_NORMAL_TYPE_MASK |
0x0003 |
MV1_FRAME_VERT_FLAG_POSITION_B16 |
0x0004 |
MV1_FRAME_VERT_FLAG_MATRIX_WEIGHT_NONE |
0x0008 |
MV1_FRAME_VERT_FLAG_MATRIX_INDEX_MASK |
0x0010 |
MV1_FRAME_VERT_FLAG_MATRIX_WEIGHT_MASK |
0x0020 |
MV1_FRAME_VERT_FLAG_NOMRAL_TANGENT_BINORMAL |
0x0040 |
MV1_MESH_VERT_FLAG_*| 名称 | bit |
|---|---|
MV1_MESH_VERT_FLAG_POS_IND_TYPE_MASK |
0x0003 |
MV1_MESH_VERT_FLAG_NRM_IND_TYPE_MASK |
0x000c |
MV1_MESH_VERT_FLAG_UV_U16 |
0x0010 |
MV1_MESH_VERT_FLAG_COMMON_COLOR |
0x0020 |
MV1_MESH_VERT_FLAG_NON_TOON_OUTLINE |
0x0040 |
MV1_TRIANGLE_LIST_FLAG_*| 名称 | bit |
|---|---|
MV1_TRIANGLE_LIST_FLAG_MVERT_INDEX_MASK |
0x0003 |
MV1_TRIANGLE_LIST_FLAG_INDEX_MASK |
0x000c |
MV1_TEXTURE_FLAG_*| 名称 | bit |
|---|---|
MV1_TEXTURE_FLAG_REVERSE |
0x0001 |
MV1_TEXTURE_FLAG_BMP32_ALL_ZERO_ALPHA_TO_XRGB8 |
0x0002 |
MV1_TEXTURE_FLAG_VALID_SCALE_UV |
0x0004 |
MV1_FRAME_NORMAL_TYPE_* (2 bit):
| 名称 | 値 | サイズ/頂点 |
|---|---|---|
NONE |
0 | 0 (自動計算) |
S8 |
1 | 3 byte |
S16 |
2 | 6 byte |
F32 |
3 | 12 byte |
MV1_FRAME_MATRIX_INDEX_TYPE_* / MV1_FRAME_MATRIX_WEIGHT_TYPE_* (各 1 bit):
| 名称 | 値 | サイズ |
|---|---|---|
U8 |
0 | 1 byte |
U16 |
1 | 2 byte |
MV1_MESH_VERT_INDEX_TYPE_* (2 bit):
| 名称 | 値 | サイズ |
|---|---|---|
NONE |
0 | 0 |
U8 |
1 | 1 byte |
U16 |
2 | 2 byte |
U32 |
3 | 4 byte |
MV1_TRIANGLE_LIST_INDEX_TYPE_* (2 bit):
| 名称 | 値 | サイズ |
|---|---|---|
U8 |
0 | 1 byte |
U16 |
1 | 2 byte |
U32 |
2 | 4 byte |
| 名称 | 値 |
|---|---|
DX_HANDLETYPE_MODEL_BASE |
13 |
DX_HANDLETYPE_MODEL |
14 |
MATERIAL_TYPEPARAM_MAX_NUM = 4 (MV1_MATERIAL_BASE.TypeParam の要素数)
MV1_MESH_F1.VertexDataMV1_MESH_F1.VertFlag と UVUnitNum / UVSetUnitNum に応じて可変長。
VertexData の読み出し順序:
┌─────────────────────────────────────────────────────┐
│ [step 1] 共通色 (VertFlag & COMMON_COLOR が true) │
│ COLOR_U8 DiffuseColor ; // 4 byte (B,G,R,A) │
│ COLOR_U8 SpecularColor ; // 4 byte │
├─────────────────────────────────────────────────────┤
│ [step 2] 位置インデックス × VertexNum │
│ size_per_elem = sizeof_index_type( │
│ VertFlag & MV1_MESH_VERT_FLAG_POS_IND_TYPE_MASK)│
│ 合計サイズ = VertexNum * size_per_elem │
├─────────────────────────────────────────────────────┤
│ [step 3] 法線インデックス × VertexNum │
│ size_per_elem = sizeof_index_type( │
│ (VertFlag & MV1_MESH_VERT_FLAG_NRM_IND_TYPE_MASK) >> 2)│
├─────────────────────────────────────────────────────┤
│ [step 4] 頂点色 × VertexNum (COMMON_COLOR が false) │
│ COLOR_U8 Diffuse + COLOR_U8 Specular = 8 byte/頂点│
├─────────────────────────────────────────────────────┤
│ [step 5] UV 値 × VertexNum × (UVUnitNum * UVSetUnitNum)│
│ UV_U16 フラグ立ち: WORD (16bit 固定少数、65535=1.0) │
│ UV_U16 フラグ無し: float │
│ 各 UV ペアは (u, v) の 2 成分 │
│ 合計 = VertexNum * UVUnitNum * UVSetUnitNum * 2 *│
│ (UV_U16 ? 2 : 4) │
├─────────────────────────────────────────────────────┤
│ [step 6] トゥーン輪郭抑止ビット × VertexNum │
│ (NON_TOON_OUTLINE フラグが立っている場合のみ) │
│ 1 頂点 1 bit、LSB から詰める │
│ 合計 = ceil( VertexNum / 8 ) byte │
└─────────────────────────────────────────────────────┘
疑似コード:
size_t sizeof_index_type( int type_code ) {
switch ( type_code ) {
case 0 /*NONE*/: return 0 ;
case 1 /*U8*/ : return 1 ;
case 2 /*U16*/ : return 2 ;
case 3 /*U32*/ : return 4 ;
}
return 0 ;
}
size_t calc_vertex_data_size( MV1_MESH_F1 *m ) {
size_t s = 0 ;
if ( m->VertFlag & 0x0020 /*COMMON_COLOR*/ ) s += 8 ;
int posType = m->VertFlag & 0x3 ;
int nrmType = ( m->VertFlag >> 2 ) & 0x3 ;
s += (size_t)m->VertexNum * sizeof_index_type( posType ) ;
s += (size_t)m->VertexNum * sizeof_index_type( nrmType ) ;
if ( !( m->VertFlag & 0x0020 ) ) s += (size_t)m->VertexNum * 8 ;
int uvUnit = m->UVUnitNum * m->UVSetUnitNum ;
size_t uvElem = ( m->VertFlag & 0x0010 /*UV_U16*/ ) ? 2 : 4 ;
s += (size_t)m->VertexNum * uvUnit * 2 * uvElem ;
if ( m->VertFlag & 0x0040 /*NON_TOON_OUTLINE*/ )
s += ( (size_t)m->VertexNum + 7 ) / 8 ;
return s ;
}
MV1_FRAME_F1.PositionAndNormalDataVertFlag + PositionNum / NormalNum / MaxBoneBlendNum に応じて可変:
┌─────────────────────────────────────────────────────┐
│ [step 1] POSITION_B16 フラグ立ちの時のみ │
│ MV1_POSITION_16BIT_SUBINFO_F1 xSubinfo ; /* 8B */│
│ MV1_POSITION_16BIT_SUBINFO_F1 ySubinfo ; /* 8B */│
│ MV1_POSITION_16BIT_SUBINFO_F1 zSubinfo ; /* 8B */│
│ struct { float Min; float Width; } ; │
├─────────────────────────────────────────────────────┤
│ [step 2] 座標データ × PositionNum │
│ POSITION_B16 立っていれば WORD[3] (6 byte) │
│ 立っていなければ float[3] (12 byte) │
│ │
│ WORD 値 w (0..65535) からの復元: │
│ x = xSubinfo.Min + (w / 65535.0) * xSubinfo.Width│
│ (y, z も同様) │
├─────────────────────────────────────────────────────┤
│ [step 3] スキニング情報 (!MATRIX_WEIGHT_NONE の場合)│
│ 座標 × PositionNum 回: │
│ MaxBoneBlendNum ボーンぶん or インデックス=-1ま│
│ { │
│ bone_index (U8 or U16, MATRIX_INDEX_MASK 依存)│
│ weight (U8 or U16, MATRIX_WEIGHT_MASK 依存)│
│ } │
│ 終端条件: -1 (U8=0xFF, U16=0xFFFF) を bone_index │
│ で読んだら、その weight は読まず次頂点へ│
├─────────────────────────────────────────────────────┤
│ [step 4] 法線データ × NormalNum │
│ (VertFlag & NORMAL_TYPE_MASK) に応じて型: │
│ NONE (0) = スキップ │
│ S8 (1) = s8 × 3 (3 byte) │
│ S16 (2) = s16 × 3 (6 byte) │
│ F32 (3) = float × 3 (12 byte) │
│ │
│ S8/S16 の場合は (-1.0 .. +1.0) を (-127..127) / │
│ (-32767..32767) にマップ。デコード時に正規化要。 │
│ │
│ TANGENT_BINORMAL フラグ立ち時は法線 3 成分の後に │
│ 接線 + 従法線の 2 ベクトル (同じ型) が続く │
└─────────────────────────────────────────────────────┘
MV1_TRIANGLE_LIST_F1.MeshVertexIndexAndIndexData┌─────────────────────────────────────────────────────┐
│ [step 1] 使用ボーン情報 (VertexType 依存) │
│ │
│ NORMAL (0): なし (0 byte) │
│ │
│ SKIN_4BONE (1): │
│ WORD UseBoneNum ; │
│ WORD BoneIndices[ UseBoneNum ] ; │
│ │
│ SKIN_8BONE (2): │
│ WORD UseBoneNum ; │
│ WORD BoneIndices[ UseBoneNum ] ; │
│ │
│ SKIN_FREEBONE (3): │
│ WORD MaxBoneNum ; /* 1 頂点あたりの最大使用数 */│
├─────────────────────────────────────────────────────┤
│ [step 2] メッシュ頂点インデックス × VertexNum │
│ 型: (Flag & MVERT_INDEX_MASK) に応じて U8/U16/U32│
├─────────────────────────────────────────────────────┤
│ [step 3] 頂点インデックス × IndexNum │
│ 型: ((Flag & INDEX_MASK) >> 2) に応じて U8/U16/U32│
│ 3 個で 1 三角形 │
└─────────────────────────────────────────────────────┘
SKIN_FREEBONE の追加情報: MaxBoneNum は 1 頂点が参照する最大ボーン数。各頂点のボーン並びは別途 MV1_FRAME_F1.PositionAndNormalData 内に格納される (step 3 の可変長スキニング情報、-1 終端)。
StringBuffer のアクセスconst char *get_string( uint8_t *base, uint32_t strOffset ) {
return (const char *)( base + header->StringBuffer + strOffset ) ;
}
strOffset は StringBuffer 先頭からの byte 単位オフセットIsStringUTF8 == 1 なら UTF-8、0 なら Shift-JIS (CP932)Name = 0 の場合は無名 (NULL 文字列扱い)アニメキー値を 16bit で格納する場合、MV1_ANIM_KEY_16BIT_F1 補助構造体 2 byte で「最小値」と「単位」を記述し、16bit index からの浮動小数復元は以下のビットパック:
MV1_ANIM_KEY_16BIT_F1struct MV1_ANIM_KEY_16BIT_F1 {
uint8_t Min ; /* 最小値の符号付き浮動小数 (ビットパック) */
uint8_t Unit ; /* 16bit 値 1 あたりの刻み値 (ビットパック) */
} ;
Min のビットレイアウト (1 byte):
| ビット | 意味 |
|---|---|
| bit 7 | ゼロフラグ (1=Min が 0、以下無視) |
| bit 6 | 符号 (0=正, 1=負) |
| bit 5 | 指数符号 (0=正指数, 1=負指数) |
| bit 4-0 | 指数値 (0..31、実指数は 0..15) |
復元式:
if ( bit7 == 1 ) : minVal = 0.0
else : sign = ( bit6 ? -1.0 : +1.0 )
expSign = ( bit5 ? -1 : +1 )
exponent = (bit4..bit0) & 0x1F
minVal = sign * 10^( expSign * exponent )
Unit のビットレイアウト (1 byte):
| ビット | 意味 |
|---|---|
| bit 7 | 指数符号 (0=正, 1=負) |
| bit 6-4 | 指数値 (0..7、実指数は 0..7) |
| bit 3-0 | 乗算整数値 (0..15) |
復元式:
expSign = ( bit7 ? -1 : +1 )
exponent = (bit6..bit4) & 0x07
mulInt = (bit3..bit0) & 0x0F /* 0 の場合は 1 扱い */
unitVal = mulInt * 10^( expSign * exponent )
u16 rawIndex ; /* 0..65535 */
float realValue = minVal + rawIndex * unitVal ;
MV1_ANIM_KEYSET_FLAG_KEY_MP_PP / _Z_TP)MP_PP フラグが立っている場合、16bit index 0..65535 は [-π, +π] に直接マッピングされる:
angle = ( rawIndex / 65535.0 ) * 2π - π
Z_TP フラグが立っている場合は [0, 2π]:
angle = ( rawIndex / 65535.0 ) * 2π
これらのフラグ時は Min/Unit 補助情報は 省略される (2 byte 節約)。
TIME_BIT16 フラグ立ちで、時間軸も 16bit 化:
MV1_ANIM_KEY_16BIT_F1 timeSubInfo が前置 (2 byte)timeSubInfo.Min + rawTime16 * timeSubInfo.UnitTIME_UNIT フラグ立ちで、全キーが等間隔 (= 個別時刻記録を省略):
TIME_UNIT_ST_W → 開始時刻を WORD (2 byte)TIME_UNIT_ST_Z → 開始時刻は 0TIME_UNIT_UN_W → 単位時間を WORD、無ければ floatstartTime + i * unitTime| フラグ | キー数の格納 |
|---|---|
KEY_ONE |
キー数 = 1 (格納なし) |
KEYNUM_B |
1 byte (0..255) |
KEYNUM_W |
2 byte (0..65535) |
| (どれも立ってない) | 4 byte (DWORD) |
MATRIX_4X4CT_F / FLOAT4 の実メモリ配置MATRIX_4X4CT_F4 行 3 列の row-major レイアウト。4 行目は暗黙的に (0, 0, 0, 1) 固定。
typedef struct {
float m[ 4 ][ 3 ] ; /* row-major: m[row][col] */
} MATRIX_4X4CT_F ;
メモリ上の並び (48 byte = 12 float):
Offset Element
0x00 m[0][0] Basis X axis .x
0x04 m[0][1] Basis X axis .y
0x08 m[0][2] Basis X axis .z
0x0C m[1][0] Basis Y axis .x
0x10 m[1][1] Basis Y axis .y
0x14 m[1][2] Basis Y axis .z
0x18 m[2][0] Basis Z axis .x
0x1C m[2][1] Basis Z axis .y
0x20 m[2][2] Basis Z axis .z
0x24 m[3][0] Translation .x
0x28 m[3][1] Translation .y
0x2C m[3][2] Translation .z
並進成分は m[3][0..2] (4 行目の最初の 3 要素)。
[m[0][0] m[0][1] m[0][2] 0]
[m[1][0] m[1][1] m[1][2] 0]
[m[2][0] m[2][1] m[2][2] 0]
[m[3][0] m[3][1] m[3][2] 1]
DxLib の変換規約は 行ベクトル × 行列 (v' = v * M)。すなわち、点 p = (px, py, pz, 1) を左から掛けると:
p' = p * M
p'.x = px*m[0][0] + py*m[1][0] + pz*m[2][0] + 1*m[3][0]
p'.y = px*m[0][1] + py*m[1][1] + pz*m[2][1] + 1*m[3][1]
p'.z = px*m[0][2] + py*m[1][2] + pz*m[2][2] + 1*m[3][2]
FLOAT4 (クォータニオン用)typedef struct {
float x, y, z, w ;
} FLOAT4 ;
MV1_FRAME_F1.Quaternion の格納順は (x, y, z, w)。w が最後。ほとんどの 3D ライブラリと同じ順だが、一部の他形式 (例えば .x ファイル) は (w, x, y, z) なので変換時注意。
COLOR_F と COLOR_U8typedef struct { float r, g, b, a ; } COLOR_F ; /* 16 byte */
typedef struct { uint8_t r, g, b, a ; } COLOR_U8 ; /* 4 byte */
警告: COLOR_U8 は DxLib 内部では実際には b, g, r, a の順でメモリに格納 される (BGRA バイト順。DirectX ARGB と互換)。この順序は DxLib.h の定義で確認済。.mv1 内の頂点色でもこの BGRA 順で書き出される。
VECTORtypedef struct { float x, y, z ; } VECTOR ; /* 12 byte */
DWORD Padding[] で調整済DWORD は pointer 型でも 4 byte を維持、load 時にポインタ化).mv1 ファイルを開いて、モデルの主要情報 (頂点数 / 三角形数 / マテリアル数 / テクスチャパス) を標準出力する最小コード例。C99 移植性を重視し外部依存なし。
/* mv1_dump.c - .mv1 ファイルの最小ダンパ */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
/* ---- DXA decode (付録 A の疑似コード実装) ---- */
static uint32_t rd_u32( const uint8_t *p ) {
return ( uint32_t )p[ 0 ]
| ( ( uint32_t )p[ 1 ] << 8 )
| ( ( uint32_t )p[ 2 ] << 16 )
| ( ( uint32_t )p[ 3 ] << 24 ) ;
}
static uint16_t rd_u16( const uint8_t *p ) {
return ( uint16_t )p[ 0 ] | ( ( uint16_t )p[ 1 ] << 8 ) ;
}
static int dxa_decode( const uint8_t *src, size_t srcLen, uint8_t *dst, uint32_t *origSizeOut )
{
if ( srcLen < 9 ) return -1 ;
uint32_t origSize = rd_u32( src ) ;
uint32_t compSize = rd_u32( src + 4 ) ;
uint8_t key = src[ 8 ] ;
if ( origSizeOut ) *origSizeOut = origSize ;
if ( dst == NULL ) return 0 ;
if ( srcLen < compSize ) return -1 ;
const uint8_t *sp = src + 9 ;
const uint8_t *spEnd = src + compSize ;
uint8_t *dp = dst ;
while ( sp < spEnd ) {
if ( *sp != key ) { *dp++ = *sp++ ; continue ; }
if ( sp[ 1 ] == key ) { *dp++ = key ; sp += 2 ; continue ; }
uint8_t flags = sp[ 1 ] ;
if ( flags > key ) flags-- ;
sp += 2 ;
uint32_t length = ( uint32_t )( flags >> 3 ) ;
if ( flags & 0x04 ) { length |= ( uint32_t )( *sp++ ) << 5 ; }
length += 4 ;
uint32_t dist = 0 ;
switch ( flags & 0x03 ) {
case 0: dist = *sp++ ; break ;
case 1: dist = rd_u16( sp ) ; sp += 2 ; break ;
case 2: dist = rd_u16( sp ) | ( ( uint32_t )sp[ 2 ] << 16 ) ; sp += 3 ; break ;
}
dist += 1 ;
if ( dist < length ) {
uint32_t part = dist ;
while ( length > part ) {
memmove( dp, dp - part, part ) ;
dp += part ; length -= part ; part += part ;
}
memmove( dp, dp - ( part / 2 ) - ( dist > part / 2 ? ( dist - part / 2 ) : 0 ), length ) ;
dp += length ;
} else {
memmove( dp, dp - dist, length ) ;
dp += length ;
}
}
return 0 ;
}
/* ---- MV1 主要構造体 (read-only、padding は必要最小限まで無視) ---- */
#pragma pack(push, 4)
typedef struct { float r, g, b, a ; } COLOR_F ;
typedef struct {
uint8_t CheckID[ 4 ] ;
uint32_t Version ;
int32_t RightHandType ;
int32_t AutoCreateNormal ;
int32_t ChangeDrawMaterialTableSize ;
int32_t ChangeMatrixTableSize ;
uint32_t ChangeDrawMaterialTable ;
uint32_t ChangeMatrixTable ;
int32_t FrameNum ;
uint32_t Frame ;
int32_t TopFrameNum ;
uint32_t FirstTopFrame ;
uint32_t LastTopFrame ;
int32_t FrameUseSkinBoneNum ;
uint32_t FrameUseSkinBone ;
int32_t MaterialNum ;
uint32_t Material ;
int32_t TextureNum ;
uint32_t Texture ;
int32_t MeshNum ;
uint32_t Mesh ;
int32_t LightNum ;
uint32_t Light ;
int32_t SkinBoneNum ;
uint32_t SkinBone ;
int32_t SkinBoneUseFrameNum ;
uint32_t SkinBoneUseFrame ;
int32_t TriangleListNum ;
uint32_t TriangleList ;
uint32_t VertexData ;
uint32_t VertexDataSize ;
int32_t TriangleListNormalPositionNum ;
int32_t TriangleListSkinPosition4BNum ;
int32_t TriangleListSkinPosition8BNum ;
int32_t TriangleListSkinPositionFREEBSize ;
int32_t MeshPositionSize ;
int32_t MeshNormalNum ;
int32_t MeshVertexSize ;
int32_t MeshFaceNum ;
int32_t MeshVertexIndexNum ;
int32_t TriangleListIndexNum ;
int32_t TriangleNum ;
int32_t TriangleListVertexNum ;
int32_t StringSize ;
uint32_t StringBuffer ;
/* 以降は省略可 (この demo では使わない) */
} MV1MODEL_FILEHEADER_F1 ;
typedef struct {
uint32_t DimPrev, DimNext ;
uint32_t Name ;
int32_t Index ;
uint32_t ColorFilePath ;
uint32_t AlphaFilePath ;
int32_t BumpImageFlag ;
float BumpImageNextPixelLength ;
int32_t AddressModeU ;
int32_t AddressModeV ;
int32_t FilterMode ;
uint32_t UserData[ 2 ] ;
uint8_t Flag ;
uint8_t Padding1[ 3 ] ;
float ScaleU, ScaleV ;
uint32_t Padding ;
} MV1_TEXTURE_F1 ;
typedef struct {
uint32_t DimPrev, DimNext ;
uint32_t Name ;
int32_t Index ;
COLOR_F Diffuse ;
COLOR_F Ambient ;
COLOR_F Specular ;
COLOR_F Emissive ;
float Power ;
float Alpha ;
int32_t DiffuseLayerNum ;
struct { int32_t Texture ; int32_t BlendType ; uint32_t Padding[ 4 ] ; } DiffuseLayer[ 8 ] ;
/* 以降省略 */
} MV1_MATERIAL_F1_HEAD ;
#pragma pack(pop)
/* ---- メイン ---- */
int main( int argc, char **argv ) {
if ( argc != 2 ) { fprintf( stderr, "usage: %s file.mv1\n", argv[ 0 ] ) ; return 1 ; }
FILE *fp = fopen( argv[ 1 ], "rb" ) ;
if ( !fp ) { perror( argv[ 1 ] ) ; return 1 ; }
fseek( fp, 0, SEEK_END ) ;
size_t fileLen = ( size_t )ftell( fp ) ;
fseek( fp, 0, SEEK_SET ) ;
uint8_t *fileBuf = malloc( fileLen ) ;
fread( fileBuf, 1, fileLen, fp ) ;
fclose( fp ) ;
if ( memcmp( fileBuf, "MV11", 4 ) != 0 ) {
fprintf( stderr, "Not a MV11 file\n" ) ; return 1 ;
}
/* DXA 伸長 */
uint32_t origSize = 0 ;
dxa_decode( fileBuf + 4, fileLen - 4, NULL, &origSize ) ;
uint8_t *base = malloc( origSize ) ;
dxa_decode( fileBuf + 4, fileLen - 4, base, NULL ) ;
free( fileBuf ) ;
/* ルートヘッダ */
MV1MODEL_FILEHEADER_F1 *h = ( MV1MODEL_FILEHEADER_F1 * )base ;
printf( "=== MV1 Dump ===\n" ) ;
printf( "Version: %u\n", h->Version ) ;
printf( "RightHand: %s\n", h->RightHandType ? "yes" : "no" ) ;
printf( "Frames: %d\n", h->FrameNum ) ;
printf( "Meshes: %d\n", h->MeshNum ) ;
printf( "Materials: %d\n", h->MaterialNum ) ;
printf( "Textures: %d\n", h->TextureNum ) ;
printf( "SkinBones: %d\n", h->SkinBoneNum ) ;
printf( "Triangles: %d\n", h->TriangleNum ) ;
printf( "TotalVertices: %d\n", h->TriangleListVertexNum ) ;
printf( "StringPool: %d bytes\n", h->StringSize ) ;
/* テクスチャ一覧 */
MV1_TEXTURE_F1 *tex = ( MV1_TEXTURE_F1 * )( base + h->Texture ) ;
const char *strPool = ( const char * )( base + h->StringBuffer ) ;
printf( "\n--- Textures ---\n" ) ;
for ( int i = 0 ; i < h->TextureNum ; ++i ) {
const char *name = tex[ i ].Name ? ( strPool + tex[ i ].Name ) : "(null)" ;
const char *path = tex[ i ].ColorFilePath ? ( strPool + tex[ i ].ColorFilePath ) : "(null)" ;
printf( " [%d] name=%s path=%s\n", i, name, path ) ;
}
/* マテリアル一覧 */
printf( "\n--- Materials ---\n" ) ;
MV1_MATERIAL_F1_HEAD *mat = ( MV1_MATERIAL_F1_HEAD * )( base + h->Material ) ;
/* 注: MV1_MATERIAL_F1 の実サイズを調べて stride 計算が必要。このデモでは先頭のみ表示 */
const char *mname = mat[ 0 ].Name ? ( strPool + mat[ 0 ].Name ) : "(null)" ;
printf( " [0] name=%s Diffuse=(%.2f,%.2f,%.2f,%.2f) DiffuseLayerNum=%d\n",
mname,
mat[ 0 ].Diffuse.r, mat[ 0 ].Diffuse.g, mat[ 0 ].Diffuse.b, mat[ 0 ].Diffuse.a,
mat[ 0 ].DiffuseLayerNum ) ;
free( base ) ;
return 0 ;
}
gcc -O2 -std=c99 -o mv1_dump mv1_dump.c
./mv1_dump model.mv1
=== MV1 Dump ===
Version: 1
RightHand: yes
Frames: 42
Meshes: 8
Materials: 8
Textures: 5
SkinBones: 38
Triangles: 12480
TotalVertices: 38450
StringPool: 1024 bytes
--- Textures ---
[0] name=body path=textures/body.png
[1] name=face path=textures/face.png
...
MV1_MATERIAL_F1_HEAD は先頭一致の部分定義。全フィールドを読むには DxModelFile.h の完全定義に合わせる。_pragma pack(4) は MSVC / GCC / Clang で互換。他コンパイラは __attribute__((packed)) に書き換え。| 日時 | 内容 |
|---|---|
| 2026-04-22 22:29 | 初版 (概観 + 主要構造体) |
| 2026-04-22 22:32 | 拡充版: DXA 圧縮仕様 + 全 enum 値 + 可変長 byte 図 + 16bit 圧縮式 + MATRIX 詳細 + リファレンス C パーサを追加 |