繁体   English   中英

每个立方体侧面的HelixToolkit 3D WPF自定义图像

[英]HelixToolkit 3D WPF custom image for each cube side face

我正在将HelixToolkit3D用于一个小型学校项目,该项目需要根据包含对象(自定义创建对象)的现有列表生成3D对象(多维数据集),并且每个多维数据集面都应该具有自己的图像,该图像将作为字符串属性(图像的路径)到我前面提到的列表中的对象(例如front_image,back_image等)。 我正在使用Wpf,并且想使用绑定来生成3D元素。 搜索后,我发现了此链接https://github.com/helix-toolkit/helix-toolkit/tree/develop/Source/Examples/WPF/ExampleBrowser/Examples/DataTemplate正是我所需要的,并且我能够它可以工作,但只能使用纯色的多维数据集。 我试图根据图像设置材料,但无法正常工作。 我也想为每个立方体添加线框之类的边缘线。

到目前为止,这是我的代码:

3D视图XAML文件

<Controls:MetroWindow x:Class="Project1._3DView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Planom"
    xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
    xmlns:h="http://helix-toolkit.org/wpf"
    Closing="Window_Closing"
    Icon="Images/meIcon.ico"
    mc:Ignorable="d" 
    PreviewKeyDown="MetroWindow_PreviewKeyDown"
    WindowStartupLocation="CenterScreen" WindowState="Maximized"
    Title="Pamja 3-Dimensionale" Height="768" Width="1024">
<Window.Resources>
    <local:ColorConverter3D x:Key="colorConverter"/>
    <local:DataTemplate3D x:Key="{x:Type local:CubeElement}">
        <local:GenericUIElement3D widthX="{Binding Depth}" Material="{Binding Material}" heightZ="{Binding Height}" depthY="{Binding Width}" Color="{Binding color}">
            <local:GenericUIElement3D.Transform>
                <TranslateTransform3D OffsetX="{Binding Position.X}" OffsetY="{Binding Position.Y}" OffsetZ="{Binding Position.Z}" />
            </local:GenericUIElement3D.Transform>
        </local:GenericUIElement3D>
    </local:DataTemplate3D>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="71*"/>
        <RowDefinition Height="666*"/>
    </Grid.RowDefinitions>
    <h:HelixViewport3D ShowCoordinateSystem="True" ZoomExtentsWhenLoaded="True" ShowFrameRate="True" ShowCameraTarget="True" Grid.RowSpan="2">
        <h:SunLight>
            <h:SunLight.Transform>
                <TranslateTransform3D OffsetX="200" OffsetY="-200" OffsetZ="200" />
            </h:SunLight.Transform>
        </h:SunLight>
        <h:GridLinesVisual3D Center="0,0,0" Width="400" Length="400" MinorDistance="10" MajorDistance="10" Thickness="0.1" Fill="Black"/>
        <local:ItemsVisual3D ItemsSource="{Binding ObservableElements}" RefreshChildrenOnChange="True"/>
    </h:HelixViewport3D>
</Grid>

3D视图.cs文件

public partial class _3DView : MahApps.Metro.Controls.MetroWindow
{
    Shelf currentShelf;
    public ObservableCollection<Article> ObservableElements { get; set; }
    public _3DView(Shelf currentShelf)
    {
        InitializeComponent();
        this.ObservableElements = new ObservableCollection<CubeElement>();
        this.DataContext = this;
        this.currentShelf = currentShelf;
        foreach (CubeElement a in currentShelf.books)
        {
                a.Position = new Point3D((a.Depth / 2) - (a.Depth * a.depthF) + currentShelf.Depth / 2 + 1, (a.Width / 2) + a.leftPush  - currentShelf.Width / 2,  20);
                ObservableElements.Add(a);
        }
        currentShelf.items3D = ObservableElements;
    }
}

CubeElement.cs

public partial class CubeElement : INotifyPropertyChanged
{
    public CubeElement()
    {
        fSize = 12;
        changeTracking = false;
        drawRatio = 1;
        isSelectable = true;
    }

    public string Id { get; set; }
    public string Name { get; set; }
    public string left_image { get; set; }
    public string front_image { get; set; }
    public string back_image { get; set; }
    public string right_image { get; set; }
    public string top_image { get; set; }
    public Shelf shelf{ get; set; }
    private double _width { get; set; }
    public virtual double Width
    {
        get { return _width; }
        set
        {
            _width = value;
            widthDraw = _width * mainDraw;
            OnPropertyChanged("Width");
        }
    }
    private double _height { get; set; }
    public virtual double Height
    {
        get { return _height; }
        set
        {
            _height = value;
            heightDraw = _height * mainDraw;
            OnPropertyChanged(nameof(Height));
        }
    }
    public virtual double Depth { get; set; }
    public double Weight { get; set; }
    public string Color
    {
        get { return _Color; }
        set
        {
            _Color = value;
            color = (Color)ColorConverter.ConvertFromString(_Color);
            OnPropertyChanged("Color");
        }
    }  

    private string _Color { get; set; }
    public int _depthF { get; set; }
    public int depthF
    {
        get { return _depthF; }
        set
        {
            _depthF = value;
            if (shelf!= null)
            {
                Position = new Point3D((Depth / 2) - (Depth * _depthF) + shelf.Depth / 2 + 1, (Width / 2) + leftPush  - (shelf.Width / 2), 20);
            }
            OnPropertyChanged("depthF");
        }
    }
    private double _leftPush { get; set; }
    public double leftPush
    {
        get { return _leftPush; }
        set
        {
            _leftPush = value;
            leftPushP = value * drawRatioW;
            OnPropertyChanged("leftPush");
            Position = new Point3D((Depth / 2) - (Depth * depthF) + shelf.Depth / 2 + 1, (Width / 2) + (_leftPush / mainDraw) - (shelf.Width / 2), 20);
        }
    }        
    private string _imagePath { get; set; }
    [System.ComponentModel.DataAnnotations.Schema.NotMapped]
    public string imagePath
    {
        get { return _imagePath; }
        set
        {
            _imagePath = value; OnPropertyChanged("imagePath");
            if(_imagePath != null)
            {
                Material = MaterialHelper.CreateEmissiveImageMaterial(_imagePath, Brushes.Red, UriKind.Absolute);
            }
            else
            {
                Material = MaterialHelper.CreateMaterial(color);
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public object this[string propertyName]
    {
        get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
        set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
    }
    public Model3D Model { get; set; }
    public Material Material { get; set; }
    public Point3D _position;
    public Point3D Position
    {
        get { return _position; }
        set
        {
            _position = value;
            OnPropertyChanged("Position");
        }
    }
    public double Radius { get; set; }
    private bool isVisible = true;
    public bool IsVisible
    {
        get { return isVisible; }
        set
        {
            if (IsVisible != value)
            {
                isVisible = value;
                OnPropertyChanged("IsVisible");
            }
        }
    }
    private Color _color { get; set; }
    public Color color
    {
        get { return _color; }
        set
        {
            _color = value;
            OnPropertyChanged("color");
        }
    }
}

货架

public partial class Shelf
{
    public string Id { get; set; }
    public string Name { get; set; }
    private double _width { get; set; }
    public virtual double Width
    {
        get { return _width; }
        set
        {
            _width = value;
            OnPropertyChanged("Width");
        }
    }
    private double _height { get; set; }
    public virtual double Height
    {
        get { return _height; }
        set
        {
            _height = value;
            OnPropertyChanged("Height");
        }
    }
    public double Depth { get; set; }
    public double Weight { get; set; }   
    public ObservableCollection<CubeElement> books{ get; set; }
}

GenericUIElement3D.cs

public class GenericUIElement3D : UIElement3D
{
    protected GeometryModel3D Model { get; set; }

    public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
        nameof(Color), typeof(Color), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) => ((GenericUIElement3D)s).ColorChanged()));

    public static readonly DependencyProperty MaterialProperty = DependencyProperty.Register(
        nameof(Material), typeof(Material), typeof(GenericUIElement3D), new PropertyMetadata(null));

    public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(
        nameof(widthX), typeof(double), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) => ((GenericUIElement3D)s).DimensionsChanged()));

    public static readonly DependencyProperty HeightProperty = DependencyProperty.Register(
        nameof(heightZ), typeof(double), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) => ((GenericUIElement3D)s).DimensionsChanged()));

    public static readonly DependencyProperty DepthProperty = DependencyProperty.Register(
        nameof(depthY), typeof(double), typeof(GenericUIElement3D), new UIPropertyMetadata((s, e) => ((GenericUIElement3D)s).DimensionsChanged()));

    public Color Color
    {
        get { return (Color)GetValue(ColorProperty); }
        set { SetValue(ColorProperty, value); }
    }

    public double widthX
    {
        get { return (double)GetValue(WidthProperty); }
        set { SetValue(WidthProperty, value); }
    }

    protected override void OnMouseEnter(MouseEventArgs e)
    {
        base.OnMouseEnter(e);
        //MessageBox.Show("OnMouseDown raised. " + e.OriginalSource);
    }

    public double heightZ
    {
        get { return (double)GetValue(HeightProperty); }
        set { SetValue(HeightProperty, value); }
    }

    public double depthY
    {
        get { return (double)GetValue(DepthProperty); }
        set { SetValue(DepthProperty, value); }
    }

    public Material Material
    {
        get { return (Material)GetValue(MaterialProperty); }
        set { SetValue(MaterialProperty, value); }
    }

    public GenericUIElement3D()
    {
        Model = new GeometryModel3D();
        BindingOperations.SetBinding(Model, GeometryModel3D.MaterialProperty, new Binding(nameof(Material)) { Source = this });
        Visual3DModel = Model;
    }
    private void SetGeometry()
    {
        MeshBuilder meshBuilder = new MeshBuilder(false, false);
        meshBuilder.AddBox(new Point3D(0, 0, heightZ / 2), widthX, depthY, heightZ);
        Model.Geometry = meshBuilder.ToMesh();
    }

    private void ColorChanged()
    {
        Material = MaterialHelper.CreateMaterial(Color);
    }

    private void DimensionsChanged()
    {
        SetGeometry();
    }

    private void DepthChanged()
    {
        SetGeometry();
    }
}

这个项目是关于学校图书馆的,所以会有书和架子。 为了简化代码(如果有人尝试进行实验,代码会缩短)(我希望我没有删除任何关键行)。 基本上,开始时会有一个2D视图,用于创建书架和添加书籍,然后如果需要的话,用户可以在并行窗口中切换到3D视图。 我真的无法真正理解3D“ VisualTree”(到目前为止),这就是为什么我需要一些帮助。

我认为,如果您对它进行了修改,则应该可以将其用于多维数据集。

/// <summary>
/// A visual element that renders a box.
/// </summary>
/// <remarks>
/// The box is aligned with the local X, Y and Z coordinate system
/// Use a transform to orient the box in other directions.
/// </remarks>
public class TextureBoxVisual3D : ModelVisual3D
{

    /// <summary>
    /// Identifies the <see cref="Center"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty CenterProperty = DependencyProperty.Register(
        "Center", typeof(Point3D), typeof(TextureBoxVisual3D), new UIPropertyMetadata(new Point3D(), GeometryChanged));



    /// <summary>
    /// Identifies the <see cref="Height"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty HeightProperty = DependencyProperty.Register(
        "Height", typeof(double), typeof(TextureBoxVisual3D), new UIPropertyMetadata(1.0, GeometryChanged));

    /// <summary>
    /// Identifies the <see cref="Length"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty LengthProperty = DependencyProperty.Register(
        "Length", typeof(double), typeof(TextureBoxVisual3D), new UIPropertyMetadata(1.0, GeometryChanged));


    /// <summary>
    /// Identifies the <see cref="Width"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty WidthProperty = DependencyProperty.Register(
        "Width", typeof(double), typeof(TextureBoxVisual3D), new UIPropertyMetadata(1.0, GeometryChanged));

    /// <summary>
    /// Identifies the <see cref="Source"/> dependency property.
    /// </summary>
    public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
        "Source", typeof(string), typeof(TextureBoxVisual3D), new UIPropertyMetadata(null, GeometryChanged));

    /// <summary>
    /// The geometry changed.
    /// </summary>
    /// <param name="d">
    /// The d.
    /// </param>
    /// <param name="e">
    /// The event arguments.
    /// </param>
    private static void GeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((TextureBoxVisual3D)d).UpdateModel();
    }

    /// <summary>
    /// Gets or sets the center of the box.
    /// </summary>
    /// <value>The center.</value>
    public Point3D Center
    {
        get
        {
            return (Point3D)this.GetValue(CenterProperty);
        }

        set
        {
            this.SetValue(CenterProperty, value);
        }
    }

    /// <summary>
    /// Gets or sets the height (along local z-axis).
    /// </summary>
    /// <value>The height.</value>
    public double Height
    {
        get
        {
            return (double)this.GetValue(HeightProperty);
        }

        set
        {
            this.SetValue(HeightProperty, value);
        }
    }

    /// <summary>
    /// Gets or sets the length of the box (along local x-axis).
    /// </summary>
    /// <value>The length.</value>
    public double Length
    {
        get
        {
            return (double)this.GetValue(LengthProperty);
        }

        set
        {
            this.SetValue(LengthProperty, value);
        }
    }


    /// <summary>
    /// Gets or sets the width of the box (along local y-axis).
    /// </summary>
    /// <value>The width.</value>
    public double Width
    {
        get
        {
            return (double)this.GetValue(WidthProperty);
        }

        set
        {
            this.SetValue(WidthProperty, value);
        }
    }

    /// <summary>
    /// Gets or sets the panorama/skybox directory or file prefix.
    /// </summary>
    /// <remarks>
    /// If a directory is specified, the filename prefix will be set to "cube".
    /// If the filename prefix is "cube", the faces of the cube should be named
    /// cube_f.jpg
    /// cube_b.jpg
    /// cube_l.jpg
    /// cube_r.jpg
    /// cube_u.jpg
    /// cube_d.jpg
    /// </remarks>
    /// <value>The source.</value>
    public string Source
    {
        get
        {
            return (string)this.GetValue(SourceProperty);
        }

        set
        {
            this.SetValue(SourceProperty, value);
        }
    }


    /// <summary>
    /// Do the tessellation and return the <see cref="MeshGeometry3D"/>.
    /// </summary>
    /// <returns>The mesh geometry.</returns>
    protected void UpdateModel()
    {

        if (string.IsNullOrWhiteSpace(this.Source))
        {
            return;
        }
        string directory = Path.GetDirectoryName(this.Source);
        string prefix = Path.GetFileName(this.Source);

        if (string.IsNullOrEmpty(prefix))
        {
            prefix = "cube";
        }



        var front = Path.Combine(directory, prefix + "_f.jpg");
        var left = Path.Combine(directory, prefix + "_l.jpg");
        var right = Path.Combine(directory, prefix + "_r.jpg");
        var back = Path.Combine(directory, prefix + "_b.jpg");
        var up = Path.Combine(directory, prefix + "_u.jpg");
        var down = Path.Combine(directory, prefix + "_d.jpg");

        var group = new Model3DGroup();

        group.Children.Add(this.AddCubeSide(front, new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), this.Length, this.Width, this.Height));
        group.Children.Add(this.AddCubeSide(left, new Vector3D(-1, 0, 0), new Vector3D(0, 0, 1), this.Width, this.Length, this.Height));
        group.Children.Add(this.AddCubeSide(right, new Vector3D(1, 0, 0), new Vector3D(0, 0, 1), this.Width, this.Length, this.Height));
        group.Children.Add(this.AddCubeSide(back, new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), this.Length, this.Width, this.Height));
        group.Children.Add(this.AddCubeSide(up, new Vector3D(0, 0, 1), new Vector3D(0, -1, 0), this.Height, this.Width, this.Length));
        group.Children.Add(this.AddCubeSide(down, new Vector3D(0, 0, -1), new Vector3D(0, 1, 0), this.Height, this.Width, this.Length));

        this.Content = group;
    }

    private static Dictionary<string, Material> _materialDict = new Dictionary<string, Material>();

    /// <summary>
    /// The add cube side.
    /// </summary>
    /// <param name="normal">
    /// The normal.
    /// </param>
    /// <param name="up">
    /// The up.
    /// </param>
    /// <param name="fileName">
    /// The file name.
    /// </param>
    /// <returns>
    /// </returns>
    private GeometryModel3D AddCubeSide(string fileName, Vector3D normal, Vector3D up, double dist, double width, double height)
    {
        string fullPath = Path.GetFullPath(fileName);

        if (!File.Exists(fullPath))
        {
            return null;
        }

        Material material = null;
        if (_materialDict.ContainsKey(fileName))
        {
            material = _materialDict[fileName];
        }
        else
        {
            var image = new BitmapImage();
            image.BeginInit();
            image.UriSource = new Uri(fullPath);

            image.EndInit();

            var brush = new ImageBrush(image);
            material = new DiffuseMaterial(brush);

            _materialDict.Add(fileName, material);
        }

        var mesh = new MeshGeometry3D();
        var right = Vector3D.CrossProduct(normal, up);
        var origin = Center;

        var n = normal * dist / 2;
        up *= height / 2;
        right *= width / 2;

        // p1 is the lower left corner
        // p2 is the upper left
        // p3 is the lower right
        // p4 is the upper right
        var p1 = origin + n - up - right;
        var p2 = origin + n + up - right;
        var p3 = origin + n - up + right;
        var p4 = origin + n + up + right;

        mesh.Positions.Add(p1);
        mesh.Positions.Add(p2);
        mesh.Positions.Add(p3);
        mesh.Positions.Add(p4);

        //doublesided
        mesh.Positions.Add(p1); // 4
        mesh.Positions.Add(p2); // 5
        mesh.Positions.Add(p3); // 6
        mesh.Positions.Add(p4); // 7


        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(3);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(2);
        mesh.TriangleIndices.Add(3);


        //doublesided
        mesh.TriangleIndices.Add(4);
        mesh.TriangleIndices.Add(5);
        mesh.TriangleIndices.Add(7);
        mesh.TriangleIndices.Add(4);
        mesh.TriangleIndices.Add(7);
        mesh.TriangleIndices.Add(6);        



        mesh.TextureCoordinates.Add(new Point(0, 1));
        mesh.TextureCoordinates.Add(new Point(0, 0));
        mesh.TextureCoordinates.Add(new Point(1, 1));
        mesh.TextureCoordinates.Add(new Point(1, 0));

        //doublesided
        mesh.TextureCoordinates.Add(new Point(1, 1));
        mesh.TextureCoordinates.Add(new Point(1, 0));
        mesh.TextureCoordinates.Add(new Point(0, 1));
        mesh.TextureCoordinates.Add(new Point(0, 0));


        return new GeometryModel3D(mesh, material);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM