/*
	Part of the Cipher Game Engine.
	Copyright (c) 2000-2003 RH Systems Ltd
	All Rights Reserved.
*/
//----------------------------------------------------------------
// client game main entry point
//----------------------------------------------------------------

#include "cg_local.h"
#include "cg_public.h"
#include "ode/ode.h"

#define MAX_OBJEKTOV 1000
#define MAX_CONTACTS 4		// maximum number of contact points per body
#define GPB 3			// maximum number of geometries per body
#define DENSITY (5.0)		// density of all objects

cg_media_t	cg_media;
cg_t		cg;

struct MyObject
{
	dBodyID body;			// the body
	dGeomID geom[GPB];		// geometries representing this body
};


struct odestuff
{
	dWorldID world;
	dSpaceID space;
	dJointGroupID contactgroup;

	struct MyObject obj[MAX_OBJEKTOV];
	dVector3 *Vertices;
	int *Indices;

	dGeomID TriMesh;
	dGeomID Ray;
	dTriMeshDataID Data;
} ode;

cvarhandle_t cg_fov;

typedef struct commandstate_s
{
	int key1;		// track up to 2 keys triggering a command state at the same time
	int key2;
	int state;
} commandstate_t;

commandstate_t cmd_forward;
commandstate_t cmd_backward;
commandstate_t cmd_strafe_left;
commandstate_t cmd_strafe_right;
commandstate_t cmd_up;
commandstate_t cmd_down;
commandstate_t cmd_boost;
commandstate_t cmd_objekt;
commandstate_t cmd_pust;

cvarhandle_t cg_showfps;
cvarhandle_t cg_showlogo;
cvarhandle_t run_sim;

entity_t objekty[MAX_OBJEKTOV];
int pocet_objektov, objekty_used[MAX_OBJEKTOV];

/*
==========================
cg_From640x480Rect

Convert from 640x480 virtual coords to screen coords
==========================
*/
void cg_From640x480Rect(float* x, float* y, float* w, float* h)
{
	*x = *x * cg.screenscalex;
	*y = *y * cg.screenscaley;
	*w = *w * cg.screenscalex; 
	*h = *h * cg.screenscaley;
}

/*
==========================
cg_DrawImage

Draws an image using 640x480 virtual coords
==========================
*/
void cg_DrawImage(float x, float y, float w, float h, int shader)
{
	cg_From640x480Rect(&x, &y, &w, &h);
	cipher_r_DrawImage(x, y, w, h, 0, 0, 1, 1, shader);
}

/*
==========================
cg_LookAtToAxis

Given a position and a point to look at, set up the camera axis
==========================
*/
void cg_LookAtToAxis(vec3_t pos, vec3_t lookat, vec3_t right, vec3_t forward, vec3_t up)
{
	// calc the vector from the camera to the focus point
	vec3_Sub(lookat, pos, forward);
	
	// calc the right vector from the cross of this vector and the up vector
	vec3_Set(0,0,1, up);
	vec3_Cross(forward, up, right);
	
	// back calc the orthonormal up vector
	vec3_Cross(right, forward, up);
	
	// normalise them
	vec3_Normalise(forward);
	vec3_Normalise(up);;
	vec3_Normalise(right);	
}

/*
==========================
cg_EulerToAxis

Euler angles to view axis conversion
==========================
*/
void cg_EulerToAxis(const vec3_t angles, vec3_t right, vec3_t forward, vec3_t up) 
{
	float sx, sy, sz, cx, cy, cz, a;
	
	a = angles[0] * DEG2RAD;
	sx = com_sin(a);
	cx = com_cos(a);
	
	a = angles[1] * DEG2RAD;
	sy = com_sin(a);
	cy = com_cos(a);
	
	a = angles[2] * DEG2RAD;
	sz = com_sin(a);
	cz = com_cos(a);
	
	right[0] = (-sz*sx*cy - cz*-sy);
	right[1] = (-sz*sx*sy - cz*cy);
	right[2] = -sz*cx;
	
	up[0] = (cz*sx*cy - sz*-sy);
	up[1] = (cz*sx*sy - sz*cy);
	up[2] = cz*cx;
	
	forward[0] = cx*cy;
	forward[1] = cx*sy;
	forward[2] = -sx;
}

/*
==========================
cg_LoadMap

Find out the name of the current map and load it
==========================
*/
void cg_LoadMap(void)
{
	tchar_t serversetting[MAX_KEYVAL_SIZE];
	tchar_t mapname[MAX_NAME_SIZE];

	// Load the current map
	// Get the server settings (inc map name)
	cipher_GetSettingString(1, serversetting, MAX_KEYVAL_SIZE);
	cipher_keyval_GetValue(serversetting, _T("map"), mapname, MAX_NAME_SIZE);

	// Finally, load the map
	cipher_LoadMap(mapname);
}



/*
==========================
cg_Init

Called each time the client game is loaded
(eg as you enter the game, between levels, on a vid_restart)
==========================
*/
int cg_Init(void)
{
	FILE *subor_ode;
	int i, j, VertexCount, IndexCount;
	
	// clear our state
	com_zeromem(&cg_media, sizeof(cg_media));
	com_zeromem(&cg, sizeof(cg));

	// Find out about the display
	cipher_r_GetVideoInfo(&cg.videoinfo);
	cg.screenscalex = (float)cg.videoinfo.width/640.0f;
	cg.screenscaley = (float)cg.videoinfo.height/480.0f;

	// and some cvars
	cg_fov = cipher_cvar_Get(_T("cg_fov"), _T("50"), CVAR_ARCHIVE);

	// Set a default position for the camera
	vec3_Set(-160, 0, 2, cg.player.origin);
	vec3_Set(2, 2, 1, cg.player.size);
	vec3_Set(1,0,0, cg.player.axis[0]);
	vec3_Set(0,1,0, cg.player.axis[1]);
	vec3_Set(0,0,1, cg.player.axis[2]);

	// Load a model and a logo shade`r
	cg_media.skybox = cipher_r_RegisterModel(_T("models/skybox.mdl"));
	cg_media.logo = cipher_r_RegisterShader(_T("gfx/ui/logo"));	

	// Load nas objekt
	cg_media.teleso = cipher_r_RegisterModel(_T("models/gulicka.mdl"));
	//cg_media.teleso = cipher_r_RegisterModel(_T("models/cockpit.mdl"));
	
	// Load fonty
	cg_media.font = cipher_r_RegisterFont(_T("gfx/fonts/verdana.font"));

	// biela
	cg_media.white = cipher_r_RegisterShaderNoMipMap(_T("white"));
	
	cg_showfps = cipher_cvar_Get(_T("cg_showfps"), _T("0"), 0);
	cg_showlogo = cipher_cvar_Get(_T("cg_showlogo"), _T("1"), 0);
	run_sim = cipher_cvar_Get(_T("run_sim"), _T("1"), 0);
	
	cipher_RegisterCommand(_T("+forward"));
	cipher_RegisterCommand(_T("-forward"));
	cipher_RegisterCommand(_T("+backward"));
	cipher_RegisterCommand(_T("-backward"));
	cipher_RegisterCommand(_T("+left"));
	cipher_RegisterCommand(_T("-left"));
	cipher_RegisterCommand(_T("+right"));
	cipher_RegisterCommand(_T("-right"));
	cipher_RegisterCommand(_T("+up"));
	cipher_RegisterCommand(_T("-up"));
	cipher_RegisterCommand(_T("+down"));
	cipher_RegisterCommand(_T("-down"));
	cipher_RegisterCommand(_T("+kocka"));
	cipher_RegisterCommand(_T("-kocka"));
	cipher_RegisterCommand(_T("+boost"));
	cipher_RegisterCommand(_T("-boost"));
	cipher_RegisterCommand(_T("+pust"));
	cipher_RegisterCommand(_T("-pust"));
	cipher_RegisterCommand(_T("zrus_objekty"));
	
	// load in the current map
	cg_LoadMap();

	// nastavi infos pre objekty
	pocet_objektov = 0;
	for (i = 0; i < MAX_OBJEKTOV; i++)
		objekty_used[i] = 0;

	// ide nastavovanie ODE
	ode.world = dWorldCreate(); // vytvorenie worldu
	ode.space = dHashSpaceCreate(0); // vytvorenie hashu
	ode.contactgroup = dJointGroupCreate(0); // vytvorenie suboru spojov
	
	dWorldSetGravity(ode.world, 0, 0, -9.81); // nastavenie gravitacie
	dWorldSetCFM(ode.world, 1e-2); // nastavenie korekcie chyb
	dWorldSetAutoDisableFlag(ode.world, 1); // automaticke disabled objektov
	dWorldSetContactMaxCorrectingVel(ode.world, 0.1);
	dWorldSetContactSurfaceLayer(ode.world, 0.001); // max. hlbka vnorenia objektov
	
	/*dCreatePlane(ode.space, 0, 0, 1, -0.5);
	dCreatePlane(ode.space, -1, 0, 0, -20); // zatial testovacia plan
	dCreatePlane(ode.space, 1, 0, 0, -20); // zatial testovacia plan
	dCreatePlane(ode.space, 0, 1, 0, -3); // zatial testovacia plan
	dCreatePlane(ode.space, 0, -1, 0, -3); // zatial testovacia plan*/
	//dCreatePlane(ode.space, 0, -1, 0, -100); // zatial testovacia plan
	//dCreatePlane(ode.space, -1, 0, 0, -100); // zatial testovacia plan

	subor_ode = fopen("plane_ode.ode", "rt");

	fscanf(subor_ode, "%i%i", &VertexCount, &IndexCount);
	
	ode.Vertices = malloc(sizeof(dVector3) * VertexCount);
	ode.Indices = malloc(sizeof(int) * IndexCount * 3);

	for (i = 0; i < VertexCount; i++)
		fscanf(subor_ode, "%i %f %f %f", &j, &ode.Vertices[i][0], &ode.Vertices[i][1], &ode.Vertices[i][2]);

	for (i = 0; i < VertexCount; i++)
	{
		ode.Vertices[i][0] = ode.Vertices[i][0] * 0.01;
		ode.Vertices[i][1] = ode.Vertices[i][1] * 0.01;
		ode.Vertices[i][2] = ode.Vertices[i][2] * 0.01;
	}

	for (i = 0; i < IndexCount; i++)
		fscanf(subor_ode, "%i %i %i %i", &j, &ode.Indices[i * 3], &ode.Indices[i * 3 + 1], &ode.Indices[i * 3 + 2]);

	close(subor_ode);

	ode.Data = dGeomTriMeshDataCreate();
	dGeomTriMeshDataBuildSimple(ode.Data, (dReal*)ode.Vertices, VertexCount, ode.Indices, IndexCount * 3);

	ode.TriMesh = dCreateTriMesh(ode.space, ode.Data, 0, 0, 0);

	return 0;
}

/*
==========================
cg_ShutDown

volane pri konci, zabezpeci dealokovanie zdrojov ODE
==========================
*/

void cg_ShutDown(void)
{
	free(ode.Vertices);
	free(ode.Indices);
	dJointGroupDestroy(ode.contactgroup);
	dSpaceDestroy(ode.space);
	dWorldDestroy(ode.world);
}

/*
==========================
cg_Setfov

Calculate the field of view based on cg_fov
Optionally add some wobble
==========================
*/
void cg_Setfov(void)
{
	// get the starting point for the fov from a cvar
	cg.view.fov_x = cipher_cvar_Float(cg_fov);

	// set up the field of view and clamp it to a valid range.
	if (cg.view.fov_x<1)	cg.view.fov_x = 1;
	if (cg.view.fov_x>170) cg.view.fov_x = 170;
	cg.view.fov_y = cg.view.fov_x * (float)cg.videoinfo.height / (float)cg.videoinfo.width;
}

/*
==========================
cg_AddLights

Add a light to the scene
==========================
*/
void cg_AddLights(void)
{
	vec3_t pos;
	
	vec3_Set(0, 100, 500, pos);
	cipher_r_AddPointLightToScene(pos, 20000, 1, 1, 1);
}	

void cg_SetColour(float r, float g, float b, float a)
{
	float col[4];
	col[0] = r;
	col[1] = g;
	col[2] = b;
	col[3] = a;
	cipher_r_SetColour(col);
}

/*
==========================
cg_Draw3D

Draws all the 3D elements of the scene
==========================
*/
void cg_Draw3D(void)
{
	int i;
	iteminfo_t skybox, kocka;
	vec3_t move;
	const dReal *pos_body, *rot_body;
	
	// set up camera information
	com_zeromem(&cg.view, sizeof(cg.view));
	cg.view.x = cg.view.y = 0;
	cg.view.w = cg.videoinfo.width;
	cg.view.h = cg.videoinfo.height;
	cg.view.time = cg.time;
	cg.view.drawmap = rtrue;
	cg.view.fog = rfalse;

	cg_EulerToAxis(cg.viewangles, cg.view.axis[0], cg.view.axis[1], cg.view.axis[2]);

	// forward - backward
	if (cmd_forward.state == 1 && cmd_backward.state == 0)
	{
		vec3_Scale(cg.view.axis[1], (float)(cmd_boost.state ? 0.5 : 0.1), move);
		vec3_Add(move, cg.player.origin , cg.player.origin);
	}
	else if (cmd_forward.state == 0 && cmd_backward.state == 1)
	{
		vec3_Scale(cg.view.axis[1], (float)(cmd_boost.state ? -0.5 : -0.1), move);
		vec3_Add(move, cg.player.origin , cg.player.origin);
	} 
	
	// strafe left - right
	if (cmd_strafe_left.state == 1 && cmd_strafe_right.state == 0)
	{
		vec3_Scale(cg.view.axis[0], (float)(cmd_boost.state ? -0.5 : -0.1), move);
		vec3_Add(move, cg.player.origin , cg.player.origin);
	}
	else if (cmd_strafe_left.state == 0 && cmd_strafe_right.state == 1)
	{
		vec3_Scale(cg.view.axis[0], (float)(cmd_boost.state ? 0.5 : 0.1), move);
		vec3_Add(move, cg.player.origin , cg.player.origin);
	} 
	
	// up - down
	if (cmd_up.state == 1 && cmd_down.state == 0)
	{
		vec3_Scale(cg.view.axis[2], (float)(cmd_boost.state ? 0.5 : 0.1), move);
		vec3_Add(move, cg.player.origin , cg.player.origin);
	}
	else if (cmd_up.state == 0 && cmd_down.state == 1)
	{
		vec3_Scale(cg.view.axis[2], (float)(cmd_boost.state ? -0.5 : -0.1), move);
		vec3_Add(move, cg.player.origin , cg.player.origin);
	} 

	vec3_Copy(cg.player.origin, cg.view.origin);

	// Set the field of view
	cg_Setfov();

	// Clear the existing scene contents
	cipher_r_ClearScene();

	cg_SetColour(255, 255, 255, 1);
	cg_DrawImage(0, 0, 640, 480, cg_media.white);

	// Add some lights
	cg_AddLights();

	// Draw the sky box (center it on the camera)
	com_zeromem(&skybox, sizeof(skybox));
	skybox.axis[0][0] = 1;
	skybox.axis[1][1] = 1;
	skybox.axis[2][2] = 1;
	vec3_Set(0, 0, 0, skybox.origin);
	vec3_Copy(cg.player.origin , skybox.origin);
	skybox.model = cg_media.skybox;
	skybox.type = ITEMTYPE_MODEL;
	cipher_r_AddItemToScene(&skybox);

	com_zeromem(&kocka, sizeof(iteminfo_t));
	kocka.model = cg_media.teleso;
	kocka.type = ITEMTYPE_MODEL;
	kocka.flags = ITEMFLAG_CASTSHADOW;

	for (i = 0; i < MAX_OBJEKTOV; i++)
	if (objekty_used[i])
	{
		pos_body = dBodyGetPosition(ode.obj[i].body);
		rot_body = dBodyGetRotation(ode.obj[i].body);
		vec3_Set(pos_body[0] * 100, pos_body[1] * 100, pos_body[2] * 100, kocka.origin);
//		vec3_Set(pos_body[1], -pos_body[0], pos_body[2], kocka.origin);
//		vec3_Copy(objekty[i].origin, kocka.origin);
		vec3_Set(rot_body[0], rot_body[1], rot_body[2], kocka.axis[0]);
		vec3_Set(rot_body[4], rot_body[5], rot_body[6], kocka.axis[1]);
		vec3_Set(rot_body[8], rot_body[9], rot_body[10], kocka.axis[2]);

		/*vec3_Copy(objekty[i].axis[0], kocka.axis[0]);
		vec3_Copy(objekty[i].axis[1], kocka.axis[1]);
		vec3_Copy(objekty[i].axis[2], kocka.axis[2]);*/
		cipher_r_AddItemToScene(&kocka);
	}

	// Draw the scene
	cipher_r_RenderScene(&cg.view);
}


/*
==========================
cg_AddObject

Prida kocku na testovanie
==========================
*/
void cg_AddObject(void)
{
	int i;
	dReal pos[3], vel[3];
	tchar_t s_objekty[20];
	dMatrix3 otocka;
	dMass mass;
	
	if (cmd_objekt.state == 0)
		return;

	if (pocet_objektov == MAX_OBJEKTOV)
		return;

	i = 0;
	while (i < MAX_OBJEKTOV && objekty_used[i])
		i++;

	if (i == MAX_OBJEKTOV)
		return;

	pos[0] = (dReal)com_rand() * 4 / CIPHER_RAND_MAX - 2;
	pos[1] = (dReal)com_rand() * 4 / CIPHER_RAND_MAX - 2;
	pos[2] = (dReal)com_rand() * 4 / CIPHER_RAND_MAX + 4;
	vel[0] = (dReal)com_rand() / CIPHER_RAND_MAX -1;
	vel[1] = (dReal)com_rand() / CIPHER_RAND_MAX -1;
	vel[2] = (dReal)com_rand() / CIPHER_RAND_MAX -1;
	com_zeromem(&objekty[i], sizeof(entity_t));
	objekty[i].axis[0][0] = 1;
	objekty[i].axis[1][1] = 1;
	objekty[i].axis[2][2] = 1;
	vec3_Copy(pos, objekty[i].origin);
	objekty_used[i] = 1;

	ode.obj[i].body = dBodyCreate(ode.world);
	dBodySetPosition(ode.obj[i].body, pos[0], pos[1], pos[2]);
	//dBodySetLinearVel(ode.obj[i].body, vel[0], vel[1], vel[2]);
	dRFrom2Axes(otocka, 1, 0, 0, 0, 1, 0);
	dBodySetRotation(ode.obj[i].body, otocka);
	//dMassSetBox(&mass, DENSITY, 0.005, 0.005, 0.005); // 0.005 je akoze hrana kocky
	dMassSetSphere(&mass, DENSITY, 0.005); // tvarim sa, ze to je gula

//	IMPORTANT !!! treba ODE zakomponovat do jadra cipheru, lebo by som musel zadavat geometricke rozmery rucne vzdy
	//ode.obj[i].geom[0] = dCreateBox(ode.space, 0.005, 0.005, 0.005);
	ode.obj[i].geom[0] = dCreateSphere(ode.space, 0.005);
	dGeomSetBody(ode.obj[i].geom[0], ode.obj[i].body);
	dBodySetMass(ode.obj[i].body, &mass);
	
	pocet_objektov++;

	cg_sprintf(s_objekty, 20, _T("%d kociek\n"), pocet_objektov);
	cipher_con_Print(s_objekty);
}
	
/*
==========================
cg_PustObjekt

prida kocky rutiacu sa smerom pozorovatela
==========================
*/
void cg_PustObjekt(void)
{
	int i;
	dReal pos[3], vel[3];
	tchar_t s_objekty[20];
	dMatrix3 otocka;
	dMass mass;
	
	if (cmd_pust.state == 0)
		return;

	if (pocet_objektov == MAX_OBJEKTOV)
		return;

	i = 0;
	while (i < MAX_OBJEKTOV && objekty_used[i])
		i++;

	if (i == MAX_OBJEKTOV)
		return;

	cg_EulerToAxis(cg.viewangles, cg.view.axis[0], cg.view.axis[1], cg.view.axis[2]);

	pos[0] = cg.player.origin[0] / 100;
	pos[1] = cg.player.origin[1] / 100;
	pos[2] = cg.player.origin[2] / 100;
	vel[0] = cg.view.axis[1][0];
	vel[1] = cg.view.axis[1][1];
	vel[2] = cg.view.axis[1][2];

	com_zeromem(&objekty[i], sizeof(entity_t));
	vec3_Copy(cg.view.axis[0], objekty[i].axis[0]);
	vec3_Copy(cg.view.axis[1], objekty[i].axis[1]);
	vec3_Copy(cg.view.axis[2], objekty[i].axis[2]);
	vec3_Copy(pos, objekty[i].origin);
	objekty_used[i] = 1;

	ode.obj[i].body = dBodyCreate(ode.world);
	dBodySetPosition(ode.obj[i].body, pos[0], pos[1], pos[2]);
	dBodySetLinearVel(ode.obj[i].body, vel[0], vel[1], vel[2]);
	dRFrom2Axes(otocka, cg.view.axis[0][0], cg.view.axis[0][1], cg.view.axis[0][2], cg.view.axis[1][0], cg.view.axis[1][2], cg.view.axis[1][2]);
	dBodySetRotation(ode.obj[i].body, otocka);
	//dMassSetBox(&mass, DENSITY, 0.005, 0.005, 0.005); // 0.005 je akoze hrana kocky
	dMassSetSphere(&mass, DENSITY, 0.005);

//	IMPORTANT !!! treba ODE zakomponovat do jadra cipheru, lebo by som musel zadavat geometricke rozmery rucne vzdy
	//ode.obj[i].geom[0] = dCreateBox(ode.space, 0.005, 0.005, 0.005);
	ode.obj[i].geom[0] = dCreateSphere(ode.space, 0.005);
	dGeomSetBody(ode.obj[i].geom[0], ode.obj[i].body);
	dBodySetMass(ode.obj[i].body, &mass);
	
	pocet_objektov++;

	cg_sprintf(s_objekty, 20, _T("%d kociek\n"), pocet_objektov);
	cipher_con_Print(s_objekty);
}

/*
==========================
cg_sprintf

Local version of sprintf
==========================
*/
void cg_sprintf(tchar_t* dst, uint_t dst_size, const tchar_t* format, ...)
{
	// do the formating
	com_va_list valist;
    com_va_start(valist, format);
	com_vsnwprintf(dst, dst_size, format, valist);
    com_va_end(valist);
}

/*
==========================
cg_DrawFPS

vypocita a napise fps
==========================
*/

#define FPS_HISTORY 16	// keep as power of 2
void cg_DrawFPS(void)
{
	// Keep a log of the frame time each frame
	static int history[FPS_HISTORY];
	static int index;
	float fps, black[] = { 0, 0, 0, 1 };
	int oldtime;
	float x = 580 , y = 10;

	// do we want the fps showing
	if (!cipher_cvar_Bool(cg_showfps))
	{
		index=0;
		return;
	}

	// record the time at this frame and look up the
	// time in our oldest record
	history[index&(FPS_HISTORY-1)] = cg.time;
	index++;
	oldtime = history[index&(FPS_HISTORY-1)];
	if (oldtime==cg.time)
		oldtime -= 1;

	// figure out the average fps over the last few frames
	fps = (1000.0f * (FPS_HISTORY-1)) / (cg.time - oldtime);

	// draw it
	if (index>FPS_HISTORY)
	{
		tchar_t buffer[MAX_STRING_SIZE];
		cg_sprintf(buffer, MAX_STRING_SIZE, _T("%dfps"), (int) fps);
		//cg_DrawTextDropShadow(buffer, 550, 10);
		// drop shadow
		x = x * cg.screenscalex;
		y = y * cg.screenscaley;
		
		cipher_r_SetColour(black);
		cipher_r_DrawText(buffer, x +1, y +1, 16.0f, cg_media.font);

		// normal text
		cipher_r_SetColour(NULL);
		cipher_r_DrawText(buffer, x, y, 16.0f, cg_media.font);
	}
}

/*
==========================
cg_Draw2D

Draw 2D elements over the screen (eg HUD, Logos etc)
==========================
*/
void cg_Draw2D(void)
{
	// Draw a logo here
	if (cipher_cvar_Bool(cg_showlogo))
		cg_DrawImage(512, 416, 128, 64, cg_media.logo);

	cg_DrawFPS();
}

// this is called by dSpaceCollide when two objects in space are
// potentially colliding.

static void nearCallback (void *data, dGeomID o1, dGeomID o2)
{
	int i, numc;
	dContact contact[MAX_CONTACTS];   // up to MAX_CONTACTS contacts per box-box
	const dReal ss[3] = {0.02,0.02,0.02};

	// if (o1->body && o2->body) return;

	// exit without doing anything if the two bodies are connected by a joint
	dBodyID b1 = dGeomGetBody(o1);
	dBodyID b2 = dGeomGetBody(o2);
	if (b1 && b2 && dAreConnectedExcluding (b1, b2, dJointTypeContact)) return;

	for (i = 0; i < MAX_CONTACTS; i++)
	{
	    contact[i].surface.mode = dContactBounce | dContactSoftCFM;
		contact[i].surface.mu = dInfinity;
		contact[i].surface.mu2 = 0;
		contact[i].surface.bounce = 0.1;
		contact[i].surface.bounce_vel = 0.1;
		contact[i].surface.soft_cfm = 0.01;
	}
	if (numc = dCollide(o1, o2, MAX_CONTACTS, &contact[0].geom, sizeof(dContact)))
	{
		dMatrix3 RI;
		dRSetIdentity(RI);
		for (i = 0; i < numc; i++)
		{
			dJointID c = dJointCreateContact(ode.world, ode.contactgroup, contact + i);
			dJointAttach(c, b1, b2);
		}
  }
}


/*
==========================
cg_DrawFrame

Called each frame to draw the scene
==========================
*/
int cg_DrawFrame(int time)
{
	// remember the time
	cg.frametime = time - cg.time;
	cg.time = time;

	// cap long frames
	if (cg.frametime > 200)		cg.frametime = 200;
	if (cg.frametime < 1)		cg.frametime = 1;

	// najprv spravim prechod simulacie ODE
	if (cipher_cvar_Bool(run_sim))
	{
		dSpaceCollide(ode.space, 0, &nearCallback);
		dWorldQuickStep(ode.world, 0.001);
	
		dJointGroupEmpty(ode.contactgroup);
	}

	// clear out any dead particle systems
	cipher_ps_ClearParticleSystems();

	// spracuj poziadavky na novy objekt
	cg_AddObject();
	
	// hod novu kocku
	cg_PustObjekt();

	// Clear to white and draw the dancer
	cg_Draw3D();
	cg_Draw2D();
	return 0;
}

/*
==========================
cg_MouseEvent

Called as the mouse or similar device moves
==========================
*/
void cg_MouseEvent(int dx, int dy)
{
	float x, y;

	x = (float) dx * 0.33f;
	y = (float) dy * 0.33f;

	// update the view angles
	cg.viewangles[1] -= x;
	cg.viewangles[0] += y;

	// pitch is clamped to +-80 degrees
	if (cg.viewangles[0]>80)
		cg.viewangles[0] = 80;

	if (cg.viewangles[0]<-80)
		cg.viewangles[0] = -80;
}

/*
==========================
cg_ButtonDown

==========================
*/
void cg_CommandActivate(commandstate_t* b)
{
	int key;
	tchar_t keyname[MAX_STRING_SIZE];

	key = -1;
	cipher_GetArgv(1, keyname, MAX_STRING_SIZE);
	if (keyname[0])
		key = (int) com_atof(keyname);

	// is this a repeat
	if ((key == b->key1) || (key == b->key2))
		return;

	// store it in whichever key is free
	if (b->key1==0)
		b->key1 = key;
	else if (b->key2==0)
		b->key2 = key;
	else
		return;			// third key issuing this command

	// note that the key is down
	b->state = 1;
}

/*
==========================
cg_ButtonUp

==========================
*/
void cg_CommandDeactivate(commandstate_t* b)
{
	int key;
	tchar_t keyname[MAX_STRING_SIZE];

	// Find out which key was involved
	cipher_GetArgv(1, keyname, MAX_STRING_SIZE);
	if (!keyname[0])
	{
		// no key code so clear button state
		b->key1 = b->key2 = 0;
		b->state = 0;
		return;
	}

	// is it one of the keys we know about
	key = (int) com_atof(keyname);
	if (key == b->key1)
		b->key1 = 0;
	else if (key == b->key2)
		b->key2 = 0;
	else
		return;		// up, but no matching down

	// if either of the keys are still active
	// then the button is still held down
	if (b->key1 || b->key2)
		return;

	// take the key up
	b->state = 0;
}

/*
==========================
button command handlers

==========================
*/
void cg_ForwardDown_f(void)	{ cg_CommandActivate(&cmd_forward); }
void cg_ForwardUp_f(void)	{ cg_CommandDeactivate(&cmd_forward); }
void cg_BackwardDown_f(void)	{ cg_CommandActivate(&cmd_backward); }
void cg_BackwardUp_f(void)		{ cg_CommandDeactivate(&cmd_backward); }
void cg_LeftDown_f(void)	{ cg_CommandActivate(&cmd_strafe_left); }
void cg_LeftUp_f(void)		{ cg_CommandDeactivate(&cmd_strafe_left); }
void cg_RightDown_f(void)	{ cg_CommandActivate(&cmd_strafe_right); }
void cg_RightUp_f(void)		{ cg_CommandDeactivate(&cmd_strafe_right); }
void cg_UpDown_f(void)		{ cg_CommandActivate(&cmd_up); }
void cg_UpUp_f(void)		{ cg_CommandDeactivate(&cmd_up); }
void cg_DownDown_f(void)	{ cg_CommandActivate(&cmd_down); }
void cg_DownUp_f(void)		{ cg_CommandDeactivate(&cmd_down); }
void cg_KockaDown_f(void)	{ cg_CommandActivate(&cmd_objekt); }
void cg_KockaUp_f(void)		{ cg_CommandDeactivate(&cmd_objekt); }
void cg_BoostDown_f(void)	{ cg_CommandActivate(&cmd_boost); }
void cg_BoostUp_f(void)		{ cg_CommandDeactivate(&cmd_boost); }
void cg_PustDown_f(void)	{ cg_CommandActivate(&cmd_pust); }
void cg_PustUp_f(void)		{ cg_CommandDeactivate(&cmd_pust); }

/*
==========================
cg_ZrusObjekty

zrusi vsetku objekty pridane do sceny
==========================
*/
void cg_ZrusObjekty(void)
{
	int i;
	tchar_t s_objekty[100];
	
	// nastavi infos pre objekty
	pocet_objektov = 0;
	for (i = 0; i < MAX_OBJEKTOV; i++)
	if (objekty_used[i])
	{
		dBodyDestroy(ode.obj[i].body);
		dGeomDestroy(ode.obj[i].geom[0]);
		objekty_used[i] = 0;
	}

	cg_sprintf(s_objekty, 100, _T("Vsetky objekty boli zrusene\n"), pocet_objektov);
	cipher_con_Print(s_objekty);
}

/*
==========================
cg_ConsoleCommand

cgame's chance to respond to a command. 
returns rfalse if it does not know about the command
==========================
*/
rbool cg_ConsoleCommand(void)
{
	tchar_t command[MAX_STRING_SIZE];

	// see what the command is
	cipher_GetArgv(0, command, MAX_STRING_SIZE);

	if (com_strcmp(command, _T("+forward"))==0)
		cg_ForwardDown_f();
	else if (com_strcmp(command, _T("-forward"))==0)
		cg_ForwardUp_f();
	else if (com_strcmp(command, _T("+backward"))==0)
		cg_BackwardDown_f();
	else if (com_strcmp(command, _T("-backward"))==0)
		cg_BackwardUp_f();
	else if (com_strcmp(command, _T("+left"))==0)
		cg_LeftDown_f();
	else if (com_strcmp(command, _T("-left"))==0)
		cg_LeftUp_f();
	else if (com_strcmp(command, _T("+right"))==0)
		cg_RightDown_f();
	else if (com_strcmp(command, _T("-right"))==0)
		cg_RightUp_f();
	else if (com_strcmp(command, _T("+up"))==0)
		cg_UpDown_f();
	else if (com_strcmp(command, _T("-up"))==0)
		cg_UpUp_f();
	else if (com_strcmp(command, _T("+down"))==0)
		cg_DownDown_f();
	else if (com_strcmp(command, _T("-down"))==0)
		cg_DownUp_f();
	else if (com_strcmp(command, _T("+kocka"))==0)
		cg_KockaDown_f();
	else if (com_strcmp(command, _T("-kocka"))==0)
		cg_KockaUp_f();
	else if (com_strcmp(command, _T("+boost"))==0)
		cg_BoostDown_f();
	else if (com_strcmp(command, _T("-boost"))==0)
		cg_BoostUp_f();
	else if (com_strcmp(command, _T("+pust"))==0)
		cg_PustDown_f();
	else if (com_strcmp(command, _T("-pust"))==0)
		cg_PustUp_f();
	else if (com_strcmp(command, _T("zrus_objekty"))==0)
		cg_ZrusObjekty();
	else
		return rfalse;

	return rtrue;
}

/*
==========================
main_vm

This is the main entry point for cgame
It is used by both dll builds and the vm
The entry point is managed like this for
compatibility with the vm.
==========================
*/
int APIDECL main_vm(int func, ...)
{
	int* args = &func;

	switch (func)
	{
	case CG_INIT:
		return cg_Init();

	case CG_SHUTDOWN:
		cg_ShutDown();
		break;

	case CG_GETVERSION:
		return CG_INTERFACE_VERSION;

	case CG_DRAWFRAME:
		return cg_DrawFrame(args[1]);

	case CG_IN_MOUSEEVENT:
		cg_MouseEvent(args[1], args[2]);
		break;

	case CG_CONSOLECOMMAND:
		return cg_ConsoleCommand();
		break;
		
	default:
		break;
	}

	return 0;
}

