2

所以我努力让 boost::spirit::qi 在我的皮肤下。到目前为止,我的玩具示例是一个解析器,它解析具有以下格式的 Wavefront OBJ 材质库:

newmtl ShortBox
Ka  0.6 0.6 0.6
Kd  0.5 0.5 0.5
Ks  0 0 0
d  1
Ns  0
illum 2

但是,材质 ShortBox 的参数顺序可能会有所不同。我创建了以下成功解析它的语法:

template <typename Iterator>
struct mtllib_grammar : qi::grammar<Iterator, qi::blank_type> {

    mtllib_grammar() : mtllib_grammar::base_type(mtl)
    {
        using qi::char_;
        using qi::double_;
        using qi::int_;


        mtl =
            (
            ("newmtl" >> +(char_ - qi::eol)  >> qi::eol >> *(mtl_details) >> *(mtl))
            | ("#" >> *(qi::char_ - qi::eol) >> qi::eol >> *(mtl))
            );

        mtl_details =
            (
            ("Ka" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
            | ("Kd" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
            | ("Ks" >> double_ >> double_ >> double_ >> qi::eol >> *(mtl_details))
            | ("d" >> int_ >> qi::eol >> *(mtl_details))
            | ("Ns" >> int_ >> qi::eol >> *(mtl_details))
            | ("illum" >> int_ >> qi::eol >> *(mtl_details))
            );
    }
    qi::rule<Iterator, qi::blank_type> mtl;
    qi::rule<Iterator, qi::blank_type> mtl_details;
};

现在我想建立一个std::map<std::string,Material>定义Material为:

struct Material {
    Material()
    {
        Ns = 0.0f;
        Ke = glm::vec3(0.0f);
        Kd = glm::vec3(0.0f);
        Ks = glm::vec3(0.0f);
        Ka = glm::vec3(0.0f);
    }
    ~Material() {}
    glm::vec3 Ka;
    glm::vec3 Kd;
    glm::vec3 Ks;
    glm::vec3 Ke;
    float Ns;
};

具有以下融合适应:

BOOST_FUSION_ADAPT_STRUCT(
    glm::vec3,
    (float, x)
    (float, y)
    (float, z)
    )

BOOST_FUSION_ADAPT_STRUCT(
    Material,
    (glm::vec3, Ka)
    (glm::vec3, Kd)
    (glm::vec3, Ks)
    (glm::vec3, Ke)
    (float, Ns)
    )

所以我目前的想法是改变规则mtl_details,使其返回一个完整的Material规则mtl,返回一个所述 Material 的映射,其中 key 是 after 的字符串newmtl。但是,我不知道如何使用属性从解析树构建 Material 对象,映射Ka, Kd, Ksect 的所有命中。到同一个结构上。阅读示例,它们似乎都隐式映射到与其关联的任何变量,或者它们仅映射到简单值,而不是对象。

4

1 回答 1

3

感谢 cv_and_he 我找到了解决方案。首先,只有文件中可能出现的参数应该使用 映射BOOST_FUSION_ADAPT_STRUCT,所以不要映射 Material::Ke,因为它不能在文件格式中自然出现。

BOOST_FUSION_ADAPT_STRUCT(
    Material,
    (glm::vec3, Ka)
    (glm::vec3, Kd)
    (glm::vec3, Ks)
    (float, Ns)
    )

接下来,我的语法结果如下:

template <typename Iterator>
struct mtllib_grammar : qi::grammar<Iterator, std::map<std::string, Material>(), qi::blank_type> {

    mtllib_grammar() : mtllib_grammar::base_type(mtl)
    {
        using qi::char_;
        using qi::float_;
        using qi::int_;
        using qi::omit;


        mtl =
            (
            *(("newmtl" >> string_rule >> qi::eol >> mtl_details) |
            ("#" >> omit[*(qi::char_ - qi::eol)] >> qi::eol ))
            );

        mtl_details =
            (
            ("Ka" >> glm_rule >> qi::eol) ^
            ("Kd" >> glm_rule >> qi::eol) ^
            ("Ks" >> glm_rule >> qi::eol) ^
            ("d" >> omit[int_] >> qi::eol) ^
            ("Ns" >> int_ >> qi::eol) ^
            ("illum" >> omit[int_] >> qi::eol)
            );

        string_rule = +(char_ - qi::eol);
        glm_rule = float_ >> float_ >> float_;
    }
    qi::rule<Iterator, std::map<std::string, Material>(), qi::blank_type> mtl;

    qi::rule<Iterator, Material(), qi::blank_type> mtl_details;

    qi::rule<Iterator, std::string(), qi::blank_type> string_rule;
    qi::rule<Iterator, glm::vec3(), qi::blank_type> glm_rule;
};

周围未使用omit的参数加上注释。否则,编译器会因为无法映射到规则中给出的类型而失败。

于 2016-01-27T19:30:43.913 回答