// =========================================================================== // Copyright 2018 Autodesk, Inc. All rights reserved. // // Use of this software is subject to the terms of the Autodesk license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // =========================================================================== //============================================================================= // // // // // getCollisionObjects (string $particleShape) // // // string $particleShape particle shape // // // string[] : List of pathnames indexed by particle. If a // particle did not collide with a passive object // in the previous frame then the entry for that // particle will contain an empty string. // // // Returns a string array containing the pathname of the passive objects // that each particle in the given particle system collided with in // the previous frame. The particle system must have the // "collisionGeometryIndex" dynamic attribute added to it. If a // particle did not collide with any passive object in the previous // frame, then the array entry for that particle will contain an empty // string. // // // // Return list of passive collision shapes for "particleShape1" // getCollisionObjects("particleShape1"); // //============================================================================= // // Module: getCollisionObjects.mel // // Purpose: // // Tool for figuring out the exact pathname of the passive object that a // particle collided with. // // The "collisionGeometryIndex" attribute on the particle node gives the // index of the passive collision object in the particle's collision object array, // but it is more useful to know the actual pathname of the object. // That is what the getCollisionObjects() procedure provides. // // In order for this routine to work, the particle system must have // the dynamic "collisionGeometryIndex" attribute added to it. // // Details: // // The getCollisionObjects() procedure is the main procedure to call. // Given a particle system, it returns a string array containing the // full pathnames of the objects that each particle collided with in the // previous frame. If a particle did not collide with an object, then // the array entry for that particle will contain the empty string. // // The getCollisionObjectPath() procedure is a utility procedure that // determines the pathname of a particular collision object connected // to a particular particle system. Since a full pathname is provided, // this routine will differentiate between different instances of the // same shape. // //============================================================================= global proc string[] getCollisionObjects( string $particle ) // // Description: // // Returns a string array containing the pathname of the objects // that each particle in the given particle system collided with in // the previous frame. The particle system must have the // "collisionGeometryIndex" dynamic attribute added to it in order // for this routine to work. If a particle did not collide with any // object in the previous frame, then the array entry for that // particle will be the empty string. // { // make sure collisionGeometryIndex attribute exists // if( !`attributeQuery -ex -n $particle "collisionGeometryIndex"` ) { string $fmt = (uiRes("m_getCollisionObjects.kParticleMissingAttribute")); error( `format -s $particle $fmt` ); } // stores the pathnames of the collision objects connected to the // particle system. These are computed on the fly as they are needed. // string $connectedCollisionObjects[] = {}; // collisionGeometryIndex attribute gives the indices in the collision // object array of the object with which each particle collided in the // previous frame. // float $collisionIndices[] = `getAttr ($particle+".collisionGeometryIndex")`; // result array that stores the collision object names for each particle // string $collisionObjects[] = {}; // loop through each particle, and figure out the name of the object that // it collided with // int $i; for( $i = 0; $i < size($collisionIndices); $i++ ) { int $collisionObjIndex = $collisionIndices[$i]; if( $collisionObjIndex == -1 ) { // particle didn't collide with anything // $collisionObjects[$i] = ""; } else { // particle collided with an object. If we haven't already // figured out the name of this object, then compute it now // and store it in the $connectedCollisionObjects[] array. // if( $connectedCollisionObjects[$collisionObjIndex] == "" ) { // compute the name // $connectedCollisionObjects[$collisionObjIndex] = getCollisionObjectPath( $particle, $collisionObjIndex, false/*active collision*/ ); } // fill in the result array entry for this particle with the name // of the collision object // $collisionObjects[$i] = $connectedCollisionObjects[$collisionObjIndex]; } } return $collisionObjects; } global proc string getCollisionObjectPath( string $particle, int $objectIndex, int $activeCollision ) // // Description: // // Figures out the full pathname to a particular collision object connected // to the given particle system. "objectIndex" specifies the index of // the collision object of interest in the particle system's collision input // array. // // 1) Classic Particles // // For classic particles each element on the collisionGeometry array will // have a geoConnector feeding into it, and a piece of geometry feeding // into the geoConnector. It is the name of the geometry feeding into // the geoConnector that we are after. // // The geoConnector actually receives two connections from the shape: // // 1) the shape's output geometry feeds into the geoConnector's // "localGeometry" attribute. This is supposed to specify // the collision geometry in the object space of the collision // object. // // 2) the object->world matrix of the collision object is // connected to the geoConnector's "worldMatrix" attribute. // This connection will look something like: // // shape.worldMatrix[0] -> geoConnector.worldMatrix // // This means that the geoConnector receives the object->world // matrix for the first instance of the shape. If the connection // looked like: // // shape.worldMatrix[1] -> geoConnector.worldMatrix // // then the geoConnector would be getting the matrix for the // second instance of the shape. // // In practice, the matrix connection is enough to tell us which instance // of which shape is being represented by a given geoConnector. When // you ask Maya to list connections to the matrix attribute of the // geoConnector, it actually gives you the pathname that the connection // represents. This is how we figure out the pathname of the collision // object. // // 2) nParticles // // For nParticles we folow the currentState connection to the nucleus // node. // // The collision objects are connected to a nShape node which is in turn // connected to the passive (or active) input on the nucleus node. // { // get the name of the specified collision geometry geoConnector // string $invalidIndexError = (uiRes("m_getCollisionObjects.kInvalidIndex")); string $invalidGeomError = (uiRes("m_getCollisionObjects.kInvalidGeometry")); string $nodeType = `objectType $particle`; // Try classic particles first if ($nodeType == "particle") { if ($activeCollision) return ""; // Classic particles do not have active collisions // Classic particles string $geoConnectors[] = `listConnections ($particle+".collisionGeometry[" + $objectIndex + "]")`; if( size($geoConnectors) != 1 ) error( `format -s $particle $invalidIndexError` ); // list connections to the geoConnector. We want to make sure that // a shape and a matrix are connected. Specifying "-sh on" means // that the result will be a unique pathname to the actual instance // represented by the connection. // string $geoConn = $geoConnectors[0]; string $shapeConnections[] = `listConnections -sh on ($geoConn+".localGeometry")`; string $matrixConnections[] = `listConnections -sh on ($geoConn+".worldMatrix")`; if( (size($shapeConnections) != 1) || (size($matrixConnections) != 1) ) error( `format -s $objectIndex -s $particle $invalidGeomError` ); // we don't really need the shape //string $shape = $shapeConnections[0]; // this will be a shortest unique pathname to the collision shape // string $matrix = $matrixConnections[0]; // expand it to a full pathname and return it. // string $full[] = `ls -l $matrix`; return $full[0]; } // nParticles string $currentStateOutputs[] = `listConnections -sh on ($particle+".currentState")`; if( size($currentStateOutputs) != 1 ) error( `format -s $particle $invalidIndexError` ); // Follow the appropriate nShape input on the nucleus node string $nucleus = $currentStateOutputs[0]; string $collisionType = $activeCollision ? ".inputActive" : ".inputPassive"; string $collisionInputs[] = `listConnections -sh on ($nucleus + $collisionType + "[" + $objectIndex + "]")`; if( size($collisionInputs) != 1 ) error( `format -s $objectIndex -s $particle $invalidGeomError` ); string $nShape = $collisionInputs[0]; string $nShapeType = `objectType $nShape`; // If nParticleShape we are done if ($nShapeType == "nParticle") { string $full[] = `ls -l $nShape`; return $full[0]; } // Otherwise, follow the nShape connection back to the input mesh string $meshInputs[] = `listConnections -sh on ($nShape+".inputMesh")`; if( size($meshInputs) != 1 ) error( `format -s $objectIndex -s $particle $invalidGeomError` ); // Expand mesh shape to a full pathname and return it. string $shape = $meshInputs[0]; string $full[] = `ls -l $shape`; return $full[0]; }