Thread overview
arsd simpledisplay opengl 3d
Dec 26, 2017
Amorphorious
Dec 26, 2017
Adam D. Ruppe
Dec 27, 2017
Amorphorious
December 26, 2017
Hi Adam, I'm interested in using your simpledisplay for 3D in openGL. I have worked with 2D fine but 3D is problemattic. I cannot get any depth even though i have set up 3D mode by modifying gamehelpers:

SimpleWindow create2dWindow(int width = 512, int height = 512, int viewportWidth=512, int viewportHeight=512, string title="") {
	auto window = new SimpleWindow(width, height, title, OpenGlOptions.yes);

	window.setAsCurrentOpenGlContext();

	glEnable(eGL.BLEND);
	glBlendFunc(eGL.SRC_ALPHA, eGL.ONE_MINUS_SRC_ALPHA);
	glShadeModel(GL_SMOOTH);
	glClearColor(0,0,0,0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers
	glDepthFunc(GL_LESS);
	glDepthMask(GL_TRUE);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	glEnable(GL_DEPTH_TEST);

	glMatrixMode(eGL.PROJECTION);
	glLoadIdentity();
	//glOrtho(0, viewportWidth, viewportHeight, 0, 0, 1);	// Specify opengl window size, which differs from OS window size
	//glMatrixMode(eGL.MODELVIEW);
	//glLoadIdentity();
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	gluPerspective(145, 1.333, 0.01, 100);
	
	
//	glDisable(eGL.DEPTH_TEST);
	glEnable(eGL.TEXTURE_2D);

	return window;
}

I've played around with various settings but either I get nothing or 2D. Any idea what I might be missing?
December 26, 2017
On Tuesday, 26 December 2017 at 07:57:02 UTC, Amorphorious wrote:
> I've played around with various settings but either I get nothing or 2D. Any idea what I might be missing?

so i have barely actually done any 3d stuff myself but the one time I did, the function I used was here.... actually, let me paste you the whole little program. Make sure you have the newest versions of the dependencies from my arsd repo (I had to update them today to get it compiling on the new dmd)

In particular, search for the `go3d` function in here to see how I changed the matrix. Also see the example from my book: http://arsdnet.net/dcode/book/chapter_12/10/opengl.d it is old and needs to import arsd.simpledisplay instead of import simpledisplay... but it should still show you that pyramid after that.


Here's my other thing:

// dmd rotate.d ~/arsd/{simpledisplay,color,eventloop,joystick,simpleaudio,ttf,gamehelpers}.d -version=with_eventloop -debug -gc -version=rotate_3d -J/home/me/arsd
import arsd.simpledisplay;
import arsd.joystick;
import arsd.simpleaudio;

import arsd.gamehelpers;

/*
	Awesome controls:

		thrust
		look

		juke
*/

/*
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/
*/

import std.math;

bool[Key] keyboardState;

bool keyIsDown(Key key) {
	if(auto ptr = key in keyboardState)
		return *ptr;
	return false;
}

class Actor {
	Actor next; // they are a linked list

	bool inactive; // inactive things can be removed from the list by the main loop

	double x, y, z = 0.0;
	double dx = 0.0, dy = 0.0, dz = 0.0;

	// since this is based on the 2d, from the starting position,
	// rx is roll, ry is pitch, and rz is yaw
	// but these are global coordinates so that won't necessarily remain true
	double rx = 0.0, ry = 0.0, rz = 0.0; // in degrees

	// maybe these SHOULD be relative to the ship...
	double drx = 0.0, dry = 0.0, drz = 0.0;

	final double radiansTheta() {
		return rz / 180.0 * PI;
	}

	version(rotate_3d)
	final double radiansPhi() {
		return rx / 180.0 * PI;
	}

	void update() {
		x += dx;
		y += dy;
		z += dz;

		rx += drx;
		ry += dry;
		rz += drz;

		if(rx >= 360)
			rx -= 360;
		if(ry >= 360)
			ry -= 360;
		if(rz >= 360)
			rz -= 360;

		if(rx < 0)
			rx += 360;
		if(ry < 0)
			ry += 360;
		if(rz < 0)
			rz += 360;

	}

	abstract void draw();

	final void drawOnScreen() {
		glPushMatrix();

		glTranslatef(x, y, z);

		version(rotate_3d) {
			glRotatef(rz, 0.0, 0.0, 1.0);
			glRotatef(ry, 0.0, 1.0, 0.0);
			glRotatef(rx, 1.0, 0.0, 0.0);
		} else {
			glRotatef(rz, 0.0, 0.0, 1.0);
		}

		draw();

		glPopMatrix();
	}
}

class ReferencePoint : Actor {
	override void update() {}
	override void draw() {}
}

class NavigationBeacon : Actor {
	this(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}

	override void draw() {
		glColor4f(0.0, 1.0, 0.0, 128);

		glScalef(0.1, 0.1, 0.1);

		glBegin(GL_TRIANGLE_FAN);

		auto size = 1;
		glVertex3f(0, 0, size);
		glVertex3f(-size, -size, 0);
		glVertex3f(-size, size, 0);
		glVertex3f(size, -size, 0);
		glVertex3f(-size, -size, 0);
		glEnd();

		glColor4f(1.0, 1.0, 0.0, 128);

		glBegin(GL_TRIANGLE_FAN);
		glVertex3f(0, 0, -size);
		glVertex3f(-size, -size, 0);
		glVertex3f(-size, size, 0);
		glVertex3f(size, -size, 0);
		glVertex3f(-size, -size, 0);
		glEnd();

	}
	override void update() {}
}

class Ship : Actor {
	version(rotate_3d)
	this(double x, double y, double z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	else
	this(double x, double y) {
		this.x = x;
		this.y = y;
	}

	int life = 100;
	int torpedoEnergy = 100;
	int fuel = 1000;


	int thrustLeftCounter;
	int thrustRightCounter;
	int thrustForwardCounter;

	final float rotationValue(int intensity) {
		intensity /= 1000;
		// 32 is the max... cubed is 32k
		intensity = intensity * intensity * intensity; // cube to give more precision control at low levels

		return 0.00003 * intensity;
	}

	void thrustLeft(short intensity) {
		if(fuel > 2) {
			fuel -= 2;
			// FIXME
			drz -= rotationValue(intensity);

			thrustLeftCounter = 4;
		}
	}

	void thrustRight(short intensity) {
		if(fuel > 2) {
			fuel -= 2;
			// FIXME
			drz += rotationValue(intensity);

			thrustRightCounter = 4;
		}
	}

	void thrustUp(short intensity) {
		dry -= rotationValue(intensity);
	}

	void thrustDown(short intensity) {
		dry += rotationValue(intensity);
	}

	void thrustForward() {
		if(fuel > 8) {
			fuel -= 8;
			version(rotate_3d)
			auto thrust = 0.01;
			else
			auto thrust = 0.4;

			version(rotate_3d) {

				float tx, ty, tz;
				this.getVector(1, 0, 0, tx, ty, tz);
				dx += thrust * tx;
				dy += thrust * ty;
				dz += thrust * tz;
			} else {
				dx += thrust * cos(radiansTheta);
				dy += thrust * sin(radiansTheta);
			}

			thrustForwardCounter = 4;
		}
	}

	void thrustBackward() {
		if(fuel > 4) {
			fuel -= 4;
			version(rotate_3d)
			auto thrust = 0.01;
			else
			auto thrust = 0.4;

			thrust /= 2;

			version(rotate_3d) {

				float tx, ty, tz;
				this.getVector(1, 0, 0, tx, ty, tz);
				dx -= thrust * tx;
				dy -= thrust * ty;
				dz -= thrust * tz;
			} else {
				dx -= thrust * cos(radiansTheta);
				dy -= thrust * sin(radiansTheta);
			}
		}
	}

	void fire() {
		if(torpedoEnergy >= 25) {
			torpedoEnergy -= 25;
			auto torp = new Torpedo(this);
		}
	}

	void getVector(float x, float y, float z, out float xp, out float yp, out float zp) {
		rotateAboutAxis(
			rx / 180 * PI,
			x, y, z,
			1, 0, 0,
			x, y, z);
		rotateAboutAxis(
			ry / 180 * PI,
			x, y, z,
			0, 1, 0,
			x, y, z);
		rotateAboutAxis(
			rz / 180 * PI,
			x, y, z,
			0, 0, 1,
			x, y, z);

		xp = x;
		yp = y;
		zp = z;
	}

	override void update() {
		if(this.torpedoEnergy < 100)
			this.torpedoEnergy++;
		if(this.fuel < 1000)
			this.fuel++;
		super.update();

		/* up vector rotated by the angles too i guess */

		version(rotate_3d) {} else
		if(x < 10 || y < 10 || x > 1034 || y > 778) {
			x = 500;
			y = 500;
			dx = 0;
			dy = 0;
			dθ = 0;
		}
	}

	override void draw() {
		glColor3f(1.0, 1.0, 1.0);

		version(rotate_3d)
		enum size = 0.5;
		else
		enum size = 10;

		glBegin(GL_LINE_LOOP);

		/*
		glVertex2f(0, 0);
		glVertex2f(-size, -size);
		glVertex2f(size, 0);
		glVertex2f(-size, size);
		*/

		glColor3f(0.0, 1.0, 0.0);
		glVertex3f(0, 0, 0);
		glColor3f(0.0, 0.0, 1.0);
		glVertex3f(-size, -size, 0);
		glColor3f(1.0, 1.0, 1.0);
		glVertex3f(size, 0, 0);
		glColor3f(0.0, 0.0, 1.0);
		glVertex3f(-size, size, 0);

		glEnd();

		glBegin(GL_LINE_LOOP);
		glColor3f(1.0, 0, 0);
		glVertex3f(0, 0, 0.01);
		glVertex3f(-size, -size, 0.01);
		glVertex3f(size, 0, 0.01);
		glVertex3f(-size, size, 0.01);
		glEnd();

		glBegin(GL_QUADS);
		glColor3f(1.0, 0, 0);
		glVertex3f(-size, -size, 0.1);
		glVertex3f(-size, -size, -0.1);
		glVertex3f(-size, size, -0.1);
		glVertex3f(-size, size, 0.1);
		glEnd();

		glBegin(GL_QUADS);
		glColor3f(0, 0, 1.0);
		glVertex3f(-size-.01, -size, 0.1);
		glVertex3f(-size-.01, -size, -0.1);
		glVertex3f(-size-.01, size, -0.1);
		glVertex3f(-size-.01, size, 0.1);
		glEnd();

		if(thrustLeftCounter) {
			glBegin(GL_LINES);

			glColor3f(1.0, 0.2, 0.7);

			glVertex2f(size, 0);
			glVertex2f(size, size / 2);

			glVertex2f(-size, -size);
			glVertex2f(-size, -size + -size/2);

			glEnd();

			thrustLeftCounter--;
		}

		if(thrustRightCounter) {
			glBegin(GL_LINES);

			glColor3f(1.0, 0.2, 0.7);

			glVertex2f(size, 0);
			glVertex2f(size, -size / 2);

			glVertex2f(-size, size);
			glVertex2f(-size, size + size/2);

			glEnd();

			thrustRightCounter--;
		}

		if(thrustForwardCounter) {
			glBegin(GL_LINES);

			glColor3f(1.0, 0.2, 0.7);

			glVertex2f(0, 0);
			glVertex2f(-size, -size / 4);

			glVertex2f(0, 0);
			glVertex2f(-size, size / 4);

			glVertex2f(0, 0);
			glVertex2f(-size, 0);

			glEnd();

			thrustForwardCounter--;

		}

		/+
		if(1 /* shields up */) {
			glBegin(GL_LINE_LOOP);

			glColor3f(0, 1.0, 1.0);

			enum ssize = 16;

			glVertex2f(ssize, 0);
			glVertex2f(ssize / 2, ssize);
			glVertex2f(-ssize / 2, ssize);
			glVertex2f(-ssize, 0);
			glVertex2f(-ssize / 2, -ssize);
			glVertex2f(ssize / 2, -ssize);

			glEnd();
		}
		+/


		//glRotatef(-rz, 0.0, 0.0, 1.0);
		//version(rotate_3d)
			//glRotatef(-rx, 0.0, 1.0, 0.0);

		auto statPos = 6;

		void drawStatBar(float r, float g, float b, float value, float maxValue) {
			statPos++;
			glBegin(GL_QUADS);
			glColor3f(r, g, b);

			auto length = value / maxValue * size * 2;

			glVertex2f(-size, size + statPos);
			glVertex2f(length, size + statPos);
			glVertex2f(length, size + statPos + 2);
			glVertex2f(-size, size + statPos + 2);

			glEnd();
			statPos += 2;
		}

		// life
		drawStatBar(0, 1.0, 0, life, 100);

		// torpedo power
		drawStatBar(
			1.0,
			0.20 * torpedoEnergy / 25,
			0.20 * torpedoEnergy / 25,
			torpedoEnergy,
			100);

		// fuel
		drawStatBar(0, 1.0, 1.0, fuel, 1000);
	}
}

class Torpedo : Actor {
	this(Ship firedFrom) {
		this.x = firedFrom.x;
		this.y = firedFrom.y;
		this.z = firedFrom.z;

		double speed = 8;

		version(rotate_3d) {
			speed = 0.28;
			float sx, sy, sz;
			firedFrom.getVector(1, 0, 0, sx, sy, sz);
			dx = firedFrom.dx + speed * sx;
			dy = firedFrom.dy + speed * sy;
			dz = firedFrom.dz + speed * sz;
		} else {
			this.dx = firedFrom.dx + speed * cos(firedFrom.radiansTheta);
			this.dy = firedFrom.dy + speed * sin(firedFrom.radiansTheta);
		}

		this.drx = 12;
		this.dry = 12;
		this.drz = 12;

		this.next = firedFrom.next;
		firedFrom.next = this;
	}

	int life = 2000 / 8; // just setting it to a ballpark number where it will no longer be on the screen anyway

	override void update() {
		this.life--;
		if(this.life <= 0)
			this.inactive = true;
		else
			super.update();
	}

	override void draw() {
		glColor3f(1.0, 1.0, 0);

		version(rotate_3d)
		auto size = 0.1;
		else
		auto size = 3;

		glBegin(GL_POLYGON);

		glVertex3f(0, size, 0.1);
		glVertex3f(size/2, -size, 0.0);
		glVertex3f(-size, size/2, -0.1);
		glVertex3f(size, size/2, 0.0);
		glVertex3f(-size/2, -size, 0.1);

		glEnd();
	}
}

void go2d() {
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, 1024, 768, 0, 0, 1);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
}

void go3d() {
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	// glFrustum(0, 1024, 768, 0, -10, 10);

	auto w = 1024;
	auto h = 768;

	gluPerspective(80.0, cast(double) w / h, 0.5, 1000.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);

	gluLookAt(0, 0, -4, 0, 0, 0, 0, 1, 0);
}

void main() {
	auto window = new SimpleWindow(1024, 768, "Rotate 14!!!", OpenGlOptions.yes);

	auto root = new ReferencePoint();

	import arsd.ttf;
	static import std.file;
	auto font = TtfFont(cast(ubyte[]) import("sans-serif.ttf"));

	version(rotate_3d)
		auto ship = new Ship(0, 0, 0.0);
	else
		auto ship = new Ship(500, 500);

	void addItem(Actor actor) {
		auto n = root.next;
		root.next = actor;
		actor.next = n;
	}

	addItem(ship);

	foreach(x; 1 .. 10) {
		addItem(new NavigationBeacon(-1, x, 0));
		addItem(new NavigationBeacon(1, x, 0));
	}

	addItem(new Ship(12, 45, 2));

	ship.dx = 0; //0.04;
	ship.dy = 0; // 0.04;

	ship.rz = 90;

	window.setAsCurrentOpenGlContext();

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glClearColor(0,0,0,0);
	glDepthFunc(GL_LEQUAL);

	OpenGlTexture[string] textTextures;
	OpenGlTexture getText(string text) {
		if(auto t = text in textTextures)
			return *t;
		auto t = new OpenGlTexture(&font, 16, text);
		textTextures[text] = t;
		return t;
	}

	float f = 0.0;

	version(rotate_3d)
		go3d();
	else
		go2d();

	bool firstPersonViewMode = true;

	window.redrawOpenGlScene = delegate() {
		Actor obj = root;
		Actor previous;
		while(obj) {
			obj.update();
			if(obj.inactive) {
				previous.next = obj.next;
			} else {
				previous = obj;
			}

			obj = obj.next;
		}

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);

	version(rotate_3d) {
	glLoadIdentity();
	/*
	// trying for third person...
	gluLookAt(
		cameraX, cameraY, cameraZ,
		// looking at vector, x,y,z
		ship.x, ship.y, ship.z, // and looks at the ship

		upX, upY, upZ
		// and rotates with the ship - this is up, perpendicular to the direction of thrust
		//cos(rt) * sin(rp),
		//sin(rt) * sin(rp),
		//cos(rp)
		//0
	);
	*/

	/*
	import std.stdio;
	writeln( cameraX, " ",  cameraY, " ", cameraZ);
	*/
	if(firstPersonViewMode != keyIsDown(Key.A)) {
		float cameraX, cameraY, cameraZ;
		float upX, upY, upZ;

		ship.getVector(1, 0, 0, cameraX, cameraY, cameraZ);
		ship.getVector(0, 0, -1, upX, upY, upZ);

		gluLookAt(
			ship.x, ship.y, ship.z,
			ship.x + cameraX, ship.y + cameraY, ship.z + cameraZ,
			upX, upY, upZ
		);
	} else

	// follow the ship, fixed angle
	gluLookAt(
		ship.x, ship.y, ship.z - 35,
		ship.x, ship.y, ship.z,
		0.0, 1.0, 0);
	/*
	// fixed camera (works for the 2d overview but sucks for 3d, you get lost)
	gluLookAt(
		0, 0, 0 - 35,
		0, 0, 0,
		0.0, 1.0, 0);
	*/

	//glTranslatef(-cameraX, -cameraY, -cameraZ);
	//glRotatef(-ship.rz, 0.0, 0.0, 1.0);
	//glRotatef(ship.rx, 0.0, 1.0, 0.0);


	}

	/*
	glColor3f(1, 1, 1);
	// reference point stars
	glBegin(GL_POINTS);
	foreach(i; 0 .. 10)
	foreach(i2; 0 .. 10)
		glVertex3f(i * 10, i2 * 10, ship.z+ 800);

	glEnd();
	*/


		// this works, I can draw 3d stuff then go 2d and draw a 2d overlay on top of it
		version(rotate_3d) {
			//go3d();
			glPushMatrix();
			glTranslatef(4.0, 0, 0.0);
			glRotatef(f, 1, 0, 0);
			f += 4.4;

			glBegin(GL_TRIANGLES);
			// base of the pyramid
			glColor3f(1, 0, 0); glVertex3f(0.5, -0.5, 0);
			glColor3f(0, 1, 0); glVertex3f(0, 0.5, 0);
			glColor3f(0, 0, 1); glVertex3f(-0.5, -0.5, 0);
			// the other three sides connect to the top
			glColor3f(1, 1, 1); glVertex3f(0, 0, 0.5);
			glColor3f(0, 1, 0); glVertex3f(0, 0.5, 0);
			glColor3f(0, 0, 1); glVertex3f(-0.5, -0.5, 0);
			glColor3f(1, 0, 0); glVertex3f(0.5, -0.5, 0);
			glColor3f(1, 1, 1); glVertex3f(0, 0, 0.5);
			glColor3f(0, 0, 1); glVertex3f(-0.5, -0.5, 0);
			glColor3f(1, 1, 1); glVertex3f(0, 0, 0.5);
			glColor3f(1, 0, 0); glVertex3f(0.5, -0.5, 0);
			glColor3f(0, 1, 0); glVertex3f(0, 0.5, 0);
			glEnd();

			glPopMatrix();

			//go2d();
		}

		obj = root;
		while(obj) {
			obj.drawOnScreen();
			obj = obj.next;
		}

		version(rotate_3d) {
			go2d();
			// go 2d to draw the instruments overlay

			import std.string;
			auto texture = getText(format("%0.3f", ship.x));
			texture.draw(Point(0, 0));
			texture = getText(format("%0.3f", ship.y));
			texture.draw(Point(0, 16));
			texture = getText(format("%0.3f", ship.z));
			texture.draw(Point(0, 32));

			texture = getText(format("%0.3f", ship.rx));
			texture.draw(Point(0, 48));
			texture = getText(format("%0.3f", ship.ry));
			texture.draw(Point(0, 64));
			texture = getText(format("%0.3f", ship.rz));
			texture.draw(Point(0, 80));



			go3d();
		}
	};

	auto players = enableJoystickInput(0, -1);

	auto timerEvent = delegate () {
		auto joy = getJoystickUpdate(0);

		enum digitalThrust = 12800;

		auto axis = joy.axisPosition(Axis.horizontalDpad, digitalThrust);

		if(!axis) {
			if(keyIsDown(Key.Left))
				ship.thrustLeft(digitalThrust);
			if(keyIsDown(Key.Right))
				ship.thrustRight(digitalThrust);
		} else {
			if(axis < 0)
				ship.thrustLeft(-axis);
			else
				ship.thrustRight(axis);

		}

		if(joy.buttonWasJustPressed(Button.r1))
			firstPersonViewMode = !firstPersonViewMode;

		if(keyIsDown(Key.S)) {
			ship.x = 0;
			ship.y = 0;
			ship.z = 0;
		}
		if(keyIsDown(Key.D)) {
			ship.dx = 0;
			ship.dy = 0;
			ship.dz = 0;
		}
		if(keyIsDown(Key.F) || joy.buttonWasJustPressed(Button.l3)) {
			ship.drx = 0;
			ship.dry = 0;
			ship.drz = 0;
		}
		if(keyIsDown(Key.G)) {
			ship.rx = 0;
			ship.ry = 0;
			ship.rz = 0;
		}
		if(keyIsDown(Key.I)) {
			ship.rx = 180;
		}
		if(keyIsDown(Key.O)) {
			ship.rx = 0;
		}

		version(rotate_3d) {
			axis = joy.axisPosition(Axis.verticalDpad, digitalThrust);
			if(!axis) {
				if(keyIsDown(Key.Up))
					ship.thrustUp(digitalThrust);
				if(keyIsDown(Key.Down))
					ship.thrustDown(digitalThrust);
			} else {
				if(axis < 0)
					ship.thrustUp(-axis);
				else
					ship.thrustDown(axis);
			}
		} else {
			if(keyIsDown(Key.Up) || joy.axisPosition(Axis.verticalDpad) < 0)
				ship.thrustForward();
		}
		if(keyIsDown(Key.Enter) || joy.buttonIsPressed(Button.r2))
			ship.thrustForward();
		if(keyIsDown(Key.Backspace) || joy.buttonIsPressed(Button.l2))
			ship.thrustBackward();
		if(keyIsDown(Key.Space) || joy.buttonWasJustPressed(Button.circle)) {
			ship.fire();
			import std.stdio;
			writeln(ship.x, " ", ship.y, " ", ship.z, " @ ", ship.dx, " ", ship.dy, " ", ship.dz);
		}

		window.redrawOpenGlSceneNow();
	};

	auto keyEvent = delegate (KeyEvent ke) {
		keyboardState[ke.key] = ke.pressed;
	};

	version(Windows)
	window.eventLoop(50,
		timerEvent,
		keyEvent
	);
	else version(linux) {
		import arsd.eventloop;
		window.handleKeyEvent = keyEvent;
		auto timer = setInterval(timerEvent, 50);
		flushGui();
		loop();
	}

	closeJoysticks();
}
December 27, 2017
Thanks. It's working. Seems like either it was the drawing code or the setting one of the setup conditions.