//- // ========================================================================== // Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files contain unpublished // information proprietary to Autodesk, Inc. ("Autodesk") and/or its // licensors, which is protected by U.S. and Canadian federal copyright // law and by international treaties. // // The Data is provided for use exclusively by You. You have the right // to use, modify, and incorporate this Data into other products for // purposes authorized by the Autodesk software license agreement, // without fee. // // The copyright notices in the Software and this entire statement, // including the above license grant, this restriction and the // following disclaimer, must be included in all copies of the // Software, in whole or in part, and all derivative works of // the Software, unless such copies or derivative works are solely // in the form of machine-executable object code generated by a // source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. // AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED // WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF // NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR // PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR // TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS // BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, // DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK // AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY // OR PROBABILITY OF SUCH DAMAGES. // // ========================================================================== //+ // // Class: particleAttrNode // // Author: Lonnie Li // // Description: // // particleAttrNode is an example node that extends the MPxParticleAttributeMapperNode // type. These nodes allow us to define custom nodes to map per-particle attribute data // to a particle shape. // // In this particular example, we have defined two operations: // // - compute2DTexture() // - computeMesh() // // compute2DTexture() replicates the internal behaviour of Maya's internal 'arrayMapper' // node at the API level. Given an input texture node and a U/V coord per particle input, // this node will evaluate the texture at the given coordinates and map the result back // to the outColorPP or outValuePP attributes. See the method description for compute2DTexture() // for details on how to setup a proper attribute mapping graph. // // computeMesh() is a user-defined behaviour. It is called when the 'computeNode' attribute // is connected to a polygonal mesh. From there, given a particle count, it will map the // object space vertex positions of the mesh to a user-defined 'outPositions' vector attribute. // This 'outPositions' attribute can then be connected to a vector typed, per-particle attribute // on the shape to drive. In our particular example we drive the particle shape's 'rampPosition' // attribute. For further details, see the method description for computeMesh() to setup // this example. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MTypeId particleAttrNode::id = 0x81036; MObject particleAttrNode::outPositionPP; MObject particleAttrNode::particleCount; particleAttrNode::particleAttrNode() {} particleAttrNode::~particleAttrNode() {} void* particleAttrNode::creator() { return new particleAttrNode(); } MStatus particleAttrNode::initialize() { MStatus status = MS::kSuccess; // In addition to the standard arrayMapper attributes, // create an additional vector attribute. // MFnTypedAttribute typedAttrFn; MVectorArray defaultVectArray; MFnVectorArrayData vectArrayDataFn; vectArrayDataFn.create( defaultVectArray ); typedAttrFn.create( "outPosition", "opos", MFnData::kVectorArray, vectArrayDataFn.object(), &status ); // Transient output attribute. Not writable nor storable. // typedAttrFn.setWritable(false); typedAttrFn.setStorable(false); outPositionPP = typedAttrFn.object(); addAttribute( outPositionPP ); MFnNumericAttribute numAttrFn; numAttrFn.create( "particleCount", "pc", MFnNumericData::kInt ); particleCount = numAttrFn.object(); addAttribute( particleCount ); attributeAffects( computeNode, outPositionPP ); attributeAffects( particleCount, outPositionPP ); return status; } MStatus particleAttrNode::compute( const MPlug& plug, MDataBlock& block ) { MStatus status = MS::kUnknownParameter; if( (plug.attribute() == outColorPP) || (plug.attribute() == outValuePP) ) { status = compute2DTexture( plug, block ); } else if( plug.attribute() == outPositionPP ) { status = computeMesh( plug, block ); } return status; } MStatus particleAttrNode::compute2DTexture( const MPlug& plug, MDataBlock& block ) // // Description: // // This computes an outputValue/Color based on input U and V coordinates. // The code contained herein replicates the compute code of the internal Maya // 'arrayMapper' node. // // How to use: // // 1) Create a ramp // 2) Create a particleAttr node // 3) Create an emitter with a particleShape // 4) Create a per particle color attribute (rgbPP) on the particleShape via the AE. // 5) connectAttr ramp1.outColor particleAttr1.computeNode // 6) connectAttr particleShape1.age particleAttr1.vCoordPP // 7) connectAttr particleAttr1.outColorPP particleShape1.rgbPP // { MStatus status = MS::kSuccess; // // We request the computeNodeColor attributes here to make sure that // they get marked clean as the arrayMapper's output attribute(s) get // computed. // /* MDataHandle hComputeNodeColor = */ block.inputValue( computeNodeColor ); /* MDataHandle hComputeNodeColorR = */ block.inputValue( computeNodeColorR ); /* MDataHandle hComputeNodeColorG = */ block.inputValue( computeNodeColorG ); /* MDataHandle hComputeNodeColorB = */ block.inputValue( computeNodeColorB ); bool doUcoord = false; bool doVcoord = false; bool doOutColor = ( plug.attribute() == outColorPP ); bool doOutValue = ( plug.attribute() == outValuePP ); // Get pointers to the source attributes: aUCoordPP, aVCoordPP. // MFnDoubleArrayData dataDoubleArrayFn; MObject uCoordD = block.inputValue( uCoordPP ).data(); status = dataDoubleArrayFn.setObject( uCoordD ); MDoubleArray uAry; if( status == MS::kSuccess ) { uAry = dataDoubleArrayFn.array(); if (uAry.length()) { doUcoord = true; } } status = MS::kSuccess; MObject vCoordD = block.inputValue( vCoordPP ).data(); status = dataDoubleArrayFn.setObject( vCoordD ); MDoubleArray vAry; if( status == MS::kSuccess ) { vAry = dataDoubleArrayFn.array(); if (vAry.length()) { doVcoord = true; } } // Get pointers to destination attributes: outColorPP, outValuePP. // status = MS::kSuccess; MFnVectorArrayData dataVectorArrayFn; MVectorArray outColorAry; if( doOutColor ) { MObject colorD = block.outputValue( outColorPP ).data(); status = dataVectorArrayFn.setObject( colorD ); if( status == MS::kSuccess ) { outColorAry = dataVectorArrayFn.array(); } } status = MS::kSuccess; MDoubleArray outValueAry; if( doOutValue ) { MObject valueD = block.outputValue( outValuePP ).data(); status = dataDoubleArrayFn.setObject( valueD ); if( status == MS::kSuccess ) { outValueAry = dataDoubleArrayFn.array(); } } // Setup loop counter. // unsigned int uCount = ( doUcoord ? uAry.length() : 0 ); unsigned int vCount = ( doVcoord ? vAry.length() : 0 ); unsigned int count = ( (uCount) > (vCount) ? (uCount) : (vCount) ); // Resize the output arrays to match the max size // of the input arrays. // if( doOutColor ) outColorAry.setLength( count ); if( doOutValue ) outValueAry.setLength( count ); // Check for a texture node // bool hasTextureNode = MDynamicsUtil::hasValidDynamics2dTexture( thisMObject(), computeNode ); // Call the texture node to compute color values for // the input u,v coordinate arrays. // if( hasTextureNode ) { MDynamicsUtil::evalDynamics2dTexture( thisMObject(), computeNode, uAry, vAry, &outColorAry, &outValueAry ); // Scale the output value and/or color to the min/max range. // const double& minValue = block.inputValue( outMinValue ).asDouble(); const double& maxValue = block.inputValue( outMaxValue ).asDouble(); if( (minValue != 0.0) || (maxValue != 1.0) ) { double r = maxValue - minValue; if( doOutValue ) { for( unsigned int i = 0; i < count; i++ ) { outValueAry[i] = (r * outValueAry[i]) + minValue; } } if( doOutColor ) { MVector minVector( minValue, minValue, minValue ); for( unsigned int i = 0; i < count; i++ ) { outColorAry[i] = (r * outColorAry[i]) + minVector; } } } } else { // Not connected to texture node, so simply set output // arrays to default values. // for( unsigned int i = 0; i < count; i++ ) { MVector clr( 0.0, 0.0, 0.0 ); if( doOutColor ) { outColorAry[i] = clr; } if( doOutValue ) { outValueAry[i] = clr.x; } } } dataVectorArrayFn.set( outColorAry ); dataDoubleArrayFn.set( outValueAry ); return MS::kSuccess; } MStatus particleAttrNode::computeMesh( const MPlug& plug, MDataBlock& block ) // // Description: // // To illustrate a custom use of the arrayMapper node, we'll define this // alternative compute that will map particles to mesh vertex positions. If // array lengths do not match, we'll wrap around the arrays. // // How to use: // // 1) Create a poly surface. // 2) connectAttr polyShape1.outMesh particleAttr1.computeNode // 3) connectAttr particleShape1.count particleAttr1.particleCount // 4) connectAttr particleAttr1.outPosition particleShape1.rampPosition (*) // // * You may choose to drive any vector based per particle attribute. // rampPosition was selected here as an example. // { MStatus status = MS::kSuccess; // Verify that computeNode has a mesh connected to computeNode // MPlug compPlug( thisMObject(), computeNode ); MPlugArray conns; compPlug.connectedTo( conns, true, false, &status ); if( conns.length() <= 0 ) { return MS::kFailure; } MPlug conn = conns[0]; MObject compNode = conn.node(); MFnMesh meshFn( compNode, &status ); if( status != MS::kSuccess ) { return MS::kFailure; } // Retrieve the mesh vertices // MPointArray points; meshFn.getPoints( points ); unsigned int nPoints = points.length(); // Retrieve the current particle count. // // NOTE: Due to the order in which the particle system requests // various pieces of data, some attributes are requested prior // to the actual emission of particles (eg. rampPosition), whereas // other attributes are requested after particles have been emitted. // // If the driven PP attribute on the particleShape is requested prior // to the emission of particles, this compute() method will be called // before any particles have been emitted. As a result, the effect // will be lagged by one frame. // unsigned int nParticles = 0; int nSignedPart = block.inputValue( particleCount ).asInt(); if( nSignedPart > 0 ) { nParticles = nSignedPart; } // Get pointer to destination attribute: outPositionPP // MFnVectorArrayData dataVectorArrayFn; MVectorArray outPosArray; MObject posD = block.outputValue( outPositionPP ).data(); //const char* typeStr = posD.apiTypeStr(); status = dataVectorArrayFn.setObject( posD ); if( status == MS::kSuccess ) { outPosArray = dataVectorArrayFn.array(); } outPosArray.setLength( nParticles ); for( unsigned int i = 0; i < nParticles; i++ ) { unsigned int index = i; if( nParticles > nPoints ) { index = i % nPoints; } MPoint point = points[index]; MVector pos( point.x, point.y, point.z ); outPosArray[i] = pos; } dataVectorArrayFn.set( outPosArray ); return MS::kSuccess; }