/*-
 * Copyright (c) 1999,2000 Qube Software, Ltd.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * 1. Redistributions of the source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Any redistribution solely in binary form must conspicuously
 *    reproduce the following disclaimer in documentation provided with the
 *    binary redistribution.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'', WITHOUT ANY WARRANTIES, EXPRESS
 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  LICENSOR SHALL
 * NOT BE LIABLE FOR ANY LOSS OR DAMAGES RESULTING FROM THE USE OF THIS
 * SOFTWARE, EITHER ALONE OR IN COMBINATION WITH ANY OTHER SOFTWARE.
 * 
 * 	$Id: texmap.cpp,v 1.4 2000/04/20 15:48:17 n_hibma Exp $
 */

#include "maths.h"
#include "texmap.h"

TextureMapper::TextureMapper()
{
    uScale_ = 1;
    vScale_ = 1;
    uOffset_ = 0;
    vOffset_ = 0;
    orientation_ = 0;
    texTransform_.setIdentity();
    done_ = false;
}

TextureMapper::TextureMapper(float uScale, float vScale,
			     float uOffset, float vOffset,
			     float orientation)
    : uScale_(uScale), vScale_(vScale),
      uOffset_(uOffset), vOffset_(vOffset),
      orientation_(orientation), done_(false)
{
    makeTransform();
}

void
TextureMapper::makeTransform()
{
    Matrix3f rot;
    Matrix3f scale;
    Matrix3f translate;

    float cosRot;
    float sinRot;
    
    rot.setIdentity();
    scale.setIdentity();
    translate.setIdentity();

    if (orientation_ != 0) {
	cosRot = cosf(orientation_);
	sinRot = sinf(orientation_);

	rot[0] = Vec3f(cosRot, -sinRot, 0);
	rot[1] = Vec3f(sinRot, cosRot, 0);
    }

    scale[0] = Vec3f(uScale_, 0, 0);
    scale[1] = Vec3f(0, vScale_, 0);

    translate[2] = Vec3f(uOffset_, vOffset_, 1);
    
    texTransform_ = rot * scale * translate;
}

void
TextureMapper::directions(const Vec3f& up, const Vec3f& side)
{
    transform_[1] = up;
    transform_[0] = side;
    transform_[2] = Maths::cross(side, up);
    done_ = true;
}

Vec2f
FlatTextureMapper::map(const Vec3f& v)
{
    if (!done()) return Vec2f(0, 0);

    Vec3f pvec = v * transform();
    Vec3f tmp(pvec.x, pvec.z, 1);
    Vec3f transfmd = tmp * texTransform();

    return Vec2f(transfmd.x, transfmd.y);
}

Vec2f
CylinderTextureMapper::map(const Vec3f& v)
{
    if (!done()) return Vec2f(0, 0);
    
    // First convert to the cylinders cartesian space, so the y axis is the
    // axis of the cylinder
    Vec3f cyspace = v * transform();

    // Now convert into cylindrical polar coords, we don't care what the
    // radius is, so make it 1, so we immeadiately have vector ready for the
    // texTransform.
    Vec3f polar(atan2f(cyspace.z, cyspace.x), cyspace.y, 1);

    // Now make the angle range over [0,1) instead of [-pi, pi)
    polar[0] = (polar[0] + PI) / (2 * PI);

    Vec3f transfmd = polar * texTransform();

    return Vec2f(transfmd.x, transfmd.y);
}

Vec2f
SphereTextureMapper::map(const Vec3f& v)
{
    if (!done()) return Vec2f(0, 0);

    // First do the transform
    Vec3f spspace = v * transform();

    // Then convert to spherical polars, again we don't need the radius so set
    // it to 1 for the later transform.
    float invRadius = Maths::fastInvSqrtf(spspace.x * spspace.x +
					  spspace.y * spspace.y +
					  spspace.z * spspace.z);

    Vec3f polar(atan2f(spspace.z, spspace.x),
		acosf(spspace.y * invRadius), 1);

    // Now normalise the angle ranges.
    polar[0] = (polar[0] + PI) / (2 * PI);
    polar[1] = (PI - polar[1]) / PI;

    Vec3f transfmd = polar * texTransform();

    return Vec2f(transfmd.x, transfmd.y);
}

void
CylinderMapHelper::init()
{
    wrapOffset = 0;
    lastVal = 0;
    wrapSet = false;
    inc = true;
    count = 0;
}

Vec2f
CylinderMapHelper::map(const Vec3f& v)
{
    Vec2f texCo = mapper.map(v);

    if (count > 0 && !wrapSet) {
	float diff = texCo.x - lastVal;
	bool newInc;
	
	if (diff > 0) {
	    newInc = true;
	} else {
	    newInc = false;
	}

	if (count == 1) {
	    inc = newInc;
	} else if (inc != newInc) {
	    wrapOffset = mapper.uScale();

	    if (!inc)
		wrapOffset *= -1;

	    wrapSet = true;
	}
    }

    if (wrapSet)
	texCo.x += wrapOffset;
    
    lastVal = texCo.x;
    ++count;

    return texCo;
}
