Esempio n. 1
0
 void operator()(FeatureType const & ft)
 {
   static CarModel const carModel;
   if (ft.GetFeatureType() != feature::GEOM_LINE || !carModel.IsRoad(ft))
     return;
   uint32_t const featureId = ft.GetID().m_index;
   for (auto const n : m_routingMapping.m_segMapping.GetNodeIdByFid(featureId))
     n_nodeIds.push_back(n);
 }
Esempio n. 2
0
//  updatePoses - Set car pos for Ogre nodes, update particles, trails
//---------------------------------------------------------------------------------------------------------------
void App::updatePoses(float time)
{
    if (carModels.empty())  return;
    PROFILER.beginBlock(".updPos ");

    //  Update all carmodels from their carPos
    const CarModel* playerCar = carModels.front();

    int cgh = -1;
    for (int c = 0; c < carModels.size(); ++c)
    {
        CarModel* carM = carModels[c];
        if (!carM)  {
            PROFILER.endBlock(".updPos ");
            return;
        }

        ///  ghosts visibility  . . .
        //  hide when empty or near car
        bool bGhostCar = carM->eType == (isGhost2nd ? CarModel::CT_GHOST2 : CarModel::CT_GHOST),  // show only actual
             bGhTrkVis = carM->isGhostTrk() && ghtrk.GetTimeLength()>0 && pSet->rpl_trackghost,
             bGhostVis = ghplay.GetNumFrames()>0 && pSet->rpl_ghost,
             bGhostEnd = pGame->timer.GetPlayerTime(0) > ghplay.GetTimeLength();
        if (bGhostCar)  cgh = c;

        if (carM->isGhost())  // for all
        {
            bool loading = iLoad1stFrames >= 0;  // show during load ?..
            bool curVisible = carM->mbVisible;
            bool newVisible = bGhostVis && bGhostCar /**/&& !bGhostEnd/**/ || bGhTrkVis;

            if (loading)
                carM->setVisible(true);  //!carM->isGhost());
            else
            {   //  hide ghost when close to player
                float d = carM->pMainNode->getPosition().squaredDistance(playerCar->pMainNode->getPosition());
                if (d < pSet->ghoHideDist * pSet->ghoHideDist)
                    newVisible = false;

                if (carM->isGhostTrk() && cgh >= 0)  // hide track's ghost when near ghost
                {
                    float d = carM->pMainNode->getPosition().squaredDistance(carModels[cgh]->pMainNode->getPosition());
                    if (d < pSet->ghoHideDistTrk * pSet->ghoHideDistTrk)
                        newVisible = false;
                }
                if (curVisible == newVisible)
                    carM->hideTime = 0.f;
                else
                {   carM->hideTime += time;  // change vis after delay
                    if (carM->hideTime > gPar.ghostHideTime)
                        carM->setVisible(newVisible);
                }
            }
        }


        //  update car pos
        int q = iCurPoses[c];
        int cc = (c + iRplCarOfs) % carModels.size();  // replay offset, camera from other car
        int qq = iCurPoses[cc];
        PosInfo& pi = carPoses[q][c], &pic = carPoses[qq][cc];
        carM->Update(carPoses[q][c], carPoses[qq][cc], time);


        //  nick text pos upd  3d to 2d
        if (carM->pNickTxt && carM->pMainNode)
        {
            Camera* cam = playerCar->fCam->mCamera;  //above car 1m
            Vector3 p = hud->projectPoint(cam, carM->pMainNode->getPosition() + Vector3(0,1.f,0));
            p.x = p.x * mSplitMgr->mDims[0].width * 0.5f;  //1st viewport dims
            p.y = p.y * mSplitMgr->mDims[0].height * 0.5f;
            carM->pNickTxt->setPosition(p.x-40, p.y-16);  //center doesnt work
            carM->pNickTxt->setVisible(p.z > 0.f);
        }
    }

    ///  Replay info
    if (bRplPlay && !pGame->cars.empty())
    {
        double pos = pGame->timer.GetPlayerTime(0);
        float len = replay.GetTimeLength();
        gui->valRplPerc->setCaption(fToStr(pos/len*100.f, 1,4)+" %");
        gui->valRplCur->setCaption(CHud::StrTime(pos));
        gui->valRplLen->setCaption(CHud::StrTime(len));

        float v = pos/len;
        gui->slRplPos->setValue(v);
    }


    ///  objects - dynamic (props)  -------------------------------------------------------------
    for (int i=0; i < scn->sc->objects.size(); ++i)
    {
        Object& o = scn->sc->objects[i];
        if (o.ms)
        {
            btTransform tr, ofs;
            o.ms->getWorldTransform(tr);
            const btVector3& p = tr.getOrigin();
            const btQuaternion& q = tr.getRotation();
            o.pos[0] = p.x();
            o.pos[1] = p.y();
            o.pos[2] = p.z();
            o.rot[0] = q.x();
            o.rot[1] = q.y();
            o.rot[2] = q.z();
            o.rot[3] = q.w();
            o.SetFromBlt();
        }
    }

    PROFILER.endBlock(".updPos ");
}
Esempio n. 3
0
//  newPoses - Get new car pos from game
//  caution: called from GAME, 2nd thread, no Ogre stuff here
/// Todo: move arrow update and ChampionshipAdvance to updatePoses ...
//---------------------------------------------------------------------------------------------------------------
void App::newPoses(float time)  // time only for camera update
{
    if (!pGame || bLoading || pGame->cars.empty() /*|| carPoses.empty() || iCurPoses.empty()*/)
        return;
    PROFILER.beginBlock(".newPos ");

    double rplTime = pGame->timer.GetReplayTime(0);  // from start
    double lapTime = pGame->timer.GetPlayerTime(0);
    double rewTime = pSet->rpl_ghostrewind ? pGame->timer.GetRewindTimeGh(0) : lapTime;

    //  iterate through all car models and set new pos info (from vdrift sim or replay)
    CarModel* carM0 = carModels[0];
    for (int c = 0; c < carModels.size(); ++c)
    {
        CarModel* carM = carModels[c];
        bool bGhost = carM->isGhost();
        CAR* pCar = carM->pCar;

        PosInfo pi;  // new, to fill data


        //  car perf test  logic
        //--------------------------------
        if (bPerfTest && c==0)
            newPerfTest(time);


        //  play  get data from replay / ghost
        ///-----------------------------------------------------------------------
        if (bGhost)
        {   ///>>  track's ghost
            if (carM->isGhostTrk())
            {
                TrackFrame tf;       // par: sec after, 1st lap
                float lap1 = pGame->timer.GetCurrentLap(0) > 0 ? 2.f : 0.f;
                bool ok = ghtrk.GetFrame(rewTime + lap1, &tf);
                //  car
                Axes::toOgre(pi.pos, tf.pos);
                pi.rot = Axes::toOgre(tf.rot);
                pi.braking = tf.brake;
                pi.steer = tf.steer / 127.f;
                //pi.fboost = 0.f;  pi.speed = 0.f;  pi.percent = 0.f;
                //pi.fHitTime = 0.f;  pi.fParIntens = 0.f;  pi.fParVel = 0.f;
                //pi.vHitPos = Vector3::ZERO;  pi.vHitNorm = Vector3::UNIT_Y;

                //  wheels
                //dynamics.SetSteering(state.steer, pGame->GetSteerRange());  //peers can have other game settins..
                for (int w=0; w < carM->numWheels; ++w)
                {
                    MATHVECTOR<float,3> whP = carM->whPos[w];
                    whP[2] += 0.05f;  // up
                    tf.rot.RotateVector(whP);
                    Axes::toOgre(pi.whPos[w], tf.pos + whP);

                    if (w < 2)  // front steer
                    {   float a = (pi.steer * carM->maxangle) * -PI_d/180.f;
                        QUATERNION<float> q;
                        q.Rotate(a, 0,0,1);
                        pi.whRot[w] = Axes::toOgreW(tf.rot * carM->qFixWh[w%2] * q);
                    } else
                        pi.whRot[w] = Axes::toOgreW(tf.rot * carM->qFixWh[w%2]);
                }
            } else  ///>>  ghost
            {
                ReplayFrame2 gf;
                float ti = std::min((float)rewTime, ghplay.GetTimeLength());
                bool ok = ghplay.GetFrame(ti, &gf, 0);
                if (ok)
                    pi.FromRpl2(&gf, 0);

                if (carM->vtype == V_Sphere)
                {   //  weird fix, mini rot
                    pi.carY = Vector3::UNIT_Y;
                    pi.hov_roll = -pi.hov_roll;
                }
            }
        }
        else  ///>>  replay
            if (bRplPlay)
            {
#ifdef DEBUG
                assert(c < frm.size());
#endif
                ReplayFrame2& rf = frm[c];  // frm also used in car.cpp for sounds
                if (c < replay.header.numPlayers)
                {
                    bool ok = replay.GetFrame(rplTime, &rf, c);
                    if (ok)
                    {   pi.FromRpl2(&rf, &pCar->dynamics);
                        pCar->SetPosition(rf.pos, rf.rot);  // for objs hit
                        carM->trackPercent = rf.percent /255.f*100.f;
                    } else
                    {   carM->fCam->First();
                        pGame->timer.RestartReplay(0);  //at end
                    }
                }
            }
            else  ///>>  sim, game  -  get data from vdrift
                if (pCar)
                {
                    pi.FromCar(pCar);
                    pi.percent = carM->trackPercent;
                }
        pi.bNew = true;


        //<<  rewind
        ///-----------------------------------------------------------------------
        if (!bRplPlay && !pGame->pause && !bGhost && pCar)
            if (pCar->bRewind && pSet->game.rewind_type > 0)
            {   //  do rewind (go back)
                double& gtime = pGame->timer.GetRewindTime(c);
                gtime = std::max(0.0, gtime - time * gPar.rewindSpeed);
                double& ghtim = pGame->timer.GetRewindTimeGh(c);
                ghtim = std::max(0.0, ghtim - time * gPar.rewindSpeed);  //rewind ghost time too
                if (gPar.backTime)
                {   pGame->timer.Back(c, - time * gPar.rewindSpeed);
                    ghost.DeleteFrames(0, ghtim);
                }
                RewindFrame rf;
                bool ok = rewind.GetFrame(gtime, &rf, c);

                pCar->SetPosRewind(rf.pos, rf.rot, rf.vel, rf.angvel);
                pCar->dynamics.fDamage = rf.fDamage;  // take damage back
                if (carModels[c]->vtype == V_Sphere)
                    pCar->dynamics.sphereYaw = rf.hov_roll;
                carModels[c]->First();
            }
            else if (c < 4)  // save data
            {
                const CARDYNAMICS& cd = pCar->dynamics;
                RewindFrame fr;
                fr.time = pGame->timer.GetRewindTime(c);

                fr.pos = cd.body.GetPosition();
                fr.rot = cd.body.GetOrientation();
                fr.vel = cd.GetVelocity();
                fr.angvel = cd.GetAngularVelocity();
                fr.fDamage = cd.fDamage;
                if (cd.vtype == V_Sphere)
                    fr.hov_roll = cd.sphereYaw;
                else
                    fr.hov_roll = cd.hov_roll;  //? fr.hov_throttle = cd.hov_throttle;

                rewind.AddFrame(fr, c);  // rec rewind
            }

        //<<  record  save data
        ///-----------------------------------------------------------------------
        if (pSet->rpl_rec && !bRplPlay && !pGame->pause && !bGhost && pCar)
        {
            if (iRplSkip++ >= 1)  // 1 half game framerate
            {   iRplSkip = 0;

                ReplayFrame2 fr;
                fr.time = rplTime;
                fr.percent = carM->trackPercent /100.f*255.f;

                fr.FromCar(pCar, replay.GetLastHitTime(c));

                replay.AddFrame(fr, c);  // rec replay
                if (c==0)  /// rec ghost lap
                {
                    fr.time = lapTime;
                    ghost.AddFrame(fr, 0);
                }

                //  recorded info ..in update
                {
                    int size = replay.GetNumFrames() * 232;  //par approx  sizeof(ReplayFrame);
                    std::string s = fToStr( float(size)/1000000.f, 2,5);
                    String ss = String( TR("#{RplRecTime}: ")) + CHud::StrTime(replay.GetTimeLength()) + TR("   #{RplSize}: ") + s + TR(" #{UnitMB}");
                    gui->valRplName2->setCaption(ss);
                }
            }
        }
        if (bRplPlay)  gui->valRplName2->setCaption("");
        ///-----------------------------------------------------------------------


        //  checkpoints, lap start
        //-----------------------------------------------------------------------
        if (bRplPlay || bGhost)   // dont check for replay or ghost
            carM->bWrongChk = false;
        else
        {
            ///  arrow update  --------------------------------------
            SplineRoad* road = scn->road;
            if (pSet->check_arrow && carM->eType == CarModel::CT_LOCAL
                    && !bRplPlay && hud->arrow.node && road && road->mChks.size()>0)
            {
                //  set animation start to old orientation
                hud->arrow.qStart = hud->arrow.qCur;

                //  game start: no animation
                bool noAnim = carM->iNumChks == 0;

                //  get vector from camera to checkpoint
                Vector3 chkPos = road->mChks[std::max(0, std::min((int)road->mChks.size()-1, carM->iNextChk))].pos;

                //  last checkpoint, point to start pos
                if (carM->iNumChks == road->mChks.size())
                    chkPos = carM->vStartPos;

                const Vector3& playerPos = pi.pos;
                Vector3 dir = chkPos - playerPos;
                dir[1] = 0;  // only x and z rotation
                Quaternion quat = Vector3::UNIT_Z.getRotationTo(-dir);

                const bool valid = !quat.isNaN();
                if (valid)
                {   if (noAnim)  hud->arrow.qStart = quat;
                    hud->arrow.qEnd = quat;

                    //  calc angle towards cam
                    Real angle = (hud->arrow.qCur.zAxis().dotProduct(carM->fCam->mCamera->getOrientation().zAxis())+1)/2.0f;

                    //  set color in material (red for wrong dir)
                    Vector3 col1 = angle * Vector3(0.0, 1.0, 0.0) + (1-angle) * Vector3(1.0, 0.0, 0.0);
                    Vector3 col2 = angle * Vector3(0.0, 0.4, 0.0) + (1-angle) * Vector3(0.4, 0.0, 0.0);

                    sh::Vector3* v1 = new sh::Vector3(col1.x, col1.y, col1.z);
                    sh::Vector3* v2 = new sh::Vector3(col2.x, col2.y, col2.z);
                    sh::Factory::getInstance().setSharedParameter("arrowColour1", sh::makeProperty <sh::Vector3>(v1));
                    sh::Factory::getInstance().setSharedParameter("arrowColour2", sh::makeProperty <sh::Vector3>(v2));
                }
            }

            //----------------------------------------------------------------------------
            if (carM->bGetStPos)  // first pos is at start
            {   carM->bGetStPos = false;
                carM->matStPos.makeInverseTransform(pi.pos, Vector3::UNIT_SCALE, pi.rot);
                carM->ResetChecks();
            }
            if (road && !carM->bGetStPos)
            {
                //  start/finish box dist
                Vector4 carP(pi.pos.x,pi.pos.y,pi.pos.z,1);
                carM->vStDist = carM0->matStPos * carP;  // start pos from 1st car always
                carM->bInSt = abs(carM->vStDist.x) < road->vStBoxDim.x &&
                              abs(carM->vStDist.y) < road->vStBoxDim.y &&
                              abs(carM->vStDist.z) < road->vStBoxDim.z;

                carM->iInChk = -1;
                carM->bWrongChk = false;
                bool locar = carM->eType == CarModel::CT_LOCAL;
                int ncs = road->mChks.size();
                if (ncs > 0)
                {
                    //  Finish  --------------------------------------
                    if (locar &&
                            (carM->bInSt && carM->iNumChks == ncs && carM->iCurChk != -1))
                    {
                        ///  Lap
                        bool finished = (pGame->timer.GetCurrentLap(c) >= pSet->game.num_laps)
                                        && (mClient || pSet->game.local_players > 1);  // multiplay or split
                        bool best = finished ? false :  // dont inc laps when race over (in multiplayer or splitscreen mode)
                                    pGame->timer.Lap(c, !finished, pSet->game.trackreverse);  //,boost_type?
                        double timeCur = pGame->timer.GetPlayerTimeTot(c);

                        //  Network notification, send: car id, lap time  ----
                        if (mClient && c == 0 && !finished)
                            mClient->lap(pGame->timer.GetCurrentLap(c), pGame->timer.GetLastLap(c));

                        ///  new best lap, save ghost
                        bool newbest = false;
                        if (!pSet->rpl_bestonly || best || gPar.backTime)
                            if (c==0 && pSet->rpl_rec)  // for many, only 1st car
                            {
                                ghost.SaveFile(gui->GetGhostFile());  //,boost_type?
                                ghplay.CopyFrom(ghost);
                                isGhost2nd = false;  // hide 2nd ghost
                                newbest = true;
                            }

                        bool champ = pSet->game.champ_num >= 0, chall = pSet->game.chall_num >= 0;
                        bool chs = champ || chall;

                        if (!chs)
                        {   if (newbest)
                                pGame->snd_lapbest->start();  //)
                            else
                                pGame->snd_lap->start();  //)
                        }
                        ghost.Clear();

                        carM->ResetChecks();

                        //  restore boost fuel, each lap  ----
                        if (pSet->game.boost_type == 1 && carM->pCar)
                            carM->pCar->dynamics.boostFuel = carM->pCar->dynamics.boostFuelStart;

                        //  damage decrease, each lap  ---
                        if (pSet->game.damage_dec > 0.f)
                            carM->pCar->dynamics.fDamage = std::max(0.f,
                                                                    carM->pCar->dynamics.fDamage - pSet->game.damage_dec);

                        //  upd lap results ----
                        carM->updLap = false;
                        carM->fLapAlpha = 1.f;

                        ///  all laps
                        finished = pGame->timer.GetCurrentLap(c) >= pSet->game.num_laps;
                        if (finished && !mClient)
                        {
                            if (!chs)
                            {
                                if (carM->iWonPlace == 0)	//  split screen winner places
                                {
                                    if (pSet->game.local_players > 1)
                                    {
                                        int n = std::min(2, std::max(0, 3 - carIdWin));
                                        pGame->snd_win[n]->start();  //)
                                    }
                                    carM->iWonPlace = carIdWin++;
                                }
                            }
                            else if (champ)
                                gui->ChampionshipAdvance(timeCur);
                            else
                                gui->ChallengeAdvance(timeCur);
                        }
                    }

                    //  checkpoints  --------------------------------------
                    for (int i=0; i < ncs; ++i)
                    {
                        const CheckSphere& cs = road->mChks[i];
                        Real d2 = pi.pos.squaredDistance(cs.pos);
                        if (d2 < cs.r2)  // car in checkpoint
                        {
                            carM->iInChk = i;

                            //\  loop camera change
                            if (pSet->cam_loop_chng && carM->fCam &&
                                    cs.loop && (carM->iLoopChk == -1 || carM->iLoopChk != i) &&
                                    pCar->dynamics.vtype != V_Sphere)
                            {
                                carM->iLoopChk = i;
                                if (carM->iLoopLastCam == -1)
                                {
                                    carM->iLoopLastCam = carM->fCam->miCurrent;
                                    carM->fCam->setCamera(pSet->cam_in_loop);
                                } else
                                {   carM->fCam->setCamera(carM->iLoopLastCam);
                                    carM->iLoopLastCam = -1;
                                }
                            }
                            //  next check
                            if (i == carM->iNextChk && carM->iNumChks < ncs)
                            {
                                carM->iCurChk = i;
                                carM->iNumChks++;
                                carM->timeAtCurChk = pGame->timer.GetPlayerTime(c);
                                int ii = (pSet->game.trackreverse ? -1 : 1) * road->iDir;
                                carM->iNextChk = (carM->iCurChk + ii + ncs) % ncs;
                                carM->UpdNextCheck();
                                //  save car pos and rot
                                carM->pCar->SavePosAtCheck();
                                carM->updTimes = true;

                                if (pSet->snd_chk && locar)
                                    pGame->snd_chk->start();  //)
                            }
                            else if (carM->iInChk != carM->iCurChk &&
                                     !scn->sc->noWrongChks)  // denies
                            {
                                carM->bWrongChk = true;

                                if (carM->iInWrChk != carM->iInChk)
                                {   carM->iInWrChk = carM->iInChk;

                                    if (pSet->snd_chkwr && locar)
                                        pGame->snd_chkwr->start();  //)
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }


        ///  store new pos info in queue  _________
        if (!(isFocGui || isTweakTab()) || mClient)  // dont if gui, but network always
        {
            int qn = (iCurPoses[c] + 1) % CarPosCnt;  // next index in queue
            carPoses[qn][c] = pi;
            //  update camera
            if (carM->fCam)
                carM->fCam->update(time, pi, &carPoses[qn][c], &pGame->collision, !bRplPlay && pSet->cam_bounce);
            iCurPoses[c] = qn;  // atomic, set new index in queue

            ///))  upd sound camera
            if (c == 0 && pGame->snd)
            {
                Vector3 x,y,z;
                carPoses[qn][c].camRot.ToAxes(x,y,z);
                pGame->snd->setCamera(carPoses[qn][c].camPos, -z, y, Vector3::ZERO);
            }
        }
    }
    PROFILER.endBlock(".newPos ");
}
Esempio n. 4
0
///---------------------------------------------------------------------------------------------------------------
//  Update HUD
///---------------------------------------------------------------------------------------------------------------
void CHud::Update(int carId, float time)
{
	PROFILER.beginBlock("g.hud");

	
	//  update HUD elements for all cars that have a viewport (local or replay)
	//-----------------------------------------------------------------------------------
	int cnt = std::min(6/**/, (int)app->carModels.size());  // all cars
	int cntC = std::min(4/**/, cnt - (app->isGhost2nd && !app->bRplPlay ? 1 : 0));  // all vis plr
	int c;
	
	//  gui viewport - done once for all
	if (carId == -1)
	for (c = 0; c < cntC; ++c)
	if (app->carModels[c]->eType == CarModel::CT_LOCAL)
	{
		//  hud rpm,vel
		float vel=0.f, rpm=0.f, clutch=1.f;  int gear=1;
		GetVals(c,&vel,&rpm,&clutch,&gear);
		
		//  update all mini pos tri
		for (int i=0; i < cnt; ++i)
			UpdRot(c, i, vel, rpm);
	}

	///  all minimap car pos-es rot
	const static Real tc[4][2] = {{0,1}, {1,1}, {0,0}, {1,0}};
	const float z = pSet->size_minipos;  // tri size
	
	if (carId == -1 && moPos)
	{	moPos->beginUpdate(0);

		const int plr = app->mSplitMgr->mNumViewports;
		for (int v = 0; v < plr; ++v)  // all viewports
		{
			const Hud& h = hud[v];
			const float sc = pSet->size_minimap * app->mSplitMgr->mDims[v].avgsize;
			const Vector3& pos = h.ndMap->getPosition();
			
			for (c = 0; c < cntC; ++c)  // all mini pos for one car
			{
				const SMiniPos& sp = h.vMiniPos[c];
				const ColourValue& clr = app->carModels[c]->color;

				for (int p=0; p < 4; ++p)  // all 4 points
				{
					float x = pos.x + (sp.x + sp.px[p]*z)*sc;
					float y = pos.y + (sp.y + sp.py[p]*z)*sc*asp;
					moPos->position(x, y, 0);
					moPos->textureCoord(tc[p][0], tc[p][1]);
					moPos->colour(clr);
		}	}	}
		
		int ii = plr * cntC;
		for (int i=0; i < ii; ++i)
		{	int n = i*4;
			moPos->quad(n,n+1,n+3,n+2);
		}
		moPos->end();
	}

	//  track% local, updated always
	for (c = 0; c < cntC; ++c)
	{	CarModel* cm = app->carModels[c];
		if (cm->eType == CarModel::CT_LOCAL ||
			cm->eType == CarModel::CT_REPLAY)
			cm->UpdTrackPercent();
	}
	
	if (carId == -1 || app->carModels.empty())
	{
		PROFILER.endBlock("g.hud");
		return;
	}

	#ifdef DEBUG
	assert(carId >= 0);
	assert(carId < app->carModels.size());
	#endif


	CarModel* pCarM = app->carModels[carId];
	CAR* pCar = pCarM ? pCarM->pCar : 0;

	float vel=0.f, rpm=0.f, clutch=1.f;  int gear=1;
	GetVals(carId,&vel,&rpm,&clutch,&gear);
	Hud& h = hud[carId];


	///  multiplayer
	// -----------------------------------------------------------------------------------
	static float tm = 0.f;  tm += time;
	if (tm > 0.2f /**/&& app->mClient/**/)  // not every frame, each 0.2s
	// if (pSet->game.isNetw) ..
	{
		//  sort winners
		std::list<CarModel*> cms;
		for (c=0; c < cnt; ++c)
			cms.push_back(app->carModels[c]);

		cms.sort(SortWin);
		//stable_sort(cms.begin(), cms.end(), SortWin);
		
		String msg = "";  int place = 1;  // assing places
		for (std::list<CarModel*>::iterator it = cms.begin(); it != cms.end(); ++it)
		{
			CarModel* cm = *it;
			bool end = app->pGame->timer.GetCurrentLap(cm->iIndex) >= pSet->game.num_laps;
			cm->iWonPlace = end ? place++ : 0;  // when ended race

			//  detect change (won),  can happen more than once, if time diff < ping delay
			if (cm->iWonPlace != cm->iWonPlaceOld)
			{	cm->iWonPlaceOld = cm->iWonPlace;
				cm->iWonMsgTime = gPar.timeWonMsg;
				if (cm->iIndex == 0)  // for local player, show end wnd
					app->mWndNetEnd->setVisible(true);
			}
			if (cm->iWonMsgTime > 0.f)
			{	cm->iWonMsgTime -= tm;
				if (cm->iWonPlace != 0)
					msg += cm->sDispName + " " + TR("#{FinishedCommaPlace}") + ": " + toStr(cm->iWonPlace) + "\n";
			}
		}
		if (app->mClient && /*ap->pGame->timer.pretime <= 0.f &&*/ app->pGame->timer.waiting)
			msg += TR("#{NetWaitingForOthers}")+"...\n";
			
		//  chat 2 last lines
		if (gui->sChatLast1 != "")	msg += gui->sChatLast1 + "\n";
		if (gui->sChatLast2 != "")	msg += gui->sChatLast2;
			
		++gui->iChatMove;
		if (gui->iChatMove >= 10)  //par 2sec
		{	gui->iChatMove = 0;
			gui->sChatLast1 = gui->sChatLast2;
			gui->sChatLast2 = "";
		}
		
		//  upd hud msgs
		if (txMsg)
		{	txMsg->setCaption(msg);
			bckMsg->setVisible(!msg.empty());
		}

		//  upd end list
		if (app->mWndNetEnd->getVisible())
		{
			MyGUI::MultiList2* li = gui->liNetEnd;
			li->removeAllItems();
			for (std::list<CarModel*>::iterator it = cms.begin(); it != cms.end(); ++it)
			{
				CarModel* cm = *it;
				String clr = StrClr(cm->color);

				li->addItem(""/*clr+ toStr(c+1)*/, 0);  int l = li->getItemCount()-1;
				li->setSubItemNameAt(1,l, clr+ (cm->iWonPlace == 0 ? "--" : toStr(cm->iWonPlace)));
				li->setSubItemNameAt(2,l, clr+ cm->sDispName);
				li->setSubItemNameAt(3,l, clr+ StrTime( cm->iWonPlace == 0 ? 0.f : app->pGame->timer.GetPlayerTimeTot(cm->iIndex) ));
				//li->setSubItemNameAt(4,l, clr+ fToStr(cm->iWonMsgTime,1,3));
				li->setSubItemNameAt(4,l, clr+ StrTime( app->pGame->timer.GetBestLapRace(cm->iIndex) ));
				li->setSubItemNameAt(5,l, clr+ toStr( app->pGame->timer.GetCurrentLap(cm->iIndex) ));
		}	}
		tm = 0.f;
	}


	///  opponents list
	// -----------------------------------------------------------------------------------
	bool visOpp = h.txOpp[0] && pSet->show_opponents;
	if (visOpp && pCarM && pCarM->pMainNode)
	{
		std::list<CarModel*> cms;  // sorted list
		for (c=0; c < cnt; ++c)
		{	//  cars only
			CarModel* cm = app->carModels[c];
			if (!cm->isGhost())
			{	if (app->bRplPlay)
					cm->trackPercent = app->carPoses[app->iCurPoses[c]][c].percent;
				cms.push_back(cm);
		}	}
		if (pSet->opplist_sort)
			cms.sort(SortPerc);
		
		for (c=0; c < cnt; ++c)
		{	//  add last, if visible, ghost1 (dont add 2nd) and track's ghost
			CarModel* cm = app->carModels[c];
			if (cm->eType == (app->isGhost2nd ? CarModel::CT_GHOST2 : CarModel::CT_GHOST) && pSet->rpl_ghost ||
				cm->isGhostTrk() && pSet->rpl_trackghost)
			{
				cm->trackPercent = app->carPoses[app->iCurPoses[c]][c].percent;  // ghost,rpl
				cms.push_back(cm);
		}	}

		bool bGhostEnd = app->pGame->timer.GetPlayerTime(0) > app->ghplay.GetTimeLength();
		String s0,s1,s2;  // Track% Dist Nick
		ColourValue clr;  c = 0;
		for (std::list<CarModel*>::iterator it = cms.begin(); it != cms.end(); ++it)
		{
			CarModel* cm = *it;
			if (cm->pMainNode)
			{
				bool bGhost = cm->isGhost() && !cm->isGhostTrk();
				bool bGhostVis = (app->ghplay.GetNumFrames() > 0) && pSet->rpl_ghost;
				bool bGhEmpty = bGhost && !bGhostVis;

				//  dist  -----------
				if (cm == pCarM || bGhEmpty)  // no dist to self or to empty ghost
					s1 += "\n";
				else
				{	Vector3 v = cm->pMainNode->getPosition() - pCarM->pMainNode->getPosition();
					float dist = v.length();  // meters
					Real h = std::min(60.f, dist) / 60.f;
					clr.setHSB(0.5f - h * 0.4f, 1, 1);
					s1 += StrClr(clr)+ fToStr(dist,0,3)+"m\n";
				}
					
				//  percent %  -----------
				if (bGhEmpty || cm->isGhostTrk())
					s0 += "\n";
				else
				{	float perc = bGhost && bGhostEnd ? 100.f : cm->trackPercent;
					clr.setHSB(perc*0.01f * 0.4f, 0.7f, 1);
					s0 += StrClr(clr)+ fToStr(perc,0,3)+"%\n";
				}
				
				//  nick name  -----------
				if (cm->eType != CarModel::CT_REPLAY)
				{
					s2 += StrClr(cm->color)+ cm->sDispName;
					bool end = app->pGame->timer.GetCurrentLap(cm->iIndex) >= pSet->game.num_laps
							&& (app->mClient || pSet->game.local_players > 1);  // multiplay or split
					if (end)  //  place (1)
						s2 += "  (" + toStr(cm->iWonPlace) + ")";
				}
				s2 += "\n";  ++c;
		}	}
		//  upd pos, size
		if (h.lastOppH != c)
		{	h.lastOppH = c;
			int y = c*25 +4, yo = h.yOpp - y-4;

			for (int n=0; n < 3; ++n)
			{	h.txOpp[n]->setPosition(h.xOpp + n*65+5, yo + 3);
				h.txOpp[n]->setSize(90,y);
			}
			h.bckOpp->setPosition(h.xOpp, yo);
			h.bckOpp->setSize(230,y);
		}
		h.txOpp[0]->setCaption(s0);  h.txOpp[1]->setCaption(s1);  h.txOpp[2]->setCaption(s2);
	}

	//  Set motion blur intensity for this viewport, depending on car's linear velocity
	//-----------------------------------------------------------------------------------
	if (pSet->blur)
	{
		// use velocity squared to achieve an exponential motion blur
		float speed = pCar->GetVelocity().MagnitudeSquared();
		
		// peak at 250 kmh (=69 m/s), 69² = 4761
		// motion blur slider: 1.0 = peak at 100 km/h   0.0 = peak at 400 km/h   -> 0.5 = peak at 250 km/h
		// lerp(100, 400, 1-motionBlurIntensity)
		float peakSpeed = 100 + (1-pSet->blur_int) * (400-100);
		float intens = fabs(speed) / pow((peakSpeed/3.6f), 2);
		
		// higher fps = less perceived motion blur time a frame will be still visible on screen:
		// each frame, 1-intens of the original image is lost
		// example (intens = 0.7):
		//	   frame 1: full img		   frame 2: 0.7  * image
		//	   frame 3: 0.7² * image	   frame 4: 0.7³ * image
		// portion of image visible after 'n' frames: pow(intens, n);
		//	   example 1: 60 fps	   0.7³ image after 4 frames: 0.066 sec
		//	   example 2: 120 fps	   0.7³ image after 4 frames: 0.033 sec
		// now: need to achieve *same* time for both fps values
		// to do this, adjust intens
		// (1.0/fps) * pow(intens, n) == (1.0/fps2) * pow(intens2, n)
		// set n=4  intens_new = sqrt(sqrt((intens^4 * fpsReal/desiredFps))
		intens = sqrt(sqrt( pow(intens, 4) * ((1.0f/time) / 120.0f) ));
			
		intens = std::min(intens, 0.9f);  // clamp to 0.9f
		app->motionBlurIntensity = intens;
	}


	///  gear, vel texts  -----------------------------
	if (h.txVel && h.txGear && pCar)
	{
		float cl = clutch*0.8f + 0.2f;
		if (gear == -1)
		{	h.txGear->setCaption("R");  h.txGear->setTextColour(Colour(0.3,1,1,cl));  }
		else if (gear == 0)
		{	h.txGear->setCaption("N");  h.txGear->setTextColour(Colour(0.3,1,0.3,cl));  }
		else if (gear > 0 && gear < 8)
		{	h.txGear->setCaption(toStr(gear));  h.txGear->setTextColour(Colour(1,1-gear*0.1,0.2,cl));  }

		h.txVel->setCaption(fToStr(fabs(vel),0,3));

		float k = pCar->GetSpeedometer() * 3.6f * 0.0025f;	// vel clr
		#define m01(x)  std::min(1.0f, std::max(0.0f, (float) (x) ))
		h.txVel->setTextColour(Colour(m01(k*2), m01(0.5+k*1.5-k*k*2.5), m01(1+k*0.8-k*k*3.5)));
	}

	//  boost fuel (time)  ------
	if (h.txBFuel && pCar && h.txBFuel->getVisible())
	{
		h.txBFuel->setCaption(fToStr(pCar->dynamics.boostFuel,1,3));
	}

	//  damage %  ------
	if (h.txDamage && pCar && h.txDamage->getVisible())
	{
		float d = std::min(100.f, pCar->dynamics.fDamage);
		//h.txDamage->setCaption(TR("#{Damage}\n     ")+fToStr(d,0,3)+" %");  d*=0.01f;
		h.txDamage->setCaption(fToStr(d,0,3)+" %");  d*=0.01f;
		float e = std::min(1.f, 0.8f + d*2.f);
		h.txDamage->setTextColour(Colour(e-d*d*0.4f, std::max(0.f, e-d), std::max(0.f, e-d*2.f) ));
	}
	
	//  abs, tcs on  ------
	if (h.txAbs && h.txTcs && pCar)
	{
		bool vis = pCar->GetABSEnabled();  h.txAbs->setVisible(vis);
		if (vis)  h.txAbs->setAlpha(pCar->GetABSActive() ? 1.f : 0.6f);
		
		vis = pCar->GetTCSEnabled();  h.txTcs->setVisible(vis);
		if (vis)  h.txTcs->setAlpha(pCar->GetTCSActive() ? 1.f : 0.6f);
	}
	
	
	///  times, race pos  -----------------------------
	if (pSet->show_times && pCar)
	{
		TIMER& tim = app->pGame->timer;
		bool hasLaps = pSet->game.local_players > 1 || pSet->game.champ_num >= 0 || pSet->game.chall_num >= 0 || app->mClient;
		if (hasLaps)
		{	//  place
			if (pCarM->iWonPlace > 0 && h.txPlace)
			{
				String s = TR("---  "+toStr(pCarM->iWonPlace)+" #{TBPlace}  ---");
				h.txPlace->setCaption(s);
				const static Colour clrPlace[4] = {
					Colour(0.4,1,0.2), Colour(1,1,0.3), Colour(1,0.7,0.2), Colour(1,0.5,0.2) };
				h.txPlace->setTextColour(clrPlace[pCarM->iWonPlace-1]);
				h.bckPlace->setVisible(true);
		}	}

		//  times  ------------------------------
		bool cur = pCarM->iCurChk >= 0 && !app->vTimeAtChks.empty();
		float ghTimeES = cur ? app->vTimeAtChks[pCarM->iCurChk] : 0.f;
		float part = ghTimeES / app->fLastTime;  // fraction which track ghost has driven

		bool coldStart = tim.GetCurrentLap(carId) == 1;  // was 0
		float carMul = app->GetCarTimeMul(pSet->game.car[carId], pSet->game.sim_mode);
		//| cur
		float ghTimeC = ghTimeES + (coldStart ? 0 : 1);
		float ghTime = ghTimeC * carMul;  // scaled
		float diffT = pCarM->timeAtCurChk - ghTime;  // cur car diff at chk
		float diff = 0.f;  // on hud
		
		//!- if (pCarM->updTimes || pCarM->updLap)
		{	pCarM->updTimes = false;

			//  track time, points
			float last = tim.GetLastLap(carId), best = tim.GetBestLap(carId, pSet->game.trackreverse);
			float timeCur = last < 0.1f ? best : last;
			float timeTrk = app->data->tracks->times[pSet->game.track];
			bool b = timeTrk > 0.f && timeCur > 0.f;

			//bool coldStart = tim.GetCurrentLap(carId) == 1;  // was 0
			float time = (/*place*/1 * app->data->cars->magic * timeTrk + timeTrk) / carMul;  // trk time (for 1st place)
			//float t1pl = data->carsXml.magic * timeTrk;

			float points = 0.f, curPoints = 0.f;
			int place = app->GetRacePos(timeCur, timeTrk, carMul, coldStart, &points);
			//| cur
			float timCC = timeTrk + (coldStart ? 0 : 1);
			float timCu = timCC * carMul;
			diff = pCarM->timeAtCurChk + /*(coldStart ? 1:0)*carMul*/ - time * part;  ///new

			float chkPoints = 0.f;  // cur, at chk, assume diff time later than track ghost
			int chkPlace = app->GetRacePos(timCu + diffT, timeTrk, carMul, coldStart, &chkPoints);
			bool any = cur || b;
	
			h.sTimes =
				"\n#80E080" + StrTime(time)+
				"\n#D0D040" + (cur ? toStr( chkPlace )     : "--")+
				"\n#F0A040" + (cur ? fToStr(chkPoints,1,3) : "--");
			float dlap = last - time;
			h.sLap =
				"#D0E8FF"+TR("#{TBLapResults}") +
				"\n#80C8FF" + StrTime(last)+
				(last > 0.f ? String("  ") + (dlap > 0.f ? "#80E0FF+" : "#60FF60-") + fToStr(fabs(dlap), 1,3) : "")+
				"\n#80E0E0" + StrTime(best)+
				"\n#80E080" + StrTime(time)+
				"\n#D0D040" + (b ? toStr(place)      : "--")+
				"\n#F0A040" + (b ? fToStr(points,1,3) : "--");
			if (h.txLap)
				h.txLap->setCaption(h.sLap);
		}
		if (h.txTimes)
			h.txTimes->setCaption(
				(hasLaps ? "#A0E0D0"+toStr(tim.GetCurrentLap(carId)+1)+" / "+toStr(pSet->game.num_laps) : "") +
				"\n#A0E0E0" + StrTime(tim.GetPlayerTime(carId))+
				(cur ? String("  ") + (diff > 0.f ? "#80E0FF+" : "#60FF60-")+
					fToStr(fabs(diff), 1,3) : "")+
				h.sTimes+
				"\n#E0B090" + fToStr(pCarM->trackPercent,0,1)+"%" );

		if (h.txLap)
		{
			//if (pCarM->updLap)
			//{	pCarM->updLap = false;
				//h.txLap->setCaption(h.sLap);
			//}
			float a = std::min(1.f, pCarM->fLapAlpha * 2.f);
			bool hasRoad = app->scn->road && app->scn->road->getNumPoints() > 2;
			bool vis = pSet->show_times && hasRoad && a > 0.f;
			if (vis)
			{	if (app->iLoad1stFrames == -2)  //bLoading)  //  fade out
				{	pCarM->fLapAlpha -= !hasRoad ? 1.f : time * gPar.fadeLapResults;
					if (pCarM->fLapAlpha < 0.f)  pCarM->fLapAlpha = 0.f;
				}
				h.bckLap->setAlpha(a);
				h.txLapTxt->setAlpha(a);  h.txLap->setAlpha(a);
			}
			h.bckLap->setVisible(vis);
			h.txLapTxt->setVisible(vis);  h.txLap->setVisible(vis);
		}
	}


	//  checkpoint warning  --------
	if (app->scn->road && h.bckWarn && pCarM)
	{
		/* checks debug *
		if (ov[0].oU)  {
			//"ghost:  "  + GetTimeString(ghost.GetTimeLength()) + "  "  + toStr(ghost.GetNumFrames()) + "\n" +
			//"ghplay: " + GetTimeString(ghplay.GetTimeLength()) + "  " + toStr(ghplay.GetNumFrames()) + "\n" +
			ov[0].oU->setCaption(String("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") +
				"         st " + toStr(pCarM->bInSt ? 1:0) + " in" + toStr(pCarM->iInChk) +
				"  |  cur" + toStr(pCarM->iCurChk) + " > next " + toStr(pCarM->iNextChk) +
				"  |  Num " + toStr(pCarM->iNumChks) + " / All " + toStr(road->mChks.size()));
		}	/**/

		if (pCarM->bWrongChk)
			pCarM->fChkTime = gPar.timeShowChkWarn;
			
		bool show = pCarM->fChkTime > 0.f;
		if (show)  pCarM->fChkTime -= time;
		h.bckWarn->setVisible(show && pSet->show_times);
	}

	//  race countdown  ------
	if (h.txCountdown)
	{
		bool vis = app->pGame->timer.pretime > 0.f && !app->pGame->timer.waiting;
		if (vis)
			h.txCountdown->setCaption(fToStr(app->pGame->timer.pretime,1,3));
		h.txCountdown->setVisible(vis);
	}

	//  camera cur  ------
	if (h.txCam)
	{	FollowCamera* cam = pCarM->fCam;
		if (cam && cam->updName)
		{	cam->updName = false;
			h.txCam->setCaption(cam->sName);
	}	}


	//-------------------------------------------------------------------------------------------------------------------
	///  debug infos
	//-------------------------------------------------------------------------------------------------------------------
	
	static int oldTxtClr = -2;  // clr upd
	if (oldTxtClr != pSet->car_dbgtxtclr)
	{	oldTxtClr = pSet->car_dbgtxtclr;
		UpdDbgTxtClr();
	}

	//  car debug text  --------
	static bool oldCarTxt = false;
	if (pCar && ov[0].oU)
	{
		if (pSet->car_dbgtxt)
		{	std::stringstream s1,s2,s3,s4;
			pCar->DebugPrint(s1, true, false, false, false);  ov[0].oU->setCaption(s1.str());
			pCar->DebugPrint(s2, false, true, false, false);  ov[1].oU->setCaption(s2.str());
			pCar->DebugPrint(s3, false, false, true, false);  ov[2].oU->setCaption(s3.str());
			pCar->DebugPrint(s4, false, false, false, true);  ov[3].oU->setCaption(s4.str());
		}else
		if (pSet->car_dbgtxt != oldCarTxt)
		{	ov[0].oU->setCaption(""); /*ovU[1]->setCaption(""); ovU[2]->setCaption(""); ovU[3]->setCaption("");*/	}
	}
	oldCarTxt = pSet->car_dbgtxt;
	

	//  profiling times --------
	if (pSet->profilerTxt && ov[1].oU)
	{
		PROFILER.endCycle();
		static int frame=0;  ++frame;

		if (frame > 10)  //par
		{	frame = 0;
			std::string sProf = PROFILER.getAvgSummary(quickprof::MILLISECONDS);
			//sProf = "PROF "+fToStr(1000.f/PROFILER.getAvgDuration(" frameSt",quickprof::MILLISECONDS), 2,4);
			ov[1].oU->setCaption(sProf);
		}
		//if (newPosInfos.size() > 0)
		//ov[3].oU->setCaption("carm: " + toStr(ap->carModels.size()) + " newp: " + toStr((*newPosInfos.begin()).pos));
	}


	//  bullet profiling text  --------
	static bool oldBltTxt = false;
	if (ov[1].oU)
	{
		if (pSet->bltProfilerTxt)
		{
			static int cc = 0;  ++cc;
			if (cc > 40)
			{	cc = 0;
				std::stringstream os;
				bltDumpAll(os);
				ov[1].oU->setCaption(os.str());
			}
		}
		else
		if (pSet->bltProfilerTxt != oldBltTxt)
			ov[1].oU->setCaption("");
	}
	oldBltTxt = pSet->bltProfilerTxt;

	
	//  wheels slide, susp bars  --------
	if (pSet->car_dbgbars && pCar)
	{
		const Real xp = 80, yp = -530, ln = 20, y4 = 104;
		//const static char swh[4][6] = {"F^L<","F^R>","RvL<","RvR>"};
		for (int w=0; w < 4; ++w)
		if (ov[3-w].oL && ov[3-w].oR && ov[3-w].oS)
		{	
			float slide = /*-1.f*/0.f, sLong = 0.f, sLat = 0.f;
			float squeal = pCar->GetTireSquealAmount((WHEEL_POSITION)w, &slide, &sLong, &sLat);

			//MATHVECTOR<float,3> vwhVel = pCar->dynamics.GetWheelVelocity((WHEEL_POSITION)w);
			//float whVel = vwhVel.Magnitude() * 3.6f;

			/**  //  info
			static char ss[256];
			sprintf(ss, "%s %6.3f %6.3f  %6.3f %6.3f\n", swh[w],
				sLong/4.f, sLat/3.f, slide, squeal);
			ColourValue clr;  clr.setHSB( slide/20.f, 0.8f, 1.f );  //clr.a = min(0.f, slide/2.f);
			ovL[3-w]->setCaption(String(ss));
			ovL[3-w]->setColour(clr);
			//ovL[3-w]->setPosition(0.f, 230 + w*22);
			/**/

			//  bar meters |
			float susp = pCar->dynamics.GetSuspension(WHEEL_POSITION(w)).GetDisplacementPercent();
			sLong = fabs(sLong);
			float slng = sLong / sLong * powf(sLong, 0.3f);  // slide*20.f

			ov[3-w].oR->setPosition(slng * 14.f +xp, yp + w*ln);
			ov[3-w].oL->setPosition(sLat * 14.f +xp, yp + w*ln +y4);
			ov[3-w].oS->setPosition(susp * 70.f +xp, yp + w*ln -y4);
		}
		if (ov[4].oL)  ov[4].oL->setPosition(xp, yp + -20 +y4+3);
		if (ov[4].oS)  ov[4].oS->setPosition(xp + 70, yp + -20 -104-3);

		//ov[3-w].oR->setCaption("|");  ov[3-w].oR->setColour(ColourValue(0.6,1.0,0.7));
	}


	//  input values
	/*if (pCar && ap->pGame && ap->pGame->profilingmode)
	{	const std::vector<float>& inp = pCar->dynamics.inputsCopy;
	if (ov[2].oU && inp.size() == CARINPUT::ALL)
	{	sprintf(s, 
		" Throttle %5.2f\n Brake %5.2f\n Steer %5.2f\n"
		" Handbrake %5.2f\n Boost %5.2f\n Flip %5.2f\n"
		,inp[CARINPUT::THROTTLE], inp[CARINPUT::BRAKE], -inp[CARINPUT::STEER_LEFT]+inp[CARINPUT::STEER_RIGHT]
		,inp[CARINPUT::HANDBRAKE],inp[CARINPUT::BOOST], inp[CARINPUT::FLIP] );
		ov[2].oU->setCaption(String(s));
	}	}/**/


	//  wheels ter mtr, surface info  ---------
	if (pSet->car_dbgsurf && pCar)
	{
		String ss = "";
		ss = pCarM->txtDbgSurf;

		//  surfaces  info
		/*ss += "\n";
		for (int i=0; i < ap->pGame->track.tracksurfaces.size(); ++i)
			ss += String(ap->pGame->track.tracksurfaces[i].name.c_str()) + "\n";/**/

		//ovCarDbg->show();
		if (ov[4].oX)  {  //ov[4].oL->setTop(400);
			ov[4].oX->setCaption(ss);  }
	}
	

	///  tire vis circles  + + + +
	if (pCar && moTireVis[0] && pSet->car_tirevis)
	{
		const Real z = 6000.f / pSet->tc_r, zy = pSet->tc_xr,
			m_z = 2.f * z;  // scale, max factor
		const int na = 32;  // circle quality
		const Real ad = 2.f*PI_d/na, u = 0.02f;  // u line thickness
		const ColourValue cb(0.8,0.8,0.8),cl(0.2,1,0.2),cr(0.9,0.4,0),cc(1,1,0);
		
		for (int i=0; i < 4; ++i)
		{
			const CARDYNAMICS& cd = pCar->dynamics;
			const CARWHEEL::SlideSlip& t = cd.wheel[i].slips;
			float d = cd.wheel_contact[i].GetDepth() - 2*cd.wheel[i].GetRadius();
			bool off = !(d > -0.1f && d <= -0.01f);  // not in air
			//bool on = cd.wheel_contact[i].GetColObj();
			
			ManualObject* m = moTireVis[i];
			m->beginUpdate(0);
			//  back +
			m->position(-1,0,0);  m->colour(cb);
			m->position( 1,0,0);  m->colour(cb);
			m->position(0,-1,0);  m->colour(cb);
			m->position(0, 1,0);  m->colour(cb);
			
			//  tire, before combine
			Real lx = off ? 0.f : t.preFy/z*zy,  ly = off ? 0.f : t.preFx/z;
			for (int y=-1; y<=1; ++y)
			for (int x=-1; x<=1; ++x)  {
				m->position(0  +x*u, 0  +y*u, 0);  m->colour(cr);
				m->position(lx +x*u, ly +y*u, 0);  m->colour(cr);  }

			//  tire line /
			lx = off ? 0.f : t.Fy/z*zy;  ly = off ? 0.f : t.Fx/z;
			for (int y=-2; y<=2; ++y)
			for (int x=-2; x<=2; ++x)  {
				m->position(0  +x*u, 0  +y*u, 0);  m->colour(cl);
				m->position(lx +x*u, ly +y*u, 0);  m->colour(cl);  }

			//  max circle o
			Real rx = off || t.Fym > m_z ? 0.f : t.Fym/z,
			     ry = off || t.Fxm > m_z ? 0.f : t.Fxm/z, a = 0.f;
			Vector3 p(0,0,0),po(0,0,0);
			
			for (int n=0; n <= na; ++n)
			{
				p.x = rx*cosf(a)*zy;  p.y =-ry*sinf(a);
				if (n > 0)  {
					m->position(po);  m->colour(cc);
					m->position(p);   m->colour(cc);  }
				a += ad;  po = p;
			}
			m->end();
		}
	}

	PROFILER.endBlock("g.hud");
}
Esempio n. 5
0
void App::channelChanged(ICS::Channel *channel, float currentValue, float previousValue)
{
	if (currentValue != 1.f)
		return;

	#define action(a) (channel->getNumber() == a)

	//  change tabs
	//----------------------------------------------------------------------------------------
	//  tweak
	if (mWndTweak->getVisible())
	{
		TabPtr tab = gui->tabTweak;
		if (!shift && tab->getIndexSelected() == 0)
			tab = gui->tabEdCar;  // car edit sections

		if (action(A_PrevTab)) {  // prev gui subtab
			int num = tab->getItemCount();
			tab->setIndexSelected( (tab->getIndexSelected() - 1 + num) % num );  }
		else if (action(A_NextTab)) {  // next gui subtab
			int num = tab->getItemCount();
			tab->setIndexSelected( (tab->getIndexSelected() + 1) % num );  }

		if (tab == gui->tabEdCar)  // focus ed
		{	pSet->car_ed_tab = tab->getIndexSelected();
			MyGUI::InputManager::getInstance().resetKeyFocusWidget();
			MyGUI::InputManager::getInstance().setKeyFocusWidget(gui->edCar[tab->getIndexSelected()]);  }
	}
	//  change gui tabs
	else if (isFocGui && !pSet->isMain)
	{
		MyGUI::TabPtr tab = 0;  MyGUI::TabControl* sub = 0;
		switch (pSet->inMenu)
		{	case MNU_Replays:  tab = mWndTabsRpl;  break;
			case MNU_Help:     tab = mWndTabsHelp;  break;
			case MNU_Options:  tab = mWndTabsOpts;  sub = gui->vSubTabsOpts[tab->getIndexSelected()];  break;
			default:           tab = mWndTabsGame;  sub = gui->vSubTabsGame[tab->getIndexSelected()];  break;
		}
		if (tab)
		if (shift)
		{	if (sub)  // prev, next subtab
			{	bool chng = false;
				if (action(A_PrevTab))
				{	int num = sub->getItemCount();  chng = true;
					sub->setIndexSelected( (sub->getIndexSelected() - 1 + num) % num );
				}else if (action(A_NextTab))
				{	int num = sub->getItemCount();  chng = true;
						sub->setIndexSelected( (sub->getIndexSelected() + 1) % num );
				}
				if (chng && !sub->eventTabChangeSelect.empty())
					sub->eventTabChangeSelect(sub, sub->getIndexSelected());
		}	}
		else  // prev, next tab
		{	int num = tab->getItemCount()-1, i = tab->getIndexSelected(), n = 0;
			bool chng = false;
			if (action(A_PrevTab))
			{	do{  if (i==1)  i = num;  else  --i;  ++n;  }
				while (n < num && tab->getButtonWidthAt(i) == 1);
				chng = true;
			}else
			if (action(A_NextTab))
			{	do{  if (i==num)  i = 1;  else  ++i;  ++n;  }
				while (n < num && tab->getButtonWidthAt(i) == 1);
				chng = true;
			}
			if (chng)
			{	tab->setIndexSelected(i);  gcom->tabMainMenu(tab,i);  return;  }
		}
	}
	//  change graphs type
	else if (!isFocGui && pSet->show_graphs)
	{
		int& v = (int&)pSet->graphs_type;  int vo = v;
		if (action(A_PrevTab))  v = (v-1 + Gh_ALL) % Gh_ALL;
		if (action(A_NextTab))  v = (v+1) % Gh_ALL;
		if (vo != v)
		{
			gui->cmbGraphs->setIndexSelected(v);
			gui->comboGraphs(gui->cmbGraphs, v);
			if (v == 4)  iUpdTireGr = 1;  //upd now
		}
	}
	//----------------------------------------------------------------------------------------


	//  Gui on/off  or close wnds
	if (action(A_ShowOptions) && !alt)
	{
		Wnd wnd = mWndNetEnd;
		if (wnd && wnd->getVisible())  {  wnd->setVisible(false);  // hide netw end
			return;	}
		else
		{	wnd = mWndChampEnd;
			if (wnd && wnd->getVisible())  wnd->setVisible(false);  // hide champs end
			wnd = mWndChallEnd;
			if (wnd && wnd->getVisible())  wnd->setVisible(false);  // chall
			gui->toggleGui(true);
			return;
		}
	}

	//  new game - Reload   not in multiplayer
	if (action(A_RestartGame) && !mClient)
	{
		bPerfTest = ctrl;  // ctrl-F5 start perf test
		if (bPerfTest)
		{	pSet->gui.track = "Test10-FlatPerf";
			pSet->gui.track_user = false;  }
		iPerfTestStage = PT_StartWait;
		NewGame();  return;
	}

	//  new game - fast (same track & cars)
	if (action(A_ResetGame) && !mClient)
	{
		for (int c=0; c < carModels.size(); ++c)
		{
			CarModel* cm = carModels[c];
			if (cm->pCar)  cm->pCar->bResetPos = true;
			if (cm->iLoopLastCam != -1 && cm->fCam)
			{	cm->fCam->setCamera(cm->iLoopLastCam);  //o restore
				cm->iLoopLastCam = -1;  }
			cm->First();
			cm->ResetChecks();
			cm->iWonPlace = 0;  cm->iWonPlaceOld = 0;
			cm->iWonMsgTime = 0.f;
		}
		pGame->timer.Reset(-1);
		pGame->timer.pretime = mClient ? 2.0f : pSet->game.pre_time;  // same for all multi players
		carIdWin = 1;  //
		ghost.Clear(); //
	}
	
	//  Screen shot
	if (action(A_Screenshot))
		mWindow->writeContentsToTimestampedFile(PATHMANAGER::Screenshots() + "/", ".jpg");
	
}
Esempio n. 6
0
bool App::keyPressed( const OIS::KeyEvent &arg )
{
	// update all keystates  (needed for all action("..") from oisb)
	if (mOISBsys)
		mOISBsys->process(0.0001/*?0*/);
	
	// action key == pressed key
	#define action(s)  actionIsActive(s, mKeyboard->getAsString(arg.key))

if (!bAssignKey)
{
	//  change gui tabs
	if (isFocGui && !pSet->isMain)
	{
		MyGUI::TabPtr tab = 0;  MyGUI::TabControl* sub = 0;
		switch (pSet->inMenu)
		{	case WND_Game:
			case WND_Champ:  tab = mWndTabsGame;  sub = vSubTabsGame[tab->getIndexSelected()];  break;
			case WND_Replays:  tab = mWndTabsRpl;  break;
			case WND_Help:  tab = mWndTabsHelp;  break;
			case WND_Options:  tab = mWndTabsOpts;  sub = vSubTabsOpts[tab->getIndexSelected()];  break;
		}
		if (tab)
		if (shift)
		{	if (action("PrevTab")) {  // prev gui subtab
				if (sub)  {  int num = sub->getItemCount();
					sub->setIndexSelected( (sub->getIndexSelected() - 1 + num) % num );  }	}
			else if (action("NextTab")) {  // next gui subtab
				if (sub)  {  int num = sub->getItemCount();
					sub->setIndexSelected( (sub->getIndexSelected() + 1) % num );  }  }
		}else
		{	int num = tab->getItemCount()-1, i = 0, n = 0;
			if (action("PrevTab")) {
				i = tab->getIndexSelected();
				do{  if (i==1)  i = num;  else  --i;  ++n;  }
				while (n < num && tab->getButtonWidthAt(i) == 1);
				tab->setIndexSelected(i);  MenuTabChg(tab,i);  return true;  }
			else if (action("NextTab")) {
				i = tab->getIndexSelected();
				do{  if (i==num)  i = 1;  else  ++i;  ++n;  }
				while (n < num && tab->getButtonWidthAt(i) == 1);
				tab->setIndexSelected(i);  MenuTabChg(tab,i);  return true;  }
		}
	}
	else if (!isFocGui && pSet->show_graphs)  // change graphs type
	{
		int& v = pSet->graphs_type;  int vo = v;
		if (action("PrevTab"))  v = (v-1 + graph_types) % graph_types;
		if (action("NextTab"))	v = (v+1) % graph_types;
		if (vo != v)
		{	float fv = float(v) / graph_types;
			slGraphsType(slGraphT, fv);  slGraphT->setValue(fv);
			if (v == 4)  iUpdTireGr = 1;  //upd now
		}
	}

	//  gui on/off  or close wnds
	if (action("ShowOptions") && !alt)
	{
		if (mWndNetEnd && mWndNetEnd->getVisible())  {  mWndNetEnd->setVisible(false);  // hide netw end
			return false;	}
		else
		{
			if (mWndChampEnd && mWndChampEnd->getVisible())  mWndChampEnd->setVisible(false);  // hide champs end
			toggleGui(true);  return false;
		}
	}

	//  new game - reload   not in multiplayer
	if (action("RestartGame") && !mClient)
	{	NewGame();  return false;	}

	//  new game - fast (same track & cars)
	if (action("ResetGame") && !mClient)
	{
		for (int c=0; c < carModels.size(); ++c)
		{
			CarModel* cm = carModels[c];
			if (cm->pCar)  cm->pCar->bResetPos = true;
			if (cm->fCam)  cm->fCam->first = true;
			cm->ResetChecks();
			cm->iWonPlace = 0;  cm->iWonPlaceOld = 0;
			cm->iWonMsgTime = 0.f;
		}
		pGame->timer.Reset(-1);
		pGame->timer.pretime = mClient ? 2.0f : pSet->game.pre_time;  // same for all multi players
		carIdWin = 1;  //
		ghost.Clear(); //
	}


	//  Screen shot
	//if (action("Screenshot"))  //isnt working
	const OISB::Action* act = OISB::System::getSingleton().lookupAction("General/Screenshot",false);
	if (act && act->isActive())
	{	mWindow->writeContentsToTimestampedFile(PATHMANAGER::GetScreenShotDir() + "/", ".jpg");
		return false;	}
	
	using namespace OIS;


	//  main menu keys
	if (pSet->isMain && isFocGui)
	{
		switch (arg.key)
		{
		case KC_UP:  case KC_NUMPAD8:
			pSet->inMenu = (pSet->inMenu-1+WND_ALL)%WND_ALL;
			toggleGui(false);  return true;

		case KC_DOWN:  case KC_NUMPAD2:
			pSet->inMenu = (pSet->inMenu+1)%WND_ALL;
			toggleGui(false);  return true;

		case KC_RETURN:
			pSet->isMain = false;
			toggleGui(false);  return true;
		}
	}

	//  esc
	if (arg.key == KC_ESCAPE)
	{
		if (pSet->escquit)
			mShutDown = true;	// quit
		else
			if (mWndChampStage->getVisible())  ///  close champ wnds
				btnChampStageStart(0);
			else
				toggleGui(true);	// gui on/off
		return true;
	}

	//  shortcut keys for gui access (alt-T,H,S,G,V,.. )
	if (alt)
		switch (arg.key)
		{	case KC_Q:
			case KC_T:	GuiShortcut(WND_Game, 1);	return true;  // Q,T Track
			case KC_C:	GuiShortcut(WND_Game, 2);	return true;  // C Car
			case KC_H:	GuiShortcut(WND_Champ, 6);	return true;  // H Champs
			case KC_U:	GuiShortcut(WND_Game, 5);	return true;  // U Multiplayer
			case KC_W:	GuiShortcut(WND_Game, 4);	return true;  // W Setup

			case KC_R:	GuiShortcut(WND_Replays, 1);	return true;  // R Replays

			case KC_S:	GuiShortcut(WND_Options, 1);	return true;  // S Screen
			 case KC_E:	GuiShortcut(WND_Options, 1,1);	return true;  // E -Effects
			case KC_G:	GuiShortcut(WND_Options, 2);	return true;  // G Graphics
			 case KC_N:	GuiShortcut(WND_Options, 2,2);	return true;  // N -Vegetation

			case KC_V:	GuiShortcut(WND_Options, 3);	return true;  // V View
			 case KC_M:	GuiShortcut(WND_Options, 3,1);	return true;  // M -Minimap
			 case KC_O:	GuiShortcut(WND_Options, 3,3);	return true;  // O -Other
			case KC_I:	GuiShortcut(WND_Options, 4);	return true;  // I Input
		}

	///* tire edit */
	if (pSet->graphs_type == 4)
	{
		int& iCL = iEdTire==1 ? iCurLong : (iEdTire==0 ? iCurLat : iCurAlign);
		int iCnt = iEdTire==1 ? 11 : (iEdTire==0 ? 15 : 18);
		switch (arg.key)
		{
			case KC_HOME: case KC_NUMPAD7:  // mode long/lat
				iEdTire = iEdTire==1 ? 0 : 1;  iUpdTireGr=1;  return true;

			case KC_END: case KC_NUMPAD1:	// mode align
				iEdTire = iEdTire==2 ? 0 : 2;  iUpdTireGr=1;  return true;

			/*case KC_1:*/ case KC_PGUP: case KC_NUMPAD9:    // prev val
				iCL = (iCL-1 +iCnt)%iCnt;  iUpdTireGr=1;  return true;

			/*case KC_2:*/ case KC_PGDOWN: case KC_NUMPAD3:  // next val
				iCL = (iCL+1)%iCnt;  iUpdTireGr=1;  return true;
		}
	}
	
	
	//  not main menus
	//if (/*&& !pSet->isMain*/)
	{
		Widget* wf = MyGUI::InputManager::getInstance().getKeyFocusWidget();
		bool edFoc = wf && wf->getTypeName() == "EditBox";
		//if (wf)  LogO(wf->getTypeName()+" " +toStr(edFoc));
		switch (arg.key)
		{
			case KC_BACK:
				if (mClient && !isFocGui)  // show/hide players net wnd
				{	mWndNetEnd->setVisible(!mWndNetEnd->getVisible());  return true;  }
					
				if (mWndChampStage->getVisible())	// back from champs stage wnd
				{	btnChampStageBack(0);  return true;  }

				if (pSet->isMain)  break;
				if (isFocGui)
				{	if (edFoc)  break;
					pSet->isMain = true;  toggleGui(false);  }
				else
					if (mWndRpl && !isFocGui)	bRplWnd = !bRplWnd;  // replay controls
				return true;

			case KC_P:		// replay play/pause
				if (bRplPlay && !isFocGui)
				{	bRplPause = !bRplPause;  UpdRplPlayBtn();
					return true;  }
				break;
				
			case KC_K:		// replay car ofs
				if (bRplPlay && !isFocGui)	{	--iRplCarOfs;  return true;  }
				break;
			case KC_L:		// replay car ofs
				if (bRplPlay && !isFocGui)	{	++iRplCarOfs;  return true;  }
				break;
				
			case KC_F:		// focus on find edit
				if (ctrl && edFind && isFocGui &&
					!pSet->isMain && pSet->inMenu == WND_Game && mWndTabsGame->getIndexSelected() == 1)
				{
					MyGUI::InputManager::getInstance().resetKeyFocusWidget();
					MyGUI::InputManager::getInstance().setKeyFocusWidget(edFind);
					return true;
				}	break;
				

			case KC_F7:		// Times
				if (shift)
				{	WP wp = chOpponents;  ChkEv(show_opponents);  ShowHUD();  }
				else if (!ctrl)
				{	WP wp = chTimes;  ChkEv(show_times);  ShowHUD();  }
				return false;
				
			case KC_F8:		// graphs
				if (ctrl)
				{	WP wp = chGraphs;  ChkEv(show_graphs);
					for (int i=0; i < graphs.size(); ++i)
						graphs[i]->SetVisible(pSet->show_graphs);
				}
				else		// Minimap
				if (!shift)
				{	WP wp = chMinimp;  ChkEv(trackmap);
					for (int c=0; c < 4; ++c)
						if (ndMap[c])  ndMap[c]->setVisible(pSet->trackmap);
				}	return false;

			case KC_F9:
				if (ctrl)	// car debug surfaces
				{	WP wp = chDbgS;  ChkEv(car_dbgsurf);  ShowHUD();  }
				else
				if (shift)	// car debug text
				{	WP wp = chDbgT;  ChkEv(car_dbgtxt);  ShowHUD();  }
				else		// car debug bars
				{	WP wp = chDbgB;  ChkEv(car_dbgbars);   ShowHUD();  }
				return true;

			case KC_F11:
				if (shift)	// profiler times
				{	WP wp = chProfTxt;  ChkEv(profilerTxt);  ShowHUD();  }
				else
				if (!ctrl)  // Fps
				{	WP wp = chFps;  ChkEv(show_fps); 
					if (pSet->show_fps)  mFpsOverlay->show();  else  mFpsOverlay->hide();
					return false;
				}	break;

			case KC_F10:	//  blt debug, txt
				if (shift)
				{	WP wp = chBltTxt;  ChkEv(bltProfilerTxt);  return false;  }
				else if (ctrl)
				{	WP wp = chBlt;  ChkEv(bltDebug);  return false;  }
				else		// wireframe
					toggleWireframe();
				return false;

			
			case KC_F5:		//  new game
				NewGame();  return false;


			case KC_RETURN:		///  close champ wnds
				if (mWndChampStage->getVisible())
					btnChampStageStart(0);
				else			//  chng trk/car + new game  after up/dn
				if (isFocGui && !pSet->isMain)
					switch (pSet->inMenu)
					{
					case WND_Replays:	btnRplLoad(0);  break;
					case WND_Game:  case WND_Champ:
					{	switch (mWndTabsGame->getIndexSelected())
						{
						case 1:	changeTrack();	btnNewGame(0);  break;
						case 2:	changeCar();	btnNewGame(0);  break;
						case 4:	chatSendMsg();  break;
						case 5:	btnChampStart(0);  break;
					}	break;
				}	}
				return false;
		}
	}
}
	InputBind(arg.key);
	

	if (isFocGui && mGUI)	// gui
	{
		MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(arg.key), arg.text);
		return false;
	}

	return true;
}
Esempio n. 7
0
//  updatePoses - Set car pos for Ogre nodes, update particles, trails
//---------------------------------------------------------------------------------------------------------------
void App::updatePoses(float time)
{	
	//  Update all carmodels with their newPosInfo
	int i=0;
	std::vector<CarModel*>::iterator carIt = carModels.begin();
	std::vector<PosInfo>::iterator newPosIt = newPosInfos.begin();
	while (carIt != carModels.end())
	{
		CarModel* carM = *carIt;
		if (!carM)  return;
		PosInfo newPosInfo = *newPosIt;
		
		//  hide ghost when empty
		bool bGhost = carM->eType == CarModel::CT_GHOST,
			bGhostVis = (ghplay.GetNumFrames() > 0) && pSet->rpl_ghost;
		if (bGhost)
		{
			carM->setVisible(bGhostVis);
			
			//  hide ghost car when close to player car (only when not transparent)
			if (!pSet->rpl_alpha)
			{
				CarModel* playerCar = carModels.front();
				float distance = carM->pMainNode->getPosition().squaredDistance(playerCar->pMainNode->getPosition());
				if (distance < 16.f)
					carM->setVisible(false);
			}
		}
		
		carM->Update(newPosInfo, time);
		
		//  nick text 3d pos
		//projectPoint()
		
		//  pos on minimap  x,y = -1..1
		//if (!bGhost)
		{	float xp =(-newPosInfo.pos[2] - minX)*scX*2-1,
				  yp =-(newPosInfo.pos[0] - minY)*scY*2+1;
			newPosInfos[i].miniPos = Vector2(xp,yp);
			
			if (vNdPos[i])
				if (bGhost && !bGhostVis)  vNdPos[i]->setPosition(-100,0,0);  //hide
				else if (pSet->mini_zoomed)  vNdPos[i]->setPosition(0,0,0);
				else					vNdPos[i]->setPosition(xp,yp,0);
		}
		carIt++;  newPosIt++;  i++;
	}
	
	///  Replay info
	if (bRplPlay && pGame->cars.size() > 0)
	{
		double pos = pGame->timer.GetPlayerTime();
		float len = replay.GetTimeLength();
		if (valRplPerc){  sprintf(s, "%4.1f %%", pos/len * 100.f);  valRplPerc->setCaption(s);  }
		if (valRplCur)  valRplCur->setCaption(GetTimeString(pos));
		if (valRplLen)  valRplLen->setCaption(GetTimeString(len));

		if (slRplPos)
		{	int v = pos/len * res;  slRplPos->setScrollPosition(v);  }
	}	
}
Esempio n. 8
0
//  newPoses - Get new car pos from game
//---------------------------------------------------------------------------------------------------------------
void App::newPoses()
{
	if (!pGame)  return;
	if (pGame->cars.size() == 0)  return;
	double rplTime = pGame->timer.GetReplayTime();
	double lapTime = pGame->timer.GetPlayerTime();

	// Iterate through all car models and get new pos info
	int iCarNum = 0;  CarModel* carM0 = 0;
	std::vector<CarModel*>::iterator carMIt = carModels.begin();
	std::vector<PosInfo>::iterator newPosIt = newPosInfos.begin();
	while (carMIt != carModels.end())
	{
		CarModel* carM = *carMIt;  if (iCarNum==0)  carM0 = carM;
		CAR* pCar = carM->pCar;
		PosInfo posInfo;
		bool bGhost = carM->eType == CarModel::CT_GHOST;
		
		//  local data  car,wheels
		MATHVECTOR <float,3> pos, whPos[4];
		QUATERNION <float> rot, whRot[4];  //?double


		///-----------------------------------------------------------------------
		//  play  get data from replay
		///-----------------------------------------------------------------------
		if (bGhost)
		{
			ReplayFrame frame;
			bool ok = ghplay.GetFrame(lapTime, &frame, 0);
			//  car
			pos = frame.pos;  rot = frame.rot;
			//  wheels
			for (int w=0; w < 4; ++w)
			{
				whPos[w] = frame.whPos[w];  whRot[w] = frame.whRot[w];
				posInfo.whVel[w] = frame.whVel[w];
				posInfo.whSlide[w] = frame.slide[w];  posInfo.whSqueal[w] = frame.squeal[w];
				posInfo.whR[w] = replay.header.whR[iCarNum][w];//
				posInfo.whTerMtr[w] = frame.whTerMtr[w];  posInfo.whRoadMtr[w] = frame.whRoadMtr[w];
				posInfo.fboost = frame.fboost;
			}
			ghostFrame = frame;
		}
		else if (bRplPlay)
		{
			//  time  from start
			bool ok = replay.GetFrame(rplTime, &fr, iCarNum);
				if (!ok)	pGame->timer.RestartReplay();
			
			//  car
			pos = fr.pos;  rot = fr.rot;
			//  wheels
			for (int w=0; w < 4; ++w)
			{
				whPos[w] = fr.whPos[w];  whRot[w] = fr.whRot[w];
				posInfo.whVel[w] = fr.whVel[w];
				posInfo.whSlide[w] = fr.slide[w];  posInfo.whSqueal[w] = fr.squeal[w];
				posInfo.whR[w] = replay.header.whR[iCarNum][w];//
				posInfo.whTerMtr[w] = fr.whTerMtr[w];  posInfo.whRoadMtr[w] = fr.whRoadMtr[w];
				posInfo.fboost = fr.fboost;
			}
		}
		else
		//  get data from vdrift
		//-----------------------------------------------------------------------
		if (pCar)
		{
			pos = pCar->dynamics.GetPosition();
			rot = pCar->dynamics.GetOrientation();
			
			for (int w=0; w < 4; ++w)
			{	WHEEL_POSITION wp = WHEEL_POSITION(w);
				whPos[w] = pCar->dynamics.GetWheelPosition(wp);
				whRot[w] = pCar->dynamics.GetWheelOrientation(wp);
				//float wR = pCar->GetTireRadius(wp);
				posInfo.whVel[w] = pCar->dynamics.GetWheelVelocity(wp).Magnitude();
				posInfo.whSlide[w] = -1.f;  posInfo.whSqueal[w] = pCar->GetTireSquealAmount(wp, &posInfo.whSlide[w]);
				posInfo.whR[w] = pCar->GetTireRadius(wp);//
				posInfo.whTerMtr[w] = carM->whTerMtr[w];  posInfo.whRoadMtr[w] = carM->whRoadMtr[w];
			}
			posInfo.fboost = pCar->dynamics.boostVal;
		}
		

		//  transform axes, vdrift to ogre  car & wheels
		//-----------------------------------------------------------------------

		posInfo.pos = Vector3(pos[0],pos[2],-pos[1]);
		Quaternion q(rot[0],rot[1],rot[2],rot[3]), q1;
		Radian rad;  Vector3 axi;  q.ToAngleAxis(rad, axi);
		q1.FromAngleAxis(-rad,Vector3(axi.z,-axi.x,-axi.y));  posInfo.rot = q1 * qFixCar;
		Vector3 vcx,vcz;  q1.ToAxes(vcx,posInfo.carY,vcz);

		if (!isnan(whPos[0][0]))
		for (int w=0; w < 4; w++)
		{
			posInfo.whPos[w] = Vector3(whPos[w][0],whPos[w][2],-whPos[w][1]);
			Quaternion q(whRot[w][0],whRot[w][1],whRot[w][2],whRot[w][3]), q1;
			Radian rad;  Vector3 axi;  q.ToAngleAxis(rad, axi);
			q1.FromAngleAxis(-rad,Vector3(axi.z,-axi.x,-axi.y));  posInfo.whRot[w] = q1 * qFixWh;
		}
		posInfo.bNew = true;
		

		///  sound listener  - - - - -
		if (!bGhost)
		{
			if (pGame->sound.Enabled())  // TODO: set from camera, for each player? ..
			{
				pGame->sound.SetListener(
					MATHVECTOR <float,3> (pos[0], pos[1], pos[2]),
					QUATERNION <float>(),
					//QUATERNION <float> (rot.x(), rot.y(), rot.z(), rot.w()),
					MATHVECTOR <float,3>());
			}
			bool incar = true;
			if (pCar)
			{
				std::list <SOUNDSOURCE *> soundlist;
				pCar->GetEngineSoundList(soundlist);
				for (std::list <SOUNDSOURCE *>::iterator s = soundlist.begin(); s != soundlist.end(); s++)
					(*s)->Set3DEffects(!incar);
			}
		}

		///-----------------------------------------------------------------------
		//  record  save data for replay
		///-----------------------------------------------------------------------
		if (pSet->rpl_rec && !pGame->pause && !bGhost && pCar)
		{
			//static int ii = 0;
			//if (ii++ >= 0)	// 1 half game framerate
			{	//ii = 0;
				ReplayFrame fr;
				fr.time = rplTime;  //  time  from start
				fr.pos = pos;  fr.rot = rot;  //  car
				//  wheels
				for (int w=0; w < 4; w++)
				{	fr.whPos[w] = whPos[w];  fr.whRot[w] = whRot[w];

					WHEEL_POSITION wp = WHEEL_POSITION(w);
					const TRACKSURFACE* surface = pCar->dynamics.GetWheelContact(wp).GetSurfacePtr();
					fr.surfType[w] = !surface ? TRACKSURFACE::NONE : surface->type;
					//  squeal
					fr.slide[w] = -1.f;  fr.squeal[w] = pCar->GetTireSquealAmount(wp, &fr.slide[w]);
					fr.whVel[w] = pCar->dynamics.GetWheelVelocity(wp).Magnitude();
					//  susp
					fr.suspVel[w] = pCar->dynamics.GetSuspension(wp).GetVelocity();
					fr.suspDisp[w] = pCar->dynamics.GetSuspension(wp).GetDisplacementPercent();
					//replay.header.whR[w] = pCar->GetTireRadius(wp);//
					fr.whTerMtr[w] = carM->whTerMtr[w];  fr.whRoadMtr[w] = carM->whRoadMtr[w];
				}
				//  hud
				fr.vel = pCar->GetSpeedometer();  fr.rpm = pCar->GetEngineRPM();
				fr.gear = pCar->GetGear();  fr.clutch = pCar->GetClutch();
				fr.throttle = pCar->dynamics.GetEngine().GetThrottle();
				fr.steer = pCar->GetLastSteer();
				fr.fboost = pCar->dynamics.doBoost;
				//  eng snd
				fr.posEngn = pCar->dynamics.GetEnginePosition();
				fr.speed = pCar->GetSpeed();
				fr.dynVel = pCar->dynamics.GetVelocity().Magnitude();
				
				replay.AddFrame(fr, iCarNum);  // rec replay
				if (iCarNum==0)  /// rec ghost lap
				{
					fr.time = lapTime;
					ghost.AddFrame(fr, 0);
				}
				
				if (valRplName2)  // recorded info
				{
					int size = replay.GetNumFrames() * sizeof(ReplayFrame);
					sprintf(s, "%5.2f", float(size)/1000000.f);
					String ss = String( TR("#{RplRecTime}: ")) + GetTimeString(replay.GetTimeLength()) + TR("   #{RplSize}: ") + s + TR(" #{UnitMB}");
					valRplName2->setCaption(ss);
				}
			}
		}
		if (bRplPlay && valRplName2)  valRplName2->setCaption("");
		///-----------------------------------------------------------------------
		

		//  chekpoints, lap start
		//-----------------------------------------------------------------------
		if (bRplPlay || bGhost || !sc.ter)   // dont check when replay play
			carM->bWrongChk = false;
		else
		{
			// checkpoint arrow
			if (pSet->check_arrow && !bRplPlay && arrowNode && road && road->mChks.size()>0)
			{
				// set animation start to old orientation
				arrowAnimStart = arrowAnimCur;
				
				// game start: no animation
				bool noAnim = carM->iNumChks == 0;
				
				// get vector from camera to checkpoint
				Vector3 chkPos = road->mChks[std::max(0, std::min((int)road->mChks.size()-1, carM->iNextChk))].pos;
					
				// workaround for last checkpoint
				if (carM->iNumChks == road->mChks.size())
				{
					// point arrow to start position
					chkPos = carM->vStartPos;
				}
				
				//const Vector3& playerPos = carM->fCam->mCamera->getPosition();
				const Vector3& playerPos = carM->pMainNode->getPosition();
				Vector3 dir = chkPos - playerPos;
				dir[1] = 0; // only x and z rotation
				Quaternion quat = Vector3::UNIT_Z.getRotationTo(-dir); // convert to quaternion

				const bool valid = !quat.isNaN();
				if (valid) {
					if (noAnim) arrowAnimStart = quat;
					arrowAnimEnd = quat;
				
					// set arrow color (wrong direction: red arrow)
					// calc angle towards cam
					Real angle = (arrowAnimCur.zAxis().dotProduct(carM->fCam->mCamera->getOrientation().zAxis())+1)/2.0f;
					// set color in material
					MaterialPtr arrowMat = MaterialManager::getSingleton().getByName("Arrow");
					if (arrowMat->getTechnique(0)->getPass(1)->hasFragmentProgram())
					{
						GpuProgramParametersSharedPtr fparams = arrowMat->getTechnique(0)->getPass(1)->getFragmentProgramParameters();
						// green: 0.0 1.0 0.0     0.0 0.4 0.0
						// red:   1.0 0.0 0.0     0.4 0.0 0.0
						Vector3 col1 = angle * Vector3(0.0, 1.0, 0.0) + (1-angle) * Vector3(1.0, 0.0, 0.0);
						Vector3 col2 = angle * Vector3(0.0, 0.4, 0.0) + (1-angle) * Vector3(0.4, 0.0, 0.0);
						fparams->setNamedConstant("color1", col1);
						fparams->setNamedConstant("color2", col2);
					}
				}
			}
			
			if (carM->bGetStPos)  // first pos is at start
			{	carM->bGetStPos = false;
				carM->matStPos.makeInverseTransform(posInfo.pos, Vector3::UNIT_SCALE, posInfo.rot);
				carM->ResetChecks();
			}
			if (road && !carM->bGetStPos)
			{
				//  start/finish box dist
				Vector4 carP(posInfo.pos.x,posInfo.pos.y,posInfo.pos.z,1);
				carM->vStDist = carM0->matStPos * carP;  // start pos from 1st car always
				carM->bInSt = abs(carM->vStDist.x) < road->vStBoxDim.x && 
					abs(carM->vStDist.y) < road->vStBoxDim.y && 
					abs(carM->vStDist.z) < road->vStBoxDim.z;
							
				carM->iInChk = -1;  carM->bWrongChk = false;
				int ncs = road->mChks.size();
				if (ncs > 0)
				{	if (carM->bInSt && carM->iNumChks == ncs && carM->iCurChk != -1)  // finish
					{
						bool best = pGame->timer.Lap(iCarNum, 0,0, true, pSet->trackreverse);  //pGame->cartimerids[pCar] ?

						if (!pSet->rpl_bestonly || best)  ///  new best lap, save ghost
						if (iCarNum==0 && pSet->rpl_rec)  // for many, only 1st-
						{
							ghost.SaveFile(GetGhostFile());
							ghplay.CopyFrom(ghost);
						}
						ghost.Clear();
						
						carM->ResetChecks();
						//  restore boost fuel, each lap
						if (pSet->boost_type == 1 && carM->pCar)
							carM->pCar->dynamics.boostFuel = gfBoostFuelStart;

						///  winner places  for local players > 1
						if (carM->iWonPlace == 0 && pGame->timer.GetCurrentLap(iCarNum) >= pSet->num_laps)
							carM->iWonPlace = carIdWin++;
					}
					for (int i=0; i < ncs; ++i)
					{
						const CheckSphere& cs = road->mChks[i];
						Real d2 = posInfo.pos.squaredDistance(cs.pos);
						if (d2 < cs.r2)  // car in checkpoint
						{
							carM->iInChk = i;
							//  next check
							if (i == carM->iNextChk && carM->iNumChks < ncs)
							{
								carM->iCurChk = i;  carM->iNumChks++;
								int ii = (pSet->trackreverse ? -1 : 1) * road->iDir;
								carM->iNextChk = (carM->iCurChk + ii + ncs) % ncs;
								//  save car pos and rot
								carM->pCar->SavePosAtCheck();
							}
							else
							if (carM->iInChk != carM->iCurChk)
								carM->bWrongChk = true;
							break;
						}
				}	}
		}	}

		(*newPosIt) = posInfo;
		carMIt++;  newPosIt++;  iCarNum++;  // next
	}
}