diff options
| author | 53hornet <53hornet@gmail.com> | 2019-02-02 23:33:15 -0500 | 
|---|---|---|
| committer | 53hornet <53hornet@gmail.com> | 2019-02-02 23:33:15 -0500 | 
| commit | db072ad4dc181eca5a1458656b130beb43f475bf (patch) | |
| tree | a3c03c7f5497cb70503e2486662fa85cfb53415a /hw4/src | |
| download | csci427-master.tar.xz csci427-master.zip | |
Diffstat (limited to 'hw4/src')
46 files changed, 7946 insertions, 0 deletions
| diff --git a/hw4/src/boundedCompound.cpp b/hw4/src/boundedCompound.cpp new file mode 100644 index 0000000..1f43a82 --- /dev/null +++ b/hw4/src/boundedCompound.cpp @@ -0,0 +1,93 @@ +/******************************************************************/ +/* 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 <cassert> +#include <algorithm> + +#include "boundedCompound.h" +#include "intersector_factory_base.h" + +///////////////// +// Constructor // +///////////////// +boundedCompound::boundedCompound(void) +  : boundedPrimitive() +{ +  _transform = transformation3d(); +  _intersector = nullptr; +} + + +boundedCompound::boundedCompound(const transformation3d& transform, const std::shared_ptr<const shader_base>& shader) +				  +  : boundedPrimitive(boundingBox(), shader) +{ +  _transform = transform; +  _intersector = nullptr; +} + + +///////////// +// Methods // +///////////// +intersectionPoint boundedCompound::intersect(const ray& r) const +{ +  // sanity check +  assert(_intersector); + +  // HW5: Implement this +  //      Incorporate _transformation in the intersection computation. +  // Returns: intersectionPoint +  // Modifies: nothing. + +  // pass intersection computation to _intersector +  intersectionPoint ip = _intersector->intersect(r); + +  // Done. +  return ip; +} + + +void boundedCompound::initialize(const intersector_factory_base& ifb) +{ +  // create the _intersector +  _intersector = ifb(*this); +} +  + +void boundedCompound::initializeBoundingBox(void) +{ +  // compute the bounding box in world coordinates +  _bb = boundingBox(); +  for_each(compounds().begin(), compounds().end(), [&](const std::shared_ptr<const boundedPrimitive>& prim) +  { +    _bb += transform(prim->boundingbox(), _transform); +  }); +} + + +bool boundedCompound::hasShader(void) const +{ +  // check if this has a shader +  if(boundedPrimitive::hasShader()) return true; + +  // check if each child has a shader +  for(auto itr = compounds().begin(); itr != compounds().end(); itr++) +  { +    if(!(*itr)->hasShader()) return false; +  } + +  // Done. +  return true; +} + + +void boundedCompound::_print(std::ostream& s) const +{ +  s << "boundedCompound (" << _bb << ", " << compounds().size() << " compounds)"; +} diff --git a/hw4/src/boundedPrimitive.cpp b/hw4/src/boundedPrimitive.cpp new file mode 100644 index 0000000..d328b16 --- /dev/null +++ b/hw4/src/boundedPrimitive.cpp @@ -0,0 +1,50 @@ +/******************************************************************/ +/* 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 "boundedPrimitive.h" + +///////////////// +// Constructor // +///////////////// +boundedPrimitive::boundedPrimitive(void) +{ +  _bb = boundingBox(); +  _shader = nullptr; +} + +boundedPrimitive::boundedPrimitive(const boundingBox& bb, const std::shared_ptr<const shader_base>& shader) +{ +  _bb = bb; +  _shader = shader; +} + + +///////////// +// Methods // +///////////// +const boundingBox& boundedPrimitive::boundingbox(void) const +{ +  return _bb; +} + + +bool boundedPrimitive::hitBoundingBox(const ray& r) const +{ +  return _bb.isHit(r); +} + + +bool boundedPrimitive::hasShader(void) const +{ +  return (_shader != nullptr);  +}   + +void boundedPrimitive::_print(std::ostream& s) const +{ +  s << "boundedPrimitive (" << _bb << ")"; +} diff --git a/hw4/src/boundedTriangle.cpp b/hw4/src/boundedTriangle.cpp new file mode 100644 index 0000000..bf8a367 --- /dev/null +++ b/hw4/src/boundedTriangle.cpp @@ -0,0 +1,53 @@ +/******************************************************************/ +/* 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 "boundedTriangle.h" + + +////////////////// +// Constructors // +////////////////// +boundedTriangle::boundedTriangle(void) +  : boundedPrimitive() +{ +  // Nothing +} + +boundedTriangle::boundedTriangle(const triangle& tri, const std::shared_ptr<const shader_base>& shader) +  : boundedPrimitive(tri.boundingbox(), shader) +{ +  _triangle = tri; +} + + +///////////// +// Methods // +///////////// +intersectionPoint boundedTriangle::intersect(const ray& r) const +{ +  // intersect triangle +  float t; +  vec3d bc; +  bool hit = _triangle.intersect(r, bc, t); + +  // empty ip if not hit +  if(!hit) return intersectionPoint(); + +  // if hit, create intersectionPoint record. +  else return intersectionPoint(r, t, _shader,  +				_triangle.normal(bc), +				_triangle.shadingAxis(), +				_triangle.textureCoordinate(bc)); +} + + +void boundedTriangle::_print(std::ostream& s) const +{ +  s << "boundedTriangle (" << _bb << ", " << _triangle << ")"; +} diff --git a/hw4/src/boundedVolumeNode.cpp b/hw4/src/boundedVolumeNode.cpp new file mode 100644 index 0000000..fd143bb --- /dev/null +++ b/hw4/src/boundedVolumeNode.cpp @@ -0,0 +1,97 @@ +/******************************************************************/ +/* 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 <cassert> +#include <algorithm> + +#include "boundedVolumeNode.h" +#include "random_number.h" + +#include <iostream> // remove before submission +#include <stdio.h>  // remove before submission + +boundedVolumeNode::boundedVolumeNode(const std::vector<std::shared_ptr<const boundedPrimitive>>::iterator& start, const std::vector<std::shared_ptr<const boundedPrimitive>>::iterator& end) + +  : _left(nullptr), _right(nullptr), boundedPrimitive() +{ +  // HW4: Implement this. +  //      Constructs the bounding volume hierarchy. You can alter the order of elements between +  //      the 'start' and 'end' iterator. The _left and _right node can either be a boundedPrimitive +  //      or a new boundedVolume node (see _left and _right below respectively for an example of both). +  //      Don't forget to initialize the bounding box (_bb). Average time complexity: O(NlogN). +  // Modifies: _left, _right, and _bb. +  // Returns: nothing. + +  // create bounding box _bb for the incoming vector of primitives +  _bb = boundingBox(); +  for (auto itr = start; itr != end; itr++) { _bb += (**itr).boundingbox(); } + +  // select random axis to work with +  unsigned int axis = random_int(2); + +  // terminate recursion with final remaining primitives +  switch(std::distance(start, end)) { +    case 0: return; // no elements remain +    case 1: _left = *start; return; // single element remains; chose _left node +    case 2: { // two elements remain; sort and set +      if (getAxis(*start, axis) > getAxis(*(end - 1), axis)) { +        std::swap(*start, *(end - 1)); +      } + +      _left = *start; +      _right = *(end - 1); +      return; +    } +  } + +  // perform quicksort-style reordering of vector +  auto i = start; +  auto j = end - 1; +  // select random pivot +  vec3d::value_type pivot = getAxis(*(start + random_int(std::distance(start, end) - 1)), axis); + +  while (i < j) { +    while (getAxis(*j, axis) > pivot && j > i) { j--; } +    while (getAxis(*i, axis) < pivot && i <= j) { i++; } + +    if (i < j) { +      std::swap(*i, *j); +      i++; +    } + +  } + +  // recurse upon remaining elements +  _right = std::shared_ptr<const boundedPrimitive>(new boundedVolumeNode(j, end)); +  _left = std::shared_ptr<const boundedPrimitive>(new boundedVolumeNode(start, i)); +  // Done. +} + +vec3d::value_type boundedVolumeNode::getAxis(const std::shared_ptr<const boundedPrimitive>& primitive, unsigned int axis) { + +  switch(axis) { +    case 0: return (*primitive).boundingbox().center().x; +    case 1: return (*primitive).boundingbox().center().y; +    case 2: return (*primitive).boundingbox().center().z; +  } + +} + +intersectionPoint boundedVolumeNode::intersect(const ray& r) const +{ +  // HW4: Implement this +  //      Intersect the bounding volume hierarchy, and return the nearest intersection point. +  //      You will need to explore both _left and _right. Average time complexity: O(logN). + +  intersectionPoint ip, ip_left, ip_right; +  ip = intersectionPoint(); +  if (!hitBoundingBox(r)) { return ip; } +  if (_left && _left->hitBoundingBox(r)) { ip_left = _left->intersect(r); } +  if (_right && _right->hitBoundingBox(r)) { ip_right = _right->intersect(r); } +  return (ip_left < ip_right ? ip_left : ip_right); +} diff --git a/hw4/src/boundingBox.cpp b/hw4/src/boundingBox.cpp new file mode 100644 index 0000000..0c9e651 --- /dev/null +++ b/hw4/src/boundingBox.cpp @@ -0,0 +1,186 @@ +/******************************************************************/ +/* 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 "interval.h" +#include "constants.h" +#include "boundingBox.h" + + +///////////////// +// Constructor // +///////////////// +boundingBox::boundingBox(void) +{ +  // create an empty box (i.e., 'left' lies right of 'right') +  _lfb = vec3d(+LARGE); +  _rbt = vec3d(-LARGE); +} + + +boundingBox::boundingBox(const vec3d& lfb, const vec3d& rbt) +{ +  _lfb = lfb; +  _rbt = rbt; + +  // Make sure each component in _lfb is smaller than _rbt +  for(unsigned int i=0; i < vec3d::size(); i++) +    if(_rbt[i] < _lfb[i]) std::swap(_rbt[i], _lfb[i]); + +  // Done. +} + + +boundingBox::boundingBox(const boundingBox& bb) +{ +  _lfb = bb._lfb; +  _rbt = bb._rbt; +} + + +////////////// +// Operator // +////////////// +boundingBox& boundingBox::operator=(const boundingBox& src) +{ +  _assign(src); +  return *this; +} +   + +boundingBox& boundingBox::operator+=(const boundingBox& bb) +{ +  // compute the union of two bounding boxes +  for(unsigned int i=0; i < vec3d::size(); i++) +  { +    if(_lfb[i] > bb._lfb[i]) _lfb[i] = bb._lfb[i]; +    if(_rbt[i] < bb._rbt[i]) _rbt[i] = bb._rbt[i]; +  } + +  return *this; +} + + +boundingBox boundingBox::operator+(const boundingBox& bb) const +{ +  boundingBox result(*this); +  result += bb; +  return result; +} + + +boundingBox& boundingBox::operator+=(const vec3d& point) +{ +  // expand the bounding box to include 'point' (+ a small epsilon) +  for(unsigned int i=0; i < vec3d::size(); i++) +  { +    if(_lfb[i] > point[i]-EPSILON) _lfb[i] = point[i]-EPSILON; +    if(_rbt[i] < point[i]+EPSILON) _rbt[i] = point[i]+EPSILON; +  } +  return *this; +} + + +///////////// +// Methods // +///////////// +bool boundingBox::isHit(const ray& r) const +{ +  // init +  interval boxInterval(0, +LARGE); + +  // for every slab +  for(unsigned int i=0; i != vec3d::size(); i++) +  { +    // compute the slab +    interval slab(_lfb[i], _rbt[i]); +    slab -= r.origin()[i]; + +    // check for the case where the ray is parallel to the slab +    if(fabs(r.direction()[i]) < EPSILON) +    { +      // if identical signs => no hit +      if((slab.lower() < 0.0f) == (slab.upper() < 0.0f)) +	return false; + +      // skip remainder to this iteration +      continue; +    } +    else +      slab /= r.direction()[i]; + +    // intersect +    boxInterval.intersect(slab); +    if(boxInterval.empty()) +      return false; +  } + +  // Done. +  return true;   +} + + +vec3d boundingBox::center(void) const +{ +  return 0.5f * (_lfb + _rbt); +} + + +vec3d boundingBox::corner(bool left, bool front, bool bottom) const +{ +  return vec3d( left ? _lfb.x : _rbt.x, +		front ? _lfb.y : _rbt.y, +		bottom ? _lfb.z : _rbt.z ); +} + + +////////////// +// Mutators // +////////////// +boundingBox& boundingBox::transform(const transformation3d& t) +{ +  boundingBox result; +  for(unsigned int i=0; i <= 1; i++) +    for(unsigned int j=0; j <= 1; j++) +      for(unsigned int k=0; k <= 1; k++) +	result += t.transformPoint(corner(i,j,k)); + +  // Done. +  _swap(result); +  return *this; +} + + +boundingBox& boundingBox::inverseTransform(const transformation3d& t) +{ +  boundingBox result; +  for(unsigned int i=0; i <= 1; i++) +    for(unsigned int j=0; j <= 1; j++) +      for(unsigned int k=0; k <= 1; k++) +	result += t.inverseTransformPoint(corner(i,j,k)); + +  // Done. +  _swap(result); +  return *this; +} + +///////////////////// +// Private Methods // +///////////////////// +void boundingBox::_swap(boundingBox& bb) +{ +  swap(_lfb, bb._lfb); +  swap(_rbt, bb._rbt); +} + + +void boundingBox::_assign(const boundingBox& bb) +{ +  _lfb = bb._lfb; +  _rbt = bb._rbt; +} diff --git a/hw4/src/bvh_intersector.cpp b/hw4/src/bvh_intersector.cpp new file mode 100644 index 0000000..6f9129f --- /dev/null +++ b/hw4/src/bvh_intersector.cpp @@ -0,0 +1,53 @@ +/******************************************************************/ +/* 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 "bvh_intersector.h" +#include "boundedVolumeNode.h" + +////////////////// +// Constructors // +////////////////// +bvh_intersector::bvh_intersector(void) +  : intersector_base() +{ +  // Do nothing +} + +bvh_intersector::bvh_intersector(const std::vector<std::shared_ptr<const boundedPrimitive>>& compounds) +  : intersector_base() +{ +  // quick bail out if no compounds provided +  if(compounds.empty()) return;  + +  // make a copy +  std::vector<std::shared_ptr<const boundedPrimitive>> compoundCopy = compounds; + +  // create node +  //   trivial case: only a single node +  if(compounds.size() == 1) +    _root = compounds[0]; +  //   compound case: create a bvh +  else +    _root = std::shared_ptr<const boundedPrimitive>(new boundedVolumeNode(compoundCopy.begin(), compoundCopy.end())); +} + +   +///////////// +// Methods // +///////////// +intersectionPoint bvh_intersector::intersect(const ray& r) const +{ +  // check if BVH exists => empty intersection point if not. +  if(!_root) return intersectionPoint(); + +  // intersect +  return _root->intersect(r); +} + + + diff --git a/hw4/src/camera.cpp b/hw4/src/camera.cpp new file mode 100644 index 0000000..3393dc9 --- /dev/null +++ b/hw4/src/camera.cpp @@ -0,0 +1,150 @@ +/******************************************************************/ +/* 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 <cmath> + +#include "constants.h" +#include "camera.h" + +///////////////// +// Constructor // +///////////////// +camera::camera(void) +{ +  _eye = vec3d(); +  _view = vec3d(0.0f, 0.0f, -1.0f); +  _up = vec3d(0.0f, 1.0f, 0.0f); +  _fov = 60.0f; +  _width = _height = 256; +} + + +camera::camera(const vec3d& eye, const vec3d& viewDirection, const vec3d& up, float fov, size_t xres, size_t yres) +{ +  _eye = eye; +  _view = normalize(viewDirection); +  _up = normalize(up); +  _fov = fov; +  _width = xres; +  _height = yres; + +  // fix up if needed +  vec3d right = _view.cross(up).normalize(); +  _up = right.cross(_view).normalize(); +} + + +camera::camera(const camera& cam) +{ +  _eye = cam._eye; +  _view = cam._view; +  _up = cam._up; +  _fov = cam._fov; +  _width = cam._width; +  _height = cam._height; +} + + +/////////////// +// Operators // +/////////////// +camera& camera::operator=(const camera& cam) +{ +  _assign(cam); +  return *this; +} + + +ray camera::operator()(float x, float y) const +{ +  vec3d right = _view.cross(_up).normalize(); + +  // aspect ratio +  float aspect = (float)(_height) / (float)(_width); +  float tanFov = tan(_fov / 180.0f * PI); + +  // compute view plane center, and U and V unnormalized axes. +  vec3d center = _eye + _view; +  vec3d U = 2.0f * tanFov * right; +  vec3d V = -2.0f * tanFov * aspect * _up;  // y runs from top to bottom (opposite direction of up) + +  // get point on view plane +  vec3d p = center + (x / (float)(_width) - 0.5f) * U + (y / (float)(_height) - 0.5f) * V; + +  // Done. +  return ray(_eye, p - _eye); +} + + +////////////// +// Mutators // +////////////// +void camera::frameBoundingBox(const boundingBox& bb) +{ +  // determine the best eye location, given the other parameters and a bounding box such that the bounding box maximally occupies the view +  vec3d right = _view.cross(_up).normalize(); +  _eye = 0.5f * (bb.corner(true,true,true) + bb.corner(false,false,false)); + +  // => find max projection in up and right direction. +  float maxRight=-LARGE, maxUp=-LARGE, maxDepth=-LARGE; +  for(unsigned int i=0; i < 8; i++) +  { +    vec3d c = bb.corner( (i&1)==1, (i&2)==2, (i&4)==4 ) - _eye; +     +    float r = fabs(c.dot(right)); +    float u = fabs(c.dot(_up)); +    float d = fabs(c.dot(_view)); + +    maxRight = std::max(maxRight, r); +    maxUp = std::max(maxUp, u); +    maxDepth = std::max(maxDepth, d); +  } + +  // => compute optimal distance for up and right +  float aspect = (float)(_height) / (float)(_width); +  float tanFov = tan(_fov / 180.0f * PI); +  float optDistUp = fabs(maxUp / tanFov); +  float optDistRight = fabs(maxRight / (tanFov * aspect)); +   +  // => move _eye back based on (max) optimal distance +  _eye -= _view * (std::max(optDistUp, optDistRight) + maxDepth); +} + + +///////////////////// +// Private Methods // +///////////////////// +void camera::_swap(camera& cam) +{ +  // sanity check +  if(&cam == this) return; + +  // swap +  swap(_eye, cam._eye); +  swap(_view, cam._view); +  swap(_up, cam._up); +  std::swap(_fov, cam._fov); +  std::swap(_width, cam._width); +  std::swap(_height, cam._height); +} + + +void camera::_assign(const camera& cam) +{ +  // sanity check +  if(&cam == this) return; + +  // copy +  _eye = cam._eye; +  _view = cam._view; +  _up = cam._up; +  _fov = cam._fov; +  _width = cam._width; +  _height = cam._height; +} diff --git a/hw4/src/color.cpp b/hw4/src/color.cpp new file mode 100644 index 0000000..b2b720f --- /dev/null +++ b/hw4/src/color.cpp @@ -0,0 +1,233 @@ +/******************************************************************/ +/* 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 <cmath> +#include <cassert> +#include <algorithm> +#include "color.h" + +////////////////// +// Constructors // +////////////////// +color::color(color::const_reference value) +{  +  r = g = b = value; +} + + +color::color(color::const_reference r, color::const_reference g, color::const_reference b)  +{ +  this->r = r; +  this->g = g; +  this->b = b; +} + + +color::color(const color& col) +{ +  r = col.r; +  g = col.g; +  b = col.b; +} +  + +//////////////// +// Inspectors // +//////////////// +color::const_reference color::operator[](size_t index) const  +{  +  assert(index < size()); +  return data[index];  +} + + +color::reference color::operator[](size_t index)  +{  +  assert(index < size()); +  return data[index];  +} + + +color::iterator color::begin(void) +{ +  return data; +} + + +color::const_iterator color::begin(void) const +{ +  return data; +} + + +color::iterator color::end(void)  +{ +  return begin() + size(); +} + + +color::const_iterator color::end(void) const +{ +  return begin() + size(); +} + + +/////////////// +// Operators // +/////////////// +color& color::operator=(const color& col)  +{  +  _assign(col);  +  return *this;  +} + + +bool color::operator==(const color& col) const  +{  +  return (r == col.r) && (g == col.g) && (b == col.b);  +} + + +bool color::operator!=(const color& col) const  +{  +  return (r != col.r) || (g != col.g) || (b != col.b);  +} +   + +color color::operator+(const color& col) const  +{  +  return color(r + col.r, g + col.g, b + col.b);  +} + + +color color::operator-(const color& col) const  +{  +  return color(r - col.r, g - col.g, b - col.b);  +} + + +color color::operator*(const color& col) const  +{  +  return color(r * col.r, g * col.g, b * col.b);  +} + + +color color::operator*(color::const_reference scale) const  +{  +  return color(r * scale, g * scale, b * scale);  +} + + +color color::operator/(const color& col) const  +{  +  return color(r / col.r, g / col.g, b / col.b);  +} + + +color color::operator/(color::const_reference scale) const  +{  +  return color(r / scale, g / scale, b / scale);  +} + + +color& color::operator+=(const color& col)  +{  +  r += col.r;  +  g += col.g;  +  b += col.b;  +  return *this;  +} + + +color& color::operator-=(const color& col)  +{  +  r -= col.r;  +  g -= col.g;  +  b -= col.b;  +  return *this;  +} + + +color& color::operator*=(const color& col)  +{  +  r *= col.r;  +  g *= col.g; +  b *= col.b;   +  return *this;  +} + + +color& color::operator*=(color::const_reference scale)  +{  +  r *= scale;  +  g *= scale;  +  b *= scale;  +  return *this;  +} + + +color& color::operator/=(const color& col)  +{  +  r /= col.r;  +  g /= col.g;  +  b /= col.b;  +  return *this;  +}							 + + +color& color::operator/=(color::const_reference scale)  +{  +  r /= scale;  +  g /= scale;  +  b /= scale;  +  return *this;  +} + + + +/////////////// +// Modifiers // +/////////////// +color& color::abs(void) +{ +  std::for_each(begin(), end(), [](reference val)  +  {  +    if(val<0) val = -val; +  }); +  return *this; +} + + +color& color::clamp(const_reference lowerBound, const_reference upperBound) +{ +  std::for_each(begin(), end(), [&](reference val)  +  {  +    if(val < lowerBound) val = lowerBound; +    else if(val > upperBound) val = upperBound;  +  }); +  return *this; +} + + +///////////////////// +// Private Methods // +///////////////////// +void color::_assign(const color& col) +{  +  r = col.r;   +  g = col.g;  +  b = col.b;  +} + + +void color::_swap(color& col)  +{ +  std::swap(r, col.r); +  std::swap(g, col.g); +  std::swap(b, col.b); +} diff --git a/hw4/src/compoundShader.cpp b/hw4/src/compoundShader.cpp new file mode 100644 index 0000000..5e19f1f --- /dev/null +++ b/hw4/src/compoundShader.cpp @@ -0,0 +1,60 @@ +/******************************************************************/ +/* 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 <algorithm> +#include "compoundShader.h" + +////////////////// +// Constructors // +////////////////// +compoundShader::compoundShader(const std::vector<std::shared_ptr<const shader_base>>& shader_list) +  : shader_base() +{ +  _compound = shader_list; +} + + +///////////// +// Methods // +///////////// +color compoundShader::shade(const intersectionPoint& ip, const vec3d& light_dir) const +{ +  color result(0.0f, 0.0f, 0.0f); +  for_each(_compound.begin(), _compound.end(), [&](const std::shared_ptr<const shader_base>& shader) +  { +    result += shader->shade(ip, light_dir); +  }); + +  // Done. +  return result; +} + + +shaderProperties compoundShader::properties(const intersectionPoint& ip) const +{ +  // if any component has a property set, then set it for the compound +  shaderProperties result(false, false); +  for(unsigned int i=0; i < _compound.size(); i++) +    result |= _compound[i]->properties(ip); + +  // Done. +  return result; +} + + +void compoundShader::_print(std::ostream& s) const +{ +  s << "Compound Shader (" << _compound.size() << " components): {"; +  for(unsigned int i=0; i < _compound.size(); i++) +  { +    s << *_compound[i]; +    if(i != _compound.size()-1) s << ", "; +  } +  s << "}"; +} diff --git a/hw4/src/coordinateTransform.cpp b/hw4/src/coordinateTransform.cpp new file mode 100644 index 0000000..29e19f4 --- /dev/null +++ b/hw4/src/coordinateTransform.cpp @@ -0,0 +1,67 @@ +/******************************************************************/ +/* 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 "coordinateTransform.h" + +////////////////// +// Constructors // +////////////////// +coordinateTransformation::coordinateTransformation(void) +{ +  // do nothing +} + + +coordinateTransformation::coordinateTransformation(const vec3d& normal) +{ +  // normal == Z axis +  vec3d X, Y, Z = normalize(normal); +   +  // select Y axis +  if(fabs(Z.x) < fabs(Z.y) && fabs(Z.x) < fabs(Z.z)) +    Y = normalize(vec3d(0.0f, Z.z, -Z.y)); +  else if(fabs(Z.y) < fabs(Z.z)) +    Y = normalize(vec3d(Z.z, 0.0f, -Z.x)); +  else +    Y = normalize(vec3d(Z.y, -Z.x, 0.0f)); +   +  // create other axis +  X = Y.cross(Z).normalize(); + +  // copy +  _transformation = mat3d(X, Y, Z); +  _inverseTransformation = transpose(_transformation); + +  // Done. +} + + +coordinateTransformation::coordinateTransformation(const vec3d& normal, const vec3d& axis) +{ +  // normal == Z axis +  vec3d Z = normalize(normal); +   +  // create other axis +  vec3d axis_normalized = normalize(axis); +  vec3d Y = Z.cross(axis_normalized).normalize(); +  vec3d X = Y.cross(Z).normalize(); + +  // copy +  _transformation = mat3d(X, Y, Z); +  _inverseTransformation = transpose(_transformation); + +  // Done. +} + + +coordinateTransformation::coordinateTransformation(const vec3d& X, const vec3d& Y, const vec3d& Z) +{ +  // trust user to provide an orthogonal set of vectors. +  _transformation = mat3d(X, Y, Z); +  _inverseTransformation = transpose(_transformation); +} diff --git a/hw4/src/diffuseBrdf.cpp b/hw4/src/diffuseBrdf.cpp new file mode 100644 index 0000000..4e49a67 --- /dev/null +++ b/hw4/src/diffuseBrdf.cpp @@ -0,0 +1,77 @@ +/******************************************************************/ +/* 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 <cmath> +#include "constants.h" +#include "diffuseBrdf.h" + +////////////////// +// Constructors // +////////////////// +diffuseBrdf::diffuseBrdf(const color& albedo) +{ +  _albedo = albedo; +} + + +diffuseBrdf::diffuseBrdf(const diffuseBrdf& src) +{ +  _albedo = src._albedo; +} + + +/////////////// +// Operators // +/////////////// +diffuseBrdf& diffuseBrdf::operator=(const diffuseBrdf& src) +{ +  _assign(src); +  return *this; +} + + +///////////// +// Methods // +///////////// +color diffuseBrdf::shade(const vec3d& in, const vec3d& out) const +{ +  if(in.z < 0.0f || out.z < 0.0f) return color(); +  else return _albedo * in.z; +} + + +bool diffuseBrdf::isSpecular(void) const +{ +  return false; +} + + +bool diffuseBrdf::isDiffuse(void) const +{ +  return true; +} + + +///////////////////// +// Private Methods // +///////////////////// +void diffuseBrdf::_assign(const diffuseBrdf& src) +{ +  _albedo = src._albedo; +} + + +void diffuseBrdf::_swap(diffuseBrdf& src) +{ +  swap(_albedo, src._albedo); +} + +void diffuseBrdf::_print(std::ostream& s) const +{ +  s << "Diffuse BRDF: albedo=" << _albedo; +} diff --git a/hw4/src/diffuseReflectanceShader.cpp b/hw4/src/diffuseReflectanceShader.cpp new file mode 100644 index 0000000..4d2d2a4 --- /dev/null +++ b/hw4/src/diffuseReflectanceShader.cpp @@ -0,0 +1,35 @@ +/******************************************************************/ +/* 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 "diffuseReflectanceShader.h" + + +///////////////// +// Constructor // +///////////////// +diffuseReflectanceShader::diffuseReflectanceShader(const colorReflectanceParameter& albedo) +  : reflectanceShader_base() +{ +  _albedo = albedo; +} + + +/////////////////////// +// Protected Methods // +/////////////////////// +std::unique_ptr<const brdf_base> diffuseReflectanceShader::make_brdf(const vec2d& textureCoord) const +{ +  return std::unique_ptr<const brdf_base>(new diffuseBrdf( _albedo(textureCoord) )); +} + + +void diffuseReflectanceShader::_print(std::ostream& s) const +{ +  s << "Diffuse Reflectance: albedo=" << _albedo; +} diff --git a/hw4/src/directionalLightsource.cpp b/hw4/src/directionalLightsource.cpp new file mode 100644 index 0000000..4c53895 --- /dev/null +++ b/hw4/src/directionalLightsource.cpp @@ -0,0 +1,38 @@ +/******************************************************************/ +/* 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 "constants.h" +#include "directionalLightsource.h" + +////////////////// +// Constructors // +////////////////// +directionalLightsource::directionalLightsource(const vec3d& direction, const color& power) +{ +  _direction = normalize(direction); +  _power = power; +} + + +///////////// +// Methods // +///////////// +lightSample directionalLightsource::intensityAt(const vec3d& point) const +{ +  return lightSample(_direction, _power, +LARGE); +} + + +///////////////////// +// Private Methods // +///////////////////// +void directionalLightsource::_print(std::ostream& s) const +{ +  s << "Directional Lightsource: direction=" << _direction << ", power=" << _power;   +} diff --git a/hw4/src/errorMessage.cpp b/hw4/src/errorMessage.cpp new file mode 100644 index 0000000..3eddb6a --- /dev/null +++ b/hw4/src/errorMessage.cpp @@ -0,0 +1,34 @@ +/******************************************************************/ +/* 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 "errorMessage.h" + +#include <cstdio> +#include <cstdlib> + +void errorMessage(const char* msg, ...) +{ +  printf("ERROR: "); +  va_list args; +  va_start(args, msg); +  vprintf(msg, args); +  va_end(args); +  printf("\r\n"); +  exit(-1); +} + + +void warningMessage(const char* msg, ...) +{ +  printf("WARNING: "); +  va_list args; +  va_start(args, msg); +  vprintf(msg, args); +  va_end(args); +  printf("\r\n"); +} diff --git a/hw4/src/image.cpp b/hw4/src/image.cpp new file mode 100644 index 0000000..59434cc --- /dev/null +++ b/hw4/src/image.cpp @@ -0,0 +1,122 @@ +/******************************************************************/ +/* 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 "image.h" + +#include <algorithm> +#include <cassert> + +////////////////// +// Constructors // +////////////////// +image::image(image::size_type width, image::size_type height) : _width(width), _height(height), _data() +{ +  if(width != 0 && height != 0) +    _data.reset(new value_type[width*height]); +} + + +image::image(image::size_type width, image::size_type height, const_reference col) : image(width, height) +{ +  std::fill(begin(), end(), col); +} + + +image::image(const image& src) : image(src.width(), src.height()) +{ +  std::copy(src.begin(), src.end(), begin()); +} + + +image::image(image&& src) +{ +  _swap(src); +} + + +//////////////// +// Inspectors // +//////////////// +image::iterator image::begin(void) +{ +  return _data.get(); +} + + +image::const_iterator image::begin(void) const +{ +  return _data.get(); +} + + +image::iterator image::end(void) +{ +  return begin() + size(); +} + + +image::const_iterator image::end(void) const +{ +  return begin() + size(); +} + + +/////////////// +// Operators // +/////////////// +image& image::operator=(const image& src) +{ +  _assign(src); +  return *this; +} + + +image& image::operator=(image&& src) +{ +  _swap(src); +  return *this; +} + +image::reference image::operator()(image::size_type x, image::size_type y) +{ +  assert(x >= 0 && x < width()); +  assert(y >= 0 && y < height()); +  return begin()[y*width() + x]; +} + + +image::const_reference image::operator()(image::size_type x, image::size_type y) const +{ +  assert(x >= 0 && x < width()); +  assert(y >= 0 && y < height()); +  return begin()[y*width() + x]; +} + + +///////////////////// +// Private Methods // +///////////////////// +void image::_swap(image& img) +{ +  std::swap(_width, img._width); +  std::swap(_height, img._height); +  std::swap(_data, img._data); +} + + +void image::_assign(const image& src) +{ +  // sanity check +  if(&src == this) return; + +  // make copy +  image temp(src); +  _swap(temp); + +  // Done +} diff --git a/hw4/src/imageIO.cpp b/hw4/src/imageIO.cpp new file mode 100644 index 0000000..cee716b --- /dev/null +++ b/hw4/src/imageIO.cpp @@ -0,0 +1,46 @@ +/******************************************************************/ +/* 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 "util.h" +#include "imageIO.h" +#include "imageIO.ppm.h" +#include "imageIO.pfm.h" +#include "errorMessage.h" + + +//////////// +// Import // +//////////// +void importImage(const std::string& name, image& img) +{ +  // get extension +  std::string ext = getExtension(name); +  if(ext == "") errorMessage("Unable to determine extension in '%s'.", name.c_str()); + +  // call image format handler based on extension +  if(ext == "PPM" || ext == "ppm") importPPM(name, img); +  else if(ext == "PFM" || ext == "pfm") importPFM(name, img); +  else errorMessage("Unknown image format: '%s'.", ext.c_str()); +} + + +//////////// +// Export // +//////////// +void exportImage(const std::string& name, const image& img) +{ +  // get extension +  std::string ext = getExtension(name); +  if(ext == "") errorMessage("Unable to determine extension in '%s'.", name.c_str()); + +  // call image format handler based on extension +  if(ext == "PPM" || ext == "ppm") exportPPM(name, img); +  else if(ext == "PFM" || ext == "pfm") exportPFM(name, img); +  else errorMessage("Unknown image format: '%s'.", ext.c_str()); +} + diff --git a/hw4/src/imageIO.pfm.cpp b/hw4/src/imageIO.pfm.cpp new file mode 100644 index 0000000..85604a0 --- /dev/null +++ b/hw4/src/imageIO.pfm.cpp @@ -0,0 +1,87 @@ +/******************************************************************/ +/* 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 "imageIO.pfm.h" +#include "errorMessage.h" + +#include <memory> +#include <cassert> +#include <fstream> + +////////////////////// +// Helper functions // +////////////////////// +static void skipToNewline(std::ifstream& ifs) +{ +  while(ifs.get() != '\n' && ifs.good());  +} + +static void skipComments(std::ifstream& ifs) +{ +  while(!ifs.eof() && ifs.peek() == '#') skipToNewline(ifs);  +} + + +//////////////// +// Import PFM // +//////////////// +void importPFM(const std::string& name, image& img) +{ +  // open file +  std::ifstream ifs(name.c_str()); +  if(!ifs.is_open()) errorMessage("Unable to open file: '%s'.", name.c_str()); + +  // read header +  std::string magicMark; +  ifs >> magicMark; +  skipToNewline(ifs); +  if(magicMark != "PF") errorMessage("Unsupported PFM format (%s).", magicMark.c_str()); + +  // read width and height +  image::size_type width, height; +  skipComments(ifs); +  ifs >> width >> height; +  skipToNewline(ifs); + +  // allocate +  img = image(width, height); + +  // check magic number (again) +  skipComments(ifs); +  ifs >> magicMark; +  skipToNewline(ifs); +  if(magicMark != "-1.000000") errorMessage("Unsupported byte-order in PFM."); + +  // read directly in float image +  ifs.read((char *)(img.begin()), img.size() * sizeof(color)); + +  // Done. +} + + +//////////////// +// Export PFM // +//////////////// +void exportPFM(const std::string& name, const image& img) +{ +  // sanity check +  assert(img.width() != 0 && img.height() != 0); + +  // open file +  std::ofstream ofs(name.c_str(), std::ofstream::binary); +  if(!ofs.is_open()) errorMessage("Unable to open file: '%s'.", name.c_str()); + +  // write header +  ofs << "PF\n" << img.width() << " " << img.height() << "\n-1.000000\n"; + +  // write float buffer +  ofs.write((const char*)(img.begin()), img.size() * sizeof(color)); + +  // Done. +} + diff --git a/hw4/src/imageIO.ppm.cpp b/hw4/src/imageIO.ppm.cpp new file mode 100644 index 0000000..8a92acb --- /dev/null +++ b/hw4/src/imageIO.ppm.cpp @@ -0,0 +1,106 @@ +/******************************************************************/ +/* 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 "imageIO.ppm.h" +#include "errorMessage.h" + +#include <memory> +#include <cassert> +#include <cstdint> +#include <fstream> +#include <algorithm> + +////////////////////// +// Helper functions // +////////////////////// +static void skipToNewline(std::ifstream& ifs) +{ +  while(ifs.get() != '\n' && ifs.good());  +} + +static void skipComments(std::ifstream& ifs) +{ +  while(!ifs.eof() && ifs.peek() == '#') skipToNewline(ifs);  +} + + +//////////////// +// Import PPM // +//////////////// +void importPPM(const std::string& name, image& img) +{ +  // open file +  std::ifstream ifs(name.c_str()); +  if(!ifs.is_open()) errorMessage("Unable to open file: '%s'.", name.c_str()); + +  // read header +  std::string magicMark; +  ifs >> magicMark; +  skipToNewline(ifs); +  if(magicMark != "P6") errorMessage("Unsupported PPM format (%s).", magicMark.c_str()); + +  // read width & height +  image::size_type width, height; +  skipComments(ifs); +  ifs >> width >> height; +  skipToNewline(ifs); + +  // allocate +  img = image(width, height); + +  // check magic number (again) +  skipComments(ifs); +  ifs >> magicMark; +  skipToNewline(ifs); +  if(magicMark != "255") errorMessage("Unsupported bit-depth in PPM file (%s).", magicMark.c_str()); + +  // read char buffer +  std::unique_ptr<uint8_t[]> tempBuffer(new uint8_t[img.size() * 3]); +  ifs.read((char *)(tempBuffer.get()), img.size() * 3); + +  // convert to image +  std::transform(tempBuffer.get(), tempBuffer.get() + (img.size()*3), img.begin()->begin(), [](uint8_t val)  +  {  +    return (float)(val) / 255.0f;  +  }); + +  // Done. +} + + +//////////////// +// Export PPM // +//////////////// +void exportPPM(const std::string& name, const image& img) +{ +  // sanity check +  assert(img.width() != 0 && img.height() != 0); + +  // open file +  std::ofstream ofs(name.c_str(), std::ofstream::binary); +  if(!ofs.is_open()) errorMessage("Unable to open file: '%s'.", name.c_str()); + +  // write header +  ofs << "P6\n" << img.width() << " " << img.height() << "\n" << "255\n"; + +  // convert to char buffer +  std::unique_ptr<uint8_t[]> tempBuffer(new uint8_t[img.size() * 3]); +  std::transform(img.begin()->begin(), img.end()->begin(), tempBuffer.get(), [](float val) +  {  +    return (val < 0.0f) ? 0 :  +           (val > 1.0f) ? 255 :  +           uint8_t(val*255);  +   }); +     +  // write body +  ofs.write((const char*)(tempBuffer.get()), img.size() * 3); + +  // Done. +} + + diff --git a/hw4/src/importOBJ.cpp b/hw4/src/importOBJ.cpp new file mode 100644 index 0000000..ad1e4a8 --- /dev/null +++ b/hw4/src/importOBJ.cpp @@ -0,0 +1,152 @@ +/******************************************************************/ +/* 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 <memory> +#include <string> +#include <cassert> +#include <fstream> + +#include "vec3d.h" +#include "vec2d.h" +#include "importOBJ.h" +#include "errorMessage.h" + +void importOBJ(const std::string filename, std::vector<triangle>& triangle_list) +{ +  // define buffers +  auto vertex_list = std::make_shared<std::vector<vec3d>>(); +  auto normal_list = std::make_shared<std::vector<vec3d>>(); +  auto textureCoord_list = std::make_shared<std::vector<vec2d>>(); + +  // open obj file +  std::ifstream ifs(filename, std::ifstream::binary | std::ifstream::in); +  if(!ifs.is_open())  +    errorMessage("Unable to import OBJ (%s).", filename.c_str()); + +  // read line by line +  unsigned char key; +  while(ifs.good()) +  { +    // get key +    key = ifs.get(); + +    // COMMENTS: +    if(key == '#' || key == ' ')  +    { +      std::string comment; +      ifs >> comment;  // comment: skip line +    } + +    // VERTEX DATA: +    else if(key == 'v') +    { +      // get vertex data +      key = ifs.get(); + +      // Vertex Coordinate +      if(key == ' ')  +      { +	vec3d v; +	ifs >> v.x >> v.y >> v.z; +	vertex_list->push_back(v); +      } + +      // Texture Coordinate +      else if(key == 't') +      { +	vec2d t; +	ifs >> t.u >> t.v; +	textureCoord_list->push_back(t); +      } + +      // Normal +      else if(key == 'n')   +      { +	vec3d n; +	ifs >> n.x >> n.y >> n.z; +	normal_list->push_back(n); +      } + +      // Unknown => ERROR +      else errorMessage("Unknown OBJ vertex-key: v%c.", key); +    } + +    // POLYGON: +    else if(key == 'f') +    { +      // temp data structures  +      std::vector<unsigned int> vidx; +      std::vector<unsigned int> nidx; +      std::vector<unsigned int> tidx; +       + +      // get polygon data +      // can be either:  +      // 1)  v0 v1 .. vn +      // 2)  v0//n0 v1//n1 .. vn//nn +      // 3)  v0/t0 v1/t1 ... vn/tn +      // 4)  v0/t0/n0 ... vn/tn/nn +      bool done=false; +      while(!done) +      { +	// read entry +	signed int vi, vt=-1, vn=-1; +	ifs >> vi; +	unsigned char c = ifs.get(); + +	if(c == '/') +	{ +	  c = ifs.get(); +	  if(c != '/') { ifs.unget(); ifs >> vt; c = ifs.get(); } +	  if(c == '/') { ifs >> vn; } +	} + +	// store in polygon +        if(vt != -1) tidx.push_back(vt-1); +        if(vn != -1) nidx.push_back(vn-1); +        if(vi != -1) vidx.push_back(vi-1); + +	// check if we did not eat the end-of-line chararters +	ifs.unget(); +	c = ifs.get(); +	if(c == '\r' || c == '\n') done = true; + +	// eat remaining spaces +	c  = ifs.get(); +	while(c == ' ') +	  c = ifs.get(); + +	// check again for end +	if(c == '\r' || c == '\n') done = true; +	ifs.unget(); + +        // sanity check +        assert(tidx.empty() || tidx.size() == vidx.size()); +        assert(nidx.empty() || nidx.size() == vidx.size()); + +        // store triangle +        if(vidx.size() >= 3) +        { +          size_t m=vidx.size() - 2; +          size_t l=vidx.size() - 1; +          if(tidx.empty() && nidx.empty()) triangle_list.push_back( triangle(vidx[0], vidx[m], vidx[l], vertex_list) ); +          if(tidx.empty() && !nidx.empty()) triangle_list.push_back( triangle(vidx[0], vidx[m], vidx[l], vertex_list, +									      nidx[0], nidx[m], nidx[l], normal_list) ); +          if(!tidx.empty() && nidx.empty()) triangle_list.push_back( triangle(vidx[0], vidx[m], vidx[l], vertex_list, +									      tidx[0], tidx[m], tidx[l], textureCoord_list) ); +          if(!tidx.empty() && !nidx.empty()) triangle_list.push_back( triangle(vidx[0], vidx[m], vidx[l], vertex_list, +									       nidx[0], nidx[m], nidx[l], normal_list, +									       tidx[0], tidx[m], tidx[l], textureCoord_list) ); +        } +      } +    } +  } + +  // Done. +} diff --git a/hw4/src/intersectionPoint.cpp b/hw4/src/intersectionPoint.cpp new file mode 100644 index 0000000..cbdcf11 --- /dev/null +++ b/hw4/src/intersectionPoint.cpp @@ -0,0 +1,306 @@ +/******************************************************************/ +/* 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 <cassert> +#include "shader_base.h" +#include "intersectionPoint.h" + +///////////////// +// Constructor // +///////////////// +intersectionPoint::intersectionPoint(void) +{ +  _hit = false; +} + + +intersectionPoint::intersectionPoint(const ray& r, float rayParameter, const std::shared_ptr<const shader_base>& shader, const vec3d& normal, const vec3d& axis, const vec2d& textureCoordinate) +{ +  // copy +  _ray = r; +  _rayParameter = rayParameter; +  _hit = true; +  _shader = shader; +  _normal = normalize(normal); +  _axis = normalize(axis); +  _textureCoordinate = textureCoordinate; + +  // compute position +  _position = _ray(_rayParameter); +} + + +intersectionPoint::intersectionPoint(const intersectionPoint& ip) +{ +  _hit = ip._hit; + +  // copy remainder if hit +  if(_hit) +  { +    _ray = ip._ray; +    _rayParameter = ip._rayParameter; +    _shader = ip._shader; +    _normal = ip._normal; +    _position = ip._position; +    _axis = ip._axis; +    _textureCoordinate = ip._textureCoordinate; +  } +  else _shader = nullptr; +} + +/////////////// +// Operators // +/////////////// +intersectionPoint& intersectionPoint::operator=(const intersectionPoint& ip) +{ +  _assign(ip); +  return *this; +} + + +bool intersectionPoint::operator<(const intersectionPoint& ip) const +{ +  // handle case where either one of the intersectionPoints is not a hit +  // => assume no-hit equals a hit at infinity +  if(!_hit) return false; +  if(!ip._hit) return true; + +  // decide based on rayParameter +  return _rayParameter < ip._rayParameter; +} + + +bool intersectionPoint::operator>(const intersectionPoint& ip) const +{ +  // handle case where either one of the intersectionPoints is not a hit +  // => assume no-hit equals a hit at infinity +  if(!_hit) return true; +  if(!ip._hit) return false; + +  // decide based on rayParameter +  return _rayParameter > ip._rayParameter; +} + + +bool intersectionPoint::operator<(const lightSample& ls) const +{ +  // handle case where this is not a hit +  if(!_hit) return false; + +  // decide based on rayParameter +  return _rayParameter < ls.distance(); +} + + +bool intersectionPoint::operator>(const lightSample& ls) const +{ +  // handle case where this is not a hit +  if(!_hit) return true; + +  // decide based on rayParameter +  return _rayParameter > ls.distance(); +} + +//////////////// +// Inspectors // +//////////////// +bool intersectionPoint::isHit(void) const +{ +  return _hit; +} + + +shaderProperties intersectionPoint::getShaderProperties(void) const +{ +  assert(hasShader()); +  return _shader->properties(*this); +} + + +bool intersectionPoint::hasShader(void) const +{ +  return (_shader != nullptr); +} + + +float intersectionPoint::distance(const intersectionPoint& ip) const +{ +  // set distance to infinity of one of the two points is not a hit +  if(!_hit || !ip._hit) return +LARGE; + +  // compute distance +  return _position.distance(ip._position); +} + + +const vec3d& intersectionPoint::position(void) const +{ +  return _position; +} + + +const vec3d& intersectionPoint::direction(void) const +{ +  return _ray.direction(); +} + + +const vec2d& intersectionPoint::textureCoordinate(void) const +{ +  return _textureCoordinate; +} + + +const vec3d& intersectionPoint::normal(void) const +{ +  return _normal; +} + + +coordinateTransformation intersectionPoint::shadingFrame(void) const +{ +  // transform => to global shading frame +  // inverseTransform => to local shading frame +  return coordinateTransformation(_normal, _axis); +} + + +////////////// +// Mutators // +////////////// +void intersectionPoint::transform(const transformation3d& t) +{ +  // sanity check +  if(!_hit) return; + +  /// transform +  _ray.transform(t); +  _position = t.transformPoint(_position); +  _normal = t.transformNormal(_normal); +  _axis = t.transformNormal(_axis); + +  // recompute the ray parameter (to account for potential scaling) +  _rayParameter = _ray(_position); +} + + +void intersectionPoint::inverseTransform(const transformation3d& t) +{ +  // sanity check +  if(!_hit) return; + +  // inverse transform +  _ray.inverseTransform(t); +  _position = t.inverseTransformPoint(_position); +  _normal = t.inverseTransformNormal(_normal); +  _axis = t.inverseTransformNormal(_axis); + +  // recompute the ray parameter (to account for potential scaling) +  _rayParameter = _ray(_position); +} + + +void intersectionPoint::transformShadingFrame(const transformation3d& sft) +{ +  // sanity check +  if(!_hit) return; + +  // Note: shading frame transformation are defined +  //       with respect to the local shading frame (i.e., N=Z, axis=X) +  transformation3d t = shadingFrame(); + +  // transform +  _normal = t.transformNormal( sft.transformNormal(vec3d(0.0f, 0.0f, 1.0f)) ); +  _axis = t.transformNormal( sft.transformNormal(vec3d(1.0f, 0.0f, 0.0f)) ); + +  // Done. +} + + +void intersectionPoint::inverseTransformShadingFrame(const transformation3d& sft) +{ +  // sanity check +  if(!_hit) return; + +  // Note: shading frame transformation are defined +  //       with respect to the local shading frame (i.e., N=Z, axis=X) +  // Note: this transformation will not undo the effects of +  //       transformShadingFrame since the local shading frame +  //       is altered by the transformation (and thus the frame +  //       with respect to which the inverseTransformation is  +  //       applied) +  transformation3d t = shadingFrame(); + +  // transform +  _normal = t.transformNormal( sft.inverseTransformNormal(vec3d(0.0f, 0.0f, 1.0f)) ); +  _axis = t.transformNormal( sft.inverseTransformNormal(vec3d(1.0f, 0.0f, 0.0f)) ); + +  // Done. +} + + +void intersectionPoint::setShader(const std::shared_ptr<const class shader_base>& shader) +{ +  _shader = shader; +} + + +///////////// +// Methods // +///////////// +color intersectionPoint::shade(const vec3d& out) const +{ +  assert(hasShader()); +  if(!_hit) return color(0.0f); +  return _shader->shade(*this, out); +} + +color intersectionPoint::shade(const lightSample& ls) const +{ +  assert(hasShader()); +  if(!_hit) return color(0.0f); +  return _shader->shade(*this, ls.directionToLight()) * ls.emittance(); +} + + +///////////////////// +// Private Methods // +///////////////////// +void intersectionPoint::_assign(const intersectionPoint& ip) +{ +  // sanity check +  if(&ip == this) return; + +  // copy if hit +  _hit = ip._hit; +  if(_hit) +  { +    _ray = ip._ray; +    _rayParameter = ip._rayParameter; +    _position = ip._position; +    _normal = ip._normal; +    _axis = ip._axis; +    _textureCoordinate = ip._textureCoordinate; +    _shader = ip._shader; +  } +  else _shader = nullptr; +} + + +void intersectionPoint::_swap(intersectionPoint& ip) +{ +  swap(_ray, ip._ray); +  std::swap(_hit, ip._hit); +  std::swap(_rayParameter, ip._rayParameter); +  swap(_position, ip._position); +  swap(_normal, ip._normal); +  swap(_axis, ip._axis); +  swap(_textureCoordinate, ip._textureCoordinate); +  std::swap(_shader, ip._shader); +} + diff --git a/hw4/src/interval.cpp b/hw4/src/interval.cpp new file mode 100644 index 0000000..4323cbd --- /dev/null +++ b/hw4/src/interval.cpp @@ -0,0 +1,154 @@ +/******************************************************************/ +/* 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 "interval.h" +#include "constants.h" + +////////////////// +// Constrructor // +////////////////// +interval::interval(float lower, float upper) +{ +  _lower = lower; +  _upper = upper; + +  // ensure that _lower < _upper +  if(_upper < _lower) std::swap(_upper, _lower); +} + + +interval::interval(const interval& i) +{ +  _lower = i._lower; +  _upper = i._upper; +} + + + +/////////////// +// Operators // +/////////////// +interval& interval::operator=(const interval& i) +{ +  _assign(i); +  return *this; +} + + +interval interval::operator+(float v) const +{ +  return interval(_lower + v, _upper + v);  +} + + +interval interval::operator-(float v) const +{ +  return interval(_lower - v, _upper - v);  +} + + +interval interval::operator*(float v) const +{ +  return interval(_lower * v, _upper * v);  +} + + +interval interval::operator/(float v) const +{ +  return interval(_lower / v, _upper / v);  +} +     + +interval& interval::operator+=(float v) +{ +  _lower += v; +  _upper += v; +  return *this; +} + +interval& interval::operator-=(float v) +{ +  _lower -= v; +  _upper -= v; +  return *this; +} + + +interval& interval::operator*=(float v) +{ +  _lower *= v; +  _upper *= v; + +  // ensure that _lower < _upper +  if(_upper < _lower) std::swap(_upper, _lower); + +  // Done. +  return *this; +} + +interval& interval::operator/=(float v) +{ +  _lower /= v; +  _upper /= v; + +  // ensure that _lower < _upper +  if(_upper < _lower) std::swap(_upper, _lower); + +  // Done. +  return *this; +} + + +//////////////// +// Inspectors // +//////////////// +float interval::lower(void) const +{ +  return _lower; +} + + +float interval::upper(void) const +{ +  return _upper; +} + + +bool interval::empty(void) const +{ +  return (_upper - _lower < EPSILON); +} + + + +////////////// +// Mutators // +////////////// +void interval::intersect(const interval& i) +{ +  _lower = std::max(_lower, i._lower); +  _upper = std::min(_upper, i._upper); +} + + + +///////////////////// +// Private Methods // +///////////////////// +void interval::_assign(const interval& i) +{ +  _lower = i._lower; +  _upper = i._upper; +} + + +void interval::_swap(interval& i) +{ +  std::swap(_lower, i._lower); +  std::swap(_upper, i._upper); +} diff --git a/hw4/src/lightSample.cpp b/hw4/src/lightSample.cpp new file mode 100644 index 0000000..5304a9d --- /dev/null +++ b/hw4/src/lightSample.cpp @@ -0,0 +1,121 @@ +/******************************************************************/ +/* 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 <cmath> +#include "lightSample.h" + + +////////////////// +// Constructors // +////////////////// +lightSample::lightSample(const vec3d& direction, +			 const color& emittance, +			 float distance, +			 float pdf, +			 float foreshortening) +{ +  float length = direction.length(); +  _direction = (length < EPSILON) ? vec3d(0.0f, 0.0f, 0.0f) : direction / length; +  _emittance = emittance; +  _distance = distance; +  _pdf = std::max(pdf, 0.0f); +  _foreshortening = std::max(foreshortening, 0.0f); +} + + +lightSample::lightSample(const lightSample& ls) +{ +  _direction = ls._direction; +  _emittance = ls._emittance; +  _distance = ls._distance; +  _pdf = ls._pdf; +  _foreshortening = ls._foreshortening; +} + + +/////////////// +// Operators // +/////////////// +lightSample& lightSample::operator=(const lightSample& ls) +{ +  _assign(ls); +  return(*this); +} + + +const color& lightSample::operator()(void) const +{ +  return _emittance; +} + + +//////////////// +// Inspectors // +//////////////// +const vec3d& lightSample::directionToPoint(void) const +{ +  return _direction; +} + + +vec3d lightSample::directionToLight(void) const +{ +  return -_direction; +} +   + +const color& lightSample::emittance(void) const +{ +  return _emittance; +} + + +float lightSample::distance(void) const +{ +  return _distance; +} + + +float lightSample::pdf(void) const +{ +  return _pdf; +} + + +float lightSample::foreshortening(void) const +{ +  return _foreshortening; +} + + +///////////////////// +// Private Methods // +///////////////////// +void lightSample::_assign(const lightSample& ls) +{ +  // sanity check +  if(&ls == this) return; + +  // copy +  _direction = ls._direction; +  _emittance = ls._emittance; +  _distance = ls._distance; +  _pdf = ls._pdf; +  _foreshortening = ls._foreshortening; +} + + +void lightSample::_swap(lightSample& ls) +{ +  swap(_direction, ls._direction); +  swap(_emittance, ls._emittance); +  std::swap(_distance, ls._distance); +  std::swap(_pdf, ls._pdf); +  std::swap(_foreshortening, ls._foreshortening); +} +	     diff --git a/hw4/src/linear_intersector.cpp b/hw4/src/linear_intersector.cpp new file mode 100644 index 0000000..c7cb37e --- /dev/null +++ b/hw4/src/linear_intersector.cpp @@ -0,0 +1,53 @@ +/******************************************************************/ +/* 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 "linear_intersector.h" + +////////////////// +// Constructors // +////////////////// +linear_intersector::linear_intersector(void) +  : intersector_base() +{ +  // Do nothing +} + + +linear_intersector::linear_intersector(const std::vector<std::shared_ptr<const boundedPrimitive>>& compounds) +  : intersector_base() +{ +  _compounds = compounds; +} + +   +///////////// +// Methods // +///////////// +intersectionPoint linear_intersector::intersect(const ray& r) const +{ +  intersectionPoint ip; + +  // if no compounds registered => no hit. +  if(_compounds.empty()) return ip; + +  // cycle through the list +  for(auto itr=_compounds.begin(); itr != _compounds.end(); itr++) +  { +    // intersect +    intersectionPoint newIp = (*itr)->intersect(r); + +    // keep track of closest +    if(newIp < ip) ip = newIp; +  } + +  // Done. +  return ip; +} + + + diff --git a/hw4/src/mat3d.cpp b/hw4/src/mat3d.cpp new file mode 100644 index 0000000..7674777 --- /dev/null +++ b/hw4/src/mat3d.cpp @@ -0,0 +1,260 @@ +/******************************************************************/ +/* 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 <cassert> +#include <algorithm> + +#include "mat3d.h" + + +///////////////// +// Constructor // +///////////////// +mat3d::mat3d(void) +{ +  clear(0.0f); +} + + +mat3d::mat3d(mat3d::value_type diag) +  : mat3d() +{ +  setDiagonal(diag); +} + + +mat3d::mat3d(const vec3d& X, const vec3d& Y, const vec3d& Z) +{ +  for(size_type i=0; i < 3; i++) +  { +    this->operator()(i,0) = X[i]; +    this->operator()(i,1) = Y[i]; +    this->operator()(i,2) = Z[i]; +  } +} + + +mat3d::mat3d(const mat3d& m) +{ +  _data = m._data; +} + + +//////////////// +// Inspectors // +//////////////// +mat3d::iterator mat3d::begin(void) +{ +  return _data.begin(); +} + + +mat3d::const_iterator mat3d::begin(void) const +{ +  return _data.begin(); +} + + +mat3d::iterator mat3d::end(void) +{ +  return _data.end(); +} + + +mat3d::const_iterator mat3d::end(void) const +{ +  return _data.end(); +} + + +////////////// +// Mutators // +////////////// +void mat3d::clear(mat3d::value_type value) +{ +  std::fill(begin(), end(), value); +} + + +void mat3d::setDiagonal(mat3d::value_type value) +{ +  (*this)(0,0) = (*this)(1,1) = (*this)(2,2) = value; +} + + +mat3d& mat3d::transpose(void) +{ +  for(size_type row=0; row < height(); row++) +    for(size_type column=row+1; column < width(); column++) +      std::swap( (*this)(row,column), (*this)(column,row) ); +  return *this; +} + + + +/////////////// +// Operators // +/////////////// +mat3d& mat3d::operator=(const mat3d& m) +{ +  _assign(m); +  return *this; +} + + +mat3d::reference mat3d::operator()(mat3d::size_type row, mat3d::size_type col) +{ +  assert(row < height() && col < width()); +  return _data[ row*width() + col]; +} + + +mat3d::const_reference mat3d::operator()(mat3d::size_type row, mat3d::size_type col) const +{ +  assert(row < height() && col < width()); +  return _data[ row*width() + col]; +} + + +mat3d mat3d::operator+(const mat3d& m) const +{ +  mat3d result; +  std::transform(begin(), end(), m.begin(), result.begin(), [](const_reference a, const_reference b) +  { +    return a+b; +  }); +  return result; +} + + +mat3d mat3d::operator-(const mat3d& m) const  +{ +  mat3d result; +  std::transform(begin(), end(), m.begin(), result.begin(), [](const_reference a, const_reference b) +  { +    return a-b; +  }); +  return result; +} + + +mat3d mat3d::operator*(const mat3d& m) const  +{  +  mat3d result; +  for(size_type i=0; i < result.height(); i++) +    for(size_type j=0; j < result.width(); j++) +      for(size_type k=0; k < width(); k++) +	result(i,j) += (*this)(i,k) * m(k, j); +  return result; +} + + +vec3d mat3d::operator*(const vec3d& v) const  +{  +  vec3d result; +  for(size_type i=0; i < height(); i++) +    for(size_type j=0; j < width(); j++) +      result[i] += v[j] * (*this)(i,j); +  return result; +} + + +mat3d mat3d::operator*(mat3d::value_type scale) const  +{ +  mat3d result; +  std::transform(begin(), end(), result.begin(), [&](const_reference v) +  { +    return v * scale; +  }); +  return result; +} + +mat3d mat3d::operator/(mat3d::value_type scale) const  +{ +  mat3d result; +  std::transform(begin(), end(), result.begin(), [&](const_reference v) +  { +    return v / scale; +  }); +  return result; +} + + +mat3d& mat3d::operator+=(const mat3d& m)  +{  +  std::transform(begin(), end(), m.begin(), begin(), [](const_reference a, const_reference b) +  { +    return a+b; +  }); +  return *this;  +} + + +mat3d& mat3d::operator-=(const mat3d& m)  +{ +  std::transform(begin(), end(), m.begin(), begin(), [](const_reference a, const_reference b) +  { +    return a-b; +  }); +  return *this;  +} + + +mat3d& mat3d::operator*=(const mat3d& m)  +{ +  *this = *this * m; +  return *this; +} + + +mat3d& mat3d::operator*=(mat3d::value_type scale)  +{ +  std::for_each(begin(), end(), [&](reference v) +  { +    v *= scale; +  }); +  return *this;  +} + + +mat3d& mat3d::operator/=(mat3d::value_type scale)  +{ +  std::for_each(begin(), end(), [&](reference v) +  { +    v /= scale; +  }); +  return *this;  +} +   + +///////////////////// +// Private Methods // +///////////////////// +void mat3d::_swap(mat3d& m) +{ +  if(&m == this) return; +  std::swap(_data, m._data); +} + + +void mat3d::_assign(const mat3d& m) +{ +  if(&m == this) return; +  _data = m._data; +} + + +vec3d mat3d::_premultiply(const vec3d& v) const +{ +  // result = v * *this +  vec3d result; +  for(size_type i=0; i < height(); i++) +    for(size_type j=0; j < width(); j++) +      result[j] += v[i] * (*this)(i,j); +  return result; +} diff --git a/hw4/src/phongBrdf.cpp b/hw4/src/phongBrdf.cpp new file mode 100644 index 0000000..a4d29bd --- /dev/null +++ b/hw4/src/phongBrdf.cpp @@ -0,0 +1,90 @@ +/******************************************************************/ +/* 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 <cmath> +#include "constants.h" +#include "phongBrdf.h" + +////////////////// +// Constructors // +////////////////// +phongBrdf::phongBrdf(const color& albedo, float sharpness) +{ +  _albedo = albedo; +  _sharpness = sharpness; +} + + +phongBrdf::phongBrdf(const phongBrdf& src) +{ +  _albedo = src._albedo; +  _sharpness = src._sharpness; +} + + +/////////////// +// Operators // +/////////////// +phongBrdf& phongBrdf::operator=(const phongBrdf& src) +{ +  _assign(src); +  return *this; +} + + +///////////// +// Methods // +///////////// +color phongBrdf::shade(const vec3d& in, const vec3d& out) const +{ +  // sanity check (below horizon) +  if(in.z < 0.0f || out.z < 0.0f) return color(0.0f); + +  // compute reflected direction (simplies when n=(0,0,1)) +  vec3d reflected(-in.x, -in.y, in.z); + +  // evaluate phong +  return _albedo * std::pow(std::max( reflected.dot(out), 0.0f), _sharpness); +} + + +bool phongBrdf::isSpecular(void) const +{ +  // Consider low roughness to be diffuse.   +  // Use 5.0f as an arbitrary threshold. +  return (_sharpness > 5.0f); +} + + +bool phongBrdf::isDiffuse(void) const +{ +  return !isSpecular(); +} + + +///////////////////// +// Private Methods // +///////////////////// +void phongBrdf::_assign(const phongBrdf& src) +{ +  _albedo = src._albedo; +  _sharpness = src._sharpness; +} + + +void phongBrdf::_swap(phongBrdf& src) +{ +  swap(_albedo, src._albedo); +  std::swap(_sharpness, src._sharpness); +} + + +void phongBrdf::_print(std::ostream& s) const +{ +  s << "Phong BRDF: albedo=" << _albedo << ", sharpness=" << _sharpness; +} diff --git a/hw4/src/phongReflectanceShader.cpp b/hw4/src/phongReflectanceShader.cpp new file mode 100644 index 0000000..c5ac57c --- /dev/null +++ b/hw4/src/phongReflectanceShader.cpp @@ -0,0 +1,36 @@ +/******************************************************************/ +/* 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 "phongReflectanceShader.h" + + +///////////////// +// Constructor // +///////////////// +phongReflectanceShader::phongReflectanceShader(const colorReflectanceParameter& albedo, const scalarReflectanceParameter& sharpness) +  : reflectanceShader_base() +{ +  _albedo = albedo; +  _sharpness = sharpness; +} + + +/////////////////////// +// Protected Methods // +/////////////////////// +std::unique_ptr<const brdf_base> phongReflectanceShader::make_brdf(const vec2d& textureCoord) const +{ +  return std::unique_ptr<const brdf_base>(new phongBrdf( _albedo(textureCoord), _sharpness(textureCoord) )); +} + + +void phongReflectanceShader::_print(std::ostream& s) const +{ +  s << "Phong Reflectance: albedo=" << _albedo << ", sharpness=" << _sharpness; +} diff --git a/hw4/src/ray.cpp b/hw4/src/ray.cpp new file mode 100644 index 0000000..18c839d --- /dev/null +++ b/hw4/src/ray.cpp @@ -0,0 +1,83 @@ +/******************************************************************/ +/* 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 "ray.h" + +////////////////// +// Constructors // +////////////////// +ray::ray(const vec3d& origin, const vec3d& direction) +{ +  _origin = origin; +  _direction = normalize(direction); +} + +ray::ray(const ray& r) +{ +  _origin = r._origin; +  _direction = r._direction; +} + + +/////////////// +// Operators // +/////////////// +ray& ray::operator=(const ray& r) +{ +  _assign(r); +  return *this; +} + + +vec3d ray::operator()(float t) const +{ +  return _origin + t*_direction; +} + + +float ray::operator()(const vec3d& point) const +{ +  return _direction.dot(point - _origin); +} + + +////////////// +// Mutators // +////////////// +ray& ray::transform(const transformation3d& t) +{ +  _origin = t.transformPoint(_origin); +  _direction = t.transformDirection(_direction); +  return *this; +} + + +ray& ray::inverseTransform(const transformation3d& t) +{ +  _origin = t.inverseTransformPoint(_origin); +  _direction = t.inverseTransformDirection(_direction); +  return *this; +} + + +///////////////////// +// Private Methods // +///////////////////// +void ray::_swap(ray& r) +{ +  swap(_origin, r._origin); +  swap(_direction, r._direction); +} + + +void ray::_assign(const ray& r) +{ +  _origin = r._origin; +  _direction = r._direction; +} diff --git a/hw4/src/reflectanceParameter.cpp b/hw4/src/reflectanceParameter.cpp new file mode 100644 index 0000000..5b918fb --- /dev/null +++ b/hw4/src/reflectanceParameter.cpp @@ -0,0 +1,140 @@ +/******************************************************************/ +/* 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 "reflectanceParameter.h" + +/******************************/ +/* scalarReflectanceParameter */ +/******************************/ + +///////////////// +// Constructor // +///////////////// +scalarReflectanceParameter::scalarReflectanceParameter(float value) +{ +  _value = value; +  _texture = nullptr; +  _channel = 0.0f;  +} + + +scalarReflectanceParameter::scalarReflectanceParameter(const std::shared_ptr<const texture_base>& texture, unsigned int channel) +{ +  _value = 0.0f; +  _texture = texture; +  _channel = channel; +} + + +scalarReflectanceParameter::scalarReflectanceParameter(const scalarReflectanceParameter& src) +{ +  _value = src._value; +  _texture = src._texture; +  _channel = src._channel; +} + + +////////////// +// Operator // +////////////// +scalarReflectanceParameter& scalarReflectanceParameter::operator=(const scalarReflectanceParameter& src) +{ +  _assign(src); +  return *this; +} + + +float scalarReflectanceParameter::operator()(const vec2d& textureCoord) const +{ +  if(_texture) return (*_texture)(textureCoord)[_channel]; +  else return _value; +} + + +///////////////////// +// Private Methods // +///////////////////// +void scalarReflectanceParameter::_assign(const scalarReflectanceParameter& src) +{ +  _value = src._value; +  _texture = src._texture; +  _channel = src._channel; +} + + +void scalarReflectanceParameter::_swap(scalarReflectanceParameter& swp) +{ +  std::swap(_value, swp._value); +  std::swap(_texture, swp._texture); +  std::swap(_channel, swp._channel); +} + + + +/*****************************/ +/* colorReflectanceParameter */ +/*****************************/ + +///////////////// +// Constructor // +///////////////// +colorReflectanceParameter::colorReflectanceParameter(color value) +{ +  _value = value; +  _texture = nullptr; +} + + +colorReflectanceParameter::colorReflectanceParameter(const std::shared_ptr<const texture_base>& texture) +{ +  _value = color(0.0f); +  _texture = texture; +} + + +colorReflectanceParameter::colorReflectanceParameter(const colorReflectanceParameter& src) +{ +  _value = src._value; +  _texture = src._texture; +} + + +////////////// +// Operator // +////////////// +colorReflectanceParameter& colorReflectanceParameter::operator=(const colorReflectanceParameter& src) +{ +  _assign(src); +  return *this; +} + + +color colorReflectanceParameter::operator()(const vec2d& textureCoord) const +{ +  if(_texture) return (*_texture)(textureCoord); +  else return _value; +} + + +///////////////////// +// Private Methods // +///////////////////// +void colorReflectanceParameter::_assign(const colorReflectanceParameter& src) +{ +  _value = src._value; +  _texture = src._texture; +} + + +void colorReflectanceParameter::_swap(colorReflectanceParameter& swp) +{ +  std::swap(_value, swp._value); +  std::swap(_texture, swp._texture); +} + + diff --git a/hw4/src/reflectanceShader_base.cpp b/hw4/src/reflectanceShader_base.cpp new file mode 100644 index 0000000..8ff81fc --- /dev/null +++ b/hw4/src/reflectanceShader_base.cpp @@ -0,0 +1,39 @@ +/******************************************************************/ +/* 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 "reflectanceShader_base.h" +#include "coordinateTransform.h" + +///////////// +// Methods // +///////////// +color reflectanceShader_base::shade(const intersectionPoint& ip, const vec3d& light_dir) const +{ +  // create shading frame (note: in and out point away!) +  coordinateTransformation ct = ip.shadingFrame(); +  vec3d local_out = ct.inverseTransformDirection(-ip.direction()); +  vec3d local_in = ct.inverseTransformDirection(light_dir); + +  // get brdf +  std::unique_ptr<const brdf_base> brdf = make_brdf(ip.textureCoordinate()); + +  // eval +  return brdf->shade(local_in, local_out); +} + + +shaderProperties reflectanceShader_base::properties(const intersectionPoint& ip) const +{ +  // get brdf +  std::unique_ptr<const brdf_base> brdf = make_brdf(ip.textureCoordinate()); + +  // get properties +  return shaderProperties(brdf->isDiffuse(), brdf->isSpecular()); +} + + diff --git a/hw4/src/scene.cpp b/hw4/src/scene.cpp new file mode 100644 index 0000000..4491c03 --- /dev/null +++ b/hw4/src/scene.cpp @@ -0,0 +1,42 @@ +/******************************************************************/ +/* 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 <cassert> +#include "scene.h" + +//////////////// +// Inspectors // +//////////////// +const camera& scene::getCamera(void) const +{  +  return _camera;  +} + + +const lightsource_base& scene::getLightsource(size_t idx) const +{ +  assert(idx < _lightsources.size()); +  return *(_lightsources[idx]); +} + + +size_t scene::numberOfLightsources(void) const +{ +  return _lightsources.size(); +} + + +///////////// +// Methods // +///////////// +intersectionPoint scene::intersect(const ray& r) const +{ +  return _sceneGraphRoot->intersect(r); +} + + diff --git a/hw4/src/sceneGraphNode.cpp b/hw4/src/sceneGraphNode.cpp new file mode 100644 index 0000000..1a0f600 --- /dev/null +++ b/hw4/src/sceneGraphNode.cpp @@ -0,0 +1,39 @@ +/******************************************************************/ +/* 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 <algorithm> +#include "sceneGraphNode.h" + +////////////////// +// Constructors // +////////////////// +sceneGraphNode::sceneGraphNode(void) +  : boundedCompound() +{ +  // Do nothing +} + +sceneGraphNode::sceneGraphNode(const std::vector<std::shared_ptr<const boundedPrimitive>>& nodes, const transformation3d& transform, const std::shared_ptr<const shader_base>& shader) +  : boundedCompound(transform, shader) +{ +  // copy nodes +  _nodes = nodes; + +  // init bounding box +  initializeBoundingBox(); +} + + +///////////// +// Methods // +///////////// +const std::vector<std::shared_ptr<const boundedPrimitive>>& sceneGraphNode::compounds(void) const +{ +  return _nodes; +} + diff --git a/hw4/src/sceneIO.cpp b/hw4/src/sceneIO.cpp new file mode 100644 index 0000000..bffff8e --- /dev/null +++ b/hw4/src/sceneIO.cpp @@ -0,0 +1,87 @@ +/******************************************************************/ +/* 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 "util.h" +#include "scene.h" +#include "sceneGraphNode.h" +#include "linear_intersector.h" + +#include "sceneIO_xml.h" +#include "sceneIO_core.h" +#include "sceneIO_cache.h" +#include "sceneIO_basis.h" +#include "sceneIO_light.h" +#include "sceneIO_geometry.h" +#include "sceneIO_material.h" + + +void importScene(const std::string& filename, scene& s) +{ +  // get root +  std::string rootDir = getDirectory(filename); + +  // open xml +  XMLNode sceneNode(filename); + +  // check if valid scene node +  if(!sceneNode.isValid()) +    errorMessage("Failed to find 'scene' in %s.", filename.c_str()); + +  // create sceneGraphNode, texture, and shader cache +  nodeCache<boundedPrimitive> shape_cache; +  nodeCache<shader_base> shader_cache; +  nodeCache<texture_base> texture_cache; + +  // init scene data +  bool autoTuneCamera = false; +  std::unique_ptr<const intersector_factory_base> intersector(new linear_intersector_factory()); +  +  // process sceneNode +  for(XMLNode node = sceneNode.firstChild(); node.isValid(); node++) +  { +    // check for general node-types +    if(importMaterial(node, shader_cache, texture_cache, rootDir)) continue; +    if(importGeometry(node, shape_cache, shader_cache, texture_cache, rootDir)) continue; + +    // check for scene-root specific nodes +    std::string nodeName = node.name(); +    if(nodeName == "camera") autoTuneCamera = importCamera(node, s._camera); +    else if(nodeName == "light") s._lightsources.push_back( importLight(node, shape_cache, shader_cache, texture_cache, rootDir) ); +    else if(nodeName == "intersector") importIntersector(node, intersector); +    else errorMessage("Unknown node in scene xml: '%s'", nodeName.c_str()); +  } + +  // initialize all node's intersectors +  auto nodeList = shape_cache.allNodes(); +  for(auto itr=nodeList.begin(); itr != nodeList.end(); itr++) +    static_cast<boundedCompound*>(itr->get())->initialize(*intersector); +  nodeList.clear(); + +  // add all unreferences nodes to the _sceneGraphRoot. +  // Note: remove any node without a shader +  decltype(nodeList) unusedNodes; +  nodeList = shape_cache.unusedNodes(); +  while(!nodeList.empty()) +  { +    auto bck = nodeList.back(); +    if(bck->hasShader()) unusedNodes.push_back(bck); +    nodeList.pop_back(); +  } + +  // -> init + add +  std::unique_ptr<boundedCompound> root(new sceneGraphNode( *(reinterpret_cast< std::vector<std::shared_ptr<const boundedPrimitive>>* >(&unusedNodes)) )); +  root->initialize(*intersector); +  s._sceneGraphRoot = std::unique_ptr<const boundedCompound>(root.release()); + +  // Auto Tune Camera is requested +  if(autoTuneCamera)  +    s._camera.frameBoundingBox( s._sceneGraphRoot->boundingbox() ); + +  // Done. +} + diff --git a/hw4/src/sceneIO_basis.cpp b/hw4/src/sceneIO_basis.cpp new file mode 100644 index 0000000..dc16756 --- /dev/null +++ b/hw4/src/sceneIO_basis.cpp @@ -0,0 +1,53 @@ +/******************************************************************/ +/* 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 <cassert> + +#include "errorMessage.h" +#include "sceneIO_core.h" +#include "sceneIO_basis.h" +#include "sceneIO_cache.h" + +#include "linear_intersector.h" +#include "bvh_intersector.h" + +bool importCamera(const XMLNode& node, camera& cam) +{ +  // sanity check +  assert(node.name() == "camera"); + +  // import camera +  cam = camera( getVec3d(node, "eye", vec3d(0.0f, 0.0f, 0.0f)), +		getVec3d(node, "view", vec3d(0.0f, 0.0f, -1.0f)), +		getVec3d(node, "up", vec3d(0.0f, 1.0f, 0.0f)), +		getFloat(node, "fov", 60), +		getInteger(node, "width", 256), +		getInteger(node, "height", 256) ); + +  // Done. +  return( getString(node, "auto", "false") == "true"); +} + + +void importIntersector(const XMLNode& node, std::unique_ptr<const intersector_factory_base>& intersector) +{ +  // sanity check +  assert(node.name() == "intersector"); + +  // get intersector type +  std::string type = getString(node, "type", "linear"); + +  // create intersector +  if(type == "linear") intersector = std::unique_ptr<const intersector_factory_base>( new linear_intersector_factory()); +  else if(type == "bvh") intersector = std::unique_ptr<const intersector_factory_base>( new bvh_intersector_factory()); +  else errorMessage("Unknown intersector type (%s)", type.c_str()); + +  // Done.   +} + + diff --git a/hw4/src/sceneIO_core.cpp b/hw4/src/sceneIO_core.cpp new file mode 100644 index 0000000..123287e --- /dev/null +++ b/hw4/src/sceneIO_core.cpp @@ -0,0 +1,99 @@ +/******************************************************************/ +/* 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 <string> +#include <cstdlib> + +#include "sceneIO_core.h" +#include "errorMessage.h" + +color getColor(const XMLNode& node, const std::string& name, const color& default_value) +{ +  std::string value = node.attribute(name); + +  // if defined, process.. +  if(value != "")  +  { +    color result; +    int s = sscanf(value.c_str(), "%f %f %f", &result.r, &result.g, &result.b); + +    // check if only 1 value is given or 3. +    if(s == 1) return color(result.r, result.r, result.r); +    else if(s == 3) return result; +    else errorMessage("Failed to parse color value (%s)", value.c_str()); +  } + +  // else return default +  return default_value; +} + + +vec3d getVec3d(const XMLNode& node, const std::string& name, const vec3d& default_value) +{ +  std::string value = node.attribute(name); + +  // if defined, process.. +  if(value != "")  +  { +    vec3d result; +    int s = sscanf(value.c_str(), "%f %f %f", &result.x, &result.y, &result.z); + +    // check if only 1 value is given or 3. +    if(s == 1) return vec3d(result.x, result.x, result.x); +    else if(s == 3) return result; +    else errorMessage("Failed to parse vec3d value (%s)", value.c_str()); +  } + +  // else return default +  return default_value; +} + + +vec2d getVec2d(const XMLNode& node, const std::string& name, const vec2d& default_value) +{ +  std::string value = node.attribute(name); + +  // if defined, process.. +  if(value != "")  +  { +    vec2d result; +    int s = sscanf(value.c_str(), "%f %f", &result.x, &result.y); + +    // check if only 1 value is given or 2. +    if(s == 1) return vec2d(result.x, result.x); +    else if(s == 2) return result; +    else errorMessage("Failed to parse vec2d value (%s)", value.c_str()); +  } + +  // else returnd default +  return default_value; +} + + +std::string getString(const XMLNode& node, const std::string& name, const std::string& default_value) +{ +  std::string value = node.attribute(name); +  if(value != "") return std::string(value); +  else return default_value; +} + + +float getFloat(const XMLNode& node, const std::string& name, float default_value) +{ +  std::string value = node.attribute(name); +if(value != "") return atof(value.c_str()); +  else return default_value; +} + + +unsigned int getInteger(const XMLNode& node, const std::string& name, unsigned int default_value) +{ +  std::string value = node.attribute(name); +if(value != "") return atoi(value.c_str()); +  else return default_value; +} diff --git a/hw4/src/sceneIO_geometry.cpp b/hw4/src/sceneIO_geometry.cpp new file mode 100644 index 0000000..8a5c163 --- /dev/null +++ b/hw4/src/sceneIO_geometry.cpp @@ -0,0 +1,153 @@ +/******************************************************************/ +/* 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 "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 == "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)); +} + +///////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////// +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 +  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; +} diff --git a/hw4/src/sceneIO_light.cpp b/hw4/src/sceneIO_light.cpp new file mode 100644 index 0000000..bb9e399 --- /dev/null +++ b/hw4/src/sceneIO_light.cpp @@ -0,0 +1,51 @@ +/******************************************************************/ +/* 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 <cassert> + +#include "errorMessage.h" +#include "random_number.h" + +#include "sceneIO_core.h" +#include "sceneIO_basis.h" +#include "sceneIO_light.h" +#include "sceneIO_material.h" +#include "sceneIO_geometry.h" + +#include "directionalLightsource.h" + +static std::shared_ptr<const lightsource_base> importDirectionalLight(const XMLNode& node) +{ +  // get properties +  vec3d direction = getVec3d(node, "direction", vec3d(0.0f, 0.0f, 1.0f)); +  color power = getColor(node, "power", color(1.0f, 1.0f, 1.0f)); +                                 +  // Done. +  return std::shared_ptr<const lightsource_base>( new directionalLightsource( direction, power)); +} + + +std::shared_ptr<const lightsource_base> importLight(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() == "light"); + +  // get light source type +  std::string type = getString(node, "type", "directional"); +  +  // create light source +  std::shared_ptr<const lightsource_base> ls; +  if(type == "directional") ls = importDirectionalLight(node); +  else errorMessage("Unknown light source type (%s)", type.c_str()); + +  // Done. +  return ls; +} + + + diff --git a/hw4/src/sceneIO_material.cpp b/hw4/src/sceneIO_material.cpp new file mode 100644 index 0000000..018e913 --- /dev/null +++ b/hw4/src/sceneIO_material.cpp @@ -0,0 +1,147 @@ +/******************************************************************/ +/* 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 <cassert> + +#include "sceneIO_xml.h" +#include "sceneIO_core.h" +#include "sceneIO_material.h" + +#include "compoundShader.h" +#include "reflectanceParameter.h" +#include "phongReflectanceShader.h" +#include "diffuseReflectanceShader.h" + +////////////////////////////////////////////////////////////// +static colorReflectanceParameter importColorReflectanceParameter(const XMLNode& node, nodeCache<texture_base>& texture_cache, const std::string& rootDir) +{ +  // get parameter +  color value = getColor(node, "value", color(0.0f, 0.0f, 0.0f)); +   +  // create & return parameter, give preference to textured parameters +  return colorReflectanceParameter(value); + +  // Done. +} + +////////////////////////////////////////////////////////////// +static scalarReflectanceParameter importScalarReflectanceParameter(const XMLNode& node, nodeCache<texture_base>& texture_cache, const std::string& rootDir) +{ +  // get parameters +  float value = getFloat(node, "value", 0.0f); +  unsigned int channel = getInteger(node, "channel", 0); + +  // create & return parameter, give preference to textured parameters +  return scalarReflectanceParameter(value); + +  // Done. +} + +////////////////////////////////////////////////////////////// +static std::shared_ptr<shader_base> importDiffuse(const XMLNode& node, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir) +{ +  // sanity check +  assert(node.name() == "diffuse"); + +  // parameter storage +  colorReflectanceParameter albedo; + +  // check child nodes +  for(XMLNode child=node.firstChild(); child.isValid(); child++) +  { +    std::string name = child.name(); +    if(name == "albedo") albedo = importColorReflectanceParameter(child, texture_cache, rootDir); +    else errorMessage("Unknown parameter in diffuse-node (%s).", name.c_str()); +  } + +  // Done. +  return std::shared_ptr<shader_base>(new diffuseReflectanceShader(albedo)); +} + +////////////////////////////////////////////////////////////// +static std::shared_ptr<shader_base> importPhong(const XMLNode& node, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir) +{ +  // sanity check +  assert(node.name() == "phong"); + +  // parameter storage +  colorReflectanceParameter albedo; +  scalarReflectanceParameter sharpness; + +  // check child nodes +  for(XMLNode child=node.firstChild(); child.isValid(); child++) +  { +    std::string name = child.name(); +    if(name == "albedo") albedo = importColorReflectanceParameter(child, texture_cache, rootDir); +    else if(name == "sharpness") sharpness = importScalarReflectanceParameter(child, texture_cache, rootDir); +    else errorMessage("Unknown parameter in phong-node (%s).", name.c_str()); +  } + +  // Done. +  return std::shared_ptr<shader_base>(new phongReflectanceShader(albedo, sharpness)); +} + +////////////////////////////////////////////////////////////// +static std::shared_ptr<shader_base> importCompoundMaterial(const XMLNode& node, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir) +{ +  // sanity check +  assert(node.isValid() && node.name() == "material"); + +  // storage for child material nodes +  std::vector<std::shared_ptr<const shader_base>> shader_list; + +  // for each child +  for(XMLNode child = node.firstChild(); child.isValid(); child++) +  { +    // try to import material +    std::shared_ptr<shader_base> shader = importMaterial(child, shader_cache, texture_cache, rootDir); + +    // add to list if successful. +    if(shader) shader_list.push_back(shader); +    else errorMessage("Unknown material-node (%s).", child.name().c_str()); +  } + +  // Done. +  return std::shared_ptr<shader_base>(new compoundShader(shader_list)); +} + + +////////////////////////////////////////////////////////////// +std::shared_ptr<shader_base> importMaterial(const XMLNode& node, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir) +{ +  std::string name = node.name(); + +  // check material type +  if(name != "material" && +     name != "diffuse" && +     name != "phong") return std::shared_ptr<shader_base>(nullptr); + +  // check if reference +  std::string ref = getString(node, "ref"); +  std::shared_ptr<shader_base> shader = shader_cache.get(ref); +  if(shader) return shader; +  else if(ref != "") errorMessage("Unknown %s name (%s).", name.c_str(), ref.c_str()); +  +  // get id +  std::string id = getString(node, "id"); +  if(shader_cache.get(id)) +    errorMessage("%s-id is not unique (%s).", name.c_str(), id.c_str()); + +  // process primitives based on type +  if(name == "material") shader = importCompoundMaterial(node, shader_cache, texture_cache, rootDir); +  else if(name == "diffuse") shader = importDiffuse(node, shader_cache, texture_cache, rootDir); +  else if(name == "phong") shader = importPhong(node, shader_cache, texture_cache, rootDir); +  else errorMessage("Unknown material-node (%s).", name.c_str()); + +  // update cache +  if(id != "") shader_cache.add(id, shader); + +  // Done. +  return shader; +} + diff --git a/hw4/src/sceneIO_xml.cpp b/hw4/src/sceneIO_xml.cpp new file mode 100644 index 0000000..47aa94e --- /dev/null +++ b/hw4/src/sceneIO_xml.cpp @@ -0,0 +1,112 @@ +/******************************************************************/ +/* 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 <cassert> +#include "sceneIO_xml.h" +#include "errorMessage.h" + +///////////////// +// Constructor // +///////////////// +XMLNode::XMLNode(const std::string& filename) +{ +  // open xml +  _xml = std::make_shared<tinyxml2::XMLDocument>(); +  _xml->LoadFile(filename.c_str()); + +  // check if open successful +  if(_xml->ErrorID() != tinyxml2::XML_SUCCESS) +    errorMessage("Failed to open scene file: '%s'.", filename.c_str()); + +  // set first node. +  _element = _xml->FirstChildElement("scene"); +} + + +XMLNode::XMLNode(const XMLNode& node) +{ +  _element = node._element; +  _xml = node._xml; +} + +XMLNode::XMLNode(const tinyxml2::XMLElement* element, const std::shared_ptr<tinyxml2::XMLDocument>& xml) +{ +  _element = element; +  _xml = xml; +} + + +///////////// +// Methods // +///////////// +XMLNode XMLNode::firstChild(void) const +{ +  assert(isValid()); +  return XMLNode(_element->FirstChildElement(), _xml); +} +  + +XMLNode XMLNode::findChild(const std::string& name) const +{ +  assert(isValid()); +  return XMLNode(_element->FirstChildElement(name.c_str()), _xml); +} +  +  +bool XMLNode::isValid(void) const +{ +  return (_element != NULL); +} + + +std::string XMLNode::name(void) const +{ +  assert(isValid()); +  return std::string(_element->Name()); +} + + +std::string XMLNode::attribute(const std::string& attribute) const +{ +  assert(isValid()); + +  const char* str = _element->Attribute(attribute.c_str()); +  if(str == NULL) return std::string(""); + +  // Done. +  return std::string(str); +} + + +/////////////// +// Operators // +/////////////// +XMLNode& XMLNode::operator=(const XMLNode& src) +{ +  _element = src._element; +  _xml = src._xml; +  return *this; +} + + +XMLNode& XMLNode::operator++(int) +{ +  assert(_element != NULL); +  _element = _element->NextSiblingElement(); +  return *this; +} + + +XMLNode& XMLNode::operator++(void) +{ +  assert(_element != NULL); +  _element = _element->NextSiblingElement(); +  return *this; +} + + diff --git a/hw4/src/shaderProperties.cpp b/hw4/src/shaderProperties.cpp new file mode 100644 index 0000000..235e73d --- /dev/null +++ b/hw4/src/shaderProperties.cpp @@ -0,0 +1,62 @@ +/******************************************************************/ +/* 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 "shaderProperties.h" + + +////////////// +// Operator // +////////////// +bool shaderProperties::operator==(const shaderProperties& sp) const +{ +  return (diffuse == sp.diffuse) && (specular == sp.specular); +} + + +bool shaderProperties::operator!=(const shaderProperties& sp) const +{ +  return !(*this == sp); +} + + +shaderProperties& shaderProperties::operator&=(const shaderProperties& sp) +{ +  diffuse &= sp.diffuse; +  specular &= sp.specular; +  return *this; +} + + +shaderProperties& shaderProperties::operator|=(const shaderProperties& sp) +{ +  diffuse |= sp.diffuse; +  specular |= sp.specular; +  return *this; +} + + +shaderProperties& shaderProperties::operator~(void) +{ +  diffuse = ~diffuse; +  specular = ~specular; +  return *this; +} + + +shaderProperties shaderProperties::operator&(const shaderProperties& sp) const +{ +  return shaderProperties(diffuse & sp.diffuse, +			  specular & sp.specular); +} + + +shaderProperties shaderProperties::operator|(const shaderProperties& sp) const +{ +  return shaderProperties(diffuse | sp.diffuse, +			  specular | sp.specular); +} diff --git a/hw4/src/tinyxml2.cpp b/hw4/src/tinyxml2.cpp new file mode 100644 index 0000000..0c903fe --- /dev/null +++ b/hw4/src/tinyxml2.cpp @@ -0,0 +1,2794 @@ +/*
 +Original code by Lee Thomason (www.grinninglizard.com)
 +
 +This software is provided 'as-is', without any express or implied
 +warranty. In no event will the authors be held liable for any
 +damages arising from the use of this software.
 +
 +Permission is granted to anyone to use this software for any
 +purpose, including commercial applications, and to alter it and
 +redistribute it freely, subject to the following restrictions:
 +
 +1. The origin of this software must not be misrepresented; you must
 +not claim that you wrote the original software. If you use this
 +software in a product, an acknowledgment in the product documentation
 +would be appreciated but is not required.
 +
 +2. Altered source versions must be plainly marked as such, and
 +must not be misrepresented as being the original software.
 +
 +3. This notice may not be removed or altered from any source
 +distribution.
 +*/
 +
 +#include "tinyxml2.h"
 +
 +#include <new>		// yes, this one new style header, is in the Android SDK.
 +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
 +#   include <stddef.h>
 +#   include <stdarg.h>
 +#else
 +#   include <cstddef>
 +#   include <cstdarg>
 +#endif
 +
 +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
 +	// Microsoft Visual Studio, version 2005 and higher. Not WinCE.
 +	/*int _snprintf_s(
 +	   char *buffer,
 +	   size_t sizeOfBuffer,
 +	   size_t count,
 +	   const char *format [,
 +		  argument] ...
 +	);*/
 +	static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
 +	{
 +		va_list va;
 +		va_start( va, format );
 +		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
 +		va_end( va );
 +		return result;
 +	}
 +
 +	static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
 +	{
 +		int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
 +		return result;
 +	}
 +
 +	#define TIXML_VSCPRINTF	_vscprintf
 +	#define TIXML_SSCANF	sscanf_s
 +#elif defined _MSC_VER
 +	// Microsoft Visual Studio 2003 and earlier or WinCE
 +	#define TIXML_SNPRINTF	_snprintf
 +	#define TIXML_VSNPRINTF _vsnprintf
 +	#define TIXML_SSCANF	sscanf
 +	#if (_MSC_VER < 1400 ) && (!defined WINCE)
 +		// Microsoft Visual Studio 2003 and not WinCE.
 +		#define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
 +	#else
 +		// Microsoft Visual Studio 2003 and earlier or WinCE.
 +		static inline int TIXML_VSCPRINTF( const char* format, va_list va )
 +		{
 +			int len = 512;
 +			for (;;) {
 +				len = len*2;
 +				char* str = new char[len]();
 +				const int required = _vsnprintf(str, len, format, va);
 +				delete[] str;
 +				if ( required != -1 ) {
 +					TIXMLASSERT( required >= 0 );
 +					len = required;
 +					break;
 +				}
 +			}
 +			TIXMLASSERT( len >= 0 );
 +			return len;
 +		}
 +	#endif
 +#else
 +	// GCC version 3 and higher
 +	//#warning( "Using sn* functions." )
 +	#define TIXML_SNPRINTF	snprintf
 +	#define TIXML_VSNPRINTF	vsnprintf
 +	static inline int TIXML_VSCPRINTF( const char* format, va_list va )
 +	{
 +		int len = vsnprintf( 0, 0, format, va );
 +		TIXMLASSERT( len >= 0 );
 +		return len;
 +	}
 +	#define TIXML_SSCANF   sscanf
 +#endif
 +
 +
 +static const char LINE_FEED				= (char)0x0a;			// all line endings are normalized to LF
 +static const char LF = LINE_FEED;
 +static const char CARRIAGE_RETURN		= (char)0x0d;			// CR gets filtered out
 +static const char CR = CARRIAGE_RETURN;
 +static const char SINGLE_QUOTE			= '\'';
 +static const char DOUBLE_QUOTE			= '\"';
 +
 +// Bunch of unicode info at:
 +//		http://www.unicode.org/faq/utf_bom.html
 +//	ef bb bf (Microsoft "lead bytes") - designates UTF-8
 +
 +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
 +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
 +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
 +
 +namespace tinyxml2
 +{
 +
 +struct Entity {
 +    const char* pattern;
 +    int length;
 +    char value;
 +};
 +
 +static const int NUM_ENTITIES = 5;
 +static const Entity entities[NUM_ENTITIES] = {
 +    { "quot", 4,	DOUBLE_QUOTE },
 +    { "amp", 3,		'&'  },
 +    { "apos", 4,	SINGLE_QUOTE },
 +    { "lt",	2, 		'<'	 },
 +    { "gt",	2,		'>'	 }
 +};
 +
 +
 +StrPair::~StrPair()
 +{
 +    Reset();
 +}
 +
 +
 +void StrPair::TransferTo( StrPair* other )
 +{
 +    if ( this == other ) {
 +        return;
 +    }
 +    // This in effect implements the assignment operator by "moving"
 +    // ownership (as in auto_ptr).
 +
 +    TIXMLASSERT( other != 0 );
 +    TIXMLASSERT( other->_flags == 0 );
 +    TIXMLASSERT( other->_start == 0 );
 +    TIXMLASSERT( other->_end == 0 );
 +
 +    other->Reset();
 +
 +    other->_flags = _flags;
 +    other->_start = _start;
 +    other->_end = _end;
 +
 +    _flags = 0;
 +    _start = 0;
 +    _end = 0;
 +}
 +
 +
 +void StrPair::Reset()
 +{
 +    if ( _flags & NEEDS_DELETE ) {
 +        delete [] _start;
 +    }
 +    _flags = 0;
 +    _start = 0;
 +    _end = 0;
 +}
 +
 +
 +void StrPair::SetStr( const char* str, int flags )
 +{
 +    TIXMLASSERT( str );
 +    Reset();
 +    size_t len = strlen( str );
 +    TIXMLASSERT( _start == 0 );
 +    _start = new char[ len+1 ];
 +    memcpy( _start, str, len+1 );
 +    _end = _start + len;
 +    _flags = flags | NEEDS_DELETE;
 +}
 +
 +
 +char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr )
 +{
 +    TIXMLASSERT( p );
 +    TIXMLASSERT( endTag && *endTag );
 +	TIXMLASSERT(curLineNumPtr);
 +
 +    char* start = p;
 +    char  endChar = *endTag;
 +    size_t length = strlen( endTag );
 +
 +    // Inner loop of text parsing.
 +    while ( *p ) {
 +        if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
 +            Set( start, p, strFlags );
 +            return p + length;
 +        } else if (*p == '\n') {
 +            ++(*curLineNumPtr);
 +        }
 +        ++p;
 +        TIXMLASSERT( p );
 +    }
 +    return 0;
 +}
 +
 +
 +char* StrPair::ParseName( char* p )
 +{
 +    if ( !p || !(*p) ) {
 +        return 0;
 +    }
 +    if ( !XMLUtil::IsNameStartChar( *p ) ) {
 +        return 0;
 +    }
 +
 +    char* const start = p;
 +    ++p;
 +    while ( *p && XMLUtil::IsNameChar( *p ) ) {
 +        ++p;
 +    }
 +
 +    Set( start, p, 0 );
 +    return p;
 +}
 +
 +
 +void StrPair::CollapseWhitespace()
 +{
 +    // Adjusting _start would cause undefined behavior on delete[]
 +    TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
 +    // Trim leading space.
 +    _start = XMLUtil::SkipWhiteSpace( _start, 0 );
 +
 +    if ( *_start ) {
 +        const char* p = _start;	// the read pointer
 +        char* q = _start;	// the write pointer
 +
 +        while( *p ) {
 +            if ( XMLUtil::IsWhiteSpace( *p )) {
 +                p = XMLUtil::SkipWhiteSpace( p, 0 );
 +                if ( *p == 0 ) {
 +                    break;    // don't write to q; this trims the trailing space.
 +                }
 +                *q = ' ';
 +                ++q;
 +            }
 +            *q = *p;
 +            ++q;
 +            ++p;
 +        }
 +        *q = 0;
 +    }
 +}
 +
 +
 +const char* StrPair::GetStr()
 +{
 +    TIXMLASSERT( _start );
 +    TIXMLASSERT( _end );
 +    if ( _flags & NEEDS_FLUSH ) {
 +        *_end = 0;
 +        _flags ^= NEEDS_FLUSH;
 +
 +        if ( _flags ) {
 +            const char* p = _start;	// the read pointer
 +            char* q = _start;	// the write pointer
 +
 +            while( p < _end ) {
 +                if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
 +                    // CR-LF pair becomes LF
 +                    // CR alone becomes LF
 +                    // LF-CR becomes LF
 +                    if ( *(p+1) == LF ) {
 +                        p += 2;
 +                    }
 +                    else {
 +                        ++p;
 +                    }
 +                    *q = LF;
 +                    ++q;
 +                }
 +                else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
 +                    if ( *(p+1) == CR ) {
 +                        p += 2;
 +                    }
 +                    else {
 +                        ++p;
 +                    }
 +                    *q = LF;
 +                    ++q;
 +                }
 +                else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
 +                    // Entities handled by tinyXML2:
 +                    // - special entities in the entity table [in/out]
 +                    // - numeric character reference [in]
 +                    //   中 or 中
 +
 +                    if ( *(p+1) == '#' ) {
 +                        const int buflen = 10;
 +                        char buf[buflen] = { 0 };
 +                        int len = 0;
 +                        char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
 +                        if ( adjusted == 0 ) {
 +                            *q = *p;
 +                            ++p;
 +                            ++q;
 +                        }
 +                        else {
 +                            TIXMLASSERT( 0 <= len && len <= buflen );
 +                            TIXMLASSERT( q + len <= adjusted );
 +                            p = adjusted;
 +                            memcpy( q, buf, len );
 +                            q += len;
 +                        }
 +                    }
 +                    else {
 +                        bool entityFound = false;
 +                        for( int i = 0; i < NUM_ENTITIES; ++i ) {
 +                            const Entity& entity = entities[i];
 +                            if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
 +                                    && *( p + entity.length + 1 ) == ';' ) {
 +                                // Found an entity - convert.
 +                                *q = entity.value;
 +                                ++q;
 +                                p += entity.length + 2;
 +                                entityFound = true;
 +                                break;
 +                            }
 +                        }
 +                        if ( !entityFound ) {
 +                            // fixme: treat as error?
 +                            ++p;
 +                            ++q;
 +                        }
 +                    }
 +                }
 +                else {
 +                    *q = *p;
 +                    ++p;
 +                    ++q;
 +                }
 +            }
 +            *q = 0;
 +        }
 +        // The loop below has plenty going on, and this
 +        // is a less useful mode. Break it out.
 +        if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
 +            CollapseWhitespace();
 +        }
 +        _flags = (_flags & NEEDS_DELETE);
 +    }
 +    TIXMLASSERT( _start );
 +    return _start;
 +}
 +
 +
 +
 +
 +// --------- XMLUtil ----------- //
 +
 +const char* XMLUtil::writeBoolTrue  = "true";
 +const char* XMLUtil::writeBoolFalse = "false";
 +
 +void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse)
 +{
 +	static const char* defTrue  = "true";
 +	static const char* defFalse = "false";
 +
 +	writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
 +	writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
 +}
 +
 +
 +const char* XMLUtil::ReadBOM( const char* p, bool* bom )
 +{
 +    TIXMLASSERT( p );
 +    TIXMLASSERT( bom );
 +    *bom = false;
 +    const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
 +    // Check for BOM:
 +    if (    *(pu+0) == TIXML_UTF_LEAD_0
 +            && *(pu+1) == TIXML_UTF_LEAD_1
 +            && *(pu+2) == TIXML_UTF_LEAD_2 ) {
 +        *bom = true;
 +        p += 3;
 +    }
 +    TIXMLASSERT( p );
 +    return p;
 +}
 +
 +
 +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
 +{
 +    const unsigned long BYTE_MASK = 0xBF;
 +    const unsigned long BYTE_MARK = 0x80;
 +    const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
 +
 +    if (input < 0x80) {
 +        *length = 1;
 +    }
 +    else if ( input < 0x800 ) {
 +        *length = 2;
 +    }
 +    else if ( input < 0x10000 ) {
 +        *length = 3;
 +    }
 +    else if ( input < 0x200000 ) {
 +        *length = 4;
 +    }
 +    else {
 +        *length = 0;    // This code won't convert this correctly anyway.
 +        return;
 +    }
 +
 +    output += *length;
 +
 +    // Scary scary fall throughs are annotated with carefully designed comments
 +    // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
 +    switch (*length) {
 +        case 4:
 +            --output;
 +            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
 +            input >>= 6;
 +            //fall through
 +        case 3:
 +            --output;
 +            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
 +            input >>= 6;
 +            //fall through
 +        case 2:
 +            --output;
 +            *output = (char)((input | BYTE_MARK) & BYTE_MASK);
 +            input >>= 6;
 +            //fall through
 +        case 1:
 +            --output;
 +            *output = (char)(input | FIRST_BYTE_MARK[*length]);
 +            break;
 +        default:
 +            TIXMLASSERT( false );
 +    }
 +}
 +
 +
 +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
 +{
 +    // Presume an entity, and pull it out.
 +    *length = 0;
 +
 +    if ( *(p+1) == '#' && *(p+2) ) {
 +        unsigned long ucs = 0;
 +        TIXMLASSERT( sizeof( ucs ) >= 4 );
 +        ptrdiff_t delta = 0;
 +        unsigned mult = 1;
 +        static const char SEMICOLON = ';';
 +
 +        if ( *(p+2) == 'x' ) {
 +            // Hexadecimal.
 +            const char* q = p+3;
 +            if ( !(*q) ) {
 +                return 0;
 +            }
 +
 +            q = strchr( q, SEMICOLON );
 +
 +            if ( !q ) {
 +                return 0;
 +            }
 +            TIXMLASSERT( *q == SEMICOLON );
 +
 +            delta = q-p;
 +            --q;
 +
 +            while ( *q != 'x' ) {
 +                unsigned int digit = 0;
 +
 +                if ( *q >= '0' && *q <= '9' ) {
 +                    digit = *q - '0';
 +                }
 +                else if ( *q >= 'a' && *q <= 'f' ) {
 +                    digit = *q - 'a' + 10;
 +                }
 +                else if ( *q >= 'A' && *q <= 'F' ) {
 +                    digit = *q - 'A' + 10;
 +                }
 +                else {
 +                    return 0;
 +                }
 +                TIXMLASSERT( digit < 16 );
 +                TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
 +                const unsigned int digitScaled = mult * digit;
 +                TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
 +                ucs += digitScaled;
 +                TIXMLASSERT( mult <= UINT_MAX / 16 );
 +                mult *= 16;
 +                --q;
 +            }
 +        }
 +        else {
 +            // Decimal.
 +            const char* q = p+2;
 +            if ( !(*q) ) {
 +                return 0;
 +            }
 +
 +            q = strchr( q, SEMICOLON );
 +
 +            if ( !q ) {
 +                return 0;
 +            }
 +            TIXMLASSERT( *q == SEMICOLON );
 +
 +            delta = q-p;
 +            --q;
 +
 +            while ( *q != '#' ) {
 +                if ( *q >= '0' && *q <= '9' ) {
 +                    const unsigned int digit = *q - '0';
 +                    TIXMLASSERT( digit < 10 );
 +                    TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
 +                    const unsigned int digitScaled = mult * digit;
 +                    TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
 +                    ucs += digitScaled;
 +                }
 +                else {
 +                    return 0;
 +                }
 +                TIXMLASSERT( mult <= UINT_MAX / 10 );
 +                mult *= 10;
 +                --q;
 +            }
 +        }
 +        // convert the UCS to UTF-8
 +        ConvertUTF32ToUTF8( ucs, value, length );
 +        return p + delta + 1;
 +    }
 +    return p+1;
 +}
 +
 +
 +void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
 +{
 +    TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
 +}
 +
 +
 +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
 +{
 +    TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
 +}
 +
 +
 +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
 +{
 +    TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse);
 +}
 +
 +/*
 +	ToStr() of a number is a very tricky topic.
 +	https://github.com/leethomason/tinyxml2/issues/106
 +*/
 +void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
 +{
 +    TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
 +}
 +
 +
 +void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
 +{
 +    TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
 +}
 +
 +
 +void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize)
 +{
 +	// horrible syntax trick to make the compiler happy about %lld
 +	TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v);
 +}
 +
 +
 +bool XMLUtil::ToInt( const char* str, int* value )
 +{
 +    if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
 +        return true;
 +    }
 +    return false;
 +}
 +
 +bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
 +{
 +    if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
 +        return true;
 +    }
 +    return false;
 +}
 +
 +bool XMLUtil::ToBool( const char* str, bool* value )
 +{
 +    int ival = 0;
 +    if ( ToInt( str, &ival )) {
 +        *value = (ival==0) ? false : true;
 +        return true;
 +    }
 +    if ( StringEqual( str, "true" ) ) {
 +        *value = true;
 +        return true;
 +    }
 +    else if ( StringEqual( str, "false" ) ) {
 +        *value = false;
 +        return true;
 +    }
 +    return false;
 +}
 +
 +
 +bool XMLUtil::ToFloat( const char* str, float* value )
 +{
 +    if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
 +        return true;
 +    }
 +    return false;
 +}
 +
 +
 +bool XMLUtil::ToDouble( const char* str, double* value )
 +{
 +    if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
 +        return true;
 +    }
 +    return false;
 +}
 +
 +
 +bool XMLUtil::ToInt64(const char* str, int64_t* value)
 +{
 +	long long v = 0;	// horrible syntax trick to make the compiler happy about %lld
 +	if (TIXML_SSCANF(str, "%lld", &v) == 1) {
 +		*value = (int64_t)v;
 +		return true;
 +	}
 +	return false;
 +}
 +
 +
 +char* XMLDocument::Identify( char* p, XMLNode** node )
 +{
 +    TIXMLASSERT( node );
 +    TIXMLASSERT( p );
 +    char* const start = p;
 +    int const startLine = _parseCurLineNum;
 +    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
 +    if( !*p ) {
 +        *node = 0;
 +        TIXMLASSERT( p );
 +        return p;
 +    }
 +
 +    // These strings define the matching patterns:
 +    static const char* xmlHeader		= { "<?" };
 +    static const char* commentHeader	= { "<!--" };
 +    static const char* cdataHeader		= { "<![CDATA[" };
 +    static const char* dtdHeader		= { "<!" };
 +    static const char* elementHeader	= { "<" };	// and a header for everything else; check last.
 +
 +    static const int xmlHeaderLen		= 2;
 +    static const int commentHeaderLen	= 4;
 +    static const int cdataHeaderLen		= 9;
 +    static const int dtdHeaderLen		= 2;
 +    static const int elementHeaderLen	= 1;
 +
 +    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );		// use same memory pool
 +    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );	// use same memory pool
 +    XMLNode* returnNode = 0;
 +    if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
 +        returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
 +        returnNode->_parseLineNum = _parseCurLineNum;
 +        p += xmlHeaderLen;
 +    }
 +    else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
 +        returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
 +        returnNode->_parseLineNum = _parseCurLineNum;
 +        p += commentHeaderLen;
 +    }
 +    else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
 +        XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
 +        returnNode = text;
 +        returnNode->_parseLineNum = _parseCurLineNum;
 +        p += cdataHeaderLen;
 +        text->SetCData( true );
 +    }
 +    else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
 +        returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
 +        returnNode->_parseLineNum = _parseCurLineNum;
 +        p += dtdHeaderLen;
 +    }
 +    else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
 +        returnNode =  CreateUnlinkedNode<XMLElement>( _elementPool );
 +        returnNode->_parseLineNum = _parseCurLineNum;
 +        p += elementHeaderLen;
 +    }
 +    else {
 +        returnNode = CreateUnlinkedNode<XMLText>( _textPool );
 +        returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character
 +        p = start;	// Back it up, all the text counts.
 +        _parseCurLineNum = startLine;
 +    }
 +
 +    TIXMLASSERT( returnNode );
 +    TIXMLASSERT( p );
 +    *node = returnNode;
 +    return p;
 +}
 +
 +
 +bool XMLDocument::Accept( XMLVisitor* visitor ) const
 +{
 +    TIXMLASSERT( visitor );
 +    if ( visitor->VisitEnter( *this ) ) {
 +        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
 +            if ( !node->Accept( visitor ) ) {
 +                break;
 +            }
 +        }
 +    }
 +    return visitor->VisitExit( *this );
 +}
 +
 +
 +// --------- XMLNode ----------- //
 +
 +XMLNode::XMLNode( XMLDocument* doc ) :
 +    _document( doc ),
 +    _parent( 0 ),
 +    _value(),
 +    _parseLineNum( 0 ),
 +    _firstChild( 0 ), _lastChild( 0 ),
 +    _prev( 0 ), _next( 0 ),
 +	_userData( 0 ),
 +    _memPool( 0 )
 +{
 +}
 +
 +
 +XMLNode::~XMLNode()
 +{
 +    DeleteChildren();
 +    if ( _parent ) {
 +        _parent->Unlink( this );
 +    }
 +}
 +
 +const char* XMLNode::Value() const 
 +{
 +    // Edge case: XMLDocuments don't have a Value. Return null.
 +    if ( this->ToDocument() )
 +        return 0;
 +    return _value.GetStr();
 +}
 +
 +void XMLNode::SetValue( const char* str, bool staticMem )
 +{
 +    if ( staticMem ) {
 +        _value.SetInternedStr( str );
 +    }
 +    else {
 +        _value.SetStr( str );
 +    }
 +}
 +
 +XMLNode* XMLNode::DeepClone(XMLDocument* target) const
 +{
 +	XMLNode* clone = this->ShallowClone(target);
 +	if (!clone) return 0;
 +
 +	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
 +		XMLNode* childClone = child->DeepClone(target);
 +		TIXMLASSERT(childClone);
 +		clone->InsertEndChild(childClone);
 +	}
 +	return clone;
 +}
 +
 +void XMLNode::DeleteChildren()
 +{
 +    while( _firstChild ) {
 +        TIXMLASSERT( _lastChild );
 +        DeleteChild( _firstChild );
 +    }
 +    _firstChild = _lastChild = 0;
 +}
 +
 +
 +void XMLNode::Unlink( XMLNode* child )
 +{
 +    TIXMLASSERT( child );
 +    TIXMLASSERT( child->_document == _document );
 +    TIXMLASSERT( child->_parent == this );
 +    if ( child == _firstChild ) {
 +        _firstChild = _firstChild->_next;
 +    }
 +    if ( child == _lastChild ) {
 +        _lastChild = _lastChild->_prev;
 +    }
 +
 +    if ( child->_prev ) {
 +        child->_prev->_next = child->_next;
 +    }
 +    if ( child->_next ) {
 +        child->_next->_prev = child->_prev;
 +    }
 +	child->_next = 0;
 +	child->_prev = 0;
 +	child->_parent = 0;
 +}
 +
 +
 +void XMLNode::DeleteChild( XMLNode* node )
 +{
 +    TIXMLASSERT( node );
 +    TIXMLASSERT( node->_document == _document );
 +    TIXMLASSERT( node->_parent == this );
 +    Unlink( node );
 +	TIXMLASSERT(node->_prev == 0);
 +	TIXMLASSERT(node->_next == 0);
 +	TIXMLASSERT(node->_parent == 0);
 +    DeleteNode( node );
 +}
 +
 +
 +XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
 +{
 +    TIXMLASSERT( addThis );
 +    if ( addThis->_document != _document ) {
 +        TIXMLASSERT( false );
 +        return 0;
 +    }
 +    InsertChildPreamble( addThis );
 +
 +    if ( _lastChild ) {
 +        TIXMLASSERT( _firstChild );
 +        TIXMLASSERT( _lastChild->_next == 0 );
 +        _lastChild->_next = addThis;
 +        addThis->_prev = _lastChild;
 +        _lastChild = addThis;
 +
 +        addThis->_next = 0;
 +    }
 +    else {
 +        TIXMLASSERT( _firstChild == 0 );
 +        _firstChild = _lastChild = addThis;
 +
 +        addThis->_prev = 0;
 +        addThis->_next = 0;
 +    }
 +    addThis->_parent = this;
 +    return addThis;
 +}
 +
 +
 +XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
 +{
 +    TIXMLASSERT( addThis );
 +    if ( addThis->_document != _document ) {
 +        TIXMLASSERT( false );
 +        return 0;
 +    }
 +    InsertChildPreamble( addThis );
 +
 +    if ( _firstChild ) {
 +        TIXMLASSERT( _lastChild );
 +        TIXMLASSERT( _firstChild->_prev == 0 );
 +
 +        _firstChild->_prev = addThis;
 +        addThis->_next = _firstChild;
 +        _firstChild = addThis;
 +
 +        addThis->_prev = 0;
 +    }
 +    else {
 +        TIXMLASSERT( _lastChild == 0 );
 +        _firstChild = _lastChild = addThis;
 +
 +        addThis->_prev = 0;
 +        addThis->_next = 0;
 +    }
 +    addThis->_parent = this;
 +    return addThis;
 +}
 +
 +
 +XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
 +{
 +    TIXMLASSERT( addThis );
 +    if ( addThis->_document != _document ) {
 +        TIXMLASSERT( false );
 +        return 0;
 +    }
 +
 +    TIXMLASSERT( afterThis );
 +
 +    if ( afterThis->_parent != this ) {
 +        TIXMLASSERT( false );
 +        return 0;
 +    }
 +    if ( afterThis == addThis ) {
 +        // Current state: BeforeThis -> AddThis -> OneAfterAddThis
 +        // Now AddThis must disappear from it's location and then
 +        // reappear between BeforeThis and OneAfterAddThis.
 +        // So just leave it where it is.
 +        return addThis;
 +    }
 +
 +    if ( afterThis->_next == 0 ) {
 +        // The last node or the only node.
 +        return InsertEndChild( addThis );
 +    }
 +    InsertChildPreamble( addThis );
 +    addThis->_prev = afterThis;
 +    addThis->_next = afterThis->_next;
 +    afterThis->_next->_prev = addThis;
 +    afterThis->_next = addThis;
 +    addThis->_parent = this;
 +    return addThis;
 +}
 +
 +
 +
 +
 +const XMLElement* XMLNode::FirstChildElement( const char* name ) const
 +{
 +    for( const XMLNode* node = _firstChild; node; node = node->_next ) {
 +        const XMLElement* element = node->ToElementWithName( name );
 +        if ( element ) {
 +            return element;
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +const XMLElement* XMLNode::LastChildElement( const char* name ) const
 +{
 +    for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
 +        const XMLElement* element = node->ToElementWithName( name );
 +        if ( element ) {
 +            return element;
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
 +{
 +    for( const XMLNode* node = _next; node; node = node->_next ) {
 +        const XMLElement* element = node->ToElementWithName( name );
 +        if ( element ) {
 +            return element;
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
 +{
 +    for( const XMLNode* node = _prev; node; node = node->_prev ) {
 +        const XMLElement* element = node->ToElementWithName( name );
 +        if ( element ) {
 +            return element;
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
 +{
 +    // This is a recursive method, but thinking about it "at the current level"
 +    // it is a pretty simple flat list:
 +    //		<foo/>
 +    //		<!-- comment -->
 +    //
 +    // With a special case:
 +    //		<foo>
 +    //		</foo>
 +    //		<!-- comment -->
 +    //
 +    // Where the closing element (/foo) *must* be the next thing after the opening
 +    // element, and the names must match. BUT the tricky bit is that the closing
 +    // element will be read by the child.
 +    //
 +    // 'endTag' is the end tag for this node, it is returned by a call to a child.
 +    // 'parentEnd' is the end tag for the parent, which is filled in and returned.
 +
 +    while( p && *p ) {
 +        XMLNode* node = 0;
 +
 +        p = _document->Identify( p, &node );
 +        TIXMLASSERT( p );
 +        if ( node == 0 ) {
 +            break;
 +        }
 +
 +        int initialLineNum = node->_parseLineNum;
 +
 +        StrPair endTag;
 +        p = node->ParseDeep( p, &endTag, curLineNumPtr );
 +        if ( !p ) {
 +            DeleteNode( node );
 +            if ( !_document->Error() ) {
 +                _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
 +            }
 +            break;
 +        }
 +
 +        XMLDeclaration* decl = node->ToDeclaration();
 +        if ( decl ) {
 +            // Declarations are only allowed at document level
 +            bool wellLocated = ( ToDocument() != 0 );
 +            if ( wellLocated ) {
 +                // Multiple declarations are allowed but all declarations
 +                // must occur before anything else
 +                for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) {
 +                    if ( !existingNode->ToDeclaration() ) {
 +                        wellLocated = false;
 +                        break;
 +                    }
 +                }
 +            }
 +            if ( !wellLocated ) {
 +                _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value());
 +                DeleteNode( node );
 +                break;
 +            }
 +        }
 +
 +        XMLElement* ele = node->ToElement();
 +        if ( ele ) {
 +            // We read the end tag. Return it to the parent.
 +            if ( ele->ClosingType() == XMLElement::CLOSING ) {
 +                if ( parentEndTag ) {
 +                    ele->_value.TransferTo( parentEndTag );
 +                }
 +                node->_memPool->SetTracked();   // created and then immediately deleted.
 +                DeleteNode( node );
 +                return p;
 +            }
 +
 +            // Handle an end tag returned to this level.
 +            // And handle a bunch of annoying errors.
 +            bool mismatch = false;
 +            if ( endTag.Empty() ) {
 +                if ( ele->ClosingType() == XMLElement::OPEN ) {
 +                    mismatch = true;
 +                }
 +            }
 +            else {
 +                if ( ele->ClosingType() != XMLElement::OPEN ) {
 +                    mismatch = true;
 +                }
 +                else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
 +                    mismatch = true;
 +                }
 +            }
 +            if ( mismatch ) {
 +                _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name());
 +                DeleteNode( node );
 +                break;
 +            }
 +        }
 +        InsertEndChild( node );
 +    }
 +    return 0;
 +}
 +
 +/*static*/ void XMLNode::DeleteNode( XMLNode* node )
 +{
 +    if ( node == 0 ) {
 +        return;
 +    }
 +	TIXMLASSERT(node->_document);
 +	if (!node->ToDocument()) {
 +		node->_document->MarkInUse(node);
 +	}
 +
 +    MemPool* pool = node->_memPool;
 +    node->~XMLNode();
 +    pool->Free( node );
 +}
 +
 +void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
 +{
 +    TIXMLASSERT( insertThis );
 +    TIXMLASSERT( insertThis->_document == _document );
 +
 +	if (insertThis->_parent) {
 +        insertThis->_parent->Unlink( insertThis );
 +	}
 +	else {
 +		insertThis->_document->MarkInUse(insertThis);
 +        insertThis->_memPool->SetTracked();
 +	}
 +}
 +
 +const XMLElement* XMLNode::ToElementWithName( const char* name ) const
 +{
 +    const XMLElement* element = this->ToElement();
 +    if ( element == 0 ) {
 +        return 0;
 +    }
 +    if ( name == 0 ) {
 +        return element;
 +    }
 +    if ( XMLUtil::StringEqual( element->Name(), name ) ) {
 +       return element;
 +    }
 +    return 0;
 +}
 +
 +// --------- XMLText ---------- //
 +char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
 +{
 +    if ( this->CData() ) {
 +        p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
 +        if ( !p ) {
 +            _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
 +        }
 +        return p;
 +    }
 +    else {
 +        int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
 +        if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
 +            flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
 +        }
 +
 +        p = _value.ParseText( p, "<", flags, curLineNumPtr );
 +        if ( p && *p ) {
 +            return p-1;
 +        }
 +        if ( !p ) {
 +            _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
 +{
 +    if ( !doc ) {
 +        doc = _document;
 +    }
 +    XMLText* text = doc->NewText( Value() );	// fixme: this will always allocate memory. Intern?
 +    text->SetCData( this->CData() );
 +    return text;
 +}
 +
 +
 +bool XMLText::ShallowEqual( const XMLNode* compare ) const
 +{
 +    TIXMLASSERT( compare );
 +    const XMLText* text = compare->ToText();
 +    return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
 +}
 +
 +
 +bool XMLText::Accept( XMLVisitor* visitor ) const
 +{
 +    TIXMLASSERT( visitor );
 +    return visitor->Visit( *this );
 +}
 +
 +
 +// --------- XMLComment ---------- //
 +
 +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
 +{
 +}
 +
 +
 +XMLComment::~XMLComment()
 +{
 +}
 +
 +
 +char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
 +{
 +    // Comment parses as text.
 +    p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr );
 +    if ( p == 0 ) {
 +        _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
 +    }
 +    return p;
 +}
 +
 +
 +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
 +{
 +    if ( !doc ) {
 +        doc = _document;
 +    }
 +    XMLComment* comment = doc->NewComment( Value() );	// fixme: this will always allocate memory. Intern?
 +    return comment;
 +}
 +
 +
 +bool XMLComment::ShallowEqual( const XMLNode* compare ) const
 +{
 +    TIXMLASSERT( compare );
 +    const XMLComment* comment = compare->ToComment();
 +    return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
 +}
 +
 +
 +bool XMLComment::Accept( XMLVisitor* visitor ) const
 +{
 +    TIXMLASSERT( visitor );
 +    return visitor->Visit( *this );
 +}
 +
 +
 +// --------- XMLDeclaration ---------- //
 +
 +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
 +{
 +}
 +
 +
 +XMLDeclaration::~XMLDeclaration()
 +{
 +    //printf( "~XMLDeclaration\n" );
 +}
 +
 +
 +char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
 +{
 +    // Declaration parses as text.
 +    p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
 +    if ( p == 0 ) {
 +        _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
 +    }
 +    return p;
 +}
 +
 +
 +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
 +{
 +    if ( !doc ) {
 +        doc = _document;
 +    }
 +    XMLDeclaration* dec = doc->NewDeclaration( Value() );	// fixme: this will always allocate memory. Intern?
 +    return dec;
 +}
 +
 +
 +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
 +{
 +    TIXMLASSERT( compare );
 +    const XMLDeclaration* declaration = compare->ToDeclaration();
 +    return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
 +}
 +
 +
 +
 +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
 +{
 +    TIXMLASSERT( visitor );
 +    return visitor->Visit( *this );
 +}
 +
 +// --------- XMLUnknown ---------- //
 +
 +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
 +{
 +}
 +
 +
 +XMLUnknown::~XMLUnknown()
 +{
 +}
 +
 +
 +char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr )
 +{
 +    // Unknown parses as text.
 +    p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
 +    if ( !p ) {
 +        _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
 +    }
 +    return p;
 +}
 +
 +
 +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
 +{
 +    if ( !doc ) {
 +        doc = _document;
 +    }
 +    XMLUnknown* text = doc->NewUnknown( Value() );	// fixme: this will always allocate memory. Intern?
 +    return text;
 +}
 +
 +
 +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
 +{
 +    TIXMLASSERT( compare );
 +    const XMLUnknown* unknown = compare->ToUnknown();
 +    return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
 +}
 +
 +
 +bool XMLUnknown::Accept( XMLVisitor* visitor ) const
 +{
 +    TIXMLASSERT( visitor );
 +    return visitor->Visit( *this );
 +}
 +
 +// --------- XMLAttribute ---------- //
 +
 +const char* XMLAttribute::Name() const 
 +{
 +    return _name.GetStr();
 +}
 +
 +const char* XMLAttribute::Value() const 
 +{
 +    return _value.GetStr();
 +}
 +
 +char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr )
 +{
 +    // Parse using the name rules: bug fix, was using ParseText before
 +    p = _name.ParseName( p );
 +    if ( !p || !*p ) {
 +        return 0;
 +    }
 +
 +    // Skip white space before =
 +    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 +    if ( *p != '=' ) {
 +        return 0;
 +    }
 +
 +    ++p;	// move up to opening quote
 +    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 +    if ( *p != '\"' && *p != '\'' ) {
 +        return 0;
 +    }
 +
 +    char endTag[2] = { *p, 0 };
 +    ++p;	// move past opening quote
 +
 +    p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
 +    return p;
 +}
 +
 +
 +void XMLAttribute::SetName( const char* n )
 +{
 +    _name.SetStr( n );
 +}
 +
 +
 +XMLError XMLAttribute::QueryIntValue( int* value ) const
 +{
 +    if ( XMLUtil::ToInt( Value(), value )) {
 +        return XML_SUCCESS;
 +    }
 +    return XML_WRONG_ATTRIBUTE_TYPE;
 +}
 +
 +
 +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
 +{
 +    if ( XMLUtil::ToUnsigned( Value(), value )) {
 +        return XML_SUCCESS;
 +    }
 +    return XML_WRONG_ATTRIBUTE_TYPE;
 +}
 +
 +
 +XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
 +{
 +	if (XMLUtil::ToInt64(Value(), value)) {
 +		return XML_SUCCESS;
 +	}
 +	return XML_WRONG_ATTRIBUTE_TYPE;
 +}
 +
 +
 +XMLError XMLAttribute::QueryBoolValue( bool* value ) const
 +{
 +    if ( XMLUtil::ToBool( Value(), value )) {
 +        return XML_SUCCESS;
 +    }
 +    return XML_WRONG_ATTRIBUTE_TYPE;
 +}
 +
 +
 +XMLError XMLAttribute::QueryFloatValue( float* value ) const
 +{
 +    if ( XMLUtil::ToFloat( Value(), value )) {
 +        return XML_SUCCESS;
 +    }
 +    return XML_WRONG_ATTRIBUTE_TYPE;
 +}
 +
 +
 +XMLError XMLAttribute::QueryDoubleValue( double* value ) const
 +{
 +    if ( XMLUtil::ToDouble( Value(), value )) {
 +        return XML_SUCCESS;
 +    }
 +    return XML_WRONG_ATTRIBUTE_TYPE;
 +}
 +
 +
 +void XMLAttribute::SetAttribute( const char* v )
 +{
 +    _value.SetStr( v );
 +}
 +
 +
 +void XMLAttribute::SetAttribute( int v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    _value.SetStr( buf );
 +}
 +
 +
 +void XMLAttribute::SetAttribute( unsigned v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    _value.SetStr( buf );
 +}
 +
 +
 +void XMLAttribute::SetAttribute(int64_t v)
 +{
 +	char buf[BUF_SIZE];
 +	XMLUtil::ToStr(v, buf, BUF_SIZE);
 +	_value.SetStr(buf);
 +}
 +
 +
 +
 +void XMLAttribute::SetAttribute( bool v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    _value.SetStr( buf );
 +}
 +
 +void XMLAttribute::SetAttribute( double v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    _value.SetStr( buf );
 +}
 +
 +void XMLAttribute::SetAttribute( float v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    _value.SetStr( buf );
 +}
 +
 +
 +// --------- XMLElement ---------- //
 +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
 +    _closingType( OPEN ),
 +    _rootAttribute( 0 )
 +{
 +}
 +
 +
 +XMLElement::~XMLElement()
 +{
 +    while( _rootAttribute ) {
 +        XMLAttribute* next = _rootAttribute->_next;
 +        DeleteAttribute( _rootAttribute );
 +        _rootAttribute = next;
 +    }
 +}
 +
 +
 +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
 +{
 +    for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
 +        if ( XMLUtil::StringEqual( a->Name(), name ) ) {
 +            return a;
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +const char* XMLElement::Attribute( const char* name, const char* value ) const
 +{
 +    const XMLAttribute* a = FindAttribute( name );
 +    if ( !a ) {
 +        return 0;
 +    }
 +    if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
 +        return a->Value();
 +    }
 +    return 0;
 +}
 +
 +int XMLElement::IntAttribute(const char* name, int defaultValue) const 
 +{
 +	int i = defaultValue;
 +	QueryIntAttribute(name, &i);
 +	return i;
 +}
 +
 +unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const 
 +{
 +	unsigned i = defaultValue;
 +	QueryUnsignedAttribute(name, &i);
 +	return i;
 +}
 +
 +int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const 
 +{
 +	int64_t i = defaultValue;
 +	QueryInt64Attribute(name, &i);
 +	return i;
 +}
 +
 +bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const 
 +{
 +	bool b = defaultValue;
 +	QueryBoolAttribute(name, &b);
 +	return b;
 +}
 +
 +double XMLElement::DoubleAttribute(const char* name, double defaultValue) const 
 +{
 +	double d = defaultValue;
 +	QueryDoubleAttribute(name, &d);
 +	return d;
 +}
 +
 +float XMLElement::FloatAttribute(const char* name, float defaultValue) const 
 +{
 +	float f = defaultValue;
 +	QueryFloatAttribute(name, &f);
 +	return f;
 +}
 +
 +const char* XMLElement::GetText() const
 +{
 +    if ( FirstChild() && FirstChild()->ToText() ) {
 +        return FirstChild()->Value();
 +    }
 +    return 0;
 +}
 +
 +
 +void	XMLElement::SetText( const char* inText )
 +{
 +	if ( FirstChild() && FirstChild()->ToText() )
 +		FirstChild()->SetValue( inText );
 +	else {
 +		XMLText*	theText = GetDocument()->NewText( inText );
 +		InsertFirstChild( theText );
 +	}
 +}
 +
 +
 +void XMLElement::SetText( int v ) 
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    SetText( buf );
 +}
 +
 +
 +void XMLElement::SetText( unsigned v ) 
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    SetText( buf );
 +}
 +
 +
 +void XMLElement::SetText(int64_t v)
 +{
 +	char buf[BUF_SIZE];
 +	XMLUtil::ToStr(v, buf, BUF_SIZE);
 +	SetText(buf);
 +}
 +
 +
 +void XMLElement::SetText( bool v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    SetText( buf );
 +}
 +
 +
 +void XMLElement::SetText( float v ) 
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    SetText( buf );
 +}
 +
 +
 +void XMLElement::SetText( double v ) 
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    SetText( buf );
 +}
 +
 +
 +XMLError XMLElement::QueryIntText( int* ival ) const
 +{
 +    if ( FirstChild() && FirstChild()->ToText() ) {
 +        const char* t = FirstChild()->Value();
 +        if ( XMLUtil::ToInt( t, ival ) ) {
 +            return XML_SUCCESS;
 +        }
 +        return XML_CAN_NOT_CONVERT_TEXT;
 +    }
 +    return XML_NO_TEXT_NODE;
 +}
 +
 +
 +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
 +{
 +    if ( FirstChild() && FirstChild()->ToText() ) {
 +        const char* t = FirstChild()->Value();
 +        if ( XMLUtil::ToUnsigned( t, uval ) ) {
 +            return XML_SUCCESS;
 +        }
 +        return XML_CAN_NOT_CONVERT_TEXT;
 +    }
 +    return XML_NO_TEXT_NODE;
 +}
 +
 +
 +XMLError XMLElement::QueryInt64Text(int64_t* ival) const
 +{
 +	if (FirstChild() && FirstChild()->ToText()) {
 +		const char* t = FirstChild()->Value();
 +		if (XMLUtil::ToInt64(t, ival)) {
 +			return XML_SUCCESS;
 +		}
 +		return XML_CAN_NOT_CONVERT_TEXT;
 +	}
 +	return XML_NO_TEXT_NODE;
 +}
 +
 +
 +XMLError XMLElement::QueryBoolText( bool* bval ) const
 +{
 +    if ( FirstChild() && FirstChild()->ToText() ) {
 +        const char* t = FirstChild()->Value();
 +        if ( XMLUtil::ToBool( t, bval ) ) {
 +            return XML_SUCCESS;
 +        }
 +        return XML_CAN_NOT_CONVERT_TEXT;
 +    }
 +    return XML_NO_TEXT_NODE;
 +}
 +
 +
 +XMLError XMLElement::QueryDoubleText( double* dval ) const
 +{
 +    if ( FirstChild() && FirstChild()->ToText() ) {
 +        const char* t = FirstChild()->Value();
 +        if ( XMLUtil::ToDouble( t, dval ) ) {
 +            return XML_SUCCESS;
 +        }
 +        return XML_CAN_NOT_CONVERT_TEXT;
 +    }
 +    return XML_NO_TEXT_NODE;
 +}
 +
 +
 +XMLError XMLElement::QueryFloatText( float* fval ) const
 +{
 +    if ( FirstChild() && FirstChild()->ToText() ) {
 +        const char* t = FirstChild()->Value();
 +        if ( XMLUtil::ToFloat( t, fval ) ) {
 +            return XML_SUCCESS;
 +        }
 +        return XML_CAN_NOT_CONVERT_TEXT;
 +    }
 +    return XML_NO_TEXT_NODE;
 +}
 +
 +int XMLElement::IntText(int defaultValue) const
 +{
 +	int i = defaultValue;
 +	QueryIntText(&i);
 +	return i;
 +}
 +
 +unsigned XMLElement::UnsignedText(unsigned defaultValue) const
 +{
 +	unsigned i = defaultValue;
 +	QueryUnsignedText(&i);
 +	return i;
 +}
 +
 +int64_t XMLElement::Int64Text(int64_t defaultValue) const
 +{
 +	int64_t i = defaultValue;
 +	QueryInt64Text(&i);
 +	return i;
 +}
 +
 +bool XMLElement::BoolText(bool defaultValue) const
 +{
 +	bool b = defaultValue;
 +	QueryBoolText(&b);
 +	return b;
 +}
 +
 +double XMLElement::DoubleText(double defaultValue) const
 +{
 +	double d = defaultValue;
 +	QueryDoubleText(&d);
 +	return d;
 +}
 +
 +float XMLElement::FloatText(float defaultValue) const
 +{
 +	float f = defaultValue;
 +	QueryFloatText(&f);
 +	return f;
 +}
 +
 +
 +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
 +{
 +    XMLAttribute* last = 0;
 +    XMLAttribute* attrib = 0;
 +    for( attrib = _rootAttribute;
 +            attrib;
 +            last = attrib, attrib = attrib->_next ) {
 +        if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
 +            break;
 +        }
 +    }
 +    if ( !attrib ) {
 +        attrib = CreateAttribute();
 +        TIXMLASSERT( attrib );
 +        if ( last ) {
 +            TIXMLASSERT( last->_next == 0 );
 +            last->_next = attrib;
 +        }
 +        else {
 +            TIXMLASSERT( _rootAttribute == 0 );
 +            _rootAttribute = attrib;
 +        }
 +        attrib->SetName( name );
 +    }
 +    return attrib;
 +}
 +
 +
 +void XMLElement::DeleteAttribute( const char* name )
 +{
 +    XMLAttribute* prev = 0;
 +    for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
 +        if ( XMLUtil::StringEqual( name, a->Name() ) ) {
 +            if ( prev ) {
 +                prev->_next = a->_next;
 +            }
 +            else {
 +                _rootAttribute = a->_next;
 +            }
 +            DeleteAttribute( a );
 +            break;
 +        }
 +        prev = a;
 +    }
 +}
 +
 +
 +char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
 +{
 +    XMLAttribute* prevAttribute = 0;
 +
 +    // Read the attributes.
 +    while( p ) {
 +        p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 +        if ( !(*p) ) {
 +            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() );
 +            return 0;
 +        }
 +
 +        // attribute.
 +        if (XMLUtil::IsNameStartChar( *p ) ) {
 +            XMLAttribute* attrib = CreateAttribute();
 +            TIXMLASSERT( attrib );
 +            attrib->_parseLineNum = _document->_parseCurLineNum;
 +
 +            int attrLineNum = attrib->_parseLineNum;
 +
 +            p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
 +            if ( !p || Attribute( attrib->Name() ) ) {
 +                DeleteAttribute( attrib );
 +                _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() );
 +                return 0;
 +            }
 +            // There is a minor bug here: if the attribute in the source xml
 +            // document is duplicated, it will not be detected and the
 +            // attribute will be doubly added. However, tracking the 'prevAttribute'
 +            // avoids re-scanning the attribute list. Preferring performance for
 +            // now, may reconsider in the future.
 +            if ( prevAttribute ) {
 +                TIXMLASSERT( prevAttribute->_next == 0 );
 +                prevAttribute->_next = attrib;
 +            }
 +            else {
 +                TIXMLASSERT( _rootAttribute == 0 );
 +                _rootAttribute = attrib;
 +            }
 +            prevAttribute = attrib;
 +        }
 +        // end of the tag
 +        else if ( *p == '>' ) {
 +            ++p;
 +            break;
 +        }
 +        // end of the tag
 +        else if ( *p == '/' && *(p+1) == '>' ) {
 +            _closingType = CLOSED;
 +            return p+2;	// done; sealed element.
 +        }
 +        else {
 +            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
 +            return 0;
 +        }
 +    }
 +    return p;
 +}
 +
 +void XMLElement::DeleteAttribute( XMLAttribute* attribute )
 +{
 +    if ( attribute == 0 ) {
 +        return;
 +    }
 +    MemPool* pool = attribute->_memPool;
 +    attribute->~XMLAttribute();
 +    pool->Free( attribute );
 +}
 +
 +XMLAttribute* XMLElement::CreateAttribute()
 +{
 +    TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
 +    XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
 +    TIXMLASSERT( attrib );
 +    attrib->_memPool = &_document->_attributePool;
 +    attrib->_memPool->SetTracked();
 +    return attrib;
 +}
 +
 +//
 +//	<ele></ele>
 +//	<ele>foo<b>bar</b></ele>
 +//
 +char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr )
 +{
 +    // Read the element name.
 +    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 +
 +    // The closing element is the </element> form. It is
 +    // parsed just like a regular element then deleted from
 +    // the DOM.
 +    if ( *p == '/' ) {
 +        _closingType = CLOSING;
 +        ++p;
 +    }
 +
 +    p = _value.ParseName( p );
 +    if ( _value.Empty() ) {
 +        return 0;
 +    }
 +
 +    p = ParseAttributes( p, curLineNumPtr );
 +    if ( !p || !*p || _closingType != OPEN ) {
 +        return p;
 +    }
 +
 +    p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
 +    return p;
 +}
 +
 +
 +
 +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
 +{
 +    if ( !doc ) {
 +        doc = _document;
 +    }
 +    XMLElement* element = doc->NewElement( Value() );					// fixme: this will always allocate memory. Intern?
 +    for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
 +        element->SetAttribute( a->Name(), a->Value() );					// fixme: this will always allocate memory. Intern?
 +    }
 +    return element;
 +}
 +
 +
 +bool XMLElement::ShallowEqual( const XMLNode* compare ) const
 +{
 +    TIXMLASSERT( compare );
 +    const XMLElement* other = compare->ToElement();
 +    if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
 +
 +        const XMLAttribute* a=FirstAttribute();
 +        const XMLAttribute* b=other->FirstAttribute();
 +
 +        while ( a && b ) {
 +            if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
 +                return false;
 +            }
 +            a = a->Next();
 +            b = b->Next();
 +        }
 +        if ( a || b ) {
 +            // different count
 +            return false;
 +        }
 +        return true;
 +    }
 +    return false;
 +}
 +
 +
 +bool XMLElement::Accept( XMLVisitor* visitor ) const
 +{
 +    TIXMLASSERT( visitor );
 +    if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
 +        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
 +            if ( !node->Accept( visitor ) ) {
 +                break;
 +            }
 +        }
 +    }
 +    return visitor->VisitExit( *this );
 +}
 +
 +
 +// --------- XMLDocument ----------- //
 +
 +// Warning: List must match 'enum XMLError'
 +const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
 +    "XML_SUCCESS",
 +    "XML_NO_ATTRIBUTE",
 +    "XML_WRONG_ATTRIBUTE_TYPE",
 +    "XML_ERROR_FILE_NOT_FOUND",
 +    "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
 +    "XML_ERROR_FILE_READ_ERROR",
 +    "UNUSED_XML_ERROR_ELEMENT_MISMATCH",
 +    "XML_ERROR_PARSING_ELEMENT",
 +    "XML_ERROR_PARSING_ATTRIBUTE",
 +    "UNUSED_XML_ERROR_IDENTIFYING_TAG",
 +    "XML_ERROR_PARSING_TEXT",
 +    "XML_ERROR_PARSING_CDATA",
 +    "XML_ERROR_PARSING_COMMENT",
 +    "XML_ERROR_PARSING_DECLARATION",
 +    "XML_ERROR_PARSING_UNKNOWN",
 +    "XML_ERROR_EMPTY_DOCUMENT",
 +    "XML_ERROR_MISMATCHED_ELEMENT",
 +    "XML_ERROR_PARSING",
 +    "XML_CAN_NOT_CONVERT_TEXT",
 +    "XML_NO_TEXT_NODE"
 +};
 +
 +
 +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
 +    XMLNode( 0 ),
 +    _writeBOM( false ),
 +    _processEntities( processEntities ),
 +    _errorID(XML_SUCCESS),
 +    _whitespaceMode( whitespaceMode ),
 +    _errorStr(),
 +    _errorLineNum( 0 ),
 +    _charBuffer( 0 ),
 +    _parseCurLineNum( 0 ),
 +    _unlinked(),
 +    _elementPool(),
 +    _attributePool(),
 +    _textPool(),
 +    _commentPool()
 +{
 +    // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
 +    _document = this;
 +}
 +
 +
 +XMLDocument::~XMLDocument()
 +{
 +    Clear();
 +}
 +
 +
 +void XMLDocument::MarkInUse(XMLNode* node)
 +{
 +	TIXMLASSERT(node);
 +	TIXMLASSERT(node->_parent == 0);
 +
 +	for (int i = 0; i < _unlinked.Size(); ++i) {
 +		if (node == _unlinked[i]) {
 +			_unlinked.SwapRemove(i);
 +			break;
 +		}
 +	}
 +}
 +
 +void XMLDocument::Clear()
 +{
 +    DeleteChildren();
 +	while( _unlinked.Size()) {
 +		DeleteNode(_unlinked[0]);	// Will remove from _unlinked as part of delete.
 +	}
 +
 +#ifdef TINYXML2_DEBUG
 +    const bool hadError = Error();
 +#endif
 +    ClearError();
 +
 +    delete [] _charBuffer;
 +    _charBuffer = 0;
 +
 +#if 0
 +    _textPool.Trace( "text" );
 +    _elementPool.Trace( "element" );
 +    _commentPool.Trace( "comment" );
 +    _attributePool.Trace( "attribute" );
 +#endif
 +    
 +#ifdef TINYXML2_DEBUG
 +    if ( !hadError ) {
 +        TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
 +        TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
 +        TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
 +        TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
 +    }
 +#endif
 +}
 +
 +
 +void XMLDocument::DeepCopy(XMLDocument* target) const
 +{
 +	TIXMLASSERT(target);
 +    if (target == this) {
 +        return; // technically success - a no-op.
 +    }
 +
 +	target->Clear();
 +	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
 +		target->InsertEndChild(node->DeepClone(target));
 +	}
 +}
 +
 +XMLElement* XMLDocument::NewElement( const char* name )
 +{
 +    XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
 +    ele->SetName( name );
 +    return ele;
 +}
 +
 +
 +XMLComment* XMLDocument::NewComment( const char* str )
 +{
 +    XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
 +    comment->SetValue( str );
 +    return comment;
 +}
 +
 +
 +XMLText* XMLDocument::NewText( const char* str )
 +{
 +    XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
 +    text->SetValue( str );
 +    return text;
 +}
 +
 +
 +XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
 +{
 +    XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
 +    dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
 +    return dec;
 +}
 +
 +
 +XMLUnknown* XMLDocument::NewUnknown( const char* str )
 +{
 +    XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
 +    unk->SetValue( str );
 +    return unk;
 +}
 +
 +static FILE* callfopen( const char* filepath, const char* mode )
 +{
 +    TIXMLASSERT( filepath );
 +    TIXMLASSERT( mode );
 +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
 +    FILE* fp = 0;
 +    errno_t err = fopen_s( &fp, filepath, mode );
 +    if ( err ) {
 +        return 0;
 +    }
 +#else
 +    FILE* fp = fopen( filepath, mode );
 +#endif
 +    return fp;
 +}
 +    
 +void XMLDocument::DeleteNode( XMLNode* node )	{
 +    TIXMLASSERT( node );
 +    TIXMLASSERT(node->_document == this );
 +    if (node->_parent) {
 +        node->_parent->DeleteChild( node );
 +    }
 +    else {
 +        // Isn't in the tree.
 +        // Use the parent delete.
 +        // Also, we need to mark it tracked: we 'know'
 +        // it was never used.
 +        node->_memPool->SetTracked();
 +        // Call the static XMLNode version:
 +        XMLNode::DeleteNode(node);
 +    }
 +}
 +
 +
 +XMLError XMLDocument::LoadFile( const char* filename )
 +{
 +    Clear();
 +    FILE* fp = callfopen( filename, "rb" );
 +    if ( !fp ) {
 +        SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ? filename : "<null>");
 +        return _errorID;
 +    }
 +    LoadFile( fp );
 +    fclose( fp );
 +    return _errorID;
 +}
 +
 +// This is likely overengineered template art to have a check that unsigned long value incremented
 +// by one still fits into size_t. If size_t type is larger than unsigned long type
 +// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
 +// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
 +// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
 +// types sizes relate to each other.
 +template
 +<bool = (sizeof(unsigned long) >= sizeof(size_t))>
 +struct LongFitsIntoSizeTMinusOne {
 +    static bool Fits( unsigned long value )
 +    {
 +        return value < (size_t)-1;
 +    }
 +};
 +
 +template <>
 +struct LongFitsIntoSizeTMinusOne<false> {
 +    static bool Fits( unsigned long )
 +    {
 +        return true;
 +    }
 +};
 +
 +XMLError XMLDocument::LoadFile( FILE* fp )
 +{
 +    Clear();
 +
 +    fseek( fp, 0, SEEK_SET );
 +    if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
 +        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 +        return _errorID;
 +    }
 +
 +    fseek( fp, 0, SEEK_END );
 +    const long filelength = ftell( fp );
 +    fseek( fp, 0, SEEK_SET );
 +    if ( filelength == -1L ) {
 +        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 +        return _errorID;
 +    }
 +    TIXMLASSERT( filelength >= 0 );
 +
 +    if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
 +        // Cannot handle files which won't fit in buffer together with null terminator
 +        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 +        return _errorID;
 +    }
 +
 +    if ( filelength == 0 ) {
 +        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
 +        return _errorID;
 +    }
 +
 +    const size_t size = filelength;
 +    TIXMLASSERT( _charBuffer == 0 );
 +    _charBuffer = new char[size+1];
 +    size_t read = fread( _charBuffer, 1, size, fp );
 +    if ( read != size ) {
 +        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 +        return _errorID;
 +    }
 +
 +    _charBuffer[size] = 0;
 +
 +    Parse();
 +    return _errorID;
 +}
 +
 +
 +XMLError XMLDocument::SaveFile( const char* filename, bool compact )
 +{
 +    FILE* fp = callfopen( filename, "w" );
 +    if ( !fp ) {
 +        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ? filename : "<null>");
 +        return _errorID;
 +    }
 +    SaveFile(fp, compact);
 +    fclose( fp );
 +    return _errorID;
 +}
 +
 +
 +XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
 +{
 +    // Clear any error from the last save, otherwise it will get reported
 +    // for *this* call.
 +    ClearError();
 +    XMLPrinter stream( fp, compact );
 +    Print( &stream );
 +    return _errorID;
 +}
 +
 +
 +XMLError XMLDocument::Parse( const char* p, size_t len )
 +{
 +    Clear();
 +
 +    if ( len == 0 || !p || !*p ) {
 +        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
 +        return _errorID;
 +    }
 +    if ( len == (size_t)(-1) ) {
 +        len = strlen( p );
 +    }
 +    TIXMLASSERT( _charBuffer == 0 );
 +    _charBuffer = new char[ len+1 ];
 +    memcpy( _charBuffer, p, len );
 +    _charBuffer[len] = 0;
 +
 +    Parse();
 +    if ( Error() ) {
 +        // clean up now essentially dangling memory.
 +        // and the parse fail can put objects in the
 +        // pools that are dead and inaccessible.
 +        DeleteChildren();
 +        _elementPool.Clear();
 +        _attributePool.Clear();
 +        _textPool.Clear();
 +        _commentPool.Clear();
 +    }
 +    return _errorID;
 +}
 +
 +
 +void XMLDocument::Print( XMLPrinter* streamer ) const
 +{
 +    if ( streamer ) {
 +        Accept( streamer );
 +    }
 +    else {
 +        XMLPrinter stdoutStreamer( stdout );
 +        Accept( &stdoutStreamer );
 +    }
 +}
 +
 +
 +void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... )
 +{
 +    TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
 +    _errorID = error;
 +    _errorLineNum = lineNum;
 +	_errorStr.Reset();
 +
 +    size_t BUFFER_SIZE = 1000;
 +    char* buffer = new char[BUFFER_SIZE];
 +
 +    TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
 +
 +	if (format) {
 +		size_t len = strlen(buffer);
 +		TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": ");
 +		len = strlen(buffer);
 +
 +		va_list va;
 +		va_start(va, format);
 +		TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
 +		va_end(va);
 +	}
 +	_errorStr.SetStr(buffer);
 +	delete[] buffer;
 +}
 +
 +
 +/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID)
 +{
 +	TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
 +    const char* errorName = _errorNames[errorID];
 +    TIXMLASSERT( errorName && errorName[0] );
 +    return errorName;
 +}
 +
 +const char* XMLDocument::ErrorStr() const 
 +{
 +	return _errorStr.Empty() ? "" : _errorStr.GetStr();
 +}
 +
 +
 +void XMLDocument::PrintError() const
 +{
 +    printf("%s\n", ErrorStr());
 +}
 +
 +const char* XMLDocument::ErrorName() const
 +{
 +    return ErrorIDToName(_errorID);
 +}
 +
 +void XMLDocument::Parse()
 +{
 +    TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
 +    TIXMLASSERT( _charBuffer );
 +    _parseCurLineNum = 1;
 +    _parseLineNum = 1;
 +    char* p = _charBuffer;
 +    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
 +    p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
 +    if ( !*p ) {
 +        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
 +        return;
 +    }
 +    ParseDeep(p, 0, &_parseCurLineNum );
 +}
 +
 +XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
 +    _elementJustOpened( false ),
 +    _stack(),
 +    _firstElement( true ),
 +    _fp( file ),
 +    _depth( depth ),
 +    _textDepth( -1 ),
 +    _processEntities( true ),
 +    _compactMode( compact ),
 +    _buffer()
 +{
 +    for( int i=0; i<ENTITY_RANGE; ++i ) {
 +        _entityFlag[i] = false;
 +        _restrictedEntityFlag[i] = false;
 +    }
 +    for( int i=0; i<NUM_ENTITIES; ++i ) {
 +        const char entityValue = entities[i].value;
 +        const unsigned char flagIndex = (unsigned char)entityValue;
 +        TIXMLASSERT( flagIndex < ENTITY_RANGE );
 +        _entityFlag[flagIndex] = true;
 +    }
 +    _restrictedEntityFlag[(unsigned char)'&'] = true;
 +    _restrictedEntityFlag[(unsigned char)'<'] = true;
 +    _restrictedEntityFlag[(unsigned char)'>'] = true;	// not required, but consistency is nice
 +    _buffer.Push( 0 );
 +}
 +
 +
 +void XMLPrinter::Print( const char* format, ... )
 +{
 +    va_list     va;
 +    va_start( va, format );
 +
 +    if ( _fp ) {
 +        vfprintf( _fp, format, va );
 +    }
 +    else {
 +        const int len = TIXML_VSCPRINTF( format, va );
 +        // Close out and re-start the va-args
 +        va_end( va );
 +        TIXMLASSERT( len >= 0 );
 +        va_start( va, format );
 +        TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
 +        char* p = _buffer.PushArr( len ) - 1;	// back up over the null terminator.
 +		TIXML_VSNPRINTF( p, len+1, format, va );
 +    }
 +    va_end( va );
 +}
 +
 +
 +void XMLPrinter::Write( const char* data, size_t size )
 +{
 +    if ( _fp ) {
 +        fwrite ( data , sizeof(char), size, _fp);
 +    }
 +    else {
 +        char* p = _buffer.PushArr( static_cast<int>(size) ) - 1;   // back up over the null terminator.
 +        memcpy( p, data, size );
 +        p[size] = 0;
 +    }
 +}
 +
 +
 +void XMLPrinter::Putc( char ch )
 +{
 +    if ( _fp ) {
 +        fputc ( ch, _fp);
 +    }
 +    else {
 +        char* p = _buffer.PushArr( sizeof(char) ) - 1;   // back up over the null terminator.
 +        p[0] = ch;
 +        p[1] = 0;
 +    }
 +}
 +
 +
 +void XMLPrinter::PrintSpace( int depth )
 +{
 +    for( int i=0; i<depth; ++i ) {
 +        Write( "    " );
 +    }
 +}
 +
 +
 +void XMLPrinter::PrintString( const char* p, bool restricted )
 +{
 +    // Look for runs of bytes between entities to print.
 +    const char* q = p;
 +
 +    if ( _processEntities ) {
 +        const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
 +        while ( *q ) {
 +            TIXMLASSERT( p <= q );
 +            // Remember, char is sometimes signed. (How many times has that bitten me?)
 +            if ( *q > 0 && *q < ENTITY_RANGE ) {
 +                // Check for entities. If one is found, flush
 +                // the stream up until the entity, write the
 +                // entity, and keep looking.
 +                if ( flag[(unsigned char)(*q)] ) {
 +                    while ( p < q ) {
 +                        const size_t delta = q - p;
 +                        const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
 +                        Write( p, toPrint );
 +                        p += toPrint;
 +                    }
 +                    bool entityPatternPrinted = false;
 +                    for( int i=0; i<NUM_ENTITIES; ++i ) {
 +                        if ( entities[i].value == *q ) {
 +                            Putc( '&' );
 +                            Write( entities[i].pattern, entities[i].length );
 +                            Putc( ';' );
 +                            entityPatternPrinted = true;
 +                            break;
 +                        }
 +                    }
 +                    if ( !entityPatternPrinted ) {
 +                        // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
 +                        TIXMLASSERT( false );
 +                    }
 +                    ++p;
 +                }
 +            }
 +            ++q;
 +            TIXMLASSERT( p <= q );
 +        }
 +    }
 +    // Flush the remaining string. This will be the entire
 +    // string if an entity wasn't found.
 +    TIXMLASSERT( p <= q );
 +    if ( !_processEntities || ( p < q ) ) {
 +        const size_t delta = q - p;
 +        const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
 +        Write( p, toPrint );
 +    }
 +}
 +
 +
 +void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
 +{
 +    if ( writeBOM ) {
 +        static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
 +        Write( reinterpret_cast< const char* >( bom ) );
 +    }
 +    if ( writeDec ) {
 +        PushDeclaration( "xml version=\"1.0\"" );
 +    }
 +}
 +
 +
 +void XMLPrinter::OpenElement( const char* name, bool compactMode )
 +{
 +    SealElementIfJustOpened();
 +    _stack.Push( name );
 +
 +    if ( _textDepth < 0 && !_firstElement && !compactMode ) {
 +        Putc( '\n' );
 +    }
 +    if ( !compactMode ) {
 +        PrintSpace( _depth );
 +    }
 +
 +    Write ( "<" );
 +    Write ( name );
 +
 +    _elementJustOpened = true;
 +    _firstElement = false;
 +    ++_depth;
 +}
 +
 +
 +void XMLPrinter::PushAttribute( const char* name, const char* value )
 +{
 +    TIXMLASSERT( _elementJustOpened );
 +    Putc ( ' ' );
 +    Write( name );
 +    Write( "=\"" );
 +    PrintString( value, false );
 +    Putc ( '\"' );
 +}
 +
 +
 +void XMLPrinter::PushAttribute( const char* name, int v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    PushAttribute( name, buf );
 +}
 +
 +
 +void XMLPrinter::PushAttribute( const char* name, unsigned v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    PushAttribute( name, buf );
 +}
 +
 +
 +void XMLPrinter::PushAttribute(const char* name, int64_t v)
 +{
 +	char buf[BUF_SIZE];
 +	XMLUtil::ToStr(v, buf, BUF_SIZE);
 +	PushAttribute(name, buf);
 +}
 +
 +
 +void XMLPrinter::PushAttribute( const char* name, bool v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    PushAttribute( name, buf );
 +}
 +
 +
 +void XMLPrinter::PushAttribute( const char* name, double v )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( v, buf, BUF_SIZE );
 +    PushAttribute( name, buf );
 +}
 +
 +
 +void XMLPrinter::CloseElement( bool compactMode )
 +{
 +    --_depth;
 +    const char* name = _stack.Pop();
 +
 +    if ( _elementJustOpened ) {
 +        Write( "/>" );
 +    }
 +    else {
 +        if ( _textDepth < 0 && !compactMode) {
 +            Putc( '\n' );
 +            PrintSpace( _depth );
 +        }
 +        Write ( "</" );
 +        Write ( name );
 +        Write ( ">" );
 +    }
 +
 +    if ( _textDepth == _depth ) {
 +        _textDepth = -1;
 +    }
 +    if ( _depth == 0 && !compactMode) {
 +        Putc( '\n' );
 +    }
 +    _elementJustOpened = false;
 +}
 +
 +
 +void XMLPrinter::SealElementIfJustOpened()
 +{
 +    if ( !_elementJustOpened ) {
 +        return;
 +    }
 +    _elementJustOpened = false;
 +    Putc( '>' );
 +}
 +
 +
 +void XMLPrinter::PushText( const char* text, bool cdata )
 +{
 +    _textDepth = _depth-1;
 +
 +    SealElementIfJustOpened();
 +    if ( cdata ) {
 +        Write( "<![CDATA[" );
 +        Write( text );
 +        Write( "]]>" );
 +    }
 +    else {
 +        PrintString( text, true );
 +    }
 +}
 +
 +void XMLPrinter::PushText( int64_t value )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( value, buf, BUF_SIZE );
 +    PushText( buf, false );
 +}
 +
 +void XMLPrinter::PushText( int value )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( value, buf, BUF_SIZE );
 +    PushText( buf, false );
 +}
 +
 +
 +void XMLPrinter::PushText( unsigned value )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( value, buf, BUF_SIZE );
 +    PushText( buf, false );
 +}
 +
 +
 +void XMLPrinter::PushText( bool value )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( value, buf, BUF_SIZE );
 +    PushText( buf, false );
 +}
 +
 +
 +void XMLPrinter::PushText( float value )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( value, buf, BUF_SIZE );
 +    PushText( buf, false );
 +}
 +
 +
 +void XMLPrinter::PushText( double value )
 +{
 +    char buf[BUF_SIZE];
 +    XMLUtil::ToStr( value, buf, BUF_SIZE );
 +    PushText( buf, false );
 +}
 +
 +
 +void XMLPrinter::PushComment( const char* comment )
 +{
 +    SealElementIfJustOpened();
 +    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
 +        Putc( '\n' );
 +        PrintSpace( _depth );
 +    }
 +    _firstElement = false;
 +
 +    Write( "<!--" );
 +    Write( comment );
 +    Write( "-->" );
 +}
 +
 +
 +void XMLPrinter::PushDeclaration( const char* value )
 +{
 +    SealElementIfJustOpened();
 +    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
 +        Putc( '\n' );
 +        PrintSpace( _depth );
 +    }
 +    _firstElement = false;
 +
 +    Write( "<?" );
 +    Write( value );
 +    Write( "?>" );
 +}
 +
 +
 +void XMLPrinter::PushUnknown( const char* value )
 +{
 +    SealElementIfJustOpened();
 +    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
 +        Putc( '\n' );
 +        PrintSpace( _depth );
 +    }
 +    _firstElement = false;
 +
 +    Write( "<!" );
 +    Write( value );
 +    Putc( '>' );
 +}
 +
 +
 +bool XMLPrinter::VisitEnter( const XMLDocument& doc )
 +{
 +    _processEntities = doc.ProcessEntities();
 +    if ( doc.HasBOM() ) {
 +        PushHeader( true, false );
 +    }
 +    return true;
 +}
 +
 +
 +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
 +{
 +    const XMLElement* parentElem = 0;
 +    if ( element.Parent() ) {
 +        parentElem = element.Parent()->ToElement();
 +    }
 +    const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
 +    OpenElement( element.Name(), compactMode );
 +    while ( attribute ) {
 +        PushAttribute( attribute->Name(), attribute->Value() );
 +        attribute = attribute->Next();
 +    }
 +    return true;
 +}
 +
 +
 +bool XMLPrinter::VisitExit( const XMLElement& element )
 +{
 +    CloseElement( CompactMode(element) );
 +    return true;
 +}
 +
 +
 +bool XMLPrinter::Visit( const XMLText& text )
 +{
 +    PushText( text.Value(), text.CData() );
 +    return true;
 +}
 +
 +
 +bool XMLPrinter::Visit( const XMLComment& comment )
 +{
 +    PushComment( comment.Value() );
 +    return true;
 +}
 +
 +bool XMLPrinter::Visit( const XMLDeclaration& declaration )
 +{
 +    PushDeclaration( declaration.Value() );
 +    return true;
 +}
 +
 +
 +bool XMLPrinter::Visit( const XMLUnknown& unknown )
 +{
 +    PushUnknown( unknown.Value() );
 +    return true;
 +}
 +
 +}   // namespace tinyxml2
 +
 diff --git a/hw4/src/transformation3d.cpp b/hw4/src/transformation3d.cpp new file mode 100644 index 0000000..bde9caa --- /dev/null +++ b/hw4/src/transformation3d.cpp @@ -0,0 +1,174 @@ +/******************************************************************/ +/* 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 "transformation3d.h" + + +////////////////// +// Constructors // +////////////////// +transformation3d::transformation3d(void) +{ +  _translation = vec3d(); +  _transformation = mat3d(1.0f); +  _inverseTransformation = mat3d(1.0f); +} + + +transformation3d::transformation3d(const vec3d& translation, const mat3d& transformation, const mat3d& inverseTransformation) +{ +  _translation = translation; +  _transformation = transformation; +  _inverseTransformation = inverseTransformation; +} + + +transformation3d::transformation3d(const transformation3d& t) +{ +  _translation = t._translation; +  _transformation = t._transformation; +  _inverseTransformation = t._inverseTransformation; +} + + +////////////// +// Operator // +////////////// +transformation3d& transformation3d::operator=(const transformation3d& t) +{ +  _assign(t); +  return *this; +} + + +transformation3d transformation3d::operator*(const transformation3d& t) const +{ +  return transformation3d( transformPoint(t._translation), +			   _transformation * t._transformation, +			   t._inverseTransformation * _inverseTransformation); +} + + +transformation3d& transformation3d::operator*=(const transformation3d& t) +{ +  *this = *this * t; +  return *this; +} + + +////////////// +// Mutators // +////////////// +transformation3d& transformation3d::invert(void) +{ +  _translation = _inverseTransformation * (-_translation); +  swap(_transformation, _inverseTransformation); +  return *this; +} + +///////////// +// Methods // +///////////// +vec3d transformation3d::transformPoint(const vec3d& p) const +{ +  // first transform, then translate +  vec3d transformed = _transformation * p; +  transformed += _translation; +   +  // Done. +  return transformed; +} + + +vec3d transformation3d::transformDirection(const vec3d& d) const +{ +  // Only apply transformation +  vec3d transformed = _transformation * d; + +  // Done. +  return transformed.normalize(); +} + + +vec3d transformation3d::transformNormal(const vec3d& n) const +{ +  // Don't apply translation. +  // n' = (_transformation^T)^-1 * n +  // n'^T = n^T * _transformation^-1 +  // n'^T = n^T * _inverseTransformation +  vec3d transformed = n * _inverseTransformation; +   +  // Done. +  return transformed.normalize(); +} + + +vec3d transformation3d::inverseTransformPoint(const vec3d& p) const +{ +  // for undo translation, then invert the transformation +  vec3d transformed = p - _translation; +  transformed = _inverseTransformation * transformed; + +  // Done. +  return transformed; +} + + +vec3d transformation3d::inverseTransformDirection(const vec3d& d) const +{ +  // Only invert the transformation +  vec3d transformed = _inverseTransformation * d; + +  // Done. +  return transformed.normalize(); +} + + +vec3d transformation3d::inverseTransformNormal(const vec3d& n) const +{ +  // Don't apply translation. Undo (transformation^T)^-1 +  vec3d transformed = n * _transformation; + +  // Done. +  return transformed.normalize(); +} + + +/////////////////////// +// Protected Methods // +/////////////////////// +void transformation3d::_swap(transformation3d& t) +{ +  std::swap(_translation, t._translation); +  std::swap(_transformation, t._transformation); +  std::swap(_inverseTransformation, t._inverseTransformation); +} + + +void transformation3d::_assign(const transformation3d& t) +{ +  // avoid copying when self-assigning +  if(&t == this) return; + +  // Copy +  _translation = t._translation; +  _transformation = t._transformation; +  _inverseTransformation = t._inverseTransformation; + +  // Done. +} + + +void transformation3d::_print(std::ostream& s) const +{ +  s << "{"; +  s << "Translation = " << _translation << ", "; +  s << "Transformation = " << _transformation << ", "; +  s << "Inverse Trans. = " << _inverseTransformation; +  s << "}"; +} diff --git a/hw4/src/triangle.cpp b/hw4/src/triangle.cpp new file mode 100644 index 0000000..3abe113 --- /dev/null +++ b/hw4/src/triangle.cpp @@ -0,0 +1,465 @@ +/******************************************************************/ +/* 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 <cassert> +#include <algorithm> + +#include "triangle.h" +#include "constants.h" + +////////////////// +// Constructors // +////////////////// +triangle::triangle(void) +{ +  _vertex_list = nullptr; +  _normal_list = nullptr; +  _textureCoord_list = nullptr; +} + + +////////////////////// +// Copy Constructor // +////////////////////// +triangle::triangle(const triangle& t) +  : triangle() +{ +  // share ptr to list +  _vertex_list = t._vertex_list; +  _normal_list = t._normal_list; +  _textureCoord_list = t._textureCoord_list; + +  // copy indices +  _vertex_idx = t._vertex_idx; +  _normal_idx = t._normal_idx; +  _textureCoord_idx = t._textureCoord_idx; +} + + +////////////////////// +// Move Constructor // +////////////////////// +triangle::triangle(triangle&& t) +  : triangle() +{ +  // swap list (no need to increment ref-count) +  std::swap(_vertex_list, t._vertex_list); +  std::swap(_normal_list, t._normal_list); +  std::swap(_textureCoord_list, t._textureCoord_list); + +  // copy indices +  _vertex_idx = t._vertex_idx; +  _normal_idx = t._normal_idx; +  _textureCoord_idx = t._textureCoord_idx; +} + + +////////////////////////// +// General Constructors // +////////////////////////// +triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3) +  : triangle() +{ +  // create a list of three vertices +  auto vertex_list = std::make_shared<std::vector<vec3d>>(3); +  _vertex_list = vertex_list; + +  // copy values +  (*vertex_list)[0] = v1; +  (*vertex_list)[1] = v2; +  (*vertex_list)[2] = v3; + +  // set indices +  _vertex_idx = {0,1,2}; +} + + +triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr<const std::vector<vec3d>>& vertex_list) +  : triangle() +{ +  // share vertex list +  _vertex_list = vertex_list; + +  // copy indices +  _vertex_idx[0] = v1_idx; +  _vertex_idx[1] = v2_idx; +  _vertex_idx[2] = v3_idx; +} + + + +triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3, +		  const vec3d& n1, const vec3d& n2, const vec3d& n3) +  : triangle(v1, v2, v3) +{ +  // create a list of three normals +  auto normal_list = std::make_shared<std::vector<vec3d>>(3); +  _normal_list = normal_list; + +  // copy values +  (*normal_list)[0] = n1; +  (*normal_list)[1] = n2; +  (*normal_list)[2] = n3; + +  // set indices +  _normal_idx = {0,1,2}; +} + + +triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr<const std::vector<vec3d>>& vertex_list, +		     size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr<const std::vector<vec3d>>& normal_list) +  : triangle(v1_idx, v2_idx, v3_idx, vertex_list) +{ +  // share normal list +  _normal_list = normal_list; + +  // copy indices +  _normal_idx[0] = n1_idx; +  _normal_idx[1] = n2_idx; +  _normal_idx[2] = n3_idx; +} + + + +triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3, +		   const vec2d& t1, const vec2d& t2, const vec2d& t3) +  : triangle(v1, v2, v3) +{ +  // create a list of three texture coordinates +  auto textureCoord_list = std::make_shared<std::vector<vec2d>>(3); +  _textureCoord_list = textureCoord_list; + +  // copy values +  (*textureCoord_list)[0] = t1; +  (*textureCoord_list)[1] = t2; +  (*textureCoord_list)[2] = t3; + +  // set indices +  _textureCoord_idx = {0,1,2}; +} + + +triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr<const std::vector<vec3d>>& vertex_list, +		   size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr<const std::vector<vec2d>>& texcoord_list) +  : triangle(v1_idx, v2_idx, v3_idx, vertex_list) +{ +  // share normal list +  _textureCoord_list = texcoord_list; + +  // copy indices +  _textureCoord_idx[0] = t1_idx; +  _textureCoord_idx[1] = t2_idx; +  _textureCoord_idx[2] = t3_idx; +} + + + +triangle::triangle(const vec3d& v1, const vec3d& v2, const vec3d& v3, +		   const vec3d& n1, const vec3d& n2, const vec3d& n3, +		   const vec2d& t1, const vec2d& t2, const vec2d& t3) +  : triangle(v1, v2, v3, n1, n2, n3) +{ +  // create a list of three texture coordinates +  auto textureCoord_list = std::make_shared<std::vector<vec2d>>(3); +  _textureCoord_list = textureCoord_list; + +  // copy values +  (*textureCoord_list)[0] = t1; +  (*textureCoord_list)[1] = t2; +  (*textureCoord_list)[2] = t3; + +  // set indices +  _textureCoord_idx = {0,1,2}; +} + + +triangle::triangle(size_t v1_idx, size_t v2_idx, size_t v3_idx, const std::shared_ptr<const std::vector<vec3d>>& vertex_list, +		   size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr<const std::vector<vec3d>>& normal_list, +		   size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr<const std::vector<vec2d>>& texcoord_list) +  : triangle(v1_idx, v2_idx, v3_idx, vertex_list, +	     n1_idx, n2_idx, n3_idx, normal_list) +{ +  // share normal list +  _textureCoord_list = texcoord_list; + +  // copy indices +  _textureCoord_idx[0] = t1_idx; +  _textureCoord_idx[1] = t2_idx; +  _textureCoord_idx[2] = t3_idx; +} + + + +//////////////// +// Inspectors // +//////////////// +const vec3d& triangle::vertex(size_t index) const +{ +  // bounds check +  assert(_vertex_list && index < 3); + +  // Done. +  return (*_vertex_list)[_vertex_idx[index]]; +} + + +const vec3d& triangle::normal(size_t index) const +{ +  // bounds check +  assert(_normal_list && index < 3); +   +  // Done. +  return (*_normal_list)[_normal_idx[index]]; +} + + +const vec2d& triangle::textureCoordinate(size_t index) const +{ +  // bounds check +  assert(_textureCoord_list && index < 3); + +  // Done. +  return (*_textureCoord_list)[_textureCoord_idx[index]]; +} + + +bool triangle::hasPerVertexNormals(void) const +{ +  return (bool)(_normal_list); +} + + +bool triangle::hasPerVertexTextureCoordinates(void) const +{ +  return (bool)(_textureCoord_list); +} + + +/////////////// +// Operators // +/////////////// +triangle& triangle::operator=(const triangle& t) +{ +  _assign(t); +  return *this; +} + + +triangle& triangle::operator=(triangle&& t) +{ +  // swap list (no need to increment ref-count) +  std::swap(_vertex_list, t._vertex_list); +  std::swap(_normal_list, t._normal_list); +  std::swap(_textureCoord_list, t._textureCoord_list); + +  // copy indices +  _vertex_idx = t._vertex_idx; +  _normal_idx = t._normal_idx; +  _textureCoord_idx = t._textureCoord_idx; + +  // Done. +  return *this; +} + + +///////////// +// Methods // +///////////// +bool triangle::intersect(const ray& r, vec3d& barycentricCoord, float& t) const +{ +  // Ray-Triangle intersection [Moller and Trumbore 1997] +  // +  // [t           ]                1             [ ((r.origin-v0) x (v1-v0)).(v2-v0) ] +  // [barycenter.y] = -------------------------- [ (r.dir x (v2-v0)).(r.origin-v0)   ] +  // [barycenter.z]   ((r.dir x (v2-vo)).(v1-v0) [ ((r.origin-v0) x (v1-v0)).r.dir   ] +  // +   +  // compute denominator +  vec3d e1 = vertex(1) - vertex(0); +  vec3d e2 = vertex(2) - vertex(0); +  vec3d p = r.direction().cross(e2); +  float denom = p.dot(e1); + +  // check if parallel (denominator == 0) +  if(fabs(denom) < EPSILON) return false; +  denom = 1.0f / denom; + +  // compute barycentricCoord.y +  vec3d diff = r.origin() - vertex(0); +  barycentricCoord.y = denom * p.dot(diff); + +  // check if barycenter.y inside triangle +  if(barycentricCoord.y < -EPSILON || barycentricCoord.y > 1.0f+EPSILON) return false; + +  // compute barycentricCoord.z +  vec3d diffXe1 = diff.cross(e1); +  barycentricCoord.z = denom * diffXe1.dot(r.direction()); +   +  // check if barycenter.z inside triangle +  if(barycentricCoord.z < -EPSILON || barycentricCoord.z > 1.0f+EPSILON) return false; + +  // compute barycenter.x and check if inside +  barycentricCoord.x = 1.0f - barycentricCoord.y - barycentricCoord.z; +  if(barycentricCoord.y + barycentricCoord.z > 1.0f+EPSILON) return false; + +  // compute t +  t = denom * diffXe1.dot(e2); + +  // check if in front (i.e., t>0) +  if(t < EPSILON) return false; + +  // Clamp (to compensate for roundoff errors) +  barycentricCoord.clamp(0.0f, 1.0f); + +  // Done. +  return true; +} + + +boundingBox triangle::boundingbox(void) const +{ +  boundingBox bb; +  for(unsigned int i=0; i < 3; i++) +    bb += vertex(i); +  return bb; +} + + +vec3d triangle::vertex(const vec3d& barycentricCoord) const +{ +  vec3d result(0.0f); +  for(unsigned int i=0; i < 3; i++) +    result += vertex(i) * barycentricCoord[i]; +  return result; +} + + +vec3d triangle::normal(void) const +{ +  vec3d e1 = vertex(1) - vertex(0); +  vec3d e2 = vertex(2) - vertex(0); +  return e1.cross(e2).normalize(); +} + +vec3d triangle::shadingAxis(void) const +{ +  // follow texture coordinates if defined, otherwise, return the first axis. +  if(!_textureCoord_list) return normalize(vertex(1)-vertex(0)); + +  // find the vector that follows the U-axis of the texture coordinates +  // solve: +  // [ (t1-t0) (t2-t0) ] [ delta_beta delta_gamma ]^T = [1 0]^T +  // for delta_beta and delta_gamma. +  vec3d v1 = vertex(1) - vertex(0); +  vec3d v2 = vertex(2) - vertex(0); +  vec2d t1 = textureCoordinate(1) - textureCoordinate(0); +  vec2d t2 = textureCoordinate(2) - textureCoordinate(0); + +  // check special cases where U or V is aligned with a vertex +  if( fabs(t1.v) < EPSILON ) return normalize(t1.u*v1); +  if( fabs(t2.v) < EPSILON ) return normalize(t2.u*v2); + +  // compute delta_beta +  float inv_delta_beta = t1.u - t1.v*t2.u/t2.v; + +  // => degenrate case +  if(fabs(inv_delta_beta) < EPSILON) return normalize(v1); + +  float delta_beta = 1.0f / inv_delta_beta; +  float delta_gamma = -delta_beta * t1.v / t2.v; + +  // compute U +  return normalize( delta_beta*v1 + delta_gamma*v2 ); +} + + +vec3d triangle::normal(const vec3d& barycentricCoord) const +{ +  // sanity check +  if(!hasPerVertexNormals()) return normal(); +   +  // interpolate +  vec3d result(0.0f); +  for(unsigned int i=0; i < 3; i++) +    result += normal(i) * barycentricCoord[i]; +  return result.normalize(); +} + + +vec2d triangle::textureCoordinate(const vec3d& barycentricCoord) const +{ +  // sanity check +  if(!hasPerVertexTextureCoordinates()) return vec2d(); + +  // interpolate +  vec2d result(0.0f); +  for(unsigned int i=0; i < 3; i++) +    result += textureCoordinate(i) * barycentricCoord[i]; +  return result; +} + + +float triangle::area(void) const +{ +  vec3d e1 = vertex(1) - vertex(0); +  vec3d e2 = vertex(2) - vertex(0); +  return 0.5f * sqrt( e1.squared_length() * e2.squared_length() ); +} + + +vec3d triangle::sample(float r1, float r2, vec3d& barycentricCoord, float& pdf) const +{ +  r2 = sqrt(r2); + +  // compute barycentric coordinate +  barycentricCoord.x = r1*r2; +  barycentricCoord.y = (1.0f - r2); +  barycentricCoord.z = 1.0f - barycentricCoord.x - barycentricCoord.y; +   +  // compute pdf +  pdf = 1.0f / area(); + +  // Done. +  return vertex(barycentricCoord); +} + + +///////////////////// +// Private Methods // +///////////////////// +void triangle::_assign(const triangle& t) +{ +  // avoid self-assign +  if(&t == this) return; + +  // share ptr to list +  _vertex_list = t._vertex_list; +  _normal_list = t._normal_list; +  _textureCoord_list = t._textureCoord_list; + +  // copy indices +  _vertex_idx = t._vertex_idx; +  _normal_idx = t._normal_idx; +  _textureCoord_idx = t._textureCoord_idx; +} + + +void triangle::_swap(triangle& t) +{ +  // copy list ptr +  std::swap(_vertex_list, t._vertex_list); +  std::swap(_normal_list, t._normal_list); +  std::swap(_textureCoord_list, t._textureCoord_list); + +  // copy indices +  std::swap(_vertex_idx, t._vertex_idx); +  std::swap(_normal_idx, t._normal_idx); +  std::swap(_textureCoord_idx, t._textureCoord_idx); +} + diff --git a/hw4/src/triangleMesh.cpp b/hw4/src/triangleMesh.cpp new file mode 100644 index 0000000..5507a9c --- /dev/null +++ b/hw4/src/triangleMesh.cpp @@ -0,0 +1,57 @@ +/******************************************************************/ +/* 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 <algorithm> +#include "triangleMesh.h" + +///////////////// +// Constructors // +////////////////// +triangleMesh::triangleMesh(void) +  : boundedCompound() +{ +  // Nothing. +} + + +triangleMesh::triangleMesh(const std::vector<triangle>& triangle_list, const std::shared_ptr<const shader_base>& shader, const transformation3d& transform) +  : boundedCompound(transform, shader) +{ +  // allocate sufficient memory +  _triangles.reserve(triangle_list.size()); + +  // construct vector of boundedTriangles  +  for_each(triangle_list.begin(), triangle_list.end(), [&](const triangle& tri) +  { +    _triangles.push_back( std::make_shared<boundedTriangle>(tri, shader) ); +  }); + +  // Done. +  initializeBoundingBox(); +} + + +///////////// +// Methods // +///////////// +const std::vector<std::shared_ptr<const boundedPrimitive>>& triangleMesh::compounds(void) const +{ +  return _triangles; +} + + +bool triangleMesh::hasShader(void) const +{ +  return boundedPrimitive::hasShader(); +} + + +void triangleMesh::_print(std::ostream& s) const +{ +  s << "triangleMesh (" << _bb << ", " << _triangles.size() << " compounds)"; +} diff --git a/hw4/src/util.cpp b/hw4/src/util.cpp new file mode 100644 index 0000000..917eef4 --- /dev/null +++ b/hw4/src/util.cpp @@ -0,0 +1,45 @@ +/******************************************************************/ +/* 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 "util.h" + + +std::string getFilename(const std::string& path) +{ +#ifdef _WIN32 +  unsigned char pathSep = '\\'; +#else +  unsigned char pathSep = '/'; +#endif +  size_t start = path.find_last_of(pathSep); +  size_t end = path.find_last_of("."); +  if(start == std::string::npos) start = -1; +  if(end == std::string::npos) end = path.size(); +  return path.substr(start+1, end-start-1); +} + + +std::string getExtension(const std::string& path) +{ +  size_t lastidx = path.find_last_of(".");  +  if(lastidx == std::string::npos) return std::string(); +  else return path.substr(lastidx+1, std::string::npos); +} + + +std::string getDirectory(const std::string& path) +{ +#ifdef _WIN32 +  unsigned char pathSep = '\\'; +#else +  unsigned char pathSep = '/'; +#endif +  size_t lastidx = path.find_last_of(pathSep); +  if(lastidx == std::string::npos) return std::string(); +  return path.substr(0, lastidx+1); +} diff --git a/hw4/src/vec2d.cpp b/hw4/src/vec2d.cpp new file mode 100644 index 0000000..ae12e41 --- /dev/null +++ b/hw4/src/vec2d.cpp @@ -0,0 +1,258 @@ +/******************************************************************/ +/* 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 <cassert> +#include <algorithm> +#include "vec2d.h" + +////////////////// +// Constructors // +////////////////// +vec2d::vec2d(vec2d::const_reference value) +{  +  x = y = value; +} + + +vec2d::vec2d(vec2d::const_reference x, vec2d::const_reference y)  +{ +  this->x = x; +  this->y = y; +} + + +vec2d::vec2d(const vec2d& v) +{ +  x = v.x; +  y = v.y; +} +  + +//////////////// +// Inspectors // +//////////////// +vec2d::const_reference vec2d::operator[](size_t index) const  +{  +  assert(index < size()); +  return data[index];  +} + + +vec2d::reference vec2d::operator[](size_t index)  +{  +  assert(index < size()); +  return data[index];  +} + + +vec2d::iterator vec2d::begin(void) +{ +  return data; +} + + +vec2d::const_iterator vec2d::begin(void) const +{ +  return data; +} + + +vec2d::iterator vec2d::end(void) +{ +  return begin() + size(); +} + + +vec2d::const_iterator vec2d::end(void) const +{ +  return begin() + size(); +} + + +/////////////// +// Operators // +/////////////// +vec2d& vec2d::operator=(const vec2d& v)  +{  +  _assign(v);  +  return *this;  +} + + +bool vec2d::operator==(const vec2d& v) const  +{  +  return (x == v.x) && (y == v.y);  +} + + +bool vec2d::operator!=(const vec2d& v) const  +{  +  return (x != v.x) || (y != v.y);  +} +   + +vec2d vec2d::operator-(void) const +{ +  return vec2d(-x, -y); +} + + +vec2d vec2d::operator+(const vec2d& v) const  +{  +  return vec2d(x + v.x, y + v.y);  +} + + +vec2d vec2d::operator-(const vec2d& v) const  +{  +  return vec2d(x - v.x, y - v.y);  +} + + +vec2d vec2d::operator*(const vec2d& v) const  +{  +  return vec2d(x * v.x, y * v.y);  +} + + +vec2d vec2d::operator*(vec2d::const_reference scale) const  +{  +  return vec2d(x * scale, y * scale);  +} + + +vec2d vec2d::operator/(const vec2d& v) const  +{  +  return vec2d(x / v.x, y / v.y);  +} + + +vec2d vec2d::operator/(vec2d::const_reference scale) const  +{  +  return vec2d(x / scale, y / scale);  +} + + +vec2d& vec2d::operator+=(const vec2d& v)  +{  +  x += v.x;  +  y += v.y;  +  return *this;  +} + + +vec2d& vec2d::operator-=(const vec2d& v)  +{  +  x -= v.x; +  y -= v.y; +  return *this;  +} + + +vec2d& vec2d::operator*=(const vec2d& v)  +{  +  x *= v.x; +  y *= v.y; +  return *this;  +} + + +vec2d& vec2d::operator*=(vec2d::const_reference scale)  +{  +  x *= scale; +  y *= scale; +  return *this;  +} + + +vec2d& vec2d::operator/=(const vec2d& v)  +{  +  x /= v.x; +  y /= v.y; +  return *this;  +}							 + + +vec2d& vec2d::operator/=(vec2d::const_reference scale)  +{  +  x /= scale; +  y /= scale; +  return *this;  +} + + + +/////////////// +// Modifiers // +/////////////// +vec2d::value_type vec2d::dot(const vec2d& v) const  +{  +  return (x*v.x + y*v.y); +} + + +vec2d::value_type vec2d::squared_length(void) const  +{  +  return dot(*this);  +} + + +vec2d::value_type vec2d::length(void) const  +{  +  return sqrt(squared_length());  +} + + +vec2d::value_type vec2d::squared_distance(const vec2d& v) const  +{ +  return (*this - v).squared_length();  +} + + +vec2d::value_type vec2d::distance(const vec2d& v) const  +{  +  return sqrt(squared_distance(v));  +} + + +/////////////// +// Modifiers // +/////////////// +vec2d& vec2d::abs(void) +{ +  std::for_each(begin(), end(), [](reference val)  +  {  +    if(val < 0) val = -val;  +  }); +  return *this; +} + + +vec2d& vec2d::normalize(void) +{  +  *this /= length(); +  return *this; +} + + +///////////////////// +// Private Methods // +///////////////////// +void vec2d::_assign(const vec2d& v) +{  +  x = v.x;   +  y = v.y;  +} + + +void vec2d::_swap(vec2d& v)  +{ +  std::swap(x, v.x); +  std::swap(y, v.y); +} diff --git a/hw4/src/vec3d.cpp b/hw4/src/vec3d.cpp new file mode 100644 index 0000000..3b20ad5 --- /dev/null +++ b/hw4/src/vec3d.cpp @@ -0,0 +1,287 @@ +/******************************************************************/ +/* 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 <cassert> +#include <algorithm> +#include "vec3d.h" + +////////////////// +// Constructors // +////////////////// +vec3d::vec3d(vec3d::const_reference value) +{  +  x = y = z = value; +} + + +vec3d::vec3d(vec3d::const_reference x, vec3d::const_reference y, vec3d::const_reference z)  +{ +  this->x = x; +  this->y = y; +  this->z = z; +} + + +vec3d::vec3d(const vec3d& v) +{ +  x = v.x; +  y = v.y; +  z = v.z; +} +  + +//////////////// +// Inspectors // +//////////////// +vec3d::const_reference vec3d::operator[](size_t index) const  +{  +  assert(index < size()); +  return data[index];  +} + + +vec3d::reference vec3d::operator[](size_t index)  +{  +  assert(index < size()); +  return data[index];  +} + + +vec3d::iterator vec3d::begin(void) +{ +  return data; +} + + +vec3d::const_iterator vec3d::begin(void) const +{ +  return data; +} + + +vec3d::iterator vec3d::end(void) +{ +  return begin() + size(); +} + + +vec3d::const_iterator vec3d::end(void) const +{ +  return begin() + size(); +} + + +/////////////// +// Operators // +/////////////// +vec3d& vec3d::operator=(const vec3d& v)  +{  +  _assign(v);  +  return *this;  +} + + +bool vec3d::operator==(const vec3d& v) const  +{  +  return (x == v.x) && (y == v.y) && (z == v.z);  +} + + +bool vec3d::operator!=(const vec3d& v) const  +{  +  return (x != v.x) || (y != v.y) || (z != v.z);  +} +   + +vec3d vec3d::operator-(void) const +{ +  return vec3d(-x, -y, -z); +} + + +vec3d vec3d::operator+(const vec3d& v) const  +{  +  return vec3d(x + v.x, y + v.y, z + v.z);  +} + + +vec3d vec3d::operator-(const vec3d& v) const  +{  +  return vec3d(x - v.x, y - v.y, z - v.z);  +} + + +vec3d vec3d::operator*(const vec3d& v) const  +{  +  return vec3d(x * v.x, y * v.y, z * v.z);  +} + + +vec3d vec3d::operator*(vec3d::const_reference scale) const  +{  +  return vec3d(x * scale, y * scale, z * scale);  +} + + +vec3d vec3d::operator/(const vec3d& v) const  +{  +  return vec3d(x / v.x, y / v.y, z / v.z);  +} + + +vec3d vec3d::operator/(vec3d::const_reference scale) const  +{  +  return vec3d(x / scale, y / scale, z / scale);  +} + + +vec3d& vec3d::operator+=(const vec3d& v)  +{  +  x += v.x;  +  y += v.y;  +  z += v.z;  +  return *this;  +} + + +vec3d& vec3d::operator-=(const vec3d& v)  +{  +  x -= v.x; +  y -= v.y; +  z -= v.z; +  return *this;  +} + + +vec3d& vec3d::operator*=(const vec3d& v)  +{  +  x *= v.x; +  y *= v.y; +  z *= v.z; +  return *this;  +} + + +vec3d& vec3d::operator*=(vec3d::const_reference scale)  +{  +  x *= scale; +  y *= scale; +  z *= scale; +  return *this;  +} + + +vec3d& vec3d::operator/=(const vec3d& v)  +{  +  x /= v.x; +  y /= v.y; +  z /= v.z; +  return *this;  +}							 + + +vec3d& vec3d::operator/=(vec3d::const_reference scale)  +{  +  x /= scale; +  y /= scale; +  z /= scale; +  return *this;  +} + + + +/////////////// +// Modifiers // +/////////////// +vec3d::value_type vec3d::dot(const vec3d& v) const  +{  +  return (x*v.x + y*v.y + z*v.z); +} + + +vec3d::value_type vec3d::squared_length(void) const  +{  +  return dot(*this);  +} + + +vec3d::value_type vec3d::length(void) const  +{  +  return sqrt(squared_length());  +} + + +vec3d::value_type vec3d::squared_distance(const vec3d& v) const  +{ +  return (*this - v).squared_length();  +} + + +vec3d::value_type vec3d::distance(const vec3d& v) const  +{  +  return sqrt(squared_distance(v));  +} + + +vec3d vec3d::cross(const vec3d& v) const +{ +  return vec3d(y*v.z - z*v.y, +	       z*v.x - x*v.z, +	       x*v.y - y*v.x);  +} + + +/////////////// +// Modifiers // +/////////////// +vec3d& vec3d::abs(void) +{ +  std::for_each(begin(), end(), [](reference val)  +  {  +    if(val < 0) val = -val;  +  }); +  return *this; +} + + +vec3d& vec3d::clamp(vec3d::value_type lower, vec3d::value_type upper) +{ +  std::for_each(begin(), end(), [&](reference val)  +  {  +    if(val < lower) val = lower;  +    else if(val > upper) val = upper; +  }); +  return *this; +} + + +vec3d& vec3d::normalize(void) +{  +  *this /= length(); +  return *this; +} + + +///////////////////// +// Private Methods // +///////////////////// +void vec3d::_assign(const vec3d& v) +{  +  x = v.x;   +  y = v.y;  +  z = v.z;  +} + + +void vec3d::_swap(vec3d& v)  +{ +  std::swap(x, v.x); +  std::swap(y, v.y); +  std::swap(z, v.z); +} |