Flythrough Demo Source

by Bernie Freidin © 1999-2000



hcsg.cpp hcsg_modeler.cpp collision.cpp camera.cpp vector.cpp main.cpp
hcsg.h hcsg_modeler.h collision.h camera.h vector.h


// **************************************************************************
// HCSG Demo
// (c) Bernie Freidin, 1999-2000
// **************************************************************************

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "camera.h"
#include "collision.h"
#ifndef WIN32
#define __MAC__ //FIXME: FOR NOW: enable __MAC__ if not WIN32
#endif
#ifdef __MAC__
#include <Events.h>
#endif

#ifndef __MAC__
#define _A_ 'A' // see keyboards mapping
#define _Z_ 'Z' // see keyboards mapping
#else
enum
{
	VK_NUMPAD9 = 0x5C,
	VK_NUMPAD8 = 0x5B,
	VK_NUMPAD7 = 0x59,
	VK_NUMPAD6 = 0x58,
	VK_NUMPAD5 = 0x57,
	VK_NUMPAD4 = 0x56,
	VK_NUMPAD3 = 0x55,
	VK_NUMPAD2 = 0x54,
	VK_NUMPAD1 = 0x53,
	VK_NUMPAD0 = 0x52,	
	_Z_        = 0x06,
	_A_        = 0x00,
	_C_        = 0x05,
	VK_SHIFT   = 0x38,
	VK_DOWN    = 0x7D,
	VK_UP      = 0x7E
};

static unsigned char MAC__keymap[16];

static int GetAsyncKeyState(int key)
{
	return((MAC__keymap[k>>3]>>(k&7))&1) ? 0x8000 : 0; 
}
#endif

#define _min(a,b) ((a)<(b)?(a):(b))
#define _max(a,b) ((a)>(b)?(a):(b))
#ifndef PI
#define PI 3.1415926535897932384626433832795
#endif

struct clamp_t
{
	double spring_center;
	double spring_linear;
	double spring_nonlinear;
	double minv;
	double maxv;
	int    minmax_clamp;
};

struct camera_t
{
	double orientation[16]; // world->local
	double position[3];     // worldspace
	double velocity[3];     // local
	double acceleration[3]; // local
	double ypr[3];
	double ypr_velocity[3];
	double ypr_accel[3];
	clamp_t clamp_position;
	clamp_t clamp_velocity;
	clamp_t clamp_ypr[3];
	clamp_t clamp_ypr_velocity[3];
	double instantaneous_accel[7]; // fwd,bck,hrz,vrt,yaw,pch,rol
};
static camera_t CAMERA;

static double Dot3(double v1[3], double v2[3])
{
	return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}

static double Dot3_4(double v1[3], double v2[])
{
	return v1[0]*v2[0] + v1[1]*v2[4] + v1[2]*v2[8];
}

static void Clamp(double &value, clamp_t *clamp)
{
	// ***************************
	// Apply clamp to scalar value
	// ***************************
	
	if(clamp->minmax_clamp)
	{
		value = _max(value, clamp->minv);
		value = _min(value, clamp->maxv);
	}
	value -= clamp->spring_center;
	value *= 1.f - clamp->spring_nonlinear;
	if(value > 0.0) value = _max(value - clamp->spring_linear, 0.0);
	if(value < 0.0) value = _min(value + clamp->spring_linear, 0.0);
	value += clamp->spring_center;
}

static void Clamp3(double v[3], clamp_t *clamp)
{
	// ************************
	// Apply clamp to 3D vector
	// ************************
	
	double value = sqrt(Dot3(v, v));
	double vsave = value;
	
	if(fabs(value) < 1e-12) return;
	
	Clamp(value, clamp);
	vsave = value/vsave;
	v[0] *= vsave;
	v[1] *= vsave;
	v[2] *= vsave;
}

static void ClampTo180(double &v)
{
	if(v < -180.0) v += 360.0; else
	if(v > +180.0) v -= 360.0;
}

static void LoadScalar(double *v)
{
	// *****************************
	// Load scalar value from strtok
	// *****************************
	
	char *paramstr = strtok(NULL, " =:\t");
	if(!paramstr) return;
	*v = atof(paramstr);
}

static void LoadClampParams(clamp_t *clamp)
{
	// *********************************
	// Load clamp parameters from strtok
	// *********************************
	
	char *paramstr = strtok(NULL, " =:\t"); if(!paramstr) return;
	clamp->spring_center = atof(paramstr);
	paramstr = strtok(NULL, " =:\t"); if(!paramstr) return;
	clamp->spring_linear = atof(paramstr);
	paramstr = strtok(NULL, " =:\t"); if(!paramstr) return;
	clamp->spring_nonlinear = atof(paramstr);
	paramstr = strtok(NULL, " =:\t"); if(!paramstr) return;
	clamp->minv = atof(paramstr);
	paramstr = strtok(NULL, " =:\t"); if(!paramstr) return;
	clamp->maxv = atof(paramstr);
	paramstr = strtok(NULL, " =:\t"); if(!paramstr) return;
	clamp->minmax_clamp = atoi(paramstr);
}

static void LoadVector3D(double v[3])
{
	// ****************************
	// Load a 3D vector from strtok
	// ****************************
	
	for(int i = 0; i < 3; i++)
	{
		char *paramstr = strtok(NULL, " =:\t");
		if(!paramstr) return;
		v[i] = atof(paramstr);
	}
}

void InitializeCamera(char *path)
{
	// **************************************
	// Load default camera settings from file
	// **************************************
	
	FILE *prefs = fopen(path, "r");
	char str[200];
	
	if(!prefs)
	{
		char wait[200];
		fprintf(stdout, "Can't open \"%s\"\n", path);
		fprintf(stdout, "[Press RETURN to quit]\n");
		fgets(wait, 199, stdin);
		exit(-1);
	}
	memset(&CAMERA, 0, sizeof(camera_t));
	
	while(fgets(str, 199, prefs))
	{
		if(str[0] == '#') continue;
		if(str[0] == '/' && str[1] == '/') continue;
		
		char *vname = strtok(str, " =:\t");
		
		if(!vname) continue;
		if(!strcmp(vname, "SET_POSITION"))
			LoadVector3D(CAMERA.position); else
		if(!strcmp(vname, "CLAMP_POSITION"))
			LoadClampParams(&CAMERA.clamp_position); else
		if(!strcmp(vname, "CLAMP_VELOCITY"))
			LoadClampParams(&CAMERA.clamp_velocity); else
		if(!strcmp(vname, "CLAMP_YAW"))
			LoadClampParams(&CAMERA.clamp_ypr[0]); else
		if(!strcmp(vname, "CLAMP_PITCH"))
			LoadClampParams(&CAMERA.clamp_ypr[1]); else
		if(!strcmp(vname, "CLAMP_ROLL"))
			LoadClampParams(&CAMERA.clamp_ypr[2]); else
		if(!strcmp(vname, "CLAMP_YAW_VELOCITY"))
			LoadClampParams(&CAMERA.clamp_ypr_velocity[0]); else
		if(!strcmp(vname, "CLAMP_PITCH_VELOCITY"))
			LoadClampParams(&CAMERA.clamp_ypr_velocity[1]); else
		if(!strcmp(vname, "CLAMP_ROLL_VELOCITY"))
			LoadClampParams(&CAMERA.clamp_ypr_velocity[2]); else
		if(!strcmp(vname, "SET_FORWARD_ACCELERATION"))
			LoadScalar(&CAMERA.instantaneous_accel[0]); else
		if(!strcmp(vname, "SET_BACKWARD_ACCELERATION"))
			LoadScalar(&CAMERA.instantaneous_accel[1]); else
		if(!strcmp(vname, "SET_HORIZONTAL_ACCELERATION"))
			LoadScalar(&CAMERA.instantaneous_accel[2]); else
		if(!strcmp(vname, "SET_VERTICAL_ACCELERATION"))
			LoadScalar(&CAMERA.instantaneous_accel[3]); else
		if(!strcmp(vname, "SET_YAW_ACCELERATION"))
			LoadScalar(&CAMERA.instantaneous_accel[4]); else
		if(!strcmp(vname, "SET_PITCH_ACCELERATION"))
			LoadScalar(&CAMERA.instantaneous_accel[5]); else
		if(!strcmp(vname, "SET_ROLL_ACCELERATION"))
			LoadScalar(&CAMERA.instantaneous_accel[6]);
	}
	fclose(prefs);
}

void UpdateCamera(int frames, int collisions)
{
	// **********************************************
	// UpdateCamera() performs the following actions:
	// 
	// Set accelerations based on keyboard state
	// Compute orientation matrix from YPR
	// Compute world velocity
	// Update position based on world velocity
	// Update YPR based on rotational velocity
	// Update local velocity
	// Update rotational velocity
	// Update OpenGL modelview matrix
	// **********************************************
	
	// Set accelerations based on keyboard state
	
#ifdef __MAC__
	KeyMap km;
	GetKeys(km);
	memcpy(MAC__keymap, km, 16);
#endif
	double yaw = 0.0;
	double pch = 0.0;
	double rol = 0.0;
	double fwd = 0.0;
	double sid = 0.0;
	double upd = 0.0;
	if(GetAsyncKeyState(VK_NUMPAD4) & 0x8000) yaw += CAMERA.instantaneous_accel[4];
	if(GetAsyncKeyState(VK_NUMPAD6) & 0x8000) yaw -= CAMERA.instantaneous_accel[4];
	if(GetAsyncKeyState(_A_)        & 0x8000) pch += CAMERA.instantaneous_accel[5];
	if(GetAsyncKeyState(_Z_)        & 0x8000) pch -= CAMERA.instantaneous_accel[5];
	if(GetAsyncKeyState(VK_NUMPAD7) & 0x8000) rol += CAMERA.instantaneous_accel[6];
	if(GetAsyncKeyState(VK_NUMPAD9) & 0x8000) rol -= CAMERA.instantaneous_accel[6];
	if(GetAsyncKeyState(VK_NUMPAD5) & 0x8000) fwd -= CAMERA.instantaneous_accel[0];
	if(GetAsyncKeyState(VK_NUMPAD2) & 0x8000) fwd += CAMERA.instantaneous_accel[1];
	if(GetAsyncKeyState(VK_NUMPAD1) & 0x8000) sid -= CAMERA.instantaneous_accel[2];
	if(GetAsyncKeyState(VK_NUMPAD3) & 0x8000) sid += CAMERA.instantaneous_accel[2];
	if(GetAsyncKeyState(VK_UP)      & 0x8000) upd += CAMERA.instantaneous_accel[3];
	if(GetAsyncKeyState(VK_DOWN)    & 0x8000) upd -= CAMERA.instantaneous_accel[3];
	CAMERA.acceleration[0] = sid;
	CAMERA.acceleration[1] = upd;
	CAMERA.acceleration[2] = fwd;
	CAMERA.ypr_accel[0] = yaw;
	CAMERA.ypr_accel[1] = pch;
	CAMERA.ypr_accel[2] = rol;
	
	for(int j, i = 0; i < frames; i++)
	{		
		// Update rotational velocity
		
		for(j = 0; j < 3; j++)
		{
			CAMERA.ypr_velocity[j] += CAMERA.ypr_accel[j];
			ClampTo180(CAMERA.ypr_velocity[j]);
			Clamp(CAMERA.ypr_velocity[j], &CAMERA.clamp_ypr_velocity[j]);
		}
		// Update YPR based on rotational velocity
		
		for(j = 0; j < 3; j++)
		{
			CAMERA.ypr[j] += CAMERA.ypr_velocity[j];
			ClampTo180(CAMERA.ypr[j]);
			Clamp(CAMERA.ypr[j], &CAMERA.clamp_ypr[j]);
		}
		// Update local velocity
		
		CAMERA.velocity[0] += CAMERA.acceleration[0];
		CAMERA.velocity[1] += CAMERA.acceleration[1];
		CAMERA.velocity[2] += CAMERA.acceleration[2];
		Clamp3(CAMERA.velocity, &CAMERA.clamp_velocity);
		
		// Compute orientation matrix from YPR
		
		double cy = cos(CAMERA.ypr[0]*PI/180.0);
		double sy = sin(CAMERA.ypr[0]*PI/180.0);
		double cp = cos(CAMERA.ypr[1]*PI/180.0);
		double sp = sin(CAMERA.ypr[1]*PI/180.0);
		double cr = cos(CAMERA.ypr[2]*PI/180.0);
		double sr = sin(CAMERA.ypr[2]*PI/180.0);
		
		CAMERA.orientation[0] = sp*sr*sy + cr*cy;
		CAMERA.orientation[4] = cp*sr;
		CAMERA.orientation[8] = cy*sp*sr - cr*sy;
		CAMERA.orientation[1] = cr*sp*sy - cy*sr;
		CAMERA.orientation[5] = cp*cr;
		CAMERA.orientation[9] = cr*cy*sp + sr*sy;
		CAMERA.orientation[2] = cp*sy;
		CAMERA.orientation[6] = -sp;
		CAMERA.orientation[10]= cp*cy;
		
		// Compute impulse vector
		
		vec3 impulse;
		impulse.x = Dot3(CAMERA.velocity, CAMERA.orientation+0);
		impulse.y = Dot3(CAMERA.velocity, CAMERA.orientation+4);
		impulse.z = Dot3(CAMERA.velocity, CAMERA.orientation+8);
		vec3 position;
		position.x = CAMERA.position[0];
		position.y = CAMERA.position[1];
		position.z = CAMERA.position[2];
		
		// Collision-response physics
		//FIXME: use vec3 for camera vectors so we don't have to convert
		
		if(collisions)
		{
			CDR_Collide(position, impulse, 0.4);
		}
		else
		{
			position += impulse;
		}
		CAMERA.position[0] = position.x;
		CAMERA.position[1] = position.y;
		CAMERA.position[2] = position.z;
		Clamp3(CAMERA.position, &CAMERA.clamp_position);		
	}
	double px = Dot3_4(CAMERA.position, CAMERA.orientation+0);
	double py = Dot3_4(CAMERA.position, CAMERA.orientation+1);
	double pz = Dot3_4(CAMERA.position, CAMERA.orientation+2);
	
	CAMERA.orientation[12] = -px;
	CAMERA.orientation[13] = -py;
	CAMERA.orientation[14] = -pz;
	CAMERA.orientation[3]  = 0.0;
	CAMERA.orientation[7]  = 0.0;
	CAMERA.orientation[11] = 0.0;
	CAMERA.orientation[15] = 1.0;
	
	GLint vp[4];
	glGetIntegerv(GL_VIEWPORT, vp);
	double aspect = (double)vp[2] / (double)vp[3];
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, aspect, 0.01, 100.0);
	//glScalef(1.f, 1.f, -1.f); // compensate for -z axis
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMultMatrixd(CAMERA.orientation);
}

This page © Bernie Freidin, 2000.