From db072ad4dc181eca5a1458656b130beb43f475bf Mon Sep 17 00:00:00 2001 From: 53hornet <53hornet@gmail.com> Date: Sat, 2 Feb 2019 23:33:15 -0500 Subject: Init. --- hw6/src/areaLightsource.cpp | 87 + hw6/src/bilinearTexture.cpp | 69 + hw6/src/boundedCompound.cpp | 139 ++ hw6/src/boundedPrimitive.cpp | 50 + hw6/src/boundedTriangle.cpp | 67 + hw6/src/boundedVolumeNode.cpp | 97 ++ hw6/src/boundingBox.cpp | 195 +++ hw6/src/brdfSample.cpp | 116 ++ hw6/src/bumpMap.cpp | 48 + hw6/src/bvh_intersector.cpp | 54 + hw6/src/camera.cpp | 150 ++ hw6/src/color.cpp | 233 +++ hw6/src/compoundShader.cpp | 141 ++ hw6/src/coordinateTransform.cpp | 67 + hw6/src/diffuseBrdf.cpp | 111 ++ hw6/src/diffuseReflectanceShader.cpp | 35 + hw6/src/directionalLightsource.cpp | 47 + hw6/src/environmentMap.cpp | 79 + hw6/src/errorMessage.cpp | 34 + hw6/src/image.cpp | 122 ++ hw6/src/imageIO.cpp | 46 + hw6/src/imageIO.pfm.cpp | 88 + hw6/src/imageIO.ppm.cpp | 106 ++ hw6/src/importOBJ.cpp | 152 ++ hw6/src/intersectionPoint.cpp | 342 ++++ hw6/src/interval.cpp | 154 ++ hw6/src/lightSample.cpp | 120 ++ hw6/src/lightsourceGeometry.cpp | 54 + hw6/src/lightsourceShader.cpp | 72 + hw6/src/linear_intersector.cpp | 53 + hw6/src/mat3d.cpp | 260 +++ hw6/src/nearestTexture.cpp | 52 + hw6/src/normalMap.cpp | 44 + hw6/src/pathtracing.cpp | 175 ++ hw6/src/phongBrdf.cpp | 133 ++ hw6/src/phongReflectanceShader.cpp | 36 + hw6/src/pointLightsource.cpp | 55 + hw6/src/ray.cpp | 83 + hw6/src/ray_util.cpp | 27 + hw6/src/raycasting.cpp | 47 + hw6/src/recursiveRaytracing.cpp | 94 ++ hw6/src/reflectanceParameter.cpp | 140 ++ hw6/src/reflectanceShader_base.cpp | 85 + hw6/src/rotation3d.cpp | 47 + hw6/src/rotationX3d.cpp | 42 + hw6/src/rotationY3d.cpp | 42 + hw6/src/rotationZ3d.cpp | 42 + hw6/src/scale3d.cpp | 41 + hw6/src/scene.cpp | 60 + hw6/src/sceneGraphNode.cpp | 39 + hw6/src/sceneIO.cpp | 93 ++ hw6/src/sceneIO_basis.cpp | 100 ++ hw6/src/sceneIO_core.cpp | 99 ++ hw6/src/sceneIO_geometry.cpp | 187 +++ hw6/src/sceneIO_light.cpp | 131 ++ hw6/src/sceneIO_material.cpp | 243 +++ hw6/src/sceneIO_texture.cpp | 55 + hw6/src/sceneIO_transformation3d.cpp | 80 + hw6/src/sceneIO_xml.cpp | 112 ++ hw6/src/shaderProperties.cpp | 62 + hw6/src/shadingFrameTransformation.cpp | 82 + hw6/src/spotLightsource.cpp | 69 + hw6/src/surfaceSample.cpp | 118 ++ hw6/src/texture_base.cpp | 95 ++ hw6/src/tinyxml2.cpp | 2794 ++++++++++++++++++++++++++++++++ hw6/src/transformation3d.cpp | 174 ++ hw6/src/translation3d.cpp | 29 + hw6/src/triangle.cpp | 465 ++++++ hw6/src/triangleMesh.cpp | 57 + hw6/src/util.cpp | 45 + hw6/src/vec2d.cpp | 258 +++ hw6/src/vec3d.cpp | 287 ++++ 72 files changed, 10307 insertions(+) create mode 100644 hw6/src/areaLightsource.cpp create mode 100644 hw6/src/bilinearTexture.cpp create mode 100644 hw6/src/boundedCompound.cpp create mode 100644 hw6/src/boundedPrimitive.cpp create mode 100644 hw6/src/boundedTriangle.cpp create mode 100644 hw6/src/boundedVolumeNode.cpp create mode 100644 hw6/src/boundingBox.cpp create mode 100644 hw6/src/brdfSample.cpp create mode 100644 hw6/src/bumpMap.cpp create mode 100644 hw6/src/bvh_intersector.cpp create mode 100644 hw6/src/camera.cpp create mode 100644 hw6/src/color.cpp create mode 100644 hw6/src/compoundShader.cpp create mode 100644 hw6/src/coordinateTransform.cpp create mode 100644 hw6/src/diffuseBrdf.cpp create mode 100644 hw6/src/diffuseReflectanceShader.cpp create mode 100644 hw6/src/directionalLightsource.cpp create mode 100644 hw6/src/environmentMap.cpp create mode 100644 hw6/src/errorMessage.cpp create mode 100644 hw6/src/image.cpp create mode 100644 hw6/src/imageIO.cpp create mode 100644 hw6/src/imageIO.pfm.cpp create mode 100644 hw6/src/imageIO.ppm.cpp create mode 100644 hw6/src/importOBJ.cpp create mode 100644 hw6/src/intersectionPoint.cpp create mode 100644 hw6/src/interval.cpp create mode 100644 hw6/src/lightSample.cpp create mode 100644 hw6/src/lightsourceGeometry.cpp create mode 100644 hw6/src/lightsourceShader.cpp create mode 100644 hw6/src/linear_intersector.cpp create mode 100644 hw6/src/mat3d.cpp create mode 100644 hw6/src/nearestTexture.cpp create mode 100644 hw6/src/normalMap.cpp create mode 100644 hw6/src/pathtracing.cpp create mode 100644 hw6/src/phongBrdf.cpp create mode 100644 hw6/src/phongReflectanceShader.cpp create mode 100644 hw6/src/pointLightsource.cpp create mode 100644 hw6/src/ray.cpp create mode 100644 hw6/src/ray_util.cpp create mode 100644 hw6/src/raycasting.cpp create mode 100644 hw6/src/recursiveRaytracing.cpp create mode 100644 hw6/src/reflectanceParameter.cpp create mode 100644 hw6/src/reflectanceShader_base.cpp create mode 100644 hw6/src/rotation3d.cpp create mode 100644 hw6/src/rotationX3d.cpp create mode 100644 hw6/src/rotationY3d.cpp create mode 100644 hw6/src/rotationZ3d.cpp create mode 100644 hw6/src/scale3d.cpp create mode 100644 hw6/src/scene.cpp create mode 100644 hw6/src/sceneGraphNode.cpp create mode 100644 hw6/src/sceneIO.cpp create mode 100644 hw6/src/sceneIO_basis.cpp create mode 100644 hw6/src/sceneIO_core.cpp create mode 100644 hw6/src/sceneIO_geometry.cpp create mode 100644 hw6/src/sceneIO_light.cpp create mode 100644 hw6/src/sceneIO_material.cpp create mode 100644 hw6/src/sceneIO_texture.cpp create mode 100644 hw6/src/sceneIO_transformation3d.cpp create mode 100644 hw6/src/sceneIO_xml.cpp create mode 100644 hw6/src/shaderProperties.cpp create mode 100644 hw6/src/shadingFrameTransformation.cpp create mode 100644 hw6/src/spotLightsource.cpp create mode 100644 hw6/src/surfaceSample.cpp create mode 100644 hw6/src/texture_base.cpp create mode 100644 hw6/src/tinyxml2.cpp create mode 100644 hw6/src/transformation3d.cpp create mode 100644 hw6/src/translation3d.cpp create mode 100644 hw6/src/triangle.cpp create mode 100644 hw6/src/triangleMesh.cpp create mode 100644 hw6/src/util.cpp create mode 100644 hw6/src/vec2d.cpp create mode 100644 hw6/src/vec3d.cpp (limited to 'hw6/src') 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 +#include "constants.h" +#include "areaLightsource.h" + +////////////////// +// Constructors // +////////////////// +areaLightsource::areaLightsource(const color& power, const std::shared_ptr& 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 +#include + +#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& 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& 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& 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& 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& 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 +#include + +#include "boundedVolumeNode.h" +#include "random_number.h" + +boundedVolumeNode::boundedVolumeNode(const std::vector>::iterator& start, const std::vector>::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& 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& 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(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(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 +#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 +#include +#include "bumpMap.h" +#include "coordinateTransform.h" + +////////////////// +// Constructors // +////////////////// +bumpMap::bumpMap(const std::shared_ptr& map, float scale, unsigned int channel, const std::shared_ptr& 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>& compounds) + : intersector_base() +{ + // quick bail out if no compounds provided + if(compounds.empty()) return; + + // make a copy + std::vector> 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(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 + +#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 +#include +#include +#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 +#include "compoundShader.h" +#include "random_number.h" +#include // DEBUG + +////////////////// +// Constructors // +////////////////// +compoundShader::compoundShader(const std::vector>& 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& 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& 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& 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& 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 +#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 diffuseReflectanceShader::make_brdf(const vec2d& textureCoord) const +{ + return std::unique_ptr(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 +#include "constants.h" +#include "environmentMap.h" + + +////////////////// +// Constructors // +////////////////// +environmentMap::environmentMap(const std::shared_ptr& 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 +#include + +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 +#include + +////////////////// +// 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 +#include +#include + + +////////////////////// +// 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 +#include +#include +#include +#include + +////////////////////// +// 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 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 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 +#include +#include +#include + +#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_list) +{ + // define buffers + auto vertex_list = std::make_shared>(); + auto normal_list = std::make_shared>(); + auto textureCoord_list = std::make_shared>(); + + // 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 vidx; + std::vector nidx; + std::vector 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 +#include "shader_base.h" +#include "intersectionPoint.h" + +///////////////// +// Constructor // +///////////////// +intersectionPoint::intersectionPoint(void) +{ + _hit = false; +} + + +intersectionPoint::intersectionPoint(const ray& r, float rayParameter, const std::shared_ptr& 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& 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 +#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 +#include "lightsourceShader.h" +#include "lightsourceGeometry.h" + +///////////////// +// Constructor // +///////////////// +lightsourceGeometry::lightsourceGeometry(void) + : boundedCompound() +{ + // Nothing. +} + + +lightsourceGeometry::lightsourceGeometry(const std::shared_ptr& geometry, const std::shared_ptr& ls, const std::shared_ptr& material) + : boundedCompound() +{ + assert(geometry && ls); + _geometry.push_back(geometry); + _shader = std::shared_ptr(new lightsourceShader(ls, material)); + + // Done. + initializeBoundingBox(); +} + + +///////////// +// Methods // +///////////// +bool lightsourceGeometry::hasShader(void) const +{ + // always has a shader by construction + return true; +} + + +const std::vector>& 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 +#include "lightsourceShader.h" + + +////////////////// +// Constructors // +////////////////// +lightsourceShader::lightsourceShader(const std::shared_ptr& lightsource, const std::shared_ptr& 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>& 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 +#include + +#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 +#include "normalMap.h" +#include "coordinateTransform.h" + +////////////////// +// Constructors // +////////////////// +normalMap::normalMap(const std::shared_ptr& map, const std::shared_ptr& 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 +#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 phongReflectanceShader::make_brdf(const vec2d& textureCoord) const +{ + return std::unique_ptr(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& 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& 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 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 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 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 brdf = make_brdf(ip.textureCoordinate()); + + // get reflectivity + return brdf->reflectivity(); +} + + +shaderProperties reflectanceShader_base::properties(const intersectionPoint& ip) const +{ + // get brdf + std::unique_ptr 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 +#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 +#include "sceneGraphNode.h" + +////////////////// +// Constructors // +////////////////// +sceneGraphNode::sceneGraphNode(void) + : boundedCompound() +{ + // Do nothing +} + +sceneGraphNode::sceneGraphNode(const std::vector>& nodes, const transformation3d& transform, const std::shared_ptr& shader) + : boundedCompound(transform, shader) +{ + // copy nodes + _nodes = nodes; + + // init bounding box + initializeBoundingBox(); +} + + +///////////// +// Methods // +///////////// +const std::vector>& 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 shape_cache; + nodeCache texture_cache; + nodeCache shader_cache; + + // init scene data + bool autoTuneCamera = false; + std::unique_ptr intersector(new linear_intersector_factory()); + s._renderEngine = std::unique_ptr(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(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 root(new sceneGraphNode( *(reinterpret_cast< std::vector>* >(&unusedNodes)) )); + root->initialize(*intersector); + s._sceneGraphRoot = std::unique_ptr(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 + +#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& 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( new raycasting() ); + else if(type == "recursiveRaytracing") renderer = std::unique_ptr( new recursiveRaytracing( getInteger(node, "maxDepth", 1), getInteger(node, "samplesPerPixel", 1) ) ); + else if(type == "pathtracing") renderer = std::unique_ptr( 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& 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( new linear_intersector_factory()); + else if(type == "bvh") intersector = std::unique_ptr( new bvh_intersector_factory()); + else errorMessage("Unknown intersector type (%s)", type.c_str()); + + // Done. +} + + +void importEnvironmentMap(const XMLNode& node, nodeCache& texture_cache, const std::string& rootDir, std::unique_ptr& map) +{ + // sanity check + assert(node.name() == "environmentMap"); + + // node properties + transformation3d transform; + std::shared_ptr 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(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 +#include + +#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 +#include +#include +#include +#include + +#include "sceneIO_xml.h" +#include "sceneIO_core.h" +#include "sceneIO_cache.h" +#include "sceneIO_geometry.h" +#include "sceneIO_material.h" +#include "sceneIO_transformation3d.h" + +#include "util.h" +#include "vec2d.h" +#include "vec3d.h" +#include "triangle.h" +#include "constants.h" +#include "importOBJ.h" +#include "errorMessage.h" +#include "triangleMesh.h" +#include "random_number.h" +#include "sceneGraphNode.h" + +static void importTriangle(const XMLNode& node, std::vector& triangle_list) +{ + // sanity check + assert(node.name() == "triangle" || node.name() == "polygon"); + + // allocate temp data + unsigned int idx=0; + vec3d v[3], n[3]; + vec2d t[3]; + + // for each child node, parse all vertices + for(XMLNode child = node.firstChild(); child.isValid(); child++) + { + std::string name = child.name(); + if(name == "vertex") + { + v[idx] = getVec3d(child, "v", vec3d(+LARGE)); + n[idx] = getVec3d(child, "n", vec3d(0.0f)); + t[idx] = getVec2d(child, "t", vec2d(+LARGE)); + + // check if valid vertex + if(v[idx].x >= +LARGE || v[idx].y >= +LARGE || v[idx].z >= +LARGE) + errorMessage("Invalid vertex in %s definition.", node.name().c_str()); + + // create triangle + if(idx == 2) + { + // determine which attributes are defined on the triangle + bool hasNormals = (n[0].squared_length() > +EPSILON) && (n[0].squared_length() > +EPSILON) && (n[0].squared_length() > +EPSILON); + bool hasTextureCoord = (fabs(t[0].x) < +LARGE && fabs(t[0].y) < +LARGE) && (fabs(t[1].x) < +LARGE && fabs(t[1].y) < +LARGE) && (fabs(t[2].x) < +LARGE && fabs(t[2].y) < +LARGE); + + if(!hasNormals && !hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2])); + else if(hasNormals && !hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2], n[0], n[1], n[2])); + else if(!hasNormals && hasTextureCoord) triangle_list.push_back(triangle(v[0], v[1], v[2], t[0], t[1], t[2])); + else triangle_list.push_back(triangle(v[0], v[1], v[2], n[0], n[1], n[2], t[0], t[1], t[2])); + + // cycle (to support polygons) + v[1] = v[2]; + n[1] = n[2]; + t[1] = t[2]; + } + else idx++; + } + else errorMessage("Unknown child element in %s node (%s).", node.name().c_str(), name.c_str()); + } + + // check if at least one full triangle was found + if(idx != 2) + errorMessage("%s requires at least 3 vertices.", node.name().c_str()); + + // Done. + } + + +///////////////////////////////////////////////////////////////// +static std::shared_ptr importTriangleMesh(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) +{ + // sanity check + assert(node.name() == "triangleMesh"); + + // allocate node properties + transformation3d transform; + std::shared_ptr material; + std::vector triangle_list; + + // import OBJ triangles + std::string objname = getString(node, "filename"); + if(objname != "") + { + std::string path = getDirectory(objname); + if(path == "") objname = rootDir + objname; + importOBJ(objname, triangle_list); + } + + // check child nodes + for(XMLNode child = node.firstChild(); child.isValid(); child++) + { + std::string name = child.name(); + + // general child nodes + auto tempmat = importMaterial(child, shader_cache, texture_cache, rootDir); + if(tempmat) material = tempmat; + + // specialized child nodes + else if(name == "transformation") importTransformation(child, transform); + else if(name == "triangle" || name == "polygon") importTriangle(child, triangle_list); + else errorMessage("Unknown child-node in triangleMesh (%s).", name.c_str()); + } + + // Done. + return std::shared_ptr(new triangleMesh(triangle_list, material, transform)); +} + +///////////////////////////////////////////////////////////////// + +static std::shared_ptr importSceneGraphNode(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) +{ + // sanity check + assert(node.name() == "sceneGraphNode"); + + // node properties + transformation3d transform; + std::shared_ptr material; + std::vector> child_nodes; + + // check child nodes + for(XMLNode child = node.firstChild(); child.isValid(); child++) + { + std::string name = child.name(); + + // general child nodes + auto tempmat = importMaterial(child, shader_cache, texture_cache, rootDir); + auto shape = importGeometry(child, shape_cache, shader_cache, texture_cache, rootDir); + + if(tempmat) material = tempmat; + else if(shape) child_nodes.push_back(shape); + + // node specific children. + else if(name == "transformation") importTransformation(child, transform); + else errorMessage("Unknown child-node in sceneGraphNode (%s).", name.c_str()); + } + + // Done. + return std::shared_ptr(new sceneGraphNode(child_nodes, transform, material)); +} + +///////////////////////////////////////////////////////////////// +std::shared_ptr importGeometry(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) +{ + // check if geometry type + if(node.name() != "sceneGraphNode" && node.name() != "triangleMesh") return std::shared_ptr(nullptr); + + // check if reference + std::string ref = getString(node, "ref"); + std::shared_ptr shape = shape_cache.get(ref); + if(shape) return shape; + else if(ref != "") errorMessage("Unknown %s name (%s).", node.name().c_str(), ref.c_str()); + + // generate default name & get id + char defaultname[32]; sprintf(defaultname, "noname-%u", random_int()); + std::string id = getString(node, "id", defaultname); + + if(shape_cache.get(id)) + errorMessage("%s-node id is not unique (%s).", node.name().c_str(), id.c_str()); + + // process primitive based on type + if(node.name() == "sceneGraphNode") shape = importSceneGraphNode(node, shape_cache, shader_cache, texture_cache, rootDir); + else if(node.name() == "triangleMesh") shape = importTriangleMesh(node, shape_cache, shader_cache, texture_cache, rootDir); + + // update cache + if(id != "") shape_cache.add(id, shape); + + // Done. + return shape; +} 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 + +#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 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( new directionalLightsource( direction, power)); +} + + +static std::shared_ptr 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( new pointLightsource(position, power, attenuation)); +} + + +static std::shared_ptr 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(new spotLightsource( position, direction, cutoff, sharpness, power, attenuation )); +} + + +static std::shared_ptr importAreaLight(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& 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 reflectanceMaterial; + std::shared_ptr 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 ls( new areaLightsource(power, geometry, attenuation) ); + std::shared_ptr 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 importLight(const XMLNode& node, nodeCache& shape_cache, nodeCache& shader_cache, nodeCache& 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 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 + +#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_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 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_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 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 importDiffuse(const XMLNode& node, nodeCache& shader_cache, nodeCache& 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(new diffuseReflectanceShader(albedo)); +} + +////////////////////////////////////////////////////////////// +static std::shared_ptr importPhong(const XMLNode& node, nodeCache& shader_cache, nodeCache& 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(new phongReflectanceShader(albedo, sharpness)); +} + +////////////////////////////////////////////////////////////// +static std::shared_ptr importCompoundMaterial(const XMLNode& node, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) +{ + // sanity check + assert(node.isValid() && node.name() == "material"); + + // storage for child material nodes + std::vector> shader_list; + + // for each child + for(XMLNode child = node.firstChild(); child.isValid(); child++) + { + // try to import material + std::shared_ptr 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(new compoundShader(shader_list)); +} + +////////////////////////////////////////////////////////////// +std::shared_ptr importNormalMap(const XMLNode& node, nodeCache& shader_cache, nodeCache& texture_cache, const std::string& rootDir) +{ + // sanity check + assert(getString(node, "type") == "normalMap"); + + // parameter storage + std::shared_ptr shader; + std::shared_ptr 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(new normalMap(map, shader)); +} + +////////////////////////////////////////////////////////////// +std::shared_ptr importBumpMap(const XMLNode& node, nodeCache& shader_cache, nodeCache& 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 shader; + std::shared_ptr 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(new bumpMap(map, scale, channel, shader)); +} + +////////////////////////////////////////////////////////////// +std::shared_ptr importShadingFrameTransformation(const XMLNode& node, nodeCache& shader_cache, nodeCache& 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 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 importMaterial(const XMLNode& node, nodeCache& shader_cache, nodeCache& 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(nullptr); + + // check if reference + std::string ref = getString(node, "ref"); + std::shared_ptr 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 importTexture(const XMLNode& node, nodeCache& texture_cache, const std::string& rootDir) +{ + // check if texture + if(!node.isValid() || node.name() != "texture") return std::shared_ptr(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 ctexture = texture_cache.get(key)) + return ctexture; + + // create based on type + std::shared_ptr texture; + if(type == "nearest") texture = std::shared_ptr( new nearestTexture(repeatTexture)); + else if(type == "bilinear") texture = std::shared_ptr( 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 +#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 +#include "sceneIO_xml.h" +#include "errorMessage.h" + +///////////////// +// Constructor // +///////////////// +XMLNode::XMLNode(const std::string& filename) +{ + // open xml + _xml = std::make_shared(); + _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& 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& 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 +#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 +#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 // yes, this one new style header, is in the Android SDK. +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +#else +# include +# include +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + // Microsoft Visual Studio, version 2005 and higher. Not WinCE. + /*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... + );*/ + static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) + { + va_list va; + va_start( va, format ); + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; + } + + static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) + { + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + return result; + } + + #define TIXML_VSCPRINTF _vscprintf + #define TIXML_SSCANF sscanf_s +#elif defined _MSC_VER + // Microsoft Visual Studio 2003 and earlier or WinCE + #define TIXML_SNPRINTF _snprintf + #define TIXML_VSNPRINTF _vsnprintf + #define TIXML_SSCANF sscanf + #if (_MSC_VER < 1400 ) && (!defined WINCE) + // Microsoft Visual Studio 2003 and not WinCE. + #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. + #else + // Microsoft Visual Studio 2003 and earlier or WinCE. + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = 512; + for (;;) { + len = len*2; + char* str = new char[len](); + const int required = _vsnprintf(str, len, format, va); + delete[] str; + if ( required != -1 ) { + TIXMLASSERT( required >= 0 ); + len = required; + break; + } + } + TIXMLASSERT( len >= 0 ); + return len; + } + #endif +#else + // GCC version 3 and higher + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_VSNPRINTF vsnprintf + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = vsnprintf( 0, 0, format, va ); + TIXMLASSERT( len >= 0 ); + return len; + } + #define TIXML_SSCANF sscanf +#endif + + +static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF +static const char LF = LINE_FEED; +static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out +static const char CR = CARRIAGE_RETURN; +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '\"'; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// ef bb bf (Microsoft "lead bytes") - designates UTF-8 + +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +namespace tinyxml2 +{ + +struct Entity { + const char* pattern; + int length; + char value; +}; + +static const int NUM_ENTITIES = 5; +static const Entity entities[NUM_ENTITIES] = { + { "quot", 4, DOUBLE_QUOTE }, + { "amp", 3, '&' }, + { "apos", 4, SINGLE_QUOTE }, + { "lt", 2, '<' }, + { "gt", 2, '>' } +}; + + +StrPair::~StrPair() +{ + Reset(); +} + + +void StrPair::TransferTo( StrPair* other ) +{ + if ( this == other ) { + return; + } + // This in effect implements the assignment operator by "moving" + // ownership (as in auto_ptr). + + TIXMLASSERT( other != 0 ); + TIXMLASSERT( other->_flags == 0 ); + TIXMLASSERT( other->_start == 0 ); + TIXMLASSERT( other->_end == 0 ); + + other->Reset(); + + other->_flags = _flags; + other->_start = _start; + other->_end = _end; + + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::Reset() +{ + if ( _flags & NEEDS_DELETE ) { + delete [] _start; + } + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::SetStr( const char* str, int flags ) +{ + TIXMLASSERT( str ); + Reset(); + size_t len = strlen( str ); + TIXMLASSERT( _start == 0 ); + _start = new char[ len+1 ]; + memcpy( _start, str, len+1 ); + _end = _start + len; + _flags = flags | NEEDS_DELETE; +} + + +char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( endTag && *endTag ); + TIXMLASSERT(curLineNumPtr); + + char* start = p; + char endChar = *endTag; + size_t length = strlen( endTag ); + + // Inner loop of text parsing. + while ( *p ) { + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { + Set( start, p, strFlags ); + return p + length; + } else if (*p == '\n') { + ++(*curLineNumPtr); + } + ++p; + TIXMLASSERT( p ); + } + return 0; +} + + +char* StrPair::ParseName( char* p ) +{ + if ( !p || !(*p) ) { + return 0; + } + if ( !XMLUtil::IsNameStartChar( *p ) ) { + return 0; + } + + char* const start = p; + ++p; + while ( *p && XMLUtil::IsNameChar( *p ) ) { + ++p; + } + + Set( start, p, 0 ); + return p; +} + + +void StrPair::CollapseWhitespace() +{ + // Adjusting _start would cause undefined behavior on delete[] + TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); + // Trim leading space. + _start = XMLUtil::SkipWhiteSpace( _start, 0 ); + + if ( *_start ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( *p ) { + if ( XMLUtil::IsWhiteSpace( *p )) { + p = XMLUtil::SkipWhiteSpace( p, 0 ); + if ( *p == 0 ) { + break; // don't write to q; this trims the trailing space. + } + *q = ' '; + ++q; + } + *q = *p; + ++q; + ++p; + } + *q = 0; + } +} + + +const char* StrPair::GetStr() +{ + TIXMLASSERT( _start ); + TIXMLASSERT( _end ); + if ( _flags & NEEDS_FLUSH ) { + *_end = 0; + _flags ^= NEEDS_FLUSH; + + if ( _flags ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( p < _end ) { + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { + // CR-LF pair becomes LF + // CR alone becomes LF + // LF-CR becomes LF + if ( *(p+1) == LF ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { + if ( *(p+1) == CR ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { + // Entities handled by tinyXML2: + // - special entities in the entity table [in/out] + // - numeric character reference [in] + // 中 or 中 + + if ( *(p+1) == '#' ) { + const int buflen = 10; + char buf[buflen] = { 0 }; + int len = 0; + char* adjusted = const_cast( 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(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 = { "( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += xmlHeaderLen; + } + else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += commentHeaderLen; + } + else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { + XMLText* text = CreateUnlinkedNode( _textPool ); + returnNode = text; + returnNode->_parseLineNum = _parseCurLineNum; + p += cdataHeaderLen; + text->SetCData( true ); + } + else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += dtdHeaderLen; + } + else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _elementPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += elementHeaderLen; + } + else { + returnNode = CreateUnlinkedNode( _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: + // + // + // + // With a special case: + // + // + // + // + // 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; +} + +// +// +// foobar +// +char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) +{ + // Read the element name. + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + + // The closing element is the 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( _elementPool ); + ele->SetName( name ); + return ele; +} + + +XMLComment* XMLDocument::NewComment( const char* str ) +{ + XMLComment* comment = CreateUnlinkedNode( _commentPool ); + comment->SetValue( str ); + return comment; +} + + +XMLText* XMLDocument::NewText( const char* str ) +{ + XMLText* text = CreateUnlinkedNode( _textPool ); + text->SetValue( str ); + return text; +} + + +XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) +{ + XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); + return dec; +} + + +XMLUnknown* XMLDocument::NewUnknown( const char* str ) +{ + XMLUnknown* unk = CreateUnlinkedNode( _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 : ""); + 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 += sizeof(size_t))> +struct LongFitsIntoSizeTMinusOne { + static bool Fits( unsigned long value ) + { + return value < (size_t)-1; + } +}; + +template <> +struct LongFitsIntoSizeTMinusOne { + 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 : ""); + 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( 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'] = 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(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 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( 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 ( "" ); + } + + 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( "" ); + } + 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( "" ); +} + + +void XMLPrinter::PushDeclaration( const char* value ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + _firstElement = false; + + Write( "" ); +} + + +void XMLPrinter::PushUnknown( const char* value ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + _firstElement = false; + + Write( "' ); +} + + +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 +#include + +#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>(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>& 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>(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>& vertex_list, + size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr>& 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>(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>& vertex_list, + size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr>& 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>(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>& vertex_list, + size_t n1_idx, size_t n2_idx, size_t n3_idx, const std::shared_ptr>& normal_list, + size_t t1_idx, size_t t2_idx, size_t t3_idx, const std::shared_ptr>& 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 +#include "triangleMesh.h" + +///////////////// +// Constructors // +////////////////// +triangleMesh::triangleMesh(void) + : boundedCompound() +{ + // Nothing. +} + + +triangleMesh::triangleMesh(const std::vector& triangle_list, const std::shared_ptr& 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(tri, shader) ); + }); + + // Done. + initializeBoundingBox(); +} + + +///////////// +// Methods // +///////////// +const std::vector>& 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 +#include +#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 +#include +#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); +} -- cgit v1.2.3