#include <maya/MPxNode.h>
#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MFnPlugin.h>
#include <maya/MAngle.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFloatPoint.h>
#include <maya/MFloatPointArray.h>
#include <maya/MIntArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>
#include <maya/MItMeshVertex.h>
#include <math.h>
#include <maya/MIOStream.h>
#define Rad(x) ((x)*FPI/180.0F)
#define Deg(x) ((x)*180.0F/FPI)
#define FPI 3.14159265358979323846264338327950288419716939937510582F
#define McheckErr(stat,msg) \
if ( MS::kSuccess != stat ) { \
cerr << msg; \
return MS::kFailure; \
}
class shellNode : public MPxNode
{
public:
shellNode();
virtual ~shellNode();
virtual MStatus compute( const MPlug& plug, MDataBlock& data );
static void * creator();
static MStatus initialize();
public:
static MTypeId id;
static MObject alpha;
static MObject beta;
static MObject phi;
static MObject my;
static MObject omega;
static MObject omin;
static MObject omax;
static MObject od;
static MObject smin;
static MObject smax;
static MObject sd;
static MObject A;
static MObject a;
static MObject b;
static MObject scale;
static MObject P;
static MObject L;
static MObject N;
static MObject W1;
static MObject W2;
static MObject nstart;
static MObject P2;
static MObject L2;
static MObject N2;
static MObject W12;
static MObject W22;
static MObject off2;
static MObject nstart2;
static MObject P3;
static MObject L3;
static MObject N3;
static MObject W13;
static MObject W23;
static MObject off3;
static MObject nstart3;
static MObject uamp;
static MObject ufreq;
static MObject urib;
static MObject vamp;
static MObject vfreq;
static MObject vrib;
static MObject outMesh;
private:
struct ShellParams {
float alpha;
float beta;
float phi;
float my;
float omega;
float omin;
float omax;
float od;
float smin;
float smax;
float sd;
float A;
float a;
float b;
float scale;
float P;
float L;
float N;
float W1;
float W2;
float nstart;
float P2;
float L2;
float N2;
float W12;
float W22;
float off2;
float nstart2;
float P3;
float L3;
float N3;
float W13;
float W23;
float off3;
float nstart3;
float uamp;
float ufreq;
float urib;
float vamp;
float vfreq;
float vrib;
};
ShellParams shellParams;
bool redoTopology;
bool rebuild;
int ni;
int nj;
float **pnts;
private:
float GetFloatParameter( MObject node, MObject attr );
float GetAngleParameter( MObject node, MObject attr );
static void addFloatParameter( MObject & attr, MString longName,
MString briefName, float attrDefault );
static void addAngleParameter( MObject & attr, MString longName,
MString briefName, float attrDefault );
void UpdateParameters();
void RedoTopology();
void Rebuild();
float Nodules( float s, float o );
float Ribs( float u, float v );
void Eval( float *p, float o, float s );
};
MTypeId shellNode::id( 0x8000b );
shellNode::shellNode()
: rebuild( true ),
redoTopology( true ),
pnts( NULL ),
ni( 0 ),
nj( 0 )
{}
shellNode::~shellNode() {}
MStatus shellNode::compute( const MPlug& plug, MDataBlock& data )
{
MStatus returnStatus;
int i, j;
UpdateParameters();
bool createNewMesh = redoTopology;
RedoTopology();
Rebuild();
if( !pnts || ni<2 || nj<2 ) return MS::kSuccess;
if (plug == outMesh) {
if( !pnts || ni<2 || nj<2 ) return MS::kSuccess;
MDataHandle outputHandle = data.outputValue(outMesh, &returnStatus);
McheckErr(returnStatus, "ERROR getting polygon data handle\n");
MObject mesh = outputHandle.asMesh();
if ( createNewMesh || mesh.isNull() ) {
MFnMeshData dataCreator;
MObject newOutputData = dataCreator.create(&returnStatus);
McheckErr(returnStatus, "ERROR creating outputData");
MFloatPointArray vertices;
for( j=0; j<nj; ++j ) {
for( i=0; i<ni; ++i ) {
const float *p= pnts[j] + 3*i;
vertices.append( MFloatPoint(p[0],p[1],p[2]) );
}
}
MIntArray pcounts;
i= (nj-1)*(ni-1);
while( i-- ) {
pcounts.append(4);
}
MIntArray pconnect;
for( j=0; j<nj-1; ++j ) {
for( i=0; i<ni-1; ++i ) {
int corner= i+j*ni;
pconnect.append(corner);
pconnect.append(corner+1);
pconnect.append(corner+1+ni);
pconnect.append(corner+ni);
}
}
MIntArray fec;
MDoubleArray sp;
MDoubleArray tp;
MFnMesh meshFn;
mesh= meshFn.create(
nj*ni,
(nj-1)*(ni-1),
vertices,
pcounts,
pconnect,
newOutputData,
&returnStatus
);
outputHandle.set(newOutputData);
} else {
MItMeshVertex vertIt( mesh, &returnStatus);
McheckErr(returnStatus, "ERROR creating iterator\n");
for( j=0; j<nj; ++j ) {
for( i=0; i<ni; ++i ) {
if ( vertIt.isDone() ) break;
const float *p= pnts[j] + 3*i;
vertIt.setPosition( MPoint(p[0],p[1],p[2]) );
vertIt.next();
}
if ( vertIt.isDone() ) break;
}
}
data.setClean( plug );
}
return MS::kSuccess;
}
void* shellNode::creator()
{
return new shellNode();
}
void shellNode::RedoTopology()
{
if( !redoTopology ) return;
redoTopology = false;
int oldnj= pnts ? nj : 0;
ni= 0;
nj= 0;
for( float s = shellParams.smin;
s < shellParams.smax;
s += shellParams.sd )
{
ni++;
}
for( float o = shellParams.omin;
o < shellParams.omax;
o += shellParams.od )
{
nj++;
}
if( nj<oldnj ) {
for( int i = nj; i < oldnj; ++i ) {
if( pnts[i] ) free( pnts[i] );
}
}
if( nj!=oldnj) {
pnts = (float**) realloc(pnts, nj*sizeof(float*));
}
if( nj>oldnj ) {
memset( pnts+oldnj, 0, (nj-oldnj)*sizeof(float*) );
}
for(int j=0;j<nj;++j) {
pnts[j] = (float*) realloc(pnts[j],3*ni*sizeof(float));
}
}
void shellNode::Rebuild()
{
if( !rebuild ) return;
rebuild= 0;
int i;
int j;
float s;
float o;
float *p;
o= shellParams.omin;
for( j=0; j<nj; ++j )
{
s= shellParams.smin;
p= pnts[j];
for( i=0; i<ni; ++i )
{
Eval( p, o, s );
s+=shellParams.sd;
p+=3;
}
o+=shellParams.od;
}
}
inline float SafeCot( float x )
{
float s= sinf(x);
return s ? cosf(x)/s : 0.0F;
}
inline float G( float a, float n )
{
if( !n ) return n;
float z= 2.0F*FPI;
a*=n/z;
return z/n*(a-floorf(0.5F+a));
}
float shellNode::Ribs( float u, float v )
{
ShellParams & sp = shellParams;
float zu=0.0F;
if( sp.uamp )
{
zu= sp.uamp*cosf(2.0F*FPI*sp.ufreq*u);
if( zu<0 ) zu*= (1.0F-2.0F*sp.urib);
}
float zv=0.0F;
if( sp.vamp )
{
zv= sp.vamp*cosf(2.0F*FPI*sp.vfreq*v);
if( zv<0 ) zv*= (1.0F-2.0F*sp.vrib);
}
return zu+zv;
}
float shellNode::Nodules( float s, float o )
{
ShellParams & sp = shellParams;
float p1;
float p2;
float k=0.0F;
float g=0.0F;
if( sp.L && sp.N && o>=sp.nstart )
{
g= G(o,sp.N);
p1= g/sp.W2;
p2= (s-sp.P)/sp.W1;
k= sp.L*expf( -4.0F*(p1*p1+p2*p2) );
}
if( sp.L2 && sp.N2 && o>=sp.nstart2 )
{
g= G(o+sp.off2,sp.N2);
p1= g/sp.W22;
p2= (s-sp.P2)/sp.W12;
k+= sp.L2*expf( -4.0F*(p1*p1+p2*p2) );
}
if( sp.L3 && sp.N3 && o>= sp.nstart3 )
{
g= G(o+sp.off3, sp.N3);
p1= g/sp.W23;
p2= (s-sp.P3)/sp.W13;
k+= sp.L3*expf( -4.0F*(p1*p1+p2*p2) );
}
return k;
}
void shellNode::Eval( float *p, float o, float s )
{
ShellParams & sp = shellParams;
float ss = sinf(s);
float cs = cosf(s);
float re = 1.0F/sqrtf(cs*cs/(sp.a*sp.a)
+ ss*ss/(sp.b*sp.b));
float sc = sp.scale*expf(o*SafeCot(sp.alpha));
float csphi = cosf(s+sp.phi);
float ssphi = sinf(s+sp.phi);
float sbeta = sinf(sp.beta);
float smy = sinf(sp.my);
float r = re+Nodules(s,o)+Ribs(s,o);
float x = sp.A*sbeta*cosf(o)
+ r*csphi*cosf(o+sp.omega)
- r*smy*ssphi*sinf(o);
float y = - sp.A*sbeta*sinf(o)
- r*csphi*sinf(o+sp.omega)
- r*smy*ssphi*cosf(o);
float z = - sp.A*cosf(sp.beta)
+ r*ssphi*cosf(sp.my);
p[0] = x*sc;
p[1] = -z*sc;
p[2] = y*sc;
}
MObject shellNode::alpha;
MObject shellNode::beta;
MObject shellNode::phi;
MObject shellNode::my;
MObject shellNode::omega;
MObject shellNode::omin;
MObject shellNode::omax;
MObject shellNode::od;
MObject shellNode::smin;
MObject shellNode::smax;
MObject shellNode::sd;
MObject shellNode::A;
MObject shellNode::a;
MObject shellNode::b;
MObject shellNode::scale;
MObject shellNode::P;
MObject shellNode::L;
MObject shellNode::N;
MObject shellNode::W1;
MObject shellNode::W2;
MObject shellNode::nstart;
MObject shellNode::P2;
MObject shellNode::L2;
MObject shellNode::N2;
MObject shellNode::W12;
MObject shellNode::W22;
MObject shellNode::off2;
MObject shellNode::nstart2;
MObject shellNode::P3;
MObject shellNode::L3;
MObject shellNode::N3;
MObject shellNode::W13;
MObject shellNode::W23;
MObject shellNode::off3;
MObject shellNode::nstart3;
MObject shellNode::uamp;
MObject shellNode::ufreq;
MObject shellNode::urib;
MObject shellNode::vamp;
MObject shellNode::vfreq;
MObject shellNode::vrib;
MObject shellNode::outMesh;
void shellNode::addFloatParameter( MObject & attr, MString longName,
MString briefName, float attrDefault )
{
MStatus stat;
MFnNumericAttribute nAttr;
attr = nAttr.create( longName, briefName, MFnNumericData::kFloat,
0.0, &stat );
if ( stat != MS::kSuccess ) throw stat;
stat = nAttr.setDefault ( attrDefault );
if ( stat != MS::kSuccess ) throw stat;
stat = nAttr.setKeyable ( true );
if ( stat != MS::kSuccess ) throw stat;
stat = nAttr.setCached ( true );
if ( stat != MS::kSuccess ) throw stat;
stat = nAttr.setStorable ( true );
if ( stat != MS::kSuccess ) throw stat;
stat = addAttribute( attr );
if ( stat != MS::kSuccess ) throw stat;
stat = attributeAffects( attr, outMesh );
if ( stat != MS::kSuccess ) throw stat;
}
void shellNode::addAngleParameter( MObject & attr, MString longName,
MString briefName, float attrDefault )
{
MStatus stat;
MFnUnitAttribute uAttr;
MAngle defaultAngle( (double)attrDefault, MAngle::kDegrees );
attr = uAttr.create( longName, briefName, defaultAngle, &stat );
if ( stat != MS::kSuccess ) throw stat;
stat = uAttr.setKeyable ( true );
if ( stat != MS::kSuccess ) throw stat;
stat = uAttr.setCached ( true );
if ( stat != MS::kSuccess ) throw stat;
stat = uAttr.setStorable ( true );
if ( stat != MS::kSuccess ) throw stat;
stat = addAttribute( attr );
if ( stat != MS::kSuccess ) throw stat;
stat = attributeAffects( attr, outMesh );
if ( stat != MS::kSuccess ) throw stat;
}
MStatus shellNode::initialize()
{
MFnTypedAttribute typedFn;
MStatus stat;
outMesh = typedFn.create( "outMesh", "o", MFnData::kMesh,
&stat );
if ( MS::kSuccess != stat ) {
cerr << "ERROR creating animCube output attribute\n";
return stat;
}
typedFn.setStorable(false);
typedFn.setWritable(false);
stat = addAttribute( outMesh );
McheckErr(stat, "ERROR adding attribute");
try {
addAngleParameter( alpha, "profileParam1", "pp1", 80.0F );
addAngleParameter( beta, "profileParam2", "pp2", 90.0F );
addAngleParameter( phi, "sectionStartingPoint", "ssp", 1.0F );
addAngleParameter( my, "sectionSlant", "ss", 1.0F );
addAngleParameter( omega, "sectionAngleZ", "saz", 1.0F );
addAngleParameter( omin, "spiralStartAngle", "sps", 0.0F );
addAngleParameter( omax, "spiralEndAngle", "spe", 1200.0F );
addAngleParameter( od, "spiralAngleStep", "spa", 4.0F );
addAngleParameter( smin, "sectionStartAngle", "ssa", -190.0F );
addAngleParameter( smax, "sectionEndAngle", "sea", 190.0F );
addAngleParameter( sd, "sectionAngleStep", "sas", 17.0F );
addFloatParameter( A, "distanceFromZ", "dfz", 1.9F );
addFloatParameter( a, "sectionDiameter1", "sd1", 1.0F );
addFloatParameter( b, "sectionDiameter2", "sd2", 0.9F );
addFloatParameter( scale, "scale", "s", 0.03F );
addAngleParameter( P, "positionOnSection1", "ps1", 10.0F );
addFloatParameter( L, "noduleAmplitude1", "na1", 1.0F );
addFloatParameter( N, "noduleProfileFrequency1", "nf1", 15.0F );
addAngleParameter( W1, "noduleFatness11", "f11", 100.0F );
addAngleParameter( W2, "noduleFatness21", "f21", 20.0F );
addAngleParameter( nstart, "spiralStartingPoint1", "sp1", 0.0F );
addAngleParameter( P2, "positionOnSection2", "ps2", 0.0F );
addFloatParameter( L2, "noduleAmplitude2", "na2", 0.0F );
addFloatParameter( N2, "noduleProfileFrequency2", "nf2", 0.0F );
addAngleParameter( W12, "noduleFatness12", "f12", 30.0F );
addAngleParameter( W22, "noduleFatness22", "f22", 30.0F );
addAngleParameter( off2, "noduleOffset2", "no2", 0.0F );
addAngleParameter( nstart2, "spiralStartingPoint2", "sp2", 0.0F );
addAngleParameter( P3, "positionOnSection3", "ps3", 0.0F );
addFloatParameter( L3, "noduleAmplitude3", "na3", 0.0F );
addFloatParameter( N3, "noduleProfileFrequency3", "nf3", 0.0F );
addAngleParameter( W13, "noduleFatness13", "f13", 30.0F );
addAngleParameter( W23, "noduleFatness23", "f23", 30.0F );
addAngleParameter( off3, "noduleOffset3", "no3", 0.0F );
addAngleParameter( nstart3, "spiralStartingPoint3", "sp3", 0.0F );
addFloatParameter( uamp, "sectionRibAmplitude", "sra", 0.0F );
addFloatParameter( ufreq, "sectionRibFrequency", "srf", 0.0F );
addFloatParameter( urib, "sectionRibWavePercent", "srw", 0.0F );
addFloatParameter( vamp, "profileRibAmplitude", "pra", 0.0F );
addFloatParameter( vfreq, "profileRibFrequency", "prf", 0.0F );
addFloatParameter( vrib, "profileRibWavePercent", "prw", 0.0F );
} catch ( MStatus stat ) {
fprintf(stderr,"Attribute Initialize Failed\n");
return stat;
}
return MS::kSuccess;
}
inline float shellNode::GetFloatParameter( MObject node, MObject attr )
{
MPlug plug( node, attr );
float value;
plug.getValue( value );
return value;
}
inline float shellNode::GetAngleParameter( MObject node, MObject attr )
{
MPlug plug( node, attr );
MAngle angle;
plug.getValue( angle );
return (float)angle.asRadians();
}
#define UpdateFloatAttr(ATTR,TOPOLOGY) \
oldValue = shellParams. ATTR; \
shellParams. ATTR = GetFloatParameter( thisObj, ATTR ); \
if ( shellParams. ATTR != oldValue ) { \
rebuild = true; \
redoTopology = TOPOLOGY ? true : redoTopology; \
}
#define UpdateAngleAttr(ATTR,TOPOLOGY) \
oldValue = shellParams. ATTR; \
shellParams. ATTR = GetAngleParameter( thisObj, ATTR ); \
if ( shellParams. ATTR != oldValue ) { \
rebuild = true; \
redoTopology = TOPOLOGY ? true : redoTopology; \
}
void shellNode::UpdateParameters( )
{
MObject thisObj = thisMObject();
float oldValue;
UpdateAngleAttr(alpha,false);
UpdateAngleAttr(beta,false);
UpdateAngleAttr(phi,false);
UpdateAngleAttr(my,false);
UpdateAngleAttr(omega,false);
UpdateFloatAttr(A,false);
UpdateFloatAttr(a,false);
UpdateFloatAttr(b,false);
UpdateFloatAttr(scale,false);
UpdateAngleAttr(P,false);
UpdateFloatAttr(L,false);
UpdateFloatAttr(N,false);
UpdateAngleAttr(W1,false);
UpdateAngleAttr(W2,false);
UpdateAngleAttr(nstart,false);
UpdateAngleAttr(P2,false);
UpdateFloatAttr(L2,false);
UpdateFloatAttr(N2,false);
UpdateAngleAttr(W12,false);
UpdateAngleAttr(W22,false);
UpdateAngleAttr(off2,false);
UpdateAngleAttr(nstart2,false);
UpdateAngleAttr(P3,false);
UpdateFloatAttr(L3,false);
UpdateFloatAttr(N3,false);
UpdateAngleAttr(W13,false);
UpdateAngleAttr(W23,false);
UpdateAngleAttr(off3,false);
UpdateAngleAttr(nstart3,false);
UpdateFloatAttr(uamp,false);
UpdateFloatAttr(ufreq,false);
UpdateFloatAttr(urib,false);
UpdateFloatAttr(vamp,false);
UpdateFloatAttr(vfreq,false);
UpdateFloatAttr(vrib,false);
UpdateAngleAttr(omin,true);
UpdateAngleAttr(omax,true);
UpdateAngleAttr(od,true);
UpdateAngleAttr(smin,true);
UpdateAngleAttr(smax,true);
UpdateAngleAttr(sd,true);
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");
status = plugin.registerNode( "shell", shellNode::id,
&shellNode::creator, &shellNode::initialize,
MPxNode::kDependNode );
if (!status) {
status.perror("registerNode");
return status;
}
return status;
}
MStatus uninitializePlugin( MObject obj)
{
MStatus status;
MFnPlugin plugin( obj );
status = plugin.deregisterNode( shellNode::id );
if (!status) {
status.perror("deregisterNode");
return status;
}
return status;
}