// used when ray does not intersect object // to account for perspective, all edges are flattened onto // a plane defined by the raySource and rayDirection double gpuCacheIsectUtil::getEdgeSnapPointOnTriangle(const MPoint& raySource, const MVector& rayDirection, const MPoint& vert1, const MPoint& vert2, const MPoint& vert3, MPoint& snapPoint){ MPointArray verts; verts.insert(vert1,0); verts.insert(vert2,1); verts.insert(vert3,2); int edgeIndices[3][2]={{0,1},{1,2},{2,0}}; double minDistTri = std::numeric_limits<double>::max(); for(int edge=0; edge<3;edge++){ MPoint vertex1Org = verts[edgeIndices[edge][0]]; MPoint vertex2Org = verts[edgeIndices[edge][1]]; double coef_plane = rayDirection * raySource; double d = coef_plane - rayDirection * vertex1Org; MPoint vertex1 = vertex1Org + rayDirection * d; d = coef_plane - rayDirection * vertex2Org; MPoint vertex2 = vertex2Org + rayDirection * d; MVector edgeDir = vertex2 - vertex1; if (edgeDir.length()<0.0000001){ double dist = vertex1.distanceTo(raySource); if (dist < minDistTri) { minDistTri = dist; snapPoint = vertex1Org; } } else { MPoint edgePt; // Compute the closest point from the edge to cursor Ray. double percent = gpuCacheIsectUtil::getClosestPointOnLine(raySource, vertex1, vertex2, edgePt); double dist = edgePt.distanceTo(raySource); if (dist < minDistTri) { minDistTri = dist; snapPoint = (vertex1Org + percent * (vertex2Org - vertex1Org)); } } } return minDistTri; }
// used when ray does not intersect object // to account for perspective, all edges are flattened onto // a plane defined by the raySource and rayDirection double gpuCacheIsectUtil::getEdgeSnapPointOnBox(const MPoint& raySource, const MVector& rayDirection, const MBoundingBox& bbox, MPoint& snapPoint){ //if ray intersects bbox MPoint boxIntersectionPt; if(firstRayIntersection(bbox.min(), bbox.max(), raySource, rayDirection, NULL, &boxIntersectionPt)) { // ray intersects bounding box, so snapPoint is // closest hit on the outside of the box // and distance to box is 0 (for snapping purposes) snapPoint = boxIntersectionPt; return 0.0; } MPointArray verts; MPoint vmin = bbox.min(); MPoint vmax = bbox.max(); verts.insert(vmin,0); verts.insert(MPoint(vmax[0],vmin[1],vmin[2]),1); verts.insert(MPoint(vmax[0],vmax[1],vmin[2]),2); verts.insert(MPoint(vmin[0],vmax[1],vmin[2]),3); verts.insert(MPoint(vmin[0],vmin[1],vmax[2]),4); verts.insert(MPoint(vmax[0],vmin[1],vmax[2]),5); verts.insert(vmax,6); verts.insert(MPoint(vmin[0],vmax[1],vmax[2]),7); int edgeIndices[12][2]={{0,1},{1,2},{2,3},{3,0},{4,5},{5,6},{6,7},{7,4},{0,4},{1,5},{3,7},{2,6}}; double minDistRect = std::numeric_limits<double>::max(); for(int edge=0; edge<12;edge++){ MPoint vertex1Org = verts[edgeIndices[edge][0]]; MPoint vertex2Org = verts[edgeIndices[edge][1]]; double coef_plane = rayDirection * raySource; double d = coef_plane - rayDirection * vertex1Org; MPoint vertex1 = vertex1Org + rayDirection * d; d = coef_plane - rayDirection * vertex2Org; MPoint vertex2 = vertex2Org + rayDirection * d; MVector edgeDir = vertex2 - vertex1; if (edgeDir.length()<0.0000001){ double dist = vertex1.distanceTo(raySource); if (dist < minDistRect) { minDistRect = dist; snapPoint = vertex1Org; } } else { MPoint edgePt; // Compute the closest point from the edge to cursor Ray. double percent = gpuCacheIsectUtil::getClosestPointOnLine(raySource, vertex1, vertex2, edgePt); double dist = edgePt.distanceTo(raySource); if (dist < minDistRect) { minDistRect = dist; snapPoint = (vertex1Org + percent * (vertex2Org - vertex1Org)); } } } return minDistRect; }
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; }