MMD中的VMD文件格式详解

MMD中的VMD文件格式详解

VMD是一个二进制流的文件。

Header

VMD有两个版本,你可以认为是v1跟v2。
前30个字节是版本描述。ASCII编码
v1是"Vocaloid Motion Data file"
v2是"Vocaloid Motion Data 0002"

接下来是名字,ShiftJIS编码
v1中是10个字节,v2中是20个字节

接下来每部分开头都是一个uint32_t,记录了这部分有多少关键帧。没有记录此项为0(而不是省略)。
然后跟上相应数量的记录。

类型解释
byte*30VersionInformation
byte*(10/20)ModelName
uint32_tBoneKeyFrameNumber
byte * 111 * nBoneKeyFrameRecord
uint32_tMorphKeyFrameNumber
byte* 47 *nMorphKeyFrameRecord
uint32_tCameraKeyFrameNumber
byte* 61 *nCameraKeyFrameRecord
uint32_tLightKeyFrameNumber
byte* 28 *nLightKeyFrameRecord

KeyFrameRecord

BoneKeyFrameRecord(byte*111)

类型解释
byte*15(ShiftJIS)BoneName
uint32_tFrameTime
float*3Translation.xyz
float*4Rotation.xyzw
uint8_t*16XCurve
uint8_t*16YCurve
uint8_t*16ZCurve
uint8_t*16RCurve
uint8_t那里有冗余,每四个只读第一个就行。

MorphKeyFrameRecord(byte*23)

类型解释
byte*15(ShiftJIS)MorphName
uint32_tFrameTime
floatWeight

CameraKeyFrameRecord(byte*61)

类型解释
uint32_tFrameTime
floatDistance
float*3Position.xyz
float*3Rotation.xyz
uint8_t*24Curve
floatViewAngle
uint8_tOrthographic

LightKeyFrameRecord(byte*28)

类型解释
uint32_tFrameTime
float*3Color.rgb
float*3Direction.xyz

Java Code

package com.company;

import java.io.*;
import java.nio.ByteBuffer;

public class Main {

    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("test.vmd");
            BufferedInputStream bis = new BufferedInputStream(fis);
            DataInputStream dis = new DataInputStream(bis);

            String headerString = readString(dis, 30);

            int version = 0;
            String modelName = null;

            if (headerString.startsWith("Vocaloid Motion Data file")){
                version = 1;
                modelName = readString(dis, 10);
            }else if (headerString.startsWith("Vocaloid Motion Data 0002")){
                version = 2;
                modelName = readString(dis, 20);
            }else{
                throw new IOException("VmdReader: Not A VMD File!");
            }

            int numOfBoneRecord = readInt(dis);

            for (int i = 0; i < numOfBoneRecord; i++){

                String BoneRecordNameString = readString(dis, 15);

                int frameTime = readInt(dis);

                float tx = readFloat(dis);
                float ty = readFloat(dis);
                float tz = readFloat(dis);

                float rx = readFloat(dis);
                float ry = readFloat(dis);
                float rz = readFloat(dis);
                float rw = readFloat(dis);

                int txc1 = dis.readByte();
                dis.skipBytes(3);
                int txc2 = dis.readByte();
                dis.skipBytes(3);
                int txc3 = dis.readByte();
                dis.skipBytes(3);
                int txc4 = dis.readByte();
                dis.skipBytes(3);

                int tyc1 = dis.readByte();
                dis.skipBytes(3);
                int tyc2 = dis.readByte();
                dis.skipBytes(3);
                int tyc3 = dis.readByte();
                dis.skipBytes(3);
                int tyc4 = dis.readByte();
                dis.skipBytes(3);

                int tzc1 = dis.readByte();
                dis.skipBytes(3);
                int tzc2 = dis.readByte();
                dis.skipBytes(3);
                int tzc3 = dis.readByte();
                dis.skipBytes(3);
                int tzc4 = dis.readByte();
                dis.skipBytes(3);

                int rc1 = dis.readByte();
                dis.skipBytes(3);
                int rc2 = dis.readByte();
                dis.skipBytes(3);
                int rc3 = dis.readByte();
                dis.skipBytes(3);
                int rc4 = dis.readByte();
                dis.skipBytes(3);
            }
            int numOfMorphRecord = readInt(dis);
            for (int i = 0; i < numOfMorphRecord; i++){
                String name = readString(dis, 15);
                int frameTime = readInt(dis);
                float weight = readFloat(dis);
            }
            int numOfCameraRecord = readInt(dis);
            for (int i = 0; i < numOfCameraRecord; i++){
                int frameTime = readInt(dis);

                float distance = readFloat(dis);

                float px = readFloat(dis);
                float py = readFloat(dis);
                float pz = readFloat(dis);

                float rx = readFloat(dis);
                float ry = readFloat(dis);
                float rz = readFloat(dis);

                int txc1 = dis.readByte();
                int txc2 = dis.readByte();
                int txc3 = dis.readByte();
                int txc4 = dis.readByte();

                int tyc1 = dis.readByte();
                int tyc2 = dis.readByte();
                int tyc3 = dis.readByte();
                int tyc4 = dis.readByte();

                int tzc1 = dis.readByte();
                int tzc2 = dis.readByte();
                int tzc3 = dis.readByte();
                int tzc4 = dis.readByte();

                int qc1 = dis.readByte();
                int qc2 = dis.readByte();
                int qc3 = dis.readByte();
                int qc4 = dis.readByte();

                int dc1 = dis.readByte();
                int dc2 = dis.readByte();
                int dc3 = dis.readByte();
                int dc4 = dis.readByte();

                int vc1 = dis.readByte();
                int vc2 = dis.readByte();
                int vc3 = dis.readByte();
                int vc4 = dis.readByte();

                float viewAngle = readFloat(dis);
                byte orthographic = dis.readByte();

            }




        } catch (IOException e) {
            System.err.println(e);
            e.printStackTrace();
        }

    }

    private static int readInt(DataInputStream stream) throws IOException {
        byte intBytes[] = new byte[4];
        stream.read(intBytes);
        for (int j = 0; j < intBytes.length/2; j++){
            byte temp = intBytes[j];
            intBytes[j] = intBytes[intBytes.length -1 -j];
            intBytes[intBytes.length -1 -j] = temp;
        }
        return ByteBuffer.wrap(intBytes).getInt();
    }
    private static float readFloat(DataInputStream stream) throws IOException {
        byte floatBytes[] = new byte[4];
        stream.read(floatBytes);
        for (int j = 0; j < floatBytes.length/2; j++){
            byte temp = floatBytes[j];
            floatBytes[j] = floatBytes[floatBytes.length -1 -j];
            floatBytes[floatBytes.length -1 -j] = temp;
        }
        return ByteBuffer.wrap(floatBytes).getFloat();
    }

    private static String readString(DataInputStream stream, int length) throws IOException {
        byte[] name = new byte[length];
        stream.read(name);
        String nameString = new String(name, "Shift-jis");
        int indexOfNull = nameString.indexOf('\0');
        if (indexOfNull != -1){
            nameString = nameString.substring(0, indexOfNull);
        }
        return nameString;
    }
    private static String readString(DataInputStream stream, boolean utf8) throws IOException {
        int length = readInt(stream);
        byte[] name = new byte[length];
        stream.read(name);
        String string = null;
        if (utf8== false){
            for (int i = 0; i < name.length; i+=2){
                byte temp = name[i];
                name[i] = name[i+1];
                name[i+1] = temp;
            }
            string = new String(name, "utf-16");
        }else{
            string = new String(name, "utf-8");
        }
        return string;
    }
/*
    private static Vector2f readVector2f(DataInputStream stream) throws IOException {
        float x = readFloat(stream);
        float y = readFloat(stream);
        return new Vector3f(x,y);
    }

    private static Vector3f readVector3f(DataInputStream stream) throws IOException {
        float x = readFloat(stream);
        float y = readFloat(stream);
        float z = readFloat(stream);
        return new Vector3f(x,y,z);
    }
*/

}

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值