KiCad PCB EDA Suite
render_3d_opengl.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-2020 Mario Luzeiro <[email protected]>
5  * Copyright (C) 2015-2021 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 <gal/opengl/kiglew.h> // Must be included first
26 
27 #include "render_3d_opengl.h"
28 #include "opengl_utils.h"
29 #include "common_ogl/ogl_utils.h"
30 #include <footprint.h>
31 #include <3d_math.h>
32 #include <math/util.h> // for KiROUND
33 #include <wx/log.h>
34 
35 #include <base_units.h>
36 
40 #define UNITS3D_TO_UNITSPCB (IU_PER_MM)
41 
43  CAMERA& aCamera ) :
44  RENDER_3D_BASE( aCanvas, aAdapter, aCamera )
45 {
46  wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
47 
48  m_layers.clear();
49  m_outerLayerHoles.clear();
50  m_innerLayerHoles.clear();
51  m_triangles.clear();
52  m_board = nullptr;
53  m_antiBoard = nullptr;
54 
55  m_platedPadsFront = nullptr;
56  m_platedPadsBack = nullptr;
57 
58  m_outerThroughHoles = nullptr;
59  m_outerThroughHoleRings = nullptr;
60  m_outerViaThroughHoles = nullptr;
61  m_vias = nullptr;
62  m_padHoles = nullptr;
63 
64  m_circleTexture = 0;
65  m_grid = 0;
67  m_currentRollOverItem = nullptr;
68  m_boardWithHoles = nullptr;
69 
70  m_3dModelMap.clear();
71 }
72 
73 
75 {
76  wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
77 
78  freeAllLists();
79 
80  glDeleteTextures( 1, &m_circleTexture );
81 }
82 
83 
85 {
86  return 50; // ms
87 }
88 
89 
90 void RENDER_3D_OPENGL::SetCurWindowSize( const wxSize& aSize )
91 {
92  if( m_windowSize != aSize )
93  {
94  m_windowSize = aSize;
95  glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
96 
97  // Initialize here any screen dependent data here
98  }
99 }
100 
101 
103 {
104  if( enabled )
105  glEnable( GL_LIGHT0 );
106  else
107  glDisable( GL_LIGHT0 );
108 }
109 
110 
111 void RENDER_3D_OPENGL::setLightTop( bool enabled )
112 {
113  if( enabled )
114  glEnable( GL_LIGHT1 );
115  else
116  glDisable( GL_LIGHT1 );
117 }
118 
119 
121 {
122  if( enabled )
123  glEnable( GL_LIGHT2 );
124  else
125  glDisable( GL_LIGHT2 );
126 }
127 
128 
130 {
131  const float arrow_size = RANGE_SCALE_3D * 0.30f;
132 
133  glDisable( GL_CULL_FACE );
134 
135  // YxY squared view port, this is on propose
136  glViewport( 4, 4, m_windowSize.y / 8 , m_windowSize.y / 8 );
137  glClear( GL_DEPTH_BUFFER_BIT );
138 
139  glMatrixMode( GL_PROJECTION );
140  glLoadIdentity();
141  gluPerspective( 45.0f, 1.0f, 0.001f, RANGE_SCALE_3D );
142 
143  glMatrixMode( GL_MODELVIEW );
144  glLoadIdentity();
145 
146  const glm::mat4 TranslationMatrix =
147  glm::translate( glm::mat4( 1.0f ), SFVEC3F( 0.0f, 0.0f, -( arrow_size * 2.75f ) ) );
148 
149  const glm::mat4 ViewMatrix = TranslationMatrix * m_camera.GetRotationMatrix();
150 
151  glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
152 
154 
155  glColor3f( 0.9f, 0.0f, 0.0f );
156  DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( arrow_size, 0.0f, 0.0f ), 0.275f );
157 
158  glColor3f( 0.0f, 0.9f, 0.0f );
159  DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, arrow_size, 0.0f ), 0.275f );
160 
161  glColor3f( 0.0f, 0.0f, 0.9f );
162  DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, arrow_size ), 0.275f );
163 
164  glEnable( GL_CULL_FACE );
165 }
166 
167 
169 {
170  m_materials = {};
171 
173  {
174  // http://devernay.free.fr/cours/opengl/materials.html
175 
176  // Plated copper
177  // Copper material mixed with the copper color
178  m_materials.m_Copper.m_Ambient = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.1f,
179  m_boardAdapter.m_CopperColor.g * 0.1f,
180  m_boardAdapter.m_CopperColor.b * 0.1f);
181 
182  m_materials.m_Copper.m_Specular = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.75f + 0.25f,
183  m_boardAdapter.m_CopperColor.g * 0.75f + 0.25f,
184  m_boardAdapter.m_CopperColor.b * 0.75f + 0.25f );
185 
186  // This guess the material type(ex: copper vs gold) to determine the
187  // shininess factor between 0.1 and 0.4
188  float shininessfactor = 0.40f - mapf( fabs( m_boardAdapter.m_CopperColor.r -
190  0.15f, 1.00f,
191  0.00f, 0.30f );
192 
193  m_materials.m_Copper.m_Shininess = shininessfactor * 128.0f;
194  m_materials.m_Copper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
195 
196 
197  // Non plated copper (raw copper)
198  m_materials.m_NonPlatedCopper.m_Ambient = SFVEC3F( 0.191f, 0.073f, 0.022f );
199  m_materials.m_NonPlatedCopper.m_Diffuse = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f,
200  50.0f / 255.0f );
201  m_materials.m_NonPlatedCopper.m_Specular = SFVEC3F( 0.256f, 0.137f, 0.086f );
202  m_materials.m_NonPlatedCopper.m_Shininess = 0.1f * 128.0f;
203  m_materials.m_NonPlatedCopper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
204 
205  // Paste material mixed with paste color
206  m_materials.m_Paste.m_Ambient = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r,
209 
210  m_materials.m_Paste.m_Specular = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r *
216 
217  m_materials.m_Paste.m_Shininess = 0.1f * 128.0f;
218  m_materials.m_Paste.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
219 
220  // Silk screen material mixed with silk screen color
221  m_materials.m_SilkSTop.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorTop.r,
224 
225  m_materials.m_SilkSTop.m_Specular = SFVEC3F(
227  0.10f,
229  0.10f,
231  0.10f );
232 
233  m_materials.m_SilkSTop.m_Shininess = 0.078125f * 128.0f;
234  m_materials.m_SilkSTop.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
235 
236  // Silk screen material mixed with silk screen color
237  m_materials.m_SilkSBot.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorBot.r,
240 
241  m_materials.m_SilkSBot.m_Specular = SFVEC3F(
243  0.10f,
245  0.10f,
247  0.10f );
248 
249  m_materials.m_SilkSBot.m_Shininess = 0.078125f * 128.0f;
250  m_materials.m_SilkSBot.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
251 
252  m_materials.m_SolderMask.m_Shininess = 0.8f * 128.0f;
253  m_materials.m_SolderMask.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
254 
255  // Epoxy material
256  m_materials.m_EpoxyBoard.m_Ambient = SFVEC3F( 117.0f / 255.0f, 97.0f / 255.0f,
257  47.0f / 255.0f );
258 
259  m_materials.m_EpoxyBoard.m_Specular = SFVEC3F( 18.0f / 255.0f, 3.0f / 255.0f,
260  20.0f / 255.0f );
261 
262  m_materials.m_EpoxyBoard.m_Shininess = 0.1f * 128.0f;
263  m_materials.m_EpoxyBoard.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
264  }
265  else // Technical Mode
266  {
267  const SFVEC3F matAmbientColor = SFVEC3F( 0.10f );
268  const SFVEC3F matSpecularColor = SFVEC3F( 0.10f );
269  const float matShininess = 0.1f * 128.0f;
270 
271  // Copper material
272  m_materials.m_Copper.m_Ambient = matAmbientColor;
273  m_materials.m_Copper.m_Specular = matSpecularColor;
274  m_materials.m_Copper.m_Shininess = matShininess;
275  m_materials.m_Copper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
276 
277  // Paste material
278  m_materials.m_Paste.m_Ambient = matAmbientColor;
279  m_materials.m_Paste.m_Specular = matSpecularColor;
280  m_materials.m_Paste.m_Shininess = matShininess;
281  m_materials.m_Paste.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
282 
283  // Silk screen material
284  m_materials.m_SilkSTop.m_Ambient = matAmbientColor;
285  m_materials.m_SilkSTop.m_Specular = matSpecularColor;
286  m_materials.m_SilkSTop.m_Shininess = matShininess;
287  m_materials.m_SilkSTop.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
288 
289  // Silk screen material
290  m_materials.m_SilkSBot.m_Ambient = matAmbientColor;
291  m_materials.m_SilkSBot.m_Specular = matSpecularColor;
292  m_materials.m_SilkSBot.m_Shininess = matShininess;
293  m_materials.m_SilkSBot.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
294 
295  // Solder mask material
296  m_materials.m_SolderMask.m_Ambient = matAmbientColor;
297  m_materials.m_SolderMask.m_Specular = matSpecularColor;
298  m_materials.m_SolderMask.m_Shininess = matShininess;
299  m_materials.m_SolderMask.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
300 
301  // Epoxy material
302  m_materials.m_EpoxyBoard.m_Ambient = matAmbientColor;
303  m_materials.m_EpoxyBoard.m_Specular = matSpecularColor;
304  m_materials.m_EpoxyBoard.m_Shininess = matShininess;
305  m_materials.m_EpoxyBoard.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
306 
307  // Gray material (used for example in technical vias and pad holes)
308  m_materials.m_GrayMaterial.m_Ambient = SFVEC3F( 0.8f, 0.8f, 0.8f );
309  m_materials.m_GrayMaterial.m_Diffuse = SFVEC3F( 0.3f, 0.3f, 0.3f );
310  m_materials.m_GrayMaterial.m_Specular = SFVEC3F( 0.4f, 0.4f, 0.4f );
311  m_materials.m_GrayMaterial.m_Shininess = 0.01f * 128.0f;
312  m_materials.m_GrayMaterial.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
313  }
314 }
315 
316 
318 {
319  switch( aLayerID )
320  {
321  case F_Mask:
322  case B_Mask:
323  {
324  const SFVEC4F layerColor = getLayerColor( aLayerID );
325 
326  m_materials.m_SolderMask.m_Diffuse = layerColor;
327 
328  // Convert Opacity to Transparency
329  m_materials.m_SolderMask.m_Transparency = 1.0f - layerColor.a;
330 
332  {
333  m_materials.m_SolderMask.m_Ambient = m_materials.m_SolderMask.m_Diffuse * 0.3f;
334 
335  m_materials.m_SolderMask.m_Specular =
336  m_materials.m_SolderMask.m_Diffuse * m_materials.m_SolderMask.m_Diffuse;
337  }
338 
339  OglSetMaterial( m_materials.m_SolderMask, 1.0f );
340  break;
341  }
342 
343  case B_Paste:
344  case F_Paste:
345  m_materials.m_Paste.m_Diffuse = getLayerColor( aLayerID );
346  OglSetMaterial( m_materials.m_Paste, 1.0f );
347  break;
348 
349  case B_SilkS:
350  m_materials.m_SilkSBot.m_Diffuse = getLayerColor( aLayerID );
351  OglSetMaterial( m_materials.m_SilkSBot, 1.0f );
352  break;
353 
354  case F_SilkS:
355  m_materials.m_SilkSTop.m_Diffuse = getLayerColor( aLayerID );
356  OglSetMaterial( m_materials.m_SilkSTop, 1.0f );
357  break;
358 
359  case B_Adhes:
360  case F_Adhes:
361  case Dwgs_User:
362  case Cmts_User:
363  case Eco1_User:
364  case Eco2_User:
365  case Edge_Cuts:
366  case Margin:
367  case B_CrtYd:
368  case F_CrtYd:
369  case B_Fab:
370  case F_Fab:
371  m_materials.m_Plastic.m_Diffuse = getLayerColor( aLayerID );
372 
373  m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
374  m_materials.m_Plastic.m_Diffuse.g * 0.05f,
375  m_materials.m_Plastic.m_Diffuse.b * 0.05f );
376 
377  m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
378  m_materials.m_Plastic.m_Diffuse.g * 0.7f,
379  m_materials.m_Plastic.m_Diffuse.b * 0.7f );
380 
381  m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
382  m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
383  OglSetMaterial( m_materials.m_Plastic, 1.0f );
384  break;
385 
386  default:
387  m_materials.m_Copper.m_Diffuse = getLayerColor( aLayerID );
388  OglSetMaterial( m_materials.m_Copper, 1.0f );
389  break;
390  }
391 }
392 
393 
395 {
396  SFVEC4F layerColor = m_boardAdapter.GetLayerColor( aLayerID );
397 
399  {
400  switch( aLayerID )
401  {
402  case B_Adhes:
403  case F_Adhes:
404  break;
405 
406  case B_Mask:
408  break;
409  case F_Mask:
411  break;
412 
413  case B_Paste:
414  case F_Paste:
415  layerColor = m_boardAdapter.m_SolderPasteColor;
416  break;
417 
418  case B_SilkS:
420  break;
421  case F_SilkS:
423  break;
424 
425  case Dwgs_User:
426  case Cmts_User:
427  case Eco1_User:
428  case Eco2_User:
429  case Edge_Cuts:
430  case Margin:
431  break;
432 
433  case B_CrtYd:
434  case F_CrtYd:
435  break;
436 
437  case B_Fab:
438  case F_Fab:
439  break;
440 
441  default:
442  layerColor = m_boardAdapter.m_CopperColor;
443  break;
444  }
445  }
446 
447  return layerColor;
448 }
449 
450 
451 void init_lights( void )
452 {
453  // Setup light
454  // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
455  const GLfloat ambient[] = { 0.084f, 0.084f, 0.084f, 1.0f };
456  const GLfloat diffuse0[] = { 0.3f, 0.3f, 0.3f, 1.0f };
457  const GLfloat specular0[] = { 0.5f, 0.5f, 0.5f, 1.0f };
458 
459  glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
460  glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse0 );
461  glLightfv( GL_LIGHT0, GL_SPECULAR, specular0 );
462 
463  const GLfloat diffuse12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
464  const GLfloat specular12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
465 
466  // defines a directional light that points along the negative z-axis
467  GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
468 
469  // This makes a vector slight not perpendicular with XZ plane
470  const SFVEC3F vectorLight = SphericalToCartesian( glm::pi<float>() * 0.03f,
471  glm::pi<float>() * 0.25f );
472 
473  position[0] = vectorLight.x;
474  position[1] = vectorLight.y;
475  position[2] = vectorLight.z;
476 
477  glLightfv( GL_LIGHT1, GL_AMBIENT, ambient );
478  glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse12 );
479  glLightfv( GL_LIGHT1, GL_SPECULAR, specular12 );
480  glLightfv( GL_LIGHT1, GL_POSITION, position );
481 
482  // defines a directional light that points along the positive z-axis
483  position[2] = -position[2];
484 
485  glLightfv( GL_LIGHT2, GL_AMBIENT, ambient );
486  glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse12 );
487  glLightfv( GL_LIGHT2, GL_SPECULAR, specular12 );
488  glLightfv( GL_LIGHT2, GL_POSITION, position );
489 
490  const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
491 
492  glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
493 
494  glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
495 }
496 
497 
499 {
500  OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
501 }
502 
503 
505 {
506  glEnable( GL_POLYGON_OFFSET_FILL );
507  glPolygonOffset( -0.1f, -2.0f );
508  setLayerMaterial( aLayer_id );
509 }
510 
511 
513 {
514  glDisable( GL_POLYGON_OFFSET_FILL );
515 }
516 
517 
518 void RENDER_3D_OPENGL::renderBoardBody( bool aSkipRenderHoles )
519 {
520  m_materials.m_EpoxyBoard.m_Diffuse = m_boardAdapter.m_BoardBodyColor;
521 
522  // opacity to transparency
523  m_materials.m_EpoxyBoard.m_Transparency = 1.0f - m_boardAdapter.m_BoardBodyColor.a;
524 
525  OglSetMaterial( m_materials.m_EpoxyBoard, 1.0f );
526 
527  OPENGL_RENDER_LIST* ogl_disp_list = nullptr;
528 
529  if( aSkipRenderHoles )
530  ogl_disp_list = m_board;
531  else
532  ogl_disp_list = m_boardWithHoles;
533 
534  if( ogl_disp_list )
535  {
536  ogl_disp_list->ApplyScalePosition( -m_boardAdapter.GetEpoxyThickness() / 2.0f,
538 
539  ogl_disp_list->SetItIsTransparent( true );
540 
541  ogl_disp_list->DrawAll();
542  }
543 }
544 
545 
546 bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
547  REPORTER* aWarningReporter )
548 {
549  // Initialize OpenGL
551  {
552  if( !initializeOpenGL() )
553  return false;
554  }
555 
556  if( m_reloadRequested )
557  {
558  std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
559 
560  if( aStatusReporter )
561  aStatusReporter->Report( _( "Loading..." ) );
562 
563  reload( aStatusReporter, aWarningReporter );
564 
565  // generate a new 3D grid as the size of the board may had changed
568  }
569  else
570  {
571  // Check if grid was changed
573  {
574  // and generate a new one
577  }
578  }
579 
580  setupMaterials();
581 
582  // Initial setup
583  glDepthFunc( GL_LESS );
584  glEnable( GL_CULL_FACE );
585  glFrontFace( GL_CCW ); // This is the OpenGL default
586  glEnable( GL_NORMALIZE ); // This allow OpenGL to normalize the normals after transformations
587  glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
588 
590  glDisable( GL_MULTISAMPLE );
591  else
592  glEnable( GL_MULTISAMPLE );
593 
594  // clear color and depth buffers
595  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
596  glClearDepth( 1.0f );
597  glClearStencil( 0x00 );
598  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
599 
601 
602  // Draw the background ( rectangle with color gradient)
605 
606  glEnable( GL_DEPTH_TEST );
607 
608  // Set projection and modelview matrixes
609  glMatrixMode( GL_PROJECTION );
610  glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
611  glMatrixMode( GL_MODELVIEW );
612  glLoadIdentity();
613  glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
614 
615  // Position the headlight
616  setLightFront( true );
617  setLightTop( true );
618  setLightBottom( true );
619 
620  glEnable( GL_LIGHTING );
621 
622  {
623  const SFVEC3F& cameraPos = m_camera.GetPos();
624 
625  // Place the light at a minimum Z so the diffuse factor will not drop
626  // and the board will still look with good light.
627  float zpos;
628 
629  if( cameraPos.z > 0.0f )
630  {
631  zpos = glm::max( cameraPos.z, 0.5f ) + cameraPos.z * cameraPos.z;
632  }
633  else
634  {
635  zpos = glm::min( cameraPos.z,-0.5f ) - cameraPos.z * cameraPos.z;
636  }
637 
638  // This is a point light.
639  const GLfloat headlight_pos[] = { cameraPos.x, cameraPos.y, zpos, 1.0f };
640 
641  glLightfv( GL_LIGHT0, GL_POSITION, headlight_pos );
642  }
643 
644  const bool drawMiddleSegments = !( aIsMoving &&
646 
647  const bool skipRenderHoles = aIsMoving &&
649 
650  const bool skipRenderVias = aIsMoving &&
652 
654  {
655  // Draw vias and pad holes with copper material
657  }
658  else
659  {
660  OglSetMaterial( m_materials.m_GrayMaterial, 1.0f );
661  }
662 
663  if( !( skipRenderVias || skipRenderHoles ) && m_vias )
664  m_vias->DrawAll();
665 
666  if( !skipRenderHoles && m_padHoles )
667  m_padHoles->DrawAll();
668 
669  // Display copper and tech layers
670  for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
671  {
672  const PCB_LAYER_ID layer_id = ( PCB_LAYER_ID )( ii->first );
673 
674  // Mask layers are not processed here because they are a special case
675  if( ( layer_id == B_Mask ) || ( layer_id == F_Mask ) )
676  continue;
677 
678  // Do not show inner layers when it is displaying the board and board body is opaque
679  // enough: the time to create inner layers can be *really significant*.
680  // So avoid creating them is they are not very visible
681  const double opacity_min = 0.8;
682 
684  ( m_boardAdapter.m_BoardBodyColor.a > opacity_min ) )
685  {
686  if( ( layer_id > F_Cu ) && ( layer_id < B_Cu ) )
687  continue;
688  }
689 
690  glPushMatrix();
691 
692  OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
693 
694  if( ( layer_id >= F_Cu ) && ( layer_id <= B_Cu ) )
695  {
699  setLayerMaterial( layer_id );
700  else
702 
703  if( skipRenderHoles )
704  {
705  pLayerDispList->DrawAllCameraCulled( m_camera.GetPos().z, drawMiddleSegments );
706 
707  // Draw plated pads
708  if( layer_id == F_Cu && m_platedPadsFront )
709  {
710  setPlatedCopperAndDepthOffset( layer_id );
712  drawMiddleSegments );
713  }
714  else if( layer_id == B_Cu && m_platedPadsBack )
715  {
716  setPlatedCopperAndDepthOffset( layer_id );
718  drawMiddleSegments );
719  }
720 
722  }
723  else
724  {
725  if( m_outerThroughHoles )
726  {
727  m_outerThroughHoles->ApplyScalePosition( pLayerDispList->GetZBot(),
728  pLayerDispList->GetZTop()
729  - pLayerDispList->GetZBot() );
730  }
731 
732  if( m_antiBoard )
733  {
734  m_antiBoard->ApplyScalePosition( pLayerDispList->GetZBot(),
735  pLayerDispList->GetZTop()
736  - pLayerDispList->GetZBot() );
737  }
738 
739  if( m_outerLayerHoles.find( layer_id ) != m_outerLayerHoles.end() )
740  {
741  const OPENGL_RENDER_LIST* viasHolesLayer = m_outerLayerHoles.at( layer_id );
742 
743  wxASSERT( viasHolesLayer != nullptr );
744 
745  if( viasHolesLayer != nullptr )
746  {
747  pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
749  viasHolesLayer,
750  m_antiBoard );
751 
752  // Draw plated pads
753  if( layer_id == F_Cu && m_platedPadsFront )
754  {
755  setPlatedCopperAndDepthOffset( layer_id );
757  drawMiddleSegments,
759  viasHolesLayer,
760  m_antiBoard );
761  }
762  else if( layer_id == B_Cu && m_platedPadsBack )
763  {
764  setPlatedCopperAndDepthOffset( layer_id );
766  drawMiddleSegments,
768  viasHolesLayer,
769  m_antiBoard );
770  }
771 
773  }
774  }
775  else
776  {
777  pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
779  m_antiBoard );
780 
781  if( layer_id == F_Cu && m_platedPadsFront )
782  {
783  setPlatedCopperAndDepthOffset( layer_id );
786  m_antiBoard );
787  }
788  else if( layer_id == B_Cu && m_platedPadsBack )
789  {
790  setPlatedCopperAndDepthOffset( layer_id );
793  m_antiBoard );
794  }
795 
797  }
798  }
799  }
800  else
801  {
802  setLayerMaterial( layer_id );
803 
804  OPENGL_RENDER_LIST* throughHolesOuter =
807  && ( layer_id == B_SilkS || layer_id == F_SilkS )
810 
811  if( throughHolesOuter )
812  {
813  throughHolesOuter->ApplyScalePosition(
814  pLayerDispList->GetZBot(),
815  pLayerDispList->GetZTop() - pLayerDispList->GetZBot() );
816  }
817 
818  OPENGL_RENDER_LIST* anti_board = m_antiBoard;
819 
820  if( anti_board )
821  {
822  anti_board->ApplyScalePosition(
823  pLayerDispList->GetZBot(),
824  pLayerDispList->GetZTop() - pLayerDispList->GetZBot() );
825  }
826 
827  if( !skipRenderHoles
830  && ( ( layer_id == B_SilkS && m_layers.find( B_Mask ) != m_layers.end() )
831  || ( layer_id == F_SilkS && m_layers.find( F_Mask ) != m_layers.end() ) ) )
832  {
833  const PCB_LAYER_ID layerMask_id = (layer_id == B_SilkS) ? B_Mask : F_Mask;
834 
835  const OPENGL_RENDER_LIST* pLayerDispListMask = m_layers.at( layerMask_id );
836 
837  pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
838  pLayerDispListMask,
839  throughHolesOuter, anti_board );
840  }
841  else
842  {
843  if( !skipRenderHoles && throughHolesOuter
844  && ( layer_id == B_SilkS || layer_id == F_SilkS ) )
845  {
846  pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments, nullptr,
847  throughHolesOuter,
848  anti_board );
849  }
850  else
851  {
852  // Do not render Paste layers when skipRenderHoles is enabled
853  // otherwise it will cause z-fight issues
854  if( !( skipRenderHoles && ( layer_id == B_Paste || layer_id == F_Paste ) ) )
855  {
856  pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
857  anti_board );
858  }
859  }
860  }
861  }
862 
863  glPopMatrix();
864  }
865 
866  // Render 3D Models (Non-transparent)
867  render3dModels( false, false );
868  render3dModels( true, false );
869 
870  // Display board body
872  {
873  renderBoardBody( skipRenderHoles );
874  }
875 
876  // Display transparent mask layers
878  {
879  // add a depth buffer offset, it will help to hide some artifacts
880  // on silkscreen where the SolderMask is removed
881  glEnable( GL_POLYGON_OFFSET_FILL );
882  glPolygonOffset( 0.0f, -2.0f );
883 
884  if( m_camera.GetPos().z > 0 )
885  {
887  drawMiddleSegments, skipRenderHoles );
888 
890  drawMiddleSegments, skipRenderHoles );
891  }
892  else
893  {
895  drawMiddleSegments, skipRenderHoles );
896 
898  drawMiddleSegments, skipRenderHoles );
899  }
900 
901  glDisable( GL_POLYGON_OFFSET_FILL );
902  glPolygonOffset( 0.0f, 0.0f );
903  }
904 
905  // Render 3D Models (Transparent)
906  // !TODO: this can be optimized. If there are no transparent models (or no opacity),
907  // then there is no need to make this function call.
908  glDepthMask( GL_FALSE );
909  glEnable( GL_BLEND );
910  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
911 
912  // Enables Texture Env so it can combine model transparency with each footprint opacity
913  glEnable( GL_TEXTURE_2D );
914  glActiveTexture( GL_TEXTURE0 );
915 
916  glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
917  glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
918  glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
919 
920  glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
921  glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
922 
923  glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
924  glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
925 
926  glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
927  glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
928  glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
929  glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_CONSTANT );
930 
931  render3dModels( false, true );
932  render3dModels( true, true );
933 
934  glDisable( GL_BLEND );
936 
937  glDepthMask( GL_TRUE );
938 
939  // Render Grid
941  {
942  glDisable( GL_LIGHTING );
943 
944  if( glIsList( m_grid ) )
945  glCallList( m_grid );
946 
947  glEnable( GL_LIGHTING );
948  }
949 
950  // Render 3D arrows
952  render3dArrows();
953 
954  // Return back to the original viewport (this is important if we want
955  // to take a screenshot after the render)
956  glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
957 
958  return false;
959 }
960 
961 
963 {
964  glEnable( GL_LINE_SMOOTH );
965  glShadeModel( GL_SMOOTH );
966 
967  // 4-byte pixel alignment
968  glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
969 
970  // Initialize the open GL texture to draw the filled semi-circle of the segments
972 
973  if( !circleImage )
974  return false;
975 
976  unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
977 
978  circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
979  ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
980  circleRadius,
981  0xFF );
982 
983  IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
984 
985  circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
986 
987  m_circleTexture = OglLoadTexture( *circleImageBlured );
988 
989  delete circleImageBlured;
990  circleImageBlured = nullptr;
991 
992  delete circleImage;
993  circleImage = nullptr;
994 
995  init_lights();
996 
997  // Use this mode if you want see the triangle lines (debug proposes)
998  //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
1000 
1001  return true;
1002 }
1003 
1004 
1006 {
1007  glEnable( GL_COLOR_MATERIAL );
1008  glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
1009 
1010  const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1011  const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1012  const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1013  const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
1014 
1015  glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
1016  glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
1017 
1018  glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
1019  glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
1020  glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
1021 }
1022 
1023 
1025 {
1026  if( glIsList( m_grid ) )
1027  glDeleteLists( m_grid, 1 );
1028 
1029  m_grid = 0;
1030 
1031  for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
1032  {
1033  OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
1034  delete pLayerDispList;
1035  }
1036 
1037  m_layers.clear();
1038 
1039  delete m_platedPadsFront;
1040  m_platedPadsFront = nullptr;
1041 
1042  delete m_platedPadsBack;
1043  m_platedPadsBack = nullptr;
1044 
1045  for( MAP_OGL_DISP_LISTS::const_iterator ii = m_outerLayerHoles.begin();
1046  ii != m_outerLayerHoles.end();
1047  ++ii )
1048  {
1049  OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
1050  delete pLayerDispList;
1051  }
1052 
1053  m_outerLayerHoles.clear();
1054 
1055  for( MAP_OGL_DISP_LISTS::const_iterator ii = m_innerLayerHoles.begin();
1056  ii != m_innerLayerHoles.end();
1057  ++ii )
1058  {
1059  OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
1060  delete pLayerDispList;
1061  }
1062 
1063  m_innerLayerHoles.clear();
1064 
1065  for( LIST_TRIANGLES::const_iterator ii = m_triangles.begin(); ii != m_triangles.end(); ++ii )
1066  {
1067  delete *ii;
1068  }
1069 
1070  m_triangles.clear();
1071 
1072  for( MAP_3DMODEL::const_iterator ii = m_3dModelMap.begin(); ii != m_3dModelMap.end(); ++ii )
1073  {
1074  MODEL_3D* pointer = static_cast<MODEL_3D*>(ii->second);
1075  delete pointer;
1076  }
1077 
1078  m_3dModelMap.clear();
1079 
1080  delete m_board;
1081  m_board = nullptr;
1082 
1083  delete m_boardWithHoles;
1084  m_boardWithHoles = nullptr;
1085 
1086  delete m_antiBoard;
1087  m_antiBoard = nullptr;
1088 
1089  delete m_outerThroughHoles;
1090  m_outerThroughHoles = nullptr;
1091 
1092  delete m_outerViaThroughHoles;
1093  m_outerViaThroughHoles = nullptr;
1094 
1095  delete m_outerThroughHoleRings;
1096  m_outerThroughHoleRings = nullptr;
1097 
1098  delete m_vias;
1099  m_vias = nullptr;
1100 
1101  delete m_padHoles;
1102  m_padHoles = nullptr;
1103 }
1104 
1105 
1107  bool aDrawMiddleSegments, bool aSkipRenderHoles )
1108 {
1109  wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
1110 
1111  float nonCopperThickness = m_boardAdapter.GetNonCopperLayerThickness();
1112 
1113  if( m_board )
1114  {
1115  if( m_layers.find( aLayerID ) != m_layers.end() )
1116  {
1117  OPENGL_RENDER_LIST* pLayerDispListMask = m_layers.at( aLayerID );
1118 
1120  m_outerViaThroughHoles->ApplyScalePosition( aZPosition, nonCopperThickness );
1121 
1122  m_board->ApplyScalePosition( aZPosition, nonCopperThickness );
1123 
1124  setLayerMaterial( aLayerID );
1125 
1126  m_board->SetItIsTransparent( true );
1127 
1128  if( aSkipRenderHoles )
1129  {
1130  m_board->DrawAllCameraCulled( m_camera.GetPos().z, aDrawMiddleSegments );
1131  }
1132  else
1133  {
1134  m_board->DrawAllCameraCulledSubtractLayer( aDrawMiddleSegments, pLayerDispListMask,
1136  }
1137  }
1138  else
1139  {
1140  // This case there is no layer with mask, so we will render the full board as mask
1142  m_outerViaThroughHoles->ApplyScalePosition( aZPosition, nonCopperThickness );
1143 
1144  m_board->ApplyScalePosition( aZPosition, nonCopperThickness );
1145 
1146  setLayerMaterial( aLayerID );
1147 
1148  m_board->SetItIsTransparent( true );
1149 
1150  if( aSkipRenderHoles )
1151  {
1152  m_board->DrawAllCameraCulled( m_camera.GetPos().z, aDrawMiddleSegments );
1153  }
1154  else
1155  {
1156  m_board->DrawAllCameraCulledSubtractLayer( aDrawMiddleSegments,
1158  }
1159  }
1160  }
1161 }
1162 
1163 
1164 void RENDER_3D_OPENGL::render3dModelsSelected( bool aRenderTopOrBot, bool aRenderTransparentOnly,
1165  bool aRenderSelectedOnly )
1166 {
1167  if( !m_boardAdapter.GetBoard() )
1168  return;
1169 
1170  MODEL_3D::BeginDrawMulti( !aRenderSelectedOnly );
1171 
1172  // Go for all footprints
1173  for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1174  {
1175  bool highlight = false;
1176 
1178  {
1179  if( fp->IsSelected() )
1180  highlight = true;
1181 
1183  highlight = true;
1184 
1185  if( aRenderSelectedOnly != highlight )
1186  continue;
1187  }
1188 
1189  if( !fp->Models().empty() )
1190  {
1191  if( m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
1192  {
1193  if( aRenderTopOrBot == !fp->IsFlipped() )
1194  renderFootprint( fp, aRenderTransparentOnly, highlight );
1195  }
1196  }
1197  }
1198 
1200 }
1201 
1202 
1203 void RENDER_3D_OPENGL::render3dModels( bool aRenderTopOrBot, bool aRenderTransparentOnly )
1204 {
1206  render3dModelsSelected( aRenderTopOrBot, aRenderTransparentOnly, true );
1207 
1208  render3dModelsSelected( aRenderTopOrBot, aRenderTransparentOnly, false );
1209 }
1210 
1211 
1212 void RENDER_3D_OPENGL::renderFootprint( const FOOTPRINT* aFootprint, bool aRenderTransparentOnly,
1213  bool aIsSelected )
1214 {
1215  if( !aFootprint->Models().empty() )
1216  {
1217  const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1218 
1219  glPushMatrix();
1220 
1221  wxPoint pos = aFootprint->GetPosition();
1222 
1223  glTranslatef( pos.x * m_boardAdapter.BiuTo3dUnits(), -pos.y * m_boardAdapter.BiuTo3dUnits(),
1224  zpos );
1225 
1226  if( aFootprint->GetOrientation() )
1227  glRotated( (double) aFootprint->GetOrientation() / 10.0, 0.0, 0.0, 1.0 );
1228 
1229  if( aFootprint->IsFlipped() )
1230  {
1231  glRotatef( 180.0f, 0.0f, 1.0f, 0.0f );
1232  glRotatef( 180.0f, 0.0f, 0.0f, 1.0f );
1233  }
1234 
1235  double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1236 
1237  glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
1238  modelunit_to_3d_units_factor );
1239 
1240  // Get the list of model files for this model
1241  for( const FP_3DMODEL& sM : aFootprint->Models() )
1242  {
1243  if( !sM.m_Show || sM.m_Filename.empty() )
1244  continue;
1245 
1246  // Check if the model is present in our cache map
1247  auto cache_i = m_3dModelMap.find( sM.m_Filename );
1248 
1249  if( cache_i == m_3dModelMap.end() )
1250  continue;
1251 
1252  if( const MODEL_3D* modelPtr = cache_i->second )
1253  {
1254  bool opaque = sM.m_Opacity >= 1.0;
1255 
1256  if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1257  ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1258  {
1259  glPushMatrix();
1260 
1261  // FIXME: don't do this over and over again unless the
1262  // values have changed. cache the matrix somewhere.
1263  glm::mat4 mtx( 1 );
1264  mtx = glm::translate( mtx, { sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z } );
1265  mtx = glm::rotate( mtx, glm::radians( (float) -sM.m_Rotation.z ),
1266  { 0.0f, 0.0f, 1.0f } );
1267  mtx = glm::rotate( mtx, glm::radians( (float) -sM.m_Rotation.y ),
1268  { 0.0f, 1.0f, 0.0f } );
1269  mtx = glm::rotate( mtx, glm::radians( (float) -sM.m_Rotation.x ),
1270  { 1.0f, 0.0f, 0.0f } );
1271  mtx = glm::scale( mtx, { sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z } );
1272  glMultMatrixf( glm::value_ptr( mtx ) );
1273 
1274  if( aRenderTransparentOnly )
1275  {
1276  modelPtr->DrawTransparent( sM.m_Opacity,
1277  aFootprint->IsSelected() || aIsSelected,
1279  }
1280  else
1281  {
1282  modelPtr->DrawOpaque( aFootprint->IsSelected() || aIsSelected,
1284  }
1285 
1287  {
1288  glEnable( GL_BLEND );
1289  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1290 
1291  glDisable( GL_LIGHTING );
1292 
1293  glLineWidth( 1 );
1294  modelPtr->DrawBboxes();
1295 
1296  glLineWidth( 4 );
1297  modelPtr->DrawBbox();
1298 
1299  glEnable( GL_LIGHTING );
1300  glDisable( GL_BLEND );
1301  }
1302 
1303  glPopMatrix();
1304  }
1305  }
1306  }
1307 
1308  glPopMatrix();
1309  }
1310 }
1311 
1312 
1314 {
1315  if( glIsList( m_grid ) )
1316  glDeleteLists( m_grid, 1 );
1317 
1318  m_grid = 0;
1319 
1320  if( aGridType == GRID3D_TYPE::NONE )
1321  return;
1322 
1323  m_grid = glGenLists( 1 );
1324 
1325  if( !glIsList( m_grid ) )
1326  return;
1327 
1328  glNewList( m_grid, GL_COMPILE );
1329 
1330  glEnable( GL_BLEND );
1331  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1332 
1333  const double zpos = 0.0;
1334 
1335  // Color of grid lines
1336  const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1337 
1338  // Color of grid lines every 5 lines
1339  const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1340  const double scale = m_boardAdapter.BiuTo3dUnits();
1341  const GLfloat transparency = 0.35f;
1342 
1343  double griSizeMM = 0.0;
1344 
1345  switch( aGridType )
1346  {
1347  default:
1348  case GRID3D_TYPE::NONE:
1349  return;
1350  case GRID3D_TYPE::GRID_1MM:
1351  griSizeMM = 1.0;
1352  break;
1354  griSizeMM = 2.5;
1355  break;
1356  case GRID3D_TYPE::GRID_5MM:
1357  griSizeMM = 5.0;
1358  break;
1360  griSizeMM = 10.0;
1361  break;
1362  }
1363 
1364  glNormal3f( 0.0, 0.0, 1.0 );
1365 
1366  const wxSize brd_size = m_boardAdapter.GetBoardSize();
1367  wxPoint brd_center_pos = m_boardAdapter.GetBoardPos();
1368 
1369  brd_center_pos.y = -brd_center_pos.y;
1370 
1371  const int xsize = std::max( brd_size.x, Millimeter2iu( 100 ) ) * 1.2;
1372  const int ysize = std::max( brd_size.y, Millimeter2iu( 100 ) ) * 1.2;
1373 
1374  // Grid limits, in 3D units
1375  double xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1376  double xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1377  double ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1378  double ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1379  double zmin = Millimeter2iu( -50 ) * scale;
1380  double zmax = Millimeter2iu( 100 ) * scale;
1381 
1382  // Set rasterised line width (min value = 1)
1383  glLineWidth( 1 );
1384 
1385  // Draw horizontal grid centered on 3D origin (center of the board)
1386  for( int ii = 0; ; ii++ )
1387  {
1388  if( (ii % 5) )
1389  glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1390  else
1391  glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1392  transparency );
1393 
1394  const int delta = KiROUND( ii * griSizeMM * IU_PER_MM );
1395 
1396  if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
1397  {
1398  glBegin( GL_LINES );
1399  glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1400  glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1401  glEnd();
1402 
1403  if( ii != 0 )
1404  {
1405  glBegin( GL_LINES );
1406  glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1407  glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1408  glEnd();
1409  }
1410  }
1411 
1412  if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
1413  {
1414  glBegin( GL_LINES );
1415  glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1416  glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1417  glEnd();
1418 
1419  if( ii != 0 )
1420  {
1421  glBegin( GL_LINES );
1422  glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1423  glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1424  glEnd();
1425  }
1426  }
1427 
1428  if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1429  break;
1430  }
1431 
1432  // Draw vertical grid on Z axis
1433  glNormal3f( 0.0, -1.0, 0.0 );
1434 
1435  // Draw vertical grid lines (parallel to Z axis)
1436  double posy = -brd_center_pos.y * scale;
1437 
1438  for( int ii = 0; ; ii++ )
1439  {
1440  if( (ii % 5) )
1441  glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1442  else
1443  glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1444  transparency );
1445 
1446  const double delta = ii * griSizeMM * IU_PER_MM;
1447 
1448  glBegin( GL_LINES );
1449  xmax = ( brd_center_pos.x + delta ) * scale;
1450 
1451  glVertex3f( xmax, posy, zmin );
1452  glVertex3f( xmax, posy, zmax );
1453  glEnd();
1454 
1455  if( ii != 0 )
1456  {
1457  glBegin( GL_LINES );
1458  xmin = ( brd_center_pos.x - delta ) * scale;
1459  glVertex3f( xmin, posy, zmin );
1460  glVertex3f( xmin, posy, zmax );
1461  glEnd();
1462  }
1463 
1464  if( delta > xsize / 2.0f )
1465  break;
1466  }
1467 
1468  // Draw horizontal grid lines on Z axis (parallel to X axis)
1469  for( int ii = 0; ; ii++ )
1470  {
1471  if( ii % 5 )
1472  glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1473  else
1474  glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1475 
1476  const double delta = ii * griSizeMM * IU_PER_MM * scale;
1477 
1478  if( delta <= zmax )
1479  {
1480  // Draw grid lines on Z axis (positive Z axis coordinates)
1481  glBegin( GL_LINES );
1482  glVertex3f( xmin, posy, delta );
1483  glVertex3f( xmax, posy, delta );
1484  glEnd();
1485  }
1486 
1487  if( delta <= -zmin && ( ii != 0 ) )
1488  {
1489  // Draw grid lines on Z axis (negative Z axis coordinates)
1490  glBegin( GL_LINES );
1491  glVertex3f( xmin, posy, -delta );
1492  glVertex3f( xmax, posy, -delta );
1493  glEnd();
1494  }
1495 
1496  if( ( delta > zmax ) && ( delta > -zmin ) )
1497  break;
1498  }
1499 
1500  glDisable( GL_BLEND );
1501 
1502  glEndList();
1503 }
OPENGL_RENDER_LIST * m_platedPadsBack
GRID3D_TYPE GetGridType() const noexcept
Get the current grid.
OPENGL_RENDER_LIST * m_antiBoard
bool GetFlag(DISPLAY3D_FLG aFlag) const
Get a configuration status of a flag.
BOARD_ADAPTER & m_boardAdapter
void setLightFront(bool enabled)
OPENGL_RENDER_LIST * m_board
SFVEC4F m_SilkScreenColorBot
in realistic mode: SilkScreen color ( bot )
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:183
bool IsSelected() const
Definition: eda_item.h:122
bool IsFootprintShown(FOOTPRINT_ATTR_T aFPAttributes) const
Test if footprint should be displayed in relation to attributes and the flags.
void OglSetMaterial(const SMATERIAL &aMaterial, float aOpacity, bool aUseSelectedMaterial, SFVEC3F aSelectionColor)
Set OpenGL materials.
Definition: ogl_utils.cpp:119
Implementation of conversion functions that require both schematic and board internal units.
OPENGL_RENDER_LIST * m_outerViaThroughHoles
OPENGL_RENDER_LIST * m_boardWithHoles
static constexpr double IU_PER_MM
Mock up a conversion function.
SFVEC4F GetColor(const COLOR4D &aColor) const
void DrawAllCameraCulledSubtractLayer(bool aDrawMiddle, const OPENGL_RENDER_LIST *aLayerToSubtractA=nullptr, const OPENGL_RENDER_LIST *aLayerToSubtractB=nullptr, const OPENGL_RENDER_LIST *aLayerToSubtractC=nullptr, const OPENGL_RENDER_LIST *aLayerToSubtractD=nullptr) const
float GetZBot() const
void DrawAllCameraCulled(float zCameraPos, bool aDrawMiddle=true) const
Draw all layers if they are visible by the camera if camera position is above the layer.
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:391
glm::vec4 SFVEC4F
Definition: xv3d_types.h:46
LIST_TRIANGLES m_triangles
store pointers so can be deleted latter
SFVEC4F m_SolderMaskColorTop
in realistic mode: solder mask color ( top )
MAP_OGL_DISP_LISTS m_innerLayerHoles
float GetNonCopperLayerThickness() const noexcept
Get the current non copper layers thickness.
const SFVEC3F & GetPos() const
Definition: camera.h:107
double GetOrientation() const
Definition: footprint.h:191
float mapf(float x, float in_min, float in_max, float out_min, float out_max)
Definition: 3d_math.h:133
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:48
MAP_OGL_DISP_LISTS m_layers
SFVEC4F m_BoardBodyColor
in realistic mode: FR4 board color
SFVEC4F m_SolderPasteColor
in realistic mode: solder paste color
BOARD_ITEM * m_currentRollOverItem
OPENGL_RENDER_LIST * m_outerThroughHoles
SFVEC4F m_SolderMaskColorBot
in realistic mode: solder mask color ( bot )
void OglResetTextureState()
Reset to default state the texture settings.
Definition: ogl_utils.cpp:189
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:70
const glm::mat4 GetRotationMatrix() const
Get the rotation matrix to be applied in a transformation camera.
Definition: camera.cpp:168
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
FOOTPRINT_ATTR_T
The set of attributes allowed within a FOOTPRINT, using FOOTPRINT::SetAttributes() and FOOTPRINT::Get...
Definition: footprint.h:66
#define UNITS3D_TO_UNITSPCB
Scale conversion from 3d model units to pcb units.
struct RENDER_3D_OPENGL::@2 m_materials
A class used to derive camera objects from.
Definition: camera.h:77
void EfxFilter_SkipCenter(IMAGE *aInImg, IMAGE_FILTER aFilterType, unsigned int aRadius)
Apply a filter to the input image and store it in the image class.
Definition: image.cpp:527
#define SIZE_OF_CIRCLE_TEXTURE
OPENGL_RENDER_LIST * m_vias
void reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter)
SFVEC4F m_BgColorTop
background top color
void CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
Definition: image.cpp:173
void setLightBottom(bool enabled)
wxPoint GetBoardPos() const noexcept
Get the board center.
float GetLayerTopZPos(PCB_LAYER_ID aLayerId) const noexcept
Get the top z position.
MAP_OGL_DISP_LISTS m_outerLayerHoles
GLuint m_grid
oGL list that stores current grid
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
FOOTPRINTS & Footprints()
Definition: board.h:234
float GetLayerBottomZPos(PCB_LAYER_ID aLayerId) const noexcept
Get the bottom z position.
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:427
OPENGL_RENDER_LIST * m_outerThroughHoleRings
double BiuTo3dUnits() const noexcept
Board integer units To 3D units.
void setPlatedCopperAndDepthOffset(PCB_LAYER_ID aLayer_id)
const BOARD * GetBoard() const noexcept
Get current board to be rendered.
#define _(s)
SFVEC4F m_BgColorBot
background bottom color
void renderFootprint(const FOOTPRINT *aFootprint, bool aRenderTransparentOnly, bool aIsSelected)
void ApplyScalePosition(float aZposition, float aZscale)
void setLightTop(bool enabled)
OPENGL_RENDER_LIST * m_padHoles
This is a base class to hold data and functions for render targets.
GRID3D_TYPE
Grid types.
Definition: 3d_enums.h:99
void DrawRoundArrow(SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize)
Draw a round arrow.
void DrawAll(bool aDrawMiddle=true) const
Call to draw all the display lists.
OPENGL_RENDER_LIST * m_platedPadsFront
bool m_is_opengl_initialized
void SetCurWindowSize(const wxSize &aSize) override
Before each render, the canvas will tell the render what is the size of its windows,...
float GetFootprintZPos(bool aIsFlipped) const
Get the position of the footprint in 3d integer units considering if it is flipped or not.
std::unique_ptr< BUSY_INDICATOR > CreateBusyIndicator() const
Return a created busy indicator, if a factory has been set, else a null pointer.
Store the OpenGL display lists to related with a layer.
bool m_reloadRequested
The window size that this camera is working.
bool IsFlipped() const
Definition: footprint.h:278
void generate3dGrid(GRID3D_TYPE aGridType)
Create a 3D grid to an OpenGL display list.
static void EndDrawMulti()
Cleanup render states after drawing multiple models.
Definition: 3d_model.cpp:401
void renderBoardBody(bool aSkipRenderHoles)
SFVEC4F GetLayerColor(PCB_LAYER_ID aLayerId) const
Get the technical color of a layer.
const int scale
void setLayerMaterial(PCB_LAYER_ID aLayerID)
GRID3D_TYPE m_lastGridType
Stores the last grid type.
void OglDrawBackground(const SFVEC3F &aTopColor, const SFVEC3F &aBotColor)
Definition: ogl_utils.cpp:160
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
SFVEC4F m_CopperColor
in realistic mode: copper color
Definition: layer_ids.h:71
unsigned int GetHeight() const
Definition: image.h:214
RENDER_3D_OPENGL(EDA_3D_CANVAS *aCanvas, BOARD_ADAPTER &aAdapter, CAMERA &aCamera)
SFVEC3F SphericalToCartesian(float aInclination, float aAzimuth)
https://en.wikipedia.org/wiki/Spherical_coordinate_system
Definition: 3d_math.h:43
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
Define generic OpenGL functions that are common to any OpenGL target.
Manage an 8-bit channel image.
Definition: image.h:89
wxPoint GetPosition() const override
Definition: footprint.h:187
CAMERA & m_camera
Flag if the opengl specific for this render was already initialized.
void SetItIsTransparent(bool aSetTransparent)
Defines math related functions.
float GetZTop() const
SFVEC3F m_OpenGlSelectionColor
constexpr int delta
SFVEC4F m_SilkScreenColorTop
in realistic mode: SilkScreen color ( top )
unsigned int GetWidth() const
Definition: image.h:213
float GetEpoxyThickness() const noexcept
Get the current epoxy thickness.
static void BeginDrawMulti(bool aUseColorInformation)
Set some basic render states before drawing multiple models.
Definition: 3d_model.cpp:385
GLuint OglLoadTexture(const IMAGE &aImage)
Generate a new OpenGL texture.
Definition: ogl_utils.cpp:71
MAP_3DMODEL m_3dModelMap
static constexpr int Millimeter2iu(double mm)
void render3dModels(bool aRenderTopOrBot, bool aRenderTransparentOnly)
wxSize GetBoardSize() const noexcept
Get the board size.
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:68
bool Redraw(bool aIsMoving, REPORTER *aStatusReporter, REPORTER *aWarningReporter) override
Redraw the view.
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
Definition: board_adapter.h:62
void render3dModelsSelected(bool aRenderTopOrBot, bool aRenderTransparentOnly, bool aRenderSelectedOnly)
int GetWaitForEditingTimeOut() override
Give the interface the time (in ms) that it should wait for editing or movements before (this works f...
void init_lights(void)
void renderSolderMaskLayer(PCB_LAYER_ID aLayerID, float aZPosition, bool aDrawMiddleSegments, bool aSkipRenderHoles)
SFVEC4F getLayerColor(PCB_LAYER_ID aLayerID)