Skip to content

Instantly share code, notes, and snippets.

@keepoff07
Created July 29, 2015 12:44
Show Gist options
  • Save keepoff07/43249c2f034cae53d056 to your computer and use it in GitHub Desktop.
Save keepoff07/43249c2f034cae53d056 to your computer and use it in GitHub Desktop.
VMDファイルを読み込む
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class VMDFile {
private byte[] bin;
private String header = null;
private String name = null;
private long size = 0l;
private List<VMDFrame> frames = new ArrayList<VMDFrame>();
public VMDFile(File file) {
this.bin = VMDReadUtil.convertBinary(file);
if(this.bin == null) return;
this.header = VMDReadUtil.parseString(bin, 0, 30, "Null");
this.name = VMDReadUtil.parseString(bin, 30, 20, "Null");
this.size = VMDReadUtil.parseLong(bin, 50, 0);
for(int i = 0; i < size; i++) {
VMDFrame frame = new VMDFrame(this.bin, i);
if(frame.getName() != null) this.frames.add(frame);
}
}
public byte[] getBinary() {
return this.bin;
}
public String getHeader() {
return this.header;
}
public String getName() {
return this.name;
}
public long getSize() {
return this.size;
}
public List<VMDFrame> getFrames() {
return this.frames;
}
public String[] toStrings() {
List<String> args = new ArrayList<String>();
args.add(this.header);
if(this.name != null) args.add("Name: "+this.name);
else args.add("Name: Null");
args.add("Size: "+this.size);
for(VMDFrame frame : this.frames) {
args.add(frame.toString());
}
return args.toArray(new String[0]);
}
public static class VMDFrame {
private String name = null;
private long number = 0l;
private float[] pos = new float[3];
private float[] ang = new float[4];
private float[] degAng = new float[4];
private byte[] ip = null;
public VMDFrame(byte[] bin, int index) {
int offset = 54+(index*111);
this.name = VMDReadUtil.parseString(bin, offset+0, 15, null);
if(this.name == null) return;
this.number = VMDReadUtil.parseLong(bin, offset+15, 0);
this.pos[0] = VMDReadUtil.parsePosition(bin, offset+19);
this.pos[1] = VMDReadUtil.parsePosition(bin, offset+23);
this.pos[2] = VMDReadUtil.parsePosition(bin, offset+27);
float[][] angs = new float[4][2];
angs[0] = VMDReadUtil.parseAngle(bin, offset+31);
angs[1] = VMDReadUtil.parseAngle(bin, offset+35);
angs[2] = VMDReadUtil.parseAngle(bin, offset+39);
angs[3] = VMDReadUtil.parseAngle(bin, offset+43);
for(int i = 0; i < 4; i++) {
degAng[i] = angs[i][0];
ang[i] = angs[i][1];
}
this.ip = VMDReadUtil.subBytes(bin, offset+47, 64, false);
}
public String getName() {
return this.name;
}
public long getNumber() {
return this.number;
}
public float[] getPositions() {
return this.pos;
}
public float[] getAngles() {
return this.ang;
}
@Deprecated
public byte[] getInterpolationParameter() {
return this.ip;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.number);
sb.append(": Name:");
if(this.name != null) sb.append(this.name);
else sb.append("Null");
sb.append(",{Pos:[");
sb.append(this.pos[0]); sb.append(',');
sb.append(this.pos[1]); sb.append(',');
sb.append(this.pos[2]);
sb.append("],Ang:[");
sb.append(this.ang[0]); sb.append(',');
sb.append(this.ang[1]); sb.append(',');
sb.append(this.ang[2]); sb.append(',');
sb.append(this.ang[3]); sb.append("]}");
return sb.toString();
}
}
public static class VMDReadUtil {
public static byte[] convertBinary(File file) {
byte[] bin = null;
FileInputStream fis = null;
ByteArrayOutputStream baos = null;
try {
if(file.exists()) {
bin = new byte[1];
fis = new FileInputStream(file);
baos = new ByteArrayOutputStream();
while (fis.read(bin) > 0) {
baos.write(bin);
}
bin = baos.toByteArray();
}
} catch (IOException e){
} finally {
if(baos != null) {
try {
baos.close();
} catch (IOException e) {}
}
if(fis != null) {
try {
fis.close();
} catch (IOException e) {}
}
}
return bin;
}
public static String parseString(byte[] bin, int offset, int length, String def) {
byte[] data = subBytes(bin, offset, length, true);
try {
if(data != null) return new String(data, "Shift-JIS");
} catch (UnsupportedEncodingException e) {}
return def;
}
public static int parseInt(byte[] bin, int offset, int def) {
byte[] data = subBytes(bin, offset, 4, false);
if(data != null) {
String bis = "";
for(int i = data.length; i > 0; i--) {
bis += String.format("%02X", data[i-1]);
}
return Integer.parseInt(bis, 16);
}
return def;
}
public static long parseLong(byte[] bin, int offset, long def) {
byte[] data = subBytes(bin, offset, 4, false);
if(data != null) {
String bis = "";
for(int i = data.length; i > 0; i--) {
bis += String.format("%02X", data[i-1]);
}
return Long.parseLong(bis, 16);
}
return def;
}
public static float parseFloat(byte[] bin, int offset, float def) {
byte[] data = subBytes(bin, offset, 4, false);
if(data != null) {
int len = data.length;
byte[] buf = new byte[len];
for(int i = 0; i < len; i++) {
buf[i] = data[len-1-i];
}
DataInputStream in = new DataInputStream(new ByteArrayInputStream(buf));
float f;
try {
f = in.readFloat();
} catch (IOException e) {
f = def;
} finally {
try {
in.close();
} catch (IOException e) {}
}
return f;
}
return def;
}
public static float parsePosition(byte[] bin, int offset) {
float f = parseFloat(bin, offset, 0f);
float a = new BigDecimal(f).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
return a;
}
public static float[] parseAngle(byte[] bin, int offset) {
float f = parseFloat(bin, offset, 0f);
double d = Math.toDegrees(f)*-2.0d;
float a = new BigDecimal(d).setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();
return new float[]{f, a};
}
public static byte[] subBytes(byte[] bin, int offset, int length, boolean cut) {
byte[] data = new byte[length];
int end = offset+length;
if(bin != null && bin.length >= end) {
int index = 0;
for(int i = offset; i < end; i++) {
byte b = bin[i];
if(cut && b == (byte)0) break;
data[index] = b;
index++;
}
byte[] ba = new byte[index];
for(int i = 0; i < index; i++) {
ba[i] = data[i];
}
return ba;
} else {
return null;
}
}
}
}

概要

VMDファイルを読み込みます。書き込みは考えてないです。

VMDファイル とは

MikuMikuDance(MMD)のモーションデータのこと。
gyazo

ファイルフォーマット

VMDのファイルフォーマットについてインドカレー様がまとめていたものを参考にさせていただきました。ありがとうございます。
VMDファイルフォーマット: http://atupdate.web.fc2.com/vmd_format.htm
インドカレー様ブログ: http://atupdate.blog82.fc2.com/

実行例

	static void read(String name) {
		File file = new File(name);
		VMDFile vd = new VMDFile(file);
		String[] as = vd.toStrings();
		for(String b : as) {
			System.out.println(b);
		}
	}

gyazo

フレーム順になっていないからソートする必要ありますね…
実用できるレベルではないですが、自分用としては十分なのでこの段階で置いておきます。

学んだこと

バイナリデータ難しい、というか相手(MMD)側がどう出力してるから知らないから余計わからない。
ファイルフォーマットが無ければ死んでいた(真面目に)


と言うかMMDとか真面目にやったことないのになんでVMDファイルなんて読み取る必要あるんですかね?
そりゃもちろんプラg…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment