Merge pull request #480 from maxGimeno/Polyhedron_demo-C3t3_IO_plugin-GF

I/O plugin for c3t3 meshes
This commit is contained in:
Sebastien Loriot 2015-11-26 14:34:19 +01:00
commit 8e507866bc
9 changed files with 575 additions and 13 deletions

View File

@ -65,6 +65,8 @@ typedef CGAL::Polyhedron_demo_labeled_mesh_domain_3<Function_domain> Function_me
#endif
typedef CGAL::Mesh_complex_3_in_triangulation_3<Tr> C3t3;
typedef Tr::Geom_traits Geom_traits;
// 3D complex
#endif // CGAL_DEMO_MESH_3_C3T3_TYPE_H

View File

@ -106,7 +106,6 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
NO_DEFAULT_PATH
DOC "Path to CGAL/Three/Viewer_interface.h")
hide_variable(CGAL_THREE_VIEWER_INTERFACE_H_PATH)
if(CGAL_THREE_VIEWER_INTERFACE_H_PATH)
qt5_generate_moc( "${CGAL_THREE_VIEWER_INTERFACE_H_PATH}/CGAL/Three/Viewer_interface.h"
"${CMAKE_CURRENT_BINARY_DIR}/Viewer_interface_moc.cpp" )
@ -173,7 +172,8 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
endmacro(add_item)
add_item(scene_c2t3_item Scene_c2t3_item.cpp)# Scene_c2t3_item.moc)
add_item(scene_c3t3_item Scene_c3t3_item.cpp)
target_link_libraries(scene_c3t3_item scene_polyhedron_item scene_polygon_soup_item)
add_item(scene_polyhedron_item Scene_polyhedron_item.cpp ${statisticsPolyhedronUI_FILES})# Scene_polyhedron_item.moc)
add_item(scene_polyhedron_transform_item Plugins/PCA/Scene_polyhedron_transform_item.cpp )#Scene_polyhedron_transform_item.moc)
@ -214,8 +214,7 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
target_link_libraries( scene_points_with_normal_item gl_splat)
target_link_libraries( demo_framework gl_splat)
add_item(scene_c3t3_item Scene_c3t3_item.cpp)
target_link_libraries(scene_c3t3_item scene_polyhedron_item scene_polygon_soup_item)
foreach( lib
demo_framework
@ -260,6 +259,7 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
# PLUGINS #
###########
file(GLOB DEMO_PLUGINS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/Plugins/" "${CMAKE_CURRENT_SOURCE_DIR}/Plugins/*")
FOREACH(SUB_DIR ${DEMO_PLUGINS})
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/${SUB_DIR}")

View File

@ -909,7 +909,7 @@ void MainWindow::open(QString filename)
Q_FOREACH(CGAL::Three::Polyhedron_demo_io_plugin_interface* io_plugin, io_plugins) {
if ( !io_plugin->canLoad() ) continue;
all_items << io_plugin->name();
if ( file_matches_filter(io_plugin->nameFilters(), filename) )
if ( file_matches_filter(io_plugin->loadNameFilters(), filename) )
selected_items << io_plugin->name();
}
}
@ -1291,7 +1291,7 @@ void MainWindow::on_actionLoad_triggered()
FilterPluginMap filterPluginMap;
Q_FOREACH(CGAL::Three::Polyhedron_demo_io_plugin_interface* plugin, io_plugins) {
QStringList split_filters = plugin->nameFilters().split(";;");
QStringList split_filters = plugin->loadNameFilters().split(";;");
Q_FOREACH(const QString& filter, split_filters) {
FilterPluginMap::iterator it = filterPluginMap.find(filter);
if(it != filterPluginMap.end()) {
@ -1362,7 +1362,7 @@ void MainWindow::on_actionSaveAs_triggered()
Q_FOREACH(CGAL::Three::Polyhedron_demo_io_plugin_interface* plugin, io_plugins) {
if(plugin->canSave(item)) {
canSavePlugins << plugin;
filters += plugin->nameFilters();
filters += plugin->saveNameFilters();
}
}
filters << tr("All files (*)");
@ -1389,7 +1389,7 @@ void MainWindow::save(QString filename, CGAL::Three::Scene_item* item) {
Q_FOREACH(CGAL::Three::Polyhedron_demo_io_plugin_interface* plugin, io_plugins) {
if( plugin->canSave(item) &&
file_matches_filter(plugin->nameFilters(),filename) )
file_matches_filter(plugin->saveNameFilters(),filename) )
{
if(plugin->save(item, fileinfo))
break;

View File

@ -0,0 +1,413 @@
#include <CGAL/Mesh_3/io_signature.h>
#include "Scene_c3t3_item.h"
#include <CGAL/Three/Polyhedron_demo_io_plugin_interface.h>
#include <iostream>
#include <fstream>
class Polyhedron_demo_c3t3_binary_io_plugin :
public QObject,
public CGAL::Three::Polyhedron_demo_io_plugin_interface
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::Polyhedron_demo_io_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
QString name() const { return "C3t3_io_plugin"; }
QString nameFilters() const { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma)"; }
QString saveNameFilters() const { return "binary files (*.cgal);;ascii (*.mesh);;maya (*.ma)"; }
QString loadNameFilters() const { return "binary files (*.cgal)" ; }
bool canLoad() const;
CGAL::Three::Scene_item* load(QFileInfo fileinfo);
bool canSave(const CGAL::Three::Scene_item*);
bool save(const CGAL::Three::Scene_item*, QFileInfo fileinfo);
private:
bool try_load_other_binary_format(std::istream& in, C3t3& c3t3);
bool try_load_a_cdt_3(std::istream& in, C3t3& c3t3);
};
bool Polyhedron_demo_c3t3_binary_io_plugin::canLoad() const {
return true;
}
CGAL::Three::Scene_item*
Polyhedron_demo_c3t3_binary_io_plugin::load(QFileInfo fileinfo) {
if(fileinfo.suffix().toLower() == "cgal")
{
// Open file
std::ifstream in(fileinfo.filePath().toUtf8(),
std::ios_base::in|std::ios_base::binary);
if(!in) {
std::cerr << "Error! Cannot open file "
<< (const char*)fileinfo.filePath().toUtf8() << std::endl;
return NULL;
}
Scene_c3t3_item* item = new Scene_c3t3_item();
item->setName(fileinfo.baseName());
if(item->load_binary(in)) {
item->changed();
return item;
}
item->c3t3().clear();
in.seekg(0);
if(try_load_other_binary_format(in, item->c3t3())) {
item->changed();
return item;
}
item->c3t3().clear();
in.seekg(0);
if(try_load_a_cdt_3(in, item->c3t3())) {
item->changed();
return item;
}
}
// if all loading failed...
return NULL;
}
bool Polyhedron_demo_c3t3_binary_io_plugin::canSave(const CGAL::Three::Scene_item* item)
{
// This plugin supports c3t3 items.
return qobject_cast<const Scene_c3t3_item*>(item);
}
bool
Polyhedron_demo_c3t3_binary_io_plugin::
save(const CGAL::Three::Scene_item* item, QFileInfo fileinfo)
{
const Scene_c3t3_item* c3t3_item = qobject_cast<const Scene_c3t3_item*>(item);
if ( NULL == c3t3_item )
{
return false;
}
QString path = fileinfo.absoluteFilePath();
if(path.endsWith(".cgal"))
{
std::ofstream out(fileinfo.filePath().toUtf8(),
std::ios_base::out|std::ios_base::binary);
return out && c3t3_item->save_binary(out);
}
else if (fileinfo.suffix() == "mesh")
{
std::ofstream medit_file (qPrintable(path));
c3t3_item->c3t3().output_to_medit(medit_file,true,true);
return true;
}
else if (fileinfo.suffix() == "ma")
{
std::ofstream maya_file (qPrintable(path));
c3t3_item->c3t3().output_to_maya(
maya_file,true);
return true;
}
else
return false;
}
struct Fake_mesh_domain {
typedef CGAL::Tag_true Has_features;
typedef int Subdomain_index;
typedef int Surface_patch_index;
typedef int Curve_segment_index;
typedef int Corner_index;
typedef boost::variant<Subdomain_index,Surface_patch_index> Index;
};
typedef Geom_traits Fake_gt;
typedef CGAL::Mesh_vertex_base_3<Fake_gt, Fake_mesh_domain> Fake_vertex_base;
typedef CGAL::Compact_mesh_cell_base_3<Fake_gt, Fake_mesh_domain> Fake_cell_base;
typedef CGAL::Triangulation_data_structure_3<Fake_vertex_base,Fake_cell_base> Fake_tds;
typedef CGAL::Regular_triangulation_3<Fake_gt, Fake_tds> Fake_tr;
typedef CGAL::Mesh_complex_3_in_triangulation_3<
Fake_tr,
Fake_mesh_domain::Corner_index,
Fake_mesh_domain::Curve_segment_index> Fake_c3t3;
template <class Vb = CGAL::Triangulation_vertex_base_3<Kernel> >
struct Fake_CDT_3_vertex_base : public Vb
{
typedef Vb Base;
bool steiner;
std::size_t ref_1, ref_2;
template < typename TDS2 >
struct Rebind_TDS {
typedef typename Base::template Rebind_TDS<TDS2>::Other Vb2;
typedef Fake_CDT_3_vertex_base<Vb2> Other;
};
};
template <class Vb>
std::istream&
operator>>( std::istream& is, Fake_CDT_3_vertex_base<Vb>& v)
{
is >> static_cast<typename Fake_CDT_3_vertex_base<Vb>::Base&>(v);
char s;
if( CGAL::is_ascii(is) ) {
is >> s;
if( s == 'S' ) {
v.steiner = true;
is >> v.ref_1 >> v.ref_2;
}
else {
CGAL_assertion(s == '.' || s == 'F');
v.steiner = false;
}
} else {
CGAL::read( is, s );
if(is.bad()) return is;
if( s == 'S' ) {
v.steiner = true;
CGAL::read( is, v.ref_1 );
CGAL::read( is, v.ref_2 );
}
else {
// if(s != '.') {
// std::cerr << "v.point()=" << v.point() << std::endl;
// std::cerr << "s=" << s << " (" << (int)s
// << "), just before position "
// << is.tellg() << " !\n";
// }
CGAL_assertion(s == '.' || s== 'F');
v.steiner = false;
}
}
return is;
}
template <class Cb = CGAL::Triangulation_cell_base_3<Kernel> >
struct Fake_CDT_3_cell_base : public Cb
{
typedef Cb Base;
int constrained_facet[4];
bool _restoring[6];
int to_edge_index( int li, int lj ) const {
CGAL_triangulation_precondition( li >= 0 && li < 4 );
CGAL_triangulation_precondition( lj >= 0 && lj < 4 );
CGAL_triangulation_precondition( li != lj );
return ( li==0 || lj==0 ) ? li+lj-1 : li+lj;
}
template < typename TDS2 >
struct Rebind_TDS {
typedef typename Base::template Rebind_TDS<TDS2>::Other Cb2;
typedef Fake_CDT_3_cell_base<Cb2> Other;
};
};
template <typename Cb>
std::istream&
operator>>( std::istream& is, Fake_CDT_3_cell_base<Cb>& c) {
char s;
for( int li = 0; li < 4; ++li ) {
if( CGAL::is_ascii(is) )
is >> c.constrained_facet[li];
else
CGAL::read( is, c.constrained_facet[li] );
}
if( CGAL::is_ascii(is) ) {
is >> s;
CGAL_assertion(s == '-');
}
is >> static_cast<typename Fake_CDT_3_cell_base<Cb>::Base&>(c);
for( int li = 0; li < 3; ++li ) {
for( int lj = li+1; lj < 4; ++lj ) {
char s;
is >> s;
if(s == 'C') {
c._restoring[c.to_edge_index(li, lj)] = true;
} else {
if(s != '.') {
std::cerr << "cDT cell:";
for( int li = 0; li < 4; ++li ) {
std::cerr << " " << c.constrained_facet[li];
}
std::cerr << "\n";
std::cerr << "s=" << s << " (" << (int)s
<< "), just before position "
<< is.tellg() << " !\n"; }
CGAL_assertion(s == '.');
c._restoring[c.to_edge_index(li, lj)] = false;
}
}
}
return is;
}
typedef CGAL::Triangulation_data_structure_3<Fake_CDT_3_vertex_base<>, Fake_CDT_3_cell_base<> > Fake_CDT_3_TDS;
typedef CGAL::Triangulation_3<Kernel, Fake_CDT_3_TDS> Fake_CDT_3;
namespace CGAL {
template <>
class Output_rep<Patch_id> {
typedef Patch_id T;
const T& t;
public:
//! initialize with a const reference to \a t.
Output_rep( const T& tt) : t(tt) {}
//! perform the output, calls \c operator\<\< by default.
std::ostream& operator()( std::ostream& out) const {
if(is_ascii(out)) {
out << t;
} else {
CGAL::write(out, t);
}
return out;
}
};
template <>
class Input_rep<Patch_id> {
typedef Patch_id T;
T& t;
public:
//! initialize with a const reference to \a t.
Input_rep( T& tt) : t(tt) {}
//! perform the output, calls \c operator\<\< by default.
std::istream& operator()( std::istream& in) const {
if(is_ascii(in)) {
in >> t;
} else {
CGAL::read(in, t);
}
return in;
}
};
} // end namespace CGAL
struct Update_vertex {
typedef Fake_mesh_domain::Surface_patch_index Sp_index;
template <typename V1, typename V2>
bool operator()(const V1& v1, V2& v2) {
v2.set_point(v1.point());
v2.set_dimension(v1.in_dimension());
v2.set_special(v1.is_special());
switch(v1.in_dimension()) {
case 0:
case 1:
case 3:
v2.set_index(boost::get<int>(v1.index()));
break;
default: // case 2
const typename V1::Index& index = v1.index();
const Sp_index sp_index = boost::get<Sp_index>(index);
v2.set_index(sp_index);
}
return true;
}
}; // end struct Update_vertex
struct Update_cell {
typedef Fake_mesh_domain::Surface_patch_index Sp_index;
template <typename C1, typename C2>
bool operator()(const C1& c1, C2& c2) {
c2.set_subdomain_index(c1.subdomain_index());
for(int i = 0; i < 4; ++i) {
const Sp_index sp_index = c1.surface_patch_index(i);
c2.set_surface_patch_index(i, sp_index);
}
return true;
}
}; // end struct Update_cell
#include <CGAL/Triangulation_file_input.h>
struct Update_vertex_from_CDT_3 {
template <typename V1, typename V2>
bool operator()(const V1& v1, V2& v2) {
v2.set_point(v1.point());
v2.set_dimension(2);
v2.set_special(false);
return true;
}
}; // end struct Update_vertex
struct Update_cell_from_CDT_3 {
typedef Fake_mesh_domain::Surface_patch_index Sp_index;
template <typename C1, typename C2>
bool operator()(const C1& c1, C2& c2) {
c2.set_subdomain_index(1);
for(int i = 0; i < 4; ++i) {
c2.set_surface_patch_index(i, c1.constrained_facet[i]);
}
return true;
}
}; // end struct Update_cell
bool
Polyhedron_demo_c3t3_binary_io_plugin::
try_load_a_cdt_3(std::istream& is, C3t3& c3t3)
{
std::cerr << "Try load a CDT_3...";
CGAL::set_binary_mode(is);
if(CGAL::file_input<
Fake_CDT_3,
C3t3::Triangulation,
Update_vertex_from_CDT_3,
Update_cell_from_CDT_3>(is, c3t3.triangulation()))
{
std::cerr << "Try load a CDT_3... DONE";
return true;
}
else {
return false;
}
}
//Generates a compilation error.
bool
Polyhedron_demo_c3t3_binary_io_plugin::
try_load_other_binary_format(std::istream& is, C3t3& c3t3)
{
CGAL::set_ascii_mode(is);
std::string s;
is >> s;
if (s != "binary" ||
!(is >> s) ||
s != "CGAL" ||
!(is >> s) ||
s != "c3t3")
{
return false;
}
std::getline(is, s);
if(s != "") {
if(s != std::string(" ") + CGAL::Get_io_signature<Fake_c3t3>()()) {
std::cerr << "Polyhedron_demo_c3t3_binary_io_plugin::try_load_other_binary_format:"
<< "\n expected format: " << CGAL::Get_io_signature<Fake_c3t3>()()
<< "\n got format:" << s << std::endl;
return false;
}
}
CGAL::set_binary_mode(is);
std::istream& f_is = CGAL::file_input<
Fake_c3t3::Triangulation,
C3t3::Triangulation,
Update_vertex,
Update_cell>(is, c3t3.triangulation());
return f_is.good();
}
#include <QtPlugin>
#include "C3t3_io_plugin.moc"

View File

@ -22,6 +22,8 @@ include( polyhedron_demo_macros )
qt5_wrap_ui( imgUI_FILES Image_res_dialog.ui)
polyhedron_demo_plugin(io_image_plugin Io_image_plugin ${imgUI_FILES})
target_link_libraries(io_image_plugin scene_segmented_image_item)
polyhedron_demo_plugin(c3t3_io_plugin C3t3_io_plugin)
target_link_libraries(c3t3_io_plugin scene_c3t3_item)
else( Boost_VERSION GREATER 103400 )
message(STATUS "warning: the plugin mesh_3_plugin requires Boost>=1.34.1 and will not be compiled.")
endif( Boost_VERSION GREATER 103400 )

View File

@ -12,7 +12,6 @@
#include <vector>
#include <CGAL/gl.h>
#include <CGAL/Mesh_3/dihedral_angle_3.h>
#include <CGAL/Three/Scene_interface.h>
#include <QGLViewer/manipulatedFrame.h>
@ -48,11 +47,11 @@ void Scene_c3t3_item::compile_shaders()
" \n"
"void main(void) \n"
"{ \n"
" color = vec4(colors,1.0); \n"
" fP = mv_matrix * vertex; \n"
" fN = mat3(mv_matrix)* normals; \n"
" color = vec4(colors,1.0); \n"
" fP = mv_matrix * vertex; \n"
" fN = mat3(mv_matrix)* normals; \n"
" gl_Position = mvp_matrix * \n"
" vec4(radius*vertex.x + center.x, radius* vertex.y + center.y, radius*vertex.z + center.z, 1.0) ; \n"
" vec4(radius*vertex.x + center.x, radius* vertex.y + center.y, radius*vertex.z + center.z, 1.0) ; \n"
"} \n"
};
QOpenGLShader *vertex_shader = new QOpenGLShader(QOpenGLShader::Vertex);
@ -1061,3 +1060,28 @@ void Scene_c3t3_item::compute_elements() const
}
bool Scene_c3t3_item::load_binary(std::istream& is)
{
if(!CGAL::Mesh_3::load_binary_file(is, c3t3())) return false;
// if(!c3t3().triangulation().is_valid()) std::cerr << "INVALID\n";
if(is && frame == 0) {
frame = new qglviewer::ManipulatedFrame();
}
reset_cut_plane();
if(is.good()) {
changed();
return true;
}
else
return false;
}
void
Scene_c3t3_item::reset_cut_plane() {
const Bbox& bbox = this->bbox();
const float xcenter = static_cast<float>((bbox.xmax+bbox.xmin)/2.);
const float ycenter = static_cast<float>((bbox.ymax+bbox.ymin)/2.);
const float zcenter = static_cast<float>((bbox.zmax+bbox.zmin)/2.);
frame->setPosition(qglviewer::Vec(xcenter, ycenter, zcenter));
}

View File

@ -23,6 +23,7 @@
#include <CGAL/Three/Scene_item.h>
#include <Scene_polyhedron_item.h>
#include <Scene_polygon_soup_item.h>
#include <CGAL/IO/File_binary_mesh_3.h>
struct Scene_c3t3_item_priv;
@ -38,6 +39,17 @@ public:
Scene_c3t3_item(const C3t3& c3t3);
~Scene_c3t3_item();
bool save_binary(std::ostream& os) const
{
return CGAL::Mesh_3::save_binary_file(os, c3t3());
}
bool save_ascii(std::ostream& os) const
{
os << "ascii CGAL c3t3 " << CGAL::Get_io_signature<C3t3>()() << "\n";
CGAL::set_ascii_mode(os);
return !!(os << c3t3());
}
void invalidate_buffers()
{
are_buffers_filled = false;
@ -82,6 +94,8 @@ public:
return 0;
}
bool load_binary(std::istream& is);
// data item
const Scene_item* data_item() const;
void set_data_item(const Scene_item* data_item);
@ -97,6 +111,7 @@ public:
void draw_edges(CGAL::Three::Viewer_interface* viewer) const;
void draw_points(CGAL::Three::Viewer_interface * viewer) const;
private:
void reset_cut_plane();
void draw_triangle(const Kernel::Point_3& pa,
const Kernel::Point_3& pb,
const Kernel::Point_3& pc, bool /* is_cut */) const;

View File

@ -0,0 +1,103 @@
// Copyright (c) 1997-2010 INRIA Sophia-Antipolis (France).
// Copyright (c) 2011 GeometryFactory Sarl (France)
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you may redistribute it under
// the terms of the Q Public License version 1.0.
// See the file LICENSE.QPL distributed with CGAL.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Laurent Rineau
//
// Adapted from operator>>(std::istream&, Triangulation_3&) from
// <CGAL/Triangulation_3.h>
#ifndef CGAL_TRIANGULATION_FILE_INPUT_3_H
#define CGAL_TRIANGULATION_FILE_INPUT_3_H
#include <CGAL/basic.h>
namespace CGAL {
template <typename Tr1,
typename Tr2,
typename Update_vertex,
typename Update_cell>
std::istream& file_input(std::istream& is, Tr2 &tr,
Update_vertex update_vertex = Update_vertex(),
Update_cell update_cell = Update_cell())
// reads
// the dimension
// the number of finite vertices
// the non combinatorial information on vertices (point, etc)
// the number of cells
// the cells by the indices of their vertices in the preceding list
// of vertices, plus the non combinatorial information on each cell
// the neighbors of each cell by their index in the preceding list of cells
// when dimension < 3 : the same with faces of maximal dimension
{
typedef Tr2 Triangulation;
typedef typename Triangulation::Vertex_handle Vertex_handle;
typedef typename Triangulation::Cell_handle Cell_handle;
typedef typename Tr1::Vertex Vertex1;
typedef typename Tr1::Cell Cell1;
tr.clear();
tr.tds().cells().clear();
std::size_t n;
int d;
if(is_ascii(is))
is >> d >> n;
else {
read(is, d);
read(is, n);
}
if(!is) return is;
tr.tds().set_dimension(d);
std::map< std::size_t, Vertex_handle > V;
V[0] = tr.infinite_vertex();
// the infinite vertex is numbered 0
for (std::size_t i=1; i <= n; i++) {
V[i] = tr.tds().create_vertex();
Vertex1 v;
if(!(is >> v)) return is;
if(!update_vertex(v, *V[i])) {
is.setstate(std::ios_base::failbit);
return is;
}
}
std::map< std::size_t, Cell_handle > C;
std::size_t m;
tr.tds().read_cells(is, V, m, C);
for (std::size_t j=0 ; j < m; j++) {
Cell1 c;
if(!(is >> c)) return is;
if(!update_cell(c, *(C[j]))) {
is.setstate(std::ios_base::failbit);
return is;
}
}
CGAL_triangulation_assertion( tr.is_valid(false) );
return is;
}
} // end namespace CGAL
#endif // CGAL_TRIANGULATION_FILE_INPUT_3_H

View File

@ -42,6 +42,9 @@ public:
* Example : to filter OFF files : return "OFF files (*.off)"
*/
virtual QString nameFilters() const = 0;
virtual QString saveNameFilters() const {return nameFilters();}
virtual QString loadNameFilters() const {return nameFilters();}
//! Specifies if the io_plugin is able to load an item or not.
virtual bool canLoad() const = 0;
//! Loads an item from a file.