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( "RENDER_3D_OPENGL::generateHoles: Object type is 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( "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  const BVH_CONTAINER_2D* aThroughHoles )
379 {
380  OPENGL_RENDER_LIST* dispLists = nullptr;
381  CONTAINER_2D boardContainer;
382  SHAPE_POLY_SET brd_outlines = aBoardPoly;
383 
384  ConvertPolygonToTriangles( brd_outlines, boardContainer, m_boardAdapter.BiuTo3dUnits(),
385  (const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
386 
387  const LIST_OBJECT2D& listBoardObject2d = boardContainer.GetList();
388 
389  if( listBoardObject2d.size() > 0 )
390  {
391  // We will set a unitary Z so it will in future used with transformations
392  // since the board poly will be used not only to draw itself but also the
393  // solder mask layers.
394  const float layer_z_top = 1.0f;
395  const float layer_z_bot = 0.0f;
396 
397  TRIANGLE_DISPLAY_LIST* layerTriangles =
398  new TRIANGLE_DISPLAY_LIST( listBoardObject2d.size() );
399 
400  // Convert the list of objects(triangles) to triangle layer structure
401  for( const OBJECT_2D* itemOnLayer : listBoardObject2d )
402  {
403  const OBJECT_2D* object2d_A = itemOnLayer;
404 
405  wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
406 
407  const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
408 
409  const SFVEC2F& v1 = tri->GetP1();
410  const SFVEC2F& v2 = tri->GetP2();
411  const SFVEC2F& v3 = tri->GetP3();
412 
413  addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
414  }
415 
416  if( aBoardPoly.OutlineCount() > 0 )
417  {
418  layerTriangles->AddToMiddleContourns( aBoardPoly, layer_z_bot, layer_z_top,
419  m_boardAdapter.BiuTo3dUnits(), false,
420  aThroughHoles );
421 
422  dispLists = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
423  layer_z_top, layer_z_top );
424  }
425 
426  delete layerTriangles;
427  }
428 
429  return dispLists;
430 }
431 
432 
433 void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
434 {
435  m_reloadRequested = false;
436 
437  freeAllLists();
438 
440 
441  unsigned stats_startReloadTime = GetRunningMicroSecs();
442 
443  m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
444 
445  SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
446  m_camera.SetBoardLookAtPos( camera_pos );
447 
448  if( aStatusReporter )
449  aStatusReporter->Report( _( "Load OpenGL: board" ) );
450 
451  // Create Board
453 
455  {
458  m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, -INT_MAX/2 ) );
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.Outline( 0 ).SetClosed( true );
463 
467  }
468 
469  SHAPE_POLY_SET board_poly_with_holes = m_boardAdapter.GetBoardPoly();
470  board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetThroughHoleOdPolys(),
474 
475  m_boardWithHoles = createBoard( board_poly_with_holes );
476 
477  if( m_antiBoard )
479 
480  // Create Through Holes and vias
481  if( aStatusReporter )
482  aStatusReporter->Report( _( "Load OpenGL: holes and vias" ) );
483 
485 
489 
491  outerPolyTHT, 1.0f, 0.0f, false,
493 
496  m_boardAdapter.GetThroughHoleViaOdPolys(), 1.0f, 0.0f, false );
497 
500  {
503  m_boardAdapter.GetThroughHoleAnnularRingPolys(), 1.0f, 0.0f, false );
504  }
505 
506  const MAP_POLY& innerMapHoles = m_boardAdapter.GetHoleIdPolysMap();
507  const MAP_POLY& outerMapHoles = m_boardAdapter.GetHoleOdPolysMap();
508 
509  wxASSERT( innerMapHoles.size() == outerMapHoles.size() );
510 
512 
513  if( outerMapHoles.size() > 0 )
514  {
515  float layer_z_bot = 0.0f;
516  float layer_z_top = 0.0f;
517 
518  for( const auto ii : outerMapHoles )
519  {
520  const PCB_LAYER_ID layer_id = ii.first;
521  const SHAPE_POLY_SET* poly = ii.second;
522  const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
523 
524  getLayerZPos( layer_id, layer_z_top, layer_z_bot );
525 
526  m_outerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
527  layer_z_top, layer_z_bot, false );
528  }
529 
530  for( const auto ii : innerMapHoles )
531  {
532  const PCB_LAYER_ID layer_id = ii.first;
533  const SHAPE_POLY_SET* poly = ii.second;
534  const BVH_CONTAINER_2D* container = map_holes.at( layer_id );
535 
536  getLayerZPos( layer_id, layer_z_top, layer_z_bot );
537 
538  m_innerLayerHoles[layer_id] = generateHoles( container->GetList(), *poly,
539  layer_z_top, layer_z_bot, false );
540  }
541  }
542 
543  // Generate vertical cylinders of vias and pads (copper)
545 
546  // Add layers maps
547  if( aStatusReporter )
548  aStatusReporter->Report( _( "Load OpenGL: layers" ) );
549 
550  const MAP_POLY& map_poly = m_boardAdapter.GetPolyMap();
551 
552  for( const auto ii : m_boardAdapter.GetLayerMap() )
553  {
554  const PCB_LAYER_ID layer_id = ii.first;
555 
556  if( !m_boardAdapter.Is3dLayerEnabled( layer_id ) )
557  continue;
558 
559  if( aStatusReporter )
560  aStatusReporter->Report( wxString::Format(
561  _( "Load OpenGL layer %d" ), (int)layer_id ) );
562 
563  const BVH_CONTAINER_2D* container2d = ii.second;
564 
565  SHAPE_POLY_SET polyListSubtracted;
566  SHAPE_POLY_SET* aPolyList = nullptr;
567 
568  // Load the vertical (Z axis) component of shapes
569 
570  if( map_poly.find( layer_id ) != map_poly.end() )
571  {
572  polyListSubtracted = *map_poly.at( layer_id );;
573 
575  {
576  polyListSubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly(),
578 
579  if( ( layer_id != B_Mask ) && ( layer_id != F_Mask ) )
580  {
583  polyListSubtracted.BooleanSubtract(
586  }
587 
589  {
590  if( layer_id == B_SilkS && map_poly.find( B_Mask ) != map_poly.end() )
591  {
592  polyListSubtracted.BooleanSubtract( *map_poly.at( B_Mask ),
594  }
595  else if( layer_id == F_SilkS && map_poly.find( F_Mask ) != map_poly.end() )
596  {
597  polyListSubtracted.BooleanSubtract( *map_poly.at( F_Mask ),
599  }
600  }
601  }
602 
603  aPolyList = &polyListSubtracted;
604  }
605 
606  OPENGL_RENDER_LIST* oglList = generateLayerList( container2d, aPolyList, layer_id,
608 
609  if( oglList != nullptr )
610  m_layers[layer_id] = oglList;
611 
612  }
613 
616  {
618  {
626 
628  &polySubtracted, F_Cu );
629  }
630 
632  {
640 
642  &polySubtracted, B_Cu );
643  }
644  }
645 
646  // Load 3D models
647  if( aStatusReporter )
648  aStatusReporter->Report( _( "Loading 3D models..." ) );
649 
650  load3dModels( aStatusReporter );
651 
652  if( aStatusReporter )
653  {
654  // Calculation time in seconds
655  const double calculation_time = (double)( GetRunningMicroSecs() -
656  stats_startReloadTime) / 1e6;
657 
658  aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
659  }
660 }
661 
662 
664  const SFVEC2F& v1, const SFVEC2F& v2, float top,
665  float bot )
666 {
667  aDst->m_layer_bot_triangles->AddTriangle( SFVEC3F( v0.x, v0.y, bot ),
668  SFVEC3F( v1.x, v1.y, bot ),
669  SFVEC3F( v2.x, v2.y, bot ) );
670 
671  aDst->m_layer_top_triangles->AddTriangle( SFVEC3F( v2.x, v2.y, top ),
672  SFVEC3F( v1.x, v1.y, top ),
673  SFVEC3F( v0.x, v0.y, top ) );
674 }
675 
676 
677 void RENDER_3D_OPENGL::getLayerZPos( PCB_LAYER_ID aLayerID, float& aOutZtop, float& aOutZbot ) const
678 {
679  aOutZbot = m_boardAdapter.GetLayerBottomZPos( aLayerID );
680  aOutZtop = m_boardAdapter.GetLayerTopZPos( aLayerID );
681 
682  if( aOutZtop < aOutZbot )
683  {
684  float tmpFloat = aOutZbot;
685  aOutZbot = aOutZtop;
686  aOutZtop = tmpFloat;
687  }
688 }
689 
690 
691 void RENDER_3D_OPENGL::generateCylinder( const SFVEC2F& aCenter, float aInnerRadius,
692  float aOuterRadius, float aZtop, float aZbot,
693  unsigned int aNr_sides_per_circle,
694  TRIANGLE_DISPLAY_LIST* aDstLayer )
695 {
696  std::vector< SFVEC2F > innerContour;
697  std::vector< SFVEC2F > outerContour;
698 
699  generateRing( aCenter, aInnerRadius, aOuterRadius, aNr_sides_per_circle, innerContour,
700  outerContour, false );
701 
702  for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
703  {
704  const SFVEC2F& vi0 = innerContour[i + 0];
705  const SFVEC2F& vi1 = innerContour[i + 1];
706  const SFVEC2F& vo0 = outerContour[i + 0];
707  const SFVEC2F& vo1 = outerContour[i + 1];
708 
709  aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
710  SFVEC3F( vi0.x, vi0.y, aZtop ),
711  SFVEC3F( vo0.x, vo0.y, aZtop ),
712  SFVEC3F( vo1.x, vo1.y, aZtop ) );
713 
714  aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
715  SFVEC3F( vo1.x, vo1.y, aZbot ),
716  SFVEC3F( vo0.x, vo0.y, aZbot ),
717  SFVEC3F( vi0.x, vi0.y, aZbot ) );
718  }
719 
720  aDstLayer->AddToMiddleContourns( outerContour, aZbot, aZtop, true );
721  aDstLayer->AddToMiddleContourns( innerContour, aZbot, aZtop, false );
722 }
723 
724 
726 {
727  if( !m_boardAdapter.GetBoard() )
728  return;
729 
730  const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
731  const float platingThickness3d = platingThickness * m_boardAdapter.BiuTo3dUnits();
732 
733  if( m_boardAdapter.GetViaCount() > 0 )
734  {
735  const unsigned int reserve_nr_triangles_estimation =
739 
740  TRIANGLE_DISPLAY_LIST* layerTriangleVIA =
741  new TRIANGLE_DISPLAY_LIST( reserve_nr_triangles_estimation );
742 
743  // Insert plated vertical holes inside the board
744 
745  // Insert vias holes (vertical cylinders)
746  for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
747  {
748  if( track->Type() == PCB_VIA_T )
749  {
750  const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
751 
752  const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3dUnits();
753  const int nrSegments = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
754  const float hole_inner_radius = holediameter / 2.0f;
755 
756  const SFVEC2F via_center( via->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
757  -via->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
758 
759  PCB_LAYER_ID top_layer, bottom_layer;
760  via->LayerPair( &top_layer, &bottom_layer );
761 
762  float ztop, zbot, dummy;
763 
764  getLayerZPos( top_layer, ztop, dummy );
765  getLayerZPos( bottom_layer, dummy, zbot );
766 
767  wxASSERT( zbot < ztop );
768 
769  generateCylinder( via_center, hole_inner_radius,
770  hole_inner_radius + platingThickness3d,
771  ztop, zbot, nrSegments, layerTriangleVIA );
772  }
773  }
774 
775  m_vias = new OPENGL_RENDER_LIST( *layerTriangleVIA, 0, 0.0f, 0.0f );
776 
777  delete layerTriangleVIA;
778  }
779 
780 
781  if( m_boardAdapter.GetHoleCount() > 0 )
782  {
783  SHAPE_POLY_SET tht_outer_holes_poly; // Stores the outer poly of the copper holes (the pad)
784  SHAPE_POLY_SET tht_inner_holes_poly; // Stores the inner poly of the copper holes (the hole)
785 
786  tht_outer_holes_poly.RemoveAllContours();
787  tht_inner_holes_poly.RemoveAllContours();
788 
789  // Insert pads holes (vertical cylinders)
790  for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
791  {
792  for( const PAD* pad : footprint->Pads() )
793  {
794  if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
795  {
796  const wxSize drillsize = pad->GetDrillSize();
797  const bool hasHole = drillsize.x && drillsize.y;
798 
799  if( !hasHole )
800  continue;
801 
802  pad->TransformHoleWithClearanceToPolygon( tht_outer_holes_poly,
803  platingThickness,
804  ARC_HIGH_DEF, ERROR_INSIDE );
805  pad->TransformHoleWithClearanceToPolygon( tht_inner_holes_poly, 0,
806  ARC_HIGH_DEF, ERROR_INSIDE );
807  }
808  }
809  }
810 
811  // Subtract the holes
812  tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly, SHAPE_POLY_SET::PM_FAST );
813 
816 
817  CONTAINER_2D holesContainer;
818 
819  ConvertPolygonToTriangles( tht_outer_holes_poly, holesContainer,
821  (const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
822 
823  const LIST_OBJECT2D& listHolesObject2d = holesContainer.GetList();
824 
825  if( listHolesObject2d.size() > 0 )
826  {
827  float layer_z_top, layer_z_bot, dummy;
828 
829  getLayerZPos( F_Cu, layer_z_top, dummy );
830  getLayerZPos( B_Cu, dummy, layer_z_bot );
831 
832  TRIANGLE_DISPLAY_LIST* layerTriangles =
833  new TRIANGLE_DISPLAY_LIST( listHolesObject2d.size() );
834 
835  // Convert the list of objects(triangles) to triangle layer structure
836  for( const OBJECT_2D* itemOnLayer : listHolesObject2d )
837  {
838  const OBJECT_2D* object2d_A = itemOnLayer;
839 
840  wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
841 
842  const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
843 
844  const SFVEC2F& v1 = tri->GetP1();
845  const SFVEC2F& v2 = tri->GetP2();
846  const SFVEC2F& v3 = tri->GetP3();
847 
848  addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
849  }
850 
851  wxASSERT( tht_outer_holes_poly.OutlineCount() > 0 );
852 
853  if( tht_outer_holes_poly.OutlineCount() > 0 )
854  {
855  layerTriangles->AddToMiddleContourns( tht_outer_holes_poly,
856  layer_z_bot, layer_z_top,
857  m_boardAdapter.BiuTo3dUnits(), false );
858 
859  m_padHoles = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
860  layer_z_top, layer_z_top );
861  }
862 
863  delete layerTriangles;
864  }
865  }
866 }
867 
868 
870 {
871  if( m_3dModelMap.size() > 0 )
872  return;
873 
874  wxFrame* frame = dynamic_cast<EDA_3D_VIEWER_FRAME*>( m_canvas->GetParent() );
875 
876  if( frame )
877  {
878  STATUSBAR_REPORTER activityReporter( frame->GetStatusBar(),
880  load3dModels( &activityReporter );
881  }
882  else
883  load3dModels( nullptr );
884 }
885 
886 
887 void RENDER_3D_OPENGL::load3dModels( REPORTER* aStatusReporter )
888 {
889  if( !m_boardAdapter.GetBoard() )
890  return;
891 
895  {
896  return;
897  }
898 
899  // Go for all footprints
900  for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
901  {
902  for( const FP_3DMODEL& model : footprint->Models() )
903  {
904  if( model.m_Show && !model.m_Filename.empty() )
905  {
906  if( aStatusReporter )
907  {
908  // Display the short filename of the 3D model loaded:
909  // (the full name is usually too long to be displayed)
910  wxFileName fn( model.m_Filename );
911  aStatusReporter->Report( wxString::Format( _( "Loading %s..." ),
912  fn.GetFullName() ) );
913  }
914 
915  // Check if the model is not present in our cache map
916  // (Not already loaded in memory)
917  if( m_3dModelMap.find( model.m_Filename ) == m_3dModelMap.end() )
918  {
919  // It is not present, try get it from cache
920  const S3DMODEL* modelPtr =
921  m_boardAdapter.Get3dCacheManager()->GetModel( model.m_Filename );
922 
923  // only add it if the return is not NULL
924  if( modelPtr )
925  {
927  MODEL_3D* ogl_model = new MODEL_3D( *modelPtr, materialMode );
928 
929  if( ogl_model )
930  m_3dModelMap[ model.m_Filename ] = ogl_model;
931  }
932  }
933  }
934  }
935  }
936 }
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
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:623
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:643
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 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)
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