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

时间:2017-08-27 09:38:07

标签: c# wpf image 3d helix-3d-toolkit

我正在使用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");
        }
    }
}

Shelf.cs

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&#34; VisualTree&#34;这么好(到目前为止),这就是为什么我需要一些帮助。

1 个答案:

答案 0 :(得分:0)

我认为如果你适应它,你应该能够将这个代码用于你的立方体。

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