MStatus simpleFluidEmitter::fluidEmitter( const MObject& fluidObject, const MMatrix& worldMatrix, int plugIndex ) //============================================================================== // // Description: // // Callback function that gets called once per frame by each fluid // into which this emitter is emitting. Emits values directly into // the fluid object. The MFnFluid object passed to this routine is // not pointing to a DAG object, it is pointing to an internal fluid // data structure that the fluid node is constructing, eventually to // be set into the fluid's output attribute. // // Parameters: // // fluid: fluid into which we are emitting // worldMatrix: object->world matrix for the fluid // plugIndex: identifies which fluid connected to the emitter // we are emitting into // // Returns: // // MS::kSuccess if the method wishes to override the default // emitter behaviour // MS::kUnknownParameter if the method wishes to have the default // emitter behaviour execute after this routine // exits. // // Notes: // // The method first does some work common to all emitter types, then // calls one of 4 different methods to actually do the emission. // The methods are: // // omniEmitter: omni-directional emitter from a point, // or from the vertices of an owner object. // // volumeEmitter: emits from the surface of an exact cube, sphere, // cone, cylinder, or torus. // // surfaceEmitter: emits from the surface of an owner object. // //============================================================================== { // make sure the fluid is valid. If it isn't, return MS::kSuccess, indicating // that no work needs to be done. If we return a failure code, then the default // fluid emitter code will try to run, which is pointless if the fluid is not // valid. // MFnFluid fluid( fluidObject ); if( fluid.object() != MObject::kNullObj ) { return MS::kSuccess; } // get a data block for the emitter, so we can get attribute values // MDataBlock block = forceCache(); // figure out the time interval for emission for the given fluid // double dTime = getDeltaTime( plugIndex, block ).as(MTime::kSeconds); if( dTime == 0.0 ) { // shouldn't happen, but if the time interval is 0, then no fluid should // be emitted return MS::kSuccess; } // if currentTime <= startTime, return. The startTime is connected to // the target fluid object. // MTime cTime = getCurrentTime( block ); MTime sTime = getStartTime( plugIndex, block ); // if we are at or before the start time, reset the random number // state to the appropriate seed value for the given fluid // if( cTime < sTime ) { resetRandomState( plugIndex, block ); return MS::kSuccess; } // check to see if we need to emit anything into the target fluid. // if the emission rate is 0, or if the fluid doesn't have a grid // for one of the quantities, then we needn't do any emission // // emission rates double density = fluidDensityEmission( block ); double heat = fluidHeatEmission( block ); double fuel = fluidFuelEmission( block ); bool doColor = fluidEmitColor( block ); // fluid grid settings MFnFluid::FluidMethod densityMode, tempMode, fuelMode; MFnFluid::ColorMethod colorMode; MFnFluid::FluidGradient grad; MFnFluid::FalloffMethod falloffMode; fluid.getDensityMode( densityMode, grad ); fluid.getTemperatureMode( tempMode, grad ); fluid.getFuelMode( fuelMode, grad ); fluid.getColorMode( colorMode ); fluid.getFalloffMode( falloffMode ); // see if we need to emit density, heat, fuel, or color bool densityToEmit = (density != 0.0) && ((densityMode == MFnFluid::kDynamicGrid)||(densityMode == MFnFluid::kStaticGrid)); bool heatToEmit = (heat != 0.0) && ((tempMode == MFnFluid::kDynamicGrid)||(tempMode == MFnFluid::kStaticGrid)); bool fuelToEmit = (fuel != 0.0) && ((fuelMode == MFnFluid::kDynamicGrid)||(fuelMode == MFnFluid::kStaticGrid)); bool colorToEmit = doColor && ((colorMode == MFnFluid::kDynamicColorGrid)||(colorMode == MFnFluid::kStaticColorGrid)); bool falloffEmit = (falloffMode == MFnFluid::kStaticFalloffGrid); // nothing to emit, do nothing // if( !densityToEmit && !heatToEmit && !fuelToEmit && !colorToEmit && !falloffEmit ) { return MS::kSuccess; } // get the dropoff rate for the fluid // double dropoff = fluidDropoff( block ); // modify the dropoff rate to account for fluids that have // been scaled in worldspace - larger scales mean slower // falloffs and vice versa // MTransformationMatrix xform( worldMatrix ); double xformScale[3]; xform.getScale( xformScale, MSpace::kWorld ); double dropoffScale = sqrt( xformScale[0]*xformScale[0] + xformScale[1]*xformScale[1] + xformScale[2]*xformScale[2] ); if( dropoffScale > 0.1 ) { dropoff /= dropoffScale; } // retrieve the current random state from the "randState" attribute, and // store it in the member variable "randState". We will use this member // value numerous times via the randgen() method. Once we are done emitting, // we will set the random state back into the attribute via setRandomState(). // getRandomState( plugIndex, block ); // conversion value used to map user input emission rates into internal // values. // double conversion = 0.01; MEmitterType emitterType = getEmitterType( block ); switch( emitterType ) { case kOmni: omniFluidEmitter( fluid, worldMatrix, plugIndex, block, dTime, conversion, dropoff ); break; case kVolume: volumeFluidEmitter( fluid, worldMatrix, plugIndex, block, dTime, conversion, dropoff ); break; case kSurface: surfaceFluidEmitter( fluid, worldMatrix, plugIndex, block, dTime, conversion, dropoff ); break; default: break; } // store the random state back into the datablock // setRandomState( plugIndex, block ); return MS::kSuccess; }
// // A very simple implementation of validAndSetValue(). No lock // or limit checking on the rocking attribute is done in this method. // If you wish to apply locks and limits to the rocking attribute, you // would follow the approach taken in the rockingTransformCheck example. // Meaning you would implement methods similar to: // * applyRotationLocks(); // * applyRotationLimits(); // * checkAndSetRotation(); // but for the rocking attribute. The method checkAndSetRotation() // would be called below rather than updating the rocking attribute // directly. // MStatus rockingTransformNode::validateAndSetValue(const MPlug& plug, const MDataHandle& handle, const MDGContext& context) { MStatus status = MS::kSuccess; // Make sure that there is something interesting to process. // if (plug.isNull()) return MS::kFailure; MDataBlock block = forceCache(*(MDGContext *)&context); MDataHandle blockHandle = block.outputValue(plug, &status); ReturnOnError(status); MString cachename = block.inputValue( acachename ).asString(); MString meshname = block.inputValue( ameshname ).asString(); /* if ( plug == aRockInX ) { // Update our new rock in x value double rockInX = handle.asDouble(); blockHandle.set(rockInX); rockXValue = rockInX; // Update the custom transformation matrix to the // right rock value. rockingTransformMatrix *ltm = getRockingTransformMatrix(); if (ltm) ltm->setRockInX(rockXValue); else MGlobal::displayError("Failed to get rock transform matrix"); blockHandle.setClean(); // Mark the matrix as dirty so that DG information // will update. dirtyMatrix(); } */ if ( plug == aframe ) { // Update our new rock in x value double rockInX = handle.asDouble(); blockHandle.set(rockInX); rockXValue = rockInX; // Update the custom transformation matrix to the // right rock value. rockingTransformMatrix *ltm = getRockingTransformMatrix(); if (ltm) ltm->setRockInX(rockXValue, cachename, meshname); else MGlobal::displayError("Failed to get rock transform matrix"); blockHandle.setClean(); // Mark the matrix as dirty so that DG information // will update. dirtyMatrix(); } // Allow processing for other attributes return ParentClass::validateAndSetValue(plug, handle, context); }