← Back to team overview

kicad-developers team mailing list archive

New 3d-viewer features

 

Hi,

the attached patch (against rev 3615) contains more additional features for 
the kicad 3d-viewer.

The goal was to visualize a PCB good enough to verify drilling, copper,  
solder mask and silk screen.

In addition to that, the virtual flag is interpreted to allow optional 
rendering of assembly modules.

Most layer flags and colors (from pcbnew) are used in a useful way (according 
to my opinion).
Changing a flag in pcbnew usually requires manual refresh.

The render flags Values & Reference are also evaluated. 
Values activated results in the tags on top of a 3d shape.
References gets the Ref tags printed in rendering mode and silk screen 
visible.

True zone rendering is only enabled in rendering mode with both mask layers 
visible.

Burried vias are also rendered correctly (that was one of the features I 
needed recently).Use rendered mode with PCB_Edges disabled.

PTH drills are rendered 2 mils bigger than the plated drill size.

Pblong and offset drills are also handled correctly.

Rendered mode changes all copper layers to sort of NiAu color. 

An optional mm grid is also available.

The Preferences menu has three more entries:
	Show Render Mode
	Show Grid
	Show Virtual Modules

The stuff is a heap of hacks I've done over the last 10 years or so to solve 
some issues in a quick and dirty way.

The code is only tested on linux (64bit), but should also run on other 
supported kicad platforms.

Good luck,
Thomas

PS:
be prepared, rendering takes time...

=== modified file '3d-viewer/3d_aux.cpp'
--- 3d-viewer/3d_aux.cpp	2012-04-27 14:15:11 +0000
+++ 3d-viewer/3d_aux.cpp	2012-06-15 14:42:26 +0000
@@ -192,6 +192,11 @@
     // default all special item layers Visible
     for (ii=0; ii< FL_LAST; ii++)
         m_DrawFlags[ii]=true;
+    m_DrawFlags[g_Parm_3D_Visu.FL_ZONE]=false;      // TSP_20120614
+    m_DrawFlags[g_Parm_3D_Visu.FL_AXIS]=false;      // TSP_20120603
+    m_DrawFlags[g_Parm_3D_Visu.FL_RENDER]=false;
+    m_DrawFlags[g_Parm_3D_Visu.FL_GRID]=false;
+    m_DrawFlags[g_Parm_3D_Visu.FL_VIRT]=false;
 }
 
 

=== modified file '3d-viewer/3d_draw.cpp'
--- 3d-viewer/3d_draw.cpp	2012-06-09 17:00:13 +0000
+++ 3d-viewer/3d_draw.cpp	2012-06-15 16:08:37 +0000
@@ -25,7 +25,7 @@
 
 /**
  * @file 3d_draw.cpp
-*/
+ */
 
 #include <fctsys.h>
 #include <common.h>
@@ -49,30 +49,113 @@
 #include <3d_viewer.h>
 #include <trackball.h>
 
+#include <collectors.h>
+#include <richio.h>
+
 #if !wxUSE_GLCANVAS
 #error Please set wxUSE_GLCANVAS to 1 in setup.h.
 #endif
 
 extern void CheckGLError();
 
-static void    Draw3D_FilledCircle( double posx, double posy, double rayon,
-                                    double hole_rayon, double zpos );
-static void    Draw3D_FilledSegment( double startx, double starty,
+static void Draw3D_FilledCircle( double posx, double posy, double radius,
+                                 double holeradius, double zpos );
+static void Draw3D_FilledSegment( double startx, double starty,
+                                  double endx, double endy,
+                                  double width, double zpos );
+static void Draw3D_FilledSegmentMask( double            startx,
+                                      double            starty,
+                                      double            endx,
+                                      double            endy,
+                                      double            width,
+                                      double            zpos,
+                                      GLUtesselator*    tess,
+                                      int               orient );
+static void Draw3D_FilledSegmentMask( double                startx,
+                                      double                starty,
+                                      double                endx,
+                                      double                endy,
+                                      double                width,
+                                      double                zpos,
+                                      std::vector<CPolyPt>* e_data,
+                                      int                   orient );
+
+static void Draw3D_FilledCylinder( double posx, double posy, double radius,
+                                   double height, double zpos );
+static void Draw3D_FilledPolyWithHole( wxPoint* coord,
+                                       int      n,
+                                       double   x,
+                                       double   y,
+                                       double   offX,
+                                       double   offY,
+                                       double   holeX,
+                                       double   holeY,
+                                       double   zpos,
+                                       double   oang );
+static void Draw3D_FilledSegmentWithHole( double    startx,
+                                          double    starty,
+                                          double    endx,
+                                          double    endy,
+                                          double    width,
+                                          double    holex,
+                                          double    holey,
+                                          double    holeradius,
+                                          double    zpos,
+                                          double    offX,
+                                          double    offY,
+                                          double    holeX,
+                                          double    holeY,
+                                          double    odx,
+                                          double    ody,
+                                          double    oang );
+static void     Draw3D_ArcSegment( double startx, double starty, double centrex,
+                                   double centrey, double arc_angle, double width, double zpos );
+static void     Draw3D_CircleSegment( double startx, double starty, double endx,
+                                      double endy, double width, double zpos );
+static int      Get3DLayerEnable( int act_layer );
+static GLfloat  Get3DLayerSide( int act_layer );
+static void     Draw3D_FilledOblong( double startx, double starty,
                                      double endx, double endy,
-                                     double width, double zpos );
-static void    Draw3D_FilledCylinder( double posx, double posy, double rayon,
-                                      double height, double zpos );
-static void    Draw3D_FilledSegmentWithHole( double startx, double starty,
-                                             double endx, double endy,
-                                             double width, double holex,
-                                             double holey, double holeradius,
-                                             double zpos );
-static void    Draw3D_ArcSegment( double startx, double starty, double centrex,
-                                  double centrey, double arc_angle, double width, double zpos );
-static void    Draw3D_CircleSegment( double startx, double starty, double endx,
-                                     double endy, double width, double zpos );
-static int     Get3DLayerEnable( int act_layer );
-static GLfloat Get3DLayerSide( int act_layer );
+                                     double holex,
+                                     double holey, double holeradius,
+                                     double height, double zpos );
+static void     Draw3D_FilledOblongMask( double startx, double starty,
+                                         double endx, double endy,
+                                         double holex,
+                                         double holey, double holeradius,
+                                         double height, double zpos );
+static void     Draw3D_Mask( double startx, double starty, double endx,
+                             double endy, double width, double zpos );
+static void     Draw3D_ArcSegmentMask( double startx, double starty, double centrex,
+                                       double centrey, double arc_angle, double width,
+                                       double zpos );
+static void     Draw3D_ArcSegmentMask( double startx, double starty, double centrex,
+                                       double centrey, double arc_angle, double width,
+                                       double zpos, std::vector<CPolyPt>* e_data );
+static void     Draw3D_CircleSegmentMask( double startx, double starty, double endx,
+                                          double endy, double width, double zpos );
+static void     Draw3D_CircleSegmentMask( double                startx,
+                                          double                starty,
+                                          double                endx,
+                                          double                endy,
+                                          double                width,
+                                          double                zpos,
+                                          std::vector<CPolyPt>* e_data );
+static void Draw3D_FilledSegmentWithHoleMask( double    startx,
+                                              double    starty,
+                                              double    endx,
+                                              double    endy,
+                                              double    width,
+                                              double    holex,
+                                              double    holey,
+                                              double    holeradius,
+                                              double    zpos,
+                                              double    offX,
+                                              double    offY,
+                                              double    oang );
+static void Draw3D_FilledCylinderMask( double posx, double posy, double radius,
+                                       double height, double zpos );
+void        MySetGLColor( int color, int layer, double alpha );
 
 
 #ifndef CALLBACK
@@ -80,16 +163,115 @@
 #endif
 
 // CALLBACK functions for GLU_TESS
-static void CALLBACK tessBeginCB( GLenum which );
-static void CALLBACK tessEndCB();
-static void CALLBACK tessErrorCB( GLenum errorCode );
-static void CALLBACK tessCPolyPt2Vertex( const GLvoid* data );
-static void CALLBACK tesswxPoint2Vertex( const GLvoid* data );
-
+static void CALLBACK    tessErrorCB( GLenum errorCode );
+static void CALLBACK    tessCPolyPt2Vertex( const GLvoid* data );
+static void CALLBACK    tesswxPoint2Vertex( const GLvoid* data );
+
+static void CALLBACK    mytessErrorCB( GLenum errorCode );
+static void CALLBACK    mytessCPolyPt2Vertex( const GLvoid* data );
+static void CALLBACK    mytessCombineCB( GLdouble   coords[3],
+                                         void*      vertex_data[4],
+                                         GLfloat    weight[4],
+                                         void**     outData );
+
+static void CALLBACK    my2tessBeginCB( GLenum which );
+static void CALLBACK    my2tessEndCB();
+static void CALLBACK    my2tessErrorCB( GLenum errorCode );
+static void CALLBACK    my2tessCPolyPt2Vertex( const GLvoid* data );
+static void CALLBACK    my2tessCombineCB( GLdouble  coords[3],
+                                          void*     vertex_data[4],
+                                          GLfloat   weight[4],
+                                          void**    outData );
+
+static void CALLBACK    my3tessBeginCB( GLenum which );
+static void CALLBACK    my3tessEndCB();
+static void CALLBACK    my3tessErrorCB( GLenum errorCode );
+static void CALLBACK    my3tessCPolyPt2Vertex( const GLvoid* data );
+static void CALLBACK    my3tessCombineCB( GLdouble  coords[3],
+                                          void*     vertex_data[4],
+                                          GLfloat   weight[4],
+                                          void**    outData );
+
+#define MAGIC_RESERVE   10000000
+#define MAGIC_CONT      0x80000000
+
+#ifdef USE_PCBNEW_NANOMETRES
+#warning "Tweak Edge export by 100 µ"
+#define DLIM 100000
+#warning "Copper extend by 50 µ"
+#define COPPER_EXTEND 50000
+#else
+#warning "Tweak Edge export by 10 mil"
+#define DLIM 100
+#warning "Copper extend by 2 mil"
+#define COPPER_EXTEND 20
+#endif    // USE_PCBNEW_NANOMETRES
+
+/*
+ * circle & arc smoothness...
+ */
+#define C_SLICE 8
+
+#define _SEG2( x )  ( 1800.0 / (x) )
+#define _SEG( x )   ( 3600.0 / (x) )
+
+#define ATAN2( dx, dy ) atan2( ( dx ), ( dy ) ) * 1800.0 / M_PI
+
+std::vector<CPolyPt>    e_data;
+std::vector<CPolyPt>    p_data;
+std::vector<CPolyPt>    edge_data;
+std::vector<CPolyPt>    xe_data;
+std::vector<CPolyPt>    se_data;
+std::vector<CPolyPt>    miss_data;
+int             tmonly = 0;
+int             do_values   = 0;
+int             do_ref      = 0;
+
+GLUtesselator*  tess = gluNewTess();
+GLUtesselator*  gtess;
+
+// define RAW_TIMING
+#ifdef RAW_TIMING
+double  st0 = 0.0;
+double* t0p = NULL;
+char    temps[1024];
+#include <sys/time.h>
+char* DTS( const char* s )
+{
+    struct timeval  t;
+    double          ot;
+
+    gettimeofday( &t, NULL );
+
+    if( s == NULL )
+    {
+        t0p = NULL;
+        st0 = t.tv_sec * 1.0 + t.tv_usec / 1000000.0;
+        temps[0] = 0;
+    }
+    else if( t0p == NULL )
+    {
+        t0p = &st0;
+        st0 = t.tv_sec * 1.0 + t.tv_usec / 1000000.0;
+        sprintf( temps, "%.2lf %s", st0, s );
+    }
+    else
+    {
+        ot = t.tv_sec * 1.0 + t.tv_usec / 1000000.0;
+        sprintf( temps, "%.2lf %s", ot - st0, s );
+    }
+
+    return temps;
+}
+
+
+#else
+#define DTS( s ) ""
+#endif    // RAW_TIMING
 
 void EDA_3D_CANVAS::Redraw( bool finish )
 {
-    // SwapBuffer requires the window to be shown before calling
+// SwapBuffer requires the window to be shown before calling
     if( !IsShown() )
         return;
 
@@ -99,12 +281,12 @@
     SetCurrent();
 #endif
 
-    // Set the OpenGL viewport according to the client size of this canvas.
-    // This is done here rather than in a wxSizeEvent handler because our
-    // OpenGL rendering context (and thus viewport setting) is used with
-    // multiple canvases: If we updated the viewport in the wxSizeEvent
-    // handler, changing the size of one canvas causes a viewport setting that
-    // is wrong when next another canvas is repainted.
+// Set the OpenGL viewport according to the client size of this canvas.
+// This is done here rather than in a wxSizeEvent handler because our
+// OpenGL rendering context (and thus viewport setting) is used with
+// multiple canvases: If we updated the viewport in the wxSizeEvent
+// handler, changing the size of one canvas causes a viewport setting that
+// is wrong when next another canvas is repainted.
     const wxSize ClientSize = GetClientSize();
 
     // *MUST* be called *after*  SetCurrent( ):
@@ -136,34 +318,231 @@
     }
 
     glFlush();
-    if( finish );
-        glFinish();
+
+    if( finish )
+        ;
+
+    glFinish();
 
     SwapBuffers();
 }
 
 
+int orient( std::vector<CPolyPt> poly )
+{
+    double a = 0;
+
+    for( unsigned i = 0; i < poly.size() - 1; i++ )
+    {
+        a += (double) poly[i].x *
+             (double) poly[i + 1].y - (double) poly[i + 1].x * (double) poly[i].y;
+    }
+
+    a +=
+        (double) poly[poly.size() -
+                      1].x * (double) poly[0].y - (double) poly[0].x *
+        (double) poly[poly.size() - 1].y;
+    return (a >= 0) ? 1 : -1;
+}
+
+
+/**
+ * Function findPoint
+ * searches for a DRAWSEGMENT with an end point or start point of aPoint, and
+ * if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.
+ * @param aPoint The starting or ending point to search for.
+ * @param items The list to remove from.
+ * @return DRAWSEGMENT* - The first DRAWSEGMENT that has a start or end point matching
+ *   aPoint, otherwise NULL if none.
+ */
+static DRAWSEGMENT* findPoint( const wxPoint& aPoint, TYPE_COLLECTOR* items )
+{
+    int mind    = 0x7fffffff;
+    int mindi   = 0;
+    int d, xa, ya, xe, ye;
+
+    for( int i = 0; i<items->GetCount(); ++i )
+    {
+        DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
+
+        wxASSERT( graphic->Type() == PCB_LINE_T );
+
+        switch( graphic->GetShape() )
+        {
+        case S_ARC:
+
+            if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
+            {
+                items->Remove( i );
+                return graphic;
+            }
+
+            d = abs( aPoint.x - graphic->GetArcStart().x ) + abs(
+                aPoint.y - graphic->GetArcStart().y );
+
+            if( d < mind )
+            {
+                mind    = d;
+                mindi   = i;
+                xa      = graphic->GetArcStart().x;
+                ya      = graphic->GetArcStart().y;
+                xe      = graphic->GetArcEnd().x;
+                ye      = graphic->GetArcEnd().y;
+            }
+
+            d =
+                abs( aPoint.x -
+                     graphic->GetArcEnd().x ) + abs( aPoint.y - graphic->GetArcEnd().y );
+
+            if( d < mind )
+            {
+                mind    = d;
+                mindi   = i;
+                xe      = graphic->GetArcStart().x;
+                ye      = graphic->GetArcStart().y;
+                xa      = graphic->GetArcEnd().x;
+                ya      = graphic->GetArcEnd().y;
+            }
+
+            break;
+
+        default:
+
+            if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
+            {
+                items->Remove( i );
+                return graphic;
+            }
+
+            d = abs( aPoint.x - graphic->GetStart().x ) + abs( aPoint.y - graphic->GetStart().y );
+
+            if( d < mind )
+            {
+                mind    = d;
+                mindi   = i;
+                xa      = graphic->GetStart().x;
+                ya      = graphic->GetStart().y;
+                xe      = graphic->GetEnd().x;
+                ye      = graphic->GetEnd().y;
+            }
+
+            d = abs( aPoint.x - graphic->GetEnd().x ) + abs( aPoint.y - graphic->GetEnd().y );
+
+            if( d < mind )
+            {
+                mind    = d;
+                mindi   = i;
+                xe      = graphic->GetStart().x;
+                ye      = graphic->GetStart().y;
+                xa      = graphic->GetEnd().x;
+                ya      = graphic->GetEnd().y;
+            }
+        }
+    }
+
+    if( mind > DLIM )
+        return NULL;
+
+    for( int i = 0; i<items->GetCount(); ++i )
+    {
+        DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
+
+        wxASSERT( graphic->Type() == PCB_LINE_T );
+
+        switch( graphic->GetShape() )
+        {
+        case S_ARC:
+            d = abs( aPoint.x - graphic->GetArcStart().x ) + abs(
+                aPoint.y - graphic->GetArcStart().y );
+
+            if( d == mind )
+            {
+                items->Remove( i );
+                return graphic;
+            }
+
+            d =
+                abs( aPoint.x -
+                     graphic->GetArcEnd().x ) + abs( aPoint.y - graphic->GetArcEnd().y );
+
+            if( d == mind )
+            {
+                items->Remove( i );
+                return graphic;
+            }
+
+            break;
+
+        default:
+            d = abs( aPoint.x - graphic->GetStart().x ) + abs( aPoint.y - graphic->GetStart().y );
+
+            if( d == mind )
+            {
+                items->Remove( i );
+                return graphic;
+            }
+
+            d = abs( aPoint.x - graphic->GetEnd().x ) + abs( aPoint.y - graphic->GetEnd().y );
+
+            if( d == mind )
+            {
+                items->Remove( i );
+                return graphic;
+            }
+        }
+    }
+
+#if defined(DEBUG)
+    printf( "Unable to find segment matching point (%d,%d)\n",
+            aPoint.x, aPoint.y );
+
+    for( int i = 0; i<items->GetCount(); ++i )
+    {
+        DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
+
+        printf( "type=%s, GetStart()=%d,%d  GetEnd()=%d,%d\n",
+                TO_UTF8( BOARD_ITEM::ShowShape( (STROKE_T) graphic->GetShape() ) ),
+                graphic->GetStart().x,
+                graphic->GetStart().y,
+                graphic->GetEnd().x,
+                graphic->GetEnd().y );
+    }
+
+#endif
+
+    return NULL;
+}
+
+
 GLuint EDA_3D_CANVAS::CreateDrawGL_List()
 {
     PCB_BASE_FRAME* pcbframe = Parent()->Parent();
-    BOARD* pcb = pcbframe->GetBoard();
-    TRACK* track;
-    SEGZONE*             segzone;
-    int ii;
-
-    wxBusyCursor         dummy;
-
+    BOARD*          pcb = pcbframe->GetBoard();
+    TRACK*          track;
+    SEGZONE*        segzone;
+    int             ii;
+
+    wxBusyCursor    dummy;
+    int             bpii = 0;
+
+    do_values   = pcb->IsElementVisible( MOD_VALUES_VISIBLE );
+    do_ref      = pcb->IsElementVisible( MOD_REFERENCES_VISIBLE );
+    tmonly      = 0;
+    e_data.reserve( MAGIC_RESERVE );
+    e_data.clear();
+    p_data.reserve( MAGIC_RESERVE );
+    p_data.clear();
     m_gllist = glGenLists( 1 );
 
-    EDA_RECT    bbbox = pcbframe->GetBoardBoundingBox();
+    EDA_RECT bbbox = pcbframe->GetBoardBoundingBox();
 
     g_Parm_3D_Visu.m_BoardSettings = &pcb->GetDesignSettings();
 
-    g_Parm_3D_Visu.m_BoardSize     = bbbox.GetSize();
-    g_Parm_3D_Visu.m_BoardPos      = bbbox.Centre();
+    g_Parm_3D_Visu.m_BoardSize  = bbbox.GetSize();
+    g_Parm_3D_Visu.m_BoardPos   = bbbox.Centre();
 
-    g_Parm_3D_Visu.m_BoardPos.y    = -g_Parm_3D_Visu.m_BoardPos.y;
-    g_Parm_3D_Visu.m_Layers        = pcb->GetCopperLayerCount();
+    g_Parm_3D_Visu.m_BoardPos.y = -g_Parm_3D_Visu.m_BoardPos.y;
+    g_Parm_3D_Visu.m_Layers     = pcb->GetCopperLayerCount();
 
     // Ensure the board has 2 sides for 3D views, because it is hard to find
     // a *really* single side board in the true life...
@@ -184,32 +563,60 @@
     for( ii = 0; ii < 32; ii++ )
     {
         if( ii < g_Parm_3D_Visu.m_Layers )
+        {
             g_Parm_3D_Visu.m_LayerZcoord[ii] = g_Parm_3D_Visu.m_Epoxy_Width
                                                * ii / (g_Parm_3D_Visu.m_Layers - 1);
-        else
+        }
+        else if( ii < 16 )
+        {
             g_Parm_3D_Visu.m_LayerZcoord[ii] = g_Parm_3D_Visu.m_Epoxy_Width;
+        }
+        else
+        {
+            if( ii <= SOLDERMASK_N_FRONT )
+            {
+                double offs = g_Parm_3D_Visu.m_Epoxy_Width * ( 4 - ( (ii >> 1) & 7 ) ) / 100.0;
+                g_Parm_3D_Visu.m_LayerZcoord[ii] =
+                    (ii & 1) ? (g_Parm_3D_Visu.m_Epoxy_Width + offs) : -offs;
+            }
+            else
+            {
+                g_Parm_3D_Visu.m_LayerZcoord[ii] = g_Parm_3D_Visu.m_Epoxy_Width;
+            }
+        }
     }
 
-    GLfloat zpos_cu  = 10 * g_Parm_3D_Visu.m_BoardScale;
-    GLfloat zpos_cmp = g_Parm_3D_Visu.m_Epoxy_Width + zpos_cu;
-    g_Parm_3D_Visu.m_LayerZcoord[ADHESIVE_N_BACK]    = -zpos_cu * 2;
-    g_Parm_3D_Visu.m_LayerZcoord[ADHESIVE_N_FRONT]   = zpos_cmp + zpos_cu;
-    g_Parm_3D_Visu.m_LayerZcoord[SILKSCREEN_N_BACK]  = -zpos_cu;
-    g_Parm_3D_Visu.m_LayerZcoord[SILKSCREEN_N_FRONT] = zpos_cmp;
-    g_Parm_3D_Visu.m_LayerZcoord[DRAW_N]    = zpos_cmp + zpos_cu;
-    g_Parm_3D_Visu.m_LayerZcoord[COMMENT_N] = zpos_cmp + zpos_cu;
-    g_Parm_3D_Visu.m_LayerZcoord[ECO1_N]    = zpos_cmp + zpos_cu;
-    g_Parm_3D_Visu.m_LayerZcoord[ECO2_N]    = zpos_cmp + zpos_cu;
+    GLfloat zpos_cu     = 0;
+    GLfloat zpos_cmp    = g_Parm_3D_Visu.m_Epoxy_Width;
+    g_Parm_3D_Visu.m_LayerZcoord[ADHESIVE_N_BACK]   = -4 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[ADHESIVE_N_FRONT]  = zpos_cmp + 4 *
+                                                      g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[SOLDERPASTE_N_BACK]    = -3 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[SOLDERPASTE_N_FRONT]   = zpos_cmp + 3 *
+                                                          g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[SOLDERMASK_N_BACK]     = -1 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[SOLDERMASK_N_FRONT]    = zpos_cmp + 1 *
+                                                          g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[SILKSCREEN_N_BACK]     = -2 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[SILKSCREEN_N_FRONT]    = zpos_cmp + 2 *
+                                                          g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[DRAW_N]    = zpos_cmp + 5 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[COMMENT_N] = zpos_cmp + 6 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[ECO1_N]    = zpos_cmp + 7 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[ECO2_N]    = zpos_cmp + 8 * g_Parm_3D_Visu.m_BoardScale;
 
+    g_Parm_3D_Visu.m_LayerZcoord[UNUSED_LAYER_29]   = zpos_cmp + 9 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[UNUSED_LAYER_30]   = zpos_cu - 11 * g_Parm_3D_Visu.m_BoardScale;
+    g_Parm_3D_Visu.m_LayerZcoord[UNUSED_LAYER_31]   = zpos_cmp + 11 * g_Parm_3D_Visu.m_BoardScale;
     glNewList( m_gllist, GL_COMPILE_AND_EXECUTE );
 
     glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
 
     // draw axis
-    if (g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_AXIS])
+    if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_AXIS] )
     {
         glEnable( GL_COLOR_MATERIAL );
-        SetGLColor( WHITE );
+        MySetGLColor( WHITE, -1, 1 );
         glBegin( GL_LINES );
         glNormal3f( 0.0f, 0.0f, 1.0f );     // Normal is Z axis
         glVertex3f( 0.0f, 0.0f, 0.0f );
@@ -223,31 +630,693 @@
     }
 
     // Draw epoxy limits (do not use, works and test in progress)
-    // TODO
 
     // move the board in order to draw it with its center at 0,0 3D coordinates
     glTranslatef( -g_Parm_3D_Visu.m_BoardPos.x * g_Parm_3D_Visu.m_BoardScale,
                   -g_Parm_3D_Visu.m_BoardPos.y * g_Parm_3D_Visu.m_BoardScale,
                   0.0F );
 
-    glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
-
-    // draw tracks and vias :
-    for( track = pcb->m_Track; track != NULL; track = track->Next() )
+    glNormal3f( 0.0, 0.0, 1.0 );    // Normal is Z axis
+
+    // draw tracks and vias (and text) :
+    MODULE* Module;
+
+    for( int llay = 0; llay < 16; llay++ )
     {
-        if( track->Type() == PCB_VIA_T )
-            Draw3D_Via( (SEGVIA*) track );
-        else
-            Draw3D_Track( track );
+        if( !g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( llay ) )
+            continue;
+
+        tess    = gluNewTess();
+        tmonly  = 1;
+        GLUtesselator*  jtess = gluNewTess();
+        g_Parm_3D_Visu.m_ActZpos = g_Parm_3D_Visu.m_LayerZcoord[llay];
+        GLdouble        v_data[3];
+        int             color = g_ColorsSettings.GetLayerColor( llay );
+        MySetGLColor( color, llay, 1 );
+        glNormal3f( 0.0, 0.0, (llay) ? 1.0 : -1.0 );
+        gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+        gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+        gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+        gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+        gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+        gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+        gluTessCallback( jtess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )my2tessBeginCB );
+        gluTessCallback( jtess, GLU_TESS_END, ( void (CALLBACK*) () )my2tessEndCB );
+        gluTessCallback( jtess, GLU_TESS_ERROR, ( void (CALLBACK*) () )my2tessErrorCB );
+        gluTessCallback( jtess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )my2tessCPolyPt2Vertex );
+        gluTessCallback( jtess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )my2tessCombineCB );
+        gluTessProperty( jtess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE );
+        gluTessProperty( jtess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
+
+        gluTessBeginPolygon( jtess, NULL );
+
+        gluTessBeginPolygon( tess, NULL );
+
+        gluTessNormal( jtess, 0, 0, 1 );
+
+        // all tracks on layer....
+        for( track = pcb->m_Track; track != NULL; track = track->Next() )
+        {
+            if( ( track->GetLayer() == llay ) && ( track->Type() == PCB_TRACE_T) )
+            {
+                double w, ox, oy, fx, fy;
+                w   = track->m_Width * g_Parm_3D_Visu.m_BoardScale;
+                ox  = track->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+                oy  = track->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+                fx  = track->m_End.x * g_Parm_3D_Visu.m_BoardScale;
+                fy  = track->m_End.y * g_Parm_3D_Visu.m_BoardScale;
+                Draw3D_FilledSegmentMask( ox, -oy, fx, -fy, w, g_Parm_3D_Visu.m_ActZpos, jtess,
+                                          1 );
+            }
+            else if( track->Type() == PCB_VIA_T )
+            {
+                double  x, y, r, hole;
+                int     lpii, ii;
+                int     top_layer;
+                int     bottom_layer;
+                ( (SEGVIA*) track )->ReturnLayerPair( &top_layer, &bottom_layer );
+
+                if( ( llay >= bottom_layer) && ( llay <= top_layer ) )
+                {
+// fprintf(temps, "%s %d\n", DTS( "Via" ), llay);
+
+                    r = track->m_Width * g_Parm_3D_Visu.m_BoardScale / 2;
+                    hole    = track->GetWidth();
+                    hole    *= g_Parm_3D_Visu.m_BoardScale / 2;
+                    x       = track->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+                    y       = track->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+
+                    gluTessBeginContour( jtess );
+                    lpii = p_data.size();
+                    Draw3D_FilledSegmentWithHoleMask( x,
+                                                      -y,
+                                                      x,
+                                                      -y,
+                                                      hole * 2,
+                                                      x,
+                                                      -y,
+                                                      hole,
+                                                      g_Parm_3D_Visu.m_ActZpos,
+                                                      0,
+                                                      0, 0 );
+
+                    for( ii = lpii; ii < (int) p_data.size(); ii++ )
+                    {
+                        v_data[2]   = g_Parm_3D_Visu.m_ActZpos;
+                        v_data[0]   = (double) p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;;
+                        v_data[1]   = -(double) p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;;
+                        gluTessVertex( jtess, v_data, &p_data[ii] );
+                    }
+
+                    gluTessEndContour( jtess );
+                }
+            }
+        }
+
+        if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ZONE]
+            && g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER]
+            && g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( SOLDERMASK_N_FRONT )
+            && g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( SOLDERMASK_N_BACK )
+            && ( (llay == 0) || (llay == 15) ) )
+        {
+            for( segzone = pcb->m_Zone; segzone != NULL; segzone = segzone->Next() )
+            {
+                if( segzone->Type() == PCB_ZONE_T )
+                {
+                    if( segzone->GetLayer() == llay )
+                    {
+                        double w, ox, oy, fx, fy;
+                        w   = segzone->m_Width * g_Parm_3D_Visu.m_BoardScale;
+                        ox  = segzone->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+                        oy  = segzone->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+                        fx  = segzone->m_End.x * g_Parm_3D_Visu.m_BoardScale;
+                        fy  = segzone->m_End.y * g_Parm_3D_Visu.m_BoardScale;
+                        Draw3D_FilledSegmentMask( ox,
+                                                  -oy,
+                                                  fx,
+                                                  -fy,
+                                                  w,
+                                                  g_Parm_3D_Visu.m_ActZpos,
+                                                  jtess,
+                                                  1 );
+                    }
+                }
+            }
+
+            // Draw new segments
+            for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+            {
+                ZONE_CONTAINER* curr_zone = pcb->GetArea( ii );
+
+                if( curr_zone->GetLayer() == llay )
+                {
+                    if( curr_zone->m_FillMode == 0 )
+                    {
+                        // solid polygons only are used to fill areas
+                        if( curr_zone->GetFilledPolysList().size() > 2    /*3*/ )      // TSP_20120608
+                        {
+                            // Draw solid areas contained in this zone
+                            int StartContour = 1;
+
+                            std::vector<CPolyPt> polysList = curr_zone->GetFilledPolysList();
+
+                            for( unsigned ii = 0; ii < polysList.size(); ii++ )
+                            {
+                                if( StartContour == 1 )
+                                {
+                                    gluTessBeginContour( jtess );
+                                    StartContour = 0;
+                                }
+
+                                p_data.push_back( CPolyPt( polysList[ii].x, polysList[ii].y ) );
+                                v_data[0] =
+                                    p_data[p_data.size() - 1].x * g_Parm_3D_Visu.m_BoardScale;
+                                v_data[1] =
+                                    -p_data[p_data.size() - 1].y * g_Parm_3D_Visu.m_BoardScale;
+                                v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+                                gluTessVertex( jtess, v_data, &p_data[p_data.size() - 1] );
+
+                                if( polysList[ii].end_contour == 1 )
+                                {
+                                    gluTessEndContour( tess );
+                                    StartContour = 1;
+                                }
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // segments are used to fill areas
+                        for( unsigned iseg = 0; iseg < curr_zone->m_FillSegmList.size(); iseg++ )
+                        {
+                            double w, ox, oy, fx, fy;
+                            w   = curr_zone->m_ZoneMinThickness * g_Parm_3D_Visu.m_BoardScale;
+                            ox  = curr_zone->m_FillSegmList[iseg].m_Start.x *
+                                  g_Parm_3D_Visu.m_BoardScale;
+                            oy = curr_zone->m_FillSegmList[iseg].m_Start.y *
+                                 g_Parm_3D_Visu.m_BoardScale;
+                            fx = curr_zone->m_FillSegmList[iseg].m_End.x *
+                                 g_Parm_3D_Visu.m_BoardScale;
+                            fy = curr_zone->m_FillSegmList[iseg].m_End.y *
+                                 g_Parm_3D_Visu.m_BoardScale;
+
+                            Draw3D_FilledSegmentMask( ox,
+                                                      -oy,
+                                                      fx,
+                                                      -fy,
+                                                      w,
+                                                      g_Parm_3D_Visu.m_ActZpos,
+                                                      jtess,
+                                                      1 );
+                        }
+                    }
+                }
+            }
+
+            // Draw copper areas outlines
+            for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+            {
+                ZONE_CONTAINER* zone = pcb->GetArea( ii );
+
+                if( zone->GetLayer() == llay )
+                {
+                    std::vector<CPolyPt> polysList = zone->GetFilledPolysList();
+
+                    if( polysList.size() == 0 )
+                        continue;
+
+                    if( zone->m_ZoneMinThickness <= 1 )
+                        continue;
+
+                    int         imax = polysList.size() - 1;
+                    CPolyPt*    firstcorner = &polysList[0];
+                    CPolyPt*    begincorner = firstcorner;
+                    SEGZONE dummysegment( pcb );
+
+                    for( int ic = 1; ic <= imax; ic++ )
+                    {
+                        CPolyPt* endcorner = &polysList[ic];
+
+                        if( begincorner->utility == 0 )
+                        {
+                            // Draw only basic outlines, not extra segments
+                            double w, ox, oy, fx, fy;
+                            w   = zone->m_ZoneMinThickness * g_Parm_3D_Visu.m_BoardScale;
+                            ox  = begincorner->x * g_Parm_3D_Visu.m_BoardScale;
+                            oy  = begincorner->y * g_Parm_3D_Visu.m_BoardScale;
+                            fx  = endcorner->x * g_Parm_3D_Visu.m_BoardScale;
+                            fy  = endcorner->y * g_Parm_3D_Visu.m_BoardScale;
+
+                            Draw3D_FilledSegmentMask( ox,
+                                                      -oy,
+                                                      fx,
+                                                      -fy,
+                                                      w,
+                                                      g_Parm_3D_Visu.m_ActZpos,
+                                                      jtess,
+                                                      1 );
+                        }
+
+                        if( (endcorner->end_contour) || (ic == imax) )
+                        {
+                            // the last corner of a filled area is found: draw it
+                            if( endcorner->utility == 0 )
+                            {
+                                // Draw only basic outlines, not extra segments
+                                double w, ox, oy, fx, fy;
+                                w   = zone->m_ZoneMinThickness * g_Parm_3D_Visu.m_BoardScale;
+                                ox  = endcorner->x * g_Parm_3D_Visu.m_BoardScale;
+                                oy  = endcorner->y * g_Parm_3D_Visu.m_BoardScale;
+                                fx  = begincorner->x * g_Parm_3D_Visu.m_BoardScale;
+                                fy  = begincorner->y * g_Parm_3D_Visu.m_BoardScale;
+
+                                Draw3D_FilledSegmentMask( ox,
+                                                          -oy,
+                                                          fx,
+                                                          -fy,
+                                                          w,
+                                                          g_Parm_3D_Visu.m_ActZpos,
+                                                          jtess,
+                                                          1 );
+                            }
+
+                            ic++;
+
+                            if( ic < imax - 1 )
+                                begincorner = firstcorner = &polysList[ic];
+                        }
+                        else
+                        {
+                            begincorner = endcorner;
+                        }
+                    }
+                }
+            }
+        }
+
+        // draw pads...
+        for( Module = pcb->m_Modules; Module != NULL; Module = Module->Next() )
+        {
+            D_PAD*  pad;
+            int     lpii;
+
+            for( pad = Module->m_Pads; pad != NULL; pad = pad->Next() )
+            {
+                int lm = pad->GetLayerMask();
+
+                if( lm & (1 << llay) )
+                {
+                    lpii = p_data.size();
+                    int ii, ll;
+                    int ux0, uy0,
+                        dx, dx0, dy, dy0,
+                        delta_cx, delta_cy,
+                        xc, yc;
+                    int     angle;
+                    double  scale;
+                    double  zpos = 0;
+                    wxPoint shape_pos;
+                    double  w, hole, holeX, holeY;
+                    double  drillx, drilly;
+                    double  offX, offY;
+
+                    scale   = g_Parm_3D_Visu.m_BoardScale;
+                    holeX   = (double) pad->GetDrillSize().x * scale / 2;
+                    holeY   = (double) pad->GetDrillSize().y * scale / 2;
+                    offX    = (double) pad->GetOffset().x * scale;
+                    offY    = (double) pad->GetOffset().y * scale;
+                    hole    = fmin( holeX, holeY );
+
+                    // Calculate the center of the pad.
+                    shape_pos = pad->GetPosition();
+                    ux0 = shape_pos.x;
+                    uy0 = shape_pos.y;
+                    xc  = ux0;
+                    yc  = uy0;
+
+                    dx  = dx0 = pad->GetSize().x / 2;
+                    dy  = dy0 = pad->GetSize().y / 2;
+
+                    angle   = pad->GetOrientation();
+                    drillx  = pad->GetPosition().x * scale;
+                    drilly  = pad->GetPosition().y * scale;
+
+                    switch( pad->GetShape() & 0x7F )
+                    {
+                    case PAD_CIRCLE:
+                    case PAD_OVAL:
+
+                        if( dx > dy )    // Horizontal ellipse
+                        {
+                            delta_cx    = dx - dy;
+                            delta_cy    = 0;
+                            w = pad->GetSize().y * scale;
+                        }
+                        else    // Vertical ellipse
+                        {
+                            delta_cx    = 0;
+                            delta_cy    = dy - dx;
+                            w = pad->GetSize().x * scale;
+                        }
+
+                        RotatePoint( &delta_cx, &delta_cy, angle );
+
+                        {
+                            double ox, oy, fx, fy;
+                            ox  = (double) ( ux0 + delta_cx ) * scale;
+                            oy  = (double) ( uy0 + delta_cy ) * scale;
+                            fx  = (double) ( ux0 - delta_cx ) * scale;
+                            fy  = (double) ( uy0 - delta_cy ) * scale;
+
+                            if( ( (1 << llay) & pad->GetLayerMask() ) != 0 )
+                            {
+                                Draw3D_FilledSegmentWithHoleMask( ox,
+                                                                  -oy,
+                                                                  fx,
+                                                                  -fy,
+                                                                  w,
+                                                                  drillx,
+                                                                  -drilly,
+                                                                  hole,
+                                                                  zpos,
+                                                                  offX,
+                                                                  offY, angle );
+                            }
+                        }
+                        break;
+
+                    case PAD_RECT:
+                    case PAD_TRAPEZOID:
+                        {
+                            wxPoint     coord[5];
+                            wxRealPoint fcoord[8], f_hole_coord[8];
+
+                            if( ( (1 << llay) & pad->GetLayerMask() ) != 0 )
+                            {
+                                wxSize lrp;
+                                lrp.x   = 0;
+                                lrp.y   = 0;
+                                pad->BuildPadPolygon( coord, lrp, angle );
+
+                                RotatePoint( &offX, &offY, angle );
+
+                                for( ii = 0; ii < 4; ii++ )
+                                {
+                                    coord[ii].x += ux0 + offX / scale;      // TSP_20120604
+                                    coord[ii].y += uy0 + offY / scale;
+                                    ll = ii * 2;
+                                    fcoord[ll].x    = coord[ii].x * scale;
+                                    fcoord[ll].y    = coord[ii].y * scale;
+                                }
+
+                                for( ii = 0; ii < 7; ii += 2 )
+                                {
+                                    ll = ll + 2;
+
+                                    if( ll > 7 )
+                                        ll -= 8;
+
+                                    fcoord[ii + 1].x    = (fcoord[ii].x + fcoord[ll].x) / 2;
+                                    fcoord[ii + 1].y    = (fcoord[ii].y + fcoord[ll].y) / 2;
+                                }
+
+                                for( ii = 0; ii < 8; ii++ )
+                                {
+                                    f_hole_coord[ii].x  = -hole * 0.707;
+                                    f_hole_coord[ii].y  = hole * 0.707;
+                                    RotatePoint( &f_hole_coord[ii].x, &f_hole_coord[ii].y, angle -
+                                                 (ii * 450) );
+                                    f_hole_coord[ii].x  += drillx;
+                                    f_hole_coord[ii].y  += drilly;
+                                }
+
+                                for( ii = 0; ii < 4; ii++ )
+                                {
+                                    p_data.push_back( CPolyPt( (int) (coord[ii].x),
+                                                               (int) (coord[ii].y) ) );
+                                }
+                            }
+                        }
+                        break;
+
+                    default:
+                        break;
+                    }
+
+                    if( (int) p_data.size() > lpii )
+                    {
+                        gluTessBeginContour( jtess );
+
+                        for( ii = lpii; ii < (int) p_data.size(); ii++ )
+                        {
+                            v_data[2]   = g_Parm_3D_Visu.m_ActZpos;
+                            v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                            v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                            gluTessVertex( jtess, v_data, &p_data[ii] );
+                        }
+
+                        gluTessEndContour( jtess );
+                    }
+                }
+            }
+
+            BOARD_ITEM* Struct;
+
+            for( Struct = Module->m_Drawings; Struct != NULL; Struct = Struct->Next() )
+            {
+                if( ( (DRAWSEGMENT*) Struct )->GetLayer() == llay )
+                {
+                    {
+                        double  w = ( (DRAWSEGMENT*) Struct )->GetWidth() *
+                                    g_Parm_3D_Visu.m_BoardScale;
+
+                        double  x = ( (DRAWSEGMENT*) Struct )->GetStart().x *
+                                    g_Parm_3D_Visu.m_BoardScale;
+                        double  y = ( (DRAWSEGMENT*) Struct )->GetStart().y *
+                                    g_Parm_3D_Visu.m_BoardScale;
+
+                        double  xf = ( (DRAWSEGMENT*) Struct )->GetEnd().x *
+                                     g_Parm_3D_Visu.m_BoardScale;
+                        double  yf = ( (DRAWSEGMENT*) Struct )->GetEnd().y *
+                                     g_Parm_3D_Visu.m_BoardScale;
+                        int     ii, lpii = p_data.size();
+
+                        switch( ( (DRAWSEGMENT*) Struct )->GetShape() )
+                        {
+                        case S_ARC:
+                            {
+                                Draw3D_ArcSegmentMask( x,
+                                                       -y,
+                                                       xf,
+                                                       -yf,
+                                                       ( (DRAWSEGMENT*) Struct )->GetAngle(),
+                                                       w,
+                                                       0,
+                                                       &p_data );
+                            }
+
+                            break;
+
+                        case S_CIRCLE:
+                            {
+                                Draw3D_CircleSegmentMask( x, -y, xf, -yf, w, 0, &p_data );
+                            }
+
+                            break;
+
+                        case S_POLYGON:
+                            {
+                                // We must compute true coordinates from m_PolyPoints
+                                // which are relative to module position and module orientation = 0
+                                EDGE_MODULE*            edge =
+                                    (EDGE_MODULE*) ( (DRAWSEGMENT*) Struct );
+                                std::vector<wxPoint>    points = edge->GetPolyPoints();
+                                MODULE* module = (MODULE*) edge->GetParent();
+
+                                if( Module == NULL )
+                                {
+                                    break;
+                                }
+
+                                for( int ii = points.size() - 1; ii >= 0; ii-- )
+                                {
+                                    wxPoint& pt = points[ii];
+
+                                    RotatePoint( &pt.x, &pt.y, module->GetOrientation() );
+                                    pt += module->m_Pos;
+                                    p_data.push_back( CPolyPt( (int) pt.x, (int) pt.y ) );
+                                }
+                            }
+
+                        default:
+                            {
+                                Draw3D_FilledSegmentMask( x, -y, xf, -yf, w, 0, &p_data, 1 );
+                            }
+
+                            break;
+                        }
+
+                        gluTessBeginContour( jtess );
+
+                        for( ii = p_data.size() - 1; ii >= lpii; ii-- )
+                        {
+                            if( p_data[ii].x == (int) MAGIC_CONT )
+                            {
+                                gluTessEndContour( jtess );
+                                gluTessBeginContour( jtess );
+                            }
+                            else
+                            {
+                                v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                                v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                                gluTessVertex( jtess, v_data, &p_data[ii] );
+                            }
+                        }
+
+                        gluTessEndContour( jtess );
+                    }
+                }
+            }
+        }
+
+        // render shape stuff...
+        EDA_ITEM* Struct;
+
+        for( Struct = pcb->m_Drawings; Struct != NULL; Struct = Struct->Next() )
+        {
+            if( ( (DRAWSEGMENT*) Struct )->GetLayer() == llay )
+            {
+                int ii, lpii = p_data.size();
+
+                switch( Struct->Type() )
+                {
+                case PCB_LINE_T:
+                    Draw3D_DrawSegmentMask( (DRAWSEGMENT*) Struct,
+                                            ( (DRAWSEGMENT*) Struct )->GetLayer(), &p_data );
+                    gluTessBeginContour( jtess );
+
+                    for( ii = p_data.size() - 1; ii >= lpii; ii-- )
+                    {
+                        if( p_data[ii].x == (int) MAGIC_CONT )
+                        {
+                            gluTessEndContour( jtess );
+                            gluTessBeginContour( jtess );
+                        }
+                        else
+                        {
+                            v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                            v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                            gluTessVertex( jtess, v_data, &p_data[ii] );
+                        }
+                    }
+
+                    gluTessEndContour( jtess );
+                    break;
+
+                case PCB_TEXT_T:
+                    Draw3D_DrawTextMask( (TEXTE_PCB*) Struct,
+                                         ( (DRAWSEGMENT*) Struct )->GetLayer(), jtess );
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        }
+
+        gluTessEndPolygon( jtess );
+
+        gluTessNormal( tess, 0, 0, 1 );
+
+        // subtract via holes
+        for( track = pcb->m_Track; track != NULL; track = track->Next() )
+        {
+            if( track->Type() == PCB_VIA_T )
+            {
+                double  x, y, r, hole;
+                double  height = 0;
+                int     lpii, ii;
+                int     top_layer;
+                int     bottom_layer;
+                ( (SEGVIA*) track )->ReturnLayerPair( &top_layer, &bottom_layer );
+
+                if( ( bottom_layer == 0 ) || ( top_layer == 15 ) )
+                {
+                    r = track->m_Width * g_Parm_3D_Visu.m_BoardScale / 2;
+                    hole    = track->GetDrillValue();
+                    hole    *= g_Parm_3D_Visu.m_BoardScale / 2;
+                    x       = track->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+                    y       = track->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessBeginContour( tess );
+                    lpii = p_data.size();
+                    Draw3D_FilledCylinderMask( x, -y, hole, height, g_Parm_3D_Visu.m_ActZpos );
+
+                    for( ii = lpii; ii < (int) p_data.size(); ii++ )
+                    {
+                        v_data[2]   = g_Parm_3D_Visu.m_ActZpos;
+                        v_data[0]   = (double) p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;;
+                        v_data[1]   = -(double) p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;;
+                        gluTessVertex( tess, v_data, &p_data[ii] );
+                    }
+
+                    gluTessEndContour( tess );
+                }
+                else
+                {
+                }
+            }
+        }
+
+        // subtract pad holes
+        tmonly = 2;
+        gluTessNormal( tess, 0, 0, 1 );
+
+        for( Module = pcb->m_Modules; Module != NULL; Module = Module->Next() )
+        {
+            D_PAD*  pad;
+            int     lpii;
+
+            for( pad = Module->m_Pads; pad != NULL; pad = pad->Next() )
+            {
+                lpii = p_data.size();
+                pad->Draw3D( this );
+
+                if( (int) p_data.size() > lpii )
+                {
+                    gluTessBeginContour( tess );
+
+                    for( ii = lpii; ii < (int) p_data.size(); ii++ )
+                    {
+                        v_data[2]   = g_Parm_3D_Visu.m_ActZpos;
+                        v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                        v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                        gluTessVertex( tess, v_data, &p_data[ii] );
+                    }
+
+                    gluTessEndContour( tess );
+                }
+            }
+        }
+
+        gluTessEndPolygon( tess );
+        gluDeleteTess( tess );
+        gluDeleteTess( jtess );
+        p_data.clear();
+        tmonly = 0;
     }
 
-    if (g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ZONE])
+    if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ZONE]
+        && !g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
     {
+        // primitive zone stuff
         // Draw segments used to fill copper areas. outdated!
         for( segzone = pcb->m_Zone; segzone != NULL; segzone = segzone->Next() )
         {
             if( segzone->Type() == PCB_ZONE_T )
+            {
                 Draw3D_Track( segzone );
+            }
         }
 
         // Draw new segments
@@ -258,7 +1327,7 @@
             if( curr_zone->m_FillMode == 0 )
             {
                 // solid polygons only are used to fill areas
-                if( curr_zone->GetFilledPolysList().size() > 3 )
+                if( curr_zone->GetFilledPolysList().size() > 2    /*3*/ )        // TSP_20120608
                 {
                     Draw3D_SolidPolygonsInZones( curr_zone );
                 }
@@ -269,13 +1338,14 @@
                 for( unsigned iseg = 0; iseg < curr_zone->m_FillSegmList.size(); iseg++ )
                 {
                     SEGZONE dummysegment( pcb );
+
                     dummysegment.SetLayer( curr_zone->GetLayer() );
                     dummysegment.m_Width = curr_zone->m_ZoneMinThickness;
 
-                    dummysegment.m_Start.x = curr_zone->m_FillSegmList[iseg].m_Start.x;
-                    dummysegment.m_Start.y = curr_zone->m_FillSegmList[iseg].m_Start.y;
-                    dummysegment.m_End.x   = curr_zone->m_FillSegmList[iseg].m_End.x;
-                    dummysegment.m_End.y   = curr_zone->m_FillSegmList[iseg].m_End.y;
+                    dummysegment.m_Start.x  = curr_zone->m_FillSegmList[iseg].m_Start.x;
+                    dummysegment.m_Start.y  = curr_zone->m_FillSegmList[iseg].m_Start.y;
+                    dummysegment.m_End.x    = curr_zone->m_FillSegmList[iseg].m_End.x;
+                    dummysegment.m_End.y    = curr_zone->m_FillSegmList[iseg].m_End.y;
                     Draw3D_Track( &dummysegment );
                 }
             }
@@ -284,9 +1354,9 @@
         // Draw copper areas outlines
         for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
         {
-            ZONE_CONTAINER* zone = pcb->GetArea( ii );
+            ZONE_CONTAINER*         zone = pcb->GetArea( ii );
 
-            std::vector<CPolyPt> polysList = zone->GetFilledPolysList();
+            std::vector<CPolyPt>    polysList = zone->GetFilledPolysList();
 
             if( polysList.size() == 0 )
                 continue;
@@ -294,10 +1364,11 @@
             if( zone->m_ZoneMinThickness <= 1 )
                 continue;
 
-            int      imax = polysList.size() - 1;
-            CPolyPt* firstcorner = &polysList[0];
-            CPolyPt* begincorner = firstcorner;
-            SEGZONE  dummysegment( pcb );
+            int         imax = polysList.size() - 1;
+            CPolyPt*    firstcorner = &polysList[0];
+            CPolyPt*    begincorner = firstcorner;
+            SEGZONE dummysegment( pcb );
+
             dummysegment.SetLayer( zone->GetLayer() );
             dummysegment.m_Width = zone->m_ZoneMinThickness;
 
@@ -308,10 +1379,10 @@
                 if( begincorner->utility == 0 )
                 {
                     // Draw only basic outlines, not extra segments
-                    dummysegment.m_Start.x = begincorner->x;
-                    dummysegment.m_Start.y = begincorner->y;
-                    dummysegment.m_End.x   = endcorner->x;
-                    dummysegment.m_End.y   = endcorner->y;
+                    dummysegment.m_Start.x  = begincorner->x;
+                    dummysegment.m_Start.y  = begincorner->y;
+                    dummysegment.m_End.x    = endcorner->x;
+                    dummysegment.m_End.y    = endcorner->y;
                     Draw3D_Track( &dummysegment );
                 }
 
@@ -321,10 +1392,10 @@
                     if( endcorner->utility == 0 )
                     {
                         // Draw only basic outlines, not extra segments
-                        dummysegment.m_Start.x = endcorner->x;
-                        dummysegment.m_Start.y = endcorner->y;
-                        dummysegment.m_End.x   = firstcorner->x;
-                        dummysegment.m_End.y   = firstcorner->y;
+                        dummysegment.m_Start.x  = endcorner->x;
+                        dummysegment.m_Start.y  = endcorner->y;
+                        dummysegment.m_End.x    = firstcorner->x;
+                        dummysegment.m_End.y    = firstcorner->y;
 
                         Draw3D_Track( &dummysegment );
                     }
@@ -342,32 +1413,2062 @@
         }
     }
 
-    // draw graphic items
-    EDA_ITEM* PtStruct;
-
-    for( PtStruct = pcb->m_Drawings;  PtStruct != NULL;  PtStruct = PtStruct->Next() )
-    {
-        switch( PtStruct->Type() )
-        {
-        case PCB_LINE_T:
-            Draw3D_DrawSegment( (DRAWSEGMENT*) PtStruct );
-            break;
-
-        case PCB_TEXT_T:
-            Draw3D_DrawText( (TEXTE_PCB*) PtStruct );
-            break;
-
-        default:
-            break;
-        }
-    }
-
-    // draw footprints
-    MODULE* Module = pcb->m_Modules;
-
-    for( ; Module != NULL; Module = Module->Next() )
-    {
-        Module->Draw3D( this );
+    MySetGLColor( g_ColorsSettings.GetLayerColor( LAYER_N_FRONT ), LAYER_N_FRONT,
+                  ( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] ) ? 1.0 : 0.5 );
+
+    for( track = pcb->m_Track; track != NULL; track = track->Next() )
+    {
+        // Vias just for the first layer....
+        if( track->Type() == PCB_VIA_T )
+        {
+            double  x, y, r, hole;
+            double  height = 0;
+            int     top_layer;
+            int     bottom_layer;
+            ( (SEGVIA*) track )->ReturnLayerPair( &top_layer, &bottom_layer );
+            height = g_Parm_3D_Visu.m_LayerZcoord[top_layer] -
+                     g_Parm_3D_Visu.m_LayerZcoord[bottom_layer];
+            r = track->m_Width * g_Parm_3D_Visu.m_BoardScale / 2;
+            hole    = track->GetDrillValue();
+            hole    *= g_Parm_3D_Visu.m_BoardScale / 2;
+            x       = track->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+            y       = track->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+
+            Draw3D_FilledCylinder( x, -y, hole, height,
+                                   g_Parm_3D_Visu.m_LayerZcoord[bottom_layer] );
+        }
+    }
+
+    if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
+    {
+        // draw graphic items
+        EDA_ITEM* PtStruct;
+
+        for( PtStruct = pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
+        {
+            if( ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == SILKSCREEN_N_FRONT )
+                || ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == SILKSCREEN_N_BACK ) )
+                continue;                                                                                                                               // TSP_20120606
+
+            if( ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == SOLDERMASK_N_BACK )
+                || ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == SOLDERMASK_N_FRONT ) )
+                continue;                                                                                                                               // TSP_20120606
+
+            if( ( (BOARD_ITEM*) PtStruct )->GetLayer() == EDGE_N )
+            {
+                switch( PtStruct->Type() )
+                {
+                case PCB_LINE_T:
+                    Draw3D_DrawSegment( (DRAWSEGMENT*) PtStruct );
+                    break;
+
+                case PCB_TEXT_T:
+                    Draw3D_DrawText( (TEXTE_PCB*) PtStruct );
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        }
+    }
+    else
+    {
+        // draw graphic items
+        EDA_ITEM* PtStruct;
+
+        for( PtStruct = pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
+        {
+            if( ( ( (BOARD_ITEM*) PtStruct )->GetLayer() <= LAST_COPPER_LAYER ) )
+                continue;                                                                    // TSP_20120606
+
+            {
+                switch( PtStruct->Type() )
+                {
+                case PCB_LINE_T:
+                    Draw3D_DrawSegment( (DRAWSEGMENT*) PtStruct );
+                    break;
+
+                case PCB_TEXT_T:
+                    Draw3D_DrawText( (TEXTE_PCB*) PtStruct );
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        }
+    }
+
+    std::vector<vector<CPolyPt> >   paths;
+    std::vector<vector<CPolyPt> >   vpaths;
+    std::vector<vector<CPolyPt> >   dpaths;
+    {
+        edge_data.reserve( MAGIC_RESERVE / 10 );
+        edge_data.clear();
+        xe_data.reserve( MAGIC_RESERVE / 10 );
+        xe_data.clear();
+        se_data.reserve( MAGIC_RESERVE / 10 );
+        se_data.clear();
+        {
+            TYPE_COLLECTOR          items;
+            static const KICAD_T    scanDRAWSEGMENTS[] = { PCB_LINE_T, EOT };
+
+            items.Collect( pcb, scanDRAWSEGMENTS );
+
+            bool haveEdges = false;
+
+            for( int i = 0; i<items.GetCount(); )
+            {
+                DRAWSEGMENT* item = (DRAWSEGMENT*) items[i];
+
+                wxASSERT( item->Type() == PCB_LINE_T );
+
+                if( item->GetLayer() != EDGE_N )
+                {
+                    items.Remove( i );
+                }
+                else
+                {
+                    haveEdges = true;
+                    ++i;
+                    D( item->Show( 0, std::cout ); )
+                }
+            }
+
+            if( haveEdges )
+            {
+                std::vector<CPolyPt>    path;
+
+                wxPoint         prevPt;
+
+                DRAWSEGMENT*    graphic;
+
+                // get outer polygon (xmin defines it, assume only "valid" edges
+                // find edge point wit minimal x
+                // emit this polygon...
+                wxPoint xmin    = wxPoint( 0x7fffffff, 0 );
+                int     xmini   = 0;
+
+                for( int i = 0; i < items.GetCount(); i++ )
+                {
+                    graphic = (DRAWSEGMENT*) items[i];
+
+                    switch( graphic->GetShape() )
+                    {
+                    case S_SEGMENT:
+                        {
+                            if( graphic->GetStart().x < xmin.x )
+                            {
+                                xmin    = graphic->GetStart();
+                                xmini   = i;
+                            }
+
+                            if( graphic->GetEnd().x < xmin.x )
+                            {
+                                xmin    = graphic->GetEnd();
+                                xmini   = i;
+                            }
+                        }
+                        break;
+
+                    case S_ARC:
+                        // freerouter does not yet understand arcs, so approximate
+                        // an arc with a series of short lines and put those
+                        // line segments into the !same! PATH.
+                        {
+                            const int   STEPS = 9;     // in an arc of 90 degrees
+
+                            wxPoint     start   = graphic->GetArcStart();
+                            wxPoint     end     = graphic->GetArcEnd();
+                            wxPoint     center  = graphic->GetCenter();
+                            double      angle   = -graphic->GetAngle();
+
+                            wxPoint     aPt;
+
+                            for( int step = 1; step<=STEPS; ++step )
+                            {
+                                double rotation = ( angle * step ) / STEPS;
+
+                                aPt = start;
+
+                                RotatePoint( &aPt.x, &aPt.y, center.x, center.y, rotation );
+
+                                if( aPt.x < xmin.x )
+                                {
+                                    xmin    = aPt;
+                                    xmini   = i;
+                                }
+                            }
+                        }
+                        break;
+
+                    case S_CIRCLE:
+                        // do not output a circle, freerouter does not understand it.
+                        // this might be a mounting hole or something, ignore it without error
+                        // because some of our demo boards have used the edges pcb layer to
+                        // hold islanded circles, rather than simply using holes.
+                        break;
+
+                    default:
+                        {
+                            wxString error;
+
+                            error.Printf( _( "Unsupported DRAWSEGMENT type %s" ),
+                                          GetChars( BOARD_ITEM::ShowShape(
+                                                        (STROKE_T) graphic->GetShape() ) ) );
+
+                            THROW_IO_ERROR( error );
+                        }
+                        break;
+                    }
+                }
+
+                // collect all enclosed sub polygons
+#define TWEAKER( a, b ) ( ( abs( a.x - b.x ) + abs( a.y - b.y ) ) < DLIM )
+                // the first DRAWSEGMENT is in 'graphic*', ok to remove it from 'items'
+                graphic = (DRAWSEGMENT*) items[xmini];
+                items.Remove( xmini );
+    #define mapPt( x ) x
+                wxPoint startPt = wxPoint( graphic->GetEnd() );
+                prevPt = graphic->GetEnd();
+                path.clear();
+                path.push_back( mapPt( prevPt ) );
+
+                // do not append the other end point yet, this first 'graphic' might be an arc
+                for( ; ; )
+                {
+                    switch( graphic->GetShape() )
+                    {
+                    case S_SEGMENT:
+                        {
+                            wxPoint nextPt;
+
+                            if( !TWEAKER( prevPt, graphic->GetStart() ) )
+                            {
+                                wxASSERT( TWEAKER( prevPt, graphic->GetEnd() ) );
+                                nextPt = graphic->GetStart();
+                            }
+                            else
+                            {
+                                wxASSERT( TWEAKER( prevPt, graphic->GetStart() ) );
+                                nextPt = graphic->GetEnd();
+                            }
+
+                            path.push_back( mapPt( nextPt ) );
+                            prevPt = nextPt;
+                        }
+                        break;
+
+                    case S_ARC:
+                        // freerouter does not yet understand arcs, so approximate
+                        // an arc with a series of short lines and put those
+                        // line segments into the !same! PATH.
+                        {
+                            const int   STEPS = 9;     // in an arc of 90 degrees
+
+                            wxPoint     start   = graphic->GetArcStart();
+                            wxPoint     end     = graphic->GetArcEnd();
+                            wxPoint     center  = graphic->GetCenter();
+                            double      angle   = -graphic->GetAngle();
+
+                            if( !TWEAKER( prevPt, start ) )
+                            {
+                                wxASSERT( TWEAKER( prevPt, graphic->GetArcEnd() ) );
+
+                                angle = -angle;
+                                EXCHG( start, end );
+                            }
+
+                            wxPoint nextPt;
+
+                            for( int step = 1; step<=STEPS; ++step )
+                            {
+                                double rotation = ( angle * step ) / STEPS;
+
+                                nextPt = start;
+
+                                RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
+
+                                path.push_back( mapPt( nextPt ) );
+                            }
+
+                            prevPt = nextPt;
+                        }
+                        break;
+
+                    case S_CIRCLE:
+                    // Do not output a circle, freerouter does not understand it.
+                    // tell user his board has a problem, this is better than silently
+                    // ignoring the error. "edges pcb" layer should not be used
+                    // to hold islanded circles which could or should be better done
+                    // as simple holes. (Some of our demo boards have this problem.)
+                    // fall thru here to report the error.
+
+                    default:
+                        {
+                            wxString error;
+
+                            error.Printf( _( "Unsupported DRAWSEGMENT type %s" ),
+                                          GetChars( BOARD_ITEM::ShowShape(
+                                                        (STROKE_T) graphic->GetShape() ) ) );
+
+                            THROW_IO_ERROR( error );
+                        }
+                        break;
+                    }
+
+                    if( TWEAKER( startPt, prevPt ) )
+                        break;
+
+                    graphic = findPoint( prevPt, &items );
+
+                    if( !graphic )
+                    {
+                        wxString error;
+
+                        error << _( "Unable to find the next segment with an endpoint of " );
+                        error << prevPt.x;
+                        error << wxChar( ',' );
+                        error << prevPt.y;
+                        error << wxChar( '\n' );
+                        error << _( "Edit Edges_Pcb segments, making them contiguous." );
+                        THROW_IO_ERROR( error );
+                    }
+                }
+
+                paths.push_back( path );
+
+                while( items.GetCount() )
+                {
+                    // emit a signal layers keepout for every sub-polygon left...
+                    graphic = (DRAWSEGMENT*) items[0];
+                    items.Remove( 0 );
+
+                    wxPoint startPt = wxPoint( graphic->GetEnd() );
+                    prevPt = graphic->GetEnd();
+                    path.clear();
+                    path.push_back( mapPt( prevPt ) );
+
+                    // do not append the other end point yet, this first 'graphic' might be an arc
+                    for( ; ; )
+                    {
+                        switch( graphic->GetShape() )
+                        {
+                        case S_SEGMENT:
+                            {
+                                wxPoint nextPt;
+
+                                if( !TWEAKER( prevPt, graphic->GetStart() ) )
+                                {
+                                    wxASSERT( TWEAKER( prevPt, graphic->GetEnd() ) );
+                                    nextPt = graphic->GetStart();
+                                }
+                                else
+                                {
+                                    wxASSERT( TWEAKER( prevPt, graphic->GetStart() ) );
+                                    nextPt = graphic->GetEnd();
+                                }
+
+                                prevPt = nextPt;
+                                path.push_back( mapPt( prevPt ) );
+                            }
+                            break;
+
+                        case S_ARC:
+                            // freerouter does not yet understand arcs, so approximate
+                            // an arc with a series of short lines and put those
+                            // line segments into the !same! PATH.
+                            {
+                                const int   STEPS = 9;     // in an arc of 90 degrees
+
+                                wxPoint     start   = graphic->GetArcStart();
+                                wxPoint     end     = graphic->GetArcEnd();
+                                wxPoint     center  = graphic->GetCenter();
+                                double      angle   = -graphic->GetAngle();
+
+                                if( !TWEAKER( prevPt, start ) )
+                                {
+                                    wxASSERT( TWEAKER( prevPt, graphic->GetArcEnd() ) );
+
+                                    angle = -angle;
+                                    EXCHG( start, end );
+                                }
+
+                                wxPoint nextPt;
+
+                                for( int step = 1; step<=STEPS; ++step )
+                                {
+                                    double rotation = ( angle * step ) / STEPS;
+
+                                    nextPt = start;
+
+                                    RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y,
+                                                 rotation );
+
+                                    path.push_back( mapPt( nextPt ) );
+                                }
+
+                                prevPt = nextPt;
+                            }
+                            break;
+
+                        case S_CIRCLE:
+                            // do a circle segmentation
+                            {
+                                const int   STEPS = 9 * 4;   // in an arc of 90 degrees
+
+                                wxPoint     start;
+                                wxPoint     center  = graphic->GetCenter();
+                                int         iradius = graphic->GetRadius();
+                                double      angle   = 3600.0;
+
+                                start   = center;
+                                start.x += iradius;
+
+                                wxPoint nextPt;
+
+                                for( int step = 1; step<=STEPS; ++step )
+                                {
+                                    double rotation = ( angle * step ) / STEPS;
+
+                                    nextPt = start;
+
+                                    RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y,
+                                                 rotation );
+
+                                    path.push_back( mapPt( nextPt ) );
+                                }
+
+                                prevPt = startPt;
+                            }
+                            break;
+
+                        default:
+                            {
+                                wxString error;
+
+                                error.Printf( _( "Unsupported DRAWSEGMENT type %s" ),
+                                              GetChars( BOARD_ITEM::ShowShape(
+                                                            (STROKE_T) graphic->GetShape() ) ) );
+
+                                THROW_IO_ERROR( error );
+                            }
+                            break;
+                        }
+
+                        if( TWEAKER( startPt, prevPt ) )
+                            break;
+
+                        graphic = findPoint( prevPt, &items );
+
+                        if( !graphic )
+                        {
+                            wxString error;
+
+                            error << _( "Unable to find the next segment with an endpoint of " );
+                            error << prevPt.x;
+                            error << wxChar( ',' );
+                            error << prevPt.y;
+                            error << wxChar( '\n' );
+                            error << _( "Edit Edges_Pcb segments, making them contiguous." );
+                            THROW_IO_ERROR( error );
+                        }
+                    }
+
+                    paths.push_back( path );
+                }
+
+                for( unsigned int i = 0; i < paths.size(); i++ )
+                {
+                    int ori = orient( paths[i] );
+
+                    if( ori >= 0 )
+                    {
+                        for( int j = 0; j < (int) paths[i].size(); j++ )
+                        {
+                            edge_data.push_back( paths[i][j] );
+                        }
+
+                        edge_data.push_back( CPolyPt( (int) MAGIC_CONT, 0, true, 0 ) );
+                    }
+                    else
+                    {
+                        std::vector<CPolyPt> hpath;
+
+                        for( int j = (int) paths[i].size() - 1; j >= 0; j-- )
+                        {
+                            hpath.push_back( paths[i][j] );
+                            edge_data.push_back( paths[i][j] );
+                        }
+
+                        edge_data.push_back( CPolyPt( (int) MAGIC_CONT, 0, true, 0 ) );
+                        paths[i] = hpath;
+                    }
+                }
+
+                // now paths holds splitted edge (+)polygons, paths[0] is outer edge...
+                TYPE_COLLECTOR drill_items;
+                wxSize noDrill  = wxSize( 0, 0 );
+                bool haveDrills = false;
+                static const KICAD_T scanDRILLSEGMENTS[] = { PCB_PAD_T, EOT };
+
+                drill_items.Collect( pcb, scanDRILLSEGMENTS );
+
+                for( int i = 0; i<drill_items.GetCount(); )
+                {
+                    wxSize sz = ( (D_PAD*) drill_items[i] )->GetDrillSize();
+
+                    if( (sz.x + sz.y) <= 0 )
+                    {
+                        drill_items.Remove( i );
+                    }
+                    else
+                    {
+                        haveDrills = true;
+                        ++i;
+                        D( drill_item->Show( 0, std::cout ); )
+                    }
+                }
+
+                for( int i = 0; i < drill_items.GetCount(); i++ )
+                {
+                    D_PAD*  pad     = ( (D_PAD*) drill_items[i] );
+                    wxSize  sz      = ( (D_PAD*) drill_items[i] )->GetDrillSize();
+                    wxPoint pos     = ( (D_PAD*) drill_items[i] )->GetPosition();
+                    double  ang     = ( (D_PAD*) drill_items[i] )->GetOrientation();
+                    MODULE* parent  = ( (D_PAD*) drill_items[i] )->GetParent();
+                    double  pang    = -1;
+                    double  tang;
+                    int     iradius;
+                    int     xoff;
+                    std::vector<CPolyPt> lpath;
+
+                    // if PTH --> increase size by 100 µ
+                    if( pad->GetAttribute() == PAD_STANDARD )
+                    {
+                        // XXX
+                        sz.x    += 2 * COPPER_EXTEND;
+                        sz.y    += 2 * COPPER_EXTEND;
+                    }
+
+                    if( parent != NULL )
+                    {
+                        pang = parent->GetOrientation();
+                    }
+
+                    tang = ang;
+
+                    if( sz.x < sz.y )
+                    {
+                        // more y --> flip and add 90 deg
+                        tang    += 900;
+                        iradius = sz.x / 2;
+                        xoff    = (sz.y - sz.x) / 2;
+                    }
+                    else
+                    {
+                        iradius = sz.y / 2;
+                        xoff    = (sz.x - sz.y) / 2;
+                    }
+
+                    for( int j = 0; j < 18; j++ )
+                    {
+                        int x   = 0;
+                        int y   = -iradius;
+                        RotatePoint( &x, &y, 0, 0, -j * 200 );
+                        x += (j < 9) ? xoff : -xoff;
+                        RotatePoint( &x, &y, 0, 0, tang );
+                        edge_data.push_back( CPolyPt( pos.x + x, pos.y + y, false, 0 ) );
+                        lpath.push_back( CPolyPt( pos.x + x, pos.y + y ) );
+                    }
+
+                    if( 0 )
+                    {
+                        vpaths.push_back( lpath );
+                    }
+                    else
+                    {
+                        dpaths.push_back( lpath );
+                    }
+
+                    edge_data.push_back( CPolyPt( (int) MAGIC_CONT, 0, true, 0 ) );
+
+                    if( sz.x != sz.y )
+                    {
+                    }
+                }
+
+                TYPE_COLLECTOR via_items;
+                static const KICAD_T scanVIASEGMENTS[] = { PCB_VIA_T, EOT };
+                bool haveVias = false;
+
+                via_items.Collect( pcb, scanVIASEGMENTS );
+
+                for( int i = 0; i<via_items.GetCount(); )
+                {
+                    wxSize  sz;
+                    int     top_layer;
+                    int     bottom_layer;
+                    ( (SEGVIA*) via_items[i] )->ReturnLayerPair( &top_layer, &bottom_layer );
+                    sz.x    = ( (SEGVIA*) via_items[i] )->GetDrillValue();
+                    sz.y    = sz.x;
+
+                    if( (sz.x + sz.y) <= 0 )
+                    {
+                        via_items.Remove( i );
+                    }
+                    else if( ( bottom_layer > 0 ) && ( top_layer < 15 ) )
+                    {
+                        via_items.Remove( i );
+                    }
+                    else
+                    {
+                        haveVias = true;
+                        ++i;
+                        D( drill_item->Show( 0, std::cout ); )
+                    }
+                }
+
+                for( int i = 0; i < via_items.GetCount(); i++ )
+                {
+                    wxSize  sz;
+                    wxPoint pos;
+                    int     iradius;
+                    std::vector<CPolyPt> lpath;
+
+                    sz.x    = ( (SEGVIA*) via_items[i] )->GetDrillValue() + 2 * COPPER_EXTEND;
+                    sz.y    = sz.x;
+                    iradius = sz.x / 2;
+                    pos     = ( (SEGVIA*) via_items[i] )->GetStart();
+
+                    // if PTH --> increase size by 100 µ
+                    for( int j = 0; j < 2 * C_SLICE; j++ )
+                    {
+                        int x   = 0;
+                        int y   = -iradius;
+                        RotatePoint( &x, &y, 0, 0, -j * _SEG2( C_SLICE ) );
+                        edge_data.push_back( CPolyPt( pos.x + x, pos.y + y, false, 0 ) );
+                        lpath.push_back( CPolyPt( pos.x + x, pos.y + y ) );
+                    }
+
+                    vpaths.push_back( lpath );
+                    edge_data.push_back( CPolyPt( (int) MAGIC_CONT, 0, true, 0 ) );
+                }
+            }
+            else
+            {
+                wxString error;
+                error << _( "No Edges, create some..." );
+            }
+        }
+
+        if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( EDGE_N ) )
+        {
+            GLdouble v_data[3];
+
+            // top face...
+            if( paths.size() )
+            {
+                tess = gluNewTess();
+                g_Parm_3D_Visu.m_ActZpos =
+                    g_Parm_3D_Visu.m_LayerZcoord[(g_Parm_3D_Visu.m_Layers -
+                                                  1)] - 1.0 * g_Parm_3D_Visu.m_BoardScale;                       // TSP_20120610
+                v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+                MySetGLColor( g_ColorsSettings.GetLayerColor( EDGE_N ), EDGE_N,
+                              ( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] ) ? 1.0 : 0.5 );
+                glNormal3f( 0.0, 0.0, 1.0 );
+                gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+                gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+                gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+                gluTessCallback( tess, GLU_TESS_VERTEX,
+                                 ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+                gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+                gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD );
+                GLUtesselator* jtess = gluNewTess();
+                gluTessCallback( jtess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )my2tessBeginCB );
+                gluTessCallback( jtess, GLU_TESS_END, ( void (CALLBACK*) () )my2tessEndCB );
+                gluTessCallback( jtess, GLU_TESS_ERROR, ( void (CALLBACK*) () )my2tessErrorCB );
+                gluTessCallback( jtess, GLU_TESS_VERTEX,
+                                 ( void (CALLBACK*) () )my2tessCPolyPt2Vertex );
+                gluTessCallback( jtess, GLU_TESS_COMBINE,
+                                 ( void (CALLBACK*) () )my2tessCombineCB );
+                gluTessProperty( jtess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE );
+                gluTessProperty( jtess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+                gluTessBeginPolygon( jtess, NULL );
+                gluTessNormal( jtess, 0, 0, -1 );
+                gluTessBeginPolygon( tess, NULL );
+
+                int fpol = 1;
+                gluTessBeginContour( tess );
+                gluTessBeginContour( jtess );
+
+                for( ii = 0; ii < (int) edge_data.size(); ii++ )
+                {
+                    if( edge_data[ii].x != (int) MAGIC_CONT )
+                    {
+                        v_data[0]   = edge_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                        v_data[1]   = -edge_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+
+                        if( fpol )
+                        {
+                            gluTessVertex( tess, v_data, &edge_data[ii] );
+                        }
+                        else
+                        {
+                            gluTessVertex( jtess, v_data, &edge_data[ii] );
+                        }
+                    }
+                    else
+                    {
+                        if( fpol )
+                        {
+                            fpol = 0;
+                            gluTessEndContour( tess );
+                            gluTessBeginContour( tess );
+                        }
+                        else
+                        {
+                            gluTessEndContour( jtess );
+                            gluTessBeginContour( jtess );
+                        }
+                    }
+                }
+
+                gluTessEndContour( jtess );
+                gluTessEndPolygon( jtess );
+
+                bpii = p_data.size();
+                gluTessEndPolygon( tess );
+                gluDeleteTess( tess );
+            }
+
+            // back face...
+            if( paths.size() )
+            {
+                tess = gluNewTess();
+                g_Parm_3D_Visu.m_ActZpos =
+                    g_Parm_3D_Visu.m_LayerZcoord[0] + 1.0 * g_Parm_3D_Visu.m_BoardScale; // TSP_20120610
+                v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+                MySetGLColor( g_ColorsSettings.GetLayerColor( EDGE_N ), EDGE_N,
+                              ( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] ) ? 1.0 : 0.5 );
+                glNormal3f( 0.0, 0.0, -1.0 );
+                gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+                gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+                gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+                gluTessCallback( tess, GLU_TESS_VERTEX,
+                                 ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+                gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+                gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD );
+                gluTessBeginPolygon( tess, NULL );
+
+                gluTessBeginContour( tess );
+
+                for( ii = 0; ii < (int) edge_data.size(); ii++ )
+                {
+                    if( edge_data[ii].x == (int) MAGIC_CONT )
+                        break;
+
+                    v_data[0]   = edge_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                    v_data[1]   = -edge_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessVertex( tess, v_data, &edge_data[ii] );
+                }
+
+                int need_c = 1;
+
+                for( ii = 0; ii < (int) xe_data.size(); ii++ )
+                {
+                    if( xe_data[ii].x == (int) MAGIC_CONT )
+                    {
+                        need_c = 1;
+                    }
+                    else
+                    {
+                        if( need_c )
+                        {
+                            need_c = 0;
+                            gluTessEndContour( tess );
+                            gluTessBeginContour( tess );
+                        }
+
+                        v_data[0]   = xe_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                        v_data[1]   = -xe_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                        gluTessVertex( tess, v_data, &xe_data[ii] );
+                    }
+                }
+
+                gluTessEndContour( tess );
+                gluTessEndPolygon( tess );
+                gluDeleteTess( tess );
+            }
+        }
+
+        // draw pcb edges...
+        if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] && paths.size() )
+        {
+            std::vector<S3D_Vertex> coords;
+            coords.resize( 4 );
+            int     si  = 0;
+            double  tmp = DataScale3D;
+            DataScale3D = 1.0;    // Coordinate is already in range for Set_Object_Data();
+            MySetGLColor( g_ColorsSettings.GetLayerColor( EDGE_N ), EDGE_N,
+                          ( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] ) ? 1.0 : 0.2 );
+
+            for( ii = 0; ii < (int) edge_data.size(); ii++ )
+            {
+                if( edge_data[ii].x == (int) MAGIC_CONT )
+                {
+                    if( si < ii - 1 )
+                    {
+                        coords[0].x = coords[1].x =
+                                          (double) edge_data[ii -
+                                                             1].x * g_Parm_3D_Visu.m_BoardScale;
+                        coords[0].y = coords[1].y =
+                                          -(double) edge_data[ii -
+                                                              1].y * g_Parm_3D_Visu.m_BoardScale;
+                        coords[2].x = coords[3].x = (double) edge_data[si].x *
+                                                    g_Parm_3D_Visu.m_BoardScale;
+                        coords[2].y = coords[3].y = -(double) edge_data[si].y *
+                                                    g_Parm_3D_Visu.m_BoardScale;
+                        coords[0].z = coords[3].z = g_Parm_3D_Visu.m_LayerZcoord[0] + 1.0 *
+                                                    g_Parm_3D_Visu.m_BoardScale;                                         // TSP_20120610
+                        coords[1].z = coords[2].z =
+                                          g_Parm_3D_Visu.m_LayerZcoord[(g_Parm_3D_Visu.m_Layers -
+                                                                        1)] - 1.0 *
+                                          g_Parm_3D_Visu.m_BoardScale;                                                                               // TSP_20120610
+                        {
+                            Set_Object_Data( coords );
+                        }
+
+                        for( int jj = si + 1; jj < ii; jj++ )
+                        {
+                            coords[0].x = coords[2].x;
+                            coords[0].y = coords[2].y;
+                            coords[1].x = coords[3].x;
+                            coords[1].y = coords[3].y;
+                            coords[2].x = coords[3].x = (double) edge_data[jj].x *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                            coords[2].y = coords[3].y = -(double) edge_data[jj].y *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                            {
+                                Set_Object_Data( coords );
+                            }
+                        }
+                    }
+
+                    si = ii + 1;
+                    break;
+                }
+            }
+
+            si = 0;
+
+            for( ii = 0; ii < (int) xe_data.size(); ii++ )
+            {
+                if( xe_data[ii].x == (int) MAGIC_CONT )
+                {
+                    if( si < ii - 1 )
+                    {
+                        coords[0].x = coords[1].x =
+                                          (double) xe_data[ii - 1].x * g_Parm_3D_Visu.m_BoardScale;
+                        coords[0].y = coords[1].y =
+                                          -(double) xe_data[ii -
+                                                            1].y * g_Parm_3D_Visu.m_BoardScale;
+                        coords[2].x = coords[3].x = (double) xe_data[si].x *
+                                                    g_Parm_3D_Visu.m_BoardScale;
+                        coords[2].y = coords[3].y = -(double) xe_data[si].y *
+                                                    g_Parm_3D_Visu.m_BoardScale;
+
+                        if( (xe_data[ii - 1].utility) && (xe_data[si].utility) )
+                        {
+                            Set_Object_Data( coords );
+                        }
+
+                        for( int jj = si + 1; jj < ii; jj++ )
+                        {
+                            coords[0].x = coords[2].x;
+                            coords[0].y = coords[2].y;
+                            coords[1].x = coords[3].x;
+                            coords[1].y = coords[3].y;
+                            coords[2].x = coords[3].x = (double) xe_data[jj].x *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                            coords[2].y = coords[3].y = -(double) xe_data[jj].y *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+
+                            if( (xe_data[jj - 1].utility) && (xe_data[jj].utility) )
+                            {
+                                Set_Object_Data( coords );
+                            }
+                        }
+                    }
+
+                    si = ii + 1;
+                }
+            }
+
+            DataScale3D = tmp;    // Coordinate is already in range for Set_Object_Data();
+        }
+
+        // draw footprints...
+        tmonly = 0;    // TSP_20120605
+
+        for( Module = pcb->m_Modules; Module != NULL; Module = Module->Next() )
+        {
+            Module->Draw3D( this );           // TSP_20120605
+        }
+
+        tmonly = 0;    // TSP_20120605
+
+        // draw top mask (if in rendering mode)
+        if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER]
+            && g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( SOLDERMASK_N_FRONT )
+            && paths.size() )
+        {
+            GLdouble v_data[3];
+            p_data.erase( p_data.begin() + bpii, p_data.end() );
+            tess = gluNewTess();
+            g_Parm_3D_Visu.m_ActZpos =
+                g_Parm_3D_Visu.m_LayerZcoord[SOLDERMASK_N_FRONT];
+            v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+            MySetGLColor( g_ColorsSettings.GetLayerColor(
+                              SOLDERMASK_N_FRONT ), SOLDERMASK_N_FRONT,
+                          ( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] ) ? 0.8 : 0 );
+            glNormal3f( 0.0, 0.0, 1.0 );
+            gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+            gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+            gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+            gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+            gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+            gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+            gluTessBeginPolygon( tess, NULL );
+
+            gluTessBeginContour( tess );
+
+            for( ii = 0; ii < (int) paths[0].size(); ii++ )
+            {
+                v_data[0]   = paths[0][ii].x * g_Parm_3D_Visu.m_BoardScale;
+                v_data[1]   = -paths[0][ii].y * g_Parm_3D_Visu.m_BoardScale;
+                gluTessVertex( tess, v_data, &paths[0][ii] );
+            }
+
+            gluTessEndContour( tess );
+
+            for( unsigned i = 1; i < paths.size(); i++ )
+            {
+                gluTessBeginContour( tess );
+
+                for( int j = (int) paths[i].size() - 1; j >= 0; j-- )
+                {
+                    v_data[0]   = paths[i][j].x * g_Parm_3D_Visu.m_BoardScale;
+                    v_data[1]   = -paths[i][j].y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessVertex( tess, v_data, &paths[i][j] );
+                }
+
+                gluTessEndContour( tess );
+            }
+
+            for( unsigned i = 0; i < dpaths.size(); i++ )
+            {
+                gluTessBeginContour( tess );
+
+                for( int j = (int) dpaths[i].size() - 1; j >= 0; j-- )
+                {
+                    v_data[0]   = dpaths[i][j].x * g_Parm_3D_Visu.m_BoardScale;
+                    v_data[1]   = -dpaths[i][j].y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessVertex( tess, v_data, &dpaths[i][j] );
+                }
+
+                gluTessEndContour( tess );
+            }
+
+            // redo for top mask....
+            tmonly = 1;
+
+            // pad mask
+            for( Module = pcb->m_Modules; Module != NULL; Module = Module->Next() )
+            {
+                D_PAD*  pad;
+                int     lpii;
+
+                for( pad = Module->m_Pads; pad != NULL; pad = pad->Next() )
+                {
+                    lpii = p_data.size();
+                    pad->Draw3D( this );
+
+                    if( (int) p_data.size() > lpii )
+                    {
+                        gluTessBeginContour( tess );
+
+                        for( ii = (int) p_data.size() - 1; ii >= lpii; ii-- )
+                        {
+                            v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                            v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                            gluTessVertex( tess, v_data, &p_data[ii] );
+                        }
+
+                        gluTessEndContour( tess );
+                    }
+                }
+
+                // draw rest of module mask
+                EDA_ITEM* Struct = Module->m_Drawings;
+
+                for( ; Struct != NULL; Struct = Struct->Next() )
+                {
+                    if( ( ( (BOARD_ITEM*) Struct )->GetLayer() == SOLDERMASK_N_FRONT ) )
+                    {
+                        int ii, lpii = p_data.size();
+
+                        switch( Struct->Type() )
+                        {
+                        case PCB_MODULE_TEXT_T:
+                            break;
+
+                        case PCB_MODULE_EDGE_T:
+                            {
+                                // Draw module edges when no 3d shape exists.
+                                // Always draw pcb edges.
+                                /* if( !As3dShape || edge->GetLayer() == EDGE_N ) */
+                                {
+                                    Draw3D_DrawSegmentMask( (DRAWSEGMENT*) Struct,
+                                                            SOLDERMASK_N_FRONT, &p_data );
+                                    gluTessBeginContour( tess );
+
+                                    for( ii = (int) p_data.size() - 1; ii >= lpii; ii-- )
+                                    {
+                                        if( p_data[ii].x == (int) MAGIC_CONT )
+                                        {
+                                            gluTessEndContour( tess );
+                                            gluTessBeginContour( tess );
+                                        }
+                                        else
+                                        {
+                                            v_data[0] = p_data[ii].x *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            v_data[1] = -p_data[ii].y *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            gluTessVertex( tess, v_data, &p_data[ii] );
+                                        }
+                                    }
+
+                                    gluTessEndContour( tess );
+                                }
+                            }
+                            break;
+
+                        default:
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // draw mask graphic items
+            EDA_ITEM* PtStruct;
+
+            for( PtStruct = pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
+            {
+                if( ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == SOLDERMASK_N_FRONT ) )
+                {
+                    int ii, lpii = p_data.size();
+
+                    switch( PtStruct->Type() )
+                    {
+                    case PCB_LINE_T:
+                        Draw3D_DrawSegmentMask( (DRAWSEGMENT*) PtStruct, SOLDERMASK_N_FRONT,
+                                                &p_data );
+                        break;
+
+                    case PCB_TEXT_T:
+                        Draw3D_DrawTextMask( (TEXTE_PCB*) PtStruct, SOLDERMASK_N_FRONT, NULL );
+                        break;
+
+                    default:
+                        break;
+                    }
+
+                    gluTessBeginContour( tess );
+
+                    for( ii = lpii; ii < (int) p_data.size(); ii++ )
+                    {
+                        if( p_data[ii].x == (int) MAGIC_CONT )
+                        {
+                            gluTessEndContour( tess );
+                            gluTessBeginContour( tess );
+                        }
+                        else
+                        {
+                            v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                            v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                            gluTessVertex( tess, v_data, &p_data[ii] );
+                        }
+                    }
+
+                    gluTessEndContour( tess );
+                }
+            }
+
+            // draw mask zone items
+            {
+                // Draw segments used to fill copper areas. outdated!
+                for( segzone = pcb->m_Zone; segzone != NULL; segzone = segzone->Next() )
+                {
+                    if( segzone->GetLayer() == SOLDERMASK_N_FRONT )
+                        if( segzone->Type() == PCB_ZONE_T )
+                        {
+                            Draw3D_Track( segzone );
+                        }
+
+
+                }
+
+                // Draw new segments
+                for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+                {
+                    ZONE_CONTAINER* curr_zone = pcb->GetArea( ii );
+
+                    if( curr_zone->GetLayer() == SOLDERMASK_N_FRONT )
+                    {
+                        if( curr_zone->m_FillMode == 0 )
+                        {
+                            // solid polygons only are used to fill areas
+                            if( curr_zone->GetFilledPolysList().size() > 2 )
+                            {
+                                GLdouble v_data[3];
+                                v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+
+                                // Draw solid areas contained in this zone
+                                int lpii = 0;
+
+                                std::vector<CPolyPt> polysList = curr_zone->GetFilledPolysList();
+
+                                for( unsigned ii = 0; ii < polysList.size(); ii++ )
+                                {
+                                    if( polysList[ii].end_contour == 1 )
+                                    {
+                                        int jj;
+                                        // reverse last polygon
+                                        gluTessBeginContour( tess );
+
+                                        for( jj = lpii; jj <= (int) ii; jj++ )
+                                        {
+                                            p_data.push_back( polysList[jj] );
+                                            v_data[0] = polysList[jj].x *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            v_data[1] = -polysList[jj].y *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            gluTessVertex( tess, v_data,
+                                                           &p_data[p_data.size() - 1] );
+                                        }
+
+                                        gluTessEndContour( tess );
+                                        lpii = ii + 1;
+                                    }
+                                }
+                            }
+                        }
+                        else
+                        {
+                            // segments are used to fill areas
+                            for( unsigned iseg = 0;
+                                 iseg < curr_zone->m_FillSegmList.size();
+                                 iseg++ )
+                            {
+                                SEGZONE dummysegment( pcb );
+
+                                dummysegment.SetLayer( curr_zone->GetLayer() );
+                                dummysegment.m_Width = curr_zone->m_ZoneMinThickness;
+
+                                dummysegment.m_Start.x =
+                                    curr_zone->m_FillSegmList[iseg].m_Start.x;
+                                dummysegment.m_Start.y =
+                                    curr_zone->m_FillSegmList[iseg].m_Start.y;
+                                dummysegment.m_End.x    = curr_zone->m_FillSegmList[iseg].m_End.x;
+                                dummysegment.m_End.y    = curr_zone->m_FillSegmList[iseg].m_End.y;
+                                Draw3D_Track( &dummysegment );
+                            }
+                        }
+                    }
+                }
+
+                // Draw copper areas outlines
+                for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+                {
+                    ZONE_CONTAINER* zone = pcb->GetArea( ii );
+
+                    if( zone->GetLayer() == SOLDERMASK_N_FRONT )
+                    {
+                        std::vector<CPolyPt> polysList = zone->GetFilledPolysList();
+
+                        if( polysList.size() == 0 )
+                            continue;
+
+                        if( zone->m_ZoneMinThickness <= 1 )
+                            continue;
+
+                        int         imax = polysList.size() - 1;
+                        CPolyPt*    firstcorner = &polysList[0];
+                        CPolyPt*    begincorner = firstcorner;
+                        SEGZONE dummysegment( pcb );
+
+                        dummysegment.SetLayer( zone->GetLayer() );
+                        dummysegment.m_Width = zone->m_ZoneMinThickness;
+
+                        for( int ic = 1; ic <= imax; ic++ )
+                        {
+                            CPolyPt* endcorner = &polysList[ic];
+
+                            if( begincorner->utility == 0 )
+                            {
+                                // Draw only basic outlines, not extra segments
+                                dummysegment.m_Start.x  = begincorner->x;
+                                dummysegment.m_Start.y  = begincorner->y;
+                                dummysegment.m_End.x    = endcorner->x;
+                                dummysegment.m_End.y    = endcorner->y;
+                                Draw3D_Track( &dummysegment );
+                            }
+
+                            if( (endcorner->end_contour) || (ic == imax) )
+                            {
+                                // the last corner of a filled area is found: draw it
+                                if( endcorner->utility == 0 )
+                                {
+                                    // Draw only basic outlines, not extra segments
+                                    dummysegment.m_Start.x  = endcorner->x;
+                                    dummysegment.m_Start.y  = endcorner->y;
+                                    dummysegment.m_End.x    = firstcorner->x;
+                                    dummysegment.m_End.y    = firstcorner->y;
+
+                                    Draw3D_Track( &dummysegment );
+                                }
+
+                                ic++;
+
+                                if( ic < imax - 1 )
+                                    begincorner = firstcorner = &polysList[ic];
+                            }
+                            else
+                            {
+                                begincorner = endcorner;
+                            }
+                        }
+                    }
+                }
+            }
+
+            gluTessEndPolygon( tess );
+            gluDeleteTess( tess );
+        }
+
+        // draw back mask
+        if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER]
+            && g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( SOLDERMASK_N_BACK )
+            && paths.size() )
+        {
+            GLdouble v_data[3];
+            p_data.erase( p_data.begin() + bpii, p_data.end() );
+            // bottom mask
+            tess = gluNewTess();
+            g_Parm_3D_Visu.m_ActZpos =
+                g_Parm_3D_Visu.m_LayerZcoord[SOLDERMASK_N_BACK];
+            v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+            MySetGLColor( g_ColorsSettings.GetLayerColor( SOLDERMASK_N_BACK ), SOLDERMASK_N_BACK,
+                          ( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] ) ? 0.8 : 0 );
+            glNormal3f( 0.0, 0.0, -1.0 );
+            gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+            gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+            gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+            gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+            gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+            gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+            gluTessBeginPolygon( tess, NULL );
+            gluTessBeginContour( tess );
+
+            for( ii = 0; ii < (int) paths[0].size(); ii++ )
+            {
+                v_data[0]   = paths[0][ii].x * g_Parm_3D_Visu.m_BoardScale;
+                v_data[1]   = -paths[0][ii].y * g_Parm_3D_Visu.m_BoardScale;
+                gluTessVertex( tess, v_data, &paths[0][ii] );
+            }
+
+            gluTessEndContour( tess );
+
+            for( unsigned i = 1; i < paths.size(); i++ )
+            {
+                gluTessBeginContour( tess );
+
+                for( int j = (int) paths[i].size() - 1; j >= 0; j-- )
+                {
+                    v_data[0]   = paths[i][j].x * g_Parm_3D_Visu.m_BoardScale;
+                    v_data[1]   = -paths[i][j].y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessVertex( tess, v_data, &paths[i][j] );
+                }
+
+                gluTessEndContour( tess );
+            }
+
+            for( unsigned i = 0; i < dpaths.size(); i++ )
+            {
+                gluTessBeginContour( tess );
+
+                for( int j = (int) dpaths[i].size() - 1; j >= 0; j-- )
+                {
+                    v_data[0]   = dpaths[i][j].x * g_Parm_3D_Visu.m_BoardScale;
+                    v_data[1]   = -dpaths[i][j].y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessVertex( tess, v_data, &dpaths[i][j] );
+                }
+
+                gluTessEndContour( tess );
+            }
+
+            // redo for back mask....
+            tmonly = 3;
+
+            for( Module = pcb->m_Modules; Module != NULL; Module = Module->Next() )
+            {
+                D_PAD*  pad;
+                int     lpii;
+
+                for( pad = Module->m_Pads; pad != NULL; pad = pad->Next() )
+                {
+                    lpii = p_data.size();
+                    pad->Draw3D( this );
+
+                    if( (int) p_data.size() > lpii )
+                    {
+                        gluTessBeginContour( tess );
+
+                        for( ii = (int) p_data.size() - 1; ii >= lpii; ii-- )
+                        {
+                            v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                            v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                            gluTessVertex( tess, v_data, &p_data[ii] );
+                        }
+
+                        gluTessEndContour( tess );
+                    }
+                }
+
+                // draw rest of module mask
+                EDA_ITEM* Struct = Module->m_Drawings;
+
+                for( ; Struct != NULL; Struct = Struct->Next() )
+                {
+                    if( ( ( (BOARD_ITEM*) Struct )->GetLayer() == SOLDERMASK_N_BACK ) )
+                    {
+                        int ii, lpii = p_data.size();
+
+                        switch( Struct->Type() )
+                        {
+                        case PCB_MODULE_TEXT_T:
+                            break;
+
+                        case PCB_MODULE_EDGE_T:
+                            Draw3D_DrawSegmentMask( (DRAWSEGMENT*) Struct, SOLDERMASK_N_BACK,
+                                                    &p_data );
+                            break;
+
+                        default:
+                            break;
+                        }
+
+                        gluTessBeginContour( tess );
+
+                        for( ii = (int) p_data.size() - 1; ii >= lpii; ii-- )
+                        {
+                            if( p_data[ii].x == (int) MAGIC_CONT )
+                            {
+                                gluTessEndContour( tess );
+                                gluTessBeginContour( tess );
+                            }
+                            else
+                            {
+                                v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                                v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                                gluTessVertex( tess, v_data, &p_data[ii] );
+                            }
+                        }
+
+                        gluTessEndContour( tess );
+                    }
+                }
+            }
+
+            // draw mask graphic items
+            EDA_ITEM* PtStruct;
+
+            for( PtStruct = pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
+            {
+                if( ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == SOLDERMASK_N_BACK ) )
+                {
+                    int ii, lpii = p_data.size();
+
+                    switch( PtStruct->Type() )
+                    {
+                    case PCB_LINE_T:
+                        Draw3D_DrawSegmentMask( (DRAWSEGMENT*) PtStruct, SOLDERMASK_N_BACK,
+                                                &p_data );
+                        break;
+
+                    case PCB_TEXT_T:
+                        Draw3D_DrawTextMask( (TEXTE_PCB*) PtStruct, SOLDERMASK_N_BACK, NULL );
+                        break;
+
+                    default:
+                        break;
+                    }
+
+                    gluTessBeginContour( tess );
+
+                    for( ii = lpii; ii < (int) p_data.size(); ii++ )
+                    {
+                        if( p_data[ii].x == (int) MAGIC_CONT )
+                        {
+                            gluTessEndContour( tess );
+                            gluTessBeginContour( tess );
+                        }
+                        else
+                        {
+                            v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                            v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                            gluTessVertex( tess, v_data, &p_data[ii] );
+                        }
+                    }
+
+                    gluTessEndContour( tess );
+                }
+            }
+
+            // draw mask zone items
+            {
+                // Draw segments used to fill copper areas. outdated!
+                for( segzone = pcb->m_Zone; segzone != NULL; segzone = segzone->Next() )
+                {
+                    if( segzone->GetLayer() == SOLDERMASK_N_BACK )
+                        if( segzone->Type() == PCB_ZONE_T )
+                        {
+                            Draw3D_Track( segzone );
+                        }
+
+
+                }
+
+                // Draw new segments
+                for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+                {
+                    ZONE_CONTAINER* curr_zone = pcb->GetArea( ii );
+
+                    if( curr_zone->GetLayer() == SOLDERMASK_N_BACK )
+                    {
+                        if( curr_zone->m_FillMode == 0 )
+                        {
+                            // solid polygons only are used to fill areas
+                            if( curr_zone->GetFilledPolysList().size() > 2 )
+                            {
+                                GLdouble v_data[3];
+                                v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+
+                                // Draw solid areas contained in this zone
+                                int lpii = 0;
+
+                                std::vector<CPolyPt> polysList = curr_zone->GetFilledPolysList();
+
+                                for( unsigned ii = 0; ii < polysList.size(); ii++ )
+                                {
+                                    if( polysList[ii].end_contour == 1 )
+                                    {
+                                        int jj;
+                                        // reverse last polygon
+                                        gluTessBeginContour( tess );
+
+                                        for( jj = lpii; jj <= (int) ii; jj++ )
+                                        {
+                                            p_data.push_back( polysList[jj] );
+                                            v_data[0] = polysList[jj].x *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            v_data[1] = -polysList[jj].y *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            gluTessVertex( tess, v_data,
+                                                           &p_data[p_data.size() - 1] );
+                                        }
+
+                                        gluTessEndContour( tess );
+                                        lpii = ii + 1;
+                                    }
+                                }
+                            }
+                        }
+                        else
+                        {
+                            // segments are used to fill areas
+                            for( unsigned iseg = 0;
+                                 iseg < curr_zone->m_FillSegmList.size();
+                                 iseg++ )
+                            {
+                                SEGZONE dummysegment( pcb );
+
+                                dummysegment.SetLayer( curr_zone->GetLayer() );
+                                dummysegment.m_Width = curr_zone->m_ZoneMinThickness;
+
+                                dummysegment.m_Start.x =
+                                    curr_zone->m_FillSegmList[iseg].m_Start.x;
+                                dummysegment.m_Start.y =
+                                    curr_zone->m_FillSegmList[iseg].m_Start.y;
+                                dummysegment.m_End.x    = curr_zone->m_FillSegmList[iseg].m_End.x;
+                                dummysegment.m_End.y    = curr_zone->m_FillSegmList[iseg].m_End.y;
+                                Draw3D_Track( &dummysegment );
+                            }
+                        }
+                    }
+                }
+
+                // Draw copper areas outlines
+                for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+                {
+                    ZONE_CONTAINER* zone = pcb->GetArea( ii );
+
+                    if( zone->GetLayer() == SOLDERMASK_N_BACK )
+                    {
+                        std::vector<CPolyPt> polysList = zone->GetFilledPolysList();
+
+                        if( polysList.size() == 0 )
+                            continue;
+
+                        if( zone->m_ZoneMinThickness <= 1 )
+                            continue;
+
+                        int         imax = polysList.size() - 1;
+                        CPolyPt*    firstcorner = &polysList[imax];
+                        CPolyPt*    begincorner = firstcorner;
+                        SEGZONE dummysegment( pcb );
+
+                        dummysegment.SetLayer( zone->GetLayer() );
+                        dummysegment.m_Width = zone->m_ZoneMinThickness;
+
+                        for( int ic = imax - 1; ic > 0; ic-- )
+                        {
+                            CPolyPt* endcorner = &polysList[ic];
+
+                            if( begincorner->utility == 0 )
+                            {
+                                // Draw only basic outlines, not extra segments
+                                dummysegment.m_Start.x  = begincorner->x;
+                                dummysegment.m_Start.y  = begincorner->y;
+                                dummysegment.m_End.x    = endcorner->x;
+                                dummysegment.m_End.y    = endcorner->y;
+                                Draw3D_Track( &dummysegment );
+                            }
+
+                            if( (endcorner->end_contour) || (ic == 0) )
+                            {
+                                // the last corner of a filled area is found: draw it
+                                if( endcorner->utility == 0 )
+                                {
+                                    // Draw only basic outlines, not extra segments
+                                    dummysegment.m_Start.x  = endcorner->x;
+                                    dummysegment.m_Start.y  = endcorner->y;
+                                    dummysegment.m_End.x    = firstcorner->x;
+                                    dummysegment.m_End.y    = firstcorner->y;
+
+                                    Draw3D_Track( &dummysegment );
+                                }
+
+                                ic++;
+
+                                if( ic >= 0 )
+                                    begincorner = firstcorner = &polysList[ic];
+                            }
+                            else
+                            {
+                                begincorner = endcorner;
+                            }
+                        }
+                    }
+                }
+            }
+
+            gluTessEndPolygon( tess );
+            gluDeleteTess( tess );
+        }
+
+        p_data.clear();
+    }
+
+    if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
+    {
+        // draw silk..
+        int silki;
+
+        for( silki = 0; silki < 2; silki++ )
+        {
+            // fill jtess with silk stuff
+            int llay = (silki) ? SILKSCREEN_N_FRONT : SILKSCREEN_N_BACK;
+
+            if( !g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( llay ) )
+                continue;
+
+            tess    = gluNewTess();
+            tmonly  = 1;
+            GLUtesselator* jtess = gluNewTess();
+            p_data.clear();
+            g_Parm_3D_Visu.m_ActZpos = g_Parm_3D_Visu.m_LayerZcoord[llay];
+            GLdouble v_data[3];
+            v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+            int color = g_ColorsSettings.GetLayerColor( llay );
+            MySetGLColor( color, llay, 0.8 );
+            glNormal3f( 0.0, 0.0, (silki) ? 1.0 : -1.0 );
+            gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+            gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+            gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+            gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+            gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+            gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+            gluTessCallback( jtess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )my3tessBeginCB );
+            gluTessCallback( jtess, GLU_TESS_END, ( void (CALLBACK*) () )my3tessEndCB );
+            gluTessCallback( jtess, GLU_TESS_ERROR, ( void (CALLBACK*) () )my3tessErrorCB );
+            gluTessCallback( jtess, GLU_TESS_VERTEX,
+                             ( void (CALLBACK*) () )my3tessCPolyPt2Vertex );
+            gluTessCallback( jtess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )my3tessCombineCB );
+            gluTessProperty( jtess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE );
+            gluTessProperty( jtess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
+
+            gluTessBeginPolygon( tess, NULL );
+            gluTessBeginPolygon( jtess, NULL );
+
+            gluTessNormal( tess, 0, 0, -1 );
+            gluTessNormal( jtess, 0, 0, -1 );
+
+            for( Module = pcb->m_Modules; Module != NULL; Module = Module->Next() )
+            {
+                TEXTE_MODULE* ref = Module->m_Reference;
+
+                // Draw Reference...
+                if( do_ref
+                    && ( ( ( ( (BOARD_ITEM*) Module )->GetLayer() ) ? SILKSCREEN_N_FRONT :
+                           SILKSCREEN_N_BACK )
+                         == llay ) )
+                {
+                    for( ; ref != NULL; ref = ref->Next() )
+                    {
+                        if( ref->IsVisible() )
+                        {
+                            Draw3D_DrawTextMask( (TEXTE_PCB*) ref, llay, jtess );
+                        }
+                    }
+                }
+
+                EDA_ITEM* Struct = Module->m_Drawings;
+
+                for( ; Struct != NULL; Struct = Struct->Next() )
+                {
+                    if( ( ( (BOARD_ITEM*) Struct )->GetLayer() == llay ) )
+                    {
+                        int ii, lpii = p_data.size();
+
+                        switch( Struct->Type() )
+                        {
+                        case PCB_MODULE_TEXT_T:
+                            break;
+
+                        case PCB_MODULE_EDGE_T:
+                            {
+                                // Draw module edges when no 3d shape exists.
+                                // Always draw pcb edges.
+                                /* if( !As3dShape || edge->GetLayer() == EDGE_N ) */
+                                {
+                                    Draw3D_DrawSegmentMask( (DRAWSEGMENT*) Struct, llay, &p_data );
+                                    gluTessBeginContour( jtess );
+
+                                    for( ii = (int) p_data.size() - 1; ii >= lpii; ii-- )
+                                    {
+                                        if( p_data[ii].x == (int) MAGIC_CONT )
+                                        {
+                                            gluTessEndContour( jtess );
+                                            gluTessBeginContour( jtess );
+                                        }
+                                        else
+                                        {
+                                            v_data[0] = p_data[ii].x *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            v_data[1] = -p_data[ii].y *
+                                                        g_Parm_3D_Visu.m_BoardScale;
+                                            gluTessVertex( jtess, v_data, &p_data[ii] );
+                                        }
+                                    }
+
+                                    gluTessEndContour( jtess );
+                                }
+                            }
+                            break;
+
+                        default:
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // draw silk graphic items
+            EDA_ITEM* PtStruct;
+
+            for( PtStruct = pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
+            {
+                if( ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == llay ) )
+                {
+                    int ii, lpii = p_data.size();
+
+                    switch( PtStruct->Type() )
+                    {
+                    case PCB_LINE_T:
+                        Draw3D_DrawSegmentMask( (DRAWSEGMENT*) PtStruct, llay, &p_data );
+                        gluTessBeginContour( jtess );
+
+                        for( ii = p_data.size() - 1; ii >= lpii; ii-- )
+                        {
+                            if( p_data[ii].x == (int) MAGIC_CONT )
+                            {
+                                gluTessEndContour( jtess );
+                                gluTessBeginContour( jtess );
+                            }
+                            else
+                            {
+                                v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                                v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                                gluTessVertex( jtess, v_data, &p_data[ii] );
+                            }
+                        }
+
+                        gluTessEndContour( jtess );
+                        break;
+
+                    case PCB_TEXT_T:
+                        Draw3D_DrawTextMask( (TEXTE_PCB*) PtStruct, llay, jtess );
+                        break;
+
+                    default:
+                        break;
+                    }
+                }
+            }
+
+            for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+            {
+                ZONE_CONTAINER* curr_zone = pcb->GetArea( ii );
+
+                if( curr_zone->GetLayer() == llay )
+                {
+                    if( curr_zone->m_FillMode == 0 )
+                    {
+                        // solid polygons only are used to fill areas
+                        if( curr_zone->GetFilledPolysList().size() > 2 )
+                        {
+                            GLdouble xv_data[3];
+                            xv_data[2] = g_Parm_3D_Visu.m_ActZpos;
+
+                            // Draw solid areas contained in this zone
+
+                            std::vector<CPolyPt> polysList = curr_zone->GetFilledPolysList();
+                        }
+                    }
+                    else
+                    {
+                        // segments are used to fill areas
+                        for( unsigned iseg = 0; iseg < curr_zone->m_FillSegmList.size(); iseg++ )
+                        {
+                            SEGZONE dummysegment( pcb );
+
+                            dummysegment.SetLayer( curr_zone->GetLayer() );
+                            dummysegment.m_Width = curr_zone->m_ZoneMinThickness;
+
+                            dummysegment.m_Start.x  = curr_zone->m_FillSegmList[iseg].m_Start.x;
+                            dummysegment.m_Start.y  = curr_zone->m_FillSegmList[iseg].m_Start.y;
+                            dummysegment.m_End.x    = curr_zone->m_FillSegmList[iseg].m_End.x;
+                            dummysegment.m_End.y    = curr_zone->m_FillSegmList[iseg].m_End.y;
+                            Draw3D_Track( &dummysegment );
+                        }
+                    }
+                }
+            }
+
+            // fill tess with mask stuff
+            if( edge_data.size() > 2 )
+            {
+                std::vector<CPolyPt> o_data;
+                int ei = 0;
+                int xmini   = 0;
+                int xmin    = 0x7fffffff;
+                int ymin    = 0x7fffffff;
+                int xmax    = MAGIC_CONT;
+                int ymax    = MAGIC_CONT;
+                gluTessEndPolygon( jtess );
+
+                for( ii = 0; edge_data[ii].x != (int) MAGIC_CONT; ii++ )
+                {
+                    if( (int) edge_data[ii].x > xmax )
+                    {
+                        xmax = edge_data[ii].x;
+                    }
+
+                    if( (int) edge_data[ii].y > ymax )
+                    {
+                        ymax = edge_data[ii].y;
+                    }
+
+                    if( (int) edge_data[ii].x < xmin )
+                    {
+                        xmin    = edge_data[ii].x;
+                        xmini   = ii;
+                    }
+
+                    if( (int) edge_data[ii].y < ymin )
+                    {
+                        ymin = edge_data[ii].y;
+                    }
+
+                    ei = ii;
+                }
+
+                // do cover polygon...
+                o_data.push_back( CPolyPt( (int) 0x90000000, (int) 0x90000000 ) );
+                o_data.push_back( CPolyPt( (int) 0x90000000, (int) 0x70000000 ) );
+                o_data.push_back( CPolyPt( (int) 0x70000000, (int) 0x70000000 ) );
+                o_data.push_back( CPolyPt( (int) 0x70000000, (int) 0x90000000 ) );
+
+                gluTessBeginContour( tess );
+
+                for( ii = 0; ii < 4; ii++ )
+                {
+                    v_data[0]   = o_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                    v_data[1]   = -o_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessVertex( tess, v_data, &o_data[ii] );
+                }
+
+                gluTessEndContour( tess );
+                gluTessBeginContour( tess );
+
+                for( ii = 0; edge_data[ii].x != (int) MAGIC_CONT; ii++ )
+                {
+                    v_data[0]   = edge_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                    v_data[1]   = -edge_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                    gluTessVertex( tess, v_data, &edge_data[ii] );
+                }
+
+                gluTessEndContour( tess );
+
+                // subtract pcb
+                int li = 0;
+
+                for( ii = 0; ii < (int) xe_data.size(); ii++ )
+                {
+                    if( xe_data[ii].x == (int) MAGIC_CONT )
+                    {
+                        gluTessBeginContour( tess );
+
+                        for( int j = ii - 1; j >= li; j-- )
+                        {
+                            v_data[0]   = xe_data[j].x * g_Parm_3D_Visu.m_BoardScale;
+                            v_data[1]   = -xe_data[j].y * g_Parm_3D_Visu.m_BoardScale;
+                            gluTessVertex( tess, v_data, &xe_data[j] );
+                        }
+
+                        gluTessEndContour( tess );
+                        li = ii + 1;
+                    }
+                }
+
+                // subtract pad mask holes
+                tmonly = (silki) ? 1 : 3;
+
+                for( Module = pcb->m_Modules; Module != NULL; Module = Module->Next() )
+                {
+                    D_PAD* pad;
+                    int lpii, ii;
+
+                    for( pad = Module->m_Pads; pad != NULL; pad = pad->Next() )
+                    {
+                        lpii = p_data.size();
+
+                        if( ( pad->GetLayerMask() & ( 1 << (SOLDERPASTE_N_BACK | llay) ) ) == 0 )
+                            continue;
+
+                        pad->Draw3D( this );
+
+                        if( (int) p_data.size() > lpii )
+                        {
+                            gluTessBeginContour( tess );
+
+                            for( ii = (int) p_data.size() - 1; ii >= lpii; ii-- )
+                            {
+                                v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+                                v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+                                gluTessVertex( tess, v_data, &p_data[ii] );
+                            }
+
+                            gluTessEndContour( tess );
+                        }
+                    }
+                }
+            }
+            else
+            {
+                gluTessEndPolygon( jtess );
+            }
+
+            gluTessEndPolygon( tess );
+            gluDeleteTess( tess );
+            gluDeleteTess( jtess );
+            p_data.clear();
+            tmonly = 0;
+        }
+    }
+
+    // draw rest of stuff..
+    for( int stuffi = DRAW_N; stuffi < LAYER_COUNT; stuffi++ )
+    {
+        EDA_ITEM* PtStruct;
+
+        for( PtStruct = pcb->m_Drawings; PtStruct != NULL; PtStruct = PtStruct->Next() )
+        {
+            if( ( ( (BOARD_ITEM*) PtStruct )->GetLayer() == stuffi ) )
+            {
+                switch( PtStruct->Type() )
+                {
+                case PCB_LINE_T:
+                    Draw3D_DrawSegment( (DRAWSEGMENT*) PtStruct );
+                    break;
+
+                case PCB_TEXT_T:
+                    Draw3D_DrawText( (TEXTE_PCB*) PtStruct );
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        }
+
+        for( ii = 0; ii < pcb->GetAreaCount(); ii++ )
+        {
+            ZONE_CONTAINER* curr_zone = pcb->GetArea( ii );
+
+            if( curr_zone->GetLayer() == stuffi )
+            {
+                if( curr_zone->m_FillMode == 0 )
+                {
+                    // solid polygons only are used to fill areas
+                    if( curr_zone->GetFilledPolysList().size() > 2 )
+                    {
+                        GLdouble xv_data[3];
+                        xv_data[2] = g_Parm_3D_Visu.m_ActZpos;
+
+                        // Draw solid areas contained in this zone
+
+                        std::vector<CPolyPt> polysList = curr_zone->GetFilledPolysList();
+                    }
+                }
+                else
+                {
+                    // segments are used to fill areas
+                    for( unsigned iseg = 0; iseg < curr_zone->m_FillSegmList.size(); iseg++ )
+                    {
+                        SEGZONE dummysegment( pcb );
+
+                        dummysegment.SetLayer( curr_zone->GetLayer() );
+                        dummysegment.m_Width = curr_zone->m_ZoneMinThickness;
+
+                        dummysegment.m_Start.x  = curr_zone->m_FillSegmList[iseg].m_Start.x;
+                        dummysegment.m_Start.y  = curr_zone->m_FillSegmList[iseg].m_Start.y;
+                        dummysegment.m_End.x    = curr_zone->m_FillSegmList[iseg].m_End.x;
+                        dummysegment.m_End.y    = curr_zone->m_FillSegmList[iseg].m_End.y;
+                        Draw3D_Track( &dummysegment );
+                    }
+                }
+            }
+        }
+    }
+
+#define MM2INTERN( mm )     ( (int) ( ( (double) (mm) ) * 10000.0 / 25.4 + 0.5 ) )
+#define INTERN2MM( intern ) ( (int) ( ( (double) (intern) ) / 10000.0 * 25.4 + 0.5 ) )
+
+    // draw mm grid..
+    if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_GRID] )
+    {
+        int lx  = MM2INTERN( -100 );
+        int uy  = MM2INTERN( 100 );
+        int hh  = 200;
+        int ww  = 200;
+        int need_back = g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER];
+
+        if( paths.size() > 0 )
+        {
+            lx = 0x7fffffff;
+            int ly  = 0x7fffffff;
+            int ux  = 0x80000000;
+            uy = 0x80000000;
+
+            for( unsigned i = 0; i < paths[0].size(); i++ )
+            {
+                if( paths[0][i].x > ux )
+                    ux = paths[0][i].x;
+
+                if( paths[0][i].y > uy )
+                    uy = paths[0][i].y;
+
+                if( paths[0][i].x < lx )
+                    lx = paths[0][i].x;
+
+                if( paths[0][i].y < ly )
+                    ly = paths[0][i].y;
+            }
+
+            hh  = INTERN2MM( uy - ly );
+            ww  = INTERN2MM( ux - lx );
+        }
+
+        SEGZONE dummysegment( pcb );
+
+#define GRID_SCAL 5
+        for( int mm = 0; mm <= ww; mm += 1 )
+        {
+            int w = GRID_SCAL +
+                    ( ( (mm % 10) == 0 ) ? GRID_SCAL : 0 ) + ( ( (mm % 5) == 0 ) ? GRID_SCAL : 0 );
+            dummysegment.m_Width = w;
+            // vert..
+            dummysegment.m_Start.x  = lx + MM2INTERN( mm ) - 0;
+            dummysegment.m_Start.y  = uy - MM2INTERN( 0 );
+            dummysegment.m_End.x    = lx + MM2INTERN( mm ) + 0;
+            dummysegment.m_End.y    = uy - MM2INTERN( hh );
+            dummysegment.SetLayer( 31 );
+            Draw3D_Track( &dummysegment );
+
+            if( need_back )
+            {
+                dummysegment.SetLayer( 30 );
+                Draw3D_Track( &dummysegment );
+            }
+        }
+
+        for( int mm = 0; mm <= hh; mm += 1 )
+        {
+            int w = GRID_SCAL +
+                    ( ( (mm % 10) == 0 ) ? GRID_SCAL : 0 ) + ( ( (mm % 5) == 0 ) ? GRID_SCAL : 0 );
+            dummysegment.m_Width = w;
+            // hor..
+            dummysegment.m_Start.x  = lx + MM2INTERN( 0 );
+            dummysegment.m_Start.y  = uy - MM2INTERN( mm ) - 0;
+            dummysegment.m_End.x    = lx + MM2INTERN( ww );
+            dummysegment.m_End.y    = uy - MM2INTERN( mm ) + 0;
+            dummysegment.SetLayer( 31 );
+            Draw3D_Track( &dummysegment );
+
+            if( need_back )
+            {
+                dummysegment.SetLayer( 30 );
+                Draw3D_Track( &dummysegment );
+            }
+        }
     }
 
     glEndList();
@@ -382,36 +3483,61 @@
 void EDA_3D_CANVAS::Draw3D_Track( TRACK* track )
 {
     double zpos;
-    int    layer = track->GetLayer();
+    int layer = track->GetLayer();
     double ox, oy, fx, fy;
     double w;
 
-    if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
-        return;
-
-    int color = g_ColorsSettings.GetLayerColor( layer );
-
-    if( layer == LAST_COPPER_LAYER )
-        layer = g_Parm_3D_Visu.m_Layers - 1;
-
-    zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
-
-    SetGLColor( color );
-    glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
-
-    w  = track->m_Width * g_Parm_3D_Visu.m_BoardScale;
-    ox = track->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
-    oy = track->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
-    fx = track->m_End.x * g_Parm_3D_Visu.m_BoardScale;
-    fy = track->m_End.y * g_Parm_3D_Visu.m_BoardScale;
-    Draw3D_FilledSegment( ox, -oy, fx, -fy, w, zpos );
+    if( tmonly == 1 )
+    {
+        // fill polygon...
+        if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
+            return;
+
+        w   = track->m_Width * g_Parm_3D_Visu.m_BoardScale;
+        ox  = track->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+        oy  = track->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+        fx  = track->m_End.x * g_Parm_3D_Visu.m_BoardScale;
+        fy  = track->m_End.y * g_Parm_3D_Visu.m_BoardScale;
+        Draw3D_FilledSegmentMask( ox, -oy, fx, -fy, w, 0, &p_data, 1 );
+    }
+    else
+    {
+        if( ( layer < 30) && ( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false ) )
+            return;
+
+        int color = g_ColorsSettings.GetLayerColor( layer );
+
+        if( layer == LAST_COPPER_LAYER )
+            layer = g_Parm_3D_Visu.m_Layers - 1;
+
+        zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
+
+        if( layer >= 30 )
+        {
+            glColor4f( 0.4, 0.4, 0.8, 0.7 );
+            glNormal3f( 0.0, 0.0, (layer & 1) ? 1.0 : -1.0 );
+        }
+        else
+        {
+            MySetGLColor( color, layer, 0.8 );
+            glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
+        }
+
+        w   = track->m_Width * g_Parm_3D_Visu.m_BoardScale;
+        ox  = track->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+        oy  = track->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+        fx  = track->m_End.x * g_Parm_3D_Visu.m_BoardScale;
+        fy  = track->m_End.y * g_Parm_3D_Visu.m_BoardScale;
+        Draw3D_FilledSegment( ox, -oy, fx, -fy, w, zpos );
+    }
 }
 
 
+/* XXX TSP */
 void EDA_3D_CANVAS::Draw3D_SolidPolygonsInZones( ZONE_CONTAINER* aZone )
 {
     double zpos;
-    int    layer = aZone->GetLayer();
+    int layer = aZone->GetLayer();
 
     if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
         return;
@@ -425,24 +3551,23 @@
     g_Parm_3D_Visu.m_ActZpos = zpos;
 
 
-    SetGLColor( color );
+    MySetGLColor( color, layer, 0.8 );
     glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
 
     GLUtesselator* tess = gluNewTess();
-    gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*)() )tessBeginCB );
-    gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*)() )tessEndCB );
-    gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*)() )tessErrorCB );
-    gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*)() )tessCPolyPt2Vertex );
+    gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+    gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+    gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )tessErrorCB );
+    gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )tessCPolyPt2Vertex );
 
     GLdouble v_data[3];
     v_data[2] = zpos;
 
-    //gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
-
     // Draw solid areas contained in this zone
     int StartContour = 1;
 
     std::vector<CPolyPt> polysList = aZone->GetFilledPolysList();
+
     for( unsigned ii = 0; ii < polysList.size(); ii++ )
     {
         if( StartContour == 1 )
@@ -452,8 +3577,8 @@
             StartContour = 0;
         }
 
-        v_data[0] = polysList[ii].x * g_Parm_3D_Visu.m_BoardScale;
-        v_data[1] = -polysList[ii].y * g_Parm_3D_Visu.m_BoardScale;
+        v_data[0]   = polysList[ii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -polysList[ii].y * g_Parm_3D_Visu.m_BoardScale;
         gluTessVertex( tess, v_data, &polysList[ii] );
 
         if( polysList[ii].end_contour == 1 )
@@ -471,59 +3596,68 @@
 void EDA_3D_CANVAS::Draw3D_Via( SEGVIA* via )
 {
     double x, y, r, hole;
-    int    layer, top_layer, bottom_layer;
-    double zpos, height;
-    int    color;
-
-    r     = via->m_Width * g_Parm_3D_Visu.m_BoardScale / 2;
-    hole  = via->GetDrillValue();
-    hole *= g_Parm_3D_Visu.m_BoardScale / 2;
-    x     = via->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
-    y     = via->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
-
-    via->ReturnLayerPair( &top_layer, &bottom_layer );
-
-    // Drawing filled circles:
-    for( layer = bottom_layer; layer < g_Parm_3D_Visu.m_Layers; layer++ )
-    {
-        zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
-
-        if( layer < g_Parm_3D_Visu.m_Layers - 1 )
-        {
-            if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
-                continue;
-
-            color = g_ColorsSettings.GetLayerColor( layer );
-        }
-        else
-        {
-            if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( LAYER_N_FRONT ) == false )
-                continue;
-
-            color = g_ColorsSettings.GetLayerColor( LAYER_N_FRONT );
-        }
-
-        SetGLColor( color );
-
-        // SetGLColor( LIGHTGRAY );
-        glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
-
-        if( layer == LAYER_N_BACK )
-            zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
-        else
-            zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
-
-        Draw3D_FilledCircle( x, -y, r, hole, zpos );
-
-        if( layer >= top_layer )
-            break;
-    }
-
-    // Drawing hole:
-    color = g_ColorsSettings.GetItemColor( VIAS_VISIBLE + via->m_Shape );
-    SetGLColor( color );
-    height = g_Parm_3D_Visu.m_LayerZcoord[top_layer] - g_Parm_3D_Visu.m_LayerZcoord[bottom_layer];
-    Draw3D_FilledCylinder( x, -y, hole, height, g_Parm_3D_Visu.m_LayerZcoord[bottom_layer] );
+    int layer, top_layer, bottom_layer;
+    double zpos, height = 0;
+    int color;
+
+    r = via->m_Width * g_Parm_3D_Visu.m_BoardScale / 2;
+    hole    = via->GetDrillValue();
+    hole    *= g_Parm_3D_Visu.m_BoardScale / 2;
+    x       = via->m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+    y       = via->m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+
+    if( tmonly == 0 )
+    {
+        via->ReturnLayerPair( &top_layer, &bottom_layer );
+
+        // Drawing filled circles:
+        for( layer = bottom_layer; layer < g_Parm_3D_Visu.m_Layers; layer++ )
+        {
+            zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
+
+            if( layer < g_Parm_3D_Visu.m_Layers - 1 )
+            {
+                if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
+                    continue;
+
+                color = g_ColorsSettings.GetLayerColor( layer );
+            }
+            else
+            {
+                if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( LAYER_N_FRONT ) == false )
+                    continue;
+
+                color = g_ColorsSettings.GetLayerColor( LAYER_N_FRONT );
+            }
+
+            MySetGLColor( color, layer, 1 );
+
+            glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
+
+            if( layer == LAYER_N_BACK )
+                zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
+            else
+                zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
+
+            Draw3D_FilledCircle( x, -y, r, hole, zpos );
+
+            if( layer >= top_layer )
+                break;
+        }
+
+        // Drawing hole:
+        color = g_ColorsSettings.GetItemColor( VIAS_VISIBLE + via->m_Shape );
+        MySetGLColor( color, layer, 0.8 );
+        height = g_Parm_3D_Visu.m_LayerZcoord[top_layer] -
+                 g_Parm_3D_Visu.m_LayerZcoord[bottom_layer];
+        Draw3D_FilledCylinder( x, -y, hole, height, g_Parm_3D_Visu.m_LayerZcoord[bottom_layer] );
+    }
+    else
+    {
+        bottom_layer = 0;
+        Draw3D_FilledCylinderMask( x, -y, hole, height,
+                                   g_Parm_3D_Visu.m_LayerZcoord[bottom_layer] );
+    }
 }
 
 
@@ -532,22 +3666,22 @@
     double x, y, xf, yf;
     double zpos, w;
 
-    int    layer = segment->GetLayer();
+    int layer = segment->GetLayer();
 
     if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
         return;
 
     int color = g_ColorsSettings.GetLayerColor( layer );
 
-    SetGLColor( color );
-
-    w  = segment->GetWidth() * g_Parm_3D_Visu.m_BoardScale;
-
-    x  = segment->GetStart().x * g_Parm_3D_Visu.m_BoardScale;
-    y  = segment->GetStart().y * g_Parm_3D_Visu.m_BoardScale;
-
-    xf = segment->GetEnd().x * g_Parm_3D_Visu.m_BoardScale;
-    yf = segment->GetEnd().y * g_Parm_3D_Visu.m_BoardScale;
+    MySetGLColor( color, layer, 0.8 );
+
+    w = segment->GetWidth() * g_Parm_3D_Visu.m_BoardScale;
+
+    x   = segment->GetStart().x * g_Parm_3D_Visu.m_BoardScale;
+    y   = segment->GetStart().y * g_Parm_3D_Visu.m_BoardScale;
+
+    xf  = segment->GetEnd().x * g_Parm_3D_Visu.m_BoardScale;
+    yf  = segment->GetEnd().y * g_Parm_3D_Visu.m_BoardScale;
 
     if( layer == EDGE_N )
     {
@@ -559,15 +3693,39 @@
             switch( segment->GetShape() )
             {
             case S_ARC:
-                Draw3D_ArcSegment( x, -y, xf, -yf, segment->GetAngle(), w, zpos );
+
+                if( !g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
+                    Draw3D_ArcSegment( x, -y, xf, -yf, segment->GetAngle(), w, zpos );
+
+                if( layer == 1 )
+                {
+                    Draw3D_ArcSegmentMask( x, -y, xf, -yf, segment->GetAngle(), w, zpos );
+                }
+
                 break;
 
             case S_CIRCLE:
-                Draw3D_CircleSegment( x, -y, xf, -yf, w, zpos );
+
+                if( !g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
+                    Draw3D_CircleSegment( x, -y, xf, -yf, w, zpos );
+
+                if( layer == 1 )
+                {
+                    Draw3D_CircleSegmentMask( x, y, xf, yf, w, zpos );
+                }
+
                 break;
 
             default:
-                Draw3D_FilledSegment( x, -y, xf, -yf, w, zpos );
+
+                if( !g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
+                    Draw3D_FilledSegment( x, -y, xf, -yf, w, zpos );
+
+                if( layer == 1 )
+                {
+                    Draw3D_Mask( x, y, xf, yf, w, zpos );
+                }
+
                 break;
             }
         }
@@ -598,18 +3756,118 @@
 }
 
 
+void EDA_3D_CANVAS::Draw3D_DrawSegmentMask( DRAWSEGMENT*            segment,
+                                            int                     layer,
+                                            std::vector<CPolyPt>*   e_data )
+{
+    double x, y, xf, yf;
+    double zpos = 0, w;
+
+    if( segment->GetLayer() != layer )
+        return;
+
+    if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
+        return;
+
+    w = segment->GetWidth() * g_Parm_3D_Visu.m_BoardScale;
+
+    x   = segment->GetStart().x * g_Parm_3D_Visu.m_BoardScale;
+    y   = segment->GetStart().y * g_Parm_3D_Visu.m_BoardScale;
+
+    xf  = segment->GetEnd().x * g_Parm_3D_Visu.m_BoardScale;
+    yf  = segment->GetEnd().y * g_Parm_3D_Visu.m_BoardScale;
+
+    {
+        {
+            switch( segment->GetShape() )
+            {
+            case S_ARC:
+                {
+                    Draw3D_ArcSegmentMask( x, -y, xf, -yf, segment->GetAngle(), w, zpos, &p_data );
+                }
+
+                break;
+
+            case S_CIRCLE:
+                {
+                    Draw3D_CircleSegmentMask( x, -y, xf, -yf, w, zpos, &p_data );
+                }
+
+                break;
+
+            case S_POLYGON:
+                {
+                    // We must compute true coordinates from m_PolyPoints
+                    // which are relative to module position and module orientation = 0
+                    EDGE_MODULE* edge = (EDGE_MODULE*) segment;
+                    std::vector<wxPoint> points = edge->GetPolyPoints();
+                    MODULE* module = (MODULE*) edge->GetParent();
+
+                    if( module == NULL )
+                    {
+                        break;
+                    }
+
+                    for( int ii = 0; ii < (int) points.size(); ii++ )
+                    {
+                        wxPoint& pt = points[ii];
+                        RotatePoint( &pt.x, &pt.y, module->GetOrientation() );
+                        pt += module->m_Pos;
+                        e_data->push_back( CPolyPt( (int) pt.x, (int) pt.y ) );
+                    }
+
+                    e_data->push_back( CPolyPt( (int) MAGIC_CONT, 0 ) );
+                }
+
+            default:
+                {
+                    Draw3D_FilledSegmentMask( x, -y, xf, -yf, w, zpos, &p_data, 1 );
+                }
+
+                break;
+            }
+        }
+    }
+}
+
+
 static double s_Text3DWidth, s_Text3DZPos;
 static void Draw3dTextSegm( int x0, int y0, int xf, int yf )
 {
-    double startx = x0 * g_Parm_3D_Visu.m_BoardScale;
-    double starty = y0 * g_Parm_3D_Visu.m_BoardScale;
-    double endx   = xf * g_Parm_3D_Visu.m_BoardScale;
-    double endy   = yf * g_Parm_3D_Visu.m_BoardScale;
+    double startx   = x0 * g_Parm_3D_Visu.m_BoardScale;
+    double starty   = y0 * g_Parm_3D_Visu.m_BoardScale;
+    double endx     = xf * g_Parm_3D_Visu.m_BoardScale;
+    double endy     = yf * g_Parm_3D_Visu.m_BoardScale;
 
     Draw3D_FilledSegment( startx, -starty, endx, -endy, s_Text3DWidth, s_Text3DZPos );
 }
 
 
+static void Draw3dTextSegmMask( int x0, int y0, int xf, int yf )
+{
+    double startx   = x0 * g_Parm_3D_Visu.m_BoardScale;
+    double starty   = y0 * g_Parm_3D_Visu.m_BoardScale;
+    double endx     = xf * g_Parm_3D_Visu.m_BoardScale;
+    double endy     = yf * g_Parm_3D_Visu.m_BoardScale;
+
+    Draw3D_FilledSegmentMask( startx, -starty, endx, -endy, s_Text3DWidth, s_Text3DZPos, gtess,
+                              1 );
+}
+
+
+static void Draw3dTextSegmMaskBuf( int x0, int y0, int xf, int yf )
+{
+    double startx   = x0 * g_Parm_3D_Visu.m_BoardScale;
+    double starty   = y0 * g_Parm_3D_Visu.m_BoardScale;
+    double endx     = xf * g_Parm_3D_Visu.m_BoardScale;
+    double endy     = yf * g_Parm_3D_Visu.m_BoardScale;
+
+    Draw3D_FilledSegmentMask( startx, -starty, endx, -endy, s_Text3DWidth, s_Text3DZPos, &p_data,
+                              1 );
+    p_data.push_back( CPolyPt( (int) MAGIC_CONT, 0 ) );
+}
+
+
 void EDA_3D_CANVAS::Draw3D_DrawText( TEXTE_PCB* text )
 {
     int layer = text->GetLayer();
@@ -618,10 +3876,9 @@
         return;
 
     int color = g_ColorsSettings.GetLayerColor( layer );
-
-    SetGLColor( color );
-    s_Text3DZPos  = g_Parm_3D_Visu.m_LayerZcoord[layer];
-    s_Text3DWidth = text->GetThickness() * g_Parm_3D_Visu.m_BoardScale;
+    MySetGLColor( color, layer, 1 );
+    s_Text3DZPos    = g_Parm_3D_Visu.m_LayerZcoord[layer];
+    s_Text3DWidth   = text->GetThickness() * g_Parm_3D_Visu.m_BoardScale;
     glNormal3f( 0.0, 0.0, Get3DLayerSide( layer ) );
     wxSize size = text->m_Size;
 
@@ -630,9 +3887,9 @@
 
     if( text->m_MultilineAllowed )
     {
-        wxPoint        pos  = text->m_Pos;
+        wxPoint pos = text->m_Pos;
         wxArrayString* list = wxStringSplit( text->m_Text, '\n' );
-        wxPoint        offset;
+        wxPoint offset;
 
         offset.y = text->GetInterline();
 
@@ -663,24 +3920,175 @@
 }
 
 
+void EDA_3D_CANVAS::Draw3D_DrawTextMask( TEXTE_PCB* text, int layer, GLUtesselator* tess )
+{
+    if( text->GetLayer() != layer )
+        return;
+
+    if( !Get3DLayerEnable( layer ) )
+        return;
+
+    s_Text3DWidth = text->GetThickness() * g_Parm_3D_Visu.m_BoardScale;
+    wxSize size = text->m_Size;
+
+    if( text->m_Mirror )
+        NEGATE( size.x );
+
+    if( text->m_MultilineAllowed )
+    {
+        wxPoint pos = text->m_Pos;
+        wxArrayString* list = wxStringSplit( text->m_Text, '\n' );
+        wxPoint offset;
+
+        offset.y = text->GetInterline();
+
+        RotatePoint( &offset, text->GetOrientation() );
+
+        for( unsigned i = 0; i<list->Count(); i++ )
+        {
+            wxString txt = list->Item( i );
+
+            if( tess == NULL )
+            {
+                DrawGraphicText( NULL, NULL, pos, (EDA_COLOR_T) 0,    // color,
+                                 txt, text->GetOrientation(), size,
+                                 text->m_HJustify, text->m_VJustify,
+                                 text->GetThickness(), text->m_Italic,
+                                 true, Draw3dTextSegmMaskBuf );
+            }
+            else
+            {
+                gtess = tess;
+                DrawGraphicText( NULL, NULL, pos, (EDA_COLOR_T) 0,    // color,
+                                 txt, text->GetOrientation(), size,
+                                 text->m_HJustify, text->m_VJustify,
+                                 text->GetThickness(), text->m_Italic,
+                                 true, Draw3dTextSegmMask );
+            }
+
+            pos += offset;
+        }
+
+        delete list;
+    }
+    else
+    {
+        if( tess == NULL )
+        {
+            DrawGraphicText( NULL, NULL, text->m_Pos, (EDA_COLOR_T) 0,    // color,
+                             text->m_Text, text->GetOrientation(), size,
+                             text->m_HJustify, text->m_VJustify,
+                             text->GetThickness(), text->m_Italic,
+                             true,
+                             Draw3dTextSegmMaskBuf );
+        }
+        else
+        {
+            gtess = tess;
+            DrawGraphicText( NULL, NULL, text->m_Pos, (EDA_COLOR_T) 0,    // color,
+                             text->m_Text, text->GetOrientation(), size,
+                             text->m_HJustify, text->m_VJustify,
+                             text->GetThickness(), text->m_Italic,
+                             true,
+                             Draw3dTextSegmMask );
+        }
+    }
+}
+
+
+void EDA_3D_CANVAS::Draw3D_DrawText( TEXTE_MODULE* text, double rot )
+{
+    int layer = text->GetLayer();
+
+    int color = g_ColorsSettings.GetLayerColor( layer );
+
+    MySetGLColor( color, layer, 1 );
+    s_Text3DZPos    = g_Parm_3D_Visu.m_LayerZcoord[layer];
+    s_Text3DWidth   = text->GetThickness() * g_Parm_3D_Visu.m_BoardScale;
+    glNormal3f( 0.0, 0.0, Get3DLayerSide( layer ) );
+    wxSize size = text->m_Size;
+
+    if( text->m_Mirror )
+        NEGATE( size.x );
+
+    if( text->m_MultilineAllowed )
+    {
+        wxPoint pos = text->m_Pos;
+        wxArrayString* list = wxStringSplit( text->m_Text, '\n' );
+        wxPoint offset;
+        double newrot = text->GetOrientation() + rot;
+
+        while( newrot < -900.0 )
+            newrot += 1800.0;
+
+        while( newrot > 900.0 )
+            newrot -= 1800.0;
+
+        offset.y = text->GetInterline();
+
+        RotatePoint( &offset, text->GetOrientation() + rot );
+
+        for( unsigned i = 0; i<list->Count(); i++ )
+        {
+            wxString txt = list->Item( i );
+            DrawGraphicText( NULL, NULL, pos, (EDA_COLOR_T) color,
+                             txt, newrot, size,
+                             text->m_HJustify, text->m_VJustify,
+                             text->GetThickness(), text->m_Italic,
+                             true, Draw3dTextSegm );
+            pos += offset;
+        }
+
+        delete list;
+    }
+    else
+    {
+        double newrot = text->GetOrientation() + rot;
+
+        while( newrot < -900.0 )
+            newrot += 1800.0;
+
+        while( newrot > 900.0 )
+            newrot -= 1800.0;
+
+        DrawGraphicText( NULL, NULL, text->m_Pos, (EDA_COLOR_T) color,
+                         text->m_Text, newrot, size,
+                         text->m_HJustify, text->m_VJustify,
+                         text->GetThickness(), text->m_Italic,
+                         true,
+                         Draw3dTextSegm );
+    }
+}
+
+
 void MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
 {
     D_PAD* pad = m_Pads;
 
     // Draw pads
     glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
-    glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
-
-    for( ; pad != NULL; pad = pad->Next() )
-    {
-        pad->Draw3D( glcanvas );
-    }
+    glNormal3f( 0.0, 0.0, 1.0 );    // Normal is Z axis
+
+
+    if( tmonly & 4 )           // TSP_20120605
+    {
+    }
+    else
+    {
+        for( ; pad != NULL; pad = pad->Next() )
+        {
+            pad->Draw3D( glcanvas );
+        }
+    }
+
+    if( tmonly & ~4 ) // TSP_20120605
+        return;
 
     // Draw module shape: 3D shape if exists (or module outlines if not exists)
-    S3D_MASTER* Struct3D  = m_3D_Drawings;
-    bool        As3dShape = false;
+    S3D_MASTER* Struct3D = m_3D_Drawings;
+    bool As3dShape = false;
 
-    if (g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_MODULE])
+    if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_MODULE] )
     {
         glPushMatrix();
 
@@ -703,10 +4111,112 @@
 
         for( ; Struct3D != NULL; Struct3D = Struct3D->Next() )
         {
-            if( !Struct3D->m_Shape3DName.IsEmpty() )
+            if( !Struct3D->m_Shape3DName.IsEmpty()
+                && ( ( GetAttributes() != MOD_VIRTUAL )
+                     || ( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_VIRT] ) ) )
             {
+                std::vector<S3D_Vertex> minmax;
+                Struct3D->m_MinPos.x    = Struct3D->m_MinPos.y = Struct3D->m_MinPos.z = 1E30;
+                Struct3D->m_MaxPos.x    = Struct3D->m_MaxPos.y = Struct3D->m_MaxPos.z = -1E30;
                 As3dShape = true;
-                Struct3D->ReadData();
+                Struct3D->ReadData( &m_Reference->m_Text );
+                minmax.push_back( Struct3D->m_MinPos );
+                minmax.push_back( Struct3D->m_MaxPos );
+                double hi   = minmax[1].y - minmax[0].y;
+                double hix  = minmax[1].x - minmax[0].x;
+
+                if( hi < 0 )
+                    hi = -hi;
+
+                if( hix < 0 )
+                    hix = -hix;
+
+                if( do_values )
+                {
+                    wxString txt = m_Value->m_Text;
+                    wxPoint pos;
+                    wxSize size;
+                    pos.x   = 0;
+                    pos.y   = 0;
+
+                    if( hi > 0.5 )
+                        hi = 0.5;
+
+                    size.x  = (int) (hi * 300);
+                    size.y  = (int) (hi * 300);
+                    glPushMatrix();
+                    glTranslatef( 0,
+                                  0,
+                                  g_Parm_3D_Visu.m_BoardScale * minmax[1].z * 1000.0 - 0 *
+                                  g_Parm_3D_Visu.m_LayerZcoord[m_Layer] );
+                    glColor4f( 0.2, 0.2, 0.2, 0.7 );
+                    s_Text3DWidth   = 100 * g_Parm_3D_Visu.m_BoardScale;
+                    s_Text3DZPos    = g_Parm_3D_Visu.m_BoardScale * 9;
+                    glTranslatef(
+                        g_Parm_3D_Visu.m_BoardScale * 1000 * (minmax[1].x + minmax[0].x) / 2,
+                        g_Parm_3D_Visu.m_BoardScale * 1000 * (minmax[1].y + minmax[0].y) / 2,
+                        0 );
+                    double o    = m_Orient;
+                    double ro   = 0;
+
+                    while( o < -900.0 )
+                    {
+                        o += 1800.0; ro += 180;
+                    }
+
+                    while( o > 900.0 )
+                    {
+                        o -= 1800.0; ro -= 180;
+                    }
+
+                    glRotatef( -ro, 0.0, 0.0, 1.0 );
+                    double bscf = g_Parm_3D_Visu.m_BoardScale;
+                    double scf  = 1e-4;
+                    double pw   = scf * 1000 * fabs( minmax[1].x - minmax[0].x );
+                    double ph   = scf * 1000 * fabs( minmax[1].y - minmax[0].y );
+                    double slen = NegableTextLength( txt );
+
+                    if( slen > 0 )
+                    {
+                        size.x = (int) ( (10000 * pw) / (slen + 1) );
+
+                        if( size.x < 100 )
+                            size.x = 100;
+
+                        if( size.x > 400 )
+                            size.x = 400;
+
+                        size.y = (size.x * 120) / 100;
+
+                        if( size.y > 10000.0 * ph / 1.8 )
+                        {
+                            size.y  = (int) ( (10000.0 * ph / 1.8) );
+                            size.x  = (size.y * 100) / 120;
+                        }
+
+                        pw = size.x * (slen) * bscf;
+                        double ph = 1.5 * size.y * bscf;
+                        {
+                            glBegin( GL_POLYGON );
+                            glVertex3f( pw / 2, ph / 2, s_Text3DZPos );
+                            glVertex3f( pw / 2, -ph / 2, s_Text3DZPos );
+                            glVertex3f( -pw / 2, -ph / 2, s_Text3DZPos );
+                            glVertex3f( -pw / 2, ph / 2, s_Text3DZPos );
+                            glEnd();
+                        }
+                        glColor3f( 0.8, 0.8, 0.8 );
+                        glNormal3f( 0.0, 0.0, 1.0 );    // Normal is Z axis
+                        s_Text3DZPos    = g_Parm_3D_Visu.m_BoardScale * 10;
+                        s_Text3DWidth   = 20 * g_Parm_3D_Visu.m_BoardScale;
+                        DrawGraphicText( NULL, NULL, pos, RED,
+                                         txt, 0, size,
+                                         GR_TEXT_HJUSTIFY_CENTER, GR_TEXT_VJUSTIFY_CENTER,
+                                         20, 0, true,
+                                         Draw3dTextSegm );
+                    }
+
+                    glPopMatrix();
+                }
             }
         }
 
@@ -714,28 +4224,58 @@
     }
 
     EDA_ITEM* Struct = m_Drawings;
-    glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
+    glNormal3f( 0.0, 0.0, 1.0 );    // Normal is Z axis
 
     for( ; Struct != NULL; Struct = Struct->Next() )
     {
-        switch( Struct->Type() )
-        {
-        case PCB_MODULE_TEXT_T:
-            break;
-
-        case PCB_MODULE_EDGE_T:
-        {
-            EDGE_MODULE* edge = (EDGE_MODULE*) Struct;
-
-            // Draw module edges when no 3d shape exists.
-            // Always draw pcb edges.
-            if( !As3dShape || edge->GetLayer() == EDGE_N )
-                edge->Draw3D( glcanvas );
-        }
-        break;
-
-        default:
-            break;
+        unsigned int tl = ( (BOARD_ITEM*) Struct )->GetLayer();
+
+        if( tl == 0 )
+            continue;              // TSP_20120606
+
+        if( tl == 15 )
+            continue;               // TSP_20120606
+
+        if( tl == SILKSCREEN_N_FRONT )
+            continue;                               // TSP_20120606
+
+        if( tl == SILKSCREEN_N_BACK )
+            continue;                               // TSP_20120606
+
+        if( (tl == SOLDERMASK_N_BACK) && g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
+            continue;                                                                                          // TSP_20120606
+
+        if( (tl == SOLDERMASK_N_FRONT) && g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] )
+            continue;                                                                                           // TSP_20120606
+
+        if( ( (tl == DRAW_N) && g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_DRAWINGS] )
+            || ( (tl == COMMENT_N) && g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_COMMENTS] )
+            || ( (tl == ECO1_N) && g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ECO1] )
+            || ( (tl == ECO2_N) && g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ECO2] )
+             )
+        {
+            switch( Struct->Type() )
+            {
+            case PCB_MODULE_TEXT_T:
+                glcanvas->Draw3D_DrawText( (TEXTE_PCB*) Struct );
+                break;
+
+            case PCB_MODULE_EDGE_T:
+                {
+                    EDGE_MODULE* edge = (EDGE_MODULE*) Struct;
+
+                    // Draw module edges when no 3d shape exists.
+                    // Always draw pcb edges.
+                    /* if( !As3dShape || edge->GetLayer() == EDGE_N ) */
+                    {
+                        edge->Draw3D( glcanvas );
+                    }
+                }
+                break;
+
+            default:
+                break;
+            }
         }
     }
 }
@@ -744,23 +4284,23 @@
 void EDGE_MODULE::Draw3D( EDA_3D_CANVAS* glcanvas )
 {
     wxString s;
-    int      dx, dy;
-    double   x, y, fx, fy, w, zpos;
+    int dx, dy;
+    double x, y, fx, fy, w, zpos;
 
     if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( m_Layer ) == false )
         return;
 
     int color = g_ColorsSettings.GetLayerColor( m_Layer );
 
-    SetGLColor( color );
+    MySetGLColor( color, m_Layer, 1 );
 
-    dx   = m_End.x;
-    dy   = m_End.y;
-    w    = m_Width * g_Parm_3D_Visu.m_BoardScale;
-    x    = m_Start.x * g_Parm_3D_Visu.m_BoardScale;
-    y    = m_Start.y * g_Parm_3D_Visu.m_BoardScale;
-    fx   = dx * g_Parm_3D_Visu.m_BoardScale;
-    fy   = dy * g_Parm_3D_Visu.m_BoardScale;
+    dx  = m_End.x;
+    dy  = m_End.y;
+    w   = m_Width * g_Parm_3D_Visu.m_BoardScale;
+    x   = m_Start.x * g_Parm_3D_Visu.m_BoardScale;
+    y   = m_Start.y * g_Parm_3D_Visu.m_BoardScale;
+    fx  = dx * g_Parm_3D_Visu.m_BoardScale;
+    fy  = dy * g_Parm_3D_Visu.m_BoardScale;
 
 
     if( m_Layer == EDGE_N )
@@ -785,6 +4325,56 @@
                 break;
 
             case S_POLYGON:
+                {
+                    // We must compute true coordinates from m_PolyPoints
+                    // which are relative to module position and module orientation = 0
+                    std::vector<wxPoint> points = m_PolyPoints;
+                    MODULE* module = (MODULE*) m_Parent;
+
+                    if( module == NULL )
+                        break;
+
+                    for( unsigned ii = 0; ii < points.size(); ii++ )
+                    {
+                        wxPoint& pt = points[ii];
+
+                        RotatePoint( &pt.x, &pt.y, module->GetOrientation() );
+                        pt += module->m_Pos;
+                    }
+
+                    glcanvas->Draw3D_Polygon( points, zpos );
+                }
+                break;
+
+            default:
+                s.Printf( wxT( "Error: Shape nr %d not implemented!\n" ), m_Shape );
+                D( printf( "%s", TO_UTF8( s ) ); )
+                break;
+            }
+        }
+    }
+    else
+    {
+        glNormal3f( 0.0,
+                    0.0,
+                    ( (m_Layer == LAYER_N_BACK) || (m_Layer == SILKSCREEN_N_BACK) ) ? -1.0 : 1.0 );
+        zpos = g_Parm_3D_Visu.m_LayerZcoord[m_Layer];
+
+        switch( m_Shape )
+        {
+        case S_SEGMENT:
+            Draw3D_FilledSegment( x, -y, fx, -fy, w, zpos );
+            break;
+
+        case S_CIRCLE:
+            Draw3D_CircleSegment( x, -y, fx, -fy, w, zpos );
+            break;
+
+        case S_ARC:
+            Draw3D_ArcSegment( x, -y, fx, -fy, (double) m_Angle, w, zpos );
+            break;
+
+        case S_POLYGON:
             {
                 // We must compute true coordinates from m_PolyPoints
                 // which are relative to module position and module orientation = 0
@@ -806,54 +4396,6 @@
             }
             break;
 
-            default:
-                s.Printf( wxT( "Error: Shape nr %d not implemented!\n" ), m_Shape );
-                D( printf( "%s", TO_UTF8( s ) ); )
-                break;
-            }
-        }
-    }
-    else
-    {
-        glNormal3f( 0.0, 0.0, (m_Layer == LAYER_N_BACK) ? -1.0 : 1.0 );
-        zpos = g_Parm_3D_Visu.m_LayerZcoord[m_Layer];
-
-        switch( m_Shape )
-        {
-        case S_SEGMENT:
-            Draw3D_FilledSegment( x, -y, fx, -fy, w, zpos );
-            break;
-
-        case S_CIRCLE:
-            Draw3D_CircleSegment( x, -y, fx, -fy, w, zpos );
-            break;
-
-        case S_ARC:
-            Draw3D_ArcSegment( x, -y, fx, -fy, (double) m_Angle, w, zpos );
-            break;
-
-        case S_POLYGON:
-        {
-            // We must compute true coordinates from m_PolyPoints
-            // which are relative to module position and module orientation = 0
-            std::vector<wxPoint> points = m_PolyPoints;
-            MODULE* module = (MODULE*) m_Parent;
-
-            if( module == NULL )
-                break;
-
-            for( unsigned ii = 0; ii < points.size(); ii++ )
-            {
-                wxPoint& pt = points[ii];
-
-                RotatePoint( &pt.x, &pt.y, module->GetOrientation() );
-                pt += module->m_Pos;
-            }
-
-            glcanvas->Draw3D_Polygon( points, zpos );
-        }
-        break;
-
         default:
             s.Printf( wxT( "Error: Shape nr %d not implemented!\n" ), m_Shape );
             D( printf( "%s", TO_UTF8( s ) ); )
@@ -871,99 +4413,269 @@
         dx, dx0, dy, dy0,
         delta_cx, delta_cy,
         xc, yc;
-    int     angle;
-    double  scale;
-    double  zpos;
+    int angle;
+    double scale;
+    double zpos = 0;
     wxPoint shape_pos;
-    double  x, y, r, w, hole, holeX, holeY;
-    double  drillx, drilly;
-    bool    Oncu, Oncmp, Both;
-    int     color;
+    double w, hole, holeX, holeY;
+    double drillx, drilly;
+    bool Oncu, Oncmp, Both;
+    int color;
+    double rm   = GetSolderMaskMargin();
+    wxSize rp   = GetSolderPasteMargin();
+    double offX, offY;
 
-    scale = g_Parm_3D_Visu.m_BoardScale;
-    holeX = (double) m_Drill.x * scale / 2;
-    holeY = (double) m_Drill.y * scale / 2;
-    hole  = fmin( holeX, holeY );
+    scale   = g_Parm_3D_Visu.m_BoardScale;
+    holeX   = (double) m_Drill.x * scale / 2;
+    holeY   = (double) m_Drill.y * scale / 2;
+    offX    = (double) m_Offset.x * scale;
+    offY    = (double) m_Offset.y * scale;
+    hole    = fmin( holeX, holeY );
 
     // Calculate the center of the pad.
-    shape_pos = ReturnShapePos();
+    shape_pos = m_Pos;
     ux0 = shape_pos.x;
     uy0 = shape_pos.y;
     xc  = ux0;
     yc  = uy0;
 
-    dx = dx0 = m_Size.x >> 1;
-    dy = dy0 = m_Size.y >> 1;
-
-    angle  = m_Orient;
-    drillx = m_Pos.x * scale;
-    drilly = m_Pos.y * scale;
-
-    // Draw the pad hole (TODO: draw OBLONG hole)
+    dx  = dx0 = m_Size.x / 2;
+    dy  = dy0 = m_Size.y / 2;
+
+    angle   = m_Orient;
+    drillx  = m_Pos.x * scale;
+    drilly  = m_Pos.y * scale;
+
+#define ZLO ( 9 - ( (layer & 6) >> 1 ) )
+
+    if( (tmonly & 1) == 1 )
+    {
+        nlmax   = g_Parm_3D_Visu.m_Layers - 1;
+        Oncu    = (m_layerMask & LAYER_BACK) ? true : false;
+        Oncmp   = (m_layerMask & LAYER_FRONT) ? true : false;
+        Both    = Oncu && Oncmp;
+
+        switch( m_PadShape & 0x7F )
+        {
+        case PAD_CIRCLE:
+        case PAD_OVAL:
+            rm *= scale;
+
+            if( dx > dy )    // Horizontal ellipse
+            {
+                delta_cx    = dx - dy;
+                delta_cy    = 0;
+                w = m_Size.y * scale;
+            }
+            else    // Vertical ellipse
+            {
+                delta_cx    = 0;
+                delta_cy    = dy - dx;
+                w = m_Size.x * scale;
+            }
+
+            RotatePoint( &delta_cx, &delta_cy, angle );
+
+            {
+                double ox, oy, fx, fy;
+                ox  = (double) ( ux0 + delta_cx ) * scale;
+                oy  = (double) ( uy0 + delta_cy ) * scale;
+                fx  = (double) ( ux0 - delta_cx ) * scale;
+                fy  = (double) ( uy0 - delta_cy ) * scale;
+
+                layer = (tmonly & 2) ? SOLDERMASK_N_BACK : SOLDERMASK_N_FRONT;
+
+                if( ( (1 << layer) & m_layerMask ) != 0 )
+                {
+                    Draw3D_FilledSegmentWithHoleMask( ox,
+                                                      -oy,
+                                                      fx,
+                                                      -fy,
+                                                      w + rm,
+                                                      drillx,
+                                                      -drilly,
+                                                      hole,
+                                                      zpos,
+                                                      offX,
+                                                      offY, angle );
+                }
+            }
+            break;
+
+        case PAD_RECT:
+        case PAD_TRAPEZOID:
+            {
+                wxPoint coord[5];
+                wxRealPoint fcoord[8], f_hole_coord[8];
+                layer = (tmonly & 2) ? SOLDERMASK_N_BACK : SOLDERMASK_N_FRONT;
+
+                if( ( (1 << layer) & m_layerMask ) != 0 )
+                {
+                    wxSize lrp;
+                    lrp.x   = rm;
+                    lrp.y   = rm;
+                    BuildPadPolygon( coord, lrp, angle );
+
+                    RotatePoint( &offX, &offY, angle );
+
+                    for( ii = 0; ii < 4; ii++ )
+                    {
+                        coord[ii].x += ux0 + offX / scale;      // TSP_20120604
+                        coord[ii].y += uy0 + offY / scale;
+                        ll = ii * 2;
+                        fcoord[ll].x    = coord[ii].x * scale;
+                        fcoord[ll].y    = coord[ii].y * scale;
+                    }
+
+                    for( ii = 0; ii < 7; ii += 2 )
+                    {
+                        ll = ll + 2;
+
+                        if( ll > 7 )
+                            ll -= 8;
+
+                        fcoord[ii + 1].x    = (fcoord[ii].x + fcoord[ll].x) / 2;
+                        fcoord[ii + 1].y    = (fcoord[ii].y + fcoord[ll].y) / 2;
+                    }
+
+                    for( ii = 0; ii < 8; ii++ )
+                    {
+                        f_hole_coord[ii].x  = -hole * 0.707;
+                        f_hole_coord[ii].y  = hole * 0.707;
+                        RotatePoint( &f_hole_coord[ii].x, &f_hole_coord[ii].y, angle -
+                                     (ii * 450) );
+                        f_hole_coord[ii].x  += drillx;
+                        f_hole_coord[ii].y  += drilly;
+                    }
+
+                    for( ii = 0; ii < 4; ii++ )
+                    {
+                        p_data.push_back( CPolyPt( (int) (coord[ii].x), (int) (coord[ii].y) ) );
+                    }
+                }
+            }
+            break;
+
+        default:
+            break;
+        }
+
+        return;
+    }
+    else if( tmonly == 2 )
+    {
+        // Draw the pad hole
+        if( holeX && holeY )
+        {
+            if( holeX == holeY )
+            {
+                Draw3D_FilledCylinderMask( drillx, -drilly, hole,
+                                           g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_FRONT], 0.0 );
+            }
+            else
+            {
+                double ldx, ldy;
+
+                if( holeX > holeY )    // Horizontal oval
+                {
+                    ldx = holeX - holeY;
+                    ldy = 0;
+                    w   = m_Size.y * scale;
+                }
+                else    // Vertical oval
+                {
+                    ldx = 0;
+                    ldy = holeY - holeX;
+                    w   = m_Size.x * scale;
+                }
+
+                RotatePoint( &ldx, &ldy, angle );
+
+                {
+                    double ox, oy, fx, fy;
+                    ox  = ( (double) ( ux0 ) ) * scale + ldx;
+                    oy  = ( (double) ( uy0 ) ) * scale + ldy;
+                    fx  = ( (double) ( ux0 ) ) * scale - ldx;
+                    fy  = ( (double) ( uy0 ) ) * scale - ldy;
+
+                    double height = g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_FRONT];
+                    Draw3D_FilledOblongMask( ox, -oy, fx, -fy, drillx, -drilly, hole, height,
+                                             0.0 );
+                }
+            }
+        }
+
+        return;
+    }
+
+    // Draw the pad hole
     if( holeX && holeY )
     {
-        SetGLColor( DARKGRAY );
-        Draw3D_FilledCylinder( drillx, -drilly, hole,
-                               g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_FRONT], 0.0 );
+        if( (GetAttribute() == PAD_STANDARD) )
+        {
+            MySetGLColor( DARKGRAY, 0, 1 );
+
+            if( holeX == holeY )
+            {
+                Draw3D_FilledCylinder( drillx, -drilly, hole,
+                                       g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_FRONT], 0.0 );
+            }
+            else
+            {
+                double ldx, ldy;
+
+                if( holeX > holeY ) // Horizontal oval
+                {
+                    ldx = holeX - holeY;
+                    ldy = 0;
+                    w   = m_Size.y * scale;
+                }
+                else // Vertical oval
+                {
+                    ldx = 0;
+                    ldy = holeY - holeX;
+                    w   = m_Size.x * scale;
+                }
+
+                RotatePoint( &ldx, &ldy, angle );
+                ldx /= scale;
+                ldy /= scale;
+
+                {
+                    double ox, oy, fx, fy;
+                    ox  = (double) ( (double) ux0 + ldx ) * scale;
+                    oy  = (double) ( (double) uy0 + ldy ) * scale;
+                    fx  = (double) ( (double) ux0 - ldx ) * scale;
+                    fy  = (double) ( (double) uy0 - ldy ) * scale;
+
+                    double height = g_Parm_3D_Visu.m_LayerZcoord[LAYER_N_FRONT];
+                    Draw3D_FilledOblong( ox, -oy, fx, -fy, drillx, -drilly, hole, height, 0.0 );
+                }
+            }
+        }
     }
 
-    glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
-    nlmax = g_Parm_3D_Visu.m_Layers - 1;
-    Oncu  = (m_layerMask & LAYER_BACK) ? true : false;
-    Oncmp = (m_layerMask & LAYER_FRONT) ? true : false;
-    Both  = Oncu && Oncmp;
+    glNormal3f( 0.0, 0.0, 1.0 );    // Normal is Z axis
+    nlmax   = g_Parm_3D_Visu.m_Layers - 1;
+    Oncu    = (m_layerMask & LAYER_BACK) ? true : false;
+    Oncmp   = (m_layerMask & LAYER_FRONT) ? true : false;
+    Both    = Oncu && Oncmp;
 
     switch( m_PadShape & 0x7F )
     {
     case PAD_CIRCLE:
-        x = xc * scale;
-        y = yc * scale;
-        r = (double) dx * scale;
-
-        for( layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; layer++ )
-        {
-            if( layer && (layer == nlmax) )
-                layer = LAYER_N_FRONT;
-
-            if( (layer == LAYER_N_FRONT) && !Oncmp )
-                continue;
-
-            if( (layer == LAYER_N_BACK) && !Oncu )
-                continue;
-
-            if( (layer > FIRST_COPPER_LAYER) && (layer < LAST_COPPER_LAYER) && !Both )
-                continue;
-
-            color = g_ColorsSettings.GetLayerColor( layer );
-
-            if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
-                continue;
-
-            SetGLColor( color );
-            glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
-            zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
-
-            if( layer == LAYER_N_BACK )
-                zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
-            else
-                zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
-
-            Draw3D_FilledCircle( x, -y, r, hole, zpos );
-        }
-
-        break;
-
     case PAD_OVAL:
-        if( dx > dy ) // Horizontal ellipse
+
+        if( dx > dy )    // Horizontal ellipse
         {
-            delta_cx = dx - dy;
-            delta_cy = 0;
+            delta_cx    = dx - dy;
+            delta_cy    = 0;
             w = m_Size.y * scale;
         }
-        else // Vertical ellipse
+        else    // Vertical ellipse
         {
-            delta_cx = 0;
-            delta_cy = dy - dx;
+            delta_cx    = 0;
+            delta_cy    = dy - dx;
             w = m_Size.x * scale;
         }
 
@@ -971,40 +4683,61 @@
 
         {
             double ox, oy, fx, fy;
-            ox = (double) ( ux0 + delta_cx ) * scale;
-            oy = (double) ( uy0 + delta_cy ) * scale;
-            fx = (double) ( ux0 - delta_cx ) * scale;
-            fy = (double) ( uy0 - delta_cy ) * scale;
+            ox  = (double) ( ux0 + delta_cx ) * scale;
+            oy  = (double) ( uy0 + delta_cy ) * scale;
+            fx  = (double) ( ux0 - delta_cx ) * scale;
+            fy  = (double) ( uy0 - delta_cy ) * scale;
 
-            for( layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; layer++ )
+            for( layer = FIRST_NO_COPPER_LAYER; layer <= SILKSCREEN_N_FRONT; layer++ )
             {
-                if( layer && (layer == nlmax) )
-                    layer = LAYER_N_FRONT;
-
-                if( (layer == LAYER_N_FRONT) && !Oncmp )
-                    continue;
-
-                if( (layer == LAYER_N_BACK) && !Oncu )
-                    continue;
-
-                if( (layer > FIRST_COPPER_LAYER) && (layer < LAST_COPPER_LAYER) && !Both )
+                if( ( (1 << layer) & m_layerMask ) == 0 )
+                    continue;
+
+                if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
                     continue;
 
                 color = g_ColorsSettings.GetLayerColor( layer );
-                glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
-
-                if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
-                    continue;
-
-                SetGLColor( color );
+                glNormal3f( 0.0, 0.0, (layer & 1) ? 1.0 : -1.0 );
+
+                MySetGLColor( color, layer, 0.8 );
                 zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
 
-                if( layer == LAYER_N_BACK )
-                    zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
+                if( layer & 1 )
+                    zpos = zpos + ZLO * g_Parm_3D_Visu.m_BoardScale;
                 else
-                    zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
+                    zpos = zpos - ZLO * g_Parm_3D_Visu.m_BoardScale;
 
-                Draw3D_FilledSegmentWithHole( ox, -oy, fx, -fy, w, drillx, -drilly, hole, zpos );
+                if( (layer == SOLDERPASTE_N_BACK) || (layer == SOLDERPASTE_N_FRONT) )
+                {
+                    Draw3D_FilledSegmentWithHole( ox,
+                                                  -oy,
+                                                  fx,
+                                                  -fy,
+                                                  w + (double) rp.x * scale,
+                                                  drillx,
+                                                  -drilly,
+                                                  0,
+                                                  zpos,
+                                                  offX,
+                                                  offY, 0, 0, dx, dy, angle );
+                }
+                else if( (layer == SILKSCREEN_N_BACK) || (layer == SILKSCREEN_N_FRONT) )
+                {
+                }
+                else
+                {
+                    Draw3D_FilledSegmentWithHole( ox,
+                                                  -oy,
+                                                  fx,
+                                                  -fy,
+                                                  w,
+                                                  drillx,
+                                                  -drilly,
+                                                  hole,
+                                                  zpos,
+                                                  offX,
+                                                  offY, holeX, holeY, dx, dy, angle );
+                }
             }
         }
 
@@ -1012,82 +4745,56 @@
 
     case PAD_RECT:
     case PAD_TRAPEZOID:
-    {
-        wxPoint  coord[5];
-        wxRealPoint  fcoord[8], f_hole_coord[8];
-        BuildPadPolygon( coord, wxSize(0,0), angle );
-
-        for( ii = 0; ii < 4; ii++ )
-        {
-            coord[ii].x += ux0;
-            coord[ii].y += uy0;
-            ll = ii * 2;
-            fcoord[ll].x = coord[ii].x *scale;
-            fcoord[ll].y = coord[ii].y *scale;
-        }
-
-        for( ii = 0; ii < 7; ii += 2 )
-        {
-            ll = ii + 2;
-
-            if( ll > 7 )
-                ll -= 8;
-
-            fcoord[ii + 1].x = (fcoord[ii].x + fcoord[ll].x) / 2;
-            fcoord[ii + 1].y = (fcoord[ii].y + fcoord[ll].y) / 2;
-        }
-
-        for( ii = 0; ii < 8; ii++ )
-        {
-            f_hole_coord[ii].x = -hole * 0.707;
-            f_hole_coord[ii].y = hole * 0.707;
-            RotatePoint( &f_hole_coord[ii].x, &f_hole_coord[ii].y, angle - (ii * 450) );
-            f_hole_coord[ii].x += drillx;
-            f_hole_coord[ii].y += drilly;
-        }
-
-        for( layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; layer++ )
-        {
-            if( layer && (layer == nlmax) )
-                layer = LAYER_N_FRONT;
-
-            if( (layer == LAYER_N_FRONT) && !Oncmp )
-                continue;
-
-            if( (layer == LAYER_N_BACK) && !Oncu )
-                continue;
-
-            if( (layer > FIRST_COPPER_LAYER) && (layer < LAST_COPPER_LAYER) && !Both )
-                continue;
-
-            color = g_ColorsSettings.GetLayerColor( layer );
-            glNormal3f( 0.0, 0.0, (layer == LAYER_N_BACK) ? -1.0 : 1.0 );
-
-            if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
-                continue;
-
-            SetGLColor( color );
-            zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
-
-            if( layer == LAYER_N_BACK )
-                zpos = zpos - 5 * g_Parm_3D_Visu.m_BoardScale;
-            else
-                zpos = zpos + 5 * g_Parm_3D_Visu.m_BoardScale;
-
-            glBegin( GL_QUAD_STRIP );
-
-            for( ii = 0; ii < 8; ii++ )
+        {
+            wxPoint coord[5];
+            BuildPadPolygon( coord, wxSize( 0, 0 ), angle );
+            RotatePoint( &offX, &offY, angle );
+
+            for( layer = FIRST_NO_COPPER_LAYER; layer <= SILKSCREEN_N_FRONT; layer++ )
             {
-                glVertex3f( f_hole_coord[ii].x, -f_hole_coord[ii].y, zpos );
-                glVertex3f( fcoord[ii].x, -fcoord[ii].y, zpos );
+                if( ( (1 << layer) & m_layerMask ) == 0 )
+                    continue;
+
+                color = g_ColorsSettings.GetLayerColor( layer );
+
+                glNormal3f( 0.0, 0.0, (layer & 1) ? 1.0 : -1.0 );
+
+                if( g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( layer ) == false )
+                    continue;
+
+                {
+                    wxSize lrp;
+
+                    if( (layer == SOLDERPASTE_N_BACK) || (layer == SOLDERPASTE_N_FRONT) )
+                    {
+                        lrp = rp;
+                    }
+                    else
+                    {
+                        lrp.x   = 0;
+                        lrp.y   = 0;
+                    }
+
+                    BuildPadPolygon( coord, lrp, angle );
+                }
+                MySetGLColor( color, layer, 0.8 );
+                zpos = g_Parm_3D_Visu.m_LayerZcoord[layer];
+
+                if( layer & 1 )
+                    zpos = zpos + ZLO * g_Parm_3D_Visu.m_BoardScale;
+                else
+                    zpos = zpos - ZLO * g_Parm_3D_Visu.m_BoardScale;
+
+                if( (layer == SILKSCREEN_N_BACK) || (layer == SILKSCREEN_N_FRONT) )
+                {
+                }
+                else
+                {
+                    Draw3D_FilledPolyWithHole( coord, 5, ux0, uy0, offX, offY, 0, 0, zpos, angle );
+                }
             }
-
-            glVertex3f( f_hole_coord[0].x, -f_hole_coord[0].y, zpos );
-            glVertex3f( fcoord[0].x, -fcoord[0].y, zpos );
-            glEnd();
         }
-    }
-    break;
+        break;
 
     default:
         break;
@@ -1095,35 +4802,49 @@
 }
 
 
-void SetGLColor( int color )
+void MySetGLColor( int color, int layer, double alpha )
 {
-    double       red, green, blue;
+    double red, green, blue;
     StructColors colordata = ColorRefs[color & MASKCOLOR];
 
-    red   = colordata.m_Red / 255.0;
-    blue  = colordata.m_Blue / 255.0;
-    green = colordata.m_Green / 255.0;
-    glColor3f( red, green, blue );
+    if( layer < 0 )
+    {
+        red     = colordata.m_Red / 255.0;
+        blue    = colordata.m_Blue / 255.0;
+        green   = colordata.m_Green / 255.0;
+        glColor4f( red, green, blue, 1 );
+    }
+    else if( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] && (layer < 16) )
+    {
+        glColor3f( 0.6, 0.55, 0.2 );
+    }
+    else
+    {
+        red     = colordata.m_Red / 255.0;
+        blue    = colordata.m_Blue / 255.0;
+        green   = colordata.m_Green / 255.0;
+        glColor4f( red, green, blue, (alpha < 0) ? 0.9 : alpha );
+    }
 }
 
 
 static void Draw3D_FilledCircle( double posx, double posy,
-                                 double rayon, double hole, double zpos )
+                                 double radius, double holeradius, double zpos )
 {
-    int    ii, slice = 16;
+    int ii, slice = C_SLICE * 2;
     double x, y;
 
     glBegin( GL_QUAD_STRIP );
 
     for( ii = 0; ii <= slice; ii++ )
     {
-        x = hole;
-        y = 0.0;
-        RotatePoint( &x, &y, ii * 225 );
+        x   = holeradius;
+        y   = 0.0;
+        RotatePoint( &x, &y, ii * _SEG( slice ) );
         glVertex3f( x + posx, y + posy, zpos );
-        x = rayon;
-        y = 0.0;
-        RotatePoint( &x, &y, ii * 225 );
+        x   = radius;
+        y   = 0.0;
+        RotatePoint( &x, &y, ii * _SEG( slice ) );
         glVertex3f( x + posx, y + posy, zpos );
     }
 
@@ -1131,29 +4852,29 @@
 }
 
 
-static void Draw3D_FilledCylinder( double posx, double posy, double rayon,
+static void Draw3D_FilledCylinder( double posx, double posy, double radius,
                                    double height, double zpos )
 {
-    int        ii;
-    double     x, y;
-
-#define NB_SEGM 12
-    std::vector< S3D_Vertex > coords;
+    int ii;
+    double x, y;
+
+    std::vector<S3D_Vertex> coords;
+
     coords.resize( 4 );
 
-    double     tmp = DataScale3D;
+    double tmp = DataScale3D;
 
-    DataScale3D = 1.0; // Coordinate is already in range for Set_Object_Data();
-    coords[0].x = coords[1].x = posx + rayon;
+    DataScale3D = 1.0;    // Coordinate is already in range for Set_Object_Data();
+    coords[0].x = coords[1].x = posx + radius;
     coords[0].y = coords[1].y = posy;
     coords[0].z = coords[3].z = zpos;
     coords[1].z = coords[2].z = zpos + height;
 
-    for( ii = 0; ii <= NB_SEGM; ii++ )
+    for( ii = 1; ii <= C_SLICE * 2; ii++ )
     {
-        x = rayon;
-        y = 0.0;
-        RotatePoint( &x, &y, ii * (3600 / NB_SEGM) );
+        x   = radius;
+        y   = 0.0;
+        RotatePoint( &x, &y, ii * _SEG( C_SLICE * 2 ) );
         coords[2].x = coords[3].x = posx + x;
         coords[2].y = coords[3].y = posy + y;
         Set_Object_Data( coords );
@@ -1163,8 +4884,156 @@
         coords[1].y = coords[3].y;
     }
 
-    glNormal3f( 0.0, 0.0, 1.0 ); // Normal is Z axis
-    DataScale3D = tmp;
+    glNormal3f( 0.0, 0.0, 1.0 );    // Normal is Z axis
+    DataScale3D = tmp;
+}
+
+
+static void    Draw3D_FilledOblong( double startx, double starty,
+                                    double endx, double endy,
+                                    double holex,
+                                    double holey, double holeradius,
+                                    double height, double zpos )
+{
+    double ddx, ddy, dx, dy;
+    double angle;
+    std::vector<S3D_Vertex> coords;
+
+    coords.resize( 4 );
+
+    double tmp = DataScale3D;
+
+    DataScale3D = 1.0;    // Coordinate is already in range for Set_Object_Data();
+
+    dx  = endx - startx;
+    dy  = endy - starty;
+    angle   = ATAN2( dy, dx );
+    dx      = 0;
+    dy      = holeradius;
+    RotatePoint( &dx, &dy, angle );
+    coords[0].x = coords[1].x = startx + dx;
+    coords[0].y = coords[1].y = starty - dy;
+    coords[2].x = coords[3].x = endx + dx;
+    coords[2].y = coords[3].y = endy - dy;
+    coords[0].z = coords[3].z = zpos;
+    coords[1].z = coords[2].z = zpos + height;
+    Set_Object_Data( coords );
+
+    for( int ii = 1; ii <= C_SLICE; ii++ )
+    {
+        ddx = dx;
+        ddy = -dy;
+        RotatePoint( &ddx, &ddy, -ii * _SEG2( C_SLICE ) );
+        coords[0].x = coords[2].x;
+        coords[0].y = coords[2].y;
+        coords[1].x = coords[3].x;
+        coords[1].y = coords[3].y;
+        coords[2].x = coords[3].x = endx + ddx;
+        coords[2].y = coords[3].y = endy + ddy;
+        Set_Object_Data( coords );
+    }
+
+    coords[0].x = coords[1].x = endx - dx;
+    coords[0].y = coords[1].y = endy + dy;
+    coords[2].x = coords[3].x = startx - dx;
+    coords[2].y = coords[3].y = starty + dy;
+    coords[0].z = coords[3].z = zpos;
+    coords[1].z = coords[2].z = zpos + height;
+    Set_Object_Data( coords );
+
+    for( int ii = 1; ii <= C_SLICE; ii++ )
+    {
+        ddx = -dx;
+        ddy = dy;
+        RotatePoint( &ddx, &ddy, -ii * _SEG2( C_SLICE ) );
+        coords[0].x = coords[2].x;
+        coords[0].y = coords[2].y;
+        coords[1].x = coords[3].x;
+        coords[1].y = coords[3].y;
+        coords[2].x = coords[3].x = startx + ddx;
+        coords[2].y = coords[3].y = starty + ddy;
+        Set_Object_Data( coords );
+    }
+
+    // Calculate the coordinates of the segment assumed horizontal.
+    // Then turn the strips of the desired angle.
+
+    glNormal3f( 0.0, 0.0, 1.0 );    // Normal is Z axis
+    DataScale3D = tmp;
+}
+
+
+static void    Draw3D_FilledOblongMask( double startx, double starty,
+                                        double endx, double endy,
+                                        double holex,
+                                        double holey, double holeradius,
+                                        double height, double zpos )
+{
+    double ddx, ddy, dx, dy;
+    int spii = p_data.size();
+    double angle;
+
+    dx  = endx - startx;
+    dy  = endy - starty;
+    angle   = ATAN2( dy, dx );
+    dx      = 0;
+    dy      = holeradius;
+    RotatePoint( &dx, &dy, angle );
+
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        ddx = dx;
+        ddy = -dy;
+        RotatePoint( &ddx, &ddy, -ii * _SEG2( C_SLICE ) );
+        p_data.push_back( CPolyPt( (int) ( (endx + ddx) / g_Parm_3D_Visu.m_BoardScale ),
+                                   (int) (-(endy + ddy) / g_Parm_3D_Visu.m_BoardScale) ) );
+    }
+
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        ddx = -dx;
+        ddy = dy;
+        RotatePoint( &ddx, &ddy, -ii * _SEG2( C_SLICE ) );
+        p_data.push_back( CPolyPt( (int) ( (startx + ddx) / g_Parm_3D_Visu.m_BoardScale ),
+                                   (int) (-(starty + ddy) / g_Parm_3D_Visu.m_BoardScale) ) );
+    }
+
+    for( int ii = 0; ii < ( (int) p_data.size() - spii ) / 2; ii++ )
+    {
+        CPolyPt h = p_data[spii + ii];
+        p_data[spii + ii] = p_data[p_data.size() - ii - 1];
+        p_data[p_data.size() - ii - 1] = h;
+    }
+}
+
+
+static void Draw3D_FilledCylinderMask( double posx, double posy, double radius,
+                                       double height, double zpos )
+{
+    int ii, slice = C_SLICE * 2;
+    double x, y;
+
+    for( ii = 0; ii <= slice; ii++ )
+    {
+        x   = radius;
+        y   = 0.0;
+        RotatePoint( &x, &y, ii * _SEG( slice ) );
+        x   = (x + posx) / g_Parm_3D_Visu.m_BoardScale;
+        y   = -( (y + posy) / g_Parm_3D_Visu.m_BoardScale );
+        p_data.push_back( CPolyPt( (int) x, (int) y ) );
+    }
+}
+
+
+static void Draw3D_Mask( double startx, double starty, double endx,
+                         double endy, double width, double zpos )
+{
+    startx  /= g_Parm_3D_Visu.m_BoardScale;
+    starty  /= g_Parm_3D_Visu.m_BoardScale;
+    endx    /= g_Parm_3D_Visu.m_BoardScale;
+    endy    /= g_Parm_3D_Visu.m_BoardScale;
+    e_data.push_back( CPolyPt( (int) startx, (int) starty ) );
+    e_data.push_back( CPolyPt( (int) endx, (int) endy ) );
 }
 
 
@@ -1173,13 +5042,13 @@
                                   double endy, double width, double zpos )
 {
     double dx, dy, x, y, firstx = 0, firsty = 0;
-    int    ii, angle;
+    double angle;
 
     // Calculate the coordinates of the segment assumed horizontal.
     // Then turn the strips of the desired angle.
-    dx    = endx - startx;
-    dy    = endy - starty;
-    angle = (int) ( ( atan2( dy, dx ) * 1800 / M_PI ) + 0.5 );
+    dx      = endx - startx;
+    dy      = endy - starty;
+    angle   = ATAN2( dy, dx );
 
     RotatePoint( &dx, &dy, angle );
     width /= 2;
@@ -1187,28 +5056,28 @@
     glBegin( GL_POLYGON );
 
     // Trace the flare to right (1st half polygon at the end of the segment)
-    for( ii = 0; ii <= 8; ii++ )
+    for( int ii = 0; ii <= C_SLICE; ii++ )
     {
-        x = 0.0;
-        y = -width;
-        RotatePoint( &x, &y, -ii * 225 );
+        x   = 0.0;
+        y   = -width;
+        RotatePoint( &x, &y, -ii * _SEG2( C_SLICE ) );
         x += dx;
         RotatePoint( &x, &y, -angle );
         glVertex3f( startx + x, starty + y, zpos );
 
         if( ii == 0 )
         {
-            firstx = startx + x;
-            firsty = starty + y;
+            firstx  = startx + x;
+            firsty  = starty + y;
         }
     }
 
     // Rounding the left (2nd half polygon is the origin of the segment)
-    for( ii = 0; ii <= 8; ii++ )
+    for( int ii = 0; ii <= C_SLICE; ii++ )
     {
-        int jj = ii * 225;
-        x = 0.0;
-        y = width;
+        double jj = ii * _SEG2( C_SLICE );
+        x   = 0.0;
+        y   = width;
         RotatePoint( &x, &y, -angle - jj );
         glVertex3f( startx + x, starty + y, zpos );
     }
@@ -1218,94 +5087,502 @@
 }
 
 
+// Draw a polygon similar to a segment has rounded tips
+static void Draw3D_FilledSegmentMask( double            startx,
+                                      double            starty,
+                                      double            endx,
+                                      double            endy,
+                                      double            width,
+                                      double            zpos,
+                                      GLUtesselator*    tess,
+                                      int               orient )
+{
+    double dx, dy, x, y, firstx = 0, firsty = 0;
+    double angle;
+    GLdouble v_data[3];
+    int lpii = p_data.size();
+
+    v_data[2] = g_Parm_3D_Visu.m_ActZpos;
+
+    // Calculate the coordinates of the segment assumed horizontal.
+    // Then turn the strips of the desired angle.
+    dx      = endx - startx;
+    dy      = endy - starty;
+    angle   = ATAN2( dy, dx );
+
+    RotatePoint( &dx, &dy, angle );
+    width /= 2;
+
+    // Trace the flare to right (1st half polygon at the end of the segment)
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        x   = 0.0;
+        y   = -width;
+        RotatePoint( &x, &y, -ii * _SEG2( C_SLICE ) );
+        x += dx;
+        RotatePoint( &x, &y, -angle );
+        p_data.push_back( CPolyPt( (int) ( (startx + x) / g_Parm_3D_Visu.m_BoardScale ),
+                                   -(int) ( (starty + y) / g_Parm_3D_Visu.m_BoardScale ) ) );
+
+        if( ii == 0 )
+        {
+            firstx  = startx + x;
+            firsty  = starty + y;
+        }
+    }
+
+    // Rounding the left (2nd half polygon is the origin of the segment)
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        double jj = ii * _SEG2( C_SLICE );
+        x   = 0.0;
+        y   = width;
+        RotatePoint( &x, &y, -angle - jj );
+        p_data.push_back( CPolyPt( (int) ( (startx + x) / g_Parm_3D_Visu.m_BoardScale ),
+                                   -(int) ( (starty + y) / g_Parm_3D_Visu.m_BoardScale ) ) );
+    }
+
+    gluTessBeginContour( tess );
+
+    for( int ii = p_data.size() - 1; ii >= lpii; ii-- )
+    {
+        v_data[0]   = p_data[ii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -p_data[ii].y * g_Parm_3D_Visu.m_BoardScale;
+        gluTessVertex( tess, v_data, &p_data[ii] );
+    }
+
+    gluTessEndContour( tess );
+}
+
+
+static void Draw3D_FilledSegmentMask( double                startx,
+                                      double                starty,
+                                      double                endx,
+                                      double                endy,
+                                      double                width,
+                                      double                zpos,
+                                      std::vector<CPolyPt>* p_data,
+                                      int                   orient )
+{
+    double dx, dy, x, y, firstx = 0, firsty = 0;
+
+    // Calculate the coordinates of the segment assumed horizontal.
+    // Then turn the strips of the desired angle.
+    dx  = endx - startx;
+    dy  = endy - starty;
+    double angle = ATAN2( dy, dx );
+
+    RotatePoint( &dx, &dy, angle );
+    width /= 2;
+
+    // Trace the flare to right (1st half polygon at the end of the segment)
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        x   = 0.0;
+        y   = -width;
+        RotatePoint( &x, &y, -ii * _SEG2( C_SLICE ) );
+        x += dx;
+        RotatePoint( &x, &y, -angle );
+        p_data->push_back( CPolyPt( (int) ( (startx + x) / g_Parm_3D_Visu.m_BoardScale ),
+                                    -(int) ( (starty + y) / g_Parm_3D_Visu.m_BoardScale ) ) );
+
+        if( ii == 0 )
+        {
+            firstx  = startx + x;
+            firsty  = starty + y;
+        }
+    }
+
+    // Rounding the left (2nd half polygon is the origin of the segment)
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        double jj = ii * _SEG2( C_SLICE );
+        x   = 0.0;
+        y   = width;
+        RotatePoint( &x, &y, -angle - jj );
+        p_data->push_back( CPolyPt( (int) ( (startx + x) / g_Parm_3D_Visu.m_BoardScale ),
+                                    -(int) ( (starty + y) / g_Parm_3D_Visu.m_BoardScale ) ) );
+    }
+}
+
+
+/* Draw a polygon with oblong hole
+ */
+static void Draw3D_FilledPolyWithHole( wxPoint* coord,
+                                       int      n,
+                                       double   x,
+                                       double   y,
+                                       double   offX,
+                                       double   offY,
+                                       double   holeX,
+                                       double   holeY,
+                                       double   zpos,
+                                       double   oang )
+{
+    GLUtesselator* tess = gluNewTess();
+    GLdouble v_data[3];
+    CPolyPt corner[1024];
+    int ii, ppii = 0;
+
+    v_data[2] = zpos;
+    g_Parm_3D_Visu.m_ActZpos = zpos;
+    gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+    gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+    gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+    gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+    gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+    gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD );
+
+    gluTessBeginPolygon( tess, NULL );
+
+    gluTessBeginContour( tess );
+
+    for( ii = 0; ii < n - 1; ii++ )
+    {
+        corner[ppii].x  = (int) ( (coord[ii].x + x + offX / g_Parm_3D_Visu.m_BoardScale) );
+        corner[ppii].y  = (int) ( (coord[ii].y + y + offY / g_Parm_3D_Visu.m_BoardScale) );
+        v_data[0]   = corner[ppii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -corner[ppii].y * g_Parm_3D_Visu.m_BoardScale;
+        gluTessVertex( tess, v_data, &corner[ppii] );
+        ppii++;
+    }
+
+    gluTessEndContour( tess );
+
+    // hole...
+    double startx   = x * g_Parm_3D_Visu.m_BoardScale;
+    double starty   = y * g_Parm_3D_Visu.m_BoardScale;
+    double hy;
+    double theta;
+    double holeradius;
+    double xin, yin;
+
+    if( holeX >= holeY )
+    {
+        // oang direction
+        holeradius = holeY;
+        hy = holeX - holeY;
+    }
+    else
+    {
+        // perpendicular
+        oang -= 900;
+        holeradius = holeX;
+        hy = holeY - holeX;
+    }
+
+    if( holeradius > 0 )
+    {
+        gluTessBeginContour( tess );
+
+        for( ii = 0; ii <= C_SLICE; ii++ )
+        {
+            xin     = 0.0;
+            yin     = -holeradius;
+            theta   = -ii* _SEG2( C_SLICE );
+
+            RotatePoint( &xin, &yin, theta );
+            xin += hy;
+            RotatePoint( &xin, &yin, oang );
+            corner[ppii].x  = (int) ( (startx + xin) / g_Parm_3D_Visu.m_BoardScale );
+            corner[ppii].y  = (int) ( (starty + yin) / g_Parm_3D_Visu.m_BoardScale );
+            v_data[0]   = corner[ppii].x * g_Parm_3D_Visu.m_BoardScale;
+            v_data[1]   = -corner[ppii].y * g_Parm_3D_Visu.m_BoardScale;
+            // gluTessVertex store pointers on data, not data, so do not store
+            // different corners values in a temporary variable
+            // but send pointer on each corner value in aCornersList
+            gluTessVertex( tess, v_data, &corner[ppii] );
+            ppii++;
+        }
+
+        // Layout of the rounded left (2nd half polygon is the origin of the
+        // segment)
+        for( ii = 0; ii <= C_SLICE; ii++ )
+        {
+            theta = -ii* _SEG2( C_SLICE );
+
+            xin = 0.0;
+            yin = holeradius;
+            RotatePoint( &xin, &yin, theta );
+            xin -= hy;
+            RotatePoint( &xin, &yin, oang );
+            corner[ppii].x  = (int) ( (startx + xin) / g_Parm_3D_Visu.m_BoardScale );
+            corner[ppii].y  = (int) ( (starty + yin) / g_Parm_3D_Visu.m_BoardScale );
+            v_data[0]   = corner[ppii].x * g_Parm_3D_Visu.m_BoardScale;
+            v_data[1]   = -corner[ppii].y * g_Parm_3D_Visu.m_BoardScale;
+            gluTessVertex( tess, v_data, &corner[ppii] );
+            ppii++;
+        }
+
+        gluTessEndContour( tess );
+    }
+
+    gluTessEndPolygon( tess );
+    gluDeleteTess( tess );
+}
+
+
 /* Draw a polygon similar to a segment ends with round hole
  */
-static void Draw3D_FilledSegmentWithHole( double startx, double starty,
-                                          double endx, double endy,
-                                          double width, double holex,
-                                          double holey, double holeradius,
-                                          double zpos )
+static void Draw3D_FilledSegmentWithHole( double    startx,
+                                          double    starty,
+                                          double    endx,
+                                          double    endy,
+                                          double    width,
+                                          double    holex,
+                                          double    holey,
+                                          double    holeradius,
+                                          double    zpos,
+                                          double    offX,
+                                          double    offY,
+                                          double    holeX,
+                                          double    holeY,
+                                          double    odx,
+                                          double    ody,
+                                          double    oang )
+{
+    GLUtesselator* tess = gluNewTess();
+    GLdouble v_data[3];
+    CPolyPt corner[1024];
+    int ppii = 0;
+    double x, y, xin, yin;
+    double angle, theta;
+    // Calculate the coordinates of the segment assumed horizontal
+    // Then turn the strips of the desired angle
+    // All calculations are done with startx, starty as the origin of the route
+    double hy = fabs( holeX - holeY );
+    double oendx    = endx;
+    double oendy    = endy;
+
+    v_data[2] = zpos;
+    g_Parm_3D_Visu.m_ActZpos = zpos;
+    gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+    gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+    gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )mytessErrorCB );
+    gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )mytessCPolyPt2Vertex );
+    gluTessCallback( tess, GLU_TESS_COMBINE, ( void (CALLBACK*) () )mytessCombineCB );
+    gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD );
+
+    gluTessBeginPolygon( tess, NULL );
+
+    gluTessBeginContour( tess );
+
+    endx    -= startx;
+    endy    -= starty;
+
+    if( (endx + endy) == 0 )
+    {
+        angle = oang;
+    }
+    else
+    {
+        angle = ATAN2( endy, endx );
+    }
+
+    RotatePoint( &endx, &endy, angle );
+    RotatePoint( &holex, &holey, angle );
+
+    width /= 2;
+
+    RotatePoint( &offX, &offY, oang );
+
+    // Path of the flare to right (1st half polygon at the end of the segment)
+    // around the half-hole drilling
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        x       = 0.0;
+        y       = -width;
+        theta   = -ii* _SEG2( C_SLICE );
+
+        RotatePoint( &x, &y, theta );
+        x += endx;
+        RotatePoint( &x, &y, -angle );
+        corner[ppii].x  = (int) ( (startx + x + offX ) / g_Parm_3D_Visu.m_BoardScale );
+        corner[ppii].y  = -(int) ( (starty + y - offY ) / g_Parm_3D_Visu.m_BoardScale );
+        v_data[0]   = corner[ppii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -corner[ppii].y * g_Parm_3D_Visu.m_BoardScale;
+        gluTessVertex( tess, v_data, &corner[ppii] );
+        ppii++;
+    }
+
+    // Layout of the rounded left (2nd half polygon is the origin of the
+    // segment)
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        theta = -ii* _SEG2( C_SLICE );
+
+        x   = 0.0;
+        y   = width;
+        RotatePoint( &x, &y, -angle + theta );
+        corner[ppii].x  = (int) ( (startx + x + offX) / g_Parm_3D_Visu.m_BoardScale );
+        corner[ppii].y  = -(int) ( (starty + y - offY) / g_Parm_3D_Visu.m_BoardScale );
+        v_data[0]   = corner[ppii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -corner[ppii].y * g_Parm_3D_Visu.m_BoardScale;
+        gluTessVertex( tess, v_data, &corner[ppii] );
+        ppii++;
+    }
+
+    gluTessEndContour( tess );
+
+    // hole...
+    startx  = (startx + oendx) / 2;
+    starty  = (starty + oendy) / 2;
+
+    if( (endx == 0) && (endy == 0) )
+    {
+        if( holeY > holeX )
+        {
+            angle += 900;
+        }
+    }
+    else
+    {
+        if( ( ( holeY > holeX ) && ( ody < odx) ) || ( ( holeY < holeX ) && ( ody > odx) ) )
+        {
+            angle += 900;
+        }
+    }
+
+    gluTessBeginContour( tess );
+
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        xin     = 0.0;
+        yin     = -holeradius;
+        theta   = -ii* _SEG2( C_SLICE );
+
+        RotatePoint( &xin, &yin, theta );
+        xin += hy;
+        RotatePoint( &xin, &yin, -angle );
+        corner[ppii].x  = (int) ( (startx + xin) / g_Parm_3D_Visu.m_BoardScale );
+        corner[ppii].y  = -(int) ( (starty + yin) / g_Parm_3D_Visu.m_BoardScale );
+        v_data[0]   = corner[ppii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -corner[ppii].y * g_Parm_3D_Visu.m_BoardScale;
+        // gluTessVertex store pointers on data, not data, so do not store
+        // different corners values in a temporary variable
+        // but send pointer on each corner value in aCornersList
+        gluTessVertex( tess, v_data, &corner[ppii] );
+        ppii++;
+    }
+
+    // Layout of the rounded left (2nd half polygon is the origin of the
+    // segment)
+    for( int ii = 0; ii <= C_SLICE; ii++ )
+    {
+        theta = -ii* _SEG2( C_SLICE );
+
+        xin = 0.0;
+        yin = holeradius;
+        RotatePoint( &xin, &yin, theta );
+        xin -= hy;
+        RotatePoint( &xin, &yin, -angle );
+        corner[ppii].x  = (int) ( (startx + xin) / g_Parm_3D_Visu.m_BoardScale );
+        corner[ppii].y  = -(int) ( (starty + yin) / g_Parm_3D_Visu.m_BoardScale );
+        v_data[0]   = corner[ppii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -corner[ppii].y * g_Parm_3D_Visu.m_BoardScale;
+        gluTessVertex( tess, v_data, &corner[ppii] );
+        ppii++;
+    }
+
+    gluTessEndContour( tess );
+
+    gluTessEndPolygon( tess );
+    gluDeleteTess( tess );
+}
+
+
+static void Draw3D_FilledSegmentWithHoleMask( double startx, double starty,
+                                              double endx, double endy,
+                                              double width, double holex,
+                                              double holey, double holeradius,
+                                              double zpos, double offX, double offY, double oang )
 {
     double x, y, xin, yin;
     double firstx = 0, firsty = 0, firstxin = 0, firstyin = 0;
-    int    ii, angle, theta;
+
+    int spii = p_data.size();
 
     // Calculate the coordinates of the segment assumed horizontal
     // Then turn the strips of the desired angle
     // All calculations are done with startx, starty as the origin of the route
-    endx  -= startx;
-    endy  -= starty;
-    holex -= startx;
-    holey -= starty;
-    angle  = (int) ( ( atan2( endy, endx ) * 1800 / M_PI ) + 0.5 );
+    endx    -= startx;
+    endy    -= starty;
+    holex   -= startx;
+    holey   -= starty;
+    double angle = ATAN2( endy, endx );
 
     RotatePoint( &endx, &endy, angle );
     RotatePoint( &holex, &holey, angle );
+    RotatePoint( &offX, &offY, oang );
     width /= 2;
 
-    glBegin( GL_QUAD_STRIP );
-
     // Path of the flare to right (1st half polygon at the end of the segment)
     // around the half-hole drilling
-    for( ii = 0; ii <= 8; ii++ )
+    for( int ii = 0; ii <= C_SLICE; ii++ )
     {
-        x     = 0.0;
-        y     = -width;
-        xin   = 0.0;
-        yin   = -holeradius;
-        theta = -ii * 225;
+        x   = 0.0;
+        y   = -width;
+        xin = 0.0;
+        yin = -holeradius;
+        double theta = -ii* _SEG2( C_SLICE );
+
         RotatePoint( &x, &y, theta );
         RotatePoint( &xin, &yin, theta );
         x += endx;
         RotatePoint( &x, &y, -angle );
         xin += holex;
         RotatePoint( &xin, &yin, -angle );
-        glVertex3f( startx + xin, starty + yin, zpos );
-        glVertex3f( startx + x, starty + y, zpos );
+        p_data.push_back( CPolyPt( (int) ( (startx + x + offX) / g_Parm_3D_Visu.m_BoardScale ),
+                                   -(int) ( (starty + y -
+                                             offY) / g_Parm_3D_Visu.m_BoardScale ) ) );
 
         if( ii == 0 )
         {
-            firstx   = startx + x;
-            firsty   = starty + y;
-            firstxin = startx + xin;
-            firstyin = starty + yin;
+            firstx  = startx + x;
+            firsty  = starty + y;
+            firstxin    = startx + xin;
+            firstyin    = starty + yin;
         }
     }
 
     // Layout of the rounded left (2nd half polygon is the origin of the
     // segment)
-    for( ii = 0; ii <= 8; ii++ )
+    for( int ii = 0; ii <= C_SLICE; ii++ )
     {
-        theta = -ii * 225;
-        x     = 0.0;
-        y     = width;
+        double theta = -ii* _SEG2( C_SLICE );
+
+        x   = 0.0;
+        y   = width;
         RotatePoint( &x, &y, -angle + theta );
         xin = 0.0;
         yin = holeradius;
         RotatePoint( &xin, &yin, theta );
         xin += holex;
         RotatePoint( &xin, &yin, -angle );
-        glVertex3f( startx + xin, starty + yin, zpos );
-        glVertex3f( startx + x, starty + y, zpos );
+        p_data.push_back( CPolyPt( (int) ( (startx + x + offX) / g_Parm_3D_Visu.m_BoardScale ),
+                                   -(int) ( (starty + y -
+                                             offY) / g_Parm_3D_Visu.m_BoardScale ) ) );
     }
 
-    glVertex3f( firstxin, firstyin, zpos );
-    glVertex3f( firstx, firsty, zpos );
-    glEnd();
+    for( int ii = 0; ii < ( (int) p_data.size() - spii ) / 2; ii++ )
+    {
+        CPolyPt h = p_data[spii + ii];
+        p_data[spii + ii] = p_data[p_data.size() - ii - 1];
+        p_data[p_data.size() - ii - 1] = h;
+    }
 }
 
 
 static void Draw3D_ArcSegment( double startx, double starty, double centrex,
                                double centrey, double arc_angle, double width, double zpos )
 {
-    int    ii;
-    int    slice = 36;             // Number of segments to approximate a circle by segments
-    double hole, rayon;
+    int slice = C_SLICE * 2;
+    double hole, radius;
     double arcStart_Angle;
 
-    arcStart_Angle = (atan2( startx - centrex, starty - centrey ) * 1800 / M_PI );
-    rayon = hypot( startx - centrex, starty - centrey ) + ( width / 2);
-    hole  = rayon - width;
+    arcStart_Angle = ( ATAN2( startx - centrex, starty - centrey ) );
+    radius  = hypot( startx - centrex, starty - centrey ) + ( width / 2);
+    hole    = radius - width;
 
     // Calculate the number of segments to approximate this arc
     int imax = (int) ( (double) arc_angle * slice / 3600.0 );
@@ -1322,16 +5599,16 @@
 
     glBegin( GL_QUAD_STRIP );
 
-    for( ii = 0; ii <= imax; ii++ )
+    for( int ii = 0; ii <= imax; ii++ )
     {
         double angle = (double) ii * delta_angle;
         angle += arcStart_Angle + 900;
-        double dx = hole;
-        double dy = 0.0;
+        double dx   = hole;
+        double dy   = 0.0;
         RotatePoint( &dx, &dy, (int) angle );
         glVertex3f( dx + startx, dy + starty, zpos );
-        dx = rayon;
-        dy = 0.0;
+        dx  = radius;
+        dy  = 0.0;
         RotatePoint( &dx, &dy, (int) angle );
         glVertex3f( dx + startx, dy + starty, zpos );
     }
@@ -1340,24 +5617,126 @@
 }
 
 
+static void Draw3D_ArcSegmentMask( double startx, double starty, double centrex,
+                                   double centrey, double arc_angle, double width, double zpos )
+{
+    int slice = C_SLICE * 2;
+    double radius;
+    double arcStart_Angle;
+    double lx, ly;
+
+    arcStart_Angle = ( ATAN2( startx - centrex, starty - centrey ) );
+    radius = hypot( startx - centrex, starty - centrey );
+
+    // Calculate the number of segments to approximate this arc
+    int imax = (int) ( (double) arc_angle * slice / 3600.0 );
+
+    if( imax < 0 )
+        imax = -imax;
+
+    if( imax == 0 )
+        imax = 1;
+
+    // Adjust delta_angle to have exactly imax segments in arc_angle
+    // i.e. arc_angle = imax delta_agnle.
+    double delta_angle = (double) arc_angle / imax;
+
+    for( int ii = 0; ii <= imax; ii++ )
+    {
+        double angle = (double) ii * delta_angle;
+        angle += arcStart_Angle + 900;
+        double dx   = radius;
+        double dy   = 0.0;
+        dy = 0.0;
+        RotatePoint( &dx, &dy, angle );
+        dx  += startx;
+        dy  += starty;
+        dx  /= g_Parm_3D_Visu.m_BoardScale;
+        dy  /= g_Parm_3D_Visu.m_BoardScale;
+
+        if( ii > 0 )
+        {
+            e_data.push_back( CPolyPt( (int) lx, (int) (-ly) ) );
+            e_data.push_back( CPolyPt( (int) dx, (int) (-dy) ) );
+        }
+
+        lx  = dx;
+        ly  = dy;
+    }
+}
+
+
+static void Draw3D_ArcSegmentMask( double                   startx,
+                                   double                   starty,
+                                   double                   centrex,
+                                   double                   centrey,
+                                   double                   arc_angle,
+                                   double                   width,
+                                   double                   zpos,
+                                   std::vector<CPolyPt>*    e_data )
+{
+    int slice = C_SLICE * 2;
+    double radius;
+    double arcStart_Angle;
+    double lx = 0, ly = 0;
+
+    arcStart_Angle = ( ATAN2( startx - centrex, starty - centrey ) );
+    radius = hypot( startx - centrex, starty - centrey );
+
+    // Calculate the number of segments to approximate this arc
+    int imax = (int) ( (double) arc_angle * slice / 3600.0 );
+
+    if( imax < 0 )
+        imax = -imax;
+
+    if( imax == 0 )
+        imax = 1;
+
+    // Adjust delta_angle to have exactly imax segments in arc_angle
+    // i.e. arc_angle = imax delta_agnle.
+    double delta_angle = (double) arc_angle / imax;
+
+    for( int ii = 0; ii <= imax; ii++ )
+    {
+        double angle = (double) ii * delta_angle;
+        angle += arcStart_Angle + 900;
+        double dx   = radius;
+        double dy   = 0.0;
+        dy = 0.0;
+        RotatePoint( &dx, &dy, angle );
+        dx  += startx;
+        dy  += starty;
+
+        if( ii > 0 )
+        {
+            Draw3D_FilledSegmentMask( lx, ly, dx, dy, width, zpos, e_data, 1 );
+            e_data->push_back( CPolyPt( MAGIC_CONT, 0 ) );
+        }
+
+        lx  = dx;
+        ly  = dy;
+    }
+}
+
+
 static void Draw3D_CircleSegment( double startx, double starty, double endx,
                                   double endy, double width, double zpos )
 {
-    int    ii, slice = 36;
-    double x, y, hole, rayon;
+    int slice = C_SLICE * 2;
+    double x, y, hole, radius;
 
-    rayon = hypot( startx - endx, starty - endy ) + ( width / 2);
-    hole  = rayon - width;
+    radius  = hypot( startx - endx, starty - endy ) + ( width / 2);
+    hole    = radius - width;
 
     glBegin( GL_QUAD_STRIP );
 
-    for( ii = 0; ii <= slice; ii++ )
+    for( int ii = 0; ii <= slice; ii++ )
     {
         x = hole; y = 0.0;
-        RotatePoint( &x, &y, ii * 3600 / slice );
+        RotatePoint( &x, &y, ii * _SEG( slice ) );
         glVertex3f( x + startx, y + starty, zpos );
-        x = rayon; y = 0.0;
-        RotatePoint( &x, &y, ii * 3600 / slice );
+        x = radius; y = 0.0;
+        RotatePoint( &x, &y, ii * _SEG( slice ) );
         glVertex3f( x + startx, y + starty, zpos );
     }
 
@@ -1365,29 +5744,99 @@
 }
 
 
+static void Draw3D_CircleSegmentMask( double startx, double starty, double endx,
+                                      double endy, double width, double zpos )
+{
+    int ii, slice = C_SLICE * 2;
+    double lx, ly, x, y, radius;
+
+    int seii = e_data.size();
+
+    radius = hypot( startx - endx, starty - endy );
+
+    for( ii = 0; ii <= slice; ii++ )
+    {
+        x = radius; y = 0.0;
+        RotatePoint( &x, &y, ii * _SEG( slice ) );
+        x   += startx;
+        y   += starty;
+        x   /= g_Parm_3D_Visu.m_BoardScale;
+        y   /= g_Parm_3D_Visu.m_BoardScale;
+
+        if( ii > 0 )
+        {
+            e_data.push_back( CPolyPt( (int) lx, (int) ly ) );
+            e_data.push_back( CPolyPt( (int) x, (int) y ) );
+        }
+
+        lx  = x;
+        ly  = y;
+    }
+
+    for( ii = 0; ii < ( (int) e_data.size() - seii ) / 2; ii++ )
+    {
+        CPolyPt h = e_data[seii + ii];
+        e_data[seii + ii] = e_data[e_data.size() - ii - 1];
+        e_data[e_data.size() - ii - 1] = h;
+    }
+}
+
+
+static void Draw3D_CircleSegmentMask( double                startx,
+                                      double                starty,
+                                      double                endx,
+                                      double                endy,
+                                      double                width,
+                                      double                zpos,
+                                      std::vector<CPolyPt>* e_data )
+{
+    int ii, slice = C_SLICE * 2;
+    double lx = 0, ly = 0, x, y, radius;
+
+    radius = hypot( startx - endx, starty - endy );
+
+    for( ii = 0; ii <= slice; ii++ )
+    {
+        x = radius; y = 0.0;
+        RotatePoint( &x, &y, ii * _SEG( slice ) );
+        x   += startx;
+        y   += starty;
+
+        if( ii > 0 )
+        {
+            Draw3D_FilledSegmentMask( lx, ly, x, y, width, zpos, e_data, 1 );
+            e_data->push_back( CPolyPt( MAGIC_CONT, 0 ) );
+        }
+
+        lx  = x;
+        ly  = y;
+    }
+
+#warning "TODO REVERSE LIST!"
+}
+
+
 void EDA_3D_CANVAS::Draw3D_Polygon( std::vector<wxPoint>& aCornersList, double aZpos )
 {
     g_Parm_3D_Visu.m_ActZpos = aZpos;
 
     GLUtesselator* tess = gluNewTess();
-    gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*)() )tessBeginCB );
-    gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*)() )tessEndCB );
-    gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*)() )tessErrorCB );
-    gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*)() )tesswxPoint2Vertex );
+    gluTessCallback( tess, GLU_TESS_BEGIN, ( void (CALLBACK*) () )glBegin );
+    gluTessCallback( tess, GLU_TESS_END, ( void (CALLBACK*) () )glEnd );
+    gluTessCallback( tess, GLU_TESS_ERROR, ( void (CALLBACK*) () )tessErrorCB );
+    gluTessCallback( tess, GLU_TESS_VERTEX, ( void (CALLBACK*) () )tesswxPoint2Vertex );
 
     GLdouble v_data[3];
     v_data[2] = aZpos;
 
-    //gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
-
     // Draw solid polygon
     gluTessBeginPolygon( tess, NULL );
     gluTessBeginContour( tess );
 
     for( unsigned ii = 0; ii < aCornersList.size(); ii++ )
     {
-        v_data[0] = aCornersList[ii].x * g_Parm_3D_Visu.m_BoardScale;
-        v_data[1] = -aCornersList[ii].y * g_Parm_3D_Visu.m_BoardScale;
+        v_data[0]   = aCornersList[ii].x * g_Parm_3D_Visu.m_BoardScale;
+        v_data[1]   = -aCornersList[ii].y * g_Parm_3D_Visu.m_BoardScale;
         // gluTessVertex store pointers on data, not data, so do not store
         // different corners values in a temporary variable
         // but send pointer on each corner value in aCornersList
@@ -1404,28 +5853,41 @@
 static int Get3DLayerEnable( int act_layer )
 {
     int i = -1;
+
     // see if layer needs to be shown
     // check the flags
-    switch (act_layer)
+    switch( act_layer )
     {
-        case DRAW_N:
-            i=g_Parm_3D_Visu.FL_DRAWINGS;
-            break;
-
-        case COMMENT_N:
-            i=g_Parm_3D_Visu.FL_COMMENTS;
-            break;
-
-        case ECO1_N:
-            i=g_Parm_3D_Visu.FL_ECO1;
-            break;
-
-        case ECO2_N:
-            i=g_Parm_3D_Visu.FL_ECO2;
-            break;
+    case ADHESIVE_N_FRONT:
+    case ADHESIVE_N_BACK:
+    case SOLDERMASK_N_FRONT:
+    case SOLDERMASK_N_BACK:
+    case SOLDERPASTE_N_FRONT:
+    case SOLDERPASTE_N_BACK:
+    case SILKSCREEN_N_FRONT:
+    case SILKSCREEN_N_BACK:
+        return g_Parm_3D_Visu.m_BoardSettings->IsLayerVisible( act_layer );
+        break;
+
+    case DRAW_N:
+        i = g_Parm_3D_Visu.FL_DRAWINGS;
+        break;
+
+    case COMMENT_N:
+        i = g_Parm_3D_Visu.FL_COMMENTS;
+        break;
+
+    case ECO1_N:
+        i = g_Parm_3D_Visu.FL_ECO1;
+        break;
+
+    case ECO2_N:
+        i = g_Parm_3D_Visu.FL_ECO2;
+        break;
     }
+
     // the layer was not a layer with a flag, so show it
-    if (i < 0)
+    if( i < 0 )
         return true;
 
     // if the layer has a flag, return the flag
@@ -1440,30 +5902,19 @@
     nZ = 1.0;
 
     if( ( act_layer <= LAST_COPPER_LAYER - 1 )
-       || ( act_layer == ADHESIVE_N_BACK )
-       || ( act_layer == SOLDERPASTE_N_BACK )
-       || ( act_layer == SILKSCREEN_N_BACK )
-       || ( act_layer == SOLDERMASK_N_BACK ) )
+        || ( act_layer == ADHESIVE_N_BACK )
+        || ( act_layer == SOLDERPASTE_N_BACK )
+        || ( act_layer == SILKSCREEN_N_BACK )
+        || ( act_layer == SOLDERMASK_N_BACK ) )
         nZ = -1.0;
+
     return nZ;
 }
 
 
-///////////////////////////////////////////////////////////////////////////////
+// /////////////////////////////////////////////////////////////////////////////
 // GLU_TESS CALLBACKS
-///////////////////////////////////////////////////////////////////////////////
-
-void CALLBACK tessBeginCB( GLenum which )
-{
-    glBegin( which );
-}
-
-
-void CALLBACK tessEndCB()
-{
-    glEnd();
-}
-
+// /////////////////////////////////////////////////////////////////////////////
 
 void CALLBACK tessCPolyPt2Vertex( const GLvoid* data )
 {
@@ -1475,6 +5926,7 @@
                 g_Parm_3D_Visu.m_ActZpos );
 }
 
+
 void CALLBACK tesswxPoint2Vertex( const GLvoid* data )
 {
     const wxPoint* ptr = (const wxPoint*) data;
@@ -1496,3 +5948,160 @@
     D( printf( "Tess ERROR: %s\n", errorStr ); )
 #endif
 }
+
+
+// /////////////////////////////////////////////////////////////////////////////
+// MY GLU_TESS CALLBACKS
+// /////////////////////////////////////////////////////////////////////////////
+
+void CALLBACK mytessCPolyPt2Vertex( const GLvoid* data )
+{
+    // cast back to double type
+    const CPolyPt* ptr = (const CPolyPt*) data;
+
+    glVertex3f( ptr->x * g_Parm_3D_Visu.m_BoardScale,
+                -ptr->y * g_Parm_3D_Visu.m_BoardScale,
+                g_Parm_3D_Visu.m_ActZpos );
+}
+
+
+void CALLBACK mytessCombineCB( GLdouble coords[3],
+                               void*    vertex_data[4],
+                               GLfloat  weight[4],
+                               void**   outData )
+{
+    CPolyPt* n =
+        new CPolyPt( (int) (coords[0] / g_Parm_3D_Visu.m_BoardScale),
+                     -(int) (coords[1] / g_Parm_3D_Visu.m_BoardScale) );
+
+    *outData = n;
+}
+
+
+void CALLBACK mytessErrorCB( GLenum errorCode )
+{
+#if defined(DEBUG)
+    const GLubyte* errorStr;
+
+    errorStr = gluErrorString( errorCode );
+
+    // DEBUG //
+    fprintf( stderr, "Tess ERROR: %s\n", errorStr );
+#endif
+}
+
+
+// /////////////////////////////////////////////////////////////////////////////
+// MY2 GLU_TESS CALLBACKS
+// /////////////////////////////////////////////////////////////////////////////
+
+void CALLBACK my2tessBeginCB( GLenum which )
+{
+    gluTessBeginContour( tess );
+}
+
+
+void CALLBACK my2tessEndCB()
+{
+    gluTessEndContour( tess );
+    xe_data.push_back( CPolyPt( (int) MAGIC_CONT, 0, true, 0 ) );
+}
+
+
+void CALLBACK my2tessCPolyPt2Vertex( const GLvoid* data )
+{
+    // cast back to double type
+    CPolyPt* ptr = (CPolyPt*) data;
+    GLdouble v_data[3];
+
+    v_data[0]   = ptr->x * g_Parm_3D_Visu.m_BoardScale;
+    v_data[1]   = -ptr->y * g_Parm_3D_Visu.m_BoardScale;
+    v_data[2]   = g_Parm_3D_Visu.m_ActZpos;
+    ptr->utility++;
+    xe_data.push_back( CPolyPt( ptr->x, ptr->y, false, 1 ) );
+    gluTessVertex( tess, v_data, ptr );
+}
+
+
+void CALLBACK my2tessCombineCB( GLdouble    coords[3],
+                                void*       vertex_data[4],
+                                GLfloat     weight[4],
+                                void**      outData )
+{
+    CPolyPt* n =
+        new CPolyPt( (int) (coords[0] / g_Parm_3D_Visu.m_BoardScale),
+                     -(int) (coords[1] / g_Parm_3D_Visu.m_BoardScale) );
+
+    *outData = n;
+}
+
+
+void CALLBACK my2tessErrorCB( GLenum errorCode )
+{
+#if defined(DEBUG)
+    const GLubyte* errorStr;
+
+    errorStr = gluErrorString( errorCode );
+
+    // DEBUG //
+    fprintf( stderr, "Tess 2 ERROR: %s\n", errorStr );
+#endif
+}
+
+
+// /////////////////////////////////////////////////////////////////////////////
+// MY3 GLU_TESS CALLBACKS
+// /////////////////////////////////////////////////////////////////////////////
+
+void CALLBACK my3tessBeginCB( GLenum which )
+{
+    gluTessBeginContour( tess );
+}
+
+
+void CALLBACK my3tessEndCB()
+{
+    gluTessEndContour( tess );
+    se_data.push_back( CPolyPt( (int) MAGIC_CONT, 0, true, 0 ) );
+}
+
+
+void CALLBACK my3tessCPolyPt2Vertex( const GLvoid* data )
+{
+    // cast back to double type
+    CPolyPt* ptr = (CPolyPt*) data;
+    GLdouble v_data[3];
+
+    v_data[0]   = ptr->x * g_Parm_3D_Visu.m_BoardScale;
+    v_data[1]   = -ptr->y * g_Parm_3D_Visu.m_BoardScale;
+    v_data[2]   = g_Parm_3D_Visu.m_ActZpos;
+    ptr->utility++;
+    se_data.push_back( CPolyPt( ptr->x, ptr->y, false, 1 ) );
+    gluTessVertex( tess, v_data, ptr );
+}
+
+
+void CALLBACK my3tessCombineCB( GLdouble    coords[3],
+                                void*       vertex_data[4],
+                                GLfloat     weight[4],
+                                void**      outData )
+{
+    CPolyPt* n =
+        new CPolyPt( (int) (coords[0] / g_Parm_3D_Visu.m_BoardScale),
+                     -(int) (coords[1] / g_Parm_3D_Visu.m_BoardScale) );
+
+    *outData = n;
+}
+
+
+void CALLBACK my3tessErrorCB( GLenum errorCode )
+{
+#if defined(DEBUG)
+    const GLubyte* errorStr;
+
+    errorStr = gluErrorString( errorCode );
+
+    // DEBUG //
+    fprintf( stderr, "Tess 3 ERROR: %s\n", errorStr );
+#endif
+}

=== modified file '3d-viewer/3d_frame.cpp'
--- 3d-viewer/3d_frame.cpp	2012-04-19 06:55:45 +0000
+++ 3d-viewer/3d_frame.cpp	2012-06-15 14:50:19 +0000
@@ -321,6 +321,21 @@
         NewDisplay();
         return;
 
+    case ID_MENU3D_RENDER_ONOFF:
+        g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] = isChecked;
+        NewDisplay();
+        return;
+
+    case ID_MENU3D_GRID_ONOFF:
+        g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_GRID] = isChecked;
+        NewDisplay();
+        return;
+
+    case ID_MENU3D_VIRT_ONOFF:
+        g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_VIRT] = isChecked;
+        NewDisplay();
+        return;
+
     default:
         wxMessageBox( wxT( "EDA_3D_FRAME::Process_Special_Functions() error: unknown command" ) );
         return;

=== modified file '3d-viewer/3d_read_mesh.cpp'
--- 3d-viewer/3d_read_mesh.cpp	2012-01-23 04:33:36 +0000
+++ 3d-viewer/3d_read_mesh.cpp	2012-06-15 16:21:09 +0000
@@ -35,14 +35,14 @@
 
 #include <3d_viewer.h>
 
-
 int S3D_MASTER::ReadData()
 {
-    char       line[1024], * text;
-    wxFileName fn;
-    wxString   FullFilename;
-    FILE*      file;
-    int        LineNum = 0;
+    char        line[1024], * text;
+    wxFileName  fn;
+
+    wxString    FullFilename;
+    FILE*       file;
+    int         LineNum = 0;
 
     if( m_Shape3DName.IsEmpty() )
     {
@@ -51,9 +51,9 @@
 
     wxString shape3DNname = m_Shape3DName;
 #ifdef __WINDOWS__
-    shape3DNname.Replace( wxT("/"), wxT("\\") );
+    shape3DNname.Replace( wxT( "/" ), wxT( "\\" ) );
 #else
-    shape3DNname.Replace( wxT("\\"), wxT("/") );
+    shape3DNname.Replace( wxT( "\\" ), wxT( "/" ) );
 #endif
 
     if( wxFileName::FileExists( shape3DNname ) )
@@ -112,16 +112,130 @@
     return 0;
 }
 
+int S3D_MASTER::ReadData(const wxString* ref)
+{
+    char        line[1024], * text;
+    wxFileName  fn;
+
+#ifndef TSP_20120603
+    wxString    FullFilename;
+#endif    // TSP_20120603
+    FILE*       file;
+    int         LineNum = 0;
+
+    if( m_Shape3DName.IsEmpty() )
+    {
+        return 1;
+    }
+
+    wxString shape3DNname = m_Shape3DName;
+#ifdef __WINDOWS__
+    shape3DNname.Replace( wxT( "/" ), wxT( "\\" ) );
+#else
+    shape3DNname.Replace( wxT( "\\" ), wxT( "/" ) );
+#endif
+
+    wxString alt_shape3DNname = (shape3DNname.Length() > 4)?shape3DNname.SubString(0, shape3DNname.Length()-5):shape3DNname;
+    alt_shape3DNname.Append( wxT( "_" ) );
+    alt_shape3DNname.Append( ref->GetChar(0) );
+    alt_shape3DNname.Append( wxT( ".wrl" ) );
+
+    if( wxFileName::FileExists( alt_shape3DNname ) )
+    {
+        FullFilename = alt_shape3DNname;
+    }
+    else
+    {
+        fn = alt_shape3DNname;
+        FullFilename = wxGetApp().FindLibraryPath( fn );
+
+        if( FullFilename.IsEmpty() )
+        {
+            if( wxFileName::FileExists( shape3DNname ) )
+            {
+                FullFilename = shape3DNname;
+            }
+            else
+            {
+                fn = shape3DNname;
+                FullFilename = wxGetApp().FindLibraryPath( fn );
+
+                if( FullFilename.IsEmpty() )
+                {
+                    wxLogDebug( wxT( "3D part library <%s> could not be found." ),
+                                GetChars( fn.GetFullPath() ) );
+                    return -1;
+                }
+            }
+        }
+    }
+
+    file = wxFopen( FullFilename, wxT( "rt" ) );
+
+    if( file == NULL )
+    {
+        return -1;
+    }
+
+    // Switch the locale to standard C (needed to print floating point numbers like 1.3)
+    SetLocaleTo_C_standard();
+
+    while( GetLine( file, line, &LineNum, 512 ) )
+    {
+        text = strtok( line, " \t\n\r" );
+
+        if( stricmp( text, "DEF" ) == 0 )
+        {
+            while( GetLine( file, line, &LineNum, 512 ) )
+            {
+                text = strtok( line, " \t\n\r" );
+
+                if( text == NULL )
+                    continue;
+
+                if( *text == '}' )
+                    break;
+
+                if( stricmp( text, "children" ) == 0 )
+                {
+                    ReadChildren( file, &LineNum );
+                }
+            }
+        }
+    }
+
+    std::vector<S3D_Vertex> m_MinMax;
+    m_MinMax.clear();
+    m_MinMax.push_back( m_MinPos );
+    m_MinMax.push_back( m_MaxPos );
+    Set_Object_Coords( m_MinMax );
+    if ( m_MinMax[0].z <= m_MinMax[1].z )
+    {
+        m_MinPos = m_MinMax[0];
+        m_MaxPos = m_MinMax[1];
+    }
+    else
+    {
+        m_MinPos = m_MinMax[1];
+        m_MaxPos = m_MinMax[0];
+    }
+
+
+    fclose( file );
+    SetLocaleTo_Default();       // revert to the current locale
+    return 0;
+}
+
 
 int S3D_MASTER::ReadMaterial( FILE* file, int* LineNum )
 {
-    char          line[512], * text, * command;
-    wxString      mat_name;
-    S3D_MATERIAL* material = NULL;
+    char            line[512], * text, * command;
+    wxString        mat_name;
+    S3D_MATERIAL*   material = NULL;
 
-    command  = strtok( NULL, " \t\n\r" );
-    text     = strtok( NULL, " \t\n\r" );
-    mat_name = FROM_UTF8( text );
+    command     = strtok( NULL, " \t\n\r" );
+    text        = strtok( NULL, " \t\n\r" );
+    mat_name    = FROM_UTF8( text );
 
     if( stricmp( command, "USE" ) == 0 )
     {
@@ -237,8 +351,8 @@
 
 int S3D_MASTER::ReadShape( FILE* file, int* LineNum )
 {
-    char line[1024], * text;
-    int  err = 1;
+    char    line[1024], * text;
+    int     err = 1;
 
     while( GetLine( file, line, LineNum, 512 ) )
     {
@@ -260,7 +374,7 @@
         }
         else
         {
-            printf( "ReadShape error line %d <%s> \n", *LineNum, text );
+            printf( "ReadShape error line %d <%s>\n", *LineNum, text );
             break;
         }
     }
@@ -271,8 +385,8 @@
 
 int S3D_MASTER::ReadAppearance( FILE* file, int* LineNum )
 {
-    char line[1024], * text;
-    int  err = 1;
+    char    line[1024], * text;
+    int     err = 1;
 
     while( GetLine( file, line, LineNum, 512 ) )
     {
@@ -317,20 +431,20 @@
  *  text_buffer contains the first line of this node :
  *     "coord Coordinate { point ["
  */
-void ReadCoordsList( FILE* file, char* text_buffer, std::vector< double >& aList, int* LineNum )
+void ReadCoordsList( FILE* file, char* text_buffer, std::vector<double>& aList, int* LineNum )
 {
-    unsigned int ii = 0, jj = 0;
-    char*        text;
-    bool         HasData   = false;
-    bool         StartData = false;
-    bool         EndNode   = false;
-    char         string_num[512];
+    unsigned int    ii = 0, jj = 0;
+    char*           text;
+    bool            HasData     = false;
+    bool            StartData   = false;
+    bool            EndNode     = false;
+    char            string_num[512];
 
     text = text_buffer;
 
     while( !EndNode )
     {
-        if( *text == 0 )  // Needs data !
+        if( *text == 0 )    // Needs data !
         {
             text = text_buffer;
             GetLine( file, text_buffer, LineNum, 512 );
@@ -373,6 +487,7 @@
                 break;
 
             default:
+
                 if( !StartData )
                     break;
 
@@ -395,9 +510,9 @@
 int S3D_MASTER::ReadGeometry( FILE* file, int* LineNum )
 {
     char    line[1024], buffer[1024], * text;
-    int     err    = 1;
-    std::vector< double > points;
-    std::vector< double > list;
+    int     err = 1;
+    std::vector<double> points;
+    std::vector<double> list;
 
     while( GetLine( file, line, LineNum, 512 ) )
     {
@@ -420,6 +535,7 @@
             else
             {
             }
+
             continue;
         }
 
@@ -433,6 +549,7 @@
             else
             {
             }
+
             continue;
         }
 
@@ -506,8 +623,8 @@
                 break;
             }
 
-            std::vector< int > coordIndex;
-            std::vector< S3D_Vertex > vertices;
+            std::vector<int>        coordIndex;
+            std::vector<S3D_Vertex> vertices;
 
             while( GetLine( file, line, LineNum, 512 ) )
             {
@@ -527,7 +644,7 @@
                         {
                             int kk = coordIndex[jj] * 3;
 
-                            if( (kk < 0) || ((kk + 3) > (int)points.size()) )
+                            if( (kk < 0) || ( (kk + 3) > (int) points.size() ) )
                             {
                                 wxLogError( wxT( "3D geometry index read error <%s> at line %d." ),
                                             GetChars( FROM_UTF8( text ) ), *LineNum );
@@ -536,12 +653,30 @@
                             }
 
                             S3D_Vertex vertex;
-                            vertex.x = points[kk];
-                            vertex.y = points[kk + 1];
-                            vertex.z = points[kk + 2];
+                            vertex.x    = points[kk];
+                            vertex.y    = points[kk + 1];
+                            vertex.z    = points[kk + 2];
                             vertices.push_back( vertex );
+
+                            if( vertex.x < m_MinPos.x )
+                                m_MinPos.x = vertex.x;
+
+                            if( vertex.y < m_MinPos.y )
+                                m_MinPos.y = vertex.y;
+
+                            if( vertex.z < m_MinPos.z )
+                                m_MinPos.z = vertex.z;
+
+                            if( vertex.x > m_MaxPos.x )
+                                m_MaxPos.x = vertex.x;
+
+                            if( vertex.y > m_MaxPos.y )
+                                m_MaxPos.y = vertex.y;
+
+                            if( vertex.z > m_MaxPos.z )
+                                m_MaxPos.z = vertex.z;
                         }
-
+                        
                         Set_Object_Coords( vertices );
                         Set_Object_Data( vertices );
                         vertices.clear();

=== modified file '3d-viewer/3d_struct.h'
--- 3d-viewer/3d_struct.h	2012-05-11 09:02:35 +0000
+++ 3d-viewer/3d_struct.h	2012-06-14 15:40:05 +0000
@@ -97,6 +97,8 @@
     S3D_Vertex      m_MatPosition;
     Struct3D_Shape* m_3D_Drawings;
     S3D_MATERIAL*   m_Materials;
+    S3D_Vertex      m_MinPos;
+    S3D_Vertex      m_MaxPos;
 
 public:
     S3D_MASTER( EDA_ITEM* aParent );
@@ -114,6 +116,7 @@
 
     void Copy( S3D_MASTER* pattern );
     int  ReadData();
+    int  ReadData(const wxString* ref);
 
     /**
      * Function ReadMaterial

=== modified file '3d-viewer/3d_toolbar.cpp'
--- 3d-viewer/3d_toolbar.cpp	2012-04-09 09:16:47 +0000
+++ 3d-viewer/3d_toolbar.cpp	2012-06-15 14:42:02 +0000
@@ -133,13 +133,14 @@
     bool full_options = true;
 
     // If called from the display frame of CvPcb, only some options are relevant
-    if( Parent()->GetName() == wxT( "CmpFrame" ) ) {
+    if( Parent()->GetName() == wxT( "CmpFrame" ) )
+    {
         full_options = false;
     }
 
-    wxMenuBar* menuBar   = new wxMenuBar;
-    wxMenu*    fileMenu  = new wxMenu;
-    wxMenu*    prefsMenu = new wxMenu;
+    wxMenuBar*  menuBar     = new wxMenuBar;
+    wxMenu*     fileMenu    = new wxMenu;
+    wxMenu*     prefsMenu   = new wxMenu;
 
     menuBar->Append( fileMenu, _( "&File" ) );
 
@@ -161,36 +162,49 @@
 
     wxMenuItem* item;
     item = AddMenuItem( prefsMenu, ID_MENU3D_AXIS_ONOFF,
-            _( "Show 3D &Axis" ), KiBitmap( axis3d_front_xpm ), wxITEM_CHECK );
-    item->Check(g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_AXIS]);
+                        _( "Show 3D &Axis" ), KiBitmap( axis3d_front_xpm ), wxITEM_CHECK );
+    item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_AXIS] );
 
 
     if( full_options )
     {
-       item = AddMenuItem( prefsMenu, ID_MENU3D_MODULE_ONOFF,
-               _( "Show 3D F&ootprints" ), KiBitmap( shape_3d_xpm ), wxITEM_CHECK );
-       item->Check(g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_MODULE]);
-
-       item = AddMenuItem( prefsMenu, ID_MENU3D_ZONE_ONOFF,
-               _( "Show Zone &Filling" ), KiBitmap( add_zone_xpm ), wxITEM_CHECK );
-       item->Check(g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ZONE]);
-
-       item = AddMenuItem( prefsMenu, ID_MENU3D_COMMENTS_ONOFF,
-               _( "Show &Comments Layer" ), KiBitmap( edit_sheet_xpm ), wxITEM_CHECK );
-       item->Check(g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_COMMENTS]);
-
-       item = AddMenuItem( prefsMenu, ID_MENU3D_DRAWINGS_ONOFF,
-               _( "Show &Drawings Layer" ), KiBitmap( add_polygon_xpm ), wxITEM_CHECK );
-       item->Check(g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_DRAWINGS]);
-
-       item = AddMenuItem( prefsMenu, ID_MENU3D_ECO1_ONOFF,
-               _( "Show Eco&1 Layer" ), KiBitmap( tools_xpm ), wxITEM_CHECK );
-       item->Check(g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ECO1]);
-
-       item = AddMenuItem( prefsMenu, ID_MENU3D_ECO2_ONOFF,
-               _( "Show Eco&2 Layer" ), KiBitmap( tools_xpm ), wxITEM_CHECK );
-       item->Check(g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ECO2]);
-
+        item = AddMenuItem( prefsMenu, ID_MENU3D_MODULE_ONOFF,
+                            _( "Show 3D F&ootprints" ), KiBitmap( shape_3d_xpm ), wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_MODULE] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_ZONE_ONOFF,
+                            _( "Show Zone &Filling" ), KiBitmap( add_zone_xpm ), wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ZONE] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_COMMENTS_ONOFF,
+                            _( "Show &Comments Layer" ), KiBitmap( edit_sheet_xpm ),
+                            wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_COMMENTS] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_DRAWINGS_ONOFF,
+                            _( "Show &Drawings Layer" ), KiBitmap( add_polygon_xpm ),
+                            wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_DRAWINGS] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_ECO1_ONOFF,
+                            _( "Show Eco&1 Layer" ), KiBitmap( tools_xpm ), wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ECO1] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_ECO2_ONOFF,
+                            _( "Show Eco&2 Layer" ), KiBitmap( tools_xpm ), wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_ECO2] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_RENDER_ONOFF,
+                            _( "Show &Render Mode" ), KiBitmap( tools_xpm ), wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_RENDER] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_GRID_ONOFF,
+                            _( "Show &Grid" ), KiBitmap( tools_xpm ), wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_GRID] );
+
+        item = AddMenuItem( prefsMenu, ID_MENU3D_VIRT_ONOFF,
+                            _( "Show &Virtual Modules" ), KiBitmap( tools_xpm ), wxITEM_CHECK );
+        item->Check( g_Parm_3D_Visu.m_DrawFlags[g_Parm_3D_Visu.FL_VIRT] );
     }
 
     SetMenuBar( menuBar );

=== modified file '3d-viewer/3d_viewer.h'
--- 3d-viewer/3d_viewer.h	2012-04-09 09:16:47 +0000
+++ 3d-viewer/3d_viewer.h	2012-06-15 14:43:04 +0000
@@ -51,6 +51,7 @@
 
 #include <3d_struct.h>
 #include <id.h>
+#include <PolyLine.h>
 
 
 class BOARD_DESIGN_SETTINGS;
@@ -97,6 +98,9 @@
     ID_MENU3D_COMMENTS_ONOFF,
     ID_MENU3D_ECO1_ONOFF,
     ID_MENU3D_ECO2_ONOFF,
+    ID_MENU3D_RENDER_ONOFF,
+    ID_MENU3D_GRID_ONOFF,
+    ID_MENU3D_VIRT_ONOFF,
     ID_END_COMMAND_3D,
 
     ID_MENU_SCREENCOPY_PNG,
@@ -127,40 +131,39 @@
 class SEGVIA;
 
 
-#define m_ROTX m_Rot[0]
-#define m_ROTY m_Rot[1]
-#define m_ROTZ m_Rot[2]
+#define m_ROTX  m_Rot[0]
+#define m_ROTY  m_Rot[1]
+#define m_ROTZ  m_Rot[2]
 
 /* information needed to display 3D board */
 class Info_3D_Visu
 {
-
 public:
     enum {
-        FL_AXIS=0,        FL_MODULE,        FL_ZONE,
-        FL_COMMENTS,      FL_DRAWINGS,      FL_ECO1,          FL_ECO2,
+        FL_AXIS=0, FL_MODULE, FL_ZONE,
+        FL_COMMENTS, FL_DRAWINGS, FL_ECO1, FL_ECO2,
+        FL_RENDER, FL_GRID, FL_VIRT,
         FL_LAST
     };
 
-    double    m_Beginx, m_Beginy;   /* position of mouse */
-    double    m_Quat[4];            /* orientation of object */
-    double    m_Rot[4];             /* man rotation of object */
-    double    m_Zoom;               /* field of view in degrees */
-    S3D_Color m_BgColor;
-    bool      m_DrawFlags[FL_LAST]; /* show these special items */
-    wxPoint   m_BoardPos;
-    wxSize    m_BoardSize;
-    int       m_Layers;
-
-    const BOARD_DESIGN_SETTINGS*    m_BoardSettings;   // Link to current board design settings
-
-    double    m_Epoxy_Width;    // Epoxy thickness (normalized)
-
-    double    m_BoardScale;     /* Normalization scale for coordinates:
-                                 * when scaled between -1.0 and +1.0 */
-    double    m_LayerZcoord[32];
-    double    m_ActZpos;
-
+    double      m_Beginx, m_Beginy;     /* position of mouse */
+    double      m_Quat[4];              /* orientation of object */
+    double      m_Rot[4];               /* man rotation of object */
+    double      m_Zoom;                 /* field of view in degrees */
+    S3D_Color   m_BgColor;
+    bool        m_DrawFlags[FL_LAST];   /* show these special items */
+    wxPoint     m_BoardPos;
+    wxSize      m_BoardSize;
+    int         m_Layers;
+
+    const BOARD_DESIGN_SETTINGS* m_BoardSettings;   // Link to current board design settings
+
+    double  m_Epoxy_Width;                          // Epoxy thickness (normalized)
+
+    double  m_BoardScale;                           /* Normalization scale for coordinates:
+                                                     * when scaled between -1.0 and +1.0 */
+    double  m_LayerZcoord[32];
+    double  m_ActZpos;
 public: Info_3D_Visu();
     ~Info_3D_Visu();
 };
@@ -171,76 +174,77 @@
 private:
     bool            m_init;
     GLuint          m_gllist;
-    /// Tracks whether to use Orthographic or Perspective projection
-    //  TODO: Does this belong here, or in  EDA_3D_FRAME ???
+    // / Tracks whether to use Orthographic or Perspective projection
+    // TODO: Does this belong here, or in  EDA_3D_FRAME ???
     bool            m_ortho;
     wxGLContext*    m_glRC;
     wxRealPoint     m_draw3dOffset;     // offset to draw the 3 mesh.
     double          m_ZBottom;          // position of the back layer
     double          m_ZTop;             // position of the front layer
-
-
 public:
     EDA_3D_CANVAS( EDA_3D_FRAME* parent, int* attribList = 0 );
     ~EDA_3D_CANVAS();
 
-    EDA_3D_FRAME*   Parent() { return (EDA_3D_FRAME*)GetParent(); }
+    EDA_3D_FRAME* Parent() { return (EDA_3D_FRAME*) GetParent(); }
 
-    void   ClearLists();
+    void    ClearLists();
 
     // Event functions:
-    void   OnPaint( wxPaintEvent& event );
-    void   OnEraseBackground( wxEraseEvent& event );
-    void   OnChar( wxKeyEvent& event );
-    void   OnMouseWheel( wxMouseEvent& event );
-    void   OnMouseMove( wxMouseEvent& event );
-    void   OnRightClick( wxMouseEvent& event );
-    void   OnPopUpMenu( wxCommandEvent& event );
-    void   TakeScreenshot( wxCommandEvent& event );
-    void   OnEnterWindow( wxMouseEvent& event );
+    void    OnPaint( wxPaintEvent& event );
+    void    OnEraseBackground( wxEraseEvent& event );
+    void    OnChar( wxKeyEvent& event );
+    void    OnMouseWheel( wxMouseEvent& event );
+    void    OnMouseMove( wxMouseEvent& event );
+    void    OnRightClick( wxMouseEvent& event );
+    void    OnPopUpMenu( wxCommandEvent& event );
+    void    TakeScreenshot( wxCommandEvent& event );
+    void    OnEnterWindow( wxMouseEvent& event );
 
     // Display functions
-    GLuint DisplayCubeforTest();        // Just a test function
-    void   SetView3D( int keycode );
-    void   DisplayStatus();
-    void   Redraw( bool finish = false );
-    void   Render();
+    GLuint  DisplayCubeforTest();       // Just a test function
+    void    SetView3D( int keycode );
+    void    DisplayStatus();
+    void    Redraw( bool finish = false );
+    void    Render();
 
     /**
      * Function CreateDrawGL_List
      * creates the OpenGL draw list items.
      */
-    GLuint CreateDrawGL_List();
-    void   InitGL();
-    void   SetLights();
-    void   SetOffset(double aPosX, double aPosY)
+    GLuint  CreateDrawGL_List();
+    void    InitGL();
+    void    SetLights();
+
+    void   SetOffset( double aPosX, double aPosY )
     {
-        m_draw3dOffset.x = aPosX;
-        m_draw3dOffset.y = aPosY;
+        m_draw3dOffset.x    = aPosX;
+        m_draw3dOffset.y    = aPosY;
     }
-    void   Draw3D_Track( TRACK* track );
+
+    void    Draw3D_Track( TRACK* track );
 
     /**
      * Function Draw3D_SolidPolygonsInZones
      * draw all solid polygons used as filled areas in a zone
      * @param aZone = the zone to draw
-    */
-    void   Draw3D_SolidPolygonsInZones( ZONE_CONTAINER* aZone );
+     */
+    void    Draw3D_SolidPolygonsInZones( ZONE_CONTAINER* aZone );
 
     /**
      * Function Draw3D_Polygon
      * draw one solid polygon
      * @param aCornersList = a std::vector<wxPoint> list of corners, in physical coordinates
      * @param aZpos = the z position in 3D units
-    */
-    void   Draw3D_Polygon( std::vector<wxPoint>& aCornersList, double aZpos );
+     */
+    void    Draw3D_Polygon( std::vector<wxPoint>& aCornersList, double aZpos );
 
     /**
      * Function Draw3D_Via
      * draws 3D via as a cylinder and filled circles.
      */
-    void   Draw3D_Via( SEGVIA* via );
-    void   Draw3D_DrawSegment( DRAWSEGMENT* segment );
+    void    Draw3D_Via( SEGVIA* via );
+    void    Draw3D_DrawSegment( DRAWSEGMENT* segment );
+    void    Draw3D_DrawSegmentMask( DRAWSEGMENT* segment, int layer, std::vector<CPolyPt> *le_data );
 
     /**
      * Function Draw3D_DrawText
@@ -253,16 +257,24 @@
      * Using DrawGraphicText to draw all texts ensure texts have the same shape
      * in all contexts
      */
-    void   Draw3D_DrawText( TEXTE_PCB* text );
-
-    /// Toggles orthographic projection on and off
-    void ToggleOrtho(){ m_ortho = !m_ortho ; Refresh(true);};
-
-    /// Returns the orthographic projection flag
-    bool ModeIsOrtho() { return m_ortho ;};
-
-
-    //int Get3DLayerEnable(int act_layer);
+    void    Draw3D_DrawText( TEXTE_PCB* text );
+    void    Draw3D_DrawTextMask( TEXTE_PCB* text, int layer, GLUtesselator* tess );
+    void    Draw3D_DrawText( TEXTE_MODULE* text, double rot );
+
+    // / Toggles orthographic projection on and off
+    void ToggleOrtho() { m_ortho = !m_ortho; Refresh( true ); };
+
+    // / Returns the orthographic projection flag
+    bool ModeIsOrtho() { return m_ortho; };
+
+
+    // int Get3DLayerEnable(int act_layer);
+    // GLUtesselator* Get_mftess() { return mftess; };
+    // GLUtesselator* Get_mbtess() { return mbtess; };
+    // int Get_tmonly() { return tmonly; };
+    // void Set_mftess(GLUtesselator* tess) { mftess = tess; };
+    // void Set_mbtess(GLUtesselator* tess) { mbtess = tess; };
+    // void Set_tmonly(int i) { tmonly = i; };
 
     DECLARE_EVENT_TABLE()
 };
@@ -280,7 +292,6 @@
     wxSize          m_FrameSize;
     wxAuiManager    m_auimgr;
     bool            m_reloadRequest;
-
 public:
     EDA_3D_FRAME( PCB_BASE_FRAME* parent, const wxString& title,
                   long style = KICAD_DEFAULT_3D_DRAWFRAME_STYLE );
@@ -289,15 +300,15 @@
         m_auimgr.UnInit();
     };
 
-    PCB_BASE_FRAME* Parent() { return (PCB_BASE_FRAME*)GetParent(); }
-    void Exit3DFrame( wxCommandEvent& event );
-    void OnCloseWindow( wxCloseEvent& Event );
-    void ReCreateMenuBar();
-    void ReCreateHToolbar();
-    void ReCreateVToolbar();
-    void SetToolbars();
-    void GetSettings();
-    void SaveSettings();
+    PCB_BASE_FRAME* Parent() { return (PCB_BASE_FRAME*) GetParent(); }
+    void    Exit3DFrame( wxCommandEvent& event );
+    void    OnCloseWindow( wxCloseEvent& Event );
+    void    ReCreateMenuBar();
+    void    ReCreateHToolbar();
+    void    ReCreateVToolbar();
+    void    SetToolbars();
+    void    GetSettings();
+    void    SaveSettings();
 
     /**
      * Function ReloadRequest
@@ -305,30 +316,30 @@
      * mainly after edition of the board or footprint being displayed.
      * mainly for the module editor.
      */
-    void ReloadRequest( )
+    void ReloadRequest()
     {
         m_reloadRequest = true;
     }
 
-    void OnLeftClick( wxDC* DC, const wxPoint& MousePos );
-    void OnRightClick( const wxPoint& MousePos, wxMenu* PopMenu );
-    void OnKeyEvent( wxKeyEvent& event );
-    double BestZoom();
-    void RedrawActiveWindow( wxDC* DC, bool EraseBg );
-    void Process_Special_Functions( wxCommandEvent& event );
-    void Process_Zoom( wxCommandEvent& event );
-    void OnActivate( wxActivateEvent& event );
+    void    OnLeftClick( wxDC* DC, const wxPoint& MousePos );
+    void    OnRightClick( const wxPoint& MousePos, wxMenu* PopMenu );
+    void    OnKeyEvent( wxKeyEvent& event );
+    double  BestZoom();
+    void    RedrawActiveWindow( wxDC* DC, bool EraseBg );
+    void    Process_Special_Functions( wxCommandEvent& event );
+    void    Process_Zoom( wxCommandEvent& event );
+    void    OnActivate( wxActivateEvent& event );
 
-    void NewDisplay();
-    void Set3DBgColor();
+    void    NewDisplay();
+    void    Set3DBgColor();
 
     DECLARE_EVENT_TABLE()
 };
 
-void SetGLColor( int color );
-void Set_Object_Data( std::vector< S3D_Vertex >& aVertices );
+void        SetGLColor( int color );
+void        Set_Object_Data( std::vector<S3D_Vertex>& aVertices );
 
 extern Info_3D_Visu g_Parm_3D_Visu;
 extern double       DataScale3D; // 3D scale units.
 
-#endif  /*  __3D_VIEWER_H__ */
+#endif /*  __3D_VIEWER_H__ */

=== modified file '3d-viewer/trackball.cpp'
--- 3d-viewer/trackball.cpp	2012-01-23 04:33:36 +0000
+++ 3d-viewer/trackball.cpp	2012-06-03 20:16:21 +0000
@@ -60,90 +60,91 @@
  * simple example, though, so that is left as an Exercise for the
  * Programmer.
  */
-#define TRACKBALLSIZE  (0.8f)
+#define TRACKBALLSIZE (0.8f)
 
 /*
  * Local function prototypes (not defined in trackball.h)
  */
-static double tb_project_to_sphere(double, double, double);
-static void normalize_quat(double [4]);
-
-void
-vzero(double *v)
-{
-    v[0] = 0.0;
-    v[1] = 0.0;
-    v[2] = 0.0;
-}
-
-void
-vset(double *v, double x, double y, double z)
-{
-    v[0] = x;
-    v[1] = y;
-    v[2] = z;
-}
-
-void
-vsub(const double *src1, const double *src2, double *dst)
-{
-    dst[0] = src1[0] - src2[0];
-    dst[1] = src1[1] - src2[1];
-    dst[2] = src1[2] - src2[2];
-}
-
-void
-vcopy(const double *v1, double *v2)
+static double   tb_project_to_sphere( double, double, double );
+static void     normalize_quat( double[4] );
+
+void vzero( double* v )
+{
+    v[0]    = 0.0;
+    v[1]    = 0.0;
+    v[2]    = 0.0;
+}
+
+
+void vset( double* v, double x, double y, double z )
+{
+    v[0]    = x;
+    v[1]    = y;
+    v[2]    = z;
+}
+
+
+void vsub( const double* src1, const double* src2, double* dst )
+{
+    dst[0]  = src1[0] - src2[0];
+    dst[1]  = src1[1] - src2[1];
+    dst[2]  = src1[2] - src2[2];
+}
+
+
+void vcopy( const double* v1, double* v2 )
 {
     register int i;
-    for (i = 0 ; i < 3 ; i++)
+
+    for( i = 0; i < 3; i++ )
         v2[i] = v1[i];
 }
 
-void
-vcross(const double *v1, const double *v2, double *cross)
+
+void vcross( const double* v1, const double* v2, double* cross )
 {
     double temp[3];
 
     temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
     temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
     temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
-    vcopy(temp, cross);
-}
-
-double
-vlength(const double *v)
-{
-    return (double) sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
-}
-
-void
-vscale(double *v, double div)
-{
-    v[0] *= div;
-    v[1] *= div;
-    v[2] *= div;
-}
-
-void
-vnormal(double *v)
-{
-    vscale(v, 1.0f/vlength(v));
-}
-
-double
-vdot(const double *v1, const double *v2)
-{
-    return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
-}
-
-void
-vadd(const double *src1, const double *src2, double *dst)
-{
-    dst[0] = src1[0] + src2[0];
-    dst[1] = src1[1] + src2[1];
-    dst[2] = src1[2] + src2[2];
-}
+    vcopy( temp, cross );
+}
+
+
+double vlength( const double* v )
+{
+    return (double) sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
+}
+
+
+void vscale( double* v, double div )
+{
+    v[0]    *= div;
+    v[1]    *= div;
+    v[2]    *= div;
+}
+
+
+void vnormal( double* v )
+{
+    vscale( v, 1.0f / vlength( v ) );
+}
+
+
+double vdot( const double* v1, const double* v2 )
+{
+    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
+}
+
+
+void vadd( const double* src1, const double* src2, double* dst )
+{
+    dst[0]  = src1[0] + src2[0];
+    dst[1]  = src1[1] + src2[1];
+    dst[2]  = src1[2] + src2[2];
+}
+
 
 /*
  * Ok, simulate a track-ball.  Project the points onto the virtual
@@ -157,17 +158,17 @@
  * It is assumed that the arguments to this routine are in the range
  * (-1.0 ... 1.0)
  */
-void
-trackball(double q[4], double p1x, double p1y, double p2x, double p2y)
+void trackball( double q[4], double p1x, double p1y, double p2x, double p2y )
 {
-    double a[3]; /* Axis of rotation */
-    double phi;  /* how much to rotate about axis */
-    double p1[3], p2[3], d[3];
-    double t;
+    double  a[3];   /* Axis of rotation */
+    double  phi;    /* how much to rotate about axis */
+    double  p1[3], p2[3], d[3];
+    double  t;
 
-    if (p1x == p2x && p1y == p2y) {
+    if( p1x == p2x && p1y == p2y )
+    {
         /* Zero rotation */
-        vzero(q);
+        vzero( q );
         q[3] = 1.0;
         return;
     }
@@ -176,61 +177,71 @@
      * First, figure out z-coordinates for projection of P1 and P2 to
      * deformed sphere
      */
-    vset(p1, p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y));
-    vset(p2, p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y));
+    vset( p1, p1x, p1y, tb_project_to_sphere( TRACKBALLSIZE, p1x, p1y ) );
+    vset( p2, p2x, p2y, tb_project_to_sphere( TRACKBALLSIZE, p2x, p2y ) );
 
     /*
      *  Now, we want the cross product of P1 and P2
      */
-    vcross(p2,p1,a);
+    vcross( p2, p1, a );
 
     /*
      *  Figure out how much to rotate around that axis.
      */
-    vsub(p1, p2, d);
-    t = vlength(d) / (2.0f*TRACKBALLSIZE);
+    vsub( p1, p2, d );
+    t = vlength( d ) / (2.0f * TRACKBALLSIZE);
 
     /*
      * Avoid problems with out-of-control values...
      */
-    if (t > 1.0) t = 1.0;
-    if (t < -1.0) t = -1.0;
-    phi = 2.0f * (double) asin(t);
-
-    axis_to_quat(a,phi,q);
+    if( t > 1.0 )
+        t = 1.0;
+
+    if( t < -1.0 )
+        t = -1.0;
+
+    phi = 2.0f * (double) asin( t );
+
+    axis_to_quat( a, phi, q );
 }
 
+
 /*
  *  Given an axis and angle, compute quaternion.
  */
-void
-axis_to_quat(double a[3], double phi, double q[4])
+void axis_to_quat( double a[3], double phi, double q[4] )
 {
-    vnormal(a);
-    vcopy(a, q);
-    vscale(q, (double) sin(phi/2.0));
-    q[3] = (double) cos(phi/2.0);
+    vnormal( a );
+    vcopy( a, q );
+    vscale( q, (double) sin( phi / 2.0 ) );
+    q[3] = (double) cos( phi / 2.0 );
 }
 
+
 /*
  * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
  * if we are away from the center of the sphere.
  */
-static double
-tb_project_to_sphere(double r, double x, double y)
+static double tb_project_to_sphere( double r, double x, double y )
 {
     double d, t, z;
 
-    d = (double) sqrt(x*x + y*y);
-    if (d < r * 0.70710678118654752440) {    /* Inside sphere */
-        z = (double) sqrt(r*r - d*d);
-    } else {           /* On hyperbola */
-        t = r / 1.41421356237309504880f;
-        z = t*t / d;
-    }
+    d = (double) sqrt( x * x + y * y );
+
+    if( d < r * 0.70710678118654752440 )      /* Inside sphere */
+    {
+        z = (double) sqrt( r * r - d * d );
+    }
+    else               /* On hyperbola */
+    {
+        t   = r / 1.41421356237309504880f;
+        z   = t * t / d;
+    }
+
     return z;
 }
 
+
 /*
  * Given two rotations, e1 and e2, expressed as quaternion rotations,
  * figure out the equivalent single rotation and stuff it into dest.
@@ -244,35 +255,36 @@
 
 #define RENORMCOUNT 97
 
-void
-add_quats(double q1[4], double q2[4], double dest[4])
+void add_quats( double q1[4], double q2[4], double dest[4] )
 {
-    static int count=0;
-    double t1[4], t2[4], t3[4];
-    double tf[4];
-
-    vcopy(q1,t1);
-    vscale(t1,q2[3]);
-
-    vcopy(q2,t2);
-    vscale(t2,q1[3]);
-
-    vcross(q2,q1,t3);
-    vadd(t1,t2,tf);
-    vadd(t3,tf,tf);
-    tf[3] = q1[3] * q2[3] - vdot(q1,q2);
+    static int  count = 0;
+    double      t1[4], t2[4], t3[4];
+    double      tf[4];
+
+    vcopy( q1, t1 );
+    vscale( t1, q2[3] );
+
+    vcopy( q2, t2 );
+    vscale( t2, q1[3] );
+
+    vcross( q2, q1, t3 );
+    vadd( t1, t2, tf );
+    vadd( t3, tf, tf );
+    tf[3] = q1[3] * q2[3] - vdot( q1, q2 );
 
     dest[0] = tf[0];
     dest[1] = tf[1];
     dest[2] = tf[2];
     dest[3] = tf[3];
 
-    if (++count > RENORMCOUNT) {
+    if( ++count > RENORMCOUNT )
+    {
         count = 0;
-        normalize_quat(dest);
+        normalize_quat( dest );
     }
 }
 
+
 /*
  * Quaternions always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
  * If they don't add up to 1.0, dividing by their magnitued will
@@ -285,20 +297,23 @@
  * - Pletinckx, D., Quaternion calculus as a basic tool in computer
  *   graphics, The Visual Computer 5, 2-13, 1989.
  */
-static void normalize_quat(double q[4])
+static void normalize_quat( double q[4] )
 {
-    int i;
-    double mag;
-
-    mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
-    for (i = 0; i < 4; i++) q[i] /= mag;
+    int     i;
+    double  mag;
+
+    mag = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
+
+    for( i = 0; i < 4; i++ )
+        q[i] /= mag;
 }
 
+
 /*
  * Build a rotation matrix, given a quaternion rotation.
  *
  */
-void build_rotmatrix(GLfloat m[4][4], double q[4])
+void build_rotmatrix( GLfloat m[4][4], double q[4] )
 {
     m[0][0] = 1.0f - 2.0f * (q[1] * q[1] + q[2] * q[2]);
     m[0][1] = 2.0f * (q[0] * q[1] - q[2] * q[3]);
@@ -306,7 +321,7 @@
     m[0][3] = 0.0f;
 
     m[1][0] = 2.0f * (q[0] * q[1] + q[2] * q[3]);
-    m[1][1]= 1.0f - 2.0f * (q[2] * q[2] + q[0] * q[0]);
+    m[1][1] = 1.0f - 2.0f * (q[2] * q[2] + q[0] * q[0]);
     m[1][2] = 2.0f * (q[1] * q[2] - q[0] * q[3]);
     m[1][3] = 0.0f;
 
@@ -320,4 +335,3 @@
     m[3][2] = 0.0f;
     m[3][3] = 1.0f;
 }
-

=== modified file '3d-viewer/trackball.h'
--- 3d-viewer/trackball.h	2008-10-30 10:55:46 +0000
+++ 3d-viewer/trackball.h	2012-06-03 20:16:21 +0000
@@ -47,7 +47,7 @@
  * The resulting rotation is returned as a quaternion rotation in the
  * first paramater.
  */
-void trackball(double q[4], double p1x, double p1y, double p2x, double p2y);
+void trackball( double q[4], double p1x, double p1y, double p2x, double p2y );
 
 /*
  * Given two quaternions, add them together to get a third quaternion.
@@ -57,18 +57,17 @@
  * rotation, the second and third the total rotation (which will be
  * over-written with the resulting new total rotation).
  */
-void add_quats(double *q1, double *q2, double *dest);
+void add_quats( double* q1, double* q2, double* dest );
 
 /*
  * A useful function, builds a rotation matrix in Matrix based on
  * given quaternion.
  */
-void build_rotmatrix(GLfloat m[4][4], double q[4]);
+void build_rotmatrix( GLfloat m[4][4], double q[4] );
 
 /*
  * This function computes a quaternion based on an axis (defined by
  * the given vector) and an angle about which to rotate.  The angle is
  * expressed in radians.  The result is put into the third argument.
  */
-void axis_to_quat(double a[3], double phi, double q[4]);
-
+void axis_to_quat( double a[3], double phi, double q[4] );