Saturday, January 30, 2010

Getting Started with F# and OpenGL on OS X

I am exploring programming with F# using mono on OS X. I'm thinking about using F# for a computer vision project and so I wanted to start out by playing around with some example code to see how it all works. This post is just to document the steps to get up and running. I'm a complete newbie to F# and mono so please give feedback if there are better ways to go about what I'm doing. I'd also like to eventually have a build environment that works well with mono on OS X and MS Visual Studio. Please leave a comment if you have any advice on this.
  • Initially I installed mono 2.6.1. using the OS X Intel installer which worked without any issues.
  • Next I downloaded and unzipped the F# 1.9.7.8 zip file from the F# downloads page. The zip file contains a convenient install-mono.sh script so all you have to do is
    chmod +x install-mono.sh && sudo ./install-mono.sh
  • I decided to create bash scripts for the compiler and interpreter just for the sake of convenience:
    mv FSharp-1.9.7.8 ~/bin/
    The contents of ~/bin/fsc.bash:
    #!/bin/bash
    
    mono `dirname $0`/FSharp-1.9.7.8/bin/fsc.exe "$@"
    
    ... and likewise the contents of ~/fsi.bash:
    #!/bin/bash
    
    mono `dirname $0`/FSharp-1.9.7.8/bin/fsi.exe "$@"
    
OK great, we now have a working F# compiler and interpreter on OS X (make sure that ~/bin is on your $PATH though... you already knew that didn't you?). OK now let's try to work with one of the F# Samples to make sure things are working as expected. We can build and run the Samples101 example with this command:
fsc.bash --resource:SampleForm.resx \
    sample.fs sampleform.fs Beginners.fs \
    intermediate.fs program.fs
mono program.exe

hmm... almost there. It looks like there is a hard-coded "..\..\" that gets inserted in source code path. A minor code change in sample.fs fixes that:
60c60,61
<         let dir = System.IO.Path.Combine(appdir, @"..\..\")
---
>         //let dir = System.IO.Path.Combine(appdir, @"..\..\")
>         let dir = appdir
... now for a second try:

That's better. OK, now let's see about getting an OpenGL example running.
  • First I downloaded the TAO Framework (version 2.1.0) which provides .NET bindings to OpenGL and other API's for gaming
  • You can install all of the libraries into mono's global registry by:
    cd $TAO_HOME/bin
    for file in *.dll; do sudo gacutil -i $file; done
    hmm, I get the following error while trying to install FreeType:
    Installed Tao.FreeType.dll into the gac (/Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/gac)
    
    ** (/Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/2.0/gacutil.exe:253): WARNING **:
    Error parsing  \x87\xa0: Error on line 3 char 46: 'libglfw.so"' is not a valid name: '"'
    I'm going to ignore this for now since it doesn't seem to matter for my simple example code.
OK now let's translate the simplest TAO example to F# and see about getting it to run. This is a translation of the $TAO_HOME/source/examples/Redbook/Hello.cs example which is under the MIT License (see license text at end of post). Apologies for the lack of syntax hilighting:
open System
open Tao.FreeGlut
open Tao.OpenGl

let init () =
    // Select clearing color
    Gl.glClearColor (0.0f, 0.0f, 0.0f, 0.0f);

    // Initialize viewing values
    Gl.glMatrixMode Gl.GL_PROJECTION;
    Gl.glLoadIdentity ();
    Gl.glOrtho (0.0, 1.0, 0.0, 1.0, -1.0, 1.0)

let display () =
    // Clear all pixels.
    Gl.glClear Gl.GL_COLOR_BUFFER_BIT;

    // Draw white polygon (rectangle) with corners at (0.25, 0.25, 0.0) and
    // (0.75, 0.75, 0.0).
    Gl.glColor3f (1.0f, 1.0f, 1.0f);
    Gl.glBegin Gl.GL_POLYGON;
        Gl.glVertex3f (0.25f, 0.25f, 0.0f);
        Gl.glVertex3f (0.75f, 0.25f, 0.0f);
        Gl.glVertex3f (0.75f, 0.75f, 0.0f);
        Gl.glVertex3f (0.25f, 0.75f, 0.0f);
    Gl.glEnd ();

    // Don't wait!  Start processing buffered OpenGL routines.
    Gl.glFlush ()

// Declares initial window size, position, and display mode (single buffer and
// RGBA).  Open window with "Hello" in its title bar.  Call initialization
// routines.  Register callback function to display graphics.  Enter main loop
// and process events.
let main =
    Glut.glutInit ();
    Glut.glutInitDisplayMode (Glut.GLUT_SINGLE ||| Glut.GLUT_RGB);
    Glut.glutInitWindowSize (250, 250);
    Glut.glutInitWindowPosition (100, 100);
    ignore (Glut.glutCreateWindow "Hello");
    init ();
    Glut.glutDisplayFunc (new Glut.DisplayCallback(display));
    Glut.glutMainLoop ()

[<STAThread>]
do main
Now I just compile and run with:
fsc.bash -I $TAO_HOME/bin -r Tao.OpenGl.dll -r Tao.FreeGlut.dll hello.fs
mono hello.exe
Hey, it works! That wasn't so painful.
MIT License Copyright 2003-2005 Tao Framework Team http://www.taoframework.com All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2 comments:

  1. Excellent example.

    Only one update needed. The Tao link doesn't work. Try this instead:
    http://sourceforge.net/projects/taoframework/
    ReplyDelete
  2. Fixed the link. Thanks for letting me know.
    ReplyDelete