//-----------------------------------------------------------------------------
// Purpose: 
// Input  : buf - 
//-----------------------------------------------------------------------------
void CBaseDemoAction::SaveKeysToBuffer( int depth, CUtlBuffer& buf )
{
	// All derived actions will need to do a BaseClass::SaveKeysToBuffer call
	g_bSaveChained = true;

	BufPrintf( depth, buf, "name \"%s\"\n", GetActionName() );
	if ( ActionHasTarget() )
	{
		BufPrintf( depth, buf, "target \"%s\"\n", GetActionTarget() );
	}
	switch ( GetTimingType() )
	{
	default:
	case ACTION_USES_NEITHER:
		break;
	case ACTION_USES_TICK:
		{
			BufPrintf( depth, buf, "starttick \"%i\"\n", GetStartTick() );
		}
		break;
	case ACTION_USES_TIME:
		{
			BufPrintf( depth, buf, "starttime \"%.3f\"\n", GetStartTime() );
		}
		break;
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : demoframe - 
//			demotime - 
//-----------------------------------------------------------------------------
bool CBaseDemoAction::Update( const DemoActionTimingContext& tc )
{
	// Already fired and done?
	if ( HasActionFinished() )
	{
		Assert( GetActionFired() );
		return false;
	}

	// Already fired, just waiting for finished tag
	if ( GetActionFired() )
	{
		return true;
	}
	
	// See if it's time to fire
	switch ( GetTimingType() )
	{
	default:
	case ACTION_USES_NEITHER:
		return false;
	case ACTION_USES_TICK:
		{
			if ( GetStartTick() >= tc.prevtick && GetStartTick() <= tc.curtick )
			{
				demoaction->InsertFireEvent( this );
			}
		}
		break;
	case ACTION_USES_TIME:
		{
			if ( GetStartTime() >= tc.prevtime && GetStartTime() <= tc.curtime )
			{
				demoaction->InsertFireEvent( this );
			}
		}
		break;
	}

	return true;
}
bool SGP_MaxInterface::GetMtlAnim( StdMat* pStdMtl, ColorTrack& track, int nChannel )
{
	if( pStdMtl == NULL )
	{
		assert( false && "std mtl is NULL" );
		return false;
	}

	int nFrameCount = 0;
	TimeValue nStartTick = GetStartTick();
	TimeValue nEndTick = GetEndTick();
	int nTickPerFrame = GetTickPerFrame();



	track.bTiling = false;

	StdUVGen *uv = NULL;

	Texmap *tx = pStdMtl->GetSubTexmap(nChannel);
	if( tx )
	{
		if( tx->ClassID() == Class_ID( BMTEX_CLASS_ID, 0 ) )
		{
			BitmapTex *bmt = (BitmapTex*)tx;
			uv = bmt->GetUVGen();
			if( uv )
			{
				track.nUTile = (int)uv->GetUScl(0);
				track.nVTile = (int)uv->GetVScl(0);
				if( track.nUTile == 1 && track.nVTile == 1 )
					track.bTiling = false;
				else
					track.bTiling = true;
				track.nStartFrame = bmt->GetStartTime();
				track.fPlaybackRate = bmt->GetPlaybackRate();
				track.nLoopMode = bmt->GetEndCondition();

				if( uv->GetUAng( 0 ) != 0.0f ||
					uv->GetVAng( 0 ) != 0.0f )
				{
					track.fUSpeed = uv->GetUAng( 0 ) / piOver180;
					track.fVSpeed = uv->GetVAng( 0 ) / piOver180;
					track.bUVMoving = true;
				}
				else
					track.bUVMoving = false;
			}
		}
	}


	TimeValue t;
	for( t = nStartTick; t <= nEndTick; t += nTickPerFrame )
		nFrameCount++;


	track.ColorKeyFrame.resize( nFrameCount );

	t = nStartTick;
	for( int i = 0; i < nFrameCount; i++, t += nTickPerFrame )
	{
		SGP_ColorKey key;
		memset( &key, 0x00, sizeof( key ) );
		Color diffuse	= pStdMtl->GetDiffuse( t );
		Color ambient	= pStdMtl->GetAmbient( t );
		Color specular	= pStdMtl->GetSpecular( t );

		Color filter	= pStdMtl->GetFilter( t );
		float alpha		= pStdMtl->GetOpacity( t );
		float shinstr	= pStdMtl->GetShinStr(t);
		float selfillum = pStdMtl->GetSelfIllum( t );

		float uoffset	= 0;
		float voffset	= 0;
		if( uv )
		{
			uoffset	= uv->GetUOffs( t );
			voffset	= uv->GetVOffs( t );
		}
/*
		int	nTransparencyType = pStdMtl->GetTransparencyType();

		key.dwBlendMode = 0;
		switch( nTransparencyType )
		{
		case TRANSP_SUBTRACTIVE:
			key.dwBlendMode |= HR3D_MDX2_MODULATE;
			break;
		case TRANSP_ADDITIVE:
			key.dwBlendMode |= HR3D_MDX2_ADD;
			break;
		case TRANSP_FILTER:
			key.dwBlendMode |= HR3D_MDX2_MODULATE2X;
			break;
		default:
			break;
		};
*/
		key.dr = diffuse.r;
		key.dg = diffuse.g;
		key.db = diffuse.b;

		key.da = alpha;


		if( uv )
		{
			key.uoffset = uv->GetUOffs( t );
			key.voffset = uv->GetVOffs( t );
		}
		else
		{
			key.uoffset = 0;
			key.voffset = 0;
		}

		track.ColorKeyFrame.getReference(i) = key;
	}

	return true;
}
int SGP_MaxInterface::GetFrameCount()
{
	return (GetEndTick()-GetStartTick())/GetFps() + 1;
}
void SGP_MaxInterface::GetTracks( int nNodeCount, INode** nodes, Track** tracks )
{
	StartProgressInfo(_M("Get node track..."));

	TimeValue nStartTick = GetStartTick();
	TimeValue nEndTick = GetEndTick();
	int nTickPerFrame = GetTickPerFrame();
	int nFrameCount = 0;

	for( TimeValue t = nStartTick; t <= nEndTick; t += nTickPerFrame )
		nFrameCount++;

	for( int i = 0; i < nNodeCount; i++ )
	{
		tracks[i]->vectorVisible.resize( nFrameCount );
		tracks[i]->vectorTrans.resize( nFrameCount );
		tracks[i]->vectorRot.resize( nFrameCount );
		tracks[i]->vectorScale.resize( nFrameCount );

		Matrix3 matrix = nodes[i]->GetObjTMAfterWSM ( 0 );
		bool bMirror = DotProd ( CrossProd ( matrix.GetRow ( 0 ), matrix.GetRow ( 1 ) ), matrix.GetRow ( 2 ) ) < 0.0 ? true : false;
		tracks[i]->bMirror = bMirror;
	}
	TimeValue t = nStartTick;
	for( int nFrameId = 0; nFrameId < nFrameCount; nFrameId++, t += nTickPerFrame )
	{
		SetProgressInfo( 100.0f*nFrameId/nFrameCount );

		for( int nNodeId = 0; nNodeId < nNodeCount; nNodeId++ )
		{
			INode* pNode = nodes[nNodeId];
			Track* pTrack = tracks[nNodeId];

			Matrix3 tm = pNode->GetNodeTM(t);

			// The coordinate system of 3DMax9 is Right-X Up-Z Screenin-Y
			// But coordinate system of SGP Engine is like D3D Right-X Up-Y Screenin-Z
			// Node Transform Matrix should be swaped.
			/*
			If your matrix looks like this:
			{ rx, ry, rz, 0 }  
			{ ux, uy, uz, 0 }  
			{ lx, ly, lz, 0 }  
			{ px, py, pz, 1 }
			To change it from left to right or right to left, flip it like this:
			{ rx, rz, ry, 0 }  
			{ lx, lz, ly, 0 }  
			{ ux, uz, uy, 0 }  
			{ px, pz, py, 1 }
			*/
			Point3 Row0 = tm.GetRow(0);
			Point3 Row1 = tm.GetRow(1);
			Point3 Row2 = tm.GetRow(2);
			Point3 Row3 = tm.GetRow(3);
			sgp::swapVariables( Row0.y,  Row0.z );
			sgp::swapVariables( Row1.x,  Row2.x );
			sgp::swapVariables( Row1.y,  Row2.z );
			sgp::swapVariables( Row1.z,  Row2.y );
			sgp::swapVariables( Row3.y,  Row3.z );
			tm.SetRow(0, Row0);
			tm.SetRow(1, Row1);
			tm.SetRow(2, Row2);
			tm.SetRow(3, Row3);


			Point3 trans;
			Quat quat;
			Point3 scale;

			{
				// calculate the translation component
				Point3 p;
				p = tm.GetTrans();

				trans.x = p.x;
				trans.y = p.y;
				trans.z = p.z;

				scale.x = tm.GetRow(0).Length();
				scale.y = tm.GetRow(1).Length();
				scale.z = tm.GetRow(2).Length();

				tm.NoScale();

				// calculate the rotation component
				Quat q(tm);
				if( tracks[nNodeId]->bMirror )
				{
					float m[4][3];
					memcpy( m, &tm, sizeof(float)*4*3 );

					m[0][0] *= -1;
					m[1][0] *= -1;
					m[2][0] *= -1;
	
					Matrix3 mm(m);

					Quat q0(mm);
					q = q0;
				}

				quat.x = q.x;
				quat.y = q.y;
				quat.z = q.z;
				quat.w = q.w;
			}


			pTrack->vectorTrans.getReference(nFrameId) = trans;
			pTrack->vectorRot.getReference(nFrameId) = quat;
			pTrack->vectorScale.getReference(nFrameId) = scale;

			float fv = pNode->GetVisibility( t );
			if( fv == 0 )
				pTrack->vectorVisible.getReference(nFrameId) = false;
			else
				pTrack->vectorVisible.getReference(nFrameId) = true;
		}
	}
	StopProgressInfo();
}