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