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