519 lines
14 KiB
C++
519 lines
14 KiB
C++
#include "StdAfx.h"
|
|
#include "EtAniObject.h"
|
|
#include "EtSaveMat.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
|
|
#endif
|
|
|
|
using namespace EternityEngine;
|
|
|
|
CSyncLock CEtAniObject::s_CalcAniLock;
|
|
|
|
CEtAniObject::CEtAniObject(void)
|
|
{
|
|
m_bShadowCast = true;
|
|
m_bShadowReceive = false;
|
|
m_nCalcPositionFlag = CALC_POSITION_Y;
|
|
m_nSaveMatIndex = -1;
|
|
EnableLightMapInfluence( true );
|
|
m_bSkipPhysics = false;
|
|
m_bForceSkipSimulateAni = false;
|
|
|
|
m_nExtraAniIndex = -1;
|
|
m_fExtraAniFrame = 0.0f;
|
|
|
|
m_bAniObject = true;
|
|
}
|
|
|
|
CEtAniObject::~CEtAniObject(void)
|
|
{
|
|
SAFE_RELEASE_SPTR( m_hAni );
|
|
}
|
|
|
|
int CEtAniObject::Initialize( EtSkinHandle hSkin, EtAniHandle hAni )
|
|
{
|
|
bool bSkinReady, bAniReady;
|
|
|
|
m_hSkin = hSkin;
|
|
m_hAni = hAni;
|
|
m_pSkinInstance = new CEtSkinInstance();
|
|
|
|
bSkinReady = !m_hSkin->AddCallback( this );
|
|
bAniReady = false;
|
|
|
|
if( m_hAni )
|
|
{
|
|
bAniReady = !m_hAni->AddCallback( this );
|
|
if( ( bSkinReady ) && ( bAniReady ) )
|
|
{
|
|
if( m_hSkin->GetMeshHandle() ) {
|
|
m_hSkin->GetMeshHandle()->LinkToAni( m_hAni );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bSkinReady )
|
|
{
|
|
CommonInitialize();
|
|
}
|
|
|
|
SetCollisionGroup( COLLISION_GROUP_DYNAMIC( 1 ) );
|
|
SetTargetCollisionGroup( COLLISION_GROUP_STATIC( 1 ) );
|
|
|
|
return ET_OK;
|
|
}
|
|
|
|
EtMatrix *CEtAniObject::GetBoneTransMat( int nBoneIndex )
|
|
{
|
|
EtMatrix BoneWorldMat;
|
|
static EtMatrix BoneMat;
|
|
|
|
nBoneIndex = m_hAni->ConvertBoneIndex( nBoneIndex );
|
|
if( !GetEtSaveMat()->IsValidIndex( m_nSaveMatIndex ) )
|
|
{
|
|
CalcAni();
|
|
}
|
|
|
|
std::vector< EtMatrix > &vecInvWordlList = GetMesh()->GetInvWorldMatList();
|
|
// 임시 siva - 에서 에러처리로 바꿉니다.
|
|
if( nBoneIndex < 0 || nBoneIndex >= (int)vecInvWordlList.size() ) {
|
|
_ASSERT(0&&"본 인덱스가 잘못됫습니다.");
|
|
}
|
|
else
|
|
{
|
|
EtMatrixInverse( &BoneWorldMat, NULL, &vecInvWordlList[ nBoneIndex ] );
|
|
|
|
if( GetEtSaveMat()->IsValidIndex( m_nSaveMatIndex + nBoneIndex ) )
|
|
{
|
|
return EtMatrixMultiply( &BoneMat, &BoneWorldMat, GetEtSaveMat()->GetMatrix( m_nSaveMatIndex + nBoneIndex ) );
|
|
}
|
|
}
|
|
EtMatrixIdentity( &BoneMat );
|
|
|
|
return &BoneMat;
|
|
}
|
|
|
|
EtMatrix *CEtAniObject::GetDummyTransMat( int nDummyIndex )
|
|
{
|
|
EtMeshHandle hMesh = m_hSkin->GetMeshHandle();
|
|
if( hMesh )
|
|
{
|
|
std::vector< EtMatrix > &vecDummyMat = hMesh->GetDummyMatrixList();
|
|
std::vector< std::string > &vecParentName = hMesh->GetDummyParentNameList();
|
|
if( nDummyIndex < ( int )vecDummyMat.size() )
|
|
{
|
|
static EtMatrix ReturnMat;
|
|
int nParentIndex = -1;
|
|
if( m_hAni )
|
|
{
|
|
nParentIndex = m_hAni->GetBoneIndex( vecParentName[ nDummyIndex ].c_str() );
|
|
}
|
|
if( nParentIndex == -1 )
|
|
{
|
|
ReturnMat = vecDummyMat[ nDummyIndex ];
|
|
}
|
|
else
|
|
{
|
|
EtMatrixMultiply( &ReturnMat, &vecDummyMat[ nDummyIndex ], GetBoneTransMat( nParentIndex ) );
|
|
}
|
|
return &ReturnMat;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EtMatrix *CEtAniObject::GetBoneMat( int nBoneIndex )
|
|
{
|
|
nBoneIndex = m_hAni->ConvertBoneIndex( nBoneIndex );
|
|
if( !GetEtSaveMat()->IsValidIndex( m_nSaveMatIndex ) )
|
|
{
|
|
CalcAni();
|
|
}
|
|
|
|
if( GetEtSaveMat()->IsValidIndex( m_nSaveMatIndex ) )
|
|
{
|
|
return GetEtSaveMat()->GetMatrix( m_nSaveMatIndex + nBoneIndex );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CEtAniObject::ResetAniFrame()
|
|
{
|
|
m_vecAniInfo.clear();
|
|
m_vecBlendAniInfo.clear();
|
|
m_vecDisableBone.clear();
|
|
m_vecBoneRotation.clear();
|
|
m_vecBoneScale.clear();
|
|
m_nSaveMatIndex = -1;
|
|
}
|
|
|
|
void CEtAniObject::SetAniFrame( int nAni, float fFrame, int nBoneIndex )
|
|
{
|
|
ASSERT( fFrame >= 0.0f );
|
|
|
|
if( nBoneIndex == 0 ) // 셋팅하는 본이 루트본일 경우는 모든 애니메이션을 초기화 시켜준다.
|
|
{
|
|
ResetAniFrame();
|
|
}
|
|
|
|
SAniInfo AniInfo;
|
|
|
|
if( nAni == -1 ) return;
|
|
AniInfo.BoneAniInfo.nAni = nAni;
|
|
AniInfo.BoneAniInfo.fFrame = fFrame;
|
|
AniInfo.nBoneIndex = nBoneIndex;
|
|
m_vecAniInfo.push_back( AniInfo );
|
|
}
|
|
|
|
void CEtAniObject::BlendAniFrame( int nBlendAni, float fBlendFrame, float fBlendWeight, int nBlendBoneIndex )
|
|
{
|
|
SAniInfo AniInfo;
|
|
|
|
if( nBlendAni == -1 ) return;
|
|
if( fBlendWeight < 0.0f || fBlendWeight > 10.0f )
|
|
{
|
|
ASSERT( 0 && "잘못된 블렌드 웨이트 값" );
|
|
return;
|
|
}
|
|
AniInfo.BoneAniInfo.nAni = nBlendAni;
|
|
AniInfo.BoneAniInfo.fFrame = fBlendFrame;
|
|
AniInfo.BoneAniInfo.fWeight = fBlendWeight;
|
|
AniInfo.nBoneIndex = nBlendBoneIndex;
|
|
m_vecBlendAniInfo.push_back( AniInfo );
|
|
m_vecDisableBone.resize( m_vecBlendAniInfo.size() );
|
|
}
|
|
|
|
void CEtAniObject::BlendAniFrame( int nBlendAni, float fBlendFrame, float fBlendWeight, int nBlendBoneIndex, std::vector< int > &vecDisableBone )
|
|
{
|
|
BlendAniFrame( nBlendAni, fBlendFrame, fBlendWeight, nBlendBoneIndex );
|
|
int nBlendCount = ( int )m_vecBlendAniInfo.size();
|
|
if( nBlendCount == 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( !vecDisableBone.empty() )
|
|
{
|
|
m_vecDisableBone[ nBlendCount - 1 ].resize( vecDisableBone.size() );
|
|
std::copy( vecDisableBone.begin(), vecDisableBone.end(), m_vecDisableBone[ nBlendCount - 1 ].begin() );
|
|
}
|
|
}
|
|
|
|
void CEtAniObject::BlendAniFrame( int nBlendAni, float fBlendFrame, float fBlendWeight, int nBlendBoneIndex, int nDisableBoneIndex )
|
|
{
|
|
BlendAniFrame( nBlendAni, fBlendFrame, fBlendWeight, nBlendBoneIndex );
|
|
int nBlendCount = ( int )m_vecBlendAniInfo.size();
|
|
if( nBlendCount == 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( nDisableBoneIndex != -1 )
|
|
{
|
|
m_vecDisableBone[ nBlendCount - 1 ].insert( m_vecDisableBone[ nBlendCount - 1 ].begin(), nDisableBoneIndex );
|
|
}
|
|
// 미리 CalcAni()가 불려서 m_nSaveMatIndex가 Valid한 값이라도 중간에 BlendAni나 SetBoneRotation이 불리면
|
|
// m_nSaveMatIndex가 Invalid함을 표시한다 이렇게 하는 이유는 m_nSaveMatIndex를 -1로 바꿔줄 경우 애니메이션 계산 되면서
|
|
// 계속 메모리가 할당 될 가능성이 있다.
|
|
m_bValidSaveMatIndex = false;
|
|
}
|
|
|
|
void CEtAniObject::SetBoneRotation( int nBoneIndex, EtVector3 &BoneRotation )
|
|
{
|
|
SBoneRotationInfo BoneRotationInfo;
|
|
|
|
if( nBoneIndex == -1 ) {
|
|
ASSERT( 0 && "Rotate Bone Index -1" );
|
|
return;
|
|
}
|
|
BoneRotationInfo.nBoneIndex = nBoneIndex;
|
|
BoneRotationInfo.BoneRotation = BoneRotation;
|
|
m_vecBoneRotation.push_back( BoneRotationInfo );
|
|
// 미리 CalcAni()가 불려서 m_nSaveMatIndex가 Valid한 값이라도 중간에 BlendAni나 SetBoneRotation이 불리면
|
|
// m_nSaveMatIndex가 Invalid함을 표시한다 이렇게 하는 이유는 m_nSaveMatIndex를 -1로 바꿔줄 경우 애니메이션 계산 되면서
|
|
// 계속 메모리가 할당 될 가능성이 있다.
|
|
m_bValidSaveMatIndex = false;
|
|
}
|
|
|
|
void CEtAniObject::SetBoneScale( int nBoneIndex, float fScale )
|
|
{
|
|
SBoneScaleInfo BoneScaleInfo;
|
|
|
|
if( nBoneIndex == -1 ) {
|
|
ASSERT( 0 && "Scale Bone Index -1" );
|
|
return;
|
|
}
|
|
BoneScaleInfo.nBoneIndex = nBoneIndex;
|
|
BoneScaleInfo.fScale = fScale;
|
|
m_vecBoneScale.push_back( BoneScaleInfo );
|
|
m_bValidSaveMatIndex = false;
|
|
}
|
|
|
|
void CEtAniObject::ReBuildSkin( EtSkinHandle hSkin )
|
|
{
|
|
if( ! hSkin ) return;
|
|
|
|
SAFE_RELEASE_SPTR( m_hSkin );
|
|
SAFE_DELETE( m_pSkinInstance );
|
|
|
|
bool bSkinReady, bAniReady;
|
|
m_hSkin = hSkin;
|
|
m_pSkinInstance = new CEtSkinInstance();
|
|
|
|
bSkinReady = !m_hSkin->AddCallback( this );
|
|
bAniReady = false;
|
|
|
|
if( m_hAni )
|
|
{
|
|
bAniReady = !m_hAni->AddCallback( this );
|
|
if( ( bSkinReady ) && ( bAniReady ) )
|
|
{
|
|
if( m_hSkin->GetMeshHandle() ) {
|
|
m_hSkin->GetMeshHandle()->LinkToAni( m_hAni );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bSkinReady )
|
|
{
|
|
CommonInitialize();
|
|
}
|
|
}
|
|
|
|
void CEtAniObject::CalcAni()
|
|
{
|
|
// 다른 쓰레드에서 동시에 호출되는 경우 있다.. 막아야 한다..
|
|
ScopeLock< CSyncLock > Lock( s_CalcAniLock );
|
|
|
|
if( !m_hAni )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int i, j;
|
|
|
|
m_hAni->Reset();
|
|
m_hAni->SetCalcPositionFlag( m_nCalcPositionFlag );
|
|
if( m_nExtraAniIndex != -1 )
|
|
{
|
|
SBoneAniInfo BoneInfo;
|
|
BoneInfo.nAni = m_nExtraAniIndex;
|
|
BoneInfo.fFrame = m_fExtraAniFrame;
|
|
BoneInfo.fWeight = 0.0f;
|
|
m_hAni->SetAni( &BoneInfo, 0 );
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < ( int )m_vecAniInfo.size(); i++ )
|
|
{
|
|
m_hAni->SetAni( &m_vecAniInfo[ i ].BoneAniInfo, m_vecAniInfo[ i ].nBoneIndex );
|
|
}
|
|
for( i = 0; i < ( int )m_vecBlendAniInfo.size(); i++ )
|
|
{
|
|
for( j = 0; j < ( int )m_vecDisableBone[ i ].size(); j++ )
|
|
{
|
|
m_hAni->EnableBlend( m_vecDisableBone[ i ][ j ], false );
|
|
}
|
|
m_hAni->BlendAni( &m_vecBlendAniInfo[ i ].BoneAniInfo, m_vecBlendAniInfo[ i ].nBoneIndex );
|
|
for( j = 0; j < ( int )m_vecDisableBone[ i ].size(); j++ )
|
|
{
|
|
m_hAni->EnableBlend( m_vecDisableBone[ i ][ j ], true );
|
|
}
|
|
}
|
|
for( i = 0; i < ( int )m_vecBoneRotation.size(); i++ )
|
|
{
|
|
m_hAni->SetBoneRotation( m_vecBoneRotation[ i ].nBoneIndex, &m_vecBoneRotation[ i ].BoneRotation );
|
|
}
|
|
for( i = 0; i < ( int )m_vecBoneScale.size(); i++ )
|
|
{
|
|
m_hAni->SetBoneScale( m_vecBoneScale[ i ].nBoneIndex, m_vecBoneScale[ i ].fScale );
|
|
}
|
|
}
|
|
|
|
m_nSaveMatIndex = m_hAni->CalcAni( GetMesh()->GetInvWorldMatList() );
|
|
m_bValidSaveMatIndex = true;
|
|
}
|
|
|
|
void CEtAniObject::CreateSimulation( const char *szFileName )
|
|
{
|
|
std::map<std::string, int > boneIndexMap;
|
|
int nBoneCount = m_hAni->GetBoneCount();
|
|
for( int i = 0; i < nBoneCount; i++) {
|
|
boneIndexMap[ m_hAni->GetBone(i)->GetName() ] = i;
|
|
}
|
|
m_Spring.Create( szFileName, GetMesh()->GetInvWorldMatList(), boneIndexMap );
|
|
}
|
|
|
|
void CEtAniObject::SimulateAni()
|
|
{
|
|
// 애니 잘못 연결하면 아래 InvWorldMatList 얻어오는데서 덤프납니다.
|
|
if( m_hAni->GetBoneCount() > (int)(GetMesh()->GetInvWorldMatList().size()) )
|
|
return;
|
|
|
|
int i;
|
|
m_Spring.Simulate( m_WorldMat, GetMesh()->GetInvWorldMatList() );
|
|
|
|
int nBoneCount = m_hAni->GetBoneCount();
|
|
|
|
EtMatrix *pLockedMatrices = NULL;
|
|
m_nSaveMatIndex = GetEtSaveMat()->LockMatrix( nBoneCount, &pLockedMatrices);
|
|
for( i = 0; i < nBoneCount; i++) {
|
|
EtMatrix TransMat, VertexTransMat;
|
|
if( i == 0 ) {
|
|
EtMatrixIdentity( &VertexTransMat );
|
|
pLockedMatrices[ i ] = VertexTransMat;
|
|
}
|
|
else {
|
|
EtMatrix InvWorldMat;
|
|
EtMatrixInverse(&InvWorldMat, NULL, &m_WorldMat);
|
|
int nChildOffset = 0;
|
|
if( m_hAni->GetBone( i )->GetChildBoneCount() > 0 ) {
|
|
nChildOffset = m_hAni->GetBone( i )->GetChild( 0 )->GetBoneIndex() - m_hAni->GetBone( i )->GetBoneIndex();
|
|
}
|
|
else {
|
|
nChildOffset = m_hAni->GetBone( i )->GetParent()->GetBoneIndex() - m_hAni->GetBone( i )->GetBoneIndex();
|
|
}
|
|
TransMat = m_Spring.GetMatrix( i, nChildOffset );
|
|
EtMatrixMultiply( &TransMat, &TransMat, &InvWorldMat);
|
|
EtMatrixMultiply( &VertexTransMat, &GetMesh()->GetInvWorldMatList()[i], &TransMat );
|
|
pLockedMatrices[ i ] = VertexTransMat;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEtAniObject::ForceSkipSimulateAni( bool bForceSkip, bool bApplyChild )
|
|
{
|
|
if( m_Spring.IsEnable() )
|
|
{
|
|
m_bForceSkipSimulateAni = bForceSkip;
|
|
}
|
|
|
|
if( bApplyChild )
|
|
{
|
|
for( int i = 0; i < ( int )m_vecChild.size(); i++ )
|
|
{
|
|
if( m_vecChild[ i ]->IsAniObject() )
|
|
( ( EtAniObjectHandle )m_vecChild[ i ] )->ForceSkipSimulateAni( bForceSkip );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEtAniObject::InitRender( int nSaveMatIndex )
|
|
{
|
|
if( !IsShow() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// #25479
|
|
// 마을에서 /go 404하는거 말고, 게임들어간 상태에서 또 다시 /go 404 할때
|
|
// 게임태스크의 Process함수의 EtInterface::Process( fDelta ); 이 라인이 수행되면서 페이드 아웃이 끝났을때 다음번 스테이지 이니셜을 한다.
|
|
// 이때 몬스터도 생성하고 몬스터가 들고있는 프레일 오브젝트도 생성하는데...
|
|
// 그 아래 EternityEngine::RenderFrame( fDelta ); 라인이 프로세스호출하기전에 먼저 수행되는 바람에, 포지션값이 FLT_MAX 초기값 상태 그대로 있게 된다.
|
|
// 이 상태에서 행렬계산하는건 어차피 다음번 프로세스 호출되고 나서 다시 호출될때 재계산될테니 상관없는데,
|
|
// 시뮬레이션은 값을 누적해서 사용하기때문에 한번이라도 FLT_MAX값이 들어가있는 상태에서 계산하면 계속 이상한 값으로 남아있게 되버린다.
|
|
// 그래서 아래와 같이 FLT_MAX인지 판단해서 만약 Invalid하다면 SimulateAni를 호출하지 않도록 하겠다.
|
|
bool bInvalidPos = ( m_PrevWorldMat._11 == FLT_MAX );
|
|
|
|
// 유효한 최상위 부모의 월드행렬값이 이상하면 시뮬레이션 하지 않게 처리한다.
|
|
// 말타면서 쓰레드로 부모 설정하는것, 게이지Face에서 강제로 InitRender호출하는 것들때문에, 프로세스 돌기전에 들어오는 경우가 있는 거 같다.
|
|
if( m_hParent )
|
|
{
|
|
EtObjectHandle hParent = m_hParent;
|
|
while( 1 )
|
|
{
|
|
if( hParent->GetParent() )
|
|
hParent = hParent->GetParent();
|
|
else
|
|
break;
|
|
}
|
|
|
|
if( hParent->GetWorldMat()->_41 == FLT_MAX )
|
|
bInvalidPos = true;
|
|
}
|
|
|
|
int i;
|
|
|
|
if( m_hParent )
|
|
{
|
|
CalcParentBoneMat();
|
|
}
|
|
|
|
if( m_hAni )
|
|
{
|
|
if( m_Spring.IsEnable() ) {
|
|
if( m_bSkipPhysics ) {
|
|
CalcAni();
|
|
m_Spring.InputKeyframe( m_WorldMat, m_nSaveMatIndex );
|
|
}
|
|
else {
|
|
if( m_bForceSkipSimulateAni == false && bInvalidPos == false )
|
|
SimulateAni();
|
|
}
|
|
}
|
|
else {
|
|
if( m_hParent ) {
|
|
if( m_hAni->GetMyIndex() != m_hParent->GetAniFileIndex() )
|
|
{
|
|
nSaveMatIndex = -1;
|
|
}
|
|
else if( m_nLinkBoneIndex != -1 )
|
|
{
|
|
// SaveMatIndex에 대해서 간단히 설명하겠다.
|
|
// 주된 목적은 같은 ani파일을 사용하는 파츠들의 애니 계산을 여러번 반복해서 하지 않기 위해 같은 SaveMatIndex값을 넣어두고,
|
|
// (보통 동일 캐릭의 머리, 헤어, 머지오브젝트, 워려의 건틀렛 등이 하나의 부모아래 있는 자식이기때문에 같은 SaveMatIndex를 가지게 됩니다.)
|
|
// RenderStack::RenderBlock 함수에서 SetWorldMatArray 호출할때
|
|
// 해당 Object의 WorldMat와 설정된 SaveMat값을 곱해서 EtSaveMat::m_vecTransMat에 넣어두고 쓰게된다.
|
|
//
|
|
// 그런데 위 Ani파일만 검사하는거로는 문제가 발생할 수 있다.
|
|
// 만약 같은 SaveMatIndex를 사용하는 Object들 중에
|
|
// 누구는 m_nLinkBoneIndex가 -1인데 누구는 특정 본인덱스값이 들어있다면, 두 Object의 WorldMat가 달라질수밖에 없다.
|
|
// 이때 렌더링 루틴을 타면서 먼저 RenderBlock이 호출되는 곳에서 TransMat를 한번 구해놓고는
|
|
// 이 후 그 값을 공유해서 쓰기 때문에, 특정 오브젝트에 잘못 링크된것처럼 보이게 된다.
|
|
// 그래서 이렇게 LinkBoneIndex가 -1이 아니라면 부모로부터 전달받은 SaveMatIndex를 무효화시키고
|
|
// 스스로 계산한 m_nSaveMatIndex를 사용하도록 한다.
|
|
nSaveMatIndex = -1;
|
|
}
|
|
}
|
|
bool bMySaveMatInvalid = false;
|
|
if( ( !GetEtSaveMat()->IsValidIndex( m_nSaveMatIndex ) ) || ( !m_bValidSaveMatIndex ) )
|
|
{
|
|
bMySaveMatInvalid = true;
|
|
}
|
|
if( ( !GetEtSaveMat()->IsValidIndex( nSaveMatIndex ) ) && ( bMySaveMatInvalid ) )
|
|
{
|
|
CalcAni();
|
|
}
|
|
else if( !GetEtSaveMat()->IsValidIndex( m_nSaveMatIndex ) )
|
|
{
|
|
m_nSaveMatIndex = nSaveMatIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < ( int )m_vecChild.size(); i++ )
|
|
{
|
|
m_vecChild[ i ]->InitRender( m_nSaveMatIndex );
|
|
}
|
|
}
|
|
|
|
void CEtAniObject::OnLoadComplete( CBackgroundLoader *pLoader )
|
|
{
|
|
if( m_hAni )
|
|
{
|
|
if( ( m_hSkin->IsReady() ) && ( m_hAni->IsReady() ) )
|
|
{
|
|
if( m_hSkin->GetMeshHandle() ) {
|
|
m_hSkin->GetMeshHandle()->LinkToAni( m_hAni );
|
|
}
|
|
}
|
|
}
|
|
if( m_hSkin->IsReady() )
|
|
{
|
|
return CEtObject::OnLoadComplete( pLoader );
|
|
}
|
|
|
|
CBackgroundLoaderCallback::OnLoadComplete( pLoader );
|
|
}
|
|
|