void simpleFluidEmitter::omniFluidEmitter( MFnFluid& fluid, const MMatrix& fluidWorldMatrix, int plugIndex, MDataBlock& block, double dt, double conversion, double dropoff ) //============================================================================== // // Method: // // simpleFluidEmitter::omniFluidEmitter // // Description: // // Emits fluid from a point, or from a set of object control points. // // 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 each emission point. // // Notes: // // If no owner object is present for the emitter, we simply emit from // the emitter position. If an owner object is present, then we emit // from each control point of that object in an identical fashion. // // To associate an owner object with an emitter, use the // addDynamic MEL command, e.g. "addDynamic simpleFluidEmitter1 pPlane1". // //============================================================================== { // find the positions that we need to emit from // MVectorArray emitterPositions; // first, try to get them from an owner object, which will have its // "ownerPositionData" attribute feeding into the emitter. These // values are in worldspace // bool gotOwnerPositions = false; MObject ownerShape = getOwnerShape(); if( ownerShape != MObject::kNullObj ) { MStatus status; MDataHandle hOwnerPos = block.inputValue( mOwnerPosData, &status ); if( status == MS::kSuccess ) { MObject dOwnerPos = hOwnerPos.data(); MFnVectorArrayData fnOwnerPos( dOwnerPos ); MVectorArray posArray = fnOwnerPos.array( &status ); if( status == MS::kSuccess ) { // assign vectors from block to ownerPosArray. // for( unsigned int i = 0; i < posArray.length(); i ++ ) { emitterPositions.append( posArray[i] ); } gotOwnerPositions = true; } } } // there was no owner object, so we just use the emitter position for // emission. // if( !gotOwnerPositions ) { MPoint emitterPos = getWorldPosition(); emitterPositions.append( emitterPos ); } // 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; // emission will only happen for voxels whose centers lie within // "minDist" and "maxDist" of an emitter position // double minDist = getMinDistance( block ); double maxDist = getMaxDistance( block ); // bump up the min/max distance values so that they // are both > 0, and there is at least about a half // voxel between the min and max values, to prevent aliasing // artifacts caused by emitters missing most voxel centers // MTransformationMatrix fluidXform( fluidWorldMatrix ); double fluidScale[3]; fluidXform.getScale( fluidScale, MSpace::kWorld ); // compute smallest voxel diagonal length double wsX = fabs(fluidScale[0]*dx); double wsY = fabs(fluidScale[1]*dy); double wsZ = fabs(fluidScale[2]*dz); double wsMin = MIN( MIN( wsX, wsY), wsZ ); double wsMax = MAX( MAX( wsX, wsY), wsZ ); double wsDiag = wsMin * sqrt(3.0); // make sure emission range is bigger than 0.5 voxels if ( maxDist <= minDist || maxDist <= (wsDiag/2.0) ) { if ( minDist < 0 ) minDist = 0; maxDist = minDist + wsDiag/2.0; dropoff = 0; } // Now, it's time to actually emit into the fluid: // // foreach emitter point // foreach voxel // - select some points in the voxel // - compute a dropoff function from the emitter point // - emit an appropriate amount of fluid into the voxel // // Since we've already expanded the min/max distances to cover // the smallest voxel dimension, we should only need 1 sample per // voxel, unless the voxels are highly non-square. We increase the // number of samples in these cases. // // If the "jitter" flag is enabled, we jitter each sample position, // using the rangen() function, which keeps track of independent // random states for each fluid, to make sure that results are // repeatable for multiple simulation runs. // // basic sample count int numSamples = 1; // increase samples if necessary for non-square voxels if(wsMin >.00001) { numSamples = (int)(wsMax/wsMin + .5); if(numSamples > 8) numSamples = 8; if(numSamples < 1) numSamples = 1; } bool jitter = fluidJitter(block); if( !jitter ) { // I don't have a good uniform sample generator for an // arbitrary number of samples. It would be a good idea to use // one here. For now, just use 1 sample for the non-jittered case. // numSamples = 1; } for( unsigned int p = 0; p < emitterPositions.length(); p++ ) { MPoint emitterWorldPos = emitterPositions[p]; // loop through all voxels, looking for ones that lie at least // partially within the dropoff field around this emitter point // for( unsigned int i = 0; i < res[0]; i++ ) { double x = Ox + i*dx; for( unsigned int j = 0; j < res[1]; j++ ) { double y = Oy + j*dy; for( unsigned int k = 0; k < res[2]; k++ ) { double z = Oz + k*dz; int si; for( si = 0; si < numSamples; si++ ) { // compute sample point (fluid object space) // double rx, ry, rz; if( jitter ) { rx = x + randgen()*dx; ry = y + randgen()*dy; rz = z + randgen()*dz; } else { rx = x + 0.5*dx; ry = y + 0.5*dy; rz = z + 0.5*dz; } // compute distance from sample to emitter point // MPoint point( rx, ry, rz ); point *= fluidWorldMatrix; MVector diff = point - emitterWorldPos; double distSquared = diff * diff; double dist = diff.length(); // discard if outside min/max range // if( (dist < minDist) || (dist > maxDist) ) { continue; } // drop off the emission rate according to the falloff // parameter, and divide to accound for multiple samples // in the voxel // double distDrop = dropoff * distSquared; double newVal = theRate * exp( -distDrop ) / (double)numSamples; // emit density/heat/fuel/color into the current voxel // if( newVal != 0 ) { fluid.emitIntoArrays( (float) newVal, i, j, k, (float)densityEmit, (float)heatEmit, (float)fuelEmit, doEmitColor, emitColor ); } float *fArray = fluid.falloff(); if( fArray != NULL ) { MPoint midPoint( x+0.5*dx, y+0.5*dy, z+0.5*dz ); midPoint.x *= 0.2; midPoint.y *= 0.2; midPoint.z *= 0.2; float fdist = (float) sqrt( midPoint.x*midPoint.x + midPoint.y*midPoint.y + midPoint.z*midPoint.z ); fdist /= sqrtf(3.0f); fArray[fluid.index(i,j,k)] = 1.0f-fdist; } } } } } } }
void exportF3d::setF3dField(MFnFluid &fluidFn, const char *outputPath, const MDagPath &dagPath) { try { MStatus stat; unsigned int i, xres = 0, yres = 0, zres = 0; double xdim,ydim,zdim; // Get the resolution of the fluid container stat = fluidFn.getResolution(xres, yres, zres); stat = fluidFn.getDimensions(xdim, ydim, zdim); V3d size(xdim,ydim,zdim); const V3i res(xres, yres, zres); int psizeTot = fluidFn.gridSize(); /// get the transform and rotation MObject parentObj = fluidFn.parent(0, &stat); if (stat != MS::kSuccess) { MGlobal::displayError("Can't find fluid's parent node"); return; } MDagPath parentPath = dagPath; parentPath.pop(); MTransformationMatrix tmatFn(dagPath.inclusiveMatrix()); if (stat != MS::kSuccess) { MGlobal::displayError("Failed to get transformation matrix of fluid's parent node"); return; } MFnTransform fnXform(parentPath, &stat); if (stat != MS::kSuccess) { MGlobal::displayError("Can't create a MFnTransform from fluid's parent node"); return; } if (m_verbose) { fprintf(stderr, "cellnum: %dx%dx%d = %d\n", xres, yres, zres,psizeTot); } float *density(NULL), *temp(NULL), *fuel(NULL); float *pressure(NULL), *falloff(NULL); density = fluidFn.density( &stat ); if ( stat.error() ) m_density = false; temp = fluidFn.temperature( &stat ); if ( stat.error() ) m_temperature = false; fuel = fluidFn.fuel( &stat ); if ( stat.error() ) m_fuel = false; pressure= fluidFn.pressure( &stat ); if ( stat.error() ) m_pressure = false; falloff = fluidFn.falloff( &stat ); if ( stat.error() ) m_falloff = false; float *r,*g,*b; if (m_color) { stat = fluidFn.getColors(r,b,g); if ( stat.error() ) m_color = false; }else m_color = false; float *u,*v,*w; if (m_texture) { stat = fluidFn.getCoordinates(u,v,w); if ( stat.error() ) m_texture = false; }else m_texture = false; /// velocity info float *Xvel(NULL),*Yvel(NULL), *Zvel(NULL); if (m_vel) { stat = fluidFn.getVelocity( Xvel,Yvel,Zvel ); if ( stat.error() ) m_vel = false; } if (m_density == false && m_temperature==false && m_fuel==false && m_pressure==false && m_falloff==false && m_vel == false && m_color == false && m_texture==false) { MGlobal::displayError("No fluid attributes found for writing, please check fluids settings"); return; } /// Fields DenseFieldf::Ptr densityFld, tempFld, fuelFld, pressureFld, falloffFld; DenseField3f::Ptr CdFld, uvwFld; MACField3f::Ptr vMac; MPlug autoResizePlug = fluidFn.findPlug("autoResize", &stat); bool autoResize; autoResizePlug.getValue(autoResize); // maya's fluid transformation V3d dynamicOffset(0); M44d localToWorld; MatrixFieldMapping::Ptr mapping(new MatrixFieldMapping()); M44d fluid_mat(tmatFn.asMatrix().matrix); if(autoResize) { fluidFn.findPlug("dofx").getValue(dynamicOffset[0]); fluidFn.findPlug("dofy").getValue(dynamicOffset[1]); fluidFn.findPlug("dofz").getValue(dynamicOffset[2]); } Box3i extents; extents.max = res - V3i(1); extents.min = V3i(0); mapping->setExtents(extents); localToWorld.setScale(size); localToWorld *= M44d().setTranslation( -(size*0.5) ); localToWorld *= M44d().setTranslation( dynamicOffset ); localToWorld *= fluid_mat; mapping->setLocalToWorld(localToWorld); if (m_density){ densityFld = new DenseFieldf; densityFld->setSize(res); densityFld->setMapping(mapping); } if (m_fuel){ fuelFld = new DenseFieldf; fuelFld->setSize(res); fuelFld->setMapping(mapping); } if (m_temperature){ tempFld = new DenseFieldf; tempFld->setSize(res); tempFld->setMapping(mapping); } if (m_pressure){ pressureFld = new DenseFieldf; pressureFld->setSize(res); pressureFld->setMapping(mapping); } if (m_falloff){ falloffFld = new DenseFieldf; falloffFld->setSize(res); falloffFld->setMapping(mapping); } if (m_vel){ vMac = new MACField3f; vMac->setSize(res); vMac->setMapping(mapping); } if (m_color){ CdFld = new DenseField3f; CdFld->setSize(res); CdFld->setMapping(mapping); } if (m_texture){ uvwFld = new DenseField3f; uvwFld->setSize(res); uvwFld->setMapping(mapping); } size_t iX, iY, iZ; for( iZ = 0; iZ < zres; iZ++ ) { for( iX = 0; iX < xres; iX++ ) { for( iY = 0; iY < yres ; iY++ ) { /// data is in x major but we are writting in z major order i = fluidFn.index( iX, iY, iZ); if ( m_density ) densityFld->lvalue(iX, iY, iZ) = density[i]; if ( m_temperature ) tempFld->lvalue(iX, iY, iZ) = temp[i]; if ( m_fuel ) fuelFld->lvalue(iX, iY, iZ) = fuel[i]; if ( m_pressure ) pressureFld->lvalue(iX, iY, iZ) = pressure[i]; if ( m_falloff ) falloffFld->lvalue(iX, iY, iZ) = falloff[i]; if (m_color) CdFld->lvalue(iX, iY, iZ) = V3f(r[i], g[i], b[i]); if (m_texture) uvwFld->lvalue(iX, iY, iZ) = V3f(u[i], v[i], w[i]); } } } if (m_vel) { unsigned x,y,z; for(z=0;z<zres;++z) for(y=0;y<yres;++y) for(x=0;x<xres+1;++x) { vMac->u(x,y,z) = *Xvel++; } for(z=0;z<zres;++z) for(y=0;y<yres+1;++y) for(x=0;x<xres;++x) { vMac->v(x,y,z) = *Yvel++; } for(z=0;z<zres+1;++z) for(y=0;y<yres;++y) for(x=0;x<xres;++x) { vMac->w(x,y,z) = *Zvel++; } } Field3DOutputFile out; if (!out.create(outputPath)) { MGlobal::displayError("Couldn't create file: "+ MString(outputPath)); return; } string fieldname("maya"); if (m_density){ out.writeScalarLayer<float>(fieldname, "density", densityFld); } if (m_fuel) { out.writeScalarLayer<float>(fieldname,"fuel", fuelFld); } if (m_temperature){ out.writeScalarLayer<float>(fieldname,"temperature", tempFld); } if (m_color) { out.writeVectorLayer<float>(fieldname,"Cd", CdFld); } if (m_vel) out.writeVectorLayer<float>(fieldname,"v_mac", vMac); out.close(); } catch(const std::exception &e) { MGlobal::displayError( MString(e.what()) ); return; } }