Commit 1ba42445 authored by Jan Möbius's avatar Jan Möbius

Plugin Collections for file, type and postprocessor plugins



git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@15803 383ad7c9-94d9-4d36-a494-682f7c89f535
parents
include (plugin)
openflipper_plugin ()
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen *
* www.openflipper.org *
* *
*--------------------------------------------------------------------------- *
* This file is part of OpenFlipper. *
* *
* OpenFlipper is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenFlipper is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU LesserGeneral Public *
* License along with OpenFlipper. If not, *
* see <http://www.gnu.org/licenses/>. *
* *
\*===========================================================================*/
/*===========================================================================*\
* *
* $Revision$ *
* $LastChangedBy$ *
* $Date$ *
* *
\*===========================================================================*/
#include <QtGui>
#include <QFileInfo>
#include <QSettings>
#include <QPushButton>
#include "FileOFF.hh"
#include <iostream>
#include <fstream>
#include <sstream>
#include <ACG/GL/GLState.hh>
#include "OpenFlipper/BasePlugin/PluginFunctions.hh"
#include "OpenFlipper/common/GlobalOptions.hh"
#include <OpenMesh/Core/IO/IOManager.hh>
#include <OpenMesh/Core/Utils/color_cast.hh>
#include <OpenFlipper/ACGHelper/DrawModeConverter.hh>
// Defines for the type handling drop down box
#define TYPEAUTODETECT 0
#define TYPEASK 1
#define TYPEPOLY 2
#define TYPETRIANGLE 3
/// Constructor
FileOFFPlugin::FileOFFPlugin()
: loadOptions_(0),
saveOptions_(0),
saveBinary_(0),
saveVertexColor_(0),
saveFaceColor_(0),
saveAlpha_(0),
saveNormals_(0),
saveTexCoords_(0),
savePrecisionLabel_(0),
savePrecision_(0),
saveDefaultButton_(0),
triMeshHandling_(0),
loadVertexColor_(0),
loadFaceColor_(0),
loadAlpha_(0),
loadNormals_(0),
loadTexCoords_(0),
loadCheckManifold_(0),
loadDefaultButton_(0),
userReadOptions_(0),
userWriteOptions_(0),
forceTriangleMesh_(false),
forcePolyMesh_(false),
readColorComp_(false) {
}
//-----------------------------------------------------------------------------------------------------
void FileOFFPlugin::initializePlugin() {
// Initialize standard options that can then be changed in the file dialogs
if(OpenFlipperSettings().value("FileOff/Load/VertexColor",true).toBool())
userReadOptions_ |= OFFImporter::VERTEXCOLOR;
if(OpenFlipperSettings().value("FileOff/Load/FaceColor",true).toBool())
userReadOptions_ |= OFFImporter::FACECOLOR;
if(OpenFlipperSettings().value("FileOff/Load/Alpha",true).toBool())
userReadOptions_ |= OFFImporter::COLORALPHA;
if(OpenFlipperSettings().value("FileOff/Load/Normal",true).toBool())
userReadOptions_ |= OFFImporter::VERTEXNORMAL;
if(OpenFlipperSettings().value("FileOff/Load/TexCoords",true).toBool())
userReadOptions_ |= OFFImporter::VERTEXTEXCOORDS;
if(OpenFlipperSettings().value("FileOff/Save/Binary",true).toBool())
userWriteOptions_ |= OFFImporter::BINARY;
if(OpenFlipperSettings().value("FileOff/Save/VertexColor",true).toBool())
userWriteOptions_ |= OFFImporter::VERTEXCOLOR;
if(OpenFlipperSettings().value("FileOff/Save/FaceColor",true).toBool())
userWriteOptions_ |= OFFImporter::FACECOLOR;
if(OpenFlipperSettings().value("FileOff/Save/Alpha",true).toBool())
userWriteOptions_ |= OFFImporter::COLORALPHA;
if(OpenFlipperSettings().value("FileOff/Save/Normal",true).toBool())
userWriteOptions_ |= OFFImporter::VERTEXNORMAL;
if(OpenFlipperSettings().value("FileOff/Save/TexCoords",true).toBool())
userWriteOptions_ |= OFFImporter::VERTEXTEXCOORDS;
}
//-----------------------------------------------------------------------------------------------------
QString FileOFFPlugin::getLoadFilters() {
return QString( tr("Object File Format files ( *.off )") );
};
//-----------------------------------------------------------------------------------------------------
QString FileOFFPlugin::getSaveFilters() {
return QString( tr("Object File Format files ( *.off )") );
};
//-----------------------------------------------------------------------------------------------------
DataType FileOFFPlugin::supportedType() {
DataType type = DATA_POLY_MESH | DATA_TRIANGLE_MESH;
return type;
}
//-----------------------------------------------------------------------------------------------------
void FileOFFPlugin::trimString( std::string& _string) {
// Trim Both leading and trailing spaces
size_t start = _string.find_first_not_of(" \t\r\n");
size_t end = _string.find_last_not_of(" \t\r\n");
if(( std::string::npos == start ) || ( std::string::npos == end))
_string = "";
else
_string = _string.substr( start, end-start+1 );
}
//-----------------------------------------------------------------------------------------------------
bool FileOFFPlugin::getCleanLine( std::istream& ifs , std::string& _string, bool _skipEmptyLines) {
// while we are not at the end of the file
while (true) {
// get the current line:
std::getline(ifs,_string);
// Remove whitespace at beginning and end
trimString(_string);
// Check if string is not empty ( otherwise we continue
if ( _string.size() != 0 ) {
// Check if string is a comment ( starting with # )
if ( _string[0] != '#') {
return true;
}
} else {
if ( !_skipEmptyLines )
return true;
}
if ( ifs.eof() ) {
std::cerr << "End of file reached while searching for input!" << std::endl;
return false;
}
}
return false;
}
//-----------------------------------------------------------------------------------------------------
void FileOFFPlugin::updateUserOptions() {
// If the options dialog has not been initialized, keep
// the initial values
if( OpenFlipper::Options::nogui() )
return;
// Load options
if(loadVertexColor_) {
if(loadVertexColor_->isChecked()) userReadOptions_ |= OFFImporter::VERTEXCOLOR;
else { if(userReadOptions_ & OFFImporter::VERTEXCOLOR) userReadOptions_ -= OFFImporter::VERTEXCOLOR; }
}
if(loadFaceColor_) {
if(loadFaceColor_->isChecked()) userReadOptions_ |= OFFImporter::FACECOLOR;
else { if(userReadOptions_ & OFFImporter::FACECOLOR) userReadOptions_ -= OFFImporter::FACECOLOR; }
}
if(loadAlpha_) {
if(loadAlpha_->isChecked()) userReadOptions_ |= OFFImporter::COLORALPHA;
else { if(userReadOptions_ & OFFImporter::COLORALPHA) userReadOptions_ -= OFFImporter::COLORALPHA; }
}
if(loadNormals_) {
if(loadNormals_->isChecked()) userReadOptions_ |= OFFImporter::VERTEXNORMAL;
else { if(userReadOptions_ & OFFImporter::VERTEXNORMAL) userReadOptions_ -= OFFImporter::VERTEXNORMAL; }
}
if(loadTexCoords_) {
if(loadTexCoords_->isChecked()) userReadOptions_ |= OFFImporter::VERTEXTEXCOORDS;
else { if(userReadOptions_ & OFFImporter::VERTEXTEXCOORDS) userReadOptions_ -= OFFImporter::VERTEXTEXCOORDS; }
}
// Save options
if(saveBinary_) {
if(saveBinary_->isChecked()) userWriteOptions_ |= OFFImporter::BINARY;
else { if(userWriteOptions_ & OFFImporter::BINARY) userWriteOptions_ -= OFFImporter::BINARY; }
}
if(saveVertexColor_) {
if(saveVertexColor_->isChecked()) userWriteOptions_ |= OFFImporter::VERTEXCOLOR;
else { if(userWriteOptions_ & OFFImporter::VERTEXCOLOR) userWriteOptions_ -= OFFImporter::VERTEXCOLOR; }
}
if(saveFaceColor_) {
if(saveFaceColor_->isChecked()) userWriteOptions_ |= OFFImporter::FACECOLOR;
else { if(userWriteOptions_ & OFFImporter::FACECOLOR) userWriteOptions_ -= OFFImporter::FACECOLOR; }
}
if(saveAlpha_) {
if(saveAlpha_->isChecked()) userWriteOptions_ |= OFFImporter::COLORALPHA;
else { if(userWriteOptions_ & OFFImporter::COLORALPHA) userWriteOptions_ -= OFFImporter::COLORALPHA; }
}
if(saveNormals_) {
if(saveNormals_->isChecked()) userWriteOptions_ |= OFFImporter::VERTEXNORMAL;
else { if(userWriteOptions_ & OFFImporter::VERTEXNORMAL) userWriteOptions_ -= OFFImporter::VERTEXNORMAL; }
}
if(saveTexCoords_) {
if(saveTexCoords_->isChecked()) userWriteOptions_ |= OFFImporter::VERTEXTEXCOORDS;
else { if(userWriteOptions_ & OFFImporter::VERTEXTEXCOORDS) userWriteOptions_ -= OFFImporter::VERTEXTEXCOORDS; }
}
}
//-----------------------------------------------------------------------------------------------------
bool FileOFFPlugin::readFileOptions(QString _filename, OFFImporter& _importer) {
/* Constitution of an OFF-file
==================================================================
[ST] [C] [N] [4][n]OFF [BINARY] # comment
nV nF nE # number of vertices, faces and edges (edges are skipped)
v[0] v[1] v[2] [n[0] n[1] n[2]] [c[0] c[1] c[1]] [t[0] t[0]]
...
faceValence vIdx[0] ... vIdx[faceValence-1] colorspec
...
==================================================================
*/
const unsigned int LINE_LEN = 4096;
std::ifstream ifs(_filename.toUtf8(), std::ios_base::binary);
if ( (!ifs.is_open()) || (!ifs.good())) {
emit log(LOGERR, tr("Error: Could not read file options of specified OFF-file! Aborting."));
return false;
}
// read 1st line
char line[LINE_LEN], *p;
ifs.getline(line, LINE_LEN);
p = line;
int remainingChars = ifs.gcount();
// check header: [ST][C][N][4][n]OFF BINARY
while(remainingChars > 0) {
if ( ( remainingChars > 1 ) && ( p[0] == 'S' && p[1] == 'T') ) {
_importer.addOption(OFFImporter::VERTEXTEXCOORDS);
p += 2;
remainingChars -= 2;
} else if ( ( remainingChars > 0 ) && ( p[0] == 'C') ) {
_importer.addOption(OFFImporter::VERTEXCOLOR);
++p;
--remainingChars;
} else if ( ( remainingChars > 0 ) && ( p[0] == 'N') ) {
_importer.addOption(OFFImporter::VERTEXNORMAL);
++p;
--remainingChars;
} else if ( ( remainingChars > 0 ) && (p[0] == '3' ) ) {
++p;
--remainingChars;
} else if ( ( remainingChars > 0 ) && (p[0] == '4' ) ) {
/// \todo Implement extended coordinates
std::cerr << "Error: Extended coordinates are currently not supported!" << std::endl;
ifs.close();
return false;
} else if ( ( remainingChars > 0 ) && (p[0] == 'n' ) ) {
/// \todo Implement any space dimension
std::cerr << "Error: n-dimensional coordinates are currently not supported!" << std::endl;
ifs.close();
return false;
} else if ( ( remainingChars >= 3 ) && (p[0] == 'O' && p[1] == 'F' && p[2] == 'F') ) {
// Skip "OFF " (plus space):
p += 4;
remainingChars -= 4;
} else if ( ( remainingChars >= 6 ) && ( strncmp(p, "BINARY", 6) == 0 ) ) {
_importer.addOption(OFFImporter::BINARY);
p += 6;
remainingChars -= 6;
} else if ( ( remainingChars > 0 ) && ( p[0] == '#' ) ) {
// Skip the rest of the line since it's a comment
remainingChars = 0;
} else {
// Skip unknown character or space
++p;
--remainingChars;
}
}
// Now extract data type by iterating over
// the face valences
unsigned int nV, nF, dummy_uint;
unsigned int vertexCount = 0;
unsigned int tmp_count = 0;
std::string trash;
std::string str;
std::istringstream sstr;
if(_importer.isBinary()) {
// Parse BINARY file
float dummy_f;
// + #Vertices, #Faces, #Edges
readValue(ifs, nV);
readValue(ifs, nF);
readValue(ifs, dummy_uint);
for (uint i=0; i<nV && !ifs.eof(); ++i) {
// Skip vertices
for(int i = 0; i < 3; ++i) readValue(ifs, dummy_f);
if ( _importer.hasVertexNormals() ) {
for(int i = 0; i < 3; ++i) readValue(ifs, dummy_f);
}
if ( _importer.hasVertexColors() ) {
for(int i = 0; i < 3; ++i) readValue(ifs, dummy_f);
}
if ( _importer.hasTextureCoords() ) {
for(int i = 0; i < 2; ++i) readValue(ifs, dummy_f);
}
}
for (uint i=0; i<nF; ++i) {
// Get valence of current face
readValue(ifs, tmp_count);
if (ifs.eof())
break;
if(tmp_count > vertexCount) vertexCount = tmp_count;
// Skip the rest
// Vertex indices
for(uint i = 0; i < tmp_count; ++i) readValue(ifs, dummy_uint);
// Get number of color components
readValue(ifs, tmp_count);
if(!_importer.hasFaceColors() && tmp_count > 0) {
_importer.addOption(OFFImporter::FACECOLOR);
}
// Face color
for (uint i = 0; i < tmp_count; ++i) {
readValue(ifs, dummy_f);
}
}
} else {
// Parse ASCII file
// Get whole line since there could be comments in it
getCleanLine(ifs, str);
sstr.str(str);
// check if #vertices, #faces and #edges follow
// on the next line
if ( str.compare("OFF") == 0 ) {
getCleanLine(ifs, str);
sstr.str(str);
}
// + #Vertices, #Faces, #Edges
sstr >> nV;
sstr >> nF;
sstr >> dummy_uint;
// Skip vertices
for(unsigned int i = 0; i < nV; ++i) {
getCleanLine(ifs, trash);
}
trash = "";
// Count vertices per face
for(unsigned int i = 0; i < nF; ++i) {
sstr.clear();
getCleanLine(ifs, trash);
sstr.str(trash);
sstr >> tmp_count;
if(tmp_count > vertexCount) vertexCount = tmp_count;
// Skip vertex indices
for(uint i = 0; i < tmp_count; ++i) {
sstr >> dummy_uint;
}
// Look if there's at least one face color specified
// Note: Comments should not be here, so don't treat them
if(!_importer.hasFaceColors()) {
if(!sstr.eof()) {
_importer.addOption(OFFImporter::FACECOLOR);
}
}
}
}
ifs.close();
_importer.maxFaceValence(vertexCount);
if(vertexCount == 3) {
_importer.addOption(OFFImporter::TRIMESH);
_importer.removeOption(OFFImporter::POLYMESH);
} else if (vertexCount == 0 && nF != 0) {
// Something went wrong
return false;
} else {
_importer.addOption(OFFImporter::POLYMESH);
_importer.removeOption(OFFImporter::TRIMESH);
}
return true;
}
//-----------------------------------------------------------------------------------------------------
bool FileOFFPlugin::readOFFFile(QString _filename, OFFImporter& _importer) {
if ( !QFile(_filename).exists() ){
emit log(LOGERR, tr("Unable to load OFF file. File not found!"));
return false;
}
if(!readFileOptions(_filename, _importer)) {
return false;
}
// Let's see if the user has specified some options
updateUserOptions();
std::ifstream ifile(_filename.toUtf8(), (_importer.isBinary() ? std::ios::binary | std::ios::in
: std::ios::in) );
if (!ifile.is_open() || !ifile.good())
{
emit log(LOGERR, tr("Cannot open OFF file for reading!"));
return false;
}
assert(ifile);
int triMeshControl = TYPEAUTODETECT; // 0 == Auto-Detect
if ( OpenFlipper::Options::gui() ){
if ( triMeshHandling_ != 0 ){
triMeshControl = triMeshHandling_->currentIndex();
} else {
triMeshControl = TYPEAUTODETECT;
}
}
if ( forceTriangleMesh_ )
triMeshControl = TYPETRIANGLE;
if ( forcePolyMesh_ )
triMeshControl = TYPEPOLY;
DataType type = DATA_TRIANGLE_MESH;
switch (triMeshControl) {
case TYPEAUTODETECT:
// Auto-detect
type = _importer.isTriangleMesh() ? DATA_TRIANGLE_MESH : DATA_POLY_MESH;
break;
case TYPEASK:
if( !OpenFlipper::Options::nogui() ) {
// Create message box
QMessageBox msgBox;
QPushButton *detectButton = msgBox.addButton(tr("Auto-Detect"), QMessageBox::ActionRole);
QPushButton *triButton = msgBox.addButton(tr("Open as triangle mesh"), QMessageBox::ActionRole);
QPushButton *polyButton = msgBox.addButton(tr("Open as poly mesh"), QMessageBox::ActionRole);
msgBox.setWindowTitle( tr("Mesh types in file") );
msgBox.setText( tr("You are about to open a file containing one or more mesh types. \n\n Which mesh type should be used?") );
msgBox.setDefaultButton( detectButton );
msgBox.exec();
if (msgBox.clickedButton() == triButton)
type = DATA_TRIANGLE_MESH;
else if (msgBox.clickedButton() == polyButton)
type = DATA_POLY_MESH;
else
type = _importer.isTriangleMesh() ? DATA_TRIANGLE_MESH : DATA_POLY_MESH;
} else {
// No gui mode
type = _importer.isTriangleMesh() ? DATA_TRIANGLE_MESH : DATA_POLY_MESH;
}
break;
case TYPEPOLY:
// Always load as PolyMesh
type = DATA_POLY_MESH;
break;
case TYPETRIANGLE:
// Always load as TriangleMesh
type = DATA_TRIANGLE_MESH;
break;
default:
break;
}
return _importer.isBinary() ? parseBinary(ifile, _importer, type, _filename) : parseASCII(ifile, _importer, type, _filename);
}
//-----------------------------------------------------------------------------------------------------
bool FileOFFPlugin::parseASCII(std::istream& _in, OFFImporter& _importer, DataType _type, QString& _objectName) {
unsigned int idx;
unsigned int nV, nF, dummy;
OpenMesh::Vec3f v, n;
OpenMesh::Vec2f t;
OpenMesh::Vec3i c3;
OpenMesh::Vec3f c3f;
OpenMesh::Vec4i c4;
OpenMesh::Vec4f c4f;
std::vector<VertexHandle> vhandles;
VertexHandle vh;
FaceHandle fh;
int objectId = -1;
emit addEmptyObject(_type, objectId);
BaseObject* object(0);
if(!PluginFunctions::getObject( objectId, object )) {
emit log(LOGERR, tr("Could not create new object!"));
return false;
}
// Set object's name to match file name
QFileInfo f(_objectName);
object->setName(f.fileName());
// Set initial object
_importer.addObject(object);
std::string line;
std::istringstream sstr;
// read header line
getCleanLine(_in, line);
// + #Vertices, #Faces, #Edges
// Note: We use a stringstream because there
// could be comments in the line
getCleanLine(_in, line);
sstr.str(line);
sstr >> nV;
sstr >> nF;
sstr >> dummy;
// Reserve memory
_importer.reserve(nV, nF * _importer.maxFaceValence() /*Upper bound*/, nF);
// skip empty lines and comments
std::string tmp;
while (true) {
char c = _in.peek();
if ( (c == '\n') || (c == '#') )
std::getline(_in, tmp);
else
break;
}
// read vertices: coord [hcoord] [normal] [color] [texcoord]
for (uint i=0; i<nV && !_in.eof(); ++i) {
// Always read VERTEX
_in >> v[0] >> v[1] >> v[2];
vh = _importer.addVertex(v);
// perhaps read NORMAL
if ( _importer.hasVertexNormals() ){
_in >> n[0] >> n[1] >> n[2];
if(userReadOptions_ & OFFImporter::VERTEXNORMAL) {
int nid = _importer.addNormal(n);
_importer.setNormal(vh, nid);
}
}
sstr.clear();
getCleanLine(_in, line, false);
sstr.str(line);
int colorType = getColorType(line, _importer.hasTextureCoords() );
//perhaps read COLOR
if ( _importer.hasVertexColors() ){
std::string trash;
switch (colorType){
case 0 : break; //no color
case 1 : sstr >> trash; break; //one int (isn't handled atm)
case 2 : sstr >> trash; sstr >> trash; break; //corrupt format (ignore)
// rgb int
case 3 : sstr >> c3[0]; sstr >> c3[1]; sstr >> c3[2];
if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>( c3 ) );
_importer.setVertexColor(vh, cidx);
}
break;
// rgba int
case 4 : sstr >> c4[0]; sstr >> c4[1]; sstr >> c4[2]; sstr >> c4[3];
if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>( c4 ) );
_importer.setVertexColor(vh, cidx);
_importer.addOption(OFFImporter::COLORALPHA);
}
break;
// rgb floats
case 5 : sstr >> c3f[0]; sstr >> c3f[1]; sstr >> c3f[2];
if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>(c3f) );
_importer.setVertexColor(vh, cidx);
}
break;
// rgba floats
case 6 : sstr >> c4f[0]; sstr >> c4f[1]; sstr >> c4f[2]; sstr >> c4f[3];
if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>(c4f) );
_importer.setVertexColor(vh, cidx);
_importer.addOption(OFFImporter::COLORALPHA);
}
break;