kicad/3d-viewer/3d_draw.cpp

1217 lines
38 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>
extern bool useFastModeForPolygons;
/* 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( useFastModeForPolygons );
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 );
}