#light #r "FSharp.Core" #r "PresentationCore" #r "PresentationFramework" #r "WindowsBase" #r "FSharp.PowerPack" #r "System.Xaml" open System open System.Collections.Generic open System.IO open System.Net open System.Text open System.Windows open System.Windows.Shapes open System.Windows.Controls open System.Windows.Markup open System.Windows.Input open System.Windows.Media open System.Windows.Media.Media3D open System.Windows.Media.Imaging open System.Windows.Threading open System.Xml open System.Diagnostics open System.Threading open System.Xaml (* Helper functions *) let parseXAML (xaml : string) = use ms = new MemoryStream(Encoding.ASCII.GetBytes(xaml)) ms.Position <- int64 0 XamlReader.Load(ms) let getUrlAsTxt (url:string) = use sr = new StreamReader(WebRequest.Create(url).GetResponse().GetResponseStream()) sr.ReadToEnd() let square() = let sqrXaml =" " parseXAML(sqrXaml) :?> MeshGeometry3D let cube() = let cubeXAML = " " parseXAML(cubeXAML) :?> MeshGeometry3D let windowXaml = " " // create our window let window = parseXAML(windowXaml) :?> Window let viewport = window.FindName("ViewPort") :?> Viewport3D do let backPanel = window.FindName("BackPanel") :?> Canvas let frontPanel = window.FindName("FrontPanel") :?> Canvas // Add camera to viewport let camera = PerspectiveCamera(Point3D(0.,0.,0.), Vector3D(0., 0., 1.), Vector3D(0., 1., 0.), 45.) viewport.Camera <- camera // Create the transforms let zoom = TranslateTransform3D() let tran = TranslateTransform3D() let rotx = AxisAngleRotation3D(Vector3D(1.,0.,0.),0.) let roty = AxisAngleRotation3D(Vector3D(0.,1.,0.),0.) let rotz = AxisAngleRotation3D(Vector3D(0.,0.,1.),0.) zoom.OffsetZ <- -10. roty.Angle <- 180. // Add the transform to the camera let group = Transform3DGroup() group.Children.Add(zoom) group.Children.Add(RotateTransform3D(rotz)) group.Children.Add(RotateTransform3D(rotx)) group.Children.Add(RotateTransform3D(roty)) group.Children.Add(tran) camera.Transform <- group let addFront c = frontPanel.Children.Add(c) let removeFront c = frontPanel.Children.Remove(c) let getAspectRatio() = viewport.ActualWidth / viewport.ActualHeight let getProjMatrix (camera:PerspectiveCamera) aspectRatio = let degToRad deg = deg * (Math.PI / 180.0) let hFov = degToRad camera.FieldOfView let zn = camera.NearPlaneDistance let zf = camera.FarPlaneDistance let xScale = 1.0 / tan(hFov / 2.0) let yScale = aspectRatio * xScale let m33 = if zf = Double.PositiveInfinity then -1. else zf / (zn - zf) let m43 = zn * m33 Matrix3D( xScale, 0., 0., 0., 0., yScale, 0., 0., 0., 0., m33, -1., 0., 0., m43, 0.) let getViewMatrix (camera:PerspectiveCamera) = let zAxis = -camera.LookDirection zAxis.Normalize() let xAxis = Vector3D.CrossProduct(camera.UpDirection, zAxis) xAxis.Normalize() let yAxis = Vector3D.CrossProduct(zAxis,xAxis) let position = Vector3D(X=camera.Position.X,Y=camera.Position.Y,Z=camera.Position.Z) let offsetX = -Vector3D.DotProduct(xAxis,position) let offsetY = -Vector3D.DotProduct(yAxis,position) let offsetZ = -Vector3D.DotProduct(zAxis,position) Matrix3D( xAxis.X, yAxis.X, zAxis.X, 0., xAxis.Y, yAxis.Y, zAxis.Y, 0., xAxis.Z, yAxis.Z, zAxis.Z, 0., offsetX, offsetY, offsetZ, 1.) // Camrea Settings let first = ref true // set to true when viewport3D focus is lost let lastMouseMovePos = ref (Point()) let lastMouseDownPos = ref (Point()) let mouse_move = fun (e:Input.MouseEventArgs) -> let p = e.GetPosition(viewport) if !first then lastMouseMovePos := p first := false let d = !lastMouseMovePos - p if e.RightButton = Input.MouseButtonState.Pressed then if e.LeftButton = Input.MouseButtonState.Pressed then // zoom zoom.OffsetZ <- zoom.OffsetZ + zoom.OffsetZ * 10. * d.Y / viewport.ActualHeight else // rotation rotx.Angle <- rotx.Angle + (d.Y / viewport.ActualHeight) * 180. // z is pitch roty.Angle <- roty.Angle + (d.X / viewport.ActualWidth) * 180. lastMouseMovePos := p backPanel.MouseMove.Add(mouse_move) viewport.MouseMove.Add(mouse_move) backPanel.MouseWheel.Add(fun e -> if e.Delta < 0 then zoom.OffsetZ <- zoom.OffsetZ * 1.1 else zoom.OffsetZ <- zoom.OffsetZ / 1.1) viewport.MouseWheel.Add(fun e -> if e.Delta < 0 then zoom.OffsetZ <- zoom.OffsetZ * 1.1 else zoom.OffsetZ <- zoom.OffsetZ / 1.1) backPanel.MouseUp.Add((fun _ -> first := true)) viewport.MouseUp.Add((fun _ -> first := true)) backPanel.MouseLeave.Add(fun _ -> first := true) viewport.MouseLeave.Add(fun _ -> first := true) let key_down = fun (e:KeyEventArgs) -> if Keyboard.IsKeyDown(Key.A) then tran.OffsetX <- tran.OffsetX - 1. if Keyboard.IsKeyDown(Key.D) then tran.OffsetX <- tran.OffsetX + 1. if Keyboard.IsKeyDown(Key.W) then tran.OffsetZ <- tran.OffsetZ - 1. if Keyboard.IsKeyDown(Key.S) then tran.OffsetZ <- tran.OffsetZ + 1. if Keyboard.IsKeyDown(Key.Space) then tran.OffsetY <- tran.OffsetY + 1. if Keyboard.IsKeyDown(Key.LeftCtrl) then tran.OffsetY <- tran.OffsetY - 1. e.Handled <- false window.KeyDown.Add(key_down) /// Function to add the model to the viewport let add mv3d = viewport.Children.Add(mv3d); mv3d /// Function to remove the model from the viewport let remove mv3d = viewport.Children.Remove(mv3d) |> ignore let addRange rs = Array.map (fun m -> m |> add) rs let removeRange rs = Array.map (fun m -> m |> remove) rs window.Topmost <- true window.Show() module Math3D = /// Multiply a 3d vector by a 4x4 matrix let multMV (m:matrix) (v:vector) = let tmp = (m * vector [v.[0]; v.[1]; v.[2]; 1.]) vector [tmp.[0]; tmp.[1]; tmp.[2]] let cross (v1 : vector) (v2 : vector) = vector[v1.[1]*v2.[2]-v1.[2]*v2.[1];v1.[2]*v2.[0]-v1.[0]*v2.[2];v1.[0]*v2.[1]-v1.[1]*v2.[0]] let dot (v1:vector) (v2:vector) = v1.Transpose * v2 let length (v:vector) = sqrt (dot v v) let angle_between v1 v2 = acos ((dot v1 v2) / ((v1 |> length) * (v2 |> length))) let unit = matrix [[ 1.; 0.; 0.; 0.]; [ 0.; 1.; 0.; 0.]; [ 0.; 0.; 1.; 0.]; [ 0.; 0.; 0.; 1.]] let scale x y z = matrix [[ x; 0.; 0.; 0.]; [ 0.; y; 0.; 0.]; [ 0.; 0.; z; 0.]; [ 0.; 0.; 0.; 1.]] let translate x y z = matrix [[ 1.; 0.; 0.; x]; [ 0.; 1.; 0.; y]; [ 0.; 0.; 1.; z]; [ 0.; 0.; 0.; 1.]] let rotateX t = let c = cos t let s = sin t matrix [[ 1.; 0.; 0.; 0.]; [ 0.; c; s; 0.]; [ 0.; -s; c; 0.]; [ 0.; 0.; 0.; 1.]] let rotateY t = let c = cos t let s = sin t matrix [[ c; 0.; s; 0.]; [ 0.; 1.; 0.; 0.]; [ -s; 0.; c; 0.]; [ 0.; 0.; 0.; 1.]] let rotateZ t = let c = cos t let s = sin t matrix [[ c; s; 0.; 0.]; [ -s; c; 0.; 0.]; [ 0.; 0.; 1.; 0.]; [ 0.; 0.; 0.; 1.]] let rotateXYZ x y z = (rotateX x) * (rotateY y) * (rotateZ z) let rotateAxisAngle (v:vector) a = let c = cos a let s = sin a let x = v.[0] let y = v.[1] let z = v.[2] matrix [[ x*x + (1.-x*x)*c; x*y*(1.-c) - z*s; x*z*(1.-c) + z*s; 0.]; [ x*y*(1.-c) + z*s; y*y + (1.-y*y)*c; y*z*(1.-c) - x*s; 0.]; [ x*z*(1.-c) - y*s; y*z*(1.-c) + x*s; z*z + (1.-z*z)*c; 0.]; [ 0.; 0.; 0.; 1.]] /// rotation of a (-1,0,0) vector to for a vector at point 1 to point to point 2 let rotateP1toP2 p1 p2 = let v = p1 - p2 rotateAxisAngle (cross v (vector [-1.; 0.; 0.])) (angle_between v (vector [1.; 0.; 0.])) /// Get a Matrix3D object from a matrix for use with Point3D and Vector3D let Matrix3D (m:matrix) = new Matrix3D( m.[0,0], m.[1,0], m.[2,0], m.[3,0], m.[0,1], m.[1,1], m.[2,1], m.[3,1], m.[0,2], m.[1,2], m.[2,2], m.[3,2], m.[0,3], m.[1,3], m.[2,3], m.[3,3]) let applyMatrix matrix (mv3D:Visual3D) = mv3D.Transform <- MatrixTransform3D(matrix); mv3D let addMatrix matrix (mv3D:Visual3D) = match mv3D.Transform with | :? MatrixTransform3D as mx -> mv3D.Transform <- MatrixTransform3D(mx.Matrix * matrix) | _ -> mv3D.Transform <- MatrixTransform3D(matrix) mv3D let translate x y z (mv3D:Visual3D) = addMatrix (Math3D.Matrix3D(Math3D.translate x y z)) mv3D let scale x y z (mv3D:Visual3D) = addMatrix (Math3D.Matrix3D(Math3D.scale x y z)) mv3D let rotate x y z (mv3D:Visual3D) = addMatrix (Math3D.Matrix3D(Math3D.rotateXYZ x y z)) mv3D let reset (mv3D:Visual3D) = applyMatrix (Math3D.Matrix3D(Math3D.unit)) mv3D let imageModel url model = ModelVisual3D(Content = GeometryModel3D(Geometry = (model), Material = (DiffuseMaterial(ImageBrush(BitmapImage(Uri(url))))))) let brushModel brush model = ModelVisual3D(Content = GeometryModel3D(Geometry = (model), BackMaterial = (DiffuseMaterial(brush)), Material = (DiffuseMaterial(Brushes.Goldenrod)))) let terrain = square() |> imageModel @"http://www.mattssoftwareblog.com/software/3DWPF/skyPic.jpeg" |> scale 200. 200. 200. |> rotate (Math.PI / 2.0) 0. 0. |> translate 1. -1. 1. |> add let tank1 = @"http://www.mattssoftwareblog.com/software/3DWPF/TankXAML.txt" |> getUrlAsTxt|> parseXAML :?> ModelVisual3D |> rotate 0. (Math.PI * 0.3) 0. |> scale 0.1 0.1 0.1 |> translate -50. 1. 5. |> add let tank2 = @"http://www.mattssoftwareblog.com/software/3DWPF/TankXAML.txt" |> getUrlAsTxt |> parseXAML :?> ModelVisual3D |> scale 0.1 0.1 0.1 |> translate -40. 1. -20. |> add let walker1 = @"http://www.mattssoftwareblog.com/software/3DWPF/WalkerXAML.txt" |> getUrlAsTxt|> parseXAML :?> ModelVisual3D |> rotate 0. (Math.PI * 1.5) 0. |> translate 5. -1. 5. |> add let walker2 = @"http://www.mattssoftwareblog.com/software/3DWPF/WalkerXAML.txt" |> getUrlAsTxt |> parseXAML :?> ModelVisual3D |> rotate 0. (Math.PI * 1.3) 0. |> translate 0. -1. 10. |> add let cube1 = cube() |> brushModel Brushes.Goldenrod |> add cube1 |> scale 1.2 1.1 1.2 cube1 |> rotate 0. 0.2 0. cube1 |> translate 1. 1. 0. cube1 |> reset |> scale 5. 5. 5. |> translate 100. -1. 0. cube1 |> rotate 0. 0.1 0.