//-***************************************************************************** // // Copyright (c) 2009-2012, // Sony Pictures Imageworks, Inc. and // Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Sony Pictures Imageworks, nor // Industrial Light & Magic nor the names of their contributors may be used // to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //-***************************************************************************** #include #include //-***************************************************************************** // I'm breaking with the Alembic "Foundation" and "FoundationPrivate" // conventions here because I want to limit the exposure to these files, // as they take FOREVER to parse/compile, because of all the template-hoo-haa. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AbcClients { namespace WFObjConvert { //-***************************************************************************** // Create a namespace to hide the parser stuff in. // Following spirit convention, calling this 'client' namespace client { // Bring in all the pieces we need. namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace phoenix = boost::phoenix; using qi::double_; using qi::int_; using qi::long_long; using qi::phrase_parse; using qi::_1; using qi::_val; using qi::lit; using qi::eol; using qi::eps; using qi::lexeme; using qi::attr; using ascii::space; using ascii::space_type; using ascii::char_; using ascii::string; using phoenix::ref; using phoenix::push_back; //-***************************************************************************** // Symbol table for 'on off' struct onOff_ : qi::symbols { onOff_() { add ( "on" , true ) ( "On" , true ) ( "ON" , true ) ( "true", true ) ( "True", true ) ( "TRUE" , true ) ( "off", false ) ( "Off", false ) ( "OFF", false ) ( "false", false ) ( "False", false ) ( "FALSE", false ) ; } }; //-***************************************************************************** typedef boost::fusion::vector index_triplet; //-***************************************************************************** // Create a local parse reader that uses the boost::fusion index-triplet. class LclParseReader : public ParseReader { public: LclParseReader( Reader &iReadInto ) : ParseReader( iReadInto ) {} LclParseReader( const ParseReader &iCopy ) : ParseReader( iCopy ) {} static V3idx toV3( index_triplet const &iVal ) { return V3idx( ( pindex_t )boost::fusion::at_c<0>( iVal ), ( pindex_t )boost::fusion::at_c<1>( iVal ), ( pindex_t )boost::fusion::at_c<2>( iVal ) ); } void f_( std::vector const &vals ) { std::vector cvt; for ( std::vector::const_iterator iter = vals.begin(); iter != vals.end(); ++iter ) { cvt.push_back( toV3(*iter) ); } this->f( cvt ); } void l_( std::vector const &vals ) { std::vector cvt; for ( std::vector::const_iterator iter = vals.begin(); iter != vals.end(); ++iter ) { cvt.push_back( toV3(*iter) ); } this->l( cvt ); } void p_( std::vector const &vals ) { std::vector cvt; for ( std::vector::const_iterator iter = vals.begin(); iter != vals.end(); ++iter ) { cvt.push_back( toV3(*iter) ); } this->p( cvt ); } }; //-***************************************************************************** typedef std::string::const_iterator Iterator; //-***************************************************************************** // This grammar parses OBJ's different styles of index-triplets. // template struct indexTriplet : qi::grammar { qi::rule start; #define LL long_long indexTriplet() : indexTriplet::base_type( start ) { //start = lexeme[( ( LL >> attr( -1 ) >> attr( -1 ) ) | // ( LL >> '/' >> LL >> '/' >> LL ) | // ( LL >> '/' >> LL >> attr( -1 ) ) | // ( LL >> '/' >> attr(-1) >> '/' >> LL ) )]; start = lexeme[( ( LL >> '/' >> LL >> '/' >> LL ) | ( LL >> '/' >> attr( -1 ) >> '/' >> LL ) | ( LL >> '/' >> LL >> '/' >> attr( -1 ) ) | ( LL >> attr( -1 ) >> attr( -1 ) ) )]; } #undef LL }; //-***************************************************************************** // REAL PARSE FUNCTION void doParseOBJ( Reader &iReadInto, const std::string &iName, std::istream &iStream ) { // Create an index-triplet parser, call it "IDX" //indexTriplet IDX; indexTriplet IDX; // Create a 'space-less' name parser. qi::rule STR = lexeme[+(char_)]; // Make an on-off symbol table onOff_ ON_OFF; // Create types of rules. the PT_RULE is one which reads point // declarations. typedef qi::rule(), space_type> PT_RULE; // The ELEM_RULE is one which reads element declarations with // lists of index triplets. typedef qi::rule(), space_type> ELEM_RULE; // The NAME RULE is one which reads a single name typedef qi::rule NAME_RULE; // The NAMES_RULE is one which reads lists of names typedef qi::rule(), space_type> NAMES_RULE; // The BOOL_RULE is one which reads 'on' or 'off' or 'true' or 'false' // or 'ON' or 'OFF' ... etc. typedef qi::rule BOOL_RULE; // The INT_RULE is one which reads a single int typedef qi::rule INT_RULE; // The REACT macro binds the result of a successfully parsed rule // to a member function of the PreReader LclParseReader preReader( iReadInto ); #define REACT( F ) boost::bind( &LclParseReader:: F , &preReader, ::_1 ) // Declare our rules. PT_RULE VERTEX = "v" >> +(double_); PT_RULE VTEX = "vt" >> +(double_); PT_RULE VNORM = "vn" >> +(double_); PT_RULE VPARAM = "vp" >> +(double_); ELEM_RULE FACE = "f" >> IDX >> IDX >> +(IDX); ELEM_RULE LINE = "l" >> IDX >> +(IDX); ELEM_RULE POINT = "p" >> +(IDX); NAMES_RULE GROUP = "g" >> +(STR); NAME_RULE OBJECT = "o" >> (STR); NAME_RULE MTLLIB = "mtllib" >> STR; NAME_RULE MAPLIB = "maplib" >> STR; NAME_RULE USEMAP = "usemap" >> STR; NAME_RULE USEMTL = "usemtl" >> STR; NAME_RULE TRACE_OBJ = "trace_obj" >> STR; NAME_RULE SHADOW_OBJ = "shadow_obj" >> STR; BOOL_RULE BEVEL = "bevel" >> ON_OFF; BOOL_RULE CINTERP = "cinterp" >> ON_OFF; BOOL_RULE DINTERP = "dinterp" >> ON_OFF; BOOL_RULE SMOOTHB = "s" >> ON_OFF; INT_RULE SMOOTHI = "s" >> int_; INT_RULE LOD = "lod" >> int_; qi::rule ALL = qi::omit[ ( VERTEX[REACT(v)] | VTEX[REACT(vt)] | VNORM[REACT(vn)] | VPARAM[REACT(vp)] | FACE[REACT(f_)] | LINE[REACT(l_)] | POINT[REACT(p_)] | GROUP[REACT(g)] | OBJECT[REACT(o)] | MTLLIB[REACT(mtllib)] | MAPLIB[REACT(maplib)] | USEMTL[REACT(usemtl)] | USEMAP[REACT(usemap)] | TRACE_OBJ[REACT(trace_obj)] | SHADOW_OBJ[REACT(shadow_obj)] | BEVEL[REACT(bevel)] | CINTERP[REACT(cinterp)] | DINTERP[REACT(dinterp)] | SMOOTHB[REACT(sB)] | SMOOTHI[REACT(s)] | LOD[REACT(lod)] | // Comments ( '#' >> *(char_) ) ) ]; //-************************************************************************* // BEGIN THE PARSING! //-************************************************************************* preReader.start( iName ); std::string str; size_t lineCount = 1; while ( std::getline( iStream, str ) ) { if ( iStream.eof() ) { break; } if ( str.empty() ) { continue; } std::string::const_iterator iter = str.begin(); std::string::const_iterator end = str.end(); try { //bool r = client::parseObjLine( iter, end, reader ); bool r = phrase_parse( iter, end, ALL, space ); if ( !r ) { std::runtime_error exc( "Syntax Error" ); throw( exc ); } ++lineCount; } catch ( std::exception &exc ) { std::stringstream sstr; sstr << "ERROR: OBJ stream \"" << iName << "\": " << std::endl << "LINE: " << lineCount << std::endl << "---> " << str << std::endl << "REASON: " << exc.what() << std::endl; preReader.error( iName, sstr.str(), lineCount ); return; } catch ( ... ) { std::stringstream sstr; sstr << "ERROR: OBJ stream \"" << iName << "\": " << std::endl << "LINE: " << lineCount << std::endl << "---> " << str << std::endl; preReader.error( iName, sstr.str(), lineCount ); return; } } preReader.finish( iName, lineCount ); } } // End namespace client //-***************************************************************************** // PARSE FUNCTION void ParseOBJ( Reader &iReadInto, const std::string &iName, std::istream &iStream ) { client::doParseOBJ( iReadInto, iName, iStream ); } //-***************************************************************************** void ParseOBJ( Reader &iReadInto, const std::string &iFileName ) { std::ifstream fStr( iFileName.c_str() ); if ( !fStr ) { ParseReader reader( iReadInto ); reader.start( iFileName ); std::stringstream sstr; sstr << "ERROR: OBJ stream \"" << iFileName << "\": " << std::endl << "Couldn't open file: " << iFileName << std::endl; reader.error( iFileName, sstr.str(), 0 ); return; } ParseOBJ( iReadInto, iFileName, fStr ); } } // End namespace WFObjConvert } // End namespace AbcClients