MBoundingBox ovalLocator::boundingbox (float multiplier /*=1.0f*/) {
	static MBoundingBox boundingbox ;
	if ( boundingbox.min () == boundingbox.max () ) {
		MPointArray vert =ovalLocator::vertices () ;
		for ( unsigned int i =0 ; i < vert.length () ; i++ )
			boundingbox.expand (vert [i]) ;
	}
	MBoundingBox bbox (boundingbox) ;
	if ( multiplier != 1.0f ) {
		double factors [3] ={ multiplier, multiplier, multiplier } ;
		MTransformationMatrix mat ;
		mat.setScale (factors, MSpace::kWorld) ;
		bbox.transformUsing (mat.asScaleMatrix ()) ;
	}
	return (bbox) ;
}
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 );
						}
					}
				}
			}
		}
	}
}
MBoundingBox AbcWriteJob::getBoundingBox(double iFrame, const MMatrix & eMInvMat)
{
    MStatus status;
    MBoundingBox curBBox;

    if (iFrame == mFirstFrame)
    {
        // Set up bbox shape map in the first frame.
        // If we have a lot of transforms and shapes, we don't need to
        // iterate them for each frame.
        MItDag dagIter;
        for (dagIter.reset(mCurDag); !dagIter.isDone(); dagIter.next())
        {
            MObject object = dagIter.currentItem();
            MDagPath path;
            dagIter.getPath(path);

            // short-circuit if the selection flag is on but this node is not in the
            // active selection

            // MGlobal::isSelected(ob) doesn't work, because DG node and DAG node is
            // not the same even if they refer to the same MObject
            if (mArgs.useSelectionList && !mSList.hasItem(path))
            {
                dagIter.prune();
                continue;
            }

            MFnDagNode dagNode(path, &status);
            if (status == MS::kSuccess)
            {
                // check for riCurves flag for flattening all curve object to
                // one curve group
                MPlug riCurvesPlug = dagNode.findPlug("riCurves", &status);
                if ( status == MS::kSuccess && riCurvesPlug.asBool() == true)
                {
                    MBoundingBox box = dagNode.boundingBox();
                    box.transformUsing(path.exclusiveMatrix()*eMInvMat);
                    curBBox.expand(box);

                    // Prune this curve group
                    dagIter.prune();

                    // Save children paths
                    std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
                        mBBoxShapeMap.insert(std::make_pair(mCurDag, util::ShapeSet())).first;
                    if (iter != mBBoxShapeMap.end())
                        (*iter).second.insert(path);
                }
                else if (object.hasFn(MFn::kParticle)
                    || object.hasFn(MFn::kMesh)
                    || object.hasFn(MFn::kNurbsCurve)
                    || object.hasFn(MFn::kNurbsSurface) )
                {
                    if (util::isIntermediate(object))
                        continue;

                    MBoundingBox box = dagNode.boundingBox();
                    box.transformUsing(path.exclusiveMatrix()*eMInvMat);
                    curBBox.expand(box);

                    // Save children paths
                    std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
                        mBBoxShapeMap.insert(std::make_pair(mCurDag, util::ShapeSet())).first;
                    if (iter != mBBoxShapeMap.end())
                        (*iter).second.insert(path);
                }
            }
        }
    }
    else
    {
        // We have already find out all the shapes for the dag path.
        std::map< MDagPath, util::ShapeSet, util::cmpDag >::iterator iter =
            mBBoxShapeMap.find(mCurDag);
        if (iter != mBBoxShapeMap.end())
        {
            // Iterate through the saved paths to calculate the box.
            util::ShapeSet& paths = (*iter).second;
            for (util::ShapeSet::iterator pathIter = paths.begin();
                pathIter != paths.end(); pathIter++)
            {
                MFnDagNode dagNode(*pathIter, &status);
                if (status == MS::kSuccess)
                {
                    MBoundingBox box = dagNode.boundingBox();
                    box.transformUsing((*pathIter).exclusiveMatrix()*eMInvMat);
                    curBBox.expand(box);
                }
            }
        }
    }

    return curBBox;
}