From 0f6e20a334339b90b271ac64057ed34fcde364f7 Mon Sep 17 00:00:00 2001 From: Garux Date: Fri, 20 Dec 2019 17:37:10 +0300 Subject: [PATCH] * brushexport plugin: "Weld vertices" option (welds inside group or object, thus result depends on collapse option) rephrase, shorten code disable debug prints about exclusions in release print error to console with nothing selected more picky conditions in ExportData::GetShaderNameFromShaderPath slash search --- contrib/brushexport/callbacks.cpp | 41 ++++----- contrib/brushexport/export.cpp | 144 +++++++++++++++--------------- contrib/brushexport/export.h | 2 +- contrib/brushexport/interface.cpp | 30 ++----- 4 files changed, 103 insertions(+), 114 deletions(-) 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; }