kicad-source/3d-viewer/3d_draw.cpp
unknown d40ea8adcb 3D viewer: fix issues with transparencies in some models (material issues in VRML2 parser)
Fix some issues in filling zones normals.
Fix an issue with some models that have materials but didn't defined the diffuse color.
Workaround for Bug #1443431.
Implement some missing "code logic" for pervertexperface normals.
Remove some not used functions.
Calculate normals using double type.
2015-04-16 09:43:27 +02:00

1835 lines
61 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014-2015 Mario Luzeiro <mrluzeiro@gmail.com>
* 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 <fctsys.h>
#include <common.h>
#include <trigo.h>
#include <pcbstruct.h>
#include <drawtxt.h>
#include <layers_id_colors_and_visibility.h>
#include <wxBasePcbFrame.h>
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_edge_mod.h>
#include <class_zone.h>
#include <class_drawsegment.h>
#include <class_pcb_text.h>
#include <colors_selection.h>
#include <convert_basic_shapes_to_polygon.h>
#define GLM_FORCE_RADIANS
#include <gal/opengl/glm/gtc/matrix_transform.hpp>
#include <gal/opengl/opengl_compositor.h>
#ifdef __WINDOWS__
#include <GL/glew.h> // must be included before gl.h
#endif
#include <3d_viewer.h>
#include <3d_canvas.h>
#include <info3d_visu.h>
#include <trackball.h>
#include <3d_draw_basic_functions.h>
#include <CImage.h>
#include <reporter.h>
/* 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
*/
static 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 )
{
SetReportAll( true );
SetReportWarnings( true );
SetReportErrors( true );
m_hasMessage = false;
}
REPORTER& Report( const wxString& aText )
{
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( !IsShown() )
return;
wxString err_messages;
WX_STRING_REPORTER errorReporter( &err_messages );
STATUS_TEXT_REPORTER activityReporter( Parent(), 0 );
errorReporter.SetReportAll( false );
errorReporter.SetReportWarnings( m_reportWarnings );
// 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
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_COMMENTS ) )
{
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 ) && m_glLists[GL_ID_GRID] )
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 );
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 );
ReportWarnings( false );
}
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::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
REPORTER* aErrorMessages, REPORTER* aActivity )
{
BOARD* pcb = GetBoard();
// If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas.
// If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas,
// but the calculation time is twice shorter.
bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES );
bool realistic_mode = isRealisticMode();
bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES );
// Number of segments to convert a circle to polygon
// Boost polygon (at least v1.57 and previous) in very rare cases crashes
// when using 16 segments to approximate a circle.
// So using 18 segments is a workaround to try to avoid these crashes
// ( We already used this trick in plot_board_layers.cpp,
// see PlotSolderMaskLayer() )
const int segcountforcircle = 18;
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2.0) );
const int segcountLowQuality = 12; // segments to draw a circle with low quality
// to reduce time calculations
// for holes and items which do not need
// a fine representation
double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) );
CPOLYGONS_LIST bufferPolys;
bufferPolys.reserve( 500000 ); // Reserve for large board: tracks mainly
// + zones when holes are removed from zones
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
allLayerHoles.reserve( 20000 );
// Build a polygon from edge cut items
wxString msg;
if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) )
{
if( aErrorMessages && aErrorMessages->ReportWarnings() )
{
*aErrorMessages << msg << wxT("\n") <<
_("Unable to calculate the board outlines.\n"
"Therefore use the board boundary box.") << wxT("\n\n");
}
}
CPOLYGONS_LIST bufferZonesPolys;
bufferZonesPolys.reserve( 300000 ); // Reserve for large board ( copper zones mainly )
// when holes are not removed from zones
CPOLYGONS_LIST currLayerHoles; // Contains holes for the current layer
bool throughHolesListBuilt = false; // flag to build the through hole polygon list only once
LSET cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount );
#if 1
LAYER_ID cu_seq[MAX_CU_LAYERS]; // preferred sequence, could have called CuStack()
// but I assume that's backwards
glNewList( aBoardList, GL_COMPILE );
for( unsigned i=0; i < DIM( cu_seq ); ++i )
cu_seq[i] = ToLAYER_ID( B_Cu - i );
for( LSEQ cu = cu_set.Seq( cu_seq, DIM(cu_seq) ); cu; ++cu )
#else
for( LSEQ cu = cu_set.CuStack(); cu; ++cu )
#endif
{
LAYER_ID layer = *cu;
// Skip non enabled layers in normal mode,
// and internal layers in realistic mode
if( !is3DLayerEnabled( layer ) )
continue;
if( aActivity )
aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) );
bufferPolys.RemoveAllContours();
bufferZonesPolys.RemoveAllContours();
currLayerHoles.RemoveAllContours();
// Draw tracks:
for( TRACK* track = pcb->m_Track; track; track = track->Next() )
{
if( !track->IsOnLayer( layer ) )
continue;
track->TransformShapeWithClearanceToPolygon( bufferPolys,
0, segcountforcircle,
correctionFactor );
// Add via hole
if( track->Type() == PCB_VIA_T )
{
VIA *via = static_cast<VIA*>( track );
VIATYPE_T viatype = via->GetViaType();
int holediameter = via->GetDrillValue();
int thickness = GetPrm3DVisu().GetCopperThicknessBIU();
int hole_outer_radius = (holediameter + thickness) / 2;
if( viatype != VIA_THROUGH )
TransformCircleToPolygon( currLayerHoles,
via->GetStart(), hole_outer_radius,
segcountLowQuality );
else if( !throughHolesListBuilt )
TransformCircleToPolygon( allLayerHoles,
via->GetStart(), hole_outer_radius,
segcountLowQuality );
}
}
// draw pads
for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
{
int thickness = GetPrm3DVisu().GetCopperThicknessBIU();
// Note: NPTH pads are not drawn on copper layers when the pad
// has same shape as its hole
module->TransformPadsShapesWithClearanceToPolygon( layer,
bufferPolys,
0,
segcountforcircle,
correctionFactor, true );
// Micro-wave modules may have items on copper layers
module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
bufferPolys,
0,
segcountforcircle,
correctionFactor );
// Add pad hole, if any
if( !throughHolesListBuilt )
{
D_PAD* pad = module->Pads();
for( ; pad; pad = pad->Next() )
{
// Calculate a factor to apply to segcount for large holes ( > 1 mm)
// (bigger pad drill size -> more segments) because holes in pads can have
// very different sizes and optimizing this segcount gives a better look
// Mainly mounting holes have a size bigger thon 1 mm
wxSize padHole = pad->GetDrillSize();
if( ! padHole.x ) // Not drilled pad like SMD pad
continue;
// we use the hole diameter to calculate the seg count.
// for round holes, padHole.x == padHole.y
// for oblong holes, the diameter is the smaller of (padHole.x, padHole.y)
int diam = std::min( padHole.x, padHole.y );
double segFactor = (double)diam / Millimeter2iu( 1.0 );
int segcount = (int)(segcountLowQuality * segFactor);
// Clamp segcount between segcountLowQuality and 48.
// 48 segm for a circle is a very good approx.
segcount = Clamp( segcountLowQuality, segcount, 48 );
// The hole in the body is inflated by copper thickness.
int inflate = thickness;
// If not plated, no copper.
if( pad->GetAttribute () == PAD_HOLE_NOT_PLATED )
inflate = 0;
pad->BuildPadDrillShapePolygon( allLayerHoles, inflate, segcount );
}
}
}
// Draw copper zones. Note:
// * if the holes are removed from copper zones
// the polygons are stored in bufferPolys (which contains all other polygons)
// * if the holes are NOT removed from copper zones
// the polygons are stored in bufferZonesPolys
if( isEnabled( FL_ZONE ) )
{
for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
{
ZONE_CONTAINER* zone = pcb->GetArea( ii );
LAYER_NUM zonelayer = zone->GetLayer();
if( zonelayer == layer )
{
zone->TransformSolidAreasShapesToPolygonSet(
remove_Holes ? bufferPolys : bufferZonesPolys,
segcountLowQuality, correctionFactorLQ );
}
}
}
// draw graphic items on copper layers (texts)
for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
{
if( !item->IsOnLayer( layer ) )
continue;
switch( item->Type() )
{
case PCB_LINE_T: // should not exist on copper layers
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
bufferPolys, 0, segcountforcircle, correctionFactor );
break;
case PCB_TEXT_T:
( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
bufferPolys, 0, segcountforcircle, correctionFactor );
break;
default:
break;
}
}
// bufferPolys contains polygons to merge. Many overlaps .
// Calculate merged polygons
if( bufferPolys.GetCornersCount() == 0 )
continue;
KI_POLYGON_SET currLayerPolyset;
KI_POLYGON_SET polysetHoles;
// Add polygons, without holes
bufferPolys.ExportTo( currLayerPolyset );
// Add through holes (created only once) in current polygon holes list
currLayerHoles.Append( allLayerHoles );
if( currLayerHoles.GetCornersCount() > 0 )
currLayerHoles.ExportTo( polysetHoles );
// Merge polygons, and remove holes
currLayerPolyset -= polysetHoles;
int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );
float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)
// If we are not using thickness, then the znormal must face the layer direction
// because it will draw just one plane
if( !thickness )
zNormal = Get3DLayer_Z_Orientation( layer );
if( realistic_mode )
{
setGLCopperColor();
}
else
{
EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer );
SetGLColor( color );
}
bufferPolys.RemoveAllContours();
bufferPolys.ImportFrom( currLayerPolyset );
// If holes are removed from copper zones, bufferPolys contains all polygons
// to draw (tracks+zones+texts).
Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness,
GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
zNormal );
// If holes are not removed from copper zones (for calculation time reasons,
// the zone polygons are stored in bufferZonesPolys and have to be drawn now:
if( bufferZonesPolys.GetCornersCount() )
{
Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness,
GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
zNormal );
}
throughHolesListBuilt = true;
}
if( aActivity )
aActivity->Report( _( "Build board body" ) );
// Draw plated vertical holes inside the board, but not always. They are drawn:
// - if the board body is not shown, to show the holes.
// - or if the copper thickness is shown
if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) )
{
// Draw vias holes (vertical cylinders)
for( const TRACK* track = pcb->m_Track; track; track = track->Next() )
{
if( track->Type() == PCB_VIA_T )
{
const VIA *via = static_cast<const VIA*>(track);
draw3DViaHole( via );
}
}
// Draw pads holes (vertical cylinders)
for( const MODULE* module = pcb->m_Modules; module; module = module->Next() )
{
for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
if( pad->GetAttribute () != PAD_HOLE_NOT_PLATED )
draw3DPadHole( pad );
}
}
glEndList();
// Build the body board:
glNewList( aBodyOnlyList, GL_COMPILE );
if( isRealisticMode() )
{
setGLEpoxyColor( 1.00 );
}
else
{
EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts );
SetGLColor( color, 0.7 );
}
float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU();
// a small offset between substrate and external copper layer to avoid artifacts
// when drawing copper items on board
float epsilon = Millimeter2iu( 0.01 );
float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu )
- GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
// items on copper layers and having a thickness = copper_thickness
// are drawn from zpos - copper_thickness/2 to zpos + copper_thickness
// therefore substrate position is copper_thickness/2 to
// substrate_height - copper_thickness/2
zpos += (copper_thickness + epsilon) / 2.0f;
board_thickness -= copper_thickness + epsilon;
KI_POLYGON_SET currLayerPolyset;
KI_POLYGON_SET polysetHoles;
// Add polygons, without holes
bufferPcbOutlines.ExportTo( currLayerPolyset );
// Build holes list
allLayerHoles.ExportTo( polysetHoles );
// remove holes
currLayerPolyset -= polysetHoles;
bufferPcbOutlines.RemoveAllContours();
bufferPcbOutlines.ImportFrom( currLayerPolyset );
if( bufferPcbOutlines.GetCornersCount() )
{
Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0,
board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
1.0f );
}
glEndList();
}
void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* aActivity )
{
BOARD* pcb = GetBoard();
bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES );
// Number of segments to draw a circle using segments
const int segcountforcircle = 18;
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
const int segcountLowQuality = 12; // segments to draw a circle with low quality
// to reduce time calculations
// for holes and items which do not need
// a fine representation
double correctionFactorLQ = 1.0 / cos( M_PI / (segcountLowQuality * 2) );
CPOLYGONS_LIST bufferPolys;
bufferPolys.reserve( 100000 ); // Reserve for large board
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
allLayerHoles.reserve( 20000 );
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
// Build a polygon from edge cut items
wxString msg;
if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) )
{
if( aErrorMessages && aErrorMessages->ReportWarnings() )
{
*aErrorMessages << msg << wxT("\n") <<
_("Unable to calculate the board outlines.\n"
"Therefore use the board boundary box.") << wxT("\n\n");
}
}
int thickness = GetPrm3DVisu().GetCopperThicknessBIU();
// Add via holes
for( VIA* via = GetFirstVia( pcb->m_Track ); via;
via = GetFirstVia( via->Next() ) )
{
VIATYPE_T viatype = via->GetViaType();
int holediameter = via->GetDrillValue();
int hole_outer_radius = (holediameter + thickness) / 2;
if( viatype == VIA_THROUGH )
TransformCircleToPolygon( allLayerHoles,
via->GetStart(), hole_outer_radius,
segcountLowQuality );
}
// draw pads holes
for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
{
// Add pad hole, if any
D_PAD* pad = module->Pads();
for( ; pad; pad = pad->Next() )
pad->BuildPadDrillShapePolygon( allLayerHoles, 0,
segcountLowQuality );
}
// draw graphic items, on technical layers
KI_POLYGON_SET brdpolysetHoles;
allLayerHoles.ExportTo( brdpolysetHoles );
static const LAYER_ID teckLayerList[] = {
B_Adhes,
F_Adhes,
B_Paste,
F_Paste,
B_SilkS,
F_SilkS,
B_Mask,
F_Mask,
};
// User layers are not drawn here, only technical layers
for( LSEQ seq = LSET::AllTechMask().Seq( teckLayerList, DIM( teckLayerList ) ); seq; ++seq )
{
LAYER_ID layer = *seq;
if( !is3DLayerEnabled( layer ) )
continue;
if( layer == Edge_Cuts && isEnabled( FL_SHOW_BOARD_BODY ) )
continue;
if( aActivity )
aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) );
bufferPolys.RemoveAllContours();
for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
{
if( !item->IsOnLayer( layer ) )
continue;
switch( item->Type() )
{
case PCB_LINE_T:
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
bufferPolys, 0, segcountforcircle, correctionFactor );
break;
case PCB_TEXT_T:
( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
bufferPolys, 0, segcountforcircle, correctionFactor );
break;
default:
break;
}
}
for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
{
if( layer == F_SilkS || layer == B_SilkS )
{
D_PAD* pad = module->Pads();
int linewidth = g_DrawDefaultLineThickness;
for( ; pad; pad = pad->Next() )
{
if( !pad->IsOnLayer( layer ) )
continue;
buildPadShapeThickOutlineAsPolygon( pad, bufferPolys,
linewidth, segcountforcircle, correctionFactor );
}
}
else
module->TransformPadsShapesWithClearanceToPolygon( layer,
bufferPolys, 0, segcountforcircle, correctionFactor );
module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
bufferPolys, 0, segcountforcircle, correctionFactor );
}
// Draw non copper zones
if( isEnabled( FL_ZONE ) )
{
for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
{
ZONE_CONTAINER* zone = pcb->GetArea( ii );
if( !zone->IsOnLayer( layer ) )
continue;
zone->TransformSolidAreasShapesToPolygonSet(
bufferPolys, segcountLowQuality, correctionFactorLQ );
}
}
// bufferPolys contains polygons to merge. Many overlaps .
// Calculate merged polygons and remove pads and vias holes
if( bufferPolys.GetCornersCount() == 0 )
continue;
KI_POLYGON_SET currLayerPolyset;
KI_POLYGON_SET polyset;
// Solder mask layers are "negative" layers.
// Shapes should be removed from the full board area.
if( layer == B_Mask || layer == F_Mask )
{
bufferPcbOutlines.ExportTo( currLayerPolyset );
bufferPolys.Append( allLayerHoles );
bufferPolys.ExportTo( polyset );
currLayerPolyset -= polyset;
}
// Remove holes from Solder paste layers and siklscreen
else if( layer == B_Paste || layer == F_Paste
|| layer == B_SilkS || layer == F_SilkS )
{
bufferPolys.ExportTo( currLayerPolyset );
currLayerPolyset -= brdpolysetHoles;
}
else // usuall layers, merge polys built from each item shape:
{
bufferPolys.ExportTo( polyset );
currLayerPolyset += polyset;
}
int thickness = 0;
if( layer != B_Mask && layer != F_Mask )
thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );
if( layer == Edge_Cuts )
{
thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu )
- GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu )
+ (thickness / 2);
}
else
{
// for Draw3D_SolidHorizontalPolyPolygons, zpos it the middle between bottom and top
// sides.
// However for top layers, zpos should be the bottom layer pos,
// and for bottom layers, zpos should be the top layer pos.
if( Get3DLayer_Z_Orientation( layer ) > 0 )
zpos += thickness/2;
else
zpos -= thickness/2 ;
}
bufferPolys.RemoveAllContours();
bufferPolys.ImportFrom( currLayerPolyset );
float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)
// If we are not using thickness, then the znormal must face the layer direction
// because it will draw just one plane
if( !thickness )
zNormal = Get3DLayer_Z_Orientation( layer );
setGLTechLayersColor( layer );
Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos,
thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
zNormal );
}
}
/**
* Function buildBoard3DAuxLayers
* Called by CreateDrawGL_List()
* Fills the OpenGL GL_ID_BOARD draw list with items
* on aux layers only
*/
void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* aActivity )
{
const int segcountforcircle = 18;
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
BOARD* pcb = GetBoard();
CPOLYGONS_LIST bufferPolys;
bufferPolys.reserve( 5000 ); // Reserve for items not on board
static const LAYER_ID sequence[] = {
Dwgs_User,
Cmts_User,
Eco1_User,
Eco2_User,
Edge_Cuts,
Margin
};
for( LSEQ aux( sequence, sequence+DIM(sequence) ); aux; ++aux )
{
LAYER_ID layer = *aux;
if( !is3DLayerEnabled( layer ) )
continue;
if( aActivity )
aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) );
bufferPolys.RemoveAllContours();
for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
{
if( !item->IsOnLayer( layer ) )
continue;
switch( item->Type() )
{
case PCB_LINE_T:
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
bufferPolys, 0, segcountforcircle, correctionFactor );
break;
case PCB_TEXT_T:
( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
bufferPolys, 0, segcountforcircle, correctionFactor );
break;
default:
break;
}
}
for( MODULE* module = pcb->m_Modules; module; module = module->Next() )
{
module->TransformPadsShapesWithClearanceToPolygon( layer,
bufferPolys,
0,
segcountforcircle,
correctionFactor );
module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
bufferPolys,
0,
segcountforcircle,
correctionFactor );
}
// bufferPolys contains polygons to merge. Many overlaps .
// Calculate merged polygons and remove pads and vias holes
if( bufferPolys.GetCornersCount() == 0 )
continue;
KI_POLYGON_SET currLayerPolyset;
KI_POLYGON_SET polyset;
bufferPolys.ExportTo( polyset );
currLayerPolyset += polyset;
int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );
// for Draw3D_SolidHorizontalPolyPolygons,
// zpos it the middle between bottom and top sides.
// However for top layers, zpos should be the bottom layer pos,
// and for bottom layers, zpos should be the top layer pos.
if( Get3DLayer_Z_Orientation( layer ) > 0 )
zpos += thickness/2;
else
zpos -= thickness/2 ;
bufferPolys.RemoveAllContours();
bufferPolys.ImportFrom( currLayerPolyset );
float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)
// If we are not using thickness, then the znormal must face the layer direction
// because it will draw just one plane
if( !thickness )
zNormal = Get3DLayer_Z_Orientation( layer );
setGLTechLayersColor( layer );
Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos,
thickness, GetPrm3DVisu().m_BiuTo3Dunits, false,
zNormal );
}
}
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
bool report_warn = aErrorMessages->ReportWarnings();
aErrorMessages->SetReportWarnings( false );
buildTechLayers3DView( aErrorMessages, aActivity );
aErrorMessages->SetReportWarnings( report_warn );
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 );
}
GLfloat Get3DLayer_Z_Orientation( LAYER_NUM aLayer )
{
double nZ = 1.0;
if( ( aLayer == B_Cu )
|| ( aLayer == B_Adhes )
|| ( aLayer == B_Paste )
|| ( aLayer == B_SilkS )
|| ( aLayer == B_Mask ) )
nZ = -1.0;
return nZ;
}