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;
}
bool PFOperatorSimpleOrientation::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;

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

	// we may need speed channel for "Speed Space" and "Speed Space Follow" types
	IParticleChannelPoint3R* chSpeed;
	int iDir = pblock()->GetInt(kSimpleOrientation_direction, 0);
	if ((iDir == kSO_Speed) || (iDir == kSO_SpeedFollow))
		chSpeed = GetParticleChannelSpeedRInterface(pCont);
	bool bRestrictToAxis = (pblock()->GetInt(kSimpleOrientation_restrictToAxis, 0) != 0);

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

	// the channel of interest
	IParticleChannelQuatW* chOrient = (IParticleChannelQuatW*)chCont->EnsureInterface(PARTICLECHANNELORIENTATIONW_INTERFACE,
																			ParticleChannelQuat_Class_ID,
																			true, PARTICLECHANNELORIENTATIONR_INTERFACE,
																			PARTICLECHANNELORIENTATIONW_INTERFACE, true );
	if (chOrient == NULL) return false;

	RandGenerator* prg = randLinker().GetRandGenerator(pCont);
	Matrix3 m3Orient;
	float fEulerAng[3];
	int iQuant = chAmount->Count();
	for(int i = 0; i < iQuant; i++) {
		if((chNew->IsNew(i)) || (iDir == kSO_SpeedFollow)) { // apply only to new particles or to all for "follow" type
			TimeValue tv = chTime->GetValue(i).TimeValue();
			// set particle direction in user selected direction
			switch(iDir) {
				case kSO_Rand_3D: {
						Point3 p3x = RandSphereSurface(prg);
						Point3 p3y = RandSphereSurface(prg);
						while(p3x == p3y)
							p3y = RandSphereSurface(prg);
						p3y = Normalize(p3y - p3x * DotProd(p3x, p3y));
						Point3 p3z = p3x ^ p3y;
						m3Orient = Matrix3(p3x, p3y, p3z, Point3::Origin);
					}
					break;
				case kSO_Rand_Horiz: {
						fEulerAng[0] = fEulerAng[1] = 0.0f;
						fEulerAng[2] = TWOPI * prg->Rand01();
						EulerToMatrix(fEulerAng, m3Orient, EULERTYPE_XYZ);
					}
					break;
				case kSO_World: {
						fEulerAng[0] = GetPFFloat(pblock(), kSimpleOrientation_x, tv);
						fEulerAng[1] = GetPFFloat(pblock(), kSimpleOrientation_y, tv);
						fEulerAng[2] = GetPFFloat(pblock(), kSimpleOrientation_z, tv);
						EulerToMatrix(fEulerAng, m3Orient, EULERTYPE_XYZ);
					}
					break;
				case kSO_Speed:
				case kSO_SpeedFollow: {
						fEulerAng[0] = GetPFFloat(pblock(), kSimpleOrientation_x, tv);
						fEulerAng[1] = GetPFFloat(pblock(), kSimpleOrientation_y, tv);
						fEulerAng[2] = GetPFFloat(pblock(), kSimpleOrientation_z, tv);
						if (chSpeed != NULL)
							m3Orient = SpeedSpaceMatrix(chSpeed->GetValue(i));
						else
							m3Orient = Matrix3(Point3::XAxis, Point3::YAxis, Point3::ZAxis, Point3::Origin);
//						m3Orient.SetRotate(Quat(m3Orient));
						Matrix3 eulerRot;
						EulerToMatrix(fEulerAng, eulerRot, EULERTYPE_XYZ);
//						m3Orient = m3Orient * eulerRot;
						m3Orient = eulerRot * m3Orient;
					}
					break;
			}
			// account for divergence parameter
			if ((iDir != kSO_SpeedFollow) && (iDir != kSO_Rand_3D)) {
				float fDiv = GetPFFloat(pblock(), kSimpleOrientation_divergence, tv);
				Point3 p3RotAxis = RandSphereSurface(prg);
				if(fDiv > 0.f) {
					if (bRestrictToAxis) {
						p3RotAxis.x = GetPFFloat(pblock(), kSimpleOrientation_axisX, tv);
						p3RotAxis.y = GetPFFloat(pblock(), kSimpleOrientation_axisY, tv);
						p3RotAxis.z = GetPFFloat(pblock(), kSimpleOrientation_axisZ, tv);
						if (LengthSquared(p3RotAxis) > 0.0f) {
							p3RotAxis = Normalize(p3RotAxis);
						} else {
							p3RotAxis = Point3::XAxis;
							fDiv = 0.0f;
						}
					}
					float fRandDiv = fDiv * prg->Rand11();
					m3Orient = m3Orient * RotAngleAxisMatrix(p3RotAxis, fRandDiv);
				} else { // perform operations that change randomness state
					prg->Rand11();
				}
			}
			chOrient->SetValue(i, Quat(m3Orient));
		}
	}

	return true;
}
bool PFOperatorMaterialFrequency::Proceed(IObject* pCont, 
									 PreciseTimeValue timeStart, 
									 PreciseTimeValue& timeEnd,
									 Object* pSystem,
									 INode* pNode,
									 INode* actionNode,
									 IPFIntegrator* integrator)
{
	if (pblock() == NULL) return false;
	int assignID = pblock()->GetInt(kMaterialFrequency_assignID, timeEnd);
	if (assignID == 0) return true; // nothing to assign

	int showInViewport = pblock()->GetInt(kMaterialFrequency_showInViewport, timeEnd);
	if (!showInViewport) { // check if the system is in render; if not then return
		IPFSystem* iSystem = GetPFSystemInterface(pSystem);
		if (iSystem == NULL) return false;
		if (!iSystem->IsRenderState()) return true; // nothing to show in viewport
	}

	// 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 for a particle
	IParticleChannelNewR* chNew = GetParticleChannelNewRInterface(pCont);
	if (chNew == NULL) return false; // can't find newly entered particles for duration calculation
	
	IChannelContainer* chCont = GetChannelContainerInterface(pCont);
	if (chCont == NULL) return false;

	// ensure material index channel
	IParticleChannelIntW* chMtlIDW = (IParticleChannelIntW*)chCont->EnsureInterface(PARTICLECHANNELMTLINDEXW_INTERFACE,
																ParticleChannelInt_Class_ID,
																true, PARTICLECHANNELMTLINDEXR_INTERFACE,
																PARTICLECHANNELMTLINDEXW_INTERFACE, true);
	if (chMtlIDW == NULL) return false; // can't modify Material Index channel in the container
	IParticleChannelIntR* chMtlIDR = GetParticleChannelMtlIndexRInterface(pCont);

	RandGenerator* randGen = randLinker().GetRandGenerator(pCont);

	int i, j, count = chAmount->Count();
	int mtlID;
	float idShare[10], slideShare[10];
	int pblockIDShare[] = { kMaterialFrequency_id1, kMaterialFrequency_id2, kMaterialFrequency_id3, kMaterialFrequency_id4, kMaterialFrequency_id5, kMaterialFrequency_id6, kMaterialFrequency_id7, kMaterialFrequency_id8, kMaterialFrequency_id9, kMaterialFrequency_id10 };

	for(i=0; i<count; i++) {
		if (!chNew->IsNew(i)) continue; // the ID is already set
		TimeValue curTime = chTime->GetValue(i).TimeValue();
		float totalShare = 0.0f;
		for(j=0; j<10; j++) {
			totalShare += (idShare[j] = GetPFFloat(pblock(), pblockIDShare[j], curTime));
			slideShare[j] = totalShare;
		}
		float randomShare = totalShare*randGen->Rand01();
		for(j=0; j<10; j++) {
			mtlID = j;
			if (randomShare < slideShare[j]) break;
		}
		chMtlIDW->SetValue(i, mtlID);
	}
	
	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;
}