diff --git a/contrib/brushexport/callbacks.cpp b/contrib/brushexport/callbacks.cpp index 6a346f3a..aca422b3 100644 --- a/contrib/brushexport/callbacks.cpp +++ b/contrib/brushexport/callbacks.cpp @@ -10,7 +10,13 @@ // stuff from interface.cpp void DestroyWindow(); - +//! TODO add tooltip for ignore: shader name after last slash, case sensitive // or make insensitive +//! TODO console print on success +//! TODO make togglebuttons inactive on !exportmat +//! TODO add ignore mat on ENTER, del on del +//! TODO add entry with path to save to (to resave faster) +//! TODO tooltip for weld //inside groups +//! TODO ignore case in mat name comparison materials_comparator namespace callbacks { void OnDestroy( GtkWidget* w, gpointer data ){ @@ -39,15 +45,17 @@ void OnExportClicked( GtkButton* button, gpointer user_data ){ { gchar* data; gtk_tree_model_get( GTK_TREE_MODEL( list ), &iter, 0, &data, -1 ); +#ifdef _DEBUG globalOutputStream() << data << "\n"; +#endif ignore.insert( std::string( data ) ); g_free( data ); valid = gtk_tree_model_iter_next( GTK_TREE_MODEL( list ), &iter ); } - - for ( std::set::iterator it( ignore.begin() ); it != ignore.end(); ++it ) - globalOutputStream() << it->c_str() << "\n"; - +#ifdef _DEBUG + for ( const std::string& str : ignore ) + globalOutputStream() << str.c_str() << "\n"; +#endif // collapse mode collapsemode mode = COLLAPSE_NONE; @@ -77,34 +85,27 @@ void OnExportClicked( GtkButton* button, gpointer user_data ){ GtkWidget* toggle = lookup_widget( GTK_WIDGET( button ), "t_exportmaterials" ); ASSERT_NOTNULL( toggle ); - bool exportmat = FALSE; - - if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) ) ) { - exportmat = TRUE; - } + const bool exportmat = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) ); // limit material names? toggle = lookup_widget( GTK_WIDGET( button ), "t_limitmatnames" ); ASSERT_NOTNULL( toggle ); - bool limitMatNames = FALSE; - - if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) ) && exportmat ) { - limitMatNames = TRUE; - } + const bool limitMatNames = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) ) && exportmat; // create objects instead of groups? toggle = lookup_widget( GTK_WIDGET( button ), "t_objects" ); ASSERT_NOTNULL( toggle ); - bool objects = FALSE; + const bool objects = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) ) && exportmat; - if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) ) && exportmat ) { - objects = TRUE; - } + toggle = lookup_widget( GTK_WIDGET( button ), "t_weld" ); + ASSERT_NOTNULL( toggle ); + + const bool weld = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggle ) ); // export - ExportSelection( ignore, mode, exportmat, path, limitMatNames, objects ); + ExportSelection( ignore, mode, exportmat, path, limitMatNames, objects, weld ); } void OnAddMaterial( GtkButton* button, gpointer user_data ){ diff --git a/contrib/brushexport/export.cpp b/contrib/brushexport/export.cpp index 64446ac8..2d4d4bcf 100644 --- a/contrib/brushexport/export.cpp +++ b/contrib/brushexport/export.cpp @@ -22,7 +22,7 @@ class ExportData { public: -ExportData( const std::set& ignorelist, collapsemode mode, bool limNames, bool objs ); +ExportData( const std::set& ignorelist, collapsemode mode ); virtual ~ExportData( void ); virtual void BeginBrush( Brush& b ); @@ -49,11 +49,11 @@ private: void GetShaderNameFromShaderPath( const char* path, std::string& name ); group* current; -collapsemode mode; +const collapsemode mode; const std::set& ignorelist; }; -ExportData::ExportData( const std::set& _ignorelist, collapsemode _mode, bool _limNames, bool _objs ) +ExportData::ExportData( const std::set& _ignorelist, collapsemode _mode ) : mode( _mode ), ignorelist( _ignorelist ){ current = 0; @@ -136,7 +136,7 @@ void ExportData::GetShaderNameFromShaderPath( const char* path, std::string& nam size_t last_slash = tmp.find_last_of( "/" ); - if ( last_slash != std::string::npos && last_slash == ( tmp.length() - 1 ) ) { + if ( last_slash == std::string::npos || last_slash == ( tmp.length() - 1 ) ) { name = path; } else{ @@ -154,16 +154,18 @@ void ExportData::GetShaderNameFromShaderPath( const char* path, std::string& nam class ExportDataAsWavefront : public ExportData { private: -bool expmat; -bool limNames; -bool objs; +const bool expmat; +const bool limNames; +const bool objs; +const bool weld; public: -ExportDataAsWavefront( const std::set& _ignorelist, collapsemode _mode, bool _expmat, bool _limNames, bool _objs ) - : ExportData( _ignorelist, _mode, _limNames, _objs ){ - expmat = _expmat; - limNames = _limNames; - objs = _objs; +ExportDataAsWavefront( const std::set& _ignorelist, collapsemode _mode, bool _expmat, bool _limNames, bool _objs, bool _weld ) + : ExportData( _ignorelist, _mode ), + expmat( _expmat ), + limNames( _limNames ), + objs( _objs ), + weld( _weld ){ } bool WriteToFile( const std::string& path, collapsemode mode ) const; @@ -178,11 +180,10 @@ bool ExportDataAsWavefront::WriteToFile( const std::string& path, collapsemode m std::string mtlFile = objFile.substr( 0, objFile.length() - 4 ) + ".mtl"; - typedef std::pair Material; - auto materials_comparator = []( const Material& ma1, const Material& ma2 ) { - return ma1.first < ma2.first; + auto materials_comparator = []( const std::string& lhs, const std::string& rhs ) { + return lhs < rhs; }; - auto materials = std::set( materials_comparator ); + auto materials = std::map( materials_comparator ); TextFileOutputStream out( objFile.c_str() ); @@ -200,15 +201,13 @@ bool ExportDataAsWavefront::WriteToFile( const std::string& path, collapsemode m } unsigned int vertex_count = 0; + unsigned int texcoord_count = 0; - const std::list::const_iterator gend( groups.end() ); - for ( std::list::const_iterator git( groups.begin() ); git != gend; ++git ) + for ( const ExportData::group& group : groups ) { - typedef std::multimap bm; - bm brushMaterials; - typedef std::pair String_Pair; + std::multimap brushMaterials; - const std::list::const_iterator end( git->faces.end() ); + std::vector vertices; // unique vertices list for welding // submesh starts here if ( objs ) { @@ -217,31 +216,59 @@ bool ExportDataAsWavefront::WriteToFile( const std::string& path, collapsemode m else { out << "\ng "; } - out << git->name.c_str() << "\n"; + out << group.name.c_str() << "\n"; // material if ( expmat && mode == COLLAPSE_ALL ) { out << "usemtl material" << "\n\n"; - materials.insert( std::make_pair( std::string( "material" ), Colour3( 0.5, 0.5, 0.5 ) ) ); + materials.emplace( "material", Colour3( 0.5, 0.5, 0.5 ) ); } - for ( std::list::const_iterator it( git->faces.begin() ); it != end; ++it ) + for ( const Face* face : group.faces ) { - const Winding& w( ( *it )->getWinding() ); + const Winding& w( face->getWinding() ); + + // faces + StringOutputStream faceLine( 256 ); + faceLine << "\nf"; - // vertices size_t i = w.numpoints; do{ --i; - out << "v " << FloatFormat( w[i].vertex.x(), 1, 6 ) << " " << FloatFormat( w[i].vertex.z(), 1, 6 ) << " " << FloatFormat( -w[i].vertex.y(), 1, 6 ) << "\n"; + ++texcoord_count; + std::size_t vertexN = 0; // vertex index to use, 0 is special value = no vertex to weld to found + const Vector3& vertex = w[i].vertex; + if( weld ){ + auto found = std::find_if( vertices.begin(), vertices.end(), [&vertex]( const Vector3& othervertex ){ + return Edge_isDegenerate( vertex, othervertex ); + } ); + if( found == vertices.end() ){ // unique vertex, add to the list + vertices.emplace_back( vertex ); + } + else{ + vertexN = vertex_count - std::distance( found, vertices.end() ) + 1; // reuse existing index + } + } + // write vertices + if( vertexN == 0 ){ + vertexN = ++vertex_count; + out << "v " << FloatFormat( vertex.x(), 1, 6 ) << " " << FloatFormat( vertex.z(), 1, 6 ) << " " << FloatFormat( -vertex.y(), 1, 6 ) << "\n"; + } + faceLine << " " << vertexN << "/" << texcoord_count; // store faces } while( i != 0 ); + + if ( mode != COLLAPSE_ALL ) + materials.emplace( face->getShader().getShader(), face->getShader().state()->getTexture().color ); + + brushMaterials.emplace( face->getShader().getShader(), faceLine.c_str() ); } + out << "\n"; - for ( std::list::const_iterator it( git->faces.begin() ); it != end; ++it ) + for ( const Face* face : group.faces ) { - const Winding& w( ( *it )->getWinding() ); + const Winding& w( face->getWinding() ); // texcoords size_t i = w.numpoints; @@ -252,54 +279,24 @@ bool ExportDataAsWavefront::WriteToFile( const std::string& path, collapsemode m while( i != 0 ); } - for ( std::list::const_iterator it( git->faces.begin() ); it != end; ++it ) { - const Winding& w( ( *it )->getWinding() ); - - // faces - StringOutputStream faceLine( 256 ); - faceLine << "\nf"; - - size_t i = w.numpoints; - do{ - --i; - ++vertex_count; - faceLine << " " << vertex_count << "/" << vertex_count; - } - while( i != 0 ); - - if ( mode != COLLAPSE_ALL ) { - materials.insert( std::make_pair( std::string( ( *it )->getShader().getShader() ), ( *it )->getShader().state()->getTexture().color ) ); - brushMaterials.insert( String_Pair( ( *it )->getShader().getShader(), faceLine.c_str() ) ); - } - else { - out << faceLine.c_str(); - } - - - } - - if ( mode != COLLAPSE_ALL ) { std::string lastMat; - std::string mat; - std::string faces; - - for ( bm::iterator iter = brushMaterials.begin(); iter != brushMaterials.end(); iter++ ) + for ( const auto& stringpair : brushMaterials ) { - mat = ( *iter ).first.c_str(); - faces = ( *iter ).second.c_str(); + const std::string& mat = stringpair.first; + const std::string& faces = stringpair.second; - if ( mat != lastMat ) { + if ( mode != COLLAPSE_ALL && mat != lastMat ) { if ( limNames && mat.size() > MAX_MATERIAL_NAME ) { out << "\nusemtl " << mat.substr( mat.size() - MAX_MATERIAL_NAME, mat.size() ).c_str(); } else { out << "\nusemtl " << mat.c_str(); } + lastMat = mat; } - + // write faces out << faces.c_str(); - lastMat = mat; } } @@ -315,10 +312,10 @@ bool ExportDataAsWavefront::WriteToFile( const std::string& path, collapsemode m outMtl << "# Wavefront material file exported with NetRadiants brushexport plugin.\n"; outMtl << "# Material Count: " << (const Unsigned)materials.size() << "\n\n"; - for ( std::set>::const_iterator it( materials.begin() ); it != materials.end(); ++it ) + for ( const auto& material : materials ) { - const std::string& str = it->first; - const Colour3& clr = it->second; + const std::string& str = material.first; + const Colour3& clr = material.second; if ( limNames && str.size() > MAX_MATERIAL_NAME ) { outMtl << "newmtl " << str.substr( str.size() - MAX_MATERIAL_NAME, str.size() ).c_str() << "\n"; } @@ -374,8 +371,13 @@ private: ExportData& exporter; }; -bool ExportSelection( const std::set& ignorelist, collapsemode m, bool exmat, const std::string& path, bool limNames, bool objs ){ - ExportDataAsWavefront exporter( ignorelist, m, exmat, limNames, objs ); +bool ExportSelection( const std::set& ignorelist, collapsemode m, bool exmat, const std::string& path, bool limNames, bool objs, bool weld ){ + ExportDataAsWavefront exporter( ignorelist, m, exmat, limNames, objs, weld ); + + if( GlobalSelectionSystem().countSelected() == 0 ){ + globalErrorStream() << "Nothing is selected.\n"; + return false; + } ForEachSelected vis( exporter ); GlobalSelectionSystem().foreachSelected( vis ); diff --git a/contrib/brushexport/export.h b/contrib/brushexport/export.h index 40fa7c44..ee5d9e21 100644 --- a/contrib/brushexport/export.h +++ b/contrib/brushexport/export.h @@ -10,6 +10,6 @@ enum collapsemode COLLAPSE_NONE }; -bool ExportSelection( const std::set& ignorelist, collapsemode m, bool exmat, const std::string& path, bool limitMatNames, bool objects ); +bool ExportSelection( const std::set& ignorelist, collapsemode m, bool exmat, const std::string& path, bool limitMatNames, bool objects, bool weld ); #endif diff --git a/contrib/brushexport/interface.cpp b/contrib/brushexport/interface.cpp index ea3a3839..06437137 100644 --- a/contrib/brushexport/interface.cpp +++ b/contrib/brushexport/interface.cpp @@ -38,36 +38,32 @@ create_w_plugplug2( void ){ GtkWidget *t_exportmaterials; GtkWidget *t_limitmatnames; GtkWidget *t_objects; + GtkWidget *t_weld; GtkTooltips *tooltips; tooltips = gtk_tooltips_new(); w_plugplug2 = gtk_window_new( GTK_WINDOW_TOPLEVEL ); - gtk_widget_set_name( w_plugplug2, "w_plugplug2" ); gtk_window_set_title( GTK_WINDOW( w_plugplug2 ), "BrushExport-Plugin 3.0 by namespace" ); gtk_window_set_position( GTK_WINDOW( w_plugplug2 ), GTK_WIN_POS_CENTER_ON_PARENT ); gtk_window_set_transient_for( GTK_WINDOW( w_plugplug2 ), GTK_WINDOW( g_pRadiantWnd ) ); gtk_window_set_destroy_with_parent( GTK_WINDOW( w_plugplug2 ), TRUE ); vbox1 = gtk_vbox_new( FALSE, 0 ); - gtk_widget_set_name( vbox1, "vbox1" ); gtk_widget_show( vbox1 ); gtk_container_add( GTK_CONTAINER( w_plugplug2 ), vbox1 ); gtk_container_set_border_width( GTK_CONTAINER( vbox1 ), 5 ); hbox2 = gtk_hbox_new( TRUE, 5 ); - gtk_widget_set_name( hbox2, "hbox2" ); gtk_widget_show( hbox2 ); gtk_box_pack_start( GTK_BOX( vbox1 ), hbox2, FALSE, FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( hbox2 ), 5 ); vbox4 = gtk_vbox_new( TRUE, 0 ); - gtk_widget_set_name( vbox4, "vbox4" ); gtk_widget_show( vbox4 ); gtk_box_pack_start( GTK_BOX( hbox2 ), vbox4, TRUE, FALSE, 0 ); r_collapse = gtk_radio_button_new_with_mnemonic( NULL, "Collapse mesh" ); - gtk_widget_set_name( r_collapse, "r_collapse" ); gtk_tooltips_set_tip( GTK_TOOLTIPS( tooltips ), r_collapse, "Collapse all brushes into a single group", "Collapse all brushes into a single group" ); gtk_widget_show( r_collapse ); gtk_box_pack_start( GTK_BOX( vbox4 ), r_collapse, FALSE, FALSE, 0 ); @@ -75,7 +71,6 @@ create_w_plugplug2( void ){ r_collapse_group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( r_collapse ) ); r_collapsebymaterial = gtk_radio_button_new_with_mnemonic( NULL, "Collapse by material" ); - gtk_widget_set_name( r_collapsebymaterial, "r_collapsebymaterial" ); gtk_tooltips_set_tip( GTK_TOOLTIPS( tooltips ), r_collapsebymaterial, "Collapse into groups by material", "Collapse into groups by material" ); gtk_widget_show( r_collapsebymaterial ); gtk_box_pack_start( GTK_BOX( vbox4 ), r_collapsebymaterial, FALSE, FALSE, 0 ); @@ -83,7 +78,6 @@ create_w_plugplug2( void ){ r_collapse_group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( r_collapsebymaterial ) ); r_nocollapse = gtk_radio_button_new_with_mnemonic( NULL, "Don't collapse" ); - gtk_widget_set_name( r_nocollapse, "r_nocollapse" ); gtk_tooltips_set_tip( GTK_TOOLTIPS( tooltips ), r_nocollapse, "Every brush is stored in its own group", "Every brush is stored in its own group" ); gtk_widget_show( r_nocollapse ); gtk_box_pack_start( GTK_BOX( vbox4 ), r_nocollapse, FALSE, FALSE, 0 ); @@ -92,80 +86,71 @@ create_w_plugplug2( void ){ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( r_nocollapse ), TRUE ); vbox3 = gtk_vbox_new( FALSE, 0 ); - gtk_widget_set_name( vbox3, "vbox3" ); gtk_widget_show( vbox3 ); gtk_box_pack_start( GTK_BOX( hbox2 ), vbox3, FALSE, FALSE, 0 ); b_export = gtk_button_new_from_stock( "gtk-save" ); - gtk_widget_set_name( b_export, "b_export" ); gtk_widget_show( b_export ); gtk_box_pack_start( GTK_BOX( vbox3 ), b_export, TRUE, FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( b_export ), 5 ); b_close = gtk_button_new_from_stock( "gtk-cancel" ); - gtk_widget_set_name( b_close, "b_close" ); gtk_widget_show( b_close ); gtk_box_pack_start( GTK_BOX( vbox3 ), b_close, TRUE, FALSE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( b_close ), 5 ); vbox2 = gtk_vbox_new( FALSE, 5 ); - gtk_widget_set_name( vbox2, "vbox2" ); gtk_widget_show( vbox2 ); gtk_box_pack_start( GTK_BOX( vbox1 ), vbox2, TRUE, TRUE, 0 ); gtk_container_set_border_width( GTK_CONTAINER( vbox2 ), 2 ); label1 = gtk_label_new( "Ignored materials:" ); - gtk_widget_set_name( label1, "label1" ); gtk_widget_show( label1 ); gtk_box_pack_start( GTK_BOX( vbox2 ), label1, FALSE, FALSE, 0 ); scrolledwindow1 = gtk_scrolled_window_new( NULL, NULL ); - gtk_widget_set_name( scrolledwindow1, "scrolledwindow1" ); gtk_widget_show( scrolledwindow1 ); gtk_box_pack_start( GTK_BOX( vbox2 ), scrolledwindow1, TRUE, TRUE, 0 ); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolledwindow1 ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( scrolledwindow1 ), GTK_SHADOW_IN ); t_materialist = gtk_tree_view_new(); - gtk_widget_set_name( t_materialist, "t_materialist" ); gtk_widget_show( t_materialist ); gtk_container_add( GTK_CONTAINER( scrolledwindow1 ), t_materialist ); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( t_materialist ), FALSE ); gtk_tree_view_set_enable_search( GTK_TREE_VIEW( t_materialist ), FALSE ); ed_materialname = gtk_entry_new(); - gtk_widget_set_name( ed_materialname, "ed_materialname" ); gtk_widget_show( ed_materialname ); gtk_box_pack_start( GTK_BOX( vbox2 ), ed_materialname, FALSE, FALSE, 0 ); hbox1 = gtk_hbox_new( TRUE, 0 ); - gtk_widget_set_name( hbox1, "hbox1" ); gtk_widget_show( hbox1 ); gtk_box_pack_start( GTK_BOX( vbox2 ), hbox1, FALSE, FALSE, 0 ); b_addmaterial = gtk_button_new_from_stock( "gtk-add" ); - gtk_widget_set_name( b_addmaterial, "b_addmaterial" ); gtk_widget_show( b_addmaterial ); gtk_box_pack_start( GTK_BOX( hbox1 ), b_addmaterial, FALSE, FALSE, 0 ); b_removematerial = gtk_button_new_from_stock( "gtk-remove" ); - gtk_widget_set_name( b_removematerial, "b_removematerial" ); gtk_widget_show( b_removematerial ); gtk_box_pack_start( GTK_BOX( hbox1 ), b_removematerial, FALSE, FALSE, 0 ); t_limitmatnames = gtk_check_button_new_with_mnemonic( "Use short material names (max. 20 chars)" ); - gtk_widget_set_name( t_limitmatnames, "t_limitmatnames" ); gtk_widget_show( t_limitmatnames ); gtk_box_pack_end( GTK_BOX( vbox2 ), t_limitmatnames, FALSE, FALSE, 0 ); t_objects = gtk_check_button_new_with_mnemonic( "Create (o)bjects instead of (g)roups" ); - gtk_widget_set_name( t_objects, "t_objects" ); gtk_widget_show( t_objects ); gtk_box_pack_end( GTK_BOX( vbox2 ), t_objects, FALSE, FALSE, 0 ); + t_weld = gtk_check_button_new_with_mnemonic( "Weld vertices" ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( t_weld ), TRUE ); + gtk_widget_show( t_weld ); + gtk_box_pack_end( GTK_BOX( vbox2 ), t_weld, FALSE, FALSE, 0 ); + t_exportmaterials = gtk_check_button_new_with_mnemonic( "Create material information (.mtl file)" ); - gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( t_exportmaterials ), true ); - gtk_widget_set_name( t_exportmaterials, "t_exportmaterials" ); + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( t_exportmaterials ), TRUE ); gtk_widget_show( t_exportmaterials ); gtk_box_pack_end( GTK_BOX( vbox2 ), t_exportmaterials, FALSE, FALSE, 10 ); @@ -200,6 +185,7 @@ create_w_plugplug2( void ){ GLADE_HOOKUP_OBJECT( w_plugplug2, t_exportmaterials, "t_exportmaterials" ); GLADE_HOOKUP_OBJECT( w_plugplug2, t_limitmatnames, "t_limitmatnames" ); GLADE_HOOKUP_OBJECT( w_plugplug2, t_objects, "t_objects" ); + GLADE_HOOKUP_OBJECT( w_plugplug2, t_weld, "t_weld" ); return w_plugplug2; }