Autor Wątek: Kolizje 2D OdsuwanieKul/Impuls  (Przeczytany 2958 razy)

Offline Vizzzard

  • Użytkownik

# Sierpień 01, 2007, 13:44:13
Witam :)

Ostatnio napisałem sobie mały silniczek który oblicza kolizje dla kul w 2D, o ile obiczenia kątowe i wymiana sił wygląda dobrze i dziala dobrze to problemy zaczynają wystepować gdy mamy powiedzy 100 kul na ekranie które tworzą stos (piramidke), otóz zaczynaja nachodzic na siebie w Y, jak i w X gdy sie juz nie mieszczą.

dzieje sie tak zapewne dlatego ze:

A-> Brak mi kodu odsuwającego je od siebie poniewarz niewiem o ile powinienem je odsunąć wykorzystując wzór

Distance = sqrt((mBall[b1].x-mBall[b2].x)*(mBall[b1].x - mBall[b2].x)+(mBall[b1].y - mBall[b2].y)*(mBall[b1].y - mBall[b2].y));

i gdy grawitacja = 2 a VektorY bili jest mniejszy niz 2 to zaczyna sie nakładanie. to samo tyczy sie tarcia...

B-> brak mi równowaznika siły który zrównowarzył by Wektory i doprowadził je do zera gdy są mniejsze od od grawitacji i tarcia.

Skłaniam sie bardziej ku rozwiązaniu A poniewarz metode B probowałem i jakos kiepsko to wyszło, mozliwe dlatego ze źle to zrobiłem

podaje moj kod
Distance = sqrt((mBall[b1].x-mBall[b2].x)*(mBall[b1].x - mBall[b2].x)+(mBall[b1].y mBall[b2].y)*(mBall[b1].y - mBall[b2].y));

//liczenie pdległosci miedzy kulami


//jesli dystans jest mniejszy od promieni obu kul

if(Distance <= 10 + 10)
{

float px;
float py;
float p;
float alpha;
float dp;
float dpx;
float dpy;


px=(mBall[b2].VecX- mBall[b1].VecX)*mBall[b2].mass; //liczymy wektor pędu px
py=(mBall[b2].VecY- mBall[b1].VecY)*mBall[b2].mass; //liczymy wektor pędu py

p = sqrt(pow(px,2)+pow(py,2)); //liczymy wektor pedu ogólny

if (p>0) //jesli pęd jest wiekszy od zera
{
alpha =( ((mBall[b1].x-mBall[b2].x)*px)+((mBall[b1].y-mBall[b2].y)*py))/(Distance*p); // policz kosinus alfa

if (alpha>0) //jesli alpha jest wieksze od 0
{
dp=p*alpha*2*mBall[b1].mass/(mBall[b1].mass+mBall[b2].mass);
dpx=((mBall[b1].x-mBall[b2].x)/Distance)*dp; // policz  dp
dpy=((mBall[b1].y-mBall[b2].y)/Distance)*dp;


mBall[b1].VecX+=dpx/mBall[b1].mass; //dodajemy nowe wektory do kuli               
mBall[b1].VecY+=dpy/mBall[b1].mass; //pierwszej x i y



mBall[b2].VecX-=dpx/mBall[b2].mass; //tutaj nalezy je odjąć                 
mBall[b2].VecY-=dpy/mBall[b2].mass; //x i y

czy ktos mugł by sypnac kodem lub wzorami na odsuniecie od siebie 2 kul w momencie zderzenia?  ;D

będe wdzieczny za kazdą pomoc

EDIT:

Oznaczenia:

mBall[b1].x <- pozycja x bili pierwszej
mBall[b1].y <- pozycja y bili pierwszej

mBall[b1].VecX <- prędkosc z jaką bila porusza sie po osi X (dł Wektora X)
mBall[b1].VecY <- prędkosc z jaką bila porusza sie po osi Y (dł Wektora Y)

blia 2 ma index mBall[b2]
« Ostatnia zmiana: Sierpień 01, 2007, 13:47:32 wysłana przez Vizzzard »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Willard

  • Użytkownik
    • Seventhtear

# Sierpień 01, 2007, 16:08:11
Nie wiem czy dobrze Cię zrozumiałem. Ale jeżeli chcesz cofnąć kulę, gdy się ona zagłębi w drugiej to sprawa jest prosta. Policzyłeś już Distance (jest to odległość między środkami kul - jak rozumiem), zatem jeżeli Distance jest mniejszy niż suma promieni kul, to znaczy, że nachodzą one na siebie i należy cofnąć o wartość (Distance - (promien1+promien2)). Cofnięcia należy dokonać np wzdłuż wektora prędkości poruszającej się kuli.

Mała uwaga. Wiadomo, że liczenie pierwiastka na komputerze jest bardzo wolne w stosunku np do liczenia kwadratu. Jak liczysz Distance to pomiń liczenie pierwiastka. Za to przy porównaniu z sumą promieni, zrób porównanie z kwadratem sumy promieni.

Offline Vizzzard

  • Użytkownik

# Sierpień 01, 2007, 16:17:05
ok zobacze co otrzymam i czy teraz będzie dobrze, bo nie wiem czy przy tej metodzie kule na stosie nie bedą poprostu skakac. (zamieszcze wyniki z kodem tej metody)

a co myslisz o metodie akumulacji impulsu aby wyrównać siły?

jeśli beda jakieś niejasności to moge przesłać cały projekt lub zamiescic screeny jak jest obecnie a jak być powinno bo mam podobny test zrobiony przy pomocy ODE i do podobnych wynikow chce dojsc  ;D.

Offline Vizzzard

  • Użytkownik

# Sierpień 01, 2007, 17:17:55
po dodaniu kodu odsuniecia:

if(Distance <= 10 + 10)
{

float px;
float py;
float p;
float alpha;
float dp;
float dpx;
float dpy;

float dt;


dt = (Distance - (10+10));

mBall[b1].VecX += dt;
mBall[b1].VecY += dt;
mBall[b2].VecX -= dt;
mBall[b2].VecY -= dt;
....

zaraz po zetknięciu, i przed obliczeniem odpowiednich sił dostałem prawie ze idealną symulacje i idealny niemalze stos (1 kulka na spodzie zachodziła na 2 o pare px) dla parametórw:

Grawitacja = 1;
Tarcie = 0.05;
Masa kazdej kuli = 1;

natomiast dla parametórw

Grawitacja = 2;
Tarcie = 0.05;
Masa kazdej kuli = Random od 1 do 5;

kule juz troche nachodziły na siebie ale i tak nie jak na początku gdzie był to dramat, obrazek tego wyniku jest w załączniku  :) (phys1) i jak widac niektóre kulki wciaz nachodzą na siebie.

Paradoksalnie gdy kod odsuniecia dodałem na samym koncu juz po obliczeniu nowych sił wektorów wyniki były o wiele gorsze a wydawało mi sie ze lepsze powinny byc.



dla porównania podaje załącznik z tego samego testu ale z wykorzystaniem ODE (odephys),
moze ktos ma jakis pomysł zeby moją symualcje doprowadzic do poziomu z 2 obrazka, puki co bez obrotów kuli, chodzi o to aby kule nie nachodziły na siebie bo to puki co najwazniejsze a kolejna sprawa to taka ze moje kulki nawet jak są na stosie to lekko drgaja w symulacji ode wogole sie to nie dzieje.

pozdrawiam, i dzieki za kod odsuniecia (przydał sie :) )




Offline Willard

  • Użytkownik
    • Seventhtear

# Sierpień 01, 2007, 21:57:28
Drgania mogą być powodem błędów numerycznych (niedokładności obliczeń). Zauważ, że gdy kule leżą na sobie, mimo wszystko ta na górze będzie miała co chwila niewielki wektor  prędkości skierowany w dół (wtedy ty ja cofasz i nie pozwalasz wbić się w ta niżej). Spróbuj ignorować niewielkie wektory prędkości. Oczywiście nie gwarantuje, że to będzie w 100% dobre.
Podaje Ci link do fragmentu filmu z mojej pracy dyplomowej (inżynierka) "Fizyka bryły sztywnej w programowaniu gier" (wiem, że nie jest super ale obroniłem to na satysfakcjonującą ocenę)
http://www.seventhtear.com/data/dyplom_0005.dat (zmień po ściągnięciu rozszerzenie na .wmv)
Jest to fizyka w 3d - niestety.

Offline Vizzzard

  • Użytkownik

# Sierpień 01, 2007, 23:12:12
dzieki za filmik :D

Ja dopiero po wakacjach bede pierwszaczkiem na studiach :D

fajny projekt, monza by z tego zrobic silnik efektów cząstkowych, btw probowałes zrobic u sebie taki stress test z setkami kul w małym powieszczeniu? bo moze brakuje mi czegos drobnego w kodzie co ty masz, lub moze skrócenie vektora do 0 gdy jego wartosc jest mała + impuls akumulacji by pomogło musze poeksperymentować. Ew przejrzec zawiły kod z ODE co do tych wyliczen sfer.

z tego co jeszcze zaobserwowałem. im wieksza grawitacja tym wiekszy wspułczynnik nachodzenia

dla zabawy zwiekszyłem grawitacje z 2 do 20 i z 4 rzedów kul w 1 sek zrobił mi sie 1, oczywiscie w innych silnikach fizycznych nawet po ustawianiu absurdalnie niemozliwych parametrów fizycznych swiata (grawitacja, wsp odbicia, tarcie) nie doszło by do takiej sytuacji.

musze pokombinowac, jak dojde do czegos pozytywnego to napisze :D

aha i jesli posiadasz jeszcze ten silnik który uzyłes na obrone to jesli mozesz to przeprowadz u siebie taki stress test i zobacz czy kule nachodza na siebie :) bym był zobowiazany, wtedy łatwiej bedzie mi dosjc do rozwiazania

Pozdrawiam

Offline Willard

  • Użytkownik
    • Seventhtear

# Sierpień 02, 2007, 00:54:38
OK, http://www.seventhtear.com/data/test.zip
No niestety też nie jest dobrze. Poza tym u mnie jest ten problem ze przy każdym uruchomieniu symulacja przebiega inaczej.
To był mój pierwszy w życiu silnik fizyki (jak widać), dlatego teraz skupiłem się na silniku fizyki 2d, mam nadzieje ze ten przynajmniej bedzie porządny.
Podobny test do Twojego jest pod klawiszem 7. Klawiszologia jest wypisana na ekranie.

P.S. Silnik ma też inne wady i niedociągnięcia. Czasami je widać, czasami nie.
« Ostatnia zmiana: Sierpień 02, 2007, 00:56:19 wysłana przez Willard »

Offline Vizzzard

  • Użytkownik

# Sierpień 02, 2007, 01:08:13
Fakt, no wiec skoro oboje robimy 2D Engine to soimy przed dokładnie tym samym problemem dla kul przynajmniej, niewiem jak kwadraty by sie zachowały ale sądze ze tez błąd by wystapił.

za to natknołem się na kod Box2D najnowsza wersja, gdzie o ile sie nie myle mozemy włączyć i wyłączyć cos co nazywa się akumulacją impulsu, gdy to wyłączymy stosy kostek nachodzą na siebie, gdy włączymy wszystko jest idealne. Czyzby wiec nalezało by zastosować podejscie impulsowe????

problem polega na tym ze ten kod jest dostepny ale napisany tak zawile ze prawie nic z niego nie rozumiem, przeciazone są Operatory, itp. poplątanie z pomieszaniem generalnie :)

daj znac jesli znajdziesz sensowne rozwiazanie problemu :), ja zrobie to samo jesli mnie sie uda.

BTW. Moze by tak połączyć sily? :D

Offline Spider100

  • Moderator
    • Strona domowa

# Sierpień 02, 2007, 09:16:10
Hmm...  a mój silnik sobie z tym radzi bardzo dobrze przy 300 obiektach i 9 iteracjach impulsów :D

Cytuj
za to natknołem się na kod Box2D najnowsza wersja, gdzie o ile sie nie myle mozemy włączyć i wyłączyć cos co nazywa się akumulacją impulsu, gdy to wyłączymy stosy kostek nachodzą na siebie, gdy włączymy wszystko jest idealne. Czyżby wiec należało by zastosować podejscie impulsowe?

Tak dobrze kombinujesz teraz tylko zagłębisz się w kodzie i problem rozwiązany.

Offline Vizzzard

  • Użytkownik

# Sierpień 02, 2007, 11:57:25
Spider:

łatwo ci mowic, ten kod jest tak pomieszany i mało opisany ze niewiem co jest gdzie i czym :D

byś moze powiedział jak powinno sie obliczac poprawnie tą akumulacje, i jak ty to zrobiłes? :)

bo moj stress test przy losowych masach 1 - 10 kul i grawitacji wiekszej niz 1 nie wyglada słodko, a musze sie z tym uporac jesli chce myslec o połączeniach linowych i innych figurach.

Pozdrawiam

Offline Charibo

  • Redaktor

# Sierpień 02, 2007, 12:21:10
Cytuj
łatwo ci mowic, ten kod jest tak pomieszany i mało opisany ze niewiem co jest gdzie i czym
W takiej sytuacji najlepiej jest go poprostu przepisac :)

Offline Vizzzard

  • Użytkownik

# Sierpień 02, 2007, 12:26:49
niebardzo, ja w swoim kodzie nie mam Vec2 które ma poprzeciazane wszystkie mozliwe operatory, ani tez niemam funkcji typedef, nie potrzebne mi narazie takie optymalizajcje, patrząc na tamten engine czasem ma sie wrazenie ze niektore wywołania i deklaracje biorą sie znikąd.  :-\

wiec przepisanie tylko kolizji do mojego kodu, jest poprostu niemozliwe :D

Offline zephyr

  • Użytkownik

# Sierpień 02, 2007, 12:27:50
Może spróbować "kul sprężynek" - przy kolizji dać siłę wprost proporcjonalną do zagłębienia kul w sobie umieszczoną na prostej przechodzącej przez środki kul.
Silnika fizycznego nie pisałem to nie wiem jak to się sprawdzi w praktyce.

A co do przeciążania - przecież jak ktoś to robi sensownie to kod jest czytelniejszy...

Offline Vizzzard

  • Użytkownik

# Sierpień 02, 2007, 12:49:15
juz probowałem, narazie tylko systemem prowizorycznym, przy danych gravitacja = 1 masa = 1;

wygladało to tak ze po ustawienia wektora siły sprawdzało czy Wektory nie są mniejsze od grawitacji i jesli są to wyrównywało je odpowiednio. Owszem poprawiło to nachodzenie na siebie ale nadal nachodziły.

Offline Spider100

  • Moderator
    • Strona domowa

# Sierpień 02, 2007, 15:57:26
Spider:

łatwo ci mowic, ten kod jest tak pomieszany i mało opisany ze niewiem co jest gdzie i czym :D

byś moze powiedział jak powinno sie obliczac poprawnie tą akumulacje, i jak ty to zrobiłes? :)

bo moj stress test przy losowych masach 1 - 10 kul i grawitacji wiekszej niz 1 nie wyglada słodko, a musze sie z tym uporac jesli chce myslec o połączeniach linowych i innych figurach.

Pozdrawiam

Przepraszam ale nie mam czasu sie rozpozpisywać może mój kod będzie bardziej czytelny:

{------------------------------------------------------------------------------}
{ Przygotowanie impulsow }

procedure TArbiter3D.PreStep(inv_dt: Single);
var
  i: Integer;
  impulse: TVector3D;
  kNormal: Single;
  kTangent: Single;
  dv: Tv3D;
begin
  cSurface := Mix_Surface(aGeom[0].Surface^, aGeom[1].Surface^);
  vSurfaceVel := v3d_Sub(aGeom[0].pMass^.vSurfaceVel, aGeom[1].pMass^.vSurfaceVel);

  aBody[0] := TRigidBody3D(aGeom[0].Body);
  aBody[1] := TRigidBody3D(aGeom[1].Body);

  for i := 0 to iContacts - 1 do
    with aContacts[i] do
    begin

    // Obliczanie promieni
      vRay[0] := v3D_Sub(vPoint, aBody[0].vPosition);
      vRay[1] := v3D_Sub(vPoint, aBody[1].vPosition);

    // Predkosc wzgledna
      dv := v3D_Sub(aBody[1].Get_Velocity(vRay[1]), aBody[0].Get_Velocity(vRay[0]));

    // Przeliczenie masy normalnej (wybicia)
      kNormal :=
        aBody[0].Get_PointImpulse(vNormal, vRay[0]) +
        aBody[1].Get_PointImpulse(vNormal, vRay[1]);
      sMassN := 1 / kNormal;

    // Przeliczenie masy rownoleglej (tarcie)
      vTangent := v3D_Normalizef(v3D_Cross(vNormal, v3D_Cross(dv, vNormal)));
      kTangent :=
        aBody[0].Get_PointImpulse(vTangent, vRay[0]) +
        aBody[1].Get_PointImpulse(vTangent, vRay[1]);
      sMassT := 1 / kTangent;

   // Redukcja bledu przenikania
      sBias := -cSurface.sBiassCoeff * inv_dt * get_min(0, sSeparation + cSurface.sPenetration);
      tAccum.sBias := 0.0;

   // Licznie odbicia
      sBounce := v3d_Dot(vNormal, dv) * cSurface.sRestitution;

   // Sumowanie impulsu normalnego i tengenta
      impulse := v3D_ADD(
        v3D_Scale(vNormal, tAccum.sImpulseN),
        v3D_Scale(vTangent, tAccum.sImpulseT));

   // Dodawanie imulsow
      aBody[0].Apply_World_Impulse(v3D_Inverse(impulse), vRay[0]);
      aBody[1].Apply_World_Impulse(impulse, vRay[1]);

    end;
end;

{------------------------------------------------------------------------------}
{ Dodawanie impulsow }

procedure TArbiter3D.ApplyImpulse();
var
  i: integer;
  RelativeVel, Impulse: Tv3D;
  oldTangentImpulse, tangentImpulse, maxTangentImpulse: Single;
  oldNormalImpulse, normalImpulse: Single;
  sVdotN: Single;

  // biass
  normalBiasImpulse, jbnOld: Single;

begin

  for i := 0 to iContacts - 1 do
    with aContacts[i] do
    begin

  // Redukcja bledu
      normalBiasImpulse := v3D_Dot(v3d_Sub(aBody[1].Get_Bias(vRay[1]), aBody[0].Get_Bias(vRay[0])), vNormal); //redukcja wzgledna

      normalBiasImpulse := (sBias - normalBiasImpulse) * sMassN;
      jbnOld := tAccum.sBias;
      tAccum.sBias := get_max(jbnOld + normalBiasImpulse, 0.0);
      normalBiasImpulse := tAccum.sBias - jbnOld;

      Impulse := v3d_Scale(vNormal, normalBiasImpulse);

   // uzycie impulsu bledu
      aBody[0].Apply_World_ImpulseBias(v3d_Inverse(Impulse), vRay[0]);
      aBody[1].Apply_World_ImpulseBias(Impulse, vRay[1]);

   // Liczenie predkosci wzglednej
      RelativeVel := v3D_Sub(aBody[1].Get_Velocity(vRay[1]), aBody[0].Get_Velocity(vRay[0]));

   // Licznie impulsu odbicia
      sVdotN := v3D_Dot(RelativeVel, vNormal);

  // Impuls normalny
      normalImpulse := -(sBounce + sVdotN) * sMassN;

      oldNormalImpulse := tAccum.sImpulseN;
      tAccum.sImpulseN := get_Max(oldNormalImpulse + normalImpulse, 0);
      normalImpulse := tAccum.sImpulseN - oldNormalImpulse;

    // Licznie impulsu tarcia
      maxTangentImpulse := cSurface.sFriction * tAccum.sImpulseN { get_min(tAccum.sImpulseN, 50)};
      tangentImpulse := sMassT * (-v3D_Dot(RelativeVel, vTangent));

      oldTangentImpulse := tAccum.sImpulseT;
      tAccum.sImpulseT := get_clamp(oldTangentImpulse + tangentImpulse, -maxTangentImpulse, maxTangentImpulse);
      tangentImpulse := tAccum.sImpulseT - oldTangentImpulse;

    // Licznie wspólnego impulsu dla odbicia i tarcia
      Impulse := v3d_Add(v3D_Scale(vNormal, normalImpulse), v3D_Scale(vTangent, tangentImpulse));

    // Dodawanie imulsow do obiektow
      aBody[0].Apply_World_Impulse(v3D_Inverse(Impulse), vRay[0]);
      aBody[1].Apply_World_Impulse(Impulse, vRay[1]);

    end;
end;