XNA Game Studio is one of the main pure managed 3D data visualization tools. It is widely used for game development and operates not only on PC but on Xbox and Windows Phones too.
As you probably know, The F# Software Foundation has a “Game And Visualization Stacks” page with description of options available from F#. On this page you can find a link to the “F# With XNA Game Studio” post by AzerDark (@azer89). In the post you can find the detailed guide of how to create a new F# project, reference all required assemblies and create minimal XNA application that shows up an empty window.
I have tried to create something a bit more interesting than an empty window. It is a rotating cube :). A full description of how it works you can find in the post “Getting started with 3D XNA” by David Conrad (with source code in C#). In this post you can see F# code that create XNA game object and model of cube, initialize basic effect (turn on light and configure light, projection and view), create an animation(rotating) for cube and render it in XNA window. As a result, you will see something like that (but with animation 😉 ):
open System open Microsoft.Xna.Framework open Microsoft.Xna.Framework.Graphics open Microsoft.Xna.Framework.Input type Game1() as this = inherit Game() let graphics = new GraphicsDeviceManager(this) let cube = let texCoords = new Vector2(0.0f, 0.0f); let face = [|Vector3(-1.0f, 1.0f, 0.0f); Vector3(-1.0f, -1.0f, 0.0f); Vector3(1.0f, 1.0f, 0.0f); //TopLeft-BottomLeft-TopRight Vector3(-1.0f, -1.0f, 0.0f); Vector3(1.0f, -1.0f, 0.0f); Vector3(1.0f, 1.0f, 0.0f);|] //BottomLeft-BottomRight-TopRight let faceNormals = [|Vector3.UnitZ; -Vector3.UnitZ; //Front & Back faces Vector3.UnitX; -Vector3.UnitX; //Left & Right faces Vector3.UnitY; -Vector3.UnitY|]; //Top & Bottom faces let ang90 = (float32)Math.PI / 2.0f; let faceRotations = [|Matrix.CreateRotationY(2.0f*ang90); Matrix.CreateRotationY(0.0f); Matrix.CreateRotationY(-ang90); Matrix.CreateRotationY(ang90); Matrix.CreateRotationX(ang90); Matrix.CreateRotationX(-ang90)|]; Array.init 36 (fun x -> let i,j = x%6, x/6 VertexPositionNormalTexture( Vector3.Transform(face.[i], faceRotations.[j]) + faceNormals.[j], faceNormals.[j], texCoords)) let mutable effect = null let angle = ref 0.0f; override Game.Initialize() = effect <- new BasicEffect(graphics.GraphicsDevice, AmbientLightColor = Vector3(0.0f, 1.0f, 0.0f), LightingEnabled = true, View = Matrix.CreateTranslation(0.0f,0.0f,-10.0f), Projection = Matrix.CreatePerspectiveFieldOfView( (float32)Math.PI / 4.0f, (float32)this.Window.ClientBounds.Width / (float32)this.Window.ClientBounds.Height, 1.0f, 10.0f)); effect.DirectionalLight0.Enabled <- true; effect.DirectionalLight0.DiffuseColor <- Vector3.One; effect.DirectionalLight0.Direction <- Vector3.Normalize(Vector3.One); base.Initialize() override Game.Update gameTime = angle := !angle + 0.005f if (!angle > 2.0f * (float32)Math.PI) then angle := 0.0f; let R = Matrix.CreateRotationY(!angle) * Matrix.CreateRotationX(0.4f); let T = Matrix.CreateTranslation(0.0f, 0.0f, 5.0f); effect.World <- R * T; base.Update gameTime override Game.Draw gameTime = this.GraphicsDevice.Clear(Color.CornflowerBlue) graphics.GraphicsDevice.RasterizerState <- new RasterizerState(); effect.CurrentTechnique.Passes |> Seq.iter (fun pass -> pass.Apply() graphics.GraphicsDevice.DrawUserPrimitives( PrimitiveType.TriangleList, cube, 0, 12)) base.Draw gameTime [<EntryPoint>] let main argv = use g = new Game1() g.Run() 0