//- // ========================================================================== // 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. // // ========================================================================== //+ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MTypeId ownerEmitter::id( 0x80015 ); ownerEmitter::ownerEmitter() : lastWorldPoint(0, 0, 0, 1) { } ownerEmitter::~ownerEmitter() { } void *ownerEmitter::creator() { return new ownerEmitter; } MStatus ownerEmitter::initialize() // // Descriptions: // Initialize the node, create user defined attributes. // { return( MS::kSuccess ); } MStatus ownerEmitter::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"); // Create the data and apply the function set, // particle array initialized to length zero, // fnOutput.clear() // 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 input position and velocity arrays where new particles are from, // also known as the owner. An owner is determined if connections exist // to the emitter node from a shape such as nurbs, polymesh, curve, // or a lattice shape. // // Check positions from an owner, if an owner exists. // MVectorArray inPosAry; inPosAry.clear(); ownerPosition( block, inPosAry ); // Check velocities from an owner, if an owner exists. // MVectorArray inVelAry; inVelAry.clear(); ownerVelocity( multiIndex, block, inVelAry ); // inPosAry and inVelAry should have the same length. // If not, return a failure. // unsigned inPosLength = inPosAry.length(); if( (inPosLength == 0) || (inPosLength != inVelAry.length()) ) { // something is wrong, these two arrays should always // be the same length. // return( MS::kFailure ); } // 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 ); } // Compute and store the emission count per-point. // MIntArray emitCountPP; emitCountPP.clear(); status = emitCountPerPoint( plug, block, inPosLength, emitCountPP ); // 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 ); MVector rotatedV = useRotation ( dirV ); // Start emitting particles. // 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 ownerEmitter::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 ); } } } void ownerEmitter::ownerPosition ( MDataBlock& block, MVectorArray &ownerPosArray ) // // Descriptions: // If this emitter has an owner, get the owner's position array or // centroid, then assign it to the ownerPosArray. // If it does not have owner, get the emitter position in the world // space, and assign it to the given array, ownerPosArray. // { MStatus status; bool hasOwner = false; 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 ++ ) ownerPosArray.append( posArray[i] ); // Got position array from owner, turn hasOwnerPos on. // hasOwner = true; } } // If there is not an owner, get the emitter position // in the world space and add it into ownerPosArray. // if( !hasOwner ) { MPoint worldPos(0.0, 0.0, 0.0); status = getWorldPosition( worldPos ); MVector worldV; worldV[0] = worldPos[0]; worldV[1] = worldPos[1]; worldV[2] = worldPos[2]; ownerPosArray.append( worldV ); } } void ownerEmitter::ownerVelocity ( int plugIndex, MDataBlock& block, MVectorArray &ownerVelArray ) // // Descriptions: // If this emitter has an owner, get the owner's velocity array // then assign it to the ownerVelArray. If it does not have owner, // get the current emitter position, compute velocity vector, modify // lastWorldPoint, and assign it to the given array, ownerVelArray. // { MStatus status; bool hasOwner = false; MDataHandle hOwnerVel = block.inputValue( mOwnerVelData, &status ); if( status == MS::kSuccess ) { MObject dOwnerVel = hOwnerVel.data(); MFnVectorArrayData fnOwnerVel( dOwnerVel ); MVectorArray velArray = fnOwnerVel.array( &status ); if( status == MS::kSuccess ) { // assign vectors from block to ownerPosArray. // for( unsigned int i = 0; i < velArray.length(); i ++ ) ownerVelArray.append( velArray[i] ); // Got position array from owner, turn hasOwnerPos on. // hasOwner = true; } } // If there is not an owner, get the emitter position // in the world space and calcuate velocity and add it // into ownerVelArray. if( !hasOwner ) { MVector velV(0.0, 0.0, 0.0); MPoint worldPos(0.0, 0.0, 0.0); status = getWorldPosition( worldPos ); MTime dT = deltaTimeValue( plugIndex, block ); double dt = dT.as( MTime::kSeconds ); if( dt > 0.0 ) velV = (worldPos - lastWorldPoint) / dt; ownerVelArray.append( velV ); lastWorldPoint = worldPos; } } MStatus ownerEmitter::getWorldPosition( MPoint &point ) // // Descriptions: // get the emitter position in the world space. // The position value is from inherited attribute, aWorldMatrix. // { MStatus status; MObject thisNode = thisMObject(); MFnDependencyNode fnThisNode( thisNode ); // get worldMatrix attribute. // MObject worldMatrixAttr = fnThisNode.attribute( "worldMatrix" ); // build worldMatrix plug, and specify which element the plug refers to. // We use the first element(the first dagPath of this emitter). // MPlug matrixPlug( thisNode, worldMatrixAttr ); matrixPlug = matrixPlug.elementByLogicalIndex( 0 ); // Get the value of the 'worldMatrix' attribute // MObject matrixObject; status = matrixPlug.getValue( matrixObject ); if( !status ) { status.perror("ownerEmitter::getWorldPosition: get matrixObject"); return( status ); } MFnMatrixData worldMatrixData( matrixObject, &status ); if( !status ) { status.perror("ownerEmitter::getWorldPosition: get worldMatrixData"); return( status ); } MMatrix worldMatrix = worldMatrixData.matrix( &status ); if( !status ) { status.perror("ownerEmitter::getWorldPosition: get worldMatrix"); return( status ); } // assign the translate to the given vector. // point[0] = worldMatrix( 3, 0 ); point[1] = worldMatrix( 3, 1 ); point[2] = worldMatrix( 3, 2 ); return( status ); } MStatus ownerEmitter::getWorldPosition( MDataBlock& block, MPoint &point ) // // Descriptions: // Find the emitter position in the world space. // { MStatus status; MObject thisNode = thisMObject(); MFnDependencyNode fnThisNode( thisNode ); // get worldMatrix attribute. // MObject worldMatrixAttr = fnThisNode.attribute( "worldMatrix" ); // build worldMatrix plug, and specify which element the plug refers to. // We use the first element(the first dagPath of this emitter). // MPlug matrixPlug( thisNode, worldMatrixAttr ); matrixPlug = matrixPlug.elementByLogicalIndex( 0 ); MDataHandle hWMatrix = block.inputValue( matrixPlug, &status ); McheckErr(status, "ERROR getting hWMatrix from dataBlock.\n"); if( status == MS::kSuccess ) { MMatrix wMatrix = hWMatrix.asMatrix(); point[0] = wMatrix(3, 0); point[1] = wMatrix(3, 1); point[2] = wMatrix(3, 2); } return( status ); } MVector ownerEmitter::useRotation ( MVector &direction ) { MStatus status; MVector rotatedVector; MObject thisNode = thisMObject(); MFnDependencyNode fnThisNode( thisNode ); // get worldMatrix attribute. // MObject worldMatrixAttr = fnThisNode.attribute( "worldMatrix" ); // build worldMatrix plug, and specify which element the plug refers to. // We use the first element(the first dagPath of this emitter). // MPlug matrixPlug( thisNode, worldMatrixAttr ); matrixPlug = matrixPlug.elementByLogicalIndex( 0 ); // Get the value of the 'worldMatrix' attribute // MObject matrixObject; status = matrixPlug.getValue( matrixObject ); if( !status ) { status.perror("ownerEmitter::getWorldPosition: get matrixObject"); return ( direction ); } MFnMatrixData worldMatrixData( matrixObject, &status ); if( !status ) { status.perror("ownerEmitter::getWorldPosition: get worldMatrixData"); return( direction ); } MMatrix worldMatrix = worldMatrixData.matrix( &status ); if( !status ) { status.perror("ownerEmitter::getWorldPosition: get worldMatrix"); return( direction ); } rotatedVector = direction * worldMatrix; return( rotatedVector ); } MStatus ownerEmitter::emitCountPerPoint ( const MPlug &plug, MDataBlock &block, int length, // length of emitCountPP MIntArray &emitCountPP // output: emitCount for each point ) // // Descriptions: // Compute emitCount for each point where new particles come from. // { MStatus status; int plugIndex = plug.logicalIndex( &status ); McheckErr(status, "ERROR in emitCountPerPoint: when plug.logicalIndex.\n"); // Get rate and delta time. // double rate = rateValue( block ); MTime dt = deltaTimeValue( plugIndex, block ); // Compute emitCount for each point. // double dblCount = rate * dt.as( MTime::kSeconds ); int intCount = (int)dblCount; for( int i = 0; i < length; i++ ) { emitCountPP.append( intCount ); } return( MS::kSuccess ); } MStatus initializePlugin(MObject obj) { MStatus status; MFnPlugin plugin(obj, PLUGIN_COMPANY, "3.0", "Any"); status = plugin.registerNode( "ownerEmitter", ownerEmitter::id, &ownerEmitter::creator, &ownerEmitter::initialize, MPxNode::kEmitterNode ); if (!status) { status.perror("registerNode"); return status; } return status; } MStatus uninitializePlugin(MObject obj) { MStatus status; MFnPlugin plugin(obj); status = plugin.deregisterNode( ownerEmitter::id ); if (!status) { status.perror("deregisterNode"); return status; } return status; }