# Boids/Wren

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Wren version of Boids.

## 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) {
}
```
```   +(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 *
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)
```
```   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.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)

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);
```

}

void C_shadeModel(WrenVM* vm) {

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

}

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) {
char fullName[strlen(name) + 6];
strcpy(fullName, name);
strcat(fullName, ".wren");
}
return result;
```

}

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

```   glutInit(&argc, argv);
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = &writeFn;
config.errorFn = &errorFn;
config.bindForeignMethodFn = &bindForeignMethod;
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.
```