bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMtoWRMLunit, bool aExport3DFiles, const wxString& a3D_Subdir ) { wxString msg; BOARD* pcb = GetBoard(); bool ok = true; MODEL_VRML model3d; model_vrml = &model3d; std::ofstream output_file; try { output_file.exceptions( std::ofstream::failbit ); output_file.open( TO_UTF8( aFullFileName ), std::ios_base::out ); // Switch the locale to standard C (needed to print floating point numbers like 1.3) SetLocaleTo_C_standard(); // Begin with the usual VRML boilerplate wxString name = aFullFileName; name.Replace( wxT( "\\" ), wxT( "/" ) ); ChangeIllegalCharacters( name, false ); output_file << "#VRML V2.0 utf8\n"; output_file << "WorldInfo {\n"; output_file << " title \"" << TO_UTF8( name ) << " - Generated by Pcbnew\"\n"; output_file << "}\n"; // Set the VRML world scale factor model3d.SetScale( aMMtoWRMLunit ); output_file << "Transform {\n"; // compute the offset to center the board on (0, 0, 0) // XXX - NOTE: we should allow the user a GUI option to specify the offset EDA_RECT bbbox = pcb->ComputeBoundingBox(); model3d.SetOffset( -model3d.scale * bbbox.Centre().x, model3d.scale * bbbox.Centre().y ); output_file << " children [\n"; // Preliminary computation: the z value for each layer compute_layer_Zs( model3d, pcb ); // board edges and cutouts export_vrml_board( model3d, pcb ); // Drawing and text on the board export_vrml_drawings( model3d, pcb ); // Export vias and trackage export_vrml_tracks( model3d, pcb ); // Export zone fills export_vrml_zones( model3d, pcb); /* scaling factor to convert 3D models to board units (decimils) * Usually we use Wings3D to create thems. * One can consider the 3D units is 0.1 inch (2.54 mm) * So the scaling factor from 0.1 inch to board units * is 2.54 * aMMtoWRMLunit */ double wrml_3D_models_scaling_factor = 2.54 * aMMtoWRMLunit; // Export footprints for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() ) export_vrml_module( model3d, pcb, module, output_file, wrml_3D_models_scaling_factor, aExport3DFiles, a3D_Subdir ); // write out the board and all layers write_layers( model3d, output_file, pcb ); // Close the outer 'transform' node output_file << "]\n}\n"; } catch( const std::exception& e ) { wxString msg; msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() ); wxMessageBox( msg ); ok = false; } // End of work output_file.exceptions( std::ios_base::goodbit ); output_file.close(); SetLocaleTo_Default(); // revert to the current locale return ok; }
/* Note1: * When copying 3D shapes files, the new filename is build from * the full path name, changing the separators by underscore. * this is needed because files with the same shortname can exist in different directories * Note 2: * ExportVRML_File generates coordinates in board units (BIU) inside the file. * (TODO: use mm inside the file) * A general scale transform is applied to the whole file * (1.0 to have the actual WRML unit im mm, 0.001 to have the actual WRML unit im meter * Note 3: * For 3D models built by a 3D modeler, the unit is 0,1 inch * A specfic scale is applied to 3D models to convert them to BIU * */ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString & aFullFileName, double aMMtoWRMLunit, bool aExport3DFiles, const wxString & a3D_Subdir ) { wxString msg; FILE* output_file; BOARD* pcb = GetBoard(); output_file = wxFopen( aFullFileName, wxT( "wt" ) ); if( output_file == NULL ) return false; // Switch the locale to standard C (needed to print floating point numbers like 1.3) SetLocaleTo_C_standard(); // Begin with the usual VRML boilerplate wxString name = aFullFileName; name.Replace(wxT("\\"), wxT("/" ) ); ChangeIllegalCharacters( name, false ); fprintf( output_file, "#VRML V2.0 utf8\n" "WorldInfo {\n" " title \"%s - Generated by Pcbnew\"\n" "}\n", TO_UTF8( name ) ); /* The would be in BIU and not in meters, as the standard wants. * It is trivial to embed everything in a transform node to * fix it. For example here we build the world in inches... */ // Global VRML scale to export to a different scale. // (aMMtoWRMLScale = 1.0 to export in mm) double boardIU2WRML = aMMtoWRMLunit / MM_PER_IU; fprintf( output_file, "Transform {\n" ); /* Define the translation to have the board centre to the 2D axis origin * more easy for rotations... */ EDA_RECT bbbox = pcb->ComputeBoundingBox(); double dx = boardIU2WRML * bbbox.Centre().x; double dy = boardIU2WRML * bbbox.Centre().y; fprintf( output_file, " translation %g %g 0.0\n", -dx, dy ); fprintf( output_file, " children [\n" ); // Preliminary computation: the z value for each layer compute_layer_Zs( pcb ); // Drawing and text on the board, and edges which are special export_vrml_drawings( pcb ); // Export vias and trackage export_vrml_tracks( pcb ); // Export zone fills /* TODO export_vrml_zones(pcb); */ /* scaling factor to convert 3D models to board units (decimils) * Usually we use Wings3D to create thems. * One can consider the 3D units is 0.1 inch (2.54 mm) * So the scaling factor from 0.1 inch to board units * is 2.54 * aMMtoWRMLunit */ double wrml_3D_models_scaling_factor = 2.54 * aMMtoWRMLunit; // Export footprints for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() ) export_vrml_module( pcb, module, output_file, wrml_3D_models_scaling_factor, aExport3DFiles, a3D_Subdir, boardIU2WRML ); /* Output the bagged triangles for each layer * Each layer will be a separate shape */ for( int layer = 0; layer < LAYER_COUNT; layer++ ) write_and_empty_triangle_bag( output_file, layer_triangles[layer], pcb->GetLayerColor(layer), boardIU2WRML ); // Same thing for the via layers for( int i = 0; i < 4; i++ ) write_and_empty_triangle_bag( output_file, via_triangles[i], pcb->GetVisibleElementColor( VIAS_VISIBLE + i ), boardIU2WRML ); // Close the outer 'transform' node fputs( "]\n}\n", output_file ); // End of work fclose( output_file ); SetLocaleTo_Default(); // revert to the current locale return true; }
static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule, std::ofstream& aOutputFile, double aVRMLModelsToBiu, bool aExport3DFiles, const wxString& a3D_Subdir ) { // Reference and value if( aModule->Reference().IsVisible() ) export_vrml_text_module( &aModule->Reference() ); if( aModule->Value().IsVisible() ) export_vrml_text_module( &aModule->Value() ); // Export module edges for( EDA_ITEM* item = aModule->GraphicalItems(); item; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: export_vrml_text_module( static_cast<TEXTE_MODULE*>( item ) ); break; case PCB_MODULE_EDGE_T: export_vrml_edge_module( aModel, static_cast<EDGE_MODULE*>( item ), aModule->GetOrientation() ); break; default: break; } } // Export pads for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) export_vrml_pad( aModel, aPcb, pad ); bool isFlipped = aModule->GetLayer() == B_Cu; // Export the object VRML model(s) for( S3D_MASTER* vrmlm = aModule->Models(); vrmlm; vrmlm = vrmlm->Next() ) { if( !vrmlm->Is3DType( S3D_MASTER::FILE3D_VRML ) ) continue; wxString fname = vrmlm->GetShape3DFullFilename(); fname.Replace( wxT( "\\" ), wxT( "/" ) ); wxString source_fname = fname; if( aExport3DFiles ) { // Change illegal characters in filenames ChangeIllegalCharacters( fname, true ); fname = a3D_Subdir + wxT( "/" ) + fname; if( !wxFileExists( fname ) ) wxCopyFile( source_fname, fname ); } /* Calculate 3D shape rotation: * this is the rotation parameters, with an additional 180 deg rotation * for footprints that are flipped * When flipped, axis rotation is the horizontal axis (X axis) */ double rotx = -vrmlm->m_MatRotation.x; double roty = -vrmlm->m_MatRotation.y; double rotz = -vrmlm->m_MatRotation.z; if( isFlipped ) { rotx += 180.0; NEGATE( roty ); NEGATE( rotz ); } // Do some quaternion munching double q1[4], q2[4], rot[4]; build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 ); build_quat( 0, 1, 0, DEG2RAD( roty ), q2 ); compose_quat( q1, q2, q1 ); build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 ); compose_quat( q1, q2, q1 ); // Note here aModule->GetOrientation() is in 0.1 degrees, // so module rotation has to be converted to radians build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 ); compose_quat( q1, q2, q1 ); from_quat( q1, rot ); aOutputFile << "Transform {\n"; // A null rotation would fail the acos! if( rot[3] != 0.0 ) { aOutputFile << " rotation " << std::setprecision( 3 ); aOutputFile << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n"; } // adjust 3D shape local offset position // they are given in inch, so they are converted in board IU. double offsetx = vrmlm->m_MatPosition.x * IU_PER_MILS * 1000.0; double offsety = vrmlm->m_MatPosition.y * IU_PER_MILS * 1000.0; double offsetz = vrmlm->m_MatPosition.z * IU_PER_MILS * 1000.0; if( isFlipped ) NEGATE( offsetz ); else // In normal mode, Y axis is reversed in Pcbnew. NEGATE( offsety ); RotatePoint( &offsetx, &offsety, aModule->GetOrientation() ); aOutputFile << " translation " << std::setprecision( aModel.precision ); aOutputFile << (( offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx ) << " "; aOutputFile << ( -(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty ) << " "; aOutputFile << ( (offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) ) << "\n"; aOutputFile << " scale "; aOutputFile << ( vrmlm->m_MatScale.x * aVRMLModelsToBiu ) << " "; aOutputFile << ( vrmlm->m_MatScale.y * aVRMLModelsToBiu ) << " "; aOutputFile << ( vrmlm->m_MatScale.z * aVRMLModelsToBiu ) << "\n"; if( fname.EndsWith( wxT( "x3d" ) ) ) { X3D_MODEL_PARSER* parser = new X3D_MODEL_PARSER( vrmlm ); if( parser ) { // embed x3d model in vrml format double vrml_to_x3d = aVRMLModelsToBiu; parser->Load( fname, vrml_to_x3d ); try { aOutputFile << " children [\n "; aOutputFile << TO_UTF8( parser->VRML2_representation() ) << " ]\n"; aOutputFile << " }\n"; } catch( const std::exception& e ) { delete parser; throw; } } } else { aOutputFile << " children [\n Inline {\n url \""; aOutputFile << TO_UTF8( fname ) << "\"\n } ]\n"; aOutputFile << " }\n"; } } }
static void export_vrml_module( BOARD* aPcb, MODULE* aModule, FILE* aOutputFile, double aVRMLModelsToBiu, bool aExport3DFiles, const wxString & a3D_Subdir, double boardIU2WRML ) { // Reference and value export_vrml_text_module( aModule->m_Reference ); export_vrml_text_module( aModule->m_Value ); // Export module edges for( EDA_ITEM* item = aModule->m_Drawings; item != NULL; item = item->Next() ) { switch( item->Type() ) { case PCB_MODULE_TEXT_T: export_vrml_text_module( dynamic_cast<TEXTE_MODULE*>(item) ); break; case PCB_MODULE_EDGE_T: export_vrml_edge_module( dynamic_cast<EDGE_MODULE*>(item) ); break; default: break; } } // Export pads for( D_PAD* pad = aModule->m_Pads; pad; pad = pad->Next() ) export_vrml_pad( aPcb, pad ); bool isFlipped = aModule->GetLayer() == LAYER_N_BACK; // Export the object VRML model(s) for( S3D_MASTER* vrmlm = aModule->m_3D_Drawings; vrmlm != 0; vrmlm = vrmlm->Next() ) { wxString fname = vrmlm->m_Shape3DName; if( fname.IsEmpty() ) continue; if( ! wxFileName::FileExists( fname ) ) { wxFileName fn = fname; fname = wxGetApp().FindLibraryPath( fn ); if( fname.IsEmpty() ) // keep "short" name if full filemane not found fname = vrmlm->m_Shape3DName; } fname.Replace(wxT("\\"), wxT("/" ) ); wxString source_fname = fname; if( aExport3DFiles ) // Change illegal characters in short filename { ChangeIllegalCharacters( fname, true ); fname = a3D_Subdir + wxT("/") + fname; if( !wxFileExists( fname ) ) wxCopyFile( source_fname, fname ); } /* Calculate 3D shape rotation: * this is the rotation parameters, with an additional 180 deg rotation * for footprints that are flipped * When flipped, axis rotation is the horizontal axis (X axis) */ double rotx = - vrmlm->m_MatRotation.x; double roty = - vrmlm->m_MatRotation.y; double rotz = - vrmlm->m_MatRotation.z; if ( isFlipped ) { rotx += 180.0; NEGATE(roty); NEGATE(rotz); } // Do some quaternion munching double q1[4], q2[4], rot[4]; build_quat( 1, 0, 0, rotx / 180.0 * M_PI, q1 ); build_quat( 0, 1, 0, roty / 180.0 * M_PI, q2 ); compose_quat( q1, q2, q1 ); build_quat( 0, 0, 1, rotz / 180.0 * M_PI, q2 ); compose_quat( q1, q2, q1 ); // Note here aModule->GetOrientation() is in 0.1 degrees, // so module rotation is aModule->GetOrientation() / 1800.0 build_quat( 0, 0, 1, aModule->GetOrientation() / 1800.0 * M_PI, q2 ); compose_quat( q1, q2, q1 ); from_quat( q1, rot ); fprintf( aOutputFile, "Transform {\n" ); // A null rotation would fail the acos! if( rot[3] != 0.0 ) { fprintf( aOutputFile, " rotation %g %g %g %g\n", rot[0], rot[1], rot[2], rot[3] ); } // adjust 3D shape offset position int offsetx = vrmlm->m_MatPosition.x; int offsety = vrmlm->m_MatPosition.y; double offsetz = vrmlm->m_MatPosition.z; if ( isFlipped ) NEGATE(offsetz); else // In normal mode, Y axis is reversed in Pcbnew. NEGATE(offsety); RotatePoint(&offsetx, &offsety, aModule->GetOrientation()); fprintf( aOutputFile, " translation %g %g %g\n", (double) (offsetx + aModule->m_Pos.x) * boardIU2WRML, - (double)(offsety + aModule->m_Pos.y) * boardIU2WRML, // Y axis is reversed in Pcbnew offsetz + layer_z[aModule->GetLayer()] * boardIU2WRML); fprintf( aOutputFile, " scale %g %g %g\n", vrmlm->m_MatScale.x * aVRMLModelsToBiu, vrmlm->m_MatScale.y * aVRMLModelsToBiu, vrmlm->m_MatScale.z * aVRMLModelsToBiu ); fprintf( aOutputFile, // " children [\n Inline {\n url \"file://%s\"\n } ]\n", " children [\n Inline {\n url \"%s\"\n } ]\n", TO_UTF8( fname ) ); fprintf( aOutputFile, " }\n" ); } }