fix: .gitignore + add 44 language JSON files

This commit is contained in:
shuiyu1123 committed 2026-07-14 01:17:33 +08:00
1 parent e8fd68e27b
commit 8de545dd2c
227 files changed
+22603

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>editor</AssemblyName>
<RootNamespace>Lity3DEditor</RootNamespace>
<PublishSingleFile>false</PublishSingleFile>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>
+369
View File
@@ -0,0 +1,369 @@
using System.ComponentModel;
namespace Lity3DEditor;
public class MainForm : Form
{
private List<SceneNode> _nodes = new();
private string? _currentPath;
private bool _modified;
private TreeView _tree = null!;
private PropertyGrid _propertyGrid = null!;
private StatusStrip _status = null!;
private ToolStripButton _btnSave = null!;
public MainForm()
{
Text = "Lity3D Engine Scene Editor";
Size = new Size(1280, 800);
StartPosition = FormStartPosition.CenterScreen;
BackColor = Color.FromArgb(30, 30, 38);
Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point);
BuildMenu();
BuildToolbar();
BuildPanels();
BuildStatus();
Load += (_, _) =>
{
foreach (var c in Controls.OfType<SplitContainer>())
{
if (c.Orientation == Orientation.Vertical)
{
c.Panel1MinSize = 180;
c.Panel2MinSize = 300;
c.SplitterDistance = 260;
}
else
{
c.Panel1MinSize = 200;
c.Panel2MinSize = 120;
c.SplitterDistance = c.Height - 220;
}
}
TryOpenScene(Path.Combine(Application.StartupPath, "scene.txt"));
};
FormClosing += (_, e) => { if (!PromptSave()) e.Cancel = true; };
}
private void BuildMenu()
{
var menu = new MenuStrip();
var file = new ToolStripMenuItem("文件(&F)");
file.DropDownItems.Add(new ToolStripMenuItem("打开...", null, (_, _) => OpenScene()) { ShortcutKeys = Keys.Control | Keys.O });
file.DropDownItems.Add(new ToolStripMenuItem("保存", null, (_, _) => SaveScene()) { ShortcutKeys = Keys.Control | Keys.S });
file.DropDownItems.Add(new ToolStripMenuItem("另存为...", null, (_, _) => SaveSceneAs()));
file.DropDownItems.Add(new ToolStripSeparator());
file.DropDownItems.Add(new ToolStripMenuItem("退出", null, (_, _) => Close()) { ShortcutKeys = Keys.Alt | Keys.F4 });
menu.Items.Add(file);
var edit = new ToolStripMenuItem("编辑(&E)");
edit.DropDownItems.Add(new ToolStripMenuItem("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
edit.DropDownItems.Add(new ToolStripSeparator());
edit.DropDownItems.Add(new ToolStripMenuItem("删除选中", null, (_, _) => DeleteSelected()) { ShortcutKeys = Keys.Delete });
menu.Items.Add(edit);
MainMenuStrip = menu;
Controls.Add(menu);
}
private void BuildToolbar()
{
var tool = new ToolStrip();
tool.GripStyle = ToolStripGripStyle.Hidden;
tool.BackColor = Color.FromArgb(40, 40, 50);
tool.ForeColor = Color.White;
tool.Renderer = new DarkToolStripRenderer();
_btnSave = new ToolStripButton("保存", null, (_, _) => SaveScene()) { Enabled = false };
tool.Items.Add(_btnSave);
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
tool.Items.Add(new ToolStripButton("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
tool.Items.Add(new ToolStripButton("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("删除", null, (_, _) => DeleteSelected()));
tool.Dock = DockStyle.Top;
Controls.Add(tool);
}
private void BuildPanels()
{
var split = new SplitContainer
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(30, 30, 38)
};
split.Panel1.BackColor = Color.FromArgb(35, 35, 45);
split.Panel2.BackColor = Color.FromArgb(35, 35, 45);
_tree = new TreeView
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
BorderStyle = BorderStyle.None,
FullRowSelect = true,
HideSelection = false,
ShowLines = true,
ShowPlusMinus = true,
LineColor = Color.FromArgb(100, 120, 160)
};
_tree.AfterSelect += (_, _) => OnNodeSelected();
split.Panel1.Controls.Add(_tree);
var rightSplit = new SplitContainer
{
Dock = DockStyle.Fill,
Orientation = Orientation.Horizontal,
BackColor = Color.FromArgb(30, 30, 38)
};
rightSplit.Panel1.BackColor = Color.FromArgb(35, 35, 45);
rightSplit.Panel2.BackColor = Color.FromArgb(28, 28, 36);
_propertyGrid = new PropertyGrid
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
CategoryForeColor = Color.FromArgb(180, 180, 200),
CommandsBackColor = Color.FromArgb(35, 35, 45),
CommandsForeColor = Color.White,
DisabledItemForeColor = Color.Gray,
HelpBackColor = Color.FromArgb(28, 28, 36),
HelpForeColor = Color.White,
ViewBackColor = Color.FromArgb(35, 35, 45),
ViewForeColor = Color.White,
LineColor = Color.FromArgb(60, 60, 80),
ToolbarVisible = false,
PropertySort = PropertySort.Categorized
};
_propertyGrid.PropertyValueChanged += (_, _) => OnPropertyChanged();
rightSplit.Panel1.Controls.Add(_propertyGrid);
var previewLabel = new Label
{
Dock = DockStyle.Fill,
Text = "3D 预览区\n(由 Lity3D Engine 运行时渲染)",
ForeColor = Color.FromArgb(120, 120, 140),
BackColor = Color.FromArgb(28, 28, 36),
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font(Font.FontFamily, 11f, FontStyle.Regular)
};
rightSplit.Panel2.Controls.Add(previewLabel);
split.Panel2.Controls.Add(rightSplit);
Controls.Add(split);
}
private void BuildStatus()
{
_status = new StatusStrip();
_status.BackColor = Color.FromArgb(40, 40, 50);
_status.ForeColor = Color.White;
_status.Items.Add(new ToolStripStatusLabel("就绪"));
Controls.Add(_status);
}
private void TryOpenScene(string path)
{
if (!File.Exists(path)) return;
try
{
_nodes = SceneIO.Load(path);
_currentPath = path;
RebuildTree();
SetModified(false);
SetStatus($"已打开 {path}");
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "打开失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void OpenScene()
{
if (!PromptSave()) return;
using var dlg = new OpenFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "打开场景"
};
if (dlg.ShowDialog(this) == DialogResult.OK)
TryOpenScene(dlg.FileName);
}
private bool SaveScene()
{
if (string.IsNullOrEmpty(_currentPath))
return SaveSceneAs();
try
{
SceneIO.Save(_currentPath, _nodes);
SetModified(false);
SetStatus($"已保存 {_currentPath}");
return true;
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private bool SaveSceneAs()
{
using var dlg = new SaveFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "另存为"
};
if (dlg.ShowDialog(this) != DialogResult.OK) return false;
_currentPath = dlg.FileName;
return SaveScene();
}
private bool PromptSave()
{
if (!_modified) return true;
var r = MessageBox.Show(this, "场景已修改,是否保存?", "保存确认",
MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Cancel) return false;
if (r == DialogResult.Yes) return SaveScene();
return true;
}
private void AddNode(MeshType type, string baseName)
{
int idx = 1;
string name = baseName;
while (_nodes.Any(n => n.Name == name))
name = $"{baseName}_{idx++}";
var parentIdx = -1;
if (_tree.SelectedNode?.Tag is SceneNode sel && sel.Name != "root")
parentIdx = _nodes.IndexOf(sel);
else if (_nodes.Count > 0)
parentIdx = 0; // root
var node = new SceneNode
{
Name = name,
ParentIndex = parentIdx,
Mesh = type,
PosY = type == MeshType.PLANE ? -1.0f : 0.0f
};
_nodes.Add(node);
RebuildTree();
SelectNode(node);
SetModified(true);
SetStatus($"添加 {name}");
}
private void DeleteSelected()
{
if (_tree.SelectedNode?.Tag is not SceneNode node || node.Name == "root")
{
MessageBox.Show(this, "不能删除根节点", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
_nodes.Remove(node);
RebuildTree();
SetModified(true);
SetStatus($"删除 {node.Name}");
}
private void RebuildTree()
{
_tree.Nodes.Clear();
var treeMap = new Dictionary<int, TreeNode>();
for (int i = 0; i < _nodes.Count; i++)
{
var n = _nodes[i];
var tn = new TreeNode(n.Name) { Tag = n, ForeColor = Color.White };
treeMap[i] = tn;
if (n.ParentIndex >= 0 && treeMap.TryGetValue(n.ParentIndex, out var parent))
parent.Nodes.Add(tn);
else
_tree.Nodes.Add(tn);
}
_tree.ExpandAll();
}
private void SelectNode(SceneNode node)
{
foreach (TreeNode root in _tree.Nodes)
{
var found = FindNode(root, node);
if (found != null)
{
_tree.SelectedNode = found;
return;
}
}
}
private static TreeNode? FindNode(TreeNode parent, SceneNode node)
{
if (parent.Tag == node) return parent;
foreach (TreeNode c in parent.Nodes)
{
var r = FindNode(c, node);
if (r != null) return r;
}
return null;
}
private void OnNodeSelected()
{
_propertyGrid.SelectedObject = _tree.SelectedNode?.Tag;
if (_tree.SelectedNode?.Tag is SceneNode n)
SetStatus(n.Name);
}
private void OnPropertyChanged()
{
SetModified(true);
_tree.Refresh();
}
private void SetModified(bool modified)
{
_modified = modified;
_btnSave.Enabled = modified;
Text = "Lity3D Engine Scene Editor" + (modified ? " *" : "") + (string.IsNullOrEmpty(_currentPath) ? "" : $" - {_currentPath}");
}
private void SetStatus(string msg)
{
if (_status.Items[0] is ToolStripStatusLabel lbl)
lbl.Text = msg;
}
}
public class DarkToolStripRenderer : ToolStripProfessionalRenderer
{
public DarkToolStripRenderer() : base(new DarkColorTable()) { }
}
public class DarkColorTable : ProfessionalColorTable
{
public override Color ToolStripBorder => Color.FromArgb(60, 60, 80);
public override Color ToolStripGradientBegin => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientEnd => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientMiddle => Color.FromArgb(45, 45, 55);
public override Color ButtonSelectedHighlight => Color.FromArgb(70, 70, 90);
public override Color ButtonSelectedBorder => Color.FromArgb(100, 120, 160);
public override Color ButtonPressedHighlight => Color.FromArgb(60, 80, 120);
public override Color ButtonCheckedHighlight => Color.FromArgb(60, 80, 120);
public override Color MenuBorder => Color.FromArgb(60, 60, 80);
public override Color MenuItemSelected => Color.FromArgb(70, 70, 90);
public override Color MenuItemBorder => Color.FromArgb(100, 120, 160);
}
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Update="Form1.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>
+11
View File
@@ -0,0 +1,11 @@
namespace Lity3DEditor;
static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
+56
View File
@@ -0,0 +1,56 @@
using System.Globalization;
namespace Lity3DEditor;
public static class SceneIO
{
public static List<SceneNode> Load(string path)
{
var nodes = new List<SceneNode>();
using var reader = new StreamReader(path);
string? header = reader.ReadLine();
if (header == null || !header.StartsWith("LITY3D_SCENE"))
throw new InvalidDataException("不是有效的 Lity3D 场景文件");
int lineIdx = 1;
string? line;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line)) continue;
var parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 13) continue;
var node = new SceneNode
{
Name = parts[0],
ParentIndex = int.Parse(parts[1]),
Mesh = Enum.Parse<MeshType>(parts[2], true),
MeshPath = parts[3],
PosX = float.Parse(parts[4], CultureInfo.InvariantCulture),
PosY = float.Parse(parts[5], CultureInfo.InvariantCulture),
PosZ = float.Parse(parts[6], CultureInfo.InvariantCulture),
RotX = float.Parse(parts[7], CultureInfo.InvariantCulture),
RotY = float.Parse(parts[8], CultureInfo.InvariantCulture),
RotZ = float.Parse(parts[9], CultureInfo.InvariantCulture),
ScaleX = float.Parse(parts[10], CultureInfo.InvariantCulture),
ScaleY = float.Parse(parts[11], CultureInfo.InvariantCulture),
ScaleZ = float.Parse(parts[12], CultureInfo.InvariantCulture),
LineIndex = lineIdx
};
nodes.Add(node);
lineIdx++;
}
return nodes;
}
public static void Save(string path, List<SceneNode> nodes)
{
using var writer = new StreamWriter(path);
writer.WriteLine("LITY3D_SCENE 1");
foreach (var node in nodes)
{
int parent = node.ParentIndex;
writer.WriteLine(node.ToSceneLine(parent));
}
}
}
+52
View File
@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.Globalization;
namespace Lity3DEditor;
public enum MeshType
{
[Description("无")]
NONE,
[Description("立方体")]
CUBE,
[Description("球体")]
SPHERE,
[Description("平面")]
PLANE,
[Description("OBJ 模型")]
OBJ
}
public class SceneNode
{
public string Name { get; set; } = "";
public int ParentIndex { get; set; } = -1;
public MeshType Mesh { get; set; } = MeshType.NONE;
public string MeshPath { get; set; } = "-";
public float PosX { get; set; }
public float PosY { get; set; }
public float PosZ { get; set; }
public float RotX { get; set; }
public float RotY { get; set; }
public float RotZ { get; set; }
public float ScaleX { get; set; } = 1.0f;
public float ScaleY { get; set; } = 1.0f;
public float ScaleZ { get; set; } = 1.0f;
[Browsable(false)]
public int LineIndex { get; set; }
public override string ToString() => Name;
public string ToSceneLine(int parentIndex)
{
ParentIndex = parentIndex;
return string.Format(CultureInfo.InvariantCulture,
"{0} {1} {2} {3} {4:F4} {5:F4} {6:F4} {7:F4} {8:F4} {9:F4} {10:F4} {11:F4} {12:F4}",
Name, parentIndex, Mesh, MeshPath,
PosX, PosY, PosZ, RotX, RotY, RotZ, ScaleX, ScaleY, ScaleZ);
}
}
+15
View File
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>editor</AssemblyName>
<RootNamespace>Lity3DEditor</RootNamespace>
<PublishSingleFile>false</PublishSingleFile>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>
+369
View File
@@ -0,0 +1,369 @@
using System.ComponentModel;
namespace Lity3DEditor;
public class MainForm : Form
{
private List<SceneNode> _nodes = new();
private string? _currentPath;
private bool _modified;
private TreeView _tree = null!;
private PropertyGrid _propertyGrid = null!;
private StatusStrip _status = null!;
private ToolStripButton _btnSave = null!;
public MainForm()
{
Text = "Lity3D Engine Scene Editor";
Size = new Size(1280, 800);
StartPosition = FormStartPosition.CenterScreen;
BackColor = Color.FromArgb(30, 30, 38);
Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point);
BuildMenu();
BuildToolbar();
BuildPanels();
BuildStatus();
Load += (_, _) =>
{
foreach (var c in Controls.OfType<SplitContainer>())
{
if (c.Orientation == Orientation.Vertical)
{
c.Panel1MinSize = 180;
c.Panel2MinSize = 300;
c.SplitterDistance = 260;
}
else
{
c.Panel1MinSize = 200;
c.Panel2MinSize = 120;
c.SplitterDistance = c.Height - 220;
}
}
TryOpenScene(Path.Combine(Application.StartupPath, "scene.txt"));
};
FormClosing += (_, e) => { if (!PromptSave()) e.Cancel = true; };
}
private void BuildMenu()
{
var menu = new MenuStrip();
var file = new ToolStripMenuItem("文件(&F)");
file.DropDownItems.Add(new ToolStripMenuItem("打开...", null, (_, _) => OpenScene()) { ShortcutKeys = Keys.Control | Keys.O });
file.DropDownItems.Add(new ToolStripMenuItem("保存", null, (_, _) => SaveScene()) { ShortcutKeys = Keys.Control | Keys.S });
file.DropDownItems.Add(new ToolStripMenuItem("另存为...", null, (_, _) => SaveSceneAs()));
file.DropDownItems.Add(new ToolStripSeparator());
file.DropDownItems.Add(new ToolStripMenuItem("退出", null, (_, _) => Close()) { ShortcutKeys = Keys.Alt | Keys.F4 });
menu.Items.Add(file);
var edit = new ToolStripMenuItem("编辑(&E)");
edit.DropDownItems.Add(new ToolStripMenuItem("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
edit.DropDownItems.Add(new ToolStripMenuItem("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
edit.DropDownItems.Add(new ToolStripSeparator());
edit.DropDownItems.Add(new ToolStripMenuItem("删除选中", null, (_, _) => DeleteSelected()) { ShortcutKeys = Keys.Delete });
menu.Items.Add(edit);
MainMenuStrip = menu;
Controls.Add(menu);
}
private void BuildToolbar()
{
var tool = new ToolStrip();
tool.GripStyle = ToolStripGripStyle.Hidden;
tool.BackColor = Color.FromArgb(40, 40, 50);
tool.ForeColor = Color.White;
tool.Renderer = new DarkToolStripRenderer();
_btnSave = new ToolStripButton("保存", null, (_, _) => SaveScene()) { Enabled = false };
tool.Items.Add(_btnSave);
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("添加立方体", null, (_, _) => AddNode(MeshType.CUBE, "cube")));
tool.Items.Add(new ToolStripButton("添加球体", null, (_, _) => AddNode(MeshType.SPHERE, "sphere")));
tool.Items.Add(new ToolStripButton("添加平面", null, (_, _) => AddNode(MeshType.PLANE, "plane")));
tool.Items.Add(new ToolStripSeparator());
tool.Items.Add(new ToolStripButton("删除", null, (_, _) => DeleteSelected()));
tool.Dock = DockStyle.Top;
Controls.Add(tool);
}
private void BuildPanels()
{
var split = new SplitContainer
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(30, 30, 38)
};
split.Panel1.BackColor = Color.FromArgb(35, 35, 45);
split.Panel2.BackColor = Color.FromArgb(35, 35, 45);
_tree = new TreeView
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
BorderStyle = BorderStyle.None,
FullRowSelect = true,
HideSelection = false,
ShowLines = true,
ShowPlusMinus = true,
LineColor = Color.FromArgb(100, 120, 160)
};
_tree.AfterSelect += (_, _) => OnNodeSelected();
split.Panel1.Controls.Add(_tree);
var rightSplit = new SplitContainer
{
Dock = DockStyle.Fill,
Orientation = Orientation.Horizontal,
BackColor = Color.FromArgb(30, 30, 38)
};
rightSplit.Panel1.BackColor = Color.FromArgb(35, 35, 45);
rightSplit.Panel2.BackColor = Color.FromArgb(28, 28, 36);
_propertyGrid = new PropertyGrid
{
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(35, 35, 45),
ForeColor = Color.White,
CategoryForeColor = Color.FromArgb(180, 180, 200),
CommandsBackColor = Color.FromArgb(35, 35, 45),
CommandsForeColor = Color.White,
DisabledItemForeColor = Color.Gray,
HelpBackColor = Color.FromArgb(28, 28, 36),
HelpForeColor = Color.White,
ViewBackColor = Color.FromArgb(35, 35, 45),
ViewForeColor = Color.White,
LineColor = Color.FromArgb(60, 60, 80),
ToolbarVisible = false,
PropertySort = PropertySort.Categorized
};
_propertyGrid.PropertyValueChanged += (_, _) => OnPropertyChanged();
rightSplit.Panel1.Controls.Add(_propertyGrid);
var previewLabel = new Label
{
Dock = DockStyle.Fill,
Text = "3D 预览区\n(由 Lity3D Engine 运行时渲染)",
ForeColor = Color.FromArgb(120, 120, 140),
BackColor = Color.FromArgb(28, 28, 36),
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font(Font.FontFamily, 11f, FontStyle.Regular)
};
rightSplit.Panel2.Controls.Add(previewLabel);
split.Panel2.Controls.Add(rightSplit);
Controls.Add(split);
}
private void BuildStatus()
{
_status = new StatusStrip();
_status.BackColor = Color.FromArgb(40, 40, 50);
_status.ForeColor = Color.White;
_status.Items.Add(new ToolStripStatusLabel("就绪"));
Controls.Add(_status);
}
private void TryOpenScene(string path)
{
if (!File.Exists(path)) return;
try
{
_nodes = SceneIO.Load(path);
_currentPath = path;
RebuildTree();
SetModified(false);
SetStatus($"已打开 {path}");
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "打开失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void OpenScene()
{
if (!PromptSave()) return;
using var dlg = new OpenFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "打开场景"
};
if (dlg.ShowDialog(this) == DialogResult.OK)
TryOpenScene(dlg.FileName);
}
private bool SaveScene()
{
if (string.IsNullOrEmpty(_currentPath))
return SaveSceneAs();
try
{
SceneIO.Save(_currentPath, _nodes);
SetModified(false);
SetStatus($"已保存 {_currentPath}");
return true;
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private bool SaveSceneAs()
{
using var dlg = new SaveFileDialog
{
Filter = "Lity3D 场景|*.txt|所有文件|*.*",
Title = "另存为"
};
if (dlg.ShowDialog(this) != DialogResult.OK) return false;
_currentPath = dlg.FileName;
return SaveScene();
}
private bool PromptSave()
{
if (!_modified) return true;
var r = MessageBox.Show(this, "场景已修改,是否保存?", "保存确认",
MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (r == DialogResult.Cancel) return false;
if (r == DialogResult.Yes) return SaveScene();
return true;
}
private void AddNode(MeshType type, string baseName)
{
int idx = 1;
string name = baseName;
while (_nodes.Any(n => n.Name == name))
name = $"{baseName}_{idx++}";
var parentIdx = -1;
if (_tree.SelectedNode?.Tag is SceneNode sel && sel.Name != "root")
parentIdx = _nodes.IndexOf(sel);
else if (_nodes.Count > 0)
parentIdx = 0; // root
var node = new SceneNode
{
Name = name,
ParentIndex = parentIdx,
Mesh = type,
PosY = type == MeshType.PLANE ? -1.0f : 0.0f
};
_nodes.Add(node);
RebuildTree();
SelectNode(node);
SetModified(true);
SetStatus($"添加 {name}");
}
private void DeleteSelected()
{
if (_tree.SelectedNode?.Tag is not SceneNode node || node.Name == "root")
{
MessageBox.Show(this, "不能删除根节点", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
_nodes.Remove(node);
RebuildTree();
SetModified(true);
SetStatus($"删除 {node.Name}");
}
private void RebuildTree()
{
_tree.Nodes.Clear();
var treeMap = new Dictionary<int, TreeNode>();
for (int i = 0; i < _nodes.Count; i++)
{
var n = _nodes[i];
var tn = new TreeNode(n.Name) { Tag = n, ForeColor = Color.White };
treeMap[i] = tn;
if (n.ParentIndex >= 0 && treeMap.TryGetValue(n.ParentIndex, out var parent))
parent.Nodes.Add(tn);
else
_tree.Nodes.Add(tn);
}
_tree.ExpandAll();
}
private void SelectNode(SceneNode node)
{
foreach (TreeNode root in _tree.Nodes)
{
var found = FindNode(root, node);
if (found != null)
{
_tree.SelectedNode = found;
return;
}
}
}
private static TreeNode? FindNode(TreeNode parent, SceneNode node)
{
if (parent.Tag == node) return parent;
foreach (TreeNode c in parent.Nodes)
{
var r = FindNode(c, node);
if (r != null) return r;
}
return null;
}
private void OnNodeSelected()
{
_propertyGrid.SelectedObject = _tree.SelectedNode?.Tag;
if (_tree.SelectedNode?.Tag is SceneNode n)
SetStatus(n.Name);
}
private void OnPropertyChanged()
{
SetModified(true);
_tree.Refresh();
}
private void SetModified(bool modified)
{
_modified = modified;
_btnSave.Enabled = modified;
Text = "Lity3D Engine Scene Editor" + (modified ? " *" : "") + (string.IsNullOrEmpty(_currentPath) ? "" : $" - {_currentPath}");
}
private void SetStatus(string msg)
{
if (_status.Items[0] is ToolStripStatusLabel lbl)
lbl.Text = msg;
}
}
public class DarkToolStripRenderer : ToolStripProfessionalRenderer
{
public DarkToolStripRenderer() : base(new DarkColorTable()) { }
}
public class DarkColorTable : ProfessionalColorTable
{
public override Color ToolStripBorder => Color.FromArgb(60, 60, 80);
public override Color ToolStripGradientBegin => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientEnd => Color.FromArgb(40, 40, 50);
public override Color ToolStripGradientMiddle => Color.FromArgb(45, 45, 55);
public override Color ButtonSelectedHighlight => Color.FromArgb(70, 70, 90);
public override Color ButtonSelectedBorder => Color.FromArgb(100, 120, 160);
public override Color ButtonPressedHighlight => Color.FromArgb(60, 80, 120);
public override Color ButtonCheckedHighlight => Color.FromArgb(60, 80, 120);
public override Color MenuBorder => Color.FromArgb(60, 60, 80);
public override Color MenuItemSelected => Color.FromArgb(70, 70, 90);
public override Color MenuItemBorder => Color.FromArgb(100, 120, 160);
}
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Update="Form1.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>
+11
View File
@@ -0,0 +1,11 @@
namespace Lity3DEditor;
static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
+56
View File
@@ -0,0 +1,56 @@
using System.Globalization;
namespace Lity3DEditor;
public static class SceneIO
{
public static List<SceneNode> Load(string path)
{
var nodes = new List<SceneNode>();
using var reader = new StreamReader(path);
string? header = reader.ReadLine();
if (header == null || !header.StartsWith("LITY3D_SCENE"))
throw new InvalidDataException("不是有效的 Lity3D 场景文件");
int lineIdx = 1;
string? line;
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line)) continue;
var parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 13) continue;
var node = new SceneNode
{
Name = parts[0],
ParentIndex = int.Parse(parts[1]),
Mesh = Enum.Parse<MeshType>(parts[2], true),
MeshPath = parts[3],
PosX = float.Parse(parts[4], CultureInfo.InvariantCulture),
PosY = float.Parse(parts[5], CultureInfo.InvariantCulture),
PosZ = float.Parse(parts[6], CultureInfo.InvariantCulture),
RotX = float.Parse(parts[7], CultureInfo.InvariantCulture),
RotY = float.Parse(parts[8], CultureInfo.InvariantCulture),
RotZ = float.Parse(parts[9], CultureInfo.InvariantCulture),
ScaleX = float.Parse(parts[10], CultureInfo.InvariantCulture),
ScaleY = float.Parse(parts[11], CultureInfo.InvariantCulture),
ScaleZ = float.Parse(parts[12], CultureInfo.InvariantCulture),
LineIndex = lineIdx
};
nodes.Add(node);
lineIdx++;
}
return nodes;
}
public static void Save(string path, List<SceneNode> nodes)
{
using var writer = new StreamWriter(path);
writer.WriteLine("LITY3D_SCENE 1");
foreach (var node in nodes)
{
int parent = node.ParentIndex;
writer.WriteLine(node.ToSceneLine(parent));
}
}
}
+52
View File
@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.Globalization;
namespace Lity3DEditor;
public enum MeshType
{
[Description("无")]
NONE,
[Description("立方体")]
CUBE,
[Description("球体")]
SPHERE,
[Description("平面")]
PLANE,
[Description("OBJ 模型")]
OBJ
}
public class SceneNode
{
public string Name { get; set; } = "";
public int ParentIndex { get; set; } = -1;
public MeshType Mesh { get; set; } = MeshType.NONE;
public string MeshPath { get; set; } = "-";
public float PosX { get; set; }
public float PosY { get; set; }
public float PosZ { get; set; }
public float RotX { get; set; }
public float RotY { get; set; }
public float RotZ { get; set; }
public float ScaleX { get; set; } = 1.0f;
public float ScaleY { get; set; } = 1.0f;
public float ScaleZ { get; set; } = 1.0f;
[Browsable(false)]
public int LineIndex { get; set; }
public override string ToString() => Name;
public string ToSceneLine(int parentIndex)
{
ParentIndex = parentIndex;
return string.Format(CultureInfo.InvariantCulture,
"{0} {1} {2} {3} {4:F4} {5:F4} {6:F4} {7:F4} {8:F4} {9:F4} {10:F4} {11:F4} {12:F4}",
Name, parentIndex, Mesh, MeshPath,
PosX, PosY, PosZ, RotX, RotY, RotZ, ScaleX, ScaleY, ScaleZ);
}
}
@@ -0,0 +1,70 @@
{
"format": 1,
"restore": {
"C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj": {}
},
"projects": {
"C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"projectName": "editor",
"projectPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"packagesPath": "C:\\Users\\Administrator\\.nuget\\packages\\",
"outputPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\obj\\",
"projectStyle": "PackageReference",
"configFilePaths": [
"C:\\Users\\Administrator\\AppData\\Roaming\\NuGet\\NuGet.Config"
],
"originalTargetFrameworks": [
"net9.0-windows"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
},
"Microsoft.WindowsDesktop.App.WindowsForms": {
"privateAssets": "none"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.315/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Administrator\.nuget\packages\</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.3</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\Administrator\.nuget\packages\" />
</ItemGroup>
</Project>
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]
@@ -0,0 +1,24 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("editor")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0669d102cea79704d7f44edeff1f40db4677933a")]
[assembly: System.Reflection.AssemblyProductAttribute("editor")]
[assembly: System.Reflection.AssemblyTitleAttribute("editor")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
[assembly: System.Runtime.Versioning.TargetPlatformAttribute("Windows7.0")]
[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
// 由 MSBuild WriteCodeFragment 类生成。
@@ -0,0 +1 @@
eccab4cf931872db70f4fcf49f5ea48daaa37596c654c8ae7292da5835f1d1aa
@@ -0,0 +1,22 @@
is_global = true
build_property.ApplicationManifest =
build_property.StartupObject =
build_property.ApplicationDefaultFont =
build_property.ApplicationHighDpiMode =
build_property.ApplicationUseCompatibleTextRendering =
build_property.ApplicationVisualStyles =
build_property.TargetFramework = net9.0-windows
build_property.TargetPlatformMinVersion = 7.0
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Lity3DEditor
build_property.ProjectDir = C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =
@@ -0,0 +1,10 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.Drawing;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
global using global::System.Windows.Forms;
@@ -0,0 +1 @@
778c6fb97436ff7fada39ea9df7644fb35174eb8848c29619b96adb0a6265bee
@@ -0,0 +1,14 @@
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.exe
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.deps.json
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.runtimeconfig.json
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.dll
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\build\editor.pdb
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.GeneratedMSBuildEditorConfig.editorconfig
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.AssemblyInfoInputs.cache
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.AssemblyInfo.cs
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.csproj.CoreCompileInputs.cache
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\editor.dll
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\refint\editor.dll
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\editor.pdb
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\Lity3DEditor.genruntimeconfig.cache
C:\Users\Administrator\Downloads\mimocode-windows-x64\GameEngineASM\EditorCS\obj\Release\net9.0-windows\ref\editor.dll
@@ -0,0 +1 @@
25097d32ce528727f2896c2b3b57b55035840d12dbe72050663b5fc129c5cec5
Binary file not shown.
+75
View File
@@ -0,0 +1,75 @@
{
"version": 3,
"targets": {
"net9.0-windows7.0": {}
},
"libraries": {},
"projectFileDependencyGroups": {
"net9.0-windows7.0": []
},
"packageFolders": {
"C:\\Users\\Administrator\\.nuget\\packages\\": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"projectName": "editor",
"projectPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"packagesPath": "C:\\Users\\Administrator\\.nuget\\packages\\",
"outputPath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\obj\\",
"projectStyle": "PackageReference",
"configFilePaths": [
"C:\\Users\\Administrator\\AppData\\Roaming\\NuGet\\NuGet.Config"
],
"originalTargetFrameworks": [
"net9.0-windows"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0-windows7.0": {
"targetAlias": "net9.0-windows",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
},
"Microsoft.WindowsDesktop.App.WindowsForms": {
"privateAssets": "none"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.315/PortableRuntimeIdentifierGraph.json"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"version": 2,
"dgSpecHash": "icz9DlDWgaA=",
"success": true,
"projectFilePath": "C:\\Users\\Administrator\\Downloads\\mimocode-windows-x64\\GameEngineASM\\EditorCS\\Lity3DEditor.csproj",
"expectedPackageFiles": [],
"logs": []
}