KiCad PCB EDA Suite
Loading...
Searching...
No Matches
render_3d_opengl.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2020 Mario Luzeiro <[email protected]>
5 * Copyright (C) 2023 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <cstdint>
23#include <kicad_gl/kiglad.h> // Must be included first
24
26#include "render_3d_opengl.h"
27#include "opengl_utils.h"
30#include <board.h>
31#include <footprint.h>
33#include <3d_math.h>
34#include <glm/geometric.hpp>
35#include <lset.h>
36#include <pgm_base.h>
37#include <math/util.h> // for KiROUND
38#include <base_units.h>
39
40#include <utility>
41#include <vector>
42
43#include <wx/log.h>
44#include <wx/utils.h>
45
49#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
50
52 RENDER_3D_BASE( aAdapter, aCamera ),
53 m_canvas( aCanvas )
54{
55 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
56
57 m_layers.clear();
58 m_outerLayerHoles.clear();
59 m_innerLayerHoles.clear();
60 m_triangles.clear();
61 m_board = nullptr;
62 m_antiBoard = nullptr;
63
64 m_platedPadsFront = nullptr;
65 m_platedPadsBack = nullptr;
66 m_offboardPadsFront = nullptr;
67 m_offboardPadsBack = nullptr;
68
69 m_outerThroughHoles = nullptr;
71 m_outerViaThroughHoles = nullptr;
72 m_microviaHoles = nullptr;
73 m_padHoles = nullptr;
74 m_viaFrontCover = nullptr;
75 m_viaBackCover = nullptr;
76
78 m_grid = 0;
80 m_currentRollOverItem = nullptr;
81 m_boardWithHoles = nullptr;
82 m_postMachinePlugs = nullptr;
83
84 m_3dModelMap.clear();
85
86 m_spheres_gizmo = new SPHERES_GIZMO( 4, 4 );
87}
88
89
91{
92 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::~RENDER_3D_OPENGL" ) );
93
95
97
98 m_placeholderModel.reset();
99
101 glDeleteTextures( 1, &m_circleTexture );
102
103 delete m_spheres_gizmo;
104}
105
106
108{
109 return 50; // ms
110}
111
112
113void RENDER_3D_OPENGL::SetCurWindowSize( const wxSize& aSize )
114{
115 if( m_windowSize != aSize )
116 {
117 int viewport[4];
118 int fbWidth, fbHeight;
119 glGetIntegerv( GL_VIEWPORT, viewport );
120
121 m_windowSize = aSize;
122 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
124 // Initialize here any screen dependent data here
125 }
126}
127
128
130{
131 if( enabled )
132 glEnable( GL_LIGHT0 );
133 else
134 glDisable( GL_LIGHT0 );
135}
136
137
139{
140 if( enabled )
141 glEnable( GL_LIGHT1 );
142 else
143 glDisable( GL_LIGHT1 );
144}
145
146
148{
149 if( enabled )
150 glEnable( GL_LIGHT2 );
151 else
152 glDisable( GL_LIGHT2 );
153}
154
155
157{
158 m_spheres_gizmo->resetSelectedGizmoSphere();
159}
160
161
166
167
168void RENDER_3D_OPENGL::setGizmoViewport( int x, int y, int width, int height )
169{
170 m_spheres_gizmo->setViewport( x, y, width, height );
171}
172
173
174std::tuple<int, int, int, int> RENDER_3D_OPENGL::getGizmoViewport() const
175{
176 return m_spheres_gizmo->getViewport();
177}
178
179
180void RENDER_3D_OPENGL::handleGizmoMouseInput( int mouseX, int mouseY )
181{
182 m_spheres_gizmo->handleMouseInput( mouseX, mouseY );
183}
184
185
186void RENDER_3D_OPENGL::updateGizmoSelection( glm::mat4 aCameraRotationMatrix )
187{
188 m_spheres_gizmo->updateSelection( aCameraRotationMatrix );
189}
190
191
193{
194 m_materials = {};
195
196 // http://devernay.free.fr/cours/opengl/materials.html
197
198 // Plated copper
199 // Copper material mixed with the copper color
200 m_materials.m_Copper.m_Ambient = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.1f,
201 m_boardAdapter.m_CopperColor.g * 0.1f,
202 m_boardAdapter.m_CopperColor.b * 0.1f);
203
204 m_materials.m_Copper.m_Specular = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.75f + 0.25f,
205 m_boardAdapter.m_CopperColor.g * 0.75f + 0.25f,
206 m_boardAdapter.m_CopperColor.b * 0.75f + 0.25f );
207
208 // This guess the material type(ex: copper vs gold) to determine the
209 // shininess factor between 0.1 and 0.4
210 float shininessfactor = 0.40f - mapf( fabs( m_boardAdapter.m_CopperColor.r -
211 m_boardAdapter.m_CopperColor.g ),
212 0.15f, 1.00f,
213 0.00f, 0.30f );
214
215 m_materials.m_Copper.m_Shininess = shininessfactor * 128.0f;
216 m_materials.m_Copper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
217
218
219 // Non plated copper (raw copper)
220 m_materials.m_NonPlatedCopper.m_Ambient = SFVEC3F( 0.191f, 0.073f, 0.022f );
221 m_materials.m_NonPlatedCopper.m_Diffuse = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f,
222 50.0f / 255.0f );
223 m_materials.m_NonPlatedCopper.m_Specular = SFVEC3F( 0.256f, 0.137f, 0.086f );
224 m_materials.m_NonPlatedCopper.m_Shininess = 0.1f * 128.0f;
225 m_materials.m_NonPlatedCopper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
226
227 // Paste material mixed with paste color
228 m_materials.m_Paste.m_Ambient = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r,
229 m_boardAdapter.m_SolderPasteColor.g,
230 m_boardAdapter.m_SolderPasteColor.b );
231
232 m_materials.m_Paste.m_Specular = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r *
233 m_boardAdapter.m_SolderPasteColor.r,
234 m_boardAdapter.m_SolderPasteColor.g *
235 m_boardAdapter.m_SolderPasteColor.g,
236 m_boardAdapter.m_SolderPasteColor.b *
237 m_boardAdapter.m_SolderPasteColor.b );
238
239 m_materials.m_Paste.m_Shininess = 0.1f * 128.0f;
240 m_materials.m_Paste.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
241
242 // Silk screen material mixed with silk screen color
243 m_materials.m_SilkSTop.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorTop.r,
244 m_boardAdapter.m_SilkScreenColorTop.g,
245 m_boardAdapter.m_SilkScreenColorTop.b );
246
247 m_materials.m_SilkSTop.m_Specular = SFVEC3F(
248 m_boardAdapter.m_SilkScreenColorTop.r * m_boardAdapter.m_SilkScreenColorTop.r + 0.10f,
249 m_boardAdapter.m_SilkScreenColorTop.g * m_boardAdapter.m_SilkScreenColorTop.g + 0.10f,
250 m_boardAdapter.m_SilkScreenColorTop.b * m_boardAdapter.m_SilkScreenColorTop.b + 0.10f );
251
252 m_materials.m_SilkSTop.m_Shininess = 0.078125f * 128.0f;
253 m_materials.m_SilkSTop.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
254
255 // Silk screen material mixed with silk screen color
256 m_materials.m_SilkSBot.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorBot.r,
257 m_boardAdapter.m_SilkScreenColorBot.g,
258 m_boardAdapter.m_SilkScreenColorBot.b );
259
260 m_materials.m_SilkSBot.m_Specular = SFVEC3F(
261 m_boardAdapter.m_SilkScreenColorBot.r * m_boardAdapter.m_SilkScreenColorBot.r + 0.10f,
262 m_boardAdapter.m_SilkScreenColorBot.g * m_boardAdapter.m_SilkScreenColorBot.g + 0.10f,
263 m_boardAdapter.m_SilkScreenColorBot.b * m_boardAdapter.m_SilkScreenColorBot.b + 0.10f );
264
265 m_materials.m_SilkSBot.m_Shininess = 0.078125f * 128.0f;
266 m_materials.m_SilkSBot.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
267
268 m_materials.m_SolderMask.m_Shininess = 0.8f * 128.0f;
269 m_materials.m_SolderMask.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
270
271 // Epoxy material
272 m_materials.m_EpoxyBoard.m_Ambient = SFVEC3F( 117.0f / 255.0f, 97.0f / 255.0f,
273 47.0f / 255.0f );
274
275 m_materials.m_EpoxyBoard.m_Specular = SFVEC3F( 18.0f / 255.0f, 3.0f / 255.0f,
276 20.0f / 255.0f );
277
278 m_materials.m_EpoxyBoard.m_Shininess = 0.1f * 128.0f;
279 m_materials.m_EpoxyBoard.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
280}
281
282
284{
285 if( m_boardAdapter.GetUseBoardEditorCopperLayerColors() && IsCopperLayer( aLayerID ) )
286 {
287 COLOR4D copper_color = m_boardAdapter.m_BoardEditorColors[aLayerID];
288 m_materials.m_Copper.m_Diffuse = SFVEC3F( copper_color.r, copper_color.g,
289 copper_color.b );
290 OglSetMaterial( m_materials.m_Copper, 1.0f );
291 m_materials.m_NonPlatedCopper.m_Diffuse = m_materials.m_Copper.m_Diffuse;
292 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
293
294 return;
295 }
296
297 switch( aLayerID )
298 {
299 case F_Mask:
300 case B_Mask:
301 {
302 const SFVEC4F layerColor = aLayerID == F_Mask ? m_boardAdapter.m_SolderMaskColorTop
303 : m_boardAdapter.m_SolderMaskColorBot;
304
305 m_materials.m_SolderMask.m_Diffuse = layerColor;
306
307 // Convert Opacity to Transparency
308 m_materials.m_SolderMask.m_Transparency = 1.0f - layerColor.a;
309
310 m_materials.m_SolderMask.m_Ambient = m_materials.m_SolderMask.m_Diffuse * 0.3f;
311
312 m_materials.m_SolderMask.m_Specular = m_materials.m_SolderMask.m_Diffuse
313 * m_materials.m_SolderMask.m_Diffuse;
314
315 OglSetMaterial( m_materials.m_SolderMask, 1.0f );
316 break;
317 }
318
319 case B_Paste:
320 case F_Paste:
321 m_materials.m_Paste.m_Diffuse = m_boardAdapter.m_SolderPasteColor;
322 OglSetMaterial( m_materials.m_Paste, 1.0f );
323 break;
324
325 case B_SilkS:
326 m_materials.m_SilkSBot.m_Diffuse = m_boardAdapter.m_SilkScreenColorBot;
327 OglSetMaterial( m_materials.m_SilkSBot, 1.0f );
328 break;
329
330 case F_SilkS:
331 m_materials.m_SilkSTop.m_Diffuse = m_boardAdapter.m_SilkScreenColorTop;
332 OglSetMaterial( m_materials.m_SilkSTop, 1.0f );
333 break;
334
335 case B_Adhes:
336 case F_Adhes:
337 case Dwgs_User:
338 case Cmts_User:
339 case Eco1_User:
340 case Eco2_User:
341 case Edge_Cuts:
342 case Margin:
343 case B_CrtYd:
344 case F_CrtYd:
345 case B_Fab:
346 case F_Fab:
347 switch( aLayerID )
348 {
349 case Dwgs_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
350 case Cmts_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserCommentsColor; break;
351 case Eco1_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO1Color; break;
352 case Eco2_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO2Color; break;
353 case Edge_Cuts: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
354 case Margin: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
355 case F_Fab: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_FFabColor; break;
356 case B_Fab: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_BFabColor; break;
357 case F_CrtYd: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_FCourtyardColor; break;
358 case B_CrtYd: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_BCourtyardColor; break;
359 default:
360 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.GetLayerColor( aLayerID );
361 break;
362 }
363
364 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
365 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
366 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
367
368 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
369 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
370 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
371
372 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
373 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
374 OglSetMaterial( m_materials.m_Plastic, 1.0f );
375 break;
376
377 default:
378 {
379 int layer3D = MapPCBLayerTo3DLayer( aLayerID );
380
381 // Note: MUST do this in LAYER_3D space; User_1..User_45 are NOT contiguous
382 if( layer3D >= LAYER_3D_USER_1 && layer3D <= LAYER_3D_USER_45 )
383 {
384 int user_idx = layer3D - LAYER_3D_USER_1;
385
386 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDefinedLayerColor[ user_idx ];
387 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
388 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
389 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
390
391 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
392 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
393 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
394
395 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
396 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
397 OglSetMaterial( m_materials.m_Plastic, 1.0f );
398 break;
399 }
400
401 m_materials.m_Copper.m_Diffuse = m_boardAdapter.m_CopperColor;
402 OglSetMaterial( m_materials.m_Copper, 1.0f );
403 break;
404 }
405 }
406}
407
408
410{
411 // Setup light
412 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
413 const GLfloat ambient[] = { 0.084f, 0.084f, 0.084f, 1.0f };
414 const GLfloat diffuse0[] = { 0.3f, 0.3f, 0.3f, 1.0f };
415 const GLfloat specular0[] = { 0.5f, 0.5f, 0.5f, 1.0f };
416
417 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
418 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse0 );
419 glLightfv( GL_LIGHT0, GL_SPECULAR, specular0 );
420
421 const GLfloat diffuse12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
422 const GLfloat specular12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
423
424 // defines a directional light that points along the negative z-axis
425 GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
426
427 // This makes a vector slight not perpendicular with XZ plane
428 const SFVEC3F vectorLight = SphericalToCartesian( glm::pi<float>() * 0.03f,
429 glm::pi<float>() * 0.25f );
430
431 position[0] = vectorLight.x;
432 position[1] = vectorLight.y;
433 position[2] = vectorLight.z;
434
435 glLightfv( GL_LIGHT1, GL_AMBIENT, ambient );
436 glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse12 );
437 glLightfv( GL_LIGHT1, GL_SPECULAR, specular12 );
438 glLightfv( GL_LIGHT1, GL_POSITION, position );
439
440 // defines a directional light that points along the positive z-axis
441 position[2] = -position[2];
442
443 glLightfv( GL_LIGHT2, GL_AMBIENT, ambient );
444 glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse12 );
445 glLightfv( GL_LIGHT2, GL_SPECULAR, specular12 );
446 glLightfv( GL_LIGHT2, GL_POSITION, position );
447
448 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
449
450 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
451
452 glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
453}
454
455
457{
458 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
459}
460
461
463{
464 glEnable( GL_POLYGON_OFFSET_FILL );
465 glPolygonOffset( -0.1f, -2.0f );
466 setLayerMaterial( aLayer_id );
467}
468
469
471{
472 glDisable( GL_POLYGON_OFFSET_FILL );
473}
474
475
476void RENDER_3D_OPENGL::renderBoardBody( bool aSkipRenderHoles )
477{
478 m_materials.m_EpoxyBoard.m_Diffuse = m_boardAdapter.m_BoardBodyColor;
479
480 // opacity to transparency
481 m_materials.m_EpoxyBoard.m_Transparency = 1.0f - m_boardAdapter.m_BoardBodyColor.a;
482
483 OglSetMaterial( m_materials.m_EpoxyBoard, 1.0f );
484
485 std::shared_ptr<OPENGL_RENDER_LIST_DEFERRED> board_disp_list_def = nullptr;
486
487 if( aSkipRenderHoles || !m_boardWithHoles )
488 board_disp_list_def = m_board;
489 else
490 board_disp_list_def = m_boardWithHoles;
491
492 if( board_disp_list_def )
493 {
494 // Constructs the actual OpenGL render list with correct GL context we're in
495 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = board_disp_list_def->MakeOrGet() )
496 {
497 ogl_disp_list->ApplyScalePosition( -m_boardAdapter.GetBoardBodyThickness() / 2.0f,
498 m_boardAdapter.GetBoardBodyThickness() );
499
500 ogl_disp_list->SetItIsTransparent( true );
501 ogl_disp_list->DrawAll();
502 }
503 }
504
505 // Also render post-machining plugs (board material that remains after backdrill/counterbore/countersink)
506 if( !aSkipRenderHoles && m_postMachinePlugs )
507 {
508 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = m_postMachinePlugs->MakeOrGet() )
509 {
510 ogl_disp_list->ApplyScalePosition( -m_boardAdapter.GetBoardBodyThickness() / 2.0f,
511 m_boardAdapter.GetBoardBodyThickness() );
512
513 ogl_disp_list->SetItIsTransparent( true );
514 ogl_disp_list->DrawAll();
515 }
516 }
517}
518
519
520static inline SFVEC4F premultiplyAlpha( const SFVEC4F& aInput )
521{
522 return SFVEC4F( aInput.r * aInput.a, aInput.g * aInput.a, aInput.b * aInput.a, aInput.a );
523}
524
525
526bool RENDER_3D_OPENGL::Redraw( bool aIsMoving )
527{
528 // Initialize OpenGL
530 {
531 if( !initializeOpenGL() )
532 return false;
533 }
534
536
537 const bool reloadRequested = m_reloadRequested;
538
539 if( reloadRequested )
540 {
541 std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
542
544 m_activityReporter->Report( _( "Loading..." ) );
545
546 // Careful here!
547 // We are in the middle of rendering and the reload method may show
548 // a dialog box that requires the opengl context for a redraw.
549 //
550 // reload() runs outside m_renderMutex: it stops/joins the previous
551 // background worker, and that worker needs the mutex to publish
552 // intermediate results before it can exit.
554 {
555 reload();
556 } );
557 }
558
559 const GRID3D_TYPE gridType = static_cast<GRID3D_TYPE>( cfg.grid_type );
560
561 // Regenerate after reload (board size may have changed) or when the grid type setting changes.
562 if( reloadRequested || gridType != m_lastGridType )
563 {
564 m_lastGridType = gridType;
566 }
567
569
570 std::lock_guard<std::recursive_mutex> lock( m_renderMutex );
571
573
574 // Initial setup
575 glDepthFunc( GL_LESS );
576 glEnable( GL_CULL_FACE );
577 glFrontFace( GL_CCW ); // This is the OpenGL default
578 glEnable( GL_NORMALIZE ); // This allow OpenGL to normalize the normals after transformations
579 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
580
581 if( aIsMoving && cfg.opengl_AA_disableOnMove )
582 glDisable( GL_MULTISAMPLE );
583 else
584 glEnable( GL_MULTISAMPLE );
585
586 // clear color and depth buffers
587 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
588 glClearDepth( 1.0f );
589 glClearStencil( 0x00 );
590 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
591
593
594 // Draw the background ( rectangle with color gradient)
596 premultiplyAlpha( m_boardAdapter.m_BgColorBot ) );
597
598 glEnable( GL_DEPTH_TEST );
599
600 // Set projection and modelview matrixes
601 glMatrixMode( GL_PROJECTION );
602 glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
603 glMatrixMode( GL_MODELVIEW );
604 glLoadIdentity();
605 glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
606
607 // Position the headlight
608 setLightFront( true );
609 setLightTop( true );
610 setLightBottom( true );
611
612 glEnable( GL_LIGHTING );
613
614 {
615 const SFVEC3F& cameraPos = m_camera.GetPos();
616
617 // Place the light at a minimum Z so the diffuse factor will not drop
618 // and the board will still look with good light.
619 float zpos;
620
621 if( cameraPos.z > 0.0f )
622 zpos = glm::max( cameraPos.z, 0.5f ) + cameraPos.z * cameraPos.z;
623 else
624 zpos = glm::min( cameraPos.z,-0.5f ) - cameraPos.z * cameraPos.z;
625
626 // This is a point light.
627 const GLfloat headlight_pos[] = { cameraPos.x, cameraPos.y, zpos, 1.0f };
628
629 glLightfv( GL_LIGHT0, GL_POSITION, headlight_pos );
630 }
631
632 bool skipThickness = aIsMoving && cfg.opengl_thickness_disableOnMove;
633 bool skipRenderHoles = aIsMoving && cfg.opengl_holes_disableOnMove;
634 bool skipRenderMicroVias = aIsMoving && cfg.opengl_microvias_disableOnMove;
635 bool showThickness = !skipThickness;
636
637 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
638
640
641 if( !( skipRenderMicroVias || skipRenderHoles ) && m_microviaHoles )
642 {
643 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = m_microviaHoles->MakeOrGet() )
644 ogl_disp_list->DrawAll();
645 }
646
647 if( !skipRenderHoles && m_padHoles )
648 {
649 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = m_padHoles->MakeOrGet() )
650 ogl_disp_list->DrawAll();
651 }
652
653 // Display copper and tech layers
654 for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
655 {
656 const PCB_LAYER_ID layer = ( PCB_LAYER_ID )( ii->first );
657 bool isSilkLayer = layer == F_SilkS || layer == B_SilkS;
658 bool isMaskLayer = layer == F_Mask || layer == B_Mask;
659 bool isPasteLayer = layer == F_Paste || layer == B_Paste;
660
661 // Mask layers are not processed here because they are a special case
662 if( isMaskLayer )
663 continue;
664
665 // Do not show inner layers when it is displaying the board and board body is opaque
666 // enough: the time to create inner layers can be *really significant*.
667 // So avoid creating them is they are not very visible
668 const double opacity_min = 0.8;
669
670 if( layerFlags.test( LAYER_3D_BOARD ) && m_boardAdapter.m_BoardBodyColor.a > opacity_min )
671 {
672 // generating internal copper layers is time consuming. so skip them
673 // if the board body is masking them (i.e. if the opacity is near 1.0)
674 // B_Cu is layer 2 and all inner layers are higher values
675 if( layer > B_Cu && IsCopperLayer( layer ) )
676 continue;
677 }
678
679 glPushMatrix();
680
681 std::shared_ptr<OPENGL_RENDER_LIST_DEFERRED> pLayerDispListDef = ii->second;
682 std::shared_ptr<OPENGL_RENDER_LIST> pLayerDispList = pLayerDispListDef->MakeOrGet();
683
684 if( IsCopperLayer( layer ) )
685 {
686 if( cfg.DifferentiatePlatedCopper() )
688 else
689 setLayerMaterial( layer );
690
691 std::shared_ptr<OPENGL_RENDER_LIST> anti_board = m_antiBoard ? m_antiBoard->MakeOrGet() : nullptr;
692
693 std::shared_ptr<OPENGL_RENDER_LIST> outerTH = nullptr;
694 std::shared_ptr<OPENGL_RENDER_LIST> viaHoles = nullptr;
695
696 if( !skipRenderHoles )
697 {
698 outerTH = m_outerThroughHoles ? m_outerThroughHoles->MakeOrGet() : nullptr;
699 viaHoles = m_outerLayerHoles[layer] ? m_outerLayerHoles[layer]->MakeOrGet() : nullptr;
700 }
701
702 if( anti_board )
703 anti_board->ApplyScalePosition( pLayerDispList );
704
705 if( outerTH )
706 outerTH->ApplyScalePosition( pLayerDispList );
707
708 pLayerDispList->DrawCulled( showThickness, outerTH, viaHoles, anti_board );
709
710 // Draw plated & offboard pads
711 if( layer == F_Cu && ( m_platedPadsFront || m_offboardPadsFront ) )
712 {
714
716 {
717 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = m_platedPadsFront->MakeOrGet() )
718 ogl_disp_list->DrawCulled( showThickness, outerTH, viaHoles, anti_board );
719 }
720
722 {
723 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = m_offboardPadsFront->MakeOrGet() )
724 ogl_disp_list->DrawCulled( showThickness, outerTH, viaHoles );
725 }
726 }
727 else if( layer == B_Cu && ( m_platedPadsBack || m_offboardPadsBack ) )
728 {
730
731 if( m_platedPadsBack )
732 {
733 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = m_platedPadsBack->MakeOrGet() )
734 ogl_disp_list->DrawCulled( showThickness, outerTH, viaHoles, anti_board );
735 }
736
738 {
739 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_disp_list = m_offboardPadsBack->MakeOrGet() )
740 ogl_disp_list->DrawCulled( showThickness, outerTH, viaHoles );
741 }
742 }
743
745 }
746 else
747 {
748 setLayerMaterial( layer );
749
750 std::shared_ptr<OPENGL_RENDER_LIST> throughHolesOuter = nullptr;
751 std::shared_ptr<OPENGL_RENDER_LIST> anti_board = nullptr;
752 std::shared_ptr<OPENGL_RENDER_LIST> solder_mask = nullptr;
753
754 if( !skipRenderHoles )
755 {
756 if( isSilkLayer && cfg.clip_silk_on_via_annuli )
757 throughHolesOuter = m_outerThroughHoleRings ? m_outerThroughHoleRings->MakeOrGet() : nullptr;
758 else
759 throughHolesOuter = m_outerThroughHoles ? m_outerThroughHoles->MakeOrGet() : nullptr;
760 }
761
762 if( isSilkLayer && cfg.show_off_board_silk )
763 anti_board = nullptr;
764 else if( LSET::PhysicalLayersMask().test( layer ) )
765 anti_board = m_antiBoard ? m_antiBoard->MakeOrGet() : nullptr;
766
767 if( isSilkLayer && cfg.subtract_mask_from_silk && !cfg.show_off_board_silk )
768 {
769 PCB_LAYER_ID maskLayer = ( layer == B_SilkS ) ? B_Mask : F_Mask;
770 solder_mask = m_layers[maskLayer] ? m_layers[maskLayer]->MakeOrGet() : nullptr;
771 }
772
773 if( throughHolesOuter )
774 throughHolesOuter->ApplyScalePosition( pLayerDispList );
775
776 if( anti_board )
777 anti_board->ApplyScalePosition( pLayerDispList );
778
779 if( solder_mask )
780 solder_mask->ApplyScalePosition( pLayerDispList );
781
782 // Offset non-copper layers slightly closer to the screen than soldermask to avoid Z-fighting.
783 glEnable( GL_POLYGON_OFFSET_FILL );
784 glPolygonOffset( 0.0f, -4.0f );
785
786 pLayerDispList->DrawCulled( showThickness, solder_mask, throughHolesOuter, anti_board );
787
788 glDisable( GL_POLYGON_OFFSET_FILL );
789 glPolygonOffset( 0.0f, 0.0f );
790 }
791
792 glPopMatrix();
793 }
794
795 glm::mat4 cameraViewMatrix;
796
797 glGetFloatv( GL_MODELVIEW_MATRIX, glm::value_ptr( cameraViewMatrix ) );
798
799 // Render 3D Models (Non-transparent)
800 renderOpaqueModels( cameraViewMatrix );
801
802 // Render extruded 3D bodies
803 {
805 const SFVEC3F extSelColor = m_boardAdapter.GetColor( extCfg.opengl_selection_color );
806
807 // Render extruded pad standoffs (metallic pins)
808 for( auto& [fp, renderListDef] : m_extrudedPadLists )
809 {
810 if( !renderListDef )
811 continue;
812
813 std::shared_ptr<OPENGL_RENDER_LIST> renderList = renderListDef->MakeOrGet();
814
815 if( !renderList )
816 continue;
817
818 bool highlight = false;
819
820 if( m_boardAdapter.m_IsBoardView )
821 {
822 if( fp->IsSelected() )
823 highlight = true;
824
825 if( extCfg.highlight_on_rollover && fp == m_currentRollOverItem )
826 highlight = true;
827 }
828
829 SMATERIAL mat = m_materials.m_Copper;
830 mat.m_Diffuse = SFVEC3F( 0.75f, 0.75f, 0.75f );
831 mat.m_Specular = SFVEC3F( 0.85f, 0.85f, 0.85f );
832 mat.m_Shininess = 0.6f * 128.0f;
833 mat.m_Transparency = 0.0f;
834
835 OglSetMaterial( mat, 1.0f, highlight, extSelColor );
836 renderList->DrawAll();
837 }
838
839 renderExtrudedBodies( false );
840 }
841
842 // Display board body
843 if( layerFlags.test( LAYER_3D_BOARD ) )
844 {
845 // Make the board body appear further from the screen
846 // to avoid Z-fighting with copper items.
847 glEnable( GL_POLYGON_OFFSET_FILL );
848 glPolygonOffset( 0.0f, 2.0f );
849
850 renderBoardBody( skipRenderHoles );
851
852 glDisable( GL_POLYGON_OFFSET_FILL );
853 glPolygonOffset( 0.0f, 0.0f );
854 }
855
856 // Display transparent mask layers
857 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
858 || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
859 {
860 // Make the soldermask appear closer to the screen
861 // to avoid Z-fighting with copper items
862 glEnable( GL_POLYGON_OFFSET_FILL );
863 glPolygonOffset( 0.0f, -2.0f );
864
865 if( m_camera.GetPos().z > 0 )
866 {
867 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
868 {
870 showThickness, skipRenderHoles );
871 }
872
873 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
874 {
875 renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
876 showThickness, skipRenderHoles );
877 }
878 }
879 else
880 {
881 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
882 {
883 renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
884 showThickness, skipRenderHoles );
885 }
886
887 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
888 {
890 showThickness, skipRenderHoles );
891 }
892 }
893
894 glDisable( GL_POLYGON_OFFSET_FILL );
895 glPolygonOffset( 0.0f, 0.0f );
896 }
897
898 // Render 3D Models (Transparent)
899 // !TODO: this can be optimized. If there are no transparent models (or no opacity),
900 // then there is no need to make this function call.
901 glDepthMask( GL_FALSE );
902 glEnable( GL_BLEND );
903 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
904
905 // Enables Texture Env so it can combine model transparency with each footprint opacity
906 glEnable( GL_TEXTURE_2D );
907 glActiveTexture( GL_TEXTURE0 );
908
909 // Uses an existent texture so the glTexEnv operations will work.
910 glBindTexture( GL_TEXTURE_2D, m_circleTexture );
911
912 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
913 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
914 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
915
916 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
917 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
918
919 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
920 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
921
922 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
923 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
924 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
925 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
926
927 renderTransparentModels( cameraViewMatrix );
928
929 glDisable( GL_BLEND );
931
932 glEnable( GL_BLEND );
933 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
934
935 renderExtrudedBodies( true );
936
937 glDisable( GL_BLEND );
938
939 glDepthMask( GL_TRUE );
940
941 // Render Grid
942 if( cfg.grid_type != GRID3D_TYPE::NONE )
943 {
944 glDisable( GL_LIGHTING );
945
946 if( glIsList( m_grid ) )
947 glCallList( m_grid );
948
949 glEnable( GL_LIGHTING );
950 }
951
952 // Render 3D arrows
953 if( cfg.show_navigator )
954 m_spheres_gizmo->render3dSpheresGizmo( m_camera.GetRotationMatrix() );
955
956 // Return back to the original viewport (this is important if we want
957 // to take a screenshot after the render)
958 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
959
960 return false;
961}
962
963
965{
966 glEnable( GL_LINE_SMOOTH );
967 glShadeModel( GL_SMOOTH );
968
969 // 4-byte pixel alignment
970 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
971
972 // Initialize the open GL texture to draw the filled semi-circle of the segments
974
975 if( !circleImage )
976 return false;
977
978 unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
979
980 circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
981 ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
982 circleRadius,
983 0xFF );
984
985 IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
986
987 circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
988
989 m_circleTexture = OglLoadTexture( *circleImageBlured );
990
991 delete circleImageBlured;
992 circleImageBlured = nullptr;
993
994 delete circleImage;
995 circleImage = nullptr;
996
997 init_lights();
998
999 // Use this mode if you want see the triangle lines (debug proposes)
1000 //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
1001 m_canvasInitialized = true;
1002
1003 return true;
1004}
1005
1006
1008{
1009 glEnable( GL_COLOR_MATERIAL );
1010 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
1011
1012 const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1013 const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1014 const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
1015 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
1016
1017 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
1018 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
1019
1020 glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
1021 glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
1022 glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
1023}
1024
1025
1027{
1028#define DELETE_AND_FREE( ptr ) \
1029 { \
1030 ptr.reset(); \
1031 } \
1032
1033#define DELETE_AND_FREE_MAP( map ) \
1034 { \
1035 for( auto& [ layer, ptr ] : map ) \
1036 ptr.reset(); \
1037 \
1038 map.clear(); \
1039 }
1040
1041 if( m_canvasInitialized && glIsList( m_grid ) )
1042 glDeleteLists( m_grid, 1 );
1043
1044 m_grid = 0;
1045
1047
1052
1055
1056 for( std::shared_ptr<TRIANGLE_DISPLAY_LIST>& list : m_triangles )
1057 list.reset();
1058
1059 m_triangles.clear();
1060
1062
1063 m_3dModelMatrixMap.clear();
1064
1069
1073
1079
1082}
1083
1084
1086{
1087 std::vector<std::shared_ptr<void>> retired;
1088
1089 {
1090 std::lock_guard<std::recursive_mutex> lock( m_renderMutex );
1091 retired.swap( m_retiredRenderPtrs );
1092 }
1093}
1094
1095
1097 bool aShowThickness, bool aSkipRenderHoles )
1098{
1099 wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
1100
1101 if( m_board )
1102 {
1103 std::shared_ptr<OPENGL_RENDER_LIST> board_list = m_board->MakeOrGet();
1104 std::shared_ptr<OPENGL_RENDER_LIST> solder_mask =
1105 m_layers[aLayerID] ? m_layers[aLayerID]->MakeOrGet() : nullptr;
1106 std::shared_ptr<OPENGL_RENDER_LIST> via_holes =
1107 !aSkipRenderHoles && m_outerThroughHoles ? m_outerThroughHoles->MakeOrGet() : nullptr;
1108
1109 if( via_holes )
1110 via_holes->ApplyScalePosition( aZPos, m_boardAdapter.GetNonCopperLayerThickness() );
1111
1112 board_list->ApplyScalePosition( aZPos, m_boardAdapter.GetNonCopperLayerThickness() );
1113
1114 setLayerMaterial( aLayerID );
1115 board_list->SetItIsTransparent( true );
1116 board_list->DrawCulled( aShowThickness, solder_mask, via_holes );
1117
1118 if( aLayerID == F_Mask && m_viaFrontCover )
1119 {
1120 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_draw_list = m_viaFrontCover->MakeOrGet() )
1121 {
1122 ogl_draw_list->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
1123 ogl_draw_list->DrawTop();
1124 }
1125 }
1126 else if( aLayerID == B_Mask && m_viaBackCover )
1127 {
1128 if( std::shared_ptr<OPENGL_RENDER_LIST> ogl_draw_list = m_viaBackCover->MakeOrGet() )
1129 {
1130 ogl_draw_list->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
1131 ogl_draw_list->DrawBot();
1132 }
1133 }
1134 }
1135}
1136
1137
1138void RENDER_3D_OPENGL::get3dModelsSelected( std::list<MODELTORENDER> &aDstRenderList, bool aGetTop,
1139 bool aGetBot, bool aRenderTransparentOnly,
1140 bool aRenderSelectedOnly )
1141{
1142 wxASSERT( ( aGetTop == true ) || ( aGetBot == true ) );
1143
1144 if( !m_boardAdapter.GetBoard() )
1145 return;
1146
1148
1149 // Go for all footprints
1150 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1151 {
1152 bool highlight = false;
1153
1154 if( m_boardAdapter.m_IsBoardView )
1155 {
1156 if( fp->IsSelected() )
1157 highlight = true;
1158
1160 highlight = true;
1161
1162 if( aRenderSelectedOnly != highlight )
1163 continue;
1164 }
1165
1166 bool hasModels = !fp->Models().empty();
1167 bool showMissing = m_boardAdapter.m_Cfg->m_Render.show_missing_models;
1168
1169 if( hasModels || showMissing )
1170 {
1171 if( m_boardAdapter.IsFootprintShown( fp ) )
1172 {
1173 const bool isFlipped = fp->IsFlipped();
1174
1175 if( aGetTop == !isFlipped || aGetBot == isFlipped )
1176 get3dModelsFromFootprint( aDstRenderList, fp, aRenderTransparentOnly,
1177 highlight );
1178 }
1179 }
1180 }
1181}
1182
1183
1184void RENDER_3D_OPENGL::get3dModelsFromFootprint( std::list<MODELTORENDER> &aDstRenderList,
1185 const FOOTPRINT* aFootprint,
1186 bool aRenderTransparentOnly, bool aIsSelected )
1187{
1188 if( !aFootprint->Models().empty() )
1189 {
1190 const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1191
1192 VECTOR2I pos = aFootprint->GetPosition();
1193
1194 glm::mat4 fpMatrix( 1.0f );
1195
1196 fpMatrix = glm::translate( fpMatrix, SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1197 -pos.y * m_boardAdapter.BiuTo3dUnits(), zpos ) );
1198
1199 if( !aFootprint->GetOrientation().IsZero() )
1200 {
1201 fpMatrix = glm::rotate( fpMatrix, (float) aFootprint->GetOrientation().AsRadians(),
1202 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1203 }
1204
1205 if( aFootprint->IsFlipped() )
1206 {
1207 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1208 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1209 }
1210
1211 double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1212
1213 fpMatrix = glm::scale( fpMatrix, SFVEC3F( modelunit_to_3d_units_factor ) );
1214
1215 // The placeholder stands in for the whole footprint, so several missing models share one.
1216 bool placeholderAdded = false;
1217
1218 // Get the list of model files for this model
1219 for( const FP_3DMODEL& sM : aFootprint->Models() )
1220 {
1221 if( !sM.m_Show || sM.m_Filename.empty() )
1222 continue;
1223
1224 // Check if the model is present in our cache map
1225 auto cache_i = m_3dModelMap.find( sM.m_Filename );
1226
1227 if( cache_i == m_3dModelMap.end() )
1228 {
1229 if( !placeholderAdded )
1230 {
1231 renderPlaceholderForFootprint( aDstRenderList, fpMatrix, aFootprint, aRenderTransparentOnly,
1232 aIsSelected, aRenderTransparentOnly ? sM.m_Opacity : 1.0f );
1233 placeholderAdded = true;
1234 }
1235
1236 continue;
1237 }
1238
1239 if( const std::shared_ptr<MODEL_3D_DEFERRED>& modelPtrDef = cache_i->second )
1240 {
1241 std::shared_ptr<MODEL_3D> modelPtr = modelPtrDef->MakeOrGet();
1242
1243 if( !modelPtr )
1244 continue;
1245
1246 bool opaque = sM.m_Opacity >= 1.0;
1247
1248 if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1249 ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1250 {
1251 glm::mat4 modelworldMatrix = fpMatrix;
1252
1253 const SFVEC3F offset = SFVEC3F( sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z );
1254 const SFVEC3F rotation = SFVEC3F( sM.m_Rotation.x, sM.m_Rotation.y,
1255 sM.m_Rotation.z );
1256 const SFVEC3F scale = SFVEC3F( sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z );
1257
1258 std::vector<float> key = { offset.x, offset.y, offset.z,
1259 rotation.x, rotation.y, rotation.z,
1260 scale.x, scale.y, scale.z };
1261
1262 auto it = m_3dModelMatrixMap.find( key );
1263
1264 if( it != m_3dModelMatrixMap.end() )
1265 {
1266 modelworldMatrix *= it->second;
1267 }
1268 else
1269 {
1270 glm::mat4 mtx( 1.0f );
1271 mtx = glm::translate( mtx, offset );
1272 mtx = glm::rotate( mtx, glm::radians( -rotation.z ), { 0.0f, 0.0f, 1.0f } );
1273 mtx = glm::rotate( mtx, glm::radians( -rotation.y ), { 0.0f, 1.0f, 0.0f } );
1274 mtx = glm::rotate( mtx, glm::radians( -rotation.x ), { 1.0f, 0.0f, 0.0f } );
1275 mtx = glm::scale( mtx, scale );
1276 m_3dModelMatrixMap[ key ] = mtx;
1277
1278 modelworldMatrix *= mtx;
1279 }
1280
1281 aDstRenderList.emplace_back( modelworldMatrix, modelPtr,
1282 aRenderTransparentOnly ? sM.m_Opacity : 1.0f,
1283 aRenderTransparentOnly,
1284 aFootprint->IsSelected() || aIsSelected );
1285 }
1286 }
1287 }
1288 }
1289 else
1290 {
1291 const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1292
1293 VECTOR2I pos = aFootprint->GetPosition();
1294
1295 glm::mat4 fpMatrix( 1.0f );
1296
1297 fpMatrix = glm::translate( fpMatrix, SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1298 -pos.y * m_boardAdapter.BiuTo3dUnits(), zpos ) );
1299
1300 if( !aFootprint->GetOrientation().IsZero() )
1301 {
1302 fpMatrix = glm::rotate( fpMatrix, (float) aFootprint->GetOrientation().AsRadians(),
1303 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1304 }
1305
1306 if( aFootprint->IsFlipped() )
1307 {
1308 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1309 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1310 }
1311
1312 double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1313
1314 fpMatrix = glm::scale( fpMatrix, SFVEC3F( modelunit_to_3d_units_factor ) );
1315
1316 renderPlaceholderForFootprint( aDstRenderList, fpMatrix, aFootprint, aRenderTransparentOnly, aIsSelected,
1317 1.0f );
1318 }
1319}
1320
1321
1322void RENDER_3D_OPENGL::renderPlaceholderForFootprint( std::list<MODELTORENDER>& aDstRenderList,
1323 const glm::mat4& aFpMatrix, const FOOTPRINT* aFootprint,
1324 bool aRenderTransparentOnly, bool aIsSelected, float aOpacity )
1325{
1326 if( !m_boardAdapter.m_Cfg->m_Render.show_missing_models || !m_placeholderModel )
1327 return;
1328
1329 // Suppress placeholder if a valid extruded body was built
1330 if( m_extrudedBodyLists.count( aFootprint ) )
1331 return;
1332
1333 BOX2I localBox = CalcPlaceholderLocalBox( aFootprint );
1334
1335 float bboxW = std::abs( localBox.GetWidth() ) / pcbIUScale.IU_PER_MM * 0.9f;
1336 float bboxH = std::abs( localBox.GetHeight() ) / pcbIUScale.IU_PER_MM * 0.9f;
1337
1338 float scaleX = bboxW;
1339 float scaleY = bboxH;
1340 float scaleZ = std::min( bboxW, bboxH ) * 0.5f;
1341
1342 VECTOR2I localCenter = localBox.GetCenter();
1343 float offsetX = localCenter.x / pcbIUScale.IU_PER_MM;
1344 float offsetY = -localCenter.y / pcbIUScale.IU_PER_MM;
1345 float offsetZ = scaleZ * 0.5f;
1346
1347 if( aFootprint->IsFlipped() )
1348 offsetY = -offsetY;
1349
1350 glm::mat4 mtx = aFpMatrix;
1351 mtx = glm::translate( mtx, SFVEC3F( offsetX, offsetY, offsetZ ) );
1352 mtx = glm::scale( mtx, SFVEC3F( scaleX, scaleY, scaleZ ) );
1353
1354 bool placeholderOpaque = aOpacity >= 1.0;
1355
1356 if( ( !aRenderTransparentOnly && m_placeholderModel->HasOpaqueMeshes() && placeholderOpaque )
1357 || ( aRenderTransparentOnly && ( m_placeholderModel->HasTransparentMeshes() || !placeholderOpaque ) ) )
1358 {
1359 aDstRenderList.emplace_back( mtx, m_placeholderModel, aOpacity, aRenderTransparentOnly,
1360 aFootprint->IsSelected() || aIsSelected );
1361 }
1362}
1363
1364
1365void RENDER_3D_OPENGL::renderOpaqueModels( const glm::mat4 &aCameraViewMatrix )
1366{
1368
1369 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1370
1371 glPushMatrix();
1372
1373 std::list<MODELTORENDER> renderList;
1374
1375 if( m_boardAdapter.m_IsBoardView )
1376 {
1377 renderList.clear();
1378
1379 get3dModelsSelected( renderList, true, true, false, true );
1380
1381 if( !renderList.empty() )
1382 {
1383 MODEL_3D::BeginDrawMulti( false );
1384
1385 for( const MODELTORENDER& mtr : renderList )
1386 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1387
1389 }
1390 }
1391
1392 renderList.clear();
1393 get3dModelsSelected( renderList, true, true, false, false );
1394
1395 if( !renderList.empty() )
1396 {
1398
1399 for( const MODELTORENDER& mtr : renderList )
1400 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1401
1403 }
1404
1405 glPopMatrix();
1406}
1407
1408
1409void RENDER_3D_OPENGL::renderExtrudedBodies( bool aTransparentPass )
1410{
1412 const SFVEC3F extSelColor = m_boardAdapter.GetColor( extCfg.opengl_selection_color );
1413
1414 for( auto& [fp, renderListDef] : m_extrudedBodyLists )
1415 {
1416 const EXTRUDED_3D_BODY* body = fp->GetExtrudedBody();
1417
1418 if( !body )
1419 continue;
1420
1421 if( !renderListDef )
1422 continue;
1423
1424 std::shared_ptr<OPENGL_RENDER_LIST> renderList = renderListDef->MakeOrGet();
1425
1426 if( !renderList )
1427 continue;
1428
1429 bool highlight = false;
1430
1431 if( m_boardAdapter.m_IsBoardView )
1432 {
1433 if( fp->IsSelected() )
1434 highlight = true;
1435
1436 if( extCfg.highlight_on_rollover && fp == m_currentRollOverItem )
1437 highlight = true;
1438 }
1439
1440 KIGFX::COLOR4D c = body->m_color;
1441
1444
1445 if( ( c.a < 1.0 ) != aTransparentPass )
1446 continue;
1447
1448 SMATERIAL mat;
1449
1450 SFVEC3F diffuse( c.r, c.g, c.b );
1451 EXTRUSION_MATERIAL_PROPS props = GetMaterialProps( body->m_material, diffuse );
1452
1453 mat.m_Diffuse = diffuse;
1454 mat.m_Ambient = props.m_Ambient;
1455 mat.m_Specular = props.m_Specular;
1456 mat.m_Shininess = props.m_Shininess;
1457 mat.m_Emissive = SFVEC3F( 0.0f );
1458 mat.m_Transparency = 1.0f - c.a;
1459
1460 OglSetMaterial( mat, 1.0f, highlight, extSelColor );
1461 renderList->DrawAll();
1462 }
1463}
1464
1465
1466void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatrix )
1467{
1469
1470 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1471
1472 std::list<MODELTORENDER> renderListModels; // do not clear it until this function returns
1473
1474 if( m_boardAdapter.m_IsBoardView )
1475 {
1476 // Get Transparent Selected
1477 get3dModelsSelected( renderListModels, true, true, true, true );
1478 }
1479
1480 // Get Transparent Not Selected
1481 get3dModelsSelected( renderListModels, true, true, true, false );
1482
1483 if( renderListModels.empty() )
1484 return;
1485
1486 std::vector<std::pair<const MODELTORENDER *, float>> transparentModelList;
1487
1488 transparentModelList.reserve( renderListModels.size() );
1489
1490 // Calculate the distance to the camera for each model
1491 const SFVEC3F &cameraPos = m_camera.GetPos();
1492
1493 for( const MODELTORENDER& mtr : renderListModels )
1494 {
1495 const BBOX_3D& bBox = mtr.m_model->GetBBox();
1496 const SFVEC3F& bBoxCenter = bBox.GetCenter();
1497 const SFVEC3F bBoxWorld = mtr.m_modelWorldMat * glm::vec4( bBoxCenter, 1.0f );
1498
1499 const float distanceToCamera = glm::length( cameraPos - bBoxWorld );
1500
1501 transparentModelList.emplace_back( &mtr, distanceToCamera );
1502 }
1503
1504 // Sort from back to front
1505 std::sort( transparentModelList.begin(), transparentModelList.end(),
1506 [&]( std::pair<const MODELTORENDER *, float>& a,
1507 std::pair<const MODELTORENDER *, float>& b )
1508 {
1509 if( a.second != b.second )
1510 return a.second > b.second;
1511
1512 return a.first > b.first; // use pointers as a last resort
1513 } );
1514
1515 // Start rendering calls
1516 glPushMatrix();
1517
1518 bool isUsingColorInformation = !( transparentModelList.begin()->first->m_isSelected &&
1519 m_boardAdapter.m_IsBoardView );
1520
1521 MODEL_3D::BeginDrawMulti( isUsingColorInformation );
1522
1523 for( const std::pair<const MODELTORENDER *, float>& mtr : transparentModelList )
1524 {
1525 if( m_boardAdapter.m_IsBoardView )
1526 {
1527 // Toggle between using model color or the select color
1528 if( !isUsingColorInformation && !mtr.first->m_isSelected )
1529 {
1530 isUsingColorInformation = true;
1531
1532 glEnableClientState( GL_COLOR_ARRAY );
1533 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
1534 glEnable( GL_COLOR_MATERIAL );
1535 }
1536 else if( isUsingColorInformation && mtr.first->m_isSelected )
1537 {
1538 isUsingColorInformation = false;
1539
1540 glDisableClientState( GL_COLOR_ARRAY );
1541 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
1542 glDisable( GL_COLOR_MATERIAL );
1543 }
1544 }
1545
1546 // Render model, sort each individuall material group
1547 // by passing cameraPos
1548 renderModel( aCameraViewMatrix, *mtr.first, selColor, &cameraPos );
1549 }
1550
1552
1553 glPopMatrix();
1554}
1555
1556
1557void RENDER_3D_OPENGL::renderModel( const glm::mat4 &aCameraViewMatrix,
1558 const MODELTORENDER &aModelToRender,
1559 const SFVEC3F &aSelColor, const SFVEC3F *aCameraWorldPos )
1560{
1562
1563 const glm::mat4 modelviewMatrix = aCameraViewMatrix * aModelToRender.m_modelWorldMat;
1564
1565 glLoadMatrixf( glm::value_ptr( modelviewMatrix ) );
1566
1567 aModelToRender.m_model->Draw( aModelToRender.m_isTransparent, aModelToRender.m_opacity,
1568 aModelToRender.m_isSelected, aSelColor,
1569 &aModelToRender.m_modelWorldMat, aCameraWorldPos );
1570
1571 if( cfg.show_model_bbox )
1572 {
1573 const bool wasBlendEnabled = glIsEnabled( GL_BLEND );
1574
1575 if( !wasBlendEnabled )
1576 {
1577 glEnable( GL_BLEND );
1578 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1579 }
1580
1581 glDisable( GL_LIGHTING );
1582
1583 glLineWidth( 1 );
1584 aModelToRender.m_model->DrawBboxes();
1585
1586 glLineWidth( 4 );
1587 aModelToRender.m_model->DrawBbox();
1588
1589 glEnable( GL_LIGHTING );
1590
1591 if( !wasBlendEnabled )
1592 glDisable( GL_BLEND );
1593 }
1594}
1595
1596
1598{
1599 if( glIsList( m_grid ) )
1600 glDeleteLists( m_grid, 1 );
1601
1602 m_grid = 0;
1603
1604 if( aGridType == GRID3D_TYPE::NONE )
1605 return;
1606
1607 m_grid = glGenLists( 1 );
1608
1609 if( !glIsList( m_grid ) )
1610 return;
1611
1612 glNewList( m_grid, GL_COMPILE );
1613
1614 glEnable( GL_BLEND );
1615 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1616
1617 const double zpos = 0.0;
1618
1619 // Color of grid lines
1620 const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1621
1622 // Color of grid lines every 5 lines
1623 const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1624 const double scale = m_boardAdapter.BiuTo3dUnits();
1625 const GLfloat transparency = 0.35f;
1626
1627 double griSizeMM = 0.0;
1628
1629 switch( aGridType )
1630 {
1631 case GRID3D_TYPE::GRID_1MM: griSizeMM = 1.0; break;
1632 case GRID3D_TYPE::GRID_2P5MM: griSizeMM = 2.5; break;
1633 case GRID3D_TYPE::GRID_5MM: griSizeMM = 5.0; break;
1634 case GRID3D_TYPE::GRID_10MM: griSizeMM = 10.0; break;
1635
1636 default:
1637 case GRID3D_TYPE::NONE: return;
1638 }
1639
1640 glNormal3f( 0.0, 0.0, 1.0 );
1641
1642 const VECTOR2I brd_size = m_boardAdapter.GetBoardSize();
1643 VECTOR2I brd_center_pos = m_boardAdapter.GetBoardPos();
1644
1645 brd_center_pos.y = -brd_center_pos.y;
1646
1647 const int xsize = std::max( brd_size.x, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1648 const int ysize = std::max( brd_size.y, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1649
1650 // Grid limits, in 3D units
1651 double xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1652 double xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1653 double ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1654 double ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1655 double zmin = pcbIUScale.mmToIU( -50 ) * scale;
1656 double zmax = pcbIUScale.mmToIU( 100 ) * scale;
1657
1658 // Set rasterised line width (min value = 1)
1659 glLineWidth( 1 );
1660
1661 // Draw horizontal grid centered on 3D origin (center of the board)
1662 for( int ii = 0; ; ii++ )
1663 {
1664 if( (ii % 5) )
1665 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1666 else
1667 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1668 transparency );
1669
1670 const int delta = KiROUND( ii * griSizeMM * pcbIUScale.IU_PER_MM );
1671
1672 if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
1673 {
1674 glBegin( GL_LINES );
1675 glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1676 glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1677 glEnd();
1678
1679 if( ii != 0 )
1680 {
1681 glBegin( GL_LINES );
1682 glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1683 glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1684 glEnd();
1685 }
1686 }
1687
1688 if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
1689 {
1690 glBegin( GL_LINES );
1691 glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1692 glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1693 glEnd();
1694
1695 if( ii != 0 )
1696 {
1697 glBegin( GL_LINES );
1698 glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1699 glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1700 glEnd();
1701 }
1702 }
1703
1704 if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1705 break;
1706 }
1707
1708 // Draw vertical grid on Z axis
1709 glNormal3f( 0.0, -1.0, 0.0 );
1710
1711 // Draw vertical grid lines (parallel to Z axis)
1712 double posy = -brd_center_pos.y * scale;
1713
1714 for( int ii = 0; ; ii++ )
1715 {
1716 if( (ii % 5) )
1717 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1718 else
1719 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1720 transparency );
1721
1722 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM;
1723
1724 glBegin( GL_LINES );
1725 xmax = ( brd_center_pos.x + delta ) * scale;
1726
1727 glVertex3f( xmax, posy, zmin );
1728 glVertex3f( xmax, posy, zmax );
1729 glEnd();
1730
1731 if( ii != 0 )
1732 {
1733 glBegin( GL_LINES );
1734 xmin = ( brd_center_pos.x - delta ) * scale;
1735 glVertex3f( xmin, posy, zmin );
1736 glVertex3f( xmin, posy, zmax );
1737 glEnd();
1738 }
1739
1740 if( delta > xsize / 2.0f )
1741 break;
1742 }
1743
1744 // Draw horizontal grid lines on Z axis (parallel to X axis)
1745 for( int ii = 0; ; ii++ )
1746 {
1747 if( ii % 5 )
1748 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1749 else
1750 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1751
1752 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM * scale;
1753
1754 if( delta <= zmax )
1755 {
1756 // Draw grid lines on Z axis (positive Z axis coordinates)
1757 glBegin( GL_LINES );
1758 glVertex3f( xmin, posy, delta );
1759 glVertex3f( xmax, posy, delta );
1760 glEnd();
1761 }
1762
1763 if( delta <= -zmin && ( ii != 0 ) )
1764 {
1765 // Draw grid lines on Z axis (negative Z axis coordinates)
1766 glBegin( GL_LINES );
1767 glVertex3f( xmin, posy, -delta );
1768 glVertex3f( xmax, posy, -delta );
1769 glEnd();
1770 }
1771
1772 if( ( delta > zmax ) && ( delta > -zmin ) )
1773 break;
1774 }
1775
1776 glDisable( GL_BLEND );
1777
1778 glEndList();
1779}
1780
1781
1783{
1784 // Unit cube: 1mm × 1mm × 1mm, centered at origin
1785 static SFVEC3F positions[24] = { // +Z (top)
1786 { -0.5f, -0.5f, 0.5f },
1787 { 0.5f, -0.5f, 0.5f },
1788 { 0.5f, 0.5f, 0.5f },
1789 { -0.5f, 0.5f, 0.5f },
1790
1791 // -Z (bottom)
1792 { -0.5f, 0.5f, -0.5f },
1793 { 0.5f, 0.5f, -0.5f },
1794 { 0.5f, -0.5f, -0.5f },
1795 { -0.5f, -0.5f, -0.5f },
1796
1797 // +X
1798 { 0.5f, -0.5f, -0.5f },
1799 { 0.5f, 0.5f, -0.5f },
1800 { 0.5f, 0.5f, 0.5f },
1801 { 0.5f, -0.5f, 0.5f },
1802
1803 // -X
1804 { -0.5f, -0.5f, 0.5f },
1805 { -0.5f, 0.5f, 0.5f },
1806 { -0.5f, 0.5f, -0.5f },
1807 { -0.5f, -0.5f, -0.5f },
1808
1809 // +Y
1810 { -0.5f, 0.5f, 0.5f },
1811 { 0.5f, 0.5f, 0.5f },
1812 { 0.5f, 0.5f, -0.5f },
1813 { -0.5f, 0.5f, -0.5f },
1814
1815 // -Y
1816 { -0.5f, -0.5f, -0.5f },
1817 { 0.5f, -0.5f, -0.5f },
1818 { 0.5f, -0.5f, 0.5f },
1819 { -0.5f, -0.5f, 0.5f }
1820 };
1821
1822 static SFVEC3F normals[24] = { // +Z
1823 { 0, 0, 1 },
1824 { 0, 0, 1 },
1825 { 0, 0, 1 },
1826 { 0, 0, 1 },
1827 // -Z
1828 { 0, 0, -1 },
1829 { 0, 0, -1 },
1830 { 0, 0, -1 },
1831 { 0, 0, -1 },
1832 // +X
1833 { 1, 0, 0 },
1834 { 1, 0, 0 },
1835 { 1, 0, 0 },
1836 { 1, 0, 0 },
1837 // -X
1838 { -1, 0, 0 },
1839 { -1, 0, 0 },
1840 { -1, 0, 0 },
1841 { -1, 0, 0 },
1842 // +Y
1843 { 0, 1, 0 },
1844 { 0, 1, 0 },
1845 { 0, 1, 0 },
1846 { 0, 1, 0 },
1847 // -Y
1848 { 0, -1, 0 },
1849 { 0, -1, 0 },
1850 { 0, -1, 0 },
1851 { 0, -1, 0 }
1852 };
1853
1854 static unsigned int indices[36] = {
1855 0, 1, 2, 0, 2, 3, // +Z
1856 4, 5, 6, 4, 6, 7, // -Z
1857 8, 9, 10, 8, 10, 11, // +X
1858 12, 13, 14, 12, 14, 15, // -X
1859 16, 17, 18, 16, 18, 19, // +Y
1860 20, 21, 22, 20, 22, 23 // -Y
1861 };
1862
1863 static SMATERIAL material = {
1864 SFVEC3F( 0.2f, 0.1f, 0.0f ), // ambient
1865 SFVEC3F( 1.0f, 0.5f, 0.0f ), // diffuse
1866 SFVEC3F( 0.0f, 0.0f, 0.0f ), // emissive
1867 SFVEC3F( 0.1f, 0.1f, 0.1f ), // specular
1868 0.1f, // shininess
1869 0.4f // transparency
1870 };
1871
1872 static SMESH mesh = { 24, positions, normals, nullptr, nullptr, 36, indices, 0 };
1873
1874 static S3DMODEL model = { 1, &mesh, 1, &material };
1875
1876 m_placeholderModel = std::make_shared<MODEL_3D>( model, MATERIAL_MODE::NORMAL );
1877}
GRID3D_TYPE
Grid types.
Definition 3d_enums.h:50
@ NORMAL
Use all material properties from model file.
Definition 3d_enums.h:68
Defines math related functions.
float mapf(float x, float in_min, float in_max, float out_min, float out_max)
Definition 3d_math.h:129
SFVEC3F SphericalToCartesian(float aInclination, float aAzimuth)
https://en.wikipedia.org/wiki/Spherical_coordinate_system
Definition 3d_math.h:39
EXTRUSION_MATERIAL_PROPS GetMaterialProps(EXTRUSION_MATERIAL aMaterial, const SFVEC3F &aDiffuse)
BOX2I CalcPlaceholderLocalBox(const FOOTPRINT *aFootprint)
Calculate a local space bounding box for a placeholder 3D model.
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
Helper class to handle information needed to display 3D board.
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr size_type GetHeight() const
Definition box2.h:212
A class used to derive camera objects from.
Definition camera.h:99
Implement a canvas based on a wxGLCanvas.
bool IsZero() const
Definition eda_angle.h:136
double AsRadians() const
Definition eda_angle.h:120
bool IsSelected() const
Definition eda_item.h:134
KIGFX::COLOR4D m_color
Definition footprint.h:120
static KIGFX::COLOR4D GetDefaultColor(EXTRUSION_MATERIAL aMaterial)
Definition footprint.h:128
EXTRUSION_MATERIAL m_material
Definition footprint.h:121
EDA_ANGLE GetOrientation() const
Definition footprint.h:438
bool IsFlipped() const
Definition footprint.h:660
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:424
VECTOR2I GetPosition() const override
Definition footprint.h:435
auto RunWithoutCtxLock(Func &&aFunction, Args &&... args)
Run the given function first releasing the GL context lock, then restoring it.
Manage an 8-bit channel image.
Definition image.h:86
void CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
Definition image.cpp:169
void EfxFilter_SkipCenter(IMAGE *aInImg, IMAGE_FILTER aFilterType, unsigned int aRadius)
Apply a filter to the input image and store it in the image class.
Definition image.cpp:523
unsigned int GetHeight() const
Definition image.h:210
unsigned int GetWidth() const
Definition image.h:209
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:390
double g
Green component.
Definition color4d.h:391
double a
Alpha component.
Definition color4d.h:393
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:399
double b
Blue component.
Definition color4d.h:392
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
void DrawBbox() const
Draw main bounding box of the model.
Definition 3d_model.cpp:566
static void EndDrawMulti()
Cleanup render states after drawing multiple models.
Definition 3d_model.cpp:400
void Draw(bool aTransparent, float aOpacity, bool aUseSelectedMaterial, const SFVEC3F &aSelectionColor, const glm::mat4 *aModelWorldMatrix, const SFVEC3F *aCameraWorldPos) const
Render the model into the current context.
Definition 3d_model.cpp:413
static void BeginDrawMulti(bool aUseColorInformation)
Set some basic render states before drawing multiple models.
Definition 3d_model.cpp:384
void DrawBboxes() const
Draw individual bounding boxes of each mesh.
Definition 3d_model.cpp:585
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition pgm_base.h:113
std::unique_ptr< BUSY_INDICATOR > CreateBusyIndicator() const
Return a created busy indicator, if a factory has been set, else a null pointer.
RENDER_3D_BASE(BOARD_ADAPTER &aBoardAdapter, CAMERA &aCamera)
bool m_canvasInitialized
Flag if the canvas specific for this render was already initialized.
std::shared_ptr< REPORTER > m_activityReporter
wxSize m_windowSize
The window size that this camera is working.
BOARD_ADAPTER & m_boardAdapter
Settings reference in use for this render.
SPHERES_GIZMO::GizmoSphereSelection getSelectedGizmoSphere() const
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_offboardPadsBack
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_microviaHoles
std::map< const FOOTPRINT *, std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > > m_extrudedBodyLists
GRID3D_TYPE m_lastGridType
Stores the last grid type.
std::tuple< int, int, int, int > getGizmoViewport() const
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_viaFrontCover
void renderOpaqueModels(const glm::mat4 &aCameraViewMatrix)
void StopBgWorker() override
Request stop and join any in-progress background loading.
void generate3dGrid(GRID3D_TYPE aGridType)
Create a 3D grid to an OpenGL display list.
void setLightFront(bool enabled)
std::shared_ptr< MODEL_3D > m_placeholderModel
bool Redraw(bool aIsMoving) override
Redraw the view.
MAP_OGL_DISP_LISTS m_layers
MAP_OGL_DISP_LISTS m_innerLayerHoles
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_postMachinePlugs
Board material plugs for backdrill/counterbore/countersink.
RENDER_3D_OPENGL(EDA_3D_CANVAS *aCanvas, BOARD_ADAPTER &aAdapter, CAMERA &aCamera)
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_offboardPadsFront
MAP_OGL_DISP_LISTS m_outerLayerHoles
BOARD_ITEM * m_currentRollOverItem
void renderBoardBody(bool aSkipRenderHoles)
std::map< std::vector< float >, glm::mat4 > m_3dModelMatrixMap
std::recursive_mutex m_renderMutex
LIST_TRIANGLES m_triangles
store pointers so can be deleted latter
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_platedPadsFront
void setLayerMaterial(PCB_LAYER_ID aLayerID)
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_platedPadsBack
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_board
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_boardWithHoles
struct RENDER_3D_OPENGL::@136145154067207014164113243162246125147361200233 m_materials
void renderModel(const glm::mat4 &aCameraViewMatrix, const MODELTORENDER &aModelToRender, const SFVEC3F &aSelColor, const SFVEC3F *aCameraWorldPos)
int GetWaitForEditingTimeOut() override
Give the interface the time (in ms) that it should wait for editing or movements before (this works f...
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_antiBoard
std::map< wxString, std::shared_ptr< MODEL_3D_DEFERRED > > m_3dModelMap
void renderSolderMaskLayer(PCB_LAYER_ID aLayerID, float aZPos, bool aShowThickness, bool aSkipRenderHoles)
void renderTransparentModels(const glm::mat4 &aCameraViewMatrix)
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_outerThroughHoles
void get3dModelsSelected(std::list< MODELTORENDER > &aDstRenderList, bool aGetTop, bool aGetBot, bool aRenderTransparentOnly, bool aRenderSelectedOnly)
void setPlatedCopperAndDepthOffset(PCB_LAYER_ID aLayer_id)
void updateGizmoSelection(glm::mat4 aCameraRotationMatrix)
std::map< const FOOTPRINT *, std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > > m_extrudedPadLists
void SetCurWindowSize(const wxSize &aSize) override
Before each render, the canvas will tell the render what is the size of its windows,...
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_padHoles
EDA_3D_CANVAS * m_canvas
void renderPlaceholderForFootprint(std::list< MODELTORENDER > &aDstRenderList, const glm::mat4 &aFpMatrix, const FOOTPRINT *aFootprint, bool aRenderTransparentOnly, bool aIsSelected, float aOpacity)
std::vector< std::shared_ptr< void > > m_retiredRenderPtrs
SPHERES_GIZMO * m_spheres_gizmo
GLuint m_grid
oGL list that stores current grid
void get3dModelsFromFootprint(std::list< MODELTORENDER > &aDstRenderList, const FOOTPRINT *aFootprint, bool aRenderTransparentOnly, bool aIsSelected)
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_outerViaThroughHoles
void handleGizmoMouseInput(int mouseX, int mouseY)
void setLightBottom(bool enabled)
void setGizmoViewport(int x, int y, int width, int height)
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_outerThroughHoleRings
std::shared_ptr< OPENGL_RENDER_LIST_DEFERRED > m_viaBackCover
void setLightTop(bool enabled)
Renders a set of colored spheres in 3D space that act as a directional orientation gizmo.
GizmoSphereSelection
Enum to indicate which sphere (direction) is selected.
@ LIGHTBLUE
Definition color4d.h:58
@ DARKGRAY
Definition color4d.h:42
#define DELETE_AND_FREE_MAP(map)
#define DELETE_AND_FREE(ptr)
#define _(s)
#define UNITS3D_TO_UNITSPCB
Implements a model viewer canvas.
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
@ GAUSSIAN_BLUR
Definition image.h:61
int MapPCBLayerTo3DLayer(PCB_LAYER_ID aLayer)
Definition layer_id.cpp:347
@ LAYER_3D_USER_1
Definition layer_ids.h:593
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:582
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:581
@ LAYER_3D_BOARD
Definition layer_ids.h:576
@ LAYER_3D_USER_45
Definition layer_ids.h:637
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_Adhes
Definition layer_ids.h:99
@ Edge_Cuts
Definition layer_ids.h:108
@ Dwgs_User
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:100
@ Cmts_User
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:98
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ Eco1_User
Definition layer_ids.h:105
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ F_Fab
Definition layer_ids.h:115
@ Margin
Definition layer_ids.h:109
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ Eco2_User
Definition layer_ids.h:106
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
void OglResetTextureState()
Reset to default state the texture settings.
void OglSetMaterial(const SMATERIAL &aMaterial, float aOpacity, bool aUseSelectedMaterial, SFVEC3F aSelectionColor)
Set OpenGL materials.
GLuint OglLoadTexture(const IMAGE &aImage)
Generate a new OpenGL texture.
Definition ogl_utils.cpp:91
void OglDrawBackground(const SFVEC4F &aTopColor, const SFVEC4F &aBotColor)
Define generic OpenGL functions that are common to any OpenGL target.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
void init_lights()
static SFVEC4F premultiplyAlpha(const SFVEC4F &aInput)
#define SIZE_OF_CIRCLE_TEXTURE
const int scale
Manage a bounding box defined by two SFVEC3F min max points.
Definition bbox_3d.h:39
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition bbox_3d.cpp:128
bool DifferentiatePlatedCopper()
return true if platted copper aeras and non platted copper areas must be drawn using a different colo...
std::shared_ptr< MODEL_3D > m_model
Store the a model based on meshes and materials.
Definition c3dmodel.h:111
float m_Shininess
Definition c3dmodel.h:39
SFVEC3F m_Specular
Definition c3dmodel.h:38
SFVEC3F m_Ambient
Definition c3dmodel.h:35
float m_Transparency
1.0 is completely transparent, 0.0 completely opaque
Definition c3dmodel.h:40
SFVEC3F m_Emissive
Definition c3dmodel.h:37
SFVEC3F m_Diffuse
Default diffuse color if m_Color is NULL.
Definition c3dmodel.h:36
Per-vertex normal/color/texcoors structure.
Definition c3dmodel.h:77
KIBIS_MODEL * model
int delta
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
glm::vec3 SFVEC3F
Definition xv3d_types.h:40
glm::vec4 SFVEC4F
Definition xv3d_types.h:42