2 using System.Collections.Generic;
\r
3 using System.Drawing.Design;
\r
6 using System.Runtime.InteropServices;
\r
7 using System.ComponentModel;
\r
8 using System.Windows.Forms;
\r
9 using System.Windows.Forms.Design;
\r
11 namespace Tso2MqoGui
\r
13 public unsafe class TSOGenerator
\r
16 private TSOGenerateConfig config;
\r
17 private PointCluster pc;
\r
18 private MqoFile mqo;
\r
19 private TSOFile tsor;
\r
20 private List<Vertex> vlst;
\r
21 private Dictionary<string, TSONode> nodes;
\r
22 private List<TSOMesh> meshes;
\r
23 private string mqoin;
\r
24 private string tsoref;
\r
25 private string tsoex;
\r
26 private ImportInfo ii;
\r
27 private BinaryWriter bw;
\r
28 private Dictionary<string, MaterialInfo> materials;
\r
29 private Dictionary<string, TextureInfo> textures;
\r
31 public TSOFile LoadTSO(string file)
\r
33 TSOFile tso = new TSOFile(file);
\r
38 private void CreatePointCluster(TSOFile tso)
\r
40 vlst= new List<Vertex>();
\r
42 foreach(TSOMesh i in tso.meshes)
\r
43 foreach(TSOSubMesh j in i.sub)
\r
44 vlst.AddRange(j.vertices);
\r
46 pc = new PointCluster(vlst.Count);
\r
48 foreach(Vertex i in vlst)
\r
49 pc.Add(i.Pos.x, i.Pos.y, i.Pos.z);
\r
54 private bool Common_DoSetupDir()
\r
56 Environment.CurrentDirectory= dir= Path.GetDirectoryName(mqoin);
\r
60 private bool Common_DoLoadMQO()
\r
63 mqo = new MqoFile();
\r
69 private bool AutoBone_DoLoadRefTSO()
\r
72 tsor = LoadTSO(tsoref);
\r
74 foreach(TSOMesh i in tsor.meshes)
\r
75 foreach(TSOSubMesh j in i.sub)
\r
77 int[] bones = j.bones;
\r
79 for(int k= 0, n= j.numvertices; k < n; ++k)
\r
82 uint idx0= j.vertices[k].Idx;
\r
83 byte* idx = (byte*)(&idx0);
\r
84 idx[0] = (byte)bones[idx[0]];
\r
85 idx[1] = (byte)bones[idx[1]];
\r
86 idx[2] = (byte)bones[idx[2]];
\r
87 idx[3] = (byte)bones[idx[3]];
\r
88 j.vertices[k].Idx = idx0;
\r
92 CreatePointCluster(tsor);
\r
96 private bool OneBone_DoLoadRefTSO()
\r
99 tsor = LoadTSO(tsoref);
\r
103 private bool Common_DoLoadXml()
\r
106 ii = ImportInfo.Load(Path.ChangeExtension(mqoin, ".xml"));
\r
109 materials = new Dictionary<string, MaterialInfo>();
\r
110 bool validmap= true;
\r
112 foreach(MqoMaterial i in mqo.Materials)
\r
114 MaterialInfo mi = new MaterialInfo(dir, i, ii.GetMaterial(i.name));
\r
115 validmap &=mi.Valid;
\r
116 materials.Add(i.name, mi);
\r
119 if(!validmap || config.materialconfig)
\r
122 throw new Exception("マテリアルの設定が無効です");
\r
124 FormMaterial dlg = new FormMaterial();
\r
125 dlg.materials = materials;
\r
127 if(dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK)
\r
132 textures = new Dictionary<string, TextureInfo>();
\r
134 foreach(MaterialInfo i in materials.Values)
\r
136 string name= Path.GetFileNameWithoutExtension(i.diffuse);
\r
138 if(!textures.ContainsKey(name))
\r
139 textures.Add(name, new TextureInfo(name, i.diffuse));
\r
141 name = Path.GetFileNameWithoutExtension(i.shadow);
\r
143 if(!textures.ContainsKey(name))
\r
144 textures.Add(name, new TextureInfo(name, i.shadow));
\r
150 private bool Common_DoWriteHeader()
\r
152 bw.Write(0x314F5354);
\r
156 private bool Common_DoWriteNodeNames()
\r
158 bw.Write(tsor.nodes.Length);
\r
160 nodes = new Dictionary<string,TSONode>();
\r
162 foreach(TSONode i in tsor.nodes)
\r
164 WriteString(bw, i.Name);
\r
165 nodes.Add(i.ShortName, i);
\r
171 private bool Common_DoWriteNodeMatrices()
\r
173 bw.Write(tsor.nodes.Length);
\r
175 foreach(TSONode i in tsor.nodes)
\r
176 WriteMatrix(bw, i.Matrix);
\r
181 private bool Common_DoWriteTextures()
\r
183 bw.Write(textures.Count);
\r
185 foreach(TextureInfo i in textures.Values)
\r
187 string file= i.file;
\r
188 string name= i.name;
\r
190 WriteString(bw, name);
\r
191 WriteString(bw, "\"" + Path.GetFileName(file) + "\"");
\r
194 TSOTex tex = LoadTex(file);
\r
196 bw.Write(tex.Width);
\r
197 bw.Write(tex.Height);
\r
198 bw.Write(tex.Depth);
\r
199 bw.Write(tex.data, 0, tex.data.Length);
\r
201 ImportTextureInfo iti = new ImportTextureInfo(tex);
\r
202 ii.textures.Add(iti);
\r
204 // テクスチャが同じフォルダにない場合、コピーしておく
\r
205 if(Path.GetDirectoryName(file).ToUpper() != dir.ToUpper())
\r
207 iti.File = Path.Combine(dir, Path.GetFileName(file));
\r
208 File.Copy(file, iti.File, true);
\r
215 private bool Common_DoWriteEffects()
\r
217 bw.Write(ii.effects.Count);
\r
219 foreach(ImportEffectInfo i in ii.effects)
\r
221 string file= Path.Combine(dir, i.Name);
\r
222 string[] code= File.ReadAllLines(file, Encoding.Default);
\r
224 WriteString(bw, i.Name);
\r
225 bw.Write(code.Length);
\r
227 foreach(string j in code)
\r
228 WriteString(bw, j.Trim('\r', '\n'));
\r
234 private bool Common_DoWriteMaterials()
\r
236 bw.Write(mqo.Materials.Count);
\r
238 foreach(MqoMaterial i in mqo.Materials)
\r
240 MaterialInfo mi = materials[i.name];
\r
241 string[] code= mi.GetCode();
\r
243 WriteString(bw, i.name);
\r
244 WriteString(bw, "cgfxShader");
\r
245 bw.Write(code.Length);
\r
247 foreach(string j in code)
\r
248 WriteString(bw, j.Trim('\r', '\n'));
\r
250 ImportMaterialInfo imi = new ImportMaterialInfo();
\r
252 imi.File = "cgfxShader";
\r
253 ii.materials.Add(imi);
\r
256 File.WriteAllLines(Path.Combine(dir, i.name), code);
\r
262 private bool AutoBone_DoGenerateMeshes()
\r
264 meshes = new List<TSOMesh>();
\r
266 foreach(MqoObject i in mqo.Objects)
\r
268 if(i.name.ToLower() == "bone")
\r
272 List<int> vref= new List<int>(i.vertices.Count);
\r
274 foreach(Point3 j in i.vertices)
\r
275 vref.Add(pc.NearestIndex(j.x, j.y, j.z));
\r
278 Point3[] nrm = new Point3[i.vertices.Count];
\r
280 foreach(MqoFace j in i.faces)
\r
282 Point3 v1 = Point3.Normalize(i.vertices[j.b] - i.vertices[j.a]);
\r
283 Point3 v2 = Point3.Normalize(i.vertices[j.c] - i.vertices[j.b]);
\r
284 Point3 n = Point3.Normalize(Point3.Cross(v1, v2));
\r
291 for(int j= 0; j < nrm.Length; ++j)
\r
292 nrm[j] = Point3.Normalize(nrm[j]);
\r
295 List<int> faces1 = new List<int>();
\r
296 List<int> faces2 = new List<int>();
\r
297 //int[] bonecnv = new int[tsor.nodes.Length]; // ボーン変換テーブル
\r
298 VertexHeap<Vertex> vh = new VertexHeap<Vertex>();
\r
299 Vertex[] v = new Vertex[3];
\r
300 List<int> bones = new List<int>(16);
\r
301 List<ushort> indices = new List<ushort>();
\r
302 Dictionary<int, int> selected= new Dictionary<int,int>();
\r
303 Dictionary<int, int> work = new Dictionary<int,int>();
\r
304 List<TSOSubMesh> subs = new List<TSOSubMesh>();
\r
306 for(int j= 0, n= i.faces.Count; j < n; ++j)
\r
310 while(faces1.Count > 0)
\r
312 int mtl = i.faces[faces1[0]].mtl;
\r
318 foreach(int j in faces1)
\r
320 MqoFace f = i.faces[j];
\r
328 v[0] = vlst[vref[f.a]];
\r
329 v[1] = vlst[vref[f.b]];
\r
330 v[2] = vlst[vref[f.c]];
\r
334 for(int k= 0; k < 3; ++k)
\r
337 UInt32 idx0 = vv.Idx;
\r
338 Point4 wgt0 = vv.Wgt;
\r
339 byte* idx = (byte*)(&idx0);
\r
340 float* wgt = (float*)(&wgt0);
\r
345 for(int l= 0; l < 4; ++l)
\r
347 if(wgt[l] <= float.Epsilon) continue;
\r
348 if(selected.ContainsKey(idx[l])) continue;
\r
350 if(selected.Count == 16)
\r
356 if(!work.ContainsKey(idx[l]))
\r
357 work.Add(idx[l], 0);
\r
359 if(selected.Count + work.Count >= 17)
\r
377 foreach(KeyValuePair<int, int> l in work)
\r
379 System.Diagnostics.Debug.WriteLine(
\r
380 string.Format("Add: {0} -> {1}", l.Key, selected.Count));
\r
381 selected.Add(l.Key, selected.Count); // ボーンテーブルに追加
\r
386 Vertex va = new Vertex(i.vertices[f.a], v[0].Wgt, v[0].Idx, nrm[f.a], new Point2(f.ta.x, 1-f.ta.y));
\r
387 Vertex vb = new Vertex(i.vertices[f.b], v[1].Wgt, v[1].Idx, nrm[f.b], new Point2(f.tb.x, 1-f.tb.y));
\r
388 Vertex vc = new Vertex(i.vertices[f.c], v[2].Wgt, v[2].Idx, nrm[f.c], new Point2(f.tc.x, 1-f.tc.y));
\r
390 indices.Add(vh.Add(va));
\r
391 indices.Add(vh.Add(vc));
\r
392 indices.Add(vh.Add(vb));
\r
396 ushort[] nidx = NvTriStrip.Optimize(indices.ToArray());
\r
399 Vertex[] verts = vh.verts.ToArray();
\r
401 for(int j= 0; j < verts.Length; ++j)
\r
403 uint idx0= verts[j].Idx;
\r
404 byte* idx = (byte*)(&idx0);
\r
405 Point4 wgt0= verts[j].Wgt;
\r
406 float* wgt = (float*)(&wgt0);
\r
408 for(int k= 0; k < 4; ++k)
\r
409 if(wgt[k] > float.Epsilon)
\r
410 idx[k] = (byte)selected[idx[k]];
\r
412 verts[j].Idx = idx0;
\r
416 TSOSubMesh sub = new TSOSubMesh();
\r
418 sub.numbones = bones.Count;
\r
419 sub.bones = bones.ToArray();
\r
420 sub.numvertices = nidx.Length;
\r
421 sub.vertices = new Vertex[nidx.Length];
\r
423 for(int j= 0; j < nidx.Length; ++j)
\r
424 sub.vertices[j] = verts[nidx[j]];
\r
429 List<int> t = faces1;
\r
436 TSOMesh mesh = new TSOMesh();
\r
437 mesh.name = i.name;
\r
438 mesh.numsubs = subs.Count;
\r
439 mesh.sub = subs.ToArray();
\r
440 mesh.matrix = Matrix44.Identity;
\r
448 private bool OneBone_DoGenerateMeshes()
\r
450 meshes = new List<TSOMesh>();
\r
452 foreach(MqoObject i in mqo.Objects)
\r
454 if(i.name.ToLower() == "bone")
\r
458 Point3[] nrm = new Point3[i.vertices.Count];
\r
460 foreach(MqoFace j in i.faces)
\r
462 Point3 v1 = Point3.Normalize(i.vertices[j.b] - i.vertices[j.a]);
\r
463 Point3 v2 = Point3.Normalize(i.vertices[j.c] - i.vertices[j.b]);
\r
464 Point3 n = Point3.Normalize(Point3.Cross(v1, v2));
\r
470 for(int j= 0; j < nrm.Length; ++j)
\r
471 nrm[j] = Point3.Normalize(nrm[j]);
\r
474 uint idx = 0x00000000;
\r
475 Point4 wgt = new Point4(1, 0, 0, 0);
\r
476 int[] bones = new int[1];
\r
477 string bone = config.boneref[i.name];
\r
478 bones[0] = nodes[bone].ID;
\r
481 List<ushort> indices = new List<ushort>();
\r
482 VertexHeap<Vertex> vh = new VertexHeap<Vertex>();
\r
483 List<TSOSubMesh> subs = new List<TSOSubMesh>();
\r
485 for(int j= 0, n= materials.Count; j < n; ++j)
\r
490 foreach(MqoFace f in i.faces)
\r
495 Vertex va = new Vertex(i.vertices[f.a], wgt, idx, nrm[f.a], new Point2(f.ta.x, 1-f.ta.y));
\r
496 Vertex vb = new Vertex(i.vertices[f.b], wgt, idx, nrm[f.b], new Point2(f.tb.x, 1-f.tb.y));
\r
497 Vertex vc = new Vertex(i.vertices[f.c], wgt, idx, nrm[f.c], new Point2(f.tc.x, 1-f.tc.y));
\r
499 indices.Add(vh.Add(va));
\r
500 indices.Add(vh.Add(vc));
\r
501 indices.Add(vh.Add(vb));
\r
504 if(indices.Count == 0)
\r
508 ushort[] nidx = NvTriStrip.Optimize(indices.ToArray());
\r
511 Vertex[] verts= vh.verts.ToArray();
\r
512 TSOSubMesh sub = new TSOSubMesh();
\r
514 sub.numbones = bones.Length;
\r
516 sub.numvertices = nidx.Length;
\r
517 sub.vertices = new Vertex[nidx.Length];
\r
519 for(int k= 0; k < nidx.Length; ++k)
\r
520 sub.vertices[k] = verts[nidx[k]];
\r
526 TSOMesh mesh = new TSOMesh();
\r
527 mesh.name = i.name;
\r
528 mesh.numsubs = subs.Count;
\r
529 mesh.sub = subs.ToArray();
\r
530 mesh.matrix = Matrix44.Identity;
\r
538 private bool Common_DoWriteMeshes()
\r
540 bw.Write(meshes.Count);
\r
542 foreach(TSOMesh i in meshes)
\r
544 WriteString(bw, i.Name);
\r
545 WriteMatrix(bw, i.Matrix);
\r
547 bw.Write(i.numsubs);
\r
549 foreach(TSOSubMesh j in i.sub)
\r
552 bw.Write(j.numbones);
\r
554 foreach(int k in j.bones)
\r
557 bw.Write(j.numvertices);
\r
559 foreach(Vertex k in j.vertices)
\r
560 WriteVertex(bw, k);
\r
567 private bool AutoBone_DoOutput()
\r
569 //----- 出力処理 -----------------------------------------------
\r
570 ii.materials.Clear();
\r
571 ii.textures.Clear();
\r
573 using(FileStream fs= File.OpenWrite(tsoex))
\r
576 bw = new BinaryWriter(fs);
\r
578 Common_DoWriteHeader();
\r
579 Common_DoWriteNodeNames();
\r
580 Common_DoWriteNodeMatrices();
\r
581 Common_DoWriteTextures();
\r
582 Common_DoWriteEffects();
\r
583 Common_DoWriteMaterials();
\r
584 AutoBone_DoGenerateMeshes();
\r
585 Common_DoWriteMeshes();
\r
591 private bool OneBone_DoOutput()
\r
593 //----- 出力処理 -----------------------------------------------
\r
594 ii.materials.Clear();
\r
595 ii.textures.Clear();
\r
597 using(FileStream fs= File.OpenWrite(tsoex))
\r
600 bw = new BinaryWriter(fs);
\r
602 Common_DoWriteHeader();
\r
603 Common_DoWriteNodeNames();
\r
604 Common_DoWriteNodeMatrices();
\r
605 Common_DoWriteTextures();
\r
606 Common_DoWriteEffects();
\r
607 Common_DoWriteMaterials();
\r
608 OneBone_DoGenerateMeshes();
\r
609 Common_DoWriteMeshes();
\r
615 private bool Common_DoSaveXml()
\r
618 ImportInfo.Save(Path.ChangeExtension(mqoin, ".xml"), ii);
\r
622 private bool Common_DoCleanup()
\r
640 System.GC.Collect();
\r
644 public unsafe void GenerateOneBone(string mqoin, string tsoref, string tsoex, TSOGenerateConfig config)
\r
646 this.mqoin = mqoin;
\r
647 this.tsoref = tsoref;
\r
648 this.tsoex = tsoex;
\r
649 this.config = config;
\r
653 if(!Common_DoSetupDir()) return;
\r
654 if(!Common_DoLoadMQO()) return;
\r
655 if(!OneBone_DoLoadRefTSO()) return;
\r
656 if(!Common_DoLoadXml()) return;
\r
657 if(!OneBone_DoOutput()) return;
\r
658 if(!Common_DoSaveXml()) return;
\r
661 Common_DoCleanup();
\r
665 public unsafe void GenerateAutoBone(string mqoin, string tsoref, string tsoex, TSOGenerateConfig config)
\r
667 this.mqoin = mqoin;
\r
668 this.tsoref = tsoref;
\r
669 this.tsoex = tsoex;
\r
670 this.config = config;
\r
674 if(!Common_DoSetupDir()) return;
\r
675 if(!Common_DoLoadMQO()) return;
\r
676 if(!AutoBone_DoLoadRefTSO()) return;
\r
677 if(!Common_DoLoadXml()) return;
\r
678 if(!AutoBone_DoOutput()) return;
\r
679 if(!Common_DoSaveXml()) return;
\r
682 Common_DoCleanup();
\r
686 public void WriteString(BinaryWriter bw, string s)
\r
688 byte[] b = Encoding.Default.GetBytes(s);
\r
693 public void WriteMatrix(BinaryWriter bw, Matrix44 m)
\r
695 bw.Write(m.M11); bw.Write(m.M12); bw.Write(m.M13); bw.Write(m.M14);
\r
696 bw.Write(m.M21); bw.Write(m.M22); bw.Write(m.M23); bw.Write(m.M24);
\r
697 bw.Write(m.M31); bw.Write(m.M32); bw.Write(m.M33); bw.Write(m.M34);
\r
698 bw.Write(m.M41); bw.Write(m.M42); bw.Write(m.M43); bw.Write(m.M44);
\r
701 public unsafe void WriteVertex(BinaryWriter bw, Vertex v)
\r
704 byte* idx = (byte*)(&idx0);
\r
705 List<int> idxs = new List<int>(4);
\r
706 List<float> wgts = new List<float>(4);
\r
708 if(v.Wgt.x > 0) { idxs.Add(idx[0]); wgts.Add(v.Wgt.x); }
\r
709 if(v.Wgt.y > 0) { idxs.Add(idx[1]); wgts.Add(v.Wgt.y); }
\r
710 if(v.Wgt.z > 0) { idxs.Add(idx[2]); wgts.Add(v.Wgt.z); }
\r
711 if(v.Wgt.w > 0) { idxs.Add(idx[3]); wgts.Add(v.Wgt.w); }
\r
713 bw.Write(v.Pos.X); bw.Write(v.Pos.Y); bw.Write(v.Pos.Z);
\r
714 bw.Write(v.Nrm.X); bw.Write(v.Nrm.Y); bw.Write(v.Nrm.Z);
\r
715 bw.Write(v.Tex.X); bw.Write(v.Tex.Y);
\r
717 bw.Write(wgts.Count);
\r
719 for(int i= 0, n= idxs.Count; i < n; ++i)
\r
727 public TSOTex LoadTex(string file)
\r
729 string ext = Path.GetExtension(file).ToUpper();
\r
734 case ".TGA": tex= LoadTarga(file); break;
\r
735 case ".BMP": tex= LoadBitmap(file); break;
\r
736 default: throw new Exception("Unsupported texture file: " + file);
\r
739 for(int i= 0, n= tex.data.Length; i < n; i+=tex.Depth)
\r
741 byte b = tex.data[i+0];
\r
742 tex.data[i+0] = tex.data[i+2];
\r
749 public unsafe TSOTex LoadTarga(string file)
\r
751 using(FileStream fs= File.OpenRead(file))
\r
753 BinaryReader br = new BinaryReader(fs);
\r
754 TARGA_HEADER header;
\r
756 Marshal.Copy(br.ReadBytes(sizeof(TARGA_HEADER)), 0, (IntPtr)(&header), sizeof(TARGA_HEADER));
\r
758 if(header.imagetype != 0x02) throw new Exception("Invalid imagetype: " + file);
\r
759 if(header.depth != 24
\r
760 && header.depth != 32) throw new Exception("Invalid depth: " + file);
\r
762 TSOTex tex = new TSOTex();
\r
763 tex.depth = header.depth / 8;
\r
764 tex.width = header.width;
\r
765 tex.height = header.height;
\r
767 tex.data = br.ReadBytes(tex.width * tex.height * tex.depth);
\r
773 public unsafe TSOTex LoadBitmap(string file)
\r
775 using(FileStream fs= File.OpenRead(file))
\r
777 BinaryReader br = new BinaryReader(fs);
\r
778 BITMAPFILEHEADER bfh;
\r
779 BITMAPINFOHEADER bih;
\r
781 Marshal.Copy(br.ReadBytes(sizeof(BITMAPFILEHEADER)), 0, (IntPtr)(&bfh), sizeof(BITMAPFILEHEADER));
\r
782 Marshal.Copy(br.ReadBytes(sizeof(BITMAPINFOHEADER)), 0, (IntPtr)(&bih), sizeof(BITMAPINFOHEADER));
\r
784 if(bfh.bfType != 0x4D42) throw new Exception("Invalid imagetype: " + file);
\r
785 if(bih.biBitCount != 24
\r
786 && bih.biBitCount != 32) throw new Exception("Invalid depth: " + file);
\r
788 TSOTex tex = new TSOTex();
\r
789 tex.depth = bih.biBitCount / 8;
\r
790 tex.width = bih.biWidth;
\r
791 tex.height = bih.biHeight;
\r
793 tex.data = br.ReadBytes(tex.width * tex.height * tex.depth);
\r
801 public class TextureInfo
\r
803 public string name;
\r
804 public string file;
\r
806 public TextureInfo(string name, string file)
\r
813 public class MaterialInfo
\r
815 public string name;
\r
816 public string shader;
\r
817 public string diffuse;
\r
818 public string shadow;
\r
819 //public Dictionary<string, string> parameters;
\r
821 public MaterialInfo(string path, MqoMaterial mqom, ImportMaterialInfo impm)
\r
824 diffuse = mqom.tex;
\r
828 string file= Path.Combine(path, impm.Name);
\r
830 if(File.Exists(file))
\r
833 if(impm.shadow != null)
\r
835 file = Path.Combine(path, impm.shadow.File);
\r
837 if(File.Exists(file))
\r
847 return File.Exists(shader)
\r
848 && File.Exists(diffuse)
\r
849 && File.Exists(shadow);
\r
853 public string[] GetCode()
\r
855 TSOMaterialCode code= TSOMaterialCode.GenerateFromFile(shader);
\r
856 List<string> line= new List<string>();
\r
858 code.SetValue("ColorTex", Path.GetFileNameWithoutExtension(diffuse));
\r
859 code.SetValue("ShadeTex", Path.GetFileNameWithoutExtension(shadow));
\r
861 foreach(KeyValuePair<string, TSOParameter> i in code)
\r
862 line.Add(i.Value.ToString());
\r
864 return line.ToArray();
\r
867 public string Name { get { return name; } }
\r
869 [Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
\r
870 [DisplayNameAttribute("シェーダー設定ファイル")]
\r
871 public string ShaderFile { get { return shader; } set { shader = value; } }
\r
873 [Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
\r
874 [DisplayNameAttribute("テクスチャ:カラー")]
\r
875 public string DiffuseTexture { get { return diffuse; } set { diffuse = value; } }
\r
877 [Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
\r
878 [DisplayNameAttribute("テクスチャ:シェーティング")]
\r
879 public string ShadowTexture { get { return shadow; } set { shadow = value; } }
\r