XSIPLUGINCALLBACK CStatus OutputMaterials( Selection& in_sel )
{
	// prepare the mtl file
	Project prj = app.GetActiveProject();
	Scene scn = prj.GetActiveScene();
	CString tmpLocation = Get3DCoatParam( L"tempLocation" ).GetValue();

	ULONG npos = tmpLocation.ReverseFindString(L".");
	CString substr = tmpLocation.GetSubString(0, npos+1);
	CString strOut = substr + L"mtl";	

	//app.LogMessage(L"strOut:" + strOut);

	std::ofstream matfw;
	matfw.open(strOut.GetAsciiString(), std::ios_base::out | std::ios_base::trunc);
	if (matfw.is_open())
	{
		CRefArray tempMats;

		for(int i=0; i< in_sel.GetCount(); i++)
		{
			X3DObject xobj(in_sel.GetItem(i));

			// 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();

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

			for (LONG n=0; n < materials.GetCount(); n++)
			{
				bar.PutStatusText( L"materials" );

				Material mat(materials[n]);
				bool inMats = false;
				//app.LogMessage(CString(n) +L" : "+ CString(i)+ L" :" + mat.GetName());

				for(int m = 0; m < tempMats.GetCount(); m++)
				{
					Material tmat(tempMats[m]);
					if(mat.GetName() == tmat.GetName())
					{
						inMats = true;
						break;
					}
				}

				//app.LogMessage(CString(inMats));

				if(!inMats)
				{
					CString string = L"newmtl " + mat.GetName() + L"\n";
					matfw << string.GetAsciiString();

					Parameter surf = mat.GetParameters().GetItem(L"surface");
					Shader lShader(surf.GetSource());
					//app.LogMessage(L"shader: " + lShader.GetFullName());
					//app.LogMessage(L"shader: " + lShader.GetProgID());
					if ( lShader.GetProgID() == L"Softimage.material-phong.1" )
					{
						float r, g, b, a;

						lShader.GetColorParameterValue(L"ambient", r, g, b, a );
						CString ka = L"Ka " + FormatNumber(r) + L" " + FormatNumber(g) + L" " + FormatNumber(b);
						lShader.GetColorParameterValue(L"diffuse", r, g, b, a );
						CString kd = L"Kd " + FormatNumber(r) + L" " + FormatNumber(g) + L" " + FormatNumber(b);
						lShader.GetColorParameterValue(L"specular", r, g, b, a );
						CString ks = L"Ks " + FormatNumber(r) + L" " + FormatNumber(g) + L" " + FormatNumber(b);
						float ns = lShader.GetParameterValue(L"shiny");
						float d = 1.0f;
						CValue illum = 2;

						matfw << ka.GetAsciiString();
						matfw << "\n";
						matfw << kd.GetAsciiString();
						matfw << "\n";
						matfw << ks.GetAsciiString();
						matfw << "\n";
						matfw << "Ns ";
						matfw << FormatNumber(ns).GetAsciiString();
						matfw << "\n";
						matfw << "d ";
						matfw << FormatNumber(d).GetAsciiString();
						matfw << "\n";
						matfw << "illum ";
						matfw << illum.GetAsText().GetAsciiString();
						matfw << "\n";

						Parameter diff = lShader.GetParameters().GetItem(L"diffuse");
						Shader lImageD(diff.GetSource());

						if (lImageD.GetProgID() == L"Softimage.txt2d-image-explicit.1")
						{
							Parameter tex = lImageD.GetParameters().GetItem(L"tex");
							ImageClip2 lTextureD(tex.GetSource());

							//app.LogMessage( L"Found texture shader: " + lTexture.GetFullName() + L", Class: " + lTexture.GetClassIDName() + L", Type: " + lTexture.GetType() );
							app.LogMessage(L"texture GetFileName: " + lTextureD.GetFileName());
							matfw << "map_Kd ";
							matfw << lTextureD.GetFileName().GetAsciiString();
							matfw << "\n";
						}

						Parameter spec = lShader.GetParameters().GetItem(L"specular");
						Shader lImageS(spec.GetSource());

						if (lImageS.GetProgID() == L"Softimage.txt2d-image-explicit.1")
						{
							Parameter tex = lImageD.GetParameters().GetItem(L"tex");
							ImageClip2 lTextureS(tex.GetSource());

							//app.LogMessage( L"Found texture shader: " + lTexture.GetFullName() + L", Class: " + lTexture.GetClassIDName() + L", Type: " + lTexture.GetType() );
							//app.LogMessage(L"texture GetFileName: " + lTexture.GetFileName());
							matfw << "map_Ks ";
							matfw << lTextureS.GetFileName().GetAsciiString();
							matfw << "\n";
						}
					}
					tempMats.Add(mat);
					matfw << "\n";
					matfw << "\n";
				}
			}		
		}
		matfw.close();
	}
	return CStatus::OK;
}
/** Event handler for when the options dialog is interacted with */
CStatus OgreMeshExportOptions_PPGEvent( const CRef& io_Ctx )
{
	// This callback is called when events happen in the user interface
	// This is where you implement the "logic" code.

	Application app ;
	static bool hasSkel = false;

	PPGEventContext ctx( io_Ctx ) ;

	PPGEventContext::PPGEvent eventID = ctx.GetEventID() ;

	CustomProperty prop = ctx.GetSource() ;	
	Parameter objectNameParam = prop.GetParameters().GetItem( L"objectName" ) ;
    // On open dialog
    if ( eventID == PPGEventContext::siOnInit )
	{
		CString theObjectName;
        // Pre-populate object with currently selected item(s)
		Selection sel(app.GetSelection());
		if (sel.GetCount() > 0)
		{
			CString val;
			for (int i = 0; i < sel.GetCount(); ++i)
			{
				CString thisName = SIObject(sel[i]).GetName();
				val += thisName;
				theObjectName += thisName;
				if (i < sel.GetCount() - 1)
				{
					val += L", ";
					theObjectName += L"_";
				}
			}
			prop.PutParameterValue(L"objectName", val);
		}
		else
		{
			// no selection, assume entire scene
			prop.PutParameterValue(L"objectName", CString(L"[Entire Scene]"));
		}
        // Make the selection read-only
		objectNameParam.PutCapabilityFlag( siReadOnly, true );

		// Default mesh name
		if (prop.GetParameterValue(L"targetMeshFileName") == L"")
		{
			// default name
			prop.PutParameterValue(L"targetMeshFileName", theObjectName + L".mesh");
		}

		// Default material name
		if (prop.GetParameterValue(L"targetMaterialFileName") == L"")
		{
			// default name
			prop.PutParameterValue(L"targetMaterialFileName", theObjectName + L".material");
		}

		// default the frame rate to that selected in animation panel
		prop.PutParameterValue(L"fps", CTime().GetFrameRate());

		// enable / disable the skeleton export based on envelopes
		if (!hasSkeleton(sel, true))
		{
			prop.PutParameterValue(L"exportSkeleton", false);
			Parameter param = prop.GetParameters().GetItem(L"exportSkeleton");
			param.PutCapabilityFlag(siReadOnly, true);
			param = prop.GetParameters().GetItem(L"targetSkeletonFileName");
			param.PutCapabilityFlag(siReadOnly, true);
			hasSkel = false;
		}
		else
		{
			prop.PutParameterValue(L"exportSkeleton", true);
			Parameter param = prop.GetParameters().GetItem(L"exportSkeleton");
			param.PutCapabilityFlag(siReadOnly, false);
			param = prop.GetParameters().GetItem(L"targetSkeletonFileName");
			param.PutCapabilityFlag(siReadOnly, false);

			if (prop.GetParameterValue(L"targetSkeletonFileName") == L"")
			{
				// default name
				prop.PutParameterValue(L"targetSkeletonFileName", theObjectName + L".skeleton");
			}
			hasSkel = true;
		}
		// value of param is a griddata object
		// initialise it with all detected animations if it's empty
		Parameter param = prop.GetParameters().GetItem(L"animationList");
		GridData gd(param.GetValue());
		if (gd.GetRowCount() == 0 || gd.GetCell(0,0) == L"No data has been set")
		{
			populateAnimationsList(gd);
		}
			
	}
    // On clicking a button
	else if ( eventID == PPGEventContext::siButtonClicked )
	{
		CValue buttonPressed = ctx.GetAttribute( L"Button" );	
        // Clicked the refresh animation button
		if ( buttonPressed.GetAsText() == L"refreshAnimation" )
		{
			LONG btn;
			CStatus ret = app.GetUIToolkit().MsgBox(
				L"Are you sure you want to lose the current contents "
				L"of the animations list and to refresh it from mixers?",
				siMsgYesNo,
				L"Confirm",
				btn);
			if (btn == 6)
			{
				Parameter param = prop.GetParameters().GetItem(L"animationList");
				GridData gd(param.GetValue());
				populateAnimationsList(gd);
			}
			
		}
		else if( buttonPressed.GetAsText() == L"addAnimation" )
		{
			Parameter param = prop.GetParameters().GetItem(L"animationList");
			GridData gd(param.GetValue());

			gd.PutRowCount(gd.GetRowCount() + 1);
			// default export to true and sample rate
			gd.PutCell(ANIMATION_LIST_EXPORT_COL, gd.GetRowCount()-1, true);
			gd.PutCell(ANIMATION_LIST_IKFREQ_COL, gd.GetRowCount()-1, (LONG)5);
		}
		else if( buttonPressed.GetAsText() == L"removeAnimation" )
		{
			Parameter param = prop.GetParameters().GetItem(L"animationList");
			GridData gd(param.GetValue());
			GridWidget gw = gd.GetGridWidget();

			// cell-level selection, so have to search for selection in every cell
			long selRow = -1;
			for (long row = 0; row < gd.GetRowCount() && selRow == -1; ++row)
			{
				for (long col = 0; col < gd.GetColumnCount() && selRow == -1; ++col)
				{
					if (gw.IsCellSelected(col, row))
					{
						selRow = row;
					}
				}
			}

			if (selRow != -1)
			{
				LONG btn;
				CStatus ret = app.GetUIToolkit().MsgBox(
					L"Are you sure you want to remove this animation entry?",
					siMsgYesNo,
					L"Confirm",
					btn);
				if (btn == 6)
				{
					// Move all the contents up one
					for (long row = selRow; row < gd.GetRowCount(); ++row)
					{
						for (long col = 0; col < gd.GetColumnCount(); ++col)
						{
							gd.PutCell(col, row, gd.GetCell(col, row+1));
						}
					}
					// remove last row
					gd.PutRowCount(gd.GetRowCount() - 1);
				}

			}

		}
	}
    // Changed a parameter
	else if ( eventID == PPGEventContext::siParameterChange )
	{
		Parameter changed = ctx.GetSource() ;	
		CustomProperty prop = changed.GetParent() ;	
		CString   paramName = changed.GetScriptName() ; 

        // Check paramName against parameter names, perform custom onChanged event
		if (paramName == L"targetMeshFileName")
		{
			// Default skeleton & material name 
			Ogre::String meshName = XSItoOgre(XSI::CString(changed.GetValue()));
			if (hasSkel)
			{
				Ogre::String skelName = meshName;
				if (Ogre::StringUtil::endsWith(skelName, ".mesh"))
				{
					skelName = skelName.substr(0, skelName.size() - 5) + ".skeleton";
				}
				CString xsiSkelName = OgretoXSI(skelName);
				prop.PutParameterValue(L"targetSkeletonFileName", xsiSkelName);
			}
			// default material script name 
			Ogre::String matName = meshName;
			if (Ogre::StringUtil::endsWith(matName, ".mesh"))
			{
				matName = matName.substr(0, matName.size() - 5) + ".material";
			}
			CString xsiMatName = OgretoXSI(matName);
			prop.PutParameterValue(L"targetMaterialFileName", xsiMatName);

			
		}
	}


	return CStatus::OK;	

}
SICALLBACK AppLink_3DCoat_PPGEvent( const CRef& in_ctxt )
{	
	//Application app;
	PPGEventContext ctxt( in_ctxt ) ;
	CustomProperty prop = ctxt.GetSource();
	PPGEventContext::PPGEvent eventID = ctxt.GetEventID() ;

	//CString s_ExeLocation = prop.GetParameter(L"coatExe").GetValue();

	if ( eventID == PPGEventContext::siOnInit )
	{
		CString s_ExportLocation = prop.GetParameter(L"tempLocation").GetValue();
		CString s_CoatLocation = prop.GetParameter(L"coatLocation").GetValue();

		Plugin pl(app.GetPlugins().GetItem(L"AppLink_3DCoat_Plugin"));
		pluginPath = CUtils::BuildPath(app.GetInstallationPath(pl.GetOrigin()), L"AppLink_3DCoat", L"Application", L"Plugins");

		if(prop.GetParameter(L"tempLocation").GetValue() == L"")
		{
			Project prj = app.GetActiveProject();
			Scene scn = prj.GetActiveScene();
			CString tempPath = CUtils::BuildPath(app.GetInstallationPath(siProjectPath), L"3DCoat", scn.GetName(), scn.GetName() + L".obj");
			prop.GetParameter(L"tempLocation").PutValue(tempPath);
			prop.GetParameter(L"tempLocation").PutCapabilityFlag(siReadOnly, true);
		}

		if(prop.GetParameter(L"coatLocation").GetValue() == L"")
		{
			TCHAR Path[MAX_PATH];
			if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, Path))) 
			{
				CString exchPath = CUtils::BuildPath(Path, L"3D-CoatV3", L"Exchange");
				prop.GetParameter(L"coatLocation").PutValue(exchPath);
			}
		}

		//prop.GetParameter(L"bImpNorm").PutCapabilityFlag(siNotInspectable, true);
		//prop.GetParameter(L"bImpNewMat").PutCapabilityFlag(siReadOnly, true);

		ctxt.PutAttribute(L"Refresh",true);

	}
	else if ( eventID == PPGEventContext::siButtonClicked )
	{
		CValue buttonPressed = ctxt.GetAttribute( L"Button" );
		CString tempLocation = prop.GetParameter(L"tempLocation").GetValue();
		CString coatLocation = prop.GetParameter(L"coatLocation").GetValue();
	
		if( buttonPressed.GetAsText() == L"Coat3DExport" )
		{
			CustomProperty in_pset = ctxt.GetSource();

			if(CUtils::EnsureFolderExists(tempLocation, true))
			{
				CValueArray args(6);
				args[0] = tempLocation;
				args[1] = coatLocation;
				args[2] = in_pset.GetParameterValue( L"typePaint" );
				//args[3] = in_pset.GetParameterValue( L"bCopyTexE" );
				args[3] = in_pset.GetParameterValue( L"bExpMat" );
				args[4] = in_pset.GetParameterValue( L"bExpUV" );
				args[5] = in_pset.GetParameterValue( L"bExpNorm" );
				
				CValue retVal;

				app.ExecuteCommand( L"Coat3DExport", args, retVal );
				
				CString exeLocation = prop.GetParameter(L"exeLocation").GetValue();
				bool bStart = prop.GetParameter(L"bStart").GetValue();

				if(exeLocation != L"" && bStart)
				{
					if(!Find3DCoat())
					{
						if((int)::ShellExecute(NULL, TEXT("open"), exeLocation.GetAsciiString(), NULL, NULL, SW_SHOWNORMAL) <= 32)
						{
							app.LogMessage(L"3D-Coat.exe not found!", siWarningMsg);
						}
					}
					else
					{
						app.LogMessage(L"3D-Coat.exe is run!", siWarningMsg);
					}
				}

			}
			else
			{
				app.LogMessage(L"Note temp path exists!", siErrorMsg);
			}
		}
		else if( buttonPressed.GetAsText() == L"Coat3DImport" )
		{
			CustomProperty in_pset = ctxt.GetSource();

			CString exportPath = CUtils::BuildPath(coatLocation, L"export.txt");
			CString objPath;

			std::ifstream mfrExportTxt;
			mfrExportTxt.open (exportPath.GetAsciiString());//c:\Documents and Settings\user\My Documents\3D-CoatV3\Exchange\export.txt
			//app.LogMessage(L"Export.txt is it! :" + strOut);
			if(mfrExportTxt.good())
			{
				std::string row;
				std::getline(mfrExportTxt, row);
				objPath = row.c_str();
				//app.LogMessage(L"first row: " + CString(row.c_str()));
			}
			else
			{
				UIToolkit uitool = app.GetUIToolkit();
				LONG out;
				uitool.MsgBox(L"import file not found!", siMsgOkOnly|siMsgExclamation, L"", (LONG) out);
				if(out == siMsgOk)
				{
					return CStatus::False;
				}
			}
			mfrExportTxt.close();
			app.LogMessage(coatLocation);
			app.LogMessage(exportPath);

			if(objPath != L"")
			{
				CValueArray args(2);
				args[0] = objPath;
				args[1] = in_pset.GetParameterValue( L"bReplace" );

				CValue noret;
				app.ExecuteCommand( L"Coat3DImport", args, noret );
			}
		}
	}
	else if ( eventID == PPGEventContext::siParameterChange )
	{
		Parameter changed = ctxt.GetSource();
		CustomProperty prop = changed.GetParent();		
		CString   paramName = changed.GetScriptName();

		if ( paramName == L"bImpMat" )
		{
			CValue bFlag = prop.GetParameter(L"bImpMat").GetValue();
			//prop.GetParameter(L"swMap").PutCapabilityFlag(siNotInspectable, !bFlag);
			prop.GetParameter(L"swMap").PutCapabilityFlag(siReadOnly, !bFlag);
		}
		ctxt.PutAttribute(L"Refresh",true);
	}

	return CStatus::OK ;
}
// PPG Event ==================================================
XSIPLUGINCALLBACK CStatus sn_null2surface_op_PPGEvent( const CRef& in_ctxt )
{
   PPGEventContext ctxt( in_ctxt ) ;
   PPGEventContext::PPGEvent eventID = ctxt.GetEventID() ;

   if ( eventID == PPGEventContext::siOnInit )
   {
      CRefArray props = ctxt.GetInspectedObjects();
      for (LONG i=0; i<props.GetCount( ); i++)
      {
         // we don't do anything, but I keep the code around
         CustomOperator prop( props[i] );
      }
   }
   else if ( eventID == PPGEventContext::siOnClosed )
   {
      CRefArray props = ctxt.GetInspectedObjects();
      for (LONG i=0; i<props.GetCount( ); i++)
      {
         // we don't do anything, but I keep the code around
         CustomOperator prop( props[i] );
      }
   }
   else if ( eventID == PPGEventContext::siButtonClicked )
   {
      CValue buttonPressed = ctxt.GetAttribute( L"Button" ) ;
      if(buttonPressed.GetAsText() == L"setInputUV")
      {
         CRefArray ops = ctxt.GetInspectedObjects();
         for (LONG i=0; i<ops.GetCount( ); i++)
         {
            CustomOperator op( ops[i] );
            CRef driverRef = InputPort(op.GetInputPorts()[0]).GetTarget();
            KinematicState driverState(driverRef);
            CVector3 driverPos = driverState.GetTransform().GetTranslation();
            CRef surfacePrimRef = InputPort(op.GetInputPorts()[2]).GetTarget();
            NurbsSurfaceMesh surfaceMesh(Primitive(surfacePrimRef).GetGeometry());

            LONG surface_index;
            double surface_u;
            double surface_v;
            double surface_un;
            double surface_vn;
            double surface_distance;
            CVector3 surface_pos;
            CVector3 surface_utan;
            CVector3 surface_vtan;
            CVector3 surface_normal;

            surfaceMesh.GetClosestSurfacePosition(driverPos,surface_index,surface_distance,surface_un,surface_vn,surface_pos);
            NurbsSurface surface(surfaceMesh.GetSurfaces()[surface_index]);
            surface.GetUVFromNormalizedUV(surface_un,surface_vn,surface_u,surface_v);

            op.PutParameterValue(L"iu_center",surface_u);
            op.PutParameterValue(L"iv_center",surface_v);
         }
      }
      else if(buttonPressed.GetAsText() == L"setInputXY")
      {
         CRefArray ops = ctxt.GetInspectedObjects();
         for (LONG i=0; i<ops.GetCount( ); i++)
         {
            CustomOperator op( ops[i] );
            CRef driverRef = InputPort(op.GetInputPorts()[4]).GetTarget();
            KinematicState driverState(driverRef);
            CVector3 driverPos = driverState.GetTransform().GetTranslation();

            // choose the right values based on the axis_mode
            long axis_mode = (LONG)op.GetParameterValue(L"axis_mode");
            double surface_u = 0;
            double surface_v = 0;
            switch(axis_mode)
            {
               case 0: // X Y plane
               {
                  surface_u = driverPos.GetX();
                  surface_v = driverPos.GetY();
                  break;
               }
               case 1: // X Z plane
               {
                  surface_u = driverPos.GetX();
                  surface_v = driverPos.GetZ();
                  break;
               }
               case 2: // Y Z plane
               {
                  surface_u = driverPos.GetY();
                  surface_v = driverPos.GetZ();
                  break;
               }
               case 3: // Y X plane
               {
                  surface_u = driverPos.GetY();
                  surface_v = driverPos.GetX();
                  break;
               }
               case 4: // Z X plane
               {
                  surface_u = driverPos.GetZ();
                  surface_v = driverPos.GetX();
                  break;
               }
               case 5: // Z Y plane
               {
                  surface_u = driverPos.GetZ();
                  surface_v = driverPos.GetY();
                  break;
               }
            }

            op.PutParameterValue(L"ix_center",surface_u);
            op.PutParameterValue(L"iy_center",surface_v);
         }
      }
      else if(buttonPressed.GetAsText() == L"setOutputUV")
      {
         CRefArray ops = ctxt.GetInspectedObjects();
         for (LONG i=0; i<ops.GetCount( ); i++)
         {
            CustomOperator op( ops[i] );
            CRef driverRef = InputPort(op.GetInputPorts()[0]).GetTarget();
            KinematicState driverState(driverRef);
            CVector3 driverPos = driverState.GetTransform().GetTranslation();
            CRef surfacePrimRef = InputPort(op.GetInputPorts()[2]).GetTarget();
            NurbsSurfaceMesh surfaceMesh(Primitive(surfacePrimRef).GetGeometry());

            LONG surface_index;
            double surface_u;
            double surface_v;
            double surface_un;
            double surface_vn;
            double surface_distance;
            CVector3 surface_pos;
            CVector3 surface_utan;
            CVector3 surface_vtan;
            CVector3 surface_normal;

            surfaceMesh.GetClosestSurfacePosition(driverPos,surface_index,surface_distance,surface_un,surface_vn,surface_pos);
            NurbsSurface surface(surfaceMesh.GetSurfaces()[surface_index]);
            surface.GetUVFromNormalizedUV(surface_un,surface_vn,surface_u,surface_v);

            op.PutParameterValue(L"ou_center",surface_u);
            op.PutParameterValue(L"ov_center",surface_v);
         }
      }
   }
   else if ( eventID == PPGEventContext::siTabChange )
   {
      CRefArray props = ctxt.GetInspectedObjects();
      for (LONG i=0; i<props.GetCount( ); i++)
      {
         // we don't do anything, but I keep the code around
         CustomOperator prop( props[i] );
      }
   }
   else if ( eventID == PPGEventContext::siParameterChange )
   {
      CRefArray props = ctxt.GetInspectedObjects();
      for (LONG i=0; i<props.GetCount( ); i++)
      {
         // we don't do anything, but I keep the code around
         CustomOperator prop( props[i] );
      }
   }

   return CStatus::OK ;
}