Boost::Spirit::QiによるWavefront Objファイルのパーサ

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;
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX