/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014-2015 Mario Luzeiro * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * @file 3d_draw.cpp * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GLM_FORCE_RADIANS #include #include #ifdef __WINDOWS__ #include // must be included before gl.h #endif #include <3d_viewer.h> #include <3d_canvas.h> #include #include #include <3d_draw_basic_functions.h> #include #include extern SHAPE_POLY_SET::POLYGON_MODE polygonsCalcMode; /* returns the Z orientation parameter 1.0 or -1.0 for aLayer * Z orientation is 1.0 for all layers but "back" layers: * B_Cu , B_Adhes, B_Paste ), B_SilkS * used to calculate the Z orientation parameter for glNormal3f */ GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer ); /** * Class STATUS_TEXT_REPORTER * is a wrapper for reporting to a wxString in a wxFrame status text. */ class STATUS_TEXT_REPORTER : public REPORTER { wxFrame * m_frame; int m_position; bool m_hasMessage; public: STATUS_TEXT_REPORTER( wxFrame* aFrame, int aPosition = 0 ) : REPORTER(), m_frame( aFrame ), m_position( aPosition ) { m_hasMessage = false; } REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_UNDEFINED ) { if( !aText.IsEmpty() ) m_hasMessage = true; m_frame->SetStatusText( aText, m_position ); return *this; } bool HasMessage() const { return m_hasMessage; } }; void EDA_3D_CANVAS::create_and_render_shadow_buffer( GLuint *aDst_gl_texture, GLuint aTexture_size, bool aDraw_body, int aBlurPasses ) { glDisable( GL_TEXTURE_2D ); glViewport( 0, 0, aTexture_size, aTexture_size); glClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Render body and shapes if( aDraw_body && m_glLists[GL_ID_BODY] ) glCallList( m_glLists[GL_ID_BODY] ); if( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ) glCallList( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ); // Create and Initialize the float depth buffer float *depthbufferFloat = (float*) malloc( aTexture_size * aTexture_size * sizeof(float) ); for( unsigned int i = 0; i < (aTexture_size * aTexture_size); i++ ) depthbufferFloat[i] = 1.0f; glPixelStorei( GL_PACK_ALIGNMENT, 4 ); glPixelStorei( GL_UNPACK_ALIGNMENT, 4 ); glReadBuffer( GL_BACK_LEFT ); glReadPixels( 0, 0, aTexture_size, aTexture_size, GL_DEPTH_COMPONENT, GL_FLOAT, depthbufferFloat ); CheckGLError( __FILE__, __LINE__ ); glEnable( GL_TEXTURE_2D ); glGenTextures( 1, aDst_gl_texture ); glBindTexture( GL_TEXTURE_2D, *aDst_gl_texture ); CIMAGE imgDepthBuffer( aTexture_size, aTexture_size ); CIMAGE imgDepthBufferAux( aTexture_size, aTexture_size ); imgDepthBuffer.SetPixelsFromNormalizedFloat( depthbufferFloat ); free( depthbufferFloat ); // Debug texture image //wxString filename; //filename.Printf( "imgDepthBuffer_%04d", *aDst_gl_texture ); //imgDepthBuffer.SaveAsPNG( filename ); while( aBlurPasses > 0 ) { aBlurPasses--; imgDepthBufferAux.EfxFilter( &imgDepthBuffer, FILTER_GAUSSIAN_BLUR ); imgDepthBuffer.EfxFilter( &imgDepthBufferAux, FILTER_GAUSSIAN_BLUR ); } // Debug texture image //filename.Printf( "imgDepthBuffer_blur%04d", *aDst_gl_texture ); //imgDepthBuffer.SaveAsPNG( filename ); unsigned char *depthbufferRGBA = (unsigned char*) malloc( aTexture_size * aTexture_size * 4 ); unsigned char *pPixels = imgDepthBuffer.GetBuffer(); // Convert it to a RGBA buffer for( unsigned int i = 0; i < (aTexture_size * aTexture_size); i++ ) { depthbufferRGBA[i * 4 + 0] = 0; depthbufferRGBA[i * 4 + 1] = 0; depthbufferRGBA[i * 4 + 2] = 0; depthbufferRGBA[i * 4 + 3] = 255 - pPixels[i]; // Store in alpha channel the inversion of the image } glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, aTexture_size, aTexture_size, 0, GL_RGBA, GL_UNSIGNED_BYTE, depthbufferRGBA ); free( depthbufferRGBA ); CheckGLError( __FILE__, __LINE__ ); } /// Scale factor to make a bigger BBox in order to blur the texture and dont have artifacts in the edges #define SHADOW_BOUNDING_BOX_SCALE 1.25f void EDA_3D_CANVAS::generateFakeShadowsTextures( REPORTER* aErrorMessages, REPORTER* aActivity ) { if( m_shadow_init == true ) { return; } // Init info 3d parameters and create gl lists: CreateDrawGL_List( aErrorMessages, aActivity ); DBG( unsigned strtime = GetRunningMicroSecs() ); m_shadow_init = true; glClearColor( 0, 0, 0, 1 ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); const float zDistMax = Millimeter2iu( 3.5 ) * GetPrm3DVisu().m_BiuTo3Dunits; glOrtho( -GetPrm3DVisu().m_BoardSize.x * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, GetPrm3DVisu().m_BoardSize.x * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, -GetPrm3DVisu().m_BoardSize.y * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, GetPrm3DVisu().m_BoardSize.y * GetPrm3DVisu().m_BiuTo3Dunits / 2.0f, 0.0, zDistMax ); float zpos = GetPrm3DVisu().GetLayerZcoordBIU( F_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; // Render FRONT shadow glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glTranslatef( 0.0f, 0.0f, zpos ); glRotatef( 180.0f, 0.0f, 1.0f, 0.0f ); // move the board in order to draw it with its center at 0,0 3D coordinates glTranslatef( -GetPrm3DVisu().m_BoardPos.x * GetPrm3DVisu().m_BiuTo3Dunits, -GetPrm3DVisu().m_BoardPos.y * GetPrm3DVisu().m_BiuTo3Dunits, 0.0f ); create_and_render_shadow_buffer( &m_text_fake_shadow_front, 512, false, 1 ); zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; // Render BACK shadow glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glTranslatef( 0.0f, 0.0f, fabs( zpos ) ); // move the board in order to draw it with its center at 0,0 3D coordinates glTranslatef( -GetPrm3DVisu().m_BoardPos.x * GetPrm3DVisu().m_BiuTo3Dunits, -GetPrm3DVisu().m_BoardPos.y * GetPrm3DVisu().m_BiuTo3Dunits, 0.0f ); create_and_render_shadow_buffer( &m_text_fake_shadow_back, 512, false, 1 ); // Render ALL BOARD shadow glMatrixMode( GL_PROJECTION ); glLoadIdentity(); // Normalization scale to convert bouding box // to normalize 3D units between -1.0 and +1.0 S3D_VERTEX v = m_fastAABBox_Shadow.Max() - m_fastAABBox_Shadow.Min(); float BoundingBoxBoardiuTo3Dunits = 2.0f / glm::max( v.x, v.y ); //float zDistance = (m_lightPos.z * zDistMax) / sqrt( (m_lightPos.z - m_fastAABBox_Shadow.Min().z) ); float zDistance = (m_lightPos.z - m_fastAABBox_Shadow.Min().z) / 3.0f; glOrtho( -v.x * BoundingBoxBoardiuTo3Dunits / 2.0f, v.x * BoundingBoxBoardiuTo3Dunits / 2.0f, -v.y * BoundingBoxBoardiuTo3Dunits / 2.0f, v.y * BoundingBoxBoardiuTo3Dunits / 2.0f, 0.0f, zDistance ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // fits the bouding box to scale this size glScalef( BoundingBoxBoardiuTo3Dunits, BoundingBoxBoardiuTo3Dunits, 1.0f ); // Place the eye in the lowerpoint of boudingbox and turn arround and look up to the model glTranslatef( 0.0f, 0.0f, m_fastAABBox_Shadow.Min().z ); glRotatef( 180.0, 0.0f, 1.0f, 0.0f ); // move the bouding box in order to draw it with its center at 0,0 3D coordinates glTranslatef( -(m_fastAABBox_Shadow.Min().x + v.x / 2.0f), -(m_fastAABBox_Shadow.Min().y + v.y / 2.0f), 0.0f ); create_and_render_shadow_buffer( &m_text_fake_shadow_board, 512, true, 10 ); DBG( printf( " generateFakeShadowsTextures total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); } void EDA_3D_CANVAS::Redraw() { // SwapBuffer requires the window to be shown before calling if( !IsShownOnScreen() ) return; wxString err_messages; WX_STRING_REPORTER errorReporter( &err_messages ); STATUS_TEXT_REPORTER activityReporter( Parent(), 0 ); // Display build time at the end of build unsigned strtime = GetRunningMicroSecs(); SetCurrent( *m_glRC ); // 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. wxSize size = GetClientSize(); InitGL(); if( isRealisticMode() && isEnabled( FL_RENDER_SHADOWS ) ) { generateFakeShadowsTextures( &errorReporter, &activityReporter ); } // *MUST* be called *after* SetCurrent( ): glViewport( 0, 0, size.x, size.y ); // clear color and depth buffers glClearColor( 0.95, 0.95, 1.0, 1.0 ); glClearStencil( 0 ); glClearDepth( 1.0 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); glShadeModel( GL_SMOOTH ); // Draw background glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glDisable( GL_LIGHTING ); glDisable( GL_COLOR_MATERIAL ); glDisable( GL_DEPTH_TEST ); glDisable( GL_TEXTURE_2D ); // Draw the background ( rectangle with color gradient) glBegin( GL_QUADS ); SetGLColor( GetPrm3DVisu().m_BgColor_Top, 1.0 ); glVertex2f( -1.0, 1.0 ); // Top left corner SetGLColor( GetPrm3DVisu().m_BgColor, 1.0 ); glVertex2f( -1.0,-1.0 ); // bottom left corner glVertex2f( 1.0,-1.0 ); // bottom right corner SetGLColor( GetPrm3DVisu().m_BgColor_Top, 1.0 ); glVertex2f( 1.0, 1.0 ); // top right corner glEnd(); glEnable( GL_DEPTH_TEST ); // set viewing projection glMatrixMode( GL_PROJECTION ); glLoadIdentity(); #define MAX_VIEW_ANGLE 160.0 / 45.0 if( GetPrm3DVisu().m_Zoom > MAX_VIEW_ANGLE ) GetPrm3DVisu().m_Zoom = MAX_VIEW_ANGLE; if( Parent()->ModeIsOrtho() ) { // OrthoReductionFactor is chosen to provide roughly the same size as // Perspective View const double orthoReductionFactor = 400 / GetPrm3DVisu().m_Zoom; // Initialize Projection Matrix for Ortographic View glOrtho( -size.x / orthoReductionFactor, size.x / orthoReductionFactor, -size.y / orthoReductionFactor, size.y / orthoReductionFactor, 1, 100 ); } else { // Ratio width / height of the window display double ratio_HV = (double) size.x / size.y; // Initialize Projection Matrix for Perspective View gluPerspective( 45.0f * GetPrm3DVisu().m_Zoom, ratio_HV, 1, 100 ); } // position viewer glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); glTranslatef( 0.0f, 0.0f, -(m_ZBottom + m_ZTop) / 2.0f ); // Setup light sources: SetLights(); CheckGLError( __FILE__, __LINE__ ); glMatrixMode( GL_MODELVIEW ); // position viewer // transformations GLfloat mat[4][4]; // Translate motion first, so rotations don't mess up the orientation... glTranslatef( m_draw3dOffset.x, m_draw3dOffset.y, 0.0F ); build_rotmatrix( mat, GetPrm3DVisu().m_Quat ); glMultMatrixf( &mat[0][0] ); glRotatef( GetPrm3DVisu().m_Rot[0], 1.0, 0.0, 0.0 ); glRotatef( GetPrm3DVisu().m_Rot[1], 0.0, 1.0, 0.0 ); glRotatef( GetPrm3DVisu().m_Rot[2], 0.0, 0.0, 1.0 ); if( ! m_glLists[GL_ID_BOARD] || ! m_glLists[GL_ID_TECH_LAYERS] ) CreateDrawGL_List( &errorReporter, &activityReporter ); if( isEnabled( FL_AXIS ) && m_glLists[GL_ID_AXIS] ) glCallList( m_glLists[GL_ID_AXIS] ); // move the board in order to draw it with its center at 0,0 3D coordinates glTranslatef( -GetPrm3DVisu().m_BoardPos.x * GetPrm3DVisu().m_BiuTo3Dunits, -GetPrm3DVisu().m_BoardPos.y * GetPrm3DVisu().m_BiuTo3Dunits, 0.0f ); if( isEnabled( FL_MODULE ) ) { if( ! m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ) CreateDrawGL_List( &errorReporter, &activityReporter ); } glEnable( GL_LIGHTING ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); if( isRealisticMode() && isEnabled( FL_RENDER_TEXTURES ) ) glEnable( GL_TEXTURE_2D ); else glDisable( GL_TEXTURE_2D ); // Set material for the board glEnable( GL_COLOR_MATERIAL ); SetOpenGlDefaultMaterial(); //glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, FALSE ); // Board Body GLint shininess_value = 32; glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, shininess_value ); if( isEnabled( FL_SHOW_BOARD_BODY ) ) { if( m_glLists[GL_ID_BODY] ) { glCallList( m_glLists[GL_ID_BODY] ); } } // Board // specify material parameters for the lighting model. shininess_value = 52; glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, shininess_value ); glm::vec4 specular( GetPrm3DVisu().m_CopperColor.m_Red * 0.20f, GetPrm3DVisu().m_CopperColor.m_Green * 0.20f, GetPrm3DVisu().m_CopperColor.m_Blue * 0.20f, 1.0f ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.x ); if( m_glLists[GL_ID_BOARD] ) { glCallList( m_glLists[GL_ID_BOARD] ); } // Tech layers shininess_value = 32; glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, shininess_value ); glm::vec4 specularTech( 0.0f, 0.0f, 0.0f, 1.0f ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specularTech.x ); if( m_glLists[GL_ID_TECH_LAYERS] ) { glCallList( m_glLists[GL_ID_TECH_LAYERS] ); } if( isEnabled( FL_COMMENTS ) || isEnabled( FL_ECO ) ) { if( ! m_glLists[GL_ID_AUX_LAYERS] ) CreateDrawGL_List( &errorReporter, &activityReporter ); glCallList( m_glLists[GL_ID_AUX_LAYERS] ); } //glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, TRUE ); // Draw Component Shadow if( isEnabled( FL_MODULE ) && isRealisticMode() && isEnabled( FL_RENDER_SHADOWS ) ) { glEnable( GL_CULL_FACE ); glDisable( GL_DEPTH_TEST ); glEnable( GL_COLOR_MATERIAL ) ; SetOpenGlDefaultMaterial(); glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); glEnable( GL_TEXTURE_2D ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); if( m_glLists[GL_ID_SHADOW_FRONT] ) { glBindTexture( GL_TEXTURE_2D, m_text_fake_shadow_front ); glCallList( m_glLists[GL_ID_SHADOW_FRONT] ); } if( m_glLists[GL_ID_SHADOW_BACK] ) { glBindTexture( GL_TEXTURE_2D, m_text_fake_shadow_back ); glCallList( m_glLists[GL_ID_SHADOW_BACK] ); } glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); glEnable( GL_DEPTH_TEST ); glDisable( GL_TEXTURE_2D ); glDisable( GL_CULL_FACE ); } glEnable( GL_COLOR_MATERIAL ); SetOpenGlDefaultMaterial(); glDisable( GL_BLEND ); // Draw Solid Shapes if( isEnabled( FL_MODULE ) ) { if( ! m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ) CreateDrawGL_List( &errorReporter, &activityReporter ); glCallList( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] ); } glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // Grid uses transparency: draw it after all objects if( isEnabled( FL_GRID ) ) { if( ! m_glLists[GL_ID_GRID] ) CreateDrawGL_List( &errorReporter, &activityReporter ); glCallList( m_glLists[GL_ID_GRID] ); } // Draw Board Shadow if( isRealisticMode() && isEnabled( FL_RENDER_SHADOWS ) ) { if( m_glLists[GL_ID_SHADOW_BOARD] ) { glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glColor4f( 1.0, 1.0, 1.0, 0.75f ); glEnable( GL_CULL_FACE ); glDisable( GL_COLOR_MATERIAL ); glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, m_text_fake_shadow_board ); glCallList( m_glLists[GL_ID_SHADOW_BOARD] ); glDisable( GL_CULL_FACE ); glDisable( GL_TEXTURE_2D ); } } // This list must be drawn last, because it contains the // transparent gl objects, which should be drawn after all // non transparent objects if( isEnabled( FL_MODULE ) && m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] ) { glEnable( GL_COLOR_MATERIAL ); SetOpenGlDefaultMaterial(); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glCallList( m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] ); } // Debug bounding boxes /* glDisable( GL_BLEND ); glDisable( GL_COLOR_MATERIAL ); glDisable( GL_LIGHTING ); glColor4f( 1.0f, 0.0f, 1.0f, 1.0f ); m_fastAABBox_Shadow.GLdebug(); glColor4f( 0.0f, 1.0f, 1.0f, 1.0f ); m_boardAABBox.GLdebug(); */ SwapBuffers(); // Show calculation time if some activity was reported if( activityReporter.HasMessage() ) { // Calculation time in seconds double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e6; activityReporter.Report( wxString::Format( _( "Build time %.3f s" ), calculation_time ) ); } else activityReporter.Report( wxEmptyString ); if( !err_messages.IsEmpty() ) wxLogMessage( err_messages ); } /** * Function buildBoard3DAuxLayers * Called by CreateDrawGL_List() * Fills the OpenGL GL_ID_BOARD draw list with items * on aux layers only */ void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* aActivity ) { const int segcountforcircle = 18; double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) ); BOARD* pcb = GetBoard(); SHAPE_POLY_SET bufferPolys; static const LAYER_ID sequence[] = { Dwgs_User, Cmts_User, Eco1_User, Eco2_User, Edge_Cuts, Margin }; for( LSEQ aux( sequence, sequence+DIM(sequence) ); aux; ++aux ) { LAYER_ID layer = *aux; if( !is3DLayerEnabled( layer ) ) continue; if( aActivity ) aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) ); bufferPolys.RemoveAllContours(); for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() ) { if( !item->IsOnLayer( layer ) ) continue; switch( item->Type() ) { case PCB_LINE_T: ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( bufferPolys, 0, segcountforcircle, correctionFactor ); break; case PCB_TEXT_T: ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet( bufferPolys, 0, segcountforcircle, correctionFactor ); break; default: break; } } for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { module->TransformPadsShapesWithClearanceToPolygon( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); module->TransformGraphicShapesWithClearanceToPolygonSet( layer, bufferPolys, 0, segcountforcircle, correctionFactor ); } // bufferPolys contains polygons to merge. Many overlaps . // Calculate merged polygons and remove pads and vias holes if( bufferPolys.IsEmpty() ) continue; bufferPolys.Simplify( polygonsCalcMode ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); // for Draw3D_SolidHorizontalPolyPolygons, // zpos it the middle between bottom and top sides. // However for top layers, zpos should be the bottom layer pos, // and for bottom layers, zpos should be the top layer pos. if( Get3DLayer_Z_Orientation( layer ) > 0 ) zpos += thickness/2; else zpos -= thickness/2 ; float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted) // If we are not using thickness, then the znormal must face the layer direction // because it will draw just one plane if( !thickness ) zNormal = Get3DLayer_Z_Orientation( layer ); setGLTechLayersColor( layer ); Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness, GetPrm3DVisu().m_BiuTo3Dunits, false, zNormal ); } } void EDA_3D_CANVAS::buildShadowList( GLuint aFrontList, GLuint aBacklist, GLuint aBoardList ) { // Board shadows are based on board dimension. float xmin = m_boardAABBox.Min().x; float xmax = m_boardAABBox.Max().x; float ymin = m_boardAABBox.Min().y; float ymax = m_boardAABBox.Max().y; float zpos = GetPrm3DVisu().GetLayerZcoordBIU( F_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; // Shadow FRONT glNewList( aFrontList, GL_COMPILE ); glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( F_Paste ) ); glBegin (GL_QUADS); glTexCoord2f( 1.0, 0.0 ); glVertex3f( xmin, ymin, zpos ); glTexCoord2f( 0.0, 0.0 ); glVertex3f( xmax, ymin, zpos ); glTexCoord2f( 0.0, 1.0 ); glVertex3f( xmax, ymax, zpos ); glTexCoord2f( 1.0, 1.0 ); glVertex3f( xmin, ymax, zpos ); glEnd(); glEndList(); // Shadow BACK zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Paste ) * GetPrm3DVisu().m_BiuTo3Dunits; glNewList( aBacklist, GL_COMPILE ); glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( B_Paste ) ); glBegin (GL_QUADS); glTexCoord2f( 0.0, 0.0 ); glVertex3f( xmin, ymin, zpos ); glTexCoord2f( 0.0, 1.0 ); glVertex3f( xmin, ymax, zpos ); glTexCoord2f( 1.0, 1.0 ); glVertex3f( xmax, ymax, zpos ); glTexCoord2f( 1.0, 0.0 ); glVertex3f( xmax, ymin, zpos ); glEnd(); glEndList(); // Shadow BOARD // Floor shadow is based on axis alighned bounding box dimension xmin = m_fastAABBox_Shadow.Min().x; xmax = m_fastAABBox_Shadow.Max().x; ymin = m_fastAABBox_Shadow.Min().y; ymax = m_fastAABBox_Shadow.Max().y; glNewList( aBoardList, GL_COMPILE ); glNormal3f( 0.0, 0.0, Get3DLayer_Z_Orientation( F_Paste ) ); glBegin (GL_QUADS); glTexCoord2f( 1.0, 0.0 ); glVertex3f( xmin, ymin, m_fastAABBox_Shadow.Min().z ); glTexCoord2f( 0.0, 0.0 ); glVertex3f( xmax, ymin, m_fastAABBox_Shadow.Min().z ); glTexCoord2f( 0.0, 1.0 ); glVertex3f( xmax, ymax, m_fastAABBox_Shadow.Min().z ); glTexCoord2f( 1.0, 1.0 ); glVertex3f( xmin, ymax, m_fastAABBox_Shadow.Min().z ); glEnd(); glEndList(); } void EDA_3D_CANVAS::CreateDrawGL_List( REPORTER* aErrorMessages, REPORTER* aActivity ) { BOARD* pcb = GetBoard(); wxBusyCursor dummy; // Build 3D board parameters: GetPrm3DVisu().InitSettings( pcb ); glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); // Create axis gl list (if it is not shown, the list will be not called draw3DAxis(); // Create Board full gl lists: if( ! m_glLists[GL_ID_BOARD] ) { DBG( unsigned strtime = GetRunningMicroSecs() ); m_glLists[GL_ID_BOARD] = glGenLists( 1 ); m_glLists[GL_ID_BODY] = glGenLists( 1 ); buildBoard3DView(m_glLists[GL_ID_BOARD], m_glLists[GL_ID_BODY], aErrorMessages, aActivity ); CheckGLError( __FILE__, __LINE__ ); DBG( printf( " buildBoard3DView total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); } if( ! m_glLists[GL_ID_TECH_LAYERS] ) { DBG( unsigned strtime = GetRunningMicroSecs() ); m_glLists[GL_ID_TECH_LAYERS] = glGenLists( 1 ); glNewList( m_glLists[GL_ID_TECH_LAYERS], GL_COMPILE ); // when calling BuildTechLayers3DView, // do not show warnings, which are the same as buildBoard3DView buildTechLayers3DView( aErrorMessages, aActivity ); glEndList(); CheckGLError( __FILE__, __LINE__ ); DBG( printf( " buildTechLayers3DView total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); } if( ! m_glLists[GL_ID_AUX_LAYERS] ) { DBG( unsigned strtime = GetRunningMicroSecs() ); m_glLists[GL_ID_AUX_LAYERS] = glGenLists( 1 ); glNewList( m_glLists[GL_ID_AUX_LAYERS], GL_COMPILE ); buildBoard3DAuxLayers( aErrorMessages, aActivity ); glEndList(); CheckGLError( __FILE__, __LINE__ ); DBG( printf( " buildBoard3DAuxLayers total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); } // draw modules 3D shapes if( ! m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] && isEnabled( FL_MODULE ) ) { m_glLists[GL_ID_3DSHAPES_SOLID_FRONT] = glGenLists( 1 ); // GL_ID_3DSHAPES_TRANSP_FRONT is an auxiliary list for 3D shapes; // Ensure it is cleared before rebuilding it if( m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] ) glDeleteLists( m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT], 1 ); bool useMaterial = g_Parm_3D_Visu.GetFlag( FL_RENDER_MATERIAL ); if( useMaterial ) m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] = glGenLists( 1 ); else m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT] = 0; buildFootprintShape3DList( m_glLists[GL_ID_3DSHAPES_SOLID_FRONT], m_glLists[GL_ID_3DSHAPES_TRANSP_FRONT], aErrorMessages, aActivity ); CheckGLError( __FILE__, __LINE__ ); } calcBBox(); // Create grid gl list if( ! m_glLists[GL_ID_GRID] ) { m_glLists[GL_ID_GRID] = glGenLists( 1 ); glNewList( m_glLists[GL_ID_GRID], GL_COMPILE ); draw3DGrid( GetPrm3DVisu().m_3D_Grid ); glEndList(); } if( !m_glLists[GL_ID_SHADOW_FRONT] ) m_glLists[GL_ID_SHADOW_FRONT] = glGenLists( 1 ); if( !m_glLists[GL_ID_SHADOW_BACK] ) m_glLists[GL_ID_SHADOW_BACK] = glGenLists( 1 ); if( !m_glLists[GL_ID_SHADOW_BOARD] ) m_glLists[GL_ID_SHADOW_BOARD] = glGenLists( 1 ); buildShadowList( m_glLists[GL_ID_SHADOW_FRONT], m_glLists[GL_ID_SHADOW_BACK], m_glLists[GL_ID_SHADOW_BOARD] ); CheckGLError( __FILE__, __LINE__ ); } void EDA_3D_CANVAS::calcBBox() { BOARD* pcb = GetBoard(); m_fastAABBox.Reset(); for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) { CBBOX tmpFastAABBox; // Compute the transformation matrix for this module based on translation, rotation and orientation. float zpos = GetPrm3DVisu().GetModulesZcoord3DIU( module->IsFlipped() ); wxPoint pos = module->GetPosition(); glm::mat4 fullTransformMatrix; fullTransformMatrix = glm::translate( glm::mat4(), S3D_VERTEX( (float)(pos.x * GetPrm3DVisu().m_BiuTo3Dunits), (float)(-pos.y * GetPrm3DVisu().m_BiuTo3Dunits), zpos ) ); if( module->GetOrientation() ) fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians( (float)(module->GetOrientation() / 10.0f) ), S3D_VERTEX( 0.0f, 0.0f, 1.0f ) ); if( module->IsFlipped() ) { fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians( 180.0f ), S3D_VERTEX( 0.0f, 1.0f, 0.0f ) ); fullTransformMatrix = glm::rotate( fullTransformMatrix, glm::radians( 180.0f ), S3D_VERTEX( 0.0f, 0.0f, 1.0f ) ); } // Compute a union bounding box for all the shapes of the model S3D_MASTER* shape3D = module->Models(); for( ; shape3D; shape3D = shape3D->Next() ) { if( shape3D->Is3DType( S3D_MASTER::FILE3D_VRML ) ) tmpFastAABBox.Union( shape3D->getFastAABBox() ); } tmpFastAABBox.ApplyTransformationAA( fullTransformMatrix ); m_fastAABBox.Union( tmpFastAABBox ); } // Create a board bounding box based on board size wxSize brd_size = getBoardSize(); wxPoint brd_center_pos = getBoardCenter(); float xsize = brd_size.x; float ysize = brd_size.y; float scale = GetPrm3DVisu().m_BiuTo3Dunits; float xmin = (brd_center_pos.x - xsize / 2.0) * scale; float xmax = (brd_center_pos.x + xsize / 2.0) * scale; float ymin = (brd_center_pos.y - ysize / 2.0) * scale; float ymax = (brd_center_pos.y + ysize / 2.0) * scale; float zmin = GetPrm3DVisu().GetLayerZcoordBIU( B_Adhes ) * scale; float zmax = GetPrm3DVisu().GetLayerZcoordBIU( F_Adhes ) * scale; m_boardAABBox = CBBOX( S3D_VERTEX(xmin, ymin, zmin), S3D_VERTEX(xmax, ymax, zmax) ); // Add BB board with BB models and scale it a bit m_fastAABBox.Union( m_boardAABBox ); m_fastAABBox_Shadow = m_fastAABBox; m_fastAABBox_Shadow.Scale( SHADOW_BOUNDING_BOX_SCALE ); } void EDA_3D_CANVAS::buildFootprintShape3DList( GLuint aOpaqueList, GLuint aTransparentList, REPORTER* aErrorMessages, REPORTER* aActivity ) { DBG( unsigned strtime = GetRunningMicroSecs() ); if( aActivity ) aActivity->Report( _( "Load 3D Shapes" ) ); // clean the parser list if it have any already loaded files m_model_parsers_list.clear(); m_model_filename_list.clear(); BOARD* pcb = GetBoard(); for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) read3DComponentShape( module ); DBG( printf( " read3DComponentShape total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); DBG( strtime = GetRunningMicroSecs() ); bool useMaterial = g_Parm_3D_Visu.GetFlag( FL_RENDER_MATERIAL ); if( useMaterial ) { // aOpaqueList is the gl list for non transparent items // aTransparentList is the gl list for non transparent items, // which need to be drawn after all other items glNewList( aOpaqueList, GL_COMPILE ); bool loadOpaqueObjects = true; for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) render3DComponentShape( module, loadOpaqueObjects, !loadOpaqueObjects ); glEndList(); glNewList( aTransparentList, GL_COMPILE ); bool loadTransparentObjects = true; for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) render3DComponentShape( module, !loadTransparentObjects, loadTransparentObjects ); glEndList(); } else { // Just create one list glNewList( aOpaqueList, GL_COMPILE ); for( MODULE* module = pcb->m_Modules; module; module = module->Next() ) render3DComponentShape( module, false, false ); glEndList(); } DBG( printf( " render3DComponentShape total time %f ms\n", (double) (GetRunningMicroSecs() - strtime) / 1000.0 ) ); } bool EDA_3D_CANVAS::read3DComponentShape( MODULE* module ) { if( module ) { S3D_MASTER* shape3D = module->Models(); for( ; shape3D; shape3D = shape3D->Next() ) { if( shape3D->Is3DType( S3D_MASTER::FILE3D_VRML ) ) { bool found = false; unsigned int i; wxString shape_filename = shape3D->GetShape3DFullFilename(); // Search for already loaded files for( i = 0; i < m_model_filename_list.size(); i++ ) { if( shape_filename.Cmp(m_model_filename_list[i]) == 0 ) { found = true; break; } } if( found == false ) { // Create a new parser S3D_MODEL_PARSER *newParser = S3D_MODEL_PARSER::Create( shape3D, shape3D->GetShape3DExtension() ); if( newParser ) { // Read file if( shape3D->ReadData( newParser ) == 0 ) { // Store this couple filename / parsed file m_model_filename_list.push_back( shape_filename ); m_model_parsers_list.push_back( newParser ); } } } else { // Reusing file shape3D->m_parser = m_model_parsers_list[i]; } } } } return true; } void EDA_3D_CANVAS::render3DComponentShape( MODULE* module, bool aIsRenderingJustNonTransparentObjects, bool aIsRenderingJustTransparentObjects ) { double zpos = GetPrm3DVisu().GetModulesZcoord3DIU( module->IsFlipped() ); glPushMatrix(); wxPoint pos = module->GetPosition(); glTranslatef( pos.x * GetPrm3DVisu().m_BiuTo3Dunits, -pos.y * GetPrm3DVisu().m_BiuTo3Dunits, zpos ); if( module->GetOrientation() ) glRotatef( (double) module->GetOrientation() / 10.0, 0.0f, 0.0f, 1.0f ); if( module->IsFlipped() ) { glRotatef( 180.0f, 0.0f, 1.0f, 0.0f ); glRotatef( 180.0f, 0.0f, 0.0f, 1.0f ); } S3D_MASTER* shape3D = module->Models(); for( ; shape3D; shape3D = shape3D->Next() ) { if( shape3D->Is3DType( S3D_MASTER::FILE3D_VRML ) ) { glPushMatrix(); shape3D->Render( aIsRenderingJustNonTransparentObjects, aIsRenderingJustTransparentObjects ); if( isEnabled( FL_RENDER_SHOW_MODEL_BBOX ) ) { // Set the alpha current color to opaque float currentColor[4]; glGetFloatv( GL_CURRENT_COLOR,currentColor ); currentColor[3] = 1.0f; glColor4fv( currentColor ); CBBOX thisBBox = shape3D->getBBox(); thisBBox.GLdebug(); } glPopMatrix(); // Debug AABBox //thisBBox = shape3D->getfastAABBox(); //thisBBox.GLdebug(); } } glPopMatrix(); } bool EDA_3D_CANVAS::is3DLayerEnabled( LAYER_ID aLayer ) const { DISPLAY3D_FLG flg; // see if layer needs to be shown // check the flags switch( aLayer ) { case B_Adhes: case F_Adhes: flg = FL_ADHESIVE; break; case B_Paste: case F_Paste: flg = FL_SOLDERPASTE; break; case B_SilkS: case F_SilkS: flg = FL_SILKSCREEN; break; case B_Mask: case F_Mask: flg = FL_SOLDERMASK; break; case Dwgs_User: case Cmts_User: if( isRealisticMode() ) return false; flg = FL_COMMENTS; break; case Eco1_User: case Eco2_User: if( isRealisticMode() ) return false; flg = FL_ECO; break; case B_Cu: case F_Cu: return GetPrm3DVisu().m_BoardSettings->IsLayerVisible( aLayer ) || isRealisticMode(); break; default: // the layer is an internal copper layer, used the visibility // if( isRealisticMode() ) return false; return GetPrm3DVisu().m_BoardSettings->IsLayerVisible( aLayer ); } // The layer has a flag, return the flag return isEnabled( flg ); }