KiCad PCB EDA Suite
export_vrml.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 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-2021 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 
27 #include <exception>
28 #include <fstream>
29 #include <iomanip>
30 #include <vector>
31 #include <wx/dir.h>
32 
33 #include "3d_cache/3d_cache.h"
34 #include "3d_cache/3d_info.h"
35 #include "board.h"
36 #include "fp_shape.h"
37 #include "footprint.h"
38 #include "pcb_text.h"
39 #include "track.h"
40 #include "convert_to_biu.h"
41 #include <core/arraydim.h>
42 #include <filename_resolver.h>
43 #include "plugins/3dapi/ifsg_all.h"
44 #include "streamwrapper.h"
45 #include "vrml_layer.h"
46 #include "pcb_edit_frame.h"
47 
50 #include <macros.h>
51 
52 #include <exporter_vrml.h>
53 
54 
56  m_OutputPCB( (SGNODE*) NULL )
57 {
58  m_ReuseDef = true;
59  m_precision = 6;
60  m_WorldScale = 1.0;
61  m_Cache3Dmodels = nullptr;
62  m_Pcb = nullptr;
65  m_BoardToVrmlScale = MM_PER_IU;
66 
67  for( int ii = 0; ii < VRML_COLOR_LAST; ++ii )
68  m_sgmaterial[ii] = nullptr;
69 
70  for( unsigned i = 0; i < arrayDim( m_layer_z ); ++i )
71  m_layer_z[i] = 0;
72 
73  // this default only makes sense if the output is in mm
74  m_brd_thickness = 1.6;
75 
76  // pcb green
78  0.12f, 0.20f, 0.19f, 0.01f, 0.03f, 0.01f, 0.0f, 0.0f, 0.0f, 0.8f, 0.0f, 0.02f );
79  // copper color
81  0.72f, 0.45f, 0.2f, 0.01f, 0.05f, 0.01f, 0.0f, 0.0f, 0.0f, 0.8f, 0.0f, 0.02f );
82  // silkscreen white
84  0.7f, 0.7f, 0.9f, 0.1f, 0.1f, 0.1f, 0.0f, 0.0f, 0.0f, 0.9f, 0.0f, 0.02f );
85  // solder paste silver (gray)
86  vrml_colors_list[VRML_COLOR_PASTE] = VRML_COLOR( 0.4f, 0.4f, 0.4f, 0.2f, 0.2f, 0.2f, 0.0f,
87  0.0f, 0.0f, 0.8f, 0.0f, 0.8f );
88  // solder mask green with transparency
90  0.07f, 0.3f, 0.12f, 0.01f, 0.03f, 0.01f, 0.0f, 0.0f, 0.0f, 0.8f, 0.25f, 0.02f );
91 
92  SetOffset( 0.0, 0.0 );
93 }
94 
95 
97 {
98  // destroy any unassociated material appearances
99  for( int j = 0; j < VRML_COLOR_LAST; ++j )
100  {
103 
104  m_sgmaterial[j] = NULL;
105  }
106 
107  if( !m_components.empty() )
108  {
109  IFSG_TRANSFORM tmp( false );
110 
111  for( auto i : m_components )
112  {
113  tmp.Attach( i );
114  tmp.SetParent( NULL );
115  }
116 
117  m_components.clear();
119  }
120 }
121 
122 
123 bool EXPORTER_PCB_VRML::SetScale( double aWorldScale )
124 {
125  // set the scaling of the VRML world
126  if( aWorldScale < 0.001 || aWorldScale > 10.0 )
127  throw( std::runtime_error( "WorldScale out of range (valid range is 0.001 to 10.0)" ) );
128 
129  m_OutputPCB.SetScale( aWorldScale * 2.54 );
130  m_WorldScale = aWorldScale * 2.54;
131 
132  return true;
133 }
134 
135 
136 void EXPORTER_PCB_VRML::SetOffset( double aXoff, double aYoff )
137 {
138  m_tx = aXoff;
139  m_ty = -aYoff;
140 
141  m_holes.SetVertexOffsets( aXoff, aYoff );
142  m_3D_board.SetVertexOffsets( aXoff, aYoff );
143  m_top_copper.SetVertexOffsets( aXoff, aYoff );
144  m_bot_copper.SetVertexOffsets( aXoff, aYoff );
145  m_top_silk.SetVertexOffsets( aXoff, aYoff );
146  m_bot_silk.SetVertexOffsets( aXoff, aYoff );
147  m_top_paste.SetVertexOffsets( aXoff, aYoff );
148  m_bot_paste.SetVertexOffsets( aXoff, aYoff );
149  m_top_soldermask.SetVertexOffsets( aXoff, aYoff );
150  m_bot_soldermask.SetVertexOffsets( aXoff, aYoff );
151  m_plated_holes.SetVertexOffsets( aXoff, aYoff );
152 }
153 
154 
155 bool EXPORTER_PCB_VRML::GetLayer3D( LAYER_NUM layer, VRML_LAYER** vlayer )
156 {
157  // select the VRML layer object to draw on; return true if
158  // a layer has been selected.
159  switch( layer )
160  {
161  case B_Cu: *vlayer = &m_bot_copper; return true;
162  case F_Cu: *vlayer = &m_top_copper; return true;
163  case B_SilkS: *vlayer = &m_bot_silk; return true;
164  case F_SilkS: *vlayer = &m_top_silk; return true;
165  case B_Mask: *vlayer = &m_bot_soldermask; return true;
166  case F_Mask: *vlayer = &m_top_soldermask; return true;
167  case B_Paste: *vlayer = &m_bot_paste; return true;
168  case F_Paste: *vlayer = &m_top_paste; return true;
169  default: return false;
170  }
171 }
172 
174 {
175  SHAPE_POLY_SET holes, outlines = m_pcbOutlines;
176  // holes is the solder mask opening.
177  // the actual shape is the negative shape of mask opening.
178  PCB_LAYER_ID pcb_layer = F_Mask;
179  VRML_LAYER* vrmllayer = &m_top_soldermask;
180 
181  for( int lcnt = 0; lcnt < 2; lcnt++ )
182  {
183  holes.RemoveAllContours();
184  outlines.RemoveAllContours();
185  outlines = m_pcbOutlines;
186  m_Pcb->ConvertBrdLayerToPolygonalContours( pcb_layer, holes );
187 
190  ExportVrmlPolygonSet( vrmllayer, outlines );
191 
192  pcb_layer = B_Mask;
193  vrmllayer = &m_bot_soldermask;
194  }
195 }
196 
197 
198 // Build and export the 4 layers F_Cu, B_Cu, F_silk, B_Silk
200 {
201  SHAPE_POLY_SET outlines;
202 
203  PCB_LAYER_ID pcb_layer[] =
204  {
206  };
207 
208  VRML_LAYER* vrmllayer[] =
209  {
211  nullptr // Sentinel
212  };
213 
214  for( int lcnt = 0; ; lcnt++ )
215  {
216  if( vrmllayer[lcnt] == nullptr )
217  break;
218 
219  outlines.RemoveAllContours();
220  m_Pcb->ConvertBrdLayerToPolygonalContours( pcb_layer[lcnt], outlines );
222 
223  ExportVrmlPolygonSet( vrmllayer[lcnt], outlines );
224  }
225 }
226 
227 // static var. for dealing with text
229 
230 
231 void EXPORTER_PCB_VRML::write_triangle_bag( std::ostream& aOut_file, const VRML_COLOR& aColor,
232  VRML_LAYER* aLayer, bool aPlane, bool aTop,
233  double aTop_z, double aBottom_z )
234 {
235  /* A lot of nodes are not required, but blender sometimes chokes
236  * without them */
237  static const char* shape_boiler[] =
238  {
239  "Transform {\n",
240  " children [\n",
241  " Group {\n",
242  " children [\n",
243  " Shape {\n",
244  " appearance Appearance {\n",
245  " material Material {\n",
246  0, // Material marker
247  " }\n",
248  " }\n",
249  " geometry IndexedFaceSet {\n",
250  " solid TRUE\n",
251  " coord Coordinate {\n",
252  " point [\n",
253  0, // Coordinates marker
254  " ]\n",
255  " }\n",
256  " coordIndex [\n",
257  0, // Index marker
258  " ]\n",
259  " }\n",
260  " }\n",
261  " ]\n",
262  " }\n",
263  " ]\n",
264  "}\n",
265  0 // End marker
266  };
267 
268  int marker_found = 0, lineno = 0;
269 
270  while( marker_found < 4 )
271  {
272  if( shape_boiler[lineno] )
273  aOut_file << shape_boiler[lineno];
274  else
275  {
276  marker_found++;
277 
278  switch( marker_found )
279  {
280  case 1: // Material marker
281  {
282  std::streamsize lastPrecision = aOut_file.precision();
283  aOut_file << " diffuseColor " << std::setprecision(3);
284  aOut_file << aColor.diffuse_red << " ";
285  aOut_file << aColor.diffuse_grn << " ";
286  aOut_file << aColor.diffuse_blu << "\n";
287 
288  aOut_file << " specularColor ";
289  aOut_file << aColor.spec_red << " ";
290  aOut_file << aColor.spec_grn << " ";
291  aOut_file << aColor.spec_blu << "\n";
292 
293  aOut_file << " emissiveColor ";
294  aOut_file << aColor.emit_red << " ";
295  aOut_file << aColor.emit_grn << " ";
296  aOut_file << aColor.emit_blu << "\n";
297 
298  aOut_file << " ambientIntensity " << aColor.ambient << "\n";
299  aOut_file << " transparency " << aColor.transp << "\n";
300  aOut_file << " shininess " << aColor.shiny << "\n";
301  aOut_file.precision( lastPrecision );
302  }
303  break;
304 
305  case 2:
306 
307  if( aPlane )
308  aLayer->WriteVertices( aTop_z, aOut_file, m_precision );
309  else
310  aLayer->Write3DVertices( aTop_z, aBottom_z, aOut_file, m_precision );
311 
312  aOut_file << "\n";
313  break;
314 
315  case 3:
316 
317  if( aPlane )
318  aLayer->WriteIndices( aTop, aOut_file );
319  else
320  aLayer->Write3DIndices( aOut_file );
321 
322  aOut_file << "\n";
323  break;
324 
325  default:
326  break;
327  }
328  }
329 
330  lineno++;
331  }
332 }
333 
334 
335 void EXPORTER_PCB_VRML::writeLayers( const char* aFileName,
336  OSTREAM* aOutputFile )
337 {
338  // VRML_LAYER board;
339  m_3D_board.Tesselate( &m_holes );
340  double brdz = m_brd_thickness / 2.0
342 
344  {
345  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PCB ),
346  &m_3D_board, false, false, brdz, -brdz );
347  }
348  else
349  {
351  }
352 
353  // VRML_LAYER m_top_copper;
354  m_top_copper.Tesselate( &m_holes );
355 
357  {
359  &m_top_copper, true, true, GetLayerZ( F_Cu ), 0 );
360  }
361  else
362  {
364  GetLayerZ( F_Cu ), true );
365  }
366 
367  // VRML_LAYER m_top_paste;
368  m_top_paste.Tesselate( &m_holes );
369 
371  {
372  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PASTE ),
373  &m_top_paste, true, true,
375  0 );
376  }
377  else
378  {
381  true );
382  }
383 
384  // VRML_LAYER m_top_soldermask;
385  m_top_soldermask.Tesselate( &m_holes );
386 
388  {
390  &m_top_soldermask, true, true,
392  0 );
393  }
394  else
395  {
398  true );
399  }
400 
401  // VRML_LAYER m_bot_copper;
402  m_bot_copper.Tesselate( &m_holes );
403 
405  {
407  &m_bot_copper, true, false, GetLayerZ( B_Cu ), 0 );
408  }
409  else
410  {
412  GetLayerZ( B_Cu ), false );
413  }
414 
415  // VRML_LAYER m_bot_paste;
416  m_bot_paste.Tesselate( &m_holes );
417 
419  {
420  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PASTE ),
421  &m_bot_paste, true, false,
422  GetLayerZ( B_Cu )
424  0 );
425  }
426  else
427  {
430  false );
431  }
432 
433  // VRML_LAYER m_bot_mask:
434  m_bot_soldermask.Tesselate( &m_holes );
435 
437  {
439  &m_bot_soldermask, true, false,
441  0 );
442  }
443  else
444  {
447  false );
448  }
449 
450  // VRML_LAYER PTH;
451  m_plated_holes.Tesselate( NULL, true );
452 
454  {
455  write_triangle_bag( *aOutputFile, GetColor( VRML_COLOR_PASTE ),
456  &m_plated_holes, false, false,
459  }
460  else
461  {
465  }
466 
467  // VRML_LAYER m_top_silk;
468  m_top_silk.Tesselate( &m_holes );
469 
471  {
473  true, true, GetLayerZ( F_SilkS ), 0 );
474  }
475  else
476  {
478  GetLayerZ( F_SilkS ), true );
479  }
480 
481  // VRML_LAYER m_bot_silk;
482  m_bot_silk.Tesselate( &m_holes );
483 
485  {
487  true, false, GetLayerZ( B_SilkS ), 0 );
488  }
489  else
490  {
492  GetLayerZ( B_SilkS ), false );
493  }
494 
496  S3D::WriteVRML( aFileName, true, m_OutputPCB.GetRawPtr(), true, true );
497 }
498 
499 
501 {
502  int copper_layers = m_Pcb->GetCopperLayerCount();
503 
504  // We call it 'layer' thickness, but it's the whole board thickness!
506  double half_thickness = m_brd_thickness / 2;
507 
508  // Compute each layer's Z value, more or less like the 3d view
509  for( LSEQ seq = LSET::AllCuMask().Seq(); seq; ++seq )
510  {
511  PCB_LAYER_ID i = *seq;
512 
513  if( i < copper_layers )
514  SetLayerZ( i, half_thickness - m_brd_thickness * i / (copper_layers - 1) );
515  else
516  SetLayerZ( i, - half_thickness ); // bottom layer
517  }
518 
519  // To avoid rounding interference, we apply an epsilon to each successive layer
520  double epsilon_z = Millimeter2iu( ART_OFFSET ) * m_BoardToVrmlScale;
521  SetLayerZ( B_Paste, -half_thickness - epsilon_z );
522  SetLayerZ( B_Adhes, -half_thickness - epsilon_z );
523  SetLayerZ( B_SilkS, -half_thickness - epsilon_z * 3 );
524  SetLayerZ( B_Mask, -half_thickness - epsilon_z * 2 );
525  SetLayerZ( F_Mask, half_thickness + epsilon_z * 2 );
526  SetLayerZ( F_SilkS, half_thickness + epsilon_z * 3 );
527  SetLayerZ( F_Adhes, half_thickness + epsilon_z );
528  SetLayerZ( F_Paste, half_thickness + epsilon_z );
529  SetLayerZ( Dwgs_User, half_thickness + epsilon_z * 5 );
530  SetLayerZ( Cmts_User, half_thickness + epsilon_z * 6 );
531  SetLayerZ( Eco1_User, half_thickness + epsilon_z * 7 );
532  SetLayerZ( Eco2_User, half_thickness + epsilon_z * 8 );
533  SetLayerZ( Edge_Cuts, 0 );
534 }
535 
536 
537 void EXPORTER_PCB_VRML::ExportVrmlPolygonSet( VRML_LAYER* aVlayer, const SHAPE_POLY_SET& aOutlines )
538 {
539  // Polygons in SHAPE_POLY_SET must be without hole, i.e. holes must be linked
540  // previously to their main outline.
541  for( int icnt = 0; icnt < aOutlines.OutlineCount(); icnt++ )
542  {
543  const SHAPE_LINE_CHAIN& outline = aOutlines.COutline( icnt );
544 
545  int seg = aVlayer->NewContour();
546 
547  for( int jj = 0; jj < outline.PointCount(); jj++ )
548  {
549  if( !aVlayer->AddVertex( seg, outline.CPoint( jj ).x * m_BoardToVrmlScale,
550  -outline.CPoint( jj ).y * m_BoardToVrmlScale ) )
551  throw( std::runtime_error( aVlayer->GetError() ) );
552  }
553 
554  aVlayer->EnsureWinding( seg, false );
555  }
556 }
557 
558 
559 // board edges and cutouts
561 {
563  {
564  wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
565  }
566 
567  int seg;
568 
569  for( int cnt = 0; cnt < m_pcbOutlines.OutlineCount(); cnt++ )
570  {
571  const SHAPE_LINE_CHAIN& outline = m_pcbOutlines.COutline( cnt );
572 
573  seg = m_3D_board.NewContour();
574 
575  for( int j = 0; j < outline.PointCount(); j++ )
576  {
577  m_3D_board.AddVertex( seg, (double)outline.CPoint(j).x * m_BoardToVrmlScale,
578  -((double)outline.CPoint(j).y * m_BoardToVrmlScale ) );
579 
580  }
581 
582  m_3D_board.EnsureWinding( seg, false );
583 
584  // Generate board holes from outlines:
585  for( int ii = 0; ii < m_pcbOutlines.HoleCount( cnt ); ii++ )
586  {
587  const SHAPE_LINE_CHAIN& hole = m_pcbOutlines.Hole( cnt, ii );
588 
589  seg = m_holes.NewContour();
590 
591  if( seg < 0 )
592  {
593  wxLogError( _( "VRML Export Failed: Could not add holes to contours." ) );
594  return;
595  }
596 
597  for( int j = 0; j < hole.PointCount(); j++ )
598  {
599  m_holes.AddVertex( seg, (double) hole.CPoint(j).x * m_BoardToVrmlScale,
600  -( (double) hole.CPoint(j).y * m_BoardToVrmlScale ) );
601  }
602 
603  m_holes.EnsureWinding( seg, true );
604  }
605  }
606 }
607 
608 // Max error allowed to approximate a circle by segments, in mm
609 static const double err_approx_max = 0.005;
610 
612 {
613  PCB_LAYER_ID top_layer, bottom_layer;
614 
615  for( TRACK* track : m_Pcb->Tracks() )
616  {
617  if( track->Type() != PCB_VIA_T )
618  continue;
619 
620  const VIA* via = (const VIA*) track;
621 
622  via->LayerPair( &top_layer, &bottom_layer );
623 
624  // do not render a buried via
625  if( top_layer != F_Cu && bottom_layer != B_Cu )
626  return;
627 
628  // Export all via holes to m_holes
629  double hole_radius = via->GetDrillValue() * m_BoardToVrmlScale / 2.0;
630 
631  if( hole_radius <= 0 )
632  continue;
633 
634  double x = via->GetStart().x * m_BoardToVrmlScale;
635  double y = via->GetStart().y * m_BoardToVrmlScale;
636 
637  // Set the optimal number of segments to approximate a circle.
638  // SetArcParams needs a count max, and the minimal and maximal length
639  // of segments
640  int nsides = GetArcToSegmentCount( via->GetDrillValue(),
641  Millimeter2iu( err_approx_max ), 360.0 );
642  double minSegLength = M_PI * 2.0 * hole_radius / nsides;
643  double maxSegLength = minSegLength*2.0;
644 
645  m_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
646  m_plated_holes.SetArcParams( nsides, minSegLength, maxSegLength );
647 
648  m_holes.AddCircle( x, -y, hole_radius, true, true );
649  m_plated_holes.AddCircle( x, -y, hole_radius, true, false );
650 
651  m_holes.ResetArcParams();
652  m_plated_holes.ResetArcParams();
653  }
654 }
655 
656 
658 {
659  double hole_drill_w = (double) aPad->GetDrillSize().x * m_BoardToVrmlScale / 2.0;
660  double hole_drill_h = (double) aPad->GetDrillSize().y * m_BoardToVrmlScale / 2.0;
661  double hole_drill = std::min( hole_drill_w, hole_drill_h );
662  double hole_x = aPad->GetPosition().x * m_BoardToVrmlScale;
663  double hole_y = aPad->GetPosition().y * m_BoardToVrmlScale;
664 
665  // Export the hole on the edge layer
666  if( hole_drill > 0 )
667  {
668  int nsides = GetArcToSegmentCount( hole_drill,
669  Millimeter2iu( err_approx_max ), 360.0 );
670  double minSegLength = M_PI * hole_drill / nsides;
671  double maxSegLength = minSegLength*2.0;
672 
673  m_holes.SetArcParams( nsides*2, minSegLength, maxSegLength );
674  m_plated_holes.SetArcParams( nsides, minSegLength, maxSegLength );
675 
676  bool pth = false;
677 
678  if( ( aPad->GetAttribute() != PAD_ATTRIB::NPTH ) )
679  pth = true;
680 
681  if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
682  {
683  // Oblong hole (slot)
684 
685  if( pth )
686  {
687  m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0 + PLATE_OFFSET,
688  hole_drill_h * 2.0 + PLATE_OFFSET,
689  aPad->GetOrientation()/10.0, true, true );
690 
691  m_plated_holes.AddSlot( hole_x, -hole_y,
692  hole_drill_w * 2.0, hole_drill_h * 2.0,
693  aPad->GetOrientation()/10.0, true, false );
694  }
695  else
696  {
697  m_holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
698  aPad->GetOrientation()/10.0, true, false );
699 
700  }
701  }
702  else
703  {
704  // Drill a round hole
705  if( pth )
706  {
707  m_holes.AddCircle( hole_x, -hole_y, hole_drill + PLATE_OFFSET, true, true );
708  m_plated_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
709  }
710  else
711  {
712  m_holes.AddCircle( hole_x, -hole_y, hole_drill, true, false );
713  }
714 
715  }
716 
717  m_holes.ResetArcParams();
718  m_plated_holes.ResetArcParams();
719  }
720 }
721 
722 
723 // From axis/rot to quaternion
724 static void build_quat( double x, double y, double z, double a, double q[4] )
725 {
726  double sina = sin( a / 2 );
727 
728  q[0] = x * sina;
729  q[1] = y * sina;
730  q[2] = z * sina;
731  q[3] = cos( a / 2 );
732 }
733 
734 
735 // From quaternion to axis/rot
736 static void from_quat( double q[4], double rot[4] )
737 {
738  rot[3] = acos( q[3] ) * 2;
739 
740  for( int i = 0; i < 3; i++ )
741  rot[i] = q[i] / sin( rot[3] / 2 );
742 }
743 
744 
745 // Quaternion composition
746 static void compose_quat( double q1[4], double q2[4], double qr[4] )
747 {
748  double tmp[4];
749 
750  tmp[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1];
751  tmp[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2];
752  tmp[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0];
753  tmp[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2];
754 
755  qr[0] = tmp[0];
756  qr[1] = tmp[1];
757  qr[2] = tmp[2];
758  qr[3] = tmp[3];
759 }
760 
761 
763  std::ostream* aOutputFile )
764 {
765  // Export pad holes
766  for( PAD* pad : aFootprint->Pads() )
768 
769  bool isFlipped = aFootprint->GetLayer() == B_Cu;
770 
771  // Export the object VRML model(s)
772  auto sM = aFootprint->Models().begin();
773  auto eM = aFootprint->Models().end();
774 
775  wxFileName subdir( m_Subdir3DFpModels, "" );
776 
777  while( sM != eM )
778  {
779  SGNODE* mod3d = (SGNODE*) m_Cache3Dmodels->Load( sM->m_Filename );
780 
781  if( NULL == mod3d )
782  {
783  ++sM;
784  continue;
785  }
786 
787  /* Calculate 3D shape rotation:
788  * this is the rotation parameters, with an additional 180 deg rotation
789  * for footprints that are flipped
790  * When flipped, axis rotation is the horizontal axis (X axis)
791  */
792  double rotx = -sM->m_Rotation.x;
793  double roty = -sM->m_Rotation.y;
794  double rotz = -sM->m_Rotation.z;
795 
796  if( isFlipped )
797  {
798  rotx += 180.0;
799  roty = -roty;
800  rotz = -rotz;
801  }
802 
803  // Do some quaternion munching
804  double q1[4], q2[4], rot[4];
805  build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 );
806  build_quat( 0, 1, 0, DEG2RAD( roty ), q2 );
807  compose_quat( q1, q2, q1 );
808  build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 );
809  compose_quat( q1, q2, q1 );
810 
811  // Note here aFootprint->GetOrientation() is in 0.1 degrees, so footprint rotation
812  // has to be converted to radians
813  build_quat( 0, 0, 1, DECIDEG2RAD( aFootprint->GetOrientation() ), q2 );
814  compose_quat( q1, q2, q1 );
815  from_quat( q1, rot );
816 
817  double offsetFactor = 1000.0f * IU_PER_MILS / 25.4f;
818 
819  // adjust 3D shape local offset position
820  // they are given in mm, so they are converted in board IU.
821  double offsetx = sM->m_Offset.x * offsetFactor;
822  double offsety = sM->m_Offset.y * offsetFactor;
823  double offsetz = sM->m_Offset.z * offsetFactor;
824 
825  if( isFlipped )
826  offsetz = -offsetz;
827  else // In normal mode, Y axis is reversed in Pcbnew.
828  offsety = -offsety;
829 
830  RotatePoint( &offsetx, &offsety, aFootprint->GetOrientation() );
831 
832  SGPOINT trans;
833  trans.x = ( offsetx + aFootprint->GetPosition().x ) * m_BoardToVrmlScale + m_tx;
834  trans.y = -( offsety + aFootprint->GetPosition().y) * m_BoardToVrmlScale - m_ty;
835  trans.z = (offsetz * m_BoardToVrmlScale ) + GetLayerZ( aFootprint->GetLayer() );
836 
838  {
839  wxFileName srcFile = m_Cache3Dmodels->GetResolver()->ResolvePath( sM->m_Filename );
840  wxFileName dstFile;
841  dstFile.SetPath( m_Subdir3DFpModels );
842  dstFile.SetName( srcFile.GetName() );
843  dstFile.SetExt( "wrl" );
844 
845  // copy the file if necessary
846  wxDateTime srcModTime = srcFile.GetModificationTime();
847  wxDateTime destModTime = srcModTime;
848 
849  destModTime.SetToCurrent();
850 
851  if( dstFile.FileExists() )
852  destModTime = dstFile.GetModificationTime();
853 
854  if( srcModTime != destModTime )
855  {
856  wxString fileExt = srcFile.GetExt();
857  fileExt.LowerCase();
858 
859  // copy VRML models and use the scenegraph library to
860  // translate other model types
861  if( fileExt == "wrl" )
862  {
863  if( !wxCopyFile( srcFile.GetFullPath(), dstFile.GetFullPath() ) )
864  continue;
865  }
866  else
867  {
868  if( !S3D::WriteVRML( dstFile.GetFullPath().ToUTF8(), true, mod3d, m_ReuseDef, true ) )
869  continue;
870  }
871  }
872 
873  (*aOutputFile) << "Transform {\n";
874 
875  // only write a rotation if it is >= 0.1 deg
876  if( std::abs( rot[3] ) > 0.0001745 )
877  {
878  (*aOutputFile) << " rotation " << std::setprecision( 5 );
879  (*aOutputFile) << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
880  }
881 
882  (*aOutputFile) << " translation " << std::setprecision( m_precision );
883  (*aOutputFile) << trans.x << " ";
884  (*aOutputFile) << trans.y << " ";
885  (*aOutputFile) << trans.z << "\n";
886 
887  (*aOutputFile) << " scale ";
888  (*aOutputFile) << sM->m_Scale.x << " ";
889  (*aOutputFile) << sM->m_Scale.y << " ";
890  (*aOutputFile) << sM->m_Scale.z << "\n";
891 
892  (*aOutputFile) << " children [\n Inline {\n url \"";
893 
895  {
896  wxFileName tmp = dstFile;
897  tmp.SetExt( "" );
898  tmp.SetName( "" );
899  tmp.RemoveLastDir();
900  dstFile.MakeRelativeTo( tmp.GetPath() );
901  }
902 
903  wxString fn = dstFile.GetFullPath();
904  fn.Replace( "\\", "/" );
905  (*aOutputFile) << TO_UTF8( fn ) << "\"\n } ]\n";
906  (*aOutputFile) << " }\n";
907  }
908  else
909  {
910  IFSG_TRANSFORM* modelShape = new IFSG_TRANSFORM( m_OutputPCB.GetRawPtr() );
911 
912  // only write a rotation if it is >= 0.1 deg
913  if( std::abs( rot[3] ) > 0.0001745 )
914  modelShape->SetRotation( SGVECTOR( rot[0], rot[1], rot[2] ), rot[3] );
915 
916  modelShape->SetTranslation( trans );
917  modelShape->SetScale( SGPOINT( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ) );
918 
919  if( NULL == S3D::GetSGNodeParent( mod3d ) )
920  {
921  m_components.push_back( mod3d );
922  modelShape->AddChildNode( mod3d );
923  }
924  else
925  {
926  modelShape->AddRefNode( mod3d );
927  }
928 
929  }
930 
931  ++sM;
932  }
933 }
934 
935 
936 bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMtoWRMLunit,
937  bool aExport3DFiles, bool aUseRelativePaths,
938  const wxString& a3D_Subdir,
939  double aXRef, double aYRef )
940 {
941  BOARD* pcb = GetBoard();
942  bool success = true;
943 
944  EXPORTER_PCB_VRML model3d;
945  model_vrml = &model3d;
946  model3d.m_Pcb = GetBoard();
947  model3d.SetScale( aMMtoWRMLunit );
948  model3d.m_UseInlineModelsInBrdfile = aExport3DFiles;
949  model3d.m_Subdir3DFpModels = a3D_Subdir;
950  model3d.m_UseRelPathIn3DModelFilename = aUseRelativePaths;
951  model3d.m_Cache3Dmodels = Prj().Get3DCacheManager();
952 
953  if( model3d.m_UseInlineModelsInBrdfile )
954  {
955  model3d.m_BoardToVrmlScale = MM_PER_IU / 2.54;
956  model3d.SetOffset( -aXRef / 2.54, aYRef / 2.54 );
957  }
958  else
959  {
960  model3d.m_BoardToVrmlScale = MM_PER_IU;
961  model3d.SetOffset( -aXRef, aYRef );
962  }
963 
964  try
965  {
966  // Preliminary computation: the z value for each layer
967  model3d.ComputeLayer3D_Zpos();
968 
969  // board edges and cutouts
970  model3d.ExportVrmlBoard();
971 
972  // Draw solder mask layer (negative layer)
973  model3d.ExportVrmlSolderMask();
974 #if 1
975  model3d.ExportVrmlViaHoles();
976  model3d.ExportStandardLayers();
977 #else
978  // Drawing and text on the board
979  model3d.ExportVrmlDrawings();
980 
981  // Export vias and trackage
982  model3d.ExportVrmlTracks();
983 
984  // Export zone fills
985  model3d.ExportVrmlZones();
986 #endif
987  if( model3d.m_UseInlineModelsInBrdfile )
988  {
989  // Copy fp 3D models in a folder, and link these files in
990  // the board .vrml file
991  model3d.ExportFp3DModelsAsLinkedFile( aFullFileName );
992  }
993  else
994  {
995  // merge footprints in the .vrml board file
996  for( FOOTPRINT* footprint : pcb->Footprints() )
997  model3d.ExportVrmlFootprint( footprint, NULL );
998 
999  // write out the board and all layers
1000  model3d.writeLayers( TO_UTF8( aFullFileName ), NULL );
1001  }
1002  }
1003  catch( const std::exception& e )
1004  {
1005  wxString msg;
1006  msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
1007  wxMessageBox( msg );
1008 
1009  success = false;
1010  }
1011 
1012  return success;
1013 }
1014 
1015 void EXPORTER_PCB_VRML::ExportFp3DModelsAsLinkedFile( const wxString& aFullFileName )
1016 {
1017  // check if the 3D Subdir exists - create if not
1018  wxFileName subdir( m_Subdir3DFpModels, "" );
1019 
1020  if( ! subdir.DirExists() )
1021  {
1022  if( !wxDir::Make( subdir.GetFullPath() ) )
1023  throw( std::runtime_error( "Could not create 3D model subdirectory" ) );
1024  }
1025 
1026  OPEN_OSTREAM( output_file, TO_UTF8( aFullFileName ) );
1027 
1028  if( output_file.fail() )
1029  {
1030  std::ostringstream ostr;
1031  ostr << "Could not open file '" << TO_UTF8( aFullFileName ) << "'";
1032  throw( std::runtime_error( ostr.str().c_str() ) );
1033  }
1034 
1035  output_file.imbue( std::locale::classic() );
1036 
1037  // Begin with the usual VRML boilerplate
1038  wxString fn = aFullFileName;
1039  fn.Replace( "\\" , "/" );
1040  output_file << "#VRML V2.0 utf8\n";
1041  output_file << "WorldInfo {\n";
1042  output_file << " title \"" << TO_UTF8( fn ) << " - Generated by Pcbnew\"\n";
1043  output_file << "}\n";
1044  output_file << "Transform {\n";
1045  output_file << " scale " << std::setprecision( m_precision );
1046  output_file << m_WorldScale << " ";
1047  output_file << m_WorldScale << " ";
1048  output_file << m_WorldScale << "\n";
1049  output_file << " children [\n";
1050 
1051  // Export footprints
1052  for( FOOTPRINT* footprint : m_Pcb->Footprints() )
1053  ExportVrmlFootprint( footprint, &output_file );
1054 
1055  // write out the board and all layers
1056  writeLayers( TO_UTF8( aFullFileName ), &output_file );
1057 
1058  // Close the outer 'transform' node
1059  output_file << "]\n}\n";
1060 
1061  CLOSE_STREAM( output_file );
1062 }
1063 
1065 {
1066  if( colorIdx == -1 )
1067  colorIdx = VRML_COLOR_PCB;
1068  else if( colorIdx == VRML_COLOR_LAST )
1069  return NULL;
1070 
1071  if( m_sgmaterial[colorIdx] )
1072  return m_sgmaterial[colorIdx];
1073 
1074  IFSG_APPEARANCE vcolor( (SGNODE*) NULL );
1075  VRML_COLOR* cp = &vrml_colors_list[colorIdx];
1076 
1077  vcolor.SetSpecular( cp->spec_red, cp->spec_grn, cp->spec_blu );
1078  vcolor.SetDiffuse( cp->diffuse_red, cp->diffuse_grn, cp->diffuse_blu );
1079  vcolor.SetShininess( cp->shiny );
1080  // NOTE: XXX - replace with a better equation; using this definition
1081  // of ambient will not yield the best results
1082  vcolor.SetAmbient( cp->ambient, cp->ambient, cp->ambient );
1083  vcolor.SetTransparency( cp->transp );
1084 
1085  m_sgmaterial[colorIdx] = vcolor.GetRawPtr();
1086 
1087  return m_sgmaterial[colorIdx];
1088 }
1089 
1090 
1092  VRML_LAYER* layer, double top_z, bool aTopPlane )
1093 {
1094  std::vector< double > vertices;
1095  std::vector< int > idxPlane;
1096 
1097  if( !( *layer ).Get2DTriangles( vertices, idxPlane, top_z, aTopPlane ) )
1098  {
1099  return;
1100  }
1101 
1102  if( ( idxPlane.size() % 3 ) )
1103  {
1104  throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a triangle list)" ) );
1105  }
1106 
1107  std::vector< SGPOINT > vlist;
1108  size_t nvert = vertices.size() / 3;
1109  size_t j = 0;
1110 
1111  for( size_t i = 0; i < nvert; ++i, j+= 3 )
1112  vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
1113 
1114  // create the intermediate scenegraph
1115  IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1116  IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1117  IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1118  IFSG_COORDS cp( face ); // coordinates for all faces
1119  cp.SetCoordsList( nvert, &vlist[0] );
1120  IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1121  coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1122  IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1123 
1124  // set the normals
1125  if( aTopPlane )
1126  {
1127  for( size_t i = 0; i < nvert; ++i )
1128  norms.AddNormal( 0.0, 0.0, 1.0 );
1129  }
1130  else
1131  {
1132  for( size_t i = 0; i < nvert; ++i )
1133  norms.AddNormal( 0.0, 0.0, -1.0 );
1134  }
1135 
1136  // assign a color from the palette
1137  SGNODE* modelColor = getSGColor( colorID );
1138 
1139  if( NULL != modelColor )
1140  {
1141  if( NULL == S3D::GetSGNodeParent( modelColor ) )
1142  shape.AddChildNode( modelColor );
1143  else
1144  shape.AddRefNode( modelColor );
1145  }
1146 }
1147 
1148 
1150  VRML_LAYER* layer, double top_z, double bottom_z )
1151 {
1152  std::vector< double > vertices;
1153  std::vector< int > idxPlane;
1154  std::vector< int > idxSide;
1155 
1156  if( top_z < bottom_z )
1157  {
1158  double tmp = top_z;
1159  top_z = bottom_z;
1160  bottom_z = tmp;
1161  }
1162 
1163  if( !( *layer ).Get3DTriangles( vertices, idxPlane, idxSide, top_z, bottom_z )
1164  || idxPlane.empty() || idxSide.empty() )
1165  {
1166  return;
1167  }
1168 
1169  if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
1170  {
1171  throw( std::runtime_error( "[BUG] index lists are not a multiple of 3 (not a "
1172  "triangle list)" ) );
1173  }
1174 
1175  std::vector< SGPOINT > vlist;
1176  size_t nvert = vertices.size() / 3;
1177  size_t j = 0;
1178 
1179  for( size_t i = 0; i < nvert; ++i, j+= 3 )
1180  vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
1181 
1182  // create the intermediate scenegraph
1183  IFSG_TRANSFORM tx0( PcbOutput.GetRawPtr() ); // tx0 = Transform for this outline
1184  IFSG_SHAPE shape( tx0 ); // shape will hold (a) all vertices and (b) a local list of normals
1185  IFSG_FACESET face( shape ); // this face shall represent the top and bottom planes
1186  IFSG_COORDS cp( face ); // coordinates for all faces
1187  cp.SetCoordsList( nvert, &vlist[0] );
1188  IFSG_COORDINDEX coordIdx( face ); // coordinate indices for top and bottom planes only
1189  coordIdx.SetIndices( idxPlane.size(), &idxPlane[0] );
1190  IFSG_NORMALS norms( face ); // normals for the top and bottom planes
1191 
1192  // number of TOP (and bottom) vertices
1193  j = nvert / 2;
1194 
1195  // set the TOP normals
1196  for( size_t i = 0; i < j; ++i )
1197  norms.AddNormal( 0.0, 0.0, 1.0 );
1198 
1199  // set the BOTTOM normals
1200  for( size_t i = 0; i < j; ++i )
1201  norms.AddNormal( 0.0, 0.0, -1.0 );
1202 
1203  // assign a color from the palette
1204  SGNODE* modelColor = getSGColor( colorID );
1205 
1206  if( NULL != modelColor )
1207  {
1208  if( NULL == S3D::GetSGNodeParent( modelColor ) )
1209  shape.AddChildNode( modelColor );
1210  else
1211  shape.AddRefNode( modelColor );
1212  }
1213 
1214  // create a second shape describing the vertical walls of the extrusion
1215  // using per-vertex-per-face-normals
1216  shape.NewNode( tx0 );
1217  shape.AddRefNode( modelColor ); // set the color to be the same as the top/bottom
1218  face.NewNode( shape );
1219  cp.NewNode( face ); // new vertex list
1220  norms.NewNode( face ); // new normals list
1221  coordIdx.NewNode( face ); // new index list
1222 
1223  // populate the new per-face vertex list and its indices and normals
1224  std::vector< int >::iterator sI = idxSide.begin();
1225  std::vector< int >::iterator eI = idxSide.end();
1226 
1227  size_t sidx = 0; // index to the new coord set
1228  SGPOINT p1, p2, p3;
1229  SGVECTOR vnorm;
1230 
1231  while( sI != eI )
1232  {
1233  p1 = vlist[*sI];
1234  cp.AddCoord( p1 );
1235  ++sI;
1236 
1237  p2 = vlist[*sI];
1238  cp.AddCoord( p2 );
1239  ++sI;
1240 
1241  p3 = vlist[*sI];
1242  cp.AddCoord( p3 );
1243  ++sI;
1244 
1245  vnorm.SetVector( S3D::CalcTriNorm( p1, p2, p3 ) );
1246  norms.AddNormal( vnorm );
1247  norms.AddNormal( vnorm );
1248  norms.AddNormal( vnorm );
1249 
1250  coordIdx.AddIndex( (int)sidx );
1251  ++sidx;
1252  coordIdx.AddIndex( (int)sidx );
1253  ++sidx;
1254  coordIdx.AddIndex( (int)sidx );
1255  ++sidx;
1256  }
1257 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
#define PLATE_OFFSET
Definition: exporter_vrml.h:29
Definition: track.h:343
void ExportVrmlFootprint(FOOTPRINT *aFootprint, std::ostream *aOutputFile)
IFSG_TRANSFORM m_OutputPCB
VRML_LAYER m_plated_holes
double GetLayerZ(LAYER_NUM aLayer)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
double x
Definition: sg_base.h:70
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:178
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:1925
static void compose_quat(double q1[4], double q2[4], double qr[4])
std::list< SGNODE * > m_components
IFSG_COORDS is the wrapper for SGCOORDS.
Definition: ifsg_coords.h:40
void ExportVrmlPadHole(PAD *aPad)
IFSG_COORDINDEX is the wrapper for SGCOORDINDEX.
void ConvertBrdLayerToPolygonalContours(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aOutlines) const
Build a set of polygons which are the outlines of copper items (pads, tracks, vias,...
bool SetTransparency(float aTransparency) noexcept
bool Attach(SGNODE *aNode) override
Function Attach associates a given SGNODE* with this wrapper.
float transp
Definition: exporter_vrml.h:59
VRML_COLOR vrml_colors_list[VRML_COLOR_LAST]
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
VRML_COLOR_INDEX
Definition: exporter_vrml.h:32
#define OSTREAM
void SetLayerZ(LAYER_NUM aLayer, double aValue)
double GetOrientation() const
Definition: footprint.h:186
bool AddRefNode(SGNODE *aNode)
Function AddRefNode adds a reference to an existing node which is not owned by (not a child of) this ...
Definition: ifsg_node.cpp:128
void SetVector(double aXVal, double aYVal, double aZVal)
Definition: sg_base.cpp:233
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:593
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition: ifsg_api.cpp:492
SGNODE * getSGColor(VRML_COLOR_INDEX colorIdx)
bool SetParent(SGNODE *aParent)
Function SetParent sets the parent SGNODE of this object.
Definition: ifsg_node.cpp:87
bool ExportVRML_File(const wxString &aFullFileName, double aMMtoWRMLunit, bool aExport3DFiles, bool aUseRelativePaths, const wxString &a3D_Subdir, double aXRef, double aYRef)
Create the file(s) exporting current BOARD to a VRML file.
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
The base class of all Scene Graph nodes.
Definition: sg_node.h:74
collects header files for all SG* wrappers and the API
float spec_blu
Definition: exporter_vrml.h:52
defines the basic data associated with a single 3D model.
SGNODE * GetRawPtr(void) noexcept
Function GetRawPtr() returns the raw internal SGNODE pointer.
Definition: ifsg_node.cpp:65
VRML_LAYER m_top_silk
int PointCount() const
Function PointCount()
VRML_LAYER m_top_copper
bool AddChildNode(SGNODE *aNode)
Function AddChildNode adds a node as a child owned by this node.
Definition: ifsg_node.cpp:148
void ExportFp3DModelsAsLinkedFile(const wxString &aFullFileName)
VRML_LAYER m_bot_silk
VRML_LAYER m_bot_soldermask
static const double err_approx_max
PADS & Pads()
Definition: footprint.h:164
bool AddIndex(int aIndex)
Function AddIndex adds a single index to the list.
Definition: ifsg_index.cpp:57
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Function WriteVRML writes out the given node and its subnodes to a VRML2 file.
Definition: ifsg_api.cpp:76
This file contains miscellaneous commonly used macros and functions.
void write_triangle_bag(std::ostream &aOut_file, const VRML_COLOR &aColor, VRML_LAYER *aLayer, bool aPlane, bool aTop, double aTop_z, double aBottom_z)
void writeLayers(const char *aFileName, OSTREAM *aOutputFile)
static void build_quat(double x, double y, double z, double a, double q[4])
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
bool AddNormal(double aXValue, double aYValue, double aZValue)
const VECTOR2I & CPoint(int aIndex) const
Function Point()
float ambient
Definition: exporter_vrml.h:58
float diffuse_grn
Definition: exporter_vrml.h:47
const wxSize & GetDrillSize() const
Definition: pad.h:242
like PAD_PTH, but not plated
PCB_LAYER_ID
A quick note on layer IDs:
wxString m_Subdir3DFpModels
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:148
VRML_LAYER m_bot_copper
IFSG_NORMALS is the wrapper for the SGNORMALS class.
Definition: ifsg_normals.h:40
#define NULL
bool SetAmbient(float aRVal, float aGVal, float aBVal)
double GetOrientation() const
Return the rotation angle of the pad in a variety of units (the basic call returns tenths of degrees)...
Definition: pad.h:349
bool m_UseRelPathIn3DModelFilename
Represent a set of closed polygons.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
void ComputeLayer3D_Zpos()
FOOTPRINTS & Footprints()
Definition: board.h:305
VRML_LAYER m_top_soldermask
VRML_COLOR & GetColor(VRML_COLOR_INDEX aIndex)
#define OPEN_OSTREAM(var, name)
bool GetLayer3D(LAYER_NUM layer, VRML_LAYER **vlayer)
float spec_red
Definition: exporter_vrml.h:50
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
void create_vrml_shell(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double top_z, double bottom_z)
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
a few functions useful in geometry calculations.
void create_vrml_plane(IFSG_TRANSFORM &PcbOutput, VRML_COLOR_INDEX colorID, VRML_LAYER *layer, double aHeight, bool aTopPlane)
SGNODE * m_sgmaterial[VRML_COLOR_LAST]
bool SetShininess(float aShininess) noexcept
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
float spec_grn
Definition: exporter_vrml.h:51
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
static EXPORTER_PCB_VRML * model_vrml
int LAYER_NUM
This can be replaced with int and removed.
float emit_grn
Definition: exporter_vrml.h:55
void ExportVrmlSolderMask()
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
wxString ResolvePath(const wxString &aFileName)
Determines the full path of the given file name.
bool SetIndices(size_t nIndices, int *aIndexList)
Function SetIndices sets the number of indices and creates a copy of the given index data.
Definition: ifsg_index.cpp:47
bool SetScale(double aWorldScale)
SGLIB_API SGVECTOR CalcTriNorm(const SGPOINT &p1, const SGPOINT &p2, const SGPOINT &p3)
Function CalcTriNorm returns the normal vector of a triangle described by vertices p1,...
Definition: ifsg_api.cpp:462
SHAPE_POLY_SET m_pcbOutlines
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
float emit_blu
Definition: exporter_vrml.h:56
double DEG2RAD(double deg)
Definition: trigo.h:231
wxPoint GetPosition() const override
Definition: pad.h:177
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: pad.h:354
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
float diffuse_red
Definition: exporter_vrml.h:46
SHAPE_LINE_CHAIN.
PAD_ATTRIB GetAttribute() const
Definition: pad.h:371
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
IFSG_FACESET is the wrapper for the SGFACESET class.
Definition: ifsg_faceset.h:40
FILENAME_RESOLVER * GetResolver() noexcept
Definition: 3d_cache.cpp:608
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
IFSG_TRANSFORM is the wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH.
int GetCopperLayerCount() const
Definition: board.cpp:435
double DECIDEG2RAD(double deg)
Definition: trigo.h:235
static void from_quat(double q[4], double rot[4])
S3D_CACHE * m_Cache3Dmodels
#define IU_PER_MILS
Definition: plotter.cpp:137
bool SetTranslation(const SGPOINT &aTranslation) noexcept
wxPoint GetPosition() const override
Definition: footprint.h:182
bool SetScale(const SGPOINT &aScale) noexcept
void SetOffset(double aXoff, double aYoff)
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
VRML_LAYER m_bot_paste
VRML_LAYER m_holes
void Destroy(void)
Function Destroy deletes the object held by this wrapper.
Definition: ifsg_node.cpp:55
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
#define CLOSE_STREAM(var)
SCENEGRAPH * Load(const wxString &aModelFile)
Attempt to load the scene data for a model.
Definition: 3d_cache.cpp:283
BOARD * GetBoard() const
void ExportStandardLayers()
VRML_LAYER m_top_paste
Definition: pad.h:60
static constexpr int Millimeter2iu(double mm)
VRML_LAYER m_3D_board
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:173
float emit_red
Definition: exporter_vrml.h:54
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
bool AddCoord(double aXValue, double aYValue, double aZValue)
float diffuse_blu
Definition: exporter_vrml.h:48
TRACKS & Tracks()
Definition: board.h:302
#define ART_OFFSET
Definition: exporter_vrml.h:27
Definition: track.h:83
bool m_UseInlineModelsInBrdfile
void ExportVrmlPolygonSet(VRML_LAYER *aVlayer, const SHAPE_POLY_SET &aOutlines)
IFSG_SHAPE is the wrapper for the SGSHAPE class.
Definition: ifsg_shape.h:40
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
double m_layer_z[PCB_LAYER_ID_COUNT]