diff --git a/src/SConscript b/src/SConscript index a3dffe4..fc8bb07 100644 --- a/src/SConscript +++ b/src/SConscript @@ -8,6 +8,7 @@ Import('env') objs = [] subdirs = [ + 'basics', 'yaml', ] @@ -17,9 +18,9 @@ for d in subdirs: files = [ - 'basics.cc', 'camera.cc', 'light.cc', + 'lightPoint.cc', 'log.cc', 'material.cc', 'object.cc', diff --git a/src/basics.cc b/src/basics.cc deleted file mode 100644 index fc4ddd7..0000000 --- a/src/basics.cc +++ /dev/null @@ -1,530 +0,0 @@ -/* basics.c - * - * Definition of basic types. - * - * - Vector3 is a three tuple vector of x, y, and z. - * - Ray is a vector plus a direction. - * - Color is a four tuple of red, green, blue, and alpha. - * - * Eryn Wells - */ - -#include -#include "basics.h" - -#pragma mark - Vectors - -const Vector3 Vector3::Zero = Vector3(); -const Vector3 Vector3::X = Vector3(1, 0, 0); -const Vector3 Vector3::Y = Vector3(0, 1, 0); -const Vector3 Vector3::Z = Vector3(0, 0, 1); - - -/* - * Vector3::Vector3 -- - * - * Default constructor. Create a zero vector. - */ -Vector3::Vector3() - : Vector3(0.0, 0.0, 0.0) -{ } - - -/* - * Vector3::Vector3 -- - * - * Constructor. Create a vector consisting of the given coordinates. - */ -Vector3::Vector3(Double _x, Double _y, Double _z) - : x(_x), y(_y), z(_z) -{ } - - -/* - * Vector3::operator= -- - * - * Copy the given vector's values into this vector. Return a reference to this vector. - */ -Vector3 & -Vector3::operator=(const Vector3 &v) -{ - x = v.x; - y = v.y; - z = v.z; - return *this; -} - - -/* - * Vector3::operator*= -- - * Vector3::operator/= -- - * Vector3::operator+= -- - * Vector3::operator-= -- - * - * Perform the corresponding arithmetic operation on this vector and the given vector. These methods are destructive and - * a reference to this vector is returned. - */ -Vector3 & -Vector3::operator*=(const Double &rhs) -{ - x *= rhs; - y *= rhs; - z *= rhs; - return *this; -} - -Vector3 & -Vector3::operator/=(const Double &rhs) -{ - return *this *= (1.0f / rhs); -} - -Vector3 & -Vector3::operator+=(const Vector3 &rhs) -{ - x += rhs.x; - y += rhs.y; - z += rhs.z; - return *this; -} - -Vector3 & -Vector3::operator-=(const Vector3 &rhs) -{ - return *this += -rhs; -} - - -/* - * Vector3::operator* -- - * Vector3::operator/ -- - * Vector3::operator+ -- - * Vector3::operator- -- - * - * Perform the corresponding operation on a copy of this vector. Return a new vector. - */ -Vector3 -Vector3::operator*(const Double &rhs) - const -{ - return Vector3(*this) *= rhs; -} - -Vector3 -Vector3::operator/(const Double &rhs) - const -{ - return Vector3(*this) /= rhs; -} - -Vector3 -Vector3::operator+(const Vector3 &rhs) - const -{ - return Vector3(*this) += rhs; -} - -Vector3 -Vector3::operator-(const Vector3 &rhs) - const -{ - return Vector3(*this) -= rhs; -} - - -/* - * Vector3::operator- -- - * - * Negate this vector. Return a new vector. - */ -Vector3 -Vector3::operator-() - const -{ - return Vector3(-x, -y, -z); -} - - -/* - * Vector3::operator== -- - * Vector3::operator!= -- - * - * Compute boolean equality and non-equality of this and the given vectors. - */ -bool -Vector3::operator==(const Vector3 &rhs) - const -{ - return x == rhs.x && y == rhs.y && z == rhs.z; -} - -bool -Vector3::operator!=(const Vector3 &rhs) - const -{ - return !(*this == rhs); -} - - -/* - * Vector3::length2 -- - * - * Compute and return the length-squared of this vector. - */ -Double -Vector3::length2() - const -{ - return x*x + y*y + z*z; -} - - -/* - * Vector3::length -- - * - * Compute and return the length of this vector. - */ -Double -Vector3::length() - const -{ - return sqrt(length2()); -} - - -/* - * Vector3::dot -- - * - * Compute and return the dot product of this and the given vectors. - */ -Double -Vector3::dot(const Vector3 &v) - const -{ - return x*v.x + y*v.y + z*v.z; -} - - -/* - * Vector3::cross -- - * - * Compute and return the cross product of this and the given vectors. - */ -Vector3 -Vector3::cross(const Vector3 &v) - const -{ - return Vector3(y*v.z - z*v.y, z*v.x - x*v.z, x*v.y - y*v.x); -} - - -/* - * Vector3::normalize -- - */ -Vector3 & -Vector3::normalize() -{ - // Use the overloaded /= compound operator to do this. - return *this /= length(); -} - - -/* - * Vector3::normalized -- - */ -Vector3 -Vector3::normalized() - const -{ - return *this / length(); -} - - -/* - * operator* -- - * - * Multiply the given float by the given vector. Return a new vector. - */ -const Vector3 -operator*(const Double &lhs, const Vector3 &rhs) -{ - return rhs * lhs; -} - - -std::ostream & -operator<<(std::ostream &os, const Vector3 &v) -{ - // Stream the vector like this: - os << "<" << v.x << ", " << v.y << ", " << v.z << ">"; - return os; -} - - -Vector3 -LinearCombination(const Double k1, const Vector3& v1, - const Double k2, const Vector3& v2, - const Double k3, const Vector3& v3) -{ - return Vector3(k1 * v1.x + k2 * v2.x + k3 * v3.x, - k1 * v1.y + k2 * v2.y + k3 * v3.y, - k1 * v1.z + k2 * v2.z + k3 * v3.z); -} - -#pragma mark - Rays - -/* - * Ray::Ray -- - * - * Default constructor. Create a ray at the origin (0, 0, 0) with direction (0, 0, 0). - */ -Ray::Ray() - : Ray(Vector3::Zero, Vector3::Zero) -{ } - - -/* - * Ray::Ray -- - * - * Constructor. Create a ray with the given origin and direction. - */ -Ray::Ray(Vector3 o, Vector3 d) - : origin(o), direction(d) -{ } - - -/* - * Ray::parameterize -- - * - * Compute and return the point given by parameterizing this Ray by time t. - */ -Vector3 -Ray::parameterize(const Double& t) - const -{ - return origin + t * direction; -} - - -std::ostream & -operator<<(std::ostream &os, const Ray &r) -{ - os << "[Ray " << r.origin << " " << r.direction << "]"; - return os; -} - -#pragma mark - Colors - -const Color Color::Black = Color(); -const Color Color::White = Color(1.0, 1.0, 1.0, 1.0); -const Color Color::Red = Color(1.0, 0.0, 0.0, 1.0); -const Color Color::Green = Color(0.0, 1.0, 0.0, 1.0); -const Color Color::Blue = Color(0.0, 0.0, 1.0, 1.0); - - -/* - * Color::Color -- - * - * Default constructor. Create a new Color with zeros for all components (black). - */ -Color::Color() - : Color(0.0, 0.0, 0.0, 0.0) -{ } - - -/* - * Color::Color -- - * - * Constructor. Create a new Color with the given RGB components. Alpha is 1.0. - */ -Color::Color(const float &r, const float &g, const float &b) - : Color(r, g, b, 1.0) -{ } - - -/* - * Color::Color -- - * - * Constructor. Create a new Color with the given components. - */ -Color::Color(const float &r, const float &g, const float &b, const float &a) - : red(r), green(g), blue(b), alpha(a) -{ } - - -/* - * Color::operator*= -- - * Color::operator/= -- - * Color::operator+= -- - * Color::operator-= -- - * - * Perform the corresponding arithmetic operation on this color and the given scalar. These methods are destructive and - * a reference to this color is returned. - */ -Color & -Color::operator*=(const float &rhs) -{ - red *= rhs; - green *= rhs; - blue *= rhs; - return *this; -} - -Color & -Color::operator/=(const float &rhs) -{ - return *this *= (1.0 / rhs); -} - -Color & -Color::operator+=(const float &rhs) -{ - red += rhs; - green += rhs; - blue += rhs; - alpha += rhs; - return *this; -} - -Color & -Color::operator-=(const float &rhs) -{ - return *this += -rhs; -} - - -/* - * Color::operator* -- - * Color::operator/ -- - * Color::operator+ -- - * Color::operator- -- - * - * Perform the corresponding operation on a copy of this color and the given scalar. Return a new vector. - */ -Color -Color::operator*(const float &rhs) - const -{ - return Color(*this) *= rhs; -} - -Color -Color::operator/(const float &rhs) - const -{ - return Color(*this) /= rhs; -} - -Color -Color::operator+(const float &rhs) - const -{ - return Color(*this) += rhs; -} - -Color -Color::operator-(const float &rhs) - const -{ - return Color(*this) -= rhs; -} - - -/* - * Color::operator= -- - * - * Copy the given color's values into this color. Return a reference to this color. - */ -Color & -Color::operator=(const Color &rhs) -{ - red = rhs.red; - green = rhs.green; - blue = rhs.blue; - alpha = rhs.alpha; - return *this; -} - - -Color & -Color::operator*=(const Color &rhs) -{ - red *= rhs.red; - green *= rhs.green; - blue *= rhs.blue; - return *this; -} - -Color & -Color::operator/=(const Color &rhs) -{ - red *= (1.0 / rhs.red); - green *= (1.0 / rhs.green); - blue *= (1.0 / rhs.blue); - return *this; -} - -Color & -Color::operator+=(const Color &rhs) -{ - red += rhs.red; - green += rhs.green; - blue += rhs.blue; - alpha += rhs.alpha; - return *this; -} - -Color & -Color::operator-=(const Color &rhs) -{ - red -= rhs.red; - green -= rhs.green; - blue -= rhs.blue; - alpha -= rhs.alpha; - return *this; -} - - -Color -Color::operator*(const Color &rhs) - const -{ - return Color(*this) *= rhs; -} - -Color -Color::operator/(const Color &rhs) - const -{ - return Color(*this) /= rhs; -} - -Color -Color::operator+(const Color &rhs) - const -{ - return Color(*this) += rhs; -} - -Color -Color::operator-(const Color &rhs) - const -{ - return Color(*this) -= rhs; -} - - -const Color -operator*(const float &lhs, const Color &rhs) -{ - return rhs * lhs; -} - - -std::ostream & -operator<<(std::ostream &os, const Color &c) -{ - // Stream colors like this: - os << "<" << c.red << ", " << c.green << ", " << c.blue << ", " << c.alpha << ">"; - return os; -} diff --git a/src/basics.h b/src/basics.h deleted file mode 100644 index d84c197..0000000 --- a/src/basics.h +++ /dev/null @@ -1,174 +0,0 @@ -/* basics.h - * vim: set tw=80: - * Eryn Wells - */ - -#ifndef __BASICS_H__ -#define __BASICS_H__ - -#include -#include - -#include "types.hh" - -/* - * XXX: THIS SHOULD NOT BE HERE. REMOVE IT WHEN MOVING TO CHARLES NAMESPACE IS - * DONE. - */ -using charles::Double; - - -/** - * A very small constant. If a value is between EPSILON and 0.0, it is - * considered to be zero. - */ -const Double EPSILON = 1.0e-10; - -/** - * The maximum distance a ray can travel. This is the maximum value t can be. - */ -const Double MAX_DISTANCE = 1.0e7; - - -/** - * Determine if the value is "close enough" to zero. Takes the absolute value - * and compares it to `EPSILON`. - * - * @see EPSILON - * - * @param [in] value The value to check - * @returns `true` if the value is close enough to zero - */ -template -inline bool -NearZero(const T value) -{ - return std::fabs(value) < EPSILON; -} - - -/** - * Determine if two values are "close enough" to be considered equal. Subtracts - * one from the other and checks if the result is near zero. - * - * @see NearZero - * - * @param [in] left The left parameter - * @param [in] right The right parameter - * @returns `true` if the values are close enough to be equal - */ -template -inline bool -NearlyEqual(const T left, - const T right) -{ - return NearZero(left - right); -} - - -inline bool -TooFar(const Double& value) -{ - return value > MAX_DISTANCE; -} - - -struct Vector3 -{ - Vector3(); - Vector3(Double x, Double y, Double z); - - Vector3 &operator=(const Vector3 &v); - Vector3 &operator*=(const Double &rhs); - Vector3 &operator/=(const Double &rhs); - Vector3 &operator+=(const Vector3 &rhs); - Vector3 &operator-=(const Vector3 &rhs); - Vector3 operator*(const Double &rhs) const; - Vector3 operator/(const Double &rhs) const; - Vector3 operator+(const Vector3 &rhs) const; - Vector3 operator-(const Vector3 &rhs) const; - Vector3 operator-() const; - - bool operator==(const Vector3 &rhs) const; - bool operator!=(const Vector3 &rhs) const; - - Double length2() const; - Double length() const; - Double dot(const Vector3 &v) const; - Vector3 cross(const Vector3 &v) const; - - /** Normalize and return a reference to this vector. */ - Vector3 &normalize(); - - /** Return a copy of this vector, normalized. Does not modify this vector. */ - Vector3 normalized() const; - - static const Vector3 Zero; - // Unit vectors in each of the three cartesian directions. - static const Vector3 X, Y, Z; - - Double x, y, z; -}; - -const Vector3 operator*(const Double &lhs, const Vector3 &rhs); -std::ostream &operator<<(std::ostream &os, const Vector3 &v); - - -Vector3 LinearCombination(const Double k1, const Vector3& v1, - const Double k2, const Vector3& v2, - const Double k3, const Vector3& v3); - - -struct Ray -{ - Ray(); - Ray(Vector3 o, Vector3 d); - - Vector3 parameterize(const Double& t) const; - - Vector3 origin, direction; -}; - -std::ostream &operator<<(std::ostream &os, const Ray &r); - - -struct Color -{ - Color(); - Color(const float &r, const float &g, const float &b); - Color(const float &r, const float &g, const float &b, const float &a); - - Color &operator*=(const float &rhs); - Color &operator/=(const float &rhs); - Color &operator+=(const float &rhs); - Color &operator-=(const float &rhs); - Color operator*(const float &rhs) const; - Color operator/(const float &rhs) const; - Color operator+(const float &rhs) const; - Color operator-(const float &rhs) const; - - Color &operator=(const Color &rhs); - - // These operators blend the two colors. - Color &operator*=(const Color &rhs); - Color &operator/=(const Color &rhs); - Color &operator+=(const Color &rhs); - Color &operator-=(const Color &rhs); - Color operator*(const Color &rhs) const; - Color operator/(const Color &rhs) const; - Color operator+(const Color &rhs) const; - Color operator-(const Color &rhs) const; - - static const Color Black; - static const Color White; - static const Color Red; - static const Color Green; - static const Color Blue; - - float red, green, blue, alpha; -}; - -const Color operator*(const float &lhs, const Color &rhs); -std::ostream &operator<<(std::ostream &os, const Color &c); - -#endif diff --git a/src/basics/SConscript b/src/basics/SConscript new file mode 100644 index 0000000..0f47e1c --- /dev/null +++ b/src/basics/SConscript @@ -0,0 +1,16 @@ +# SConscript +# vim: set ft=python: +# Eryn Wells + +Import('env') + + +files = [ + 'color.cc', + 'matrix.cc', + 'ray.cc', + 'vector.cc', +] + +objs = [env.Object(f) for f in files] +Return('objs') diff --git a/src/basics/basics.hh b/src/basics/basics.hh new file mode 100644 index 0000000..bedc145 --- /dev/null +++ b/src/basics/basics.hh @@ -0,0 +1,21 @@ +/* basics.hh + * vim: set tw=80: + * Eryn Wells + */ +/** + * Top-level include for the basics module. + */ + +#ifndef __BASICS_BASICS_HH__ +#define __BASICS_BASICS_HH__ + +#include + +#include "basics/color.hh" +#include "basics/matrix.hh" +#include "basics/ray.hh" +#include "basics/types.hh" +#include "basics/util.hh" +#include "basics/vector.hh" + +#endif /* __BASICS_BASICS_HH__ */ diff --git a/src/basics/color.cc b/src/basics/color.cc new file mode 100644 index 0000000..3c75aaf --- /dev/null +++ b/src/basics/color.cc @@ -0,0 +1,272 @@ +/* color.cc + * vim: set tw=80: + * Eryn Wells + */ + +#include + +#include "basics/color.hh" + +#include "basics/types.hh" + + +namespace charles { +namespace basics { + +const Color Color::Black = Color(); +const Color Color::White = Color(1, 1, 1); +const Color Color::Red = Color(1, 0, 0); +const Color Color::Green = Color(0, 1, 0); +const Color Color::Blue = Color(0, 0, 1); + + +/* + * charles::basics::Color::Color -- + */ +Color::Color() + : Color(0.0, 0.0, 0.0, 0.0) +{ } + + +/* + * charles::basics::Color::Color -- + */ +Color::Color(const Double& r, + const Double& g, + const Double& b, + const Double& a) + : red(r), + green(g), + blue(b), + alpha(a) +{ } + + +/* + * charles::basics::Color::operator= -- + */ +Color& +Color::operator=(const Color& rhs) +{ + red = rhs.red; + green = rhs.green; + blue = rhs.blue; + alpha = rhs.alpha; + return *this; +} + + +/* + * charles::basics::Color::operator*= -- + */ +Color& +Color::operator*=(const Double& rhs) +{ + red *= rhs; + green *= rhs; + blue *= rhs; + return *this; +} + + +/* + * charles::basics::Color::operator/= -- + */ +Color& +Color::operator/=(const Double& rhs) +{ + return *this *= (1.0 / rhs); +} + + +/* + * charles::basics::Color::operator+= -- + */ +Color& +Color::operator+=(const Double& rhs) +{ + red += rhs; + green += rhs; + blue += rhs; + alpha += rhs; + return *this; +} + + +/* + * charles::basics::Color::operator-= -- + */ +Color& +Color::operator-=(const Double& rhs) +{ + return *this += -rhs; +} + + +/* + * charles::basics::Color::operator* -- + */ +Color +Color::operator*(const Double& rhs) + const +{ + return Color(*this) *= rhs; +} + + +/* + * charles::basics::Color::operator/ -- + */ +Color +Color::operator/(const Double& rhs) + const +{ + return Color(*this) /= rhs; +} + + +/* + * charles::basics::Color::operator+ -- + */ +Color +Color::operator+(const Double& rhs) + const +{ + return Color(*this) += rhs; +} + + +/* + * charles::basics::Color::operator- -- + */ +Color +Color::operator-(const Double& rhs) + const +{ + return Color(*this) -= rhs; +} + + +/* + * charles::basics::Color::operator*= -- + */ +Color& +Color::operator*=(const Color& rhs) +{ + red *= rhs.red; + green *= rhs.green; + blue *= rhs.blue; + return *this; +} + + +/* + * charles::basics::Color::operator/= -- + */ +Color& +Color::operator/=(const Color& rhs) +{ + red /= rhs.red; + green /= rhs.green; + blue /= rhs.blue; + return *this; +} + + +/* + * charles::basics::Color::operator+= -- + */ +Color& +Color::operator+=(const Color& rhs) +{ + red += rhs.red; + green += rhs.green; + blue += rhs.blue; + alpha += rhs.alpha; + return *this; +} + + +/* + * charles::basics::Color::operator-= -- + */ +Color& +Color::operator-=(const Color& rhs) +{ + red -= rhs.red; + green -= rhs.green; + blue -= rhs.blue; + alpha -= rhs.alpha; + return *this; +} + + +/* + * charles::basics::Color::operator* -- + */ +Color +Color::operator*(const Color& rhs) + const +{ + return Color(*this) *= rhs; +} + + +/* + * charles::basics::Color::operator/ -- + */ +Color +Color::operator/(const Color& rhs) + const +{ + return Color(*this) /= rhs; +} + + +/* + * charles::basics::Color::operator+ -- + */ +Color +Color::operator+(const Color& rhs) + const +{ + return Color(*this) += rhs; +} + + +/* + * charles::basics::Color::operator- -- + */ +Color +Color::operator-(const Color& rhs) + const +{ + return Color(*this) -= rhs; +} + + +/* + * charles::basics::operator* -- + */ +const Color +operator*(const Double& lhs, + const Color& rhs) +{ + return rhs * lhs; +} + + +/* + * charles::basics::operator<< -- + */ +std::ostream & +operator<<(std::ostream& os, + const Color& c) +{ + // Stream colors like this: + os << "<" << c.red << ", " << c.green << ", " << c.blue << ", " << c.alpha << ">"; + return os; +} + +} /* namespace basics */ +} /* namespace charles */ diff --git a/src/basics/color.hh b/src/basics/color.hh new file mode 100644 index 0000000..b2624b1 --- /dev/null +++ b/src/basics/color.hh @@ -0,0 +1,58 @@ +/* color.hh + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __BASICS_COLOR_HH__ +#define __BASICS_COLOR_HH__ + +#include "basics/types.hh" + + +namespace charles { +namespace basics { + +struct Color +{ + Color(); + Color(const Double& r, const Double& g, const Double& b, const Double& a = 1.0); + + Color &operator*=(const Double &rhs); + Color &operator/=(const Double &rhs); + Color &operator+=(const Double &rhs); + Color &operator-=(const Double &rhs); + Color operator*(const Double &rhs) const; + Color operator/(const Double &rhs) const; + Color operator+(const Double &rhs) const; + Color operator-(const Double &rhs) const; + + Color &operator=(const Color &rhs); + + // These operators blend the two colors. + Color &operator*=(const Color &rhs); + Color &operator/=(const Color &rhs); + Color &operator+=(const Color &rhs); + Color &operator-=(const Color &rhs); + Color operator*(const Color &rhs) const; + Color operator/(const Color &rhs) const; + Color operator+(const Color &rhs) const; + Color operator-(const Color &rhs) const; + + static const Color Black; + static const Color White; + static const Color Red; + static const Color Green; + static const Color Blue; + + Double red, green, blue, alpha; +}; + + +const Color operator*(const Double &lhs, const Color &rhs); + +std::ostream &operator<<(std::ostream &os, const Color &c); + +} /* namespace basics */ +} /* namespace charles */ + +#endif /* __BASICS_COLOR_HH__ */ diff --git a/src/basics/matrix.cc b/src/basics/matrix.cc new file mode 100644 index 0000000..b8f6ba6 --- /dev/null +++ b/src/basics/matrix.cc @@ -0,0 +1,382 @@ +/* matrix.cc + * vim: set tw=80: + * Eryn Wells + */ + +#include +#include +#include +#include + +#include "basics/matrix.hh" + +#include "basics/util.hh" +#include "basics/vector.hh" + + +namespace charles { +namespace basics { + +#pragma mark Static Methods + +/* + * charles::basics::Matrix4::Zero -- + */ +/* static */ Matrix4 +Matrix4::Zero() +{ + Matrix4 m; + bzero(m.mData, sizeof(Double) * 16); + return m; +} + + +/* + * charles::basics::Matrix4::Identity -- + */ +/* static */ Matrix4 +Matrix4::Identity() +{ + auto m = Matrix4::Zero(); + for (size_t i = 0; i < 4; i++) { + m(i,i) = 1.0; + } + return m; +} + + +/* + * charles::basics::Translation -- + */ +/* static */ Matrix4 +Matrix4::Translation(const Vector4& p) +{ + return Translation(p.X(), p.Y(), p.Z()); +} + + +/* + * charles::basics::TranslationMatrix -- + */ +/* static */ Matrix4 +Matrix4::Translation(Double x, + Double y, + Double z) +{ + Matrix4 m = Matrix4::Identity(); + m(0,3) = x; + m(1,3) = y; + m(2,3) = z; + return m; +} + +#pragma mark Constructors and Assignment + +/* + * charles::basics::Matrix4::Matrix4 -- + */ +Matrix4::Matrix4() + : mData(), + mTransposed(false) +{ } + + +/* + * charles::basics::Matrix4::Matrix4 -- + */ +Matrix4::Matrix4(const Double data[16]) + : mTransposed(false) +{ + /* TODO: Replace with std::copy */ + memcpy(mData, data, sizeof(Double) * 16); +} + + +/* + * charles::basics::Matrix4::Matrix4 -- + */ +Matrix4::Matrix4(const Matrix4 &rhs) + : Matrix4(rhs.mData) +{ + /* + * Needs to be in the body instead of the initializer list because + * (apparently) delegating constructors must be the only thing in the list. + */ + mTransposed = rhs.mTransposed; +} + + +/* + * charles::basics::Matrix4::operator= -- + */ +Matrix4& +Matrix4::operator=(const Matrix4 &rhs) +{ + /* TODO: Replace with std::copy */ + memcpy(mData, rhs.mData, sizeof(Double) * 16); + mTransposed = rhs.mTransposed; + return *this; +} + +#pragma mark Boolean Operators + +/* + * charles::basics::Matrix4::operator== -- + */ +bool +Matrix4::operator==(const Matrix4 &rhs) + const +{ + for (int i = 0; i < 16; i++) { + if (!NearlyEqual(mData[i], rhs.mData[i])) { + return false; + } + } + return true; +} + + +/* + * charles::basics::Matrix4::operator!= -- + */ +bool +Matrix4::operator!=(const Matrix4 &rhs) + const +{ + return !(*this == rhs); +} + +#pragma mark Element Access + +/* + * charles::basics::Matrix4::operator() -- + */ +Double& +Matrix4::operator()(UInt i, + UInt j) +{ + if (i >= 4 || j >= 4) { + std::stringstream ss; + ss << "matrix index out of bounds: i = " << i << ", j = " << j; + throw std::out_of_range(ss.str()); + } + + if (!mTransposed) { + return mData[i*4 + j]; + } else { + return mData[i + j*4]; + } +} + + +/* + * charles::basics::Matrix4::operator() -- + */ +Double +Matrix4::operator()(UInt i, + UInt j) + const +{ + if (i >= 4 || j >= 4) { + std::stringstream ss; + ss << "matrix index out of bounds: i = " << i << ", j = " << j; + throw std::out_of_range(ss.str()); + } + if (!mTransposed) { + return mData[i*4 + j]; + } else { + return mData[i + j*4]; + } +} + + +Vector4 +Matrix4::Column(const UInt i) + const noexcept +{ + return Vector4(operator()(i,0), operator()(i,1), operator()(i,2), operator()(i,3)); +} + + +/* + * charles::basics::Matrix4::CArray -- + */ +const Double* +Matrix4::CArray() + const +{ + return mData; +} + +#pragma mark Maths + +/* + * charles::basics::Matrix4::operator* -- + */ +Matrix4 +Matrix4::operator*(Double rhs) + const +{ + return Matrix4(*this) *= rhs; +} + + +/* + * charles::basics::Matrix4::operator/ -- + */ +Matrix4 +Matrix4::operator/(Double rhs) + const +{ + return Matrix4(*this) /= rhs; +} + + +/* + * charles::basics::Matrix4::operator*= -- + */ +Matrix4& +Matrix4::operator*=(Double rhs) +{ + for (UInt i = 0; i < 16; i++) { + mData[i] *= rhs; + } + return *this; +} + + +/* + * charles::basics::Matrix4::operator/= -- + */ +Matrix4& +Matrix4::operator/=(Double rhs) +{ + return *this *= (1.0 / rhs); +} + + +/* + * charles::basics::Matrix4::operator* -- + */ +Matrix4 +Matrix4::operator*(const Matrix4& rhs) + const +{ + Matrix4 result; + for (UInt i = 0; i < 4; i++) { + for (UInt j = 0; j < 4; j++) { + /* Each cell is Sigma(k=0, M)(lhs[ik] * rhs[kj]) */ + result(i,j) = 0.0; + for (UInt k = 0; k < 4; k++) { + result(i,j) += mData[i*4 + k] * rhs(k,j); + } + } + } + return result; +} + + +/* + * charles::basics::Matrix4::operator* -- + */ +Vector4 +Matrix4::operator*(const Vector4 &rhs) + const +{ + Vector4 result; + for (UInt i = 0; i < 4; i++) { + result(i) = 0.0; + for (UInt k = 0; k < 4; k++) { + result(i) += mData[i*4 + k] * rhs(k); + } + } + return result; +} + + +/* + * charles::basics::Matrix4::Transpose -- + */ +Matrix4& +Matrix4::Transpose() +{ + mTransposed = !mTransposed; + return *this; +} + + +Matrix4& +Matrix4::Inverse() +{ + /* XXX: Only translation matrices are supported right now. */ + operator()(0,3) = -operator()(0,3); + operator()(1,3) = -operator()(1,3); + operator()(2,3) = -operator()(2,3); + return *this; +} + + +/* + * charles::basics::operator* -- + */ +Matrix4 +operator*(Double lhs, + const Matrix4& rhs) +{ + /* Scalar multiplication is commutative. */ + return rhs * lhs; +} + + +/* + * charles::basics::Transposed -- + */ +Matrix4 +Transpose(Matrix4 m) +{ + return m.Transpose(); +} + + +/* + * charles::basics::Inverse -- + */ +Matrix4 +Inverse(Matrix4 m) +{ + return m.Inverse(); +} + + +/* + * charles::basics::operator<< -- + */ +std::ostream& +operator<<(std::ostream &ost, + const Matrix4 &m) +{ + ost << "["; + for (UInt i = 0; i < 4; i++) { + if (i != 0) { + ost << " "; + } + ost << "["; + for (UInt j = 0; j < 4; j++) { + ost << m(i,j); + if (j < 3) { + ost << " "; + } + } + ost << "]"; + if (i < 3) { + ost << "\n"; + } + } + ost << "]"; + return ost; +} + + +} /* namespace mespace */ +} /* namespace charles */ + diff --git a/src/basics/matrix.hh b/src/basics/matrix.hh new file mode 100644 index 0000000..bd434fa --- /dev/null +++ b/src/basics/matrix.hh @@ -0,0 +1,108 @@ +/* matrix.hh + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __BASICS_MATRIX_HH__ +#define __BASICS_MATRIX_HH__ + +#include +#include +#include + +#include "basics/types.hh" + + +namespace charles { +namespace basics { + +struct Vector4; + + +/** A 4x4 matrix, used for 3D transforms. */ +struct Matrix4 +{ + /** Create a 4x4 matrix of zeros. */ + static Matrix4 Zero(); + + /** Create a 4x4 identity matrix. */ + static Matrix4 Identity(); + + /** Create a 4x4 translation matrix. */ + static Matrix4 Translation(Double x, Double y, Double z); + static Matrix4 Translation(const Vector4 &p); + + Matrix4(); + Matrix4(const Double *data); + Matrix4(const Matrix4 &rhs); + + Matrix4& operator=(const Matrix4 &rhs); + + bool operator==(const Matrix4 &rhs) const; + bool operator!=(const Matrix4 &rhs) const; + + /** + * Get the ij'th item. In debug builds, this will assert if i or j are + * outside the bounds of the array. + */ + Double& operator()(UInt i, UInt j); + Double operator()(UInt i, UInt j) const; + Vector4 Column(const UInt i) const noexcept; + + /** Get the underlying C array */ + const Double *CArray() const; + + /* + * TODO: For completeness, matrix addition and subtraction, though I have + * yet to find a need for them... + */ + + /** + * @defgroup Scalar multiplication + * @{ + */ + Matrix4 operator*(Double rhs) const; + Matrix4 operator/(Double rhs) const; + Matrix4& operator*=(Double rhs); + Matrix4& operator/=(Double rhs); + /** @} */ + + /** + * @defgroup Matrix multiplication + * @{ + */ + Matrix4 operator*(const Matrix4 &rhs) const; + Vector4 operator*(const Vector4 &rhs) const; + /** @} */ + + Matrix4 &Transpose(); + Matrix4 &Inverse(); + +protected: + /** The matrix data */ + Double mData[16]; + + /** + * `true` if the matrix has been transposed (i.e. should be indexed in + * column-major format). + */ + bool mTransposed; +}; + + +/** Scalar multiplication, scalar factor on the left. */ +Matrix4 operator*(Double lhs, const Matrix4 &rhs); + +/** Transpose the given matrix. */ +Matrix4 Transpose(Matrix4 m); + +/** Invert the given matrix. */ +Matrix4 Inverse(Matrix4 m); + + +std::ostream& operator<<(std::ostream &ost, const Matrix4 &m); + +} /* namespace basics */ +} /* namespace charles */ + +#endif /* __BASICS_MATRIX_HH__ */ diff --git a/src/basics/ray.cc b/src/basics/ray.cc new file mode 100644 index 0000000..197de91 --- /dev/null +++ b/src/basics/ray.cc @@ -0,0 +1,35 @@ +/* ray.cc + * vim: set tw=80: + * Eryn Wells + */ + +#include "basics/ray.hh" + + +namespace charles { +namespace basics { + +/* + * charles::basics::Ray::Ray -- + */ +Ray::Ray(Vector4 o, + Vector4 d) + : origin(o), + direction(d) +{ } + + +/* + * charles::basics::Ray::Parameterize -- + */ +Vector4 +Ray::Parameterize(const Double& t) + const +{ + return origin + direction * t; +} + + +} /* namespace basics */ +} /* namespace charles */ + diff --git a/src/basics/ray.hh b/src/basics/ray.hh new file mode 100644 index 0000000..26057a3 --- /dev/null +++ b/src/basics/ray.hh @@ -0,0 +1,25 @@ +/* ray.hh + * vim: set tw=80: + * Eryn Wells + */ + +#include "basics/types.hh" +#include "basics/vector.hh" + + +namespace charles { +namespace basics { + +struct Ray +{ + Ray(Vector4 o = Vector4(), Vector4 d = Vector4()); + + Vector4 Parameterize(const Double& t) const; + + Vector4 origin; + Vector4 direction; +}; + +} /* namespace basics */ +} /* namespace charles */ + diff --git a/src/basics/types.hh b/src/basics/types.hh new file mode 100644 index 0000000..e847353 --- /dev/null +++ b/src/basics/types.hh @@ -0,0 +1,21 @@ +/* types.hh + * vim: set tw=80: + * Eryn Wells + */ +/** + * Some basic types. + */ + +#ifndef __BASICS_TYPES_HH__ +#define __BASICS_TYPES_HH__ + +#include + + +typedef double Double; +typedef unsigned int UInt; + +typedef std::vector DoubleVector; +typedef DoubleVector TVector; + +#endif /* __BASICS_TYPES_HH__ */ diff --git a/src/basics/util.hh b/src/basics/util.hh new file mode 100644 index 0000000..2c36c0f --- /dev/null +++ b/src/basics/util.hh @@ -0,0 +1,68 @@ +/* util.hh + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __BASICS_UTIL_HH__ +#define __BASICS_UTIL_HH__ + +#include + + +/** + * A very small constant. If a value is between EPSILON and 0.0, it is + * considered to be zero. + */ +const Double EPSILON = 1.0e-10; + + +/** + * The maximum distance a ray can travel. This is the maximum value t can be. + */ +const Double MAX_DISTANCE = 1.0e7; + + +/** + * Determine if the value is "close enough" to zero. Takes the absolute value + * and compares it to `EPSILON`. + * + * @see EPSILON + * + * @param [in] value The value to check + * @returns `true` if the value is close enough to zero + */ +template +inline bool +NearZero(T value) +{ + return std::fabs(value) < EPSILON; +} + + +/** + * Determine if two values are "close enough" to be considered equal. Subtracts + * one from the other and checks if the result is near zero. + * + * @see NearZero + * + * @param [in] left The left parameter + * @param [in] right The right parameter + * @returns `true` if the values are close enough to be equal + */ +template +inline bool +NearlyEqual(T left, + U right) +{ + return NearZero(left - right); +} + + +inline bool +TooFar(const Double& value) +{ + return value > MAX_DISTANCE; +} + +#endif /* __BASICS_UTIL_HH__ */ + diff --git a/src/basics/vector.cc b/src/basics/vector.cc new file mode 100644 index 0000000..02a07ff --- /dev/null +++ b/src/basics/vector.cc @@ -0,0 +1,400 @@ +/* vector.cc + * vim: set tw=80: + * Eryn Wells + */ + +#include +#include +#include +#include + +#include "basics/vector.hh" + +#include "basics/util.hh" + + +namespace charles { +namespace basics { + +#pragma mark Constructors and Assignment + +/* + * charles::basics::Vector4::Vector4 -- + */ +Vector4::Vector4() +{ + bzero(mData, sizeof(Double) * 4); +} + + +/* + * charles::basics::Vector4::Vector4 -- + */ +Vector4::Vector4(Double x, + Double y, + Double z) + : Vector4(x, y, z, 1.0) +{ } + + +/* + * charles::basics::Vector4::Vector4 -- + */ +Vector4::Vector4(Double x, + Double y, + Double z, + Double w) +{ + mData[0] = x; + mData[1] = y; + mData[2] = z; + mData[3] = w; +} + + +/* + * charles::basics::Vector4::operator= -- + */ +Vector4& +Vector4::operator=(const Vector4 &rhs) +{ + memcpy(mData, rhs.mData, sizeof(Double) * 4); + return *this; +} + +#pragma mark Component Access + +/* + * charles::basics::Vector4::X -- + */ +Double& +Vector4::X() +{ + return mData[0]; +} + + +/* + * charles::basics::Vector4::X -- + */ +Double +Vector4::X() + const +{ + return mData[0]; +} + + +/* + * charles::basics::Vector4::Y -- + */ +Double& +Vector4::Y() +{ + return mData[1]; +} + + +/* + * charles::basics::Vector4::Y -- + */ +Double +Vector4::Y() + const +{ + return mData[1]; +} + + +/* + * charles::basics::Vector4::Z -- + */ +Double& +Vector4::Z() +{ + return mData[2]; +} + + +/* + * charles::basics::Vector4::Z -- + */ +Double +Vector4::Z() + const +{ + return mData[2]; +} + + +Double& +Vector4::operator()(UInt i) +{ + if (i >= 4) { + std::stringstream ss; + ss << "vector index out of bounds: i = " << i; + throw std::out_of_range(ss.str()); + } + return mData[i]; +} + + +Double +Vector4::operator()(UInt i) + const +{ + if (i >= 4) { + std::stringstream ss; + ss << "vector index out of bounds: i = " << i; + throw std::out_of_range(ss.str()); + } + return mData[i]; +} + + +/* + * charles::basics::Vector4::CArray -- + */ +const Double* +Vector4::CArray() + const +{ + return mData; +} + +#pragma mark Boolean Operators + +/* + * charles::basics::Vector4::operator== -- + */ +bool +Vector4::operator==(const Vector4 &rhs) + const +{ + for (UInt i = 0; i < 4; i++) { + if (!NearlyEqual(mData[i], rhs.mData[i])) { + return false; + } + } + return true; +} + + +/* + * charles::basics::Vector4::operator!= -- + */ +bool +Vector4::operator!=(const Vector4 &rhs) + const +{ + return !(*this == rhs); +} + +#pragma mark Maths + +/* + * charles::basics::Vector4::operator* -- + */ +Vector4 +Vector4::operator*(Double rhs) + const +{ + return Vector4(*this) *= rhs; +} + + +/* + * charles::basics::Vector4::operator* -- + */ +Vector4 +Vector4::operator/(Double rhs) + const +{ + return Vector4(*this) /= rhs; +} + + +/* + * charles::basics::Vector4::operator*= -- + */ +Vector4& +Vector4::operator*=(Double rhs) +{ + for (int i = 0; i < 4; i++) { + mData[i] *= rhs; + } + return *this; +} + + +/* + * charles::basics::Vector4::operator/= -- + */ +Vector4& +Vector4::operator/=(Double rhs) +{ + return *this *= (1.0 / rhs); +} + + +/* + * charles::basics::Vector4::operator+ -- + */ +Vector4 +Vector4::operator+(const Vector4 &rhs) + const +{ + return Vector4(*this) += rhs; +} + + +/* + * charles::basics::Vector4::operator- -- + */ +Vector4 +Vector4::operator-(const Vector4 &rhs) + const +{ + return Vector4(*this) -= rhs; +} + + +/* + * charles::basics::Vector4::operator+= -- + */ +Vector4& +Vector4::operator+=(const Vector4 &rhs) +{ + mData[0] += rhs.mData[0]; + mData[1] += rhs.mData[1]; + mData[2] += rhs.mData[2]; + return *this; +} + + +/* + * charles::basics::Vector4::operator-= -- + */ +Vector4& +Vector4::operator-=(const Vector4 &rhs) +{ + return *this += -rhs; +} + + +/* + * charles::basics::Vector4::operator- -- + */ +Vector4 +Vector4::operator-() + const +{ + return Vector4(-X(), -Y(), -Z()); +} + + +/* + * charles::basics::Vector4::Length2 -- + */ +Double +Vector4::Length2() + const +{ + return X()*X() + Y()*Y() + Z()*Z(); +} + + +/* + * charles::basics::Vector4::Length -- + */ +Double +Vector4::Length() + const +{ + return std::sqrt(Length2()); +} + + +/* + * charles::basics::Vector4::Dot -- + */ +Double +Vector4::Dot(const Vector4& rhs) + const +{ + return X()*rhs.X() + Y()*rhs.Y() + Z()*rhs.Z(); +} + + +/* + * charles::basics::Vector4::Cross -- + */ +Vector4 +Vector4::Cross(const Vector4& rhs) + const +{ + return Vector4(mData[1]*rhs.mData[2] - mData[2]*rhs.mData[1], + mData[2]*rhs.mData[0] - mData[0]*rhs.mData[2], + mData[0]*rhs.mData[1] - mData[1]*rhs.mData[0]); +} + + +/* + * charles::basics::Vector4::Normalize -- + */ +Vector4& +Vector4::Normalize() +{ + return *this /= Length(); +} + + +/* + * charles::basics::operator* -- + */ +Vector4 +operator*(Double lhs, + const Vector4& rhs) +{ + return rhs * lhs; +} + + +/* + * charles::basics::Normalized -- + */ +Vector4 +Normalized(Vector4 v) +{ + return v.Normalize(); +} + + +/* + * charles::basics::LinearCombination -- + */ +Vector4 +LinearCombination(const Double k1, const Vector4& v1, + const Double k2, const Vector4& v2, + const Double k3, const Vector4& v3) +{ + return Vector4(k1 * v1.X() + k2 * v2.X() + k3 * v3.X(), + k1 * v1.Y() + k2 * v2.Y() + k3 * v3.Y(), + k1 * v1.Z() + k2 * v2.Z() + k3 * v3.Z()); +} + + +/* + * charles::basics::operator<< -- + */ +std::ostream& +operator<<(std::ostream& os, const Vector4& v) +{ + // Stream the vector like this: + os << "<" << v.X() << ", " << v.Y() << ", " << v.Z() << ">"; + return os; +} + +} /* namespace basics */ +} /* namespace charles */ + diff --git a/src/basics/vector.hh b/src/basics/vector.hh new file mode 100644 index 0000000..b83a5c2 --- /dev/null +++ b/src/basics/vector.hh @@ -0,0 +1,110 @@ +/* vector.hh + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __BASICS_VECTOR_HH__ +#define __BASICS_VECTOR_HH__ + +#include + +#include "basics/matrix.hh" +#include "basics/types.hh" + + +namespace charles { +namespace basics { + +/** A 4x1 matrix, used for specifying points and directions in 3D space. */ +struct Vector4 +{ + Vector4(); + Vector4(Double x, Double y, Double z); + Vector4(Double x, Double y, Double z, Double w); + + Vector4 &operator=(const Vector4 &rhs); + + /** + * @defgroup Component access + * @{ + */ + Double& X(); + Double X() const; + Double& Y(); + Double Y() const; + Double& Z(); + Double Z() const; + + Double &operator()(UInt i); + Double operator()(UInt i) const; + + /** Get the underlying C array. */ + const Double *CArray() const; + /** @} */ + + bool operator==(const Vector4 &rhs) const; + bool operator!=(const Vector4 &rhs) const; + + /** + * @defgroup Scalar multiplication + * @{ + */ + Vector4 operator*(Double rhs) const; + Vector4 operator/(Double rhs) const; + Vector4 &operator*=(Double rhs); + Vector4 &operator/=(Double rhs); + /** @} */ + + /** + * @defgroup Vector addition and subtraction + * @{ + */ + Vector4 operator+(const Vector4 &rhs) const; + Vector4 operator-(const Vector4 &rhs) const; + Vector4 &operator+=(const Vector4 &rhs); + Vector4 &operator-=(const Vector4 &rhs); + /** @} */ + + /** Negate this vector. */ + Vector4 operator-() const; + + /** Get the length-squared of this vector. */ + Double Length2() const; + + /** Get the length of this vector. */ + Double Length() const; + + /** Get the dot product of `this` and `rhs`. */ + Double Dot(const Vector4 &rhs) const; + + /** Get the cross product of `this` and `rhs`. */ + Vector4 Cross(const Vector4& rhs) const; + + /** Normalize this vector. */ + Vector4& Normalize(); + +private: + Double mData[4]; +}; + + +/** Scalar multiplication of vectors, with the scalar factor on the left. */ +Vector4 operator*(Double lhs, const Vector4 &rhs); + + +/** Normalize a copy of the given vector. */ +Vector4 Normalized(Vector4 v); + + +Vector4 LinearCombination(Double k1, const Vector4 &v1, + Double k2, const Vector4 &v2, + Double k3, const Vector4 &v3); + + +std::ostream& operator<<(std::ostream &ost, const Vector4 &v); + +} /* namespace basics */ +} /* namespace charles */ + +#endif /* __BASICS_VECTOR_HH__ */ + diff --git a/src/camera.cc b/src/camera.cc index 4605497..86ed8be 100644 --- a/src/camera.cc +++ b/src/camera.cc @@ -3,29 +3,45 @@ * Eryn Wells */ -#include "camera.h" +#include "camera.hh" #include "log.hh" #define LOG_NAME "camera" #include "logModule.hh" +using charles::basics::Ray; +using charles::basics::Vector4; + + +namespace charles { + #pragma mark - Generic Camera +/* + * charles::Camera::Camera -- + */ Camera::Camera() - : mDirection(Vector3::Z), + : mDirection(0, 0, 1), mRight(1.33, 0, 0), - mUp(Vector3::Y) + mUp(0, 1, 0) { } -Camera::Camera(const Camera& other) - : mDirection(other.mDirection), - mRight(other.mRight), - mUp(other.mUp) +/* + * charles::Camera::Camera -- + */ +Camera::Camera(const Camera& rhs) + : mOrigin(rhs.mOrigin), + mDirection(rhs.mDirection), + mRight(rhs.mRight), + mUp(rhs.mUp) { } +/* + * charles::Camera::~Camera -- + */ Camera::~Camera() { } @@ -33,7 +49,17 @@ Camera::~Camera() /* * Camera::GetOrigin -- */ -const Vector3& +Vector4& +Camera::GetOrigin() +{ + return mOrigin; +} + + +/* + * Camera::GetOrigin -- + */ +const Vector4& Camera::GetOrigin() const { @@ -45,27 +71,57 @@ Camera::GetOrigin() * Camera::SetOrigin -- */ void -Camera::SetOrigin(const Vector3 &origin) +Camera::SetOrigin(const Vector4& origin) { mOrigin = origin; } -const Vector3& -Camera::get_direction() +/* + * Camera::GetDirection -- + */ +Vector4& +Camera::GetDirection() +{ + return mDirection; +} + + +/* + * Camera::GetDirection -- + */ +const Vector4& +Camera::GetDirection() const { return mDirection; } + +/* + * Camera::SetDirection -- + */ void -Camera::set_direction(const Vector3 &direction) +Camera::SetDirection(const Vector4& direction) { mDirection = direction; } -const Vector3& +/* + * Camera::GetRight -- + */ +Vector4& +Camera::GetRight() +{ + return mRight; +} + + +/* + * Camera::GetRight -- + */ +const Vector4& Camera::GetRight() const { @@ -73,14 +129,30 @@ Camera::GetRight() } +/* + * Camera::SetRight -- + */ void -Camera::SetRight(const Vector3& right) +Camera::SetRight(const Vector4& right) { mRight = right; } -const Vector3& +/* + * Camera::GetUp -- + */ +Vector4& +Camera::GetUp() +{ + return mUp; +} + + +/* + * Camera::GetUp -- + */ +const Vector4& Camera::GetUp() const { @@ -88,13 +160,19 @@ Camera::GetUp() } +/* + * Camera::SetUp -- + */ void -Camera::SetUp(const Vector3& up) +Camera::SetUp(const Vector4& up) { mUp = up; } +/* + * Camera::IsLeftHanded -- + */ bool Camera::IsLeftHanded() const @@ -110,24 +188,27 @@ Camera::IsLeftHanded() * than 0, the vector is pointing left of the up-direction plane and the * coordinate system is left-handed. */ - return mUp.cross(mDirection).dot(mRight) < 0.0; + return mUp.Cross(mDirection).Dot(mRight) < 0.0; } +/* + * Camera::LookAt -- + */ void -Camera::LookAt(const Vector3& pt) +Camera::LookAt(const Vector4& p) { /* * Precalulate these in order to preserve the aspect ratio and orientation * of the camera across the LookAt operation. */ - const Double directionLength = mDirection.length(); - const Double rightLength = mRight.length(); - const Double upLength = mUp.length(); + const Double directionLength = mDirection.Length(); + const Double rightLength = mRight.Length(); + const Double upLength = mUp.Length(); const bool isLeftHanded = IsLeftHanded(); /* Orient the camera towards the point. */ - mDirection = (pt - mOrigin).normalize(); + mDirection = basics::Normalized(p - mOrigin); /* TODO: Check for zero length direction vector. */ /* @@ -141,8 +222,8 @@ Camera::LookAt(const Vector3& pt) * specifies the vector along which LookAt pans and tilts the camera. It * might be worth looking into, at some point. */ - mRight = Vector3::Y.cross(mDirection).normalize(); - mUp = mDirection.cross(mRight); + mRight = basics::Normalized(Vector4(0, 1, 0).Cross(mDirection)); + mUp = mDirection.Cross(mRight); /* * Now, fix up the direction, right, and up vectors so that their magnitudes @@ -152,10 +233,13 @@ Camera::LookAt(const Vector3& pt) mRight *= isLeftHanded ? rightLength : -rightLength; mUp *= upLength; - LOG_DEBUG << "Camera is looking at " << pt; + LOG_DEBUG << "Camera is looking at " << p; } +/* + * charles::Camera::GetTypeString -- + */ std::string Camera::GetTypeString() const @@ -164,6 +248,9 @@ Camera::GetTypeString() } +/* + * charles::Camera::WriteType -- + */ void Camera::WriteType(std::ostream& ost) const @@ -184,23 +271,23 @@ PerspectiveCamera::PerspectiveCamera(const Camera& other) Ray -PerspectiveCamera::compute_primary_ray(const int x, - const int width, - const int y, - const int height) +PerspectiveCamera::PrimaryRay(const int x, + const int width, + const int y, + const int height) const { /* * Center x and y in the pixel and convert them to be coordinates between * -0.5 and 0.5. */ - double x0 = (x + 0.5) / width - 0.5; - double y0 = ((height - 1.0) - (y - 0.5)) / height - 0.5; + Double x0 = (x + 0.5) / width - 0.5; + Double y0 = ((height - 1.0) - (y - 0.5)) / height - 0.5; - Vector3 direction = LinearCombination(1.0, get_direction(), + Vector4 direction = LinearCombination(1.0, GetDirection(), x0, GetRight(), y0, GetUp()); - return Ray(GetOrigin(), direction.normalize()); + return Ray(GetOrigin(), basics::Normalized(direction)); } @@ -226,30 +313,24 @@ OrthographicCamera::OrthographicCamera(const Camera& other) /* * OrthographicCamera::compute_primary_ray -- */ -/** - * Compute a primary ray given an (x,y) coordinate pair. The orthographic camera - * projects rays parallel to the viewing direction through the (x,y) coordinate - * given. Thus, the size of the orthographic camera should be set to the size of - * the view into the scene. - */ Ray -OrthographicCamera::compute_primary_ray(const int x, - const int width, - const int y, - const int height) +OrthographicCamera::PrimaryRay(const int x, + const int width, + const int y, + const int height) const { /* * Center x and y in the pixel and convert them to be coordinates between * -0.5 and 0.5. */ - double x0 = (x + 0.5) / width + 0.5; - double y0 = ((height - 1.0) - (y - 0.5)) / height - 0.5; + Double x0 = (x + 0.5) / width + 0.5; + Double y0 = ((height - 1.0) - (y - 0.5)) / height - 0.5; - Vector3 origin = LinearCombination(1.0, GetOrigin(), + Vector4 origin = LinearCombination(1.0, GetOrigin(), x0, GetRight(), y0, GetUp()); - return Ray(origin, get_direction()); + return Ray(origin, GetDirection()); } @@ -274,3 +355,5 @@ operator<<(std::ostream& ost, << "]"; return ost; } + +} /* namespace charles */ diff --git a/src/camera.h b/src/camera.h deleted file mode 100644 index f3b46d1..0000000 --- a/src/camera.h +++ /dev/null @@ -1,117 +0,0 @@ -/* camera.h - * - * The Camera is the eye into the scene. It defines several parameters and a - * single compute_primary_ray method that generates rays with which the ray - * tracer draws the scene. - * - * Eryn Wells - */ - -#ifndef __CAMERA_H__ -#define __CAMERA_H__ - -#include - -#include "basics.h" - - -struct Camera -{ - typedef std::shared_ptr Ptr; - - Camera(); - Camera(const Camera& other); - virtual ~Camera(); - - const Vector3& GetOrigin() const; - void SetOrigin(const Vector3& origin); - - const Vector3& get_direction() const; - void set_direction(const Vector3& direction); - - const Vector3& GetRight() const; - void SetRight(const Vector3& right); - - const Vector3& GetUp() const; - void SetUp(const Vector3& up); - - /** - * Get the camera's handedness. Left handed is the default. - * - * @returns `true` if the camera is set up for left-handed coordinates. - */ - bool IsLeftHanded() const; - - /** - * Pan and tilt the camera towards the given point. - * - * @param [in] pt The point at which to face the camera - */ - void LookAt(const Vector3& pt); - - virtual Ray compute_primary_ray(const int x, const int width, - const int y, const int height) const = 0; - - virtual std::string GetTypeString() const; - -private: - friend std::ostream& operator<<(std::ostream& ost, const Camera& camera); - - void WriteType(std::ostream& ost) const; - - /** - * The location of the camera in the scene. Depending on the type of camera, - * this is the point from which rays will be emitted. - */ - Vector3 mOrigin; - - /** A normalized vector defining where the camera is pointed. */ - Vector3 mDirection; - - /** - * A vector defining the width of the camera's image plane. The ratio of - * this and mUp determine the aspect ratio of the image. - */ - Vector3 mRight; - - /** - * A vector defining the height of the camera's image plane. The ratio of - * this and mRight determine the aspect ratio of the image. - */ - Vector3 mUp; -}; - - -class PerspectiveCamera - : public Camera -{ -public: - PerspectiveCamera(); - PerspectiveCamera(const Camera& other); - - Ray compute_primary_ray(const int x, const int width, - const int y, const int height) const; - -private: - std::string GetTypeString() const; -}; - - -class OrthographicCamera - : public Camera -{ -public: - OrthographicCamera(); - OrthographicCamera(const Camera& other); - - Ray compute_primary_ray(const int x, const int width, - const int y, const int height) const; - -private: - std::string GetTypeString() const; -}; - - -std::ostream& operator<<(std::ostream& ost, const Camera& camera); - -#endif diff --git a/src/camera.hh b/src/camera.hh new file mode 100644 index 0000000..b34e605 --- /dev/null +++ b/src/camera.hh @@ -0,0 +1,124 @@ +/* camera.hh + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __CAMERA_H__ +#define __CAMERA_H__ + +#include + +#include "basics/basics.hh" + + +namespace charles { + +/** + * The Camera is the eye into the scene. It defines several parameters and a + * single compute_primary_ray method that generates rays with which the ray + * tracer draws the scene. + */ +struct Camera +{ + typedef std::shared_ptr Ptr; + + Camera(); + Camera(const Camera& other); + virtual ~Camera(); + + basics::Vector4& GetOrigin(); + const basics::Vector4& GetOrigin() const; + void SetOrigin(const basics::Vector4& origin); + + basics::Vector4& GetDirection(); + const basics::Vector4& GetDirection() const; + void SetDirection(const basics::Vector4& direction); + + basics::Vector4& GetRight(); + const basics::Vector4& GetRight() const; + void SetRight(const basics::Vector4& right); + + basics::Vector4& GetUp(); + const basics::Vector4& GetUp() const; + void SetUp(const basics::Vector4& up); + + /** Get the camera's handedness. Left handed is the default. */ + bool IsLeftHanded() const; + + /** Pan and tilt the camera towards the given point. */ + void LookAt(const basics::Vector4& p); + + virtual basics::Ray PrimaryRay(const int x, const int width, + const int y, const int height) const = 0; + + virtual std::string GetTypeString() const; + +private: + friend std::ostream& operator<<(std::ostream& ost, const Camera& camera); + + void WriteType(std::ostream& ost) const; + + /** + * The location of the camera in the scene. Depending on the type of camera, + * this is the point from which rays will be emitted. + */ + basics::Vector4 mOrigin; + + /** A vector defining where the camera is pointed. */ + basics::Vector4 mDirection; + + /** + * A vector defining the width of the camera's image plane. The ratio of + * this and mUp determine the aspect ratio of the image. + */ + basics::Vector4 mRight; + + /** + * A vector defining the height of the camera's image plane. The ratio of + * this and mRight determine the aspect ratio of the image. + */ + basics::Vector4 mUp; +}; + + +class PerspectiveCamera + : public Camera +{ +public: + PerspectiveCamera(); + PerspectiveCamera(const Camera& other); + + basics::Ray PrimaryRay(const int x, const int width, + const int y, const int height) const; + +private: + std::string GetTypeString() const; +}; + + +class OrthographicCamera + : public Camera +{ +public: + OrthographicCamera(); + OrthographicCamera(const Camera& other); + + /** + * Compute a primary ray given an (x,y) coordinate pair. The orthographic + * camera projects rays parallel to the viewing direction through the (x,y) + * coordinate given. Thus, the size of the orthographic camera should be set + * to the size of the view into the scene. + */ + basics::Ray PrimaryRay(const int x, const int width, + const int y, const int height) const; + +private: + std::string GetTypeString() const; +}; + + +std::ostream& operator<<(std::ostream& ost, const Camera& camera); + +} /* namespace charles */ + +#endif diff --git a/src/charles.cc b/src/charles.cc index 22260fe..e27947a 100644 --- a/src/charles.cc +++ b/src/charles.cc @@ -8,18 +8,19 @@ #include #include -#include "basics.h" #include "log.hh" -#include "light.h" +#include "light.hh" #include "reader_yaml.hh" -#include "scene.h" +#include "scene.hh" #include "writer_png.h" +#include "basics/basics.hh" #define LOG_NAME "ROOT" #include "logModule.hh" -int verbosity = 0; +using namespace charles; +using namespace charles::basics; static void @@ -38,37 +39,10 @@ main(int argc, Scene scene; - scene.get_ambient().set_intensity(1.0); + PointLight *l1 = new PointLight(Vector4(4.0, 6.0, 1.0), Color::White, 0.8); + scene.AddLight(l1); -#if 0 - Material *m1 = new Material(); - m1->set_diffuse_color(Color::Red); - Material *m2 = new Material(); - m2->set_diffuse_color(Color::Green); - Material *m3 = new Material(); - m3->set_diffuse_color(Color::Blue); - - // Make some spheres. - Sphere *s1 = new Sphere(Vector3(0, 0.5, 2), 0.33); - Sphere *s2 = new Sphere(Vector3(-0.33, 0, 2), 0.33); - Sphere *s3 = new Sphere(Vector3(0.33, 0, 2), 0.33); - s1->set_material(m1); - s2->set_material(m2); - s3->set_material(m3); - scene.add_shape(s1); - scene.add_shape(s2); - scene.add_shape(s3); - - // Make a plane - /* - Plane *p1 = new Plane(Vector3(0, 460, 400), Vector3(0, 1, 0.01)); - p1->set_material(m1); - scene.add_shape(p1); - */ -#endif - - PointLight *l1 = new PointLight(Vector3(4.0, 6.0, 1.0), Color::White, 0.8); - scene.add_light(l1); + bool shouldRender = true; std::string logFilename; unsigned int logLevel = 0; @@ -76,7 +50,7 @@ main(int argc, std::string outfile, infile; int opt; - while ((opt = getopt(argc, (char *const *)argv, "hl:L:o:v:")) != -1) { + while ((opt = getopt(argc, (char *const *)argv, "hl:L:no:v:")) != -1) { switch (opt) { case 'h': usage(argv[0]); @@ -88,6 +62,9 @@ main(int argc, case 'L': logLevel = std::stoul(optarg); break; + case 'n': + shouldRender = false; + break; case 'o': outfile = optarg; break; @@ -124,12 +101,14 @@ main(int argc, } /* Call tracer. */ - LOG_INFO << "Beginning render"; - scene.render(); + if (shouldRender) { + LOG_INFO << "Beginning render"; + scene.Render(); - /* Write rendered scene to PNG file. */ - PNGWriter writer; - scene.write(writer, outfile); + /* Write rendered scene to PNG file. */ + PNGWriter writer; + scene.Write(writer, outfile); + } if (logLevel > 0) { Log::Close(); diff --git a/src/light.cc b/src/light.cc index 9cdd977..847602f 100644 --- a/src/light.cc +++ b/src/light.cc @@ -1,112 +1,81 @@ /* light.cc - * - * Lights light the scene. - * + * vim: set tw=80: * Eryn Wells */ -#include "basics.h" -#include "light.h" -#include "object.h" +#include "light.hh" -#pragma mark - Ambient Lights -AmbientLight::AmbientLight() - : AmbientLight(Color::White) +using charles::basics::Color; + + +namespace charles { + +Light::Light(const Color& color, + const Double& intensity) + : mColor(color), + mIntensity(ClampIntensity(intensity)) { } -AmbientLight::AmbientLight(const Color &c) - : AmbientLight(c, 0.0) +Light::~Light() { } -AmbientLight::AmbientLight(const Color &c, const float &i) - : color(c), - intensity(i) +Color& +Light::GetColor() { - _clamp_intensity(); + return mColor; } -const Color & -AmbientLight::get_color() +const Color& +Light::GetColor() const { - return color; + return mColor; } -const float & -AmbientLight::get_intensity() +void +Light::SetColor(const Color& color) +{ + mColor = color; +} + + +Double +Light::GetIntensity() const { + return mIntensity; +} + + +void +Light::SetIntensity(const Double& intensity) +{ + mIntensity = ClampIntensity(intensity); +} + + +Color&& +Light::Contribution() + const +{ + return mColor * mIntensity; +} + + +Double +Light::ClampIntensity(const Double& intensity) +{ + if (intensity < 0.0) { + return 0.0; + } else if (intensity > 1.0) { + return 1.0; + } return intensity; } -void -AmbientLight::set_intensity(const float &i) -{ - intensity = i; - _clamp_intensity(); -} - - -Color -AmbientLight::compute_color_contribution() - const -{ - return color * intensity; -} - - -void -AmbientLight::_clamp_intensity() -{ - if (intensity < 0.0) { - intensity = 0.0; - } - else if (intensity > 1.0) { - intensity = 1.0; - } -} - -#pragma mark - Point Lights - -PointLight::PointLight() - : PointLight(Vector3()) -{ } - - -PointLight::PointLight(const Vector3 &o) - : PointLight(o, Color::White) -{ } - - -PointLight::PointLight(const Vector3 &o, - const Color &c) - : PointLight(o, c, 1.0) -{ } - - -PointLight::PointLight(const Vector3 &o, - const Color &c, - const float &i) - : AmbientLight(c, i), - mOrigin(o) -{ } - - -const Vector3& -PointLight::GetOrigin() - const -{ - return mOrigin; -} - - -void -PointLight::SetOrigin(const Vector3& origin) -{ - mOrigin = origin; -} +} /* namespace charles */ diff --git a/src/light.h b/src/light.h deleted file mode 100644 index 0749a0a..0000000 --- a/src/light.h +++ /dev/null @@ -1,53 +0,0 @@ -/* light.h - * - * Lights light the scene. - * - * Eryn Wells - */ - -#ifndef __LIGHT_H__ -#define __LIGHT_H__ - -#include "basics.h" - - -class AmbientLight -{ -public: - AmbientLight(); - AmbientLight(const Color &c); - AmbientLight(const Color &c, const float &i); - - const Color &get_color() const; - const float &get_intensity() const; - void set_intensity(const float &i); - - Color compute_color_contribution() const; - -protected: - Color color; - float intensity; - -private: - void _clamp_intensity(); -}; - - -class PointLight - : public AmbientLight -{ -public: - PointLight(); - - PointLight(const Vector3 &o); - PointLight(const Vector3 &o, const Color &c); - PointLight(const Vector3 &o, const Color &c, const float &i); - - const Vector3& GetOrigin() const; - void SetOrigin(const Vector3& origin); - -private: - Vector3 mOrigin; -}; - -#endif diff --git a/src/light.hh b/src/light.hh new file mode 100644 index 0000000..3175178 --- /dev/null +++ b/src/light.hh @@ -0,0 +1,52 @@ +/* light.hh + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __LIGHT_H__ +#define __LIGHT_H__ + +#include + +#include "basics/basics.hh" + + +namespace charles { + +struct Light +{ + typedef std::shared_ptr Ptr; + + Light(const basics::Color& color, + const Double& intensity = 1.0); + + virtual ~Light(); + + basics::Color& GetColor(); + const basics::Color& GetColor() const; + void SetColor(const basics::Color& color); + + Double GetIntensity() const; + void SetIntensity(const Double& intensity); + + virtual basics::Color&& Contribution() const; + +private: + Double ClampIntensity(const Double& intensity); + + /** The color of the light. */ + basics::Color mColor; + + /** + * The intensity of the light. Normal values range from 0.0 to 1.0, but + * they can be set higher. + */ + Double mIntensity; +}; + + +typedef Light AmbientLight; + +} /* namespace charles */ + +#endif diff --git a/src/lightPoint.cc b/src/lightPoint.cc new file mode 100644 index 0000000..2a1021a --- /dev/null +++ b/src/lightPoint.cc @@ -0,0 +1,37 @@ +/* lightPoint.cc + * vim: set tw=80: + * Eryn Wells + */ + +#include "lightPoint.hh" + + +using charles::basics::Color; +using charles::basics::Vector4; + + +namespace charles { + +PointLight::PointLight(const Vector4& origin, + const Color& color, + const Double& intensity) + : Light(color, intensity), + mOrigin(origin) +{ } + + +Vector4& +PointLight::GetOrigin() +{ + return mOrigin; +} + + +void +PointLight::SetOrigin(const Vector4& origin) +{ + mOrigin = origin; +} + +} /* namespace charles */ + diff --git a/src/lightPoint.hh b/src/lightPoint.hh new file mode 100644 index 0000000..2881de3 --- /dev/null +++ b/src/lightPoint.hh @@ -0,0 +1,35 @@ +/* lightPoint.hh + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __LIGHTPOINT_HH__ +#define __LIGHTPOINT_HH__ + +#include "light.hh" + + +namespace charles { + +/** + * The simplest light source. Emits light in all directions uniformly, without + * falloff. + */ +class PointLight + : public Light +{ +public: + PointLight(const basics::Vector4 &origin, + const basics::Color& color, + const Double& intensity); + + basics::Vector4& GetOrigin(); + void SetOrigin(const basics::Vector4& origin); + +private: + basics::Vector4 mOrigin; +}; + +} /* namespace charles */ + +#endif /* __LIGHTPOINT_HH__ */ diff --git a/src/log.hh b/src/log.hh index 24727ef..31e3a23 100644 --- a/src/log.hh +++ b/src/log.hh @@ -69,8 +69,8 @@ struct Tracer ~Tracer(); private: - const std::string& mName; - const std::string& mFunction; + const std::string mName; + const std::string mFunction; }; diff --git a/src/material.cc b/src/material.cc index a6ad762..a2edd8c 100644 --- a/src/material.cc +++ b/src/material.cc @@ -1,11 +1,12 @@ -/* material.h - * - * Materials are applied to shapes and determine color, texture mapping, shading, etc. - * +/* material.cc + * vim: set tw=80: * Eryn Wells */ -#include "material.h" +#include "material.hh" + + +using charles::basics::Color; namespace charles { @@ -36,9 +37,8 @@ Material::SetDiffuseIntensity(const Double& kd) } -const Color& +Color& Material::GetDiffuseColor() - const { return mDiffuseColor; } @@ -67,9 +67,8 @@ Material::SetSpecularIntensity(const Double& ks) } -const Color& +Color& Material::GetSpecularColor() - const { return mSpecularColor; } diff --git a/src/material.h b/src/material.hh similarity index 66% rename from src/material.h rename to src/material.hh index 3d52461..64feb5c 100644 --- a/src/material.h +++ b/src/material.hh @@ -1,18 +1,20 @@ -/* material.h - * - * Materials are applied to shapes and determine color, texture mapping, shading, etc. - * +/* material.hh + * vim: set tw=80: * Eryn Wells */ -#ifndef __MATERIAL_H__ -#define __MATERIAL_H__ +#ifndef __MATERIAL_HH__ +#define __MATERIAL_HH__ + +#include "basics/basics.hh" -#include "basics.h" -#include "types.hh" namespace charles { +/** + * Materials are applied to shapes and determine color, texture mapping, + * shading, etc. + */ struct Material { enum class DiffuseShaderModel { @@ -29,14 +31,14 @@ struct Material Double GetDiffuseIntensity() const; void SetDiffuseIntensity(const Double& kd); - const Color& GetDiffuseColor() const; - void SetDiffuseColor(const Color& c); + basics::Color& GetDiffuseColor(); + void SetDiffuseColor(const basics::Color& c); Double GetSpecularIntensity() const; void SetSpecularIntensity(const Double& kd); - const Color &GetSpecularColor() const; - void SetSpecularColor(const Color& c); + basics::Color& GetSpecularColor(); + void SetSpecularColor(const basics::Color& c); private: void ClampParameter(Double& param); @@ -44,12 +46,12 @@ private: // Diffuse parameters. DiffuseShaderModel mDiffuseModel; Double mDiffuseIntensity; - Color mDiffuseColor; + basics::Color mDiffuseColor; // Specular parameters. SpecularShaderModel mSpecularModel; Double mSpecularIntensity; - Color mSpecularColor; + basics::Color mSpecularColor; }; } /* namespace charles */ diff --git a/src/object.cc b/src/object.cc index b74dcbe..e268033 100644 --- a/src/object.cc +++ b/src/object.cc @@ -9,54 +9,38 @@ #include #include -#include "basics.h" -#include "material.h" -#include "object.h" +#include "object.hh" + +#include "material.hh" +#include "basics/basics.hh" + + +using charles::basics::Ray; +using charles::basics::Matrix4; +using charles::basics::Vector4; + namespace charles { -#pragma mark - Objects - /* - * Object::Object -- - * - * Default constructor. Create a new Object with an origin at (0, 0, 0). + * charles::Object::Object -- */ -Object::Object() - : Object(Vector3::Zero) -{ } - - -/* - * Object::Object -- - * - * Constructor. Create a new Object with an origin at o. - */ -Object::Object(Vector3 origin) - : mOrigin(origin), +Object::Object(const Vector4& origin) + : mTranslation(Matrix4::Translation(origin)), mMaterial() { } -Vector3 -Object::GetOrigin() - const -{ - return mOrigin; -} - - +/* + * charles::Object::~Object -- + */ Object::~Object() { } -void -Object::SetOrigin(const Vector3& origin) -{ - mOrigin = origin; -} - - +/* + * charles::Object::GetMaterial -- + */ Material& Object::GetMaterial() { @@ -64,6 +48,19 @@ Object::GetMaterial() } +/* + * charles::Object::Place -- + */ +void +Object::Place(const Vector4 &p) +{ + mTranslation = Matrix4::Translation(p); +} + + +/* + * charles::Object::SetMaterial -- + */ void Object::SetMaterial(const Material& material) { @@ -71,6 +68,67 @@ Object::SetMaterial(const Material& material) } +/* + * charles::Object::Intersect -- + */ +bool +Object::Intersect(const basics::Ray& ray, + TVector& t, + Stats& stats) + const +{ + return DoIntersect(ToObjectSpace(ray), t, stats); +} + + +/* + * charles::Object::Normal -- + */ +Vector4 +Object::Normal(const Vector4& p) + const +{ + return FromObjectSpace(DoNormal(ToObjectSpace(p))); +} + + +/* + * charles::Object::ToObjectSpace -- + */ +Ray +Object::ToObjectSpace(Ray ray) + const +{ + ray.origin = mTranslation * ray.origin; + return ray; +} + + +/* + * charles::Object::ToObjectSpace -- + */ +Vector4 +Object::ToObjectSpace(const Vector4& v) + const +{ + return mTranslation * v; +} + + +/* + * charles::Object::FromObjectSpace -- + */ +Vector4 +Object::FromObjectSpace(const Vector4& v) + const +{ + return Inverse(mTranslation) * v; +} + + +/* + * charles::Object::Write -- + */ void Object::Write(std::ostream& ost) const @@ -79,11 +137,16 @@ Object::Write(std::ostream& ost) } +/* + * charles::operator<< -- + */ std::ostream& operator<<(std::ostream& ost, const Object& object) { + ost << "["; object.Write(ost); + ost << " translate=" << object.mTranslation.Column(3) << "]"; return ost; } diff --git a/src/object.h b/src/object.h deleted file mode 100644 index 4f906a7..0000000 --- a/src/object.h +++ /dev/null @@ -1,72 +0,0 @@ -/* object.h - * - * Declaration of abstract, top-level scene objects. The Object class is the top of this hierarchy. All other scene - * objects are based on it. The Shape class defines a visible shape in the scene. - * - * Eryn Wells - */ - -#ifndef __OBJECT_H__ -#define __OBJECT_H__ - -#include -#include -#include - -#include "basics.h" -#include "material.h" -#include "stats.hh" -#include "texture.h" -#include "types.hh" - -namespace charles { - -struct Object -{ - typedef std::shared_ptr Ptr; - - Object(); - Object(Vector3 o); - virtual ~Object(); - - Vector3 GetOrigin() const; - void SetOrigin(const Vector3& origin); - - Material& GetMaterial(); - void SetMaterial(const Material& material); - - /** - * Determines if the given ray intersects with this object. All intersection - * t values are returned in the `t` argument, in increasing order. - * - * @param [in] ray The ray to test for intersection - * @param [out] t A vector of all intersection t values - * @return `true` if the ray intersects with this object - */ - virtual bool DoesIntersect(const Ray& ray, TVector& t, Stats& stats) const = 0; - virtual bool point_is_on_surface(const Vector3 &p) const = 0; - virtual Vector3 compute_normal(const Vector3 &p) const = 0; - - virtual void Write(std::ostream& ost) const; - -private: - /** The location of this object. */ - Vector3 mOrigin; - - /** This object's material, surface properties, etc. */ - Material mMaterial; -}; - - -/** - * Write a string representation of the Object to an output stream. - * - * @param [in] ost The output stream - * @param [in] object The object - * @returns The output stream - */ -std::ostream& operator<<(std::ostream& ost, const Object& object); - -} /* namespace charles */ - -#endif diff --git a/src/object.hh b/src/object.hh new file mode 100644 index 0000000..ee2fca2 --- /dev/null +++ b/src/object.hh @@ -0,0 +1,89 @@ +/* object.h + * vim: set tw=80: + * Eryn Wells + */ + +#ifndef __OBJECT_H__ +#define __OBJECT_H__ + +#include +#include +#include + +#include "material.hh" +#include "stats.hh" +#include "basics/basics.hh" + + +namespace charles { + +struct Object +{ + typedef std::shared_ptr Ptr; + + Object(const basics::Vector4& origin = basics::Vector4()); + virtual ~Object(); + + Material& GetMaterial(); + void SetMaterial(const Material& material); + + void Place(const basics::Vector4 &p); + + /** + * Determine if the given ray intersects with this object. Converts the + * ray's origin and direction to object space before calling the protected + * Object::DoIntersect method. All intersection t values are returned in the + * `t` argument, in ascending order. + */ + bool Intersect(const basics::Ray& ray, TVector& t, Stats& stats) const; + + /** + * Get the normal vector at the given point p. p is assumed to be on the + * surface. + */ + basics::Vector4 Normal(const basics::Vector4& p) const; + + /** Write a string representation of this object to the stream. */ + virtual void Write(std::ostream& ost) const; + +protected: + /** + * Do the actual intersection work. Subclasses are expected to override + * this. + */ + virtual bool DoIntersect(const basics::Ray& ray, TVector& t, Stats& stats) const = 0; + + /** + * Do the actual work of finding a normal for point p. Subclasses are + * expected to override this. + */ + virtual basics::Vector4 DoNormal(const basics::Vector4& p) const = 0; + +private: + friend std::ostream& operator<<(std::ostream& ost, const Object& object); + + + /** Convert `ray` to object space from global space. */ + basics::Ray ToObjectSpace(basics::Ray ray) const; + + /** Convert `v` to object space from global space. */ + basics::Vector4 ToObjectSpace(const basics::Vector4& v) const; + + /** Convert `v` to global space from object space. */ + basics::Vector4 FromObjectSpace(const basics::Vector4& v) const; + + + /** A translation matrix from global coordinates to this object's space. */ + basics::Matrix4 mTranslation; + + /** This object's material, surface properties, etc. */ + Material mMaterial; +}; + + +/** Write a string representation of the Object to an output stream. */ +std::ostream& operator<<(std::ostream& ost, const Object& object); + +} /* namespace charles */ + +#endif diff --git a/src/objectBox.cc b/src/objectBox.cc index ab818a0..90b5670 100644 --- a/src/objectBox.cc +++ b/src/objectBox.cc @@ -8,66 +8,153 @@ #include "objectBox.hh" +using charles::basics::Ray; +using charles::basics::Vector4; + + namespace charles { +/* + * charles::Box::Box -- + */ Box::Box() /* A unit box centered on the origin. */ - : Box(Vector3(-0.5, -0.5, -0.5), Vector3(0.5, 0.5, 0.5)) + : Box(Vector4(-0.5, -0.5, -0.5), Vector4(0.5, 0.5, 0.5)) { } -Box::Box(const Vector3& near, const Vector3& far) +/* + * charles::Box::Box -- + */ +Box::Box(const Vector4& near, + const Vector4& far) : Object(), mNear(near), mFar(far) { } -Vector3& +/* + * charles::Box::GetNear -- + */ +Vector4& Box::GetNear() { return mNear; } +/* + * charles::Box::SetNear -- + */ void -Box::SetNear(const Vector3& near) +Box::SetNear(const Vector4& near) { mNear = near; } -Vector3& +/* + * charles::Box::GetFar -- + */ +Vector4& Box::GetFar() { return mFar; } +/* + * charles::Box::SetFar -- + */ void -Box::SetFar(const Vector3& far) +Box::SetFar(const Vector4& far) { mFar = far; } +/* + * charles::Box::DoNormal -- + */ +Vector4 +Box::DoNormal(const Vector4& p) + const +{ + if (NearlyEqual(p.X(), mNear.X())) { + return Vector4(-1, 0, 0); + } else if (NearlyEqual(p.X(), mFar.X())) { + return Vector4(1, 0, 0); + } else if (NearlyEqual(p.Y(), mNear.Y())) { + return Vector4(0, -1, 0); + } else if (NearlyEqual(p.Y(), mFar.Y())) { + return Vector4(0, 1, 0); + } else if (NearlyEqual(p.Z(), mNear.Z())) { + return Vector4(0, 0, -1); + } else if (NearlyEqual(p.Z(), mFar.Z())) { + return Vector4(0, 0, 1); + } + + /* TODO: Eventually, I might want to raise an error here. */ + return Vector4(); +} + + +/* + * charles::Box::DoIntersect -- + */ bool -Box::DoesIntersect(const Ray& ray, - TVector& t, - Stats& stats) +Box::DoIntersect(const Ray& ray, + TVector& t, + Stats& stats) const { stats.boxIntersectionTests++; - /* - * XXX: For now, I'm assuming that all boxes are parallel to the coordinate - * axes. This is the Kay-Kajiya box intersection algorithm. - */ + /* This is the Kay-Kajiya box intersection algorithm. */ - //Double t0, t1; Double tNear = -std::numeric_limits::infinity(); Double tFar = std::numeric_limits::infinity(); + if (!IntersectSlab(mNear.X(), mFar.X(), + ray.origin.X(), ray.direction.X(), + tNear, tFar)) { + return false; + } + if (!IntersectSlab(mNear.Y(), mFar.Y(), + ray.origin.Y(), ray.direction.Y(), + tNear, tFar)) { + return false; + } + if (!IntersectSlab(mNear.Z(), mFar.Z(), + ray.origin.Z(), ray.direction.Z(), + tNear, tFar)) { + return false; + } + + /* We have an intersection! */ + stats.boxIntersections++; + t.push_back(tNear); + t.push_back(tFar); + + return true; +} + + +/* + * charles::Box::IntersectSlab -- + */ +inline bool +Box::IntersectSlab(const Double& slabLow, + const Double& slabHigh, + const Double& rayOriginComponent, + const Double& rayDirectionComponent, + Double& tNear, + Double& tFar) + const +{ + Double t0, t1; + /* * From the Ray Tracing book: * @@ -82,77 +169,6 @@ Box::DoesIntersect(const Ray& ray, * planes (X, Y, and Z planes). */ - if (!IntersectSlab(mNear.x, mFar.x, ray.origin.x, ray.direction.x, tNear, tFar)) { - return false; - } - if (!IntersectSlab(mNear.y, mFar.y, ray.origin.y, ray.direction.y, tNear, tFar)) { - return false; - } - if (!IntersectSlab(mNear.z, mFar.z, ray.origin.z, ray.direction.z, tNear, tFar)) { - return false; - } - - /* We have an intersection! */ - stats.boxIntersections++; - t.push_back(tNear); - t.push_back(tFar); - - return true; -} - - -bool -Box::point_is_on_surface(const Vector3& p) - const -{ - if (p.x == mNear.x || p.x == mFar.x) { - return (p.y > mNear.y && p.y < mFar.y) - && (p.z > mNear.z && p.z < mFar.z); - } else if (p.y == mNear.y || p.y == mFar.y) { - return (p.x > mNear.x && p.x < mFar.x) - && (p.z > mNear.z && p.z < mFar.z); - } else if (p.z == mNear.z || p.z == mFar.z) { - return (p.x > mNear.x && p.x < mFar.x) - && (p.y > mNear.y && p.y < mFar.y); - } - return false; -} - - -Vector3 -Box::compute_normal(const Vector3& p) - const -{ - if (NearlyEqual(p.x, mNear.x)) { - return Vector3(-1, 0, 0); - } else if (NearlyEqual(p.x, mFar.x)) { - return Vector3(1, 0, 0); - } else if (NearlyEqual(p.y, mNear.y)) { - return Vector3(0, -1, 0); - } else if (NearlyEqual(p.y, mFar.y)) { - return Vector3(0, 1, 0); - } else if (NearlyEqual(p.z, mNear.z)) { - return Vector3(0, 0, -1); - } else if (NearlyEqual(p.z, mFar.z)) { - return Vector3(0, 0, 1); - } - - /* TODO: Eventually, I might want to raise an error here. */ - return Vector3(); -} - - -inline bool -Box::IntersectSlab(const Double& slabLow, - const Double& slabHigh, - const Double& rayOriginComponent, - const Double& rayDirectionComponent, - Double& tNear, - Double& tFar) - const -{ - Double t0, t1; - if (NearZero(rayDirectionComponent)) { /* The ray is parallel to the X axis. */ if (rayOriginComponent < slabLow || rayOriginComponent > slabHigh) { @@ -170,12 +186,13 @@ Box::IntersectSlab(const Double& slabLow, tNear = t1; tFar = t0; } -#endif +#else if (t0 > t1) { Double tmp = t0; t0 = t1; t1 = tmp; } +#endif if (t0 > tNear) { tNear = t0; } @@ -196,6 +213,9 @@ Box::IntersectSlab(const Double& slabLow, } +/* + * charles::Box::Write -- + */ void Box::Write(std::ostream& ost) const diff --git a/src/objectBox.hh b/src/objectBox.hh index 155f1c3..55b3b7b 100644 --- a/src/objectBox.hh +++ b/src/objectBox.hh @@ -3,9 +3,11 @@ * Eryn Wells */ -#include "basics.h" -#include "object.h" -#include "types.hh" +#ifndef __OBJECTBOX_HH__ +#define __OBJECTBOX_HH__ + +#include "object.hh" +#include "basics/basics.hh" namespace charles { @@ -14,20 +16,21 @@ struct Box : public Object { Box(); - Box(const Vector3& near, const Vector3& far); + Box(const basics::Vector4& near, const basics::Vector4& far); - Vector3& GetNear(); - void SetNear(const Vector3& near); - Vector3& GetFar(); - void SetFar(const Vector3& far); + basics::Vector4& GetNear(); + void SetNear(const basics::Vector4& near); - bool DoesIntersect(const Ray& ray, TVector& t, Stats& stats) const; - bool point_is_on_surface(const Vector3 &p) const; - Vector3 compute_normal(const Vector3 &p) const; + basics::Vector4& GetFar(); + void SetFar(const basics::Vector4& far); /** @see charles::Object::Write */ void Write(std::ostream& ost) const; +protected: + bool DoIntersect(const basics::Ray& ray, TVector& t, Stats& stats) const; + basics::Vector4 DoNormal(const basics::Vector4& p) const; + private: /** * Perform the intersection test on a slab, defined by `slabHigh` and @@ -49,10 +52,12 @@ private: Double& tFar) const; /** The near, lower left corner. */ - Vector3 mNear; + basics::Vector4 mNear; /** The far, upper right corner. */ - Vector3 mFar; + basics::Vector4 mFar; }; } /* namespace charles */ + +#endif /* __OBJECTBOX_HH__ */ diff --git a/src/objectPlane.cc b/src/objectPlane.cc index d94a8e5..cbd3fd1 100644 --- a/src/objectPlane.cc +++ b/src/objectPlane.cc @@ -10,22 +10,28 @@ #include #include -#include "basics.h" -#include "object.h" #include "objectPlane.hh" + +using charles::basics::Ray; +using charles::basics::Vector4; + + namespace charles { /* * charles::Plane::Plane -- */ Plane::Plane() - : mNormal(Vector3::Y), + : mNormal(0, 1, 0), mDistance(0.0) { } -const Vector3& +/* + * charles::Plane::GetNormal -- + */ +const Vector4& Plane::GetNormal() const { @@ -33,13 +39,19 @@ Plane::GetNormal() } +/* + * charles::Plane::SetNormal -- + */ void -Plane::SetNormal(const Vector3& normal) +Plane::SetNormal(const Vector4& normal) { - mNormal = normal.normalized(); + mNormal = basics::Normalized(normal); } +/* + * charles::Plane::GetDistance -- + */ Double Plane::GetDistance() const @@ -48,6 +60,9 @@ Plane::GetDistance() } +/* + * charles::Plane::SetDistance -- + */ void Plane::SetDistance(Double distance) { @@ -59,9 +74,9 @@ Plane::SetDistance(Double distance) * charles::Plane::DoesIntersect -- */ bool -Plane::DoesIntersect(const Ray &ray, - TVector& t, - Stats& stats) +Plane::DoIntersect(const Ray& ray, + TVector& t, + Stats& stats) const { /* @@ -94,14 +109,14 @@ Plane::DoesIntersect(const Ray &ray, stats.planeIntersectionTests++; /* The denominator for the t equation above. */ - Double vd = mNormal.dot(ray.direction); + Double vd = mNormal.Dot(ray.direction); if (NearZero(vd)) { /* The ray is parallel to the plane. */ return false; } /* The numerator of the equation for t above. */ - Double vo = -(mNormal.dot(ray.origin) + mDistance); + Double vo = -(mNormal.Dot(ray.origin) + mDistance); Double t0 = vo / vd; if (t0 < 0.0) { @@ -119,33 +134,14 @@ Plane::DoesIntersect(const Ray &ray, } -/* - * charles::Plane::point_is_on_surface -- - */ -bool -Plane::point_is_on_surface(const Vector3 &p) - const -{ - /* - * Plug point p into the equation for a plane: - * - * A * x + B * y + C * z + D = 0 - * - * where (A, B, C) are the coordinates of the normal vector, and D is the - * distance along that vector from the origin. - */ - return NearZero(mNormal.dot(p) + mDistance); -} - - #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" /* - * charles::Plane::compute_normal -- + * charles::Plane::DoNormal -- */ -Vector3 -Plane::compute_normal(const Vector3 &p) +Vector4 +Plane::DoNormal(const Vector4& p) const { return mNormal; @@ -154,6 +150,9 @@ Plane::compute_normal(const Vector3 &p) #pragma clang diagnostic pop +/* + * charles::Plane::Write -- + */ void Plane::Write(std::ostream& ost) const diff --git a/src/objectPlane.hh b/src/objectPlane.hh index 9dabbb4..38c6207 100644 --- a/src/objectPlane.hh +++ b/src/objectPlane.hh @@ -8,9 +8,9 @@ #ifndef __OBJECT_PLANE_H__ #define __OBJECT_PLANE_H__ -#include "basics.h" -#include "object.h" -#include "types.hh" +#include "object.hh" +#include "basics/basics.hh" + namespace charles { @@ -21,25 +21,22 @@ public: /** Default constructor. Creates a plane with a normal along the Y axis. */ Plane(); - const Vector3& GetNormal() const; - void SetNormal(const Vector3& normal); + const basics::Vector4& GetNormal() const; + void SetNormal(const basics::Vector4& normal); Double GetDistance() const; void SetDistance(Double distance); - /** - * @see charles::Object::DoesIntersect - */ - bool DoesIntersect(const Ray &ray, TVector& t, Stats& stats) const; - bool point_is_on_surface(const Vector3 &p) const; - Vector3 compute_normal(const Vector3 &p) const; - /** @see charles::Object::Write */ void Write(std::ostream& ost) const; +protected: + bool DoIntersect(const basics::Ray& ray, TVector& t, Stats& stats) const; + basics::Vector4 DoNormal(const basics::Vector4& p) const; + private: /** A normal vector, which specified the orientation of the plane. */ - Vector3 mNormal; + basics::Vector4 mNormal; /** * The distance from the origin along the normal vector that this plane is diff --git a/src/objectSphere.cc b/src/objectSphere.cc index 5adfb4d..a6229df 100644 --- a/src/objectSphere.cc +++ b/src/objectSphere.cc @@ -1,7 +1,5 @@ -/* object_sphere.h - * - * Spheres are Scene objects defined by a center point and a radius. - * +/* objectSphere.cc + * vim: set tw=80: * Eryn Wells */ @@ -10,39 +8,28 @@ #include #include -#include "basics.h" -#include "object.h" #include "objectSphere.hh" + +using charles::basics::Ray; +using charles::basics::Vector4; + + namespace charles { /* - * Sphere::Sphere -- - * - * Default constructor. Create a Sphere with radius 1.0. + * charles::Sphere::Sphere -- */ -Sphere::Sphere() - : Sphere(1.0) +Sphere::Sphere(const Vector4& origin, + Double radius) + : Object(origin), + mRadius(radius) { } /* - * Sphere::Sphere -- - * - * Constructor. Create a Sphere with the given radius. + * charles::Sphere::GetRadius -- */ -Sphere::Sphere(Double r) - : Sphere(Vector3::Zero, r) -{ } - - -Sphere::Sphere(Vector3 o, - Double r) - : Object(o), - mRadius(r) -{ } - - Double Sphere::GetRadius() const @@ -51,31 +38,31 @@ Sphere::GetRadius() } +/* + * charles::Sphere::SetRadius -- + */ void -Sphere::SetRadius(Double r) +Sphere::SetRadius(const Double& r) { mRadius = std::fabs(r); } /* - * Sphere::DoesIntersect -- + * charles::Sphere::DoesIntersect -- */ bool -Sphere::DoesIntersect(const Ray& ray, - TVector& t, - Stats& stats) +Sphere::DoIntersect(const Ray& ray, + TVector& t, + Stats& stats) const { stats.sphereIntersectionTests++; - /* Origin of the vector in object space. */ - Vector3 rayOriginObj = ray.origin - GetOrigin(); - /* Coefficients for quadratic equation. */ - Double a = ray.direction.dot(ray.direction); - Double b = ray.direction.dot(rayOriginObj) * 2.0; - Double c = rayOriginObj.dot(rayOriginObj) - (mRadius * mRadius); + Double a = ray.direction.Dot(ray.direction); + Double b = ray.direction.Dot(ray.origin) * 2.0; + Double c = ray.origin.Dot(ray.origin) - (mRadius * mRadius); /* Discriminant for the quadratic equation. */ Double discrim = (b * b) - (4.0 * a * c); @@ -92,7 +79,7 @@ Sphere::DoesIntersect(const Ray& ray, * Compute the intersections, the roots of the quadratic equation. Spheres * have at most two intersections. */ - Double sqrtDiscrim = sqrt(discrim); + Double sqrtDiscrim = std::sqrt(discrim); Double t0 = (-b - sqrtDiscrim) / (2.0 * a); Double t1 = (-b + sqrtDiscrim) / (2.0 * a); @@ -125,45 +112,28 @@ Sphere::DoesIntersect(const Ray& ray, /* - * Sphere::point_is_on_surface -- - * - * Determine if a point lies on the surface of this Sphere. + * charles::Sphere::DoNormal -- */ -bool -Sphere::point_is_on_surface(const Vector3 &p) +Vector4 +Sphere::DoNormal(const Vector4& p) const { - Vector3 o = GetOrigin(); - Double x = p.x - o.x; - Double y = p.y - o.y; - Double z = p.z - o.z; - - return x*x + y*y + z*z == mRadius*mRadius; + /* + * The fun thing about sphere is the normal to any point on the sphere is + * the point itself. Woo! + */ + return Normalized(p); } /* - * Sphere::compute_normal -- - * - * Compute the normal for this Sphere at the given point. If the point does not lie on the surface of the sphere, a zero - * vector is returned. + * charles::Sphere::Write -- */ -Vector3 -Sphere::compute_normal(const Vector3 &p) - const -{ - // The fun thing about sphere is the normal to any point on the sphere is the point itself. Woo! - Vector3 normal = p - GetOrigin(); - normal.normalize(); - return normal; -} - - void Sphere::Write(std::ostream& ost) const { - ost << "[Sphere origin=" << GetOrigin() << " r=" << mRadius << "]"; + ost << "[Sphere r=" << mRadius << "]"; } } /* namespace charles */ diff --git a/src/objectSphere.hh b/src/objectSphere.hh index 12f9061..e5e9499 100644 --- a/src/objectSphere.hh +++ b/src/objectSphere.hh @@ -8,8 +8,9 @@ #ifndef __OBJECTSPHERE_HH__ #define __OBJECTSPHERE_HH__ -#include "basics.h" -#include "object.h" +#include "object.hh" +#include "basics/basics.hh" + namespace charles { @@ -17,20 +18,18 @@ class Sphere : public Object { public: - Sphere(); - Sphere(Double r); - Sphere(Vector3 o, Double r); + Sphere(const basics::Vector4& origin = basics::Vector4(), Double radius = 1.0); Double GetRadius() const; - void SetRadius(Double r); - - bool DoesIntersect(const Ray& ray, TVector& t, Stats& stats) const; - bool point_is_on_surface(const Vector3 &p) const; - Vector3 compute_normal(const Vector3 &p) const; + void SetRadius(const Double& r); /** @see charles::Object::Write */ void Write(std::ostream& ost) const; +protected: + bool DoIntersect(const basics::Ray& ray, TVector& t, Stats& stats) const override; + basics::Vector4 DoNormal(const basics::Vector4& p) const override; + private: Double mRadius; }; diff --git a/src/reader.hh b/src/reader.hh index b235953..a6ef0e7 100644 --- a/src/reader.hh +++ b/src/reader.hh @@ -2,17 +2,18 @@ * vim: set tw=80: * Eryn Wells */ -/** - * Interface for an input file reader. - */ - #ifndef __READER_HH__ #define __READER_HH__ -#include "scene.h" +#include "scene.hh" +namespace charles { + +/** + * Interface for an input file reader. + */ struct Reader { Reader(Scene& scene) @@ -29,4 +30,6 @@ protected: Scene& mScene; }; +} /* namespace charles */ + #endif /* __READER_HH__ */ diff --git a/src/reader_yaml.cc b/src/reader_yaml.cc index 4df4f32..d376024 100644 --- a/src/reader_yaml.cc +++ b/src/reader_yaml.cc @@ -14,11 +14,15 @@ #include "yaml.h" #include "charles.hh" +#include "log.hh" #include "reader_yaml.hh" #include "yaml/parsers.hh" #include "yaml/sceneParser.hh" +#define LOG_NAME "yaml" +#include "logModule.hh" + #if 0 struct ObjectParser @@ -58,6 +62,7 @@ private: }; #endif +namespace charles { ssize_t YAMLReader::read_file(const std::string& filename) @@ -86,47 +91,45 @@ YAMLReader::read_file(const std::string& filename) goto error; } - if (verbosity >= 4) { - switch (event.type) { - case YAML_NO_EVENT: - printf("YAML_NO_EVENT\n"); - break; + switch (event.type) { + case YAML_NO_EVENT: + LOG_TRACE << "YAML_NO_EVENT"; + break; - case YAML_STREAM_START_EVENT: - printf("YAML_STREAM_START_EVENT\n"); - break; - case YAML_STREAM_END_EVENT: - printf("YAML_STREAM_END_EVENT\n"); - break; + case YAML_STREAM_START_EVENT: + LOG_TRACE << "YAML_STREAM_START_EVENT"; + break; + case YAML_STREAM_END_EVENT: + LOG_TRACE << "YAML_STREAM_END_EVENT"; + break; - case YAML_DOCUMENT_START_EVENT: - printf("YAML_DOCUMENT_START_EVENT\n"); - break; - case YAML_DOCUMENT_END_EVENT: - printf("YAML_DOCUMENT_END_EVENT\n"); - break; + case YAML_DOCUMENT_START_EVENT: + LOG_TRACE << "YAML_DOCUMENT_START_EVENT"; + break; + case YAML_DOCUMENT_END_EVENT: + LOG_TRACE << "YAML_DOCUMENT_END_EVENT"; + break; - case YAML_ALIAS_EVENT: - printf("YAML_ALIAS_EVENT\n"); - break; - case YAML_SCALAR_EVENT: - printf("YAML_SCALAR_EVENT\n"); - break; + case YAML_ALIAS_EVENT: + LOG_TRACE << "YAML_ALIAS_EVENT"; + break; + case YAML_SCALAR_EVENT: + LOG_TRACE << "YAML_SCALAR_EVENT"; + break; - case YAML_SEQUENCE_START_EVENT: - printf("YAML_SEQUENCE_START_EVENT\n"); - break; - case YAML_SEQUENCE_END_EVENT: - printf("YAML_SEQUENCE_END_EVENT\n"); - break; + case YAML_SEQUENCE_START_EVENT: + LOG_TRACE << "YAML_SEQUENCE_START_EVENT"; + break; + case YAML_SEQUENCE_END_EVENT: + LOG_TRACE << "YAML_SEQUENCE_END_EVENT"; + break; - case YAML_MAPPING_START_EVENT: - printf("YAML_MAPPING_START_EVENT\n"); - break; - case YAML_MAPPING_END_EVENT: - printf("YAML_MAPPING_END_EVENT\n"); - break; - } + case YAML_MAPPING_START_EVENT: + LOG_TRACE << "YAML_MAPPING_START_EVENT"; + break; + case YAML_MAPPING_END_EVENT: + LOG_TRACE << "YAML_MAPPING_END_EVENT"; + break; } if (event.type == YAML_DOCUMENT_START_EVENT) { @@ -152,3 +155,5 @@ error: yaml_parser_delete(&parser); return success; } + +} /* namespace charles */ diff --git a/src/reader_yaml.hh b/src/reader_yaml.hh index ecf4c09..62b5829 100644 --- a/src/reader_yaml.hh +++ b/src/reader_yaml.hh @@ -2,18 +2,18 @@ * vim: set tw=80: * Eryn Wells */ + +#ifndef __READERYAML_HH__ +#define __READERYAML_HH__ + +#include "reader.hh" +#include "scene.hh" + +namespace charles { + /** * An input file reader for YAML files. */ - - -#ifndef __READER_YAML_HH__ -#define __READER_YAML_HH__ - -#include "reader.hh" -#include "scene.h" - - struct YAMLReader : public Reader { @@ -21,9 +21,9 @@ struct YAMLReader : Reader(scene) { } - ~YAMLReader() { } - ssize_t read_file(const std::string& filename); }; +} /* namespace charles */ + #endif /* __READER_YAML_HH__ */ diff --git a/src/scene.cc b/src/scene.cc index 9aae326..a48ec14 100644 --- a/src/scene.cc +++ b/src/scene.cc @@ -5,84 +5,123 @@ * Eryn Wells */ -#include +#include #include #include +#include -#include "basics.h" -#include "light.h" +#include "scene.hh" + +#include "light.hh" #include "log.hh" -#include "object.h" -#include "scene.h" +#include "object.hh" #include "writer.h" +#include "basics/basics.hh" #define LOG_NAME "scene" #include "logModule.hh" -using namespace charles; + +using charles::basics::Color; +using charles::basics::Ray; +using charles::basics::Vector4; +namespace charles { + +/* + * charles::Scene::Scene -- + */ Scene::Scene() - : width(640), height(480), + : mWidth(640), + mHeight(480), mCamera(new PerspectiveCamera()), - max_depth(5), - min_weight(1e-4), - ambient(new AmbientLight()), - shapes(), - lights(), + mMaxDepth(5), + mMinWeight(1e-6), + mAmbient(Color(1, 1, 1), 0.2), + mObjects(), + mLights(), mStats(), - pixels(NULL) + mPixels(NULL) { } +/* + * charles::Scene::~Scene -- + */ Scene::~Scene() { mCamera.reset(); - if (ambient != NULL) { - delete ambient; - } + mObjects.clear(); - shapes.clear(); - - for (PointLight *l : lights) { + for (PointLight *l : mLights) { delete l; } - lights.clear(); + mLights.clear(); - if (pixels != NULL) { - delete[] pixels; - _is_rendered = false; + if (mPixels != NULL) { + delete[] mPixels; + mIsRendered = false; } } -bool -Scene::is_rendered() - const -{ - return _is_rendered; -} - - -int -Scene::get_width() - const -{ - return width; -} - - -int -Scene::get_height() - const -{ - return height; -} - - /* - * Scene::GetCamera -- + * charles::Scene::IsRendered -- + */ +bool +Scene::IsRendered() + const +{ + return mIsRendered; +} + + +/* + * charles::Scene::GetWidth -- + */ +UInt +Scene::GetWidth() + const +{ + return mWidth; +} + + +/* + * charles::Scene::SetWidth -- + */ +void +Scene::SetWidth(UInt w) +{ + mWidth = w; +} + + +/* + * charles::Scene::GetHeight -- + */ +UInt +Scene::GetHeight() + const +{ + return mHeight; +} + + +/* + * charles::Scene::SetHeight -- + */ +void +Scene::SetHeight(UInt h) +{ + mHeight = h; +} + + +/* + * charles::Scene::GetCamera -- */ Camera::Ptr Scene::GetCamera() @@ -93,63 +132,55 @@ Scene::GetCamera() /* - * Scene::SetCamera -- + * charles::Scene::SetCamera -- */ void -Scene::SetCamera(Camera* camera) +Scene::SetCamera(Camera::Ptr camera) { - mCamera.reset(camera); -} - - -AmbientLight & -Scene::get_ambient() - const -{ - return *ambient; -} - - -const Color * -Scene::get_pixels() - const -{ - return pixels; + mCamera = camera; } /* - * scene_load -- - * - * Load scene objects into this Scene from the given file. + * charles::Scene::GetAmbient -- */ -void -Scene::read(const std::string &filename) -{ } +AmbientLight& +Scene::GetAmbient() +{ + return mAmbient; +} /* - * scene_save -- - * - * Write a rendered scene to the given file. + * charles::Scene::GetPixels -- + */ +const Color* +Scene::GetPixels() + const +{ + return mPixels; +} + + +/* + * charles::Scene::Write -- */ void -Scene::write(Writer &writer, const std::string &filename) +Scene::Write(Writer& writer, + const std::string& filename) { writer.write_scene(*this, filename); } /* - * Scene::render -- - * - * Render the given Scene. + * charles::Scene::Render -- */ void -Scene::render() +Scene::Render() { - LOG_INFO << "Rendering scene with " << shapes.size() << " objects."; - printf("Rendering scene with %lu objects.\n", shapes.size()); + LOG_INFO << "Rendering scene with " << mObjects.size() << " objects."; + printf("Rendering scene with %lu objects.\n", mObjects.size()); LogCamera(); LogObjects(); @@ -157,23 +188,23 @@ Scene::render() std::chrono::time_point start, end; start = std::chrono::system_clock::now(); - pixels = new Color[width * height]; + mPixels = new Color[mWidth * mHeight]; - Ray primary_ray; - Vector3 o, d; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - primary_ray = mCamera->compute_primary_ray(x, width, y, height); + Ray primaryRay; + Vector4 o, d; + for (UInt y = 0; y < mHeight; y++) { + for (UInt x = 0; x < mWidth; x++) { + primaryRay = mCamera->PrimaryRay(x, mWidth, y, mHeight); mStats.primaryRays++; - Color c = trace_ray(primary_ray); - pixels[y * width + x] = c; + Color c = TraceRay(primaryRay); + mPixels[y * mWidth + x] = c; } } end = std::chrono::system_clock::now(); - std::chrono::duration seconds = end - start; + std::chrono::duration seconds = end - start; - _is_rendered = true; + mIsRendered = true; printf("Rendering completed in %f seconds.\n\n", seconds.count()); LOG_INFO << "Rendering completed in " << seconds.count() << " seconds."; @@ -184,83 +215,77 @@ Scene::render() /* - * Scene::add_shape -- - * - * Add a shape to the scene. + * charles::Scene::AddObject -- */ void -Scene::add_shape(Object::Ptr shape) +Scene::AddObject(Object::Ptr obj) { - shapes.push_back(shape); + mObjects.push_back(obj); } /* - * Scene::add_light -- - * - * Add a light to the scene. + * charles::Scene::AddLight -- */ void -Scene::add_light(PointLight *light) +Scene::AddLight(PointLight* light) { - lights.push_back(light); + mLights.push_back(light); } /* - * Scene::trace_ray -- - * - * Trace the given ray through the scene, recursing until depth has been reached. + * charles::Scene::TraceRay -- */ Color -Scene::trace_ray(const Ray &ray, - const int depth, - const float weight) +Scene::TraceRay(const Ray &ray, + const int depth, + const Double weight) { - if (depth >= max_depth || weight <= min_weight) { + if (depth >= mMaxDepth || weight <= mMinWeight) { return Color::Black; } - Color out_color = Color::Black; - Object::Ptr intersected_shape; + Color outColor = Color::Black; + Object::Ptr intersectedObj; TVector ts; - Double nearest_t = INFINITY; + Double nearestT = INFINITY; ts.reserve(2); // Find intersections of this ray with objects in the scene. - for (Object::Ptr s : shapes) { + for (Object::Ptr obj : mObjects) { ts.clear(); - if (s->DoesIntersect(ray, ts, mStats)) { - if (ts[0] < nearest_t) { - intersected_shape = s; - nearest_t = ts[0]; + if (obj->Intersect(ray, ts, mStats)) { + if (ts[0] < nearestT) { + intersectedObj = obj; + nearestT = ts[0]; } } } // If there was no intersection, return black. - if (!intersected_shape) { - return out_color; + if (!intersectedObj) { + return outColor; } - Material& shape_material = intersected_shape->GetMaterial(); - const Color& shape_color = shape_material.GetDiffuseColor(); + Material& material = intersectedObj->GetMaterial(); + const Color& shapeColor = material.GetDiffuseColor(); - Vector3 intersection = ray.parameterize(nearest_t); - Vector3 normal = intersected_shape->compute_normal(intersection); + Vector4 intersection = ray.Parameterize(nearestT); + Vector4 normal = intersectedObj->Normal(intersection); /* * Diffuse lighting. (Shading, etc.) */ - Vector3 light_direction; - Double ldotn, diffuse_level, ambient_level; + Vector4 lightDirection; + Double ldotn, diffuseLevel, ambientLevel; Ray shadowRay; - for (PointLight *l : lights) { - light_direction = (l->GetOrigin() - intersection).normalize(); - ldotn = light_direction.dot(normal); + for (PointLight *l : mLights) { + lightDirection = basics::Normalized(l->GetOrigin() - intersection); + ldotn = lightDirection.Dot(normal); /* * TODO: What is this even for? Removing it makes the darker showers @@ -270,12 +295,12 @@ Scene::trace_ray(const Ray &ray, ldotn = 0.0; } - diffuse_level = shape_material.GetDiffuseIntensity(); - ambient_level = 1.0 - diffuse_level; + diffuseLevel = material.GetDiffuseIntensity(); + ambientLevel = 1.0 - diffuseLevel; - shadowRay = Ray(intersection, light_direction); - for (Object::Ptr s : shapes) { - if (s == intersected_shape) { + shadowRay = Ray(intersection, lightDirection); + for (Object::Ptr obj : mObjects) { + if (obj == intersectedObj) { /* Skip the intersected shape. */ continue; } @@ -284,8 +309,8 @@ Scene::trace_ray(const Ray &ray, /* Figure out if we're in shadow. */ ts.clear(); - if (s->DoesIntersect(shadowRay, ts, mStats)) { - diffuse_level = 0.0; + if (obj->Intersect(shadowRay, ts, mStats)) { + diffuseLevel = 0.0; break; } } @@ -293,8 +318,8 @@ Scene::trace_ray(const Ray &ray, /* * Compute basic Lambert diffuse shading for this object. */ - out_color += shape_color * ( ambient_level * ambient->compute_color_contribution() - + diffuse_level * ldotn); + outColor += shapeColor * ( ambientLevel * mAmbient.Contribution() + + diffuseLevel * ldotn); } /* @@ -328,10 +353,13 @@ Scene::trace_ray(const Ray &ray, out_color += specular_level * specular_color * reflection_color; #endif - return out_color; + return outColor; } +/* + * charles::Scene::LogCamera -- + */ void Scene::LogCamera() const @@ -341,7 +369,7 @@ Scene::LogCamera() LOG_DEBUG << "BEGIN CAMERA"; LOG_DEBUG << " type = " << c.GetTypeString(); LOG_DEBUG << " origin = " << c.GetOrigin(); - LOG_DEBUG << " direction = " << c.get_direction(); + LOG_DEBUG << " direction = " << c.GetDirection(); LOG_DEBUG << " right = " << c.GetRight(); LOG_DEBUG << " up = " << c.GetUp(); LOG_DEBUG << " coordinate system is " @@ -350,15 +378,20 @@ Scene::LogCamera() } +/* + * charles::Scene::LogObjects -- + */ void Scene::LogObjects() const { LOG_DEBUG << "BEGIN SCENE OBJECTS"; - for (Object::Ptr obj : shapes) { + for (Object::Ptr obj : mObjects) { LOG_DEBUG << " " << *obj; } LOG_DEBUG << "END SCENE OBJECTS"; } + +} /* namespace charles */ diff --git a/src/scene.h b/src/scene.h deleted file mode 100644 index d8a86ad..0000000 --- a/src/scene.h +++ /dev/null @@ -1,84 +0,0 @@ -/* scene.h - * - * Definition of the Scene class. - * - * Scenes are the top level object in charles. Scenes contain objects, lights, a camera, - * etc. and can be rendered to pixel data and written to a file. - * - * Eryn Wells - */ - -#ifndef __SCENE_H__ -#define __SCENE_H__ - -#include -#include -#include "basics.h" -#include "camera.h" -#include "object.h" -#include "stats.hh" - - -class AmbientLight; -class PointLight; -class Writer; - - -class Scene -{ -public: - Scene(); - ~Scene(); - - bool is_rendered() const; - - int get_width() const; - void set_width(int w) { width = w; } - int get_height() const; - void set_height(int h) { height = h; } - - Camera::Ptr GetCamera() const; - void SetCamera(Camera* camera); - - AmbientLight &get_ambient() const; - const Color *get_pixels() const; - - void read(const std::string &filename); - void write(Writer &writer, const std::string &filename); - void render(); - - void add_shape(charles::Object::Ptr obj); - void add_light(PointLight *light); - -private: - Color trace_ray(const Ray &ray, const int depth = 0, const float weight = 1.0); - - void LogCamera() const; - void LogObjects() const; - - // Pixel dimensions of the image. - int width, height; - - Camera::Ptr mCamera; - - /* - * Ray tracing parameters. max_depth indicates the maximum depth of the ray tree. min_weight indicates the minimum - * specular weight to apply before giving up. - */ - int max_depth; - float min_weight; - - // Scene objects. - AmbientLight *ambient; - std::list shapes; - std::list lights; - - // Rendering stats - charles::Stats mStats; - - // Rendering output. - bool _is_rendered; - Color *pixels; -}; - -#endif diff --git a/src/scene.hh b/src/scene.hh new file mode 100644 index 0000000..14031e5 --- /dev/null +++ b/src/scene.hh @@ -0,0 +1,87 @@ +/* scene.hh + * vim: tw=80: + * Eryn Wells + */ + +#ifndef __SCENE_H__ +#define __SCENE_H__ + +#include +#include + +#include "camera.hh" +#include "light.hh" +#include "lightPoint.hh" +#include "object.hh" +#include "stats.hh" +#include "basics/basics.hh" + + +namespace charles { + +class Writer; + + +/** + * Scenes are the top level object in charles. Scenes contain objects, lights, a + * camera, etc. and can be rendered to pixel data and written to a file. + */ +struct Scene +{ + Scene(); + ~Scene(); + + UInt GetWidth() const; + void SetWidth(UInt w); + UInt GetHeight() const; + void SetHeight(UInt h); + + Camera::Ptr GetCamera() const; + void SetCamera(Camera::Ptr camera); + + void Write(Writer& writer, const std::string& filename); + + void Render(); + bool IsRendered() const; + const basics::Color* GetPixels() const; + + AmbientLight& GetAmbient(); + void AddObject(Object::Ptr obj); + void AddLight(PointLight* light); + +private: + basics::Color TraceRay(const basics::Ray &ray, + const int depth = 0, + const Double weight = 1.0); + + void LogCamera() const; + void LogObjects() const; + + /** Pixel width */ + UInt mWidth; + /** Pixel height */ + UInt mHeight; + + Camera::Ptr mCamera; + + /** Maximum depth of the ray tree */ + int mMaxDepth; + /** Minimum specular weight to apply before giving up */ + float mMinWeight; + + // Scene objects. + AmbientLight mAmbient; + std::list mObjects; + std::list mLights; + + // Rendering stats + charles::Stats mStats; + + // Rendering output. + bool mIsRendered; + basics::Color *mPixels; +}; + +} /* namespace charles */ + +#endif diff --git a/src/writer.h b/src/writer.h index 1d3bee1..981a17e 100644 --- a/src/writer.h +++ b/src/writer.h @@ -11,7 +11,9 @@ #include -class Scene; +namespace charles { + +struct Scene; class Writer @@ -24,4 +26,6 @@ public: virtual int write_scene(const Scene &scene, const std::string &filename) = 0; }; +} /* namespace charles */ + #endif diff --git a/src/writer_png.cc b/src/writer_png.cc index 3001159..6a04187 100644 --- a/src/writer_png.cc +++ b/src/writer_png.cc @@ -9,18 +9,24 @@ #include #include -#include "scene.h" +#include "scene.hh" #include "writer_png.h" +#include "basics/basics.hh" extern "C" { #include } +using charles::basics::Color; + + static void png_user_error(png_structp png, png_const_charp msg); static void png_user_warning(png_structp png, png_const_charp msg); +namespace charles { + /* * PNGWriter::write_scene -- * @@ -29,7 +35,7 @@ static void png_user_warning(png_structp png, png_const_charp msg); int PNGWriter::write_scene(const Scene &scene, const std::string &filename) { - if (!scene.is_rendered()) { + if (!scene.IsRendered()) { return -1; } @@ -62,7 +68,7 @@ PNGWriter::write_scene(const Scene &scene, const std::string &filename) * - No compression */ png_set_IHDR(png, png_info, - scene.get_width(), scene.get_height(), + scene.GetWidth(), scene.GetHeight(), 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, @@ -75,21 +81,20 @@ PNGWriter::write_scene(const Scene &scene, const std::string &filename) png_write_info(png, png_info); // Write it! - const Color *pixels = scene.get_pixels(); + const Color* pixels = scene.GetPixels(); png_byte *row = NULL; int nbytes = 0; - for (int y = 0; y < scene.get_height(); y++) { - row = new png_byte[scene.get_width() * 3]; + for (UInt y = 0; y < scene.GetHeight(); y++) { + row = new png_byte[scene.GetWidth() * 3]; if (row == NULL) { // TODO: DANGER! WILL ROBINSON! } - for (int x = 0; x < scene.get_width(); x++) { - Color c = pixels[y * scene.get_width() + x]; + for (UInt x = 0; x < scene.GetWidth(); x++) { + Color c = pixels[y * scene.GetWidth() + x]; row[x*3+0] = 0xff * c.red; row[x*3+1] = 0xff * c.green; row[x*3+2] = 0xff * c.blue; nbytes += 3; - } png_write_row(png, row); delete[] row; @@ -103,6 +108,8 @@ PNGWriter::write_scene(const Scene &scene, const std::string &filename) return nbytes; } +} /* namespace charles */ + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" @@ -130,4 +137,4 @@ png_user_warning(png_structp png, png_const_charp msg) { } -#pragma clang diagnostic pop +#pragma clang diagnostic pop \ No newline at end of file diff --git a/src/writer_png.h b/src/writer_png.h index 5a3918f..0507aea 100644 --- a/src/writer_png.h +++ b/src/writer_png.h @@ -1,16 +1,19 @@ /* writer_png.h - * - * Declaration of the PNG writer. - * + * vim: set tw=80: * Eryn Wells */ -#ifndef __WRITER_PNG_H__ -#define __WRITER_PNG_H__ +#ifndef __WRITERPNG_HH__ +#define __WRITERPNG_HH__ #include "writer.h" +namespace charles { + +/** + * Write for producing PNG files. + */ class PNGWriter : public Writer { @@ -18,4 +21,6 @@ public: int write_scene(const Scene &scene, const std::string &filename); }; +} /* namespace charles */ + #endif diff --git a/src/yaml/cameraParser.cc b/src/yaml/cameraParser.cc index 2ae49fa..2db4cdc 100644 --- a/src/yaml/cameraParser.cc +++ b/src/yaml/cameraParser.cc @@ -5,11 +5,15 @@ #include -#include "camera.h" +#include "camera.hh" #include "yaml/cameraParser.hh" #include "yaml/vectorParser.hh" +using charles::basics::Vector4; + + +namespace charles { namespace yaml { CameraParser::CameraParser(Scene& scene, @@ -86,13 +90,13 @@ CameraParser::HandleDirectionEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 origin) { - mCamera->set_direction(origin); + auto onDone = [this](Vector4 direction) { + mCamera->SetDirection(direction); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -105,13 +109,13 @@ CameraParser::HandleLookAtEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 lookAt) { + auto onDone = [this](Vector4 lookAt) { mCamera->LookAt(lookAt); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -124,13 +128,13 @@ CameraParser::HandleOriginEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 origin) { + auto onDone = [this](Vector4 origin) { mCamera->SetOrigin(origin); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -143,13 +147,13 @@ CameraParser::HandleRightEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 right) { + auto onDone = [this](Vector4 right) { mCamera->SetRight(right); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -165,18 +169,14 @@ CameraParser::HandleTypeEvent(yaml_event_t& event) event.data.scalar.length); if (value == "perspective") { if (mType == TypeOrthographic) { - Camera *newCamera = new PerspectiveCamera(*mCamera); - delete mCamera; - mCamera = newCamera; - GetScene().SetCamera(newCamera); + mCamera.reset(new PerspectiveCamera(*mCamera)); + GetScene().SetCamera(mCamera); } } else if (value == "orthographic") { if (mType == TypePerspective) { - Camera *newCamera = new OrthographicCamera(*mCamera); - delete mCamera; - mCamera = newCamera; - GetScene().SetCamera(newCamera); + mCamera.reset(new OrthographicCamera(*mCamera)); + GetScene().SetCamera(mCamera); } } else { @@ -197,13 +197,14 @@ CameraParser::HandleUpEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 origin) { + auto onDone = [this](Vector4 origin) { mCamera->SetUp(origin); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } } /* namespace yaml */ +} /* namespace charles */ diff --git a/src/yaml/cameraParser.hh b/src/yaml/cameraParser.hh index 6724b16..13223ff 100644 --- a/src/yaml/cameraParser.hh +++ b/src/yaml/cameraParser.hh @@ -12,6 +12,7 @@ struct Camera; +namespace charles { namespace yaml { struct CameraParser @@ -47,12 +48,13 @@ private: void HandleTypeEvent(yaml_event_t& event); void HandleUpEvent(yaml_event_t& event); - Camera *mCamera; + Camera::Ptr mCamera; Section mSection; Type mType; }; } /* namespace yaml */ +} /* namespace charles */ #endif /* __YAML_CAMERAPARSER_HH__ */ diff --git a/src/yaml/objectParser.cc b/src/yaml/objectParser.cc index f21b511..330b001 100644 --- a/src/yaml/objectParser.cc +++ b/src/yaml/objectParser.cc @@ -10,8 +10,8 @@ #include #include -#include "material.h" -#include "object.h" +#include "material.hh" +#include "object.hh" #include "objectBox.hh" #include "objectPlane.hh" #include "objectSphere.hh" @@ -19,9 +19,11 @@ #include "yaml/vectorParser.hh" -using namespace charles; +using charles::basics::Color; +using charles::basics::Vector4; +namespace charles { namespace yaml { ObjectParser::ObjectParser(Scene& scene, @@ -43,7 +45,7 @@ ObjectParser::ObjectParser(Scene& scene, } else { assert(false); } - GetScene().add_shape(mObject); + GetScene().AddObject(mObject); } @@ -171,13 +173,13 @@ ObjectParser::HandleOriginEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 origin) { - mObject->SetOrigin(origin); + auto onDone = [this](Vector4 origin) { + mObject->Place(origin); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -211,13 +213,13 @@ ObjectParser::HandleNearEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 near) { + auto onDone = [this](Vector4 near) { std::dynamic_pointer_cast(mObject)->SetNear(near); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -230,13 +232,13 @@ ObjectParser::HandleFarEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 far) { + auto onDone = [this](Vector4 far) { std::dynamic_pointer_cast(mObject)->SetFar(far); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -249,13 +251,13 @@ ObjectParser::HandleNormalEvent(yaml_event_t& event) return; } - auto onDone = [this](Vector3 normal) { + auto onDone = [this](Vector4 normal) { std::dynamic_pointer_cast(mObject)->SetNormal(normal); mSection = NoSection; SetShouldExpectKey(true); }; - GetParsers().push(new Vector3Parser(GetScene(), GetParsers(), onDone)); + GetParsers().push(new Vector4Parser(GetScene(), GetParsers(), onDone)); } @@ -280,3 +282,4 @@ ObjectParser::HandleDistanceEvent(yaml_event_t& event) } } /* namespace yaml */ +} /* namespace charles */ diff --git a/src/yaml/objectParser.hh b/src/yaml/objectParser.hh index 80bf7e8..ca0a077 100644 --- a/src/yaml/objectParser.hh +++ b/src/yaml/objectParser.hh @@ -13,6 +13,7 @@ #include "yaml/scalarMappingParser.hh" +namespace charles { namespace yaml { struct ObjectParser @@ -66,5 +67,6 @@ private: }; } /* namespace yaml */ +} /* namespace charles */ #endif /* __YAML_OBJECTPARSER_HH__ */ diff --git a/src/yaml/parsers.cc b/src/yaml/parsers.cc index 4267247..5106241 100644 --- a/src/yaml/parsers.cc +++ b/src/yaml/parsers.cc @@ -13,6 +13,7 @@ #include "parsers.hh" +namespace charles { namespace yaml { #pragma mark Parser @@ -81,3 +82,4 @@ template<> const char* ScalarParserTraits::fmt = "%lf"; } /* namespace yaml */ +} /* namespace charles */ diff --git a/src/yaml/parsers.hh b/src/yaml/parsers.hh index 2f3b759..46e0c6e 100644 --- a/src/yaml/parsers.hh +++ b/src/yaml/parsers.hh @@ -15,9 +15,10 @@ #include "yaml.h" -#include "scene.h" +#include "scene.hh" +namespace charles { namespace yaml { struct Parser; @@ -144,5 +145,6 @@ ParseScalar(const std::string& scalar, } } /* namespace yaml */ +} /* namespace charles */ #endif /* __YAML_PARSERS_HH__ */ diff --git a/src/yaml/scalarMappingParser.cc b/src/yaml/scalarMappingParser.cc index f453f2d..d04d6ea 100644 --- a/src/yaml/scalarMappingParser.cc +++ b/src/yaml/scalarMappingParser.cc @@ -12,6 +12,7 @@ #include "scalarMappingParser.hh" +namespace charles { namespace yaml { ScalarMappingParser::ScalarMappingParser(Scene& scene, @@ -83,3 +84,4 @@ ScalarMappingParser::GetShouldExpectKey() } } /* namespace yaml */ +} /* namespace charles */ diff --git a/src/yaml/scalarMappingParser.hh b/src/yaml/scalarMappingParser.hh index 9e8fa8b..38ef759 100644 --- a/src/yaml/scalarMappingParser.hh +++ b/src/yaml/scalarMappingParser.hh @@ -17,6 +17,7 @@ #include "yaml/parsers.hh" +namespace charles { namespace yaml { struct ScalarMappingParser @@ -40,5 +41,6 @@ private: }; } /* namespace yaml */ +} /* namespace charles */ #endif /* __YAML_SCALARMAPPINGPARSER_HH__ */ diff --git a/src/yaml/sceneParser.cc b/src/yaml/sceneParser.cc index a435df8..e206cc6 100644 --- a/src/yaml/sceneParser.cc +++ b/src/yaml/sceneParser.cc @@ -15,6 +15,7 @@ #include "vectorParser.hh" +namespace charles { namespace yaml { SceneParser::SceneParser(Scene& scene, @@ -95,8 +96,8 @@ SceneParser::HandleDimensionsEvent(yaml_event_t& event) } Scene& sc = GetScene(); - sc.set_width(dimensions.at(0)); - sc.set_height(dimensions.at(1)); + sc.SetWidth(dimensions.at(0)); + sc.SetHeight(dimensions.at(1)); mSection = NoSection; SetShouldExpectKey(true); @@ -137,3 +138,4 @@ SceneParser::HandleObjectsEvent(yaml_event_t& event) } } /* namespace yaml */ +} /* namespace charles */ diff --git a/src/yaml/sceneParser.hh b/src/yaml/sceneParser.hh index 2279669..be62be8 100644 --- a/src/yaml/sceneParser.hh +++ b/src/yaml/sceneParser.hh @@ -15,6 +15,7 @@ #include "scalarMappingParser.hh" +namespace charles { namespace yaml { struct SceneParser @@ -43,5 +44,6 @@ private: }; } /* namespace yaml */ +} /* namespace charles */ #endif /* __YAML_SCENE_PARSER_HH__ */ diff --git a/src/yaml/vectorParser.cc b/src/yaml/vectorParser.cc index 5409f97..3283fdf 100644 --- a/src/yaml/vectorParser.cc +++ b/src/yaml/vectorParser.cc @@ -8,9 +8,14 @@ #include "yaml/vectorParser.hh" +using charles::basics::Color; +using charles::basics::Vector4; + + +namespace charles { namespace yaml { -Vector3Parser::Vector3Parser(Scene& scene, +Vector4Parser::Vector4Parser(Scene& scene, ParserStack& parsers, CallbackFunction onDone) : ScalarSequenceParser(scene, parsers) @@ -20,16 +25,12 @@ Vector3Parser::Vector3Parser(Scene& scene, assert(seq.size() != 3); return; } - onDone(Vector3(seq[0], seq[1], seq[2])); + onDone(Vector4(seq[0], seq[1], seq[2])); }; SetCallback(onSeqDone); } -Vector3Parser::~Vector3Parser() -{ } - - ColorParser::ColorParser(Scene& scene, ParserStack& parsers, CallbackFunction onDone) @@ -50,8 +51,5 @@ ColorParser::ColorParser(Scene& scene, SetCallback(onSeqDone); } - -ColorParser::~ColorParser() -{ } - } /* namespace yaml */ +} /* namespace charles */ diff --git a/src/yaml/vectorParser.hh b/src/yaml/vectorParser.hh index 2eb1bb5..2d66391 100644 --- a/src/yaml/vectorParser.hh +++ b/src/yaml/vectorParser.hh @@ -14,8 +14,10 @@ #include #include "parsers.hh" +#include "basics/basics.hh" +namespace charles { namespace yaml { /** @@ -82,25 +84,24 @@ private: }; -struct Vector3Parser - : ScalarSequenceParser +struct Vector4Parser + : ScalarSequenceParser { - typedef std::function CallbackFunction; + typedef std::function CallbackFunction; - Vector3Parser(Scene& scene, ParserStack& parsers, CallbackFunction onDone); - ~Vector3Parser(); + Vector4Parser(Scene& scene, ParserStack& parsers, CallbackFunction onDone); }; struct ColorParser - : ScalarSequenceParser + : ScalarSequenceParser { - typedef std::function CallbackFunction; + typedef std::function CallbackFunction; ColorParser(Scene& scene, ParserStack& parsers, CallbackFunction onDone); - ~ColorParser(); }; } /* namespace yaml */ +} /* namespace charles */ #endif /* __YAML_VECTOR_PARSER_HH__ */ diff --git a/test/testObject.cc b/test/testObject.cc new file mode 100644 index 0000000..f855cbe --- /dev/null +++ b/test/testObject.cc @@ -0,0 +1,8 @@ +/* test_object.cc + * vim: set tw=80: + * Eryn Wells + */ + +#include "gtest/gtest.h" + +#include "object.hh" diff --git a/test/test_basics.cc b/test/test_basics.cc index a6395b1..b07ec78 100644 --- a/test/test_basics.cc +++ b/test/test_basics.cc @@ -7,158 +7,319 @@ #include "gtest/gtest.h" -#include "basics.h" +#include "basics/basics.hh" -class Vector3Test +using charles::basics::Vector4; +using charles::basics::Matrix4; + + +class Vector4Test : public ::testing::Test { public: virtual void SetUp(); protected: - Vector3 v1, v2; + Vector4 v1, v2; }; void -Vector3Test::SetUp() +Vector4Test::SetUp() { - v1 = Vector3(1, 3, 5); - v2 = Vector3(7, 13, 17); + v1 = Vector4(1, 3, 5); + v2 = Vector4(7, 13, 17); } -TEST_F(Vector3Test, OperatorEq) +TEST_F(Vector4Test, OperatorEq) { v1 = v2; EXPECT_EQ(v2, v1); } -TEST_F(Vector3Test, OperatorMul) +TEST_F(Vector4Test, OperatorMul) { - Vector3 out; + Vector4 out; out = v1 * 5; - EXPECT_EQ(5, out.x); - EXPECT_EQ(15, out.y); - EXPECT_EQ(25, out.z); + EXPECT_EQ(5, out.X()); + EXPECT_EQ(15, out.Y()); + EXPECT_EQ(25, out.Z()); out = v1 * -7; - EXPECT_EQ(-7, out.x); - EXPECT_EQ(-21, out.y); - EXPECT_EQ(-35, out.z); + EXPECT_EQ(-7, out.X()); + EXPECT_EQ(-21, out.Y()); + EXPECT_EQ(-35, out.Z()); out = v1; out *= 5; - EXPECT_EQ(5, out.x); - EXPECT_EQ(15, out.y); - EXPECT_EQ(25, out.z); + EXPECT_EQ(5, out.X()); + EXPECT_EQ(15, out.Y()); + EXPECT_EQ(25, out.Z()); out = v1; out *= -7; - EXPECT_EQ(-7, out.x); - EXPECT_EQ(-21, out.y); - EXPECT_EQ(-35, out.z); + EXPECT_EQ(-7, out.X()); + EXPECT_EQ(-21, out.Y()); + EXPECT_EQ(-35, out.Z()); } -TEST_F(Vector3Test, OperatorDiv) +TEST_F(Vector4Test, OperatorDiv) { - Vector3 out; + Vector4 out; out = v1 / 5.0; - EXPECT_FLOAT_EQ(1.0/5.0, out.x); - EXPECT_FLOAT_EQ(3.0/5.0, out.y); - EXPECT_FLOAT_EQ(5.0/5.0, out.z); + EXPECT_FLOAT_EQ(1.0/5.0, out.X()); + EXPECT_FLOAT_EQ(3.0/5.0, out.Y()); + EXPECT_FLOAT_EQ(5.0/5.0, out.Z()); out = v1 / -7.0; - EXPECT_FLOAT_EQ(1.0/-7.0, out.x); - EXPECT_FLOAT_EQ(3.0/-7.0, out.y); - EXPECT_FLOAT_EQ(5.0/-7.0, out.z); + EXPECT_FLOAT_EQ(1.0/-7.0, out.X()); + EXPECT_FLOAT_EQ(3.0/-7.0, out.Y()); + EXPECT_FLOAT_EQ(5.0/-7.0, out.Z()); out = v1; out /= 5.0; - EXPECT_FLOAT_EQ(1.0/5.0, out.x); - EXPECT_FLOAT_EQ(3.0/5.0, out.y); - EXPECT_FLOAT_EQ(5.0/5.0, out.z); + EXPECT_FLOAT_EQ(1.0/5.0, out.X()); + EXPECT_FLOAT_EQ(3.0/5.0, out.Y()); + EXPECT_FLOAT_EQ(5.0/5.0, out.Z()); out = v1; out /= -7.0; - EXPECT_FLOAT_EQ(1.0/-7.0, out.x); - EXPECT_FLOAT_EQ(3.0/-7.0, out.y); - EXPECT_FLOAT_EQ(5.0/-7.0, out.z); + EXPECT_FLOAT_EQ(1.0/-7.0, out.X()); + EXPECT_FLOAT_EQ(3.0/-7.0, out.Y()); + EXPECT_FLOAT_EQ(5.0/-7.0, out.Z()); } -TEST_F(Vector3Test, OperatorAdd) +TEST_F(Vector4Test, OperatorAdd) { - Vector3 out; + Vector4 out; out = v1 + v2; - EXPECT_EQ(8, out.x); - EXPECT_EQ(16, out.y); - EXPECT_EQ(22, out.z); + EXPECT_EQ(8, out.X()); + EXPECT_EQ(16, out.Y()); + EXPECT_EQ(22, out.Z()); out = v1; out += v2; - EXPECT_EQ(8, out.x); - EXPECT_EQ(16, out.y); - EXPECT_EQ(22, out.z); + EXPECT_EQ(8, out.X()); + EXPECT_EQ(16, out.Y()); + EXPECT_EQ(22, out.Z()); } -TEST_F(Vector3Test, OperatorSub) +TEST_F(Vector4Test, OperatorSub) { - Vector3 out; + Vector4 out; out = v1 - v2; - EXPECT_EQ(-6, out.x); - EXPECT_EQ(-10, out.y); - EXPECT_EQ(-12, out.z); + EXPECT_EQ(-6, out.X()); + EXPECT_EQ(-10, out.Y()); + EXPECT_EQ(-12, out.Z()); out = v2 - v1; - EXPECT_EQ(6, out.x); - EXPECT_EQ(10, out.y); - EXPECT_EQ(12, out.z); + EXPECT_EQ(6, out.X()); + EXPECT_EQ(10, out.Y()); + EXPECT_EQ(12, out.Z()); v1 -= v2; - EXPECT_EQ(-6, v1.x); - EXPECT_EQ(-10, v1.y); - EXPECT_EQ(-12, v1.z); + EXPECT_EQ(-6, v1.X()); + EXPECT_EQ(-10, v1.Y()); + EXPECT_EQ(-12, v1.Z()); } -TEST_F(Vector3Test, OperatorBoolEq) +TEST_F(Vector4Test, OperatorBoolEq) { EXPECT_EQ(v1, v1); EXPECT_EQ(v2, v2); } -TEST_F(Vector3Test, OperatorBoolNe) +TEST_F(Vector4Test, OperatorBoolNe) { EXPECT_NE(v1, v2); EXPECT_NE(v2, v1); } -TEST_F(Vector3Test, Length2) +TEST_F(Vector4Test, Length2) { - EXPECT_EQ(35.0, v1.length2()); - EXPECT_EQ(507.0, v2.length2()); + EXPECT_EQ(35.0, v1.Length2()); + EXPECT_EQ(507.0, v2.Length2()); } -TEST_F(Vector3Test, Length) +TEST_F(Vector4Test, Length) { - EXPECT_FLOAT_EQ(5.916079783099616, v1.length()); - EXPECT_FLOAT_EQ(22.5166604983954, v2.length()); + EXPECT_FLOAT_EQ(5.916079783099616, v1.Length()); + EXPECT_FLOAT_EQ(22.5166604983954, v2.Length()); } -TEST_F(Vector3Test, DotProduct) +TEST_F(Vector4Test, DotProduct) { - EXPECT_EQ(131.0, v1.dot(v2)); + EXPECT_EQ(131.0, v1.Dot(v2)); +} + +#pragma mark Matrix4 Tests + +class Matrix4Test + : public ::testing::Test +{ +public: + virtual void SetUp(); + +protected: + Matrix4 m1, m2; + Matrix4 id; +}; + + +void +Matrix4Test::SetUp() +{ + const Double m1Cells[] = { 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16}; + m1 = Matrix4(m1Cells); + + const Double m2Cells[] = { 1, 1, 2, 3, + 5, 8, 13, 21, + 34, 55, 89, 144, + 233, 377, 610, 987}; + m2 = Matrix4(m2Cells); + + id = Matrix4::Identity(); +} + + +TEST(Matrix4StaticTest, Zero) +{ + Matrix4 zero = Matrix4::Zero(); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(zero.CArray()[i], 0.0); + } +} + + +TEST(Matrix4StaticTest, Identity) +{ + Matrix4 id = Matrix4::Identity(); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + EXPECT_EQ(id.CArray()[i * 4 + j], ((i == j) ? 1.0 : 0.0)); + } + } +} + + +TEST_F(Matrix4Test, OperatorCall) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + EXPECT_EQ(m1(i, j), 4 * i + j + 1); + } + } +} + + +TEST_F(Matrix4Test, ScalarMultiplication) +{ + Matrix4 p1 = m1 * 2.0; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + EXPECT_EQ(p1(i, j), m1(i, j) * 2.0); + } + } + + Matrix4 p2 = 2.0 * m1; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + EXPECT_EQ(p2(i, j), m1(i, j) * 2.0); + } + } +} + + +TEST_F(Matrix4Test, MatrixMatrixMultiplication) +{ + const Double p1Expect[] = {1045, 1690, 2735, 4425, + 2137, 3454, 5591, 9045, + 3229, 5218, 8447, 13665, + 4321, 6982, 11303, 18285}; + Matrix4 p1 = m1 * m2; + for (int i = 0; i < 16; i++) { + EXPECT_EQ(p1.CArray()[i], p1Expect[i]); + } + + const Double p2Expect[] = { 63, 70, 77, 84, + 435, 482, 529, 576, + 2982, 3304, 3626, 3948, + 20439, 22646, 24853, 27060}; + Matrix4 p2 = m2 * m1; + for (int i = 0; i < 16; i++) { + EXPECT_EQ(p2.CArray()[i], p2Expect[i]); + } +} + + +TEST_F(Matrix4Test, MatrixIdentityMultiplication) +{ + Matrix4 p1 = m1 * id; + for (UInt i = 0; i < 16; i++) { + EXPECT_EQ(p1.CArray()[i], m1.CArray()[i]); + } + + Matrix4 p2 = id * m1; + for (UInt i = 0; i < 16; i++) { + EXPECT_EQ(p2.CArray()[i], m1.CArray()[i]); + } +} + + +TEST_F(Matrix4Test, IdentityVectorMultiplication) +{ + Vector4 v1(1, 2, 3); + + Vector4 p1 = id * v1; + EXPECT_EQ(p1.X(), v1.X()); + EXPECT_EQ(p1.Y(), v1.Y()); + EXPECT_EQ(p1.Z(), v1.Z()); +} + + +TEST_F(Matrix4Test, TranslationVectorMultiplication) +{ + Matrix4 tr = Matrix4::Translation(1, 2, 3); + Vector4 v1(3, 7, 9); + + Vector4 p1 = tr * v1; + EXPECT_EQ(p1.X(), 4.0); + EXPECT_EQ(p1.Y(), 9.0); + EXPECT_EQ(p1.Z(), 12.0); +} + + +TEST_F(Matrix4Test, Transpose) +{ + Matrix4 t1 = Transpose(m1); + for (UInt i = 0; i < 4; i++) { + for (UInt j = 0; j < 4; j++) { + EXPECT_EQ(m1(i,j), t1(j,i)); + } + } + + t1.Transpose(); + for (UInt i = 0; i < 4; i++) { + for (UInt j = 0; j < 4; j++) { + EXPECT_EQ(m1(i,j), t1(i,j)); + } + } } diff --git a/test/test_object.c b/test/test_object.c deleted file mode 100644 index 7aa655a..0000000 --- a/test/test_object.c +++ /dev/null @@ -1,94 +0,0 @@ -/* test_object.c - * - * Unit tests for the object module. - * - * Eryn Wells - */ - -#include - -#include - -#include "object.h" -#include "test_asserts.h" -#include "test_suites.h" - - -void check_sphere_intersection(Object *sphere, Ray ray, Vector3 *tvectors, int ntvectors); - - -START_TEST(test_sphere_does_intersect) -{ - // Create a sphere at the origin of radius 1. - Object *sphere = object_init(ObjectTypeSphere); - object_sphere_set_radius(sphere, 1.0); - - Vector3 loc, dir; - Vector3 tvectors[2]; - Ray ray; - - loc = vector_init(0, 0, -5); - dir = vector_init(0, 0, 1); - ray = ray_init(loc, dir); - tvectors[0] = vector_init(0, 0, -1); - tvectors[1] = vector_init(0, 0, 1); - check_sphere_intersection(sphere, ray, tvectors, 2); - - loc = vector_init(0, -5, 0); - dir = vector_init(0, 1, 0); - ray = ray_init(loc, dir); - tvectors[0] = vector_init(0, -1, 0); - tvectors[1] = vector_init(0, 1, 0); - check_sphere_intersection(sphere, ray, tvectors, 2); -} -END_TEST - - -void -check_sphere_intersection(Object *sphere, Ray ray, Vector3 *tvectors, int ntvectors) -{ - float *t; - int nints = object_does_intersect(sphere, ray, &t); - ck_assert(nints == ntvectors); - - Vector3 rp; - for (int i = 0; i < nints; i++) { - rp = ray_parameterize(ray, t[i]); - test_assert_within_epsilon(rp.x, tvectors[i].x, 1e-4); - test_assert_within_epsilon(rp.y, tvectors[i].y, 1e-4); - test_assert_within_epsilon(rp.z, tvectors[i].z, 1e-4); - } - - if (nints > 0) { - free(t); - } -} - - -START_TEST(test_sphere_point_lies_on_surface) -{ - ck_assert(0); -} -END_TEST - - -START_TEST(test_sphere_compute_normal) -{ - ck_assert(0); -} -END_TEST - - -Suite * -test_object_create_suite() -{ - Suite *s = suite_create("object"); - - TCase *tc_sphere = tcase_create("sphere"); - tcase_add_test(tc_sphere, test_sphere_does_intersect); - tcase_add_test(tc_sphere, test_sphere_point_lies_on_surface); - tcase_add_test(tc_sphere, test_sphere_compute_normal); - suite_add_tcase(s, tc_sphere); - - return s; -}