I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Boids/Wren

Wren version of Boids.

## Code

Translation of: C
Library: FreeGLUT
Library: Wren-dynamic
Library: Wren-fmt

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.

`/* boids.wren */ import "random" for Randomimport "./dynamic" for Structimport "./fmt" for Fmt var MIN_MOUNTAIN_RADIUS = 3var MAX_MOUNTAIN_RADIUS = 5var MAX_MOUNTAIN_HEIGHT = 25var MOUNTAIN_RATIO      = 500 var IDEAL_HEIGHT   = 1  // how high a boid prefers to stay above groundvar IDEAL_DISTANCE = 5  // how far boids prefer to stay away from each othervar MOVE_SPEED     = 1e-2var N              = 100var WORLD_SIZE     = 40var START          = System.clock // 3D vectorclass 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 = 0x1701var GL_QUAD_STRIP = 0x0008var GL_TRIANGLES  = 0x0004var GL_LIGHTING   = 0x0b50var GL_LIGHT1     = 0x4001var GL_AMBIENT    = 0x1200var GL_DIFFUSE    = 0x1201var GL_POSITION   = 0x1203var GL_FLAT       = 0x1d00var GL_MODELVIEW  = 0x1700 var GL_COLOR_MATERIAL   = 0x0b57var GL_COLOR_BUFFER_BIT = 0x4000var GL_DEPTH_BUFFER_BIT = 0x0100var GL_DEPTH_TEST       = 0x0b71 var GLUT_UP     = 0x0001var GLUT_DEPTH  = 0x0010var GLUT_RGB    = 0x0000var GLUT_DOUBLE = 0x0002 var GLUT_ACTION_ON_WINDOW_CLOSE      = 0x01f9var 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 = 600var WinHeight = 400var CursorX = 0var 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()`

We now embed this Wren script in the following C program, compile and run it.

`#include <stdio.h>#include <stdlib.h>#include <string.h>#include <GL/gl.h>#include <GL/freeglut.h>#include <GL/glu.h>#include <unistd.h>#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;}`
Output:
```Similar to C entry.
```