⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wmlcreateclodmesh.cpp

📁 3D Game Engine Design Source Code非常棒
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// Magic Software, Inc.
// http://www.magic-software.com
// http://www.wild-magic.com
// Copyright (c) 2003.  All Rights Reserved
//
// The Wild Magic Library (WML) source code is supplied under the terms of
// the license agreement http://www.magic-software.com/License/WildMagic.pdf
// and may not be copied or disclosed except in accordance with the terms of
// that agreement.

#include "WmlCreateClodMesh.h"
using namespace Wml;

#include <fstream>
#include <stack>
using namespace std;

//----------------------------------------------------------------------------
CreateClodMesh::CreateClodMesh (int iVQuantity, Vector3f* akVertex,
    Vector3f* akNormal, ColorRGB* akColor, Vector2f* akTexture,
    int iTQuantity, int* aiConnect, int& riCQuantity,
    CollapseRecord*& rakCRecord)
{
    // Hang onto these to avoid having to pass them through member function
    // calls.
    m_iVQuantity = iVQuantity;
    m_akVertex = akVertex;
    m_akNormal = akNormal;
    m_akColor = akColor;
    m_akTexture = akTexture;
    m_iTQuantity = iTQuantity;
    m_aiConnect = aiConnect;

    // for reordering vertices and triangles
    m_iVCurrent = m_iVQuantity-1;
    m_iTCurrent = m_iTQuantity-1;
    m_aiVOrdered = new int[m_iVQuantity];
    m_aiVPermute = new int[m_iVQuantity];
    m_aiNewConnect = new int[3*m_iTQuantity];

    // Insert the triangles into the mesh.  The triangle indices are attached
    // as extra data.
    m_bCollapsing = false;
    for (int i = 0; i < m_iTQuantity; i++)
    {
        int iV0 = aiConnect[3*i];
        int iV1 = aiConnect[3*i+1];
        int iV2 = aiConnect[3*i+2];
        assert( iV0 != iV1 && iV0 != iV2 && iV1 != iV2 );
        Triangle kT(aiConnect[3*i],aiConnect[3*i+1],aiConnect[3*i+2]);
        InsertTriangle(kT);
        *(int*)GetData(kT) = i;
    }

    assert( (int)(m_kVMap.size()) == m_iVQuantity );
    assert( (int)(m_kTMap.size()) == m_iTQuantity );

    InitializeHeap();

    m_bCollapsing = true;
    while ( m_iHQuantity > 0 )
    {
        if ( m_apkHeap[0]->m_fMetric == FLT_MAX )
        {
            // all remaining heap elements have infinite weight
            FlushVertices();
            FlushTriangles();
            break;
        }

        DoCollapse();

        assert( (int)(m_kVMap.size()) == m_iVCurrent+1 );
        assert( (int)(m_kTMap.size()) == m_iTCurrent+1 );
    }
    m_bCollapsing = false;

    // Permute the vertices and triangle connectivity so that the last
    // vertex/triangle in the array is the first vertex/triangle to be
    // removed.
    Reorder();

    // The collapse records store the incremental changes that are used for
    // dynamic LOD changes in the caller of this constructor.
    ComputeRecords(riCQuantity,rakCRecord);
}
//----------------------------------------------------------------------------
CreateClodMesh::~CreateClodMesh ()
{
    RemoveAllTriangles();
    delete[] m_apkHeap;
    delete[] m_aiVOrdered;
    delete[] m_aiVPermute;
    delete[] m_aiNewConnect;
}
//----------------------------------------------------------------------------
void CreateClodMesh::DoCollapse ()
{
    // Define a 2-edge to be an edge that has exactly two triangles sharing
    // it.  An edge is collapsible if it is a 2-edge and has at least one end
    // point whose sharing edges are all 2-edges.  In this case, such an end
    // point will be the 'throw' vertex.  This keeps the boundary and junction
    // edges from changing geometry and helps preserve the shape of the mesh.
    // The topology is always guaranteed not to change.

    // When this function is called, the metric has already been calculated
    // and is finite (so exactly two triangles must be sharing this edge).
    assert( m_apkHeap[0]->m_fMetric < FLT_MAX );
    Edge kEdge = m_apkHeap[0]->m_kEdge;

    // test end points to see if either has only 2-edges sharing it
    int i;
    for (i = 0; i < 2; i++)
    {
        const SmallSet<Edge>* pkESet = GetEdges(kEdge.m_aiV[i]);
        int j;
        for (j = 0; j < pkESet->GetSize(); j++)
        {
            MECIter pkEM = m_kEMap.find((*pkESet)[j]);
            assert( pkEM != m_kEMap.end() );
            if ( pkEM->second.m_kTSet.GetSize() != 2 )
                break;
        }

        if ( j == pkESet->GetSize() )
        {
            // all edges sharing this end point are 2-edges
            break;
        }
    }

    if ( i < 2 )
    {
        int iVThrow = kEdge.m_aiV[i];
        int iVKeep = kEdge.m_aiV[1-i];
        if ( !CollapseCausesFolding(iVKeep,iVThrow) )
        {
            Remove();
            CollapseEdge(iVKeep,iVThrow);
            return;
        }
    }

    // edge not collapsible, assign it infinite weight and update the heap
    Update(0,FLT_MAX);
}
//----------------------------------------------------------------------------
bool CreateClodMesh::CollapseCausesFolding (int iVKeep, int iVThrow)
{
    MVIter pkVT = m_kVMap.find(iVThrow);
    assert( pkVT != m_kVMap.end() );

    Edge kCollapse(iVKeep,iVThrow);
    for (int j = 0; j < pkVT->second.m_kTSet.GetSize(); j++)
    {
        Triangle kT = pkVT->second.m_kTSet[j];
        if ( kCollapse == Edge(kT.m_aiV[0],kT.m_aiV[1])
        ||   kCollapse == Edge(kT.m_aiV[1],kT.m_aiV[2])
        ||   kCollapse == Edge(kT.m_aiV[2],kT.m_aiV[0]) )
        {
            // This triangle would be removed in a collapse, so it does not
            // contribute to any folding.
            continue;
        }

        for (int i = 0; i < 3; i++)
        {
            if ( kT.m_aiV[i] == iVThrow )
            {
                // Test if potential replacement triangle (either ordering)
                // is in the mesh.
                int iV0 = iVKeep;
                int iV1 = kT.m_aiV[(i+1)%3];
                int iV2 = kT.m_aiV[(i+2)%3];

                if ( m_kTMap.find(Triangle(iV0,iV1,iV2)) != m_kTMap.end()
                ||   m_kTMap.find(Triangle(iV0,iV2,iV1)) != m_kTMap.end() )
                {
                    return true;
                }
            }
        }
    }

    return false;
}
//----------------------------------------------------------------------------
float CreateClodMesh::GetMetric (MEIter pkE)
{
    const float fLengthWeight = 10.0f;
    const float fAngleWeight = 1.0f;

    // Compute the metric for the edge.  Only manifold edges (exactly two
    // triangles sharing the edge) are allowed to collapse.
    if ( pkE->second.m_kTSet.GetSize() == 2 )
    {
        // length contribution
        Vector3f& rkEnd0 = m_akVertex[pkE->first.m_aiV[0]];
        Vector3f& rkEnd1 = m_akVertex[pkE->first.m_aiV[1]];
        Vector3f kDiff = rkEnd1 - rkEnd0;
        float fMetric = fLengthWeight*kDiff.Length();

        // angle/area contribution
        Triangle kT = pkE->second.m_kTSet[0];
        Vector3f kV0 = m_akVertex[kT.m_aiV[0]];
        Vector3f kV1 = m_akVertex[kT.m_aiV[1]];
        Vector3f kV2 = m_akVertex[kT.m_aiV[2]];
        Vector3f kE0 = kV1 - kV0;
        Vector3f kE1 = kV2 - kV0;
        Vector3f kN0 = kE0.Cross(kE1);

        kT = pkE->second.m_kTSet[1];
        kV0 = m_akVertex[kT.m_aiV[0]];
        kV1 = m_akVertex[kT.m_aiV[1]];
        kV2 = m_akVertex[kT.m_aiV[2]];
        kE0 = kV1 - kV0;
        kE1 = kV2 - kV0;
        Vector3f kN1 = kE0.Cross(kE1);

        Vector3f kCross = kN0.Cross(kN1);
        fMetric += fAngleWeight*kCross.Length();

        return fMetric;
    }

    // Boundary edges (one triangle containing edge) and junction edges
    // (3 or more triangles sharing edge) are not allowed to collapse.
    return FLT_MAX;
}
//----------------------------------------------------------------------------
void CreateClodMesh::RemoveTriangle (const Triangle& rkT)
{
    // If the triangle is an original one, reorder the connectivity array so
    // that the triangle occurs at the end.
    int iTIndex = *(int*)GetData(rkT);
    if ( iTIndex >= 0 )
    {
        assert( m_iTCurrent >= 0 );
        m_aiNewConnect[3*m_iTCurrent] = m_aiConnect[3*iTIndex];
        m_aiNewConnect[3*m_iTCurrent+1] = m_aiConnect[3*iTIndex+1];
        m_aiNewConnect[3*m_iTCurrent+2] = m_aiConnect[3*iTIndex+2];
        m_iTCurrent--;
    }

    VETMesh::RemoveTriangle(rkT);
}
//----------------------------------------------------------------------------
void CreateClodMesh::ModifyTriangle (Triangle& rkT, int iVKeep, int iVThrow)
{
#ifdef _DEBUG
    int iTStart = (int)m_kTMap.size();
#endif

    // Get the index of the pre-modified triangle, then remove the triangle
    // from the mesh.
    int iTIndex = *(int*)GetData(rkT);
    VETMesh::RemoveTriangle(rkT);

    // replace 'throw' by 'keep'
    for (int i = 0; i < 3; i++)
    {
        if ( rkT.m_aiV[i] == iVThrow )
        {
            rkT.m_aiV[i] = iVKeep;
            break;
        }
    }

    // Indices on modified triangles are the same as the indices on the
    // pre-modified triangles.
    InsertTriangle(rkT);
    *(int*)GetData(rkT) = iTIndex;

#ifdef _DEBUG
    int iTFinal = (int)m_kTMap.size();
    assert( iTFinal == iTStart );
#endif
}
//----------------------------------------------------------------------------
void CreateClodMesh::CollapseEdge (int iVKeep, int iVThrow)
{
#ifdef _DEBUG
    int iVStart = (int)m_kVMap.size();
    int iTStart = (int)m_kTMap.size();
    assert( iVStart > 0 && iTStart > 0 );
#endif

    // find the edge to collapse
    Edge kCollapse(iVKeep,iVThrow);
    MEIter pkEM = m_kEMap.find(kCollapse);
    assert ( pkEM != m_kEMap.end() );

    // keep track of vertices that are deleted in the collapse
    m_kVDelete.clear();

    // Remove the collapse-edge-shared triangles.  Using a copy of the
    // triangle set from the collapse edge is required since removal of the
    // last triangle sharing the collapse edge will remove that edge from
    // the edge map, thereby invalidating any iterator that points to data
    // in the collapse edge.
    SmallSet<Triangle> kTSet = pkEM->second.m_kTSet;
    int iTDeletions = kTSet.GetSize();
    assert( iTDeletions > 0 );
    int j;
    for (j = 0; j < kTSet.GetSize(); j++)
        RemoveTriangle(kTSet[j]);

    // Replace 'throw' vertices by 'keep' vertices in the remaining triangles
    // at the 'throw' vertex.  The old triangles are removed and the modified
    // triangles are inserted.
    Triangle kT;
    MVIter pkVM = m_kVMap.find(iVThrow);
    if ( pkVM != m_kVMap.end() )
    {
        kTSet = pkVM->second.m_kTSet;
        for (j = 0; j < kTSet.GetSize(); j++)
        {
            // The explicit typecast is needed for version 2.96 of g++ that
            // ships with Red Hat Linux 7.0.  The compiler incorrectly
            // complains that *pkTS is 'const Wml::VETMesh::Triangle'
            // when in fact it is 'Wml::VETMesh::Triangle'.
            //
            // TO DO.  I removed the typecast.  Check g++ again.
            kT = kTSet[j];
            ModifyTriangle(kT,iVKeep,iVThrow);
        }
    }

    // The set of potentially modified edges consists of all those edges that
    // are shared by the triangles containing the 'keep' vertex.  Modify these
    // metrics and update the heap.
    set<Edge> kModified;
    const SmallSet<Triangle>* pkTSet = GetTriangles(iVKeep);
    if ( pkTSet )
    {
        kTSet = *pkTSet;
        for (j = 0; j < kTSet.GetSize(); j++)
        {
            kT = kTSet[j];
            kModified.insert(Edge(kT.m_aiV[0],kT.m_aiV[1]));
            kModified.insert(Edge(kT.m_aiV[1],kT.m_aiV[2]));
            kModified.insert(Edge(kT.m_aiV[2],kT.m_aiV[0]));
        }

        set<Edge>::iterator pkES;
        for (pkES = kModified.begin(); pkES != kModified.end(); pkES++)
        {
            MEIter pkEM = m_kEMap.find(*pkES);
            HeapRecord* pkRecord = (HeapRecord*)pkEM->second.m_pvData;
            float fMetric = GetMetric(pkEM);
            Update(pkRecord->m_iHIndex,fMetric);
        }
    }

#ifdef _DEBUG
    int iVFinal = (int)m_kVMap.size();
    int iVDiff = iVStart - iVFinal;
    int iTFinal = (int)m_kTMap.size();
    int iTDiff = iTStart - iTFinal;
    assert( iVDiff == (int)(m_kVDelete.size()) && iTDiff == iTDeletions );
#endif

    // save vertex reordering information
    set<int>::iterator pkVS;
    for (pkVS = m_kVDelete.begin(); pkVS != m_kVDelete.end(); pkVS++)
    {
        assert( 0 <= m_iVCurrent && m_iVCurrent < m_iVQuantity );

        int iV = *pkVS;
        assert( 0 <= iV && iV < m_iVQuantity );

        m_aiVOrdered[m_iVCurrent] = iV;
        m_aiVPermute[iV] = m_iVCurrent;
        m_iVCurrent--;
    }

    // Save the collapse information for use in constructing the final
    // collapse records for the caller of the constructor of this class.
    CollapseRecord kCR(iVKeep,iVThrow,(int)m_kVDelete.size(),iTDeletions);
    m_kEDelete.push_back(kCR);
}
//----------------------------------------------------------------------------
void CreateClodMesh::FlushVertices ()
{
    for (MVCIter pkV = m_kVMap.begin(); pkV != m_kVMap.end(); pkV++)
    {
        assert( 0 <= m_iVCurrent && m_iVCurrent < m_iVQuantity );

        int iV = pkV->first;
        assert( 0 <= iV && iV < m_iVQuantity );

        m_aiVOrdered[m_iVCurrent] = iV;
        m_aiVPermute[iV] = m_iVCurrent;
        m_iVCurrent--;
    }

    assert( m_iVCurrent == -1 );
}
//----------------------------------------------------------------------------
void CreateClodMesh::FlushTriangles ()
{
    for (MTCIter pkT = m_kTMap.begin(); pkT != m_kTMap.end(); pkT++)
    {
        assert( pkT->second.m_pvData != NULL );
        int iTIndex = *(int*)(pkT->second.m_pvData);
        if ( iTIndex >= 0 )
        {
            assert( m_iTCurrent >= 0 );
            m_aiNewConnect[3*m_iTCurrent] = m_aiConnect[3*iTIndex];
            m_aiNewConnect[3*m_iTCurrent+1] = m_aiConnect[3*iTIndex+1];
            m_aiNewConnect[3*m_iTCurrent+2] = m_aiConnect[3*iTIndex+2];
            m_iTCurrent--;
        }
    }

    assert( m_iTCurrent == -1 );
}
//----------------------------------------------------------------------------
void CreateClodMesh::Reorder ()
{
    // permute the vertices and copy to the original array
    Vector3f* akNewVertex = new Vector3f[m_iVQuantity];
    int i;
    for (i = 0; i < m_iVQuantity; i++)
        akNewVertex[i] = m_akVertex[m_aiVOrdered[i]];
    memcpy(m_akVertex,akNewVertex,m_iVQuantity*sizeof(Vector3f));
    delete[] akNewVertex;

    // permute the normal vectors (if any)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -