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)の場合削除する。
85 * @return 末尾LFが削除された文字列
87 public static String chopLastLF(String name){
90 if(name.endsWith(LF)){
91 result = name.substring(0, name.length() - 1);
101 * @param handler ハンドラ
103 public void setBasicHandler(PmdBasicHandler handler){
105 this.basicHandler = PmdUnifiedHandler.EMPTY;
107 this.basicHandler = handler;
114 * @param handler ハンドラ
116 public void setShapeHandler(PmdShapeHandler handler){
118 this.shapeHandler = PmdUnifiedHandler.EMPTY;
120 this.shapeHandler = handler;
127 * @param handler ハンドラ
129 public void setMaterialHandler(PmdMaterialHandler handler){
131 this.materialHandler = PmdUnifiedHandler.EMPTY;
133 this.materialHandler = handler;
140 * @param handler ハンドラ
142 public void setBoneHandler(PmdBoneHandler handler){
144 this.boneHandler = PmdUnifiedHandler.EMPTY;
146 this.boneHandler = handler;
153 * @param handler ハンドラ
155 public void setMorphHandler(PmdMorphHandler handler){
157 this.morphHandler = PmdUnifiedHandler.EMPTY;
159 this.morphHandler = handler;
165 * パースによって得られたボーン数を返す。
168 protected int getBoneCount(){
169 return this.boneCount;
173 * パースによって得られたモーフ数を返す。
176 protected int getMorphCount(){
177 return this.morphCount;
181 * パースによって得られたボーングループ数を返す。
184 protected int getBoneGroupCount(){
185 return this.boneGroupCount;
189 * 指定されたバイト長に収まるゼロ終端(0x00)文字列を読み込む。
191 * <p>入力バイト列はwindows-31jエンコーディングとして解釈される。
193 * <p>ゼロ終端以降のデータは無視されるが、
194 * IO入力は指定バイト数だけ読み進められる。
195 * ゼロ終端が見つからないまま指定バイト数が読み込み終わった場合、
196 * そこまでのデータから文字列を構成する。
198 * @param byteLen 読み込みバイト数
200 * @throws IOException IOエラー
201 * @throws MmdEofException 読み込む途中でストリーム終端に達した。
202 * @throws MmdFormatException 不正な文字エンコーディングが検出された。
204 protected String parsePmdText(int byteLen)
208 String result = parseString(this.decoderWin31j, byteLen);
214 * @throws IOException IOエラー
215 * @throws MmdFormatException フォーマットエラー
217 public void parsePmd()
218 throws IOException, MmdFormatException {
219 this.basicHandler.pmdParseStart();
223 boolean hasMoreData = hasMore();
224 this.basicHandler.pmdParseEnd(hasMoreData);
230 * PMDファイル本体のパースを開始する。
231 * パーサを拡張する場合はこのメソッドをオーバーライドする。
232 * @throws IOException IOエラー
233 * @throws MmdFormatException フォーマットエラー
235 protected void parseBody() throws IOException, MmdFormatException{
244 parseMorphOrderList();
245 parseBoneGroupList();
246 parseGroupedBoneList();
252 * PMDファイルヘッダ部のパースと通知。
253 * @throws IOException IOエラー
254 * @throws MmdFormatException フォーマットエラー
256 private void parsePmdHeader() throws IOException, MmdFormatException{
257 byte[] header = new byte[HEADER_LENGTH];
258 parseByteArray(header);
260 if( ! Arrays.equals(header, MAGIC_BYTES) ){
261 throw new MmdFormatException("unknown PMD-header type");
265 parsePmdText(PmdConst.MAXBYTES_MODELNAME);
267 parsePmdText(PmdConst.MAXBYTES_MODELDESC);
268 description = description.replace(CRLF, LF);
270 this.basicHandler.pmdHeaderInfo(header);
271 this.basicHandler.pmdModelInfo(modelName, description);
278 * @throws IOException IOエラー
279 * @throws MmdFormatException フォーマットエラー
281 private void parseVertexList() throws IOException, MmdFormatException{
282 int vertexNum = parseLeInt();
284 this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);
286 for(int ct = 0; ct < vertexNum; ct++){
287 float xPos = parseLeFloat();
288 float yPos = parseLeFloat();
289 float zPos = parseLeFloat();
290 this.shapeHandler.pmdVertexPosition(xPos, yPos, zPos);
292 float xVec = parseLeFloat();
293 float yVec = parseLeFloat();
294 float zVec = parseLeFloat();
295 this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);
297 float uVal = parseLeFloat();
298 float vVal = parseLeFloat();
299 this.shapeHandler.pmdVertexUV(uVal, vVal);
301 int boneId1 = parseLeUShortAsInt();
302 int boneId2 = parseLeUShortAsInt();
303 int weightForB1 = parseUByteAsInt();
304 this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);
306 boolean hideEdge = parseBoolean();
307 this.shapeHandler.pmdVertexEdge(hideEdge);
309 this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);
312 this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);
319 * @throws IOException IOエラー
320 * @throws MmdFormatException フォーマットエラー
322 private void parseSurfaceList() throws IOException, MmdFormatException{
323 int vertexNum = parseLeInt();
324 if(vertexNum % TRIVTX != 0) throw new MmdFormatException();
325 int surfaceNum = vertexNum / TRIVTX;
327 this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);
329 for(int ct = 0; ct < surfaceNum; ct++){
330 int vertexId1 = parseLeUShortAsInt();
331 int vertexId2 = parseLeUShortAsInt();
332 int vertexId3 = parseLeUShortAsInt();
333 this.shapeHandler.pmdSurfaceTriangle(vertexId1,
336 this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);
339 this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);
346 * @throws IOException IOエラー
347 * @throws MmdFormatException フォーマットエラー
349 private void parseMaterialList() throws IOException, MmdFormatException{
350 int materialNum = parseLeInt();
352 this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,
355 for(int ct = 0; ct < materialNum; ct++){
358 int toonidx = parseUByteAsInt();
359 boolean hasEdge = parseBoolean();
360 int surfaceCount = parseLeInt();
363 parsePmdText(PmdConst.MAXBYTES_TEXTUREFILENAME);
364 String[] splitted = ShadingUtil.splitShadingFileInfo(shadingFile);
365 String textureFile = splitted[0];
366 String sphereFile = splitted[1];
368 this.materialHandler.pmdMaterialShading(toonidx,
369 textureFile, sphereFile );
370 this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);
372 this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);
375 this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);
382 * @throws IOException IOエラー
383 * @throws MmdFormatException フォーマットエラー
385 private void parseColor() throws IOException, MmdFormatException{
390 red = parseLeFloat();
391 green = parseLeFloat();
392 blue = parseLeFloat();
393 float alpha = parseLeFloat();
395 this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);
397 float shininess = parseLeFloat();
398 red = parseLeFloat();
399 green = parseLeFloat();
400 blue = parseLeFloat();
402 this.materialHandler.pmdMaterialSpecular(red, green, blue,
405 red = parseLeFloat();
406 green = parseLeFloat();
407 blue = parseLeFloat();
409 this.materialHandler.pmdMaterialAmbient(red, green, blue);
416 * @throws IOException IOエラー
417 * @throws MmdFormatException フォーマットエラー
419 private void parseBoneList() throws IOException, MmdFormatException{
420 this.boneCount = parseLeUShortAsInt();
422 this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);
424 for(int ct = 0; ct < this.boneCount; ct++){
426 parsePmdText(PmdConst.MAXBYTES_BONENAME);
427 int parentId = parseLeUShortAsInt();
428 int tailId = parseLeUShortAsInt();
429 byte boneKind = parseByte();
430 int srcId = parseLeUShortAsInt();
432 this.boneHandler.pmdBoneInfo(boneName, boneKind);
433 this.boneHandler.pmdBoneLink(parentId, tailId, srcId);
435 float xPos = parseLeFloat();
436 float yPos = parseLeFloat();
437 float zPos = parseLeFloat();
439 this.boneHandler.pmdBonePosition(xPos, yPos, zPos);
441 this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);
444 this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);
451 * @throws IOException IOエラー
452 * @throws MmdFormatException フォーマットエラー
454 private void parseIKList() throws IOException, MmdFormatException{
455 int ikCount = parseLeUShortAsInt();
457 this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);
459 for(int ct = 0; ct < ikCount; ct++){
460 int boneId = parseLeUShortAsInt();
461 int targetId = parseLeUShortAsInt();
462 int chainLength = parseUByteAsInt();
463 int depth = parseLeUShortAsInt();
464 float weight = parseLeFloat();
466 this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);
468 parseIKChainList(chainLength);
470 this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);
473 this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);
480 * @param chainLength チェーン長
481 * @throws IOException IOエラー
482 * @throws MmdFormatException フォーマットエラー
484 private void parseIKChainList(int chainLength)
485 throws IOException, MmdFormatException{
486 this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,
489 for(int ct = 0; ct < chainLength; ct++){
490 int childId = parseLeUShortAsInt();
491 this.boneHandler.pmdIKChainInfo(childId);
493 this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);
496 this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);
503 * @throws IOException IOエラー
504 * @throws MmdFormatException フォーマットエラー
506 private void parseMorphList() throws IOException, MmdFormatException{
507 this.morphCount = parseLeUShortAsInt();
509 this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,
512 for(int ct = 0; ct < this.morphCount; ct++){
514 parsePmdText(PmdConst.MAXBYTES_MORPHNAME);
515 int vertexCount = parseLeInt();
516 byte morphType = parseByte();
518 this.morphHandler.pmdMorphInfo(morphName, morphType);
520 parseMorphVertexList(vertexCount);
522 this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);
525 this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);
532 * @param vertexCount 頂点数
533 * @throws IOException IOエラー
534 * @throws MmdFormatException フォーマットエラー
536 private void parseMorphVertexList(int vertexCount)
537 throws IOException, MmdFormatException{
538 this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,
541 for(int ct = 0; ct < vertexCount; ct++){
542 int vertexId = parseLeInt();
543 float xPos = parseLeFloat();
544 float yPos = parseLeFloat();
545 float zPos = parseLeFloat();
546 this.morphHandler.pmdMorphVertexInfo(vertexId, xPos, yPos, zPos);
548 this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);
551 this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);
558 * @throws IOException IOエラー
559 * @throws MmdFormatException フォーマットエラー
561 private void parseMorphOrderList()
562 throws IOException, MmdFormatException{
563 int morphOrderCount = parseUByteAsInt();
565 this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,
568 for(int ct = 0; ct < morphOrderCount; ct++){
569 int morphId = parseLeUShortAsInt();
570 this.morphHandler.pmdMorphOrderInfo(morphId);
572 this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);
575 this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);
582 * @throws IOException IOエラー
583 * @throws MmdFormatException フォーマットエラー
585 private void parseBoneGroupList()
586 throws IOException, MmdFormatException{
587 this.boneGroupCount = parseUByteAsInt();
589 this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,
590 this.boneGroupCount);
592 for(int ct = 0; ct < this.boneGroupCount; ct++){
594 parsePmdText(PmdConst.MAXBYTES_BONEGROUPNAME);
595 groupName = chopLastLF(groupName);
596 this.boneHandler.pmdBoneGroupInfo(groupName);
598 this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);
601 this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);
608 * @throws IOException IOエラー
609 * @throws MmdFormatException フォーマットエラー
611 private void parseGroupedBoneList()
612 throws IOException, MmdFormatException{
613 int groupedBoneCount = parseLeInt();
615 this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,
618 for(int ct = 0; ct < groupedBoneCount; ct++){
619 int boneId = parseLeUShortAsInt();
620 int groupId = parseUByteAsInt();
621 this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);
623 this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);
626 this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);