//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFTest									 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestGoToNextEvent::Proceed(IObject* pCont, 
							PreciseTimeValue timeStart, 
							PreciseTimeValue& timeEnd, 
							Object* pSystem, 
							INode* pNode, 
							INode* actionNode, 
							IPFIntegrator* integrator, 
							BitArray& testResult, 
							Tab<float>& testTime)
{
	int conditionType	= pblock()->GetInt(kGoToNextEvent_conditionType, timeStart);
	bool exactStep = IsExactIntegrationStep(timeEnd, pSystem);

	// 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

	int count = chAmount->Count();
	if (count <= 0) return true;

	testResult.SetSize(count);
	testTime.SetCount(count);
	if((conditionType == kGoToNextEvent_conditionType_all) && exactStep) {
		testResult.SetAll();
		for(int i=0; i<count; i++) testTime[i] = 0.0f;
	} else {
		testResult.ClearAll();
	}

	return true;
}
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFTest									 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestSplitSelected::Proceed(IObject* pCont, 
							PreciseTimeValue timeStart, 
							PreciseTimeValue& timeEnd, 
							Object* pSystem, 
							INode* pNode, 
							INode* actionNode, 
							IPFIntegrator* integrator, 
							BitArray& testResult, 
							Tab<float>& testTime)
{
	bool exactStep = IsExactIntegrationStep(timeEnd, pSystem);
	int conditionType	= pblock()->GetInt(kSplitSelected_conditionType, timeStart);

	// acquire absolutely necessary particle channels
	IParticleChannelAmountR* chAmount = GetParticleChannelAmountRInterface(pCont);
	if (chAmount == NULL) return false; // can't find number of particles in the container
	int count = chAmount->Count();
	IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont);
	if (chTime == NULL) return false; // can't read timing info for a particle
	IParticleChannelBoolR* chSelect = GetParticleChannelSelectionRInterface(pCont);

	// test all particles
	testResult.SetSize(count);
	testResult.ClearAll();
	testTime.SetCount(count);
	for(int i=0; i<count; i++)
	{
		bool selected = (chSelect != NULL) ? chSelect->GetValue(i) : false;
		bool sendOut = ((selected && (conditionType == kSplitSelected_conditionType_selected)) 
						|| (!selected && (conditionType == kSplitSelected_conditionType_notSelected)));
		if (sendOut && exactStep) {
			testResult.Set(i);
			testTime[i] = 0.0f;
		}
	}
	return true;
}
bool PFOperatorMaterialStatic::Proceed(IObject* pCont, 
									 PreciseTimeValue timeStart, 
									 PreciseTimeValue& timeEnd,
									 Object* pSystem,
									 INode* pNode,
									 INode* actionNode,
									 IPFIntegrator* integrator)
{
	if (pblock() == NULL) return false;
	int assignID = pblock()->GetInt(kMaterialStatic_assignID, timeEnd);
	if (assignID == 0) return true; // nothing to assign

	int showInViewport = pblock()->GetInt(kMaterialStatic_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
	}

	int type = pblock()->GetInt(kMaterialStatic_type, timeEnd);

	// 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 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 materialStatic 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 MaterialStatic Index channel in the container
	IParticleChannelIntR* chMtlIDR = GetParticleChannelMtlIndexRInterface(pCont);

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

	int rateType = pblock()->GetInt(kMaterialStatic_rateType, timeEnd);
	int numSubMtls = pblock()->GetInt(kMaterialStatic_numSubMtls, timeEnd);
	if (numSubMtls < 1) numSubMtls = 1;
	int cycleLoop = pblock()->GetInt(kMaterialStatic_loop, timeEnd);
	float rateSec = pblock()->GetFloat(kMaterialStatic_ratePerSecond, timeEnd);
	float ratePart = pblock()->GetFloat(kMaterialStatic_ratePerParticle, timeEnd);
	int mtlID;

	bool useRatePerSec = ((type != kMaterialStatic_type_id) && (rateType == kMaterialStatic_rateType_second));
	bool useRatePerPart = ((type != kMaterialStatic_type_id) && (rateType == kMaterialStatic_rateType_particle));

	bool initRands = false;
	// recalc offset if necessary
	if (_offsetTime(pCont) == TIME_NegInfinity) {
		_offsetTime(pCont) = timeStart.TimeValue();
		// find the earliest time of the coming particles
		PreciseTimeValue minTime = chTime->GetValue(0);
		for(i=1; i<count; i++)
			if (minTime > chTime->GetValue(i)) 
				minTime = chTime->GetValue(i);
		if (useRatePerSec)
			if (type == kMaterialStatic_type_cycle)
				_cycleOffset(pCont) = float(PreciseTimeValue(_offsetTime(pCont)) - minTime)*rateSec/TIME_TICKSPERSEC;
		initRands = true;
	} else if (_offsetTime(pCont) != timeStart.TimeValue()) {
		if (useRatePerSec) {
			if (type == kMaterialStatic_type_cycle) {
				float timeDelta = float(timeStart.TimeValue() - _offsetTime(pCont));
				float addOffset = timeDelta*rateSec/TIME_TICKSPERSEC;
				_cycleOffset(pCont) += addOffset;
				if (_cycleOffset(pCont) >= numSubMtls) {
					if (cycleLoop) _cycleOffset(pCont) -= numSubMtls*int(_cycleOffset(pCont)/numSubMtls);
					else _cycleOffset(pCont) = numSubMtls - 1.0f;
				}
			}
		}
		_offsetTime(pCont) = timeStart.TimeValue();
		initRands = true;
	}
	if (initRands && useRatePerSec && (type == kMaterialStatic_type_random)) {
		int intervalDelta = int(timeEnd.TimeValue() - timeStart.TimeValue()) + 1;
		_randMtlIndex(pCont).SetCount(intervalDelta);
		float curOffset = _cycleOffset(pCont);
		int curMtlID = int(curOffset);
		_randMtlIndex(pCont)[0] = curMtlID;
		for(int i=1; i<intervalDelta; i++) {
			float addOffset = rateSec/TIME_TICKSPERSEC;
			curOffset += addOffset;
			if (int(curOffset) != curMtlID) {
				curOffset = randGen->Rand0X(numSubMtls-1) + (curOffset - floor(curOffset));
				curMtlID = int(curOffset);
			}
			_randMtlIndex(pCont)[i] = curMtlID;
			_cycleOffset(pCont) = curOffset;
		}
	}

	float curCycleOffset = _cycleOffset(pCont);
	float ratePerPart = pblock()->GetFloat(kMaterialStatic_ratePerParticle, timeEnd);

	for(i=0; i<count; i++) {
		if (!chNew->IsNew(i)) continue; // the ID is already set
		switch(type) {
		case kMaterialStatic_type_id:
			mtlID = GetPFInt(pblock(), kMaterialStatic_materialID, chTime->GetValue(i).TimeValue());
			mtlID--;
			break;
		case kMaterialStatic_type_cycle:
			if (rateType == kMaterialStatic_rateType_second) {
				float timeDelta = float(chTime->GetValue(i) - timeStart);
				float addOffset = timeDelta*rateSec/TIME_TICKSPERSEC;
				mtlID = int(curCycleOffset + addOffset);
				if (mtlID >= numSubMtls) {
					if (cycleLoop) mtlID = mtlID%numSubMtls;
					else mtlID = numSubMtls - 1;
				}
			} else { // per particle rate type
				mtlID = int(curCycleOffset);
				if (mtlID >= numSubMtls) {
					if (cycleLoop) mtlID = mtlID%numSubMtls;
					else mtlID = numSubMtls - 1;
				}
				if (ratePart > 0.0f) {
					curCycleOffset += 1.0f/ratePart;
					if (curCycleOffset >= numSubMtls) {
						if (cycleLoop) curCycleOffset -= numSubMtls*int(curCycleOffset/numSubMtls);
						else curCycleOffset = numSubMtls - 1.0f;
					}
				}
			}
			break;
		case kMaterialStatic_type_random:
			if (rateType == kMaterialStatic_rateType_second) {
				int timeDelta = int( chTime->GetValue(i) - timeStart.TimeValue() );
				mtlID = _randMtlIndex(pCont)[timeDelta];
			} else { // per particle rate type
				mtlID = int(curCycleOffset);
				int oldMtlID = mtlID;
				if (mtlID >= numSubMtls) {
					if (cycleLoop) mtlID = mtlID%numSubMtls;
					else mtlID = numSubMtls - 1;
				}
				if (ratePart > 0.0f) {
					curCycleOffset += 1.0f/ratePart;
					if (int(curCycleOffset) != oldMtlID)
						curCycleOffset = randGen->Rand0X(numSubMtls-1) + (curCycleOffset - floor(curCycleOffset));
				}
			}
			break;
		default: DbgAssert(0);
		}
		if (mtlID < 0) mtlID = 0;
		chMtlIDW->SetValue(i, mtlID);
	}

	if (useRatePerPart)
		_cycleOffset(pCont) = curCycleOffset;
	
	return true;
}
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;
}
void AlembicPoints::ReadShapeFromOperator( IParticleGroup *particleGroup, PFSimpleOperator *pSimpleOperator, int particleId, TimeValue ticks, ShapeType &type, Abc::uint16_t &instanceId, float &animationTime)
{
	if(!pSimpleOperator){
		return;
	}

	if (pSimpleOperator->ClassID() == PFOperatorSimpleShape_Class_ID)
    {
        IParamBlock2 *pblock = pSimpleOperator->GetParamBlockByID(0);
        int nShapeId = pblock->GetInt(PFlow_kSimpleShape_shape, ticks);

        switch(nShapeId)
        {
        case PFlow_kSimpleShape_shape_pyramid:
            type = ShapeType_Cone;
            break;
        case PFlow_kSimpleShape_shape_cube:
            type = ShapeType_Box;
            break;
        case PFlow_kSimpleShape_shape_sphere:
            type = ShapeType_Sphere;
            break;
        case PFlow_kSimpleShape_shape_vertex:
            type = ShapeType_Point;
            break;
		default:
			type = ShapeType_Point;
        }
    }
	else if (pSimpleOperator->ClassID() == PFOperatorShapeLib_Class_ID)
	{
        IParamBlock2 *pblock = pSimpleOperator->GetParamBlockByID(0);
		int nDimension = pblock->GetInt(PFlow_kShapeLibary_dimensionType, ticks);
		if(nDimension == PFlow_kShapeLibrary_dimensionType_2D){
			int n2DShapeId = pblock->GetInt(PFlow_kShapeLibary_2DType, ticks);
			if( n2DShapeId == PFlow_kShapeLibrary_dimensionType_2D_square){
				type = ShapeType_Rectangle;
			}
			else{
				ESS_LOG_INFO("Unsupported shape type.");
				type = ShapeType_Point;
			}
		}
		else if(nDimension == PFlow_kShapeLibrary_dimensionType_3D){
			int n3DShapeId = pblock->GetInt(PFlow_kShapeLibary_3DType, ticks);
			if(n3DShapeId == PFlow_kShapeLibary_3DType_cube){
				type = ShapeType_Box;
			}
			else if(n3DShapeId == PFlow_kShapeLibary_3DType_Sphere20sides ||
					n3DShapeId == PFlow_kShapeLibary_3DType_Sphere80sides){
				type = ShapeType_Sphere;
			}
			else{
				ESS_LOG_INFO("Unsupported shape type.");
				type = ShapeType_Point;
			}
		
			//ShapeType_Cylinder unsupported
			//ShapeType_Cone unsupported
			//ShapeType_Disc unsupported
			//ShapeType_NbElements unsupported
		}
		else{
			ESS_LOG_INFO("Unknown dimension.");
			type = ShapeType_Point;
		}
			
		//int nNumParams = pblock->NumParams();

		//for(int i=0; i<nNumParams; i++){
	
		//	ParamID id = pblock->IndextoID(i);
		//	MSTR paramStr = pblock->GetLocalName(id, 0);
		//	int n = 0;
		//
		//}

	}
    else if (pSimpleOperator->ClassID() == PFOperatorInstanceShape_Class_ID)
    {
        // Assign animation time and shape here
        IParamBlock2 *pblock = pSimpleOperator->GetParamBlockByID(0);
        INode *pNode = pblock->GetINode(PFlow_kInstanceShape_objectMaxscript, ticks);
        if (pNode == NULL || pNode->GetName() == NULL)
        {
            return;
        }
        
        type = ShapeType_Instance;

		bool bFlatten = GetCurrentJob()->GetOption("flattenHierarchy");
		std::string nodePath = getNodeAlembicPath( EC_MCHAR_to_UTF8( pNode->GetName() ), bFlatten);

        // Find if the name is alerady registered, otherwise add it to the list
   //     instanceId = FindInstanceName(nodePath);
   //     if (instanceId == USHRT_MAX)
   //     {
			//mInstanceNames.push_back(nodePath);
   //         instanceId = (Abc::uint16_t)mInstanceNames.size()-1;
   //     }

        // Determine if we have an animated shape
        BOOL animatedShape = pblock->GetInt(PFlow_kInstanceShape_animatedShape);
	    BOOL acquireShape = pblock->GetInt(PFlow_kInstanceShape_acquireShape);

        if (!animatedShape && !acquireShape)
        {
            return;
        }

        // Get the necesary particle channels to grab the current time values
        ::IObject *pCont = particleGroup->GetParticleContainer();
        if (!pCont)
        {
            return;
        }

        // Get synch values that we are interested in fromt the param block
        int syncType = pblock->GetInt(PFlow_kInstanceShape_syncType);
        BOOL syncRandom = pblock->GetInt(PFlow_kInstanceShape_syncRandom);

        IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont);
        if (chTime == NULL) 
        {
            return; // can't find particle times in the container
        }

        IChannelContainer* chCont = GetChannelContainerInterface(pCont);
        if (chCont == NULL) 
        {
            return;  // can't get access to ChannelContainer interface
        }

        IParticleChannelPTVR* chBirthTime = NULL;
        IParticleChannelPTVR* chEventStartR = NULL;
        bool initEventStart = false;

        if (syncType == PFlow_kInstanceShape_syncBy_particleAge) 
        {
            chBirthTime = GetParticleChannelBirthTimeRInterface(pCont);
            if (chBirthTime == NULL) 
            {
                return; // can't read particle age
            }
        }
        else if (syncType == PFlow_kInstanceShape_syncBy_eventStart) 
        {
            chEventStartR = GetParticleChannelEventStartRInterface(pCont);
             
            if (chEventStartR == NULL) 
            {
                return; // can't read event start time
            }
        }

        IParticleChannelIntR* chLocalOffR = NULL;
        bool initLocalOff = false;

        // acquire LocalOffset particle channel; if not present then create it.
        if (syncRandom) 
        {
            chLocalOffR =  (IParticleChannelIntR*)chCont->GetPrivateInterface(PARTICLECHANNELLOCALOFFSETR_INTERFACE, pSimpleOperator);
        }

        // get new shape from the source
        PreciseTimeValue time = chTime->GetValue(particleId);
        switch(syncType)
        {
        case PFlow_kInstanceShape_syncBy_absoluteTime:
            break;
        case PFlow_kInstanceShape_syncBy_particleAge:
            time -= chBirthTime->GetValue(particleId);
            break;
        case PFlow_kInstanceShape_syncBy_eventStart:
            time -= chEventStartR->GetValue(particleId);
            break;
        default:
            break;
        }

        if (syncRandom) 
        {
            if (chLocalOffR != NULL)
                time += chLocalOffR->GetValue(particleId);
        }
		
		//timeValueMap::iterator it = mTimeValueMap.find(time);
		//if( it != mTimeValueMap.end() ){
		//	ESS_LOG_WARNING("sampleTime already seen.");
		//}
		//else{
		//	mTimeValueMap[time] = true;
		//	mTimeSamplesCount++;
		//	ESS_LOG_WARNING("sampleTime: "<<(float)time<<" totalSamples: "<<mTimeSamplesCount);
		//}

        TimeValue t = TimeValue(time);
        animationTime = (float)GetSecondsFromTimeValue(t);
    }
	else if (pSimpleOperator->ClassID() == PFOperatorMarkShape_Class_ID)
	{
		ESS_LOG_INFO("Shape Mark operator not supported.");
	}
	else if (pSimpleOperator->ClassID() == PFOperatorFacingShape_Class_ID)
	{
		ESS_LOG_INFO("Shape Facing operator not supported.");
	}

}
bool PFOperatorForceSpaceWarp::Proceed(IObject* pCont, 
									 PreciseTimeValue timeStart, 
									 PreciseTimeValue& timeEnd,
									 Object* pSystem,
									 INode* pNode,
									 INode* actionNode,
									 IPFIntegrator* integrator)
{
	// acquire all necessary channels, create additional if needed

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

	IParticleChannelAmountR* chAmount = GetParticleChannelAmountRInterface(pCont);
	if(chAmount == NULL) return false;
	int iQuant = chAmount->Count();
	if (iQuant < 1) return true; // no particles to proceed

	IParticleChannelNewR* chNew = GetParticleChannelNewRInterface(pCont);
	if (chNew == NULL) return false; 

	IParticleChannelIDR* chID = GetParticleChannelIDRInterface(pCont);
	if (chID == NULL) return false;

	IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont);
	if(chTime == NULL) return false;

	IParticleChannelPTVR* chAge = GetParticleChannelBirthTimeRInterface(pCont);
	if(chAge == NULL) return false;

// the channel of interest speed
	bool initSpeed = false;
//channel does not exist so make it and note that we have to fill it out
	IParticleChannelPoint3W* chSpeedW = (IParticleChannelPoint3W*)chCont->EnsureInterface(PARTICLECHANNELSPEEDW_INTERFACE,
																			ParticleChannelPoint3_Class_ID,
																			true, PARTICLECHANNELSPEEDR_INTERFACE,
																			PARTICLECHANNELSPEEDW_INTERFACE, true,
																			actionNode, NULL, &initSpeed);
	IParticleChannelPoint3R* chSpeed = GetParticleChannelSpeedRInterface(pCont);
	if ((chSpeedW == NULL) || (chSpeed == NULL)) return false;

	bool initPosition = false;
	IParticleChannelPoint3W* chPosW = (IParticleChannelPoint3W*)chCont->EnsureInterface(PARTICLECHANNELPOSITIONW_INTERFACE,
																			ParticleChannelPoint3_Class_ID,
																			true, PARTICLECHANNELPOSITIONR_INTERFACE,
																			PARTICLECHANNELPOSITIONW_INTERFACE, true,
																			actionNode, NULL, &initPosition);
	IParticleChannelPoint3R* chPos = GetParticleChannelPositionRInterface(pCont);
	if ((chPosW == NULL) || (chPos == NULL)) return false;

	bool useScript = ((scriptPBlock()->GetInt(kForceSpaceWarp_useScriptWiring, 0) != 0)
						&& (scriptPBlock()->GetInt(kForceSpaceWarp_useFloat, 0) == kForceSpaceWarp_useFloat_influence));
	IParticleChannelFloatR* chFloat = NULL;
	if (useScript) {
		chFloat = GetParticleChannelMXSFloatRInterface(pCont);
		if (chFloat == NULL) return false;
	}

	int timeType = kAbsoluteTime;
	_pblock()->GetValue(kForceSpaceWarp_Sync,0, timeType, FOREVER);

	IParticleChannelPTVR* chEventStart = NULL;
	IParticleChannelPTVW* chEventStartW = NULL;
	bool initEventStart = false;
	if (timeType == kEventDuration)
	{
		chEventStartW = (IParticleChannelPTVW*) chCont->EnsureInterface(PARTICLECHANNELEVENTSTARTW_INTERFACE,
								  ParticleChannelPTV_Class_ID,
								  true, PARTICLECHANNELEVENTSTARTR_INTERFACE,
								  PARTICLECHANNELEVENTSTARTW_INTERFACE, false,
								  actionNode, NULL, &initEventStart);

		chEventStart = GetParticleChannelEventStartRInterface(pCont);
		if ((chEventStart == NULL) || (chEventStartW == NULL)) return false;
	}

	int overlapping = pblock()->GetInt(kForceSpaceWarp_Overlapping, 0);
	// collecting force fields
	Tab<ForceField*> ff;
	ForceField* curFF;
	int i, j;
	for(i=0; i<pblock()->Count(kForceSpaceWarp_ForceNodeList); i++) {
		INode* node = pblock()->GetINode(kForceSpaceWarp_ForceNodeList, 0, i);
		if (node == NULL) continue;
		Object* ob = GetPFObject(node->GetObjectRef());
		if (ob == NULL) continue;
		if (ob->SuperClassID() == WSM_OBJECT_CLASS_ID) {
			WSMObject* obref = (WSMObject*)ob;
			curFF = obref->GetForceField(node);
			if (curFF != NULL) {
				if (ob->ClassID() == CS_VFIELDOBJECT_CLASS_ID) {
					// CS VectorField SW doesn't init properly partobj on GetForceField
					// this is a quick fix for that (bayboro 3/6/2003)
					CS_VectorField* vf = (CS_VectorField*)curFF;
					vf->partobj = GetParticleInterface(pSystem);
				}
				ff.Append(1, &curFF);
			}
		}
	}
	if (ff.Count() == 0) return true; // no force fields

	// some calls for a reference node TM may initiate REFMSG_CHANGE notification
	// we have to ignore that while processing the particles
	bool wasIgnoring = IsIgnoringRefNodeChange();
	if (!wasIgnoring) SetIgnoreRefNodeChange();

	float influence = 0.0f;
	for(i = 0; i < iQuant; i++) 
	{
		TimeValue t = 0;
		if (timeType == kAbsoluteTime)
			t = chTime->GetValue(i).TimeValue();
		else if (timeType == kParticleAge)
			t = chTime->GetValue(i).TimeValue() - chAge->GetValue(i).TimeValue();
		else 
		{
			if (initEventStart && chNew->IsNew(i))
				chEventStartW->SetValue(i, chTime->GetValue(i));
			t = chTime->GetValue(i).TimeValue() - chEventStart->GetValue(i).TimeValue();
		}

		if (useScript) {
			influence = chFloat->GetValue(i);
		} else {
			influence = GetPFFloat(pblock(), kForceSpaceWarp_Influence, t);
		}

		Point3 v(0.0f,0.0f,0.0f);
		if (!initSpeed || !chNew->IsNew(i)) //if we created a speed channel the channel incoming is bogus so just use 0,0,0 ad default
			v = chSpeed->GetValue(i);

		Point3 p(0.0f,0.0f,0.0f);
		if (!initPosition || !chNew->IsNew(i)) //if we created a pos channel the channel incoming is bogus so just use 0,0,0 ad default
			p = chPos->GetValue(i);

		Point3 force = Point3::Origin;
		for(j=0; j<ff.Count(); j++) {
			// buffer vectors to guard true position and speed from malicious force
			Point3 pp = p;
			Point3 vv = v;
			Point3 nextForce = ff[j]->Force(t,pp,vv,chID->GetParticleBorn(i)) * influence;
			float lenSq = LengthSquared(nextForce);
			if (lenSq <= 0.0f) continue; // not a valid force
			if (overlapping == kForceSpaceWarp_Overlapping_additive) {
				force += nextForce;
			} else {
				if (lenSq > LengthSquared(force))
					force = nextForce;
			}
//			p = pp;
//			v = vv;
		}

		v += force * float(timeEnd - chTime->GetValue(i));
		chPosW->SetValue(i, p);
		chSpeedW->SetValue(i, v);
	}

	for(i=0; i<ff.Count(); i++)
		if (ff[i] != NULL) ff[i]->DeleteThis();

	if (!wasIgnoring) ClearIgnoreRefNodeChange();
	return true;
}
Exemple #7
0
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFTest									 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestSpeed::Proceed(IObject* pCont, 
							PreciseTimeValue timeStart, 
							PreciseTimeValue& timeEnd, 
							Object* pSystem, 
							INode* pNode, 
							INode* actionNode, 
							IPFIntegrator* integrator, 
							BitArray& testResult, 
							Tab<float>& testTime)
{
	bool exactStep = IsExactIntegrationStep(timeEnd, pSystem);

	// get the constant properties of the test
	int testType = pblock()->GetInt(kSpeedTest_testType, timeEnd);
	int condType = pblock()->GetInt(kSpeedTest_conditionType, timeEnd);
	int syncType = pblock()->GetInt(kSpeedTest_sync, timeEnd);
	ParamID varParamID = (testType == kSpeedTest_testType_steering) ? kSpeedTest_angleVariation : kSpeedTest_unitVariation;
	bool hasTestVariation = (pblock()->GetFloat(varParamID, 0) != 0.0f);
	if (!hasTestVariation) {
		Control* ctrl = pblock()->GetControllerByID(varParamID);
		if (ctrl != NULL)
			hasTestVariation = (ctrl->IsAnimated() != 0);
	}
	if (testType >= kSpeedTest_testType_whenAccels) {
		hasTestVariation = false;
		syncType = kSpeedTest_sync_time;
	}
	bool needPrevValue = (testType >= kSpeedTest_testType_accel);

	// 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
	int count = chAmount->Count();
	if (count == 0) return true; // no particles to test
	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
	IParticleChannelPoint3R* chSpeed = GetParticleChannelSpeedRInterface(pCont);
	if (chSpeed == NULL) return false; // can't read speed values

	// acquire more particle channels
	IParticleChannelPTVR* chBirthTime = NULL;
	if (syncType == kSpeedTest_sync_age && (testType < kSpeedTest_testType_whenAccels))
	{
		chBirthTime = GetParticleChannelBirthTimeRInterface(pCont);
		if (chBirthTime == NULL) return false; // can't read particle age
	}
	IParticleChannelPTVR* chEventStartR = NULL;
	IParticleChannelPTVW* chEventStartW = NULL;
	bool initEventStart = false;
	if (syncType == kSpeedTest_sync_event && (testType < kSpeedTest_testType_whenAccels)) {
		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
		}
	}
	IParticleChannelFloatR* chRandFloatR = NULL;
	IParticleChannelFloatW* chRandFloatW = NULL;
	bool initRandFloat = false;
	if (hasTestVariation) {
		chRandFloatW = (IParticleChannelFloatW*)chCont->EnsureInterface(PARTICLECHANNELRANDFLOATW_INTERFACE,
																		ParticleChannelFloat_Class_ID,
																		true, PARTICLECHANNELRANDFLOATR_INTERFACE,
																		PARTICLECHANNELRANDFLOATW_INTERFACE, true,
																		actionNode, (Object*)this, &initRandFloat);
		chRandFloatR = (IParticleChannelFloatR*)chCont->GetPrivateInterface(PARTICLECHANNELRANDFLOATR_INTERFACE, (Object*)this);
		if ((chRandFloatR == NULL) || (chRandFloatW == NULL)) return false; // can't set rand float value for newly entered particles
	}
	IParticleChannelPoint3R* chPrevSpeedR = NULL;
	IParticleChannelPoint3W* chPrevSpeedW = NULL;
	IParticleChannelPTVR* chPrevTimeR = NULL;
	IParticleChannelPTVW* chPrevTimeW = NULL;
	bool initPrevValue = false;
	if (needPrevValue) {
		chPrevSpeedW = (IParticleChannelPoint3W*)chCont->EnsureInterface(PARTICLECHANNELPREVSPEEDW_INTERFACE,
																		ParticleChannelPoint3_Class_ID,
																		true, PARTICLECHANNELPREVSPEEDR_INTERFACE,
																		PARTICLECHANNELPREVSPEEDW_INTERFACE, true,
																		actionNode, (Object*)this, &initPrevValue);
		chPrevSpeedR = (IParticleChannelPoint3R*)chCont->GetPrivateInterface(PARTICLECHANNELPREVSPEEDR_INTERFACE, (Object*)this);
		chPrevTimeW = (IParticleChannelPTVW*)chCont->EnsureInterface(PARTICLECHANNELPREVTIMEW_INTERFACE,
																		ParticleChannelPTV_Class_ID,
																		true, PARTICLECHANNELPREVTIMER_INTERFACE,
																		PARTICLECHANNELPREVTIMEW_INTERFACE, true,
																		actionNode, (Object*)this, &initPrevValue);
		chPrevTimeR = (IParticleChannelPTVR*)chCont->GetPrivateInterface(PARTICLECHANNELPREVTIMER_INTERFACE, (Object*)this);
		if ((chPrevSpeedR == NULL) || (chPrevSpeedW == NULL) || (chPrevTimeR == NULL) || (chPrevTimeW == NULL)) return false; 
	}

	// grab the rand generator for test variation
	RandGenerator* randGen = randLinker().GetRandGenerator(pCont);
	if (randGen == NULL) return false;

	// check all particles
	testResult.SetSize(count);
	testResult.ClearAll();
	testTime.SetCount(count);
	for(int i=0; i<count; i++)
	{
		if (chNew->IsNew(i)) { // initialize some channels
			if (initEventStart)
				chEventStartW->SetValue(i, chTime->GetValue(i));
			if (initRandFloat)
				chRandFloatW->SetValue(i, randGen->Rand11());
		}

		PreciseTimeValue prevTime;
		Point3 prevSpeed;
		Point3 currentSpeed = chSpeed->GetValue(i);
		PreciseTimeValue currentTime = chTime->GetValue(i);
		if (needPrevValue) {
			prevTime = chPrevTimeR->GetValue(i);
			prevSpeed = chPrevSpeedR->GetValue(i);
			chPrevTimeW->SetValue(i, currentTime);
			chPrevSpeedW->SetValue(i, currentSpeed);
			if (initPrevValue && chNew->IsNew(i))
				continue; // particle just came into the event and doesn't have previous value
		}

		PreciseTimeValue syncTime = currentTime;
		switch(syncType) {
		case kSpeedTest_sync_age:
			syncTime -= chBirthTime->GetValue(i);
			break;
		case kSpeedTest_sync_event:
			syncTime -= chEventStartR->GetValue(i);
			break;
		}
		TimeValue syncTimeTV = TimeValue(syncTime);

		float testValue = 0.0f;
		if (testType < kSpeedTest_testType_whenAccels) {
			if (testType == kSpeedTest_testType_steering) {
				testValue = GetPFFloat(pblock(), kSpeedTest_angleValue, syncTimeTV);
				if (hasTestVariation)
					testValue += chRandFloatR->GetValue(i)*GetPFFloat(pblock(), kSpeedTest_angleVariation, syncTimeTV);
			} else {
				testValue = GetPFFloat(pblock(), kSpeedTest_unitValue, syncTimeTV);
				if (hasTestVariation)
					testValue += chRandFloatR->GetValue(i)*GetPFFloat(pblock(), kSpeedTest_unitVariation, syncTimeTV);
			}
			testValue /= TIME_TICKSPERSEC;
		}

		float currentValue = 0.0f;
		bool testSatisfied = false;
		if (testType < kSpeedTest_testType_whenAccels) {
			if (testType < kSpeedTest_testType_accel) {
				switch(testType) {
				case kSpeedTest_testType_speed:
					currentValue = Length(currentSpeed);
					break;
				case kSpeedTest_testType_speedX:
					currentValue = currentSpeed.x;
					break;
				case kSpeedTest_testType_speedY:
					currentValue = currentSpeed.y;
					break;
				case kSpeedTest_testType_speedZ:
					currentValue = currentSpeed.z;
					break;
				}
			} else if (testType == kSpeedTest_testType_steering) {
				float timeDif = float(currentTime - prevTime);
				if (timeDif <= 0.0f) continue; // no time difference
				float normFactor = Length(currentSpeed)*Length(prevSpeed);
				if (normFactor <= 0.0f) continue; // steering rate is not calculatable
				float vv = DotProd(currentSpeed,prevSpeed)/normFactor;
				float uu = 0.0;
				if (vv >= 1.0f) uu = 0.0;
				else if (vv <= -1.0f) uu = PI;
				else uu = acos(vv);
				currentValue = uu/timeDif;
			} else { // acceleration
				float timeDif = float(currentTime - prevTime);
				if (timeDif <= 0.0f) continue; // no time difference
				Point3 curAccel;
				switch(testType) {
				case kSpeedTest_testType_accel:
					currentValue = Length((currentSpeed - prevSpeed)/timeDif);
					break;
				case kSpeedTest_testType_accelX:
					currentValue = (currentSpeed.x - prevSpeed.x)/timeDif;	
					break;
				case kSpeedTest_testType_accelY:
					currentValue = (currentSpeed.y - prevSpeed.y)/timeDif;	
					break;
				case kSpeedTest_testType_accelZ:
					currentValue = (currentSpeed.z - prevSpeed.z)/timeDif;	
					break;
				}
				testValue /= TIME_TICKSPERSEC; // acceleration is per second squared
			}
			testSatisfied = (condType == kSpeedTest_conditionType_less) ?
								(currentValue < testValue) : (currentValue > testValue);
		} else {
			if (testType == kSpeedTest_testType_whenAccels) {
				testSatisfied = (Length(currentSpeed) > Length(prevSpeed));
			} else {
				testSatisfied = (Length(currentSpeed) < Length(prevSpeed));
			}
		}

		if (testSatisfied && exactStep) {
			testResult.Set(i);
			testTime[i] = 0.0f;
		}
	}

	return true;
}
Exemple #8
0
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFTest									 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestScale::Proceed(IObject* pCont, 
							PreciseTimeValue timeStart, 
							PreciseTimeValue& timeEnd, 
							Object* pSystem, 
							INode* pNode, 
							INode* actionNode, 
							IPFIntegrator* integrator, 
							BitArray& testResult, 
							Tab<float>& testTime)
{
	bool exactStep = IsExactIntegrationStep(timeEnd, pSystem);

	// get the constant properties of the test
	int testType = pblock()->GetInt(kScaleTest_testType, timeEnd);
	int axisType = pblock()->GetInt(kScaleTest_axisType, timeEnd);
	int condType = pblock()->GetInt(kScaleTest_conditionType, timeEnd);
	int syncType = pblock()->GetInt(kScaleTest_sync, timeEnd);
	ParamID varParamID = (testType == kScaleTest_testType_scale) ? kScaleTest_scaleVariation : kScaleTest_sizeVariation;
	bool hasTestVariation = (pblock()->GetFloat(varParamID, 0) != 0.0f);
	if (!hasTestVariation) {
		Control* ctrl = pblock()->GetControllerByID(varParamID);
		if (ctrl != NULL)
			hasTestVariation = (ctrl->IsAnimated() != 0);
	}
	
	// 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
	int count = chAmount->Count();
	if (count == 0) return true; // no particles to test
	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 (syncType == kScaleTest_sync_age)
	{
		chBirthTime = GetParticleChannelBirthTimeRInterface(pCont);
		if (chBirthTime == NULL) return false; // can't read particle age
	}
	IParticleChannelPTVR* chEventStartR = NULL;
	IParticleChannelPTVW* chEventStartW = NULL;
	bool initEventStart = false;
	if (syncType == kScaleTest_sync_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
		}
	}
	IParticleChannelMeshR* chShape = NULL;
	if (testType != kScaleTest_testType_scale) {
		chShape = GetParticleChannelShapeRInterface(pCont);
		if (chShape == NULL) return false; // can't read particle shape to find bounding box
	}
	IParticleChannelPoint3R* chScale = NULL;
	if (testType != kScaleTest_testType_preSize) {
		chScale = GetParticleChannelScaleRInterface(pCont);
		if (chScale == NULL) return false; // can't read particle scale
	}
	IParticleChannelFloatR* chRandFloatR = NULL;
	IParticleChannelFloatW* chRandFloatW = NULL;
	bool initRandFloat = false;
	if (hasTestVariation) {
		chRandFloatW = (IParticleChannelFloatW*)chCont->EnsureInterface(PARTICLECHANNELRANDFLOATW_INTERFACE,
																		ParticleChannelFloat_Class_ID,
																		true, PARTICLECHANNELRANDFLOATR_INTERFACE,
																		PARTICLECHANNELRANDFLOATW_INTERFACE, true,
																		actionNode, (Object*)this, &initRandFloat);
		chRandFloatR = (IParticleChannelFloatR*)chCont->GetPrivateInterface(PARTICLECHANNELRANDFLOATR_INTERFACE, (Object*)this);
		if ((chRandFloatR == NULL) || (chRandFloatW == NULL)) return false; // can't set rand float value for newly entered particles
	}

	// grab the rand generator for test variation
	RandGenerator* randGen = randLinker().GetRandGenerator(pCont);
	if (randGen == NULL) return false;

	// check all particles
	testResult.SetSize(count);
	testResult.ClearAll();
	testTime.SetCount(count);
	for(int i=0; i<count; i++)
	{
		if (chNew->IsNew(i)) { // initialize some channels
			if (initEventStart)
				chEventStartW->SetValue(i, chTime->GetValue(i));
			if (initRandFloat)
				chRandFloatW->SetValue(i, randGen->Rand11());
		}

		PreciseTimeValue syncTime = chTime->GetValue(i);
		switch(syncType) {
		case kScaleTest_sync_age:
			syncTime -= chBirthTime->GetValue(i);
			break;
		case kScaleTest_sync_event:
			syncTime -= chEventStartR->GetValue(i);
			break;
		}
		TimeValue syncTimeTV = TimeValue(syncTime);

		float testValue = 0.0f;
		if (testType == kScaleTest_testType_scale) {
			testValue = GetPFFloat(pblock(), kScaleTest_scaleValue, syncTimeTV);
			if (hasTestVariation)
				testValue += chRandFloatR->GetValue(i)*GetPFFloat(pblock(), kScaleTest_scaleVariation, syncTimeTV);
		} else {
			testValue = GetPFFloat(pblock(), kScaleTest_sizeValue, syncTimeTV);
			if (hasTestVariation)
				testValue += chRandFloatR->GetValue(i)*GetPFFloat(pblock(), kScaleTest_sizeVariation, syncTimeTV);
		}

		Point3 cur3DValue;
		if (testType == kScaleTest_testType_scale) {
			cur3DValue = chScale->GetValue(i);
		} else {
			Mesh* curMesh = const_cast <Mesh*>(chShape->GetValue(i));
			if (curMesh == NULL) continue;
			Box3 curBox = curMesh->getBoundingBox();
			cur3DValue = curBox.pmax - curBox.pmin;
			if (testType == kScaleTest_testType_postSize)
				cur3DValue *= chScale->GetValue(i);
		}

		float currentValue = 0.0f;
		switch(axisType) {
		case kScaleTest_axisType_average:
			currentValue = (cur3DValue.x + cur3DValue.y + cur3DValue.z)/3.0f;
			break;
		case kScaleTest_axisType_minimum:
			currentValue = min(cur3DValue.x, min(cur3DValue.y, cur3DValue.z));
			break;
		case kScaleTest_axisType_median:
			currentValue = max(min(cur3DValue.x, cur3DValue.y), 
								max(min(cur3DValue.x, cur3DValue.z), min(cur3DValue.y, cur3DValue.z)));
			break;
		case kScaleTest_axisType_maximum:
			currentValue = max(cur3DValue.x, max(cur3DValue.y, cur3DValue.z));
			break;
		case kScaleTest_axisType_x:
			currentValue = cur3DValue.x;
			break;
		case kScaleTest_axisType_y:
			currentValue = cur3DValue.y;
			break;
		case kScaleTest_axisType_z:
			currentValue = cur3DValue.z;
			break;
		}

		bool testSatisfied = (condType == kScaleTest_conditionType_less) ?
								(currentValue < testValue) : (currentValue > testValue);
		if (testSatisfied && exactStep) {
			testResult.Set(i);
			testTime[i] = 0.0f;
		}
	}

	return true;
}
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							From IPFTest									 |
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
bool PFTestGoToRotation::Proceed(IObject* pCont, 
							PreciseTimeValue timeStart, 
							PreciseTimeValue& timeEnd, 
							Object* pSystem, 
							INode* pNode, 
							INode* actionNode, 
							IPFIntegrator* integrator, 
							BitArray& testResult, 
							Tab<float>& testTime)
{

	if (pblock() == NULL) return false;
	// 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
	int i, count = chAmount->Count();
	if (count == 0) return true; // no particles to test
	IParticleChannelPTVR* chTime = GetParticleChannelTimeRInterface(pCont);
	if (chTime == NULL) return false; // can't read timing info for a particle
	IParticleChannelQuatW* chOrientW = GetParticleChannelOrientationWInterface(pCont);
	if (chOrientW == NULL) return false; // can't modify current orientation for a particle
	IParticleChannelAngAxisR* chSpinR = GetParticleChannelSpinRInterface(pCont);
	if (chSpinR == NULL) return false; // can't read current spin for a particle
	IParticleChannelAngAxisW* chSpinW = GetParticleChannelSpinWInterface(pCont);
	if (chSpinW == NULL) return false; // can't modify current spin for a particle

	// acquire private channels
	IParticleChannelPTVR* chEndTime = (IParticleChannelPTVR*)(chCont->GetPrivateInterface(PARTICLECHANNELENDTIMER_INTERFACE, (Object*)this));
	if (chEndTime == NULL) return false;
	IParticleChannelBoolR* chGotInitR = (IParticleChannelBoolR*)(chCont->GetPrivateInterface(PARTICLECHANNELGOTINITR_INTERFACE, (Object*)this));
	if (chGotInitR == NULL) return false;
	IParticleChannelQuatR* chEndRotR = (IParticleChannelQuatR*)(chCont->GetPrivateInterface(PARTICLECHANNELENDROTR_INTERFACE, (Object*)this));
	if (chEndRotR == NULL) return false;
	IParticleChannelFloatR* chEndSpin = (IParticleChannelFloatR*)(chCont->GetPrivateInterface(PARTICLECHANNELENDSPINR_INTERFACE, (Object*)this));
	if (chEndSpin == NULL) return false;

	bool sendOut = (pblock()->GetInt(kGoToRotation_sendOut, timeEnd) != 0);
	bool stopSpin = (pblock()->GetInt(kGoToRotation_stopSpin, timeEnd) != 0);

	// test all particles
	PreciseTimeValue curTime;
	testResult.SetSize(count);
	testResult.ClearAll();
	testTime.SetCount(count);
	BitArray particlesToAdvance;
	particlesToAdvance.SetSize(count);
	particlesToAdvance.ClearAll();
	for(i=0; i<count; i++)
	{
		curTime = chEndTime->GetValue(i);
		if (curTime > timeEnd) continue;
		if (curTime < timeStart) {
			testTime[i] = float(chTime->GetValue(i) - timeStart);
		} else {
			testTime[i] = float(curTime - timeStart);
			particlesToAdvance.Set(i);
		}
		testResult.Set(i);
	}

	// 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);
	}

	for(i=0; i<count; i++) {
		if (particlesToAdvance[i] == 0) continue;

		Quat curRot = chEndRotR->GetValue(i);

		chOrientW->SetValue(i, chEndRotR->GetValue(i));
		AngAxis spin = chSpinR->GetValue(i);
		spin.angle = stopSpin ? 0.0f : chEndSpin->GetValue(i);
		chSpinW->SetValue(i, spin);
	}

	if (!sendOut) testResult.ClearAll();

	return true;
}
//+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+
//|							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;
}
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;
}
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;
}
Exemple #13
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;
}