Skip to content

Instantly share code, notes, and snippets.

@ninegrid
Created May 14, 2014 09:18
Show Gist options
  • Save ninegrid/aa17ff24000a43cd86af to your computer and use it in GitHub Desktop.
Save ninegrid/aa17ff24000a43cd86af to your computer and use it in GitHub Desktop.

#r @"E:\Assemblies\OpenTK\OpenTK.dll"
open System
open System.Drawing
open System.Collections.Generic
open OpenTK
open OpenTK.Graphics
open OpenTK.Graphics.OpenGL
open OpenTK.Input
module Oculus =
type settings =
{ title : string;
width : int;
height : int;
fill : Color;
gmode : GraphicsMode
mmode : MatrixMode
drawf : unit -> unit }
type window (s : settings) =
inherit GameWindow(s.width,s.height,s.gmode,s.title)
do base.VSync <- VSyncMode.On
override o.OnLoad e =
base.OnLoad(e)
GL.ClearColor(s.fill)
GL.Enable(EnableCap.DepthTest)
override o.OnResize e =
base.OnResize e
GL.Viewport(base.ClientRectangle.X, base.ClientRectangle.Y,
base.ClientRectangle.Width, base.ClientRectangle.Height)
let mutable projection =
Matrix4.CreatePerspectiveFieldOfView(float32 (Math.PI / 4.), float32 base.Width / float32 base.Height, 1.f, 64.f)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadMatrix(&projection)
override o.OnUpdateFrame e =
base.OnUpdateFrame e
if base.Keyboard.[Key.Escape] then base.Close()
override o.OnRenderFrame(e) =
base.OnRenderFrame e
base.OnRenderFrame e
GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit)
let mutable modelview = Matrix4.LookAt(Vector3.Zero, Vector3.UnitZ, Vector3.UnitY)
GL.MatrixMode(s.mmode)
GL.LoadMatrix(&modelview)
s.drawf ()
base.SwapBuffers()
(*
module Scenes =
let triangle3d =
(fun () ->
GL.Begin(BeginMode.Triangles)
GL.Color3(Color.Red); GL.Vertex3(-1.f, -1.f, 4.f)
GL.Color3(Color.Blue); GL.Vertex3(1.f, -1.f, 4.f)
GL.Color3(Color.Green); GL.Vertex3(0.f, 1.f, 4.f)
GL.End())
let points2d width height =
(fun () ->
GL.LoadIdentity()
GL.Ortho(0.,width,height,0.,0.,1.)
GL.MatrixMode(MatrixMode.Modelview)
GL.Disable(EnableCap.DepthTest)
GL.Begin(BeginMode.Points)
GL.Color3(Color.Bisque)
GL.Color3(Color.Red); GL.Vertex2(256.5f, 480.5f)
GL.Color3(Color.Blue); GL.Vertex2(35.5f, 35.5f)
GL.Color3(Color.Green); GL.Vertex2(480.5f, 35.5f)
let centroid (ax,ay) (bx,by) (cx,cy) = (ax + bx + cx) / 3.f,(ay + by + cy) / 3.f
let (x,y) = centroid (256.5f, 480.5f) (35.5f, 35.5f) (480.5f, 35.5f)
GL.Color3(Color.White); GL.Vertex2(x,y)
GL.End())
module Test3D =
open Oculus
let s = {
title = "testing";
width = 512;
height = 512;
fill = Color.Black;
gmode = GraphicsMode.Default;
mmode = MatrixMode.Modelview
drawf = Scenes.triangle3d
}
let w = new window(s)
do w.Run(30.)
module Test2D =
open Oculus
let s = {
title = "testing";
width = 512;
height = 512;
fill = Color.Black;
gmode = GraphicsMode.Default;
mmode = MatrixMode.Projection;
drawf = Scenes.points2d (512.) (512.)
}
let w = new window(s)
do w.Run(30.)
*)
(* create a module that represents the window and what runs inside it *)
module Chaos2D =
(* open the Oculus module *)
open Oculus
(* i used a "square" window for this sample *)
let len = 1024
let flen = float len
(* this is for the "dice" this sample uses... *)
module Dice =
let die = new System.Random()
let roll n = (float32 <| die.Next(0,n)) + 0.5f
(* I'm using the dice to roll a random 2D point (just a tuple) *)
let randPoint () = let roll () = Dice.roll len in (roll (), roll ())
(* I'll "roll" 3 random points to make three invisible "anchors" *)
let anchor = [| randPoint (); randPoint (); randPoint () |]
(* this is just some junk to get a midpoint between two points *)
let midpoint (x,y) (x',y') = ((x + x') / 2.f), ((y + y') / 2.f)
(* this function unfolds points at random, but based on some rules:
1) choose a random anchor.
2) Plot a point midway between the last point and the point in the anchor
3) Do it again. *)
let serpenskiAttractor (anchor : (float32 * float32) array) =
let roll () = Dice.die.Next(0,3) in
Seq.unfold (
fun (xi, r) -> let a = anchor.[r] in Some(xi,((midpoint a xi), roll ()))
) (randPoint (), (roll ()))
(* this function becomes the draw function in the settings record
(defined in the oculus module) *)
let scene =
(fun () ->
GL.LoadIdentity()
GL.Ortho(0.,flen,flen,0.,0.,1.)
GL.MatrixMode(MatrixMode.Modelview)
GL.Disable(EnableCap.DepthTest)
GL.Begin(BeginMode.Points)
GL.Color3(Color.White)
(* after setting some opengl stuff, we just take 50,000 elements from our
serpenskiAttractor *)
//Seq.take (50000) (serpenskiAttractor anchor ) |> (* Seq.skip 10 |> *) Seq.iter (fun (x,y) -> GL.Vertex2(x,y))
Seq.take (50000) (serpenskiAttractor [|randPoint (); randPoint(); randPoint()|] ) |> (* Seq.skip 10 |> *) Seq.iter (fun (x,y) -> GL.Vertex2(x,y))
(* open tk is going to draw all those elements as GL.Vertex2s, then call GL.End()
which clears the screen, it will do this for EVERY FRAME as long as the window
is open. *)
GL.End())
(* It would be interesting to play a new Chaos Game every frame... you could
see that by replacing Seq.take above with this code: *)
// Seq.take (50000) (serpenskiAttractor [|randPoint (); randPoint(); randPoint()|] ) |> (* Seq.skip 10 |> *) Seq.iter (fun (x,y) -> GL.Vertex2(x,y))
(* which initializes new anchors for every group of 50,000 pixels plotted *)
(* we create a settings record here, set the settings... our drawf is
set to the scen function above *)
let s = {
title = "chaos game";
width = len;
height = len;
fill = Color.Black
gmode = GraphicsMode.Default;
mmode = MatrixMode.Projection;
drawf = scene
}
(* we construct a window from the settings record *)
let w = new window(s)
(* we call it to run at 30 frames per second *)
do w.Run(30.)
(* evaluate this in FSI and the window will open. use this module as a reference
for creating your own *)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment