Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Approximate / force upgrade modes for Mitsuba 1 files #91

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 187 additions & 16 deletions src/libcore/xml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,14 +260,22 @@ struct XMLParseContext {
Transform4f transform;
size_t id_counter = 0;
bool parallelize;
bool load_permissive = false;
bool upgrade_approximate = false;
bool data_modified = false;
ColorMode color_mode;

XMLParseContext(const std::string &variant) : variant(variant) {
XMLParseContext(const std::string &variant, ParameterList const& params) : variant(variant) {
color_mode = MTS_INVOKE_VARIANT(variant, variant_to_color_mode);

/* Don't load the scene in parallel when running in GPU mode
(The Enoki CUDA backend is currently not multi-threaded) */
parallelize = !MTS_INVOKE_VARIANT(variant, check_cuda);

for (auto& p : params) {
load_permissive |= (p.first == "__load_permissive");
upgrade_approximate |= (p.first == "__upgrade_approximate");
}
}

std::string variant;
Expand Down Expand Up @@ -340,7 +348,7 @@ Vector3f parse_vector(XMLSource &src, pugi::xml_node &node, Float def_val = 0.f)
}
}

void upgrade_tree(XMLSource &src, pugi::xml_node &node, const Version &version) {
void upgrade_tree(XMLSource &src, pugi::xml_node &node, const Version &version, bool approximate) {
if (version == Version(MTS_VERSION_MAJOR, MTS_VERSION_MINOR, MTS_VERSION_PATCH))
return;

Expand All @@ -349,8 +357,11 @@ void upgrade_tree(XMLSource &src, pugi::xml_node &node, const Version &version)

if (version < Version(2, 0, 0)) {
// Upgrade all attribute names from camelCase to underscore_case
for (pugi::xpath_node result: node.select_nodes("//@name")) {
pugi::xml_attribute name_attrib = result.attribute();
for (pugi::xpath_node result: node.select_nodes("//*[@name]")) {
pugi::xml_node n = result.node();
if (std::strcmp(n.name(), "default") == 0)
continue;
pugi::xml_attribute name_attrib = n.attribute("name");
std::string name = name_attrib.value();
for (size_t i = 0; i < name.length() - 1; ++i) {
if (std::islower(name[i]) && std::isupper(name[i + 1])) {
Expand All @@ -366,6 +377,159 @@ void upgrade_tree(XMLSource &src, pugi::xml_node &node, const Version &version)
}
for (pugi::xpath_node result: node.select_nodes("//lookAt"))
result.node().set_name("lookat");
// automatically rename reserved identifiers
for (pugi::xpath_node result: node.select_nodes("//@id")) {
pugi::xml_attribute id_attrib = result.attribute();
char const* val = id_attrib.value();
if (val && val[0] == '_') {
std::string new_id = std::string("ID") + val + "__UPGR";
Log(Warn, "Changing identifier: \"%s\" -> \"%s\"", val, new_id.c_str());
id_attrib = new_id.c_str();
}
}
// renamed features
for (pugi::xpath_node result: node.select_nodes("//bsdf[@type='bump']"))
result.node().attribute("type") = "bumpmap";
// approximate unsupported features
if (approximate) {
for (pugi::xpath_node result: node.select_nodes("//bsdf[@type='phong' or string/@value='phong']")) {
pugi::xml_node n = result.node();
pugi::xml_attribute at = n.attribute("type");
if (std::strcmp(at.value(), "phong") == 0) {
Log(Warn, "Changing phong -> rough plastic: \"%s\"", n.attribute("id").value());
at = "roughplastic";
} else {
Log(Warn, "Changing %s -> beckmann: \"%s\"", at.value(), n.attribute("id").value());
for (pugi::xpath_node result: n.select_nodes("string/@value[.='phong']"))
result.attribute() = "beckmann";
}
for (pugi::xpath_node result: n.select_nodes("float[@name='exponent']")) {
pugi::xml_node nv = result.node();
Float e = stof(nv.attribute("value").value());
Float alpha = std::sqrt(2 / (2+e));
nv.attribute("name") = "alpha";
nv.attribute("value") = std::to_string(alpha).c_str();
}
}
for (pugi::xpath_node result: node.select_nodes("//bsdf[@type='mixturebsdf']")) {
pugi::xml_node n = result.node();
Log(Warn, "Changing mixturebsdf -> blendbsdf: \"%s\"", n.attribute("id").value());
n.attribute("type") = "blendbsdf";
for (pugi::xpath_node result: n.select_nodes("string[@name='weights']")) {
pugi::xml_node wn = result.node();
std::string val = string::trim( wn.attribute("value").value() );
size_t sp = val.find_last_of(" \t,");
if (sp != val.npos)
// note: last value ok, load fails implcitly for more than 2 bsdfs
val.erase(val.begin(), val.begin() + (sp + 1));
wn.attribute("value") = val.c_str();
wn.attribute("name") = "weight";
wn.set_name("float");
}
}
for (pugi::xpath_node result: node.select_nodes("//bsdf[@type='coating' or @type='roughcoating']")) {
pugi::xml_node n = result.node();
char const* stype = strstr(n.attribute("type").value(), "rough") ? "roughplastic" : "plastic";
Log(Warn, "Changing coating -> blended %s: \"%s\"", stype, n.attribute("id").value());
pugi::xml_node pn = n.append_child("bsdf");
pn.append_attribute("type") = stype;
for (pugi::xpath_node result: n.select_nodes("*[not(self::bsdf)]"))
pn.append_move(result.node());
n.attribute("type") = "blendbsdf";
pugi::xml_node wn = n.append_child("float");
wn.append_attribute("name") = "weight";
wn.append_attribute("value") = "0.04";
}
for (pugi::xpath_node result: node.select_nodes("//bsdf[@type='roughdiffuse']")) {
pugi::xml_node n = result.node();
Log(Warn, "Changing rough diffuse -> diffuse: \"%s\"", n.attribute("id").value());
n.attribute("type") = "diffuse";
}
for (pugi::xpath_node result: node.select_nodes("//bsdf[@type='bumpmap' or @type='normalmap']")) {
pugi::xml_node n = result.node();
char const* id = n.attribute("id").value();
Log(Warn, "Bump/normalmaps currently unsupported -> bypassing! \"%s\"", id);
for (pugi::xpath_node result: n.select_nodes("bsdf")) {
pugi::xml_node nn = result.node();
if (id && id[0]) {
nn.remove_attribute("id");
nn.append_attribute("id") = id;
}
n.parent().insert_move_before(nn, n);
}
n.parent().remove_child(n);
}
for (pugi::xpath_node result: node.select_nodes("//shape[@type='instance']")) {
pugi::xml_node n = result.node();
Log(Warn, "Unrolling instance -> shapes! \"%s\"", n.attribute("id").value());
int shapeCtr = 0;
for (pugi::xpath_node result: n.select_nodes("ref/@id")) {
char const* shapegroup_id = result.attribute().value();
std::string group_selector = std::string("//shape[@id='") + shapegroup_id + "' and @type='shapegroup']";
pugi::xml_node sg = node.select_node(group_selector.c_str()).node();
if (!sg) Throw("Unknown shape group \"%s\" referenced", shapegroup_id);
if (!sg.attribute("was_referenced"))
sg.append_attribute("was_referenced") = "1";
// clone shapes from shape group
for (pugi::xpath_node result: sg.select_nodes("shape")) {
pugi::xml_node sn = n.parent().insert_copy_before(result.node(), n);
++shapeCtr;
// concat instance transformations (XML parser already chains node effects)
for (pugi::xpath_node result: n.select_nodes("transform")) {
pugi::xml_node itn = result.node();
char const* transform_name = itn.attribute("name").value();
std::string transform_selector = std::string("transform[@name='") + transform_name + "']";
pugi::xml_node stn = sn.select_node(transform_selector.c_str()).node();
if (!stn) {
stn = sn.append_child("transform");
stn.append_attribute("name") = transform_name;
}
for (pugi::xml_node t: itn.children())
stn.append_copy(t);
}
}
}
Log(shapeCtr > 0 ? Info : Warn, "Instantiated %d shapes", shapeCtr);
n.parent().remove_child(n);
}
for (pugi::xpath_node result: node.select_nodes("//shape[@type='shapegroup']")) {
pugi::xml_node n = result.node();
if (!n.attribute("was_referenced"))
Log(Warn, "Unreferenced shape group: \"%s\"", n.attribute("id").value());
n.parent().remove_child(n);
}
for (pugi::xpath_node result: node.select_nodes("//srgb")) {
Log(Warn, "Changing <srgb> -> <rgb>");
result.node().set_name("rgb");
}
for (pugi::xpath_node result: node.select_nodes("//spectrum[@value[not(contains(.,':'))]]")) {
pugi::xml_node n = result.node();
if (string::tokenize(n.attribute("value").value()).size() > 1) {
Log(Warn, "Changing <spectrum> w/o wavelengths -> <rgb>");
n.set_name("rgb");
}
}
}
// changed parameters
for (pugi::xpath_node result: node.select_nodes("//bsdf[@type='diffuse']/*/@name[.='diffuse_reflectance']"))
result.attribute() = "reflectance";
for (pugi::xpath_node result: node.select_nodes("//rgb/@value[starts-with(.,'#')]")) {
pugi::xml_attribute a = result.attribute();
std::string val = string::trim( a.value() );
Color3f color(0);
if (val.size() == 7)
color = Color3f( (float) std::stoul(val.substr(1, 2), nullptr, 16) / 255.0f,
(float) std::stoul(val.substr(3, 2), nullptr, 16) / 255.0f,
(float) std::stoul(val.substr(5, 2), nullptr, 16) / 255.0f );
else if (val.size() == 7)
color = Color3f( (float) std::stoul(val.substr(1, 1), nullptr, 16) / 15.0f,
(float) std::stoul(val.substr(2, 1), nullptr, 16) / 15.0f,
(float) std::stoul(val.substr(3, 1), nullptr, 16) / 15.0f );
else
Throw("Invalid color code \"%s\"", val.c_str());
val = std::to_string(color.r()) + ' ' + std::to_string(color.g()) + ' ' + std::to_string(color.b());
a = val.c_str();
}

// Update 'uoffset', 'voffset', 'uscale', 'vscale' to transform block
for (pugi::xpath_node result : node.select_nodes(
Expand Down Expand Up @@ -485,7 +649,8 @@ static std::pair<std::string, std::string> parse_xml(XMLSource &src, XMLParseCon
} catch (const std::exception &) {
src.throw_error(node, "could not parse version number \"%s\"", version_attr.value());
}
upgrade_tree(src, node, version);
upgrade_tree(src, node, version, ctx.upgrade_approximate);
ctx.data_modified |= src.modified;
node.remove_attribute(version_attr);
}

Expand Down Expand Up @@ -643,7 +808,8 @@ static std::pair<std::string, std::string> parse_xml(XMLSource &src, XMLParseCon
} catch (const std::exception &) {
nested_src.throw_error(*doc.begin(), "could not parse version number \"%s\"", version_attr_incl.value());
}
upgrade_tree(nested_src, *doc.begin(), version);
upgrade_tree(nested_src, *doc.begin(), version, ctx.upgrade_approximate);
ctx.data_modified |= nested_src.modified;
doc.begin()->remove_attribute(version_attr_incl);
}

Expand Down Expand Up @@ -1064,30 +1230,35 @@ static ref<Object> instantiate_node(XMLParseContext &ctx, const std::string &id)
inst.object = PluginManager::instance()->create_object(props, inst.class_);
} catch (const std::exception &e) {
Throw("Error while loading \"%s\" (near %s): could not instantiate "
"%s plugin of type \"%s\": %s", inst.src_id, inst.offset(inst.location),
"%s plugin of type \"%s\": %s%s", inst.src_id, inst.offset(inst.location),
string::to_lower(inst.class_->name()), props.plugin_name(),
e.what());
e.what(),
ctx.data_modified && !ctx.upgrade_approximate ? " (try -D__upgrade_approximate=1)" : "");
}

auto unqueried = props.unqueried();
if (!unqueried.empty()) {
for (auto &v : unqueried) {
if (props.type(v) == Properties::Type::Object) {
const auto &obj = props.object(v);
Throw("Error while loading \"%s\" (near %s): unreferenced "
"object %s (within %s of type \"%s\")",
Log(ctx.load_permissive ? Warn : Error,
"Error while loading \"%s\" (near %s): unreferenced "
"object %s (within %s of type \"%s\")%s",
inst.src_id, inst.offset(inst.location),
obj, string::to_lower(inst.class_->name()),
inst.props.plugin_name());
inst.props.plugin_name(),
!ctx.load_permissive ? " (try -D__load_permissive=1)" : "");
} else {
v = "\"" + v + "\"";
}
}
Throw("Error while loading \"%s\" (near %s): unreferenced %s "
"%s in %s plugin of type \"%s\"",
Log(ctx.load_permissive ? Warn : Error,
"Error while loading \"%s\" (near %s): unreferenced %s "
"%s in %s plugin of type \"%s\"%s",
inst.src_id, inst.offset(inst.location),
unqueried.size() > 1 ? "properties" : "property", unqueried,
string::to_lower(inst.class_->name()), props.plugin_name());
string::to_lower(inst.class_->name()), props.plugin_name(),
!ctx.load_permissive ? " (try -D__load_permissive=1)" : "");
}
return inst.object;
}
Expand All @@ -1111,7 +1282,7 @@ ref<Object> load_string(const std::string &string, const std::string &variant,
src.offset(result.offset), result.description());

pugi::xml_node root = doc.document_element();
detail::XMLParseContext ctx(variant);
detail::XMLParseContext ctx(variant, param);
Properties prop;
size_t arg_counter; // Unused
auto scene_id = detail::parse_xml(src, ctx, root, Tag::Invalid, prop,
Expand Down Expand Up @@ -1145,7 +1316,7 @@ ref<Object> load_file(const fs::path &filename_, const std::string &variant,

pugi::xml_node root = doc.document_element();

detail::XMLParseContext ctx(variant);
detail::XMLParseContext ctx(variant, param);
Properties prop;
size_t arg_counter = 0; // Unused
auto scene_id = detail::parse_xml(src, ctx, root, Tag::Invalid, prop,
Expand Down