Example #1
0
bool AlembicPoints::Save(double time, bool bLastFrame)
{
   ESS_PROFILE_FUNC();

    // Note: Particles are always considered to be animated even though
    //       the node does not have the IsAnimated() flag.

    // Extract our particle emitter at the given time
    TimeValue ticks = GetTimeValueFromFrame(time);
    Object *obj = mMaxNode->EvalWorldState(ticks).obj;

	SaveMetaData(mMaxNode, this);

	SimpleParticle* pSimpleParticle = (SimpleParticle*)obj->GetInterface(I_SIMPLEPARTICLEOBJ);
	IPFSystem* ipfSystem = GetPFSystemInterface(obj);
	IParticleObjectExt* particlesExt = GetParticleObjectExtInterface(obj);
	
#ifdef THINKING_PARTICLES
	ParticleMat* pThinkingParticleMat = NULL;
	TP_MasterSystemInterface* pTPMasterSystemInt = NULL;
	if(obj->CanConvertToType(MATTERWAVES_CLASS_ID))
	{
		pThinkingParticleMat = reinterpret_cast<ParticleMat*>(obj->ConvertToType(ticks, MATTERWAVES_CLASS_ID));
		pTPMasterSystemInt = reinterpret_cast<TP_MasterSystemInterface*>(obj->GetInterface(IID_TP_MASTERSYSTEM));     

	}
#endif

	const bool bAutomaticInstancing = GetCurrentJob()->GetOption("automaticInstancing");

	if( 
#ifdef THINKING_PARTICLES
		!pThinkingParticleMat && 
#endif
		!particlesExt && !pSimpleParticle){
		return false;
	}


	//We have to put the particle system into the renders state so that PFOperatorMaterialFrequency::Proceed will set the materialID channel
	//Note: settting the render state to true breaks the shape node instancing export
	bool bRenderStateForced = false;
	if(bAutomaticInstancing && ipfSystem && !ipfSystem->IsRenderState()){
		ipfSystem->SetRenderState(true);
		bRenderStateForced = true;
	}

	int numParticles = 0;
#ifdef THINKING_PARTICLES
	if(pThinkingParticleMat){
		numParticles = pThinkingParticleMat->NumParticles();	
	}
	else 
#endif
	if(particlesExt){
		particlesExt->UpdateParticles(mMaxNode, ticks);
		numParticles = particlesExt->NumParticles();
	}
	else if(pSimpleParticle){
		pSimpleParticle->Update(ticks, mMaxNode);
		numParticles = pSimpleParticle->parts.points.Count();
	}


    // Store positions, velocity, width/size, scale, id, bounding box
    std::vector<Abc::V3f> positionVec;
    std::vector<Abc::V3f> velocityVec;
    std::vector<Abc::V3f> scaleVec;
    std::vector<float> widthVec;
    std::vector<float> ageVec;
    std::vector<float> massVec;
    std::vector<float> shapeTimeVec;
    std::vector<Abc::uint64_t> idVec;
    std::vector<Abc::uint16_t> shapeTypeVec;
    std::vector<Abc::uint16_t> shapeInstanceIDVec;
    std::vector<Abc::Quatf> orientationVec;
    std::vector<Abc::Quatf> angularVelocityVec;
    std::vector<Abc::C4f> colorVec;

    positionVec.reserve(numParticles);
    velocityVec.reserve(numParticles);
    scaleVec.reserve(numParticles);
    widthVec.reserve(numParticles);
    ageVec.reserve(numParticles);
    massVec.reserve(numParticles);
    shapeTimeVec.reserve(numParticles);
    idVec.reserve(numParticles);
    shapeTypeVec.reserve(numParticles);
    shapeInstanceIDVec.reserve(numParticles);
    orientationVec.reserve(numParticles);
    angularVelocityVec.reserve(numParticles);
    colorVec.reserve(numParticles);

    //std::vector<std::string> instanceNamesVec;
    Abc::Box3d bbox;
    bool constantPos = true;
    bool constantVel = true;
    bool constantScale = true;
    bool constantWidth = true;
    bool constantAge = true;
    bool constantOrientation = true;
    bool constantAngularVel = true;
	bool constantColor = true;


	if(bAutomaticInstancing){
		SetMaxSceneTime(ticks);
	}

	//The MAX interfaces return everything in world coordinates,
	//so we need to multiply the inverse the node world transform matrix
    Matrix3 nodeWorldTM = mMaxNode->GetObjTMAfterWSM(ticks);
    // Convert the max transform to alembic
    Matrix3 alembicMatrix;
    ConvertMaxMatrixToAlembicMatrix(nodeWorldTM, alembicMatrix);
    Abc::M44d nodeWorldTrans(	alembicMatrix.GetRow(0).x,  alembicMatrix.GetRow(0).y,  alembicMatrix.GetRow(0).z,  0,
										alembicMatrix.GetRow(1).x,  alembicMatrix.GetRow(1).y,  alembicMatrix.GetRow(1).z,  0,
										alembicMatrix.GetRow(2).x,  alembicMatrix.GetRow(2).y,  alembicMatrix.GetRow(2).z,  0,
										alembicMatrix.GetRow(3).x,  alembicMatrix.GetRow(3).y,  alembicMatrix.GetRow(3).z,  1);
	Abc::M44d nodeWorldTransInv = nodeWorldTrans.inverse();


	//ESS_LOG_WARNING("tick: "<<ticks<<"   numParticles: "<<numParticles<<"\n");

	ExoNullView nullView;
	particleGroupInterface groupInterface(particlesExt, obj, mMaxNode, &nullView);

    {
    ESS_PROFILE_SCOPE("AlembicPoints::SAVE - numParticlesLoop");

	for (int i = 0; i < numParticles; ++i)
	{
		Abc::V3f pos(0.0);
		Abc::V3f vel(0.0);
		Abc::V3f scale(1.0);
		Abc::C4f color(0.5, 0.5, 0.5, 1.0);
		float age = 0;
		Abc::uint64_t id = 0;
	    Abc::Quatd orientation(0.0, 0.0, 1.0, 0.0);
		Abc::Quatd spin(0.0, 0.0, 1.0, 0.0);
		// Particle size is a uniform scale multiplier in XSI.  In Max, I need to learn where to get this 
		// For now, we'll just default to 1
		float width = 1.0f;

		ShapeType shapetype = ShapeType_Point;
		float shapeInstanceTime = (float)time;
		Abc::uint16_t shapeInstanceId = 0;

#ifdef THINKING_PARTICLES
		if(pThinkingParticleMat){
            

			if(pTPMasterSystemInt->IsAlive(i) == FALSE){
				continue;
			}

			//TimeValue ageValue = particlesExt->GetParticleAgeByIndex(i);
			TimeValue ageValue = pTPMasterSystemInt->Age(i);
			if(ageValue == -1){
				continue;
			}

            ESS_PROFILE_SCOPE("AlembicPoints::SAVE - numParticlesLoop - ThinkingParticles");
			age = (float)GetSecondsFromTimeValue(ageValue);

			//pos = ConvertMaxPointToAlembicPoint(*particlesExt->GetParticlePositionByIndex(i));
			pos = ConvertMaxPointToAlembicPoint(pTPMasterSystemInt->Position(i));
			//vel = ConvertMaxVectorToAlembicVector(*particlesExt->GetParticleSpeedByIndex(i) * TIME_TICKSPERSEC);
			vel = ConvertMaxVectorToAlembicVector(pTPMasterSystemInt->Velocity(i) * TIME_TICKSPERSEC);
			scale = ConvertMaxScaleToAlembicScale(pTPMasterSystemInt->Scale(i));
			scale *= pTPMasterSystemInt->Size(i);
			
			//ConvertMaxEulerXYZToAlembicQuat(*particlesExt->GetParticleOrientationByIndex(i), orientation);

			Matrix3 alignmentMatMax = pTPMasterSystemInt->Alignment(i);
			Abc::M44d alignmentMat;
			ConvertMaxMatrixToAlembicMatrix(alignmentMatMax, alignmentMat);
			/*alignmentMat = Abc::M44d( alignmentMatMax.GetRow(0).x,  alignmentMatMax.GetRow(0).y,  alignmentMatMax.GetRow(0).z,  0,
                                 alignmentMatMax.GetRow(1).x,  alignmentMatMax.GetRow(1).y,  alignmentMatMax.GetRow(1).z,  0,
                                 alignmentMatMax.GetRow(2).x,  alignmentMatMax.GetRow(2).y,  alignmentMatMax.GetRow(2).z,  0,
                                 alignmentMatMax.GetRow(3).x,  alignmentMatMax.GetRow(3).y,  alignmentMatMax.GetRow(3).z,  1);*/
			//orientation = ConvertMaxQuatToAlembicQuat(extracctuat(alignmentMat), true);

			alignmentMat = alignmentMat * nodeWorldTransInv;
			orientation = extractQuat(alignmentMat);

			//ConvertMaxAngAxisToAlembicQuat(*particlesExt->GetParticleSpinByIndex(i), spin);
			ConvertMaxAngAxisToAlembicQuat(pTPMasterSystemInt->Spin(i), spin);


			id = particlesExt->GetParticleBornIndex(i);

			//seems to always return 0
			//int nPid = pThinkingParticleMat->ParticleID(i);
					
			int nMatId = -1;
			Matrix3 meshTM;
			meshTM.IdentityMatrix();
			BOOL bNeedDelete = FALSE;
			BOOL bChanged = FALSE;
            Mesh* pMesh = NULL;
            {
            ESS_PROFILE_SCOPE("AlembicPoints::SAVE - numParticlesLoop - ThinkingParticles - GetParticleRenderMesh");
			pMesh = pThinkingParticleMat->GetParticleRenderMesh(ticks, mMaxNode, nullView, bNeedDelete, i, meshTM, bChanged);
            }

			if(pMesh){
                ESS_PROFILE_SCOPE("AlembicPoints::SAVE - numParticlesLoop - ThinkingParticles - CacheShapeMesh");
            meshInfo mi = CacheShapeMesh(pMesh, bNeedDelete, meshTM, nMatId, i, ticks, shapetype, shapeInstanceId, shapeInstanceTime);
            Abc::V3d min = pos + mi.bbox.min;
            Abc::V3d max = pos + mi.bbox.max;
            bbox.extendBy(min);
            bbox.extendBy(max);
			}
			else{
				shapetype = ShapeType_Point;
			}
		}
		else 
#endif
		if(particlesExt && ipfSystem){

			TimeValue ageValue = particlesExt->GetParticleAgeByIndex(i);
			if(ageValue == -1){
				continue;
			}
			age = (float)GetSecondsFromTimeValue(ageValue);

			pos = ConvertMaxPointToAlembicPoint(*particlesExt->GetParticlePositionByIndex(i));
			vel = ConvertMaxVectorToAlembicVector(*particlesExt->GetParticleSpeedByIndex(i) * TIME_TICKSPERSEC);
			scale = ConvertMaxScaleToAlembicScale(*particlesExt->GetParticleScaleXYZByIndex(i));
			ConvertMaxEulerXYZToAlembicQuat(*particlesExt->GetParticleOrientationByIndex(i), orientation);
			ConvertMaxAngAxisToAlembicQuat(*particlesExt->GetParticleSpinByIndex(i), spin);
			//age = (float)GetSecondsFromTimeValue(particlesExt->GetParticleAgeByIndex(i));
			id = particlesExt->GetParticleBornIndex(i);
			if(bAutomaticInstancing){

				int nMatId = -1;
				if(ipfSystem){
					if( groupInterface.setCurrentParticle(ticks, i) ){
						nMatId = groupInterface.getCurrentMtlId();
					}
					else{
						ESS_LOG_WARNING("Error: cound retrieve material ID for particle mesh "<<i);
					}
				}

				Matrix3 meshTM;
				meshTM.IdentityMatrix();
				BOOL bNeedDelete = FALSE;
				BOOL bChanged = FALSE;
				Mesh* pMesh = pMesh = particlesExt->GetParticleShapeByIndex(i);

				if(pMesh){
					meshInfo mi = CacheShapeMesh(pMesh, bNeedDelete, meshTM, nMatId, i, ticks, shapetype, shapeInstanceId, shapeInstanceTime);
               Abc::V3d min = pos + mi.bbox.min;
               Abc::V3d max = pos + mi.bbox.max;
               bbox.extendBy(min);
               bbox.extendBy(max);
				}
				else{
					shapetype = ShapeType_Point;
				}
			}
			else{
				GetShapeType(particlesExt, i, ticks, shapetype, shapeInstanceId, shapeInstanceTime);
			}
			color = GetColor(particlesExt, i, ticks);
		}
		else if(pSimpleParticle){
			if( ! pSimpleParticle->parts.Alive( i ) ) {
				continue;
			}

			pos = ConvertMaxPointToAlembicPoint(pSimpleParticle->ParticlePosition(ticks, i));
			vel = ConvertMaxVectorToAlembicVector(pSimpleParticle->ParticleVelocity(ticks, i));
			//simple particles have no scale?
			//simple particles have no orientation?
			age = (float)GetSecondsFromTimeValue( pSimpleParticle->ParticleAge(ticks, i) );
			//simple particles have born index
			width = pSimpleParticle->ParticleSize(ticks, i);

         Abc::V3d min(pos.x - width/2, pos.y - width/2, pos.z - width/2);
         Abc::V3d max(pos.x + width/2, pos.y + width/2, pos.z + width/2);
         bbox.extendBy(min);
         bbox.extendBy(max);
		}

        {
        ESS_PROFILE_SCOPE("AlembicPoints::SAVE - numParticlesLoop - end loop save");

		//move everything from world space to local space
		pos = pos * nodeWorldTransInv;

		Abc::V4f vel4(vel.x, vel.y, vel.z, 0.0);
		vel4 = vel4 * nodeWorldTransInv;
		vel.setValue(vel4.x, vel4.y, vel4.z);

		//scale = scale * nodeWorldTransInv;
		//orientation = Abc::extractQuat(orientation.toMatrix44() * nodeWorldTransInv);
		//spin = Abc::extractQuat(spin.toMatrix44() * nodeWorldTransInv);

		bbox.extendBy( pos );




		positionVec.push_back( pos );
		velocityVec.push_back( vel );
		scaleVec.push_back( scale );
		widthVec.push_back( width );
		ageVec.push_back( age );
		idVec.push_back( id );
		orientationVec.push_back( orientation );
		angularVelocityVec.push_back( spin );

        shapeTypeVec.push_back( shapetype );
        shapeInstanceIDVec.push_back( shapeInstanceId );
        shapeTimeVec.push_back( shapeInstanceTime );
		colorVec.push_back( color );

		constantPos &= (pos == positionVec[0]);
		constantVel &= (vel == velocityVec[0]);
		constantScale &= (scale == scaleVec[0]);
		constantWidth &= (width == widthVec[0]);
		constantAge &= (age == ageVec[0]);
		constantOrientation &= (orientation == orientationVec[0]);
		constantAngularVel &= (spin == angularVelocityVec[0]);
		constantColor &= (color == colorVec[0]);

		// Set the archive bounding box
		// Positions for particles are already cnsider to be in world space
		if (mJob)
		{
			mJob->GetArchiveBBox().extendBy(pos);
		}

        }
	}

    }

  //  if (numParticles > 1)
  //  {
  //     ESS_PROFILE_SCOPE("AlembicPoints::Save - vectorResize");
  //      if (constantPos)        { positionVec.resize(1); }
  //      if (constantVel)        { velocityVec.resize(1); }
  //      if (constantScale)      { scaleVec.resize(1); }
  //      if (constantWidth)      { widthVec.resize(1); }
  //      if (constantAge)        { ageVec.resize(1); }
  //      if (constantOrientation){ orientationVec.resize(1); }
  //      if (constantAngularVel) { angularVelocityVec.resize(1); }
		//if (constantColor)		{ colorVec.resize(1); }
  //  }

    {
    ESS_PROFILE_SCOPE("AlembicPoints::Save - sample writing");
    // Store the information into our properties and points schema
    Abc::P3fArraySample positionSample( positionVec);
    Abc::P3fArraySample velocitySample(velocityVec);
    Abc::P3fArraySample scaleSample(scaleVec);
    Abc::FloatArraySample widthSample(widthVec);
    Abc::FloatArraySample ageSample(ageVec);
    Abc::FloatArraySample massSample(massVec);
    Abc::FloatArraySample shapeTimeSample(shapeTimeVec);
    Abc::UInt64ArraySample idSample(idVec);
    Abc::UInt16ArraySample shapeTypeSample(shapeTypeVec);
    Abc::UInt16ArraySample shapeInstanceIDSample(shapeInstanceIDVec);
    Abc::QuatfArraySample orientationSample(orientationVec);
    Abc::QuatfArraySample angularVelocitySample(angularVelocityVec);
    Abc::C4fArraySample colorSample(colorVec);  

    mScaleProperty.set(scaleSample);
    mAgeProperty.set(ageSample);
    mMassProperty.set(massSample);
    mShapeTimeProperty.set(shapeTimeSample);
    mShapeTypeProperty.set(shapeTypeSample);
    mShapeInstanceIDProperty.set(shapeInstanceIDSample);
    mOrientationProperty.set(orientationSample);
    mAngularVelocityProperty.set(angularVelocitySample);
    mColorProperty.set(colorSample);

    mPointsSample.setPositions(positionSample);
    mPointsSample.setVelocities(velocitySample);
    mPointsSample.setWidths(AbcG::OFloatGeomParam::Sample(widthSample, AbcG::kVertexScope));
    mPointsSample.setIds(idSample);
    mPointsSample.setSelfBounds(bbox);
	mPointsSchema.getChildBoundsProperty().set( bbox);

    mPointsSchema.set(mPointsSample);
    }

    mNumSamples++;

	//mInstanceNames.pop_back();

	if(bAutomaticInstancing){
		saveCurrentFrameMeshes();
	}

	if(bRenderStateForced){
		ipfSystem->SetRenderState(false);
	}

    if(bLastFrame){
       ESS_PROFILE_SCOPE("AlembicParticles::Save - save instance names property");

       std::vector<std::string> instanceNames(mNumShapeMeshes);

       for(faceVertexHashToShapeMap::iterator it = mShapeMeshCache.begin(); it != mShapeMeshCache.end(); it++){
          std::stringstream pathStream;
          pathStream << "/" << it->second.name<< "/" << it->second.name <<"Shape";
          instanceNames[it->second.nMeshInstanceId] = pathStream.str();
       }

	   //for some reason the .dims property is not written when there is exactly one entry if we don't push an empty string
	   //having an extra unreferenced entry seems to be harmless
	   instanceNames.push_back("");

       mInstanceNamesProperty.set(Abc::StringArraySample(instanceNames));
    }

    return true;
}
Example #2
0
void BlobMesh::BuildMesh(TimeValue t)
{

	//TODO: Implement the code to build the mesh representation of the object
	//    using its parameter settings at the time passed. The plug-in should 
	//    use the data member mesh to store the built mesh.
	int numberOfNodes = pblock2->Count(pb_nodelist);

	float size, tension, renderCoarseness, viewCoarseness,coarseness;

	// Interval valid;
	pblock2->GetValue(pb_size,t,size,ivalid);
	pblock2->GetValue(pb_tension,t,tension,ivalid);
	if (tension < 0.011f) tension = 0.011f;
	if (tension > 1.0f) tension = 1.0f;

	pblock2->GetValue(pb_render,t,renderCoarseness,ivalid);
	pblock2->GetValue(pb_viewport,t,viewCoarseness,ivalid);

	BOOL autoCoarseness;
	pblock2->GetValue(pb_relativecoarseness,t,autoCoarseness,ivalid);

	BOOL largeDataSetOpt;
	pblock2->GetValue(pb_oldmetaballmethod,t,largeDataSetOpt,ivalid);


	BOOL useSoftSel;
	float minSize;
	pblock2->GetValue(pb_usesoftsel,t,useSoftSel,ivalid);
	pblock2->GetValue(pb_minsize,t,minSize,ivalid);

	if (inRender)
		coarseness = renderCoarseness;
	else coarseness = viewCoarseness;

	if (autoCoarseness)
		coarseness = size/coarseness;

	Tab<INode *> pfList;

	BOOL useAllPFEvents;
	pblock2->GetValue(pb_useallpf,0,useAllPFEvents,FOREVER);



	int pfCt = pblock2->Count(pb_pfeventlist);

	BOOL offInView;
	pblock2->GetValue(pb_offinview,0,offInView,FOREVER);



	for (int i = 0; i < pfCt; i++)
	{
		INode* node = NULL;
		pblock2->GetValue(pb_pfeventlist,0,node,FOREVER,i);
		if (node)
			pfList.Append(1,&node);
	}

	float thres = 0.6f;

	//need to get our tm
	if (selfNode == NULL)
	{
		MyEnumProc dep(true);
		DoEnumDependents(&dep);
		if (dep.Nodes.Count() > 0)
			selfNode = dep.Nodes[0];
	}

	Matrix3 baseTM(1);

	if (selfNode != NULL)
		baseTM = Inverse(selfNode->GetObjectTM(t));
	//loop throght the nodes

	if (!inRender)
	{
		if (offInView)
			numberOfNodes = 0;

	}

	std::vector<SphereData> data;
	data.reserve(500);

	for (int i = 0; i < numberOfNodes; i++)
	{
		INode* node = NULL;
		pblock2->GetValue(pb_nodelist,t,node,ivalid,i);

		if (node)
		{
			//get the nodes tm
			Matrix3 objectTM = node->GetObjectTM(t);
			Matrix3 toLocalSpace = objectTM*baseTM;

			ObjectState tos =  node->EvalWorldState(t,TRUE);

			if (tos.obj->IsParticleSystem())
			{
				SimpleParticle* pobj      = NULL;
				IParticleObjectExt* epobj = NULL;
				pobj = static_cast<SimpleParticle*>( tos.obj->GetInterface(I_SIMPLEPARTICLEOBJ) );
				if (pobj)
				{
					pobj->UpdateParticles(t, node);

					int count = pobj->parts.Count();
					data.reserve(data.size() + count);
					float closest = 999999999.9f;
					SphereData d;
					for (int pid = 0; pid < count; pid++)
					{
						TimeValue age  = pobj->ParticleAge(t,pid);
						TimeValue life = pobj->ParticleLife(t,pid);
						if (age != -1)
						{
							float psize = pobj->ParticleSize(t,pid);
							Point3 curval = pobj->parts.points[pid];
							d.center = curval * baseTM;
							d.radius = psize;
							d.oradius = psize;
							d.rsquare = psize * psize;
							d.tover4 = tension * d.rsquare *d.rsquare ;
							data.push_back(d);
						}
					}
				}
				else
				{
					epobj = (IParticleObjectExt*) tos.obj->GetInterface(PARTICLEOBJECTEXT_INTERFACE);
					if (epobj) 
					{
						
						epobj->UpdateParticles(node, t);

						int count = epobj->NumParticles();
						data.reserve(data.size() + count);
						for (int pid = 0; pid < count; pid++)
						{
							TimeValue age  = epobj->GetParticleAgeByIndex(pid);
							if (age!=-1)
							{
								INode *node = epobj->GetParticleGroup(pid);

								Point3 *curval = epobj->GetParticlePositionByIndex(pid);

								BOOL useParticle = TRUE;

								if (!useAllPFEvents)
								{
									useParticle = FALSE;
									for (int k = 0; k < pfList.Count(); k++)
									{
										if (node == pfList[k])
										{
											useParticle = TRUE;
											k = pfList.Count();
										}
									}
								}

								if ((curval) && (useParticle))
								{
									float scale = epobj->GetParticleScaleByIndex(pid) ;
									float psize = scale;

									SphereData d;
									d.center = *curval*baseTM;
									d.radius = psize;
									d.oradius = psize;
									d.rsquare = psize * psize;
									d.tover4 = tension * d.rsquare *d.rsquare ;
									data.push_back(d);
								}
							}
						}
					}

				}
			}
			else if (tos.obj->IsShapeObject()) 
			{
				PolyShape shape;

				ShapeObject *pathOb = (ShapeObject*)tos.obj;
				pathOb->MakePolyShape(t, shape);
				
				// first find out how many points there are:
				size_t num_points = 0;
				for (int i = 0; i < shape.numLines; i++)
				{
					PolyLine& line = shape.lines[i];
					num_points += line.numPts;
				}
				data.reserve(data.size() + num_points);

				for (int i = 0; i < shape.numLines; i++)
				{
					PolyLine line = shape.lines[i];
					for (int j = 0; j < line.numPts; j++)
					{
						SphereData d;
						float tsize = size;
						d.center = line.pts[j].p*toLocalSpace;
						d.radius = tsize;
						d.oradius = tsize;
						d.rsquare = tsize * tsize;
						d.tover4 = tension * d.rsquare *d.rsquare ;
						data.push_back(d);
					}
				}
			}
			else if (tos.obj->SuperClassID()==GEOMOBJECT_CLASS_ID)
			{
				SphereData d;
				BOOL converted = FALSE;

				TriObject *triObj = NULL;


				if(tos.obj->IsSubClassOf(triObjectClassID)) 
				{
					triObj = (TriObject *)tos.obj;
				}

				// If it can convert to a TriObject, do it
				else if(tos.obj->CanConvertToType(triObjectClassID)) 
				{
					triObj = (TriObject *)tos.obj->ConvertToType(t, triObjectClassID);
					converted = TRUE;
				}

				if (triObj != NULL)
				{
					Mesh* mesh = &triObj->GetMesh();
					if (mesh)
					{
						int vcount = mesh->getNumVerts();
						float *vsw = mesh->getVSelectionWeights ();
						BitArray vsel =  mesh->VertSel();
						data.reserve(data.size() + vcount);
						for (int j = 0; j < vcount; j++)
						{
							float tsize = size;
							if (useSoftSel)
							{
								tsize = 0.0f;
								if (vsw)
								{
									float v = 0.0f;
									if (vsel[j]) 
										v = 1.0f;
									else 
									{
										if (vsw)
											v = vsw[j];
									}
									if (v == 0.0f)
										tsize = 0.0f;
									else
									{
										tsize = minSize + (size -minSize)*v;
									}

								}
								else
								{
									float v = 0.0f;
									if (vsel[j]) 
										v = 1.0f;
									tsize = minSize + (size -minSize)*v;
								}
							}
							if (tsize != 0.0f)
							{
								d.center = mesh->getVert(j)*toLocalSpace;
								d.radius = tsize;
								d.oradius = tsize;
								d.rsquare = tsize * tsize;
								d.tover4 = tension * d.rsquare *d.rsquare ;
								data.push_back(d);
							}
						}
					}

					if (converted) triObj->DeleteThis();
				}
			}
			else
			{
				SphereData d;
				d.center = Point3(0.0f,0.0f,0.0f)*toLocalSpace;
				d.radius = size;
				d.oradius = size;
				d.rsquare = size * size;
				d.tover4 = tension * d.rsquare *d.rsquare ;
				data.push_back(d);
			}
		}
	}


	if ((data.size() == 0) && (numberOfNodes==0))
	{
		data.resize(1);
		data[0].center = Point3(0.0f,0.0f,0.0f);
		data[0].radius = size;
		data[0].oradius = size;
		data[0].rsquare = size * size;
		data[0].tover4 = tension * data[0].rsquare *data[0].rsquare ;

	}

	if (data.size() > 0)
	{
		int iRes = 1;
		if (!largeDataSetOpt)
		{
			MetaParticle oldBlob;
			iRes = oldBlob.CreatePodMetas(&data[0],(int)data.size(),&mesh,thres,coarseness);
		}
		else
		{
			MetaParticleFast blob;
			iRes = blob.CreatePodMetas(&data[0],(int)data.size(),&mesh,thres,coarseness);
		}

		// An out of memory error is the only reason iRes would be zero in either case
		if( (iRes == 0) && GetCOREInterface() )
			GetCOREInterface()->DisplayTempPrompt(GetString(IDS_NOT_ENOUGH_MEM), 5000 );
	}
	else
	{
		mesh.setNumFaces(0);
		mesh.setNumVerts(0);
	}

	mesh.InvalidateTopologyCache();

	ivalid.Set(t,t);
}