Program Listing for File mesh_splitter.cpp

Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/physics/mesh_splitter_utils/mesh_splitter.cpp)

#include <physics/mesh_splitter_utils/mesh_splitter.hpp>

namespace legion::physics
{
    void MeshSplitter::InitializePolygons(ecs::entity_handle entity)
    {
        owner = entity;

        auto [meshFilter, meshRenderer] = entity.get_component_handles<rendering::mesh_renderable>();

        ownerMaterialH = meshRenderer.read().material;

        auto [posH, rotH, scaleH] = entity.get_component_handles<transform>();

        if (meshFilter && posH && rotH && scaleH)
        {
            log::debug("Mesh and Transform found");
            std::queue<meshHalfEdgePtr> meshHalfEdges;

            //auto renderable = renderable.read();
            mesh& mesh = meshFilter.read().get().second;

            const math::mat4 transform = math::compose(scaleH.read(), rotH.read(), posH.read());
            //debugHelper.DEBUG_transform = transform;

            HalfEdgeFinder edgeFinder;
            edgeFinder.FindHalfEdge(mesh, transform, meshHalfEdges);

            BFSPolygonize(meshHalfEdges, transform);

            log::debug("Mesh vertices {}, Mesh indices {}", mesh.vertices.size(), mesh.indices.size());

        }
        else
        {
            log::warn("The given entity does not have a meshHandle!");
        }

        for (auto face : meshPolygons)
        {
            for (auto edge : face->GetMeshEdges())
            {
                assert(edge->owner.lock());
                assert(edge->pairingEdge->owner.lock());
            }
        }



    }

    void MeshSplitter::MultipleSplitMesh(const std::vector<MeshSplitParams>& splittingPlanes,
        std::vector<ecs::entity_handle>& entitiesGenerated, bool keepBelow, int debugAt)
    {
        int currentDebug = 0;

        auto [posH, rotH, scaleH] = owner.get_component_handles<transform>();
        const math::mat4& transform = math::compose(scaleH.read(), rotH.read(), posH.read());

        //-------------------------------- copy polygons of original mesh and add it to the output list -----------------------------------------//

        std::vector< std::vector<SplittablePolygonPtr>> outputPolygonIslandsGenerated;

        std::vector<SplittablePolygonPtr> copiedPolygons;
        CopyPolygons(meshPolygons, copiedPolygons);

        outputPolygonIslandsGenerated.push_back(std::move(copiedPolygons));

        //-------------------------------- spllit mesh based on list of splitting planes -----------------------------------------//
        for (const MeshSplitParams& splitParam : splittingPlanes)
        {
            std::vector< std::vector<SplittablePolygonPtr>> inputList = std::move(outputPolygonIslandsGenerated);
            outputPolygonIslandsGenerated.clear();

            for (std::vector<SplittablePolygonPtr>& polygonIsland : inputList)
            {
                for (SplittablePolygonPtr polygon : polygonIsland)
                {
                    polygon->isVisited = false;
                }

                SplitPolygons
                (polygonIsland,
                    splitParam.planeNormal,
                    splitParam.planePostion,
                    transform,
                    outputPolygonIslandsGenerated, keepBelow,currentDebug == debugAt);
            }
            currentDebug++;
        }

        //-------------------------------- use each polygon list to create a new object -----------------------------------------//

        for (auto& polygonIsland : outputPolygonIslandsGenerated)
        {
            PrimitiveMesh newMesh(owner, polygonIsland, ownerMaterialH);
            auto newEnt = newMesh.InstantiateNewGameObject();

            entitiesGenerated.push_back(newEnt);
        }
    }

    void MeshSplitter::SplitPolygons(std::vector<SplittablePolygonPtr>& polygonsToSplit, const math::vec3& planeNormal, const math::vec3& planePosition,
        const math::mat4& transform, std::vector<std::vector<SplittablePolygonPtr>>& resultingIslands, bool keepBelow, bool shouldDebug)
    {

        //log::debug("SplitPolygons");

        //----------------------- Find out which polygons are below,above, or intersecting the splitting plane -----------------------------------//

        for (auto polygon : polygonsToSplit)
        {
            //log::debug("Polygon has {} edges  ", polygon->GetMeshEdges().size());
            polygon->isVisited = false;
            polygon->CalculatePolygonSplit(transform, planePosition, planeNormal, keepBelow);

           /* switch (polygon->GetPolygonSplitState())
            {
            case SplitState::Above:
                log::debug("Above ");
                break;
            case SplitState::Below:
                log::debug("Below ");
                break;
            case SplitState::Split:
                log::debug("Split ");
                break;
            case SplitState::Unknown:
                log::debug("Unknown ");
                break;

            default:
                break;
            }*/

        }

        SplitState requestedState = keepBelow ? SplitState::Below : SplitState::Above;

        SplittablePolygonPtr initialFound = nullptr;

        bool foundUnvisited =
            FindFirstIntersectingOrRequestedState
            (initialFound, requestedState, polygonsToSplit);

        /*if (math::distance(planeNormal, math::vec3(-1, 0, 0)) < 0.01f)
        {
            DebugBreak();
        }*/
        //while there is an unvisited polygon that is on the requestedState or is intersecting the splitting plane
        while (foundUnvisited)
        {
            std::vector<SplittablePolygonPtr> splitMesh;
            std::vector<SplittablePolygonPtr> nonSplitMesh;

            //------------------------ BFS search polygons that are in the same island -------------------------------------------//
            //------------------------ and  divide them into a list of split and nonsplit polygons--------------------------//
            initialFound->isVisited = false;
            BFSFindRequestedAndIntersecting(
                initialFound,
                splitMesh,
                nonSplitMesh, requestedState);


            std::vector < std::vector <meshHalfEdgePtr>> holeIslands;
            std::vector<std::vector<SplittablePolygonPtr>> intersectionIslands;

            //----------------------------------- Detect multiple holes in mesh  --------------------------------------------------//

            DetectIntersectionIsland(splitMesh, intersectionIslands);

            //----------------------------------- Filter Edges in polygon in order to fit sliced mesh --------------------------------------------------//

            std::vector<IntersectionEdgeInfo> generatedIntersectionEdges;
            int indexCurrent = 0;
            int indexToSee = 1;

            /*if (shouldDebug)
            {
                DebugBreak();
            }*/

            for (std::vector<SplittablePolygonPtr>& intersectionIsland : intersectionIslands)
            {

                for (SplittablePolygonPtr islandPolygon : intersectionIsland)
                {
                    SplitPolygon(islandPolygon, transform, planePosition, planeNormal, requestedState, generatedIntersectionEdges, indexCurrent == indexToSee && shouldDebug);

                    if (indexCurrent == indexToSee && shouldDebug)
                    {
                        log::debug("indexCurrent {} ", indexCurrent);
                        log::debug("generatedIntersectionEdges {} ", generatedIntersectionEdges.size());
                    }


                    indexCurrent++;
                }
            }


            math::vec3 localNormal = transform * math::vec4(planeNormal, 0);
            SplittablePolygonPtr intersectionPolygon = CreateIntersectionPolygon(generatedIntersectionEdges, math::normalize(localNormal));
            intersectionPolygon->isVisited = true;
            intersectionPolygon->ResetEdgeVisited();

            float max = generatedIntersectionEdges.size();
            int i = 0;
            for (auto intersectionInfo : generatedIntersectionEdges)
            {

                if (shouldDebug)
                {
                    float interpolant = (float)i / max;
                    math::vec3 color = math::color(1, 0, 1) * interpolant;
                    //log::debug("generatedIntersectionEdges {} ", generatedIntersectionEdges.size());
                   /* math::vec3 first = debugHelper.DEBUG_transform * math::vec4(intersectionInfo.first, 1);
                    math::vec3 second = debugHelper.DEBUG_transform * math::vec4(intersectionInfo.second, 1);*/

                    /*debug::user_projectDrawLine(first + (second-first) * 0.05f, second + (first - second) * 0.05f, math::color(color.x,color.y,color.z,1.0f),
                        math::linearRand(8.0f,12.0f), FLT_MAX, true);*/
                }

                i++;
            }

            //---------------------------------- Add intersecting and nonsplit to primitive mesh ---------------------------------//

            std::vector< SplittablePolygonPtr> resultPolygons;

            resultPolygons.insert(resultPolygons.end()
                , std::make_move_iterator(splitMesh.begin()), std::make_move_iterator(splitMesh.end()));

            resultPolygons.insert(resultPolygons.end()
                , std::make_move_iterator(nonSplitMesh.begin()), std::make_move_iterator(nonSplitMesh.end()));

            if (!generatedIntersectionEdges.empty())
            {
                resultPolygons.push_back(std::move(intersectionPolygon));
            }


            foundUnvisited =
                FindFirstIntersectingOrRequestedState
                (initialFound, requestedState, polygonsToSplit);

            resultingIslands.push_back(std::move(resultPolygons));

        }
    }

    void MeshSplitter::CopyPolygons(std::vector<SplittablePolygonPtr>& originalSplitMesh,
        std::vector<SplittablePolygonPtr>& copySplitMesh)
    {

        //----copy all edges of all polygons and connect them to each edge that is within the same polygon--//
        for (SplittablePolygonPtr originalPolygon : originalSplitMesh)
        {
            originalPolygon->ResetEdgeVisited();

            std::vector<meshHalfEdgePtr> copyPolygonEdges;
            CopyEdgeVector(originalPolygon->GetMeshEdges(), copyPolygonEdges);

            auto copyPolygon = std::make_shared<SplittablePolygon>(copyPolygonEdges, originalPolygon->localNormal);
            copyPolygon->AssignEdgeOwnership();
            copySplitMesh.push_back(copyPolygon);
        }

        //----use shadow edge to connect boundary edges between copied polygons--//

        for (SplittablePolygonPtr originalPolygon : originalSplitMesh)
        {


            for (auto originalEdge : originalPolygon->GetMeshEdges())
            {
                if (originalEdge->isBoundary && originalEdge->pairingEdge)
                {
                    auto shadowEdge = originalEdge->shadowEdge;
                    auto shadowEdgePairing = originalEdge->pairingEdge->shadowEdge;

                    shadowEdge->setPairing(shadowEdgePairing);
                }

            }
        }

        //----nullify shadow edge for each edge in the original polygon list--//

        for (SplittablePolygonPtr originalPolygon : originalSplitMesh)
        {
            originalPolygon->ResetEdgeVisited();

            for (auto originalEdge : originalPolygon->GetMeshEdges())
            {
                originalEdge->shadowEdge = nullptr;
            }
        }
    }

    void MeshSplitter::CopyEdgeVector(std::vector<meshHalfEdgePtr>& originalHalfEdgeList, std::vector<meshHalfEdgePtr>& resultCopyList)
    {
        for (meshHalfEdgePtr originalEdge : originalHalfEdgeList)
        {
            //copy polygon and place on shadow edge
            originalEdge->cloneOnShadowEdge();
        }

        //BFS connect with clone edge
        std::queue<meshHalfEdgePtr> unvisitedOriginalHalfEdgeList;
        unvisitedOriginalHalfEdgeList.push(originalHalfEdgeList[0]);

        while (!unvisitedOriginalHalfEdgeList.empty())
        {
            auto originalEdge = unvisitedOriginalHalfEdgeList.front();
            unvisitedOriginalHalfEdgeList.pop();

            if (!originalEdge->isVisited)
            {
                originalEdge->isVisited = true;

                auto [original1, original2, original3] = originalEdge->getTriangle();
                auto [shadow1, shadow2, shadow3] = originalEdge->getShadowTriangle();

                MeshHalfEdge::connectIntoTriangle(shadow1, shadow2, shadow3);

                if (!original1->isBoundary)
                {
                    shadow1->setPairing(original1->pairingEdge->shadowEdge);
                    unvisitedOriginalHalfEdgeList.push(original1->pairingEdge);
                }

                if (!original2->isBoundary)
                {
                    shadow2->setPairing(original2->pairingEdge->shadowEdge);
                    unvisitedOriginalHalfEdgeList.push(original2->pairingEdge);
                }

                if (!original3->isBoundary)
                {
                    shadow3->setPairing(original3->pairingEdge->shadowEdge);
                    unvisitedOriginalHalfEdgeList.push(original3->pairingEdge);
                }

                shadow1->populateVectorWithTriangle(resultCopyList);

            }
        }
    }

    void MeshSplitter::BFSFindRequestedAndIntersecting(SplittablePolygonPtr& intialPolygon, std::vector<SplittablePolygonPtr>& originalSplitMesh, std::vector<SplittablePolygonPtr>& originalNonSplitMesh, SplitState requestedState)
    {
        std::queue<SplittablePolygonPtr> unvisitedPolygonQueue;
        unvisitedPolygonQueue.push(intialPolygon);

        while (!unvisitedPolygonQueue.empty())
        {
            auto polygonPtr = unvisitedPolygonQueue.front();
            unvisitedPolygonQueue.pop();

            //assert(polygonPtr);

            if (!polygonPtr->isVisited)
            {
                polygonPtr->isVisited = true;

                auto polygonSplitState = polygonPtr->GetPolygonSplitState();

                bool polygonAtRequestedState = polygonSplitState == requestedState;
                bool polygonAtIntersection = polygonSplitState == SplitState::Split;

                //place polygon in correct list

                if (polygonAtRequestedState)
                {
                    originalNonSplitMesh.push_back(polygonPtr);
                    //debugHelper.nonIntersectionPolygons.push_back(polygonPtr->localCentroid);
                }
                else if (polygonAtIntersection)
                {
                    originalSplitMesh.push_back(polygonPtr);
                    //debugHelper.intersectionsPolygons.push_back(polygonPtr->localCentroid);
                }

                //only put it on the unvisited list if polygon is at requested state or is intersecting the splitting plane

                if (polygonAtIntersection || polygonAtRequestedState)
                {
                    for (auto edge : polygonPtr->GetMeshEdges())
                    {
                        bool isBoundary = edge->isBoundary;
                        bool hasPairing = (edge->pairingEdge) != nullptr;

                        if (isBoundary && hasPairing)
                        {
                            auto newPolygon = edge->pairingEdge->owner.lock();
                            //assert(newPolygon);
                            unvisitedPolygonQueue.push(newPolygon);
                        }
                    }
                }
            }
        }
    }

    void MeshSplitter::DetectIntersectionIsland(std::vector<SplittablePolygonPtr>& splitPolygons, std::vector<std::vector<SplittablePolygonPtr>>& intersectionIslands)
    {
        for (auto pol : splitPolygons) { pol->isVisited = false; }

        //find first intersection polygon
        SplittablePolygonPtr initialPolygon;
        bool foundUnvisited = FindFirstUnivistedIntersectionPolygon(splitPolygons, initialPolygon);

        //while can find intersection polygon
        while (foundUnvisited)
        {
            std::vector< math::vec3> DEBUG_ONLY_polygonPositions;
            std::vector< SplittablePolygonPtr> intersectionIsland;
            std::queue< SplittablePolygonPtr> unvisitedPolygons;
            unvisitedPolygons.push(initialPolygon);

            while (!unvisitedPolygons.empty())
            {
                auto polygon = unvisitedPolygons.front();
                unvisitedPolygons.pop();

                if (!polygon->isVisited && polygon->GetPolygonSplitState() == SplitState::Split)
                {
                    polygon->isVisited = true;
                    intersectionIsland.push_back(polygon);
                    DEBUG_ONLY_polygonPositions.push_back(polygon->localCentroid);

                    for (auto edge : polygon->GetMeshEdges())
                    {
                        auto pairingEdge = edge->pairingEdge;

                        if (edge->isBoundary && pairingEdge)
                        {
                            if (auto pairingPolygon = pairingEdge->owner.lock())
                            {
                                unvisitedPolygons.push(pairingPolygon);
                            }

                        }
                    }
                }
            }

            intersectionIslands.push_back(intersectionIsland);
            //debugHelper.intersectionIslands.push_back(DEBUG_ONLY_polygonPositions);

            foundUnvisited = FindFirstUnivistedIntersectionPolygon(splitPolygons, initialPolygon);

        }
    }

    void MeshSplitter::SplitPolygon(SplittablePolygonPtr splitPolygon, const math::mat4& transform, const math::vec3 cutPosition,
        const math::vec3 cutNormal, SplitState requestedState, std::vector<IntersectionEdgeInfo>& generatedIntersectionEdges, bool shouldDebug)
    {
        IntersectingPolygonOrganizer polygonOrganizer;
        polygonOrganizer.SplitPolygon(splitPolygon, transform, cutPosition, cutNormal, requestedState, generatedIntersectionEdges,shouldDebug);
    }

    void MeshSplitter::DEBUG_DrawPolygonData(const math::mat4& transform)
    {
        for (auto polygon : meshPolygons)
        {
            polygon->DEBUG_drawEdgeBoundaryInset(transform);
        }

    }

}