Files
OpenSceneGraph/src/osgPlugins/obj/obj.cpp
2004-08-27 16:14:21 +00:00

502 lines
18 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2004 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library 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
* OpenSceneGraph Public License for more details.
*/
#include <fstream>
#include "obj.h"
#include <osg/Notify>
using namespace obj;
bool Model::readline(std::istream& fin, char* line, const int LINE_SIZE)
{
if (LINE_SIZE<1) return false;
bool eatWhiteSpaceAtStart = true;
char* ptr = line;
char* end = line+LINE_SIZE-1;
bool skipNewline = false;
while (fin && ptr<end)
{
int c=fin.get();
int p=fin.peek();
if (c=='\r')
{
if (p=='\n')
{
// we have a windows line endings.
fin.get();
// osg::notify(osg::NOTICE)<<"We have dos line ending"<<std::endl;
if (skipNewline)
{
skipNewline = false;
continue;
}
else break;
}
// we have Mac line ending
// osg::notify(osg::NOTICE)<<"We have mac line ending"<<std::endl;
if (skipNewline)
{
skipNewline = false;
continue;
}
else break;
}
else if (c=='\n')
{
// we have unix line ending.
// osg::notify(osg::NOTICE)<<"We have unix line ending"<<std::endl;
if (skipNewline)
{
skipNewline = false;
continue;
}
else break;
}
else if (c=='\\' && (p=='\r' || p=='\n'))
{
// need to keep return;
skipNewline = true;
}
else if (c!=std::ifstream::traits_type::eof()) // don't copy eof.
{
skipNewline = false;
if (!eatWhiteSpaceAtStart || (c!=' ' && c!='\t'))
{
eatWhiteSpaceAtStart = false;
*ptr++ = c;
}
}
}
*ptr = 0;
return true;
}
bool Model::readMTL(std::istream& fin)
{
osg::notify(osg::INFO)<<"Reading MTL file"<<std::endl;
const int LINE_SIZE = 4096;
char line[LINE_SIZE];
float r = 1.0f, g = 1.0f, b = 1.0f, a = 1.0f;
Material* material = &(materialMap[""]);
while (fin)
{
readline(fin,line,LINE_SIZE);
if (line[0]=='#')
{
// comment line
// osg::notify(osg::NOTICE) <<"Comment: "<<line<<std::endl;
}
else if (strlen(line)>0)
{
if (strncmp(line,"Ka ",3)==0)
{
unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
if (fieldsRead==1) material->ambient.set(r,0.0f,0.0f,1.0f);
else if (fieldsRead==2) material->ambient.set(r,g,0.0f,1.0f);
else if (fieldsRead==3) material->ambient.set(r,g,b,1.0f);
else if (fieldsRead==4) material->ambient.set(r,g,b,a);
}
else if (strncmp(line,"Kd ",3)==0)
{
unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
if (fieldsRead==1) material->diffuse.set(r,0.0f,0.0f,1.0f);
else if (fieldsRead==2) material->diffuse.set(r,g,0.0f,1.0f);
else if (fieldsRead==3) material->diffuse.set(r,g,b,1.0f);
else if (fieldsRead==4) material->diffuse.set(r,g,b,a);
}
else if (strncmp(line,"Ks ",3)==0)
{
unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
if (fieldsRead==1) material->specular.set(r,0.0f,0.0f,1.0f);
else if (fieldsRead==2) material->specular.set(r,g,0.0f,1.0f);
else if (fieldsRead==3) material->specular.set(r,g,b,1.0f);
else if (fieldsRead==4) material->specular.set(r,g,b,a);
}
else if (strncmp(line,"Ke ",3)==0)
{
unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
if (fieldsRead==1) material->emissive.set(r,0.0f,0.0f,1.0f);
else if (fieldsRead==2) material->emissive.set(r,g,0.0f,1.0f);
else if (fieldsRead==3) material->emissive.set(r,g,b,1.0f);
else if (fieldsRead==4) material->emissive.set(r,g,b,a);
}
else if (strncmp(line,"Tf ",3)==0)
{
unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
if (fieldsRead==1) material->Tf.set(r,0.0f,0.0f,1.0f);
else if (fieldsRead==2) material->Tf.set(r,g,0.0f,1.0f);
else if (fieldsRead==3) material->Tf.set(r,g,b,1.0f);
else if (fieldsRead==4) material->Tf.set(r,g,b,a);
}
else if (strncmp(line,"newmtl ",7)==0)
{
std::string materialName(line+7);
if (material->name != materialName)
{
material = & materialMap[materialName];
material->name = materialName;
}
}
else if (strncmp(line,"sharpness ",10)==0)
{
float sharpness = 0.0f;
unsigned int fieldsRead = sscanf(line+10,"%f", &sharpness);
if (fieldsRead==1) material->sharpness = sharpness;
}
else if (strncmp(line,"illum ",6)==0)
{
int illum = 0;
unsigned int fieldsRead = sscanf(line+6,"%d", &illum);
if (fieldsRead==1) material->illum = illum;
}
else if (strncmp(line,"Ns ",3)==0)
{
int Ns = 0;
unsigned int fieldsRead = sscanf(line+3,"%d", &Ns);
if (fieldsRead==1) material->Ns = Ns;
}
else if (strncmp(line,"Ni ",3)==0)
{
int Ni = 0;
unsigned int fieldsRead = sscanf(line+3,"%d", &Ni);
if (fieldsRead==1) material->Ni = Ni;
}
else if (strncmp(line,"illum ",6)==0)
{
int illum = 0;
unsigned int fieldsRead = sscanf(line+6,"%d", &illum);
if (fieldsRead==1) material->illum = illum;
}
else if (strncmp(line,"Tr ",3)==0)
{
float alpha=1.0f;
unsigned int fieldsRead = sscanf(line+3,"%f", &alpha);
if (fieldsRead==1)
{
material->ambient[3] = alpha;
material->diffuse[3] = alpha;
material->specular[3] = alpha;
material->emissive[3] = alpha;
}
}
else if (strncmp(line,"d ",2)==0)
{
float alpha=1.0f;
unsigned int fieldsRead = sscanf(line+2,"%f", &alpha);
if (fieldsRead==1)
{
material->ambient[3] = alpha;
material->diffuse[3] = alpha;
material->specular[3] = alpha;
material->emissive[3] = alpha;
}
}
else if (strncmp(line,"map_Ka ",7)==0)
{
std::string filename(line+7);
material->map_Ka = filename;
}
else if (strncmp(line,"map_Kd ",7)==0)
{
std::string filename(line+7);
material->map_Kd = filename;
}
else if (strncmp(line,"map_Ks ",7)==0)
{
std::string filename(line+7);
material->map_Ks = filename;
}
else
{
osg::notify(osg::NOTICE) <<"*** line not handled *** :"<<line<<std::endl;
}
}
}
return true;
}
bool Model::readOBJ(std::istream& fin)
{
osg::notify(osg::INFO)<<"Reading OBJ file"<<std::endl;
const int LINE_SIZE = 4096;
char line[LINE_SIZE];
float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f;
while (fin)
{
readline(fin,line,LINE_SIZE);
if (line[0]=='#')
{
// comment line
// osg::notify(osg::NOTICE) <<"Comment: "<<line<<std::endl;
}
else if (strlen(line)>0)
{
if (strncmp(line,"v ",2)==0)
{
unsigned int fieldsRead = sscanf(line+2,"%f %f %f %f", &x, &y, &z, &w);
if (fieldsRead==1) vertices.push_back(osg::Vec3(x,0.0f,0.0f));
else if (fieldsRead==2) vertices.push_back(osg::Vec3(x,y,0.0f));
else if (fieldsRead==3) vertices.push_back(osg::Vec3(x,y,z));
else if (fieldsRead>=4) vertices.push_back(osg::Vec3(x/w,y/w,z/w));
}
else if (strncmp(line,"vn ",3)==0)
{
unsigned int fieldsRead = sscanf(line+3,"%f %f %f", &x, &y, &z);
if (fieldsRead==1) normals.push_back(osg::Vec3(x,0.0f,0.0f));
else if (fieldsRead==2) normals.push_back(osg::Vec3(x,y,0.0f));
else if (fieldsRead==3) normals.push_back(osg::Vec3(x,y,z));
}
else if (strncmp(line,"vt ",3)==0)
{
unsigned int fieldsRead = sscanf(line+3,"%f %f %f", &x, &y, &z);
if (fieldsRead==1) texcoords.push_back(osg::Vec2(x,0.0f));
else if (fieldsRead==2) texcoords.push_back(osg::Vec2(x,y));
else if (fieldsRead==3) texcoords.push_back(osg::Vec2(x,y));
}
else if (strncmp(line,"l ",2)==0 ||
strncmp(line,"p ",2)==0 ||
strncmp(line,"f ",2)==0)
{
char* ptr = line+2;
Element* element = new Element( (line[0]=='p') ? Element::POINTS :
(line[0]=='l') ? Element::POLYLINE :
Element::POLYGON );
// osg::notify(osg::NOTICE)<<"face"<<ptr<<std::endl;
int vi=0, ti=0, ni=0;
while(*ptr!=0)
{
// skip white space
while(*ptr==' ') ++ptr;
if (sscanf(ptr, "%d/%d/%d", &vi, &ti, &ni) == 3)
{
// osg::notify(osg::NOTICE)<<" vi="<<vi<<"/ti="<<ti<<"/ni="<<ni<<std::endl;
element->vertexIndices.push_back(remapVertexIndex(vi));
element->normalIndices.push_back(remapNormalIndex(ni));
element->texCoordIndices.push_back(remapTexCoordIndex(ti));
}
else if (sscanf(ptr, "%d//%d", &vi, &ni) == 2)
{
// osg::notify(osg::NOTICE)<<" vi="<<vi<<"//ni="<<ni<<std::endl;
element->vertexIndices.push_back(remapVertexIndex(vi));
element->normalIndices.push_back(remapNormalIndex(ni));
}
else if (sscanf(ptr, "%d/%d", &vi, &ti) == 2)
{
// osg::notify(osg::NOTICE)<<" vi="<<vi<<"/ti="<<ti<<std::endl;
element->vertexIndices.push_back(remapVertexIndex(vi));
element->texCoordIndices.push_back(remapTexCoordIndex(ti));
}
else if (sscanf(ptr, "%d", &vi) == 1)
{
// osg::notify(osg::NOTICE)<<" vi="<<vi<<std::endl;
element->vertexIndices.push_back(remapVertexIndex(vi));
}
// skip to white space or end of line
while(*ptr!=' ' && *ptr!=0) ++ptr;
}
if (!element->normalIndices.empty() && element->normalIndices.size() != element->vertexIndices.size())
{
element->normalIndices.clear();
}
if (!element->texCoordIndices.empty() && element->texCoordIndices.size() != element->vertexIndices.size())
{
element->texCoordIndices.clear();
}
if (!element->vertexIndices.empty())
{
Element::CoordinateCombination coordateCombination = element->getCoordinateCombination();
if (coordateCombination!=currentElementState.coordinateCombination)
{
currentElementState.coordinateCombination = coordateCombination;
currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
}
addElement(element);
}
else
{
// empty element, don't both adding, just unref to delete it.
element->unref();
}
}
else if (strncmp(line,"usemtl ",7)==0)
{
std::string materialName(line+7);
if (currentElementState.materialName != materialName)
{
currentElementState.materialName = materialName;
currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
}
}
else if (strncmp(line,"mtllib ",7)==0)
{
std::ifstream mfin(line+7);
if (mfin)
{
readMTL(mfin);
}
}
else if (strncmp(line,"o ",2)==0)
{
std::string objectName(line+2);
if (currentElementState.objectName != objectName)
{
currentElementState.objectName = objectName;
currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
}
}
else if (strncmp(line,"g ",2)==0)
{
std::string groupName(line+2);
if (currentElementState.groupName != groupName)
{
currentElementState.groupName = groupName;
currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
}
}
else if (strncmp(line,"s ",2)==0)
{
int smoothingGroup=0;
if (strncmp(line+2,"off",3)==0) smoothingGroup = 0;
else sscanf(line+2,"%d",&smoothingGroup);
if (currentElementState.smoothingGroup != smoothingGroup)
{
currentElementState.smoothingGroup = smoothingGroup;
currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
}
}
else
{
osg::notify(osg::NOTICE) <<"*** line not handled *** :"<<line<<std::endl;
}
}
}
#if 0
osg::notify(osg::NOTICE) <<"vertices :"<<vertices.size()<<std::endl;
osg::notify(osg::NOTICE) <<"normals :"<<normals.size()<<std::endl;
osg::notify(osg::NOTICE) <<"texcoords :"<<texcoords.size()<<std::endl;
osg::notify(osg::NOTICE) <<"materials :"<<materialMap.size()<<std::endl;
osg::notify(osg::NOTICE) <<"elementStates :"<<elementStateMap.size()<<std::endl;
unsigned int pos=0;
for(ElementStateMap::iterator itr=elementStateMap.begin();
itr!=elementStateMap.end();
++itr,++pos)
{
const ElementState& es = itr->first;
ElementList& el = itr->second;
osg::notify(osg::NOTICE)<<"ElementState "<<pos<<std::endl;
osg::notify(osg::NOTICE)<<" es.objectName="<<es.objectName<<std::endl;
osg::notify(osg::NOTICE)<<" es.groupName="<<es.groupName<<std::endl;
osg::notify(osg::NOTICE)<<" es.materialName="<<es.materialName<<std::endl;
osg::notify(osg::NOTICE)<<" es.smoothGroup="<<es.smoothingGroup<<std::endl;
osg::notify(osg::NOTICE)<<" ElementList ="<<el.size()<<std::endl;
}
#endif
return true;
}
void Model::addElement(Element* element)
{
if (!currentElementList)
{
currentElementList = & (elementStateMap[currentElementState]);
}
currentElementList->push_back(element);
}
osg::Vec3 Model::averageNormal(const Element& element) const
{
osg::Vec3 normal;
for(Element::IndexList::const_iterator itr=element.normalIndices.begin();
itr!=element.normalIndices.end();
++itr)
{
normal += normals[*itr];
}
normal.normalize();
return normal;
}
osg::Vec3 Model::computeNormal(const Element& element) const
{
osg::Vec3 normal;
for(unsigned int i=0;i<element.vertexIndices.size()-2;++i)
{
osg::Vec3 a = vertices[element.vertexIndices[i]];
osg::Vec3 b = vertices[element.vertexIndices[i+1]];
osg::Vec3 c = vertices[element.vertexIndices[i+2]];
osg::Vec3 localNormal = (b-a) ^(c-b);
normal += localNormal;
}
normal.normalize();
return normal;
}
bool Model::needReverse(const Element& element) const
{
if (element.normalIndices.empty()) return false;
return computeNormal(element)*averageNormal(element) < 0.0f;
}