Example #1
0
void OptimizeIndices(ModelSubGeometryLodLevel* subGeom, ModelVertexBuffer* vb, ModelIndexBuffer* ib)
{
    PODVector<Triangle> oldTriangles;
    PODVector<Triangle> newTriangles;

    if (subGeom->indexCount_ % 3)
    {
        PrintLine("Index count is not divisible by 3, skipping index optimization");
        return;
    }

    for (unsigned i = 0; i < vb->vertices_.Size(); ++i)
    {
        vb->vertices_[i].useCount_ = 0;
        vb->vertices_[i].cachePosition_ = -1;
    }

    for (unsigned i = subGeom->indexStart_; i < subGeom->indexStart_ + subGeom->indexCount_; i += 3)
    {
        Triangle triangle;
        triangle.v0_ = ib->indices_[i];
        triangle.v1_ = ib->indices_[i + 1];
        triangle.v2_ = ib->indices_[i + 2];
        vb->vertices_[triangle.v0_].useCount_++;
        vb->vertices_[triangle.v1_].useCount_++;
        vb->vertices_[triangle.v2_].useCount_++;
        oldTriangles.Push(triangle);
    }

    for (unsigned i = 0; i < vb->vertices_.Size(); ++i)
        CalculateScore(vb->vertices_[i]);

    PODVector<unsigned> vertexCache;

    while (oldTriangles.Size())
    {
        unsigned bestTriangle = M_MAX_UNSIGNED;
        float bestTriangleScore = -1.0f;

        // Find the best triangle at this point
        for (unsigned i = 0; i < oldTriangles.Size(); ++i)
        {
            Triangle& triangle = oldTriangles[i];
            float triangleScore =
                vb->vertices_[triangle.v0_].score_ +
                vb->vertices_[triangle.v1_].score_ +
                vb->vertices_[triangle.v2_].score_;

            if (triangleScore > bestTriangleScore)
            {
                bestTriangle = i;
                bestTriangleScore = triangleScore;
            }
        }

        if (bestTriangle == M_MAX_UNSIGNED)
        {
            PrintLine("Could not find next triangle, aborting index optimization");
            return;
        }

        // Add the best triangle
        Triangle triangleCopy = oldTriangles[bestTriangle];
        newTriangles.Push(triangleCopy);
        oldTriangles.Erase(oldTriangles.Begin() + bestTriangle);

        // Reduce the use count
        vb->vertices_[triangleCopy.v0_].useCount_--;
        vb->vertices_[triangleCopy.v1_].useCount_--;
        vb->vertices_[triangleCopy.v2_].useCount_--;

        // Model the LRU cache behaviour
        // Erase the triangle vertices from the middle of the cache, if they were there
        for (unsigned i = 0; i < vertexCache.Size(); ++i)
        {
            if ((vertexCache[i] == triangleCopy.v0_) ||
                    (vertexCache[i] == triangleCopy.v1_) ||
                    (vertexCache[i] == triangleCopy.v2_))
            {
                vertexCache.Erase(vertexCache.Begin() + i);
                --i;
            }
        }

        // Then push them to the front
        vertexCache.Insert(vertexCache.Begin(), triangleCopy.v0_);
        vertexCache.Insert(vertexCache.Begin(), triangleCopy.v1_);
        vertexCache.Insert(vertexCache.Begin(), triangleCopy.v2_);

        // Update positions & scores of all vertices in the cache
        // Give position -1 if vertex is going to be erased
        for (unsigned i = 0; i < vertexCache.Size(); ++i)
        {
            ModelVertex& vertex = vb->vertices_[vertexCache[i]];
            if (i >= VERTEX_CACHE_SIZE)
                vertex.cachePosition_ = -1;
            else
                vertex.cachePosition_ = i;
            CalculateScore(vertex);
        }

        // Finally erase the extra vertices
        if (vertexCache.Size() > VERTEX_CACHE_SIZE)
            vertexCache.Resize(VERTEX_CACHE_SIZE);
    }

    // Rewrite the index data now
    unsigned i = subGeom->indexStart_;
    for (unsigned j = 0; j < newTriangles.Size(); ++j)
    {
        ib->indices_[i++] = newTriangles[j].v0_;
        ib->indices_[i++] = newTriangles[j].v1_;
        ib->indices_[i++] = newTriangles[j].v2_;
    }
}