2009-10-16 186 views
2

如何使用WPF的3D組件或使用僞3D效果創建「碗」效果,其中用戶正在俯視碗並可以拖動矩形並且矩形的透視變化,以便它看起來像他們在碗上下移動?我沒有任何重力效應或任何東西后,只是當項目移動,我需要他們的視角進行調整...WPF和3D創建碗效果

編輯:我一直在尋找到WPF中可用的實際3D效果,看起來非常非常強大的,所以也許有人可以幫助在我的應用程序上獲得半球,然後將一些3D網格物體(矩形)放到其表面上?

有什麼想法?

謝謝, 馬克

回答

3

啊沒錯,你在這裏去那麼 - 你可以拖着紅色的矩形圍繞碗 - 現在享受!

<Window x:Class="wpfbowl.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Title="Window1" Height="500" Width="500" 
    DataContext="{Binding RelativeSource={RelativeSource Self}}" MouseDown="Window_MouseDown" MouseMove="Window_MouseMove" MouseUp="Window_MouseUp" > 
<Window.Resources> 
    <Transform3DGroup x:Key="WorldTrans"> 

     <RotateTransform3D> 
      <RotateTransform3D.Rotation> 
       <AxisAngleRotation3D x:Name="myAngleRotation" Axis="0,0,1" Angle="{Binding RotationLeftRight}" /> 

      </RotateTransform3D.Rotation> 

     </RotateTransform3D> 
     <RotateTransform3D> 
      <RotateTransform3D.Rotation> 
       <AxisAngleRotation3D x:Name="myAngleRotation2" Axis="1,0,0" Angle="{Binding RotationUpDown}" /> 

      </RotateTransform3D.Rotation> 

     </RotateTransform3D> 
    </Transform3DGroup> 
</Window.Resources> 
    <StackPanel> 
    <Viewport3D Name="mainViewport" ClipToBounds="True" HorizontalAlignment="Stretch" Margin="0" Height="500" > 
     <Viewport3D.Camera> 
      <PerspectiveCamera 
      LookDirection="0,5,0" 
      UpDirection="0,0,1" 
      Position="0,-10,0" 
      /> 
     </Viewport3D.Camera> 

     <ModelVisual3D > 
     <ModelVisual3D> 
      <ModelVisual3D.Content> 

       <Model3DGroup> 

        <PointLight Position="0,-10,0" Range="150" Color="White" /> 


        </Model3DGroup> 

      </ModelVisual3D.Content> 
     </ModelVisual3D> 
    </ModelVisual3D> 


    <ModelVisual3D Transform="{StaticResource WorldTrans}"> 
     <ModelVisual3D Content="{Binding Models}"> 

     </ModelVisual3D> 
    </ModelVisual3D> 
     <ModelVisual3D > 
      <ModelVisual3D Content="{Binding BowlModel}"> 

      </ModelVisual3D> 
     </ModelVisual3D> 

    </Viewport3D> 
    </StackPanel> 
</Window> 

和後面的代碼...

using System; 
using System.ComponentModel; 
using System.Timers; 
using System.Windows.Media; 
using System.Windows.Media.Media3D; 
using System.Windows.Threading; 
using System.Windows; 
using System.Windows.Input; 

namespace wpfbowl 
{ 
/// <summary> 
/// Interaction logic for Window1.xaml 
/// </summary> 
public partial class Window1 : INotifyPropertyChanged 
{ 
    public Window1() 
    { 
     InitModels(); 
     InitializeComponent(); 

    } 

    private Model3DGroup _cube; 
    private bool _cubeSelected; 
    private bool _cubeMoving; 
    private Point3D _startPoint; 
    private Point3D _currentPoint; 

    public void InitModels() 
    { 
     const int bowlQuality = 20; 
     Models = new Model3DGroup(); 
     BowlModel = new Model3DGroup(); 

     _cube = GetCube(GetSurfaceMaterial(Colors.Red), new Point3D(0, 2.6, 0), new Size3D(1.5, 0.2, 2)); 
     Models.Children.Add(_cube); 

     var bowl = CreateBowl(new Point3D(0, 0, 0), 3, bowlQuality, bowlQuality, GetSurfaceMaterial(Colors.Green)); 
     BowlModel.Children.Add(bowl); 
    } 


    private readonly Timer _timer; 

    public Model3DGroup Models { get; set; } 

    public Model3DGroup BowlModel { get; set; } 

    private double _rotationLeftRight; 
    public double RotationLeftRight 
    { 
     get { return _rotationLeftRight; } 
     set 
     { 
      if (_rotationLeftRight == value) return; 
      _rotationLeftRight = value; 
      OnPropertyChanged("RotationLeftRight"); 
     } 
    } 

    private double _rotationUpDown; 
    public double RotationUpDown 
    { 
     get { return _rotationUpDown; } 
     set 
     { 
      if (_rotationUpDown == value) return; 
      _rotationUpDown = value; 
      OnPropertyChanged("RotationUpDown"); 
     } 
    } 


    public static Model3DGroup CreateBowl(Point3D center, double radius, int u, int v, MaterialGroup materialGroup) 
    { 
     var bowl = new Model3DGroup(); 
     if (u < 2 || v < 2) return null; 
     var pts = new Point3D[u, v]; 
     for (var i = 0; i < u; i++) 
     { 
      for (var j = 0; j < v; j++) 
      { 
       pts[i, j] = GetPosition(radius, i * 180/(u - 1), j * 360/(v - 1)); 
       pts[i, j] += (Vector3D)center; 
      } 
     } 

     var p = new Point3D[4]; 
     for (var i = 0; i < (u /2) - 1; i++) 
     { 
      for (var j = 0; j < v - 1; j++) 
      { 
       p[0] = pts[i, j]; 
       p[1] = pts[i + 1, j]; 
       p[2] = pts[i + 1, j + 1]; 
       p[3] = pts[i, j + 1]; 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[0], p[1], p[2])); 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[2], p[1], p[0])); 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[2], p[3], p[0])); 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[0], p[3], p[2])); 
      } 
     } 
     return bowl; 
    } 


    private static Model3DGroup CreateTriangleModel(Material material, Point3D p0, Point3D p1, Point3D p2) 
    { 
     var mesh = new MeshGeometry3D(); 
     mesh.Positions.Add(p0); 
     mesh.Positions.Add(p1); 
     mesh.Positions.Add(p2); 
     mesh.TriangleIndices.Add(0); 
     mesh.TriangleIndices.Add(1); 
     mesh.TriangleIndices.Add(2); 
     var normal = CalculateNormal(p0, p1, p2); 
     mesh.Normals.Add(normal); 
     mesh.Normals.Add(normal); 
     mesh.Normals.Add(normal); 

     var model = new GeometryModel3D(mesh, material); 

     var group = new Model3DGroup(); 
     group.Children.Add(model); 
     return group; 
    } 

    private static Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2) 
    { 
     var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z); 
     var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z); 
     return Vector3D.CrossProduct(v0, v1); 
    } 

    private static Point3D GetPosition(double radius, double theta, double phi) 
    { 
     var pt = new Point3D(); 
     var snt = Math.Sin(theta * Math.PI/180); 
     var cnt = Math.Cos(theta * Math.PI/180); 
     var snp = Math.Sin(phi * Math.PI/180); 
     var cnp = Math.Cos(phi * Math.PI/180); 
     pt.X = radius * snt * cnp; 
     pt.Y = radius * cnt; 
     pt.Z = -radius * snt * snp; 
     return pt; 
    } 

    public static MaterialGroup GetSurfaceMaterial(Color colour) 
    { 
     var materialGroup = new MaterialGroup(); 
     var emmMat = new EmissiveMaterial(new SolidColorBrush(colour)); 
     materialGroup.Children.Add(emmMat); 
     materialGroup.Children.Add(new DiffuseMaterial(new SolidColorBrush(colour))); 
     var specMat = new SpecularMaterial(new SolidColorBrush(Colors.White), 30); 
     materialGroup.Children.Add(specMat); 
     return materialGroup; 
    } 

    public static Model3DGroup GetCube(MaterialGroup materialGroup, Point3D point, Size3D size) 
    { 
     var farPoint = new Point3D(point.X - (size.X/2), point.Y - (size.Y/2), point.Z - (size.Z/2)); 
     var nearPoint = new Point3D(point.X + (size.X/2), point.Y + (size.Y/2), point.Z + (size.Z/2)); 
     var cube = new Model3DGroup(); 

     var p0 = new Point3D(farPoint.X, farPoint.Y, farPoint.Z); 
     var p1 = new Point3D(nearPoint.X, farPoint.Y, farPoint.Z); 
     var p2 = new Point3D(nearPoint.X, farPoint.Y, nearPoint.Z); 
     var p3 = new Point3D(farPoint.X, farPoint.Y, nearPoint.Z); 
     var p4 = new Point3D(farPoint.X, nearPoint.Y, farPoint.Z); 
     var p5 = new Point3D(nearPoint.X, nearPoint.Y, farPoint.Z); 
     var p6 = new Point3D(nearPoint.X, nearPoint.Y, nearPoint.Z); 
     var p7 = new Point3D(farPoint.X, nearPoint.Y, nearPoint.Z); 
     //front side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p3, p2, p6)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p3, p6, p7)); 
     //right side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p1, p5)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p5, p6)); 
     //back side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p1, p0, p4)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p1, p4, p5)); 
     //left side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p0, p3, p7)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p0, p7, p4)); 
     //top side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p7, p6, p5)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p7, p5, p4)); 
     //bottom side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p3, p0)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p0, p1)); 
     return cube; 
    } 


    #region INotifyPropertyChanged Members 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string name) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 

    #endregion 

    private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 

      var mousePos = e.GetPosition(mainViewport); 
      var hitParams = new PointHitTestParameters(mousePos); 
      VisualTreeHelper.HitTest(mainViewport, null, ResultCallback, hitParams); 


    } 

    public HitTestResultBehavior ResultCallback(HitTestResult result) 
    { 
     // Did we hit 3D? 
     var rayResult = result as RayHitTestResult; 
     if (rayResult != null) 
     { 
      // Did we hit a MeshGeometry3D? 
      var rayMeshResult = rayResult as RayMeshGeometry3DHitTestResult; 

      if (rayMeshResult != null) 
      { 
       if (_cubeSelected) 
       { 
        _cubeMoving = true; 
        _currentPoint = rayMeshResult.PointHit; 
        RotationLeftRight = (_startPoint.X - _currentPoint.X) * 15; 
        RotationUpDown = (_currentPoint.Z -_startPoint.Z)*15; 
       } 
       else 
       { 
        var model = rayMeshResult.ModelHit; 
        foreach (var c in _cube.Children) 
        { 
         if (c.GetType() != typeof(Model3DGroup)) continue; 
         var model3DGroup = (Model3DGroup)c; 
         foreach (var sc in model3DGroup.Children) 
         { 
          if (model != sc) continue; 

          _cubeSelected = true; 
          _startPoint = rayMeshResult.PointHit; 

         } 
        } 
       } 

      } 
     } 
     return HitTestResultBehavior.Continue; 
    } 

    private void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) 
    { 
     if (!_cubeSelected) return; 
     var mousePos = e.GetPosition(mainViewport); 
     var hitParams = new PointHitTestParameters(mousePos); 
     VisualTreeHelper.HitTest(mainViewport, null, ResultCallback, hitParams); 
    } 

    private void Window_MouseUp(object sender, MouseButtonEventArgs e) 
    { 
     if (!_cubeSelected) return; 
     _cubeSelected = false; 
     _cubeMoving = false; 
    } 

} 
} 
+1

這是驚人的,我不能感謝你夠了,認真... – Mark 2009-10-20 23:38:12

+1

沒有後顧之憂,我幻想了挑戰! :) – 2009-10-21 07:14:16

1

在這裏,你走了,我不太清楚你的意思有關,所以我剛剛添加圍繞綠色碗的開幕四個紅色長方形矩形什麼。

乾杯,

安迪

的XAML第一...

<Window x:Class="wpfbowl.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
Title="Window1" Height="500" Width="500" 
    DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
<Window.Resources> 
    <Transform3DGroup x:Key="WorldTrans"> 

     <RotateTransform3D> 
      <RotateTransform3D.Rotation> 
       <AxisAngleRotation3D x:Name="myAngleRotation" Axis="0,0,1" Angle="{Binding Rotation}" /> 
      </RotateTransform3D.Rotation> 
     </RotateTransform3D> 
    </Transform3DGroup> 
</Window.Resources> 
    <StackPanel> 
    <Viewport3D Name="mainViewport" ClipToBounds="True" HorizontalAlignment="Stretch" Margin="0" Height="500" > 
     <Viewport3D.Camera> 
      <PerspectiveCamera 
      LookDirection="0,5,0" 
      UpDirection="0,0,1" 
      Position="0,-10,0" 
      /> 
     </Viewport3D.Camera> 

     <ModelVisual3D > 
     <ModelVisual3D> 
      <ModelVisual3D.Content> 

       <Model3DGroup> 

        <PointLight Position="0,-10,0" Range="150" Color="White" /> 


        </Model3DGroup> 

      </ModelVisual3D.Content> 
     </ModelVisual3D> 
    </ModelVisual3D> 


    <ModelVisual3D Transform="{StaticResource WorldTrans}"> 
     <ModelVisual3D Content="{Binding Models}"> 

     </ModelVisual3D> 
    </ModelVisual3D> 

</Viewport3D> 
    </StackPanel> 
</Window> 

...和繼承人後面的代碼...

using System; 
using System.ComponentModel; 
using System.Timers; 
using System.Windows.Media; 
using System.Windows.Media.Media3D; 
using System.Windows.Threading; 

namespace wpfbowl 
{ 
/// <summary> 
/// Interaction logic for Window1.xaml 
/// </summary> 
public partial class Window1 : INotifyPropertyChanged 
{ 
    public Window1() 
    { 
     InitModels(); 
     InitializeComponent(); 

     _timer = new Timer(100); 
     _timer.Elapsed += TimerElapsed; 
     _timer.Enabled = true; 
    } 

    void TimerElapsed(object sender, ElapsedEventArgs e) 
    { 
     Dispatcher.Invoke(DispatcherPriority.Normal, new Action<double>(Transform), 2); 
    } 

    private void Transform(double value) 
    { 
     Rotation += value; 
    } 

    public void InitModels() 
    { 
     const int bowlQuality = 20; 
     Models = new Model3DGroup(); 
     var sphere = CreateBowl(new Point3D(0, 0, 0), 3, bowlQuality, bowlQuality, GetSurfaceMaterial(Colors.Green)); 

     Models.Children.Add(GetCube(GetSurfaceMaterial(Colors.Red), new Point3D(3, 0, 0), new Size3D(1.5, 0.2, 2))); 
     Models.Children.Add(GetCube(GetSurfaceMaterial(Colors.Red), new Point3D(-3, 0, 0), new Size3D(1.5, 0.2, 2))); 
     Models.Children.Add(GetCube(GetSurfaceMaterial(Colors.Red), new Point3D(0, 0, 3), new Size3D(1.5, 0.2, 2))); 
     Models.Children.Add(GetCube(GetSurfaceMaterial(Colors.Red), new Point3D(0, 0, -3), new Size3D(1.5, 0.2, 2))); 
     Models.Children.Add(sphere); 
    } 


    private readonly Timer _timer; 

    public Model3DGroup Models { get; set; } 
    private double _rotation; 
    public double Rotation 
    { 
     get { return _rotation; } 
     set 
     { 
      if (_rotation == value) return; 
      _rotation = value; 
      OnPropertyChanged("Rotation"); 
     } 
    } 

    public static Model3DGroup CreateBowl(Point3D center, double radius, int u, int v, MaterialGroup materialGroup) 
    { 
     var bowl = new Model3DGroup(); 
     if (u < 2 || v < 2) return null; 
     var pts = new Point3D[u, v]; 
     for (var i = 0; i < u; i++) 
     { 
      for (var j = 0; j < v; j++) 
      { 
       pts[i, j] = GetPosition(radius, i * 180/(u - 1), j * 360/(v - 1)); 
       pts[i, j] += (Vector3D)center; 
      } 
     } 

     var p = new Point3D[4]; 
     for (var i = 0; i < (u /2) - 1; i++) 
     { 
      for (var j = 0; j < v - 1; j++) 
      { 
       p[0] = pts[i, j]; 
       p[1] = pts[i + 1, j]; 
       p[2] = pts[i + 1, j + 1]; 
       p[3] = pts[i, j + 1]; 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[0], p[1], p[2])); 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[2], p[1], p[0])); 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[2], p[3], p[0])); 
       bowl.Children.Add(CreateTriangleModel(materialGroup, p[0], p[3], p[2])); 
      } 
     } 
     return bowl; 
    } 


    private static Model3DGroup CreateTriangleModel(Material material, Point3D p0, Point3D p1, Point3D p2) 
    { 
     var mesh = new MeshGeometry3D(); 
     mesh.Positions.Add(p0); 
     mesh.Positions.Add(p1); 
     mesh.Positions.Add(p2); 
     mesh.TriangleIndices.Add(0); 
     mesh.TriangleIndices.Add(1); 
     mesh.TriangleIndices.Add(2); 
     var normal = CalculateNormal(p0, p1, p2); 
     mesh.Normals.Add(normal); 
     mesh.Normals.Add(normal); 
     mesh.Normals.Add(normal); 

     var model = new GeometryModel3D(mesh, material); 

     var group = new Model3DGroup(); 
     group.Children.Add(model); 
     return group; 
    } 

    private static Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2) 
    { 
     var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z); 
     var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z); 
     return Vector3D.CrossProduct(v0, v1); 
    } 

    private static Point3D GetPosition(double radius, double theta, double phi) 
    { 
     var pt = new Point3D(); 
     var snt = Math.Sin(theta * Math.PI/180); 
     var cnt = Math.Cos(theta * Math.PI/180); 
     var snp = Math.Sin(phi * Math.PI/180); 
     var cnp = Math.Cos(phi * Math.PI/180); 
     pt.X = radius * snt * cnp; 
     pt.Y = radius * cnt; 
     pt.Z = -radius * snt * snp; 
     return pt; 
    } 

    public static MaterialGroup GetSurfaceMaterial(Color colour) 
    { 
     var materialGroup = new MaterialGroup(); 
     var emmMat = new EmissiveMaterial(new SolidColorBrush(colour)); 
     materialGroup.Children.Add(emmMat); 
     materialGroup.Children.Add(new DiffuseMaterial(new SolidColorBrush(colour))); 
     var specMat = new SpecularMaterial(new SolidColorBrush(Colors.White), 30); 
     materialGroup.Children.Add(specMat); 
     return materialGroup; 
    } 

    public static Model3DGroup GetCube(MaterialGroup materialGroup, Point3D point, Size3D size) 
    { 
     var farPoint = new Point3D(point.X - (size.X/2), point.Y - (size.Y/2), point.Z - (size.Z/2)); 
     var nearPoint = new Point3D(point.X + (size.X/2), point.Y + (size.Y/2), point.Z + (size.Z/2)); 
     var cube = new Model3DGroup(); 

     var p0 = new Point3D(farPoint.X, farPoint.Y, farPoint.Z); 
     var p1 = new Point3D(nearPoint.X, farPoint.Y, farPoint.Z); 
     var p2 = new Point3D(nearPoint.X, farPoint.Y, nearPoint.Z); 
     var p3 = new Point3D(farPoint.X, farPoint.Y, nearPoint.Z); 
     var p4 = new Point3D(farPoint.X, nearPoint.Y, farPoint.Z); 
     var p5 = new Point3D(nearPoint.X, nearPoint.Y, farPoint.Z); 
     var p6 = new Point3D(nearPoint.X, nearPoint.Y, nearPoint.Z); 
     var p7 = new Point3D(farPoint.X, nearPoint.Y, nearPoint.Z); 
     //front side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p3, p2, p6)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p3, p6, p7)); 
     //right side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p1, p5)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p5, p6)); 
     //back side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p1, p0, p4)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p1, p4, p5)); 
     //left side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p0, p3, p7)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p0, p7, p4)); 
     //top side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p7, p6, p5)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p7, p5, p4)); 
     //bottom side triangles 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p3, p0)); 
     cube.Children.Add(CreateTriangleModel(materialGroup, p2, p0, p1)); 
     return cube; 
    } 


    #region INotifyPropertyChanged Members 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string name) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 

    #endregion 

} 
} 
+0

哇!這真是太神奇了,非常感謝Andy。我真的很感謝你爲此付出的細節和努力。矩形的意思是我想能夠將它們「拖」到碗的內部,想象一下,如果你的水果碗裏有一張名片,然後把手指放在它的上面,然後將它拖到裏面這是我所指的效果...再次感謝:) – Mark 2009-10-20 00:31:13