【WPF】AnaglyphDemo\BackgroundUpdate\Billboard\BuildingDemo

e538c2200711b08093db137b61c586d4.png

1.AnaglyphDemo  浮雕效果

<!--########### AnaglyphDemo  一个浮雕/立体影片查看器控件。###########-->
<Window x:Class="AnaglyphDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:h="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf" 
        xmlns:System="clr-namespace:System;assembly=mscorlib" 
        xmlns:local="clr-namespace:AnaglyphDemo" 
        Title="AnaglyphDemo" Height="480" Width="640">
    <Window.Resources>  <!-- 窗体资源 -->
    <!--静态资源 对象数据提供器:关键词 methods    方法名:GetValues   对象类型:Enum-->
        <ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type System:Enum}" x:Key="methods">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="h:AnaglyphMethod" />     <!-- 方法参数  类型:浮雕方法 AnaglyphMethod -->
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    <!-- 画刷brush1 -->
        <DrawingBrush x:Key="brush1" ViewportUnits="Absolute" Viewport="0,0,0.1,0.1" Viewbox="0,0,1,1" ViewboxUnits="Absolute">
            <DrawingBrush.Drawing>
                <DrawingGroup>
                    <GeometryDrawing Brush="White"> <!-- 白色  -->
                        <GeometryDrawing.Geometry>
                            <RectangleGeometry Rect="0,0,1,1"/>  <!--  -->
                        </GeometryDrawing.Geometry>
                    </GeometryDrawing>
                    <GeometryDrawing Brush="Black">  <!-- 黑色 -->
                        <GeometryDrawing.Geometry>
                            <RectangleGeometry Rect="0.25,0.25,0.5,0.5"/><!--  -->
                        </GeometryDrawing.Geometry>
                    </GeometryDrawing>
                </DrawingGroup>
            </DrawingBrush.Drawing>
        </DrawingBrush>
    <!-- 半潜式 -->
        <ModelVisual3D x:Key="SemiSubmersible">
      <!--桌面及其上面 共计4个box -->
            <h:BoxVisual3D Width="50" Height="4" Length="50" Center="0,0,12" Fill="White"/>
            <h:BoxVisual3D Width="6" Height="20" Length="6" Center="0,0,24" Fill="Yellow"/>
            <h:BoxVisual3D Width="10" Height="4" Length="30" Center="0,10,16" Fill="White"/>
            <h:BoxVisual3D Width="10" Height="4" Length="10" Center="10,10,20" Fill="White"/>
      <!-- 四个柱子 -->
            <h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="-20,-20,-10" Height="20" Fill="Green"/>
            <h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="-20,20,-10" Height="20" Fill="Green"/>
            <h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="20,-20,-10" Height="20" Fill="Green"/>
            <h:TruncatedConeVisual3D BaseRadius="5" TopRadius="5" BaseCap="True" Origin="20,20,-10" Height="20" Fill="Green"/>
      <!--  柱子之间4个连接杆 -->
            <h:BoxVisual3D Center="-20,0,-6" Height="4" Width="40" Length="4" Fill="Gray"/>
            <h:BoxVisual3D Center="20,0,-6" Height="4" Width="40" Length="4" Fill="Gray"/>
            <h:BoxVisual3D Center="0,-20,-6" Height="4" Width="4" Length="40" Fill="Gray"/>
            <h:BoxVisual3D Center="0,20,-6" Height="4" Width="4" Length="40" Fill="Gray"/>
            <h:GridLinesVisual3D Fill="White"/>
        </ModelVisual3D>
    </Window.Resources>
  <!-- 停靠面板:停靠顶部, 菜单项File  -->
    <DockPanel >
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">                
            </MenuItem>
        </Menu>
    <!-- 状态栏 -->
        <StatusBar DockPanel.Dock="Bottom">
            <Label Content="Model:" Margin="0 0 4 0"/>
      <!-- 下拉列表框对象:ModelItem   显示title -->
            <ComboBox Name="model" DisplayMemberPath="Title" Width="100" SelectionChanged="model_SelectionChanged">
                <local:ModelItem Title="Cubes"/>
                <local:ModelItem Title="Semi-submersible" Model="{StaticResource SemiSubmersible}"/>   <!-- 通过xaml设置下拉列表框第二项  -->
            </ComboBox>
            <Label Content="Method:" Margin="10 0 4 0"/>
      <!-- 下拉列表框  项源绑定:静态资源  methods-->
            <ComboBox Name="method" ItemsSource="{Binding Source={StaticResource methods}}" SelectedIndex="5" Width="100"/>
            <Label Content="Stereobase:" Margin="10 0 4 0"/>
      <!-- 滑动条  -->
            <Slider Name="stereoBase" Minimum="0" Maximum="5" Value="0.15" Width="100"/>
      <!-- 文本框绑定滑动条的值 -->
            <TextBlock Text="{Binding Value, ElementName=stereoBase, StringFormat={}{0:0.000}}" Padding="4 0 4 0"/>
        </StatusBar>
    <!-- 浮雕3D视图  StereoBase属性绑定 滑动条的值 -->
        <h:AnaglyphView3D x:Name="anaglyphView1" Background="Black" StereoBase="{Binding Value, ElementName=stereoBase}" Method="{Binding SelectedValue, ElementName=method}"
                          CopyDirectionVector="False"/>
    </DockPanel>
</Window>
<!-- 主窗体交互逻辑 MainWindow : Window
namespace AnaglyphDemo
{
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;


    using ExampleBrowser;


    using HelixToolkit.Wpf;


    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    [Example(null, "Showing a stereo view using the AnaglyphView3D control.")]
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
      //通过代码设置 下拉列表框 数据项第一项
            var cubes = new ModelVisual3D();
            AddCubes(cubes.Children, Brushes.White, 0);
            AddCubes(cubes.Children, Brushes.Green, -4);
            AddCubes(cubes.Children, Brushes.Green, 4);
            ((ModelItem)model.Items[0]).Model = cubes;


            Loaded += OnLoaded;
        }
    //添加立方体
        private void AddCubes(Visual3DCollection c, Brush brush, double x)
        {
            for (double y = -5; y <= 5; y += 10)
                c.Add(new CubeVisual3D { Fill = brush, Center = new Point3D(x, y, 0) });
        }
    //默认选择第一项model
        void OnLoaded(object sender, RoutedEventArgs e)
        {
            model.SelectedIndex = 0;
        }
    //下拉列表框改变
        private void model_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (anaglyphView1 == null || anaglyphView1.Children.Count == 0)//浮雕3D视图为null或者没有子对象
                return;
            var lights = anaglyphView1.Children[0];//获取光源
            anaglyphView1.Clear();//清空视图
            var m = model.SelectedItem as ModelItem;//获取模型数据项
            anaglyphView1.Children.Add(lights);//添加光源
            anaglyphView1.Children.Add(m.Model);//添加模型
            anaglyphView1.SynchronizeStereoModel();//同步立体模型
            // anaglyphView1.ZoomExtents();
        }
    }
  //获取  可见模型对象
    public class ModelItem
    {
        public string Title { get; set; } //标题
        public ModelVisual3D Model { get; set; }//3D可见模型
    }
}
 -->

2.  BackgroundUpdate 后台线程

<!--########### BackgroundUpdate  后台线程更新几何体 ###########-->
<Window x:Class="BackgroundUpdateDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:h="http://helix-toolkit.org/wpf"
        Title="Background worker update" Height="480" Width="640">
    <Grid>
    <!-- 3D场景:4个模型 -->
        <h:HelixViewport3D>
            <h:SunLight/>
            <ModelVisual3D x:Name="model1"/>
            <ModelVisual3D x:Name="model2"/>
            <ModelVisual3D x:Name="model3"/>
            <ModelVisual3D x:Name="model4"/>
        </h:HelixViewport3D>
    <!--堆栈面板: 4个复选框-4个布尔变量    添加点、添加冻结的几何体、添加冻结模型、添加到模型组 -->
        <StackPanel Margin="8">
            <CheckBox IsChecked="{Binding AddPoints}" Content="Points (blue)"/>
            <CheckBox IsChecked="{Binding AddFrozenGeometry}" Content="Geometry (red)"/>
            <CheckBox IsChecked="{Binding AddFrozenModel}" Content="Model (green)"/>
            <CheckBox IsChecked="{Binding AddToModelGroup}" Content="ModelGroup (gold)"/>
        </StackPanel>
    <!--  堆栈面板:4个文本框,绑定 4个后台线程执行次数-->
        <StackPanel Margin="8" VerticalAlignment="Bottom">
            <TextBlock Text="{Binding Count1, StringFormat='Points: {0}'}"/>
            <TextBlock Text="{Binding Count2, StringFormat='Geometry: {0}'}"/>
            <TextBlock Text="{Binding Count3, StringFormat='Model: {0}'}"/>
            <TextBlock Text="{Binding Count4, StringFormat='ModelGroup: {0}'}"/>
        </StackPanel>
    </Grid>
</Window>
<!--  主界面交互逻辑    MainWindow : Window, INotifyPropertyChanged
namespace BackgroundUpdateDemo
{
    using System;
    using System.Windows;
    using System.Windows.Media;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Media.Media3D;
    using System.Windows.Threading;


    using ExampleBrowser;


    using HelixToolkit.Wpf;


    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    [Example(null, "Updates the visual model in a background thread.")]
    public partial class MainWindow : Window, INotifyPropertyChanged
    {  //https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.cancellationtokensource?view=net-6.0
    //向System.Threading.CancellationToken发出应该取消的信号。
        private CancellationTokenSource source;//对象通过其 Token 属性提供取消令牌,并通过调用取消 Cancel 消息或 CancelAfter 方法发送取消消息。


        Material mat2 = MaterialHelper.CreateMaterial(Colors.Red);//红色材质


        int n = 10;//如果太卡,减小n
    //四种颜色,四个计数
        private int count1;


        private int count2;


        private int count3;


        private int count4;


        private int runningWorkers;//运行的后台数


        [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed. Suppression is OK here.")]
        public MainWindow()
        {
            this.InitializeComponent();
            this.DataContext = this;//更新数据绑定上下文
            this.Loaded += this.MainWindowLoaded;
            this.Closing += this.MainWindowClosing;
            this.AddPoints = true;//添加点  
            this.AddFrozenGeometry = true;//添加冻结的几何体
            this.AddFrozenModel = true;
            this.AddToModelGroup = true;
        }


        public bool AddPoints { get; set; }//是否添加点


        public bool AddFrozenGeometry { get; set; }//是否添加冻结几何体


        public bool AddFrozenModel { get; set; }//添加冻结模型


        public bool AddToModelGroup { get; set; }//添加到模型组


        public int Count1
        {
            get
            {
                return this.count1;
            }
            set
            {
                this.count1 = value;
                this.OnPropertyChanged("Count1");
            }
        }


        public int Count2
        {
            get
            {
                return this.count2;
            }
            set
            {
                this.count2 = value;
                this.OnPropertyChanged("Count2");
            }
        }


        public int Count3
        {
            get
            {
                return this.count3;
            }
            set
            {
                this.count3 = value;
                this.OnPropertyChanged("Count3");
            }
        }


        public int Count4
        {
            get
            {
                return this.count4;
            }
            set
            {
                this.count4 = value;
                this.OnPropertyChanged("Count4");
            }
        }
    //关闭窗口时,取消令牌。
        void MainWindowClosing(object sender, CancelEventArgs e)
        {
            this.source.Cancel();//沟通取消请求。
        }


        void MainWindowLoaded(object sender, RoutedEventArgs e)
        {
            Materials.Gold.Freeze();//使当前对象不可修改,并将其System.Windows.Freezable.IsFrozen属性设置为true


            this.source = new CancellationTokenSource();//初始化System.Threading.CancellationTokenSource。
      // this.Dispatcher获取System.Windows.Threading.DispatcherObjec所关联的System.Windows.Threading.Dispatcher。
            Task.Factory.StartNew(this.Worker1, this.Dispatcher, source.Token);
            Task.Factory.StartNew(this.Worker2, this.Dispatcher, source.Token);
            Task.Factory.StartNew(this.Worker3, this.Dispatcher, source.Token);
            Task.Factory.StartNew(this.Worker4, this.Dispatcher, source.Token);
        }
    //后台线程1:第2象限  蓝色    添加盒子到model1  nxnxn个
        private void Worker1(object d)
        {
            var dispatcher = (Dispatcher)d;
            Interlocked.Increment(ref this.runningWorkers);//为由多个线程共享的变量提供原子操作。增加指定变量并将结果作为原子操作存储。
            while (!this.source.IsCancellationRequested)
            {
                if (!this.AddPoints || this.runningWorkers < 4)
                {
                    Thread.Yield();//导致调用线程将执行让出给准备在当前处理器上运行的另一个线程。操作系统选择要屈从的线程。
                    continue; //4个后台线程都开始执行之后,才开始绘制
                }


                for (int i = 1; i <= n && this.AddPoints; i++)
                {
                    for (int j = 1; j <= n && this.AddPoints; j++)
                    {
                        for (int k = 1; k <= n && this.AddPoints; k++)
                        {  //调用add方法 添加盒子到model1。 Point3D(-i, j, k)
                            dispatcher.Invoke(new Action<Point3D, ModelVisual3D>(this.Add), new Point3D(-i, j, k), this.model1);
                        }
                    }
                }


                dispatcher.Invoke((Action)(() => this.Count1++));//后台线程1执行次数++
                dispatcher.Invoke(new Action<ModelVisual3D>(this.Clear), this.model1);//清空model1显示
            }
        }
    //添加红色盒子到model2    第1象限
        private void Worker2(object d)
        {
            var dispatcher = (Dispatcher)d;
            Interlocked.Increment(ref this.runningWorkers);
            while (!this.source.IsCancellationRequested)
            {
                if (!this.AddFrozenGeometry || this.runningWorkers < 4)
                {
                    Thread.Yield();
                    continue;
                }


                for (int i = 1; i <= n; i++)
                {
                    var b = new MeshBuilder();
                    for (int j = 1; j <= n; j++)
                    {
                        for (int k = 1; k <= n; k++)
                        {
                            b.AddBox(new Point3D(i, j, k), 0.8, 0.8, 0.8);//添加盒子 Point3D(i, j, k)
                        }
                    }


                    dispatcher.Invoke(new Action<MeshGeometry3D, Material, ModelVisual3D>(this.Add), b.ToMesh(true), mat2, model2);//添加所有新增盒子的网格面到model2
                }


                dispatcher.Invoke((Action)(() => this.Count2++));//线程2执行次数


                dispatcher.Invoke(new Action<ModelVisual3D>(this.Clear), this.model2);//清空模型2显示
            }
        }
    //后台线程3: 第5象限 绿色盒子
        private void Worker3(object d)
        {
            var dispatcher = (Dispatcher)d;
            var m = MaterialHelper.CreateMaterial(Colors.Green);//绿色
            Interlocked.Increment(ref this.runningWorkers);//运行后台线程数
            while (!this.source.IsCancellationRequested)
            {
                if (!this.AddFrozenModel || this.runningWorkers < 4)
                {
                    Thread.Yield();
                    continue;
                }


                for (int i = 1; i <= n; i++)
                {
                    var b = new MeshBuilder();
                    for (int j = 1; j <= n; j++)
                    {
                        for (int k = 1; k <= n; k++)
                        {
                            b.AddBox(new Point3D(i, j, -k), 0.8, 0.8, 0.8);//添加盒子Point3D(i, j, -k)
                        }
                    }


                    var box = new GeometryModel3D { Geometry = b.ToMesh(false), Material = m };
                    box.Freeze();//新建几何体,固定元素。。使当前对象不可修改,并将其System.Windows.Freezable.IsFrozen属性设置为true。


                    dispatcher.Invoke(new Action<Model3D, ModelVisual3D>(this.Add), box, this.model3);//添加box到model3并显示。
                }


                dispatcher.Invoke((Action)(() => this.Count3++));
                dispatcher.Invoke(new Action<ModelVisual3D>(this.Clear), model3);//清空model3,box没有消失?
            }
        }
    //后台线程4:第6象限
        private void Worker4(object d)
        {
            var dispatcher = (Dispatcher)d;
            var m = MaterialHelper.CreateMaterial(Colors.Gold);//金色材质固定
            Model3DGroup mg = null;
            dispatcher.Invoke(new Action(() => this.model4.Content = mg = new Model3DGroup()));//初始化model4模型组


            Interlocked.Increment(ref this.runningWorkers);


            while (!this.source.IsCancellationRequested)
            {
                if (!this.AddToModelGroup || this.runningWorkers < 4)
                {
                    Thread.Yield();
                    continue;
                }


                for (int i = 1; i <= n; i++)
                {
                    var b = new MeshBuilder();
                    for (int j = 1; j <= n; j++)
                    {
                        for (int k = 1; k <= n; k++)
                        {
                            b.AddBox(new Point3D(-i, j, -k), 0.8, 0.8, 0.8);//添加盒子Point3D(-i, j, -k)
                        }
                    }


                    var box = new GeometryModel3D { Geometry = b.ToMesh(false), Material = m };//金色材质
                    box.Freeze();//盒子固定
                    dispatcher.Invoke(new Action(() => mg.Children.Add(box)));//添加box到模型组model4
                }
                dispatcher.Invoke((Action)(() => this.Count4++));
                dispatcher.Invoke((Action)(() => mg.Children.Clear()));//清空model4
            }
        }
    //清空3D可见模型
        private void Clear(ModelVisual3D visual)
        {
            visual.Children.Clear();
        }
    //后台线程1添加盒子到 model1
        private void Add(Point3D p, ModelVisual3D visual)
        {
            // 在 UI 线程上创建可视化box
            var box = new BoxVisual3D { Center = p, Width = 0.8, Height = 0.8, Length = 0.8 };
            visual.Children.Add(box);
        }
    //后台线程2添加网格几何体到 model2
        private void Add(MeshGeometry3D g, Material m, ModelVisual3D visual)
        {
            var box = new GeometryModel3D { Geometry = g, Material = m };
            visual.Children.Add(new ModelVisual3D { Content = box });
        }
    //后台线程3添加model到model3
        private void Add(Model3D model, ModelVisual3D visual)
        {
            visual.Children.Add(new ModelVisual3D { Content = model });
        }
    //属性值改变
        public event PropertyChangedEventHandler PropertyChanged;


        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
 -->

3. Billboard  广告牌

<!--########### Billboard  广告牌 ###########-->
<Window x:Class="BillboardDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ht="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf" 
        Title="BillboardDemo" Height="480" Width="640">
    <Grid>
        <ht:HelixViewport3D ZoomExtentsWhenLoaded="True">
            <!-- 添加一些光源 -->
            <ht:SunLight/>
            
            <!-- 路 -->
            <ht:RectangleVisual3D Width="10" Length="60" Origin="0,30,0" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material Gray}"/>
            
            <!-- 黄色线条 -->
            <ht:RectangleVisual3D Width="0.1" Length="60" Origin="0.1,30,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material Yellow}"/>
            <ht:RectangleVisual3D Width="0.1" Length="60" Origin="-0.1,30,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material Yellow}"/>


            <!--通过复制/粘贴的斑马线 10条,宽0.5,长5,方向Y,法线z。白色。 A Zebra crossing by copy/paste :) -->
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="4.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="3.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="2.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="1.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="0.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="-0.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="-1.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="-2.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="-3.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>
            <ht:RectangleVisual3D Width="0.5" Length="5" Origin="-4.5,50,0.01" LengthDirection="0,1,0" Normal="0,0,1" Material="{ht:Material White}"/>


            <!--标志,看起来不太好,但显示了广告牌的工作原理  IsEmissive自发光,图片材质 Signs, not looking good, but shows how the billboards work -->
            <ht:BillboardVisual3D Position="6,10,3" Width="40" Height="40" Material="{ht:ImageMaterial 'pack://application:,,,/Examples/Billboard/roadsign.png', IsEmissive=true}"/>
            <ht:BillboardVisual3D Position="6,30,3" Width="40" Height="40" Material="{ht:ImageMaterial 'pack://application:,,,/Examples/Billboard/roadsign.png', IsEmissive=true}"/>
            <ht:BillboardVisual3D Position="6,50,3" Width="40" Height="40" Material="{ht:ImageMaterial 'pack://application:,,,/Examples/Billboard/roadsign.png', IsEmissive=true}"/>
 
            <!-- 标志杆Sign poles -->
            <ht:TruncatedConeVisual3D Origin="6,10,0" Height="3" BaseRadius="0.1" TopRadius="0.1" Material="{ht:Material LightGray}"/>
            <ht:TruncatedConeVisual3D Origin="6,30,0" Height="3" BaseRadius="0.1" TopRadius="0.1" Material="{ht:Material LightGray}"/>
            <ht:TruncatedConeVisual3D Origin="6,50,0" Height="3" BaseRadius="0.1" TopRadius="0.1" Material="{ht:Material LightGray}"/>


            <!--文字广告牌,始终面向相机 Text billboards, always facing camera-->
            <ht:BillboardTextVisual3D Position="5,0,0" Text="5,0,0" DepthOffset="0.01"/>
            <ht:BillboardTextVisual3D Position="-5,0,0" Text="-5,0,0" DepthOffset="0.01"/>
            <ht:BillboardTextVisual3D Position="5,60,0" Text="5,60,0" DepthOffset="0.01"/>
            <ht:BillboardTextVisual3D Position="-5,60,0" Text="-5,60,0" DepthOffset="0.01"/>


        </ht:HelixViewport3D>
    </Grid>
</Window>

4. BuildingDemo 建筑物

<!--########### BuildingDemo 建筑物 模型绑定界面设置  ###########-->
<Window x:Class="BuildingDemo.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:h="http://helix-toolkit.org/wpf"
        xmlns:buildingDemo="clr-namespace:BuildingDemo"
        xmlns:pt="http://propertytools.org/wpf"
        xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=System"
        Title="BuildingDemo" Height="720" Width="1080">
    <Window.Resources>      <!-- 窗体资源 -->
        <ImageBrush x:Key="SmokeBrush" ImageSource="smoke.png"/>    <!-- 图片刷子:烟雾图片 -->
        <ImageBrush x:Key="FenceBrush" ImageSource="fence.png" ViewportUnits="Absolute" TileMode="Tile"/>  <!-- 图片刷子: 围栏图片  -->
        <DrawingBrush x:Key="KerbBrush" ViewportUnits="Absolute" TileMode="Tile">  <!-- 路边刷子 -->
            <DrawingBrush.Drawing>
                <DrawingGroup>    <!-- 绘制组:黑色矩形,半个黄色矩形 -->
                    <GeometryDrawing Brush="Black">  <!-- 黑色,单位矩形 -->
                        <GeometryDrawing.Geometry>
                            <GeometryGroup>
                                <RectangleGeometry Rect="0,0,1,1" />
                            </GeometryGroup>
                        </GeometryDrawing.Geometry>
                    </GeometryDrawing>
                    <GeometryDrawing Brush="Yellow"> <!-- 黄色,半个矩形 -->
                        <GeometryDrawing.Geometry>
                            <GeometryGroup>
                                <RectangleGeometry Rect="0,0,0.5,1" />
                            </GeometryGroup>
                        </GeometryDrawing.Geometry>
                    </GeometryDrawing>
                </DrawingGroup>
            </DrawingBrush.Drawing>
        </DrawingBrush>
        <buildingDemo:BoundsConverter x:Key="BoundsConverter"></buildingDemo:BoundsConverter>  <!-- 边界转换器类对象:选定对象后显示边界框 -->
        <Point3DCollection x:Key="FencePositions">    <!-- 围栏位置 3d点集 -->
            <Point3D X="-22" Y="78" Z="0"/>
            <Point3D X="72" Y="78" Z="0"/>
            <Point3D X="72" Y="172" Z="0"/>
            <Point3D X="-22" Y="172" Z="0"/>
            <Point3D X="-22" Y="78" Z="0"/>
        </Point3DCollection>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>  <!-- 两列 -->
            <ColumnDefinition Width="1.61*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>
    <!-- 3D视图 -->
        <h:HelixViewport3D ZoomExtentsWhenLoaded="True" MouseDown="UIElement_OnMouseDown" Background="LightGray" IsHeadLightEnabled="True" ZoomAroundMouseDownPoint="True" RotateAroundMouseDownPoint="True">
            <ModelVisual3D>
        <!-- 厂房3D -->
                <buildingDemo:HouseVisual3D/>
                <buildingDemo:HouseVisual3D Transform="{h:Translate 40,0,0}"/>
                <buildingDemo:HouseVisual3D Transform="{h:Translate 0,20,0}"/>
                <buildingDemo:HouseVisual3D Transform="{h:Translate 40,20,0}"/>
        <!-- 默认筒仓3D -->
                <buildingDemo:SiloVisual3D Transform="{h:Translate 0,100,0}"/>
                <buildingDemo:SiloVisual3D Transform="{h:Translate 50,100,0}"/>
                <buildingDemo:SiloVisual3D Transform="{h:Translate 0,150,0}"/>
                <buildingDemo:SiloVisual3D Transform="{h:Translate 50,150,0}"/>
        <!-- 自定义筒仓 -->
                <buildingDemo:SiloVisual3D Transform="{h:Translate 100,125,0}" Height="40" Diameter="20" DomeDiameter="18" DomeHeight="3"/>
                <buildingDemo:SiloVisual3D Transform="{h:Translate 130,125,0}" Height="40" Diameter="20" DomeDiameter="18" DomeHeight="3"/>
                <buildingDemo:SiloVisual3D Transform="{h:Translate 160,125,0}" Height="40" Diameter="20" DomeDiameter="18" DomeHeight="3"/>
        <!-- 路边模型 -->
                <buildingDemo:KerbVisual3D Transform="{h:Translate 130,125,0}" Width="0.2" Height="0.1" Positions="{buildingDemo:Superellipse 45,15,40}" Texture="{StaticResource KerbBrush}"/>
        <!-- 烟囱3D -->
                <buildingDemo:ChimneyVisual3D x:Name="chimney1" Position="100,0,0"/>
                <buildingDemo:ChimneyVisual3D Position="120,0,0"/>
        <!-- 篱笆3D -->
                <buildingDemo:FenceVisual3D Height="2" Transform="{h:Translate 110,0,0}" Positions="{buildingDemo:Superellipse 20,20,3}" PoleTexture="Silver" FenceTexture="{StaticResource FenceBrush}"/>
                <buildingDemo:FenceVisual3D Height="2" Positions="{StaticResource FencePositions}" PoleTexture="Silver" FenceTexture="{StaticResource FenceBrush}"/>
            </ModelVisual3D>
      
            <ModelVisual3D><!-- 光照:散射光-白色  -->
                <ModelVisual3D.Content>
                    <AmbientLight Color="White"/>
                </ModelVisual3D.Content>
        <!-- 粒子系统:纹理,位置绑定chimney1的TopPosition -->
                <h:ParticleSystem Texture="{StaticResource SmokeBrush}" Position="{Binding TopPosition, ElementName=chimney1}"/>
            </ModelVisual3D>


      <!--  边界框3D :绑定选择对象, 边界框转换器  -->
            <h:BoundingBoxVisual3D BoundingBox="{Binding SelectedObject, Converter={StaticResource BoundsConverter}}" Diameter="0.2"/>
        </h:HelixViewport3D>
    <!-- 属性网格 1列,选择对象SelectedObject,可浏览属性BrowsableAttribute。  选择对象后右侧网格内显示属性。-->
        <pt:PropertyGrid Grid.Column="1" SelectedObject="{Binding SelectedObject}" TabVisibility="Collapsed" Margin="4" RequiredAttribute="{x:Type componentModel:BrowsableAttribute}"/>
    </Grid>
</Window>
<!--  主窗体交互逻辑: 单击对象,选择对象。
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media.Media3D;


    using ExampleBrowser;


    using HelixToolkit.Wpf;


    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    [Example("BuildingDemo", "Using MeshBuilder to create buildings.")]
    public partial class Window1 : Window
    {
        private ViewModel viewModel;


        public Window1()
        {
            this.InitializeComponent();
            this.DataContext = this.viewModel = new ViewModel();//数据上下文: 视图模型
        }


        private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            var viewport = (HelixViewport3D)sender;//获取3D视口
            var firstHit = viewport.Viewport.FindHits(e.GetPosition(viewport)).FirstOrDefault();//找到单击对象//
            if (firstHit != null)
            {
                this.viewModel.Select(firstHit.Visual);//选择对象的可见模型//
            }
            else
            {
                this.viewModel.Select(null);//选择null
            }
        }
    }
 -->
<!-- 边界转换:为指定的可视对象查找边界框。
    using System;
    using System.Globalization;
    using System.Windows.Data;
    using System.Windows.Media.Media3D;


    using HelixToolkit.Wpf;


    [ValueConversion(typeof(Visual3D), typeof(Rect3D))]
    public class BoundsConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var visual = value as Visual3D;
            return visual != null ? visual.FindBounds(Transform3D.Identity) : Rect3D.Empty;
        }


        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
-->
<!--烟囱3D:  注册依赖属性    烟囱3d继承自 UI元素3D,可点击。
  using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;


    using HelixToolkit.Wpf;


    using PropertyTools.DataAnnotations;//属性工具-数据注释
    using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;//可浏览属性


    public class ChimneyVisual3D : UIElement3D  //烟囱3d继承自 UI元素3D,可点击。
    {
        public static readonly DependencyProperty PositionProperty = DependencyPropertyEx.Register<Point3D, ChimneyVisual3D>("Position", new Point3D(0, 0, 0), (s, e) => s.AppearanceChanged()); //注册烟囱位置依赖属性 默认【0,0,0】
        public static readonly DependencyProperty TopPositionProperty = DependencyPropertyEx.Register<Point3D, ChimneyVisual3D>("TopPosition", new Point3D(0, 0, 0), (s, e) => s.AppearanceChanged());//注册顶部位置依赖属性 默认【0,0,0】
        public static readonly DependencyProperty BaseDiameterProperty = DependencyPropertyEx.Register<double, ChimneyVisual3D>("BaseDiameter", 6, (s, e) => s.AppearanceChanged());  //注册基座直径依赖属性 默认6
        public static readonly DependencyProperty TopDiameterProperty = DependencyPropertyEx.Register<double, ChimneyVisual3D>("TopDiameter", 3, (s, e) => s.AppearanceChanged());  //注册顶部直径依赖属性  默认3
        public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, ChimneyVisual3D>("Height", 80, (s, e) => s.AppearanceChanged());  //注册 高度依赖属性,默认80
        public static readonly DependencyProperty BandsProperty = DependencyPropertyEx.Register<int, ChimneyVisual3D>("Bands", 8, (s, e) => s.AppearanceChanged());  //注册  条纹带段数 依赖属性 默认8


        private readonly GeometryModel3D redbands = new GeometryModel3D();// 红色带 
        private readonly GeometryModel3D whitebands = new GeometryModel3D();//白色条带 


        public ChimneyVisual3D()
        {
            this.redbands.Material = MaterialHelper.CreateMaterial(Brushes.Red, ambient: 10);  //设置红色条带颜色材质--红色刷子
            this.whitebands.Material = MaterialHelper.CreateMaterial(Brushes.White, ambient: 10);//设置白色条带材质-白色刷子
            this.AppearanceChanged();//更新烟囱外观 
            var group = new Model3DGroup();//
            group.Children.Add(this.redbands);//红色带
            group.Children.Add(this.whitebands);//白色带
            this.Visual3DModel = group;//设置UIElement3D烟囱的可见模型为红色带+白色带
        }
    //位置属性: get依赖属性的值强制转换为Point3D; 设置依赖属性的值。
        public Point3D Position
        {
            get { return (Point3D)this.GetValue(PositionProperty); }
            set { this.SetValue(PositionProperty, value); }
        }
    //烟囱顶部位置属性
        public Point3D TopPosition
        {
            get { return (Point3D)this.GetValue(TopPositionProperty); }
            set { this.SetValue(TopPositionProperty, value); }
        }
    //烟囱基座直径属性        注释Chimney properties;可滑动[0-20];字符串格式[0.00];可浏览属性;
        [Category("Chimney properties")]
        [Slidable(0, 20)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double BaseDiameter
        {
            get { return (double)this.GetValue(BaseDiameterProperty); }
            set { this.SetValue(BaseDiameterProperty, value); }
        }
    //烟囱顶部直径属性:可滑动条设置0-20,可浏览属性。
        [Slidable(0, 20)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double TopDiameter
        {
            get { return (double)this.GetValue(TopDiameterProperty); }
            set { this.SetValue(TopDiameterProperty, value); }
        }
    //烟囱高度属性 ,可滑动0-100,可浏览。
        [Slidable(0, 100)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Height
        {
            get { return (double)this.GetValue(HeightProperty); }
            set { this.SetValue(HeightProperty, value); }
        }
    //烟囱条带属性:可滑动1-20,可浏览。 
        [Slidable(1, 20)]
        [FormatString("0.00")]
        [Browsable(true)]
        public int Bands
        {
            get { return (int)this.GetValue(BandsProperty); }
            set { this.SetValue(BandsProperty, value); }
        }
    //外观改变函数
        private void AppearanceChanged()
        {
            var redbuilder = new MeshBuilder(false, false); //红色条带网格构建器
            var whitebuilder = new MeshBuilder(false, false);//白色条带网格构建器
            for (int i = 0; i < this.Bands; i++)
            {
                var f0 = (double)i / this.Bands;//   i/8
                var f1 = (double)(i + 1) / this.Bands;//   (i+1)/8
                var y0 = this.Height * f0; //条带底部高度
                var y1 = this.Height * f1;//条带顶部高度
                var d0 = (this.BaseDiameter * (1 - f0)) + (this.TopDiameter * f0);//计算条带底部直径
                var d1 = (this.BaseDiameter * (1 - f1)) + (this.TopDiameter * f1);//计算条带顶部直径
                var builder = (this.Bands - i) % 2 == 1 ? redbuilder : whitebuilder;//交替创建红色和白色条带
                builder.AddCone(this.Position + new Vector3D(0, 0, y0), new Vector3D(0, 0, 1), d0 / 2, d1 / 2, y1 - y0, i == 0, i == this.Bands - 1, 20);//添加新的红色或者白色圆锥台
            }
      //更新烟囱顶部中心位置
            this.TopPosition = this.Position + new Vector3D(0, 0, this.Height);


            this.redbands.Geometry = redbuilder.ToMesh(true);//红色圆锥台组 网格化。
            this.whitebands.Geometry = whitebuilder.ToMesh(true);//设置白色条带集的几何体 : 白色圆锥台组网格化。
        }
    }
-->
<!--篱笆3D:  创建标杆和围栏网格。
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;


    using HelixToolkit.Wpf;


    using PropertyTools.DataAnnotations;//属性工具-数据注释
    using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;
  //继承自UI元素3D。 
    public class FenceVisual3D : UIElement3D
    {  // 标杆直径 注册依赖属性
        public static readonly DependencyProperty DiameterProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("Diameter", 0.05, (s, e) => s.AppearanceChanged());
    //标杆高度 注册依赖属性:调用外观改变函数
        public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("Height", 1, (s, e) => s.AppearanceChanged());
    //  围栏位置 注册依赖属性
        public static readonly DependencyProperty PositionsProperty = DependencyPropertyEx.Register<Point3DCollection, FenceVisual3D>("Positions", new Point3DCollection(), (s, e) => s.AppearanceChanged());
    //网格大小 注册依赖属性
        public static readonly DependencyProperty MeshSizeProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("MeshSize", 0.06, (s, e) => s.AppearanceChanged());
    //标杆间距 注册依赖属性
        public static readonly DependencyProperty PoleDistanceProperty = DependencyPropertyEx.Register<double, FenceVisual3D>("PoleDistance", 2, (s, e) => s.AppearanceChanged());
    //篱笆纹理 依赖属性
        public static readonly DependencyProperty FenceTextureProperty = DependencyPropertyEx.Register<Brush, FenceVisual3D>("FenceTexture", null, (s, e) => s.FenceTextureChanged());
    //标杆纹理属性
        public static readonly DependencyProperty PoleTextureProperty = DependencyPropertyEx.Register<Brush, FenceVisual3D>("PoleTexture", null, (s, e) => s.PoleTextureChanged());


        private readonly GeometryModel3D postsModel = new GeometryModel3D();//所有标杆几何体
        private readonly GeometryModel3D fenceModel = new GeometryModel3D();


        public FenceVisual3D()
        {
            this.AppearanceChanged();
            var group = new Model3DGroup();
            group.Children.Add(this.postsModel);//所有标杆几何体
            group.Children.Add(this.fenceModel);
            this.Visual3DModel = group;
        }
    //标杆直径属性,可浏览属性 0.01-2 
        [Category("Fence properties")]
        [Slidable(0.01, 0.2)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Diameter
        {
            get { return (double)this.GetValue(DiameterProperty); }
            set { this.SetValue(DiameterProperty, value); }
        }
    //高度属性   可浏览,0-2
        [Slidable(0, 2)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Height
        {
            get { return (double)this.GetValue(HeightProperty); }
            set { this.SetValue(HeightProperty, value); }
        }
    //围栏位置属性: 3D点集。  一个围栏可以由很多标杆。
        public Point3DCollection Positions
        {
            get { return (Point3DCollection)this.GetValue(PositionsProperty); }
            set { this.SetValue(PositionsProperty, value); }
        }
    //网格尺寸属性,0.01-2 ,可浏览
        [Slidable(0.01, 0.2)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double MeshSize
        {
            get { return (double)this.GetValue(MeshSizeProperty); }
            set { this.SetValue(MeshSizeProperty, value); }
        }
    //杆距离,0.2-10.可浏览
        [Slidable(0.2, 10)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double PoleDistance
        {
            get { return (double)this.GetValue(PoleDistanceProperty); }
            set { this.SetValue(PoleDistanceProperty, value); }
        }
    //篱笆纹理属性
        public Brush FenceTexture
        {
            get { return (Brush)this.GetValue(FenceTextureProperty); }
            set { this.SetValue(FenceTextureProperty, value); }
        }
    //标杆纹理属性
        public Brush PoleTexture
        {
            get { return (Brush)this.GetValue(PoleTextureProperty); }
            set { this.SetValue(PoleTextureProperty, value); }
        }
    //篱笆纹理改变   白色
        private void FenceTextureChanged()
        {
            this.fenceModel.Material = this.fenceModel.BackMaterial = this.FenceTexture != null ?
                new DiffuseMaterial(this.FenceTexture) { AmbientColor = Colors.White } : null;
        }
    //标杆纹理改变   
        private void PoleTextureChanged()
        {
            this.postsModel.Material = this.postsModel.BackMaterial = this.PoleTexture != null ? MaterialHelper.CreateMaterial(this.PoleTexture) : null;
        }
    //外观改变  
        private void AppearanceChanged()
        {
            var builder = new MeshBuilder(false, false);//标杆网格构建器
            foreach (var p1 in DistributePoles(this.Positions, this.PoleDistance))//遍历标杆位置
            {
                var p2 = p1 + new Vector3D(0, 0, this.Height);
                builder.AddCylinder(p1, p2, this.Diameter, 36);//绘制标杆
            }


            this.postsModel.Geometry = builder.ToMesh();//所有标杆几何体


            var fenceBuilder = new MeshBuilder(false, true);//围栏网格  构建器
            var w0 = 0d;
            for (int i = 0; i + 1 < this.Positions.Count; i++)//遍历围栏位置 
            {  //围栏网格 的 四个顶点
                var p0 = this.Positions[i];
                var p1 = this.Positions[i + 1];
                var p2 = this.Positions[i + 1] + new Vector3D(0, 0, this.Height);
                var p3 = this.Positions[i] + new Vector3D(0, 0, this.Height);
                var h = this.Height / this.MeshSize;//围栏网格的单元网格尺寸
                var dw = p0.DistanceTo(p1) / this.MeshSize;
                fenceBuilder.AddQuad(
                    p0,
                    p1,
                    p2,
                    p3,
                    new Point(w0, h),
                    new Point(w0 + dw, h),
                    new Point(w0 + dw, 0),
                    new Point(w0, 0));
                w0 += dw;
            }


            this.fenceModel.Geometry = fenceBuilder.ToMesh();
        }
    //离散标杆:得到标杆位置点集合
        private static IEnumerable<Point3D> DistributePoles(IList<Point3D> positions, double distance)
        {
            var l0 = 0d;
            var x = distance;
            for (int i = 0; i + 1 < positions.Count; i++)
            {
                var p0 = positions[i];
                var p1 = positions[i + 1];
                var d = p0.DistanceTo(p1);//围栏位置间距  
                while (x >= l0 && x < l0 + d)//离散化
                {
                    var f = (x - l0) / d;
                    yield return p0 + ((p1 - p0) * f);//计算 标杆位置
                    x += distance;
                }


                l0 += d;
                if (i + 1 == positions.Count)
                {
                    yield return p1;//最后一个标杆位置
                }
            }
        }
    }
 -->
<!-- 厂房3D:
    using System;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;


    using HelixToolkit.Wpf;


    using PropertyTools.DataAnnotations;
    using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;


    public class HouseVisual3D : UIElement3D
    {  //依赖属性:房顶角度 默认15
        public static readonly DependencyProperty RoofAngleProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("RoofAngle", 15, (s, e) => s.AppearanceChanged());
    //依赖属性:房顶厚度 默认0.2
        public static readonly DependencyProperty RoofThicknessProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("RoofThickness", 0.2, (s, e) => s.AppearanceChanged());
    //依赖属性:地面厚度 默认0.2
        public static readonly DependencyProperty FloorThicknessProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("FloorThickness", 0.2, (s, e) => s.AppearanceChanged());
    //依赖属性:仓库高度  默认2.5
        public static readonly DependencyProperty StoryHeightProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("StoryHeight", 2.5, (s, e) => s.AppearanceChanged());
    //依赖属性:宽度 默认10
        public static readonly DependencyProperty WidthProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("Width", 10, (s, e) => s.AppearanceChanged());
    //依赖属性:长度  默认20
        public static readonly DependencyProperty LengthProperty = DependencyPropertyEx.Register<double, HouseVisual3D>("Length", 20, (s, e) => s.AppearanceChanged());
    //依赖属性:楼层属性:默认1 
        public static readonly DependencyProperty StoriesProperty = DependencyPropertyEx.Register<int, HouseVisual3D>("Stories", 1, (s, e) => s.AppearanceChanged());
    //依赖属性:爆炸屋顶属性,默认false
        public static readonly DependencyProperty ExplodedRoofProperty = DependencyPropertyEx.Register<bool, HouseVisual3D>("ExplodedRoof", false, (s, e) => s.ExplodedRoofChanged());


        private readonly GeometryModel3D roof = new GeometryModel3D();//房顶几何模型
        private readonly GeometryModel3D walls = new GeometryModel3D();//墙几何模型


        public HouseVisual3D()  
        {
            this.roof.Material = MaterialHelper.CreateMaterial(Brushes.Brown, ambient: 10);//房顶材质-棕色
            this.walls.Material = MaterialHelper.CreateMaterial(Brushes.White, ambient: 10);//墙材质 --白色
            this.AppearanceChanged();//更新外观
            var model = new Model3DGroup();
            model.Children.Add(this.roof);//房顶 
            model.Children.Add(this.walls);//墙
            this.Visual3DModel = model;//设置可见模型
        }
    //厂房宽度属性
        [Category("House properties")]
        [Slidable(0, 60)]
        [Browsable(true)]
        public double Width
        {
            get { return (double)this.GetValue(WidthProperty); }
            set { this.SetValue(WidthProperty, value); }
        }
    //厂房长度属性
        [Slidable(0, 60)]
        [Browsable(true)]
        public double Length
        {
            get { return (double)this.GetValue(LengthProperty); }
            set { this.SetValue(LengthProperty, value); }
        }
    //楼层高度
        [Slidable(0, 4)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double StoryHeight
        {
            get { return (double)this.GetValue(StoryHeightProperty); }
            set { this.SetValue(StoryHeightProperty, value); }
        }
    //楼层数
        [Slidable(1, 8)]
        [Browsable(true)]
        public int Stories
        {
            get { return (int)this.GetValue(StoriesProperty); }
            set { this.SetValue(StoriesProperty, value); }
        }
    //房顶角度 
        [Slidable(0, 60)]
        [Browsable(true)]
        public double RoofAngle
        {
            get { return (double)this.GetValue(RoofAngleProperty); }
            set { this.SetValue(RoofAngleProperty, value); }
        }
    //房顶厚度
        [Slidable(0, 2)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double RoofThickness
        {
            get { return (double)this.GetValue(RoofThicknessProperty); }
            set { this.SetValue(RoofThicknessProperty, value); }
        }
    //地面厚度 
        [Slidable(0, 1)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double FloorThickness
        {
            get { return (double)this.GetValue(FloorThicknessProperty); }
            set { this.SetValue(FloorThicknessProperty, value); }
        }
    //爆炸房顶
        [Browsable(true)]
        public bool ExplodedRoof
        {
            get { return (bool)this.GetValue(ExplodedRoofProperty); }
            set { this.SetValue(ExplodedRoofProperty, value); }
        }
    //爆炸房顶改变:
        private void ExplodedRoofChanged()
        {
            if (ExplodedRoof)
                roof.Transform = new TranslateTransform3D(0, 0, 30);//房顶沿Z轴平移30. 提高房顶。
            else
                roof.Transform = Transform3D.Identity;//房顶位置不变
        }
    //外观改变  
        private void AppearanceChanged()
        {
            var y0 = 0d;
            var wallBuilder = new MeshBuilder(false, false);//墙构建器
            for (int i = 0; i < this.Stories; i++)//遍历楼层
            {
                if (i > 0 && this.FloorThickness > 0)//楼层索引>0 且  地面厚度>0
                {  //盒子参数:中心位置,长,宽,高
                    wallBuilder.AddBox(new Point3D(0, 0, y0 + this.FloorThickness / 2), this.Length + 0.2, this.Width + 0.2, this.FloorThickness);//添加盒子 
                    y0 += this.FloorThickness;//更新高度
                }


                wallBuilder.AddBox(new Point3D(0, 0, y0 + this.StoryHeight / 2), this.Length, this.Width, this.StoryHeight);//绘制墙体盒子
                y0 += this.StoryHeight;//更新高度
            }
       
            var theta = Math.PI / 180 * this.RoofAngle;//房顶倾角-弧度
            var roofBuilder = new MeshBuilder(false, false);
            var y1 = y0 + Math.Tan(theta) * this.Width / 2;
            var p0 = new Point(0, y1);
            var p1 = new Point(this.Width / 2 + 0.2 * Math.Cos(theta), y0 - 0.2 * Math.Sin(theta));
            var p2 = new Point(p1.X + this.RoofThickness * Math.Sin(theta), p1.Y + this.RoofThickness * Math.Cos(theta));
            var p3 = new Point(0, y1 + this.RoofThickness / Math.Cos(theta));
            var p4 = new Point(-p2.X, p2.Y);
            var p5 = new Point(-p1.X, p1.Y);
            var roofSection = new[] { p0, p1, p1, p2, p2, p3, p3, p4, p4, p5, p5, p0 };//房顶的六个3D线段
            roofBuilder.AddExtrudedSegments(roofSection, new Vector3D(0, -1, 0), new Point3D(-this.Length / 2, 0, 0), new Point3D(this.Length / 2, 0, 0));//添加指定3d线段集合的挤出面。  
      //绘制房顶棱线
            var cap = new[] { p0, p1, p2, p3, p4, p5 };
            roofBuilder.AddPolygon(cap, new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), new Point3D(-this.Length / 2, 0, 0));
            roofBuilder.AddPolygon(cap, new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), new Point3D(this.Length / 2, 0, 0));
            var p6 = new Point(this.Width / 2, y0);
            var p7 = new Point(-this.Width / 2, y0);
      //两三角形
            wallBuilder.AddPolygon(new[] { p0, p6, p7 }, new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), new Point3D(-this.Length / 2, 0, 0));
            wallBuilder.AddPolygon(new[] { p0, p6, p7 }, new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), new Point3D(this.Length / 2, 0, 0));
            this.walls.Geometry = wallBuilder.ToMesh(true);
            this.roof.Geometry = roofBuilder.ToMesh(true);
        }
    }
 -->
<!--  路边3d:
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;


    using HelixToolkit.Wpf;


    using PropertyTools.DataAnnotations;
    using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;


    public class KerbVisual3D : UIElement3D
    {  //宽度0.2,高度0.1,位置点集,长度1,纹理。 注册依赖属性,更新外观。
        public static readonly DependencyProperty WidthProperty = DependencyPropertyEx.Register<double, KerbVisual3D>("Width", 0.2, (s, e) => s.AppearanceChanged());
        public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, KerbVisual3D>("Height", 0.1, (s, e) => s.AppearanceChanged());
        public static readonly DependencyProperty PositionsProperty = DependencyPropertyEx.Register<Point3DCollection, KerbVisual3D>("Positions", new Point3DCollection(), (s, e) => s.AppearanceChanged());
        public static readonly DependencyProperty LengthProperty = DependencyPropertyEx.Register<double, KerbVisual3D>("Length", 1, (s, e) => s.AppearanceChanged());
        public static readonly DependencyProperty TextureProperty = DependencyPropertyEx.Register<Brush, KerbVisual3D>("Texture", null, (s, e) => s.TextureChanged());


        private readonly GeometryModel3D kerbModel = new GeometryModel3D();//路边几何模型


        public KerbVisual3D()
        {
            this.AppearanceChanged();//更新外观
            this.Visual3DModel = this.kerbModel;//设置几何模型
        }
    //宽度
        [Category("Kerb properties")]
        [Slidable(0.1, 1)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Width
        {
            get { return (double)this.GetValue(WidthProperty); }
            set { this.SetValue(WidthProperty, value); }
        }
    //高度
        [Slidable(0, 2)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Height
        {
            get { return (double)this.GetValue(HeightProperty); }
            set { this.SetValue(HeightProperty, value); }
        }
    //位置点集
        public Point3DCollection Positions
        {
            get { return (Point3DCollection)this.GetValue(PositionsProperty); }
            set { this.SetValue(PositionsProperty, value); }
        }
    //长度
        [Slidable(0.1, 10)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Length
        {
            get { return (double)this.GetValue(LengthProperty); }
            set { this.SetValue(LengthProperty, value); }
        }
    //纹理
        public Brush Texture
        {
            get { return (Brush)this.GetValue(TextureProperty); }
            set { this.SetValue(TextureProperty, value); }
        }
    //路边材质和背面材质
        private void TextureChanged()
        {
            this.kerbModel.Material = this.Texture != null ? new DiffuseMaterial(this.Texture) : null;
            this.kerbModel.BackMaterial = this.Texture != null ? Materials.Gray : null;
        }
    //更新外观
        private void AppearanceChanged()
        {
            var builder = new MeshBuilder(false, true);


            //硬编码路边部分 hard code a kerb section
            var section = new PointCollection();
            int m = 41;//修改m,n 改变路边尺寸
            double n = 4;//
            double a = this.Width / 2;
            double b = this.Height;
            for (int i = 0; i < m; i++)
            {
                double t = Math.PI * i / (m - 1);
                section.Add(new Point(
                    a * Math.Sign(Math.Cos(t)) * Math.Pow(Math.Abs(Math.Cos(t)), 2 / n),
                    -b * Math.Sign(Math.Sin(t)) * Math.Pow(Math.Abs(Math.Sin(t)), 2 / n)));
            }


            //计算纹理坐标 calculate the texture coordinates
            var values = new List<double> { 0 };
            for (int i = 1; i < this.Positions.Count; i++)
            {
                var d = this.Positions[i - 1].DistanceTo(this.Positions[i]);
                values.Add(values[values.Count - 1] + (d / this.Length));
            }


            // create the extruded geometry创建拉伸几何体
            builder.AddTube(this.Positions, null, values, null, section, new Vector3D(1, 0, 0), false, false);


            this.kerbModel.Geometry = builder.ToMesh();//设置几何体
        }
    }
-->
<!-- 筒仓3D:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;


    using HelixToolkit.Wpf;


    using PropertyTools.DataAnnotations;
    using BrowsableAttribute = System.ComponentModel.BrowsableAttribute;


    public class SiloVisual3D : UIElement3D
    {  //直径属性,高度20,圆顶高度,圆顶直径。
        public static readonly DependencyProperty DiameterProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("Diameter", 40, (s, e) => s.AppearanceChanged());
        public static readonly DependencyProperty HeightProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("Height", 20, (s, e) => s.AppearanceChanged());
        public static readonly DependencyProperty DomeHeightProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("DomeHeight", 8, (s, e) => s.AppearanceChanged());
        public static readonly DependencyProperty DomeDiameterProperty = DependencyPropertyEx.Register<double, SiloVisual3D>("DomeDiameter", 38, (s, e) => s.AppearanceChanged());


        private readonly GeometryModel3D walls = new GeometryModel3D();//围墙
        private readonly GeometryModel3D railing = new GeometryModel3D();//栏杆
        private readonly GeometryModel3D stairs = new GeometryModel3D();//楼梯


        public SiloVisual3D()
        {
            this.walls.Material = MaterialHelper.CreateMaterial(Brushes.White, ambient: 10);
            this.walls.BackMaterial = MaterialHelper.CreateMaterial(Brushes.Green);
            this.railing.Material = MaterialHelper.CreateMaterial(Brushes.Silver, ambient: 10);
            this.stairs.Material = MaterialHelper.CreateMaterial(Brushes.Brown, ambient: 10);
            this.AppearanceChanged();
            var group = new Model3DGroup();
            group.Children.Add(this.walls);
            group.Children.Add(this.railing);
            group.Children.Add(this.stairs);
            this.Visual3DModel = group;
        }
    //直径
        [Category("Silo/tank properties")]
        [Slidable(0, 100)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Diameter
        {
            get { return (double)this.GetValue(DiameterProperty); }
            set { this.SetValue(DiameterProperty, value); }
        }
    //高度
        [Slidable(0, 100)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double Height
        {
            get { return (double)this.GetValue(HeightProperty); }
            set { this.SetValue(HeightProperty, value); }
        }
    //圆顶高度
        [Slidable(0, 20)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double DomeHeight
        {
            get { return (double)this.GetValue(DomeHeightProperty); }
            set { this.SetValue(DomeHeightProperty, value); }
        }
    //圆顶直径
        [Slidable(0, 50)]
        [FormatString("0.00")]
        [Browsable(true)]
        public double DomeDiameter
        {
            get { return (double)this.GetValue(DomeDiameterProperty); }
            set { this.SetValue(DomeDiameterProperty, value); }
        }
    //更新外观
        private void AppearanceChanged()
        {
            var builder = new MeshBuilder(false, false);
            var p0 = new Point(0, 0);
            var p1 = new Point(this.Diameter / 2, 0);
            var p2 = new Point(this.Diameter / 2, this.Height);
            var p3 = new Point(this.DomeDiameter / 2, this.Height);


            var section = new List<Point> { p0, p1, p1, p2, p2, p3 };
            var sectionIndices = new List<int> { 0, 1, 2, 3, 4, 5 };
            int n = 40;
            for (int i = n; i >= 0; i--)
            {
                double x = (double)i / n;
                double y = x * x;
                if (i < n)
                {
                    sectionIndices.Add(section.Count - 1);
                    sectionIndices.Add(section.Count);
                }


                section.Add(new Point(x * this.DomeDiameter / 2, this.Height + (this.DomeHeight * (1 - y))));
            }


            builder.AddSurfaceOfRevolution(new Point3D(0, 0, 0), new Vector3D(0, 0, 1), section, sectionIndices, 80);//围墙
            this.walls.Geometry = builder.ToMesh(true);


            var treadDepth = 0.3;
            var riseHeight = 0.15;
            var thickness = 0.05;
            var width = 1;
            var steps = (int)(this.Height / riseHeight);
            var r = (this.Diameter * 0.5) + (width * 0.5);
            var rp = (this.Diameter * 0.5) + (width * 0.95);
            var stairBuilder = new MeshBuilder(false, false);
            var railBases = new List<Point3D>();
            for (int i = 0; i < steps; i++)
            {
                var theta = treadDepth * i / r;
                var p = new Point3D(Math.Cos(theta) * r, Math.Sin(theta) * r, (riseHeight * i) + (thickness / 2));
                var x = new Vector3D(Math.Cos(theta), Math.Sin(theta), 0);
                var z = new Vector3D(0, 0, 1);
                var y = Vector3D.CrossProduct(z, x);
                stairBuilder.AddBox(p, x, y, width, treadDepth, thickness);//楼梯
                railBases.Add(new Point3D(Math.Cos(theta) * rp, Math.Sin(theta) * rp, (riseHeight * i) + thickness));//楼梯围栏
            }


            var lastTheta = treadDepth * steps / r;


            // Railing along stairs
            var railingHeight = 0.8;
            var railingDiameter = 0.05;
            var railings = 3;
            var railingBuilder = new MeshBuilder(false, false);


            // 顶部栏杆Top railing
            var railingPostDistance = 0.5;
            int topRailingPosts = (int)(this.Diameter * Math.PI / railingPostDistance);
            var tr = (this.Diameter / 2) - railingDiameter;
            for (int i = 0; i < topRailingPosts; i++)
            {
                var theta = lastTheta + (2 * Math.PI * i / topRailingPosts);
                railBases.Add(new Point3D(Math.Cos(theta) * tr, Math.Sin(theta) * tr, this.Height));
            }


            BuildRailing(railingBuilder, railBases, railingHeight, railingDiameter, railings);


            this.stairs.Geometry = stairBuilder.ToMesh();
            this.railing.Geometry = railingBuilder.ToMesh();
        }
    //构建栏杆
        private static void BuildRailing(
            MeshBuilder railingBuilder,
            List<Point3D> bases,
            double height,
            double diameter,
            int railings)
        {
            foreach (var point in bases)
            {
                railingBuilder.AddCylinder(point, point + new Vector3D(0, 0, height), diameter, 10);
            }


            for (int i = 1; i <= railings; i++)
            {
                var h = height * i / railings;
                var path = bases.Select(p => p + new Vector3D(0, 0, h)).ToArray();
                railingBuilder.AddTube(path, diameter, 10, false);
            }
        }
    }
 -->
<!-- 超椭圆球  为 XAML 标记扩展提供基类。
    using System;
    using System.Windows.Markup;
    using System.Windows.Media.Media3D;


    public class Superellipse : MarkupExtension
    {
        private readonly double a;


        private readonly double b;


        private readonly double n;


        public Superellipse(double a, double b, double n)
        {
            this.a = a;
            this.b = b;
            this.n = n;
        }


        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var c = new Point3DCollection();
            int m = 400;
            for (int i = 0; i + 1 < m; i++)
            {
                double t = 2 * Math.PI * i / (m - 2);
                c.Add(new Point3D(
                    this.a * Math.Sign(Math.Cos(t)) * Math.Pow(Math.Abs(Math.Cos(t)), 2 / this.n),
                    this.b * Math.Sign(Math.Sin(t)) * Math.Pow(Math.Abs(Math.Sin(t)), 2 / this.n),
                    0));
            }


            c.Add(c[0]);
            return c;
        }
    }
 -->
<!--  视图模型:可观察
   using System.Windows.Media.Media3D;


    using PropertyTools;//属性工具


    public class ViewModel : Observable
    {
        private object selectedObject;//选择的对象
    // 获取或设置选择的对象
        public object SelectedObject
        {
            get
            {
                return this.selectedObject;
            }


            set
            {
                this.SetValue(ref this.selectedObject, value, nameof(this.SelectedObject));
            }
        }
    //选择对象
        public void Select(Visual3D visual)
        {
            this.SelectedObject = visual;
        }
    }
-->
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值