/******************************************************************/ /* This file is part of the homework assignments for CSCI-427/527 */ /* at The College of William & Mary and authored by Pieter Peers. */ /* No part of this file, whether altered or in original form, can */ /* be distributed or used outside the context of CSCI-427/527 */ /* without consent of either the College of William & Mary or */ /* Pieter Peers. */ /******************************************************************/ #include #include #include #include #include #include "sceneIO_xml.h" #include "sceneIO_core.h" #include "sceneIO_cache.h" #include "sceneIO_geometry.h" #include "sceneIO_material.h" #include "sceneIO_transformation3d.h" #include "util.h" #include "vec2d.h" #include "vec3d.h" #include "triangle.h" #include "constants.h" #include "importOBJ.h" #include "errorMessage.h" #include "triangleMesh.h" #include "random_number.h" #include "sceneGraphNode.h" static void importTriangle(const XMLNode& node, std::vector& triangle_list) { // sanity check assert(node.name() == "triangle" || node.name() == "polygon"); // allocate temp data unsigned int idx=0; vec3d v[3], n[3]; vec2d t[3]; // for each child node, parse all vertices for(XMLNode child = node.firstChild(); child.isValid(); child++) { std::string name = child.name(); if(name == "vertex") { v[idx] = getVec3d(child, "v", vec3d(+LARGE)); n[idx] = getVec3d(child, "n", vec3d(0.0f)); t[idx] = getVec2d(child, "t", vec2d(+LARGE)); // check if valid vertex if(v[idx].x >= +LARGE || v[idx].y >= +LARGE || v[idx].z >= +LARGE) errorMessage("Invalid vertex in %s definition.", node.name().c_str()); // create triangle if(idx == 2) { // determine which attributes are defined on the triangle bool hasNormals = (n[0].squared_length() > +EPSILON) && (n[0].squared_length() > +EPSILON) && (n[0].squared_length() > +EPSILON); bool hasTextureCoord = (fabs(t[0].x) < +LARGE && fabs(t[0].y) < +LARGE) && (fabs(t[1].x) < +LARGE && fabs(t[1].y) < +LARGE) && (fabs(t[2].x) < +LARGE && fabs(t[2].y) < +LARGE); if(!hasNormals && !hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2])); else if(hasNormals && !hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2], n[0], n[1], n[2])); else if(!hasNormals && hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2], t[0], t[1], t[2])); else triangle_list.push_back(triangle(v[0], v[1], v[2], n[0], n[1], n[2], t[0], t[1], t[2])); // cycle (to support polygons) v[1] = v[2]; n[1] = n[2]; t[1] = t[2]; } else idx++; } else errorMessage("Unknown child element in %s node (%s).", node.name().c_str(), name.c_str()); } // check if at least one full triangle was found if(idx != 2) errorMessage("%s requires at least 3 vertices.", node.name().c_str()); // Done. } ///////////////////////////////////////////////////////////////// static std::shared_ptr importTriangleMesh(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) { // sanity check assert(node.name() == "triangleMesh"); // allocate node properties transformation3d transform; std::shared_ptr material; std::vector triangle_list; // import OBJ triangles std::string objname = getString(node, "filename"); if(objname != "") { std::string path = getDirectory(objname); if(path == "") objname = rootDir + objname; importOBJ(objname, triangle_list); } // check child nodes for(XMLNode child = node.firstChild(); child.isValid(); child++) { std::string name = child.name(); // general child nodes auto tempmat = importMaterial(child, shader_cache, texture_cache, rootDir); if(tempmat) material = tempmat; // specialized child nodes else if(name == "transformation") importTransformation(child, transform); else if(name == "triangle" || name == "polygon") importTriangle(child, triangle_list); else errorMessage("Unknown child-node in triangleMesh (%s).", name.c_str()); } // Done. return std::shared_ptr(new triangleMesh(triangle_list, material, transform)); } ///////////////////////////////////////////////////////////////// static std::shared_ptr importSceneGraphNode(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) { // sanity check assert(node.name() == "sceneGraphNode"); // node properties transformation3d transform; std::shared_ptr material; std::vector> child_nodes; // check child nodes for(XMLNode child = node.firstChild(); child.isValid(); child++) { std::string name = child.name(); // general child nodes auto tempmat = importMaterial(child, shader_cache, texture_cache, rootDir); auto shape = importGeometry(child, shape_cache, shader_cache, texture_cache, rootDir); if(tempmat) material = tempmat; else if(shape) child_nodes.push_back(shape); // node specific children. else if(name == "transformation") importTransformation(child, transform); else errorMessage("Unknown child-node in sceneGraphNode (%s).", name.c_str()); } // Done. return std::shared_ptr(new sceneGraphNode(child_nodes, transform, material)); } ///////////////////////////////////////////////////////////////// std::shared_ptr importGeometry(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) { // check if geometry type if(node.name() != "sceneGraphNode" && node.name() != "triangleMesh") return std::shared_ptr(nullptr); // check if reference std::string ref = getString(node, "ref"); std::shared_ptr shape = shape_cache.get(ref); if(shape) return shape; else if(ref != "") errorMessage("Unknown %s name (%s).", node.name().c_str(), ref.c_str()); // generate default name & get id char defaultname[32]; sprintf(defaultname, "noname-%u", random_int()); std::string id = getString(node, "id", defaultname); if(shape_cache.get(id)) errorMessage("%s-node id is not unique (%s).", node.name().c_str(), id.c_str()); // process primitive based on type if(node.name() == "sceneGraphNode") shape = importSceneGraphNode(node, shape_cache, shader_cache, texture_cache, rootDir); else if(node.name() == "triangleMesh") shape = importTriangleMesh(node, shape_cache, shader_cache, texture_cache, rootDir); // update cache if(id != "") shape_cache.add(id, shape); // Done. return shape; }