Exemple #1
0
void AnimExportUtil::getTransformAnimation( INode* node, Interval animRange, Vector<math::Matrix4x4>* anim )
{
	require( animRange.Start() <= animRange.End() );

	anim->clear();
	TimeValue dt = SGEXPORT_TICKS_PER_SAMPLE;
	for ( TimeValue t = animRange.Start() ; t <= animRange.End() ; t += dt )
	{
		Matrix4x4 tm = TmUtil::getModelToParentLH( node, t );
		anim->add( tm );
	}

	require( anim->size() > 0 );
}
Exemple #2
0
void AnimExportUtil::resamplePositionAnimation( const Vector<Matrix4x4>& tm, Interval animRange, KeyFrameContainer* pos )
{
	// find out maximum acceptable error
	float maxErrPercent = MAX_POSITION_RESAMPLING_ERROR;
	float defr = 1e9f;
	Vector3 minbox( defr, defr, defr );
	Vector3 maxbox(-defr,-defr,-defr );
	for ( int k = 0 ; k < tm.size() ; k += 2 )
	{
		Vector3 pos = tm[k].translation();
		minbox = pos.minElements( minbox );
		maxbox = pos.maxElements( maxbox );
	}
	float maxErr = (minbox-maxbox).length()/100.f * maxErrPercent;
	if ( maxErr < 1e-6f )
		maxErr = 1e-6f;

	int firstFrame = animRange.Start() / SGEXPORT_TICKS_PER_SAMPLE;
	require( firstFrame >= 0 && firstFrame < tm.size() );
	AnimExportUtil::addPositionKey( *pos, tm[firstFrame], TicksToSec(animRange.Start()) );
	AnimExportUtil::addPositionKey( *pos, tm.lastElement(), TicksToSec(animRange.End()) );
	Debug::println( "max position error = {0}", maxErr );
	resamplePositionKeys( *pos, animRange, maxErr, tm );
	if ( pos->keys() == 2 && isEqualValue(pos->getKey(0),pos->getKey(1),3) )
		pos->removeKey( 1 );
}
Exemple #3
0
void GR2ImportImpl::ImportAnimations()
{
	if (info.Animations.size() == 0 || !enableAnimation)
		return;

	ClearAnimation();

	//for (int anim=0, nanim=info.Animations.size(); anim<nanim; ++anim)
	//{
	//	Animation& anim = (*info.Animations[anim]);
	//}
	Interval range; range.SetInstant(0);

	float time = FrameToTime(0);
	for (int ianim=0, nanim=info.Animations.size(); ianim<nanim; ++ianim)
	{
		Animation& anim = (*info.Animations[ianim]);
		TimeValue animEnd = TimeToFrame(time + anim.Duration);
		if (animEnd > range.End())
			range.SetEnd(animEnd);
		// Build Default Time
		int nkeys = anim.Duration / anim.TimeStep;
		GR2Array<granny_real32> defaultKeys(nkeys);
		granny_real32 curtime = 0.0f;
		for (int ikeys=0; ikeys<nkeys; ++ikeys, curtime += anim.TimeStep)
			defaultKeys[ikeys] = curtime;

		for (int grp=0, ngrp=anim.TrackGroups.size(); grp<ngrp; ++grp)
		{
			TrackGroup& group = (*anim.TrackGroups[grp]);
			if (INode *root = o->gi->GetINodeByName(group.Name))
			{
				Point3 s( group.InitialPlacement.Scale.m[0][0]
						, group.InitialPlacement.Scale.m[1][1]
						, group.InitialPlacement.Scale.m[2][2] );
				for (int itrack=0, ntrack=group.TransformTracks.size(); itrack<ntrack; ++itrack)
				{
					TransformTrack& track = group.TransformTracks[itrack];
					if (INode *node = o->gi->GetINodeByName(track.Name))
					{
						if (Control *c = node->GetTMController())
						{
							DWORD flags=INHERIT_ALL;
							c->SetInheritanceFlags(INHERIT_ALL,FALSE);

							ImportPosition(c, track, time, defaultKeys);
							ImportRotation(c, track, time, defaultKeys);
							ImportScale(c, track, time, defaultKeys);
						}
					}
				}
				Matrix3 rot(true); group.InitialPlacement.Rotation.MakeMatrix(rot);
				Matrix3 m = TransMatrix(group.InitialPlacement.Origin) * Inverse(rot) * ScaleMatrix( s );
				PosRotScaleNode(root, m);
				// TODO: Move to initial transform
			}
		}
	}
	o->gi->SetAnimRange(range);
}
	//---------------------------------------------------------------
	bool DocumentExporter::showExportOptions(bool suppressPrompts)
	{
		if (!suppressPrompts) 
		{
			// Prompt the user with our dialogbox, and get all the options.
			// The user may cancel the export at this point.
			if (!mOptions.ShowDialog()) 
				return false;
		}
		else
		{
			mOptions.LoadOptions();
			if (!mOptions.getSampleAnimation())
			{
				Interval animRange = GetCOREInterface()->GetAnimRange();
				int sceneStart = animRange.Start();
				int sceneEnd = animRange.End();
				mOptions.setAnimBounds(sceneStart, sceneEnd);
			}
		}

		// Set relative/absolute export
	//	colladaDocument->GetFileManager()->SetForceAbsoluteFlag(!options->ExportRelativePaths());

		return true;
	}
Exemple #5
0
// Dialog proc
static INT_PTR CALLBACK ExportDlgProc(HWND hWnd, UINT msg,
	WPARAM wParam, LPARAM lParam)
{
	Interval animRange;
	ISpinnerControl  *spin;

	AscOut *exp = (AscOut*)GetWindowLongPtr(hWnd,GWLP_USERDATA); 
	switch (msg) {
	case WM_INITDIALOG:
		exp = (AscOut*)lParam;
		SetWindowLongPtr(hWnd,GWLP_USERDATA,lParam); 
		CenterWindow(hWnd, GetParent(hWnd)); 

		// Setup the spinner controls for the floating point precision 
		spin = GetISpinner(GetDlgItem(hWnd, IDC_PREC_SPIN)); 
		spin->LinkToEdit(GetDlgItem(hWnd,IDC_PREC), EDITTYPE_INT ); 
		spin->SetLimits(1, 10, TRUE); 
		spin->SetScale(1.0f);
		spin->SetValue(exp->GetPrecision() ,FALSE);
		ReleaseISpinner(spin);

		// Setup the spinner control for the static frame#
		// We take the frame 0 as the default value
		animRange = exp->GetInterface()->GetAnimRange();
		spin = GetISpinner(GetDlgItem(hWnd, IDC_STATIC_FRAME_SPIN)); 
		spin->LinkToEdit(GetDlgItem(hWnd,IDC_STATIC_FRAME), EDITTYPE_INT ); 
		spin->SetLimits(animRange.Start() / GetTicksPerFrame(), animRange.End() / GetTicksPerFrame(), TRUE); 
		spin->SetScale(1.0f);
		spin->SetValue(0, FALSE);
		ReleaseISpinner(spin);
		break;

	case CC_SPINNER_CHANGE:
		spin = (ISpinnerControl*)lParam; 
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:
			spin = GetISpinner(GetDlgItem(hWnd, IDC_PREC_SPIN)); 
			exp->SetPrecision(spin->GetIVal());
			ReleaseISpinner(spin);
		
			spin = GetISpinner(GetDlgItem(hWnd, IDC_STATIC_FRAME_SPIN)); 
			exp->SetStaticFrame(spin->GetIVal() * GetTicksPerFrame());
			ReleaseISpinner(spin);
			
			EndDialog(hWnd, 1);
			break;
		case IDCANCEL:
			EndDialog(hWnd, 0);
			break;
		}
		break;
		default:
			return FALSE;
	}
	return TRUE;
}       
Exemple #6
0
Interval Interval::Union(const Interval &i) const {
	Interval ret;

	ret.m_start = std::min(Start(), i.Start());
	ret.m_end = std::max(End(), i.End());
	
	return ret;
}
Exemple #7
0
void AnimExportUtil::addScaleAnimation( const Vector<Matrix4x4>& tm, Interval animRange, KeyFrameContainer* scale )
{
	int firstFrame = animRange.Start() / SGEXPORT_TICKS_PER_SAMPLE;
	for ( TimeValue ticks = animRange.Start() ; ticks <= animRange.End() ; ticks += SGEXPORT_TICKS_PER_SAMPLE )
	{
		require( firstFrame >= 0 && firstFrame < tm.size() );
		AnimExportUtil::addScaleKey( *scale, tm[firstFrame++], TicksToSec(ticks) );
	}
}
Exemple #8
0
void AnimExportUtil::addFloatAnimation( const util::Vector<float>& frames, Interval animRange, KeyFrameContainer* anim, float maxErr )
{
	int firstFrame = animRange.Start() / SGEXPORT_TICKS_PER_SAMPLE;
	for ( TimeValue ticks = animRange.Start() ; ticks <= animRange.End() ; ticks += SGEXPORT_TICKS_PER_SAMPLE )
	{
		require( firstFrame >= 0 && firstFrame < frames.size() );
		anim->insertKey( KeyFrame(TicksToSec(ticks),INTERP_TYPE,&frames[firstFrame++],1) );
	}
}
Exemple #9
0
void AnimExportUtil::resampleFloatAnimation( const util::Vector<float>& frames, Interval animRange, KeyFrameContainer* anim, float maxErr )
{
	int firstFrame = animRange.Start() / SGEXPORT_TICKS_PER_SAMPLE;
	require( firstFrame >= 0 && firstFrame < frames.size() );
	anim->insertKey( KeyFrame(TicksToSec(animRange.Start()),INTERP_TYPE,&frames[firstFrame],1) );
	anim->insertKey( KeyFrame(TicksToSec(animRange.End()),INTERP_TYPE,&frames.lastElement(),1) );
	resampleFloatKeys( *anim, animRange, maxErr, frames );
	if ( anim->keys() == 2 && isEqualValue(anim->getKey(0),anim->getKey(1),3) )
		anim->removeKey( 1 );
}
Exemple #10
0
// SAMPLENODEMOTION
// top level function for sampling all the motion on a single node
plSampleVec * SampleNodeMotion(INode* node, INode* parent, int sampleRate, Interface *theInterface)
{
    Interval interval = theInterface->GetAnimRange();
    TimeValue start = interval.Start();                 // in ticks
    TimeValue end = interval.End();

    sampleRate *= GetTicksPerFrame();                   // convert sample rate to ticks

    return SampleNodeMotion(node, parent, sampleRate, start, end);
}
Exemple #11
0
void ValuesTable::Move(const Interval &in) {
	if (in.End() < 0 || in.Start() >= (int)m_values.size()) {
		m_values.resize(0);
		m_values.resize(in.End() - in.Start() + 1);
		return;
	}

	while ((in.End() + 1) < (int)m_values.size())
		PopBack();

	for (int i = 0; i < in.Start(); ++i)
		PopFront();

	for (int i = 0; i > in.Start(); --i)
		PushFront();

	for (int i = m_values.size(); i <= in.End() - in.Start(); ++i) 
		PushBack();
}
Exemple #12
0
void RandKeysUtil::SetStates()
	{
	if (doTime) {
		iPosTime->Enable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_POSTIMELABEL),TRUE);
		iNegTime->Enable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_NEGTIMELABEL),TRUE);
	} else {
		iPosTime->Disable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_POSTIMELABEL),FALSE);
		iNegTime->Disable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_NEGTIMELABEL),FALSE);
		}

	if (doVal) {
		iPosVal->Enable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_POSVALLABEL),TRUE);
		iNegVal->Enable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_NEGVALLABEL),TRUE);
	} else {
		iPosVal->Disable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_POSVALLABEL),FALSE);
		iNegVal->Disable();
		EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_NEGVALLABEL),FALSE);
		}

	switch (iu->GetMajorMode()) {
		case TVMODE_EDITKEYS:
		case TVMODE_EDITFCURVE:
			SetDlgItemText(hWnd,IDC_RANDKEYS_TEXT,
				GetString(IDS_RANDKEYS_KEYTEXT));
			EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_APPLY),TRUE);
			break;

		case TVMODE_EDITTIME: {
			Interval iv = iu->GetTimeSelection();
			TSTR buf, start, end;
			TimeToString(iv.Start(),start);
			TimeToString(iv.End(),end);
			buf.printf(GetString(IDS_RANDKEYS_TIMETEXT),start,end);
			SetDlgItemText(hWnd,IDC_RANDKEYS_TEXT,buf);
			EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_APPLY),TRUE);
			break;
			}

		case TVMODE_EDITRANGES:
		case TVMODE_POSRANGES:
			SetDlgItemText(hWnd,IDC_RANDKEYS_TEXT,_T(""));
			EnableWindow(GetDlgItem(hWnd,IDC_RANDKEYS_APPLY),FALSE);
			break;
		
		}
	}
Exemple #13
0
void AnimExportUtil::resampleRotationAnimation( const Vector<Matrix4x4>& tm, Interval animRange, KeyFrameContainer* rot )
{
	float maxErr = Math::toRadians(MAX_ROTATION_RESAMPLING_ERROR);
	int firstFrame = animRange.Start() / SGEXPORT_TICKS_PER_SAMPLE;
	require( firstFrame >= 0 && firstFrame < tm.size() );
	AnimExportUtil::addRotationKey( *rot, tm[firstFrame], TicksToSec(animRange.Start()) );
	AnimExportUtil::addRotationKey( *rot, tm.lastElement(), TicksToSec(animRange.End()) );
	Debug::println( "max rotation error = {0} degrees", Math::toDegrees(maxErr) );
	resampleRotationKeys( *rot, animRange, maxErr, tm );
	if ( rot->keys() == 2 && isEqualValue(rot->getKey(0),rot->getKey(1),4) )
		rot->removeKey( 1 );
}
Exemple #14
0
void AnimExportUtil::resampleScaleAnimation( const Vector<Matrix4x4>& tm, Interval animRange, KeyFrameContainer* scale )
{
	float maxErrPercent = MAX_SCALE_RESAMPLING_ERROR;
	int firstFrame = animRange.Start() / SGEXPORT_TICKS_PER_SAMPLE;
	require( firstFrame >= 0 && firstFrame < tm.size() );
	AnimExportUtil::addScaleKey( *scale, tm[firstFrame], TicksToSec(animRange.Start()) );
	AnimExportUtil::addScaleKey( *scale, tm.lastElement(), TicksToSec(animRange.End()) );
	Debug::println( "max scale error = {0}%", maxErrPercent );
	resampleScaleKeys( *scale, animRange, maxErrPercent/100.f, tm );
	if ( scale->keys() == 2 && isEqualValue(scale->getKey(0),scale->getKey(1),3) )
		scale->removeKey( 1 );
}
Exemple #15
0
/** 
 * Resamples animation until maximum error is smaller than specified value. 
 * @param maxErr Maximum absolute error
 */
static void resampleFloatKeys( KeyFrameContainer& anim, Interval range, float maxErr, const Vector<float>& frames )
{
	TimeValue dticks = SGEXPORT_TICKS_PER_SAMPLE;
	int frame = range.Start() / dticks;
	require( frame >= 0 && frame < frames.size() );
	TimeValue rangeLen = (range.Duration()/SGEXPORT_TICKS_PER_SAMPLE)*SGEXPORT_TICKS_PER_SAMPLE;

	for ( TimeValue ticks = range.Start() ; ticks < range.End() ; ticks += dticks )
	{
		if ( ticks > range.End() )
			ticks = range.End();

		// find out error (distance) between real and sampled animation
		require( frame >= 0 && frame < frames.size() );
		float realValue = frames[frame++];
		float sampledValue = 0.f;
		anim.getValue( TicksToSec(ticks), &sampledValue, 1 );
		float err = Math::abs( realValue - sampledValue );

		// sample more accurately if needed
		if ( err > maxErr && rangeLen > dticks )
		{
			TimeValue halfRange = alignTicks( (range.End() + range.Start())/2 );
			anim.insertKey( KeyFrame( TicksToSec(halfRange), INTERP_TYPE, &frames[halfRange/dticks], 1 ) );

			if ( ticks <= halfRange )
				resampleFloatKeys( anim, Interval(range.Start(),halfRange), maxErr, frames );
			else
				resampleFloatKeys( anim, Interval(halfRange,range.End()), maxErr, frames );
		}

		if ( ticks == range.End() )
			break;
	}
}
Exemple #16
0
Interval AnimExportUtil::getKeyFramedRotationRange( INode* node3ds, Interval animRange )
{
	require( node3ds->GetTMController() );

	TimeValue start = animRange.Start();
	TimeValue end = animRange.End();

	// target defines rotation
	if ( node3ds->GetTarget() )
		return Interval( start, start );

	Control* cont = node3ds->GetTMController()->GetRotationController();
	IKeyControl* ikeys = cont ? GetKeyControlInterface( cont ) : 0;
	if ( ikeys )
	{
		ITCBRotKey key1;
		IBezQuatKey key2;
		ILinRotKey key3;
		IKey* keyp = 0;

		if ( cont->ClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0) )
			keyp = &key1;
		else if ( cont->ClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0) )
			keyp = &key2;
		else if ( cont->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID, 0) )
			keyp = &key3;

		if ( keyp )
		{
			if ( 0 == ikeys->GetNumKeys() )
				return Interval( start, start );

			ikeys->GetKey( 0, keyp );
			start = keyp->time;
			ikeys->GetKey( ikeys->GetNumKeys()-1, keyp );
			end = keyp->time;
		}
	}

	return Interval( start, end );
}
//from IUnReplaceableControl
Control * EulerExposeControl::GetReplacementClone()
{
	Control *control = NewDefaultRotationController();
	if(control)
	{
		// set key per frame
		Interval range =GetCOREInterface()->GetAnimRange();
		TimeValue tpf = GetTicksPerFrame();	
		SuspendAnimate();
		AnimateOn();
		Quat v;
		for(TimeValue t= range.Start(); t<=range.End();t+=tpf)
		{
			GetValue(t,&v,Interval(t,t));
			control->SetValue(t,&v,1,CTRL_ABSOLUTE);
		}
	
		ResumeAnimate();
	}
	return control;
}
void Import::SetSceneParameters()
{
    Interval range;

    range.SetStart(0);
    range.SetEnd(30);
    SetFrameRate(30);

    SetTicksPerFrame(160);

    range.SetStart(range.Start() * GetTicksPerFrame());
    range.SetEnd(range.End() * GetTicksPerFrame());

    m_ip->SetAnimRange(range);

    Point3 bkColor (0.0f, 0.0f, 0.0f);
    m_impip->SetBackGround(0, bkColor);

    Point3 amColor (0.3f, 0.3f, 0.3f);
    m_impip->SetAmbient(0, amColor);
}
Exemple #19
0
  TEST(basic, should_measure_intervals) {
      const int iterations = 10000;
      Interval interval;
      for (int i = 0; i < iterations; i++) {
          std::string s("");
          interval.Start("FOO");
          for (int j = 0; j < iterations; j++) {
              s += "a";
          }
          interval.End("FOO");
      }
      auto v = interval.Intervals("FOO");
      ASSERT_EQ(v.size(), iterations);

      float sum = 0;
      for (auto d : v) {
          ASSERT_GT(d.count(), 0);
          sum += d.count();
      }
      printf("avg per concatenation: %.10f\n", sum / v.size() / iterations);
  }
// ###########################################################
// #######   EXPORTERs DI SINGOLE ENTITA' DELLA SCENA  #######
// ###########################################################
void SkeletonExporter::export_general_info(void)
{
	int ff, lf, fs;
	Interval range = ip->GetAnimRange();
	Box3 bbox;

	fprintf(fTXT, "[AD]Turbo ROX as usual !! \n\n");

	INode *root = ip->GetRootNode();
	bbox.pmin.x=bbox.pmin.y=bbox.pmin.z=(float)1E10;
	bbox.pmax.x=bbox.pmax.y=bbox.pmax.z=(float)-1E10;
    GetSceneBoundingBox(root, 0, &bbox);

	ff=range.Start() / GetTicksPerFrame();
	lf=range.End() / GetTicksPerFrame();
	fs=GetFrameRate();

	// Start with a file identifier and format version
	fprintf(fTXT, "First frame : %d\n", ff);
	fprintf(fTXT, "Last frame : %d\n", lf);
	fprintf(fTXT, "Frame speed : %d\n", fs);
	fprintf(fTXT, "Scene Bounding Box at time 0\n");
	fprintf(fTXT, "min : %f, %f, %f\n",
		    bbox.pmin.x, bbox.pmin.y, bbox.pmin.z);
	fprintf(fTXT, "max : %f, %f, %f\n",
		    bbox.pmax.x, bbox.pmax.y, bbox.pmax.z);

	write_chunk_header(fA3D, SCENE_GENERAL_INFO_ID, "Scene Root", 36);
	fwrite(&ff, sizeof(int), 1, fA3D);
	fwrite(&lf, sizeof(int), 1, fA3D);
	fwrite(&fs, sizeof(int), 1, fA3D);
	write_bbox(&bbox, fA3D);

	if (makeRAY) write_chunk_header(fRAY, SCENE_GENERAL_INFO_ID, "Scene Root", 36);
	if (makeRAY) fwrite(&ff, sizeof(int), 1, fRAY);
	if (makeRAY) fwrite(&lf, sizeof(int), 1, fRAY);
	if (makeRAY) fwrite(&fs, sizeof(int), 1, fRAY);
	if (makeRAY) write_bbox(&bbox, fRAY);
}
Exemple #21
0
void ValuesTable::MoveView(int d) { 

	m_view.Move(d);
	
	if (!m_draw->m_draws_controller->GetDoubleCursor())
		m_stats.Move(d);

	Interval in = m_view.Union(m_stats);
	Interval fin = in;
	fin.m_start = fin.Start() - m_draw->m_draws_controller->GetFilter();
	fin.m_end = fin.End() + m_draw->m_draws_controller->GetFilter();

	Move(fin);

	m_view.Move(-in.Start() + m_draw->m_draws_controller->GetFilter());
	m_stats.Move(-in.Start() + m_draw->m_draws_controller->GetFilter());

	if (!m_draw->m_draws_controller->GetDoubleCursor()) {
		RecalculateStats();
		m_draw->m_observers->NotifyStatsChanged(m_draw);
	}

}
Exemple #22
0
/** 
 * Resamples animation until maximum error is smaller than specified angle (radians). 
 * @param maxErr Maximum absolute error (radians).
 */
static void resampleRotationKeys( KeyFrameContainer& anim, Interval range, float maxErr, const Vector<Matrix4x4>& tm )
{
	TimeValue dticks = SGEXPORT_TICKS_PER_SAMPLE;
	int frame = range.Start() / dticks;
	require( frame >= 0 && frame < tm.size() );
	TimeValue rangeLen = (range.Duration()/SGEXPORT_TICKS_PER_SAMPLE)*SGEXPORT_TICKS_PER_SAMPLE;

	for ( TimeValue ticks = range.Start() ; ticks < range.End() ; ticks += dticks )
	{
		if ( ticks > range.End() )
			ticks = range.End();

		// find out error (distance) between real and sampled animation
		require( frame >= 0 && frame < tm.size() );
		const Matrix4x4& m = tm[frame++];
		Matrix3x3 ref = m.rotation().orthonormalize();
		float q[4];
		anim.getValue( TicksToSec(ticks), q, 4 );
		Matrix3x3 cmp( Quaternion(q[0],q[1],q[2],q[3]) );
		float xang = Math::abs( Math::acos( clamp(cmp.getColumn(0).dot(ref.getColumn(0)), -1.f, 1.f) ) );
		float yang = Math::abs( Math::acos( clamp(cmp.getColumn(1).dot(ref.getColumn(1)), -1.f, 1.f) ) );
		float zang = Math::abs( Math::acos( clamp(cmp.getColumn(2).dot(ref.getColumn(2)), -1.f, 1.f) ) );
		float err = xang;
		if ( yang > err )
			err = yang;
		if ( zang > err )
			err = zang;

		// sample more accurately if needed
		if ( err > maxErr && rangeLen > dticks )
		{
			TimeValue halfRange = alignTicks( (range.End() + range.Start())/2 );
			AnimExportUtil::addRotationKey( anim, tm[halfRange/dticks], TicksToSec(halfRange) );
			if ( ticks <= halfRange )
				resampleRotationKeys( anim, Interval(range.Start(),halfRange), maxErr, tm );
			else
				resampleRotationKeys( anim, Interval(halfRange,range.End()), maxErr, tm );
		}

		if ( ticks == range.End() )
			break;
	}
}
Exemple #23
0
/** 
 * Resamples animation until maximum error is smaller than specified value. 
 * @param maxErr Maximum relative error, i.e. 0.05f == 5%.
 */
static void resampleScaleKeys( KeyFrameContainer& anim, Interval range, float maxErr, const Vector<Matrix4x4>& tm )
{
	TimeValue dticks = SGEXPORT_TICKS_PER_SAMPLE;
	int frame = range.Start() / dticks;
	require( frame >= 0 && frame < tm.size() );
	TimeValue rangeLen = (range.Duration()/SGEXPORT_TICKS_PER_SAMPLE)*SGEXPORT_TICKS_PER_SAMPLE;

	for ( TimeValue ticks = range.Start() ; ticks < range.End() ; ticks += dticks )
	{
		if ( ticks > range.End() )
			ticks = range.End();

		// find out error (distance) between real and sampled animation
		require( frame >= 0 && frame < tm.size() );
		const Matrix4x4& m = tm[frame++];
		Vector3 ref;
		Vector3 a;
		a = getAxis(m,0); ref[0] = a.dot(normalize0(a));
		a = getAxis(m,1); ref[1] = a.dot(normalize0(a));
		a = getAxis(m,2); ref[2] = a.dot(normalize0(a));
		float cmp[3];
		anim.getValue( TicksToSec(ticks), cmp, 3 );
		float err = (Vector3(cmp[0],cmp[1],cmp[2]) - ref).length();
		if ( ref.length() > Float::MIN_VALUE )
			err /= ref.length();

		// sample more accurately if needed
		if ( err > maxErr && rangeLen > dticks )
		{
			TimeValue halfRange = alignTicks( (range.End() + range.Start())/2 );
			AnimExportUtil::addScaleKey( anim, tm[halfRange/dticks], TicksToSec(halfRange) );
			if ( ticks <= halfRange )
				resampleScaleKeys( anim, Interval(range.Start(),halfRange), maxErr, tm );
			else
				resampleScaleKeys( anim, Interval(halfRange,range.End()), maxErr, tm );
		}

		if ( ticks == range.End() )
			break;
	}
}
Exemple #24
0
int		LinkTimeControl::PaintFCurves( ParamDimensionBase *dim, HDC hdc, Rect& rcGraph, Rect& rcPaint,
			float tzoom, int tscroll, float vzoom, int vscroll, DWORD flags )
{
	const int n = NumKeys();
	if ( n == 0 )
		return 0;

	Interval valid;
	int h = rcGraph.h()-1;
	HPEN dpen,spen;
	BOOL init=FALSE;		
	Interval range = GetTimeRange(TIMERANGE_ALL);	
	SetBkMode(hdc,TRANSPARENT);	

	dpen = CreatePen(PS_DOT,0,GetColorManager()->GetColor(kFunctionCurveFloat));
	spen = CreatePen(PS_SOLID,0,GetColorManager()->GetColor(kFunctionCurveFloat));

	SIZE size;
	GetTextExtentPoint( hdc, _T("0"), 1, &size );

	float val;
	TimeValue leftTime = ScreenToTime(rcPaint.left,tzoom,tscroll);
	TimeValue rightTime = ScreenToTime(rcPaint.right,tzoom,tscroll);
	int x, y;

	// dotted line to left of keys
	if ( leftTime < range.Start() )
	{
		SelectObject(hdc,dpen);
		GetValue(range.Start(),&val,valid);
		y = ValueToScreen(dim->Convert(val),h,vzoom,vscroll);
		MoveToEx(hdc,rcPaint.left,y,NULL);
		LineTo(hdc,TimeToScreen(range.Start(),tzoom,tscroll),y);
	}

	SelectObject(hdc,spen);

	// first node text
	{
		TimeValue t = GetKeyTime( 0 );
		if ( t >= leftTime && t <= rightTime )
		{
			GetValue(t,&val,valid);
			y = ValueToScreen(dim->Convert(val),h,vzoom,vscroll);
			x = TimeToScreen(t,tzoom,tscroll);
			INode* node = fOwner->GetNode( 0 );
			DLTextOut( hdc, x, y-1-size.cy, node ? node->GetName() : _T("World") );
		}
	}

	// solid line between keys
	for ( int i=1; i<n; ++i )
	{
		TimeValue t0 = GetKeyTime( i-1 );
		TimeValue t1 = GetKeyTime( i );
		if ( t1 < leftTime || t0 > rightTime )
			continue;
		GetValue(t0,&val,valid);
		y = ValueToScreen(dim->Convert(val),h,vzoom,vscroll);
		MoveToEx(hdc,TimeToScreen(t0,tzoom,tscroll),y,NULL);
		x = TimeToScreen(t1,tzoom,tscroll);
		LineTo(hdc,x,y);
		GetValue(t1,&val,valid);
		y = ValueToScreen(dim->Convert(val),h,vzoom,vscroll);
		LineTo(hdc,x,y);

		INode* node = fOwner->GetNode( i );
		DLTextOut( hdc, x, y-1-size.cy, node ? node->GetName() : _T("World") );
	}

	// dotted line to right of keys
	if ( rightTime > range.End() )
	{
		SelectObject(hdc,dpen);
		GetValue(range.End(),&val,valid);
		y = ValueToScreen(dim->Convert(val),h,vzoom,vscroll);
		MoveToEx(hdc,TimeToScreen(range.End(),tzoom,tscroll),y,NULL);
		LineTo(hdc,rcPaint.right,y);
	}

	SelectObject( hdc, spen );
	HBRUSH hUnselBrush = CreateSolidBrush(GetColorManager()->GetColor(kTrackbarKeys));
	HBRUSH hSelBrush = CreateSolidBrush(GetColorManager()->GetColor(kTrackbarSelKeys));

	// render keys themselves
	for ( int i=0; i<n; ++i )
	{
		TimeValue t = GetKeyTime( i );
		if ( t < leftTime || t > rightTime )
			continue;
		GetValue(t,&val,valid);
		y = ValueToScreen(dim->Convert(val),h,vzoom,vscroll);
		x = TimeToScreen(t,tzoom,tscroll);
		SelectObject( hdc, IsKeySelected( i ) ? hSelBrush : hUnselBrush );
		Rectangle(hdc,x-3,y-3,x+3,y+3);
	}

	SetBkMode(hdc,OPAQUE);
	SelectObject(hdc,GetStockObject(BLACK_PEN));	

	DeleteObject(spen);
	DeleteObject(dpen);
	DeleteObject(hUnselBrush);
	DeleteObject(hSelBrush);

	return 0;
}
void SkeletonExporter::export_particle_spray(INode *node)
{
  char sval[50];
  Interval range = ip->GetAnimRange();
  ObjectState os = node->EvalWorldState(0);
  if (!os.obj) return;

  SimpleParticle *partsys;
  Object *p; IDerivedObject *q; Modifier *m;
  partsys=(SimpleParticle *)os.obj;
  Point3 row;
  Matrix3 mat;
  Mtl *materiale;
  INode *padre=node->GetParentNode();
  int mod_names=0, k, mod_count=0;
  char modNames[25][50], refname[50];
  int sf, sm, mf, n;

  if ((padre) && (strcmp(padre->GetName(), "Scene Root")!=0))
    sf=strlen(padre->GetName())+1;
  else sf=0;
  
  materiale=node->GetMtl();
  if (materiale) sm=strlen(materiale->GetName())+1;
  else sm=0;

  mf=0;

  // trovo i modificatori Wind e Gravity con il loro
  // nome
  p=node->GetObjOrWSMRef();
  if ((p->SuperClassID()==GEN_DERIVOB_CLASS_ID) &&
	 (p->ClassID()==Class_ID(WSM_DERIVOB_CLASS_ID, 0)))
  {
	q=(IDerivedObject *)p;
	n=q->NumModifiers();
	mod_names=mod_count=0;
	for (k=0; k<n; k++)
	{
      m=q->GetModifier(k);
      Class_ID cidd = m->ClassID();
	  if ((cidd==Class_ID(WINDMOD_CLASS_ID, 0)) ||
		  (cidd==Class_ID(GRAVITYMOD_CLASS_ID, 0)) ||
		  (cidd==Class_ID(BOMB_OBJECT_CLASS_ID, 0)))
	  {
		 SimpleWSMMod *wm=(SimpleWSMMod *)m;
		 strcpy(refname, wm->nodeRef->GetName());
		 strcpy(modNames[mod_count], refname);
		 mod_names+=(strlen(refname)+1);
         mod_count++;
	  }
	}
  }

  write_chunk_header(fA3D, SPRAY_PARTICLE_SYSTEM_ID, node->GetName(),
	                 4+sf+4+sm+  // padre, materiale
					 12+48+  //pivot, matrice world(t=0)
					 4+mf+   // nome mesh/oggetto
					 4+4+4+4+ // emitter: width, height, speed , variation
					 4+4+4+4+ // life, startTime, stopTime, max_particles
					 4+mod_names // num WSM, nomi WSM
					 );

  fprintf(fTXT, "Spray particle system found\n");
  fprintf(fTXT, "Name : %s\n", node->GetName());
  if (padre) fprintf(fTXT, "Parent name : %s\n", node->GetParentNode()->GetName());
  if (materiale) fprintf(fTXT, "Material name : %s\n", materiale->GetName());
  if (makeADP)
    fprintf(fADP, "  particle %c%s%c\n  {\n", '"', node->GetName(), '"');

  // -----------    scrivo il padre (flag, nome)   ------------------
  fwrite(&sf, sizeof(int), 1, fA3D);
  if (sf>0)
  {
	 write_string0(fA3D, padre->GetName());
	 if (makeADP) fprintf(fADP, "     father=%c%s%c;\n", '"', padre->GetName(), '"');
  }
  else
	 if (makeADP) fprintf(fADP, "     father=%cNONE%c;\n", '"', '"');


  // ---------   scrivo il materiale di base (flag, nome)   ----------
  fwrite(&sm, sizeof(int), 1, fA3D);
  if (sm>0)
  {
     write_string0(fA3D, materiale->GetName());
	 //if (makeADP) fprintf(fADP, "     material=%c%s%c;\n", '"', materiale->GetName(), '"');
  }
  else
	 //if (makeADP) fprintf(fADP, "     material=%cNONE%c;\n", '"', '"');


  // ---------------   scrittura del punto di pivot   ----------------
  GetPivotOffset(node, &row);
  fprintf(fTXT, "Pivot point : %f, %f, %f\n", row.x, row.y, row.z);
  write_point3(&row, fA3D);

  // -------------------   scrittura matrice   -----------------------
  mat = node->GetNodeTM(0);
  write_matrix(&mat, fA3D);

  // ---------   scrittura dell'eventuale nome della mesh   ----------
  fwrite(&mf, sizeof(int), 1, fA3D);
  if (mf>0)
  {
    write_string0(fA3D, "TO_DO");
    if (makeADP) fprintf(fADP, "    lod=%cTO_DO, 0, 99999.99%c;\n", '"', '"');
  }


  // *************  ESPORAZIONE DATI DEL PARTCLE SYSTEM  *************
  float initVel, var, width, height;
  int count, life, start_time, stop_time;

  partsys->pblock->GetValue(PB_EMITTERWIDTH, 0, width, FOREVER);
  partsys->pblock->GetValue(PB_EMITTERHEIGHT, 0, height, FOREVER);
  partsys->pblock->GetValue(PB_SPEED, 0, initVel, FOREVER);
  partsys->pblock->GetValue(PB_VARIATION, 0, var, FOREVER);
  partsys->pblock->GetValue(PB_LIFETIME, 0, life, FOREVER);
  life=life/ GetTicksPerFrame();
  partsys->pblock->GetValue(PB_STARTTIME, 0, start_time, FOREVER);
  start_time=start_time / GetTicksPerFrame();
  stop_time=range.End() / GetTicksPerFrame();
  partsys->pblock->GetValue(PB_RNDPARTICLES, 0, count, FOREVER);

  fprintf(fTXT, "Emitter width: %f\n", width);
  fprintf(fTXT, "Emitter height: %f\n", height);
  fprintf(fTXT, "Emitter speed (init vel): %f\n", initVel);
  fprintf(fTXT, "Emitter variation: %f\n", var);
  fprintf(fTXT, "Life: %d\n", life);
  fprintf(fTXT, "Start time: %d\n", start_time);
  fprintf(fTXT, "Stop time: %d\n", stop_time);
  fprintf(fTXT, "Max particles: %d\n", count);

  // informazioni in formato binario (.A3D)
  fwrite(&width, sizeof(float), 1, fA3D);
  fwrite(&height, sizeof(float), 1, fA3D);
  fwrite(&initVel, sizeof(float), 1, fA3D);
  fwrite(&var, sizeof(float), 1, fA3D);
  fwrite(&life, sizeof(int), 1, fA3D);
  fwrite(&start_time, sizeof(int), 1, fA3D);
  fwrite(&stop_time, sizeof(int), 1, fA3D);
  fwrite(&count, sizeof(int), 1, fA3D);

  fwrite(&mod_count, sizeof(int), 1, fA3D);
  fprintf(fTXT, "Modificatori (wind, gravity, bomb) collegati: %d\n", mod_count);
  for (k=0; k<mod_count; k++)
  {
     fprintf(fTXT, "Modificatore %d: %s\n", k, modNames[k]);
	 write_string0(fA3D, modNames[k]);
	 //if (makeADP)
       //fprintf(fADP, "     modifier=%c%s, ON%c;\n", '"', modNames[k], '"');
  }


  //------------------------
  // ESPORTAZIONE KEYFRAMER
  //------------------------
  Control *c;
  int size_key;

  // NB: per gli oggetti mesh e quant'altre tipologie di
  // oggetti che possono essere linkati (ovvero dove e'
  // possibile implmenetare gerarchie), esporto SEMPRE una key 
  // di posizione, una di rotazione ed una di scaling
  // POSITION CONTROLLER
  c=node->GetTMController()->GetPositionController();
  if ((c) && (c->NumKeys()>0))
  {
	 if (IsTCBControl(c)) size_key=36;
	 else
	 if (IsBezierControl(c)) size_key=40;
	 else size_key=16;
	 fprintf(fTXT, "Particle position track present.\n");
     write_chunk_header(fA3D, POSITION_TRACK_ID,
	                    node->GetName(), 1+2+4+c->NumKeys()*size_key);
     export_point3_track(c, 1, fA3D);
  }
  else
  {
	fprintf(fTXT, "Particle position track present. (1 key case)\n");
	if (!c) 	fprintf(fTXT, "c nullo !\n");
    fflush(fTXT);
	size_key=36;
    write_chunk_header(fA3D, POSITION_TRACK_ID,
	                   node->GetName(), 1+2+4+1*size_key);
    export_1key_point3_track(c, 1, fA3D);
  }


  // ROTATION CONTROLLER
  c=node->GetTMController()->GetRotationController();
  if ((c) && (c->NumKeys()>0))
  {
	 if (IsTCBControl(c)) size_key=40;
	 else size_key=20;
	 fprintf(fTXT, "Particle rotation track present.\n");
     write_chunk_header(fA3D, ROTATION_TRACK_ID,
	                    node->GetName(), 1+2+4+c->NumKeys()*size_key);
     export_rot_track(c, fA3D);
  }
  else
  {
	fprintf(fTXT, "Particle rotation track present. (1 key case)\n");
    fflush(fTXT);
	size_key=40;
    write_chunk_header(fA3D, ROTATION_TRACK_ID,
	                   node->GetName(), 1+2+4+1*size_key);
    export_1key_rot_track(c, fA3D);
  }

  
  // SCALE CONTROLLER
  c=node->GetTMController()->GetScaleController();
  if ((c) && (c->NumKeys()>0))
  {
	 if (IsTCBControl(c)) size_key=36;
	 else
	 if (IsBezierControl(c)) size_key=40;
	 else size_key=16;
	 fprintf(fTXT, "Particle scaling track present.\n");
     write_chunk_header(fA3D, SCALE_TRACK_ID,
	                    node->GetName(), 1+2+4+c->NumKeys()*size_key);
     export_scale_track(c, fA3D);
  }
  else
  {
	fprintf(fTXT, "Particle scaling track present. (1 key case)\n");
	size_key=36;
    write_chunk_header(fA3D, SCALE_TRACK_ID,
	                   node->GetName(), 1+2+4+1*size_key);
    export_1key_scale_track(c, fA3D);
  }

  // keyframer emitter width
  c=partsys->pblock->GetController(PB_EMITTERWIDTH);
  if ((c) && (c->NumKeys()>0))
  {
	 if (IsTCBControl(c)) size_key=28;
	 else
	 if (IsBezierControl(c)) size_key=16;
	 else size_key=8;
	 fprintf(fTXT, "Particle emitter width track present.\n");
     write_chunk_header(fA3D, PARTICLE_EMITTER_WIDTH_TRACK_ID,
	                    node->GetName(), 1+2+4+c->NumKeys()*size_key);
     export_float_track(c, 1, fA3D);
  }

  // keyframer emitter length
  c=partsys->pblock->GetController(PB_EMITTERHEIGHT);
  if ((c) && (c->NumKeys()>0))
  {
	 if (IsTCBControl(c)) size_key=28;
	 else
	 if (IsBezierControl(c)) size_key=16;
	 else size_key=8;
	 fprintf(fTXT, "Particle emitter length track present.\n");
     write_chunk_header(fA3D, PARTICLE_EMITTER_LENGTH_TRACK_ID,
	                    node->GetName(), 1+2+4+c->NumKeys()*size_key);
     export_float_track(c, 1, fA3D);
  }

  // keyframer particles speed
  c=partsys->pblock->GetController(PB_SPEED);
  if ((c) && (c->NumKeys()>0))
  {
	 if (IsTCBControl(c)) size_key=28;
	 else
	 if (IsBezierControl(c)) size_key=16;
	 else size_key=8;
	 fprintf(fTXT, "Particle emitter speed track present.\n");
     write_chunk_header(fA3D, PARTICLE_EMITTER_SPEED_TRACK_ID,
	                    node->GetName(), 1+2+4+c->NumKeys()*size_key);
     export_float_track(c, 1, fA3D);
  }

  // keyframer particles variations
  c=partsys->pblock->GetController(PB_VARIATION);
  if ((c) && (c->NumKeys()>0))
  {
	 if (IsTCBControl(c)) size_key=28;
	 else
	 if (IsBezierControl(c)) size_key=16;
	 else size_key=8;
	 fprintf(fTXT, "Particle emitter variation track present.\n");
     write_chunk_header(fA3D, PARTICLE_EMITTER_VARIATION_TRACK_ID,
	                    node->GetName(), 1+2+4+c->NumKeys()*size_key);
     export_float_track(c, 1, fA3D);
  }


  if (makeADP)
  {
    fprintf(fADP, "     texture=%cNONE%c;\n", '"', '"');
	my_ftoa(width, sval);
	fprintf(fADP, "     emitter_width=%c%s%c;\n", '"', sval, '"');
	my_ftoa(height, sval);
    fprintf(fADP, "     emitter_height=%c%s%c;\n", '"', sval, '"');
    fprintf(fADP, "     faded_particles=%cOFF%c;\n", '"', '"');
    fprintf(fADP, "     max_particles=%c%d%c;\n", '"', count, '"');
    fprintf(fADP, "     start_time=%c%d%c;\n", '"', start_time, '"');
    fprintf(fADP, "     end_time=%c%d%c;\n", '"', stop_time, '"');
    fprintf(fADP, "     life=%c%d%c;\n", '"', life, '"');
	my_ftoa(initVel, sval);
    fprintf(fADP, "     speed=%c%s%c;\n", '"', sval, '"');
	my_ftoa(var, sval);
    fprintf(fADP, "     variation=%c%s%c;\n", '"', sval, '"');
    fprintf(fADP, "     size_attenuation=%c0.4, 1.0, 0.8, 0.1%c;\n", '"', '"');
	fprintf(fADP, "  }\n\n");
  }
  fprintf(fTXT, "\n\n");
}
Exemple #26
0
/*
====================
DoExport
====================
*/
int G3DAExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options)
{
	// clear the old data
	if(mRoot){delete mRoot, mRoot = NULL;}
	mNodeCount = 0;
	mSkins.clear();

	// normalize the file path
	Str path = UnifySlashes(name);

	// get the export dir
	mPath = UnifySlashes(i->GetDir(APP_EXPORT_DIR));
	mPath += '/';
	if( !strstr( path.c_str(), mPath.c_str() ) )
	{
		G3DAssert("The export path(%s) is illegal!",name);
		return 1;
	}

	// get the animation range
	Interval inter = i->GetAnimRange();
	mTime = i->GetTime();
	mStart = inter.Start();
	mEnd = inter.End();

	// recursive export all of the node
	exportNode(i->GetRootNode());

	// export all of the skin
	for(int k = 0; k < mSkins.size(); k++) exportSkin(mSkins[k]);
	
	// recursive export all of the animation
	exportAnimation(mRoot);

	// check all of the nodes
	std::set<Str> names, duplicates;
	check(mRoot, names, duplicates);
	for(std::set<Str>::iterator it = duplicates.begin(); it != duplicates.end(); ++it)
	{
		G3DAssert( "Duplicate node name : %s", (*it).c_str() );
		return 1;
	}

#if 0
	// write all of the data	
	FILE *output = fopen(path.c_str(), "wb");
	if(output==NULL) { G3DAssert("Fail to open the file : %s", path.c_str()); return 1; }

	// write count of the frame
	unsigned int frame_count = (mEnd-mStart)/GetTicksPerFrame()+1;
	fwrite( &frame_count, sizeof(unsigned int), 1, output );

	// write number of the node
	fwrite( &mNodeCount, sizeof(unsigned int), 1, output );

	// write count of the action
	unsigned int action_count = 0;
	fwrite( &action_count, sizeof(unsigned int), 1, output );

	// write all of the node animation
	wirteAnimation(output,mRoot);

	// close file
	fclose(output);
#else
	// write all of the data	
	FILE *output = fopen(path.c_str(), "wb");
	if(output==NULL) { G3DAssert("Fail to open the file : %s", path.c_str()); return 1; }

	// get the count of the frame
	unsigned int frame_count = (mEnd-mStart)/GetTicksPerFrame()+1;

	// begin to write the animation
	fprintf(output, "<animation>\n");

	// begin to write the frame
	fprintf(output, "<frame count=\"%d\">\n", frame_count);

	// write all of the node frame
	wirteFrame(output,mRoot);

	// end to wirte the frame
	fprintf(output, "</frame>\n");

	// end to wirte the animation
	fprintf(output, "</animation>\n");

	// close file
	fclose(output);
#endif
	// clear all of the data
	mSkins.clear();
	if(mRoot){delete mRoot, mRoot = NULL;}

	return 1;
}
	// options dialog message handler
	INT_PTR Options::ExportOptionsDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM)
	{
		ISpinnerControl* spin;
		//	int ticksPerFrame = GetTicksPerFrame();
		Interval animRange = mMaxInterface->GetAnimRange();
		int sceneStart = animRange.Start();
		int sceneEnd = animRange.End();

		switch (message)
		{
		case WM_INITDIALOG: {
			CenterWindow(hWnd, GetParent(hWnd)); 

			// grab scene timing details & clip anim start & end if needed
			if (mAnimationStart < sceneStart) 
				mAnimationStart = sceneStart;

			if (mAnimationStart > sceneEnd) 
				mAnimationStart = sceneEnd;

			if (mAnimationEnd < sceneStart) 
				mAnimationEnd = sceneStart;

			if (mAnimationEnd > sceneEnd) 
				mAnimationEnd = sceneEnd;

			// setup checkboxes
			CheckDlgButton(hWnd, IDC_GEOM_NORMALS, mNormals);
			CheckDlgButton(hWnd, IDC_GEOM_TRIANGLES, mTriangulate);
			CheckDlgButton(hWnd, IDC_GEOM_XREFS, mIncludeXrefs);
			CheckDlgButton(hWnd, IDC_GEOM_TANGENTS, mTangents);
			CheckDlgButton(hWnd, IDC_ANIM_ENABLE, mAnimations);
			CheckDlgButton(hWnd, IDC_ANIM_SAMPLE, mSampleAnimation);
			CheckDlgButton(hWnd, IDC_ANIM_CLIP, mCreateClip);
			CheckDlgButton(hWnd, IDC_LAYERS_TO_CLIPS, mLayersAsClips);
			CheckDlgButton(hWnd, IDC_BAKE_MATRICES, mBakeMatrices);
			CheckDlgButton(hWnd, IDC_RELATIVE_PATHS, mRelativePaths);
			CheckDlgButton(hWnd, IDC_COPY_IMAGES, mCopyImages);
			CheckDlgButton(hWnd, IDC_EXPORT_USER_PROPERTIES, mExportUserDefinedProperties);

			// Animation checkboxes depend on the enable button.
			EnableDlgControl(hWnd, IDC_ANIM_SAMPLE, mAnimations);
			EnableDlgControl(hWnd, IDC_ANIM_CLIP, mAnimations);

#if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, mAnimations);
#else
			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, false);
#endif

			// setup spinners
			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_START), EDITTYPE_TIME);
			spin->SetLimits(sceneStart, sceneEnd, true); 
			spin->SetScale(1.0f);
			spin->SetValue(mAnimationStart, true);
			spin->Enable(mSampleAnimation && mAnimations);
			EnableWindow(GetDlgItem(hWnd, IDC_START_LABEL), mSampleAnimation && mAnimations);
			ReleaseISpinner(spin);

			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_END), EDITTYPE_TIME); 
			spin->SetLimits(sceneStart, sceneEnd, true); 
			spin->SetScale(1.0f);
			spin->SetValue(mAnimationEnd, false);
			spin->Enable(mSampleAnimation && mAnimations);
			EnableWindow(GetDlgItem(hWnd, IDC_END_LABEL), mSampleAnimation && mAnimations);
			ReleaseISpinner(spin);

			return true;
							}

		case WM_COMMAND:
			switch (LOWORD(wParam)) {
		case IDC_ANIM_ENABLE:
			mAnimations = IsDlgButtonChecked(hWnd, IDC_ANIM_ENABLE) == BST_CHECKED;
			EnableDlgControl(hWnd, IDC_ANIM_SAMPLE, mAnimations);
			EnableDlgControl(hWnd, IDC_ANIM_CLIP, mAnimations);
#if defined(MAX_RELEASE_R17) && (MAX_RELEASE >= MAX_RELEASE_R17)
			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, mAnimations);
#else
			EnableDlgControl(hWnd, IDC_LAYERS_TO_CLIPS, false);
#endif

			/*spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN)); 
			//spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_START), EDITTYPE_INT);
			spin->Enable(sampleAnim && animations);
			EnableWindow(GetDlgItem(hWnd, IDC_START_LABEL), sampleAnim && animations);
			ReleaseISpinner(spin);

			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN)); 
			//spin->LinkToEdit(GetDlgItem(hWnd,IDC_ANIM_END), EDITTYPE_INT);
			spin->Enable(sampleAnim && animations);
			EnableWindow(GetDlgItem(hWnd, IDC_END_LABEL), sampleAnim && animations);
			ReleaseISpinner(spin);
			break; */

		case IDC_ANIM_SAMPLE:
			mSampleAnimation = IsDlgButtonChecked(hWnd, IDC_ANIM_SAMPLE) == BST_CHECKED;

			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN)); 
			spin->Enable(mSampleAnimation && mAnimations);
			EnableWindow(GetDlgItem(hWnd, IDC_START_LABEL), mSampleAnimation && mAnimations);
			ReleaseISpinner(spin);

			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN)); 
			spin->Enable(mSampleAnimation && mAnimations);
			EnableWindow(GetDlgItem(hWnd, IDC_END_LABEL), mSampleAnimation && mAnimations);
			ReleaseISpinner(spin);
			break;

		case IDOK:
			// hit OK, pick up control values & end dialog
			mBakeMatrices = IsDlgButtonChecked(hWnd, IDC_BAKE_MATRICES) == BST_CHECKED;
			mRelativePaths = IsDlgButtonChecked(hWnd, IDC_RELATIVE_PATHS) == BST_CHECKED;
			mAnimations = IsDlgButtonChecked(hWnd, IDC_ANIM_ENABLE) == BST_CHECKED;
			mSampleAnimation = IsDlgButtonChecked(hWnd, IDC_ANIM_SAMPLE) == BST_CHECKED;
			mCreateClip = IsDlgButtonChecked(hWnd, IDC_ANIM_CLIP) == BST_CHECKED;
			mLayersAsClips = IsDlgButtonChecked(hWnd, IDC_LAYERS_TO_CLIPS) == BST_CHECKED;
			mNormals = IsDlgButtonChecked(hWnd, IDC_GEOM_NORMALS) == BST_CHECKED;
			mTriangulate = IsDlgButtonChecked(hWnd, IDC_GEOM_TRIANGLES) == BST_CHECKED;
			mIncludeXrefs = IsDlgButtonChecked(hWnd, IDC_GEOM_XREFS) == BST_CHECKED;
			mTangents = IsDlgButtonChecked(hWnd, IDC_GEOM_TANGENTS) == BST_CHECKED;
			mCopyImages = IsDlgButtonChecked(hWnd, IDC_COPY_IMAGES) == BST_CHECKED;
			mExportUserDefinedProperties = IsDlgButtonChecked(hWnd, IDC_EXPORT_USER_PROPERTIES) == BST_CHECKED;

			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_START_SPIN)); 
			mAnimationStart = mSampleAnimation ? spin->GetIVal() : sceneStart;
			ReleaseISpinner(spin);
			spin = GetISpinner(GetDlgItem(hWnd, IDC_ANIM_END_SPIN)); 
			mAnimationEnd = mSampleAnimation ? spin->GetIVal() : sceneEnd; 
			ReleaseISpinner(spin);

			EndDialog(hWnd, 1);
			break;

		case IDCANCEL:
			EndDialog(hWnd, 0);
			break;
			}
		default:
			return false;
		}
		return true;
	}
Exemple #28
0
static INT_PTR CALLBACK ElMaxPluginDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_INITDIALOG:
		{
			theElMaxPlugin.Init(hWnd);
			
			Interval animRange;
			ISpinnerControl* spin;
			int frameStart, frameEnd;

			// Setup the spinner control for the static frame#
			// We take the frame 0 as the default value
			animRange = theElMaxPlugin.GetInterface()->GetAnimRange();
			frameStart = animRange.Start() / GetTicksPerFrame();
			frameEnd = animRange.End() / GetTicksPerFrame();
			spin = GetISpinner(GetDlgItem(hWnd, IDC_STATIC_FRAME_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd,IDC_STATIC_FRAME), EDITTYPE_INT); 
			spin->SetLimits(frameStart, frameEnd, TRUE);
			spin->SetScale(1.0f);
			spin->SetValue(theElMaxPlugin.GetStaticFrame(), FALSE);
			ReleaseISpinner(spin);

			// Setup the spinner controls for the mesh animation start frame
			spin = GetISpinner(GetDlgItem(hWnd, IDC_MESHANIM_START_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd, IDC_MESHANIM_START), EDITTYPE_INT); 
			spin->SetLimits(frameStart, frameEnd, TRUE); 
			spin->SetScale(1.0f);
			spin->SetValue(frameStart, FALSE);
			ReleaseISpinner(spin);

			// Setup the spinner controls for the mesh animation end frame
			spin = GetISpinner(GetDlgItem(hWnd, IDC_MESHANIM_END_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd, IDC_MESHANIM_END), EDITTYPE_INT); 
			spin->SetLimits(frameStart, frameEnd, TRUE); 
			spin->SetScale(1.0f);
			spin->SetValue(frameEnd, FALSE);
			ReleaseISpinner(spin);

			// Setup the spinner controls for the mesh animation key sample rate 
			spin = GetISpinner(GetDlgItem(hWnd, IDC_MESHANIM_STEP_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd, IDC_MESHANIM_STEP), EDITTYPE_INT); 
			spin->SetLimits(1, 100, TRUE); 
			spin->SetScale(1.0f);
			spin->SetValue(theElMaxPlugin.GetMeshAnimFrameStep(), FALSE);
			ReleaseISpinner(spin);

			// Setup the spinner controls for the skeletal animation start frame
			spin = GetISpinner(GetDlgItem(hWnd, IDC_SKELETALANIM_START_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd, IDC_SKELETALANIM_START), EDITTYPE_INT); 
			spin->SetLimits(frameStart, frameEnd, TRUE); 
			spin->SetScale(1.0f);
			spin->SetValue(frameStart, FALSE);
			ReleaseISpinner(spin);

			// Setup the spinner controls for the skeletal animation end frame
			spin = GetISpinner(GetDlgItem(hWnd, IDC_SKELETALANIM_END_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd, IDC_SKELETALANIM_END), EDITTYPE_INT); 
			spin->SetLimits(frameStart, frameEnd, TRUE); 
			spin->SetScale(1.0f);
			spin->SetValue(frameEnd, FALSE);
			ReleaseISpinner(spin);

			// Setup the spinner controls for the skeletal animation key sample rate 
			spin = GetISpinner(GetDlgItem(hWnd, IDC_SKELETALANIM_STEP_SPIN)); 
			spin->LinkToEdit(GetDlgItem(hWnd, IDC_SKELETALANIM_STEP), EDITTYPE_INT); 
			spin->SetLimits(1, 100, TRUE); 
			spin->SetScale(1.0f);
			spin->SetValue(theElMaxPlugin.GetSkeletalAnimFrameStep(), FALSE);
			ReleaseISpinner(spin);
		}
		break;

	case WM_DESTROY:
		theElMaxPlugin.Destroy(hWnd);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_EXPORT_MESH:
			if (HIWORD(wParam) == BN_CLICKED)
			{
				char filename[MAX_PATH] = {0};
				if (GetSaveFileName(hWnd, filename, sizeof(filename), _T("ElMesh(*.ELM)\0*.ELM\0All(*.*)\0*.*\0"), _T("ELM"), _T("Mesh file to save")))
				{
					ISpinnerControl* spin = GetISpinner(GetDlgItem(hWnd, IDC_STATIC_FRAME_SPIN));
					theElMaxPlugin.SetStaticFrame(spin->GetIVal());
					ReleaseISpinner(spin);

					theElMaxPlugin.ExportMesh(filename);
				}
			}
			break;
		case IDC_EXPORT_MESHANIM:
			if (HIWORD(wParam) == BN_CLICKED)
			{
				char filename[MAX_PATH] = {0};
				if (GetSaveFileName(hWnd, filename, sizeof(filename), _T("ElMeshAnimation(*.EMA)\0*.EMA\0All(*.*)\0*.*\0"), _T("EMA"), _T("MeshAnimation file to save")))
				{
					ISpinnerControl* spin = GetISpinner(GetDlgItem(hWnd, IDC_STATIC_FRAME_SPIN));
					theElMaxPlugin.SetStaticFrame(spin->GetIVal());
					ReleaseISpinner(spin);

					spin = GetISpinner(GetDlgItem(hWnd, IDC_MESHANIM_START_SPIN));
					theElMaxPlugin.SetMeshAnimFrameStart(spin->GetIVal());
					ReleaseISpinner(spin);

					spin = GetISpinner(GetDlgItem(hWnd, IDC_MESHANIM_END_SPIN));
					theElMaxPlugin.SetMeshAnimFrameEnd(spin->GetIVal());
					ReleaseISpinner(spin);

					spin = GetISpinner(GetDlgItem(hWnd, IDC_MESHANIM_STEP_SPIN));
					theElMaxPlugin.SetMeshAnimFrameStep(spin->GetIVal());
					ReleaseISpinner(spin);

					theElMaxPlugin.ExportMeshAnim(filename);
				}
			}
			break;
		case IDC_EXPORT_SKELETON:
			if (HIWORD(wParam) == BN_CLICKED)
			{
				char filename[MAX_PATH] = {0};
				if (GetSaveFileName(hWnd, filename, sizeof(filename), _T("ElSkeleton(*.ELS)\0*.ELS\0All(*.*)\0*.*\0"), _T("ELS"), _T("Skeleton file to save")))
				{
					ISpinnerControl* spin = GetISpinner(GetDlgItem(hWnd, IDC_STATIC_FRAME_SPIN));
					theElMaxPlugin.SetStaticFrame(spin->GetIVal());
					ReleaseISpinner(spin);

					theElMaxPlugin.ExportSkeleton(filename);
				}
			}
			break;
		case IDC_EXPORT_SKELETALANIM:
			if (HIWORD(wParam) == BN_CLICKED)
			{
				char filename[MAX_PATH] = {0};
				if (GetSaveFileName(hWnd, filename, sizeof(filename), _T("ElSkeletalAnimation(*.ESA)\0*.ESA\0All(*.*)\0*.*\0"), _T("ESA"), _T("SkeletalAnimation file to save")))
				{
					ISpinnerControl* spin = GetISpinner(GetDlgItem(hWnd, IDC_STATIC_FRAME_SPIN));
					theElMaxPlugin.SetStaticFrame(spin->GetIVal());
					ReleaseISpinner(spin);

					spin = GetISpinner(GetDlgItem(hWnd, IDC_SKELETALANIM_START_SPIN));
					theElMaxPlugin.SetSkeletalAnimFrameStart(spin->GetIVal());
					ReleaseISpinner(spin);

					spin = GetISpinner(GetDlgItem(hWnd, IDC_SKELETALANIM_END_SPIN));
					theElMaxPlugin.SetSkeletalAnimFrameEnd(spin->GetIVal());
					ReleaseISpinner(spin);

					spin = GetISpinner(GetDlgItem(hWnd, IDC_SKELETALANIM_STEP_SPIN));
					theElMaxPlugin.SetSkeletalAnimFrameStep(spin->GetIVal());
					ReleaseISpinner(spin);

					theElMaxPlugin.ExportSkeletalAnim(filename);
				}
			}
			break;
		}
		break;

	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MOUSEMOVE:
		theElMaxPlugin.ip->RollupMouseMessage(hWnd,msg,wParam,lParam); 
		break;

	default:
		return 0;
	}
	return 1;
}
Exemple #29
0
// Output float keys if this is a known float controller that
// supports key operations. Otherwise we will sample the controller 
// once for each frame to get the value.
void AsciiExp::DumpFloatKeys(Control* cont, int indentLevel) 
{
	if (!cont)
		return;
	
	int i;
	TSTR indent = GetIndent(indentLevel);
	IKeyControl *ikc = NULL;

	// If the user wants us to always sample, we will ignore the KeyControlInterface
	if (!GetAlwaysSample())
		ikc = GetKeyControlInterface(cont);
	
	// TCB float
	if (ikc && cont->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0)) {
		_ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_FLOAT_TCB); 
		for (i=0; i<ikc->GetNumKeys(); i++) {
			ITCBFloatKey key;
			ikc->GetKey(i, &key);
			_ftprintf(pStream, _T("%s\t\t\t%s %d\t%s"),
				indent.data(),
				ID_TCB_FLOAT_KEY,
				key.time,
				Format(key.val));
			_ftprintf(pStream, _T("\t%s\t%s\t%s\t%s\t%s\n"), Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut));
		}
		_ftprintf(pStream, _T("%s\t\t}\n"), indent.data());
	}
	// Bezier float
	else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0)) {
		_ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_FLOAT_BEZIER); 
		for (i=0; i<ikc->GetNumKeys(); i++) {
			IBezFloatKey key;
			ikc->GetKey(i, &key);
			_ftprintf(pStream, _T("%s\t\t\t%s %d\t%s"),
				indent.data(),
				ID_BEZIER_FLOAT_KEY,
				key.time, 
				Format(key.val));
			_ftprintf(pStream, _T("\t%s\t%s\t%d\n"), Format(key.intan), Format(key.outtan), key.flags);
		}
		_ftprintf(pStream, _T("%s\t\t}\n"), indent.data());
	}
	else if (ikc && cont->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID, 0)) {
		_ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_FLOAT_LINEAR); 
		for (i=0; i<ikc->GetNumKeys(); i++) {
			ILinFloatKey key;
			ikc->GetKey(i, &key);
			_ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"),
				indent.data(),
				ID_FLOAT_KEY,
				key.time,
				Format(key.val));
		}
		_ftprintf(pStream, _T("%s\t\t}\n"), indent.data());
	}
	else {
		
		// Unknown controller, no key interface or sample on demand -
		// This might be a procedural controller or something else we
		// don't know about. The last resort is to get the value from the 
		// controller at every n frames.
		
		TSTR name;
		cont->GetClassName(name);
		TSTR className = FixupName(name);
		Interface14 *iface = GetCOREInterface14();
		UINT codepage  = iface-> DefaultTextSaveCodePage(true);
		const char* className_locale = className.ToCP(codepage);
		_ftprintf(pStream, _T("%s\t\t%s \"%hs\" {\n"), indent.data(), ID_CONTROL_FLOAT_SAMPLE,
			className_locale);
		
		// If it is animated at all...
		if (cont->IsAnimated()) {
			// Get the range of the controller animation 
			Interval range; 
			// Get range of full animation
			Interval animRange = ip->GetAnimRange(); 
			TimeValue t = cont->GetTimeRange(TIMERANGE_ALL).Start();
			float value;
			
			// While we are inside the animation... 
			while (animRange.InInterval(t)) {
				// Sample the controller
				range = FOREVER;
				cont->GetValue(t, &value, range);
				
				// Set time to start of controller validity interval 
				t = range.Start();
				
				// Output the sample
				_ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"),
					indent.data(),
					ID_FLOAT_KEY,
					t,
					Format(value));
				
				// If the end of the controller validity is beyond the 
				// range of the animation
				if (range.End() > cont->GetTimeRange(TIMERANGE_ALL).End()) {
					break;
				}
				else {
					t = (range.End()/GetTicksPerFrame()+GetKeyFrameStep()) * GetTicksPerFrame();
				}
			}
		}
		_ftprintf(pStream, _T("%s\t\t}\n"), indent.data());
	}
}
Exemple #30
0
void AudioBaseControl::Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int etype)
{
	if(type == AUDIO_FLOAT_CONTROL_CLASS_ID1) {
		float fval0, fval1, fval2, res = 0.0f;
		switch (etype) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&fval0,valid);
				GetValueLocalTime(range.Start()+1,&fval1,valid);
				res = LinearExtrapolate(range.Start(),t,fval0,fval1,fval0);				
			} 
			else {
				GetValueLocalTime(range.End()-1,&fval0,valid);
				GetValueLocalTime(range.End(),&fval1,valid);
				res = LinearExtrapolate(range.End(),t,fval0,fval1,fval1);
			}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&fval0,valid);
				res = IdentityExtrapolate(range.Start(),t,fval0);
			} 
			else {
				GetValueLocalTime(range.End(),&fval0,valid);
				res = IdentityExtrapolate(range.End(),t,fval0);
			}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&fval0,valid);
			GetValueLocalTime(range.End(),&fval1,valid);
			GetValueLocalTime(CycleTime(range,t),&fval2,valid);
			res = RepeatExtrapolate(range,t,fval0,fval1,fval2);			
			break;
		}
		valid.Set(t,t);
		*((float*)val) = res;
	}
	else if(type == AUDIO_SCALE_CONTROL_CLASS_ID1) {
		ScaleValue val0, val1, val2, res;
		switch (etype) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				GetValueLocalTime(range.Start()+1,&val1,valid);
				res = LinearExtrapolate(range.Start(),t,val0,val1,val0);				
			} 
			else {
				GetValueLocalTime(range.End()-1,&val0,valid);
				GetValueLocalTime(range.End(),&val1,valid);
				res = LinearExtrapolate(range.End(),t,val0,val1,val1);
			}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				res = IdentityExtrapolate(range.Start(),t,val0);
			} 
			else {
				GetValueLocalTime(range.End(),&val0,valid);
				res = IdentityExtrapolate(range.End(),t,val0);
			}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&val0,valid);
			GetValueLocalTime(range.End(),&val1,valid);
			GetValueLocalTime(CycleTime(range,t),&val2,valid);
			res = RepeatExtrapolate(range,t,val0,val1,val2);			
			break;
		}
		valid.Set(t,t);
		*((ScaleValue *)val) = res;
	}
	else {
		Point3 val0, val1, val2, res;
		switch (etype) {
		case ORT_LINEAR:			
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				GetValueLocalTime(range.Start()+1,&val1,valid);
				res = LinearExtrapolate(range.Start(),t,val0,val1,val0);				
			} 
			else {
				GetValueLocalTime(range.End()-1,&val0,valid);
				GetValueLocalTime(range.End(),&val1,valid);
				res = LinearExtrapolate(range.End(),t,val0,val1,val1);
			}
			break;

		case ORT_IDENTITY:
			if (t<range.Start()) {
				GetValueLocalTime(range.Start(),&val0,valid);
				res = IdentityExtrapolate(range.Start(),t,val0);
			} 
			else {
				GetValueLocalTime(range.End(),&val0,valid);
				res = IdentityExtrapolate(range.End(),t,val0);
			}
			break;

		case ORT_RELATIVE_REPEAT:
			GetValueLocalTime(range.Start(),&val0,valid);
			GetValueLocalTime(range.End(),&val1,valid);
			GetValueLocalTime(CycleTime(range,t),&val2,valid);
			res = RepeatExtrapolate(range,t,val0,val1,val2);			
			break;
		}
		valid.Set(t,t);
		*((Point3 *)val) = res;
	}
}