Example #1
0
File: ai.cpp Project: mutnig/vdrift
///note that carposition must be in patch space
///returns distance from left side of the track
float GetHorizontalDistanceAlongPatch(const BEZIER & patch, MATHVECTOR <float, 3> carposition)
{
	MATHVECTOR <float, 3> leftside = (patch.GetPoint(0,0) + patch.GetPoint(3,0))*0.5;
	MATHVECTOR <float, 3> rightside = (patch.GetPoint(0,3) + patch.GetPoint(3,3))*0.5;
	MATHVECTOR <float, 3> patchwidthvector = rightside - leftside;
	return patchwidthvector.Normalize().dot(carposition-leftside);
}
Example #2
0
//  3D effects
//--------------------------------------------------------------------------------------------------------------------
void SOUND::Compute3DEffects(std::list <SOUNDSOURCE *> & sources, const MATHVECTOR <float, 3> & listener_pos, const QUATERNION <float> & listener_rot) const
{
	for (std::list <SOUNDSOURCE *>::iterator i = sources.begin(); i != sources.end(); ++i)
	{
		if ((*i)->Get3DEffects())
		{
			MATHVECTOR <float, 3> relvec = (*i)->GetPosition() - listener_pos;
			float len = relvec.Magnitude();
			if (len < 0.1)
			{
				relvec[2] = 0.1;
				len = relvec.Magnitude();
			}
			listener_rot.RotateVector(relvec);

			//  attenuation
			//float cgain = 0.25 / log(100.0) * (log(1000.0) - 1.6 * log(len));
			float cgain = log(1000.0 / pow((double)len, 1.3)) / log(100.0);
			cgain = std::min(1.f, std::max(0.f, cgain));

			//  pan
			float xcoord = -relvec.Normalize()[0];
			float pgain1 = std::max(0.f, -xcoord);
			float pgain2 = std::max(0.f,  xcoord);

			(*i)->SetComputationResults(cgain*(*i)->GetGain()*(1.f-pgain1), cgain*(*i)->GetGain()*(1.f-pgain2));
		}
		else
		{
			(*i)->SetComputationResults((*i)->GetGain(), (*i)->GetGain());
		}
	}
}
void CAMERA_MOUNT::Update(const MATHVECTOR <float, 3> & newpos, const QUATERNION <float> & newdir, float dt)
{
	rotation = newdir * offsetrot;

	MATHVECTOR <float, 3> pos = offset;
	newdir.RotateVector(pos);
	pos = pos + newpos;

	MATHVECTOR <float, 3> vel = pos - position;
	effect = (vel.Magnitude() - 0.02) / 0.04;
	if (effect < 0) effect = 0;
	else if (effect > 1) effect = 1;

	float bumpdiff = randgen.Get();
	float power = pow(bumpdiff, 32);
	if (power < 0) power = 0;
	else if (power > 0.2) power = 0.2;
	float veleffect = std::min(pow(vel.Magnitude() * ( 2.0 - stiffness), 3.0), 1.0);
	float bumpimpulse = power * 130.0 * veleffect;

	float k = 800.0 + stiffness * 800.0 * 4.0;
	float c = 2.0 * std::sqrt(k * mass) * 0.35;
	MATHVECTOR <float, 3> bumpforce = direction::Up * bumpimpulse;
	MATHVECTOR <float, 3> springforce = -displacement * k;
	MATHVECTOR <float, 3> damperforce = -velocity * c;
	
	velocity = velocity + (springforce + damperforce + bumpforce) * dt;
	displacement = displacement + velocity * dt;

	UpdatePosition(pos);
}
Example #4
0
void CARDYNAMICS::ApplyForce(const MATHVECTOR<Dbl,3> & force, const MATHVECTOR<Dbl,3> & offset)
{
	body.ApplyForce(force, offset);
	MATHVECTOR<Dbl,3> fo = offset * force.Magnitude();
	cam_body.ApplyForce((force * gPar.camBncFo + fo * gPar.camBncFof) * fBncMass);
	//chassis->applyForce(ToBulletVector(force), ToBulletVector(offset));
}
Example #5
0
//simple hinge (arc) suspension displacement
MATHVECTOR<Dbl,3> CARDYNAMICS::GetLocalWheelPosition(WHEEL_POSITION wp, Dbl displacement_percent) const
{
	//const
	const MATHVECTOR<Dbl,3> & wheelext = wheel[wp].GetExtendedPosition();
	const MATHVECTOR<Dbl,3> & hinge = suspension[wp].GetHinge();
	MATHVECTOR<Dbl,3> relwheelext = wheelext - hinge;
	MATHVECTOR<Dbl,3> up(0,0,1);
	MATHVECTOR<Dbl,3> rotaxis = up.cross ( relwheelext.Normalize() );
	Dbl hingeradius = relwheelext.Magnitude();
	Dbl travel = suspension[wp].GetTravel();
	//const

	Dbl displacement = displacement_percent * travel;
	Dbl displacementradians = displacement / hingeradius;
	QUATERNION<Dbl> hingerotate;
	hingerotate.Rotate ( -displacementradians, rotaxis[0], rotaxis[1], rotaxis[2] );
	MATHVECTOR<Dbl,3> localwheelpos = relwheelext;
	hingerotate.RotateVector ( localwheelpos );
	return localwheelpos + hinge;
}
Example #6
0
File: ai.cpp Project: mutnig/vdrift
///trim the patch's width in-place
void TrimPatch(BEZIER & patch, float trimleft_front, float trimright_front, float trimleft_back, float trimright_back)
{
	MATHVECTOR <float, 3> frontvector = (patch.GetPoint(0,3) - patch.GetPoint(0,0));
	MATHVECTOR <float, 3> backvector = (patch.GetPoint(3,3) - patch.GetPoint(3,0));
	float frontwidth = frontvector.Magnitude();
	float backwidth = backvector.Magnitude();
	if (trimleft_front + trimright_front > frontwidth)
	{
		float scale = frontwidth/(trimleft_front + trimright_front);
		trimleft_front *= scale;
		trimright_front *= scale;
	}
	if (trimleft_back + trimright_back > backwidth)
	{
		float scale = backwidth/(trimleft_back + trimright_back);
		trimleft_back *= scale;
		trimright_back *= scale;
	}

	MATHVECTOR <float, 3> newfl = patch.GetPoint(0,0);
	MATHVECTOR <float, 3> newfr = patch.GetPoint(0,3);
	MATHVECTOR <float, 3> newbl = patch.GetPoint(3,0);
	MATHVECTOR <float, 3> newbr = patch.GetPoint(3,3);

	if (frontvector.Magnitude() > 0.001)
	{
		MATHVECTOR <float, 3> trimdirection_front = frontvector.Normalize();
		newfl = patch.GetPoint(0,0) + trimdirection_front*trimleft_front;
		newfr = patch.GetPoint(0,3) - trimdirection_front*trimright_front;
	}

	if (backvector.Magnitude() > 0.001)
	{
		MATHVECTOR <float, 3> trimdirection_back = backvector.Normalize();
		newbl = patch.GetPoint(3,0) + trimdirection_back*trimleft_back;
		newbr = patch.GetPoint(3,3) - trimdirection_back*trimright_back;
	}

	patch.SetFromCorners(newfl, newfr, newbl, newbr);
}
///fix invalid normals (my own fault, i suspect.  the DOF converter i wrote may have flipped Y & Z normals)
static bool NeedsNormalSwap(JOEObject & Object)
{
	bool need_normal_flip = false;
	for (int f = 0; f < Object.info.num_frames; f++)
	{
		int normal_flip_count = 0;
		for (int i = 0; i < Object.info.num_faces; i++)
		{
			MATHVECTOR <float,3> tri[3];
			MATHVECTOR <float,3> norms[3];
			for (unsigned int v = 0; v < 3; v++)
			{
				assert(Object.frames[f].faces[i].vertexIndex[v] < Object.frames[f].num_verts);
				assert(Object.frames[f].faces[i].normalIndex[v] < Object.frames[f].num_normals);
				tri[v].Set(Object.frames[f].verts[Object.frames[f].faces[i].vertexIndex[v]].vertex);
				norms[v].Set(Object.frames[f].normals[Object.frames[f].faces[i].normalIndex[v]].vertex);
			}
			MATHVECTOR <float,3> norm;
			for (unsigned int v = 0; v < 3; v++)
				norm = norm + norms[v];
			MATHVECTOR <float,3> tnorm = (tri[2] - tri[0]).cross(tri[1] - tri[0]);
			if (tnorm.Magnitude() > 0.0001 && norm.Magnitude() > 0.0001)
			{
				norm = norm.Normalize();
				tnorm = tnorm.Normalize();
				if (norm.dot(tnorm) < 0.5 && norm.dot(tnorm) > -0.5)
				{
					normal_flip_count++;
					//std::cout << norm.dot(tnorm) << std::endl;
					//std::cout << norm << " -- " << tnorm << std::endl;
				}
			}
		}

		if (normal_flip_count > Object.info.num_faces/4)
			need_normal_flip = true;
	}
	return need_normal_flip;
}
Example #8
0
File: ai.cpp Project: mutnig/vdrift
void AI::analyzeOthers(AI_Car *c, float dt, const std::list <CAR> & othercars)
{
	//const float speed = std::max(1.0f,c->car->GetVelocity().Magnitude());
	const float half_carlength = 1.25; //in meters

	//std::cout << speed << ": " << authority << std::endl;

	//const MATHVECTOR <float, 3> steer_right_axis = direction::Right;
	const MATHVECTOR <float, 3> throttle_axis = direction::Forward;

#ifdef VISUALIZE_AI_DEBUG
	//c->avoidancedraw->ClearLine();
#endif

	for (std::list <CAR>::const_iterator i = othercars.begin(); i != othercars.end(); ++i)
	{
		if (&(*i) != c->car)
		{
			struct AI_Car::OTHERCARINFO & info = c->othercars[&(*i)];

			//find direction of othercar in our frame
			MATHVECTOR <float, 3> relative_position = i->GetCenterOfMassPosition() - c->car->GetCenterOfMassPosition();
			(-c->car->GetOrientation()).RotateVector(relative_position);

			//std::cout << relative_position.dot(throttle_axis) << ", " << relative_position.dot(steer_right_axis) << std::endl;

			//only make a move if the other car is within our distance limit
			float fore_position = relative_position.dot(throttle_axis);
			//float speed_diff = i->GetVelocity().dot(throttle_axis) - c->car->GetVelocity().dot(throttle_axis); //positive if other car is faster

			MATHVECTOR <float, 3> myvel = c->car->GetVelocity();
			MATHVECTOR <float, 3> othervel = i->GetVelocity();
			(-c->car->GetOrientation()).RotateVector(myvel);
			(-i->GetOrientation()).RotateVector(othervel);
			float speed_diff = othervel.dot(throttle_axis) - myvel.dot(throttle_axis); //positive if other car is faster

			//std::cout << speed_diff << std::endl;
			//float distancelimit = clamp(distancelimitcoeff*-speed_diff, distancelimitmin, distancelimitmax);
			const float fore_position_offset = -half_carlength;
			if (fore_position > fore_position_offset)// && fore_position < distancelimit) //only pay attention to cars roughly in front of us
			{
				//float horizontal_distance = relative_position.dot(steer_right_axis); //fallback method if not on a patch
				//float orig_horiz = horizontal_distance;

				const BEZIER * othercarpatch = GetCurrentPatch(&(*i));
				const BEZIER * mycarpatch = GetCurrentPatch(c->car);

				if (othercarpatch && mycarpatch)
				{
					float my_track_placement = GetHorizontalDistanceAlongPatch(*mycarpatch, TransformToPatchspace(c->car->GetCenterOfMassPosition()));
					float their_track_placement = GetHorizontalDistanceAlongPatch(*othercarpatch, TransformToPatchspace(i->GetCenterOfMassPosition()));

					float speed_diff_denom = clamp(speed_diff, -100, -0.01);
					float eta = (fore_position-fore_position_offset)/-speed_diff_denom;

					info.fore_distance = fore_position;

					if (!info.active)
						info.eta = eta;
					else
						info.eta = RateLimit(info.eta, eta, 10.f*dt, 10000.f*dt);

					float horizontal_distance = their_track_placement - my_track_placement;
					//if (!info.active)
						info.horizontal_distance = horizontal_distance;
					/*else
						info.horizontal_distance = RateLimit(info.horizontal_distance, horizontal_distance, spacingdistance*dt, spacingdistance*dt);*/

					//std::cout << info.horizontal_distance << ", " << info.eta << std::endl;

					info.active = true;
				}
				else
					info.active = false;

				//std::cout << orig_horiz << ", " << horizontal_distance << ",    " << fore_position << ", " << speed_diff << std::endl;

				/*if (!min_horizontal_distance)
					min_horizontal_distance = optional <float> (horizontal_distance);
				else if (std::abs(min_horizontal_distance.get()) > std::abs(horizontal_distance))
					min_horizontal_distance = optional <float> (horizontal_distance);*/
			}
			else
				info.active = false;

/*#ifdef VISUALIZE_AI_DEBUG
			if (info.active)
			{
				c->avoidancedraw->AddLinePoint(c->car->GetCenterOfMassPosition());
				MATHVECTOR <float, 3> feeler1(speed*info.eta,0,0);
				c->car->GetOrientation().RotateVector(feeler1);
				MATHVECTOR <float, 3> feeler2(0,-info.horizontal_distance,0);
				c->car->GetOrientation().RotateVector(feeler2);
				c->avoidancedraw->AddLinePoint(c->car->GetCenterOfMassPosition()+feeler1+feeler2);
				c->avoidancedraw->AddLinePoint(c->car->GetCenterOfMassPosition());
			}
#endif*/
		}
	}
}
void MODEL_JOE03::ReadData ( FILE * m_FilePointer, const JOEPACK * pack, JOEObject & Object )
{
	int num_frames = Object.info.num_frames;
	int num_faces = Object.info.num_faces;

	Object.frames.resize(num_frames);

	for ( int i = 0; i < num_frames; i++ )
	{
		Object.frames[i].faces.resize(num_faces);

		BinaryRead ( &Object.frames[i].faces[0], sizeof ( JOEFace ), num_faces, m_FilePointer, pack );
		CorrectEndian ( Object.frames[i].faces );

		BinaryRead ( &Object.frames[i].num_verts, sizeof ( int ), 1, m_FilePointer, pack );
		Object.frames[i].num_verts = ENDIAN_SWAP_32 ( Object.frames[i].num_verts );
		BinaryRead ( &Object.frames[i].num_texcoords, sizeof ( int ), 1, m_FilePointer, pack );
		Object.frames[i].num_texcoords = ENDIAN_SWAP_32 ( Object.frames[i].num_texcoords );
		BinaryRead ( &Object.frames[i].num_normals, sizeof ( int ), 1, m_FilePointer, pack );
		Object.frames[i].num_normals = ENDIAN_SWAP_32 ( Object.frames[i].num_normals );

		Object.frames[i].verts.resize(Object.frames[i].num_verts);
		Object.frames[i].normals.resize(Object.frames[i].num_normals);
		Object.frames[i].texcoords.resize(Object.frames[i].num_texcoords);

		BinaryRead ( &Object.frames[i].verts[0], sizeof ( JOEVertex ), Object.frames[i].num_verts, m_FilePointer, pack );
		CorrectEndian ( Object.frames[i].verts );
		BinaryRead ( &Object.frames[i].normals[0], sizeof ( JOEVertex ), Object.frames[i].num_normals, m_FilePointer, pack );
		CorrectEndian ( Object.frames[i].normals );
		BinaryRead ( &Object.frames[i].texcoords[0], sizeof ( JOETexCoord ), Object.frames[i].num_texcoords, m_FilePointer, pack );
		CorrectEndian ( Object.frames[i].texcoords );
	}

	//cout << "!!! loading " << modelpath << endl;

	//go do scaling
	for (int i = 0; i < num_frames; i++)
	{
		for ( int v = 0; v < Object.frames[i].num_verts; v++ )
		{
			MATHVECTOR <float, 3> temp;

			temp.Set ( Object.frames[i].verts[v].vertex );
			temp = temp * MODEL_SCALE;

			for (int n = 0; n < 3; n++)
				Object.frames[i].verts[v].vertex[n] = temp[n];
		}
	}

	if (NeedsNormalSwap(Object))
	{
		for (int i = 0; i < num_frames; i++)
		{
			for ( int v = 0; v < Object.frames[i].num_normals; v++ )
			{
				std::swap(Object.frames[i].normals[v].vertex[1],
					  Object.frames[i].normals[v].vertex[2]);
				Object.frames[i].normals[v].vertex[1] = -Object.frames[i].normals[v].vertex[1];
			}
		}
		//std::cout << "!!! swapped normals !!!" << std::endl;
	}

	//assert(!NeedsNormalFlip(pObject));

	/*//make sure vertex ordering is consistent with normals
	for (i = 0; i < Object.info.num_faces; i++)
	{
		short vi[3];
		VERTEX tri[3];
		VERTEX norms[3];
		for (unsigned int v = 0; v < 3; v++)
		{
			vi[v] = GetFace(i)[v];
			tri[v].Set(GetVert(vi[v]));
			norms[v].Set(GetNorm(GetNormIdx(i)[v]));
		}
		VERTEX norm;
		for (unsigned int v = 0; v < 3; v++)
			norm = norm + norms[v];
		norm = norm.normalize();
		VERTEX tnorm = (tri[2] - tri[0]).cross(tri[1] - tri[0]);
		if (norm.dot(tnorm) > 0)
		{
			short tvi = Object.frames[0].faces[i].vertexIndex[1];
			Object.frames[0].faces[i].vertexIndex[1] = Object.frames[0].faces[i].vertexIndex[2];
			Object.frames[0].faces[i].vertexIndex[2] = tvi;

			tvi = Object.frames[0].faces[i].normalIndex[1];
			Object.frames[0].faces[i].normalIndex[1] = Object.frames[0].faces[i].normalIndex[2];
			Object.frames[0].faces[i].normalIndex[2] = tvi;

			tvi = Object.frames[0].faces[i].textureIndex[1];
			Object.frames[0].faces[i].textureIndex[1] = Object.frames[0].faces[i].textureIndex[2];
			Object.frames[0].faces[i].textureIndex[2] = tvi;
		}
	}*/

	//build unique vertices
	//cout << "building unique vertices...." << endl;
	int frame(0);

	typedef size_t size_type;

	vector <VERT_ENTRY> vert_master ((size_type)(Object.frames[frame].num_verts));
	vert_master.reserve(Object.frames[frame].num_verts*2);

	vector <int> v_faces((size_type)(Object.info.num_faces*3));

	for (int i = 0; i < Object.info.num_faces; i++)
	{
		for (int v = 0; v < 3; v++)
		{
			VERT_ENTRY & ve = vert_master[Object.frames[frame].faces[i].vertexIndex[v]];
			if (ve.original_index == -1) //first entry
			{
				ve.original_index = Object.frames[frame].faces[i].vertexIndex[v];
				ve.norm_index = Object.frames[frame].faces[i].normalIndex[v];
				ve.tex_index = Object.frames[frame].faces[i].textureIndex[v];
				//if (ve.tex_index < 0)
				assert(ve.tex_index >= 0);

				v_faces[i*3+v] = Object.frames[frame].faces[i].vertexIndex[v];
				//cout << "(first) face " << i << " vert " << v << " index: " << v_faces[i*3+v] << endl;

				//cout << "first entry: " << ve.original_index << "," << ve.norm_index << "," << ve.tex_index << endl;
			}
			else
			{
				//see if we match the pre-existing entry
				if (ve.norm_index == Object.frames[frame].faces[i].normalIndex[v] &&
					ve.tex_index == Object.frames[frame].faces[i].textureIndex[v])
				{
					v_faces[i*3+v] = Object.frames[frame].faces[i].vertexIndex[v];
					assert(ve.tex_index >= 0);
					//cout << "(matched) face " << i << " vert " << v << " index: " << v_faces[i*3+v] << endl;

					//cout << "matched entry: " << ve.original_index << "," << ve.norm_index << "," << ve.tex_index << endl;
				}
				else
				{
					//create a new entry
					vert_master.push_back(VERT_ENTRY());
					vert_master.back().original_index = Object.frames[frame].faces[i].vertexIndex[v];
					vert_master.back().norm_index = Object.frames[frame].faces[i].normalIndex[v];
					vert_master.back().tex_index = Object.frames[frame].faces[i].textureIndex[v];

					assert(vert_master.back().tex_index >= 0);

					v_faces[i*3+v] = vert_master.size()-1;
					//cout << "(new) face " << i << " vert " << v << " index: " << v_faces[i*3+v] << endl;

					//cout << "new entry: " << vert_master.back().original_index << "," << vert_master.back().norm_index << "," << vert_master.back().tex_index << " (" << ve.original_index << "," << ve.norm_index << "," << ve.tex_index << ")" << endl;
				}
			}
		}
	}

	/*for (int i = 0; i < vert_master.size(); i++)
	{
		if (vert_master[i].original_index < 0)
			std::cout << i << ", " << Object.frames[frame].num_verts << ", " << vert_master.size() << std::endl;
		assert(vert_master[i].original_index >= 0);
	}*/

	float newvertnum = vert_master.size();
	/*std::cout << modelpath << " (" << Object.info.num_faces << ") used to have " << Object.frames[frame].num_verts << " vertices, " <<
			Object.frames[frame].num_normals << " normals, " << Object.frames[frame].num_texcoords
			<< " tex coords, now it has " << newvertnum << " combo verts (combo indices)" << std::endl;*/

	//now, fill up the vertices, normals, and texcoords
	vector <float> v_vertices((size_type)(newvertnum*3));
	vector <float> v_texcoords((size_type)(newvertnum*2));
	vector <float> v_normals((size_type)(newvertnum*3));
	for (int i = 0; i < newvertnum; ++i)
	{
		if (vert_master[i].original_index >= 0)
		{
			for (int d = 0; d < 3; d++)
				v_vertices[i*3+d] = Object.frames[frame].verts[vert_master[i].original_index].vertex[d];

			for (int d = 0; d < 3; d++)
				v_normals[i*3+d] = Object.frames[frame].normals[vert_master[i].norm_index].vertex[d];

			//std::cout << i << ", " << vert_master[i].tex_index << ", " << vert_master.size() << ", " << Object.frames[frame].num_texcoords << std::endl;
			//assert(vert_master[i].tex_index >= 0);
			//assert(vert_master[i].tex_index < Object.frames[frame].num_texcoords);
			if (vert_master[i].tex_index < Object.frames[frame].num_texcoords)
			{
				v_texcoords[i*2+0] = Object.frames[frame].texcoords[vert_master[i].tex_index].u;
				v_texcoords[i*2+1] = Object.frames[frame].texcoords[vert_master[i].tex_index].v;
			}
			else
			{
				v_texcoords[i*2+0] = 0;
				v_texcoords[i*2+1] = 0;
			}
		}
	}

	/*for (int i = 0; i < newvertnum; i++)
		cout << v_vertices[i*3] << "," << v_vertices[i*3+1] << "," << v_vertices[i*3+2] << endl;*/

	//assign to our mesh
	m_mesh.SetFaces(&v_faces[0], v_faces.size());
	m_mesh.SetVertices(&v_vertices[0], v_vertices.size());
	m_mesh.SetNormals(&v_normals[0], v_normals.size());
	m_mesh.SetTexCoordSets(1);
	m_mesh.SetTexCoords(0, &v_texcoords[0], v_texcoords.size());
}
Example #10
0
bool BEZIER::IntersectQuadrilateralF(const MATHVECTOR<float,3> & orig, const MATHVECTOR<float,3> & dir,
				     const MATHVECTOR<float,3> & v_00, const MATHVECTOR<float,3> & v_10,
				     const MATHVECTOR<float,3> & v_11, const MATHVECTOR<float,3> & v_01,
				     float &t, float &u, float &v) const
{
	const float EPSILON = 0.000001;
	
	// Reject rays that are parallel to Q, and rays that intersect the plane
	// of Q either on the left of the line V00V01 or below the line V00V10.
	MATHVECTOR<float,3> E_01 = v_10 - v_00;
	MATHVECTOR<float,3> E_03 = v_01 - v_00;
	MATHVECTOR<float,3> P = dir.cross(E_03);
	float det = E_01.dot(P);
	
	if (std::abs(det) < EPSILON) return false;
	
	MATHVECTOR<float,3> T = orig - v_00;
	float alpha = T.dot(P) / det;
	
	if (alpha < 0.0) return false;
	
	MATHVECTOR<float,3> Q = T.cross(E_01);
	float beta = dir.dot(Q) / det;
	
	if (beta < 0.0) return false;
	
	if (alpha + beta > 1.0)
	{
		// Reject rays that that intersect the plane of Q either on
		// the right of the line V11V10 or above the line V11V00.
		MATHVECTOR<float,3> E_23 = v_01 - v_11;
		MATHVECTOR<float,3> E_21 = v_10 - v_11;
		MATHVECTOR<float,3> P_prime = dir.cross(E_21);
		float det_prime = E_23.dot(P_prime);
		
		if (std::abs(det_prime) < EPSILON) return false;
			
		MATHVECTOR<float,3> T_prime = orig - v_11;
		float alpha_prime = T_prime.dot(P_prime) / det_prime;
		
		if (alpha_prime < 0.0) return false;
			
		MATHVECTOR<float,3> Q_prime = T_prime.cross(E_23);
		float beta_prime = dir.dot(Q_prime) / det_prime;
		
		if (beta_prime < 0.0) return false;
	}
	
	// Compute the ray parameter of the intersection point, and
	// reject the ray if it does not hit Q.
	t = E_03.dot(Q) / det;
	
	if (t < 0.0) return false;
	
	// Compute the barycentric coordinates of the fourth vertex.
	// These do not depend on the ray, and can be precomputed
	// and stored with the quadrilateral.
	float alpha_11, beta_11;
	MATHVECTOR<float,3> E_02 = v_11 - v_00;
	MATHVECTOR<float,3> n = E_01.cross(E_03);
	
	if ((std::abs(n[0]) >= std::abs(n[1]))
		    && (std::abs(n[0]) >= std::abs(n[2])))
	{
		alpha_11 = ((E_02[1] * E_03[2]) - (E_02[2] * E_03[1])) / n[0];
		beta_11 = ((E_01[1] * E_02[2]) - (E_01[2]  * E_02[1])) / n[0];
	}
	else if ((std::abs(n[1]) >= std::abs(n[0]))
			 && (std::abs(n[1]) >= std::abs(n[2])))
	{
		alpha_11 = ((E_02[2] * E_03[0]) - (E_02[0] * E_03[2])) / n[1];
		beta_11 = ((E_01[2] * E_02[0]) - (E_01[0]  * E_02[2])) / n[1];
	}
	else
	{
		alpha_11 = ((E_02[0] * E_03[1]) - (E_02[1] * E_03[0])) / n[2];
		beta_11 = ((E_01[0] * E_02[1]) - (E_01[1]  * E_02[0])) / n[2];
	}
	
	// Compute the bilinear coordinates of the intersection point.
	if (std::abs(alpha_11 - (1.0)) < EPSILON)
	{
		// Q is a trapezium.
		u = alpha;
		if (std::abs(beta_11 - (1.0)) < EPSILON) v = beta; // Q is a parallelogram.
		else v = beta / ((u * (beta_11 - (1.0))) + (1.0)); // Q is a trapezium.
	}
	else if (std::abs(beta_11 - (1.0)) < EPSILON)
	{
		// Q is a trapezium.
		v = beta;
		if ( ((v * (alpha_11 - (1.0))) + (1.0)) == 0 )
		{
			return false;
		}
		u = alpha / ((v * (alpha_11 - (1.0))) + (1.0));
	}
	else
	{
		float A = (1.0) - beta_11;
		float B = (alpha * (beta_11 - (1.0)))
				- (beta * (alpha_11 - (1.0))) - (1.0);
		float C = alpha;
		float D = (B * B) - ((4.0) * A * C);
		if (D < 0) return false;
		float Q = (-0.5) * (B + ((B < (0.0) ? (-1.0) : (1.0))
				* std::sqrt(D)));
		u = Q / A;
		if ((u < (0.0)) || (u > (1.0))) u = C / Q;
		v = beta / ((u * (beta_11 - (1.0))) + (1.0));
	}
	
	return true;
}
Example #11
0
//-----------------------------------------------------------------------------------
void CAR::GraphsNewVals(double dt)		 // CAR
{	
	size_t gsi = pApp->graphs.size();
	bool tireEdit = false;

	//  RANGE  gui sld ..
	//const Dbl fMAX = 9000.0, max_y = 80.0, max_x = 1.0;
	//const Dbl fMAX = 7000.0, max_y = 180.0, max_x = 12.0, pow_x = 1.0;
	//const Dbl fMAX = 7000.0, max_y = 5080.0, max_x = 502.0, pow_x = 5.5;
	Dbl fMAX = pSet->te_yf, max_y = pSet->te_xfy, max_x = pSet->te_xfx, pow_x = pSet->te_xf_pow;
	if (pApp->scn->sc->asphalt)  max_y *= 0.5;

	switch (pApp->pSet->graphs_type)
	{
	case Gh_BulletHit:  /// bullet hit  force,normvel, sndnum,scrap,screech
		if (gsi >= 6)
		{
			const CARDYNAMICS& cd = dynamics;
			pApp->graphs[0]->AddVal(std::min(1.f, cd.fHitForce * 2.f));
			pApp->graphs[1]->AddVal(std::min(1.f, cd.fHitForce2));
			pApp->graphs[2]->AddVal(std::min(1.f, cd.fHitForce3));
			pApp->graphs[3]->AddVal(std::min(1.f, cd.fCarScrap));
			pApp->graphs[4]->AddVal(std::min(1.f, cd.fCarScreech));
			pApp->graphs[5]->AddVal(cd.fHitDmgA);
		}
		break;

	case Gh_CarAccelG:  /// car accel x,y,z
		if (gsi >= 3)
		{
			MATHVECTOR<Dbl,3> v = dynamics.body.GetForce();
			(-dynamics.Orientation()).RotateVector(v);
			float m = dynamics.body.GetMass();
			//LogO("mass: "+fToStr(m,1,5)+"  x: "+fToStr(v[0]/m,2,4)+"  y: "+fToStr(v[1]/m,2,4)+"  z: "+fToStr(v[2]/m,2,4));

			for (int i=0; i < 3; ++i)
				pApp->graphs[i]->AddVal( std::max(0.f, std::min(1.f, float(
					v[i]/m *0.63f /9.81f/3.f + (i==2 ? 0.f : 0.5f) ) )));
		}	break;

	case Gh_CamBounce:  /// cam bounce x,y,z
		if (gsi >= 3)
		{
			const MATHVECTOR<Dbl,3> v = dynamics.cam_body.GetPosition();
			for (int i=0; i < 3; ++i)
				pApp->graphs[i]->AddVal( std::max(0.f, std::min(1.f, 
					(float)v[i] * 3.f + 0.5f)));
		}	break;
		
	case Gh_TireSlips:  /// tire slide,slip
		if (gsi >= 8)
		for (int i=0; i < 4; ++i)
		{
			pApp->graphs[i]->AddVal(negPow(dynamics.wheel[i].slips.slideratio, 0.2) * 0.12f +0.5f);
			//pApp->graphs[i]->AddVal(dynamics.wheel[i].slips.slide * 0.1f +0.5f);
			pApp->graphs[i+4]->AddVal(dynamics.wheel[i].slips.slipratio * 0.1f +0.5f);
		}	break;
		
	case Gh_Suspension:  /// suspension
		if (gsi >= 8)
		for (int i=0; i < 4; ++i)
		{
			const CARSUSPENSION& susp = dynamics.GetSuspension((WHEEL_POSITION)i);
			pApp->graphs[i+4]->AddVal( dynamics.hover ?
				susp.GetVelocity() * 0.2f +0.5f : negPow(susp.GetVelocity(), 0.5) * 0.2f +0.5f);
			pApp->graphs[i]->AddVal(susp.GetDisplacementPercent());
		}	break;

		
	case Gh_TorqueCurve:  /// torque curves, gears
	//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
	{	static int ii = 0;  ++ii;  // skip upd cntr
		if (ii >= 1/*! 10*/ && gsi >= 6*2)
		{	ii = 0;
			const CARDYNAMICS& d = dynamics;
			const Dbl fin = d.diff_center.GetFinalDrive();
			const Dbl r = 1.0 / (2 * PI_d * d.wheel[0].GetRadius());

			String s0="  ratio\n", s1="rpmLow\n", s2="velMax\n";  // text legend
			for (int i=0; i < 6; ++i)
			{
				int g = i+1;
				bool bValid = i < d.transmission.GetForwardGears();
				bool bCur = (g == GetGear()) && d.clutch.IsLocked();

				const Dbl gr = bValid ? d.transmission.GetGearRatio(g) : 0.01, grfin = gr * fin;

				Dbl engRpm = d.engine.GetRPM();
				Dbl downRpm = i>0 ? d.DownshiftRPM(g) : d.engine.GetStartRPM();

				Dbl rmax = d.engine.GetRpmMax(),
				    rmin = d.engine.GetStartRPM(), rng = rmax-rmin;
				Dbl rpmOld = 0;

				//  graph  ------
				for (int x = 0; x < 512; ++x)
				{
					// 100 kmh = 28 car.m/s = 15 wh.rps = 102 eng.rps = 6100 eng.rpm
					//       / 3.6     /1.95 (2*PI*r)   *6.87 ratio = (3.9 * 1.76) 3rd gear
					Dbl xx = x/511.0;  // 0..1
					Dbl vel = xx * 250.0 / 3.6;  // 250 kmh max, in m/s
					Dbl whrps = vel * r;  // wheel revs per sec
					Dbl rpm = whrps * grfin * 60.0;  // engine rpm

					Dbl tq = gr * d.engine.GetTorqueCurve(1.0, rpm);
					if (rpm > rmax)  tq = 0.0;  if (rpm < rmin)  tq = 0.0;  // lines down ||

					Dbl v = tq / 2400.0;  // 2400 max
					if (bCur && engRpm > rpmOld && engRpm <= rpm)  // cur rpm mark ^
						v = 1.0;

					pApp->graphs[i]->AddVal(v);  // torque
					if (i>0)
					{	v = rpm < downRpm  //downRpm > rpmOld && downRpm <= rpm
							? 0.0 : std::max(0.0, std::min(1.0, (rpm-rmin)/rng ));  // rpm range
						pApp->graphs[i+6]->AddVal(v);  }
					rpmOld = rpm;
				}
				pApp->graphs[i]->SetUpdate();
				if (i>0)
				pApp->graphs[i+6]->SetUpdate();

				//  text  ------
				if (bValid)
				{
					Dbl velMax = 3.6 * d.engine.GetRpmMax() / (grfin * 60.0) / r;
					if (!bValid)  velMax = 0;
					
					s0 += toStr(g)+": "+fToStr(gr,3,5)+"\n";
					s1 += fToStr(downRpm,0,4)+"\n";
					s2 += fToStr(velMax,0,3)+"\n";
				}
			}
			s0 += "  final\n    "+fToStr(fin,3,5);
			pApp->graphs[0]->UpdTitle(s0);  pApp->graphs[1]->UpdTitle(s1);  pApp->graphs[2]->UpdTitle(s2);
	}	}	break;

		
	case Gh_Engine:  /// engine torque, power
	{	static int ii = 0;  ++ii;  // skip upd cntr
		if (ii >= 10 && gsi >= 2)
		{	ii = 0;
			const CARENGINE& eng = dynamics.engine;
			float maxTrq = 0.f, maxPwr = 0.f;
			int rpmMaxTq = 0, rpmMaxPwr = 0;
			float rmin = eng.GetStartRPM(), rmax = eng.GetRpmMax(), rng = rmax - rmin;

			for (int x = 0; x < 512; ++x)
			{
				float r = x/512.f * rng + rmin;
				float tq = eng.GetTorqueCurve(1.0, r);
				float pwr = tq * 2.0 * PI_d * r / 60.0 * 0.001 * 1.341;  //kW  // 1kW = 1.341 bhp
				if (tq > maxTrq)  {  maxTrq = tq;  rpmMaxTq = r;  }
				if (pwr > maxPwr)  {  maxPwr = pwr;  rpmMaxPwr = r;  }

				pApp->graphs[0]->AddVal( tq / 600.0 );
				pApp->graphs[1]->AddVal( pwr / 600.0 );
			}
			pApp->graphs[0]->SetUpdate();
			pApp->graphs[1]->SetUpdate();
			//pApp->graphs[0]->UpdTitle(ss);
	}	}	break;

	
	case Gh_Clutch:  /// clutch,rpm,gears
		if (gsi >= 4)
		{
			const CARENGINE& eng = dynamics.engine;
			const CARCLUTCH& clu = dynamics.clutch;
			pApp->graphs[0]->AddVal(eng.GetRPM() / 7500.0);
			#if 0
				MATHVECTOR<Dbl,3> vel = dynamics.GetVelocity(), vx(1,0,0);
				dynamics.Orientation().RotateVector(vx);
				Dbl d = vel.dot(vx),
					velCar = vel.Magnitude() * (d >= 0.0 ? 1 : -1), velWh = dynamics.GetSpeedMPS();
				//pApp->graphs[1]->AddVal(velCar * 0.02f));
				//pApp->graphs[2]->AddVal(velWh * 0.02f);
				if (velWh < 1.1f && velWh > -1.1f)
					d = 1.f;
				else
					d = fabs(velCar >= velWh ? velCar/velWh : velWh/velCar);
				pApp->graphs[2]->AddVal(/*std::min(1.f, std::max(1.f,*/ d * 0.5f);
			#else
			pApp->graphs[1]->AddVal(clu.GetClutch() * 0.3f + 0.15f);
			pApp->graphs[2]->AddVal(clu.IsLocked() ? 0.15f : 0.f);
			#endif
			pApp->graphs[3]->AddVal(GetGear() / 6.f);
		}	break;


	case Gh_Diffs:  /// differentials
		if (gsi >= 6)
		for (int i=0; i < 3*2; ++i)
		{
			CARDIFFERENTIAL* diff;
			switch (i%3)
			{
			case 0: diff = &dynamics.diff_front;  break;
			case 1: diff = &dynamics.diff_rear;  break;
			case 2: diff = &dynamics.diff_center;  break;
			}
			Dbl d;
			if (i/3==0) {
				d = diff->GetSide1Speed() - diff->GetSide2Speed();
				d = negPow(d, 0.4)*0.07 + 0.5;  }  // blue
			else {
				d = diff->GetSide1Torque() - diff->GetSide2Torque();
				d = d*0.0004 + 0.5;  }  // orange
			pApp->graphs[i]->AddVal(d);
		}	break;
		

	case Gh_TireEdit:  /// tire pacejka
	//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
	{	static int ii = 0;  ++ii;  // skip upd cntr
		const int im = pApp->iUpdTireGr > 0 ? 2 : 8;  // faster when editing val
		if (ii >= im && gsi >= TireNG*2)
		{	ii = 0;  pApp->iUpdTireGr = 0;

			const CARTIRE* tire = dynamics.GetTire(FRONT_LEFT);
			Dbl* ft = new Dbl[TireLenG];

			Dbl fmin, fmax, frng, maxF;
			const bool common = 1;  // common range for all
			const bool cust = 1;

			///  Fy lateral --
			for (int i=0; i < TireNG; ++i)
			{
				bool comi = common || i == 0;
				if (comi)
				{	fmin = FLT_MAX;  fmax = FLT_MIN;  frng = 0.0;  }
				
				for (int x=0; x < TireLenG; ++x)
				{	Dbl x0 = Dbl(x) / TireLenG;	 //Dbl yy = max_y * 2.0 * (x-LEN*0.5) / LEN;
					Dbl yy = max_y * pow(x0, pow_x);
					Dbl n = (TireNG-1-i+1) * 0.65;
					Dbl fy = !pApp->iTireLoad ? tire->Pacejka_Fy(yy, n, 0, 1.0, maxF)  // normF
											: tire->Pacejka_Fy(yy, 3, n-2, 1.0, maxF); // camber
					ft[x] = fy;

					if (comi)  // get min, max
					{	if (fy < fmin)  fmin = fy;
						if (fy > fmax)  fmax = fy;  }
				}
				if (comi)  // get range
					frng = 1.0 / (fmax - fmin);
				if (cust)
				{	fmax = fMAX;  fmin = 0.0;  frng = 1.0 / (fmax - fmin);  }
				
				for (int x = 0; x < TireLenG; ++x)
					pApp->graphs[i]->AddVal( (ft[x] - fmin) * frng );
				pApp->graphs[i]->SetUpdate();

				if (i==0)
					pApp->graphs[i]->UpdTitle("Fy Lateral--\n"
						"max y "+fToStr((float)fmax,0,1)+"  x "+fToStr(max_y,0,1)+"\n");
			}

			///  Fx long |
			for (int i=0; i < TireNG; ++i)
			{
				bool comi = common || i == 0;
				if (comi)
				{	fmin = FLT_MAX;  fmax = FLT_MIN;  frng = 0.0;  }
				
				for (int x=0; x < TireLenG; ++x)
				{	Dbl x0 = Dbl(x) / TireLenG;
					Dbl xx = max_x * pow(x0, pow_x);
					Dbl n = (TireNG-1-i+1) * 0.65;
					Dbl fx = pApp->iEdTire != 2 ? tire->Pacejka_Fx(xx, n, 1.0, maxF)  // normF
							 : (!pApp->iTireLoad ? tire->Pacejka_Mz(xx, 0, n, 0.0, 1.0, maxF)    // align- norm
												 : tire->Pacejka_Mz(0, xx, n, 0.0, 1.0, maxF));  // align- camber
					ft[x] = fx;

					if (comi)  // get min, max
					{	if (fx < fmin)  fmin = fx;
						if (fx > fmax)  fmax = fx;  }
				}
				if (comi)  // get range
					frng = 1.0 / (fmax - fmin);
				if (cust)
				{	fmax = fMAX;  fmin = 0.0;  frng = 1.0 / (fmax - fmin);  }
				
				for (int x = 0; x < TireLenG; ++x)
					pApp->graphs[i+TireNG]->AddVal( (ft[x] - fmin) * frng );
				pApp->graphs[i+TireNG]->SetUpdate();

				if (i==0)
					pApp->graphs[i+TireNG]->UpdTitle("Fx Longit |\n"
						"max y "+fToStr((float)fmax,0,1)+"  x "+fToStr(max_x,0,1)+"\n");
			}
			delete[]ft;
		}
	}	tireEdit = true;
	break;
	
	case Gh_Tires4Edit:  /// all tires pacejka vis, edit
	//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
	{	static int ii = 0;  ++ii;  // skip upd cntr
		const int im = 1;  //pApp->iUpdTireGr > 0 ? 2 : 8;  // faster when editing val
		if (ii >= im && gsi >= TireNG*2)
		{	ii = 0;  pApp->iUpdTireGr = 0;

			Dbl* ft = new Dbl[TireLenG];

			Dbl fmin, fmax, frng, maxF;
			const bool common = 1;  // common range for all
			const bool cust = 1;

			///  Fy lateral --  ........
			for (int i=0; i < TireNG; ++i)
			{
				WHEEL_POSITION wp = (WHEEL_POSITION)i;
				const CARTIRE* tire = dynamics.GetTire(wp);
				const CARWHEEL& wh = dynamics.GetWheel(wp);
				const CARWHEEL::SlideSlip& t = wh.slips;

				bool comi = common || i == 0;
				if (comi)
				{	fmin = FLT_MAX;  fmax = FLT_MIN;  frng = 0.0;  }
				
				Dbl yyo=0;
				for (int x=0; x < TireLenG; ++x)
				{	Dbl x0 = Dbl(x) / TireLenG;
					Dbl yy = max_y * pow(x0, pow_x);
					Dbl fy = fabs(t.fy_ar * tire->Pacejka_Fy(yy, t.Fz, t.gamma, t.frict, maxF));

					if (t.fy_rar > yyo && t.fy_rar <= yy)
						fy = 0.0;  // cur mark |
					yyo = yy;

					ft[x] = fy;
					if (comi)  // get min, max
					{	if (fy < fmin)  fmin = fy;
						if (fy > fmax)  fmax = fy;  }
				}
				if (comi)  // get range
					frng = 1.0 / (fmax - fmin);
				if (cust)
				{	fmax = fMAX;  fmin = 0.0;  frng = 1.0 / (fmax - fmin);  }
				
				for (int x = 0; x < TireLenG; ++x)
					pApp->graphs[i]->AddVal( (ft[x] - fmin) * frng );
				pApp->graphs[i]->SetUpdate();

				if (i==0)
					pApp->graphs[i]->UpdTitle("Fy Lateral --   "
						/*"max y "+fToStr((float)fmax,0,1)+"  x "+fToStr(max_y,0,1)+"\n"/**/);
			}

			///  Fx long |  ........
			for (int i=0; i < TireNG; ++i)
			{
				WHEEL_POSITION wp = (WHEEL_POSITION)i;
				const CARTIRE* tire = dynamics.GetTire(wp);
				const CARWHEEL& wh = dynamics.GetWheel(wp);
				const CARWHEEL::SlideSlip& t = wh.slips;

				bool comi = common || i == 0;
				if (comi)
				{	fmin = FLT_MAX;  fmax = FLT_MIN;  frng = 0.0;  }
				
				Dbl xxo=0;
				for (int x=0; x < TireLenG; ++x)
				{	Dbl x0 = Dbl(x) / TireLenG;
					Dbl xx = max_x * pow(x0, pow_x);
					Dbl fx = fabs(t.fx_sr * tire->Pacejka_Fx(xx, t.Fz, t.frict, maxF));

					if (t.fx_rsr > xxo && t.fx_rsr <= xx)
						fx = 0.0;  // cur mark |
					xxo = xx;

					ft[x] = fx;
					if (comi)  // get min, max
					{	if (fx < fmin)  fmin = fx;
						if (fx > fmax)  fmax = fx;  }
				}
				if (comi)  // get range
					frng = 1.0 / (fmax - fmin);
				if (cust)
				{	fmax = fMAX;  fmin = 0.0;  frng = 1.0 / (fmax - fmin);  }
				
				for (int x = 0; x < TireLenG; ++x)
					pApp->graphs[i+TireNG]->AddVal( (ft[x] - fmin) * frng );
				pApp->graphs[i+TireNG]->SetUpdate();

				if (i==0)
					pApp->graphs[i+TireNG]->UpdTitle("Fx Longit |   "
						/*"max y "+fToStr((float)fmax,0,1)+"  x "+fToStr(max_x,0,1)+"\n"/**/);
			}
			delete[]ft;
		}
	}	tireEdit = true;
	break;
	}

	if (tireEdit)
	{	const CGui* g = pApp->gui;
		const CARTIRE* tire = dynamics.GetTire(FRONT_LEFT);
		String ss,sd;
		if (pApp->iEdTire == 0)
		{
			ss += "#A0F0FF--Lateral--\n";  sd += g->sCommon;
			for (int i=0; i < tire->lateral.size(); ++i)
			{
				float f = tire->lateral[i];
				char p = f > 100 ? 0 : (f > 10 ? 1 : (f > 1 ? 2 : 3));
				bool cur = (i == pApp->iCurLat);

				ss += cur ? "#A0EEFF" : "#E0FFFF";
				ss += g->csLateral[i][0] +" "+ fToStr(f, p,5);
				ss += cur ? "  <\n" : "\n";
				sd += g->csLateral[i][1] +"\n";
			}

			ss += "\n#C0F0F0alpha hat\n";
			int z = (int)tire->alpha_hat.size()-1;
			ss += "  "+fToStr( tire->alpha_hat[0], 3,5) + "\n";
			ss += "  "+fToStr( tire->alpha_hat[z/2], 3,5) + "\n";
			ss += "  "+fToStr( tire->alpha_hat[z], 3,5) + "\n";
		}
		else if (pApp->iEdTire == 1)
		{
			ss += "#FFFF70| Longit |\n";  sd += g->sCommon;
			for (int i=0; i < tire->longitudinal.size(); ++i)
			{
				float f = tire->longitudinal[i];
				char p = f > 100 ? 0 : (f > 10 ? 1 : (f > 1 ? 2 : 3));
				bool cur = (i == pApp->iCurLong);

				ss += cur ? "#FFE090" : "#FFFFD0";
				ss += g->csLongit[i][0] +" "+ fToStr(f, p,5);
				ss += cur ? "  <\n" : "\n";
				sd += g->csLongit[i][1] +"\n";
			}

			ss += "\n#F0F0C0sigma hat\n";
			int z = (int)tire->sigma_hat.size()-1;
			ss += "  "+fToStr( tire->sigma_hat[0], 3,5) + "\n";
			ss += "  "+fToStr( tire->sigma_hat[z/2], 3,5) + "\n";
			ss += "  "+fToStr( tire->sigma_hat[z], 3,5) + "\n";
		}
		
		pApp->graphs[gsi-2]->UpdTitle(ss);
		pApp->graphs[gsi-1]->UpdTitle(sd);
	}
}
void CarModel::LoadConfig(const std::string & pathCar)
{
	Defaults();

	///  load  -----
	CONFIGFILE cf;
	if (!cf.Load(pathCar))
	{  LogO("!! CarModel: Can't load .car "+pathCar);  return;  }


	//-  custom interior model offset
	cf.GetParam("model_ofs.interior-x", interiorOffset[0]);
	cf.GetParam("model_ofs.interior-y", interiorOffset[1]);
	cf.GetParam("model_ofs.interior-z", interiorOffset[2]);
	cf.GetParam("model_ofs.rot_fix", bRotFix);

	//~  boost offset
	cf.GetParam("model_ofs.boost-x", boostOffset[0]);
	cf.GetParam("model_ofs.boost-y", boostOffset[1]);
	cf.GetParam("model_ofs.boost-z", boostOffset[2]);
	cf.GetParam("model_ofs.boost-size-z", boostSizeZ);
	cf.GetParam("model_ofs.boost-name", sBoostParName);
	
	//  thruster  spaceship hover
	cf.GetParam("model_ofs.thrust-x", thrusterOfs[0]);
	cf.GetParam("model_ofs.thrust-y", thrusterOfs[1]);
	cf.GetParam("model_ofs.thrust-z", thrusterOfs[2]);
	cf.GetParam("model_ofs.thrust-size-z", thrusterSizeZ);
	cf.GetParam("model_ofs.thrust-name", sThrusterPar);
	

	//~  brake flares
	float pos[3];  bool ok=true;  int i=0;
	while (ok)
	{	ok = cf.GetParam("flares.brake-pos"+toStr(i), pos);  ++i;
		if (ok)  brakePos.push_back(bRotFix ? Vector3(-pos[0],pos[2],pos[1]) : Vector3(-pos[1],-pos[2],pos[0]));
	}
	cf.GetParam("flares.brake-color", pos);
	brakeClr = ColourValue(pos[0],pos[1],pos[2]);
	cf.GetParam("flares.brake-size", brakeSize);
	
	
	//-  custom exhaust pos for boost particles
	if (cf.GetParam("model_ofs.exhaust-x", exhaustPos[0]))
	{
		manualExhaustPos = true;
		cf.GetParam("model_ofs.exhaust-y", exhaustPos[1]);
		cf.GetParam("model_ofs.exhaust-z", exhaustPos[2]);
	}else
		manualExhaustPos = false;
	if (!cf.GetParam("model_ofs.exhaust-mirror-second", has2exhausts))
		has2exhausts = false;


	//- load cameras pos
	cf.GetParam("driver.view-position", pos, pGame->error_output);
	driver_view[0]=pos[1]; driver_view[1]=-pos[0]; driver_view[2]=pos[2];
	
	cf.GetParam("driver.hood-position", pos, pGame->error_output);
	hood_view[0]=pos[1]; hood_view[1]=-pos[0]; hood_view[2]=pos[2];


	//  tire params
	WHEEL_POSITION leftside = FRONT_LEFT, rightside = FRONT_RIGHT;
	float value;
	bool both = cf.GetParam("tire-both.radius", value);
	std::string posstr = both ? "both" : "front";

	for (int p = 0; p < 2; ++p)
	{
		if (p == 1)
		{
			leftside = REAR_LEFT;
			rightside = REAR_RIGHT;
			if (!both)  posstr = "rear";
		}
		float radius;
		cf.GetParam("tire-"+posstr+".radius", radius, pGame->error_output);
		whRadius[leftside] = radius;
		whRadius[rightside] = radius;
		
		float width = 0.2f;
		cf.GetParam("tire-"+posstr+".width-trail", width);
		whWidth[leftside] = width;
		whWidth[rightside] = width;
	}
	
	//  wheel pos
	//  for track's ghost or garage view
	int version(1);
	cf.GetParam("version", version);
	for (int i = 0; i < 4; ++i)
	{
		std::string sPos;
		if (i == 0)			sPos = "FL";
		else if (i == 1)	sPos = "FR";
		else if (i == 2)	sPos = "RL";
		else				sPos = "RR";

		float pos[3];
		MATHVECTOR<float,3> vec;

		cf.GetParam("wheel-"+sPos+".position", pos, pGame->error_output);
		if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
		vec.Set(pos[0],pos[1], pos[2]);
		whPos[i] = vec;
	}
	//  steer angle
	maxangle = 26.f;
	cf.GetParam("steering.max-angle", maxangle, pGame->error_output);
	maxangle *= pGame->GetSteerRange();
}
Example #13
0
void MODEL::GenerateMeshMetrics()
{
	float maxv[3] = {0, 0, 0};
	float minv[3] = {0, 0, 0};
	bool havevals[6];
	for ( int n = 0; n < 6; n++ )
		havevals[n] = false;

	const float * verts;
	int vnum;
	mesh.GetVertices(verts, vnum);
	vnum = vnum / 3;
	for ( int v = 0; v < vnum; v++ )
	{
		MATHVECTOR <float, 3> temp;

		temp.Set ( verts + v*3 );
		
		//cout << verts[v*3] << "," << verts[v*3+1] << "," << verts[v*3+2] << endl;
	
		//cache for bbox stuff
		for ( int n = 0; n < 3; n++ )
		{
			if (!havevals[n])
			{
				maxv[n] = temp[n];
				havevals[n] = true;
			}
			else if (temp[n] > maxv[n])
				maxv[n] = temp[n];
			
			if (!havevals[n+3])
			{
				minv[n] = temp[n];
				havevals[n+3] = true;
			}
			else if (temp[n] < minv[n])
				minv[n] = temp[n];
		}

		float r = temp.Magnitude();
		MATHVECTOR <float, 2> tempxz;
		tempxz.Set(temp[0], temp[2]);
		float rxz = tempxz.Magnitude();
		if ( r > radius )
			radius = r;
		if ( rxz > radiusxz )
			radiusxz = rxz;
	}

	bboxmin.Set(minv[0], minv[1], minv[2]);
	bboxmax.Set(maxv[0], maxv[1], maxv[2]);
	
	MATHVECTOR <float, 3> center;
	center = (bboxmin + bboxmax)*0.5;
	radius = (bboxmin - center).Magnitude();
	
	MATHVECTOR <float, 3> minv_noy = bboxmin;
	minv_noy[1] = 0;
	center[1] = 0;
	radiusxz = (minv_noy - center).Magnitude();
	
	generatedmetrics = true;
}
Example #14
0
//-----------------------------------------------------------------------------------
void CAR::GraphsNewVals(double dt)		 // CAR
{	
	size_t gsi = pApp->graphs.size();
	if (pApp->pSet->graphs_type != Gh_TireEdit)
	switch (pApp->pSet->graphs_type)
	{
	case Gh_BulletHit:  /// bullet hit  force,normvel, sndnum,scrap,screech
		if (gsi >= 6)
		{
			const CARDYNAMICS& cd = dynamics;
			pApp->graphs[0]->AddVal(std::min(1.f, cd.fHitForce * 2.f));
			pApp->graphs[1]->AddVal(std::min(1.f, cd.fHitForce2));
			pApp->graphs[2]->AddVal(std::min(1.f, cd.fHitForce3));
			pApp->graphs[3]->AddVal(std::min(1.f, cd.fCarScrap));
			pApp->graphs[4]->AddVal(std::min(1.f, cd.fCarScreech));
			pApp->graphs[5]->AddVal(cd.fHitDmgA);
		}
		break;

	case Gh_CarAccelG:  /// car accel x,y,z
		if (gsi >= 3)
		{
			MATHVECTOR<Dbl,3> v = dynamics.body.GetForce();
			(-dynamics.Orientation()).RotateVector(v);
			float m = dynamics.body.GetMass();
			//LogO("mass: "+fToStr(m,1,5)+"  x: "+fToStr(v[0]/m,2,4)+"  y: "+fToStr(v[1]/m,2,4)+"  z: "+fToStr(v[2]/m,2,4));

			for (int i=0; i < 3; ++i)
				pApp->graphs[i]->AddVal( std::max(0.f, std::min(1.f, float(
					v[i]/m *0.63f /9.81f/3.f + (i==2 ? 0.f : 0.5f) ) )));
		}	break;
		
	case Gh_TireSlips:  /// tire slide,slip
		if (gsi >= 8)
		for (int i=0; i < 4; ++i)
		{
			pApp->graphs[i]->AddVal(negPow(dynamics.wheel[i].slips.slideratio, 0.2) * 0.12f +0.5f);
			//pApp->graphs[i]->AddVal(dynamics.wheel[i].slips.slide * 0.1f +0.5f);
			pApp->graphs[i+4]->AddVal(dynamics.wheel[i].slips.slipratio * 0.1f +0.5f);
		}	break;
		
	case Gh_Suspension:  /// suspension
		if (gsi >= 8)
		for (int i=0; i < 4; ++i)
		{
			const CARSUSPENSION& susp = dynamics.GetSuspension((WHEEL_POSITION)i);
			pApp->graphs[i+4]->AddVal(negPow(susp.GetVelocity(), 0.5) * 0.2f +0.5f);
			pApp->graphs[i]->AddVal(susp.GetDisplacementPercent());
		}	break;

		
	case Gh_TorqueCurve:  /// torque curves, gears
	//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
	{	static int ii = 0;  ++ii;  // skip upd cntr
		if (ii >= 1/*! 10*/ && gsi >= 6*2)
		{	ii = 0;
			const CARDYNAMICS& d = dynamics;
			const Dbl fin = d.diff_center.GetFinalDrive();
			const Dbl r = 1.0 / (2 * PI_d * d.wheel[0].GetRadius());

			String s0="  ratio\n", s1="rpmLow\n", s2="velMax\n";  // text legend
			for (int i=0; i < 6; ++i)
			{
				int g = i+1;
				bool bValid = i < d.transmission.GetForwardGears();
				bool bCur = (g == GetGear()) && d.clutch.IsLocked();

				const Dbl gr = bValid ? d.transmission.GetGearRatio(g) : 0.01, grfin = gr * fin;

				Dbl engRpm = d.engine.GetRPM();
				Dbl downRpm = i>0 ? d.DownshiftRPM(g) : d.engine.GetStartRPM();

				Dbl rmax = d.engine.GetRpmMax(),
				    rmin = d.engine.GetStartRPM(), rng = rmax-rmin;
				Dbl rpmOld = 0;

				//  graph  ------
				for (int x = 0; x < 512; ++x)
				{
					// 100 kmh = 28 car.m/s = 15 wh.rps = 102 eng.rps = 6100 eng.rpm
					//       / 3.6     /1.95 (2*PI*r)   *6.87 ratio = (3.9 * 1.76) 3rd gear
					Dbl xx = x/511.0;  // 0..1
					Dbl vel = xx * 250.0 / 3.6;  // 250 kmh max, in m/s
					Dbl whrps = vel * r;  // wheel revs per sec
					Dbl rpm = whrps * grfin * 60.0;  // engine rpm

					Dbl tq = gr * d.engine.GetTorqueCurve(1.0, rpm);
					if (rpm > rmax)  tq = 0.0;  if (rpm < rmin)  tq = 0.0;  // lines down ||

					Dbl v = tq / 2400.0;  // 2400 max
					if (bCur && engRpm > rpmOld && engRpm <= rpm)  // cur rpm mark ^
						v = 1.0;

					pApp->graphs[i]->AddVal(v);  // torque
					if (i>0)
					{	v = rpm < downRpm  //downRpm > rpmOld && downRpm <= rpm
							? 0.0 : std::max(0.0, std::min(1.0, (rpm-rmin)/rng ));  // rpm range
						pApp->graphs[i+6]->AddVal(v);  }
					rpmOld = rpm;
				}
				pApp->graphs[i]->SetUpdate();
				if (i>0)
				pApp->graphs[i+6]->SetUpdate();

				//  text  ------
				if (bValid)
				{
					Dbl velMax = 3.6 * d.engine.GetRpmMax() / (grfin * 60.0) / r;
					if (!bValid)  velMax = 0;
					
					s0 += toStr(g)+": "+fToStr(gr,3,5)+"\n";
					s1 += fToStr(downRpm,0,4)+"\n";
					s2 += fToStr(velMax,0,3)+"\n";
				}
			}
			s0 += "  final\n    "+fToStr(fin,3,5);
			pApp->graphs[0]->UpdTitle(s0);  pApp->graphs[1]->UpdTitle(s1);  pApp->graphs[2]->UpdTitle(s2);
	}	}	break;

		
	case Gh_Engine:  /// engine torque, power
	{	static int ii = 0;  ++ii;  // skip upd cntr
		if (ii >= 10 && gsi >= 2)
		{	ii = 0;
			const CARENGINE& eng = dynamics.engine;
			float maxTrq = 0.f, maxPwr = 0.f;
			int rpmMaxTq = 0, rpmMaxPwr = 0;
			float rmin = eng.GetStartRPM(), rmax = eng.GetRpmMax(), rng = rmax - rmin;

			for (int x = 0; x < 512; ++x)
			{
				float r = x/512.f * rng + rmin;
				float tq = eng.GetTorqueCurve(1.0, r);
				float pwr = tq * 2.0 * PI_d * r / 60.0 * 0.001 * 1.341;  //kW  // 1kW = 1.341 bhp
				if (tq > maxTrq)  {  maxTrq = tq;  rpmMaxTq = r;  }
				if (pwr > maxPwr)  {  maxPwr = pwr;  rpmMaxPwr = r;  }

				pApp->graphs[0]->AddVal( tq / 600.0 );
				pApp->graphs[1]->AddVal( pwr / 600.0 );
			}
			pApp->graphs[0]->SetUpdate();
			pApp->graphs[1]->SetUpdate();
			//pApp->graphs[0]->UpdTitle(ss);
	}	}	break;

	
	case Gh_Clutch:  /// clutch,rpm,gears
		if (gsi >= 4)
		{
			const CARENGINE& eng = dynamics.engine;
			const CARCLUTCH& clu = dynamics.clutch;
			pApp->graphs[0]->AddVal(eng.GetRPM() / 7500.0);
			#if 0
				MATHVECTOR<Dbl,3> vel = dynamics.GetVelocity(), vx(1,0,0);
				dynamics.Orientation().RotateVector(vx);
				Dbl d = vel.dot(vx),
					velCar = vel.Magnitude() * (d >= 0.0 ? 1 : -1), velWh = dynamics.GetSpeedMPS();
				//pApp->graphs[1]->AddVal(velCar * 0.02f));
				//pApp->graphs[2]->AddVal(velWh * 0.02f);
				if (velWh < 1.1f && velWh > -1.1f)
					d = 1.f;
				else
					d = fabs(velCar >= velWh ? velCar/velWh : velWh/velCar);
				pApp->graphs[2]->AddVal(/*std::min(1.f, std::max(1.f,*/ d * 0.5f);
			#else
			pApp->graphs[1]->AddVal(clu.GetClutch() * 0.3f + 0.15f);
			pApp->graphs[2]->AddVal(clu.IsLocked() ? 0.15f : 0.f);
			#endif
			pApp->graphs[3]->AddVal(GetGear() / 6.f);
		}	break;


	case Gh_Diffs:  /// differentials
		if (gsi >= 6)
		for (int i=0; i < 3*2; ++i)
		{
			CARDIFFERENTIAL* diff;
			switch (i%3)
			{
			case 0: diff = &dynamics.diff_front;  break;
			case 1: diff = &dynamics.diff_rear;  break;
			case 2: diff = &dynamics.diff_center;  break;
			}
			Dbl d;
			if (i/3==0) {
				d = diff->GetSide1Speed() - diff->GetSide2Speed();
				d = negPow(d, 0.4)*0.07 + 0.5;  }  // blue
			else {
				d = diff->GetSide1Torque() - diff->GetSide2Torque();
				d = d*0.0004 + 0.5;  }  // orange
			pApp->graphs[i]->AddVal(d);
		}	break;
		
	}
	else  ///Gh_TireEdit:  /// tire pacejka
	//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
	{	static int ii = 0;  ++ii;  // skip upd cntr
		const int im = pApp->iUpdTireGr > 0 ? 2 : 8;  // faster when editing val
		if (ii >= im && gsi >= TireNG*2)
		{	ii = 0;  pApp->iUpdTireGr = 0;

			const CARTIRE* tire = dynamics.GetTire(FRONT_LEFT);
			Dbl* ft = new Dbl[TireLenG];

			Dbl fmin, fmax, frng, maxF;
			const bool common = 1;  // common range for all
			const bool cust = 1;
			//  RANGE  gui sld ..
			const Dbl fMAX = 9000.0, max_y = pApp->sc->asphalt ? 40.0 : 80.0, max_x = 1.0;

			///  Fy lateral --
			for (int i=0; i < TireNG; ++i)
			{
				bool comi = common || i == 0;
				if (comi)
				{	fmin = FLT_MAX;  fmax = FLT_MIN;  frng = 0.0;  }
				
				for (int x=0; x < TireLenG; ++x)
				{
					//Dbl yy = max_y * 2.0 * (x-LEN*0.5) / LEN;
					Dbl yy = max_y * x / TireLenG;
					Dbl n = (TireNG-1-i+1) * 0.65;
					Dbl fy = !pApp->iTireLoad ? tire->Pacejka_Fy(yy, n, 0, 1.0, maxF)  // normF
											: tire->Pacejka_Fy(yy, 3, n-2, 1.0, maxF); // camber
					ft[x] = fy;

					if (comi)  // get min, max
					{	if (fy < fmin)  fmin = fy;
						if (fy > fmax)  fmax = fy;  }
				}
				if (comi)  // get range
					frng = 1.0 / (fmax - fmin);
				if (cust)
				{	fmax = fMAX;  fmin = 0.0;  frng = 1.0 / (fmax - fmin);  }
				
				for (int x = 0; x < TireLenG; ++x)
					pApp->graphs[i]->AddVal( (ft[x] - fmin) * frng );
				pApp->graphs[i]->SetUpdate();

				if (i==0)
					pApp->graphs[i]->UpdTitle("Fy Lateral--\n"  //#33FF77
						//"min: "+fToStr((float)fmin,2,4)+"\n"+
						"max y "+fToStr((float)fmax,0,1)+
						"  x "+fToStr(max_y,0,1)+"\n");
			}

			///  Fx long |
			for (int i=0; i < TireNG; ++i)
			{
				bool comi = common || i == 0;
				if (comi)
				{	fmin = FLT_MAX;  fmax = FLT_MIN;  frng = 0.0;  }
				
				for (int x=0; x < TireLenG; ++x)
				{
					//Dbl xx = max_x * 2.0 * (x-LEN*0.5) / LEN;
					Dbl xx = max_x * x / TireLenG;
					Dbl n = (TireNG-1-i+1) * 0.65;
					Dbl fx = pApp->iEdTire != 2 ? tire->Pacejka_Fx(xx, n, 1.0, maxF)  // normF
							 : (!pApp->iTireLoad ? tire->Pacejka_Mz(xx, 0, n, 0.0, 1.0, maxF)    // align- norm
												 : tire->Pacejka_Mz(0, xx, n, 0.0, 1.0, maxF));  // align- camber
					ft[x] = fx;

					if (comi)  // get min, max
					{	if (fx < fmin)  fmin = fx;
						if (fx > fmax)  fmax = fx;  }
				}
				if (comi)  // get range
					frng = 1.0 / (fmax - fmin);
				if (cust)
				{	fmax = fMAX;  fmin = 0.0;  frng = 1.0 / (fmax - fmin);  }
				
				for (int x = 0; x < TireLenG; ++x)
					pApp->graphs[i+TireNG]->AddVal( (ft[x] - fmin) * frng );
				pApp->graphs[i+TireNG]->SetUpdate();

				if (i==0)
					pApp->graphs[i+TireNG]->UpdTitle("Fx Longit |\n"
						//"min: "+fToStr((float)fmin,2,4)+"\n"+
						"max y "+fToStr((float)fmax,0,1)+
						"  x "+fToStr(max_x,0,1)+"\n");
			}
			delete[]ft;
			
			//  update edit values text and descr
			///----------------------------------------------------
			const static String sLateral[15][2] = {
				"  a0","#F0FFFFShape factor",
				"  a1","#C0E0FFLoad infl. on friction coeff",
				"  a2","#F0FFFFLateral friction coeff at load = 0",
				"  a3","#F0FFFFMaximum stiffness",
				"  a4","#F0FFFFLoad at maximum stiffness",
				"  a5","#C0E0FF-Camber infl. on stiffness",
				"  a6","Curvature change with load",
				"  a7","Curvature at load = 0",
				"  a8","#A0C0D0  -Horiz. shift because of camber",
				"  a9","  Load infl. on horizontal shift",
				" a10","  Horizontal shift at load = 0",
				"a111","  -Camber infl. on vertical shift",
				"a112","  -Camber infl. on vertical shift",
				" a12","  Load infl. on vertical shift",
				" a13","  Vertical shift at load = 0" };
			const static String sLongit[13][2] = {
				"  b0","#FFFFF0Shape factor",
				"  b1","#F0F0A0Load infl. on long. friction coeff",
				"  b2","#FFFFF0Longit. friction coeff at load = 0",
				"  b3","#F0F0A0Curvature factor of stiffness",
				"  b4","#F0F0A0Change of stiffness with load at load = 0",
				"  b5","#E0C080Change of progressivity/load",  //of stiffness
				"  b6","Curvature change with load^2",
				"  b7","Curvature change with load",
				"  b8","Curvature at load = 0",
				"  b9","#D0D0A0  Load infl. on horizontal shift",
				" b10","  Horizontal shift at load = 0",
				" b11","  Load infl. on vertical shift",
				" b12","  Vertical shift at load = 0" };
			const static String sAlign[18][2] = {
				" c0","#E0FFE0Shape factor",
				" c1","Load infl. of peak value",
				" c2","Load infl. of peak value",
				" c3","Curvature factor of stiffness",
				" c4","Change of stiffness with load at load = 0",
				" c5","Change of progressivity/load",
				" c6","-Camber infl. on stiffness",
				" c7","Curvature change with load",
				" c8","Curvature change with load",
				" c9","Curvature at load = 0",
				"c10","-Camber infl. of stiffness",
				"c11","  -Camber infl. on horizontal shift",
				"c12","  Load infl. on horizontal shift",
				"c13","  Horizontal shift at load = 0",
				"c14","  -Camber infl. on vertical shift",
				"c15","  -Camber infl. on vertical shift",
				"c16","  Load infl. on vertical shift",
				"c17","  Vertical shift at load = 0" };
			const static String sCommon = "#C8C8F0Pacejka's Magic Formula coeffs\n";

			String ss,sd;
			if (pApp->iEdTire == 0)
			{
				ss += "#A0F0FF--Lateral--\n";  sd += sCommon;
				for (int i=0; i < tire->lateral.size(); ++i)
				{
					//ss += (i == pApp->iCurLat) ? "." : "  ";
					float f = tire->lateral[i];
					char p = f > 100 ? 0 : (f > 10 ? 1 : (f > 1 ? 2 : 3));
					bool cur = (i == pApp->iCurLat);

					ss += cur ? "#A0EEFF" : "#E0FFFF";
					ss += sLateral[i][0] +" "+ fToStr(f, p,5);
					ss += cur ? "  <\n" : "\n";
					sd += sLateral[i][1] +"\n";
				}

				ss += "\n#C0F0F0alpha hat\n";
				//for (int a=0; a < tire->alpha_hat.size(); ++a)
				//	ss += "  "+fToStr( tire->alpha_hat[a], 3,5) + "\n";

				int z = (int)tire->alpha_hat.size()-1;
				ss += "  "+fToStr( tire->alpha_hat[0], 3,5) + "\n";
				ss += "  "+fToStr( tire->alpha_hat[z/2], 3,5) + "\n";
				ss += "  "+fToStr( tire->alpha_hat[z], 3,5) + "\n";
			}
			else if (pApp->iEdTire == 1)
			{
				ss += "#FFFF70| Longit |\n";  sd += sCommon;
				for (int i=0; i < tire->longitudinal.size(); ++i)
				{
					//ss += (i == pApp->iCurLong) ? "." : "  ";
					float f = tire->longitudinal[i];
					char p = f > 100 ? 0 : (f > 10 ? 1 : (f > 1 ? 2 : 3));
					bool cur = (i == pApp->iCurLong);

					ss += cur ? "#FFE090" : "#FFFFD0";
					ss += sLongit[i][0] +" "+ fToStr(f, p,5);
					ss += cur ? "  <\n" : "\n";
					sd += sLongit[i][1] +"\n";
				}

				ss += "\n#F0F0C0sigma hat\n";
				//for (int a=0; a < tire->sigma_hat.size(); ++a)
				//	ss += "  "+fToStr( tire->sigma_hat[a], 3,5) + "\n";

				int z = (int)tire->sigma_hat.size()-1;
				ss += "  "+fToStr( tire->sigma_hat[0], 3,5) + "\n";
				ss += "  "+fToStr( tire->sigma_hat[z/2], 3,5) + "\n";
				ss += "  "+fToStr( tire->sigma_hat[z], 3,5) + "\n";
			}
			else //if (pApp->iEdTire == 2)
			{
				ss += "#D0FFD0o Align o\n";  sd += sCommon;
				for (int i=0; i < tire->aligning.size(); ++i)
				{
					float f = tire->aligning[i];
					char p = f > 100 ? 0 : (f > 10 ? 1 : (f > 1 ? 2 : 3));
					bool cur = (i == pApp->iCurAlign);

					ss += cur ? "#80FF80" : "#E0FFE0";
					ss += sAlign[i][0] +" "+ fToStr(f, p,5);
					ss += cur ? "  <\n" : "\n";
					sd += sAlign[i][1] +"\n";
				}
			}
			
			pApp->graphs[gsi-2]->UpdTitle(ss);
			pApp->graphs[gsi-1]->UpdTitle(sd);
			pApp->graphs[2]->UpdTitle(TireVar[pApp->iTireLoad]); //-
		}
	}
}
Example #15
0
void BEZIER::SetFromCorners(const MATHVECTOR<float,3> & fl, const MATHVECTOR<float,3> & fr, const MATHVECTOR<float,3> & bl, const MATHVECTOR<float,3> & br)
{
	MATHVECTOR<float,3> temp;
	
	center = fl + fr + bl + br;
	center = center*0.25;
	radius = 0;
	if ((fl - center).Magnitude() > radius)
		radius = (fl - center).Magnitude();
	if ((fr - center).Magnitude() > radius)
		radius = (fr - center).Magnitude();
	if ((bl - center).Magnitude() > radius)
		radius = (bl - center).Magnitude();
	if ((br - center).Magnitude() > radius)
		radius = (br - center).Magnitude();
	
	//assign corners
	points[0][0] = fl;
	points[0][3] = fr;
	points[3][3] = br;
	points[3][0] = bl;
	
	//calculate intermediate front and back points
	temp = fr - fl;
	if (temp.Magnitude() < 0.0001)
	{
		points[0][1] = fl;
		points[0][2] = fl;
	}
	else
	{
		points[0][1] = fl + temp.Normalize()*(temp.Magnitude()/3.0);
		points[0][2] = fl + temp.Normalize()*(2.0*temp.Magnitude()/3.0);
	}
	
	temp = br - bl;
	if (temp.Magnitude() < 0.0001)
	{
		points[3][1] = bl;
		points[3][2] = bl;
	}
	else
	{
		points[3][1] = bl + temp.Normalize()*(temp.Magnitude()/3.0);
		points[3][2] = bl + temp.Normalize()*(2.0*temp.Magnitude()/3.0);
	}
	
	
	//calculate intermediate left and right points	
	int i;
	for (i = 0; i < 4; i++)
	{
		temp = points[3][i] - points[0][i];
		if (temp.Magnitude() > 0.0001)
		{
			points[1][i] = points[0][i] + temp.Normalize()*(temp.Magnitude()/3.0);
			points[2][i] = points[0][i] + temp.Normalize()*(2.0*temp.Magnitude()/3.0);
		}
		else
		{
			points[1][i] = points[0][i];
			points[2][i] = points[0][i];
		}
	}
	
	//CheckForProblems();
}
//----------------------------------------------------------------------------------------------------------------------------------
///  Load  (.car file)
//----------------------------------------------------------------------------------------------------------------------------------
bool CARDYNAMICS::Load(GAME* pGame, CONFIGFILE & c, ostream & error_output)
{
	QTimer ti;  ti.update(); /// time

	//bTerrain = false;
	string drive = "RWD";
	int version(1);
	c.GetParam("version", version);
	if (version > 2)
	{
		error_output << "Unsupported car version: " << version << endl;
		return false;
	}
	float temp_vec3[3];

	//load the engine
	{
		float mass, rpm_limit, inertia, friction,
			start_rpm, stall_rpm, fuel_consumption;
		MATHVECTOR<double,3> position;

		if (!c.GetParam("engine.rpm-limit", rpm_limit, error_output))  return false;
		engine.SetRpmMax(rpm_limit);

		if (!c.GetParam("engine.inertia", inertia, error_output))  return false;
		engine.SetInertia(inertia);

		if (!c.GetParam("engine.friction", friction, error_output))  return false;
		engine.SetFrictionB(friction);

		if (!c.GetParam("engine.start-rpm", start_rpm, error_output))  return false;
		engine.SetStartRPM(start_rpm);

		if (!c.GetParam("engine.stall-rpm", stall_rpm, error_output))  return false;
		engine.SetStallRPM(stall_rpm);

		if (!c.GetParam("engine.fuel-consumption", fuel_consumption, error_output))  return false;
		engine.SetFuelConsumption(fuel_consumption);

		if (!c.GetParam("engine.mass", mass, error_output))  return false;
		if (!c.GetParam("engine.position", temp_vec3, error_output))  return false;
		if (version == 2)  ConvertV2to1(temp_vec3[0],temp_vec3[1],temp_vec3[2]);
		position.Set(temp_vec3[0],temp_vec3[1],temp_vec3[2]);
		engine.SetMass(mass);
		engine.SetPosition(position);
		AddMassParticle(mass, position);

		float mul = 1.f, max_torque = 0;
		c.GetParam("engine.torque-val-mul", mul);

		float torque_point[3];
		string torque_str("engine.torque-curve-00");
		vector <pair <double, double> > torques;
		int curve_num = 0;
		while (c.GetParam(torque_str, torque_point))
		{
			max_torque = max(max_torque, torque_point[1] * mul);
			torques.push_back(pair <float, float> (torque_point[0], torque_point[1] * mul));

			curve_num++;
			stringstream str;
			str << "engine.torque-curve-";  str.width(2);  str.fill('0');
			str << curve_num;
			torque_str = str.str();
		}
		if (torques.size() <= 1)
		{
			error_output << "You must define at least 2 torque curve points." << endl;
			return false;
		}
		engine.SetTorqueCurve(rpm_limit, torques);

		//load the clutch
		{
			float mul;  //max_torque = sliding * radius * area * max_pressure;
			//if (!c.GetParam("clutch.max-torque", max_torque, error_output))  return false;
			if (!c.GetParam("clutch.max-torque-mul", mul, error_output))  return false;
			clutch.SetMaxTorque(max_torque * mul);
		}

		//  factor for stats  -
		mul = 1.f;
		if (c.GetParam("engine.real-pow-tq-mul", mul))
			engine.real_pow_tq_mul = mul;
		
		mul = 1.f;
		if (c.GetParam("engine.sound-vol-mul", mul))
			engine_vol_mul = mul;
	}

	//load the transmission
	{
		float time = 0;
		float ratio;
		int gears;

		c.GetParam("transmission.shift-delay", time);
		shift_time = time;

		if (!c.GetParam("transmission.gear-ratio-r", ratio, error_output))  return false;
		transmission.SetGearRatio(-1, ratio);

		if (!c.GetParam("transmission.gears", gears, error_output))  return false;

		for (int i = 0; i < gears; i++)
		{
			stringstream s;
			s << "transmission.gear-ratio-" << i+1;
			if (!c.GetParam(s.str(), ratio, error_output))  return false;
			transmission.SetGearRatio(i+1, ratio);
		}
	}

	//load the differential(s)
	string drivetype;
	if (!c.GetParam("drive", drivetype, error_output))  return false;
	SetDrive(drivetype);
	float final_drive, a, a_tq(0), a_tq_dec(0);
	
	///  new 3 sets
	if (drivetype == "AWD" &&
		c.GetParam("diff-center.final-drive", a))
	{
		c.GetParam("diff-rear.anti-slip", a, error_output);
		c.GetParam("diff-rear.torque", a_tq);
		c.GetParam("diff-rear.torque-dec", a_tq_dec);
		diff_rear.SetFinalDrive(1.0);
		diff_rear.SetAntiSlip(a, a_tq, a_tq_dec);

		c.GetParam("diff-front.anti-slip", a, error_output);
		c.GetParam("diff-front.torque", a_tq);
		c.GetParam("diff-front.torque-dec", a_tq_dec);
		diff_front.SetFinalDrive(1.0);
		diff_front.SetAntiSlip(a, a_tq, a_tq_dec);

		c.GetParam("diff-center.final-drive", final_drive, error_output);
		c.GetParam("diff-center.anti-slip", a, error_output);
		c.GetParam("diff-center.torque", a_tq);
		c.GetParam("diff-center.torque-dec", a_tq_dec);
		diff_center.SetFinalDrive(final_drive);
		diff_center.SetAntiSlip(a, a_tq, a_tq_dec);
	}
	else  // old 1 for all
	{
		if (!c.GetParam("differential.final-drive", final_drive, error_output))  return false;
		if (!c.GetParam("differential.anti-slip", a, error_output))  return false;
		c.GetParam("differential.torque", a_tq);
		c.GetParam("differential.torque-dec", a_tq_dec);

		if (drivetype == "RWD")
		{
			diff_rear.SetFinalDrive(final_drive);
			diff_rear.SetAntiSlip(a, a_tq, a_tq_dec);
		}
		else if (drivetype == "FWD")
		{
			diff_front.SetFinalDrive(final_drive);
			diff_front.SetAntiSlip(a, a_tq, a_tq_dec);
		}
		else if (drivetype == "AWD")
		{
			diff_rear.SetFinalDrive(1.0);
			diff_rear.SetAntiSlip(a, a_tq, a_tq_dec);

			diff_front.SetFinalDrive(1.0);
			diff_front.SetAntiSlip(a, a_tq, a_tq_dec);

			diff_center.SetFinalDrive(final_drive);
			diff_center.SetAntiSlip(a, a_tq, a_tq_dec);
		}else
		{	error_output << "Unknown drive type: " << drive << endl;
			return false;
		}
	}

	//load the brake
	{
		for (int i = 0; i < 2; i++)
		{
			string pos = "front";
			WHEEL_POSITION left = FRONT_LEFT;
			WHEEL_POSITION right = FRONT_RIGHT;
			if (i == 1)
			{
				left = REAR_LEFT;
				right = REAR_RIGHT;
				pos = "rear";
			}

			float friction, max_pressure, area, bias, radius, handbrake(0);

			if (!c.GetParam("brakes-"+pos+".friction", friction, error_output))  return false;
			brake[left].SetFriction(friction);
			brake[right].SetFriction(friction);

			if (!c.GetParam("brakes-"+pos+".area", area, error_output))  return false;
			brake[left].SetArea(area);
			brake[right].SetArea(area);

			if (!c.GetParam("brakes-"+pos+".radius", radius, error_output))  return false;
			brake[left].SetRadius(radius);
			brake[right].SetRadius(radius);

			c.GetParam("brakes-"+pos+".handbrake", handbrake);
			brake[left].SetHandbrake(handbrake);
			brake[right].SetHandbrake(handbrake);

			if (!c.GetParam("brakes-"+pos+".bias", bias, error_output))  return false;
			brake[left].SetBias(bias);
			brake[right].SetBias(bias);

			if (!c.GetParam("brakes-"+pos+".max-pressure", max_pressure, error_output))  return false;
			brake[left].SetMaxPressure(max_pressure*bias);
			brake[right].SetMaxPressure(max_pressure*bias);
		}
	}

	//load the fuel tank
	{
		float pos[3];
		MATHVECTOR<double,3> position;
		float capacity, volume, fuel_density;

		if (!c.GetParam("fuel-tank.capacity", capacity, error_output))  return false;
		fuel_tank.SetCapacity(capacity);

		if (!c.GetParam("fuel-tank.volume", volume, error_output))  return false;
		fuel_tank.SetVolume(volume);

		if (!c.GetParam("fuel-tank.fuel-density", fuel_density, error_output))  return false;
		fuel_tank.SetDensity(fuel_density);

		if (!c.GetParam("fuel-tank.position", pos, error_output))  return false;
		if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
		position.Set(pos[0],pos[1],pos[2]);
		fuel_tank.SetPosition(position);
		//AddMassParticle(fuel_density*volume, position);
	}

	//load the suspension
	{
		for (int i = 0; i < 2; i++)
		{
			string posstr = "front";
			string posshortstr = "F";
			WHEEL_POSITION posl = FRONT_LEFT;
			WHEEL_POSITION posr = FRONT_RIGHT;
			if (i == 1)
			{
				posstr = "rear";
				posshortstr = "R";
				posl = REAR_LEFT;
				posr = REAR_RIGHT;
			}

			float spring_constant, bounce, rebound, travel, camber, caster, toe, anti_roll;//, maxcompvel;
			float hinge[3];
			MATHVECTOR<double,3> tempvec;

			if (!c.GetParam("suspension-"+posstr+".spring-constant", spring_constant, error_output))  return false;
			suspension[posl].SetSpringConstant(spring_constant);
			suspension[posr].SetSpringConstant(spring_constant);

			if (!c.GetParam("suspension-"+posstr+".bounce", bounce, error_output))  return false;
			suspension[posl].SetBounce(bounce);
			suspension[posr].SetBounce(bounce);

			if (!c.GetParam("suspension-"+posstr+".rebound", rebound, error_output))  return false;
			suspension[posl].SetRebound(rebound);
			suspension[posr].SetRebound(rebound);

			string file;
			if (c.GetParam("suspension-"+posstr+".factors-file", file))
			{
				int id = pGame->suspS_map[file]-1;
				if (id == -1)  {  id = 0;
					error_output << "Can't find suspension spring factors file: " << file << endl;  }

				suspension[posl].SetSpringFactorPoints(pGame->suspS[id]);
				suspension[posr].SetSpringFactorPoints(pGame->suspS[id]);

				id = pGame->suspD_map[file]-1;
				if (id == -1)  {  id = 0;
					error_output << "Can't find suspension damper factors file: " << file << endl;  }
				
				suspension[posl].SetDamperFactorPoints(pGame->suspD[id]);
				suspension[posr].SetDamperFactorPoints(pGame->suspD[id]);
			}else
			{	//  factor points
				vector <pair <double, double> > damper, spring;
				c.GetPoints("suspension-"+posstr, "damper-factor", damper);
				suspension[posl].SetDamperFactorPoints(damper);
				suspension[posr].SetDamperFactorPoints(damper);

				c.GetPoints("suspension-"+posstr, "spring-factor", spring);
				suspension[posl].SetSpringFactorPoints(spring);
				suspension[posr].SetSpringFactorPoints(spring);
			}

			if (!c.GetParam("suspension-"+posstr+".travel", travel, error_output))  return false;
			suspension[posl].SetTravel(travel);
			suspension[posr].SetTravel(travel);

			if (!c.GetParam("suspension-"+posstr+".camber", camber, error_output))  return false;
			suspension[posl].SetCamber(camber);
			suspension[posr].SetCamber(camber);

			if (!c.GetParam("suspension-"+posstr+".caster", caster, error_output))  return false;
			suspension[posl].SetCaster(caster);
			suspension[posr].SetCaster(caster);

			if (!c.GetParam("suspension-"+posstr+".toe", toe, error_output))  return false;
			suspension[posl].SetToe(toe);
			suspension[posr].SetToe(toe);

			if (!c.GetParam("suspension-"+posstr+".anti-roll", anti_roll, error_output))  return false;
			suspension[posl].SetAntiRollK(anti_roll);
			suspension[posr].SetAntiRollK(anti_roll);

			if (!c.GetParam("suspension-"+posshortstr+"L.hinge", hinge, error_output))  return false;
			//cap hinge to reasonable values
			for (int i = 0; i < 3; i++)
			{
				if (hinge[i] < -100)	hinge[i] = -100;
				if (hinge[i] > 100)		hinge[i] = 100;
			}
			if (version == 2)  ConvertV2to1(hinge[0],hinge[1],hinge[2]);
			tempvec.Set(hinge[0],hinge[1], hinge[2]);
			suspension[posl].SetHinge(tempvec);

			if (!c.GetParam("suspension-"+posshortstr+"R.hinge", hinge, error_output))  return false;
			for (int i = 0; i < 3; i++)
			{
				if (hinge[i] < -100)	hinge[i] = -100;
				if (hinge[i] > 100)		hinge[i] = 100;
			}
			if (version == 2)  ConvertV2to1(hinge[0],hinge[1],hinge[2]);
			tempvec.Set(hinge[0],hinge[1], hinge[2]);
			suspension[posr].SetHinge(tempvec);
		}
	}

	//load the wheels
	{
		for (int i = 0; i < 4; i++)
		{
			string sPos;
			WHEEL_POSITION wp;
			if (i == 0)		{	sPos = "FL";	wp = FRONT_LEFT;	}
			else if (i == 1){	sPos = "FR";	wp = FRONT_RIGHT;	}
			else if (i == 2){	sPos = "RL";	wp = REAR_LEFT;	}
			else			{	sPos = "RR";	wp = REAR_RIGHT;	}

			float roll_h, mass;
			float pos[3];
			MATHVECTOR<double,3> vec;

			if (!c.GetParam("wheel-"+sPos+".mass", mass, error_output))  return false;
			wheel[wp].SetMass(mass);

			if (!c.GetParam("wheel-"+sPos+".roll-height", roll_h, error_output))  return false;
			wheel[wp].SetRollHeight(roll_h);

			if (!c.GetParam("wheel-"+sPos+".position", pos, error_output))  return false;
			if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
			vec.Set(pos[0],pos[1], pos[2]);
			wheel[wp].SetExtendedPosition(vec);

			AddMassParticle(mass, vec);
		}

		//load the rotational inertia parameter from the tire section
		float front_inertia;
		float rear_inertia;
		if (c.GetParam("tire-both.rotational-inertia", front_inertia, error_output))
			rear_inertia = front_inertia;
		else
		{	if (!c.GetParam("tire-front.rotational-inertia", front_inertia, error_output))  return false;
			if (!c.GetParam("tire-rear.rotational-inertia", rear_inertia, error_output))  return false;
		}
		wheel[FRONT_LEFT].SetInertia(front_inertia);
		wheel[FRONT_RIGHT].SetInertia(front_inertia);

		wheel[REAR_LEFT].SetInertia(rear_inertia);
		wheel[REAR_RIGHT].SetInertia(rear_inertia);
	}

	//load the tire parameters
	{
		WHEEL_POSITION leftside = FRONT_LEFT;
		WHEEL_POSITION rightside = FRONT_RIGHT;
		float value;
		bool both = c.GetParam("tire-both.radius", value);
		string posstr = both ? "both" : "front";

		for (int p = 0; p < 2; ++p)
		{
			if (p == 1)
			{
				leftside = REAR_LEFT;
				rightside = REAR_RIGHT;
				if (!both)  posstr = "rear";
			}

			float rolling_resistance[3];
			if (!c.GetParam("tire-"+posstr+".rolling-resistance", rolling_resistance, error_output))  return false;
			wheel[leftside].SetRollingResistance(rolling_resistance[0], rolling_resistance[1]);
			wheel[rightside].SetRollingResistance(rolling_resistance[0], rolling_resistance[1]);

			float radius;
			if (!c.GetParam("tire-"+posstr+".radius", radius, error_output))  return false;
			wheel[leftside].SetRadius(radius);
			wheel[rightside].SetRadius(radius);
		}
	}

	//load the mass-only particles
	{
		MATHVECTOR<double,3> position;
		float pos[3], mass;

		if (c.GetParam("contact-points.mass", mass))
		{
			int paramnum(0);
			string paramname("contact-points.position-00");
			stringstream output_supression;
			while (c.GetParam(paramname, pos))
			{
				if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
				position.Set(pos[0],pos[1],pos[2]);
				AddMassParticle(mass, position);
				paramnum++;
				stringstream str;
				str << "contact-points.position-";  str.width(2);  str.fill('0');
				str << paramnum;
				paramname = str.str();
			}
		}

		string paramname = "particle-00";
		int paramnum = 0;
		while (c.GetParam(paramname+".mass", mass))
		{
			if (!c.GetParam(paramname+".position", pos, error_output))  return false;
			if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
			position.Set(pos[0],pos[1],pos[2]);
			AddMassParticle(mass, position);
			paramnum++;
			stringstream str;
			str << "particle-";  str.width(2);  str.fill('0');
			str << paramnum;
			paramname = str.str();
		}
	}

	//load the max steering angle
	{
		float maxangle = 26.f;
		if (!c.GetParam("steering.max-angle", maxangle, error_output))  return false;
		SetMaxSteeringAngle( maxangle );

		float a = 1.f;
		c.GetParam("steering.flip-pow-mul", a);	 flip_mul = a;
	}
	///car angular damping -new
	{
		float a = 0.4f;
		c.GetParam("steering.angular-damping", a, error_output);
		SetAngDamp(a);

		a=0.f;  c.GetParam("rot_drag.roll", a);  rot_coef[0] = a;
		a=0.f;  c.GetParam("rot_drag.pitch", a); rot_coef[1] = a;
		a=0.f;  c.GetParam("rot_drag.yaw", a);	 rot_coef[2] = a;
		a=0.f;  c.GetParam("rot_drag.yaw2", a);	 rot_coef[3] = a;
	}

	//load the driver
	{
		float mass;
		float pos[3];
		MATHVECTOR<double,3> position;

		if (!c.GetParam("driver.mass", mass, error_output))  return false;
		if (!c.GetParam("driver.position", pos, error_output))  return false;
		if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
		position.Set(pos[0], pos[1], pos[2]);
		AddMassParticle(mass, position);
	}

	//load the aerodynamics
	{
		float drag_area, drag_c, lift_area, lift_c, lift_eff;
		float pos[3];
		MATHVECTOR<double,3> position;

		if (!c.GetParam("drag.frontal-area", drag_area, error_output))  return false;
		if (!c.GetParam("drag.drag-coefficient", drag_c, error_output))  return false;
		if (!c.GetParam("drag.position", pos, error_output))  return false;
		if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
		position.Set(pos[0], pos[1], pos[2]);
		AddAerodynamicDevice(position, drag_area, drag_c, 0,0,0);

		for (int i = 0; i < 2; i++)
		{
			string wingpos = "front";
			if (i == 1)
				wingpos = "rear";
			if (!c.GetParam("wing-"+wingpos+".frontal-area", drag_area, error_output))  return false;
			if (!c.GetParam("wing-"+wingpos+".drag-coefficient", drag_c, error_output))  return false;
			if (!c.GetParam("wing-"+wingpos+".surface-area", lift_area, error_output))  return false;
			if (!c.GetParam("wing-"+wingpos+".lift-coefficient", lift_c, error_output))  return false;
			if (!c.GetParam("wing-"+wingpos+".efficiency", lift_eff, error_output))  return false;
			if (!c.GetParam("wing-"+wingpos+".position", pos, error_output))  return false;
			if (version == 2)  ConvertV2to1(pos[0],pos[1],pos[2]);
			position.Set(pos[0], pos[1], pos[2]);
			AddAerodynamicDevice(position, drag_area, drag_c, lift_area, lift_c, lift_eff);
		}
	}

	UpdateMass();

	ti.update(); /// time
	float dt = ti.dt * 1000.f;
	LogO(Ogre::String(":::: Time car dynamics load: ") + fToStr(dt,0,3) + " ms");
	return true;
}
Example #17
0
void FollowCamera::update( Real time )
{
	if (!mGoalNode || !ca || !mCamera)  return;

	Vector3 posGoal = mGoalNode ? mGoalNode->getPosition() : Vector3::UNIT_Y;
	Quaternion orientGoal = mGoalNode ? mGoalNode->getOrientation() : Quaternion::IDENTITY;

	const static Quaternion  qO = Quaternion(Degree(180),Vector3::UNIT_Z) * Quaternion(Degree(-90),Vector3::UNIT_Y),
		qR = Quaternion(Degree(90),Vector3(0,1,0));
	Quaternion  orient = orientGoal * qO;
	Vector3  ofs = orient * ca->mOffset,  goalLook = posGoal + ofs;
	
    if (ca->mType == CAM_Car)	/* 3 Car - car pos & rot full */
    {
		mCamera->setPosition( goalLook );
		mCamera->setOrientation( orient );
		updInfo(time);
		return;
	}
    if (ca->mType == CAM_Follow)  ofs = ca->mOffset;
    
	Vector3  pos,goalPos;
	pos     = mCamera->getPosition() - ofs;
	goalPos = posGoal;
	
	Vector3  xyz;
	if (ca->mType != CAM_Arena)
	{
		Real x,y,z,xz;   // pitch & yaw to direction vector
		Real ap = ca->mPitch.valueRadians(), ay =  ca->mYaw.valueRadians();
		y = sin(ap), xz = cos(ap);
		x = sin(ay) * xz, z = cos(ay) * xz;
		xyz = Vector3(x,y,z);  xyz *= ca->mDist;
	}
	
	bool manualOrient = false;
	switch (ca->mType)
	{
		case CAM_Arena:		/* 2 Arena - free pos & rot */
		    goalPos = ca->mOffset - ofs;
		    break;
		    
		case CAM_Free:		/* 1 Free - free rot, pos from car */
			goalPos += xyz;
			break;
			
		case CAM_Follow:	/* 0 Follow - car rotY & pos from behind car, smooth */
		{	Quaternion  orient = orientGoal * qR;
			orient.FromAngleAxis(orient.getYaw(), Vector3::UNIT_Y);
			goalPos += orient * xyz;
		}	break;
		
		case CAM_ExtAng:    /* 4 Extended Angle - car in center, angle smooth */
		{	Quaternion  orient = orientGoal * qR;
			Quaternion  ory;  ory.FromAngleAxis(orient.getYaw(), Vector3::UNIT_Y);

			if (first)  {  qq = ory;  first = false;  }
			else  qq = orient.Slerp(ca->mSpeed * time, qq, ory, true);

			//  smooth dist from vel
			if (0)
			{
				if (first)  {  mPosNodeOld = posGoal;  }
				Real vel = (posGoal - mPosNodeOld).length() / std::max(0.001f, std::min(0.1f, time));
				mPosNodeOld = posGoal;
				if (first)  mVel = 0.f;  else
					mVel += (vel - mVel) * time * 8.f;  // par  vel smooth speed
				if (!first)
					xyz *= 1.f + std::min(100.f, mVel) * 0.01f;  // par  vel dist factor
			}

			Quaternion  qy = Quaternion(ca->mYaw,Vector3(0,1,0));
			goalPos += qq * (xyz + ca->mOffset);
			
			mCamera->setPosition( goalPos );
			mCamera->setOrientation( qq * qy * Quaternion(Degree(-ca->mPitch),Vector3(1,0,0)) );
			manualOrient = true;
		}	break;
	}

	if (!manualOrient)  // if !CAM_ExtAng
	{
		float dtmul = ca->mSpeed == 0 ? 1.0f : ca->mSpeed * time;

		if (ca->mType ==  CAM_Arena)
		{
			Vector3  Pos(0,0,0), goalPos = ca->mOffset;
			Pos = mCamera->getPosition();
			Pos += (goalPos - Pos) * dtmul;
			
			static Radian  pitch(0), yaw(0);
			pitch += (ca->mPitch - pitch) * dtmul;  yaw += (ca->mYaw - yaw) * dtmul;
			
			if (first)  {  Pos = goalPos;  pitch = ca->mPitch;  yaw = ca->mYaw;  first = false;  }
			mCamera->setPosition( Pos );
			mCamera->setOrientation(Quaternion::IDENTITY);
			mCamera->pitch(pitch);  mCamera->yaw(yaw);
			manualOrient = true;
		}
		else
		{
			if (first)  pos = goalPos;
			Vector3  addPos,addLook;
			addPos = (goalPos - pos).normalisedCopy() * (goalPos - pos).length() * dtmul;
			if (addPos.squaredLength() > (goalPos - pos).squaredLength())  addPos = goalPos - pos;
			pos += addPos;
			mCamera->setPosition( pos + ofs );
		
			if (mGoalNode)  goalLook = posGoal + ofs;
			if (first)	{	mLook = goalLook;  first = false;  }

			addLook = (goalLook - mLook) * dtmul;//Rot;
			mLook += addLook;
		}
	}

	/// cast ray from car to camera, to prevent objects blocking the camera's sight
    #ifdef CAM_BLT
	// update sphere pos
	btVector3 carPos = BtOgre::Convert::toBullet(posGoal);
	state->setWorldTransform( btTransform(btQuaternion(0,0,0,1), carPos ));
	
	// calculate origin & direction of the ray, convert to vdrift coordinates
	MATHVECTOR<float,3> origin;
	origin.Set( carPos.x(), carPos.y(), carPos.z() );
	MATHVECTOR<float,3> direction;
	btVector3 dir = BtOgre::Convert::toBullet(mCamera->getPosition()-posGoal);
	direction.Set(dir.x(), dir.y(), dir.z());
	Real distance = (mCamera->getPosition()-posGoal ).length();
	int pOnRoad;
	
	// shoot our ray
	COLLISION_CONTACT contact;
	mWorld->CastRay( origin, direction, distance, body, contact, &pOnRoad );
	
	if (contact.col != NULL)
	{
		LogO("Collision occured");
		// collision occured - update cam pos
		mCamera->setPosition( BtOgre::Convert::toOgre( btVector3(contact.GetPosition()[0], contact.GetPosition()[1], contact.GetPosition()[2]) ) );
	}
	#endif
	
	moveAboveTerrain();
	if (!manualOrient)
		mCamera->lookAt( mLook );
	updInfo(time);
}