19 #include <nori/parser.h>
20 #include <nori/proplist.h>
21 #include <Eigen/Geometry>
22 #include <pugixml.hpp>
28 NoriObject *loadFromXML(
const std::string &filename) {
30 pugi::xml_document doc;
31 pugi::xml_parse_result result = doc.load_file(filename.c_str());
34 auto offset = [&](ptrdiff_t pos) -> std::string {
35 std::fstream is(filename);
37 int line = 0, linestart = 0, offset = 0;
39 is.read(buffer,
sizeof(buffer));
40 for (
int i = 0; i < is.gcount(); ++i) {
41 if (buffer[i] ==
'\n') {
42 if (offset + i >= pos)
43 return tfm::format(
"row %i, col %i", line + 1, pos - linestart);
45 linestart = offset + i;
48 offset += (int) is.gcount();
50 return "byte offset " + std::to_string(pos);
54 throw NoriException(
"Error while parsing \"%s\": %s (at %s)", filename, result.description(), offset(result.offset));
59 EScene = NoriObject::EScene,
60 EMesh = NoriObject::EMesh,
61 ETexture = NoriObject::ETexture,
62 EBSDF = NoriObject::EBSDF,
63 EPhaseFunction = NoriObject::EPhaseFunction,
64 EEmitter = NoriObject::EEmitter,
65 EMedium = NoriObject::EMedium,
66 ECamera = NoriObject::ECamera,
67 EIntegrator = NoriObject::EIntegrator,
68 ESampler = NoriObject::ESampler,
69 ETest = NoriObject::ETest,
70 EReconstructionFilter = NoriObject::EReconstructionFilter,
73 EBoolean = NoriObject::EClassTypeCount,
91 std::map<std::string, ETag> tags;
92 tags[
"scene"] = EScene;
94 tags[
"texture"] = ETexture;
96 tags[
"emitter"] = EEmitter;
97 tags[
"camera"] = ECamera;
98 tags[
"medium"] = EMedium;
99 tags[
"phase"] = EPhaseFunction;
100 tags[
"integrator"] = EIntegrator;
101 tags[
"sampler"] = ESampler;
102 tags[
"rfilter"] = EReconstructionFilter;
103 tags[
"test"] = ETest;
104 tags[
"boolean"] = EBoolean;
105 tags[
"integer"] = EInteger;
106 tags[
"float"] = EFloat;
107 tags[
"string"] = EString;
108 tags[
"point"] = EPoint;
109 tags[
"vector"] = EVector;
110 tags[
"color"] = EColor;
111 tags[
"transform"] = ETransform;
112 tags[
"translate"] = ETranslate;
113 tags[
"matrix"] = EMatrix;
114 tags[
"rotate"] = ERotate;
115 tags[
"scale"] = EScale;
116 tags[
"lookat"] = ELookAt;
119 auto check_attributes = [&](
const pugi::xml_node &node, std::set<std::string> attrs) {
120 for (
auto attr : node.attributes()) {
121 auto it = attrs.find(attr.name());
122 if (it == attrs.end())
123 throw NoriException(
"Error while parsing \"%s\": unexpected attribute \"%s\" in \"%s\" at %s",
124 filename, attr.name(), node.name(), offset(node.offset_debug()));
128 throw NoriException(
"Error while parsing \"%s\": missing attribute \"%s\" in \"%s\" at %s",
129 filename, *attrs.begin(), node.name(), offset(node.offset_debug()));
132 Eigen::Affine3f transform;
138 if (node.type() == pugi::node_comment || node.type() == pugi::node_declaration)
141 if (node.type() != pugi::node_element)
143 "Error while parsing \"%s\": unexpected content at %s",
144 filename, offset(node.offset_debug()));
147 auto it = tags.find(node.name());
148 if (it == tags.end())
149 throw NoriException(
"Error while parsing \"%s\": unexpected tag \"%s\" at %s",
150 filename, node.name(), offset(node.offset_debug()));
151 int tag = it->second;
154 bool hasParent = parentTag != EInvalid;
155 bool parentIsObject = hasParent && parentTag < NoriObject::EClassTypeCount;
156 bool currentIsObject = tag < NoriObject::EClassTypeCount;
157 bool parentIsTransform = parentTag == ETransform;
158 bool currentIsTransformOp = tag == ETranslate || tag == ERotate || tag == EScale || tag == ELookAt || tag == EMatrix;
160 if (!hasParent && !currentIsObject)
161 throw NoriException(
"Error while parsing \"%s\": root element \"%s\" must be a Nori object (at %s)",
162 filename, node.name(), offset(node.offset_debug()));
164 if (parentIsTransform != currentIsTransformOp)
165 throw NoriException(
"Error while parsing \"%s\": transform nodes "
166 "can only contain transform operations (at %s)",
167 filename, offset(node.offset_debug()));
169 if (hasParent && !parentIsObject && !(parentIsTransform && currentIsTransformOp))
170 throw NoriException(
"Error while parsing \"%s\": node \"%s\" requires a Nori object as parent (at %s)",
171 filename, node.name(), offset(node.offset_debug()));
174 node.append_attribute(
"type") =
"scene";
175 else if (tag == ETransform)
176 transform.setIdentity();
179 std::vector<NoriObject *> children;
180 for (pugi::xml_node &ch: node.children()) {
181 NoriObject *child = parseTag(ch, propList, tag);
183 children.push_back(child);
188 if (currentIsObject) {
193 node.attribute(
"type").value(),
199 "Unexpectedly constructed an object "
200 "of type <%s> (expected type <%s>): %s",
207 result->
setIdName(node.attribute(
"name").value());
210 for (
auto ch: children) {
212 ch->setParent(result);
221 check_attributes(node, {
"name",
"value" });
222 list.setString(node.attribute(
"name").value(), node.attribute(
"value").value());
226 check_attributes(node, {
"name",
"value" });
227 list.setFloat(node.attribute(
"name").value(), toFloat(node.attribute(
"value").value()));
231 check_attributes(node, {
"name",
"value" });
232 list.setInteger(node.attribute(
"name").value(), toInt(node.attribute(
"value").value()));
236 check_attributes(node, {
"name",
"value" });
237 list.setBoolean(node.attribute(
"name").value(), toBool(node.attribute(
"value").value()));
241 check_attributes(node, {
"name",
"value" });
242 auto name = node.attribute(
"name").value();
243 auto val = node.attribute(
"value").value();
244 auto n = vectorSize(val);
246 list.setPoint2(name,
Point2f(toVector2f(val)));
248 list.setPoint3(name,
Point3f(toVector3f(val)));
250 throw NoriException(
"Point %s (value: %s) is not of size 2 or 3", name, val);
254 check_attributes(node, {
"name",
"value" });
255 auto name = node.attribute(
"name").value();
256 auto val = node.attribute(
"value").value();
257 auto n = vectorSize(val);
259 list.setVector2(name,
Vector2f(toVector2f(val)));
261 list.setVector3(name,
Vector3f(toVector3f(val)));
263 throw NoriException(
"Vector %s (value: %s) is not of size 2 or 3", name, val);
267 check_attributes(node, {
"name",
"value" });
268 list.setColor(node.attribute(
"name").value(),
Color3f(toVector3f(node.attribute(
"value").value()).array()));
272 check_attributes(node, {
"name" });
273 list.setTransform(node.attribute(
"name").value(), transform.matrix());
277 check_attributes(node, {
"value" });
278 Eigen::Vector3f v = toVector3f(node.attribute(
"value").value());
279 transform = Eigen::Translation<float, 3>(v.x(), v.y(), v.z()) * transform;
283 check_attributes(node, {
"value" });
284 std::vector<std::string> tokens = tokenize(node.attribute(
"value").value());
285 if (tokens.size() != 16)
287 Eigen::Matrix4f matrix;
288 for (
int i=0; i<4; ++i)
289 for (
int j=0; j<4; ++j)
290 matrix(i, j) = toFloat(tokens[i*4+j]);
291 transform = Eigen::Affine3f(matrix) * transform;
295 check_attributes(node, {
"value" });
296 Eigen::Vector3f v = toVector3f(node.attribute(
"value").value());
297 transform = Eigen::DiagonalMatrix<float, 3>(v) * transform;
301 check_attributes(node, {
"angle",
"axis" });
302 float angle = degToRad(toFloat(node.attribute(
"angle").value()));
303 Eigen::Vector3f axis = toVector3f(node.attribute(
"axis").value());
304 transform = Eigen::AngleAxis<float>(angle, axis) * transform;
308 check_attributes(node, {
"origin",
"target",
"up" });
309 Eigen::Vector3f origin = toVector3f(node.attribute(
"origin").value());
310 Eigen::Vector3f target = toVector3f(node.attribute(
"target").value());
311 Eigen::Vector3f up = toVector3f(node.attribute(
"up").value());
313 Vector3f dir = (target - origin).normalized();
314 Vector3f left = up.normalized().cross(dir).normalized();
315 Vector3f newUp = dir.cross(left).normalized();
317 Eigen::Matrix4f trafo;
318 trafo << left, newUp, dir, origin,
321 transform = Eigen::Affine3f(trafo) * transform;
325 default:
throw NoriException(
"Unhandled element \"%s\"", node.name());
329 throw NoriException(
"Error while parsing \"%s\": %s (at %s)", filename,
330 e.what(), offset(node.offset_debug()));
337 return parseTag(*doc.begin(), list, EInvalid);
Simple exception class, which stores a human-readable error description.
static NoriObject * createInstance(const std::string &name, const PropertyList &propList)
Construct an instance from the class of the given name.
Base class of all objects.
virtual void activate()
Perform some action associated with the object.
void setIdName(const std::string &name)
Allow to assign a name to the object.
virtual void addChild(NoriObject *child)
Add a child object to the current instance.
virtual std::string toString() const =0
Return a brief string summary of the instance (for debugging purposes)
static std::string classTypeName(EClassType type)
Turn a class type into a human-readable string.
virtual EClassType getClassType() const =0
Return the type of object (i.e. Mesh/BSDF/etc.) provided by this instance.
This is an associative container used to supply the constructors of NoriObject subclasses with parame...
Represents a linear RGB color value.