2009-10-16 7 views
2

Comment utiliser les composants 3D de WPF ou utiliser un effet pseudo-3D pour créer un effet "Bowl", où l'utilisateur regarde vers le bas sur un bol et peut traîner rectangles et ont la perspective de rectangles changer de sorte qu'il semble qu'ils montent, descendent et autour du bol? Je ne suis pas après tout effet de gravité ou quoi que ce soit, juste quand les objets bougent, j'ai besoin que leur point de vue soit ajusté ...WPF et 3D pour créer un effet de bol

EDIT: J'ai regardé les effets 3D réels disponibles dans WPF qui semblent très très puissant, alors peut-être que quelqu'un pourrait aider à obtenir une demi-sphère sur mon application et ensuite bing quelques mailles 3D (rectangles) à sa surface?

Des pensées?

Merci, Mark

Répondre

3

droit Ahh, ici vous allez - vous pouvez maintenant faire glisser le rectangle rouge autour du bol - profitez-en!

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

et le code derrière ...

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

C'est incroyable, je ne peux pas vous remercier assez, sérieusement ... – Mark

+1

pas de soucis, je croyais le défi! :) –

1

Ici, vous allez, je n'étais pas tout à fait sûr de ce que vous vouliez dire sur les rectangles donc je viens d'ajouter quatre rectangles rouges autour de l'ouverture d'un bol vert.

Cheers,

Andy

Xaml premier ...

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

... et voici le code derrière ...

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

Woah! C'est incroyable, merci beaucoup Andy. J'apprécie vraiment les détails et les efforts que vous avez faits pour cela. Ce que je voulais dire par les rectangles, c'est que je veux pouvoir les "glisser" dans le bol, imaginez si vous aviez une carte de visite à l'intérieur d'un bol à fruits et que vous posiez votre doigt dessus du bol, c'est l'effet que je référais à ... Merci encore :) – Mark

Questions connexes