using System; using System.Collections.Generic; using System.IO; using System.Text; namespace Tso2MqoGui { public enum MqoBoneMode { None, RokDeBone, Mikoto, } public class Pair { public T First; public U Second; public Pair() { } public Pair(T first, U second) { First = first; Second = second; } } public class MqoWriter : IDisposable { public TextWriter tw; public string OutPath; public string OutFile; public MqoBoneMode BoneMode = MqoBoneMode.None; public MqoWriter(string file) { FileStream fs = File.OpenWrite(file); fs.SetLength(0); tw = new StreamWriter(fs, Encoding.Default); OutFile = file; OutPath = Path.GetDirectoryName(file); } void IDisposable.Dispose() { Close(); } public void Close() { if(tw != null) tw.Close(); tw = null; } public void CreateTextureFile(TSOTex tex) { string file= Path.Combine(OutPath, Path.GetFileName(tex.file.Trim('"'))); byte[] data= tex.data; using(FileStream fs= File.OpenWrite(file)) { BinaryWriter bw = new BinaryWriter(fs); switch(Path.GetExtension(file).ToUpper()) { case ".TGA": bw.Write((byte)0); // id bw.Write((byte)0); // colormap bw.Write((byte)2); // imagetype bw.Write((byte)0); // unknown0 bw.Write((byte)0); // unknown1 bw.Write((byte)0); // unknown2 bw.Write((byte)0); // unknown3 bw.Write((byte)0); // unknown4 bw.Write((short)0); // width bw.Write((short)0); // height bw.Write((short)tex.Width); // width bw.Write((short)tex.Height); // height bw.Write((byte)(tex.depth * 8));// depth bw.Write((byte)0); // depth break; case ".BMP": bw.Write((byte)'B'); bw.Write((byte)'M'); bw.Write((int)(54 + data.Length)); bw.Write((int)0); bw.Write((int)54); bw.Write((int)40); bw.Write((int)tex.Width); bw.Write((int)tex.Height); bw.Write((short)1); bw.Write((short)(tex.Depth*8)); bw.Write((int)0); bw.Write((int)data.Length); bw.Write((int)0); bw.Write((int)0); bw.Write((int)0); bw.Write((int)0); break; } bw.Write(data, 0, data.Length); bw.Flush(); } } public void Write(TSOFile file) { tw.WriteLine("Metasequoia Document"); tw.WriteLine("Format Text Ver 1.0"); tw.WriteLine(""); tw.WriteLine("Scene {"); tw.WriteLine(" pos -7.0446 4.1793 1541.1764"); tw.WriteLine(" lookat 11.8726 193.8590 0.4676"); tw.WriteLine(" head 0.8564"); tw.WriteLine(" pich 0.1708"); tw.WriteLine(" ortho 0"); tw.WriteLine(" zoom2 31.8925"); tw.WriteLine(" amb 0.250 0.250 0.250"); tw.WriteLine("}"); //VertexHeap vh = new VertexHeap(); VertexHeap vh = new VertexHeap(); List face= new List(2048*3); List uv = new List(2048*3 * 2); List mtl = new List(2048); foreach(TSOTex i in file.textures) CreateTextureFile(i); tw.WriteLine("Material {0} {{", file.materials.Length); foreach(TSOMaterial i in file.materials) { if(i.ColorTex != null) { TSOTex tex = file.texturemap[i.ColorTex]; tw.WriteLine( " \"{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}\")", i.name, Path.Combine(OutPath, tex.File.Trim('"'))); } else { tw.WriteLine( " \"{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))", i.name); } } tw.WriteLine("}"); foreach(TSOMesh i in file.meshes) { vh.Clear(); face.Clear(); uv.Clear(); mtl.Clear(); foreach(TSOSubMesh j in i.sub) { int cnt = 0; ushort a= 0, b= 0, c= 0; Vertex va= new Vertex(), vb= new Vertex(), vc= new Vertex(); foreach(Vertex k in j.vertices) { ++cnt; va= vb; a= b; vb= vc; b= c; vc= k; c= vh.Add(new UVertex(k.Pos, k.Nrm, k.Tex, j.spec)); if(cnt < 3) continue; if(a == b || b == c || c == a) continue; if((cnt & 1) == 0) { face.Add(a); uv.Add(va.Tex.x); uv.Add(1-va.Tex.y); face.Add(b); uv.Add(vb.Tex.x); uv.Add(1-vb.Tex.y); face.Add(c); uv.Add(vc.Tex.x); uv.Add(1-vc.Tex.y); mtl.Add(j.spec); } else { face.Add(a); uv.Add(va.Tex.x); uv.Add(1-va.Tex.y); face.Add(c); uv.Add(vc.Tex.x); uv.Add(1-vc.Tex.y); face.Add(b); uv.Add(vb.Tex.x); uv.Add(1-vb.Tex.y); mtl.Add(j.spec); } } } tw.WriteLine("Object \"{0}\" {{", i.Name); tw.WriteLine(" visible {0}", 15); tw.WriteLine(" locking {0}", 0); tw.WriteLine(" shading {0}", 1); tw.WriteLine(" facet {0}", 59.5); tw.WriteLine(" color {0:F3} {1:F3} {2:F3}", 0.898f, 0.498f, 0.698f); tw.WriteLine(" color_type {0}", 0); // tw.WriteLine(" vertex {0} {{", vh.Count); foreach(UVertex j in vh.verts) WriteVertex(j.Pos.x, j.Pos.y, j.Pos.z); tw.WriteLine(" }"); // tw.WriteLine(" face {0} {{", face.Count / 3); System.Diagnostics.Debug.Assert(face.Count*2 == uv.Count); System.Diagnostics.Debug.Assert(face.Count == mtl.Count * 3); for(int j= 0, n= face.Count; j < n; j+=3) WriteFace(face[j+0], face[j+1], face[j+2], uv[j*2+0], uv[j*2+1], uv[j*2+2], uv[j*2+3], uv[j*2+4], uv[j*2+5], mtl[j/3]); tw.WriteLine(" }"); tw.WriteLine("}"); } // ボーンを出す switch(BoneMode) { case MqoBoneMode.None: break; case MqoBoneMode.RokDeBone: { // マトリクス計算 foreach(TSONode i in file.nodes) { if(i.parent == null) i.world = i.Matrix; else i.world = Matrix44.Mul(i.Matrix, i.parent.World); } List points = new List(); List bones = new List(); tw.WriteLine("Object \"{0}\" {{", "Bone"); tw.WriteLine(" visible {0}", 15); tw.WriteLine(" locking {0}", 0); tw.WriteLine(" shading {0}", 1); tw.WriteLine(" facet {0}", 59.5); tw.WriteLine(" color {0} {1} {2}", 1, 0, 0); tw.WriteLine(" color_type {0}", 0); foreach(TSONode i in file.nodes) { if(i.children.Count == 0) continue; Point3 q = new Point3(i.world.M41, i.world.M42, i.world.M43); Point3 p = new Point3(); foreach(TSONode j in i.children) { p.x +=j.world.M41; p.y +=j.world.M42; p.z +=j.world.M43; } p.x /=i.children.Count; p.y /=i.children.Count; p.z /=i.children.Count; bones.Add(points.Count); points.Add(q); bones.Add(points.Count); points.Add(p); } tw.WriteLine(" vertex {0} {{", points.Count); foreach(Point3 j in points) WriteVertex(j.x, j.y, j.z); tw.WriteLine(" }"); // tw.WriteLine(" face {0} {{", bones.Count / 2); for(int j= 0, n= bones.Count; j < n; j+=2) tw.WriteLine(string.Format(" 2 V({0} {1})", bones[j+0], bones[j+1])); tw.WriteLine(" }"); tw.WriteLine("}"); } break; case MqoBoneMode.Mikoto: { } break; } tw.WriteLine("Eof"); } public void WriteFace(int a, int b, int c, float u1, float v1, float u2, float v2, float u3, float v3, int m) { tw.WriteLine(" {0} V({1} {2} {3}) M({10}) UV({4:F5} {5:F5} {6:F5} {7:F5} {8:F5} {9:F5})", 3, a, b, c, u1, v1, u2, v2, u3, v3, m); } public void WriteVertex(float x, float y, float z) { tw.WriteLine(" {0:F4} {1:F4} {2:F4}", x, y, z); } } public class UVertex : IComparable { public Point3 Pos; public Point3 Nrm; public Point2 Tex; public int mtl; public UVertex() { } public UVertex(Point3 pos, Point3 nrm, Point2 tex, int mtl) { Pos = pos; Nrm = nrm; Tex = tex; this.mtl= mtl; } public int CompareTo(UVertex o) { int cmp; cmp = Pos.CompareTo(o.Pos); if (cmp != 0) return cmp; cmp = Nrm.CompareTo(o.Nrm); return cmp; } public override int GetHashCode() { return Pos.GetHashCode() ^ Nrm.GetHashCode(); } public override bool Equals(object obj) { if (obj is UVertex) { UVertex v = (UVertex)obj; return Pos.Equals(v.Pos) && Nrm.Equals(v.Nrm); } return false; } public bool Equals(UVertex v) { if ((object)v == null) { return false; } return Pos.Equals(v.Pos) && Nrm.Equals(v.Nrm); } } }