1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2016 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 "render_3d_raytrace.h"
26 #include "shapes3D/plane_3d.h"
27 #include "shapes3D/round_segment_3d.h"
28 #include "shapes3D/layer_item_3d.h"
29 #include "shapes3D/cylinder_3d.h"
30 #include "shapes3D/triangle_3d.h"
31 #include "shapes2D/layer_item_2d.h"
32 #include "shapes2D/ring_2d.h"
33 #include "shapes2D/polygon_2d.h"
34 #include "shapes2D/filled_circle_2d.h"
35 #include "shapes2D/round_segment_2d.h"
36 #include "accelerators/bvh_pbrt.h"
37 #include "3d_fastmath.h"
38 #include "3d_math.h"
39
40 #include <board.h>
41 #include <footprint.h>
42
43 #include <base_units.h>
44 #include <profile.h> // To use GetRunningMicroSecs or another profiling utility
45
46 /**
47 * Perform an interpolation step to easy control the transparency based on the
48 * gray color value and transparency.
49 *
50 * @param aGrayColorValue - diffuse gray value
51 * @param aTransparency - control
52 * @return transparency to use in material
53 */
TransparencyControl(float aGrayColorValue,float aTransparency)54 static float TransparencyControl( float aGrayColorValue, float aTransparency )
55 {
56 const float aaa = aTransparency * aTransparency * aTransparency;
57
58 // 1.00-1.05*(1.0-x)^3
59 float ca = 1.0f - aTransparency;
60 ca = 1.00f - 1.05f * ca * ca * ca;
61
62 return glm::max( glm::min( aGrayColorValue * ca + aaa, 1.0f ), 0.0f );
63 }
64
65 /**
66 * Scale conversion from 3d model units to pcb units
67 */
68 #define UNITS3D_TO_UNITSPCB ( IU_PER_MM )
69
70
setupMaterials()71 void RENDER_3D_RAYTRACE::setupMaterials()
72 {
73 MATERIAL::SetDefaultRefractionRayCount( m_boardAdapter.m_RtRefractionSampleCount );
74 MATERIAL::SetDefaultReflectionRayCount( m_boardAdapter.m_RtReflectionSampleCount );
75
76 MATERIAL::SetDefaultRefractionRecursionCount( m_boardAdapter.m_RtRecursiveRefractionCount );
77 MATERIAL::SetDefaultReflectionRecursionCount( m_boardAdapter.m_RtRecursiveReflectionCount );
78
79 double mmTo3Dunits = IU_PER_MM * m_boardAdapter.BiuTo3dUnits();
80
81 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
82 {
83 m_boardMaterial = BOARD_NORMAL( 0.40f * mmTo3Dunits );
84
85 m_copperMaterial = COPPER_NORMAL( 4.0f * mmTo3Dunits, &m_boardMaterial );
86
87 m_platedCopperMaterial = PLATED_COPPER_NORMAL( 0.5f * mmTo3Dunits );
88
89 m_solderMaskMaterial = SOLDER_MASK_NORMAL( &m_boardMaterial );
90
91 m_plasticMaterial = PLASTIC_NORMAL( 0.05f * mmTo3Dunits );
92
93 m_shinyPlasticMaterial = PLASTIC_SHINE_NORMAL( 0.1f * mmTo3Dunits );
94
95 m_brushedMetalMaterial = BRUSHED_METAL_NORMAL( 0.05f * mmTo3Dunits );
96
97 m_silkScreenMaterial = SILK_SCREEN_NORMAL( 0.25f * mmTo3Dunits );
98 }
99
100 // http://devernay.free.fr/cours/opengl/materials.html
101 // Copper
102 const SFVEC3F copperSpecularLinear =
103 ConvertSRGBToLinear( glm::clamp( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.5f + 0.25f,
104 SFVEC3F( 0.0f ), SFVEC3F( 1.0f ) ) );
105
106 m_materials.m_Copper = BLINN_PHONG_MATERIAL(
107 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.3f ),
108 SFVEC3F( 0.0f ), copperSpecularLinear, 0.4f * 128.0f, 0.0f, 0.0f );
109
110 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
111 m_materials.m_Copper.SetGenerator( &m_platedCopperMaterial );
112
113 m_materials.m_NonPlatedCopper = BLINN_PHONG_MATERIAL(
114 ConvertSRGBToLinear( SFVEC3F( 0.191f, 0.073f, 0.022f ) ), SFVEC3F( 0.0f, 0.0f, 0.0f ),
115 SFVEC3F( 0.256f, 0.137f, 0.086f ), 0.15f * 128.0f, 0.0f, 0.0f );
116
117 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
118 m_materials.m_NonPlatedCopper.SetGenerator( &m_copperMaterial );
119
120 m_materials.m_Paste = BLINN_PHONG_MATERIAL(
121 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
122 * ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
123 SFVEC3F( 0.0f, 0.0f, 0.0f ),
124 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
125 * ConvertSRGBToLinear(
126 (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
127 0.10f * 128.0f, 0.0f, 0.0f );
128
129 m_materials.m_SilkS = BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( SFVEC3F( 0.11f ) ),
130 SFVEC3F( 0.0f, 0.0f, 0.0f ),
131 glm::clamp( ( ( SFVEC3F )( 1.0f ) - ConvertSRGBToLinear(
132 (SFVEC3F) m_boardAdapter.m_SilkScreenColorTop ) ),
133 SFVEC3F( 0.0f ), SFVEC3F( 0.10f ) ), 0.078125f * 128.0f, 0.0f, 0.0f );
134
135 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
136 m_materials.m_SilkS.SetGenerator( &m_silkScreenMaterial );
137
138 // Assume that SolderMaskTop == SolderMaskBot
139 const float solderMask_gray =
140 ( m_boardAdapter.m_SolderMaskColorTop.r + m_boardAdapter.m_SolderMaskColorTop.g
141 + m_boardAdapter.m_SolderMaskColorTop.b )
142 / 3.0f;
143
144 const float solderMask_transparency = TransparencyControl( solderMask_gray,
145 1.0f - m_boardAdapter.m_SolderMaskColorTop.a );
146
147 m_materials.m_SolderMask = BLINN_PHONG_MATERIAL(
148 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorTop ) * 0.10f,
149 SFVEC3F( 0.0f, 0.0f, 0.0f ),
150 SFVEC3F( glm::clamp( solderMask_gray * 2.0f, 0.25f, 1.0f ) ), 0.85f * 128.0f,
151 solderMask_transparency, 0.16f );
152
153 m_materials.m_SolderMask.SetCastShadows( true );
154 m_materials.m_SolderMask.SetRefractionRayCount( 1 );
155
156 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
157 m_materials.m_SolderMask.SetGenerator( &m_solderMaskMaterial );
158
159 m_materials.m_EpoxyBoard =
160 BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( SFVEC3F( 16.0f / 255.0f, 14.0f / 255.0f,
161 10.0f / 255.0f ) ),
162 SFVEC3F( 0.0f, 0.0f, 0.0f ),
163 ConvertSRGBToLinear( SFVEC3F( 10.0f / 255.0f, 8.0f / 255.0f,
164 10.0f / 255.0f ) ),
165 0.1f * 128.0f, 1.0f - m_boardAdapter.m_BoardBodyColor.a, 0.0f );
166
167 m_materials.m_EpoxyBoard.SetAbsorvance( 10.0f );
168
169 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
170 m_materials.m_EpoxyBoard.SetGenerator( &m_boardMaterial );
171
172 SFVEC3F bgTop = ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_BgColorTop );
173
174 m_materials.m_Floor = BLINN_PHONG_MATERIAL( bgTop * 0.125f, SFVEC3F( 0.0f, 0.0f, 0.0f ),
175 ( SFVEC3F( 1.0f ) - bgTop ) / 3.0f,
176 0.10f * 128.0f, 0.0f, 0.50f );
177 m_materials.m_Floor.SetCastShadows( false );
178 m_materials.m_Floor.SetReflectionRecursionCount( 1 );
179 }
180
181
createObject(CONTAINER_3D & aDstContainer,const OBJECT_2D * aObject2D,float aZMin,float aZMax,const MATERIAL * aMaterial,const SFVEC3F & aObjColor)182 void RENDER_3D_RAYTRACE::createObject( CONTAINER_3D& aDstContainer, const OBJECT_2D* aObject2D,
183 float aZMin, float aZMax, const MATERIAL* aMaterial,
184 const SFVEC3F& aObjColor )
185 {
186 switch( aObject2D->GetObjectType() )
187 {
188 case OBJECT_2D_TYPE::DUMMYBLOCK:
189 {
190 m_convertedDummyBlockCount++;
191
192 XY_PLANE* objPtr;
193 objPtr = new XY_PLANE( BBOX_3D(
194 SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMin ),
195 SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMin ) ) );
196 objPtr->SetMaterial( aMaterial );
197 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
198 aDstContainer.Add( objPtr );
199
200 objPtr = new XY_PLANE( BBOX_3D(
201 SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMax ),
202 SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMax ) ) );
203 objPtr->SetMaterial( aMaterial );
204 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
205 aDstContainer.Add( objPtr );
206 break;
207 }
208
209 case OBJECT_2D_TYPE::ROUNDSEG:
210 {
211 m_converted2dRoundSegmentCount++;
212
213 const ROUND_SEGMENT_2D* aRoundSeg2D = static_cast<const ROUND_SEGMENT_2D*>( aObject2D );
214 ROUND_SEGMENT* objPtr = new ROUND_SEGMENT( *aRoundSeg2D, aZMin, aZMax );
215 objPtr->SetMaterial( aMaterial );
216 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
217 aDstContainer.Add( objPtr );
218 break;
219 }
220
221 default:
222 {
223 LAYER_ITEM* objPtr = new LAYER_ITEM( aObject2D, aZMin, aZMax );
224 objPtr->SetMaterial( aMaterial );
225 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
226 aDstContainer.Add( objPtr );
227 break;
228 }
229 }
230 }
231
232
createItemsFromContainer(const BVH_CONTAINER_2D * aContainer2d,PCB_LAYER_ID aLayer_id,const MATERIAL * aMaterialLayer,const SFVEC3F & aLayerColor,float aLayerZOffset)233 void RENDER_3D_RAYTRACE::createItemsFromContainer( const BVH_CONTAINER_2D* aContainer2d,
234 PCB_LAYER_ID aLayer_id,
235 const MATERIAL* aMaterialLayer,
236 const SFVEC3F& aLayerColor,
237 float aLayerZOffset )
238 {
239 if( aContainer2d == nullptr )
240 return;
241
242 const LIST_OBJECT2D& listObject2d = aContainer2d->GetList();
243
244 if( listObject2d.size() == 0 )
245 return;
246
247 for( const OBJECT_2D* object2d_A : listObject2d )
248 {
249 // not yet used / implemented (can be used in future to clip the objects in the
250 // board borders
251 OBJECT_2D* object2d_C = CSGITEM_FULL;
252
253 std::vector<const OBJECT_2D*>* object2d_B = CSGITEM_EMPTY;
254
255 object2d_B = new std::vector<const OBJECT_2D*>();
256
257 // Subtract holes but not in SolderPaste
258 // (can be added as an option in future)
259 if( !( aLayer_id == B_Paste || aLayer_id == F_Paste ) )
260 {
261 // Check if there are any layerhole that intersects this object
262 // Eg: a segment is cut by a via hole or THT hole.
263 const MAP_CONTAINER_2D_BASE& layerHolesMap = m_boardAdapter.GetLayerHoleMap();
264
265 if( layerHolesMap.find( aLayer_id ) != layerHolesMap.end() )
266 {
267 const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( aLayer_id );
268
269 CONST_LIST_OBJECT2D intersecting;
270
271 holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
272
273 for( const OBJECT_2D* hole2d : intersecting )
274 object2d_B->push_back( hole2d );
275 }
276
277 // Check if there are any THT that intersects this object. If we're processing a silk
278 // layer and the flag is set, then clip the silk at the outer edge of the annular ring,
279 // rather than the at the outer edge of the copper plating.
280 const BVH_CONTAINER_2D& throughHoleOuter =
281 ( m_boardAdapter.GetFlag( FL_CLIP_SILK_ON_VIA_ANNULUS )
282 && m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE )
283 && ( ( aLayer_id == B_SilkS ) || ( aLayer_id == F_SilkS ) ) ) ?
284 m_boardAdapter.GetThroughHoleAnnularRings() :
285 m_boardAdapter.GetThroughHoleOds();
286
287 if( !throughHoleOuter.GetList().empty() )
288 {
289 CONST_LIST_OBJECT2D intersecting;
290
291 throughHoleOuter.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
292
293 for( const OBJECT_2D* hole2d : intersecting )
294 object2d_B->push_back( hole2d );
295 }
296 }
297
298 if( !m_antioutlineBoard2dObjects->GetList().empty() )
299 {
300 CONST_LIST_OBJECT2D intersecting;
301
302 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
303 intersecting );
304
305 for( const OBJECT_2D* obj : intersecting )
306 object2d_B->push_back( obj );
307 }
308
309 const MAP_CONTAINER_2D_BASE& mapLayers = m_boardAdapter.GetLayerMap();
310
311 if( m_boardAdapter.GetFlag( FL_SUBTRACT_MASK_FROM_SILK )
312 && m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE )
313 && ( ( aLayer_id == B_SilkS && mapLayers.find( B_Mask ) != mapLayers.end() )
314 || ( aLayer_id == F_SilkS && mapLayers.find( F_Mask ) != mapLayers.end() ) ) )
315 {
316 const PCB_LAYER_ID layerMask_id = ( aLayer_id == B_SilkS ) ? B_Mask : F_Mask;
317
318 const BVH_CONTAINER_2D* containerMaskLayer2d = mapLayers.at( layerMask_id );
319
320 CONST_LIST_OBJECT2D intersecting;
321
322 if( containerMaskLayer2d ) // can be null if B_Mask or F_Mask is not shown
323 containerMaskLayer2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
324
325 for( const OBJECT_2D* obj2d : intersecting )
326 object2d_B->push_back( obj2d );
327 }
328
329 if( object2d_B->empty() )
330 {
331 delete object2d_B;
332 object2d_B = CSGITEM_EMPTY;
333 }
334
335 if( ( object2d_B == CSGITEM_EMPTY ) && ( object2d_C == CSGITEM_FULL ) )
336 {
337 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
338 m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
339 m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
340 objPtr->SetMaterial( aMaterialLayer );
341 objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
342 m_objectContainer.Add( objPtr );
343 }
344 else
345 {
346 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, object2d_C,
347 object2d_A->GetBoardItem() );
348 m_containerWithObjectsToDelete.Add( itemCSG2d );
349
350 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
351 m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
352 m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
353
354 objPtr->SetMaterial( aMaterialLayer );
355 objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
356
357 m_objectContainer.Add( objPtr );
358 }
359 }
360 }
361
362
363 extern void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline );
364
365
Reload(REPORTER * aStatusReporter,REPORTER * aWarningReporter,bool aOnlyLoadCopperAndShapes)366 void RENDER_3D_RAYTRACE::Reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter,
367 bool aOnlyLoadCopperAndShapes )
368 {
369 m_reloadRequested = false;
370
371 m_modelMaterialMap.clear();
372
373 OBJECT_2D_STATS::Instance().ResetStats();
374 OBJECT_3D_STATS::Instance().ResetStats();
375
376 unsigned stats_startReloadTime = GetRunningMicroSecs();
377
378 if( !aOnlyLoadCopperAndShapes )
379 {
380 m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
381
382 SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
383 m_camera.SetBoardLookAtPos( camera_pos );
384 }
385
386 m_objectContainer.Clear();
387 m_containerWithObjectsToDelete.Clear();
388
389 setupMaterials();
390
391 if( aStatusReporter )
392 aStatusReporter->Report( _( "Load Raytracing: board" ) );
393
394 // Create and add the outline board
395 delete m_outlineBoard2dObjects;
396 delete m_antioutlineBoard2dObjects;
397
398 m_outlineBoard2dObjects = new CONTAINER_2D;
399 m_antioutlineBoard2dObjects = new BVH_CONTAINER_2D;
400
401 if( !aOnlyLoadCopperAndShapes )
402 {
403 const int outlineCount = m_boardAdapter.GetBoardPoly().OutlineCount();
404
405 if( outlineCount > 0 )
406 {
407 float divFactor = 0.0f;
408
409 if( m_boardAdapter.GetViaCount() )
410 divFactor = m_boardAdapter.GetAverageViaHoleDiameter() * 18.0f;
411 else if( m_boardAdapter.GetHoleCount() )
412 divFactor = m_boardAdapter.GetAverageHoleDiameter() * 8.0f;
413
414 SHAPE_POLY_SET boardPolyCopy = m_boardAdapter.GetBoardPoly();
415
416 // Calculate an antiboard outline
417 SHAPE_POLY_SET antiboardPoly;
418
419 buildBoardBoundingBoxPoly( m_boardAdapter.GetBoard(), antiboardPoly );
420
421 antiboardPoly.BooleanSubtract( boardPolyCopy, SHAPE_POLY_SET::PM_FAST );
422 antiboardPoly.Fracture( SHAPE_POLY_SET::PM_FAST );
423
424 for( int ii = 0; ii < antiboardPoly.OutlineCount(); ii++ )
425 {
426 ConvertPolygonToBlocks( antiboardPoly, *m_antioutlineBoard2dObjects,
427 m_boardAdapter.BiuTo3dUnits(), -1.0f,
428 *m_boardAdapter.GetBoard(), ii );
429 }
430
431 m_antioutlineBoard2dObjects->BuildBVH();
432
433 boardPolyCopy.Fracture( SHAPE_POLY_SET::PM_FAST );
434
435 for( int ii = 0; ii < outlineCount; ii++ )
436 {
437 ConvertPolygonToBlocks( boardPolyCopy, *m_outlineBoard2dObjects,
438 m_boardAdapter.BiuTo3dUnits(), divFactor,
439 *m_boardAdapter.GetBoard(), ii );
440 }
441
442 if( m_boardAdapter.GetFlag( FL_SHOW_BOARD_BODY ) )
443 {
444 const LIST_OBJECT2D& listObjects = m_outlineBoard2dObjects->GetList();
445
446 for( const OBJECT_2D* object2d_A : listObjects )
447 {
448 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
449
450 // Check if there are any THT that intersects this outline object part
451 if( !m_boardAdapter.GetThroughHoleOds().GetList().empty() )
452 {
453 const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetThroughHoleOds();
454 CONST_LIST_OBJECT2D intersecting;
455
456 throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
457
458 for( const OBJECT_2D* hole : intersecting )
459 {
460 if( object2d_A->Intersects( hole->GetBBox() ) )
461 object2d_B->push_back( hole );
462 }
463 }
464
465 if( !m_antioutlineBoard2dObjects->GetList().empty() )
466 {
467 CONST_LIST_OBJECT2D intersecting;
468
469 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
470 intersecting );
471
472 for( const OBJECT_2D* obj : intersecting )
473 object2d_B->push_back( obj );
474 }
475
476 if( object2d_B->empty() )
477 {
478 delete object2d_B;
479 object2d_B = CSGITEM_EMPTY;
480 }
481
482 if( object2d_B == CSGITEM_EMPTY )
483 {
484 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
485 m_boardAdapter.GetLayerBottomZPos( F_Cu ),
486 m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
487
488 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
489 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
490 m_objectContainer.Add( objPtr );
491 }
492 else
493 {
494
495 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
496 CSGITEM_FULL,
497 *m_boardAdapter.GetBoard() );
498
499 m_containerWithObjectsToDelete.Add( itemCSG2d );
500
501 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
502 m_boardAdapter.GetLayerBottomZPos( F_Cu ),
503 m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
504
505 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
506 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
507 m_objectContainer.Add( objPtr );
508 }
509 }
510
511 // Add cylinders of the board body to container
512 // Note: This is actually a workaround for the holes in the board.
513 // The issue is because if a hole is in a border of a divided polygon ( ex
514 // a polygon or dummy block) it will cut also the render of the hole.
515 // So this will add a full hole.
516 // In fact, that is not need if the hole have copper.
517 if( !m_boardAdapter.GetThroughHoleOds().GetList().empty() )
518 {
519 const LIST_OBJECT2D& holeList = m_boardAdapter.GetThroughHoleOds().GetList();
520
521 for( const OBJECT_2D* hole2d : holeList )
522 {
523 if( !m_antioutlineBoard2dObjects->GetList().empty() )
524 {
525 CONST_LIST_OBJECT2D intersecting;
526
527 m_antioutlineBoard2dObjects->GetIntersectingObjects( hole2d->GetBBox(),
528 intersecting );
529
530 // Do not add cylinder if it intersects the edge of the board
531 if( !intersecting.empty() )
532 continue;
533 }
534
535 switch( hole2d->GetObjectType() )
536 {
537 case OBJECT_2D_TYPE::FILLED_CIRCLE:
538 {
539 const float radius = hole2d->GetBBox().GetExtent().x * 0.5f * 0.999f;
540
541 CYLINDER* objPtr = new CYLINDER( hole2d->GetCentroid(),
542 NextFloatDown( m_boardAdapter.GetLayerBottomZPos( F_Cu ) ),
543 NextFloatUp( m_boardAdapter.GetLayerBottomZPos( B_Cu ) ),
544 radius );
545
546 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
547 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
548
549 m_objectContainer.Add( objPtr );
550 }
551 break;
552
553 default:
554 break;
555 }
556 }
557 }
558 }
559 }
560 }
561
562 if( aStatusReporter )
563 aStatusReporter->Report( _( "Load Raytracing: layers" ) );
564
565 // Add layers maps (except B_Mask and F_Mask)
566 for( auto entry : m_boardAdapter.GetLayerMap() )
567 {
568 PCB_LAYER_ID layer_id = entry.first;
569 const BVH_CONTAINER_2D* container2d = entry.second;
570
571 // Only process layers that exist
572 if( !container2d )
573 continue;
574
575 if( aOnlyLoadCopperAndShapes && !IsCopperLayer( layer_id ) )
576 continue;
577
578 // Mask layers are not processed here because they are a special case
579 if( layer_id == B_Mask || layer_id == F_Mask )
580 continue;
581
582 MATERIAL* materialLayer = &m_materials.m_SilkS;
583 SFVEC3F layerColor = SFVEC3F( 0.0f, 0.0f, 0.0f );
584
585 switch( layer_id )
586 {
587 case B_Adhes:
588 case F_Adhes:
589 break;
590
591 case B_Paste:
592 case F_Paste:
593 materialLayer = &m_materials.m_Paste;
594
595 if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
596 layerColor = m_boardAdapter.m_SolderPasteColor;
597 else
598 layerColor = m_boardAdapter.GetLayerColor( layer_id );
599
600 break;
601
602 case B_SilkS:
603 materialLayer = &m_materials.m_SilkS;
604
605 if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
606 layerColor = m_boardAdapter.m_SilkScreenColorBot;
607 else
608 layerColor = m_boardAdapter.GetLayerColor( layer_id );
609
610 break;
611
612 case F_SilkS:
613 materialLayer = &m_materials.m_SilkS;
614
615 if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
616 layerColor = m_boardAdapter.m_SilkScreenColorTop;
617 else
618 layerColor = m_boardAdapter.GetLayerColor( layer_id );
619
620 break;
621
622 case Dwgs_User:
623 case Cmts_User:
624 case Eco1_User:
625 case Eco2_User:
626 case Edge_Cuts:
627 case Margin:
628 break;
629
630 case B_CrtYd:
631 case F_CrtYd:
632 break;
633
634 case B_Fab:
635 case F_Fab:
636 break;
637
638 default:
639 if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
640 {
641 if( m_boardAdapter.GetFlag( FL_RENDER_PLATED_PADS_AS_PLATED ) )
642 layerColor = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f, 50.0f / 255.0f );
643 else
644 layerColor = m_boardAdapter.m_CopperColor;
645 }
646 else
647 {
648 layerColor = m_boardAdapter.GetLayerColor( layer_id );
649 }
650
651 materialLayer = &m_materials.m_NonPlatedCopper;
652 break;
653 }
654
655 createItemsFromContainer( container2d, layer_id, materialLayer, layerColor, 0.0f );
656 } // for each layer on map
657
658 // Create plated copper
659 if( m_boardAdapter.GetFlag( FL_RENDER_PLATED_PADS_AS_PLATED )
660 && m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
661 {
662 createItemsFromContainer( m_boardAdapter.GetPlatedPadsFront(), F_Cu, &m_materials.m_Copper,
663 m_boardAdapter.m_CopperColor,
664 m_boardAdapter.GetCopperThickness() * 0.1f );
665
666 createItemsFromContainer( m_boardAdapter.GetPlatedPadsBack(), B_Cu, &m_materials.m_Copper,
667 m_boardAdapter.m_CopperColor,
668 -m_boardAdapter.GetCopperThickness() * 0.1f );
669 }
670
671 if( !aOnlyLoadCopperAndShapes )
672 {
673 // Add Mask layer
674 // Solder mask layers are "negative" layers so the elements that we have in the container
675 // should remove the board outline. We will check for all objects in the outline if it
676 // intersects any object in the layer container and also any hole.
677 if( m_boardAdapter.GetFlag( FL_SOLDERMASK ) && !m_outlineBoard2dObjects->GetList().empty() )
678 {
679 const MATERIAL* materialLayer = &m_materials.m_SolderMask;
680
681 for( auto entry : m_boardAdapter.GetLayerMap() )
682 {
683 PCB_LAYER_ID layer_id = entry.first;
684 const BVH_CONTAINER_2D* container2d = entry.second;
685
686 // Only process layers that exist
687 if( !container2d )
688 continue;
689
690 // Only get the Solder mask layers (and only if the board has them)
691 if( !( layer_id == B_Mask || layer_id == F_Mask ) )
692 continue;
693
694 SFVEC3F layerColor;
695
696 if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
697 {
698 if( layer_id == B_Mask )
699 layerColor = m_boardAdapter.m_SolderMaskColorBot;
700 else
701 layerColor = m_boardAdapter.m_SolderMaskColorTop;
702 }
703 else
704 {
705 layerColor = m_boardAdapter.GetLayerColor( layer_id );
706 }
707
708 const float zLayerMin = m_boardAdapter.GetLayerBottomZPos( layer_id );
709 const float zLayerMax = m_boardAdapter.GetLayerTopZPos( layer_id );
710
711 // Get the outline board objects
712 for( const OBJECT_2D* object2d_A : m_outlineBoard2dObjects->GetList() )
713 {
714 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
715
716 // Check if there are any THT that intersects this outline object part
717 if( !m_boardAdapter.GetThroughHoleOds().GetList().empty() )
718 {
719 const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetThroughHoleOds();
720 CONST_LIST_OBJECT2D intersecting;
721
722 throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
723
724 for( const OBJECT_2D* hole : intersecting )
725 {
726 if( object2d_A->Intersects( hole->GetBBox() ) )
727 object2d_B->push_back( hole );
728 }
729 }
730
731 // Check if there are any objects in the layer to subtract with the current
732 // object
733 if( !container2d->GetList().empty() )
734 {
735 CONST_LIST_OBJECT2D intersecting;
736
737 container2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
738
739 for( const OBJECT_2D* obj : intersecting )
740 object2d_B->push_back( obj );
741 }
742
743 if( object2d_B->empty() )
744 {
745 delete object2d_B;
746 object2d_B = CSGITEM_EMPTY;
747 }
748
749 if( object2d_B == CSGITEM_EMPTY )
750 {
751 #if 0
752 createObject( m_objectContainer, object2d_A, zLayerMin, zLayerMax,
753 materialLayer, layerColor );
754 #else
755 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, zLayerMin, zLayerMax );
756
757 objPtr->SetMaterial( materialLayer );
758 objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
759
760 m_objectContainer.Add( objPtr );
761 #endif
762 }
763 else
764 {
765 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
766 CSGITEM_FULL,
767 object2d_A->GetBoardItem() );
768
769 m_containerWithObjectsToDelete.Add( itemCSG2d );
770
771 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, zLayerMin, zLayerMax );
772 objPtr->SetMaterial( materialLayer );
773 objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
774
775 m_objectContainer.Add( objPtr );
776 }
777 }
778 }
779 }
780
781 addPadsAndVias();
782 }
783
784 #ifdef PRINT_STATISTICS_3D_VIEWER
785 unsigned stats_endConvertTime = GetRunningMicroSecs();
786 unsigned stats_startLoad3DmodelsTime = stats_endConvertTime;
787 #endif
788
789 if( aStatusReporter )
790 aStatusReporter->Report( _( "Loading 3D models..." ) );
791
792 load3DModels( m_objectContainer, aOnlyLoadCopperAndShapes );
793
794 #ifdef PRINT_STATISTICS_3D_VIEWER
795 unsigned stats_endLoad3DmodelsTime = GetRunningMicroSecs();
796 #endif
797
798 if( !aOnlyLoadCopperAndShapes )
799 {
800 // Add floor
801 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_BACKFLOOR ) )
802 {
803 BBOX_3D boardBBox = m_boardAdapter.GetBBox();
804
805 if( boardBBox.IsInitialized() )
806 {
807 boardBBox.Scale( 3.0f );
808
809 if( m_objectContainer.GetList().size() > 0 )
810 {
811 BBOX_3D containerBBox = m_objectContainer.GetBBox();
812
813 containerBBox.Scale( 1.3f );
814
815 const SFVEC3F centerBBox = containerBBox.GetCenter();
816
817 // Floor triangles
818 const float minZ = glm::min( containerBBox.Min().z, boardBBox.Min().z );
819
820 const SFVEC3F v1 =
821 SFVEC3F( -RANGE_SCALE_3D * 4.0f, -RANGE_SCALE_3D * 4.0f, minZ )
822 + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
823
824 const SFVEC3F v3 =
825 SFVEC3F( +RANGE_SCALE_3D * 4.0f, +RANGE_SCALE_3D * 4.0f, minZ )
826 + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
827
828 const SFVEC3F v2 = SFVEC3F( v1.x, v3.y, v1.z );
829 const SFVEC3F v4 = SFVEC3F( v3.x, v1.y, v1.z );
830
831 SFVEC3F backgroundColor = ConvertSRGBToLinear( m_boardAdapter.m_BgColorTop );
832
833 TRIANGLE* newTriangle1 = new TRIANGLE( v1, v2, v3 );
834 TRIANGLE* newTriangle2 = new TRIANGLE( v3, v4, v1 );
835
836 m_objectContainer.Add( newTriangle1 );
837 m_objectContainer.Add( newTriangle2 );
838
839 newTriangle1->SetMaterial( &m_materials.m_Floor );
840 newTriangle2->SetMaterial( &m_materials.m_Floor );
841
842 newTriangle1->SetColor( backgroundColor );
843 newTriangle2->SetColor( backgroundColor );
844
845 // Ceiling triangles
846 const float maxZ = glm::max( containerBBox.Max().z, boardBBox.Max().z );
847
848 const SFVEC3F v5 = SFVEC3F( v1.x, v1.y, maxZ );
849 const SFVEC3F v6 = SFVEC3F( v2.x, v2.y, maxZ );
850 const SFVEC3F v7 = SFVEC3F( v3.x, v3.y, maxZ );
851 const SFVEC3F v8 = SFVEC3F( v4.x, v4.y, maxZ );
852
853 TRIANGLE* newTriangle3 = new TRIANGLE( v7, v6, v5 );
854 TRIANGLE* newTriangle4 = new TRIANGLE( v5, v8, v7 );
855
856 m_objectContainer.Add( newTriangle3 );
857 m_objectContainer.Add( newTriangle4 );
858
859 newTriangle3->SetMaterial( &m_materials.m_Floor );
860 newTriangle4->SetMaterial( &m_materials.m_Floor );
861
862 newTriangle3->SetColor( backgroundColor );
863 newTriangle4->SetColor( backgroundColor );
864 }
865 }
866 }
867
868 // Init initial lights
869 for( LIGHT* light : m_lights )
870 delete light;
871
872 m_lights.clear();
873
874 auto IsColorZero =
875 []( const SFVEC3F& aSource )
876 {
877 return ( ( aSource.r < ( 1.0f / 255.0f ) ) && ( aSource.g < ( 1.0f / 255.0f ) )
878 && ( aSource.b < ( 1.0f / 255.0f ) ) );
879 };
880
881 m_cameraLight = new DIRECTIONAL_LIGHT( SFVEC3F( 0.0f, 0.0f, 0.0f ),
882 m_boardAdapter.m_RtCameraLightColor );
883 m_cameraLight->SetCastShadows( false );
884
885 if( !IsColorZero( m_boardAdapter.m_RtCameraLightColor ) )
886 m_lights.push_back( m_cameraLight );
887
888 const SFVEC3F& boardCenter = m_boardAdapter.GetBBox().GetCenter();
889
890 if( !IsColorZero( m_boardAdapter.m_RtLightColorTop ) )
891 {
892 m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
893 +RANGE_SCALE_3D * 2.0f ),
894 m_boardAdapter.m_RtLightColorTop ) );
895 }
896
897 if( !IsColorZero( m_boardAdapter.m_RtLightColorBottom ) )
898 {
899 m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
900 -RANGE_SCALE_3D * 2.0f ),
901 m_boardAdapter.m_RtLightColorBottom ) );
902 }
903
904 wxASSERT( m_boardAdapter.m_RtLightColor.size()
905 == m_boardAdapter.m_RtLightSphericalCoords.size() );
906
907 for( size_t i = 0; i < m_boardAdapter.m_RtLightColor.size(); ++i )
908 {
909 if( !IsColorZero( m_boardAdapter.m_RtLightColor[i] ) )
910 {
911 const SFVEC2F sc = m_boardAdapter.m_RtLightSphericalCoords[i];
912
913 m_lights.push_back( new DIRECTIONAL_LIGHT(
914 SphericalToCartesian( glm::pi<float>() * sc.x, glm::pi<float>() * sc.y ),
915 m_boardAdapter.m_RtLightColor[i] ) );
916 }
917 }
918 }
919
920 // Set min. and max. zoom range. This doesn't really fit here, but moving this outside of this
921 // class would require reimplementing bounding box calculation (feel free to do this if you
922 // have time and patience).
923 if( m_objectContainer.GetList().size() > 0 )
924 {
925 float ratio =
926 std::max( 1.0f, m_objectContainer.GetBBox().GetMaxDimension() / RANGE_SCALE_3D );
927 m_camera.SetMaxZoom( m_camera.GetMaxZoom() * ratio );
928
929 m_camera.SetMinZoom( static_cast<float>( MIN_DISTANCE_IU * m_boardAdapter.BiuTo3dUnits()
930 / -m_camera.GetCameraInitPos().z ) );
931 }
932
933 // Create an accelerator
934 delete m_accelerator;
935 m_accelerator = new BVH_PBRT( m_objectContainer, 8, SPLITMETHOD::MIDDLE );
936
937 if( aStatusReporter )
938 {
939 // Calculation time in seconds
940 double calculation_time = (double) GetRunningMicroSecs() - stats_startReloadTime / 1e6;
941
942 aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
943 }
944 }
945
946
insertHole(const PCB_VIA * aVia)947 void RENDER_3D_RAYTRACE::insertHole( const PCB_VIA* aVia )
948 {
949 PCB_LAYER_ID top_layer, bottom_layer;
950 int radiusBUI = ( aVia->GetDrillValue() / 2 );
951
952 aVia->LayerPair( &top_layer, &bottom_layer );
953
954 float topZ = m_boardAdapter.GetLayerBottomZPos( top_layer )
955 + m_boardAdapter.GetCopperThickness();
956
957 float botZ = m_boardAdapter.GetLayerBottomZPos( bottom_layer )
958 - m_boardAdapter.GetCopperThickness();
959
960 const SFVEC2F center = SFVEC2F( aVia->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
961 -aVia->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
962
963 RING_2D* ring = new RING_2D( center, radiusBUI * m_boardAdapter.BiuTo3dUnits(),
964 ( radiusBUI + m_boardAdapter.GetHolePlatingThickness() )
965 * m_boardAdapter.BiuTo3dUnits(), *aVia );
966
967 m_containerWithObjectsToDelete.Add( ring );
968
969 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, topZ, botZ );
970
971 objPtr->SetMaterial( &m_materials.m_Copper );
972
973 if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
974 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
975 else if( aVia->GetViaType() == VIATYPE::MICROVIA )
976 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.GetItemColor( LAYER_VIA_MICROVIA ) ) );
977 else if( aVia->GetViaType() == VIATYPE::BLIND_BURIED )
978 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.GetItemColor( LAYER_VIA_BBLIND ) ) );
979 else
980 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.GetItemColor( LAYER_VIAS ) ) );
981
982 m_objectContainer.Add( objPtr );
983 }
984
985
insertHole(const PAD * aPad)986 void RENDER_3D_RAYTRACE::insertHole( const PAD* aPad )
987 {
988 const OBJECT_2D* object2d_A = nullptr;
989
990 SFVEC3F objColor;
991
992 if( m_boardAdapter.GetFlag( FL_USE_REALISTIC_MODE ) )
993 objColor = m_boardAdapter.m_CopperColor;
994 else
995 objColor = m_boardAdapter.GetItemColor( LAYER_PADS_TH );
996
997 const wxSize drillsize = aPad->GetDrillSize();
998 const bool hasHole = drillsize.x && drillsize.y;
999
1000 if( !hasHole )
1001 return;
1002
1003 CONST_LIST_OBJECT2D antiOutlineIntersectionList;
1004
1005 const float topZ = m_boardAdapter.GetLayerBottomZPos( F_Cu )
1006 + m_boardAdapter.GetCopperThickness() * 0.99f;
1007
1008 const float botZ = m_boardAdapter.GetLayerBottomZPos( B_Cu )
1009 - m_boardAdapter.GetCopperThickness() * 0.99f;
1010
1011 if( drillsize.x == drillsize.y ) // usual round hole
1012 {
1013 SFVEC2F center = SFVEC2F( aPad->GetPosition().x * m_boardAdapter.BiuTo3dUnits(),
1014 -aPad->GetPosition().y * m_boardAdapter.BiuTo3dUnits() );
1015
1016 int innerRadius = drillsize.x / 2;
1017 int outerRadius = innerRadius + m_boardAdapter.GetHolePlatingThickness();
1018
1019 RING_2D* ring = new RING_2D( center, innerRadius * m_boardAdapter.BiuTo3dUnits(),
1020 outerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
1021
1022 m_containerWithObjectsToDelete.Add( ring );
1023
1024 object2d_A = ring;
1025
1026 // If the object (ring) is intersected by an antioutline board,
1027 // it will use instead a CSG of two circles.
1028 if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
1029 {
1030 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
1031 antiOutlineIntersectionList );
1032 }
1033
1034 if( !antiOutlineIntersectionList.empty() )
1035 {
1036 FILLED_CIRCLE_2D* innerCircle = new FILLED_CIRCLE_2D(
1037 center, innerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
1038
1039 FILLED_CIRCLE_2D* outterCircle = new FILLED_CIRCLE_2D(
1040 center, outerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
1041 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1042 object2d_B->push_back( innerCircle );
1043
1044 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outterCircle, object2d_B, CSGITEM_FULL,
1045 *aPad );
1046
1047 m_containerWithObjectsToDelete.Add( itemCSG2d );
1048 m_containerWithObjectsToDelete.Add( innerCircle );
1049 m_containerWithObjectsToDelete.Add( outterCircle );
1050
1051 object2d_A = itemCSG2d;
1052 }
1053 }
1054 else // Oblong hole
1055 {
1056 wxPoint ends_offset;
1057 int width;
1058
1059 if( drillsize.x > drillsize.y ) // Horizontal oval
1060 {
1061 ends_offset.x = ( drillsize.x - drillsize.y ) / 2;
1062 width = drillsize.y;
1063 }
1064 else // Vertical oval
1065 {
1066 ends_offset.y = ( drillsize.y - drillsize.x ) / 2;
1067 width = drillsize.x;
1068 }
1069
1070 RotatePoint( &ends_offset, aPad->GetOrientation() );
1071
1072 wxPoint start = aPad->GetPosition() + ends_offset;
1073 wxPoint end = aPad->GetPosition() - ends_offset;
1074
1075 ROUND_SEGMENT_2D* innerSeg =
1076 new ROUND_SEGMENT_2D( SFVEC2F( start.x * m_boardAdapter.BiuTo3dUnits(),
1077 -start.y * m_boardAdapter.BiuTo3dUnits() ),
1078 SFVEC2F( end.x * m_boardAdapter.BiuTo3dUnits(),
1079 -end.y * m_boardAdapter.BiuTo3dUnits() ),
1080 width * m_boardAdapter.BiuTo3dUnits(), *aPad );
1081
1082 ROUND_SEGMENT_2D* outerSeg =
1083 new ROUND_SEGMENT_2D( SFVEC2F( start.x * m_boardAdapter.BiuTo3dUnits(),
1084 -start.y * m_boardAdapter.BiuTo3dUnits() ),
1085 SFVEC2F( end.x * m_boardAdapter.BiuTo3dUnits(),
1086 -end.y * m_boardAdapter.BiuTo3dUnits() ),
1087 ( width + m_boardAdapter.GetHolePlatingThickness() * 2 )
1088 * m_boardAdapter.BiuTo3dUnits(), *aPad );
1089
1090 // NOTE: the round segment width is the "diameter", so we double the thickness
1091 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1092 object2d_B->push_back( innerSeg );
1093
1094 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outerSeg, object2d_B, CSGITEM_FULL, *aPad );
1095
1096 m_containerWithObjectsToDelete.Add( itemCSG2d );
1097 m_containerWithObjectsToDelete.Add( innerSeg );
1098 m_containerWithObjectsToDelete.Add( outerSeg );
1099
1100 object2d_A = itemCSG2d;
1101
1102 if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
1103 {
1104 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
1105 antiOutlineIntersectionList );
1106 }
1107 }
1108
1109 if( object2d_A )
1110 {
1111 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1112
1113 // Check if there are any other THT that intersects this hole
1114 // It will use the non inflated holes
1115 if( !m_boardAdapter.GetThroughHoleIds().GetList().empty() )
1116 {
1117 CONST_LIST_OBJECT2D intersecting;
1118
1119 m_boardAdapter.GetThroughHoleIds().GetIntersectingObjects( object2d_A->GetBBox(),
1120 intersecting );
1121
1122 for( const OBJECT_2D* hole2d : intersecting )
1123 {
1124 if( object2d_A->Intersects( hole2d->GetBBox() ) )
1125 object2d_B->push_back( hole2d );
1126 }
1127 }
1128
1129 for( const OBJECT_2D* obj : antiOutlineIntersectionList )
1130 object2d_B->push_back( obj );
1131
1132 if( object2d_B->empty() )
1133 {
1134 delete object2d_B;
1135 object2d_B = CSGITEM_EMPTY;
1136 }
1137
1138 if( object2d_B == CSGITEM_EMPTY )
1139 {
1140 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, topZ, botZ );
1141
1142 objPtr->SetMaterial( &m_materials.m_Copper );
1143 objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
1144 m_objectContainer.Add( objPtr );
1145 }
1146 else
1147 {
1148 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, CSGITEM_FULL,
1149 *aPad );
1150
1151 m_containerWithObjectsToDelete.Add( itemCSG2d );
1152
1153 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, topZ, botZ );
1154
1155 objPtr->SetMaterial( &m_materials.m_Copper );
1156 objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
1157
1158 m_objectContainer.Add( objPtr );
1159 }
1160 }
1161 }
1162
1163
addPadsAndVias()1164 void RENDER_3D_RAYTRACE::addPadsAndVias()
1165 {
1166 if( !m_boardAdapter.GetBoard() )
1167 return;
1168
1169 // Insert plated vertical holes inside the board
1170
1171 // Insert vias holes (vertical cylinders)
1172 for( PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1173 {
1174 if( track->Type() == PCB_VIA_T )
1175 {
1176 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1177 insertHole( via );
1178 }
1179 }
1180
1181 // Insert pads holes (vertical cylinders)
1182 for( FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1183 {
1184 for( PAD* pad : footprint->Pads() )
1185 {
1186 if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
1187 insertHole( pad );
1188 }
1189 }
1190 }
1191
1192
load3DModels(CONTAINER_3D & aDstContainer,bool aSkipMaterialInformation)1193 void RENDER_3D_RAYTRACE::load3DModels( CONTAINER_3D& aDstContainer, bool aSkipMaterialInformation )
1194 {
1195 if( !m_boardAdapter.GetBoard() )
1196 return;
1197
1198 if( !m_boardAdapter.GetFlag( FL_FP_ATTRIBUTES_NORMAL )
1199 && !m_boardAdapter.GetFlag( FL_FP_ATTRIBUTES_NORMAL_INSERT )
1200 && !m_boardAdapter.GetFlag( FL_FP_ATTRIBUTES_VIRTUAL ) )
1201 {
1202 return;
1203 }
1204
1205 // Go for all footprints
1206 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1207 {
1208 if( !fp->Models().empty()
1209 && m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
1210 {
1211 double zpos = m_boardAdapter.GetFootprintZPos( fp->IsFlipped() );
1212
1213 wxPoint pos = fp->GetPosition();
1214
1215 glm::mat4 fpMatrix = glm::mat4( 1.0f );
1216
1217 fpMatrix = glm::translate( fpMatrix,
1218 SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1219 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1220 zpos ) );
1221
1222 if( fp->GetOrientation() )
1223 {
1224 fpMatrix = glm::rotate( fpMatrix,
1225 ( (float) ( fp->GetOrientation() / 10.0f ) / 180.0f ) * glm::pi<float>(),
1226 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1227 }
1228
1229 if( fp->IsFlipped() )
1230 {
1231 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1232
1233 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1234 }
1235
1236 const double modelunit_to_3d_units_factor =
1237 m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1238
1239 fpMatrix = glm::scale(
1240 fpMatrix, SFVEC3F( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
1241 modelunit_to_3d_units_factor ) );
1242
1243 BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( fp );
1244
1245 // Get the list of model files for this model
1246 S3D_CACHE* cacheMgr = m_boardAdapter.Get3dCacheManager();
1247 auto sM = fp->Models().begin();
1248 auto eM = fp->Models().end();
1249
1250 while( sM != eM )
1251 {
1252 if( ( static_cast<float>( sM->m_Opacity ) > FLT_EPSILON )
1253 && ( sM->m_Show && !sM->m_Filename.empty() ) )
1254 {
1255 // get it from cache
1256 const S3DMODEL* modelPtr = cacheMgr->GetModel( sM->m_Filename );
1257
1258 // only add it if the return is not NULL.
1259 if( modelPtr )
1260 {
1261 glm::mat4 modelMatrix = fpMatrix;
1262
1263 modelMatrix = glm::translate( modelMatrix,
1264 SFVEC3F( sM->m_Offset.x, sM->m_Offset.y, sM->m_Offset.z ) );
1265
1266 modelMatrix = glm::rotate( modelMatrix,
1267 (float) -( sM->m_Rotation.z / 180.0f ) * glm::pi<float>(),
1268 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1269
1270 modelMatrix = glm::rotate( modelMatrix,
1271 (float) -( sM->m_Rotation.y / 180.0f ) * glm::pi<float>(),
1272 SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1273
1274 modelMatrix = glm::rotate( modelMatrix,
1275 (float) -( sM->m_Rotation.x / 180.0f ) * glm::pi<float>(),
1276 SFVEC3F( 1.0f, 0.0f, 0.0f ) );
1277
1278 modelMatrix = glm::scale( modelMatrix,
1279 SFVEC3F( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ) );
1280
1281 addModels( aDstContainer, modelPtr, modelMatrix, (float) sM->m_Opacity,
1282 aSkipMaterialInformation, boardItem );
1283 }
1284 }
1285
1286 ++sM;
1287 }
1288 }
1289 }
1290 }
1291
1292
getModelMaterial(const S3DMODEL * a3DModel)1293 MODEL_MATERIALS* RENDER_3D_RAYTRACE::getModelMaterial( const S3DMODEL* a3DModel )
1294 {
1295 MODEL_MATERIALS* materialVector;
1296
1297 // Try find if the materials already exists in the map list
1298 if( m_modelMaterialMap.find( a3DModel ) != m_modelMaterialMap.end() )
1299 {
1300 // Found it, so get the pointer
1301 materialVector = &m_modelMaterialMap[a3DModel];
1302 }
1303 else
1304 {
1305 // Materials was not found in the map, so it will create a new for
1306 // this model.
1307
1308 m_modelMaterialMap[a3DModel] = MODEL_MATERIALS();
1309 materialVector = &m_modelMaterialMap[a3DModel];
1310
1311 materialVector->resize( a3DModel->m_MaterialsSize );
1312
1313 for( unsigned int imat = 0; imat < a3DModel->m_MaterialsSize; ++imat )
1314 {
1315 if( m_boardAdapter.GetMaterialMode() == MATERIAL_MODE::NORMAL )
1316 {
1317 const SMATERIAL& material = a3DModel->m_Materials[imat];
1318
1319 // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJtaW4oc3FydCh4LTAuMzUpKjAuNDAtMC4wNSwxLjApIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiMC4wNzA3NzM2NzMyMzY1OTAxMiIsIjEuNTY5NTcxNjI5MjI1NDY5OCIsIi0wLjI3NDYzNTMyMTc1OTkyOTMiLCIwLjY0NzcwMTg4MTkyNTUzNjIiXSwic2l6ZSI6WzY0NCwzOTRdfV0-
1320
1321 float reflectionFactor = 0.0f;
1322
1323 if( ( material.m_Shininess - 0.35f ) > FLT_EPSILON )
1324 {
1325 reflectionFactor = glm::clamp(
1326 glm::sqrt( ( material.m_Shininess - 0.35f ) ) * 0.40f - 0.05f, 0.0f,
1327 0.5f );
1328 }
1329
1330 BLINN_PHONG_MATERIAL& blinnMaterial = ( *materialVector )[imat];
1331
1332 blinnMaterial = BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( material.m_Ambient ),
1333 ConvertSRGBToLinear( material.m_Emissive ),
1334 ConvertSRGBToLinear( material.m_Specular ), material.m_Shininess * 180.0f,
1335 material.m_Transparency, reflectionFactor );
1336
1337 if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) )
1338 {
1339 // Guess material type and apply a normal perturbator
1340 if( ( RGBtoGray( material.m_Diffuse ) < 0.3f )
1341 && ( material.m_Shininess < 0.36f )
1342 && ( material.m_Transparency == 0.0f )
1343 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) < 0.15f )
1344 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
1345 < 0.15f )
1346 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
1347 < 0.15f ) ) )
1348 {
1349 // This may be a black plastic..
1350 blinnMaterial.SetGenerator( &m_plasticMaterial );
1351 }
1352 else
1353 {
1354 if( ( RGBtoGray( material.m_Diffuse ) > 0.3f )
1355 && ( material.m_Shininess < 0.30f )
1356 && ( material.m_Transparency == 0.0f )
1357 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) > 0.25f )
1358 || ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) > 0.25f )
1359 || ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
1360 > 0.25f ) ) )
1361 {
1362 // This may be a color plastic ...
1363 blinnMaterial.SetGenerator( &m_shinyPlasticMaterial );
1364 }
1365 else
1366 {
1367 if( ( RGBtoGray( material.m_Diffuse ) > 0.6f )
1368 && ( material.m_Shininess > 0.35f )
1369 && ( material.m_Transparency == 0.0f )
1370 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g )
1371 < 0.40f )
1372 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
1373 < 0.40f )
1374 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
1375 < 0.40f ) ) )
1376 {
1377 // This may be a brushed metal
1378 blinnMaterial.SetGenerator( &m_brushedMetalMaterial );
1379 }
1380 }
1381 }
1382 }
1383 }
1384 else
1385 {
1386 ( *materialVector )[imat] = BLINN_PHONG_MATERIAL(
1387 SFVEC3F( 0.2f ), SFVEC3F( 0.0f ), SFVEC3F( 0.0f ), 0.0f, 0.0f, 0.0f );
1388 }
1389 }
1390 }
1391
1392 return materialVector;
1393 }
1394
1395
addModels(CONTAINER_3D & aDstContainer,const S3DMODEL * a3DModel,const glm::mat4 & aModelMatrix,float aFPOpacity,bool aSkipMaterialInformation,BOARD_ITEM * aBoardItem)1396 void RENDER_3D_RAYTRACE::addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
1397 const glm::mat4& aModelMatrix, float aFPOpacity,
1398 bool aSkipMaterialInformation, BOARD_ITEM* aBoardItem )
1399 {
1400 // Validate a3DModel pointers
1401 wxASSERT( a3DModel != nullptr );
1402
1403 if( a3DModel == nullptr )
1404 return;
1405
1406 wxASSERT( a3DModel->m_Materials != nullptr );
1407 wxASSERT( a3DModel->m_Meshes != nullptr );
1408 wxASSERT( a3DModel->m_MaterialsSize > 0 );
1409 wxASSERT( a3DModel->m_MeshesSize > 0 );
1410 wxASSERT( aFPOpacity > 0.0f );
1411 wxASSERT( aFPOpacity <= 1.0f );
1412
1413 if( aFPOpacity > 1.0f )
1414 {
1415 aFPOpacity = 1.0f;
1416 }
1417
1418 if( ( a3DModel->m_Materials != nullptr ) && ( a3DModel->m_Meshes != nullptr )
1419 && ( a3DModel->m_MaterialsSize > 0 ) && ( a3DModel->m_MeshesSize > 0 ) )
1420 {
1421 MODEL_MATERIALS* materialVector = nullptr;
1422
1423 if( !aSkipMaterialInformation )
1424 {
1425 materialVector = getModelMaterial( a3DModel );
1426 }
1427
1428 const glm::mat3 normalMatrix = glm::transpose( glm::inverse( glm::mat3( aModelMatrix ) ) );
1429
1430 for( unsigned int mesh_i = 0; mesh_i < a3DModel->m_MeshesSize; ++mesh_i )
1431 {
1432 const SMESH& mesh = a3DModel->m_Meshes[mesh_i];
1433
1434 // Validate the mesh pointers
1435 wxASSERT( mesh.m_Positions != nullptr );
1436 wxASSERT( mesh.m_FaceIdx != nullptr );
1437 wxASSERT( mesh.m_Normals != nullptr );
1438 wxASSERT( mesh.m_FaceIdxSize > 0 );
1439 wxASSERT( ( mesh.m_FaceIdxSize % 3 ) == 0 );
1440
1441
1442 if( ( mesh.m_Positions != nullptr ) && ( mesh.m_Normals != nullptr )
1443 && ( mesh.m_FaceIdx != nullptr ) && ( mesh.m_FaceIdxSize > 0 )
1444 && ( mesh.m_VertexSize > 0 ) && ( ( mesh.m_FaceIdxSize % 3 ) == 0 )
1445 && ( mesh.m_MaterialIdx < a3DModel->m_MaterialsSize ) )
1446 {
1447 float fpTransparency;
1448 const BLINN_PHONG_MATERIAL* blinn_material;
1449
1450 if( !aSkipMaterialInformation )
1451 {
1452 blinn_material = &( *materialVector )[mesh.m_MaterialIdx];
1453
1454 fpTransparency =
1455 1.0f - ( ( 1.0f - blinn_material->GetTransparency() ) * aFPOpacity );
1456 }
1457
1458 // Add all face triangles
1459 for( unsigned int faceIdx = 0; faceIdx < mesh.m_FaceIdxSize; faceIdx += 3 )
1460 {
1461 const unsigned int idx0 = mesh.m_FaceIdx[faceIdx + 0];
1462 const unsigned int idx1 = mesh.m_FaceIdx[faceIdx + 1];
1463 const unsigned int idx2 = mesh.m_FaceIdx[faceIdx + 2];
1464
1465 wxASSERT( idx0 < mesh.m_VertexSize );
1466 wxASSERT( idx1 < mesh.m_VertexSize );
1467 wxASSERT( idx2 < mesh.m_VertexSize );
1468
1469 if( ( idx0 < mesh.m_VertexSize ) && ( idx1 < mesh.m_VertexSize )
1470 && ( idx2 < mesh.m_VertexSize ) )
1471 {
1472 const SFVEC3F& v0 = mesh.m_Positions[idx0];
1473 const SFVEC3F& v1 = mesh.m_Positions[idx1];
1474 const SFVEC3F& v2 = mesh.m_Positions[idx2];
1475
1476 const SFVEC3F& n0 = mesh.m_Normals[idx0];
1477 const SFVEC3F& n1 = mesh.m_Normals[idx1];
1478 const SFVEC3F& n2 = mesh.m_Normals[idx2];
1479
1480 // Transform vertex with the model matrix
1481 const SFVEC3F vt0 = SFVEC3F( aModelMatrix * glm::vec4( v0, 1.0f ) );
1482 const SFVEC3F vt1 = SFVEC3F( aModelMatrix * glm::vec4( v1, 1.0f ) );
1483 const SFVEC3F vt2 = SFVEC3F( aModelMatrix * glm::vec4( v2, 1.0f ) );
1484
1485 const SFVEC3F nt0 = glm::normalize( SFVEC3F( normalMatrix * n0 ) );
1486 const SFVEC3F nt1 = glm::normalize( SFVEC3F( normalMatrix * n1 ) );
1487 const SFVEC3F nt2 = glm::normalize( SFVEC3F( normalMatrix * n2 ) );
1488
1489 TRIANGLE* newTriangle = new TRIANGLE( vt0, vt2, vt1, nt0, nt2, nt1 );
1490
1491 newTriangle->SetBoardItem( aBoardItem );
1492
1493 aDstContainer.Add( newTriangle );
1494
1495 if( !aSkipMaterialInformation )
1496 {
1497 newTriangle->SetMaterial( blinn_material );
1498 newTriangle->SetModelTransparency( fpTransparency );
1499
1500 if( mesh.m_Color == nullptr )
1501 {
1502 const SFVEC3F diffuseColor =
1503 a3DModel->m_Materials[mesh.m_MaterialIdx].m_Diffuse;
1504
1505 if( m_boardAdapter.GetMaterialMode() == MATERIAL_MODE::CAD_MODE )
1506 newTriangle->SetColor( ConvertSRGBToLinear(
1507 MaterialDiffuseToColorCAD( diffuseColor ) ) );
1508 else
1509 newTriangle->SetColor( ConvertSRGBToLinear( diffuseColor ) );
1510 }
1511 else
1512 {
1513 if( m_boardAdapter.GetMaterialMode() == MATERIAL_MODE::CAD_MODE )
1514 newTriangle->SetColor(
1515 ConvertSRGBToLinear( MaterialDiffuseToColorCAD(
1516 mesh.m_Color[idx0] ) ),
1517 ConvertSRGBToLinear( MaterialDiffuseToColorCAD(
1518 mesh.m_Color[idx1] ) ),
1519 ConvertSRGBToLinear( MaterialDiffuseToColorCAD(
1520 mesh.m_Color[idx2] ) ) );
1521 else
1522 newTriangle->SetColor(
1523 ConvertSRGBToLinear( mesh.m_Color[idx0] ),
1524 ConvertSRGBToLinear( mesh.m_Color[idx1] ),
1525 ConvertSRGBToLinear( mesh.m_Color[idx2] ) );
1526 }
1527 }
1528 }
1529 }
1530 }
1531 }
1532 }
1533 }
1534