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