4 * License : The MIT License
5 * Copyright(c) 2010 MikuToga Partners
8 package jp.sfjp.mikutoga.pmd.parser;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.nio.charset.Charset;
13 import java.util.Arrays;
14 import jp.sfjp.mikutoga.bin.parser.CommonParser;
15 import jp.sfjp.mikutoga.bin.parser.MmdEofException;
16 import jp.sfjp.mikutoga.bin.parser.MmdFormatException;
17 import jp.sfjp.mikutoga.bin.parser.TextDecoder;
18 import jp.sfjp.mikutoga.pmd.PmdConst;
19 import jp.sfjp.mikutoga.pmd.ShadingUtil;
24 public class PmdParserBase extends CommonParser {
27 * PMDで用いられる文字エンコーディング(windows-31j)。
28 * ほぼShift_JISのスーパーセットと思ってよい。
29 * デコード結果はUCS-2集合に収まるはず。
31 public static final Charset CS_WIN31J = Charset.forName("windows-31j");
34 protected static final String CR = "\r"; // 0x0d
36 protected static final String LF = "\n"; // 0x0a
38 protected static final String CRLF = CR + LF; // 0x0d, 0x0a
41 private static final int TRIVTX = 3;
43 private static final int HEADER_LENGTH = 7;
44 private static final byte[] MAGIC_BYTES = {
45 (byte)0x50, (byte)0x6d, (byte)0x64, // "Pmd"
46 (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x3f, // 1.0f
51 assert MAGIC_BYTES.length == HEADER_LENGTH;
55 private final TextDecoder decoderWin31j = new TextDecoder(CS_WIN31J);
57 private PmdBasicHandler basicHandler = PmdUnifiedHandler.EMPTY;
58 private PmdShapeHandler shapeHandler = PmdUnifiedHandler.EMPTY;
59 private PmdMaterialHandler materialHandler = PmdUnifiedHandler.EMPTY;
60 private PmdBoneHandler boneHandler = PmdUnifiedHandler.EMPTY;
61 private PmdMorphHandler morphHandler = PmdUnifiedHandler.EMPTY;
63 private int boneCount = -1;
64 private int morphCount = -1;
65 private int boneGroupCount = -1;
72 public PmdParserBase(InputStream source){
74 this.decoderWin31j.setZeroChopMode(true);
80 * 文字列の最後がLF(0x0a)の場合削除する。
83 * @return 末尾LFが削除された文字列
85 public static String chopLastLF(String name){
88 if(name.endsWith(LF)){
89 result = name.substring(0, name.length() - 1);
101 public void setBasicHandler(PmdBasicHandler handler){
103 this.basicHandler = PmdUnifiedHandler.EMPTY;
105 this.basicHandler = handler;
112 * @param handler ハンドラ
114 public void setShapeHandler(PmdShapeHandler handler){
116 this.shapeHandler = PmdUnifiedHandler.EMPTY;
118 this.shapeHandler = handler;
125 * @param handler ハンドラ
127 public void setMaterialHandler(PmdMaterialHandler handler){
129 this.materialHandler = PmdUnifiedHandler.EMPTY;
131 this.materialHandler = handler;
138 * @param handler ハンドラ
140 public void setBoneHandler(PmdBoneHandler handler){
142 this.boneHandler = PmdUnifiedHandler.EMPTY;
144 this.boneHandler = handler;
151 * @param handler ハンドラ
153 public void setMorphHandler(PmdMorphHandler handler){
155 this.morphHandler = PmdUnifiedHandler.EMPTY;
157 this.morphHandler = handler;
163 * パースによって得られたボーン数を返す。
166 protected int getBoneCount(){
167 return this.boneCount;
171 * パースによって得られたモーフ数を返す。
174 protected int getMorphCount(){
175 return this.morphCount;
179 * パースによって得られたボーングループ数を返す。
182 protected int getBoneGroupCount(){
183 return this.boneGroupCount;
187 * 指定されたバイト長に収まるゼロ終端(0x00)文字列を読み込む。
188 * <p>入力バイト列はwindows-31jエンコーディングとして解釈される。
189 * <p>ゼロ終端以降のデータは無視されるが、
190 * IO入力は指定バイト数だけ読み進められる。
191 * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、
192 * そこまでのデータから文字列を構成する。
193 * @param byteLen 読み込みバイト数
195 * @throws IOException IOエラー
196 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
197 * @throws MmdFormatException 不正な文字エンコーディングが検出された。
199 protected String parsePmdText(int byteLen)
203 String result = parseString(this.decoderWin31j, byteLen);
209 * @throws IOException IOエラー
210 * @throws MmdFormatException フォーマットエラー
212 public void parsePmd()
213 throws IOException, MmdFormatException {
214 this.basicHandler.pmdParseStart();
218 boolean hasMoreData = hasMore();
219 this.basicHandler.pmdParseEnd(hasMoreData);
225 * PMDファイル本体のパースを開始する。
226 * パーサを拡張する場合はこのメソッドをオーバーライドする。
227 * @throws IOException IOエラー
228 * @throws MmdFormatException フォーマットエラー
230 protected void parseBody() throws IOException, MmdFormatException{
239 parseMorphOrderList();
240 parseBoneGroupList();
241 parseGroupedBoneList();
247 * PMDファイルヘッダ部のパースと通知。
248 * @throws IOException IOエラー
249 * @throws MmdFormatException フォーマットエラー
251 private void parsePmdHeader() throws IOException, MmdFormatException{
252 byte[] header = new byte[HEADER_LENGTH];
253 parseByteArray(header);
255 if( ! Arrays.equals(header, MAGIC_BYTES) ){
256 throw new MmdFormatException("unknown PMD-header type");
260 parsePmdText(PmdConst.MAXBYTES_MODELNAME);
262 parsePmdText(PmdConst.MAXBYTES_MODELDESC);
263 description = description.replace(CRLF, LF);
265 this.basicHandler.pmdHeaderInfo(header);
266 this.basicHandler.pmdModelInfo(modelName, description);
273 * @throws IOException IOエラー
274 * @throws MmdFormatException フォーマットエラー
276 private void parseVertexList() throws IOException, MmdFormatException{
277 int vertexNum = parseLeInt();
279 this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);
281 for(int ct = 0; ct < vertexNum; ct++){
282 float xPos = parseLeFloat();
283 float yPos = parseLeFloat();
284 float zPos = parseLeFloat();
285 this.shapeHandler.pmdVertexPosition(xPos, yPos, zPos);
287 float xVec = parseLeFloat();
288 float yVec = parseLeFloat();
289 float zVec = parseLeFloat();
290 this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);
292 float uVal = parseLeFloat();
293 float vVal = parseLeFloat();
294 this.shapeHandler.pmdVertexUV(uVal, vVal);
296 int boneId1 = parseLeUShortAsInt();
297 int boneId2 = parseLeUShortAsInt();
298 int weightForB1 = parseUByteAsInt();
299 this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);
301 boolean hideEdge = parseBoolean();
302 this.shapeHandler.pmdVertexEdge(hideEdge);
304 this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);
307 this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);
314 * @throws IOException IOエラー
315 * @throws MmdFormatException フォーマットエラー
317 private void parseSurfaceList() throws IOException, MmdFormatException{
318 int vertexNum = parseLeInt();
319 if(vertexNum % TRIVTX != 0) throw new MmdFormatException();
320 int surfaceNum = vertexNum / TRIVTX;
322 this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);
324 for(int ct = 0; ct < surfaceNum; ct++){
325 int vertexId1 = parseLeUShortAsInt();
326 int vertexId2 = parseLeUShortAsInt();
327 int vertexId3 = parseLeUShortAsInt();
328 this.shapeHandler.pmdSurfaceTriangle(vertexId1,
331 this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);
334 this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);
341 * @throws IOException IOエラー
342 * @throws MmdFormatException フォーマットエラー
344 private void parseMaterialList() throws IOException, MmdFormatException{
345 int materialNum = parseLeInt();
347 this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,
350 for(int ct = 0; ct < materialNum; ct++){
353 int toonidx = parseUByteAsInt();
354 boolean hasEdge = parseBoolean();
355 int surfaceCount = parseLeInt();
358 parsePmdText(PmdConst.MAXBYTES_TEXTUREFILENAME);
359 String[] splitted = ShadingUtil.splitShadingFileInfo(shadingFile);
360 String textureFile = splitted[0];
361 String sphereFile = splitted[1];
363 this.materialHandler.pmdMaterialShading(toonidx,
364 textureFile, sphereFile );
365 this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);
367 this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);
370 this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);
377 * @throws IOException IOエラー
378 * @throws MmdFormatException フォーマットエラー
380 private void parseColor() throws IOException, MmdFormatException{
385 red = parseLeFloat();
386 green = parseLeFloat();
387 blue = parseLeFloat();
388 float alpha = parseLeFloat();
390 this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);
392 float shininess = parseLeFloat();
393 red = parseLeFloat();
394 green = parseLeFloat();
395 blue = parseLeFloat();
397 this.materialHandler.pmdMaterialSpecular(red, green, blue,
400 red = parseLeFloat();
401 green = parseLeFloat();
402 blue = parseLeFloat();
404 this.materialHandler.pmdMaterialAmbient(red, green, blue);
411 * @throws IOException IOエラー
412 * @throws MmdFormatException フォーマットエラー
414 private void parseBoneList() throws IOException, MmdFormatException{
415 this.boneCount = parseLeUShortAsInt();
417 this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);
419 for(int ct = 0; ct < this.boneCount; ct++){
421 parsePmdText(PmdConst.MAXBYTES_BONENAME);
422 int parentId = parseLeUShortAsInt();
423 int tailId = parseLeUShortAsInt();
424 byte boneKind = parseByte();
425 int srcId = parseLeUShortAsInt();
427 this.boneHandler.pmdBoneInfo(boneName, boneKind);
428 this.boneHandler.pmdBoneLink(parentId, tailId, srcId);
430 float xPos = parseLeFloat();
431 float yPos = parseLeFloat();
432 float zPos = parseLeFloat();
434 this.boneHandler.pmdBonePosition(xPos, yPos, zPos);
436 this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);
439 this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);
446 * @throws IOException IOエラー
447 * @throws MmdFormatException フォーマットエラー
449 private void parseIKList() throws IOException, MmdFormatException{
450 int ikCount = parseLeUShortAsInt();
452 this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);
454 for(int ct = 0; ct < ikCount; ct++){
455 int boneId = parseLeUShortAsInt();
456 int targetId = parseLeUShortAsInt();
457 int chainLength = parseUByteAsInt();
458 int depth = parseLeUShortAsInt();
459 float weight = parseLeFloat();
461 this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);
463 parseIKChainList(chainLength);
465 this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);
468 this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);
475 * @param chainLength チェーン長
476 * @throws IOException IOエラー
477 * @throws MmdFormatException フォーマットエラー
479 private void parseIKChainList(int chainLength)
480 throws IOException, MmdFormatException{
481 this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,
484 for(int ct = 0; ct < chainLength; ct++){
485 int childId = parseLeUShortAsInt();
486 this.boneHandler.pmdIKChainInfo(childId);
488 this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);
491 this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);
498 * @throws IOException IOエラー
499 * @throws MmdFormatException フォーマットエラー
501 private void parseMorphList() throws IOException, MmdFormatException{
502 this.morphCount = parseLeUShortAsInt();
504 this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,
507 for(int ct = 0; ct < this.morphCount; ct++){
509 parsePmdText(PmdConst.MAXBYTES_MORPHNAME);
510 int vertexCount = parseLeInt();
511 byte morphType = parseByte();
513 this.morphHandler.pmdMorphInfo(morphName, morphType);
515 parseMorphVertexList(vertexCount);
517 this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);
520 this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);
527 * @param vertexCount 頂点数
528 * @throws IOException IOエラー
529 * @throws MmdFormatException フォーマットエラー
531 private void parseMorphVertexList(int vertexCount)
532 throws IOException, MmdFormatException{
533 this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,
536 for(int ct = 0; ct < vertexCount; ct++){
537 int vertexId = parseLeInt();
538 float xPos = parseLeFloat();
539 float yPos = parseLeFloat();
540 float zPos = parseLeFloat();
541 this.morphHandler.pmdMorphVertexInfo(vertexId, xPos, yPos, zPos);
543 this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);
546 this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);
553 * @throws IOException IOエラー
554 * @throws MmdFormatException フォーマットエラー
556 private void parseMorphOrderList()
557 throws IOException, MmdFormatException{
558 int morphOrderCount = parseUByteAsInt();
560 this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,
563 for(int ct = 0; ct < morphOrderCount; ct++){
564 int morphId = parseLeUShortAsInt();
565 this.morphHandler.pmdMorphOrderInfo(morphId);
567 this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);
570 this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);
577 * @throws IOException IOエラー
578 * @throws MmdFormatException フォーマットエラー
580 private void parseBoneGroupList()
581 throws IOException, MmdFormatException{
582 this.boneGroupCount = parseUByteAsInt();
584 this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,
585 this.boneGroupCount);
587 for(int ct = 0; ct < this.boneGroupCount; ct++){
589 parsePmdText(PmdConst.MAXBYTES_BONEGROUPNAME);
590 groupName = chopLastLF(groupName);
591 this.boneHandler.pmdBoneGroupInfo(groupName);
593 this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);
596 this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);
603 * @throws IOException IOエラー
604 * @throws MmdFormatException フォーマットエラー
606 private void parseGroupedBoneList()
607 throws IOException, MmdFormatException{
608 int groupedBoneCount = parseLeInt();
610 this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,
613 for(int ct = 0; ct < groupedBoneCount; ct++){
614 int boneId = parseLeUShortAsInt();
615 int groupId = parseUByteAsInt();
616 this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);
618 this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);
621 this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);