summaryrefslogblamecommitdiff
path: root/hw6/src/sceneIO_geometry.cpp
blob: 5fa4a5c0e906845709c0092838d309f72769e467 (plain) (tree)


























































































































































































                                                                                                                                                                                                                                      
/******************************************************************/
/* 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;
}