//------------------------------------------------------------
// $Id: calcRadioSRG.cc,v 1.2.2.1 2000/06/14 15:00:10 yamauchi Exp $
//------------------------------------------------------------
//	calcRadioSRG.cc 
//	calculation of Secondary Ray Generation unit for radiosity 
// 1996/8/8  calcRayTrSRG.cc 
// 1999/2/18 
//	(C) 1995-1999 Copyright YAMAUCHI Hitoshi 山内 斉
//	
//	ray-tracing 法と radiosity 法では反射レイの生成に違いあり．
//	2 次レイ発生 for Radiosity
//------------------------------------------------------------
#include <assert.h>
#include "PE.h"

//------------------------------
// 反射による二次レイ発生
//	現在錐体を利用した ray tracing ではない
//------------------------------
static inline Ray *newReflectionRadiosityRays(const  Ray *const pRay,
					      const  materialClass*  pMat,
					      double minRayCoefNorm,
					      objBaseClass *pHitObj)
{
    assert(pHitObj != NULL);

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

    // commented by tosho : これまでは容赦なく ray を生成していた
    //   ある反射減衰係数以下のエネルギの ray はこれ以上伝搬しない
    //   この部分，minRayCoefNorm は ray-tracing と同じであるのは誤り
    //   かもしれない． FIXHERE
    if(afterReflectRadiosity.dInn_prd(afterReflectRadiosity) < minRayCoefNorm)
    {
	return(NULL);
    }

    // 反射方向を求める
    Vector3 normal = pHitObj->getNormal(pRay->getHitPos());
    assert((normal.getNorm() - 1.0) < D_ERROR_RES);//法線は正規化されているはず
    Vector3 reflectedRayDir = 
	(-2.0 * ((pRay->getDir()).dInn_prd(normal)) * normal) + pRay->getDir();
    
    // 反射レイ作成 newRay! for radiosity 色もセットする
    // このコンストラクタで作成したレイは一度当った物体には交差しない 
    Ray *pNewRay = Ray::newForReflectionRadiosity(pRay, reflectedRayDir, 
						  afterReflectRadiosity, 
						  pHitObj);
    return(pNewRay);
}

//------------------------------
// 屈折による二次レイ発生
//------------------------------
static inline Ray *newRefractionRadiosityRays(const  Ray *const pRay,
					      const  materialClass*  pMat,
					      double minRayCoefNorm,
					      objBaseClass *pHitObj)
{
    // ray を copy して pMat の透過係数を掛け、更新する
    Vector3 afterRefractRadiosity = (pRay->getColor())* (pMat->getSpecTrans());

    if(afterRefractRadiosity.dInn_prd(afterRefractRadiosity) < minRayCoefNorm)
    {
	return(NULL);
    }
    
    // 透過方向計算
    Vector3 refractedRayDir;
    if(pMat->getIdxRefraction() != 1.0)	
    {
	// 屈折する 屈折レイ作成
	assert(pHitObj != NULL);
	Vector3 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 n   = pMat->getIdxRefraction(); // 屈折率
	const double ndi = (pRay->getDir()).dInn_prd(normal); // (N \dot I)
	const double rt  =  sqrt(1.0 - (n * n) * (1.0 - (ndi * ndi)));
	refractedRayDir = 
	    (((n * (-ndi)) - rt) * normal) + (n * pRay->getDir());
    }else
    {
	// pMat->getIdxRefraction() == 1.0  : 透過率 1 無意味かな。
	refractedRayDir = pRay->getDir();
    }

    // 透過レイ作成 newRay! for radiosity 色もセットする
    // このコンストラクタで作成したレイは一度当った物体には交差しない 
    // 基本的に初期化は refraction も refraction も同じ
    Ray *pNewRay = 
    	Ray::newForReflectionRadiosity(pRay, refractedRayDir, 
				       afterRefractRadiosity, pHitObj);
    return(pNewRay);
}


//------------------------------------------------------------
// 2次 radiosity ray 生成
//     	輝度計算は IC で済んでいる
//	(論文に書いている設計とは違い，このことは IC->SRG の接続を示し
//	 ている．論文では反射毎に FrameBuffer へと送信しているが，実装
//	 には ray 消滅の際にしか FrameBuffer への更新が起こらない．
//		(see mpi2doc_implementation)
//------------------------------------------------------------
YorN PEClass::
newSecondaryRadiosityRays(Ray *pRay, const materialClass *pMat, 
			  objBaseClass * pHitObj)
{
    YorN isGenerateRay = NO;	// レイは発生したか? 

    // この ray は反射・屈折回数を越えているか
    if(pRay->getReflectedNum() >= this->mViewData.getMaxRayRefNum())
    {				// 越えている。ray は発生しない。
	assert(isGenerateRay == NO);
	return(isGenerateRay);
    }
    
    //------------------------------------------------------------
    // 反射 2 次 radiosity ray を new し、PE の処理キューへ登録
    //------------------------------------------------------------
    Ray *pNewHanshaRay = 
    	newReflectionRadiosityRays(pRay, pMat, 
				   this->mViewData.getMinRayRefCoeffNorm(),
				   pHitObj);
    if(pNewHanshaRay != NULL)	// ray を発生した．キューへ
    {
	this->setRayPtrToThisPE(pNewHanshaRay);		   
	isGenerateRay = YES;
	// 反射 radiosity ray 生成を数えるのならばここ Hint TOKUNAGA
    }

    //------------------------------------------------------------
    // 透過 2 次l radiosity ray を new し、PE の処理キューへ登録
    //------------------------------------------------------------
    Ray *pNewToukaRay = 
	newRefractionRadiosityRays(pRay, pMat, 
				   this->mViewData.getMinRayRefCoeffNorm(),
				   pHitObj);
    if(pNewToukaRay != NULL)
    {
 	this->setRayPtrToThisPE(pNewToukaRay);
	isGenerateRay = YES;
	// 屈折レイ生成を数えるのならばここ Hint TOKUNAGA
    }
    return(isGenerateRay);
}
