/* PhBShadingFrameDistortion.C: PhBShadingFrameDistortion nodes (public source) */

#include "PhBShadingFrameDistortion.H"

namespace xrml {

void PhBShadingFrameDistortion::render(void)
{
  if (texture) texture->render();
  if(transform) transform->render();
}

Mat3 PhBShadingFrameDistortion::distort(const Vec3 point)
{
  int nrChannels = 0;
  Vec3 X, Xi, Y, Yi, Z;
  Mat3 Rot, Zax;

  // get d values
  Vec3 texco = point;
  if (transform) texco *= transform->xf;
  float *value = (texture) ? texture->values(point.x, point.y, &nrChannels) : NULL;

  // ***Brushmap rotation***
  // need atleast 3 channels (ignore first 2 channels, 3rd = rotation around Z)
  if (nrChannels >= 3) {
    // dertemine X, Y, Z axis
    float a = value[2] * 2 * M_PI;

    Z = Vec3(0, 0, 1);
    X = Vec3(cos(a), -sin(a), 0);
    Y = Vec3(sin(a),  cos(a), 0);

    Rot = Mat3(X, Y, Z);
  }

  // **NormalMap rotation***
  // need atleast 2 channels (X and Y component of Z-axis)
  if (nrChannels >= 2) {
    // dertemine X, Y, Z axis
    float x = value[0]*2 - 1,  y = value[1]*2 - 1;
    double l = x*x + y*y;

    if (l <= 1)   Z = Vec3(x, y,  sqrt(1 - l) );
    else Z = Vec3(x, y, 0).normalize();

    if (fabs(Z.x) < EPSILON) Xi = Vec3(Z.z, 0, -Z.x).normalize();
    else Xi = Vec3(1, 0, 0);
    if (fabs(Z.y) < EPSILON) Yi = Vec3(0, Z.z, -Z.y).normalize();
    else Yi = Vec3(0, 1, 0);

    X = ((Yi ^ Z) + Xi).normalize();
    Y = ((Z ^ Xi) + Yi).normalize();

    Zax = Mat3(X, Y, Z);
  }

  // return product of the two rotations
  return (Rot * Zax);
}

}  // namespace xrml
