4 * License : The MIT License
5 * Copyright(c) 2010 MikuToga Partners
8 package jp.sourceforge.mikutoga.pmd.parser;
10 import java.io.IOException;
11 import jp.sourceforge.mikutoga.parser.CommonParser;
12 import jp.sourceforge.mikutoga.parser.MmdFormatException;
13 import jp.sourceforge.mikutoga.parser.MmdSource;
18 public class PmdParserBase extends CommonParser {
20 public static final int HEADER_LENGTH = 7;
23 protected static final String CR = "\r"; // 0x0d
25 protected static final String LF = "\n"; // 0x0a
27 protected static final String CRLF = CR + LF; // 0x0d, 0x0a
29 private static final byte[] MAGIC_BYTES = {
30 (byte)0x50, (byte)0x6d, (byte)0x64, // "Pmd"
31 (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x3f, // 1.0f
34 private static final int VERTEX_DATA_SZ = 38;
35 private static final int SURFACE_DATA_SZ = 6;
36 private static final int MATERIAL_DATA_SZ = 70;
37 private static final int BONE_DATA_SZ = 39;
38 private static final int MORPHVERTEX_DATA_SZ = 16;
39 private static final int MORPHORDER_DATA_SZ = 2;
40 private static final int BONEGROUP_DATA_SZ = 50;
41 private static final int GROUPEDBONE_DATA_SZ = 3;
45 assert MAGIC_BYTES.length == HEADER_LENGTH;
49 private PmdBasicHandler basicHandler = null;
50 private PmdShapeHandler shapeHandler = null;
51 private PmdMaterialHandler materialHandler = null;
52 private PmdBoneHandler boneHandler = null;
53 private PmdMorphHandler morphHandler = null;
55 private int boneCount = -1;
56 private int morphCount = -1;
57 private int boneGroupCount = -1;
64 public PmdParserBase(MmdSource source){
70 * 文字列の最後がLF(0x0a)の場合削除する。
72 * @return 末尾LFが削除された文字列
74 public static String chopLastLF(String name){
77 if(name.endsWith(LF)){
78 result = name.substring(0, name.length() - 1);
88 * テクスチャファイル名とスフィアマップファイル名を分離する。
89 * @param shadingFile シェーディング用ファイル情報
90 * @return [0]:テクスチャファイル名 [1]:スフィアマップファイル名。
93 public static String[] splitShadingFileInfo(String shadingFile){
96 result = shadingFile.split('\\'+"*", 2);
97 assert result.length == 1 || result.length == 2;
99 if(result.length == 1){
100 String onlyFile = result[0];
101 result = new String[2];
104 if(onlyFile.endsWith(".sph") || onlyFile.endsWith(".spa")){
105 result[1] = onlyFile;
107 result[0] = onlyFile;
111 assert result.length == 2;
118 * @param handler ハンドラ
120 public void setBasicHandler(PmdBasicHandler handler){
121 this.basicHandler = handler;
127 * @param handler ハンドラ
129 public void setShapeHandler(PmdShapeHandler handler){
130 this.shapeHandler = handler;
136 * @param handler ハンドラ
138 public void setMaterialHandler(PmdMaterialHandler handler){
139 this.materialHandler = handler;
145 * @param handler ハンドラ
147 public void setBoneHandler(PmdBoneHandler handler){
148 this.boneHandler = handler;
154 * @param handler ハンドラ
156 public void setMorphHandler(PmdMorphHandler handler){
157 this.morphHandler = handler;
162 * パースによって得られたボーン数を返す。
165 protected int getBoneCount(){
166 return this.boneCount;
170 * パースによって得られたモーフ数を返す。
173 protected int getMorphCount(){
174 return this.morphCount;
178 * パースによって得られたボーングループ数を返す。
181 protected int getBoneGroupCount(){
182 return this.boneGroupCount;
187 * @throws IOException IOエラー
188 * @throws MmdFormatException フォーマットエラー
190 public void parsePmd()
191 throws IOException, MmdFormatException {
192 if(this.basicHandler != null){
193 this.basicHandler.pmdParseStart();
198 boolean hasMoreData = hasMore();
199 if(this.basicHandler != null){
200 this.basicHandler.pmdParseEnd(hasMoreData);
207 * PMDファイル本体のパースを開始する。
208 * パーサを拡張する場合はこのメソッドをオーバーライドする。
209 * @throws IOException IOエラー
210 * @throws MmdFormatException フォーマットエラー
212 protected void parseBody() throws IOException, MmdFormatException{
221 parseMorphOrderList();
222 parseBoneGroupList();
223 parseGroupedBoneList();
229 * PMDファイルヘッダ部のパースと通知。
230 * @throws IOException IOエラー
231 * @throws MmdFormatException フォーマットエラー
233 private void parsePmdHeader() throws IOException, MmdFormatException{
234 byte[] header = new byte[HEADER_LENGTH];
235 parseByteArray(header);
237 for(int idx = 0; idx < MAGIC_BYTES.length; idx++){
238 if(header[idx] != MAGIC_BYTES[idx]){
239 throw new MmdFormatException("unknown PMD-header type");
244 parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELNAME);
246 parseZeroTermWin31J(PmdLimits.MAXBYTES_MODELDESC);
247 description = description.replace(CRLF, LF);
249 if(this.basicHandler != null){
250 this.basicHandler.pmdHeaderInfo(header);
251 this.basicHandler.pmdModelInfo(modelName, description);
259 * @throws IOException IOエラー
260 * @throws MmdFormatException フォーマットエラー
262 private void parseVertexList() throws IOException, MmdFormatException{
263 int vertexNum = parseInteger();
265 if(this.shapeHandler == null){
266 skip(VERTEX_DATA_SZ * vertexNum);
270 this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);
272 for(int ct = 0; ct < vertexNum; ct++){
273 float xPos = parseFloat();
274 float yPos = parseFloat();
275 float zPos = parseFloat();
276 this.shapeHandler.pmdVertexPosition(xPos, yPos, zPos);
278 float xVec = parseFloat();
279 float yVec = parseFloat();
280 float zVec = parseFloat();
281 this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);
283 float uVal = parseFloat();
284 float vVal = parseFloat();
285 this.shapeHandler.pmdVertexUV(uVal, vVal);
287 int boneId1 = parseUShortAsInteger();
288 int boneId2 = parseUShortAsInteger();
289 int weightForB1 = parseUByteAsInteger();
290 this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);
292 boolean hideEdge = parseBoolean();
293 this.shapeHandler.pmdVertexEdge(hideEdge);
295 this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);
298 this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);
305 * @throws IOException IOエラー
306 * @throws MmdFormatException フォーマットエラー
308 private void parseSurfaceList() throws IOException, MmdFormatException{
309 int vertexNum = parseInteger();
310 if(vertexNum % 3 != 0) throw new MmdFormatException();
311 int surfaceNum = vertexNum / 3;
313 if(this.shapeHandler == null){
314 skip(SURFACE_DATA_SZ * surfaceNum);
318 this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);
320 for(int ct = 0; ct < surfaceNum; ct++){
321 int vertexId1 = parseUShortAsInteger();
322 int vertexId2 = parseUShortAsInteger();
323 int vertexId3 = parseUShortAsInteger();
324 this.shapeHandler.pmdSurfaceTriangle(vertexId1,
327 this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);
330 this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);
337 * @throws IOException IOエラー
338 * @throws MmdFormatException フォーマットエラー
340 private void parseMaterialList() throws IOException, MmdFormatException{
341 int materialNum = parseInteger();
343 if(this.materialHandler == null){
344 skip(MATERIAL_DATA_SZ * materialNum);
348 this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,
351 for(int ct = 0; ct < materialNum; ct++){
357 green = parseFloat();
359 float alpha = parseFloat();
360 this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);
362 float shininess = parseFloat();
364 green = parseFloat();
366 this.materialHandler.pmdMaterialSpecular(red, green, blue,
370 green = parseFloat();
372 this.materialHandler.pmdMaterialAmbient(red, green, blue);
374 int toonidx = parseUByteAsInteger();
375 boolean hasEdge = parseBoolean();
376 int surfaceCount = parseInteger();
378 parseZeroTermWin31J(PmdLimits.MAXBYTES_TEXTUREFILENAME);
379 String[] splitted = splitShadingFileInfo(shadingFile);
380 String textureFile = splitted[0];
381 String sphereFile = splitted[1];
383 this.materialHandler.pmdMaterialShading(toonidx,
384 textureFile, sphereFile );
385 this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);
387 this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);
390 this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);
397 * @throws IOException IOエラー
398 * @throws MmdFormatException フォーマットエラー
400 private void parseBoneList() throws IOException, MmdFormatException{
401 this.boneCount = parseUShortAsInteger();
403 if(this.boneHandler == null){
404 skip(BONE_DATA_SZ * this.boneCount);
408 this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);
410 for(int ct = 0; ct < this.boneCount; ct++){
412 parseZeroTermWin31J(PmdLimits.MAXBYTES_BONENAME);
413 int parentId = parseUShortAsInteger();
414 int tailId = parseUShortAsInteger();
415 byte boneKind = parseByte();
416 int ikId = parseUShortAsInteger();
418 this.boneHandler.pmdBoneInfo(boneName, boneKind);
419 this.boneHandler.pmdBoneLink(parentId, tailId, ikId);
421 float xPos = parseFloat();
422 float yPos = parseFloat();
423 float zPos = parseFloat();
425 this.boneHandler.pmdBonePosition(xPos, yPos, zPos);
427 this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);
430 this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);
437 * @throws IOException IOエラー
438 * @throws MmdFormatException フォーマットエラー
440 private void parseIKList() throws IOException, MmdFormatException{
441 int ikCount = parseUShortAsInteger();
443 if(this.boneHandler != null){
444 this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);
447 for(int ct = 0; ct < ikCount; ct++){
448 int boneId = parseUShortAsInteger();
449 int targetId = parseUShortAsInteger();
450 int chainLength = parseUByteAsInteger();
451 int depth = parseUShortAsInteger();
452 float weight = parseFloat();
454 parseIKChainList(chainLength);
456 if(this.boneHandler != null){
457 this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);
458 this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);
462 if(this.boneHandler != null){
463 this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);
471 * @param chainLength チェーン長
472 * @throws IOException IOエラー
473 * @throws MmdFormatException フォーマットエラー
475 private void parseIKChainList(int chainLength)
476 throws IOException, MmdFormatException{
477 if(this.boneHandler != null){
478 this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,
482 for(int ct = 0; ct < chainLength; ct++){
483 int childId = parseUShortAsInteger();
484 if(this.boneHandler != null){
485 this.boneHandler.pmdIKChainInfo(childId);
486 this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);
490 if(this.boneHandler != null){
491 this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);
499 * @throws IOException IOエラー
500 * @throws MmdFormatException フォーマットエラー
502 private void parseMorphList() throws IOException, MmdFormatException{
503 this.morphCount = parseUShortAsInteger();
505 if(this.morphHandler != null){
506 this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,
510 for(int ct = 0; ct < this.morphCount; ct++){
512 parseZeroTermWin31J(PmdLimits.MAXBYTES_MORPHNAME);
513 int vertexCount = parseInteger();
514 byte morphType = parseByte();
516 if(this.morphHandler != null){
517 this.morphHandler.pmdMorphInfo(morphName, morphType);
520 parseMorphVertexList(vertexCount);
522 if(this.morphHandler != null){
523 this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);
527 if(this.morphHandler != null){
528 this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);
536 * @param vertexCount 頂点数
537 * @throws IOException IOエラー
538 * @throws MmdFormatException フォーマットエラー
540 private void parseMorphVertexList(int vertexCount)
541 throws IOException, MmdFormatException{
542 if(this.morphHandler == null){
543 skip(MORPHVERTEX_DATA_SZ * vertexCount);
547 this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,
550 for(int ct = 0; ct < vertexCount; ct++){
551 int vertexId = parseInteger();
552 float xPos = parseFloat();
553 float yPos = parseFloat();
554 float zPos = parseFloat();
555 this.morphHandler.pmdMorphVertexInfo(vertexId, xPos, yPos, zPos);
556 this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);
559 this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);
566 * @throws IOException IOエラー
567 * @throws MmdFormatException フォーマットエラー
569 private void parseMorphOrderList()
570 throws IOException, MmdFormatException{
571 int morphOrderCount = parseUByteAsInteger();
573 if(this.morphHandler == null){
574 skip(MORPHORDER_DATA_SZ * morphOrderCount);
578 this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,
581 for(int ct = 0; ct < morphOrderCount; ct++){
582 int morphId = parseUShortAsInteger();
583 this.morphHandler.pmdMorphOrderInfo(morphId);
585 this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);
588 this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);
595 * @throws IOException IOエラー
596 * @throws MmdFormatException フォーマットエラー
598 private void parseBoneGroupList()
599 throws IOException, MmdFormatException{
600 this.boneGroupCount = parseUByteAsInteger();
602 if(this.boneHandler == null){
603 skip(BONEGROUP_DATA_SZ * this.boneGroupCount);
607 this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,
608 this.boneGroupCount);
610 for(int ct = 0; ct < this.boneGroupCount; ct++){
612 parseZeroTermWin31J(PmdLimits.MAXBYTES_BONEGROUPNAME);
613 groupName = chopLastLF(groupName);
614 this.boneHandler.pmdBoneGroupInfo(groupName);
616 this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);
619 this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);
626 * @throws IOException IOエラー
627 * @throws MmdFormatException フォーマットエラー
629 private void parseGroupedBoneList()
630 throws IOException, MmdFormatException{
631 int groupedBoneCount = parseInteger();
633 if(this.boneHandler == null){
634 skip(GROUPEDBONE_DATA_SZ * groupedBoneCount);
638 this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,
641 for(int ct = 0; ct < groupedBoneCount; ct++){
642 int boneId = parseUShortAsInteger();
643 int groupId = parseUByteAsInteger();
644 this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);
646 this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);
649 this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);