summaryrefslogtreecommitdiff
path: root/hw6/src
diff options
context:
space:
mode:
author53hornet <53hornet@gmail.com>2019-02-02 23:33:15 -0500
committer53hornet <53hornet@gmail.com>2019-02-02 23:33:15 -0500
commitdb072ad4dc181eca5a1458656b130beb43f475bf (patch)
treea3c03c7f5497cb70503e2486662fa85cfb53415a /hw6/src
downloadcsci427-master.tar.xz
csci427-master.zip
Diffstat (limited to 'hw6/src')
-rw-r--r--hw6/src/areaLightsource.cpp87
-rw-r--r--hw6/src/bilinearTexture.cpp69
-rw-r--r--hw6/src/boundedCompound.cpp139
-rw-r--r--hw6/src/boundedPrimitive.cpp50
-rw-r--r--hw6/src/boundedTriangle.cpp67
-rw-r--r--hw6/src/boundedVolumeNode.cpp97
-rw-r--r--hw6/src/boundingBox.cpp195
-rw-r--r--hw6/src/brdfSample.cpp116
-rw-r--r--hw6/src/bumpMap.cpp48
-rw-r--r--hw6/src/bvh_intersector.cpp54
-rw-r--r--hw6/src/camera.cpp150
-rw-r--r--hw6/src/color.cpp233
-rw-r--r--hw6/src/compoundShader.cpp141
-rw-r--r--hw6/src/coordinateTransform.cpp67
-rw-r--r--hw6/src/diffuseBrdf.cpp111
-rw-r--r--hw6/src/diffuseReflectanceShader.cpp35
-rw-r--r--hw6/src/directionalLightsource.cpp47
-rw-r--r--hw6/src/environmentMap.cpp79
-rw-r--r--hw6/src/errorMessage.cpp34
-rw-r--r--hw6/src/image.cpp122
-rw-r--r--hw6/src/imageIO.cpp46
-rw-r--r--hw6/src/imageIO.pfm.cpp88
-rw-r--r--hw6/src/imageIO.ppm.cpp106
-rw-r--r--hw6/src/importOBJ.cpp152
-rw-r--r--hw6/src/intersectionPoint.cpp342
-rw-r--r--hw6/src/interval.cpp154
-rw-r--r--hw6/src/lightSample.cpp120
-rw-r--r--hw6/src/lightsourceGeometry.cpp54
-rw-r--r--hw6/src/lightsourceShader.cpp72
-rw-r--r--hw6/src/linear_intersector.cpp53
-rw-r--r--hw6/src/mat3d.cpp260
-rw-r--r--hw6/src/nearestTexture.cpp52
-rw-r--r--hw6/src/normalMap.cpp44
-rw-r--r--hw6/src/pathtracing.cpp175
-rw-r--r--hw6/src/phongBrdf.cpp133
-rw-r--r--hw6/src/phongReflectanceShader.cpp36
-rw-r--r--hw6/src/pointLightsource.cpp55
-rw-r--r--hw6/src/ray.cpp83
-rw-r--r--hw6/src/ray_util.cpp27
-rw-r--r--hw6/src/raycasting.cpp47
-rw-r--r--hw6/src/recursiveRaytracing.cpp94
-rw-r--r--hw6/src/reflectanceParameter.cpp140
-rw-r--r--hw6/src/reflectanceShader_base.cpp85
-rw-r--r--hw6/src/rotation3d.cpp47
-rw-r--r--hw6/src/rotationX3d.cpp42
-rw-r--r--hw6/src/rotationY3d.cpp42
-rw-r--r--hw6/src/rotationZ3d.cpp42
-rw-r--r--hw6/src/scale3d.cpp41
-rw-r--r--hw6/src/scene.cpp60
-rw-r--r--hw6/src/sceneGraphNode.cpp39
-rw-r--r--hw6/src/sceneIO.cpp93
-rw-r--r--hw6/src/sceneIO_basis.cpp100
-rw-r--r--hw6/src/sceneIO_core.cpp99
-rw-r--r--hw6/src/sceneIO_geometry.cpp187
-rw-r--r--hw6/src/sceneIO_light.cpp131
-rw-r--r--hw6/src/sceneIO_material.cpp243
-rw-r--r--hw6/src/sceneIO_texture.cpp55
-rw-r--r--hw6/src/sceneIO_transformation3d.cpp80
-rw-r--r--hw6/src/sceneIO_xml.cpp112
-rw-r--r--hw6/src/shaderProperties.cpp62
-rw-r--r--hw6/src/shadingFrameTransformation.cpp82
-rw-r--r--hw6/src/spotLightsource.cpp69
-rw-r--r--hw6/src/surfaceSample.cpp118
-rw-r--r--hw6/src/texture_base.cpp95
-rw-r--r--hw6/src/tinyxml2.cpp2794
-rw-r--r--hw6/src/transformation3d.cpp174
-rw-r--r--hw6/src/translation3d.cpp29
-rw-r--r--hw6/src/triangle.cpp465
-rw-r--r--hw6/src/triangleMesh.cpp57
-rw-r--r--hw6/src/util.cpp45
-rw-r--r--hw6/src/vec2d.cpp258
-rw-r--r--hw6/src/vec3d.cpp287
72 files changed, 10307 insertions, 0 deletions
diff --git a/hw6/src/areaLightsource.cpp b/hw6/src/areaLightsource.cpp
new file mode 100644
index 0000000..9005939
--- /dev/null
+++ b/hw6/src/areaLightsource.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 <cassert>
+#include "constants.h"
+#include "areaLightsource.h"
+
+//////////////////
+// Constructors //
+//////////////////
+areaLightsource::areaLightsource(const color& power, const std::shared_ptr<const boundedPrimitive>& geometry, const vec3d& attenuation)
+{
+ _power = power;
+ _geometry = geometry;
+ _attenuation = attenuation;
+
+ if(_geometry)
+ {
+ _center = _geometry->sample(0.5f, 0.5).position();
+ _area = _geometry->area();
+ }
+}
+
+
+/////////////
+// Methods //
+/////////////
+lightSample areaLightsource::intensityAt(const vec3d& point) const
+{
+ // use the 'center position' and emulate a point light source
+ vec3d direction = point - _center;
+ float distance = direction.length();
+ float attenuation = ((_attenuation[2]*distance + _attenuation[1])*distance + _attenuation[0]);
+
+ // Done.
+ return lightSample(direction, _power / attenuation, distance - 2.0f * EPSILON); // reduce distance to avoid self-intersection
+}
+
+
+lightSample areaLightsource::emittanceAt(const vec3d& point, float r1, float r2) const
+{
+ if(!_geometry) return lightSample();
+
+ // sample surface
+ surfaceSample sample = _geometry->sample(r1, r2);
+
+ // direction
+ vec3d direction = point - sample.position();
+ float distance = direction.length();
+
+ // handle backside case
+ if(direction.dot(sample.normal()) < 0.0f) return lightSample();
+
+ // Done.
+ return lightSample(direction,
+ _power / (2.0f * PI * _area),
+ distance - 2.0f * EPSILON, // reduce distance to avoid self-intersection
+ sample.pdf(),
+ direction.dot( sample.normal() ) / distance);
+}
+
+
+color areaLightsource::_emittance(const intersectionPoint& ip) const
+{
+ // check if back hit
+ // Note: ip.direction toward the intersection point
+ if(ip.normal().dot(ip.direction()) > 0.0f) return color();
+
+ // evaluate emittance (assume point lies on light source)
+ return _power / (2.0f * PI * _area);
+}
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void areaLightsource::_print(std::ostream& s) const
+{
+ if(_geometry)
+ s << "Area Lightsource: power=" << _power << ", attenuation=" << _attenuation << ", area=" << _area << " centered at: " << _center;
+ else s << "Invalid Area Lightsource";
+}
+
diff --git a/hw6/src/bilinearTexture.cpp b/hw6/src/bilinearTexture.cpp
new file mode 100644
index 0000000..f9697bf
--- /dev/null
+++ b/hw6/src/bilinearTexture.cpp
@@ -0,0 +1,69 @@
+/******************************************************************/
+/* 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 "bilinearTexture.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+bilinearTexture::bilinearTexture(bool repeat)
+ : texture_base(repeat)
+{
+ // Do nothing.
+}
+
+
+bilinearTexture::bilinearTexture(const bilinearTexture& src)
+ : texture_base(src)
+{
+ // Do nothing
+}
+
+
+///////////////
+// Operators //
+///////////////
+bilinearTexture& bilinearTexture::operator=(const bilinearTexture& src)
+{
+ _assign(src);
+ return *this;
+}
+
+
+image::value_type bilinearTexture::operator()(const vec2d& textureCoord) const
+{
+ float scaled_u = textureCoord.u*(width()-1);
+ float scaled_v = textureCoord.v*(height()-1);
+
+ // compute corners
+ signed int left = floor(scaled_u);
+ signed int right = ceil(scaled_u);
+ signed int top = floor(scaled_v);
+ signed int bottom = ceil(scaled_v);
+
+ // compute weights
+ float hw = fabs(right - scaled_u);
+ float vw = fabs(bottom - scaled_v);
+
+ // compute horizontal interpolated colors
+ color topColor = hw * _at(left, top) + (1.0f - hw) * _at(right, top);
+ color bottomColor = hw * _at(left, bottom) + (1.0f - hw) * _at(right, bottom);
+
+ // interpolate vertically
+ return vw * topColor + (1.0f - vw) * bottomColor;
+}
+
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void bilinearTexture::_print(std::ostream& s) const
+{
+ s << "Bilinear texture (" << this->width() << "x" << this->height() << ")";
+}
diff --git a/hw6/src/boundedCompound.cpp b/hw6/src/boundedCompound.cpp
new file mode 100644
index 0000000..c37ca0f
--- /dev/null
+++ b/hw6/src/boundedCompound.cpp
@@ -0,0 +1,139 @@
+/******************************************************************/
+/* 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);
+
+ // inverse transform ray
+ ray transformedRay = inverseTransform(r, _transform);
+
+ // pass intersection computation to _intersector
+ intersectionPoint ip = _intersector->intersect( transformedRay );
+
+ // if no shader, insert current shader
+ if(!ip.hasShader()) ip.setShader(_shader);
+
+ // transform the intersection point
+ ip.transform(_transform);
+
+ // Done.
+ return ip;
+}
+
+
+float boundedCompound::area(void) const
+{
+ float total_area = 0.0f;
+ for_each(compounds().begin(), compounds().end(), [&](const std::shared_ptr<const boundedPrimitive>& prim)
+ {
+ total_area += prim->area();
+ });
+
+ // Done.
+ return total_area;
+}
+
+
+surfaceSample boundedCompound::sample(float r1, float r2) const
+{
+ float total_area = area();
+ auto primItr = compounds().begin();
+
+ // sample compounds (proportional to area)
+ float prim_area = (*primItr)->area();
+ float residual = r1*total_area;
+ while(residual > prim_area && std::next(primItr) != compounds().end())
+ {
+ residual -= prim_area;
+ primItr++;
+ prim_area = (*primItr)->area();
+ }
+
+ // rescale the random variable
+ r1 = std::min(residual / prim_area, 1.0f);
+
+ // sample point in triangle (and adjust pdf to include the above selection)
+ surfaceSample sample = (*primItr)->sample(r1, r2) * (prim_area / total_area);
+
+ // apply transformation
+ sample.transform(_transform);
+
+ // Done.
+ return sample;
+}
+
+
+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/hw6/src/boundedPrimitive.cpp b/hw6/src/boundedPrimitive.cpp
new file mode 100644
index 0000000..d328b16
--- /dev/null
+++ b/hw6/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/hw6/src/boundedTriangle.cpp b/hw6/src/boundedTriangle.cpp
new file mode 100644
index 0000000..1119796
--- /dev/null
+++ b/hw6/src/boundedTriangle.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 "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));
+}
+
+
+float boundedTriangle::area(void) const
+{
+ return _triangle.area();
+}
+
+
+surfaceSample boundedTriangle::sample(float r1, float r2) const
+{
+ float pdf;
+ vec3d bc, position = _triangle.sample(r1, r2, bc, pdf);
+ return surfaceSample(position, _triangle.normal(bc), pdf);
+}
+
+
+void boundedTriangle::_print(std::ostream& s) const
+{
+ s << "boundedTriangle (" << _bb << ", " << _triangle << ")";
+}
diff --git a/hw6/src/boundedVolumeNode.cpp b/hw6/src/boundedVolumeNode.cpp
new file mode 100644
index 0000000..fc790b5
--- /dev/null
+++ b/hw6/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"
+
+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()
+{
+ assert(std::distance(start, end) > 1);
+
+ // compute the bounding box
+ for_each(start, end, [&](const std::shared_ptr<const boundedPrimitive>& prim)
+ {
+ _bb += prim->boundingbox();
+ });
+
+ // select random splitting dimension & pivot
+ unsigned int split = random_int(2);
+ float pivot = _bb.center()[split];
+
+ // partition
+ auto middle = std::partition(start, end, [&](const std::shared_ptr<const boundedPrimitive>& prim)
+ {
+ return prim->boundingbox().center()[split] < pivot;
+ });
+
+ // special case: all centers are the same.
+ if(start == middle) middle++;
+
+ // create _left (recurse if more than one primitive)
+ if(std::distance(start, middle) == 1)
+ _left = *start;
+ else
+ _left = std::shared_ptr<const boundedPrimitive>(new boundedVolumeNode(start, middle));
+
+ // create _right (recurse if more than one primitive)
+ if(std::distance(middle, end) == 1)
+ _right = *middle;
+ else
+ _right = std::shared_ptr<const boundedPrimitive>(new boundedVolumeNode(middle, end));
+
+ // Done.
+}
+
+intersectionPoint boundedVolumeNode::intersect(const ray& r) const
+{
+ intersectionPoint leftIp, rightIp;
+
+ // check left child
+ if(_left && _left->hitBoundingBox(r))
+ leftIp = _left->intersect(r);
+
+ // check right child
+ if(_right && _right->hitBoundingBox(r))
+ rightIp = _right->intersect(r);
+
+ // return closest
+ return std::min(leftIp, rightIp);
+}
+
+
+float boundedVolumeNode::area(void) const
+{
+ float total_area = 0.0f;
+ if(_left) total_area += _left->area();
+ if(_right) total_area += _right->area();
+ return total_area;
+}
+
+
+surfaceSample boundedVolumeNode::sample(float r1, float r2) const
+{
+ if(_left && !_right) return _left->sample(r1, r2);
+ else if(_right && !_left) return _right->sample(r1, r2);
+ else
+ {
+ float area_left = _left->area();
+ float area_right = _right->area();
+ float prob_left = area_left / (area_left + area_right);
+ float prob_right = 1.0f - prob_left;
+
+ if(r1 <= prob_left)
+ return _left->sample(r1 / prob_left, r2) * prob_left;
+ else
+ return _right->sample((r1 - prob_left) / prob_right, r2) * prob_right;
+ }
+}
diff --git a/hw6/src/boundingBox.cpp b/hw6/src/boundingBox.cpp
new file mode 100644
index 0000000..96b2261
--- /dev/null
+++ b/hw6/src/boundingBox.cpp
@@ -0,0 +1,195 @@
+/******************************************************************/
+/* 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 );
+}
+
+
+vec3d boundingBox::size(void) const
+{
+ vec3d s = (_rbt - _lfb);
+ if(s.x < 0 || s.y < 0 || s.z < 0) return vec3d();
+ else return s;
+}
+
+
+//////////////
+// 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/hw6/src/brdfSample.cpp b/hw6/src/brdfSample.cpp
new file mode 100644
index 0000000..90acfe4
--- /dev/null
+++ b/hw6/src/brdfSample.cpp
@@ -0,0 +1,116 @@
+/******************************************************************/
+/* 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 "brdfSample.h"
+
+//////////////////
+// Constructors //
+//////////////////
+brdfSample::brdfSample(void)
+{
+ _out = vec3d(0.0f, 0.0f, 1.0f);
+ _pdf = 0.0f;
+ _reflectance = color(0.0f);
+}
+
+brdfSample::brdfSample(const vec3d& out, const float pdf, const color& reflectance)
+{
+ _out = out;
+ _pdf = std::max(pdf, 0.0f);
+ _reflectance = reflectance;
+}
+
+
+brdfSample::brdfSample(const brdfSample& bs)
+{
+ _out = bs._out;
+ _pdf = bs._pdf;
+ _reflectance = bs._reflectance;
+}
+
+
+///////////////
+// Operators //
+///////////////
+brdfSample& brdfSample::operator=(const brdfSample& bs)
+{
+ _assign(bs);
+ return *this;
+}
+
+
+brdfSample brdfSample::operator*(float pdf) const
+{
+ return brdfSample(_out, _pdf * pdf, _reflectance);
+}
+
+
+brdfSample& brdfSample::operator*=(float pdf)
+{
+ _pdf *= pdf;
+ return *this;
+}
+
+
+////////////////
+// Inspectors //
+////////////////
+const vec3d& brdfSample::exitantDirection(void) const
+{
+ return _out;
+}
+
+
+float brdfSample::pdf(void) const
+{
+ return _pdf;
+}
+
+
+const color& brdfSample::reflectance(void) const
+{
+ return _reflectance;
+}
+
+
+/////////////
+// Methods //
+/////////////
+brdfSample& brdfSample::transform(const transformation3d& t)
+{
+ _out = t.transformDirection(_out);
+ return *this;
+}
+
+
+brdfSample& brdfSample::inverseTransform(const transformation3d& t)
+{
+ _out = t.inverseTransformDirection(_out);
+ return *this;
+}
+
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void brdfSample::_swap(brdfSample& bs)
+{
+ swap(_out, bs._out);
+ std::swap(_pdf, bs._pdf);
+ swap(_reflectance, bs._reflectance);
+}
+
+
+void brdfSample::_assign(const brdfSample& bs)
+{
+ _out = bs._out;
+ _pdf = bs._pdf;
+ _reflectance = bs._reflectance;
+}
+
diff --git a/hw6/src/bumpMap.cpp b/hw6/src/bumpMap.cpp
new file mode 100644
index 0000000..ecd6f49
--- /dev/null
+++ b/hw6/src/bumpMap.cpp
@@ -0,0 +1,48 @@
+/******************************************************************/
+/* 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 "bumpMap.h"
+#include "coordinateTransform.h"
+
+//////////////////
+// Constructors //
+//////////////////
+bumpMap::bumpMap(const std::shared_ptr<const texture_base>& map, float scale, unsigned int channel, const std::shared_ptr<const shader_base>& shader)
+ : shadingFrameTransformation(shader)
+{
+ _map = map;
+ _scale = scale;
+ _channel = std::max(channel, 2u);
+}
+
+/////////////
+// Methods //
+/////////////
+transformation3d bumpMap::_transformation(const vec2d& textureCoord) const
+{
+ // get derivative in X and Y direction
+ assert(_map);
+ float dx = (*_map)(textureCoord)[_channel] - (*_map)(textureCoord + vec2d(1.0f/_map->width(), 0.0f))[_channel];
+ float dy = (*_map)(textureCoord)[_channel] - (*_map)(textureCoord + vec2d(0.0f, 1.0f/_map->height()))[_channel];
+
+ // get desired local normal = (1.0, 0.0, dx) cross (0.0, 1.0, dy)
+ vec3d normal(-dx, -dy, 1.0f / _scale);
+ normal.normalize();
+
+ // create coordinate system. Keep U aligned as close as possible to the X axis.
+ return coordinateTransformation(normal, vec3d(1.0f, 0.0, 0.0));
+}
+
+
+void bumpMap::_print(std::ostream& s) const
+{
+ s << "bumpMap (" << _scale << " x " << *_map << "[" << _channel << "]) -> {" << *_shader << "}";
+}
+
diff --git a/hw6/src/bvh_intersector.cpp b/hw6/src/bvh_intersector.cpp
new file mode 100644
index 0000000..2eedb2f
--- /dev/null
+++ b/hw6/src/bvh_intersector.cpp
@@ -0,0 +1,54 @@
+/******************************************************************/
+/* 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/hw6/src/camera.cpp b/hw6/src/camera.cpp
new file mode 100644
index 0000000..3393dc9
--- /dev/null
+++ b/hw6/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/hw6/src/color.cpp b/hw6/src/color.cpp
new file mode 100644
index 0000000..b2b720f
--- /dev/null
+++ b/hw6/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/hw6/src/compoundShader.cpp b/hw6/src/compoundShader.cpp
new file mode 100644
index 0000000..6456fdc
--- /dev/null
+++ b/hw6/src/compoundShader.cpp
@@ -0,0 +1,141 @@
+/******************************************************************/
+/* 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"
+#include "random_number.h"
+#include <iostream> // DEBUG
+
+//////////////////
+// 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;
+}
+
+
+color compoundShader::reflectance(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->reflectance(ip, light_dir);
+ });
+
+ // Done.
+ return result;
+}
+
+
+brdfSample compoundShader::sample(const intersectionPoint& ip, float r1, float r2) const
+{
+ // santiy check
+ if(_compound.empty()) return brdfSample();
+
+ // initialize
+ auto currentCompound = _compound.begin();
+ float pdf = 1.0f;
+
+ // HW6: implement importance sampling of BRDF, where you select
+ // a BRDF propertional to their relative reflectivity.
+ // For example, if the compound shader has two BRDFs with
+ // an reflectivity of 20% and 40%, then the likelihood that
+ // the second BRDF is sampled is twice that of the first BRDF.
+ // Practically, you need to point the 'currentCompound' to the BRDF
+ // you want sample, and set 'pdf' to the PDF of the selection.
+ // Modifies: nothing
+ // Returns: brdfSample
+
+ float cdf = 0.0f;
+ float choice = random_float(1.0f) * reflectivity(ip);
+
+ // compute cdf until it exceeds choice probability and select that compound
+ while (currentCompound < _compound.end() && choice >= cdf) {
+ pdf = (**currentCompound).reflectivity(ip);
+ cdf += pdf;
+ currentCompound++;
+ }
+
+ // move currentCompound back to selection
+ currentCompound--;
+
+ // generate sample (do not remove -- this line will produce the correct
+ // result if currentCompound and pdf are set correctly).
+ return (*currentCompound)->sample(ip, r1, r2) * pdf;
+}
+
+
+float compoundShader::reflectivity(const intersectionPoint& ip) const
+{
+ // return sum of reflectivity
+ float total_reflectivity = 0.0f;
+ for_each(_compound.begin(), _compound.end(), [&](const std::shared_ptr<const shader_base>& shader)
+ {
+ total_reflectivity += shader->reflectivity(ip);
+ });
+
+ // Done.
+ return total_reflectivity;
+}
+
+
+color compoundShader::emittance(const intersectionPoint& ip) const
+{
+ // return total sum of emittance
+ color total_emittance;
+ for_each(_compound.begin(), _compound.end(), [&](const std::shared_ptr<const shader_base>& shader)
+ {
+ total_emittance += shader->emittance(ip);
+ });
+
+ // Done.
+ return total_emittance;
+}
+
+
+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/hw6/src/coordinateTransform.cpp b/hw6/src/coordinateTransform.cpp
new file mode 100644
index 0000000..29e19f4
--- /dev/null
+++ b/hw6/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/hw6/src/diffuseBrdf.cpp b/hw6/src/diffuseBrdf.cpp
new file mode 100644
index 0000000..2256f73
--- /dev/null
+++ b/hw6/src/diffuseBrdf.cpp
@@ -0,0 +1,111 @@
+/******************************************************************/
+/* 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;
+}
+
+
+color diffuseBrdf::reflectance(const vec3d& in, const vec3d& out) const
+{
+ if(in.z < 0.0f || out.z < 0.0f) return color(0.0f);
+ return _albedo / PI;
+}
+
+
+brdfSample diffuseBrdf::sample(const vec3d& in, float r1, float r2) const
+{
+ // check if below surface.
+ if(in.z < EPSILON) return brdfSample();
+
+ // sample hemisphere proportional to the cosine
+ vec3d out( cos(2.0f * PI * r1) * sqrt(std::max(1.0f - r2, 0.0f)),
+ sin(2.0f * PI * r1) * sqrt(std::max(1.0f - r2, 0.0f)),
+ sqrt(r2) );
+
+ // set pdf
+ float pdf = out.z / PI;
+
+ // Done.
+ return brdfSample(out,
+ out.z / PI, // PDF proportional to cosine
+ _albedo / PI // reflectance
+ );
+}
+
+
+bool diffuseBrdf::isSpecular(void) const
+{
+ return false;
+}
+
+
+bool diffuseBrdf::isDiffuse(void) const
+{
+ return true;
+}
+
+
+float diffuseBrdf::reflectivity(void) const
+{
+ return (_albedo.r + _albedo.g + _albedo.b) / 3.0f;
+}
+
+
+/////////////////////
+// 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/hw6/src/diffuseReflectanceShader.cpp b/hw6/src/diffuseReflectanceShader.cpp
new file mode 100644
index 0000000..4d2d2a4
--- /dev/null
+++ b/hw6/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/hw6/src/directionalLightsource.cpp b/hw6/src/directionalLightsource.cpp
new file mode 100644
index 0000000..7703eb1
--- /dev/null
+++ b/hw6/src/directionalLightsource.cpp
@@ -0,0 +1,47 @@
+/******************************************************************/
+/* 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);
+}
+
+
+lightSample directionalLightsource::emittanceAt(const vec3d& point, float r1, float r2) const
+{
+ // Set emittance to _power * distance^2 to compensate for squared distance fall off.
+ // Distance is set to large (to ensure everything can occlude).
+ // Foreshortening == 1
+ // PDF == 1 (no selection).
+ return lightSample(_direction, _power * LARGE * LARGE, LARGE, 1.0f, 1.0f);
+}
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void directionalLightsource::_print(std::ostream& s) const
+{
+ s << "Directional Lightsource: direction=" << _direction << ", power=" << _power;
+}
diff --git a/hw6/src/environmentMap.cpp b/hw6/src/environmentMap.cpp
new file mode 100644
index 0000000..896d78e
--- /dev/null
+++ b/hw6/src/environmentMap.cpp
@@ -0,0 +1,79 @@
+/******************************************************************/
+/* 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 "environmentMap.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+environmentMap::environmentMap(const std::shared_ptr<const texture_base>& map, const transformation3d& transform)
+{
+ _map = map;
+ _transform = transform;
+}
+
+
+environmentMap::environmentMap(const environmentMap& src)
+{
+ _map = src._map;
+ _transform = src._transform;
+}
+
+
+///////////////
+// Operators //
+///////////////
+environmentMap environmentMap::operator=(const environmentMap& src)
+{
+ _assign(src);
+ return *this;
+}
+
+
+color environmentMap::operator()(const vec3d& direction) const
+{
+ // normalize & apply transformation
+ vec3d dir = _transform.inverseTransformDirection(normalize(direction));
+
+ // compute spherical coordinates
+ float theta = acos(dir.z);
+ float phi = atan2(dir.y, dir.x);
+
+ if(phi < 0.0f) phi += 2.0f * PI;
+
+ // get texel
+ return _map->operator()(vec2d(0.5f * phi / PI, theta / PI));
+}
+
+
+///////////////////////
+// Protected Methods //
+///////////////////////
+void environmentMap::_assign(const environmentMap& src)
+{
+ if(&src == this) return;
+
+ _map = src._map;
+ _transform = src._transform;
+}
+
+
+void environmentMap::_swap(environmentMap& swp)
+{
+ std::swap(_map, swp._map);
+ swap(_transform, swp._transform);
+}
+
+
+void environmentMap::_print(std::ostream& s) const
+{
+ s << "Environment Map (" << *_map << ", " << _transform << ")";
+}
diff --git a/hw6/src/errorMessage.cpp b/hw6/src/errorMessage.cpp
new file mode 100644
index 0000000..3eddb6a
--- /dev/null
+++ b/hw6/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/hw6/src/image.cpp b/hw6/src/image.cpp
new file mode 100644
index 0000000..59434cc
--- /dev/null
+++ b/hw6/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/hw6/src/imageIO.cpp b/hw6/src/imageIO.cpp
new file mode 100644
index 0000000..cee716b
--- /dev/null
+++ b/hw6/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/hw6/src/imageIO.pfm.cpp b/hw6/src/imageIO.pfm.cpp
new file mode 100644
index 0000000..30d1c23
--- /dev/null
+++ b/hw6/src/imageIO.pfm.cpp
@@ -0,0 +1,88 @@
+/******************************************************************/
+/* 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(), std::ifstream::binary);
+ 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/hw6/src/imageIO.ppm.cpp b/hw6/src/imageIO.ppm.cpp
new file mode 100644
index 0000000..00b3eee
--- /dev/null
+++ b/hw6/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(), std::ifstream::binary);
+ 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/hw6/src/importOBJ.cpp b/hw6/src/importOBJ.cpp
new file mode 100644
index 0000000..53354be
--- /dev/null
+++ b/hw6/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 skipLine(std::ifstream& ifs)
+{
+ unsigned char c = ifs.get();
+ while(ifs.good() && c != '\r' && c != '\n') c = ifs.get();
+ while(ifs.good() && (c == '\r' || c == '\n')) c = ifs.get();
+ ifs.unget();
+}
+
+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::in | std::ifstream::binary);
+ 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();
+
+ // VERTEX DATA:
+ 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; c = ifs.get(); }
+ }
+
+ // sanity check
+ assert(vt != 0 && vn != 0 && vi != 0);
+
+ // store in polygon
+ if(vt != -1) { assert(vt-1 < textureCoord_list->size()); tidx.push_back(vt-1); }
+ if(vn != -1) { assert(vn-1 < normal_list->size()); nidx.push_back(vn-1); }
+ if(vi != -1) { assert(vi-1 < vertex_list->size()); vidx.push_back(vi-1); }
+
+ // eat spaces and end-of-lines
+ ifs.unget();
+ c = ifs.get();
+ while(ifs.good() && (c == ' ' ||c == '\r' || c == '\n'))
+ {
+ if(c == '\r' || c == '\n') done = true;
+ c = ifs.get();
+ }
+ 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) );
+ }
+ }
+ }
+ else { ifs.unget(); skipLine(ifs); }
+ }
+
+ // Done.
+}
diff --git a/hw6/src/intersectionPoint.cpp b/hw6/src/intersectionPoint.cpp
new file mode 100644
index 0000000..5b1d15e
--- /dev/null
+++ b/hw6/src/intersectionPoint.cpp
@@ -0,0 +1,342 @@
+/******************************************************************/
+/* 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();
+}
+
+
+float intersectionPoint::reflectivity(void) const
+{
+ assert(hasShader());
+
+ // get reflectivity
+ float albedo = _shader->reflectivity(*this);
+
+ // cannot reflect more than 100%
+ assert(albedo <= 1.0f);
+
+ // Done.
+ return albedo;
+}
+
+
+color intersectionPoint::reflectance(const vec3d& out) const
+{
+ assert(hasShader());
+ return _shader->reflectance(*this, out);
+}
+
+
+brdfSample intersectionPoint::sample(float r1, float r2) const
+{
+ assert(hasShader());
+ return _shader->sample(*this, r1, r2);
+}
+
+
+color intersectionPoint::emittance(void) const
+{
+ assert(hasShader());
+ return _shader->emittance(*this);
+}
+
+
+/////////////////////
+// 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/hw6/src/interval.cpp b/hw6/src/interval.cpp
new file mode 100644
index 0000000..4323cbd
--- /dev/null
+++ b/hw6/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/hw6/src/lightSample.cpp b/hw6/src/lightSample.cpp
new file mode 100644
index 0000000..cfe4ee5
--- /dev/null
+++ b/hw6/src/lightSample.cpp
@@ -0,0 +1,120 @@
+/******************************************************************/
+/* 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/hw6/src/lightsourceGeometry.cpp b/hw6/src/lightsourceGeometry.cpp
new file mode 100644
index 0000000..3f028aa
--- /dev/null
+++ b/hw6/src/lightsourceGeometry.cpp
@@ -0,0 +1,54 @@
+/******************************************************************/
+/* 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 "lightsourceShader.h"
+#include "lightsourceGeometry.h"
+
+/////////////////
+// Constructor //
+/////////////////
+lightsourceGeometry::lightsourceGeometry(void)
+ : boundedCompound()
+{
+ // Nothing.
+}
+
+
+lightsourceGeometry::lightsourceGeometry(const std::shared_ptr<const boundedPrimitive>& geometry, const std::shared_ptr<const lightsource_base>& ls, const std::shared_ptr<const shader_base>& material)
+ : boundedCompound()
+{
+ assert(geometry && ls);
+ _geometry.push_back(geometry);
+ _shader = std::shared_ptr<const shader_base>(new lightsourceShader(ls, material));
+
+ // Done.
+ initializeBoundingBox();
+}
+
+
+/////////////
+// Methods //
+/////////////
+bool lightsourceGeometry::hasShader(void) const
+{
+ // always has a shader by construction
+ return true;
+}
+
+
+const std::vector<std::shared_ptr<const boundedPrimitive>>& lightsourceGeometry::compounds(void) const
+{
+ return _geometry;
+}
+
+
+void lightsourceGeometry::_print(std::ostream& s) const
+{
+ s << "lightsourceGeometry (" << _bb << ") -> [" << *_shader << "]";
+}
diff --git a/hw6/src/lightsourceShader.cpp b/hw6/src/lightsourceShader.cpp
new file mode 100644
index 0000000..f399f8e
--- /dev/null
+++ b/hw6/src/lightsourceShader.cpp
@@ -0,0 +1,72 @@
+/******************************************************************/
+/* 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 "lightsourceShader.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+lightsourceShader::lightsourceShader(const std::shared_ptr<const lightsource_base>& lightsource, const std::shared_ptr<const shader_base>& reflectanceShader)
+{
+ assert(lightsource);
+ _lightsource = lightsource;
+ _reflectanceShader = reflectanceShader;
+}
+
+
+/////////////
+// Methods //
+/////////////
+color lightsourceShader::shade(const intersectionPoint& ip, const vec3d& light_dir) const
+{
+ color result = _lightsource->_emittance(ip);
+ if(_reflectanceShader) result += _reflectanceShader->shade(ip, light_dir);
+ return result;
+}
+
+
+color lightsourceShader::reflectance(const intersectionPoint& ip, const vec3d& light_dir) const
+{
+ return (_reflectanceShader) ? _reflectanceShader->reflectance(ip, light_dir) : color();
+}
+
+
+brdfSample lightsourceShader::sample(const intersectionPoint& ip, float r1, float r2) const
+{
+ return (_reflectanceShader) ? _reflectanceShader->sample(ip, r1, r2) : brdfSample();
+}
+
+
+float lightsourceShader::reflectivity(const intersectionPoint& ip) const
+{
+ return (_reflectanceShader) ? _reflectanceShader->reflectivity(ip) : 0.0f;
+}
+
+
+shaderProperties lightsourceShader::properties(const intersectionPoint& ip) const
+{
+ return (_reflectanceShader) ? _reflectanceShader->properties(ip) : shaderProperties(false, false);
+}
+
+
+color lightsourceShader::emittance(const intersectionPoint& ip) const
+{
+ return _lightsource->_emittance(ip);
+}
+
+///////////////////////
+// Protected Methods //
+///////////////////////
+void lightsourceShader::_print(std::ostream& s) const
+{
+ s << "LightsourceShader: light=" << *_lightsource << ", reflectance shader=";
+ if(_reflectanceShader) s << *_reflectanceShader;
+ else s << "None.";
+}
diff --git a/hw6/src/linear_intersector.cpp b/hw6/src/linear_intersector.cpp
new file mode 100644
index 0000000..c7cb37e
--- /dev/null
+++ b/hw6/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/hw6/src/mat3d.cpp b/hw6/src/mat3d.cpp
new file mode 100644
index 0000000..7674777
--- /dev/null
+++ b/hw6/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/hw6/src/nearestTexture.cpp b/hw6/src/nearestTexture.cpp
new file mode 100644
index 0000000..258ebd8
--- /dev/null
+++ b/hw6/src/nearestTexture.cpp
@@ -0,0 +1,52 @@
+/******************************************************************/
+/* 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 "nearestTexture.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+nearestTexture::nearestTexture(bool repeat)
+ : texture_base(repeat)
+{
+ // Do nothing.
+}
+
+
+nearestTexture::nearestTexture(const nearestTexture& src)
+ : texture_base(src)
+{
+ // Do nothing
+}
+
+
+///////////////
+// Operators //
+///////////////
+nearestTexture& nearestTexture::operator=(const nearestTexture& src)
+{
+ _assign(src);
+ return *this;
+}
+
+
+image::value_type nearestTexture::operator()(const vec2d& textureCoord) const
+{
+ return _at(floor(textureCoord.u * width()),
+ floor(textureCoord.v * height()));
+}
+
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void nearestTexture::_print(std::ostream& s) const
+{
+ s << "Nearest texture (" << this->width() << "x" << this->height() << ")";
+}
diff --git a/hw6/src/normalMap.cpp b/hw6/src/normalMap.cpp
new file mode 100644
index 0000000..9cce3c3
--- /dev/null
+++ b/hw6/src/normalMap.cpp
@@ -0,0 +1,44 @@
+/******************************************************************/
+/* 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 "normalMap.h"
+#include "coordinateTransform.h"
+
+//////////////////
+// Constructors //
+//////////////////
+normalMap::normalMap(const std::shared_ptr<const texture_base>& map, const std::shared_ptr<const shader_base>& shader)
+ : shadingFrameTransformation(shader)
+{
+ _map = map;
+}
+
+/////////////
+// Methods //
+/////////////
+transformation3d normalMap::_transformation(const vec2d& textureCoord) const
+{
+ // get texel
+ assert(_map);
+ color texel = (*_map)(textureCoord);
+
+ // get desired local normal
+ vec3d normal(texel.r, texel.g, texel.b);
+ normal.normalize();
+
+ // create coordinate system. Keep U aligned as close as possible to the X axis.
+ return coordinateTransformation(normal, vec3d(1.0f, 0.0, 0.0));
+}
+
+
+void normalMap::_print(std::ostream& s) const
+{
+ s << "normalMap (" << *_map << ") -> {" << *_shader << "}";
+}
+
diff --git a/hw6/src/pathtracing.cpp b/hw6/src/pathtracing.cpp
new file mode 100644
index 0000000..dc9dda1
--- /dev/null
+++ b/hw6/src/pathtracing.cpp
@@ -0,0 +1,175 @@
+/******************************************************************/
+/* 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_util.h"
+#include "pathtracing.h"
+#include "random_number.h"
+
+//////////////////
+// Constructors //
+//////////////////
+pathtracing::pathtracing(unsigned int samplesPerPixel, bool directOnly)
+{
+ _samples = samplesPerPixel;
+ _directOnly = directOnly;
+}
+
+
+/////////////
+// Methods //
+/////////////
+image pathtracing::render(const scene& s) const
+{
+ image result(s.getCamera().width(), s.getCamera().height());
+
+ // for every pixel
+ for(image::size_type y=0; y < result.height(); y++)
+ for(image::size_type x=0; x < result.width(); x++)
+ {
+ color pixelRadiance(0.0f, 0.0f, 0.0f);
+
+ // for every sample
+ for(unsigned int sample = 0; sample < _samples; sample++)
+ {
+ color sampleRadiance(0.0f, 0.0f, 0.0f);
+
+ // generate random camera ray through pixel:
+ float px = (float)(x) + random_float(); float py = (float)(y) + random_float();
+ ray r = s.getCamera()(px, py);
+
+ // get initial intersection point
+ intersectionPoint ip = s.intersect(r);
+
+ // get radiance from directly visible *area* light sources
+ if(ip.isHit())
+ sampleRadiance += ip.emittance();
+
+ // get scene radiance
+ sampleRadiance += radiance(s, r, ip);
+
+ // store
+ pixelRadiance += sampleRadiance / (float)(_samples);
+ }
+
+ // store in image
+ result(x,y) = pixelRadiance;
+ }
+
+ // Done.
+ return result;
+}
+
+
+/////////////////////
+// Private Methods //
+/////////////////////
+color pathtracing::radiance(const scene& s, const ray& r, const intersectionPoint& ip) const
+{
+ color radiance(0.0f, 0.0f, 0.0f);
+
+ // if scene hit
+ if(ip.isHit())
+ {
+ // solve rendering equation
+ radiance = directRadiance(s, ip);
+ if(!_directOnly) radiance += indirectRadiance(s, ip);
+ }
+
+ // if not hit
+ else if(s.hasEnvironmentMap())
+ {
+ radiance += s.evaluateEnvironmentMap(r.direction());
+ }
+
+ // Done.
+ return radiance;
+}
+
+
+color pathtracing::directRadiance(const scene& s, const intersectionPoint& ip) const
+{
+ // HW6: implement this. Compute the direct radiance incident
+ // at the intersection point 'ip' according to the
+ // rendering equation.
+ // Modifies: nothing
+ // Returns: computed direct radiance.
+
+ color result = color(0.0f);
+
+ // for every light source,
+ for (int l = 0; l < s.numberOfLightsources(); l++) {
+ // sample the light source
+ lightSample ls = s.getLightsource(l).emittanceAt(ip.position(), random_float(), random_float());
+
+ // check if occluded
+ ray shadowRay = createRay(ip, ls.directionToLight());
+ intersectionPoint shadowIp = s.intersect(shadowRay);
+
+ // if not occluded,
+ if (ls < shadowIp && ls.distance() > sqrt(EPSILON)) {
+ float cosine = ls.directionToLight().dot(ip.normal()) / ls.distance();
+
+ // if no division by zero,
+ if (ls.pdf() > EPSILON && cosine >= 0) {
+ // compute radiance via rendering equation for direct illumination
+ result += (ip.reflectance(ls.directionToLight()) // fr(x, -psi<->theta) (brdf)
+ * ls.emittance() // Le(y->psi)
+ * cosine // cos(nx, -psi)
+ * ls.foreshortening() // cos(ny, psi)
+ / ls.distance() // r^2 (other r in cosine calculation)
+ / ls.pdf() // pdf (monte-carlo integration probability)
+ );
+ }
+
+ }
+
+ }
+
+ return result;
+}
+
+
+color pathtracing::indirectRadiance(const scene& s, const intersectionPoint& ip) const
+{
+ // HW6: implement this. Computed the indirect radiance incident
+ // at the intersection point 'ip' according to the rendering
+ // equation.
+ // Modifies: nothing
+ // Returns: computed indirect radiance.
+
+ color result = color(0.0f);
+
+ // if absorption falls above a random value, end recursion (russian roulette)
+ if (1 - ip.reflectivity() > random_float(1.0f)) { return result; }
+
+ // sample the surface
+ brdfSample brdf = ip.sample(random_float(), random_float());
+
+ // create exitant ray
+ ray exitantRay = createRay(ip, brdf.exitantDirection());
+
+ // recursively compute radiance from reflected point
+ color reflectedColor = radiance(s, exitantRay, s.intersect(exitantRay));
+
+ // compute cosine
+ float cosine = brdf.exitantDirection().dot(ip.normal());
+
+ // if no division by zero via pdf and cosines are not negative,
+ if (brdf.pdf() > EPSILON && cosine > EPSILON) {
+ // compute radiance via rendering equation for indirect illumination
+ result += (brdf.reflectance() // fr(x, -psi<->theta) (brdf)
+ * reflectedColor // Le(y->psi)
+ * cosine // cos(nx, -psi)
+ / brdf.pdf() // pdf (monte-carlo integration probability)
+ / ip.reflectivity() // russian roulette
+ );
+
+ }
+
+ return result;
+}
diff --git a/hw6/src/phongBrdf.cpp b/hw6/src/phongBrdf.cpp
new file mode 100644
index 0000000..939f3a1
--- /dev/null
+++ b/hw6/src/phongBrdf.cpp
@@ -0,0 +1,133 @@
+/******************************************************************/
+/* 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"
+#include "coordinateTransform.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);
+}
+
+
+color phongBrdf::reflectance(const vec3d& in, const vec3d& out) const
+{
+ // use shade evaluation times a normalization factor to ensure
+ // conservation of enegery.
+ return shade(in, out) * (_sharpness + 2.0f) / (2.0f * PI);
+}
+
+
+brdfSample phongBrdf::sample(const vec3d& in, float r1, float r2) const
+{
+ // sanity check
+ if(in.z < 0.0f) return brdfSample();
+
+ // compute reflected direction
+ vec3d reflected(-in.x, -in.y, in.z);
+
+ // sample hemisphere around reflected direction.
+ vec3d out( sqrt( 1.0f - pow(r1, 2.0f / (_sharpness + 1.0f)) ) * cos(2.0f * PI * r2),
+ sqrt( 1.0f - pow(r1, 2.0f / (_sharpness + 1.0f)) ) * sin(2.0f * PI * r2),
+ pow(r1, 1.0f / (_sharpness + 1.0f)) );
+
+ // transform to global coord
+ coordinateTransformation trans(reflected);
+ out = trans.transformDirection(out);
+
+ // compute pdf
+ float pdf = 0.0f;
+ if(out.z >= 0.0f)
+ pdf = (_sharpness + 1.0f) / (2.0f * PI) * pow(reflected.dot(out), _sharpness);
+
+ // Done.
+ return brdfSample(out, pdf, this->reflectance(in, out));
+}
+
+
+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();
+}
+
+
+float phongBrdf::reflectivity(void) const
+{
+ // Done.
+ return (_albedo.r + _albedo.g + _albedo.b) / 3.0f;
+}
+
+
+/////////////////////
+// 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/hw6/src/phongReflectanceShader.cpp b/hw6/src/phongReflectanceShader.cpp
new file mode 100644
index 0000000..c5ac57c
--- /dev/null
+++ b/hw6/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/hw6/src/pointLightsource.cpp b/hw6/src/pointLightsource.cpp
new file mode 100644
index 0000000..4d4b85d
--- /dev/null
+++ b/hw6/src/pointLightsource.cpp
@@ -0,0 +1,55 @@
+/******************************************************************/
+/* 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 "pointLightsource.h"
+
+//////////////////
+// Constructors //
+//////////////////
+pointLightsource::pointLightsource(const vec3d& position, const color& power, const vec3d& attenuation)
+{
+ _position = position;
+ _power = power;
+ _attenuation = attenuation;
+}
+
+
+/////////////
+// Methods //
+/////////////
+lightSample pointLightsource::intensityAt(const vec3d& point) const
+{
+ vec3d direction = point - _position;
+ float distance = direction.length();
+ float attenuation = ((_attenuation[2]*distance + _attenuation[1])*distance + _attenuation[0]);
+
+ // Done.
+ return lightSample(direction, _power / attenuation, distance);
+}
+
+
+lightSample pointLightsource::emittanceAt(const vec3d& point, float r1, float r2) const
+{
+ vec3d direction = point - _position;
+ float distance = direction.length();
+
+ // emittance == _power / area sphere
+ // pdf = 1.0 (there is no selection)
+ // foreshortning = 1.0 (everything is normal to the point)
+ return lightSample(direction, _power / (4.0f * PI), distance, 1.0f, 1.0f);
+}
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void pointLightsource::_print(std::ostream& s) const
+{
+ s << "Point Lightsource: position=" << _position << ", power=" << _power << ", attenuation=" << _attenuation;
+}
diff --git a/hw6/src/ray.cpp b/hw6/src/ray.cpp
new file mode 100644
index 0000000..18c839d
--- /dev/null
+++ b/hw6/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/hw6/src/ray_util.cpp b/hw6/src/ray_util.cpp
new file mode 100644
index 0000000..580f249
--- /dev/null
+++ b/hw6/src/ray_util.cpp
@@ -0,0 +1,27 @@
+/******************************************************************/
+/* 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 "ray_util.h"
+
+
+ray createRay(const intersectionPoint& ip, const vec3d& dir)
+{
+ return ray(ip.position() + EPSILON * dir, dir);
+}
+
+
+ray reflectRay(const intersectionPoint& ip)
+{
+ // compute reflected direction
+ // Note: ip.direction points toward ip point!
+ vec3d reflectedDir = ip.direction() - 2.0f * (ip.normal().dot(ip.direction())) * ip.normal();
+
+ // create ray
+ return createRay(ip, reflectedDir);
+}
diff --git a/hw6/src/raycasting.cpp b/hw6/src/raycasting.cpp
new file mode 100644
index 0000000..e533d5a
--- /dev/null
+++ b/hw6/src/raycasting.cpp
@@ -0,0 +1,47 @@
+/******************************************************************/
+/* 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 "raycasting.h"
+
+/////////////
+// Methods //
+/////////////
+image raycasting::render(const scene& s) const
+{
+ image result(s.getCamera().width(), s.getCamera().height());
+
+ // for every pixel
+ for(image::size_type y=0; y < result.height(); y++)
+ for(image::size_type x=0; x < result.width(); x++)
+ {
+ // generate view ray
+ ray r = s.getCamera()(x,y);
+
+ // intersect scene
+ intersectionPoint ip = s.intersect(r);
+
+ // shade pixel
+ if(ip.isHit())
+ {
+ // for each light source
+ for(unsigned int l=0; l < s.numberOfLightsources(); l++)
+ {
+ // connect to light source
+ lightSample ls = s.getLightsource(l).intensityAt(ip.position());
+
+ // shade
+ result(x,y) += ip.shade(ls);
+ }
+ }
+ else result(x,y) = color(0.0f, 0.0f, 0.0f);
+ }
+
+
+ // Done.
+ return result;
+}
diff --git a/hw6/src/recursiveRaytracing.cpp b/hw6/src/recursiveRaytracing.cpp
new file mode 100644
index 0000000..1163b2b
--- /dev/null
+++ b/hw6/src/recursiveRaytracing.cpp
@@ -0,0 +1,94 @@
+/******************************************************************/
+/* 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_util.h"
+#include "random_number.h"
+#include "recursiveRaytracing.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+recursiveRaytracing::recursiveRaytracing(unsigned int maxDepth, unsigned int samplesPerPixel)
+{
+ _depth = maxDepth;
+ _samples = samplesPerPixel;
+}
+
+
+/////////////
+// Methods //
+/////////////
+image recursiveRaytracing::render(const scene& s) const
+{
+ image result(s.getCamera().width(), s.getCamera().height());
+
+ // for every pixel
+ for(image::size_type y=0; y < result.height(); y++)
+ for(image::size_type x=0; x < result.width(); x++)
+ {
+ // for every sample per pixel
+ for(unsigned int sample = 0; sample < _samples; sample++)
+ {
+ // select point in pixel
+ float px = (_samples == 1) ? (float)(x) + 0.5f : (float)(x) + random_float();
+ float py = (_samples == 1) ? (float)(y) + 0.5f : (float)(y) + random_float();
+
+ // generate view ray
+ ray r = s.getCamera()(px,py);
+
+ // recursive ray tracing
+ result(x,y) += traceRay(s, r, 1) / (float)(_samples);
+ }
+ }
+
+
+ // Done.
+ return result;
+}
+
+
+
+color recursiveRaytracing::traceRay(const scene& s, const ray& r, unsigned int currentDepth) const
+{
+ // intersect
+ intersectionPoint ip = s.intersect(r);
+
+ // if hit
+ color result;
+ if(ip.isHit())
+ {
+ // shade point
+ for(unsigned int l=0; l < s.numberOfLightsources(); l++)
+ {
+ // connect to light source
+ lightSample ls = s.getLightsource(l).intensityAt(ip.position());
+
+ // check if occluded
+ ray shadowRay = createRay(ip, ls.directionToLight());
+ intersectionPoint shadowIp = s.intersect(shadowRay);
+ if(ls < shadowIp)
+ result += ip.shade(ls);
+ }
+
+ // recurse
+ if(currentDepth < _depth && ip.getShaderProperties().specular)
+ {
+ ray reflectedRay = reflectRay(ip);
+ result += ip.shade(reflectedRay.direction()) * traceRay(s, reflectedRay, currentDepth+1);
+ }
+
+ }
+
+ // if not hit, check for environment map
+ else if(s.hasEnvironmentMap())
+ result += s.evaluateEnvironmentMap(r.direction());
+
+ // Done.
+ return result;
+}
diff --git a/hw6/src/reflectanceParameter.cpp b/hw6/src/reflectanceParameter.cpp
new file mode 100644
index 0000000..5b918fb
--- /dev/null
+++ b/hw6/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/hw6/src/reflectanceShader_base.cpp b/hw6/src/reflectanceShader_base.cpp
new file mode 100644
index 0000000..780f119
--- /dev/null
+++ b/hw6/src/reflectanceShader_base.cpp
@@ -0,0 +1,85 @@
+/******************************************************************/
+/* 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);
+}
+
+
+color reflectanceShader_base::reflectance(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->reflectance(local_in, local_out);
+}
+
+
+brdfSample reflectanceShader_base::sample(const intersectionPoint& ip, float r1, float r2) const
+{
+ // create shading frame (note: in and out point away!)
+ coordinateTransformation ct = ip.shadingFrame();
+ vec3d local_out = ct.inverseTransformDirection(-ip.direction());
+
+ // get brdf
+ std::unique_ptr<const brdf_base> brdf = make_brdf(ip.textureCoordinate());
+
+ // sample (local shading frame)
+ brdfSample sample = brdf->sample(local_out, r1, r2);
+
+ // transform back to global shading frame
+ return sample.transform(ct);
+}
+
+
+float reflectanceShader_base::reflectivity(const intersectionPoint& ip) const
+{
+ // get brdf
+ std::unique_ptr<const brdf_base> brdf = make_brdf(ip.textureCoordinate());
+
+ // get reflectivity
+ return brdf->reflectivity();
+}
+
+
+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());
+}
+
+
+color reflectanceShader_base::emittance(const intersectionPoint& ip) const
+{
+ return color();
+}
diff --git a/hw6/src/rotation3d.cpp b/hw6/src/rotation3d.cpp
new file mode 100644
index 0000000..13fddd5
--- /dev/null
+++ b/hw6/src/rotation3d.cpp
@@ -0,0 +1,47 @@
+/******************************************************************/
+/* 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 "rotation3d.h"
+
+//////////////////
+// Constructors //
+//////////////////
+rotation3d::rotation3d(void)
+ : transformation3d()
+{
+ // Do Nothing
+}
+
+
+rotation3d::rotation3d(float angle, const vec3d& axis)
+ : transformation3d()
+{
+ vec3d norm_axis = normalize(axis);
+ float s = sin(angle * PI / 180.0f);
+ float c = cos(angle * PI / 180.0f);
+
+ _transformation(0,0) = c + ((norm_axis.x * norm_axis.x) * (1.0f - c));
+ _transformation(0,1) = -(norm_axis.z * s) + ((norm_axis.x * norm_axis.y) * (1.0f - c));
+ _transformation(0,2) = (norm_axis.y * s) + ((norm_axis.x * norm_axis.z) * (1.0f - c));
+ _transformation(1,0) = (norm_axis.z * s) + ((norm_axis.x * norm_axis.y) * (1.0f - c));
+ _transformation(1,1) = c + ((norm_axis.y * norm_axis.y) * (1.0f - c));
+ _transformation(1,2) = -(norm_axis.x * s) + ((norm_axis.y * norm_axis.z) * (1.0f - c));
+ _transformation(2,0) = -(norm_axis.y * s) + ((norm_axis.x * norm_axis.z) * (1.0f - c));
+ _transformation(2,1) = (norm_axis.x * s) + ((norm_axis.y * norm_axis.z) * (1.0f - c));
+ _transformation(2,2) = c + ((norm_axis.z * norm_axis.z) * (1.0f - c));
+
+ _inverseTransformation = transpose(_transformation);
+}
+
+
+rotation3d::rotation3d(const rotation3d& r)
+ : transformation3d(r)
+{
+ // Do Nothing
+}
diff --git a/hw6/src/rotationX3d.cpp b/hw6/src/rotationX3d.cpp
new file mode 100644
index 0000000..4a53570
--- /dev/null
+++ b/hw6/src/rotationX3d.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 "constants.h"
+#include "rotationX3d.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+rotationX3d::rotationX3d(void)
+ : transformation3d()
+{
+ // Do Nothing
+}
+
+
+rotationX3d::rotationX3d(float angle)
+ : transformation3d()
+{
+ float s = sin(angle * PI / 180.0f);
+ float c = cos(angle * PI / 180.0f);
+
+ _transformation(1,1) = c;
+ _transformation(2,1) = s;
+ _transformation(1,2) = -s;
+ _transformation(2,2) = c;
+
+ _inverseTransformation = transpose(_transformation);
+}
+
+
+rotationX3d::rotationX3d(const rotationX3d& r)
+ : transformation3d(r)
+{
+ // Do Nothing
+}
diff --git a/hw6/src/rotationY3d.cpp b/hw6/src/rotationY3d.cpp
new file mode 100644
index 0000000..9ecd572
--- /dev/null
+++ b/hw6/src/rotationY3d.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 "constants.h"
+#include "rotationY3d.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+rotationY3d::rotationY3d(void)
+ : transformation3d()
+{
+ // Do Nothing
+}
+
+
+rotationY3d::rotationY3d(float angle)
+ : transformation3d()
+{
+ float s = sin(angle * PI / 180.0f);
+ float c = cos(angle * PI / 180.0f);
+
+ _transformation(0,0) = c;
+ _transformation(2,0) = -s;
+ _transformation(0,2) = s;
+ _transformation(2,2) = c;
+
+ _inverseTransformation = transpose(_transformation);
+}
+
+
+rotationY3d::rotationY3d(const rotationY3d& r)
+ : transformation3d(r)
+{
+ // Do Nothing
+}
diff --git a/hw6/src/rotationZ3d.cpp b/hw6/src/rotationZ3d.cpp
new file mode 100644
index 0000000..5c73a78
--- /dev/null
+++ b/hw6/src/rotationZ3d.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 "constants.h"
+#include "rotationZ3d.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+rotationZ3d::rotationZ3d(void)
+ : transformation3d()
+{
+ // Do Nothing
+}
+
+
+rotationZ3d::rotationZ3d(float angle)
+ : transformation3d()
+{
+ float s = sin(angle * PI / 180.0f);
+ float c = cos(angle * PI / 180.0f);
+
+ _transformation(0,0) = c;
+ _transformation(1,0) = s;
+ _transformation(0,1) = -s;
+ _transformation(1,1) = c;
+
+ _inverseTransformation = transpose(_transformation);
+}
+
+
+rotationZ3d::rotationZ3d(const rotationZ3d& r)
+ : transformation3d(r)
+{
+ // Do Nothing
+}
diff --git a/hw6/src/scale3d.cpp b/hw6/src/scale3d.cpp
new file mode 100644
index 0000000..7124f12
--- /dev/null
+++ b/hw6/src/scale3d.cpp
@@ -0,0 +1,41 @@
+/******************************************************************/
+/* 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 "scale3d.h"
+
+//////////////////
+// Constructors //
+//////////////////
+scale3d::scale3d(void) : transformation3d()
+{
+ // Do Nothing
+}
+
+
+scale3d::scale3d(float uniformScale) : transformation3d()
+{
+ _transformation.setDiagonal(uniformScale);
+ _inverseTransformation.setDiagonal(1.0f / uniformScale);
+}
+
+
+scale3d::scale3d(float scaleX, float scaleY, float scaleZ) : transformation3d()
+{
+ _transformation(0,0) = scaleX;
+ _transformation(1,1) = scaleY;
+ _transformation(2,2) = scaleZ;
+
+ _inverseTransformation(0,0) = 1.0f / scaleX;
+ _inverseTransformation(1,1) = 1.0f / scaleY;
+ _inverseTransformation(2,2) = 1.0f / scaleZ;
+}
+
+scale3d::scale3d(const scale3d& s) : transformation3d(s)
+{
+ // Do Nothing
+}
diff --git a/hw6/src/scene.cpp b/hw6/src/scene.cpp
new file mode 100644
index 0000000..35e0fe4
--- /dev/null
+++ b/hw6/src/scene.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 <cassert>
+#include "scene.h"
+#include "render_base.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();
+}
+
+
+bool scene::hasEnvironmentMap(void) const
+{
+ return (_environmentMap != nullptr);
+}
+
+
+color scene::evaluateEnvironmentMap(const vec3d& direction) const
+{
+ assert(_environmentMap);
+ return (*_environmentMap)(direction);
+}
+
+
+/////////////
+// Methods //
+/////////////
+intersectionPoint scene::intersect(const ray& r) const
+{
+ return _sceneGraphRoot->intersect(r);
+}
+
+
+image scene::render(void) const
+{
+ return _renderEngine->render(*this);
+}
diff --git a/hw6/src/sceneGraphNode.cpp b/hw6/src/sceneGraphNode.cpp
new file mode 100644
index 0000000..1a0f600
--- /dev/null
+++ b/hw6/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/hw6/src/sceneIO.cpp b/hw6/src/sceneIO.cpp
new file mode 100644
index 0000000..59e586d
--- /dev/null
+++ b/hw6/src/sceneIO.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 "util.h"
+#include "scene.h"
+#include "raycasting.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_texture.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<texture_base> texture_cache;
+ nodeCache<shader_base> shader_cache;
+
+ // init scene data
+ bool autoTuneCamera = false;
+ std::unique_ptr<const intersector_factory_base> intersector(new linear_intersector_factory());
+ s._renderEngine = std::unique_ptr<const render_base>(new raycasting());
+
+ // process sceneNode
+ for(XMLNode node = sceneNode.firstChild(); node.isValid(); node++)
+ {
+ // check for general node-types
+ if(importTexture(node, texture_cache, rootDir)) continue;
+ 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 == "environmentMap") importEnvironmentMap(node, texture_cache, rootDir, s._environmentMap);
+ else if(nodeName == "intersector") importIntersector(node, intersector);
+ else if(nodeName == "renderer") importRenderEngine(node, s._renderEngine);
+ 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/hw6/src/sceneIO_basis.cpp b/hw6/src/sceneIO_basis.cpp
new file mode 100644
index 0000000..6f768b0
--- /dev/null
+++ b/hw6/src/sceneIO_basis.cpp
@@ -0,0 +1,100 @@
+/******************************************************************/
+/* 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 "sceneIO_texture.h"
+#include "sceneIO_transformation3d.h"
+
+#include "raycasting.h"
+#include "pathtracing.h"
+#include "recursiveRaytracing.h"
+
+#include "bvh_intersector.h"
+#include "linear_intersector.h"
+
+#include "environmentMap.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 importRenderEngine(const XMLNode& node, std::unique_ptr<const render_base>& renderer)
+{
+ // sanity check
+ assert(node.name() == "renderer");
+
+ // get render engine type
+ std::string type = getString(node, "type", "raycasting");
+
+ // create render type
+ if(type == "raycasting") renderer = std::unique_ptr<const render_base>( new raycasting() );
+ else if(type == "recursiveRaytracing") renderer = std::unique_ptr<const render_base>( new recursiveRaytracing( getInteger(node, "maxDepth", 1), getInteger(node, "samplesPerPixel", 1) ) );
+ else if(type == "pathtracing") renderer = std::unique_ptr<const render_base>( new pathtracing( getInteger(node, "samplesPerPixel", 1), getString(node, "directOnly", "false") == "true") );
+ else errorMessage("Unknown render engine type (%s)", type.c_str());
+
+ // Done.
+}
+
+
+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.
+}
+
+
+void importEnvironmentMap(const XMLNode& node, nodeCache<texture_base>& texture_cache, const std::string& rootDir, std::unique_ptr<const environmentMap>& map)
+{
+ // sanity check
+ assert(node.name() == "environmentMap");
+
+ // node properties
+ transformation3d transform;
+ std::shared_ptr<const texture_base> texture;
+
+ // check child nodes
+ for(XMLNode child=node.firstChild(); child.isValid(); child++)
+ {
+ std::string name = child.name();
+ if(name == "transformation") importTransformation(child, transform);
+ else if(name == "texture") texture = importTexture(child, texture_cache, rootDir);
+ else errorMessage("Unknown node (%s) in environmentMap.", name.c_str());
+ }
+
+ // Done.
+ map = std::unique_ptr<const environmentMap>(new environmentMap(texture, transform));
+}
diff --git a/hw6/src/sceneIO_core.cpp b/hw6/src/sceneIO_core.cpp
new file mode 100644
index 0000000..123287e
--- /dev/null
+++ b/hw6/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/hw6/src/sceneIO_geometry.cpp b/hw6/src/sceneIO_geometry.cpp
new file mode 100644
index 0000000..5fa4a5c
--- /dev/null
+++ b/hw6/src/sceneIO_geometry.cpp
@@ -0,0 +1,187 @@
+/******************************************************************/
+/* This file is part of the homework assignments for CSCI-427/527 */
+/* at The College of William & Mary and authored by Pieter Peers. */
+/* No part of this file, whether altered or in original form, can */
+/* be distributed or used outside the context of CSCI-427/527 */
+/* without consent of either the College of William & Mary or */
+/* Pieter Peers. */
+/******************************************************************/
+#include <vector>
+#include <memory>
+#include <string>
+#include <cstdio>
+#include <cassert>
+
+#include "sceneIO_xml.h"
+#include "sceneIO_core.h"
+#include "sceneIO_cache.h"
+#include "sceneIO_geometry.h"
+#include "sceneIO_material.h"
+#include "sceneIO_transformation3d.h"
+
+#include "util.h"
+#include "vec2d.h"
+#include "vec3d.h"
+#include "triangle.h"
+#include "constants.h"
+#include "importOBJ.h"
+#include "errorMessage.h"
+#include "triangleMesh.h"
+#include "random_number.h"
+#include "sceneGraphNode.h"
+
+static void importTriangle(const XMLNode& node, std::vector<triangle>& triangle_list)
+{
+ // sanity check
+ assert(node.name() == "triangle" || node.name() == "polygon");
+
+ // allocate temp data
+ unsigned int idx=0;
+ vec3d v[3], n[3];
+ vec2d t[3];
+
+ // for each child node, parse all vertices
+ for(XMLNode child = node.firstChild(); child.isValid(); child++)
+ {
+ std::string name = child.name();
+ if(name == "vertex")
+ {
+ v[idx] = getVec3d(child, "v", vec3d(+LARGE));
+ n[idx] = getVec3d(child, "n", vec3d(0.0f));
+ t[idx] = getVec2d(child, "t", vec2d(+LARGE));
+
+ // check if valid vertex
+ if(v[idx].x >= +LARGE || v[idx].y >= +LARGE || v[idx].z >= +LARGE)
+ errorMessage("Invalid vertex in %s definition.", node.name().c_str());
+
+ // create triangle
+ if(idx == 2)
+ {
+ // determine which attributes are defined on the triangle
+ bool hasNormals = (n[0].squared_length() > +EPSILON) && (n[0].squared_length() > +EPSILON) && (n[0].squared_length() > +EPSILON);
+ bool hasTextureCoord = (fabs(t[0].x) < +LARGE && fabs(t[0].y) < +LARGE) && (fabs(t[1].x) < +LARGE && fabs(t[1].y) < +LARGE) && (fabs(t[2].x) < +LARGE && fabs(t[2].y) < +LARGE);
+
+ if(!hasNormals && !hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2]));
+ else if(hasNormals && !hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2], n[0], n[1], n[2]));
+ else if(!hasNormals && hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2], t[0], t[1], t[2]));
+ else triangle_list.push_back(triangle(v[0], v[1], v[2], n[0], n[1], n[2], t[0], t[1], t[2]));
+
+ // cycle (to support polygons)
+ v[1] = v[2];
+ n[1] = n[2];
+ t[1] = t[2];
+ }
+ else idx++;
+ }
+ else errorMessage("Unknown child element in %s node (%s).", node.name().c_str(), name.c_str());
+ }
+
+ // check if at least one full triangle was found
+ if(idx != 2)
+ errorMessage("%s requires at least 3 vertices.", node.name().c_str());
+
+ // Done.
+ }
+
+
+/////////////////////////////////////////////////////////////////
+static std::shared_ptr<boundedPrimitive> importTriangleMesh(const XMLNode& node, nodeCache<boundedPrimitive>& shape_cache, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // sanity check
+ assert(node.name() == "triangleMesh");
+
+ // allocate node properties
+ transformation3d transform;
+ std::shared_ptr<const shader_base> material;
+ std::vector<triangle> triangle_list;
+
+ // import OBJ triangles
+ std::string objname = getString(node, "filename");
+ if(objname != "")
+ {
+ std::string path = getDirectory(objname);
+ if(path == "") objname = rootDir + objname;
+ importOBJ(objname, triangle_list);
+ }
+
+ // check child nodes
+ for(XMLNode child = node.firstChild(); child.isValid(); child++)
+ {
+ std::string name = child.name();
+
+ // general child nodes
+ auto tempmat = importMaterial(child, shader_cache, texture_cache, rootDir);
+ if(tempmat) material = tempmat;
+
+ // specialized child nodes
+ else if(name == "transformation") importTransformation(child, transform);
+ else if(name == "triangle" || name == "polygon") importTriangle(child, triangle_list);
+ else errorMessage("Unknown child-node in triangleMesh (%s).", name.c_str());
+ }
+
+ // Done.
+ return std::shared_ptr<boundedPrimitive>(new triangleMesh(triangle_list, material, transform));
+}
+
+/////////////////////////////////////////////////////////////////
+
+static std::shared_ptr<boundedPrimitive> importSceneGraphNode(const XMLNode& node, nodeCache<boundedPrimitive>& shape_cache, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // sanity check
+ assert(node.name() == "sceneGraphNode");
+
+ // node properties
+ transformation3d transform;
+ std::shared_ptr<const shader_base> material;
+ std::vector<std::shared_ptr<const boundedPrimitive>> child_nodes;
+
+ // check child nodes
+ for(XMLNode child = node.firstChild(); child.isValid(); child++)
+ {
+ std::string name = child.name();
+
+ // general child nodes
+ auto tempmat = importMaterial(child, shader_cache, texture_cache, rootDir);
+ auto shape = importGeometry(child, shape_cache, shader_cache, texture_cache, rootDir);
+
+ if(tempmat) material = tempmat;
+ else if(shape) child_nodes.push_back(shape);
+
+ // node specific children.
+ else if(name == "transformation") importTransformation(child, transform);
+ else errorMessage("Unknown child-node in sceneGraphNode (%s).", name.c_str());
+ }
+
+ // Done.
+ return std::shared_ptr<boundedPrimitive>(new sceneGraphNode(child_nodes, transform, material));
+}
+
+/////////////////////////////////////////////////////////////////
+std::shared_ptr<boundedPrimitive> importGeometry(const XMLNode& node, nodeCache<boundedPrimitive>& shape_cache, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // check if geometry type
+ if(node.name() != "sceneGraphNode" && node.name() != "triangleMesh") return std::shared_ptr<boundedPrimitive>(nullptr);
+
+ // check if reference
+ std::string ref = getString(node, "ref");
+ std::shared_ptr<boundedPrimitive> shape = shape_cache.get(ref);
+ if(shape) return shape;
+ else if(ref != "") errorMessage("Unknown %s name (%s).", node.name().c_str(), ref.c_str());
+
+ // generate default name & get id
+ char defaultname[32]; sprintf(defaultname, "noname-%u", random_int());
+ std::string id = getString(node, "id", defaultname);
+
+ if(shape_cache.get(id))
+ errorMessage("%s-node id is not unique (%s).", node.name().c_str(), id.c_str());
+
+ // process primitive based on type
+ if(node.name() == "sceneGraphNode") shape = importSceneGraphNode(node, shape_cache, shader_cache, texture_cache, rootDir);
+ else if(node.name() == "triangleMesh") shape = importTriangleMesh(node, shape_cache, shader_cache, texture_cache, rootDir);
+
+ // update cache
+ if(id != "") shape_cache.add(id, shape);
+
+ // Done.
+ return shape;
+}
diff --git a/hw6/src/sceneIO_light.cpp b/hw6/src/sceneIO_light.cpp
new file mode 100644
index 0000000..d763970
--- /dev/null
+++ b/hw6/src/sceneIO_light.cpp
@@ -0,0 +1,131 @@
+/******************************************************************/
+/* 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_texture.h"
+#include "sceneIO_material.h"
+#include "sceneIO_geometry.h"
+
+#include "spotLightsource.h"
+#include "areaLightsource.h"
+#include "pointLightsource.h"
+#include "directionalLightsource.h"
+
+#include "lightsourceGeometry.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));
+}
+
+
+static std::shared_ptr<const lightsource_base> importPointLight(const XMLNode& node)
+{
+ // get properties
+ vec3d position = getVec3d(node, "position", vec3d(0.0f, 0.0f, 0.0f));
+ color power = getColor(node, "power", color(1.0f, 1.0f, 1.0f));
+ vec3d attenuation = getVec3d(node, "attenuation", vec3d(0.0f, 0.0f, 1.0f));
+
+ // Done
+ return std::shared_ptr<const lightsource_base>( new pointLightsource(position, power, attenuation));
+}
+
+
+static std::shared_ptr<const lightsource_base> importSpotLight(const XMLNode& node)
+{
+ // Get properties
+ vec3d position = getVec3d(node, "position", vec3d(0.0f, 0.0f, 0.0f));
+ vec3d direction = getVec3d(node, "direction", vec3d(0.0f, 0.0f, 1.0f));
+ float cutoff = getFloat(node, "cutoff", 30.0f);
+ float sharpness = getFloat(node, "sharpness", 1.0f);
+ color power = getColor(node, "power", color(1.0f, 1.0f, 1.0f));
+ vec3d attenuation = getVec3d(node, "attenuation", vec3d(0.0f, 0.0f, 1.0f));
+
+ // Done.
+ return std::shared_ptr<const lightsource_base>(new spotLightsource( position, direction, cutoff, sharpness, power, attenuation ));
+}
+
+
+static std::shared_ptr<const lightsource_base> importAreaLight(const XMLNode& node, nodeCache<boundedPrimitive>& shape_cache, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // get properties
+ color power = getColor(node, "power", color(1.0f, 1.0f, 1.0f));
+ vec3d attenuation = getVec3d(node, "attenuation", vec3d(0.0f, 0.0f, 1.0f));
+
+ // node properties
+ std::shared_ptr<const shader_base> reflectanceMaterial;
+ std::shared_ptr<const boundedPrimitive> geometry;
+
+ // get children
+ for(XMLNode child = node.firstChild(); child.isValid(); child++)
+ {
+ std::string name = child.name();
+
+ // general child nodes
+ auto tempMat = importMaterial(child, shader_cache, texture_cache, rootDir);
+ auto tempGeom = importGeometry(child, shape_cache, shader_cache, texture_cache, rootDir);
+
+ // specific child nodes
+ if(tempMat) reflectanceMaterial = tempMat;
+ else if(tempGeom) geometry = tempGeom;
+ else errorMessage("Unknown child-node in areaLightsource (%s).", name.c_str());
+ }
+
+ // produce warning if shader defined on the geometry
+ if(geometry && geometry->hasShader() && !reflectanceMaterial)
+ warningMessage("Any shader defined on the geometry are ignored in AreaLightsource. Define a reflectance material explicitely in AreaLightsource instead.");
+
+ // Create
+ // 1) light source
+ // 2) light source geometry
+ std::shared_ptr<const lightsource_base> ls( new areaLightsource(power, geometry, attenuation) );
+ std::shared_ptr<boundedPrimitive> lsShape( new lightsourceGeometry(geometry, ls, reflectanceMaterial) );
+
+ // Add lsShape to shape_cache
+ char id[32]; sprintf(id, "lightgeometry-%u", random_int());
+shape_cache.add(std::string(id), lsShape);
+
+ // Done.
+ return ls;
+}
+
+
+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 if(type == "point") ls = importPointLight(node);
+ else if(type == "spot") ls = importSpotLight(node);
+ else if(type == "area") ls = importAreaLight(node, shape_cache, shader_cache, texture_cache, rootDir);
+ else errorMessage("Unknown light source type (%s)", type.c_str());
+
+ // Done.
+ return ls;
+}
+
+
+
diff --git a/hw6/src/sceneIO_material.cpp b/hw6/src/sceneIO_material.cpp
new file mode 100644
index 0000000..7999eca
--- /dev/null
+++ b/hw6/src/sceneIO_material.cpp
@@ -0,0 +1,243 @@
+/******************************************************************/
+/* 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_texture.h"
+#include "sceneIO_material.h"
+
+#include "bumpMap.h"
+#include "normalMap.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));
+
+ // check for texture
+ std::shared_ptr<const texture_base> texture = importTexture(node.firstChild(), texture_cache, rootDir);
+
+ // create & return parameter, give preference to textured parameters
+ if(texture) return colorReflectanceParameter(texture);
+ else 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);
+
+ // check for texture
+ std::shared_ptr<const texture_base> texture = importTexture(node.firstChild(), texture_cache, rootDir);
+
+ // create & return parameter, give preference to textured parameters
+ if(texture) return scalarReflectanceParameter(texture, channel);
+ else 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> importNormalMap(const XMLNode& node, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // sanity check
+ assert(getString(node, "type") == "normalMap");
+
+ // parameter storage
+ std::shared_ptr<const shader_base> shader;
+ std::shared_ptr<const texture_base> map;
+
+ // process child nodes
+ for(XMLNode child=node.firstChild(); child.isValid(); child++)
+ {
+ // process specific nodes
+ if(child.name() == "texture") map = importTexture(child, texture_cache, rootDir);
+
+ // process general shader nodes
+ else if(!shader) shader = importMaterial(child, shader_cache, texture_cache, rootDir);
+
+ else if(!shader) errorMessage("Unknown node in normalMap (%s).", child.name().c_str());
+ }
+
+ // check if shader & texture was specified
+ if(!shader) errorMessage("NormalMap requires a shader.");
+ if(!map) errorMessage("NormalMap requires a texture.");
+
+ // Done.
+ return std::shared_ptr<shader_base>(new normalMap(map, shader));
+}
+
+//////////////////////////////////////////////////////////////
+std::shared_ptr<shader_base> importBumpMap(const XMLNode& node, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // sanity check
+ assert(getString(node, "type") == "bumpMap");
+
+ // obtain parameters
+ float scale = getFloat(node, "scale", 1.0f);
+ unsigned int channel = getInteger(node, "channel", 0);
+
+ // parameter storage
+ std::shared_ptr<const shader_base> shader;
+ std::shared_ptr<const texture_base> map;
+
+ // process child nodes
+ for(XMLNode child=node.firstChild(); child.isValid(); child++)
+ {
+ // process specific nodes
+ if(child.name() == "texture") map = importTexture(child, texture_cache, rootDir);
+
+ // process general shader nodes
+ else if(!shader) shader = importMaterial(child, shader_cache, texture_cache, rootDir);
+
+ else if(!shader) errorMessage("Unknown node in bumpMap (%s).", child.name().c_str());
+ }
+
+ // check if shader & texture was specified
+ if(!shader) errorMessage("BumpMap requires a shader.");
+ if(!map) errorMessage("BumpMap requires a texture.");
+
+ // Done.
+ return std::shared_ptr<shader_base>(new bumpMap(map, scale, channel, shader));
+}
+
+//////////////////////////////////////////////////////////////
+std::shared_ptr<shader_base> importShadingFrameTransformation(const XMLNode& node, nodeCache<shader_base>& shader_cache, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // sanity check
+ assert(node.name() == "shadingFrameTransformation");
+
+ // get type
+ std::string type = getString(node, "type", "none specified");
+
+ // specialize based on type
+ std::shared_ptr<shader_base> sft;
+
+ if(type == "normalMap") sft = importNormalMap(node, shader_cache, texture_cache, rootDir);
+ else if(type == "bumpMap") sft = importBumpMap(node, shader_cache, texture_cache, rootDir);
+ else errorMessage("Unknown shadingFrameTransformation type (%s).", type.c_str());
+
+ // Done.
+ return sft;
+}
+
+//////////////////////////////////////////////////////////////
+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 != "shadingFrameTransformation" &&
+ 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 == "shadingFrameTransformation") shader = importShadingFrameTransformation(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/hw6/src/sceneIO_texture.cpp b/hw6/src/sceneIO_texture.cpp
new file mode 100644
index 0000000..f537613
--- /dev/null
+++ b/hw6/src/sceneIO_texture.cpp
@@ -0,0 +1,55 @@
+/******************************************************************/
+/* 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 "sceneIO_core.h"
+#include "sceneIO_texture.h"
+
+#include "util.h"
+#include "imageIO.h"
+#include "nearestTexture.h"
+#include "bilinearTexture.h"
+
+std::shared_ptr<texture_base> importTexture(const XMLNode& node, nodeCache<texture_base>& texture_cache, const std::string& rootDir)
+{
+ // check if texture
+ if(!node.isValid() || node.name() != "texture") return std::shared_ptr<texture_base>(nullptr);
+
+ // get attributes
+ std::string type = getString(node, "type", "nearest");
+ std::string filename = getString(node, "filename");
+ bool repeatTexture = (getString(node, "repeatTexture", "false") == "false");
+
+ // sanity check
+ if(filename == "")
+ errorMessage("Missing texture 'filename'.");
+
+ // get full filename
+ std::string path = getDirectory(filename);
+ if(path == "") filename = rootDir + filename;
+
+ // get key name
+ std::string key = filename + "#" + type;
+ if(repeatTexture) key += "Repeat";
+
+ // check cache
+ if(std::shared_ptr<texture_base> ctexture = texture_cache.get(key))
+ return ctexture;
+
+ // create based on type
+ std::shared_ptr<texture_base> texture;
+ if(type == "nearest") texture = std::shared_ptr<texture_base>( new nearestTexture(repeatTexture));
+ else if(type == "bilinear") texture = std::shared_ptr<texture_base>( new bilinearTexture(repeatTexture) );
+ else errorMessage("Unknown texture type (%s).", type.c_str());
+
+ // load and add texture to cache
+ importImage(filename, *texture);
+ texture_cache.add(key, texture);
+
+ // Done.
+ return texture;
+}
diff --git a/hw6/src/sceneIO_transformation3d.cpp b/hw6/src/sceneIO_transformation3d.cpp
new file mode 100644
index 0000000..cf54449
--- /dev/null
+++ b/hw6/src/sceneIO_transformation3d.cpp
@@ -0,0 +1,80 @@
+/******************************************************************/
+/* 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_core.h"
+#include "sceneIO_transformation3d.h"
+
+#include "rotationX3d.h"
+#include "rotationY3d.h"
+#include "rotationZ3d.h"
+#include "rotation3d.h"
+#include "translation3d.h"
+#include "scale3d.h"
+
+#include "errorMessage.h"
+
+static transformation3d importRotationX(const XMLNode& node)
+{
+ return rotationX3d(getFloat(node, "angle", 0.0f));
+}
+
+static transformation3d importRotationY(const XMLNode& node)
+{
+ return rotationY3d(getFloat(node, "angle", 0.0f));
+}
+
+static transformation3d importRotationZ(const XMLNode& node)
+{
+ return rotationZ3d(getFloat(node, "angle", 0.0f));
+}
+
+static transformation3d importRotationAxis(const XMLNode& node)
+{
+ return rotation3d(getFloat(node, "angle", 0.0f),
+ getVec3d(node, "axis", vec3d(0.0f, 0.0f, 1.0f)));
+}
+
+static transformation3d importTranslation(const XMLNode& node)
+{
+ return translation3d(getVec3d(node, "offset", vec3d(0.0f)));
+}
+
+static transformation3d importScale(const XMLNode& node)
+{
+ vec3d scale = getVec3d(node, "scale", vec3d(1.0f));
+ return scale3d(scale.x, scale.y, scale.z);
+}
+
+
+
+void importTransformation(const XMLNode& node, transformation3d& transform)
+{
+ // sanity check
+ assert(node.name() == "transformation");
+
+ // accumulate all child nodes
+ for(XMLNode childNode = node.firstChild(); childNode.isValid(); childNode++)
+ {
+ // get transformation type:
+ std::string type = childNode.name();
+
+ // parse
+ if(type == "rotationX") transform *= importRotationX(childNode);
+ else if(type == "rotationY") transform *= importRotationY(childNode);
+ else if(type == "rotationZ") transform *= importRotationZ(childNode);
+ else if(type == "rotation") transform *= importRotationAxis(childNode);
+ else if(type == "translation") transform *= importTranslation(childNode);
+ else if(type == "scale") transform *= importScale(childNode);
+ else errorMessage("Unknown transformation (%s)", type.c_str());
+ }
+
+ // Done.
+}
+
+
diff --git a/hw6/src/sceneIO_xml.cpp b/hw6/src/sceneIO_xml.cpp
new file mode 100644
index 0000000..47aa94e
--- /dev/null
+++ b/hw6/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/hw6/src/shaderProperties.cpp b/hw6/src/shaderProperties.cpp
new file mode 100644
index 0000000..235e73d
--- /dev/null
+++ b/hw6/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/hw6/src/shadingFrameTransformation.cpp b/hw6/src/shadingFrameTransformation.cpp
new file mode 100644
index 0000000..8dfb589
--- /dev/null
+++ b/hw6/src/shadingFrameTransformation.cpp
@@ -0,0 +1,82 @@
+/******************************************************************/
+/* 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 "shadingFrameTransformation.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+shadingFrameTransformation::shadingFrameTransformation(const std::shared_ptr<const shader_base>& shader)
+ : shader_base()
+{
+ _shader = shader;
+}
+
+
+/////////////
+// Methods //
+/////////////
+color shadingFrameTransformation::shade(const intersectionPoint& ip, const vec3d& light_dir) const
+{
+ // transform to local shading frame
+ intersectionPoint localIp(ip);
+ localIp.inverseTransformShadingFrame( _transformation(ip.textureCoordinate()) );
+
+ // Done.
+ return _shader->shade(localIp, light_dir);
+}
+
+
+color shadingFrameTransformation::reflectance(const intersectionPoint& ip, const vec3d& light_dir) const
+{
+ // transform to local shading frame
+ intersectionPoint localIp(ip);
+ localIp.inverseTransformShadingFrame( _transformation(ip.textureCoordinate()) );
+
+ // Done.
+ return _shader->reflectance(localIp, light_dir);
+}
+
+
+brdfSample shadingFrameTransformation::sample(const intersectionPoint& ip, float r1, float r2) const
+{
+ // transform to local shading frame
+ intersectionPoint localIp(ip);
+ transformation3d ct = _transformation(ip.textureCoordinate());
+ localIp.inverseTransformShadingFrame(ct);
+
+ //get sample (in local shading frame)
+ brdfSample sample = _shader->sample(localIp, r1, r2);
+
+ // transform to global shading frame
+ return sample.transform(ct);
+}
+
+
+float shadingFrameTransformation::reflectivity(const intersectionPoint& ip) const
+{
+ return _shader->reflectivity(ip);
+}
+
+
+color shadingFrameTransformation::emittance(const intersectionPoint& ip) const
+{
+ // transform to local shading frame
+ intersectionPoint localIp(ip);
+ localIp.inverseTransformShadingFrame( _transformation(ip.textureCoordinate()) );
+
+ // Done.
+ return _shader->emittance(localIp);
+}
+
+
+shaderProperties shadingFrameTransformation::properties(const intersectionPoint& ip) const
+{
+ return _shader->properties(ip);
+}
diff --git a/hw6/src/spotLightsource.cpp b/hw6/src/spotLightsource.cpp
new file mode 100644
index 0000000..9b4d0ab
--- /dev/null
+++ b/hw6/src/spotLightsource.cpp
@@ -0,0 +1,69 @@
+/******************************************************************/
+/* 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 "spotLightsource.h"
+
+//////////////////
+// Constructors //
+//////////////////
+spotLightsource::spotLightsource(const vec3d& position, const vec3d& direction, float cutoffDegrees, float sharpness, const color& power, const vec3d& attenuation)
+{
+ _position = position;
+ _direction = normalize(direction);
+ _cutoff = cos(cutoffDegrees * PI / 180.0f);
+ _sharpness = sharpness;
+ _power = power;
+ _attenuation = attenuation;
+}
+
+
+/////////////
+// Methods //
+/////////////
+lightSample spotLightsource::intensityAt(const vec3d& point) const
+{
+ vec3d direction = point - _position;
+ float distance = direction.length();
+ float cosAngle = _direction.dot(direction) / distance;
+
+ // check if outside cone
+ if(cosAngle < _cutoff) return lightSample(direction, color(0.0f, 0.0f, 0.0f), distance);
+
+ // compute attenuation & scale
+ float attenuation = ((_attenuation[2]*distance + _attenuation[1])*distance + _attenuation[0]);
+ float scale = pow(cosAngle, _sharpness);
+
+ // Done.
+ return lightSample(direction, _power * scale / attenuation, distance);
+}
+
+
+lightSample spotLightsource::emittanceAt(const vec3d& point, float r1, float r2) const
+{
+ vec3d direction = point - _position;
+ float distance = direction.length();
+ float cosAngle = _direction.dot(direction) / distance;
+
+ // compute emittance
+ color emittance = (cosAngle < _cutoff) ? color(0.0f, 0.0f, 0.0f) : _power * pow(cosAngle, _sharpness) / (4.0f * PI);
+
+ // Done.
+ // PDF = 1.0 => there is only a single point
+ // Foreshortening => no normal
+ return lightSample(direction, emittance, distance, 1.0f, 1.0f);
+}
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void spotLightsource::_print(std::ostream& s) const
+{
+ s << "Spot Lightsource: position=" << _position << ", direction=" << _direction << ", cutoff=" << (acos(_cutoff)*180.0f/PI) << ", sharpness=" << _sharpness << ", power=" << _power << ", attenuation=" << _attenuation;
+}
diff --git a/hw6/src/surfaceSample.cpp b/hw6/src/surfaceSample.cpp
new file mode 100644
index 0000000..464c969
--- /dev/null
+++ b/hw6/src/surfaceSample.cpp
@@ -0,0 +1,118 @@
+/******************************************************************/
+/* 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 "surfaceSample.h"
+
+//////////////////
+// Constructors //
+//////////////////
+surfaceSample::surfaceSample(void)
+{
+ _position = vec3d();
+ _normal = vec3d();
+ _pdf = 0.0f;
+}
+
+
+surfaceSample::surfaceSample(const vec3d& position, const vec3d& normal, const float pdf)
+{
+ _position = position;
+ _normal = normal;
+ _pdf = std::max(pdf, 0.0f);
+}
+
+
+surfaceSample::surfaceSample(const surfaceSample& ss)
+{
+ _position = ss._position;
+ _normal = ss._normal;
+ _pdf = ss._pdf;
+}
+
+
+///////////////
+// Operators //
+///////////////
+surfaceSample& surfaceSample::operator=(const surfaceSample& ss)
+{
+ _assign(ss);
+ return *this;
+}
+
+
+surfaceSample surfaceSample::operator*(float pdf) const
+{
+ return surfaceSample(_position, _normal, _pdf * pdf);
+}
+
+
+surfaceSample& surfaceSample::operator*=(float pdf)
+{
+ _pdf *= pdf;
+ return *this;
+}
+
+
+////////////////
+// Inspectors //
+////////////////
+const vec3d& surfaceSample::position(void) const
+{
+ return _position;
+}
+
+
+const vec3d& surfaceSample::normal(void) const
+{
+ return _normal;
+}
+
+
+float surfaceSample::pdf(void) const
+{
+ return _pdf;
+}
+
+
+/////////////
+// Methods //
+/////////////
+surfaceSample& surfaceSample::transform(const transformation3d& t)
+{
+ _position = t.transformPoint(_position);
+ _normal = t.transformNormal(_normal);
+ return *this;
+}
+
+
+surfaceSample& surfaceSample::inverseTransform(const transformation3d& t)
+{
+ _position = t.inverseTransformPoint(_position);
+ _normal = t.inverseTransformNormal(_normal);
+ return *this;
+}
+
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void surfaceSample::_swap(surfaceSample& s)
+{
+ swap(_position, s._position);
+ swap(_normal, s._normal);
+ std::swap(_pdf, s._pdf);
+}
+
+
+void surfaceSample::_assign(const surfaceSample& s)
+{
+ _position = s._position;
+ _normal = s._normal;
+ _pdf = s._pdf;
+}
diff --git a/hw6/src/texture_base.cpp b/hw6/src/texture_base.cpp
new file mode 100644
index 0000000..2a90146
--- /dev/null
+++ b/hw6/src/texture_base.cpp
@@ -0,0 +1,95 @@
+/******************************************************************/
+/* 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 "texture_base.h"
+
+
+//////////////////
+// Constructors //
+//////////////////
+texture_base::texture_base(bool repeat)
+ : image()
+{
+ _repeat = repeat;
+}
+
+texture_base::texture_base(const texture_base& src)
+ : image(src)
+{
+ _repeat = src._repeat;
+}
+
+
+///////////////
+// Operators //
+///////////////
+texture_base& texture_base::operator=(const texture_base& src)
+{
+ _assign(src);
+ return *this;
+}
+
+
+/////////////////////
+// Private Methods //
+/////////////////////
+void texture_base::_assign(const texture_base& src)
+{
+ image::_assign(src);
+ _repeat = src._repeat;
+}
+
+
+static void repeatCoordinate(signed int& p, signed int maxCoord)
+{
+ if(p < 0) p = 0;
+ else if(p >= maxCoord) p = maxCoord-1;
+}
+
+static void wrapCoordinate(signed int& p, signed int maxCoord)
+{
+ if(p < 0) p = (maxCoord + (p % maxCoord)) % maxCoord;
+ else if(p >= maxCoord) p %= maxCoord;
+}
+
+color& texture_base::_at(signed int x, signed int y)
+{
+ // repeat
+ if(_repeat)
+ {
+ repeatCoordinate(x, width());
+ repeatCoordinate(y, height());
+ }
+ // wrap
+ else {
+ wrapCoordinate(x, width());
+ wrapCoordinate(y, height());
+ }
+
+ // get value
+ return image::operator()(x,y);
+}
+
+
+const color& texture_base::_at(signed int x, signed int y) const
+{
+ // repeat
+ if(_repeat)
+ {
+ repeatCoordinate(x, width());
+ repeatCoordinate(y, height());
+ }
+ // wrap
+ else {
+ wrapCoordinate(x, width());
+ wrapCoordinate(y, height());
+ }
+
+ // get value
+ return image::operator()(x,y);
+}
diff --git a/hw6/src/tinyxml2.cpp b/hw6/src/tinyxml2.cpp
new file mode 100644
index 0000000..0c903fe
--- /dev/null
+++ b/hw6/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]
+ // &#20013; or &#x4e2d;
+
+ 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/hw6/src/transformation3d.cpp b/hw6/src/transformation3d.cpp
new file mode 100644
index 0000000..bde9caa
--- /dev/null
+++ b/hw6/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/hw6/src/translation3d.cpp b/hw6/src/translation3d.cpp
new file mode 100644
index 0000000..a8facd3
--- /dev/null
+++ b/hw6/src/translation3d.cpp
@@ -0,0 +1,29 @@
+/******************************************************************/
+/* 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 "translation3d.h"
+
+//////////////////
+// Constructors //
+//////////////////
+translation3d::translation3d(void) : transformation3d()
+{
+ // Do Nothing
+}
+
+
+translation3d::translation3d(const vec3d& translation) : transformation3d()
+{
+ _translation = translation;
+}
+
+
+translation3d::translation3d(const translation3d& t) : transformation3d(t)
+{
+ // Do Nothing
+}
diff --git a/hw6/src/triangle.cpp b/hw6/src/triangle.cpp
new file mode 100644
index 0000000..940363e
--- /dev/null
+++ b/hw6/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 * e1.cross(e2).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/hw6/src/triangleMesh.cpp b/hw6/src/triangleMesh.cpp
new file mode 100644
index 0000000..5507a9c
--- /dev/null
+++ b/hw6/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/hw6/src/util.cpp b/hw6/src/util.cpp
new file mode 100644
index 0000000..917eef4
--- /dev/null
+++ b/hw6/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/hw6/src/vec2d.cpp b/hw6/src/vec2d.cpp
new file mode 100644
index 0000000..ae12e41
--- /dev/null
+++ b/hw6/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/hw6/src/vec3d.cpp b/hw6/src/vec3d.cpp
new file mode 100644
index 0000000..3b20ad5
--- /dev/null
+++ b/hw6/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);
+}