// board edges and cutouts
static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
{
    CPOLYGONS_LIST  bufferPcbOutlines;      // stores the board main outlines
    CPOLYGONS_LIST  allLayerHoles;          // Contains through holes, calculated only once

    allLayerHoles.reserve( 20000 );

    // Build a polygon from edge cut items
    wxString msg;

    if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines,
                allLayerHoles, &msg ) )
    {
        msg << wxT( "\n\n" ) <<
        _( "Unable to calculate the board outlines;\n"
           "fall back to using the board boundary box." );
        wxMessageBox( msg );
    }

    double  scale = aModel.scale;

    int i = 0;
    int seg;

    // deal with the solid outlines
    int nvert = bufferPcbOutlines.GetCornersCount();

    while( i < nvert )
    {
        seg = aModel.board.NewContour();

        if( seg < 0 )
        {
            msg << wxT( "\n\n" ) <<
                _( "VRML Export Failed:\nCould not add outline to contours." );
            wxMessageBox( msg );

            return;
        }

        while( i < nvert )
        {
            if( bufferPcbOutlines[i].end_contour )
                break;

            aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale,
                    -(bufferPcbOutlines[i].y * scale ) );

            ++i;
        }

        aModel.board.EnsureWinding( seg, false );
        ++i;
    }

    // deal with the holes
    nvert = allLayerHoles.GetCornersCount();

    i = 0;
    while( i < nvert )
    {
        seg = aModel.holes.NewContour();

        if( seg < 0 )
        {
            msg << wxT( "\n\n" ) <<
            _( "VRML Export Failed:\nCould not add holes to contours." );
            wxMessageBox( msg );

            return;
        }

        while( i < nvert )
        {
            if( allLayerHoles[i].end_contour )
                break;

            aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale,
                                    -(allLayerHoles[i].y * scale ) );

            ++i;
        }

        aModel.holes.EnsureWinding( seg, true );
        ++i;
    }
}
/**
 * Function buildBoard3DAuxLayers
 * Called by CreateDrawGL_List()
 * Fills the OpenGL GL_ID_BOARD draw list with items
 * on aux layers only
 */
void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* aActivity )
{
    const int   segcountforcircle   = 18;
    double      correctionFactor    = 1.0 / cos( M_PI / (segcountforcircle * 2) );
    BOARD*      pcb = GetBoard();

    CPOLYGONS_LIST  bufferPolys;

    bufferPolys.reserve( 5000 );    // Reserve for items not on board

    static const LAYER_ID sequence[] = {
        Dwgs_User,
        Cmts_User,
        Eco1_User,
        Eco2_User,
        Edge_Cuts,
        Margin
    };

    for( LSEQ aux( sequence, sequence+DIM(sequence) );  aux;  ++aux )
    {
        LAYER_ID layer = *aux;

        if( !is3DLayerEnabled( layer ) )
            continue;

        if( aActivity )
            aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) );

        bufferPolys.RemoveAllContours();

        for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
        {
            if( !item->IsOnLayer( layer ) )
                continue;

            switch( item->Type() )
            {
            case PCB_LINE_T:
                ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
                    bufferPolys, 0, segcountforcircle, correctionFactor );
                break;

            case PCB_TEXT_T:
                ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
                    bufferPolys, 0, segcountforcircle, correctionFactor );
                break;

            default:
                break;
            }
        }

        for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
        {
            module->TransformPadsShapesWithClearanceToPolygon( layer,
                                                               bufferPolys,
                                                               0,
                                                               segcountforcircle,
                                                               correctionFactor );

            module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
                                                                     bufferPolys,
                                                                     0,
                                                                     segcountforcircle,
                                                                     correctionFactor );
        }

        // bufferPolys contains polygons to merge. Many overlaps .
        // Calculate merged polygons and remove pads and vias holes
        if( bufferPolys.GetCornersCount() == 0 )
            continue;
        KI_POLYGON_SET  currLayerPolyset;
        KI_POLYGON_SET  polyset;
        bufferPolys.ExportTo( polyset );
        currLayerPolyset += polyset;

        int         thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
        int         zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );
        // for Draw3D_SolidHorizontalPolyPolygons,
        // zpos it the middle between bottom and top sides.
        // However for top layers, zpos should be the bottom layer pos,
        // and for bottom layers, zpos should be the top layer pos.
        if( Get3DLayer_Z_Orientation( layer ) > 0 )
            zpos += thickness/2;
        else
            zpos -= thickness/2 ;

        bufferPolys.RemoveAllContours();
        bufferPolys.ImportFrom( currLayerPolyset );

        float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)

        // If we are not using thickness, then the znormal must face the layer direction
        // because it will draw just one plane
        if( !thickness )
            zNormal = Get3DLayer_Z_Orientation( layer );

        setGLTechLayersColor( layer );
        Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos,
                                            thickness, GetPrm3DVisu().m_BiuTo3Dunits, false,
                                            zNormal );
    }
}