/******************************************************************/
/* 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 <vector>
#include <memory>
#include <string>
#include <cstdio>
#include <cassert>
#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>& 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<boundedPrimitive> importTriangleMesh(const XMLNode& node, nodeCache<boundedPrimitive>& shape_cache, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
{
// sanity check
assert(node.name() == "triangleMesh");
// allocate node properties
transformation3d transform;
std::shared_ptr<const shader_base> material;
std::vector<triangle> 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<boundedPrimitive>(new triangleMesh(triangle_list, material, transform));
}
/////////////////////////////////////////////////////////////////
static std::shared_ptr<boundedPrimitive> importSceneGraphNode(const XMLNode& node, nodeCache<boundedPrimitive>& shape_cache, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
{
// sanity check
assert(node.name() == "sceneGraphNode");
// node properties
transformation3d transform;
std::shared_ptr<const shader_base> material;
std::vector<std::shared_ptr<const boundedPrimitive>> 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<boundedPrimitive>(new sceneGraphNode(child_nodes, transform, material));
}
/////////////////////////////////////////////////////////////////
std::shared_ptr<boundedPrimitive> importGeometry(const XMLNode& node, nodeCache<boundedPrimitive>& shape_cache, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
{
// check if geometry type
if(node.name() != "sceneGraphNode" && node.name() != "triangleMesh") return std::shared_ptr<boundedPrimitive>(nullptr);
// check if reference
std::string ref = getString(node, "ref");
std::shared_ptr<boundedPrimitive> 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;
}