//------------------------------------------------------------
// $Id: calcRayTrSRGgetdir.h,v 1.2 2000/06/14 13:29:37 yamauchi Exp $
//------------------------------------------------------------
//	calcRayTrSRGgetDir.cc
//	calculation of secondary ray direction
//	1999/3/24
//	(C) 1998-1999 Copyright YAMAUCHI Hitoshi 山内 斉
//	(C) 1998-1999 Copyright TOKUNAGA Mayumi  徳永 真由美
//
// inline functions for get refrection/refraction direction 
//------------------------------------------------------------
#ifndef _calcRayTrSRGgetdir_h
#define _calcRayTrSRGgetdir_h 1

#include <assert.h>
#include "PE.h"

//------------------------------
// 反射による二次レイ発生
//	現在錐体を利用した ray tracing ではない
// @param pRay 		    IN 	入射レイ
// @param pMat 		    IN 	反射する物体(= pRay と交差した物体)の material 
// @param minRayCoefNorm    IN  反射時の最小エネルギ
// @param pHitObj 	    IN	pRay との交差物体
// @param pReflectRayDir    OUT	反射レイ方向ベクトル
// @param pReflectRayCoeff  OUT	反射レイの輝度係数
//------------------------------
static inline YorN 
getReflectionRayDirRayTrace(const  Ray* const 	  pRay,
			    const  materialClass* pMat,
			    double 		  minRayCoefNorm,
			    objBaseClass *	  pHitObj,
			    Vector3      *	  pReflectRayDir,
			    Vector3 	 *	  pReflectRayCoeff)
{
    assert(pHitObj != NULL);

    // 鏡面反射減衰係数を計算する
    (*pReflectRayCoeff) = pRay->getRefCoeff() * (pMat->getSpecRef());

    // commented by tosho : これまでは容赦なく ray を生成していた
    //   ある反射減衰係数以下の ray はこれ以上伝搬しない
    if((*pReflectRayCoeff).dInn_prd(*pReflectRayCoeff) < minRayCoefNorm)
    {
	return(NO);
    }

//     cout << "DEBUG:refcoef " << (*pReflectRayCoeff)	<< ", inprd " << 
// 	(*pReflectRayCoeff).dInn_prd(*pReflectRayCoeff) << " < min " << 
// 	    minRayCoefNorm << ", raycoeff " << pRay->getRefCoeff() << 
// 		", matspec " << (pMat->mSpecRef) << endl;

    // 反射方向を求める
    // tokunaga change: 
    // sphere の場合は getNormal で返ってくるベクトルが球の外を向いて
    // いるので物体中にレイがいる場合は、ひとまず法線を逆向きにする。  
    Vector3 normal;
    switch(pHitObj->who())
    {
    case TRIANGLE_OBJ_E:
	normal = pHitObj->getNormal(pRay->getHitPos());
	break;

    case SPHERE_OBJ_E:
	if(pRay->isInside() == YES)
	{
	    normal = (-1.0) * pHitObj->getNormal(pRay->getHitPos()); 
	}else
	{
	    normal = pHitObj->getNormal(pRay->getHitPos());	    
	}
	break;
	
    case NULL_OBJ_E:
    default:
	cout << "Internal Error! : illegal object : "
	    "getReflectionRayDirRayTrace" << endl;
	break;
    }

    assert((normal.getNorm() - 1.0) < D_ERROR_RES);//法線は正規化されているはず
    (*pReflectRayDir) = 
	(-2.0 * ((pRay->getDir()).dInn_prd(normal)) * normal) + pRay->getDir();
    
    return(YES);
}

//------------------------------
// 屈折による 2 次 ray-tracing ray 発生
//
// @param pRay 		    IN 	入射レイ
// @param pMat 		    IN 	反射する物体(= pRay と交差した物体)の material 
// @param minRayCoefNorm    IN  反射時の最小エネルギ
// @param pHitObj 	    IN	pRay との交差物体
// @param pRefractRayDir    OUT	屈折レイ方向ベクトル
// @param pRefractRayCoeff  OUT	屈折レイの輝度係数
//------------------------------
static inline YorN
getRefractionRayDirRayTrace(const  Ray* const 	  pRay,
			    const  materialClass* pMat,
			    double 		  minRayCoefNorm,
			    objBaseClass *	  pHitObj,
			    Vector3 * 		  pRefractRayDir,
			    Vector3 * 		  pRefractRayCoeff)
{
    // ray を copy して pMat の透過係数を掛け、更新する
    (*pRefractRayCoeff) = (pRay->getRefCoeff()) * (pMat->getSpecTrans());

    if((*pRefractRayCoeff).dInn_prd(*pRefractRayCoeff) < minRayCoefNorm)
    {
	return(NO);
    }
    
    // 透過方向計算
    if(pMat->getIdxRefraction() != 1.0)	
    {
	// 屈折する 屈折レイ作成
	assert(pHitObj != NULL);

	// 物体内部にいる場合は屈折率の逆数を屈折率にする
	// sphere の場合は反射レイと同じく法線ベクトルを逆向きに
	Vector3 normal;  // 法線は正規化されているはず
	double  n;        // 屈折率
	if(pRay->isInside() == YES)
	{
	    n = pMat->getIdxRefraction(); 
	    
	    switch(pHitObj->who())
	    {
	    case TRIANGLE_OBJ_E:
		normal = pHitObj->getNormal(pRay->getHitPos());
		break;
		
	    case SPHERE_OBJ_E:
		if(pRay->isInside() == YES)
		{
		    normal = (-1.0) * pHitObj->getNormal(pRay->getHitPos()); 
		}else
		{
		    normal = pHitObj->getNormal(pRay->getHitPos());
		}
		break;
		
	    case NULL_OBJ_E:
	    default:
		cout << "Internal Error! : illegal object : "
		    "getRefractionRayDirRayTrace" << endl;
		break;
	    }
	}
	else
	{
	    n = 1.0 / (pMat->getIdxRefraction());
	    normal = pHitObj->getNormal(pRay->getHitPos());
	}
	//法線は正規化されているはず
	assert((normal.getNorm() - 1.0) < D_ERROR_RES);
	
	// 屈折方向のベクトル T (I 入射ベクトル, N 物体法線ベクト
	// ル, n 相対屈折率 >= 1.0)  
	// T = (n (-N \dot I) - \sqrt(1 - n^2(1 - (N \dot I)^2)) N) + nI
	
	const double ndi = (pRay->getDir()).dInn_prd(normal); // (N \dot I)
	const double rt_2  =  1.0 - (n * n) * (1.0 - (ndi * ndi));
	    
	// tokunaga changed:
	if(rt_2 < 0.0) // \sqrt の中身が 0 未満の場合には透過光は発生しない
	{
	    return(NO);
	}
	else
	{
	    const double rt = sqrt(rt_2);
	    (*pRefractRayDir) = 
		(((n * (-ndi)) - rt) * normal) + (n * pRay->getDir());
	}
    }
    else
    {
	// pMat->mIdxRefraction == 1.0  : 透過率 1 無意味かな。
	(*pRefractRayDir) = pRay->getDir();
    }

    // 透過レイ作成 newRay!
    // このコンストラクタで作成したレイは一度当った物体には交差しない 
    //     Ray *pNewRay = 
    //     	Ray::newForReflectionRayTracing(pRay, refractedRayDir, 
    //					newRefCoef, pHitObj);

    // 屈折の場合には物体の中と外が変換される
    // pNewRay->changeSide();
    // DELETEME
    return(YES);
}

/**
//反射、屈折をエネルギ配分で決定 未使用
// tokunaga changed
static inline double reflectEnergyAlloc(const  Ray *const pRay,
					const  materialClass*  pMat,
					objBaseClass *pHitObj)

{
    Vector3 normal = pHitObj->getNormal(pRay->getHitPos());
    double cos_in =
	((pRay->getDir()).dInn_prd(normal))
	    /((pRay->getDir()).getNorm() * normal.getNorm()); 

    double n = pMat->mIdxRefraction;
    double tmp = n * n - 1 + cos_in * cos_in;
    if (tmp <= 0)
    {
	return(1.0);
    }
    else
    {
	double r_half = (cos_in - sqrt(tmp))
	    /(cos_in + sqrt(tmp));
	double r = r_half * r_half;
	return(r);
    }
}
*/

// 徳永改造モーード！
// YorN PEClass::
// newSecondaryRays(Ray *pRay, const materialClass *pMat, 
// 		 objBaseClass * pHitObj)
// {
//     YorN isGenerateRay = NO;	// レイは発生したか? 
//     
//     //tokunaga directioanl diffuse 
//     if(pMat->mfrustrum_rad != 0.0)
//     {
// 	// 
// 	return(this->secondDistrbuteRays(pRay, pMat, pHitObj));
//     }else
//     {
// 	// この ray は反射・屈折回数を越えているか
// 	if(pRay->getReflectedNum() >= this->mViewData.getMaxRayRefNum())
// 	{				// 越えている。ray は発生しない。
// 	    assert(isGenerateRay == NO);
// 	    return(isGenerateRay);
// 	}
// 	
// 	//------------------------------------------------------------
// 	// 反射二次レイを new し、PE の処理キューへ登録
// 	//------------------------------------------------------------
// 	Ray *pNewHanshaRay = 
// 	    newReflectionRays(pRay, pMat, 
// 			      this->mViewData.getMinRayRefCoeffNorm(),
// 			      pHitObj);
// 	if(pNewHanshaRay != NULL)	// ray を発生した。輝度を託す。
// 	{
// 	    pNewHanshaRay->setColor(pRay->getColor());
// 	    this->setRayPtrToThisPE(pNewHanshaRay);		   
// 	    isGenerateRay = YES;
// 	    // 反射レイ生成を数えるのならばここ Hint TOKUNAGA
// 	}
// 	
// 	//------------------------------------------------------------
// 	// 透過二次レイを new し、PE の処理キューへ登録
// 	//------------------------------------------------------------
// 	Ray *pNewToukaRay = 
// 	    newRefractionRays(pRay, pMat, 
// 			      this->mViewData.getMinRayRefCoeffNorm(),
// 			      pHitObj);
// 	if(pNewToukaRay != NULL)
// 	{
// 	// ray を発生した。既に輝度は託されているのか?
// 	    if(isGenerateRay == NO)
// 	    {			// 託されていない。輝度を託す
// 		pNewToukaRay->setColor(pRay->getColor());	    
// 	    }
// 	    this->setRayPtrToThisPE(pNewToukaRay);
// 	    isGenerateRay = YES;
// 	    // 屈折レイ生成を数えるのならばここ Hint TOKUNAGA
// 	}
// 	return(isGenerateRay);
//     }
// }

#endif				// #ifdef   _calcRayTrSRGgetdir_h
