Cube  renderextras.mm at [b81e2948d7]

File src/renderextras.mm artifact 5d964620ef part of check-in b81e2948d7


// renderextras.cpp: misc gl render code and the HUD

#include "cube.h"

#import "DynamicEntity.h"

void
line(int x1, int y1, float z1, int x2, int y2, float z2)
{
	glBegin(GL_POLYGON);
	glVertex3f((float)x1, z1, (float)y1);
	glVertex3f((float)x1, z1, y1 + 0.01f);
	glVertex3f((float)x2, z2, y2 + 0.01f);
	glVertex3f((float)x2, z2, (float)y2);
	glEnd();
	xtraverts += 4;
}

void
linestyle(float width, int r, int g, int b)
{
	glLineWidth(width);
	glColor3ub(r, g, b);
}

void
box(block &b, float z1, float z2, float z3, float z4)
{
	glBegin(GL_POLYGON);
	glVertex3f((float)b.x, z1, (float)b.y);
	glVertex3f((float)b.x + b.xs, z2, (float)b.y);
	glVertex3f((float)b.x + b.xs, z3, (float)b.y + b.ys);
	glVertex3f((float)b.x, z4, (float)b.y + b.ys);
	glEnd();
	xtraverts += 4;
}

void
dot(int x, int y, float z)
{
	const float DOF = 0.1f;
	glBegin(GL_POLYGON);
	glVertex3f(x - DOF, (float)z, y - DOF);
	glVertex3f(x + DOF, (float)z, y - DOF);
	glVertex3f(x + DOF, (float)z, y + DOF);
	glVertex3f(x - DOF, (float)z, y + DOF);
	glEnd();
	xtraverts += 4;
}

void
blendbox(int x1, int y1, int x2, int y2, bool border)
{
	glDepthMask(GL_FALSE);
	glDisable(GL_TEXTURE_2D);
	glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
	glBegin(GL_QUADS);
	if (border)
		glColor3d(0.5, 0.3, 0.4);
	else
		glColor3d(1.0, 1.0, 1.0);
	glVertex2i(x1, y1);
	glVertex2i(x2, y1);
	glVertex2i(x2, y2);
	glVertex2i(x1, y2);
	glEnd();
	glDisable(GL_BLEND);
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	glBegin(GL_POLYGON);
	glColor3d(0.2, 0.7, 0.4);
	glVertex2i(x1, y1);
	glVertex2i(x2, y1);
	glVertex2i(x2, y2);
	glVertex2i(x1, y2);
	glEnd();
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	xtraverts += 8;
	glEnable(GL_BLEND);
	glEnable(GL_TEXTURE_2D);
	glDepthMask(GL_TRUE);
}

const int MAXSPHERES = 50;
struct sphere {
	OFVector3D o;
	float size, max;
	int type;
	sphere *next;
};
sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
bool sinit = false;

void
newsphere(const OFVector3D &o, float max, int type)
{
	if (!sinit) {
		loopi(MAXSPHERES)
		{
			spheres[i].next = sempty;
			sempty = &spheres[i];
		}
		sinit = true;
	}
	if (sempty) {
		sphere *p = sempty;
		sempty = p->next;
		p->o = o;
		p->max = max;
		p->size = 1;
		p->type = type;
		p->next = slist;
		slist = p;
	}
}

void
renderspheres(int time)
{
	glDepthMask(GL_FALSE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glBindTexture(GL_TEXTURE_2D, 4);

	for (sphere *p, **pp = &slist; p = *pp;) {
		glPushMatrix();
		float size = p->size / p->max;
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f - size);
		glTranslatef(p->o.x, p->o.z, p->o.y);
		glRotatef(lastmillis / 5.0f, 1, 1, 1);
		glScalef(p->size, p->size, p->size);
		glCallList(1);
		glScalef(0.8f, 0.8f, 0.8f);
		glCallList(1);
		glPopMatrix();
		xtraverts += 12 * 6 * 2;

		if (p->size > p->max) {
			*pp = p->next;
			p->next = sempty;
			sempty = p;
		} else {
			p->size += time / 100.0f;
			pp = &p->next;
		}
	}

	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);
}

static OFString *closeent;
OFString *entnames[] = {
	@"none?",
	@"light",
	@"playerstart",
	@"shells",
	@"bullets",
	@"rockets",
	@"riflerounds",
	@"health",
	@"healthboost",
	@"greenarmour",
	@"yellowarmour",
	@"quaddamage",
	@"teleport",
	@"teledest",
	@"mapmodel",
	@"monster",
	@"trigger",
	@"jumppad",
	@"?",
	@"?",
	@"?",
	@"?",
	@"?",
};

void
renderents() // show sparkly thingies for map entities in edit mode
{
	closeent = @"";
	if (!editmode)
		return;
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			continue;
		OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
		particle_splash(2, 2, 40, v);
	}
	int e = closestent();
	if (e >= 0) {
		entity &c = ents[e];
		closeent = [[OFString alloc]
		    initWithFormat:@"closest entity = %@ (%d, %d, %d, %d), "
		                   @"selection = (%d, %d)",
		    entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4,
		    getvar(@"selxs"), getvar(@"selys")];
	}
}

void
loadsky(OFString *basename)
{
	@autoreleasepool {
		static OFString *lastsky = @"";

		basename = [basename stringByReplacingOccurrencesOfString:@"\\"
		                                               withString:@"/"];

		if ([lastsky isEqual:basename])
			return;

		static const OFString *side[] = { @"ft", @"bk", @"lf", @"rt",
			@"dn", @"up" };
		int texnum = 14;
		loopi(6)
		{
			OFString *path =
			    [OFString stringWithFormat:@"packages/%@_%@.jpg",
			              basename, side[i]];

			int xs, ys;
			if (!installtex(texnum + i,
			        [Cube.sharedInstance.gameDataIRI
			            IRIByAppendingPathComponent:path],
			        &xs, &ys, true))
				conoutf(@"could not load sky textures");
		}

		lastsky = basename;
	}
}
COMMAND(loadsky, ARG_1STR)

float cursordepth = 0.9f;
GLint viewport[4];
GLdouble mm[16], pm[16];
OFVector3D worldpos;

void
readmatrices()
{
	glGetIntegerv(GL_VIEWPORT, viewport);
	glGetDoublev(GL_MODELVIEW_MATRIX, mm);
	glGetDoublev(GL_PROJECTION_MATRIX, pm);
}

// stupid function to cater for stupid ATI linux drivers that return incorrect
// depth values

float
depthcorrect(float d)
{
	return (d <= 1 / 256.0f) ? d * 256 : d;
}

// find out the 3d target of the crosshair in the world easily and very
// acurately. sadly many very old cards and drivers appear to fuck up on
// glReadPixels() and give false coordinates, making shooting and such
// impossible. also hits map entities which is unwanted. could be replaced by a
// more acurate version of monster.cpp los() if needed

void
readdepth(int w, int h)
{
	glReadPixels(
	    w / 2, h / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth);
	double worldx = 0, worldy = 0, worldz = 0;
	gluUnProject(w / 2, h / 2, depthcorrect(cursordepth), mm, pm, viewport,
	    &worldx, &worldz, &worldy);
	worldpos.x = (float)worldx;
	worldpos.y = (float)worldy;
	worldpos.z = (float)worldz;
	OFVector3D r = OFMakeVector3D(mm[0], mm[4], mm[8]);
	OFVector3D u = OFMakeVector3D(mm[1], mm[5], mm[9]);
	setorient(r, u);
}

void
drawicon(float tx, float ty, int x, int y)
{
	glBindTexture(GL_TEXTURE_2D, 5);
	glBegin(GL_QUADS);
	tx /= 192;
	ty /= 192;
	float o = 1 / 3.0f;
	int s = 120;
	glTexCoord2f(tx, ty);
	glVertex2i(x, y);
	glTexCoord2f(tx + o, ty);
	glVertex2i(x + s, y);
	glTexCoord2f(tx + o, ty + o);
	glVertex2i(x + s, y + s);
	glTexCoord2f(tx, ty + o);
	glVertex2i(x, y + s);
	glEnd();
	xtraverts += 4;
}

void
invertperspective()
{
	// This only generates a valid inverse matrix for matrices generated by
	// gluPerspective()
	GLdouble inv[16];
	memset(inv, 0, sizeof(inv));

	inv[0 * 4 + 0] = 1.0 / pm[0 * 4 + 0];
	inv[1 * 4 + 1] = 1.0 / pm[1 * 4 + 1];
	inv[2 * 4 + 3] = 1.0 / pm[3 * 4 + 2];
	inv[3 * 4 + 2] = -1.0;
	inv[3 * 4 + 3] = pm[2 * 4 + 2] / pm[3 * 4 + 2];

	glLoadMatrixd(inv);
}

VARP(crosshairsize, 0, 15, 50);

int dblend = 0;
void
damageblend(int n)
{
	dblend += n;
}

VAR(hidestats, 0, 0, 1);
VARP(crosshairfx, 0, 1, 1);

void
gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater)
{
	readmatrices();
	if (editmode) {
		if (cursordepth == 1.0f)
			worldpos = player1.o;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		cursorupdate();
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}

	glDisable(GL_DEPTH_TEST);
	invertperspective();
	glPushMatrix();
	glOrtho(0, VIRTW, VIRTH, 0, -1, 1);
	glEnable(GL_BLEND);

	glDepthMask(GL_FALSE);

	if (dblend || underwater) {
		glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
		glBegin(GL_QUADS);
		if (dblend)
			glColor3d(0.0f, 0.9f, 0.9f);
		else
			glColor3d(0.9f, 0.5f, 0.0f);
		glVertex2i(0, 0);
		glVertex2i(VIRTW, 0);
		glVertex2i(VIRTW, VIRTH);
		glVertex2i(0, VIRTH);
		glEnd();
		dblend -= curtime / 3;
		if (dblend < 0)
			dblend = 0;
	}

	glEnable(GL_TEXTURE_2D);

	@autoreleasepool {
		OFString *command = getcurcommand();
		OFString *player = playerincrosshair();

		if (command)
			draw_textf(@"> %@_", 20, 1570, 2, command);
		else if (closeent.length > 0)
			draw_text(closeent, 20, 1570, 2);
		else if (player != nil)
			draw_text(player, 20, 1570, 2);
	}

	renderscores();
	if (!rendermenu()) {
		glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
		glBindTexture(GL_TEXTURE_2D, 1);
		glBegin(GL_QUADS);
		glColor3ub(255, 255, 255);
		if (crosshairfx) {
			if (player1.gunwait)
				glColor3ub(128, 128, 128);
			else if (player1.health <= 25)
				glColor3ub(255, 0, 0);
			else if (player1.health <= 50)
				glColor3ub(255, 128, 0);
		}
		float chsize = (float)crosshairsize;
		glTexCoord2d(0.0, 0.0);
		glVertex2f(VIRTW / 2 - chsize, VIRTH / 2 - chsize);
		glTexCoord2d(1.0, 0.0);
		glVertex2f(VIRTW / 2 + chsize, VIRTH / 2 - chsize);
		glTexCoord2d(1.0, 1.0);
		glVertex2f(VIRTW / 2 + chsize, VIRTH / 2 + chsize);
		glTexCoord2d(0.0, 1.0);
		glVertex2f(VIRTW / 2 - chsize, VIRTH / 2 + chsize);
		glEnd();
	}

	glPopMatrix();

	glPushMatrix();
	glOrtho(0, VIRTW * 4 / 3, VIRTH * 4 / 3, 0, -1, 1);
	renderconsole();

	if (!hidestats) {
		glPopMatrix();
		glPushMatrix();
		glOrtho(0, VIRTW * 3 / 2, VIRTH * 3 / 2, 0, -1, 1);
		draw_textf(@"fps %d", 3200, 2390, 2, curfps);
		draw_textf(@"wqd %d", 3200, 2460, 2, nquads);
		draw_textf(@"wvt %d", 3200, 2530, 2, curvert);
		draw_textf(@"evt %d", 3200, 2600, 2, xtraverts);
	}

	glPopMatrix();

	if (player1.state == CS_ALIVE) {
		glPushMatrix();
		glOrtho(0, VIRTW / 2, VIRTH / 2, 0, -1, 1);
		draw_textf(@"%d", 90, 827, 2, player1.health);
		if (player1.armour)
			draw_textf(@"%d", 390, 827, 2, player1.armour);
		draw_textf(@"%d", 690, 827, 2, player1.ammo[player1.gunselect]);
		glPopMatrix();
		glPushMatrix();
		glOrtho(0, VIRTW, VIRTH, 0, -1, 1);
		glDisable(GL_BLEND);
		drawicon(128, 128, 20, 1650);
		if (player1.armour)
			drawicon(
			    (float)(player1.armourtype * 64), 0, 620, 1650);
		int g = player1.gunselect;
		int r = 64;
		if (g > 2) {
			g -= 3;
			r = 128;
		}
		drawicon((float)(g * 64), (float)r, 1220, 1650);
		glPopMatrix();
	}

	glDepthMask(GL_TRUE);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);
}