简体   繁体   English

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

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

I'm using HelixToolkit3D for a small school project which requires to generate 3D objects(Cubes) based on a existing list with objects(Custom create objects) and each cube face should be able to have its own image which will be as an string attribute(path to the image) to the object on the list that I mentioned earlier (for example front_image, back_image etc). 我正在将HelixToolkit3D用于一个小型学校项目,该项目需要根据包含对象(自定义创建对象)的现有列表生成3D对象(多维数据集),并且每个多维数据集面都应该具有自己的图像,该图像将作为字符串属性(图像的路径)到我前面提到的列表中的对象(例如front_image,back_image等)。 I'm using Wpf and I wanted to use binding for generating the 3D elements. 我正在使用Wpf,并且想使用绑定来生成3D元素。 After searching I found this link https://github.com/helix-toolkit/helix-toolkit/tree/develop/Source/Examples/WPF/ExampleBrowser/Examples/DataTemplate which was exactly what I needed, and I was able to make it work but only cubes with a solid color. 搜索后,我发现了此链接https://github.com/helix-toolkit/helix-toolkit/tree/develop/Source/Examples/WPF/ExampleBrowser/Examples/DataTemplate正是我所需要的,并且我能够它可以工作,但只能使用纯色的多维数据集。 I tried to set the material from image but it's not working. 我试图根据图像设置材料,但无法正常工作。 Also I wanted to add edge lines like a wireframe for each cube. 我也想为每个立方体添加线框之类的边缘线。

Here is my code so far: 到目前为止,这是我的代码:

3D view xaml file 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 view .cs file 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 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 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();
    }
}

This project is about a school library so there will be books and a shelf. 这个项目是关于学校图书馆的,所以会有书和架子。 Code is shorten in order to be more easy if someone tries to experiment(I hope I didn't remove any crucial lines). 为了简化代码(如果有人尝试进行实验,代码会缩短)(我希望我没有删除任何关键行)。 Basically there will be a 2D view on the beginning which will be used to create the shelf and add books and then if wanted user can switch to a 3D view in parallel windows. 基本上,开始时会有一个2D视图,用于创建书架和添加书籍,然后如果需要的话,用户可以在并行窗口中切换到3D视图。 I cant really understand the 3D "VisualTree" so good(so far) that's why I need some help. 我真的无法真正理解3D“ VisualTree”(到目前为止),这就是为什么我需要一些帮助。

I think you should be able to use this code for your cube if you adapt it. 我认为,如果您对它进行了修改,则应该可以将其用于多维数据集。

/// <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