KiCad PCB EDA Suite
legacy/create_scene.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
5  * Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include "render_3d_legacy.h"
26 #include "ogl_legacy_utils.h"
27 #include <board.h>
28 #include <footprint.h>
29 #include "../../3d_math.h"
30 #include <trigo.h>
31 #include <project.h>
32 #include <profile.h> // To use GetRunningMicroSecs or another profiling utility
33 #include <eda_3d_canvas.h>
34 #include <eda_3d_viewer_frame.h>
35 
36 
38  TRIANGLE_DISPLAY_LIST* aDstLayer,
39  float aZtop, float aZbot )
40 {
41  const SFVEC2F& center = aFilledCircle->GetCenter();
42  const float radius = aFilledCircle->GetRadius() * 2.0f; // Double because the render triangle
43 
44  // This is a small adjustment to the circle texture
45  const float texture_factor = ( 8.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
46  const float f = ( sqrtf( 2.0f ) / 2.0f ) * radius * texture_factor;
47 
48  // Top and Bot segments ends are just triangle semi-circles, so need to add it in duplicated.
49  aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZtop ),
50  SFVEC3F( center.x - f, center.y, aZtop ),
51  SFVEC3F( center.x, center.y - f, aZtop ) );
52 
53  aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZtop ),
54  SFVEC3F( center.x + f, center.y, aZtop ),
55  SFVEC3F( center.x, center.y + f, aZtop ) );
56 
57  aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZbot ),
58  SFVEC3F( center.x + f, center.y, aZbot ),
59  SFVEC3F( center.x, center.y - f, aZbot ) );
60 
61  aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZbot ),
62  SFVEC3F( center.x - f, center.y, aZbot ),
63  SFVEC3F( center.x, center.y + f, aZbot ) );
64 }
65 
66 
68  TRIANGLE_DISPLAY_LIST* aDstLayer,
69  float aZtop, float aZbot )
70 {
71  const SFVEC2F& v0 = aPoly->GetV0();
72  const SFVEC2F& v1 = aPoly->GetV1();
73  const SFVEC2F& v2 = aPoly->GetV2();
74  const SFVEC2F& v3 = aPoly->GetV3();
75 
76  addTopAndBottomTriangles( aDstLayer, v0, v2, v1, aZtop, aZbot );
77  addTopAndBottomTriangles( aDstLayer, v2, v0, v3, aZtop, aZbot );
78 }
79 
80 
81 void RENDER_3D_LEGACY::generateRing( const SFVEC2F& aCenter, float aInnerRadius,
82  float aOuterRadius, unsigned int aNr_sides_per_circle,
83  std::vector< SFVEC2F >& aInnerContourResult,
84  std::vector< SFVEC2F >& aOuterContourResult,
85  bool aInvertOrder )
86 {
87  aInnerContourResult.clear();
88  aInnerContourResult.reserve( aNr_sides_per_circle + 2 );
89 
90  aOuterContourResult.clear();
91  aOuterContourResult.reserve( aNr_sides_per_circle + 2 );
92 
93  const int delta = 3600 / aNr_sides_per_circle;
94 
95  for( int ii = 0; ii < 3600; ii += delta )
96  {
97  float angle = (float)( aInvertOrder ? ( 3600 - ii ) : ii )
98  * 2.0f * glm::pi<float>() / 3600.0f;
99  const SFVEC2F rotatedDir = SFVEC2F( cos( angle ), sin( angle ) );
100 
101  aInnerContourResult.emplace_back( aCenter.x + rotatedDir.x * aInnerRadius,
102  aCenter.y + rotatedDir.y * aInnerRadius );
103 
104  aOuterContourResult.emplace_back( aCenter.x + rotatedDir.x * aOuterRadius,
105  aCenter.y + rotatedDir.y * aOuterRadius );
106  }
107 
108  aInnerContourResult.push_back( aInnerContourResult[0] );
109  aOuterContourResult.push_back( aOuterContourResult[0] );
110 
111  wxASSERT( aInnerContourResult.size() == aOuterContourResult.size() );
112 }
113 
114 
116  TRIANGLE_DISPLAY_LIST* aDstLayer,
117  float aZtop, float aZbot )
118 {
119  const SFVEC2F& center = aRing->GetCenter();
120  const float inner = aRing->GetInnerRadius();
121  const float outer = aRing->GetOuterRadius();
122 
123  std::vector< SFVEC2F > innerContour;
124  std::vector< SFVEC2F > outerContour;
125 
126  generateRing( center, inner, outer, m_boardAdapter.GetCircleSegmentCount( outer * 2.0f ),
127  innerContour, outerContour, false );
128 
129  // This will add the top and bot quads that will form the approximated ring
130  for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
131  {
132  const SFVEC2F& vi0 = innerContour[i + 0];
133  const SFVEC2F& vi1 = innerContour[i + 1];
134  const SFVEC2F& vo0 = outerContour[i + 0];
135  const SFVEC2F& vo1 = outerContour[i + 1];
136 
137  aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
138  SFVEC3F( vi0.x, vi0.y, aZtop ),
139  SFVEC3F( vo0.x, vo0.y, aZtop ),
140  SFVEC3F( vo1.x, vo1.y, aZtop ) );
141 
142  aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
143  SFVEC3F( vo1.x, vo1.y, aZbot ),
144  SFVEC3F( vo0.x, vo0.y, aZbot ),
145  SFVEC3F( vi0.x, vi0.y, aZbot ) );
146  }
147 }
148 
149 
151  TRIANGLE_DISPLAY_LIST* aDstLayer,
152  float aZtop, float aZbot )
153 {
154  const SFVEC2F& v1 = aTri->GetP1();
155  const SFVEC2F& v2 = aTri->GetP2();
156  const SFVEC2F& v3 = aTri->GetP3();
157 
158  addTopAndBottomTriangles( aDstLayer, v1, v2, v3, aZtop, aZbot );
159 }
160 
161 
163  TRIANGLE_DISPLAY_LIST* aDstLayer,
164  float aZtop, float aZbot )
165 {
166  const SFVEC2F& leftStart = aSeg->GetLeftStar();
167  const SFVEC2F& leftEnd = aSeg->GetLeftEnd();
168  const SFVEC2F& leftDir = aSeg->GetLeftDir();
169 
170  const SFVEC2F& rightStart = aSeg->GetRightStar();
171  const SFVEC2F& rightEnd = aSeg->GetRightEnd();
172  const SFVEC2F& rightDir = aSeg->GetRightDir();
173  const float radius = aSeg->GetRadius();
174 
175  const SFVEC2F& start = aSeg->GetStart();
176  const SFVEC2F& end = aSeg->GetEnd();
177 
178  const float texture_factor = ( 12.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
179  const float texture_factorF = ( 6.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
180 
181  const float radius_of_the_square = sqrtf( aSeg->GetRadiusSquared() * 2.0f );
182  const float radius_triangle_factor = ( radius_of_the_square - radius ) / radius;
183 
184  const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor,
185  rightDir.x * radius * radius_triangle_factor );
186 
187  const SFVEC2F factorE = SFVEC2F( -leftDir.y * radius * radius_triangle_factor,
188  leftDir.x * radius * radius_triangle_factor );
189 
190  // Top end segment triangles (semi-circles)
192  SFVEC3F( rightEnd.x + texture_factor * factorS.x,
193  rightEnd.y + texture_factor * factorS.y,
194  aZtop ),
195  SFVEC3F( leftStart.x + texture_factor * factorE.x,
196  leftStart.y + texture_factor * factorE.y,
197  aZtop ),
198  SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
199  start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
200  aZtop ) );
201 
203  SFVEC3F( leftEnd.x + texture_factor * factorE.x,
204  leftEnd.y + texture_factor * factorE.y, aZtop ),
205  SFVEC3F( rightStart.x + texture_factor * factorS.x,
206  rightStart.y + texture_factor * factorS.y, aZtop ),
207  SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
208  end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
209  aZtop ) );
210 
211  // Bot end segment triangles (semi-circles)
213  SFVEC3F( leftStart.x + texture_factor * factorE.x,
214  leftStart.y + texture_factor * factorE.y,
215  aZbot ),
216  SFVEC3F( rightEnd.x + texture_factor * factorS.x,
217  rightEnd.y + texture_factor * factorS.y,
218  aZbot ),
219  SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
220  start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
221  aZbot ) );
222 
224  SFVEC3F( rightStart.x + texture_factor * factorS.x,
225  rightStart.y + texture_factor * factorS.y, aZbot ),
226  SFVEC3F( leftEnd.x + texture_factor * factorE.x,
227  leftEnd.y + texture_factor * factorE.y, aZbot ),
228  SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
229  end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
230  aZbot ) );
231 
232  // Segment top and bot planes
233  aDstLayer->m_layer_top_triangles->AddQuad(
234  SFVEC3F( rightEnd.x, rightEnd.y, aZtop ),
235  SFVEC3F( rightStart.x, rightStart.y, aZtop ),
236  SFVEC3F( leftEnd.x, leftEnd.y, aZtop ),
237  SFVEC3F( leftStart.x, leftStart.y, aZtop ) );
238 
239  aDstLayer->m_layer_bot_triangles->AddQuad(
240  SFVEC3F( rightEnd.x, rightEnd.y, aZbot ),
241  SFVEC3F( leftStart.x, leftStart.y, aZbot ),
242  SFVEC3F( leftEnd.x, leftEnd.y, aZbot ),
243  SFVEC3F( rightStart.x, rightStart.y, aZbot ) );
244 }
245 
246 
248  const SHAPE_POLY_SET& aPoly, float aZtop,
249  float aZbot, bool aInvertFaces,
250  const BVH_CONTAINER_2D* aThroughHoles )
251 {
252  OPENGL_RENDER_LIST* ret = nullptr;
253 
254  if( aListHolesObject2d.size() > 0 )
255  {
256  TRIANGLE_DISPLAY_LIST* layerTriangles =
257  new TRIANGLE_DISPLAY_LIST( aListHolesObject2d.size() * 2 );
258 
259  // Convert the list of objects(filled circles) to triangle layer structure
260  for( const OBJECT_2D* itemOnLayer : aListHolesObject2d )
261  {
262  const OBJECT_2D* object2d_A = itemOnLayer;
263 
264  wxASSERT( ( object2d_A->GetObjectType() == OBJECT_2D_TYPE::FILLED_CIRCLE )
265  || ( object2d_A->GetObjectType() == OBJECT_2D_TYPE::ROUNDSEG ) );
266 
267  switch( object2d_A->GetObjectType() )
268  {
270  addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d_A ),
271  layerTriangles, aZtop, aZbot );
272  break;
273 
275  addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d_A ),
276  layerTriangles, aZtop, aZbot );
277  break;
278 
279  default:
280  wxFAIL_MSG( "RENDER_3D_LEGACY::generateHoles: Object type is not implemented" );
281  break;
282  }
283  }
284 
285  // Note: he can have a aListHolesObject2d with holes but without contours
286  // eg: when there are only NPTH on the list and the contours were not added
287  if( aPoly.OutlineCount() > 0 )
288  {
289  layerTriangles->AddToMiddleContourns( aPoly, aZbot, aZtop,
291  aInvertFaces, aThroughHoles );
292  }
293 
294  ret = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, aZbot, aZtop );
295 
296  delete layerTriangles;
297  }
298 
299  return ret;
300 }
301 
302 
304  const SHAPE_POLY_SET* aPolyList,
305  PCB_LAYER_ID aLayerId,
306  const BVH_CONTAINER_2D* aThroughHoles )
307 {
308  if( aContainer == nullptr )
309  return nullptr;
310 
311  const LIST_OBJECT2D& listObject2d = aContainer->GetList();
312 
313  if( listObject2d.size() == 0 )
314  return nullptr;
315 
316  float layer_z_bot = 0.0f;
317  float layer_z_top = 0.0f;
318 
319  getLayerZPos( aLayerId, layer_z_top, layer_z_bot );
320 
321  // Calculate an estimation for the nr of triangles based on the nr of objects
322  unsigned int nrTrianglesEstimation = listObject2d.size() * 8;
323 
324  TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( nrTrianglesEstimation );
325 
326  // store in a list so it will be latter deleted
327  m_triangles.push_back( layerTriangles );
328 
329  // Load the 2D (X,Y axis) component of shapes
330  for( const OBJECT_2D* itemOnLayer : listObject2d )
331  {
332  const OBJECT_2D* object2d_A = itemOnLayer;
333 
334  switch( object2d_A->GetObjectType() )
335  {
337  addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d_A ),
338  layerTriangles, layer_z_top, layer_z_bot );
339  break;
340 
342  addObjectTriangles( static_cast<const POLYGON_4PT_2D*>( object2d_A ),
343  layerTriangles, layer_z_top, layer_z_bot );
344  break;
345 
347  addObjectTriangles( static_cast<const RING_2D*>( object2d_A ),
348  layerTriangles, layer_z_top, layer_z_bot );
349  break;
350 
352  addObjectTriangles( static_cast<const TRIANGLE_2D*>( object2d_A ),
353  layerTriangles, layer_z_top, layer_z_bot );
354  break;
355 
357  addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d_A ),
358  layerTriangles, layer_z_top, layer_z_bot );
359  break;
360 
361  default:
362  wxFAIL_MSG( "RENDER_3D_LEGACY: Object type is not implemented" );
363  break;
364  }
365  }
366 
367  if( aPolyList && aPolyList->OutlineCount() > 0 )
368  {
369  layerTriangles->AddToMiddleContourns( *aPolyList, layer_z_bot, layer_z_top,
370  m_boardAdapter.BiuTo3dUnits(), false, aThroughHoles );
371  }
372 
373  // Create display list
374  return new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, layer_z_bot, layer_z_top );
375 }
376 
377 
379  const BVH_CONTAINER_2D* aThroughHoles )
380 {
381  OPENGL_RENDER_LIST* dispLists = nullptr;
382  CONTAINER_2D boardContainer;
383  SHAPE_POLY_SET brd_outlines = aBoardPoly;
384 
385  ConvertPolygonToTriangles( brd_outlines, boardContainer, m_boardAdapter.BiuTo3dUnits(),
386  (const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
387 
388  const LIST_OBJECT2D& listBoardObject2d = boardContainer.GetList();
389 
390  if( listBoardObject2d.size() > 0 )
391  {
392  // We will set a unitary Z so it will in future used with transformations
393  // since the board poly will be used not only to draw itself but also the
394  // solder mask layers.
395  const float layer_z_top = 1.0f;
396  const float layer_z_bot = 0.0f;
397 
398  TRIANGLE_DISPLAY_LIST* layerTriangles =
399  new TRIANGLE_DISPLAY_LIST( listBoardObject2d.size() );
400 
401  // Convert the list of objects(triangles) to triangle layer structure
402  for( const OBJECT_2D* itemOnLayer : listBoardObject2d )
403  {
404  const OBJECT_2D* object2d_A = itemOnLayer;
405 
406  wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
407 
408  const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
409 
410  const SFVEC2F& v1 = tri->GetP1();
411  const SFVEC2F& v2 = tri->GetP2();
412  const SFVEC2F& v3 = tri->GetP3();
413 
414  addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
415  }
416 
417  if( aBoardPoly.OutlineCount() > 0 )
418  {
419  layerTriangles->AddToMiddleContourns( aBoardPoly, layer_z_bot, layer_z_top,
420  m_boardAdapter.BiuTo3dUnits(), false,
421  aThroughHoles );
422 
423  dispLists = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
424  layer_z_top, layer_z_top );
425  }
426 
427  delete layerTriangles;
428  }
429 
430  return dispLists;
431 }
432 
433 
434 void RENDER_3D_LEGACY::reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
435 {
436  m_reloadRequested = false;
437 
438  freeAllLists();
439 
441 
442  unsigned stats_startReloadTime = GetRunningMicroSecs();
443 
444  m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
445 
446  SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
447  m_camera.SetBoardLookAtPos( camera_pos );
448 
449  if( aStatusReporter )
450  aStatusReporter->Report( _( "Load OpenGL: board" ) );
451 
452  // Create Board
454 
456  {
459  m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, -INT_MAX/2 ) );
460  m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, -INT_MAX/2 ) );
461  m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, INT_MAX/2 ) );
462  m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, INT_MAX/2 ) );
463  m_antiBoardPolys.Outline( 0 ).SetClosed( true );
464 
468  }
469 
470  SHAPE_POLY_SET board_poly_with_holes = m_boardAdapter.GetBoardPoly();
471  board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetThroughHoleOdPolys(),
475 
476  m_boardWithHoles = createBoard( board_poly_with_holes );
477 
478  if( m_antiBoard )
480 
481  // Create Through Holes and vias
482  if( aStatusReporter )
483  aStatusReporter->Report( _( "Load OpenGL: holes and vias" ) );
484 
486 
490 
492  outerPolyTHT, 1.0f, 0.0f, false,
494 
497  m_boardAdapter.GetThroughHoleViaOdPolys(), 1.0f, 0.0f, false );
498 
501  {
504  m_boardAdapter.GetThroughHoleAnnularRingPolys(), 1.0f, 0.0f, false );
505  }
506 
507  const MAP_POLY& innerMapHoles = m_boardAdapter.GetHoleIdPolysMap();
508  const MAP_POLY& outerMapHoles = m_boardAdapter.GetHoleOdPolysMap();
509 
510  wxASSERT( innerMapHoles.size() == outerMapHoles.size() );
511 
513 
514  if( outerMapHoles.size() > 0 )
515  {
516  float layer_z_bot = 0.0f;
517  float layer_z_top = 0.0f;
518 
519  for( const auto ii : outerMapHoles )
520  {
521  const PCB_LAYER_ID layer_id = ii.first;
522  const SHAPE_POLY_SET* poly = ii.second;
523  const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
524 
525  getLayerZPos( layer_id, layer_z_top, layer_z_bot );
526 
527  m_outerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
528  layer_z_top, layer_z_bot, false );
529  }
530 
531  for( const auto ii : innerMapHoles )
532  {
533  const PCB_LAYER_ID layer_id = ii.first;
534  const SHAPE_POLY_SET* poly = ii.second;
535  const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
536 
537  getLayerZPos( layer_id, layer_z_top, layer_z_bot );
538 
539  m_innerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
540  layer_z_top, layer_z_bot, false );
541  }
542  }
543 
544  // Generate vertical cylinders of vias and pads (copper)
546 
547  // Add layers maps
548  if( aStatusReporter )
549  aStatusReporter->Report( _( "Load OpenGL: layers" ) );
550 
551  const MAP_POLY& map_poly = m_boardAdapter.GetPolyMap();
552 
553  for( const auto ii : m_boardAdapter.GetLayerMap() )
554  {
555  const PCB_LAYER_ID layer_id = ii.first;
556 
557  if( !m_boardAdapter.Is3dLayerEnabled( layer_id ) )
558  continue;
559 
560  if( aStatusReporter )
561  aStatusReporter->Report( wxString::Format(
562  _( "Load OpenGL layer %d" ), (int)layer_id ) );
563 
564  const BVH_CONTAINER_2D* container2d = ii.second;
565 
566  SHAPE_POLY_SET polyListSubtracted;
567  SHAPE_POLY_SET* aPolyList = nullptr;
568 
569  // Load the vertical (Z axis) component of shapes
570 
571  if( map_poly.find( layer_id ) != map_poly.end() )
572  {
573  polyListSubtracted = *map_poly.at( layer_id );;
574 
575  if( ( layer_id != B_Paste ) && ( layer_id != F_Paste ) &&
577  {
578  polyListSubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly(),
580 
581  if( ( layer_id != B_Mask ) && ( layer_id != F_Mask ) )
582  {
585  polyListSubtracted.BooleanSubtract(
588  }
589 
591  {
592  if( layer_id == B_SilkS && map_poly.find( B_Mask ) != map_poly.end() )
593  {
594  polyListSubtracted.BooleanSubtract( *map_poly.at( B_Mask ),
596  }
597  else if( layer_id == F_SilkS && map_poly.find( F_Mask ) != map_poly.end() )
598  {
599  polyListSubtracted.BooleanSubtract( *map_poly.at( F_Mask ),
601  }
602  }
603  }
604 
605  aPolyList = &polyListSubtracted;
606  }
607 
608  OPENGL_RENDER_LIST* oglList = generateLayerList( container2d, aPolyList, layer_id,
610 
611  if( oglList != nullptr )
612  m_layers[layer_id] = oglList;
613 
614  }
615 
618  {
620  {
628 
630  &polySubtracted, F_Cu );
631  }
632 
634  {
642 
644  &polySubtracted, B_Cu );
645  }
646  }
647 
648  // Load 3D models
649  if( aStatusReporter )
650  aStatusReporter->Report( _( "Loading 3D models..." ) );
651 
652  load3dModels( aStatusReporter );
653 
654  if( aStatusReporter )
655  {
656  // Calculation time in seconds
657  const double calculation_time = (double)( GetRunningMicroSecs() -
658  stats_startReloadTime) / 1e6;
659 
660  aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
661  }
662 }
663 
664 
666  const SFVEC2F& v1, const SFVEC2F& v2,
667  float top, float bot )
668 {
669  aDst->m_layer_bot_triangles->AddTriangle( SFVEC3F( v0.x, v0.y, bot ),
670  SFVEC3F( v1.x, v1.y, bot ),
671  SFVEC3F( v2.x, v2.y, bot ) );
672 
673  aDst->m_layer_top_triangles->AddTriangle( SFVEC3F( v2.x, v2.y, top ),
674  SFVEC3F( v1.x, v1.y, top ),
675  SFVEC3F( v0.x, v0.y, top ) );
676 }
677 
678 
679 void RENDER_3D_LEGACY::getLayerZPos( PCB_LAYER_ID aLayerID, float& aOutZtop,
680  float& aOutZbot ) const
681 {
682  aOutZbot = m_boardAdapter.GetLayerBottomZPos( aLayerID );
683  aOutZtop = m_boardAdapter.GetLayerTopZPos( aLayerID );
684 
685  if( aOutZtop < aOutZbot )
686  {
687  float tmpFloat = aOutZbot;
688  aOutZbot = aOutZtop;
689  aOutZtop = tmpFloat;
690  }
691 }
692 
693 
694 void RENDER_3D_LEGACY::generateCylinder( const SFVEC2F& aCenter, float aInnerRadius,
695  float aOuterRadius, float aZtop, float aZbot,
696  unsigned int aNr_sides_per_circle,
697  TRIANGLE_DISPLAY_LIST* aDstLayer )
698 {
699  std::vector< SFVEC2F > innerContour;
700  std::vector< SFVEC2F > outerContour;
701 
702  generateRing( aCenter, aInnerRadius, aOuterRadius, aNr_sides_per_circle, innerContour,
703  outerContour, false );
704 
705  for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
706  {
707  const SFVEC2F& vi0 = innerContour[i + 0];
708  const SFVEC2F& vi1 = innerContour[i + 1];
709  const SFVEC2F& vo0 = outerContour[i + 0];
710  const SFVEC2F& vo1 = outerContour[i + 1];
711 
712  aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
713  SFVEC3F( vi0.x, vi0.y, aZtop ),
714  SFVEC3F( vo0.x, vo0.y, aZtop ),
715  SFVEC3F( vo1.x, vo1.y, aZtop ) );
716 
717  aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
718  SFVEC3F( vo1.x, vo1.y, aZbot ),
719  SFVEC3F( vo0.x, vo0.y, aZbot ),
720  SFVEC3F( vi0.x, vi0.y, aZbot ) );
721  }
722 
723  aDstLayer->AddToMiddleContourns( outerContour, aZbot, aZtop, true );
724  aDstLayer->AddToMiddleContourns( innerContour, aZbot, aZtop, false );
725 }
726 
727 
729 {
730  if( !m_boardAdapter.GetBoard() )
731  return;
732 
733  const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
734  const float platingThickness3d = platingThickness * m_boardAdapter.BiuTo3dUnits();
735 
736  if( m_boardAdapter.GetViaCount() > 0 )
737  {
738  const unsigned int reserve_nr_triangles_estimation =
742 
743  TRIANGLE_DISPLAY_LIST* layerTriangleVIA =
744  new TRIANGLE_DISPLAY_LIST( reserve_nr_triangles_estimation );
745 
746  // Insert plated vertical holes inside the board
747 
748  // Insert vias holes (vertical cylinders)
749  for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
750  {
751  if( track->Type() == PCB_VIA_T )
752  {
753  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
754 
755  const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3dUnits();
756  const int nrSegments = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
757  const float hole_inner_radius = holediameter / 2.0f;
758 
759  const SFVEC2F via_center( via->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
760  -via->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
761 
762  PCB_LAYER_ID top_layer, bottom_layer;
763  via->LayerPair( &top_layer, &bottom_layer );
764 
765  float ztop, zbot, dummy;
766 
767  getLayerZPos( top_layer, ztop, dummy );
768  getLayerZPos( bottom_layer, dummy, zbot );
769 
770  wxASSERT( zbot < ztop );
771 
772  generateCylinder( via_center, hole_inner_radius,
773  hole_inner_radius + platingThickness3d,
774  ztop, zbot, nrSegments, layerTriangleVIA );
775  }
776  }
777 
778  m_vias = new OPENGL_RENDER_LIST( *layerTriangleVIA, 0, 0.0f, 0.0f );
779 
780  delete layerTriangleVIA;
781  }
782 
783 
784  if( m_boardAdapter.GetHoleCount() > 0 )
785  {
786  SHAPE_POLY_SET tht_outer_holes_poly; // Stores the outer poly of the copper holes (the pad)
787  SHAPE_POLY_SET tht_inner_holes_poly; // Stores the inner poly of the copper holes (the hole)
788 
789  tht_outer_holes_poly.RemoveAllContours();
790  tht_inner_holes_poly.RemoveAllContours();
791 
792  // Insert pads holes (vertical cylinders)
793  for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
794  {
795  for( const PAD* pad : footprint->Pads() )
796  {
797  if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
798  {
799  const wxSize drillsize = pad->GetDrillSize();
800  const bool hasHole = drillsize.x && drillsize.y;
801 
802  if( !hasHole )
803  continue;
804 
805  pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly,
806  platingThickness,
807  ARC_HIGH_DEF, ERROR_INSIDE );
808  pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, 0,
809  ARC_HIGH_DEF, ERROR_INSIDE );
810  }
811  }
812  }
813 
814  // Subtract the holes
815  tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly, SHAPE_POLY_SET::PM_FAST );
816 
819 
820  CONTAINER_2D holesContainer;
821 
822  ConvertPolygonToTriangles( tht_outer_holes_poly, holesContainer,
824  (const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
825 
826  const LIST_OBJECT2D& listHolesObject2d = holesContainer.GetList();
827 
828  if( listHolesObject2d.size() > 0 )
829  {
830  float layer_z_top, layer_z_bot, dummy;
831 
832  getLayerZPos( F_Cu, layer_z_top, dummy );
833  getLayerZPos( B_Cu, dummy, layer_z_bot );
834 
835  TRIANGLE_DISPLAY_LIST* layerTriangles =
836  new TRIANGLE_DISPLAY_LIST( listHolesObject2d.size() );
837 
838  // Convert the list of objects(triangles) to triangle layer structure
839  for( const OBJECT_2D* itemOnLayer : listHolesObject2d )
840  {
841  const OBJECT_2D* object2d_A = itemOnLayer;
842 
843  wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
844 
845  const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
846 
847  const SFVEC2F& v1 = tri->GetP1();
848  const SFVEC2F& v2 = tri->GetP2();
849  const SFVEC2F& v3 = tri->GetP3();
850 
851  addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
852  }
853 
854  wxASSERT( tht_outer_holes_poly.OutlineCount() > 0 );
855 
856  if( tht_outer_holes_poly.OutlineCount() > 0 )
857  {
858  layerTriangles->AddToMiddleContourns( tht_outer_holes_poly,
859  layer_z_bot, layer_z_top,
860  m_boardAdapter.BiuTo3dUnits(), false );
861 
862  m_padHoles = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
863  layer_z_top, layer_z_top );
864  }
865 
866  delete layerTriangles;
867  }
868  }
869 }
870 
871 
873 {
874  if( m_3dModelMap.size() > 0 )
875  return;
876 
877  wxFrame* frame = dynamic_cast<EDA_3D_VIEWER_FRAME*>( m_canvas->GetParent() );
878 
879  if( frame )
880  {
881  STATUSBAR_REPORTER activityReporter( frame->GetStatusBar(),
883  load3dModels( &activityReporter );
884  }
885  else
886  load3dModels( nullptr );
887 }
888 
889 
890 void RENDER_3D_LEGACY::load3dModels( REPORTER* aStatusReporter )
891 {
892  if( !m_boardAdapter.GetBoard() )
893  return;
894 
898  {
899  return;
900  }
901 
902  // Go for all footprints
903  for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
904  {
905  for( const FP_3DMODEL& model : footprint->Models() )
906  {
907  if( model.m_Show && !model.m_Filename.empty() )
908  {
909  if( aStatusReporter )
910  {
911  // Display the short filename of the 3D model loaded:
912  // (the full name is usually too long to be displayed)
913  wxFileName fn( model.m_Filename );
914  aStatusReporter->Report( wxString::Format( _( "Loading %s..." ),
915  fn.GetFullName() ) );
916  }
917 
918  // Check if the model is not present in our cache map
919  // (Not already loaded in memory)
920  if( m_3dModelMap.find( model.m_Filename ) == m_3dModelMap.end() )
921  {
922  // It is not present, try get it from cache
923  const S3DMODEL* modelPtr =
924  m_boardAdapter.Get3dCacheManager()->GetModel( model.m_Filename );
925 
926  // only add it if the return is not NULL
927  if( modelPtr )
928  {
930  MODEL_3D* ogl_model = new MODEL_3D( *modelPtr, materialMode );
931 
932  if( ogl_model )
933  m_3dModelMap[ model.m_Filename ] = ogl_model;
934  }
935  }
936  }
937  }
938  }
939 }
float GetRadius() const
A wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:286
bool GetFlag(DISPLAY3D_FLG aFlag) const
Get a configuration status of a flag.
void SetBoardLookAtPos(const SFVEC3F &aBoardPos)
Definition: camera.h:114
BOARD_ADAPTER & m_boardAdapter
const SFVEC2F & GetV1() const
void ConvertPolygonToTriangles(SHAPE_POLY_SET &aPolyList, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, const BOARD_ITEM &aBoardItem)
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
void reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter)
OPENGL_RENDER_LIST * createBoard(const SHAPE_POLY_SET &aBoardPoly, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
const SFVEC2F & GetCenter() const
const SFVEC2F & GetP1() const
Definition: triangle_2d.h:44
Store arrays of triangles to be used to create display lists.
float GetRadiusSquared() const
LIST_TRIANGLES m_triangles
store pointers so can be deleted latter
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
float GetInnerRadius() const
Definition: ring_2d.h:47
std::map< PCB_LAYER_ID, SHAPE_POLY_SET * > MAP_POLY
A type that stores polysets for each layer id.
Definition: board_adapter.h:57
float GetRadius() const
const SFVEC2F & GetP2() const
Definition: triangle_2d.h:45
const SHAPE_POLY_SET & GetOuterNonPlatedThroughHolePoly() const noexcept
EDA_3D_CANVAS * m_canvas
Settings reference in use for this render.
MAP_OGL_DISP_LISTS m_layers
const BVH_CONTAINER_2D & GetThroughHoleAnnularRings() const noexcept
Get the through hole annular rings container.
const SHAPE_POLY_SET & GetThroughHoleOdPolys() const noexcept
Get through hole outside diameter 2D polygons.
const MAP_CONTAINER_2D_BASE & GetLayerMap() const noexcept
Get the map of containers that have the objects per layer.
TRIANGLE_LIST * m_layer_top_triangles
void InitSettings(REPORTER *aStatusReporter, REPORTER *aWarningReporter)
Function to be called by the render when it need to reload the settings for the board.
OPENGL_RENDER_LIST * m_platedPadsFront
const SHAPE_POLY_SET & GetThroughHoleViaOdPolys() const noexcept
const SHAPE_POLY_SET * GetFrontPlatedPadPolys()
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:70
float GetOuterRadius() const
Definition: ring_2d.h:48
MAP_3DMODEL m_3dModelMap
void addTopAndBottomTriangles(TRIANGLE_DISPLAY_LIST *aDst, const SFVEC2F &v0, const SFVEC2F &v1, const SFVEC2F &v2, float top, float bot)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
void getLayerZPos(PCB_LAYER_ID aLayerID, float &aOutZtop, float &aOutZbot) const
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
void generateRing(const SFVEC2F &aCenter, float aInnerRadius, float aOuterRadius, unsigned int aNr_sides_per_circle, std::vector< SFVEC2F > &aInnerContourResult, std::vector< SFVEC2F > &aOuterContourResult, bool aInvertOrder)
void generateCylinder(const SFVEC2F &aCenter, float aInnerRadius, float aOuterRadius, float aZtop, float aZbot, unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST *aDstLayer)
const SHAPE_POLY_SET * GetBackPlatedPadPolys()
OPENGL_RENDER_LIST * m_outerThroughHoles
const SHAPE_POLY_SET & GetThroughHoleAnnularRingPolys() const noexcept
const LIST_OBJECT2D & GetList() const
Definition: container_2d.h:66
S3DMODEL * GetModel(const wxString &aModelFileName)
Attempt to load the scene data for a model and to translate it into an S3D_MODEL structure for displa...
Definition: 3d_cache.cpp:643
const BVH_CONTAINER_2D & GetThroughHoleIds() const noexcept
Get the through hole inner diameter container.
OPENGL_RENDER_LIST * m_outerThroughHoleRings
OPENGL_RENDER_LIST * m_boardWithHoles
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
bool Is3dLayerEnabled(PCB_LAYER_ID aLayer) const
Check if a layer is enabled.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
like PAD_PTH, but not plated
const BVH_CONTAINER_2D * GetPlatedPadsBack() const noexcept
#define SIZE_OF_CIRCLE_TEXTURE
const SFVEC2F & GetV2() const
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
S3D_CACHE * Get3dCacheManager() const noexcept
Return the 3D cache manager pointer.
Definition: board_adapter.h:88
const SFVEC2F & GetLeftEnd() const
MAP_OGL_DISP_LISTS m_outerLayerHoles
OPENGL_RENDER_LIST * m_antiBoard
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
float GetLayerTopZPos(PCB_LAYER_ID aLayerId) const noexcept
Get the top z position.
const SFVEC2F & GetEnd() const
const SFVEC2F & GetLeftStar() const
FOOTPRINTS & Footprints()
Definition: board.h:233
float GetLayerBottomZPos(PCB_LAYER_ID aLayerId) const noexcept
Get the bottom z position.
MATERIAL_MODE GetMaterialMode() const noexcept
static OBJECT_2D_STATS & Instance()
Definition: object_2d.h:137
double BiuTo3dUnits() const noexcept
Board integer units To 3D units.
void AddQuad(const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3, const SFVEC3F &aV4)
void AddTriangle(const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3)
OPENGL_RENDER_LIST * m_board
const BOARD * GetBoard() const noexcept
Get current board to be rendered.
const MAP_POLY & GetHoleIdPolysMap() const noexcept
void addObjectTriangles(const RING_2D *aRing, TRIANGLE_DISPLAY_LIST *aDstLayer, float aZtop, float aZbot)
#define _(s)
const SFVEC2F & GetRightDir() const
const BVH_CONTAINER_2D & GetThroughHoleOds() const noexcept
Get the inflated through hole outside diameters container.
const MAP_POLY & GetHoleOdPolysMap() const noexcept
void AddToMiddleContourns(const SHAPE_LINE_CHAIN &outlinePath, float zBot, float zTop, double aBiuTo3Du, bool aInvertFaceDirection, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
TRIANGLE_LIST * m_layer_bot_triangles
OBJECT_2D_TYPE GetObjectType() const
Definition: object_2d.h:107
OPENGL_RENDER_LIST * generateHoles(const LIST_OBJECT2D &aListHolesObject2d, const SHAPE_POLY_SET &aPoly, float aZtop, float aZbot, bool aInvertFaces, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
const SHAPE_POLY_SET & GetBoardPoly() const noexcept
Get the current polygon of the epoxy board.
MATERIAL_MODE
Render 3d model shape materials mode.
Definition: 3d_enums.h:118
std::map< PCB_LAYER_ID, BVH_CONTAINER_2D * > MAP_CONTAINER_2D_BASE
A type that stores a container of 2d objects for each layer id.
Definition: board_adapter.h:51
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union between a and b, store the result in it self For aFastMode meaning,...
int NewOutline()
Creates a new hole in a given outline.
OPENGL_RENDER_LIST * generateLayerList(const BVH_CONTAINER_2D *aContainer, const SHAPE_POLY_SET *aPolyList, PCB_LAYER_ID aLayerId, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
void load3dModels(REPORTER *aStatusReporter)
Load footprint models from the cache and load it to openGL lists in the form of MODEL_3D objects.
const MAP_POLY & GetPolyMap() const noexcept
Get map of polygon's layers.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
Store the OpenGL display lists to related with a layer.
bool m_reloadRequested
The window size that this camera is working.
Simple non-intersecting polygon with 4 points.
unsigned int GetViaCount() const noexcept
Get number of vias in this board.
void Load3dModelsIfNeeded()
Load footprint models if they are not already loaded, i.e.
unsigned int GetHoleCount() const noexcept
Get number of holes in this board.
const BVH_CONTAINER_2D * GetPlatedPadsFront() const noexcept
const SFVEC2F & GetLeftDir() const
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapset time (in microsecondes) to class PROF_COUNTER.
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
const BVH_CONTAINER_2D & GetThroughHoleViaOds() const noexcept
Definition: layer_ids.h:71
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
SHAPE_POLY_SET m_antiBoardPolys
The negative polygon representation of the board outline.
const SFVEC3F & GetBoardCenter() const noexcept
The board center position in 3D units.
const SFVEC2F & GetRightEnd() const
float GetAverageViaHoleDiameter() const noexcept
Thee average diameter of the via holes.
CAMERA & m_camera
Flag if the opengl specific for this render was already initialized.
void SetItIsTransparent(bool aSetTransparent)
constexpr int delta
std::list< OBJECT_2D * > LIST_OBJECT2D
Definition: container_2d.h:36
const SFVEC2F & GetV3() const
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
Store the a model based on meshes and materials.
Definition: c3dmodel.h:90
OPENGL_RENDER_LIST * m_platedPadsBack
MAP_OGL_DISP_LISTS m_innerLayerHoles
TRIANGLE_LIST * m_layer_bot_segment_ends
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
void ResetStats()
Definition: object_2d.h:122
Definition: pad.h:57
OPENGL_RENDER_LIST * m_vias
const SFVEC2F & GetP3() const
Definition: triangle_2d.h:46
const MAP_CONTAINER_2D_BASE & GetLayerHoleMap() const noexcept
Get the map of container that have the holes per layer.
const SFVEC2F & GetCenter() const
Definition: ring_2d.h:46
const SFVEC2F & GetStart() const
unsigned int GetCircleSegmentCount(float aDiameter3DU) const
OPENGL_RENDER_LIST * m_padHoles
const SFVEC2F & GetV0() const
TRACKS & Tracks()
Definition: board.h:230
TRIANGLE_LIST * m_layer_top_segment_ends
OPENGL_RENDER_LIST * m_outerViaThroughHoles
int GetHolePlatingThickness() const noexcept
Get the current copper layer thickness.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
const SFVEC2F & GetRightStar() const