Commit ae918bfb authored by Dirk Wilden's avatar Dirk Wilden
Browse files

obj reader / writer plugin

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@8100 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 8f1d4e4d
include (plugin)
openflipper_plugin ()
/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (C) 2001-2009 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: 7926 $ *
* $Author: wilden $ *
* $Date: 2009-12-15 11:04:11 +0100 (Tue, 15 Dec 2009) $ *
* *
\*===========================================================================*/
#include <QtGui>
#include <QFileInfo>
#include <QSettings>
#include <QPushButton>
#include "FileOBJ.hh"
#include <iostream>
#include <ACG/GL/GLState.hh>
#include "OpenFlipper/BasePlugin/PluginFunctions.hh"
#include "OpenFlipper/common/GlobalOptions.hh"
#include <OpenMesh/Core/IO/IOManager.hh>
#include <OpenFlipper/ACGHelper/DrawModeConverter.hh>
#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
/// Constructor
FileOBJPlugin::FileOBJPlugin()
: loadOptions_(0),
saveOptions_(0),
triMeshHandling_(0),
forceTriangleMesh_(false),
forcePolyMesh_(false)
{
}
//-----------------------------------------------------------------------------------------------------
void FileOBJPlugin::initializePlugin() {
}
//-----------------------------------------------------------------------------------------------------
QString FileOBJPlugin::getLoadFilters() {
return QString( tr("Alias/Wavefront ( *.obj )") );
};
//-----------------------------------------------------------------------------------------------------
QString FileOBJPlugin::getSaveFilters() {
return QString( tr("Alias/Wavefront ( *.obj )") );
};
//-----------------------------------------------------------------------------------------------------
DataType FileOBJPlugin::supportedType() {
DataType type = DATA_POLY_MESH | DATA_TRIANGLE_MESH;
return type;
}
//-----------------------------------------------------------------------------------------------------
void 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 FileOBJPlugin::readMaterial(QString _filename, OBJImporter& _importer)
{
std::string line;
std::string keyWrd;
std::string textureName;
std::string matName;
Material mat;
float f1,f2,f3;
bool insideDefintion = false;
int textureId = 1;
//open stream
std::fstream matStream( _filename.toStdString().c_str(), std::ios_base::in );
if ( !matStream ){
emit log(LOGERR, tr("readMaterial : cannot not open file %1").arg(_filename) );
return false;
}
//clear temp material
mat.cleanup();
//parse material file
while( matStream && !matStream.eof() )
{
std::getline(matStream,line);
if ( matStream.bad() ){
emit log(LOGERR, tr("readMaterial : Warning! Could not read file properly!"));
return false;
}
if ( line.empty() )
continue;
std::stringstream stream(line);
stream >> keyWrd;
if( isspace(line[0]) || line[0] == '#' )
{
if (insideDefintion && !matName.empty() && mat.is_valid())
{
_importer.materials()[matName] = mat;
mat.cleanup();
}
}
else if (keyWrd == "newmtl") // begin new material definition
{
stream >> matName;
insideDefintion = true;
}
else if (keyWrd == "Kd") // diffuse color
{
stream >> f1; stream >> f2; stream >> f3;
if( !stream.fail() )
mat.set_Kd(f1,f2,f3);
}
else if (keyWrd == "Ka") // ambient color
{
stream >> f1; stream >> f2; stream >> f3;
if( !stream.fail() )
mat.set_Ka(f1,f2,f3);
}
else if (keyWrd == "Ks") // specular color
{
stream >> f1; stream >> f2; stream >> f3;
if( !stream.fail() )
mat.set_Ks(f1,f2,f3);
}
#if 0
else if (keyWrd == "illum") // diffuse/specular shading model
{
; // just skip this
}
else if (keyWrd == "Ns") // Shininess [0..200]
{
; // just skip this
}
else if (keyWrd == "map_") // map images
{
// map_Ks, specular map
// map_Ka, ambient map
// map_Bump, bump map
// map_d, opacity map
; // just skip this
}
#endif
else if (keyWrd == "map_Kd" ) {
// Get the rest of the line, removing leading or trailing spaces
// This will define the filename of the texture
std::getline(stream,textureName);
trimString(textureName);
if ( ! textureName.empty() )
mat.set_map_Kd( textureName, textureId++ );
}
else if (keyWrd == "Tr") // transparency value
{
stream >> f1;
if( !stream.fail() )
mat.set_Tr(f1);
}
else if (keyWrd == "d") // transparency value
{
stream >> f1;
if( !stream.fail() )
mat.set_Tr(f1);
}
if ( matStream && insideDefintion && mat.is_valid() && !matName.empty())
_importer.materials()[matName] = mat;
}
// for ( MaterialList::iterator material = _importer.materials().begin(); material != _importer.materials().end(); material++ )
// {
// emit log("Material:" + QString(((*material).first).c_str()) );
// }
emit log( tr("%1 materials loaded.").arg( _importer.materials().size() ) );
return true;
}
//-----------------------------------------------------------------------------
void FileOBJPlugin::addNewObject( OBJImporter& _importer, QString _name )
{
//if no additional object is needed return
if ( _importer.currentObject()+1 >= (int)_importer.objectOptions().size() )
return;
if ( _importer.isTriangleMesh( _importer.currentObject()+1 ) ){
// add a triangle mesh
int id = -1;
emit addEmptyObject(DATA_TRIANGLE_MESH, id);
BaseObjectData* object(0);
if(PluginFunctions::getObject( id, object)){
_importer.addObject( object );
object->path( _importer.path() );
object->setName( _name );
}
} else if ( _importer.isPolyMesh( _importer.currentObject()+1 ) ){
int id = -1;
emit addEmptyObject(DATA_POLY_MESH, id);
BaseObjectData* object(0);
if(PluginFunctions::getObject( id, object)){
_importer.addObject( object );
object->path( _importer.path() );
object->setName( _name );
}
}
//force gui settings
if ( OpenFlipper::Options::gui() ){
if (loadFaceColor_ && !loadFaceColor_->isChecked() )
_importer.objectOptions()[ _importer.currentObject() ] |= OBJImporter::FORCE_NOCOLOR;
if (loadNormals_ && !loadNormals_->isChecked() )
_importer.objectOptions()[ _importer.currentObject() ] |= OBJImporter::FORCE_NONORMALS;
if (loadTexCoords_ && !loadTexCoords_->isChecked() )
_importer.objectOptions()[ _importer.currentObject() ] |= OBJImporter::FORCE_NOTEXTURES;
}
}
//add textures to the mesh
void FileOBJPlugin::addTextures(OBJImporter& _importer, int _objectID ){
// TODO : If only one Texture, use single Texturing mode
if ( true ) {
BaseObject* object = _importer.object(_objectID);
if (!object)
return;
std::map< int,int > newMapping;
// zero ( no texture ) always maps to to zero
newMapping[0]=0;
const std::vector<std::string> matNames = _importer.usedMaterials( _objectID );
for (uint i=0; i < matNames.size(); i++){
Material& material = _importer.materials()[ matNames[i] ];
int textureId = -1;
QString textureBlock = QString( material.map_Kd().c_str());
QStringList options = textureBlock.split(" ",QString::SkipEmptyParts);
while ( options.size() > 1 ) {
if ( options[0] == "-blendu" ) {
} else if ( options[0] == "-blendu" ) {
options.pop_front();
options.pop_front();
} else if ( options[0] == "-blendv" ) {
options.pop_front();
options.pop_front();
} else if ( options[0] == "-cc" ) {
options.pop_front();
options.pop_front();
} else if ( options[0] == "-clamp" ) {
options.pop_front();
options.pop_front();
} else if ( options[0] == "-mm" ) {
options.pop_front();
options.pop_front();
options.pop_front();
} else if ( options[0] == "-o" ) {
options.pop_front();
options.pop_front();
options.pop_front();
options.pop_front();
} else if ( options[0] == "-s" ) {
options.pop_front();
options.pop_front();
options.pop_front();
options.pop_front();
} else if ( options[0] == "-t" ) {
options.pop_front();
options.pop_front();
options.pop_front();
options.pop_front();
} else if ( options[0] == "-texres" ) {
options.pop_front();
options.pop_front();
} else {
break;
}
}
QString fullName = _importer.path() + QDir::separator() + options.join(" ");
QFileInfo info(fullName);
if ( info.exists() )
emit addMultiTexture("OBJ Data", fullName, fullName, object->id(), textureId );
else {
emit log(LOGWARN, tr("Unable to load texture image %1").arg( QString(material.map_Kd().c_str()) ) );
addMultiTexture("OBJ Data","Unknown Texture image " + QString::number(textureId), "unknown.png", object->id(), textureId );
}
newMapping[ material.map_Kd_index() ] = textureId;
}
//now map all texture indices to the real texture indices used in OpenFlipper
OpenMesh::FPropHandleT< int > indexProperty;
//handle PolyMeshes
PolyMeshObject* polyMeshObj = dynamic_cast< PolyMeshObject* > (object);
if ( polyMeshObj ){
PolyMesh& mesh = *(polyMeshObj->mesh());
PolyMesh::FaceIter f_it;
PolyMesh::FaceIter f_end = mesh.faces_end();
if (! mesh.get_property_handle(indexProperty,"OriginalTexIndexMapping") )
return;
for (f_it = mesh.faces_begin(); f_it != f_end; ++f_it)
mesh.property(indexProperty, f_it) = newMapping[ mesh.property(indexProperty, f_it) ];
return;
}
//handle new TriMeshes
TriMeshObject* triMeshObj = dynamic_cast< TriMeshObject* > (object);
if ( triMeshObj ){
TriMesh& mesh = *(triMeshObj->mesh());
TriMesh::FaceIter f_it;
TriMesh::FaceIter f_end = mesh.faces_end();
if (! mesh.get_property_handle(indexProperty,"OriginalTexIndexMapping") )
return;
for (f_it = mesh.faces_begin(); f_it != f_end; ++f_it)
mesh.property(indexProperty, f_it) = newMapping[ mesh.property(indexProperty, f_it) ];
return;
}
}
}
void FileOBJPlugin::readOBJFile(QString _filename, OBJImporter& _importer)
{
QString path = QFileInfo(_filename).absolutePath();
//setup filestream
std::fstream input( _filename.toStdString().c_str(), std::ios_base::in );
if ( !input.is_open() || !input.good() ){
emit log(LOGERR, tr("readOBJFile : cannot not open file %1").arg(_filename) );
return;
}
std::string line;
std::string keyWrd;
float x, y, z, u, v;
std::vector<VertexHandle> vhandles;
std::vector<int> face_texcoords;
std::string matname;
int faceCount = 0;
_importer.setPath( path );
addNewObject(_importer, QFileInfo(_filename).fileName() );
while( input && !input.eof() )
{
std::getline(input,line);
if ( input.bad() ){
emit log(LOGERR, tr("readOBJFile : Warning! Could not read file properly!"));
return;
}
// Trim Both leading and trailing spaces
trimString(line);
// comment
if ( line.size() == 0 || line[0] == '#' || isspace(line[0]) ) {
continue;
}
std::stringstream stream(line);
stream >> keyWrd;
// material file
if (keyWrd == "mtllib")
{
std::string matString;
// This will define the filename of the texture
std::getline(stream,matString);
QString matFile = path + QDir::separator() + QString( matString.c_str() ).trimmed();
emit log( tr("Loading material file: %1").arg( matFile ) );
readMaterial( matFile, _importer );
}
// usemtl
else if (keyWrd == "usemtl")
{
stream >> matname;
if ( _importer.materials().find(matname)==_importer.materials().end() )
{
emit log( LOGERR, tr("Warning! Material '%1' not defined in material file").arg( QString(matname.c_str()) ) );
matname="";
}else{
Material& mat = _importer.materials()[matname];
if ( mat.has_map_Kd() )
_importer.useMaterial( matname );
}
}
// vertex
else if (keyWrd == "v")
{
stream >> x; stream >> y; stream >> z;
if ( !stream.fail() )
_importer.addVertex( OpenMesh::Vec3f(x,y,z) );
}
// texture coord
else if (keyWrd == "vt")
{
stream >> u; stream >> v;
if ( !stream.fail() ){
_importer.addTexCoord( OpenMesh::Vec2f(u, v) );
}else{
emit log( LOGERR, tr("Only single 2D texture coordinate per vertex allowed") );
return;
}
}
// normal
else if (keyWrd == "vn")
{
stream >> x; stream >> y; stream >> z;
if ( !stream.fail() ){
_importer.addNormal( OpenMesh::Vec3f(x,y,z) );
}
}
// group
else if (keyWrd == "g"){
std::string groupName;
std::getline(stream,groupName);
if ( faceCount > 0 )
addNewObject( _importer, QString(groupName.c_str()) );
else
_importer.setObjectName( _importer.currentObject(), QString(groupName.c_str()) );
//since obj-groups are used, all new objects will be grouped together in OpenFlipper
_importer.setGroup( QFileInfo(_filename).fileName() );
faceCount = 0;
}
// face
else if (keyWrd == "f")
{
int component(0), nV(0);
int value;
vhandles.clear();
face_texcoords.clear();
// read full line after detecting a face
std::string faceLine;
std::getline(stream,faceLine);
std::stringstream lineData( faceLine );
// work on the line until nothing left to read
while ( !lineData.eof() )
{
// read one block from the line ( vertex/texCoord/normal )
std::string vertex;
lineData >> vertex;
do{
//get the component (vertex/texCoord/normal)
size_t found=vertex.find("/");
// parts are seperated by '/' So if no '/' found its the last component
if( found != std::string::npos ){
// read the index value
std::stringstream tmp( vertex.substr(0,found) );
// If we get an empty string this property is undefined in the file
if ( vertex.substr(0,found).empty() ) {
// Switch to next field
vertex = vertex.substr(found+1);
// Now we are at the next component
++component;
// Skip further processing of this component
continue;
}
// Read current value
tmp >> value;