Files
bluflame/intromat/Intromat/Nodes/Textures/DxTextureNodeBase.cs
2026-04-18 22:31:51 +02:00

158 lines
5.8 KiB
C#

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