Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Black-Panther

Alter Hase

  • »Black-Panther« ist der Autor dieses Themas

Beiträge: 1 443

Wohnort: Innsbruck

  • Private Nachricht senden

1

21.02.2008, 21:05

Problem mit Physikengine

Hi!

Ich arbeite derzeit an einer Physikengine. Kollisionserkennung läuft mittels der SeparatingAxis-Methode, optional im Prediction-Mode. Auflösen tue ich die Kollisionen mittels eines impulsbasierten Ansatzes von Jan Bender.
Nun hab ich allerdings das Problem, dass die Objekte wenn sie ruhig liegen sollten anfangen zu hüpfen. Ein bisschen hüpfen wäre ja i.O., zumal RestingContacts mit Microimpulsen aufgelöst werden, da ist das ja natürlich. Doch bei mir hüpft das Zeug eindeutig zu viel. Nun such ich den verdammten Fehler schon seit langem, komm aber einfach nicht dahinter. Kollisionserkennung läuft richtig. Kontaktgeometrie wird ebenfalls richtig berechnet, es scheitert folglich bei der Kollisionsauflösung.

Ich hab euch mal den aktuellen Stand hochgeladen, damit ihr genau seht was ich meine (vor allem in test4.ogam Modell)
Hier der Downloadlink.

ACHJA: (edit) Vielleicht noch etwas zum Programm. Mit F7 schaltet man die Gravitation ein. Ansonsten sind alle Befehle mit F1 einblendbar!

Nun zum Code. Es tut mir leid, doch muss ich jetzt hier die ganze Kollisionsauflösung posten, da ich es leider nicht eingrenzen konnte.
Vorab allerdings noch die Einstellungen der Engine:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Gibt an ob ein linksorientierter (D3D) Vektorraum oder ein rechtsorientierter (OGL) verwendet wird

const bool              c_bLeftOrientedCoordSystem              = true;
//Soll zur Kollisionserkennung ein Kollisionsvorhersagesystem verwendet werden?

const bool              c_bUseCollisionPrediction               = true;
//Gibt die maximale Anzahl der globalen Kräfte pro Frame an

const unsigned int      c_iMaxGlobalForces                      = 100;
//Entscheidet, ab welcher Anzahl von Vertizes der HillClimbing-Query

//dem linearen BruteForce vorgezogen werden soll

//Varname steht für BreakEvenPointForExtremeVertexQuery

const unsigned int      c_iBEPForExtremeVertexQuery             = 40;
//Gibt die allg. zu verwendende FloatingPoint Toleranz an

const float             c_fFloatingPointTolerance               = 0.0001f;
//Wenn die relative Geschwindigkeit zweier Kontaktpunkte in Normalrichtung betragsmäßig kleiner ist

//als dieser Wert, handelt es sich um einen Resting-Kontakt!

const float             c_fRestingContactTolerance              = sqrtf(19.62f * c_fFloatingPointTolerance);
//Gibt den Standard-Elastizitätsfaktor an

const float             c_fStdElasticyCoeff                     = 0.8f;
//Toleranzwert für welchen eine Rückstoßgeschwindigkeit als erreicht betrachtet wird

const float             c_fReimpulsiveVelocityTolerance         = 0.0001f;
//Maximale Anzahl der Iterationen die verwendet werden dürfen um alle Kollisions aufzulösen! 

const unsigned int      c_iMaxCollisionImpulsIterations         = 5000;
//Maximale Anzahl der Iterationen die verwendet werden dürfen um alle RestingContacts aufzulösen! 

const unsigned int      c_iMaxRestingContactIterations          = 5000;
//Toleranzwert, ab welchem eine Gelenksbedingung als aufgelöst gilt

const float             c_fJointTolerance                       = 0.0001f;
//Standardwert für Gleitreibungskoeffizient

const float             c_fStdDynamicFrictionCoeff              = 0.01f;
//Standardwert für Haftreibungskoeffizient

const float             c_fStdStaticFrictionCoeff               = 0.02f;


So... und nun die Kollisionsauflösung:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
const bool ogpCollisionResponse::DoCollisionResponse(ogpContactList* pFirstContactPoint, const UINT iCPCount, 
                                                     const float fStepSize)
{
    if(iCPCount <= 0) return true;
    //Falls nötig Arrays vergrößern!

    if(iCPCount > m_iTempInfoSize)
    {
        m_iTempInfoSize = iCPCount;
        if(m_pTempInfo) OG_SAFE_DELETE_ARRAY(m_pTempInfo);
        m_pTempInfo = new STempInfos[m_iTempInfoSize];
    }
    if(iCPCount > m_iContactArraySize)
    {
        m_iContactArraySize = iCPCount;
        if(m_pContactArray) OG_SAFE_DELETE_ARRAY(m_pContactArray);
        m_pContactArray = new ogpContact[m_iContactArraySize];
    }

    //Liste der Kontakte in Array kopieren

    ogpContactList* pContact = pFirstContactPoint;
    for(UINT i = 0; i < iCPCount; ++i) {m_pContactArray[i] = pContact->Contact; pContact = pContact->pNext;}

    //Bleibende und kollidierende Kontakte trennen. Kollidierende am Anfang und bleibende am Ende des Arrays

    UINT iCCCount = iCPCount;
    for(int i = 0; i < static_cast<int>(iCCCount); ++i)
    {
        const og3DVector urel   = m_pContactArray[i].pA->CalcPointVelocity(m_pContactArray[i].vPoint) - 
                                  m_pContactArray[i].pB->CalcPointVelocity(m_pContactArray[i].vPoint);
        const float fUrelDotN   = og3DVectorDot(urel, m_pContactArray[i].vNormal);
        if(fabsf(fUrelDotN) < c_fRestingContactTolerance)
        {
            //RestingContact!

            const ogpContact Temp       = m_pContactArray[i];
            m_pContactArray[i--]        = m_pContactArray[--iCCCount];
            m_pContactArray[iCCCount]   = Temp;
        }
    }
    const UINT iRCCount = iCPCount - iCCCount;

    //Kollisionsauflösung für kollidierende Kontakte

    if(iCCCount > 0)
    {
        if(!DoCRForCollidingContact(m_pContactArray, iCCCount)) 
            OG_WARNING(L"CollidingContact-Auflösung wurde nicht ordnungsgemäß beendet!");
    }

    //Kollisionsauflösung für bleibende Kontakte

    if(iRCCount > 0)
    {
        if(!DoCRForRestingContact(m_pContactArray + iCCCount, iRCCount, fStepSize)) 
            OG_WARNING(L"RestingContact-Auflösung wurde nicht ordnungsgemäß beendet!");
    }

    return true;
}

//-----------------------------------------------------------------//


const bool ogpCollisionResponse::DoCRForCollidingContact(ogpContact* pContacts, const UINT iCPCount)
{
    //Folgende Daten müssen nur einmal berechnet werden!

    for(UINT i = 0; i < iCPCount; ++i)
    {
        //Relative Geschwindkeit berechnen

        const og3DVector urel   = pContacts[i].pA->CalcPointVelocity(pContacts[i].vPoint) -
                                  pContacts[i].pB->CalcPointVelocity(pContacts[i].vPoint);
        const og3DVector vUrel  = og3DVectorDot(urel, pContacts[i].vNormal) * pContacts[i].vNormal;

        m_pTempInfo[i].fElasticity = pContacts[i].pA->GetElasticyCoeff() * pContacts[i].pB->GetElasticyCoeff();
        m_pTempInfo[i].vTargetUrel = -m_pTempInfo[i].fElasticity * vUrel;
        m_pTempInfo[i].K = pContacts[i].pA->CalcKMatrix(pContacts[i].vPoint, pContacts[i].vPoint) +
                           pContacts[i].pB->CalcKMatrix(pContacts[i].vPoint, pContacts[i].vPoint);

        //Achse in Normalrichtung schauen lassen

        if(og3DVectorDot(pContacts[i].vNormal, pContacts[i].vAxis) < 0.f) pContacts[i].vAxis = -pContacts[i].vAxis;
    }

    //iOk speichert die Anzahl der Kontaktpunkte, welche erfolgreich aufgelöst wurden!

    //Bei einer Impulsanwendung wird iOk auf 1 gesetzt!

    UINT            iOk                 = 0;
    UINT            iIndex              = 0;
    UINT            iIterations         = 0;
    og3DVector      vImpulsSum          = 0.f;
    while(iOk++ < iCPCount)
    {
        //Relative Geschwindigkeit der beiden Kontaktpunkte berechnen!

        const og3DVector urel   = pContacts[iIndex].pA->CalcPointVelocity(pContacts[iIndex].vPoint) - 
                                  pContacts[iIndex].pB->CalcPointVelocity(pContacts[iIndex].vPoint);
        const float fUrelDotN   = og3DVectorDot(urel, pContacts[iIndex].vNormal);
        const og3DVector vUrel  = fUrelDotN * pContacts[iIndex].vNormal;

        //1. Bedingung: Gesamtimpuls gleich Null, und Zielgeschwindigkeit überschritten --> Erfolgreiche Kollisionsauflösung

        if(vImpulsSum != og3DVector(0.f) || fUrelDotN <= og3DVectorDot(m_pTempInfo[iIndex].vTargetUrel, pContacts[iIndex].vNormal))
        {
            const og3DVector vDeltaUrel = m_pTempInfo[iIndex].vTargetUrel - vUrel;
            const float fDotDeltaUrel   = og3DVectorDot(vDeltaUrel, pContacts[iIndex].vNormal);
            //2. Bedingung: Zielgeschwindigkeit bereits erreicht!

            if(fDotDeltaUrel > c_fReimpulsiveVelocityTolerance)
            {
                //Abstoßenden Impuls berechnen und anwenden

                const float fVectorMatrixProduct = ogUtilsMatrixVectorProduct(pContacts[iIndex].vNormal, m_pTempInfo[iIndex].K);
                og3DVector p = vDeltaUrel;
                if(fabsf(fVectorMatrixProduct) > c_fFloatingPointTolerance) p /= fVectorMatrixProduct;
                pContacts[iIndex].pA->ApplyImpuls(p, pContacts[iIndex].vPoint);
                pContacts[iIndex].pB->ApplyImpuls(-p, pContacts[iIndex].vPoint);
                vImpulsSum += p;
                iOk = 1;
            }
            else if(fDotDeltaUrel < -c_fReimpulsiveVelocityTolerance && 
                    og3DVectorDot(vImpulsSum, pContacts[iIndex].vNormal) > -c_fFloatingPointTolerance)
            {
                //Anziehenden KorrekturImpuls berechnen...

                const float fVectorMatrixProduct = ogUtilsMatrixVectorProduct(pContacts[iIndex].vNormal, m_pTempInfo[iIndex].K);
                og3DVector p = vDeltaUrel;
                if(fabsf(fVectorMatrixProduct) > c_fFloatingPointTolerance) p /= fVectorMatrixProduct;
                if(og3DVectorDot(vImpulsSum + p, pContacts[iIndex].vNormal) < -c_fFloatingPointTolerance)
                {
                    //Impuls zu stark --> maximal gültigen anwenden!

                    p = -vImpulsSum;
                }
                //... und anwenden

                pContacts[iIndex].pA->ApplyImpuls(p, pContacts[iIndex].vPoint);
                pContacts[iIndex].pB->ApplyImpuls(-p, pContacts[iIndex].vPoint);
                vImpulsSum += p;
                iOk = 1;
            }
        }

        //Maximale Anzahl an Iterationen erreicht? --> Abbruch!

        if(++iIterations > c_iMaxCollisionImpulsIterations) return false;
        //Kontaktpunkte werden wie ein Ringbuffer immer wieder durchlaufen!

        if(++iIndex >= iCPCount) iIndex = 0;
    }

    return true;
}

//-----------------------------------------------------------------//


//ANMERKUNG: fStepSize soll den künftigen Zeitschritt approximieren. Es

//ist der aktuelle Zeitschritt und es wird davon ausgegangen, dass sich

//die Schrittweite nicht groß ändert! (optimale Lösung --> FrameBasedRendering)

const bool ogpCollisionResponse::DoCRForRestingContact(ogpContact* pContacts, const UINT iCPCount, 
                                                       const float fStepSize)
{
    //Folgende Daten müssen nur einmal berechnet werden!

    for(UINT i = 0; i < iCPCount; ++i)
    {
        m_pTempInfo[i].K = pContacts[i].pA->CalcKMatrix(pContacts[i].vPoint, pContacts[i].vPoint) +
                           pContacts[i].pB->CalcKMatrix(pContacts[i].vPoint, pContacts[i].vPoint);
        //Achse in Normalrichtung schauen lassen

        if(og3DVectorDot(pContacts[i].vNormal, pContacts[i].vAxis) < 0.f) pContacts[i].vAxis = -pContacts[i].vAxis;
    }

    //iOk speichert die Anzahl der Kontaktpunkte, welche erfolgreich aufgelöst wurden!

    UINT            iOk                 = 0;
    UINT            iIndex              = 0;
    UINT            iIterations         = 0;
    og3DVector      vImpulsSum          = 0.f;
    while(iOk++ < iCPCount)
    {
        //Wenn Körper ineinander driften, dann Zeit Geschwindigkeit in Normalrichtung

        const og3DVector vrel   = pContacts[iIndex].pB->GetVelocity() - pContacts[iIndex].pA->GetVelocity();
        //Positiv, falls Körper ineinander driften

        const float fVn         = og3DVectorDot(vrel, pContacts[iIndex].vAxis);
        //N * d < 0, falls sich Körper schneiden!

        const og3DVector d      = (pContacts[iIndex].fDistance - fVn * fStepSize) * pContacts[iIndex].vAxis;
        //Positiv, wenn Körper sich überlappen

        const float fdDotN      = og3DVectorDot(-d, pContacts[iIndex].vNormal);

        //1. Bedingung: GesamtImpuls gleich Null, und Abstand in Normalenrichtung positiv!

        if(vImpulsSum != og3DVector(0.f) || fdDotN > -c_fJointTolerance)
        {
            //2. Bedingung: Abgstand klein genug, dass als erfüllt angesehen werden kann

            if(fabsf(fdDotN) > c_fJointTolerance)
            {
                //Positions-Korrektur-Impuls berechnen

                const float fVectorMatrixProduct = ogUtilsMatrixVectorProduct(pContacts[iIndex].vNormal, m_pTempInfo[iIndex].K);
                og3DVector p = (fdDotN * pContacts[iIndex].vNormal) / (fVectorMatrixProduct * fStepSize);
                if(fdDotN < 0.f && og3DVectorDot(vImpulsSum + p, pContacts[iIndex].vNormal) < -c_fFloatingPointTolerance)
                {
                    //Impuls zu stark --> maximal gültigen anwenden!

                    p = -vImpulsSum;
                }
                //und anweden!

                pContacts[iIndex].pA->ApplyImpuls(p, pContacts[iIndex].vPoint);
                pContacts[iIndex].pB->ApplyImpuls(-p, pContacts[iIndex].vPoint);

                vImpulsSum += p;
                iOk = 1;
            }
        }

        if(++iIterations > c_iMaxRestingContactIterations) return false;
        if(++iIndex >= iCPCount) iIndex = 0;
    }

    return true;
}


Danke schon mal... Wäre auch froh, wenn manche nur das Programm testen und meine Beobachtungen bestätigen könnten. Optimal wäre natürlich wenn jemand genau eine Situation herbeiführen könnte, da das Problem IMMER auftritt!
Danke
stillalive studios
Drone Swarm (32.000 Dronen gleichzeitig steuern!)
facebook, twitter

Black-Panther

Alter Hase

  • »Black-Panther« ist der Autor dieses Themas

Beiträge: 1 443

Wohnort: Innsbruck

  • Private Nachricht senden

2

24.02.2008, 21:36

Hat es bis jetzt wirklich noch keiner probiert? Wie gesagt, es würde mir auch durchaus helfen, wenn man meine oben genannten Probleme bestätigen würde, und mir eventuell so beim debugging helfen könnte. (Abgesehen davon, würde mich interessieren, was die FPS bei euch so machen.... Beim test4-Modell)
stillalive studios
Drone Swarm (32.000 Dronen gleichzeitig steuern!)
facebook, twitter

Frede

Treue Seele

Beiträge: 259

Wohnort: Hameln

Beruf: Schüler

  • Private Nachricht senden

3

24.02.2008, 21:44

Problem bestätigt. Fps zwischen 400 und 500. (standard settings, 1240x1024)

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

4

25.02.2008, 14:18

Also bei Rendermode auf ps3 bricht die FPS-Rate massiv ein. Ansonsten bei ps2 liegt die FPS-Rate um 200.
Wegen der Physikproblematik: also soweit ich das sehe scheint die abstoßende Kraft umsogrößer zu werden je kleiner die Geschwindigkeit (sprich fast Ruhe=>sehr große Kraft) ist. Leider habe ich von dem Thema wenig Ahnung aber vielleicht hast du irgendwo * mit / verwechselt, sodass die abstoßende Kraft nicht immer kleiner wird, je kleiner die Relativgeschwindigkeit, wird sondern immer größer. Wäre zumindest nach meinen Überlegungen eine Möglichkeit.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Werbeanzeige