3Dメッシュフォーマットの一つ、Wavefront Objのパーサを書いてみた。Boost::Spiritの勉強を兼ねている。
Boost::Spirit::Qiはパーサコンビネータ。気や霊魂をモチーフにしたネーミングが中二病くさいことで散々ネタにされているけれど、実際結構すごいやつ。
けれども、それに輪を掛けてすごいのが、Boost::Phoenix。C++11が出たらBoost::Lambdaなんていらないと思っていたけれど、それは間違いだった。拡張性が半端ない。boost::spirit::qiでは、返値のための変数_valを文脈に応じて独自に定義するために使用している感じか、たぶん。
開発環境:gcc4.6 boost1.48.0 動作テスト:してない
3Dメッシュフォーマットの一つ、Wavefront Objのパーサを書いてみた。Boost::Spiritの勉強を兼ねている。
Boost::Spirit::Qiはパーサコンビネータ。気や霊魂をモチーフにしたネーミングが中二病くさいことで散々ネタにされているけれど、実際結構すごいやつ。
けれども、それに輪を掛けてすごいのが、Boost::Phoenix。C++11が出たらBoost::Lambdaなんていらないと思っていたけれど、それは間違いだった。拡張性が半端ない。boost::spirit::qiでは、返値のための変数_valを文脈に応じて独自に定義するために使用している感じか、たぶん。
開発環境:gcc4.6 boost1.48.0 動作テスト:してない
#include <boost/fusion/adapted/struct.hpp>#include <boost/spirit/include/qi.hpp>#include <boost/spirit/home/phoenix.hpp>#include <fstream>#include "Vector2d.h"#include "Vector3d.h"BOOST_FUSION_ADAPT_STRUCT(float3d,(float, x)(float, y)(float, z));BOOST_FUSION_ADAPT_STRUCT(float2d,(float, x)(float, y));class ObjFile {public:class Group {public:std::string name;std::string material;std::vector<float3d> vertexList;std::vector<float3d> normalList;std::vector<float2d> texCoordList;};class Index {public:u32 vertex, texcoord, normal;};class Face {public:Index i1, i2, i3;};std::string materialFile;std::vector<Group> groups;};BOOST_FUSION_ADAPT_STRUCT(ObjFile,(std::string, materialFile)(std::vector<ObjFile::Group>, groups));BOOST_FUSION_ADAPT_STRUCT(ObjFile::Group,(std::string, name)(std::string, material)(std::vector<float3d>, vertexList)(std::vector<float3d>, normalList)(std::vector<float2d>, texCoordList)(std::vector<ObjFile::Face>, faceList));BOOST_FUSION_ADAPT_STRUCT(ObjFile::Index,(u32, vertex)(u32, texcoord)(u32, normal));BOOST_FUSION_ADAPT_STRUCT(ObjFile::Face,(ObjFile::Index, i1)(ObjFile::Index, i2)(ObjFile::Index, i3));using namespace boost::spirit;template <typename Iterator>class ObjParser : public qi::grammar<Iterator, ObjFile()>{qi::rule<Iterator, float3d()> float3d_;qi::rule<Iterator, float2d()> float2d_;qi::rule<Iterator, ObjFile::Index()> index;qi::rule<Iterator, float3d()> vert_line;qi::rule<Iterator, float3d()> normal_line;qi::rule<Iterator, float2d()> texcoord_line;qi::rule<Iterator, std::string()> group_line;qi::rule<Iterator, std::string()> usemtl_line;qi::rule<Iterator, std::string()> mtllib_line;qi::rule<Iterator, ObjFile::Face()> face_line;qi::rule<Iterator> surface_line;qi::rule<Iterator> comment_line;qi::rule<Iterator, ObjFile::Group()> group;qi::rule<Iterator, ObjFile()> start;public:ObjParser(): ObjParser::base_type(start){using namespace qi;namespace phx = boost::phoenix;float3d_ %= float_ >> ' ' >> float_ >> ' ' >> float_;float2d_ %= float_ >> ' ' >> float_;index = eps[_val = phx::construct<ObjFile::Index>()]>> uint_[phx::at_c<0>(_val) = qi::_1]>> !('/' >> uint_[phx::at_c<1>(_val) = qi::_1]>> !('/' >> uint_[phx::at_c<2>(_val) = qi::_1]));vert_line %= "v " >> float3d_ >> eol;normal_line %= "vn " >> float3d_ >> eol;texcoord_line %= "vt " >> float2d_ >> eol;group_line %= "g " >> +(char_ - eol) >> eol;face_line %= "f " >> index >> ' ' >> index >> ' ' >> index >> eol;surface_line = "s " >> *(char_ - eol) >> eol;usemtl_line %= "usemtl " >> +(char_ - eol) >> eol;mtllib_line %= "mtllib " >> +(char_ - eol) >> eol;comment_line = '#' >> *(char_ - eol) >> eol | eol;group = eps[_val = phx::construct<ObjFile::Group>()]>> group_line[phx::at_c<0>(_val) = qi::_1]>> *(usemtl_line[phx::at_c<1>(_val) = qi::_1]| vert_line[phx::push_back(phx::at_c<2>(_val), qi::_1)]| normal_line[phx::push_back(phx::at_c<3>(_val), qi::_1)]| texcoord_line[phx::push_back(phx::at_c<4>(_val), qi::_1)]| comment_line);start = eps[_val = phx::construct<ObjFile>()]>> *(group[phx::push_back(phx::at_c<1>(_val), qi::_1)]| mtllib_line[phx::at_c<0>(_val) = qi::_1]| comment_line);}};int main(int argc, const char** argv) {if (argc == 0) {std::cout << "require one argument : .obj filepath" << std::endl;return -1;}std::ifstream file(argv[0]);std::string source;for(char c; !file.eof() && file.get(c); ) {source.push_back(c);}file.close();ObjFile value;ObjParser<std::string::iterator> parser;qi::parse(source.begin(), source.end(), parser, value);return 0;}