예제 #1
0
bool PFOperatorSimpleSpeed::Proceed(IObject* pCont, 
									 PreciseTimeValue timeStart, 
									 PreciseTimeValue& timeEnd,
									 Object* pSystem,
									 INode* pNode,
									 INode* actionNode,
									 IPFIntegrator* integrator)
{
	// acquire all necessary channels, create additional if needed
	IParticleChannelNewR* chNew = GetParticleChannelNewRInterface(pCont);
	if(chNew == NULL) return false;
	IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont);
	if(chTime == NULL) return false;
	IParticleChannelAmountR* chAmount = GetParticleChannelAmountRInterface(pCont);
	if(chAmount == NULL) return false;
	// the position channel may not be present. For some option configurations it is okay
	IParticleChannelPoint3R* chPos = GetParticleChannelPositionRInterface(pCont);
	int iDir = _pblock()->GetInt(kSimpleSpeed_direction, timeStart);
	if ((chPos == NULL) && ((iDir == kSS_Icon_Center_Out) || (iDir == kSS_Icon_Arrow_Out)))
		return false;

	IChannelContainer* chCont;
	chCont = GetChannelContainerInterface(pCont);
	if (chCont == NULL) return false;

	// the channel of interest
	bool initSpeed = false;
	IParticleChannelPoint3W* chSpeed = (IParticleChannelPoint3W*)chCont->EnsureInterface(PARTICLECHANNELSPEEDW_INTERFACE,
																			ParticleChannelPoint3_Class_ID,
																			true, PARTICLECHANNELSPEEDR_INTERFACE,
																			PARTICLECHANNELSPEEDW_INTERFACE, true,
																			actionNode, (Object*)NULL, &initSpeed);
	IParticleChannelPoint3R* chSpeedR = GetParticleChannelSpeedRInterface(pCont);
	if ((chSpeed == NULL) || (chSpeedR == NULL)) return false;

	// there are no new particles
	if (chNew->IsAllOld()) return true;

	float fUPFScale = 1.0f/TIME_TICKSPERSEC; // conversion units per seconds to units per tick
	Point3 pt3SpeedVec;
	RandGenerator* prg = randLinker().GetRandGenerator(pCont);
	int iQuant = chAmount->Count();
	bool wasIgnoringEmitterTMChange = IsIgnoringEmitterTMChange();
	if (!wasIgnoringEmitterTMChange) SetIgnoreEmitterTMChange();
	for(int i = 0; i < iQuant; i++) {
		if(chNew->IsNew(i)) { // apply only to new particles
			TimeValue tv = chTime->GetValue(i).TimeValue();
			Matrix3 nodeTM = pNode->GetObjectTM(tv);
			float fSpeedParam = fUPFScale * GetPFFloat(pblock(), kSimpleSpeed_speed, tv);
			// change speed in user selected direction
			switch(iDir) {
				case kSS_Along_Icon_Arrow: {
						// icon arrow appears to be in the negative z direction
						pt3SpeedVec = -Normalize(nodeTM.GetRow(2));
					}
					break;
				case kSS_Icon_Center_Out: {
						Point3 pt3IconCenter = nodeTM.GetTrans();
						Point3 pt3PartPos = chPos->GetValue(i);
						pt3SpeedVec = Normalize(pt3PartPos - pt3IconCenter);
					}
					break;
				case kSS_Icon_Arrow_Out: {
						Point3 pt3PartPos = chPos->GetValue(i);
						Point3 pt3ArrowVec = nodeTM.GetRow(2);
						Point3 pt3Tmp = CrossProd(pt3PartPos - nodeTM.GetTrans(), pt3ArrowVec);
						pt3SpeedVec = Normalize(CrossProd(pt3ArrowVec, pt3Tmp));
					}
					break;
				case kSS_Rand_3D: {
						pt3SpeedVec = RandSphereSurface(prg);
					}
					break;
				case kSS_Rand_Horiz: {
						float fAng = TWOPI * prg->Rand01();
						// establish x, y coordinates of random angle, z component zero
						float x = cos(fAng); float y = sin(fAng); float z = 0.0f;
						pt3SpeedVec = Point3(x, y, z);
					}
					break;
				case kSS_Inherit_Prev: {
						if (initSpeed) 
							pt3SpeedVec = Point3::Origin;
						else
							pt3SpeedVec = Normalize(chSpeedR->GetValue(i));
					}
					break;
			}
			// account for reverse check box
			int iRev = _pblock()->GetInt(kSimpleSpeed_reverse, 0);
			float fDirMult = iRev > 0 ? -1.f : 1.f;
			// calculate variation
			float fVar = fUPFScale * GetPFFloat(pblock(), kSimpleSpeed_variation, tv);
			if(fVar > 0.f)
				fSpeedParam = fSpeedParam + fVar * prg->Rand11();
			pt3SpeedVec = fDirMult * fSpeedParam * pt3SpeedVec;
			// calculate divergence
			float fDiv = GetPFFloat(pblock(), kSimpleSpeed_divergence, tv);
			pt3SpeedVec = DivergeVectorRandom(pt3SpeedVec, prg, fDiv);

			chSpeed->SetValue(i, pt3SpeedVec);
		}
	}
	if (!wasIgnoringEmitterTMChange) ClearIgnoreEmitterTMChange();

	return true;
}
예제 #2
0
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFTest									 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestDuration::Proceed(IObject* pCont, 
							PreciseTimeValue timeStart, 
							PreciseTimeValue& timeEnd, 
							Object* pSystem, 
							INode* pNode, 
							INode* actionNode, 
							IPFIntegrator* integrator, 
							BitArray& testResult, 
							Tab<float>& testTime)
{
	TimeValue proceedTime = timeStart;
	int testType	= pblock()->GetInt(kDuration_testType, proceedTime);
	int disparity	= pblock()->GetInt(kDuration_disparity, proceedTime);

	// get channel container interface
	IChannelContainer* chCont;
	chCont = GetChannelContainerInterface(pCont);
	if (chCont == NULL) return false;

	// acquire absolutely necessary particle channels
	IParticleChannelAmountR* chAmount = GetParticleChannelAmountRInterface(pCont);
	if (chAmount == NULL) return false; // can't find number of particles in the container
	IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont);
	if (chTime == NULL) return false; // can't read timing info for a particle
	IParticleChannelNewR* chNew = GetParticleChannelNewRInterface(pCont);
	if (chNew == NULL) return false; // can't find newly entered particles for duration calculation

	// acquire more particle channels
	IParticleChannelPTVR* chBirthTime = NULL;
	if (testType == kDuration_testType_age)
	{
		chBirthTime = GetParticleChannelBirthTimeRInterface(pCont);
		if (chBirthTime == NULL) return false; // can't read particle age
	}
	IParticleChannelIDR* chID = NULL;
	if (disparity != 0)
	{
		chID = GetParticleChannelIDRInterface(pCont);
		if (chID == NULL) return false; // can't read particle index for first-to-last disparity
	}

	IParticleChannelPTVR* chEventStartR = NULL;
	IParticleChannelPTVW* chEventStartW = NULL;
	bool initEventStart = false;
	if (testType == kDuration_testType_event) {
		chEventStartR = (IParticleChannelPTVR*)chCont->EnsureInterface(PARTICLECHANNELEVENTSTARTR_INTERFACE,
																		ParticleChannelPTV_Class_ID,
																		true, PARTICLECHANNELEVENTSTARTR_INTERFACE,
																		PARTICLECHANNELEVENTSTARTW_INTERFACE, false,
																		actionNode, NULL, &initEventStart);
		if (chEventStartR == NULL) return false; // can't read event start time
		if (initEventStart) {
			chEventStartW = GetParticleChannelEventStartWInterface(pCont);
			if (chEventStartW == NULL) return false; // can't write event start time
		}
	}

	// acquire TestDuration private particle channel; if not present then create it		
	IParticleChannelPTVW* chTestW = (IParticleChannelPTVW*)chCont->EnsureInterface(PARTICLECHANNELTESTDURATIONW_INTERFACE,
																			ParticleChannelPTV_Class_ID,
																			true, PARTICLECHANNELTESTDURATIONR_INTERFACE,
																			PARTICLECHANNELTESTDURATIONW_INTERFACE, false,
																			actionNode, (Object*)this);
	IParticleChannelPTVR* chTestR = (IParticleChannelPTVR*)chCont->GetPrivateInterface(PARTICLECHANNELTESTDURATIONR_INTERFACE, (Object*)this);
	if ((chTestR == NULL) || (chTestW == NULL)) return false; // can't set test value for newly entered particles

	int i, count;
	PreciseTimeValue curTestValue;
	count = chAmount->Count();
	
	// check if all particles are "old". If some particles are "new" then we
	// have to calculate test values for those.
	if (!chNew->IsAllOld())
	{
		TimeValue testValue	= pblock()->GetTimeValue(kDuration_testValue, proceedTime);
		TimeValue variation = pblock()->GetTimeValue(kDuration_variation, proceedTime);
		int subframe		= pblock()->GetInt(kDuration_subframeSampling, proceedTime);
		TimeValue testFirst = pblock()->GetInt(kDuration_testFirst, proceedTime);
		TimeValue testLast	= pblock()->GetInt(kDuration_testLast, proceedTime);
		TimeValue lastIndex = pblock()->GetInt(kDuration_lastIndex, proceedTime);
		if (lastIndex <= 0) lastIndex = 1;
		int tpf = GetTicksPerFrame();

		RandGenerator* randGen = NULL;
		if (variation != 0) randGen = randLinker().GetRandGenerator(pCont);
		
		int index;
		for(i=0; i<count; i++) 
			if (chNew->IsNew(i)) // calculate test value only for new particles
			{
				if (disparity) {
					index = chID->GetParticleIndex(i);
					if (index > lastIndex) 
						curTestValue = PreciseTimeValue( testLast );
					else 
						curTestValue = PreciseTimeValue( testFirst + (testLast-testFirst)*(float(index)/lastIndex) );
				} else
					curTestValue = PreciseTimeValue( testValue );

				if (variation != 0)
					curTestValue += PreciseTimeValue( randGen->Rand11()*variation );

				// adjust test value according to test type
				if (testType == kDuration_testType_age)
					curTestValue += chBirthTime->GetValue(i);
				else if (testType == kDuration_testType_event) {
					if (initEventStart)
						chEventStartW->SetValue(i, chTime->GetValue(i));
					curTestValue += chEventStartR->GetValue(i);
				}

				if (!subframe) // round the test value to the nearest frame
					curTestValue = PreciseTimeValue(int(floor(TimeValue(curTestValue)/float(tpf) + 0.5f) * tpf));

				chTestW->SetValue(i, curTestValue);
			}
	}
	
	// test all particles
	PreciseTimeValue curParticleValue;
	testResult.SetSize(count);
	testResult.ClearAll();
	testTime.SetCount(count);
	int condType = pblock()->GetInt(kDuration_conditionType, proceedTime);
	BitArray particlesToAdvance;
	particlesToAdvance.SetSize(count);
	particlesToAdvance.ClearAll();
	for(i=0; i<count; i++)
	{
		curTestValue = chTestR->GetValue(i);
		curParticleValue = chTime->GetValue(i);
		if (curParticleValue > timeEnd) continue; // particle has been proceeded beyond 
												  // the testing interval [timeStart,timeEnd]
		switch( condType )
		{
		case kDuration_conditionType_less:
			if (curParticleValue <= curTestValue)
			{ // particle doesn't need to be advanced in "time" since the current time value is the condition value
				testResult.Set(i);
				testTime[i] = float( curParticleValue - timeStart );
			}
			break;
		case kDuration_conditionType_greater:
			if (timeEnd < curTestValue) break;	// particle won't satisfy the condition 
												// even at the end of the test interval			
			if (curParticleValue >= curTestValue)
			{ // particle doesn't need to be advanced in "time" since the current time value is more than satisfactory
				testResult.Set(i);
				testTime[i] = float( curParticleValue - timeStart );
				break;
			}

			testResult.Set(i);
			testTime[i] = float( curTestValue - timeStart );
			// the particle needs to be advanced in time if possible
			particlesToAdvance.Set(i);
			break;
		default:
			DbgAssert(0);
			break;
		}
	}

	// advance particles in time if they satisfy the condition and need to be pushed forward
	if (integrator != NULL)
	{
		Tab<PreciseTimeValue> timeToAdvance;
		timeToAdvance.SetCount(count);
		for(i=0; i<count; i++)
			if (particlesToAdvance[i] != 0)
				timeToAdvance[i] = timeStart + testTime[i];
		integrator->Proceed(pCont, timeToAdvance, particlesToAdvance);
	}

	return true;
}
예제 #3
0
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFOperator								 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestGoToRotation::Proceed(IObject* pCont, 
									 PreciseTimeValue timeStart, 
									 PreciseTimeValue& timeEnd,
									 Object* pSystem,
									 INode* pNode,
									 INode* actionNode,
									 IPFIntegrator* integrator)
{
	if (postProceed()) return doPostProceed(pCont, timeStart, timeEnd, pSystem, pNode, actionNode, integrator);

	if (pblock() == NULL) return false;
	IChannelContainer* chCont;
	chCont = GetChannelContainerInterface(pCont);
	if (chCont == NULL) return false;

	// acquire absolutely necessary particle channels
	IParticleChannelAmountR* chAmount = GetParticleChannelAmountRInterface(pCont);
	if (chAmount == NULL) return false; // can't find number of particles in the container
	int i, count = chAmount->Count();
	if (count == 0) return true; // no particles to modify
	IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont);
	if (chTime == NULL) return false; // can't read timing info for a particle
	IParticleChannelNewR* chNew = GetParticleChannelNewRInterface(pCont);
	if (chNew == NULL) return false; // can't find newly entered particles for speedGoToTarget calculation
	IParticleChannelPTVR* chBirth = GetParticleChannelBirthTimeRInterface(pCont);
	if (chBirth == NULL) return false; // can't read birth time data
	IParticleChannelQuatR* chOrient = GetParticleChannelOrientationRInterface(pCont);
	if (chOrient == NULL) return false; // can't read current orientation for a particle

	// may create and initialize spin channel if it is not present
	IParticleChannelAngAxisR* chSpinR = NULL;
	IParticleChannelAngAxisW* chSpinW = NULL;
	bool initSpin = false;
	chSpinR = (IParticleChannelAngAxisR*)chCont->EnsureInterface(PARTICLECHANNELSPINR_INTERFACE,
																ParticleChannelAngAxis_Class_ID,
																true, PARTICLECHANNELSPINR_INTERFACE,
																PARTICLECHANNELSPINW_INTERFACE, true,
																actionNode, NULL, &initSpin);
	if (chSpinR == NULL) return false; // can't read spin data
	if (initSpin) {
		chSpinW = GetParticleChannelSpinWInterface(pCont);
		if (chSpinW == NULL) return false; // can't modify spin data
	}
	if (initSpin) {
		AngAxis aa(Point3::XAxis, 0.0f);
		if (!chNew->IsAllOld())
			for(i=0; i<count; i++) {
				if (chNew->IsNew(i))
					chSpinW->SetValue(i, aa);
			}
	}

	// create channel to store the start moment of the transition process
	// the time is when a particle enters the event
	IParticleChannelPTVW* chStartTimeW = NULL;
	bool initStartTime = false;
	chStartTimeW = (IParticleChannelPTVW*)chCont->EnsureInterface(PARTICLECHANNELSTARTTIMEW_INTERFACE,
																ParticleChannelPTV_Class_ID,
																true, PARTICLECHANNELSTARTTIMER_INTERFACE,
																PARTICLECHANNELSTARTTIMEW_INTERFACE, true,
																actionNode, (Object*)this, &initStartTime);
	if (chStartTimeW == NULL) return false; // can't modify the start time
	
	// create channel to store the end moment of the transition process
	// the time is used to determine when a particle should go to the next event, and when to finish the transition process
	IParticleChannelPTVW* chEndTimeW = NULL;
	bool initEndTime = false;
	chEndTimeW = (IParticleChannelPTVW*)chCont->EnsureInterface(PARTICLECHANNELENDTIMEW_INTERFACE,
																ParticleChannelPTV_Class_ID,
																true, PARTICLECHANNELENDTIMER_INTERFACE,
																PARTICLECHANNELENDTIMEW_INTERFACE, true,
																actionNode, (Object*)this, &initEndTime);
	if (chEndTimeW == NULL) return false; // can't modify the end time

	// create channel to store info about the last time for each particle for the proceed function
	// the data is used to rollback the effect of integration to find the desirable orientation
	IParticleChannelPTVW* chProceedTimeW = NULL;
	chProceedTimeW = (IParticleChannelPTVW*)chCont->EnsureInterface(PARTICLECHANNELPROCEEDTIMEW_INTERFACE,
																ParticleChannelPTV_Class_ID,
																true, PARTICLECHANNELPROCEEDTIMER_INTERFACE,
																PARTICLECHANNELPROCEEDTIMEW_INTERFACE, false,
																actionNode, (Object*)this);
	if (chProceedTimeW == NULL) return false; // can't modify the proceed time
	for(i=0; i<count; i++) chProceedTimeW->SetValue(i, chTime->GetValue(i));

	// create channel to store info if the final rotation has been initialized
	IParticleChannelBoolW* chGotInitW = NULL;
	bool initGotInit = false;
	chGotInitW = (IParticleChannelBoolW*)chCont->EnsureInterface(PARTICLECHANNELGOTINITW_INTERFACE,
															ParticleChannelBool_Class_ID,
															true, PARTICLECHANNELGOTINITR_INTERFACE,
															PARTICLECHANNELGOTINITW_INTERFACE, true,
															actionNode, (Object*)this, &initGotInit);
	if (chGotInitW == NULL) return false; // can't modify if init data

	// create channel to store initial rotation
	IParticleChannelQuatW* chStartRotW = NULL;
	bool initStartRot = false;
	chStartRotW = (IParticleChannelQuatW*)chCont->EnsureInterface(PARTICLECHANNELSTARTROTW_INTERFACE,
																ParticleChannelQuat_Class_ID,
																true, PARTICLECHANNELSTARTROTR_INTERFACE,
																PARTICLECHANNELSTARTROTW_INTERFACE, true,
																actionNode, (Object*)this, &initStartRot);
	if (chStartRotW == NULL) return false; // can't modify the start rotation

	// create channel to store end rotation
	IParticleChannelQuatW* chEndRotW = NULL;
	bool initEndRot = false;
	chEndRotW = (IParticleChannelQuatW*)chCont->EnsureInterface(PARTICLECHANNELENDROTW_INTERFACE,
																ParticleChannelQuat_Class_ID,
																true, PARTICLECHANNELENDROTR_INTERFACE,
																PARTICLECHANNELENDROTW_INTERFACE, true,
																actionNode, (Object*)this, &initEndRot);
	if (chEndRotW == NULL) return false; // can't modify the end rotation

	// create channel to store initial spin
	IParticleChannelAngAxisW* chStartSpinW = NULL;
	bool initStartSpin = false;
	chStartSpinW = (IParticleChannelAngAxisW*)chCont->EnsureInterface(PARTICLECHANNELSTARTSPINW_INTERFACE,
																ParticleChannelAngAxis_Class_ID,
																true, PARTICLECHANNELSTARTSPINR_INTERFACE,
																PARTICLECHANNELSTARTSPINW_INTERFACE, true,
																actionNode, (Object*)this, &initStartSpin);
	if (chStartSpinW == NULL) return false; // can't modify the start rotation
	
	// create channel to store final spin rate as a float
	IParticleChannelFloatW* chEndSpinW = NULL;
	bool initEndSpin = false;
	chEndSpinW = (IParticleChannelFloatW*)chCont->EnsureInterface(PARTICLECHANNELENDSPINW_INTERFACE,
																ParticleChannelFloat_Class_ID,
																true, PARTICLECHANNELENDSPINR_INTERFACE,
																PARTICLECHANNELENDSPINW_INTERFACE, true,
																actionNode, (Object*)this, &initEndSpin);
	if (chEndSpinW == NULL) return false; // can't modify the start rotation

	int sync = pblock()->GetInt(kGoToRotation_syncBy, timeEnd);
	TimeValue time = pblock()->GetTimeValue(kGoToRotation_time, timeEnd);
	TimeValue timeVar = pblock()->GetTimeValue(kGoToRotation_variation, timeEnd);
	int matchSpin = pblock()->GetInt(kGoToRotation_matchSpin, timeEnd);
	float spin = GetPFFloat(pblock(), kGoToRotation_spin, timeEnd.TimeValue())/TIME_TICKSPERSEC;
	float spinVar = GetPFFloat(pblock(), kGoToRotation_spinVariation, timeEnd.TimeValue())/TIME_TICKSPERSEC;

	RandGenerator* randGen = randLinker().GetRandGenerator(pCont);
	if (randGen == NULL) return false;

	if (!chNew->IsAllOld()) {
		for(i=0; i<count; i++) {
			if (!chNew->IsNew(i)) continue;
			if (initStartTime) 
				chStartTimeW->SetValue(i, chTime->GetValue(i) );
			if (initEndTime) {
				PreciseTimeValue endTime(time);
				switch(sync) {
				case kGoToRotation_syncBy_age:
					endTime += chBirth->GetValue(i);
					break;
				case kGoToRotation_syncBy_event:
					endTime += chTime->GetValue(i);
					break;
				}
				if (timeVar > 0) {
					int sign = randGen->RandSign();
					endTime += PreciseTimeValue(sign*randGen->Rand0X(timeVar));
				} else {
					randGen->RandSign();
					randGen->Rand0X(10);
				}
				chEndTimeW->SetValue(i, endTime);
			}
			if (initGotInit)
				chGotInitW->SetValue(i, false);
			if (initStartRot)
				chStartRotW->SetValue(i, chOrient->GetValue(i));
			if (initEndRot)
				chEndRotW->SetValue(i, chOrient->GetValue(i));
			if (initStartSpin)
				chStartSpinW->SetValue(i, chSpinR->GetValue(i));
			if (initEndSpin) {
				float endSpin = 0;
				if (matchSpin) {
					AngAxis aa = chSpinR->GetValue(i);
					endSpin = aa.angle;
				} else endSpin = spin;
				if (spinVar > 0.0f) endSpin += spinVar*randGen->Rand11();
				else randGen->Rand11();
				chEndSpinW->SetValue(i, endSpin);
			}
		}
	}

	return true;
}
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFTest									 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestSplitByAmount::Proceed(IObject* pCont, 
							PreciseTimeValue timeStart, 
							PreciseTimeValue& timeEnd, 
							Object* pSystem, 
							INode* pNode, 
							INode* actionNode, 
							IPFIntegrator* integrator, 
							BitArray& testResult, 
							Tab<float>& testTime)
{
	int contIndex;
	if (!hasParticleContainer(pCont, contIndex)) return false;
	_lastUpdate(contIndex) = timeEnd.TimeValue();
	bool exactStep = IsExactIntegrationStep(timeEnd, pSystem);

	// update all other systems to the current time; everybody should be in sync
	// for proper accumulation amounts
	int i;
	for(i=0; i<allParticleContainers().Count(); i++) {
		if (allParticleContainer(i) == pCont) continue;
		if (allSystemNode(i) == pNode) continue;
		if (lastUpdate(i) == timeEnd.TimeValue()) continue;
		TimeValue timeToUpdateTo = timeEnd.TimeValue();
		allSystemNode(i)->NotifyDependents(FOREVER, PartID(&timeToUpdateTo), kPFMSG_UpdateToTime, NOTIFY_ALL, TRUE );
	}

	// get channel container interface
	IChannelContainer* chCont;
	chCont = GetChannelContainerInterface(pCont);
	if (chCont == NULL) return false;

	// acquire absolutely necessary particle channels
	IParticleChannelAmountR* chAmount = GetParticleChannelAmountRInterface(pCont);
	if (chAmount == NULL) return false; // can't find number of particles in the container
	IParticleChannelNewR* chNew = GetParticleChannelNewRInterface(pCont);
	if (chNew == NULL) return false; // can't find "new" property of particles in the container

	// acquire TestSplitByAmount private particle channel; if not present then create it		
	IParticleChannelBoolW* chTestW = (IParticleChannelBoolW*)chCont->EnsureInterface(PARTICLECHANNELTESTSPLITBYAMOUNTW_INTERFACE,
																			ParticleChannelBool_Class_ID,
																			true, PARTICLECHANNELTESTSPLITBYAMOUNTR_INTERFACE,
																			PARTICLECHANNELTESTSPLITBYAMOUNTW_INTERFACE, false,
																			actionNode, (Object*)this);
	IParticleChannelBoolR* chTestR = (IParticleChannelBoolR*)chCont->GetPrivateInterface(PARTICLECHANNELTESTSPLITBYAMOUNTR_INTERFACE, (Object*)this);
	if ((chTestR == NULL) || (chTestW == NULL)) return false; // can't set test value for newly entered particles

	int count = chAmount->Count();
	
	// check if all particles are "old". If some particles are "new" then we
	// have to calculate test values for those.
	if (!chNew->IsAllOld())
	{
		RandGenerator* randGen = randLinker().GetRandGenerator(pCont);
		if (randGen == NULL) return false;

		int testType	= pblock()->GetInt(kSplitByAmount_testType, timeStart);
		float fraction = GetPFFloat(pblock(), kSplitByAmount_fraction, timeStart);
		int everyN = GetPFInt(pblock(), kSplitByAmount_everyN, timeStart);
		int firstN = pblock()->GetInt(kSplitByAmount_firstN, timeStart);
		bool perSource = (pblock()->GetInt(kSplitByAmount_perSource, timeStart) != 0);
		int curWentThru = perSource ? wentThruTotal(pNode) : wentThruTotal();

		// number of "first N" particles is adjusted by multiplier coefficient
		// of the master particle system. This is done to make "first N"
		// parameter to be consistent to "total" number of particles acclaimed
		// by a birth operator
		IPFSystem* pfSys = PFSystemInterface(pSystem);
		if (pfSys == NULL) return false; // no handle for PFSystem interface
		firstN *= pfSys->GetMultiplier(timeStart); 

		for(i=0; i<count; i++) {
			if (chNew->IsNew(i)) { // calculate test value only for new particles
				bool sendOut = false;
				switch(testType) {
				case kSplitByAmount_testType_fraction:
					sendOut = (randGen->Rand01() <= fraction);
					break;
				case kSplitByAmount_testType_everyN:
					_wentThruAccum(contIndex) += 1;
					if (wentThruAccum(contIndex) >= everyN) {
						sendOut = true;
						_wentThruAccum(contIndex) = 0;
					}
					break;
				case kSplitByAmount_testType_firstN:
					_wentThruTotal(contIndex) += 1;
					if (curWentThru++ < firstN) sendOut = true;
					break;
				case kSplitByAmount_testType_afterFirstN:
					_wentThruTotal(contIndex) += 1;
					if (curWentThru++ >= firstN) sendOut = true;
					break;
				}
				chTestW->SetValue(i, sendOut);
			}
		}
	}

	// check all particles by predefined test channel
	testResult.SetSize(count);
	testResult.ClearAll();
	testTime.SetCount(count);
	if (exactStep) {
		for(i=0; i<count; i++)
		{	
			if (chTestR->GetValue(i)) {
				testResult.Set(i);
				testTime[i] = 0.0f;
			}
		}
	}
	return true;
}