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