arsd simpledisplay opengl 3d

Adam D. Ruppe destructionator at gmail.com
Tue Dec 26 16:34:14 UTC 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();
}


More information about the Digitalmars-d-learn mailing list