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>(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>(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(EPortType.Texture) { Name = "Output", Group = textureGroup }); var dxHost = Locator.Current.GetService()!; 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> Width { get; } public DimensionEditorViewModel HeightEditor { get; } public ValueNodeInputViewModel> Height { get; } public ValueNodeOutputViewModel Output { get; } } }