bool ProxyViz::loadInternal(MDataBlock& block) { MDataHandle tmH = block.inputValue(aplantTransformCache); MFnPointArrayData tmFn(tmH.data()); MPointArray plantTms = tmFn.array(); if(plantTms.length() < 1) return false; MDataHandle idH = block.inputValue(aplantIdCache); MFnIntArrayData idFn(idH.data()); MIntArray plantIds = idFn.array(); if(plantIds.length() < 1) return false; MDataHandle triH = block.inputValue(aplantTriangleIdCache); MFnIntArrayData triFn(triH.data()); MIntArray plantTris = triFn.array(); if(plantTris.length() < 1) return false; MDataHandle crdH = block.inputValue(aplantTriangleCoordCache); MFnVectorArrayData crdFn(crdH.data()); MVectorArray plantCoords = crdFn.array(); if(plantCoords.length() < 1) return false; MDataHandle cotH = block.inputValue(aplantOffsetCache); MFnVectorArrayData cotFn(cotH.data()); MVectorArray plantOffsets = cotFn.array(); return loadPlants(plantTms, plantIds, plantTris, plantCoords, plantOffsets); }
void rigidBodyNode::clearContactInfo() { MObject thisObject(thisMObject()); // contactCount m_contactCount = 0; MPlug plugContactCount(thisObject, rigidBodyNode::oa_contactCount); plugContactCount.setValue(m_contactCount); // contactName MStringArray stringArray; stringArray.clear(); MFnStringArrayData stringArrayData; MObject strArrObject = stringArrayData.create(stringArray); MPlug plugContactName(thisObject, rigidBodyNode::oa_contactName); if ( !plugContactName.isNull() ) plugContactName.setValue(strArrObject); // contactPosition MPlug plugContactPosition(thisObject, rigidBodyNode::oa_contactPosition); bool isArray = plugContactPosition.isArray(); MVectorArray vectorArray; vectorArray.clear(); MFnVectorArrayData vectorArrayData; MObject arrObject = vectorArrayData.create(vectorArray); if ( !plugContactPosition.isNull() ) plugContactPosition.setValue(arrObject); }
bool PxrUsdMayaWriteUtil::ReadMayaAttribute( const MFnDependencyNode& depNode, const MString& name, VtVec3fArray* val) { MStatus status; depNode.attribute(name, &status); if (status == MS::kSuccess) { MPlug plug = depNode.findPlug(name); MObject dataObj; if ( (plug.getValue(dataObj) == MS::kSuccess) && (dataObj.hasFn(MFn::kVectorArrayData)) ) { MFnVectorArrayData dData(dataObj, &status); if (status == MS::kSuccess) { MVectorArray arrayValues = dData.array(); size_t numValues = arrayValues.length(); val->resize(numValues); for (size_t i = 0; i < numValues; ++i) { (*val)[i].Set( arrayValues[i][0], arrayValues[i][1], arrayValues[i][2]); } return true; } } } return false; }
MStatus metro_model_translator::create_shape( const m2033::mesh_ptr m ) { MFloatPointArray v; MVectorArray norm; MIntArray p; MIntArray idx; MFnTransform transform_fn; MObject transform_obj = transform_fn.create( MObject::kNullObj ); transform_fn.setName( m->get_name().c_str() ); m2033::mesh::vertices mv = m->get_vertices(); m2033::mesh::indices mi = m->get_indices(); m2033::mesh::texcoords mt = m->get_tex_coords(); m2033::mesh::normals mn = m->get_normals(); for( unsigned i = 0; i < mv.size(); i++ ) { v.append( -mv[i].x, mv[i].y, mv[i].z ); norm.append( MVector( -mn[i].x, mn[i].y, mn[i].z ) ); } for( unsigned i = 0; i < mi.size() / 3; i++ ) { idx.append( mi[i*3+2] ); idx.append( mi[i*3+1] ); idx.append( mi[i*3] ); p.append( 3 ); } MFloatArray u_values, v_values; for( unsigned i = 0; i < mt.size(); i++ ) { u_values.append( mt[i].x ); v_values.append( -mt[i].y ); } MFnMesh meshFn; MObject mesh = meshFn.create( v.length(), p.length(), v, p, idx, u_values, v_values, transform_obj ); MString name = m->get_name().c_str(); meshFn.setName( name + MString("_shape") ); MStatus s = meshFn.assignUVs( p, idx, 0 ); if( !s ) { return s; } s = meshFn.unlockVertexNormals( idx ); if( !s ) { return s; } meshFn.setVertexNormals( norm, idx ); MObject mat = create_material( m->get_texture_name(), &s ); if( !s ) { return s; } MFnSet mat_fn(mat); mat_fn.addMember(mesh); return MS::kSuccess; }
void liqRibData::parseVectorAttributes( MFnDependencyNode & nodeFn, MStringArray & strArray, ParameterType pType ) { int i; MStatus status; if ( strArray.length() > 0 ) { for ( i = 0; i < strArray.length(); i++ ) { liqTokenPointer tokenPointerPair; MString cutString = strArray[i].substring(5, strArray[i].length()); MPlug vPlug = nodeFn.findPlug( strArray[i] ); MObject plugObj; status = vPlug.getValue( plugObj ); if ( plugObj.apiType() == MFn::kVectorArrayData ) { MFnVectorArrayData fnVectorArrayData( plugObj ); MVectorArray vectorArrayData = fnVectorArrayData.array( &status ); tokenPointerPair.set( cutString.asChar(), pType, ( type() == MRT_Nurbs || type() == MRT_NuCurve ) ? true : false, true, false, vectorArrayData.length() ); for ( int kk = 0; kk < vectorArrayData.length(); kk++ ) { tokenPointerPair.setTokenFloat( kk, vectorArrayData[kk].x, vectorArrayData[kk].y, vectorArrayData[kk].z ); } // should it be per vertex or face-varying if ( ( ( type() == MRT_Mesh ) || ( type() == MRT_Subdivision ) ) && ( vectorArrayData.length() == faceVaryingCount ) ) { tokenPointerPair.setDetailType( rFaceVarying); } else { tokenPointerPair.setDetailType( rVertex ); } // store it all tokenPointerArray.push_back( tokenPointerPair ); } else { // Hmmmm float ? double ? float x, y, z; tokenPointerPair.set( cutString.asChar(), pType, ( type() == MRT_Nurbs || type() == MRT_NuCurve ) ? true : false, false, false, 0 ); vPlug.child(0).getValue( x ); vPlug.child(1).getValue( y ); vPlug.child(2).getValue( z ); tokenPointerPair.setTokenFloat( 0, x, y, z ); tokenPointerPair.setDetailType( rConstant ); tokenPointerArray.push_back( tokenPointerPair ); } } } }
void dynExprField::apply( MDataBlock &block, int receptorSize, const MDoubleArray &magnitudeArray, const MDoubleArray &magnitudeOwnerArray, const MVectorArray &directionArray, const MVectorArray &directionOwnerArray, MVectorArray &outputForce ) // // Compute output force for each particle. If there exists the // corresponding per particle attribute, use the data passed from // particle shape (stored in magnitudeArray and directionArray). // Otherwise, use the attribute value from the field. // { // get the default values MVector defaultDir = direction(block); double defaultMag = magnitude(block); int magArraySize = magnitudeArray.length(); int dirArraySize = directionArray.length(); int magOwnerArraySize = magnitudeOwnerArray.length(); int dirOwnerArraySize = directionOwnerArray.length(); int numOfOwner = magOwnerArraySize; if( dirOwnerArraySize > numOfOwner ) numOfOwner = dirOwnerArraySize; double magnitude = defaultMag; MVector direction = defaultDir; for (int ptIndex = 0; ptIndex < receptorSize; ptIndex ++ ) { if(receptorSize == magArraySize) magnitude = magnitudeArray[ptIndex]; if(receptorSize == dirArraySize) direction = directionArray[ptIndex]; if( numOfOwner > 0) { for( int nthOwner = 0; nthOwner < numOfOwner; nthOwner++ ) { if(magOwnerArraySize == numOfOwner) magnitude = magnitudeOwnerArray[nthOwner]; if(dirOwnerArraySize == numOfOwner) direction = directionOwnerArray[nthOwner]; outputForce.append( direction * magnitude ); } } else { outputForce.append( direction * magnitude ); } } }
void rigidBodyNode::addContactInfo(const MString& contactObjectName, const MVector& point) { MObject thisObject(thisMObject()); // contactCount m_contactCount++; MPlug plugContactCount(thisObject, rigidBodyNode::oa_contactCount); plugContactCount.setValue(m_contactCount); // contactName MPlug plugContactName(thisObject, rigidBodyNode::oa_contactName); if ( !plugContactName.isNull() ) { MObject strArrObject; plugContactName.getValue(strArrObject); MFnStringArrayData stringArrayData(strArrObject); MStringArray stringArray = stringArrayData.array(); stringArray.append(contactObjectName); MFnStringArrayData newStringArrayData; MObject newStrArrObject = newStringArrayData.create(stringArray); plugContactName.setValue(newStrArrObject); } // contactPosition MPlug plugContactPosition(thisObject, rigidBodyNode::oa_contactPosition); if ( !plugContactPosition.isNull() ) { MObject arrObject; plugContactPosition.getValue(arrObject); MFnVectorArrayData vectorArrayData(arrObject); MVectorArray vectorArray = vectorArrayData.array(); vectorArray.append(point); MFnVectorArrayData newVectorArrayData; MObject newArrObject = newVectorArrayData.create(vectorArray); plugContactPosition.setValue(newArrObject); } }
MStatus XmlCacheFormat::readDoubleVectorArray( MVectorArray& array, unsigned arraySize ) { MStringArray value; if( !readXmlTagValue(doubleVectorArrayTag, value) ) { return MS::kFailure; } assert( value.length() == arraySize * 3 ); array.setLength( arraySize ); for (unsigned i = 0; i < arraySize; i++ ) { double v[3]; v[0] = strtod( value[i*3].asChar(), NULL ); v[1] = strtod( value[i*3+1].asChar(), NULL ); v[2] = strtod( value[i*3+2].asChar(), NULL ); array.set( v, i ); } return MS::kSuccess; }
MStatus XmlCacheFormat::writeDoubleVectorArray( const MVectorArray& array ) { int size = array.length(); assert(size != 0); writeXmlTagValue(sizeTag,size); startXmlBlock( doubleVectorArrayTag ); for(int i = 0; i < size; i++) { writeXmlValue(array[i][0]); writeXmlValue(array[i][1]); writeXmlValue(array[i][2]); string endl("\n"); writeXmlValue(endl); } endXmlBlock(); return MS::kSuccess; }
void CylinderMesh::transform(MPointArray& points, MVectorArray& normals) { MVector forward = mEnd - mStart; double s = forward.length(); forward.normalize(); MVector left = MVector(0,0,1)^forward; MVector up; if (left.length() < 0.0001) { up = forward^MVector(0,1,0); left = up^forward; } else { up = forward^left; } MMatrix mat; mat[0][0] = forward[0]; mat[0][1] = left[0]; mat[0][2] = up[0]; mat[0][3] = 0; mat[1][0] = forward[1]; mat[1][1] = left[1]; mat[1][2] = up[1]; mat[1][3] = 0; mat[2][0] = forward[2]; mat[2][1] = left[2]; mat[2][2] = up[2]; mat[2][3] = 0; mat[3][0] = 0; mat[3][1] = 0; mat[3][2] = 0; mat[3][3] = 1; mat = mat.transpose(); for (int i = 0; i < gPoints.length(); i++) { MPoint p = gPoints[i]; p.x = p.x * s; // scale p = p * mat + mStart; // transform points.append(p); MVector n = gNormals[i] * mat; normals.append(n); } }
MString CBPoseSpaceCmd::cacheResult(const MPointArray& bindPoints, const MPointArray& posePoints, const MVectorArray& dx, const MVectorArray& dy, const MVectorArray& dz) { MDGModifier modif; MObject opose = modif.createNode("sculptSpaceRecord"); modif.doIt(); unsigned count = dx.length(); MVectorArray row0Array; row0Array.setLength(count); MVectorArray row1Array; row1Array.setLength(count); MVectorArray row2Array; row2Array.setLength(count); MVectorArray row3Array; row3Array.setLength(count); MVectorArray bndArray; bndArray.setLength(count); MVectorArray posArray; posArray.setLength(count); float m[4][4]; for(unsigned i=0; i < count; i++) { m[0][0] = dx[i].x; m[0][1] = dx[i].y; m[0][2] = dx[i].z; m[0][3] = 0.f; m[1][0] = dy[i].x; m[1][1] = dy[i].y; m[1][2] = dy[i].z; m[1][3] = 0.f; m[2][0] = dz[i].x; m[2][1] = dz[i].y; m[2][2] = dz[i].z; m[2][3] = 0.f; m[3][0] = 0.f; m[3][1] = 0.f; m[3][2] = 0.f; m[3][3] = 1.f; MMatrix tm(m); tm = tm.inverse(); tm.get(m); row0Array[i].x = m[0][0]; row0Array[i].y = m[0][1]; row0Array[i].z = m[0][2]; row1Array[i].x = m[1][0]; row1Array[i].y = m[1][1]; row1Array[i].z = m[1][2]; row2Array[i].x = m[2][0]; row2Array[i].y = m[2][1]; row2Array[i].z = m[2][2]; row3Array[i].x = m[3][0]; row3Array[i].y = m[3][1]; row3Array[i].z = m[3][2]; bndArray[i] = bindPoints[i]; posArray[i] = posePoints[i]; } MFnDependencyNode fposec(opose); MStatus stat; MPlug pspacerow0 = fposec.findPlug("poseSpaceRow0", false, &stat); MPlug pspacerow1 = fposec.findPlug("poseSpaceRow1", false, &stat); MPlug pspacerow2 = fposec.findPlug("poseSpaceRow2", false, &stat); MPlug pspacerow3 = fposec.findPlug("poseSpaceRow3", false, &stat); MPlug pbind = fposec.findPlug("bpnt", false, &stat); MPlug ppose = fposec.findPlug("ppnt", false, &stat); MFnVectorArrayData frow0; MObject orow0 = frow0.create(row0Array); pspacerow0.setMObject(orow0); MFnVectorArrayData frow1; MObject orow1 = frow1.create(row1Array); pspacerow1.setMObject(orow1); MFnVectorArrayData frow2; MObject orow2 = frow2.create(row2Array); pspacerow2.setMObject(orow2); MFnVectorArrayData frow3; MObject orow3 = frow3.create(row3Array); pspacerow3.setMObject(orow3); MFnVectorArrayData fbind; MObject obind = fbind.create(bndArray); pbind.setMObject(obind); MFnVectorArrayData fpose; MObject oposed = fpose.create(posArray); ppose.setMObject(oposed); return fposec.name(); }
void CBPoseSpaceCmd::calculateVertexPoseSpace(const MObject& poseMesh, const MObject& bindMesh) { MStatus status; MFnMesh poseFn(poseMesh, &status); MPointArray originalPoseVertex; poseFn.getPoints ( originalPoseVertex, MSpace::kObject ); unsigned numVert = originalPoseVertex.length(); MFnMesh bindFn(bindMesh, &status); MPointArray originalbindVertex; bindFn.getPoints ( originalbindVertex, MSpace::kObject ); MPointArray movedVertex(originalbindVertex); for(unsigned i=0; i < numVert; i++) { movedVertex[i].x = originalbindVertex[i].x + 1.0; } bindFn.setPoints(movedVertex, MSpace::kObject ); bindFn.updateSurface(); poseFn.updateSurface(); MPointArray xPoseVertex; poseFn.getPoints ( xPoseVertex, MSpace::kObject ); MVectorArray dirx; dirx.setLength(numVert); for(unsigned i=0; i < numVert; i++) { dirx[i] = xPoseVertex[i] - originalPoseVertex[i]; } for(unsigned i=0; i < numVert; i++) { movedVertex[i].x = originalbindVertex[i].x; movedVertex[i].y = originalbindVertex[i].y + 1.0; } bindFn.setPoints(movedVertex, MSpace::kObject ); bindFn.updateSurface(); poseFn.updateSurface(); poseFn.getPoints ( xPoseVertex, MSpace::kObject ); MVectorArray diry; diry.setLength(numVert); for(unsigned i=0; i < numVert; i++) { diry[i] = xPoseVertex[i] - originalPoseVertex[i]; } for(unsigned i=0; i < numVert; i++) { movedVertex[i].y = originalbindVertex[i].y; movedVertex[i].z = originalbindVertex[i].z + 1.0; } bindFn.setPoints(movedVertex, MSpace::kObject ); bindFn.updateSurface(); poseFn.updateSurface(); poseFn.getPoints ( xPoseVertex, MSpace::kObject ); MVectorArray dirz; dirz.setLength(numVert); for(unsigned i=0; i < numVert; i++) { dirz[i] = xPoseVertex[i] - originalPoseVertex[i]; } bindFn.setPoints(originalbindVertex, MSpace::kObject ); bindFn.updateSurface(); const MString cacheNodeName = cacheResult(originalbindVertex, originalPoseVertex, dirx, diry, dirz); MGlobal::displayInfo(MString("correct shape recordes ") + numVert + " points in " + cacheNodeName); setResult(cacheNodeName); }
MString CBPoseSpaceCmd::saveResult(const MPointArray& bindPoints, const MPointArray& posePoints, const MVectorArray& dx, const MVectorArray& dy, const MVectorArray& dz) { unsigned count = dx.length(); float* data = new float[count * (3 + 3 + 16)]; for(unsigned i=0; i < count; i++) { data[i*3] = bindPoints[i].x; data[i*3+1] = bindPoints[i].y; data[i*3+2] = bindPoints[i].z; } unsigned offset = count * 3; for(unsigned i=0; i < count; i++) { data[offset+i*3] = posePoints[i].x; data[offset+i*3+1] = posePoints[i].y; data[offset+i*3+2] = posePoints[i].z; } offset = count * 6; for(unsigned i=0; i < count; i++) { float m[4][4]; m[0][0] = dx[i].x; m[0][1] = dx[i].y; m[0][2] = dx[i].z; m[0][3] = 0.f; m[1][0] = dy[i].x; m[1][1] = dy[i].y; m[1][2] = dy[i].z; m[1][3] = 0.f; m[2][0] = dz[i].x; m[2][1] = dz[i].y; m[2][2] = dz[i].z; m[2][3] = 0.f; m[3][0] = 0.f; m[3][1] = 0.f; m[3][2] = 0.f; m[3][3] = 1.f; MMatrix tm(m); tm = tm.inverse(); tm.get(m); unsigned ivx = offset+i*16; data[ivx] = m[0][0]; data[ivx+1] = m[0][1]; data[ivx+2] = m[0][2]; data[ivx+3] = m[0][3]; data[ivx+4] = m[1][0]; data[ivx+5] = m[1][1]; data[ivx+6] = m[1][2]; data[ivx+7] = m[1][3]; data[ivx+8] = m[2][0]; data[ivx+9] = m[2][1]; data[ivx+10] = m[2][2]; data[ivx+11] = m[2][3]; data[ivx+12] = m[3][0]; data[ivx+13] = m[3][1]; data[ivx+14] = m[3][2]; data[ivx+15] = m[3][3]; } io::filtering_ostream out; out.push(boost::iostreams::gzip_compressor()); MString filename = _cacheName; if(filename == "") { MString projRoot; MGlobal::executeCommand(MString("workspace -q -dir"), projRoot, 0, 0); projRoot = projRoot + "/poses/"; if(!exists( projRoot.asChar() )) { create_directory(projRoot.asChar()); } const ptime now = second_clock::local_time(); std::string file_time = to_iso_string(now); filename = projRoot+file_time.c_str()+".pos"; } out.push(io::file_sink(filename.asChar(), ios::binary)); out.write((char*)data, count*(3 + 3 + 16)*4); out.flush(); delete[] data; MGlobal::displayInfo(MString("corrective blendshape writes pose cache to ") + filename); return filename; }
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 ); } } } } }
void sweptEmitter::emit ( const MVectorArray &inPosAry, // points where new particles from const MVectorArray &inVelAry, // initial velocity of new particles const MIntArray &emitCountPP, // # of new particles per point double dt, // elapsed time double speed, // speed factor double inheritFactor, // for inherit velocity MVector dirV, // emit direction MVectorArray &outPosAry, // holding new particles position MVectorArray &outVelAry // holding new particles velocity ) // // Descriptions: // { // check the length of input arrays. // int posLength = inPosAry.length(); int velLength = inVelAry.length(); int countLength = emitCountPP.length(); if( (posLength != velLength) || (posLength != countLength) ) return; // Compute total emit count. // int index; int totalCount = 0; for( index = 0; index < countLength; index ++ ) totalCount += emitCountPP[index]; if( totalCount <= 0 ) return; // Map direction vector into world space and normalize it. // dirV.normalize(); // Start emission. // int emitCount; MVector newPos, newVel; MVector prePos, sPos, sVel; for( index = 0; index < posLength; index++ ) { emitCount = emitCountPP[index]; if( emitCount <= 0 ) continue; sPos = inPosAry[index]; sVel = inVelAry[index]; prePos = sPos - sVel * dt; for( int i = 0; i < emitCount; i++ ) { double alpha = ( (double)i + drand48() ) / (double)emitCount; newPos = (1 - alpha) * prePos + alpha * sPos; newVel = dirV * speed; newPos += newVel * ( dt * (1 - alpha) ); newVel += sVel * inheritFactor; // Add new data into output arrays. // outPosAry.append( newPos ); outVelAry.append( newVel ); } } }
MStatus sweptEmitter::compute(const MPlug& plug, MDataBlock& block) // // Descriptions: // Call emit emit method to generate new particles. // { MStatus status; // Determine if we are requesting the output plug for this emitter node. // if( !(plug == mOutput) ) return( MS::kUnknownParameter ); // Get the logical index of the element this plug refers to, // because the node can be emitting particles into more // than one particle shape. // int multiIndex = plug.logicalIndex( &status ); McheckErr(status, "ERROR in plug.logicalIndex.\n"); // Get output data arrays (position, velocity, or parentId) // that the particle shape is holding from the previous frame. // MArrayDataHandle hOutArray = block.outputArrayValue(mOutput, &status); McheckErr(status, "ERROR in hOutArray = block.outputArrayValue.\n"); // Create a builder to aid in the array construction efficiently. // MArrayDataBuilder bOutArray = hOutArray.builder( &status ); McheckErr(status, "ERROR in bOutArray = hOutArray.builder.\n"); // Get the appropriate data array that is being currently evaluated. // MDataHandle hOut = bOutArray.addElement(multiIndex, &status); McheckErr(status, "ERROR in hOut = bOutArray.addElement.\n"); // Get the data and apply the function set. // MFnArrayAttrsData fnOutput; MObject dOutput = fnOutput.create ( &status ); McheckErr(status, "ERROR in fnOutput.create.\n"); // Check if the particle object has reached it's maximum, // hence is full. If it is full then just return with zero particles. // bool beenFull = isFullValue( multiIndex, block ); if( beenFull ) { return( MS::kSuccess ); } // Get deltaTime, currentTime and startTime. // If deltaTime <= 0.0, or currentTime <= startTime, // do not emit new pariticles and return. // MTime cT = currentTimeValue( block ); MTime sT = startTimeValue( multiIndex, block ); MTime dT = deltaTimeValue( multiIndex, block ); if( (cT <= sT) || (dT <= 0.0) ) { // We do not emit particles before the start time, // and do not emit particles when moving backwards in time. // // This code is necessary primarily the first time to // establish the new data arrays allocated, and since we have // already set the data array to length zero it does // not generate any new particles. // hOut.set( dOutput ); block.setClean( plug ); return( MS::kSuccess ); } // Get speed, direction vector, and inheritFactor attributes. // double speed = speedValue( block ); MVector dirV = directionVector( block ); double inheritFactor = inheritFactorValue( multiIndex, block ); // Get the position and velocity arrays to append new particle data. // MVectorArray fnOutPos = fnOutput.vectorArray("position", &status); MVectorArray fnOutVel = fnOutput.vectorArray("velocity", &status); // Convert deltaTime into seconds. // double dt = dT.as( MTime::kSeconds ); // Apply rotation to the direction vector MVector rotatedV = useRotation ( dirV ); // position, MVectorArray inPosAry; // velocity MVectorArray inVelAry; // emission rate MIntArray emitCountPP; // Get the swept geometry data // MObject thisObj = this->thisMObject(); MPlug sweptPlug( thisObj, mSweptGeometry ); if ( sweptPlug.isConnected() ) { MDataHandle sweptHandle = block.inputValue( mSweptGeometry ); // MObject sweptData = sweptHandle.asSweptGeometry(); MObject sweptData = sweptHandle.data(); MFnDynSweptGeometryData fnSweptData( sweptData ); // Curve emission // if (fnSweptData.lineCount() > 0) { int numLines = fnSweptData.lineCount(); for ( int i=0; i<numLines; i++ ) { inPosAry.clear(); inVelAry.clear(); emitCountPP.clear(); MDynSweptLine line = fnSweptData.sweptLine( i ); // ... process current line ... MVector p1 = line.vertex( 0 ); MVector p2 = line.vertex( 1 ); inPosAry.append( p1 ); inPosAry.append( p2 ); inVelAry.append( MVector( 0,0,0 ) ); inVelAry.append( MVector( 0,0,0 ) ); // emit Rate for two points on line emitCountPP.clear(); status = emitCountPerPoint( plug, block, 2, emitCountPP ); emit( inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor, rotatedV, fnOutPos, fnOutVel ); } } // Surface emission (nurb or polygon) // if (fnSweptData.triangleCount() > 0) { int numTriangles = fnSweptData.triangleCount(); for ( int i=0; i<numTriangles; i++ ) { inPosAry.clear(); inVelAry.clear(); emitCountPP.clear(); MDynSweptTriangle tri = fnSweptData.sweptTriangle( i ); // ... process current triangle ... MVector p1 = tri.vertex( 0 ); MVector p2 = tri.vertex( 1 ); MVector p3 = tri.vertex( 2 ); MVector center = p1 + p2 + p3; center /= 3.0; inPosAry.append( center ); inVelAry.append( MVector( 0,0,0 ) ); // emit Rate for two points on line emitCountPP.clear(); status = emitCountPerPoint( plug, block, 1, emitCountPP ); emit( inPosAry, inVelAry, emitCountPP, dt, speed, inheritFactor, rotatedV, fnOutPos, fnOutVel ); } } } // Update the data block with new dOutput and set plug clean. // hOut.set( dOutput ); block.setClean( plug ); return( MS::kSuccess ); }
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; } } } } } } }
MStatus particlePathsCmd::doIt( const MArgList& args ) { MStatus stat = parseArgs( args ); if( stat != MS::kSuccess ) { return stat; } MFnParticleSystem cloud( particleNode ); if( ! cloud.isValid() ) { MGlobal::displayError( "The function set is invalid!" ); return MS::kFailure; } // // Create curves from the particle system in two stages. First, sample // all particle positions from the start time to the end time. Then, // use the data that was collected to create curves. // // Create the particle hash table at a fixed size. This should work fine // for small particle systems, but may become inefficient for larger ones. // If the plugin is running very slow, increase the size. The value should // be roughly the number of particles that are expected to be emitted // within the time period. // ParticleIdHash hash(1024); MIntArray idList; // // Stage 1 // MVectorArray positions; MIntArray ids; int i = 0; for (double time = start; time <= finish + TOLERANCE; time += increment) { MTime timeSeconds(time,MTime::kSeconds); // It is necessary to query the worldPosition attribute to force the // particle positions to update. // cloud.evaluateDynamics(timeSeconds,false); // MGlobal::executeCommand(MString("getAttr ") + cloud.name() + // MString(".worldPosition")); if (!cloud.isValid()) { MGlobal::displayError( "Particle system has become invalid." ); return MS::kFailure; } MGlobal::displayInfo( MString("Received ") + (int)(cloud.count()) + " particles, at time " + time); // Request position and ID data for particles // cloud.position( positions ); cloud.particleIds( ids ); if (ids.length() != cloud.count() || positions.length() != cloud.count()) { MGlobal::displayError( "Invalid array sizes." ); return MS::kFailure; } for (int j = 0; j < (int)cloud.count(); j++) { // Uncomment to show particle positions as the plugin accumulates // samples. /* MGlobal::displayInfo(MString("(") + (positions[j])[0] + MString(",") + (positions[j])[1] + MString(",") + (positions[j])[2] + MString(")")); */ MPoint pt(positions[j]); if (hash.getPoints(ids[j]).length() == 0) { idList.append(ids[j]); } hash.insert(ids[j],pt); } i++; } // // Stage 2 // for (i = 0; i < (int)(idList.length()); i++) { MPointArray points = hash.getPoints(idList[i]); // Don't bother with single samples if (points.length() <= 1) { continue; } // Add two additional points, so that the curve covers all sampled // values. // MPoint p1 = points[0]*2 - points[1]; MPoint p2 = points[points.length()-1]*2 - points[points.length()-2]; points.insert(p1,0); points.append(p2); // Uncomment to show information about the generated curves /* MGlobal::displayInfo( MString("ID ") + (int)(idList[i]) + " has " + (int)(points.length()) + " curve points."); for (int j = 0; j < (int)(points.length()); j++) { MGlobal::displayInfo(MString("(") + points[j][0] + MString(",") + points[j][1] + MString(",") + points[j][2] + MString(")")); } */ MDoubleArray knots; knots.insert(0.0,0); for (int j = 0; j < (int)(points.length()); j++) { knots.append((double)j); } knots.append(points.length()-1); MStatus status; MObject dummy; MFnNurbsCurve curve; curve.create(points,knots,3,MFnNurbsCurve::kOpen,false,false,dummy,&status); if (!status) { MGlobal::displayError("Failed to create nurbs curve."); return MS::kFailure; } } return MS::kSuccess; }
bool SargassoNode::creatRestShape(const MObject & m) { MStatus stat; MFnMesh fmesh(m, &stat); if(!stat) { MGlobal::displayInfo("val is not mesh"); return false; } MObject thisObj = thisMObject(); MPlug pnv(thisObj, atargetNv); const int restNv = pnv.asInt(); if(restNv != fmesh.numVertices()) { MGlobal::displayInfo("target nv not match"); return false; } MPlug pntriv(thisObj, atargetNt); const int restNtriv = pntriv.asInt(); MIntArray triangleCounts, triangleVertices; fmesh.getTriangles(triangleCounts, triangleVertices); if(restNtriv != triangleVertices.length()) { MGlobal::displayInfo("target ntri not match"); return false; } m_mesh->create(restNv, restNtriv/3); MPlug prestp(thisObj, atargetRestP); MObject orestp; prestp.getValue(orestp); MFnPointArrayData frestp(orestp); MPointArray restPArray = frestp.array(); if(restPArray.length() != restNv) { MGlobal::displayInfo("cached target nv not match"); return false; } Vector3F * p = m_mesh->points(); unsigned i=0; for(;i<restNv;i++) p[i].set(restPArray[i].x, restPArray[i].y, restPArray[i].z); MPlug presttri(thisObj, atargetTri); MObject oresttri; presttri.getValue(oresttri); MFnIntArrayData fresttri(oresttri); MIntArray restTriArray = fresttri.array(); if(restTriArray.length() != restNtriv) { MGlobal::displayInfo("cached target ntri not match"); return false; } unsigned * ind = m_mesh->indices(); for(i=0;i<restNtriv;i++) ind[i] = restTriArray[i]; const BoundingBox box = ((AGenericMesh *)m_mesh)->calculateBBox(); AHelper::Info<BoundingBox>("target mesh box", box); m_diff = new TriangleDifference(m_mesh); MPlug ptargetbind(thisObj, atargetBind); MObject otargetbind; ptargetbind.getValue(otargetbind); MFnIntArrayData ftargetbind(otargetbind); MIntArray targetBindArray = ftargetbind.array(); const unsigned nbind = targetBindArray.length(); AHelper::Info<unsigned>("n binded triangles", nbind); std::vector<unsigned> vbind; for(i=0;i<nbind;i++) vbind.push_back(targetBindArray[i]); m_diff->requireQ(vbind); vbind.clear(); MPlug pobjLocal(thisObj, aobjLocal); MObject oobjLocal; pobjLocal.getValue(oobjLocal); MFnVectorArrayData fobjLocal(oobjLocal); MVectorArray localPArray = fobjLocal.array(); m_numObjects = localPArray.length(); AHelper::Info<unsigned>("n constrained objects", m_numObjects); m_localP->create(m_numObjects * 12); Vector3F * lp = localP(); for(i=0;i<m_numObjects;i++) lp[i].set(localPArray[i].x, localPArray[i].y, localPArray[i].z); m_triId->create(m_numObjects * 4); MPlug pobjtri(thisObj, aobjTri); MObject oobjtri; pobjtri.getValue(oobjtri); MFnIntArrayData fobjtri(oobjtri); MIntArray objtriArray = fobjtri.array(); unsigned * tri = objectTriangleInd(); for(i=0;i<m_numObjects;i++) tri[i] = objtriArray[i]; return true; }
MStatus PTCMapCmd::doIt( const MArgList& args) { MStatus status = parseArgs( args ); if( status != MS::kSuccess ) return status; MArgDatabase argData(syntax(), args); MAnimControl timeControl; MTime time = timeControl.currentTime(); int frame =int(time.value()); MString proj; MGlobal::executeCommand( MString ("string $p = `workspace -q -fn`"), proj ); MString cache_path = proj + "/data"; MString cache_name = "foo"; MString cache_attrib; double cache_mindist = 0.1; int max_level = 3; double root_size = 32; MString dem_trans = "nil"; double cloud_os = 0.05; MString key_trans = "nil"; MString eye_trans = "nil"; if (argData.isFlagSet("-p")) argData.getFlagArgument("-p", 0, cache_path); if (argData.isFlagSet("-n")) argData.getFlagArgument("-n", 0, cache_name); if (argData.isFlagSet("-a")) argData.getFlagArgument("-a", 0, cache_attrib); if (argData.isFlagSet("-mnd")) argData.getFlagArgument("-mnd", 0, cache_mindist); if (argData.isFlagSet("-ml")) argData.getFlagArgument("-ml", 0, max_level); if (argData.isFlagSet("-rs")) argData.getFlagArgument("-rs", 0, root_size); if (argData.isFlagSet("-t")) argData.getFlagArgument("-t", 0, dem_trans); if(argData.isFlagSet("-o")) argData.getFlagArgument("-o", 0, cloud_os); if(argData.isFlagSet("-tk")) argData.getFlagArgument("-tk", 0, key_trans); if(argData.isFlagSet("-te")) argData.getFlagArgument("-te", 0, eye_trans); float def_area = root_size; int last = 0; while(last < max_level-2) { def_area /= 2; last++; } def_area = 1.0f; //def_area *= def_area; // get bounding box center MDagPath p_bbox; zWorks::getTypedPathByName(MFn::kTransform, dem_trans, p_bbox); MObject o_bbox = p_bbox.transform(); float m_space[4][4]; m_space[0][0]=1; m_space[0][1]=0; m_space[0][2]=0; m_space[1][0]=0; m_space[1][1]=1; m_space[1][2]=0; m_space[2][0]=0; m_space[2][1]=0; m_space[2][2]=1; m_space[3][0]=0; m_space[3][1]=0; m_space[3][2]=0; if(o_bbox.isNull()) MGlobal::displayWarning("Cannot find pmap dimension, use default space."); else zWorks::getTransformWorldNoScale(p_bbox.partialPathName(), m_space); XYZ root_center(m_space[3][0], m_space[3][1], m_space[3][2]); // get key light dir MDagPath p_key; zWorks::getTypedPathByName(MFn::kTransform, key_trans, p_key); MObject o_key = p_key.transform(); m_space[0][0]=1; m_space[0][1]=0; m_space[0][2]=0; m_space[1][0]=0; m_space[1][1]=1; m_space[1][2]=0; m_space[2][0]=0; m_space[2][1]=0; m_space[2][2]=1; m_space[3][0]=0; m_space[3][1]=0; m_space[3][2]=0; if(o_key.isNull()) MGlobal::displayWarning("Cannot find key camera, use default space."); else zWorks::getTransformWorldNoScale(p_key.partialPathName(), m_space); XYZ key_dir(m_space[2][0], m_space[2][1], m_space[2][2]); key_dir.normalize(); // get view dir MDagPath p_eye; zWorks::getTypedPathByName(MFn::kTransform, eye_trans, p_eye); MObject o_eye = p_eye.transform(); m_space[0][0]=1; m_space[0][1]=0; m_space[0][2]=0; m_space[1][0]=0; m_space[1][1]=1; m_space[1][2]=0; m_space[2][0]=0; m_space[2][1]=0; m_space[2][2]=1; m_space[3][0]=0; m_space[3][1]=0; m_space[3][2]=0; if(o_eye.isNull()) MGlobal::displayWarning("Cannot find render camera, use default space."); else zWorks::getTransformWorldNoScale(p_eye.partialPathName(), m_space); XYZ view_dir(-m_space[2][0], -m_space[2][1], -m_space[2][2]); view_dir.normalize(); // additional attribs MStringArray attribArray; cache_attrib.split('.', attribArray); MSelectionList slist; MGlobal::getActiveSelectionList( slist ); MItSelectionList list( slist, MFn::kParticle, &status ); if (MS::kSuccess != status) { displayError( "Could not create selection list iterator"); return status; } if (list.isDone()) { displayError( "No particles selected" ); return MS::kSuccess; } MDagPath fDagPath; MObject component; unsigned npt = 0,acc = 0; for(; !list.isDone(); list.next()) { list.getDagPath (fDagPath, component); MFnParticleSystem ps( fDagPath ); npt += ps.count(); } if(npt < 1) { MGlobal::displayInfo(" zero particle: do nothing "); return MS::kSuccess; } std::list<AParticle *> particles; RGRID *buf = new RGRID[npt]; unsigned *idxb = new unsigned[npt]; float *opab = new float[npt]; float *ageb = new float[npt]; list.reset(); for(; !list.isDone(); list.next()) { list.getDagPath (fDagPath, component); MFnParticleSystem ps( fDagPath ); MVectorArray positions; ps.position( positions ); MVectorArray velarr; ps.velocity( velarr ); MIntArray ids; ps.particleIds(ids); MVectorArray cols; ps.rgb(cols); MDoubleArray opas; ps.opacity(opas); MDoubleArray ages; ps.opacity(ages); for(unsigned i=0; i<positions.length(); i++,acc++ ) { buf[acc].pos.x = positions[i].x; buf[acc].pos.y = positions[i].y; buf[acc].pos.z = positions[i].z; buf[acc].nor.x = velarr[i].x; buf[acc].nor.y = velarr[i].y; buf[acc].nor.z = velarr[i].z; buf[acc].area = def_area; if(ps.hasRgb()) { buf[acc].col.x = cols[i].x; buf[acc].col.y = cols[i].y; buf[acc].col.z = cols[i].z; } else buf[acc].col = XYZ(1,1,1); idxb[acc] = ids[i]; if(ps.hasOpacity ()) opab[acc] = opas[i]; else opab[acc] = 1.f; ageb[acc] = ages[i]; AParticle *pt = new AParticle(); pt->pos.x = positions[i].x; pt->pos.y = positions[i].y; pt->pos.z = positions[i].z; pt->r = def_area; particles.push_back(pt); } } /* Z3DTexture* tree = new Z3DTexture(); tree->setGrid(buf, npt); tree->constructTree(root_center, root_size, max_level); tree->setGridIdData(idxb, npt); tree->setGridOpacityData(opab, npt); tree->setGridAgeData(ageb, npt); MGlobal::displayInfo(MString(" num grid ")+ tree->getNumGrid()); MGlobal::displayInfo(MString(" num voxel ")+ tree->getNumVoxel()); MGlobal::displayInfo(MString(" num leaf ")+ tree->getNumLeaf()); MGlobal::displayInfo(MString(" max level ")+ tree->getMaxLevel()); MGlobal::displayInfo(" calculating voxel volume occlusion..."); tree->occlusionVolume(cloud_os, key_dir, view_dir); MGlobal::displayInfo(" done"); MGlobal::displayInfo(" updating grid distance to neighbour..."); tree->distanceToNeighbour(cache_mindist); MGlobal::displayInfo(" done"); char filename[512]; sprintf( filename, "%s/%s.%d.pmap", cache_path.asChar(), cache_name.asChar(), frame ); MGlobal::displayInfo(MString("PTCMap saved ") + filename); tree->save(filename); delete tree; */ if(!particles.empty()) { GPUOctree *data = new GPUOctree(); data->create(root_center, root_size, 8, particles); MGlobal::displayInfo(MString(" num voxel ")+ data->getNumVoxel()+ MString(" minvar ")+ data->getMinVariation()+ MString(" max level ")+ data->getMaxLevel()+ MString(" filter size ")+ def_area); char filename[512]; sprintf( filename, "%s/%s.%d", cache_path.asChar(), cache_name.asChar(), frame ); data->dumpIndirection(filename); delete data; particles.clear(); } return MS::kSuccess; }
MStatus dynExprField::compute(const MPlug& plug, MDataBlock& block) // // Descriptions: // compute output force. // { MStatus status; if( !(plug == mOutputForce) ) return( MS::kUnknownParameter ); // get the logical index of the element this plug refers to. // int multiIndex = plug.logicalIndex( &status ); McheckErr(status, "ERROR in plug.logicalIndex.\n"); // Get input data handle, use outputArrayValue since we do not // want to evaluate both inputs, only the one related to the // requested multiIndex. Evaluating both inputs at once would cause // a dependency graph loop. MArrayDataHandle hInputArray = block.outputArrayValue( mInputData, &status ); McheckErr(status,"ERROR in hInputArray = block.outputArrayValue().\n"); status = hInputArray.jumpToElement( multiIndex ); McheckErr(status, "ERROR: hInputArray.jumpToElement failed.\n"); // get children of aInputData. MDataHandle hCompond = hInputArray.inputValue( &status ); McheckErr(status, "ERROR in hCompond=hInputArray.inputValue\n"); MDataHandle hPosition = hCompond.child( mInputPositions ); MObject dPosition = hPosition.data(); MFnVectorArrayData fnPosition( dPosition ); MVectorArray points = fnPosition.array( &status ); McheckErr(status, "ERROR in fnPosition.array(), not find points.\n"); // Comment out the following since velocity, and mass are // not needed in this field. // // MDataHandle hVelocity = hCompond.child( mInputVelocities ); // MObject dVelocity = hVelocity.data(); // MFnVectorArrayData fnVelocity( dVelocity ); // MVectorArray velocities = fnVelocity.array( &status ); // McheckErr(status, "ERROR in fnVelocity.array(), not find velocities.\n"); // // MDataHandle hMass = hCompond.child( mInputMass ); // MObject dMass = hMass.data(); // MFnDoubleArrayData fnMass( dMass ); // MDoubleArray masses = fnMass.array( &status ); // McheckErr(status, "ERROR in fnMass.array(), not find masses.\n"); // The attribute mInputPPData contains the attribute in an array form // parpared by the particleShape if the particleShape has per particle // attribute fieldName_attrName. // // Suppose a field with the name dynExprField1 is connecting to // particleShape1, and the particleShape1 has per particle float attribute // dynExprField1_magnitude and vector attribute dynExprField1_direction, // then hInputPPArray will contains a MdoubleArray with the corresponding // name "magnitude" and a MvectorArray with the name "direction". This // is a mechanism to allow the field attributes being driven by dynamic // expression. MArrayDataHandle mhInputPPData = block.inputArrayValue( mInputPPData, &status ); McheckErr(status,"ERROR in mhInputPPData = block.inputArrayValue().\n"); status = mhInputPPData.jumpToElement( multiIndex ); McheckErr(status, "ERROR: mhInputPPArray.jumpToElement failed.\n"); MDataHandle hInputPPData = mhInputPPData.inputValue( &status ); McheckErr(status, "ERROR in hInputPPData = mhInputPPData.inputValue\n"); MObject dInputPPData = hInputPPData.data(); MFnArrayAttrsData inputPPArray( dInputPPData ); MDataHandle hOwnerPPData = block.inputValue( mOwnerPPData, &status ); McheckErr(status, "ERROR in hOwnerPPData = block.inputValue\n"); MObject dOwnerPPData = hOwnerPPData.data(); MFnArrayAttrsData ownerPPArray( dOwnerPPData ); const MString magString("magnitude"); MFnArrayAttrsData::Type doubleType(MFnArrayAttrsData::kDoubleArray); bool arrayExist; MDoubleArray magnitudeArray; arrayExist = inputPPArray.checkArrayExist(magString, doubleType, &status); // McheckErr(status, "ERROR in checkArrayExist(magnitude)\n"); if(arrayExist) { magnitudeArray = inputPPArray.getDoubleData(magString, &status); // McheckErr(status, "ERROR in inputPPArray.doubleArray(magnitude)\n"); } MDoubleArray magnitudeOwnerArray; arrayExist = ownerPPArray.checkArrayExist(magString, doubleType, &status); // McheckErr(status, "ERROR in checkArrayExist(magnitude)\n"); if(arrayExist) { magnitudeOwnerArray = ownerPPArray.getDoubleData(magString, &status); // McheckErr(status, "ERROR in ownerPPArray.doubleArray(magnitude)\n"); } const MString dirString("direction"); MFnArrayAttrsData::Type vectorType(MFnArrayAttrsData::kVectorArray); arrayExist = inputPPArray.checkArrayExist(dirString, vectorType, &status); MVectorArray directionArray; // McheckErr(status, "ERROR in checkArrayExist(direction)\n"); if(arrayExist) { directionArray = inputPPArray.getVectorData(dirString, &status); // McheckErr(status, "ERROR in inputPPArray.vectorArray(direction)\n"); } arrayExist = ownerPPArray.checkArrayExist(dirString, vectorType, &status); MVectorArray directionOwnerArray; // McheckErr(status, "ERROR in checkArrayExist(direction)\n"); if(arrayExist) { directionOwnerArray = ownerPPArray.getVectorData(dirString, &status); // McheckErr(status, "ERROR in ownerPPArray.vectorArray(direction)\n"); } // Compute the output force. // MVectorArray forceArray; apply( block, points.length(), magnitudeArray, magnitudeOwnerArray, directionArray, directionOwnerArray, forceArray ); // get output data handle // MArrayDataHandle hOutArray = block.outputArrayValue( mOutputForce, &status); McheckErr(status, "ERROR in hOutArray = block.outputArrayValue.\n"); MArrayDataBuilder bOutArray = hOutArray.builder( &status ); McheckErr(status, "ERROR in bOutArray = hOutArray.builder.\n"); // get output force array from block. // MDataHandle hOut = bOutArray.addElement(multiIndex, &status); McheckErr(status, "ERROR in hOut = bOutArray.addElement.\n"); MFnVectorArrayData fnOutputForce; MObject dOutputForce = fnOutputForce.create( forceArray, &status ); McheckErr(status, "ERROR in dOutputForce = fnOutputForce.create\n"); // update data block with new output force data. // hOut.set( dOutputForce ); block.setClean( plug ); return( MS::kSuccess ); }
MStatus ProxyViz::compute( const MPlug& plug, MDataBlock& block ) { if(!m_enableCompute) return MS::kSuccess; if( plug == outValue ) { updateWorldSpace(thisMObject() ); MStatus status; ExampVox * defBox = plantExample(0); defBox->setGeomSizeMult(block.inputValue(aradiusMult).asFloat() ); defBox->setGeomBox(block.inputValue(abboxminx).asFloat(), block.inputValue(abboxminy).asFloat(), block.inputValue(abboxminz).asFloat(), block.inputValue(abboxmaxx).asFloat(), block.inputValue(abboxmaxy).asFloat(), block.inputValue(abboxmaxz).asFloat()); float grdsz = defBox->geomExtent() * 32.f ; if(grdsz < 32.f) { AHelper::Info<float>(" ProxyViz input box is too small", grdsz); grdsz = 32.f; AHelper::Info<float>(" trancated to", grdsz); } if(m_toSetGrid) { m_toSetGrid = false; resetGrid(grdsz); } if(_firstLoad) { /// internal cache only, initializing from external cache is obsolete if(!loadInternal(block) ) std::cout<<"\n ERROR proxviz cannot load internal cache"; _firstLoad = 0; } if(!m_toCheckVisibility) { MArrayDataHandle groundMeshArray = block.inputArrayValue(agroundMesh ); MArrayDataHandle groundSpaceArray = block.inputArrayValue(agroundSpace ); /// in case no ground is connected if(updateGround(groundMeshArray, groundSpaceArray )) { moveWithGround(); AHelper::Info<std::string>(" ProxyViz ground ", groundBuildLog() ); } } if(!m_hasParticle) { block.setClean(plug); return MS::kSuccess; } const int ngroups = block.inputValue(agroupcount).asInt(); MDataHandle hdata = block.inputValue(outPositionPP, &status); MFnVectorArrayData farray(hdata.data(), &status); if(!status) { MGlobal::displayInfo("proxy viz is not properly connected to a particle system"); block.setClean(plug); return MS::kSuccess; } MDataHandle scaledata = block.inputValue(outScalePP, &status); MFnVectorArrayData scalearray(scaledata.data(), &status); if(!status) { MGlobal::displayInfo("proxy viz is not properly connected to a particle system"); block.setClean(plug); return MS::kSuccess; } MDataHandle rotatedata = block.inputValue(outRotationPP, &status); MFnVectorArrayData rotatearray(rotatedata.data(), &status); if(!status) { MGlobal::displayInfo("proxy viz is not properly connected to a particle system"); block.setClean(plug); return MS::kSuccess; } MDataHandle replaceData = block.inputValue(outReplacePP, &status); MFnDoubleArrayData replaceArrayFn(replaceData.data(), &status); if(!status) { MGlobal::displayInfo("proxy viz is not properly connected to a particle system, needs userScalarPP"); block.setClean(plug); return MS::kSuccess; } MVectorArray outPosArray = farray.array(); MVectorArray outScaleArray = scalearray.array(); MVectorArray outRotateArray = rotatearray.array(); MDoubleArray outReplaceArray = replaceArrayFn.array(); if( outPosArray.length() < 1) { block.setClean(plug); return MS::kSuccess; } computePPAttribs(outPosArray, outRotateArray, outScaleArray, outReplaceArray, ngroups); float result = outPosArray.length(); MDataHandle outputHandle = block.outputValue( outValue ); outputHandle.set( result ); block.setClean(plug); } if(plug == outValue1) { MArrayDataHandle hArray = block.inputArrayValue(ainexamp); updateExamples(hArray); float result = 91.f; MDataHandle outputHandle = block.outputValue( outValue1 ); outputHandle.set( result ); block.setClean(plug); } return MS::kSuccess; }
void apiSimpleShapeUI::drawVertices( const MDrawRequest & request, M3dView & view ) const // // Description: // // Component (vertex) drawing routine // // Arguments: // // request - request to be drawn // view - view to draw into // { MDrawData data = request.drawData(); MVectorArray * geom = (MVectorArray*)data.geometry(); view.beginGL(); // Query current state so it can be restored // bool lightingWasOn = glIsEnabled( GL_LIGHTING ) ? true : false; if ( lightingWasOn ) { glDisable( GL_LIGHTING ); } float lastPointSize; glGetFloatv( GL_POINT_SIZE, &lastPointSize ); // Set the point size of the vertices // glPointSize( POINT_SIZE ); // If there is a component specified by the draw request // then loop over comp (using an MFnComponent class) and draw the // active vertices, otherwise draw all vertices. // MObject comp = request.component(); if ( ! comp.isNull() ) { MFnSingleIndexedComponent fnComponent( comp ); for ( int i=0; i<fnComponent.elementCount(); i++ ) { int index = fnComponent.element( i ); glBegin( GL_POINTS ); MVector& point = (*geom)[index]; glVertex3f( (float)point[0], (float)point[1], (float)point[2] ); glEnd(); char annotation[32]; sprintf( annotation, "%d", index ); view.drawText( annotation, point ); } } else { for ( unsigned int i=0; i<geom->length(); i++ ) { glBegin( GL_POINTS ); MVector point = (*geom)[ i ]; glVertex3f( (float)point[0], (float)point[1], (float)point[2] ); glEnd(); } } // Restore the state // if ( lightingWasOn ) { glEnable( GL_LIGHTING ); } glPointSize( lastPointSize ); view.endGL(); }
// // Main routine /////////////////////////////////////////////////////////////////////////////// MStatus particleSystemInfoCmd::doIt( const MArgList& args ) { MStatus stat = parseArgs( args ); if( stat != MS::kSuccess ) return stat; if( particleNode.isNull() ) { MObject parent; MFnParticleSystem dummy; particleNode = dummy.create(&stat); CHECKRESULT(stat,"MFnParticleSystem::create(status) failed!"); MFnParticleSystem ps( particleNode, &stat ); CHECKRESULT(stat,"MFnParticleSystem::MFnParticleSystem(MObject,status) failed!"); MPointArray posArray; posArray.append(MPoint(-5, 5, 0)); posArray.append(MPoint(-5, 10, 0)); MVectorArray velArray; velArray.append(MPoint(1, 1, 0)); velArray.append(MPoint(1, 1, 0)); stat = ps.emit(posArray, velArray); CHECKRESULT(stat,"MFnParticleSystem::emit(posArray,velArray) failed!"); stat = ps.emit(MPoint(5, 5, 0)); CHECKRESULT(stat,"MFnParticleSystem::emit(pos) failed!"); stat = ps.emit(MPoint(5, 10, 0)); CHECKRESULT(stat,"MFnParticleSystem::emit(pos) failed!"); stat = ps.saveInitialState(); CHECKRESULT(stat,"MFnParticleSystem::saveInitialState() failed!"); MVectorArray accArray; accArray.setLength(4); for( unsigned int i=0; i<accArray.length(); i++ ) { MVector& acc = accArray[i]; acc.x = acc.y = acc.z = 3.0; } MString accName("acceleration"); ps.setPerParticleAttribute( accName, accArray, &stat ); CHECKRESULT(stat,"MFnParticleSystem::setPerParticleAttribute(vectorArray) failed!"); } MFnParticleSystem ps( particleNode, &stat ); CHECKRESULT(stat,"MFnParticleSystem::MFnParticleSystem(MObject,status) failed!"); if( ! ps.isValid() ) { MGlobal::displayError( "The function set is invalid!" ); return MS::kFailure; } const MString name = ps.particleName(); const MFnParticleSystem::RenderType psType = ps.renderType(); const unsigned int count = ps.count(); const char* typeString = NULL; switch( psType ) { case MFnParticleSystem::kCloud: typeString = "Cloud"; break; case MFnParticleSystem::kTube: typeString = "Tube system"; break; case MFnParticleSystem::kBlobby: typeString = "Blobby"; break; case MFnParticleSystem::kMultiPoint: typeString = "MultiPoint"; break; case MFnParticleSystem::kMultiStreak: typeString = "MultiStreak"; break; case MFnParticleSystem::kNumeric: typeString = "Numeric"; break; case MFnParticleSystem::kPoints: typeString = "Points"; break; case MFnParticleSystem::kSpheres: typeString = "Spheres"; break; case MFnParticleSystem::kSprites: typeString = "Sprites"; break; case MFnParticleSystem::kStreak: typeString = "Streak"; break; default: typeString = "Particle system"; assert( false ); break; } char buffer[256]; sprintf( buffer, "%s \"%s\" has %u primitives.", typeString, name.asChar(), count ); MGlobal::displayInfo( buffer ); unsigned i; MIntArray ids; ps.particleIds( ids ); sprintf( buffer, "count : %u ", count ); MGlobal::displayInfo( buffer ); sprintf( buffer, "%u ids.", ids.length() ); MGlobal::displayInfo( buffer ); assert( ids.length() == count ); for( i=0; i<ids.length(); i++ ) { sprintf( buffer, "id %d ", ids[i] ); MGlobal::displayInfo( buffer ); } MVectorArray positions; ps.position( positions ); assert( positions.length() == count ); for( i=0; i<positions.length(); i++ ) { MVector& p = positions[i]; sprintf( buffer, "pos %f %f %f ", p[0], p[1], p[2] ); MGlobal::displayInfo( buffer ); } MVectorArray vels; ps.velocity( vels ); assert( vels.length() == count ); for( i=0; i<vels.length(); i++ ) { const MVector& v = vels[i]; sprintf( buffer, "vel %f %f %f ", v[0], v[1], v[2] ); MGlobal::displayInfo( buffer ); } MVectorArray accs; ps.acceleration( accs ); assert( accs.length() == count ); for( i=0; i<accs.length(); i++ ) { const MVector& a = accs[i]; sprintf( buffer, "acc %f %f %f ", a[0], a[1], a[2] ); MGlobal::displayInfo( buffer ); } bool flag = ps.isDeformedParticleShape(&stat); CHECKRESULT(stat,"MFnParticleSystem::isDeformedParticleShape() failed!"); if( flag ) { MObject obj = ps.originalParticleShape(&stat); CHECKRESULT(stat,"MFnParticleSystem::originalParticleShape() failed!"); if( obj != MObject::kNullObj ) { MFnParticleSystem ps( obj ); sprintf( buffer, "original particle shape : %s ", ps.particleName().asChar() ); MGlobal::displayInfo( buffer ); } } flag = ps.isDeformedParticleShape(&stat); CHECKRESULT(stat,"MFnParticleSystem::isDeformedParticleShape() failed!"); if( !flag ) { MObject obj = ps.deformedParticleShape(&stat); CHECKRESULT(stat,"MFnParticleSystem::deformedParticleShape() failed!"); if( obj != MObject::kNullObj ) { MFnParticleSystem ps( obj ); sprintf( buffer, "deformed particle shape : %s ", ps.particleName().asChar() ); MGlobal::displayInfo( buffer ); } } if( ids.length() == positions.length() && ids.length() == vels.length() && ids.length() == accs.length() ) { setResult( int(ids.length()) ); } else { setResult( int(-1) ); } return MS::kSuccess; }
bool ToMayaMeshConverter::doConversion( IECore::ConstObjectPtr from, MObject &to, IECore::ConstCompoundObjectPtr operands ) const { MStatus s; IECore::ConstMeshPrimitivePtr mesh = IECore::runTimeCast<const IECore::MeshPrimitive>( from ); assert( mesh ); if ( !mesh->arePrimitiveVariablesValid() ) { return false; } MFloatPointArray vertexArray; MIntArray polygonCounts; MIntArray polygonConnects; MFnMesh fnMesh; int numVertices = 0; IECore::PrimitiveVariableMap::const_iterator it = mesh->variables.find("P"); if ( it != mesh->variables.end() ) { /// \todo Employ some M*Array converters to simplify this IECore::ConstV3fVectorDataPtr p = IECore::runTimeCast<const IECore::V3fVectorData>(it->second.data); if (p) { numVertices = p->readable().size(); vertexArray.setLength( numVertices ); for (int i = 0; i < numVertices; i++) { vertexArray[i] = IECore::convert<MFloatPoint, Imath::V3f>( p->readable()[i] ); } } else { IECore::ConstV3dVectorDataPtr p = IECore::runTimeCast<const IECore::V3dVectorData>(it->second.data); if (p) { numVertices = p->readable().size(); vertexArray.setLength( numVertices ); for (int i = 0; i < numVertices; i++) { vertexArray[i] = IECore::convert<MFloatPoint, Imath::V3d>( p->readable()[i] ); } } else { // "P" is not convertible to an array of "points" return false; } } } IECore::ConstIntVectorDataPtr verticesPerFace = mesh->verticesPerFace(); assert( verticesPerFace ); int numPolygons = verticesPerFace->readable().size(); polygonCounts.setLength( numPolygons ); for (int i = 0; i < numPolygons; i++) { polygonCounts[i] = verticesPerFace->readable()[i]; } IECore::ConstIntVectorDataPtr vertexIds = mesh->vertexIds(); assert( vertexIds ); int numPolygonConnects = vertexIds->readable().size(); polygonConnects.setLength( numPolygonConnects ); for (int i = 0; i < numPolygonConnects; i++) { polygonConnects[i] = vertexIds->readable()[i]; } MObject mObj = fnMesh.create( numVertices, numPolygons, vertexArray, polygonCounts, polygonConnects, to, &s ); if (!s) { return false; } it = mesh->variables.find("N"); if ( it != mesh->variables.end() ) { if (it->second.interpolation == IECore::PrimitiveVariable::FaceVarying ) { /// \todo Employ some M*Array converters to simplify this MVectorArray vertexNormalsArray; IECore::ConstV3fVectorDataPtr n = IECore::runTimeCast<const IECore::V3fVectorData>(it->second.data); if (n) { int numVertexNormals = n->readable().size(); vertexNormalsArray.setLength( numVertexNormals ); for (int i = 0; i < numVertexNormals; i++) { vertexNormalsArray[i] = IECore::convert<MVector, Imath::V3f>( n->readable()[i] ); } } else { IECore::ConstV3dVectorDataPtr n = IECore::runTimeCast<const IECore::V3dVectorData>(it->second.data); if (n) { int numVertexNormals = n->readable().size(); vertexNormalsArray.setLength( numVertexNormals ); for (int i = 0; i < numVertexNormals; i++) { vertexNormalsArray[i] = IECore::convert<MVector, Imath::V3d>( n->readable()[i] ); } } else { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", boost::format( "PrimitiveVariable \"N\" has unsupported type \"%s\"." ) % it->second.data->typeName() ); } } if ( vertexNormalsArray.length() ) { MStatus status; MItMeshPolygon itPolygon( mObj, &status ); if( status != MS::kSuccess ) { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "Failed to create mesh iterator" ); } unsigned v = 0; MIntArray vertexIds; MIntArray faceIds; for ( ; !itPolygon.isDone(); itPolygon.next() ) { for ( v=0; v < itPolygon.polygonVertexCount(); ++v ) { faceIds.append( itPolygon.index() ); vertexIds.append( itPolygon.vertexIndex( v ) ); } } if( !fnMesh.setFaceVertexNormals( vertexNormalsArray, faceIds, vertexIds ) ) { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "Setting normals failed" ); } } } else { IECore::msg( IECore::Msg::Warning, "ToMayaMeshConverter::doConversion", "PrimitiveVariable \"N\" has unsupported interpolation (expected FaceVarying)." ); } } bool haveDefaultUVs = false; IECore::PrimitiveVariableMap::const_iterator sIt = mesh->variables.find( "s" ); IECore::RefCountedPtr sDataRef = ( sIt == mesh->variables.end() ) ? 0 : static_cast<IECore::RefCountedPtr>( sIt->second.data ); /// Add named UV sets std::set< std::string > uvSets; for ( it = mesh->variables.begin(); it != mesh->variables.end(); ++it ) { const std::string &sName = it->first; size_t suffixOffset = sName.rfind( "_s" ); if ( ( suffixOffset != std::string::npos) && ( suffixOffset == sName.length() - 2 ) ) { std::string uvSetNameStr = sName.substr( 0, suffixOffset ); if ( uvSetNameStr.size() ) { MString uvSetName = uvSetNameStr.c_str(); std::string tName = uvSetNameStr + "_t"; std::string stIdName = uvSetNameStr + "Indices"; addUVSet( fnMesh, polygonCounts, mesh, sName, tName, stIdName, &uvSetName ); uvSets.insert( uvSetNameStr ); if ( sDataRef == static_cast<IECore::RefCountedPtr>( it->second.data ) ) { haveDefaultUVs = true; } } } } /// Add default UV set if it isn't just a reference to a named set if ( !haveDefaultUVs ) { addUVSet( fnMesh, polygonCounts, mesh, "s", "t", "stIndices" ); } // We do the search again, but looking for primvars ending "_t", so we can catch cases where either "UVSETNAME_s" or "UVSETNAME_t" is present, but not both, taking care // not to attempt adding any duplicate sets for ( it = mesh->variables.begin(); it != mesh->variables.end(); ++it ) { const std::string &tName = it->first; size_t suffixOffset = tName.rfind( "_t" ); if ( ( suffixOffset != std::string::npos) && ( suffixOffset == tName.length() - 2 ) ) { std::string uvSetNameStr = tName.substr( 0, suffixOffset ); if ( uvSetNameStr.size() && uvSets.find( uvSetNameStr ) == uvSets.end() ) { MString uvSetName = uvSetNameStr.c_str(); std::string sName = uvSetNameStr + "_s"; std::string stIdName = uvSetNameStr + "Indices"; addUVSet( fnMesh, polygonCounts, mesh, sName, tName, stIdName, &uvSetName ); uvSets.insert( uvSetNameStr ); } } } /// If we're making a mesh node (rather than a mesh data) then make sure it belongs /// to the default shading group and add the ieMeshInterpolation attribute. MObject oMesh = fnMesh.object(); if( oMesh.apiType()==MFn::kMesh ) { assignDefaultShadingGroup( oMesh ); setMeshInterpolationAttribute( oMesh, mesh->interpolation() ); } /// \todo Other primvars, e.g. vertex color ("Cs") return true; }
bool collide( const MObject hairSystem, const int follicleIndex, MVectorArray &hairPositions, MVectorArray &hairPositionsLast, const MDoubleArray &hairWidths, const int startIndex, const int endIndex, const double curTime, const double friction, const void *privateData ) { // Get the private data for the collision objects which was returned // from preFrame(). // COLLISION_INFO *ci = (COLLISION_INFO *) privateData; if ( !ci ) { fprintf( stderr,"%s:%d: collide() privateData pointer is NULL\n", __FILE__, __LINE__ ); return( false ); } // If object has no vertices, or hair has no segments, then there is // nothing to collide. In our example, we'll return here, but if you // want to implement your own hair processing such as smoothing or // freeze on the hair, you could proceed and let the processing happen // after the object loop so that the data gets processed even if no // collisions occur. // if ( ci->numObjs <= 0 || hairPositions.length() <= 0 ) { return( true ); } int obj; for ( obj = 0; obj < ci->numObjs; ++obj ) { COLLISION_OBJ *co = &ci->objs[obj]; // For our plug-in example, we just collide the segments of the // hair against the top of the bounding box for the geometry. // In an implementation where you only care about hair falling // downwards onto flat objects, this might be OK. However, in the // most deluxe of implementation, you should do the following: // // o Determine the motion of each face of your collision // object during the time range. // o Step through the follicle, and consider each segment // to be a moving cylinder where the start and end radii // can differ. The radii come from `hairWidths' and the // motion comes from the difference between `hairPositions' // and `hairPositionsLast'. // o Intersect each moving element (e.g. moving triangle // if you have a mesh obect) with each hair segment // e.g. moving cylinder). This intersection may occur // at a point within the frame. (Remember that the // hairPositions[] array holds the DESIRED location where // the hair system wants the hair to go. // o There can be multiple collisions along a hair segment. // Use the first location found and the maximal velocity. // // If a collision is detected, the `hairPositions' array should be // updated. `hairPositionsLast' may also be updated to provide a // dampening effect only. // // Loop through the follicle, starting at the root and advancing // toward the tip. // int numSegments = hairPositions.length() - 1; int seg; for ( seg = 0; seg < numSegments; ++seg ) { // Get the desired position of the segment (i.e. where the // solver is trying to put the hair for the current frame) // and the velocity required to get to that desired position. // // Thus, // P = hairPositions // Desired pos'n at cur frame. // L = hairPositionsLast // Position at prev frame. // V = P - L // Desired velocity of hair. // MVector pStart = hairPositions[seg]; MVector pEnd = hairPositions[seg + 1]; MVector vStart = pStart - hairPositionsLast[seg]; MVector vEnd = pEnd - hairPositionsLast[seg + 1]; // The proper way to time sample is to intersect the moving // segment of width `hairWidths[seg] to hairWidths[seg + 1]' // with the moving collision object. For the purposes of our // demo plug-in, we will simply assume the collision object is // static, the hair segment has zero width, and instead of // intersecting continuously in time, we will sample discrete // steps along the segment. // #define STEPS 4 int step; for ( step = 0; step < STEPS; ++step ) { // Compute the point for this step and its corresponding // velocity. This is a "time swept" point: // p1 = desired position at current time // p0 = position at previous time to achieve desired pos // MVector pCur, pPrev, v; double fracAlongSeg = step / ( (double) STEPS ); v = vStart * ( 1.0 - fracAlongSeg ) + vEnd * fracAlongSeg; MVector p1 = pStart * ( 1.0 - fracAlongSeg ) + pEnd * fracAlongSeg; MVector p0 = p1 - v; // See if BOTH endpoints are outside of the bounding box // on the same side. If so, then the segment cannot // intersect the bounding box. Note that we assume the // bounding box is static. // if ( (p0.x < co->minx && p1.x < co->minx) || (p0.y < co->miny && p1.y < co->miny) || (p0.z < co->minz && p1.z < co->minz) || (p0.x > co->maxx && p1.x > co->maxx) || (p0.y > co->maxy && p1.y > co->maxy) || (p0.z > co->maxz && p1.z > co->maxz) ) { continue; } // For the purposes of this example plug-in, we'll assume // the hair always moves downwards (due to gravity and // thus in the negative Y direction). As such, we only // need to test for collisions with the TOP of the // bounding box. Expanding the example to all 6 sides is // left as an exercise for the reader. // // Remember that p1 is the point at current time, and // p0 is the point at the previous time. Since we assume // the bounding box to be static, this simplifies things. // MVector where(-100000,-100000,-100000); // Loc'n of collision double fracTime; // Time at which collision happens 0..1 if ( fabs( v.y ) < EPSILON // velocity is zero && fabs( p1.y - co->maxy ) < EPSILON ) { // right on the bbox // Velocity is zero and the desired location (p1) is // right on top of the bounding box. // where = p1; fracTime = 1.0; // Collides right at end; } else { fracTime = ( co->maxy - p0.y ) / v.y; if ( fracTime >= 0.0 && fracTime <= 1.0 ) { // Compute the collision of the swept point with the // plane defined by the top of the bounding box. // where = p0 + v * fracTime; } } // If `seg' lies between startIndex and endIndex // we can move it. If its <= startIndex, the root is // locked and if >= endIndex the tip is locked. // if ( seg >= startIndex && seg <= endIndex ) { // Since we are just colliding with the top of the // bounding box, the normal where we hit is (0,1,0). // For the object velocity, we SHOULD measure the // relative motion of the object during the time // interval, but for our example, we'll assume its // not moving (0,0,0). // bool segCollides = false; MVector normal(0.0,1.0,0.0); // normal is always up MVector objectVel(0.0,0.0,0.0); // assume bbox is static // If we get the this point, then the intersection // point `where' is on the plane of the bounding box // top. See if it lies within the actual bounds of the // boxtop, and if so compute the position and velocity // information and apply to the hair segment. // if ( where.x >= co->minx && where.x <= co->maxx && where.z >= co->minz && where.z <= co->maxz && fracTime >= 0.0 && fracTime <= 0.0 ) { // We have a collision at `where' with the plane. // Compute the new velocity for the hair at the // point of collision. // MVector objVelAlongNormal = (objectVel * normal) * normal; MVector objVelAlongTangent = objectVel - objVelAlongNormal; MVector pntVelAlongTangent = v - ( v * normal ) * normal; MVector reflectedPntVelAlongTangent = pntVelAlongTangent * ( 1.0 - friction ) + objVelAlongTangent * friction; MVector newVel = objVelAlongNormal + reflectedPntVelAlongTangent; // Update the hair position. It actually looks // more stable from a simulation standpoint to // just move the closest segment endpoint, but // you are free to experiment. What we'll do in // our example is move the closest segment end- // point by the amount the collided point (where) // has to move to have velocity `newVel' yet still // pass through `where' at time `fracTime'. // if ( fracAlongSeg > 0.5 ) { MVector deltaPos = -newVel * fracTime; hairPositionsLast[seg + 1] += deltaPos; hairPositions[seg + 1] = hairPositionsLast[seg] + newVel; } else { MVector deltaPos = newVel * fracTime; hairPositionsLast[seg] += deltaPos; hairPositions[seg] = hairPositionsLast[seg] + newVel; } segCollides = true; } // Check for segment endpoints that may still be // inside. Note that segments which started out being // totally inside will never collide using the // algorithm we use above, so we'll simply clamp them // here. One might expect an inside-the-bounding-box // test instead. However, this does not work if the // collision object is a very thin object. // bool inside = false; if ( belowCollisionPlane( co, hairPositions[seg] ) ) { hairPositions[seg].y = co->maxy + EPSILON; hairPositionsLast[seg] = hairPositions[seg] - objectVel; inside = true; } if ( belowCollisionPlane( co, hairPositions[seg+1] ) ) { hairPositions[seg+1].y = co->maxy + EPSILON; hairPositionsLast[seg+1] = hairPositions[seg+1] - objectVel; inside = true; } // If we collided, go onto the next segment. // if ( segCollides || inside ) { goto nextSeg; } } } nextSeg:; } } // You could perform any global filtering that you want on the hair // right here. For example: smoothing. This code is independent of // whether or not a collision occurred, and note that collide() is // called even with zero collision objects, so you will be guaranteed // of reaching here once per hair per iteration. // return( true ); }
MStatus DA_GridGenerator::compute(const MPlug &plug, MDataBlock &data) { MStatus stat; if (plug != aOutDynamicArray) return MS::kFailure; // // Control Inputs // double dWidth = data.inputValue(aWidth).asDouble(); double dHeight = data.inputValue(aHeight).asDouble(); int iResolutionX = data.inputValue(aResolutionX).asInt(); int iResolutionY = data.inputValue(aResolutionY).asInt(); short ePattern = data.inputValue(aPattern).asShort(); // Create output MFnArrayAttrsData fnOutDynamicArray; fnOutDynamicArray.create(); // Create position data MVectorArray outPositionPP = fnOutDynamicArray.vectorArray("position"); // // Create grid // double xOffset = dWidth / ((double)iResolutionX - 1); double yOffset = dHeight / ((double)iResolutionY - 1); // Keep brick pattern in range if (ePattern == 1) xOffset -= (xOffset/2) / double(iResolutionX); if (ePattern == 2) yOffset -= (yOffset/2) / double(iResolutionY); // Generate grid for(int i = 0; i < iResolutionX; i++) { for(int j = 0; j < iResolutionY; j++) { MVector position; position.x = -dWidth / 2; position.y = 0; position.z = -dHeight / 2; // Pattern offset if (ePattern == 1) position.x += (xOffset/2) * double(j % 2); if (ePattern == 2) position.z += (yOffset/2) * double(i % 2); position.x += xOffset * i; position.z += yOffset * j; outPositionPP.append( position ); } } // // Set output data // MDataHandle outArray = data.outputValue(aOutDynamicArray); outArray.set(fnOutDynamicArray.object()); // Set plug to clean data.setClean(aOutDynamicArray); // Done return MS::kSuccess; }
MStatus liqAttachPrefAttribute::redoIt() { MFnTypedAttribute tAttr; MStatus status; for ( unsigned i( 0 ); i < objectNames.length(); i++ ) { MSelectionList nodeList; nodeList.add( objectNames[i] ); MObject depNodeObj; nodeList.getDependNode( 0, depNodeObj ); MDagPath dagNode; nodeList.getDagPath( 0, dagNode ); MFnDependencyNode depNode( depNodeObj ); MObject prefAttr; MString attrName, varName; // make sure the renderer description is up to date liqglo.liquidRenderer.setRenderer(); // build the name of the attribute varName = ( ( exportN && depNodeObj.hasFn( MFn::kMesh ) )? "N":"P" ); attrName = "rman"; attrName += varName; attrName += ( ( liqglo.liquidRenderer.requires__PREF )? "__":"" ); attrName += varName + "ref"; // create the attribute prefAttr = tAttr.create( attrName, attrName, MFnData::kPointArray ); if ( depNodeObj.hasFn( MFn::kNurbsSurface ) ) { MFnNurbsSurface nodeFn( depNodeObj ); MPointArray nodePArray; MItSurfaceCV cvs( dagNode, MObject::kNullObj, liqglo.liquidRenderer.requires_SWAPPED_UVS == false, &status ); while( !cvs.isDone() ) { while( !cvs.isRowDone() ) { MPoint pt = (worldSpace)? cvs.position( MSpace::kWorld ) : cvs.position( MSpace::kObject ); nodePArray.append( pt ); cvs.next(); } cvs.nextRow(); } nodeFn.addAttribute( prefAttr ); MFnPointArrayData pArrayData; MObject prefDefault = pArrayData.create( nodePArray ); MPlug nodePlug( depNodeObj, prefAttr ); nodePlug.setValue( prefDefault ); } else if ( depNodeObj.hasFn( MFn::kNurbsCurve ) ) { // Carsten: added support for PREF on nurbs curves // MFnNurbsCurve nodeFn( depNodeObj ); MPointArray nodePArray; nodeFn.getCVs( nodePArray ); nodeFn.addAttribute( prefAttr ); MFnPointArrayData pArrayData; MObject prefDefault = pArrayData.create( nodePArray ); MPlug nodePlug( depNodeObj, prefAttr ); nodePlug.setValue( prefDefault ); } else if ( depNodeObj.hasFn( MFn::kMesh ) ) { MFnMesh nodeFn( depNodeObj ); // Moritz: modified this line to dim nodePArray -- otherwise // nodePArray.set() in the wile loop below throws an exception // which was why __Pref didn't work MPointArray nodePArray( MFnMesh( depNodeObj ).numVertices() ); unsigned count; nodeFn.addAttribute( prefAttr ); if ( exportN ) { // export Nref unsigned vertex; unsigned normal; unsigned face = 0; unsigned faceVertex = 0; unsigned int numNormals = nodeFn.numNormals(); unsigned int numPoints = nodeFn.numVertices(); MFloatVectorArray normals; MVectorArray normalAttArray; nodeFn.getNormals( normals ); if ( numNormals > numPoints ) { // if we get more than 1 normal per vertex, // force the arraysize to the full facevarying size unsigned faceVaryingCount( 0 ); for ( unsigned pOn( 0 ); pOn < nodeFn.numPolygons(); pOn++ ) faceVaryingCount += nodeFn.polygonVertexCount( pOn ); normalAttArray.setLength( faceVaryingCount ); } else normalAttArray.setLength(normals.length()); for ( MItMeshPolygon polyIt ( depNodeObj ); polyIt.isDone() == false; polyIt.next() ) { count = polyIt.polygonVertexCount(); while ( count > 0 ) { --count; normal = polyIt.normalIndex( count ); vertex = polyIt.vertexIndex( count ); if( numNormals == numPoints ) normalAttArray.set(normals[normal], vertex); else normalAttArray.set(normals[normal], faceVertex); ++faceVertex; } ++face; } MFnVectorArrayData pArrayData; MObject prefDefault = pArrayData.create( normalAttArray ); MPlug nodePlug( depNodeObj, prefAttr ); nodePlug.setValue( prefDefault ); } else { // TODO: do we need to account for the altMeshExport algo that's // used in liquidRibMeshData? // Moritz: no, it's basically the same as the algo below for ( MItMeshPolygon polyIt( dagNode, MObject::kNullObj ); !polyIt.isDone(); polyIt.next()) { count = polyIt.polygonVertexCount(); while ( count > 0 ) { --count; unsigned vertexIndex = polyIt.vertexIndex( count ); MPoint nodePoint = (worldSpace)? polyIt.point( count, MSpace::kWorld ) : polyIt.point( count, MSpace::kObject ); // Moritz: this returns MS::kFailure but seems to work?! nodePArray.set( nodePoint, vertexIndex ); } } MFnPointArrayData pArrayData; MObject prefDefault = pArrayData.create( nodePArray ); MPlug nodePlug( depNodeObj, prefAttr ); nodePlug.setValue( prefDefault ); } } else cerr << "Neither a Nurbs nor a Mesh !!" << endl; } return MS::kSuccess; }
IECore::DataPtr convert( const MCommandResult &result ) { MStatus s; switch (result.resultType()) { case MCommandResult::kInvalid: { // No result return 0; } case MCommandResult::kInt: { int i; s = result.getResult(i); assert(s); IECore::IntDataPtr data = new IECore::IntData(); data->writable() = i; return data; } case MCommandResult::kIntArray: { MIntArray v; s = result.getResult(v); assert(s); unsigned sz = v.length(); IECore::IntVectorDataPtr data = new IECore::IntVectorData(); data->writable().resize(sz); for (unsigned i = 0; i < sz; i++) { (data->writable())[i] = v[i]; } return data; } case MCommandResult::kDouble: { double d; s = result.getResult(d); assert(s); IECore::FloatDataPtr data = new IECore::FloatData(); data->writable() = static_cast<float>(d); return data; } case MCommandResult::kDoubleArray: { MDoubleArray v; s = result.getResult(v); assert(s); unsigned sz = v.length(); IECore::DoubleVectorDataPtr data = new IECore::DoubleVectorData(); data->writable().resize(sz); for (unsigned i = 0; i < sz; i++) { data->writable()[i] = v[i]; } return data; } case MCommandResult::kString: { MString str; s = result.getResult(str); assert(s); IECore::StringDataPtr data = new IECore::StringData(); data->writable() = std::string(str.asChar()); return data; } case MCommandResult::kStringArray: { MStringArray v; s = result.getResult(v); assert(s); unsigned sz = v.length(); IECore::StringVectorDataPtr data = new IECore::StringVectorData(); data->writable().resize(sz); for (unsigned i = 0; i < sz; i++) { data->writable()[i] = std::string(v[i].asChar()); } return data; } case MCommandResult::kVector: { MVector v; s = result.getResult(v); assert(s); IECore::V3fDataPtr data = new IECore::V3fData(); data->writable() = Imath::V3f(v.x, v.y, v.z); return data; } case MCommandResult::kVectorArray: { MVectorArray v; s = result.getResult(v); assert(s); unsigned sz = v.length(); IECore::V3fVectorDataPtr data = new IECore::V3fVectorData(); data->writable().resize(sz); for (unsigned i = 0; i < sz; i++) { data->writable()[i] = Imath::V3f(v[i].x, v[i].y, v[i].z); } return data; } case MCommandResult::kMatrix: { MDoubleArray v; int numRows, numColumns; s = result.getResult(v, numRows, numColumns); assert(s); if (numRows > 4 || numColumns > 4) { throw IECoreMaya::StatusException( MS::kFailure ); } IECore::M44fDataPtr data = new IECore::M44fData(); for (int i = 0; i < numColumns; i++) { for (int j = 0; j < numRows; j++) { (data->writable())[i][j] = v[i*numRows+j]; } } return data; } case MCommandResult::kMatrixArray: { return 0; } default: assert( false ); return 0; } }
//apply fields in the scene from the rigid body void dSolverNode::applyFields(MPlugArray &rbConnections, float dt) { MVectorArray position; MVectorArray velocity; MDoubleArray mass; std::vector<rigid_body_t*> rigid_bodies; //gather active rigid bodies for(size_t i = 0; i < rbConnections.length(); ++i) { MObject node = rbConnections[i].node(); MFnDagNode fnDagNode(node); if(fnDagNode.typeId() == rigidBodyNode::typeId) { rigidBodyNode *rbNode = static_cast<rigidBodyNode*>(fnDagNode.userNode()); rigid_body_t::pointer rb = rbNode->rigid_body(); MPlug plgMass(node, rigidBodyNode::ia_mass); float mass = 0.f; plgMass.getValue(mass); bool active = (mass>0.f); if(active) { rigid_bodies.push_back(rb.get()); } } else if(fnDagNode.typeId() == rigidBodyArrayNode::typeId) { rigidBodyArrayNode *rbNode = static_cast<rigidBodyArrayNode*>(fnDagNode.userNode()); std::vector<rigid_body_t::pointer>& rbs = rbNode->rigid_bodies(); MPlug plgMass(node, rigidBodyArrayNode::ia_mass); float mass = 0.f; plgMass.getValue(mass); bool active = (mass>0.f); if(active) { for(size_t j = 0; j < rbs.size(); ++j) { rigid_bodies.push_back(rbs[j].get()); } } } } //clear forces and get the properties needed for field computation for(size_t i = 0; i < rigid_bodies.size(); ++i) { rigid_bodies[i]->clear_forces(); vec3f pos, vel; quatf rot; rigid_bodies[i]->get_transform(pos, rot); rigid_bodies[i]->get_linear_velocity(vel); position.append(MVector(pos[0], pos[1], pos[2])); velocity.append(MVector(vel[0], vel[1], vel[2])); //TODO mass.append(1.0); // } //apply the fields to the rigid bodies MVectorArray force; for(MItDag it(MItDag::kDepthFirst, MFn::kField); !it.isDone(); it.next()) { MFnField fnField(it.item()); fnField.getForceAtPoint(position, velocity, mass, force, dt); for(size_t i = 0; i < rigid_bodies.size(); ++i) { rigid_bodies[i]->apply_central_force(vec3f(force[i].x, force[i].y, force[i].z)); } } }