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