/*
 * Function Draw3D_ZaxisOblongCylinder:
 * draw a segment with an oblong hole.
 * Used to draw oblong holes
 * If aHeight = height of the cylinder is 0, only one ring will be drawn
 * If aThickness = 0, only one cylinder will be drawn
 */
void Draw3D_ZaxisOblongCylinder( wxPoint aAxis1Pos, wxPoint aAxis2Pos,
                                 int aRadius, int aHeight, int aThickness,
                                 int aZpos, double aBiuTo3DUnits  )
{
    const int slice = SEGM_PER_CIRCLE;

    // Build the points to approximate oblong cylinder by segments
    CPOLYGONS_LIST outer_cornerBuffer;

    int segm_width = (aRadius * 2) + aThickness;
    TransformRoundedEndsSegmentToPolygon( outer_cornerBuffer, aAxis1Pos,
                                          aAxis2Pos, slice, segm_width );

    // Draw the oblong outer cylinder
    if( aHeight )
        Draw3D_VerticalPolygonalCylinder( outer_cornerBuffer, aHeight, aZpos,
                                          false, aBiuTo3DUnits );

    if( aThickness )
    {
        CPOLYGONS_LIST inner_cornerBuffer;
        segm_width = aRadius * 2;
        TransformRoundedEndsSegmentToPolygon( inner_cornerBuffer, aAxis1Pos,
                                              aAxis2Pos, slice, segm_width );

        // Draw the oblong inner cylinder
        if( aHeight )
            Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer, aHeight,
                                              aZpos, true, aBiuTo3DUnits );

        // Build the horizontal full polygon shape
        // (outer polygon shape - inner polygon shape)
        outer_cornerBuffer.Append( inner_cornerBuffer );

        CPOLYGONS_LIST polygon;
        ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );

        // draw top (front) horizontal side (ring)
        SetNormalZpos();
        Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits, false );

        if( aHeight )
        {
            // draw bottom (back) horizontal side (ring)
            SetNormalZneg();
            Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, 0, aBiuTo3DUnits, false );
        }
    }

    SetNormalZpos();
}
/* draw a cylinder (a tube) using 3D primitives.
 * the cylinder axis is parallel to the Z axis
 * If aHeight = height of the cylinder is 0, only one ring will be drawn
 * If aThickness = 0, only one cylinder will be drawn
 */
void Draw3D_ZaxisCylinder( wxPoint aCenterPos, int aRadius,
                           int aHeight, int aThickness,
                           int aZpos, double aBiuTo3DUnits )
{
    const int slice = SEGM_PER_CIRCLE;
    CPOLYGONS_LIST outer_cornerBuffer;

    TransformCircleToPolygon( outer_cornerBuffer, aCenterPos,
                              aRadius + (aThickness / 2), slice );

    std::vector<S3D_VERTEX> coords;
    coords.resize( 4 );

    CPOLYGONS_LIST inner_cornerBuffer;
    if( aThickness )    // build the the vertical inner polygon (hole)
        TransformCircleToPolygon( inner_cornerBuffer, aCenterPos,
                                  aRadius - (aThickness / 2), slice );

    if( aHeight )
    {
        // Draw the vertical outer side
        Draw3D_VerticalPolygonalCylinder( outer_cornerBuffer,
                                          aHeight, aZpos, false, aBiuTo3DUnits );
        if( aThickness )
            // Draws the vertical inner side (hole)
            Draw3D_VerticalPolygonalCylinder( inner_cornerBuffer,
                                              aHeight, aZpos, true, aBiuTo3DUnits );
    }

    if( aThickness )
    {
        // draw top (front) and bottom (back) horizontal sides (rings)
        SetNormalZpos();
        outer_cornerBuffer.Append( inner_cornerBuffer );
        CPOLYGONS_LIST polygon;

        ConvertPolysListWithHolesToOnePolygon( outer_cornerBuffer, polygon );
        // draw top (front) horizontal ring
        Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos + aHeight, 0, aBiuTo3DUnits, false );

        if( aHeight )
        {
            // draw bottom (back) horizontal ring
            SetNormalZneg();
            Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, 0, aBiuTo3DUnits, false );
        }
    }

    SetNormalZpos();
}
/* draw the solid polygon found in aPolysList
 * The first polygon is the main polygon, others are holes
 * See Draw3D_SolidHorizontalPolyPolygons for more info
 */
void Draw3D_SolidHorizontalPolygonWithHoles( const CPOLYGONS_LIST& aPolysList,
        int aZpos, int aThickness,
        double aBiuTo3DUnits, bool aUseTextures )
{
    CPOLYGONS_LIST polygon;

    ConvertPolysListWithHolesToOnePolygon( aPolysList, polygon );
    Draw3D_SolidHorizontalPolyPolygons( polygon, aZpos, aThickness, aBiuTo3DUnits, aUseTextures );
}
/* draw a thick segment using 3D primitives, in a XY plane
 * wxPoint aStart, wxPoint aEnd = YX position of end in board units
 * aWidth = width of segment in board units
 * aThickness = thickness of segment in board units
 * aZpos = z position of segment in board units
 */
void Draw3D_SolidSegment( const wxPoint& aStart, const wxPoint& aEnd,
                          int aWidth, int aThickness, int aZpos, double aBiuTo3DUnits )
{
    CPOLYGONS_LIST   cornerBuffer;
    const int               slice = SEGM_PER_CIRCLE;

    TransformRoundedEndsSegmentToPolygon( cornerBuffer, aStart, aEnd, slice, aWidth );

    Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits, false );
}
void Draw3D_ArcSegment( const wxPoint&  aCenterPos, const wxPoint& aStartPoint,
                        double aArcAngle, int aWidth, int aThickness,
                        int aZpos, double aBiuTo3DUnits )
{
    const int   slice = SEGM_PER_CIRCLE;

    CPOLYGONS_LIST cornerBuffer;
    TransformArcToPolygon( cornerBuffer, aCenterPos, aStartPoint, aArcAngle,
                           slice, aWidth );

    Draw3D_SolidHorizontalPolyPolygons( cornerBuffer, aZpos, aThickness, aBiuTo3DUnits, false );
}
void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* aActivity )
{
    BOARD* pcb = GetBoard();
    bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES );

    // Number of segments to draw a circle using segments
    const int       segcountforcircle   = 18;
    double          correctionFactor    = 1.0 / cos( M_PI / (segcountforcircle * 2) );
    const int       segcountLowQuality  = 12;   // segments to draw a circle with low quality
                                                // to reduce time calculations
                                                // for holes and items which do not need
                                                // a fine representation

    double          correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2) );

    // segments to draw a circle to build texts. Is is used only to build
    // the shape of each segment of the stroke font, therefore no need to have
    // many segments per circle.
    const int       segcountInStrokeFont = 8;

    SHAPE_POLY_SET bufferPolys;
    SHAPE_POLY_SET allLayerHoles;              // Contains through holes, calculated only once
    SHAPE_POLY_SET bufferPcbOutlines;          // stores the board main outlines

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

    if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) )
    {
        if( aErrorMessages )
        {
            msg << wxT("\n") <<
                _("Unable to calculate the board outlines.\n"
                  "Therefore use the board boundary box.") << wxT("\n\n");
            aErrorMessages->Report( msg, REPORTER::RPT_WARNING );
        }
    }

    // Build board holes, with no optimization of large holes shape.
    buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, false );

    // draw graphic items, on technical layers

    static const LAYER_ID teckLayerList[] = {
        B_Adhes,
        F_Adhes,
        B_Paste,
        F_Paste,
        B_SilkS,
        F_SilkS,
        B_Mask,
        F_Mask,
    };

    // User layers are not drawn here, only technical layers
    for( LSEQ seq = LSET::AllTechMask().Seq( teckLayerList, DIM( teckLayerList ) );  seq;  ++seq )
    {
        LAYER_ID layer = *seq;

        if( !is3DLayerEnabled( layer ) )
            continue;

        if( layer == Edge_Cuts && isEnabled( FL_SHOW_BOARD_BODY )  )
            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, segcountLowQuality, 1.0 );
                break;

            default:
                break;
            }
        }

        for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
        {
            if( layer == F_SilkS || layer == B_SilkS )
            {
                // On silk screen layers, the pad shape is only the pad outline
                // never a filled shape
                D_PAD*  pad = module->Pads();
                int     linewidth = g_DrawDefaultLineThickness;

                for( ; pad; pad = pad->Next() )
                {
                    if( !pad->IsOnLayer( layer ) )
                        continue;

                    buildPadShapeThickOutlineAsPolygon( pad, bufferPolys,
                            linewidth, segcountforcircle, correctionFactor );
                }
            }
            else
                module->TransformPadsShapesWithClearanceToPolygon( layer,
                        bufferPolys, 0, segcountforcircle, correctionFactor );

            // On tech layers, use a poor circle approximation, only for texts (stroke font)
            module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
                    bufferPolys, 0, segcountforcircle, correctionFactor, segcountInStrokeFont );
        }

        // Draw non copper zones
        if( isEnabled( FL_ZONE ) )
        {
            for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
            {
                ZONE_CONTAINER* zone = pcb->GetArea( ii );

                if( !zone->IsOnLayer( layer ) )
                    continue;

                zone->TransformSolidAreasShapesToPolygonSet(
                        bufferPolys, segcountLowQuality, correctionFactorLQ );
            }
        }

        // bufferPolys contains polygons to merge. Many overlaps .
        // Calculate merged polygons and remove pads and vias holes
        if( bufferPolys.IsEmpty() )
            continue;

        // Solder mask layers are "negative" layers.
        // Shapes should be removed from the full board area.
        if( layer == B_Mask || layer == F_Mask )
        {
            SHAPE_POLY_SET cuts = bufferPolys;
            bufferPolys = bufferPcbOutlines;

            cuts.Append(allLayerHoles);
            cuts.Simplify();

            bufferPolys.BooleanSubtract( cuts );
        }
        // Remove holes from Solder paste layers and silkscreen
        else if( layer == B_Paste || layer == F_Paste
                 || layer == B_SilkS || layer == F_SilkS  )
        {
            bufferPolys.BooleanSubtract( allLayerHoles );
        }

        int thickness = 0;

        if( layer != B_Mask && layer != F_Mask )
            thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );

        int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );

        if( layer == Edge_Cuts )
        {
            thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu )
                        - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
            zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu )
                   + (thickness / 2);
        }
        else
        {
            // 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 ;
        }


        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, useTextures,
                zNormal );
    }
}
void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
                                      REPORTER* aErrorMessages, REPORTER* aActivity  )
{
    BOARD* pcb = GetBoard();

    // If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas.
    // If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas,
    // but the calculation time is twice shorter.
    bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES );

    bool realistic_mode = isRealisticMode();
    bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES );

    // Number of segments to convert a circle to polygon
    // We use 2 values: the first gives a good shape (for instanes rond pads)
    // the second is used to speed up calculations, when a poor approximation is acceptable (holes)
    const int       segcountforcircle   = 18;
    double          correctionFactor    = 1.0 / cos( M_PI / (segcountforcircle * 2.0) );
    const int       segcountLowQuality  = 12;   // segments to draw a circle with low quality
                                                // to reduce time calculations
                                                // for holes and items which do not need
                                                // a fine representation
    double          correctionFactorLQ  = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) );

    SHAPE_POLY_SET  bufferPolys;        // copper areas: tracks, pads and filled zones areas
                                        // when holes are removed from zones
    SHAPE_POLY_SET  bufferPcbOutlines;  // stores the board main outlines
    SHAPE_POLY_SET  bufferZonesPolys;   // copper filled zones areas
                                        // when holes are not removed from zones
    SHAPE_POLY_SET  currLayerHoles;     // Contains holes for the current layer
    SHAPE_POLY_SET  allLayerHoles;      // Contains holes for all layers

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

    if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) )
    {
        if( aErrorMessages )
        {
            msg << wxT("\n") << _("Unable to calculate the board outlines.\n"
                                  "Therefore use the board boundary box.") << wxT("\n\n");

            aErrorMessages->Report( msg, REPORTER::RPT_WARNING );
        }
    }

    // Build board holes, with optimization of large holes shape.
    buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true );

    LSET            cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount );

    glNewList( aBoardList, GL_COMPILE );

    for( LSEQ cu = cu_set.CuStack();  cu;  ++cu )
    {
        LAYER_ID layer = *cu;

        // Skip non enabled layers in normal mode,
        // and internal layers in realistic mode
        if( !is3DLayerEnabled( layer ) )
            continue;

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

        bufferPolys.RemoveAllContours();
        bufferZonesPolys.RemoveAllContours();
        currLayerHoles.RemoveAllContours();

        // Draw track shapes:
        for( TRACK* track = pcb->m_Track;  track;  track = track->Next() )
        {
            if( !track->IsOnLayer( layer ) )
                continue;

            track->TransformShapeWithClearanceToPolygon( bufferPolys,
                                                         0, segcountforcircle,
                                                         correctionFactor );

            // Add blind/buried via holes
            if( track->Type() == PCB_VIA_T )
            {
                VIA *via = static_cast<VIA*>( track );

                if( via->GetViaType() == VIA_THROUGH )
                    continue;   // already done

                int holediameter = via->GetDrillValue();
                int thickness = GetPrm3DVisu().GetCopperThicknessBIU();
                int hole_outer_radius = (holediameter + thickness) / 2;

                TransformCircleToPolygon( currLayerHoles,
                                          via->GetStart(), hole_outer_radius,
                                          segcountLowQuality );
            }
        }

        // draw pad shapes
        for( MODULE* module = pcb->m_Modules;  module;  module = module->Next() )
        {
            // Note: NPTH pads are not drawn on copper layers when the pad
            // has same shape as its hole
            module->TransformPadsShapesWithClearanceToPolygon( layer,
                                                               bufferPolys,
                                                               0,
                                                               segcountforcircle,
                                                               correctionFactor, true );

            // Micro-wave modules may have items on copper layers
            module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
                                                                     bufferPolys,
                                                                     0,
                                                                     segcountforcircle,
                                                                     correctionFactor );

            // pad holes are already in list.
        }

        // Draw copper zones. Note:
        // * if the holes are removed from copper zones
        // the polygons are stored in bufferPolys (which contains all other polygons)
        // * if the holes are NOT removed from copper zones
        // the polygons are stored in bufferZonesPolys
        if( isEnabled( FL_ZONE ) )
        {
            for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
            {
                ZONE_CONTAINER* zone = pcb->GetArea( ii );
                LAYER_NUM       zonelayer = zone->GetLayer();

                if( zonelayer == layer )
                {
                    zone->TransformSolidAreasShapesToPolygonSet(
                        remove_Holes ? bufferPolys : bufferZonesPolys,
                        segcountLowQuality, correctionFactorLQ );
                }
            }
        }

        // draw graphic items on copper layers (texts)
        for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
        {
            if( !item->IsOnLayer( layer ) )
                continue;

            switch( item->Type() )
            {
            case PCB_LINE_T:    // should not exist on copper layers
                ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
                    bufferPolys, 0, segcountforcircle, correctionFactor );
                break;

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

            default:
                break;
            }
        }

        // bufferPolys contains polygons to merge. Many overlaps .
        // Calculate merged polygons
        if( bufferPolys.IsEmpty() )
            continue;

        // Use Clipper lib to subtract holes to copper areas
        if( currLayerHoles.OutlineCount() )
        {
            currLayerHoles.Append(allLayerHoles);
            currLayerHoles.Simplify();
            bufferPolys.BooleanSubtract( currLayerHoles );
        }
        else
            bufferPolys.BooleanSubtract( allLayerHoles );

        int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
        int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );

        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 z-normal has to match the layer direction
        // because just one plane will be drawn
        if( !thickness )
            zNormal = Get3DLayer_Z_Orientation( layer );

        if( realistic_mode )
        {
            setGLCopperColor();
        }
        else
        {
            EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer );
            SetGLColor( color );
        }

        // If holes are removed from copper zones, bufferPolys contains all polygons
        // to draw (tracks+zones+texts).
        Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness,
                                            GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                            zNormal );

        // If holes are not removed from copper zones (for calculation time reasons,
        // the zone polygons are stored in bufferZonesPolys and have to be drawn now:
        if( !bufferZonesPolys.IsEmpty() )
        {
            Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness,
                                    GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                    zNormal );
        }
    }

    if( aActivity )
        aActivity->Report( _( "Build board body" ) );

    // Draw plated vertical holes inside the board, but not always. They are drawn:
    // - if the board body is not shown, to show the holes.
    // - or if the copper thickness is shown
    if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) )
    {
        // Draw vias holes (vertical cylinders)
        for( const TRACK* track = pcb->m_Track;  track;  track = track->Next() )
        {
            if( track->Type() == PCB_VIA_T )
            {
                const VIA *via = static_cast<const VIA*>(track);
                draw3DViaHole( via );
            }
        }

        // Draw pads holes (vertical cylinders)
        for( const MODULE* module = pcb->m_Modules;  module;  module = module->Next() )
        {
            for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
                if( pad->GetAttribute () != PAD_HOLE_NOT_PLATED )
                    draw3DPadHole( pad );
        }
    }

    glEndList();

    // Build the body board:
    glNewList( aBodyOnlyList, GL_COMPILE );

    if( isRealisticMode() )
    {
        setGLEpoxyColor( 1.00 );
    }
    else
    {
        EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts );
        SetGLColor( color, 0.7 );
    }

    float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU();

    // a small offset between substrate and external copper layer to avoid artifacts
    // when drawing copper items on board
    float epsilon = Millimeter2iu( 0.01 );
    float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
    float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu )
                        - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );

    // items on copper layers and having a thickness = copper_thickness
    // are drawn from zpos - copper_thickness/2 to zpos + copper_thickness
    // therefore substrate position is copper_thickness/2 to
    // substrate_height - copper_thickness/2
    zpos += (copper_thickness + epsilon) / 2.0f;
    board_thickness -= copper_thickness + epsilon;

    bufferPcbOutlines.BooleanSubtract( allLayerHoles );

    if( !bufferPcbOutlines.IsEmpty() )
    {
        Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0,
                                            board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                            1.0f );
    }

    glEndList();
}
예제 #8
0
/**
 * 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();

    SHAPE_POLY_SET  bufferPolys;

    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.IsEmpty() )
            continue;

        bufferPolys.Simplify( polygonsCalcMode );

        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 ;

        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 );
    }
}