//------------------------------------------------------------
// $Id: triangleObject.cc,v 1.6 2000/06/14 13:27:47 yamauchi Exp $
//------------------------------------------------------------
//	triangle object 
//	1996/3/18 
//	(C) Copyright Hitoshi Yamauchi
//	三角形のポリゴン
//------------------------------------------------------------
#include <stdio.h>
#include "assert.h"
#include "constants.h"
#include "vector.h"
#include "triangleObject.h"

//------------------------------------------------------------
// constructors
//------------------------------------------------------------
// for array construct
//------------------------------
triangleObjectClass::
triangleObjectClass(void)
{
    // Commented objects are already initialized by their constructor.
    // Vector3 mVertex[3];

    // Vector3  mEdge[3];       
    // Vector3  mNrm;       
    // Vector3  mEdgeNrm[3];
    // Vector3  mGpoi;      
    this->mPlaneConst	= 0.0;
    this->mEdgeConst[0]	= 0.0;
    this->mEdgeConst[1]	= 0.0;
    this->mEdgeConst[2]	= 0.0;
    this->mArea		= 0.0;

    // Vector3	mRadiosity;     
    // Vector3  mNonRad;
    // double   mEnergy
    this->mIsInRadiationQ = NO;
    static const Vector3 minus(-1.0e6, -1.0e6, -1.0e6);
    int i;
    for(i = 0; i < 3; i++)
    {
	this->mIntensity[i] = minus;
	this->mAddedIntensityNum[i] = 0;
    }
}

triangleObjectClass::
triangleObjectClass(int   objectID,  
		    int   materialID,
		    const Vector3& v1,	const Vector3& v2,
		    const Vector3& v3,  const Vector3& spcpos,
		    const Vector3& nonRadiation)
{
    // objBaseClass
    this->mObjectID	= objectID;
    this->mSpcPos	= Vec3toInt3(spcpos);
    this->mMaterialID 	= materialID;

    // this class
    this->mVertex[0] = v1;
    this->mVertex[1] = v2;
    this->mVertex[2] = v3;
    
    this->calcGeometoryInfo();

    //------------------------------
    //  ラジオシティ値
    //------------------------------
    static const Vector3 zero(0.0, 0.0, 0.0); 
    this->mRadiosity 	= zero;
    this->mNonRad	= zero;

    this->mIsInRadiationQ = NO; // not in queue, unlocked.

    static const Vector3 minus(-1.0e6, -1.0e6, -1.0e6);
    int i;
    for(i = 0; i < 3; i++)
    {
	this->mIntensity[i] = minus;
	this->mAddedIntensityNum[i] = 0;
    }
    
    this->addUnshotRadiosity(nonRadiation); // これによりエネルギ初期化
}

triangleObjectClass::
triangleObjectClass(const triangleObjectClass& rhs)
{
    // object class (base class)
    this->mObjectID	= rhs.mObjectID;
    this->mSpcPos	= rhs.mSpcPos;
    this->mMaterialID 	= rhs.mMaterialID;

    // this class    
    int i = 0;
    for(i = 0; i < 3; i++)
    {
	this->mVertex[i]	= rhs.mVertex[i];	    
	this->mEdge[i]		= rhs.mEdge[i];      
	this->mEdgeNrm[i]	= rhs.mEdgeNrm[i]; 
	this->mEdgeConst[i]	= rhs.mEdgeConst[i];
    }
    this->mNrm		= rhs.mNrm;        
    this->mPlaneConst   = rhs.mPlaneConst; 
    this->mArea		= rhs.mArea;       
    this->mRadiosity	= rhs.mRadiosity;
    this->mNonRad	= rhs.mNonRad;  
    this->mIsInRadiationQ = rhs.mIsInRadiationQ;
    this->mIntensity[0] = rhs.mIntensity[0];
    this->mIntensity[1] = rhs.mIntensity[1];
    this->mIntensity[2] = rhs.mIntensity[2];
    this->mAddedIntensityNum[0] = rhs.mAddedIntensityNum[0];
    this->mAddedIntensityNum[1] = rhs.mAddedIntensityNum[1];
    this->mAddedIntensityNum[2] = rhs.mAddedIntensityNum[2];
}

const triangleObjectClass triangleObjectClass::
operator=(const triangleObjectClass& rhs)
{
    if(this != &rhs)
    {
	// object class (base class)
	this->mObjectID		= rhs.mObjectID;
	this->mSpcPos		= rhs.mSpcPos;
	this->mMaterialID 	= rhs.mMaterialID;

	// this class    
	int i = 0;
	for(i = 0; i < 3; i++)
	{
	    this->mVertex[i]	= rhs.mVertex[i];	    
	    this->mEdge[i]	= rhs.mEdge[i];      
	    this->mEdgeNrm[i]	= rhs.mEdgeNrm[i]; 
	    this->mEdgeConst[i]	= rhs.mEdgeConst[i];
	    this->mIntensity[i] = rhs.mIntensity[i];
	    this->mAddedIntensityNum[i] = rhs.mAddedIntensityNum[i];
	}
	this->mNrm		= rhs.mNrm;        
	this->mPlaneConst      	= rhs.mPlaneConst; 
	this->mArea		= rhs.mArea;       
	this->mRadiosity	= rhs.mRadiosity;
	this->mNonRad		= rhs.mNonRad;  
	this->mIsInRadiationQ 	= rhs.mIsInRadiationQ;
    }
    return(*this);
}

//------------------------------------------------------------
// 形状に関する情報を求める
//------------------------------------------------------------
void triangleObjectClass::
calcGeometoryInfo(void)
{
    static const Vector3 zero(0.0, 0.0, 0.0); 
    //------------------------------
    // エッジのベクトル(正規化していない)
    //------------------------------
    this->mEdge[0] = this->mVertex[1] - this->mVertex[0];
    this->mEdge[1] = this->mVertex[2] - this->mVertex[1];
    this->mEdge[2] = this->mVertex[0] - this->mVertex[2];

    //------------------------------
    // パッチ法線ベクトル
    //------------------------------
    this->mNrm = this->mEdge[0].VOut_prd(this->mEdge[1]);
    this->mNrm.VNormalize();

    //------------------------------
    // エッジから外部を見るベクトル
    //------------------------------
    this->mEdgeNrm[0] = this->mNrm.VOut_prd(this->mEdge[0]);
    this->mEdgeNrm[0].VNormalize();
    
    this->mEdgeNrm[1] = this->mNrm.VOut_prd(this->mEdge[1]);
    this->mEdgeNrm[1].VNormalize();
    
    this->mEdgeNrm[2] = this->mNrm.VOut_prd(this->mEdge[2]);
    this->mEdgeNrm[2].VNormalize();
    
    //------------------------------
    // Nrm・P1 = - d: plane constant
    //------------------------------
    this->mPlaneConst = this->mNrm.dInn_prd(this->mVertex[0]);

    //------------------------------
    // パッチ法線に垂直なエッジを含む平面の定数 N・v1 = d_e1
    //------------------------------
    this->mEdgeConst[0] = this->mEdgeNrm[0].dInn_prd(this->mVertex[0]);
    this->mEdgeConst[1] = this->mEdgeNrm[1].dInn_prd(this->mVertex[1]);
    this->mEdgeConst[2] = this->mEdgeNrm[2].dInn_prd(this->mVertex[2]);

    //------------------------------
    // 三角形の面積 ... tripatch.Area.tex 参照
    // S = \frac{1}{2} ab sin(\theta) 
    //   = \frac{1}{2} \sqrt{|a|^2 |b|^2 - (a \dot b)^2} (overflow が問題だが)
    //------------------------------
    double aSquare = this->mEdge[0].dInn_prd(this->mEdge[0]); // これは大きさ
    double bSquare = this->mEdge[2].dInn_prd(this->mEdge[2]); // で符号は良い
    double aDotb   = this->mEdge[0].dInn_prd(zero - this->mEdge[2]);
    this->mArea = 0.5 * sqrt(aSquare * bSquare - aDotb * aDotb);
}


//------------------------------
// radiosity 未放射値を加える
//------------------------------
void triangleObjectClass::
addUnshotRadiosity(const Vector3& rad)
{
    this->mNonRad = this->mNonRad + rad;
}

//------------------------------
// 未放射の radiosity 値を radiosity 値に加え、
// 各頂点の輝度を初期化する(計算終了時に利用する)  
//   @return 最後まで放射しなかった分の放射エネルギを返す
//------------------------------
double triangleObjectClass::
flushUnshotRadiosityNInitVerInt(void)
{
    // 最後まで放射しなかったエネルギ
    const double nonRadEnergy = this->getNonRadEnergy(); 

    // 未放射の分をこのパッチの明るさとして加える
    this->mRadiosity 	= this->mRadiosity + this->mNonRad;
    this->mNonRad.set(0.0, 0.0, 0.0);

    // 各頂点の初期値は radiosity そのもので加えた数は 1 
    this->mIntensity[0] = this->mRadiosity;
    this->mIntensity[1] = this->mRadiosity;
    this->mIntensity[2] = this->mRadiosity;
    this->mAddedIntensityNum[0] = 1;
    this->mAddedIntensityNum[1]= 1;
    this->mAddedIntensityNum[2] = 1;

    return(nonRadEnergy);
}

//------------------------------
// この patch の energy 1998年12月 9日(水)
//   この計算式が妥当かどうかもまた昔から謎なんだよな
//   (まあ，Vector3(R,G,B) * Area は良いとしても)
//	Graphics Gems の II の Progressive Refinement 実装例ではこうし
//	ているようだ． 1998年12月11日(金)
//------------------------------
double triangleObjectClass::
getNonRadEnergy(void) const
{
    double ret = this->mArea * (this->mNonRad.get(Vector3::X) +
				this->mNonRad.get(Vector3::Y) +
				this->mNonRad.get(Vector3::Z)); 
    return(ret);	
}

//------------------------------
// 重心 (HemiCube の放射中心位置)
//   その都度計算するようにする．1998年12月 3日(木)
//------------------------------
const Vector3 triangleObjectClass::
getGCenter(void) const
{
    return((this->mVertex[0] + this->mVertex[1] + this->mVertex[2]) / 3.0);
}


//------------------------------
// パッチの未放射 radioosity 値を返す
//	ラジオシティ値増加 + 未放射クリア
//	ここまで atomic な操作(本来はキューから出すことも atomic だが、
//	それは上位のルーチンで保つ) 
//------------------------------
const Vector3 triangleObjectClass::
getNClearNonRadAndUpdateRadiosity(void)
{
    static Vector3 retNonRad;	// static for return reference 

    this->mRadiosity = this->mRadiosity + this->mNonRad; // ラジオシティ値増加
    retNonRad = this->mNonRad;				 // 未放射値保存
    this->mNonRad.set(0.0, 0.0, 0.0);			 // 未放射値クリア
    return(retNonRad);					 // 保存未放射値 return
}


//------------------------------
// 各頂点の輝度を平均化する
//------------------------------
void triangleObjectClass::
calcVerIntAveVal(void)
{
    int i;
    for(i = 0; i < 3; i++)
    {
    	this->mIntensity[i] = 
		this->mIntensity[i] / this->mAddedIntensityNum[i];
    }
}

//------------------------------------------------------------
// 物体までの距離(if ret PlusInfty =  not hit)
//------------------------------------------------------------
double triangleObjectClass::
getDistance(const Ray *const apRay, Vector3 *pHitPos)
{
    double distance = PlusInfty; // 交差点までの距離          
    double tpDotRay = 0.0;	 // 内積の値                  
    
    // 三角形ポリゴンを含む平面との交差       
    tpDotRay = (this->getNormal()).dInn_prd(apRay->getDir());
    if(tpDotRay >= -(D_ERROR_RES))
    {				// 見えない(裏か交差しない)
	return(PlusInfty);
    }

    // 三角形ポリゴンを含む平面との交差点との距離
    distance = (this->getPlaneConstant() - 
		(this->getNormal()).dInn_prd(apRay->getPos())) / tpDotRay;

    if(distance <= D_ERROR_RES)
    {				// 見えない(後ろにある)
	return(PlusInfty);
    }
    
    // 交差点を求める
    (*pHitPos) = (apRay->getPos()) + (apRay->getDir()) * distance;
    
    // エッジの検査(三角形の内部か?)
    for(int i = 0; i < 3; i++)
    {
	double dTes = (this->getEdgeNormal(i)).dInn_prd(*pHitPos) - 
	    this->getEdgeConstant(i);
	if(dTes < 0.0)
	{			// ここに来たら三角パッチの外にある = 外れ
	    return(PlusInfty);
	}
    }
    // Hit
    return(distance); 
}

//------------------------------------------------------------
// 出力
//------------------------------------------------------------
// ostream& operator<<(ostream& s, const triangleObjectClass& rhs)
// {
//     s << "f " << rhs.mObjectID << rhs.mSpcPos << 
// 	rhs.mVertex[0] << rhs.mVertex[1] << rhs.mVertex[2] << rhs.mSpcPos;
//     return (s);
// }

//------------------------------
// porigonID と頂点の位置があっていたら輝度を加える
//------------------------------
void triangleObjectClass::
addIntensityToVertex(const VertexIntensity& vertex)
{
    if(this->getObjectID() == vertex.getPolygonID())
    {
	for(int i = 0; i < 3; i++)
	{
	    if(this->mVertex[i] == vertex.getVertex())
	    {
		this->mIntensity[i] += vertex.getIntensity();
		this->mAddedIntensityNum[i]++;
		break;		// 頂点は必ず違うので一つ合えば終了
	    }
	}
    }
}

//------------------------------
// ポリゴンで一様な Radiosity 値と頂点の位置情報を取り出す
//	各輝度を取り出さないのは輝度の増加の時に各輝度は増加していくた
//	めに互いに送り合うと正しい値にならない。
//
//	dest spcpos は正しくないものが入っている
//	返り値は src (= この頂点のいた) 空間位置
//------------------------------
void triangleObjectClass::
getRadVer(VertexIntensity *pRadiosityVertexInfo)
{
    static IntVec3 illegalSpcPos(-1, -2, -3);
    int i;
    for(i = 0; i < 3; i++)
    {
	assert(this->mIntensity[i].get(Vector3::X) >= 0.0);
	pRadiosityVertexInfo[i].set(this->getObjectID(), illegalSpcPos,
				    this->mRadiosity,    this->mVertex[i]);
    }
}

//------------------------------
// 各頂点と各輝度を取り出す
//	dest spcpos は正しくないものが入っている
//	返り値は src (= この頂点のいた) 空間位置
//------------------------------
void triangleObjectClass::
getVerIntensity(VertexIntensity *pIntensityVertexInfo)
{
    static IntVec3 illegalSpcPos(-1, -2, -3);
    int i;
    for(i = 0; i < 3; i++)
    {
	assert(this->mIntensity[i].get(Vector3::X) >= 0.0);
	pIntensityVertexInfo[i].
	    set(this->getObjectID(),    illegalSpcPos,
		this->mIntensity[i], this->mVertex[i]);
    }
}

//------------------------------
// print out 
//------------------------------
void triangleObjectClass::
print(const char *pHead)
{
    cout << pHead << " # material id = " << this->mMaterialID << endl;    
    cout << pHead << " f " << this->getObjectID() << " ";
    cout << this->mVertex[0] << " ";
    cout << this->mVertex[1] << " ";
    cout << this->mVertex[2] << " ";
    cout << this->mSpcPos    << endl;
}

//------------------------------
// get mpi2 format strings
//------------------------------
void triangleObjectClass::
getMpi2Form(int type, char *pRes)
{
    switch(type)
    {
    case NORMAL_ObjOut_e:
	sprintf(pRes, "f %d  %.16g %.16g %.16g  %.16g %.16g %.16g  %.16g %.16g %.16g %d %d %d",
		this->getObjectID(),
		this->mVertex[0].get(Vector3::X),
		this->mVertex[0].get(Vector3::Y),
		this->mVertex[0].get(Vector3::Z),
		this->mVertex[1].get(Vector3::X),
		this->mVertex[1].get(Vector3::Y),
		this->mVertex[1].get(Vector3::Z),
		this->mVertex[2].get(Vector3::X),
		this->mVertex[2].get(Vector3::Y),
		this->mVertex[2].get(Vector3::Z),
		this->mSpcPos.get(IntVec3::X),
		this->mSpcPos.get(IntVec3::Y),
		this->mSpcPos.get(IntVec3::Z));
	break;
	
    case WITH_SHOT_RADOUT_e:
	sprintf(pRes, "F %d  %.16g %.16g %.16g  %.16g %.16g %.16g  %.16g %.16g %.16g %d %d %d  %.16g %.16g %.16g",
		this->getObjectID(),
		this->mVertex[0].get(Vector3::X),
		this->mVertex[0].get(Vector3::Y),
		this->mVertex[0].get(Vector3::Z),
		this->mVertex[1].get(Vector3::X),
		this->mVertex[1].get(Vector3::Y),
		this->mVertex[1].get(Vector3::Z),
		this->mVertex[2].get(Vector3::X),
		this->mVertex[2].get(Vector3::Y),
		this->mVertex[2].get(Vector3::Z),
		this->mSpcPos.get(IntVec3::X),
		this->mSpcPos.get(IntVec3::Y),
		this->mSpcPos.get(IntVec3::Z),
		this->mRadiosity.get(Vector3::X),
		this->mRadiosity.get(Vector3::Y),
		this->mRadiosity.get(Vector3::Z));
	break;
	
    case WITH_UNSHOT_RADOUT_e:
	sprintf(pRes, "F %d  %.16g %.16g %.16g  %.16g %.16g %.16g  %.16g %.16g %.16g %d %d %d  %.16g %.16g %.16g",
		this->getObjectID(),
		this->mVertex[0].get(Vector3::X),
		this->mVertex[0].get(Vector3::Y),
		this->mVertex[0].get(Vector3::Z),
		this->mVertex[1].get(Vector3::X),
		this->mVertex[1].get(Vector3::Y),
		this->mVertex[1].get(Vector3::Z),
		this->mVertex[2].get(Vector3::X),
		this->mVertex[2].get(Vector3::Y),
		this->mVertex[2].get(Vector3::Z),
		this->mSpcPos.get(IntVec3::X),
		this->mSpcPos.get(IntVec3::Y),
		this->mSpcPos.get(IntVec3::Z),
		this->mNonRad.get(Vector3::X),
		this->mNonRad.get(Vector3::Y),
		this->mNonRad.get(Vector3::Z));	
	break;
    default:
	cout << "Internal Error! : triangleObjectClass::getMpi2Form : "
	"Illegal Output Type." << endl;
	exit(1);
	break;
    }
}

//------------------------------
// who am i 
//------------------------------
int triangleObjectClass::
who(void) const
{
    return(TRIANGLE_OBJ_E);
}

//------------------------------
// 正しい空間にいるか
//------------------------------
YorN triangleObjectClass::
checkSpcPos(const Vector3 &partmin, const Vector3 &subSpcSize,
	    int subdivnum) const
{
    // check spcpos num
    int i;    
    for(i = 0; i < 3; i++)
    {
	if((this->mSpcPos.get(i) < 0) || (this->mSpcPos.get(i) >= subdivnum))
	{
	    cout << "Error! :f(triangle): spcpos is out of define space." 
		<< endl;
	    return(NO);
	}
    }

    // check vertex range
    double min[3];
    double max[3];
    for(i = 0; i < 3; i++)
    {
	min[i] = partmin.get(i) + 
	    (double)(this->mSpcPos.get(i))     * subSpcSize.get(i);
	max[i] = partmin.get(i) + 
	    (double)(this->mSpcPos.get(i) + 1) * subSpcSize.get(i);
    }
    const double err_tolerance = subSpcSize.get(0) * 1e-3; // 1 % までは OK 
    int ver;
    for(ver = 0; ver < 3; ver++)
    {
	for(i = 0; i < 3; i++)
	{
	    if((this->mVertex[ver].get(i) < (min[i] - err_tolerance)) ||
	       (this->mVertex[ver].get(i) > (max[i] + err_tolerance)))
	    {
		static const char *s[] = { "x", "y", "z", };
		cout << "Error! :f(triangle): vertex[" << ver << "](" << 
		    s[i] << ") is not in legal spcpos" << endl;
		cout << "It must be in [" << min[i] << ", " << max[i] <<
		    "]. (tolerance = " << err_tolerance << ")" << endl;
		cout << "spcpos = "  << this->mSpcPos 
		     << ", partmin = " << partmin << endl;
		return(NO);
	    }
	}
    }
    return(YES);
}

//------------------------------------------------------------
// for 2 dim draw funcs
//------------------------------------------------------------
#ifdef FOR_2DIM_OUT
int triangleObjectClass::
get2dimPoiNumbers(void) const
{
    return(3);
}

void triangleObjectClass::
get2dimPoint(int idx, double *x, double *y, double *z)
{
    assert((idx >= 0) && (idx < 3));
    (*x) = this->mVertex[idx].get(Vector3::X);
    (*y) = this->mVertex[idx].get(Vector3::Y);    
    (*z) = this->mVertex[idx].get(Vector3::Z);
}

Vector3 triangleObjectClass::
getFlatRadiosityColor(const Vector3& hitPos)
{
    return(hitPos);
}

Vector3 triangleObjectClass::
getShadedRadiosityColor(const Vector3& hitPos)
{
    return(hitPos);
}
#endif

#ifdef READTEST
Vector3 triangleObjectClass::
getFlatRadiosityColor(const Vector3& hitPos)
{
    return(hitPos);
}

Vector3 triangleObjectClass::
getShadedRadiosityColor(const Vector3& hitPos)
{
    return(hitPos);
}
#endif

