public static void ReconNodePath(this TDCG.TSOFile tso) { foreach (TSONode node in tso.nodes) { node.Path = GetReconPath(node); } }
/// <summary> /// フィギュア情報を削除します。 /// </summary> public void Clear() { gvShaderParams.DataSource = null; this.sub_script = null; lvSubScripts.Items.Clear(); this.tso = null; lvTSOFiles.Items.Clear(); this.fig = null; }
/// <summary> /// 指定位置にあるtsoの位置を入れ替えます。描画順を変更します。 /// </summary> /// <param name="aidx">リスト上の位置a</param> /// <param name="bidx">リスト上の位置b</param> public void SwapAt(int aidx, int bidx) { Debug.Assert(aidx < bidx); TSOFile a = TsoList[aidx]; TSOFile b = TsoList[bidx]; TsoList.RemoveAt(bidx); TsoList.RemoveAt(aidx); TsoList.Insert(aidx, b); TsoList.Insert(bidx, a); }
private void lvTSOFiles_SelectedIndexChanged(object sender, EventArgs e) { if (lvTSOFiles.SelectedItems.Count == 0) { return; } ListViewItem li = lvTSOFiles.SelectedItems[0]; TSOFile tso = li.Tag as TSOFile; SetTSOFile(tso); }
/// <summary> /// 指定名称(短い形式)を持つnodeを検索します。 /// </summary> /// <param name="name">node名称(短い形式)</param> /// <returns></returns> public static TSONode FindNodeByName(this TDCG.TSOFile tso, string name) { foreach (TSONode node in tso.nodes) { if (node.Name == name) { return(node); } } return(null); }
/// <summary> /// tsoに対するnodemapを追加します。 /// </summary> /// <param name="tso">tso</param> protected void AddNodeMap(TSOFile tso) { foreach (TSONode tso_node in tso.nodes) { TMONode tmo_node; if (tmo.nodemap.TryGetValue(tso_node.Path, out tmo_node)) { nodemap.Add(tso_node, tmo_node); } } }
/// techmapを基にテクニック名に対応するindexを代入します。 public void AssignTechniqueIndices(TSOFile tso) { foreach (TSOSubScript sub_script in tso.sub_scripts) { int idx; if (!technique_map.TryGetValue(sub_script.shader.technique_name, out idx)) { Console.WriteLine("name not found in technique-map: " + sub_script.shader.technique_name); idx = 0; } sub_script.shader.technique_idx = idx; } }
/// <summary> /// tsoをUIに設定します。 /// </summary> /// <param name="tso">tso</param> public void SetTSOFile(TSOFile tso) { this.tso = tso; lvSubScripts.Items.Clear(); foreach (TSOSubScript sub_script in tso.sub_scripts) { ListViewItem li = new ListViewItem(sub_script.Name); li.SubItems.Add(sub_script.File); li.Tag = sub_script; lvSubScripts.Items.Add(li); } lvSubScripts.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize); }
/// <summary> /// スキン変形行列の配列を得ます。 /// </summary> /// <param name="tso">tso</param> /// <returns>スキン変形行列の配列</returns> public Matrix[] ClipBoneMatrices(TSOFile tso) { Matrix[] clipped_boneMatrices = new Matrix[tso.nodes.Length]; for (int numPalettes = 0; numPalettes < tso.nodes.Length; numPalettes++) { TSONode tso_node = tso.nodes[numPalettes]; TMONode tmo_node; if (nodemap.TryGetValue(tso_node, out tmo_node)) { clipped_boneMatrices[numPalettes] = tso_node.offset_matrix * tmo_node.combined_matrix; } } return(clipped_boneMatrices); }
private void DrawSubMeshForWireFrame(Figure fig, TSOFile tso, TSOSubMesh sub_mesh) { device.SetRenderState(RenderStates.FillMode, (int)FillMode.WireFrame); //device.RenderState.VertexBlend = (VertexBlend)(4 - 1); device.SetStreamSource(0, sub_mesh.vb, 0, 52); tso.SwitchShader(sub_mesh); effect.SetValue(handle_LocalBoneMats, fig.ClipBoneMatrices(sub_mesh)); int npass = effect.Begin(0); for (int ipass = 0; ipass < npass; ipass++) { effect.BeginPass(ipass); device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, sub_mesh.vertices.Length - 2); effect.EndPass(); } effect.End(); }
/// <summary> /// フィギュアをUIに設定します。 /// </summary> /// <param name="fig">フィギュア</param> public void SetFigure(Figure fig) { this.fig = fig; this.tbSlideArm.Value = (int)(fig.slide_matrices.ArmRatio * (float)tbSlideArm.Maximum); this.tbSlideLeg.Value = (int)(fig.slide_matrices.LegRatio * (float)tbSlideLeg.Maximum); this.tbSlideWaist.Value = (int)(fig.slide_matrices.WaistRatio * (float)tbSlideWaist.Maximum); this.tbSlideBust.Value = (int)(fig.slide_matrices.BustRatio * (float)tbSlideBust.Maximum); this.tbSlideTall.Value = (int)(fig.slide_matrices.TallRatio * (float)tbSlideTall.Maximum); this.tbSlideEye.Value = (int)(fig.slide_matrices.EyeRatio * (float)tbSlideEye.Maximum); lvTSOFiles.Items.Clear(); for (int i = 0; i < fig.TSOList.Count; i++) { TSOFile tso = fig.TSOList[i]; ListViewItem li = new ListViewItem("TSO #" + i.ToString()); li.Tag = tso; lvTSOFiles.Items.Add(li); } lvTSOFiles.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize); }
/// <summary> /// 指定ディレクトリからフィギュアを作成して追加します。 /// </summary> /// <param name="source_file">TSOFileを含むディレクトリ</param> public void AddFigureFromTSODirectory(string source_file) { List <TSOFile> tso_list = new List <TSOFile>(); try { string[] files = Directory.GetFiles(source_file, "*.TSO"); foreach (string file in files) { TSOFile tso = new TSOFile(); Debug.WriteLine("loading " + file); tso.Load(file); tso_list.Add(tso); } } catch (Exception ex) { Console.WriteLine("Error: " + ex); } Figure fig = new Figure(); foreach (TSOFile tso in tso_list) { if (TSOFileOpen != null) { TSOFileOpen(tso); } fig.TSOFileList.Add(tso); } fig.UpdateNodeMapAndBoneMatrices(); int idx = FigureList.Count; FigureList.Add(fig); SetFigureIndex(idx); if (FigureEvent != null) { FigureEvent(this, EventArgs.Empty); } }
/// <summary> /// 指定パスからPNGFileを読み込みフィギュアを作成します。 /// </summary> /// <param name="source_file">PNGFileのパス</param> public void Load(string source_file) { if (File.Exists(source_file)) { try { PNGFile png = new PNGFile(); Figure fig = null; png.Hsav += delegate(string type) { this.type = type; fig = new Figure(); this.figures.Add(fig); }; png.Pose += delegate(string type) { this.type = type; }; png.Scne += delegate(string type) { this.type = type; }; png.Cami += delegate(Stream dest, int extract_length) { byte[] buf = new byte[extract_length]; dest.Read(buf, 0, extract_length); List <float> factor = new List <float>(); for (int offset = 0; offset < extract_length; offset += sizeof(float)) { float flo = BitConverter.ToSingle(buf, offset); factor.Add(flo); } if (CameraUpdate != null) { Vector3 translation = new Vector3(-factor[0], -factor[1], -factor[2]); Vector3 angle = new Vector3(-factor[5], -factor[4], -factor[6]); CameraUpdate(translation, angle); } }; png.Lgta += delegate(Stream dest, int extract_length) { byte[] buf = new byte[extract_length]; dest.Read(buf, 0, extract_length); List <float> factor = new List <float>(); for (int offset = 0; offset < extract_length; offset += sizeof(float)) { float flo = BitConverter.ToSingle(buf, offset); factor.Add(flo); } Matrix m; m.M11 = factor[0]; m.M12 = factor[1]; m.M13 = factor[2]; m.M14 = factor[3]; m.M21 = factor[4]; m.M22 = factor[5]; m.M23 = factor[6]; m.M24 = factor[7]; m.M31 = factor[8]; m.M32 = factor[9]; m.M33 = factor[10]; m.M34 = factor[11]; m.M41 = factor[12]; m.M42 = factor[13]; m.M43 = factor[14]; m.M44 = factor[15]; this.LightDirection = Vector3.TransformCoordinate(new Vector3(0.0f, 0.0f, -1.0f), m); }; png.Ftmo += delegate(Stream dest, int extract_length) { this.Tmo = new TMOFile(); this.Tmo.Load(dest); }; png.Figu += delegate(Stream dest, int extract_length) { fig = new Figure(); fig.LightDirection = this.LightDirection; fig.Tmo = this.Tmo; this.figures.Add(fig); byte[] buf = new byte[extract_length]; dest.Read(buf, 0, extract_length); List <float> ratios = new List <float>(); for (int offset = 0; offset < extract_length; offset += sizeof(float)) { float flo = BitConverter.ToSingle(buf, offset); ratios.Add(flo); } /* * ◆FIGU * スライダの位置。値は float型で 0.0 .. 1.0 * 0: 姉妹 * 1: うで * 2: あし * 3: 胴まわり * 4: おっぱい * 5: つり目たれ目 * 6: やわらか */ fig.slider_matrix.TallRatio = ratios[0]; fig.slider_matrix.ArmRatio = ratios[1]; fig.slider_matrix.LegRatio = ratios[2]; fig.slider_matrix.WaistRatio = ratios[3]; fig.slider_matrix.BustRatio = ratios[4]; fig.slider_matrix.EyeRatio = ratios[5]; //fig.TransformTpo(); }; png.Ftso += delegate(Stream dest, int extract_length, byte[] opt1) { TSOFile tso = new TSOFile(); tso.Load(dest); tso.Row = opt1[0]; fig.TSOFileList.Add(tso); }; Debug.WriteLine("loading " + source_file); png.Load(source_file); if (this.type == "HSAV") { BMPSaveData data = new BMPSaveData(); using (Stream stream = File.OpenRead(source_file)) data.Read(stream); fig.slider_matrix.TallRatio = data.GetSliderValue(4); fig.slider_matrix.ArmRatio = data.GetSliderValue(5); fig.slider_matrix.LegRatio = data.GetSliderValue(6); fig.slider_matrix.WaistRatio = data.GetSliderValue(7); fig.slider_matrix.BustRatio = data.GetSliderValue(0); fig.slider_matrix.EyeRatio = data.GetSliderValue(8); for (int i = 0; i < fig.TSOFileList.Count; i++) { TSOFile tso = fig.TSOFileList[i]; string file = data.GetFileName(tso.Row); if (file != "") { tso.FileName = Path.GetFileName(file); } else { tso.FileName = string.Format("{0:X2}", tso.Row); } } } } catch (Exception ex) { Console.WriteLine("Error: " + ex); } } }
public List <Figure> LoadPNGFile(Stream stream) { List <Figure> fig_list = new List <Figure>(); // if (File.Exists(source_file)) try { PNGFile png = new PNGFile(); Figure fig = null; TMOFile tmo = null; string png_type = null; png.Hsav += delegate(string type) { fig = new Figure(); fig_list.Add(fig); png_type = type; }; png.Lgta += delegate(Stream dest, int extract_length) { fig = new Figure(); fig_list.Add(fig); }; png.Ftmo += delegate(Stream dest, int extract_length) { tmo = new TMOFile(); tmo.Load(dest); fig.Tmo = tmo; }; png.Figu += delegate(Stream dest, int extract_length) { byte[] buf = new byte[extract_length]; dest.Read(buf, 0, extract_length); List <float> ratios = new List <float>(); for (int offset = 0; offset < extract_length; offset += sizeof(float)) { float flo = BitConverter.ToSingle(buf, offset); ratios.Add(flo); } /* * ◆FIGU * スライダの位置。値は float型で 0.0 .. 1.0 * 0: 姉妹 * 1: うで * 2: あし * 3: 胴まわり * 4: おっぱい * 5: つり目たれ目 * 6: やわらか */ fig.slide_matrices.TallRatio = ratios[0]; fig.slide_matrices.ArmRatio = ratios[1]; fig.slide_matrices.LegRatio = ratios[2]; fig.slide_matrices.WaistRatio = ratios[3]; fig.slide_matrices.BustRatio = ratios[4]; fig.slide_matrices.EyeRatio = ratios[5]; fig.TransformTpo(); }; png.Ftso += delegate(Stream dest, int extract_length, byte[] opt1) { TSOFile tso = new TSOFile(); tso.Load(dest); Debug.WriteLine("tso sum vertices count: " + tso.SumVerticesCount().ToString()); fig.TSOList.Add(tso); }; //Debug.WriteLine("loading " + source_file); png.Load(stream); if (png_type == "HSAV") { MemoryStream ms = new MemoryStream(); png.Save(ms); ms.Seek(0, SeekOrigin.Begin); BMPSaveData data = new BMPSaveData(); data.Read(ms); fig.slide_matrices.TallRatio = data.proportions[1]; fig.slide_matrices.ArmRatio = data.proportions[2]; fig.slide_matrices.LegRatio = data.proportions[3]; fig.slide_matrices.WaistRatio = data.proportions[4]; fig.slide_matrices.BustRatio = data.proportions[0]; fig.slide_matrices.EyeRatio = data.proportions[5]; } } catch (Exception ex) { Console.WriteLine("Error: " + ex); } return(fig_list); }
/// <summary> /// フィギュアを描画します。 /// </summary> protected override void DrawFigure() { device.SetRenderState(RenderStates.AlphaBlendEnable, true); device.SetRenderTarget(0, dev_surface); device.DepthStencilSurface = dev_zbuf; device.Clear(ClearFlags.Target | ClearFlags.ZBuffer | ClearFlags.Stencil, ScreenColor, 1.0f, 0); device.VertexDeclaration = vd; Figure fig; if (TryGetFigure(out fig)) { switch (mesh_view_mode) { case MeshViewMode.Toon: switch (mesh_selection_mode) { case MeshSelectionMode.AllMeshes: { foreach (TSOFile tso in fig.TSOList) { tso.BeginRender(); foreach (TSOMesh mesh in tso.meshes) { foreach (TSOSubMesh sub_mesh in mesh.sub_meshes) { DrawSubMeshForToonRendering(fig, tso, sub_mesh); } } tso.EndRender(); } } break; case MeshSelectionMode.SelectedMesh: if (SelectedTSOFile != null && SelectedMesh != null) { { TSOFile tso = SelectedTSOFile; tso.BeginRender(); foreach (TSOSubMesh sub_mesh in SelectedMesh.sub_meshes) { DrawSubMeshForToonRendering(fig, tso, sub_mesh); } tso.EndRender(); } } break; } break; case MeshViewMode.Heat: switch (mesh_selection_mode) { case MeshSelectionMode.AllMeshes: { foreach (TSOFile tso in fig.TSOList) { tso.BeginRender(); foreach (TSOMesh mesh in tso.meshes) { foreach (TSOSubMesh sub_mesh in mesh.sub_meshes) { DrawSubMeshForWeightHeating(fig, sub_mesh); } } tso.EndRender(); } } break; case MeshSelectionMode.SelectedMesh: if (SelectedTSOFile != null && SelectedMesh != null) { TSOFile tso = SelectedTSOFile; tso.BeginRender(); foreach (TSOSubMesh sub_mesh in SelectedMesh.sub_meshes) { DrawSubMeshForWeightHeating(fig, sub_mesh); } tso.EndRender(); } break; } break; case MeshViewMode.Wire: switch (mesh_selection_mode) { case MeshSelectionMode.AllMeshes: { foreach (TSOFile tso in fig.TSOList) { tso.BeginRender(); foreach (TSOMesh mesh in tso.meshes) { foreach (TSOSubMesh sub_mesh in mesh.sub_meshes) { DrawSubMeshForWireFrame(fig, tso, sub_mesh); } } tso.EndRender(); } } break; case MeshSelectionMode.SelectedMesh: if (SelectedTSOFile != null && SelectedMesh != null) { { TSOFile tso = SelectedTSOFile; tso.BeginRender(); foreach (TSOSubMesh sub_mesh in SelectedMesh.sub_meshes) { DrawSubMeshForWireFrame(fig, tso, sub_mesh); } tso.EndRender(); } } break; } break; } } }
/// <summary> /// 指定パスからPNGFileを読み込みフィギュアを作成します。 /// </summary> /// <param name="source_file">PNGFileのパス</param> public void Load(string source_file) { if (File.Exists(source_file)) try { PNGFile png = new PNGFile(); Figure fig = null; png.Hsav += delegate(string type) { this.type = type; fig = new Figure(); this.figures.Add(fig); }; png.Pose += delegate(string type) { this.type = type; }; png.Scne += delegate(string type) { this.type = type; }; png.Cami += delegate(Stream dest, int extract_length) { byte[] buf = new byte[extract_length]; dest.Read(buf, 0, extract_length); List<float> factor = new List<float>(); for (int offset = 0; offset < extract_length; offset += sizeof(float)) { float flo = BitConverter.ToSingle(buf, offset); factor.Add(flo); } if (CameraUpdate != null) { Vector3 translation = new Vector3(-factor[0], -factor[1], -factor[2]); Vector3 angle = new Vector3(-factor[5], -factor[4], -factor[6]); CameraUpdate(translation, angle); } }; png.Lgta += delegate(Stream dest, int extract_length) { byte[] buf = new byte[extract_length]; dest.Read(buf, 0, extract_length); List<float> factor = new List<float>(); for (int offset = 0; offset < extract_length; offset += sizeof(float)) { float flo = BitConverter.ToSingle(buf, offset); factor.Add(flo); } Matrix m; m.M11 = factor[0]; m.M12 = factor[1]; m.M13 = factor[2]; m.M14 = factor[3]; m.M21 = factor[4]; m.M22 = factor[5]; m.M23 = factor[6]; m.M24 = factor[7]; m.M31 = factor[8]; m.M32 = factor[9]; m.M33 = factor[10]; m.M34 = factor[11]; m.M41 = factor[12]; m.M42 = factor[13]; m.M43 = factor[14]; m.M44 = factor[15]; this.LightDirection = Vector3.TransformCoordinate(new Vector3(0.0f, 0.0f, -1.0f), m); }; png.Ftmo += delegate(Stream dest, int extract_length) { this.Tmo = new TMOFile(); this.Tmo.Load(dest); }; png.Figu += delegate(Stream dest, int extract_length) { fig = new Figure(); fig.LightDirection = this.LightDirection; fig.Tmo = this.Tmo; this.figures.Add(fig); byte[] buf = new byte[extract_length]; dest.Read(buf, 0, extract_length); List<float> ratios = new List<float>(); for (int offset = 0; offset < extract_length; offset += sizeof(float)) { float flo = BitConverter.ToSingle(buf, offset); ratios.Add(flo); } /* ◆FIGU スライダの位置。値は float型で 0.0 .. 1.0 0: 姉妹 1: うで 2: あし 3: 胴まわり 4: おっぱい 5: つり目たれ目 6: やわらか */ fig.slider_matrix.TallRatio = ratios[0]; fig.slider_matrix.ArmRatio = ratios[1]; fig.slider_matrix.LegRatio = ratios[2]; fig.slider_matrix.WaistRatio = ratios[3]; fig.slider_matrix.BustRatio = ratios[4]; fig.slider_matrix.EyeRatio = ratios[5]; //fig.TransformTpo(); }; png.Ftso += delegate(Stream dest, int extract_length, byte[] opt1) { TSOFile tso = new TSOFile(); tso.Load(dest); tso.Row = opt1[0]; fig.TSOFileList.Add(tso); }; Debug.WriteLine("loading " + source_file); png.Load(source_file); if (this.type == "HSAV") { BMPSaveData data = new BMPSaveData(); using (Stream stream = File.OpenRead(source_file)) data.Read(stream); fig.slider_matrix.TallRatio = data.GetSliderValue(4); fig.slider_matrix.ArmRatio = data.GetSliderValue(5); fig.slider_matrix.LegRatio = data.GetSliderValue(6); fig.slider_matrix.WaistRatio = data.GetSliderValue(7); fig.slider_matrix.BustRatio = data.GetSliderValue(0); fig.slider_matrix.EyeRatio = data.GetSliderValue(8); for (int i = 0; i < fig.TSOFileList.Count; i++) { TSOFile tso = fig.TSOFileList[i]; string file = data.GetFileName(tso.Row); if (file != "") tso.FileName = Path.GetFileName(file); else tso.FileName = string.Format("{0:X2}", tso.Row); } } } catch (Exception ex) { Console.WriteLine("Error: " + ex); } }
/// メッシュ操作を生成します。 public SmoothMeshCommand(TSOFile tso, TSOMesh mesh, TSONode selected_node) { this.tso = tso; this.mesh = mesh; this.selected_node = selected_node; }
/// <summary> /// 指定ストリームからTSOFileを読み込みます。 /// </summary> /// <param name="source_stream">ストリーム</param> /// <param name="file">ファイル名</param> public void LoadTSOFile(Stream source_stream, string file) { List<TSOFile> tso_list = new List<TSOFile>(); try { TSOFile tso = new TSOFile(); tso.Load(source_stream); tso.FileName = file != null ? Path.GetFileNameWithoutExtension(file) : null; tso_list.Add(tso); } catch (Exception ex) { Console.WriteLine("Error: " + ex); } Figure fig = GetSelectedOrCreateFigure(); foreach (TSOFile tso in tso_list) { if (TSOFileOpen != null) TSOFileOpen(tso); fig.TSOFileList.Add(tso); } fig.UpdateNodeMapAndBoneMatrices(); if (FigureEvent != null) FigureEvent(this, EventArgs.Empty); }
/// <summary> /// 指定ディレクトリからフィギュアを作成して追加します。 /// </summary> /// <param name="source_file">TSOFileを含むディレクトリ</param> public void AddFigureFromTSODirectory(string source_file) { List<TSOFile> tso_list = new List<TSOFile>(); try { string[] files = Directory.GetFiles(source_file, "*.TSO"); foreach (string file in files) { TSOFile tso = new TSOFile(); Debug.WriteLine("loading " + file); tso.Load(file); tso_list.Add(tso); } } catch (Exception ex) { Console.WriteLine("Error: " + ex); } Figure fig = new Figure(); foreach (TSOFile tso in tso_list) { if (TSOFileOpen != null) TSOFileOpen(tso); fig.TSOFileList.Add(tso); } fig.UpdateNodeMapAndBoneMatrices(); int idx = FigureList.Count; FigureList.Add(fig); SetFigureIndex(idx); if (FigureEvent != null) FigureEvent(this, EventArgs.Empty); }
/// <summary> /// tsoをUIに設定します。 /// </summary> /// <param name="tso">tso</param> public void SetTSOFile(TSOFile tso) { this.tso = tso; lvSubScripts.Items.Clear(); foreach (TSOSubScript sub_script in tso.sub_scripts) { ListViewItem li = new ListViewItem(sub_script.Name); li.SubItems.Add(sub_script.FileName); li.Tag = sub_script; lvSubScripts.Items.Add(li); } lvSubScripts.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize); }
/// <summary> /// tsoに対するnodemapを追加します。 /// </summary> /// <param name="tso">tso</param> protected void AddNodeMap(TSOFile tso) { foreach (TSONode tso_node in tso.nodes) { TMONode tmo_node; if (tmo.nodemap.TryGetValue(tso_node.Path, out tmo_node)) nodemap.Add(tso_node, tmo_node); } }
/// <summary> /// フィギュアに含まれるnode treeを描画する。 /// </summary> void DrawNodeTree() { TSOFile tso = selected_tso_file; if (tso == null) { return; } Line line = new Line(device); foreach (TSONode node in tso.nodes) { if (HiddenNode(node)) { continue; } Vector3 p0 = GetNodePositionOnScreen(node); p0.Z = 0.0f; TSONode parent_node = node.parent; if (parent_node != null) { if (HiddenNode(parent_node)) { continue; } Vector3 p1 = GetNodePositionOnScreen(parent_node); p1.Z = 0.0f; Vector3 pd = p0 - p1; float len = Vector3.Length(pd); float scale = 4.0f / len; Vector2 p3 = new Vector2(p1.X + pd.Y * scale, p1.Y - pd.X * scale); Vector2 p4 = new Vector2(p1.X - pd.Y * scale, p1.Y + pd.X * scale); Vector2[] vertices = new Vector2[3]; vertices[0] = new Vector2(p3.X, p3.Y); vertices[1] = new Vector2(p0.X, p0.Y); vertices[2] = new Vector2(p4.X, p4.Y); line.Draw(vertices, TSONodeLineColor); } } line.Dispose(); line = null; Rectangle rect = new Rectangle(0, 16, 15, 15); //node circle Vector3 rect_center = new Vector3(7, 7, 0); sprite.Begin(SpriteFlags.None); foreach (TSONode node in tso.nodes) { if (HiddenNode(node)) { continue; } Vector3 p0 = GetNodePositionOnScreen(node); p0.Z = 0.0f; sprite.Draw(dot_texture, rect, rect_center, p0, Color.White); } sprite.End(); }