//------------------------------------------------------------
// $Id: calcRayTrIC.cc,v 1.3.2.1 2000/06/14 15:00:10 yamauchi Exp $
//------------------------------------------------------------
//	calcRayTrIC.cc
//	calculation of Intensity Calculation unit for ray tracing 
//	1996/8/2, 1996/9/18 
//	(C) 1995-1998 Copyright YAMAUCHI Hitoshi
//
//	輝度計算 
//	(Mapping) Shading は物体の方で行うが、
//	mapping はその限りに非ず 
//------------------------------------------------------------
#include <assert.h>
#include <stdlib.h>
#include <math.h>

#include "PE.h"
#include "mpi2exit.h"
#include "ray.h"
#include "triangleObject.h"


// NO_OUT_SHADE_ERROR が定義されていない場合には shading の計算に失敗
// するとエラーメッセージを出力する 

//------------------------------------------------------------
// calculate Intensity 
// 	輝度を計算し、それを ray に加えていく
//------------------------------------------------------------
void PEClass::
calcNsetIntensityRayTr(Ray *pRay, objBaseClass *pHitObj)
{
    // assert(pRay->getIntObj() != NULL);
    assert(pHitObj != NULL);

    // 輝度計算 計算済みの物体の拡散輝度を求める 
    // 拡散反射は計算されているとし、ここでは物体の拡散反射係数は掛けない
    Vector3 intensity = pRay->getColor();

    // swich in most deep location, can I improve? IMPROVEME
    switch(this->mSysInfo.getShadingScheme())
    {
    case FlatShading:
	intensity = intensity + (pRay->getRefCoeff()) * 
		(pHitObj->getFlatRadiosityColor(pRay->getHitPos()));
	break;
    case GouroudShading:
	intensity = intensity + (pRay->getRefCoeff()) * 
		(pHitObj->getShadedRadiosityColor(pRay->getHitPos()));
	break;
    default:
	cout << "Internal Error! : illegal shading scheme in " << 
	    "PEClass::calcNsetIntensityRayTr" << endl;
	commMpi2_exit(1);
    }

    pRay->setColor(intensity);
    pRay->incReflectedNum();	// 当たった回数を増加させる

    // この面に反射したために減衰係数が変化するがそれは二次光の発生部
    // 分で行う 
}


//------------------------------
// hit point の位置の輝度を返す
// 	flat shading
//------------------------------
Vector3 triangleObjectClass::
getFlatRadiosityColor(const Vector3& hitPos)
{
    return(this->mRadiosity);	// radiosity 値を返しているだけ
    cout << hitPos << endl;	// for Wall
}

//------------------------------
// エラーが発生した場合の値の出力
//------------------------------
void showvers(const Vector3& hitpos, const Vector3& hvec, const Vector3& evec,
	      const Vector3& top,    const Vector3& side, const Vector3& other,
	      int asX, int asY, double oeh, double t, double s)
{
    cout << "hitpos        " << hitpos<< endl;
    cout << "to hitpos vec " << hvec  << endl;
    cout << "edge vector   " << evec  << endl;
    cout << "vertex top    " << top   << endl;
    cout << "vertex side   " << side  << endl;
    cout << "vertex other  " << other << endl;
    cout << "asX           " << asX   << endl;
    cout << "asY           " << asY   << endl;
    cout << "oeh           " << oeh   << endl;
    cout << "t             " << t     << endl;
    cout << "s             " << s     << endl;
}

//------------------------------
// hit point の位置の輝度を返す
// 	Gouroud shading
//------------------------------
Vector3 triangleObjectClass::
getShadedRadiosityColor(const Vector3& hitPos)
{
    const double ERRTOL = 1.0e-20; // error tolerance 

    int topver   = -1;		// 初期化しないと文句が出る
    int sidever  = -1;
    int otherver = -2;
    int asX	 = -3;
    int asY	 = -5;
    // find (asX, asY) plane
    for(topver = 0; topver < 3; topver++)
    {
	sidever  = (topver + 1) % 3;
	otherver = (topver + 2) % 3;
	asX = asY = -1;
	int i, j;
	for(i = 0; i < 3; i++)
	{
	    //// 三点が異なる点に見えるように X,Y を選択する
	    if(fabs(this->mVertex[topver  ].get(i) - 
 		    this->mVertex[sidever ].get(i)) > 1e-6)
	    {			//// top, side の異なる軸であり、
 		for(j = 0; j < 3; j++)
 		{
		    if(i != j)	// i 軸は決定している
		    {
			if(fabs(this->mVertex[topver  ].get(j) -
				this->mVertex[otherver].get(j)) > 1e-6)
			{
			    asX = i;
			    asY = j;
			    i = 3; // 内ループ脱出 : goto で出るべきかな
			    break;
			}
		    }
 		}
	    }
	}
	if(asX >= 0)
	{
	    break;
	}
    }
    if(asX < 0)
    {
#ifndef NO_OUT_SHADE_ERROR
	cout << "Error! : can't solve this triangle. asX(" << 
	  asX << ") < 0) but ignored and average intensity." << endl;
#endif
	Vector3 aveInt = (this->mIntensity[0] + this->mIntensity[1] +
			  this->mIntensity[2]) / 3.0;  
	return(aveInt);
    }
    
    if((fabs(this->mVertex[topver].get(Vector3::X) - 
	                    hitPos.get(Vector3::X)) < ERRTOL) && 
       (fabs(this->mVertex[topver].get(Vector3::Y) - 
	                    hitPos.get(Vector3::Y)) < ERRTOL) && 
       (fabs(this->mVertex[topver].get(Vector3::Z) - 
	                    hitPos.get(Vector3::Z)) < ERRTOL))
    {
	return(this->mIntensity[topver]); // top と頂点が一致の時にはそのまま
    }
    
    Vector3 hvec = hitPos - this->mVertex[topver];
    Vector3 evec = this->mVertex[otherver] - this->mVertex[sidever];
    double ex	 = evec.get(asX);
    double ey 	 = evec.get(asY);
    double hx	 = hvec.get(asX);
    double hy	 = hvec.get(asY);
    double Tx	 = this->mVertex[topver].get(asX); // P0x (top vertex)
    double Ty	 = this->mVertex[topver].get(asY); // P0y
    double Sx	 = this->mVertex[sidever].get(asX); // P1x (side vertex)
    double Sy	 = this->mVertex[sidever].get(asY); // P1y
    double oeh	 = ex * hy - ey * hx;		    // t を求める時の分母

    if(fabs(oeh) < ERRTOL)		
    {				// この選択ではこうならないはず
#ifndef NO_OUT_SHADE_ERROR
	cout << "Error! : can't solve this triangle. oeh near 0" << endl;
	cout << "ignored and average intensity (t, s is not calced)" << endl;
#endif
#ifndef NDEBUG
	showvers(hitPos, hvec, evec, this->mVertex[topver],
		 this->mVertex[sidever], this->mVertex[otherver], 
		 asX, asY, oeh, -1.0, -1.0);
#endif
	Vector3 aveInt = (this->mIntensity[0] + this->mIntensity[1] +
			  this->mIntensity[2]) / 3.0;  
	return(aveInt);
	// commMpi2_exit(1);
    }
    double t = (Tx * hy - Ty * hx - Sx * hy + Sy * hx) / oeh;
    double s;
    if(fabs(hx) > fabs(hy))
    {
	s = (Sx - Tx + t * ex) / hx;
    }else
    {
	s = (Sy - Ty + t * ey) / hy; // hx が小さすぎる場合には hy で計算
    }
    Vector3 intensityP = (1.0 - t) * this->mIntensity[sidever]
 	+ t * this->mIntensity[otherver];
    Vector3 intensityH =  (1.0 - (1.0 / s)) * this->mIntensity[topver]
 	+ (1.0 / s) * intensityP;

#ifndef NDEBUG
    if((t < -0.0001) || (s < -0.0001))
    {
	cout << "Error! : illegal t or s" << endl;
	showvers(hitPos, hvec, evec, this->mVertex[topver], 
		 this->mVertex[sidever], this->mVertex[otherver], 
		 asX, asY, oeh, t, s);
    }
#endif
    // 輝度計算に失敗した場合には各頂点の平均値を輝度とする。
    if((intensityH.get(Vector3::X) < 0.0) || 
       (intensityH.get(Vector3::Y) < 0.0) || 
       (intensityH.get(Vector3::Z) < 0.0))
    {
#ifndef NO_OUT_SHADE_ERROR
	cout << "Warning! : calcRayTrIC::intensity " << intensityH << 
	    ": < 0.0" << endl;
#endif
	intensityH = (this->mIntensity[0] + this->mIntensity[1] +
		      this->mIntensity[2]) / 3.0;
#ifndef NDEBUG
	showvers(hitPos, hvec, evec, this->mVertex[topver],
		 this->mVertex[sidever],  this->mVertex[otherver], 
		 asX, asY, oeh, t, s);
#endif
    }
    return(intensityH);
}
