4 * License : The MIT License
5 * Copyright(c) 2010 MikuToga Partners
8 package jp.sourceforge.mikutoga.parser.pmd;
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 {
21 protected static final String CR = "\r"; // 0x0d
23 protected static final String LF = "\n"; // 0x0a
25 protected static final String CRLF = CR + LF; // 0x0d, 0x0a
27 private static final String MAGIC = "Pmd";
28 private static final int MAGIC_SZ = MAGIC.getBytes(CS_WIN31J).length;
30 private static final int VERTEX_DATA_SZ = 38;
31 private static final int SURFACE_DATA_SZ = 6;
32 private static final int MATERIAL_DATA_SZ = 70;
33 private static final int BONE_DATA_SZ = 39;
34 private static final int MORPHVERTEX_DATA_SZ = 16;
35 private static final int MORPHORDER_DATA_SZ = 2;
36 private static final int BONEGROUP_DATA_SZ = 50;
37 private static final int GROUPEDBONE_DATA_SZ = 3;
40 private PmdBasicHandler basicHandler = null;
41 private PmdShapeHandler shapeHandler = null;
42 private PmdMaterialHandler materialHandler = null;
43 private PmdBoneHandler boneHandler = null;
44 private PmdMorphHandler morphHandler = null;
46 private int boneCount = -1;
47 private int morphCount = -1;
48 private int boneGroupCount = -1;
55 public PmdParserBase(MmdSource source){
61 * 文字列の最後がLF(0x0a)の場合削除する。
63 * @return 末尾LFが削除された文字列
65 public static String chopLastLF(String name){
68 if(name.endsWith(LF)){
69 result = name.substring(0, name.length() - 1);
79 * テクスチャファイル名とスフィアマップファイル名を分離する。
80 * @param shadingFile シェーディング用ファイル情報
81 * @return [0]:テクスチャファイル名 [1]:スフィアマップファイル名。
84 public static String[] splitShadingFileInfo(String shadingFile){
87 result = shadingFile.split('\\'+"*", 2);
88 assert result.length == 1 || result.length == 2;
90 if(result.length == 1){
91 String onlyFile = result[0];
92 result = new String[2];
95 if(onlyFile.endsWith(".sph") || onlyFile.endsWith(".spa")){
102 assert result.length == 2;
109 * @param handler ハンドラ
111 public void setBasicHandler(PmdBasicHandler handler){
112 this.basicHandler = handler;
118 * @param handler ハンドラ
120 public void setShapeHandler(PmdShapeHandler handler){
121 this.shapeHandler = handler;
127 * @param handler ハンドラ
129 public void setMaterialHandler(PmdMaterialHandler handler){
130 this.materialHandler = handler;
136 * @param handler ハンドラ
138 public void setBoneHandler(PmdBoneHandler handler){
139 this.boneHandler = handler;
145 * @param handler ハンドラ
147 public void setMorphHandler(PmdMorphHandler handler){
148 this.morphHandler = handler;
153 * パースによって得られたボーン数を返す。
156 protected int getBoneCount(){
157 return this.boneCount;
161 * パースによって得られたモーフ数を返す。
164 protected int getMorphCount(){
165 return this.morphCount;
169 * パースによって得られたボーングループ数を返す。
172 protected int getBoneGroupCount(){
173 return this.boneGroupCount;
178 * @throws IOException IOエラー
179 * @throws MmdFormatException フォーマットエラー
181 public void parsePmd()
182 throws IOException, MmdFormatException {
183 if(this.basicHandler != null){
184 this.basicHandler.pmdParseStart();
189 boolean hasMoreData = hasMore();
190 if(this.basicHandler != null){
191 this.basicHandler.pmdParseEnd(hasMoreData);
198 * PMDファイル本体のパースを開始する。
199 * パーサを拡張する場合はこのメソッドをオーバーライドする。
200 * @throws IOException IOエラー
201 * @throws MmdFormatException フォーマットエラー
203 protected void parseBody() throws IOException, MmdFormatException{
212 parseMorphOrderList();
213 parseBoneGroupList();
214 parseGroupedBoneList();
220 * PMDファイルヘッダ部のパースと通知。
221 * @throws IOException IOエラー
222 * @throws MmdFormatException フォーマットエラー
224 private void parsePmdHeader() throws IOException, MmdFormatException{
225 String magic = parseZeroTermString(MAGIC_SZ);
226 if( ! magic.equals(MAGIC) ){
227 throw new MmdFormatException("unrecognized magic data");
230 float ver = parseFloat();
232 parseZeroTermString(PmdLimits.MAXBYTES_MODELNAME);
234 parseZeroTermString(PmdLimits.MAXBYTES_MODELDESC);
235 description = description.replace(CRLF, LF);
237 if(this.basicHandler != null){
238 this.basicHandler.pmdHeaderInfo(ver);
239 this.basicHandler.pmdModelInfo(modelName, description);
247 * @throws IOException IOエラー
248 * @throws MmdFormatException フォーマットエラー
250 private void parseVertexList() throws IOException, MmdFormatException{
251 int vertexNum = parseInteger();
253 if(this.shapeHandler == null){
254 skip(VERTEX_DATA_SZ * vertexNum);
258 this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);
260 for(int ct = 0; ct < vertexNum; ct++){
261 float xPos = parseFloat();
262 float yPos = parseFloat();
263 float zPos = parseFloat();
264 this.shapeHandler.pmdVertexPosition(xPos, yPos, zPos);
266 float xVec = parseFloat();
267 float yVec = parseFloat();
268 float zVec = parseFloat();
269 this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);
271 float uVal = parseFloat();
272 float vVal = parseFloat();
273 this.shapeHandler.pmdVertexUV(uVal, vVal);
275 int boneId1 = parseUShortAsInteger();
276 int boneId2 = parseUShortAsInteger();
277 int weightForB1 = parseUByteAsInteger();
278 this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);
280 boolean hideEdge = parseBoolean();
281 this.shapeHandler.pmdVertexEdge(hideEdge);
283 this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);
286 this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);
293 * @throws IOException IOエラー
294 * @throws MmdFormatException フォーマットエラー
296 private void parseSurfaceList() throws IOException, MmdFormatException{
297 int vertexNum = parseInteger();
298 if(vertexNum % 3 != 0) throw new MmdFormatException();
299 int surfaceNum = vertexNum / 3;
301 if(this.shapeHandler == null){
302 skip(SURFACE_DATA_SZ * surfaceNum);
306 this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);
308 for(int ct = 0; ct < surfaceNum; ct++){
309 int vertexId1 = parseUShortAsInteger();
310 int vertexId2 = parseUShortAsInteger();
311 int vertexId3 = parseUShortAsInteger();
312 this.shapeHandler.pmdSurfaceTriangle(vertexId1,
315 this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);
318 this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);
325 * @throws IOException IOエラー
326 * @throws MmdFormatException フォーマットエラー
328 private void parseMaterialList() throws IOException, MmdFormatException{
329 int materialNum = parseInteger();
331 if(this.materialHandler == null){
332 skip(MATERIAL_DATA_SZ * materialNum);
336 this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,
339 for(int ct = 0; ct < materialNum; ct++){
345 green = parseFloat();
347 float alpha = parseFloat();
348 this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);
350 float shininess = parseFloat();
352 green = parseFloat();
354 this.materialHandler.pmdMaterialSpecular(red, green, blue,
358 green = parseFloat();
360 this.materialHandler.pmdMaterialAmbient(red, green, blue);
362 int toonidx = parseUByteAsInteger();
363 boolean hasEdge = parseBoolean();
364 int surfaceCount = parseInteger();
366 parseZeroTermString(PmdLimits.MAXBYTES_TEXTUREFILENAME);
367 String[] splitted = splitShadingFileInfo(shadingFile);
368 String textureFile = splitted[0];
369 String sphereFile = splitted[1];
371 this.materialHandler.pmdMaterialShading(toonidx,
372 textureFile, sphereFile );
373 this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);
375 this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);
378 this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);
385 * @throws IOException IOエラー
386 * @throws MmdFormatException フォーマットエラー
388 private void parseBoneList() throws IOException, MmdFormatException{
389 this.boneCount = parseUShortAsInteger();
391 if(this.boneHandler == null){
392 skip(BONE_DATA_SZ * this.boneCount);
396 this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);
398 for(int ct = 0; ct < this.boneCount; ct++){
400 parseZeroTermString(PmdLimits.MAXBYTES_BONENAME);
401 int parentId = parseUShortAsInteger();
402 int tailId = parseUShortAsInteger();
403 byte boneKind = parseByte();
404 int ikId = parseUShortAsInteger();
406 this.boneHandler.pmdBoneInfo(boneName, boneKind);
407 this.boneHandler.pmdBoneLink(parentId, tailId, ikId);
409 float xPos = parseFloat();
410 float yPos = parseFloat();
411 float zPos = parseFloat();
413 this.boneHandler.pmdBonePosition(xPos, yPos, zPos);
415 this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);
418 this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);
425 * @throws IOException IOエラー
426 * @throws MmdFormatException フォーマットエラー
428 private void parseIKList() throws IOException, MmdFormatException{
429 int ikCount = parseUShortAsInteger();
431 if(this.boneHandler != null){
432 this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);
435 for(int ct = 0; ct < ikCount; ct++){
436 int boneId = parseUShortAsInteger();
437 int targetId = parseUShortAsInteger();
438 int chainLength = parseUByteAsInteger();
439 int depth = parseUShortAsInteger();
440 float weight = parseFloat();
442 parseIKChainList(chainLength);
444 if(this.boneHandler != null){
445 this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);
446 this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);
450 if(this.boneHandler != null){
451 this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);
459 * @param chainLength チェーン長
460 * @throws IOException IOエラー
461 * @throws MmdFormatException フォーマットエラー
463 private void parseIKChainList(int chainLength)
464 throws IOException, MmdFormatException{
465 if(this.boneHandler != null){
466 this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,
470 for(int ct = 0; ct < chainLength; ct++){
471 int childId = parseUShortAsInteger();
472 if(this.boneHandler != null){
473 this.boneHandler.pmdIKChainInfo(childId);
474 this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);
478 if(this.boneHandler != null){
479 this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);
487 * @throws IOException IOエラー
488 * @throws MmdFormatException フォーマットエラー
490 private void parseMorphList() throws IOException, MmdFormatException{
491 this.morphCount = parseUShortAsInteger();
493 if(this.morphHandler != null){
494 this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,
498 for(int ct = 0; ct < this.morphCount; ct++){
500 parseZeroTermString(PmdLimits.MAXBYTES_MORPHNAME);
501 int vertexCount = parseInteger();
502 byte morphType = parseByte();
504 if(this.morphHandler != null){
505 this.morphHandler.pmdMorphInfo(morphName, morphType);
508 parseMorphVertexList(vertexCount);
510 if(this.morphHandler != null){
511 this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);
515 if(this.morphHandler != null){
516 this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);
524 * @param vertexCount 頂点数
525 * @throws IOException IOエラー
526 * @throws MmdFormatException フォーマットエラー
528 private void parseMorphVertexList(int vertexCount)
529 throws IOException, MmdFormatException{
530 if(this.morphHandler == null){
531 skip(MORPHVERTEX_DATA_SZ * vertexCount);
535 this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,
538 for(int ct = 0; ct < vertexCount; ct++){
539 int vertexId = parseInteger();
540 float xPos = parseFloat();
541 float yPos = parseFloat();
542 float zPos = parseFloat();
543 this.morphHandler.pmdMorphVertexInfo(vertexId, xPos, yPos, zPos);
544 this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);
547 this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);
554 * @throws IOException IOエラー
555 * @throws MmdFormatException フォーマットエラー
557 private void parseMorphOrderList()
558 throws IOException, MmdFormatException{
559 int morphOrderCount = parseUByteAsInteger();
561 if(this.morphHandler == null){
562 skip(MORPHORDER_DATA_SZ * morphOrderCount);
566 this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,
569 for(int ct = 0; ct < morphOrderCount; ct++){
570 int morphId = parseUShortAsInteger();
571 this.morphHandler.pmdMorphOrderInfo(morphId);
573 this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);
576 this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);
583 * @throws IOException IOエラー
584 * @throws MmdFormatException フォーマットエラー
586 private void parseBoneGroupList()
587 throws IOException, MmdFormatException{
588 this.boneGroupCount = parseUByteAsInteger();
590 if(this.boneHandler == null){
591 skip(BONEGROUP_DATA_SZ * this.boneGroupCount);
595 this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,
596 this.boneGroupCount);
598 for(int ct = 0; ct < this.boneGroupCount; ct++){
600 parseZeroTermString(PmdLimits.MAXBYTES_BONEGROUPNAME);
601 groupName = chopLastLF(groupName);
602 this.boneHandler.pmdBoneGroupInfo(groupName);
604 this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);
607 this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);
614 * @throws IOException IOエラー
615 * @throws MmdFormatException フォーマットエラー
617 private void parseGroupedBoneList()
618 throws IOException, MmdFormatException{
619 int groupedBoneCount = parseInteger();
621 if(this.boneHandler == null){
622 skip(GROUPEDBONE_DATA_SZ * groupedBoneCount);
626 this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,
629 for(int ct = 0; ct < groupedBoneCount; ct++){
630 int boneId = parseUShortAsInteger();
631 int groupId = parseUByteAsInteger();
632 this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);
634 this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);
637 this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);