Here you go, I wasn't quite sure what you meant about the rectangles so I've just added four red rectangles around the opening of a green bowl.
Cheers,
Andy
Xaml first ...
<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>
... and heres the code behind ...
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
}
}