port from perforce

This commit is contained in:
2026-04-18 22:31:51 +02:00
commit 8d0ab5b7cc
8409 changed files with 3972376 additions and 0 deletions

7
intromat/.editorconfig Normal file
View File

@@ -0,0 +1,7 @@
[*.cs]
# IDE0058: Expression value is never used
dotnet_diagnostic.IDE0058.severity = none
csharp_style_var_elsewhere = true;
csharp_style_var_for_built_in_types = true;
csharp_style_var_when_type_is_apparent = true;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,107 @@
.assembly extern mscorlib {}
.assembly extern SharpDX
{
.publickeytoken = (b4 dc f0 f3 5e 55 21 f1)
.ver 4:2:0:0
}
.assembly Intromat.DXExt
{
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module Intromat.DXExt.dll
.class public auto ansi beforefieldinit
Intromat.DXExt.GraphicsAnalysis
extends [SharpDX]SharpDX.ComObject
{
.custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string)
= (
01 00 24 39 66 32 35 31 35 31 34 2d 39 64 34 64 // ..$9f251514-9d4d
2d 34 39 30 32 2d 39 64 36 30 2d 31 38 39 38 38 // -4902-9d60-18988
61 62 37 64 34 62 35 00 00 // ab7d4b5..
)
.method public hidebysig specialname rtspecialname instance void
.ctor(
native int nativePtr
) cil managed
{
.maxstack 8
// [17 9 - 17 24]
IL_0000: ldarg.0 // this
IL_0001: ldarg.1 // nativePtr
IL_0002: call instance void [SharpDX]SharpDX.ComObject::.ctor(native int)
IL_0007: ret
} // end of method GraphicsAnalysis::.ctor
.method public hidebysig static specialname class Intromat.DXExt.GraphicsAnalysis
op_Explicit(
native int nativePtr
) cil managed
{
.maxstack 8
// [21 80 - 21 177]
IL_0000: ldarg.0 // nativePtr
IL_0001: ldsfld native int [mscorlib]System.IntPtr::Zero
IL_0006: call bool [mscorlib]System.IntPtr::op_Equality(native int, native int)
IL_000b: brtrue.s IL_0014
IL_000d: ldarg.0 // nativePtr
IL_000e: newobj instance void Intromat.DXExt.GraphicsAnalysis::.ctor(native int)
IL_0013: ret
IL_0014: ldnull
IL_0015: ret
} // end of method GraphicsAnalysis::op_Explicit
.method public hidebysig instance int32
BeginCapture() cil managed
{
.maxstack 8
// [67 37 - 67 166]
IL_0000: ldarg.0 // this
IL_0001: ldfld void* [SharpDX]SharpDX.CppObject::_nativePointer
IL_0006: ldarg.0 // this
IL_0007: ldfld void* [SharpDX]SharpDX.CppObject::_nativePointer
IL_000c: ldind.i
IL_000d: ldc.i4.3
IL_000e: conv.i
IL_000f: sizeof void*
IL_0015: mul
IL_0016: add
IL_0017: ldind.i
IL_0018: calli int32 (void*)
IL_001d: ret
} // end of method GraphicsAnalysis::BeginCapture
.method public hidebysig instance int32
EndCapture() cil managed
{
.maxstack 8
// [67 37 - 67 166]
IL_0000: ldarg.0 // this
IL_0001: ldfld void* [SharpDX]SharpDX.CppObject::_nativePointer
IL_0006: ldarg.0 // this
IL_0007: ldfld void* [SharpDX]SharpDX.CppObject::_nativePointer
IL_000c: ldind.i
IL_000d: ldc.i4.4
IL_000e: conv.i
IL_000f: sizeof void*
IL_0015: mul
IL_0016: add
IL_0017: ldind.i
IL_0018: calli int32 (void*)
IL_001d: ret
} // end of method GraphicsAnalysis::EndCapture
} // end of class Intromat.DXExt.GraphicsAnalysis

48
intromat/Intromat.sln Normal file
View File

@@ -0,0 +1,48 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33516.290
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodeNetwork", "NodeNetwork\NodeNetwork.csproj", "{96E1761E-AE02-48E9-95E8-4A3B2FC32025}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NodeNetworkToolkit", "NodeNetworkToolkit\NodeNetworkToolkit.csproj", "{391D0670-646F-4633-9E86-E9A966F61953}"
ProjectSection(ProjectDependencies) = postProject
{96E1761E-AE02-48E9-95E8-4A3B2FC32025} = {96E1761E-AE02-48E9-95E8-4A3B2FC32025}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Intromat", "Intromat\Intromat.csproj", "{274A545D-8943-44F7-AF51-772904AE10A9}"
ProjectSection(ProjectDependencies) = postProject
{96E1761E-AE02-48E9-95E8-4A3B2FC32025} = {96E1761E-AE02-48E9-95E8-4A3B2FC32025}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77ABA972-073E-4010-BC7B-9951DC38CFB6}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{96E1761E-AE02-48E9-95E8-4A3B2FC32025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96E1761E-AE02-48E9-95E8-4A3B2FC32025}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96E1761E-AE02-48E9-95E8-4A3B2FC32025}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96E1761E-AE02-48E9-95E8-4A3B2FC32025}.Release|Any CPU.Build.0 = Release|Any CPU
{391D0670-646F-4633-9E86-E9A966F61953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{391D0670-646F-4633-9E86-E9A966F61953}.Debug|Any CPU.Build.0 = Debug|Any CPU
{391D0670-646F-4633-9E86-E9A966F61953}.Release|Any CPU.ActiveCfg = Release|Any CPU
{391D0670-646F-4633-9E86-E9A966F61953}.Release|Any CPU.Build.0 = Release|Any CPU
{274A545D-8943-44F7-AF51-772904AE10A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{274A545D-8943-44F7-AF51-772904AE10A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{274A545D-8943-44F7-AF51-772904AE10A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{274A545D-8943-44F7-AF51-772904AE10A9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {923D42E4-533A-4A65-BB3E-A7CCC2585386}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,13 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_METHOD_CALLS/@EntryValue">CHOP_ALWAYS</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Anchorable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=dxgi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Grayscale/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=hwnd/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ints/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rasterizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Templated/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -0,0 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;&#xD;
&lt;Assembly Path="C:\Users\frank\.nuget\packages\sharpdx.d3dcompiler\4.2.0\lib\netstandard1.1\SharpDX.D3DCompiler.dll" /&gt;&#xD;
&lt;/AssemblyExplorer&gt;</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Akavache/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Intromat/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ungroup/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -0,0 +1,35 @@
using System;
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
namespace Intromat.Actions.Hierarchy
{
public class CreateFileAction : IUndoItem
{
private readonly FileViewModel _file;
private readonly FolderViewModel _parent;
public CreateFileAction(MainViewModel mainVm, FileViewModel file, ModuleViewModel module, FolderViewModel parent)
{
_file = file;
_parent = parent;
File = module;
}
public IFile File { get; }
public void Redo()
{
_parent.Files.Add(_file);
_parent.IsExpanded = true;
_file.IsSelected = true;
}
public void Undo()
{
_parent.Files.Remove(_file);
_parent.IsSelected = true;
}
}
}

View File

@@ -0,0 +1,34 @@
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
namespace Intromat.Actions.Hierarchy
{
public class CreateFolderAction : IUndoItem
{
private readonly FolderViewModel _folder;
private readonly FolderViewModel _parent;
public CreateFolderAction(string name, ModuleViewModel module, FolderViewModel parent)
{
_folder = new FolderViewModel(module, parent, name);
_parent = parent;
File = module;
}
public IFile File { get; }
public void Redo()
{
_parent.Folders.Add(_folder);
_parent.IsExpanded = true;
_folder.IsSelected = true;
}
public void Undo()
{
_parent.Folders.Remove(_folder);
_parent.IsSelected = true;
}
}
}

View File

@@ -0,0 +1,34 @@
using System.IO;
using Intromat.Interfaces;
using Intromat.ViewModels;
namespace Intromat.Actions.Hierarchy
{
public class CreateModuleAction : IUndoItem
{
private readonly ModuleViewModel _module;
private readonly ProjectViewModel _parent;
public CreateModuleAction(string name, ProjectViewModel parent)
{
var modulePath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(parent.FullPath)!, name, $"{name}.imodule"));
_module = new ModuleViewModel(parent, name, modulePath);
File = _parent = parent;
}
public IFile File { get; }
public void Redo()
{
_parent.Modules.Add(_module);
_parent.IsExpanded = true;
_module.IsSelected = true;
}
public void Undo()
{
_parent.Modules.Remove(_module);
_parent.IsSelected = true;
}
}
}

View File

@@ -0,0 +1,33 @@
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
namespace Intromat.Actions.Hierarchy
{
public class DeleteFileAction : IUndoItem
{
private readonly FileViewModel _file;
private readonly FolderViewModel _parent;
public DeleteFileAction(FileViewModel file)
{
_parent = file.Parent;
File = _parent.Module;
_file = file;
}
public IFile File { get; }
public void Redo()
{
_parent.Files.Remove(_file);
_parent.IsSelected = true;
}
public void Undo()
{
_parent.Files.Add(_file);
_file.IsSelected = true;
}
}
}

View File

@@ -0,0 +1,33 @@
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
namespace Intromat.Actions.Hierarchy
{
public class DeleteFolderAction : IUndoItem
{
private readonly FolderViewModel _folder;
private readonly FolderViewModel _parent;
public DeleteFolderAction(FolderViewModel folder)
{
File = folder.Module;
_parent = (FolderViewModel)folder.Parent;
_folder = folder;
}
public IFile File { get; }
public void Redo()
{
_parent.Folders.Remove(_folder);
_parent.IsSelected = true;
}
public void Undo()
{
_parent.Folders.Add(_folder);
_folder.IsSelected = true;
}
}
}

View File

@@ -0,0 +1,31 @@
using Intromat.Interfaces;
using Intromat.ViewModels;
namespace Intromat.Actions.Hierarchy
{
public class DeleteModuleAction : IUndoItem
{
private readonly ModuleViewModel _module;
private readonly ProjectViewModel _parent;
public DeleteModuleAction(ModuleViewModel module)
{
File = _parent = (ProjectViewModel)module.Parent;
_module = module;
}
public IFile File { get; }
public void Redo()
{
_parent.Modules.Remove(_module);
_parent.IsSelected = true;
}
public void Undo()
{
_parent.Modules.Add(_module);
_module.IsSelected = true;
}
}
}

View File

@@ -0,0 +1,32 @@
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
using NodeNetwork.ViewModels;
namespace Intromat.Actions.Network
{
public class AddConnectionAction : IUndoItem
{
private readonly CodeGenNetworkViewModel _network;
private readonly ConnectionViewModel _connection;
public AddConnectionAction(DocumentViewModel document, CodeGenNetworkViewModel network, ConnectionViewModel connection)
{
File = document;
_network = network;
_connection = connection;
}
public void Redo()
{
_network.Connections.Add(_connection);
}
public void Undo()
{
_network.Connections.Remove(_connection);
}
public IFile File { get; }
}
}

View File

@@ -0,0 +1,32 @@
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
using NodeNetwork.ViewModels;
namespace Intromat.Actions.Network
{
public class AddNodeAction : IUndoItem
{
private readonly CodeGenNetworkViewModel _network;
private readonly NodeViewModel _node;
public AddNodeAction(DocumentViewModel document, CodeGenNetworkViewModel network, NodeViewModel node)
{
File = document;
_network = network;
_node = node;
}
public void Redo()
{
_network.Nodes.Add(_node);
}
public void Undo()
{
_network.Nodes.Remove(_node);
}
public IFile File { get; }
}
}

View File

@@ -0,0 +1,32 @@
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
using NodeNetwork.ViewModels;
namespace Intromat.Actions.Network
{
public class DeleteConnectionAction : IUndoItem
{
private readonly CodeGenNetworkViewModel _network;
private readonly ConnectionViewModel _connection;
public DeleteConnectionAction(DocumentViewModel document, CodeGenNetworkViewModel network, ConnectionViewModel connection)
{
File = document;
_network = network;
_connection = connection;
}
public void Redo()
{
_network.Connections.Remove(_connection);
}
public void Undo()
{
_network.Connections.Add(_connection);
}
public IFile File { get; }
}
}

View File

@@ -0,0 +1,32 @@
using DynamicData;
using Intromat.Interfaces;
using Intromat.ViewModels;
using NodeNetwork.ViewModels;
namespace Intromat.Actions.Network
{
public class DeleteNodeAction : IUndoItem
{
private readonly CodeGenNetworkViewModel _network;
private readonly NodeViewModel _node;
public DeleteNodeAction(DocumentViewModel document, CodeGenNetworkViewModel network, NodeViewModel node)
{
File = document;
_network = network;
_node = node;
}
public void Redo()
{
_network.Nodes.Remove(_node);
}
public void Undo()
{
_network.Nodes.Add(_node);
}
public IFile File { get; }
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Reactive;
using Akavache;
using ReactiveUI;
namespace Intromat
{
public class AkavacheSuspensionDriver<TAppState> : ISuspensionDriver where TAppState : class
{
private const string AppStateKey = "appState";
public AkavacheSuspensionDriver()
{
BlobCache.ApplicationName = "Your Application Name";
}
public IObservable<Unit> InvalidateState()
{
return BlobCache.UserAccount.InvalidateObject<TAppState>(AppStateKey);
}
public IObservable<object> LoadState()
{
return BlobCache.UserAccount.GetObject<TAppState>(AppStateKey)!;
}
public IObservable<Unit> SaveState(object state)
{
return BlobCache.UserAccount.InsertObject(AppStateKey, (TAppState)state);
}
}
}

View File

@@ -0,0 +1,18 @@
<Application x:Class="Intromat.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/ColorfulDarkTheme.xaml" />
<ResourceDictionary Source="Themes/CodeGenNodeView.xaml" />
<ResourceDictionary Source="Themes/CodeGenNodeInputView.xaml" />
<ResourceDictionary Source="Themes/CodeGenNodeOutputView.xaml" />
<ResourceDictionary Source="Themes/Resources.xaml" />
<ResourceDictionary Source="Themes/ExtendedWpfToolkit.xaml" />
<ResourceDictionary Source="Themes/Icons.xaml" />
<ResourceDictionary Source="Themes/EditorStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,51 @@
using System.Runtime.CompilerServices;
using System.Windows;
using Intromat.Graphics;
using Intromat.ViewModels;
using Intromat.Views;
using NodeNetwork;
using NodeNetwork.Toolkit;
using ReactiveUI;
using Splat;
namespace Intromat
{
public partial class App : Application
{
public AutoSuspendHelper SuspendHelper { get; }
public App()
{
// Initialize the suspension driver after AutoSuspendHelper.
SuspendHelper = new AutoSuspendHelper(this);
RxApp.SuspensionHost.CreateNewAppState = () => new AppStateViewModel();
RxApp.SuspensionHost.SetupDefaultSuspendResume(new AkavacheSuspensionDriver<AppStateViewModel>());
}
protected override void OnStartup(StartupEventArgs e)
{
var splashScreen = new SplashScreenWindow();
MainWindow = splashScreen;
splashScreen.Show();
var dxHost = new DxHost();
Locator.CurrentMutable.RegisterConstant(dxHost);
var mainWindow = new MainWindow { Visibility = Visibility.Collapsed };
base.OnStartup(e);
foreach (var assemblyType in typeof(NNViewRegistrar).Assembly.GetTypes())
RuntimeHelpers.RunClassConstructor(assemblyType.TypeHandle);
foreach (var assemblyType in typeof(NodeTemplate).Assembly.GetTypes())
RuntimeHelpers.RunClassConstructor(assemblyType.TypeHandle);
foreach (var assemblyType in GetType().Assembly.GetTypes())
RuntimeHelpers.RunClassConstructor(assemblyType.TypeHandle);
NNViewRegistrar.RegisterSplat();
MainWindow = mainWindow;
mainWindow.Show();
splashScreen.Close();
mainWindow.Visibility = Visibility.Visible;
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.ComponentModel;
using System.Windows;
using ICSharpCode.AvalonEdit;
namespace Intromat.Controls
{
public class SourceEditor : TextEditor, INotifyPropertyChanged
{
/// <summary>
/// The bindable text property dependency property
/// </summary>
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(SourceEditor), new FrameworkPropertyMetadata
{
DefaultValue = default(string),
BindsTwoWayByDefault = true,
PropertyChangedCallback = OnDependencyPropertyChanged
});
/// <summary>
/// A bindable Text property
/// </summary>
public new string Text
{
get => (string)GetValue(TextProperty);
set
{
SetValue(TextProperty, value);
RaisePropertyChanged("Text");
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected static void OnDependencyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var target = (SourceEditor)obj;
if (target.Document == null)
return;
var caretOffset = target.CaretOffset;
var newValue = args.NewValue ?? "";
target.Document.Text = (string)newValue;
target.CaretOffset = Math.Min(caretOffset, target.Document.Text.Length);
}
protected override void OnTextChanged(EventArgs e)
{
if (Document != null)
Text = Document.Text;
base.OnTextChanged(e);
}
/// <summary>
/// Raises a property changed event
/// </summary>
/// <param name="property">The name of the property that updates</param>
public void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Intromat.Converters
{
public class WidthToMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var width = (double)value;
return Side ? new Thickness(-width, 0, 0, 0) : new Thickness(0, 0, -width, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public bool Side { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]

View File

@@ -0,0 +1,52 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using Intromat.DXExt;
using SharpDX;
namespace Intromat.Graphics
{
internal static class DebugInterface
{
[Flags]
public enum LoadLibraryFlags : uint
{
LoadLibrarySearchSystem32 = 0x00000800
}
private static readonly GetDebugInterface? getDebugInterface;
static DebugInterface()
{
var moduleHandle = LoadLibraryEx("dxgi.dll", IntPtr.Zero, LoadLibraryFlags.LoadLibrarySearchSystem32);
if (moduleHandle != IntPtr.Zero)
{
var procedureHandle = GetProcAddress(moduleHandle, "DXGIGetDebugInterface1");
if (procedureHandle != IntPtr.Zero)
getDebugInterface = (GetDebugInterface)Marshal.GetDelegateForFunctionPointer(procedureHandle, typeof(GetDebugInterface));
}
}
public static GraphicsAnalysis? TryCreateGraphicsAnalysis()
{
if (getDebugInterface == null)
return null;
var guid = typeof(GraphicsAnalysis).GetTypeInfo().GUID;
var result = getDebugInterface(0, ref guid, out var comPtr);
if (result.Failure)
return null;
return comPtr != IntPtr.Zero ? (GraphicsAnalysis)comPtr : null;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate Result GetDebugInterface(uint flags, ref Guid guid, out IntPtr result);
}
}

View File

@@ -0,0 +1,134 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using Intromat.DXExt;
using Intromat.Pipelines;
using SharpDX.Direct3D;
using SharpDX.Direct3D9;
using Splat;
using D3D11 = SharpDX.Direct3D11;
namespace Intromat.Graphics
{
public class DxHost
{
public delegate void RenderDelegate(D3D11.Device device, D3D11.DeviceContext deviceContext);
public readonly ConcurrentQueue<RenderDelegate> _actionQueue;
public readonly D3D11.Device Device;
private readonly Thread _renderThread;
private IntPtr _renderWindowHandle;
public DeviceEx? D3D9Device;
public Dispatcher? RenderDispatcher;
public Dispatcher? MainDispatcher;
private readonly ManualResetEvent _renderThreadStarted = new(false);
public readonly D3D11.UserDefinedAnnotation? _annotation;
public readonly GraphicsAnalysis? _graphicsAnalysis;
public DxHost()
{
#if DEBUG
var flags = D3D11.DeviceCreationFlags.Debug;
#else
var flags = D3D11.DeviceCreationFlags.None;
#endif
Device = new D3D11.Device(DriverType.Hardware, flags);
_annotation = Device.ImmediateContext.QueryInterface<D3D11.UserDefinedAnnotation>();
_graphicsAnalysis = DebugInterface.TryCreateGraphicsAnalysis();
_actionQueue = new();
_renderThread = new(RenderThreadStart) { Name = "WPF SwapChain" };
_renderThread.SetApartmentState(ApartmentState.STA);
_renderThread.Start();
_renderThreadStarted.WaitOne();
var device = Device;
var context = Device.ImmediateContext;
MainDispatcher = Dispatcher.CurrentDispatcher;
Locator.CurrentMutable.Register(() => new DisplayTexturePipeline(device, context));
}
public void Load(Window window)
{
_renderWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
CreateDevice();
}
public void Unload()
{
RenderDispatcher?.BeginInvokeShutdown(DispatcherPriority.Background);
RenderDispatcher = null;
}
private void RenderThreadStart(object? o)
{
RenderDispatcher = Dispatcher.CurrentDispatcher;
RenderDispatcher.BeginInvoke(new Action(UpdateFrameTick));
_renderThreadStarted.Set();
Dispatcher.Run();
}
private void UpdateFrameTick()
{
if (RenderDispatcher == null)
return;
if (_actionQueue.Count == 0)
{
RenderDispatcher.BeginInvoke(new Action(UpdateFrameTick), DispatcherPriority.Background);
return;
}
EnsureTargetDevice();
while (_actionQueue.TryDequeue(out var action))
action(Device, Device.ImmediateContext);
RenderDispatcher?.BeginInvoke(new Action(UpdateFrameTick), DispatcherPriority.Render);
}
public void EnqueueAction(RenderDelegate action)
{
_actionQueue.Enqueue(action);
}
[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr hWnd, int flags);
private void EnsureTargetDevice()
{
if (D3D9Device != null)
return;
MainDispatcher!.Invoke(CreateDevice);
}
private void CreateDevice()
{
var d3d9Ex = new Direct3DEx();
var adapter = 0;
if (d3d9Ex.AdapterCount > 1)
{
const int monitorDefaultToNearest = 2;
var hMon = MonitorFromWindow(_renderWindowHandle, monitorDefaultToNearest);
foreach (var a in d3d9Ex.AdaptersEx)
if (a.Monitor == hMon)
adapter = a.Adapter;
}
var parameters = new PresentParameters
{
PresentationInterval = PresentInterval.Immediate,
SwapEffect = SwapEffect.Discard,
Windowed = true
};
D3D9Device = new DeviceEx(d3d9Ex, adapter, DeviceType.Hardware, _renderWindowHandle, CreateFlags.HardwareVertexProcessing | CreateFlags.FpuPreserve | CreateFlags.Multithreaded, parameters);
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Intromat.Helpers
{
public static class IOHelper
{
public static string GetRelativePath(string basePath, string fullPath)
{
if (string.IsNullOrEmpty(basePath))
return fullPath ?? "";
if (string.IsNullOrEmpty(fullPath))
return "";
if (basePath.Last() != '\\')
basePath += "\\";
var builder = new StringBuilder(4096);
_ = PathRelativePathTo(builder, basePath, 0, fullPath, 0);
return builder.ToString()[2..];
}
[DllImport("shlwapi.dll", EntryPoint = "PathRelativePathTo", CharSet = CharSet.Unicode)]
private static extern bool PathRelativePathTo(StringBuilder lpszDst, string from, uint attrFrom, string to, uint attrTo);
}
}

View File

@@ -0,0 +1,20 @@
using Intromat.Nodes.Code;
using Intromat.ViewModels;
namespace Intromat.Interfaces
{
public interface IExpressionEditor
{
ERelativeSource RelativeSource
{
get;
set;
}
bool HasParentSource { get; }
bool HasInputSource { get; }
CodeGenPortViewModel CodeGenPort { get; }
}
}

View File

@@ -0,0 +1,24 @@
using Intromat.ViewModels;
namespace Intromat.Interfaces
{
public interface IFile
{
bool IsDirty
{
get;
set;
}
string FullPath
{
get;
}
string Name
{
get;
set;
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Intromat.Interfaces
{
public interface IPipeline
{
void Apply();
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DynamicData;
namespace Intromat.Interfaces
{
public interface IRefreshProperties
{
IObservableList<PropertyDescriptor>? PropertyDescriptors { get; }
}
}

View File

@@ -0,0 +1,9 @@
namespace Intromat.Interfaces
{
public interface ITreeItem
{
string Name { get; }
bool IsExpanded { get; set; }
bool IsSelected { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
namespace Intromat.Interfaces
{
public interface IUndoItem
{
void Redo();
void Undo();
IFile File { get; }
}
}

View File

@@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0-windows</TargetFrameworks>
<OutputType>WinExe</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<StartupObject>Intromat.App</StartupObject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\grid.png" />
<None Remove="Resources\HLSL-Mode.xshd" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\HLSL-Mode.xshd" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\grid.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NodeNetworkToolkit\NodeNetworkToolkit.csproj" />
<ProjectReference Include="..\NodeNetwork\NodeNetwork.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="akavache" Version="7.3.1" />
<PackageReference Include="AvalonEdit" Version="6.1.3.50" />
<PackageReference Include="Dirkster.AvalonDock.Themes.VS2013" Version="4.60.0" />
<PackageReference Include="log4net" Version="2.0.12" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="PresentationFramework.Aero2" Version="1.0.1">
<NoWarn>NU1701</NoWarn>
</PackageReference>
<PackageReference Include="RxFileSystemWatcher" Version="1.0.0" />
<PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct3D9" Version="4.2.0" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.1.0">
<NoWarn>NU1701</NoWarn>
</PackageReference>
<PackageReference Include="MoonSharp" Version="2.0.0.0" />
<PackageReference Include="morelinq" Version="3.3.2" />
<PackageReference Include="ReactiveUI" Version="13.2.18" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="Intromat.DXExt">
<HintPath>..\Intromat.DXExt\Intromat.DXExt.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup>
<Compile Update="Views\CodeGenNetworkView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\DocumentView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Editors\BooleanExpressionEditorView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Editors\BooleanValueEditorView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Editors\DimensionEditorView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Editors\EditorHeader.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Editors\EnumEditorView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Editors\FloatExpressionEditorView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Editors\FloatValueEditorView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\ExplorerView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\NewFileWindow.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Preview2DView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Preview3DView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Previews\DxMeshPreview.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Previews\DxTexturePreview.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\Previews\StringPreview.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\PropertiesView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\ShaderFileView.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\SplashScreenWindow.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Update="Themes\CodeGenNodeView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\CodeGenNodeInputView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\CodeGenNodeOutputView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\Endpoint.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\ExtendedWpfToolkit.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\Icons.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\Resources.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\CodeGenNetworkView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\DocumentView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Editors\EditorHeader.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\EditorStyles.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\ExplorerView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\NewFileWindow.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Preview2DView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Preview3DView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Previews\DxMeshPreview.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Previews\DxTexturePreview.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\Previews\StringPreview.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\PropertiesView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\ShaderFileView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Views\SplashScreenWindow.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
namespace Intromat.Model.Compiler
{
public class ScopeDefinition
{
public ScopeDefinition(string identifier)
{
Identifier = identifier;
}
public string Identifier { get; }
public List<IVariableDefinition> Variables { get; } = new();
}
public class CompilerContext
{
public Stack<ScopeDefinition> VariablesScopesStack { get; } = new();
public string FindFreeVariableName()
{
return "v" + VariablesScopesStack.SelectMany(s => s.Variables).Count();
}
public void AddVariableToCurrentScope(IVariableDefinition variable)
{
VariablesScopesStack.Peek().Variables.Add(variable);
}
public void EnterNewScope(string scopeIdentifier)
{
VariablesScopesStack.Push(new ScopeDefinition(scopeIdentifier));
}
public void LeaveScope()
{
VariablesScopesStack.Pop();
}
public bool IsInScope(IVariableDefinition variable)
{
if (variable == null) return false;
return VariablesScopesStack.Any(s => s.Variables.Contains(variable));
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Intromat.Model.Compiler.Error
{
public class CompilerException : Exception
{
public CompilerException(string msg) : base(msg)
{
}
public CompilerException(string msg, Exception inner) : base(msg, inner)
{
}
}
}

View File

@@ -0,0 +1,13 @@
namespace Intromat.Model.Compiler.Error
{
public class VariableOutOfScopeException : CompilerException
{
public VariableOutOfScopeException(string variableName)
: base($"The variable '{variableName}' was referenced outside its scope.")
{
VariableName = variableName;
}
public string VariableName { get; }
}
}

View File

@@ -0,0 +1,10 @@
using System.Text;
namespace Intromat.Model.Compiler
{
public interface IStatement
{
void Compile(CompilerContext context, StringBuilder sb);
void CompileHeader(CompilerContext context, StringBuilder sb);
}
}

View File

@@ -0,0 +1,15 @@
using System.Text;
namespace Intromat.Model.Compiler
{
public interface IExpression
{
void Compile(CompilerContext context, StringBuilder sb);
}
public interface ITypedExpression<T> : IExpression
{
string? PreviewValue { get; }
T Evaluate();
}
}

View File

@@ -0,0 +1,15 @@
using SharpDX.Direct3D11;
namespace Intromat.Model.Compiler
{
public class MeshValue
{
public MeshValue()
{
}
public MeshValue(MeshValue other)
{
}
}
}

View File

@@ -0,0 +1,24 @@
using SharpDX.Direct3D11;
namespace Intromat.Model.Compiler
{
public class TextureValue
{
public TextureValue()
{
}
public TextureValue(TextureValue other)
{
Width = other.Width;
Height = other.Height;
ShaderResourceView = other.ShaderResourceView;
UnorderedAccessView = other.UnorderedAccessView;
}
public int Width { get; set; }
public int Height { get; set; }
public ShaderResourceView? ShaderResourceView { get; set; }
public UnorderedAccessView? UnorderedAccessView { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using Intromat.Model.Compiler;
namespace Intromat.Model
{
public interface IVariableDefinition : IStatement
{
string? VariableName { get; }
}
public interface ITypedVariableDefinition<T> : IVariableDefinition
{
}
}

View File

@@ -0,0 +1,25 @@
using System.Diagnostics;
using System.Text;
using Intromat.Model.Compiler;
namespace Intromat.Model
{
public class InlineVariableDefinition<T> : ITypedVariableDefinition<T>
{
public ITypedExpression<T>? Value { get; set; }
public string? VariableName { get; private set; }
public void Compile(CompilerContext context, StringBuilder sb)
{
VariableName = context.FindFreeVariableName();
context.AddVariableToCurrentScope(this);
sb.Append($"{VariableName} = ");
Debug.Assert(Value != null, nameof(Value) + " != null");
Value.Compile(context, sb);
}
public void CompileHeader(CompilerContext context, StringBuilder sb)
{
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Diagnostics;
using System.Text;
using Intromat.Model.Compiler;
namespace Intromat.Model
{
public class LocalVariableDefinition<T> : ITypedVariableDefinition<T>
{
public string? Value { get; set; }
public string? VariableName { get; private set; }
public void Compile(CompilerContext context, StringBuilder sb)
{
VariableName = context.FindFreeVariableName();
context.AddVariableToCurrentScope(this);
Debug.Assert(Value != null, nameof(Value) + " != null");
sb.Append($"local {VariableName} = {Value}\n");
}
public void CompileHeader(CompilerContext context, StringBuilder sb)
{
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.Nodes.Code
{
[XmlRoot("BooleanLiteral", Namespace = _namespace)]
public sealed class BooleanLiteralModel : LiteralModelBase<bool>
{
public override CodeGenNodeViewModel CreateViewModel()
{
return new BooleanLiteralNode();
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using ReactiveUI;
using Splat;
namespace Intromat.Nodes.Code
{
public class BooleanLiteralNode : CodeGenNodeViewModel
{
static BooleanLiteralNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<BooleanLiteralNode>));
}
public BooleanLiteralNode() : base(NodeType.Literal)
{
Name = "Boolean";
Output = new CodeGenOutputViewModel<ITypedExpression<bool>>(EPortType.Boolean)
{
Name = "Value",
Editor = ValueEditor,
Value = ValueEditor.ValueChanged.Select(v => new BooleanLiteralValue { Value = v })
};
Outputs.Add(Output);
var stringPreview = new StringPreviewViewModel();
Preview = stringPreview;
this.WhenAnyValue(vm => vm.Output.CurrentValue)
.Where(v => v != null)
.Subscribe(_ => stringPreview.Value = Output.CurrentValue.PreviewValue ?? "False");
}
public BooleanValueEditorViewModel ValueEditor { get; } = new();
public ValueNodeOutputViewModel<ITypedExpression<bool>> Output { get; }
public override NodeModelBase CreateModel()
{
return new BooleanLiteralModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var booleanLiteral = (BooleanLiteralModel)model;
ValueEditor.Value = booleanLiteral.Value;
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var booleanLiteral = (BooleanLiteralModel)model;
booleanLiteral.Value = ValueEditor.Value;
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Intromat.Nodes.Code
{
public class BooleanLiteralValue : LiteralValueBase<bool>
{
}
}

View File

@@ -0,0 +1,14 @@
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.Nodes.Code
{
[XmlRoot("FloatLiteral", Namespace = _namespace)]
public sealed class FloatLiteralModel : LiteralModelBase<float>
{
public override CodeGenNodeViewModel CreateViewModel()
{
return new FloatLiteralNode();
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using ReactiveUI;
using Splat;
namespace Intromat.Nodes.Code
{
public class FloatLiteralNode : CodeGenNodeViewModel
{
static FloatLiteralNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<FloatLiteralNode>));
}
public FloatLiteralNode() : base(NodeType.Literal)
{
Name = "Float";
Output = new CodeGenOutputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Value",
Editor = ValueEditor,
Value = ValueEditor.ValueChanged.Select(v => new FloatLiteralValue { Value = v })
};
Outputs.Add(Output);
var stringPreview = new StringPreviewViewModel();
Preview = stringPreview;
this.WhenAnyValue(vm => vm.Output.CurrentValue)
.Where(v => v != null)
.Subscribe(_ => stringPreview.Value = Output.CurrentValue.PreviewValue ?? "0.0");
}
public FloatValueEditorViewModel ValueEditor { get; } = new();
public ValueNodeOutputViewModel<ITypedExpression<float>> Output { get; }
public override NodeModelBase CreateModel()
{
return new FloatLiteralModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var floatLiteral = (FloatLiteralModel)model;
ValueEditor.Value = floatLiteral.Value;
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var floatLiteral = (FloatLiteralModel)model;
floatLiteral.Value = ValueEditor.Value;
}
}
}

View File

@@ -0,0 +1,9 @@
using System.Globalization;
namespace Intromat.Nodes.Code
{
public class FloatLiteralValue : LiteralValueBase<float>
{
public override string? PreviewValue => Value.ToString(CultureInfo.InvariantCulture);
}
}

View File

@@ -0,0 +1,19 @@
using System.Xml.Serialization;
using Intromat.PersistentModel;
using Intromat.ViewModels;
namespace Intromat.Nodes.Code
{
[XmlRoot("ForLoop", Namespace = _namespace)]
public sealed class ForLoopModel : NodeModelBase
{
public IntLiteralModel FirstIndex { get; set; } = null!;
public IntLiteralModel LastIndex { get; set; } = null!;
public override CodeGenNodeViewModel CreateViewModel()
{
return new ForLoopNode();
}
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.Reactive;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.ViewModels;
using ReactiveUI;
using Splat;
namespace Intromat.Nodes.Code
{
public class ForLoopNode : ExecutionNodeBase
{
static ForLoopNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<ForLoopNode>));
}
public ForLoopNode() : base(NodeType.Code)
{
var boundsGroup = new EndpointGroup("Bounds");
Name = "For Loop";
LoopBodyFlow = new CodeGenInputViewModel<IStatement>(EPortType.Execution)
{
Name = "Loop Body",
Group = _executionFlowGroup
};
Inputs.Add(LoopBodyFlow);
FirstIndex = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "First Index",
Group = boundsGroup,
Editor = FirstIndexEditor
};
Inputs.Add(FirstIndex);
LastIndex = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "Last Index",
Group = boundsGroup,
Editor = LastIndexEditor
};
Inputs.Add(LastIndex);
var loopBodyChanged = LoopBodyFlow.ValueChanged.Select(_ => Unit.Default).StartWith(Unit.Default);
FlowIn.Value = loopBodyChanged
.CombineLatest(FlowOutChanged, FirstIndex.ValueChanged, LastIndex.ValueChanged, (bodyChange, endChange, firstI, lastI) => (BodyChange: bodyChange, EndChange: endChange, FirstI: firstI, LastI: lastI))
.Select(v => new ForLoopValue
{
LoopBody = LoopBodyFlow.Value,
FlowOut = FlowOut.Value,
LowerBound = v.FirstI,
UpperBound = v.LastI
});
CurrentIndex = new CodeGenOutputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "Current Index",
Value = FlowIn.Value.Select(v => new VariableReference<int> { LocalVariable = ((ForLoopValue)v).CurrentIndex })
};
Outputs.Add(CurrentIndex);
var stringPreview = new StringPreviewViewModel();
Preview = stringPreview;
FlowIn.Value.Subscribe(v =>
{
var forLoop = (ForLoopValue)v;
stringPreview.Value = $"[{forLoop.LowerBound?.PreviewValue ?? "0"}..{forLoop.UpperBound?.PreviewValue ?? "0"}]";
});
}
public ValueNodeInputViewModel<IStatement> LoopBodyFlow { get; }
public IntegerExpressionEditorViewModel FirstIndexEditor { get; } = new();
public ValueNodeInputViewModel<ITypedExpression<int>> FirstIndex { get; }
public IntegerExpressionEditorViewModel LastIndexEditor { get; } = new();
public ValueNodeInputViewModel<ITypedExpression<int>> LastIndex { get; }
public ValueNodeOutputViewModel<ITypedExpression<int>> CurrentIndex { get; }
public override NodeModelBase CreateModel()
{
return new ForLoopModel();
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var forLoop = (ForLoopModel)model;
forLoop.FirstIndex = FirstIndexEditor.CreateModel();
forLoop.LastIndex = LastIndexEditor.CreateModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var forLoop = (ForLoopModel)model;
FirstIndexEditor.LoadModel(forLoop.FirstIndex);
LastIndexEditor.LoadModel(forLoop.LastIndex);
}
}
}

View File

@@ -0,0 +1,48 @@
using System.Diagnostics;
using System.Text;
using Intromat.Model;
using Intromat.Model.Compiler;
namespace Intromat.Nodes.Code
{
public class ForLoopValue : ExecutionValueBase
{
public IStatement? LoopBody { get; set; }
public ITypedExpression<int>? LowerBound { get; set; }
public ITypedExpression<int>? UpperBound { get; set; }
public InlineVariableDefinition<int> CurrentIndex { get; } = new();
public override void Compile(CompilerContext context, StringBuilder sb)
{
Debug.Assert(UpperBound != null, nameof(UpperBound) + " != null");
context.EnterNewScope("For loop");
CurrentIndex.Value = LowerBound;
sb.Append("for (");
CurrentIndex.Compile(context, sb);
sb.Append("; ");
sb.Append(CurrentIndex.VariableName);
sb.Append(" <= ");
UpperBound.Compile(context, sb);
sb.Append("; ++");
sb.Append(CurrentIndex.VariableName);
sb.Append(")\n{\n");
LoopBody?.Compile(context, sb);
sb.Append("\n}\n");
context.LeaveScope();
base.Compile(context, sb);
}
public override void CompileHeader(CompilerContext context, StringBuilder sb)
{
LoopBody?.CompileHeader(context, sb);
base.CompileHeader(context, sb);
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Intromat.Model.Compiler;
namespace Intromat.Nodes.Code
{
public class FunctionCall : ExecutionValueBase
{
public string? FunctionName { get; set; }
public List<IExpression> Parameters { get; } = new();
public override void Compile(CompilerContext context, StringBuilder sb)
{
Debug.Assert(FunctionName != null, nameof(FunctionName) + " != null");
sb.Append($"{FunctionName}(");
for (int i = 0, n = Parameters.Count; i < n; ++i)
{
var p = Parameters[i];
p.Compile(context, sb);
if (i != n - 1)
sb.Append(", ");
}
sb.Append(")\n");
base.Compile(context, sb);
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.Nodes.Code
{
[XmlRoot("IntLiteral", Namespace = _namespace)]
public sealed class IntLiteralModel : LiteralModelBase<int>
{
public override CodeGenNodeViewModel CreateViewModel()
{
return new IntLiteralNode();
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using ReactiveUI;
using Splat;
namespace Intromat.Nodes.Code
{
public class IntLiteralNode : CodeGenNodeViewModel
{
static IntLiteralNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<IntLiteralNode>));
}
public IntLiteralNode() : base(NodeType.Literal)
{
Name = "Integer";
Output = new CodeGenOutputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "Value",
Editor = ValueEditor,
Value = ValueEditor.ValueChanged.Select(v => new IntLiteralValue { Value = v })
};
Outputs.Add(Output);
var stringPreview = new StringPreviewViewModel();
Preview = stringPreview;
this.WhenAnyValue(vm => vm.Output.CurrentValue)
.Where(v => v != null)
.Subscribe(_ => stringPreview.Value = Output.CurrentValue.PreviewValue ?? "0");
}
public IntegerValueEditorViewModel ValueEditor { get; } = new();
public ValueNodeOutputViewModel<ITypedExpression<int>> Output { get; }
public override NodeModelBase CreateModel()
{
return new IntLiteralModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var intLiteral = (IntLiteralModel)model;
ValueEditor.Value = intLiteral.Value;
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var intLiteral = (IntLiteralModel)model;
intLiteral.Value = ValueEditor.Value;
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Intromat.Nodes.Code
{
public class IntLiteralValue : LiteralValueBase<int>
{
public int EvaluateDimension()
{
var relativeDimension = RelativeSource switch
{
ERelativeSource.Parent => 1 << ParentValue,
ERelativeSource.Input => InputValue,
_ => 1
};
var result = Value switch
{
< 0 => relativeDimension >> -Value,
> 0 => relativeDimension << Value,
_ => relativeDimension
};
return Math.Max(1, Math.Min(8192, result));
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Xml.Serialization;
using Intromat.Nodes.Textures;
using Intromat.PersistentModel;
namespace Intromat.Nodes.Code
{
public enum ERelativeSource
{
Custom,
Parent,
Input
}
[XmlInclude(typeof(IntLiteralModel))]
[XmlInclude(typeof(StringLiteralModel))]
[XmlInclude(typeof(FloatLiteralModel))]
[XmlInclude(typeof(BooleanLiteralModel))]
[XmlInclude(typeof(SamplerLiteralModel))]
public abstract class LiteralModelBase : NodeModelBase
{
public ERelativeSource RelativeSource { get; set; }
}
public class LiteralModelEntry
{
public string Key { get; set; } = null!;
public LiteralModelBase Literal { get; set; } = null!;
}
public abstract class LiteralModelBase<T> : LiteralModelBase
{
public T Value { get; set; } = default!;
}
}

View File

@@ -0,0 +1,28 @@
using System.Text;
using Intromat.Model.Compiler;
namespace Intromat.Nodes.Code
{
public abstract class LiteralValueBase<T> : ITypedExpression<T>
{
public virtual string? PreviewValue => Value?.ToString();
public T ParentValue { get; set; } = default!;
public T InputValue { get; set; } = default!;
public T Value { get; set; } = default!;
public ERelativeSource RelativeSource { get; set; }
public virtual void Compile(CompilerContext context, StringBuilder sb)
{
sb.Append(Value);
}
public virtual T Evaluate()
{
return Value;
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Xml.Serialization;
using Intromat.PersistentModel;
using Intromat.ViewModels;
namespace Intromat.Nodes.Code
{
[XmlRoot("Print", Namespace = _namespace)]
public sealed class PrintModel : NodeModelBase
{
public string? Text { get; set; }
public override CodeGenNodeViewModel CreateViewModel()
{
return new PrintNode();
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using ReactiveUI;
using Splat;
namespace Intromat.Nodes.Code
{
public class PrintNode : ExecutionNodeBase
{
static PrintNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<PrintNode>));
}
public PrintNode() : base(NodeType.Code)
{
Name = "Print";
Text = new CodeGenInputViewModel<ITypedExpression<string>>(EPortType.String)
{
Name = "Text",
Editor = TextEditor
};
Inputs.Add(Text);
FlowIn.Value = FlowOutChanged
.CombineLatest(Text.ValueChanged, (flowOutChanged, textValueChanged) => (FlowOutChanged: flowOutChanged, StringExpression: textValueChanged))
.Select(v => new FunctionCall
{
FunctionName = "print",
FlowOut = FlowOut.Value,
Parameters = { v.StringExpression ?? new StringLiteralValue { Value = "" } }
});
var stringPreview = new StringPreviewViewModel();
Preview = stringPreview;
this.WhenAnyValue(vm => vm.Text.Value)
.Where(v => v != null)
.Subscribe(_ => stringPreview.Value = Text.Value.PreviewValue ?? string.Empty);
}
public StringExpressionEditorViewModel TextEditor { get; } = new();
public ValueNodeInputViewModel<ITypedExpression<string>> Text { get; }
public override NodeModelBase CreateModel()
{
return new PrintModel();
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var print = (PrintModel)model;
print.Text = TextEditor.Value is StringLiteralValue text ? text.Value : null;
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var print = (PrintModel)model;
if (print.Text != null)
TextEditor.Value = new StringLiteralValue { Value = print.Text };
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.Nodes.Code
{
[XmlRoot("StringFile", Namespace = _namespace)]
public sealed class StringFileModel : LiteralModelBase<string?>
{
public override CodeGenNodeViewModel CreateViewModel()
{
return new StringFileNode();
}
}
}

View File

@@ -0,0 +1,121 @@
using System;
using System.IO;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.Utilities;
using ReactiveUI;
using RxFileSystemWatcher;
using Splat;
namespace Intromat.Nodes.Code
{
public class StringFileNode : CodeGenNodeViewModel
{
private readonly ObservableAsPropertyHelper<ObservableFileSystemWatcher> _fileWatcher;
private readonly ObservableAsPropertyHelper<string> _fullPath;
static StringFileNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<StringFileNode>));
}
public StringFileNode()
: base(NodeType.Literal)
{
Name = "String (file)";
Path = new CodeGenInputViewModel<string?>(EPortType.String)
{
Name = "Path",
Editor = PathEditor,
};
Inputs.Add(Path);
Contents = new CodeGenOutputViewModel<ITypedExpression<string?>>(EPortType.String)
{
Name = "Contents",
};
var stringPreview = new StringPreviewViewModel();
Preview = stringPreview;
this.WhenAnyValue(vm => vm.Parent, vm => vm.Path.Value)
.Where(pair => pair.Item1 != null && pair.Item2 != null)
.Select(pair => System.IO.Path.Combine(((CodeGenNetworkViewModel)pair.Item1).Document.Parent.FullPath, pair.Item2!))
.StartWith(string.Empty)
.ToProperty(this, vm => vm.FullPath, out _fullPath);
this.WhenAnyValue(vm => vm.FullPath)
.Where(File.Exists)
.Select(path => new ObservableFileSystemWatcher(c =>
{
c.Path = System.IO.Path.GetDirectoryName(path)!;
}))
.ToProperty(this, vm => vm.FileWatcher, out _fileWatcher);
Contents.Value = this.WhenAnyValue(vm => vm.FileWatcher)
.PairWithPreviousValue()
.Select(pair =>
{
var oldWatcher = pair.OldValue;
var newWatcher = pair.NewValue;
oldWatcher?.Dispose();
if (newWatcher != null)
{
stringPreview.Value = Path.Value!;
var result =
newWatcher.Renamed.Where(e => e.FullPath == FullPath)
.Merge(newWatcher.Changed.Where(e => e.FullPath == FullPath))
.Throttle(TimeSpan.FromMilliseconds(100))
.Select(_ => new StringLiteralValue() { Value = File.ReadAllText(FullPath) })
.StartWith(new StringLiteralValue() { Value = File.ReadAllText(FullPath) });
newWatcher.Start();
return result;
}
stringPreview.Value = string.Empty;
return Observable.Empty<StringLiteralValue>();
})
.Switch();
Outputs.Add(Contents);
}
public ObservableFileSystemWatcher FileWatcher => _fileWatcher.Value;
public string FullPath => _fullPath.Value;
public StringValueEditorViewModel PathEditor { get; } = new();
public ValueNodeInputViewModel<string?> Path { get; }
public ValueNodeOutputViewModel<ITypedExpression<string?>> Contents { get; }
public override NodeModelBase CreateModel()
{
return new StringFileModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var fileModel = (StringFileModel)model;
PathEditor.Value = fileModel.Value;
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var fileModel = (StringFileModel)model;
fileModel.Value = PathEditor.Value;
}
}
}

View File

@@ -0,0 +1,14 @@
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.Nodes.Code
{
[XmlRoot("StringLiteral", Namespace = _namespace)]
public sealed class StringLiteralModel : LiteralModelBase<string?>
{
public override CodeGenNodeViewModel CreateViewModel()
{
return new StringLiteralNode();
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using ReactiveUI;
using Splat;
namespace Intromat.Nodes.Code
{
public class StringLiteralNode : CodeGenNodeViewModel
{
static StringLiteralNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<StringLiteralNode>));
}
public StringLiteralNode() : base(NodeType.Literal)
{
Name = "String";
Output = new CodeGenOutputViewModel<ITypedExpression<string?>>(EPortType.String)
{
Name = "Value",
Editor = ValueEditor,
Value = ValueEditor.ValueChanged.Select(v => new StringLiteralValue { Value = v })
};
Outputs.Add(Output);
var stringPreview = new StringPreviewViewModel();
Preview = stringPreview;
this.WhenAnyValue(vm => vm.Output.CurrentValue)
.Where(v => v != null)
.Subscribe(_ => stringPreview.Value = ((StringLiteralValue)Output.CurrentValue).Value ?? string.Empty);
}
public StringValueEditorViewModel ValueEditor { get; } = new();
public ValueNodeOutputViewModel<ITypedExpression<string?>> Output { get; }
public override NodeModelBase CreateModel()
{
return new StringLiteralModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var textLiteral = (StringLiteralModel)model;
ValueEditor.Value = textLiteral.Value;
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var textLiteral = (StringLiteralModel)model;
textLiteral.Value = ValueEditor.Value;
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Diagnostics;
using System.Text;
using Intromat.Model.Compiler;
namespace Intromat.Nodes.Code
{
public class StringLiteralValue : LiteralValueBase<string?>
{
public override string? PreviewValue => Value;
public override void Compile(CompilerContext context, StringBuilder sb)
{
sb.Append($"\"{Value}\"");
}
public override string Evaluate()
{
return Value ?? string.Empty;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Diagnostics;
using System.Text;
using Intromat.Model;
using Intromat.Model.Compiler;
using Intromat.Model.Compiler.Error;
namespace Intromat.Nodes.Code
{
public class VariableReference<T> : ITypedExpression<T>
{
public string? PreviewValue => LocalVariable?.VariableName;
public ITypedVariableDefinition<T>? LocalVariable { get; set; }
public void Compile(CompilerContext context, StringBuilder sb)
{
var localVariable = LocalVariable;
Debug.Assert(localVariable != null, nameof(localVariable) + " != null");
Debug.Assert(localVariable.VariableName != null, $"{nameof(localVariable)}.{nameof(localVariable.VariableName)} != null");
if (!context.IsInScope(localVariable))
throw new VariableOutOfScopeException(localVariable.VariableName);
sb.Append(localVariable.VariableName);
}
public T Evaluate()
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Reactive;
using System.Reactive.Linq;
using DynamicData;
using Intromat.ViewModels;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.ViewModels;
namespace Intromat.Nodes
{
public abstract class DxNodeBase : CodeGenNodeViewModel
{
protected DxNodeBase()
: base(NodeType.Literal)
{
AnyInputChanged = Inputs
.Connect()
.Filter(input => input is ValueNodeInputViewModelBase)
.WhenValueChanged(input => ((ValueNodeInputViewModelBase)input).UnitValueChanged)
.SelectMany(i => i!);
AnyInputChanged.Subscribe(_ => { });
Resizable = ResizeOrientation.HorizontalAndVertical;
}
public IObservable<Unit> AnyInputChanged { get; }
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Reactive;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.ViewModels;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.ViewModels;
namespace Intromat.Nodes
{
public abstract class ExecutionNodeBase : CodeGenNodeViewModel
{
protected readonly EndpointGroup _executionFlowGroup;
protected ExecutionNodeBase(NodeType type)
: base(type)
{
_executionFlowGroup = new EndpointGroup("Execution Flow");
FlowOut = new CodeGenInputViewModel<IStatement>(EPortType.Execution)
{
Name = "Flow",
Group = _executionFlowGroup
};
Inputs.Add(FlowOut);
FlowOutChanged = FlowOut.ValueChanged.Select(_ => Unit.Default).StartWith(Unit.Default);
FlowIn = new CodeGenOutputViewModel<IStatement>(EPortType.Execution)
{
Name = "Flow",
Group = _executionFlowGroup
};
Outputs.Add(FlowIn);
}
public ValueNodeOutputViewModel<IStatement> FlowIn { get; }
public ValueNodeInputViewModel<IStatement> FlowOut { get; }
public IObservable<Unit> FlowOutChanged { get; }
}
}

View File

@@ -0,0 +1,24 @@
using System.Text;
using Intromat.Model.Compiler;
namespace Intromat.Nodes
{
public abstract class ExecutionValueBase : IStatement
{
public IStatement? FlowOut { get; set; }
public virtual void Compile(CompilerContext context, StringBuilder sb)
{
if (FlowOut != null)
{
FlowOut.Compile(context, sb);
sb.Append("\n");
}
}
public virtual void CompileHeader(CompilerContext context, StringBuilder sb)
{
FlowOut?.CompileHeader(context, sb);
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Xml.Serialization;
using Intromat.PersistentModel;
using Intromat.ViewModels;
namespace Intromat.Nodes
{
[XmlRoot("MainEntryPoint", Namespace = _namespace)]
public sealed class MainEntryPointModel : NodeModelBase
{
public override CodeGenNodeViewModel CreateViewModel()
{
return new MainEntryPointNode();
}
}
}

View File

@@ -0,0 +1,36 @@
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using ReactiveUI;
using Splat;
namespace Intromat.Nodes
{
public class MainEntryPointNode : CodeGenNodeViewModel
{
static MainEntryPointNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<MainEntryPointNode>));
}
public MainEntryPointNode()
: base(NodeType.Special)
{
Name = "Main";
OnClickFlow = new CodeGenInputViewModel<IStatement>(EPortType.Execution) { Name = "Entry point" };
Inputs.Add(OnClickFlow);
}
public ValueNodeInputViewModel<IStatement> OnClickFlow { get; }
public override NodeModelBase CreateModel()
{
return new MainEntryPointModel();
}
}
}

View File

@@ -0,0 +1,8 @@
using Intromat.PersistentModel;
namespace Intromat.Nodes.Meshes
{
public abstract class DxMeshModelBase : NodeModelBase
{
}
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Graphics;
using Intromat.Model.Compiler;
using Intromat.Nodes.Code;
using Intromat.Nodes.Meshes;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.ViewModels;
using ReactiveUI;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Splat;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using Device = SharpDX.Direct3D11.Device;
namespace Intromat.Nodes.Meshs
{
[CategoryOrder("Mesh", 0)]
public abstract class DxMeshNodeBase : DxNodeBase
{
protected MeshValue? _output;
protected bool _resourcesCreated;
protected DxMeshNodeBase()
{
var meshGroup = new EndpointGroup("Mesh");
Outputs.Add(Output = new CodeGenOutputViewModel<MeshValue?>(EPortType.Mesh)
{
Name = "Output",
Group = meshGroup
});
var dxHost = Locator.Current.GetService<DxHost>()!;
Output.Value = this
.WhenAnyObservable(vm => vm.ParentChanged, vm => vm.AnyInputChanged, (parent, _) => parent)
.Where(parent => parent != null)
.Throttle(TimeSpan.FromMilliseconds(1))
.ObserveOn(dxHost.RenderDispatcher)
.Do(_ => { UpdateFrame(dxHost.Device, dxHost.Device.ImmediateContext); })
.Select(_ => new MeshValue(_output!));
var dxMeshPreviewViewModel = new DxMeshPreviewViewModel(this, Output.Value);
Preview = dxMeshPreviewViewModel;
}
[MemberNotNull(nameof(_output))]
protected bool EnsureBuffers(Device device)
{
return EnsureMesh(device, ref _output);
}
protected bool EnsureMesh(Device device, [NotNull] ref MeshValue? meshValue)
{
meshValue = null!;
//if (false) // TODO: changed
//{
// return true;
//}
return false;
}
protected virtual void UpdateFrame(Device device, DeviceContext deviceContext)
{
if (!_resourcesCreated)
{
_resourcesCreated = true;
CreateDeviceResources(device);
}
EnsureBuffers(device);
}
protected virtual void CreateDeviceResources(Device device)
{
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var meshModel = (DxMeshModelBase)model;
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var meshModel = (DxMeshModelBase)model;
}
public ValueNodeOutputViewModel<MeshValue?> Output { get; }
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.Nodes.Textures
{
[XmlRoot("Blend2DColor", Namespace = _namespace)]
public class Blend2DColorModel : DxTextureModelBase
{
public enum EBlendMode
{
Multiply
}
public EBlendMode BlendMode { get; set; }
public override CodeGenNodeViewModel CreateViewModel()
{
return new Blend2DColorNode();
}
}
}

View File

@@ -0,0 +1,129 @@
using System;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using ReactiveUI;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using Splat;
using Buffer = SharpDX.Direct3D11.Buffer;
namespace Intromat.Nodes.Textures
{
public class Blend2DColorNode : DxTextureFilterNodeBase
{
private Buffer? _constantBuffer;
private ComputeShader? _cs;
private const string _computeShader = @"cbuffer cb
{
int m;
}
Texture2D tSource;
Texture2D tTarget;
SamplerState ss;
RWTexture2D<float4> o;
[numthreads(16,16,1)]
void main(in uint3 i : SV_DispatchThreadID)
{
float2 dim;
o.GetDimensions(dim.x, dim.y);
float2 uv = i.xy / dim;
float4 src = tSource.SampleLevel(ss, uv, 0);
float4 dst = tTarget.SampleLevel(ss, uv, 0);
switch (m)
{
case 0: o[i.xy] = src * dst; break;
}
}";
static Blend2DColorNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<Blend2DColorNode>));
}
public Blend2DColorNode()
{
Name = "Blend";
Inputs.Add(Target = new CodeGenInputViewModel<TextureValue?>(EPortType.Texture)
{
Name = "Target",
Group = Width.Group
});
Inputs.Add(BlendMode = new CodeGenInputViewModel<int>(EPortType.None)
{
Name = "Blend mode",
Group = Width.Group,
Editor = BlendModeEditor
});
}
public override NodeModelBase CreateModel()
{
return new Blend2DColorModel();
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var blend2D = (Blend2DColorModel)model;
blend2D.BlendMode = (Blend2DColorModel.EBlendMode)BlendModeEditor.Value;
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var blend2D = (Blend2DColorModel)model;
BlendModeEditor.Value = (int)blend2D.BlendMode;
}
protected override void CreateDeviceResources(Device device)
{
base.CreateDeviceResources(device);
var bufferDesc = new BufferDescription(16, ResourceUsage.Dynamic, BindFlags.ConstantBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
_constantBuffer = new Buffer(device, bufferDesc);
var flags = ShaderFlags.None;
#if DEBUG
flags |= ShaderFlags.Debug;
#endif
_cs = new ComputeShader(device, ShaderBytecode.Compile(_computeShader, "main", "cs_5_0", flags).Bytecode.Data);
}
protected override unsafe void UpdateFrame(Device device, DeviceContext context)
{
base.UpdateFrame(device, context);
var srvSource = Input.Value?.ShaderResourceView;
var srvTarget = Target.Value?.ShaderResourceView;
if (srvSource == null || srvTarget == null)
return;
context.MapSubresource(_constantBuffer, 0, MapMode.WriteDiscard, MapFlags.None, out var stream);
var ints = new Span<int>((void*)stream.DataPointer, 4);
ints[0] = BlendMode.Value;
context.UnmapSubresource(_constantBuffer, 0);
context.ComputeShader.SetConstantBuffer(0, _constantBuffer);
context.ComputeShader.SetShaderResource(0, srvSource);
context.ComputeShader.SetShaderResource(1, srvTarget);
context.ComputeShader.SetSampler(0, _samplerState);
context.ComputeShader.SetShader(_cs, null, 0);
context.ComputeShader.SetUnorderedAccessView(0, _output!.UnorderedAccessView);
context.Dispatch(_texDesc.Width / 16, _texDesc.Height / 16, 1);
context.ComputeShader.SetShaderResource(0, null);
context.ComputeShader.SetShaderResource(1, null);
context.ComputeShader.SetUnorderedAccessView(0, null);
}
public ValueNodeInputViewModel<TextureValue?> Target { get; }
public EnumEditorViewModel BlendModeEditor { get; } = new(typeof(Blend2DColorModel.EBlendMode));
public ValueNodeInputViewModel<int> BlendMode { get; }
}
}

View File

@@ -0,0 +1,39 @@
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.ViewModels;
using NodeNetwork.Toolkit.ValueNode;
using SharpDX.Direct3D11;
namespace Intromat.Nodes.Textures
{
public abstract class DxTextureFilterNodeBase : DxTextureNodeBase
{
protected SamplerState? _samplerState;
protected DxTextureFilterNodeBase()
{
Inputs.Add(Input = new CodeGenInputViewModel<TextureValue?>(EPortType.Texture)
{
Name = "Input",
Group = Width.Group
});
var inputObservable = Input.ValueChanged;
WidthEditor.InputValueProvider = inputObservable.Select(input => input?.Width ?? 0);
HeightEditor.InputValueProvider = inputObservable.Select(input => input?.Height ?? 0);
}
[MemberNotNull(nameof(_samplerState))]
protected override void CreateDeviceResources(Device device)
{
base.CreateDeviceResources(device);
var samplerDesc = SamplerStateDescription.Default();
samplerDesc.Filter = Filter.MinMagMipLinear;
_samplerState = new SamplerState(device, samplerDesc);
}
public ValueNodeInputViewModel<TextureValue?> Input { get; }
}
}

View File

@@ -0,0 +1,12 @@
using Intromat.Nodes.Code;
using Intromat.PersistentModel;
namespace Intromat.Nodes.Textures
{
public abstract class DxTextureModelBase : NodeModelBase
{
public IntLiteralModel Width { get; set; } = null!;
public IntLiteralModel Height { get; set; } = null!;
}
}

View File

@@ -0,0 +1,157 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Graphics;
using Intromat.Model.Compiler;
using Intromat.Nodes.Code;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.ViewModels.Previews;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.ViewModels;
using ReactiveUI;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Splat;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using Device = SharpDX.Direct3D11.Device;
namespace Intromat.Nodes.Textures
{
[CategoryOrder("Texture", 0)]
public abstract class DxTextureNodeBase : DxNodeBase
{
protected TextureValue? _output;
protected Texture2DDescription _texDesc;
protected bool _resourcesCreated;
protected DxTextureNodeBase()
{
var textureGroup = new EndpointGroup("Texture");
var parentObservable = this.WhenAnyObservable(vm => vm.ParentChanged).Select(parent => (CodeGenNetworkViewModel?)parent);
Inputs.Add(Width = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.None)
{
Name = "Width",
Group = textureGroup,
Editor = WidthEditor = new()
{
ParentValueProvider = parentObservable.Select(parent => parent?.DefaultWidth ?? -1),
MinValue = -16,
MaxValue = 16,
CustomValue = 0,
RelativeSource = ERelativeSource.Parent
}
});
Inputs.Add(Height = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.None)
{
Name = "Height",
Group = textureGroup,
Editor = HeightEditor = new()
{
ParentValueProvider = parentObservable.Select(parent => parent?.DefaultHeight ?? -1),
MinValue = -16,
MaxValue = 16,
CustomValue = 0,
RelativeSource = ERelativeSource.Parent
}
});
Outputs.Add(Output = new CodeGenOutputViewModel<TextureValue?>(EPortType.Texture)
{
Name = "Output",
Group = textureGroup
});
var dxHost = Locator.Current.GetService<DxHost>()!;
Output.Value = this
.WhenAnyObservable(vm => vm.ParentChanged, vm => vm.AnyInputChanged, (parent, _) => parent)
.Where(parent => parent != null)
.Throttle(TimeSpan.FromMilliseconds(1))
.ObserveOn(dxHost.RenderDispatcher)
.Do(_ => { UpdateFrame(dxHost.Device, dxHost.Device.ImmediateContext); })
.Select(_ => new TextureValue(_output!));
var dxTexturePreviewViewModel = new DxTexturePreviewViewModel(this, Output.Value);
Preview = dxTexturePreviewViewModel;
_texDesc = new Texture2DDescription
{
ArraySize = 1,
Format = Format.R16G16B16A16_Float,
BindFlags = BindFlags.ShaderResource | BindFlags.UnorderedAccess,
Usage = ResourceUsage.Default,
SampleDescription = new(1, 0)
};
}
[MemberNotNull(nameof(_output))]
protected bool EnsureBuffers(Device device)
{
return EnsureTexture(device, ref _output);
}
protected bool EnsureTexture(Device device, [NotNull] ref TextureValue? textureValue)
{
var width = ((IntLiteralValue)Width.Value).EvaluateDimension();
var height = ((IntLiteralValue)Height.Value).EvaluateDimension();
if (_texDesc.Width != width || _texDesc.Height != height || textureValue == null)
{
textureValue?.ShaderResourceView?.Resource?.Dispose();
textureValue?.ShaderResourceView?.Dispose();
textureValue?.UnorderedAccessView?.Dispose();
_texDesc.Width = width;
_texDesc.Height = height;
var texture = new Texture2D(device, _texDesc);
textureValue ??= new TextureValue();
textureValue.Width = width;
textureValue.Height = height;
textureValue.ShaderResourceView = new ShaderResourceView(device, texture);
textureValue.UnorderedAccessView = new UnorderedAccessView(device, texture);
return true;
}
return false;
}
protected virtual void UpdateFrame(Device device, DeviceContext deviceContext)
{
if (!_resourcesCreated)
{
_resourcesCreated = true;
CreateDeviceResources(device);
}
EnsureBuffers(device);
}
protected virtual void CreateDeviceResources(Device device)
{
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var textureModel = (DxTextureModelBase)model;
textureModel.Width = WidthEditor.CreateModel();
textureModel.Height = HeightEditor.CreateModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var textureModel = (DxTextureModelBase)model;
WidthEditor.LoadModel(textureModel.Width);
HeightEditor.LoadModel(textureModel.Height);
}
public DimensionEditorViewModel WidthEditor { get; }
public ValueNodeInputViewModel<ITypedExpression<int>> Width { get; }
public DimensionEditorViewModel HeightEditor { get; }
public ValueNodeInputViewModel<ITypedExpression<int>> Height { get; }
public ValueNodeOutputViewModel<TextureValue?> Output { get; }
}
}

View File

@@ -0,0 +1,23 @@
using ReactiveUI;
using SharpDX.Direct3D11;
namespace Intromat.Nodes.Textures
{
public class SamplerDesc : ReactiveObject
{
private Filter _filter = SamplerStateDescription.Default().Filter;
private TextureAddressMode _address = SamplerStateDescription.Default().AddressU;
public Filter Filter
{
get => _filter;
set => this.RaiseAndSetIfChanged(ref _filter, value);
}
public TextureAddressMode Address
{
get => _address;
set => this.RaiseAndSetIfChanged(ref _address, value);
}
}
}

View File

@@ -0,0 +1,16 @@
using System.Xml.Serialization;
using Intromat.Nodes.Code;
using Intromat.ViewModels;
using SharpDX.Direct3D11;
namespace Intromat.Nodes.Textures
{
[XmlRoot("SamplerLiteral", Namespace = _namespace)]
public sealed class SamplerLiteralModel : LiteralModelBase<SamplerDesc>
{
public override CodeGenNodeViewModel? CreateViewModel()
{
return null;
}
}
}

View File

@@ -0,0 +1,10 @@
using Intromat.Nodes.Code;
using SharpDX.Direct3D11;
namespace Intromat.Nodes.Textures
{
public class SamplerLiteralValue : LiteralValueBase<SamplerDesc>
{
public SamplerState? SamplerState { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using System.Xml.Serialization;
using Intromat.Nodes.Code;
using Intromat.ViewModels;
namespace Intromat.Nodes.Textures
{
[XmlRoot("ShaderTexture", Namespace = _namespace)]
public sealed class ShaderTextureModel : DxTextureModelBase
{
public StringLiteralModel Source { get; set; } = null!;
public LiteralModelEntry[] InputValues { get; set; } = null!;
public override CodeGenNodeViewModel CreateViewModel()
{
return new ShaderTextureNode();
}
}
}

View File

@@ -0,0 +1,477 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using DynamicData;
using Intromat.Graphics;
using Intromat.Model.Compiler;
using Intromat.Nodes.Code;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.Views;
using NodeNetwork.ViewModels;
using ReactiveUI;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using Splat;
using Buffer = SharpDX.Direct3D11.Buffer;
namespace Intromat.Nodes.Textures
{
public class ShaderTextureNode : DxTextureNodeBase
{
private readonly List<Buffer> _constantBuffers = new();
private ComputeShader? _cs;
private byte[]? _byteCode;
private TextureValue[]? _customOutputs;
private readonly List<(NodeInputViewModel, InputDesc)> _customInputs = new();
private readonly EndpointGroup _group;
private readonly Dictionary<string, object> _inputValues = new();
public record InputDesc(int ConstantBufferIndex, int Offset, Type VariableType, string Name);
static ShaderTextureNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<ShaderTextureNode>));
}
public ShaderTextureNode()
{
Name = "Shader Texture";
_group = new EndpointGroup("Shader Texture");
Inputs.Add(Source = new CodeGenInputViewModel<ITypedExpression<string>>(EPortType.String)
{
Name = "Source",
Group = _group,
Editor = SourceEditor
});
var dxHost = Locator.Current.GetService<DxHost>()!;
Source.ValueChanged
.ObserveOn(dxHost.RenderDispatcher)
.Subscribe(_ =>
{
_resourcesCreated = false;
UpdateFrame(dxHost.Device, dxHost.Device.ImmediateContext);
});
}
public override NodeModelBase CreateModel()
{
return new ShaderTextureModel();
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var shaderTexture = (ShaderTextureModel)model;
shaderTexture.Source = SourceEditor.CreateModel();
shaderTexture.InputValues = _customInputs
.Where(i => i.Item1.Editor != null)
.Select(i => new LiteralModelEntry()
{
Key = i.Item1.Name,
Literal = CreateLiteralModel((dynamic)i.Item1.Editor)
})
.ToArray();
}
private TModel CreateLiteralModel<T, TValue, TModel>(ExpressionEditorBaseViewModel<T, TValue, TModel> editor) where TValue : LiteralValueBase<T>, new() where TModel : LiteralModelBase<T>, new()
{
return editor.CreateModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var shaderTexture = (ShaderTextureModel)model;
SourceEditor.LoadModel(shaderTexture.Source);
foreach (var inputValue in shaderTexture.InputValues)
{
_inputValues[inputValue.Key] = inputValue.Literal;
}
var dxHost = Locator.Current.GetService<DxHost>()!;
_resourcesCreated = true;
CreateDeviceResources(dxHost.Device);
}
protected override void CreateDeviceResources(Device device)
{
foreach (var cb in _constantBuffers)
cb.Dispose();
_constantBuffers.Clear();
var flags = ShaderFlags.None;
#if DEBUG
flags |= ShaderFlags.Debug;
#endif
var errors = new List<string>();
var shader = Source.Value.Evaluate();
if (string.IsNullOrEmpty(shader))
{
errors.Add("Shader is empty");
}
else
{
try
{
var compilationResult = ShaderBytecode.Compile(shader, "main", "cs_5_0", flags);
if (compilationResult.HasErrors)
{
errors.Add(compilationResult.Message);
}
else
{
_byteCode = compilationResult.Bytecode.Data;
var reflection = new ShaderReflection(_byteCode);
var newInputs = new List<InputDesc>();
var newOutputs = new List<InputDesc>();
for (int i = 0; i < reflection.Description.BoundResources; ++i)
{
var resBindingDesc = reflection.GetResourceBindingDescription(i);
switch (resBindingDesc.Type)
{
case ShaderInputType.ConstantBuffer:
{
var cb = reflection.GetConstantBuffer(resBindingDesc.Name);
var cbIndex = resBindingDesc.BindPoint;
var cbSize = cb.Description.Size;
var bufferDesc = new BufferDescription(cbSize, ResourceUsage.Dynamic, BindFlags.ConstantBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
_constantBuffers.Add(new Buffer(device, bufferDesc));
for (int j = 0; j < cb.Description.VariableCount; ++j)
{
var variable = cb.GetVariable(j);
var varTypeDesc = variable.GetVariableType()
.Description;
var type = varTypeDesc.Type switch
{
ShaderVariableType.Float => typeof(float),
ShaderVariableType.Int => typeof(int),
ShaderVariableType.Bool => typeof(bool),
_ => null
};
if (type == null)
{
errors.Add($"Constant buffer {resBindingDesc.Name} at slot {cbIndex} has a variable {variable.Description.Name} with unsupported type {varTypeDesc.Type}");
continue;
}
for (int k = 0; k < varTypeDesc.ColumnCount; ++k)
{
var suffix = "" + 'X' + k;
var inputName = varTypeDesc.ColumnCount == 1 ? variable.Description.Name : $"{variable.Description.Name}.{suffix}";
newInputs.Add(new(cbIndex, variable.Description.StartOffset + k * variable.Description.Size, type, inputName));
}
}
break;
}
case ShaderInputType.Texture:
{
var texName = resBindingDesc.Name;
newInputs.Add(new(-1, resBindingDesc.BindPoint, typeof(TextureValue), texName));
break;
}
case ShaderInputType.Sampler:
{
var texName = resBindingDesc.Name;
newInputs.Add(new(-1, resBindingDesc.BindPoint, typeof(SamplerLiteralValue), texName));
break;
}
case ShaderInputType.UnorderedAccessViewRWTyped:
{
var texName = resBindingDesc.Name;
if (resBindingDesc.Dimension != ShaderResourceViewDimension.Texture2D)
{
errors.Add($"UAV {texName} at slot {resBindingDesc.BindPoint} has unsupported dimension {resBindingDesc.Dimension}");
break;
}
newOutputs.Add(new(-1, resBindingDesc.BindPoint, typeof(TextureValue), texName));
break;
}
default:
{
errors.Add($"Resource {resBindingDesc.Name} at slot {resBindingDesc.BindPoint} has unsupported type {resBindingDesc.Type}");
break;
}
}
}
if (newOutputs.Count == 0)
{
errors.Add("The shader doesn't contain any UAV suitable for the texture output");
}
Inputs.Edit(inputs =>
{
var maxSamplerStateIndex = 0;
for (var i = _customInputs.Count - 1; i >= 0; --i)
{
var existingInput = _customInputs[i];
if (newInputs.All(inputDesc => inputDesc.Name != existingInput.Item1.Name))
{
inputs.Remove(existingInput.Item1);
_customInputs.RemoveAt(i);
}
}
foreach (var newInputDesc in newInputs)
{
if (_customInputs.Any(i => i.Item2 == newInputDesc))
continue;
NodeInputViewModel newInput;
if (newInputDesc.VariableType == typeof(float))
{
newInput = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = newInputDesc.Name,
Group = _group,
Editor = new FloatExpressionEditorViewModel { MaxValue = 1 }
};
if (_inputValues.TryGetValue(newInputDesc.Name, out var inputValue) && inputValue is FloatLiteralModel model)
((FloatExpressionEditorViewModel)newInput.Editor).LoadModel(model);
}
else if (newInputDesc.VariableType == typeof(int))
{
newInput = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = newInputDesc.Name,
Group = _group,
Editor = new IntegerExpressionEditorViewModel { MaxValue = 100 }
};
if (_inputValues.TryGetValue(newInputDesc.Name, out var inputValue) && inputValue is IntLiteralModel model)
((IntegerExpressionEditorViewModel)newInput.Editor).LoadModel(model);
}
else if (newInputDesc.VariableType == typeof(bool))
{
newInput = new CodeGenInputViewModel<ITypedExpression<bool>>(EPortType.Boolean)
{
Name = newInputDesc.Name,
Group = _group,
Editor = new BooleanExpressionEditorViewModel()
};
if (_inputValues.TryGetValue(newInputDesc.Name, out var inputValue) && inputValue is BooleanLiteralModel model)
((BooleanExpressionEditorViewModel)newInput.Editor).LoadModel(model);
}
else if (newInputDesc.VariableType == typeof(TextureValue))
{
newInput = new CodeGenInputViewModel<TextureValue>(EPortType.Texture) { Name = newInputDesc.Name };
if (newInputDesc.Offset == 0)
{
var inputObservable = ((CodeGenInputViewModel<TextureValue>)newInput).ValueChanged;
WidthEditor.InputValueProvider = inputObservable.Select(input => input?.Width ?? 0);
HeightEditor.InputValueProvider = inputObservable.Select(input => input?.Height ?? 0);
}
}
else if (newInputDesc.VariableType == typeof(SamplerLiteralValue))
{
maxSamplerStateIndex = Math.Max(maxSamplerStateIndex, newInputDesc.Offset);
newInput = new CodeGenInputViewModel<ITypedExpression<SamplerDesc>>(EPortType.None)
{
Name = newInputDesc.Name,
Group = _group,
Editor = new SamplerEditorViewModel { CustomValue = new SamplerDesc() }
};
if (_inputValues.TryGetValue(newInputDesc.Name, out var inputValue) && inputValue is SamplerLiteralModel model)
((SamplerEditorViewModel)newInput.Editor).LoadModel(model);
}
else
{
continue;
}
_customInputs.Add((newInput, newInputDesc));
inputs.Add(newInput);
}
});
Outputs.Edit(outputs =>
{
if (outputs.Count > newOutputs.Count)
{
if (newOutputs.Count == 0)
{
if (outputs.Count > 1)
outputs.RemoveRange(1, outputs.Count - 1);
outputs[0]
.Name = "Output";
}
else
{
outputs.RemoveRange(newOutputs.Count, outputs.Count - newOutputs.Count);
}
}
var outputIndex = 0;
if (outputIndex < newOutputs.Count)
for (; outputIndex < outputs.Count; ++outputIndex)
outputs[outputIndex]
.Name = newOutputs[outputIndex]
.Name;
for (; outputIndex < newOutputs.Count; ++outputIndex)
{
var newOutputDesc = newOutputs[outputIndex];
NodeOutputViewModel newOutput;
if (newOutputDesc.VariableType == typeof(TextureValue))
newOutput = new CodeGenOutputViewModel<TextureValue>(EPortType.Texture) { Name = newOutputDesc.Name };
else
continue;
outputs.Add(newOutput);
}
_customOutputs = new TextureValue[outputs.Count];
});
}
}
catch (CompilationException e)
{
errors.Add(e.Message);
}
}
_cs?.Dispose();
_cs = errors.Count == 0 ? new ComputeShader(device, _byteCode) : null;
Errors.Edit(e =>
{
e.Clear();
e.AddRange(errors);
});
}
protected override unsafe void UpdateFrame(Device device, DeviceContext context)
{
if (!_resourcesCreated)
{
_resourcesCreated = true;
var dxHost = Locator.Current.GetService<DxHost>()!;
dxHost.MainDispatcher!.Invoke(() => CreateDeviceResources(device));
}
EnsureBuffers(device);
if (_customOutputs == null || _cs == null)
return;
_customOutputs[0] = _output;
for (int i = 1; i < Outputs.Count; ++i)
{
EnsureTexture(device, ref _customOutputs![i]);
}
var cbData = _constantBuffers.Select(cb =>
{
context.MapSubresource(cb, 0, MapMode.WriteDiscard, MapFlags.None, out var stream);
return stream.DataPointer;
}).ToArray();
int maxSrvIndex = 0;
foreach (var input in _customInputs)
{
var desc = input.Item2;
if (desc.ConstantBufferIndex >= 0)
{
var dataPtr = cbData[desc.ConstantBufferIndex] + desc.Offset;
if (desc.VariableType == typeof(float))
{
_inputValues[input.Item1.Name] = ((FloatExpressionEditorViewModel)input.Item1.Editor).CreateModel();
*(float*)dataPtr = ((CodeGenInputViewModel<ITypedExpression<float>>)input.Item1).Value.Evaluate();
}
else if (desc.VariableType == typeof(int))
{
_inputValues[input.Item1.Name] = ((IntegerExpressionEditorViewModel)input.Item1.Editor).CreateModel();
*(int*)dataPtr = ((CodeGenInputViewModel<ITypedExpression<int>>)input.Item1).Value.Evaluate();
}
else if (desc.VariableType == typeof(bool))
{
_inputValues[input.Item1.Name] = ((BooleanExpressionEditorViewModel)input.Item1.Editor).CreateModel();
*(bool*)dataPtr = ((CodeGenInputViewModel<ITypedExpression<bool>>)input.Item1).Value.Evaluate();
}
}
else
{
if (desc.VariableType == typeof(TextureValue))
{
maxSrvIndex = Math.Max(maxSrvIndex, desc.Offset);
context.ComputeShader.SetShaderResource(desc.Offset, ((CodeGenInputViewModel<TextureValue>)input.Item1).Value?.ShaderResourceView);
}
else if (desc.VariableType == typeof(SamplerLiteralValue))
{
_inputValues[input.Item1.Name] = ((SamplerEditorViewModel)input.Item1.Editor).CreateModel();
var samplerValue = (SamplerLiteralValue)((CodeGenInputViewModel<ITypedExpression<SamplerDesc>>)input.Item1).Value;
EnsureSamplerState(device, ref samplerValue);
context.ComputeShader.SetSampler(desc.Offset, samplerValue.SamplerState);
}
}
}
for (var i = 0; i < _customOutputs!.Length; i++)
{
var output = _customOutputs![i];
context.ComputeShader.SetUnorderedAccessView(i, output.UnorderedAccessView);
}
for (int i = 0; i < _constantBuffers.Count; ++i)
{
context.UnmapSubresource(_constantBuffers[i], 0);
context.ComputeShader.SetConstantBuffer(i, _constantBuffers[i]);
}
context.ComputeShader.SetShader(_cs, null, 0);
context.Dispatch(_texDesc.Width / 16, _texDesc.Height / 16, 1);
for (var i = 0; i < _customOutputs!.Length; i++)
{
context.ComputeShader.SetUnorderedAccessView(i, null);
}
for (var i = 0; i <= maxSrvIndex; i++)
{
context.ComputeShader.SetShaderResource(i, null);
}
}
private void EnsureSamplerState(Device device, ref SamplerLiteralValue samplerLiteralValue)
{
if (samplerLiteralValue.SamplerState == null || samplerLiteralValue.SamplerState.Description.AddressU != samplerLiteralValue.Value.Address || samplerLiteralValue.SamplerState.Description.Filter != samplerLiteralValue.Value.Filter)
{
var desc = SamplerStateDescription.Default();
desc.AddressU = desc.AddressV = desc.AddressW = samplerLiteralValue.Value.Address;
desc.Filter = samplerLiteralValue.Value.Filter;
samplerLiteralValue.SamplerState = new SamplerState(device, desc);
}
}
public ISourceList<string> Errors { get; } = new SourceList<string>();
public CodeGenInputViewModel<ITypedExpression<string>> Source { get; }
public StringExpressionEditorViewModel SourceEditor { get; } = new()
{
CustomValue = @"
cbuffer cb
{
float f;
}
Texture2D t0;
SamplerState ss;
RWTexture2D<float4> o;
[numthreads(16,16,1)]
void main(uint3 i : SV_DispatchThreadID)
{
float2 s;
o.GetDimensions(s.x, s.y);
float2 uv = i.xy/s;
float4 c = t0.SampleLevel(ss, uv, 0);
o[i.xy] = float4(c.xyz, 1);
}"
};
}
}

View File

@@ -0,0 +1,34 @@
using System.Xml.Serialization;
using Intromat.Nodes.Code;
using Intromat.ViewModels;
namespace Intromat.Nodes.Textures
{
[XmlRoot("Shape2DGrayscale", Namespace = _namespace)]
public class Shape2DGrayscaleModel : DxTextureModelBase
{
public enum EPattern
{
Square
}
public IntLiteralModel Tiling { get; set; } = default!;
public EPattern Pattern { get; set; }
public FloatLiteralModel Scale { get; set; } = default!;
public FloatLiteralModel SizeX { get; set; } = default!;
public FloatLiteralModel SizeY { get; set; } = default!;
public FloatLiteralModel Angle { get; set; } = default!;
public BooleanLiteralModel Rotate45 { get; set; } = default!;
public override CodeGenNodeViewModel CreateViewModel()
{
return new Shape2DGrayscaleNode();
}
}
}

View File

@@ -0,0 +1,213 @@
using System;
using System.Diagnostics.CodeAnalysis;
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.ViewModels;
using ReactiveUI;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using Splat;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using Buffer = SharpDX.Direct3D11.Buffer;
namespace Intromat.Nodes.Textures
{
[CategoryOrder("Shape 2D", 1)]
public class Shape2DGrayscaleNode : DxTextureNodeBase
{
private Buffer? _constantBuffer;
private ComputeShader? _cs;
private const string _computeShader = @"cbuffer cb
{
int t;
int p;
float s;
float sx;
float sy;
float a;
int r;
}
float Square(float2 uv)
{
return step(uv.x, sx) * step(-sx, uv.x) * step(uv.y, sy) * step(-sy, uv.y);
}
RWTexture2D<float> o;
[numthreads(16,16,1)]
void main(in uint3 i : SV_DispatchThreadID)
{
float sa, ca;
sincos(a, sa, ca);
float2 dim;
o.GetDimensions(dim.x, dim.y);
float2 uv = i.xy / dim;
if (r > 0)
{
float f = sqrt(2)/2;
uv = uv * 2 - 1;
uv = mul(uv, float2x2(f, -f, f, f));
uv = uv * .5 + .5;
}
uv *= t;
uv = frac(uv);
uv = uv * 2 - 1;
uv /= s;
uv = mul(uv, float2x2(ca, -sa, sa, ca));
float z = 0;
switch (p)
{
case 0: z = Square(uv); break;
}
o[i.xy] = z;
}";
static Shape2DGrayscaleNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<Shape2DGrayscaleNode>));
}
public Shape2DGrayscaleNode()
{
Name = "Shape 2D";
var group = new EndpointGroup("Shape 2D");
Inputs.Add(Tiling = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "Tiling",
Group = group,
Editor = TilingEditor
});
Inputs.Add(Pattern = new CodeGenInputViewModel<int>(EPortType.None)
{
Name = "Pattern",
Group = group,
Editor = PatternEditor
});
Inputs.Add(Scale = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Scale",
Group = group,
Editor = ScaleEditor
});
Inputs.Add(SizeX = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Size X",
Group = group,
Editor = SizeXEditor
});
Inputs.Add(SizeY = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Size Y",
Group = group,
Editor = SizeYEditor
});
Inputs.Add(Angle = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Angle",
Group = group,
Editor = AngleEditor
});
Inputs.Add(Rotate45 = new CodeGenInputViewModel<ITypedExpression<bool>>(EPortType.Float)
{
Name = "Rotate 45°",
Group = group,
Editor = Rotate45Editor
});
}
public override NodeModelBase CreateModel()
{
return new Shape2DGrayscaleModel();
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var shape2D = (Shape2DGrayscaleModel)model;
shape2D.Tiling = TilingEditor.CreateModel();
shape2D.Pattern = (Shape2DGrayscaleModel.EPattern)PatternEditor.Value;
shape2D.Scale = ScaleEditor.CreateModel();
shape2D.SizeX = SizeXEditor.CreateModel();
shape2D.SizeY = SizeYEditor.CreateModel();
shape2D.Angle = AngleEditor.CreateModel();
shape2D.Rotate45 = Rotate45Editor.CreateModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var shape2D = (Shape2DGrayscaleModel)model;
TilingEditor.LoadModel(shape2D.Tiling);
PatternEditor.Value = (int)shape2D.Pattern;
ScaleEditor.LoadModel(shape2D.Scale);
SizeXEditor.LoadModel(shape2D.SizeX);
SizeYEditor.LoadModel(shape2D.SizeY);
AngleEditor.LoadModel(shape2D.Angle);
Rotate45Editor.LoadModel(shape2D.Rotate45);
}
protected override unsafe void UpdateFrame(Device device, DeviceContext context)
{
base.UpdateFrame(device, context);
context.MapSubresource(_constantBuffer, 0, MapMode.WriteDiscard, MapFlags.None, out var stream);
var floats = new Span<float>((void*)stream.DataPointer, 8);
var ints = new Span<int>((void*)stream.DataPointer, 8);
ints[0] = Tiling.Value.Evaluate();
ints[1] = Pattern.Value;
floats[2] = Scale.Value.Evaluate();
floats[3] = SizeX.Value.Evaluate();
floats[4] = SizeY.Value.Evaluate();
floats[5] = Angle.Value.Evaluate();
ints[6] = Rotate45.Value.Evaluate() ? 1 : 0;
context.UnmapSubresource(_constantBuffer, 0);
context.ComputeShader.SetConstantBuffer(0, _constantBuffer);
context.ComputeShader.SetShader(_cs, null, 0);
context.ComputeShader.SetUnorderedAccessView(0, _output!.UnorderedAccessView);
context.Dispatch(_texDesc.Width / 16, _texDesc.Height / 16, 1);
context.ComputeShader.SetUnorderedAccessView(0, null);
}
[MemberNotNull(nameof(_constantBuffer))]
[MemberNotNull(nameof(_cs))]
protected override void CreateDeviceResources(Device device)
{
base.CreateDeviceResources(device);
var bufferDesc = new BufferDescription(32, ResourceUsage.Dynamic, BindFlags.ConstantBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
_constantBuffer = new Buffer(device, bufferDesc);
var flags = ShaderFlags.None;
#if DEBUG
flags |= ShaderFlags.Debug;
#endif
_cs = new ComputeShader(device, ShaderBytecode.Compile(_computeShader, "main", "cs_5_0", flags).Bytecode.Data);
}
public IntegerExpressionEditorViewModel TilingEditor { get; } = new() { CustomValue = 1, MinValue = 1, MaxValue = 16 };
public ValueNodeInputViewModel<ITypedExpression<int>> Tiling { get; }
public EnumEditorViewModel PatternEditor { get; } = new(typeof(Shape2DGrayscaleModel.EPattern));
public ValueNodeInputViewModel<int> Pattern { get; }
public FloatExpressionEditorViewModel ScaleEditor { get; } = new() { CustomValue = 1, MaxValue = 1 };
public ValueNodeInputViewModel<ITypedExpression<float>> Scale { get; }
public FloatExpressionEditorViewModel SizeXEditor { get; } = new() { CustomValue = .5f, MaxValue = 1 };
public ValueNodeInputViewModel<ITypedExpression<float>> SizeX { get; }
public FloatExpressionEditorViewModel SizeYEditor { get; } = new() { CustomValue = .5f, MaxValue = 1 };
public ValueNodeInputViewModel<ITypedExpression<float>> SizeY { get; }
public FloatExpressionEditorViewModel AngleEditor { get; } = new() { MaxValue = (float)(2 * Math.PI) };
public ValueNodeInputViewModel<ITypedExpression<float>> Angle { get; }
public BooleanExpressionEditorViewModel Rotate45Editor { get; } = new();
public ValueNodeInputViewModel<ITypedExpression<bool>> Rotate45 { get; }
}
}

View File

@@ -0,0 +1,19 @@
using System.Xml.Serialization;
using Intromat.Nodes.Code;
using Intromat.ViewModels;
namespace Intromat.Nodes.Textures
{
[XmlRoot("SolidColor", Namespace = _namespace)]
public sealed class SolidColorModel : DxTextureModelBase
{
public IntLiteralModel Red { get; set; } = null!;
public IntLiteralModel Green { get; set; } = null!;
public IntLiteralModel Blue { get; set; } = null!;
public override CodeGenNodeViewModel CreateViewModel()
{
return new SolidColorNode();
}
}
}

View File

@@ -0,0 +1,90 @@
using DynamicData;
using Intromat.Model.Compiler;
using Intromat.PersistentModel;
using Intromat.ViewModels;
using Intromat.ViewModels.Editors;
using Intromat.Views;
using NodeNetwork.Toolkit.ValueNode;
using NodeNetwork.ViewModels;
using ReactiveUI;
using SharpDX.Direct3D11;
using SharpDX.Mathematics.Interop;
using Splat;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
namespace Intromat.Nodes.Textures
{
[CategoryOrder("Solid Color", 1)]
public class SolidColorNode : DxTextureNodeBase
{
static SolidColorNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<SolidColorNode>));
}
public SolidColorNode()
{
Name = "Solid Color";
var group = new EndpointGroup("SolidColor");
Inputs.Add(Red = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "Input",
Group = group,
Editor = RedEditor
});
Inputs.Add(Green = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "Green",
Group = group,
Editor = GreenEditor
});
Inputs.Add(Blue = new CodeGenInputViewModel<ITypedExpression<int>>(EPortType.Integer)
{
Name = "Blue",
Group = group,
Editor = BlueEditor
});
}
public override NodeModelBase CreateModel()
{
return new SolidColorModel();
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var solidColor = (SolidColorModel)model;
solidColor.Red = RedEditor.CreateModel();
solidColor.Green = GreenEditor.CreateModel();
solidColor.Blue = BlueEditor.CreateModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var solidColor = (SolidColorModel)model;
RedEditor.LoadModel(solidColor.Red);
GreenEditor.LoadModel(solidColor.Green);
BlueEditor.LoadModel(solidColor.Blue);
}
protected override void UpdateFrame(Device device, DeviceContext context)
{
base.UpdateFrame(device, context);
float r = Red.Value.Evaluate() / 255.0f;
float g = Green.Value.Evaluate() / 255.0f;
float b = Blue.Value.Evaluate() / 255.0f;
context.ClearUnorderedAccessView(_output!.UnorderedAccessView, new RawVector4(r, g, b, 1.0f));
}
public IntegerExpressionEditorViewModel RedEditor { get; } = new() { MinValue = 0, MaxValue = 255};
public ValueNodeInputViewModel<ITypedExpression<int>> Red { get; }
public IntegerExpressionEditorViewModel GreenEditor { get; } = new() { MinValue = 0, MaxValue = 255};
public ValueNodeInputViewModel<ITypedExpression<int>> Green { get; }
public IntegerExpressionEditorViewModel BlueEditor { get; } = new() { MinValue = 0, MaxValue = 255};
public ValueNodeInputViewModel<ITypedExpression<int>> Blue { get; }
}
}

Some files were not shown because too many files have changed in this diff Show More