KiCad PCB EDA Suite
opengl/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 <[email protected]>
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_opengl.h"
26 #include "opengl_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, float aZtop,
39  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_OPENGL::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  float aZtop, float aZbot )
117 {
118  const SFVEC2F& center = aRing->GetCenter();
119  const float inner = aRing->GetInnerRadius();
120  const float outer = aRing->GetOuterRadius();
121 
122  std::vector< SFVEC2F > innerContour;
123  std::vector< SFVEC2F > outerContour;
124 
125  generateRing( center, inner, outer, m_boardAdapter.GetCircleSegmentCount( outer * 2.0f ),
126  innerContour, outerContour, false );
127 
128  // This will add the top and bot quads that will form the approximated ring
129  for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
130  {
131  const SFVEC2F& vi0 = innerContour[i + 0];
132  const SFVEC2F& vi1 = innerContour[i + 1];
133  const SFVEC2F& vo0 = outerContour[i + 0];
134  const SFVEC2F& vo1 = outerContour[i + 1];
135 
136  aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
137  SFVEC3F( vi0.x, vi0.y, aZtop ),
138  SFVEC3F( vo0.x, vo0.y, aZtop ),
139  SFVEC3F( vo1.x, vo1.y, aZtop ) );
140 
141  aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
142  SFVEC3F( vo1.x, vo1.y, aZbot ),
143  SFVEC3F( vo0.x, vo0.y, aZbot ),
144  SFVEC3F( vi0.x, vi0.y, aZbot ) );
145  }
146 }
147 
148 
150  TRIANGLE_DISPLAY_LIST* aDstLayer,
151  float aZtop, float aZbot )
152 {
153  const SFVEC2F& v1 = aTri->GetP1();
154  const SFVEC2F& v2 = aTri->GetP2();
155  const SFVEC2F& v3 = aTri->GetP3();
156 
157  addTopAndBottomTriangles( aDstLayer, v1, v2, v3, aZtop, aZbot );
158 }
159 
160 
162  TRIANGLE_DISPLAY_LIST* aDstLayer,
163  float aZtop, float aZbot )
164 {
165  const SFVEC2F& leftStart = aSeg->GetLeftStar();
166  const SFVEC2F& leftEnd = aSeg->GetLeftEnd();
167  const SFVEC2F& leftDir = aSeg->GetLeftDir();
168 
169  const SFVEC2F& rightStart = aSeg->GetRightStar();
170  const SFVEC2F& rightEnd = aSeg->GetRightEnd();
171  const SFVEC2F& rightDir = aSeg->GetRightDir();
172  const float radius = aSeg->GetRadius();
173 
174  const SFVEC2F& start = aSeg->GetStart();
175  const SFVEC2F& end = aSeg->GetEnd();
176 
177  const float texture_factor = ( 12.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
178  const float texture_factorF = ( 6.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
179 
180  const float radius_of_the_square = sqrtf( aSeg->GetRadiusSquared() * 2.0f );
181  const float radius_triangle_factor = ( radius_of_the_square - radius ) / radius;
182 
183  const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor,
184  rightDir.x * radius * radius_triangle_factor );
185 
186  const SFVEC2F factorE = SFVEC2F( -leftDir.y * radius * radius_triangle_factor,
187  leftDir.x * radius * radius_triangle_factor );
188 
189  // Top end segment triangles (semi-circles)
191  SFVEC3F( rightEnd.x + texture_factor * factorS.x,
192  rightEnd.y + texture_factor * factorS.y,
193  aZtop ),
194  SFVEC3F( leftStart.x + texture_factor * factorE.x,
195  leftStart.y + texture_factor * factorE.y,
196  aZtop ),
197  SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
198  start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
199  aZtop ) );
200 
202  SFVEC3F( leftEnd.x + texture_factor * factorE.x,
203  leftEnd.y + texture_factor * factorE.y, aZtop ),
204  SFVEC3F( rightStart.x + texture_factor * factorS.x,
205  rightStart.y + texture_factor * factorS.y, aZtop ),
206  SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
207  end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
208  aZtop ) );
209 
210  // Bot end segment triangles (semi-circles)
212  SFVEC3F( leftStart.x + texture_factor * factorE.x,
213  leftStart.y + texture_factor * factorE.y,
214  aZbot ),
215  SFVEC3F( rightEnd.x + texture_factor * factorS.x,
216  rightEnd.y + texture_factor * factorS.y,
217  aZbot ),
218  SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
219  start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
220  aZbot ) );
221 
223  SFVEC3F( rightStart.x + texture_factor * factorS.x,
224  rightStart.y + texture_factor * factorS.y, aZbot ),
225  SFVEC3F( leftEnd.x + texture_factor * factorE.x,
226  leftEnd.y + texture_factor * factorE.y, aZbot ),
227  SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
228  end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
229  aZbot ) );
230 
231  // Segment top and bot planes
232  aDstLayer->m_layer_top_triangles->AddQuad(
233  SFVEC3F( rightEnd.x, rightEnd.y, aZtop ),
234  SFVEC3F( rightStart.x, rightStart.y, aZtop ),
235  SFVEC3F( leftEnd.x, leftEnd.y, aZtop ),
236  SFVEC3F( leftStart.x, leftStart.y, aZtop ) );
237 
238  aDstLayer->m_layer_bot_triangles->AddQuad(
239  SFVEC3F( rightEnd.x, rightEnd.y, aZbot ),
240  SFVEC3F( leftStart.x, leftStart.y, aZbot ),
241  SFVEC3F( leftEnd.x, leftEnd.y, aZbot ),
242  SFVEC3F( rightStart.x, rightStart.y, aZbot ) );
243 }
244 
245 
247  const SHAPE_POLY_SET& aPoly, float aZtop,
248  float aZbot, bool aInvertFaces,
249  const BVH_CONTAINER_2D* aThroughHoles )
250 {
251  OPENGL_RENDER_LIST* ret = nullptr;
252 
253  if( aListHolesObject2d.size() > 0 )
254  {
255  TRIANGLE_DISPLAY_LIST* layerTriangles =
256  new TRIANGLE_DISPLAY_LIST( aListHolesObject2d.size() * 2 );
257 
258  // Convert the list of objects(filled circles) to triangle layer structure
259  for( const OBJECT_2D* itemOnLayer : aListHolesObject2d )
260  {
261  const OBJECT_2D* object2d_A = itemOnLayer;
262 
263  wxASSERT( ( object2d_A->GetObjectType() == OBJECT_2D_TYPE::FILLED_CIRCLE )
264  || ( object2d_A->GetObjectType() == OBJECT_2D_TYPE::ROUNDSEG ) );
265 
266  switch( object2d_A->GetObjectType() )
267  {
269  addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d_A ),
270  layerTriangles, aZtop, aZbot );
271  break;
272 
274  addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d_A ),
275  layerTriangles, aZtop, aZbot );
276  break;
277 
278  default:
279  wxFAIL_MSG( wxT( "RENDER_3D_OPENGL::generateHoles: Object type not implemented" ) );
280  break;
281  }
282  }
283 
284  // Note: he can have a aListHolesObject2d with holes but without contours
285  // eg: when there are only NPTH on the list and the contours were not added
286  if( aPoly.OutlineCount() > 0 )
287  {
288  layerTriangles->AddToMiddleContourns( aPoly, aZbot, aZtop,
290  aInvertFaces, aThroughHoles );
291  }
292 
293  ret = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, aZbot, aZtop );
294 
295  delete layerTriangles;
296  }
297 
298  return ret;
299 }
300 
301 
303  const SHAPE_POLY_SET* aPolyList,
304  PCB_LAYER_ID aLayerId,
305  const BVH_CONTAINER_2D* aThroughHoles )
306 {
307  if( aContainer == nullptr )
308  return nullptr;
309 
310  const LIST_OBJECT2D& listObject2d = aContainer->GetList();
311 
312  if( listObject2d.size() == 0 )
313  return nullptr;
314 
315  float layer_z_bot = 0.0f;
316  float layer_z_top = 0.0f;
317 
318  getLayerZPos( aLayerId, layer_z_top, layer_z_bot );
319 
320  // Calculate an estimation for the nr of triangles based on the nr of objects
321  unsigned int nrTrianglesEstimation = listObject2d.size() * 8;
322 
323  TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( nrTrianglesEstimation );
324 
325  // store in a list so it will be latter deleted
326  m_triangles.push_back( layerTriangles );
327 
328  // Load the 2D (X,Y axis) component of shapes
329  for( const OBJECT_2D* itemOnLayer : listObject2d )
330  {
331  const OBJECT_2D* object2d_A = itemOnLayer;
332 
333  switch( object2d_A->GetObjectType() )
334  {
336  addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d_A ),
337  layerTriangles, layer_z_top, layer_z_bot );
338  break;
339 
341  addObjectTriangles( static_cast<const POLYGON_4PT_2D*>( object2d_A ),
342  layerTriangles, layer_z_top, layer_z_bot );
343  break;
344 
346  addObjectTriangles( static_cast<const RING_2D*>( object2d_A ),
347  layerTriangles, layer_z_top, layer_z_bot );
348  break;
349 
351  addObjectTriangles( static_cast<const TRIANGLE_2D*>( object2d_A ),
352  layerTriangles, layer_z_top, layer_z_bot );
353  break;
354 
356  addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d_A ),
357  layerTriangles, layer_z_top, layer_z_bot );
358  break;
359 
360  default:
361  wxFAIL_MSG( wxT( "RENDER_3D_OPENGL: Object type is not implemented" ) );
362  break;
363  }
364  }
365 
366  if( aPolyList && aPolyList->OutlineCount() > 0 )
367  {
368  layerTriangles->AddToMiddleContourns( *aPolyList, layer_z_bot, layer_z_top,
369  m_boardAdapter.BiuTo3dUnits(), false, aThroughHoles );
370  }
371 
372  // Create display list
373  return new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, layer_z_bot, layer_z_top );
374 }
375 
376 
378 {
379  float layer_z_bot = 0.0f;
380  float layer_z_top = 0.0f;
381 
382  getLayerZPos( aLayerId, layer_z_top, layer_z_bot );
383 
384  TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( 1 );
385 
386  // store in a list so it will be latter deleted
387  m_triangles.push_back( layerTriangles );
388 
389  return new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, layer_z_bot, layer_z_top );
390 }
391 
392 
394  const BVH_CONTAINER_2D* aThroughHoles )
395 {
396  OPENGL_RENDER_LIST* dispLists = nullptr;
397  CONTAINER_2D boardContainer;
398  SHAPE_POLY_SET brd_outlines = aBoardPoly;
399 
400  ConvertPolygonToTriangles( brd_outlines, boardContainer, m_boardAdapter.BiuTo3dUnits(),
401  (const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
402 
403  const LIST_OBJECT2D& listBoardObject2d = boardContainer.GetList();
404 
405  if( listBoardObject2d.size() > 0 )
406  {
407  // We will set a unitary Z so it will in future used with transformations since the
408  // board poly will be used not only to draw itself but also the solder mask layers.
409  const float layer_z_top = 1.0f;
410  const float layer_z_bot = 0.0f;
411 
412  TRIANGLE_DISPLAY_LIST* layerTriangles =
413  new TRIANGLE_DISPLAY_LIST( listBoardObject2d.size() );
414 
415  // Convert the list of objects(triangles) to triangle layer structure
416  for( const OBJECT_2D* itemOnLayer : listBoardObject2d )
417  {
418  const OBJECT_2D* object2d_A = itemOnLayer;
419 
420  wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
421 
422  const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
423 
424  const SFVEC2F& v1 = tri->GetP1();
425  const SFVEC2F& v2 = tri->GetP2();
426  const SFVEC2F& v3 = tri->GetP3();
427 
428  addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
429  }
430 
431  if( aBoardPoly.OutlineCount() > 0 )
432  {
433  layerTriangles->AddToMiddleContourns( aBoardPoly, layer_z_bot, layer_z_top,
434  m_boardAdapter.BiuTo3dUnits(), false,
435  aThroughHoles );
436 
437  dispLists = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
438  layer_z_top, layer_z_top );
439  }
440 
441  delete layerTriangles;
442  }
443 
444  return dispLists;
445 }
446 
447 
448 void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
449 {
450  m_reloadRequested = false;
451 
452  freeAllLists();
453 
455 
456  unsigned stats_startReloadTime = GetRunningMicroSecs();
457 
458  m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
459 
460  SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
461  m_camera.SetBoardLookAtPos( camera_pos );
462 
463  if( aStatusReporter )
464  aStatusReporter->Report( _( "Load OpenGL: board" ) );
465 
466  // Create Board
468 
470  {
473  m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, -INT_MAX/2 ) );
474  m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, -INT_MAX/2 ) );
475  m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, INT_MAX/2 ) );
476  m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, INT_MAX/2 ) );
477  m_antiBoardPolys.Outline( 0 ).SetClosed( true );
478 
482  }
483 
484  SHAPE_POLY_SET board_poly_with_holes = m_boardAdapter.GetBoardPoly();
485  board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetThroughHoleOdPolys(),
489 
490  m_boardWithHoles = createBoard( board_poly_with_holes );
491 
492  if( m_antiBoard )
494 
495  // Create Through Holes and vias
496  if( aStatusReporter )
497  aStatusReporter->Report( _( "Load OpenGL: holes and vias" ) );
498 
500 
504 
506  outerPolyTHT, 1.0f, 0.0f, false,
508 
511  m_boardAdapter.GetThroughHoleViaOdPolys(), 1.0f, 0.0f, false );
512 
515  {
518  m_boardAdapter.GetThroughHoleAnnularRingPolys(), 1.0f, 0.0f, false );
519  }
520 
521  const MAP_POLY& innerMapHoles = m_boardAdapter.GetHoleIdPolysMap();
522  const MAP_POLY& outerMapHoles = m_boardAdapter.GetHoleOdPolysMap();
523 
524  wxASSERT( innerMapHoles.size() == outerMapHoles.size() );
525 
527 
528  if( outerMapHoles.size() > 0 )
529  {
530  float layer_z_bot = 0.0f;
531  float layer_z_top = 0.0f;
532 
533  for( const auto ii : outerMapHoles )
534  {
535  const PCB_LAYER_ID layer_id = ii.first;
536  const SHAPE_POLY_SET* poly = ii.second;
537  const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
538 
539  getLayerZPos( layer_id, layer_z_top, layer_z_bot );
540 
541  m_outerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
542  layer_z_top, layer_z_bot, false );
543  }
544 
545  for( const auto ii : innerMapHoles )
546  {
547  const PCB_LAYER_ID layer_id = ii.first;
548  const SHAPE_POLY_SET* poly = ii.second;
549  const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
550 
551  getLayerZPos( layer_id, layer_z_top, layer_z_bot );
552 
553  m_innerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
554  layer_z_top, layer_z_bot, false );
555  }
556  }
557 
558  // Generate vertical cylinders of vias and pads (copper)
560 
561  // Add layers maps
562  if( aStatusReporter )
563  aStatusReporter->Report( _( "Load OpenGL: layers" ) );
564 
565  const MAP_POLY& map_poly = m_boardAdapter.GetPolyMap();
566 
567  for( const auto ii : m_boardAdapter.GetLayerMap() )
568  {
569  const PCB_LAYER_ID layer_id = ii.first;
570 
571  if( !m_boardAdapter.Is3dLayerEnabled( layer_id ) )
572  continue;
573 
574  if( aStatusReporter )
575  aStatusReporter->Report( wxString::Format(
576  _( "Load OpenGL layer %d" ), (int)layer_id ) );
577 
578  const BVH_CONTAINER_2D* container2d = ii.second;
579 
580  SHAPE_POLY_SET polyListSubtracted;
581  SHAPE_POLY_SET* polyList = nullptr;
582 
583  // Load the vertical (Z axis) component of shapes
584 
585  if( map_poly.find( layer_id ) != map_poly.end() )
586  {
587  polyListSubtracted = *map_poly.at( layer_id );;
588 
590  {
591  polyListSubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly(),
593 
594  if( ( layer_id != B_Mask ) && ( layer_id != F_Mask ) )
595  {
600  }
601 
603  {
604  if( layer_id == B_SilkS && map_poly.find( B_Mask ) != map_poly.end() )
605  {
606  polyListSubtracted.BooleanSubtract( *map_poly.at( B_Mask ),
608  }
609  else if( layer_id == F_SilkS && map_poly.find( F_Mask ) != map_poly.end() )
610  {
611  polyListSubtracted.BooleanSubtract( *map_poly.at( F_Mask ),
613  }
614  }
615  }
616 
617  polyList = &polyListSubtracted;
618  }
619 
620  OPENGL_RENDER_LIST* oglList = generateLayerList( container2d, polyList, layer_id,
622 
623  if( oglList != nullptr )
624  m_layers[layer_id] = oglList;
625 
626  }
627 
630  {
632  {
640 
642  &polySubtracted, F_Cu );
643 
644  // An entry for F_Cu must exist in m_layers or we'll never look at m_platedPadsFront
645  if( m_layers.count( F_Cu ) == 0 )
647  }
648 
650  {
658 
660  &polySubtracted, B_Cu );
661 
662  // An entry for B_Cu must exist in m_layers or we'll never look at m_platedPadsBack
663  if( m_layers.count( B_Cu ) == 0 )
665  }
666  }
667 
668  // Load 3D models
669  if( aStatusReporter )
670  aStatusReporter->Report( _( "Loading 3D models..." ) );
671 
672  load3dModels( aStatusReporter );
673 
674  if( aStatusReporter )
675  {
676  // Calculation time in seconds
677  const double calculation_time = (double)( GetRunningMicroSecs() -
678  stats_startReloadTime) / 1e6;
679 
680  aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
681  }
682 }
683 
684 
686  const SFVEC2F& v1, const SFVEC2F& v2, float top,
687  float bot )
688 {
689  aDst->m_layer_bot_triangles->AddTriangle( SFVEC3F( v0.x, v0.y, bot ),
690  SFVEC3F( v1.x, v1.y, bot ),
691  SFVEC3F( v2.x, v2.y, bot ) );
692 
693  aDst->m_layer_top_triangles->AddTriangle( SFVEC3F( v2.x, v2.y, top ),
694  SFVEC3F( v1.x, v1.y, top ),
695  SFVEC3F( v0.x, v0.y, top ) );
696 }
697 
698 
699 void RENDER_3D_OPENGL::getLayerZPos( PCB_LAYER_ID aLayerID, float& aOutZtop, float& aOutZbot ) const
700 {
701  aOutZbot = m_boardAdapter.GetLayerBottomZPos( aLayerID );
702  aOutZtop = m_boardAdapter.GetLayerTopZPos( aLayerID );
703 
704  if( aOutZtop < aOutZbot )
705  {
706  float tmpFloat = aOutZbot;
707  aOutZbot = aOutZtop;
708  aOutZtop = tmpFloat;
709  }
710 }
711 
712 
713 void RENDER_3D_OPENGL::generateCylinder( const SFVEC2F& aCenter, float aInnerRadius,
714  float aOuterRadius, float aZtop, float aZbot,
715  unsigned int aNr_sides_per_circle,
716  TRIANGLE_DISPLAY_LIST* aDstLayer )
717 {
718  std::vector< SFVEC2F > innerContour;
719  std::vector< SFVEC2F > outerContour;
720 
721  generateRing( aCenter, aInnerRadius, aOuterRadius, aNr_sides_per_circle, innerContour,
722  outerContour, false );
723 
724  for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
725  {
726  const SFVEC2F& vi0 = innerContour[i + 0];
727  const SFVEC2F& vi1 = innerContour[i + 1];
728  const SFVEC2F& vo0 = outerContour[i + 0];
729  const SFVEC2F& vo1 = outerContour[i + 1];
730 
731  aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
732  SFVEC3F( vi0.x, vi0.y, aZtop ),
733  SFVEC3F( vo0.x, vo0.y, aZtop ),
734  SFVEC3F( vo1.x, vo1.y, aZtop ) );
735 
736  aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
737  SFVEC3F( vo1.x, vo1.y, aZbot ),
738  SFVEC3F( vo0.x, vo0.y, aZbot ),
739  SFVEC3F( vi0.x, vi0.y, aZbot ) );
740  }
741 
742  aDstLayer->AddToMiddleContourns( outerContour, aZbot, aZtop, true );
743  aDstLayer->AddToMiddleContourns( innerContour, aZbot, aZtop, false );
744 }
745 
746 
748 {
749  if( !m_boardAdapter.GetBoard() )
750  return;
751 
752  const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
753  const float platingThickness3d = platingThickness * m_boardAdapter.BiuTo3dUnits();
754 
755  if( m_boardAdapter.GetViaCount() > 0 )
756  {
757  const unsigned int reserve_nr_triangles_estimation =
761 
762  TRIANGLE_DISPLAY_LIST* layerTriangleVIA =
763  new TRIANGLE_DISPLAY_LIST( reserve_nr_triangles_estimation );
764 
765  // Insert plated vertical holes inside the board
766 
767  // Insert vias holes (vertical cylinders)
768  for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
769  {
770  if( track->Type() == PCB_VIA_T )
771  {
772  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
773 
774  const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3dUnits();
775  const int nrSegments = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
776  const float hole_inner_radius = holediameter / 2.0f;
777 
778  const SFVEC2F via_center( via->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
779  -via->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
780 
781  PCB_LAYER_ID top_layer, bottom_layer;
782  via->LayerPair( &top_layer, &bottom_layer );
783 
784  float ztop, zbot, dummy;
785 
786  getLayerZPos( top_layer, ztop, dummy );
787  getLayerZPos( bottom_layer, dummy, zbot );
788 
789  wxASSERT( zbot < ztop );
790 
791  generateCylinder( via_center, hole_inner_radius,
792  hole_inner_radius + platingThickness3d,
793  ztop, zbot, nrSegments, layerTriangleVIA );
794  }
795  }
796 
797  m_vias = new OPENGL_RENDER_LIST( *layerTriangleVIA, 0, 0.0f, 0.0f );
798 
799  delete layerTriangleVIA;
800  }
801 
802 
803  if( m_boardAdapter.GetHoleCount() > 0 )
804  {
805  SHAPE_POLY_SET tht_outer_holes_poly; // Stores the outer poly of the copper holes (the pad)
806  SHAPE_POLY_SET tht_inner_holes_poly; // Stores the inner poly of the copper holes (the hole)
807 
808  tht_outer_holes_poly.RemoveAllContours();
809  tht_inner_holes_poly.RemoveAllContours();
810 
811  // Insert pads holes (vertical cylinders)
812  for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
813  {
814  for( const PAD* pad : footprint->Pads() )
815  {
816  if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
817  {
818  const wxSize drillsize = pad->GetDrillSize();
819  const bool hasHole = drillsize.x && drillsize.y;
820 
821  if( !hasHole )
822  continue;
823 
824  pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly,
825  platingThickness,
826  ARC_HIGH_DEF, ERROR_INSIDE );
827  pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, 0,
828  ARC_HIGH_DEF, ERROR_INSIDE );
829  }
830  }
831  }
832 
833  // Subtract the holes
834  tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly, SHAPE_POLY_SET::PM_FAST );
835 
838 
839  CONTAINER_2D holesContainer;
840 
841  ConvertPolygonToTriangles( tht_outer_holes_poly, holesContainer,
843  (const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
844 
845  const LIST_OBJECT2D& listHolesObject2d = holesContainer.GetList();
846 
847  if( listHolesObject2d.size() > 0 )
848  {
849  float layer_z_top, layer_z_bot, dummy;
850 
851  getLayerZPos( F_Cu, layer_z_top, dummy );
852  getLayerZPos( B_Cu, dummy, layer_z_bot );
853 
854  TRIANGLE_DISPLAY_LIST* layerTriangles =
855  new TRIANGLE_DISPLAY_LIST( listHolesObject2d.size() );
856 
857  // Convert the list of objects(triangles) to triangle layer structure
858  for( const OBJECT_2D* itemOnLayer : listHolesObject2d )
859  {
860  const OBJECT_2D* object2d_A = itemOnLayer;
861 
862  wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
863 
864  const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
865 
866  const SFVEC2F& v1 = tri->GetP1();
867  const SFVEC2F& v2 = tri->GetP2();
868  const SFVEC2F& v3 = tri->GetP3();
869 
870  addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
871  }
872 
873  wxASSERT( tht_outer_holes_poly.OutlineCount() > 0 );
874 
875  if( tht_outer_holes_poly.OutlineCount() > 0 )
876  {
877  layerTriangles->AddToMiddleContourns( tht_outer_holes_poly,
878  layer_z_bot, layer_z_top,
879  m_boardAdapter.BiuTo3dUnits(), false );
880 
881  m_padHoles = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
882  layer_z_top, layer_z_top );
883  }
884 
885  delete layerTriangles;
886  }
887  }
888 }
889 
890 
892 {
893  if( m_3dModelMap.size() > 0 )
894  return;
895 
896  wxFrame* frame = dynamic_cast<EDA_3D_VIEWER_FRAME*>( m_canvas->GetParent() );
897 
898  if( frame )
899  {
900  STATUSBAR_REPORTER activityReporter( frame->GetStatusBar(),
902  load3dModels( &activityReporter );
903  }
904  else
905  load3dModels( nullptr );
906 }
907 
908 
909 void RENDER_3D_OPENGL::load3dModels( REPORTER* aStatusReporter )
910 {
911  if( !m_boardAdapter.GetBoard() )
912  return;
913 
917  {
918  return;
919  }
920 
921  // Go for all footprints
922  for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
923  {
924  for( const FP_3DMODEL& model : footprint->Models() )
925  {
926  if( model.m_Show && !model.m_Filename.empty() )
927  {
928  if( aStatusReporter )
929  {
930  // Display the short filename of the 3D model loaded:
931  // (the full name is usually too long to be displayed)
932  wxFileName fn( model.m_Filename );
933  aStatusReporter->Report( wxString::Format( _( "Loading %s..." ),
934  fn.GetFullName() ) );
935  }
936 
937  // Check if the model is not present in our cache map
938  // (Not already loaded in memory)
939  if( m_3dModelMap.find( model.m_Filename ) == m_3dModelMap.end() )
940  {
941  // It is not present, try get it from cache
942  const S3DMODEL* modelPtr =
943  m_boardAdapter.Get3dCacheManager()->GetModel( model.m_Filename );
944 
945  // only add it if the return is not NULL
946  if( modelPtr )
947  {
949  MODEL_3D* ogl_model = new MODEL_3D( *modelPtr, materialMode );
950 
951  if( ogl_model )
952  m_3dModelMap[ model.m_Filename ] = ogl_model;
953  }
954  }
955  }
956  }
957  }
958 }
float GetRadius() const
A wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:286
OPENGL_RENDER_LIST * m_platedPadsBack
OPENGL_RENDER_LIST * m_antiBoard
bool GetFlag(DISPLAY3D_FLG aFlag) const
Get a configuration status of a flag.
void SetBoardLookAtPos(const SFVEC3F &aBoardPos)
Definition: camera.h:112
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.
OPENGL_RENDER_LIST * m_board
int OutlineCount() const
Return the number of vertices in a given outline/hole.
void addTopAndBottomTriangles(TRIANGLE_DISPLAY_LIST *aDst, const SFVEC2F &v0, const SFVEC2F &v1, const SFVEC2F &v2, float top, float bot)
const SFVEC2F & GetCenter() const
void Load3dModelsIfNeeded()
Load footprint models if they are not already loaded, i.e.
void load3dModels(REPORTER *aStatusReporter)
Load footprint models from the cache and load it to openGL lists in the form of MODEL_3D objects.
const SFVEC2F & GetP1() const
Definition: triangle_2d.h:44
Store arrays of triangles to be used to create display lists.
float GetRadiusSquared() const
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
OPENGL_RENDER_LIST * m_outerViaThroughHoles
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
OPENGL_RENDER_LIST * m_boardWithHoles
const SHAPE_POLY_SET & GetOuterNonPlatedThroughHolePoly() const noexcept
EDA_3D_CANVAS * m_canvas
Settings reference in use for this render.
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.
LIST_TRIANGLES m_triangles
store pointers so can be deleted latter
MAP_OGL_DISP_LISTS m_innerLayerHoles
OPENGL_RENDER_LIST * generateEmptyLayerList(PCB_LAYER_ID aLayerId)
const MAP_CONTAINER_2D_BASE & GetLayerMap() const noexcept
Get the map of containers that have the objects per layer.
void getLayerZPos(PCB_LAYER_ID aLayerID, float &aOutZtop, float &aOutZbot) const
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.
MAP_OGL_DISP_LISTS m_layers
OPENGL_RENDER_LIST * m_outerThroughHoles
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
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
SHAPE_POLY_SET m_antiBoardPolys
The negative polygon representation of the board outline.
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
const SHAPE_POLY_SET * GetBackPlatedPadPolys()
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:645
const BVH_CONTAINER_2D & GetThroughHoleIds() const noexcept
Get the through hole inner diameter container.
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.
#define SIZE_OF_CIRCLE_TEXTURE
OPENGL_RENDER_LIST * m_vias
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
like PAD_PTH, but not plated
void reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter)
const BVH_CONTAINER_2D * GetPlatedPadsBack() const noexcept
const SFVEC2F & GetV2() const
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
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)
S3D_CACHE * Get3dCacheManager() const noexcept
Return the 3D cache manager pointer.
Definition: board_adapter.h:88
const SFVEC2F & GetLeftEnd() const
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.
MAP_OGL_DISP_LISTS m_outerLayerHoles
const SFVEC2F & GetEnd() const
const SFVEC2F & GetLeftStar() const
FOOTPRINTS & Footprints()
Definition: board.h:234
float GetLayerBottomZPos(PCB_LAYER_ID aLayerId) const noexcept
Get the bottom z position.
MATERIAL_MODE GetMaterialMode() const noexcept
OPENGL_RENDER_LIST * m_outerThroughHoleRings
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)
const BOARD * GetBoard() const noexcept
Get current board to be rendered.
const MAP_POLY & GetHoleIdPolysMap() const noexcept
#define _(s)
const SFVEC2F & GetRightDir() const
OPENGL_RENDER_LIST * createBoard(const SHAPE_POLY_SET &aBoardPoly, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
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
OPENGL_RENDER_LIST * m_padHoles
OBJECT_2D_TYPE GetObjectType() const
Definition: object_2d.h:107
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 * m_platedPadsFront
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.
void addObjectTriangles(const RING_2D *aRing, TRIANGLE_DISPLAY_LIST *aDstLayer, float aZtop, float aZbot)
unsigned int GetViaCount() const noexcept
Get number of vias in this board.
void generateCylinder(const SFVEC2F &aCenter, float aInnerRadius, float aOuterRadius, float aZtop, float aZbot, unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST *aDstLayer)
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 elapsed 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)
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 * generateHoles(const LIST_OBJECT2D &aListHolesObject2d, const SHAPE_POLY_SET &aPoly, float aZtop, float aZbot, bool aInvertFaces, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
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
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.
MAP_3DMODEL m_3dModelMap
const SFVEC2F & GetCenter() const
Definition: ring_2d.h:46
const SFVEC2F & GetStart() const
unsigned int GetCircleSegmentCount(float aDiameter3DU) const
const SFVEC2F & GetV0() const
TRACKS & Tracks()
Definition: board.h:231
TRIANGLE_LIST * m_layer_top_segment_ends
OPENGL_RENDER_LIST * generateLayerList(const BVH_CONTAINER_2D *aContainer, const SHAPE_POLY_SET *aPolyList, PCB_LAYER_ID aLayerId, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
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