//  Ray
//-------------------------------------------------------------------------------------------------------------------------------
bool COLLISION_WORLD::CastRay(
	const MATHVECTOR <float, 3> & origin,
	const MATHVECTOR <float, 3> & direction,
	const float length,
	const btCollisionObject * caster,
	COLLISION_CONTACT & contact,
	int* pOnRoad) const
{
	btVector3 from = ToBulletVector(origin);
	btVector3 to = ToBulletVector(origin + direction * length);
	MyRayResultCallback rayCallback(from, to, caster);
	
	MATHVECTOR <float, 3> p, n;  float d;
	const TRACKSURFACE * s = TRACKSURFACE::None();
	const BEZIER * b = NULL;
	btCollisionObject * c = NULL;
	
	// track geometry collision
	world->rayTest(from, to, rayCallback);
	bool geometryHit = rayCallback.hasHit();
	if (geometryHit)
	{
		p = ToMathVector<float>(rayCallback.m_hitPointWorld);
		n = ToMathVector<float>(rayCallback.m_hitNormalWorld);
		d = rayCallback.m_closestHitFraction * length;
		c = rayCallback.m_collisionObject;
		if (c->isStaticObject() /*&& (c->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE == 0)*/)
		{
			void * ptr = c->getCollisionShape()->getUserPointer();
			if (ptr == (void*)7777)  // road
			{
				s = trackSurface[0];
				*pOnRoad = 1;
			}
			if (ptr == (void*)7788)  // pipe
			{
				s = trackSurface[0];
				*pOnRoad = 2;
			}
			else if (ptr == 0)
			{
				*pOnRoad = 0;
				void * ptr = c->getUserPointer();
				if (ptr != NULL)
				{
					const TRACK_OBJECT * const obj = reinterpret_cast <const TRACK_OBJECT * const> (ptr);
					assert(obj);
					s = obj->GetSurface();
				}
				else //track geometry
				{
					int shapeId = rayCallback.m_shapeId;
					//assert(shapeId >= 0 && shapeId < trackSurface.size());
					if (shapeId >= trackSurface.size() || shapeId < 0)  shapeId = 0;  //crash hf-
					//if (trackSurface.size() > 0)
						s = trackSurface[shapeId];
				}
			}
		}
		
		// track bezierpatch collision
		if (track != NULL)
		{
			MATHVECTOR <float, 3> bezierspace_raystart(origin[1], origin[2], origin[0]);
			MATHVECTOR <float, 3> bezierspace_dir(direction[1], direction[2], direction[0]);
			MATHVECTOR <float, 3> colpoint, colnormal;
			const BEZIER * colpatch = NULL;
			bool bezierHit = track->CastRay(bezierspace_raystart, bezierspace_dir, length, colpoint, colpatch, colnormal);
			if (bezierHit)
			{
				p = MATHVECTOR <float, 3> (colpoint[2], colpoint[0], colpoint[1]);
				n = MATHVECTOR <float, 3> (colnormal[2], colnormal[0], colnormal[1]);
				d = (colpoint - bezierspace_raystart).Magnitude();
				s = track->GetRoadSurface();
				b = colpatch;
				c = NULL;
				*pOnRoad = 1;
			}
		}

		contact.Set(p, n, d, s, b, c);
		return true;
	}
	
	// should only happen on vehicle rollover
	contact.Set(origin + direction * length, -direction, length, s, b, c);
	return false;
}
//--------------------------------------------------------------------------------------------------------------------------------
//  Ray
///-------------------------------------------------------------------------------------------------------------------------------
bool COLLISION_WORLD::CastRay(
	const MATHVECTOR<float,3> & origin,
	const MATHVECTOR<float,3> & direction, const float length,
	const btCollisionObject * caster,
	COLLISION_CONTACT & contact,  //out
	CARDYNAMICS* cd, int w, //out pCarDyn, nWheel
	bool ignoreCars, bool camTilt, bool camDist) const
{
	btVector3 from = ToBulletVector(origin);
	btVector3 to = ToBulletVector(origin + direction * length);
	MyRayResultCallback res(from, to, caster, ignoreCars, camTilt, camDist);
	
	//  data to set
	MATHVECTOR<float,3> pos, norm;  float dist;
	const TRACKSURFACE * surf = TRACKSURFACE::None();
	btCollisionObject * col = NULL;  const BEZIER * bzr = NULL;
	
	world->rayTest(from, to, res);

	bool geometryHit = res.hasHit();
	if (geometryHit)
	{
		pos = ToMathVector<float>(res.m_hitPointWorld);
		norm = ToMathVector<float>(res.m_hitNormalWorld);
		dist = res.m_closestHitFraction * length;
		col = res.m_collisionObject;
		const TerData& td = pApp->scn->sc->td;

		if (col->isStaticObject() /*&& (c->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE == 0)*/)
		{
			long long ptrU = (long long)col->getCollisionShape()->getUserPointer(),
				su = ptrU & 0xFF00, mtr = ptrU & 0xFF;  //void*

			///  set surface, basing on shape type  -----------------

			if (ptrU)
			switch (su)
			{
				case SU_Road:  // road
				{
					int id = td.layerRoad.surfId;  // Road[mtr].
					surf = &pApp->pGame->surfaces[id];

					if (cd)
					{	cd->iWhOnRoad[w] = 1;   cd->whRoadMtr[w] = mtr;  cd->whTerMtr[w] = 0;  }
				}	break;

				case SU_Pipe:  // pipe
				{
					int id = td.layerRoad.surfId;
					surf = &pApp->pGame->surfaces[id];

					if (cd)
					{	cd->iWhOnRoad[w] = 2;   cd->whRoadMtr[w] = mtr+30;  cd->whTerMtr[w] = 0;  }
				}	break;

				case SU_Terrain:  // Terrain  get surface from blendmap mtr
				{
					int t = pApp->blendMapSize;
					float tws = td.fTerWorldSize;

					int mx = (pos[0] + 0.5*tws)/tws*t;  mx = std::max(0,std::min(t-1, mx));
					int my = (pos[1] + 0.5*tws)/tws*t;  my = std::max(0,std::min(t-1, t-1-my));

					int mtr = pApp->blendMtr[my*t + mx];

					int id = td.layersAll[td.layers[mtr]].surfId;
					surf = &pApp->pGame->surfaces[id];

					if (cd)                                              // mtr 0 = not on terrain
					{	cd->iWhOnRoad[w] = 0;   cd->whRoadMtr[w] = 60;  cd->whTerMtr[w] = mtr + 1;  }
				}	break;
				
				//case SU_RoadWall: //case SU_RoadColumn:
				//case SU_Vegetation: case SU_Border:
				//case SU_ObjectStatic: //case SU_ObjectDynamic:
				default:
				{
					int id = td.layerRoad.surfId;
					surf = &pApp->pGame->surfaces[id];

					if (cd)
					{	cd->iWhOnRoad[w] = 0;   cd->whRoadMtr[w] = 80;  cd->whTerMtr[w] = 0;  }
				}	break;
			}
			else  //if (ptrU == 0)
			{
				int id = td.layersAll[0].surfId;  //0 only 1st
				surf = &pApp->pGame->surfaces[id];

				if (cd)
				{	cd->iWhOnRoad[w] = 0;   cd->whRoadMtr[w] = 0;  cd->whTerMtr[w] = 1;  }

				/*void * ptr = col->getUserPointer();
				if (ptr != NULL)
				{
					const TRACK_OBJECT * const obj = reinterpret_cast <const TRACK_OBJECT * const> (ptr);
					assert(obj);
					surf = obj->GetSurface();
				}
				else  // track geometry
				{
					int shapeId = res.m_shapeId;
					//assert(shapeId >= 0 && shapeId < trackSurface.size());
					if (shapeId >= trackSurface.size() || shapeId < 0)  shapeId = 0;  //crash hf-
					if (trackSurface.size() > 0)
						surf = 0;//trackSurface[shapeId];
				}*/
			}
		}
		
		//  track bezierpatch collision
		if (track != NULL)
		{
			MATHVECTOR<float,3> bs_pos(origin[1], origin[2], origin[0]);  //bezierspace
			MATHVECTOR<float,3> bs_dir(direction[1], direction[2], direction[0]);
			MATHVECTOR<float,3> colpos, colnorm;
			const BEZIER * colpatch = NULL;
			bool bezierHit = track->CastRay(bs_pos, bs_dir, length, colpos, colpatch, colnorm);
			if (bezierHit)
			{
				pos = MATHVECTOR<float,3> (colpos[2], colpos[0], colpos[1]);
				norm = MATHVECTOR<float,3> (colnorm[2], colnorm[0], colnorm[1]);
				dist = (colpos - bs_pos).Magnitude();
				//surf = 0;//track->GetRoadSurface();
				bzr = colpatch;  col = NULL;

				int id = td.layerRoad.surfId;
				surf = &pApp->pGame->surfaces[id];

				if (cd)
				{	cd->iWhOnRoad[w] = 1;   cd->whRoadMtr[w] = 0;  cd->whTerMtr[w] = 0;  }
			}
		}

		contact.Set(pos, norm, dist, surf, bzr, col);
		return true;
	}
	
	//  should only happen on vehicle rollover
	contact.Set(origin + direction * length, -direction, length, surf, bzr, col);
	return false;
}