/*<html><pre>*/
#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>

/* Used to fade speed if nothing pressed */
#define	FADE_ACCEL_MULTIPLIER			0.2
#define	FADE_ANGLE_ACCEL_MULTIPLIER		0.5
/* Used to determine if speed is almost zero */
#define	ZERO_SPEED_MULTIPLIER			1e-3
#define	ZERO_ANGLE_SPEED_MULTIPLIER		1e-3

#ifdef	_MSC_VER
/*	Overflow in FP contant arithmetic */
#pragma warning(disable:4056)
#endif	/* _MSC_VER */

/* 6DOF camera methods */
void	V_GLOBAL
camInit(cam_t *cam)
{
	V_ASSERT(cam);
	memZero(cam,sizeof(cam_t));
	cam->modified=true;
	vec3Set(cam->origin,0,-10,0);
	vec3Set(cam->dir,0,1,0);
	vec3Set(cam->up,0,0,1);
	vec2Set(cam->target_distances,4,100);
	vec3Set(cam->baseplane.normal,0,0,1);	/* xOy plane */
	vec2Set(cam->baseplane_distances,0,100);
	cam->baseplane.dist=0;
}

void	V_GLOBAL
camSetTarget(cam_t *cam,vec3_t target)
{
	V_ASSERT(cam);
	if(target)
	{
		vec3Copy(cam->target,target);
		cam->targeted=true;
	}
	else
		cam->targeted=false;
}

void	V_LOCAL
camCalcTransforms(cam_t *cam)
{
	int i;
	vec3_t idir;
	V_ASSERT(cam);
	vec3Unit(cam->dir,cam->dir);
	vec3Unit(cam->up,cam->up);
	vec3Negate(idir,cam->dir);
	mat3x4FromPointDirAndUp(cam->transform,cam->origin,idir,cam->up);
	mat4Zero(cam->gl_transform);
	for(i=0;i<3;i++)
	{
		cam->gl_transform[i][0]=cam->transform[0][i];
		cam->gl_transform[i][1]=cam->transform[1][i];
		cam->gl_transform[i][2]=cam->transform[2][i];
		cam->gl_transform[3][i]=cam->transform[i][3];
	}
	cam->gl_transform[3][3]=1;
	cam->modified=false;
}

void	V_GLOBAL
camGetTransform(cam_t *cam,matrix3x4_t dest)
{
	V_ASSERT(cam);
	if(cam->modified)
		camCalcTransforms(cam);
	mat3x4Copy(dest,cam->transform);
}

void	V_GLOBAL
camLoadGLTransform(cam_t *cam)
{
	V_ASSERT(cam);
	if(cam->modified)
		camCalcTransforms(cam);
#ifndef	CONFIG_DOUBLEVEC
	glLoadMatrixf((GLfloat*)cam->gl_transform);
#else	/* CONFIG_DOUBLEVEC */
	glLoadMatrixd((GLdouble*)cam->gl_transform);
#endif	/* CONFIG_DOUBLEVEC */
}

/*
 *	Changes only orientation of camera; called automatically after
 *	moving of target cameras.
 */
void	V_GLOBAL
camLookAt(cam_t *cam,vec3_t target)
{
	vec_t len;
	vec3_t look, right;
	V_ASSERT(cam);
	vec3Sub(look,target,cam->origin);
	len=vec3Abs(look);
	if(v_abs(len)>1e-3)
	{
		vec3Scale(cam->dir,look,1/len);
		vec3Cross(right,cam->dir,cam->up);
		vec3Unit(right,right);
		vec3Cross(cam->up,right,cam->dir);
	}
}

/*
 *	Computes new origin of camera, so vectors (origin->target) and dir
 *	becomes colinear (camera looks to 'target'); dir & up unchanged.
 *	Called automatically after ROTATION of target cameras.
 */
void	V_GLOBAL
camTargetObject(cam_t *cam,vec3_t target)
{
	vec3_t ot;
	vec_t distance;
	V_ASSERT(cam);
	vec3Sub(ot,target,cam->origin);
	distance=vec3Abs(ot);
	vec3Scale(ot,cam->dir,distance);
	vec3Sub(cam->origin,target,ot);
}

void	V_GLOBAL
camChangeOrigin(cam_t *cam,vec3_t pos)
{
	V_ASSERT(cam);
	if(!cam->targeted)
		vec3Copy(cam->origin,pos);
	else
	{
		vec3_t normal;
		vec_t dist;
		vec3Sub(normal,pos,cam->target);
		dist=vec3Abs(normal);
		if(IsZero(dist))
		{
			vec3Copy(cam->origin,pos);
			return;
		}
		vec3Scale(normal,normal,1/dist);
		/* now we have equation of line passing through camera's target 
		 * and position; check wheither distance is in valid range. */
		if(dist<cam->target_distances[0])
			dist=cam->target_distances[0];
		else
			if(dist>cam->target_distances[1])
				dist=cam->target_distances[1];
		vec3Scale(normal,normal,dist);
		vec3Add(pos,cam->target,normal);
		/* Re - orient camera */
		vec3Copy(cam->origin,pos);
		camLookAt(cam,cam->target);
	}
}

void	V_GLOBAL
camChangeDir(cam_t *cam,vec3_t dir)
{
	vec3_t right;
	V_ASSERT(cam);
	vec3Copy(cam->dir,dir);
	vec3Cross(right,dir,cam->up);
	vec3Unit(right,right);
	vec3Cross(cam->up,right,dir);
	if(cam->targeted)
		camTargetObject(cam,cam->target);
}

void	V_GLOBAL
camChangeUp(cam_t *cam,vec3_t up)
{
	vec3_t right;
	V_ASSERT(cam);
	vec3Copy(cam->up,up);
	vec3Cross(right,cam->dir,up);
	vec3Unit(right,right);
	vec3Cross(cam->dir,up,right);
	if(cam->targeted)
		camTargetObject(cam,cam->target);
}

/*
 *	Rotation order: YAW (angles[1]), than PITCH (angles[0]), than ROLL.
 */
void	V_GLOBAL
camDirAndUpFromAngles(cam_t *cam,vec3_t angles)
{
	vec3_t v,right;
	V_ASSERT(cam);
	/* yaw right around up (Oz) */
	v_fsincos(right+1,right,angles[1]);
	right[2]=0;
	vec3Set(cam->up,0,0,1);
	/* compute dir and pitch it around right */
	vec3Cross(v,cam->up,right);
	vec3RotateAround(cam->dir,right,v,angles[0]);
	/* roll right arounf dir */
	vec3RotateAround(v,cam->dir,right,angles[2]);
	/* compute up */
	vec3Cross(cam->up,v,cam->dir);
	/*
	if(cam->targeted)
		camTargetObject(cam,cam->target);
	*/
}

/* 6DOF controller methods */
void	V_GLOBAL
sdcInit(sdof_control_t *sdc)
{
	V_ASSERT(sdc);
	memZero(sdc,sizeof(sdof_control_t));

	vec3Set(sdc->max_speed,8,16,8);
	vec3Set(sdc->max_angle_speed,3,3,5);
	vec3Set(sdc->accel,40,40,40);
	vec3Set(sdc->angle_accel,8*M_PI,8*M_PI,8*M_PI);

	vec3Set(sdc->step,(vec_t).3,(vec_t).3,(vec_t).3);
	vec3Set(sdc->angle_step,ANGLE_1,ANGLE_1,ANGLE_1);
	
	vec3Scale(sdc->fade_accel,sdc->accel,(vec_t)FADE_ACCEL_MULTIPLIER);
	vec3Scale(sdc->fade_angle_accel,sdc->angle_accel,
		(vec_t)FADE_ANGLE_ACCEL_MULTIPLIER);
	vec3Scale(sdc->zero_speed,sdc->max_speed,(vec_t)ZERO_SPEED_MULTIPLIER);
	vec3Scale(sdc->zero_angle_speed,sdc->max_angle_speed,
		(vec_t)ZERO_ANGLE_SPEED_MULTIPLIER);
}

void	V_GLOBAL
sdcSetStep(sdof_control_t *sdc,vec3_t step)
{
	V_ASSERT(sdc);
	vec3Copy(sdc->step,step);
}

void	V_GLOBAL
sdcSetMaxSpeed(sdof_control_t *sdc,vec3_t max_speed)
{
	V_ASSERT(sdc);
	vec3Copy(sdc->max_speed,max_speed);
	vec3Scale(sdc->zero_speed,sdc->max_speed,(vec_t)ZERO_SPEED_MULTIPLIER);
}

void	V_GLOBAL
sdcSetAccel(sdof_control_t *sdc,vec3_t accel)
{
	V_ASSERT(sdc);
	vec3Copy(sdc->accel,accel);
	vec3Scale(sdc->fade_accel,sdc->accel,(vec_t)FADE_ACCEL_MULTIPLIER);
}

void	V_GLOBAL
sdcSetAngleStep(sdof_control_t *sdc,vec3_t angle_step)
{
	V_ASSERT(sdc);
	vec3Copy(sdc->angle_step,angle_step);
}

void	V_GLOBAL
sdcSetMaxAngleSpeed(sdof_control_t *sdc,vec3_t max_angle_speed)
{
	V_ASSERT(sdc);
	vec3Copy(sdc->max_angle_speed,max_angle_speed);
	vec3Scale(sdc->zero_angle_speed,sdc->max_angle_speed,
		(vec_t)ZERO_ANGLE_SPEED_MULTIPLIER);
}

void	V_GLOBAL
sdcSetAngleAccel(sdof_control_t *sdc,vec3_t angle_accel)
{
	V_ASSERT(sdc);
	vec3Copy(sdc->angle_accel,angle_accel);
	vec3Scale(sdc->fade_angle_accel,sdc->angle_accel,
		FADE_ANGLE_ACCEL_MULTIPLIER);
}

static 	vec3_t	spd_steps={.5f,1.f,.5f};

/*
 *	Descent-like control function.
 *	Changes state of keymap, control and camera.
 */
V_LOCAL	void
sdcDSyncronize(sdof_keymap_t *km,sdof_control_t *sdc,cam_t *cam)
{
	int i;
	vec_t curtime, angle, dist;
	char zero_accel[3]={1,1,1}, zero_angle_accel[3]={1,1,1};
	vec3_t accel={0,0,0}, angle_accel={0,0,0}, right, dp, dmt, drt;
	vec3_t new_origin,prev_speed,prev_angle_speed;
	V_ASSERT(km && sdc && cam);
	curtime=(vec_t)0.001*timerMilliseconds(km->timer);
	/*
	 *	Stepping.
	 */
	/* TODO: implement smooth stepping */
	if(km->stepping_forward)
	{
		/* km->motion_axis[1]-=steps[1]; */
		sdc->speed[1]-=spd_steps[1];
		km->stepping_forward=false;
	}
	if(km->stepping_backward)
	{
		/* km->motion_axis[1]+=steps[1]; */
		sdc->speed[1]+=spd_steps[1];
		km->stepping_backward=false;
	}
	if(km->stepping_left)
	{
		/* km->motion_axis[0]+=steps[0]; */
		sdc->speed[0]+=spd_steps[0];
		km->stepping_left=false;
	}
	if(km->stepping_right)
	{
		/* km->motion_axis[0]-=steps[0]; */
		sdc->speed[0]-=spd_steps[0];
		km->stepping_right=false;
	}
	if(km->stepping_down)
	{
		/* km->motion_axis[2]-=steps[2]; */
		sdc->speed[2]-=spd_steps[2];
		km->stepping_down=false;
	}
	if(km->stepping_up)
	{
		/* km->motion_axis[2]+=steps[2]; */
		sdc->speed[2]+=spd_steps[2];
		km->stepping_up=false;
	}
	/* Calc accels */
	if(km->moving_right)
		accel[0]-=sdc->accel[0];
	if(km->moving_left)
		accel[0]+=sdc->accel[0];
	if(!IsZero(accel[0]))
		zero_accel[0]=0;
	if(km->moving_forward)
		accel[1]-=sdc->accel[1];
	if(km->moving_backward)
		accel[1]+=sdc->accel[1];
	if(!IsZero(accel[1]))
		zero_accel[1]=0;
	if(km->moving_down)
		accel[2]-=sdc->accel[2];
	if(km->moving_up)
		accel[2]+=sdc->accel[2];
	if(!IsZero(accel[2]))
		zero_accel[2]=0;
	if(km->pitching_up)
		angle_accel[0]+=sdc->angle_accel[0];
	if(km->pitching_down)
		angle_accel[0]-=sdc->angle_accel[0];
	if(!IsZero(angle_accel[0]))
		zero_angle_accel[0]=0;
	if(km->yawing_right)
		angle_accel[1]-=sdc->angle_accel[1];
	if(km->yawing_left)
		angle_accel[1]+=sdc->angle_accel[1];
	if(!IsZero(angle_accel[1]))
		zero_angle_accel[1]=0;
	if(km->rolling_cw)
		angle_accel[2]-=sdc->angle_accel[2];
	if(km->rolling_ccw)
		angle_accel[2]+=sdc->angle_accel[2];
	if(!IsZero(angle_accel[2]))
		zero_angle_accel[2]=0;
	/* compute speed deltas, store in accel vector */
	for(i=0;i<3;i++)
	{
		dmt[i]=curtime-km->moving_times[i];
		drt[i]=curtime-km->rotation_times[i];
		if(zero_accel[i])
		{
			if(v_abs(sdc->speed[i])>sdc->zero_speed[i])
				if(sdc->speed[i]>0)
					accel[i]=-sdc->fade_accel[i]*dmt[i];
				else
					accel[i]=sdc->fade_accel[i]*dmt[i];
			else
				sdc->speed[i]=0;
		}
		else
			accel[i]*=dmt[i];
		if(zero_angle_accel[i])
		{
			if(v_abs(sdc->angle_speed[i])>sdc->zero_angle_speed[i])
				if(sdc->angle_speed[i]>0)
					angle_accel[i]=-sdc->fade_angle_accel[i]*drt[i];
				else
					angle_accel[i]=sdc->fade_angle_accel[i]*drt[i];
			else
				sdc->angle_speed[i]=0;
		}
		else
			angle_accel[i]*=drt[i];
	}
	/* Calc sdc speeds and clamp it to max values */
	vec3Copy(prev_speed,sdc->speed);
	vec3Copy(prev_angle_speed,sdc->angle_speed);
	vec3Add(sdc->speed,sdc->speed,accel);
	vec3Add(sdc->angle_speed,sdc->angle_speed,angle_accel);
	for(i=0;i<3;i++)
	{
		/* Prevent oscillation by checking speed sign change */
		if(zero_accel[i])
		{
			if(prev_speed[i]*sdc->speed[i]<0)
				sdc->speed[i]=0;
		}
		if(zero_angle_accel[i])
		{
			if(prev_angle_speed[i]*sdc->angle_speed[i]<0)
				sdc->angle_speed[i]=0;
		}
		if(sdc->speed[i]>sdc->max_speed[i])
    		sdc->speed[i]=sdc->max_speed[i];
    	else
    		if(sdc->speed[i]<-sdc->max_speed[i])
    			sdc->speed[i]=-sdc->max_speed[i];
		if(sdc->angle_speed[i]>sdc->max_angle_speed[i])
			sdc->angle_speed[i]=sdc->max_angle_speed[i];
		else
			if(sdc->angle_speed[i]<-sdc->max_angle_speed[i])
				sdc->angle_speed[i]=-sdc->max_angle_speed[i];
	}
	/*	translation: r-l, f-b, d-u */
	vec3Copy(new_origin,cam->origin);
	dist=km->motion_axis[0]+sdc->speed[0]*dmt[0];
	if(!IsZero(dist))
	{
		vec3Cross(right,cam->dir,cam->up);
		vec3Scale(dp,right,dist);
		vec3Sub(new_origin,new_origin,dp);
	}
	dist=km->motion_axis[1]+sdc->speed[1]*dmt[1];
	if(!IsZero(dist))
	{
		vec3Scale(dp,cam->dir,dist);
		vec3Sub(new_origin,new_origin,dp);
	}
	dist=km->motion_axis[2]+sdc->speed[2]*dmt[2];
	if(!IsZero(dist))
	{
		vec3Scale(dp,cam->up,dist);
		vec3Sub(new_origin,new_origin,dp);
	}
	camChangeOrigin(cam,new_origin);
	/* rotation: u-d, r-l, cw-ccw */
	angle=km->angle_axis[0]+sdc->angle_speed[0]*drt[0];
	if(!IsZero(angle))
	{
		vec3Cross(right,cam->dir,cam->up);
		vec3RotateAround(dp,right,cam->dir,angle);
		vec3Copy(cam->dir,dp);
		/* regenerate up vector */
		vec3Cross(cam->up,right,cam->dir);
	}
	angle=km->angle_axis[1]+sdc->angle_speed[1]*drt[1];
	if(!IsZero(angle))
	{
		vec3RotateAround(dp,cam->up,cam->dir,angle);
		vec3Copy(cam->dir,dp);
	}
	angle=km->angle_axis[2]+sdc->angle_speed[2]*drt[2];
	if(!IsZero(angle))
	{
		vec3RotateAround(dp,cam->dir,cam->up,angle);
		vec3Copy(cam->up,dp);
	}
	if(cam->targeted)
		camTargetObject(cam,cam->target);
	/* update last access time in keymap */
	vec3Zero(km->angle_axis);
	vec3Zero(km->motion_axis);
	vec3Set(km->moving_times,curtime,curtime,curtime);
	vec3Set(km->rotation_times,curtime,curtime,curtime);
	cam->modified=true;
}

void	V_GLOBAL
sdcSyncronize(sdof_keymap_t *km,sdof_control_t *sdc,cam_t *cam)
{
	V_ASSERT(km && sdc && cam);
	if(cam->snapped)
	/*
		sdcQSyncronize(km,sdc,cam) currently screwed up :( */;
	else
		sdcDSyncronize(km,sdc,cam);
}

/*
 * 6DOF controller keymap methods
 */
sdof_keymap_p	V_GLOBAL
sdckCreate(void)
{
	sdof_keymap_p km = (sdof_keymap_p)memAlloc(sizeof(sdof_keymap_t));
	memZero(km,sizeof(sdof_keymap_t));
	/*	My favorite keymap */
	/*	Left hand - motion:
	 *	R,F			- forward and back;
	 *	D,G			- slide left and right;
	 *	Z,A			- slide up and down;
	 */
	km->move_forward=key_R;
	km->move_backward=key_F;
	km->move_left=key_D;
	km->move_right=key_G;
	km->move_up=key_Z;
	km->move_down=key_A;
	/*	Right hand - rotation:
	 *  up,down		- pitch up and down;
	 *	left,right	- yaw left and right;
	 *	del,pgdown	- roll ccw and cw.
	 */
	km->pitch_up=key_up;
	km->pitch_down=key_down;
	km->yaw_left=key_left;
	km->yaw_right=key_right;
	km->roll_cw=key_pagedown;
	km->roll_ccw=key_delete;
	/*
	 *	X - axis contorls YAW;
	 *	Y - axis controls PITCH.
	 */
	km->mouse_x_index=1;
	km->mouse_y_index=0;
	/* FIXME: implement cleanup code */
	km->timer=timerCreate();
	timerStart(km->timer);
	return km;
}

void	V_GLOBAL
sdckDestroy(sdof_keymap_p km)
{
	V_ASSERT(km);
	timerDestroy(km->timer);
	memFree(km);
}

vboolean	V_GLOBAL
sdckProcessKey(sdof_keymap_t *km,input_event_t event,keycode_t code)
{
	vec_t curtime=(vec_t)0.001*timerMilliseconds(km->timer);
	if(event==ev_key_down)
	{
    	if(code==km->move_forward)
    	{
    		km->stepping_forward=true;
    		km->moving_forward=true;
    		km->moving_times[1]=curtime;
    		return true;
    	} else if(code==km->move_backward)
    	{
    		km->stepping_backward=true;
    		km->moving_backward=true;
    		km->moving_times[1]=curtime;
    		return true;
    	} else if(code==km->move_up)
    	{
    		km->stepping_up=true;
    		km->moving_up=true;
    		km->moving_times[2]=curtime;
    		return true;
    	} else if(code==km->move_down)
    	{
    		km->stepping_down=true;
    		km->moving_down=true;
    		km->moving_times[2]=curtime;
    		return true;
    	} else if(code==km->move_left)
    	{
    		km->stepping_left=true;
    		km->moving_left=true;
    		km->moving_times[0]=curtime;
    		return true;
    	} else if(code==km->move_right)
    	{
    		km->stepping_right=true;
    		km->moving_right=true;
    		km->moving_times[0]=curtime;
    		return true;
    	} else if(code==km->yaw_left)
    	{
    		km->yawing_left=true;
    		km->rotation_times[1]=curtime;
    		return true;
    	} else if(code==km->yaw_right)
    	{
    		km->yawing_right=true;
    		km->rotation_times[1]=curtime;
    		return true;
    	} else if(code==km->pitch_up)
    	{
    		km->pitching_up=true;
    		km->rotation_times[0]=curtime;
    		return true;
    	} else if(code==km->pitch_down)
    	{
    		km->pitching_down=true;
    		km->rotation_times[0]=curtime;
    		return true;
    	} else if(code==km->roll_cw)
    	{
    		km->rolling_cw=true;
    		km->rotation_times[2]=curtime;
    		return true;
    	} else if(code==km->roll_ccw)
    	{
    		km->rolling_ccw=true;
    		km->rotation_times[2]=curtime;
    		return true;
    	}
    } else if(event==ev_key_up)
    {
    	/* TODO: update motion_axis[] */
    	if(code==km->move_forward)
    	{
    		km->moving_forward=false;
    		return true;
    	} else if(code==km->move_backward)
    	{
    		km->moving_backward=false;
    		return true;
    	} else if(code==km->move_up)
    	{
    		km->moving_up=false;
    		return true;
    	} else if(code==km->move_down)
    	{
    		km->moving_down=false;
    		return true;
    	} else if(code==km->move_left)
    	{
    		km->moving_left=false;
    		return true;
    	} else if(code==km->move_right)
    	{
    		km->moving_right=false;
    		return true;
    	} else if(code==km->yaw_left)
    	{
    		km->yawing_left=false;
    		return true;
    	} else if(code==km->yaw_right)
    	{
    		km->yawing_right=false;
    		return true;
    	} else if(code==km->pitch_up)
    	{
    		km->pitching_up=false;
    		return true;
    	} else if(code==km->pitch_down)
    	{
    		km->pitching_down=false;
    		return true;
    	} else if(code==km->roll_cw)
    	{
    		km->rolling_cw=false;
    		return true;
    	} else if(code==km->roll_ccw)
    	{
    		km->rolling_ccw=false;
    		return true;
    	}
    }
	return false;
}

/*
 *	Mouse MUST be captured, cursor MUST be placed at 0,0 and then hidden;
 *	when this function returns true, cursor MUST be placed at 0,0 again.
 *	TODO1: change to use sensitivity coefficients (< 0 for inv. axis)
 */
vboolean	V_GLOBAL
sdckProcessMouse(sdof_keymap_t *km,input_record_t *in)
{
	V_ASSERT(in);
	if(in->event!=ev_mouse_move)
		return false;
	km->angle_axis[km->mouse_x_index]=(vec_t)in->first_code;
	km->angle_axis[km->mouse_y_index]=(vec_t)in->second_code;
	/* pitch inverted; my guess, it seems more natural ;) */
	km->angle_axis[0]=-((vec_t)0.005*km->angle_axis[0]);
	km->angle_axis[1]=-((vec_t)0.005*km->angle_axis[1]);
	km->angle_axis[2]=0; /* -((vec_t)0.005*km->angle_axis[2]); */
	return true;
}

/*		==================================================================
 *		Special case: drag-style mouse control
 */
vboolean	V_GLOBAL
sdofMouseDragControl(sdof_keymap_t *km,mouse_drag_control_t *ctl,
	cam_t *cam,input_record_t *in)
{
	vec3_t right, dp;
	s32_t m_x, m_y;
	switch(in->event)
	{
		case ev_mouse_move:
    		m_x=in->first_code;
    		m_y=in->second_code;
    		if(ctl->begin_drag)
    		{
    			ctl->p_x=m_x;
    			ctl->p_y=m_y;
    			ctl->begin_drag=false;
    			return true;
    		}
    		switch(ctl->m_key)
    		{
    			case key_left_mouse:
    				vec3Cross(right,cam->dir,cam->up);
    				vec3Scale(dp,right,
    					-((vec_t)0.02*(m_x-ctl->p_x)));
    				vec3Add(cam->origin,cam->origin,dp);
    				vec3Scale(dp,cam->up,
    					+((vec_t)0.02*(vec_t)(m_y-ctl->p_y)));
    				vec3Add(cam->origin,cam->origin,dp);
    				cam->modified=true;
    				break;
    			case key_right_mouse:
    				km->angle_axis[1]+=((vec_t)0.00118*(vec_t)(m_x-ctl->p_x));
    				km->angle_axis[0]+=((vec_t)0.00118*(vec_t)(m_y-ctl->p_y));
    				break;
    			case key_middle_mouse:
    				if(!cam->snapped)
    					km->angle_axis[2]+=((vec_t)0.00118*(vec_t)(m_x-ctl->p_x));
    				vec3Scale(dp,cam->dir,
    					-((vec_t)0.05*(vec_t)(m_y-ctl->p_y)));
    				vec3Add(cam->origin,cam->origin,dp);
    				cam->modified=true;
    				break;
    			default:
    				break;
    		}
    		ctl->p_x=m_x;
    		ctl->p_y=m_y;
    		return true;
    	case ev_key_up:
    		if(in->first_code>=key_left_mouse && in->first_code<=key_right_mouse)
    		{
    			ctl->m_key=key_unknown;
    			return true;
    		}
    		break;
    	case ev_key_down:
    		if(in->first_code>=key_left_mouse && in->first_code<=key_right_mouse)
    		{
    			if(ctl->emulate_three_buttons && ctl->m_key!=0)
    				ctl->m_key=key_middle_mouse;
    			else
    				ctl->m_key=(keycode_t)in->first_code;
    			ctl->begin_drag=true;
    			return true;
    		}
    	default:
    		break;
	}
	return false;
}

/*</pre></html>*/
/*EOF*/
