Silverlight 2で3D(パクリ)
id:nitoyonさんがおもしろいことをやっていたので、Silverlightでマネをしてみた。
ActionScriptで3Dエンジンを一から作ってみようという話。ActionScript自体はあまり知らないけど3Dプログラミング自体には前から興味があったので、楽しく読ませて頂きました。
数学が苦手なので理屈がまだよくわからないけど、Silverlight2で同じのを作ってみました。
以下ソース
Page.xaml
<UserControl x:Class="Silverlight3D.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <Grid Width="400" Height="400" Background="White" Margin="6" HorizontalAlignment="Left" VerticalAlignment="Top" MouseMove="Grid_MouseMove" MouseLeave="Grid_MouseLeave"> <Border BorderBrush="Black" BorderThickness="1" /> <Canvas x:Name="canvas" Margin="200,200,0,0" /> </Grid> </UserControl>
Page.xaml.cs
using System; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Controls; namespace Silverlight3D { public partial class Page : UserControl { private static readonly Brush lineBrush = new SolidColorBrush(Colors.Black); private static readonly Brush pointBrush = new SolidColorBrush(Colors.Blue); private Point3D p1 = new Point3D { X = 0, Y = 0, Z = 100 }; private Point3D p2 = new Point3D { X = 100, Y = 0, Z = 0 }; private Point3D p3 = new Point3D { X = 0, Y = 100, Z = 0 }; private Point3D p4 = new Point3D { X = -50, Y = -50, Z = -50 }; private Point? prevPos; public Page() { InitializeComponent(); Draw3D(0, 0); } private void Draw3D(double rotateX, double rotateY) { this.canvas.Children.Clear(); p1 = Rotate(p1, rotateX, rotateY); p2 = Rotate(p2, rotateX, rotateY); p3 = Rotate(p3, rotateX, rotateY); p4 = Rotate(p4, rotateX, rotateY); DrawLine(p1, p2); DrawLine(p1, p3); DrawLine(p1, p4); DrawLine(p2, p3); DrawLine(p2, p4); DrawLine(p3, p4); DrawPoints(p1, p2, p3, p4); } private void DrawLine(Point3D p1, Point3D p2) { var line = new Line { X1 = p1.X, X2 = p2.X, Y1 = p1.Y, Y2 = p2.Y, Stroke = lineBrush, StrokeThickness = 1.0 }; this.canvas.Children.Add(line); } private void DrawPoints(params Point3D[] points) { foreach(var p in points) { var circle = new Ellipse { Width = 10, Height = 10, Fill = pointBrush }; Canvas.SetLeft(circle, p.X - circle.Width / 2); Canvas.SetTop(circle, p.Y - circle.Height / 2); this.canvas.Children.Add(circle); } } private Point3D Rotate(Point3D origin, double x, double y) { var result = origin.Clone(); // y方向 var p = Rotate2D(result.Z, result.X, x / 180 * Math.PI); result.Z = p.X; result.X = p.Y; // z方向 p = Rotate2D(result.X, result.Y, -y / 180 * Math.PI); result.X = p.X; result.Y = p.Y; return result; } private Point Rotate2D(double x, double y, double rad) { return new Point( Math.Cos(rad) * x + Math.Sin(rad) * y, -Math.Sin(rad) * x + Math.Cos(rad) * y ); } private void Grid_MouseMove(object sender, MouseEventArgs e) { var curPos = e.GetPosition(canvas); if(this.prevPos != null) { Draw3D(curPos.X - prevPos.Value.X, curPos.Y - prevPos.Value.Y); } this.prevPos = curPos; } private void Grid_MouseLeave(object sender, MouseEventArgs e) { this.prevPos = null; } } public class Point3D { public double X { get; set; } public double Y { get; set; } public double Z { get; set; } public Point3D Clone() { return (Point3D)this.MemberwiseClone(); } } }
元のActionScript版だと86行ということだったけど、Silverlight版だと115行(CSharp)+ 12行(XAML)で127行になってしまった。負けた・・・orz(いや、空行とかを切り詰めれば。。。)
既にこの段階でついていけてないけど、これはじっくり腰を据えて勉強してみたい分野ですね。