void OutputArray( std::ofstream& in_mfw, CFloatArray& in_array, int in_nDims, const CString& in_str )
{	
	if (!in_nDims) return;	

	LONG nVals = in_array.GetCount()/in_nDims;
	double s;
	
	bar.PutMinimum(0);
	bar.PutMaximum(nVals);
	for ( LONG i=0, nCurrent=0; i<nVals; i++ )
	{
		bar.PutValue(i);
		CString str;
		s = in_array[nCurrent];
		str += FormatNumber(s);

		for (LONG j = 1; j < in_nDims; j++)
		{
			str += L" ";
			s = in_array[nCurrent+j];
			str += FormatNumber(s);
		}

		in_mfw << in_str.GetAsciiString();
		in_mfw << str.GetAsciiString();
		in_mfw << "\n";
		
		nCurrent += in_nDims;
		gVn++;
		//bar.Increment();
	}
}
void OutputArrayPositions( std::ofstream& in_mfw, CDoubleArray& in_array, MATH::CTransformation& localTransform)
{	
	MATH::CVector3 position;
	MATH::CVector3 worldPosition;
	double x, y, z;
	
	bar.PutMinimum(0);
	bar.PutMaximum(in_array.GetCount()/3);
	for ( LONG i=0; i<in_array.GetCount(); i += 3 )
	{
		bar.PutValue(i);
		CString str;
		position.PutX(in_array[i]);
		position.PutY(in_array[i+1]);
		position.PutZ(in_array[i+2]);

		worldPosition = MATH::MapObjectPositionToWorldSpace(localTransform, position);

		x = worldPosition.GetX();
		y = worldPosition.GetY();
		z = worldPosition.GetZ();

		str = L"v " + FormatNumber(x) + L" " + FormatNumber(y) + L" " + FormatNumber(z);

		in_mfw << str.GetAsciiString();
		in_mfw << "\n";
		
		gV++;
		//bar.Increment();
	}
}
CStatus AlembicWriteJob::Process()
{
    // check filenames
    if(mFileName.IsEmpty())
    {
        Application().LogMessage(L"[alembic] No filename specified.",siErrorMsg);
        return CStatus::InvalidArgument;
    }

    // check objects
    if(mSelection.GetCount() == 0)
    {
        Application().LogMessage(L"[alembic] No objects specified.",siErrorMsg);
        return CStatus::InvalidArgument;
    }

    // check frames
    if(mFrames.size() == 0)
    {
        Application().LogMessage(L"[alembic] No frames specified.",siErrorMsg);
        return CStatus::InvalidArgument;
    }

    // init archive (use a locally scoped archive)
    CString sceneFileName = L"Exported from: "+Application().GetActiveProject().GetActiveScene().GetParameterValue(L"FileName").GetAsText();
    mArchive = CreateArchiveWithInfo(
                   Alembic::AbcCoreHDF5::WriteArchive(),
                   mFileName.GetAsciiString(),
                   "Softimage Alembic Plugin",
                   sceneFileName.GetAsciiString(),
                   Alembic::Abc::ErrorHandler::kThrowPolicy);

    // get the frame rate
    double frameRate = 25.0;
    CValue returnVal;
    CValueArray args(1);
    args[0] = L"PlayControl.Rate";
    Application().ExecuteCommand(L"GetValue",args,returnVal);
    frameRate = returnVal;
    if(frameRate == 0.0)
        frameRate = 25.0;
    double timePerSample = 1.0 / frameRate;

    // create the sampling
    AbcA::TimeSampling sampling(timePerSample,0.0);
    mTs = mArchive.addTimeSampling(sampling);

    Alembic::Abc::OBox3dProperty boxProp = Alembic::AbcGeom::CreateOArchiveBounds(mArchive,mTs);

    // create object for each
    std::vector<AlembicObjectPtr> objects;
    for(LONG i=0; i<mSelection.GetCount(); i++)
    {
        X3DObject xObj(mSelection[i]);
        if(xObj.GetType().IsEqualNoCase(L"camera"))
        {
            AlembicObjectPtr ptr;
            ptr.reset(new AlembicCamera(xObj.GetActivePrimitive().GetRef(),this));
            objects.push_back(ptr);
        }
        else if(xObj.GetType().IsEqualNoCase(L"polymsh"))
        {
            AlembicObjectPtr ptr;
            ptr.reset(new AlembicPolyMesh(xObj.GetActivePrimitive().GetRef(),this));
            objects.push_back(ptr);
        }
    }

    ProgressBar prog;
    prog = Application().GetUIToolkit().GetProgressBar();
    prog.PutMinimum(0);
    prog.PutMaximum((LONG)(mFrames.size() * objects.size()));
    prog.PutValue(0);
    prog.PutCancelEnabled(true);
    prog.PutVisible(true);

    for(unsigned int frame=0; frame < (unsigned int)mFrames.size(); frame++)
    {
        for(size_t i=0; i<objects.size(); i++)
        {
            prog.PutCaption(L"Frame "+CString(mFrames[frame])+L" of "+objects[i]->GetRef().GetAsText());
            CStatus status = objects[i]->Save(mFrames[frame]);
            if(status != CStatus::OK)
                return status;
            if(prog.IsCancelPressed())
                break;
            prog.Increment();
        }
        if(prog.IsCancelPressed())
            break;
    }

    prog.PutVisible(false);

    return CStatus::OK;
}
XSIPLUGINCALLBACK CStatus Coat3DExport_Execute( CRef& in_ctxt )
{
	// Unpack the command argument values
	Context ctxt( in_ctxt );
	CValueArray args = ctxt.GetAttribute(L"Arguments");
	CString string;

	// A 3d object with a mesh geometry must be selected
	Selection selection(app.GetSelection());

	bool isPolymesh = true;
	for(int i =0; i< selection.GetCount(); i++)
	{
		X3DObject obj(selection[i]);
		//app.LogMessage(L"obj.IsA(siPolygonMeshID): " + CString(obj.GetType()));
		if(obj.GetType() != L"polymsh" )
		{
			isPolymesh = false;
			break;
		}
	}

	if (selection.GetCount() > 0 && isPolymesh)
	{
		gV = 0; gVn = 0; gVt = 0;
		gVprev = 0; gVnPrev = 0; gVtPrev = 0;
		// prepare the output text file
		CString strOut = Get3DCoatParam( L"tempLocation" ).GetValue();
		
		std::ofstream mfw;
		mfw.open(strOut.GetAsciiString(), std::ios_base::out | std::ios_base::trunc);
		if (mfw.is_open())
		{
			bar.PutMaximum( selection.GetCount() );
			bar.PutStep( 1 );
			bar.PutVisible( true );		

			OutputHeader( mfw);				
			// output the data
			for (int i=0; i < selection.GetCount(); i++)
			{
				gObjCnt = i;
				gVprev = gV;
				gVtPrev = gVt;
				gVnPrev = gVn;
				X3DObject xobj(selection.GetItem(i));

				bar.PutValue(i);
				bar.PutCaption( L"Exporting " + xobj.GetName());

				mfw << "\n";
				mfw << "# Hierarchy (from self to top father)\n";
				string = L"g " + xobj.GetName() + L"\n";
				mfw << string.GetAsciiString();
				mfw << "\n";

				// Get a geometry accessor from the selected object	
				Primitive prim = xobj.GetActivePrimitive();
				PolygonMesh mesh = prim.GetGeometry();
				if (!mesh.IsValid()) return CStatus::False;

				CGeometryAccessor ga = mesh.GetGeometryAccessor();

				OutputVertices( mfw, ga, xobj );
				if (bar.IsCancelPressed()) return CStatus::False;
				OutputPolygonComponents( mfw, ga );
				if (bar.IsCancelPressed()) return CStatus::False;
				//bar.Increment();		
			}
		}
		mfw.close();

		if(Get3DCoatParam(L"bExpMat").GetValue())
		{
			OutputMaterials(selection );
		}
		bar.PutStatusText( L"import.txt" );
		OutputImportTxt();
		bar.PutVisible(false);
                app.LogMessage(L"Export done!");
	}
	else
	{
		app.LogMessage(L"Please, select objects!", siWarningMsg);
		return CStatus::False;
	}

	return CStatus::OK;
}
void OutputPolygonComponents( std::ofstream& in_mfw, CGeometryAccessor& in_ga)
{
	// polygon node indices
	CLongArray polyNodeIdxArray;
	CStatus st = in_ga.GetNodeIndices(polyNodeIdxArray);
	st.AssertSucceeded( L"GetNodeIndices");
	
	//app.LogMessage(L"NodeIdx: " + polyNodeIdxArray.GetAsText());

	CLongArray polySizeArray;
	st = in_ga.GetPolygonVerticesCount(polySizeArray);
	st.AssertSucceeded( L"GetPolygonVerticesCount" );

	//app.LogMessage(L"GetPolygonVerticesCount: " + polySizeArray.GetAsText());

	// polygon vertex indices
	CLongArray polyVtxIdxArray;
	st = in_ga.GetVertexIndices(polyVtxIdxArray);
	st.AssertSucceeded( L"GetVertexIndices" );

	CString string = L"#begin " +  CString(in_ga.GetPolygonCount()) + L" faces\n";
	in_mfw << string.GetAsciiString();

	// get the material objects used by the mesh
	CRefArray materials = in_ga.GetMaterials();
	CRefArray uvProps = in_ga.GetUVs();

	// get the material indices used by each polygon
	CLongArray pmIndices;
	in_ga.GetPolygonMaterialIndices(pmIndices);

	CString prevMat = L"";
	CString curMat = L"";

	bar.PutMinimum(0);
	bar.PutMaximum(polySizeArray.GetCount());
	bar.PutStatusText(L"Faces...");

	bool bUV = (bool)Get3DCoatParam(L"bExpUV").GetValue();
	bool bNrm = (bool)Get3DCoatParam(L"bExpNorm").GetValue();
	bool bMtl = (bool)Get3DCoatParam(L"bExpMat").GetValue();
	bool bUVCnt	= (uvProps.GetCount() > 0)?true:false;
	CString strVertices;

	for (LONG i=0, offset=0; i < polySizeArray.GetCount(); i++)
	{	
		bar.PutValue(i);
		strVertices = "";
		strVertices += CString(polyVtxIdxArray[offset] + 1 + gVprev);// vertices idx[0]
		if(bUV && bUVCnt)
		{
			strVertices += L"/";
			//strVertices += CString(polyNodeIdxArray[offset] + 1 + gVtPrev);// texture nodes idx[0]
			strVertices += CString(g_aNodeIsland[offset] + 1 + gVtPrev);// texture nodes idx[0]
			
			if(bNrm)
			{
				strVertices += L"/";
				strVertices += CString(polyNodeIdxArray[offset] + 1 + gVnPrev);// normal vertices idx[0]
			}
		}
		else if(bNrm)
		{
			strVertices += L"//";
			strVertices += CString(polyNodeIdxArray[offset] + 1 + gVnPrev);// normal vertices idx[0]
		}

		for (LONG j=1; j<polySizeArray[i]; j++)
		{
			strVertices += L" ";
			strVertices += CString(polyVtxIdxArray[offset+j] + 1 + gVprev);// vertices idx[12]
			if(bUV && bUVCnt)
			{
				strVertices += L"/";
				//strVertices += CString(polyNodeIdxArray[offset+j] + 1 + gVtPrev);// texture nodes idx[12]
				strVertices += CString(g_aNodeIsland[offset+j] + 1 + gVtPrev);// texture nodes idx[0]
				
				if(bNrm)
				{
					strVertices += L"/";
					strVertices += CString(polyNodeIdxArray[offset+j] + 1 + gVnPrev);// normal vertices idx[12]
				}
			}
			else if(bNrm)
			{
				strVertices += L"//";
				strVertices += CString(polyNodeIdxArray[offset+j] + 1 + gVnPrev);// normal vertices idx[12]
			}
		}

		if(bMtl)
		{
			Material mat(materials[ pmIndices[i] ]);
			curMat = mat.GetName();
			if(curMat != prevMat)
			{
				in_mfw << "usemtl ";
				in_mfw << curMat.GetAsciiString();
				in_mfw << "\n";
				prevMat = curMat;
			}
		}

		in_mfw << "f ";
		in_mfw << strVertices.GetAsciiString();
		in_mfw << "\n";
		
		offset += polySizeArray[i];

		//bar.Increment();
	}
	string = L"#end " +  CString(in_ga.GetPolygonCount()) + L" faces\n";
	in_mfw << string.GetAsciiString();
	in_mfw << "\n";
}
void OutputClusterPropertyValues( std::ofstream& in_mfw,	CGeometryAccessor& in_ga, CRefArray& in_array)
{	
	LONG nVals = in_array.GetCount();
	double s;

	ClusterProperty cp(in_array[0]);

	CFloatArray valueArray;
	CBitArray flags;		
	cp.GetValues( valueArray, flags );

	LONG nValueSize = cp.GetValueSize();
	
	bar.PutValue(0);
	bar.PutStatusText(L"Optimize UV...");
	// polygon node indices
	CLongArray polyNodeIdxArray;
	CStatus st = in_ga.GetNodeIndices(polyNodeIdxArray);
	st.AssertSucceeded( L"GetNodeIndices");

	//app.LogMessage(L"polyNodeIdxArray: " + polyNodeIdxArray.GetAsText());
	g_aNodeIsland = polyNodeIdxArray;
	std::vector<float> newValueArray;
	newValueArray.clear();

	for ( LONG j=0; j < polyNodeIdxArray.GetCount(); j++)
	{	
		float u = valueArray[polyNodeIdxArray[j]*3];
		float v = valueArray[polyNodeIdxArray[j]*3+1];
		//app.LogMessage(L"u = " + CString(u)+ "; v = "+ CString(v));
		LONG nmb = 0;
		bool bIs = false;
		for ( ULONG k = 0; k < newValueArray.size(); k += 3 )
		{
			if(fabs(newValueArray.at(k) - u) < 0.000002 && fabs(newValueArray.at(k+1) - v) < 0.000002)
			{
				nmb = k/3;
				bIs = true;
				break;
				//app.LogMessage(L"Yarr!: g_aNodeIsland["+ CString(j)+"] = "+ CString(k/3));
			}
		}
		
		if(bIs)
		{
			g_aNodeIsland[j] = nmb;
			//app.LogMessage(L"Yarr!: g_aNodeIsland["+ CString(j)+"] = "+ CString(nmb));
		}
		else
		{
			newValueArray.push_back(u);
			newValueArray.push_back(v);
			newValueArray.push_back(0.0f);
			g_aNodeIsland[j] = (LONG)(newValueArray.size()/3-1);
			//app.LogMessage(L"g_aNodeIsland["+ CString(j)+"] = "+ CString(newValueArray.size()/3-1));
		}
	}

	in_mfw << "#begin ";
	in_mfw << CString((ULONG)newValueArray.size()/nValueSize).GetAsciiString();
	in_mfw << "\n";
	
	bar.PutStatusText(L"Clusters...");
	bar.PutMinimum(0);
	bar.PutMaximum((LONG)newValueArray.size()/nValueSize);
	for ( ULONG j=0; j < newValueArray.size(); j += nValueSize)
	{
		//bar.PutValue(j);
		s = newValueArray.at(j);
		CString strValues = FormatNumber(s);
		
		for ( LONG k=1; k<nValueSize; k++ )
		{
			s = newValueArray.at(j+k);
			strValues += L" " + FormatNumber(s);
		}
		
		in_mfw << "vt ";
		in_mfw << strValues.GetAsciiString();
		in_mfw << "\n";

		gVt++;
		bar.Increment();
	}
	CString string = L"#end " +  CString((ULONG)newValueArray.size()/nValueSize) + L"\n";
	in_mfw << string.GetAsciiString();
	in_mfw << "\n";
}