AtNode *convert( const IECore::ExternalProcedural *procedural )
{
	std::string nodeType = "procedural";
	// Allow a parameter "ai:nodeType" == "volume" to create a volume shape rather
	// than a procedural shape. Volume shapes provide "dso", "min" and "max" parameters
	// just as procedural shapes do, so the mapping is a fairly natural one.
	const CompoundDataMap &parameters = procedural->parameters()->readable();
	CompoundDataMap::const_iterator nodeTypeIt = parameters.find( "ai:nodeType" );
	if( nodeTypeIt != parameters.end() && nodeTypeIt->second->isInstanceOf( StringData::staticTypeId() ) )
	{
		nodeType = static_cast<const StringData *>( nodeTypeIt->second.get() )->readable();
	}
	AtNode *node = AiNode( nodeType.c_str() );

	AiNodeSetStr( node, "dso", procedural->getFileName().c_str() );
	ParameterAlgo::setParameters( node, parameters );

	const Box3f bound = procedural->bound();
	if( bound != Renderer::Procedural::noBound )
	{
		AiNodeSetPnt( node, "min", bound.min.x, bound.min.y, bound.min.z );
		AiNodeSetPnt( node, "max", bound.max.x, bound.max.y, bound.max.z );
	}
	else
	{
		// No bound available - expand procedural immediately.
		AiNodeSetBool( node, "load_at_init", true );
	}

	return node;
}
Beispiel #2
0
                virtual AtNode* ExportInstance(AtNode *instance, const MDagPath& masterInstance)
                {
                   AtNode* masterNode = AiNodeLookUpByName(masterInstance.partialPathName().asChar());


                   int instanceNum = m_dagPath.instanceNumber();

                   if ( instanceNum > 0 )
                     {
                       AiNodeSetStr(instance, "name", m_dagPath.partialPathName().asChar());

                       ExportMatrix(instance, 0);

                       AiNodeSetPtr(instance, "node", masterNode);
                       AiNodeSetBool(instance, "inherit_xform", false);
                       int visibility = AiNodeGetInt(masterNode, "visibility");
                       AiNodeSetInt(instance, "visibility", visibility);

                       AtNode *shader = arnoldShader();
                       if( shader )
                       {
                           AiNodeSetPtr( instance, "shader", shader );
                       }

                       // Export light linking per instance
                       ExportLightLinking(instance);
                     }
                   return instance;
                }
void ApplyOverrides(std::string name, AtNode* node, std::vector<std::string> tags, ProcArgs & args)
{
   bool foundInPath = false;
   for(std::vector<std::string>::iterator it=args.overrides.begin(); it!=args.overrides.end(); ++it)
   {
      Json::Value overrides;                        
      if(it->find("/") != std::string::npos) // Based on path
      {
        if(name.find(*it) != std::string::npos)
        {
          overrides = args.overrideRoot[*it];
          foundInPath = true;
        }
      } 
      else if(matchPattern(name,*it)) // based on wildcard expression
      {
          overrides = args.overrideRoot[*it];
          foundInPath = true;
      }
      else if(foundInPath == false)
      {
        if (std::find(tags.begin(), tags.end(), *it) != tags.end())
        {
          overrides = args.overrideRoot[*it];
        }
      }

      if(overrides.size() > 0)
      {
        for( Json::ValueIterator itr = overrides.begin() ; itr != overrides.end() ; itr++ ) 
        {
          std::string attribute = itr.key().asString();

          const AtNodeEntry* nodeEntry = AiNodeGetNodeEntry(node);
          const AtParamEntry* paramEntry = AiNodeEntryLookUpParameter(nodeEntry, attribute.c_str());

          if ( paramEntry != NULL && attribute!="invert_normals" || attribute == "matte")
          {

            Json::Value val = args.overrideRoot[*it][itr.key().asString()];
            if( val.isString() ) 
              AiNodeSetStr(node, attribute.c_str(), val.asCString());
            else if( val.isBool() )
            {
              if(attribute == "matte")
              {
                  AiMsgDebug("[ABC] adding enable_matte to %s", AiNodeGetName(node));
                  AddUserGeomParams(node,"enable_matte",AI_TYPE_BOOLEAN);
                  AiNodeSetBool(node,"enable_matte", val.asBool());
              }
              else
              {
                  AiNodeSetBool(node, attribute.c_str(), val.asBool());
              }
            }
            else if( val.isInt() ) 
            {
              //make the difference between Byte & int!
              int typeEntry = AiParamGetType(paramEntry);
              if(typeEntry == AI_TYPE_BYTE)
              { 
                if(attribute=="visibility")
                {
                  AtByte attrViz = val.asInt();
                  // special case, we must determine it against the general viz.
                  AtByte procViz = AiNodeGetByte( args.proceduralNode, "visibility" );
                  AtByte compViz = AI_RAY_ALL;
                  {
                    compViz &= ~AI_RAY_GLOSSY;
                    if(procViz > compViz)
                      procViz &= ~AI_RAY_GLOSSY;
                    else
                      attrViz &= ~AI_RAY_GLOSSY;
                    compViz &= ~AI_RAY_DIFFUSE;
                    if(procViz > compViz)
                      procViz &= ~AI_RAY_DIFFUSE;
                    else
                      attrViz &= ~AI_RAY_DIFFUSE;
                    compViz &= ~AI_RAY_REFRACTED;
                    if(procViz > compViz)
                      procViz &= ~AI_RAY_REFRACTED;
                    else
                      attrViz &= ~AI_RAY_REFRACTED;
                    compViz &= ~AI_RAY_REFLECTED;
                    if(procViz > compViz)
                      procViz &= ~AI_RAY_REFLECTED;
                    else
                      attrViz &= ~AI_RAY_REFLECTED;
                    compViz &= ~AI_RAY_SHADOW;
                    if(procViz > compViz)
                      procViz &= ~AI_RAY_SHADOW;
                    else
                      attrViz &= ~AI_RAY_SHADOW;
                    compViz &= ~AI_RAY_CAMERA;
                    if(procViz > compViz)
                      procViz &= ~AI_RAY_CAMERA;
                    else
                      attrViz &= ~AI_RAY_CAMERA;
                  }

                  AiNodeSetByte(node, attribute.c_str(), attrViz);
                }
                else
                  AiNodeSetByte(node, attribute.c_str(), val.asInt());
              }
              else 
                AiNodeSetInt(node, attribute.c_str(), val.asInt());
            }
            else if( val.isUInt() ) 
              AiNodeSetUInt(node, attribute.c_str(), val.asUInt());
            else if( val.isDouble() ) 
              AiNodeSetFlt(node, attribute.c_str(), val.asDouble());
          }
        }
      }

   }
}
void ApplyUserAttributes(std::string name, AtNode* node,std::vector<std::string> tags,ProcArgs & args)
{

   bool foundInPath = false;
   for(std::vector<std::string>::iterator it=args.userAttributes.begin(); it!=args.userAttributes.end(); ++it)
   {
      Json::Value userAttributes;                        
      if(it->find("/") != std::string::npos) // Based on path
      {
        if(name.find(*it) != std::string::npos)
        {
          userAttributes = args.userAttributesRoot[*it];
          foundInPath = true;
        }
      } 
      else if(matchPattern(name,*it)) // based on wildcard expression
      {
          userAttributes = args.userAttributesRoot[*it];
          foundInPath = true;
      }
      else if(foundInPath == false)
      {
        if (std::find(tags.begin(), tags.end(), *it) != tags.end())
        {
          userAttributes = args.userAttributesRoot[*it];
        }
      }

      if(userAttributes.size() > 0)
      {
        for( Json::ValueIterator itr = userAttributes.begin() ; itr != userAttributes.end() ; itr++ ) 
        {
            std::string attribute = itr.key().asString();

            if( AiNodeLookUpUserParameter(node,attribute.c_str()))
              continue;

            const AtNodeEntry* nodeEntry = AiNodeGetNodeEntry(node);
            Json::Value val = args.userAttributesRoot[*it][attribute];
            if( val.isString() ) 
            {
              AddUserGeomParams(node,attribute.c_str(),AI_TYPE_STRING);
              AiNodeSetStr(node, attribute.c_str(), val.asCString());
            } 
            else if( val.isBool() ) 
            {
              AddUserGeomParams(node,attribute.c_str(),AI_TYPE_BOOLEAN);
              AiNodeSetBool(node, attribute.c_str(), val.asBool());
            }
            else if( val.isInt() ) 
            {
              AddUserGeomParams(node,attribute.c_str(),AI_TYPE_INT);
              AiNodeSetInt(node, attribute.c_str(), val.asInt());
            }
            else if( val.isUInt() ) 
            {
              AddUserGeomParams(node,attribute.c_str(),AI_TYPE_UINT);
              AiNodeSetUInt(node, attribute.c_str(), val.asUInt());
            }
            else if(val.isDouble())
            {
              AddUserGeomParams(node,attribute.c_str(),AI_TYPE_FLOAT);
              AiNodeSetFlt(node, attribute.c_str(), val.asDouble());
            }
            else if(val.isArray()) 
            {              

              // in the future we will convert to an arnold array type for now lets just 
              // write out a json string

              AddUserGeomParams(node,attribute.c_str(),AI_TYPE_STRING);
              Json::FastWriter writer;
              AiNodeSetStr(node, attribute.c_str(), writer.write(val).c_str());
              
              // AddUserGeomParams(node,attribute.c_str(),AI_TYPE_ARRAY );

              // // get the type of the first entry, this will be our key as to
              // // what type of data is in this array
              // Json::Value firstValue = val[0];
              // if (firstValue.isString())
              // {
              //   AtArray* arrayValues = AiArrayAllocate( val.size() , 1, AI_TYPE_STRING);

              //   for( uint idx = 0 ; idx != val.size() ; idx++ ) 
              //   {
              //     AiMsgInfo("[ABC] adding string %s to user array attribute '%s'",val[idx].asCString(),attribute.c_str());
              //     AiArraySetStr(arrayValues,idx,val[idx].asCString());
              //   }

              //   AiNodeSetArray(node, attribute.c_str(), arrayValues);         
              // }
     
            }

            // TODO color, matrix, vector

         }
      }
   }
}
Beispiel #5
0
AtNode *createInstanceNode(nodeData &nodata, userData * ud, int i)
{
  Alembic::AbcGeom::IPoints typedObject(ud->gIObjects[i].abc, Alembic::Abc::kWrapExisting);

  instanceCloudInfo * info = ud->gIObjects[i].instanceCloud;

  // check that we have the masternode
  size_t id = (size_t)ud->gIObjects[i].ID;
  size_t instanceID = (size_t)ud->gIObjects[i].instanceID;
  if(instanceID >= info->groupInfos.size())
  {
    AiMsgError("[ExocortexAlembicArnold] Instance '%s.%d' has an invalid instanceID  . Aborting.",ud->gIObjects[i].abc.getFullName().c_str(),(int)id);
    return NULL;
  }
  size_t groupID = (size_t)ud->gIObjects[i].instanceGroupID;
  if(groupID >= info->groupInfos[instanceID].identifiers.size())
  {
    AiMsgError("[ExocortexAlembicArnold] Instance '%s.%d' has an invalid instanceGroupID. Aborting.",ud->gIObjects[i].abc.getFullName().c_str(),(int)id);
    return NULL;
  }

  instanceGroupInfo * group = &info->groupInfos[instanceID];

  // get the right centroidTime
  float centroidTime = ud->gCentroidTime;
  if(info->time.size() > 0)
  {
    centroidTime = info->time[0]->get()[id < info->time[0]->size() ? id : info->time[0]->size() - 1];
    if(info->time.size() > 1)
      centroidTime = (1.0f - info->timeAlpha) * centroidTime + info->timeAlpha * info->time[1]->get()[id < info->time[1]->size() ? id : info->time[1]->size() - 1];
    centroidTime = roundCentroid(centroidTime);
  }

  std::map<float,AtNode*>::iterator it = group->nodes[groupID].find(centroidTime);
  if(it == group->nodes[groupID].end())
  {
    AiMsgError("[ExocortexAlembicArnold] Cannot find masterNode '%s' for centroidTime '%f'. Aborting.",group->identifiers[groupID].c_str(),centroidTime);
    return NULL;
  }
  AtNode *usedMasterNode = it->second;

  AtNode *shapeNode = AiNode("ginstance");

  // setup name, id and the master node
  AiNodeSetStr(shapeNode, "name", getNameFromIdentifier(ud->gIObjects[i].abc.getFullName(),ud->gIObjects[i].ID,(long)groupID).c_str());
  AiNodeSetInt(shapeNode, "id", ud->gIObjects[i].instanceID); 
  AiNodeSetPtr(shapeNode, "node", usedMasterNode);

  // declare color on the ginstance
  if(info->color.size() > 0 && AiNodeDeclare(shapeNode, "Color", "constant RGBA"))
  {
    Alembic::Abc::C4f color = info->color[0]->get()[id < info->color[0]->size() ? id : info->color[0]->size() - 1];
    AiNodeSetRGBA(shapeNode, "Color", color.r, color.g, color.b, color.a);
  }

  // now let's take care of the transform
  AtArray * matrices = AiArrayAllocate(1,(AtInt)ud->gMbKeys.size(),AI_TYPE_MATRIX);
  for(size_t j=0;j<ud->gMbKeys.size(); ++j)
  {
    SampleInfo sampleInfo = getSampleInfo(
      ud->gMbKeys[j],
      typedObject.getSchema().getTimeSampling(),
      typedObject.getSchema().getNumSamples()
    );

    Alembic::Abc::M44f matrixAbc;
    matrixAbc.makeIdentity();
    const size_t floorIndex = j << 1;
    const size_t ceilIndex =  floorIndex + 1;

    // apply translation
    if(info->pos[floorIndex]->size() == info->pos[ceilIndex]->size())
    {
      matrixAbc.setTranslation(float(1.0 - sampleInfo.alpha) * info->pos[floorIndex]->get()[id < info->pos[floorIndex]->size() ? id : info->pos[floorIndex]->size() - 1] + 
                               float(sampleInfo.alpha) * info->pos[ceilIndex]->get()[id < info->pos[ceilIndex]->size() ? id : info->pos[ceilIndex]->size() - 1]);
    }
    else
    {
      const float timeAlpha = getTimeOffsetFromObject( typedObject, sampleInfo );

      matrixAbc.setTranslation(info->pos[floorIndex]->get()[id < info->pos[floorIndex]->size() ? id : info->pos[floorIndex]->size() - 1] + 
                               info->vel[floorIndex]->get()[id < info->vel[floorIndex]->size() ? id : info->vel[floorIndex]->size() - 1] * timeAlpha);
    }

    // now take care of rotation
    if(info->rot.size() == ud->gMbKeys.size())
    {
      Alembic::Abc::Quatf rotAbc = info->rot[j]->get()[id < info->rot[j]->size() ? id : info->rot[j]->size() - 1];
      if(info->ang.size() == ud->gMbKeys.size() && sampleInfo.alpha > 0.0)
      {
        Alembic::Abc::Quatf angAbc = info->ang[j]->get()[id < info->ang[j]->size() ? id : info->ang[j]->size() -1] * (float)sampleInfo.alpha;
        if(angAbc.axis().length2() != 0.0f && angAbc.r != 0.0f)
        {
          rotAbc = angAbc * rotAbc;
          rotAbc.normalize();
        }
      }
      Alembic::Abc::M44f matrixAbcRot;
      matrixAbcRot.setAxisAngle(rotAbc.axis(),rotAbc.angle());
      matrixAbc = matrixAbcRot * matrixAbc;
    }

    // and finally scaling
    if(info->scale.size() == ud->gMbKeys.size() * 2)
    {
      const Alembic::Abc::V3f scalingAbc = info->scale[floorIndex]->get()[id < info->scale[floorIndex]->size() ? id : info->scale[floorIndex]->size() - 1] * 
                                           info->width[floorIndex]->get()[id < info->width[floorIndex]->size() ? id : info->width[floorIndex]->size() - 1] * float(1.0 - sampleInfo.alpha) + 
                                           info->scale[ceilIndex]->get()[id < info->scale[ceilIndex]->size() ? id : info->scale[ceilIndex]->size() - 1] * 
                                           info->width[ceilIndex]->get()[id < info->width[ceilIndex]->size() ? id : info->width[ceilIndex]->size() - 1] * float(sampleInfo.alpha);
      matrixAbc.scale(scalingAbc);
    }
    else
    {
      const float width = info->width[floorIndex]->get()[id < info->width[floorIndex]->size() ? id : info->width[floorIndex]->size() - 1] * float(1.0 - sampleInfo.alpha) + 
                          info->width[ceilIndex]->get()[id < info->width[ceilIndex]->size() ? id : info->width[ceilIndex]->size() - 1] * float(sampleInfo.alpha);
      matrixAbc.scale(Alembic::Abc::V3f(width,width,width));
    }

    // if we have offset matrices
    if(group->parents.size() > groupID && group->matrices.size() > groupID)
    {
      if(group->objects[groupID].valid() && group->parents[groupID].valid())
      {
        // we have a matrix map and a parent.
        // now we need to check if we already exported the matrices
        std::map<float,std::vector<Alembic::Abc::M44f> >::iterator it;
        std::vector<Alembic::Abc::M44f> offsets;
        it = group->matrices[groupID].find(centroidTime);
        if(it == group->matrices[groupID].end())
        {
          std::vector<float> samples(ud->gMbKeys.size());
          offsets.resize(ud->gMbKeys.size());
          for(AtInt sampleIndex=0;sampleIndex<(AtInt)ud->gMbKeys.size(); ++sampleIndex)
          {
            offsets[sampleIndex].makeIdentity();
            // centralize the time once more
            samples[sampleIndex] = centroidTime + ud->gMbKeys[sampleIndex] - ud->gCentroidTime;
          }

          // if the transform differs, we need to compute the offset matrices
          // get the parent, which should be a transform
          Alembic::Abc::IObject parent = group->parents[groupID];
          Alembic::Abc::IObject xform = group->objects[groupID].getParent();
          while(Alembic::AbcGeom::IXform::matches(xform.getMetaData()) && xform.getFullName() != parent.getFullName())
          {
            // cast to a xform
            Alembic::AbcGeom::IXform parentXform(xform,Alembic::Abc::kWrapExisting);
            if(parentXform.getSchema().getNumSamples() == 0)
              break;

            // loop over all samples
            for(size_t sampleIndex=0;sampleIndex<ud->gMbKeys.size(); ++sampleIndex)
            {
              SampleInfo sampleInfo = getSampleInfo(
                 samples[sampleIndex],
                 parentXform.getSchema().getTimeSampling(),
                 parentXform.getSchema().getNumSamples()
              );

              // get the data and blend it if necessary
              Alembic::AbcGeom::XformSample sample;
              parentXform.getSchema().get(sample,sampleInfo.floorIndex);
              Alembic::Abc::M44f abcMatrix;
              Alembic::Abc::M44d abcMatrixd = sample.getMatrix();
              for(int x=0;x<4;x++)
                 for(int y=0;y<4;y++)
                    abcMatrix[x][y] = (float)abcMatrixd[x][y];
               
              if(sampleInfo.alpha >= sampleTolerance)
              {
                parentXform.getSchema().get(sample,sampleInfo.ceilIndex);
                Alembic::Abc::M44d ceilAbcMatrixd = sample.getMatrix();
                Alembic::Abc::M44f ceilAbcMatrix;
                for(int x=0;x<4;x++)
                  for(int y=0;y<4;y++)
                     ceilAbcMatrix[x][y] = (float)ceilAbcMatrixd[x][y];
                abcMatrix = float(1.0 - sampleInfo.alpha) * abcMatrix + float(sampleInfo.alpha) * ceilAbcMatrix;
              }

              offsets[sampleIndex] = abcMatrix * offsets[sampleIndex];
            }

            // go upwards
            xform = xform.getParent();
          }
          group->matrices[groupID].insert(std::pair<float,std::vector<Alembic::Abc::M44f> >(centroidTime,offsets));
        }
        else
          offsets = it->second;

        // this means we have the right amount of matrices to blend against
        if(offsets.size() > j)
          matrixAbc = offsets[j] * matrixAbc;
      }
    }

    // store it to the array
    AiArraySetMtx(matrices,(AtULong)j,matrixAbc.x);
  }

  AiNodeSetArray(shapeNode,"matrix",matrices);
  AiNodeSetBool(shapeNode, "inherit_xform", FALSE);

  return shapeNode;
}
Beispiel #6
0
		virtual void ExportProcedural( AtNode *node )
		{
			// do basic node export
			
			ExportMatrix( node, 0 );
			
			AtNode *shader = arnoldShader();
			if( shader )
			{
				AiNodeSetPtr( node, "shader", shader );
			}
			
			AiNodeSetInt( node, "visibility", ComputeVisibility() );
			
			MPlug plug = FindMayaObjectPlug( "receiveShadows" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "receive_shadows", plug.asBool() );
			}
			
			plug = FindMayaObjectPlug( "aiSelfShadows" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "self_shadows", plug.asBool() );
			}
			
			plug = FindMayaObjectPlug( "aiOpaque" );
			if( !plug.isNull() )
			{
				AiNodeSetBool( node, "opaque", plug.asBool() );
			}
			
			// export any shading groups or displacement shaders which look like they
			// may be connected to procedural parameters. this ensures that maya shaders
			// the procedural will expect to find at rendertime will be exported to the
			// ass file (they otherwise might not be if they're not assigned to any objects).
			
			exportShadingInputs();
			
			// now set the procedural-specific parameters
			
			MFnDagNode fnDagNode( m_dagPath );
			MBoundingBox bound = fnDagNode.boundingBox();
			
			AiNodeSetPnt( node, "min", bound.min().x, bound.min().y, bound.min().z );
			AiNodeSetPnt( node, "max", bound.max().x, bound.max().y, bound.max().z );
			
			const char *dsoPath = getenv( "IECOREARNOLD_PROCEDURAL_PATH" );
			AiNodeSetStr( node, "dso", dsoPath ? dsoPath : "ieProcedural.so" );
			
			AiNodeDeclare( node, "className", "constant STRING" );
			AiNodeDeclare( node, "classVersion", "constant INT" );
			AiNodeDeclare( node, "parameterValues", "constant ARRAY STRING" );
			
			// cast should be ok as we're registered to only work on procedural holders
			IECoreMaya::ProceduralHolder *pHolder = static_cast<IECoreMaya::ProceduralHolder *>( fnDagNode.userNode() );
			
			std::string className;
			int classVersion;
			IECore::ParameterisedProceduralPtr procedural = pHolder->getProcedural( &className, &classVersion );
			
			AiNodeSetStr( node, "className", className.c_str() );
			AiNodeSetInt( node, "classVersion", classVersion );
			
			IECorePython::ScopedGILLock gilLock;
			try
			{
				boost::python::object parser = IECoreMaya::PythonCmd::globalContext()["IECore"].attr( "ParameterParser" )();
				boost::python::object serialised = parser.attr( "serialise" )( procedural->parameters() );
				
				size_t numStrings = IECorePython::len( serialised );
				AtArray *stringArray = AiArrayAllocate( numStrings, 1, AI_TYPE_STRING );
				for( size_t i=0; i<numStrings; i++ )
				{
					std::string s = boost::python::extract<std::string>( serialised[i] );
					// hack to workaround ass parsing errors
					/// \todo Remove when we get the Arnold version that fixes this
					for( size_t c = 0; c<s.size(); c++ )
					{
						if( s[c] == '#' )
						{
							s[c] = '@';
						}
					}
					AiArraySetStr( stringArray, i, s.c_str() );
				}
				
				AiNodeSetArray( node, "parameterValues", stringArray );
			}
			catch( boost::python::error_already_set )
			{
				PyErr_Print();
			}
		
		}
void CScriptedShapeTranslator::RunScripts(AtNode *atNode, unsigned int step, bool update)
{
   std::map<std::string, CScriptedTranslator>::iterator translatorIt;
   MFnDependencyNode fnNode(GetMayaObject());
   
   translatorIt = gTranslators.find(fnNode.typeName().asChar());
   if (translatorIt == gTranslators.end())
   {
      AiMsgError("[mtoa.scriptedTranslators] No command to export node \"%s\" of type %s.", fnNode.name().asChar(), fnNode.typeName().asChar());
      return;
   }
   
   MString exportCmd = translatorIt->second.exportCmd;
   MString cleanupCmd = translatorIt->second.cleanupCmd;
   
   MFnDagNode node(m_dagPath.node());
   
   bool isMasterDag = false;
   bool transformBlur = IsMotionBlurEnabled(MTOA_MBLUR_OBJECT) && IsLocalMotionBlurEnabled();
   bool deformBlur = IsMotionBlurEnabled(MTOA_MBLUR_DEFORM) && IsLocalMotionBlurEnabled();
   
   char buffer[64];
   
   MString command = exportCmd;
   command += "(";
   
   sprintf(buffer, "%f", GetExportFrame());
   command += buffer;
   command += ", ";
   
   sprintf(buffer, "%d", step);
   command += buffer;
   command += ", ";
   
   // current sample frame
   sprintf(buffer, "%f", GetSampleFrame(m_session, step));
   command += buffer;
   command += ", ";
   
   // List of arnold attributes the custom shape export command has overriden
   MStringArray attrs;
   
   if (!m_masterNode)
   {
      command += "(\"" + m_dagPath.partialPathName() + "\", \"";
      command += AiNodeGetName(atNode);
      command += "\"), None)";
      isMasterDag = true;
   }
   else
   {
      command += "(\"" + m_dagPath.partialPathName() + "\", \"";
      command += AiNodeGetName(atNode);
      command += "\"), (\"" + GetMasterInstance().partialPathName() + "\", \"";
      command += AiNodeGetName(m_masterNode);
      command += "\"))";
   }
   
   MStatus status = MGlobal::executePythonCommand(command, attrs);
   if (!status)
   {
      AiMsgError("[mtoa.scriptedTranslators] Failed to export node \"%s\".", node.name().asChar());
      return;
   }
   
   // Build set of attributes already processed
   std::set<std::string> attrsSet;
   for (unsigned int i=0; i<attrs.length(); ++i)
   {
      attrsSet.insert(attrs[i].asChar());
   }
   std::set<std::string>::iterator attrsEnd = attrsSet.end();
   
   // Should be getting displacement shader from master instance only
   //   as arnold do not support displacement shader overrides for ginstance
   MFnDependencyNode masterShadingEngine;
   MFnDependencyNode shadingEngine;
   float dispPadding = -AI_BIG;
   float dispHeight = 1.0f;
   float dispZeroValue = 0.0f;
   bool dispAutobump = false;
   bool outputDispPadding = false;
   bool outputDispHeight = false;
   bool outputDispZeroValue = false;
   bool outputDispAutobump = false;
   
   const AtNodeEntry *anodeEntry = AiNodeGetNodeEntry(atNode);
   
   GetShapeInstanceShader(m_dagPath, shadingEngine);
   if (!IsMasterInstance())
   {
      GetShapeInstanceShader(GetMasterInstance(), masterShadingEngine);
   }
   else
   {
      masterShadingEngine.setObject(shadingEngine.object());
   }
   
   AtMatrix matrix;
   MMatrix mmatrix = m_dagPath.inclusiveMatrix();
   ConvertMatrix(matrix, mmatrix);
   
   // Set transformation matrix
   if (attrsSet.find("matrix") == attrsEnd)
   {
      if (HasParameter(anodeEntry, "matrix"))
      {
         if (transformBlur)
         {
            if (step == 0)
            {
               AtArray* matrices = AiArrayAllocate(1, GetNumMotionSteps(), AI_TYPE_MATRIX);
               AiArraySetMtx(matrices, step, matrix);
               AiNodeSetArray(atNode, "matrix", matrices);
            }
            else
            {
               AtArray* matrices = AiNodeGetArray(atNode, "matrix");
               AiArraySetMtx(matrices, step, matrix);
            }
         }
         else
         {
            AiNodeSetMatrix(atNode, "matrix", matrix);
         }
      }
   }
   
   // Set bounding box
   if (attrsSet.find("min") == attrsEnd && attrsSet.find("max") == attrsEnd)
   {
      // Now check if min and max parameters are valid parameter names on arnold node
      if (HasParameter(anodeEntry, "min") != 0 && HasParameter(anodeEntry, "max") != 0)
      {
         if (step == 0)
         {
            MBoundingBox bbox = node.boundingBox();
            
            MPoint bmin = bbox.min();
            MPoint bmax = bbox.max();
            
            AiNodeSetPnt(atNode, "min", static_cast<float>(bmin.x), static_cast<float>(bmin.y), static_cast<float>(bmin.z));
            AiNodeSetPnt(atNode, "max", static_cast<float>(bmax.x), static_cast<float>(bmax.y), static_cast<float>(bmax.z));
         }
         else
         {
            if (transformBlur || deformBlur)
            {
               AtPoint cmin = AiNodeGetPnt(atNode, "min");
               AtPoint cmax = AiNodeGetPnt(atNode, "max");
               
               MBoundingBox bbox = node.boundingBox();
               
               MPoint bmin = bbox.min();
               MPoint bmax = bbox.max();
               
               if (bmin.x < cmin.x)
                  cmin.x = static_cast<float>(bmin.x);
               if (bmin.y < cmin.y)
                  cmin.y = static_cast<float>(bmin.y);
               if (bmin.z < cmin.z)
                  cmin.z = static_cast<float>(bmin.z);
               if (bmax.x > cmax.x)
                  cmax.x = static_cast<float>(bmax.x);
               if (bmax.y > cmax.y)
                  cmax.y = static_cast<float>(bmax.y);
               if (bmax.z > cmax.z)
                  cmax.z = static_cast<float>(bmax.z);
               
               AiNodeSetPnt(atNode, "min", cmin.x, cmin.y, cmin.z);
               AiNodeSetPnt(atNode, "max", cmax.x, cmax.y, cmax.z);
            }
         }
      }
   }
   
   if (step == 0)
   {
      // Set common attributes
      MPlug plug;
      
      if (AiNodeIs(atNode, "procedural"))
      {
         // Note: it is up to the procedural to properly forward (or not) those parameters to the node
         //       it creates
         
         if (attrsSet.find("subdiv_type") == attrsEnd)
         {
            plug = FindMayaPlug("subdiv_type");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSubdivType");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "subdiv_type", atNode, "constant INT"))
            {
               AiNodeSetInt(atNode, "subdiv_type", plug.asInt());
            }
         }
         
         if (attrsSet.find("subdiv_iterations") == attrsEnd)
         {
            plug = FindMayaPlug("subdiv_iterations");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSubdivIterations");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "subdiv_iterations", atNode, "constant BYTE"))
            {
               AiNodeSetByte(atNode, "subdiv_iterations", plug.asInt());
            }
         }
         
         if (attrsSet.find("subdiv_adaptive_metric") == attrsEnd)
         {
            plug = FindMayaPlug("subdiv_adaptive_metric");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSubdivAdaptiveMetric");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "subdiv_adaptive_metric", atNode, "constant INT"))
            {
               AiNodeSetInt(atNode, "subdiv_adaptive_metric", plug.asInt());
            }
         }
         
         if (attrsSet.find("subdiv_pixel_error") == attrsEnd)
         {
            plug = FindMayaPlug("subdiv_pixel_error");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSubdivPixelError");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "subdiv_pixel_error", atNode, "constant FLOAT"))
            {
               AiNodeSetFlt(atNode, "subdiv_pixel_error", plug.asFloat());
            }
         }
         
         if (attrsSet.find("subdiv_dicing_camera") == attrsEnd)
         {
            plug = FindMayaPlug("subdiv_dicing_camera");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSubdivDicingCamera");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "subdiv_dicing_camera", atNode, "constant NODE"))
            {
               AtNode *cameraNode = NULL;
               
               MPlugArray plugs;
               plug.connectedTo(plugs, true, false);
               
               if (plugs.length() == 1)
               {
                  MFnDagNode camDag(plugs[0].node());
                  MDagPath camPath;
                  
                  if (camDag.getPath(camPath) == MS::kSuccess)
                  {
                     cameraNode = ExportDagPath(camPath);
                  }
               }
               
               AiNodeSetPtr(atNode, "subdiv_dicing_camera", cameraNode);
            }
         }
         
         if (attrsSet.find("subdiv_uv_smoothing") == attrsEnd)
         {
            plug = FindMayaPlug("subdiv_uv_smoothing");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSubdivUvSmoothing");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "subdiv_uv_smoothing", atNode, "constant INT"))
            {
               AiNodeSetInt(atNode, "subdiv_uv_smoothing", plug.asInt());
            }
         }
         
         if (attrsSet.find("subdiv_smooth_derivs") == attrsEnd)
         {
            plug = FindMayaPlug("aiSubdivSmoothDerivs");
            if (!plug.isNull() && HasParameter(anodeEntry, "subdiv_smooth_derivs", atNode, "constant BOOL"))
            {
               AiNodeSetBool(atNode, "subdiv_smooth_derivs", plug.asBool());
            }
         }
         
         if (attrsSet.find("smoothing") == attrsEnd)
         {
            // Use maya shape built-in attribute
            plug = FindMayaPlug("smoothShading");
            if (!plug.isNull() && HasParameter(anodeEntry, "smoothing", atNode, "constant BOOL"))
            {
               AiNodeSetBool(atNode, "smoothing", plug.asBool());
            }
         }
         
         if (attrsSet.find("disp_height") == attrsEnd)
         {
            plug = FindMayaPlug("aiDispHeight");
            if (!plug.isNull())
            {
               outputDispHeight = true;
               dispHeight = plug.asFloat();
            }
         }
         
         if (attrsSet.find("disp_zero_value") == attrsEnd)
         {
            plug = FindMayaPlug("aiDispZeroValue");
            if (!plug.isNull())
            {
               outputDispZeroValue = true;
               dispZeroValue = plug.asFloat();
            }
         }
         
         if (attrsSet.find("disp_autobump") == attrsEnd)
         {
            plug = FindMayaPlug("aiDispAutobump");
            if (!plug.isNull())
            {
               outputDispAutobump = true;
               dispAutobump = plug.asBool();
            }
         }
         
         if (attrsSet.find("disp_padding") == attrsEnd)
         {
            plug = FindMayaPlug("aiDispPadding");
            if (!plug.isNull())
            {
               outputDispPadding = true;
               dispPadding = MAX(dispPadding, plug.asFloat());
            }
         }
         
         // Set diplacement shader
         if (attrsSet.find("disp_map") == attrsEnd)
         {
            if (masterShadingEngine.object() != MObject::kNullObj)
            {
               MPlugArray shaderConns;
               
               MPlug shaderPlug = masterShadingEngine.findPlug("displacementShader");
               
               shaderPlug.connectedTo(shaderConns, true, false);
               
               if (shaderConns.length() > 0)
               {
                  MFnDependencyNode dispNode(shaderConns[0].node());
                  
                  plug = dispNode.findPlug("aiDisplacementPadding");
                  if (!plug.isNull())
                  {
                     outputDispPadding = true;
                     dispPadding = MAX(dispPadding, plug.asFloat());
                  }
                  
                  plug = dispNode.findPlug("aiDisplacementAutoBump");
                  if (!plug.isNull())
                  {
                     outputDispAutobump = true;
                     dispAutobump = dispAutobump || plug.asBool();
                  }
                  
                  if (HasParameter(anodeEntry, "disp_map", atNode, "constant ARRAY NODE"))
                  {
                     AtNode *dispImage = ExportNode(shaderConns[0]);
                     AiNodeSetArray(atNode, "disp_map", AiArrayConvert(1, 1, AI_TYPE_NODE, &dispImage));
                  }
               }
            }
         }
         
         if (outputDispHeight && HasParameter(anodeEntry, "disp_height", atNode, "constant FLOAT"))
         {
            AiNodeSetFlt(atNode, "disp_height", dispHeight);
         }
         if (outputDispZeroValue && HasParameter(anodeEntry, "disp_zero_value", atNode, "constant FLOAT"))
         {
            AiNodeSetFlt(atNode, "disp_zero_value", dispZeroValue);
         }
         if (outputDispPadding && HasParameter(anodeEntry, "disp_padding", atNode, "constant FLOAT"))
         {
            AiNodeSetFlt(atNode, "disp_padding", dispPadding);
         }
         if (outputDispAutobump && HasParameter(anodeEntry, "disp_autobump", atNode, "constant BOOL"))
         {
            AiNodeSetBool(atNode, "disp_autobump", dispAutobump);
         }
         
         // Old point based SSS parameter
         if (attrsSet.find("sss_sample_distribution") == attrsEnd)
         {
            plug = FindMayaPlug("sss_sample_distribution");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSssSampleDistribution");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "sss_sample_distribution", atNode, "constant INT"))
            {
               AiNodeSetInt(atNode, "sss_sample_distribution", plug.asInt());
            }
         }
         
         // Old point based SSS parameter
         if (attrsSet.find("sss_sample_spacing") == attrsEnd)
         {
            plug = FindMayaPlug("sss_sample_spacing");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiSssSampleSpacing");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "sss_sample_spacing", atNode, "constant FLOAT"))
            {
               AiNodeSetFlt(atNode, "sss_sample_spacing", plug.asFloat());
            }
         }
         
         if (attrsSet.find("min_pixel_width") == attrsEnd)
         {
            plug = FindMayaPlug("aiMinPixelWidth");
            if (!plug.isNull() && HasParameter(anodeEntry, "min_pixel_width", atNode, "constant FLOAT"))
            {
               AiNodeSetFlt(atNode, "min_pixel_width", plug.asFloat());
            }
         }
         
         if (attrsSet.find("mode") == attrsEnd)
         {
            plug = FindMayaPlug("aiMode");
            if (!plug.isNull() && HasParameter(anodeEntry, "mode", atNode, "constant INT"))
            {
               AiNodeSetInt(atNode, "mode", plug.asShort());
            }
         }
         
         if (attrsSet.find("basis") == attrsEnd)
         {
            plug = FindMayaPlug("aiBasis");
            if (!plug.isNull() && HasParameter(anodeEntry, "basis", atNode, "constant INT"))
            {
               AiNodeSetInt(atNode, "basis", plug.asShort());
            }
         }
      }
      
      if (AiNodeIs(atNode, "ginstance"))
      {
         if (attrsSet.find("node") == attrsEnd)
         {
            AiNodeSetPtr(atNode, "node", m_masterNode);
         }
         
         if (attrsSet.find("inherit_xform") == attrsEnd)
         {
            AiNodeSetBool(atNode, "inherit_xform", false);
         }
      }
      else
      {
         // box or procedural
         if (attrsSet.find("step_size") == attrsEnd)
         {
            plug = FindMayaPlug("step_size");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiStepSize");
            }
            if (!plug.isNull() && HasParameter(anodeEntry, "step_size", atNode, "constant FLOAT"))
            {
               AiNodeSetFlt(atNode, "step_size", plug.asFloat());
            }
         }
      }
      
      if (attrsSet.find("sidedness") == attrsEnd)
      {
         // Use maya shape built-in attribute
         plug = FindMayaPlug("doubleSided");
         if (!plug.isNull() && HasParameter(anodeEntry, "sidedness", atNode, "constant BYTE"))
         {
            AiNodeSetByte(atNode, "sidedness", plug.asBool() ? AI_RAY_ALL : 0);
            
            // Only set invert_normals if doubleSided attribute could be found
            if (!plug.asBool() && attrsSet.find("invert_normals") == attrsEnd)
            {
               // Use maya shape built-in attribute
               plug = FindMayaPlug("opposite");
               if (!plug.isNull() && HasParameter(anodeEntry, "invert_normals", atNode, "constant BOOL"))
               {
                  AiNodeSetBool(atNode, "invert_normals", plug.asBool());
               }
            }
         }
      }
      
      if (attrsSet.find("receive_shadows") == attrsEnd)
      {
         // Use maya shape built-in attribute
         plug = FindMayaPlug("receiveShadows");
         if (!plug.isNull() && HasParameter(anodeEntry, "receive_shadows", atNode, "constant BOOL"))
         {
            AiNodeSetBool(atNode, "receive_shadows", plug.asBool());
         }
      }
      
      if (attrsSet.find("self_shadows") == attrsEnd)
      {
         plug = FindMayaPlug("self_shadows");
         if (plug.isNull())
         {
            plug = FindMayaPlug("aiSelfShadows");
         }
         if (!plug.isNull() && HasParameter(anodeEntry, "self_shadows", atNode, "constant BOOL"))
         {
            AiNodeSetBool(atNode, "self_shadows", plug.asBool());
         }
      }
      
      if (attrsSet.find("opaque") == attrsEnd)
      {
         plug = FindMayaPlug("opaque");
         if (plug.isNull())
         {
            plug = FindMayaPlug("aiOpaque");
         }
         if (!plug.isNull() && HasParameter(anodeEntry, "opaque", atNode, "constant BOOL"))
         {
            AiNodeSetBool(atNode, "opaque", plug.asBool());
         }
      }
      
      if (attrsSet.find("visibility") == attrsEnd)
      {
         if (HasParameter(anodeEntry, "visibility", atNode, "constant BYTE"))
         {
            int visibility = AI_RAY_ALL;
            
            // Use maya shape built-in attribute
            plug = FindMayaPlug("castsShadows");
            if (!plug.isNull() && !plug.asBool())
            {
               visibility &= ~AI_RAY_SHADOW;
            }
            
            // Use maya shape built-in attribute
            plug = FindMayaPlug("primaryVisibility");
            if (!plug.isNull() && !plug.asBool())
            {
               visibility &= ~AI_RAY_CAMERA;
            }
            
            // Use maya shape built-in attribute
            plug = FindMayaPlug("visibleInReflections");
            if (!plug.isNull() && !plug.asBool())
            {
               visibility &= ~AI_RAY_REFLECTED;
            }
            
            // Use maya shape built-in attribute
            plug = FindMayaPlug("visibleInRefractions");
            if (!plug.isNull() && !plug.asBool())
            {
               visibility &= ~AI_RAY_REFRACTED;
            }
            
            plug = FindMayaPlug("diffuse_visibility");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiVisibleInDiffuse");
            }
            if (!plug.isNull() && !plug.asBool())
            {
               visibility &= ~AI_RAY_DIFFUSE;
            }
            
            plug = FindMayaPlug("glossy_visibility");
            if (plug.isNull())
            {
               plug = FindMayaPlug("aiVisibleInGlossy");
            }
            if (!plug.isNull() && !plug.asBool())
            {
               visibility &= ~AI_RAY_GLOSSY;
            }
            
            AiNodeSetByte(atNode, "visibility", visibility & 0xFF);
         }
      }
      
      if (attrsSet.find("sss_setname") == attrsEnd)
      {
         plug = FindMayaPlug("aiSssSetname");
         if (!plug.isNull() && plug.asString().length() > 0)
         {
            if (HasParameter(anodeEntry, "sss_setname", atNode, "constant STRING"))
            {
               AiNodeSetStr(atNode, "sss_setname", plug.asString().asChar());
            }
         }
      }
      
      // Set surface shader
      if (HasParameter(anodeEntry, "shader", atNode, "constant NODE"))
      {
         if (attrsSet.find("shader") == attrsEnd)
         {
            if (shadingEngine.object() != MObject::kNullObj)
            {
               AtNode *shader = ExportNode(shadingEngine.findPlug("message"));
               if (shader != NULL)
               {
                  const AtNodeEntry *entry = AiNodeGetNodeEntry(shader);
                  
                  if (AiNodeEntryGetType(entry) != AI_NODE_SHADER)
                  {
                     MGlobal::displayWarning("[mtoaScriptedTranslators] Node generated from \"" + shadingEngine.name() +
                                             "\" of type " + shadingEngine.typeName() + " for shader is not a shader but a " +
                                             MString(AiNodeEntryGetTypeName(entry)));
                  }
                  else
                  {
                     AiNodeSetPtr(atNode, "shader", shader);
                     
                     if (AiNodeLookUpUserParameter(atNode, "mtoa_shading_groups") == 0)
                     {
                        AiNodeDeclare(atNode, "mtoa_shading_groups", "constant ARRAY NODE");
                        AiNodeSetArray(atNode, "mtoa_shading_groups", AiArrayConvert(1, 1, AI_TYPE_NODE, &shader));
                     }
                  }
               }
            }
         }
      }
   }
   
   ExportLightLinking(atNode);
   
   MPlug plug = FindMayaPlug("aiTraceSets");
   if (!plug.isNull())
   {
      ExportTraceSets(atNode, plug);
   }
   
   // Call cleanup command on last export step
   
   if (!IsMotionBlurEnabled() || !IsLocalMotionBlurEnabled() || int(step) >= (int(GetNumMotionSteps()) - 1))
   {
      if (HasParameter(anodeEntry, "disp_padding", atNode))
      {
         float padding = AiNodeGetFlt(atNode, "disp_padding");
         
         AtPoint cmin = AiNodeGetPnt(atNode, "min");
         AtPoint cmax = AiNodeGetPnt(atNode, "max");
         
         cmin.x -= padding;
         cmin.y -= padding;
         cmin.z -= padding;
         cmax.x += padding;
         cmax.y += padding;
         cmax.z += padding;
         
         AiNodeSetPnt(atNode, "min", cmin.x, cmin.y, cmin.z);
         AiNodeSetPnt(atNode, "max", cmax.x, cmax.y, cmax.z);
      }
      
      if (cleanupCmd != "")
      {
         command = cleanupCmd += "((\"" + m_dagPath.partialPathName() + "\", \"";
         command += AiNodeGetName(atNode);
         command += "\"), ";
         
         if (!m_masterNode)
         {
            command += "None)";
         }
         else
         {
            command += "(\"" + GetMasterInstance().partialPathName() + "\", \"";
            command += AiNodeGetName(m_masterNode);
            command += "\"))";
         }
         
         status = MGlobal::executePythonCommand(command);
         
         if (!status)
         {
            AiMsgError("[mtoa.scriptedTranslators] Failed to cleanup node \"%s\".", node.name().asChar());
         }
      }
   }
}
                virtual void ExportUserAttrs( AtNode *node )
                {
                        // Get the optional attributes and export them as user vars

                        MPlug plug = FindMayaObjectPlug( "shaderAssignation" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "shaderAssignation", "constant STRING" );
                                AiNodeSetStr( node, "shaderAssignation", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "displacementAssignation" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "displacementAssignation", "constant STRING" );
                                AiNodeSetStr( node, "displacementAssignation", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "shaderAssignmentfile" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "shaderAssignmentfile", "constant STRING" );
                                AiNodeSetStr( node, "shaderAssignmentfile", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "overrides" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "overrides", "constant STRING" );
                                AiNodeSetStr( node, "overrides", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "overridefile" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "overridefile", "constant STRING" );
                                AiNodeSetStr( node, "overridefile", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "userAttributes" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "userAttributes", "constant STRING" );
                                AiNodeSetStr( node, "userAttributes", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "userAttributesfile" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "userAttributesfile", "constant STRING" );
                                AiNodeSetStr( node, "userAttributesfile", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "skipJson" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "skipJson", "constant BOOL" );   
                                AiNodeSetBool( node, "skipJson", plug.asBool() );
                        }

                        plug = FindMayaObjectPlug( "skipShaders" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "skipShaders", "constant BOOL" );   
                                AiNodeSetBool( node, "skipShaders", plug.asBool() );
                        }

                        plug = FindMayaObjectPlug( "skipOverrides" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "skipOverrides", "constant BOOL" );   
                                AiNodeSetBool( node, "skipOverrides", plug.asBool() );
                        }

                        plug = FindMayaObjectPlug( "skipUserAttributes" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "skipUserAttributes", "constant BOOL" );   
                                AiNodeSetBool( node, "skipUserAttributes", plug.asBool() );
                        }

                        plug = FindMayaObjectPlug( "skipDisplacements" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "skipDisplacements", "constant BOOL" );                          
                                AiNodeSetBool( node, "skipDisplacements", plug.asBool() );
                        }

                        plug = FindMayaObjectPlug( "objectPattern" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "objectPattern", "constant STRING" );
                                AiNodeSetStr( node, "objectPattern", plug.asString().asChar() );
                        }                       
                        
                        plug = FindMayaObjectPlug( "assShaders" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "assShaders", "constant STRING" );
                                AiNodeSetStr( node, "assShaders", plug.asString().asChar() );
                        }

                        plug = FindMayaObjectPlug( "radiusPoint" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "radiusPoint", "constant FLOAT" );
                                AiNodeSetFlt( node, "radiusPoint", plug.asFloat() );
                        }

                        plug = FindMayaObjectPlug( "radiusCurve" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "radiusCurve", "constant FLOAT" );
                                AiNodeSetFlt( node, "radiusCurve", plug.asFloat() );
                        }

                        plug = FindMayaObjectPlug( "modeCurve" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "modeCurve", "constant STRING" );

                                int modeCurveInt = plug.asInt();

                                if (modeCurveInt == 1)
                                   AiNodeSetStr(node, "modeCurve", "thick");
                                else if (modeCurveInt == 2)
                                   AiNodeSetStr(node, "modeCurve", "oriented");
                                else
                                   AiNodeSetStr(node, "modeCurve", "ribbon");                               
                        }

                        plug = FindMayaObjectPlug( "scaleVelocity" );
                        if( !plug.isNull() )
                        {
                                AiNodeDeclare( node, "scaleVelocity", "constant FLOAT" );
                                AiNodeSetFlt( node, "scaleVelocity", plug.asFloat() );
                        }

                }
                virtual void ExportProcedural( AtNode *node )
                {
                        // do basic node export
                        ExportMatrix( node, 0 );

                        // AiNodeSetPtr( node, "shader", arnoldShader(node) );


                        AiNodeSetInt( node, "visibility", ComputeVisibility() );

                        MPlug plug = FindMayaObjectPlug( "receiveShadows" );
                        if( !plug.isNull() )
                        {
                                AiNodeSetBool( node, "receive_shadows", plug.asBool() );
                        }

                        plug = FindMayaObjectPlug( "aiSelfShadows" );
                        if( !plug.isNull() )
                        {
                                AiNodeSetBool( node, "self_shadows", plug.asBool() );
                        }

                        plug = FindMayaObjectPlug( "aiOpaque" );
                        if( !plug.isNull() )
                        {
                                AiNodeSetBool( node, "opaque", plug.asBool() );
                        }

                        // now set the procedural-specific parameters

                        AiNodeSetBool( node, "load_at_init", true ); // just for now so that it can load the shaders at the right time

                        MFnDagNode fnDagNode( m_dagPath );
                        MBoundingBox bound = fnDagNode.boundingBox();

                        AiNodeSetPnt( node, "min", bound.min().x-m_dispPadding, bound.min().y-m_dispPadding, bound.min().z-m_dispPadding );
                        AiNodeSetPnt( node, "max", bound.max().x+m_dispPadding, bound.max().y, bound.max().z+m_dispPadding );

                        const char *dsoPath = getenv( "ALEMBIC_ARNOLD_PROCEDURAL_PATH" );
                        AiNodeSetStr( node, "dso",  dsoPath ? dsoPath : "bb_AlembicArnoldProcedural.so" );

                        // Set the parameters for the procedural

                        //abcFile path
                        MString abcFile = fnDagNode.findPlug("cacheFileName").asString().expandEnvironmentVariablesAndTilde();

                        //object path
                        MString objectPath = fnDagNode.findPlug("cacheGeomPath").asString();

                        //object pattern
                        MString objectPattern = "*";

                        plug = FindMayaObjectPlug( "objectPattern" );
                        if (!plug.isNull() )
                        {
                              if (plug.asString() != "")
                              {
                                objectPattern = plug.asString();
                              }
                        }

                        //object pattern
                        MString excludePattern = "";

                        plug = FindMayaObjectPlug( "excludePattern" );
                        if (!plug.isNull() )
                        {
                              if (plug.asString() != "")
                              {
                                excludePattern = plug.asString();
                              }
                        }

                        float shutterOpen = 0.0;
                        plug = FindMayaObjectPlug( "shutterOpen" );
                        if (!plug.isNull() )
                        {
                                shutterOpen = plug.asFloat();
                        }

                        float shutterClose = 0.0;
                        plug = FindMayaObjectPlug( "shutterClose" );
                        if (!plug.isNull() )
                        {
                                shutterClose = plug.asFloat();
                        }

                        float timeOffset = 0.0;
                        plug = FindMayaObjectPlug( "timeOffset" );
                        if (!plug.isNull() )
                        {
                                timeOffset = plug.asFloat();
                        }

                        int subDIterations = 0;
                        plug = FindMayaObjectPlug( "ai_subDIterations" );
                        if (!plug.isNull() )
                        {
                                subDIterations = plug.asInt();
                        }

                        MString nameprefix = "";
                        plug = FindMayaObjectPlug( "namePrefix" );
                        if (!plug.isNull() )
                        {
                                nameprefix = plug.asString();
                        }

                        // bool exportFaceIds = fnDagNode.findPlug("exportFaceIds").asBool();

                        bool makeInstance = true; // always on for now
                        plug = FindMayaObjectPlug( "makeInstance" );
                        if (!plug.isNull() )
                        {
                                makeInstance = plug.asBool();
                        }
                        
                        bool flipv = false; 
                        plug = FindMayaObjectPlug( "flipv" );
                        if (!plug.isNull() )
                        {
                                flipv = plug.asBool();
                        }

                        bool invertNormals = false; 
                        plug = FindMayaObjectPlug( "invertNormals" );
                        if (!plug.isNull() )
                        {
                                invertNormals = plug.asBool();
                        }
                        
                        short i_subDUVSmoothing = 1;
                        plug = FindMayaObjectPlug( "ai_subDUVSmoothing" );
                        if (!plug.isNull() )
                        {
                                i_subDUVSmoothing = plug.asShort();
                        }

                        MString  subDUVSmoothing;

                        switch (i_subDUVSmoothing)
                        {
                          case 0:
                            subDUVSmoothing = "pin_corners";
                            break;
                          case 1:
                            subDUVSmoothing = "pin_borders";
                            break;
                          case 2:
                            subDUVSmoothing = "linear";
                            break;
                          case 3:
                            subDUVSmoothing = "smooth";
                            break;
                          default :
                            subDUVSmoothing = "pin_corners";
                            break;
                        }

                        MTime curTime = MAnimControl::currentTime();
                        // fnDagNode.findPlug("time").getValue( frame );

                        // MTime frameOffset;
                        // fnDagNode.findPlug("timeOffset").getValue( frameOffset );

                        float time = curTime.as(MTime::kFilm)+timeOffset;

                        MString argsString;
                        if (objectPath != "|"){
                                argsString += "-objectpath ";
                                // convert "|" to "/"

                                argsString += MString(replace_all(objectPath,"|","/").c_str());
                        }
                        if (objectPattern != "*"){
                                argsString += "-pattern ";
                                argsString += objectPattern;
                        }
                        if (excludePattern != ""){
                                argsString += "-excludepattern ";
                                argsString += excludePattern;
                        }
                        if (shutterOpen != 0.0){
                                argsString += " -shutteropen ";
                                argsString += shutterOpen;
                        }
                        if (shutterClose != 0.0){
                                argsString += " -shutterclose ";
                                argsString += shutterClose;
                        }
                        if (subDIterations != 0){
                                argsString += " -subditerations ";
                                argsString += subDIterations;
                                argsString += " -subduvsmoothing ";
                                argsString += subDUVSmoothing;
                        }
                        if (makeInstance){
                                argsString += " -makeinstance ";
                        }
                        if (nameprefix != ""){
                                argsString += " -nameprefix ";
                                argsString += nameprefix;
                        }
                        if (flipv){
                                argsString += " -flipv ";
                        }
                        if (invertNormals){
                                argsString += " -invertNormals ";
                        }
                        argsString += " -filename ";
                        argsString += abcFile;
                        argsString += " -frame ";
                        argsString += time;

                        if (m_displaced){

                            argsString += " -disp_map ";
                            argsString += AiNodeGetName(m_dispNode);

                        }

                        AiNodeSetStr(node, "data", argsString.asChar());

                        ExportUserAttrs(node);

                        // Export light linking per instance
                        ExportLightLinking(node);

                }
static AtNode *MyGetNode(void *user_ptr, int i){

	cout << "BEGIN GET NODE" << endl;
	const McdMiddleData* params = static_cast<McdMiddleData*>(user_ptr);

	const char* g_assetsPath	= params->m_assetsPath;
	int g_agentID				= params->m_agentID;
	int g_ppID					= params->m_ppID;	
	int g_geoID					= params->m_geoID;	
	int g_poly0_subd1			= params->m_poly0_subd1;
	int g_motionBlur			= params->m_motionBlur;
	float g_motionShutter		= params->m_motionShutter; 
	const char* g_poseData		= params->m_poseData;	

	string agentFolder;
	string agentPath2,geoPath2,weightPath2;
	string currentLine;
	//long counter1;
	//long counter2;

#ifdef WIN32
	EnterCriticalSection(&g_cs1);
#else
	pthread_mutex_lock( &cs_mutex );
#endif

	if (!have_Data)
		initAssets(params);

#ifdef WIN32
	LeaveCriticalSection(&g_cs1);
#else
	pthread_mutex_unlock( &cs_mutex );
#endif


#ifdef MiarmyDebug
	ofstream logFile("e:/logFile.txt");
#endif

	// agent file:
	// /////////////////////////////////////////////////////////////////////////////
	// /////////////////////////////////////////////////////////////////////////////

	bool motionBlurDef = false;
	float motionShutter = 0.0f;
	if (g_motionBlur == 1) {
		motionBlurDef = true;
		motionShutter = g_motionShutter;
	}
	vector<float> poseDataRaw;
	vector<float> poseDataRawNext;

	McdUtils utils;
	utils.getPoseRawData(poseDataRaw,poseDataRawNext, motionBlurDef, g_poseData);

#ifdef MiarmyDebug
	logFile<<"pose data raw"<<endl;
	for (uint i = 0; i < poseDataRaw.size(); i++){
		logFile<<poseDataRaw[i]<<" ";
	}
#endif

	char tempBuf[10];
	string ppIdStr, agentTypeIdStr, geoTypeIdStr;
	string assetsFolder(g_assetsPath);
	sprintf(tempBuf, "%d", g_ppID);		ppIdStr			= tempBuf;
	sprintf(tempBuf, "%d", g_agentID);	agentTypeIdStr	= tempBuf;
	sprintf(tempBuf, "%d", g_geoID);	geoTypeIdStr	= tempBuf;

	string geomStr = "geom";
	geomStr = geomStr + ppIdStr;


	agentFolder =  agentFolderPre + agentTypeIdStr;

	//			d:/pp		   /	   McdAgentType0 /		 McdAgentMain.txt
	agentPath2= assetsFolder +  slash + agentFolder +  slash +  agentFile2;
	//		  d:/pp			 /		 McdAgentType0 /	   McdGeoFiles /		 McdGeo		  0	
	geoPath2= assetsFolder +  slash + agentFolder +  slash +  geoFolder +  slash +  geoFilePre + geoTypeIdStr;
	//		     d:/pp			/ 		McdAgentType0 /		  McdGeoFiles /		McdWeight			0
	weightPath2= assetsFolder +  slash + agentFolder +  slash +  geoFolder +  slash +  weightFilePre + geoTypeIdStr;

	// we already have agentPath2, geoPath2, and weightPath2, let's extract data:
	// get bone number and org pose 
	vector<McdMatrix> orgPoseMatrixList;
	vector<McdMatrix> currentPoseMatrixList;
	vector<McdMatrix> nextPoseMatrixList;
	int nbBone = (int)(agentFiles_Data[g_agentID][0] + .1f);

	utils.getPoseMatrices( orgPoseMatrixList,
						   currentPoseMatrixList,
					       nextPoseMatrixList,
					       agentFiles_Data,
					       poseDataRaw,
					       poseDataRawNext,
						   nbBone, g_agentID, motionBlurDef);

	// now get the weights 
	McdPointWeight weightList;
	int nbPoints = utils.getWeightsFromFile( weightList, weightPath2, weightFiles_Data, g_agentID, g_geoID );


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// session 3: get the data from geo file //////////////////////////////////////////////////////////////////////

	//bool startCollect = false;
	McdGeo geo;

	vector<float> currentPoints, nextPoints;	// modified from points;
	vector<float> currentNormals, nextNormals;	// modified from normals;

	utils.getGeoFromFile( geo, geoFiles_Data, currentNormals, nextNormals, g_geoID, g_agentID, motionBlurDef);

	// statistic final information:
	int nbNormalFromRib = geo.nidxs.size();

	if (geo.points.size() / 3 != nbPoints) return 0;
	
#ifdef MiarmyDebug
	logFile << "-----------> points number from pp exportor:  >>>>" <<nbPoints<<endl;
	logFile << "-----------> normals number from rib:  >>>>" <<nbNormalFromRib<<endl;
#endif
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// session 4: modify the data in vector ///////////////////////////////////////////////////////////////////////


	// modify points by skinning info
#ifdef MiarmyDebug
	logFile << "*************start to deal with points********************************************"<<endl;
#endif

	McdPoint currentPoint;
	McdPoint nextPoint;
	int nbWeights;
	int boneID; float weight;
	for (int i = 0; i < nbPoints; i++)
	{
		currentPoint.x = geo.points[i*3]; currentPoint.y = geo.points[i*3 + 1]; currentPoint.z = geo.points[i*3 + 2];
		if (motionBlurDef)
		{
			nextPoint = currentPoint;
		}

		nbWeights = weightList.nbWeights[i];
#ifdef MiarmyDebug
		logFile<<"-------------------------------"<<endl;
		logFile << "point pre info: "<<currentPoint.x<<"   "<<currentPoint.y<<"   "<<currentPoint.z<<"   "<<endl;
#endif

		McdMatrix deformMat; deformMat = deformMat * 0.0f; // zero out
		McdMatrix deformMatNext; deformMatNext = deformMatNext * 0.0f; // zero out
		for (int j = 0; j < nbWeights; j++)
		{
			boneID = weightList.pointIdList[i][j];
			weight = weightList.pointWeights[i][j];
			
#ifdef MiarmyDebug
			logFile <<"pointid: "<<i<<" boneId: "<<boneID<<" weight: "<<weight<<endl;
#endif 
			deformMat = deformMat + (orgPoseMatrixList[boneID] * currentPoseMatrixList[boneID]) * weight;
			if (motionBlurDef)
			{
				deformMatNext = deformMatNext + (orgPoseMatrixList[boneID] * nextPoseMatrixList[boneID]) * weight;
			}

		}

		currentPoint = currentPoint * deformMat;
		if (motionBlurDef)
		{
			nextPoint = nextPoint * deformMatNext;
		}

		// put result to points:
		currentPoints.push_back(currentPoint.x); currentPoints.push_back(currentPoint.y); currentPoints.push_back(currentPoint.z);
		if (motionBlurDef)
		{
			nextPoints.push_back(nextPoint.x); nextPoints.push_back(nextPoint.y); nextPoints.push_back(nextPoint.z);
		}

#ifdef MiarmyDebug
		logFile << "result point info: "<<currentPoints[i*3]<<"   "<<currentPoints[i*3+1]<<"   "<<currentPoints[i*3+2]<<"   "<<endl;
		if (motionBlurDef)
			logFile << "next point info: "<<nextPoints[i*3]<<"   "<<nextPoints[i*3+1]<<"   "<<nextPoints[i*3+2]<<"   "<<endl;
#endif
	}

	// modify normal by skinning info
	McdVector currentNormal, nextNormal;
	int normalPointID, normalID;
	for (int i = 0; i < nbNormalFromRib; i++){
		normalID = geo.nidxs[i];

		currentNormal.x = geo.normals[normalID*3]; currentNormal.y = geo.normals[normalID*3+1]; currentNormal.z = geo.normals[normalID*3+2];
		if (motionBlurDef){
			nextNormal.x = geo.normals[normalID*3]; nextNormal.y = geo.normals[normalID*3+1]; nextNormal.z = geo.normals[normalID*3+2];
		}

		normalPointID = geo.vidxs[i];

		nbWeights = weightList.nbWeights[normalPointID];
#ifdef MiarmyDebug
		logFile << "point normal info: "<<currentNormal.x<<"   "<<currentNormal.y<<"   "<<currentNormal.z<<"   "<<endl;

		if (motionBlurDef){
			logFile << "next point normal info: "<<nextNormal.x<<"   "<<nextNormal.y<<"   "<<nextNormal.z<<"   "<<endl;
		}
#endif

		McdMatrix deformMat; deformMat = deformMat * 0.0f; // zero out
		McdMatrix deformMatNext; deformMatNext = deformMatNext * 0.0f; // zero out
		for (int j = 0; j < nbWeights; j++){
			
			boneID = weightList.pointIdList[normalPointID][j];
			weight = weightList.pointWeights[normalPointID][j];

			deformMat = deformMat + (orgPoseMatrixList[boneID] * currentPoseMatrixList[boneID]) * weight;
			if (motionBlurDef){
				deformMat = deformMat + (orgPoseMatrixList[boneID] * nextPoseMatrixList[boneID]) * weight;
			}

		}

		currentNormal = currentNormal * deformMat;
		currentNormal.normalize();
		if (motionBlurDef){
			nextNormal = nextNormal * deformMatNext;
			nextNormal.normalize();
		}

		// put result to normals:
		currentNormals[normalID*3] = currentNormal.x; currentNormals[normalID*3+1] = currentNormal.y; currentNormals[normalID*3+2] = currentNormal.z; 
		if (motionBlurDef){
			nextNormals[normalID*3] = nextNormal.x; nextNormals[normalID*3+1] = nextNormal.y; nextNormals[normalID*3+2] = nextNormal.z; 
		}

#ifdef MiarmyDebug
		logFile << "result normal info: "<<currentNormals[normalID*3]<<"   "<<currentNormals[normalID*3+1]<<"   "<<currentNormals[normalID*3+2]<<"   "<<endl;
		if (motionBlurDef)
			logFile << "result next normal info: "<<nextNormals[normalID*3]<<"   "<<nextNormals[normalID*3+1]<<"   "<<nextNormals[normalID*3+2]<<"   "<<endl;
#endif
	}

#ifdef MiarmyDebug
	logFile <<endl<< "----------------------- END -----------------------: ";
	logFile.close();
#endif

	AtArray *nsidesArray;
	AtArray *vidxsArray;
	AtArray *nidxsArray;
	AtArray *uvidxsArray;
	AtArray *vlistArray;
	AtArray *nlistArray;
	AtArray *uvlistArray;

	// proc geo: 
	if (!motionBlurDef){
		nsidesArray = AiArrayAllocate(geo.nsides.size(), 1, AI_TYPE_UINT);
		vidxsArray = AiArrayAllocate(geo.vidxs.size(), 1, AI_TYPE_UINT);
		nidxsArray = AiArrayAllocate(geo.nidxs.size(), 1, AI_TYPE_UINT);
		uvidxsArray = AiArrayAllocate(geo.uvidxs.size(), 1, AI_TYPE_UINT);
		vlistArray = AiArrayAllocate(geo.points.size() / 3, 1, AI_TYPE_POINT);
		nlistArray = AiArrayAllocate(geo.normals.size() / 3, 1, AI_TYPE_VECTOR);
		uvlistArray = AiArrayAllocate(geo.uvlist.size() / 2, 1, AI_TYPE_POINT2);

		uint i;
		AtPoint tempPoint; AtVector tempNormal; AtPoint2 tempPoint2;
		uint nbPnts = currentPoints.size() / 3;
		uint nbNormals = currentNormals.size() / 3;
		uint nbUVs = geo.uvlist.size() / 2;
		for (i = 0; i < geo.nsides.size(); i++)
			AiArraySetUInt(nsidesArray, i, geo.nsides[i]);
		for (i = 0; i < geo.vidxs.size(); i++)
			AiArraySetUInt(vidxsArray, i, geo.vidxs[i]);
		for (i = 0; i < geo.nidxs.size(); i++)
			AiArraySetUInt(nidxsArray, i, geo.nidxs[i]);
		for (i = 0; i < geo.uvidxs.size(); i++)
			AiArraySetUInt(uvidxsArray, i, geo.uvidxs[i]);
		for (i = 0; i < nbPnts; i++){
			tempPoint.x = currentPoints[i*3]; tempPoint.y = currentPoints[i*3+1]; tempPoint.z = currentPoints[i*3+2];
			AiArraySetPnt(vlistArray, i, tempPoint);
		}
		for (i = 0; i < nbNormals; i++){
			tempNormal.x = currentNormals[i*3]; tempNormal.y = currentNormals[i*3+1]; tempNormal.z = currentNormals[i*3+2];
			AiArraySetPnt(nlistArray, i, tempNormal);
		}
		for (i = 0; i < nbUVs; i++){
			tempPoint2.x = geo.uvlist[i*2]; tempPoint2.y = geo.uvlist[i*2+1];
			AiArraySetPnt2(uvlistArray, i, tempPoint2);
		}


	} else {
		nsidesArray = AiArrayAllocate(geo.nsides.size(), 1, AI_TYPE_UINT);
		vidxsArray = AiArrayAllocate(geo.vidxs.size(), 1, AI_TYPE_UINT);
		nidxsArray = AiArrayAllocate(geo.nidxs.size(), 1, AI_TYPE_UINT);
		uvidxsArray = AiArrayAllocate(geo.uvidxs.size(), 1, AI_TYPE_UINT);
		vlistArray = AiArrayAllocate(geo.points.size() / 3, 2, AI_TYPE_POINT);
		nlistArray = AiArrayAllocate(geo.normals.size() / 3, 2, AI_TYPE_VECTOR);
		uvlistArray = AiArrayAllocate(geo.uvlist.size() / 2, 1, AI_TYPE_POINT2);

		uint i;
		AtPoint tempPoint; AtVector tempNormal; AtPoint2 tempPoint2;
		uint nbPnts = currentPoints.size() / 3;
		uint nbNormals = currentNormals.size() / 3;
		uint nbUVs = geo.uvlist.size() / 2;
		for (i = 0; i < geo.nsides.size(); i++)
			AiArraySetUInt(nsidesArray, i, geo.nsides[i]);
		for (i = 0; i < geo.vidxs.size(); i++)
			AiArraySetUInt(vidxsArray, i, geo.vidxs[i]);
		for (i = 0; i < geo.nidxs.size(); i++)
			AiArraySetUInt(nidxsArray, i, geo.nidxs[i]);
		for (i = 0; i < geo.uvidxs.size(); i++)
			AiArraySetUInt(uvidxsArray, i, geo.uvidxs[i]);
		for (i = 0; i < nbPnts; i++){
			tempPoint.x = currentPoints[i*3]; tempPoint.y = currentPoints[i*3+1]; tempPoint.z = currentPoints[i*3+2];
			AiArraySetPnt(vlistArray, i, tempPoint);
		}
		for (i = 0; i < nbNormals; i++){
			tempNormal.x = currentNormals[i*3]; tempNormal.y = currentNormals[i*3+1]; tempNormal.z = currentNormals[i*3+2];
			AiArraySetPnt(nlistArray, i, tempNormal);
		}
		for (i = 0; i < nbPnts; i++){
			tempPoint.x = nextPoints[i*3]; tempPoint.y = nextPoints[i*3+1]; tempPoint.z = nextPoints[i*3+2];
			AiArraySetPnt(vlistArray, i+nbPnts, tempPoint);
		}
		for (i = 0; i < nbNormals; i++){
			tempNormal.x = nextNormals[i*3]; tempNormal.y = nextNormals[i*3+1]; tempNormal.z = nextNormals[i*3+2];
			AiArraySetPnt(nlistArray, i+nbNormals, tempNormal);
		}
		for (i = 0; i < nbUVs; i++){
			tempPoint2.x = geo.uvlist[i*2]; tempPoint2.y = geo.uvlist[i*2+1];
			AiArraySetPnt2(uvlistArray, i, tempPoint2);
		}
	}

	AtNode *meshNode = AiNode("polymesh");
	AiNodeSetStr(meshNode, "name", geomStr.c_str());
	AiNodeSetArray(meshNode, "nsides", nsidesArray);
	AiNodeSetArray(meshNode, "vidxs", vidxsArray);
	if (g_poly0_subd1 != 1)
		AiNodeSetArray(meshNode, "nidxs", nidxsArray);
	AiNodeSetArray(meshNode, "uvidxs", uvidxsArray);
	AiNodeSetArray(meshNode, "vlist", vlistArray);
	if (g_poly0_subd1 != 1)
		AiNodeSetArray(meshNode, "nlist", nlistArray);
	AiNodeSetArray(meshNode, "uvlist", uvlistArray);
	AiNodeSetBool(meshNode, "smoothing", true);

	if (g_poly0_subd1 == 1)
		AiNodeSetStr(meshNode, "subdiv_type", "catclark");


	lockup = false;
	cout << "END GET NODE" << endl;
	return meshNode;
}