void AlembicVisibilityController::GetValueLocalTime(TimeValue t, void *ptr,
                                                    Interval &valid,
                                                    GetSetMethod method)
{
  ESS_CPP_EXCEPTION_REPORTING_START
  ESS_PROFILE_FUNC();

  Interval interval = FOREVER;

  MCHAR const *strPath = NULL;
  this->pblock->GetValue(AlembicVisibilityController::ID_PATH, t, strPath,
                         interval);

  MCHAR const *strIdentifier = NULL;
  this->pblock->GetValue(AlembicVisibilityController::ID_IDENTIFIER, t,
                         strIdentifier, interval);

  float fTime;
  this->pblock->GetValue(AlembicVisibilityController::ID_TIME, t, fTime,
                         interval);

  BOOL bMuted;
  this->pblock->GetValue(AlembicVisibilityController::ID_MUTED, t, bMuted,
                         interval);

  if (bMuted || !strPath || !strIdentifier) {
    return;
  }

  std::string szPath = EC_MCHAR_to_UTF8(strPath);
  std::string szIdentifier = EC_MCHAR_to_UTF8(strIdentifier);

  AbcG::IObject iObj = getObjectFromArchive(szPath, szIdentifier);

  if (!iObj.valid()) {
    return;
  }

  alembic_fillvis_options visOptions;
  visOptions.pIObj = &iObj;
  visOptions.dTicks = t;
  visOptions.bOldVisibility = m_bOldVisibility;
  AlembicImport_FillInVis(visOptions);

  float fBool = visOptions.bVisibility ? 1.0f : 0.0f;
  m_bOldVisibility = visOptions.bVisibility;

  valid = interval;

  if (method == CTRL_ABSOLUTE) {
    float *fInVal = (float *)ptr;
    *fInVal = fBool;
  }
  else {  // CTRL_RELATIVE
    float *fInVal = (float *)ptr;
    *fInVal = fBool * (*fInVal);
  }

  ESS_CPP_EXCEPTION_REPORTING_END
}
int createAlembicObject(AbcG::IObject &iObj, INode **pMaxNode,
                        alembic_importoptions &options, std::string &file)
{
  AbcA::MetaData mdata = iObj.getMetaData();

  int ret = alembic_success;
  // if(AbcG::IXform::matches(iObj.getMetaData())) //Transform
  //{
  //	ESS_LOG_INFO( "AlembicImport_XForm: " << objects[j].getFullName() );
  //	int ret = AlembicImport_PolyMesh(file, iObj, options, pMaxNode);
  //}
  if (AbcG::IPolyMesh::matches(mdata) ||
      AbcG::ISubD::matches(mdata)) {  // PolyMesh / SubD
    ESS_LOG_INFO("AlembicImport_PolyMesh: " << iObj.getFullName());
    ret = AlembicImport_PolyMesh(file, iObj, options, pMaxNode);
  }
  else if (AbcG::ICamera::matches(mdata)) {  // Camera
    ESS_LOG_INFO("AlembicImport_Camera: " << iObj.getFullName());
    ret = AlembicImport_Camera(file, iObj, options, pMaxNode);
  }
  else if (AbcG::IPoints::matches(mdata)) {  // Points
    ESS_LOG_INFO("AlembicImport_Points: " << iObj.getFullName());
    ret = AlembicImport_Points(file, iObj, options, pMaxNode);
  }
  else if (AbcG::ICurves::matches(mdata)) {  // Curves
    if (options.loadCurvesAsNurbs) {
      ESS_LOG_INFO("AlembicImport_Nurbs: " << iObj.getFullName());
      ret = AlembicImport_NURBS(file, iObj, options, pMaxNode);
    }
    else {
      ESS_LOG_INFO("AlembicImport_Shape: " << iObj.getFullName());
      ret = AlembicImport_Shape(file, iObj, options, pMaxNode);
    }
  }
  else if (AbcG::ILight::matches(mdata)) {  // Light
    ESS_LOG_INFO("AlembicImport_Light: " << iObj.getFullName());
    ret = AlembicImport_Light(file, iObj, options, pMaxNode);
  }
  else if (AbcM::IMaterial::matches(mdata)) {
    ESS_LOG_WARNING(
        "Alembic IMaterial not yet supported: " << iObj.getFullName());
  }
  else {  // NURBS
    if (options.failOnUnsupported) {
      ESS_LOG_ERROR("Alembic data type not supported: " << iObj.getFullName());
      return alembic_failure;
    }
    else {
      ESS_LOG_WARNING(
          "Alembic data type not supported: " << iObj.getFullName());
    }
  }
  return ret;
}
Abc::ICompoundProperty getArbGeomParams(const AbcG::IObject& iObj,
                                        AbcA::TimeSamplingPtr& timeSampling,
                                        int& nSamples)
{
  if (AbcG::IXform::matches(iObj.getMetaData())) {
    AbcG::IXform obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else if (AbcG::IPolyMesh::matches(iObj.getMetaData())) {
    AbcG::IPolyMesh obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else if (AbcG::ISubD::matches(iObj.getMetaData())) {
    AbcG::ISubD obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else if (AbcG::ICamera::matches(iObj.getMetaData())) {
    AbcG::ICamera obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else if (AbcG::IPoints::matches(iObj.getMetaData())) {
    AbcG::IPoints obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else if (AbcG::ICurves::matches(iObj.getMetaData())) {
    AbcG::ICurves obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else if (AbcG::ILight::matches(iObj.getMetaData())) {
    AbcG::ILight obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else if (AbcG::INuPatch::matches(iObj.getMetaData())) {
    AbcG::INuPatch obj(iObj, Abc::kWrapExisting);
    timeSampling = obj.getSchema().getTimeSampling();
    nSamples = (int)obj.getSchema().getNumSamples();
    return obj.getSchema().getArbGeomParams();
  }
  else {
    ESS_LOG_WARNING("Could not read ArgGeomParams from " << iObj.getFullName());
    return Abc::ICompoundProperty();
  }
}
Abc::ICompoundProperty AbcNodeUtils::getUserProperties(
    const AbcG::IObject& iObj)
{
  if (AbcG::IXform::matches(iObj.getMetaData())) {
    AbcG::IXform obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else if (AbcG::IPolyMesh::matches(iObj.getMetaData())) {
    AbcG::IPolyMesh obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else if (AbcG::ISubD::matches(iObj.getMetaData())) {
    AbcG::ISubD obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else if (AbcG::ICamera::matches(iObj.getMetaData())) {
    AbcG::ICamera obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else if (AbcG::IPoints::matches(iObj.getMetaData())) {
    AbcG::IPoints obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else if (AbcG::ICurves::matches(iObj.getMetaData())) {
    AbcG::ICurves obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else if (AbcG::ILight::matches(iObj.getMetaData())) {
    AbcG::ILight obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else if (AbcG::INuPatch::matches(iObj.getMetaData())) {
    AbcG::INuPatch obj(iObj, Abc::kWrapExisting);
    return obj.getSchema().getUserProperties();
  }
  else {
    ESS_LOG_WARNING("Could not read ArgGeomParams from " << iObj.getFullName());
    return Abc::ICompoundProperty();
  }
}
void AbcNodeUtils::printObjectProperties(AbcG::IObject iObj, int options)
{
  bool bProp = options & ObjectPrint::PROPERTIES;
  bool bUserProp = options & ObjectPrint::USER_PROPERTIES;
  bool bGeomProp = options & ObjectPrint::ARB_GEOM_PROPERTIES;

  if (bProp) {
    Abc::ICompoundProperty props = iObj.getProperties();
    printCompoundProperty(props);
  }

  if (bUserProp) {
    Abc::ICompoundProperty userProps = getUserProperties(iObj);
    printCompoundProperty(userProps);
  }

  if (bGeomProp) {
    Abc::ICompoundProperty arbGeom = getArbGeomParams(iObj);
    printCompoundProperty(arbGeom);
  }
}
void AlembicNurbsModifier::ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node) 
{
	ESS_CPP_EXCEPTION_REPORTING_START
ESS_PROFILE_FUNC();

	Interval interval = FOREVER;//os->obj->ObjectValidity(t);
	//ESS_LOG_INFO( "Interval Start: " << interval.Start() << " End: " << interval.End() );

    MCHAR const* strPath = NULL;
	this->pblock->GetValue( AlembicNurbsModifier::ID_PATH, t, strPath, interval);

	MCHAR const* strIdentifier = NULL;
	this->pblock->GetValue( AlembicNurbsModifier::ID_IDENTIFIER, t, strIdentifier, interval);
 
	float fTime;
	this->pblock->GetValue( AlembicNurbsModifier::ID_TIME, t, fTime, interval);

	BOOL bMuted;
	this->pblock->GetValue( AlembicNurbsModifier::ID_MUTED, t, bMuted, interval);
	
	BOOL bIgnoreSubframeSamples;
	this->pblock->GetValue( AlembicNurbsModifier::ID_IGNORE_SUBFRAME_SAMPLES, t, bIgnoreSubframeSamples, interval);

	//ESS_LOG_INFO( "AlembicNurbsModifier::ModifyObject strPath: " << strPath << " strIdentifier: " << strIdentifier << " fTime: " << fTime << 
	//	" bTopology: " << bTopology << " bGeometry: " << bGeometry << " bNormals: " << bNormals << " bUVs: " << bUVs << " bMuted: " << bMuted );

	if( bMuted || !strPath || !strIdentifier) {
		return;
	}

	std::string szPath = EC_MCHAR_to_UTF8( strPath );
	std::string szIdentifier = EC_MCHAR_to_UTF8( strIdentifier );

	AbcG::IObject iObj;
	try {
		iObj = getObjectFromArchive(szPath, szIdentifier);
	} catch( std::exception exp ) {
        extern bool g_hasModifierErrorOccurred;
        g_hasModifierErrorOccurred = true;
		ESS_LOG_ERROR( "Can not open Alembic data stream.  Path: " << szPath << " identifier: " << szIdentifier << " reason: " << exp.what() );
		return;
	}

	if(!iObj.valid()) {
        extern bool g_hasModifierErrorOccurred;
        g_hasModifierErrorOccurred = true;
		ESS_LOG_ERROR( "Not a valid Alembic data stream.  Path: " << szPath << " identifier: " << szIdentifier );
		return;
	}


	
	alembic_NURBSload_options options;
    options.pIObj =  &iObj;
    options.dTicks = GetTimeValueFromSeconds( fTime );
 
	if(bIgnoreSubframeSamples){
		options.nDataFillFlags |= ALEMBIC_DATAFILL_IGNORE_SUBFRAME_SAMPLES;
	}

	//SClass_ID superClassID = os->obj->SuperClassID();
	Class_ID classID = os->obj->ClassID();

    if(classID == EDITABLE_SURF_CLASS_ID){
        options.pObject = os->obj;
    }
	else {
        extern bool g_hasModifierErrorOccurred;
        g_hasModifierErrorOccurred = true;
		ESS_LOG_ERROR( "Can not convert internal object data into a ShapeObject, confused. (2)" );
	}

   try {
		AlembicImport_LoadNURBS_Internal(options);
   }
   catch(std::exception exp ) {
        extern bool g_hasModifierErrorOccurred;
        g_hasModifierErrorOccurred = true;
		ESS_LOG_ERROR( "Error reading shape from Alembic data stream.  Path: " << strPath << " identifier: " << strIdentifier << " reason: " << exp.what() );
		return;
   }

   os->obj = options.pObject;
   //os->obj->UnlockObject();

	os->obj->SetChannelValidity(TOPO_CHAN_NUM, interval);
	os->obj->SetChannelValidity(GEOM_CHAN_NUM, interval);
	os->obj->SetChannelValidity(TEXMAP_CHAN_NUM, interval);
	os->obj->SetChannelValidity(MTL_CHAN_NUM, interval);
	os->obj->SetChannelValidity(SELECT_CHAN_NUM, interval);
	os->obj->SetChannelValidity(SUBSEL_TYPE_CHAN_NUM, interval);
	os->obj->SetChannelValidity(DISP_ATTRIB_CHAN_NUM, interval);

   	ESS_CPP_EXCEPTION_REPORTING_END
}
void AlembicSplineGeomModifier::ModifyObject(TimeValue t, ModContext &mc,
                                             ObjectState *os, INode *node)
{
  ESS_CPP_EXCEPTION_REPORTING_START
  ESS_PROFILE_FUNC();

  Interval interval = FOREVER;  // os->obj->ObjectValidity(t);
  // ESS_LOG_INFO( "Interval Start: " << interval.Start() << " End: " <<
  // interval.End() );

  MCHAR const *strPath = NULL;
  this->pblock->GetValue(AlembicSplineGeomModifier::ID_PATH, t, strPath,
                         interval);

  MCHAR const *strIdentifier = NULL;
  this->pblock->GetValue(AlembicSplineGeomModifier::ID_IDENTIFIER, t,
                         strIdentifier, interval);

  float fTime;
  this->pblock->GetValue(AlembicSplineGeomModifier::ID_TIME, t, fTime,
                         interval);

  BOOL bTopology = false;
  BOOL bGeometry = true;
  float fGeoAlpha = 1.0f;
  BOOL bNormals = false;
  BOOL bUVs = true;

  BOOL bMuted;
  this->pblock->GetValue(AlembicSplineGeomModifier::ID_MUTED, t, bMuted,
                         interval);

  // ESS_LOG_INFO( "AlembicSplineGeomModifier::ModifyObject strPath: " <<
  // strPath << " strIdentifier: " << strIdentifier << " fTime: " << fTime <<
  //	" bTopology: " << bTopology << " bGeometry: " << bGeometry << "
  // bNormals: " << bNormals << " bUVs: " << bUVs << " bMuted: " << bMuted );

  if (bMuted || !strPath || !strIdentifier) {
    return;
  }

  std::string szPath = EC_MCHAR_to_UTF8(strPath);
  std::string szIdentifier = EC_MCHAR_to_UTF8(strIdentifier);

  AbcG::IObject iObj;
  try {
    iObj = getObjectFromArchive(szPath, szIdentifier);
  }
  catch (std::exception exp) {
    extern bool g_hasModifierErrorOccurred;
    g_hasModifierErrorOccurred = true;
    ESS_LOG_ERROR("Can not open Alembic data stream.  Path: "
                  << szPath << " identifier: " << szIdentifier
                  << " reason: " << exp.what());
    return;
  }

  if (!iObj.valid()) {
    extern bool g_hasModifierErrorOccurred;
    g_hasModifierErrorOccurred = true;
    ESS_LOG_ERROR("Not a valid Alembic data stream.  Path: "
                  << szPath << " identifier: " << szIdentifier);
    return;
  }

  ShapeObject *shape = (ShapeObject *)os->obj;

  BezierShape bezierShape;
  PolyShape polyShape;

  alembic_fillshape_options options;
  options.pIObj = &iObj;
  options.pShapeObject = shape;
  options.dTicks = GetTimeValueFromSeconds(fTime);
  options.nDataFillFlags = 0;
  options.nDataFillFlags |= ALEMBIC_DATAFILL_VERTEX;
  options.nDataFillFlags |= ALEMBIC_DATAFILL_BOUNDINGBOX;

  SClass_ID superClassID = os->obj->SuperClassID();
  Class_ID classID = os->obj->ClassID();
  if (superClassID != SHAPE_CLASS_ID) {
    extern bool g_hasModifierErrorOccurred;
    g_hasModifierErrorOccurred = true;
    ESS_LOG_ERROR(
        "Can not convert internal object data into a ShapeObject, confused. "
        "(1)");
    return;
  }

  if (classID == Class_ID(SPLINESHAPE_CLASS_ID, 0)) {
    SplineShape *pSplineShape = (SplineShape *)os->obj;
    options.pBezierShape = &pSplineShape->shape;
  }
  else if (classID == Class_ID(LINEARSHAPE_CLASS_ID, 0)) {
    LinearShape *pLinearShape = (LinearShape *)os->obj;
    options.pPolyShape = &pLinearShape->shape;
  }
  else {
    extern bool g_hasModifierErrorOccurred;
    g_hasModifierErrorOccurred = true;
    ESS_LOG_ERROR(
        "Can not convert internal object data into a ShapeObject, confused. "
        "(2)");
  }

  try {
    AlembicImport_FillInShape(options);
  }
  catch (std::exception exp) {
    extern bool g_hasModifierErrorOccurred;
    g_hasModifierErrorOccurred = true;
    ESS_LOG_ERROR("Error reading shape from Alembic data stream.  Path: "
                  << strPath << " identifier: " << strIdentifier
                  << " reason: " << exp.what());
    return;
  }

  // update the validity channel
  if (bTopology) {
    os->obj->UpdateValidity(TOPO_CHAN_NUM, interval);
    os->obj->UpdateValidity(GEOM_CHAN_NUM, interval);
  }
  if (bGeometry) {
    os->obj->UpdateValidity(GEOM_CHAN_NUM, interval);
  }

  ESS_CPP_EXCEPTION_REPORTING_END
}
int importAlembicScene(AbcArchiveCache *pArchiveCache,
                       AbcObjectCache *pRootObjectCache,
                       alembic_importoptions &options, std::string &file,
                       progressUpdate &progress,
                       std::map<std::string, bool> &nodeFullPaths)
{
  std::vector<stackElement> sceneStack;
  sceneStack.reserve(200);
  for (size_t j = 0; j < pRootObjectCache->childIdentifiers.size(); j++) {
    sceneStack.push_back(stackElement(
        &(pArchiveCache->find(pRootObjectCache->childIdentifiers[j])->second)));
  }

  while (!sceneStack.empty()) {
    stackElement sElement = sceneStack.back();
    sceneStack.pop_back();
    Abc::IObject &iObj = sElement.pObjectCache->obj;
    INode *pParentMaxNode = sElement.pParentMaxNode;

    if (!iObj.valid()) {
      return alembic_failure;
    }

    const std::string fullname = iObj.getFullName();
    const std::string pname = (pParentMaxNode)
                                  ? EC_MCHAR_to_UTF8(pParentMaxNode->GetName())
                                  : std::string("");
    const std::string name = iObj.getName();

    ESS_LOG_INFO("Importing " << fullname);

    bool bCreateDummyNode = false;
    int mergedGeomNodeIndex = -1;
    AbcObjectCache *pMergedObjectCache = NULL;
    getMergeInfo(pArchiveCache, sElement.pObjectCache, bCreateDummyNode,
                 mergedGeomNodeIndex, &pMergedObjectCache);

    INode *pMaxNode =
        NULL;  // the newly create node, which may be a merged node
    INode *pExistingNode = NULL;
    int keepTM = 1;  // I don't remember why this needed to be set in some
    // cases.

    bool bCreateNode = true;

    if (!nodeFullPaths.empty()) {
      if (mergedGeomNodeIndex != -1) {
        AbcG::IObject mergedGeomChild = pMergedObjectCache->obj;
        bCreateNode = nodeFullPaths.find(mergedGeomChild.getFullName()) !=
                      nodeFullPaths.end();
      }
      else {
        bCreateNode = nodeFullPaths.find(fullname) != nodeFullPaths.end();
      }
    }

    if (bCreateNode) {
      // if we are about to merge a camera with its parent transform, force it
      // to create a dummy node instead if the camera's
      // transform also has children. This is done to prevent the camera
      // correction matrix from being applied to the other children
      if (!bCreateDummyNode && pMergedObjectCache &&
          sElement.pObjectCache->childIdentifiers.size() > 1 &&
          AbcG::ICamera::matches(pMergedObjectCache->obj.getMetaData())) {
        bCreateDummyNode = true;
        mergedGeomNodeIndex = -1;
      }

      if (bCreateDummyNode) {
        std::string importName = removeXfoSuffix(iObj.getName());

        pExistingNode = GetChildNodeFromName(importName, pParentMaxNode);
        if (options.attachToExisting && pExistingNode) {
          pMaxNode = pExistingNode;

          // see if a controller already exists, and then delete it

          int ret = AlembicImport_XForm(pParentMaxNode, pMaxNode, iObj, NULL,
                                        file, options);
          if (ret != 0) {
            return ret;
          }
        }  // only create node if either attachToExisting is false or it is true
        // and the object does not already exist
        else {
          int ret =
              AlembicImport_DummyNode(iObj, options, &pMaxNode, importName);
          if (ret != 0) {
            return ret;
          }

          ret = AlembicImport_XForm(pParentMaxNode, pMaxNode, iObj, NULL, file,
                                    options);
          if (ret != 0) {
            return ret;
          }
        }
      }
      else {
        if (mergedGeomNodeIndex !=
            -1) {  // we are merging, so look at the child geometry node
          AbcG::IObject mergedGeomChild = pMergedObjectCache->obj;
          std::string importName =
              removeXfoSuffix(iObj.getName());  // mergedGeomChild.getName());
          pExistingNode = GetChildNodeFromName(importName, pParentMaxNode);
          if (options.attachToExisting && pExistingNode) {
            pMaxNode = pExistingNode;
          }  // only create node if either attachToExisting is false or it is
          // true and the object does not already exist

          int ret =
              createAlembicObject(mergedGeomChild, &pMaxNode, options, file);
          if (ret != 0) {
            return ret;
          }
          if (pMaxNode != NULL) {
            ret = AlembicImport_XForm(pParentMaxNode, pMaxNode, iObj,
                                      &mergedGeomChild, file, options);
            if (ret != 0) {
              return ret;
            }
          }
        }
        else {  // geometry node(s) under a dummy node (in pParentMaxNode)
          pExistingNode = GetChildNodeFromName(iObj.getName(), pParentMaxNode);
          if (options.attachToExisting && pExistingNode) {
            pMaxNode = pExistingNode;
          }  // only create node if either attachToExisting is false or it is
          // true and the object does not already exist

          int ret = createAlembicObject(iObj, &pMaxNode, options, file);
          if (ret != 0) {
            return ret;
          }

          // since the transform is the identity, should position relative to
          // parent
          keepTM = 0;

          if (AbcG::ICamera::matches(iObj.getMetaData())) {
            // apply camera adjustment matrix to the identity
            Matrix3 rotation(TRUE);
            rotation.RotateX(HALFPI);
            TimeValue zero(0);
            pMaxNode->SetNodeTM(zero, rotation);
          }

          // import identity matrix, since more than goemetry node share the
          // same transform
          // Should we just list MAX put a default position/scale/rotation
          // controller on?

          //	int ret = AlembicImport_XForm(pMaxNode, *piParentObj, file,
          // options);
        }

        if (options.failOnUnsupported) {
          if (!pMaxNode) {
            return alembic_failure;
          }
        }
      }
    }

    if (pMaxNode && pParentMaxNode && !pExistingNode) {
      pParentMaxNode->AttachChild(pMaxNode, keepTM);
    }

    progress.increment();
    progress.update();

    if (pMaxNode) {
      for (size_t j = 0; j < sElement.pObjectCache->childIdentifiers.size();
           j++) {
        AbcObjectCache *pChildObjectCache =
            &(pArchiveCache->find(sElement.pObjectCache->childIdentifiers[j])
                  ->second);
        if (NodeCategory::get(pChildObjectCache->obj) ==
            NodeCategory::UNSUPPORTED) {
          continue;  // skip over unsupported types
        }

        // I assume that geometry nodes are always leaf nodes. Thus, if we
        // merged a geometry node will its parent transform, we don't
        // need to push it to the stack.
        // A geometry node can't be combined with its transform node, the
        // transform node has other tranform nodes as children. These
        // nodes must be pushed.
        if (mergedGeomNodeIndex != j) {
          sceneStack.push_back(stackElement(pChildObjectCache, pMaxNode));
        }
      }
    }
  }

  return alembic_success;
}
int AlembicImport_Light(const std::string &path, AbcG::IObject& iObj, alembic_importoptions &options, INode** pMaxNode)
{
//#define OMNI_LIGHT_CLASS_ID  		0x1011
//#define SPOT_LIGHT_CLASS_ID  		0x1012
//#define DIR_LIGHT_CLASS_ID  		0x1013
//#define FSPOT_LIGHT_CLASS_ID  		0x1014
//#define TDIR_LIGHT_CLASS_ID  		0x1015

//#define OMNI_LIGHT		0	// Omnidirectional
//#define TSPOT_LIGHT		1	// Targeted
//#define DIR_LIGHT		2	// Directional
//#define FSPOT_LIGHT		3	// Free
//#define TDIR_LIGHT		4   // Targeted directional

   if(options.attachToExisting){
      ESS_LOG_WARNING("Attach to existing for lights is not yet supported. Could not attach "<<iObj.getFullName());
      return alembic_success;
   }

   std::vector<matShader> shaders;

   AbcG::ILight objLight = AbcG::ILight(iObj, Alembic::Abc::kWrapExisting);

   std::string identifier = objLight.getFullName();

   //CompoundPropertyReaderPtr propReader = objLight.getProperties();

   Abc::ICompoundProperty props = objLight.getProperties();

   InputLightType::enumt lightType = InputLightType::NUM_INPUT_LIGHT_TYPES;

   for(int i=0; i<props.getNumProperties(); i++){
      Abc::PropertyHeader propHeader = props.getPropertyHeader(i);
      if(AbcM::IMaterialSchema::matches(propHeader)){
         AbcM::IMaterialSchema matSchema(props, propHeader.getName());
   
         //ESS_LOG_WARNING("MaterialSchema present on light.");

         lightType = readShader(matSchema, shaders);
      }

      //ESS_LOG_WARNING("name: "<<propHeader.getName());

      //if( AbcG::ICameraSchema::matches(propHeader) ){
      //   ESS_LOG_WARNING("Found light camera.");
      //   //AbcG::ICameraSchema camSchema(props, propHeader.getName());

      //}
   }



   bool bReplaceExisting = false;
   int nodeRes = alembic_failure;
   if(lightType == InputLightType::AMBIENT_LIGHT){
      nodeRes = createNode(iObj, LIGHT_CLASS_ID, Class_ID(OMNI_LIGHT_CLASS_ID, 0), pMaxNode, bReplaceExisting);

      //Modifier* pModifier = FindModifier(*pMaxNode, Class_ID(OMNI_LIGHT_CLASS_ID, 0));

      //if(pModifier){
      //   ESS_LOG_WARNING("NumParamBlocks: "<<pModifier->NumParamBlocks());
      //}
   
      //printControllers(*pMaxNode);

      //pMaxNode>GetParamBlockByID( 0 )->SetValue( GetParamIdByName( pModifier, 0, "muted" ), zero, FALSE );


      GET_MAX_INTERFACE()->SelectNode(*pMaxNode);

      //set the ambient check box, intensity controller, and light colour controller (not sure how to this in C++)
      std::stringstream evalStream;
      std::string modkey("");

      for(int s=0; s<shaders.size(); s++){
         std::string target = shaders[s].target;
         std::string type = shaders[s].type;

         for(int i=0; i<shaders[s].props.size(); i++){
            std::string propName = shaders[s].props[i].name;
            std::string& val = shaders[s].props[i].displayVal;
            bool& bConstant = shaders[s].props[i].bConstant;

            const AbcA::DataType& datatype = shaders[s].props[i].propHeader.getDataType();
            const AbcA::MetaData& metadata = shaders[s].props[i].propHeader.getMetaData();

            if(datatype.getPod() == AbcA::kFloat32POD){

               std::stringstream propStream;
               propStream<<target<<"."<<type<<"."<<propName;
               if(datatype.getExtent() == 1 && propName.find("intensity") != std::string::npos ){ //intensity property found, so attach controller
                  addFloatController(evalStream, options, modkey, std::string("multiplier"), path, iObj.getFullName(), propStream.str());
               }
               else if(datatype.getExtent() == 3 && propName.find("lightcolor") != std::string::npos ){ //color property found, so attach controller

                  std::stringstream xStream, yStream, zStream;

                  xStream<<propStream.str()<<"."<<metadata.get("interpretation")<<".x";
                  yStream<<propStream.str()<<"."<<metadata.get("interpretation")<<".y";
                  zStream<<propStream.str()<<"."<<metadata.get("interpretation")<<".z";

                  evalStream<<"$.rgb.controller = Color_RGB()\n";

                  addFloatController(evalStream, options, modkey, std::string("rgb.controller.r"), path, iObj.getFullName(), xStream.str());
                  addFloatController(evalStream, options, modkey, std::string("rgb.controller.g"), path, iObj.getFullName(), yStream.str());
                  addFloatController(evalStream, options, modkey, std::string("rgb.controller.b"), path, iObj.getFullName(), zStream.str());
               }
            }
            else{
               
            }
            evalStream<<"\n";
         }
      }

      evalStream<<"$.ambientOnly = true\n";
      ExecuteMAXScriptScript( EC_UTF8_to_TCHAR((char*)evalStream.str().c_str()));
   }
   else{//create a null, if we don't know what type of light this is
      nodeRes = createNode(iObj, HELPER_CLASS_ID, Class_ID(DUMMY_CLASS_ID,0), pMaxNode, bReplaceExisting);
   }

   if(nodeRes == alembic_failure){
      return nodeRes;
   }


   GET_MAX_INTERFACE()->SelectNode(*pMaxNode);

   for(int i=0; i<shaders.size(); i++){

      std::sort(shaders[i].props.begin(), shaders[i].props.end(), sortFunc);

      Modifier* pMod = createDisplayModifier("Shader Properties", shaders[i].name, shaders[i].props);

      std::string target = shaders[i].target;
      std::string type = shaders[i].type;

      addControllersToModifier("Shader Properties", shaders[i].name, shaders[i].props, target, type, path, iObj.getFullName(), options);
   }

   // ----- TODO: add camera modifier
   //createCameraModifier(path, identifier, *pMaxNode);


   // ----- TODO: don't attach controllers for constant parameters


   //TODO: make the spinners read only



   return alembic_success;
}
void AlembicMeshGeomModifier::ModifyObject(TimeValue t, ModContext &mc,
                                           ObjectState *os, INode *node)
{
  ESS_CPP_EXCEPTION_REPORTING_START
  ESS_PROFILE_FUNC();

  Interval interval = FOREVER;  // os->obj->ObjectValidity(t);
  // ESS_LOG_INFO( "Interval Start: " << interval.Start() << " End: " <<
  // interval.End() );

  MCHAR const *strPath = NULL;
  this->pblock->GetValue(AlembicMeshGeomModifier::ID_PATH, t, strPath,
                         interval);

  MCHAR const *strIdentifier = NULL;
  this->pblock->GetValue(AlembicMeshGeomModifier::ID_IDENTIFIER, t,
                         strIdentifier, interval);

  float fTime;
  this->pblock->GetValue(AlembicMeshGeomModifier::ID_TIME, t, fTime, interval);

  BOOL bTopology = false;
  BOOL bGeometry = true;

  float fGeoAlpha;
  this->pblock->GetValue(AlembicMeshGeomModifier::ID_GEOALPHA, t, fGeoAlpha,
                         interval);

  BOOL bNormals = false;
  BOOL bUVs = false;

  BOOL bMuted;
  this->pblock->GetValue(AlembicMeshGeomModifier::ID_MUTED, t, bMuted,
                         interval);

  BOOL bAdditive;
  this->pblock->GetValue(AlembicMeshGeomModifier::ID_ADDITIVE, t, bAdditive,
                         interval);

  // ESS_LOG_INFO( "AlembicMeshGeomModifier::ModifyObject strPath: " << strPath
  // << " strIdentifier: " << strIdentifier << " fTime: " << fTime <<
  //	" bTopology: " << bTopology << " bGeometry: " << bGeometry << "
  // bNormals: " << bNormals << " bUVs: " << bUVs << " bMuted: " << bMuted );

  // IParamBlock2* pBlock = this->GetParamBlock(0);
  // if(pBlock){
  //   ESS_LOG_WARNING("ParamBlock: "<<pBlock->GetVersion());
  //}

  if (!bMuted && strPath && strIdentifier) {
    std::string szPath = EC_MCHAR_to_UTF8(strPath);
    std::string szIdentifier = EC_MCHAR_to_UTF8(strIdentifier);

    AbcG::IObject iObj;
    try {
      ESS_PROFILE_SCOPE("getObjectFromArchive");
      iObj = getObjectFromArchive(szPath, szIdentifier);
    }
    catch (std::exception exp) {
      extern bool g_hasModifierErrorOccurred;
      g_hasModifierErrorOccurred = true;
      ESS_LOG_ERROR("Can not open Alembic data stream.  Path: "
                    << szPath << " identifier: " << szIdentifier
                    << " reason: " << exp.what());
      return;
    }

    if (!iObj.valid()) {
      extern bool g_hasModifierErrorOccurred;
      g_hasModifierErrorOccurred = true;
      ESS_LOG_ERROR("Not a valid Alembic data stream.  Path: "
                    << szPath << " identifier: " << szIdentifier);
      return;
    }

    alembic_fillmesh_options options;
    options.fileName = szPath;
    options.pObjectCache = getObjectCacheFromArchive(szPath, szIdentifier);
    options.identifier = szIdentifier;
    options.pIObj = &iObj;
    options.dTicks = GetTimeValueFromSeconds(fTime);
    options.nDataFillFlags = 0;
    options.fVertexAlpha = fGeoAlpha;
    if (bAdditive == TRUE) {
      options.bAdditive = true;
    }
    else {
      options.bAdditive = false;
    }
    if (bTopology) {
      options.nDataFillFlags |= ALEMBIC_DATAFILL_FACELIST;
      options.nDataFillFlags |= ALEMBIC_DATAFILL_MATERIALIDS;
    }
    if (bGeometry) {
      options.nDataFillFlags |= ALEMBIC_DATAFILL_VERTEX;
    }
    if (bNormals) {
      options.nDataFillFlags |= ALEMBIC_DATAFILL_NORMALS;
    }
    if (bUVs) {
      options.nDataFillFlags |= ALEMBIC_DATAFILL_UVS;
    }

    options.pObject = os->obj;

    // Find out if we are modifying a poly object or a tri object
    if (os->obj->ClassID() == Class_ID(POLYOBJ_CLASS_ID, 0)) {
      ESS_PROFILE_SCOPE("reinterpret_cast1");
      PolyObject *pPolyObj = reinterpret_cast<PolyObject *>(os->obj);

      options.pMNMesh = &(pPolyObj->GetMesh());
    }
    else if (os->obj->CanConvertToType(Class_ID(POLYOBJ_CLASS_ID, 0))) {
      ESS_PROFILE_SCOPE("reinterpret_cast2");
      PolyObject *pPolyObj = reinterpret_cast<PolyObject *>(
          os->obj->ConvertToType(t, Class_ID(POLYOBJ_CLASS_ID, 0)));

      options.pMNMesh = &(pPolyObj->GetMesh());

      if (os->obj != pPolyObj) {
        os->obj = pPolyObj;
        os->obj->UnlockObject();
      }
    }
    else {
      ESS_LOG_ERROR(
          "Can not convert internal mesh data into a PolyObject, confused.");
      return;
    }

    try {
      AlembicImport_FillInPolyMesh(options);
    }
    catch (std::exception exp) {
      ESS_LOG_ERROR("Error reading mesh from Alembic data stream.  Path: "
                    << strPath << " identifier: " << strIdentifier
                    << " reason: " << exp.what());
      return;
    }
  }

  // update the validity channel
  if (bTopology) {
    ESS_PROFILE_SCOPE("UpdateValidity_Topology_Geom");
    os->obj->UpdateValidity(TOPO_CHAN_NUM, interval);
    os->obj->UpdateValidity(GEOM_CHAN_NUM, interval);
  }
  if (bGeometry) {
    ESS_PROFILE_SCOPE("UpdateValidity_Geom");
    os->obj->UpdateValidity(GEOM_CHAN_NUM, interval);
  }
  if (bUVs) {
    ESS_PROFILE_SCOPE("UpdateValidity_UV");
    os->obj->UpdateValidity(TEXMAP_CHAN_NUM, interval);
  }

  ESS_CPP_EXCEPTION_REPORTING_END
}
void AlembicFloatController::GetValueLocalTime(TimeValue t, void* ptr,
                                               Interval& valid,
                                               GetSetMethod method)
{
  ESS_CPP_EXCEPTION_REPORTING_START

  Interval interval = FOREVER;

  MCHAR const* strPath = NULL;
  this->pblock->GetValue(AlembicFloatController::ID_PATH, t, strPath, interval);

  MCHAR const* strIdentifier = NULL;
  this->pblock->GetValue(AlembicFloatController::ID_IDENTIFIER, t,
                         strIdentifier, interval);

  MCHAR const* strCategory = NULL;
  this->pblock->GetValue(AlembicFloatController::ID_CATEGORY, t, strCategory,
                         interval);

  MCHAR const* strProperty = NULL;
  this->pblock->GetValue(AlembicFloatController::ID_PROPERTY, t, strProperty,
                         interval);

  float fTime;
  this->pblock->GetValue(AlembicFloatController::ID_TIME, t, fTime, interval);

  BOOL bMuted;
  this->pblock->GetValue(AlembicFloatController::ID_MUTED, t, bMuted, interval);

  extern bool g_bVerboseLogging;

  if (g_bVerboseLogging) {
    ESS_LOG_WARNING("Param block at tick " << t << "-----------------------");
    ESS_LOG_WARNING("PATH: " << strPath);
    ESS_LOG_WARNING("IDENTIFIER: " << strIdentifier);
    ESS_LOG_WARNING("PROPERTY: " << strProperty);
    ESS_LOG_WARNING("TIME: " << fTime);
    ESS_LOG_WARNING("MUTED: " << bMuted);
    ESS_LOG_WARNING("Param block end -------------");
  }

  const float fDefaultVal = -1.0;

  std::string szPath = EC_MCHAR_to_UTF8(strPath);
  std::string szIdentifier = EC_MCHAR_to_UTF8(strIdentifier);
  std::string szProperty = EC_MCHAR_to_UTF8(strProperty);
  std::string szCategory = EC_MCHAR_to_UTF8(strCategory);

  if (szCategory.empty()) {  // default to standard properties for backwards
    // compatibility
    szCategory = std::string("standardProperties");
  }

  if (!strProperty || !strPath || !strIdentifier /*|| !strCategory*/) {
    return setController("1", szProperty, valid, interval, method, ptr,
                         fDefaultVal);
  }

  if (bMuted) {
    return setController("2", szProperty, valid, interval, method, ptr,
                         fDefaultVal);
  }

  // if( szCategory.size() == 0 ) {
  //   ESS_LOG_ERROR( "No category specified." );
  //   return setController("3a", szProperty, valid, interval,   method,   ptr,
  //   fDefaultVal);
  //}

  if (szProperty.size() == 0) {
    ESS_LOG_ERROR("No property specified.");
    return setController("3b", szProperty, valid, interval, method, ptr,
                         fDefaultVal);
  }

  AbcG::IObject iObj = getObjectFromArchive(szPath, szIdentifier);

  if (!iObj.valid()) {
    return setController("4", szProperty, valid, interval, method, ptr,
                         fDefaultVal);
  }

  TimeValue dTicks = GetTimeValueFromSeconds(fTime);
  double sampleTime = GetSecondsFromTimeValue(dTicks);

  float fSampleVal = fDefaultVal;

  if (boost::iequals(szCategory, "standardProperties")) {
    if (Alembic::AbcGeom::ICamera::matches(
            iObj.getMetaData())) {  // standard camera properties

      Alembic::AbcGeom::ICamera objCamera =
          Alembic::AbcGeom::ICamera(iObj, Alembic::Abc::kWrapExisting);

      SampleInfo sampleInfo =
          getSampleInfo(sampleTime, objCamera.getSchema().getTimeSampling(),
                        objCamera.getSchema().getNumSamples());
      Alembic::AbcGeom::CameraSample sample;
      objCamera.getSchema().get(sample, sampleInfo.floorIndex);

      double sampleVal;
      if (!getCameraSampleVal(objCamera, sampleInfo, sample, szProperty,
                              sampleVal)) {
        return setController("5", szProperty, valid, interval, method, ptr,
                             fDefaultVal);
      }

      // Blend the camera values, if necessary
      if (sampleInfo.alpha != 0.0) {
        objCamera.getSchema().get(sample, sampleInfo.ceilIndex);
        double sampleVal2 = 0.0;
        if (getCameraSampleVal(objCamera, sampleInfo, sample, szProperty,
                               sampleVal2)) {
          sampleVal = (1.0 - sampleInfo.alpha) * sampleVal +
                      sampleInfo.alpha * sampleVal2;
        }
      }

      fSampleVal = (float)sampleVal;
    }
    else if (Alembic::AbcGeom::ILight::matches(
                 iObj.getMetaData())) {  // ILight material properties

      ESS_PROFILE_SCOPE(
          "AlembicFloatController::GetValueLocalTime - read ILight shader "
          "parameter");

      Alembic::AbcGeom::ILight objLight =
          Alembic::AbcGeom::ILight(iObj, Alembic::Abc::kWrapExisting);

      SampleInfo sampleInfo =
          getSampleInfo(sampleTime, objLight.getSchema().getTimeSampling(),
                        objLight.getSchema().getNumSamples());

      AbcM::IMaterialSchema matSchema = getMatSchema(objLight);

      std::string strProp = szProperty;

      std::vector<std::string> parts;
      boost::split(parts, strProp, boost::is_any_of("."));

      if (parts.size() == 3) {
        const std::string& target = parts[0];
        const std::string& type = parts[1];
        const std::string& prop = parts[2];

        Abc::IFloatProperty fProp = readShaderScalerProp<Abc::IFloatProperty>(
            matSchema, target, type, prop);
        if (fProp.valid()) {
          fProp.get(fSampleVal, sampleInfo.floorIndex);
        }
        else {
          ESS_LOG_WARNING("Float Controller Error: could find shader parameter "
                          << strProp);
        }
      }
      else if (parts.size() == 5) {
        const std::string& target = parts[0];
        const std::string& type = parts[1];
        const std::string& prop = parts[2];
        const std::string& propInterp = parts[3];
        const std::string& propComp = parts[4];

        // ESS_LOG_WARNING("propInterp: "<<propInterp);

        if (propInterp == "rgb") {
          Abc::IC3fProperty fProp = readShaderScalerProp<Abc::IC3fProperty>(
              matSchema, target, type, prop);
          if (fProp.valid()) {
            Abc::C3f v3f;
            fProp.get(v3f, sampleInfo.floorIndex);
            if (propComp == "x") {
              fSampleVal = v3f.x;
            }
            else if (propComp == "y") {
              fSampleVal = v3f.y;
            }
            else if (propComp == "z") {
              fSampleVal = v3f.z;
            }
            else {
              ESS_LOG_WARNING(
                  "Float Controller Error: invalid component: " << propComp);
            }
          }
          else {
            ESS_LOG_WARNING(
                "Float Controller Error: could find shader parameter "
                << strProp);
          }
        }
        else {
          ESS_LOG_WARNING(
              "Float Controller Error: unrecognized parameter interpretation: "
              << propInterp);
        }
      }
      else {
        ESS_LOG_WARNING(
            "Float Controller Error: could not parse property field: "
            << strProperty);
      }
    }
  }
  else if (boost::iequals(szCategory, "userProperties")) {
    // AbcA::TimeSamplingPtr timeSampling = obj.getSchema().getTimeSampling();
    // int nSamples = (int)obj.getSchema().getNumSamples();

    AbcA::TimeSamplingPtr timeSampling;
    int nSamples = 0;
    Abc::ICompoundProperty propk =
        AbcNodeUtils::getUserProperties(iObj, timeSampling, nSamples);

    if (propk.valid()) {
      SampleInfo sampleInfo = getSampleInfo(sampleTime, timeSampling, nSamples);

      std::vector<std::string> parts;
      boost::split(parts, szProperty, boost::is_any_of("."));

      if (parts.size() == 1) {
        Abc::IFloatProperty fProp =
            readScalarProperty<Abc::IFloatProperty>(propk, szProperty);
        if (fProp.valid()) {
          fProp.get(fSampleVal, sampleInfo.floorIndex);
        }
        else {
          Abc::IInt32Property intProp =
              readScalarProperty<Abc::IInt32Property>(propk, szProperty);
          if (intProp.valid()) {
            int intVal;
            intProp.get(intVal, sampleInfo.floorIndex);
            fSampleVal = (float)intVal;
          }
          else {
            ESS_LOG_WARNING(
                "Float Controller Error: could not read user property "
                << szProperty);
          }
        }
      }
      else if (parts.size() == 3) {
        const std::string& prop = parts[0];
        const std::string& propInterp = parts[1];
        const std::string& propComp = parts[2];

        // ESS_LOG_WARNING("interpretation: "<<propInterp);

        if (propInterp == "rgb") {
          fSampleVal = readScalarPropertyExt3<Abc::IC3fProperty, Abc::C3f>(
              propk, sampleInfo, prop, propComp);
        }
        else if (propInterp == "vector") {
          fSampleVal = readScalarPropertyExt3<Abc::IV3fProperty, Abc::V3f>(
              propk, sampleInfo, prop, propComp);
        }
        else {
          ESS_LOG_WARNING(
              "Float Controller Error: unrecognized parameter interpretation: "
              << propInterp);
        }
      }
    }
  }
  // else if( boost::iequals(szCategory, "arbGeomParams") ){

  //}

  return setController("6", szProperty, valid, interval, method, ptr,
                       fSampleVal);

  ESS_CPP_EXCEPTION_REPORTING_END
}
int AlembicImport_Camera(const std::string& path, AbcG::IObject& iObj,
                         alembic_importoptions& options, INode** pMaxNode)
{
    const std::string& identifier = iObj.getFullName();

    if (!AbcG::ICamera::matches(iObj.getMetaData())) {
        return alembic_failure;
    }
    AbcG::ICamera objCamera = AbcG::ICamera(iObj, Abc::kWrapExisting);
    if (!objCamera.valid()) {
        return alembic_failure;
    }
    bool isConstant = objCamera.getSchema().isConstant();

    TimeValue zero(0);

    INode* pNode = *pMaxNode;
    CameraObject* pCameraObj = NULL;
    if (!pNode) {
        // Create the camera object and place it in the scene
        GenCamera* pGenCameraObj =
            GET_MAX_INTERFACE()->CreateCameraObject(FREE_CAMERA);
        if (pGenCameraObj == NULL) {
            return alembic_failure;
        }
        pGenCameraObj->Enable(TRUE);
        pGenCameraObj->SetConeState(TRUE);
        pGenCameraObj->SetManualClip(TRUE);

        IMultiPassCameraEffect* pCameraEffect =
            pGenCameraObj->GetIMultiPassCameraEffect();
        const int TARGET_DISTANCE = 0;
        pCameraEffect->GetParamBlockByID(0)->SetValue(TARGET_DISTANCE, zero, FALSE);

        pCameraObj = pGenCameraObj;

        Abc::IObject parent = iObj.getParent();
        std::string name = removeXfoSuffix(parent.getName().c_str());
        pNode = GET_MAX_INTERFACE()->CreateObjectNode(
                    pGenCameraObj, EC_UTF8_to_TCHAR(name.c_str()));
        if (pNode == NULL) {
            return alembic_failure;
        }
        *pMaxNode = pNode;
    }
    else {
        Object* obj = pNode->EvalWorldState(zero).obj;

        if (obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0))) {
            pCameraObj = reinterpret_cast<CameraObject*>(
                             obj->ConvertToType(zero, Class_ID(SIMPLE_CAM_CLASS_ID, 0)));
        }
        else if (obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0))) {
            pCameraObj = reinterpret_cast<CameraObject*>(
                             obj->ConvertToType(zero, Class_ID(LOOKAT_CAM_CLASS_ID, 0)));
        }
        else {
            return alembic_failure;
        }
    }

    // Fill in the mesh
    //   alembic_fillcamera_options dataFillOptions;
    //   dataFillOptions.pIObj = &iObj;
    //   dataFillOptions.pCameraObj = pCameraObj;
    //   dataFillOptions.dTicks =  GET_MAX_INTERFACE()->GetTime();
    // AlembicImport_FillInCamera(dataFillOptions);

    // printAnimatables(pCameraObj);

    Interval interval = FOREVER;

    AlembicFloatController* pControl = NULL;
    {
        std::string prop("horizontalFOV");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 0, 0);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 0, 0) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName << "mynode2113.FOV.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }
    {
        std::string prop("FocusDistance");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 1, 0, 1);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 1, 0, 1) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName
                    << "mynode2113.MultiPass_Effect.focalDepth.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }
    {
        std::string prop("NearClippingPlane");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 0, 2);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 0, 2) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName << "mynode2113.nearclip.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }
    {
        std::string prop("FarClippingPlane");
        if (options.attachToExisting) {
            pControl = getController(pCameraObj, identifier, prop, 0, 3);
        }
        if (pControl) {
            pControl->GetParamBlockByID(0)->SetValue(
                GetParamIdByName(pControl, 0, "path"), zero,
                EC_UTF8_to_TCHAR(path.c_str()));
        }
        else if (assignController(createFloatController(path, identifier, prop),
                                  pCameraObj, 0, 3) &&
                 !isConstant) {
            std::stringstream controllerName;
            controllerName << GET_MAXSCRIPT_NODE(pNode);
            controllerName << "mynode2113.farclip.controller.time";
            AlembicImport_ConnectTimeControl(controllerName.str().c_str(), options);
        }
    }

    // if(assignControllerToLevel1SubAnim(createFloatController(path, identifier,
    // std::string("FocusDistance")), pCameraObj, 0, 1) && !isConstant){
    //	AlembicImport_ConnectTimeControl( "$.targetDistance.controller.time",
    // options );
    //}

    createCameraModifier(path, identifier, pNode);

    // Add the new inode to our current scene list
    SceneEntry* pEntry = options.sceneEnumProc.Append(
                             pNode, pCameraObj, OBTYPE_CAMERA, &std::string(iObj.getFullName()));
    options.currentSceneList.Append(pEntry);

    // Set the visibility controller
    AlembicImport_SetupVisControl(path, identifier, iObj, pNode, options);

    importMetadata(pNode, iObj);

    return 0;
}