/* virtual */ MStatus cgfxVector::compute( const MPlug& plug, MDataBlock& data ) { MStatus status; MFnData::Type dataType = MFnData::kInvalid; if( plug == sWorldVector || plug == sWorldVectorX || plug == sWorldVectorY || plug == sWorldVectorZ || plug == sWorldVectorW) { // We do isDirection first simply because if there is an // error, the isDirection error is more legible than the // vector or matrix error. // MDataHandle dhIsDirection = data.inputValue(sIsDirection, &status); if (!status) { status.perror("cgfxVector: isDirection handle"); return status; } dataType = dhIsDirection.type(); MDataHandle dhVector = data.inputValue(sVector, &status); if (!status) { status.perror("cgfxVector: vector handle"); return status; } dataType = dhVector.type(); MMatrix matrix; MPlug matrixPlug(thisMObject(), sMatrix); if (matrixPlug.isNull()) { OutputDebugString("matrixPlug is NULL!\n"); } // TODO: Fix this kludge. // // We should not have to do this but for some reason, // using data.inputValue() fails for the sMatrix attribute. // Instead, we get a plug to the attribute and then get // the value directly. // MObject oMatrix; matrixPlug.getValue(oMatrix); MFnMatrixData fndMatrix(oMatrix, &status); if (!status) { status.perror("cgfxVector: matrix data"); } matrix= fndMatrix.matrix(&status); if (!status) { status.perror("cgfxVector: get matrix"); } #if 0 // TODO: This is how we are supposed to do it. (I think). // MDataHandle dhMatrix = data.inputValue(sMatrix, &status); if (!status) { status.perror("cgfxVector: matrix handle"); } dataType = dhMatrix.type(); oMatrix = dhMatrix.data(); MFnMatrixData fnMatrix(oMatrix, &status); if (!status) { status.perror("cgfxVector: matrix function set"); } matrix = fnMatrix.matrix(); #endif /* 0 */ bool isDirection = dhIsDirection.asBool(); double3& vector = dhVector.asDouble3(); double mat[4][4]; matrix.get(mat); double ix, iy, iz, iw; // Input vector float ox, oy, oz, ow; // Output vector ix = vector[0]; iy = vector[1]; iz = vector[2]; iw = isDirection ? 0.0 : 1.0; ox = (float)(mat[0][0] * ix + mat[1][0] * iy + mat[2][0] * iz + mat[3][0] * iw); oy = (float)(mat[0][1] * ix + mat[1][1] * iy + mat[2][1] * iz + mat[3][1] * iw); oz = (float)(mat[0][2] * ix + mat[1][2] * iy + mat[2][2] * iz + mat[3][2] * iw); ow = (float)(mat[0][3] * ix + mat[1][3] * iy + mat[2][3] * iz + mat[3][3] * iw); MDataHandle dhWVector = data.outputValue(sWorldVector, &status); if (!status) { status.perror("cgfxVector: worldVector handle"); return status; } MDataHandle dhWVectorW = data.outputValue(sWorldVectorW, &status); if (!status) { status.perror("cgfxVector: worldVectorW handle"); return status; } dhWVector.set(ox, oy, oz); dhWVectorW.set(ow); data.setClean(sWorldVector); data.setClean(sWorldVectorW); } else { return MS::kUnknownParameter; } return MS::kSuccess; }
MStatus sseDeformer::compute(const MPlug& plug, MDataBlock& data) { MStatus status; if (plug.attribute() != outputGeom) { printf("Ignoring requested plug\n"); return status; } unsigned int index = plug.logicalIndex(); MObject thisNode = this->thisMObject(); // get input value MPlug inPlug(thisNode,input); inPlug.selectAncestorLogicalIndex(index,input); MDataHandle hInput = data.inputValue(inPlug, &status); MCheckStatus(status, "ERROR getting input mesh\n"); // get the input geometry MDataHandle inputData = hInput.child(inputGeom); if (inputData.type() != MFnData::kMesh) { printf("Incorrect input geometry type\n"); return MStatus::kFailure; } MObject iSurf = inputData.asMesh() ; MFnMesh inMesh; inMesh.setObject( iSurf ) ; MDataHandle outputData = data.outputValue(plug); outputData.copy(inputData); if (outputData.type() != MFnData::kMesh) { printf("Incorrect output mesh type\n"); return MStatus::kFailure; } MObject oSurf = outputData.asMesh() ; if(oSurf.isNull()) { printf("Output surface is NULL\n"); return MStatus::kFailure; } MFnMesh outMesh; outMesh.setObject( oSurf ) ; MCheckStatus(status, "ERROR setting points\n"); // get all points at once for demo purposes. Really should get points from the current group using iterator MFloatPointArray pts; outMesh.getPoints(pts); int nPoints = pts.length(); MDataHandle envData = data.inputValue(envelope, &status); float env = envData.asFloat(); MDataHandle sseData = data.inputValue(sseEnabled, &status); bool sseEnabled = (bool) sseData.asBool(); // NOTE: Using MTimer and possibly other classes disables // autovectorization with Intel <=10.1 compiler on OSX and Linux!! // Must compile this function with -fno-exceptions on OSX and // Linux to guarantee autovectorization is done. Use -fvec_report2 // to check for vectorization status messages with Intel compiler. MTimer timer; timer.beginTimer(); if(sseEnabled) { // Innter loop will autovectorize. Around 3x faster than the // loop below it. It would be faster if first element was // guaranteed to be aligned on 16 byte boundary. for(int i=0; i<nPoints; i++) { float* ptPtr = &pts[i].x; for(int j=0; j<4; j++) { ptPtr[j] = env * (cosf(ptPtr[j]) * sinf(ptPtr[j]) * tanf(ptPtr[j])); } } } else { // This inner loop will not autovectorize. for(int i=0; i<nPoints; i++) { MFloatPoint& pt = pts[i]; for(int j=0; j<3; j++) { pt[j] = env * (cosf(pt[j]) * sinf(pt[j]) * tanf(pt[j])); } } } timer.endTimer(); if(sseEnabled) { printf("SSE enabled, runtime %f\n", timer.elapsedTime()); } else { printf("SSE disabled, runtime %f\n", timer.elapsedTime()); } outMesh.setPoints(pts); return status; }
/*! Compute function, gets the input surface, determines what type it is and calls the appropriate conversion function Encapsulates an cowpointer to the body into the naiadBodyData type and outputs it */ MStatus NBuddySurfaceToBodyNode::compute( const MPlug& plug, MDataBlock& data ) { MStatus status; if (plug == _outBody) { //Get the body name MDataHandle bodyNameHndl = data.inputValue( _bodyName, &status ); MString bodyName = bodyNameHndl.asString(); //Create the MFnPluginData for the naiadBody MFnPluginData dataFn; dataFn.create( MTypeId( naiadBodyData::id ), &status); NM_CheckMStatus( status, "Failed to create naiadBodyData in MFnPluginData"); //Get subdivision info from plugs so better approximations of meshes can be done int divisions = data.inputValue( _subDivide, &status ).asBool(); //Getting genericAttribute handle containing the surface and pick the correct conversion function MObject meshObj; MDataHandle inSurfaceHdl = data.inputValue( _inSurface, &status ); if (inSurfaceHdl.type() == MFnData::kNurbsSurface) { MFnNurbsSurface nurbsFn(inSurfaceHdl.asNurbsSurface()); // Create the data holder for the tesselated mesh MFnMeshData dataCreator; MObject newOutputData = dataCreator.create(&status); //Setup the tesselation parameters MTesselationParams tParams; tParams.setOutputType( MTesselationParams::kTriangles ); tParams.setFormatType( MTesselationParams::kGeneralFormat ); tParams.setUIsoparmType( MTesselationParams::kSpanEquiSpaced ); tParams.setVIsoparmType( MTesselationParams::kSpanEquiSpaced ); tParams.setUNumber( divisions+1 ); tParams.setVNumber( divisions+1 ); // Tesselate and get the returned mesh meshObj = nurbsFn.tesselate( tParams, newOutputData, &status ); NM_CheckMStatus( status, "NBuddySurfaceToBodyNode::compute Failed to tesselate nurbs surface to poly"); } else if (inSurfaceHdl.type() == MFnData::kMesh) { meshObj = inSurfaceHdl.asMesh(); if ( divisions > 0 ) { MFnMeshData dataCreator; MObject newOutputData = dataCreator.create(&status); MFnMesh meshFn(meshObj); MIntArray faceIds; for ( unsigned int i(0); i < meshFn.numPolygons(); ++i ) faceIds.append(i); meshFn.subdivideFaces( faceIds , divisions ); } } else if (inSurfaceHdl.type() == MFnData::kSubdSurface) { // Create the subd function set so we can tesselate MFnSubd subDfn(inSurfaceHdl.asSubdSurface()); // Create the data holder for the tesselated mesh MFnMeshData dataCreator; MObject newOutputData = dataCreator.create(&status); // Tesselate the subD surface meshObj = subDfn.tesselate(true, 1 , divisions , newOutputData, &status ); NM_CheckMStatus( status, "NBuddySurfaceToBodyNode::compute Failed to tesselate SubD surface to poly"); } else return status ; //Get the handle for the input transform MDataHandle inTransformHdl = data.inputValue( _inTransform, &status ); NM_CheckMStatus( status, "Failed to get inTransform handle"); MDataHandle useTransformHdl = data.inputValue( _useTransform, &status); NM_CheckMStatus( status, "Failed to get worldSpaceHdl "); bool useTransform = useTransformHdl.asBool(); //Get a new naiadBodyData naiadBodyData * newBodyData = (naiadBodyData*)dataFn.data( &status ); NM_CheckMStatus( status, "Failed to get naiadBodyData handle from MFnPluginData"); try { newBodyData->nBody = mayaMeshToNaiadBody( meshObj, std::string(bodyName.asChar()), useTransform, inTransformHdl.asMatrix() ); } catch(std::exception& ex) { NM_ExceptionPlugDisplayError("NBuddySurfaceToBodyNode::compute ", plug, ex ); } //Give the data to the output handle and set it clean MDataHandle bodyDataHnd = data.outputValue( _outBody, &status ); NM_CheckMStatus( status, "Failed to get outputData handle for outBody"); bodyDataHnd.set( newBodyData ); data.setClean( plug ); } return status; }
MStatus splatDeformer::compute(const MPlug& plug, MDataBlock& data) { // do this if we are using an OpenMP implementation that is not the same as Maya's. // Even if it is the same, it does no harm to make this call. MThreadUtils::syncNumOpenMPThreads(); MStatus status = MStatus::kUnknownParameter; if (plug.attribute() != outputGeom) { return status; } unsigned int index = plug.logicalIndex(); MObject thisNode = this->thisMObject(); // get input value MPlug inPlug(thisNode,input); inPlug.selectAncestorLogicalIndex(index,input); MDataHandle hInput = data.inputValue(inPlug, &status); MCheckStatus(status, "ERROR getting input mesh\n"); // get the input geometry MDataHandle inputData = hInput.child(inputGeom); if (inputData.type() != MFnData::kMesh) { printf("Incorrect input geometry type\n"); return MStatus::kFailure; } // get the input groupId - ignored for now... MDataHandle hGroup = inputData.child(groupId); unsigned int groupId = hGroup.asLong(); // get deforming mesh MDataHandle deformData = data.inputValue(deformingMesh, &status); MCheckStatus(status, "ERROR getting deforming mesh\n"); if (deformData.type() != MFnData::kMesh) { printf("Incorrect deformer geometry type %d\n", deformData.type()); return MStatus::kFailure; } MObject dSurf = deformData.asMeshTransformed(); MFnMesh fnDeformingMesh; fnDeformingMesh.setObject( dSurf ) ; MDataHandle outputData = data.outputValue(plug); outputData.copy(inputData); if (outputData.type() != MFnData::kMesh) { printf("Incorrect output mesh type\n"); return MStatus::kFailure; } MItGeometry iter(outputData, groupId, false); // create fast intersector structure MMeshIntersector intersector; intersector.create(dSurf); // get all points at once. Faster to query, and also better for // threading than using iterator MPointArray verts; iter.allPositions(verts); int nPoints = verts.length(); // use bool variable as lightweight object for failure check in loop below bool failed = false; MTimer timer; timer.beginTimer(); #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i<nPoints; i++) { // Cannot break out of an OpenMP loop, so if one of the // intersections failed, skip the rest if(failed) continue; // mesh point object must be in loop-local scope to avoid race conditions MPointOnMesh meshPoint; // Do intersection. Need to use per-thread status value as // MStatus has internal state and may trigger race conditions // if set from multiple threads. Probably benign in this case, // but worth being careful. MStatus localStatus = intersector.getClosestPoint(verts[i], meshPoint); if(localStatus != MStatus::kSuccess) { // NOTE - we cannot break out of an OpenMP region, so set // bad status and skip remaining iterations failed = true; continue; } // default OpenMP scheduling breaks traversal into large // chunks, so low risk of false sharing here in array write. verts[i] = meshPoint.getPoint(); } timer.endTimer(); printf("Runtime for threaded loop %f\n", timer.elapsedTime()); // write values back onto output using fast set method on iterator iter.setAllPositions(verts); if(failed) { printf("Closest point failed\n"); return MStatus::kFailure; } return status; }
MStatus finalproject::compute(const MPlug& plug, MDataBlock& data) { // do this if we are using an OpenMP implementation that is not the same as Maya's. // Even if it is the same, it does no harm to make this call. MThreadUtils::syncNumOpenMPThreads(); MStatus status = MStatus::kUnknownParameter; if (plug.attribute() != outputGeom) { return status; } unsigned int index = plug.logicalIndex(); MObject thisNode = this->thisMObject(); // get input value MPlug inPlug(thisNode,input); inPlug.selectAncestorLogicalIndex(index,input); MDataHandle hInput = data.inputValue(inPlug, &status); MCheckStatus(status, "ERROR getting input mesh\n"); // get the input geometry MDataHandle inputData = hInput.child(inputGeom); if (inputData.type() != MFnData::kMesh) { printf("Incorrect input geometry type\n"); return MStatus::kFailure; } // get the input groupId - ignored for now... MDataHandle hGroup = inputData.child(groupId); unsigned int groupId = hGroup.asLong(); // get deforming mesh MDataHandle deformData = data.inputValue(deformingMesh, &status); MCheckStatus(status, "ERROR getting deforming mesh\n"); if (deformData.type() != MFnData::kMesh) { printf("Incorrect deformer geometry type %d\n", deformData.type()); return MStatus::kFailure; } MDataHandle offloadData = data.inputValue(offload, &status); //gathers world space positions of the object and the magnet MObject dSurf = deformData.asMeshTransformed(); MObject iSurf = inputData.asMeshTransformed(); MFnMesh fnDeformingMesh, fnInputMesh; fnDeformingMesh.setObject( dSurf ) ; fnInputMesh.setObject( iSurf ) ; MDataHandle outputData = data.outputValue(plug); outputData.copy(inputData); if (outputData.type() != MFnData::kMesh) { printf("Incorrect output mesh type\n"); return MStatus::kFailure; } MItGeometry iter(outputData, groupId, false); // get all points at once. Faster to query, and also better for // threading than using iterator MPointArray objVerts; iter.allPositions(objVerts); int objNumPoints = objVerts.length(); MPointArray magVerts, tempverts; fnDeformingMesh.getPoints(magVerts); fnInputMesh.getPoints(tempverts); int magNumPoints = magVerts.length(); double min = DBL_MAX, max = -DBL_MAX; //finds min and max z-coordinate values to determine middle point (choice of z-axis was ours) for (int i = 0; i < magNumPoints; i++) { min = magVerts[i].z < min ? magVerts[i].z : min; max = magVerts[i].z > max ? magVerts[i].z : max; } double middle = (min + max) / 2; double polarity[magNumPoints]; //assigns polarity based on middle point of mesh for (int i = 0; i < magNumPoints; i++) { polarity[i] = magVerts[i].z > middle ? max / magVerts[i].z : -min / magVerts[i].z; } double* objdVerts = (double *)malloc(sizeof(double) * objNumPoints * 3); double* magdVerts = (double *)malloc(sizeof(double) * magNumPoints * 3); //creates handles to use attribute data MDataHandle vecX = data.inputValue(transX, &status); MDataHandle vecY = data.inputValue(transY, &status); MDataHandle vecZ = data.inputValue(transZ, &status); //gathers previously stored coordinates of the center of the object double moveX = vecX.asFloat(); double moveY = vecY.asFloat(); double moveZ = vecZ.asFloat(); //translates object based on the position stored in the attribute values for (int i=0; i<objNumPoints; i++) { objdVerts[i * 3] = tempverts[i].x + moveX; objdVerts[i * 3 + 1] = tempverts[i].y + moveY; objdVerts[i * 3 + 2] = tempverts[i].z + moveZ; } for (int i=0; i<magNumPoints; i++) { magdVerts[i * 3] = magVerts[i].x; magdVerts[i * 3 + 1] = magVerts[i].y; magdVerts[i * 3 + 2] = magVerts[i].z; } double teslaData = data.inputValue(tesla, &status).asDouble(); MDataHandle posiData = data.inputValue(positivelycharged, &status); double pivot[6] = {DBL_MAX, -DBL_MAX, DBL_MAX, -DBL_MAX, DBL_MAX, -DBL_MAX}; //finds the pivot point of the object in world space prior to being affected by the magnet for (int i = 0; i < tempverts.length(); i++) { pivot[0] = tempverts[i].x < pivot[0] ? tempverts[i].x : pivot[0]; pivot[1] = tempverts[i].x > pivot[1] ? tempverts[i].x : pivot[1]; pivot[2] = tempverts[i].y < pivot[2] ? tempverts[i].y : pivot[2]; pivot[3] = tempverts[i].y > pivot[3] ? tempverts[i].y : pivot[3]; pivot[4] = tempverts[i].z < pivot[4] ? tempverts[i].z : pivot[4]; pivot[5] = tempverts[i].z > pivot[5] ? tempverts[i].z : pivot[5]; } MTimer timer; timer.beginTimer(); //main function call magnetForce(magNumPoints, objNumPoints, teslaData, magdVerts, objdVerts, polarity, posiData.asBool(), offloadData.asBool()); timer.endTimer(); printf("Runtime for threaded loop %f\n", timer.elapsedTime()); for (int i=0; i<objNumPoints; i++) { objVerts[i].x = objdVerts[i * 3 + 0]; objVerts[i].y = objdVerts[i * 3 + 1]; objVerts[i].z = objdVerts[i * 3 + 2]; } //finds the pivot point of object in world space after being affected by the magnet double objCenter[6] = {DBL_MAX, -DBL_MAX, DBL_MAX, -DBL_MAX, DBL_MAX, -DBL_MAX}; for (int i = 0; i < tempverts.length(); i++) { objCenter[0] = objVerts[i].x < objCenter[0] ? objVerts[i].x : objCenter[0]; objCenter[1] = objVerts[i].x > objCenter[1] ? objVerts[i].x : objCenter[1]; objCenter[2] = objVerts[i].y < objCenter[2] ? objVerts[i].y : objCenter[2]; objCenter[3] = objVerts[i].y > objCenter[3] ? objVerts[i].y : objCenter[3]; objCenter[4] = objVerts[i].z < objCenter[4] ? objVerts[i].z : objCenter[4]; objCenter[5] = objVerts[i].z > objCenter[5] ? objVerts[i].z : objCenter[5]; } //creates vector based on the two calculated pivot points moveX = (objCenter[0] + objCenter[1]) / 2 - (pivot[0] + pivot[1]) / 2; moveY = (objCenter[2] + objCenter[3]) / 2 - (pivot[2] + pivot[3]) / 2; moveZ = (objCenter[4] + objCenter[5]) / 2 - (pivot[4] + pivot[5]) / 2; //stores pivot vector for next computation if (teslaData) { vecX.setFloat(moveX); vecY.setFloat(moveY); vecZ.setFloat(moveZ); } // write values back onto output using fast set method on iterator iter.setAllPositions(objVerts, MSpace::kWorld); free(objdVerts); free(magdVerts); return status; }