OBJ: more robust .mtl texture offset/scale parsing (T89421)

As pointed out in a comment on T89421, if a MTL file contained
something like: `map_Ka -o 1 2.png` then it was parsed as having
offset `1 2` and the texture filename just a `.png`. Make it so that
mtl option numbers are parsed in a way where the number is only
accepted only if it's followed by whitespace.

Differential Revision: https://developer.blender.org/D15385
This commit is contained in:
Aras Pranckevicius 2022-07-07 11:34:04 +03:00
parent bddcb89cda
commit 50f9c1c09c
5 changed files with 46 additions and 14 deletions

View File

@ -626,15 +626,15 @@ static bool parse_texture_option(const char *&p,
{
p = drop_whitespace(p, end);
if (parse_keyword(p, end, "-o")) {
p = parse_floats(p, end, 0.0f, tex_map.translation, 3);
p = parse_floats(p, end, 0.0f, tex_map.translation, 3, true);
return true;
}
if (parse_keyword(p, end, "-s")) {
p = parse_floats(p, end, 1.0f, tex_map.scale, 3);
p = parse_floats(p, end, 1.0f, tex_map.scale, 3, true);
return true;
}
if (parse_keyword(p, end, "-bm")) {
p = parse_float(p, end, 1.0f, material->map_Bump_strength);
p = parse_float(p, end, 1.0f, material->map_Bump_strength, true, true);
return true;
}
if (parse_keyword(p, end, "-type")) {

View File

@ -62,8 +62,12 @@ static const char *drop_plus(const char *p, const char *end)
return p;
}
const char *parse_float(
const char *p, const char *end, float fallback, float &dst, bool skip_space)
const char *parse_float(const char *p,
const char *end,
float fallback,
float &dst,
bool skip_space,
bool require_trailing_space)
{
if (skip_space) {
p = drop_whitespace(p, end);
@ -73,13 +77,23 @@ const char *parse_float(
if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) {
dst = fallback;
}
else if (require_trailing_space && res.ptr < end && !is_whitespace(*res.ptr)) {
/* If there are trailing non-space characters, do not eat up the number. */
dst = fallback;
return p;
}
return res.ptr;
}
const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count)
const char *parse_floats(const char *p,
const char *end,
float fallback,
float *dst,
int count,
bool require_trailing_space)
{
for (int i = 0; i < count; ++i) {
p = parse_float(p, end, fallback, dst[i]);
p = parse_float(p, end, fallback, dst[i], true, require_trailing_space);
}
return p;
}

View File

@ -62,12 +62,17 @@ const char *parse_int(
* The parsed result is stored in `dst`. The function skips
* leading white-space unless `skip_space=false`. If the
* number can't be parsed (invalid syntax, out of range),
* `fallback` value is stored instead.
* `fallback` value is stored instead. If `require_trailing_space`
* is true, the character after the number has to be whitespace.
*
* Returns the start of remainder of the input string after parsing.
*/
const char *parse_float(
const char *p, const char *end, float fallback, float &dst, bool skip_space = true);
const char *parse_float(const char *p,
const char *end,
float fallback,
float &dst,
bool skip_space = true,
bool require_trailing_space = false);
/**
* Parse a number of white-space separated floats from an input string.
@ -77,6 +82,11 @@ const char *parse_float(
*
* Returns the start of remainder of the input string after parsing.
*/
const char *parse_floats(const char *p, const char *end, float fallback, float *dst, int count);
const char *parse_floats(const char *p,
const char *end,
float fallback,
float *dst,
int count,
bool require_trailing_space = false);
} // namespace blender::io::obj

View File

@ -29,9 +29,14 @@ static StringRef parse_int(StringRef s, int fallback, int &dst, bool skip_space
{
return StringRef(parse_int(s.begin(), s.end(), fallback, dst, skip_space), s.end());
}
static StringRef parse_float(StringRef s, float fallback, float &dst, bool skip_space = true)
static StringRef parse_float(StringRef s,
float fallback,
float &dst,
bool skip_space = true,
bool require_trailing_space = false)
{
return StringRef(parse_float(s.begin(), s.end(), fallback, dst, skip_space), s.end());
return StringRef(
parse_float(s.begin(), s.end(), fallback, dst, skip_space, require_trailing_space), s.end());
}
TEST(obj_import_string_utils, drop_whitespace)
@ -126,6 +131,9 @@ TEST(obj_import_string_utils, parse_float_invalid)
/* Has leading white-space when we don't expect it */
EXPECT_STRREF_EQ(" 1", parse_float(" 1", -4.0f, val, false));
EXPECT_EQ(val, -4.0f);
/* Has trailing non-number characters when we don't want them */
EXPECT_STRREF_EQ("123.5.png", parse_float(" 123.5.png", -5.0f, val, true, true));
EXPECT_EQ(val, -5.0f);
}
} // namespace blender::io::obj

View File

@ -247,7 +247,7 @@ TEST_F(obj_mtl_parser_test, materials)
ks.image_path = "ScaleOffsetBothTwovalues.png";
tex_map_XX &ns = mat[5].tex_map_of_type(eMTLSyntaxElement::map_Ns);
ns.scale = {0.5f, 1.0f, 1.0f};
ns.image_path = "ScaleOneValue.png";
ns.image_path = "1.Value.png";
}
check("materials.mtl", mat, ARRAY_SIZE(mat));