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.

1901 lines
58 KiB

14 years ago
14 years ago
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
12 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2009-2013 Lorenzo Mercantonio
  5. * Copyright (C) 2014-2017 Cirilo Bernardo
  6. * Copyright (C) 2018 Jean-Pierre Charras jp.charras at wanadoo.fr
  7. * Copyright (C) 2004-2018 KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <cmath>
  27. #include <exception>
  28. #include <fstream>
  29. #include <iomanip>
  30. #include <vector>
  31. #include <wx/dir.h>
  32. #include "3d_cache/3d_cache.h"
  33. #include "3d_cache/3d_info.h"
  34. #include "class_board.h"
  35. #include "class_edge_mod.h"
  36. #include "class_module.h"
  37. #include "class_pcb_text.h"
  38. #include "class_track.h"
  39. #include "class_zone.h"
  40. #include "convert_to_biu.h"
  41. #include "draw_graphic_text.h"
  42. #include "macros.h"
  43. #include "pgm_base.h"
  44. #include "plugins/3dapi/ifsg_all.h"
  45. #include "streamwrapper.h"
  46. #include "vrml_layer.h"
  47. #include "pcb_edit_frame.h"
  48. #include "../../kicad/kicad.h"
  49. #include <convert_basic_shapes_to_polygon.h>
  50. #include <zone_filler.h>
  51. // minimum width (mm) of a VRML line
  52. #define MIN_VRML_LINEWIDTH 0.05 // previously 0.12
  53. // offset for art layers, mm (silk, paste, etc)
  54. #define ART_OFFSET 0.025
  55. // offset for plating
  56. #define PLATE_OFFSET 0.005
  57. static S3D_CACHE* cache;
  58. static bool USE_INLINES; // true to use legacy inline{} behavior
  59. static bool USE_DEFS; // true to reuse component definitions
  60. static bool USE_RELPATH; // true to use relative paths in VRML inline{}
  61. static double WORLD_SCALE = 1.0; // scaling from 0.1 in to desired VRML unit
  62. static double BOARD_SCALE; // scaling from mm to desired VRML world scale
  63. static const int PRECISION = 6; // legacy precision factor (now set to 6)
  64. static wxString SUBDIR_3D; // legacy 3D subdirectory
  65. static wxString PROJ_DIR; // project directory
  66. struct VRML_COLOR
  67. {
  68. float diffuse_red;
  69. float diffuse_grn;
  70. float diffuse_blu;
  71. float spec_red;
  72. float spec_grn;
  73. float spec_blu;
  74. float emit_red;
  75. float emit_grn;
  76. float emit_blu;
  77. float ambient;
  78. float transp;
  79. float shiny;
  80. VRML_COLOR()
  81. {
  82. // default green
  83. diffuse_red = 0.13;
  84. diffuse_grn = 0.81;
  85. diffuse_blu = 0.22;
  86. spec_red = 0.01;
  87. spec_grn = 0.08;
  88. spec_blu = 0.02;
  89. emit_red = 0.0;
  90. emit_grn = 0.0;
  91. emit_blu = 0.0;
  92. ambient = 0.8;
  93. transp = 0;
  94. shiny = 0.02;
  95. }
  96. VRML_COLOR( float dr, float dg, float db,
  97. float sr, float sg, float sb,
  98. float er, float eg, float eb,
  99. float am, float tr, float sh )
  100. {
  101. diffuse_red = dr;
  102. diffuse_grn = dg;
  103. diffuse_blu = db;
  104. spec_red = sr;
  105. spec_grn = sg;
  106. spec_blu = sb;
  107. emit_red = er;
  108. emit_grn = eg;
  109. emit_blu = eb;
  110. ambient = am;
  111. transp = tr;
  112. shiny = sh;
  113. }
  114. };
  115. enum VRML_COLOR_INDEX
  116. {
  117. VRML_COLOR_NONE = -1,
  118. VRML_COLOR_PCB = 0,
  119. VRML_COLOR_TRACK,
  120. VRML_COLOR_SILK,
  121. VRML_COLOR_TIN,
  122. VRML_COLOR_LAST
  123. };
  124. static VRML_COLOR colors[VRML_COLOR_LAST];
  125. static SGNODE* sgmaterial[VRML_COLOR_LAST] = { NULL };
  126. class MODEL_VRML
  127. {
  128. private:
  129. double m_layer_z[PCB_LAYER_ID_COUNT];
  130. int m_iMaxSeg; // max. sides to a small circle
  131. double m_arcMinLen, m_arcMaxLen; // min and max lengths of an arc chord
  132. public:
  133. IFSG_TRANSFORM m_OutputPCB;
  134. VRML_LAYER m_holes;
  135. VRML_LAYER m_board;
  136. VRML_LAYER m_top_copper;
  137. VRML_LAYER m_bot_copper;
  138. VRML_LAYER m_top_silk;
  139. VRML_LAYER m_bot_silk;
  140. VRML_LAYER m_top_tin;
  141. VRML_LAYER m_bot_tin;
  142. VRML_LAYER m_plated_holes;
  143. std::list< SGNODE* > m_components;
  144. bool m_plainPCB;
  145. double m_minLineWidth; // minimum width of a VRML line segment
  146. double m_tx; // global translation along X
  147. double m_ty; // global translation along Y
  148. double m_brd_thickness; // depth of the PCB
  149. LAYER_NUM m_text_layer;
  150. int m_text_width;
  151. MODEL_VRML() : m_OutputPCB( (SGNODE*) NULL )
  152. {
  153. for( unsigned i = 0; i < DIM( m_layer_z ); ++i )
  154. m_layer_z[i] = 0;
  155. m_holes.GetArcParams( m_iMaxSeg, m_arcMinLen, m_arcMaxLen );
  156. // this default only makes sense if the output is in mm
  157. m_brd_thickness = 1.6;
  158. // pcb green
  159. colors[ VRML_COLOR_PCB ] = VRML_COLOR( .07, .3, .12, .01, .03, .01,
  160. 0, 0, 0, 0.8, 0, 0.02 );
  161. // track green
  162. colors[ VRML_COLOR_TRACK ] = VRML_COLOR( .08, .5, .1, .01, .05, .01,
  163. 0, 0, 0, 0.8, 0, 0.02 );
  164. // silkscreen white
  165. colors[ VRML_COLOR_SILK ] = VRML_COLOR( .9, .9, .9, .1, .1, .1,
  166. 0, 0, 0, 0.9, 0, 0.02 );
  167. // pad silver
  168. colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761,
  169. 0, 0, 0, 0.8, 0, 0.8 );
  170. m_plainPCB = false;
  171. SetOffset( 0.0, 0.0 );
  172. m_text_layer = F_Cu;
  173. m_text_width = 1;
  174. m_minLineWidth = MIN_VRML_LINEWIDTH;
  175. }
  176. ~MODEL_VRML()
  177. {
  178. // destroy any unassociated material appearances
  179. for( int j = 0; j < VRML_COLOR_LAST; ++j )
  180. {
  181. if( sgmaterial[j] && NULL == S3D::GetSGNodeParent( sgmaterial[j] ) )
  182. S3D::DestroyNode( sgmaterial[j] );
  183. sgmaterial[j] = NULL;
  184. }
  185. if( !m_components.empty() )
  186. {
  187. IFSG_TRANSFORM tmp( false );
  188. for( auto i : m_components )
  189. {
  190. tmp.Attach( i );
  191. tmp.SetParent( NULL );
  192. }
  193. m_components.clear();
  194. m_OutputPCB.Destroy();
  195. }
  196. }
  197. VRML_COLOR& GetColor( VRML_COLOR_INDEX aIndex )
  198. {
  199. return colors[aIndex];
  200. }
  201. void SetOffset( double aXoff, double aYoff )
  202. {
  203. m_tx = aXoff;
  204. m_ty = -aYoff;
  205. m_holes.SetVertexOffsets( aXoff, aYoff );
  206. m_board.SetVertexOffsets( aXoff, aYoff );
  207. m_top_copper.SetVertexOffsets( aXoff, aYoff );
  208. m_bot_copper.SetVertexOffsets( aXoff, aYoff );
  209. m_top_silk.SetVertexOffsets( aXoff, aYoff );
  210. m_bot_silk.SetVertexOffsets( aXoff, aYoff );
  211. m_top_tin.SetVertexOffsets( aXoff, aYoff );
  212. m_bot_tin.SetVertexOffsets( aXoff, aYoff );
  213. m_plated_holes.SetVertexOffsets( aXoff, aYoff );
  214. }
  215. double GetLayerZ( LAYER_NUM aLayer )
  216. {
  217. if( unsigned( aLayer ) >= DIM( m_layer_z ) )
  218. return 0;
  219. return m_layer_z[ aLayer ];
  220. }
  221. void SetLayerZ( LAYER_NUM aLayer, double aValue )
  222. {
  223. m_layer_z[aLayer] = aValue;
  224. }
  225. // set the scaling of the VRML world
  226. bool SetScale( double aWorldScale )
  227. {
  228. if( aWorldScale < 0.001 || aWorldScale > 10.0 )
  229. throw( std::runtime_error( "WorldScale out of range (valid range is 0.001 to 10.0)" ) );
  230. m_OutputPCB.SetScale( aWorldScale * 2.54 );
  231. WORLD_SCALE = aWorldScale * 2.54;
  232. return true;
  233. }
  234. };
  235. // static var. for dealing with text
  236. static MODEL_VRML* model_vrml;
  237. // select the VRML layer object to draw on; return true if
  238. // a layer has been selected.
  239. static bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer )
  240. {
  241. switch( layer )
  242. {
  243. case B_Cu:
  244. *vlayer = &aModel.m_bot_copper;
  245. break;
  246. case F_Cu:
  247. *vlayer = &aModel.m_top_copper;
  248. break;
  249. case B_SilkS:
  250. *vlayer = &aModel.m_bot_silk;
  251. break;
  252. case F_SilkS:
  253. *vlayer = &aModel.m_top_silk;
  254. break;
  255. default:
  256. return false;
  257. }
  258. return true;
  259. }
  260. static void create_vrml_shell( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
  261. VRML_LAYER* layer, double top_z, double bottom_z );
  262. static void create_vrml_plane( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
  263. VRML_LAYER* layer, double aHeight, bool aTopPlane );
  264. static void write_triangle_bag( std::ostream& aOut_file, VRML_COLOR& aColor,
  265. VRML_LAYER* aLayer, bool aPlane, bool aTop,
  266. double aTop_z, double aBottom_z )
  267. {
  268. /* A lot of nodes are not required, but blender sometimes chokes
  269. * without them */
  270. static const char* shape_boiler[] =
  271. {
  272. "Transform {\n",
  273. " children [\n",
  274. " Group {\n",
  275. " children [\n",
  276. " Shape {\n",
  277. " appearance Appearance {\n",
  278. " material Material {\n",
  279. 0, // Material marker
  280. " }\n",
  281. " }\n",
  282. " geometry IndexedFaceSet {\n",
  283. " solid TRUE\n",
  284. " coord Coordinate {\n",
  285. " point [\n",
  286. 0, // Coordinates marker
  287. " ]\n",
  288. " }\n",
  289. " coordIndex [\n",
  290. 0, // Index marker
  291. " ]\n",
  292. " }\n",
  293. " }\n",
  294. " ]\n",
  295. " }\n",
  296. " ]\n",
  297. "}\n",
  298. 0 // End marker
  299. };
  300. int marker_found = 0, lineno = 0;
  301. while( marker_found < 4 )
  302. {
  303. if( shape_boiler[lineno] )
  304. aOut_file << shape_boiler[lineno];
  305. else
  306. {
  307. marker_found++;
  308. switch( marker_found )
  309. {
  310. case 1: // Material marker
  311. aOut_file << " diffuseColor " << std::setprecision(3);
  312. aOut_file << aColor.diffuse_red << " ";
  313. aOut_file << aColor.diffuse_grn << " ";
  314. aOut_file << aColor.diffuse_blu << "\n";
  315. aOut_file << " specularColor ";
  316. aOut_file << aColor.spec_red << " ";
  317. aOut_file << aColor.spec_grn << " ";
  318. aOut_file << aColor.spec_blu << "\n";
  319. aOut_file << " emissiveColor ";
  320. aOut_file << aColor.emit_red << " ";
  321. aOut_file << aColor.emit_grn << " ";
  322. aOut_file << aColor.emit_blu << "\n";
  323. aOut_file << " ambientIntensity " << aColor.ambient << "\n";
  324. aOut_file << " transparency " << aColor.transp << "\n";
  325. aOut_file << " shininess " << aColor.shiny << "\n";
  326. break;
  327. case 2:
  328. if( aPlane )
  329. aLayer->WriteVertices( aTop_z, aOut_file, PRECISION );
  330. else
  331. aLayer->Write3DVertices( aTop_z, aBottom_z, aOut_file, PRECISION );
  332. aOut_file << "\n";
  333. break;
  334. case 3:
  335. if( aPlane )
  336. aLayer->WriteIndices( aTop, aOut_file );
  337. else
  338. aLayer->Write3DIndices( aOut_file );
  339. aOut_file << "\n";
  340. break;
  341. default:
  342. break;
  343. }
  344. }
  345. lineno++;
  346. }
  347. }
  348. static void write_layers( MODEL_VRML& aModel, BOARD* aPcb,
  349. const char* aFileName, OSTREAM* aOutputFile )
  350. {
  351. // VRML_LAYER board;
  352. aModel.m_board.Tesselate( &aModel.m_holes );
  353. double brdz = aModel.m_brd_thickness / 2.0
  354. - ( Millimeter2iu( ART_OFFSET / 2.0 ) ) * BOARD_SCALE;
  355. if( USE_INLINES )
  356. {
  357. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_PCB ),
  358. &aModel.m_board, false, false, brdz, -brdz );
  359. }
  360. else
  361. {
  362. create_vrml_shell( aModel.m_OutputPCB, VRML_COLOR_PCB, &aModel.m_board, brdz, -brdz );
  363. }
  364. if( aModel.m_plainPCB )
  365. {
  366. if( !USE_INLINES )
  367. S3D::WriteVRML( aFileName, true, aModel.m_OutputPCB.GetRawPtr(), USE_DEFS, true );
  368. return;
  369. }
  370. // VRML_LAYER m_top_copper;
  371. aModel.m_top_copper.Tesselate( &aModel.m_holes );
  372. if( USE_INLINES )
  373. {
  374. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TRACK ),
  375. &aModel.m_top_copper, true, true,
  376. aModel.GetLayerZ( F_Cu ), 0 );
  377. }
  378. else
  379. {
  380. create_vrml_plane( aModel.m_OutputPCB, VRML_COLOR_TRACK, &aModel.m_top_copper,
  381. aModel.GetLayerZ( F_Cu ), true );
  382. }
  383. // VRML_LAYER m_top_tin;
  384. aModel.m_top_tin.Tesselate( &aModel.m_holes );
  385. if( USE_INLINES )
  386. {
  387. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TIN ),
  388. &aModel.m_top_tin, true, true,
  389. aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
  390. 0 );
  391. }
  392. else
  393. {
  394. create_vrml_plane( aModel.m_OutputPCB, VRML_COLOR_TIN, &aModel.m_top_tin,
  395. aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
  396. true );
  397. }
  398. // VRML_LAYER m_bot_copper;
  399. aModel.m_bot_copper.Tesselate( &aModel.m_holes );
  400. if( USE_INLINES )
  401. {
  402. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TRACK ),
  403. &aModel.m_bot_copper, true, false,
  404. aModel.GetLayerZ( B_Cu ), 0 );
  405. }
  406. else
  407. {
  408. create_vrml_plane( aModel.m_OutputPCB, VRML_COLOR_TRACK, &aModel.m_bot_copper,
  409. aModel.GetLayerZ( B_Cu ), false );
  410. }
  411. // VRML_LAYER m_bot_tin;
  412. aModel.m_bot_tin.Tesselate( &aModel.m_holes );
  413. if( USE_INLINES )
  414. {
  415. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TIN ),
  416. &aModel.m_bot_tin, true, false,
  417. aModel.GetLayerZ( B_Cu )
  418. - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
  419. 0 );
  420. }
  421. else
  422. {
  423. create_vrml_plane( aModel.m_OutputPCB, VRML_COLOR_TIN, &aModel.m_bot_tin,
  424. aModel.GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
  425. false );
  426. }
  427. // VRML_LAYER PTH;
  428. aModel.m_plated_holes.Tesselate( NULL, true );
  429. if( USE_INLINES )
  430. {
  431. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_TIN ),
  432. &aModel.m_plated_holes, false, false,
  433. aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
  434. aModel.GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE );
  435. }
  436. else
  437. {
  438. create_vrml_shell( aModel.m_OutputPCB, VRML_COLOR_TIN, &aModel.m_plated_holes,
  439. aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE,
  440. aModel.GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) * BOARD_SCALE );
  441. }
  442. // VRML_LAYER m_top_silk;
  443. aModel.m_top_silk.Tesselate( &aModel.m_holes );
  444. if( USE_INLINES )
  445. {
  446. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_SILK ), &aModel.m_top_silk,
  447. true, true, aModel.GetLayerZ( F_SilkS ), 0 );
  448. }
  449. else
  450. {
  451. create_vrml_plane( aModel.m_OutputPCB, VRML_COLOR_SILK, &aModel.m_top_silk,
  452. aModel.GetLayerZ( F_SilkS ), true );
  453. }
  454. // VRML_LAYER m_bot_silk;
  455. aModel.m_bot_silk.Tesselate( &aModel.m_holes );
  456. if( USE_INLINES )
  457. {
  458. write_triangle_bag( *aOutputFile, aModel.GetColor( VRML_COLOR_SILK ), &aModel.m_bot_silk,
  459. true, false, aModel.GetLayerZ( B_SilkS ), 0 );
  460. }
  461. else
  462. {
  463. create_vrml_plane( aModel.m_OutputPCB, VRML_COLOR_SILK, &aModel.m_bot_silk,
  464. aModel.GetLayerZ( B_SilkS ), false );
  465. }
  466. if( !USE_INLINES )
  467. S3D::WriteVRML( aFileName, true, aModel.m_OutputPCB.GetRawPtr(), true, true );
  468. }
  469. static void compute_layer_Zs( MODEL_VRML& aModel, BOARD* pcb )
  470. {
  471. int copper_layers = pcb->GetCopperLayerCount();
  472. // We call it 'layer' thickness, but it's the whole board thickness!
  473. aModel.m_brd_thickness = pcb->GetDesignSettings().GetBoardThickness() * BOARD_SCALE;
  474. double half_thickness = aModel.m_brd_thickness / 2;
  475. // Compute each layer's Z value, more or less like the 3d view
  476. for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq )
  477. {
  478. PCB_LAYER_ID i = *seq;
  479. if( i < copper_layers )
  480. aModel.SetLayerZ( i, half_thickness - aModel.m_brd_thickness * i / (copper_layers - 1) );
  481. else
  482. aModel.SetLayerZ( i, - half_thickness ); // bottom layer
  483. }
  484. /* To avoid rounding interference, we apply an epsilon to each
  485. * successive layer */
  486. double epsilon_z = Millimeter2iu( ART_OFFSET ) * BOARD_SCALE;
  487. aModel.SetLayerZ( B_Paste, -half_thickness - epsilon_z * 4 );
  488. aModel.SetLayerZ( B_Adhes, -half_thickness - epsilon_z * 3 );
  489. aModel.SetLayerZ( B_SilkS, -half_thickness - epsilon_z * 2 );
  490. aModel.SetLayerZ( B_Mask, -half_thickness - epsilon_z );
  491. aModel.SetLayerZ( F_Mask, half_thickness + epsilon_z );
  492. aModel.SetLayerZ( F_SilkS, half_thickness + epsilon_z * 2 );
  493. aModel.SetLayerZ( F_Adhes, half_thickness + epsilon_z * 3 );
  494. aModel.SetLayerZ( F_Paste, half_thickness + epsilon_z * 4 );
  495. aModel.SetLayerZ( Dwgs_User, half_thickness + epsilon_z * 5 );
  496. aModel.SetLayerZ( Cmts_User, half_thickness + epsilon_z * 6 );
  497. aModel.SetLayerZ( Eco1_User, half_thickness + epsilon_z * 7 );
  498. aModel.SetLayerZ( Eco2_User, half_thickness + epsilon_z * 8 );
  499. aModel.SetLayerZ( Edge_Cuts, 0 );
  500. }
  501. static void export_vrml_line( MODEL_VRML& aModel, LAYER_NUM layer,
  502. double startx, double starty,
  503. double endx, double endy, double width )
  504. {
  505. VRML_LAYER* vlayer;
  506. if( !GetLayer( aModel, layer, &vlayer ) )
  507. return;
  508. if( width < aModel.m_minLineWidth)
  509. width = aModel.m_minLineWidth;
  510. starty = -starty;
  511. endy = -endy;
  512. double angle = atan2( endy - starty, endx - startx ) * 180.0 / M_PI;
  513. double length = Distance( startx, starty, endx, endy ) + width;
  514. double cx = ( startx + endx ) / 2.0;
  515. double cy = ( starty + endy ) / 2.0;
  516. if( !vlayer->AddSlot( cx, cy, length, width, angle, false ) )
  517. throw( std::runtime_error( vlayer->GetError() ) );
  518. }
  519. static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
  520. double startx, double starty,
  521. double endx, double endy, double width )
  522. {
  523. VRML_LAYER* vlayer;
  524. if( !GetLayer( aModel, layer, &vlayer ) )
  525. return;
  526. if( width < aModel.m_minLineWidth )
  527. width = aModel.m_minLineWidth;
  528. starty = -starty;
  529. endy = -endy;
  530. double hole, radius;
  531. radius = Distance( startx, starty, endx, endy ) + ( width / 2);
  532. hole = radius - width;
  533. if( !vlayer->AddCircle( startx, starty, radius, false ) )
  534. throw( std::runtime_error( vlayer->GetError() ) );
  535. if( hole > 0.0001 )
  536. {
  537. if( !vlayer->AddCircle( startx, starty, hole, true ) )
  538. throw( std::runtime_error( vlayer->GetError() ) );
  539. }
  540. }
  541. static void export_vrml_arc( MODEL_VRML& aModel, LAYER_NUM layer,
  542. double centerx, double centery,
  543. double arc_startx, double arc_starty,
  544. double width, double arc_angle )
  545. {
  546. VRML_LAYER* vlayer;
  547. if( !GetLayer( aModel, layer, &vlayer ) )
  548. return;
  549. if( width < aModel.m_minLineWidth )
  550. width = aModel.m_minLineWidth;
  551. centery = -centery;
  552. arc_starty = -arc_starty;
  553. if( !vlayer->AddArc( centerx, centery, arc_startx, arc_starty, width, -arc_angle, false ) )
  554. throw( std::runtime_error( vlayer->GetError() ) );
  555. }
  556. static void export_vrml_polygon( MODEL_VRML& aModel, LAYER_NUM layer,
  557. DRAWSEGMENT *aOutline, double aOrientation, wxPoint aPos )
  558. {
  559. const int circleSegmentsCount = 32;
  560. if( aOutline->IsPolyShapeValid() )
  561. {
  562. SHAPE_POLY_SET shape = aOutline->GetPolyShape();
  563. VRML_LAYER* vlayer;
  564. if( !GetLayer( aModel, layer, &vlayer ) )
  565. return;
  566. if( aOutline->GetWidth() )
  567. {
  568. shape.Inflate( aOutline->GetWidth()/2, circleSegmentsCount );
  569. shape.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  570. }
  571. shape.Rotate( -aOrientation, VECTOR2I( 0, 0 ) );
  572. shape.Move( aPos );
  573. const SHAPE_LINE_CHAIN& outline = shape.COutline( 0 );
  574. int seg = vlayer->NewContour();
  575. for( int j = 0; j < outline.PointCount(); j++ )
  576. {
  577. if( !vlayer->AddVertex( seg, outline.CPoint( j ).x * BOARD_SCALE,
  578. -outline.CPoint( j ).y * BOARD_SCALE ) )
  579. throw( std::runtime_error( vlayer->GetError() ) );
  580. }
  581. vlayer->EnsureWinding( seg, false );
  582. }
  583. }
  584. static void export_vrml_drawsegment( MODEL_VRML& aModel, DRAWSEGMENT* drawseg )
  585. {
  586. LAYER_NUM layer = drawseg->GetLayer();
  587. double w = drawseg->GetWidth() * BOARD_SCALE;
  588. double x = drawseg->GetStart().x * BOARD_SCALE;
  589. double y = drawseg->GetStart().y * BOARD_SCALE;
  590. double xf = drawseg->GetEnd().x * BOARD_SCALE;
  591. double yf = drawseg->GetEnd().y * BOARD_SCALE;
  592. double r = sqrt( pow( x - xf, 2 ) + pow( y - yf, 2 ) );
  593. // Items on the edge layer are handled elsewhere; just return
  594. if( layer == Edge_Cuts )
  595. return;
  596. switch( drawseg->GetShape() )
  597. {
  598. case S_ARC:
  599. export_vrml_arc( aModel, layer,
  600. (double) drawseg->GetCenter().x * BOARD_SCALE,
  601. (double) drawseg->GetCenter().y * BOARD_SCALE,
  602. (double) drawseg->GetArcStart().x * BOARD_SCALE,
  603. (double) drawseg->GetArcStart().y * BOARD_SCALE,
  604. w, drawseg->GetAngle() / 10 );
  605. break;
  606. case S_CIRCLE:
  607. // Break circles into two 180 arcs to prevent the vrml hole from obscuring objects
  608. // within the hole area of the circle.
  609. export_vrml_arc( aModel, layer, x, y, x, y-r, w, 180.0 );
  610. export_vrml_arc( aModel, layer, x, y, x, y+r, w, 180.0 );
  611. break;
  612. case S_POLYGON:
  613. export_vrml_polygon( aModel, layer, drawseg, 0.0, wxPoint( 0, 0 ) );
  614. break;
  615. default:
  616. export_vrml_line( aModel, layer, x, y, xf, yf, w );
  617. break;
  618. }
  619. }
  620. /* C++ doesn't have closures and neither continuation forms... this is
  621. * for coupling the vrml_text_callback with the common parameters */
  622. static void vrml_text_callback( int x0, int y0, int xf, int yf, void* aData )
  623. {
  624. LAYER_NUM m_text_layer = model_vrml->m_text_layer;
  625. int m_text_width = model_vrml->m_text_width;
  626. export_vrml_line( *model_vrml, m_text_layer,
  627. x0 * BOARD_SCALE, y0 * BOARD_SCALE,
  628. xf * BOARD_SCALE, yf * BOARD_SCALE,
  629. m_text_width * BOARD_SCALE );
  630. }
  631. static void export_vrml_pcbtext( MODEL_VRML& aModel, TEXTE_PCB* text )
  632. {
  633. model_vrml->m_text_layer = text->GetLayer();
  634. model_vrml->m_text_width = text->GetThickness();
  635. wxSize size = text->GetTextSize();
  636. if( text->IsMirrored() )
  637. size.x = -size.x;
  638. COLOR4D color = COLOR4D::BLACK; // not actually used, but needed by DrawGraphicText
  639. if( text->IsMultilineAllowed() )
  640. {
  641. wxArrayString strings_list;
  642. wxStringSplit( text->GetShownText(), strings_list, '\n' );
  643. std::vector<wxPoint> positions;
  644. positions.reserve( strings_list.Count() );
  645. text->GetPositionsOfLinesOfMultilineText( positions, strings_list.Count() );
  646. for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
  647. {
  648. wxString& txt = strings_list.Item( ii );
  649. DrawGraphicText( NULL, NULL, positions[ii], color,
  650. txt, text->GetTextAngle(), size,
  651. text->GetHorizJustify(), text->GetVertJustify(),
  652. text->GetThickness(), text->IsItalic(),
  653. true,
  654. vrml_text_callback );
  655. }
  656. }
  657. else
  658. {
  659. DrawGraphicText( NULL, NULL, text->GetTextPos(), color,
  660. text->GetShownText(), text->GetTextAngle(), size,
  661. text->GetHorizJustify(), text->GetVertJustify(),
  662. text->GetThickness(), text->IsItalic(),
  663. true,
  664. vrml_text_callback );
  665. }
  666. }
  667. static void export_vrml_drawings( MODEL_VRML& aModel, BOARD* pcb )
  668. {
  669. // draw graphic items
  670. for( auto drawing : pcb->Drawings() )
  671. {
  672. PCB_LAYER_ID layer = drawing->GetLayer();
  673. if( layer != F_Cu && layer != B_Cu && layer != B_SilkS && layer != F_SilkS )
  674. continue;
  675. switch( drawing->Type() )
  676. {
  677. case PCB_LINE_T:
  678. export_vrml_drawsegment( aModel, (DRAWSEGMENT*) drawing );
  679. break;
  680. case PCB_TEXT_T:
  681. export_vrml_pcbtext( aModel, (TEXTE_PCB*) drawing );
  682. break;
  683. default:
  684. break;
  685. }
  686. }
  687. }
  688. // board edges and cutouts
  689. static void export_vrml_board( MODEL_VRML& aModel, BOARD* aPcb )
  690. {
  691. SHAPE_POLY_SET pcbOutlines; // stores the board main outlines
  692. wxString msg;
  693. if( !aPcb->GetBoardPolygonOutlines( pcbOutlines, &msg ) )
  694. {
  695. msg << "\n\n" <<
  696. _( "Unable to calculate the board outlines; fall back to using the board boundary box." );
  697. wxMessageBox( msg );
  698. }
  699. int seg;
  700. for( int cnt = 0; cnt < pcbOutlines.OutlineCount(); cnt++ )
  701. {
  702. const SHAPE_LINE_CHAIN& outline = pcbOutlines.COutline( cnt );
  703. seg = aModel.m_board.NewContour();
  704. for( int j = 0; j < outline.PointCount(); j++ )
  705. {
  706. aModel.m_board.AddVertex( seg, (double)outline.CPoint(j).x * BOARD_SCALE,
  707. -((double)outline.CPoint(j).y * BOARD_SCALE ) );
  708. }
  709. aModel.m_board.EnsureWinding( seg, false );
  710. // Generate holes:
  711. for( int ii = 0; ii < pcbOutlines.HoleCount( cnt ); ii++ )
  712. {
  713. const SHAPE_LINE_CHAIN& hole = pcbOutlines.Hole( cnt, ii );
  714. seg = aModel.m_holes.NewContour();
  715. if( seg < 0 )
  716. {
  717. msg << "\n\n" <<
  718. _( "VRML Export Failed: Could not add holes to contours." );
  719. wxMessageBox( msg );
  720. return;
  721. }
  722. for( int j = 0; j < hole.PointCount(); j++ )
  723. {
  724. aModel.m_holes.AddVertex( seg, (double)hole.CPoint(j).x * BOARD_SCALE,
  725. -((double)hole.CPoint(j).y * BOARD_SCALE ) );
  726. }
  727. aModel.m_holes.EnsureWinding( seg, true );
  728. }
  729. }
  730. }
  731. static void export_round_padstack( MODEL_VRML& aModel, BOARD* pcb,
  732. double x, double y, double r,
  733. LAYER_NUM bottom_layer, LAYER_NUM top_layer,
  734. double hole )
  735. {
  736. LAYER_NUM layer = top_layer;
  737. bool thru = true;
  738. // if not a thru hole do not put a hole in the board
  739. if( top_layer != F_Cu || bottom_layer != B_Cu )
  740. thru = false;
  741. if( thru && hole > 0 )
  742. aModel.m_holes.AddCircle( x, -y, hole, true );
  743. if( aModel.m_plainPCB )
  744. return;
  745. while( 1 )
  746. {
  747. if( layer == B_Cu )
  748. {
  749. aModel.m_bot_copper.AddCircle( x, -y, r );
  750. if( hole > 0 && !thru )
  751. aModel.m_bot_copper.AddCircle( x, -y, hole, true );
  752. }
  753. else if( layer == F_Cu )
  754. {
  755. aModel.m_top_copper.AddCircle( x, -y, r );
  756. if( hole > 0 && !thru )
  757. aModel.m_top_copper.AddCircle( x, -y, hole, true );
  758. }
  759. if( layer == bottom_layer )
  760. break;
  761. layer = bottom_layer;
  762. }
  763. }
  764. static void export_vrml_via( MODEL_VRML& aModel, BOARD* aPcb, const VIA* aVia )
  765. {
  766. double x, y, r, hole;
  767. PCB_LAYER_ID top_layer, bottom_layer;
  768. hole = aVia->GetDrillValue() * BOARD_SCALE / 2.0;
  769. r = aVia->GetWidth() * BOARD_SCALE / 2.0;
  770. x = aVia->GetStart().x * BOARD_SCALE;
  771. y = aVia->GetStart().y * BOARD_SCALE;
  772. aVia->LayerPair( &top_layer, &bottom_layer );
  773. // do not render a buried via
  774. if( top_layer != F_Cu && bottom_layer != B_Cu )
  775. return;
  776. // Export the via padstack
  777. export_round_padstack( aModel, aPcb, x, y, r, bottom_layer, top_layer, hole );
  778. }
  779. static void export_vrml_tracks( MODEL_VRML& aModel, BOARD* pcb )
  780. {
  781. for( TRACK* track = pcb->m_Track; track; track = track->Next() )
  782. {
  783. if( track->Type() == PCB_VIA_T )
  784. {
  785. export_vrml_via( aModel, pcb, (const VIA*) track );
  786. }
  787. else if( ( track->GetLayer() == B_Cu || track->GetLayer() == F_Cu )
  788. && !aModel.m_plainPCB )
  789. export_vrml_line( aModel, track->GetLayer(),
  790. track->GetStart().x * BOARD_SCALE,
  791. track->GetStart().y * BOARD_SCALE,
  792. track->GetEnd().x * BOARD_SCALE,
  793. track->GetEnd().y * BOARD_SCALE,
  794. track->GetWidth() * BOARD_SCALE );
  795. }
  796. }
  797. static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
  798. {
  799. for( int ii = 0; ii < aPcb->GetAreaCount(); ii++ )
  800. {
  801. ZONE_CONTAINER* zone = aPcb->GetArea( ii );
  802. VRML_LAYER* vl;
  803. if( !GetLayer( aModel, zone->GetLayer(), &vl ) )
  804. continue;
  805. // fixme: this modifies the board where it shouldn't, but I don't have the time
  806. // to clean this up - TW
  807. if( !zone->IsFilled() )
  808. {
  809. ZONE_FILLER filler( aPcb );
  810. zone->SetFillMode( ZFM_POLYGONS ); // use filled polygons
  811. filler.Fill( { zone } );
  812. }
  813. const SHAPE_POLY_SET& poly = zone->GetFilledPolysList();
  814. for( int i = 0; i < poly.OutlineCount(); i++ )
  815. {
  816. const SHAPE_LINE_CHAIN& outline = poly.COutline( i );
  817. int seg = vl->NewContour();
  818. for( int j = 0; j < outline.PointCount(); j++ )
  819. {
  820. if( !vl->AddVertex( seg, (double)outline.CPoint( j ).x * BOARD_SCALE,
  821. -((double)outline.CPoint( j ).y * BOARD_SCALE ) ) )
  822. throw( std::runtime_error( vl->GetError() ) );
  823. }
  824. vl->EnsureWinding( seg, false );
  825. }
  826. }
  827. }
  828. static void export_vrml_text_module( TEXTE_MODULE* module )
  829. {
  830. if( module->IsVisible() )
  831. {
  832. wxSize size = module->GetTextSize();
  833. if( module->IsMirrored() )
  834. size.x = -size.x; // Text is mirrored
  835. model_vrml->m_text_layer = module->GetLayer();
  836. model_vrml->m_text_width = module->GetThickness();
  837. DrawGraphicText( NULL, NULL, module->GetTextPos(), BLACK,
  838. module->GetShownText(), module->GetDrawRotation(), size,
  839. module->GetHorizJustify(), module->GetVertJustify(),
  840. module->GetThickness(), module->IsItalic(),
  841. true,
  842. vrml_text_callback );
  843. }
  844. }
  845. static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline,
  846. MODULE* aModule )
  847. {
  848. LAYER_NUM layer = aOutline->GetLayer();
  849. double x = aOutline->GetStart().x * BOARD_SCALE;
  850. double y = aOutline->GetStart().y * BOARD_SCALE;
  851. double xf = aOutline->GetEnd().x * BOARD_SCALE;
  852. double yf = aOutline->GetEnd().y * BOARD_SCALE;
  853. double w = aOutline->GetWidth() * BOARD_SCALE;
  854. switch( aOutline->GetShape() )
  855. {
  856. case S_SEGMENT:
  857. export_vrml_line( aModel, layer, x, y, xf, yf, w );
  858. break;
  859. case S_ARC:
  860. export_vrml_arc( aModel, layer, x, y, xf, yf, w, aOutline->GetAngle() / 10 );
  861. break;
  862. case S_CIRCLE:
  863. export_vrml_circle( aModel, layer, x, y, xf, yf, w );
  864. break;
  865. case S_POLYGON:
  866. export_vrml_polygon( aModel, layer, aOutline, aModule->GetOrientationRadians(),
  867. aModule->GetPosition() );
  868. break;
  869. default:
  870. break;
  871. }
  872. }
  873. static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aTinLayer, D_PAD* aPad )
  874. {
  875. // The (maybe offset) pad position
  876. wxPoint pad_pos = aPad->ShapePos();
  877. double pad_x = pad_pos.x * BOARD_SCALE;
  878. double pad_y = pad_pos.y * BOARD_SCALE;
  879. wxSize pad_delta = aPad->GetDelta();
  880. double pad_dx = pad_delta.x * BOARD_SCALE / 2.0;
  881. double pad_dy = pad_delta.y * BOARD_SCALE / 2.0;
  882. double pad_w = aPad->GetSize().x * BOARD_SCALE / 2.0;
  883. double pad_h = aPad->GetSize().y * BOARD_SCALE / 2.0;
  884. switch( aPad->GetShape() )
  885. {
  886. case PAD_SHAPE_CIRCLE:
  887. if( !aTinLayer->AddCircle( pad_x, -pad_y, pad_w, false ) )
  888. throw( std::runtime_error( aTinLayer->GetError() ) );
  889. break;
  890. case PAD_SHAPE_OVAL:
  891. if( !aTinLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0,
  892. aPad->GetOrientation()/10.0, false ) )
  893. throw( std::runtime_error( aTinLayer->GetError() ) );
  894. break;
  895. case PAD_SHAPE_ROUNDRECT:
  896. {
  897. SHAPE_POLY_SET polySet;
  898. int segmentToCircleCount = 32;
  899. const int corner_radius = aPad->GetRoundRectCornerRadius( aPad->GetSize() );
  900. TransformRoundRectToPolygon( polySet, wxPoint( 0, 0 ), aPad->GetSize(),
  901. 0.0, corner_radius, segmentToCircleCount );
  902. std::vector< wxRealPoint > cornerList;
  903. // TransformRoundRectToPolygon creates only one convex polygon
  904. SHAPE_LINE_CHAIN poly( polySet.Outline( 0 ) );
  905. for( int ii = 0; ii < poly.PointCount(); ++ii )
  906. cornerList.push_back( wxRealPoint( poly.Point( ii ).x * BOARD_SCALE,
  907. -poly.Point( ii ).y * BOARD_SCALE ) );
  908. // Close polygon
  909. cornerList.push_back( cornerList[0] );
  910. if( !aTinLayer->AddPolygon( cornerList, pad_x, -pad_y, aPad->GetOrientation() ) )
  911. throw( std::runtime_error( aTinLayer->GetError() ) );
  912. break;
  913. }
  914. case PAD_SHAPE_CUSTOM:
  915. {
  916. SHAPE_POLY_SET polySet;
  917. int segmentToCircleCount = 32;
  918. std::vector< wxRealPoint > cornerList;
  919. aPad->MergePrimitivesAsPolygon( &polySet, segmentToCircleCount );
  920. for( int cnt = 0; cnt < polySet.OutlineCount(); ++cnt )
  921. {
  922. SHAPE_LINE_CHAIN& poly = polySet.Outline( cnt );
  923. cornerList.clear();
  924. for( int ii = 0; ii < poly.PointCount(); ++ii )
  925. cornerList.push_back( wxRealPoint( poly.Point( ii ).x * BOARD_SCALE,
  926. -poly.Point( ii ).y * BOARD_SCALE ) );
  927. // Close polygon
  928. cornerList.push_back( cornerList[0] );
  929. if( !aTinLayer->AddPolygon( cornerList, pad_x, -pad_y, aPad->GetOrientation() ) )
  930. throw( std::runtime_error( aTinLayer->GetError() ) );
  931. }
  932. break;
  933. }
  934. case PAD_SHAPE_RECT:
  935. // Just to be sure :D
  936. pad_dx = 0;
  937. pad_dy = 0;
  938. case PAD_SHAPE_TRAPEZOID:
  939. {
  940. double coord[8] =
  941. {
  942. -pad_w + pad_dy, -pad_h - pad_dx,
  943. -pad_w - pad_dy, pad_h + pad_dx,
  944. +pad_w - pad_dy, -pad_h + pad_dx,
  945. +pad_w + pad_dy, pad_h - pad_dx
  946. };
  947. for( int i = 0; i < 4; i++ )
  948. {
  949. RotatePoint( &coord[i * 2], &coord[i * 2 + 1], aPad->GetOrientation() );
  950. coord[i * 2] += pad_x;
  951. coord[i * 2 + 1] += pad_y;
  952. }
  953. int lines;
  954. lines = aTinLayer->NewContour();
  955. if( lines < 0 )
  956. throw( std::runtime_error( aTinLayer->GetError() ) );
  957. if( !aTinLayer->AddVertex( lines, coord[0], -coord[1] ) )
  958. throw( std::runtime_error( aTinLayer->GetError() ) );
  959. if( !aTinLayer->AddVertex( lines, coord[4], -coord[5] ) )
  960. throw( std::runtime_error( aTinLayer->GetError() ) );
  961. if( !aTinLayer->AddVertex( lines, coord[6], -coord[7] ) )
  962. throw( std::runtime_error( aTinLayer->GetError() ) );
  963. if( !aTinLayer->AddVertex( lines, coord[2], -coord[3] ) )
  964. throw( std::runtime_error( aTinLayer->GetError() ) );
  965. if( !aTinLayer->EnsureWinding( lines, false ) )
  966. throw( std::runtime_error( aTinLayer->GetError() ) );
  967. break;
  968. }
  969. default:
  970. break;
  971. }
  972. }
  973. static void export_vrml_pad( MODEL_VRML& aModel, BOARD* aPcb, D_PAD* aPad )
  974. {
  975. double hole_drill_w = (double) aPad->GetDrillSize().x * BOARD_SCALE / 2.0;
  976. double hole_drill_h = (double) aPad->GetDrillSize().y * BOARD_SCALE / 2.0;
  977. double hole_drill = std::min( hole_drill_w, hole_drill_h );
  978. double hole_x = aPad->GetPosition().x * BOARD_SCALE;
  979. double hole_y = aPad->GetPosition().y * BOARD_SCALE;
  980. // Export the hole on the edge layer
  981. if( hole_drill > 0 )
  982. {
  983. bool pth = false;
  984. if( ( aPad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
  985. && !aModel.m_plainPCB )
  986. pth = true;
  987. if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
  988. {
  989. // Oblong hole (slot)
  990. if( pth )
  991. {
  992. aModel.m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0 + PLATE_OFFSET,
  993. hole_drill_h * 2.0 + PLATE_OFFSET,
  994. aPad->GetOrientation()/10.0, true, true );
  995. aModel.m_plated_holes.AddSlot( hole_x, -hole_y,
  996. hole_drill_w * 2.0, hole_drill_h * 2.0,
  997. aPad->GetOrientation()/10.0, true, false );
  998. }
  999. else
  1000. {
  1001. aModel.m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
  1002. aPad->GetOrientation()/10.0, true, false );
  1003. }
  1004. }
  1005. else
  1006. {
  1007. // Drill a round hole
  1008. if( pth )
  1009. {
  1010. aModel.m_holes.AddCircle( hole_x, -hole_y, hole_drill + PLATE_OFFSET, true, true );
  1011. aModel.m_plated_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
  1012. }
  1013. else
  1014. {
  1015. aModel.m_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
  1016. }
  1017. }
  1018. }
  1019. if( aModel.m_plainPCB )
  1020. return;
  1021. // The pad proper, on the selected layers
  1022. LSET layer_mask = aPad->GetLayerSet();
  1023. if( layer_mask[B_Cu] )
  1024. {
  1025. if( layer_mask[B_Mask] )
  1026. export_vrml_padshape( aModel, &aModel.m_bot_tin, aPad );
  1027. else
  1028. export_vrml_padshape( aModel, &aModel.m_bot_copper, aPad );
  1029. }
  1030. if( layer_mask[F_Cu] )
  1031. {
  1032. if( layer_mask[F_Mask] )
  1033. export_vrml_padshape( aModel, &aModel.m_top_tin, aPad );
  1034. else
  1035. export_vrml_padshape( aModel, &aModel.m_top_copper, aPad );
  1036. }
  1037. }
  1038. // From axis/rot to quaternion
  1039. static void build_quat( double x, double y, double z, double a, double q[4] )
  1040. {
  1041. double sina = sin( a / 2 );
  1042. q[0] = x * sina;
  1043. q[1] = y * sina;
  1044. q[2] = z * sina;
  1045. q[3] = cos( a / 2 );
  1046. }
  1047. // From quaternion to axis/rot
  1048. static void from_quat( double q[4], double rot[4] )
  1049. {
  1050. rot[3] = acos( q[3] ) * 2;
  1051. for( int i = 0; i < 3; i++ )
  1052. {
  1053. rot[i] = q[i] / sin( rot[3] / 2 );
  1054. }
  1055. }
  1056. // Quaternion composition
  1057. static void compose_quat( double q1[4], double q2[4], double qr[4] )
  1058. {
  1059. double tmp[4];
  1060. tmp[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1];
  1061. tmp[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2];
  1062. tmp[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0];
  1063. tmp[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2];
  1064. qr[0] = tmp[0];
  1065. qr[1] = tmp[1];
  1066. qr[2] = tmp[2];
  1067. qr[3] = tmp[3];
  1068. }
  1069. static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb,
  1070. MODULE* aModule, std::ostream* aOutputFile )
  1071. {
  1072. if( !aModel.m_plainPCB )
  1073. {
  1074. // Reference and value
  1075. if( aModule->Reference().IsVisible() )
  1076. export_vrml_text_module( &aModule->Reference() );
  1077. if( aModule->Value().IsVisible() )
  1078. export_vrml_text_module( &aModule->Value() );
  1079. // Export module edges
  1080. for( EDA_ITEM* item = aModule->GraphicalItemsList(); item; item = item->Next() )
  1081. {
  1082. switch( item->Type() )
  1083. {
  1084. case PCB_MODULE_TEXT_T:
  1085. export_vrml_text_module( static_cast<TEXTE_MODULE*>( item ) );
  1086. break;
  1087. case PCB_MODULE_EDGE_T:
  1088. export_vrml_edge_module( aModel, static_cast<EDGE_MODULE*>( item ),
  1089. aModule );
  1090. break;
  1091. default:
  1092. break;
  1093. }
  1094. }
  1095. }
  1096. // Export pads
  1097. for( D_PAD* pad = aModule->PadsList(); pad; pad = pad->Next() )
  1098. export_vrml_pad( aModel, aPcb, pad );
  1099. bool isFlipped = aModule->GetLayer() == B_Cu;
  1100. // Export the object VRML model(s)
  1101. auto sM = aModule->Models().begin();
  1102. auto eM = aModule->Models().end();
  1103. wxFileName subdir( SUBDIR_3D, "" );
  1104. while( sM != eM )
  1105. {
  1106. SGNODE* mod3d = (SGNODE*) cache->Load( sM->m_Filename );
  1107. if( NULL == mod3d )
  1108. {
  1109. ++sM;
  1110. continue;
  1111. }
  1112. /* Calculate 3D shape rotation:
  1113. * this is the rotation parameters, with an additional 180 deg rotation
  1114. * for footprints that are flipped
  1115. * When flipped, axis rotation is the horizontal axis (X axis)
  1116. */
  1117. double rotx = -sM->m_Rotation.x;
  1118. double roty = -sM->m_Rotation.y;
  1119. double rotz = -sM->m_Rotation.z;
  1120. if( isFlipped )
  1121. {
  1122. rotx += 180.0;
  1123. roty = -roty;
  1124. rotz = -rotz;
  1125. }
  1126. // Do some quaternion munching
  1127. double q1[4], q2[4], rot[4];
  1128. build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 );
  1129. build_quat( 0, 1, 0, DEG2RAD( roty ), q2 );
  1130. compose_quat( q1, q2, q1 );
  1131. build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 );
  1132. compose_quat( q1, q2, q1 );
  1133. // Note here aModule->GetOrientation() is in 0.1 degrees,
  1134. // so module rotation has to be converted to radians
  1135. build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 );
  1136. compose_quat( q1, q2, q1 );
  1137. from_quat( q1, rot );
  1138. double offsetFactor = 1000.0f * IU_PER_MILS / 25.4f;
  1139. // adjust 3D shape local offset position
  1140. // they are given in mm, so they are converted in board IU.
  1141. double offsetx = sM->m_Offset.x * offsetFactor;
  1142. double offsety = sM->m_Offset.y * offsetFactor;
  1143. double offsetz = sM->m_Offset.z * offsetFactor;
  1144. if( isFlipped )
  1145. offsetz = -offsetz;
  1146. else // In normal mode, Y axis is reversed in Pcbnew.
  1147. offsety = -offsety;
  1148. RotatePoint( &offsetx, &offsety, aModule->GetOrientation() );
  1149. SGPOINT trans;
  1150. trans.x = ( offsetx + aModule->GetPosition().x ) * BOARD_SCALE + aModel.m_tx;
  1151. trans.y = -(offsety + aModule->GetPosition().y) * BOARD_SCALE - aModel.m_ty;
  1152. trans.z = (offsetz * BOARD_SCALE ) + aModel.GetLayerZ( aModule->GetLayer() );
  1153. if( USE_INLINES )
  1154. {
  1155. wxFileName srcFile = cache->GetResolver()->ResolvePath( sM->m_Filename );
  1156. wxFileName dstFile;
  1157. dstFile.SetPath( SUBDIR_3D );
  1158. dstFile.SetName( srcFile.GetName() );
  1159. dstFile.SetExt( "wrl" );
  1160. // copy the file if necessary
  1161. wxDateTime srcModTime = srcFile.GetModificationTime();
  1162. wxDateTime destModTime = srcModTime;
  1163. destModTime.SetToCurrent();
  1164. if( dstFile.FileExists() )
  1165. destModTime = dstFile.GetModificationTime();
  1166. if( srcModTime != destModTime )
  1167. {
  1168. wxLogDebug( "Copying 3D model %s to %s.",
  1169. GetChars( srcFile.GetFullPath() ),
  1170. GetChars( dstFile.GetFullPath() ) );
  1171. wxString fileExt = srcFile.GetExt();
  1172. fileExt.LowerCase();
  1173. // copy VRML models and use the scenegraph library to
  1174. // translate other model types
  1175. if( fileExt == "wrl" )
  1176. {
  1177. if( !wxCopyFile( srcFile.GetFullPath(), dstFile.GetFullPath() ) )
  1178. continue;
  1179. }
  1180. else
  1181. {
  1182. if( !S3D::WriteVRML( dstFile.GetFullPath().ToUTF8(), true, mod3d, USE_DEFS, true ) )
  1183. continue;
  1184. }
  1185. }
  1186. (*aOutputFile) << "Transform {\n";
  1187. // only write a rotation if it is >= 0.1 deg
  1188. if( std::abs( rot[3] ) > 0.0001745 )
  1189. {
  1190. (*aOutputFile) << " rotation " << std::setprecision( 5 );
  1191. (*aOutputFile) << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
  1192. }
  1193. (*aOutputFile) << " translation " << std::setprecision( PRECISION );
  1194. (*aOutputFile) << trans.x << " ";
  1195. (*aOutputFile) << trans.y << " ";
  1196. (*aOutputFile) << trans.z << "\n";
  1197. (*aOutputFile) << " scale ";
  1198. (*aOutputFile) << sM->m_Scale.x << " ";
  1199. (*aOutputFile) << sM->m_Scale.y << " ";
  1200. (*aOutputFile) << sM->m_Scale.z << "\n";
  1201. (*aOutputFile) << " children [\n Inline {\n url \"";
  1202. if( USE_RELPATH )
  1203. {
  1204. wxFileName tmp = dstFile;
  1205. tmp.SetExt( "" );
  1206. tmp.SetName( "" );
  1207. tmp.RemoveLastDir();
  1208. dstFile.MakeRelativeTo( tmp.GetPath() );
  1209. }
  1210. wxString fn = dstFile.GetFullPath();
  1211. fn.Replace( "\\", "/" );
  1212. (*aOutputFile) << TO_UTF8( fn ) << "\"\n } ]\n";
  1213. (*aOutputFile) << " }\n";
  1214. }
  1215. else
  1216. {
  1217. IFSG_TRANSFORM* modelShape = new IFSG_TRANSFORM( aModel.m_OutputPCB.GetRawPtr() );
  1218. // only write a rotation if it is >= 0.1 deg
  1219. if( std::abs( rot[3] ) > 0.0001745 )
  1220. modelShape->SetRotation( SGVECTOR( rot[0], rot[1], rot[2] ), rot[3] );
  1221. modelShape->SetTranslation( trans );
  1222. modelShape->SetScale( SGPOINT( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ) );
  1223. if( NULL == S3D::GetSGNodeParent( mod3d ) )
  1224. {
  1225. aModel.m_components.push_back( mod3d );
  1226. modelShape->AddChildNode( mod3d );
  1227. }
  1228. else
  1229. {
  1230. modelShape->AddRefNode( mod3d );
  1231. }
  1232. }
  1233. ++sM;
  1234. }
  1235. }
  1236. bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMtoWRMLunit,
  1237. bool aExport3DFiles, bool aUseRelativePaths,
  1238. bool aUsePlainPCB, const wxString& a3D_Subdir,
  1239. double aXRef, double aYRef )
  1240. {
  1241. BOARD* pcb = GetBoard();
  1242. bool ok = true;
  1243. USE_INLINES = aExport3DFiles;
  1244. USE_DEFS = true;
  1245. USE_RELPATH = aUseRelativePaths;
  1246. cache = Prj().Get3DCacheManager();
  1247. PROJ_DIR = Prj().GetProjectPath();
  1248. SUBDIR_3D = a3D_Subdir;
  1249. MODEL_VRML model3d;
  1250. model_vrml = &model3d;
  1251. model3d.SetScale( aMMtoWRMLunit );
  1252. if( USE_INLINES )
  1253. {
  1254. BOARD_SCALE = MM_PER_IU / 2.54;
  1255. model3d.SetOffset( -aXRef / 2.54, aYRef / 2.54 );
  1256. }
  1257. else
  1258. {
  1259. BOARD_SCALE = MM_PER_IU;
  1260. model3d.SetOffset( -aXRef, aYRef );
  1261. }
  1262. // plain PCB or else PCB with copper and silkscreen
  1263. model3d.m_plainPCB = aUsePlainPCB;
  1264. try
  1265. {
  1266. // Preliminary computation: the z value for each layer
  1267. compute_layer_Zs(model3d, pcb);
  1268. // board edges and cutouts
  1269. export_vrml_board(model3d, pcb);
  1270. // Drawing and text on the board
  1271. if( !aUsePlainPCB )
  1272. export_vrml_drawings( model3d, pcb );
  1273. // Export vias and trackage
  1274. export_vrml_tracks( model3d, pcb );
  1275. // Export zone fills
  1276. if( !aUsePlainPCB )
  1277. export_vrml_zones( model3d, pcb);
  1278. if( USE_INLINES )
  1279. {
  1280. // check if the 3D Subdir exists - create if not
  1281. wxFileName subdir( SUBDIR_3D, "" );
  1282. if( ! subdir.DirExists() )
  1283. {
  1284. if( !wxDir::Make( subdir.GetFullPath() ) )
  1285. throw( std::runtime_error( "Could not create 3D model subdirectory" ) );
  1286. }
  1287. OPEN_OSTREAM( output_file, TO_UTF8( aFullFileName ) );
  1288. if( output_file.fail() )
  1289. {
  1290. std::ostringstream ostr;
  1291. ostr << "Could not open file '" << TO_UTF8( aFullFileName ) << "'";
  1292. throw( std::runtime_error( ostr.str().c_str() ) );
  1293. }
  1294. output_file.imbue( std::locale( "C" ) );
  1295. // Begin with the usual VRML boilerplate
  1296. wxString fn = aFullFileName;
  1297. fn.Replace( "\\" , "/" );
  1298. output_file << "#VRML V2.0 utf8\n";
  1299. output_file << "WorldInfo {\n";
  1300. output_file << " title \"" << TO_UTF8( fn ) << " - Generated by Pcbnew\"\n";
  1301. output_file << "}\n";
  1302. output_file << "Transform {\n";
  1303. output_file << " scale " << std::setprecision( PRECISION );
  1304. output_file << WORLD_SCALE << " ";
  1305. output_file << WORLD_SCALE << " ";
  1306. output_file << WORLD_SCALE << "\n";
  1307. output_file << " children [\n";
  1308. // Export footprints
  1309. for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
  1310. export_vrml_module( model3d, pcb, module, &output_file );
  1311. // write out the board and all layers
  1312. write_layers( model3d, pcb, TO_UTF8( aFullFileName ), &output_file );
  1313. // Close the outer 'transform' node
  1314. output_file << "]\n}\n";
  1315. CLOSE_STREAM( output_file );
  1316. }
  1317. else
  1318. {
  1319. // Export footprints
  1320. for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
  1321. export_vrml_module( model3d, pcb, module, NULL );
  1322. // write out the board and all layers
  1323. write_layers( model3d, pcb, TO_UTF8( aFullFileName ), NULL );
  1324. }
  1325. }
  1326. catch( const std::exception& e )
  1327. {
  1328. wxString msg;
  1329. msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
  1330. wxMessageBox( msg );
  1331. ok = false;
  1332. }
  1333. return ok;
  1334. }
  1335. static SGNODE* getSGColor( VRML_COLOR_INDEX colorIdx )
  1336. {
  1337. if( colorIdx == -1 )
  1338. colorIdx = VRML_COLOR_PCB;
  1339. else if( colorIdx == VRML_COLOR_LAST )
  1340. return NULL;
  1341. if( sgmaterial[colorIdx] )
  1342. return sgmaterial[colorIdx];
  1343. IFSG_APPEARANCE vcolor( (SGNODE*) NULL );
  1344. VRML_COLOR* cp = &colors[colorIdx];
  1345. vcolor.SetSpecular( cp->spec_red, cp->spec_grn, cp->spec_blu );
  1346. vcolor.SetDiffuse( cp->diffuse_red, cp->diffuse_grn, cp->diffuse_blu );
  1347. vcolor.SetShininess( cp->shiny );
  1348. // NOTE: XXX - replace with a better equation; using this definition
  1349. // of ambient will not yield the best results
  1350. vcolor.SetAmbient( cp->ambient, cp->ambient, cp->ambient );
  1351. vcolor.SetTransparency( cp->transp );
  1352. sgmaterial[colorIdx] = vcolor.GetRawPtr();
  1353. return sgmaterial[colorIdx];
  1354. }
  1355. static void create_vrml_plane( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
  1356. VRML_LAYER* layer, double top_z, bool aTopPlane )
  1357. {
  1358. std::vector< double > vertices;
  1359. std::vector< int > idxPlane;
  1360. std::vector< int > idxSide;
  1361. if( !(*layer).Get2DTriangles( vertices, idxPlane, top_z, aTopPlane ) )
  1362. {
  1363. #ifdef DEBUG
  1364. do {
  1365. std::ostringstream ostr;
  1366. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  1367. ostr << " * [INFO] no vertex data";
  1368. wxLogDebug( "%s\n", ostr.str().c_str() );
  1369. } while( 0 );
  1370. #endif
  1371. return;
  1372. }
  1373. if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
  1374. {
  1375. #ifdef DEBUG
  1376. do {
  1377. std::ostringstream ostr;
  1378. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  1379. ostr << " * [BUG] index lists are not a multiple of 3 (not a triangle list)";
  1380. wxLogDebug( "%s\n", ostr.str().c_str() );
  1381. } while( 0 );
  1382. #endif
  1383. throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a triangle list)" ) );
  1384. }
  1385. std::vector< SGPOINT > vlist;
  1386. size_t nvert = vertices.size() / 3;
  1387. size_t j = 0;
  1388. for( size_t i = 0; i < nvert; ++i, j+= 3 )
  1389. vlist.push_back( SGPOINT( vertices[j], vertices[j+1], vertices[j+2] ) );
  1390. // create the intermediate scenegraph
  1391. IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
  1392. IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
  1393. IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
  1394. IFSG_COORDS cp( face ); // coordinates for all faces
  1395. cp.SetCoordsList( nvert, &vlist[0] );
  1396. IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
  1397. coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
  1398. IFSG_NORMALS norms( face ); // normals for the top and bottom planes
  1399. // set the normals
  1400. if( aTopPlane )
  1401. {
  1402. for( size_t i = 0; i < nvert; ++i )
  1403. norms.AddNormal( 0.0, 0.0, 1.0 );
  1404. }
  1405. else
  1406. {
  1407. for( size_t i = 0; i < nvert; ++i )
  1408. norms.AddNormal( 0.0, 0.0, -1.0 );
  1409. }
  1410. // assign a color from the palette
  1411. SGNODE* modelColor = getSGColor( colorID );
  1412. if( NULL != modelColor )
  1413. {
  1414. if( NULL == S3D::GetSGNodeParent( modelColor ) )
  1415. shape.AddChildNode( modelColor );
  1416. else
  1417. shape.AddRefNode( modelColor );
  1418. }
  1419. return;
  1420. }
  1421. static void create_vrml_shell( IFSG_TRANSFORM& PcbOutput, VRML_COLOR_INDEX colorID,
  1422. VRML_LAYER* layer, double top_z, double bottom_z )
  1423. {
  1424. std::vector< double > vertices;
  1425. std::vector< int > idxPlane;
  1426. std::vector< int > idxSide;
  1427. if( top_z < bottom_z )
  1428. {
  1429. double tmp = top_z;
  1430. top_z = bottom_z;
  1431. bottom_z = tmp;
  1432. }
  1433. if( !(*layer).Get3DTriangles( vertices, idxPlane, idxSide, top_z, bottom_z ) )
  1434. {
  1435. #ifdef DEBUG
  1436. do {
  1437. std::ostringstream ostr;
  1438. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  1439. ostr << " * [INFO] no vertex data";
  1440. wxLogDebug( "%s\n", ostr.str().c_str() );
  1441. } while( 0 );
  1442. #endif
  1443. return;
  1444. }
  1445. if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
  1446. {
  1447. #ifdef DEBUG
  1448. do {
  1449. std::ostringstream ostr;
  1450. ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n";
  1451. ostr << " * [BUG] index lists are not a multiple of 3 (not a triangle list)";
  1452. wxLogDebug( "%s\n", ostr.str().c_str() );
  1453. } while( 0 );
  1454. #endif
  1455. throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a triangle list)" ) );
  1456. }
  1457. std::vector< SGPOINT > vlist;
  1458. size_t nvert = vertices.size() / 3;
  1459. size_t j = 0;
  1460. for( size_t i = 0; i < nvert; ++i, j+= 3 )
  1461. vlist.push_back( SGPOINT( vertices[j], vertices[j+1], vertices[j+2] ) );
  1462. // create the intermediate scenegraph
  1463. IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
  1464. IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
  1465. IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
  1466. IFSG_COORDS cp( face ); // coordinates for all faces
  1467. cp.SetCoordsList( nvert, &vlist[0] );
  1468. IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
  1469. coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
  1470. IFSG_NORMALS norms( face ); // normals for the top and bottom planes
  1471. // number of TOP (and bottom) vertices
  1472. j = nvert / 2;
  1473. // set the TOP normals
  1474. for( size_t i = 0; i < j; ++i )
  1475. norms.AddNormal( 0.0, 0.0, 1.0 );
  1476. // set the BOTTOM normals
  1477. for( size_t i = 0; i < j; ++i )
  1478. norms.AddNormal( 0.0, 0.0, -1.0 );
  1479. // assign a color from the palette
  1480. SGNODE* modelColor = getSGColor( colorID );
  1481. if( NULL != modelColor )
  1482. {
  1483. if( NULL == S3D::GetSGNodeParent( modelColor ) )
  1484. shape.AddChildNode( modelColor );
  1485. else
  1486. shape.AddRefNode( modelColor );
  1487. }
  1488. // create a second shape describing the vertical walls of the extrusion
  1489. // using per-vertex-per-face-normals
  1490. shape.NewNode( tx0 );
  1491. shape.AddRefNode( modelColor ); // set the color to be the same as the top/bottom
  1492. face.NewNode( shape );
  1493. cp.NewNode( face ); // new vertex list
  1494. norms.NewNode( face ); // new normals list
  1495. coordIdx.NewNode( face ); // new index list
  1496. // populate the new per-face vertex list and its indices and normals
  1497. std::vector< int >::iterator sI = idxSide.begin();
  1498. std::vector< int >::iterator eI = idxSide.end();
  1499. size_t sidx = 0; // index to the new coord set
  1500. SGPOINT p1, p2, p3;
  1501. SGVECTOR vnorm;
  1502. while( sI != eI )
  1503. {
  1504. p1 = vlist[*sI];
  1505. cp.AddCoord( p1 );
  1506. ++sI;
  1507. p2 = vlist[*sI];
  1508. cp.AddCoord( p2 );
  1509. ++sI;
  1510. p3 = vlist[*sI];
  1511. cp.AddCoord( p3 );
  1512. ++sI;
  1513. vnorm.SetVector( S3D::CalcTriNorm( p1, p2, p3 ) );
  1514. norms.AddNormal( vnorm );
  1515. norms.AddNormal( vnorm );
  1516. norms.AddNormal( vnorm );
  1517. coordIdx.AddIndex( (int)sidx );
  1518. ++sidx;
  1519. coordIdx.AddIndex( (int)sidx );
  1520. ++sidx;
  1521. coordIdx.AddIndex( (int)sidx );
  1522. ++sidx;
  1523. }
  1524. }