[ glorius @ 14.05.2014. 12:11 ] @
Kada koristim default kod za step() funkciju box2d klase b2world imam razlicit framerate u Debug i Release (u Release je dosta brze).

Code:


void DPhysicsEngine::update(float dt)
{
    static float timeStep = 1.0f/60.0f;
    static int32 velocityIters = 6;
    static int32 positionIters = 2;

    m_pWorld->Step(timeStep, velocityIters, positionIters);
}



Verujem da se framerate sa ovim kodom menja i u zavisnosti od brzine masine tako da mora da se popravi...

Probao sam za timeStep da stavim dt (da timestep bude varijabilan) ali to opet nije dovelo do konstantnog framerate. Cudi me da nije konstantan u ovom slucaju jer je u Release dt manje i onda se, verovatno, brzine i ostali parametri mnoze sa dt u Step funkciji tako da bi pozivanje update cesce sa manjim dt (Release) bilo ekvivalentno redjim pozivanjem update sa vecim dt (Debug). Ali, izgleda da to ne radi tako...

Onda sam nasao resenje na netu:

Code:


DPhysicsEngine::DPhysicsEngine(void)
{
    m_pWorld = new b2World(toPhysics(sf::Vector2f(0, 9.81f)));
    m_pWorld->SetAutoClearForces(false);
}

void DPhysicsEngine::update(float dt)
{
    float timeStep = 1.0f/60.0f;
    static int32 velocityIters = 6;
    static int32 positionIters = 2;

    static float timePassed = 0;
    timePassed += dt;
    while(timePassed > timeStep)
    {
        m_pWorld->Step(timeStep, velocityIters, positionIters);
        timePassed -= timeStep;
    }
    m_pWorld->ClearForces();
}



Ovo resenje je ok sto se tice odrzavanja konstantnog framerate ali je kretanje objekata vrlo sporo i onda moram da povecam drasticno gravitaciju, brzine i impulse. Cini mi se da je lose ako se zadaju prevelike vrednosti posto sam primetio da objekti pocnu da vibriraju i na kraju odu iza event horizon-a :)

Da li neko mozda ima iskustva sa ovim i mozda solution?

[ Rapaic Rajko @ 15.05.2014. 08:42 ] @
Vrlo, vrlo zanimljiv problem :)

Gledao sam gornji kod i ima tu dosta nedoumica; prvi i drugi primer su nebo i zemlja.
U prvom primeru, da li "radnja tece" istom brzinom i u release i u debug varijanti, ne mislim na framerate?

Pozz

P.S. Recimo da sam radio nesto slicno, doduse u Delphi-ju, ali problem je isti - tecna animacija sa constant framerates. A moze i tecna "radnja" sa variable framerates - mislim da ti ovo drugo treba.

[Ovu poruku je menjao Rapaic Rajko dana 15.05.2014. u 10:27 GMT+1]

[Ovu poruku je menjao Rapaic Rajko dana 15.05.2014. u 10:28 GMT+1]
[ glorius @ 15.05.2014. 12:24 ] @
Citat:
Rapaic Rajko

U prvom primeru, da li "radnja tece" istom brzinom i u release i u debug varijanti, ne mislim na framerate?



U pravu si, izvini, nisam bio precizan. Ono sto zelim da postignem je da se sve odvija istom brzinom a ne da bude constant frame rate.
Koliko razumem framerate i dt su uzajamno reciprocni ( framerate = 1 / dt ). Znaci, framerate je broj pozivanja update funkcije u sekundi i sto je framerate veci dt je manje i vice versa tako da se dobija konstantna brzina odvijanja kretanja u aplikaciji kada se parametri kretanja mnoze sa dt.

U medjuvremenu sam resio problem postavljanjem varijabilnog timeStep-a za step() funkciju. U prethodnom postu sam napisao:

Citat:
Probao sam za timeStep da stavim dt (da timestep bude varijabilan) ali to opet nije dovelo do konstantnog framerate. Cudi me da nije konstantan u ovom slucaju jer je u Release dt manje i onda se, verovatno, brzine i ostali parametri mnoze sa dt u Step funkciji tako da bi pozivanje update cesce sa manjim dt (Release) bilo ekvivalentno redjim pozivanjem update sa vecim dt (Debug). Ali, izgleda da to ne radi tako...


Code:


void DPhysicsEngine::update(float dt)
{
    float timeStep = dt;
    static int32 velocityIters = 6;
    static int32 positionIters = 2;

    m_pWorld->Step(timeStep*10, velocityIters, positionIters);
}



Ovo, u stvari, radi lepo nego sam u delu za input imao los kod pa nije radilo kako treba.

Jedino sto na forumima napominju da je bolje da timeStep bude konstantan ali se u tom slucaju step() funkcija u release mode vise puta poziva nego u debug u istom vremenskom intervalu i onda se kretanje odvija brze...
[ Rapaic Rajko @ 16.05.2014. 07:27 ] @
Aha, resio si tako sto si usporio framerate tako da "radnja" IPAK tece istom brzinom (sporo?) i u debug i u release varijanti... hm.

Ako ti ovo radi, super. Medjutim, sta ako naidjes na bas sporu masinu, gde ni taj (timestep * 10) ne pomaze, odnosno ipak bude sporija radnja nego na nekoj brzoj masini?
Postoji resenje i za to :). Evo kako bih ja uradio:

1) Masina mora da radi racunanje koliko god brzo moze. To znaci, postavljanje velikog timestep parametra za poziv Step() je kontraproduktivno za brzinu "radnje".
2) Od pocetka me bunilo ZASTO uopste parametar timestep za metodu Step(). Mislim da znam: Step() radi tako sto interno pamti/zapisuje (nazovimo ga tako) static float lastStepTime. To ce reci, Step() CEKA dok sistemski time ne dosegne vrednost lastStepTime + timestep, tada odradjuje posao, zapisuje novu vrednost lastStepTime i izlazi napolje.
3) Pa kako onda ujednaciti prividnu brzinu "radnje"? Prosto, preskaces poneki poziv Step() na sporoj masini, odnosno ne preskaces na brzoj.
4) Na taj nacin, na sporoj masini ce se prikazivati (recimo) svega 20-30 frames/sec (preskace se poneki frame), a na brzoj i svih 60; bitno je da "radnja" ide istom brzinom.
5) Gde uraditi/iskodirati IZBOR da li ce se (ili ne) prikazeti frame, odnosno pozvati Step()? Meni je logicno u metodi update().

Moguce da gresim u gornjoj analizi, ali uvidom u kod metode Step() bi se sve nedoumice razresile.

Pozz :)
[ glorius @ 16.05.2014. 14:43 ] @
Kod iz dubine Box2D step() funkcije koji vrsi fizicke kalkulacije:

Code:


        float32 h = step.dt;

    // Integrate velocities and apply damping. Initialize the body state.
    for (int32 i = 0; i < m_bodyCount; ++i)
    {
        b2Body* b = m_bodies[i];

        b2Vec2 c = b->m_sweep.c;
        float32 a = b->m_sweep.a;
        b2Vec2 v = b->m_linearVelocity;
        float32 w = b->m_angularVelocity;

        // Store positions for continuous collision.
        b->m_sweep.c0 = b->m_sweep.c;
        b->m_sweep.a0 = b->m_sweep.a;

        if (b->m_type == b2_dynamicBody)
        {
            // Integrate velocities.
            v += h * (b->m_gravityScale * gravity + b->m_invMass * b->m_force);
            w += h * b->m_invI * b->m_torque;

            // Apply damping.
            // ODE: dv/dt + c * v = 0
            // Solution: v(t) = v0 * exp(-c * t)
            // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
            // v2 = exp(-c * dt) * v1
            // Taylor expansion:
            // v2 = (1.0f - c * dt) * v1
            v *= b2Clamp(1.0f - h * b->m_linearDamping, 0.0f, 1.0f);
            w *= b2Clamp(1.0f - h * b->m_angularDamping, 0.0f, 1.0f);
        }

        m_positions[i].c = c;
        m_positions[i].a = a;
        m_velocities[i].v = v;
        m_velocities[i].w = w;
    }



Posmatrajuci ovaj deo koda:
v += h * (b->m_gravityScale * gravity + b->m_invMass * b->m_force);

Ovo je aproksimacija integracije jednacine za dobijanje trenutne brzine (Aproksimacija jer neke sile zavise od brzine i pozicije tako da bi realna jednacina bila dosta slozenija):
V = (g + F/m)*dt;

I iz ovoga se vidi da intenzitet vektora brzine zavisi od dt. Ako je brza masina i dt konstantno, step() ce se pozivati vise puta u sekundi u odnosu na sporiju masinu i onda ce se brze updateovati pozicija koja je:

S = S + V*dt;

To se desava u daljem delu box2D koda:

Code:


// Integrate positions
    for (int32 i = 0; i < m_bodyCount; ++i)
    {
        b2Vec2 c = m_positions[i].c;
        float32 a = m_positions[i].a;
        b2Vec2 v = m_velocities[i].v;
        float32 w = m_velocities[i].w;

        // Check for large velocities
        b2Vec2 translation = h * v;
        if (b2Dot(translation, translation) > b2_maxTranslationSquared)
        {
            float32 ratio = b2_maxTranslation / translation.Length();
            v *= ratio;
        }

        float32 rotation = h * w;
        if (rotation * rotation > b2_maxRotationSquared)
        {
            float32 ratio = b2_maxRotation / b2Abs(rotation);
            w *= ratio;
        }

        // Integrate
        c += h * v;                   // *********************** S += V*dt *******************
        a += h * w;

        m_positions[i].c = c;
        m_positions[i].a = a;
        m_velocities[i].v = v;
        m_velocities[i].w = w;
    }


Postoji jos jedan nacin da se ovo resi ali meni nije radio kako treba. Poenta je da se postavi timeStep da bude konstantan (1/60 npr) i da se napravi petlja koja zove step() sa timeStep argumentom, a u medjuvremenu da se timeStep akumulira u neku passTime promenljivu i ako je passTime>= dt da se izadje iz te petlje. Mozda nije radilo zbog spomenutih problema koje sam imao sa inputom...
Za sada mi ovaj kod sa varijabilnim timeStep radi pa ako dodje do problema daljim razvitkom aplikacije moracu da menjam algoritam...



[ Rapaic Rajko @ 17.05.2014. 18:18 ] @
Totalno sam naopako shvatio tvoj kod iz prvog posta, ocigledno.
Mislio sam da se u Step() radi samo CRTANJE objekta, a zapravo se radi racunanje + crtanje. Ali ko/sta onda poziva update(float dt)..?

Edit: obrisao sam poveci post/kod, jer nikako da skontam: vece li je dt ili timeStep..?
Ako je (ipak) dt > timeStep, kako onda radis racunanje u Step() sa dt (?), odnosno cemu uopste parametar timeStep u metodi Step?

Pozz


[Ovu poruku je menjao Rapaic Rajko dana 17.05.2014. u 19:34 GMT+1]

[Ovu poruku je menjao Rapaic Rajko dana 17.05.2014. u 19:56 GMT+1]
[ glorius @ 17.05.2014. 20:32 ] @
Hvala na zainteresovnosti :)

Pokusacu sto preciznije da objasnim.

Box2D je cisto framework koji sluzi za implementiranje fizike.
Citat:
"Mislio sam da se u Step() radi samo CRTANJE objekta, a zapravo se radi racunanje + crtanje.".

Box2D radi samo proracune vezane za fiziku. Za crtanje je zaduzen drugi deo aplikacije. Vise o tome kasnije.

Box2D:

Da ne komplikujem previse, Box2D enkapsulira sve one slozene jednacine za fiziku. Znaci, input su sile, brzine i impulsi a output su pozicija koja se dobija delovanjem tih sila + rotacija.
Ti zadas parametre sila, brzina i impulsa koji deluju u trenutnom frejmu na sve objekte i onda pozoves Step() koji uradi sve te proracune i onda ti vrati nove pozicije objekata + njihov trenutni ugao ako se objekat rotira pod uticajem sila.

Ti pokupis te info za position i angle koje su dovoljne za iscrtavanje sprajta na ekranu. A posto Box2D ima prilicno realnu fiziku, sve to izgleda vrlo lepo.

Citat:
Ali ko/sta onda poziva update(float dt)..?


U programu imam dosta klasa ali za potrebe objasnjenja ove nedoumice cu navesti samo relevantne: Application, Renderer, Sprite i Box2DPhysics

Application je glvna klasa koja vrti while petlju, izracunava dt i poziva funkcije ostalih klasa:

void Application::Run()
{
while(1)
{
float dt = GetDT(); // GetDT je hipoteticka funkcija koja na neki nacin izracunava dt
Box2DPhysics::ApplyForce(10, 0); // zadajemo silu da delje na nas objekat
Box2DPhysics::update(dt); // u ovoj funkciji se poziva nasa Step() funkcija
Sprite sprite;
sprite.setPosition(Box2DPhysics::getPosition()); // zadajemo sprite poziciju
Renderer::drawSprite(sprite);
}
}

sto se tice timeStep i dt, kao sto sam rekao:

Citat:
Postoji jos jedan nacin da se ovo resi ali meni nije radio kako treba. Poenta je da se postavi timeStep da bude konstantan (1/60 npr) i da se napravi petlja koja zove step() sa timeStep argumentom, a u medjuvremenu da se timeStep akumulira u neku passTime promenljivu i ako je passTime>= dt da se izadje iz te petlje


ovo je samo workaround koji sam nasao na netu da se postigne ista brzina kretanja nezavisno od framerate.

Nadam se da je sada malo jasnije o cemu se ovde radi :)
[ Rapaic Rajko @ 18.05.2014. 11:17 ] @
Jeste jasnije, donekle.

Medjutim, ovaj kod je cudan, i postaje jasnije zasto ti nije radilo dobro sa primerom sa net-a:

Code:
void Application::Run()
{
  while(1)
  {
    float dt = GetDT(); // GetDT je hipoteticka funkcija koja na neki nacin izracunava dt
    Box2DPhysics::ApplyForce(10, 0); // zadajemo silu da delje na nas objekat
    Box2DPhysics::update(dt); // u ovoj funkciji se poziva nasa Step() funkcija
    Sprite sprite;
    sprite.setPosition(Box2DPhysics::getPosition()); // zadajemo sprite poziciju 
    Renderer::drawSprite(sprite);
  }
}


U pozivu update(dt), rade se visestruka racunanja (timeStep by timeStep), ali crtanje (Renderer) se poziva tek po isteku dt; drugim recima crta se samo zadnja pozicija sprite-a.

Medjutim, vratimo se na primer iz tvog prvog posta:

Code:

void DPhysicsEngine::update(float dt)
{
    static float timeStep = 1.0f/60.0f;
    static int32 velocityIters = 6;
    static int32 positionIters = 2;

    m_pWorld->Step(timeStep, velocityIters, positionIters);
}


ali uz ovu izmenu:

Citat:
Probao sam za timeStep da stavim dt (da timestep bude varijabilan) ali to opet nije dovelo do konstantnog framerate.


Ako sa ovom izmenom NIJE radilo dobro, odnosno brzina radnje nije bila ista u svim varijantama, to je zbog 2 moguca razloga. Prvi je da Step() ne radi dobro, a drugi je da GetDT() (float dt = GetDT();) falsira. Nekako mi verovatnije ovo drugo; po nekoj logici, dt bi moralo biti razlika sistemskog vremena izmedju dva uzastopna prolaska kroz while(1) {...} petlju. Da li je to i slucaj?

Pozz ;)