1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
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 
37 /**
38  * Scale conversion from 3d model units to pcb units
39  */
40 #define UNITS3D_TO_UNITSPCB (IU_PER_MM)
41 
RENDER_3D_OPENGL(EDA_3D_CANVAS * aCanvas,BOARD_ADAPTER & aAdapter,CAMERA & aCamera)42 RENDER_3D_OPENGL::RENDER_3D_OPENGL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdapter,
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;
66     m_lastGridType = GRID3D_TYPE::NONE;
67     m_currentRollOverItem = nullptr;
68     m_boardWithHoles = nullptr;
69 
70     m_3dModelMap.clear();
71 }
72 
73 
~RENDER_3D_OPENGL()74 RENDER_3D_OPENGL::~RENDER_3D_OPENGL()
75 {
76     wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
77 
78     freeAllLists();
79 
80     glDeleteTextures( 1, &m_circleTexture );
81 }
82 
83 
GetWaitForEditingTimeOut()84 int RENDER_3D_OPENGL::GetWaitForEditingTimeOut()
85 {
86     return 50; // ms
87 }
88 
89 
SetCurWindowSize(const wxSize & aSize)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 
setLightFront(bool enabled)102 void RENDER_3D_OPENGL::setLightFront( bool enabled )
103 {
104     if( enabled )
105         glEnable( GL_LIGHT0 );
106     else
107         glDisable( GL_LIGHT0 );
108 }
109 
110 
setLightTop(bool enabled)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 
setLightBottom(bool enabled)120 void RENDER_3D_OPENGL::setLightBottom( bool enabled )
121 {
122     if( enabled )
123         glEnable( GL_LIGHT2 );
124     else
125         glDisable( GL_LIGHT2 );
126 }
127 
128 
render3dArrows()129 void RENDER_3D_OPENGL::render3dArrows()
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 
153     setArrowMaterial();
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 
setupMaterials()168 void RENDER_3D_OPENGL::setupMaterials()
169 {
170     m_materials = {};
171 
172     if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
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 -
189                                                     m_boardAdapter.m_CopperColor.g ),
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,
207                                                  m_boardAdapter.m_SolderPasteColor.g,
208                                                  m_boardAdapter.m_SolderPasteColor.b );
209 
210         m_materials.m_Paste.m_Specular = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r *
211                                                   m_boardAdapter.m_SolderPasteColor.r,
212                                                   m_boardAdapter.m_SolderPasteColor.g *
213                                                   m_boardAdapter.m_SolderPasteColor.g,
214                                                   m_boardAdapter.m_SolderPasteColor.b *
215                                                   m_boardAdapter.m_SolderPasteColor.b );
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,
222                                                     m_boardAdapter.m_SilkScreenColorTop.g,
223                                                     m_boardAdapter.m_SilkScreenColorTop.b );
224 
225         m_materials.m_SilkSTop.m_Specular = SFVEC3F(
226                 m_boardAdapter.m_SilkScreenColorTop.r * m_boardAdapter.m_SilkScreenColorTop.r +
227                 0.10f,
228                 m_boardAdapter.m_SilkScreenColorTop.g * m_boardAdapter.m_SilkScreenColorTop.g +
229                 0.10f,
230                 m_boardAdapter.m_SilkScreenColorTop.b * m_boardAdapter.m_SilkScreenColorTop.b +
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,
238                                                     m_boardAdapter.m_SilkScreenColorBot.g,
239                                                     m_boardAdapter.m_SilkScreenColorBot.b );
240 
241         m_materials.m_SilkSBot.m_Specular = SFVEC3F(
242                 m_boardAdapter.m_SilkScreenColorBot.r * m_boardAdapter.m_SilkScreenColorBot.r +
243                 0.10f,
244                 m_boardAdapter.m_SilkScreenColorBot.g * m_boardAdapter.m_SilkScreenColorBot.g +
245                 0.10f,
246                 m_boardAdapter.m_SilkScreenColorBot.b * m_boardAdapter.m_SilkScreenColorBot.b +
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 
setLayerMaterial(PCB_LAYER_ID aLayerID)317 void RENDER_3D_OPENGL::setLayerMaterial( PCB_LAYER_ID aLayerID )
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 
331         if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
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 
getLayerColor(PCB_LAYER_ID aLayerID)394 SFVEC4F RENDER_3D_OPENGL::getLayerColor( PCB_LAYER_ID aLayerID )
395 {
396     SFVEC4F layerColor = m_boardAdapter.GetLayerColor( aLayerID );
397 
398     if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
399     {
400         switch( aLayerID )
401         {
402         case B_Adhes:
403         case F_Adhes:
404             break;
405 
406         case B_Mask:
407             layerColor = m_boardAdapter.m_SolderMaskColorBot;
408             break;
409         case F_Mask:
410             layerColor = m_boardAdapter.m_SolderMaskColorTop;
411             break;
412 
413         case B_Paste:
414         case F_Paste:
415             layerColor = m_boardAdapter.m_SolderPasteColor;
416             break;
417 
418         case B_SilkS:
419             layerColor = m_boardAdapter.m_SilkScreenColorBot;
420             break;
421         case F_SilkS:
422             layerColor = m_boardAdapter.m_SilkScreenColorTop;
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 
init_lights(void)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 
setCopperMaterial()498 void RENDER_3D_OPENGL::setCopperMaterial()
499 {
500     OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
501 }
502 
503 
setPlatedCopperAndDepthOffset(PCB_LAYER_ID aLayer_id)504 void RENDER_3D_OPENGL::setPlatedCopperAndDepthOffset( PCB_LAYER_ID aLayer_id )
505 {
506     glEnable( GL_POLYGON_OFFSET_FILL );
507     glPolygonOffset(-0.1f, -2.0f );
508     setLayerMaterial( aLayer_id );
509 }
510 
511 
unsetDepthOffset()512 void RENDER_3D_OPENGL::unsetDepthOffset()
513 {
514     glDisable( GL_POLYGON_OFFSET_FILL );
515 }
516 
517 
renderBoardBody(bool aSkipRenderHoles)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,
537                                             m_boardAdapter.GetEpoxyThickness() );
538 
539         ogl_disp_list->SetItIsTransparent( true );
540 
541         ogl_disp_list->DrawAll();
542     }
543 }
544 
545 
Redraw(bool aIsMoving,REPORTER * aStatusReporter,REPORTER * aWarningReporter)546 bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
547                                REPORTER* aWarningReporter )
548 {
549     // Initialize OpenGL
550     if( !m_is_opengl_initialized )
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
566         m_lastGridType = m_boardAdapter.GetGridType();
567         generate3dGrid( m_lastGridType );
568     }
569     else
570     {
571         // Check if grid was changed
572         if( m_boardAdapter.GetGridType() != m_lastGridType )
573         {
574             // and generate a new one
575             m_lastGridType = m_boardAdapter.GetGridType();
576             generate3dGrid( m_lastGridType );
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 
589     if( m_boardAdapter.GetFlag( FL_RENDER_OPENGL_AA_DISABLE_ON_MOVE ) && aIsMoving )
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 
600     OglResetTextureState();
601 
602     // Draw the background ( rectangle with color gradient)
603     OglDrawBackground( SFVEC3F( m_boardAdapter.m_BgColorTop ),
604                        SFVEC3F( m_boardAdapter.m_BgColorBot ) );
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 &&
645                                     m_boardAdapter.GetFlag( FL_RENDER_OPENGL_THICKNESS_DISABLE_ON_MOVE ) );
646 
647     const bool skipRenderHoles = aIsMoving &&
648                                  m_boardAdapter.GetFlag( FL_RENDER_OPENGL_HOLES_DISABLE_ON_MOVE );
649 
650     const bool skipRenderVias = aIsMoving &&
651                                 m_boardAdapter.GetFlag( FL_RENDER_OPENGL_VIAS_DISABLE_ON_MOVE );
652 
653     if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
654     {
655         // Draw vias and pad holes with copper material
656         setLayerMaterial( B_Cu );
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 
683         if( m_boardAdapter.GetFlag( FL_SHOW_BOARD_BODY ) &&
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         {
696             if( !m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) ||
697                 !( m_boardAdapter.GetFlag( FL_RENDER_PLATED_PADS_AS_PLATED ) &&
698                    m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) ) )
699                 setLayerMaterial( layer_id );
700             else
701                 setCopperMaterial();
702 
703             if( skipRenderHoles )
704             {
705                 pLayerDispList->DrawAllCameraCulled( m_camera.GetPos().z, drawMiddleSegments );
706 
707                 // Draw copper plated pads
708                 if( ( ( layer_id == F_Cu ) || ( layer_id == B_Cu ) ) &&
709                     ( m_platedPadsFront || m_platedPadsBack ) )
710                     setPlatedCopperAndDepthOffset( layer_id );
711 
712                 if( layer_id == F_Cu && m_platedPadsFront )
713                 {
714                     m_platedPadsFront->DrawAllCameraCulled( m_camera.GetPos().z,
715                                                             drawMiddleSegments );
716                 }
717                 else if( layer_id == B_Cu && m_platedPadsBack )
718                 {
719                     m_platedPadsBack->DrawAllCameraCulled( m_camera.GetPos().z,
720                                                            drawMiddleSegments );
721                 }
722 
723                 unsetDepthOffset();
724             }
725             else
726             {
727                 if( m_outerThroughHoles )
728                 {
729                     m_outerThroughHoles->ApplyScalePosition( pLayerDispList->GetZBot(),
730                                                              pLayerDispList->GetZTop()
731                                                                  - pLayerDispList->GetZBot() );
732                 }
733 
734                 if( m_antiBoard )
735                 {
736                     m_antiBoard->ApplyScalePosition( pLayerDispList->GetZBot(),
737                                                      pLayerDispList->GetZTop()
738                                                          - pLayerDispList->GetZBot() );
739                 }
740 
741                 if( m_outerLayerHoles.find( layer_id ) != m_outerLayerHoles.end() )
742                 {
743                     const OPENGL_RENDER_LIST* viasHolesLayer = m_outerLayerHoles.at( layer_id );
744 
745                     wxASSERT( viasHolesLayer != nullptr );
746 
747                     if( viasHolesLayer != nullptr )
748                     {
749                         pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
750                                                                           m_outerThroughHoles,
751                                                                           viasHolesLayer,
752                                                                           m_antiBoard );
753 
754                         // Draw copper plated pads
755 
756                         if( ( ( layer_id == F_Cu ) || ( layer_id == B_Cu ) ) &&
757                             ( m_platedPadsFront || m_platedPadsBack ) )
758                         {
759                             setPlatedCopperAndDepthOffset( layer_id );
760                         }
761 
762                         if( layer_id == F_Cu && m_platedPadsFront )
763                         {
764                             m_platedPadsFront->DrawAllCameraCulledSubtractLayer(
765                                     drawMiddleSegments,
766                                     m_outerThroughHoles,
767                                     viasHolesLayer,
768                                     m_antiBoard );
769                         }
770                         else if( layer_id == B_Cu && m_platedPadsBack )
771                         {
772                             m_platedPadsBack->DrawAllCameraCulledSubtractLayer(
773                                     drawMiddleSegments,
774                                     m_outerThroughHoles,
775                                     viasHolesLayer,
776                                     m_antiBoard );
777                         }
778 
779                         unsetDepthOffset();
780                     }
781                 }
782                 else
783                 {
784                     pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
785                                                                       m_outerThroughHoles,
786                                                                       m_antiBoard );
787 
788                     // Draw copper plated pads
789                     if( ( ( layer_id == F_Cu ) || ( layer_id == B_Cu ) ) &&
790                         ( m_platedPadsFront || m_platedPadsBack ) )
791                     {
792                         setPlatedCopperAndDepthOffset( layer_id );
793                     }
794 
795                     if( layer_id == F_Cu && m_platedPadsFront )
796                     {
797                         m_platedPadsFront->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
798                                                                              m_outerThroughHoles,
799                                                                              m_antiBoard );
800                     }
801                     else if( layer_id == B_Cu && m_platedPadsBack )
802                     {
803                         m_platedPadsBack->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
804                                                                             m_outerThroughHoles,
805                                                                             m_antiBoard );
806                     }
807 
808                     unsetDepthOffset();
809                 }
810             }
811         }
812         else
813         {
814             setLayerMaterial( layer_id );
815 
816             OPENGL_RENDER_LIST* throughHolesOuter =
817                     m_boardAdapter.GetFlag( FL_CLIP_SILK_ON_VIA_ANNULUS )
818                         && m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE )
819                         && ( layer_id == B_SilkS || layer_id == F_SilkS )
820                             ? m_outerThroughHoleRings
821                             : m_outerThroughHoles;
822 
823             if( throughHolesOuter )
824             {
825                 throughHolesOuter->ApplyScalePosition(
826                         pLayerDispList->GetZBot(),
827                         pLayerDispList->GetZTop() - pLayerDispList->GetZBot() );
828             }
829 
830             OPENGL_RENDER_LIST* anti_board = m_antiBoard;
831 
832             if( anti_board )
833             {
834                 anti_board->ApplyScalePosition(
835                         pLayerDispList->GetZBot(),
836                         pLayerDispList->GetZTop() - pLayerDispList->GetZBot() );
837             }
838 
839             if( !skipRenderHoles
840               && m_boardAdapter.GetFlag( FL_SUBTRACT_MASK_FROM_SILK )
841               && m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE )
842               && ( ( layer_id == B_SilkS && m_layers.find( B_Mask ) != m_layers.end() )
843                  || ( layer_id == F_SilkS && m_layers.find( F_Mask ) != m_layers.end() ) ) )
844             {
845                 const PCB_LAYER_ID layerMask_id = (layer_id == B_SilkS) ? B_Mask : F_Mask;
846 
847                 const OPENGL_RENDER_LIST* pLayerDispListMask = m_layers.at( layerMask_id );
848 
849                 pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
850                                                                   pLayerDispListMask,
851                                                                   throughHolesOuter, anti_board );
852             }
853             else
854             {
855                 if( !skipRenderHoles && throughHolesOuter
856                   && ( layer_id == B_SilkS || layer_id == F_SilkS ) )
857                 {
858                     pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments, nullptr,
859                                                                       throughHolesOuter,
860                                                                       anti_board );
861                 }
862                 else
863                 {
864                     // Do not render Paste layers when skipRenderHoles is enabled
865                     // otherwise it will cause z-fight issues
866                     if( !( skipRenderHoles && ( layer_id == B_Paste || layer_id == F_Paste ) ) )
867                     {
868                         pLayerDispList->DrawAllCameraCulledSubtractLayer( drawMiddleSegments,
869                                                                           anti_board );
870                     }
871                 }
872             }
873         }
874 
875         glPopMatrix();
876     }
877 
878     // Render 3D Models (Non-transparent)
879     render3dModels( false, false );
880     render3dModels( true, false );
881 
882     // Display board body
883     if( m_boardAdapter.GetFlag( FL_SHOW_BOARD_BODY ) )
884     {
885         renderBoardBody( skipRenderHoles );
886     }
887 
888     // Display transparent mask layers
889     if( m_boardAdapter.GetFlag( FL_SOLDERMASK ) )
890     {
891         // add a depth buffer offset, it will help to hide some artifacts
892         // on silkscreen where the SolderMask is removed
893         glEnable( GL_POLYGON_OFFSET_FILL );
894         glPolygonOffset( 0.0f, -2.0f );
895 
896         if( m_camera.GetPos().z > 0 )
897         {
898             renderSolderMaskLayer( B_Mask, m_boardAdapter.GetLayerTopZPos( B_Mask ),
899                                    drawMiddleSegments, skipRenderHoles );
900 
901             renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
902                                    drawMiddleSegments, skipRenderHoles );
903         }
904         else
905         {
906             renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
907                                    drawMiddleSegments, skipRenderHoles );
908 
909             renderSolderMaskLayer( B_Mask, m_boardAdapter.GetLayerTopZPos( B_Mask ),
910                                    drawMiddleSegments, skipRenderHoles );
911         }
912 
913         glDisable( GL_POLYGON_OFFSET_FILL );
914         glPolygonOffset( 0.0f, 0.0f );
915     }
916 
917     // Render 3D Models (Transparent)
918     // !TODO: this can be optimized. If there are no transparent models (or no opacity),
919     // then there is no need to make this function call.
920     glDepthMask( GL_FALSE );
921     glEnable( GL_BLEND );
922     glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
923 
924     // Enables Texture Env so it can combine model transparency with each footprint opacity
925     glEnable( GL_TEXTURE_2D );
926     glActiveTexture( GL_TEXTURE0 );
927 
928     glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
929     glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
930     glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
931 
932     glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
933     glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
934 
935     glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
936     glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
937 
938     glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
939     glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
940     glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
941     glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_CONSTANT );
942 
943     render3dModels( false, true );
944     render3dModels( true, true );
945 
946     glDisable( GL_BLEND );
947     OglResetTextureState();
948 
949     glDepthMask( GL_TRUE );
950 
951     // Render Grid
952     if( m_boardAdapter.GetGridType() != GRID3D_TYPE::NONE )
953     {
954         glDisable( GL_LIGHTING );
955 
956         if( glIsList( m_grid ) )
957             glCallList( m_grid );
958 
959         glEnable( GL_LIGHTING );
960     }
961 
962     // Render 3D arrows
963     if( m_boardAdapter.GetFlag( FL_AXIS ) )
964         render3dArrows();
965 
966     // Return back to the original viewport (this is important if we want
967     // to take a screenshot after the render)
968     glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
969 
970     return false;
971 }
972 
973 
initializeOpenGL()974 bool RENDER_3D_OPENGL::initializeOpenGL()
975 {
976     glEnable( GL_LINE_SMOOTH );
977     glShadeModel( GL_SMOOTH );
978 
979     // 4-byte pixel alignment
980     glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
981 
982     // Initialize the open GL texture to draw the filled semi-circle of the segments
983     IMAGE* circleImage = new IMAGE( SIZE_OF_CIRCLE_TEXTURE, SIZE_OF_CIRCLE_TEXTURE );
984 
985     if( !circleImage )
986         return false;
987 
988     unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
989 
990     circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
991                                ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
992                                circleRadius,
993                                0xFF );
994 
995     IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
996 
997     circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
998 
999     m_circleTexture = OglLoadTexture( *circleImageBlured );
1000 
1001     delete circleImageBlured;
1002     circleImageBlured = nullptr;
1003 
1004     delete circleImage;
1005     circleImage = nullptr;
1006 
1007     init_lights();
1008 
1009     // Use this mode if you want see the triangle lines (debug proposes)
1010     //glPolygonMode( GL_FRONT_AND_BACK,  GL_LINE );
1011     m_is_opengl_initialized = true;
1012 
1013     return true;
1014 }
1015 
1016 
setArrowMaterial()1017 void RENDER_3D_OPENGL::setArrowMaterial()
1018 {
1019     glEnable( GL_COLOR_MATERIAL );
1020     glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
1021 
1022     const SFVEC4F ambient  = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1023     const SFVEC4F diffuse  = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1024     const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1025     const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
1026 
1027     glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
1028     glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
1029 
1030     glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT,  &ambient.r );
1031     glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE,  &diffuse.r );
1032     glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
1033 }
1034 
1035 
freeAllLists()1036 void RENDER_3D_OPENGL::freeAllLists()
1037 {
1038     if( glIsList( m_grid ) )
1039         glDeleteLists( m_grid, 1 );
1040 
1041     m_grid = 0;
1042 
1043     for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
1044     {
1045         OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
1046         delete pLayerDispList;
1047     }
1048 
1049     m_layers.clear();
1050 
1051     delete m_platedPadsFront;
1052     m_platedPadsFront = nullptr;
1053 
1054     delete m_platedPadsBack;
1055     m_platedPadsBack = nullptr;
1056 
1057     for( MAP_OGL_DISP_LISTS::const_iterator ii = m_outerLayerHoles.begin();
1058          ii != m_outerLayerHoles.end();
1059          ++ii )
1060     {
1061         OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
1062         delete pLayerDispList;
1063     }
1064 
1065     m_outerLayerHoles.clear();
1066 
1067     for( MAP_OGL_DISP_LISTS::const_iterator ii = m_innerLayerHoles.begin();
1068          ii != m_innerLayerHoles.end();
1069          ++ii )
1070     {
1071         OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
1072         delete pLayerDispList;
1073     }
1074 
1075     m_innerLayerHoles.clear();
1076 
1077     for( LIST_TRIANGLES::const_iterator ii = m_triangles.begin(); ii != m_triangles.end(); ++ii )
1078     {
1079         delete *ii;
1080     }
1081 
1082     m_triangles.clear();
1083 
1084     for( MAP_3DMODEL::const_iterator ii = m_3dModelMap.begin(); ii != m_3dModelMap.end(); ++ii )
1085     {
1086         MODEL_3D* pointer = static_cast<MODEL_3D*>(ii->second);
1087         delete pointer;
1088     }
1089 
1090     m_3dModelMap.clear();
1091 
1092     delete m_board;
1093     m_board = nullptr;
1094 
1095     delete m_boardWithHoles;
1096     m_boardWithHoles = nullptr;
1097 
1098     delete m_antiBoard;
1099     m_antiBoard = nullptr;
1100 
1101     delete m_outerThroughHoles;
1102     m_outerThroughHoles = nullptr;
1103 
1104     delete m_outerViaThroughHoles;
1105     m_outerViaThroughHoles = nullptr;
1106 
1107     delete m_outerThroughHoleRings;
1108     m_outerThroughHoleRings = nullptr;
1109 
1110     delete m_vias;
1111     m_vias = nullptr;
1112 
1113     delete m_padHoles;
1114     m_padHoles = nullptr;
1115 }
1116 
1117 
renderSolderMaskLayer(PCB_LAYER_ID aLayerID,float aZPosition,bool aDrawMiddleSegments,bool aSkipRenderHoles)1118 void RENDER_3D_OPENGL::renderSolderMaskLayer( PCB_LAYER_ID aLayerID, float aZPosition,
1119                                               bool aDrawMiddleSegments, bool aSkipRenderHoles )
1120 {
1121     wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
1122 
1123     float nonCopperThickness = m_boardAdapter.GetNonCopperLayerThickness();
1124 
1125     if( m_board )
1126     {
1127         if( m_layers.find( aLayerID ) != m_layers.end() )
1128         {
1129             OPENGL_RENDER_LIST* pLayerDispListMask = m_layers.at( aLayerID );
1130 
1131             if( m_outerViaThroughHoles )
1132                 m_outerViaThroughHoles->ApplyScalePosition( aZPosition, nonCopperThickness );
1133 
1134             m_board->ApplyScalePosition( aZPosition, nonCopperThickness );
1135 
1136             setLayerMaterial( aLayerID );
1137 
1138             m_board->SetItIsTransparent( true );
1139 
1140             if( aSkipRenderHoles )
1141             {
1142                 m_board->DrawAllCameraCulled( m_camera.GetPos().z, aDrawMiddleSegments );
1143             }
1144             else
1145             {
1146                 m_board->DrawAllCameraCulledSubtractLayer( aDrawMiddleSegments, pLayerDispListMask,
1147                                                            m_outerViaThroughHoles );
1148             }
1149         }
1150         else
1151         {
1152             // This case there is no layer with mask, so we will render the full board as mask
1153             if( m_outerViaThroughHoles )
1154                 m_outerViaThroughHoles->ApplyScalePosition( aZPosition, nonCopperThickness );
1155 
1156             m_board->ApplyScalePosition( aZPosition, nonCopperThickness );
1157 
1158             setLayerMaterial( aLayerID );
1159 
1160             m_board->SetItIsTransparent( true );
1161 
1162             if( aSkipRenderHoles )
1163             {
1164                 m_board->DrawAllCameraCulled( m_camera.GetPos().z, aDrawMiddleSegments );
1165             }
1166             else
1167             {
1168                 m_board->DrawAllCameraCulledSubtractLayer( aDrawMiddleSegments,
1169                                                            m_outerViaThroughHoles );
1170             }
1171         }
1172     }
1173 }
1174 
1175 
render3dModelsSelected(bool aRenderTopOrBot,bool aRenderTransparentOnly,bool aRenderSelectedOnly)1176 void RENDER_3D_OPENGL::render3dModelsSelected( bool aRenderTopOrBot, bool aRenderTransparentOnly,
1177                                                bool aRenderSelectedOnly )
1178 {
1179     if( !m_boardAdapter.GetBoard() )
1180         return;
1181 
1182     MODEL_3D::BeginDrawMulti( !aRenderSelectedOnly );
1183 
1184     // Go for all footprints
1185     for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1186     {
1187         bool highlight = false;
1188 
1189         if( m_boardAdapter.GetFlag( FL_USE_SELECTION ) )
1190         {
1191             if( fp->IsSelected() )
1192                 highlight = true;
1193 
1194             if( m_boardAdapter.GetFlag( FL_HIGHLIGHT_ROLLOVER_ITEM ) && fp == m_currentRollOverItem )
1195                 highlight = true;
1196 
1197             if( aRenderSelectedOnly != highlight )
1198                 continue;
1199         }
1200 
1201         if( !fp->Models().empty() )
1202         {
1203             if( m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
1204             {
1205                 if( aRenderTopOrBot == !fp->IsFlipped() )
1206                     renderFootprint( fp, aRenderTransparentOnly, highlight );
1207             }
1208         }
1209     }
1210 
1211     MODEL_3D::EndDrawMulti();
1212 }
1213 
1214 
render3dModels(bool aRenderTopOrBot,bool aRenderTransparentOnly)1215 void RENDER_3D_OPENGL::render3dModels( bool aRenderTopOrBot, bool aRenderTransparentOnly )
1216 {
1217     if( m_boardAdapter.GetFlag( FL_USE_SELECTION ) )
1218         render3dModelsSelected( aRenderTopOrBot, aRenderTransparentOnly, true );
1219 
1220     render3dModelsSelected( aRenderTopOrBot, aRenderTransparentOnly, false );
1221 }
1222 
1223 
renderFootprint(const FOOTPRINT * aFootprint,bool aRenderTransparentOnly,bool aIsSelected)1224 void RENDER_3D_OPENGL::renderFootprint( const FOOTPRINT* aFootprint, bool aRenderTransparentOnly,
1225                                         bool aIsSelected )
1226 {
1227     if( !aFootprint->Models().empty() )
1228     {
1229         const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1230 
1231         glPushMatrix();
1232 
1233         wxPoint pos = aFootprint->GetPosition();
1234 
1235         glTranslatef( pos.x * m_boardAdapter.BiuTo3dUnits(), -pos.y * m_boardAdapter.BiuTo3dUnits(),
1236                       zpos );
1237 
1238         if( aFootprint->GetOrientation() )
1239             glRotated( (double) aFootprint->GetOrientation() / 10.0, 0.0, 0.0, 1.0 );
1240 
1241         if( aFootprint->IsFlipped() )
1242         {
1243             glRotatef( 180.0f, 0.0f, 1.0f, 0.0f );
1244             glRotatef( 180.0f, 0.0f, 0.0f, 1.0f );
1245         }
1246 
1247         double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1248 
1249         glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
1250                   modelunit_to_3d_units_factor );
1251 
1252         // Get the list of model files for this model
1253         for( const FP_3DMODEL& sM : aFootprint->Models() )
1254         {
1255             if( !sM.m_Show || sM.m_Filename.empty() )
1256                 continue;
1257 
1258             // Check if the model is present in our cache map
1259             auto cache_i = m_3dModelMap.find( sM.m_Filename );
1260 
1261             if( cache_i == m_3dModelMap.end() )
1262                 continue;
1263 
1264             if( const MODEL_3D* modelPtr = cache_i->second )
1265             {
1266                 bool opaque = sM.m_Opacity >= 1.0;
1267 
1268                 if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1269                     ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1270                 {
1271                     glPushMatrix();
1272 
1273                     // FIXME: don't do this over and over again unless the
1274                     // values have changed.  cache the matrix somewhere.
1275                     glm::mat4 mtx( 1 );
1276                     mtx = glm::translate( mtx, { sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z } );
1277                     mtx = glm::rotate( mtx, glm::radians( (float) -sM.m_Rotation.z ),
1278                                        { 0.0f, 0.0f, 1.0f } );
1279                     mtx = glm::rotate( mtx, glm::radians( (float) -sM.m_Rotation.y ),
1280                                        { 0.0f, 1.0f, 0.0f } );
1281                     mtx = glm::rotate( mtx, glm::radians( (float) -sM.m_Rotation.x ),
1282                                        { 1.0f, 0.0f, 0.0f } );
1283                     mtx = glm::scale( mtx, { sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z } );
1284                     glMultMatrixf( glm::value_ptr( mtx ) );
1285 
1286                     if( aRenderTransparentOnly )
1287                     {
1288                         modelPtr->DrawTransparent( sM.m_Opacity,
1289                                                    aFootprint->IsSelected() || aIsSelected,
1290                                                    m_boardAdapter.m_OpenGlSelectionColor );
1291                     }
1292                     else
1293                     {
1294                         modelPtr->DrawOpaque( aFootprint->IsSelected() || aIsSelected,
1295                                               m_boardAdapter.m_OpenGlSelectionColor );
1296                     }
1297 
1298                     if( m_boardAdapter.GetFlag( FL_RENDER_OPENGL_SHOW_MODEL_BBOX ) )
1299                     {
1300                         glEnable( GL_BLEND );
1301                         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1302 
1303                         glDisable( GL_LIGHTING );
1304 
1305                         glLineWidth( 1 );
1306                         modelPtr->DrawBboxes();
1307 
1308                         glLineWidth( 4 );
1309                         modelPtr->DrawBbox();
1310 
1311                         glEnable( GL_LIGHTING );
1312                         glDisable( GL_BLEND );
1313                     }
1314 
1315                     glPopMatrix();
1316                 }
1317             }
1318         }
1319 
1320         glPopMatrix();
1321     }
1322 }
1323 
1324 
generate3dGrid(GRID3D_TYPE aGridType)1325 void RENDER_3D_OPENGL::generate3dGrid( GRID3D_TYPE aGridType )
1326 {
1327     if( glIsList( m_grid ) )
1328         glDeleteLists( m_grid, 1 );
1329 
1330     m_grid = 0;
1331 
1332     if( aGridType == GRID3D_TYPE::NONE )
1333         return;
1334 
1335     m_grid = glGenLists( 1 );
1336 
1337     if( !glIsList( m_grid ) )
1338         return;
1339 
1340     glNewList( m_grid, GL_COMPILE );
1341 
1342     glEnable( GL_BLEND );
1343     glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1344 
1345     const double zpos = 0.0;
1346 
1347     // Color of grid lines
1348     const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1349 
1350     // Color of grid lines every 5 lines
1351     const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1352     const double  scale            = m_boardAdapter.BiuTo3dUnits();
1353     const GLfloat transparency     = 0.35f;
1354 
1355     double griSizeMM = 0.0;
1356 
1357     switch( aGridType )
1358     {
1359     default:
1360     case GRID3D_TYPE::NONE:
1361         return;
1362     case GRID3D_TYPE::GRID_1MM:
1363         griSizeMM = 1.0;
1364         break;
1365     case GRID3D_TYPE::GRID_2P5MM:
1366         griSizeMM = 2.5;
1367         break;
1368     case GRID3D_TYPE::GRID_5MM:
1369         griSizeMM = 5.0;
1370         break;
1371     case GRID3D_TYPE::GRID_10MM:
1372         griSizeMM = 10.0;
1373         break;
1374     }
1375 
1376     glNormal3f( 0.0, 0.0, 1.0 );
1377 
1378     const wxSize brd_size = m_boardAdapter.GetBoardSize();
1379     wxPoint brd_center_pos = m_boardAdapter.GetBoardPos();
1380 
1381     brd_center_pos.y = -brd_center_pos.y;
1382 
1383     const int xsize = std::max( brd_size.x, Millimeter2iu( 100 ) ) * 1.2;
1384     const int ysize = std::max( brd_size.y, Millimeter2iu( 100 ) ) * 1.2;
1385 
1386     // Grid limits, in 3D units
1387     double  xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1388     double  xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1389     double  ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1390     double  ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1391     double  zmin = Millimeter2iu( -50 ) * scale;
1392     double  zmax = Millimeter2iu( 100 ) * scale;
1393 
1394     // Set rasterised line width (min value = 1)
1395     glLineWidth( 1 );
1396 
1397     // Draw horizontal grid centered on 3D origin (center of the board)
1398     for( int ii = 0; ; ii++ )
1399     {
1400         if( (ii % 5) )
1401             glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1402         else
1403             glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1404                        transparency );
1405 
1406         const int delta = KiROUND( ii * griSizeMM * IU_PER_MM );
1407 
1408         if( delta <= xsize / 2 )    // Draw grid lines parallel to X axis
1409         {
1410             glBegin( GL_LINES );
1411             glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1412             glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1413             glEnd();
1414 
1415             if( ii != 0 )
1416             {
1417                 glBegin( GL_LINES );
1418                 glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1419                 glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1420                 glEnd();
1421             }
1422         }
1423 
1424         if( delta <= ysize / 2 )    // Draw grid lines parallel to Y axis
1425         {
1426             glBegin( GL_LINES );
1427             glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1428             glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1429             glEnd();
1430 
1431             if( ii != 0 )
1432             {
1433                 glBegin( GL_LINES );
1434                 glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1435                 glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1436                 glEnd();
1437             }
1438         }
1439 
1440         if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1441             break;
1442     }
1443 
1444     // Draw vertical grid on Z axis
1445     glNormal3f( 0.0, -1.0, 0.0 );
1446 
1447     // Draw vertical grid lines (parallel to Z axis)
1448     double posy = -brd_center_pos.y * scale;
1449 
1450     for( int ii = 0; ; ii++ )
1451     {
1452         if( (ii % 5) )
1453             glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1454         else
1455             glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1456                        transparency );
1457 
1458         const double delta = ii * griSizeMM * IU_PER_MM;
1459 
1460         glBegin( GL_LINES );
1461         xmax = ( brd_center_pos.x + delta ) * scale;
1462 
1463         glVertex3f( xmax, posy, zmin );
1464         glVertex3f( xmax, posy, zmax );
1465         glEnd();
1466 
1467         if( ii != 0 )
1468         {
1469             glBegin( GL_LINES );
1470             xmin = ( brd_center_pos.x - delta ) * scale;
1471             glVertex3f( xmin, posy, zmin );
1472             glVertex3f( xmin, posy, zmax );
1473             glEnd();
1474         }
1475 
1476         if( delta > xsize / 2.0f )
1477             break;
1478     }
1479 
1480     // Draw horizontal grid lines on Z axis (parallel to X axis)
1481     for( int ii = 0; ; ii++ )
1482     {
1483         if( ii % 5 )
1484             glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1485         else
1486             glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1487 
1488         const double delta = ii * griSizeMM * IU_PER_MM * scale;
1489 
1490         if( delta <= zmax )
1491         {
1492             // Draw grid lines on Z axis (positive Z axis coordinates)
1493             glBegin( GL_LINES );
1494             glVertex3f( xmin, posy, delta );
1495             glVertex3f( xmax, posy, delta );
1496             glEnd();
1497         }
1498 
1499         if( delta <= -zmin && ( ii != 0 ) )
1500         {
1501             // Draw grid lines on Z axis (negative Z axis coordinates)
1502             glBegin( GL_LINES );
1503             glVertex3f( xmin, posy, -delta );
1504             glVertex3f( xmax, posy, -delta );
1505             glEnd();
1506         }
1507 
1508         if( ( delta > zmax ) && ( delta > -zmin ) )
1509             break;
1510     }
1511 
1512     glDisable( GL_BLEND );
1513 
1514     glEndList();
1515 }
1516