// Copyright (C) 1997-2001 Alias|Wavefront, a division of Silicon Graphics Limited.
// 
// The information in this file is provided for the exclusive use of the
// licensees of Alias|Wavefront.  Such users have the right to use, modify,
// and incorporate this code into other products for purposes authorized
// by the Alias|Wavefront license agreement, without fee.
// 
// ALIAS|WAVEFRONT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
// EVENT SHALL ALIAS|WAVEFRONT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $RCSfile: MayaPolyBumpPlugin.cpp $     $Revision: /main/13 $

#include "stdafx.h"
#include <iostream> 
#include <fstream>

#include "resource.h"																									// IDD_THREADDER
#include <assert.h>																										// assert()
#include <maya/MDagPath.h>
#include <maya/MSimple.h>
#include <maya/MSelectionList.h>
#include <maya/MItDag.h>
#include <maya/MFnMesh.h>
#include <maya/MItMeshPolygon.h>
//#include "About.h"																										// GetSerialFromRegistry()
#include "PolyBumpWorkerThread.h"																			// CPolyBumpWorkerThread
#include "PolyBump.h"																									// CreateBumpMap()
/*#include <maya/MGlobal.h>
#include <maya/MItMeshVertex.h>
#include <maya/MItMeshEdge.h>
#include <maya/MPxFileTranslator.h>
#include <maya/MFnSet.h>
#include <maya/MItSelectionList.h>
*/
#include "SimpleIndexedMesh.h"																				// CSimpleIndexedMesh
#include "CopyProtection.h"																						// ChangeSerialYesNo(),GetSerialFromRegistry(),HowToGetSerialDialog()

#include <vector>						// STL vector<>
using namespace std;


// Worker Thread ******************************************

// used to get init and deinit if DLL is loaded or removed
class CDLLGlobal
{
public:

	//!
	void Init( void )
	{
		m_hThread=0;
		m_hKillReceiveEvent=0;
		InitializeCriticalSection(&m_csCriticalSection);
	}

	//!
	void DeInit( void )
	{
		EndProcess();
		assert(!m_hThread);
		assert(!m_hKillReceiveEvent);
		DeleteCriticalSection(&m_csCriticalSection);
	}



	//! is thread running
	//! /return true=yes, false otherwise
	bool IsThreadRunning( void );

	//! end process
	void EndProcess( void );


//private:
	HANDLE									m_hThread;								//!< Worker thread for generation of the normalmap
	HANDLE									m_hKillReceiveEvent;			//!< Event used to kill worker thread
	CRITICAL_SECTION				m_csCriticalSection;			//!< critical section for m_pWorkerThreadData
	CPolyBumpWorkerThread		m_WorkerThreadData;				//!< worker thread data
};


CDLLGlobal	g_Globals;


// ********************************************************


// DeclareSimpleCommand(MayaPolyBumpPlugin,"PolyBump by Crytek","0.3");		// command name, vendor, version
class MayaPolyBumpPlugin : public MPxCommand 
{						
public:												
	//! constructor
													MayaPolyBumpPlugin				() {}
	//! destructor
													~MayaPolyBumpPlugin				() {}

	//! command is called
	virtual MStatus					doIt											( const MArgList& );				

	//!
	static void*						creator										() { return new MayaPolyBumpPlugin; }
};										






//
MStatus initializePlugin( MObject _obj )					
{	
	g_Globals.Init();

	MFnPlugin	plugin( _obj, "PolyBump by Crytek", "0.3" );			
	MStatus		stat;										
	stat = plugin.registerCommand( "MayaPolyBumpPlugin", MayaPolyBumpPlugin::creator );
	if ( !stat )											
		stat.perror("registerCommand");

	return stat;											
}															

//
MStatus uninitializePlugin( MObject _obj )					
{															
	g_Globals.DeInit();

	MFnPlugin	plugin( _obj );								
	MStatus		stat;										
	stat = plugin.deregisterCommand( "MayaPolyBumpPlugin" );			
	if ( !stat )											
		stat.perror("deregisterCommand");					

	return stat;											
}


bool IsEdgeSmooth( MFnMesh &inMMesh, int iniVertex1, int iniVertex2 )
{
	MStatus status;

	int nEdges=inMMesh.numEdges(&status);						if(status!=MStatus::kSuccess)return(false);

	for(int i=0;i<nEdges;i++)
	{
		int2 iVertex12;
		inMMesh.getEdgeVertices(i,iVertex12);

		if((iVertex12[0]==iniVertex1 && iVertex12[1]==iniVertex2)
		|| (iVertex12[1]==iniVertex1 && iVertex12[0]==iniVertex2))
			return(inMMesh.isEdgeSmooth(i,&status));
	}

	return(false);	// not in edge list
}


// object has to be triangulated, normal information is not retrieved
// if this method failes, outMesh is in a undefined state (call FreeData() to bring it to a defined state)
// todo: error handling
bool GetMayaObject( const MDagPath &inPath, CSimpleIndexedMesh &_outMesh )
{
	CSimpleIndexedMesh outMesh;		// to get automatic destruction if error occure
	MFnMesh mesh;

	outMesh.FreeData();

	// build internal mesh structure --------------------
	MStatus status = mesh.setObject(inPath);								if(status!=MStatus::kSuccess){ cerr << "Internal Error 1\n";return(false); }

	int nFaceCount=mesh.numPolygons(&status);								if(status!=MStatus::kSuccess){ cerr << "Internal Error 2\n";return(false); }

	// check that every polygon is 3 sided
	{
		for(int i=0;i<nFaceCount;i++)
		{
			int nVertexCount=mesh.polygonVertexCount(i,&status);	if(status!=MStatus::kSuccess){ cerr << "Internal Error 3\n";return(false); }
			if(nVertexCount!=3)
			{
				cerr << "Object '" << mesh.name(&status).asChar() << "': Only triangulated objects are supported\n";
				return(false);
			}
		}
	}

	outMesh.m_FaceCount  = nFaceCount;

	if(!outMesh.m_FaceCount)return(false);

	outMesh.m_VertCount  = mesh.numVertices(&status);				if(status!=MStatus::kSuccess){ cerr << "Internal Error 4\n";return(false); }
	outMesh.m_CoorCount	 = mesh.numUVs(&status);						if(status!=MStatus::kSuccess){ cerr << "Internal Error 5\n";return(false); }

	// get vertex positions (optimizable)
	{
		outMesh.m_pVerts=(CObjVert *)malloc(sizeof(CObjVert)*outMesh.m_VertCount);

		for(int i=0;i<outMesh.m_VertCount;i++)
		{
			MPoint pos;
			status=mesh.getPoint(i,pos,MSpace::kWorld);					if(status!=MStatus::kSuccess){ cerr << "Internal Error 6\n";return(false); }

			outMesh.m_pVerts[i].x=(float)pos.x;outMesh.m_pVerts[i].y=(float)pos.y;outMesh.m_pVerts[i].z=(float)pos.z;
/*
			{
				char str[80];

				sprintf(str,"Q %d. (%.2f %.2f %.2f)\n",i,(float)pos.x,(float)pos.y,(float)pos.z);
				OutputDebugString(str);
			}
*/
		}
	}

	// get uv coords (optimizable)
	if(outMesh.m_CoorCount)
	{
		outMesh.m_pCoors=(CObjCoor *)malloc(sizeof(CObjCoor)*outMesh.m_CoorCount);

		for(int i=0;i<outMesh.m_CoorCount;i++)
		{
			float u,v;
			status=mesh.getUV(i,u,v);					if(status!=MStatus::kSuccess){ cerr << "Internal Error 7\n";return(false); }

			outMesh.m_pCoors[i].s=u;
			outMesh.m_pCoors[i].t=v;
/*
			{
				char str[80];

				sprintf(str,"Q %d. UV (%.2f %.2f)\n",i,u,v);
				OutputDebugString(str);
			}
*/
		}
	}

	
	// get normals
	{
		MFloatVectorArray NormalArray;

		status=mesh.getNormals(NormalArray,MSpace::kWorld);					if(status!=MStatus::kSuccess){ cerr << "Internal Error 7\n";return(false); }

		outMesh.m_NormCount=NormalArray.length();
	
		outMesh.m_pNorms=(CObjNorm *)malloc(sizeof(CObjNorm)*outMesh.m_NormCount);

		for(int i=0;i<outMesh.m_NormCount;i++)
		{
			const MFloatVector &vNormal=NormalArray[i];

			outMesh.m_pNorms[i]=CObjNorm(vNormal.x,vNormal.y,vNormal.z);
		}
	}
	// alloc memory for face data
	outMesh.m_pFaces=(CObjFace *)malloc(sizeof(CObjFace)*outMesh.m_FaceCount);

	// get face data
	{
		MItMeshPolygon iter(inPath,MObject::kNullObj,&status);		if(status!=MStatus::kSuccess){ cerr << "Internal Error 8\n";return(false); }
 
		int i=0;

		for( ; !iter.isDone() ; iter.next(),i++ )		// iterate through every face
		for(int e=0;e<3;e++)
		{
			if(outMesh.m_pNorms!=0)
			{
				int iNormalIndex=iter.normalIndex(e);

				assert(iNormalIndex>=0);
				assert(iNormalIndex<outMesh.m_NormCount);
				outMesh.m_pFaces[i].n[e]=iNormalIndex;
			}
			else outMesh.m_pFaces[i].n[e]=0;		// no normals
	
/*			if(outMesh.m_pNorms!=0)
			{
				// normal
				{
					MVector normal;

					iter.getNormal(e,normal);

					assert(i*3+e<outMesh.m_NormCount);

					outMesh.m_pNorms[i*3+e]=CObjNorm((float)normal.x,(float)normal.y,(float)normal.z);
				}

				outMesh.m_pFaces[i].n[e]=i*3+e;
			}
			else outMesh.m_pFaces[i].n[e]=0;		// no normals
*/
			// UV index
			{
				int iUVIndex;

				if(iter.getUVIndex(e,iUVIndex)==MStatus::kSuccess)
				{
					assert(iUVIndex>=0);
					assert(iUVIndex<outMesh.m_CoorCount);
					outMesh.m_pFaces[i].t[e]=iUVIndex;
				}
			}	

			// vertex index
			{
				int iVertexIndex;

				iVertexIndex=iter.vertexIndex(e,&status);			assert(status==MStatus::kSuccess);

				assert(iVertexIndex>=0);
				assert(iVertexIndex<outMesh.m_VertCount);
				outMesh.m_pFaces[i].v[e]=iVertexIndex;
			}

//			{
//				char str[80];
//
//				sprintf(str,"Q %d. N (%.2f %.2f %.2f)\n",i,(float)normal.x,(float)normal.y,(float)normal.z);
//				OutputDebugString(str);
//			}

		}

		assert(outMesh.m_FaceCount==i);
	}
/*
	// get polygon data (optimizable)
	{
		outMesh.m_pFaces=(CObjFace *)malloc(sizeof(CObjFace)*outMesh.m_FaceCount);

		for(int i=0;i<nFaceCount;i++)
		{
			MIntArray vertexList;
			status=mesh.getPolygonVertices(i,vertexList);	if(status!=MStatus::kSuccess){ cerr << "Internal Error 9\n";return(false); }

			assert(vertexList.length()==3);
//			{
//				char str[80];
/
//				sprintf(str,"%d. F ",i);
//				OutputDebugString(str);
//			}
			for(int e=0;e<3;e++)
			{
				int uvId;
				status=mesh.getPolygonUVid(i,e,uvId);

				if(status==MStatus::kSuccess)
					outMesh.m_pFaces[i].t[e]=uvId;
				 else
					outMesh.m_pFaces[i].t[e]=0;					// no UV texture assignment

				outMesh.m_pFaces[i].v[e]=vertexList[e];

				if(outMesh.m_pNorms!=0)
					outMesh.m_pFaces[i].n[e]=i*3+e;
				 else
					outMesh.m_pFaces[i].n[e]=0;


//				{
//					char str[80];
//
//					sprintf(str,"%d/%d/%d ",outMesh.m_pFaces[i].v[e],outMesh.m_pFaces[i].t[e],outMesh.m_pFaces[i].n[e]);
//					OutputDebugString(str);
//				}


			}
		}
	}
	*/

	// get shader assignment
	{
		MObjectArray shaders;
		MIntArray indices;
		mesh.getConnectedShaders(0,shaders,indices);

		for(int i=0;i<nFaceCount;i++)
			outMesh.m_pFaces[i].shader_id=indices[i];
	}


/*
	// convert edge smoothing information to smoothing groups (optimizable - is only for low poly version really neccessary)
	//
	// works like this: 
	//   init SG to 0 for each traingle
	//   for each triangle
	//     find the neighbour triangles
	//     find the neighbour edges
	//     get the edge smoothing information
	//     change SG of this triangle to match the edge smoothing information
	//       (set bits in the triangle SG info to match smooth edges and hard edges)
	{
		for(int i=0;i<nFaceCount;i++)					// for each face 
		{
			int iVertices[3]={outMesh.m_pFaces[i].v[0],outMesh.m_pFaces[i].v[1],outMesh.m_pFaces[i].v[2]};

			DWORD mask_i=GetSmoothingGroupNeighbourMask(outMesh,i);

			for(int e=i+1;e<nFaceCount;e++)			// find neighbour faces
			{
				int nSharedVertices=0;
				int iEdgeVertex[2]={ -1,-1 };

				// count shared vertiecs				
				for(int f=0;f<3;f++)
				{
					if(outMesh.m_pFaces[e].v[f]==iVertices[0])	{ iEdgeVertex[nSharedVertices]=iVertices[0];nSharedVertices++; }
					if(outMesh.m_pFaces[e].v[f]==iVertices[1])	{ iEdgeVertex[nSharedVertices]=iVertices[1];nSharedVertices++; }
					if(outMesh.m_pFaces[e].v[f]==iVertices[2])
					{ 
						if(nSharedVertices==3)
						{
							MessageBox(0,"smoothing group extraction failed,\nplease modify topology (two triangles are on top of each other)","Error",MB_OK);
							return(false);			// two triangles on top of each other
						}

						iEdgeVertex[nSharedVertices]=iVertices[2];nSharedVertices++; 
					}
				}

				// do the two faces share a vertex or a edge
				if(nSharedVertices==1 || nSharedVertices==2)
				{
					// is the edge between these two triangles smooth?
					bool bEdgeSmooth=IsEdgeSmooth(mesh,iEdgeVertex[0],iEdgeVertex[1]);

					if(bEdgeSmooth)
					{
						DWORD mask_e=GetSmoothingGroupNeighbourMask(outMesh,e);
		
						int found=FindLowest0Bit(mask_i | mask_e);

						if(found!=-1)
						{
							outMesh.m_pFaces[e].m_smoothinggroup |= (1<<found);
							outMesh.m_pFaces[i].m_smoothinggroup |= (1<<found);
						}
						else
						{
							MessageBox(0,"smoothing group extraction failed,\nplease modify topology","Error",MB_OK);
							return(false);
						}
					}
				}
			}
		}
	}
*/


	// move to other CSimpleIndexedMesh (to get automatic destruction if error occure)
	_outMesh.m_CoorCount=outMesh.m_CoorCount;outMesh.m_CoorCount=0;
	_outMesh.m_FaceCount=outMesh.m_FaceCount;outMesh.m_FaceCount=0;
	_outMesh.m_NormCount=outMesh.m_NormCount;outMesh.m_NormCount=0;
	_outMesh.m_VertCount=outMesh.m_VertCount;outMesh.m_VertCount=0;
	_outMesh.m_pCoors=outMesh.m_pCoors;outMesh.m_pCoors=0;
	_outMesh.m_pFaces=outMesh.m_pFaces;outMesh.m_pFaces=0;
	_outMesh.m_pNorms=outMesh.m_pNorms;outMesh.m_pNorms=0;
	_outMesh.m_pVerts=outMesh.m_pVerts;outMesh.m_pVerts=0;

	return(true);
}



/////////////////////////////////////////////////////////////////////////////
// message handlers
INT_PTR CALLBACK ThreadderWndProc( HWND hwndDlg,		// handle to dialog box
																	 UINT uMsg,				// message
																	 WPARAM wParam,		// first message parameter
																	 LPARAM lParam )	// second message parameter
{
	return 0L;
}


// main function for the worker thread

long WINAPI WorkerThreadFunc( long lParam )
{
	CPolyBumpWorkerThread *pWorkerThreadData=(CPolyBumpWorkerThread *)(lParam);		// pointer to CPlugPanel::this

	HINSTANCE hInst=MhInstPlugin;
	HWND hwnd=CreateDialog(hInst,MAKEINTRESOURCE(IDD_THREADDER),0,ThreadderWndProc);

	assert(hwnd);

	pWorkerThreadData->SetThreadderHWND(hwnd);

	if(hwnd)::ShowWindow(hwnd,SW_SHOW);

	pWorkerThreadData->SetStartTime(timeGetTime());

	// calles ThreadPollMessages() from time to time
	CreateBumpMap(pWorkerThreadData);		

	{
		char str[80];

		sprintf(str,"\nCreateBumpMap time %dms\n\n",timeGetTime()-pWorkerThreadData->GetStartTime());
		OutputDebugString(str);
	}

	if(hwnd)::ShowWindow(hwnd,SW_HIDE);

	if(hwnd)DestroyWindow(hwnd);

	if(!pWorkerThreadData->m_bUserHasStopped)
	if(pWorkerThreadData->m_Properties.m_bOutputDisplace)
	{
		char str[256];

		sprintf(str,"The magnitude for this displacement map is\n\n%.4f",pWorkerThreadData->m_Properties.m_fReturnDisplaceMag);

		MessageBox(0,str,"PolyBump",MB_OK);
	}

	return(0);
}




// WndProc vom Treiber auswahl Dialog
BOOL CALLBACK WndProcAboutDialog(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
  {
		case WM_COMMAND:
		switch(LOWORD(wParam))
		{
			case IDC_GETSERIALFROMCRYTEK:
				HowToGetSerialDialog();
				return(TRUE);

			case IDOK:							// LevelEdit
				{
					char str[256];
					HWND hItem=GetDlgItem(hwnd,IDC_SERIAL);	assert(hItem);
					GetWindowText(hItem,str,255);
					ChangeSerialYesNo(str);
					PostQuitMessage(0);
				}
				return(TRUE);
		}
		return 0L;
		
		case WM_CLOSE:
		  PostQuitMessage(0);
		return(TRUE);
  }

    return(FALSE);
}


bool ChooseBumpmapUserDlg( std::string &outPathName )
{
	OPENFILENAME ofn;

	outPathName="";

	memset(&ofn,0,sizeof(OPENFILENAME));
	char bumpmapname[1024]="";

	ofn.lStructSize=sizeof(OPENFILENAME);
	ofn.hwndOwner=0;		// Window Handle
	ofn.hInstance=MhInstPlugin;
	ofn.lpstrFilter="TARGA Files (*.TGA)\0*.TGA\0";
	ofn.lpstrDefExt="tga";
	ofn.lpstrFile=bumpmapname;
	ofn.nMaxFile=1024;
	ofn.lpstrTitle="Open optional Bumpmap (.TGA)";
	ofn.Flags=OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST;

	if(!GetOpenFileName(&ofn))		// filename: bumpmapname
		return(false);

	outPathName=bumpmapname;
	return(true);
}

// MayaPolyBumpPlugin head03_low02 head01_high 10
// MayaPolyBumpPlugin Box01 Box01 10
// MayaPolyBumpPlugin boot_low boot 10

MStatus MayaPolyBumpPlugin::doIt( const MArgList &args )
{
	if(!TryEnterCriticalSection(&(g_Globals.m_csCriticalSection)))
		return(MS::kFailure); 

	GetSerialFromRegistry();

	// Debug
	OutputDebugString("\nParameters:\n{\n");
	for( int i = 0; i < (int)args.length(); i++ )
	{
		OutputDebugString("  \"");
		OutputDebugString(args.asString(i).asChar());
		OutputDebugString("\"\n");
	}
	OutputDebugString("}\n\n");
	

	// get command parameter --------------------
	if(args.length()<5)
	{
		if(args.length()==1)
		{
			if(MString("-About")==args.asString(0))
			{
				HWND hAboutWnd=CreateDialog(MhInstPlugin,MAKEINTRESOURCE(IDD_ABOUT),0,WndProcAboutDialog);		assert(hAboutWnd);

				if(hAboutWnd)
				{
					HWND hItem=GetDlgItem(hAboutWnd,IDC_SERIAL);	assert(hItem);
					SetWindowText(hItem,g_szSerialNo);

					ShowWindow(hAboutWnd,SW_SHOW);

#ifndef USE_COPYPROTECTION
					HWND dlgitem1=GetDlgItem(hAboutWnd,IDC_GETSERIALFROMCRYTEK);
					ShowWindow(dlgitem1,SW_HIDE);
					HWND dlgitem2=GetDlgItem(hAboutWnd,IDC_SERIAL);
					ShowWindow(dlgitem2,SW_HIDE);
					HWND dlgitem3=GetDlgItem(hAboutWnd,IDC_SERIAL_TEXT);
					ShowWindow(dlgitem3,SW_HIDE);
					HWND dlgitem4=GetDlgItem(hAboutWnd,IDOK);
					ShowWindow(dlgitem4,SW_HIDE);
#else
//						CWnd *dlgitem=GetDlgItem(IDC_NONSERIALVERSION);				assert(dlgitem);
//						if(dlgitem)dlgitem->ShowWindow(SW_HIDE);
#endif


					MSG msg;

					// Pre Message Loop
					while(GetMessage(&msg, NULL, 0U, 0U))
					{
						TranslateMessage(&msg);
						DispatchMessage(&msg);
					}
				
					DestroyWindow(hAboutWnd);	
				}
			}
			else if(MString("-Stop")==args.asString(0))
			{
				g_Globals.EndProcess();
			}
			else if(MString("-ChooseBumpmap")==args.asString(0))
			{
				std::string sRet;

				clearResult();
				ChooseBumpmapUserDlg(sRet);

				setResult(sRet.c_str());
			}
			return(MS::kSuccess);
		}
		
		cerr << "MayaPolyBumpPlugin <LowMesh> <HighMesh> <RayLength e.g. 0.9> <Width> <Height> [-Antialiasing] [-ExpandTexture] [-ExtendFilename]\n";
		cerr << "                   [-Ray_Nearest|-Ray_Latest|-Ray_Best] [-ObjectSpace|-TangentSpace|-CloneSpace] [-Bumpmap strength filename]\n";
		return(MS::kFailure);
	}


	if(g_Globals.IsThreadRunning())
	{
		MessageBox(0,"IŽam already working","PolyBump",MB_OK);
		LeaveCriticalSection(&g_Globals.m_csCriticalSection);return(MS::kFailure); 
	}


	// get maya object --------------------

	MSelectionList lowpoly,highpoly;

	lowpoly.add(args.asString(0).asChar());
	highpoly.add(args.asString(1).asChar());

	g_Globals.m_WorkerThreadData.m_Properties.m_iAntiAliasingMode=0;				// without -Antialiasing
	g_Globals.m_WorkerThreadData.m_Properties.m_iExpandTextureMode=0;				// without -ExpandTexture
//	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputNormals=false;
	g_Globals.m_WorkerThreadData.m_Properties.m_bExtendFilename=false;
	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputAccessability=false;
	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputDisplace=false;
	g_Globals.m_WorkerThreadData.m_Properties.m_fBumpmapStrength=0.0f;
	g_Globals.m_WorkerThreadData.m_Properties.m_bObjectSpace=false;
	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputClonemap=false;
	g_Globals.m_WorkerThreadData.m_Properties.m_nHitMode=0;
	g_Globals.m_WorkerThreadData.m_Properties.m_szOutputExtension=".tga";

	sscanf(args.asString(2).asChar(),"%f",&(g_Globals.m_WorkerThreadData.m_Properties.m_fRayLength));
	g_Globals.m_WorkerThreadData.m_Properties.m_fRayLength*=0.01f;

	g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=-1;	// witdh 64
	if(MString("8")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=0;
	if(MString("16")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=1;
	if(MString("32")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=2;
	if(MString("64")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=3;
	if(MString("128")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=4;
	if(MString("256")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=5;
	if(MString("512")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=6;
	if(MString("1024")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=7;
	if(MString("2048")==args.asString(3))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX=8;
	if(g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePX==-1)
	{
		cerr << "MayaPolyBumpPlugin <Width> not specified\n";
		LeaveCriticalSection(&g_Globals.m_csCriticalSection);
		return(MS::kFailure);
	}

	g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=-1;	// height 64
	if(MString("8")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=0;
	if(MString("16")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=1;
	if(MString("32")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=2;
	if(MString("64")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=3;
	if(MString("128")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=4;
	if(MString("256")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=5;
	if(MString("512")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=6;
	if(MString("1024")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=7;
	if(MString("2048")==args.asString(4))g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY=8;
	if(g_Globals.m_WorkerThreadData.m_Properties.m_iTextureSizePY==-1)
	{
		cerr << "MayaPolyBumpPlugin <Height> not specified\n";
		LeaveCriticalSection(&g_Globals.m_csCriticalSection);
		return(MS::kFailure);
	}

	for ( DWORD i = 5; i < args.length(); i++ )
	{
         if ( MString( "-Antialiasing" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_iAntiAliasingMode=6;
    else if ( MString( "-ExpandTexture" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_iExpandTextureMode=6;
    else if ( MString( "-ExtendFilename" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bExtendFilename=true;

		else if ( MString( "-Ray_Nearest" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_nHitMode=0;
    else if ( MString( "-Ray_Latest" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_nHitMode=1;
    else if ( MString( "-Ray_Best" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_nHitMode=2;

//		else if ( MString( "-OutNormal" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputNormals=true;
		else if ( MString( "-OutAccess" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputAccessability=true;

		else if ( MString( "-OutDisplace" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputDisplace=true;
		else if ( MString( "-DisplaceSigned" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bDisplaceSigned=true;
		else if ( MString( "-DisplaceNPatch" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bDisplaceNPatch=true;

		else if ( MString( "-ObjectSpace" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bObjectSpace=true;
    else if ( MString( "-TangentSpace" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bObjectSpace=false;
		else if ( MString( "-CloneSpace" ) == args.asString( i ) )	{ g_Globals.m_WorkerThreadData.m_Properties.m_bObjectSpace=true;g_Globals.m_WorkerThreadData.m_Properties.m_bOutputClonemap=true; }
		else if ( MString( "-Debug" ) == args.asString( i ) )	g_Globals.m_WorkerThreadData.m_Properties.m_bOutputDebugInfo=true;
		else if ( MString( "-Bumpmap" ) == args.asString( i ) )
		{
			i++;sscanf(args.asString(i).asChar(),"%f",&(g_Globals.m_WorkerThreadData.m_Properties.m_fBumpmapStrength));
			i++;g_Globals.m_WorkerThreadData.LoadBumpMapPathName(args.asString(i).asChar());
		}
	 }


	MDagPath lowpath,highpath;
	MObject lowcomponent,highcomponent;
	
//	m_iChannelNo,															// the chosen channel is used
//	m_iMaterialID,true,true))									// only the selected id

	if(lowpoly.getDagPath(0,lowpath,lowcomponent)!=MStatus::kSuccess || !GetMayaObject(lowpath,*g_Globals.m_WorkerThreadData.GetLowMesh()))
	{
		cerr << "MayaPolyBump: Please specify the low detail mesh\n";
		LeaveCriticalSection(&g_Globals.m_csCriticalSection);
		return(MS::kFailure);
	}

//	1,-1,false,true))							// specify channel #1, but its not used
	if(highpoly.getDagPath(0,highpath,highcomponent)!=MStatus::kSuccess || !GetMayaObject(highpath,*g_Globals.m_WorkerThreadData.GetHighMesh()))
	{
		cerr << "MayaPolyBump: Please specify the high detail mesh\n";
		LeaveCriticalSection(&g_Globals.m_csCriticalSection);
		return(MS::kFailure);
	}
/*
	{
		HWND dlgitem=::GetDlgItem(m_hPanel,IDC_BUMPMAPNAME);

		char bumpmapname[1024];
		GetWindowText(dlgitem,bumpmapname,1024);
		WorkerThreadData.LoadBumpMapPathName(bumpmapname);
	}
*/

	if(g_Globals.m_WorkerThreadData.m_Properties.m_bOutputDebugInfo)		// generate debug info
	{
		g_Globals.m_WorkerThreadData.GetLowMesh()->ExportAsOBJ("TestMayaOutLow.obj");
		g_Globals.m_WorkerThreadData.GetHighMesh()->ExportAsOBJ("TestMayaOutHigh.obj");
//	g_Globals.m_WorkerThreadData.GetLowMesh()->Debug();
//	g_Globals.m_WorkerThreadData.GetHighMesh()->Debug();

		LeaveCriticalSection(&g_Globals.m_csCriticalSection);
		return(MS::kSuccess);
	}


	{	
		std::string sFilePathName=(g_Globals.m_WorkerThreadData.m_Properties.m_szOutputFilename.c_str()+g_Globals.m_WorkerThreadData.m_Properties.m_szOutputExtension).c_str();
		if(!g_Globals.m_WorkerThreadData.m_BitMapOutput.InitWithUserDlg(sFilePathName))
		{
			LeaveCriticalSection(&g_Globals.m_csCriticalSection);
			return(MS::kFailure);
		}

		g_Globals.m_WorkerThreadData.m_Properties.SetOutputFilename(sFilePathName.c_str());

		DWORD w=g_Globals.m_WorkerThreadData.m_Properties.GetOutputWidth(), h=g_Globals.m_WorkerThreadData.m_Properties.GetOutputHeight();

		g_Globals.m_WorkerThreadData.m_BitMapOutput.AllocBitmap(w,h);
	}

	g_Globals.m_hKillReceiveEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

	ULONG iID;
	void *userdata=&(g_Globals.m_WorkerThreadData);
	g_Globals.m_hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThreadFunc,userdata,0,&iID);

	assert(g_Globals.m_hThread);
	assert(g_Globals.m_hKillReceiveEvent);

	g_Globals.m_WorkerThreadData.SetKillEvent(&(g_Globals.m_hKillReceiveEvent));

	SetThreadPriority(g_Globals.m_hThread,THREAD_PRIORITY_BELOW_NORMAL);


	// without Multithreading
//	WorkerThreadFunc(&WorkerThreadData);

	LeaveCriticalSection(&g_Globals.m_csCriticalSection);
	return(MS::kSuccess);
}




bool CDLLGlobal::IsThreadRunning( void )
{
	if(m_hThread)
	{
		if(WaitForSingleObject(m_hThread,0)==WAIT_OBJECT_0)	// is still running?
		{
			if(m_hKillReceiveEvent)
			{
				CloseHandle(m_hKillReceiveEvent);
				m_hKillReceiveEvent = NULL;
			}

			CloseHandle(m_hThread);m_hThread=0;
			g_Globals.m_WorkerThreadData.m_BitMapOutput.FreeData();
//			delete g_Globals.m_WorkerThreadData.m_pBitMapInfo;g_Globals.m_WorkerThreadData.m_pBitMapInfo=0;
//			delete g_Globals.m_WorkerThreadData.m_pAccessBitMapInfo;g_Globals.m_WorkerThreadData.m_pAccessBitMapInfo=0;
			assert(!m_hThread);
			assert(!m_hKillReceiveEvent);
			return(false);
		}
		else return(true);
	}

	assert(!m_hThread);
	assert(!m_hKillReceiveEvent);
	return(false);
}




// stop the worker thread
void CDLLGlobal::EndProcess( void ) 
{
	if(IsThreadRunning())
	{
		if(m_hKillReceiveEvent)
		{
			SetEvent(m_hKillReceiveEvent);
	//	ExitThread((DWORD)-1);

			WaitForSingleObject(m_hThread,INFINITE);
		
			CloseHandle(m_hKillReceiveEvent);
			m_hKillReceiveEvent = NULL;
		}

		CloseHandle(m_hThread);m_hThread=0;
		g_Globals.m_WorkerThreadData.m_BitMapOutput.FreeData();
//		delete g_Globals.m_WorkerThreadData.m_pBitMapInfo;g_Globals.m_WorkerThreadData.m_pBitMapInfo=0;
//		delete g_Globals.m_WorkerThreadData.m_pAccessBitMapInfo;g_Globals.m_WorkerThreadData.m_pAccessBitMapInfo=0;

		assert(!m_hThread);
		assert(!m_hKillReceiveEvent);
	}
	else
	{
		assert(!m_hThread);
		assert(!m_hKillReceiveEvent);
	}
}