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

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; }
}
}

View File

@@ -0,0 +1,22 @@
using System.Xml.Serialization;
using Intromat.Nodes.Code;
using Intromat.ViewModels;
namespace Intromat.Nodes.Textures
{
[XmlRoot("Transform2DColor", Namespace = _namespace)]
public sealed class Transform2DColorModel : DxTextureModelBase
{
public BooleanLiteralModel Clamp { get; set; } = default!;
public FloatLiteralModel ScaleX { get; set; } = default!;
public FloatLiteralModel ScaleY { get; set; } = default!;
public FloatLiteralModel OffsetX { get; set; } = default!;
public FloatLiteralModel OffsetY { get; set; } = default!;
public FloatLiteralModel Rotate { get; set; } = default!;
public override CodeGenNodeViewModel CreateViewModel()
{
return new Transform2DColorNode();
}
}
}

View File

@@ -0,0 +1,190 @@
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("Transform 2D", 1)]
public class Transform2DColorNode : DxTextureFilterNodeBase
{
private Buffer? _constantBuffer;
private ComputeShader? _cs;
private const string _computeShader = @"cbuffer cb
{
float2 s;
float2 p;
float r;
int c;
}
Texture2D t;
SamplerState ss;
RWTexture2D<float4> o;
[numthreads(16,16,1)]
void main(in uint3 i : SV_DispatchThreadID)
{
float sa, ca;
sincos(r, sa, ca);
float2 dim;
o.GetDimensions(dim.x, dim.y);
float2 uv = i.xy / dim;
uv -= p;
uv = uv * 2 - 1;
uv = mul(uv, float2x2(ca, -sa, sa, ca));
uv /= s;
uv = uv * .5 + .5;
if (c == 0) uv = frac(uv);
o[i.xy] = t.SampleLevel(ss, uv, 0);
}";
static Transform2DColorNode()
{
Locator.CurrentMutable.Register(() => new CodeGenNodeView(), typeof(IViewFor<Transform2DColorNode>));
}
public Transform2DColorNode()
{
Name = "Transform 2D";
var group = new EndpointGroup("Transform 2D");
Inputs.Add(Clamp = new CodeGenInputViewModel<ITypedExpression<bool>>(EPortType.Boolean)
{
Name = "Clamp",
Group = group,
Editor = ClampEditor
});
Inputs.Add(ScaleX = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Scale X",
Group = group,
Editor = ScaleXEditor
});
Inputs.Add(ScaleY = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Scale Y",
Group = group,
Editor = ScaleYEditor
});
Inputs.Add(OffsetX = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Offset X",
Group = group,
Editor = OffsetXEditor
});
Inputs.Add(OffsetY = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Offset Y",
Group = group,
Editor = OffsetYEditor
});
Inputs.Add(Rotate = new CodeGenInputViewModel<ITypedExpression<float>>(EPortType.Float)
{
Name = "Rotate",
Group = group,
Editor = RotateEditor
});
}
public override NodeModelBase CreateModel()
{
return new Transform2DColorModel();
}
public override void SaveModel(NodeModelBase model)
{
base.SaveModel(model);
var transform2DColor = (Transform2DColorModel)model;
transform2DColor.ScaleX = ScaleXEditor.CreateModel();
transform2DColor.ScaleY = ScaleYEditor.CreateModel();
transform2DColor.OffsetX = OffsetXEditor.CreateModel();
transform2DColor.OffsetY = OffsetYEditor.CreateModel();
transform2DColor.Rotate = RotateEditor.CreateModel();
transform2DColor.Clamp = ClampEditor.CreateModel();
}
public override void LoadModel(NodeModelBase model)
{
base.LoadModel(model);
var transform2DColor = (Transform2DColorModel)model;
ScaleXEditor.LoadModel(transform2DColor.ScaleX);
ScaleYEditor.LoadModel(transform2DColor.ScaleY);
OffsetXEditor.LoadModel(transform2DColor.OffsetX);
OffsetYEditor.LoadModel(transform2DColor.OffsetY);
RotateEditor.LoadModel(transform2DColor.Rotate);
ClampEditor.LoadModel(transform2DColor.Clamp);
}
[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);
}
protected override unsafe void UpdateFrame(Device device, DeviceContext context)
{
base.UpdateFrame(device, context);
var srv = Input.Value?.ShaderResourceView;
if (srv == null)
return;
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);
floats[0] = ScaleX.Value.Evaluate();
floats[1] = ScaleY.Value.Evaluate();
floats[2] = OffsetX.Value.Evaluate();
floats[3] = OffsetY.Value.Evaluate();
floats[4] = Rotate.Value.Evaluate();
ints[5] = Clamp.Value.Evaluate() ? 1 : 0;
context.UnmapSubresource(_constantBuffer, 0);
context.ComputeShader.SetConstantBuffer(0, _constantBuffer);
context.ComputeShader.SetShaderResource(0, srv);
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.SetUnorderedAccessView(0, null);
}
public ValueNodeInputViewModel<ITypedExpression<bool>> Clamp { get; }
public BooleanExpressionEditorViewModel ClampEditor { get; } = new();
public ValueNodeInputViewModel<ITypedExpression<float>> ScaleX { get; }
public FloatExpressionEditorViewModel ScaleXEditor { get; } = new() { CustomValue = 1, MaxValue = 4 };
public ValueNodeInputViewModel<ITypedExpression<float>> ScaleY { get; }
public FloatExpressionEditorViewModel ScaleYEditor { get; } = new() { CustomValue = 1, MaxValue = 4 };
public ValueNodeInputViewModel<ITypedExpression<float>> OffsetX { get; }
public FloatExpressionEditorViewModel OffsetXEditor { get; } = new() { MinValue = -1, MaxValue = 1 };
public ValueNodeInputViewModel<ITypedExpression<float>> OffsetY { get; }
public FloatExpressionEditorViewModel OffsetYEditor { get; } = new() { MinValue = -1, MaxValue = 1 };
public ValueNodeInputViewModel<ITypedExpression<float>> Rotate { get; }
public FloatExpressionEditorViewModel RotateEditor { get; } = new() { MaxValue = (float)(2 * Math.PI) };
}
}

View File

@@ -0,0 +1,9 @@
namespace Intromat.PersistentModel
{
public class ConnectionModel
{
public EndpointModel Input { get; set; } = null!;
public EndpointModel Output { get; set; } = null!;
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.ComponentModel;
using System.Xml.Serialization;
namespace Intromat.PersistentModel
{
public class EndpointModel
{
[XmlAttribute]
public Guid Node { get; set; }
[XmlAttribute]
[DefaultValue(null)]
public string Name { get; set; } = null!;
}
}

View File

@@ -0,0 +1,27 @@
using System.ComponentModel;
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.PersistentModel
{
public class GroupEndpointModel
{
[XmlAttribute]
public string EndpointType { get; set; } = null!;
[XmlAttribute]
public EPortType PortType { get; set; }
[XmlAttribute]
[DefaultValue(false)]
public bool List { get; set; }
[XmlAttribute]
[DefaultValue(null)]
public string Name { get; set; } = null!;
[XmlAttribute]
[DefaultValue(0)]
public int SortIndex { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Intromat.PersistentModel
{
[XmlRoot("Network", Namespace = NodeModelBase._namespace)]
public class NetworkModel
{
[XmlElement]
public NodeCollectionModel Nodes { get; set; } = new();
[XmlArray]
public List<ConnectionModel> Connections { get; set; } = new();
[XmlArray]
public List<NodeMetaData> MetaData { get; set; } = new();
[XmlArray]
public List<NetworkModel> SubNetworks { get; set; } = new();
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace Intromat.PersistentModel
{
public class NodeCollectionModel : IXmlSerializable
{
public static readonly Dictionary<string, Type> _nodeTypes;
public static readonly XmlSerializerNamespaces _nameSpaces;
static NodeCollectionModel()
{
_nodeTypes = typeof(NodeModelBase)
.Assembly
.GetTypes()
.Where(t => typeof(NodeModelBase).IsAssignableFrom(t) && !t.IsAbstract)
.Select(type =>
{
var attrib = (XmlRootAttribute)type.GetCustomAttribute(typeof(XmlRootAttribute))!;
return (attrib.ElementName, Type: type);
})
.ToDictionary(pair => pair.ElementName, pair => pair.Type);
_nameSpaces = new XmlSerializerNamespaces();
_nameSpaces.Add(string.Empty, NodeModelBase._namespace);
_nameSpaces.Add("x", "http://www.w3.org/2001/XMLSchema-instance");
}
public List<NodeModelBase> Items { get; set; } = new();
public XmlSchema? GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
var isEmptyElement = reader.IsEmptyElement;
reader.ReadStartElement();
if (!isEmptyElement)
{
while (reader.NodeType != XmlNodeType.EndElement)
{
if (_nodeTypes.TryGetValue(reader.Name, out var nodeType))
{
var serializer = new XmlSerializer(nodeType);
var node = (NodeModelBase)serializer.Deserialize(reader)!;
Items.Add(node);
}
}
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
if (Items.Count == 0)
return;
foreach (var node in Items)
{
var serializer = new XmlSerializer(node.GetType());
serializer.Serialize(writer, node, _nameSpaces);
}
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.ComponentModel;
namespace Intromat.PersistentModel
{
public class NodeMetaData
{
public Guid Node { get; set; }
public PositionModel Position { get; set; }
[DefaultValue(false)]
public bool IsCollapsed { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.ComponentModel;
using System.Xml.Serialization;
using Intromat.ViewModels;
namespace Intromat.PersistentModel
{
public abstract class NodeModelBase
{
public abstract CodeGenNodeViewModel? CreateViewModel();
[XmlAttribute]
public Guid Guid { get; set; }
[XmlAttribute]
[DefaultValue("")]
public string Name { get; set; } = "";
public const string _namespace = "http://blu-flame.org/intromat";
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using Intromat.ViewModels;
using Intromat.ViewModels.Nodes;
namespace Intromat.PersistentModel.Nodes
{
[XmlRoot("Group", Namespace = _namespace)]
public class GroupModel : NodeModelBase
{
public NetworkModel Network { get; set; } = null!;
public Guid Entrance { get; set; }
public Guid Exit { get; set; }
public List<GroupEndpointModel> Inputs { get; set; } = new();
public List<GroupEndpointModel> Outputs { get; set; } = new();
public override CodeGenNodeViewModel CreateViewModel()
{
return new GroupNodeViewModel();
}
}
}

View File

@@ -0,0 +1,18 @@
using System.Xml.Serialization;
using Intromat.ViewModels;
using Intromat.ViewModels.Nodes;
namespace Intromat.PersistentModel.Nodes
{
[XmlRoot("GroupSubnetIO", Namespace = _namespace)]
public class GroupSubnetIOModel : NodeModelBase
{
[XmlAttribute]
public bool IsEntrance { get; set; }
public override CodeGenNodeViewModel CreateViewModel()
{
return new GroupSubnetIONodeViewModel();
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Windows;
using System.Xml.Serialization;
namespace Intromat.PersistentModel
{
public struct PositionModel
{
[XmlAttribute]
public double X { get; set; }
[XmlAttribute]
public double Y { get; set; }
public static implicit operator Point(PositionModel position)
{
return new(position.X, position.Y);
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Intromat.PersistentModel
{
[XmlRoot("Project", Namespace = NodeModelBase._namespace)]
public class ProjectModel
{
[XmlAttribute]
public string Name { get; set; } = null!;
[XmlElement("Module")]
public List<string> Modules { get; set; } = new();
}
}

View File

@@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;
using System.Xml.Serialization;
using DynamicData;
using Intromat.Helpers;
using Intromat.PersistentModel.Nodes;
using Intromat.ViewModels;
using Intromat.ViewModels.Nodes;
using NodeNetwork.ViewModels;
namespace Intromat.PersistentModel
{
internal class ProjectSerializer
{
public static async Task<ProjectViewModel?> LoadProject(MainViewModel mainVm, string fullPath)
{
await using var stream = new FileStream(fullPath, FileMode.Open);
var projectSerializer = new XmlSerializer(typeof(ProjectModel));
var project = (ProjectModel)projectSerializer.Deserialize(stream)!;
var projectVm = new ProjectViewModel(project.Name, Path.GetFullPath(fullPath));
var directory = Path.GetDirectoryName(projectVm.FullPath)!;
foreach (var modulePath in project.Modules)
{
var moduleFullPath = Path.Combine(directory, modulePath);
var moduleVm = await LoadModule(mainVm, projectVm, moduleFullPath);
projectVm.Modules.Add(moduleVm);
}
return projectVm;
}
public static async Task<ModuleViewModel> LoadModule(MainViewModel mainVm, ProjectViewModel parent, string fullPath)
{
var moduleVm = new ModuleViewModel(parent, Path.GetFileName(fullPath)!, fullPath);
await LoadFolder(mainVm, moduleVm, moduleVm, moduleVm, fullPath);
return moduleVm;
}
private static async Task LoadFolder(MainViewModel mainVm, ModuleViewModel moduleVm, FolderViewModel parent, FolderViewModel target, string folderPath)
{
foreach (var subFolderPath in Directory.EnumerateDirectories(folderPath))
{
var subFolderVm = new FolderViewModel(moduleVm, parent, Path.GetDirectoryName(folderPath)!);
await LoadFolder(mainVm, moduleVm, target, subFolderVm, subFolderPath);
target.Folders.Add(subFolderVm);
}
foreach (var filePath in Directory.EnumerateFiles(folderPath))
{
switch (Path.GetExtension(filePath))
{
case ".igraph":
{
target.Files.Add(await LoadDocument(mainVm, moduleVm, target, filePath));
break;
}
case ".hlsl":
{
target.Files.Add(await LoadShader(mainVm, moduleVm, target, filePath));
break;
}
}
}
}
private static async Task<DocumentViewModel> LoadDocument(MainViewModel mainVm, ModuleViewModel moduleVm, FolderViewModel folderVm, string filePath)
{
await using var stream = new FileStream(filePath, FileMode.Open);
var networkSerializer = new XmlSerializer(typeof(NetworkModel));
var network = (NetworkModel)networkSerializer.Deserialize(stream)!;
var documentVm = new DocumentViewModel(mainVm, moduleVm, folderVm, Path.GetFileNameWithoutExtension(filePath));
LoadNetwork(documentVm.MainNetwork, network, documentVm);
return documentVm;
}
private static async Task<ShaderFileViewModel> LoadShader(MainViewModel mainVm, ModuleViewModel moduleVm, FolderViewModel folderVm, string filePath)
{
await using var stream = new FileStream(filePath, FileMode.Open);
var shaderFileVm = new ShaderFileViewModel(mainVm, moduleVm, folderVm, Path.GetFileNameWithoutExtension(filePath));
using var reader = new StreamReader(stream);
shaderFileVm.Source = await reader.ReadToEndAsync();
return shaderFileVm;
}
public static CodeGenNetworkViewModel LoadNetwork(CodeGenNetworkViewModel networkVm, NetworkModel network, DocumentViewModel documentVm, GroupNodeViewModel? baseGroup = null)
{
PreLoadNetwork(network, documentVm, baseGroup, networkVm, out var nodeMap);
PostLoadNetwork(network, networkVm, nodeMap);
return networkVm;
}
private static void PreLoadNetwork(NetworkModel network, DocumentViewModel documentVm, GroupNodeViewModel? baseGroup, CodeGenNetworkViewModel networkVm, out Dictionary<Guid, NodeViewModel> outNodeMap)
{
var nodeMap = outNodeMap = new Dictionary<Guid, NodeViewModel>();
GroupSubnetIONodeViewModel? entrance = null, exit = null;
var groups = new List<GroupModel>();
networkVm.Nodes.Edit(nodes =>
{
foreach (var node in network.Nodes.Items)
{
var nodeVm = node.CreateViewModel()!;
nodeVm.LoadModel(node);
nodes.Add(nodeVm);
nodeMap.Add(node.Guid, nodeVm);
if (nodeVm is GroupSubnetIONodeViewModel subnetIONodeVm)
{
Debug.Assert(baseGroup != null, nameof(baseGroup) + " != null");
if (subnetIONodeVm.IsEntranceNode)
{
entrance = subnetIONodeVm;
}
else
{
exit = subnetIONodeVm;
}
}
else if (node is GroupModel group)
{
groups.Add(group);
}
}
});
if (baseGroup != null && entrance != null && exit != null)
{
var groupBinding = new CodeNodeGroupIOBinding(baseGroup, entrance, exit);
baseGroup.IOBinding = groupBinding;
entrance.IOBinding = groupBinding;
exit.IOBinding = groupBinding;
}
foreach (var group in groups)
{
var groupVm = (GroupNodeViewModel)nodeMap[group.Guid];
var subNetworkVm = new CodeGenNetworkViewModel(documentVm, group.Name);
PreLoadNetwork(group.Network, documentVm, groupVm, subNetworkVm, out var subNetworkNodeMap);
var ioBinding = groupVm.IOBinding!;
groupVm.Inputs.Edit(inputs =>
{
foreach (var input in group.Inputs)
{
var valueType = Type.GetType(input.EndpointType)!;
var inputVm = ioBinding.CreateInput(input.PortType, input.Name, valueType);
inputVm.SortIndex = input.SortIndex;
inputs.Add(inputVm);
}
});
groupVm.Outputs.Edit(outputs =>
{
foreach (var output in group.Outputs)
{
var valueType = Type.GetType(output.EndpointType)!;
var outputVm = ioBinding.CreateOutput(output.PortType, output.Name, valueType, output.List);
outputVm.SortIndex = output.SortIndex;
outputs.Add(outputVm);
}
});
groupVm.Subnet = subNetworkVm;
groupVm.Name = subNetworkVm.Name;
PostLoadNetwork(group.Network, subNetworkVm, subNetworkNodeMap);
}
}
private static void PostLoadNetwork(NetworkModel network, CodeGenNetworkViewModel networkVm, Dictionary<Guid, NodeViewModel> nodeMap)
{
networkVm.Connections.Edit(connections =>
{
foreach (var connection in network.Connections)
{
var inputVm = nodeMap[connection.Input.Node];
var outputVm = nodeMap[connection.Output.Node];
var input = inputVm.Inputs.Items.Single(i => i.Name == connection.Input.Name);
var output = outputVm.Outputs.Items.Single(o => o.Name == connection.Output.Name);
((CodeGenPortViewModel)input.Port).IsPortVisible = true;
((CodeGenPortViewModel)output.Port).IsPortVisible = true;
var connectionVm = new ConnectionViewModel(networkVm, input, output);
connections.Add(connectionVm);
}
});
foreach (var metaData in network.MetaData)
{
var nodeVm = nodeMap[metaData.Node];
nodeVm.Position = new Point(metaData.Position.X, metaData.Position.Y);
nodeVm.IsCollapsed = metaData.IsCollapsed;
}
}
public static async Task<ProjectModel> SaveModel(ProjectViewModel projectVm)
{
var directoryName = Path.GetDirectoryName(projectVm.FullPath)!;
Directory.CreateDirectory(directoryName);
await using var stream = new FileStream(projectVm.FullPath, FileMode.Create);
await using var writer = new XmlTextWriter(stream, Encoding.UTF8) { Formatting = Formatting.Indented };
var project = new ProjectModel { Name = projectVm.Name };
foreach (var moduleVm in projectVm.Modules)
{
project.Modules.Add(IOHelper.GetRelativePath(directoryName, moduleVm.FullPath));
await SaveModel(moduleVm);
}
var projectSerializer = new XmlSerializer(typeof(ProjectModel));
projectSerializer.Serialize(writer, project, NodeCollectionModel._nameSpaces);
return project;
}
public static async Task SaveModel(FolderViewModel folderVm)
{
foreach (var subFolderVm in folderVm.Folders.Items)
{
await SaveModel(subFolderVm);
}
foreach (var documentVm in folderVm.Files.Items)
{
await SaveModel(documentVm);
}
}
private static async Task SaveModel(FileViewModel fileVm)
{
Directory.CreateDirectory(Path.GetDirectoryName(fileVm.FullPath)!);
await using var stream = new FileStream(fileVm.FullPath, FileMode.Create);
switch (fileVm)
{
case DocumentViewModel documentVm:
{
var network = SaveModel(documentVm.MainNetwork);
await using var writer = new XmlTextWriter(stream, Encoding.UTF8) { Formatting = Formatting.Indented };
var moduleSerializer = new XmlSerializer(typeof(NetworkModel));
moduleSerializer.Serialize(writer, network, NodeCollectionModel._nameSpaces);
break;
}
case ShaderFileViewModel shaderVm:
{
var shader = shaderVm.Source;
await using var writer = new StreamWriter(stream);
await writer.WriteAsync(shader);
break;
}
default:
{
throw new NotImplementedException();
}
}
}
public static NetworkModel SaveModel(CodeGenNetworkViewModel networkVm)
{
Debug.Assert(networkVm.Name != null, $"{nameof(networkVm)}.{nameof(networkVm.Name)} != null");
var network = new NetworkModel();
foreach (var item in networkVm.Nodes.Items)
{
var nodeVm = (CodeGenNodeViewModel)item;
var node = nodeVm.CreateModel();
nodeVm.SaveModel(node);
network.Nodes.Items.Add(node);
}
foreach (var connectionVm in networkVm.Connections.Items)
{
var connection = new ConnectionModel
{
Input = new EndpointModel { Name = connectionVm.Input.Name, Node = ((CodeGenNodeViewModel)connectionVm.Input.Parent).Guid },
Output = new EndpointModel { Name = connectionVm.Output.Name, Node = ((CodeGenNodeViewModel)connectionVm.Output.Parent).Guid },
};
network.Connections.Add(connection);
}
foreach (var nodeVm in networkVm.Nodes.Items)
{
var metaData = new NodeMetaData
{
Position = new PositionModel
{
X = nodeVm.Position.X,
Y = nodeVm.Position.Y
},
IsCollapsed = nodeVm.IsCollapsed,
Node = ((CodeGenNodeViewModel)nodeVm).Guid
};
network.MetaData.Add(metaData);
}
return network;
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.Mathematics.Interop;
namespace Intromat.Pipelines
{
public class DisplayMeshPipeline
{
private readonly VertexShader _vs;
private readonly PixelShader _ps;
private readonly DeviceContext _context;
private readonly SamplerState _sampler;
private ShaderResourceView? _srv;
private readonly RasterizerState _rs;
private readonly DepthStencilState _dss;
private const string _vertexShader = "void main(in uint i : SV_VertexID, out float2 uv : TEXCOORD, out float4 pos : SV_Position) { uv = float2(i % 2, i / 2); pos = float4(uv * 2 - 1, 0.5, 1); }";
private const string _pixelShader = "Texture2D t; SamplerState s; float4 main(in float2 uv : TEXCOORD) : SV_Target0 { return float4(t.Sample(s, uv).xyz, 1); }";
public DisplayMeshPipeline(Device device, DeviceContext context)
{
var flags = ShaderFlags.None;
#if DEBUG
flags |= ShaderFlags.Debug;
#endif
_vs = new VertexShader(device, ShaderBytecode.Compile(_vertexShader, "main", "vs_5_0", flags).Bytecode.Data);
_ps = new PixelShader(device, ShaderBytecode.Compile(_pixelShader, "main", "ps_5_0", flags).Bytecode.Data);
_context = context;
var samplerDesc = new SamplerStateDescription
{
Filter = Filter.MinMagMipLinear,
AddressU = TextureAddressMode.Clamp,
AddressV = TextureAddressMode.Clamp,
AddressW = TextureAddressMode.Clamp
};
_sampler = new SamplerState(device, samplerDesc);
var rasterizerDesc = RasterizerStateDescription.Default();
rasterizerDesc.CullMode = CullMode.None;
rasterizerDesc.IsDepthClipEnabled = false;
_rs = new RasterizerState(device, rasterizerDesc);
var depthStencilStateDesc = DepthStencilStateDescription.Default();
depthStencilStateDesc.IsDepthEnabled = false;
_dss = new DepthStencilState(device, depthStencilStateDesc);
}
public void Apply()
{
var viewport = _context.Rasterizer.GetViewports<RawViewportF>()[0];
var dim = Math.Min(viewport.Width, viewport.Height);
var offsetX = dim < viewport.Width ? (viewport.Width - dim) / 2 : 0;
var offsetY = dim < viewport.Height ? (viewport.Height - dim) / 2 : 0;
_context.Rasterizer.SetViewport(offsetX, offsetY, dim, dim);
_context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
_context.VertexShader.Set(_vs);
_context.PixelShader.Set(_ps);
_context.PixelShader.SetShaderResources(0, _srv);
_context.PixelShader.SetSampler(0, _sampler);
_context.Rasterizer.State = _rs;
_context.OutputMerger.DepthStencilState = _dss;
_context.Draw(4, 0);
_context.PixelShader.SetShaderResources(0, (ShaderResourceView?)null);
}
public ShaderResourceView? ShaderResourceView
{
get => _srv;
set => _srv = value;
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.Mathematics.Interop;
namespace Intromat.Pipelines
{
public class DisplayTexturePipeline
{
private readonly VertexShader _vs;
private readonly PixelShader _ps;
private readonly DeviceContext _context;
private readonly SamplerState _sampler;
private ShaderResourceView? _srv;
private readonly RasterizerState _rs;
private readonly DepthStencilState _dss;
private const string _vertexShader = "void main(in uint i : SV_VertexID, out float2 uv : TEXCOORD, out float4 pos : SV_Position) { uv = float2(i % 2, i / 2); pos = float4(uv * 2 - 1, 0.5, 1); }";
private const string _pixelShader = "Texture2D t; SamplerState s; float4 main(in float2 uv : TEXCOORD) : SV_Target0 { return float4(t.Sample(s, uv).xyz, 1); }";
public DisplayTexturePipeline(Device device, DeviceContext context)
{
var flags = ShaderFlags.None;
#if DEBUG
flags |= ShaderFlags.Debug;
#endif
_vs = new VertexShader(device, ShaderBytecode.Compile(_vertexShader, "main", "vs_5_0", flags).Bytecode.Data);
_ps = new PixelShader(device, ShaderBytecode.Compile(_pixelShader, "main", "ps_5_0", flags).Bytecode.Data);
_context = context;
var samplerDesc = new SamplerStateDescription
{
Filter = Filter.MinMagMipLinear,
AddressU = TextureAddressMode.Clamp,
AddressV = TextureAddressMode.Clamp,
AddressW = TextureAddressMode.Clamp
};
_sampler = new SamplerState(device, samplerDesc);
var rasterizerDesc = RasterizerStateDescription.Default();
rasterizerDesc.CullMode = CullMode.None;
rasterizerDesc.IsDepthClipEnabled = false;
_rs = new RasterizerState(device, rasterizerDesc);
var depthStencilStateDesc = DepthStencilStateDescription.Default();
depthStencilStateDesc.IsDepthEnabled = false;
_dss = new DepthStencilState(device, depthStencilStateDesc);
}
public void Apply()
{
var viewport = _context.Rasterizer.GetViewports<RawViewportF>()[0];
var dim = Math.Min(viewport.Width, viewport.Height);
var offsetX = dim < viewport.Width ? (viewport.Width - dim) / 2 : 0;
var offsetY = dim < viewport.Height ? (viewport.Height - dim) / 2 : 0;
_context.Rasterizer.SetViewport(offsetX, offsetY, dim, dim);
_context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
_context.VertexShader.Set(_vs);
_context.PixelShader.Set(_ps);
_context.PixelShader.SetShaderResources(0, _srv);
_context.PixelShader.SetSampler(0, _sampler);
_context.Rasterizer.State = _rs;
_context.OutputMerger.DepthStencilState = _dss;
_context.Draw(4, 0);
_context.PixelShader.SetShaderResources(0, (ShaderResourceView?)null);
}
public ShaderResourceView? ShaderResourceView
{
get => _srv;
set => _srv = value;
}
}
}

View File

@@ -0,0 +1,53 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Intromat")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Intromat")]
[assembly: AssemblyCopyright("Copyright (c) BluFlame")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,8 @@
{
"profiles": {
"Intromat": {
"commandName": "Project",
"nativeDebugging": false
}
}
}

View File

@@ -0,0 +1,436 @@
<?xml version="1.0"?>
<SyntaxDefinition name="C#" extensions=".cs" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
<Color name="Comment" foreground="#FF57A64A" exampleText="// comment" />
<Color name="Preprocessor" foreground="#FF9B9B9B" exampleText="#region Title"/>
<Color name="Metadata" foreground="#FF4A57A6" exampleText="[numthreads(16, 16, 1)]"/>
<Color name="Punctuation" foreground="#FFFFFFFF" exampleText="a(b.c);"/>
<Color name="MethodCall" foreground="#FFdcdcaa" exampleText="o.ToString();"/>
<Color name="FunctionCall" foreground="#FF88cdcd" exampleText="float a = clamp(b);"/>
<Color name="Attributes" foreground="#FFdcdcaa" fontWeight="bold" exampleText="float a = clamp(b);"/>
<Color name="NumberLiteral" foreground="#FFb5cea8" exampleText="3.1415f"/>
<Color name="Keywords" foreground="#FFd8a0df" exampleText="if (a) {} else {}"/>
<Color name="Modifiers" foreground="#FF559CD6" fontWeight="bold" exampleText="static readonly int a;"/>
<Color name="TrueFalse" foreground="#FF00A0FF" exampleText="b = false; a = true;"/>
<Color name="TypeKeywords" foreground="#FF559CD6" exampleText="if (x is int) { a = x as int; type = typeof(int); size = sizeof(int); c = new object(); }"/>
<Color name="SemanticKeywords" foreground="#FF88cdcd" fontWeight="bold" exampleText="if (args == null) throw new ArgumentNullException(nameof(args));" />
<Color name="CommentMarkerSetTodo" foreground="#FF008B8B" fontWeight="bold" />
<RuleSet name="CommentMarkerSet">
<Keywords color="CommentMarkerSetTodo">
<Word>TODO</Word>
<Word>FIXME</Word>
</Keywords>
</RuleSet>
<!-- This is the main ruleset. -->
<RuleSet>
<Span color="Preprocessor">
<Begin>\#</Begin>
<RuleSet name="PreprocessorSet">
<Span> <!-- preprocessor directives that allows comments -->
<Begin fontWeight="bold">
(define|undef|if|elif|else|endif|line)\b
</Begin>
<RuleSet>
<Span color="Comment" ruleSet="CommentMarkerSet">
<Begin>//</Begin>
</Span>
</RuleSet>
</Span>
<Span> <!-- preprocessor directives that don't allow comments -->
<Begin fontWeight="bold">
(region|endregion|error|warning|pragma)\b
</Begin>
</Span>
</RuleSet>
</Span>
<Span color="Comment" ruleSet="CommentMarkerSet">
<Begin>//</Begin>
</Span>
<Span color="Comment" ruleSet="CommentMarkerSet" multiline="true">
<Begin>/\*</Begin>
<End>\*/</End>
</Span>
<Rule color="TypeKeywords">\b(Buffer|bool|int|uint|half|byte|short|ushort|float|double|long|ulong)([1-4]?)\b</Rule>
<Rule color="TypeKeywords">\b(RW)?(Buffer|ByteAddressBuffer|StructuredBuffer)\b</Rule>
<Rule color="TypeKeywords">\b(AppendStructuredBuffer|ConsumeStructuredBuffer|AppendBuffer|ConsumeBuffer)\b</Rule>
<Rule color="TypeKeywords">\b(Texture1D|Texture1DArray|Texture2D|Texture2DArray|Texture3D|TextureCube)\b</Rule>
<Rule color="TypeKeywords">\b(sampler|sampler1D|sampler2D|sampler3D|samplerCUBE|sampler_state|SamplerState)\b</Rule>
<Keywords color="TrueFalse">
<Word>true</Word>
<Word>false</Word>
</Keywords>
<Keywords color="Keywords">
<Word>if</Word>
<Word>else</Word>
<Word>break</Word>
<Word>continue</Word>
<Word>do</Word>
<Word>for</Word>
<Word>switch</Word>
<Word>while</Word>
<Word>return</Word>
<Word>technique</Word>
<Word>pass</Word>
<Word>compile</Word>
<Word>sampler</Word>
<Word>register</Word>
<Word>typedef</Word>
<Word>struct</Word>
<Word>cbuffer</Word>
<Word>class</Word>
<Word>goto</Word>
<Word>union</Word>
</Keywords>
<Span>
<Begin>\[</Begin>
<End>\]</End>
<RuleSet>
<Keywords color="Attributes">
<Word>unroll</Word>
<Word>loop</Word>
<Word>domain</Word>
<Word>earlydepthstencil</Word>
<Word>instance</Word>
<Word>maxtessfactor</Word>
<Word>numthreads</Word>
<Word>outputcontrolpoints</Word>
<Word>outputtopology</Word>
<Word>partitioning</Word>
<Word>patchconstantfunc</Word>
</Keywords>
</RuleSet>
</Span>
<Keywords color="Modifiers">
<Word>in</Word>
<Word>out</Word>
<Word>inout</Word>
<Word>precise</Word>
</Keywords>
<Rule color="SemanticKeywords">
:\s*\b
(SV_ClipDistance\d
|SV_CullDistance\d
|SV_Coverage
|SV_Depth
|SV_DepthGreaterEqual
|SV_DepthLessEqual
|SV_DispatchThreadID
|SV_DomainLocation
|SV_GroupID
|SV_GroupIndex
|SV_GroupThreadID
|SV_GSInstanceID
|SV_InnerCoverage
|SV_InsideTessFactor
|SV_InstanceID
|SV_IsFrontFace
|SV_OutputControlPointID
|SV_Position
|SV_PrimitiveID
|SV_RenderTargetArrayIndex
|SV_SampleIndex
|SV_StencilRef
|SV_Target\d
|SV_TessFactor
|SV_VertexID
|SV_ViewportArrayIndex
|SV_ShadingRate
|([A-Z][A-Z\d_]+))\b
</Rule>
<Rule color="FunctionCall">
\b
(abs
|acos
|all
|AllMemoryBarrier
|AllMemoryBarrierWithGroupSync
|any
|asdouble
|asfloat
|asin
|asint
|asuint
|atan
|atan2
|ceil
|clamp
|clip
|cos
|cosh
|countbits
|cross
|D3DCOLORtoUBYTE4
|ddx
|ddx_coarse
|ddx_fine
|ddy
|ddy_coarse
|ddy_fine
|degrees
|determinant
|DeviceMemoryBarrier
|DeviceMemoryBarrierWithGroupSync
|distance
|dot
|dst
|EvaluateAttributeAtCentroid
|EvaluateAttributeAtSample
|EvaluateAttributeSnapped
|exp
|exp2
|f16tof32
|f32tof16
|facetoforward
|firstbithigh
|firstbitlow
|floor
|fmod
|frac
|frexp
|fwidth
|GetRenderTargetSampleCount
|GetRenderTargetSamplePosition
|GroupMamoryBarrier
|GroupMamoryBarrierWithGroupSync
|InterlockedAdd
|InterlockedCompareExchange
|InterlockedCompareStore
|InterlockedExchange
|InterlockedMax
|InterlockedMin
|InterlockedOr
|InterlockedXor
|isfinite
|isinf
|isnan
|ldexp
|lerp
|lit
|log
|log10
|log2
|mad
|max
|min
|modf
|mul
|noise
|normalize
|pow
|Process2DQuadTessFactorsAvg
|Process2DQuadTessFactorsMax
|Process2DQuadTessFactorsMin
|ProcessIsolineTessFactors
|ProcessQuadTessFactorsAvg
|ProcessQuadTessFactorsMax
|ProcessQuadTessFactorsMin
|ProcessTriTessFactorsAvg
|ProcessTriTessFactorsMax
|ProcessTriTessFactorsMin
|radians
|rcp
|reflect
|refract
|reversebits
|round
|rsqrt
|saturate
|sign
|sin
|sincos
|sinh
|smoothstep
|sqrt
|step
|tan
|tanh
|tex1D
|tex1Dbias
|tex1Dgrad
|tex1Dlod
|tex1Dproj
|tex2D
|tex2Dbias
|tex2Dgrad
|tex2Dlod
|tex3D
|tex3Dbias
|tex3Dgrad
|tex3Dlod
|tex3Dproj
|texCUBE
|texCUBEbias
|texCUBEgrad
|texCUBElod
|texCUBEproj
|transpose
|trunc)
(?=\s*\() # followed by (
</Rule>
<Rule color="MethodCall">
\b
[\d\w_]+ # an identifier
(?=\s*\() # followed by (
</Rule>
<!-- Digits -->
<Rule color="NumberLiteral">
\b0[xX][0-9a-fA-F]+ # hex number
|
( \b\d+(\.[0-9]+)? #number with optional floating point
| \.[0-9]+ #or just starting with floating point
)
([eE][+-]?[0-9]+)? # optional exponent
</Rule>
<Rule color="Punctuation">
[?,.;()\[\]{}+\-/%*&lt;&gt;^+~!|&amp;]+
</Rule>
</RuleSet>
</SyntaxDefinition>
<!--
<SyntaxDefinition name="HLSL" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
<RuleSet>
<Rule color="Functions">
(abs
|acos
|all
|AllMemoryBarrier
|AllMemoryBarrierWithGroupSync
|any
|asdouble
|asfloat
|asin
|asint
|asuint
|atan
|atan2
|ceil
|clamp
|clip
|cos
|cosh
|countbits
|cross
|D3DCOLORtoUBYTE4
|ddx
|ddx_coarse
|ddx_fine
|ddy
|ddy_coarse
|ddy_fine
|degrees
|determinant
|DeviceMemoryBarrier
|DeviceMemoryBarrierWithGroupSync
|distance
|dot
|dst
|EvaluateAttributeAtCentroid
|EvaluateAttributeAtSample
|EvaluateAttributeSnapped
|exp
|exp2
|f16tof32
|f32tof16
|facetoforward
|firstbithigh
|firstbitlow
|floor
|fmod
|frac
|frexp
|fwidth
|GetRenderTargetSampleCount
|GetRenderTargetSamplePosition
|GroupMamoryBarrier
|GroupMamoryBarrierWithGroupSync
|InterlockedAdd
|InterlockedCompareExchange
|InterlockedCompareStore
|InterlockedExchange
|InterlockedMax
|InterlockedMin
|InterlockedOr
|InterlockedXor
|isfinite
|isinf
|isnan
|ldexp
|lerp
|lit
|log
|log10
|log2
|mad
|max
|min
|modf
|mul
|noise
|normalize
|pow
|Process2DQuadTessFactorsAvg
|Process2DQuadTessFactorsMax
|Process2DQuadTessFactorsMin
|ProcessIsolineTessFactors
|ProcessQuadTessFactorsAvg
|ProcessQuadTessFactorsMax
|ProcessQuadTessFactorsMin
|ProcessTriTessFactorsAvg
|ProcessTriTessFactorsMax
|ProcessTriTessFactorsMin
|radians
|rcp
|reflect
|refract
|reversebits
|round
|rsqrt
|saturate
|sign
|sin
|sincos
|sinh
|smoothstep
|sqrt
|step
|tan
|tanh
|tex1D
|tex1Dbias
|tex1Dgrad
|tex1Dlod
|tex1Dproj
|tex2D
|tex2Dbias
|tex2Dgrad
|tex2Dlod
|tex3D
|tex3Dbias
|tex3Dgrad
|tex3Dlod
|tex3Dproj
|texCUBE
|texCUBEbias
|texCUBEgrad
|texCUBElod
|texCUBEproj
|transpose
|trunc)(\s*[(])
</Rule>
</RuleSet>
</SyntaxDefinition>
-->

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