WPF 3D模型上的2D元素 Viewport3D

需求

需要将UI元素布置到3D模型上,还能实现对UI元素的操作。

环境

实现

UI元素模板

控件模型UserControlTemplate.xaml

<UserControl x:Class="Melphi.UserControlTemplate"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Melphi"
             mc:Ignorable="d" 
             d:DesignHeight="200" d:DesignWidth="200">
    <Viewbox>
        <Grid Width="200" Height="200">
            

            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <Grid>
                    <Rectangle Width="1" Margin="10 0">
                        <Rectangle.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="#FFF10707" Offset="0"/>
                                <GradientStop Color="Gainsboro" Offset="1"/>
                            </RadialGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Button Click="PlayClicked" Content="A" VerticalAlignment="Bottom"/>
                </Grid>

                <Grid>
                    <Rectangle Width="1" Margin="10 0">
                        <Rectangle.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="#FFF17C07" Offset="0"/>
                                <GradientStop Color="Gainsboro" Offset="1"/>
                            </RadialGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Button Click="PlayClicked" Content="B" VerticalAlignment="Bottom"/>
                </Grid>

                <Grid>
                    <Rectangle Width="1" Margin="10 0">
                        <Rectangle.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="#FFF1F107" Offset="0"/>
                                <GradientStop Color="Gainsboro" Offset="1"/>
                            </RadialGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Button Click="PlayClicked" Content="C" VerticalAlignment="Bottom"/>
                </Grid>

                <Grid>
                    <Rectangle Width="1" Margin="10 0">
                        <Rectangle.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="#FF27F107" Offset="0"/>
                                <GradientStop Color="Gainsboro" Offset="1"/>
                            </RadialGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Button Click="PlayClicked" Content="D" VerticalAlignment="Bottom"/>
                </Grid>
                <Grid>
                    <Rectangle Width="1" Margin="10 0">
                        <Rectangle.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="#FF07F1E6" Offset="0"/>
                                <GradientStop Color="Gainsboro" Offset="1"/>
                            </RadialGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Button Click="PlayClicked" Content="E" VerticalAlignment="Bottom"/>
                </Grid>

                <Grid>
                    <Rectangle Width="1" Margin="10 0">
                        <Rectangle.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="#FF073CF1" Offset="0"/>
                                <GradientStop Color="Gainsboro" Offset="1"/>
                            </RadialGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Button Click="PlayClicked" Content="F" VerticalAlignment="Bottom"/>
                </Grid>
                <Grid>
                    <Rectangle Width="1" Margin="10 0">
                        <Rectangle.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="#FFD107F1" Offset="0"/>
                                <GradientStop Color="Gainsboro" Offset="1"/>
                            </RadialGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Button Click="PlayClicked" Content="G" VerticalAlignment="Bottom"/>
                </Grid>

            </StackPanel>

            <TextBlock  x:Name="txtblkSing" HorizontalAlignment="Center" VerticalAlignment="Center">
                <TextBlock.Background>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="#FFF55A02" Offset="0"/>
                        <GradientStop Color="#FFC90BF3" Offset="1"/>
                    </LinearGradientBrush>
                </TextBlock.Background>
            </TextBlock>
        </Grid>
    </Viewbox>
   
</UserControl>

场景控制球

鼠标操作 Trackball.cs

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Media3D;

namespace Melphi
{
    /// <summary>
    /// 鼠标跟踪球
    /// </summary>
    /// <typeparam name="TChild">Viewport3D 的首个变换模型的结构类型</typeparam>
    public class Trackball<TChild> where TChild : Visual3D
    {
        private Vector3D _center;
        private bool _centered;
        // The state of the trackball
        private bool _enabled;
        private Point _point; // Initial point of drag
        private bool _rotating;
        private Quaternion _rotation;
        private Quaternion _rotationDelta; // Change to rotation because of this drag
        private double _scale;
        private double _scaleDelta; // Change to scale because of this drag
        // The state of the current drag
        private bool _scaling;
        private List<Viewport3D> _slaves;
        private Vector3D _translate;
        private Vector3D _translateDelta;

        public Trackball()
        {
            Reset();
        }

        public List<Viewport3D> Slaves
        {
            get { return _slaves ?? (_slaves = new List<Viewport3D>()); }
            set { _slaves = value; }
        }

        public bool Enabled
        {
            get { return _enabled && (_slaves != null) && (_slaves.Count > 0); }
            set { _enabled = value; }
        }

        public void Attach(FrameworkElement element)
        {
            element.MouseMove += MouseMoveHandler;
            element.MouseRightButtonDown += MouseDownHandler;
            element.MouseRightButtonUp += MouseUpHandler;
            element.MouseWheel += OnMouseWheel;
        }

        public void Detach(FrameworkElement element)
        {
            element.MouseMove -= MouseMoveHandler;
            element.MouseRightButtonDown -= MouseDownHandler;
            element.MouseRightButtonUp -= MouseUpHandler;
            element.MouseWheel -= OnMouseWheel;
        }

        // Updates the matrices of the slaves using the rotation quaternion.
        private void UpdateSlaves(Quaternion q, double s, Vector3D t)
        {
            if (_slaves != null)
            {
                foreach (var i in _slaves)
                {
                    var mv = i.Children[0] as TChild;
                    var t3Dg = mv.Transform as Transform3DGroup;
                    var groupScaleTransform = t3Dg.Children[0] as ScaleTransform3D;
                    var groupRotateTransform = t3Dg.Children[1] as RotateTransform3D;
                    var groupTranslateTransform = t3Dg.Children[2] as TranslateTransform3D;
                    groupScaleTransform.ScaleX = s;
                    groupScaleTransform.ScaleY = s;
                    groupScaleTransform.ScaleZ = s;
                    groupRotateTransform.Rotation = new AxisAngleRotation3D(q.Axis, q.Angle);
                    groupTranslateTransform.OffsetX = t.X;
                    groupTranslateTransform.OffsetY = t.Y;
                    groupTranslateTransform.OffsetZ = t.Z;
                }
            }
        }

        private void MouseMoveHandler(object sender, MouseEventArgs e)
        {
            if (!Enabled) return;
            e.Handled = true;

            var el = (UIElement)sender;

            if (el.IsMouseCaptured)
            {
                var delta = _point - e.MouseDevice.GetPosition(el);
                delta /= 2;
                var q = _rotation;

                if (_rotating)
                {
                    // We can redefine this 2D mouse delta as a 3D mouse delta
                    // where "into the screen" is Z
                    var mouse = new Vector3D(delta.X, -delta.Y, 0);
                    var axis = Vector3D.CrossProduct(mouse, new Vector3D(0, 0, 1));
                    var len = axis.Length;
                    if (len < 0.00001 || _scaling)
                        _rotationDelta = new Quaternion(new Vector3D(0, 0, 1), 0);
                    else
                        _rotationDelta = new Quaternion(axis, len);

                    q = _rotationDelta * _rotation;
                }
                else
                {
                    delta /= 20;
                    _translateDelta.X = delta.X * -1;
                    _translateDelta.Y = delta.Y;
                }

                var t = _translate + _translateDelta;

                UpdateSlaves(q, _scale * _scaleDelta, t);
            }
        }

        private void MouseDownHandler(object sender, MouseButtonEventArgs e)
        {
            if (!Enabled) return;
            e.Handled = true;


            if (Keyboard.IsKeyDown(Key.F1))
            {
                Reset();
                return;
            }

            var el = (UIElement)sender;
            _point = e.MouseDevice.GetPosition(el);
            // Initialize the center of rotation to the lookatpoint
            if (!_centered)
            {
                var camera = (ProjectionCamera)_slaves[0].Camera;
                _center = camera.LookDirection;
                _centered = true;
            }

            _scaling = (e.MiddleButton == MouseButtonState.Pressed);

            _rotating = Keyboard.IsKeyDown(Key.Space) == false;

            el.CaptureMouse();
        }

        private void MouseUpHandler(object sender, MouseButtonEventArgs e)
        {
            if (!_enabled) return;
            e.Handled = true;

            // Stuff the current initial + delta into initial so when we next move we
            // start at the right place.
            if (_rotating)
                _rotation = _rotationDelta * _rotation;
            else
            {
                _translate += _translateDelta;
                _translateDelta.X = 0;
                _translateDelta.Y = 0;
            }

            //_scale = _scaleDelta*_scale;
            var el = (UIElement)sender;
            el.ReleaseMouseCapture();
        }

        private void OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            e.Handled = true;

            _scaleDelta += e.Delta / (double)1000;
            var q = _rotation;

            UpdateSlaves(q, _scale * _scaleDelta, _translate);
        }

        private void MouseDoubleClickHandler(object sender, MouseButtonEventArgs e)
        {
            Reset();
        }

        private void Reset()
        {
            _rotation = new Quaternion(0, 0, 0, 1);
            _scale = 1;
            _translate.X = 0;
            _translate.Y = 0;
            _translate.Z = 0;
            _translateDelta.X = 0;
            _translateDelta.Y = 0;
            _translateDelta.Z = 0;

            // Clear delta too, because if reset is called because of a double click then the mouse
            // up handler will also be called and this way it won't do anything.
            _rotationDelta = Quaternion.Identity;
            _scaleDelta = 1;
            UpdateSlaves(_rotation, _scale, _translate);
        }
    }
}

Viewport2DVisual3D

3D模型UserControlViewport2DVisual3D.xaml

<UserControl x:Class="Melphi.UserControlViewport2DVisual3D"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Melphi"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Viewport3D x:Name="viewport3DScan" ClipToBounds="True">
            <Viewport3D.Camera>
                <PerspectiveCamera Position="0,0,3.0" LookDirection="0,0,-1" UpDirection="0,1,0" NearPlaneDistance="0.25" FarPlaneDistance="20" FieldOfView="60"/>
            </Viewport3D.Camera>

            <Viewport3D.Children>
                <Viewport2DVisual3D>
                    <!-- 模型图形 -->
                    <Viewport2DVisual3D.Geometry>
                        <MeshGeometry3D Positions="-1 -1 0  1 -1 0  -1 1 0  1 1 0"
                                        Normals="0 0 1  0 0 1  0 0 1  0 0 1"
                                        TextureCoordinates="0 1  1 1  0 0  1 0   "
                                        TriangleIndices="0 1 2  1 3 2" />
                    </Viewport2DVisual3D.Geometry>

                    <!-- 变换 -->
                    <Viewport2DVisual3D.Transform>
                        <Transform3DGroup>
                            <Transform3DGroup.Children>
                                <Transform3DCollection>
                                    <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1"/>

                                    <RotateTransform3D>
                                        <RotateTransform3D.Rotation>
                                            <AxisAngleRotation3D Axis="0 1 0" Angle="0"/>
                                        </RotateTransform3D.Rotation>
                                    </RotateTransform3D>

                                    <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0"/>
                                </Transform3DCollection>
                            </Transform3DGroup.Children>
                        </Transform3DGroup>
                    </Viewport2DVisual3D.Transform>

                    <!-- 材质 -->
                    <Viewport2DVisual3D.Material>
                        <DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" Brush="Red"/>
                    </Viewport2DVisual3D.Material>
                    
                    <!-- 附着元素 -->
                    <local:UserControlTemplate/>

                </Viewport2DVisual3D>

                
                <ModelVisual3D>
                    <ModelVisual3D.Content>
                        <!--  灯光 -->
                        <DirectionalLight/>
                    </ModelVisual3D.Content>
                </ModelVisual3D>
                
            </Viewport3D.Children>
            
        </Viewport3D>
    </Grid>
</UserControl>

3D模型UserControlViewport2DVisual3D.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Media3D;

namespace Melphi
{
    /// <summary>
    /// UserControlViewport2DVisual3D.xaml 的交互逻辑
    /// </summary>
    public partial class UserControlViewport2DVisual3D : UserControl
    {
        
        Trackball<Viewport2DVisual3D> mTrackball;

        public UserControlViewport2DVisual3D()
        {
            InitializeComponent();

            Loaded += UserControlViewport2DVisual3D_Loaded;
        }

        private void UserControlViewport2DVisual3D_Loaded(object sender, RoutedEventArgs e)
        {
            mTrackball = new Trackball<Viewport2DVisual3D>();
            mTrackball.Attach(this);
            mTrackball.Slaves.Add(viewport3DScan);
            mTrackball.Enabled = true;
        }

    }
}

主窗口 MianWindow.xaml

<Window x:Class="Melphi.MainWindow"
        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:Melphi"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="800">
    <Grid>
        <TabControl>
            <TabItem Header="Viewport2DVisual3D">
                <local:UserControlViewport2DVisual3D/>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

效果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ModelVisual3D

3D模型UserControlModelVisual3D.xaml

<UserControl x:Class="Melphi.UserControlModelVisual3D"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Melphi"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="Black">
        <Viewport3D x:Name="viewport3DScan" ClipToBounds="True">
            <Viewport3D.Camera>
                <PerspectiveCamera Position="0,0,3.0" LookDirection="0,0,-1" UpDirection="0,1,0" NearPlaneDistance="0.25" FarPlaneDistance="20" FieldOfView="60"/>
            </Viewport3D.Camera>

            <Viewport3D.Children>

                <ModelVisual3D>

                    <ModelVisual3D.Transform>
                        <Transform3DGroup>
                            <Transform3DGroup.Children>
                                <Transform3DCollection>

                                    <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1" />
                                    <RotateTransform3D>
                                        <RotateTransform3D.Rotation>
                                            <AxisAngleRotation3D Axis="0 1 0" Angle="0" />
                                        </RotateTransform3D.Rotation>
                                    </RotateTransform3D>
                                    <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" />

                                </Transform3DCollection>
                            </Transform3DGroup.Children>
                        </Transform3DGroup>
                    </ModelVisual3D.Transform>

                    <ModelVisual3D.Content>

                        <Model3DGroup>
                            <Model3DGroup.Transform>
                                <Transform3DGroup>
                                    <Transform3DGroup.Children>
                                        <Transform3DCollection>

                                            <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1" />

                                            <RotateTransform3D>
                                                <RotateTransform3D.Rotation>
                                                    <AxisAngleRotation3D  Axis="0 1 0" Angle="0" />
                                                </RotateTransform3D.Rotation>
                                            </RotateTransform3D>

                                            <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" />

                                        </Transform3DCollection>
                                    </Transform3DGroup.Children>
                                </Transform3DGroup>
                            </Model3DGroup.Transform>

                            <Model3DGroup.Children>

                                <Model3DGroup>
                                    
                                    <Model3DGroup.Transform>
                                        <Transform3DGroup>
                                            <Transform3DGroup.Children>
                                                <Transform3DCollection>

                                                    <ScaleTransform3D ScaleX="1" ScaleY="1" ScaleZ="1" />

                                                    <RotateTransform3D>
                                                        <RotateTransform3D.Rotation>
                                                            <AxisAngleRotation3D  Axis="1 0 0" Angle="0" />
                                                        </RotateTransform3D.Rotation>
                                                    </RotateTransform3D>

                                                    <TranslateTransform3D OffsetX="0" OffsetY="0" OffsetZ="0" />

                                                </Transform3DCollection>
                                            </Transform3DGroup.Children>
                                        </Transform3DGroup>
                                    </Model3DGroup.Transform>
                                    
                                    <Model3DGroup.Children>

                                        <GeometryModel3D >
                                            <GeometryModel3D.Geometry>
                                                <MeshGeometry3D Positions="-1 -1 0  1 -1 0  -1 1 0  1 1 0"
                                        Normals="0 0 1  0 0 1  0 0 1  0 0 1"
                                        TextureCoordinates="0 1  1 1  0 0  1 0   "
                                        TriangleIndices="0 1 2  1 3 2" />
                                            </GeometryModel3D.Geometry>
                                            <GeometryModel3D.Material>
                                                <MaterialGroup>
                                                    <MaterialGroup.Children>
                                                        <EmissiveMaterial>
                                                            <EmissiveMaterial.Brush>
                                                                <VisualBrush>
                                                                    <VisualBrush.Visual>
                                                                        <local:UserControlTemplate/>
                                                                    </VisualBrush.Visual>
                                                                </VisualBrush>
                                                            </EmissiveMaterial.Brush>
                                                        </EmissiveMaterial>

                                                    </MaterialGroup.Children>
                                                </MaterialGroup>
                                            </GeometryModel3D.Material>

                                            <GeometryModel3D.BackMaterial>
                                                <MaterialGroup>
                                                    <MaterialGroup.Children>
                                                        <EmissiveMaterial>
                                                            <EmissiveMaterial.Brush>
                                                                <SolidColorBrush Color="Red"/>
                                                            </EmissiveMaterial.Brush>
                                                        </EmissiveMaterial>

                                                    </MaterialGroup.Children>
                                                </MaterialGroup>
                                            </GeometryModel3D.BackMaterial>

                                        </GeometryModel3D>
                                    </Model3DGroup.Children>
                                </Model3DGroup>

                            </Model3DGroup.Children>
                        </Model3DGroup>

                    </ModelVisual3D.Content>
                </ModelVisual3D>

                <!--<ModelVisual3D>
                    <ModelVisual3D.Content>
                           --><!--灯光--><!--  
                        <DirectionalLight Color="White"/>
                    </ModelVisual3D.Content>
                </ModelVisual3D>-->

            </Viewport3D.Children>

        </Viewport3D>
    </Grid>
</UserControl>

3D模型 UserControlModelVisual3D.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Media3D;

namespace Melphi
{
    /// <summary>
    /// UserControlGeometryModel3D.xaml 的交互逻辑
    /// </summary>
    public partial class UserControlModelVisual3D : UserControl
    {
        Trackball<ModelVisual3D> mTrackball;

        public UserControlModelVisual3D()
        {
            InitializeComponent();
            Loaded += UserControlModelVisual3D_Loaded; ;
        }

        private void UserControlModelVisual3D_Loaded(object sender, RoutedEventArgs e)
        {
            mTrackball = new Trackball<ModelVisual3D>();
            mTrackball.Attach(this);
            mTrackball.Slaves.Add(viewport3DScan);
            mTrackball.Enabled = true;
        }
    }
}

主窗口 MianWindow.xaml

<Window x:Class="Melphi.MainWindow"
        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:Melphi"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="800">
    <Grid>
        <TabControl>
            <TabItem Header="Viewport2DVisual3D">
                <local:UserControlViewport2DVisual3D/>
            </TabItem>
            <TabItem Header="ModelVisual3D">
                <local:UserControlModelVisual3D/>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

效果

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

总结

Viewport2DVisual3D元素能将UI元素附着到3D模型上,模型上的元素能正常操作与控制。但是ModelVisual3D上的材质元素不能正常操作与控制。


Over

每次记录一小步…点点滴滴人生路…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值