OSDN Git Service

move UVertex impl.
[tdcgexplorer/tso2mqo.git] / MqoWriter.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Text;
5 using System.Xml;
6
7 namespace Tso2MqoGui
8 {
9     public enum MqoBoneMode
10     {
11         None,
12         RokDeBone,
13         Mikoto,
14     }
15
16     public class Pair<T, U>
17     {
18         public T First;
19         public U Second;
20
21         public Pair()
22         {
23         }
24
25         public Pair(T first, U second)
26         {
27             First = first;
28             Second = second;
29         }
30     }
31
32     public class MqoWriter : IDisposable
33     {
34         public TextWriter tw;
35         public string OutPath;
36         public string OutFile;
37         public MqoBoneMode BoneMode = MqoBoneMode.None;
38
39         public MqoWriter(string file)
40         {
41             FileStream fs = File.OpenWrite(file);
42             fs.SetLength(0);
43             tw = new StreamWriter(fs, Encoding.Default);
44             OutFile = file;
45             OutPath = Path.GetDirectoryName(file);
46         }
47
48         void IDisposable.Dispose()
49         {
50             Close();
51         }
52
53         public void Close()
54         {
55             if (tw != null)
56                 tw.Close();
57             tw = null;
58         }
59
60         string GetTextureFileName(TSOTex tex)
61         {
62             string filename = Path.GetFileName(tex.File.Trim('"'));
63             if (filename == "")
64                 filename = "none";
65             return filename;
66         }
67
68         string GetTexturePath(TSOTex tex)
69         {
70             return Path.Combine(OutPath, GetTextureFileName(tex));
71         }
72
73         public void CreateTextureFile(TSOTex tex)
74         {
75             string file = GetTexturePath(tex);
76             byte[] data = tex.data;
77
78             //TODO: .bmpのはずが.psdになってるものがある
79
80             using (FileStream fs = File.OpenWrite(file))
81             {
82                 BinaryWriter bw = new BinaryWriter(fs);
83
84                 switch (Path.GetExtension(file).ToUpper())
85                 {
86                     case ".TGA":
87                         bw.Write((byte)0);              // id
88                         bw.Write((byte)0);              // colormap
89                         bw.Write((byte)2);              // imagetype
90                         bw.Write((byte)0);              // unknown0
91                         bw.Write((byte)0);              // unknown1
92                         bw.Write((byte)0);              // unknown2
93                         bw.Write((byte)0);              // unknown3
94                         bw.Write((byte)0);              // unknown4
95                         bw.Write((short)0);             // width
96                         bw.Write((short)0);             // height
97                         bw.Write((short)tex.Width);     // width
98                         bw.Write((short)tex.Height);    // height
99                         bw.Write((byte)(tex.depth * 8));// depth
100                         bw.Write((byte)0);              // depth
101                         break;
102
103                     default:
104                         bw.Write((byte)'B');
105                         bw.Write((byte)'M');
106                         bw.Write((int)(54 + data.Length));
107                         bw.Write((int)0);
108                         bw.Write((int)54);
109                         bw.Write((int)40);
110                         bw.Write((int)tex.Width);
111                         bw.Write((int)tex.Height);
112                         bw.Write((short)1);
113                         bw.Write((short)(tex.Depth * 8));
114                         bw.Write((int)0);
115                         bw.Write((int)data.Length);
116                         bw.Write((int)0);
117                         bw.Write((int)0);
118                         bw.Write((int)0);
119                         bw.Write((int)0);
120                         break;
121                 }
122
123                 bw.Write(data, 0, data.Length);
124                 bw.Flush();
125             }
126         }
127
128         public void Write(TSOFile tso)
129         {
130             // ボーンを出す
131             bool mqx_enabled = BoneMode == MqoBoneMode.RokDeBone;
132
133             tw.WriteLine("Metasequoia Document");
134             tw.WriteLine("Format Text Ver 1.0");
135             tw.WriteLine("");
136             if (mqx_enabled)
137             {
138                 tw.WriteLine("IncludeXml \"{0}\"", Path.GetFileName(Path.ChangeExtension(OutFile, ".mqx")));
139                 tw.WriteLine("");
140             }
141             tw.WriteLine("Scene {");
142             tw.WriteLine("\tpos -7.0446 4.1793 1541.1764");
143             tw.WriteLine("\tlookat 11.8726 193.8590 0.4676");
144             tw.WriteLine("\thead 0.8564");
145             tw.WriteLine("\tpich 0.1708");
146             tw.WriteLine("\tortho 0");
147             tw.WriteLine("\tzoom2 31.8925");
148             tw.WriteLine("\tamb 0.250 0.250 0.250");
149             tw.WriteLine("}");
150
151             foreach (TSOTex tex in tso.textures)
152                 CreateTextureFile(tex);
153
154             tw.WriteLine("Material {0} {{", tso.materials.Length);
155
156             foreach (TSOMaterial mat in tso.materials)
157             {
158                 TSOTex tex = null;
159                 if (tso.texturemap.TryGetValue(mat.ColorTex, out tex))
160                 {
161                     tw.WriteLine(
162                         "\t\"{0}\" col(1.000 1.000 1.000 1.000) dif(0.800) amb(0.600) emi(0.000) spc(0.000) power(5.00) tex(\"{1}\")",
163                         mat.name, GetTextureFileName(tex));
164                 }
165                 else
166                 {
167                     tw.WriteLine(
168                         "\t\"{0}\" col(1.000 1.000 1.000 1.000) dif(0.800) amb(0.600) emi(0.000) spc(0.000) power(5.00))",
169                         mat.name);
170                 }
171             }
172
173             tw.WriteLine("}");
174
175             tso.UpdateNodesWorld();
176
177             MqoBone[] bones = new MqoBone[tso.nodes.Length];
178
179             foreach (TSONode node in tso.nodes)
180             {
181                 MqoBone bone = new MqoBone();
182                 bone.id = node.id;
183                 bone.name = node.ShortName;
184                 bone.tail = node.children.Count == 0;
185
186                 if (node.parent == null)
187                 {
188                     bone.pid = -1;
189                 }
190                 else
191                 {
192                     bone.pid = node.parent.id;
193                     bones[bone.pid].cids.Add(bone.id);
194                 }
195
196                 //根本
197                 bone.q = node.world.Translation;
198                 //先端
199                 if (! bone.tail)
200                     bone.p = node.children[0].world.Translation;
201                 else
202                     bone.p = node.world.Translation;
203
204                 bones[node.id] = bone;
205             }
206
207             MqoObjectGen.uid_enabled = mqx_enabled;
208             MqoObjectGen obj = new MqoObjectGen();
209
210             ushort object_id = 0;
211             foreach (TSOMesh mesh in tso.meshes)
212             {
213                 obj.id = ++object_id;
214                 obj.name = mesh.Name;
215                 obj.Update(mesh);
216                 obj.Write(tw);
217                 obj.AddWeits(bones);
218             }
219
220             if (mqx_enabled)
221                 WriteMqxDeBone(bones, object_id /* eq numobjects */);
222
223             tw.WriteLine("Eof");
224         }
225
226         void WriteMqxDeBone(MqoBone[] bones, int numobjects)
227         {
228             XmlWriterSettings settings = new XmlWriterSettings();
229             settings.Indent = true;
230             settings.IndentChars = new String(' ', 4);
231             XmlWriter writer = XmlWriter.Create(Path.ChangeExtension(OutFile, ".mqx"), settings);
232             writer.WriteStartElement("MetasequoiaDocument");
233                 writer.WriteElementString("IncludedBy", Path.GetFileName(OutFile));
234
235             writer.WriteStartElement("Plugin.56A31D20.71F282AB");
236                 writer.WriteAttributeString("name", "Bone");
237             writer.WriteStartElement("BoneSet");
238
239             foreach (MqoBone bone in bones)
240                 bone.Write(writer);
241
242             writer.WriteEndElement();//BoneSet
243
244             for (int i = 0; i < numobjects; i++)
245             {
246                 writer.WriteStartElement("Obj");
247                 writer.WriteAttributeString("id", (i+1).ToString());
248                 writer.WriteEndElement();
249             }
250             writer.WriteEndElement();//Plugin.56A31D20.71F282AB
251
252             writer.WriteEndElement();//MetasequoiaDocument
253             writer.Close();
254         }
255
256         void WriteRokDeBone(MqoBone[] bones)
257         {
258             tw.WriteLine("Object \"{0}\" {{", "Bone");
259             tw.WriteLine("\tvisible {0}", 15);
260             tw.WriteLine("\tlocking {0}", 0);
261             tw.WriteLine("\tshading {0}", 1);
262             tw.WriteLine("\tfacet {0}", 59.5);
263             tw.WriteLine("\tcolor {0} {1} {2}", 1, 0, 0);
264             tw.WriteLine("\tcolor_type {0}", 0);
265
266             tw.WriteLine("\tvertex {0} {{", bones.Length);
267
268             foreach (MqoBone bone in bones)
269                 tw.WriteLine("\t\t{0:F4} {1:F4} {2:F4}", bone.q.x, bone.q.y, bone.q.z);
270
271             tw.WriteLine("\t}");
272
273             //
274             tw.WriteLine("\tface {0} {{", bones.Length);
275
276             foreach (MqoBone bone in bones)
277             {
278                 if (bone.pid == -1)
279                     continue;
280
281                 //根元と先端を接続するedge
282                 if (! bone.tail)
283                     tw.WriteLine(string.Format("\t\t2 V({0} {1})", bone.pid, bone.id));
284             }
285
286             tw.WriteLine("\t}");
287             tw.WriteLine("}");
288         }
289     }
290
291     public class MqoObjectGen
292     {
293         public static bool uid_enabled;
294
295         public int id; //object_id
296         public string name;
297         VertexHeap<UVertex> vh = new VertexHeap<UVertex>();
298         public List<MqoFace> faces;
299
300         public int numvertices { get { return vh.Count; } }
301         public List<UVertex> vertices { get { return vh.verts; } }
302         public int numfaces { get { return faces.Count; } }
303
304         public MqoObjectGen()
305         {
306             faces = new List<MqoFace>(2048);
307         }
308
309         public void Update(TSOMesh mesh)
310         {
311             vh.Clear();
312             faces.Clear();
313
314             foreach (TSOSubMesh sub_mesh in mesh.sub_meshes)
315             {
316                 int cnt = 0;
317                 ushort a = 0, b = 0, c = 0;
318                 Vertex va = new Vertex(), vb = new Vertex(), vc = new Vertex();
319
320                 foreach (Vertex v in sub_mesh.vertices)
321                 {
322                     ++cnt;
323                     va = vb; a = b;
324                     vb = vc; b = c;
325                     vc = v; c = vh.Add(new UVertex(v.Pos, v.Wgt, v.Idx, v.Nrm));
326
327                     if (cnt < 3) continue;
328                     if (a == b || b == c || c == a) continue;
329
330                     if ((cnt & 1) == 0)
331                     {
332                         MqoFace f = new MqoFace(a, b, c, (ushort)sub_mesh.spec,
333                                 new Point2(va.Tex.x, 1 - va.Tex.y),
334                                 new Point2(vb.Tex.x, 1 - vb.Tex.y),
335                                 new Point2(vc.Tex.x, 1 - vc.Tex.y));
336                         faces.Add(f);
337                     }
338                     else
339                     {
340                         MqoFace f = new MqoFace(a, c, b, (ushort)sub_mesh.spec,
341                                 new Point2(va.Tex.x, 1 - va.Tex.y),
342                                 new Point2(vc.Tex.x, 1 - vc.Tex.y),
343                                 new Point2(vb.Tex.x, 1 - vb.Tex.y));
344                         faces.Add(f);
345                     }
346                 }
347             }
348         }
349
350         public void Write(TextWriter tw)
351         {
352             tw.WriteLine("Object \"{0}\" {{", name);
353             if (uid_enabled)
354                 tw.WriteLine("\tuid {0}", id);
355             tw.WriteLine("\tvisible {0}", 15);
356             tw.WriteLine("\tlocking {0}", 0);
357             tw.WriteLine("\tshading {0}", 1);
358             tw.WriteLine("\tfacet {0}", 59.5);
359             tw.WriteLine("\tcolor {0:F3} {1:F3} {2:F3}", 0.898f, 0.498f, 0.698f);
360             tw.WriteLine("\tcolor_type {0}", 0);
361
362             //
363             tw.WriteLine("\tvertex {0} {{", numvertices);
364
365             foreach (UVertex v in vertices)
366                 v.Write(tw);
367
368             tw.WriteLine("\t}");
369
370             if (uid_enabled)
371             {
372                 tw.WriteLine("\tvertexattr {");
373                 tw.WriteLine("\t\tuid {");
374
375                 ushort vertex_id = 0;
376                 foreach (UVertex v in vertices)
377                     tw.WriteLine("\t\t\t{0}", ++vertex_id);
378
379                 tw.WriteLine("\t\t}");
380                 tw.WriteLine("\t}");
381             }
382
383             //
384             tw.WriteLine("\tface {0} {{", numfaces);
385
386             for (int i = 0, n = numfaces; i < n; i++)
387                 faces[i].Write(tw);
388             tw.WriteLine("\t}");
389             tw.WriteLine("}");
390         }
391
392         public unsafe void AddWeits(MqoBone[] bones)
393         {
394             ushort vertex_id = 0;
395             foreach (UVertex v in vertices)
396             {
397                 ++vertex_id;
398
399                 uint idx0 = v.Idx;
400                 byte* idx = (byte*)(&idx0);
401                 Point4 wgt0 = v.Wgt;
402                 float* wgt = (float*)(&wgt0);
403
404                 for (int k = 0; k < 4; ++k)
405                     if (wgt[k] > float.Epsilon)
406                     {
407                         MqoWeit weit = new MqoWeit();
408                         weit.object_id = id;
409                         weit.vertex_id = vertex_id;
410                         weit.weit = wgt[k];
411                         bones[idx[k]].weits.Add(weit);
412                     }
413             }
414         }
415     }
416
417     public class MqoBone
418     {
419         public int id;
420         public string name;
421         public bool tail;
422         //なければ-1
423         public int pid;
424         public List<int> cids = new List<int>();
425
426         //根本position
427         public Point3 q;
428
429         //先端position
430         public Point3 p;
431
432         public List<MqoWeit> weits;
433
434         public MqoBone()
435         {
436             weits = new List<MqoWeit>(2048*3*4);
437         }
438
439         public void Write(XmlWriter writer)
440         {
441             writer.WriteStartElement("Bone");
442             writer.WriteAttributeString("id", (id+1).ToString());
443
444             writer.WriteAttributeString("rtX", q.X.ToString());
445             writer.WriteAttributeString("rtY", q.Y.ToString());
446             writer.WriteAttributeString("rtZ", q.Z.ToString());
447
448             writer.WriteAttributeString("tpX", p.X.ToString());
449             writer.WriteAttributeString("tpY", p.Y.ToString());
450             writer.WriteAttributeString("tpZ", p.Z.ToString());
451
452             writer.WriteAttributeString("rotB", "0.0");
453             writer.WriteAttributeString("rotH", "0.0");
454             writer.WriteAttributeString("rotP", "0.0");
455
456             writer.WriteAttributeString("mvX", "0.0");
457             writer.WriteAttributeString("mvY", "0.0");
458             writer.WriteAttributeString("mvZ", "0.0");
459
460             writer.WriteAttributeString("sc", "1.0");
461
462             writer.WriteAttributeString("maxAngB", "90.0");
463             writer.WriteAttributeString("maxAngH", "180.0");
464             writer.WriteAttributeString("maxAngP", "180.0");
465
466             writer.WriteAttributeString("minAngB", "-90.0");
467             writer.WriteAttributeString("minAngH", "-180.0");
468             writer.WriteAttributeString("minAngP", "-180.0");
469
470             writer.WriteAttributeString("isDummy", tail ? "1" : "0");
471             writer.WriteAttributeString("name", name);
472
473             writer.WriteStartElement("P");
474             writer.WriteAttributeString("id", (pid+1).ToString());
475             writer.WriteEndElement();
476
477             foreach (int cid in cids)
478             {
479                 writer.WriteStartElement("C");
480                 writer.WriteAttributeString("id", (cid+1).ToString());
481                 writer.WriteEndElement();
482             }
483             foreach (MqoWeit weit in weits)
484             {
485                 weit.Write(writer);
486             }
487
488             writer.WriteEndElement();
489         }
490     }
491
492     public class MqoWeit
493     {
494         public int object_id;
495         public int vertex_id;
496         //public int bone_id;
497         public float weit;
498
499         public void Write(XmlWriter writer)
500         {
501             float weit_percent = weit * 100.0f;
502
503             writer.WriteStartElement("W");
504             writer.WriteAttributeString("oi", object_id.ToString());
505             writer.WriteAttributeString("vi", vertex_id.ToString());
506             writer.WriteAttributeString("w", weit_percent.ToString());
507             writer.WriteEndElement();
508         }
509     }
510 }