Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

510 lines
17 KiB
C++

//-*****************************************************************************
//
// Copyright (c) 2009-2011,
// Sony Pictures Imageworks, Inc. and
// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Sony Pictures Imageworks, nor
// Industrial Light & Magic nor the names of their contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//-*****************************************************************************
#include "util.h"
#include "PointHelper.h"
#include "NodeIteratorVisitorHelper.h"
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MVector.h>
#include <maya/MGlobal.h>
#include <maya/MVectorArray.h>
#include <maya/MSelectionList.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnParticleSystem.h>
#include <maya/MDagModifier.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MFnSet.h>
MStatus getPointArbGeomParamsInfos( const Alembic::AbcGeom::IPoints & iNode, MObject & iObject,
PointSampleDataList & iData )
{
MStatus status( MS::kSuccess);
Alembic::AbcGeom::IPointsSchema schema = iNode.getSchema();
Alembic::AbcGeom::IPointsSchema::Sample samp;
Alembic::Abc::ICompoundProperty props = schema.getArbGeomParams();
if ( !props.valid() )
{
return status;
}
size_t numProps = props.getNumProperties();
MFnTypedAttribute tAttr;
MObject attrObj;
for (unsigned int i = 0; i < numProps; ++ i)
{
const Alembic::Abc::PropertyHeader & propHeader =
props.getPropertyHeader(i);
const std::string propName = propHeader.getName();
int propExtent = propHeader.getDataType().getExtent();
Alembic::Abc::IArrayProperty arrayProp( props, propName );
// Get geometry scope
Alembic::AbcGeom::GeometryScope propScope = Alembic::AbcGeom::GetGeometryScope(arrayProp.getMetaData());
Alembic::AbcCoreAbstract::DataType dtype = arrayProp.getDataType();
Alembic::Util::PlainOldDataType pod = dtype.getPod();
// Ignore all but numerical
switch (pod)
{
case Alembic::Util::kBooleanPOD:
case Alembic::Util::kUint8POD:
case Alembic::Util::kInt8POD:
case Alembic::Util::kInt16POD:
case Alembic::Util::kInt32POD:
case Alembic::Util::kInt64POD:
case Alembic::Util::kFloat16POD:
case Alembic::Util::kFloat32POD:
case Alembic::Util::kFloat64POD:
{
break;
}
default:
{
continue;
}
}
Alembic::AbcCoreAbstract::ArraySamplePtr samp;
// Storing property information for fast lookup when reading each frame
PointsSampleData sampleInfo;
sampleInfo.origName = propName;
sampleInfo.scope = propScope;
sampleInfo.name = propName;
sampleInfo.extent = propExtent;
sampleInfo.arrayProp = arrayProp;
MFnDependencyNode fnparticle(iObject);
if ( fnparticle.hasAttribute( sampleInfo.name.c_str() ) )
{
MPlug attrPlug = fnparticle.findPlug(sampleInfo.name.c_str(), true);
MFnAttribute attr( attrPlug.attribute(&status));
if ( sampleInfo.extent == 1 && attr.accepts(MFnData::kDoubleArray, &status))
{
// We can skip attribute creation
iData.push_back( sampleInfo );
continue;
}
else if (attr.accepts(MFnData::kVectorArray, &status))
{
// We can skip attribute creation
iData.push_back( sampleInfo );
continue;
}
else
{
// Attribute exists but is of the wrong type, we need to rename it
std::string abcPrefix("abc_");
sampleInfo.name = abcPrefix + sampleInfo.name;
}
}
iData.push_back( sampleInfo );
}
return status;
}
MStatus readArbGeomParams(size_t pSize, Alembic::AbcCoreAbstract::index_t index,
MFnArrayAttrsData & dynDataFn, PointSampleDataList & iData)
{
MStatus status(MS::kSuccess);
size_t numProps = iData.size();
if ( iData.empty() || pSize == 0 )
{
// iData is empty, feeding empty array");
return status;
}
for (unsigned int i = 0; i < numProps; ++ i)
{
PointsSampleData sampleInfo = iData[ i ];
Alembic::Util::Dimensions dim;
sampleInfo.arrayProp.getDimensions(dim, Alembic::Abc::ISampleSelector(index));
unsigned int sampSize( dim.numPoints() );
if ( sampleInfo.scope == Alembic::AbcGeom::kVaryingScope && sampSize != pSize)
{
MString warn = "Ignoring malformed kVarying attribute: ";
warn += sampleInfo.name.c_str();
warn += ", the number of point in the sample doesn't match the number of particle";
MGlobal::displayWarning(warn);
continue;
}
// We don't forget the extent when alocating the array
std::vector< double > samp(pSize * sampleInfo.extent);
// Read everything as double
sampleInfo.arrayProp.getAs( &samp.front(), Alembic::Util::kFloat64POD, Alembic::Abc::ISampleSelector(index));
// 1d array
if ( sampleInfo.extent == 1 )
{
MDoubleArray doubleArray = dynDataFn.doubleArray( sampleInfo.name.c_str(), &status);
doubleArray.setLength( pSize );
if ( sampleInfo.scope <= Alembic::AbcGeom::kUniformScope)
{
for (unsigned int i = 0; i < pSize; ++i )
{
doubleArray[i] = samp[0];
}
}
else
{
for (unsigned int i = 0; i < pSize; ++i )
{
doubleArray[i] = samp[i];
}
}
continue;
}
else // 2d or 3d array
{
MVectorArray arr = dynDataFn.vectorArray( sampleInfo.name.c_str(), &status);
arr.setLength(pSize);
MVector vec;
if ( sampleInfo.scope <= Alembic::AbcGeom::kUniformScope)
{
for (unsigned int i = 0; i < pSize; ++i)
{
vec.x = samp[0];
vec.y = samp[1];
if (sampleInfo.extent == 3)
{
vec.z = samp[2];
}
arr[i] = vec;
}
}
else
{
for (unsigned int i = 0; i < pSize; ++i)
{
vec.x = samp[sampleInfo.extent*i];
vec.y = samp[sampleInfo.extent*i+1];
if (sampleInfo.extent == 3)
{
vec.z = samp[sampleInfo.extent*i+2];
}
arr[i] = vec;
}
}
continue;
}
}
return status;
}
MStatus createPerParticleAttributes( const Alembic::AbcGeom::IPoints & iNode, MObject & iObject )
{
// We need to create a custom attribute for each abcGeomParam found under the PointSchema
// nParticle object can only deal with doubleArray or vectorArray, so the only thing we need to know is the extent
// But because sometimes the writer does not specify it explicitly, we need to investigate the header
// we compare the sample position size to any geomParam sample size, if it is equal to twice or three time the position size,
// we assume having an extent of 2 or 3
// Initialise maya object
MStatus status;
MFnTypedAttribute tAttr;
MDagModifier modifier;
MObject attrObj;
// Handle radius
// IF we have information for the radius, set the shading to blobby surface
Alembic::AbcGeom::IFloatGeomParam widthProp = iNode.getSchema().getWidthsParam();
if ( widthProp.valid() )
{
attrObj = tAttr.create("radiusPP", "radiusPP", MFnData::kDoubleArray,
MObject::kNullObj, &status);
MCHECKERROR(status);
status = modifier.addAttribute(iObject, attrObj);
status = modifier.doIt();
MCHECKERROR(status);
// particleRenderType is an enum, 7 is for blobby surface
MPlug renderTypePlug = MFnDependencyNode(iObject).findPlug("particleRenderType", true);
status = modifier.newPlugValueInt(renderTypePlug, 7);
status = modifier.doIt();
MCHECKERROR(status);
}
PointSampleDataList iData;
status = getPointArbGeomParamsInfos(iNode, iObject, iData);
size_t numValidProps = iData.size();
if ( numValidProps == 0 )
{
// No param found, skipping
return status;
}
for (unsigned int i = 0; i < numValidProps; ++ i)
{
PointsSampleData & sampleInfo = iData[ i ];
if ( MFnDependencyNode(iObject).hasAttribute(sampleInfo.name.c_str() ))
{
continue;
}
if (sampleInfo.extent == 1)
{
attrObj = tAttr.create(sampleInfo.name.c_str(), sampleInfo.name.c_str(),
MFnData::kDoubleArray, MObject::kNullObj, &status);
MCHECKERROR(status);
status = modifier.addAttribute(iObject, attrObj);
MCHECKERROR(status);
}
else
{
attrObj = tAttr.create(sampleInfo.name.c_str(), sampleInfo.name.c_str(),
MFnData::kVectorArray, MObject::kNullObj, &status);
MCHECKERROR(status);
status = modifier.addAttribute(iObject, attrObj);
MCHECKERROR(status);
}
}
return modifier.doIt();
}
MStatus read(double iFrame, const Alembic::AbcGeom::IPoints & iNode, int & isInitializedConstant,
MFnArrayAttrsData & dynDataFn, PointSampleDataList & iData)
{
// We feed the MFnArrayAttrsData with all the nescessary doubleArray and vectorArray
// it is then used to feed the outDataPlug of the alembic node
MStatus status = MS::kSuccess;
Alembic::AbcGeom::IPointsSchema schema = iNode.getSchema();
// Get IPoints Samples
Alembic::AbcGeom::IPointsSchema::Sample samp, ceilsSamp;
Alembic::AbcCoreAbstract::index_t index, ceilIndex;
// TODO, might want to interpolate the points when we can
getWeightAndIndex(iFrame, schema.getTimeSampling(),
schema.getNumSamples(), index, ceilIndex);
schema.get(samp, index);
size_t pSize = samp.getPositions()->size();
//size_t idSize = samp.getIds()->size();
MDoubleArray countArray = dynDataFn.doubleArray("count", &status);
countArray.append( (double)pSize );
// ID
Alembic::Abc::UInt64ArraySamplePtr idPtr = samp.getIds();
MDoubleArray idArray = dynDataFn.doubleArray("id", &status);
idArray.setLength( pSize );
// POSITION
Alembic::Abc::P3fArraySamplePtr p3ptr = samp.getPositions();
MVectorArray positionArray = dynDataFn.vectorArray("position", &status);
positionArray.setLength( pSize );
// VELOCITY
Alembic::Abc::V3fArraySamplePtr v3fptr= samp.getVelocities();
MVectorArray velocityArray = dynDataFn.vectorArray("velocity", &status);
velocityArray.setLength( pSize );
// RADIUS
Alembic::AbcGeom::IFloatGeomParam widthProp = schema.getWidthsParam();
Alembic::Abc::FloatArraySamplePtr fptr;
MDoubleArray radiusArray;
Alembic::AbcGeom::GeometryScope widthScope( Alembic::AbcGeom::kUnknownScope );
if ( widthProp.valid() )
{
// TODO, shouldn't width be 2*radius ?
Alembic::AbcGeom::IFloatGeomParam::Sample widthSamp;
widthProp.getExpanded(widthSamp, index);
radiusArray = dynDataFn.doubleArray("radiusPP", &status);
radiusArray.setLength(pSize);
fptr = widthSamp.getVals();
widthScope = widthSamp.getScope();
}
MVector vec;
for (unsigned int i = 0; i < pSize; ++i )
{
idArray[i] = (*idPtr)[i];
vec.x = (*p3ptr)[i].x;
vec.y = (*p3ptr)[i].y;
vec.z = (*p3ptr)[i].z;
positionArray[i] = vec;
if ( v3fptr )
{
vec.x = (*v3fptr)[i].x;
vec.y = (*v3fptr)[i].y;
vec.z = (*v3fptr)[i].z;
velocityArray[i] = vec;
}
if ( widthProp.valid() )
{
if ( widthScope < Alembic::AbcGeom::kVaryingScope )
{
// Special Case, we got only one value, populate all particles with this value
radiusArray[i] = (*fptr)[0];
}
else
{
radiusArray[i] = (*fptr)[i];
}
}
}
if ( schema.isConstant() )
{
isInitializedConstant = true;
}
Alembic::Abc::ICompoundProperty props = schema.getArbGeomParams();
if ( !props.valid() )
{
return status;
}
status = readArbGeomParams( pSize, index, dynDataFn, iData );
MCHECKERROR(status);
return status;
}
MStatus create(double iFrame, const Alembic::AbcGeom::IPoints & iNode,
MObject & iParent, MObject & iObject)
{
MFnTypedAttribute tAttr;
MObject attrObj;
MStatus status = MS::kSuccess;
Alembic::AbcGeom::IPointsSchema schema = iNode.getSchema();
// object has no samples, bail early
if (schema.getNumSamples() == 0)
{
return status;
}
MDagModifier modifier;
iObject = modifier.createNode("nParticle", iParent, &status);
MCHECKERROR(status);
status = modifier.renameNode(iObject, iNode.getName().c_str());
status = modifier.doIt();
// To call the DG evaluation, the nParticle needs to be attached to a nucleus node
MItDependencyNodes dgIt( MFn::kNucleus );
MObject obj;
MFnDagNode nParticleFn( iObject );
// Look for a nucleus node
while ( !dgIt.isDone() )
{
obj = dgIt.thisNode();
break;
}
MString nSolver("\"\"");
if (!obj.isNull() && obj.hasFn( MFn::kNucleus))
{
MFnDependencyNode nucleusFn(obj);
nSolver = nucleusFn.name();
}
// I found no way to do this from the API, so we call a dirty simple mel command that need to have an active selection
MString cmd;
cmd += "select ";
cmd += nParticleFn.fullPathName();
cmd += ";\n";
cmd += "assignNSolver ";
cmd += nSolver;
cmd += ";\n";
cmd += "select -clear;";
// If there is no nSolver in the scene, the nSolver string will be empty and it will trigger the creation
// of a new one
MGlobal::executeCommand( cmd, true, false );
MCHECKERROR(status);
// Set attribute "isDynamic" to off, it could crash if maya tries to compute collision
// against another nParticleShape
// It is not related to alembic. It crashes also with nCached particle collision
// In maya Attribute Editor, the attribute is called "enable"
MPlug enablePlug = nParticleFn.findPlug("isDynamic", true);
status = modifier.newPlugValueBool(enablePlug, false);
// Assign default particle shader initialParticleSE to correctly display them in the viewport
MSelectionList sel;
sel.add( "initialParticleSE" );
MObject particleSG;
sel.getDependNode(0, particleSG);
MFnSet fnSG( particleSG );
fnSG.addMember(iObject);
status = createPerParticleAttributes(iNode, iObject);
return status;
}