Exemplo n.º 1
0
void 
simpleFluidEmitter::surfaceFluidEmitter(
	MFnFluid& 		fluid,
	const MMatrix&	fluidWorldMatrix,
	int 			plugIndex,
	MDataBlock& 	block,
	double 			dt,
	double			conversion,
	double			dropoff
)
//==============================================================================
//
//	Method:	
//
//		simpleFluidEmitter::surfaceFluidEmitter
//
//	Description:
//
//		Emits fluid from one of a predefined set of volumes (cube, sphere,
//		cylinder, cone, torus).
//
//	Parameters:
//
//		fluid:				fluid into which we are emitting
//		fluidWorldMatrix:	object->world matrix for the fluid
//		plugIndex:			identifies which fluid connected to the emitter
//							we are emitting into
//		block:				datablock for the emitter, to retrieve attribute
//							values
//		dt:					time delta for this frame
//		conversion:			mapping from UI emission rates to internal units
//		dropoff:			specifies how much emission rate drops off as
//							the surface points move away from the centers
//							of the voxels in which they lie.
//
//	Notes:
//		
//		To associate an owner object with an emitter, use the
//		addDynamic MEL command, e.g. "addDynamic simpleFluidEmitter1 pPlane1".
//
//==============================================================================
{
	//	get relevant world matrices
	//
	MMatrix fluidInverseWorldMatrix = fluidWorldMatrix.inverse();
	
	//	get emission rates for density, fuel, heat, and emission color
	//	
	double densityEmit = fluidDensityEmission( block );
	double fuelEmit = fluidFuelEmission( block );
	double heatEmit = fluidHeatEmission( block );
	bool doEmitColor = fluidEmitColor( block );
	MColor emitColor = fluidColor( block );
	
	//	rate modulation based on frame time, user value conversion factor, and
	//	standard emitter "rate" value (not actually exposed in most fluid
	//	emitters, but there anyway).
	//
	double theRate = getRate(block) * dt * conversion;

	//	get voxel dimensions and sizes (object space)
	//
	double size[3];
	unsigned int res[3];
	fluid.getDimensions( size[0], size[1], size[2] );
	fluid.getResolution( res[0], res[1], res[2] );
		
	//	voxel sizes
	double dx = size[0] / res[0];
	double dy = size[1] / res[1];
	double dz = size[2] / res[2];
	
	//	voxel centers
	double Ox = -size[0]/2;
	double Oy = -size[1]/2;
	double Oz = -size[2]/2;	

	//	get the "swept geometry" data for the emitter surface.  This structure
	//	tracks the motion of each emitter triangle over the time interval
	//	for this simulation step.  We just use positions on the emitter
	//	surface at the end of the time step to do the emission.
	//
	MDataHandle sweptHandle = block.inputValue( mSweptGeometry );
	MObject sweptData = sweptHandle.data();
	MFnDynSweptGeometryData fnSweptData( sweptData );

	//	for "non-jittered" sampling, just reset the random state for each 
	//	triangle, which gives us a fixed set of samples all the time.
	//	Sure, they're still jittered, but they're all jittered the same,
	//	which makes them kinda uniform.
	//
	bool jitter = fluidJitter(block);
	if( !jitter )
	{
		resetRandomState( plugIndex, block );
	}

	if( fnSweptData.triangleCount() > 0 )
	{
		//	average voxel face area - use this as the canonical unit that
		//	receives the emission rate specified by the users.  Scale the
		//	rate for other triangles accordingly.
		//
		double vfArea = pow(dx*dy*dz, 2.0/3.0);
		
		//	very rudimentary support for textured emission rate and
		//	textured emission color.  We simply sample each texture once
		//	at the center of each emitter surface triangle.  This will 
		//	cause aliasing artifacts when these triangles are large.
		//
		MFnDependencyNode fnNode( thisMObject() );
		MObject rateTextureAttr = fnNode.attribute( "textureRate" );
		MObject colorTextureAttr = fnNode.attribute( "particleColor" );

		bool texturedRate = hasValidEmission2dTexture( rateTextureAttr );
		bool texturedColor = hasValidEmission2dTexture( colorTextureAttr );
		
		//	construct texture coordinates for each triangle center
		//	
		MDoubleArray uCoords, vCoords;
		if( texturedRate || texturedColor )
		{
			uCoords.setLength( fnSweptData.triangleCount() );
			vCoords.setLength( fnSweptData.triangleCount() );
			
			int t;
			for( t = 0; t < fnSweptData.triangleCount(); t++ )
			{
				MDynSweptTriangle tri = fnSweptData.sweptTriangle( t );
				MVector uv0 = tri.uvPoint(0);
				MVector uv1 = tri.uvPoint(1);
				MVector uv2 = tri.uvPoint(2);
				
				MVector uvMid = (uv0+uv1+uv2)/3.0;
				uCoords[t] = uvMid[0];
				vCoords[t] = uvMid[1];
			}
		}

		//	evaluate textured rate and color values at the triangle centers
		//
		MDoubleArray texturedRateValues;
		if( texturedRate )
		{
			texturedRateValues.setLength( uCoords.length() );
			evalEmission2dTexture( rateTextureAttr, uCoords, vCoords, NULL, &texturedRateValues );
		}
		
		MVectorArray texturedColorValues;
		if( texturedColor )
		{
			texturedColorValues.setLength( uCoords.length() );
			evalEmission2dTexture( colorTextureAttr, uCoords, vCoords, &texturedColorValues, NULL );
		}
		
		for( int t = 0; t < fnSweptData.triangleCount(); t++ )
		{
			//	calculate emission rate and color values for this triangle
			//
			double curTexturedRate = texturedRate ? texturedRateValues[t] : 1.0;
			MColor curTexturedColor;
			if( texturedColor )
			{
				MVector& curVec = texturedColorValues[t];
				curTexturedColor.r = (float)curVec[0];
				curTexturedColor.g = (float)curVec[1];
				curTexturedColor.b = (float)curVec[2];
				curTexturedColor.a = 1.0;
			}
			else
			{
				curTexturedColor = emitColor;
			}

			MDynSweptTriangle tri = fnSweptData.sweptTriangle( t );
			MVector v0 = tri.vertex(0);
			MVector v1 = tri.vertex(1);
			MVector v2 = tri.vertex(2);

			//	compute number of samples for this triangle based on area,
			//	with large triangles receiving approximately 1 sample for 
			//	each voxel that they intersect
			//
			double triArea = tri.area();
			int numSamples = (int)(triArea / vfArea);
			if( numSamples < 1 ) numSamples = 1;
			
			//	compute emission rate for the points on the triangle.
			//	Scale the canonical rate by the area ratio of this triangle
			//	to the average voxel size, then split it amongst all the samples.
			//
			double triRate = (theRate*(triArea/vfArea))/numSamples;
			
			triRate *= curTexturedRate;
			
			for( int j = 0; j < numSamples; j++ )
			{
				//	generate a random point on the triangle,
				//	map it into fluid local space
				//
				double r1 = randgen();
				double r2 = randgen();
				
				if( r1 + r2 > 1 )
				{
					r1 = 1-r1;
					r2 = 1-r2;
				}
				double r3 = 1 - (r1+r2);
				MPoint randPoint = r1*v0 + r2*v1 + r3*v2;
				randPoint *= fluidInverseWorldMatrix;
				
				//	figure out where the current point lies
				//
				::int3 coord;
				fluid.toGridIndex( randPoint, coord );
				
				if( (coord[0]<0) || (coord[1]<0) || (coord[2]<0) ||
					(coord[0]>=(int)res[0]) || (coord[1]>=(int)res[1]) || (coord[2]>=(int)res[2]) )
				{
					continue;
				}
				
				//	do some falloff based on how far from the voxel center 
				//	the current point lies
				//
				MPoint gridPoint;
				gridPoint.x = Ox + (coord[0]+0.5)*dx;
				gridPoint.y = Oy + (coord[1]+0.5)*dy;
				gridPoint.z = Oz + (coord[2]+0.5)*dz;
				
				MVector diff = gridPoint - randPoint;
				double distSquared = diff * diff;
				double distDrop = dropoff * distSquared;
				
				double newVal = triRate * exp( -distDrop );
		
				//	emit into the voxel
				//
				if( newVal != 0 )
				{
					fluid.emitIntoArrays( (float) newVal, coord[0], coord[1], coord[2], (float)densityEmit, (float)heatEmit, (float)fuelEmit, doEmitColor, curTexturedColor );		
				}
			}
		}
	}
}
Exemplo n.º 2
0
void 
simpleFluidEmitter::volumeFluidEmitter(
	MFnFluid& 		fluid,
	const MMatrix&	fluidWorldMatrix,
	int 			plugIndex,
	MDataBlock& 	block,
	double 			dt,
	double			conversion,
	double			dropoff
)
//==============================================================================
//
//	Method:	
//
//		simpleFluidEmitter::volumeFluidEmitter
//
//	Description:
//
//		Emits fluid from points distributed over the surface of the 
//		emitter's owner object.
//
//	Parameters:
//
//		fluid:				fluid into which we are emitting
//		fluidWorldMatrix:	object->world matrix for the fluid
//		plugIndex:			identifies which fluid connected to the emitter
//							we are emitting into
//		block:				datablock for the emitter, to retrieve attribute
//							values
//		dt:					time delta for this frame
//		conversion:			mapping from UI emission rates to internal units
//		dropoff:			specifies how much emission rate drops off as
//							we move away from the local y-axis of the 
//							volume emitter shape.
//
//==============================================================================
{
	//	get emitter position and relevant matrices 
	//	
	MPoint emitterPos = getWorldPosition();
	MMatrix emitterWorldMatrix = getWorldMatrix();
	MMatrix fluidInverseWorldMatrix = fluidWorldMatrix.inverse();
	
	//	get emission rates for density, fuel, heat, and emission color
	//	
	double densityEmit = fluidDensityEmission( block );
	double fuelEmit = fluidFuelEmission( block );
	double heatEmit = fluidHeatEmission( block );
	bool doEmitColor = fluidEmitColor( block );
	MColor emitColor = fluidColor( block );
	
	//	rate modulation based on frame time, user value conversion factor, and
	//	standard emitter "rate" value (not actually exposed in most fluid
	//	emitters, but there anyway).
	//
	double theRate = getRate(block) * dt * conversion;
	
	//	get voxel dimensions and sizes (object space)
	//
	double size[3];
	unsigned int res[3];
	fluid.getDimensions( size[0], size[1], size[2] );
	fluid.getResolution( res[0], res[1], res[2] );

	//	voxel sizes
	double dx = size[0] / res[0];
	double dy = size[1] / res[1];
	double dz = size[2] / res[2];
	
	// 	voxel centers
	double Ox = -size[0]/2;
	double Oy = -size[1]/2;
	double Oz = -size[2]/2;	

	//	find the voxels that intersect the bounding box of the volume
	//	primitive associated with the emitter
	//
	MBoundingBox bbox;
	if( !volumePrimitiveBoundingBox( bbox ) )
	{
		//	shouldn't happen
		//
		return;
	}
	
	//	transform volume primitive into fluid space
	//
	bbox.transformUsing( emitterWorldMatrix );
	bbox.transformUsing( fluidInverseWorldMatrix );
	MPoint lowCorner = bbox.min();
	MPoint highCorner = bbox.max();

	//	get fluid voxel coord range of bounding box
	//
	::int3 lowCoords;
	::int3 highCoords;
	fluid.toGridIndex( lowCorner, lowCoords );
	fluid.toGridIndex( highCorner, highCoords );
	
	int i;
	for ( i = 0; i < 3; i++ )
	{
		if ( lowCoords[i] < 0 ) {
			lowCoords[i] = 0;
		} else if ( lowCoords[i] > ((int)res[i])-1 ) {
			lowCoords[i] = ((int)res[i])-1;
		}

		if ( highCoords[i] < 0 ) {
			highCoords[i] = 0;
		} else if ( highCoords[i] > ((int)res[i])-1 ) {
			highCoords[i] = ((int)res[i])-1;
		}
		
	}

	//	figure out the emitter size relative to the voxel size, and compute
	//	a per-voxel sampling rate that uses 1 sample/voxel for emitters that
	//	are >= 2 voxels big in all dimensions.  For smaller emitters, use up
	//	to 8 samples per voxel.
	//
	double emitterVoxelSize[3];
	emitterVoxelSize[0] = (highCorner[0]-lowCorner[0])/dx;
	emitterVoxelSize[1] = (highCorner[1]-lowCorner[1])/dy;
	emitterVoxelSize[2] = (highCorner[2]-lowCorner[2])/dz;
		
	double minVoxelSize = MIN(emitterVoxelSize[0],MIN(emitterVoxelSize[1],emitterVoxelSize[2]));
	if( minVoxelSize < 1.0 )
	{
		minVoxelSize = 1.0;
	}
	int maxSamples = 8;
	int numSamples = (int)(8.0/(minVoxelSize*minVoxelSize*minVoxelSize) + 0.5);
	if( numSamples < 1 ) numSamples = 1;
	if( numSamples > maxSamples ) numSamples = maxSamples;
	
	//	non-jittered, just use one sample in the voxel center.  Should replace
	//	with uniform sampling pattern.
	//
	bool jitter = fluidJitter(block);
	if( !jitter )
	{
		numSamples = 1;
	}
	
	//	for each voxel that could potentially intersect the volume emitter
	//	primitive, take some samples in the voxel.  For those inside the
	//	volume, compute their dropoff relative to the primitive's local y-axis,
	//	and emit an appropriate amount into the voxel.
	//
	for( i = lowCoords[0]; i <= highCoords[0]; i++ )
	{
		double x = Ox + (i+0.5)*dx;
			
		for( int j = lowCoords[1]; j < highCoords[1]; j++ )
		{
			double y = Oy + (j+0.5)*dy;

			for( int k = lowCoords[2]; k < highCoords[2]; k++ )
			{
				double z = Oz + (k+0.5)*dz;
				
				for ( int si = 0; si < numSamples; si++) {
					
					//	compute voxel sample point (object space)
					//
					double rx, ry, rz;
					if(jitter) {
						rx = x + dx*(randgen() - 0.5);
						ry = y + dy*(randgen() - 0.5);
						rz = z + dz*(randgen() - 0.5);
					} else {
						rx = x;
						ry = y;
						rz = z;
					}
					
					//	to world space
					MPoint pt( rx, ry, rz );
					pt *= fluidWorldMatrix;

					//	test to see if point is inside volume primitive
					//
					if( volumePrimitivePointInside( pt, emitterWorldMatrix ) )
					{
						//	compute dropoff
						//
						double dist = pt.distanceTo( emitterPos );
						double distDrop = dropoff * (dist*dist);
						double newVal = (theRate * exp( -distDrop )) / (double)numSamples;
						
						//	emit into arrays
						//
						if( newVal != 0.0 )
						{
							fluid.emitIntoArrays( (float) newVal, i, j, k, (float)densityEmit, (float)heatEmit, (float)fuelEmit, doEmitColor, emitColor );
						}
					}
				}
			}
		}
	}
}