├── Nanoforge.Tests ├── GlobalUsings.cs ├── PropertyTypeOmitter.cs └── Nanoforge.Tests.csproj ├── Nanoforge ├── assets │ ├── textures │ │ ├── 0.png │ │ ├── 0b.png │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 10b.png │ │ ├── 11.png │ │ ├── 11b.png │ │ ├── 12.png │ │ ├── 12b.png │ │ ├── 13.png │ │ ├── 13b.png │ │ ├── 14.png │ │ ├── 14b.png │ │ ├── 15.png │ │ ├── 15b.png │ │ ├── 16.png │ │ ├── 16b.png │ │ ├── 17.png │ │ ├── 17b.png │ │ ├── 18.png │ │ ├── 18b.png │ │ ├── 19.png │ │ ├── 19b.png │ │ ├── 1b.png │ │ ├── 2.png │ │ ├── 20.png │ │ ├── 20b.png │ │ ├── 21.png │ │ ├── 21b.png │ │ ├── 22.png │ │ ├── 22b.png │ │ ├── 23.png │ │ ├── 23b.png │ │ ├── 24.png │ │ ├── 24b.png │ │ ├── 25.png │ │ ├── 25b.png │ │ ├── 26.png │ │ ├── 26b.png │ │ ├── 27.png │ │ ├── 27b.png │ │ ├── 28.png │ │ ├── 28b.png │ │ ├── 29.png │ │ ├── 29b.png │ │ ├── 2b.png │ │ ├── 3.png │ │ ├── 30.png │ │ ├── 30b.png │ │ ├── 31.png │ │ ├── 31b.png │ │ ├── 32.png │ │ ├── 32b.png │ │ ├── 33.png │ │ ├── 33b.png │ │ ├── 34.png │ │ ├── 34b.png │ │ ├── 35.png │ │ ├── 35b.png │ │ ├── 36.png │ │ ├── 36b.png │ │ ├── 37.png │ │ ├── 37b.png │ │ ├── 38.png │ │ ├── 38b.png │ │ ├── 39.png │ │ ├── 39b.png │ │ ├── 3b.png │ │ ├── 4.png │ │ ├── 40.png │ │ ├── 40b.png │ │ ├── 4b.png │ │ ├── 5.png │ │ ├── 5b.png │ │ ├── 6.png │ │ ├── 6b.png │ │ ├── 7.png │ │ ├── 7b.png │ │ ├── 8.png │ │ ├── 8b.png │ │ ├── 9.png │ │ ├── 9b.png │ │ ├── Black.png │ │ ├── White.png │ │ ├── Missing.png │ │ ├── NoRender.png │ │ ├── FlatNormalMap.png │ │ └── MissingAlpha.png │ ├── fonts │ │ ├── codicon.ttf │ │ ├── fa-solid-900.ttf │ │ ├── NotoSansDisplay-Medium.ttf │ │ ├── Font Awesome LICENSE.txt │ │ └── SIL Open Font License.txt │ └── shaders │ │ ├── SolidTriList.frag │ │ ├── Linelist.frag │ │ ├── SolidTriList.vert │ │ ├── Linelist.vert │ │ ├── Constants.glsl │ │ ├── UnifiedMaterial.vert │ │ └── Colorspace.glsl ├── Gui │ ├── Models │ │ ├── Tools │ │ │ ├── Outliner.cs │ │ │ ├── Inspector.cs │ │ │ └── FileExplorer.cs │ │ └── Documents │ │ │ └── DemoDocument.cs │ ├── ViewModels │ │ ├── Pages │ │ │ └── EditorViewModel.cs │ │ ├── ViewModelBase.cs │ │ ├── Tools │ │ │ ├── FileExplorer │ │ │ │ ├── ExplorerNodeType.cs │ │ │ │ ├── FileExplorerSettings.cs │ │ │ │ └── FileExplorerNodeViewModel.cs │ │ │ ├── OutlinerViewModel.cs │ │ │ └── InspectorViewModel.cs │ │ ├── MenuItemViewModel.cs │ │ ├── NanoforgeDocument.cs │ │ ├── Docks │ │ │ └── CustomDocumentDock.cs │ │ ├── MapOptionViewModel.cs │ │ ├── Dialogs │ │ │ ├── TaskDialogViewModel.cs │ │ │ └── NewProjectDialogViewModel.cs │ │ └── Documents │ │ │ └── MapEditorDocumentViewModel.cs │ ├── Themes │ │ ├── IThemeManager.cs │ │ ├── FluentDark.axaml │ │ ├── FluentLight.axaml │ │ └── FluentThemeManager.cs │ ├── Views │ │ ├── Dialogs │ │ │ ├── NewProjectDialog.axaml.cs │ │ │ ├── DataFolderSelectorDialog.axaml.cs │ │ │ ├── TaskDialog.axaml.cs │ │ │ ├── TaskDialog.axaml │ │ │ ├── NewProjectDialog.axaml │ │ │ └── DataFolderSelectorDialog.axaml │ │ ├── MainView.axaml.cs │ │ ├── Tools │ │ │ ├── OutlinerView.axaml.cs │ │ │ ├── InspectorView.axaml.cs │ │ │ ├── InspectorView.axaml │ │ │ ├── OutlinerView.axaml │ │ │ └── FileExplorer │ │ │ │ ├── FileExplorerView.axaml.cs │ │ │ │ └── FileExplorerView.axaml │ │ ├── Documents │ │ │ ├── MapEditorDocumentView.axaml.cs │ │ │ ├── RendererTestDocumentView.axaml.cs │ │ │ ├── ChunkViewer │ │ │ │ ├── ChunkViewerDocumentView.axaml.cs │ │ │ │ ├── ChunkViewerOutliner.axaml.cs │ │ │ │ ├── ChunkViewerInspector.axaml.cs │ │ │ │ ├── ChunkViewerInspector.axaml │ │ │ │ ├── ChunkViewerOutliner.axaml │ │ │ │ └── ChunkViewerDocumentView.axaml │ │ │ ├── MapEditorDocumentView.axaml │ │ │ └── RendererTestDocumentView.axaml │ │ ├── Controls │ │ │ └── Viewport3D.axaml │ │ ├── MainWindow.axaml.cs │ │ └── MainWindow.axaml │ ├── Input.cs │ └── DockExtensions.cs ├── Render │ ├── Misc │ │ ├── MaterialType.cs │ │ ├── SwapChainSupportDetails.cs │ │ ├── PerFrameBuffer.cs │ │ ├── QueueFamilyIndices.cs │ │ ├── ColoredVertex.cs │ │ ├── LineVertex.cs │ │ ├── PerObjectConstants.cs │ │ ├── MaterialInstance.cs │ │ ├── VkDrawIndexedIndirectCommand.cs │ │ └── GpuFrameDataWriter.cs │ ├── VertexFormats │ │ ├── Rfg │ │ │ ├── LowLodTerrainVertex.cs │ │ │ ├── PixlitVertex.cs │ │ │ ├── Pixlit1UvVertex.cs │ │ │ ├── HighLodTerrainVertex.cs │ │ │ ├── Pixlit1UvNmapVertex.cs │ │ │ ├── Pixlit2UvNmapVertex.cs │ │ │ ├── Pixlit3UvNmapVertex.cs │ │ │ └── Pixlit4UvNmapVertex.cs │ │ └── UnifiedVertex.cs │ ├── RenderCommand.cs │ ├── MathHelpers.cs │ ├── Resources │ │ ├── VkMemory.cs │ │ ├── RenderObjectBase.cs │ │ ├── RenderMeshConfig.cs │ │ ├── Mesh.cs │ │ ├── SimpleRenderObject.cs │ │ └── VkBuffer.cs │ ├── TextureManager.cs │ ├── Materials │ │ ├── DescriptorAllocator.cs │ │ └── MaterialHelper.cs │ └── Camera.cs ├── Rfg │ ├── TerrainSubzone.cs │ ├── Zone.cs │ ├── Rock.cs │ ├── ZoneTerrain.cs │ ├── ProjectMesh.cs │ ├── ImportedTextures.cs │ ├── Chunk.cs │ └── ProjectTexture.cs ├── Misc │ ├── RfgNameAttribute.cs │ ├── Extensions.cs │ ├── Toggleable.cs │ └── DataHelper.cs ├── Services │ ├── IFileDialogService.cs │ └── FileDialogService.cs ├── Editor │ ├── Project.cs │ ├── GeneralSettings.cs │ ├── EditorObject.cs │ ├── Config.cs │ ├── ProjectBuffer.cs │ ├── NanoDBReferenceResolver.cs │ └── NanoDBTypeResolver.cs ├── BuildConfig.cs ├── app.manifest ├── Program.cs ├── FileSystem │ ├── EntryBase.cs │ └── FileEntry.cs ├── ViewLocator.cs ├── App.axaml └── App.axaml.cs ├── LICENSE ├── .github └── workflows │ └── dotnet-desktop.yml ├── Nanoforge.sln ├── README.md └── .gitignore /Nanoforge.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /Nanoforge/assets/textures/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/0.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/0b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/0b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/1.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/10.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/10b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/10b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/11.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/11b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/11b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/12.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/12b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/12b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/13.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/13b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/13b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/14.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/14b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/14b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/15.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/15b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/15b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/16.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/16b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/16b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/17.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/17b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/17b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/18.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/18b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/18b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/19.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/19b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/19b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/1b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/2.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/20.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/20b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/20b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/21.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/21b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/21b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/22.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/22b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/22b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/23.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/23b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/23b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/24.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/24b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/24b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/25.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/25b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/25b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/26.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/26b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/26b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/27.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/27b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/27b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/28.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/28b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/28b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/29.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/29b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/29b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/2b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/2b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/3.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/30.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/30b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/30b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/31.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/31b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/31b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/32.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/32b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/32b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/33.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/33b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/33b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/34.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/34b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/34b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/35.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/35b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/35b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/36.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/36b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/36b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/37.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/37b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/37b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/38.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/38b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/38b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/39.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/39b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/39b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/3b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/3b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/4.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/40.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/40b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/40b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/4b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/4b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/5.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/5b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/5b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/6.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/6b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/6b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/7.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/7b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/7b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/8.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/8b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/8b.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/9.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/9b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/9b.png -------------------------------------------------------------------------------- /Nanoforge/Gui/Models/Tools/Outliner.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Nanoforge.Gui.Models.Tools; 3 | 4 | public class Outliner 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Nanoforge/assets/fonts/codicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/fonts/codicon.ttf -------------------------------------------------------------------------------- /Nanoforge/assets/textures/Black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/Black.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/White.png -------------------------------------------------------------------------------- /Nanoforge/Gui/Models/Tools/Inspector.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Nanoforge.Gui.Models.Tools; 3 | 4 | public class Inspector 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Nanoforge/assets/textures/Missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/Missing.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/NoRender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/NoRender.png -------------------------------------------------------------------------------- /Nanoforge/Gui/Models/Tools/FileExplorer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Nanoforge.Gui.Models.Tools; 3 | 4 | public class FileExplorer 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Nanoforge/assets/fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /Nanoforge/assets/textures/FlatNormalMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/FlatNormalMap.png -------------------------------------------------------------------------------- /Nanoforge/assets/textures/MissingAlpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/textures/MissingAlpha.png -------------------------------------------------------------------------------- /Nanoforge/Gui/Models/Documents/DemoDocument.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Nanoforge.Gui.Models.Documents; 3 | 4 | public class DemoDocument 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Nanoforge/assets/fonts/NotoSansDisplay-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rfg-modding/Nanoforge/HEAD/Nanoforge/assets/fonts/NotoSansDisplay-Medium.ttf -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/MaterialType.cs: -------------------------------------------------------------------------------- 1 | namespace Nanoforge.Render.Misc; 2 | 3 | public enum MaterialType : int 4 | { 5 | Default = 0, 6 | HighLodTerrain = 1, 7 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Pages/EditorViewModel.cs: -------------------------------------------------------------------------------- 1 | using Dock.Model.Mvvm.Controls; 2 | 3 | namespace Nanoforge.Gui.ViewModels.Pages; 4 | 5 | public class EditorViewModel : RootDock 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace Nanoforge.Gui.ViewModels; 4 | 5 | public class ViewModelBase : ObservableObject 6 | { 7 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Tools/FileExplorer/ExplorerNodeType.cs: -------------------------------------------------------------------------------- 1 | namespace Nanoforge.Gui.ViewModels.Tools.FileExplorer; 2 | 3 | public enum ExplorerNodeType 4 | { 5 | Directory, 6 | File, 7 | } -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/LowLodTerrainVertex.cs: -------------------------------------------------------------------------------- 1 | namespace Nanoforge.Render.VertexFormats.Rfg; 2 | 3 | public struct LowLodTerrainVertex 4 | { 5 | public short PosX, PosY, PosZ, PosW; 6 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Themes/IThemeManager.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | 3 | namespace Nanoforge.Gui.Themes; 4 | 5 | public interface IThemeManager 6 | { 7 | void Initialize(Application application); 8 | 9 | void Switch(int index); 10 | } 11 | -------------------------------------------------------------------------------- /Nanoforge/assets/shaders/SolidTriList.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #include "Constants.glsl" 3 | 4 | layout(location = 0) in vec4 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() 9 | { 10 | outColor = fragColor; 11 | } 12 | -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/PixlitVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nanoforge.Render.VertexFormats.Rfg; 4 | 5 | public struct PixlitVertex 6 | { 7 | public Vector3 Position; 8 | public byte NormalX, NormalY, NormalZ, NormalW; 9 | } -------------------------------------------------------------------------------- /Nanoforge/Render/RenderCommand.cs: -------------------------------------------------------------------------------- 1 | using Nanoforge.Render.Resources; 2 | 3 | namespace Nanoforge.Render; 4 | 5 | public struct RenderCommand 6 | { 7 | public Mesh Mesh; 8 | public uint StartIndex; 9 | public uint IndexCount; 10 | public uint ObjectIndex; 11 | } -------------------------------------------------------------------------------- /Nanoforge/assets/shaders/Linelist.frag: -------------------------------------------------------------------------------- 1 | #version 460 2 | #include "Constants.glsl" 3 | 4 | //layout(location = 0) in float fragSize; 5 | layout(location = 0) in vec4 fragColor; 6 | 7 | layout(location = 0) out vec4 outColor; 8 | 9 | void main() 10 | { 11 | outColor = fragColor; 12 | } 13 | -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/Pixlit1UvVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nanoforge.Render.VertexFormats.Rfg; 4 | 5 | public struct Pixlit1UvVertex 6 | { 7 | public Vector3 Position; 8 | public byte NormalX, NormalY, NormalZ, NormalW; 9 | public short UvX, UvY; 10 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/SwapChainSupportDetails.cs: -------------------------------------------------------------------------------- 1 | using Silk.NET.Vulkan; 2 | 3 | namespace Nanoforge.Render.Misc; 4 | 5 | internal struct SwapChainSupportDetails 6 | { 7 | public SurfaceCapabilitiesKHR Capabilities; 8 | public SurfaceFormatKHR[] Formats; 9 | public PresentModeKHR[] PresentModes; 10 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Tools/OutlinerViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using Dock.Model.Mvvm.Controls; 3 | 4 | namespace Nanoforge.Gui.ViewModels.Tools; 5 | 6 | public partial class OutlinerViewModel : Tool 7 | { 8 | [ObservableProperty] 9 | private NanoforgeDocument? _focusedDocument = null; 10 | } 11 | -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Tools/InspectorViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using Dock.Model.Mvvm.Controls; 3 | 4 | namespace Nanoforge.Gui.ViewModels.Tools; 5 | 6 | public partial class InspectorViewModel : Tool 7 | { 8 | [ObservableProperty] 9 | private NanoforgeDocument? _focusedDocument = null; 10 | } 11 | -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/PerFrameBuffer.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Nanoforge.Render.Misc; 5 | 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal struct PerFrameBuffer 8 | { 9 | public Matrix4x4 View; 10 | public Matrix4x4 Projection; 11 | public Vector4 CameraPosition; 12 | } -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/HighLodTerrainVertex.cs: -------------------------------------------------------------------------------- 1 | namespace Nanoforge.Render.VertexFormats.Rfg; 2 | 3 | public struct HighLodTerrainVertex 4 | { 5 | public short PosX, PosY; //These aren't really X and Y. This is just the way the games shaders access the fields. X, Y, and Z are all in there. 6 | public byte NormalX, NormalY, NormalZ, NormalW; 7 | } -------------------------------------------------------------------------------- /Nanoforge/assets/shaders/SolidTriList.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | #include "Constants.glsl" 3 | 4 | layout(location = 0) in vec3 inPos; 5 | layout(location = 1) in vec4 inColor; 6 | 7 | layout(location = 0) out vec4 fragColor; 8 | 9 | void main() 10 | { 11 | gl_Position = ubo.proj * ubo.view * vec4(inPos.xyz, 1.0); 12 | fragColor = inColor; 13 | } 14 | -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/Pixlit1UvNmapVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nanoforge.Render.VertexFormats.Rfg; 4 | 5 | public struct Pixlit1UvNmapVertex 6 | { 7 | public Vector3 Position; 8 | public byte NormalX, NormalY, NormalZ, NormalW; 9 | public byte TangentX, TangentY, TangentZ, TangentW; 10 | public short UvX, UvY; 11 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Dialogs/NewProjectDialog.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Nanoforge.Gui.ViewModels.Dialogs; 3 | 4 | namespace Nanoforge.Gui.Views.Dialogs; 5 | 6 | public partial class NewProjectDialog : Window 7 | { 8 | public NewProjectDialog() 9 | { 10 | InitializeComponent(); 11 | DataContext = new NewProjectDialogViewModel(); 12 | } 13 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/MainView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Nanoforge.Gui.Views; 5 | 6 | public partial class MainView : UserControl 7 | { 8 | public MainView() 9 | { 10 | InitializeComponent(); 11 | } 12 | 13 | private void InitializeComponent() 14 | { 15 | AvaloniaXamlLoader.Load(this); 16 | } 17 | } -------------------------------------------------------------------------------- /Nanoforge/Rfg/TerrainSubzone.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Nanoforge.Editor; 3 | 4 | namespace Nanoforge.Rfg; 5 | 6 | //Terrain subzone. Each zone consists of 9 subzones 7 | public class TerrainSubzone : EditorObject 8 | { 9 | public ProjectMesh? Mesh; 10 | public Vector3 Position; 11 | 12 | public bool HasStitchMeshes = false; 13 | public ProjectMesh? StitchMesh; 14 | } -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/Pixlit2UvNmapVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nanoforge.Render.VertexFormats.Rfg; 4 | 5 | public struct Pixlit2UvNmapVertex 6 | { 7 | public Vector3 Position; 8 | public byte NormalX, NormalY, NormalZ, NormalW; 9 | public byte TangentX, TangentY, TangentZ, TangentW; 10 | public short Uv0X, Uv0Y; 11 | public short Uv1X, Uv1Y; 12 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Dialogs/DataFolderSelectorDialog.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Nanoforge.Gui.ViewModels.Dialogs; 3 | 4 | namespace Nanoforge.Gui.Views.Dialogs; 5 | 6 | public partial class DataFolderSelectorDialog : Window 7 | { 8 | public DataFolderSelectorDialog() 9 | { 10 | InitializeComponent(); 11 | DataContext = new DataFolderSelectorDialogViewModel(); 12 | } 13 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Tools/OutlinerView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Nanoforge.Gui.Views.Tools; 5 | 6 | public partial class OutlinerView : UserControl 7 | { 8 | public OutlinerView() 9 | { 10 | InitializeComponent(); 11 | } 12 | 13 | private void InitializeComponent() 14 | { 15 | AvaloniaXamlLoader.Load(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Tools/InspectorView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Nanoforge.Gui.Views.Tools; 5 | 6 | public partial class InspectorView : UserControl 7 | { 8 | public InspectorView() 9 | { 10 | InitializeComponent(); 11 | } 12 | 13 | private void InitializeComponent() 14 | { 15 | AvaloniaXamlLoader.Load(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/Pixlit3UvNmapVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nanoforge.Render.VertexFormats.Rfg; 4 | 5 | public struct Pixlit3UvNmapVertex 6 | { 7 | public Vector3 Position; 8 | public byte NormalX, NormalY, NormalZ, NormalW; 9 | public byte TangentX, TangentY, TangentZ, TangentW; 10 | public short Uv0X, Uv0Y; 11 | public short Uv1X, Uv1Y; 12 | public short Uv2X, Uv2Y; 13 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Tools/FileExplorer/FileExplorerSettings.cs: -------------------------------------------------------------------------------- 1 | using Nanoforge.Editor; 2 | 3 | namespace Nanoforge.Gui.ViewModels.Tools.FileExplorer; 4 | 5 | public class FileExplorerSettings : EditorObject 6 | { 7 | public bool RegexSearchMode = false; 8 | public bool HideUnsupportedFormats = false; 9 | public bool CaseSensitiveSearch = false; 10 | 11 | public static CVar CVar { get; } = new("FileExplorerSettings"); 12 | } -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/Rfg/Pixlit4UvNmapVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nanoforge.Render.VertexFormats.Rfg; 4 | 5 | public struct Pixlit4UvNmapVertex 6 | { 7 | public Vector3 Position; 8 | public byte NormalX, NormalY, NormalZ, NormalW; 9 | public byte TangentX, TangentY, TangentZ, TangentW; 10 | public short Uv0X, Uv0Y; 11 | public short Uv1X, Uv1Y; 12 | public short Uv2X, Uv2Y; 13 | public short Uv3X, Uv3Y; 14 | } -------------------------------------------------------------------------------- /Nanoforge/Misc/RfgNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Nanoforge.Misc; 4 | 5 | //Used to specify the string used for properties or enums when exporting/importing rfgzone_pc files. E.g. For the BoundingBoxType enum GpsTarget in code, but is "GPS Target" in rfgzone_pc files 6 | //This is only really needed for enums/bitflags that get stored in the zone files. 7 | public class RfgNameAttribute(string name) : Attribute 8 | { 9 | public readonly string Name = name; 10 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/QueueFamilyIndices.cs: -------------------------------------------------------------------------------- 1 | namespace Nanoforge.Render.Misc; 2 | 3 | internal struct QueueFamilyIndices 4 | { 5 | public uint? GraphicsFamily { get; set; } 6 | public uint? GraphicsFamilyQueueCount { get; set; } 7 | public uint? TransferFamily { get; set; } 8 | public uint? TransferFamilyQueueCount { get; set; } 9 | 10 | public bool IsComplete() 11 | { 12 | return GraphicsFamily.HasValue && TransferFamily.HasValue; 13 | } 14 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/MapEditorDocumentView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Nanoforge.Gui.Views.Documents; 5 | 6 | public partial class MapEditorDocumentView : UserControl 7 | { 8 | public MapEditorDocumentView() 9 | { 10 | InitializeComponent(); 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | private void InitializeComponent() 15 | { 16 | AvaloniaXamlLoader.Load(this); 17 | } 18 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/RendererTestDocumentView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Nanoforge.Gui.Views.Documents; 5 | 6 | public partial class RendererTestDocumentView : UserControl 7 | { 8 | public RendererTestDocumentView() 9 | { 10 | InitializeComponent(); 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | private void InitializeComponent() 15 | { 16 | AvaloniaXamlLoader.Load(this); 17 | } 18 | } -------------------------------------------------------------------------------- /Nanoforge/Services/IFileDialogService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Avalonia.Platform.Storage; 4 | using Nanoforge.Gui.ViewModels; 5 | 6 | namespace Nanoforge.Services; 7 | 8 | public interface IFileDialogService 9 | { 10 | public Task?> ShowOpenFileDialog(ViewModelBase parent, IReadOnlyList? filters = null); 11 | public Task?> ShowOpenFolderDialogAsync(ViewModelBase parent); 12 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/ChunkViewer/ChunkViewerDocumentView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Nanoforge.Gui.Views.Documents.ChunkViewer; 5 | 6 | public partial class ChunkViewerDocumentView : UserControl 7 | { 8 | public ChunkViewerDocumentView() 9 | { 10 | InitializeComponent(); 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | private void InitializeComponent() 15 | { 16 | AvaloniaXamlLoader.Load(this); 17 | } 18 | } -------------------------------------------------------------------------------- /Nanoforge/Editor/Project.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | 4 | namespace Nanoforge.Editor; 5 | 6 | public class Project : ObservableObject 7 | { 8 | [JsonInclude] 9 | public string Name = "No project loaded"; 10 | 11 | [JsonInclude] 12 | public string Description = ""; 13 | 14 | [JsonInclude] 15 | public string Author = ""; 16 | 17 | public string Directory = ""; 18 | public string FilePath = ""; 19 | public bool Loaded = false; 20 | } -------------------------------------------------------------------------------- /Nanoforge/Editor/GeneralSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace Nanoforge.Editor; 5 | 6 | public class GeneralSettings : EditorObject 7 | { 8 | public string DataPath = ""; 9 | public ObservableCollection RecentProjects { get; set; } = new(); 10 | public string NewProjectDirectory = ""; //So you don't have to keep picking the folder every time you make a project 11 | 12 | public static CVar CVar { get; } = new("GeneralSettings"); 13 | } -------------------------------------------------------------------------------- /Nanoforge/Misc/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Nanoforge.Misc; 5 | 6 | public static class TypeExtensions 7 | { 8 | public static bool HasBaseType(this Type type) 9 | { 10 | Type? curType = type; 11 | while (curType != null) 12 | { 13 | if (curType == typeof(T)) 14 | { 15 | return true; 16 | } 17 | 18 | curType = curType.BaseType; 19 | } 20 | 21 | return false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Nanoforge/Render/VertexFormats/UnifiedVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Nanoforge.Render.VertexFormats; 5 | 6 | //Primary vertex format used by the renderer. Meshes should be converted to this format by MeshConverter before being used by the renderer. 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct UnifiedVertex 9 | { 10 | public Vector3 Position; 11 | public byte NormalX, NormalY, NormalZ, NormalW; 12 | public byte TangentX, TangentY, TangentZ, TangentW; 13 | public short UvX, UvY; 14 | } -------------------------------------------------------------------------------- /Nanoforge/Render/MathHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Nanoforge.Render; 4 | 5 | public static class MathHelpers 6 | { 7 | public static float ToRadians(float degrees) 8 | { 9 | return MathF.PI / 180f * degrees; 10 | } 11 | 12 | public static float ToDegrees(float pitchRadians) 13 | { 14 | return (180.0f / MathF.PI) * pitchRadians; 15 | } 16 | 17 | public static float Lerp(float current, float target, float interpolant) 18 | { 19 | return current * (1.0f - interpolant) + (target * interpolant); 20 | } 21 | } -------------------------------------------------------------------------------- /Nanoforge/Rfg/Zone.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Numerics; 3 | using Nanoforge.Editor; 4 | using RFGM.Formats.Zones; 5 | 6 | namespace Nanoforge.Rfg; 7 | 8 | //A cubic section of a map. Contains map objects 9 | public class Zone : EditorObject 10 | { 11 | public string District = string.Empty; 12 | public DistrictFlags DistrictFlags = 0; 13 | public uint DistrictHash = 0; 14 | public bool ActivityLayer = false; 15 | public bool MissionLayer = false; 16 | public List Objects = new(); 17 | public ZoneTerrain? Terrain; 18 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/MenuItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Windows.Input; 3 | using CommunityToolkit.Mvvm.ComponentModel; 4 | 5 | namespace Nanoforge.Gui.ViewModels; 6 | 7 | public class MenuItemViewModel : ObservableObject 8 | { 9 | public string Header { get; set; } = string.Empty; 10 | public ICommand? Command { get; set; } = null; 11 | public object? CommandParameter { get; set; } = null; 12 | public bool IsEnabled { get; set; } = true; 13 | public ObservableCollection Items { get; set; } = new(); 14 | } 15 | -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/NanoforgeDocument.cs: -------------------------------------------------------------------------------- 1 | 2 | using Avalonia.Controls; 3 | using CommunityToolkit.Mvvm.ComponentModel; 4 | using Dock.Model.Mvvm.Controls; 5 | 6 | namespace Nanoforge.Gui.ViewModels; 7 | 8 | public partial class NanoforgeDocument : Document 9 | { 10 | //Object that should be displayed in the inspector when this document is selected 11 | [ObservableProperty] 12 | private object? _inspectorTarget = null; 13 | 14 | [ObservableProperty] 15 | private object? _outlinerTarget = null; 16 | 17 | [ObservableProperty] 18 | private bool _focused = false; 19 | } -------------------------------------------------------------------------------- /Nanoforge/assets/shaders/Linelist.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | #include "Constants.glsl" 3 | 4 | layout(location = 0) in vec4 inPosAndSize; 5 | layout(location = 1) in vec4 inColor; 6 | 7 | //layout(location = 0) out float fragSize; 8 | layout(location = 0) out vec4 fragColor; 9 | 10 | void main() 11 | { 12 | vec3 pos = inPosAndSize.xyz; 13 | float size = inPosAndSize.w; 14 | gl_Position = ubo.proj * ubo.view * vec4(pos, 1.0); 15 | //fragSize = size; 16 | fragColor = inColor; 17 | 18 | //TODO: Add geometry shader to turn the lines into quads. Use size field to determine line width 19 | } 20 | -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/ColoredVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Nanoforge.Render.Misc; 5 | 6 | //Size must be 16 bytes. Gets checked in Renderer constructor 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct ColoredVertex 9 | { 10 | public Vector3 Position; 11 | public byte R, G, B, A; 12 | 13 | public ColoredVertex(Vector3 position, Vector4 color) 14 | { 15 | Position = position; 16 | R = (byte)(color.X * 255.0f); 17 | G = (byte)(color.Y * 255.0f); 18 | B = (byte)(color.Z * 255.0f); 19 | A = (byte)(color.W * 255.0f); 20 | } 21 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Input.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Avalonia.Input; 3 | 4 | namespace Nanoforge.Gui; 5 | 6 | //TODO: Add code IsKeyPressed() code to see if the key was pressed this frame. For actions that you only want to happen one time each time a key is pressed 7 | public class Input 8 | { 9 | private readonly HashSet _downKeys = new(); 10 | 11 | public void SetKeyDown(Key k) 12 | { 13 | _downKeys.Add(k); 14 | } 15 | 16 | public void SetKeyUp(Key k) 17 | { 18 | _downKeys.Remove(k); 19 | } 20 | 21 | public bool IsKeyDown(Key k) 22 | { 23 | return _downKeys.Contains(k); 24 | } 25 | } -------------------------------------------------------------------------------- /Nanoforge/Rfg/Rock.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Nanoforge.Editor; 3 | 4 | namespace Nanoforge.Rfg; 5 | 6 | public class Rock : EditorObject 7 | { 8 | public Vector3 Position; 9 | public Matrix4x4 Rotation; 10 | public ProjectMesh? Mesh; 11 | public ProjectTexture? DiffuseTexture; 12 | //public ProjectTexture? NormalTexture; 13 | 14 | public Rock() 15 | { 16 | 17 | } 18 | 19 | public Rock(string name, Vector3 position, Matrix4x4 rotation, ProjectMesh mesh, ProjectTexture? diffuseTexture = null) 20 | { 21 | Name = name; 22 | Position = position; 23 | Rotation = rotation; 24 | Mesh = mesh; 25 | DiffuseTexture = diffuseTexture; 26 | } 27 | } -------------------------------------------------------------------------------- /Nanoforge/Rfg/ZoneTerrain.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Numerics; 3 | using Nanoforge.Editor; 4 | 5 | namespace Nanoforge.Rfg; 6 | 7 | public class ZoneTerrain: EditorObject 8 | { 9 | public Vector3 Position; 10 | 11 | public ProjectMesh[] LowLodTerrainMeshes = new ProjectMesh[9]; 12 | public ProjectTexture? CombTexture = null; 13 | public ProjectTexture? OvlTexture = null; 14 | public ProjectTexture? Splatmap = null; 15 | //Splat material textures per subzone. The splatmap has 4 channels, so it supports 4 material textures. 16 | public ProjectTexture?[] SplatmapTextures = new ProjectTexture[8]; 17 | 18 | public TerrainSubzone[] Subzones = new TerrainSubzone[9]; 19 | public List MaterialNames = []; 20 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Resources/VkMemory.cs: -------------------------------------------------------------------------------- 1 | using Silk.NET.Vulkan; 2 | 3 | namespace Nanoforge.Render.Resources; 4 | 5 | public unsafe class VkMemory 6 | { 7 | protected readonly RenderContext Context; 8 | protected Vk Vk => Context.Vk; 9 | protected Device Device => Context.Device; 10 | 11 | protected DeviceMemory Memory; 12 | protected VkMemory(RenderContext context) => Context = context; 13 | 14 | protected bool HostMapped; 15 | 16 | public Result MapMemory(ref void* pData) 17 | { 18 | HostMapped = true; 19 | return Vk.MapMemory(Device, Memory, 0, Vk.WholeSize, 0, ref pData); 20 | } 21 | 22 | public void UnmapMemory() 23 | { 24 | HostMapped = false; 25 | Vk.UnmapMemory(Device, Memory); 26 | } 27 | } -------------------------------------------------------------------------------- /Nanoforge.Tests/PropertyTypeOmitter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using AutoFixture.Kernel; 3 | 4 | namespace Nanoforge.Tests; 5 | 6 | public class PropertyTypeOmitter : ISpecimenBuilder 7 | { 8 | Type[] _typesToOmit; 9 | 10 | public PropertyTypeOmitter(Type typeToOmit) 11 | { 12 | _typesToOmit = [typeToOmit]; 13 | } 14 | 15 | public PropertyTypeOmitter(Type[] typesToOmit) 16 | { 17 | _typesToOmit = typesToOmit; 18 | } 19 | 20 | public object Create(object request, ISpecimenContext context) 21 | { 22 | var propInfo = request as PropertyInfo; 23 | if (propInfo != null && _typesToOmit.Contains(propInfo.PropertyType)) 24 | return new OmitSpecimen(); 25 | 26 | return new NoSpecimen(); 27 | } 28 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Dialogs/TaskDialog.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Interactivity; 3 | using Nanoforge.Gui.ViewModels.Dialogs; 4 | 5 | namespace Nanoforge.Gui.Views.Dialogs; 6 | 7 | public partial class TaskDialog : Window 8 | { 9 | public TaskDialogViewModel? ViewModel; 10 | 11 | public TaskDialog() 12 | { 13 | InitializeComponent(); 14 | DataContext = ViewModel = new TaskDialogViewModel(); 15 | ViewModel.Status = ""; 16 | ViewModel.StatusLog.Clear(); 17 | ViewModel.Step = 0; 18 | ViewModel.CanClose = false; 19 | ViewModel.TaskPercentage = 0.0f; 20 | ViewModel.OnClose += Close; 21 | } 22 | 23 | private void CloseButton_OnClick(object? sender, RoutedEventArgs e) 24 | { 25 | Close(); 26 | } 27 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Resources/RenderObjectBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | using System.Runtime.CompilerServices; 5 | using Nanoforge.Render.Materials; 6 | using Nanoforge.Render.Misc; 7 | using Serilog; 8 | using Silk.NET.OpenAL; 9 | using Silk.NET.Vulkan; 10 | 11 | namespace Nanoforge.Render.Resources; 12 | 13 | public abstract class RenderObjectBase(Vector3 position, Matrix4x4 orient, Vector3 scale) 14 | { 15 | public Vector3 Position = position; 16 | public Matrix4x4 Orient = orient; 17 | public Vector3 Scale = scale; 18 | 19 | public virtual unsafe void WriteDrawCommands(List commands, Camera camera, GpuFrameDataWriter constants) 20 | { 21 | 22 | } 23 | 24 | public virtual void Destroy() 25 | { 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/LineVertex.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Nanoforge.Render.Misc; 5 | 6 | //Size must be 20 bytes. Gets checked in Renderer constructor 7 | [StructLayout(LayoutKind.Sequential)] 8 | public struct LineVertex 9 | { 10 | //Position and Size positioned next to each other so they can be loaded in the vertex shader through a float4 11 | public Vector3 Position; 12 | public float Size; 13 | public byte R, G, B, A; 14 | 15 | public LineVertex(Vector3 position, Vector4 color, float size) 16 | { 17 | Position = position; 18 | R = (byte)(color.X * 255.0f); 19 | G = (byte)(color.Y * 255.0f); 20 | B = (byte)(color.Z * 255.0f); 21 | A = (byte)(color.W * 255.0f); 22 | Size = size; 23 | } 24 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Docks/CustomDocumentDock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using CommunityToolkit.Mvvm.Input; 4 | using Dock.Model.Controls; 5 | using Dock.Model.Mvvm.Controls; 6 | using Nanoforge.Gui.ViewModels.Documents; 7 | using Serilog; 8 | 9 | namespace Nanoforge.Gui.ViewModels.Docks; 10 | 11 | public class CustomDocumentDock : DocumentDock 12 | { 13 | public CustomDocumentDock() 14 | { 15 | CanCreateDocument = true; 16 | } 17 | 18 | public void AddNewDocument(T document) where T : IDocument 19 | { 20 | var index = VisibleDockables?.Count + 1; 21 | document.Id = $"{document.Title}-{index}"; 22 | 23 | Factory?.AddDockable(this, document); 24 | Factory?.SetActiveDockable(document); 25 | Factory?.SetFocusedDockable(this, document); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/PerObjectConstants.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Nanoforge.Render.Misc; 5 | 6 | [StructLayout(LayoutKind.Explicit)] 7 | public struct PerObjectConstants() 8 | { 9 | [FieldOffset(0)] 10 | public Matrix4x4 Model; 11 | 12 | [FieldOffset(64)] 13 | public Vector4 WorldPosition; 14 | 15 | //Padding needed due to shader padding rules. See here: https://registry.khronos.org/OpenGL/specs/gl/glspec45.core.pdf#page=159 Same rules apply even though we're using Vulkan. 16 | //In this case its due to rule 9. 17 | [FieldOffset(80)] 18 | public int MaterialIndex; 19 | 20 | [FieldOffset(84)] 21 | private int _padding0; 22 | 23 | [FieldOffset(88)] 24 | private int _padding1; 25 | 26 | [FieldOffset(92)] 27 | private int _padding2; 28 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/ChunkViewer/ChunkViewerOutliner.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Nanoforge.Gui.ViewModels.Documents; 4 | using ChunkViewerDocumentViewModel = Nanoforge.Gui.ViewModels.Documents.ChunkViewer.ChunkViewerDocumentViewModel; 5 | 6 | namespace Nanoforge.Gui.Views.Documents.ChunkViewer; 7 | 8 | public partial class ChunkViewerOutliner : UserControl 9 | { 10 | public static readonly StyledProperty DocumentProperty = AvaloniaProperty.Register(nameof(Document)); 11 | 12 | public ChunkViewerDocumentViewModel? Document 13 | { 14 | get => GetValue(DocumentProperty); 15 | set => SetValue(DocumentProperty, value); 16 | } 17 | 18 | public ChunkViewerOutliner() 19 | { 20 | InitializeComponent(); 21 | } 22 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/ChunkViewer/ChunkViewerInspector.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Nanoforge.Gui.ViewModels.Documents; 4 | using ChunkViewerDocumentViewModel = Nanoforge.Gui.ViewModels.Documents.ChunkViewer.ChunkViewerDocumentViewModel; 5 | 6 | namespace Nanoforge.Gui.Views.Documents.ChunkViewer; 7 | 8 | public partial class ChunkViewerInspector : UserControl 9 | { 10 | public static readonly StyledProperty DocumentProperty = AvaloniaProperty.Register(nameof(Document)); 11 | 12 | public ChunkViewerDocumentViewModel? Document 13 | { 14 | get => GetValue(DocumentProperty); 15 | set => SetValue(DocumentProperty, value); 16 | } 17 | 18 | public ChunkViewerInspector() 19 | { 20 | InitializeComponent(); 21 | } 22 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/MaterialInstance.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Nanoforge.Render.Misc; 4 | 5 | [StructLayout(LayoutKind.Explicit)] 6 | public struct MaterialInstance 7 | { 8 | [field: FieldOffset(0)] 9 | public int Texture0; 10 | [FieldOffset(4)] 11 | public int Texture1; 12 | [FieldOffset(8)] 13 | public int Texture2; 14 | [FieldOffset(12)] 15 | public int Texture3; 16 | [FieldOffset(16)] 17 | public int Texture4; 18 | [FieldOffset(20)] 19 | public int Texture5; 20 | [FieldOffset(24)] 21 | public int Texture6; 22 | [FieldOffset(28)] 23 | public int Texture7; 24 | [FieldOffset(32)] 25 | public int Texture8; 26 | [FieldOffset(36)] 27 | public int Texture9; 28 | 29 | [FieldOffset(40)] 30 | public MaterialType Type; 31 | [FieldOffset(44)] 32 | private int _padding1; 33 | } -------------------------------------------------------------------------------- /Nanoforge/BuildConfig.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.IO; 4 | 5 | namespace Nanoforge; 6 | 7 | public static class BuildConfig 8 | { 9 | public static readonly string ProjectName; 10 | public static readonly string AssetsDirectory; 11 | public static readonly string ShadersDirectory; 12 | public static readonly string Version; 13 | public static readonly bool EnableGraphicsDebugFeatures = false; 14 | 15 | static BuildConfig() 16 | { 17 | ProjectName = "Nanoforge"; 18 | #if DEBUG 19 | string workingDirectory = Environment.CurrentDirectory; 20 | string? projectDir = Directory.GetParent(workingDirectory)?.Parent?.Parent?.FullName; 21 | 22 | AssetsDirectory = $"{projectDir}/assets/"; 23 | #else 24 | AssetsDirectory = "./assets/"; 25 | #endif 26 | ShadersDirectory = $@"{AssetsDirectory}shaders/"; 27 | Version = "v2.0.0"; 28 | } 29 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Themes/FluentDark.axaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | #2F2F2F 6 | #2F2F2F 7 | #2F2F2F 8 | #FFFFFF 9 | #FFFFFF 10 | #434343 11 | #434343 12 | #FF212121 13 | #1F1F1F 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Nanoforge/Gui/Themes/FluentLight.axaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | #EFEFEF 6 | #EFEFEF 7 | #EFEFEF 8 | #212121 9 | #212121 10 | #33323232 11 | #33323232 12 | #FFFAFAFA 13 | #E2E2E2 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Nanoforge/Misc/Toggleable.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Nanoforge.Misc; 4 | 5 | //Used to represent values that can get toggled in the UI. Disabled values don't get written to the zone files when a map is exported. The value is preserved when enabled/disabled. 6 | public class Toggleable 7 | { 8 | public T Value; 9 | 10 | [JsonInclude] 11 | public bool Enabled; 12 | 13 | [JsonConstructor] 14 | private Toggleable() 15 | { 16 | Value = default!; 17 | } 18 | 19 | public Toggleable(T value) 20 | { 21 | Value = value; 22 | } 23 | 24 | public Toggleable(T value, bool enabled = false) 25 | { 26 | Value = value; 27 | } 28 | 29 | public void SetAndEnable(T value) 30 | { 31 | Value = value; 32 | Enabled = true; 33 | } 34 | 35 | public static implicit operator T(Toggleable toggleable) => toggleable.Value; 36 | } -------------------------------------------------------------------------------- /Nanoforge/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Nanoforge/assets/shaders/Constants.glsl: -------------------------------------------------------------------------------- 1 | 2 | layout(binding = 0) uniform UniformBufferObject 3 | { 4 | mat4 view; 5 | mat4 proj; 6 | vec4 cameraPos; 7 | } ubo; 8 | 9 | struct ObjectData 10 | { 11 | mat4 model; 12 | vec4 worldPos; 13 | int MaterialIndex; 14 | }; 15 | 16 | layout(std140, binding = 1) readonly buffer ObjectBuffer 17 | { 18 | ObjectData objects[]; 19 | } objectBuffer; 20 | 21 | struct MaterialInstance 22 | { 23 | int Texture0; 24 | int Texture1; 25 | int Texture2; 26 | int Texture3; 27 | int Texture4; 28 | int Texture5; 29 | int Texture6; 30 | int Texture7; 31 | int Texture8; 32 | int Texture9; 33 | int Type; //See the MaterialType enum in C# code 34 | }; 35 | 36 | layout(std140, binding = 2) readonly buffer MaterialBuffer 37 | { 38 | MaterialInstance materials[]; 39 | } materialBuffer; 40 | 41 | layout(binding = 3) uniform sampler2D textures[8192]; //Note: Array size must match TextureManager.MaxTextures -------------------------------------------------------------------------------- /Nanoforge/Gui/DockExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Dock.Model.Core; 3 | using Dock.Model.Mvvm.Core; 4 | 5 | namespace Nanoforge.Gui; 6 | 7 | public static class DockExtensions 8 | { 9 | public static List GetChildrenRecursive(this DockBase parent) 10 | { 11 | List children = new List(); 12 | DockBase currentParent = parent; 13 | GetDockChildrenRecursive(parent, children); 14 | return children; 15 | } 16 | 17 | private static void GetDockChildrenRecursive(DockBase parent, List children) 18 | { 19 | if (parent.VisibleDockables == null) 20 | return; 21 | 22 | foreach (IDockable child in parent.VisibleDockables) 23 | { 24 | children.Add(child); 25 | if (child is DockBase childDock) 26 | { 27 | GetDockChildrenRecursive(childDock, children); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/VkDrawIndexedIndirectCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Nanoforge.Render.Misc; 4 | 5 | [StructLayout(LayoutKind.Sequential)] 6 | public struct VkDrawIndexedIndirectCommand 7 | { 8 | public uint IndexCount; 9 | public uint InstanceCount; 10 | public uint FirstIndex; 11 | public int VertexOffset; 12 | public uint FirstInstance; 13 | 14 | public VkDrawIndexedIndirectCommand(uint indexCount, uint instanceCount, uint firstIndex, int vertexOffset, uint firstInstance) 15 | { 16 | IndexCount = indexCount; 17 | InstanceCount = instanceCount; 18 | FirstIndex = firstIndex; 19 | VertexOffset = vertexOffset; 20 | FirstInstance = firstInstance; 21 | } 22 | } 23 | 24 | // struct VkDrawIndexedIndirectCommand { 25 | // uint32_t indexCount; 26 | // uint32_t instanceCount; 27 | // uint32_t firstIndex; 28 | // int32_t vertexOffset; 29 | // uint32_t firstInstance; 30 | // }; 31 | -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/ChunkViewer/ChunkViewerInspector.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Nanoforge/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | using Nanoforge.Editor; 4 | using Serilog; 5 | 6 | namespace Nanoforge; 7 | 8 | sealed class Program 9 | { 10 | // Initialization code. Don't use any Avalonia, third-party APIs or any 11 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 12 | // yet and stuff might break. 13 | [STAThread] 14 | public static void Main(string[] args) 15 | { 16 | Log.Logger = new LoggerConfiguration() 17 | .WriteTo.Console() 18 | .WriteTo.File("Log.txt") 19 | .CreateLogger(); 20 | 21 | BuildAvaloniaApp() 22 | .StartWithClassicDesktopLifetime(args); 23 | } 24 | 25 | // Avalonia configuration, don't remove; also used by visual designer. 26 | public static AppBuilder BuildAvaloniaApp() 27 | { 28 | return AppBuilder.Configure() 29 | .UsePlatformDetect() 30 | .WithInterFont() 31 | .LogToTrace(); 32 | } 33 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Resources/RenderMeshConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using RFGM.Formats.Meshes.Shared; 3 | using Silk.NET.Vulkan; 4 | 5 | namespace Nanoforge.Render.Resources; 6 | 7 | //Data required to make a renderer mesh. Assumed to be using the unified renderer vertex & index formats. 8 | public class RenderMeshData 9 | { 10 | public List Submeshes; 11 | public List RenderBlocks; 12 | public IndexType IndexType; 13 | public byte[] Vertices; 14 | public byte[] Indices; 15 | public uint NumVertices; 16 | public uint NumIndices; 17 | 18 | public RenderMeshData(List submeshes, List renderBlocks, IndexType indexType, byte[] vertices, byte[] indices, uint numVertices, uint numIndices) 19 | { 20 | Submeshes = submeshes; 21 | RenderBlocks = renderBlocks; 22 | IndexType = indexType; 23 | Vertices = vertices; 24 | Indices = indices; 25 | NumVertices = numVertices; 26 | NumIndices = numIndices; 27 | } 28 | } -------------------------------------------------------------------------------- /Nanoforge/Rfg/ProjectMesh.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Nanoforge.Editor; 3 | using RFGM.Formats.Asset; 4 | using RFGM.Formats.Meshes.Shared; 5 | 6 | namespace Nanoforge.Rfg; 7 | 8 | public class ProjectMesh : EditorObject 9 | { 10 | public uint NumVertices; 11 | public byte VertexStride; 12 | public VertexFormat VertexFormat; 13 | public uint NumIndices; 14 | public byte IndexSize; 15 | public PrimitiveTopology Topology; 16 | public List Submeshes = new(); 17 | public List RenderBlocks = new(); 18 | 19 | public ProjectBuffer? IndexBuffer; 20 | public ProjectBuffer? VertexBuffer; 21 | 22 | public void InitFromRfgMeshConfig(MeshConfig config) 23 | { 24 | NumVertices = config.NumVertices; 25 | VertexStride = config.VertexStride0; 26 | VertexFormat = config.VertexFormat; 27 | NumIndices = config.NumIndices; 28 | IndexSize = config.IndexSize; 29 | Topology = config.Topology; 30 | Submeshes.AddRange(config.Submeshes); 31 | RenderBlocks.AddRange(config.RenderBlocks); 32 | } 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 moneyl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Nanoforge/Rfg/ImportedTextures.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Nanoforge.Editor; 3 | 4 | namespace Nanoforge.Rfg; 5 | 6 | //List of peg subtextures imported and stored in project buffers. Used to prevent repeat imports. Only one instance of this should exist 7 | //TODO: Use the full path instead of subtexture name as the key to properly handle textures with the same name button different contents/size 8 | public class ImportedTextures : EditorObject 9 | { 10 | private object _newTextureLock = new(); 11 | public List Textures = new(); 12 | 13 | public ProjectTexture? GetTexture(string name) 14 | { 15 | lock (_newTextureLock) 16 | { 17 | foreach (ProjectTexture texture in Textures) 18 | { 19 | if (texture.Name == name) 20 | { 21 | return texture; 22 | } 23 | } 24 | return null; 25 | } 26 | } 27 | 28 | public void AddTexture(ProjectTexture texture) 29 | { 30 | lock (_newTextureLock) 31 | { 32 | Textures.Add(texture); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Nanoforge/FileSystem/EntryBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Nanoforge.FileSystem; 5 | 6 | //Base class used by FileSystem entries in PackfileVFS 7 | public class EntryBase(string name, long dataBlockOffset = long.MaxValue, long dataOffset = long.MaxValue, uint size = uint.MaxValue, uint compressedSize = uint.MaxValue) 8 | { 9 | public EntryBase? Parent = null; 10 | public readonly string Name = name; 11 | public long DataBlockOffset { get; private set; } = dataBlockOffset; 12 | public long DataOffset { get; private set; } = dataOffset; 13 | public uint Size { get; private set; } = size; 14 | public uint CompressedSize { get; private set; } = compressedSize; 15 | 16 | public virtual bool IsDirectory => false; 17 | public virtual bool IsFile => false; 18 | 19 | public virtual Stream? OpenStream() 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | 24 | public virtual byte[]? ReadAllBytes() 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | public virtual string? ReadAllText() 30 | { 31 | throw new NotImplementedException(); 32 | } 33 | } -------------------------------------------------------------------------------- /.github/workflows/dotnet-desktop.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core Desktop 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | strategy: 14 | matrix: 15 | configuration: [Release] 16 | 17 | runs-on: windows-latest # For a list of available runner types, refer to 18 | # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on 19 | 20 | env: 21 | Solution_Name: Nanoforge.sln 22 | Test_Project_Path: Nanoforge.Tests\Nanoforge.Tests.csproj 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | 30 | # Install the .NET Core workload 31 | - name: Install .NET Core 32 | uses: actions/setup-dotnet@v4 33 | with: 34 | dotnet-version: 8.0.x 35 | 36 | # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild 37 | - name: Setup MSBuild.exe 38 | uses: microsoft/setup-msbuild@v2 39 | 40 | # Execute all unit tests in the solution 41 | - name: Execute unit tests 42 | run: dotnet test 43 | -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/MapEditorDocumentView.axaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/RendererTestDocumentView.axaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/ChunkViewer/ChunkViewerOutliner.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Tools/FileExplorer/FileExplorerNodeViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | 4 | namespace Nanoforge.Gui.ViewModels.Tools.FileExplorer; 5 | 6 | public partial class FileExplorerNodeViewModel : ObservableObject 7 | { 8 | [ObservableProperty] 9 | private ExplorerNodeType _type; 10 | 11 | [ObservableProperty] 12 | private FileExplorerNodeViewModel? _parent; 13 | 14 | [ObservableProperty] 15 | private ObservableCollection _children = []; 16 | 17 | [ObservableProperty] 18 | private string _text = string.Empty; 19 | 20 | [ObservableProperty] 21 | private bool _matchesSearch = true; 22 | 23 | [ObservableProperty] 24 | private bool _anyChildMatchesSearch = true; 25 | 26 | public string Path 27 | { 28 | get 29 | { 30 | string path = Text; 31 | FileExplorerNodeViewModel? parent = Parent; 32 | while (parent != null) 33 | { 34 | path = $"{parent.Text}/{path}"; 35 | parent = parent.Parent; 36 | } 37 | path = $"//data/{path}"; 38 | 39 | return path; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/MapOptionViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | using CommunityToolkit.Mvvm.Input; 4 | using Nanoforge.Gui.ViewModels.Documents; 5 | using Nanoforge.Gui.Views; 6 | using Serilog; 7 | 8 | namespace Nanoforge.Gui.ViewModels; 9 | 10 | public partial class MapOptionViewModel(string displayName, string fileName) : ObservableObject 11 | { 12 | [ObservableProperty] 13 | private string _displayName = displayName; 14 | 15 | [ObservableProperty] 16 | private string _fileName = fileName; 17 | 18 | [RelayCommand] 19 | private void OpenMap(MapOptionViewModel map) 20 | { 21 | DockFactory? dockFactory = MainWindow.DockFactory; 22 | if (dockFactory is null) 23 | { 24 | Log.Error("DockFactory not set when MainWindowViewModel.OpenMap() was called. Something went wrong."); 25 | throw new Exception("DockFactory not set when MainWindowViewModel.OpenMap() was called. Something went wrong."); 26 | } 27 | 28 | MapEditorDocumentViewModel document = new(map.FileName, map.DisplayName); 29 | document.Title = map.DisplayName; 30 | dockFactory.DocumentDock?.AddNewDocument(document); 31 | } 32 | } -------------------------------------------------------------------------------- /Nanoforge/Editor/EditorObject.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Nanoforge.Editor; 4 | 5 | public class EditorObject 6 | { 7 | // ReSharper disable once InconsistentNaming 8 | public const ulong NullUID = ulong.MaxValue; 9 | 10 | [JsonInclude] 11 | private ulong _uid = NullUID; 12 | 13 | // ReSharper disable once InconsistentNaming 14 | [JsonIgnore] 15 | public ulong UID 16 | { 17 | get => _uid; 18 | private set 19 | { 20 | _uid = value; 21 | } 22 | 23 | } 24 | 25 | public string Name = ""; 26 | 27 | public virtual EditorObject Clone() 28 | { 29 | EditorObject clone = (EditorObject)MemberwiseClone(); 30 | clone.UID = NullUID; //Default to NullUID so it's clear in the debugger that the object isn't valid yet. Gets a valid UID when finally added to NanoDB. 31 | clone.Name = new string(Name); 32 | return clone; 33 | } 34 | 35 | //Do NOT call this unless you know what you're doing. This is really only for NanoDB to use when creating objects. 36 | //I made it into a function instead of using the property so it's hard to accidentally change the UID. 37 | public void SetUID(ulong uid) 38 | { 39 | UID = uid; 40 | } 41 | } -------------------------------------------------------------------------------- /Nanoforge/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using CommunityToolkit.Mvvm.ComponentModel; 5 | using Dock.Model.Core; 6 | using Nanoforge.Gui.ViewModels; 7 | 8 | namespace Nanoforge; 9 | 10 | public class ViewLocator : IDataTemplate 11 | { 12 | public Control? Build(object? data) 13 | { 14 | if (data is null) 15 | return null; 16 | 17 | //TODO: Change this so it goes off the class name and ignores the namespace. I don't want to be forced to have the same folder structure for the View and ViewModel 18 | var name = data.GetType().FullName?.Replace("ViewModel", "View", StringComparison.Ordinal); 19 | if (name is null) 20 | { 21 | return new TextBlock { Text = "Invalid Data Type" }; 22 | } 23 | var type = Type.GetType(name); 24 | 25 | if (type != null) 26 | { 27 | var control = (Control)Activator.CreateInstance(type)!; 28 | control.DataContext = data; 29 | return control; 30 | } 31 | 32 | return new TextBlock { Text = "Not Found: " + name }; 33 | } 34 | 35 | public bool Match(object? data) 36 | { 37 | return data is ObservableObject || data is IDockable; 38 | } 39 | } -------------------------------------------------------------------------------- /Nanoforge.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nanoforge", "Nanoforge\Nanoforge.csproj", "{0BCBDFFF-431D-49B1-9A15-1C238BB280F9}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nanoforge.Tests", "Nanoforge.Tests\Nanoforge.Tests.csproj", "{D544DFAF-EF6F-4CA1-887C-736D3DD6840C}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release|Any CPU = Release|Any CPU 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 14 | {0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Debug|Any CPU.Build.0 = Debug|Any CPU 15 | {0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {0BCBDFFF-431D-49B1-9A15-1C238BB280F9}.Release|Any CPU.Build.0 = Release|Any CPU 17 | {D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {D544DFAF-EF6F-4CA1-887C-736D3DD6840C}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Nanoforge/Rfg/Chunk.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Numerics; 3 | using Nanoforge.Editor; 4 | using RFGM.Formats.Materials; 5 | using RFGM.Formats.Meshes.Chunks; 6 | 7 | namespace Nanoforge.Rfg; 8 | 9 | public class Chunk : EditorObject 10 | { 11 | public ProjectMesh? Mesh; 12 | public ProjectTexture? DiffuseTexture; 13 | public ProjectTexture? NormalTexture; 14 | public ProjectTexture? SpecularTexture; 15 | 16 | //TODO: Change this to use nanoforge types that only contain the required data. There's a lot of extra data we can discard. 17 | public List Destroyables = []; 18 | public List Materials = []; 19 | public List> Textures = []; 20 | public List Identifiers = []; 21 | 22 | //public List Variants = []; 23 | // 24 | ////Note: Pulls data from RFGM.Formats Subpiece and SubpieceData. Only has the data NF needs at the moment 25 | //public struct PieceData 26 | //{ 27 | // public ushort RenderSubmesh; 28 | //} 29 | // 30 | ////Note: Pulls data from RFGM.Formats Dlod 31 | //public struct PieceInstance 32 | //{ 33 | // public Vector3 Position; 34 | // public Matrix4x4 Orient; 35 | // public ushort FirstSubmesh; 36 | // public byte NumSubmeshes; 37 | //} 38 | } -------------------------------------------------------------------------------- /Nanoforge/Editor/Config.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Nanoforge.Editor; 3 | 4 | /// 5 | /// CVars are an easy way to define global EditorObjects without needing to manually add & retrieve them from NanoDB. 6 | /// Just define a field/property of the type CVar where T is an EditorObject you want to persist across NanoDB sessions and projects. 7 | /// When the instance is it'll handle loading its value from NanoDB and creating a global EditorObject for the data if it doesn't already exist. 8 | /// 9 | public class CVar where T : EditorObject, new() 10 | { 11 | public T Value { get; private set; } 12 | public readonly string Name; 13 | 14 | public CVar(string name) 15 | { 16 | Name = name; 17 | if (!NanoDB.LoadedGlobalObjects) 18 | { 19 | NanoDB.LoadGlobals(); 20 | } 21 | 22 | T? value = NanoDB.Find(Name); 23 | if (value == null) 24 | { 25 | Value = NanoDB.CreateGlobalObject(Name); 26 | NanoDB.SaveGlobals(); 27 | } 28 | else 29 | { 30 | Value = value; 31 | } 32 | } 33 | 34 | //Save the variable to Settings.nanodata. Note: Really saves all the CVars since ProjectDB doesn't support selective saves. So don't spam this. 35 | public void Save() 36 | { 37 | NanoDB.SaveGlobals(); 38 | } 39 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Documents/ChunkViewer/ChunkViewerDocumentView.axaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Nanoforge/Editor/ProjectBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | 5 | namespace Nanoforge.Editor; 6 | 7 | //Binary blob of data attached to the project. Useful for bulk binary data such as textures and meshes. 8 | public class ProjectBuffer : EditorObject 9 | { 10 | public int Size { get; private set; } = 0; 11 | private object _lock = new Mutex(); 12 | 13 | public ProjectBuffer(ulong uid, int size = 0, string name = "") 14 | { 15 | SetUID(uid); 16 | Size = size; 17 | Name = name; 18 | } 19 | 20 | public string GetPath() 21 | { 22 | return $"{NanoDB.BuffersDirectory}{Name}.{UID}.buffer"; 23 | } 24 | 25 | public byte[] Load() 26 | { 27 | lock (_lock) 28 | { 29 | byte[] bytes = File.ReadAllBytes(GetPath()); 30 | return bytes; 31 | } 32 | } 33 | 34 | public bool Save(Span data) 35 | { 36 | //TODO: Port tiny buffer merge optimization from the C++ version. Prevents having 1000s of 1KB or less files in the buffers folder since that causes performance issues. 37 | lock (_lock) 38 | { 39 | if (!Directory.Exists(NanoDB.BuffersDirectory)) 40 | { 41 | Directory.CreateDirectory(NanoDB.BuffersDirectory); 42 | } 43 | 44 | File.WriteAllBytes(GetPath(), data.ToArray()); 45 | return true; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Nanoforge/Services/FileDialogService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Avalonia.Controls; 4 | using Avalonia.Platform.Storage; 5 | using Nanoforge.Gui.ViewModels; 6 | using Nanoforge.Gui.Views; 7 | 8 | namespace Nanoforge.Services; 9 | 10 | public class FileDialogService : IFileDialogService 11 | { 12 | public async Task?> ShowOpenFileDialog(ViewModelBase parent, IReadOnlyList? filters) 13 | { 14 | var topLevel = TopLevel.GetTopLevel(MainWindow.Instance); 15 | if (topLevel == null) 16 | return null; 17 | 18 | var result = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() 19 | { 20 | AllowMultiple = false, 21 | Title = "Select a nanoproj file", 22 | FileTypeFilter = filters 23 | }); 24 | 25 | return result; 26 | } 27 | 28 | public async Task?> ShowOpenFolderDialogAsync(ViewModelBase parent) 29 | { 30 | var topLevel = TopLevel.GetTopLevel(MainWindow.Instance); 31 | if (topLevel == null) 32 | return null; 33 | 34 | var result = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions() 35 | { 36 | AllowMultiple = false, 37 | Title = "Select the RFG data folder" 38 | }); 39 | 40 | return result; 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /Nanoforge.Tests/Nanoforge.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | Nullable 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Controls/Viewport3D.axaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 17 | 19 | 20 | -------------------------------------------------------------------------------- /Nanoforge/assets/shaders/UnifiedMaterial.vert: -------------------------------------------------------------------------------- 1 | #version 460 2 | #include "Constants.glsl" 3 | 4 | layout(location = 0) in vec3 inPosition; 5 | layout(location = 1) in vec4 inNormal; 6 | layout(location = 2) in vec4 inTangent; 7 | layout(location = 3) in ivec2 inTexCoord; 8 | 9 | layout(location = 0) out vec3 fragWorldPosTransformed; 10 | layout(location = 1) out vec2 fragTexCoord; 11 | layout(location = 2) out vec4 fragTangent; 12 | layout(location = 3) out vec4 fragNormal; 13 | layout(location = 4) out int fragObjectIndex; 14 | layout(location = 5) out vec3 fragWorldPos; 15 | 16 | void main() 17 | { 18 | vec3 posWorld = inPosition.xyz + objectBuffer.objects[gl_BaseInstance].worldPos.xyz; 19 | fragWorldPos = posWorld; 20 | 21 | fragWorldPosTransformed = (objectBuffer.objects[gl_BaseInstance].model * vec4(inPosition, 1.0f)).xyz; 22 | gl_Position = ubo.proj * ubo.view * objectBuffer.objects[gl_BaseInstance].model * vec4(inPosition, 1.0); 23 | fragTexCoord = vec2(float(inTexCoord.x), float(inTexCoord.y)) / 1024.0f; 24 | 25 | mat3 normalMatrix = transpose(inverse(mat3(objectBuffer.objects[gl_BaseInstance].model))); 26 | 27 | vec3 tangent = normalize(inTangent.xyz * 2.0f - 1.0f); //Adjust range from [0, 1] to [-1, 1] 28 | tangent = normalize(normalMatrix * tangent.xyz); //Rotate the tangent 29 | fragTangent = vec4(tangent, 1.0f); 30 | 31 | vec3 normal = normalize(inNormal.xyz * 2.0f - 1.0f); //Adjust range from [0, 1] to [-1, 1] 32 | normal = normalize(normalMatrix * normal.xyz); //Rotate the normal 33 | fragNormal = vec4(normal, 1.0f); 34 | 35 | fragObjectIndex = gl_BaseInstance; 36 | } -------------------------------------------------------------------------------- /Nanoforge/assets/fonts/Font Awesome LICENSE.txt: -------------------------------------------------------------------------------- 1 | Font Awesome Free License 2 | ------------------------- 3 | 4 | Font Awesome Free is free, open source, and GPL friendly. You can use it for 5 | commercial projects, open source projects, or really almost whatever you want. 6 | Full Font Awesome Free license: https://fontawesome.com/license/free. 7 | 8 | # Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/) 9 | In the Font Awesome Free download, the CC BY 4.0 license applies to all icons 10 | packaged as SVG and JS file types. 11 | 12 | # Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL) 13 | In the Font Awesome Free download, the SIL OFL license applies to all icons 14 | packaged as web and desktop font files. 15 | 16 | # Code: MIT License (https://opensource.org/licenses/MIT) 17 | In the Font Awesome Free download, the MIT license applies to all non-font and 18 | non-icon files. 19 | 20 | # Attribution 21 | Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font 22 | Awesome Free files already contain embedded comments with sufficient 23 | attribution, so you shouldn't need to do anything additional when using these 24 | files normally. 25 | 26 | We've kept attribution comments terse, so we ask that you do not actively work 27 | to remove them from files, especially code. They're a great way for folks to 28 | learn about Font Awesome. 29 | 30 | # Brand Icons 31 | All brand icons are trademarks of their respective owners. The use of these 32 | trademarks does not indicate endorsement of the trademark holder by Font 33 | Awesome, nor vice versa. **Please do not use brand logos for any purpose except 34 | to represent the company, product, or service to which they refer.** 35 | -------------------------------------------------------------------------------- /Nanoforge/Render/Misc/GpuFrameDataWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Nanoforge.Render.Misc; 4 | 5 | public class GpuFrameDataWriter 6 | { 7 | public readonly int MaxObjects; 8 | public readonly int MaxMaterials; 9 | public uint NumObjects { get; private set; } = 0; 10 | public int NumMaterials { get; private set; } = 0; 11 | public PerObjectConstants[] Constants; 12 | public MaterialInstance[] Materials; 13 | 14 | public GpuFrameDataWriter(int maxObjects, int maxMaterials) 15 | { 16 | MaxObjects = maxObjects; 17 | MaxMaterials = maxMaterials; 18 | Constants = new PerObjectConstants[maxObjects]; 19 | Materials = new MaterialInstance[maxMaterials]; 20 | } 21 | 22 | public void Reset() 23 | { 24 | NumObjects = 0; 25 | NumMaterials = 0; 26 | } 27 | 28 | public uint AddObject(PerObjectConstants constant) 29 | { 30 | if (NumObjects == MaxObjects) 31 | throw new Exception($"Exceeded maximum render object count of {MaxObjects}. Please recompile Nanoforge with a higher maximum or rewrite the code to grow the buffer on demand."); 32 | 33 | uint objectIndex = NumObjects; 34 | Constants[NumObjects++] = constant; 35 | return objectIndex; 36 | } 37 | 38 | public int AddMaterialInstance(MaterialInstance materialInstance) 39 | { 40 | if (NumMaterials == MaxMaterials) 41 | throw new Exception($"Exceeded maximum material instance count of {MaxMaterials}. Please recompile Nanoforge with a higher maximum or rewrite the code to grow the buffer on demand."); 42 | 43 | int materialIndex = NumMaterials; 44 | Materials[NumMaterials++] = materialInstance; 45 | return materialIndex; 46 | } 47 | } -------------------------------------------------------------------------------- /Nanoforge/assets/shaders/Colorspace.glsl: -------------------------------------------------------------------------------- 1 | #ifndef saturate 2 | #define saturate(v) clamp(v, 0., 1.) 3 | #endif 4 | vec3 hue2rgb(float hue){ 5 | hue=fract(hue); 6 | return saturate(vec3( 7 | abs(hue*6.-3.)-1., 8 | 2.-abs(hue*6.-2.), 9 | 2.-abs(hue*6.-4.) 10 | )); 11 | } 12 | 13 | //RGB to HSL (hue, saturation, lightness/luminance). 14 | //Source: https://gist.github.com/yiwenl/745bfea7f04c456e0101 15 | vec3 rgb2hsl(vec3 c){ 16 | float cMin=min(min(c.r, c.g), c.b), 17 | cMax=max(max(c.r, c.g), c.b), 18 | delta=cMax-cMin; 19 | vec3 hsl=vec3(0., 0., (cMax+cMin)/2.); 20 | if (delta!=0.0){ //If it has chroma and isn't gray. 21 | if (hsl.z<.5){ 22 | hsl.y=delta/(cMax+cMin);//Saturation. 23 | } else { 24 | hsl.y=delta/(2.-cMax-cMin);//Saturation. 25 | } 26 | float deltaR=(((cMax-c.r)/6.)+(delta/2.))/delta, 27 | deltaG=(((cMax-c.g)/6.)+(delta/2.))/delta, 28 | deltaB=(((cMax-c.b)/6.)+(delta/2.))/delta; 29 | //Hue. 30 | if (c.r==cMax){ 31 | hsl.x=deltaB-deltaG; 32 | } else if (c.g==cMax){ 33 | hsl.x=(1./3.)+deltaR-deltaB; 34 | } else { //if(c.b==cMax){ 35 | hsl.x=(2./3.)+deltaG-deltaR; 36 | } 37 | hsl.x=fract(hsl.x); 38 | } 39 | return hsl; 40 | } 41 | 42 | //HSL to RGB. 43 | //Source: https://github.com/Jam3/glsl-hsl2rgb/blob/master/index.glsl 44 | vec3 hsl2rgb(vec3 hsl){ 45 | if (hsl.y==0.){ 46 | return vec3(hsl.z);//Luminance. 47 | } else { 48 | float b; 49 | if (hsl.z<.5){ 50 | b=hsl.z*(1.+hsl.y); 51 | } else { 52 | b=hsl.z+hsl.y-hsl.y*hsl.z; 53 | } 54 | float a=2.*hsl.z-b; 55 | return a+hue2rgb(hsl.x)*(b-a); 56 | } 57 | } -------------------------------------------------------------------------------- /Nanoforge/Editor/NanoDBReferenceResolver.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace Nanoforge.Editor; 6 | 7 | public class NanoDBReferenceResolver : ReferenceResolver 8 | { 9 | private uint _referenceCount; 10 | private readonly Dictionary _referenceIdToObjectMap = []; 11 | private readonly Dictionary _objectToReferenceIdMap = new (ReferenceEqualityComparer.Instance); 12 | 13 | public override void AddReference(string referenceId, object value) 14 | { 15 | if (!_referenceIdToObjectMap.TryAdd(referenceId, value)) 16 | { 17 | throw new JsonException(); 18 | } 19 | } 20 | 21 | public override string GetReference(object value, out bool alreadyExists) 22 | { 23 | if (_objectToReferenceIdMap.TryGetValue(value, out string? referenceId)) 24 | { 25 | alreadyExists = true; 26 | } 27 | else 28 | { 29 | _referenceCount++; 30 | referenceId = _referenceCount.ToString(); 31 | _objectToReferenceIdMap.Add(value, referenceId); 32 | alreadyExists = false; 33 | } 34 | 35 | return referenceId; 36 | } 37 | 38 | public override object ResolveReference(string referenceId) 39 | { 40 | if (!_referenceIdToObjectMap.TryGetValue(referenceId, out object? value)) 41 | { 42 | throw new JsonException(); 43 | } 44 | 45 | return value; 46 | } 47 | } 48 | 49 | class NanoDBReferenceHandler : ReferenceHandler 50 | { 51 | public NanoDBReferenceHandler() => Reset(); 52 | private ReferenceResolver? _rootedResolver; 53 | public override ReferenceResolver CreateResolver() => _rootedResolver!; 54 | public void Reset() => _rootedResolver = new NanoDBReferenceResolver(); 55 | } -------------------------------------------------------------------------------- /Nanoforge/Editor/NanoDBTypeResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Text.Json; 5 | using System.Text.Json.Serialization; 6 | using System.Text.Json.Serialization.Metadata; 7 | using Nanoforge.Misc; 8 | 9 | namespace Nanoforge.Editor; 10 | 11 | //Resolves all types that inherit a type using reflection. Used by NanoDB during serialization and deserialization. 12 | //This is so we don't need to put a [JsonDerivedType] on EditorObject and other bases types for every single type that inherits them. 13 | //Stupid default behavior that's prone to human error. We have a reflection for a reason. 14 | //More information here: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism 15 | public class NanoDBTypeResolver : DefaultJsonTypeInfoResolver 16 | { 17 | public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) 18 | { 19 | JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); 20 | 21 | if (jsonTypeInfo.Type.HasBaseType()) 22 | { 23 | jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions() 24 | { 25 | //TypeDiscriminatorPropertyName = "$point-type", 26 | IgnoreUnrecognizedTypeDiscriminators = false, 27 | UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization, 28 | DerivedTypes = {} 29 | }; 30 | var derivedTypes = Assembly.GetAssembly(typeof(EditorObject))?.GetTypes() 31 | .Select(type => type) 32 | .Where(type => jsonTypeInfo.Type.IsAssignableFrom(type)).ToArray(); 33 | 34 | if (derivedTypes != null) 35 | { 36 | foreach (var derivedType in derivedTypes) 37 | { 38 | jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derivedType, derivedType.Name)); 39 | } 40 | } 41 | 42 | } 43 | 44 | return jsonTypeInfo; 45 | } 46 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Dialogs/TaskDialog.axaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 68 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Nanoforge/Render/Resources/SimpleRenderObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | using Nanoforge.Render.Misc; 5 | using RFGM.Formats.Meshes.Shared; 6 | using Serilog; 7 | 8 | namespace Nanoforge.Render.Resources; 9 | 10 | public class SimpleRenderObject : RenderObjectBase 11 | { 12 | public Mesh Mesh; 13 | public Texture2D[] Textures = new Texture2D[10]; 14 | public readonly MaterialType MaterialType; 15 | 16 | public SimpleRenderObject(Vector3 position, Matrix4x4 orient, Mesh mesh, Texture2D[] textures, MaterialType materialType = MaterialType.Default) : base(position, orient, Vector3.One) 17 | { 18 | if (textures.Length > 10) 19 | { 20 | string err = $"Attempted to create a RenderObject with {textures.Length} textures. They can have 10 textures at most."; 21 | Log.Error(err); 22 | throw new Exception(err); 23 | } 24 | 25 | Position = position; 26 | Orient = orient; 27 | Mesh = mesh; 28 | for (var i = 0; i < textures.Length; i++) 29 | { 30 | Textures[i] = textures[i]; 31 | } 32 | //Use default white texture when one isn't provided. It simplifies the code to just give every RenderObject 10 textures 33 | if (textures.Length < 10) 34 | { 35 | for (int i = textures.Length; i < 10; i++) 36 | { 37 | Textures[i] = Texture2D.MissingTexture; 38 | } 39 | } 40 | 41 | MaterialType = materialType; 42 | } 43 | 44 | public override void WriteDrawCommands(List commands, Camera camera, GpuFrameDataWriter constants) 45 | { 46 | Matrix4x4 translation = Matrix4x4.CreateTranslation(Position); 47 | Matrix4x4 rotation = Orient; 48 | Matrix4x4 scale = Matrix4x4.CreateScale(Scale); 49 | Matrix4x4 model = rotation * translation * scale; 50 | 51 | MaterialInstance materialInstance = new() 52 | { 53 | Texture0 = Textures[0].Index, 54 | Texture1 = Textures[1].Index, 55 | Texture2 = Textures[2].Index, 56 | Texture3 = Textures[3].Index, 57 | Texture4 = Textures[4].Index, 58 | Texture5 = Textures[5].Index, 59 | Texture6 = Textures[6].Index, 60 | Texture7 = Textures[7].Index, 61 | Texture8 = Textures[8].Index, 62 | Texture9 = Textures[9].Index, 63 | Type = MaterialType, 64 | }; 65 | int materialIndex = constants.AddMaterialInstance(materialInstance); 66 | 67 | PerObjectConstants objectConstants = new() 68 | { 69 | Model = model, 70 | WorldPosition = new Vector4(Position.X, Position.Y, Position.Z, 1.0f), 71 | MaterialIndex = materialIndex, 72 | }; 73 | uint objectIndex = constants.AddObject(objectConstants); 74 | 75 | foreach (SubmeshData submesh in Mesh.Submeshes) 76 | { 77 | uint firstBlock = submesh.RenderBlocksOffset; 78 | for (int j = 0; j < submesh.NumRenderBlocks; j++) 79 | { 80 | RenderBlock block = Mesh.RenderBlocks[(int)(firstBlock + j)]; 81 | commands.Add(new RenderCommand 82 | { 83 | Mesh = Mesh, 84 | IndexCount = block.NumIndices, 85 | StartIndex = block.StartIndex, 86 | ObjectIndex = objectIndex, 87 | }); 88 | } 89 | } 90 | } 91 | 92 | public override void Destroy() 93 | { 94 | Mesh.Destroy(); 95 | foreach (Texture2D texture in Textures) 96 | { 97 | TextureManager.RemoveReference(texture); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /Nanoforge/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using Avalonia; 6 | using Avalonia.Controls; 7 | using Avalonia.Controls.ApplicationLifetimes; 8 | using Avalonia.Data.Core.Plugins; 9 | using Avalonia.Markup.Xaml; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Nanoforge.Gui.Themes; 12 | using Nanoforge.Gui.Views; 13 | using Nanoforge.Render; 14 | using Nanoforge.Services; 15 | using MainWindowViewModel = Nanoforge.Gui.ViewModels.MainWindowViewModel; 16 | 17 | namespace Nanoforge; 18 | 19 | public partial class App : Application 20 | { 21 | public readonly Renderer? Renderer; 22 | 23 | public static IThemeManager? ThemeManager; 24 | 25 | public new static App Current => (App)Application.Current!; 26 | 27 | public ServiceProvider Services { get; } 28 | 29 | private static IEnumerable Windows => (Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Windows ?? Array.Empty(); 30 | 31 | public App() 32 | { 33 | //Don't try starting the renderer if we're in the designer. Breaks the preview. 34 | if (!Design.IsDesignMode) 35 | { 36 | Renderer = new Renderer(1920, 1080); 37 | } 38 | Services = ConfigureServices(); 39 | } 40 | 41 | public override void Initialize() 42 | { 43 | ThemeManager = new FluentThemeManager(); 44 | ThemeManager.Initialize(this); 45 | 46 | AvaloniaXamlLoader.Load(this); 47 | 48 | //TODO: Make this user configurable 49 | //Default to dark theme for now 50 | ThemeManager.Switch(1); 51 | } 52 | 53 | public override void OnFrameworkInitializationCompleted() 54 | { 55 | // DockManager.s_enableSplitToWindow = true; 56 | 57 | var mainWindowViewModel = new MainWindowViewModel(); 58 | 59 | switch (ApplicationLifetime) 60 | { 61 | case IClassicDesktopStyleApplicationLifetime desktopLifetime: 62 | { 63 | // Line below is needed to remove Avalonia data validation. 64 | // Without this line you will get duplicate validations from both Avalonia and CT 65 | BindingPlugins.DataValidators.RemoveAt(0); 66 | var mainWindow = new MainWindow 67 | { 68 | DataContext = mainWindowViewModel 69 | }; 70 | 71 | mainWindow.Closing += (_, _) => 72 | { 73 | mainWindowViewModel.CloseLayout(); 74 | }; 75 | 76 | desktopLifetime.MainWindow = mainWindow; 77 | 78 | desktopLifetime.Exit += (_, _) => 79 | { 80 | mainWindowViewModel.CloseLayout(); 81 | }; 82 | 83 | break; 84 | } 85 | case ISingleViewApplicationLifetime singleViewLifetime: 86 | { 87 | var mainView = new MainView() 88 | { 89 | DataContext = mainWindowViewModel 90 | }; 91 | 92 | singleViewLifetime.MainView = mainView; 93 | 94 | break; 95 | } 96 | } 97 | 98 | base.OnFrameworkInitializationCompleted(); 99 | #if DEBUG 100 | this.AttachDevTools(); 101 | #endif 102 | } 103 | 104 | private ServiceProvider ConfigureServices() 105 | { 106 | var services = new ServiceCollection(); 107 | services.AddSingleton(); 108 | return services.BuildServiceProvider(); 109 | } 110 | 111 | public Window? FindWindowByViewModel(INotifyPropertyChanged viewModel) 112 | { 113 | return Windows.FirstOrDefault(x => ReferenceEquals(viewModel, x.DataContext)); 114 | } 115 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Tools/FileExplorer/FileExplorerView.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Avalonia.Controls; 5 | using Avalonia.Input; 6 | using Avalonia.Markup.Xaml; 7 | using MsBox.Avalonia; 8 | using MsBox.Avalonia.Enums; 9 | using Nanoforge.Gui.ViewModels; 10 | using Nanoforge.Gui.ViewModels.Documents.ChunkViewer; 11 | using Nanoforge.Gui.ViewModels.Tools.FileExplorer; 12 | using Serilog; 13 | 14 | namespace Nanoforge.Gui.Views.Tools.FileExplorer; 15 | 16 | public partial class FileExplorerView : UserControl 17 | { 18 | public static readonly List SupportedFileExtensions = [".cchk_pc", ".gchk_pc"]; 19 | 20 | public FileExplorerView() 21 | { 22 | InitializeComponent(); 23 | } 24 | 25 | private void InitializeComponent() 26 | { 27 | AvaloniaXamlLoader.Load(this); 28 | } 29 | 30 | //TODO: See if this can be done in the ViewModel. Ideally without directly referencing. Xaml behaviors might be helpful for this. 31 | private void FileTreeView_OnDoubleTapped(object? sender, TappedEventArgs e) 32 | { 33 | if (sender is not TreeView { SelectedItem: FileExplorerNodeViewModel node }) 34 | return; 35 | 36 | string extension = Path.GetExtension(node.Text); 37 | if (!SupportedFileExtensions.Contains(extension)) 38 | { 39 | if (extension != ".vpp_pc" && extension != ".str2_pc") //Don't plan on letting people open packfiles from the explorer + the popup is annoying when you're just double clicking to expand a node 40 | { 41 | Log.Warning($"Can not open file with extension {extension} in file explorer."); 42 | var messageBox = MessageBoxManager.GetMessageBoxStandard("Unsupported file type", $"Nanoforge can't open {extension} files from the file explorer yet.", ButtonEnum.Ok); 43 | messageBox.ShowWindowDialogAsync(MainWindow.Instance); 44 | } 45 | return; 46 | } 47 | 48 | Log.Information($"Opening {node.Text} from the file explorer...."); 49 | OpenFile(node); 50 | } 51 | 52 | private void OpenFile(FileExplorerNodeViewModel node) 53 | { 54 | try 55 | { 56 | //TODO: DO THIS BEFORE COMMIT - USE XTBL AS EXAMPLE OF ANOTHER FILE FORMAT THAT DOESN'T HAVE A GPU FILE 57 | //TODO: MAYBE ADD BASIC VIEWER FOR ANOTHER FORMAT LIKE STATIC MESHES TO BE SURE EVERYTHING WORKS CORRECTLY 58 | //TODO: Rewrite this to support an arbitrary number of formats. Support opening from either the cpu file or gpu file like below 59 | string extension = Path.GetExtension(node.Text); 60 | string cpuFilePath; 61 | if (extension == ".cchk_pc") 62 | { 63 | cpuFilePath = node.Path; 64 | } 65 | else if (extension == ".gchk_pc") 66 | { 67 | string gpuFilePath = node.Path; 68 | cpuFilePath = gpuFilePath.Replace(".gchk_pc", ".cchk_pc"); 69 | } 70 | else 71 | { 72 | throw new Exception($"Unsupported file extension {extension}"); 73 | } 74 | 75 | Console.WriteLine($"Opening {node.Text} from {cpuFilePath}"); 76 | 77 | DockFactory? dockFactory = MainWindow.DockFactory; 78 | if (dockFactory is null) 79 | { 80 | Log.Error("DockFactory not set when FileExplorerView.OpenFile() was called. Something went wrong."); 81 | throw new Exception("DockFactory not set when FileExplorerView.OpenFile() was called. Something went wrong."); 82 | } 83 | 84 | ChunkViewerDocumentViewModel document = new(cpuFilePath); 85 | document.Title = node.Text; 86 | dockFactory.DocumentDock?.AddNewDocument(document); 87 | } 88 | catch (Exception ex) 89 | { 90 | Log.Error(ex, $"Error opening file {node.Text} in the file explorer."); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Nanoforge/Render/TextureManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Nanoforge.Render.Resources; 5 | 6 | namespace Nanoforge.Render; 7 | 8 | //Tracks all textures used by the renderer and their reference count 9 | public static class TextureManager 10 | { 11 | //Note: If you change this, also update the texture array size in Constants.glsl 12 | public const int MaxTextures = 8192; 13 | 14 | public static bool DescriptorSetsNeedUpdate = false; 15 | 16 | public class TextureSlot 17 | { 18 | public string? TextureName; 19 | public Texture2D? Texture; 20 | public int ReferenceCount = 0; 21 | public bool InUse { get; set; } = false; 22 | public bool NeverDestroy { get; set; } = false; 23 | public readonly int Index; 24 | 25 | public TextureSlot(int index) 26 | { 27 | Index = index; 28 | } 29 | } 30 | 31 | public static TextureSlot[] TextureSlots = new TextureSlot[MaxTextures]; 32 | 33 | static TextureManager() 34 | { 35 | for (var index = 0; index < TextureSlots.Length; index++) 36 | { 37 | TextureSlot slot = new(index) 38 | { 39 | InUse = false, 40 | ReferenceCount = 0, 41 | 42 | }; 43 | TextureSlots[index] = slot; 44 | } 45 | } 46 | 47 | public static bool IsTextureLoaded(string textureName) 48 | { 49 | return TextureSlots.Any(t => t.TextureName == textureName); 50 | } 51 | 52 | public static void NewTexture(string textureName, Texture2D texture, bool neverDestroy = false) 53 | { 54 | TextureSlot? slot = GetNextOpenSlot(); 55 | if (slot == null) 56 | { 57 | throw new Exception($"Exceeded maximum texture count of {MaxTextures}."); 58 | } 59 | if (TextureSlots.Any(textureMetadata => textureMetadata.TextureName == textureName)) 60 | { 61 | return; 62 | } 63 | 64 | slot.TextureName = textureName; 65 | slot.Texture = texture; 66 | slot.ReferenceCount = 1; 67 | slot.InUse = true; 68 | slot.NeverDestroy = neverDestroy; 69 | texture.Index = slot.Index; 70 | DescriptorSetsNeedUpdate = true; 71 | } 72 | 73 | private static TextureSlot? GetNextOpenSlot() 74 | { 75 | return TextureSlots.FirstOrDefault(slot => !slot.InUse); 76 | } 77 | 78 | public static Texture2D? GetTexture(string textureName) 79 | { 80 | TextureSlot? metadata = TextureSlots.FirstOrDefault(slot => slot.TextureName == textureName && slot.InUse); 81 | if (metadata != null) 82 | { 83 | metadata.ReferenceCount++; 84 | } 85 | 86 | return metadata?.Texture; 87 | } 88 | 89 | public static void RemoveReference(Texture2D texture) 90 | { 91 | TextureSlot? slot = TextureSlots.FirstOrDefault(slot => slot.Texture == texture && slot.InUse); 92 | if (slot == null) 93 | return; 94 | 95 | slot.ReferenceCount = Math.Clamp(--slot.ReferenceCount, 0, int.MaxValue); 96 | if (slot is { ReferenceCount: 0, NeverDestroy: false }) 97 | { 98 | slot.Texture!.Destroy(); 99 | slot.Texture = null; 100 | slot.InUse = false; 101 | slot.TextureName = null; 102 | DescriptorSetsNeedUpdate = true; 103 | } 104 | } 105 | 106 | public static void DestroyUnusedTextures() 107 | { 108 | foreach (TextureSlot slot in TextureSlots.Where(metadata => metadata is { ReferenceCount: 0, NeverDestroy: false, InUse: true, Texture: not null }).ToArray()) 109 | { 110 | slot.Texture!.Destroy(); 111 | slot.Texture = null; 112 | slot.InUse = false; 113 | slot.TextureName = null; 114 | slot.ReferenceCount = 0; 115 | } 116 | DescriptorSetsNeedUpdate = true; 117 | } 118 | } -------------------------------------------------------------------------------- /Nanoforge/assets/fonts/SIL Open Font License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Mariela Monsalve (marmonsalve@gmail.com), 2 | Copyright (c) 2011, Angelina Sanchez (ange_dg@yahoo.com.ar), 3 | With Reserved Font Name "Ruda" 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. 14 | 15 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. 16 | 17 | DEFINITIONS 18 | "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. 19 | 20 | "Reserved Font Name" refers to any names specified as such after the copyright statement(s). 21 | 22 | "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. 25 | 26 | "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. 27 | 28 | PERMISSION & CONDITIONS 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 30 | 31 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 32 | 33 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 34 | 35 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 36 | 37 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 38 | 39 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. 40 | 41 | TERMINATION 42 | This license becomes null and void if any of the above conditions are not met. 43 | 44 | DISCLAIMER 45 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /Nanoforge/Misc/DataHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Serilog; 5 | 6 | namespace Nanoforge.Misc; 7 | 8 | public static class DataHelper 9 | { 10 | public static Toggleable DeepCopyToggleableBytes(Toggleable input) 11 | { 12 | if (input.Value == null) 13 | { 14 | return new Toggleable(null); 15 | } 16 | 17 | byte[] bytesCopy = new byte[input.Value.Length]; 18 | input.Value.CopyTo(bytesCopy, 0); 19 | return new Toggleable(bytesCopy); 20 | } 21 | 22 | public static bool ToRfgName(T value, out string rfgName) where T : Enum 23 | { 24 | Type enumType = typeof(T); 25 | foreach (FieldInfo field in enumType.GetFields(BindingFlags.Static | BindingFlags.Public)) 26 | { 27 | if (field.GetCustomAttribute() is { } attribute) 28 | { 29 | //object fieldValue = field.GetValue(value); 30 | var fieldValue = (T?)field.GetValue(null); 31 | if (fieldValue != null && EqualityComparer.Default.Equals(fieldValue, value)) 32 | { 33 | rfgName = attribute.Name; 34 | return true; 35 | } 36 | } 37 | } 38 | 39 | rfgName = string.Empty; 40 | return false; 41 | } 42 | 43 | public static bool FromRfgName(string rfgName, out T value) where T : Enum 44 | { 45 | Type enumType = typeof(T); 46 | foreach (FieldInfo field in enumType.GetFields(BindingFlags.Static | BindingFlags.Public)) 47 | { 48 | if (field.GetCustomAttribute() is { } attribute) 49 | { 50 | if (rfgName == attribute.Name && field.GetValue(null) is T enumValue) 51 | { 52 | value = enumValue; 53 | return true; 54 | } 55 | } 56 | } 57 | 58 | value = default!; 59 | return false; 60 | } 61 | 62 | //Converts bitflag enums to space separated list of strings based on the [RfgName] for each flag. Used in rfgzone_pc files. 63 | //For example. An ObjectMover with ChunkFlags = (.Building | .WorldAnchor) would be converted to "building world_anchor" 64 | public static bool ToRfgFlagsString(T value, out string flagsString) where T : Enum 65 | { 66 | flagsString = ""; 67 | Type enumType = typeof(T); 68 | int valueAsInt = (int)Convert.ChangeType(value, typeof(int)); 69 | foreach (FieldInfo field in enumType.GetFields(BindingFlags.Static | BindingFlags.Public)) //Loop through flags 70 | { 71 | T? flagEnum = (T?)field.GetValue(null); 72 | if (flagEnum == null) 73 | continue; 74 | 75 | int fieldValue = (int)Convert.ChangeType(flagEnum, typeof(int)); 76 | if (fieldValue == 0) 77 | continue; //0 value isn't ever displayed in the string. Only used in code as a default. If not flags are set the result is an empty string. 78 | 79 | if (field.GetCustomAttribute() is { } attribute) 80 | { 81 | if ((valueAsInt & fieldValue) != 0) //Flag is set 82 | { 83 | if (flagsString.Length > 0) 84 | { 85 | flagsString += " "; 86 | } 87 | flagsString += attribute.Name; 88 | } 89 | } 90 | else 91 | { 92 | Log.Error("Failed to convert enum of type '{0}' to RFG bitflag string. Field '{1}' is missing [RfgName]", typeof(T).FullName, field.Name); 93 | } 94 | } 95 | 96 | return true; 97 | } 98 | 99 | public static unsafe bool FromRfgFlagsString(string flagsString, out T value) where T : unmanaged, Enum 100 | { 101 | int flags = 0; 102 | Type enumType = typeof(T); 103 | foreach (FieldInfo field in enumType.GetFields(BindingFlags.Static | BindingFlags.Public)) //Loop through flags and see which rfg names are present in str 104 | { 105 | T? flagEnum = (T?)field.GetValue(null); 106 | if (flagEnum == null) 107 | continue; 108 | 109 | int fieldValue = (int)Convert.ChangeType(flagEnum, typeof(int)); 110 | if (fieldValue == 0) 111 | continue; //0 value isn't ever displayed in the string. Only used in code as a default. If not flags are set the result is an empty string. 112 | if (field.GetCustomAttribute() is { } attribute) 113 | { 114 | if (flagsString.Contains(attribute.Name)) 115 | { 116 | flags |= fieldValue; 117 | } 118 | } 119 | } 120 | value = *(T*)&flags; 121 | return true; 122 | } 123 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Resources/VkBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using Silk.NET.Vulkan; 4 | using Buffer = Silk.NET.Vulkan.Buffer; 5 | 6 | namespace Nanoforge.Render.Resources; 7 | 8 | public unsafe class VkBuffer : VkMemory 9 | { 10 | private Buffer _vkHandle; 11 | public Buffer VkHandle => _vkHandle; 12 | public ulong Size { get; private set; } 13 | 14 | private readonly BufferUsageFlags _usage; 15 | private readonly MemoryPropertyFlags _properties; 16 | 17 | public readonly bool CanGrow; 18 | 19 | public VkBuffer(RenderContext context, ulong size, BufferUsageFlags usage, MemoryPropertyFlags properties, bool canGrow = false) : base(context) 20 | { 21 | Size = size; 22 | _usage = usage; 23 | _properties = properties; 24 | CanGrow = canGrow; 25 | Init(); 26 | } 27 | 28 | private void Init() 29 | { 30 | BufferCreateInfo bufferInfo = new() 31 | { 32 | SType = StructureType.BufferCreateInfo, 33 | Size = this.Size, 34 | Usage = this._usage, 35 | SharingMode = SharingMode.Exclusive, 36 | }; 37 | 38 | fixed (Buffer* bufferPtr = &_vkHandle) 39 | { 40 | if (Vk.CreateBuffer(Device, in bufferInfo, null, bufferPtr) != Result.Success) 41 | { 42 | throw new Exception("failed to create vulkan buffer!"); 43 | } 44 | } 45 | 46 | MemoryRequirements memRequirements = new(); 47 | Vk.GetBufferMemoryRequirements(Device, VkHandle, out memRequirements); 48 | 49 | MemoryAllocateInfo allocateInfo = new() 50 | { 51 | SType = StructureType.MemoryAllocateInfo, 52 | AllocationSize = memRequirements.Size, 53 | MemoryTypeIndex = Context.FindMemoryType(memRequirements.MemoryTypeBits, _properties), 54 | }; 55 | 56 | fixed (DeviceMemory* bufferMemoryPtr = &Memory) 57 | { 58 | if (Vk.AllocateMemory(Device, in allocateInfo, null, bufferMemoryPtr) != Result.Success) 59 | { 60 | throw new Exception("Failed to allocate VkBuffer memory!"); 61 | } 62 | } 63 | 64 | Vk.BindBufferMemory(Device, VkHandle, Memory, 0); 65 | } 66 | 67 | public void SetData(ref T data, ulong offset = 0) where T : unmanaged 68 | { 69 | fixed (void* ptr = &data) 70 | { 71 | int sizeInBytes = Unsafe.SizeOf(); 72 | SetData(new Span(ptr, sizeInBytes), offset); 73 | } 74 | } 75 | 76 | public void SetData(Span data, ulong offset = 0) where T : unmanaged 77 | { 78 | fixed (void* ptr = data) 79 | { 80 | int sizeInBytes = Unsafe.SizeOf() * data.Length; 81 | SetData(new Span(ptr, sizeInBytes), offset); 82 | } 83 | } 84 | 85 | public void SetData(Span data, ulong offset = 0) 86 | { 87 | if ((ulong)data.Length > Size) 88 | { 89 | if (!CanGrow) 90 | { 91 | throw new Exception("Buffer size exceeded! Auto buffer resize not yet implemented!"); 92 | } 93 | 94 | Console.WriteLine($"Growing buffer from {Size} bytes to {data.Length} bytes"); 95 | Destroy(); 96 | Size = (ulong)data.Length; 97 | Init(); 98 | } 99 | 100 | void* ptr = null; 101 | MapMemory(ref ptr); 102 | byte* offsetPtr = (byte*)ptr; 103 | offsetPtr += offset; 104 | data.CopyTo(new Span(offsetPtr, data.Length)); 105 | UnmapMemory(); 106 | } 107 | 108 | public unsafe void Destroy() 109 | { 110 | Vk.DestroyBuffer(Device, VkHandle, null); 111 | Vk.FreeMemory(Device, Memory, null); 112 | } 113 | 114 | public void CopyTo(VkBuffer destination, ulong copySize, CommandPool pool, Queue queue) 115 | { 116 | CommandBuffer commandBuffer = Context.BeginSingleTimeCommands(pool); 117 | BufferCopy copyRegion = new() 118 | { 119 | Size = copySize, 120 | }; 121 | 122 | Vk.CmdCopyBuffer(commandBuffer, srcBuffer: this.VkHandle, dstBuffer: destination.VkHandle, 1, in copyRegion); 123 | 124 | Context.EndSingleTimeCommands(commandBuffer, pool, queue); 125 | } 126 | 127 | public void CopyToImage(Image image, uint width, uint height, CommandPool pool, Queue queue) 128 | { 129 | CommandBuffer commandBuffer = Context.BeginSingleTimeCommands(pool); 130 | 131 | BufferImageCopy region = new() 132 | { 133 | BufferOffset = 0, 134 | BufferRowLength = 0, 135 | BufferImageHeight = 0, 136 | ImageSubresource = 137 | { 138 | AspectMask = ImageAspectFlags.ColorBit, 139 | MipLevel = 0, 140 | BaseArrayLayer = 0, 141 | LayerCount = 1, 142 | }, 143 | ImageOffset = new Offset3D(0, 0, 0), 144 | ImageExtent = new Extent3D(width, height, 1), 145 | 146 | }; 147 | 148 | Context.Vk.CmdCopyBufferToImage(commandBuffer, _vkHandle, image, ImageLayout.TransferDstOptimal, 1, in region); 149 | 150 | Context.EndSingleTimeCommands(commandBuffer, pool, queue); 151 | } 152 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/Views/Tools/FileExplorer/FileExplorerView.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 44 | 47 | 50 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 63 | 64 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Nanoforge/FileSystem/FileEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using ICSharpCode.SharpZipLib.Zip.Compression.Streams; 5 | using RFGM.Formats.Streams; 6 | using RFGM.Formats.Vpp.Models; 7 | using Serilog; 8 | 9 | namespace Nanoforge.FileSystem; 10 | 11 | //Primitive file - anything that isn't a vpp_pc or str2_pc 12 | public class FileEntry(string name, long dataOffset = 0, uint size = 0, uint compressedSize = 0) : EntryBase(name, long.MaxValue, dataOffset, size, compressedSize) 13 | { 14 | public override bool IsDirectory => false; 15 | 16 | public override bool IsFile => true; 17 | 18 | public byte[]? Data; 19 | public bool Preloaded => Data != null; 20 | 21 | public override Stream? OpenStream() 22 | { 23 | try 24 | { 25 | //If PackfileVFS.PreloadDirectory() is called then files can have their contents cached in memory 26 | if (Data != null) 27 | { 28 | return new MemoryStream(Data); 29 | } 30 | 31 | if (Parent == null) 32 | return null; 33 | 34 | DirectoryEntry parent = (DirectoryEntry)Parent!; 35 | bool compressed = parent.Compressed; 36 | bool condensed = parent.Condensed; 37 | bool compacted = compressed && condensed; 38 | 39 | using Stream? parentStream = parent.OpenStream(); 40 | if (parentStream == null) 41 | { 42 | Log.Error($"Failed to open parent stream in FileEntry.OpenStream() for file entry '{Name}'"); 43 | return null; 44 | } 45 | 46 | parentStream.Seek(0, SeekOrigin.Begin); 47 | if (compacted) 48 | { 49 | byte[] inflateBuffer = new byte[parent.DataBlockSize]; 50 | 51 | //For compacted files we must decompress the entire block 52 | StreamView compressedView = new(parentStream, parent.DataBlockOffset, parent.DataBlockSizeCompressed, Name); 53 | using InflaterInputStream inflaterStream = new(compressedView); 54 | int bytesRead = inflaterStream.Read(inflateBuffer); 55 | if (bytesRead != inflateBuffer.Length) 56 | { 57 | Log.Error($"Failed to inflate data for file entry '{Name}'. Expected {inflateBuffer.Length} bytes but got {bytesRead}."); 58 | return null; 59 | } 60 | 61 | //TODO: See if this could be done without an intermediate buffer by using StreamViews. Have to make sure inflation works properly if the StreamView offset is not 0 62 | byte[] justThisFilesBytes = new byte[Size]; 63 | Array.Copy(sourceArray: inflateBuffer, sourceIndex: DataOffset, 64 | destinationArray: justThisFilesBytes, destinationIndex: 0, length: justThisFilesBytes.Length); 65 | 66 | MemoryStream result = new(justThisFilesBytes); 67 | return result; 68 | } 69 | else if (compressed) 70 | { 71 | byte[] inflateBuffer = new byte[Size]; 72 | using InflaterInputStream inflaterStream = new(parentStream); 73 | inflaterStream.Skip(parent.DataBlockOffset + DataOffset); //InflaterInputStream cannot seek 74 | 75 | var bytesRead = inflaterStream.Read(inflateBuffer); 76 | if (bytesRead != inflateBuffer.Length) 77 | { 78 | Log.Error($"Failed to inflate data for file entry '{Name}'. Expected {inflateBuffer.Length} bytes but got {bytesRead}."); 79 | return null; 80 | } 81 | 82 | MemoryStream result = new(inflateBuffer); 83 | return result; 84 | } 85 | else 86 | { 87 | byte[] bytes = new byte[Size]; 88 | parentStream.Seek(parent.DataBlockOffset + DataOffset, SeekOrigin.Begin); 89 | int bytesRead = parentStream.Read(bytes); 90 | if (bytesRead != bytes.Length) 91 | { 92 | Log.Error($"Failed to read data for file entry '{Name}'. Expected {bytes.Length} bytes but got {bytesRead}."); 93 | return null; 94 | } 95 | 96 | MemoryStream result = new(bytes); 97 | return result; 98 | } 99 | } 100 | catch (Exception ex) 101 | { 102 | Log.Error($"Exception in FileEntry.OpenStream() for {Name}. Ex: {ex.Message}"); 103 | return null; 104 | } 105 | } 106 | 107 | public override byte[]? ReadAllBytes() 108 | { 109 | using Stream? stream = OpenStream(); 110 | if (stream == null) 111 | return null; 112 | 113 | byte[] buffer = new byte[stream.Length]; 114 | var bytesRead = stream.Read(buffer, 0, buffer.Length); 115 | if (bytesRead != buffer.Length) 116 | { 117 | Log.Error($"FileEntry.ReadAllBytes() failed to read {Name}. Expected {buffer.Length} bytes but got {bytesRead} bytes."); 118 | return null; 119 | } 120 | 121 | return buffer; 122 | } 123 | 124 | public override string? ReadAllText() 125 | { 126 | using Stream? stream = OpenStream(); 127 | if (stream == null) 128 | return null; 129 | 130 | using StreamReader reader = new(stream); 131 | string result = reader.ReadToEnd(); 132 | return result; 133 | } 134 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Materials/DescriptorAllocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Silk.NET.Vulkan; 4 | 5 | namespace Nanoforge.Render.Materials; 6 | 7 | //Originally based on the DescriptorAllocatorGrowable from vkguide.dev: https://vkguide.dev/docs/new_chapter_4/descriptor_abstractions/ 8 | //It manages a list of descriptor pools and allocates new ones on demand when more room is needed for descriptor sets. 9 | public class DescriptorAllocator 10 | { 11 | private RenderContext _context; 12 | private PoolSizeRatio[] _ratios; 13 | private List _fullPools = new(); 14 | private List _readyPools = new(); 15 | private uint _setsPerPool; 16 | private const uint MaxSetsPerPool = 4096; 17 | 18 | public struct PoolSizeRatio 19 | { 20 | public DescriptorType Type; 21 | public float Ratio; 22 | } 23 | 24 | public DescriptorAllocator(RenderContext context, uint maxSets, PoolSizeRatio[] poolRatios) 25 | { 26 | _context = context; 27 | _ratios = poolRatios; 28 | 29 | DescriptorPool newPool = CreatePool(maxSets, poolRatios); 30 | _setsPerPool = (uint)(maxSets * 1.5f); //Grow it next allocation 31 | _readyPools.Add(newPool); 32 | } 33 | 34 | public void ClearPools() 35 | { 36 | foreach (DescriptorPool pool in _readyPools) 37 | { 38 | _context.Vk.ResetDescriptorPool(_context.Device, pool, 0); 39 | } 40 | foreach (DescriptorPool pool in _fullPools) 41 | { 42 | _context.Vk.ResetDescriptorPool(_context.Device, pool, 0); 43 | _readyPools.Add(pool); 44 | } 45 | _fullPools.Clear(); 46 | } 47 | 48 | public unsafe void Destroy() 49 | { 50 | foreach (DescriptorPool pool in _readyPools) 51 | { 52 | _context.Vk.DestroyDescriptorPool(_context.Device, pool, null); 53 | } 54 | foreach (DescriptorPool pool in _fullPools) 55 | { 56 | _context.Vk.DestroyDescriptorPool(_context.Device, pool, null); 57 | } 58 | _readyPools.Clear(); 59 | _fullPools.Clear(); 60 | } 61 | 62 | public unsafe DescriptorSet Allocate(DescriptorSetLayout layout, void* next = null) 63 | { 64 | DescriptorPool poolToUse = GetPool(); 65 | 66 | DescriptorSetAllocateInfo allocInfo = new() 67 | { 68 | SType = StructureType.DescriptorSetAllocateInfo, 69 | PNext = next, 70 | DescriptorPool = poolToUse, 71 | DescriptorSetCount = 1, 72 | PSetLayouts = &layout 73 | }; 74 | 75 | DescriptorSet descriptorSet; 76 | Result result = _context.Vk.AllocateDescriptorSets(_context.Device, &allocInfo, out descriptorSet); 77 | 78 | if (result == Result.ErrorOutOfPoolMemory || result == Result.ErrorFragmentedPool) 79 | { 80 | //Allocation failed. Try again with new pool. 81 | _fullPools.Add(poolToUse); 82 | 83 | poolToUse = GetPool(); 84 | allocInfo.DescriptorPool = poolToUse; 85 | 86 | result = _context.Vk.AllocateDescriptorSets(_context.Device, &allocInfo, out descriptorSet); 87 | } 88 | if (result != Result.Success) 89 | { 90 | throw new Exception($"DescriptorAllocator failed to allocate descriptor set. Result: {result}"); 91 | } 92 | 93 | _readyPools.Add(poolToUse); 94 | return descriptorSet; 95 | } 96 | 97 | private DescriptorPool GetPool() 98 | { 99 | DescriptorPool newPool; 100 | if (_readyPools.Count != 0) 101 | { 102 | int lastIndex = _readyPools.Count - 1; 103 | newPool = _readyPools[lastIndex]; 104 | _readyPools.RemoveAt(lastIndex); 105 | } 106 | else 107 | { 108 | //Need to create a new pool 109 | newPool = CreatePool(_setsPerPool, _ratios); 110 | 111 | //Increase sets per pool so that each successive allocation is larger 112 | _setsPerPool = (uint)(_setsPerPool * 1.5f); 113 | if (_setsPerPool > MaxSetsPerPool) 114 | { 115 | _setsPerPool = MaxSetsPerPool; 116 | } 117 | } 118 | 119 | return newPool; 120 | } 121 | 122 | private unsafe DescriptorPool CreatePool(uint setCount, Span poolRatios) 123 | { 124 | var poolSizes = new DescriptorPoolSize[poolRatios.Length]; 125 | for (int i = 0; i < poolRatios.Length; i++) 126 | { 127 | PoolSizeRatio ratio = poolRatios[i]; 128 | poolSizes[i] = new DescriptorPoolSize 129 | { 130 | Type = ratio.Type, 131 | DescriptorCount = (uint)(ratio.Ratio * setCount), 132 | }; 133 | } 134 | 135 | fixed (DescriptorPoolSize* poolSizesPtr = poolSizes) 136 | { 137 | DescriptorPoolCreateInfo createInfo = new() 138 | { 139 | SType = StructureType.DescriptorPoolCreateInfo, 140 | Flags = 0, 141 | MaxSets = setCount, 142 | PoolSizeCount = (uint)poolSizes.Length, 143 | PPoolSizes = poolSizesPtr, 144 | }; 145 | 146 | DescriptorPool newPool; 147 | if (_context.Vk.CreateDescriptorPool(_context.Device, &createInfo, null, &newPool) != Result.Success) 148 | { 149 | throw new Exception("Failed to create descriptor pool"); 150 | } 151 | 152 | return newPool; 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Materials/MaterialHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using Nanoforge.Render.Misc; 5 | using Nanoforge.Render.Resources; 6 | using Serilog; 7 | using Silk.NET.Vulkan; 8 | using VkPrimitiveTopology = Silk.NET.Vulkan.PrimitiveTopology; 9 | 10 | namespace Nanoforge.Render.Materials; 11 | 12 | public static class MaterialHelper 13 | { 14 | private static RenderContext? _context; 15 | private static bool _initialized = false; 16 | 17 | private static Dictionary _materials = new(); 18 | 19 | //Note: At the moment there's just one render pass used by the renderer. The MaterialPipelines need it, so it gets passed here. 20 | private static RenderPass _renderPass; 21 | private static VkBuffer[]? _uniformBuffers; 22 | private static VkBuffer[]? _perObjectConstantBuffers; 23 | private static VkBuffer[]? _materialInfoBuffers; 24 | 25 | public static void Init(RenderContext context, RenderPass renderPass, VkBuffer[] uniformBuffers, VkBuffer[] perObjectConstantBuffers, VkBuffer[] materialInfoBuffers) 26 | { 27 | try 28 | { 29 | if (_initialized) 30 | { 31 | throw new InvalidOperationException("Attempted to initializer MaterialHelper more than once. Not allowed."); 32 | } 33 | 34 | _context = context; 35 | _renderPass = renderPass; 36 | _uniformBuffers = uniformBuffers; 37 | _perObjectConstantBuffers = perObjectConstantBuffers; 38 | _materialInfoBuffers = materialInfoBuffers; 39 | 40 | CreateMaterial("UnifiedMaterial", VkPrimitiveTopology.TriangleStrip, stride: 24, 41 | attributes: 42 | [ 43 | new VertexInputAttributeDescription { Binding = 0, Location = 0, Format = Format.R32G32B32Sfloat, Offset = 0 }, //Position 44 | new VertexInputAttributeDescription { Binding = 0, Location = 1, Format = Format.R8G8B8A8Unorm, Offset = 12 }, //Normal 45 | new VertexInputAttributeDescription { Binding = 0, Location = 2, Format = Format.R8G8B8A8Unorm, Offset = 16 }, //Tangent 46 | new VertexInputAttributeDescription { Binding = 0, Location = 3, Format = Format.R16G16Sint, Offset = 20 } //TexCoord0 47 | ] 48 | ); 49 | CreateMaterial("Linelist", VkPrimitiveTopology.LineList, stride: 20, 50 | attributes: 51 | [ 52 | new VertexInputAttributeDescription { Binding = 0, Location = 0, Format = Format.R32G32B32A32Sfloat, Offset = 0 }, //Position and size 53 | new VertexInputAttributeDescription { Binding = 0, Location = 1, Format = Format.R8G8B8A8Unorm, Offset = 16 }, //Color 54 | ] 55 | ); 56 | //TODO: See if there's a way to draw these without disabling face culling. Was having problems with some faces being hidden at some camera angles regardless of front face being CW or CCW 57 | CreateMaterial("SolidTriList", VkPrimitiveTopology.TriangleList, stride: 16, 58 | attributes: 59 | [ 60 | new VertexInputAttributeDescription { Binding = 0, Location = 0, Format = Format.R32G32B32Sfloat, Offset = 0 }, //Position 61 | new VertexInputAttributeDescription { Binding = 0, Location = 1, Format = Format.R8G8B8A8Unorm, Offset = 12 }, //Color 62 | ], 63 | disableFaceCulling: true); 64 | 65 | _initialized = true; 66 | } 67 | catch (Exception ex) 68 | { 69 | Log.Error($"MaterialHelper.Init() failed: `{ex.Message}`"); 70 | throw; 71 | } 72 | } 73 | 74 | private static void CreateMaterial(string name, VkPrimitiveTopology topology, uint stride, Span attributes, bool disableFaceCulling = false) 75 | { 76 | if (_materials.ContainsKey(name)) 77 | throw new Exception($"Material with name '{name}' already exists!"); 78 | if (_uniformBuffers is null || _perObjectConstantBuffers is null || _materialInfoBuffers is null) 79 | throw new Exception("Required GPU buffers not passed to MaterialHelper!"); 80 | 81 | MaterialPipeline materialPipeline = new(_context!, name, _renderPass, topology, stride, attributes, _uniformBuffers, _perObjectConstantBuffers, _materialInfoBuffers, disableFaceCulling); 82 | _materials.Add(name, materialPipeline); 83 | } 84 | 85 | public static MaterialPipeline? GetMaterialPipeline(string name) 86 | { 87 | foreach (var kv in _materials) 88 | { 89 | if (kv.Key.Equals(name, StringComparison.InvariantCultureIgnoreCase)) 90 | { 91 | return kv.Value; 92 | } 93 | } 94 | 95 | return null; 96 | } 97 | 98 | public static void ReloadEditedShaders() 99 | { 100 | foreach (MaterialPipeline material in _materials.Values) 101 | { 102 | material.ReloadEditedShaders(); 103 | } 104 | } 105 | 106 | public static void Destroy() 107 | { 108 | foreach (MaterialPipeline material in _materials.Values) 109 | { 110 | material.Destroy(); 111 | } 112 | 113 | _materials.Clear(); 114 | } 115 | 116 | public static void UpdateTextureArrayDescriptors() 117 | { 118 | foreach (MaterialPipeline pipeline in _materials.Values) 119 | { 120 | pipeline.UpdateDescriptorSets(); 121 | //pipeline.UpdateTextureArrayDescriptors(); 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /Nanoforge/Render/Camera.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Numerics; 4 | using Avalonia.Input; 5 | using Nanoforge.Gui; 6 | using Nanoforge.Gui.Views; 7 | using Nanoforge.Gui.Views.Controls; 8 | 9 | namespace Nanoforge.Render; 10 | 11 | public class Camera 12 | { 13 | public Vector3 Position; 14 | public Vector3 TargetPosition; 15 | 16 | public Matrix4x4 View; 17 | public Matrix4x4 Projection; 18 | 19 | public float FovRadians = 60.0f; 20 | public float PitchRadians; 21 | public float YawRadians; 22 | public float TargetPitchRadians; 23 | public float TargetYawRadians; 24 | 25 | private float _aspectRatio; 26 | private float _nearPlane; 27 | private float _farPlane; 28 | 29 | public float Speed = 150.0f; 30 | public float MovementSmoothing = 0.125f; 31 | public float LookSensitivity = 0.03f; 32 | public float LookSmoothing = 0.4f; 33 | 34 | public Vector3 Forward; 35 | public Vector3 Up; 36 | public Vector3 Right; 37 | 38 | public Camera(Vector3 position, float fovDegrees, Vector2 viewportSize, float nearPlane, float farPlane) 39 | { 40 | Position = position; 41 | TargetPosition = position; 42 | FovRadians = MathHelpers.ToRadians(fovDegrees); 43 | _aspectRatio = viewportSize.X / viewportSize.Y; 44 | _nearPlane = nearPlane; 45 | _farPlane = farPlane; 46 | 47 | Forward = Vector3.Normalize(-position); 48 | Up = Vector3.UnitY; 49 | Right = Vector3.Cross(Up, Forward); 50 | 51 | TargetPitchRadians = PitchRadians = 0.66f; 52 | TargetYawRadians = YawRadians = 0.72f; 53 | UpdateViewMatrix(); 54 | UpdateProjectionMatrix(); 55 | } 56 | 57 | public void Update(SceneFrameUpdateParams updateParams) 58 | { 59 | Position = Vector3.Lerp(Position, TargetPosition, MovementSmoothing); 60 | 61 | if (updateParams.CameraControlsEnabled) 62 | { 63 | Input input = (MainWindow.Instance as MainWindow)!.Input; 64 | if (input.IsKeyDown(Key.W)) 65 | { 66 | TargetPosition += updateParams.DeltaTime * Speed * Forward; 67 | } 68 | else if (input.IsKeyDown(Key.S)) 69 | { 70 | TargetPosition += updateParams.DeltaTime * Speed * -Forward; 71 | } 72 | 73 | if (input.IsKeyDown(Key.A)) 74 | { 75 | TargetPosition += updateParams.DeltaTime * Speed * Right; 76 | } 77 | else if (input.IsKeyDown(Key.D)) 78 | { 79 | TargetPosition += updateParams.DeltaTime * Speed * -Right; 80 | } 81 | 82 | if (input.IsKeyDown(Key.Q)) 83 | { 84 | TargetPosition.Y -= updateParams.DeltaTime * Speed; 85 | } 86 | else if (input.IsKeyDown(Key.E)) 87 | { 88 | TargetPosition.Y += updateParams.DeltaTime * Speed; 89 | } 90 | } 91 | 92 | UpdateRotationFromMouse(updateParams); 93 | YawRadians = MathHelpers.Lerp(YawRadians, TargetYawRadians, LookSmoothing); 94 | PitchRadians = MathHelpers.Lerp(PitchRadians, TargetPitchRadians, LookSmoothing); 95 | 96 | UpdateViewMatrix(); 97 | } 98 | 99 | private void UpdateRotationFromMouse(SceneFrameUpdateParams updateParams) 100 | { 101 | MouseState mouse = updateParams.Mouse; 102 | if (mouse.RightMouseButtonDown) 103 | { 104 | TargetYawRadians += -mouse.PositionDelta.X * LookSensitivity; 105 | TargetPitchRadians += mouse.PositionDelta.Y * LookSensitivity; 106 | 107 | float maxPitch = MathHelpers.ToRadians(89.0f); 108 | float minPitch = MathHelpers.ToRadians(-89.0f); 109 | 110 | if (TargetPitchRadians > maxPitch) 111 | TargetPitchRadians = maxPitch; 112 | if (TargetPitchRadians < minPitch) 113 | TargetPitchRadians = minPitch; 114 | } 115 | } 116 | 117 | public void UpdateViewMatrix() 118 | { 119 | Vector3 defaultForward = new Vector3(0.0f, 0.0f, 1.0f); 120 | Vector3 defaultRight = new Vector3(1.0f, 0.0f, 0.0f); 121 | 122 | Matrix4x4 rotation = Matrix4x4.CreateFromYawPitchRoll(YawRadians, PitchRadians, roll: 0.0f); 123 | 124 | Right = Transform(defaultRight, rotation); 125 | Forward = Transform(defaultForward, rotation); 126 | Up = Vector3.Cross(Forward, Right); 127 | 128 | Vector3 focus = Position + Vector3.Normalize(Transform(defaultForward, rotation)); 129 | View = Matrix4x4.CreateLookAt(Position, focus, Up); 130 | } 131 | 132 | private Vector3 Transform(Vector3 v, Matrix4x4 transform) 133 | { 134 | Vector4 result = new Vector4(transform.M41, transform.M42, transform.M43, transform.M44) + 135 | (v.X * new Vector4(transform.M11, transform.M12, transform.M13, transform.M14)) + 136 | (v.Y * new Vector4(transform.M21, transform.M22, transform.M23, transform.M24)) + 137 | (v.Z * new Vector4(transform.M31, transform.M32, transform.M33, transform.M34)); 138 | 139 | result /= result.W; 140 | return new Vector3(result.X, result.Y, result.Z); 141 | } 142 | 143 | public void UpdateProjectionMatrix() 144 | { 145 | Projection = Matrix4x4.CreatePerspectiveFieldOfView(FovRadians, _aspectRatio, _nearPlane, _farPlane); 146 | Projection.M22 *= -1; 147 | } 148 | 149 | public void ViewportResize(Vector2 viewportSize) 150 | { 151 | _aspectRatio = viewportSize.X / viewportSize.Y; 152 | UpdateProjectionMatrix(); 153 | } 154 | } -------------------------------------------------------------------------------- /Nanoforge/Gui/ViewModels/Documents/MapEditorDocumentViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Avalonia; 6 | using Avalonia.Controls; 7 | using CommunityToolkit.Mvvm.ComponentModel; 8 | using CommunityToolkit.Mvvm.Input; 9 | using Dock.Model.Mvvm.Controls; 10 | using Nanoforge.Editor; 11 | using Nanoforge.Gui.ViewModels.Dialogs; 12 | using Nanoforge.Gui.Views; 13 | using Nanoforge.Gui.Views.Dialogs; 14 | using Nanoforge.Render; 15 | using Nanoforge.Rfg; 16 | using Nanoforge.Rfg.Import; 17 | using Serilog; 18 | 19 | namespace Nanoforge.Gui.ViewModels.Documents; 20 | 21 | public partial class MapEditorDocumentViewModel : NanoforgeDocument 22 | { 23 | [ObservableProperty] 24 | private Scene _scene = new(); 25 | 26 | [ObservableProperty] 27 | private bool _loading = false; 28 | 29 | [ObservableProperty] 30 | private bool _loaded = false; 31 | 32 | [ObservableProperty] 33 | private bool _loadFailed = false; 34 | 35 | [ObservableProperty] 36 | private string _loadFailureReason = string.Empty; 37 | 38 | public string Filename { get; set; } 39 | public string DisplayName { get; set; } 40 | 41 | public TimeSpan ImportTime = new TimeSpan(0, 0, 0); 42 | public TimeSpan ImportAndLoadTime = new TimeSpan(0, 0, 0); 43 | 44 | public Territory? Map; 45 | 46 | public MapEditorDocumentViewModel() 47 | { 48 | if (!Design.IsDesignMode) 49 | { 50 | throw new Exception("The MapEditorDocumentViewModel parameterless constructor should only be used by the avalonia designer"); 51 | } 52 | Filename = "DebugFilename.vpp_pc"; 53 | DisplayName = "Debug display name"; 54 | 55 | //TODO: Change to be the currently selected zone object when the map editor is implemented 56 | InspectorTarget = this; 57 | OutlinerTarget = this; 58 | } 59 | 60 | public MapEditorDocumentViewModel(string filename, string displayName) 61 | { 62 | Filename = filename; 63 | DisplayName = displayName; 64 | //TODO: Change to be the currently selected zone object when the map editor is implemented 65 | InspectorTarget = this; 66 | OutlinerTarget = this; 67 | TaskDialog dialog = new TaskDialog(); //Easier to create this here since it must be created on the avalonia thread 68 | dialog.ShowDialog(MainWindow.Instance); 69 | ThreadPool.QueueUserWorkItem(_ => LoadMap(dialog)); 70 | } 71 | 72 | private void LoadMap(TaskDialog taskDialog) 73 | { 74 | try 75 | { 76 | Loading = true; 77 | DateTime loadingStart = DateTime.Now; 78 | Renderer renderer = (Application.Current as App)!.Renderer!; 79 | 80 | Log.Information($"Opening {Filename}..."); 81 | 82 | Territory? findResult = NanoDB.Find(Filename); 83 | if (findResult is null) 84 | { 85 | //Map needs to be imported 86 | Log.Information($"First time opening {Filename} in this project. Importing..."); 87 | DateTime importStart = DateTime.Now; 88 | 89 | MapImporter importer = new(); 90 | if (importer.ImportMap(Filename, taskDialog.ViewModel) is { } map) 91 | { 92 | Map = map; 93 | } 94 | else 95 | { 96 | LoadFailed = true; 97 | LoadFailureReason = $"Failed to import {Filename}. See the log for more details"; 98 | Log.Error(LoadFailureReason); 99 | return; 100 | } 101 | 102 | ImportTime = DateTime.Now - importStart; 103 | //TODO: Uncomment once unsaved changes popup logic is added 104 | //UnsavedChanges = true; 105 | } 106 | else 107 | { 108 | Log.Information($"Found {Filename} in NanoDB. Loading from project files..."); 109 | Map = findResult; 110 | } 111 | 112 | //Import done. Now load the map from the project files 113 | if (!Map.Load(renderer, Scene)) 114 | { 115 | LoadFailed = true; 116 | LoadFailureReason = $"Failed to load map {Filename}. Check the log for more details."; 117 | Log.Error(LoadFailureReason); 118 | } 119 | 120 | //TODO: Auto center camera on only/first zone 121 | //TODO: Count object class instances 122 | //TODO: Initialize inspectors if necessary in this port. In the previous version it caused a hitch when loading xml files. May not be necessary in avalonia with async. 123 | 124 | Scene.Init(renderer.Context); 125 | Scene.Camera!.TargetPosition = new Vector3(65.97262f, 296.2423f, -592.8933f); 126 | renderer.ActiveScenes.Add(Scene); 127 | ImportAndLoadTime = DateTime.Now - loadingStart; 128 | Loaded = true; 129 | taskDialog.ViewModel!.CloseDialog(); 130 | } 131 | catch (Exception ex) 132 | { 133 | Log.Error(ex, $"Failed to load map: {Filename}"); 134 | Loading = false; 135 | Loaded = false; 136 | LoadFailed = true; 137 | LoadFailureReason = ex.Message; 138 | } 139 | } 140 | 141 | public override bool OnClose() 142 | { 143 | Renderer renderer = App.Current.Renderer!; 144 | Scene.Destroy(); 145 | renderer.ActiveScenes.Remove(Scene); 146 | return base.OnClose(); 147 | } 148 | 149 | [RelayCommand] 150 | private void Update(SceneFrameUpdateParams updateParams) 151 | { 152 | updateParams.CameraControlsEnabled = Focused; 153 | Scene.Update(updateParams); 154 | } 155 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | out/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | project.fragment.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.jfm 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | 257 | # CodeRush 258 | .cr/ 259 | 260 | # Python Tools for Visual Studio (PTVS) 261 | __pycache__/ 262 | *.pyc 263 | 264 | TODO 265 | 266 | # SPIRV bytecode files (usually compiled shaders) 267 | *.spv 268 | 269 | cmake-build-debug/ 270 | 271 | cmake-build-release/ 272 | 273 | cmake-build-relwithdebinfo/ 274 | 275 | sh.exe.stackdump 276 | 277 | Project26CppCheck-cppcheck-build-dir/ 278 | 279 | Project26CppCheck.cppcheck 280 | 281 | StructLayoutSettings.json 282 | 283 | recovery/ 284 | 285 | build/ 286 | 287 | BeefSpace_User.toml 288 | imgui.ini 289 | OLD_TODO 290 | TODO.old.cppVersion 291 | Count.bat 292 | combined.txt 293 | GourceCombined.txt 294 | GourceCombinedFiltered.txt 295 | GourceNF.txt 296 | GourceRfgTools.txt 297 | GourceRfgToolsPlusPlus.txt 298 | Config.nanodata 299 | 300 | 301 | .idea/ --------------------------------------------------------------------------------