OSDN Git Service

PMD出力機能及びXML入出力機能のソースをマージ
[mikutoga/TogaGem.git] / src / main / java / jp / sourceforge / mikutoga / pmd / xml / PmdXmlExporter.java
1 /*\r
2  * pmd-xml exporter\r
3  *\r
4  * License : The MIT License\r
5  * Copyright(c) 2010 MikuToga Partners\r
6  */\r
7 \r
8 package jp.sourceforge.mikutoga.pmd.xml;\r
9 \r
10 import java.awt.Color;\r
11 import java.io.IOException;\r
12 import java.io.OutputStream;\r
13 import java.util.List;\r
14 import java.util.Map;\r
15 import jp.sourceforge.mikutoga.corelib.I18nText;\r
16 import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
17 import jp.sourceforge.mikutoga.pmd.BoneGroup;\r
18 import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
19 import jp.sourceforge.mikutoga.pmd.BoneType;\r
20 import jp.sourceforge.mikutoga.pmd.Deg3d;\r
21 import jp.sourceforge.mikutoga.pmd.DynamicsInfo;\r
22 import jp.sourceforge.mikutoga.pmd.IKChain;\r
23 import jp.sourceforge.mikutoga.pmd.JointInfo;\r
24 import jp.sourceforge.mikutoga.pmd.Material;\r
25 import jp.sourceforge.mikutoga.pmd.MorphPart;\r
26 import jp.sourceforge.mikutoga.pmd.MorphType;\r
27 import jp.sourceforge.mikutoga.pmd.MorphVertex;\r
28 import jp.sourceforge.mikutoga.pmd.PmdModel;\r
29 import jp.sourceforge.mikutoga.pmd.Pos2d;\r
30 import jp.sourceforge.mikutoga.pmd.Pos3d;\r
31 import jp.sourceforge.mikutoga.pmd.Rad3d;\r
32 import jp.sourceforge.mikutoga.pmd.RigidGroup;\r
33 import jp.sourceforge.mikutoga.pmd.RigidInfo;\r
34 import jp.sourceforge.mikutoga.pmd.RigidShape;\r
35 import jp.sourceforge.mikutoga.pmd.RigidShapeType;\r
36 import jp.sourceforge.mikutoga.pmd.ShadeInfo;\r
37 import jp.sourceforge.mikutoga.pmd.Surface;\r
38 import jp.sourceforge.mikutoga.pmd.ToonMap;\r
39 import jp.sourceforge.mikutoga.pmd.TripletRange;\r
40 import jp.sourceforge.mikutoga.pmd.Vec3d;\r
41 import jp.sourceforge.mikutoga.pmd.Vertex;\r
42 import jp.sourceforge.mikutoga.xml.BasicXmlExporter;\r
43 import jp.sourceforge.mikutoga.xml.XmlResourceResolver;\r
44 \r
45 /**\r
46  * XML形式でPMDモデルデータを出力する。\r
47  */\r
48 public class PmdXmlExporter extends BasicXmlExporter{\r
49 \r
50     private static final String GENERATOR = "Mikutoga" + " Ver 0.0.1";\r
51     private static final String TOP_COMMENT =\r
52             "  MikuMikuDance\n    model-data(*.pmd) on XML";\r
53     private static final String SCHEMA_LOCATION =\r
54             PmdXmlResources.NS_PMDXML + " " + PmdXmlResources.SCHEMA_PMDXML;\r
55 \r
56     /** 改行文字列 CR。 */\r
57     private static final String CR = "\r";       // 0x0d\r
58     /** 改行文字列 LF。 */\r
59     private static final String LF = "\n";       // 0x0a\r
60     /** 改行文字列 CRLF。 */\r
61     private static final String CRLF = CR + LF;  // 0x0d, 0x0a\r
62 \r
63     private static final String PFX_SURFACEGROUP = "sg";\r
64     private static final String PFX_TOONFILE = "tf";\r
65     private static final String PFX_VERTEX = "vtx";\r
66     private static final String PFX_BONE = "bn";\r
67     private static final String PFX_RIGID = "rd";\r
68     private static final String PFX_RIGIDGROUP = "rg";\r
69 \r
70     private static final String BONETYPE_COMMENT =\r
71           "Bone types:\n"\r
72         + "[0 : ROTATE      : Rotate       : 回転           :]\n"\r
73         + "[1 : ROTMOV      : Rotate/Move  : 回転/移動      :]\n"\r
74         + "[2 : IK          : IK           : IK             :]\n"\r
75         + "[3 : UNKNOWN     : Unknown      : 不明           :]\n"\r
76         + "[4 : UNDERIK     : Under IK     : IK影響下(回転) :]\n"\r
77         + "[5 : UNDERROT    : Under rotate : 回転影響下     :]\n"\r
78         + "[6 : IKCONNECTED : IK connected : IK接続先       :]\n"\r
79         + "[7 : HIDDEN      : Hidden       : 非表示         :]\n"\r
80         + "[8 : TWIST       : Twist        : 捩り           :]\n"\r
81         + "[9 : LINKEDROT   : Linked Rotate: 回転連動       :]\n";\r
82 \r
83     private static final String MORPHTYPE_COMMENT =\r
84           "Morph types:\n"\r
85         + "[1 : EYEBROW : まゆ   ]\n"\r
86         + "[2 : EYE     : 目     ]\n"\r
87         + "[3 : LIP     : リップ ]\n"\r
88         + "[4 : EXTRA   : その他 ]\n";\r
89 \r
90     private static final String RIGIDBEHAVIOR_COMMENT =\r
91           "Rigid behavior types:\n"\r
92         + "[0 : FOLLOWBONE    : ボーン追従       ]\n"\r
93         + "[1 : ONLYDYNAMICS  : 物理演算         ]\n"\r
94         + "[2 : BONEDDYNAMICS : ボーン位置合わせ ]\n";\r
95 \r
96     /**\r
97      * コンストラクタ。\r
98      * 文字エンコーディングはUTF-8が用いられる。\r
99      * @param stream 出力ストリーム\r
100      */\r
101     public PmdXmlExporter(OutputStream stream){\r
102         super(stream);\r
103         return;\r
104     }\r
105 \r
106     /**\r
107      * 任意の文字列がBasicLatin文字のみから構成されるか判定する。\r
108      * @param seq 文字列\r
109      * @return null、長さ0もしくはBasicLatin文字のみから構成されるならtrue\r
110      */\r
111     public static boolean hasOnlyBasicLatin(CharSequence seq){\r
112         if(seq == null) return true;\r
113         int length = seq.length();\r
114         for(int pos = 0; pos < length; pos++){\r
115             char ch = seq.charAt(pos);\r
116             if(ch > 0x007f) return false;\r
117         }\r
118         return true;\r
119     }\r
120 \r
121     /**\r
122      * {@inheritDoc}\r
123      * @return {@inheritDoc}\r
124      * @throws IOException {@inheritDoc}\r
125      */\r
126     @Override\r
127     public PmdXmlExporter ind() throws IOException{\r
128         super.ind();\r
129         return this;\r
130     }\r
131 \r
132     /**\r
133      * 文字参照によるエスケープを補佐するためのコメントを出力する。\r
134      * @param seq 文字列\r
135      * @return this本体\r
136      * @throws IOException 出力エラー\r
137      */\r
138     protected PmdXmlExporter putUnescapedComment(CharSequence seq)\r
139             throws IOException{\r
140         if( ! isBasicLatinOnlyOut() ) return this;\r
141         if(hasOnlyBasicLatin(seq)) return this;\r
142         put(' ').putLineComment(seq);\r
143         return this;\r
144     }\r
145 \r
146     /**\r
147      * 多言語化された各種識別名を出力する。\r
148      * プライマリ名は出力対象外。\r
149      * @param text 多言語文字列\r
150      * @return this本体\r
151      * @throws IOException 出力エラー\r
152      */\r
153     protected PmdXmlExporter putI18nName(I18nText text) throws IOException{\r
154         for(String lang639 : text.lang639CodeList()){\r
155             if(lang639.equals(I18nText.CODE639_PRIMARY)) continue;\r
156             String name = text.getText(lang639);\r
157             ind().put("<i18nName ");\r
158             putAttr("lang", lang639).put(' ');\r
159             putAttr("name", name);\r
160             put(" />");\r
161             putUnescapedComment(name);\r
162             ln();\r
163         }\r
164         return this;\r
165     }\r
166 \r
167     /**\r
168      * 番号付けされたID(IDREF)属性を出力する。\r
169      * @param attrName 属性名\r
170      * @param prefix IDプレフィクス\r
171      * @param num 番号\r
172      * @return this本体\r
173      * @throws IOException 出力エラー\r
174      */\r
175     protected PmdXmlExporter putNumberedIdAttr(CharSequence attrName,\r
176                                                  CharSequence prefix,\r
177                                                  int num )\r
178             throws IOException{\r
179         put(attrName).put("=\"");\r
180         put(prefix).put(num);\r
181         put('"');\r
182         return this;\r
183     }\r
184 \r
185     /**\r
186      * 番号付けされたID(IDREF)属性を出力する。\r
187      * @param attrName 属性名\r
188      * @param prefix IDプレフィクス\r
189      * @param numbered 番号付けされたオブジェクト\r
190      * @return this本体\r
191      * @throws IOException 出力エラー\r
192      */\r
193     protected PmdXmlExporter putNumberedIdAttr(CharSequence attrName,\r
194                                                  CharSequence prefix,\r
195                                                  SerialNumbered numbered )\r
196             throws IOException{\r
197         putNumberedIdAttr(attrName, prefix, numbered.getSerialNumber());\r
198         return this;\r
199     }\r
200 \r
201     /**\r
202      * 位置情報を出力する。\r
203      * @param position 位置情報\r
204      * @return this本体\r
205      * @throws IOException 出力エラー\r
206      */\r
207     protected PmdXmlExporter putPosition(Pos3d position) throws IOException{\r
208         put("<position ");\r
209         putFloatAttr("x", position.getXPos()).put(' ');\r
210         putFloatAttr("y", position.getYPos()).put(' ');\r
211         putFloatAttr("z", position.getZPos()).put(' ');\r
212         put("/>");\r
213         return this;\r
214     }\r
215 \r
216     /**\r
217      * 姿勢情報(ラジアン)を出力する。\r
218      * @param rotation 姿勢情報\r
219      * @return this本体\r
220      * @throws IOException 出力エラー\r
221      */\r
222     protected PmdXmlExporter putRadRotation(Rad3d rotation)\r
223             throws IOException{\r
224         put("<radRotation ");\r
225         putFloatAttr("xRad", rotation.getXRad()).put(' ');\r
226         putFloatAttr("yRad", rotation.getYRad()).put(' ');\r
227         putFloatAttr("zRad", rotation.getZRad()).put(' ');\r
228         put("/>");\r
229         return this;\r
230     }\r
231 \r
232     /**\r
233      * 多言語識別名属性のローカルな名前をコメント出力する。\r
234      * @param name 多言語識別名\r
235      * @return this本体\r
236      * @throws IOException 出力エラー\r
237      */\r
238     protected PmdXmlExporter putLocalNameComment(I18nText name)\r
239             throws IOException{\r
240         String localName = name.getText();\r
241         ind().putLineComment(localName);\r
242         return this;\r
243     }\r
244 \r
245     /**\r
246      * 多言語識別名属性のプライマリな名前を出力する。\r
247      * @param attrName 属性名\r
248      * @param name 多言語識別名\r
249      * @return this本体\r
250      * @throws IOException 出力エラー\r
251      */\r
252     protected PmdXmlExporter putPrimaryNameAttr(CharSequence attrName,\r
253                                                    I18nText name)\r
254             throws IOException{\r
255         String primaryName = name.getPrimaryText();\r
256         putAttr(attrName, primaryName);\r
257         return this;\r
258     }\r
259 \r
260     /**\r
261      * PMDモデルデータをXML形式で出力する。\r
262      * @param model PMDモデルデータ\r
263      * @throws IOException 出力エラー\r
264      */\r
265     public void putPmdModel(PmdModel model) throws IOException{\r
266         ind().put("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>").ln(2);\r
267 \r
268         ind().putBlockComment(TOP_COMMENT).ln(2);\r
269 \r
270         ind().put("<!DOCTYPE pmdModel").ln();\r
271         ind().put(" SYSTEM \"")\r
272              .put(PmdXmlResources.DTD_PMDXML)\r
273              .put("\" >")\r
274              .ln(3);\r
275 \r
276         I18nText modelName = model.getModelName();\r
277         ind().putLocalNameComment(modelName).ln();\r
278         ind().put("<pmdModel").ln();\r
279         pushNest();\r
280         ind().putAttr("xmlns", PmdXmlResources.NS_PMDXML).ln();\r
281         ind().putAttr("xmlns:xsi", XmlResourceResolver.NS_XSD).ln();\r
282         ind().putAttr("xsi:schemaLocation", SCHEMA_LOCATION).ln();\r
283         ind().putAttr("schemaVersion", PmdXmlResources.VER_PMDXML).ln(2);\r
284         ind().putPrimaryNameAttr("name", modelName).ln();\r
285         popNest();\r
286         put(">").ln(2);\r
287 \r
288         putModelInfo(model).flush();\r
289         putMetaInfo(model).flush();\r
290         putMaterialList(model).flush();\r
291         putToonMap(model).flush();\r
292         putBoneList(model).flush();\r
293         putBoneGroupList(model).flush();\r
294         putIKChainList(model).flush();\r
295         putMorphList(model).flush();\r
296         putRigidList(model).flush();\r
297         putRigidGroupList(model).flush();\r
298         putJointList(model).flush();\r
299         putSurfaceGroupList(model).flush();\r
300         putVertexList(model).flush();\r
301 \r
302         ind().put("</pmdModel>").ln(2);\r
303         ind().put("<!-- EOF -->").ln();\r
304 \r
305         return;\r
306     }\r
307 \r
308     /**\r
309      * モデル基本情報を出力する。\r
310      * @param model モデル情報\r
311      * @return this本体\r
312      * @throws IOException 出力エラー\r
313      */\r
314     private PmdXmlExporter putModelInfo(PmdModel model)\r
315             throws IOException{\r
316         I18nText modelName = model.getModelName();\r
317         putI18nName(modelName);\r
318         ln();\r
319 \r
320         I18nText description = model.getDescription();\r
321         for(String lang639 : description.lang639CodeList()){\r
322             String descText = description.getText(lang639);\r
323             putDescription(lang639, descText);\r
324             ln();\r
325         }\r
326 \r
327         return this;\r
328     }\r
329 \r
330     /**\r
331      * モデル詳細テキストを出力する。\r
332      * @param lang639 言語コード\r
333      * @param content 詳細内容\r
334      * @return this本体\r
335      * @throws IOException 出力エラー\r
336      */\r
337     private PmdXmlExporter putDescription(CharSequence lang639,\r
338                                               CharSequence content)\r
339             throws IOException{\r
340         String text = content.toString();\r
341         text = text.replace(CRLF, LF);\r
342         text = text.replace(CR,   LF);\r
343 \r
344         ind().put("<description");\r
345         if( ! I18nText.CODE639_PRIMARY.equals(lang639) ){\r
346             put(" ");\r
347             putAttr("lang", lang639);\r
348         }\r
349         put(">").ln();\r
350 \r
351         putBRedContent(text);\r
352 \r
353         ln();\r
354         ind().put("</description>").ln();\r
355 \r
356         if( ! hasOnlyBasicLatin(text) && isBasicLatinOnlyOut() ){\r
357             putBlockComment(text);\r
358         }\r
359 \r
360         return this;\r
361     }\r
362 \r
363     /**\r
364      * break要素を含む要素内容を出力する。\r
365      * 必要に応じてXML定義済み実体文字が割り振られた文字、\r
366      * コントロールコード、および非BasicLatin文字がエスケープされる。\r
367      * \nはbrタグに変換される。\r
368      * @param content 内容\r
369      * @return this本体\r
370      * @throws IOException 出力エラー\r
371      */\r
372     protected BasicXmlExporter putBRedContent(CharSequence content)\r
373             throws IOException{\r
374         int length = content.length();\r
375 \r
376         for(int pos = 0; pos < length; pos++){\r
377             char ch = content.charAt(pos);\r
378             if(ch == '\n'){\r
379                 put("<br/>").ln();\r
380             }else if(Character.isISOControl(ch)){\r
381                 putCharRef2Hex(ch);\r
382             }else if( ! isBasicLatin(ch) && isBasicLatinOnlyOut()){\r
383                 putCharRef4Hex(ch);\r
384             }else{\r
385                 switch(ch){\r
386                 case '&':  put("&amp;");  break;\r
387                 case '<':  put("&lt;");   break;\r
388                 case '>':  put("&gt;");   break;\r
389                 case '"':  put("&quot;"); break;\r
390                 case '\'': put("&apos;"); break;\r
391                 default:   put(ch);       break;\r
392                 }\r
393             }\r
394         }\r
395 \r
396         return this;\r
397     }\r
398 \r
399     /**\r
400      * 各種メタ情報を出力する。\r
401      * @param model モデルデータ\r
402      * @return this本体\r
403      * @throws IOException 出力エラー\r
404      */\r
405     private PmdXmlExporter putMetaInfo(PmdModel model) throws IOException{\r
406         ind().put("<license>").ln();\r
407         ind().put("</license>").ln(2);\r
408 \r
409         ind().put("<credits>").ln();\r
410         ind().put("</credits>").ln(2);\r
411 \r
412         ind().put("<meta ");\r
413         putAttr("name", "generator").put(' ').putAttr("content", GENERATOR);\r
414         put(" />").ln();\r
415         ind().put("<meta ");\r
416         putAttr("name", "siteURL").put(' ').putAttr("content", "");\r
417         put(" />").ln();\r
418         ind().put("<meta ");\r
419         putAttr("name", "imageURL").put(' ').putAttr("content", "");\r
420         put(" />").ln(2);\r
421 \r
422         return this;\r
423     }\r
424 \r
425     /**\r
426      * マテリアル素材一覧を出力する。\r
427      * @param model モデルデータ\r
428      * @return this本体\r
429      * @throws IOException 出力エラー\r
430      */\r
431     private PmdXmlExporter putMaterialList(PmdModel model)\r
432             throws IOException{\r
433         ind().put("<materialList>").ln(2);\r
434         pushNest();\r
435 \r
436         int ct = 0;\r
437         for(Material material : model.getMaterialList()){\r
438             putMaterial(material, ct++);\r
439         }\r
440 \r
441         popNest();\r
442         ind().put("</materialList>").ln(2);\r
443 \r
444         return this;\r
445     }\r
446 \r
447     /**\r
448      * マテリアル素材情報を出力する。\r
449      * @param material マテリアル素材\r
450      * @param no マテリアル通し番号\r
451      * @return this本体\r
452      * @throws IOException 出力エラー\r
453      */\r
454     private PmdXmlExporter putMaterial(Material material, int no)\r
455             throws IOException{\r
456         String bool;\r
457         if(material.getEdgeAppearance()) bool = "true";\r
458         else                             bool = "false";\r
459         I18nText name = material.getMaterialName();\r
460         String primary = name.getPrimaryText();\r
461         String local = name.getText();\r
462 \r
463         if(local != null && local.length() > 0){\r
464             ind().putLineComment(local).ln();\r
465         }\r
466         ind().put("<material ");\r
467         if(primary != null && primary.length() > 0){\r
468             putAttr("name", primary).put(' ');\r
469         }\r
470 \r
471         putAttr("showEdge", bool);\r
472         put(" ");\r
473         putNumberedIdAttr("surfaceGroupIdRef", PFX_SURFACEGROUP, no);\r
474         put('>').ln();\r
475         pushNest();\r
476 \r
477         putI18nName(name);\r
478 \r
479         float[] rgba = new float[4];\r
480 \r
481         Color diffuse = material.getDiffuseColor();\r
482         diffuse.getRGBComponents(rgba);\r
483         ind().put("<diffuse ");\r
484         putFloatAttr("r", rgba[0]).put(' ');\r
485         putFloatAttr("g", rgba[1]).put(' ');\r
486         putFloatAttr("b", rgba[2]).put(' ');\r
487         putFloatAttr("alpha", rgba[3]).put(' ');\r
488         put("/>").ln();\r
489 \r
490         Color specular = material.getSpecularColor();\r
491         specular.getRGBComponents(rgba);\r
492         float shininess = material.getShininess();\r
493         ind().put("<specular ");\r
494         putFloatAttr("r", rgba[0]).put(' ');\r
495         putFloatAttr("g", rgba[1]).put(' ');\r
496         putFloatAttr("b", rgba[2]).put(' ');\r
497         putFloatAttr("shininess", shininess).put(' ');\r
498         put("/>").ln();\r
499 \r
500         Color ambient = material.getAmbientColor();\r
501         ambient.getRGBComponents(rgba);\r
502         ind().put("<ambient ");\r
503         putFloatAttr("r", rgba[0]).put(' ');\r
504         putFloatAttr("g", rgba[1]).put(' ');\r
505         putFloatAttr("b", rgba[2]).put(' ');\r
506         put("/>").ln();\r
507 \r
508         ShadeInfo shade = material.getShadeInfo();\r
509         String textureFileName = shade.getTextureFileName();\r
510         String spheremapFileName = shade.getSpheremapFileName();\r
511 \r
512         if(shade.isValidToonIndex()){\r
513             ind().put("<toon ");\r
514             int toonIdx = shade.getToonIndex();\r
515             putNumberedIdAttr("toonFileIdRef", PFX_TOONFILE, toonIdx);\r
516             put(" />");\r
517             String toonFileName = shade.getToonFileName();\r
518             if(toonFileName != null && toonFileName.length() > 0){\r
519                 put(' ').putLineComment(toonFileName);\r
520             }\r
521             ln();\r
522         }\r
523 \r
524         if(textureFileName != null && textureFileName.length() > 0){\r
525             ind().put("<textureFile ");\r
526             putAttr("winFileName", textureFileName);\r
527             put(" />").ln();\r
528         }\r
529 \r
530         if(spheremapFileName != null && spheremapFileName.length() > 0){\r
531             ind().put("<spheremapFile ");\r
532             putAttr("winFileName", spheremapFileName);\r
533             put(" />").ln();\r
534         }\r
535 \r
536         popNest();\r
537         ind().put("</material>").ln(2);\r
538 \r
539         return this;\r
540     }\r
541 \r
542     /**\r
543      * トゥーンファイルマッピング情報を出力する。\r
544      * @param model モデルデータ\r
545      * @return this本体\r
546      * @throws IOException 出力エラー\r
547      */\r
548     private PmdXmlExporter putToonMap(PmdModel model)\r
549             throws IOException{\r
550         ind().put("<toonMap>").ln();\r
551         pushNest();\r
552 \r
553         ToonMap map = model.getToonMap();\r
554         for(int index = 0; index <= 9; index++){\r
555             ind().putToon(map, index).ln();\r
556         }\r
557 \r
558         popNest();\r
559         ind().put("</toonMap>").ln(2);\r
560         return this;\r
561     }\r
562 \r
563     /**\r
564      * 個別のトゥーンファイル情報を出力する。\r
565      * @param map トゥーンマップ\r
566      * @param index インデックス値\r
567      * @return this本体\r
568      * @throws IOException 出力エラー\r
569      */\r
570     private PmdXmlExporter putToon(ToonMap map, int index)\r
571             throws IOException{\r
572         put("<toonDef ");\r
573         putNumberedIdAttr("toonFileId", PFX_TOONFILE, index).put(' ');\r
574         putIntAttr("index", index).put(' ');\r
575         String toonFile = map.getIndexedToon(index);\r
576         putAttr("winFileName", toonFile);\r
577         put(" />");\r
578         putUnescapedComment(toonFile);\r
579         return this;\r
580     }\r
581 \r
582     /**\r
583      * サーフェイスグループリストを出力する。\r
584      * @param model モデルデータ\r
585      * @return this本体\r
586      * @throws IOException 出力エラー\r
587      */\r
588     private PmdXmlExporter putSurfaceGroupList(PmdModel model)\r
589             throws IOException{\r
590         ind().put("<surfaceGroupList>").ln(2);\r
591         pushNest();\r
592 \r
593         int ct = 0;\r
594         for(Material material : model.getMaterialList()){\r
595             List<Surface> surfaceList = material.getSurfaceList();\r
596             putSurfaceList(surfaceList, ct++);\r
597         }\r
598 \r
599         popNest();\r
600         ind().put("</surfaceGroupList>").ln(2);\r
601 \r
602         return this;\r
603     }\r
604 \r
605     /**\r
606      * 個別のサーフェイスグループを出力する。\r
607      * @param surfaceList サーフェイスのリスト\r
608      * @param index グループインデックス\r
609      * @return this本体\r
610      * @throws IOException 出力エラー\r
611      */\r
612     private PmdXmlExporter putSurfaceList(List<Surface> surfaceList,\r
613                                               int index)\r
614             throws IOException{\r
615         ind().put("<surfaceGroup ");\r
616         putNumberedIdAttr("surfaceGroupId", PFX_SURFACEGROUP, index);\r
617         put(">").ln();\r
618         pushNest();\r
619 \r
620         for(Surface surface : surfaceList){\r
621             putSurface(surface);\r
622         }\r
623 \r
624         popNest();\r
625         ind().put("</surfaceGroup>").ln(2);\r
626 \r
627         return this;\r
628     }\r
629 \r
630     /**\r
631      * 個別のサーフェイスを出力する。\r
632      * @param surface サーフェイス\r
633      * @return this本体\r
634      * @throws IOException 出力エラー\r
635      */\r
636     private PmdXmlExporter putSurface(Surface surface)\r
637             throws IOException{\r
638         ind().put("<surface ");\r
639 \r
640         Vertex vertex1 = surface.getVertex1();\r
641         Vertex vertex2 = surface.getVertex2();\r
642         Vertex vertex3 = surface.getVertex3();\r
643 \r
644         putNumberedIdAttr("vtxIdRef1", PFX_VERTEX, vertex1).put(' ');\r
645         putNumberedIdAttr("vtxIdRef2", PFX_VERTEX, vertex2).put(' ');\r
646         putNumberedIdAttr("vtxIdRef3", PFX_VERTEX, vertex3).put(' ');\r
647 \r
648         put("/>").ln();\r
649         return this;\r
650     }\r
651 \r
652     /**\r
653      * 頂点リストを出力する。\r
654      * @param model モデルデータ\r
655      * @return this本体\r
656      * @throws IOException 出力エラー\r
657      */\r
658     private PmdXmlExporter putVertexList(PmdModel model)\r
659             throws IOException{\r
660         ind().put("<vertexList>").ln(2);\r
661         pushNest();\r
662 \r
663         for(Vertex vertex : model.getVertexList()){\r
664             putVertex(vertex);\r
665         }\r
666 \r
667         popNest();\r
668         ind().put("</vertexList>").ln(2);\r
669 \r
670         return this;\r
671     }\r
672 \r
673     /**\r
674      * 個別の頂点情報を出力する。\r
675      * @param vertex 頂点\r
676      * @return this本体\r
677      * @throws IOException 出力エラー\r
678      */\r
679     private PmdXmlExporter putVertex(Vertex vertex)\r
680             throws IOException{\r
681         String bool;\r
682         if(vertex.getEdgeAppearance()) bool = "true";\r
683         else                           bool = "false";\r
684 \r
685         ind().put("<vertex ");\r
686         putNumberedIdAttr("vtxId", PFX_VERTEX, vertex).put(' ');\r
687         putAttr("showEdge", bool);\r
688         put(">").ln();\r
689         pushNest();\r
690 \r
691         Pos3d position = vertex.getPosition();\r
692         ind().putPosition(position).ln();\r
693 \r
694         Vec3d normal = vertex.getNormal();\r
695         ind().put("<normal ");\r
696         putFloatAttr("x", normal.getXVal()).put(' ');\r
697         putFloatAttr("y", normal.getYVal()).put(' ');\r
698         putFloatAttr("z", normal.getZVal()).put(' ');\r
699         put("/>").ln();\r
700 \r
701         Pos2d uvPos = vertex.getUVPosition();\r
702         ind().put("<uvMap ");\r
703         putFloatAttr("u", uvPos.getXPos()).put(' ');\r
704         putFloatAttr("v", uvPos.getYPos()).put(' ');\r
705         put("/>").ln();\r
706 \r
707         BoneInfo boneA = vertex.getBoneA();\r
708         BoneInfo boneB = vertex.getBoneB();\r
709         int weight = vertex.getWeightA();\r
710         ind().put("<skinning ");\r
711         putNumberedIdAttr("boneIdRef1", PFX_BONE, boneA).put(' ');\r
712         putNumberedIdAttr("boneIdRef2", PFX_BONE, boneB).put(' ');\r
713         putIntAttr("weightBalance", weight).put(' ');\r
714         put("/>").ln();\r
715 \r
716         popNest();\r
717         ind().put("</vertex>").ln(2);\r
718 \r
719         return this;\r
720     }\r
721 \r
722     /**\r
723      * ボーンリストを出力する。\r
724      * @param model モデルデータ\r
725      * @return this本体\r
726      * @throws IOException 出力エラー\r
727      */\r
728     private PmdXmlExporter putBoneList(PmdModel model)\r
729             throws IOException{\r
730         ind().put("<boneList>").ln(2);\r
731         pushNest();\r
732 \r
733         putBlockComment(BONETYPE_COMMENT).ln();\r
734 \r
735         for(BoneInfo bone : model.getBoneList()){\r
736             putBone(bone);\r
737         }\r
738 \r
739         popNest();\r
740         ind().put("</boneList>").ln(2);\r
741 \r
742         return this;\r
743     }\r
744 \r
745     /**\r
746      * 個別のボーン情報を出力する。\r
747      * @param bone ボーン情報\r
748      * @return this本体\r
749      * @throws IOException 出力エラー\r
750      */\r
751     private PmdXmlExporter putBone(BoneInfo bone)\r
752             throws IOException{\r
753         I18nText i18nName = bone.getBoneName();\r
754         BoneType type = bone.getBoneType();\r
755 \r
756         putLocalNameComment(i18nName).putLineComment(type.getGuiName()).ln();\r
757         ind().put("<bone ");\r
758         putPrimaryNameAttr("name", i18nName).put(' ');\r
759         putNumberedIdAttr("boneId", PFX_BONE, bone).put(' ');\r
760         putAttr("type", type.name());\r
761         put(">").ln();\r
762         pushNest();\r
763 \r
764         putI18nName(i18nName);\r
765 \r
766         Pos3d position = bone.getPosition();\r
767         ind().putPosition(position).ln();\r
768 \r
769         BoneInfo ikBone = bone.getIKBone();\r
770         if(bone.getBoneType() == BoneType.LINKEDROT){\r
771             ind().put("<rotationRatio ");\r
772             putIntAttr("ratio", bone.getRotationRatio());\r
773             put(" />").ln();\r
774         }else if(ikBone != null){\r
775             ind().put("<ikBone ");\r
776             putNumberedIdAttr("boneIdRef", PFX_BONE, ikBone);\r
777             put(" /> ");\r
778             String ikBoneName = "Ref:" + ikBone.getBoneName().getText();\r
779             putLineComment(ikBoneName);\r
780             ln();\r
781         }\r
782 \r
783         StringBuilder chainComment = new StringBuilder();\r
784         ind().put("<boneChain");\r
785         BoneInfo prev = bone.getPrevBone();\r
786         BoneInfo next = bone.getNextBone();\r
787         if(prev != null){\r
788             put(' ');\r
789             putNumberedIdAttr("prevBoneIdRef", PFX_BONE, prev);\r
790             chainComment.append('[')\r
791                         .append(prev.getBoneName().getPrimaryText())\r
792                         .append(']')\r
793                         .append("=> #");\r
794         }\r
795         if(next != null){\r
796             put(' ');\r
797             putNumberedIdAttr("nextBoneIdRef", PFX_BONE, next);\r
798             if(chainComment.length() <= 0) chainComment.append("#");\r
799             chainComment.append(" =>")\r
800                         .append('[')\r
801                         .append(next.getBoneName().getPrimaryText())\r
802                         .append(']');\r
803         }\r
804         put(" />").ln();\r
805         ind().putLineComment(chainComment).ln();\r
806 \r
807         popNest();\r
808         ind().put("</bone>").ln(2);\r
809 \r
810         return this;\r
811     }\r
812 \r
813     /**\r
814      * ボーングループリストを出力する。\r
815      * @param model モデルデータ\r
816      * @return this本体\r
817      * @throws IOException 出力エラー\r
818      */\r
819     private PmdXmlExporter putBoneGroupList(PmdModel model)\r
820             throws IOException{\r
821         ind().put("<boneGroupList>").ln(2);\r
822         pushNest();\r
823 \r
824         for(BoneGroup group : model.getBoneGroupList()){\r
825             if(group.isDefaultBoneGroup()) continue;\r
826             putBoneGroup(group);\r
827         }\r
828 \r
829         popNest();\r
830         ind().put("</boneGroupList>").ln(2);\r
831 \r
832         return this;\r
833     }\r
834 \r
835     /**\r
836      * 個別のボーングループ情報を出力する。\r
837      * @param group ボーングループ情報\r
838      * @return this本体\r
839      * @throws IOException 出力エラー\r
840      */\r
841     private PmdXmlExporter putBoneGroup(BoneGroup group)\r
842             throws IOException{\r
843         I18nText i18nName = group.getGroupName();\r
844 \r
845         putLocalNameComment(i18nName).ln();\r
846         ind().put("<boneGroup ");\r
847         putPrimaryNameAttr("name", i18nName);\r
848         put(">").ln();\r
849         pushNest();\r
850 \r
851         putI18nName(i18nName);\r
852 \r
853         for(BoneInfo bone : group){\r
854             ind().put("<boneGroupMember ");\r
855             putNumberedIdAttr("boneIdRef", PFX_BONE, bone);\r
856             put(" /> ");\r
857             String boneName = "Ref:" + bone.getBoneName().getText();\r
858             putLineComment(boneName).ln();\r
859         }\r
860 \r
861         popNest();\r
862         ind().put("</boneGroup>").ln(2);\r
863 \r
864         return this;\r
865     }\r
866 \r
867     /**\r
868      * IKチェーンリストを出力する。\r
869      * @param model モデルデータ\r
870      * @return this本体\r
871      * @throws IOException 出力エラー\r
872      */\r
873     private PmdXmlExporter putIKChainList(PmdModel model)\r
874             throws IOException{\r
875         ind().put("<ikChainList>").ln(2);\r
876         pushNest();\r
877 \r
878         for(IKChain chain : model.getIKChainList()){\r
879             putIKChain(chain);\r
880         }\r
881 \r
882         popNest();\r
883         ind().put("</ikChainList>").ln(2);\r
884 \r
885         return this;\r
886     }\r
887 \r
888     /**\r
889      * 個別のIKチェーン情報を出力する。\r
890      * @param chain チェーン情報\r
891      * @return this本体\r
892      * @throws IOException 出力エラー\r
893      */\r
894     private PmdXmlExporter putIKChain(IKChain chain)\r
895             throws IOException{\r
896         int depth = chain.getIKDepth();\r
897         float weight = chain.getIKWeight();\r
898         BoneInfo ikBone = chain.getIkBone();\r
899 \r
900         ind().putLineComment("Ref:" + ikBone.getBoneName().getText()).ln();\r
901         ind().put("<ikChain ");\r
902         putNumberedIdAttr("ikBoneIdRef", PFX_BONE, ikBone).put(' ');\r
903         putIntAttr("recursiveDepth", depth).put(' ');\r
904         putFloatAttr("weight", weight);\r
905         put("> ").ln();\r
906         pushNest();\r
907 \r
908         for(BoneInfo bone : chain){\r
909             ind().put("<chainOrder ");\r
910             putNumberedIdAttr("boneIdRef", PFX_BONE, bone);\r
911             put(" /> ");\r
912             putLineComment("Ref:" + bone.getBoneName().getText());\r
913             ln();\r
914         }\r
915 \r
916         popNest();\r
917         ind().put("</ikChain>").ln(2);\r
918 \r
919         return this;\r
920     }\r
921 \r
922     /**\r
923      * モーフリストを出力する。\r
924      * @param model モデルデータ\r
925      * @return this本体\r
926      * @throws IOException 出力エラー\r
927      */\r
928     private PmdXmlExporter putMorphList(PmdModel model)\r
929             throws IOException{\r
930         ind().put("<morphList>").ln(2);\r
931         pushNest();\r
932 \r
933         putBlockComment(MORPHTYPE_COMMENT).ln();\r
934 \r
935         Map<MorphType, List<MorphPart>> morphMap = model.getMorphMap();\r
936         for(MorphType type : MorphType.values()){\r
937             if(type == MorphType.BASE) continue;\r
938             List<MorphPart> partList = morphMap.get(type);\r
939             if(partList == null) continue;\r
940             for(MorphPart part : partList){\r
941                 putMorphPart(part);\r
942             }\r
943         }\r
944 \r
945         popNest();\r
946         ind().put("</morphList>").ln(2);\r
947 \r
948         return this;\r
949     }\r
950 \r
951     /**\r
952      * 個別のモーフ情報を出力する。\r
953      * @param part モーフ情報\r
954      * @return this本体\r
955      * @throws IOException 出力エラー\r
956      */\r
957     private PmdXmlExporter putMorphPart(MorphPart part)\r
958             throws IOException{\r
959         I18nText i18nName = part.getMorphName();\r
960         String primary = i18nName.getPrimaryText();\r
961 \r
962         ind().put("<morph ");\r
963         putAttr("name", primary).put(' ');\r
964         putAttr("type", part.getMorphType().name());\r
965         put(">");\r
966         putUnescapedComment(primary);\r
967         ln();\r
968         pushNest();\r
969 \r
970         putI18nName(i18nName);\r
971 \r
972         for(MorphVertex mvertex : part){\r
973             Pos3d offset = mvertex.getOffset();\r
974             Vertex base = mvertex.getBaseVertex();\r
975 \r
976             ind().put("<morphVertex ");\r
977             putNumberedIdAttr("vtxIdRef", PFX_VERTEX, base).put(' ');\r
978             putFloatAttr("xOff", offset.getXPos()).put(' ');\r
979             putFloatAttr("yOff", offset.getYPos()).put(' ');\r
980             putFloatAttr("zOff", offset.getZPos()).put(' ');\r
981             put("/>");\r
982             ln();\r
983         }\r
984 \r
985         popNest();\r
986         ind().put("</morph>").ln(2);\r
987 \r
988         return this;\r
989     }\r
990 \r
991     /**\r
992      * 剛体リストを出力する。\r
993      * @param model モデルデータ\r
994      * @return this本体\r
995      * @throws IOException 出力エラー\r
996      */\r
997     private PmdXmlExporter putRigidList(PmdModel model)\r
998             throws IOException{\r
999         ind().put("<rigidList>").ln(2);\r
1000         pushNest();\r
1001 \r
1002         putBlockComment(RIGIDBEHAVIOR_COMMENT).ln();\r
1003 \r
1004         for(RigidInfo rigid : model.getRigidList()){\r
1005             putRigid(rigid);\r
1006         }\r
1007 \r
1008         popNest();\r
1009         ind().put("</rigidList>").ln(2);\r
1010 \r
1011         return this;\r
1012     }\r
1013 \r
1014     /**\r
1015      * 個別の剛体情報を出力する。\r
1016      * @param rigid 剛体情報\r
1017      * @return this本体\r
1018      * @throws IOException 出力エラー\r
1019      */\r
1020     private PmdXmlExporter putRigid(RigidInfo rigid)\r
1021             throws IOException{\r
1022         BoneInfo linkedBone = rigid.getLinkedBone();\r
1023         I18nText i18nName = rigid.getRigidName();\r
1024         String primary = i18nName.getPrimaryText();\r
1025 \r
1026         putLocalNameComment(i18nName).ln();\r
1027         ind().put("<rigid ");\r
1028         putAttr("name", primary).put(' ');\r
1029         putNumberedIdAttr("rigidId", PFX_RIGID, rigid).put(' ');\r
1030         putAttr("behavior", rigid.getBehaviorType().name());\r
1031         put(">").ln();\r
1032         pushNest();\r
1033 \r
1034         putI18nName(i18nName);\r
1035 \r
1036         ind().put("<linkedBone ");\r
1037         putNumberedIdAttr("boneIdRef", PFX_BONE, linkedBone);\r
1038         put(" /> ");\r
1039         putLineComment("Ref:" + linkedBone.getBoneName().getText());\r
1040         ln(2);\r
1041 \r
1042         RigidShape shape = rigid.getRigidShape();\r
1043         putRigidShape(shape);\r
1044 \r
1045         Pos3d position = rigid.getPosition();\r
1046         ind().putPosition(position).ln();\r
1047 \r
1048         Rad3d rotation = rigid.getRotation();\r
1049         ind().putRadRotation(rotation).ln();\r
1050 \r
1051         DynamicsInfo dynamics = rigid.getDynamicsInfo();\r
1052         putDynamics(dynamics).ln();\r
1053 \r
1054         for(RigidGroup group : rigid.getThroughGroupColl()){\r
1055             ind().put("<throughRigidGroup ");\r
1056             putNumberedIdAttr("rigidGroupIdRef",\r
1057                               PFX_RIGIDGROUP,\r
1058                               group.getSerialNumber() + 1).put(' ');\r
1059             put(" />").ln();\r
1060         }\r
1061 \r
1062         popNest();\r
1063         ind().put("</rigid>").ln(2);\r
1064 \r
1065         return this;\r
1066     }\r
1067 \r
1068     /**\r
1069      * 剛体形状を出力する。\r
1070      * @param shape 剛体形状\r
1071      * @return this本体\r
1072      * @throws IOException 出力エラー\r
1073      */\r
1074     private PmdXmlExporter putRigidShape(RigidShape shape)\r
1075             throws IOException{\r
1076         RigidShapeType type = shape.getShapeType();\r
1077 \r
1078         switch(type){\r
1079         case BOX:\r
1080             ind().put("<rigidShapeBox ");\r
1081             putFloatAttr("width", shape.getWidth()).put(' ');\r
1082             putFloatAttr("height", shape.getHeight()).put(' ');\r
1083             putFloatAttr("depth", shape.getDepth()).put(' ');\r
1084             break;\r
1085         case SPHERE:\r
1086             ind().put("<rigidShapeSphere ");\r
1087             putFloatAttr("radius", shape.getRadius()).put(' ');\r
1088             break;\r
1089         case CAPSULE:\r
1090             ind().put("<rigidShapeCapsule ");\r
1091             putFloatAttr("height", shape.getHeight()).put(' ');\r
1092             putFloatAttr("radius", shape.getRadius()).put(' ');\r
1093             break;\r
1094         default:\r
1095             assert false;\r
1096             throw new AssertionError();\r
1097         }\r
1098 \r
1099         put("/>").ln();\r
1100 \r
1101         return this;\r
1102     }\r
1103 \r
1104     /**\r
1105      * 力学設定を出力する。\r
1106      * @param dynamics 力学設定\r
1107      * @return this本体\r
1108      * @throws IOException 出力エラー\r
1109      */\r
1110     private PmdXmlExporter putDynamics(DynamicsInfo dynamics)\r
1111             throws IOException{\r
1112         ind().put("<dynamics").ln();\r
1113         pushNest();\r
1114         ind().putFloatAttr("mass", dynamics.getMass()).ln();\r
1115         ind().putFloatAttr("dampingPosition",\r
1116                 dynamics.getDampingPosition()).ln();\r
1117         ind().putFloatAttr("dampingRotation",\r
1118                 dynamics.getDampingRotation()).ln();\r
1119         ind().putFloatAttr("restitution", dynamics.getRestitution()).ln();\r
1120         ind().putFloatAttr("friction", dynamics.getFriction()).ln();\r
1121         popNest();\r
1122         ind().put("/>").ln();\r
1123 \r
1124         return this;\r
1125     }\r
1126 \r
1127     /**\r
1128      * 剛体グループリストを出力する。\r
1129      * @param model モデルデータ\r
1130      * @return this本体\r
1131      * @throws IOException 出力エラー\r
1132      */\r
1133     private PmdXmlExporter putRigidGroupList(PmdModel model)\r
1134             throws IOException{\r
1135         ind().put("<rigidGroupList>").ln(2);\r
1136         pushNest();\r
1137 \r
1138         for(RigidGroup group : model.getRigidGroupList()){\r
1139             ind().put("<rigidGroup ");\r
1140             putNumberedIdAttr("rigidGroupId",\r
1141                               PFX_RIGIDGROUP,\r
1142                               group.getSerialNumber() + 1);\r
1143             List<RigidInfo> rigidList = group.getRigidList();\r
1144             if(rigidList.size() <= 0){\r
1145                 put(" />").ln(2);\r
1146                 continue;\r
1147             }\r
1148             put(">").ln();\r
1149             pushNest();\r
1150 \r
1151             for(RigidInfo rigid : rigidList){\r
1152                 ind().put("<rigidGroupMember ");\r
1153                 putNumberedIdAttr("rigidIdRef", PFX_RIGID, rigid).put(' ');\r
1154                 put("/>");\r
1155                 put(' ');\r
1156                 putLineComment("Ref:" + rigid.getRigidName().getText());\r
1157                 ln();\r
1158             }\r
1159 \r
1160             popNest();\r
1161             ind().put("</rigidGroup>").ln(2);\r
1162         }\r
1163 \r
1164         popNest();\r
1165         ind().put("</rigidGroupList>").ln(2);\r
1166 \r
1167         return this;\r
1168     }\r
1169 \r
1170     /**\r
1171      * ジョイントリストを出力する。\r
1172      * @param model モデルデータ\r
1173      * @return this本体\r
1174      * @throws IOException 出力エラー\r
1175      */\r
1176     private PmdXmlExporter putJointList(PmdModel model)\r
1177             throws IOException{\r
1178         ind().put("<jointList>").ln(2);\r
1179         pushNest();\r
1180 \r
1181         for(JointInfo joint : model.getJointList()){\r
1182             putJoint(joint);\r
1183         }\r
1184 \r
1185         popNest();\r
1186         ind().put("</jointList>").ln(2);\r
1187 \r
1188         return this;\r
1189     }\r
1190 \r
1191     /**\r
1192      * 個別のジョイント情報を出力する。\r
1193      * @param joint ジョイント情報\r
1194      * @return this本体\r
1195      * @throws IOException 出力エラー\r
1196      */\r
1197     private PmdXmlExporter putJoint(JointInfo joint)\r
1198             throws IOException{\r
1199         I18nText i18nName = joint.getJointName();\r
1200 \r
1201         putLocalNameComment(i18nName).ln();\r
1202         ind().put("<joint ");\r
1203         putPrimaryNameAttr("name", i18nName);\r
1204         put(">").ln();\r
1205         pushNest();\r
1206 \r
1207         putI18nName(i18nName);\r
1208 \r
1209         RigidInfo rigidA = joint.getRigidA();\r
1210         RigidInfo rigidB = joint.getRigidB();\r
1211         ind().put("<jointedRigidPair ");\r
1212         putNumberedIdAttr("rigidIdRef1", PFX_RIGID, rigidA).put(' ');\r
1213         putNumberedIdAttr("rigidIdRef2", PFX_RIGID, rigidB).put(' ');\r
1214         put("/>").ln();\r
1215         ind();\r
1216         putLineComment("[" + rigidA.getRigidName().getText() + "]"\r
1217                 + " <=> [" + rigidB.getRigidName().getText() + "]");\r
1218         ln(2);\r
1219 \r
1220         Pos3d position = joint.getPosition();\r
1221         ind().putPosition(position).ln();\r
1222 \r
1223         TripletRange posRange = joint.getPositionRange();\r
1224         ind().put("<limitPosition").ln();\r
1225         pushNest();\r
1226         ind();\r
1227         putFloatAttr("xFrom", posRange.getXFrom()).put(' ');\r
1228         putFloatAttr("xTo",   posRange.getXTo()).ln();\r
1229         ind();\r
1230         putFloatAttr("yFrom", posRange.getYFrom()).put(' ');\r
1231         putFloatAttr("yTo",   posRange.getYTo()).ln();\r
1232         ind();\r
1233         putFloatAttr("zFrom", posRange.getZFrom()).put(' ');\r
1234         putFloatAttr("zTo",   posRange.getZTo()).ln();\r
1235         popNest();\r
1236         ind().put("/>").ln(2);\r
1237 \r
1238         Rad3d rotation = joint.getRotation();\r
1239         ind().putRadRotation(rotation).ln();\r
1240         TripletRange rotRange = joint.getRotationRange();\r
1241         ind().put("<limitRotation").ln();\r
1242         pushNest();\r
1243         ind();\r
1244         putFloatAttr("xFrom", rotRange.getXFrom()).put(' ');\r
1245         putFloatAttr("xTo",   rotRange.getXTo()).ln();\r
1246         ind();\r
1247         putFloatAttr("yFrom", rotRange.getYFrom()).put(' ');\r
1248         putFloatAttr("yTo",   rotRange.getYTo()).ln();\r
1249         ind();\r
1250         putFloatAttr("zFrom", rotRange.getZFrom()).put(' ');\r
1251         putFloatAttr("zTo",   rotRange.getZTo()).ln();\r
1252         popNest();\r
1253         ind().put("/>").ln(2);\r
1254 \r
1255         Pos3d elaPosition = joint.getElasticPosition();\r
1256         ind().put("<elasticPosition ");\r
1257         putFloatAttr("x", elaPosition.getXPos()).put(' ');\r
1258         putFloatAttr("y", elaPosition.getYPos()).put(' ');\r
1259         putFloatAttr("z", elaPosition.getZPos()).put(' ');\r
1260         put("/>").ln();\r
1261 \r
1262         Deg3d elaRotation = joint.getElasticRotation();\r
1263         ind().put("<elasticRotation ");\r
1264         putFloatAttr("xDeg", elaRotation.getXDeg()).put(' ');\r
1265         putFloatAttr("yDeg", elaRotation.getYDeg()).put(' ');\r
1266         putFloatAttr("zDeg", elaRotation.getZDeg()).put(' ');\r
1267         put("/>").ln(2);\r
1268 \r
1269         popNest();\r
1270         ind().put("</joint>").ln(2);\r
1271 \r
1272         return this;\r
1273     }\r
1274 \r
1275 }\r