#pragma once #include "Utils.h" #include "Isaac.h" /// /// Curves class. /// namespace EmberNs { /// /// The Bezier curves used to adjust the colors during final accumulation. /// This functionality was gotten directly from Apophysis. /// template class EMBER_API Curves { public: /// /// Constructor which sets the curve and weight values to their defaults. /// Curves(bool init = false) { if (init) Init(); else Clear(); } /// /// Default copy constructor. /// /// The Curves object to copy Curves(const Curves& curves) { Curves::operator=(curves); } /// /// Copy constructor to copy a Curves object of type U. /// Special case that must be here in the header because it has /// a second template parameter. /// /// The Curves object to copy template Curves(const Curves& curves) { Curves::operator=(curves); } /// /// Default assignment operator. /// /// The Curves object to copy Curves& operator = (const Curves& curves) { if (this != &curves) Curves::operator=(curves); return *this; } /// /// Assignment operator to assign a Curves object of type U. /// /// The Curves object to copy /// Reference to updated self template Curves& operator = (const Curves& curves) { for (size_t i = 0; i < 4; i++) { m_Points[i][0].x = T(curves.m_Points[i][0].x); m_Points[i][0].y = T(curves.m_Points[i][0].y); m_Weights[i].x = T(curves.m_Weights[i].x); m_Points[i][1].x = T(curves.m_Points[i][1].x); m_Points[i][1].y = T(curves.m_Points[i][1].y); m_Weights[i].y = T(curves.m_Weights[i].y); m_Points[i][2].x = T(curves.m_Points[i][2].x); m_Points[i][2].y = T(curves.m_Points[i][2].y); m_Weights[i].z = T(curves.m_Weights[i].z); m_Points[i][3].x = T(curves.m_Points[i][3].x); m_Points[i][3].y = T(curves.m_Points[i][3].y); m_Weights[i].w = T(curves.m_Weights[i].w); } return *this; } /// /// Unary addition operator to add a Curves object to this one. /// /// The Curves object to add /// Reference to updated self Curves& operator += (const Curves& curves) { for (size_t i = 0; i < 4; i++) { m_Points[i][0] += curves.m_Points[i][0]; m_Points[i][1] += curves.m_Points[i][1]; m_Points[i][2] += curves.m_Points[i][2]; m_Points[i][3] += curves.m_Points[i][3]; m_Weights[i] += curves.m_Weights[i]; } return *this; } /// /// Unary multiplication operator to multiply this object by another Curves object. /// /// The Curves object to multiply this one by /// Reference to updated self Curves& operator *= (const Curves& curves) { for (size_t i = 0; i < 4; i++) { m_Points[i][0] *= curves.m_Points[i][0]; m_Points[i][1] *= curves.m_Points[i][1]; m_Points[i][2] *= curves.m_Points[i][2]; m_Points[i][3] *= curves.m_Points[i][3]; m_Weights[i] *= curves.m_Weights[i]; } return *this; } /// /// Unary multiplication operator to multiply this object by a scalar of type T. /// /// The scalar to multiply this object by /// Reference to updated self Curves& operator *= (const T& t) { for (size_t i = 0; i < 4; i++) { m_Points[i][0] *= t; m_Points[i][1] *= t; m_Points[i][2] *= t; m_Points[i][3] *= t; m_Weights[i] *= t; } return *this; } /// /// Set the curve and weight values to their default state. /// void Init() { for (size_t i = 0; i < 4; i++) { m_Points[i][0] = v2T(0);//0,0 -> 0,0 -> 1,1 -> 1,1. m_Points[i][1] = v2T(0); m_Points[i][2] = v2T(1); m_Points[i][3] = v2T(1); m_Weights[i] = v4T(1); } } /// /// Set the curve and weight values to an empty state. /// void Clear() { memset(&m_Points, 0, sizeof(m_Points)); memset(&m_Weights, 0, sizeof(m_Weights)); } /// /// Whether any points are not the default. /// /// True if any point has been set to a value other than the default, else false. bool CurvesSet() { bool set = false; for (size_t i = 0; i < 4; i++) { if ((m_Points[i][0] != v2T(0)) || (m_Points[i][1] != v2T(0)) || (m_Points[i][2] != v2T(1)) || (m_Points[i][3] != v2T(1))) { set = true; break; } } return set; } /// /// Wrapper around calling BezierSolve() on each of the 4 weight and point vectors. /// /// The position to apply /// vec4 that contains the y component of the solution for each vector in each component v4T BezierFunc(const T& t) { v4T result; v2T solution(0, 0); BezierSolve(t, m_Points[0], &m_Weights[0], solution); result.x = solution.y; BezierSolve(t, m_Points[1], &m_Weights[1], solution); result.y = solution.y; BezierSolve(t, m_Points[2], &m_Weights[2], solution); result.z = solution.y; BezierSolve(t, m_Points[3], &m_Weights[3], solution); result.w = solution.y; return result; } private: /// /// Solve the given point and weight vectors for the given position and store /// the output in the solution vec2 passed in. /// /// The position to apply /// A pointer to an array of 4 vec2 /// A pointer to an array of 4 weights /// The vec2 to store the solution in void BezierSolve(const T& t, v2T* src, v4T* w, v2T& solution) { T s, s2, s3, t2, t3, nom_x, nom_y, denom; s = 1 - t; s2 = s * s; s3 = s * s * s; t2 = t * t; t3 = t * t * t; nom_x = (w->x * s3 * src->x) + (w->y * s2 * 3 * t * src[1].x) + (w->z * s * 3 * t2 * src[2].x) + (w->w * t3 * src[3].x); nom_y = (w->x * s3 * src->y) + (w->y * s2 * 3 * t * src[1].y) + (w->z * s * 3 * t2 * src[2].y) + (w->w * t3 * src[3].y); denom = (w->x * s3) + (w->y * s2 * 3 * t) + (w->z * s * 3 * t2) + (w->w * t3); if (std::isnan(nom_x) || std::isnan(nom_y) || std::isnan(denom) || denom == 0) return; solution.x = nom_x / denom; solution.y = nom_y / denom; } public: v2T m_Points[4][4]; v4T m_Weights[4]; }; //Must declare this outside of the class to provide for both orders of parameters. /// /// Multiplication operator to multiply a Curves object by a scalar of type T. /// /// The curves object to multiply /// The scalar to multiply curves by by /// Copy of new Curves template Curves operator * (const Curves& curves, const T& t) { Curves c(curves); for (size_t i = 0; i < 4; i++) { c.m_Points[i][0] *= t; c.m_Points[i][1] *= t; c.m_Points[i][2] *= t; c.m_Points[i][3] *= t; c.m_Weights[i] *= t; } return c; } /// /// Multiplication operator for reverse order. /// /// The scalar to multiply curves by by /// The curves object to multiply /// Copy of new Curves template Curves operator * (const T& t, const Curves& curves) { return curves * t; } }