#include "stdafx.h"
#include "DynamicSceneNode.h"

#include "SceneTree.h"
#include "SceneManager.h"

#include "ResourceManager.h"
#include "RenderSystem.h"
#include "CameraManager.h"

#include "EffectSceneNode.h"
//#include "LightAgent.h"
//#include "FogAgent.h"
#include "SoundSystem.h"
#include "SoundSceneNode.h"
//#include "ObjectNameCard.h"
//#include "ShadowGeometry.h"
//
//#include "ChatBubble.h"


cDynamicSceneNode::cDynamicSceneNode( eType type ) 
: cSceneNode( type )
, mTargetAnimation( UINT_MAX )
, mRandomIdle( 0 )
, mModel(0)
//, mpNameCard(0)
//, mpChatBubble(0)
, mLinkInfo(0)
, mActorManager(0)
{
	mHead = 0;
	mHeadDist = 20.0f;

	mMorpherController = 0;
	mEyeMorpherTime = 0;
	mWinkInterval = 4.f;
	mStopMorpherProcess = false;

	mLastTime = -FLT_MAX;
	mScaleFactor = 1.0f;

	mRadius = 0.0f;

	mBaseAnimationIndex = 0;
}

cDynamicSceneNode::~cDynamicSceneNode()
{
	/// 등록된 이펙트가 있다면 모두 제거 한다.
	NiTListIterator pos = mEffectList.GetHeadPos();
	while( pos )
	{
		cEffectSceneNode* pnode = mEffectList.GetNext(pos);
		SCENEMAN->DestroyEffectNode( pnode, true );
	}
	mEffectList.RemoveAll();

	pos = mSoundList.GetHeadPos();
	while( pos )
	{
		cSoundSceneNode* pnode = mSoundList.GetNext(pos);
		SCENEMAN->DestroySoundNode( pnode, true );
	}
	mSoundList.RemoveAll();

	pos = mRibbonList.GetHeadPos();
	while( pos )
	{
		sTrailInfo* p = mRibbonList.GetNext(pos);
		SAFE_NIDELETE( p );
	}
	mRibbonList.RemoveAll();

	/// 말풍선 제거
	//SAFE_DELETE(mpChatBubble);

	/// 이름 객체 제거
	//SAFE_DELETE(mpNameCard);

	if( mLinkInfo )
	{
		sLinkInfo* info = 0;
		for( unsigned int i=0; i<mLinkInfo->GetSize(); ++i )
		{
			info = mLinkInfo->GetAt( i );
			if( info )
			{
				NiDelete info;
			}
		}
		mLinkInfo->RemoveAll();
	}

	SAFE_NIDELETE( mLinkInfo );
	SAFE_NIDELETE( mRandomIdle );

	mPartsNameMap.RemoveAll();

	SAFE_NIDELETE( mActorManager );

	mModel = 0;
}

void cDynamicSceneNode::ClearAllEffect()
{
	NiTListIterator pos = mEffectList.GetHeadPos();
	while( pos )
	{
		cEffectSceneNode* pnode = mEffectList.GetNext(pos);
		SCENEMAN->DestroyEffectNode( pnode, true );
	}
	mEffectList.RemoveAll();

	pos = mRibbonList.GetHeadPos();
	while( pos )
	{
		sTrailInfo* pInfo = mRibbonList.GetNext(pos);
		SAFE_DELETE( pInfo );
	}
	mRibbonList.RemoveAll();
	mDeleteRibbonList.RemoveAll();
}

void cDynamicSceneNode::OnProcess( float deltaTime, float accumTime  )
{
	/// 임의로 뼈대정보를 변경하는 경우에 사용한다.
	UpdateBipedRootTransforms( accumTime );

	/// Alpha Fade In/Out 처리
	OnProcessAlpha( deltaTime );

/*
	/// 모퍼가 있다면, 갱신한다.
	if( mMorpherController && !mStopMorpherProcess )
	{
		if( accumTime - mEyeMorpherTime > mWinkInterval )
		{
			mEyeMorpherTime = accumTime;
			mMorpherController->Start();
			mMorpherController->SetManagerControlled( false );
		}

		mMorpherController->OnPreDisplay();

		if( !mMorpherController->GetManagerControlled() )
		{
			if( mMorpherController->GetLastScaledTime() == mMorpherController->GetEndKeyTime() )
			{
				mMorpherController->Stop();
				mMorpherController->SetManagerControlled( true );
			}
		}
	}
*/
	/// actormanager 전용 시간 계산
	float delta = accumTime - mLastTime;
	delta = delta * mScaleFactor;

	if( mLastTime == -FLT_MAX )
	{
		mScaleAccumTime = accumTime;
	}
	else
	{
		mScaleAccumTime += delta;
	}

	/// 객체의 Update
	mActorManager->Update( mScaleAccumTime );

	NiNode* pRoot = NiDynamicCast(NiNode, mNiObject);
	pRoot->Update( mScaleAccumTime );

	/// 경계구를 설정
	mBoundSphere.Set( GetCenter(), mRadius );

	/// 장면 트리를 갱신
	if( mContainer )
		mContainer->Update( this );

	ProcessTrail( accumTime - mLastTime );

	/// delete trail....
	if( mDeleteRibbonList.GetSize() )
	{
		NiTListIterator pos = mDeleteRibbonList.GetHeadPos();
		while( pos )
		{
			sTrailInfo* p = mDeleteRibbonList.GetNext(pos);
			mRibbonList.Remove(p);

			SAFE_DELETE(p);
		}
		mDeleteRibbonList.RemoveAll();
	}

	///
	if( mNeedUpdate )
	{
		mNeedUpdate = false;
	}

	/// 누적 시간을 설정
	mAccumTime = accumTime;
	mLastTime = accumTime;
}

//bool cDynamicSceneNode::OnVisible( cSceneCuller& culler )
//{
//	bool ret = cSceneNode::OnVisible( culler );
//
//	if( ret )
//	{
//		assert( mShadowGeom );
//		mShadowGeom->Update( mNeedUpdateShadow, mAlpha );
//
//		mNeedUpdateShadow = false;
//	}
//
//	return ret;
//}

bool cDynamicSceneNode::OnVisible( cSceneCuller& /*culler*/ )
{
	if( mAlpha < 0.00001f )
		return false;

//	if( mCulled )
//		return false;

	return true;
}

bool cDynamicSceneNode::Init( const cDynamicSceneNodeParam& param )
{
	//mpGameObject = param.mpObject;
	//assert( mpGameObject && "UnUsed Base Object" );

	/// 리소스 관리자로 부터 모델정보 획득..
	mModel = RESOURCEMAN->GetModelByName( param.mPathName.Cstr() );

	if( mModel == 0 )
	{
		RESOURCEMAN->LoadKFM( param.mPathName );
		mModel = RESOURCEMAN->GetModelByName( param.mPathName.Cstr() );

		if( mModel == 0 )
		{
			assert(0 && "Model file not found");
			NiMessageBox("Model file not found","Error_DynamicSceneNode");
			return false;
		}
	}

	/// 링크를 위한 메모리를 잡는다.
	if( InitLinkInfo() == false )
	{
		NiMessageBox("Fail InitLinkInfo","Error_DynamicSceneNode");
		return false;
	}

	/// 기본 파트의 네이밍 초기화
	if( InitPartsName() == false )
	{
		NiMessageBox("Fail InitPartsName","Error_DynamicSceneNode");
		return false;
	}

	/// ActorManager 생성..
	mActorManager = cActorManagerForPartition::Create( mModel, mPartsNameMap );
	if( mActorManager == 0 )
	{
		assert( 0 );
		NiMessageBox("Fail Create Actormanager","Error_DynamicSceneNode");
		return false;
	}

	/// 모핑 정보가 있다면 셋팅한다.
	mMorpherController = mActorManager->GetMorpherController();
	if( mMorpherController )
	{
//		mpMorpherController->SetActive( false );
		mMorpherController->SetAlwaysUpdate( false );
//		mpMorpherController->Stop();

		StartMorpher();
	}

	/// 갱신
	mActorManager->Update(0.0f);

	/// IDLE 상태 애니메이션 셋팅.. (여러가지 형태의 IDLE이 존재 하는 경우)
	InitializeRandomIdle();

	/// 엔진 노드를 설정
	NiNode* n = mActorManager->GetNifRoot();
	if( n == 0 )
	{
		assert( 0 );
		return false;
	}

	/// 상위 단계 초기화
	mNiObject = n;
//	param.mNiObject = n;
	if( cSceneNode::Init( param ) == false )
	{
		return false;
	}

	/// 알파 속성이 없으면 생성
	NiAlphaProperty* alphaProp = (NiAlphaProperty*)n->GetProperty( NiProperty::ALPHA );

	//if( alphaProp == 0 )
	//{
	//	alphaProp = NiNew NiAlphaProperty;
	//	alphaProp->SetAlphaBlending( false );
	//	alphaProp->SetAlphaTesting( false );
	//	n->AttachProperty( alphaProp );
	//}
	mAlphaForced = (alphaProp != 0);

	if( mAlphaForced )
	{
		mAlphaBlended = true;
	}
	else
	{
		mAlphaBlended = false;

		/// 알파 속성이 없으면 생성
		alphaProp = NiNew NiAlphaProperty;
		alphaProp->SetAlphaBlending( false );
		alphaProp->SetAlphaTesting( false );
		n->AttachProperty( alphaProp );

		bool blendEnabled = alphaProp->GetAlphaBlending();
		bool testEnabled = alphaProp->GetAlphaTesting();
		NiAlphaProperty::TestFunction testFunc = alphaProp->GetTestMode();
		unsigned char testRef = alphaProp->GetTestRef();

		mAlphaDataList.PushBack( new cAlphaData( blendEnabled, testEnabled, testFunc, testRef, alphaProp ) );
	}

	/// 속성들을 수집
	bool blendEnabled = alphaProp->GetAlphaBlending();
	bool testEnabled = alphaProp->GetAlphaTesting();
	NiAlphaProperty::TestFunction testFunc = alphaProp->GetTestMode();
	unsigned char testRef = alphaProp->GetTestRef();
	mAlphaDataList.PushBack( new cAlphaData( blendEnabled, testEnabled, testFunc, testRef, alphaProp ) );

	CollectProperties( n );

	/// picking용 데이터를 추출 한다.
	CollectPickData( mNiObject );

	mPick.SetPickType( NiPick::FIND_FIRST );
	mPick.SetIntersectType( NiPick::TRIANGLE_INTERSECT );
	mPick.SetSortType( NiPick::NO_SORT );
	mPick.SetFrontOnly( false );

	/// 조명을 부착
//	n->AttachEffect( SCENEMAN->GetAmbientLight() );
//	n->AttachEffect( SCENEMAN->GetDirLight() );

	/// 안개 속성을 부착
	//n->AttachProperty( SCENEMAN->GetFogProperty() );

	/// 갱신
	bool selectiveUpdate = true;
	bool rigid = false;
	n->SetSelectiveUpdateFlags( selectiveUpdate, true, rigid );

	n->UpdateProperties();
	n->UpdateEffects();
	n->Update( 0.0f );
	n->UpdateNodeBound();

	mRadius = cSceneNode::GetRadius();

//	mRadius = n->GetWorldBound().GetRadius();

	///// 정보표시를 위한 객체 생성
	//if( param.mCreateNameCard )
	//{
	//	mpNameCard = CreateNameCard( mpGameObject );
	//	assert(mpNameCard);
	//}

	return true;
}

void cDynamicSceneNode::SetTargetAnimation( unsigned int code )
{
	if( mTargetAnimation != code )
	{
		if( mActorManager->SetTargetAnimation( code ) == false )
			return;

		mTargetAnimation = code;
	}
}

bool cDynamicSceneNode::UpdateTargetAnimation( unsigned int code )
{
	if( mTargetAnimation != code )
	{
		if( mActorManager->SetTargetAnimation( code ) == false )
			return false;

		mTargetAnimation = code;
		return true;;
	}

	return mActorManager->UpdateTargetAnimation( code );
}

/* ------------------------------------------------------------------
* 함수이름 :	ChangeParts( unsigned int index, NiNode* pObj )
* 목    적 :	파트를 교체하는데 사용
* 주의사항 :	본LOD가 완전히 테스트된 상태는 아니다. (좀더 수정요!!)
*				스킨과 본 정보 갱신에 특히 유의 해야한다.
* ------------------------------------------------------------------ */
bool cDynamicSceneNode::ChangeParts( unsigned int partsIdx, NiNode* pNode )
{
	/// 1. 기존 파트에 대한 정보를 수집한다.
	NiAVObject* pOldNode = mActorManager->GetParts( partsIdx );
	if( !pOldNode )
	{
		/// 임의 제거했을 경우 Keep된 정보가 있는지 체크한다.
		pOldNode = mActorManager->GetKeepParts( partsIdx );
		if( !pOldNode )
		{
			assert(0);
			return false;
		}
	}

	/// Geom객체를 획득한다.
	NiTriBasedGeom* pOldGeom = 0;
	if( NiIsKindOf(NiTriBasedGeom, pOldNode) )
	{
		pOldGeom = (NiTriBasedGeom*)pOldNode;
	}
	else
	{
		//pOldGeom = (NiTriBasedGeom*)GetGeom( pOldNode );
		pOldGeom = (NiTriBasedGeom*)GetGeom( (NiNode*)pOldNode );
	}

	if( !pOldGeom )
	{
		assert(0);
		return false;
	}

	/// 스킨정보 획득
	NiSkinInstance* pOldSkinInst = ((NiGeometry*)pOldGeom)->GetSkinInstance();
	assert(pOldSkinInst);
	if( !pOldSkinInst )
	{
		return false;
	}
	NiSkinData* pOldSkinData = pOldSkinInst->GetSkinData();
	assert(pOldSkinData);
	if( !pOldSkinData )
	{
		return false;
	}

	/// 2. 교체 되어질 파트에 대한 정보 수집
	const char* partsName = 0;
	if( mPartsNameMap.GetAt( partsIdx, partsName) == false )
	{
		assert(0);
		return false;
	}

	pNode->DetachAllEffects();

	/// 교체 객체의 Geom객체 정보
	NiTriBasedGeom* pGeom = (NiTriBasedGeom*)GetGeom( pNode );
	assert(pGeom);
	if( !pGeom )
	{
		return false;
	}

	/// 교체 객체의 스킨정보
	NiSkinInstance* pSkinInst = pGeom->GetSkinInstance();
	assert(pSkinInst);
	if( !pSkinInst )
	{
		return false;
	}
	NiSkinData* pSkinData = pSkinInst->GetSkinData();
	assert(pSkinData);
	if( !pSkinData )
	{
		return false;
	}

	/// 교체되어질 Bone정보
	NiAVObject*const* ppBones = pSkinInst->GetBones();
	assert(ppBones);
	if( !ppBones )
	{
		return false;
	}
	NiSkinData::BoneData* pBoneData = pSkinData->GetBoneData();
	assert(pBoneData);
	if( !pBoneData )
	{
		return false;
	}

	/// 새로운 모델의 Bone정보를 현재 Bone Hierachy의 Bone Node들로 교체한다.
	/// 하나의 본셋에서 분할한 것이므로 본은 기존것을 공유한다.
	/// 분할 객체 자체도 독립객체로 출력이 가능하도록 본 정보를 갖는다.
	/// 주의!! 이 본정보는 모두 같지만, 참조 위치를 기존 포인터로 변경해야 한다.
	/// (스킨정보가 참조하는 메모리 주소로의 변경이다.)
	unsigned int i;
	NiAVObject *pBone = 0;
	NiAVObject *pNewBone = 0;
	NiTPrimitiveSet<NiAVObject*> newBones;
	NiTPrimitiveSet<sBoneDataObj*> newBoneDataObjs;
	sBoneDataObj* pBoneDataObj = 0;

	NiAVObject* pRoot= mActorManager->GetNifRoot();
	for( i=0; i<pSkinData->GetBoneCount(); ++i )
	{
		pBone = ppBones[i];
		if( !pBone )
		{
			continue;
		}

		pNewBone = (NiAVObject*)pRoot->GetObjectByName( pBone->GetName() );
		if( !pNewBone )
		{
			continue;
		}

		newBones.Add( pNewBone );
		pBoneDataObj = NiNew sBoneDataObj;
		pBoneDataObj->pBoneData = &pBoneData[i];
		for( unsigned int k = 0; k < pBoneData[i].m_usVerts; ++k )
		{
			pBoneDataObj->BoneVertData.Add( &pBoneData[i].m_pkBoneVertData[k] );
		}
		newBoneDataObjs.Add( pBoneDataObj );
	}

	/// Create the new arrays.
	unsigned int numNewBones = newBones.GetSize();
	NiAVObject** ppNewBones = NiAlloc(NiAVObject*, numNewBones);
	NiSkinData::BoneData* pNewBoneData = NiNew NiSkinData::BoneData[numNewBones];

	/// Fill the new arrays.
	for( i = 0; i<numNewBones; ++i )
	{
		ppNewBones[i] = newBones.GetAt( i );

		pBoneDataObj = newBoneDataObjs.GetAt( i );
		pNewBoneData[i].m_kBound = pBoneDataObj->pBoneData->m_kBound;
		pNewBoneData[i].m_kSkinToBone = pBoneDataObj->pBoneData->m_kSkinToBone;

		/// 구동상의 문제는 아니지만, 별도의 체크를 요한다!!
		assert( pBoneDataObj->BoneVertData.GetSize() < (unsigned int) (unsigned short) ~0 );
		pNewBoneData[i].m_usVerts = (unsigned short)pBoneDataObj->BoneVertData.GetSize();
		pNewBoneData[i].m_pkBoneVertData = NiNew NiSkinData::BoneVertData[pNewBoneData[i].m_usVerts];

		for ( unsigned short k = 0; k<pNewBoneData[i].m_usVerts; ++k )
		{
			pNewBoneData[i].m_pkBoneVertData[k].m_fWeight = pBoneDataObj->BoneVertData.GetAt( k )->m_fWeight;
			pNewBoneData[i].m_pkBoneVertData[k].m_usVert = pBoneDataObj->BoneVertData.GetAt( k )->m_usVert;
		}
	}

	/// Create the new NiSkinData and NiSkinInstance objects.
	NiSkinData* pNewSkinData = NiNew NiSkinData( numNewBones, pNewBoneData, 
		pSkinData->GetRootParentToSkin(), pGeom->GetVertices() );
	pNewSkinData->SortAndMergeBoneData();
	NiSkinInstance* pNewSkinInst = NiNew NiSkinInstance( pNewSkinData,
		pOldSkinInst->GetRootParent(), ppNewBones );

	/// Delete allocated memory.
	for( i = 0; i< newBoneDataObjs.GetSize(); ++i )
	{
		NiDelete newBoneDataObjs.GetAt( i );
	}

	/// Clone한 Object에 있던 Bone Root정보를 삭제한다.
//	pNode->DetachChild( pNode->GetObjectByName("Bip01") );
	pNode->DetachChild( pNode->GetObjectByName("foot_dummy") );

	/// Skin Instance 갱신
	pGeom->SetSkinInstance( pNewSkinInst );

	/// 새로운 객체로 바꾼다.
	mActorManager->ChangePartsObject( partsIdx, pNode );

	/// Bone LOD 정보의 갱신..
	NiBoneLODController* pOldCtrl = 0;
	pOldCtrl = mActorManager->GetBoneLODController();
	if( pOldCtrl )
	{
		int curLOD = pOldCtrl->GetBoneLOD();
		pOldCtrl->ProcessScene( mActorManager->GetNifRoot() );
		pOldCtrl->ReplaceSkin( pOldGeom, pGeom );

		mActorManager->RefreshControllerManager();

		NiBoneLODController* pNewCtrl = mActorManager->GetBoneLODController();
		pNewCtrl->SetBoneLOD( curLOD );
	}

	/// 기하 정보를 갱신
	RecollectGeoms();

	if( partsIdx == 1 /* ePART_FACE */ )
	{
		NiGeomMorpherController* n = mActorManager->GetMorpherController();
		if( mMorpherController != n )
		{
			mMorpherController = n;
			if( mMorpherController )
			{
				mMorpherController->SetAlwaysUpdate( false );

				mMorpherController->Start();
				mMorpherController->SetManagerControlled( false );
			}
		}
	}
	return true;
}

bool cDynamicSceneNode::ChangePartsTexture( unsigned int partsIdx, NiTexture* pTex )
{
	/// 1. 기존 파트에 대한 정보를 수집한다.
	NiAVObject* pNode = mActorManager->GetParts( partsIdx );
	if( pNode && pTex )
	{
		NiAVObject* pGeom = GetGeom( pNode );
		if( pGeom )
		{
			NiTexturingProperty* pProperty = (NiTexturingProperty*)pGeom->GetProperty( NiProperty::TEXTURING );
			if( pProperty )
			{
				pProperty->SetBaseTexture( pTex );
				pGeom->UpdateProperties();
				return true;
			}
		}
	}

	return false;
}

/* ------------------------------------------------------------------
* 함수이름 :	KeepParts( unsigned int index )
* 목    적 :	상하의 한벌과 같이 두개의 파트를 차지하는 객체를 위해서 만든다.
* 주의사항 :	
* ------------------------------------------------------------------ */
void cDynamicSceneNode::KeepParts( unsigned int partIdx )
{
	/// 루트 노드 상에서는 제거되고
	/// 별도의 공간에 보관하게 된다.
	mActorManager->KeepPartsObject( partIdx );
}

/* ------------------------------------------------------------------
* 함수이름 :	LinkObject( unsigned int index, NiNode* pObj )
* 목    적 :	무기를 특정더미에 링크 시킨다.
* 주의사항 :	리턴되는 인덱스를 활용해서 제거되므로 꼭 받아서 보관할것..
* ------------------------------------------------------------------ */
unsigned int cDynamicSceneNode::LinkObject( unsigned int dummyIdx, NiNode* pNode )
{
	/// Parts Name점검.. 잘못된 인덱스 체크
	const char* partsName = 0;
	if( mPartsNameMap.GetAt( dummyIdx, partsName ) == false )
	{
		assert(0);
		return UINT_MAX;
	}

	pNode->DetachAllEffects();

	LinkDataInit( pNode );
	NiNode* pobj = pNode;
	if( pobj == 0 )
	{
		assert(0);
		return UINT_MAX;
	}

	/// 링크시킬 위치 객체 획득..
	NiNode* pDummy = (NiNode*)mActorManager->GetNifRoot()->GetObjectByName( partsName );
	assert(pDummy);
	if( pDummy == 0 )
	{
		assert(0);
		return UINT_MAX;
	}

	/// 원본객체 복사본 생성..
	NiCloningProcess cloning;
	cloning.m_eCopyType = NiObjectNET::COPY_EXACT;
	NiNode* pNewObj = NiDynamicCast( NiNode, pobj->Clone( cloning ) );
	assert(pNewObj);
	if( pNewObj == 0 )
	{
		assert(0);
		return UINT_MAX;
	}

	pNewObj->SetTranslate( 0.0f, 0.0f, 0.0f );

	/// 실제 링크..
	pDummy->AttachChild( pNewObj );

	/// 갱신
	pNewObj->Update(0.0f);
	pNewObj->UpdateProperties();
	pNewObj->UpdateEffects();

	NiNode* pRoot = (NiNode*)(NiAVObject*)mNiObject;
	pRoot->UpdateNodeBound();

	/// 링크 정보 입력..
	sLinkInfo* pkInfo = NiNew sLinkInfo;
	pkInfo->DummyIndex = dummyIdx;
	pkInfo->pObject = pNewObj;

	unsigned int linkIdx = mLinkInfo->Add( pkInfo );

	/// 기하 정보를 갱신
	RecollectGeoms();

	return linkIdx;
}

/* ------------------------------------------------------------------
* 함수이름 :	UnLinkObject( unsigned int index )
* 목    적 :	현재 링크된 객체를 제거한다.
* 주의사항 :	
* ------------------------------------------------------------------ */
void cDynamicSceneNode::UnLinkObject( unsigned int linkIdx )
{
	if( linkIdx == UINT_MAX )
	{
		return;
	}

	sLinkInfo* pInfo = mLinkInfo->GetAt( linkIdx );
	if( pInfo == 0 )
	{
		assert(0);
		return;
	}

	const char* partsName = 0;
	if( mPartsNameMap.GetAt( pInfo->DummyIndex, partsName ) == false )
	{
		assert(0);
		return;
	}

	/// 링크된 더미를 찾는다.
	NiNode* pDummy = (NiNode*)mActorManager->GetNifRoot()->GetObjectByName( partsName );
	if( pDummy == 0 )
	{
		assert(0);
		return;
	}

	/// 더미
//	pDummy->GetParent()->DetachChild( pInfo->pObject );
	pDummy->DetachChild( pInfo->pObject );
	mLinkInfo->RemoveAt( linkIdx );
	SAFE_NIDELETE(pInfo);

	NiNode* pRoot = (NiNode*)(NiAVObject*)mNiObject;
	pDummy->Update(0.0f, false);
	pRoot->UpdateNodeBound();

	/// 기하 정보를 갱신
	RecollectGeoms();
}

NiAVObject*	cDynamicSceneNode::GetLinkObject( unsigned int linkId )
{
	if( linkId == UINT_MAX ) return 0;

	sLinkInfo* pInfo = mLinkInfo->GetAt( linkId );
	if( pInfo )
	{
		return pInfo->pObject;
	}
	return 0;
}

cEffectSceneNode* cDynamicSceneNode::LinkEffect( unsigned int dummyIdx, const char* fileName, NiTransform* trans, bool bLoop, bool bFollow )
{
	NiNode* pDummy = 0;
	/// Parts Name점검.. 잘못된 인덱스 체크
	const char* partsName = 0;
	if( mPartsNameMap.GetAt( dummyIdx, partsName ) )
	{
		/// 링크시킬 위치 객체 획득..
		pDummy = (NiNode*)mActorManager->GetNifRoot()->GetObjectByName( partsName );
		if( pDummy == 0 )
			pDummy = SCENEMAN->GetSceneDummy();
	}
	else
	{
		pDummy = SCENEMAN->GetSceneDummy();
	}

	cEffectSceneNodeParam param;
	param.mpFollowSceneNode = this;
	param.mpDummy = pDummy;
	param.mPathName = fileName;	

	if( trans != 0 )
	{
		param.mTranslate = trans->m_Translate;
		param.mRotate = trans->m_Rotate;
		param.mScale = trans->m_fScale;
	}
	param.mFollowing = bFollow;
	param.mLooping = bLoop;

	cEffectSceneNode* pnode = SCENEMAN->CreateEffect( param );
	if( pnode )
		mEffectList.AddTail(pnode);

	NiNode* pRoot = (NiNode*)(NiAVObject*)mNiObject;
	pRoot->UpdateNodeBound();

	return pnode;
}

/// 주의사항 :
void cDynamicSceneNode::UnLinkEffect( cEffectSceneNode* pnode )
{
	assert(pnode);
	if( pnode == 0 )
		return;

	SCENEMAN->AddDeleteEffectNode( pnode );
	mEffectList.Remove( pnode );
}

bool cDynamicSceneNode::IsLinkedEffect( cEffectSceneNode* pnode )
{
	return (!mEffectList.FindPos( pnode ))? false:true;
}

cSoundSceneNode* cDynamicSceneNode::LinkSound( unsigned long soundIdx, bool bLoop )
{
	/// 참조할 위치 객체 획득..
	NiNode* pDummy = (NiNode*)mActorManager->GetNifRoot()->GetObjectByName( "body_dummy");
	if( pDummy == NULL )
	{
		return NULL;
	}

	sSoundInfo* pInfo = SOUNDSYS->GetSoundInfo( soundIdx );
	if( pInfo == 0 )
		return 0;

	cSoundSceneNodeParam param;
	if( pInfo->mRadius <= 0.0f )
	{
		param.mRadius = 100.0f;
	}
	else
	{
		param.mRadius = pInfo->mRadius;
	}
	param.mLoopCount = bLoop? 0:1;
	param.mVolumeRatio = pInfo->mVolume;

	/// 기본 속성 인자들을 설정
	param.mPathName = pInfo->mFileName;
	param.mTranslate = pDummy->GetWorldTranslate();
	param.mManaged = true;
	param.mpOwnerSceneNode = this;

	cSoundSceneNode* pnode = SCENEMAN->CreateEffectSound( param );
	if( pnode )
		mSoundList.AddTail(pnode);

	return pnode;
}

void cDynamicSceneNode::UnLinkSound( cSoundSceneNode* pnode )
{
	assert(pnode);
	if( pnode == 0 )
		return;

	SCENEMAN->AddDeleteSoundNode( pnode );
	mSoundList.Remove( pnode );
}

bool cDynamicSceneNode::IsLinkedSound( cSoundSceneNode* pnode )
{
	return (!mSoundList.FindPos( pnode ))? false:true;
}

///
void cDynamicSceneNode::StartMorpher()
{
	mStopMorpherProcess = false;
}

///
void cDynamicSceneNode::StopMorpher()
{
	mStopMorpherProcess = true;
}

///
//void cDynamicSceneNode::CreateShadow()
//{
//}

///
void cDynamicSceneNode::AddToVisibleArray( NiVisibleArray* psolidArray, NiVisibleArray* pskinedArray, NiVisibleArray* palphaArray )
{
	cSceneNode::AddToVisibleArray( psolidArray, pskinedArray, palphaArray );

	NiTListIterator pos = mRibbonList.GetHeadPos();
	while( pos )
	{
		sTrailInfo* p = mRibbonList.GetNext(pos);
		cRibbonGeometry* geom = p->mRibbonGeom;
		if( geom && geom->GetNumVerts() >= 4 )
		{
			geom->SetCenter( GetCenter() );
			palphaArray->Add( *geom );
		}
	}
}

/////
//void cDynamicSceneNode::AddDepthPlaneObject() const
//{
//	/// 말풍선 보이기 여부
//	if( mpChatBubble && mpChatBubble->IsActive() )
//	{
//		/// 말풍선 등록
//		SCENEMAN->AddPlane( mpChatBubble );
//		return;
//	}
//
//	if( mHead == 0 )
//	{
////		assert( 0 );
//		return;
//	}
//
//	NiPoint3 headPos = mHead->GetWorldTranslate();
//	headPos.z += mHeadDist;
//
//	if( mpNameCard )
//	{
//		mpNameCard->OnUpdate( CAMERAMAN->GetCurrentNi(), headPos );
//
//		SCENEMAN->AddDepthPlane( mpNameCard );
//	}
//}
//
/////
//void cDynamicSceneNode::ActiveChatBubble( LPTSTR msg, unsigned long color )
//{
//	if( mpChatBubble )
//		mpChatBubble->Active( msg, color );
//}
//
//void cDynamicSceneNode::UpdateNameCardTextValue()
//{
//	if( mpNameCard )
//		mpNameCard->ChangeTextValue();
//}
//
//void cDynamicSceneNode::UpdateNameCardGaugeValue()
//{
//	if( mpNameCard )
//		mpNameCard->ChangeGaugeValue();
//}

void cDynamicSceneNode::CollectPickData( NiAVObject* pobj )
{
	if( pobj->GetName() == "PickObj" )
	{
		mPickDataList.PushBack( pobj );

		return;
	}

	if( NiIsKindOf(NiNode, pobj) )
	{
		NiNode* node = (NiNode*)pobj;
		for( unsigned int i = 0, end = node->GetArrayCount(); i < end; ++i )
		{
			NiAVObject* child = node->GetAt(i);
			if( child )
				CollectPickData( child );
		}
	}
}

bool cDynamicSceneNode::Pick( const NiPoint3& origin, const NiPoint3& dir )
{
	if( mPickDataList.IsEmpty() )
	{
		return cSceneNode::Pick( origin, dir );
	}

	NiAVObject* pObj = 0;
	cPickDataList::cConstIterator i = mPickDataList.Begin();
	cPickDataList::cConstIterator end = mPickDataList.End();
	for( ; i != end; ++i )
	{
		pObj = (NiAVObject*)*i;
		mPick.SetTarget( pObj );

		if( mPick.PickObjects( origin, dir ) )
		{
			NiPick::Record* record = mPick.GetResults().GetAt(0);
			if( record )
			{
				mPickPos = record->GetIntersection();
				mPickDistance = record->GetDistance();
			}
			return true;
		}
	}
	return false;
}

/// 주의 : scalefactor 값으로 
void cDynamicSceneNode::UpdateAniScaleFactor( float factor )
{
	if( factor <= 0.0f )
		factor = 0.1f;
	if( factor > 2.0f )
		factor = 2.0f;
	mScaleFactor = factor;
}

NiAVObject* cDynamicSceneNode::GetPartObject( unsigned int index ) const
{
	const char* partsName = 0;
	if( mPartsNameMap.GetAt( index, partsName ) == false )
	{
		return 0;
	}

	/// 링크시킬 위치 객체 획득..
	return mActorManager->GetNifRoot()->GetObjectByName( partsName );
}

void cDynamicSceneNode::LinkDataInit( NiNode* node )
{
	if( node == 0 )
		return;

	NiAVObject* child = 0;

	for( unsigned int i = 0, iend = node->GetArrayCount(); i < iend; ++i )
	{
		child = node->GetAt( i );
		if( child )
		{
			if( NiIsKindOf(NiGeometry, child) )
			{
				NiGeometry* geom = (NiGeometry*)child;
				NiTransform trans;
				trans.MakeIdentity();

				geom->SetLocalTransform( trans );
				geom->SetWorldTranslate( NiPoint3::ZERO );
				geom->SetWorldRotate( NiMatrix3::IDENTITY );
				geom->SetWorldScale( trans.m_fScale  );

				NiNode* parent = geom->GetParent();
				if( parent )
				{
					parent->SetLocalTransform( trans );
					parent->SetWorldTranslate( NiPoint3::ZERO );
					parent->SetWorldRotate( NiMatrix3::IDENTITY );
					parent->SetWorldScale( trans.m_fScale  );
				}
			}
			else
			{
				LinkDataInit( NiDynamicCast(NiNode, child) );
			}
		}
	}
}

void cDynamicSceneNode::GotoLastFrame()
{
	NiControllerSequence* p = mActorManager->GetSequence( mActorManager->GetTargetAnimation() );
	if( p )
	{
		float time = 0.0f;
		time = p->GetEndKeyTime();

		if( time != NiControllerSequence::INVALID_TIME )
			mActorManager->Update( -time );
	}
}

float cDynamicSceneNode::GetRadius() const
{
	return mRadius;
}

sTrailInfo*	 cDynamicSceneNode::LinkTrailEffect(cString str, float lifeTime, unsigned int link1, unsigned int link2, float /*lenPer*/, float /*factor*/ )
{
	LPDIRECT3DTEXTURE9 tex = RESOURCEMAN->LoadD3DTexture( str );
	if( tex == 0 )
	{
		assert(0);
		return 0;
	}

	cRibbonGeometry* geom = NiNew cRibbonGeometry;
	if( geom->Init( 100, 0.01f, 0.02f, 1.0f ) == false)
	{
		NiDelete geom;
		return 0;
	}

	geom->SetTexture( tex );

	sTrailInfo* info = new sTrailInfo;
	info->mRibbonGeom = geom;
	info->mLifeTime = lifeTime;
	info->mStartLink = link1;
	info->mEndLink = link2;
	info->mLoop = (lifeTime == 0);

	mRibbonList.AddTail( info );

	return info;
}

void cDynamicSceneNode::UnLinkTrailEffect( sTrailInfo* pInfo )
{
	if( pInfo == 0 )
		return;

	NiTListIterator pos = mRibbonList.FindPos( pInfo );
	if( pos )
	{
		cRibbonGeometry* p = pInfo->mRibbonGeom;
		if( p )
			p->SetPushEnabled( false );
		pInfo->mLifeTime = 0;
	}
}

