float AI_Car_Experimental::RayCastDistance( MATHVECTOR <float, 3> direction, float max_length){
	btVector3 pos = car->GetCarDynamics().GetPosition();
	btVector3 dir = car->GetCarDynamics().LocalToWorld(ToBulletVector(direction));
	dir -= pos;
	COLLISION_CONTACT contact;
	car->GetDynamicsWorld()->castRay(
		pos,
		dir,
		max_length,
		&car->GetCarDynamics().getCollisionObject(),
		contact
		);
	float depth = contact.GetDepth();
	float dist = std::min(max_length, depth);
#ifdef VISUALIZE_AI_DEBUG
	MATHVECTOR<float, 3> pos_start(ToMathVector<float>(pos));
	MATHVECTOR<float, 3> pos_end = pos_start + (ToMathVector<float>(dir) * dist);
	AddLinePoint(raycastshape, pos_start);
	AddLinePoint(raycastshape, pos_end);
#endif
	return dist;
}
Esempio n. 2
0
void FollowCamera::update(Real time, const PosInfo& posIn, PosInfo* posOut, COLLISION_WORLD* world, bool bounce)
{
    if (!ca || !posOut)  return;

    ///  input from car posInfoIn
    Vector3 posGoal = posIn.pos;
    Quaternion orientGoal = posIn.rot;
    ///  output saved back to car posInfoOut
    Quaternion camRotFinal;

    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;

    first = iFirst < 2;  ///par few first frames after reset
    if (iFirst < 10)  // after reset
    {
        ++iFirst;
        mDistReduce = 0.f;
        mATilt = 0.f;
    }

    ///  Camera Tilt from terrain/road slope under car
    //-------------------------------------------------------------------------------------------
    const float			//  params  . . .
    Rdist = 1.f,     // dist from car to ray (front or back)
    HupCar = 1.5f,	  // car up dir dist - for pipes - so pos stays inside pipe
    Habove = 1.5f,    // up axis dist, above car - for very high terrain angles
    HMaxDepth = 12.f;  // how far down can the ray goes
    const static Radian r0(0.f),
          angMin = Degree(10.f),   // below this angle, tilt has no effect - terrain bumps
          maxDiff = Degree(1.4f);  // max diff of tilt - no sudden jumps
    const float smoothSpeed = 14.f;  // how fast to apply tilt change

    bool bUseTilt = ca->mType == CAM_ExtAng || ca->mType == CAM_Follow;
    Radian tilt(0.f);
    if (pSet->cam_tilt && bUseTilt)
    {
        //  car pos
        Vector3 carUp = posIn.pos - HupCar * posIn.carY;
        MATHVECTOR<float,3> pos(carUp.x, -carUp.z, carUp.y + Habove);  // to vdr/blt
        const static MATHVECTOR<float,3> dir(0,0,-1);  // cast rays down

        //  car rot, yaw angle
        Quaternion q = posIn.rot * Quaternion(Degree(90),Vector3(0,1,0));
        float angCarY = q.getYaw().valueRadians() + PI_d/2.f;
        float ax = cosf(angCarY)*Rdist, ay = sinf(angCarY)*Rdist;
        //LogO("pos: "+fToStr(pos[0],2,4)+" "+fToStr(pos[1],2,4)+"  a: "+fToStr(angCarY,2,4)+"  dir: "+fToStr(ax,2,4)+" "+fToStr(ay,2,4));

        //  cast 2 rays - 2 times, average 2 angles
        COLLISION_CONTACT ct0,ct1,ct20,ct21;
        MATHVECTOR<float,3> ofs(ax*0.5f,ay*0.5f,0),ofs2(ax,ay,0);
        world->CastRay(pos+ofs, dir, HMaxDepth,chassis, ct0,  0,0, true, true);
        world->CastRay(pos-ofs, dir, HMaxDepth,chassis, ct1,  0,0, true, true);
        world->CastRay(pos+ofs2,dir, HMaxDepth,chassis, ct20, 0,0, true, true);
        world->CastRay(pos-ofs2,dir, HMaxDepth,chassis, ct21, 0,0, true, true);

#ifdef CAM_TILT_DBG
        MATHVECTOR<float,3> v;
        v = pos+ofs;
        posHit[0] = Vector3(v[0],v[2]- ct0.GetDepth(), -v[1]);
        v = pos-ofs;
        posHit[1] = Vector3(v[0],v[2]- ct1.GetDepth(), -v[1]);
        v = pos+ofs2;
        posHit[2] = Vector3(v[0],v[2]- ct20.GetDepth(),-v[1]);
        v = pos-ofs2;
        posHit[3] = Vector3(v[0],v[2]- ct21.GetDepth(),-v[1]);
#endif

        if (ct0.GetColObj() && ct1.GetColObj() && ct20.GetColObj() && ct21.GetColObj() )
            tilt = (GetAngle(Rdist, ct1.GetDepth() - ct0.GetDepth()) +
                    GetAngle(2.f*Rdist, ct21.GetDepth() - ct20.GetDepth())) * 0.5f;
        //else  LogO(String("no hit: ")+(ct0.col?"1":"0")+(ct1.col?" 1":" 0"));

        //if (tilt < angMin && tilt > -angMin)  tilt = 0.f;
        if (tilt < r0 && tilt >-angMin) {
            Radian d = tilt-angMin;
            tilt = std::min(r0, tilt + d*d*5.f);
        }
        if (tilt > r0 && tilt < angMin) {
            Radian d =-angMin-tilt;
            tilt = std::max(r0, tilt - d*d*5.f);
        }

        //LogO("a "+fToStr(angCarY,3,5)+" d  "+fToStr(ct0.GetDepth(),3,5)+" "+fToStr(ct1.GetDepth(),3,5)+"  t "+fToStr(tilt.valueDegrees(),3,5));
    }
    //  smooth tilt angle
    mATilt += std::min(maxDiff, std::max(-maxDiff, tilt - mATilt)) * time * smoothSpeed;


    //-------------------------------------------------------------------------------------------
    if (ca->mType == CAM_Car)	/* 3 Car - car pos & rot full */
    {
        camPosFinal = goalLook;
        camRotFinal = orient;

        posOut->camPos = camPosFinal;  // save result in out posInfo
        posOut->camRot = camRotFinal;
        return;
    }

    if (ca->mType == CAM_Follow)  ofs = ca->mOffset;

    Vector3  pos,goalPos;
    pos     = camPosFinal - ofs;
    goalPos = posGoal;

    Vector3  xyz;
    if (ca->mType != CAM_Arena)
    {
        Real x,y,z,xz;   // pitch & yaw to direction vector
        Real ap = bUseTilt ? (ca->mPitch.valueRadians() + mATilt.valueRadians()) : 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;
        }
        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.002f, 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
        }
#endif

        Quaternion  qy = Quaternion(ca->mYaw,Vector3(0,1,0));
        goalPos += qq * (xyz + ca->mOffset);

        camPosFinal = goalPos;
        camRotFinal = qq * qy * Quaternion(Degree(-ca->mPitch - mATilt), 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 = camPosFinal;  //read last state (smooth)
            Pos += (goalPos - Pos) * dtmul;

            mAPitch += (ca->mPitch - mAPitch) * dtmul;
            mAYaw += (ca->mYaw - mAYaw) * dtmul;

            if (first)  {
                Pos = goalPos;
                mAPitch = ca->mPitch;
                mAYaw = ca->mYaw;
            }
            camPosFinal = Pos;
            camRotFinal = Quaternion(Degree(mAYaw),Vector3(0,1,0)) * Quaternion(Degree(mAPitch),Vector3(1,0,0));
            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;
            camPosFinal = pos + ofs;

            goalLook = posGoal + ofs;
            if (first)	{
                mLook = goalLook;
            }

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

    //camLookFinal = mLook;
    if (!manualOrient)  // CAM_Free or CAM_Follow
    {
        Vector3 zdir = camPosFinal - mLook;
        zdir.normalise();
        Vector3 xVec = Vector3::UNIT_Y.crossProduct(zdir);
        xVec.normalise();
        Vector3 yVec = zdir.crossProduct(xVec);
        yVec.normalise();
        Quaternion q;
        q.FromAxes(xVec, yVec, zdir);
        camRotFinal = q;
    }

    //  cast ray from car to camera, reduce dist if hit
    //-------------------------------------------------------------------------------------------
    Vector3 pp = camPosFinal;
    if (bounce)
        pp += posIn.camOfs * ca->mOfsMul
              * gPar.camBncScale * pSet->cam_bnc_mul;

    Vector3 p = posGoal;
    p.y += 1.f;  //up
    //Vector3 d = camRotFinal * Vector3::UNIT_Z;  d.normalise();
    Vector3 d = pp - p;
    d.normalise();

    if (!first && ca->mType != CAM_Arena)
    {
        MATHVECTOR<float,3> pos1(p.x,-p.z,p.y), dir(d.x,-d.z,d.y);  //dir = dir.Normalize();
        COLLISION_CONTACT ct;
        float maxLen = (p - pp).length();  //cam near
        world->CastRay(pos1, dir, maxLen,chassis, ct,  0,0, true, true, true/*+*/);
        //dbgLen = -maxLen;

        if (ct.GetColObj())
        {
            float len = ct.GetDepth();  //dbgLen = len;
            len -= 0.2f + ct.GetNormal()[2];  ///par  normal up, flat terrain, move closer
            if (len < maxLen)
            {
                Real dist = maxLen - len;
                if (dist > mDistReduce)
                    mDistReduce = dist;
            }
        }
    }

    //  save result in out posInfo
    posOut->camPos = mDistReduce > 0.0001f ? (pp - d * mDistReduce) : pp;
    posOut->camRot = camRotFinal;

    //  smooth, back to normal dist
    if (mDistReduce > 0.f)
        mDistReduce -= time * 10.f;
}
bool DynamicsWorld::castRay(
	const btVector3 & origin,
	const btVector3 & direction,
	const btScalar length,
	const btCollisionObject * caster,
	COLLISION_CONTACT & contact) const
{
	btVector3 p = origin + direction * length;
	btVector3 n = -direction;
	btScalar d = length;
	int patch_id = -1;
	const BEZIER * b = 0;
	const TRACKSURFACE * s = TRACKSURFACE::None();
	btCollisionObject * c = 0;

	MyRayResultCallback ray(origin, p, caster);
	rayTest(origin, p, ray);

	// track geometry collision
	bool geometryHit = ray.hasHit();
	if (geometryHit)
	{
		p = ray.m_hitPointWorld;
		n = ray.m_hitNormalWorld;
		d = ray.m_closestHitFraction * length;
		c = ray.m_collisionObject;
		if (c->isStaticObject())
		{
			TRACKSURFACE* tsc = static_cast<TRACKSURFACE*>(c->getUserPointer());
			const std::vector<TRACKSURFACE> & surfaces = track->GetSurfaces();
			if (tsc >= &surfaces[0] && tsc <= &surfaces[surfaces.size()-1])
			{
				s = tsc;
			}
#ifndef EXTBULLET
			else if (c->getCollisionShape()->isCompound())
			{
				TRACKSURFACE* tss = static_cast<TRACKSURFACE*>(ray.m_shape->getUserPointer());
				if (tss >= &surfaces[0] && tss <= &surfaces[surfaces.size()-1])
				{
					s = tss;
				}
			}
#endif
			//std::cerr << "static object without surface" << std::endl;
		}

		// track bezierpatch collision
		if (track)
		{
			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;
			MATHVECTOR<float, 3> colnormal;
			patch_id = contact.GetPatchId();

			if (track->CastRay(bezierspace_raystart, bezierspace_dir, length,
				patch_id, colpoint, b, colnormal))
			{
				p = btVector3(colpoint[2], colpoint[0], colpoint[1]);
				n = btVector3(colnormal[2], colnormal[0], colnormal[1]);
				d = (colpoint - bezierspace_raystart).Magnitude();
			}
		}

		contact = COLLISION_CONTACT(p, n, d, patch_id, b, s, c);
		return true;
	}

	// should only happen on vehicle rollover
	contact = COLLISION_CONTACT(p, n, d, patch_id, b, s, c);
	return false;
}
Esempio n. 4
0
//  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;
}
Esempio n. 5
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);
}
Esempio n. 6
0
//--------------------------------------------------------------------------------------------------------------------------------
//  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;
}