You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1576 lines
63 KiB

7 months ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2022 Mario Luzeiro <mrluzeiro@ua.pt>
  5. * Copyright (C) 2023 CERN
  6. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include "render_3d_raytrace_base.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. #include <board.h>
  40. #include <footprint.h>
  41. #include <fp_lib_table.h>
  42. #include <eda_3d_viewer_frame.h>
  43. #include <project_pcb.h>
  44. #include <base_units.h>
  45. #include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
  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. */
  54. static float TransparencyControl( float aGrayColorValue, float aTransparency )
  55. {
  56. const float aaa = aTransparency * aTransparency * aTransparency;
  57. // 1.00-1.05*(1.0-x)^3
  58. float ca = 1.0f - aTransparency;
  59. ca = 1.00f - 1.05f * ca * ca * ca;
  60. return glm::max( glm::min( aGrayColorValue * ca + aaa, 1.0f ), 0.0f );
  61. }
  62. /**
  63. * Scale conversion from 3d model units to pcb units
  64. */
  65. #define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
  66. void RENDER_3D_RAYTRACE_BASE::setupMaterials()
  67. {
  68. MATERIAL::SetDefaultRefractionRayCount(
  69. m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_refractions );
  70. MATERIAL::SetDefaultReflectionRayCount(
  71. m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_reflections );
  72. MATERIAL::SetDefaultRefractionRecursionCount(
  73. m_boardAdapter.m_Cfg->m_Render.raytrace_recursivelevel_refractions );
  74. MATERIAL::SetDefaultReflectionRecursionCount(
  75. m_boardAdapter.m_Cfg->m_Render.raytrace_recursivelevel_reflections );
  76. double mmTo3Dunits = pcbIUScale.IU_PER_MM * m_boardAdapter.BiuTo3dUnits();
  77. if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
  78. {
  79. m_boardMaterial = BOARD_NORMAL( 0.40f * mmTo3Dunits );
  80. m_copperMaterial = COPPER_NORMAL( 4.0f * mmTo3Dunits, &m_boardMaterial );
  81. m_platedCopperMaterial = PLATED_COPPER_NORMAL( 0.5f * mmTo3Dunits );
  82. m_solderMaskMaterial = SOLDER_MASK_NORMAL( &m_boardMaterial );
  83. m_plasticMaterial = PLASTIC_NORMAL( 0.05f * mmTo3Dunits );
  84. m_shinyPlasticMaterial = PLASTIC_SHINE_NORMAL( 0.1f * mmTo3Dunits );
  85. m_brushedMetalMaterial = BRUSHED_METAL_NORMAL( 0.05f * mmTo3Dunits );
  86. m_silkScreenMaterial = SILK_SCREEN_NORMAL( 0.25f * mmTo3Dunits );
  87. }
  88. // http://devernay.free.fr/cours/opengl/materials.html
  89. // Copper
  90. const SFVEC3F copperSpecularLinear =
  91. ConvertSRGBToLinear( glm::clamp( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.5f + 0.25f,
  92. SFVEC3F( 0.0f ), SFVEC3F( 1.0f ) ) );
  93. m_materials.m_Copper = BLINN_PHONG_MATERIAL(
  94. ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.3f ),
  95. SFVEC3F( 0.0f ), copperSpecularLinear, 0.4f * 128.0f, 0.0f, 0.0f );
  96. if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
  97. m_materials.m_Copper.SetGenerator( &m_platedCopperMaterial );
  98. m_materials.m_NonPlatedCopper = BLINN_PHONG_MATERIAL(
  99. ConvertSRGBToLinear( SFVEC3F( 0.191f, 0.073f, 0.022f ) ), SFVEC3F( 0.0f, 0.0f, 0.0f ),
  100. SFVEC3F( 0.256f, 0.137f, 0.086f ), 0.15f * 128.0f, 0.0f, 0.0f );
  101. if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
  102. m_materials.m_NonPlatedCopper.SetGenerator( &m_copperMaterial );
  103. m_materials.m_Paste = BLINN_PHONG_MATERIAL(
  104. ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
  105. * ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
  106. SFVEC3F( 0.0f, 0.0f, 0.0f ),
  107. ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
  108. * ConvertSRGBToLinear(
  109. (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
  110. 0.10f * 128.0f, 0.0f, 0.0f );
  111. m_materials.m_SilkS = BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( SFVEC3F( 0.11f ) ),
  112. SFVEC3F( 0.0f, 0.0f, 0.0f ),
  113. glm::clamp( ( ( SFVEC3F )( 1.0f ) - ConvertSRGBToLinear(
  114. (SFVEC3F) m_boardAdapter.m_SilkScreenColorTop ) ),
  115. SFVEC3F( 0.0f ), SFVEC3F( 0.10f ) ), 0.078125f * 128.0f, 0.0f, 0.0f );
  116. if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
  117. m_materials.m_SilkS.SetGenerator( &m_silkScreenMaterial );
  118. // Assume that SolderMaskTop == SolderMaskBot
  119. const float solderMask_gray =
  120. ( m_boardAdapter.m_SolderMaskColorTop.r + m_boardAdapter.m_SolderMaskColorTop.g
  121. + m_boardAdapter.m_SolderMaskColorTop.b )
  122. / 3.0f;
  123. const float solderMask_transparency = TransparencyControl( solderMask_gray,
  124. 1.0f - m_boardAdapter.m_SolderMaskColorTop.a );
  125. m_materials.m_SolderMask = BLINN_PHONG_MATERIAL(
  126. ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorTop ) * 0.10f,
  127. SFVEC3F( 0.0f, 0.0f, 0.0f ),
  128. SFVEC3F( glm::clamp( solderMask_gray * 2.0f, 0.25f, 1.0f ) ), 0.85f * 128.0f,
  129. solderMask_transparency, 0.16f );
  130. m_materials.m_SolderMask.SetCastShadows( true );
  131. m_materials.m_SolderMask.SetRefractionRayCount( 1 );
  132. if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
  133. m_materials.m_SolderMask.SetGenerator( &m_solderMaskMaterial );
  134. m_materials.m_EpoxyBoard =
  135. BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( SFVEC3F( 16.0f / 255.0f, 14.0f / 255.0f,
  136. 10.0f / 255.0f ) ),
  137. SFVEC3F( 0.0f, 0.0f, 0.0f ),
  138. ConvertSRGBToLinear( SFVEC3F( 10.0f / 255.0f, 8.0f / 255.0f,
  139. 10.0f / 255.0f ) ),
  140. 0.1f * 128.0f, 1.0f - m_boardAdapter.m_BoardBodyColor.a, 0.0f );
  141. m_materials.m_EpoxyBoard.SetAbsorvance( 10.0f );
  142. if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
  143. m_materials.m_EpoxyBoard.SetGenerator( &m_boardMaterial );
  144. SFVEC3F bgTop = ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_BgColorTop );
  145. m_materials.m_Floor = BLINN_PHONG_MATERIAL( bgTop * 0.125f, SFVEC3F( 0.0f, 0.0f, 0.0f ),
  146. ( SFVEC3F( 1.0f ) - bgTop ) / 3.0f,
  147. 0.10f * 128.0f, 1.0f, 0.50f );
  148. m_materials.m_Floor.SetCastShadows( false );
  149. m_materials.m_Floor.SetReflectionRecursionCount( 1 );
  150. }
  151. void RENDER_3D_RAYTRACE_BASE::createObject( CONTAINER_3D& aDstContainer, const OBJECT_2D* aObject2D,
  152. float aZMin, float aZMax, const MATERIAL* aMaterial,
  153. const SFVEC3F& aObjColor )
  154. {
  155. switch( aObject2D->GetObjectType() )
  156. {
  157. case OBJECT_2D_TYPE::DUMMYBLOCK:
  158. {
  159. m_convertedDummyBlockCount++;
  160. XY_PLANE* objPtr;
  161. objPtr = new XY_PLANE( BBOX_3D(
  162. SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMin ),
  163. SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMin ) ) );
  164. objPtr->SetMaterial( aMaterial );
  165. objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
  166. aDstContainer.Add( objPtr );
  167. objPtr = new XY_PLANE( BBOX_3D(
  168. SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMax ),
  169. SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMax ) ) );
  170. objPtr->SetMaterial( aMaterial );
  171. objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
  172. aDstContainer.Add( objPtr );
  173. break;
  174. }
  175. case OBJECT_2D_TYPE::ROUNDSEG:
  176. {
  177. m_converted2dRoundSegmentCount++;
  178. const ROUND_SEGMENT_2D* aRoundSeg2D = static_cast<const ROUND_SEGMENT_2D*>( aObject2D );
  179. ROUND_SEGMENT* objPtr = new ROUND_SEGMENT( *aRoundSeg2D, aZMin, aZMax );
  180. objPtr->SetMaterial( aMaterial );
  181. objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
  182. aDstContainer.Add( objPtr );
  183. break;
  184. }
  185. default:
  186. {
  187. LAYER_ITEM* objPtr = new LAYER_ITEM( aObject2D, aZMin, aZMax );
  188. objPtr->SetMaterial( aMaterial );
  189. objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
  190. aDstContainer.Add( objPtr );
  191. break;
  192. }
  193. }
  194. }
  195. void RENDER_3D_RAYTRACE_BASE::createItemsFromContainer( const BVH_CONTAINER_2D* aContainer2d,
  196. PCB_LAYER_ID aLayer_id,
  197. const MATERIAL* aMaterialLayer,
  198. const SFVEC3F& aLayerColor,
  199. float aLayerZOffset )
  200. {
  201. if( aContainer2d == nullptr )
  202. return;
  203. EDA_3D_VIEWER_SETTINGS::RENDER_SETTINGS& cfg = m_boardAdapter.m_Cfg->m_Render;
  204. bool isSilk = aLayer_id == B_SilkS || aLayer_id == F_SilkS;
  205. const LIST_OBJECT2D& listObject2d = aContainer2d->GetList();
  206. if( listObject2d.size() == 0 )
  207. return;
  208. for( const OBJECT_2D* object2d_A : listObject2d )
  209. {
  210. // not yet used / implemented (can be used in future to clip the objects in the
  211. // board borders
  212. OBJECT_2D* object2d_C = CSGITEM_FULL;
  213. std::vector<const OBJECT_2D*>* object2d_B = CSGITEM_EMPTY;
  214. object2d_B = new std::vector<const OBJECT_2D*>();
  215. // Subtract holes but not in SolderPaste
  216. // (can be added as an option in future)
  217. if( !( aLayer_id == B_Paste || aLayer_id == F_Paste ) )
  218. {
  219. // Check if there are any layerhole that intersects this object
  220. // Eg: a segment is cut by a via hole or THT hole.
  221. const MAP_CONTAINER_2D_BASE& layerHolesMap = m_boardAdapter.GetLayerHoleMap();
  222. if( layerHolesMap.find( aLayer_id ) != layerHolesMap.end() )
  223. {
  224. const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( aLayer_id );
  225. CONST_LIST_OBJECT2D intersecting;
  226. holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
  227. for( const OBJECT_2D* hole2d : intersecting )
  228. object2d_B->push_back( hole2d );
  229. }
  230. // Check if there are any THT that intersects this object. If we're processing a silk
  231. // layer and the flag is set, then clip the silk at the outer edge of the annular ring,
  232. // rather than the at the outer edge of the copper plating.
  233. const BVH_CONTAINER_2D& throughHoleOuter =
  234. cfg.clip_silk_on_via_annuli && isSilk ? m_boardAdapter.GetViaAnnuli()
  235. : m_boardAdapter.GetTH_ODs();
  236. if( !throughHoleOuter.GetList().empty() )
  237. {
  238. CONST_LIST_OBJECT2D intersecting;
  239. throughHoleOuter.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
  240. for( const OBJECT_2D* hole2d : intersecting )
  241. object2d_B->push_back( hole2d );
  242. }
  243. }
  244. if( !m_antioutlineBoard2dObjects->GetList().empty() )
  245. {
  246. CONST_LIST_OBJECT2D intersecting;
  247. m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
  248. intersecting );
  249. for( const OBJECT_2D* obj : intersecting )
  250. object2d_B->push_back( obj );
  251. }
  252. const MAP_CONTAINER_2D_BASE& mapLayers = m_boardAdapter.GetLayerMap();
  253. if( cfg.subtract_mask_from_silk
  254. && ( ( aLayer_id == B_SilkS && mapLayers.find( B_Mask ) != mapLayers.end() )
  255. || ( aLayer_id == F_SilkS && mapLayers.find( F_Mask ) != mapLayers.end() ) ) )
  256. {
  257. const PCB_LAYER_ID maskLayer = ( aLayer_id == B_SilkS ) ? B_Mask : F_Mask;
  258. const BVH_CONTAINER_2D* containerMaskLayer2d = mapLayers.at( maskLayer );
  259. CONST_LIST_OBJECT2D intersecting;
  260. if( containerMaskLayer2d ) // can be null if B_Mask or F_Mask is not shown
  261. containerMaskLayer2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
  262. for( const OBJECT_2D* obj2d : intersecting )
  263. object2d_B->push_back( obj2d );
  264. }
  265. if( object2d_B->empty() )
  266. {
  267. delete object2d_B;
  268. object2d_B = CSGITEM_EMPTY;
  269. }
  270. if( ( object2d_B == CSGITEM_EMPTY ) && ( object2d_C == CSGITEM_FULL ) )
  271. {
  272. LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
  273. m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
  274. m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
  275. objPtr->SetMaterial( aMaterialLayer );
  276. objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
  277. m_objectContainer.Add( objPtr );
  278. }
  279. else
  280. {
  281. LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, object2d_C,
  282. object2d_A->GetBoardItem() );
  283. m_containerWithObjectsToDelete.Add( itemCSG2d );
  284. LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
  285. m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
  286. m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
  287. objPtr->SetMaterial( aMaterialLayer );
  288. objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
  289. m_objectContainer.Add( objPtr );
  290. }
  291. }
  292. }
  293. extern void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline );
  294. void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter,
  295. bool aOnlyLoadCopperAndShapes )
  296. {
  297. m_reloadRequested = false;
  298. m_modelMaterialMap.clear();
  299. OBJECT_2D_STATS::Instance().ResetStats();
  300. OBJECT_3D_STATS::Instance().ResetStats();
  301. int64_t stats_startReloadTime = GetRunningMicroSecs();
  302. if( !aOnlyLoadCopperAndShapes )
  303. {
  304. m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
  305. SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
  306. m_camera.SetBoardLookAtPos( camera_pos );
  307. }
  308. m_objectContainer.Clear();
  309. m_containerWithObjectsToDelete.Clear();
  310. setupMaterials();
  311. if( aStatusReporter )
  312. aStatusReporter->Report( _( "Load Raytracing: board" ) );
  313. // Create and add the outline board
  314. delete m_outlineBoard2dObjects;
  315. delete m_antioutlineBoard2dObjects;
  316. m_outlineBoard2dObjects = new CONTAINER_2D;
  317. m_antioutlineBoard2dObjects = new BVH_CONTAINER_2D;
  318. std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
  319. if( !aOnlyLoadCopperAndShapes )
  320. {
  321. const int outlineCount = m_boardAdapter.GetBoardPoly().OutlineCount();
  322. if( outlineCount > 0 )
  323. {
  324. float divFactor = 0.0f;
  325. if( m_boardAdapter.GetViaCount() )
  326. divFactor = m_boardAdapter.GetAverageViaHoleDiameter() * 18.0f;
  327. else if( m_boardAdapter.GetHoleCount() )
  328. divFactor = m_boardAdapter.GetAverageHoleDiameter() * 8.0f;
  329. SHAPE_POLY_SET boardPolyCopy = m_boardAdapter.GetBoardPoly();
  330. // Calculate an antiboard outline
  331. SHAPE_POLY_SET antiboardPoly;
  332. buildBoardBoundingBoxPoly( m_boardAdapter.GetBoard(), antiboardPoly );
  333. antiboardPoly.BooleanSubtract( boardPolyCopy );
  334. antiboardPoly.Fracture();
  335. for( int ii = 0; ii < antiboardPoly.OutlineCount(); ii++ )
  336. {
  337. ConvertPolygonToBlocks( antiboardPoly, *m_antioutlineBoard2dObjects,
  338. m_boardAdapter.BiuTo3dUnits(), -1.0f,
  339. *m_boardAdapter.GetBoard(), ii );
  340. }
  341. m_antioutlineBoard2dObjects->BuildBVH();
  342. boardPolyCopy.Fracture();
  343. for( int ii = 0; ii < boardPolyCopy.OutlineCount(); ii++ )
  344. {
  345. ConvertPolygonToBlocks( boardPolyCopy, *m_outlineBoard2dObjects,
  346. m_boardAdapter.BiuTo3dUnits(), divFactor,
  347. *m_boardAdapter.GetBoard(), ii );
  348. }
  349. if( layerFlags.test( LAYER_3D_BOARD ) )
  350. {
  351. const LIST_OBJECT2D& listObjects = m_outlineBoard2dObjects->GetList();
  352. for( const OBJECT_2D* object2d_A : listObjects )
  353. {
  354. std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
  355. // Check if there are any THT that intersects this outline object part
  356. if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
  357. {
  358. const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetTH_ODs();
  359. CONST_LIST_OBJECT2D intersecting;
  360. throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
  361. for( const OBJECT_2D* hole : intersecting )
  362. {
  363. if( object2d_A->Intersects( hole->GetBBox() ) )
  364. object2d_B->push_back( hole );
  365. }
  366. }
  367. if( !m_antioutlineBoard2dObjects->GetList().empty() )
  368. {
  369. CONST_LIST_OBJECT2D intersecting;
  370. m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
  371. intersecting );
  372. for( const OBJECT_2D* obj : intersecting )
  373. object2d_B->push_back( obj );
  374. }
  375. if( object2d_B->empty() )
  376. {
  377. delete object2d_B;
  378. object2d_B = CSGITEM_EMPTY;
  379. }
  380. if( object2d_B == CSGITEM_EMPTY )
  381. {
  382. LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
  383. m_boardAdapter.GetLayerBottomZPos( F_Cu ),
  384. m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
  385. objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
  386. objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
  387. m_objectContainer.Add( objPtr );
  388. }
  389. else
  390. {
  391. LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
  392. CSGITEM_FULL,
  393. *m_boardAdapter.GetBoard() );
  394. m_containerWithObjectsToDelete.Add( itemCSG2d );
  395. LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
  396. m_boardAdapter.GetLayerBottomZPos( F_Cu ),
  397. m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
  398. objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
  399. objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
  400. m_objectContainer.Add( objPtr );
  401. }
  402. }
  403. // Add cylinders of the board body to container
  404. // Note: This is actually a workaround for the holes in the board.
  405. // The issue is because if a hole is in a border of a divided polygon ( ex
  406. // a polygon or dummy block) it will cut also the render of the hole.
  407. // So this will add a full hole.
  408. // In fact, that is not need if the hole have copper.
  409. if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
  410. {
  411. const LIST_OBJECT2D& holeList = m_boardAdapter.GetTH_ODs().GetList();
  412. for( const OBJECT_2D* hole2d : holeList )
  413. {
  414. if( !m_antioutlineBoard2dObjects->GetList().empty() )
  415. {
  416. CONST_LIST_OBJECT2D intersecting;
  417. m_antioutlineBoard2dObjects->GetIntersectingObjects( hole2d->GetBBox(),
  418. intersecting );
  419. // Do not add cylinder if it intersects the edge of the board
  420. if( !intersecting.empty() )
  421. continue;
  422. }
  423. switch( hole2d->GetObjectType() )
  424. {
  425. case OBJECT_2D_TYPE::FILLED_CIRCLE:
  426. {
  427. const float radius = hole2d->GetBBox().GetExtent().x * 0.5f * 0.999f;
  428. CYLINDER* objPtr = new CYLINDER( hole2d->GetCentroid(),
  429. NextFloatDown( m_boardAdapter.GetLayerBottomZPos( F_Cu ) ),
  430. NextFloatUp( m_boardAdapter.GetLayerBottomZPos( B_Cu ) ),
  431. radius );
  432. objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
  433. objPtr->SetColor(
  434. ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
  435. m_objectContainer.Add( objPtr );
  436. }
  437. break;
  438. default:
  439. break;
  440. }
  441. }
  442. }
  443. }
  444. }
  445. }
  446. if( aStatusReporter )
  447. aStatusReporter->Report( _( "Load Raytracing: layers" ) );
  448. // Add layers maps (except B_Mask and F_Mask)
  449. for( const std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& entry :
  450. m_boardAdapter.GetLayerMap() )
  451. {
  452. const PCB_LAYER_ID layer_id = entry.first;
  453. const BVH_CONTAINER_2D* container2d = entry.second;
  454. // Only process layers that exist
  455. if( !container2d )
  456. continue;
  457. if( aOnlyLoadCopperAndShapes && !IsCopperLayer( layer_id ) )
  458. continue;
  459. // Mask layers are not processed here because they are a special case
  460. if( layer_id == B_Mask || layer_id == F_Mask )
  461. continue;
  462. MATERIAL* materialLayer = &m_materials.m_SilkS;
  463. SFVEC3F layerColor = SFVEC3F( 0.0f, 0.0f, 0.0f );
  464. switch( layer_id )
  465. {
  466. case B_Adhes:
  467. case F_Adhes:
  468. break;
  469. case B_Paste:
  470. case F_Paste:
  471. materialLayer = &m_materials.m_Paste;
  472. layerColor = m_boardAdapter.m_SolderPasteColor;
  473. break;
  474. case B_SilkS:
  475. materialLayer = &m_materials.m_SilkS;
  476. layerColor = m_boardAdapter.m_SilkScreenColorBot;
  477. break;
  478. case F_SilkS:
  479. materialLayer = &m_materials.m_SilkS;
  480. layerColor = m_boardAdapter.m_SilkScreenColorTop;
  481. break;
  482. case Dwgs_User:
  483. layerColor = m_boardAdapter.m_UserDrawingsColor;
  484. break;
  485. case Cmts_User:
  486. layerColor = m_boardAdapter.m_UserCommentsColor;
  487. break;
  488. case Eco1_User:
  489. layerColor = m_boardAdapter.m_ECO1Color;
  490. break;
  491. case Eco2_User:
  492. layerColor = m_boardAdapter.m_ECO2Color;
  493. break;
  494. case B_CrtYd:
  495. case F_CrtYd:
  496. break;
  497. case B_Fab:
  498. case F_Fab:
  499. break;
  500. default:
  501. {
  502. int layer3D = MapPCBLayerTo3DLayer( layer_id );
  503. // Note: MUST do this in LAYER_3D space; User_1..User_45 are NOT contiguous
  504. if( layer3D >= LAYER_3D_USER_1 && layer3D <= LAYER_3D_USER_45 )
  505. {
  506. layerColor = m_boardAdapter.m_UserDefinedLayerColor[ layer3D - LAYER_3D_USER_1 ];
  507. }
  508. else if( m_boardAdapter.m_Cfg->m_Render.differentiate_plated_copper )
  509. {
  510. layerColor = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f, 50.0f / 255.0f );
  511. materialLayer = &m_materials.m_NonPlatedCopper;
  512. }
  513. else
  514. {
  515. layerColor = m_boardAdapter.m_CopperColor;
  516. materialLayer = &m_materials.m_Copper;
  517. }
  518. break;
  519. }
  520. }
  521. createItemsFromContainer( container2d, layer_id, materialLayer, layerColor, 0.0f );
  522. } // for each layer on map
  523. // Create plated copper
  524. if( m_boardAdapter.m_Cfg->m_Render.differentiate_plated_copper )
  525. {
  526. createItemsFromContainer( m_boardAdapter.GetPlatedPadsFront(), F_Cu, &m_materials.m_Copper,
  527. m_boardAdapter.m_CopperColor,
  528. m_boardAdapter.GetFrontCopperThickness() * 0.1f );
  529. createItemsFromContainer( m_boardAdapter.GetPlatedPadsBack(), B_Cu, &m_materials.m_Copper,
  530. m_boardAdapter.m_CopperColor,
  531. -m_boardAdapter.GetBackCopperThickness() * 0.1f );
  532. }
  533. if( !aOnlyLoadCopperAndShapes )
  534. {
  535. // Add Mask layer
  536. // Solder mask layers are "negative" layers so the elements that we have in the container
  537. // should remove the board outline. We will check for all objects in the outline if it
  538. // intersects any object in the layer container and also any hole.
  539. if( ( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
  540. || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
  541. && !m_outlineBoard2dObjects->GetList().empty() )
  542. {
  543. const MATERIAL* materialLayer = &m_materials.m_SolderMask;
  544. for( const std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& entry :
  545. m_boardAdapter.GetLayerMap() )
  546. {
  547. const PCB_LAYER_ID layer_id = entry.first;
  548. const BVH_CONTAINER_2D* container2d = entry.second;
  549. // Only process layers that exist
  550. if( !container2d )
  551. continue;
  552. // Only get the Solder mask layers (and only if the board has them)
  553. if( layer_id == F_Mask && !layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
  554. continue;
  555. if( layer_id == B_Mask && !layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
  556. continue;
  557. // Only Mask layers are processed here because they are negative layers
  558. if( layer_id != F_Mask && layer_id != B_Mask )
  559. continue;
  560. SFVEC3F layerColor;
  561. if( layer_id == B_Mask )
  562. layerColor = m_boardAdapter.m_SolderMaskColorBot;
  563. else
  564. layerColor = m_boardAdapter.m_SolderMaskColorTop;
  565. const float zLayerMin = m_boardAdapter.GetLayerBottomZPos( layer_id );
  566. const float zLayerMax = m_boardAdapter.GetLayerTopZPos( layer_id );
  567. // Get the outline board objects
  568. for( const OBJECT_2D* object2d_A : m_outlineBoard2dObjects->GetList() )
  569. {
  570. std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
  571. // Check if there are any THT that intersects this outline object part
  572. if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
  573. {
  574. const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetTH_ODs();
  575. CONST_LIST_OBJECT2D intersecting;
  576. throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
  577. for( const OBJECT_2D* hole : intersecting )
  578. {
  579. if( object2d_A->Intersects( hole->GetBBox() ) )
  580. object2d_B->push_back( hole );
  581. }
  582. }
  583. // Check if there are any objects in the layer to subtract with the current
  584. // object
  585. if( !container2d->GetList().empty() )
  586. {
  587. CONST_LIST_OBJECT2D intersecting;
  588. container2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
  589. for( const OBJECT_2D* obj : intersecting )
  590. object2d_B->push_back( obj );
  591. }
  592. if( object2d_B->empty() )
  593. {
  594. delete object2d_B;
  595. object2d_B = CSGITEM_EMPTY;
  596. }
  597. if( object2d_B == CSGITEM_EMPTY )
  598. {
  599. #if 0
  600. createObject( m_objectContainer, object2d_A, zLayerMin, zLayerMax,
  601. materialLayer, layerColor );
  602. #else
  603. LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, zLayerMin, zLayerMax );
  604. objPtr->SetMaterial( materialLayer );
  605. objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
  606. m_objectContainer.Add( objPtr );
  607. #endif
  608. }
  609. else
  610. {
  611. LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
  612. CSGITEM_FULL,
  613. object2d_A->GetBoardItem() );
  614. m_containerWithObjectsToDelete.Add( itemCSG2d );
  615. LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, zLayerMin, zLayerMax );
  616. objPtr->SetMaterial( materialLayer );
  617. objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
  618. m_objectContainer.Add( objPtr );
  619. }
  620. }
  621. }
  622. }
  623. addPadsAndVias();
  624. }
  625. #ifdef PRINT_STATISTICS_3D_VIEWER
  626. int64_t stats_endConvertTime = GetRunningMicroSecs();
  627. int64_t stats_startLoad3DmodelsTime = stats_endConvertTime;
  628. #endif
  629. if( aStatusReporter )
  630. aStatusReporter->Report( _( "Loading 3D models..." ) );
  631. load3DModels( m_objectContainer, aOnlyLoadCopperAndShapes );
  632. #ifdef PRINT_STATISTICS_3D_VIEWER
  633. int64_t stats_endLoad3DmodelsTime = GetRunningMicroSecs();
  634. #endif
  635. if( !aOnlyLoadCopperAndShapes )
  636. {
  637. // Add floor
  638. if( m_boardAdapter.m_Cfg->m_Render.raytrace_backfloor )
  639. {
  640. BBOX_3D boardBBox = m_boardAdapter.GetBBox();
  641. if( boardBBox.IsInitialized() )
  642. {
  643. boardBBox.Scale( 3.0f );
  644. if( m_objectContainer.GetList().size() > 0 )
  645. {
  646. BBOX_3D containerBBox = m_objectContainer.GetBBox();
  647. containerBBox.Scale( 1.3f );
  648. const SFVEC3F centerBBox = containerBBox.GetCenter();
  649. // Floor triangles
  650. const float minZ = glm::min( containerBBox.Min().z, boardBBox.Min().z );
  651. const SFVEC3F v1 =
  652. SFVEC3F( -RANGE_SCALE_3D * 4.0f, -RANGE_SCALE_3D * 4.0f, minZ )
  653. + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
  654. const SFVEC3F v3 =
  655. SFVEC3F( +RANGE_SCALE_3D * 4.0f, +RANGE_SCALE_3D * 4.0f, minZ )
  656. + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
  657. const SFVEC3F v2 = SFVEC3F( v1.x, v3.y, v1.z );
  658. const SFVEC3F v4 = SFVEC3F( v3.x, v1.y, v1.z );
  659. SFVEC3F floorColor = ConvertSRGBToLinear( m_boardAdapter.m_BgColorTop );
  660. TRIANGLE* newTriangle1 = new TRIANGLE( v1, v2, v3 );
  661. TRIANGLE* newTriangle2 = new TRIANGLE( v3, v4, v1 );
  662. m_objectContainer.Add( newTriangle1 );
  663. m_objectContainer.Add( newTriangle2 );
  664. newTriangle1->SetMaterial( &m_materials.m_Floor );
  665. newTriangle2->SetMaterial( &m_materials.m_Floor );
  666. newTriangle1->SetColor( floorColor );
  667. newTriangle2->SetColor( floorColor );
  668. // Ceiling triangles
  669. const float maxZ = glm::max( containerBBox.Max().z, boardBBox.Max().z );
  670. const SFVEC3F v5 = SFVEC3F( v1.x, v1.y, maxZ );
  671. const SFVEC3F v6 = SFVEC3F( v2.x, v2.y, maxZ );
  672. const SFVEC3F v7 = SFVEC3F( v3.x, v3.y, maxZ );
  673. const SFVEC3F v8 = SFVEC3F( v4.x, v4.y, maxZ );
  674. TRIANGLE* newTriangle3 = new TRIANGLE( v7, v6, v5 );
  675. TRIANGLE* newTriangle4 = new TRIANGLE( v5, v8, v7 );
  676. m_objectContainer.Add( newTriangle3 );
  677. m_objectContainer.Add( newTriangle4 );
  678. newTriangle3->SetMaterial( &m_materials.m_Floor );
  679. newTriangle4->SetMaterial( &m_materials.m_Floor );
  680. newTriangle3->SetColor( floorColor );
  681. newTriangle4->SetColor( floorColor );
  682. }
  683. }
  684. }
  685. // Init initial lights
  686. for( LIGHT* light : m_lights )
  687. delete light;
  688. m_lights.clear();
  689. auto IsColorZero =
  690. []( const SFVEC3F& aSource )
  691. {
  692. return ( ( aSource.r < ( 1.0f / 255.0f ) ) && ( aSource.g < ( 1.0f / 255.0f ) )
  693. && ( aSource.b < ( 1.0f / 255.0f ) ) );
  694. };
  695. SFVEC3F cameraLightColor =
  696. m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorCamera );
  697. SFVEC3F topLightColor =
  698. m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorTop );
  699. SFVEC3F bottomLightColor =
  700. m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorBottom );
  701. m_cameraLight = new DIRECTIONAL_LIGHT( SFVEC3F( 0.0f, 0.0f, 0.0f ), cameraLightColor );
  702. m_cameraLight->SetCastShadows( false );
  703. if( !IsColorZero( cameraLightColor ) )
  704. m_lights.push_back( m_cameraLight );
  705. const SFVEC3F& boardCenter = m_boardAdapter.GetBBox().GetCenter();
  706. if( !IsColorZero( topLightColor ) )
  707. {
  708. m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
  709. +RANGE_SCALE_3D * 2.0f ),
  710. topLightColor ) );
  711. }
  712. if( !IsColorZero( bottomLightColor ) )
  713. {
  714. m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
  715. -RANGE_SCALE_3D * 2.0f ),
  716. bottomLightColor ) );
  717. }
  718. for( size_t i = 0; i < m_boardAdapter.m_Cfg->m_Render.raytrace_lightColor.size(); ++i )
  719. {
  720. SFVEC3F lightColor =
  721. m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColor[i] );
  722. if( !IsColorZero( lightColor ) )
  723. {
  724. const SFVEC2F sc = m_boardAdapter.GetSphericalCoord( i );
  725. m_lights.push_back( new DIRECTIONAL_LIGHT(
  726. SphericalToCartesian( glm::pi<float>() * sc.x, glm::pi<float>() * sc.y ),
  727. lightColor ) );
  728. }
  729. }
  730. }
  731. // Set min. and max. zoom range. This doesn't really fit here, but moving this outside of this
  732. // class would require reimplementing bounding box calculation (feel free to do this if you
  733. // have time and patience).
  734. if( m_objectContainer.GetList().size() > 0 )
  735. {
  736. float ratio =
  737. std::max( 1.0f, m_objectContainer.GetBBox().GetMaxDimension() / RANGE_SCALE_3D );
  738. float max_zoom = CAMERA::DEFAULT_MAX_ZOOM * ratio;
  739. float min_zoom = static_cast<float>( MIN_DISTANCE_IU * m_boardAdapter.BiuTo3dUnits()
  740. / -m_camera.GetCameraInitPos().z );
  741. if( min_zoom > max_zoom )
  742. std::swap( min_zoom, max_zoom );
  743. float zoom_ratio = max_zoom / min_zoom;
  744. // Set the minimum number of zoom 'steps' between max and min.
  745. int steps = 3 * 3;
  746. steps -= static_cast<int>( ceil( log( zoom_ratio ) / log( 1.26f ) ) );
  747. steps = std::max( steps, 0 );
  748. // Resize max and min zoom to accomplish the number of steps.
  749. float increased_zoom = pow( 1.26f, steps / 2 );
  750. max_zoom *= increased_zoom;
  751. min_zoom /= increased_zoom;
  752. if( steps & 1 )
  753. min_zoom /= 1.26f;
  754. min_zoom = std::min( min_zoom, 1.0f );
  755. m_camera.SetMaxZoom( max_zoom );
  756. m_camera.SetMinZoom( min_zoom );
  757. }
  758. // Create an accelerator
  759. delete m_accelerator;
  760. m_accelerator = new BVH_PBRT( m_objectContainer, 8, SPLITMETHOD::MIDDLE );
  761. if( aStatusReporter )
  762. {
  763. // Calculation time in seconds
  764. double calculation_time = (double) ( GetRunningMicroSecs() - stats_startReloadTime ) / 1e6;
  765. aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
  766. }
  767. }
  768. void RENDER_3D_RAYTRACE_BASE::insertHole( const PCB_VIA* aVia )
  769. {
  770. PCB_LAYER_ID top_layer, bottom_layer;
  771. int radiusBUI = ( aVia->GetDrillValue() / 2 );
  772. aVia->LayerPair( &top_layer, &bottom_layer );
  773. float topZ = m_boardAdapter.GetLayerBottomZPos( top_layer )
  774. + m_boardAdapter.GetFrontCopperThickness();
  775. float botZ = m_boardAdapter.GetLayerBottomZPos( bottom_layer )
  776. - m_boardAdapter.GetBackCopperThickness();
  777. const SFVEC2F center = SFVEC2F( aVia->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
  778. -aVia->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
  779. RING_2D* ring = new RING_2D( center, radiusBUI * m_boardAdapter.BiuTo3dUnits(),
  780. ( radiusBUI + m_boardAdapter.GetHolePlatingThickness() )
  781. * m_boardAdapter.BiuTo3dUnits(), *aVia );
  782. m_containerWithObjectsToDelete.Add( ring );
  783. LAYER_ITEM* objPtr = new LAYER_ITEM( ring, topZ, botZ );
  784. objPtr->SetMaterial( &m_materials.m_Copper );
  785. objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
  786. m_objectContainer.Add( objPtr );
  787. }
  788. void RENDER_3D_RAYTRACE_BASE::insertHole( const PAD* aPad )
  789. {
  790. const OBJECT_2D* object2d_A = nullptr;
  791. SFVEC3F objColor = m_boardAdapter.m_CopperColor;
  792. const VECTOR2I drillsize = aPad->GetDrillSize();
  793. const bool hasHole = drillsize.x && drillsize.y;
  794. if( !hasHole )
  795. return;
  796. CONST_LIST_OBJECT2D antiOutlineIntersectionList;
  797. const float topZ = m_boardAdapter.GetLayerBottomZPos( F_Cu )
  798. + m_boardAdapter.GetFrontCopperThickness() * 0.99f;
  799. const float botZ = m_boardAdapter.GetLayerBottomZPos( B_Cu )
  800. - m_boardAdapter.GetBackCopperThickness() * 0.99f;
  801. if( drillsize.x == drillsize.y ) // usual round hole
  802. {
  803. SFVEC2F center = SFVEC2F( aPad->GetPosition().x * m_boardAdapter.BiuTo3dUnits(),
  804. -aPad->GetPosition().y * m_boardAdapter.BiuTo3dUnits() );
  805. int innerRadius = drillsize.x / 2;
  806. int outerRadius = innerRadius + m_boardAdapter.GetHolePlatingThickness();
  807. RING_2D* ring = new RING_2D( center, innerRadius * m_boardAdapter.BiuTo3dUnits(),
  808. outerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
  809. m_containerWithObjectsToDelete.Add( ring );
  810. object2d_A = ring;
  811. // If the object (ring) is intersected by an antioutline board,
  812. // it will use instead a CSG of two circles.
  813. if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
  814. {
  815. m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
  816. antiOutlineIntersectionList );
  817. }
  818. if( !antiOutlineIntersectionList.empty() )
  819. {
  820. FILLED_CIRCLE_2D* innerCircle = new FILLED_CIRCLE_2D(
  821. center, innerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
  822. FILLED_CIRCLE_2D* outterCircle = new FILLED_CIRCLE_2D(
  823. center, outerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
  824. std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
  825. object2d_B->push_back( innerCircle );
  826. LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outterCircle, object2d_B, CSGITEM_FULL,
  827. *aPad );
  828. m_containerWithObjectsToDelete.Add( itemCSG2d );
  829. m_containerWithObjectsToDelete.Add( innerCircle );
  830. m_containerWithObjectsToDelete.Add( outterCircle );
  831. object2d_A = itemCSG2d;
  832. }
  833. }
  834. else // Oblong hole
  835. {
  836. VECTOR2I ends_offset;
  837. int width;
  838. if( drillsize.x > drillsize.y ) // Horizontal oval
  839. {
  840. ends_offset.x = ( drillsize.x - drillsize.y ) / 2;
  841. width = drillsize.y;
  842. }
  843. else // Vertical oval
  844. {
  845. ends_offset.y = ( drillsize.y - drillsize.x ) / 2;
  846. width = drillsize.x;
  847. }
  848. RotatePoint( ends_offset, aPad->GetOrientation() );
  849. VECTOR2I start = VECTOR2I( aPad->GetPosition() ) + ends_offset;
  850. VECTOR2I end = VECTOR2I( aPad->GetPosition() ) - ends_offset;
  851. ROUND_SEGMENT_2D* innerSeg =
  852. new ROUND_SEGMENT_2D( SFVEC2F( start.x * m_boardAdapter.BiuTo3dUnits(),
  853. -start.y * m_boardAdapter.BiuTo3dUnits() ),
  854. SFVEC2F( end.x * m_boardAdapter.BiuTo3dUnits(),
  855. -end.y * m_boardAdapter.BiuTo3dUnits() ),
  856. width * m_boardAdapter.BiuTo3dUnits(), *aPad );
  857. ROUND_SEGMENT_2D* outerSeg =
  858. new ROUND_SEGMENT_2D( SFVEC2F( start.x * m_boardAdapter.BiuTo3dUnits(),
  859. -start.y * m_boardAdapter.BiuTo3dUnits() ),
  860. SFVEC2F( end.x * m_boardAdapter.BiuTo3dUnits(),
  861. -end.y * m_boardAdapter.BiuTo3dUnits() ),
  862. ( width + m_boardAdapter.GetHolePlatingThickness() * 2 )
  863. * m_boardAdapter.BiuTo3dUnits(), *aPad );
  864. // NOTE: the round segment width is the "diameter", so we double the thickness
  865. std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
  866. object2d_B->push_back( innerSeg );
  867. LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outerSeg, object2d_B, CSGITEM_FULL, *aPad );
  868. m_containerWithObjectsToDelete.Add( itemCSG2d );
  869. m_containerWithObjectsToDelete.Add( innerSeg );
  870. m_containerWithObjectsToDelete.Add( outerSeg );
  871. object2d_A = itemCSG2d;
  872. if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
  873. {
  874. m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
  875. antiOutlineIntersectionList );
  876. }
  877. }
  878. if( object2d_A )
  879. {
  880. std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
  881. // Check if there are any other THT that intersects this hole
  882. // It will use the non inflated holes
  883. if( !m_boardAdapter.GetTH_IDs().GetList().empty() )
  884. {
  885. CONST_LIST_OBJECT2D intersecting;
  886. m_boardAdapter.GetTH_IDs().GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
  887. for( const OBJECT_2D* hole2d : intersecting )
  888. {
  889. if( object2d_A->Intersects( hole2d->GetBBox() ) )
  890. object2d_B->push_back( hole2d );
  891. }
  892. }
  893. for( const OBJECT_2D* obj : antiOutlineIntersectionList )
  894. object2d_B->push_back( obj );
  895. if( object2d_B->empty() )
  896. {
  897. delete object2d_B;
  898. object2d_B = CSGITEM_EMPTY;
  899. }
  900. if( object2d_B == CSGITEM_EMPTY )
  901. {
  902. LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, topZ, botZ );
  903. objPtr->SetMaterial( &m_materials.m_Copper );
  904. objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
  905. m_objectContainer.Add( objPtr );
  906. }
  907. else
  908. {
  909. LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, CSGITEM_FULL,
  910. *aPad );
  911. m_containerWithObjectsToDelete.Add( itemCSG2d );
  912. LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, topZ, botZ );
  913. objPtr->SetMaterial( &m_materials.m_Copper );
  914. objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
  915. m_objectContainer.Add( objPtr );
  916. }
  917. }
  918. }
  919. void RENDER_3D_RAYTRACE_BASE::addPadsAndVias()
  920. {
  921. if( !m_boardAdapter.GetBoard() )
  922. return;
  923. // Insert plated vertical holes inside the board
  924. // Insert vias holes (vertical cylinders)
  925. for( PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
  926. {
  927. if( track->Type() == PCB_VIA_T )
  928. {
  929. const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
  930. insertHole( via );
  931. }
  932. }
  933. // Insert pads holes (vertical cylinders)
  934. for( FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
  935. {
  936. for( PAD* pad : footprint->Pads() )
  937. {
  938. if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
  939. insertHole( pad );
  940. }
  941. }
  942. }
  943. void RENDER_3D_RAYTRACE_BASE::load3DModels( CONTAINER_3D& aDstContainer,
  944. bool aSkipMaterialInformation )
  945. {
  946. if( !m_boardAdapter.GetBoard() )
  947. return;
  948. if( !m_boardAdapter.m_IsPreviewer
  949. && !m_boardAdapter.m_Cfg->m_Render.show_footprints_normal
  950. && !m_boardAdapter.m_Cfg->m_Render.show_footprints_insert
  951. && !m_boardAdapter.m_Cfg->m_Render.show_footprints_virtual )
  952. {
  953. return;
  954. }
  955. // Go for all footprints
  956. for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
  957. {
  958. if( !fp->Models().empty()
  959. && m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
  960. {
  961. double zpos = m_boardAdapter.GetFootprintZPos( fp->IsFlipped() );
  962. VECTOR2I pos = fp->GetPosition();
  963. glm::mat4 fpMatrix = glm::mat4( 1.0f );
  964. fpMatrix = glm::translate( fpMatrix,
  965. SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
  966. -pos.y * m_boardAdapter.BiuTo3dUnits(),
  967. zpos ) );
  968. if( !fp->GetOrientation().IsZero() )
  969. {
  970. fpMatrix = glm::rotate( fpMatrix, (float) fp->GetOrientation().AsRadians(),
  971. SFVEC3F( 0.0f, 0.0f, 1.0f ) );
  972. }
  973. if( fp->IsFlipped() )
  974. {
  975. fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
  976. fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
  977. }
  978. const double modelunit_to_3d_units_factor =
  979. m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
  980. fpMatrix = glm::scale(
  981. fpMatrix, SFVEC3F( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
  982. modelunit_to_3d_units_factor ) );
  983. // Get the list of model files for this model
  984. S3D_CACHE* cacheMgr = m_boardAdapter.Get3dCacheManager();
  985. wxString libraryName = fp->GetFPID().GetLibNickname();
  986. wxString footprintBasePath = wxEmptyString;
  987. if( m_boardAdapter.GetBoard()->GetProject() )
  988. {
  989. try
  990. {
  991. // FindRow() can throw an exception
  992. const FP_LIB_TABLE_ROW* fpRow =
  993. PROJECT_PCB::PcbFootprintLibs( m_boardAdapter.GetBoard()->GetProject() )
  994. ->FindRow( libraryName, false );
  995. if( fpRow )
  996. footprintBasePath = fpRow->GetFullURI( true );
  997. }
  998. catch( ... )
  999. {
  1000. // Do nothing if the libraryName is not found in lib table
  1001. }
  1002. }
  1003. for( FP_3DMODEL& model : fp->Models() )
  1004. {
  1005. if( !model.m_Show || model.m_Filename.empty() )
  1006. continue;
  1007. // get it from cache
  1008. std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
  1009. embeddedFilesStack.push_back( fp->GetEmbeddedFiles() );
  1010. embeddedFilesStack.push_back( m_boardAdapter.GetBoard()->GetEmbeddedFiles() );
  1011. const S3DMODEL* modelPtr = cacheMgr->GetModel( model.m_Filename, footprintBasePath,
  1012. std::move( embeddedFilesStack ) );
  1013. // only add it if the return is not NULL.
  1014. if( modelPtr )
  1015. {
  1016. glm::mat4 modelMatrix = fpMatrix;
  1017. modelMatrix = glm::translate( modelMatrix,
  1018. SFVEC3F( model.m_Offset.x, model.m_Offset.y, model.m_Offset.z ) );
  1019. modelMatrix = glm::rotate( modelMatrix,
  1020. (float) -( model.m_Rotation.z / 180.0f ) * glm::pi<float>(),
  1021. SFVEC3F( 0.0f, 0.0f, 1.0f ) );
  1022. modelMatrix = glm::rotate( modelMatrix,
  1023. (float) -( model.m_Rotation.y / 180.0f ) * glm::pi<float>(),
  1024. SFVEC3F( 0.0f, 1.0f, 0.0f ) );
  1025. modelMatrix = glm::rotate( modelMatrix,
  1026. (float) -( model.m_Rotation.x / 180.0f ) * glm::pi<float>(),
  1027. SFVEC3F( 1.0f, 0.0f, 0.0f ) );
  1028. modelMatrix = glm::scale( modelMatrix,
  1029. SFVEC3F( model.m_Scale.x, model.m_Scale.y, model.m_Scale.z ) );
  1030. addModels( aDstContainer, modelPtr, modelMatrix, (float) model.m_Opacity,
  1031. aSkipMaterialInformation, fp );
  1032. }
  1033. }
  1034. }
  1035. }
  1036. }
  1037. MODEL_MATERIALS* RENDER_3D_RAYTRACE_BASE::getModelMaterial( const S3DMODEL* a3DModel )
  1038. {
  1039. MODEL_MATERIALS* materialVector;
  1040. // Try find if the materials already exists in the map list
  1041. if( m_modelMaterialMap.find( a3DModel ) != m_modelMaterialMap.end() )
  1042. {
  1043. // Found it, so get the pointer
  1044. materialVector = &m_modelMaterialMap[a3DModel];
  1045. }
  1046. else
  1047. {
  1048. // Materials was not found in the map, so it will create a new for
  1049. // this model.
  1050. m_modelMaterialMap[a3DModel] = MODEL_MATERIALS();
  1051. materialVector = &m_modelMaterialMap[a3DModel];
  1052. materialVector->resize( a3DModel->m_MaterialsSize );
  1053. for( unsigned int imat = 0; imat < a3DModel->m_MaterialsSize; ++imat )
  1054. {
  1055. if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::NORMAL )
  1056. {
  1057. const SMATERIAL& material = a3DModel->m_Materials[imat];
  1058. // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJtaW4oc3FydCh4LTAuMzUpKjAuNDAtMC4wNSwxLjApIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiMC4wNzA3NzM2NzMyMzY1OTAxMiIsIjEuNTY5NTcxNjI5MjI1NDY5OCIsIi0wLjI3NDYzNTMyMTc1OTkyOTMiLCIwLjY0NzcwMTg4MTkyNTUzNjIiXSwic2l6ZSI6WzY0NCwzOTRdfV0-
  1059. float reflectionFactor = 0.0f;
  1060. if( ( material.m_Shininess - 0.35f ) > FLT_EPSILON )
  1061. {
  1062. reflectionFactor = glm::clamp(
  1063. glm::sqrt( ( material.m_Shininess - 0.35f ) ) * 0.40f - 0.05f, 0.0f,
  1064. 0.5f );
  1065. }
  1066. BLINN_PHONG_MATERIAL& blinnMaterial = ( *materialVector )[imat];
  1067. blinnMaterial = BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( material.m_Ambient ),
  1068. ConvertSRGBToLinear( material.m_Emissive ),
  1069. ConvertSRGBToLinear( material.m_Specular ), material.m_Shininess * 180.0f,
  1070. material.m_Transparency, reflectionFactor );
  1071. if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
  1072. {
  1073. // Guess material type and apply a normal perturbator
  1074. if( ( RGBtoGray( material.m_Diffuse ) < 0.3f )
  1075. && ( material.m_Shininess < 0.36f )
  1076. && ( material.m_Transparency == 0.0f )
  1077. && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) < 0.15f )
  1078. && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
  1079. < 0.15f )
  1080. && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
  1081. < 0.15f ) ) )
  1082. {
  1083. // This may be a black plastic..
  1084. blinnMaterial.SetGenerator( &m_plasticMaterial );
  1085. }
  1086. else
  1087. {
  1088. if( ( RGBtoGray( material.m_Diffuse ) > 0.3f )
  1089. && ( material.m_Shininess < 0.30f )
  1090. && ( material.m_Transparency == 0.0f )
  1091. && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) > 0.25f )
  1092. || ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) > 0.25f )
  1093. || ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
  1094. > 0.25f ) ) )
  1095. {
  1096. // This may be a color plastic ...
  1097. blinnMaterial.SetGenerator( &m_shinyPlasticMaterial );
  1098. }
  1099. else
  1100. {
  1101. if( ( RGBtoGray( material.m_Diffuse ) > 0.6f )
  1102. && ( material.m_Shininess > 0.35f )
  1103. && ( material.m_Transparency == 0.0f )
  1104. && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g )
  1105. < 0.40f )
  1106. && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
  1107. < 0.40f )
  1108. && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
  1109. < 0.40f ) ) )
  1110. {
  1111. // This may be a brushed metal
  1112. blinnMaterial.SetGenerator( &m_brushedMetalMaterial );
  1113. }
  1114. }
  1115. }
  1116. }
  1117. }
  1118. else
  1119. {
  1120. ( *materialVector )[imat] = BLINN_PHONG_MATERIAL(
  1121. SFVEC3F( 0.2f ), SFVEC3F( 0.0f ), SFVEC3F( 0.0f ), 0.0f, 0.0f, 0.0f );
  1122. }
  1123. }
  1124. }
  1125. return materialVector;
  1126. }
  1127. void RENDER_3D_RAYTRACE_BASE::addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
  1128. const glm::mat4& aModelMatrix, float aFPOpacity,
  1129. bool aSkipMaterialInformation, BOARD_ITEM* aBoardItem )
  1130. {
  1131. // Validate a3DModel pointers
  1132. wxASSERT( a3DModel != nullptr );
  1133. if( a3DModel == nullptr )
  1134. return;
  1135. wxASSERT( a3DModel->m_Materials != nullptr );
  1136. wxASSERT( a3DModel->m_Meshes != nullptr );
  1137. wxASSERT( a3DModel->m_MaterialsSize > 0 );
  1138. wxASSERT( a3DModel->m_MeshesSize > 0 );
  1139. if( aFPOpacity > 1.0f )
  1140. aFPOpacity = 1.0f;
  1141. if( aFPOpacity < 0.0f )
  1142. aFPOpacity = 0.0f;
  1143. if( ( a3DModel->m_Materials != nullptr ) && ( a3DModel->m_Meshes != nullptr )
  1144. && ( a3DModel->m_MaterialsSize > 0 ) && ( a3DModel->m_MeshesSize > 0 ) )
  1145. {
  1146. MODEL_MATERIALS* materialVector = nullptr;
  1147. if( !aSkipMaterialInformation )
  1148. {
  1149. materialVector = getModelMaterial( a3DModel );
  1150. }
  1151. const glm::mat3 normalMatrix = glm::transpose( glm::inverse( glm::mat3( aModelMatrix ) ) );
  1152. for( unsigned int mesh_i = 0; mesh_i < a3DModel->m_MeshesSize; ++mesh_i )
  1153. {
  1154. const SMESH& mesh = a3DModel->m_Meshes[mesh_i];
  1155. // Validate the mesh pointers
  1156. wxASSERT( mesh.m_Positions != nullptr );
  1157. wxASSERT( mesh.m_FaceIdx != nullptr );
  1158. wxASSERT( mesh.m_Normals != nullptr );
  1159. wxASSERT( mesh.m_FaceIdxSize > 0 );
  1160. wxASSERT( ( mesh.m_FaceIdxSize % 3 ) == 0 );
  1161. if( ( mesh.m_Positions != nullptr ) && ( mesh.m_Normals != nullptr )
  1162. && ( mesh.m_FaceIdx != nullptr ) && ( mesh.m_FaceIdxSize > 0 )
  1163. && ( mesh.m_VertexSize > 0 ) && ( ( mesh.m_FaceIdxSize % 3 ) == 0 )
  1164. && ( mesh.m_MaterialIdx < a3DModel->m_MaterialsSize ) )
  1165. {
  1166. float fpTransparency;
  1167. const BLINN_PHONG_MATERIAL* blinn_material;
  1168. if( !aSkipMaterialInformation )
  1169. {
  1170. blinn_material = &( *materialVector )[mesh.m_MaterialIdx];
  1171. fpTransparency =
  1172. 1.0f - ( ( 1.0f - blinn_material->GetTransparency() ) * aFPOpacity );
  1173. }
  1174. // Add all face triangles
  1175. for( unsigned int faceIdx = 0; faceIdx < mesh.m_FaceIdxSize; faceIdx += 3 )
  1176. {
  1177. const unsigned int idx0 = mesh.m_FaceIdx[faceIdx + 0];
  1178. const unsigned int idx1 = mesh.m_FaceIdx[faceIdx + 1];
  1179. const unsigned int idx2 = mesh.m_FaceIdx[faceIdx + 2];
  1180. wxASSERT( idx0 < mesh.m_VertexSize );
  1181. wxASSERT( idx1 < mesh.m_VertexSize );
  1182. wxASSERT( idx2 < mesh.m_VertexSize );
  1183. if( ( idx0 < mesh.m_VertexSize ) && ( idx1 < mesh.m_VertexSize )
  1184. && ( idx2 < mesh.m_VertexSize ) )
  1185. {
  1186. const SFVEC3F& v0 = mesh.m_Positions[idx0];
  1187. const SFVEC3F& v1 = mesh.m_Positions[idx1];
  1188. const SFVEC3F& v2 = mesh.m_Positions[idx2];
  1189. const SFVEC3F& n0 = mesh.m_Normals[idx0];
  1190. const SFVEC3F& n1 = mesh.m_Normals[idx1];
  1191. const SFVEC3F& n2 = mesh.m_Normals[idx2];
  1192. // Transform vertex with the model matrix
  1193. const SFVEC3F vt0 = SFVEC3F( aModelMatrix * glm::vec4( v0, 1.0f ) );
  1194. const SFVEC3F vt1 = SFVEC3F( aModelMatrix * glm::vec4( v1, 1.0f ) );
  1195. const SFVEC3F vt2 = SFVEC3F( aModelMatrix * glm::vec4( v2, 1.0f ) );
  1196. const SFVEC3F nt0 = glm::normalize( SFVEC3F( normalMatrix * n0 ) );
  1197. const SFVEC3F nt1 = glm::normalize( SFVEC3F( normalMatrix * n1 ) );
  1198. const SFVEC3F nt2 = glm::normalize( SFVEC3F( normalMatrix * n2 ) );
  1199. TRIANGLE* newTriangle = new TRIANGLE( vt0, vt2, vt1, nt0, nt2, nt1 );
  1200. newTriangle->SetBoardItem( aBoardItem );
  1201. aDstContainer.Add( newTriangle );
  1202. if( !aSkipMaterialInformation )
  1203. {
  1204. newTriangle->SetMaterial( blinn_material );
  1205. newTriangle->SetModelTransparency( fpTransparency );
  1206. if( mesh.m_Color == nullptr )
  1207. {
  1208. const SFVEC3F diffuseColor =
  1209. a3DModel->m_Materials[mesh.m_MaterialIdx].m_Diffuse;
  1210. if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
  1211. newTriangle->SetColor( ConvertSRGBToLinear(
  1212. MaterialDiffuseToColorCAD( diffuseColor ) ) );
  1213. else
  1214. newTriangle->SetColor( ConvertSRGBToLinear( diffuseColor ) );
  1215. }
  1216. else
  1217. {
  1218. if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
  1219. {
  1220. newTriangle->SetColor(
  1221. ConvertSRGBToLinear( MaterialDiffuseToColorCAD(
  1222. mesh.m_Color[idx0] ) ),
  1223. ConvertSRGBToLinear( MaterialDiffuseToColorCAD(
  1224. mesh.m_Color[idx1] ) ),
  1225. ConvertSRGBToLinear( MaterialDiffuseToColorCAD(
  1226. mesh.m_Color[idx2] ) ) );
  1227. }
  1228. else
  1229. {
  1230. newTriangle->SetColor(
  1231. ConvertSRGBToLinear( mesh.m_Color[idx0] ),
  1232. ConvertSRGBToLinear( mesh.m_Color[idx1] ),
  1233. ConvertSRGBToLinear( mesh.m_Color[idx2] ) );
  1234. }
  1235. }
  1236. }
  1237. }
  1238. }
  1239. }
  1240. }
  1241. }
  1242. }