Boids/Wren

From Rosetta Code
Revision as of 16:24, 19 September 2021 by PureFox (talk | contribs) (Added code page.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Wren version of Boids.

Code

Wren

Translation of: C
Library: FreeGLUT


As it's not currently possible for Wren-cli to access OpenGL directly, we embed a Wren script in a C application to complete this task. See the OpenGL#Wren task for some of the issues involved here. <lang ecmascript>/* boids.wren */

import "random" for Random import "./dynamic" for Struct import "./fmt" for Fmt

var MIN_MOUNTAIN_RADIUS = 3 var MAX_MOUNTAIN_RADIUS = 5 var MAX_MOUNTAIN_HEIGHT = 25 var MOUNTAIN_RATIO = 500

var IDEAL_HEIGHT = 1 // how high a boid prefers to stay above ground var IDEAL_DISTANCE = 5 // how far boids prefer to stay away from each other var MOVE_SPEED = 1e-2 var N = 100 var WORLD_SIZE = 40 var START = System.clock

// 3D vector class Vec {

   static zero { new([0, 0, 0]) }
   construct new(x) {
       _x = x
   }
   x { _x }
   [i]     { _x[i] }
   [i]=(v) { _x[i] = v }
   scale(r) {
       for (i in 0..2) _x[i] = _x[i] * r
   }
   mulAddTo(y, r) {
       for (i in 0..2) _x[i] = _x[i] + r * y[i]
   }
   addTo(y) {
       mulAddTo(y, 1)
   }
   +(y) {
       var z = List.filled(3, 0)
       for (i in 0..2) z[i] = _x[i] + y[i]
       return Vec.new(z)
   }
   -(y) {
       var z = List.filled(3, 0)
       for (i in 0..2) z[i] = _x[i] - y[i]
       return Vec.new(z)
   }
   len2 { _x[0]*_x[0] + _x[1]*_x[1] + _x[2]*_x[2] }
   dist2(y) { len2(this - y) }
   cross(y) {
       return Vec.new(
           _x[1]*y[2] - _x[2]*y[1],

_x[2]*y[0] - _x[0]*y[2], _x[0]*y[1] - _x[1]*y[0]

       )
   }
   normalize() {
       var r = len2.sqrt
       if (r == 0) return
       scale(1/r)
   }

}

var Boid = Struct.create("Boid", ["position", "heading", "newHeading", "speed"])

var Boids = List.filled(N, null) for (i in 0...N) Boids[i] = Boid.new(Vec.zero, Vec.zero, Vec.zero, 0)

var Mountain = Struct.create("Mountain", ["x", "y", "h", "r", "next"])

var World = Struct.create("World", ["x", "y", "ground", "groundNormal", "hills"])

var WorldV = World.new([0, 0], [0, 0], null, null, null)

var Camera = Struct.create("Camera", ["pitch", "yaw", "distance", "target"])

var CameraV = Camera.new(-Num.pi/4, 0, 100, Vec.zero)

var Rand = Random.new()

var UpdateTime = 0

var GetMsec = Fn.new { ((System.clock - START) * 1000).round }

var HashXY = Fn.new { |x, y|

   var ror = Fn.new { |a, d| (a << d) | (a >> (32 - d)) }
   var h = 0x12345678
   h = h + ror.call(h, 15) ^ ror.call(x, 5)
   h = h + ror.call(h, 15) ^ ror.call(y, 5)
   h = h ^ ror.call(h,  7)
   h = h + ror.call(h, 23)
   h = h ^ ror.call(h, 19)
   h = h + ror.call(h, 11)
   return h

}

var HillHeight = Fn.new { |m, x, y|

   x = x - m.x
   y = y - m.y
   return m.h * (-(x * x + y * y) / (m.r * m.r)).exp

}

var GroundHeight = Fn.new { |x, y|

   var m = WorldV.hills
   var h = 0
   while (m) {
       h = h + HillHeight.call(m, x, y)
       m = m.next
   }
   return h

}

var CalcNormal = Fn.new { |x, y|

   var v = Vec.zero
   var m = WorldV.hills
   while (m) {
       var h = HillHeight.call(m, x, y)
       var t = 2 / (m.r * m.r)
       v[0] = v[0] + (x - m.x) * t * h
       v[1] = v[1] + (y - m.y) * t * h
       m = m.next
   }
   v[2] = 1
   v.normalize()
   return v

}

var MakeTerrain = Fn.new { |cx, cy|

   if (cx*2 == WorldV.x[0] + WorldV.x[1] && cy*2 == WorldV.y[0] + WorldV.y[1]) return
   WorldV.x[0] = cx - WORLD_SIZE

WorldV.x[1] = cx + WORLD_SIZE WorldV.y[0] = cy - WORLD_SIZE WorldV.y[1] = cy + WORLD_SIZE

   var nx = WorldV.x[1] - WorldV.x[0] + 1

var ny = WorldV.y[1] - WorldV.y[0] + 1

   while (WorldV.hills) {
       var m = WorldV.hills.next
       WorldV.hills = m
   }
   for (x in WorldV.x[0]..WorldV.x[1]) {
       for (y in WorldV.y[0]..WorldV.y[1]) {
           var h = HashXY.call(x, y) % MOUNTAIN_RATIO
           if (h != 0) continue
           var m = Mountain.new(
               x, y,
               HashXY.call(((y + x)/2).floor, ((y - x)/2).floor) % MAX_MOUNTAIN_HEIGHT,
               MIN_MOUNTAIN_RADIUS + (HashXY.call(y, x) % 100) / 100 *
               (MAX_MOUNTAIN_RADIUS -  MIN_MOUNTAIN_RADIUS),
               WorldV.hills
           )
           WorldV.hills = m
       }
   }
   if (!WorldV.ground) WorldV.ground = List.filled(nx * ny, 0)
   if (!WorldV.groundNormal) WorldV.groundNormal = List.filled(nx * ny, null)
   for (x in 0...nx) {

var xx = x + WorldV.x[0] for (y in 0...ny) { var yy = y + WorldV.y[0]

           var ix = x * ny + y

WorldV.ground[ix] = GroundHeight.call(xx, yy) WorldV.groundNormal[ix] = CalcNormal.call(xx, yy) } } }

var BoidThink = Fn.new { |b|

   var g = GroundHeight.call(b.position[0], b.position[1])
   var migrationDrive = Vec.new([0, 0.5, 0])
   var heightDrive = Vec.zero
   heightDrive[2] = (IDEAL_HEIGHT + g - b.position[2]) * 0.3
   // follow the ground surface normal
   var terrainDrive = CalcNormal.call(b.position[0], b.position[1])
   var crowdingDrive = Vec.zero
   var groupingDrive = Vec.zero
   var totalWeight = 0
   for (i in 0...N) {
       var other = Boids[i]
       if (other == b) continue
       var diff = other.position - b.position
       var d2 = diff.len2
       var weight = 1 / (d2 * d2)
       diff.normalize()
       if (d2 > IDEAL_DISTANCE * IDEAL_DISTANCE) {

crowdingDrive.mulAddTo(diff, weight) } else { crowdingDrive.mulAddTo(diff, -weight)

       }

groupingDrive.mulAddTo(other.heading, weight) totalWeight = totalWeight + weight }

   groupingDrive.scale(1/totalWeight)
   b.newHeading = migrationDrive
   b.newHeading.addTo(heightDrive)
   b.newHeading.addTo(terrainDrive)
   b.newHeading.addTo(crowdingDrive)
   b.newHeading.addTo(groupingDrive)
   b.newHeading.scale(0.2)
   b.newHeading.normalize()
   var cx = (WorldV.x[0] + WorldV.x[1]) / 2

var cy = (WorldV.y[0] + WorldV.y[1]) / 2 b.newHeading[0] = b.newHeading[0] + (cx - b.position[0]) / 400 b.newHeading[1] = b.newHeading[1] + (cy - b.position[1]) / 400 }

var RunBoids = Fn.new { |msec|

   for (i in 0...N) Boids[i].position.mulAddTo(Boids[i].heading, msec * Boids[i].speed)
   var average = Vec.zero
   for (i in 0...N) average.addTo(Boids[i].position)
   average.scale(1/N)
   CameraV.target = average
   MakeTerrain.call(average[0].truncate, average[1].truncate)
   for (i in 0...N) BoidThink.call(Boids[i])
   for (i in 0...N) Boids[i].heading = Boids[i].newHeading

}

class CUtils {

   foreign static usleep(usec)
   foreign static flushStdout()

}

// GL stuff

var GL_PROJECTION = 0x1701 var GL_QUAD_STRIP = 0x0008 var GL_TRIANGLES = 0x0004 var GL_LIGHTING = 0x0b50 var GL_LIGHT1 = 0x4001 var GL_AMBIENT = 0x1200 var GL_DIFFUSE = 0x1201 var GL_POSITION = 0x1203 var GL_FLAT = 0x1d00 var GL_MODELVIEW = 0x1700

var GL_COLOR_MATERIAL = 0x0b57 var GL_COLOR_BUFFER_BIT = 0x4000 var GL_DEPTH_BUFFER_BIT = 0x0100 var GL_DEPTH_TEST = 0x0b71

var GLUT_UP = 0x0001 var GLUT_DEPTH = 0x0010 var GLUT_RGB = 0x0000 var GLUT_DOUBLE = 0x0002

var GLUT_ACTION_ON_WINDOW_CLOSE = 0x01f9 var GLUT_ACTION_GLUTMAINLOOP_RETURNS = 0x0001

class GL {

   foreign static clear(mask)
   foreign static shadeModel(mode)
   foreign static loadIdentity()
   foreign static frustum(left, right, bottom, top, nearVal, farVal)
   foreign static pushMatrix()
   foreign static normal3f(nx, ny, nz)
   foreign static normal3fv(v)
   foreign static rotatef(angle, x, y, z)
   foreign static translatef(x, y, z)
   foreign static begin(mode)
   foreign static color3f(red, green, blue)
   foreign static vertex3f(x, y, z)
   foreign static popMatrix()
   foreign static end()
   foreign static flush()
   foreign static lightfv(light, pname, params)
   foreign static enable(cap)
   foreign static viewport(x, y, width, height)
   foreign static matrixMode(mode)

}

class GLU {

   foreign static lookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)

}

class Glut {

   foreign static initDisplayMode(mode)
   foreign static initWindowSize(width, height)
   foreign static createWindow(name)
   foreign static swapBuffers()
   foreign static ignoreKeyRepeat(ignore)
   foreign static keyboardFunc(clazz, method)
   foreign static keyboardUpFunc(clazz, method)
   foreign static reshapeFunc(clazz, method)
   foreign static idleFunc(clazz, method)
   foreign static mouseFunc(clazz, method)
   foreign static motionFunc(clazz, method)
   foreign static setOption(eWhat, value)
   foreign static leaveMainLoop()

}

var WinWidth = 600 var WinHeight = 400 var CursorX = 0 var CursorY = 0

var DrawTerrain = Fn.new { var nx = (WorldV.x[1] - WorldV.x[0] + 1).round var ny = (WorldV.y[1] - WorldV.y[0] + 1).round

GL.color3f(0.1, 0.25, 0.35)

for (x in 0...nx - 1) { var xx = x + WorldV.x[0] GL.begin(GL_QUAD_STRIP)

for (y in 0...ny) { var yy = y + WorldV.y[0]

           var ix = x * ny + y

GL.normal3fv(WorldV.groundNormal[ix].x) GL.vertex3f(xx, yy, WorldV.ground[ix])

           ix = ix + ny

GL.normal3fv(WorldV.groundNormal[ix].x) GL.vertex3f(xx + 1, yy, WorldV.ground[ix]) }

GL.end() } }

var DrawBoid = Fn.new { |b| GL.color3f(0.6, 0.3, 0.3) GL.pushMatrix() GL.translatef(b.position[0], b.position[1], b.position[2])

var x = b.heading.x var yaw = x[1].atan(x[0]) / Num.pi * 180 - 90 GL.rotatef(yaw, 0, 0, 1)

var rxy = (x[0] * x[0] + x[1] * x[1]).sqrt var pitch = x[2].atan(rxy) / Num.pi * 180 GL.rotatef(pitch, 1, 0, 0)

GL.begin(GL_TRIANGLES) GL.normal3f(-0.8, 0, 0.6) GL.vertex3f(0, 0.5, 0) GL.vertex3f(-0.5, -0.5, 0) GL.vertex3f(0, 0, 0.1)

GL.normal3f(0.8, 0, 0.6) GL.vertex3f(0, 0.5, 0) GL.vertex3f(0.5, -0.5, 0) GL.vertex3f(0, 0, 0.1)

GL.normal3f(-0.8, 0, -0.6) GL.vertex3f(0, 0.5, 0) GL.vertex3f(-0.5, -0.5, 0) GL.vertex3f(0, 0, -0.1)

GL.normal3f(0.8, 0, -0.6) GL.vertex3f(0, 0.5, 0) GL.vertex3f(0.5, -0.5, 0) GL.vertex3f(0, 0, -0.1)

GL.normal3f(1, -1, 0) GL.vertex3f(-0.5, -0.5, 0) GL.vertex3f(0, 0, 0.1) GL.vertex3f(0, 0, -0.1)

GL.normal3f(-1, -1, 0) GL.vertex3f(0.5, -0.5, 0) GL.vertex3f(0, 0, 0.1) GL.vertex3f(0, 0, -0.1)

GL.end()

GL.popMatrix() }

var SetProjection = Fn.new { |w, h|

   var hor
   var ver
   if (w > h) {
       hor = 0.05
       ver = hor * h / w
   } else {
       ver = 0.05
       hor = ver * w / h
   }
   GL.matrixMode(GL_PROJECTION)
   GL.loadIdentity()
   GL.frustum(-hor, hor, -ver, ver, 0.1, 1000)

}

var SetLighting = Fn.new { var lightAmbient = [0.3, 0.3, 0.3, 1] var lightDiffuse = [1, 1, 1, 1] var lightPosition = [0, 1, 2, 1]

GL.enable(GL_LIGHTING) GL.lightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient) GL.lightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse) GL.lightfv(GL_LIGHT1, GL_POSITION, lightPosition) GL.enable(GL_LIGHT1) GL.shadeModel(GL_FLAT) GL.enable(GL_COLOR_MATERIAL) }

class GLCallbacks {

   static keyDown(key, x, y) {
       System.print("Key down: %(key) (%(x) %(y))")
       if (key == 113) Glut.leaveMainLoop() // q pressed
   }
   static keyUp(key, x, y) {
       System.print("Key up: %(key) (%(x) %(y))")
   }
   static resize(w, h) {
       WinWidth = w
       WinHeight = h
       GL.viewport(0, 0, w, h)
   }
   static render() {
       var msec = GetMsec.call()

if (msec < UpdateTime + 16) { CUtils.usleep((UpdateTime + 16 - msec) * 1000) return } RunBoids.call(msec - UpdateTime) UpdateTime = msec

GL.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) GL.enable(GL_DEPTH_TEST)

SetProjection.call(WinWidth, WinHeight)

CameraV.distance = CameraV.distance.clamp(1, 1000) CameraV.pitch = CameraV.pitch.clamp(-Num.pi / 2.1, Num.pi / 2.1)

var rz = CameraV.distance * (CameraV.pitch.sin) var rxy = CameraV.distance * (CameraV.pitch.cos)

GL.matrixMode(GL_MODELVIEW) GL.loadIdentity()

SetLighting.call() Fmt.write("$0.5f $0.5f\r", CameraV.target[0], CameraV.target[1]) CUtils.flushStdout()

GLU.lookAt(

           CameraV.target[0] - rxy * (CameraV.yaw.cos),

CameraV.target[1] - rxy * (CameraV.yaw.sin), CameraV.target[2] - rz, CameraV.target[0], CameraV.target[1], CameraV.target[2], 0, 0, 1

       )

DrawTerrain.call() for (i in 0...N) DrawBoid.call(Boids[i])

       GL.flush()

Glut.swapBuffers()

   }
   static mouseButton(button, state, x, y) {
       if (state == GLUT_UP) return
       if (button == 3) {
           CameraV.distance = CameraV.distance / 2
       } else if (button == 4) {
           CameraV.distance = CameraV.distance * 2
       }
       CursorX = x
       CursorY = y
   }
   static mouseMove(x, y) {
       var ext = WinWidth
       if (ext < WinHeight) ext = WinHeight
       ext = (ext / 4).floor
       CameraV.yaw   = CameraV.yaw   - (x - CursorX)/ext
       CameraV.pitch = CameraV.pitch - (y - CursorY)/ext
       CursorX = x
       CursorY = y
   }

}

var InitGL = Fn.new {

   UpdateTime = GetMsec.call()

Glut.initDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_DOUBLE) Glut.initWindowSize(600, 400) Glut.createWindow("Boids")

   Glut.ignoreKeyRepeat(1)

Glut.keyboardFunc("GLCallbacks", "keyDown(_,_,_)") Glut.keyboardUpFunc("GLCallbacks", "keyUp(_,_,_)") Glut.reshapeFunc("GLCallbacks", "resize(_,_)") Glut.idleFunc("GLCallbacks", "render()") Glut.mouseFunc("GLCallbacks", "mouseButton(_,_,_,_)") Glut.motionFunc("GLCallbacks", "mouseMove(_,_)")

   Glut.setOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS)

SetLighting.call() }

MakeTerrain.call(0, 1) for (i in 0...N) {

   var x = Rand.float() * 10 - 5
   var y = Rand.float() * 10 - 5
   var z = (Rand.float() + 0.5) * IDEAL_HEIGHT + GroundHeight.call(x, y)
   Boids[i].position = Vec.new([x, y, z])
   Boids[i].speed = (0.98 + 0.04 * Rand.float()) * MOVE_SPEED

} InitGL.call()</lang>
We now embed this Wren script in the following C program, compile and run it. <lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>
  3. include <GL/gl.h>
  4. include <GL/freeglut.h>
  5. include <GL/glu.h>
  6. include <unistd.h>
  7. include "wren.h"

/* C <=> Wren interface functions */

WrenVM *vm;

const char *keyboardClass, *keyboardMethod, *keyboardUpClass, *keyboardUpMethod,

          *reshapeClass, *reshapeMethod, *idleClass, *idleMethod,
          *mouseClass, *mouseMethod, *motionClass, *motionMethod;

void keyboard(unsigned char key, int x, int y) {

   wrenEnsureSlots(vm, 4);
   wrenGetVariable(vm, "main", keyboardClass, 0);
   wrenSetSlotDouble(vm, 1, (double)key);
   wrenSetSlotDouble(vm, 2, (double)x);
   wrenSetSlotDouble(vm, 3, (double)y);
   WrenHandle *method = wrenMakeCallHandle(vm, keyboardMethod);
   wrenCall(vm, method);
   wrenReleaseHandle(vm, method);

}

void keyboardUp(unsigned char key, int x, int y) {

   wrenEnsureSlots(vm, 4);
   wrenGetVariable(vm, "main", keyboardUpClass, 0);
   wrenSetSlotDouble(vm, 1, (double)key);
   wrenSetSlotDouble(vm, 2, (double)x);
   wrenSetSlotDouble(vm, 3, (double)y);
   WrenHandle *method = wrenMakeCallHandle(vm, keyboardUpMethod);
   wrenCall(vm, method);
   wrenReleaseHandle(vm, method);

}

void reshape(int width, int height) {

   wrenEnsureSlots(vm, 3);
   wrenGetVariable(vm, "main", reshapeClass, 0);
   wrenSetSlotDouble(vm, 1, (double)width);
   wrenSetSlotDouble(vm, 2, (double)height);
   WrenHandle *method = wrenMakeCallHandle(vm, reshapeMethod);
   wrenCall(vm, method);
   wrenReleaseHandle(vm, method);

}

void idle() {

   wrenEnsureSlots(vm, 1);
   wrenGetVariable(vm, "main", idleClass, 0);
   WrenHandle *method = wrenMakeCallHandle(vm, idleMethod);
   wrenCall(vm, method);
   wrenReleaseHandle(vm, method);

}

void mouse(int button, int state, int x, int y) {

   wrenEnsureSlots(vm, 5);
   wrenGetVariable(vm, "main", mouseClass, 0);
   wrenSetSlotDouble(vm, 1, (double)button);
   wrenSetSlotDouble(vm, 2, (double)state);
   wrenSetSlotDouble(vm, 3, (double)x);
   wrenSetSlotDouble(vm, 4, (double)y);
   WrenHandle *method = wrenMakeCallHandle(vm, mouseMethod);
   wrenCall(vm, method);
   wrenReleaseHandle(vm, method);

}

void motion(int x, int y) {

   wrenEnsureSlots(vm, 3);
   wrenGetVariable(vm, "main", motionClass, 0);
   wrenSetSlotDouble(vm, 1, (double)x);
   wrenSetSlotDouble(vm, 2, (double)y);
   WrenHandle *method = wrenMakeCallHandle(vm, motionMethod);
   wrenCall(vm, method);
   wrenReleaseHandle(vm, method);

}

void C_clear(WrenVM* vm) {

   GLbitfield mask = (GLbitfield)wrenGetSlotDouble(vm, 1);
   glClear(mask);

}

void C_shadeModel(WrenVM* vm) {

   GLenum mode = (GLenum)wrenGetSlotDouble(vm, 1);
   glShadeModel(mode);

}

void C_loadIdentity(WrenVM* vm) {

   glLoadIdentity();

}

void C_frustum(WrenVM* vm) {

   GLdouble left    = (GLdouble)wrenGetSlotDouble(vm, 1);
   GLdouble right   = (GLdouble)wrenGetSlotDouble(vm, 2);
   GLdouble bottom  = (GLdouble)wrenGetSlotDouble(vm, 3);    
   GLdouble top     = (GLdouble)wrenGetSlotDouble(vm, 4);
   GLdouble nearVal = (GLdouble)wrenGetSlotDouble(vm, 5);    
   GLdouble farVal  = (GLdouble)wrenGetSlotDouble(vm, 6);
   glFrustum(left, right, bottom, top, nearVal, farVal);

}

void C_pushMatrix(WrenVM* vm) {

   glPushMatrix();

}

void C_normal3f(WrenVM* vm) {

   GLfloat nx = (GLfloat)wrenGetSlotDouble(vm, 1);
   GLfloat ny = (GLfloat)wrenGetSlotDouble(vm, 2);
   GLfloat nz = (GLfloat)wrenGetSlotDouble(vm, 3);
   glNormal3f(nx, ny, nz);

}

void C_normal3fv(WrenVM* vm) {

   GLfloat v[3];
   int i;
   for (i = 0; i < 3; ++i) {
       wrenGetListElement(vm, 1, i, 2);
       v[i] = (GLfloat)wrenGetSlotDouble(vm, 2);
   }
   glNormal3fv((const GLfloat*)v);

}

void C_rotatef(WrenVM* vm) {

   GLdouble angle = (GLdouble)wrenGetSlotDouble(vm, 1);
   GLdouble x     = (GLdouble)wrenGetSlotDouble(vm, 2);
   GLdouble y     = (GLdouble)wrenGetSlotDouble(vm, 3);
   GLdouble z     = (GLdouble)wrenGetSlotDouble(vm, 4);
   glRotatef(angle, x, y, z);

}

void C_translatef(WrenVM* vm) {

   GLfloat x = (GLfloat)wrenGetSlotDouble(vm, 1);
   GLfloat y = (GLfloat)wrenGetSlotDouble(vm, 2);
   GLfloat z = (GLfloat)wrenGetSlotDouble(vm, 3);
   glTranslatef(x, y, z);

}

void C_begin(WrenVM* vm) {

   GLenum mode = (GLenum)wrenGetSlotDouble(vm, 1);
   glBegin(mode);

}

void C_color3f(WrenVM* vm) {

   GLfloat red   = (GLfloat)wrenGetSlotDouble(vm, 1);
   GLfloat green = (GLfloat)wrenGetSlotDouble(vm, 2);
   GLfloat blue  = (GLfloat)wrenGetSlotDouble(vm, 3);
   glColor3f(red, green, blue);

}

void C_vertex3f(WrenVM* vm) {

   GLfloat x = (GLfloat)wrenGetSlotDouble(vm, 1);
   GLfloat y = (GLfloat)wrenGetSlotDouble(vm, 2);
   GLfloat z = (GLfloat)wrenGetSlotDouble(vm, 3);
   glVertex3f(x, y, z);

}

void C_popMatrix(WrenVM* vm) {

   glPopMatrix();

}

void C_end(WrenVM* vm) {

   glEnd();

}

void C_flush(WrenVM* vm) {

   glFlush();

}

void C_lightfv(WrenVM* vm) {

   GLenum light  = (GLenum)wrenGetSlotDouble(vm, 1);
   GLenum pname  = (GLenum)wrenGetSlotDouble(vm, 2);
   int count = wrenGetListCount(vm, 3);
   GLfloat params[count];
   int i;
   for (i = 0; i < count; ++i) {
       wrenGetListElement(vm, 3, i, 1);
       params[i] = (GLfloat)wrenGetSlotDouble(vm, 1);
   }
   glLightfv(light, pname, (const GLfloat*)params);

}

void C_enable(WrenVM* vm) {

   GLenum cap = (GLenum)wrenGetSlotDouble(vm, 1);
   glEnable(cap);

}

void C_viewport(WrenVM* vm) {

   GLint x        = (GLint)  wrenGetSlotDouble(vm, 1);
   GLint y        = (GLint)  wrenGetSlotDouble(vm, 2);
   GLsizei width  = (GLsizei)wrenGetSlotDouble(vm, 3);
   GLsizei height = (GLsizei)wrenGetSlotDouble(vm, 4);
   glViewport(x, y, width, height);

}

void C_matrixMode(WrenVM* vm) {

   GLenum mode = (GLenum)wrenGetSlotDouble(vm, 1);
   glMatrixMode(mode);

}

void C_lookAt(WrenVM* vm) {

   GLdouble eyeX    = (GLdouble)wrenGetSlotDouble(vm, 1);
   GLdouble eyeY    = (GLdouble)wrenGetSlotDouble(vm, 2);
   GLdouble eyeZ    = (GLdouble)wrenGetSlotDouble(vm, 3);    
   GLdouble centerX = (GLdouble)wrenGetSlotDouble(vm, 4);
   GLdouble centerY = (GLdouble)wrenGetSlotDouble(vm, 5);
   GLdouble centerZ = (GLdouble)wrenGetSlotDouble(vm, 6);
   GLdouble upX     = (GLdouble)wrenGetSlotDouble(vm, 7);
   GLdouble upY     = (GLdouble)wrenGetSlotDouble(vm, 8);
   GLdouble upZ     = (GLdouble)wrenGetSlotDouble(vm, 9);
   gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);

}

void C_initDisplayMode(WrenVM* vm) {

   unsigned int mode = (unsigned int)wrenGetSlotDouble(vm, 1);
   glutInitDisplayMode(mode);

}

void C_initWindowSize(WrenVM* vm) {

   int width  = (int)wrenGetSlotDouble(vm, 1);
   int height = (int)wrenGetSlotDouble(vm, 2);
   glutInitWindowSize(width, height);

}

void C_createWindow(WrenVM* vm) {

   const char *name = wrenGetSlotString(vm, 1);
   glutCreateWindow(name);

}

void C_swapBuffers(WrenVM* vm) {

   glutSwapBuffers();

}

void C_ignoreKeyRepeat(WrenVM* vm) {

   int ignore = (int)wrenGetSlotDouble(vm, 1);
   glutIgnoreKeyRepeat(ignore);

}

void C_keyboardFunc(WrenVM* vm) {

   keyboardClass  = wrenGetSlotString(vm, 1);
   keyboardMethod = wrenGetSlotString(vm, 2);
   glutKeyboardFunc(&keyboard);

}

void C_keyboardUpFunc(WrenVM* vm) {

   keyboardUpClass  = wrenGetSlotString(vm, 1);
   keyboardUpMethod = wrenGetSlotString(vm, 2);
   glutKeyboardUpFunc(&keyboardUp);

}

void C_reshapeFunc(WrenVM* vm) {

   reshapeClass  = wrenGetSlotString(vm, 1);
   reshapeMethod = wrenGetSlotString(vm, 2);
   glutReshapeFunc(&reshape);

}

void C_idleFunc(WrenVM* vm) {

   idleClass  = wrenGetSlotString(vm, 1);
   idleMethod = wrenGetSlotString(vm, 2);
   glutIdleFunc(&idle);

}

void C_mouseFunc(WrenVM* vm) {

   mouseClass  = wrenGetSlotString(vm, 1);
   mouseMethod = wrenGetSlotString(vm, 2);
   glutMouseFunc(&mouse);

}

void C_motionFunc(WrenVM* vm) {

   motionClass  = wrenGetSlotString(vm, 1);
   motionMethod = wrenGetSlotString(vm, 2);
   glutMotionFunc(&motion);

}

void C_setOption(WrenVM* vm) {

   GLenum eWhat = (GLenum)wrenGetSlotDouble(vm, 1);
   int value    = (int)wrenGetSlotDouble(vm, 2);
   glutSetOption(eWhat, value);

}

void C_leaveMainLoop(WrenVM* vm) {

   glutLeaveMainLoop();

}

void C_usleep(WrenVM* vm) {

   useconds_t usec = (useconds_t)wrenGetSlotDouble(vm, 1);
   usleep(usec);

}

void C_flushStdout(WrenVM* vm) {

   fflush(stdout);

}

WrenForeignMethodFn bindForeignMethod(

   WrenVM* vm,
   const char* module,
   const char* className,
   bool isStatic,
   const char* signature) {
   if (strcmp(module, "main") == 0) {
       if (strcmp(className, "GL") == 0) {
           if (isStatic && strcmp(signature, "clear(_)") == 0)             return C_clear;
           if (isStatic && strcmp(signature, "shadeModel(_)") == 0)        return C_shadeModel;
           if (isStatic && strcmp(signature, "loadIdentity()") == 0)       return C_loadIdentity;
           if (isStatic && strcmp(signature, "frustum(_,_,_,_,_,_)") == 0) return C_frustum;
           if (isStatic && strcmp(signature, "pushMatrix()") == 0)         return C_pushMatrix;
           if (isStatic && strcmp(signature, "normal3f(_,_,_)") == 0)      return C_normal3f;
           if (isStatic && strcmp(signature, "normal3fv(_)") == 0)         return C_normal3fv;
           if (isStatic && strcmp(signature, "rotatef(_,_,_,_)") == 0)     return C_rotatef;
           if (isStatic && strcmp(signature, "translatef(_,_,_)") == 0)    return C_translatef;
           if (isStatic && strcmp(signature, "begin(_)") == 0)             return C_begin;
           if (isStatic && strcmp(signature, "color3f(_,_,_)") == 0)       return C_color3f;
           if (isStatic && strcmp(signature, "vertex3f(_,_,_)") == 0)      return C_vertex3f;
           if (isStatic && strcmp(signature, "popMatrix()") == 0)          return C_popMatrix;
           if (isStatic && strcmp(signature, "end()") == 0)                return C_end;
           if (isStatic && strcmp(signature, "flush()") == 0)              return C_flush;
           if (isStatic && strcmp(signature, "lightfv(_,_,_)") == 0)       return C_lightfv;
           if (isStatic && strcmp(signature, "enable(_)") == 0)            return C_enable;
           if (isStatic && strcmp(signature, "viewport(_,_,_,_)") == 0)    return C_viewport;
           if (isStatic && strcmp(signature, "matrixMode(_)") == 0)        return C_matrixMode;
       } else if (strcmp(className, "GLU") == 0) {
           if (isStatic && strcmp(signature, "lookAt(_,_,_,_,_,_,_,_,_)") == 0) return C_lookAt;
       } else if (strcmp(className, "Glut") == 0) {
           if (isStatic && strcmp(signature, "initDisplayMode(_)") == 0)   return C_initDisplayMode;
           if (isStatic && strcmp(signature, "initWindowSize(_,_)") == 0)  return C_initWindowSize;
           if (isStatic && strcmp(signature, "createWindow(_)") == 0)      return C_createWindow;
           if (isStatic && strcmp(signature, "swapBuffers()") == 0)        return C_swapBuffers; 
           if (isStatic && strcmp(signature, "ignoreKeyRepeat(_)") == 0)   return C_ignoreKeyRepeat; 
           if (isStatic && strcmp(signature, "keyboardFunc(_,_)") == 0)    return C_keyboardFunc;
           if (isStatic && strcmp(signature, "keyboardUpFunc(_,_)") == 0)  return C_keyboardUpFunc;
           if (isStatic && strcmp(signature, "reshapeFunc(_,_)") == 0)     return C_reshapeFunc;
           if (isStatic && strcmp(signature, "idleFunc(_,_)") == 0)        return C_idleFunc;
           if (isStatic && strcmp(signature, "mouseFunc(_,_)") == 0)       return C_mouseFunc;
           if (isStatic && strcmp(signature, "motionFunc(_,_)") == 0)      return C_motionFunc;
           if (isStatic && strcmp(signature, "setOption(_,_)") == 0)       return C_setOption;
           if (isStatic && strcmp(signature, "leaveMainLoop()") == 0)      return C_leaveMainLoop;
       } else if (strcmp(className, "CUtils") == 0) {
           if (isStatic && strcmp(signature, "usleep(_)") == 0)            return C_usleep;
           if (isStatic && strcmp(signature, "flushStdout()") == 0)        return C_flushStdout;
       }
   }
   return NULL;

}

static void writeFn(WrenVM* vm, const char* text) {

   printf("%s", text);

}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {

   switch (errorType) {
       case WREN_ERROR_COMPILE:
           printf("[%s line %d] [Error] %s\n", module, line, msg);
           break;
       case WREN_ERROR_STACK_TRACE:
           printf("[%s line %d] in %s\n", module, line, msg);
           break;
       case WREN_ERROR_RUNTIME:
           printf("[Runtime Error] %s\n", msg);
           break;
   }

}

char *readFile(const char *fileName) {

   FILE *f = fopen(fileName, "r");
   fseek(f, 0, SEEK_END);
   long fsize = ftell(f);
   rewind(f);
   char *script = malloc(fsize + 1);
   fread(script, 1, fsize, f);
   fclose(f);
   script[fsize] = 0;
   return script;

}

static void loadModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result) {

   if( result.source) free((void*)result.source);

}

WrenLoadModuleResult loadModule(WrenVM* vm, const char* name) {

   WrenLoadModuleResult result = {0};
   if (strcmp(name, "random") != 0 && strcmp(name, "meta") != 0) {
       result.onComplete = loadModuleComplete;
       char fullName[strlen(name) + 6];
       strcpy(fullName, name);
       strcat(fullName, ".wren");
       result.source = readFile(fullName);
   }
   return result;

}

int main(int argc, char **argv) {

   glutInit(&argc, argv);
   WrenConfiguration config;
   wrenInitConfiguration(&config);
   config.writeFn = &writeFn;
   config.errorFn = &errorFn;
   config.bindForeignMethodFn = &bindForeignMethod;
   config.loadModuleFn = &loadModule;
   vm = wrenNewVM(&config);
   const char* module = "main";
   const char* fileName = "boids.wren";
   char *script = readFile(fileName);
   WrenInterpretResult result = wrenInterpret(vm, module, script);
   switch (result) {
       case WREN_RESULT_COMPILE_ERROR:
           printf("Compile Error!\n");
           break;
       case WREN_RESULT_RUNTIME_ERROR:
           printf("Runtime Error!\n");
           break;
       case WREN_RESULT_SUCCESS:
           break;
   }
   glutMainLoop();
   wrenFreeVM(vm);
   free(script);
   return 0;

}</lang>

Output:
Similar to C entry.