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

Anonymous

unregistriert

1

11.08.2003, 17:12

SplitNode (kleine Verbesserung)

Hallo David,
also nicht dass Du denkst, ich möchte jetzt meckern ;-)
Aber auf Seite 617 hast Du uns gezeigt, wie man mit SplitNode die Boundingboxen im Model splittet.
Dort erscheint mir der Nachteil der Tribase besonders deutlich.
Es wird soviel doppelt berechnet und kopiert.
Ist es nicht besser, SplitNode in die !!!Klasse!!! OcTreeNode reinzunehmen und die Intersect-Funktionen einer Box-Klasse zuzuordnen.
Dann kannst Du doch so hier in SplitNode vorgehen:

Quellcode

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
            PDWORD pdwTemp = new DWORD[m_dwNumTriangles];
            DWORD dwCounter = 0;
            CBox BoundingBox(&m_pChilds[i]->m_vBoundingBoxMin, &m_pChilds[i]->m_vBoundingBoxMax);
            // Alle Dreiecke durchgehen und prüfen, ob sie im Bereich dieses
            // untergeordneten Knotens liegen
            D3DXVECTOR3 vTriA, vTriB, vTriC;
            for(DWORD j = 0; j < m_dwNumTriangles; j++)
            {
                // Vektoren dieses Dreiecks herausfinden
                vTriA = pVectors[pdwIndices[m_pdwTriangles[j] * 3]];
                vTriB = pVectors[pdwIndices[m_pdwTriangles[j] * 3 + 1]];
                vTriC = pVectors[pdwIndices[m_pdwTriangles[j] * 3 + 2]];
    
                // Wenn auch nur einer der Vektoren innerhalb der Bounding-Box
                // liegt, dann wird das Dreieck zu diesem untergeordneten Knoten gehören.
                // Gleiches gilt auch, wenn eine Seite des Dreiecks die Bounding-Box schneidet.
                if (BoundingBox.IntersectPoint(&vTriA) ||
                    BoundingBox.IntersectPoint(&vTriB) ||
                    BoundingBox.IntersectPoint(&vTriC) ||
                    BoundingBox.IntersectLine(&vTriA, &vTriB) ||
                    BoundingBox.IntersectLine(&vTriB, &vTriC) ||
                    BoundingBox.IntersectLine(&vTriC, &vTriA))
                {
                    // Dieses Dreieck zur temporären Liste hinzufügen
                    pdwTemp[dwCounter] = m_pdwTriangles[j];
                    dwCounter++;
                }
            }

            // Die Temporäre Liste zum untergeordneten Knoten kopieren
            m_pChilds[i]->SetTriangles(pdwTemp, dwCounter);

            // Temporäre Liste wieder freigeben
            SAFE_DELETE(pdwTemp);


und hier SetTriangles:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
BOOL COcTreeNode::SetTriangles(const LPDWORD pTriangles, DWORD dwCount)
{ 
    SAFE_DELETE_ARRAY(m_pdwTriangles);
    m_dwNumTriangles = dwCount; 
    if (m_dwNumTriangles > 0)
    {
        m_pdwTriangles = new DWORD[m_dwNumTriangles];
        assert(m_pdwTriangles != NULL);
        if (m_pdwTriangles != NULL)
        {
            if (pTriangles)
                CopyMemory(m_pdwTriangles, pTriangles, m_dwNumTriangles * sizeof(DWORD));
            else
            {
                for (DWORD ix = 0; ix < m_dwNumTriangles; ix++)
                    m_pdwTriangles[ix] = ix;
            }
            return TRUE;
        }
    }
    return FALSE;
}


klar, das ist Dir vielleicht nix neues.
Vielleicht kannst Du mich auch eines besseren belehren. Würde mich freuen, wenn Du zurückmeckern würdest.

Das ist übrigens der Grund, warum ich die Tribase nicht nutze.
Wären die ganz kleinen Dinge besser, dann würde ich nicht das Rad neu erfinden wollen.

Ganz wichtig: Dieses kleine Beispiel da oben wird natürlich nur bei der Laderoutine aufgerufen, deshalb ist hier sicherlich nichts wirklich schlimmes an der Tribase auszusetzen.

Jens

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

11.08.2003, 17:55

Klar kann man das so machen. Aber wird da jetzt weniger "doppelt berechnet"? Was Du jetzt gemacht hast ist doch - wenn ich mich nicht irre - nur, aus den Structs Klassen zu machen, oder?
Übrigens: der Code-Tag ist nicht <code>, sondern mit eckigen Klammern.

Anonymous

unregistriert

3

11.08.2003, 18:15

nicht ganz

was macht z. B. tbLineHitsBox?
-> tbComputeBoxPlanes(vBoxMin, vBoxMax, mBoxTransformation, aBoxPlane);
das wird hier nur einmal in der Box gemacht.
Mit tbPointHitsBox genau das gleiche.

Mit der Identity wird hier (im Klassenorientiertem Code) auch nicht multipliziert.
tbPointHitsBox(vTriA, pNode->apChild->vBoundingBoxMin, pNode->apChild[i]->vBoundingBoxMax, tbMatrixIdentity()) ||

Bei der Kollisionserkennung halte ich diese Einsparung für ganz wichtig.

Verstehst Du? Da ist nur eine Box, während in SplitNode mit den Tribase-Funktionen ständig die gleichen Planes berechnet werden.

Ich glaube ich kann mir sogar ersparen, die Indices im Modell zu speichern.
Ich übergebe einfach die Indices der Methode SplitNode:

Quellcode

1
2
3
4
5
6
7
8
9
void COcTreeNode::SplitNode(const D3DXVECTOR3* pVectors, 
                            const LPWORD pdwIndices, 
                            int iMaxDepth)
{
    assert(pVectors != NULL && pdwIndices != NULL);
    if (iMaxDepth > 0 && pVectors != NULL && pdwIndices != NULL)
    {
        // Mittelpunkt dieses Knotens berechnen
        D3DXVECTOR3 vCenter = 0.5f * (m_vBoundingBoxMin + m_vBoundingBoxMax);


und so gehts dann rekursiv weiter:

Quellcode

1
2
3
4
            // Diesen Unterknoten rekursiv unterteilen, wenn es sich von der Anzahl der
            // Dreiecke her lohnt.
            if (dwCounter >= 32)
                m_pChilds[i]->SplitNode(pVectors, pdwIndices, iMaxDepth - 1);


Alleine durch die Kapselung hat man viel mehr Möglichkeiten, Berechnungen zu sparen.

Anonymous

unregistriert

4

11.08.2003, 18:25

und der SplitNodeAufruf in CModel

das hatte ich vergessen:

Quellcode

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
HRESULT C3DModel::Update(LPDIRECT3DDEVICE9 pd3dDevice, BOOL bExtraData)
{
    // Boundingbox und Sphere bestimmen
    HRESULT hr = CalcBounds(pd3dDevice);
    if (FAILED(hr))
        return hr;

    assert(m_pMesh != NULL);
    if (FAILED(hr = m_pMesh->SetFVF(pd3dDevice, D3DVERTEX::FVF)))
        return hr;

    // Matrix aktualisieren
    UpdateMatrix();
    // wäre dumm, wenns nicht so wäre
    assert(m_pMesh->m_pSysMemMesh != NULL);
    m_dwNumFaces = m_pMesh->m_pSysMemMesh->GetNumFaces();
    m_dwNumVertices = m_pMesh->m_pSysMemMesh->GetNumVertices();
    assert(m_dwNumVertices > 0 && m_dwNumFaces > 0);
    // wenn angegeben, Dreiecke speichern (für Kollisionsabfrage im OcTree)
    if (bExtraData && m_dwNumVertices > 0 && m_dwNumFaces > 0)
    {
        m_pvVectors = new D3DXVECTOR3[m_dwNumVertices];
        assert(m_pvVectors != NULL);
        if (!m_pvVectors)
            return E_OUTOFMEMORY;

        // Speicher für die Ebenen der Dreiecke reservieren (3 Seiten und die Ebene des Dreiecks selbst)
        m_pTrianglePlanes = new D3DXPLANE[m_dwNumFaces * 4];
        if (!m_pTrianglePlanes)
        {
            SAFE_DELETE_ARRAY(m_pvVectors);
            return E_OUTOFMEMORY;
        }

        // Vectoren auslesen
        LPD3DXMESH pMesh = m_pMesh->GetSysMemMesh(); //m_pMesh->GetLocalMesh();
        assert(pMesh != NULL);

        PWORD pIndices = NULL;
        LPDIRECT3DINDEXBUFFER9 pIB = NULL;
        if (FAILED(hr = pMesh->GetIndexBuffer(&pIB)))
        {
            SAFE_DELETE_ARRAY(m_pvVectors);
            SAFE_DELETE_ARRAY(m_pTrianglePlanes);
            return hr;
        }
        if (FAILED(hr = pIB->Lock(0, 0, (void**)&pIndices, D3DLOCK_READONLY)))
        {
            SAFE_RELEASE(pIB);
            SAFE_DELETE_ARRAY(m_pvVectors);
            SAFE_DELETE_ARRAY(m_pTrianglePlanes);
            return hr;
        }
        D3DVERTEX* pVertices = NULL;
        if (FAILED(hr = pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&pVertices)))
        {
            pIB->Unlock();
            SAFE_DELETE_ARRAY(m_pvVectors);
            SAFE_DELETE_ARRAY(m_pTrianglePlanes);
            return hr;
        }
        // calculate vertex size
        DWORD dwVertexSize = D3DXGetFVFVertexSize(pMesh->GetFVF()), ix;
        // erst mal alle Vectoren auslesen
        for (ix = 0; ix < m_dwNumVertices; ix++)
            m_pvVectors[ix] = pVertices[ix].p;
        /*
        // Dreiecksvectoren in Abhängigkeit der Indices 
        D3DINDEXBUFFER_DESC Desc;
        // Die Beschreibung des Index-Buffers abfragen
        pIB->GetDesc(&Desc);
        PWORD pIndex;
        DWORD dwIndexSize = Desc.Format == D3DFMT_INDEX16 ? sizeof(WORD) : sizeof(DWORD);
        */
        D3DXVECTOR3 vTriA, vTriB, vTriC, vTriN, vTemp;
        D3DXPLANE pTemp;
        for (ix = 0; ix < m_dwNumFaces; ix++)
        {
            // Index herausfinden
            vTriA = m_pvVectors[pIndices[3 * ix]];
            vTriB = m_pvVectors[pIndices[3 * ix + 1]];
            vTriC = m_pvVectors[pIndices[3 * ix + 2]];
            
            // Ebene des Dreiecks berechnen
            D3DXPlaneFromPoints(&pTemp, &vTriA, &vTriB, &vTriC);
            D3DXVec3Cross(&vTriN, &D3DXVECTOR3(vTriC - vTriB), &D3DXVECTOR3(vTriA - vTriB));
            D3DXPlaneNormalize(&m_pTrianglePlanes[ix * 4], &pTemp);

            // Die drei Seitenebenen berechnen
            D3DXVec3Cross(&vTemp, &D3DXVECTOR3(vTriA - vTriB), &vTriN);
            D3DXVec3Normalize(&vTemp, &vTemp);
            D3DXPlaneFromPointNormal(&m_pTrianglePlanes[ix * 4 + 1], &vTriA, &vTemp);

            D3DXVec3Cross(&vTemp, &D3DXVECTOR3(vTriB - vTriC), &vTriN);
            D3DXVec3Normalize(&vTemp, &vTemp);
            D3DXPlaneFromPointNormal(&m_pTrianglePlanes[ix * 4 + 2], &vTriB, &vTemp);

            D3DXVec3Cross(&vTemp, &D3DXVECTOR3(vTriC - vTriA), &vTriN);
            D3DXVec3Normalize(&vTemp, &vTemp);
            D3DXPlaneFromPointNormal(&m_pTrianglePlanes[ix * 4 + 3], &vTriC, &vTemp);
        }
        pMesh->UnlockVertexBuffer();

        // Boundingbox-Knoten bilden
        m_pRootNode = new COcTreeNode;
        if (!m_pRootNode)
        {
            pIB->Unlock();
            pIB->Release();
            SAFE_DELETE_ARRAY(m_pvVectors);
            SAFE_DELETE_ARRAY(m_pTrianglePlanes);
            return E_OUTOFMEMORY;
        }
        m_pRootNode->SetBounds(m_BoundingBoxLocal.GetBoundsMin(), 
            m_BoundingBoxLocal.GetBoundsMax());
        // Dreiecke erst mal vollständig auf die Root setzen
        m_pRootNode->SetTriangles(NULL, m_dwNumFaces * 3);
        // Dreiecke aufteilen
        m_pRootNode->SplitNode(m_pvVectors, pIndices, 4);
        // Indexbuffer freigeben
        pIB->Unlock();
        pIB->Release();
    }
    return hr;
}

gut hier wird nix schneller, aber weniger Code, da nun die OcTree-Klasse den ganzen Schnulli übernimmt.
Ich hoffe auch schneller. Aber wie gesagt, beim Laden ist es ja Wurscht, wichtig ist dann das Auslesen des Baumes...

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

5

11.08.2003, 18:48

Du hast vollkommen Recht. Ich habe auch ein paar kleine Dinge schon verbessert. Aber das komplette Teil jetzt umzuschreiben ist mir ehrlich gesagt mit zu viel Chaos verbunden.
In einem zweiten Buch würde ich nochmal komplett von null anfangen was das Design angeht, dann wird alles besser *G*

Anonymous

unregistriert

6

11.08.2003, 19:42

naguddi

OK, David, ich halt ja schon mein Maul.
Aber ganz von Null anfangen brauchst Du ja gar nicht...

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

7

11.08.2003, 20:03

Re: naguddi

Zitat von »"Jens"«

OK, David, ich halt ja schon mein Maul.

Hey, das war ein Missverständnis! Ich denke wirklich, dass Du Recht hast, das sollte keine Ironie sein. Inzwischen code ich anders als im Buch, von daher gefallen mir viele Dinge selbst nicht mehr.
Aber ich habe es jetzt immerhin schon so geändert, dass die Ebenen nicht jedes Mal neu berechnet werden müssen. Die werden jetzt direkt mit im Octree-Node gespeichert. Danke für den Hinweis!!!

Anonymous

unregistriert

8

11.08.2003, 20:39

echt super...

wie schnell das geht...
:-)

Anonymous

unregistriert

9

11.08.2003, 21:11

ja, so wirds

übrigens habe ich nun mal einen Test zum laufen bekommen.
Nun kann ich gleich mal a bissle angeben :-)
Ich habe nun die Änderung vollzogen und vorher 9 fps Geschwindigkeit gehabt.
Mit der Änderung, auch die gesamten Boxen im OcTreeNode zu speichern, komme ich nun auf 12 fps!!!
Ich will nicht sagen, wieviele Objekte sich hier bewegen, angezeigt und auf Kollision geprüft werden...
Es sind nähmlich nur 8 Meshs (die MS Samples) aber immer hin 2 x tyni.x ist dabei. Und übrigens läuft das auf einer 600 MHz-Maschine mit einer 4 MB Intel Corp GCH Grafikkarte :-) (also ohne Beschleunigung)
doll, nich ;-)

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

Werbeanzeige