#include "Core/Asura.h"
#include "MeshEdit/Tools_Mode_MeshEdit.h"

#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_UserInput.h"
#include "Core/Asura_MaterialFlags.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_Logfile.h"
#include "Interface/Asura_Menubar_System.h"
#include "Interface/Asura_Menubar.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Interface.h"
#include "Interface/Asura_Cursor.h"
#include "Light/Asura_Light.h"
#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Mesh/Asura_Mesh_CollMeshGenData.h"
#include "Mesh/Asura_Env_System.h"
#include "Mesh/Asura_Env.h"
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_Text_Output.h"
#include "Rendering/Asura_RenderStates.h"
#include "Sound/Asura_ModuleReverb_System.h"
#include "Sound/Asura_Sound.h"

#ifdef ASURA_USE_NEW_VISIBILITY_SYSTEM
#include "Visibility/Asura_Visibility_System.h"
#endif

#include "Tools_Overlays.h"
#include "Entity/Tools_Entity_Object.h"
#include "Lighting/Tools_Environment_LOS.h"
#include "Lighting/Tools_EnvironmentalLightTransport.h"
#include "Lighting/Tools_GlobalIllumination_EntitySamples.h"
#include "Lighting/Tools_PhotonMapping_System.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Load&Save/Tools_ModeDataFile.h"
#include "MeshEdit/Tools_VertexMove_Object.h"
#include "Misc/Tools_Render_Options.h"
#include "Misc/Tools_Polygon_Selection.h"
#include "Misc/Tools_Vertex_Selection.h"
#include "Misc/Tools_Module_Selection.h"
#include "Misc/Tools_VertexPerPoly_Selection.h"
#include "Misc/Tools_Edge_Selection.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Misc/Tools_Help.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Misc/Tools_LOS.h"
#include "Misc/Tools_Adjacency.h"
#include "Misc/Tools_Prompted_Selection.h"
#include "Misc/Tools_Render.h"
#include "Objects/Tools_Object_System.h"
#include "Objects/Tools_Object.h"
#include "Rendering/Tools_RenderModules.h"
#include "Rendering/Tools_RenderCollisionMesh.h"
#include "Texturing/Tools_Texturing_Find.h"
#include "Texturing/Tools_Texturing_MaterialResponse.h"
#include "Texturing/Tools_PickTexture.h"
#include "Texturing/Tools_Mode_Texturing.h"

#if defined( ASURA_USE_CARPET)
	#include "Carpet/Asura_Direct3D_Carpet_Game_Render.h"
#endif

#if defined( ASURA_USE_BOTANICALS)
	#include "Botanical/Asura_Direct3D_Botanicals_Game_Render.h"
#endif


///////////////////////////////////////////////////////////////////////////////////////
/*--------------**
** Help Strings **
**--------------*/
static const char* MeshEditModeHelpStrings [] =
{
	"M :           Merge selected polygons",
	"U :           Unmerge selected polygons",
//	"F :           Flag selected polygons for deletion",
//	"Shift+F :     Unflag polygons for deletion",
	"T :           Turn Edge",
	"",
	"While selecting vertices :",
	"LMB           Select vertex",
	"Shift+LMB     Deselect vertex",
	"C             Create polygon",
	"(Note that all selected vertices must be in the same shape)",
	NULL
};
//////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** Tools_Mode_MeshEdit **
**---------------------*/

//static member variables
Tools_Mode_MeshEdit* Tools_Mode_MeshEdit::s_pxThis = NULL;

Asura_Vector_2		 Tools_Mode_MeshEdit::MeshEditOverlayPosition = Asura_Vector_2(3.0f,28.0f);
bool				 Tools_Mode_MeshEdit::MeshEditOverlayOpen = true;

Asura_Handle		 Tools_Mode_MeshEdit::MenuHandle;

bool				 Tools_Mode_MeshEdit::SelectingVertices;

float				 Tools_Mode_MeshEdit::s_fTearFixTolerance = 0.001f;
bool				 Tools_Mode_MeshEdit::s_bSuppressConfirmation = false;

static PolygonID g_xSelectedPolygon;
static int g_iSelectedVertex_ModuleIndex;
static int g_iSelectedVertex_VertexIndex;
Tools_Object* g_pxSelectedObject;

void Tools_Mode_MeshEdit::InitialiseMode()
{
	//Initalise polygon selection
	Tools_Polygon_Selection::Activate();
	Tools_Vertex_Selection::Activate();
	
	//create the menubar
	MenuHandle = Asura_Menubar_System::CreateMenuSection();
	Asura_Menubar_Title* title;

	title = Asura_Menubar_System::AddTitle(MenuHandle,"Mesh Edit");
	title->AddItem("Mesh Edit",Tools_Mode_MeshEdit::ToggleMeshEditOverlay,Tools_Mode_MeshEdit::MeshEditOverlayIsOn);
	title->AddItem("Selection",Tools_Polygon_Selection::ToggleSelectionOverlay,Tools_Polygon_Selection::SelectionOverlayIsOn);
	title->AddItem("VSelection",Tools_Vertex_Selection::ToggleSelectionOverlay,Tools_Vertex_Selection::SelectionOverlayIsOn);
	title->AddItem("Find",Tools_Texturing_Find::ToggleFindOverlay,Tools_Texturing_Find::FindOverlayIsOn);

	//load values from .dat file
	TransferModeData(TOOLSMODEDATAFILE_LOAD);
	
	//create overlays
	CreateMeshEditOverlay();

	//add help
	Tools_Help::AddHelpTopic("Mesh Edit Mode",MeshEditModeHelpStrings);

	SelectingVertices = false;


	//disable selection on all objects
	for(LIF<Tools_Object*> xSelectedLIF(&Tools_Object_System::ObjectList);!xSelectedLIF.done();xSelectedLIF.next())
	{
		// JAT 21/09/07 - don't mark tools light as unselectable
		const int iObjectType = xSelectedLIF()->Type;
		const bool bSelectable = ( iObjectType == TOT_ToolsPhoton );
		xSelectedLIF()->EnableSelection( bSelectable );
	}

}


void Tools_Mode_MeshEdit::ShutdownMode()
{
	Tools_Object_System::EnableAllControls(false);
	Tools_Help::RemoveHelpTopic("Mesh Edit Mode");

	//save values to .dat file
	TransferModeData(TOOLSMODEDATAFILE_SAVE);

	//delete all the objects
	Tools_Object_System::DeleteAllModeSpecific();
	
	Tools_Polygon_Selection::Deactivate();
	Tools_Vertex_Selection::Deactivate();
	Tools_Prompted_Selection::EndSelection();

	// Line of Sight data will need to be recalculated if we've changed any mesh
	// Play it safe and force a recalc next time some one uses the system.
	Tools_Environment_LOS::Reset();

	//shutdown the menu
	if(MenuHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Menubar_System::DestroyMenuSection(MenuHandle);
		MenuHandle = ASURA_INVALID_HANDLE;
	}

	//close overlays
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_MESHEDIT);

	Tools_Render_Options::ShowDestroyedPolygons = false;
}

void Tools_Mode_MeshEdit::PreShutdown()
{
	//see if there are any polygons flagged for deletion
	bool AnyFlaggedPolygons = false;
	for(int i=0;i<Asura_Module_System::GetNumberOfModules() && !AnyFlaggedPolygons;i++)
	{
		Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(i);
		for(int j=0;j<mesh->NumberOfPolygons;j++)
		{
	  		if(Tools_Render_Options::GetRenderFlags(i,j) & ToolsRenderFlag_ToBeDestroyed)
	  		{
				AnyFlaggedPolygons = true;			
				break;
			}
		}
	}

	if(AnyFlaggedPolygons)
	{
		//ask if they should be deleted
		YesNo_Overlay overlay("Would you like to delete the flagged polygons?");
		if(overlay.DoModal())
		{
			DestroyFlaggedPolygons();
		}
	}
	
}

void Tools_Mode_MeshEdit::TransferModeData(int mode)
{
	//open the file
	Tools_ModeDataFile file("MeshEditMode.dat",mode);

	//tranfer the data
	//(none of these should be removed , and new data should go on the end, otherwise
	//the files will get screwed up)
	file.Transfer(MeshEditOverlayPosition);
	file.Transfer(MeshEditOverlayOpen);
}

void Tools_Mode_MeshEdit::Update(void)
{
 	if(SelectingVertices)
	{
		Tools_Vertex_Selection::CreateSelectionOverlay();
		Tools_Polygon_Selection::CloseSelectionOverlay();
	}
	else
	{
		Tools_Vertex_Selection::CloseSelectionOverlay();
		Tools_Polygon_Selection::CreateSelectionOverlay();
	}


 	
 	int iPolygonSelectionUpdate = PolygonSelectionUpdate_NoSelection;
 	int iVertexSelectionUpdate = VertexSelectionUpdate_NoSelection;
 	
 	if(!Tools_Object_System::SelectedObject)
	{
 		if(SelectingVertices)
		{
			iVertexSelectionUpdate = VertexSelectionUpdate_Standard;
		}
		else
		{
			iPolygonSelectionUpdate = PolygonSelectionUpdate_Standard;
		}
	}
	Tools_Vertex_Selection::Update(iVertexSelectionUpdate);
 	Tools_Polygon_Selection::Update(iPolygonSelectionUpdate);



	if(!Tools_Object_System::SelectedObject)
	{
		Tools_VertexMove_Object::DeleteAll();
	}


	//act upon keyboard input
	HandleKeyboard();
}

void Tools_Mode_MeshEdit::RenderWorld_PostToneMap(void)
{
	Tools_Polygon_Selection::Render();
 	if(SelectingVertices)
	{
		Tools_Vertex_Selection::Render();
	}
}

void Tools_Mode_MeshEdit::RenderUI(void)
{
	Tools_Polygon_Selection::RenderUI();

	float x = 2;
	float y = 5;
	if(SelectingVertices)
	{
		Asura_Text_Output::Print(x,y++,0xffffffff,0,"Vertices selected : %d",Tools_Vertex_Selection::GetNumberOfSelectedVertices());
	}
	else
	{
		Asura_Text_Output::Print(x,y++,0xffffffff,0,"Modules selected : %d",Tools_Polygon_Selection::NumSelectedModules);
	}

}

bool Tools_Mode_MeshEdit::IsModified()
{
	return Tools_SaveAsura::IsMainFileModified() || Tools_SaveAsura::IsEntityModified();
}

void Tools_Mode_MeshEdit::QuickSave()
{
	Tools_SaveAsura::QuickSaveFile();
	if(Tools_SaveAsura::IsEntityModified()) Tools_SaveAsura::SaveEntities();
	
}

void Tools_Mode_MeshEdit::HandleKeyboard()
{
	if(Tools_Object_System::SelectedObject) return;
	if(Asura_Overlay_System::OverlaysHaveKeyboardFocus()) return;
 	if(Asura_UserInput::KeyPressed(ASURA_KEYID_ALT)) return;
 	if(Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
 	{
	}
	else if(Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
	{
		//if(Asura_UserInput::DebouncedKeyPressed('f') && !SelectingVertices)
		//{
		//	UnflagPolygonsForDeletion();
		//}
	}
	else
	{
		if(Asura_UserInput::DebouncedKeyPressed('m'))
		{
			MergeSelectedPolygons();
		}
		if(Asura_UserInput::DebouncedKeyPressed('u'))
		{
			UnmergeSelectedPolygons();
		}
		//if(Asura_UserInput::DebouncedKeyPressed('f') && !SelectingVertices)
		//{
		//	FlagPolygonsForDeletion();
		//}
		if(Asura_UserInput::DebouncedKeyPressed('c'))
		{
			CreatePolygonFromSelectedVertices();
		}
		if(Asura_UserInput::DebouncedKeyPressed('t'))
		{
			TurnEdge();
		}
	}
}



bool Tools_Mode_MeshEdit::WillSelectedVerticesMakeAQuad(const Asura_Vector_3*pxVertices,int* piOrdering)
{
	//first need to check if the points are coplanar
	//get the normal for the triangle formed by the first 3 points
	Asura_Vector_3 normal = (pxVertices[1]-pxVertices[0])^(pxVertices[2]-pxVertices[1]);

	if(normal.Magnitude()<0.0001f)
	{
		//collinear points , try a different set of 3
		normal = (pxVertices[2]-pxVertices[1])^(pxVertices[3]-pxVertices[2]);
		if(normal.Magnitude()<0.0001f) return false;
	}
	normal.Normalise();

	//see how far the other point is off the plane
	bool bPlanar = true;
	float distance = (pxVertices[3]-pxVertices[0])*normal;
	if(distance<-0.001f || distance>0.001f)
	{
		bPlanar = false;
	}

	//need to see if any ordering of the points will make a concave polygon
	Asura_Vector_3 Sides[4];
	
	int bestv1,bestv2,bestv3;
	float fBestDot = -1000.0f;
	int v0,v1,v2,v3;
	v0 = 0;
	for(v1=1;v1<4;v1++)
	{
		Sides[0] = pxVertices[v1]-pxVertices[v0];
		for(v2=1;v2<4;v2++)
		{
			if(v2==v1) continue;
			Sides[1] = pxVertices[v2]-pxVertices[v1];

			v3 = 6-(v0+v1+v2);
			Sides[2] = pxVertices[v3]-pxVertices[v2];
			Sides[3] = pxVertices[v0]-pxVertices[v3];

			//check to see if v0,v1,v2,v3 is an acceptable point ordering
			//get the cross product of each consecutive pair of sides ,and
			//make sure they are in the same direction
			Asura_Vector_3 xNormal = Sides[0] ^ Sides[1];
			xNormal.Normalise();
			int i;
			float fLowDot = 1000.0f;
			for(i=1;i<4;i++)
			{
				Asura_Vector_3 xNormal2 = Sides[i] ^ Sides[(i+1)%4];
				xNormal2.Normalise();
				
				float dot = xNormal*xNormal2;
				if(dot < fLowDot) fLowDot = dot;
			}

			if(fLowDot>fBestDot)
			{
				fBestDot = fLowDot;
				bestv1 = v1;
				bestv2 = v2;
				bestv3 = v3;
			}

		}
	}

	//fill in output ordering array
	if(piOrdering)
	{
		piOrdering[0] = 0;
		piOrdering[1] = bestv1;
		piOrdering[2] = bestv2;
		piOrdering[3] = bestv3;
	}

	return bPlanar && fBestDot>=0.0f;
}

void Tools_Mode_MeshEdit::ToggleMeshEditOverlay(void)
{
	MeshEditOverlayOpen = !MeshEditOverlayOpen;

	if (MeshEditOverlayOpen)
	{
		CreateMeshEditOverlay();
	}
	else
	{
		Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_MESHEDIT);
	}
}
bool Tools_Mode_MeshEdit::MeshEditOverlayIsOn(void)
{
	return MeshEditOverlayOpen;
}

void Tools_Mode_MeshEdit::CreateMeshEditOverlay(void)
{
	if(!MeshEditOverlayOpen) return;

	Asura_Overlay* pxOverlay;
	pxOverlay = new Asura_Overlay("Mesh Edit Mode",false,&MeshEditOverlayPosition);

	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	s_pxThis->AddOverlayControls( pxOverlay, xOffset );

	pxOverlay->m_uID = TOOLS_OVERLAY_MESHEDIT;

	Asura_Overlay_System::SubmitNewOverlay(pxOverlay,false);
}

void Tools_Mode_MeshEdit::AddOverlayControls( Asura_Overlay* pxOverlay, Asura_Vector_2& xOffset )
{
	Asura_Dialog* pxDialog;

	//	pxDialog = new Asura_Tickbox_Dialog("Show Objects",xOffset,&s_bShowObjects);
	//	pxDialog->SetHelpText("Show placed objects.");
	//	pxOverlay->AddDialog(pxDialog);
	//	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_FunctionTickbox_Dialog("Show Objects", xOffset, Tools_Object_System::ShouldShowPhysicalObjectsInOtherModes, Tools_Object_System::SetShowPhysicalObjectsInOtherModes);
	pxDialog->SetHelpText("Causes tools objects for physical entities to be created (and be visible depending on other flags) in modes where they wouldn't normally be edited.\nThis won't do anything if a mode has already created the objects and is hiding them for its own diabolical schemes.");
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Tickbox_Dialog("Show Polys Flagged For Deletion",xOffset,&(Tools_Render_Options::ShowDestroyedPolygons));
	pxDialog->SetHelpText("Makes polygons flagged for deletion visible.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Function_Dialog("Flag For Deletion",xOffset,FlagPolygonsForDeletion);
	pxDialog->SetHelpText("Flags the selected polygons for deletion , and hides them.\nThey will only be deleted properly when 'Delete Flagged Polygons' is selected.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Unflag For Deletion",xOffset,UnflagPolygonsForDeletion);
	pxDialog->SetHelpText("The selected polygons will no longer be flagged for deletion.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Function_Dialog("Delete Flagged Polygons",xOffset,DestroyFlaggedPolygons);
	pxDialog->SetHelpText("Deletes all the polygons that have been flagged for deletion.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Function_Dialog("Merge Polygons (M)",xOffset,MergeSelectedPolygons);
	pxDialog->SetHelpText("Merges together any selected triangles that will make valid quads.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Unmerge Polygons (U)",xOffset,UnmergeSelectedPolygons);
	pxDialog->SetHelpText("Splits all selected quads into triangles.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Flip Normals",xOffset,FlipSelectedPolygons);
	pxDialog->SetHelpText("Reverses the vertex ordering in the selected polygons , so that they become visible from the opposite side instead.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Fix T-Junction",xOffset,FixTJunction);
	pxDialog->SetHelpText("Prompts for a polygon that needs fixing , and a vertex at which the junction exists.\nThis will insert an extra vertex into the side of the polygon to fix the tear caused by the junction.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Tear Fix",xOffset,TearFix);
	pxDialog->SetHelpText("Prompts for two vertices to be chosen.\nThis will snap the first vertex to the second.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Split Edge",xOffset,SplitEdge);
	pxDialog->SetHelpText("Prompts for an edge to be chosen.\nThis edge will have an extra vertex inserted into it, and the polygons either side will be divided at this point.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Turn Edge (T)",xOffset,TurnEdge);
	pxDialog->SetHelpText("Prompts for an edge between two triangles to be chosen.\nThe vertices in the triangles will be swapped so that the edge goes across the other diagonal.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Merge Object Into Module",xOffset,MergeObjectIntoModule);
	pxDialog->SetHelpText("Prompts for an object and a module to be chosen.\nThe object's polygons will be merged into the module , and then the object will be deleted.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Merge Selected Modules",xOffset,MergeSelectedModules);
	pxDialog->SetHelpText("Merges together all modules that have been completely selected into one module.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Tickbox_Dialog("Vertex Selection",xOffset,&(SelectingVertices));
	pxDialog->SetHelpText("Turns on vertex selection mode (disabling polygon selection).\nUsed for selecting vertices for creating a new polygon.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Create Polygon (C)",xOffset,CreatePolygonFromSelectedVertices);
	pxDialog->SetHelpText("Creates a polygon from the selected vertices.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Create Vertex",xOffset,CreateVertex);
	pxDialog->SetHelpText("Creates a vertex at the current position.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Delete Vertices",xOffset,DeleteSelectedVertices);
	pxDialog->SetHelpText("Deletes the selected vertices.\nThis only works for vertices that aren't used by any polygons.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Move Vertices",xOffset,MoveVerticesOrPolygons);
	pxDialog->SetHelpText("Creates an object for moving the selected vertices around.\nIf polygon selection is currently active , then the vertices used by the selected polygons are moved instead.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Flatten Vertices",xOffset,FlattenVertices);
	pxDialog->SetHelpText("Moves all the selected vertices so that they share the same Y value.\nIf polygon selection is currently active , then the vertices used by the selected polygons are affected instead.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Function_Dialog("Auto Tear Fix",xOffset,AutomaticTearFix);
	pxDialog->SetHelpText("Snaps together vertices that are sufficently close in any completely selected modules.\nThe tolerance level for snapping the vertices is determined by the Tolerance value below.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog  = new Asura_Numerical_Dialog("Tolerance",xOffset,&(s_fTearFixTolerance),0.0f,1.0f,0.0001f,"%.4f");
	pxDialog->SetHelpText("Tolerance level in metres for tear fix. Any vertices less than this distance apart will be moved together.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Tickbox_Dialog("Suppress Confirmation",xOffset,&s_bSuppressConfirmation);
	pxDialog->SetHelpText("This will prevent most confirmation dialog boxes from being displayed.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();
}

void Tools_Mode_MeshEdit::FlagPolygonsForDeletion()
{
	int count = 0;

	//add the destroyed flag to all the selected polygons
	PolygonID polygon;
	Tools_Polygon_Selection::StartIteration();

	while(Tools_Polygon_Selection::GetNextSelected(&polygon))
	{
		if(!(Tools_Render_Options::GetRenderFlags(polygon.ModuleIndex,polygon.PolyIndex) & ToolsRenderFlag_ToBeDestroyed))
		{
			Tools_Render_Options::AddRenderFlagPolygon(polygon.ModuleIndex,polygon.PolyIndex,ToolsRenderFlag_ToBeDestroyed);
			count++;
		}
	}
	//and clear the selection
	Tools_Polygon_Selection::DeselectAll();
	Tools_Timed_Message_System::AddMessage(0xffffff,"%d polygons flagged for deletion",count);
}

void Tools_Mode_MeshEdit::UnflagPolygonsForDeletion()
{
	int count = 0;

	//remove the destroyed flag from all the selected polygons
	PolygonID polygon;
	Tools_Polygon_Selection::StartIteration();

	while(Tools_Polygon_Selection::GetNextSelected(&polygon))
	{
		if((Tools_Render_Options::GetRenderFlags(polygon.ModuleIndex,polygon.PolyIndex) & ToolsRenderFlag_ToBeDestroyed))
		{
			Tools_Render_Options::RemoveRenderFlagPolygon(polygon.ModuleIndex,polygon.PolyIndex,ToolsRenderFlag_ToBeDestroyed);
			count++;
		}
	}
	Tools_Timed_Message_System::AddMessage(0xffffff,"%d polygons saved from deletion",count);

}

void Tools_Mode_MeshEdit::DestroyFlaggedPolygons()
{

	int count = 0;
	//check each module for polygons marked for destruction
	for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
	{
		Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(i);
		bool* DeleteArray = NULL;
		for(int j=0;j<mesh->NumberOfPolygons;j++)
		{
			if(Tools_Render_Options::GetRenderFlags(i,j) & ToolsRenderFlag_ToBeDestroyed)
			{
				if(!DeleteArray)
				{
					DeleteArray = new bool[mesh->NumberOfPolygons];
					for(int k=0;k<mesh->NumberOfPolygons;k++) DeleteArray[k] = false;
				}
				DeleteArray[j] = true;
			}
		}
		if(DeleteArray)
		{
			bool bModuleDeleted = false;
			int iDeleted = DeletePolygonsInModule(i, mesh,DeleteArray, &bModuleDeleted);

			if(bModuleDeleted)
			{
				i--;
			}
			count += iDeleted;

			delete [] DeleteArray;
		}
	}

	

	Tools_Timed_Message_System::AddMessage(0xffffff,"%d polygons deleted",count);
}

int Tools_Mode_MeshEdit::DeletePolygonsInModule(int iModule,Asura_Mesh* pxMesh,bool* pbDeleteArray, bool *pbModuleWasDeleted)
{
	Asura_Mesh* pxCurrentMesh = Asura_Module_System::GetMeshForModule(iModule);
	int iCount = pxMesh->NumberOfPolygons;
	if(pbModuleWasDeleted) * pbModuleWasDeleted = false;

	
	int* PolyConv = 0;
	if(DeletePolygons(pxMesh,pbDeleteArray,&PolyConv))
	{
		Tools_SaveAsura::SetMeshModified(pxMesh);
		
		
		iCount -= pxMesh->NumberOfPolygons;

		//If affecting the current platform mesh , then selection etc will need to be updated
		if(pxMesh == pxCurrentMesh)
		{
			Tools_Polygon_Selection::HandlePolygonConversion(iModule,PolyConv);
			Tools_Vertex_Selection::HandleVertexConversion(iModule,0);
			Tools_VertexPerPoly_Selection::HandleMeshChange(iModule);
			Tools_Edge_Selection::HandleMeshChange(iModule);
			Tools_Render_Options::HandlePolygonConversion(iModule,PolyConv);
	
			InvalidateModuleCollisionMesh(iModule);
		}
		if(pxMesh->m_pxCollMeshGenData)
		{
			pxMesh->m_pxCollMeshGenData->HandlePolygonConversion(pxMesh,PolyConv);
		}

		delete [] PolyConv;

		//If the module only contains merged objetc polygons , then it will need to be deleted.
		//RAR 21/03/2007
		bool bNeedToDeleteModule = true;
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			if(!pxMesh->IsMergedObjectPolygon(i))
			{
				bNeedToDeleteModule = false;
				break;
			}
		}
		
		if(bNeedToDeleteModule)
		{
			DeleteModule(iModule);
			if(pbModuleWasDeleted) *pbModuleWasDeleted = true;
		}
	}
	else
	{
		return 0;
	}

	if(iCount)
	{
		//undo for selection will no longer be valid
		if(pxMesh == pxCurrentMesh)
		{
			Tools_Polygon_Selection::ClearUndo();
			Tools_Vertex_Selection::ClearUndo();
			Tools_Adjacency::InvalidateModule(iModule);

			Tools_Undo_System::ClearUndo();

			//delete vertex movement objects to be safe
			Tools_VertexMove_Object::DeleteAll();

			//GI samples will have become invalidated
			Tools_GlobalIllumination_EntitySamples::Reset();

			#if defined( ASURA_USE_CARPET)
			//the carpet data refers to polygon/vertex indeces for lighting.
			Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
			#endif
		}
	}

	return iCount;	

}

void Tools_Mode_MeshEdit::DeleteEntityPolygons(Asura_Guid uGuid)
{
	for(int i=Asura_Module_System::GetNumberOfModules()-1;i>=0;i--)
	{
		Asura_Mesh* pxCurrentMesh = Asura_Module_System::GetMeshForModule(i);
		Asura_Mesh* pxDefaultMesh = pxCurrentMesh->GetDefaultPlatformMesh();
		if(!pxDefaultMesh->m_iNumberOfMergedEntities) continue;

		bool bAnyDeleted = false;
		bool bModuleDeleted = false;

		for(Asura_Mesh* pxPlatMesh = pxDefaultMesh ; pxPlatMesh && !bModuleDeleted ; pxPlatMesh = pxPlatMesh->m_pxNextPlatformMesh)
		{
			if(pxPlatMesh->m_pxCollPolygonGuids && pxPlatMesh->m_pxMergedEntityCollisionMesh)
			{
				//find polygons that need deleting from the collision mesh
				bool* pbDeleteArray = NULL;
				int iNumPolygons = pxPlatMesh->m_pxMergedEntityCollisionMesh->GetNumberOfPolys();

				int iTo = 0;
				for(int j=0;j<iNumPolygons;j++)
				{
					if(pxPlatMesh->m_pxCollPolygonGuids[j] == uGuid)
					{
						if(!pbDeleteArray)
						{
							pbDeleteArray = new bool[iNumPolygons];
							memset(pbDeleteArray,0,sizeof(bool)*iNumPolygons);
						}
						pbDeleteArray[j] = true;
						
					}
					else
					{
						if(iTo != j)
						{
							pxPlatMesh->m_pxCollPolygonGuids[iTo] = pxPlatMesh->m_pxCollPolygonGuids[j];
						}
						iTo++;
					}
				}
				if(pbDeleteArray)
				{
					
					pxPlatMesh->m_pxMergedEntityCollisionMesh->DeletePolygons(pbDeleteArray);
					delete [] pbDeleteArray;


					if(pxPlatMesh->m_pxMergedEntityCollisionMesh->GetNumberOfPolys() == 0)
					{
						delete pxPlatMesh->m_pxMergedEntityCollisionMesh;
						pxPlatMesh->m_pxMergedEntityCollisionMesh = 0;

						delete [] pxPlatMesh->m_pxCollPolygonGuids;
						pxPlatMesh->m_pxCollPolygonGuids = 0;
					}

					bAnyDeleted = true;
				}

			}
		
			if(pxPlatMesh->m_pxPolygonGuids)
			{
				//find all the polygons that need to be deleted
				bool* pbDeleteArray = NULL;
				int iNumPolygons = pxPlatMesh->NumberOfPolygons;
				for(int j=0;j<iNumPolygons;j++)
				{
					if(pxPlatMesh->m_pxPolygonGuids[j] == uGuid)
					{
						if(!pbDeleteArray)
						{
							pbDeleteArray = new bool[pxPlatMesh->NumberOfPolygons];
							memset(pbDeleteArray,0,sizeof(bool)*pxPlatMesh->NumberOfPolygons);
						}
						pbDeleteArray[j] = true;
					}
				}
				if(pbDeleteArray)
				{
					
					DeletePolygonsInModule(i,pxPlatMesh,pbDeleteArray,&bModuleDeleted);
					delete [] pbDeleteArray;

					bAnyDeleted = true;
				}
			}
		}

		if(bAnyDeleted && !bModuleDeleted)
		{
			//delete the guid/checksum for this entity		
			for(int j=0;j<pxDefaultMesh->m_iNumberOfMergedEntities;j++)
			{
				if(pxDefaultMesh->m_pxMergedEntities[j].m_uGuid == uGuid)
				{
					pxDefaultMesh->m_pxMergedEntities[j] = pxDefaultMesh->m_pxMergedEntities[--pxDefaultMesh->m_iNumberOfMergedEntities];
					break;
				}
			}
		}
	}
}

void Tools_Mode_MeshEdit::DeleteAllEntityPolygons()
{
	for(int i=Asura_Module_System::GetNumberOfModules()-1;i>=0;i--)
	{
		Asura_Mesh* pxCurrentMesh = Asura_Module_System::GetMeshForModule(i);
		bool bModuleDeleted = false;

		for(Asura_Mesh* pxPlatMesh = pxCurrentMesh->GetDefaultPlatformMesh() ; pxPlatMesh && !bModuleDeleted; pxPlatMesh = pxPlatMesh->m_pxNextPlatformMesh)
		{
			if(!pxPlatMesh->m_pxPolygonGuids) continue;

			if(pxPlatMesh->m_pxMergedEntityCollisionMesh)
			{
				delete [] pxPlatMesh->m_pxCollPolygonGuids;
				delete [] pxPlatMesh->m_pxMergedEntityCollisionMesh;
				pxPlatMesh->m_pxCollPolygonGuids = 0;
				pxPlatMesh->m_pxMergedEntityCollisionMesh = 0;

			}


			//find all the polygons that need to be deleted
			bool* pbDeleteArray = NULL;
			int iNumPolygons = pxPlatMesh->NumberOfPolygons;
			for(int j=0;j<iNumPolygons;j++)
			{
				if(pxPlatMesh->m_pxPolygonGuids[j] != ASURA_GUID_UNREGISTERED)
				{
					if(!pbDeleteArray)
					{
						pbDeleteArray = new bool[pxPlatMesh->NumberOfPolygons];
						memset(pbDeleteArray,0,sizeof(bool)*pxPlatMesh->NumberOfPolygons);
					}
					pbDeleteArray[j] = true;
				}
			}
			delete [] pxPlatMesh->m_pxMergedEntities;
			pxPlatMesh->m_pxMergedEntities = 0;
			pxPlatMesh->m_iNumberOfMergedEntities = 0;

			if(pbDeleteArray)
			{
				DeletePolygonsInModule(i,pxPlatMesh,pbDeleteArray, &bModuleDeleted);
				delete [] pbDeleteArray;
			}
		}
	}
}

void Tools_Mode_MeshEdit::DeleteModule(int iModuleIndex)
{
	Tools_SaveAsura::SetModuleModified(iModuleIndex);
	
	Tools_Polygon_Selection::DeleteModule(iModuleIndex);
	Tools_Vertex_Selection::DeleteModule(iModuleIndex);
	Tools_VertexPerPoly_Selection::DeleteModule(iModuleIndex);
	Tools_Edge_Selection::DeleteModule(iModuleIndex);
	Tools_Module_Selection::DeleteModule(iModuleIndex);
	Tools_Render_Options::DeleteModule(iModuleIndex);

	Asura_ModuleReverb_System::DeleteModule(iModuleIndex);
	Asura_Lighting_System::DeleteModule(iModuleIndex);
	Tools_PhotonMapping_System::DeleteModule(iModuleIndex);
	Asura_Environment_System::GetEnvironment()->DeleteModule(iModuleIndex);

#ifdef ASURA_USE_NEW_VISIBILITY_SYSTEM
	Asura_Visibility_System::Tools_DeleteModule( iModuleIndex );
#endif


	Asura_Module_System::DeleteModule(iModuleIndex);

	// throw away any previous LOS data
	Tools_Environment_LOS::Reset();

	if(iModuleIndex < Asura_Module_System::GetNumberOfModules())
	{
		//The module that got moved into the deleted module's slot will
		//need to have its render mesh updated , so that it knows about the new
		//module index.
		//RAR 27/06/2006
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
		Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);
	}

	Tools_VertexMove_Object::DeleteAll();

#if defined( ASURA_USE_CARPET)
	if( true == Asura_Direct3D_Carpet_Game_Render::HasDataSet())
	{
		Asura_Direct3D_Carpet_Game_Render::DeleteModule( iModuleIndex);
	}
#endif

#if defined( ASURA_USE_BOTANICALS)
	if( true == Asura_Direct3D_Botanicals_Game_Render::HasDataSet())
	{
		Asura_Direct3D_Botanicals_Game_Render::DeleteModule( iModuleIndex);
	}
#endif

	Tools_EnvironmentalLightTransport::DeleteModule( iModuleIndex);

	//undo for selection will no longer be valid
	Tools_Polygon_Selection::ClearUndo();
	Tools_Vertex_Selection::ClearUndo();
	Tools_Adjacency::Invalidate();

	#if defined( ASURA_USE_CARPET)
	//the carpet data refers to polygon/vertex indeces for lighting.
	Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
	#endif


	Tools_Undo_System::ClearUndo();

}


bool Tools_Mode_MeshEdit::DeletePolygons(Asura_Mesh* Mesh,bool* DeleteArray,int** OutputPolyConv)
{
	// throw away any previous LOS data
	Tools_Environment_LOS::Reset();

	Asura_Assert(Mesh->PolygonDataValid && Mesh->VertexDataValid,"");
	*OutputPolyConv = 0;

	//allocate a polygon and vertex conversion array
	int* PolyConv = new int[Mesh->NumberOfPolygons];
	int* VertConv = new int[Mesh->NumberOfVertices];

	//mark all the vertices with -1 to indicate that they aren't needed (yet)
	for(int i=0;i<Mesh->NumberOfVertices;i++)
	{
		//-2 indicates vertices that aren't known to be used even before
		//polygon removal
		VertConv[i] = -2;
	}

	int NewNumberOfPolygons = 0;
	int NewNumberOfTriangles = 0;
	int NewNumberOfVertices = 0;

	//check each polygon to see which is being kept
	int i;
	for(i=0;i<Mesh->NumberOfPolygons;i++)
	{
		int vertexMark;
		int num_verts = Mesh->PolygonDataPtr[i].NumberOfVertices();
		if(DeleteArray[i])
		{
			//polygon to be killed
			PolyConv[i] = -1;
			
			//-1 indicates that the vertex was ever used , but may no longer be
			vertexMark = -1;
		}
		else
		{
			//give the polygon the next index
			PolyConv[i] = NewNumberOfPolygons++;
			NewNumberOfTriangles+= num_verts-2;

			//0 indicates that the vertex is still used
			vertexMark = 0;
		}
		
		//mark all vertices with these polygons according to whether the polygon is being kep or destroyed
		for(int j=0;j<num_verts;j++)
		{
			if(VertConv[Mesh->PolygonDataPtr[i].VertexID[j]]<vertexMark)
			{
				VertConv[Mesh->PolygonDataPtr[i].VertexID[j]] = vertexMark;
			}
		}

	}

	//now assign each vertex that is to be kept its new index number
	for(i=0;i<Mesh->NumberOfVertices;i++)
	{
		if(VertConv[i]!=-1)
		{
			VertConv[i] = NewNumberOfVertices++;
		}
	}

	
	//allocate the new polygon array , and copy the polygons to be kept
	Asura_Mesh_Polygon* NewPolygonDataPtr = new Asura_Mesh_Polygon[NewNumberOfPolygons];
	for(i=0;i<Mesh->NumberOfPolygons;i++)
	{
		if(PolyConv[i]==-1) continue;

		Asura_Mesh_Polygon* new_poly = &NewPolygonDataPtr[PolyConv[i]];
		*new_poly = Mesh->PolygonDataPtr[i];

		//adjust vertex indeces in the polygon
		int num_verts = new_poly->NumberOfVertices();
		for(int j=0;j<num_verts;j++)
		{
			new_poly->VertexID[j] = (uint16)VertConv[new_poly->VertexID[j]];
		}
		  
	}
	//replace the polygon array
	delete [] Mesh->PolygonDataPtr;
	Mesh->PolygonDataPtr = NewPolygonDataPtr;

	//deal with the vertex array
	Asura_Mesh_Vertex* NewVertexDataPtr = new Asura_Mesh_Vertex[NewNumberOfVertices];
	for(i=0;i<Mesh->NumberOfVertices;i++)
	{
		if(VertConv[i]==-1) continue;

		NewVertexDataPtr[VertConv[i]] = Mesh->VertexDataPtr[i];
	}

	delete [] Mesh->VertexDataPtr;
	Mesh->VertexDataPtr = NewVertexDataPtr;

	// JGP deal with vertex painted colours
	if( Mesh->m_bPaintedColourDataValid )
	{
		Asura_Mesh_PaintedColours* pxNewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ NewNumberOfPolygons ];
		for( i = 0; i < Mesh->NumberOfPolygons; i++ )
		{
			if( PolyConv[ i ] == -1 ) 
			{
				continue;
			}
			pxNewPaintedColourDataPtr[ PolyConv[ i ] ] = Mesh->m_pxPaintedColourDataPtr[ i ];
		}
		delete [] Mesh->m_pxPaintedColourDataPtr;
		Mesh->m_pxPaintedColourDataPtr = pxNewPaintedColourDataPtr;
	}

	//deal with texturing
	if(Mesh->TextureDataValid)
	{
		Asura_Mesh_TextureInfo* NewTextureDataPtr = new Asura_Mesh_TextureInfo[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			if(PolyConv[i]==-1) continue;
			NewTextureDataPtr[PolyConv[i]] = Mesh->TextureDataPtr[i];
		}
		delete [] Mesh->TextureDataPtr;
		Mesh->TextureDataPtr = NewTextureDataPtr;
	}
	Mesh->DeleteProcessedTextureInfo();

	//deal with smoothing groups
	if(Mesh->SmoothingGroupDataValid)
	{
		int* NewSmoothingGroupDataPtr = new int[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			if(PolyConv[i]==-1) continue;
			NewSmoothingGroupDataPtr[PolyConv[i]] = Mesh->SmoothingGroupDataPtr[i];
		}
		delete [] Mesh->SmoothingGroupDataPtr;
		Mesh->SmoothingGroupDataPtr = NewSmoothingGroupDataPtr;
	}

	//deal with polygon flags
	if(Mesh->PolygonFlagDataValid)
	{
		Asura_Mesh_PolygonFlags* NewPolygonFlagDataPtr = new Asura_Mesh_PolygonFlags[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			if(PolyConv[i]==-1) continue;
			NewPolygonFlagDataPtr[PolyConv[i]] = Mesh->PolygonFlagDataPtr[i];
		}
		delete [] Mesh->PolygonFlagDataPtr;
		Mesh->PolygonFlagDataPtr = NewPolygonFlagDataPtr;
	}

	//and polygon guids
	if(Mesh->m_pxPolygonGuids)
	{
		bool bGuidsNeeded = false;
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			if(PolyConv[i]==-1) continue;
			Mesh->m_pxPolygonGuids[PolyConv[i]] = Mesh->m_pxPolygonGuids[i];
			if(Mesh->m_pxPolygonGuids[i] != ASURA_GUID_UNREGISTERED) bGuidsNeeded = true;
		}
		if(!bGuidsNeeded)
		{
			//can delete the array since there are no remaining polygons that belong to entities
			delete [] Mesh->m_pxPolygonGuids;
			Mesh->m_pxPolygonGuids = 0;
		}
	}

	// Max/maya polygon indeces
	if(Mesh->m_bPolygonIndexDataValid)
	{
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			if(PolyConv[i]==-1) continue;
			Mesh->m_piPolygonIndexDataPtr[PolyConv[i]] = Mesh->m_piPolygonIndexDataPtr[i];
		}
	}

	//and polygon lighting ids
	if(Mesh->m_puPolygonLightIDs)
	{
		List<u_int> xLightIDsUsed;
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			if(PolyConv[i]==-1) continue;
			u_int uLightID = Mesh->m_puPolygonLightIDs[i];
			Mesh->m_puPolygonLightIDs[PolyConv[i]] = uLightID;

			//build list of light ids still in use
			if(uLightID && !xLightIDsUsed.contains(uLightID))
			{
				xLightIDsUsed.add_entry(uLightID);
			}
		}

		if(!xLightIDsUsed.size())
		{
			//no remaining light IDs... so the array is redundant
			delete [] Mesh->m_puPolygonLightIDs;
			Mesh->m_puPolygonLightIDs = 0;
		}
		
		//now check the light data array for any entries that are no longer needed
		Asura_Mesh_PolygonLight* pxLightData = Mesh->m_pxPolygonLights;
		for(int i=0;i<Mesh->m_iNumberOfPolygonLights;i++,pxLightData++)
		{
			if(!xLightIDsUsed.contains(pxLightData->m_uLightID))
			{
				//delete this one
				*pxLightData = Mesh->m_pxPolygonLights[Mesh->m_iNumberOfPolygonLights-1];
				pxLightData--;
				i--;
				Mesh->m_iNumberOfPolygonLights--;
			}
		}
		if(!Mesh->m_iNumberOfPolygonLights)
		{
			delete [] Mesh->m_pxPolygonLights;
			Mesh->m_pxPolygonLights = 0;
		}

	}

	//project polygon data
	if(Mesh->m_pxProjectPolygonData)
	{
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			if(PolyConv[i]==-1) continue;
			Mesh->m_pxProjectPolygonData[PolyConv[i]] = Mesh->m_pxProjectPolygonData[i];
		}
	}

	//vertex flags
	if(Mesh->m_pxVertexFlags)
	{
		for(i=0;i<Mesh->NumberOfVertices;i++)
		{
			if(VertConv[i]==-1) continue;
			Mesh->m_pxVertexFlags[VertConv[i]] = Mesh->m_pxVertexFlags[i];
		}
	}
	
	//normals , prelighting , bounding boxes can be recalculated
	Mesh->InvalidateNormals();
	
	delete [] Mesh->PrelightingDataPtr;
	Mesh->PrelightingDataPtr = NULL;
	Mesh->PrelightingDataValid = false;
	
	Mesh->BoundingBoxDataValid = false;
	
	//update the number of polygons and vertices
	Mesh->NumberOfPolygons = NewNumberOfPolygons;
	Mesh->NumberOfVertices = NewNumberOfVertices;
	Mesh->NumberOfTriangles = NewNumberOfTriangles;

	Mesh->CreateNormals();
	Mesh->CalculateBoundingBox();


	//get rid of the conversion arrays
	delete [] VertConv;

	*OutputPolyConv = PolyConv;

	Asura_Mesh_System::PlatformResetRenderMesh(Mesh);
	Tools_RenderModule_System::UpdateModuleMesh(Mesh->Index);

	return true;
	
}

int Tools_Mode_MeshEdit::DeleteVerticesFromModule(int iModuleIndex,const bool* pbDeleteArray)
{
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);

	//Determine new number of vertices ,and vertex conversion array
	int* piVertConv = new int[pxMesh->NumberOfVertices];
	int  iNewNumVerts = 0;
	int iOldNumVerts = pxMesh->NumberOfVertices;

	for(int i=0;i<pxMesh->NumberOfVertices;i++)
	{
		bool bDelete = false;
		if(pbDeleteArray[i])
		{
			bDelete = true;
			//make sure the vertex isn't used by any polygon
			for(int j=0;j<pxMesh->NumberOfPolygons && bDelete;j++)
			{
				const Asura_Mesh_Polygon* pxPoly = &pxMesh->PolygonDataPtr[j];
				for(int k=0;k<pxPoly->NumberOfVertices();k++)
				{
					if(pxPoly->VertexID[k] == i)
					{
						bDelete = false;
						break;
					}
				}
			}
		}
		if(bDelete)
		{
			piVertConv[i] = -1;
		}
		else
		{
			piVertConv[i] = iNewNumVerts++;
		}
	}

	if(iNewNumVerts == pxMesh->NumberOfVertices)
	{
		//nothing to delete
		delete [] piVertConv;
		return 0;
	}

	//Shuffle vertices down to remove the deleted ones
	for(int i=0;i<pxMesh->NumberOfVertices;i++)
	{
		int iNewIndex = piVertConv[i];
		if(iNewIndex == i || iNewIndex == -1) continue;

		pxMesh->VertexDataPtr[iNewIndex] = pxMesh->VertexDataPtr[i];
	}
	pxMesh->NumberOfVertices = iNewNumVerts;

	//Update vertex indeces in the polygon data
	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		Asura_Mesh_Polygon* pxPoly = &pxMesh->PolygonDataPtr[i];
		int iVertsInPoly = pxPoly->NumberOfVertices();

		for(int j=0;j<iVertsInPoly;j++)
		{
			int iNewIndex = piVertConv[pxPoly->VertexID[j]];
			Asura_Assert(iNewIndex != -1,"Vertex deletion gone pear-shaped");
			pxPoly->VertexID[j] = (u_short) iNewIndex;
		}
	}

	//update vertex flags
	if(pxMesh->m_pxVertexFlags)
	{
		for(int i=0;i<pxMesh->NumberOfVertices;i++)
		{
			if(piVertConv[i] == -1) continue;
			pxMesh->m_pxVertexFlags[piVertConv[i]] = pxMesh->m_pxVertexFlags[i];
		}
	}


	//Invalidate stuff
	pxMesh->BoundingBoxDataValid = false;
	pxMesh->CalculateBoundingBox();

	InvalidateModuleCollisionMesh(iModuleIndex);
	Tools_Adjacency::InvalidateModule(iModuleIndex);

	//Update vertex selection
	Tools_Vertex_Selection::HandleVertexConversion(iModuleIndex,piVertConv);

	#if defined( ASURA_USE_CARPET)
	//the carpet data refers to polygon/vertex indeces for lighting.
	Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
	#endif

	
	delete [] piVertConv;
	piVertConv = 0;

	Tools_SaveAsura::SetMeshModified(pxMesh);

	return iOldNumVerts-iNewNumVerts;
}

void Tools_Mode_MeshEdit::CopyMesh(Asura_Mesh* pxInputMesh, Asura_Mesh* pxOutputMesh, bool (*pfnPolyTestCallback)(Asura_Mesh*, int) /* = 0 */, u_int uFlags /* = 0 */)
{
	Asura_Assert(pxInputMesh->PolygonDataValid && pxInputMesh->VertexDataValid,"Invalid input mesh");

	int i;

	//allocate a polygon and vertex conversion array
	int* PolyConv = new int[pxInputMesh->NumberOfPolygons];
	int* VertConv = new int[pxInputMesh->NumberOfVertices];

	//mark all the vertices with -2 to indicate that they aren't needed (yet)
	for(int i=0;i<pxInputMesh->NumberOfVertices;i++)
	{
		//-2 indicates vertices that aren't known to be used even before
		//polygon removal
		VertConv[i] = -2;
	}

	const int uNewNumberOfVerticesMax = 0x10000;
	int NewNumberOfPolygons = 0;
	int NewNumberOfTriangles = 0;
	int NewNumberOfVertices = 0;

	// find out which polygons are being copied
	for(i = 0; i < pxInputMesh->NumberOfPolygons; ++i)
	{
		const bool bShouldCopyThisPoly = (!pfnPolyTestCallback) || ((pfnPolyTestCallback)(pxInputMesh, i));
		int iVertexMarker;
	
		if(bShouldCopyThisPoly)
		{
			PolyConv[i] = NewNumberOfPolygons++;
			NewNumberOfTriangles += pxInputMesh->PolygonDataPtr[i].NumberOfVertices() - 2;
			iVertexMarker = 0;
		}
		else
		{
			PolyConv[i] = -1;
			iVertexMarker = -1;
		}

		// mark vertices used by this polygon.
		for( int j = 0; j < pxInputMesh->PolygonDataPtr[i].NumberOfVertices(); ++j )
		{
			// why do this test? Because some polygons may share vertices.
			if(VertConv[pxInputMesh->PolygonDataPtr[i].VertexID[j]] < iVertexMarker)
			{
				VertConv[pxInputMesh->PolygonDataPtr[i].VertexID[j]] = iVertexMarker;
			}
		}
	}

	// Because vertices may be shared, we postpone counting how many vertices we're copying until now
	for( i = 0; i < pxInputMesh->NumberOfVertices; ++i )
	{
		if(VertConv[i] != -1)
		{
			VertConv[i] = NewNumberOfVertices++;
		}
	}

	Asura_Assertf( NewNumberOfVertices < uNewNumberOfVerticesMax, ("%d vertices being copied from '%s' to '%s'. Max == %d", NewNumberOfVertices, pxInputMesh->Name.GetString(), pxOutputMesh->Name.GetString(), uNewNumberOfVerticesMax));

	// Now we have a pair of arrays representing the target indices of each polygon/vertex in the original mesh
	// along with counters for each. We can go ahead and build new arrays now.
	// We'll keep all the arrays around until the end, and put them all into the destination mesh atomically
	// This should allow people to provide the same mesh as input and output (i.e. mesh filtering) without problems.

	Asura_Mesh_Polygon*			pxNewPolygonDataPtr			= NULL;
	Asura_Mesh_Vertex*			pxNewVertexDataPtr			= NULL;
	Asura_Mesh_TextureInfo*		pxNewTextureDataPtr			= NULL;
	int*						piNewSmoothingGroupDataPtr	= NULL;
	Asura_Mesh_PolygonFlags*	pxNewPolygonFlagsDataPtr	= NULL;
	Asura_Guid*					puNewPolygonGuidsDataPtr	= NULL;
	u_int*						puNewPolygonLightIDsDataPtr	= NULL;
	Asura_Mesh_PolygonLight*	pxNewPolygonLightsDataPtr	= NULL;
	u_int						uNewNumPolygonLights		= 0;
	Asura_Mesh_Prelighting*		pxNewPrelightingDataPtr		= NULL;
	Asura_Mesh_PaintedColours*	pxNewPaintedColourDataPtr	= NULL;
	Asura_Mesh_ProjectPolygonData* pxNewProjectPolygonData  = NULL;
	Asura_Mesh_VertexFlags*		pxNewVertexFlags			= NULL;

	// Start with a new polygon array. 
	pxNewPolygonDataPtr = new Asura_Mesh_Polygon[NewNumberOfPolygons];
	for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i )
	{
		// skip polygons that are marked 'don't copy.'
		if(PolyConv[i]==-1) continue;

		// copy the polygon.
		Asura_Mesh_Polygon* new_poly = &pxNewPolygonDataPtr[PolyConv[i]];
		*new_poly = pxInputMesh->PolygonDataPtr[i];

		// adjust vertex indices in the new polygon
		int num_verts = new_poly->NumberOfVertices();
		for( int j = 0; j < num_verts; ++j )
		{
			const int uVertexID = VertConv[new_poly->VertexID[j]];
			Asura_Assertf( 0 <= uVertexID && uVertexID < uNewNumberOfVerticesMax, ("Vertex Index %d being copied into '%s' is too large. Max == %d", NewNumberOfVertices, pxOutputMesh->Name.GetString(), uNewNumberOfVerticesMax));
			new_poly->VertexID[j] = (uint16)uVertexID;
		}
	}

	// Do a similar thing for the vertex array.
	pxNewVertexDataPtr = new Asura_Mesh_Vertex[NewNumberOfVertices];
	for( i = 0; i < pxInputMesh->NumberOfVertices; ++i)
	{
		if(VertConv[i]==-1) continue;

		pxNewVertexDataPtr[VertConv[i]] = pxInputMesh->VertexDataPtr[i];
	}

	// Texturing, if the original mesh had any
	if(pxInputMesh->TextureDataValid && !(uFlags & CMNoTextures))
	{
		pxNewTextureDataPtr = new Asura_Mesh_TextureInfo[NewNumberOfPolygons];
		for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i )
		{
			if(PolyConv[i]==-1) continue;
			pxNewTextureDataPtr[PolyConv[i]] = pxInputMesh->TextureDataPtr[i];
		}
	}

	// JGP - Painted colour

	{
		pxNewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ NewNumberOfPolygons ];
		for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i )
		{
			if( PolyConv[i] == -1 ) continue;
			pxNewPaintedColourDataPtr[ PolyConv[ i ] ] = pxInputMesh->m_pxPaintedColourDataPtr[ i ];
		}
	}

	// Smoothing groups
	if(pxInputMesh->SmoothingGroupDataValid && !(uFlags & CMNoSmoothingGroups))
	{
		piNewSmoothingGroupDataPtr = new int[NewNumberOfPolygons];
		for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i)
		{
			if(PolyConv[i]==-1) continue;
			piNewSmoothingGroupDataPtr[PolyConv[i]] = pxInputMesh->SmoothingGroupDataPtr[i];
		}
	}

	// Polygon flags
	if(pxInputMesh->PolygonFlagDataValid && !(uFlags & CMNoPolyFlags))
	{
		pxNewPolygonFlagsDataPtr = new Asura_Mesh_PolygonFlags[NewNumberOfPolygons];
		for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i)
		{
			if(PolyConv[i]==-1) continue;
			pxNewPolygonFlagsDataPtr[PolyConv[i]] = pxInputMesh->PolygonFlagDataPtr[i];
		}
	}

	// Polygon guids
	if(pxInputMesh->m_pxPolygonGuids && !(uFlags & CMNoPolyGuids))
	{
		// before we blindly allocate another hunk of memory, are there any guids actually worth storing?
		bool bGuidsNeeded = false;
		for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i)
		{
			if(PolyConv[i] == -1) continue;
			if(pxInputMesh->m_pxPolygonGuids[i] != ASURA_GUID_UNREGISTERED) { bGuidsNeeded = true; break; }
		}
		if(bGuidsNeeded)
		{
			// we have at least one valid guid
			puNewPolygonGuidsDataPtr = new Asura_Guid[NewNumberOfPolygons];
			for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i)
			{
				if(PolyConv[i] == -1) continue;
				puNewPolygonGuidsDataPtr[PolyConv[i]] = pxInputMesh->m_pxPolygonGuids[i];
			}
		}
	}

	// Lighting IDs
	if(pxInputMesh->m_puPolygonLightIDs && !(uFlags & CMNoPolyLights))
	{
		// first find out which lighting IDs are still in use
		List<u_int> xLightIDsUsed;
		for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i )
		{
			if(PolyConv[i] == -1) continue;
			u_int uThisLightID = pxInputMesh->m_puPolygonLightIDs[i];
			if(uThisLightID && !xLightIDsUsed.contains(uThisLightID))
			{
				xLightIDsUsed.add_entry(uThisLightID);
			}
		}
		if(xLightIDsUsed.size())
		{
			// ok, so we'll actually have to allocate some arrays.
			puNewPolygonLightIDsDataPtr = new u_int[NewNumberOfPolygons];
			pxNewPolygonLightsDataPtr = new Asura_Mesh_PolygonLight[xLightIDsUsed.size()];
			uNewNumPolygonLights = 0;

			// the light IDs aren't really indices, so we won't worry about remapping them
			for(i = 0; i < pxInputMesh->NumberOfPolygons; ++i)
			{
				if(PolyConv[i] == -1) continue;
				puNewPolygonLightIDsDataPtr[PolyConv[i]] = pxInputMesh->m_puPolygonLightIDs[i];
			}

			for(i = 0; i < pxInputMesh->m_iNumberOfPolygonLights; ++i )
			{
				if(xLightIDsUsed.contains(pxInputMesh->m_pxPolygonLights[i].m_uLightID))
				{
					// keep this light
					pxNewPolygonLightsDataPtr[uNewNumPolygonLights++] = pxInputMesh->m_pxPolygonLights[i];
				}
			}

			Asura_Assert(uNewNumPolygonLights == (unsigned)xLightIDsUsed.size(), "Number of lights copied doesn't add up!");
		}
	}

	// Prelighting
	if(pxInputMesh->PrelightingDataValid && !(uFlags & CMNoPrelighting))
	{
		pxNewPrelightingDataPtr = new Asura_Mesh_Prelighting[NewNumberOfPolygons];
		for( i = 0; i < pxInputMesh->NumberOfPolygons; ++i )
		{
			if(PolyConv[i] == -1) continue;
			pxNewPrelightingDataPtr[PolyConv[i]] = pxInputMesh->PrelightingDataPtr[i];
		}
	}

	//project polygon data
	if(pxInputMesh->m_pxProjectPolygonData && !(uFlags & CMNoProjectPolyData))
	{
		pxNewProjectPolygonData = new Asura_Mesh_ProjectPolygonData[NewNumberOfPolygons];
		for(int i=0; i<pxInputMesh->NumberOfPolygons; i++)
		{
			if(PolyConv[i] == -1) continue;
			pxNewProjectPolygonData[PolyConv[i]] = pxInputMesh->m_pxProjectPolygonData[i];
		}
	}

	//vertex flags
	if(pxInputMesh->m_pxVertexFlags && !(uFlags & CMNoVertFlags))
	{
		pxNewVertexFlags = new Asura_Mesh_VertexFlags[NewNumberOfVertices];
		for(int i=0; i<pxInputMesh->NumberOfVertices; i++)
		{
			if(VertConv[i] == -1) continue;
			pxNewVertexFlags[VertConv[i]] = pxInputMesh->m_pxVertexFlags[i];
		}
	}

	// OK, we've gathered all our new data. Clean up any data the output mesh currently has...
	if (pxOutputMesh->VertexDataPtr)					delete [] pxOutputMesh->VertexDataPtr;					pxOutputMesh->VertexDataPtr						= NULL;					
	if (pxOutputMesh->PolygonDataPtr)					delete [] pxOutputMesh->PolygonDataPtr;					pxOutputMesh->PolygonDataPtr					= NULL;
	if (pxOutputMesh->TextureDataPtr)					delete [] pxOutputMesh->TextureDataPtr;					pxOutputMesh->TextureDataPtr					= NULL;
	if (pxOutputMesh->PrelightingDataPtr)				delete [] pxOutputMesh->PrelightingDataPtr;				pxOutputMesh->PrelightingDataPtr				= NULL;
	if (pxOutputMesh->SmoothingGroupDataPtr)			delete [] pxOutputMesh->SmoothingGroupDataPtr;			pxOutputMesh->SmoothingGroupDataPtr				= NULL;
	if (pxOutputMesh->PolygonFlagDataPtr)				delete [] pxOutputMesh->PolygonFlagDataPtr;				pxOutputMesh->PolygonFlagDataPtr				= NULL;
	if (pxOutputMesh->m_pxPolygonGuids)					delete [] pxOutputMesh->m_pxPolygonGuids;				pxOutputMesh->m_pxPolygonGuids					= NULL;
	if (pxOutputMesh->m_pxPolygonLights)				delete [] pxOutputMesh->m_pxPolygonLights;				pxOutputMesh->m_pxPolygonLights					= NULL;
	if (pxOutputMesh->m_puPolygonLightIDs)				delete [] pxOutputMesh->m_puPolygonLightIDs;			pxOutputMesh->m_puPolygonLightIDs				= NULL;
	if (pxOutputMesh->m_pxPaintedColourDataPtr)			delete [] pxOutputMesh->m_pxPaintedColourDataPtr;		pxOutputMesh->m_pxPaintedColourDataPtr			= NULL;
	if (pxOutputMesh->m_pxProjectPolygonData)			delete [] pxOutputMesh->m_pxProjectPolygonData;			pxOutputMesh->m_pxProjectPolygonData 			= NULL;
	if (pxOutputMesh->m_pxVertexFlags)					delete [] pxOutputMesh->m_pxVertexFlags;				pxOutputMesh->m_pxVertexFlags 					= NULL;
	pxOutputMesh->m_iNumberOfPolygonLights = 0;

	// Install our new data
	pxOutputMesh->PolygonDataPtr		= pxNewPolygonDataPtr;			pxOutputMesh->PolygonDataValid			= (pxNewPolygonDataPtr != 0);
	pxOutputMesh->VertexDataPtr			= pxNewVertexDataPtr;			pxOutputMesh->VertexDataValid			= (pxNewVertexDataPtr != 0);
	pxOutputMesh->TextureDataPtr		= pxNewTextureDataPtr;			pxOutputMesh->TextureDataValid			= (pxNewTextureDataPtr != 0);
	pxOutputMesh->SmoothingGroupDataPtr	= piNewSmoothingGroupDataPtr;	pxOutputMesh->SmoothingGroupDataValid	= (piNewSmoothingGroupDataPtr != 0);
	pxOutputMesh->PolygonFlagDataPtr	= pxNewPolygonFlagsDataPtr;		pxOutputMesh->PolygonFlagDataValid		= (pxNewPolygonFlagsDataPtr != 0);
	pxOutputMesh->PrelightingDataPtr	= pxNewPrelightingDataPtr;		pxOutputMesh->PrelightingDataValid		= (pxNewPrelightingDataPtr != 0);
	pxOutputMesh->m_pxPolygonGuids		= puNewPolygonGuidsDataPtr;
	pxOutputMesh->m_puPolygonLightIDs	= puNewPolygonLightIDsDataPtr;
	pxOutputMesh->m_pxPolygonLights		= pxNewPolygonLightsDataPtr;
	pxOutputMesh->m_pxPaintedColourDataPtr	= pxNewPaintedColourDataPtr;
	pxOutputMesh->m_iNumberOfPolygonLights = uNewNumPolygonLights;
	pxOutputMesh->m_pxProjectPolygonData = pxNewProjectPolygonData;
	pxOutputMesh->m_pxVertexFlags = pxNewVertexFlags;

	// Flag normals and collision data for recalculation

	pxOutputMesh->InvalidateNormals();
	pxOutputMesh->InvalidatePolygonIndices();

	pxOutputMesh->DeleteProcessedTextureInfo();

	pxOutputMesh->BoundingBoxDataValid = false;

	pxOutputMesh->NumberOfPolygons = NewNumberOfPolygons;
	pxOutputMesh->NumberOfTriangles = NewNumberOfTriangles;
	pxOutputMesh->NumberOfVertices = NewNumberOfVertices;
	
	pxOutputMesh->CopyMiscDataFrom( pxInputMesh );
	
	pxOutputMesh->m_iSourceFileType = pxInputMesh->m_iSourceFileType;

	// we're done here. clean up the temporaries we created
	delete[] VertConv;
	delete[] PolyConv;

	// Note: we don't call Asura_Mesh_System::PlatformResetRenderMesh() or Tools_RenderModule_System::UpdateModuleMesh() from here
	// If you need them, call them yourselves (after giving the new mesh a module index).
}

void Tools_Mode_MeshEdit::MergeMeshes(Asura_Mesh* pxOutputMesh, Asura_Mesh* pxMesh1, Asura_Mesh* pxMesh2, u_int uFlags /* = 0 */)
{
	const u_int uNewNumberOfVerticesMax = 0x10000;
	const u_int uNewNumberOfPolygonsMax = uNewNumberOfVerticesMax * 4;
	int uPolyIndex;

	// check the validity of data in the input meshes - if a data element in either mesh is invalid, force the
	// 'no copy' flag for that element on
	
	// we can't do anything at all without poly/vertex data, so assert on it
	Asura_Assert(pxMesh1->PolygonDataValid && pxMesh1->VertexDataValid, "MergeMeshes input mesh 1 has invalid polygon or vertex data");
	Asura_Assert(pxMesh2->PolygonDataValid && pxMesh2->VertexDataValid, "MergeMeshes input mesh 2 has invalid polygon or vertex data");

	// now set flags
	u_int uEffectiveFlags = uFlags;
	if(!pxMesh1->TextureDataValid || !pxMesh2->TextureDataValid) uEffectiveFlags |= CMNoTextures;
	if(!pxMesh1->SmoothingGroupDataValid || !pxMesh2->SmoothingGroupDataValid) uEffectiveFlags |= CMNoSmoothingGroups;
	if(!pxMesh1->PolygonFlagDataValid || !pxMesh2->PolygonFlagDataValid) uEffectiveFlags |= CMNoPolyFlags;
	if(!pxMesh1->m_pxPolygonGuids || !pxMesh2->m_pxPolygonGuids) uEffectiveFlags |= CMNoPolyGuids;
	if(!pxMesh1->m_puPolygonLightIDs || !pxMesh2->m_puPolygonLightIDs) uEffectiveFlags |= CMNoPolyLights;
	if(!pxMesh1->m_pxPolygonLights || !pxMesh2->m_pxPolygonLights) uEffectiveFlags |= CMNoPolyLights;
	if(!pxMesh1->PrelightingDataValid || !pxMesh2->PrelightingDataValid) uEffectiveFlags |= CMNoPrelighting;
	Asura_Assert(uEffectiveFlags == uFlags, "One or more data elements are invalid, but the 'no copy' flag for those element were not set");


	Asura_Mesh_Polygon*			pxNewPolygonDataPtr			= NULL;
	Asura_Mesh_Vertex*			pxNewVertexDataPtr			= NULL;
	Asura_Mesh_TextureInfo*		pxNewTextureDataPtr			= NULL;
	int*						piNewSmoothingGroupDataPtr	= NULL;
	Asura_Mesh_PolygonFlags*	pxNewPolygonFlagsDataPtr	= NULL;
	Asura_Guid*					puNewPolygonGuidsDataPtr	= NULL;
	u_int*						puNewPolygonLightIDsDataPtr	= NULL;
	Asura_Mesh_PolygonLight*	pxNewPolygonLightsDataPtr	= NULL;
	u_int						uNewNumPolygonLights		= 0;
	Asura_Mesh_Prelighting*		pxNewPrelightingDataPtr		= NULL;
	Asura_Mesh_PaintedColours*	pxNewPaintedColourDataPtr	= NULL;
	Asura_Mesh_ProjectPolygonData* pxNewProjectPolygonData	= NULL;
	Asura_Mesh_VertexFlags*		pxNewVertexFlags			= NULL;

	const u_int uNewNumberOfVertices = pxMesh1->NumberOfVertices + pxMesh2->NumberOfVertices;
	Asura_Assertf( uNewNumberOfVertices <= uNewNumberOfVerticesMax, ("Trying to copy %d vertices. Max Vertices is %d.", uNewNumberOfVertices, uNewNumberOfVerticesMax));
	pxNewVertexDataPtr = new Asura_Mesh_Vertex[ uNewNumberOfVertices];
	memcpy(pxNewVertexDataPtr, pxMesh1->VertexDataPtr, sizeof(Asura_Mesh_Vertex) * pxMesh1->NumberOfVertices);
	memcpy(pxNewVertexDataPtr + pxMesh1->NumberOfVertices, pxMesh2->VertexDataPtr, sizeof(Asura_Mesh_Vertex) * pxMesh2->NumberOfVertices);

	const u_int uNewNumberOfPolygons = pxMesh1->NumberOfPolygons + pxMesh2->NumberOfPolygons;
	Asura_Assertf( uNewNumberOfPolygons <= uNewNumberOfPolygonsMax, ("Trying to copy %d polygons. Max Polygons is %d.", uNewNumberOfPolygons, uNewNumberOfPolygonsMax ) );
	pxNewPolygonDataPtr = new Asura_Mesh_Polygon[uNewNumberOfPolygons];
	memcpy(pxNewPolygonDataPtr, pxMesh1->PolygonDataPtr, sizeof(Asura_Mesh_Polygon) * pxMesh1->NumberOfPolygons);
	memcpy(pxNewPolygonDataPtr + pxMesh1->NumberOfPolygons, pxMesh2->PolygonDataPtr, sizeof(Asura_Mesh_Polygon) * pxMesh2->NumberOfPolygons);
	// go through the mesh2 polygon data and remap vertex indices
	for(uPolyIndex = 0; uPolyIndex < pxMesh2->NumberOfPolygons; ++uPolyIndex)
	{
		Asura_Mesh_Polygon& xPoly = pxNewPolygonDataPtr[pxMesh1->NumberOfPolygons + uPolyIndex];
		for(int iVertexIndex = 0; iVertexIndex < xPoly.NumberOfVertices(); ++iVertexIndex)
		{
			int uNewVertexID = xPoly.VertexID[iVertexIndex] + pxMesh1->NumberOfVertices;
			Asura_Assertf( uNewVertexID <= uNewNumberOfVerticesMax, ("Trying to vertexID %d. Max VertexID is %d.", uNewVertexID, uNewNumberOfVerticesMax));
			xPoly.VertexID[iVertexIndex] = static_cast<unsigned short>(uNewVertexID);
		}
	}

	if(!(uEffectiveFlags & CMNoTextures))
	{	
		pxNewTextureDataPtr = new Asura_Mesh_TextureInfo[uNewNumberOfPolygons];	
		memcpy(pxNewTextureDataPtr, pxMesh1->TextureDataPtr, sizeof(Asura_Mesh_TextureInfo) * pxMesh1->NumberOfPolygons);
		memcpy(pxNewTextureDataPtr+pxMesh1->NumberOfPolygons, pxMesh2->TextureDataPtr, sizeof(Asura_Mesh_TextureInfo) * pxMesh2->NumberOfPolygons);
	}

	// JGP - 14/06/06
	{	
		Asura_Assert( pxMesh1->m_pxPaintedColourDataPtr, "" );
		Asura_Assert( pxMesh2->m_pxPaintedColourDataPtr, "" );
		Asura_Assert( uNewNumberOfPolygons > 0, "" );
		pxNewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ uNewNumberOfPolygons ];	
		Asura_Assert( pxNewPaintedColourDataPtr, "" );
		memcpy(pxNewPaintedColourDataPtr, pxMesh1->m_pxPaintedColourDataPtr, sizeof( Asura_Mesh_PaintedColours ) * pxMesh1->NumberOfPolygons );
		memcpy(pxNewPaintedColourDataPtr + pxMesh1->NumberOfPolygons, pxMesh2->m_pxPaintedColourDataPtr, sizeof( Asura_Mesh_PaintedColours ) * pxMesh2->NumberOfPolygons );
	}

	if(!(uEffectiveFlags & CMNoSmoothingGroups))
	{	
		piNewSmoothingGroupDataPtr = new int[ uNewNumberOfPolygons ];	
		memcpy(piNewSmoothingGroupDataPtr, pxMesh1->SmoothingGroupDataPtr, sizeof(int) * pxMesh1->NumberOfPolygons);
		// straight concatenate the smoothing group data. This is going to mean that the same smoothing group IDs will
		// get reused, but I don't think it matters because references to smoothing group IDs also require adjacency
		// because our merged meshes won't share any vertices, the smoothing groups shouldn't interfere
		memcpy(piNewSmoothingGroupDataPtr + pxMesh1->NumberOfPolygons, pxMesh2->SmoothingGroupDataPtr, sizeof(int) * pxMesh2->NumberOfPolygons);
	}
	if(!(uEffectiveFlags & CMNoPolyFlags))
	{	
		pxNewPolygonFlagsDataPtr = new Asura_Mesh_PolygonFlags[ uNewNumberOfPolygons ];	
		memcpy(pxNewPolygonFlagsDataPtr, pxMesh1->PolygonFlagDataPtr, sizeof(Asura_Mesh_PolygonFlags) * pxMesh1->NumberOfPolygons);
		memcpy(pxNewPolygonFlagsDataPtr + pxMesh1->NumberOfPolygons, pxMesh2->PolygonFlagDataPtr, sizeof(Asura_Mesh_PolygonFlags) * pxMesh2->NumberOfPolygons);
	}
	if(!(uEffectiveFlags & CMNoPolyGuids))
	{	
		puNewPolygonGuidsDataPtr = new Asura_Guid[ uNewNumberOfPolygons ];	
		memcpy(puNewPolygonGuidsDataPtr, pxMesh1->m_pxPolygonGuids, sizeof(Asura_Guid) * pxMesh1->NumberOfPolygons);
		memcpy(puNewPolygonGuidsDataPtr + pxMesh1->NumberOfPolygons, pxMesh2->m_pxPolygonGuids, sizeof(Asura_Guid) * pxMesh2->NumberOfPolygons);
	}
	if(!(uEffectiveFlags & CMNoPolyLights))
	{	
		puNewPolygonLightIDsDataPtr = new u_int[ uNewNumberOfPolygons ];
		memcpy(puNewPolygonLightIDsDataPtr, pxMesh1->m_puPolygonLightIDs, sizeof(u_int) * pxMesh1->NumberOfPolygons);
		memcpy(puNewPolygonLightIDsDataPtr + pxMesh1->NumberOfPolygons, pxMesh2->m_puPolygonLightIDs, sizeof(u_int) * pxMesh2->NumberOfPolygons);
		// ok, we can't get away with just concatenating polygon lights - we need to ensure that IDs are kept unique
		// we'll remap IDs such that light id == index in polylight array
		for(uPolyIndex = 0; uPolyIndex < pxMesh1->NumberOfPolygons; ++uPolyIndex)
		{
			u_int uLightID = puNewPolygonLightIDsDataPtr[uPolyIndex];
			if(!uLightID)continue; // no light for this poly
			// find this light ID in the mesh1 polylight array
			for(int iLightIndex = 0; iLightIndex < pxMesh1->m_iNumberOfPolygonLights; ++iLightIndex)
			{
				if(pxMesh1->m_pxPolygonLights[iLightIndex].m_uLightID == uLightID)
				{
					// found it
					// remap the light
					puNewPolygonLightIDsDataPtr[uPolyIndex] = iLightIndex;
					break;
				}
			}
		}
		// now do mesh 2 - same as mesh 1 except remapped indices are offset
		for(uPolyIndex = 0; uPolyIndex < pxMesh2->NumberOfPolygons; ++uPolyIndex)
		{
			u_int uLightID = puNewPolygonLightIDsDataPtr[pxMesh1->NumberOfPolygons + uPolyIndex];
			if(!uLightID)continue; // no light for this poly
			// find this light ID in the mesh1 polylight array
			for(int iLightIndex = 0; iLightIndex < pxMesh2->m_iNumberOfPolygonLights; ++iLightIndex)
			{
				if(pxMesh2->m_pxPolygonLights[iLightIndex].m_uLightID == uLightID)
				{
					// found it
					// remap the light
					puNewPolygonLightIDsDataPtr[pxMesh1->NumberOfPolygons + uPolyIndex] = pxMesh1->m_iNumberOfPolygonLights + iLightIndex;
					break;
				}
			}
		}

		// now that we've remapped all the light IDs, we can copy across the polylights themselves, and update their IDs
		pxNewPolygonLightsDataPtr = new Asura_Mesh_PolygonLight[pxMesh1->m_iNumberOfPolygonLights + pxMesh2->m_iNumberOfPolygonLights];	
		memcpy(pxNewPolygonLightsDataPtr, pxMesh1->m_pxPolygonLights, sizeof(Asura_Mesh_PolygonLight) * pxMesh1->m_iNumberOfPolygonLights);
		memcpy(pxNewPolygonLightsDataPtr + pxMesh1->m_iNumberOfPolygonLights, pxMesh2->m_pxPolygonLights, sizeof(Asura_Mesh_PolygonLight) * pxMesh2->m_iNumberOfPolygonLights);
		for(int iLightIndex = 0; iLightIndex < pxMesh1->m_iNumberOfPolygonLights + pxMesh2->m_iNumberOfPolygonLights; ++iLightIndex)
		{
			pxNewPolygonLightsDataPtr[iLightIndex].m_uLightID = iLightIndex;
		}

		uNewNumPolygonLights = pxMesh1->m_iNumberOfPolygonLights + pxMesh2->m_iNumberOfPolygonLights;
	}
	if(!(uEffectiveFlags & CMNoPrelighting))
	{	
		pxNewPrelightingDataPtr = new Asura_Mesh_Prelighting[pxMesh1->NumberOfPolygons + pxMesh2->NumberOfPolygons];	
		memcpy(pxNewPrelightingDataPtr, pxMesh1->PrelightingDataPtr, sizeof(Asura_Mesh_Prelighting) * pxMesh1->NumberOfPolygons);
		memcpy(pxNewPrelightingDataPtr + pxMesh1->NumberOfPolygons, pxMesh2->PrelightingDataPtr, sizeof(Asura_Mesh_Prelighting) * pxMesh2->NumberOfPolygons);
	}

	if(!(uEffectiveFlags & CMNoProjectPolyData) && (pxMesh1->m_pxProjectPolygonData || pxMesh2->m_pxProjectPolygonData))
	{
		pxNewProjectPolygonData = new Asura_Mesh_ProjectPolygonData[pxMesh1->NumberOfPolygons + pxMesh2->NumberOfPolygons];
		if(pxMesh1->m_pxProjectPolygonData)
		{
			memcpy(pxNewProjectPolygonData, pxMesh1->m_pxProjectPolygonData, sizeof(Asura_Mesh_ProjectPolygonData) * pxMesh1->NumberOfPolygons);
		}
		else
		{
			Asura_Mem::Set(pxNewProjectPolygonData,0,sizeof(Asura_Mesh_ProjectPolygonData) * pxMesh1->NumberOfPolygons);
		}

		if(pxMesh2->m_pxProjectPolygonData)
		{
			memcpy(pxNewProjectPolygonData + pxMesh1->NumberOfPolygons, pxMesh2->m_pxProjectPolygonData, sizeof(Asura_Mesh_ProjectPolygonData) * pxMesh2->NumberOfPolygons);
		}
		else
		{
			Asura_Mem::Set(pxNewProjectPolygonData+ pxMesh1->NumberOfPolygons,0,sizeof(Asura_Mesh_ProjectPolygonData) * pxMesh2->NumberOfPolygons);
		}
	}

	if(!(uEffectiveFlags & CMNoVertFlags) && (pxMesh1->m_pxVertexFlags || pxMesh2->m_pxVertexFlags))
	{
		pxNewVertexFlags = new Asura_Mesh_VertexFlags[pxMesh1->NumberOfVertices + pxMesh2->NumberOfVertices];
		if(pxMesh1->m_pxVertexFlags)
		{
			memcpy(pxNewVertexFlags, pxMesh1->m_pxVertexFlags, sizeof(Asura_Mesh_VertexFlags) * pxMesh1->NumberOfVertices);
		}
		else
		{
			Asura_Mem::Set(pxNewVertexFlags,0,sizeof(Asura_Mesh_VertexFlags) * pxMesh1->NumberOfVertices);
		}

		if(pxMesh2->m_pxVertexFlags)
		{
			memcpy(pxNewVertexFlags + pxMesh1->NumberOfVertices, pxMesh2->m_pxVertexFlags, sizeof(Asura_Mesh_VertexFlags) * pxMesh2->NumberOfVertices);
		}
		else
		{
			Asura_Mem::Set(pxNewVertexFlags+ pxMesh1->NumberOfVertices,0,sizeof(Asura_Mesh_VertexFlags) * pxMesh2->NumberOfVertices);
		}
	}

	// now that we've built all our data, install it into the output mesh
	if (pxOutputMesh->VertexDataPtr)					delete [] pxOutputMesh->VertexDataPtr;					pxOutputMesh->VertexDataPtr						= NULL;					
	if (pxOutputMesh->PolygonDataPtr)					delete [] pxOutputMesh->PolygonDataPtr;					pxOutputMesh->PolygonDataPtr					= NULL;
	if (pxOutputMesh->TextureDataPtr)					delete [] pxOutputMesh->TextureDataPtr;					pxOutputMesh->TextureDataPtr					= NULL;
	if (pxOutputMesh->PrelightingDataPtr)				delete [] pxOutputMesh->PrelightingDataPtr;				pxOutputMesh->PrelightingDataPtr				= NULL;
	if (pxOutputMesh->SmoothingGroupDataPtr)			delete [] pxOutputMesh->SmoothingGroupDataPtr;			pxOutputMesh->SmoothingGroupDataPtr				= NULL;
	if (pxOutputMesh->PolygonFlagDataPtr)				delete [] pxOutputMesh->PolygonFlagDataPtr;				pxOutputMesh->PolygonFlagDataPtr				= NULL;
	if (pxOutputMesh->m_pxPolygonGuids)					delete [] pxOutputMesh->m_pxPolygonGuids;				pxOutputMesh->m_pxPolygonGuids					= NULL;
	if (pxOutputMesh->m_pxPolygonLights)				delete [] pxOutputMesh->m_pxPolygonLights;				pxOutputMesh->m_pxPolygonLights					= NULL;
	if (pxOutputMesh->m_puPolygonLightIDs)				delete [] pxOutputMesh->m_puPolygonLightIDs;			pxOutputMesh->m_puPolygonLightIDs				= NULL;
	if (pxOutputMesh->m_pxPaintedColourDataPtr)			delete [] pxOutputMesh->m_pxPaintedColourDataPtr;		pxOutputMesh->m_pxPaintedColourDataPtr			= NULL;
	if (pxOutputMesh->m_pxProjectPolygonData)			delete [] pxOutputMesh->m_pxProjectPolygonData;			pxOutputMesh->m_pxProjectPolygonData			= NULL;
	if (pxOutputMesh->m_pxVertexFlags)					delete [] pxOutputMesh->m_pxVertexFlags;				pxOutputMesh->m_pxVertexFlags					= NULL;
	pxOutputMesh->m_iNumberOfPolygonLights = 0;

	// Install our new data
	pxOutputMesh->PolygonDataPtr		= pxNewPolygonDataPtr;			pxOutputMesh->PolygonDataValid			= (pxNewPolygonDataPtr != 0);
	pxOutputMesh->VertexDataPtr			= pxNewVertexDataPtr;			pxOutputMesh->VertexDataValid			= (pxNewVertexDataPtr != 0);
	pxOutputMesh->TextureDataPtr		= pxNewTextureDataPtr;			pxOutputMesh->TextureDataValid			= (pxNewTextureDataPtr != 0);
	pxOutputMesh->SmoothingGroupDataPtr	= piNewSmoothingGroupDataPtr;	pxOutputMesh->SmoothingGroupDataValid	= (piNewSmoothingGroupDataPtr != 0);
	pxOutputMesh->PolygonFlagDataPtr	= pxNewPolygonFlagsDataPtr;		pxOutputMesh->PolygonFlagDataValid		= (pxNewPolygonFlagsDataPtr != 0);
	pxOutputMesh->PrelightingDataPtr	= pxNewPrelightingDataPtr;		pxOutputMesh->PrelightingDataValid		= (pxNewPrelightingDataPtr != 0);
	pxOutputMesh->m_pxPolygonGuids		= puNewPolygonGuidsDataPtr;
	pxOutputMesh->m_puPolygonLightIDs	= puNewPolygonLightIDsDataPtr;
	pxOutputMesh->m_pxPolygonLights		= pxNewPolygonLightsDataPtr;
	pxOutputMesh->m_pxPaintedColourDataPtr	= pxNewPaintedColourDataPtr;
	pxOutputMesh->m_pxProjectPolygonData	= pxNewProjectPolygonData;
	pxOutputMesh->m_pxVertexFlags	= pxNewVertexFlags;
	pxOutputMesh->m_iNumberOfPolygonLights = uNewNumPolygonLights;

	// Flag normals and collision data for recalculation

	pxOutputMesh->InvalidateNormals();
	pxOutputMesh->InvalidatePolygonIndices();

	pxOutputMesh->DeleteProcessedTextureInfo();

	pxOutputMesh->BoundingBoxDataValid = false;

	pxOutputMesh->NumberOfPolygons = pxMesh1->NumberOfPolygons + pxMesh2->NumberOfPolygons;
	pxOutputMesh->NumberOfTriangles = pxMesh1->NumberOfTriangles + pxMesh2->NumberOfTriangles;
	pxOutputMesh->NumberOfVertices = pxMesh1->NumberOfVertices + pxMesh2->NumberOfVertices;

	pxOutputMesh->CopyMiscDataFrom( pxMesh1 );

}

void Tools_Mode_MeshEdit::WeldVertices(Asura_Mesh* pxMesh, float fEpsilon /* = 1.0e-6f */)
{
	int* piVertConv = new int[pxMesh->NumberOfVertices];
	u_int uNextVertexIndex = 0;
	for(int u = 0; u < pxMesh->NumberOfVertices; ++u)
	{
		piVertConv[u] = -1;
	}

	for(int u = 0; u < pxMesh->NumberOfVertices; ++u)
	{
		if(piVertConv[u] != -1) continue; // this vertex has been remapped
		piVertConv[u] = uNextVertexIndex++;

		Asura_Vector_3 xBaseVector = pxMesh->VertexDataPtr[u].Position;

		// find all non-remapped vertices that are approximately equal to this one
		for(int j = u+1; j < pxMesh->NumberOfVertices; ++j)
		{
			if(piVertConv[j] != -1) continue;
			if(pxMesh->VertexDataPtr[j].Position.ApproximatelyEquals(xBaseVector, fEpsilon))
			{
				piVertConv[j] = piVertConv[u];
			}
		}
	}

	// process the polys and remap vertex IDs
	for(int u = 0; u < pxMesh->NumberOfPolygons; ++u)
	{
		for(int j = 0; j < pxMesh->PolygonDataPtr[u].NumberOfVertices(); ++j)
		{
			pxMesh->PolygonDataPtr[u].VertexID[j] = static_cast<unsigned short>(piVertConv[pxMesh->PolygonDataPtr[u].VertexID[j]]);
		}
	}

	// allocate a new vertex data array
	Asura_Mesh_Vertex* pxNewVertices = new Asura_Mesh_Vertex[uNextVertexIndex];
	for(int u = 0; u < pxMesh->NumberOfVertices; ++u)
	{
		pxNewVertices[piVertConv[u]] = pxMesh->VertexDataPtr[u];
	}

	// vertex flags
	if(pxMesh->m_pxVertexFlags)
	{
		for(int i=0;i<pxMesh->NumberOfVertices;i++)
		{
			pxMesh->m_pxVertexFlags[piVertConv[i]] = pxMesh->m_pxVertexFlags[i];
		}
	}

	delete[] pxMesh->VertexDataPtr;
	pxMesh->VertexDataPtr = pxNewVertices;
	pxMesh->NumberOfVertices = uNextVertexIndex;

	delete[] piVertConv;
}

void Tools_Mode_MeshEdit::UnmergeSelectedPolygons()
{
	if(!Tools_Polygon_Selection::NumSelectedModules)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"No polygons have been selected");
		return;
	}

	Asura_Mesh* CurrentMesh = 0;
	int CurrentModule = -1;
	bool* UnmergeArray = 0;


	PolygonID polygon;
	Tools_Polygon_Selection::StartIteration();

	while(Tools_Polygon_Selection::GetNextSelected(&polygon))
	{
		if(polygon.ModuleIndex != CurrentModule)
		{
			//moved onto a new module.
			//delete the polygons in the previous mesh first
			if(CurrentModule != -1)
			{
				UnmergePolygons(CurrentModule,UnmergeArray);
				delete [] UnmergeArray;
			}
			
			//get the mesh for the selected polygon , and allocate an array indicating
			//which polygons should be unmerged
			CurrentModule = polygon.ModuleIndex;
			CurrentMesh = Asura_Module_System::GetMeshForModule(CurrentModule);

			UnmergeArray = new bool[CurrentMesh->NumberOfPolygons];
			for(int i=0;i<CurrentMesh->NumberOfPolygons;i++)
			{
				UnmergeArray[i] = false;
			}
				
		}

		//flag this polygon for unmerging
		UnmergeArray[polygon.PolyIndex] = true;
	}
	
	if(CurrentModule != -1)
	{
		UnmergePolygons(CurrentModule,UnmergeArray);
		delete [] UnmergeArray;
	}

	Tools_Timed_Message_System::AddMessage(0xffffff,"Polygons Unmerged");

}

void Tools_Mode_MeshEdit::UnmergePolygons(int iModuleIndex,bool* pbUnmergeArray)
{
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);

	int *piPolyConv1,*piPolyConv2;
	if(UnmergePolygons(pxMesh,pbUnmergeArray,&piPolyConv1,&piPolyConv2))
	{
		Tools_Polygon_Selection::HandlePolygonConversion(iModuleIndex,piPolyConv1,piPolyConv2);
		Tools_VertexPerPoly_Selection::HandleMeshChange(iModuleIndex);
		Tools_Edge_Selection::HandleMeshChange(iModuleIndex);

		Tools_Render_Options::HandlePolygonConversion(iModuleIndex,piPolyConv1,piPolyConv2);
		if(pxMesh->m_pxCollMeshGenData)
		{
			pxMesh->m_pxCollMeshGenData->HandlePolygonConversion(pxMesh,piPolyConv1,piPolyConv2);
		}

		InvalidateModuleCollisionMesh(iModuleIndex);

		//undo for selection will no longer be valid
		Tools_Polygon_Selection::ClearUndo();
		Tools_Adjacency::InvalidateModule(iModuleIndex);

		Tools_Undo_System::ClearUndo();

		#if defined( ASURA_USE_CARPET)
		//the carpet data refers to polygon/vertex indeces for lighting.
		Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
		#endif


		Tools_SaveAsura::SetMeshModified(pxMesh);

		delete [] piPolyConv1;
		delete [] piPolyConv2;
	}
}

static bool* s_bBoolArray = NULL;
static bool BoolArrayTester(Asura_Mesh* pxMesh, int iPoly)
{
	if(!s_bBoolArray) return false;
	return s_bBoolArray[iPoly];
}

bool Tools_Mode_MeshEdit::UnmergePolygons(Asura_Mesh* Mesh, bool* UnmergeArray, int** OutputPolyConv1, int** OutputPolyConv2)
{
	s_bBoolArray = UnmergeArray;
	bool bResult = UnmergePolygons(Mesh, BoolArrayTester, OutputPolyConv1, OutputPolyConv2);
	Asura_Mesh_System::PlatformResetRenderMesh(Mesh);
	Tools_RenderModule_System::UpdateModuleMesh(Mesh->Index);
	return bResult;
}

bool Tools_Mode_MeshEdit::UnmergePolygons(Asura_Mesh* Mesh,bool (*pfnPolyTestCallback)(Asura_Mesh*, int), int** OutputPolyConv1,int** OutputPolyConv2)
{
	*OutputPolyConv1 = 0;
	*OutputPolyConv2 = 0;
	
	//allocate polygon conversion arrays
	int* PolyConv1 = new int[Mesh->NumberOfPolygons];
	int* PolyConv2 = new int[Mesh->NumberOfPolygons];//second array for the newly created triangles

	int NewNumberOfPolygons = 0;

	//check each polygon , to see which are being unmerged
	for(int i=0;i<Mesh->NumberOfPolygons;i++)
	{
		PolyConv1[i] = NewNumberOfPolygons++;
		PolyConv2[i] = -1;

		//is this a quad being unmerged?
		if(pfnPolyTestCallback && !(pfnPolyTestCallback(Mesh, i))) continue;
		if(Mesh->PolygonDataPtr[i].NumberOfVertices()!=4) continue;

		//this one should become a new triangle
		PolyConv2[i] = NewNumberOfPolygons++;
	}

	//allocate the new polygon array , and copy the polygons
	Asura_Mesh_Polygon* NewPolygonDataPtr = new Asura_Mesh_Polygon[NewNumberOfPolygons];
	int i;
	for(i=0;i<Mesh->NumberOfPolygons;i++)
	{
		//polygon from PolyConv1 is copied straight across
		Asura_Mesh_Polygon* poly1 = &NewPolygonDataPtr[PolyConv1[i]];
		*poly1 = Mesh->PolygonDataPtr[i];

		if(PolyConv2[i] == -1) continue;
		//remove the last vertex from the polygon
		poly1->VertexID[3] = 0xffff;

		//copy into the new triangle
		Asura_Mesh_Polygon* poly2 = &NewPolygonDataPtr[PolyConv2[i]];
		*poly2 = Mesh->PolygonDataPtr[i];

		poly2->VertexID[0] = Mesh->PolygonDataPtr[i].VertexID[3];
		poly2->VertexID[1] = Mesh->PolygonDataPtr[i].VertexID[0];
		poly2->VertexID[2] = Mesh->PolygonDataPtr[i].VertexID[2];
		poly2->VertexID[3] = 0xffff;
		
	}
	delete [] Mesh->PolygonDataPtr;
	Mesh->PolygonDataPtr = NewPolygonDataPtr;

	// JGP deal with painted colours
	if ( Mesh->m_bPaintedColourDataValid )
	{
		Asura_Mesh_PaintedColours* pxNewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ NewNumberOfPolygons ];
		for( i = 0; i < Mesh->NumberOfPolygons; i++ )
		{
			pxNewPaintedColourDataPtr[ PolyConv1[ i ] ] = Mesh->m_pxPaintedColourDataPtr[ i ];
			if( PolyConv2[ i ] == -1 ) continue;

			//copy texturing for the new triangle
			Asura_Mesh_PaintedColours* pxPaintedColourTo = &pxNewPaintedColourDataPtr[ PolyConv2[ i ] ];
			Asura_Mesh_PaintedColours* pxPaintedColourFrom = &Mesh->m_pxPaintedColourDataPtr[ i ];

			pxPaintedColourTo->Colour[ 0 ] = pxPaintedColourFrom->Colour[ 3 ];
			pxPaintedColourTo->Colour[ 1 ] = pxPaintedColourFrom->Colour[ 0 ];
			pxPaintedColourTo->Colour[ 2 ] = pxPaintedColourFrom->Colour[ 2 ];
		}
		delete [] Mesh->m_pxPaintedColourDataPtr;
		Mesh->m_pxPaintedColourDataPtr = pxNewPaintedColourDataPtr;
	}
	
	
	//deal with texturing
	if(Mesh->TextureDataValid)
	{
		Asura_Mesh_TextureInfo* NewTextureDataPtr = new Asura_Mesh_TextureInfo[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			NewTextureDataPtr[PolyConv1[i]] = Mesh->TextureDataPtr[i];
			if(PolyConv2[i]==-1) continue;

			//copy texturing for the new triangle
			Asura_Mesh_TextureInfo* texture_to = &NewTextureDataPtr[PolyConv2[i]];
			Asura_Mesh_TextureInfo* texture_from = &Mesh->TextureDataPtr[i];

			texture_to->m_iMaterialID = texture_from->m_iMaterialID;
			for(int iMat=0; iMat< TOOLS_NUM_MATERIALRESPONSE_PER_POLY; iMat++)
			{
				texture_to->m_auMaterialResponseHashID[iMat] = texture_from->m_auMaterialResponseHashID[iMat];
			}

			for(int j=0;j<ASURA_MATERIAL_MAXTEXTURES;j++)
			{
				texture_to->U[j][0] = texture_from->U[j][3];
				texture_to->V[j][0] = texture_from->V[j][3];
				texture_to->U[j][1] = texture_from->U[j][0];
				texture_to->V[j][1] = texture_from->V[j][0];
				texture_to->U[j][2] = texture_from->U[j][2];
				texture_to->V[j][2] = texture_from->V[j][2];
			}

			texture_to->m_afAlpha[0] = texture_from->m_afAlpha[3];
			texture_to->m_afAlpha[1] = texture_from->m_afAlpha[0];
			texture_to->m_afAlpha[2] = texture_from->m_afAlpha[2];
			

		}
		delete [] Mesh->TextureDataPtr;
		Mesh->TextureDataPtr = NewTextureDataPtr;
	}
	Mesh->DeleteProcessedTextureInfo();

	
	//deal with smoothing groups
	if(Mesh->SmoothingGroupDataValid)
	{
		int* NewSmoothingGroupDataPtr = new int[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			NewSmoothingGroupDataPtr[PolyConv1[i]] = Mesh->SmoothingGroupDataPtr[i];
			if(PolyConv2[i]==-1) continue;
			NewSmoothingGroupDataPtr[PolyConv2[i]] = Mesh->SmoothingGroupDataPtr[i];
		}
		delete [] Mesh->SmoothingGroupDataPtr;
		Mesh->SmoothingGroupDataPtr = NewSmoothingGroupDataPtr;
	}

	//deal with polygon flags
	if(Mesh->PolygonFlagDataValid)
	{
		Asura_Mesh_PolygonFlags* NewPolygonFlagDataPtr = new Asura_Mesh_PolygonFlags[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			NewPolygonFlagDataPtr[PolyConv1[i]] = Mesh->PolygonFlagDataPtr[i];
			if(PolyConv2[i]==-1) continue;
			NewPolygonFlagDataPtr[PolyConv2[i]] = Mesh->PolygonFlagDataPtr[i];
		}
		delete [] Mesh->PolygonFlagDataPtr;
		Mesh->PolygonFlagDataPtr = NewPolygonFlagDataPtr;
	}

	//deal with polygon guids
	if(Mesh->m_pxPolygonGuids)
	{
		Asura_Guid* pxNewPolygonGuids = new Asura_Guid[NewNumberOfPolygons];
		for(int i=0;i<Mesh->NumberOfPolygons;i++)
		{
			pxNewPolygonGuids[PolyConv1[i]] = Mesh->m_pxPolygonGuids[i];
			if(PolyConv2[i]==-1) continue;
			pxNewPolygonGuids[PolyConv2[i]] = Mesh->m_pxPolygonGuids[i];
		}

		delete [] Mesh->m_pxPolygonGuids;
		Mesh->m_pxPolygonGuids = pxNewPolygonGuids;
	}

	// Max/maya polygon indeces
	if(Mesh->m_bPolygonIndexDataValid)
	{
		int* piNewPolygonIndexData = new int[NewNumberOfPolygons];
		for(int i=0;i<Mesh->NumberOfPolygons;i++)
		{
			piNewPolygonIndexData[PolyConv1[i]] = Mesh->m_piPolygonIndexDataPtr[i];
			if(PolyConv2[i]==-1) continue;
			piNewPolygonIndexData[PolyConv2[i]] = Mesh->m_piPolygonIndexDataPtr[i];
		}

		delete [] Mesh->m_piPolygonIndexDataPtr;
		Mesh->m_piPolygonIndexDataPtr = piNewPolygonIndexData;
	}


	//and light IDs
	if(Mesh->m_puPolygonLightIDs)
	{
		u_int* pxNewLightIDs = new u_int[NewNumberOfPolygons];
		for(int i=0;i<Mesh->NumberOfPolygons;i++)
		{
			pxNewLightIDs[PolyConv1[i]] = Mesh->m_puPolygonLightIDs[i];
			if(PolyConv2[i]==-1) continue;
			pxNewLightIDs[PolyConv2[i]] = Mesh->m_puPolygonLightIDs[i];
		}

		delete [] Mesh->m_puPolygonLightIDs;
		Mesh->m_puPolygonLightIDs = pxNewLightIDs;
	}

	//project polygon data
	if(Mesh->m_pxProjectPolygonData)
	{
		Asura_Mesh_ProjectPolygonData * pxNewProjectPolygonData = new Asura_Mesh_ProjectPolygonData[NewNumberOfPolygons];
		for(int i=0;i<Mesh->NumberOfPolygons;i++)
		{
			pxNewProjectPolygonData[PolyConv1[i]] = Mesh->m_pxProjectPolygonData[i];
			if(PolyConv2[i]==-1) continue;
			pxNewProjectPolygonData[PolyConv2[i]] = Mesh->m_pxProjectPolygonData[i];
		}

		delete [] Mesh->m_pxProjectPolygonData;
		Mesh->m_pxProjectPolygonData = pxNewProjectPolygonData;
	}


	//normals , prelighting , bounding boxes can be recalculated
	if(Mesh->AreNormalsValid())
	{
		delete [] Mesh->NormalsDataPtr;
		Mesh->NormalsDataPtr = NULL;
		Mesh->m_bFaceNormalsValid = false;
		Mesh->m_bVertexNormalsValid = false;
	}
	
	delete [] Mesh->PrelightingDataPtr;
	Mesh->PrelightingDataPtr = NULL;
	Mesh->PrelightingDataValid = false;
	
	Mesh->BoundingBoxDataValid = false;
	
	//update the number of polygons
	Mesh->NumberOfPolygons = NewNumberOfPolygons;

	Mesh->CreateNormals();
	Mesh->CalculateBoundingBox();

	*OutputPolyConv1 = PolyConv1;
	*OutputPolyConv2 = PolyConv2;

	return true;

	
}


void Tools_Mode_MeshEdit::MergeSelectedPolygons()
{
	if(!Tools_Polygon_Selection::NumSelectedModules)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"No polygons have been selected");
		return;
	}

	Asura_Mesh* CurrentMesh = 0;
	int CurrentModule = -1;
	int* PolyConv = 0;
	List<int> MergeList;


	PolygonID polygon;
	Tools_Polygon_Selection::StartIteration();

	while(Tools_Polygon_Selection::GetNextSelected(&polygon))
	{
		if(polygon.ModuleIndex != CurrentModule)
		{
			//moved onto a new module.
			//merge the polygons in the previous mesh first
			if(CurrentMesh)
			{
				if(MergePolygons(CurrentMesh,MergeList,&PolyConv))
				{
					Asura_Mesh_System::PlatformResetRenderMesh(CurrentMesh);
					Tools_RenderModule_System::UpdateModuleMesh(CurrentMesh->Index);
					Tools_Polygon_Selection::HandlePolygonConversion(CurrentModule,PolyConv);
					Tools_VertexPerPoly_Selection::HandleMeshChange(CurrentModule);
					Tools_Edge_Selection::HandleMeshChange(CurrentModule);
					Tools_Render_Options::HandlePolygonConversion(CurrentModule,PolyConv);
					InvalidateModuleCollisionMesh(CurrentModule);
					Tools_Adjacency::InvalidateModule(CurrentModule);
					if(CurrentMesh->m_pxCollMeshGenData)
					{
						CurrentMesh->m_pxCollMeshGenData->HandlePolygonConversion(CurrentMesh,PolyConv);
					}

					Tools_Undo_System::ClearUndo();

				Tools_SaveAsura::SetMeshModified(CurrentMesh);


					delete [] PolyConv;
				}
				while(MergeList.size())MergeList.delete_first_entry();
			}
			
			//get the mesh for the selected polygon
			CurrentModule = polygon.ModuleIndex;
			CurrentMesh = Asura_Module_System::GetMeshForModule(CurrentModule);


				
		}

		//add to the list of polygons to merge
		MergeList.add_entry(polygon.PolyIndex);
	}
	
	if(CurrentMesh)
	{
		if(MergePolygons(CurrentMesh,MergeList,&PolyConv))
		{
			Asura_Mesh_System::PlatformResetRenderMesh(CurrentMesh);
			Tools_RenderModule_System::UpdateModuleMesh(CurrentMesh->Index);
			Tools_Polygon_Selection::HandlePolygonConversion(CurrentModule,PolyConv);
			Tools_VertexPerPoly_Selection::HandleMeshChange(CurrentModule);
			Tools_Edge_Selection::HandleMeshChange(CurrentModule);
			Tools_Render_Options::HandlePolygonConversion(CurrentModule,PolyConv);
			if(CurrentMesh->m_pxCollMeshGenData)
			{
				CurrentMesh->m_pxCollMeshGenData->HandlePolygonConversion(CurrentMesh,PolyConv);
			}
			InvalidateModuleCollisionMesh(CurrentModule);
			Tools_Adjacency::InvalidateModule(CurrentModule);

			Tools_Undo_System::ClearUndo();

			#if defined( ASURA_USE_CARPET)
			//the carpet data refers to polygon/vertex indeces for lighting.
			Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
			#endif

			Tools_SaveAsura::SetMeshModified(CurrentMesh);

			delete [] PolyConv;
		}
	}
	
	
	
	//undo for selection will no longer be valid
	Tools_Polygon_Selection::ClearUndo();
	


	Tools_Timed_Message_System::AddMessage(0xffffff,"Polygons Merged");

}


struct PossiblePolygonMerge
{
	int poly1;
	int poly2;
	float cos_angle;
};

bool Tools_Mode_MeshEdit::MergePolygons(Asura_Mesh* Mesh,List<int>& MergeList,int** OutputPolyConv)
{
	*OutputPolyConv = 0;

	//first chuck out any polygons that are already quads
	LIF<int> mlif(&MergeList);
	for(;!mlif.done();)
	{
		const int iPolygonIndex = mlif();
		Asura_Assertf( 0 <= iPolygonIndex && iPolygonIndex < Mesh->NumberOfPolygons, ("Polygon index %d is out of range. %d Polygons.", iPolygonIndex, Mesh->NumberOfPolygons));
		const Asura_Mesh_Polygon& xPolygon = Mesh->PolygonDataPtr[ iPolygonIndex];
		if( xPolygon.NumberOfVertices() == 4)
		{
			mlif.delete_current();
			continue;
		}
		
		if( xPolygon.VertexID[ 0] == xPolygon.VertexID[ 1] || xPolygon.VertexID[ 0] == xPolygon.VertexID[ 2] || xPolygon.VertexID[ 1] == xPolygon.VertexID[ 2])
		{
			Asura_Assertf( false, ("Polygon %d is degenerate.", iPolygonIndex));
			mlif.delete_current();
			continue;
		}
		
		mlif.next();
	}

	if(MergeList.size()<2) return false;

	//check each pair of triangles to see if they could be made into a quad
	List<PossiblePolygonMerge*> poss_merge_list;
	while(MergeList.size()>=2)
	{
		int poly1 = MergeList.first_entry();
		Asura_Assertf( 0 <= poly1 && poly1 < Mesh->NumberOfPolygons, ("Polygon index %d is out of range. %d Polygons.", poly1, Mesh->NumberOfPolygons));
		
		MergeList.delete_first_entry();

		//compare poly1 against each remaining polygon in the list
		for(mlif.restart();!mlif.done();mlif.next())
		{
			const int iPolygonIndex = mlif();
			Asura_Assertf( 0 <= iPolygonIndex && iPolygonIndex < Mesh->NumberOfPolygons, ("Polygon index %d is out of range. %d Polygons.", iPolygonIndex, Mesh->NumberOfPolygons));
			
			if(!CanPolygonsBeMerged(Mesh,poly1,iPolygonIndex)) continue;
			float cos_angle = GetAngleForPolygonMerge(Mesh,poly1,iPolygonIndex);

			//these 2 polygons could be merged
			//add an entry for them to the possible merge list , sorted by cos_angle
			PossiblePolygonMerge* ppm=new PossiblePolygonMerge;
			ppm->poly1=poly1;
			ppm->poly2=iPolygonIndex;
			ppm->cos_angle=cos_angle;

			//insert possible merge into ordered list
			LIF<PossiblePolygonMerge*> plif(&poss_merge_list);
			for(;!plif.done();plif.next())
			{
				if(cos_angle<=plif()->cos_angle) break;
			}
			if(plif.done())
				poss_merge_list.add_entry(ppm);
			else
				poss_merge_list.add_entry_before(ppm,plif());

		}
	}
	if(!poss_merge_list.size()) return false;

	// Line of Sight data will need to be recalculated
	Tools_Environment_LOS::Reset();

	//allocate an array to store the merges that will be done
	int* MergeArray = new int[Mesh->NumberOfPolygons];
	int i;
	for(i=0;i<Mesh->NumberOfPolygons;i++)
	{
		MergeArray[i] = -1;
	}

	//go through the list of possible merges , applying them in order of priority
	while(poss_merge_list.size())
	{
		PossiblePolygonMerge* ppm = poss_merge_list.first_entry();
		poss_merge_list.delete_first_entry();

		Asura_Assertf( -1 == ppm->poly1 || (0 <= ppm->poly1 && ppm->poly1 < Mesh->NumberOfPolygons), ("Polygon index %d is out of range. %d Polygons.", ppm->poly1, Mesh->NumberOfPolygons));
		Asura_Assertf( -1 == ppm->poly2 || (0 <= ppm->poly2 && ppm->poly2 < Mesh->NumberOfPolygons), ("Polygon index %d is out of range. %d Polygons.", ppm->poly2, Mesh->NumberOfPolygons));
		
		//are both polygons in this merge still unmerged?
		if(MergeArray[ppm->poly1] ==-1 && MergeArray[ppm->poly2] ==-1)
		{
			//mark these as merged
			MergeArray[ppm->poly1] = ppm->poly2;
			MergeArray[ppm->poly2] = ppm->poly1;
		}
		delete ppm;
	}


	//next need to get a polygon conversion array , and find the new number of polygons
	int* PolyConv = new int[Mesh->NumberOfPolygons];
	int NewNumberOfPolygons = 0;

	for(i=0;i<Mesh->NumberOfPolygons;i++)
	{
		if(MergeArray[i] == -1 || MergeArray[i] > i)
		{
			PolyConv[i] = NewNumberOfPolygons++;
		}
		else
		{
			PolyConv[i] = PolyConv[MergeArray[i]];
		}
	}

	//deal with the polygon, texturing, and prelighting data (JGP and painted colour)
	Asura_Mesh_Polygon* NewPolygonDataPtr = new Asura_Mesh_Polygon[NewNumberOfPolygons];
	Asura_Mesh_TextureInfo* NewTextureDataPtr = NULL;
	if(Mesh->TextureDataValid)
	{
		NewTextureDataPtr = new Asura_Mesh_TextureInfo[NewNumberOfPolygons];
	}
	Mesh->DeleteProcessedTextureInfo();

	Asura_Mesh_Prelighting* NewPrelightingDataPtr = NULL;
	if ( Mesh->PrelightingDataValid )
	{
		NewPrelightingDataPtr = new Asura_Mesh_Prelighting[ NewNumberOfPolygons ];
	}
	Asura_Mesh_PaintedColours* pxNewPaintedColourDataPtr = NULL;
	if ( Mesh->m_bPaintedColourDataValid )
	{
		pxNewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ NewNumberOfPolygons ];
	}

	for(i=0;i<Mesh->NumberOfPolygons;i++)
	{
		if(MergeArray[i] == -1)
		{
			//no merging here , just a straight copy
			NewPolygonDataPtr[PolyConv[i]] = Mesh->PolygonDataPtr[i];
			if(Mesh->TextureDataValid)
			{
				NewTextureDataPtr[PolyConv[i]] = Mesh->TextureDataPtr[i];
			}
			if(Mesh->PrelightingDataValid)
			{
				NewPrelightingDataPtr[PolyConv[i]] = Mesh->PrelightingDataPtr[i];
			}
			if ( Mesh->m_bPaintedColourDataValid )
			{
				pxNewPaintedColourDataPtr[ PolyConv[ i ] ] = Mesh->m_pxPaintedColourDataPtr[ i ];
			}
		}
		else if(MergeArray[i]>i)
		{
			//get the polygon data for the 2 triangles
			Asura_Mesh_Polygon* Poly1 = &Mesh->PolygonDataPtr[i];
			Asura_Mesh_Polygon* Poly2 = &Mesh->PolygonDataPtr[MergeArray[i]];

			//and the quad to copy to
			Asura_Mesh_Polygon* PolyQuad = &NewPolygonDataPtr[PolyConv[i]];

			//find the unshared vertices
			int UnsharedVertex1 = 3;
			int UnsharedVertex2 = 3;
			for(int j=0;j<3;j++)
			{
				for(int k=0;k<3;k++)
				{
					if(Poly1->VertexID[j] == Poly2->VertexID[k])
					{
						UnsharedVertex1 -= j;//doing this should give me the index of the unshared
						UnsharedVertex2 -= k;//vertices once we have finished
						break;
					}
				}
			}

			//get the order to take the vertices in the first polygon
			int VertOrder[3];
			VertOrder[0] = (UnsharedVertex1+2)%3;
			VertOrder[1] = (UnsharedVertex1);
			VertOrder[2] = (UnsharedVertex1+1)%3;

			//copy the vertices
			int j;
			for(j=0;j<3;j++)
			{
				PolyQuad->VertexID[j] = Poly1->VertexID[VertOrder[j]];
			}
			//and the unshared vertex in the second polygon
			PolyQuad->VertexID[3] = Poly2->VertexID[UnsharedVertex2];

			//copy texturing
			if(Mesh->TextureDataValid)
			{
				Asura_Mesh_TextureInfo* Texture1 = &Mesh->TextureDataPtr[i];
				Asura_Mesh_TextureInfo* Texture2 = &Mesh->TextureDataPtr[MergeArray[i]];

				Asura_Mesh_TextureInfo* TextureQuad = &NewTextureDataPtr[PolyConv[i]];

				TextureQuad->m_iMaterialID = Texture1->m_iMaterialID;
				for(int iMat=0 ;iMat < TOOLS_NUM_MATERIALRESPONSE_PER_POLY; iMat++)
				{
					TextureQuad->m_auMaterialResponseHashID[iMat] = Texture1->m_auMaterialResponseHashID[iMat];
				}

				for(int k=0;k<ASURA_MATERIAL_MAXTEXTURES;k++)
				{
					for(j=0;j<3;j++)
					{
						TextureQuad->U[k][j] = Texture1->U[k][VertOrder[j]];
						TextureQuad->V[k][j] = Texture1->V[k][VertOrder[j]];
					}
					//calculate the uv coordinates for the last point by taking the uv difference between point 2 and 3
					//in the second polygon , and adding on to the uv coordinates of point 2.
					//this should avoid any problems that might be caused by the uvs being on a different repeat of the texture
					TextureQuad->U[k][3] = TextureQuad->U[k][2] + Texture2->U[k][UnsharedVertex2] - Texture2->U[k][(UnsharedVertex2+2)%3];
					TextureQuad->V[k][3] = TextureQuad->V[k][2] + Texture2->V[k][UnsharedVertex2] - Texture2->V[k][(UnsharedVertex2+2)%3];
				}

				//copy alpha
				for(j=0;j<3;j++)
				{
					TextureQuad->m_afAlpha[j] = Texture1->m_afAlpha[VertOrder[j]];
				}
				TextureQuad->m_afAlpha[3] = Texture2->m_afAlpha[UnsharedVertex2];
				
			}
			Mesh->DeleteProcessedTextureInfo();


			//copy prelighting
			if(Mesh->PrelightingDataValid)
			{
				Asura_Mesh_Prelighting* Prelighting1 = &Mesh->PrelightingDataPtr[i];
				Asura_Mesh_Prelighting* Prelighting2 = &Mesh->PrelightingDataPtr[MergeArray[i]];

				Asura_Mesh_Prelighting* PrelightingQuad = &NewPrelightingDataPtr[PolyConv[i]];

				PrelightingQuad->Colour[0] = Prelighting1->Colour[VertOrder[0]];
				PrelightingQuad->Colour[1] = Prelighting1->Colour[VertOrder[1]];
				PrelightingQuad->Colour[2] = Prelighting1->Colour[VertOrder[2]];
				PrelightingQuad->Colour[3] = Prelighting2->Colour[UnsharedVertex2];
			}

			// copy painted colour
			if ( Mesh->m_bPaintedColourDataValid )
			{
				Asura_Mesh_PaintedColours* pxPaintedColour1 = &Mesh->m_pxPaintedColourDataPtr[ i ];
				Asura_Mesh_PaintedColours* pxPaintedColour2 = &Mesh->m_pxPaintedColourDataPtr[ MergeArray[ i ] ];

				Asura_Mesh_PaintedColours* pxNewPaintedColour = &pxNewPaintedColourDataPtr[ PolyConv[ i ] ];

				pxNewPaintedColour->Colour[ 0 ] = pxPaintedColour1->Colour[ VertOrder[ 0 ] ];
				pxNewPaintedColour->Colour[ 1 ] = pxPaintedColour1->Colour[ VertOrder[ 1 ] ];
				pxNewPaintedColour->Colour[ 2 ] = pxPaintedColour1->Colour[ VertOrder[ 2 ] ];
				pxNewPaintedColour->Colour[ 3 ] = pxPaintedColour2->Colour[ UnsharedVertex2 ];
			}
	}
	}

	delete [] Mesh->PolygonDataPtr;
	Mesh->PolygonDataPtr = NewPolygonDataPtr;
	if(Mesh->TextureDataValid)
	{
		delete [] Mesh->TextureDataPtr;
		Mesh->TextureDataPtr = NewTextureDataPtr;
	}
	Mesh->DeleteProcessedTextureInfo();

	if(Mesh->PrelightingDataValid)
	{
		delete[] Mesh->PrelightingDataPtr;
		Mesh->PrelightingDataPtr = NewPrelightingDataPtr;
	}
	if(Mesh->m_bPaintedColourDataValid)
	{
		delete[] Mesh->m_pxPaintedColourDataPtr;
		Mesh->m_pxPaintedColourDataPtr = pxNewPaintedColourDataPtr;
	}

	//deal with smoothing groups
	if(Mesh->SmoothingGroupDataValid)
	{
		int* NewSmoothingGroupDataPtr = new int[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			NewSmoothingGroupDataPtr[PolyConv[i]] = Mesh->SmoothingGroupDataPtr[i];
		}
		delete [] Mesh->SmoothingGroupDataPtr;
		Mesh->SmoothingGroupDataPtr = NewSmoothingGroupDataPtr;
	}

	//deal with polygon flags
	if(Mesh->PolygonFlagDataValid)
	{
		Asura_Mesh_PolygonFlags* NewPolygonFlagDataPtr = new Asura_Mesh_PolygonFlags[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			NewPolygonFlagDataPtr[PolyConv[i]] = Mesh->PolygonFlagDataPtr[i];
		}
		delete [] Mesh->PolygonFlagDataPtr;
		Mesh->PolygonFlagDataPtr = NewPolygonFlagDataPtr;
	}

	//and polygon guids
	if(Mesh->m_pxPolygonGuids)
	{
		Asura_Guid* pxNewPolygonGuids = new Asura_Guid[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			pxNewPolygonGuids[PolyConv[i]] = Mesh->m_pxPolygonGuids[i];
		}
		delete [] Mesh->m_pxPolygonGuids;
		Mesh->m_pxPolygonGuids = pxNewPolygonGuids;
		
	}

	// Max/maya polygon indeces
	if(Mesh->m_bPolygonIndexDataValid)
	{
		int* piNewPolygonIndexData = new int[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			piNewPolygonIndexData[PolyConv[i]] = Mesh->m_piPolygonIndexDataPtr[i];
		}
		delete [] Mesh->m_piPolygonIndexDataPtr;
		Mesh->m_piPolygonIndexDataPtr = piNewPolygonIndexData;
	}


	//and polygon light IDs
	if(Mesh->m_puPolygonLightIDs)
	{
		u_int* pxNewLightIDs = new u_int[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			pxNewLightIDs[PolyConv[i]] = Mesh->m_puPolygonLightIDs[i];
		}
		delete [] Mesh->m_puPolygonLightIDs;
		Mesh->m_puPolygonLightIDs = pxNewLightIDs;
		
	}

	//project polygon data
	if(Mesh->m_pxProjectPolygonData)
	{
		Asura_Mesh_ProjectPolygonData* pxNewProjectPolygonData = new Asura_Mesh_ProjectPolygonData[NewNumberOfPolygons];
		for(i=0;i<Mesh->NumberOfPolygons;i++)
		{
			pxNewProjectPolygonData[PolyConv[i]] = Mesh->m_pxProjectPolygonData[i];
		}
		delete [] Mesh->m_pxProjectPolygonData;
		Mesh->m_pxProjectPolygonData = pxNewProjectPolygonData;
		
	}




	//normals , prelighting , bounding boxes can be recalculated
	if(Mesh->AreNormalsValid())
	{
		delete [] Mesh->NormalsDataPtr;
		Mesh->NormalsDataPtr = NULL;
		Mesh->m_bFaceNormalsValid = false;
		Mesh->m_bVertexNormalsValid = false;
	}
	
	//delete [] Mesh->PrelightingDataPtr;
	//Mesh->PrelightingDataPtr = NULL;
	//Mesh->PrelightingDataValid = false;
	
	Mesh->BoundingBoxDataValid = false;
	
	//update the number of polygons
	Mesh->NumberOfPolygons = NewNumberOfPolygons;

	Mesh->CreateNormals();
	Mesh->CalculateBoundingBox();


	//get rid of the conversion arrays
	delete [] MergeArray;

	*OutputPolyConv = PolyConv;

	return true;
}


bool Tools_Mode_MeshEdit::CanPolygonsBeMerged(Asura_Mesh* Mesh,int PolyIndex1,int PolyIndex2)
{
	//get the polygons
	Asura_Mesh_Polygon* Poly1 = &Mesh->PolygonDataPtr[PolyIndex1];
	Asura_Mesh_Polygon* Poly2 = &Mesh->PolygonDataPtr[PolyIndex2];

	//must be triangles
	if(Poly1->NumberOfVertices() == 4) return false;
	if(Poly2->NumberOfVertices() == 4) return false;


	//count the number of shared vertices
	int SharedVerts = 0;
	int UnsharedVertex1 = 3;
	int UnsharedVertex2 = 3;
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++)
		{
			if(Poly1->VertexID[i] == Poly2->VertexID[j])
			{
				UnsharedVertex1 -= i;//doing this should give me the index of the unshared
				UnsharedVertex2 -= j;//vertices once we have finished
				SharedVerts++;
				break;
			}
		}
	}
	//need to shared vertices
	if(SharedVerts!=2) return false;

	Asura_Assertf( 0 <= UnsharedVertex1 && UnsharedVertex1 < 3, ("UnsharedVertex1 %d is out of range.", UnsharedVertex1));
	Asura_Assertf( 0 <= UnsharedVertex2 && UnsharedVertex2 < 3, ("UnsharedVertex2 %d is out of range.", UnsharedVertex2));
	//must be close to planar
	//first make sure the normals are close
	if(!Mesh->AreNormalsValid()) Mesh->CreateNormals();
	if(Mesh->NormalsDataPtr[PolyIndex1].FaceNormal*Mesh->NormalsDataPtr[PolyIndex2].FaceNormal <0.99f) return false;


	//get the vector from a point in polygon1 to the unshared vertex in polygn 2
	Asura_Vector_3 offset = Mesh->VertexDataPtr[Poly2->VertexID[UnsharedVertex2]].Position;
	offset -= Mesh->VertexDataPtr[Poly1->VertexID[0]].Position;

	//take the dot product with the polygon 1 normal ,to find how far off the plane the
	//unshared point is
	if(!Mesh->AreNormalsValid()) Mesh->CreateNormals();
	Asura_Vector_3 normal = Mesh->NormalsDataPtr[PolyIndex1].FaceNormal;
	float distance = offset*normal;

	//need polygon to be within a millimetre of being planar
	if(distance<-.001f || distance>.001f) return false;

	//finally the polygon must be concave
	//get the 2 sides from UnsharedVertex1->UnsharedVertex2
	int v1 = Poly1->VertexID[UnsharedVertex1];
	int v2 = Poly1->VertexID[(UnsharedVertex1+1)%3];
	int v3 = Poly2->VertexID[UnsharedVertex2];

	Asura_Vector_3 side1 = Mesh->VertexDataPtr[v2].Position - Mesh->VertexDataPtr[v1].Position;
	Asura_Vector_3 side2 = Mesh->VertexDataPtr[v3].Position - Mesh->VertexDataPtr[v2].Position;

	side1.Normalise();
	side2.Normalise();

	if((side1^normal)*side2 >=0) return false;

	//now check the angle between the sides from UnsharedVertex2 to UnsharedVertex1
	v1 = Poly2->VertexID[UnsharedVertex2];
	v2 = Poly2->VertexID[(UnsharedVertex2+1)%3];
	v3 = Poly1->VertexID[UnsharedVertex1];

	side1 = Mesh->VertexDataPtr[v2].Position - Mesh->VertexDataPtr[v1].Position;
	side2 = Mesh->VertexDataPtr[v3].Position - Mesh->VertexDataPtr[v2].Position;

	side1.Normalise();
	side2.Normalise();

	if((side1^normal)*side2 >=0) return false;

	//looks like these can be merged then
	return true;
}

float Tools_Mode_MeshEdit::GetAngleForPolygonMerge(Asura_Mesh* Mesh,int PolyIndex1,int PolyIndex2)
{
	/*
	This function checks the angle (well cos of the angle) at the unshared vertex in each of
	the polygons.
	The idea is that the closer to 90 degrees the angle is (the lower cos_angle) , the better
	the merge will be.
	*/

	//get the polygons
	Asura_Mesh_Polygon* Poly1 = &Mesh->PolygonDataPtr[PolyIndex1];
	Asura_Mesh_Polygon* Poly2 = &Mesh->PolygonDataPtr[PolyIndex2];

	//find the unshared vertices
	int UnsharedVertex1 = 3;
	int UnsharedVertex2 = 3;
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++)
		{
			if(Poly1->VertexID[i] == Poly2->VertexID[j])
			{
				UnsharedVertex1 -= i;//doing this should give me the index of the unshared
				UnsharedVertex2 -= j;//vertices once we have finished
				break;
			}
		}
	}
	Asura_Assert(UnsharedVertex1>=0 && UnsharedVertex1<3,"");
	Asura_Assert(UnsharedVertex2>=0 && UnsharedVertex2<3,"");

	//get the 2 sides around UnsharedVertex1
	int v1 = Poly1->VertexID[UnsharedVertex1];
	int v2 = Poly1->VertexID[(UnsharedVertex1+1)%3];
	int v3 = Poly1->VertexID[(UnsharedVertex1+2)%3];

	Asura_Vector_3 side1 = Mesh->VertexDataPtr[v2].Position - Mesh->VertexDataPtr[v1].Position;
	Asura_Vector_3 side2 = Mesh->VertexDataPtr[v3].Position - Mesh->VertexDataPtr[v1].Position;

	//normalise , and take the dot product to find cos of the angle
	side1.Normalise();
	side2.Normalise();
	float cos_angle1 = side1*side2;


	//now do the same for the the second polygon
	v1 = Poly2->VertexID[UnsharedVertex2];
	v2 = Poly2->VertexID[(UnsharedVertex2+1)%3];
	v3 = Poly2->VertexID[(UnsharedVertex2+2)%3];

	side1 = Mesh->VertexDataPtr[v2].Position - Mesh->VertexDataPtr[v1].Position;
	side2 = Mesh->VertexDataPtr[v3].Position - Mesh->VertexDataPtr[v1].Position;

	//normalise , and take the dot product to find cos of the angle
	side1.Normalise();
	side2.Normalise();
	float cos_angle2 = side1*side2;

	return max(cos_angle1,cos_angle2);

}


void Tools_Mode_MeshEdit::CreatePolygonFromSelectedVertices()
{

	int iNumSelectedVerts = Tools_Vertex_Selection::GetNumberOfSelectedVertices();
	if(iNumSelectedVerts!=3 && iNumSelectedVerts!=4)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"There must be either 3 or 4 vertices selected to create a polygon");
		return;
	}


	//get the selected vertices
	ModuleVertexPair axVerts[4];
	Tools_Vertex_Selection::StartIteration();
	for(int i=0;i<iNumSelectedVerts;i++)
	{
		Tools_Vertex_Selection::GetNextSelected(&axVerts[i]);
	}

	//determine the module that the polygon is being added to
	//if the vertices span several modules ,then ask
	List<int> xModuleList;
	ListSelection_Overlay xOverlay("Create polygon in which module?");
	for(int i=0;i<iNumSelectedVerts;i++)
	{
		if(!xModuleList.contains(axVerts[i].iModuleIndex))
		{
			xModuleList.add_entry(axVerts[i].iModuleIndex);
			xOverlay.AddItem(Asura_Module_System::GetMeshForModule(axVerts[i].iModuleIndex)->Name.GetString(),axVerts[i].iModuleIndex);
		}
	}
	int iModuleIndex = xModuleList.first_entry();
	if(xModuleList.size() > 1)
	{
		if(!xOverlay.DoModal()) return;
		iModuleIndex = xOverlay.GetSelectionData();
	}
	
		
	//get the mesh being edited
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);

	//Disallow if it will result in ther being too many vertices/polygons
	//This errs a bit on the side of safety for simplicity
	//RAR 07/09/2006
	if((pxMesh->NumberOfTriangles + 2) > Asura_Mesh::MAX_TRIANGLES || (pxMesh->NumberOfVertices + 4) > Asura_Mesh::MAX_VERTICES)
	{
		ShowMessageOverlay("Can't create polygon since there would be too many triangles/vertices");
		return;
	}

	bool bQuad = false;
	if(iNumSelectedVerts==4)
	{
		Asura_Vector_3 xPoints[4];
		//see if the vertices will make a quad
		for(int i=0;i<4;i++)
		{
			Asura_Vector_3 xModulePosition;
			Asura_Module_System::GetPositionOfModule(axVerts[i].iModuleIndex,xModulePosition);
			Asura_Mesh* pxVertMesh = Asura_Module_System::GetMeshForModule(axVerts[i].iModuleIndex);

			xPoints[i] = pxVertMesh->VertexDataPtr[axVerts[i].iVertexIndex].Position + xModulePosition;
		}

		int aiOrder[4];
		bQuad = WillSelectedVerticesMakeAQuad(xPoints,aiOrder);

		if(bQuad)
		{
			//reorder the verts
			ModuleVertexPair axTempVerts[4];
			for(int i=0;i<4;i++)
			{
				axTempVerts[i] = axVerts[i];
			}
			for(int i=0;i<4;i++)
			{
				axVerts[i] = axTempVerts[aiOrder[i]];
			}
		}
	}

	
	int aiVert[4];
	
	//Check to see if any of the selected vertices were in other modules.
	//If they are , then we need to create them in this module (or find an existing equivalent vertex)
	int iOldNumberOfVertices = pxMesh->NumberOfVertices;
	for(int i=0;i<iNumSelectedVerts;i++)
	{
		aiVert[i] = axVerts[i].iVertexIndex;
		if(axVerts[i].iModuleIndex == iModuleIndex) continue;

		//need to get the position of the vertex relative to this module
		Asura_Vector_3 xThisModulePosition,xOtherModulePosition,xVertPosition;

		Asura_Module_System::GetPositionOfModule(iModuleIndex,xThisModulePosition);
		Asura_Module_System::GetPositionOfModule(axVerts[i].iModuleIndex,xOtherModulePosition);

		Asura_Mesh* pxOtherMesh = Asura_Module_System::GetMeshForModule(axVerts[i].iModuleIndex);
		xVertPosition = pxOtherMesh->VertexDataPtr[axVerts[i].iVertexIndex].Position;
		xVertPosition += xOtherModulePosition-xThisModulePosition;

		//check against existing vertices in this module
		int iVertIndex = -1;
		for(int j=0;j<pxMesh->NumberOfVertices;j++)
		{
			if(pxMesh->VertexDataPtr[j].Position.ApproximatelyEquals(xVertPosition))
			{
				iVertIndex = j;
				break;
			}
		}

		if(iVertIndex == -1)
		{
			//need to add a new vertex to the mesh then
			Asura_Mesh_Vertex* pxNewVertexDataPtr = new Asura_Mesh_Vertex[pxMesh->NumberOfVertices+1];
			for(int j=0;j<pxMesh->NumberOfVertices;j++)
			{
				pxNewVertexDataPtr[j] = pxMesh->VertexDataPtr[j];
			}
			iVertIndex = pxMesh->NumberOfVertices;
			pxNewVertexDataPtr[iVertIndex].Position = xVertPosition;

			delete [] pxMesh->VertexDataPtr;
			pxMesh->VertexDataPtr = pxNewVertexDataPtr;

			//update vertex flags
			if(pxMesh->m_pxVertexFlags)
			{
				Asura_Mesh_VertexFlags* pxNewVertexFlags = new Asura_Mesh_VertexFlags[pxMesh->NumberOfVertices+1];
				Asura_Mem::Copy(pxNewVertexFlags, pxMesh->m_pxVertexFlags, pxMesh->NumberOfVertices * sizeof(Asura_Mesh_VertexFlags));
				pxNewVertexFlags[iVertIndex].m_uFlags = 0;

				delete [] pxMesh->m_pxVertexFlags;
				pxMesh->m_pxVertexFlags = pxNewVertexFlags;
			}

			pxMesh->NumberOfVertices++;
		}

		aiVert[i] = iVertIndex;
	}
	if(iOldNumberOfVertices != pxMesh->NumberOfVertices)
	{
		//need to generate a vertex conversion array to update vertex selection
		int * piVertConv = new int[iOldNumberOfVertices];
		for(int i=0;i<iOldNumberOfVertices;i++)
		{
			piVertConv[i] = i;
		}
		Tools_Vertex_Selection::HandleVertexConversion(iModuleIndex,piVertConv);
		delete [] piVertConv;
	}

	if(iNumSelectedVerts == 4)
	{
		if(bQuad)
		{
			CreatePolygon(iModuleIndex,pxMesh,4,aiVert);
		}
		else
		{
			CreatePolygon(iModuleIndex,pxMesh,3,aiVert);
			aiVert[1] = aiVert[3];
			CreatePolygon(iModuleIndex,pxMesh,3,aiVert);
		}
	}
	else
	{
		CreatePolygon(iModuleIndex,pxMesh,3,aiVert);
	}


}

void Tools_Mode_MeshEdit::CreatePolygon(int iModuleIndex,Asura_Mesh* pxMesh,int iNumVerts,int* piVerts)
{
	//want to create the polygon so that it faces the user...
	//first get the polygon normal with the current ordering
	Asura_Vector_3 normal = (pxMesh->VertexDataPtr[piVerts[1]].Position-pxMesh->VertexDataPtr[piVerts[0]].Position) ^
						   (pxMesh->VertexDataPtr[piVerts[2]].Position-pxMesh->VertexDataPtr[piVerts[1]].Position);

	//get the camera facing
	Asura_Vector_3 facing;
	Asura_Cursor::GetLOSDirection(facing);
	//rotate into world space
 	
	facing = facing * (*Asura_Camera::GetOrientation());

	if(normal * facing >0)
	{
		normal = -normal;
		//need to reverse the point ordering
		for(int i=0;i<iNumVerts/2;i++)
		{
			int temp = piVerts[i];
			piVerts[i] = piVerts[iNumVerts-(i+1)];
			piVerts[iNumVerts-(i+1)] = temp;
		}
	}
	if(!pxMesh->m_bFaceNormalsValid) pxMesh->CreateFaceNormals();
	//make sure there isn't already a polygon using these vertices , facing the same way
	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		Asura_Mesh_Polygon* poly = &pxMesh->PolygonDataPtr[i];
		int num_verts = poly->NumberOfVertices();
		if(normal*pxMesh->NormalsDataPtr[i].FaceNormal > 0)
		{
			int j;
			for(j=0;j<iNumVerts;j++)
			{
				int k;
				for(k=0;k<num_verts;k++)
				{
					if(piVerts[j]==poly->VertexID[k]) break;
				}	
				if(k==num_verts) break;
			}
			if(j==iNumVerts)
			{
				Tools_Timed_Message_System::AddMessage(0xffffff,"A polygon using these vertices already exists");
				return;
				
			}
		}
	}


	//allocate new polygon data
	Asura_Mesh_Polygon* NewPolygonDataPtr = new Asura_Mesh_Polygon[pxMesh->NumberOfPolygons+1];
	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		NewPolygonDataPtr[i] = pxMesh->PolygonDataPtr[i];
	}
	//fill in the new polygon
	int index = pxMesh->NumberOfPolygons;
	
	for(int i=0;i<iNumVerts;i++)
	{
		NewPolygonDataPtr[index].VertexID[i] = (uint16) piVerts[i];
	}
	if(iNumVerts==3) NewPolygonDataPtr[index].VertexID[3] = 0xffff;

	delete [] pxMesh->PolygonDataPtr;
	pxMesh->PolygonDataPtr = NewPolygonDataPtr;

	//now update texturing data
	if(pxMesh->TextureDataValid)
	{
		Asura_Mesh_TextureInfo* NewTextureDataPtr = new Asura_Mesh_TextureInfo[pxMesh->NumberOfPolygons+1];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			NewTextureDataPtr[i] = pxMesh->TextureDataPtr[i];
		}

		NewTextureDataPtr[index].m_iMaterialID = ASURA_MATERIAL_NONE; 	
		
		for(int i=0;i<ASURA_MATERIAL_MAXTEXTURES;i++)
		{
			for(int j=0;j<4;j++)
			{
				NewTextureDataPtr[index].U[i][j] = 0;
				NewTextureDataPtr[index].V[i][j] = 0;
			}
		}
		for(int i=0;i<4;i++)
		{
			NewTextureDataPtr[index].m_afAlpha[i] = 1.0f;
		}
		delete [] pxMesh->TextureDataPtr;
		pxMesh->TextureDataPtr = NewTextureDataPtr;
	}
	pxMesh->DeleteProcessedTextureInfo();


	// JGP update the painted colours
	// m_bPaintedColourDataValid m_pxPaintedColourDataPtr Asura_Mesh_Prelighting
	if ( pxMesh->m_bPaintedColourDataValid )
	{
		// Copy existing range
		Asura_Mesh_PaintedColours* NewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ pxMesh->NumberOfPolygons + 1 ];
		for( int i = 0; i < pxMesh->NumberOfPolygons; i++ )
		{
			NewPaintedColourDataPtr[ i ] = pxMesh->m_pxPaintedColourDataPtr[ i ];
		}

		// Create the new one
		for ( int i = 0; i < 4; i++ )
		{
			NewPaintedColourDataPtr[ index ].Colour[ i ] = 0x808080;
		}

		delete [] pxMesh->m_pxPaintedColourDataPtr;
		pxMesh->m_pxPaintedColourDataPtr = NewPaintedColourDataPtr;
	}

	//update smoothing groups
	if(pxMesh->SmoothingGroupDataValid)
	{
		int* NewSmoothingGroupDataPtr = new int[pxMesh->NumberOfPolygons+1];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			NewSmoothingGroupDataPtr[i] = pxMesh->SmoothingGroupDataPtr[i];
		}
		NewSmoothingGroupDataPtr[index] = 0;

		delete [] pxMesh->SmoothingGroupDataPtr;
		pxMesh->SmoothingGroupDataPtr = NewSmoothingGroupDataPtr;
	}

	//update polygon flags
	if(pxMesh->PolygonFlagDataValid)
	{
		Asura_Mesh_PolygonFlags* NewPolygonFlagDataPtr = new Asura_Mesh_PolygonFlags[pxMesh->NumberOfPolygons+1];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			NewPolygonFlagDataPtr[i] = pxMesh->PolygonFlagDataPtr[i];
		}
		memset(&NewPolygonFlagDataPtr[index],0,sizeof(Asura_Mesh_PolygonFlags));

		delete [] pxMesh->PolygonFlagDataPtr;
		pxMesh->PolygonFlagDataPtr = NewPolygonFlagDataPtr;
	}

	//and polygon guids
	if(pxMesh->m_pxPolygonGuids)
	{
		Asura_Guid* pxNewPolygonGuids = new Asura_Guid[pxMesh->NumberOfPolygons+1];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxNewPolygonGuids[i] = pxMesh->m_pxPolygonGuids[i];
		}
		pxNewPolygonGuids[index] = ASURA_GUID_UNREGISTERED;

		delete [] pxMesh->m_pxPolygonGuids;
		pxMesh->m_pxPolygonGuids = pxNewPolygonGuids;

	}

	// Max/maya polygon indeces
	if(pxMesh->m_bPolygonIndexDataValid)
	{
		int* piNewPolygonIndexData = new int[pxMesh->NumberOfPolygons+1];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			piNewPolygonIndexData[i] = pxMesh->m_piPolygonIndexDataPtr[i];
		}
		piNewPolygonIndexData[index] = -1;

		delete [] pxMesh->m_piPolygonIndexDataPtr;
		pxMesh->m_piPolygonIndexDataPtr = piNewPolygonIndexData;
	}


	//and polygon light IDs
	if(pxMesh->m_puPolygonLightIDs)
	{
		u_int* pxNewLightIDs = new u_int[pxMesh->NumberOfPolygons+1];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxNewLightIDs[i] = pxMesh->m_puPolygonLightIDs[i];
		}
		pxNewLightIDs[index] = 0;

		delete [] pxMesh->m_puPolygonLightIDs;
		pxMesh->m_puPolygonLightIDs = pxNewLightIDs;

	}

	//project polygon data
	if(pxMesh->m_pxProjectPolygonData)
	{
		Asura_Mesh_ProjectPolygonData* pxNewProjectPolygonData = new Asura_Mesh_ProjectPolygonData[pxMesh->NumberOfPolygons+1];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxNewProjectPolygonData[i] = pxMesh->m_pxProjectPolygonData[i];
		}
		pxNewProjectPolygonData[index].m_uData = 0;

		delete [] pxMesh->m_pxProjectPolygonData;
		pxMesh->m_pxProjectPolygonData = pxNewProjectPolygonData;

	}


	//invalidate the normals and prelighting
	pxMesh->InvalidateNormals();

	delete [] pxMesh->PrelightingDataPtr;
	pxMesh->PrelightingDataPtr = NULL;
	pxMesh->PrelightingDataValid = false;
	
	pxMesh->BoundingBoxDataValid = false;

	//update number of polygons
	pxMesh->NumberOfPolygons++;
	pxMesh->NumberOfTriangles += iNumVerts-2;

	//recalculate normals
	pxMesh->CreateNormals();

	Asura_Mesh_System::PlatformResetRenderMesh(pxMesh);
	Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);


	//create a polygon conversion array , for updating polygon selecting , and render flags
	int* PolyConv = new int[pxMesh->NumberOfPolygons];
	for(int i=0;i<pxMesh->NumberOfPolygons;i++) PolyConv[i] = i;

	//do the updating
	Tools_Polygon_Selection::HandlePolygonConversion(iModuleIndex,PolyConv);
	Tools_VertexPerPoly_Selection::HandleMeshChange(iModuleIndex);
	Tools_Edge_Selection::HandleMeshChange(iModuleIndex);

	Tools_Render_Options::HandlePolygonConversion(iModuleIndex,PolyConv);
	if(pxMesh->m_pxCollMeshGenData)
	{
		pxMesh->m_pxCollMeshGenData->HandlePolygonConversion(pxMesh,PolyConv);
	}

	Tools_Undo_System::ClearUndo();

	InvalidateModuleCollisionMesh(iModuleIndex);
	delete [] PolyConv;

	Tools_Adjacency::InvalidateModule(iModuleIndex);

	Tools_SaveAsura::SetMeshModified(pxMesh);
	

	Tools_Timed_Message_System::AddMessage(0xffffff,"%s Created",iNumVerts==3 ? "Triangle" : "Quad");
}

bool Tools_Mode_MeshEdit::AddVerticesToModule(int iModuleIndex,int iNumVertsToAdd,const Asura_Vector_3* pxVertices)
{
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_Vector_3 xModulePos = Asura_Module_System::GetPositionOfModule(iModuleIndex);

	if(pxMesh->NumberOfVertices + iNumVertsToAdd > Asura_Mesh::MAX_VERTICES)
	{
		return false;
	}

	//Allocate enlarged vertex array
	int iOldNumVertices = pxMesh->NumberOfVertices;
	int iNewNumVertices = pxMesh->NumberOfVertices + iNumVertsToAdd;
	Asura_Mesh_Vertex* pxNewVertexDataPtr = new Asura_Mesh_Vertex[iNewNumVertices];

	//copy old vertices
	for(int i=0;i<iOldNumVertices;i++)
	{
		pxNewVertexDataPtr[i] = pxMesh->VertexDataPtr[i];
	}

	//add the new ones
	for(int j=0;j<iNumVertsToAdd;j++)
	{
		pxNewVertexDataPtr[iOldNumVertices+j].Position = pxVertices[j]-xModulePos;
	}

	//replace the old array
	delete [] pxMesh->VertexDataPtr;
	pxMesh->VertexDataPtr = pxNewVertexDataPtr;
	pxMesh->NumberOfVertices = iNewNumVertices;

	//vertex flags
	if(pxMesh->m_pxVertexFlags)
	{
		//remove the old flags from the mesh
		Asura_Mesh_VertexFlags* pxOldVertexFlags = pxMesh->m_pxVertexFlags;
		pxMesh->m_pxVertexFlags = 0;

		//allocate enlarged array (which will initialise new entries) and copy from old
		pxMesh->AllocateVertexFlags();
		Asura_Mem::Copy(pxMesh->m_pxVertexFlags, pxOldVertexFlags, sizeof(Asura_Mesh_VertexFlags) * iOldNumVertices);

		delete [] pxOldVertexFlags;
	}

	//Invalidate stuff.
	//Not so much that matters when adding vertices
	pxMesh->BoundingBoxDataValid = false;
	pxMesh->CalculateBoundingBox();

	InvalidateModuleCollisionMesh(iModuleIndex);
	Tools_Adjacency::InvalidateModule(iModuleIndex);

	//Update vertex selection
	int* piVertexConv = new int[iOldNumVertices];
	for(int i=0;i<iOldNumVertices;i++)
	{
		piVertexConv[i] = i;
	}
	Tools_Vertex_Selection::HandleVertexConversion(iModuleIndex,piVertexConv);
	delete [] piVertexConv;

	Tools_SaveAsura::SetMeshModified(pxMesh);
	
	return true;
}



void Tools_Mode_MeshEdit::MoveVerticesOrPolygons()
{
	if(SelectingVertices)
	{
		MoveVertices();
	}
	else
	{
		MovePolygons();
	}
}

void Tools_Mode_MeshEdit::MoveVertices()
{
	List<ModuleVertexPair> xVertexList;

	ModuleVertexPair xVert;
	Tools_Vertex_Selection::StartIteration();
	while(Tools_Vertex_Selection::GetNextSelected(&xVert))
	{
		xVertexList.add_entry(xVert);
	}
	if(xVertexList.size())
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"Moving selected vertices");
		Tools_VertexMove_Object* pxObject = new Tools_VertexMove_Object(xVertexList);
		Tools_Object_System::SelectObject(pxObject);
		Tools_Object_System::ToggleFlying();
	}
}

void Tools_Mode_MeshEdit::MovePolygons()
{
	List<ModuleVertexPair> xVertexList;

	//get a list of all vertices used by the selected polygons
	PolygonID xPolygon;
	Tools_Polygon_Selection::StartIteration();
	while(Tools_Polygon_Selection::GetNextSelected(&xPolygon))
	{
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(xPolygon.ModuleIndex);
		int iIndex = xPolygon.PolyIndex;

		Asura_Mesh_Polygon* pxPoly = &pxMesh->PolygonDataPtr[iIndex];
		int iNumVerts = pxPoly->NumberOfVertices();

		for(int i=0;i<iNumVerts;i++)
		{
			ModuleVertexPair xVert;
			xVert.iModuleIndex = xPolygon.ModuleIndex;
			xVert.iVertexIndex = pxPoly->VertexID[i];
			if(!xVertexList.contains(xVert))
			{
				xVertexList.add_entry(xVert);
			}


		}
		
	}
	if(xVertexList.size())
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"Moving selected polygons");
		Tools_VertexMove_Object* pxObject = new Tools_VertexMove_Object(xVertexList);
		Tools_Object_System::SelectObject(pxObject);
		Tools_Object_System::ToggleFlying();
	}
	
}

void Tools_Mode_MeshEdit::FlattenVertices()
{
	//get a list of vertices to affect
	List<ModuleVertexPair> xVertexList;

	if(SelectingVertices)
	{
		//take from vertex selection
		ModuleVertexPair xVert;
		Tools_Vertex_Selection::StartIteration();
		while(Tools_Vertex_Selection::GetNextSelected(&xVert))
		{
			xVertexList.add_entry(xVert);
		}
	}
	else
	{
		//take from polygon selection
		PolygonID xPolygon;
		Tools_Polygon_Selection::StartIteration();
		while(Tools_Polygon_Selection::GetNextSelected(&xPolygon))
		{
			Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(xPolygon.ModuleIndex);
			int iIndex = xPolygon.PolyIndex;

			Asura_Mesh_Polygon* pxPoly = &pxMesh->PolygonDataPtr[iIndex];
			int iNumVerts = pxPoly->NumberOfVertices();

			for(int i=0;i<iNumVerts;i++)
			{
				ModuleVertexPair xVert;
				xVert.iModuleIndex = xPolygon.ModuleIndex;
				xVert.iVertexIndex = pxPoly->VertexID[i];
				if(!xVertexList.contains(xVert))
				{
					xVertexList.add_entry(xVert);
				}


			}
			
		}
	}

	if(!xVertexList.size())
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"No polygons/vertices selected");
		return;
	}


	//get the average , min and max y values
	float fAverageY = 0.0f;
	float fMinY = Asura_Maths::LargeFloat;
	float fMaxY = -Asura_Maths::LargeFloat;

	for(LIF<ModuleVertexPair> xVertLIF(&xVertexList);!xVertLIF.done();xVertLIF.next())
	{
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(xVertLIF().iModuleIndex);

		Asura_Vector_3 xModulePosition;
		Asura_Module_System::GetPositionOfModule(xVertLIF().iModuleIndex,xModulePosition);

		float fY = pxMesh->VertexDataPtr[xVertLIF().iVertexIndex].Position.y+xModulePosition.y;

		fAverageY += fY;
		fMinY = Asura_Maths::Min(fMinY,fY);
		fMaxY = Asura_Maths::Max(fMaxY,fY);
	}
	fAverageY /= (float)xVertexList.size();

	//get confirmation for doing this
	if(!s_bSuppressConfirmation)
	{
		if(!ShowYesNoOverlay("There are %d selected vertices with a Y variation of %.3f metres.\nDo you want to flatten these out so that they all have the same Y value?",xVertexList.size(),fMaxY-fMinY))
		{
			return;
		}
	}

	//create a Tools_VertexMove_Object so we can make use of some of its functionality
	Tools_VertexMove_Object* pxObject = new Tools_VertexMove_Object(xVertexList);
	//do the flattening
	pxObject->SetYPositionForVertices(fAverageY);
	//and ditch the object.
	Tools_Object_System::DeleteObject(pxObject);
	
}



void Tools_Mode_MeshEdit::RenderVertexSelection(Asura_Vector_3& position)
{
	//draw a small cube
	int colour = 0x7fff0000;

	Asura_Vector_3 points[4];
	Asura_Vector_3 x(0.05f,0,0);
	Asura_Vector_3 y(0,0.05f,0);
	Asura_Vector_3 z(0,0,0.05f);
	
	extern void RenderOutline( const Asura_Vector_3* pxPoints, int iNumPoints, u_int uColour, bool bWireframe );

	points[0] = position-x+y+z;
	points[1] = position-x-y+z;
	points[2] = position+x-y+z;
	points[3] = position+x+y+z;
	RenderOutline(points,4,colour,false);

	points[0] = position+x+y-z;
	points[1] = position+x-y-z;
	points[2] = position-x-y-z;
	points[3] = position-x+y-z;
	RenderOutline(points,4,colour,false);

	points[0] = position-y+z+x;
	points[1] = position-y-z+x;
	points[2] = position+y-z+x;
	points[3] = position+y+z+x;
	RenderOutline(points,4,colour,false);

	points[0] = position+y+z-x;
	points[1] = position+y-z-x;
	points[2] = position-y-z-x;
	points[3] = position-y+z-x;
	RenderOutline(points,4,colour,false);

	points[0] = position-z+x+y;
	points[1] = position-z-x+y;
	points[2] = position+z-x+y;
	points[3] = position+z+x+y;
	RenderOutline(points,4,colour,false);

	points[0] = position+z+x-y;
	points[1] = position+z-x-y;
	points[2] = position-z-x-y;
	points[3] = position-z+x-y;
	RenderOutline(points,4,colour,false);
}



void Tools_Mode_MeshEdit::FlipSelectedPolygons()
{
	if(!Tools_Polygon_Selection::NumSelectedModules)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"No polygons have been selected");
		return;
	}

	List<Asura_Mesh*> xMeshList;

	PolygonID xPolygon;
	Tools_Polygon_Selection::StartIteration();

	while(Tools_Polygon_Selection::GetNextSelected(&xPolygon))
	{
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(xPolygon.ModuleIndex);
		int iIndex = xPolygon.PolyIndex;

		//reverse the vertex ordering in the polygon
		Asura_Mesh_Polygon* pxPoly = &pxMesh->PolygonDataPtr[iIndex];
		int iNumVerts = pxPoly->NumberOfVertices();

		for(int i=0;i<iNumVerts/2;i++)
		{
			uint16 usTemp = pxPoly->VertexID[i];
			pxPoly->VertexID[i] = pxPoly->VertexID[iNumVerts-(i+1)];
			pxPoly->VertexID[iNumVerts-(i+1)] = usTemp;
		}

		//reverse the vertex ordering for the UVs
		if(pxMesh->TextureDataValid)
		{
			Asura_Mesh_TextureInfo* pxText = &pxMesh->TextureDataPtr[iIndex];

			
			for(int i=0;i<ASURA_MATERIAL_MAXTEXTURES;i++)
			{
				for(int j=0;j<iNumVerts/2;j++)
				{
					float fTempU = pxText->U[i][j];
					float fTempV = pxText->V[i][j];
					pxText->U[i][j] = pxText->U[i][iNumVerts-(j+1)];
					pxText->V[i][j] = pxText->V[i][iNumVerts-(j+1)];
					pxText->U[i][iNumVerts-(j+1)] = fTempU;
					pxText->V[i][iNumVerts-(j+1)] = fTempV;
				}
			}

			//alpha
			for(int j=0;j<iNumVerts/2;j++)
			{
				float fTempA = pxText->m_afAlpha[j];
				pxText->m_afAlpha[j] = pxText->m_afAlpha[iNumVerts-(j+1)];
				pxText->m_afAlpha[iNumVerts-(j+1)] = fTempA;
			}
						
			
		}
		pxMesh->DeleteProcessedTextureInfo();

		if(!xMeshList.contains(pxMesh))
		{
			xMeshList.add_entry(pxMesh);
		}

		//Invalidate Max/Maya polygon indeces for the affected polygons , since they won't be
		//mergeable when facing the opposite direction. RAR 26/09/2006
		if(pxMesh->m_bPolygonIndexDataValid)
		{
			pxMesh->m_piPolygonIndexDataPtr[iIndex] = -1;
		}


		InvalidateModuleCollisionMesh(xPolygon.ModuleIndex);
		Tools_Adjacency::InvalidateModule(xPolygon.ModuleIndex);

	}

	//the normals for all the meshes altered will need to be redone
	for(LIF<Asura_Mesh*> xMeshLIF(&xMeshList);!xMeshLIF.done();xMeshLIF.next())
	{
		Asura_Mesh* pxMesh = xMeshLIF();
		pxMesh->InvalidateNormals();
		
		
		delete [] pxMesh->PrelightingDataPtr;
		pxMesh->PrelightingDataPtr = NULL;
		pxMesh->PrelightingDataValid = false;
		
		pxMesh->CreateNormals();
		Asura_Mesh_System::PlatformResetRenderMesh(pxMesh);
		Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);

		Tools_SaveAsura::SetMeshModified(pxMesh);

	}

	

	Tools_Timed_Message_System::AddMessage(0xffffff,"Polygons Flipped");

}

bool Tools_Mode_MeshEdit::SplitPolygonEdge(int iModuleIndex,int iPolyIndex,const Asura_Vector_3& xSplitPoint)
{
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_Vector_3 xModulePos = Asura_Module_System::GetPositionOfModule(iModuleIndex);

	//convert the split point to be relative to the mesh
	Asura_Vector_3 xRelSplitPoint = xSplitPoint - xModulePos;

	//get the vertices of the polygon to be split , and make sure the new vertex
	//doesn't match any of these
	Asura_Mesh_Polygon* pxPoly = &pxMesh->PolygonDataPtr[iPolyIndex];
	Asura_Vector_3 xVertexPos[4];
	int iVertsInPolygon = pxPoly->NumberOfVertices();
	for(int i=0;i<iVertsInPolygon;i++)
	{
		xVertexPos[i] = pxMesh->VertexDataPtr[pxPoly->VertexID[i]].Position;
		if(xVertexPos[i].ApproximatelyEquals(xRelSplitPoint))
		{
			Tools_Timed_Message_System::AddMessage(0xffffff,"New vertex is too close to the existing vertices in this polygon.");
			return false;
		}
	}

	if(!pxMesh->AreNormalsValid())pxMesh->CreateNormals();
	Asura_Vector_3 xPolyNormal = pxMesh->NormalsDataPtr[iPolyIndex].FaceNormal;

	//make sure the point is approximately on the plane of the polygon
	float fDistFromPlane = (xRelSplitPoint - xVertexPos[0])*xPolyNormal;
	bool bAskedAboutFarVertex = false;
	if(Asura_Maths::Abs(fDistFromPlane) > 0.002f && !s_bSuppressConfirmation)
	{
		char szText[200];
		Asura_PrintfToBfr(szText,"The selected vertex doesn't lie on the plane of the polygon. it is %d mm away.\nWould you like to split the polygon edge here anyway?",200,true, (int)(Asura_Maths::Abs(fDistFromPlane)*1000));
		YesNo_Overlay xOverlay(szText);
		if(!xOverlay.DoModal())
		{
			return false;
		}
		bAskedAboutFarVertex = true;
	}

	//find out which side the point is closest to
	float fNearDist = Asura_Maths::LargeFloat;
	int iSide = 0;
	float fLerp = 0;

	for(int i=0;i<iVertsInPolygon;i++)
	{
		//get the normal in the direction of this side
		Asura_Vector_3 xSide = xVertexPos[(i+1)%iVertsInPolygon] - xVertexPos[i];
		Asura_Vector_3 xSideNormal = xSide;
		xSideNormal.Normalise();

		//and cross with the polygon normal to get a perpendicular vector lying on the polygon
		Asura_Vector_3 xPerp = xSideNormal^xPolyNormal;

		//get the offset of the split point from this side
		Asura_Vector_3 xOffset = xRelSplitPoint - xVertexPos[i];

		float fDist = xOffset*xPerp;

		if(Asura_Maths::Abs(fDist)<Asura_Maths::Abs(fNearDist))
		{
			//calculate the proportion of the distance along the side that the split point is
			fLerp = (xOffset*xSideNormal)/xSide.Magnitude();

			if(fLerp>=0 && fLerp<=1.0f)
			{
				fNearDist = fDist;
				iSide = i;
			}
		}
	}

	if(Asura_Maths::Abs(fNearDist)>0.01f && !bAskedAboutFarVertex && !s_bSuppressConfirmation)
	{
		char szText[200];
		Asura_PrintfToBfr(szText,"The selected vertex is %d mm away from the nearest side of the polygon.\nWould you like to split the polygon edge here anyway?",200,true, (int)(Asura_Maths::Abs(fNearDist)*1000));
		YesNo_Overlay xOverlay(szText);
		if(!xOverlay.DoModal())
		{
			return false;
		}
		bAskedAboutFarVertex = true;
	}

	//see if the split point matches any existing vertex
	int iVertIndex = -1;
	for(int i=0;i<pxMesh->NumberOfVertices;i++)
	{
		if(pxMesh->VertexDataPtr[i].Position.ApproximatelyEquals(xRelSplitPoint))
		{
			iVertIndex = i;
			break;
		}
	}
	if(iVertIndex == -1)
	{
		//need to add an extra vertex then
		Asura_Mesh_Vertex* pxNewVertexDataPtr = new Asura_Mesh_Vertex[pxMesh->NumberOfVertices+1];
		for(int i=0;i<pxMesh->NumberOfVertices;i++)
		{
			pxNewVertexDataPtr[i] = pxMesh->VertexDataPtr[i];
		}
		iVertIndex = pxMesh->NumberOfVertices;
		pxNewVertexDataPtr[iVertIndex].Position = xRelSplitPoint;

		delete [] pxMesh->VertexDataPtr;
		pxMesh->VertexDataPtr = pxNewVertexDataPtr;
		
		//update vertex flags
		if(pxMesh->m_pxVertexFlags)
		{
			Asura_Mesh_VertexFlags* pxNewVertexFlags = new Asura_Mesh_VertexFlags[pxMesh->NumberOfVertices+1];
			Asura_Mem::Copy(pxNewVertexFlags,pxMesh->m_pxVertexFlags,sizeof(Asura_Mesh_VertexFlags) * pxMesh->NumberOfVertices);
			pxNewVertexFlags[iVertIndex].m_uFlags = 0;
			
			delete [] pxMesh->m_pxVertexFlags;
			pxMesh->m_pxVertexFlags = pxNewVertexFlags;	
		
		}

		
		pxMesh->NumberOfVertices++;

		//tell vertex selection about the new vertex
		int* piVertConv = new int[pxMesh->NumberOfVertices-1];
		for(int i=0;i<pxMesh->NumberOfVertices-1;i++)
		{
			piVertConv[i] = i;
		}
		Tools_Vertex_Selection::HandleVertexConversion(iModuleIndex,piVertConv);
		delete [] piVertConv;
	}

	



	//allocate polygon conversion arrays
	int* piPolyConv1 = new int[pxMesh->NumberOfPolygons];
	int* piPolyConv2 = new int[pxMesh->NumberOfPolygons];//second array for the newly created triangle

	//and initialise them , i'll put the newly created triangle immediately after the one it is being split from
	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		piPolyConv1[i] = (i<=iPolyIndex) ? i : i+1;
		piPolyConv2[i] = (i==iPolyIndex) ? i+1 : -1;
	}


	int iNewNumberOfPolygons = pxMesh->NumberOfPolygons+1;

	//allocate the new polygon array , and copy the polygons
	Asura_Mesh_Polygon* pxNewPolygonDataPtr = new Asura_Mesh_Polygon[iNewNumberOfPolygons];
	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		if(i!=iPolyIndex)
		{
			//this polygon is copied straight across
			pxNewPolygonDataPtr[piPolyConv1[i]] = pxMesh->PolygonDataPtr[i];
		}
		else
		{
			//this is the polygon being split
			Asura_Mesh_Polygon* pxPoly1 = &pxNewPolygonDataPtr[piPolyConv1[i]];
			pxPoly1->VertexID[0] = pxPoly->VertexID[iSide];
			pxPoly1->VertexID[1] = (unsigned short)iVertIndex;
			pxPoly1->VertexID[2] = pxPoly->VertexID[(iSide+2)%iVertsInPolygon];
			if(iVertsInPolygon==4)
				pxPoly1->VertexID[3] = pxPoly->VertexID[(iSide+3)%iVertsInPolygon];
			else
				pxPoly1->VertexID[3] = 0xffff;

			//and the newly generated triangle
			Asura_Mesh_Polygon* pxPoly2 = &pxNewPolygonDataPtr[piPolyConv2[i]];
			*pxPoly2 = pxMesh->PolygonDataPtr[i];

			pxPoly2->VertexID[0] = (unsigned short)iVertIndex;
			pxPoly2->VertexID[1] = pxPoly->VertexID[(iSide+1)%iVertsInPolygon];
			pxPoly2->VertexID[2] = pxPoly->VertexID[(iSide+2)%iVertsInPolygon];
			pxPoly2->VertexID[3] = 0xffff;
		}
		
	}
	delete [] pxMesh->PolygonDataPtr;
	pxMesh->PolygonDataPtr = pxNewPolygonDataPtr;

	//deal with texturing
	if(pxMesh->TextureDataValid)
	{
		Asura_Mesh_TextureInfo* pxNewTextureDataPtr = new Asura_Mesh_TextureInfo[iNewNumberOfPolygons];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			if(i!=iPolyIndex)
			{
				pxNewTextureDataPtr[piPolyConv1[i]] = pxMesh->TextureDataPtr[i];
			}
			else
			{
				Asura_Mesh_TextureInfo* pxTex1 = &pxNewTextureDataPtr[piPolyConv1[i]];
				Asura_Mesh_TextureInfo* pxTex2 = &pxNewTextureDataPtr[piPolyConv2[i]];
				Asura_Mesh_TextureInfo* pxTex = &pxMesh->TextureDataPtr[i];

				pxTex1->m_iMaterialID = pxTex->m_iMaterialID;
			   	pxTex2->m_iMaterialID = pxTex->m_iMaterialID;
				for(int iMat=0; iMat< TOOLS_NUM_MATERIALRESPONSE_PER_POLY; iMat++)
				{
					pxTex1->m_auMaterialResponseHashID[iMat] = pxTex->m_auMaterialResponseHashID[iMat];
			   		pxTex2->m_auMaterialResponseHashID[iMat] = pxTex->m_auMaterialResponseHashID[iMat];
				}


				//calculate the interpolated UVs for the extra vertex
				for(int j=0;j<ASURA_MATERIAL_MAXTEXTURES;j++)
				{
					float fNewU = pxTex->U[j][iSide]*(1.0f-fLerp) + pxTex->U[j][(iSide+1)%iVertsInPolygon]*fLerp;
					float fNewV = pxTex->V[j][iSide]*(1.0f-fLerp) + pxTex->V[j][(iSide+1)%iVertsInPolygon]*fLerp;



					pxTex1->U[j][0] = pxTex->U[j][iSide];
					pxTex1->V[j][0] = pxTex->V[j][iSide];
					pxTex1->U[j][1] = fNewU;
					pxTex1->V[j][1] = fNewV;
					pxTex1->U[j][2] = pxTex->U[j][(iSide+2)%iVertsInPolygon];
					pxTex1->V[j][2] = pxTex->V[j][(iSide+2)%iVertsInPolygon];
					if(iVertsInPolygon==4)
					{
						pxTex1->U[j][3] = pxTex->U[j][(iSide+3)%iVertsInPolygon];
						pxTex1->V[j][3] = pxTex->V[j][(iSide+3)%iVertsInPolygon];
					}
				
					pxTex2->U[j][0] = fNewU;
					pxTex2->V[j][0] = fNewV;
					pxTex2->U[j][1] = pxTex->U[j][(iSide+1)%iVertsInPolygon];
					pxTex2->V[j][1] = pxTex->V[j][(iSide+1)%iVertsInPolygon];
					pxTex2->U[j][2] = pxTex->U[j][(iSide+2)%iVertsInPolygon];
					pxTex2->V[j][2] = pxTex->V[j][(iSide+2)%iVertsInPolygon];
				}

				//alpha
				float fNewA = pxTex->m_afAlpha[iSide]*(1.0f-fLerp) + pxTex->m_afAlpha[(iSide+1)%iVertsInPolygon]*fLerp;


				pxTex1->m_afAlpha[0] = pxTex->m_afAlpha[iSide];
				pxTex1->m_afAlpha[1] = fNewA;
				pxTex1->m_afAlpha[2] = pxTex->m_afAlpha[(iSide+2)%iVertsInPolygon];
				if(iVertsInPolygon==4)
				{
					pxTex1->m_afAlpha[3] = pxTex->m_afAlpha[(iSide+3)%iVertsInPolygon];
				}
				
				pxTex2->m_afAlpha[0] = fNewA;
				pxTex2->m_afAlpha[1] = pxTex->m_afAlpha[(iSide+1)%iVertsInPolygon];
				pxTex2->m_afAlpha[2] = pxTex->m_afAlpha[(iSide+2)%iVertsInPolygon];
			}

		}
		delete [] pxMesh->TextureDataPtr;
		pxMesh->TextureDataPtr = pxNewTextureDataPtr;
	}
	pxMesh->DeleteProcessedTextureInfo();

	
	// JGP deal with painted colour
	if ( pxMesh->m_bPaintedColourDataValid )
	{
		Asura_Mesh_PaintedColours* pxNewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ iNewNumberOfPolygons ];
		for( int i = 0; i < pxMesh->NumberOfPolygons; i++ )
		{
			if ( i != iPolyIndex )
			{
				pxNewPaintedColourDataPtr[ piPolyConv1[ i ] ] = pxMesh->m_pxPaintedColourDataPtr[ i ];
			}
			else
			{
				Asura_Mesh_PaintedColours* pxNewPaintedColour1 = &pxNewPaintedColourDataPtr[ piPolyConv1[ i ] ];
				Asura_Mesh_PaintedColours* pxNewPaintedColour2 = &pxNewPaintedColourDataPtr[ piPolyConv2[ i ] ];
				Asura_Mesh_PaintedColours* pxPaintedColour = &pxMesh->m_pxPaintedColourDataPtr[ i ];

				Asura_RGBA xCol1( pxPaintedColour->Colour[ iSide ], 0 );
				Asura_RGBA xCol2( pxPaintedColour->Colour[ ( iSide + 1 ) % iVertsInPolygon ], 0 );
				Asura_RGBA xNewColour;
				xNewColour.Lerp( xCol1, xCol2, fLerp );
				const unsigned int uNewColour = xNewColour.ToUIntARGB();

				pxNewPaintedColour1->Colour[ 0 ] = pxPaintedColour->Colour[ iSide ];
				pxNewPaintedColour1->Colour[ 1 ] = uNewColour;
				pxNewPaintedColour1->Colour[ 2 ] = pxPaintedColour->Colour[ ( iSide + 2 ) % iVertsInPolygon ];
				
				if ( iVertsInPolygon == 4 )
				{
					pxNewPaintedColour1->Colour[ 3 ] = pxPaintedColour->Colour[ ( iSide + 3 ) % iVertsInPolygon ];
				}

				pxNewPaintedColour2->Colour[ 0 ] = uNewColour;
				pxNewPaintedColour2->Colour[ 1 ] = pxPaintedColour->Colour[ ( iSide + 1 ) % iVertsInPolygon ];
				pxNewPaintedColour2->Colour[ 2 ] = pxPaintedColour->Colour[ ( iSide + 2 ) % iVertsInPolygon ];
			}
		}
		delete [] pxMesh->m_pxPaintedColourDataPtr;
		pxMesh->m_pxPaintedColourDataPtr = pxNewPaintedColourDataPtr;
	}
	
	//deal with smoothing groups
	if(pxMesh->SmoothingGroupDataValid)
	{
		int* piNewSmoothingGroupDataPtr = new int[iNewNumberOfPolygons];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			piNewSmoothingGroupDataPtr[piPolyConv1[i]] = pxMesh->SmoothingGroupDataPtr[i];
			if(piPolyConv2[i]==-1) continue;
			piNewSmoothingGroupDataPtr[piPolyConv2[i]] = pxMesh->SmoothingGroupDataPtr[i];
		}
		delete [] pxMesh->SmoothingGroupDataPtr;
		pxMesh->SmoothingGroupDataPtr = piNewSmoothingGroupDataPtr;
	}

	//deal with polygon flags
	if(pxMesh->PolygonFlagDataValid)
	{
		Asura_Mesh_PolygonFlags* pxNewPolygonFlagDataPtr = new Asura_Mesh_PolygonFlags[iNewNumberOfPolygons];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxNewPolygonFlagDataPtr[piPolyConv1[i]] = pxMesh->PolygonFlagDataPtr[i];
			if(piPolyConv2[i]==-1) continue;
			pxNewPolygonFlagDataPtr[piPolyConv2[i]] = pxMesh->PolygonFlagDataPtr[i];
		}
		delete [] pxMesh->PolygonFlagDataPtr;
		pxMesh->PolygonFlagDataPtr = pxNewPolygonFlagDataPtr;
	}

	//deal with polygon guids
	if(pxMesh->m_pxPolygonGuids)
	{
		Asura_Guid* pxNewPolygonGuids = new Asura_Guid[iNewNumberOfPolygons];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxNewPolygonGuids[piPolyConv1[i]] = pxMesh->m_pxPolygonGuids[i];
			if(piPolyConv2[i]==-1) continue;
			pxNewPolygonGuids[piPolyConv2[i]] = pxMesh->m_pxPolygonGuids[i];
		}
		delete [] pxMesh->m_pxPolygonGuids;
		pxMesh->m_pxPolygonGuids = pxNewPolygonGuids;

	}

	// Max/maya polygon indeces
	if(pxMesh->m_bPolygonIndexDataValid)
	{
		int* piNewPolygonIndexData = new int[iNewNumberOfPolygons];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			piNewPolygonIndexData[piPolyConv1[i]] = pxMesh->m_piPolygonIndexDataPtr[i];
			if(piPolyConv2[i]==-1) continue;
			piNewPolygonIndexData[piPolyConv2[i]] = pxMesh->m_piPolygonIndexDataPtr[i];
		}
		delete [] pxMesh->m_piPolygonIndexDataPtr;
		pxMesh->m_piPolygonIndexDataPtr = piNewPolygonIndexData;
	}


	//and polygon light IDs
	if(pxMesh->m_puPolygonLightIDs)
	{
		u_int* pxNewLightIDs = new u_int[iNewNumberOfPolygons];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxNewLightIDs[piPolyConv1[i]] = pxMesh->m_puPolygonLightIDs[i];
			if(piPolyConv2[i]==-1) continue;
			pxNewLightIDs[piPolyConv2[i]] = pxMesh->m_puPolygonLightIDs[i];
		}
		delete [] pxMesh->m_puPolygonLightIDs;
		pxMesh->m_puPolygonLightIDs = pxNewLightIDs;

	}

	//deal with project polygon data
	if(pxMesh->m_pxProjectPolygonData)
	{
		Asura_Mesh_ProjectPolygonData* pxNewProjectPolygonData = new Asura_Mesh_ProjectPolygonData[iNewNumberOfPolygons];
		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxNewProjectPolygonData[piPolyConv1[i]] = pxMesh->m_pxProjectPolygonData[i];
			if(piPolyConv2[i]==-1) continue;
			pxNewProjectPolygonData[piPolyConv2[i]] = pxMesh->m_pxProjectPolygonData[i];
		}
		delete [] pxMesh->m_pxProjectPolygonData;
		pxMesh->m_pxProjectPolygonData = pxNewProjectPolygonData;

	}


	//normals , prelighting , bounding boxes can be recalculated
	pxMesh->InvalidateNormals();
	
	
	delete [] pxMesh->PrelightingDataPtr;
	pxMesh->PrelightingDataPtr = NULL;
	pxMesh->PrelightingDataValid = false;
	
	pxMesh->BoundingBoxDataValid = false;
	
	//update the number of polygons
	pxMesh->NumberOfPolygons = iNewNumberOfPolygons;
	pxMesh->NumberOfTriangles++;

	pxMesh->CreateNormals();
	pxMesh->CalculateBoundingBox();


	Asura_Mesh_System::PlatformResetRenderMesh(pxMesh);
	Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);


	//tell various tools systems about the change to the number of polygons
	Tools_Polygon_Selection::HandlePolygonConversion(iModuleIndex,piPolyConv1,piPolyConv2);
	Tools_VertexPerPoly_Selection::HandleMeshChange(iModuleIndex);
	Tools_Edge_Selection::HandleMeshChange(iModuleIndex);

	Tools_Polygon_Selection::ClearUndo();
	Tools_Render_Options::HandlePolygonConversion(iModuleIndex,piPolyConv1,piPolyConv2);
	if(pxMesh->m_pxCollMeshGenData)
	{
		pxMesh->m_pxCollMeshGenData->HandlePolygonConversion(pxMesh,piPolyConv1,piPolyConv2);
	}

	Tools_Undo_System::ClearUndo();

	#if defined( ASURA_USE_CARPET)
	//the carpet data refers to polygon/vertex indeces for lighting.
	Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
	#endif


	InvalidateModuleCollisionMesh(iModuleIndex);
	delete [] piPolyConv1;
	delete [] piPolyConv2;

	return true;

}

void Tools_Mode_MeshEdit::FixTJunction()
{

	//ask for the polygon to be altered
	Tools_Prompted_Selection::ChoosePolygon("T-Junction Fix:\nClick on polygon to be altered.",FixTJunction_PolygonSelected);

	
}

void Tools_Mode_MeshEdit::FixTJunction_PolygonSelected(int iModuleIndex,int iPolygonIndex)
{
	g_xSelectedPolygon.ModuleIndex = iModuleIndex;
	g_xSelectedPolygon.PolyIndex = iPolygonIndex;

	//now get the vertex to do the split at
	Tools_Prompted_Selection::ChooseVertex("T-Junction Fix:\nClick on vertex to use for split.",FixTJunction_VertexSelected);

}

void Tools_Mode_MeshEdit::FixTJunction_VertexSelected(int iModuleIndex,int iVertexIndex)
{
	//get the position of the vertex
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_Vector_3 xPosition;
	Asura_Module_System::GetPositionOfModule(iModuleIndex,xPosition);

	Asura_Vector_3 xVertexPosition = xPosition + pxMesh->VertexDataPtr[iVertexIndex].Position;

	if(SplitPolygonEdge(g_xSelectedPolygon.ModuleIndex,g_xSelectedPolygon.PolyIndex,xVertexPosition))
	{
		Tools_Adjacency::InvalidateModule(g_xSelectedPolygon.ModuleIndex);
		Tools_SaveAsura::SetMeshModified(pxMesh);
	}
}

void Tools_Mode_MeshEdit::TearFix()
{
	//ask for the vertex to be moved
	Tools_Prompted_Selection::ChooseVertex("Tear Fix:\nClick on vertex to be moved.",TearFix_Vertex1Selected);
}

void Tools_Mode_MeshEdit::TearFix_Vertex1Selected(int iModuleIndex,int iVertexIndex)
{
	g_iSelectedVertex_ModuleIndex = iModuleIndex;
	g_iSelectedVertex_VertexIndex = iVertexIndex;

	//ask for the vertex to be snapped to
	Tools_Prompted_Selection::ChooseVertex("Tear Fix:\nClick on vertex to snap to.",TearFix_Vertex2Selected,TearFix_Vertex2Selectable);
}

void Tools_Mode_MeshEdit::TearFix_Vertex2Selected(int iModuleIndex,int iVertexIndex)
{

	//get the position of the vertex to snap to
	Asura_Mesh* pxSnapMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_Vector_3 xPosition;
	Asura_Module_System::GetPositionOfModule(iModuleIndex,xPosition);

	Asura_Vector_3 xSnapPosition = xPosition + pxSnapMesh->VertexDataPtr[iVertexIndex].Position;


	//convert to a position relative to the first mesh
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(g_iSelectedVertex_ModuleIndex);
	Asura_Module_System::GetPositionOfModule(g_iSelectedVertex_ModuleIndex,xPosition);
	xSnapPosition -= xPosition;


	//check distance of vertex away
	float fDist = (pxMesh->VertexDataPtr[g_iSelectedVertex_VertexIndex].Position-xSnapPosition).Magnitude();
	if(fDist>0.01f && !s_bSuppressConfirmation)
	{
		//warn before snapping points that are far apart
		char szMessage[200];
		Asura_PrintfToBfr(szMessage,"The vertex will be moved %d mm by doing this.\nGo ahead and snap the vertices anyway?.",200,true,(int)(fDist*1000));
		YesNo_Overlay xOverlay(szMessage);
			
		if(!xOverlay.DoModal()) return;
	}

	//snap the vertex to the position of the other one
	pxMesh->VertexDataPtr[g_iSelectedVertex_VertexIndex].Position = xSnapPosition;

	//invalidate various bits and pieces
	pxMesh->InvalidateNormals();
	
	
	delete [] pxMesh->PrelightingDataPtr;
	pxMesh->PrelightingDataPtr = NULL;
	pxMesh->PrelightingDataValid = false;
	
	pxMesh->BoundingBoxDataValid = false;
	
	pxMesh->CreateNormals();
	pxMesh->CalculateBoundingBox();


	Asura_Mesh_System::PlatformResetRenderMesh(pxMesh);
	Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);
	InvalidateModuleCollisionMesh(g_iSelectedVertex_ModuleIndex);

	Tools_SaveAsura::SetMeshModified(pxMesh);

}

bool Tools_Mode_MeshEdit::TearFix_Vertex2Selectable(int iModuleIndex,int iVertexIndex)
{
	if(iModuleIndex == g_iSelectedVertex_ModuleIndex && iVertexIndex == g_iSelectedVertex_VertexIndex)
	{
		return false;
	}
	return true;
}

void Tools_Mode_MeshEdit::SplitEdge()
{
	Tools_Prompted_Selection::ChooseEdge("Split Edge:\nClick on polygon edge to be split.\nThe new vertex will be inserted on this edge at the point nearest the cursor.",SplitEdge_EdgeSelected);
}

void Tools_Mode_MeshEdit::SplitEdge_EdgeSelected(Tools_LOS_EdgeOutput xEdge)
{
	//find the position under the cursor
	int iJunk1,iJunk2;
	Asura_Vector_3 xPosition;
	if(!Tools_LOS::FindPolyAndPositionUnderCursor(iJunk1,iJunk2,xPosition))
	{
		return;
	}

	//Get the points that make up the edge (in world space)
	int iModule = xEdge.m_axEdge[0].m_iModuleIndex;
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModule);
	Asura_Vector_3 xModulePos;
	Asura_Module_System::GetPositionOfModule(xEdge.m_axEdge[0].m_iModuleIndex,xModulePos);

	Asura_Vector_3 xPoint1 = pxMesh->VertexDataPtr[xEdge.m_axEdge[0].m_aiVertexIndex[0]].Position + xModulePos;
	Asura_Vector_3 xPoint2 = pxMesh->VertexDataPtr[xEdge.m_axEdge[0].m_aiVertexIndex[1]].Position + xModulePos;

	//Need to project the position under the cursor onto the nearest point of this line.
	Asura_Vector_3 xNormal = (xPoint2 - xPoint1);
	xNormal.Normalise();

	Asura_Vector_3 xOffset = xPosition - xPoint1;
	float fDistance = xOffset*xNormal;

	Asura_Vector_3 xSplitPoint = xPoint1 + xNormal*fDistance;

	//Now split the polygon on either side
	bool bChanged = SplitPolygonEdge(iModule,xEdge.m_axEdge[0].m_iPolygonIndex,xSplitPoint);
	if(xEdge.m_axEdge[1].m_iModuleIndex == iModule && xEdge.m_axEdge[1].m_iPolygonIndex >= 0)
	{
		//the polygon indeces will have changed from splitting the first polygon , so update
		//the adjacent polygon index if needed
		int iAdjPolyIndex = xEdge.m_axEdge[1].m_iPolygonIndex;
		if(iAdjPolyIndex > xEdge.m_axEdge[0].m_iPolygonIndex) iAdjPolyIndex ++;

		bChanged |= SplitPolygonEdge(iModule,iAdjPolyIndex,xSplitPoint);
	}

	if(bChanged)
	{
		Tools_Adjacency::InvalidateModule(iModule);
		Tools_SaveAsura::SetMeshModified(pxMesh);
	}
}

void Tools_Mode_MeshEdit::AutomaticTearFix()
{
	//get a list of completely selected modules
	List<int> xModuleList;

	for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
	{
		if(Tools_Polygon_Selection::IsModuleCompletelySelected(i))
		{
			xModuleList.add_entry(i);
		}
	}

	if(!xModuleList.size())
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"Completely select one or more modules to be altered first");
		return;
	}

 	YesNo_Overlay xOverlay("Tear Fix.\nThis will snap nearby vertices together in the currently selected modules.\nContinue?.");
	if(!xOverlay.DoModal()) return;


	for(LIF<int> xModuleLIF(&xModuleList);!xModuleLIF.done();xModuleLIF.next())
	{
		//get details for this module
		int iModule = xModuleLIF();
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModule);
		Asura_Vector_3 xModulePosition;
		Asura_Module_System::GetPositionOfModule(iModule,xModulePosition);

		if(!pxMesh->BoundingBoxDataValid) pxMesh->CalculateBoundingBox();
		//Get bounding box in world space
		Asura_Bounding_Box xBB = pxMesh->BoundingBox;
		xBB.Displace(xModulePosition);
		//expand by tolerance level 
		xBB.MinX -= s_fTearFixTolerance;
		xBB.MinY -= s_fTearFixTolerance;
		xBB.MinZ -= s_fTearFixTolerance;
		xBB.MaxX += s_fTearFixTolerance;
		xBB.MaxY += s_fTearFixTolerance;
		xBB.MaxZ += s_fTearFixTolerance;
		


		//get a list of all modules that overlap the box
		List<int> xNearModuleList;

		for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
		{
			Asura_Mesh* pxMesh2 = Asura_Module_System::GetMeshForModule(i);
			Asura_Vector_3 xModulePosition2;
			Asura_Module_System::GetPositionOfModule(i,xModulePosition2);

			if(!pxMesh2->BoundingBoxDataValid) pxMesh2->CalculateBoundingBox();
			//Get bounding box in world space
			Asura_Bounding_Box xBB2 = pxMesh2->BoundingBox;
			xBB2.Displace(xModulePosition2);

			if(xBB.Intersects(xBB2))
			{
				//Add selected nearby modules to the end of the list , and unselected to the start.
				//This will cause vertices to favour moving towards those vertices that can't be moved
				if(xModuleList.contains(i))
				{
					xNearModuleList.add_entry_end(i);
				}
				else
				{
					xNearModuleList.add_entry_start(i);
				}
			}
		}

		bool bMeshModified = false;

	
		for(int i=0;i<pxMesh->NumberOfVertices;i++)
		{
			Asura_Vector_3 xVertPos = pxMesh->VertexDataPtr[i].Position;
			
			bool bMatchFound = false;

			for(LIF<int> xNearLIF(&xNearModuleList);!xNearLIF.done() && !bMatchFound;xNearLIF.next())
			{
				int iModule2 = xNearLIF();
		
				Asura_Mesh* pxMesh2 = Asura_Module_System::GetMeshForModule(iModule2);
				Asura_Vector_3 xModulePosition2;
				Asura_Module_System::GetPositionOfModule(iModule2,xModulePosition2);
				
				//get offset between modules
				Asura_Vector_3 xOffset = xModulePosition2 - xModulePosition;

				//check for any vertices in this module that are within the tolerance distance
				for(int j=0;j<pxMesh2->NumberOfVertices && !bMatchFound;j++)
				{
					if(pxMesh == pxMesh2 && i == j)
					{
						//skip comparing vertex to itself
						continue;
					}
					
					Asura_Vector_3 xVertPos2 = pxMesh2->VertexDataPtr[j].Position + xOffset;

					float fDistance = (xVertPos2-xVertPos).Magnitude();
					if(fDistance < s_fTearFixTolerance)
					{
						//vertex is close enough , snap to this one
						pxMesh->VertexDataPtr[i].Position = xVertPos2;
						bMeshModified  |= (xVertPos!=xVertPos2);
						bMatchFound = true;
						
					}
				}
			}
		}

		if(bMeshModified)
		{
			//invalidate various bits of the mesh
			pxMesh->InvalidateNormals();
		
			
			delete [] pxMesh->PrelightingDataPtr;
			pxMesh->PrelightingDataPtr = NULL;
			pxMesh->PrelightingDataValid = false;
			
			pxMesh->BoundingBoxDataValid = false;
		
			pxMesh->CreateNormals();
			pxMesh->CalculateBoundingBox();


			Asura_Mesh_System::PlatformResetRenderMesh(pxMesh);
			Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);
			InvalidateModuleCollisionMesh(iModule);

			Tools_SaveAsura::SetMeshModified(pxMesh);

		}

	}

	Tools_Timed_Message_System::AddMessage(0xffffff,"Tear fix done on %d selected module(s)",xModuleList.size());

}



bool Tools_Mode_MeshEdit::CanMergeMeshesIntoModule(int iModuleIndex, const List<Asura_Mesh*>& xMeshList, u_int uMergeFlags, bool bPermanent)
{
	bool bMergeRender = (uMergeFlags & TOOLS_MERGEOBJECT_IGNORERENDERMESH) ? false : true;
	bool bMergeCollision = (uMergeFlags & TOOLS_MERGEOBJECT_IGNORECOLLISIONMESH) ? false : true;
	if(!bMergeRender && !bMergeCollision) return true;

	if(bPermanent)
	{
		//Collision mesh for permanent merges are handled differently
		bMergeCollision = false;
	}

	Asura_Mesh* pxCurrentMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_Mesh* pxDefaultMesh = pxCurrentMesh->GetDefaultPlatformMesh();

	//add up the number of triangles etc in the object meshes
	int iNumObjectTriangles = 0;
	int iNumObjectVerts = 0;
	int iNumObjectCollVerts = 0;

	for(CLIF<Asura_Mesh*> xMeshLIF(&xMeshList); !xMeshLIF.done(); xMeshLIF.next())
	{
		const Asura_Mesh* pxObjectMesh = xMeshLIF();

		iNumObjectVerts += pxObjectMesh->NumberOfVertices;
		
		iNumObjectTriangles += pxObjectMesh->NumberOfTriangles;
		//need to add to the count for any double sided polygons ,which unfortunately means just scanning
		//through the whole lot
		//RAR 01/05/2008
		if(pxObjectMesh->PolygonFlagDataPtr)
		{
			for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++)
			{
				if(pxObjectMesh->GetPolygonFlags(i) & AsuraPolygonFlag_DoubleSided)
				{
					iNumObjectTriangles += (pxObjectMesh->PolygonDataPtr[i].NumberOfVertices()== 4) ? 2 : 1;
				}
			}
		}

		if(pxObjectMesh->m_pxCollisionMesh)
		{
			iNumObjectCollVerts = pxObjectMesh->m_pxCollisionMesh->GetNumberOfVertices();
		}
		else
		{
			iNumObjectCollVerts = pxObjectMesh->NumberOfVertices;
		}

	}

	
	
	//make sure that it will be safe to do the merge
	if(bMergeRender)
	{
		//need to check each platform's mesh
		for(Asura_Mesh* pxPlatMesh = pxDefaultMesh; pxPlatMesh; pxPlatMesh = pxPlatMesh->m_pxNextPlatformMesh)
		{
			if(iNumObjectVerts + pxPlatMesh->NumberOfVertices > Asura_Mesh::MAX_VERTICES) return false;

			int iNumberOfTriangles = iNumObjectTriangles + pxPlatMesh->NumberOfTriangles;
			//need to add to the count for any double sided polygons ,which unfortunately means just scanning
			//through the whole lot
			//RAR 01/05/2008
			if(iNumberOfTriangles > Asura_Mesh::MAX_TRIANGLES) return false;
			if(pxPlatMesh->PolygonFlagDataPtr)
			{
				for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++)
				{
					if(pxPlatMesh->GetPolygonFlags(i) & AsuraPolygonFlag_DoubleSided)
					{
						iNumberOfTriangles += (pxPlatMesh->PolygonDataPtr[i].NumberOfVertices()== 4) ? 2 : 1;
					}
				}
			}
			if(iNumberOfTriangles > Asura_Mesh::MAX_TRIANGLES) return false;

		}
	}

	//and check the collision mesh for each platform
	for(Asura_Mesh* pxPlatMesh = pxDefaultMesh; pxPlatMesh; pxPlatMesh = pxPlatMesh->m_pxNextPlatformMesh)
	{
		if(bMergeCollision && pxPlatMesh->m_pxMergedEntityCollisionMesh)
		{
			//estimate number of vertices being merged in
			int iNumVertsAdded = iNumObjectCollVerts;

			//check each platform's mesh
			if(pxPlatMesh->m_pxMergedEntityCollisionMesh->GetNumberOfVertices() + iNumVertsAdded > 65536)
			{
				return false;
			}
			
		}
	}

	return true;	
}

void Tools_Mode_MeshEdit::MergeObjectIntoModule()
{
	Tools_Prompted_Selection::ChooseObject("Merge object into module.\nSelect an object.",MergeObjectIntoModule_ObjectSelected);
}

void Tools_Mode_MeshEdit::MergeObjectIntoModule_ObjectSelected(Tools_Object* pxObject)
{
	//make sure the object has a mesh or animation
	if(!pxObject->Mesh && !pxObject->GetAnimation())
	{
		return;
	}
	g_pxSelectedObject = pxObject;

	Tools_Prompted_Selection::ChooseModule("Merge object into module.\nSelect a module.",MergeObjectIntoModule_ModuleSelected);
}

void Tools_Mode_MeshEdit::MergeObjectIntoModule_ModuleSelected(int iModuleIndex)
{
	char szMessage[500];
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_PrintfToBfr(szMessage,"Merge %s into %s?",500,true,g_pxSelectedObject->GetName(),pxMesh->Name.GetString());

	if(!s_bSuppressConfirmation)
	{
		YesNo_Overlay xOverlay(szMessage);
		if(!xOverlay.DoModal()) return;
	}

	bool bMerged = false;
	if(g_pxSelectedObject->GetAnimation())
	{
		Asura_Hierarchy_Skin* pxSkin = g_pxSelectedObject->GetAnimation()->GetSkin();
		if(pxSkin)
		{
			bMerged = MergeSkinIntoModule(iModuleIndex,pxSkin,g_pxSelectedObject->Orientation,g_pxSelectedObject->Position,ASURA_GUID_UNREGISTERED,0,g_pxSelectedObject->GetScale(),0,0);
		}
	}
	else
	{
		bMerged = MergeObjectIntoModule(iModuleIndex,g_pxSelectedObject->Mesh,g_pxSelectedObject->Orientation,g_pxSelectedObject->Position,ASURA_GUID_UNREGISTERED,0,g_pxSelectedObject->GetScale(),0,0);
	}

	if(bMerged)
	{
		g_pxSelectedObject->Destroy();
	}
}

bool Tools_Mode_MeshEdit::MergeObjectIntoModule(int iModuleIndex,Asura_Mesh* pxObjectMesh,const Asura_Matrix_3x3& xObjectOrient,const Asura_Vector_3& xObjectPosition,Asura_Guid uObjectGuid /*= ASURA_GUID_UNREGISTERED*/,u_int uObjectChecksum,float fScale /*= 1.0f*/)
{
	Asura_Vector_3 xScale(fScale,fScale,fScale);
	u_int uPolyFlagsToAdd = 0;
	u_int uMergeFlags = 0;
	return MergeObjectIntoModule(iModuleIndex,pxObjectMesh,xObjectOrient,xObjectPosition,uObjectGuid,uObjectChecksum,xScale,uPolyFlagsToAdd,uMergeFlags);
}


bool Tools_Mode_MeshEdit::MergeObjectIntoModule(int iModuleIndex,Asura_Mesh* pxObjectMesh,const Asura_Matrix_3x3& xObjectOrient,const Asura_Vector_3& xObjectPosition,Asura_Guid uObjectGuid ,u_int uObjectChecksum,const Asura_Vector_3& xScale,u_int uPolygonFlagsToAdd,u_int uMergeFlags)
{
	bool bMergeRender = (uMergeFlags & TOOLS_MERGEOBJECT_IGNORERENDERMESH) ? false : true;
	bool bMergeCollision = (uMergeFlags & TOOLS_MERGEOBJECT_IGNORECOLLISIONMESH) ? false : true;
	if(!bMergeRender && !bMergeCollision) return true;

	if(uObjectGuid == ASURA_GUID_UNREGISTERED)
	{
		//If there isn't an object guid then the collision mesh isn't merged in the same way
		bMergeCollision = false;
	}

	// Line of Sight data will need to be recalculated
	Tools_Environment_LOS::Reset();

	//Get the mesh currently being displayed for the module , and the default mesh for that module
	Asura_Mesh* pxCurrentMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);
	Asura_Mesh* pxDefaultMesh = pxCurrentMesh->GetDefaultPlatformMesh();

	Asura_Vector_3 xModulePos = Asura_Module_System::GetPositionOfModule(iModuleIndex);

	//make sure that it will be safe to do the merge
	if(bMergeRender)
	{
		//need to check each platform's mesh
		for(Asura_Mesh* pxPlatMesh = pxDefaultMesh; pxPlatMesh; pxPlatMesh = pxPlatMesh->m_pxNextPlatformMesh)
		{
			if(pxObjectMesh->NumberOfVertices + pxPlatMesh->NumberOfVertices > Asura_Mesh::MAX_VERTICES) return false;

			int iNumberOfTriangles = pxObjectMesh->NumberOfTriangles + pxPlatMesh->NumberOfTriangles;
			//need to add to the count for any double sided polygons ,which unfortunately means just scanning
			//through the whole lot
			//RAR 01/05/2008
			if(iNumberOfTriangles > Asura_Mesh::MAX_TRIANGLES) return false;
			if(pxObjectMesh->PolygonFlagDataPtr)
			{
				for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++)
				{
					if(pxObjectMesh->GetPolygonFlags(i) & AsuraPolygonFlag_DoubleSided)
					{
						iNumberOfTriangles += (pxObjectMesh->PolygonDataPtr[i].NumberOfVertices()== 4) ? 2 : 1;
					}
				}
			}
			if(pxPlatMesh->PolygonFlagDataPtr)
			{
				for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++)
				{
					if(pxPlatMesh->GetPolygonFlags(i) & AsuraPolygonFlag_DoubleSided)
					{
						iNumberOfTriangles += (pxPlatMesh->PolygonDataPtr[i].NumberOfVertices()== 4) ? 2 : 1;
					}
				}
			}
			if(iNumberOfTriangles > Asura_Mesh::MAX_TRIANGLES) return false;

		}
	}

	//and check the collision mesh for each platform
	for(Asura_Mesh* pxPlatMesh = pxDefaultMesh; pxPlatMesh; pxPlatMesh = pxPlatMesh->m_pxNextPlatformMesh)
	{
		if(bMergeCollision && pxPlatMesh->m_pxMergedEntityCollisionMesh)
		{
			//estimate number of vertices being merged in
			int iNumVertsAdded;
			if(pxObjectMesh->m_pxCollisionMesh)
			{
				iNumVertsAdded = pxObjectMesh->m_pxCollisionMesh->GetNumberOfVertices();
			}
			else
			{
				iNumVertsAdded = pxObjectMesh->NumberOfVertices;
			}

			//check each platform's mesh
			if(pxPlatMesh->m_pxMergedEntityCollisionMesh->GetNumberOfVertices() + iNumVertsAdded > 65536)
			{
				return false;
			}
			
		}
	}
	
	//get the offset of the object from the module
	Asura_Vector_3 xOffset = xObjectPosition - xModulePos;

	//merge into each platform's mesh
	for(Asura_Mesh* pxPlatMesh = pxDefaultMesh; pxPlatMesh; pxPlatMesh = pxPlatMesh->m_pxNextPlatformMesh)
	{
		if(bMergeRender)
		{
			//allocate conversion array for indeces in the object mesh
			int* piVertConv = new int[pxObjectMesh->NumberOfVertices];

			//Need to discover which vertices in the object are used.
			//We can't afford to add any unused vertices , otherwise we will end up with a
			//continual build up of these vertices , since they won't get deleted 
			//if the object polygons are later removed. RAR 09/09/2004
			const int iVertexNotUsed = -2;
			const int iVertexUsed = -1;

			for(int i=0;i<pxObjectMesh->NumberOfVertices;i++)
			{
				piVertConv[i] = iVertexNotUsed;
			}
			for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++)
			{
				Asura_Mesh_Polygon* pxPoly = &pxObjectMesh->PolygonDataPtr[i];
				for(int j=0;j<4;j++)
				{
					u_short usVert = pxPoly->VertexID[j];
					if( usVert != 0xffff)
					{
						piVertConv[usVert] = iVertexUsed;
					}
				}
			}

	
	
			//Merge vertex arrays
			//Allocate enough space in case all the vertices turn out to be different
			Asura_Mesh_Vertex* pxNewVertexData = new Asura_Mesh_Vertex[pxPlatMesh->NumberOfVertices+pxObjectMesh->NumberOfVertices];

			//from module
			int iIndex = 0;
			for(int i=0;i<pxPlatMesh->NumberOfVertices;i++,iIndex++)
			{
				pxNewVertexData[iIndex] = pxPlatMesh->VertexDataPtr[i];
			}

			//from object
			for(int i=0;i<pxObjectMesh->NumberOfVertices;i++)
			{
				if(piVertConv[i] == iVertexNotUsed) continue;
				
				Asura_Vector_3 xPosition = pxObjectMesh->VertexDataPtr[i].Position;
				xPosition.x *= xScale.x;
				xPosition.y *= xScale.y;
				xPosition.z *= xScale.z;
				xPosition = xPosition * xObjectOrient + xOffset;

				u_int uVertFlags = pxObjectMesh->GetVertexFlags(i);

				//see if this matches any of the existing vertices
				bool bFoundMatch = false;
				for(int j=0;j<pxPlatMesh->NumberOfVertices;j++)
				{
					if(pxPlatMesh->GetVertexFlags(j) != uVertFlags) continue;
					
					if(pxPlatMesh->VertexDataPtr[j].Position.ApproximatelyEquals(xPosition))
					{
						piVertConv[i] = j;
						bFoundMatch = true;
						break;
					}
				}

				if(!bFoundMatch)
				{
					//need to add this vertex onto the end then
					piVertConv[i] = iIndex;
					pxNewVertexData[iIndex++].Position = xPosition;

				}

			}
			int iNewNumberOfVertices = iIndex;


			delete [] pxPlatMesh->VertexDataPtr;
			pxPlatMesh->VertexDataPtr = pxNewVertexData;

			//Determine suitable insertion point for the new polygons.
			//If we have a guid , try to add the object in such a way as to sort the guids
			//of all attached objects in ascending order. 
			//This should hopefully minimise the the extent to which lighting gets messed up
			//when objects get removed and attached again when templates get updated.
			int iInsertionPoint = pxPlatMesh->NumberOfPolygons;
			if(pxPlatMesh->m_pxPolygonGuids)
			{
				for(iInsertionPoint = 0;iInsertionPoint<pxPlatMesh->NumberOfPolygons;iInsertionPoint++)
				{
					if(pxPlatMesh->m_pxPolygonGuids[iInsertionPoint]>uObjectGuid)
					{
						break;
					}
				}
			}

			//merge polygon arrays
			int iOldNumberOfPolygons = pxPlatMesh->NumberOfPolygons;
			int iNewNumberOfPolygons = pxPlatMesh->NumberOfPolygons + pxObjectMesh->NumberOfPolygons;
			Asura_Mesh_Polygon* pxNewPolygonData = new Asura_Mesh_Polygon[iNewNumberOfPolygons];

			//from module 
			iIndex = 0;
			for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
			{
				if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
				pxNewPolygonData[iIndex] = pxPlatMesh->PolygonDataPtr[i];
			}

			//from object
			iIndex = iInsertionPoint;
			for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
			{
				pxNewPolygonData[iIndex] = pxObjectMesh->PolygonDataPtr[i];
				Asura_Mesh_Polygon* pxPoly = &pxNewPolygonData[iIndex];

				//need to adjust the vertex indeces
				for(int j=0;j<4;j++)
				{
					if(pxPoly->VertexID[j] != 0xffff)
					{
						Asura_Assert(piVertConv[pxPoly->VertexID[j]] >=0 , "Vertex conversion array incorrect");
						pxPoly->VertexID[j] = (u_short)(piVertConv[pxPoly->VertexID[j]]);
					}
				}
			}

	
			delete [] pxPlatMesh->PolygonDataPtr;
			pxPlatMesh->PolygonDataPtr = pxNewPolygonData;

			//copy vertex flags
			if(pxPlatMesh->m_pxVertexFlags || pxObjectMesh->m_pxVertexFlags)
			{
				Asura_Mesh_VertexFlags* pxNewVertexFlags = new Asura_Mesh_VertexFlags[iNewNumberOfVertices];
				Asura_Mem::Set(pxNewVertexFlags,0,sizeof(Asura_Mesh_VertexFlags) * iNewNumberOfVertices);

				//from module
				if(pxPlatMesh->m_pxVertexFlags)
				{
					Asura_Mem::Copy(pxNewVertexFlags,pxPlatMesh->m_pxVertexFlags,sizeof(Asura_Mesh_VertexFlags)*pxPlatMesh->NumberOfVertices);
				}

				//from object
				if(pxObjectMesh->m_pxVertexFlags)
				{
					for(int i=0;i<pxObjectMesh->NumberOfVertices;i++)
					{
						if(piVertConv[i] < 0) continue;
						pxNewVertexFlags[piVertConv[i]] = pxObjectMesh->m_pxVertexFlags[i];
					}
				}

				delete [] pxPlatMesh->m_pxVertexFlags;
				pxPlatMesh->m_pxVertexFlags = pxNewVertexFlags;
			}

			//finished with vertex conversion array
			delete [] piVertConv;
			piVertConv = 0;


			//merge texturing
			Asura_Assert(pxPlatMesh->TextureDataValid && pxObjectMesh->TextureDataValid,"");
			Asura_Mesh_TextureInfo * pxNewTextureData = new Asura_Mesh_TextureInfo[iNewNumberOfPolygons];

			//from module
			iIndex = 0;
			for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
			{
				if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
				pxNewTextureData[iIndex] = pxPlatMesh->TextureDataPtr[i];
			}

			//from object
			iIndex = iInsertionPoint;
			for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
			{
				pxNewTextureData[iIndex] = pxObjectMesh->TextureDataPtr[i];

				//Code for removing dual pass textures disabled.
				//RAR 01/06/2006
				#if 0
				if(uObjectGuid != ASURA_GUID_UNREGISTERED)
				{
					//for objects with 'merge into environment set' make sure no secondary texture
					//is in use
					int iMatID = pxNewTextureData[iIndex].m_iMaterialID;
					if(iMatID != ASURA_MATERIAL_NONE)
					{
						int iTexID = Asura_Material_System::GetTextureID(iMatID,1);
						if(iTexID != ASURA_TEXTURE_NONE)
						{
							//get a new material without the second texture
							pxNewTextureData[iIndex].m_iMaterialID = Tools_Mode_Texturing::GetMaterialWithTexture(iMatID,ASURA_TEXTURE_NONE,ASURA_TEXTURE_NONE,ASURA_TEXTURE_NONE,1);
						}
					}
					//make sure alpha is all 1
					for(int j=0;j<4;j++)
					{
						pxNewTextureData[iIndex].m_afAlpha[j] = 1.0f;			
					}

				}
				#endif
				
				//make sure any textures used by the object are added to the list of textures for the level
				if(pxNewTextureData[iIndex].m_iMaterialID != ASURA_MATERIAL_NONE)
				{
					Tools_PickTexture::AddToMaterialList(pxNewTextureData[iIndex].m_iMaterialID);
				}
			}

			delete [] pxPlatMesh->TextureDataPtr;
			pxPlatMesh->TextureDataPtr = pxNewTextureData;
			pxPlatMesh->DeleteProcessedTextureInfo();

			// JGP - Painted Colour

			//merge painted colour
			Asura_Assert( pxPlatMesh->m_bPaintedColourDataValid && pxObjectMesh->m_bPaintedColourDataValid, "" );
			Asura_Mesh_PaintedColours* pxNewPaintedColourDataPtr = new Asura_Mesh_PaintedColours[ iNewNumberOfPolygons ];

			//from module
			iIndex = 0;
			for( int i = 0; i < pxPlatMesh->NumberOfPolygons; i++, iIndex++ )
			{
				if ( i == iInsertionPoint )
				{
					iIndex += pxObjectMesh->NumberOfPolygons;
				}
				pxNewPaintedColourDataPtr[ iIndex ] = pxPlatMesh->m_pxPaintedColourDataPtr[ i ];
			}

			//from object
			iIndex = iInsertionPoint;
			for( int i = 0; i < pxObjectMesh->NumberOfPolygons; i++, iIndex++ )
			{
				pxNewPaintedColourDataPtr[ iIndex ] = pxObjectMesh->m_pxPaintedColourDataPtr[ i ];
			}

			delete [] pxPlatMesh->m_pxPaintedColourDataPtr;
			pxPlatMesh->m_pxPaintedColourDataPtr = pxNewPaintedColourDataPtr;


			//smoothing
			if(pxPlatMesh->SmoothingGroupDataValid || pxObjectMesh->SmoothingGroupDataValid)
			{
				int* piNewSmoothingData = new int[iNewNumberOfPolygons];

				//from module
				iIndex = 0;
				for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
				{
					if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
					piNewSmoothingData[iIndex] = pxPlatMesh->SmoothingGroupDataValid ? pxPlatMesh->SmoothingGroupDataPtr[i] : 0;
				}

				//from object
				iIndex = iInsertionPoint;
				for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
				{
					piNewSmoothingData[iIndex] = pxObjectMesh->SmoothingGroupDataValid ? pxObjectMesh->SmoothingGroupDataPtr[i] : 0;
				}
				delete [] pxPlatMesh->SmoothingGroupDataPtr;
				pxPlatMesh->SmoothingGroupDataPtr = piNewSmoothingData;
				pxPlatMesh->SmoothingGroupDataValid = true;
			}


			//polygon flags
			if(pxPlatMesh->PolygonFlagDataValid || pxObjectMesh->PolygonFlagDataValid)
			{
				Asura_Mesh_PolygonFlags* pxNewPolygonFlagData = new Asura_Mesh_PolygonFlags[iNewNumberOfPolygons];

				//from module
				iIndex = 0;
				for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
				{
					if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
					
					if(pxPlatMesh->PolygonFlagDataValid)
						pxNewPolygonFlagData[iIndex] = pxPlatMesh->PolygonFlagDataPtr[i];
					else
						pxNewPolygonFlagData[iIndex].Flags = 0;
				}

				//from object
				iIndex = iInsertionPoint;
				for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
				{
					if(pxObjectMesh->PolygonFlagDataValid)
						pxNewPolygonFlagData[iIndex] = pxObjectMesh->PolygonFlagDataPtr[i];
					else
						pxNewPolygonFlagData[iIndex].Flags = 0;

					if(uPolygonFlagsToAdd)
					{
						pxNewPolygonFlagData[iIndex].Flags |= uPolygonFlagsToAdd;
					}
				}

				delete [] pxPlatMesh->PolygonFlagDataPtr;
				pxPlatMesh->PolygonFlagDataPtr = pxNewPolygonFlagData;
				pxPlatMesh->PolygonFlagDataValid = true;
			}


			//copy light data
			u_int* puOldLightIDs = 0;
			u_int* puNewLightIDs = 0;
			if(pxObjectMesh->m_iNumberOfPolygonLights)
			{
				puOldLightIDs = new u_int[pxObjectMesh->m_iNumberOfPolygonLights];
				puNewLightIDs = new u_int[pxObjectMesh->m_iNumberOfPolygonLights];

				int iNewNumberOfPolygonLights = pxPlatMesh->m_iNumberOfPolygonLights + pxObjectMesh->m_iNumberOfPolygonLights;
				Asura_Mesh_PolygonLight* pxLightData = new Asura_Mesh_PolygonLight[iNewNumberOfPolygonLights];

				//copy old light data , and get the next available light ID
				u_int uLightID = 1;
				int iIndex = 0;
				for(int i=0;i<pxPlatMesh->m_iNumberOfPolygonLights;i++,iIndex++)
				{
					pxLightData[iIndex] = pxPlatMesh->m_pxPolygonLights[i];
					uLightID = Asura_Maths::Max(uLightID,pxLightData[iIndex].m_uLightID+1);
				}

				//copy lights from object , generating new light IDs
				for(int i=0;i<pxObjectMesh->m_iNumberOfPolygonLights;i++,iIndex++)
				{
					pxLightData[iIndex] = pxObjectMesh->m_pxPolygonLights[i];
					puOldLightIDs[i] = pxLightData[iIndex].m_uLightID;
					puNewLightIDs[i] = uLightID++;
					pxLightData[iIndex].m_uLightID = puNewLightIDs[i];
				}

				delete [] pxPlatMesh->m_pxPolygonLights;
				pxPlatMesh->m_pxPolygonLights = pxLightData;
				pxPlatMesh->m_iNumberOfPolygonLights = iNewNumberOfPolygonLights;
			}

			//deal with polygon lights
			if(pxPlatMesh->m_puPolygonLightIDs || pxObjectMesh->m_puPolygonLightIDs)
			{
				u_int* puNewPolygonLightIDs = new u_int[iNewNumberOfPolygons];

				//from module
				iIndex = 0;
				for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
				{
					if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
					
					if(pxPlatMesh->m_puPolygonLightIDs)
						puNewPolygonLightIDs[iIndex] = pxPlatMesh->m_puPolygonLightIDs[i];
					else
						puNewPolygonLightIDs[iIndex] = 0;
				}

				//from object
				iIndex = iInsertionPoint;
				for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
				{
					if(pxObjectMesh->m_puPolygonLightIDs)
					{
						u_int uOldLightID = pxObjectMesh->m_puPolygonLightIDs[i];
						u_int uNewLightID = 0;
						if(uOldLightID)
						{
							//need to search for converted light ID
							for(int j=0;j<pxObjectMesh->m_iNumberOfPolygonLights;j++)
							{
								if(uOldLightID == puOldLightIDs[j])
								{
									uNewLightID = puNewLightIDs[j];
									break;
								}
							}
						}
						
						puNewPolygonLightIDs[iIndex] = uNewLightID;
					}
					else
					{
						puNewPolygonLightIDs[iIndex] = 0;
					}
				}

				delete [] pxPlatMesh->m_puPolygonLightIDs;
				pxPlatMesh->m_puPolygonLightIDs = puNewPolygonLightIDs;

			}

			delete [] puOldLightIDs;
			delete [] puNewLightIDs;

			//deal with polygon guids
			if(uObjectGuid != ASURA_GUID_UNREGISTERED || pxPlatMesh->m_pxPolygonGuids)
			{
				Asura_Guid* pxNewPolygonGuids = new Asura_Guid[iNewNumberOfPolygons];

				//copy old guids from module
				iIndex = 0;
				for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
				{
					if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
					pxNewPolygonGuids[iIndex] = pxPlatMesh->m_pxPolygonGuids ? pxPlatMesh->m_pxPolygonGuids[i] : ASURA_GUID_UNREGISTERED;
				}

				//and set the polygon guids for the new polygons
				iIndex = iInsertionPoint;
				for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
				{
					pxNewPolygonGuids[iIndex] = uObjectGuid;
				}

				delete [] pxPlatMesh->m_pxPolygonGuids;
				pxPlatMesh->m_pxPolygonGuids = pxNewPolygonGuids;
			}

			// Max/maya polygon indeces
			if(pxPlatMesh->m_bPolygonIndexDataValid)
			{
				int* piNewPolygonIndexData = new int[iNewNumberOfPolygons];

				//copy from module
				iIndex = 0;
				for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
				{
					if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
					piNewPolygonIndexData[iIndex] = pxPlatMesh->m_piPolygonIndexDataPtr ? pxPlatMesh->m_piPolygonIndexDataPtr[i] : ASURA_GUID_UNREGISTERED;
				}

				//initialise new polygons to an invalid value
				iIndex = iInsertionPoint;
				for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
				{
					piNewPolygonIndexData[iIndex] = -1;
				}

				delete [] pxPlatMesh->m_piPolygonIndexDataPtr;
				pxPlatMesh->m_piPolygonIndexDataPtr = piNewPolygonIndexData;
			}

			//project polygon data
			if(pxPlatMesh->m_pxProjectPolygonData || pxObjectMesh->m_pxProjectPolygonData)
			{
				Asura_Mesh_ProjectPolygonData* pxNewProjectPolygonData = new Asura_Mesh_ProjectPolygonData[iNewNumberOfPolygons];
				Asura_Mem::Set(pxNewProjectPolygonData,0,sizeof(Asura_Mesh_ProjectPolygonData) * iNewNumberOfPolygons);

				//from module
				if(pxPlatMesh->m_pxProjectPolygonData)
				{
					iIndex = 0;
					for(int i=0;i<pxPlatMesh->NumberOfPolygons;i++,iIndex++)
					{
						if(i==iInsertionPoint) iIndex += pxObjectMesh->NumberOfPolygons;
						pxNewProjectPolygonData[iIndex] = pxPlatMesh->m_pxProjectPolygonData[i];
					}
				}

				//from object
				if(pxObjectMesh->m_pxProjectPolygonData)
				{
					iIndex = iInsertionPoint;
					for(int i=0;i<pxObjectMesh->NumberOfPolygons;i++,iIndex++)
					{
						pxNewProjectPolygonData[iIndex] = pxObjectMesh->m_pxProjectPolygonData[i];
					}
				}
				delete [] pxPlatMesh->m_pxProjectPolygonData;
				pxPlatMesh->m_pxProjectPolygonData = pxNewProjectPolygonData;
			}



			//normals , prelighting , bounding boxes can be recalculated
			pxPlatMesh->InvalidateNormals();
			
			
			delete [] pxPlatMesh->PrelightingDataPtr;
			pxPlatMesh->PrelightingDataPtr = NULL;
			pxPlatMesh->PrelightingDataValid = false;
			
			pxPlatMesh->BoundingBoxDataValid = false;
			
			//update the number of polygons and vertices
			pxPlatMesh->NumberOfPolygons += pxObjectMesh->NumberOfPolygons;
			pxPlatMesh->NumberOfVertices = iNewNumberOfVertices;
			pxPlatMesh->NumberOfTriangles += pxObjectMesh->NumberOfTriangles;

			pxPlatMesh->CreateNormals();
			pxPlatMesh->CalculateBoundingBox();

			Asura_Mesh_System::PlatformResetRenderMesh(pxPlatMesh);
			Tools_RenderModule_System::UpdateModuleMesh(pxPlatMesh->Index);


			//create a polygon conversion array , for updating polygon selecting , and render flags
			//Only need to do this if its the mesh that is currently being viewed. RAR 16/03/2007
			if(pxPlatMesh == pxCurrentMesh)
			{
				int* piPolyConv = new int[pxPlatMesh->NumberOfPolygons];
				for(int i=0;i<iOldNumberOfPolygons;i++)
				{
					piPolyConv[i] = (i<iInsertionPoint) ? i : (i+pxObjectMesh->NumberOfPolygons);
				}

				//do the updating
				Tools_Polygon_Selection::HandlePolygonConversion(iModuleIndex,piPolyConv);
				Tools_Vertex_Selection::HandleVertexConversion(iModuleIndex,0);
				Tools_VertexPerPoly_Selection::HandleMeshChange(iModuleIndex);
				Tools_Edge_Selection::HandleMeshChange(iModuleIndex);

				Tools_Render_Options::HandlePolygonConversion(iModuleIndex,piPolyConv);
				if(pxPlatMesh->m_pxCollMeshGenData)
				{
					pxPlatMesh->m_pxCollMeshGenData->HandlePolygonConversion(pxPlatMesh,piPolyConv);
				}

				Tools_Undo_System::ClearUndo();

				#if defined( ASURA_USE_CARPET)
				//the carpet data refers to polygon/vertex indeces for lighting.
				Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
				#endif


				delete [] piPolyConv;
			}
			else
			{
				//RAR Really need to remove the CollMeshGenData stuff , since it was never used anyway
				Asura_Assert(!pxPlatMesh->m_pxCollMeshGenData,"");
			}

		}//end of render mesh merging


		//Do the collision mesh
		if(bMergeCollision)
		{
			//get the collision mesh to add
			Asura_CollisionMesh* pxCollMesh;
			bool bTempCollMesh = false;

			if(pxObjectMesh->m_pxCollisionMesh)
			{
				pxCollMesh = pxObjectMesh->m_pxCollisionMesh;
			}
			else
			{
				pxCollMesh = new Asura_CollisionMesh(pxObjectMesh);
				bTempCollMesh = true;
			}

			if(!pxPlatMesh->m_pxMergedEntityCollisionMesh)
			{
				pxPlatMesh->m_pxMergedEntityCollisionMesh = new Asura_CollisionMesh;
			}
			int iOldCollPolys = pxPlatMesh->m_pxMergedEntityCollisionMesh->GetNumberOfPolys();
			pxPlatMesh->m_pxMergedEntityCollisionMesh->AddCollisionMesh(pxCollMesh,xObjectOrient,xOffset,xScale,uPolygonFlagsToAdd);
			int iNewCollPolys = pxPlatMesh->m_pxMergedEntityCollisionMesh->GetNumberOfPolys();

			//update the polygon guids
			Asura_Guid* puGuids = new Asura_Guid[iNewCollPolys];

			for(int i=0;i<iOldCollPolys;i++)
			{
				puGuids[i] = pxPlatMesh->m_pxCollPolygonGuids[i];
			}
			for(int i=iOldCollPolys;i<iNewCollPolys;i++)
			{
				puGuids[i] = uObjectGuid;
			}

			delete [] pxPlatMesh->m_pxCollPolygonGuids;
			pxPlatMesh->m_pxCollPolygonGuids = puGuids;

			if(bTempCollMesh) delete pxCollMesh;
		}
	}

	if(uObjectGuid != ASURA_GUID_UNREGISTERED)
	{
		//update the list of guids merged to this object
		//only the default mesh for the module needs to store this information

		int iIndex = -1;
		//make sure an entry doesn't already exist
		for(int i=0;i<pxDefaultMesh->m_iNumberOfMergedEntities;i++)
		{
			if(pxDefaultMesh->m_pxMergedEntities[i].m_uGuid == uObjectGuid)
			{
				iIndex = i;
				break;
			}
		}

		if(iIndex == -1)
		{
			//increase array size
			Asura_Mesh_MergedEntity* pxEntities = new Asura_Mesh_MergedEntity[pxDefaultMesh->m_iNumberOfMergedEntities+1];
			for(int i=0;i<pxDefaultMesh->m_iNumberOfMergedEntities;i++)
			{
				pxEntities[i] = pxDefaultMesh->m_pxMergedEntities[i];
			}
			delete [] pxDefaultMesh->m_pxMergedEntities;
			pxDefaultMesh->m_pxMergedEntities = pxEntities;
			iIndex = pxDefaultMesh->m_iNumberOfMergedEntities++;
		}

		//fill in details for this entity
		pxDefaultMesh->m_pxMergedEntities[iIndex].m_uGuid = uObjectGuid;
		pxDefaultMesh->m_pxMergedEntities[iIndex].m_uChecksum = uObjectChecksum;
	}




	InvalidateModuleCollisionMesh(iModuleIndex);

	Tools_Adjacency::InvalidateModule(iModuleIndex);
	Tools_SaveAsura::SetModuleModified(iModuleIndex);

	return true;
}

bool Tools_Mode_MeshEdit::MergeSkinIntoModule(int iModuleIndex,Asura_Hierarchy_Skin* pxSkin,const Asura_Matrix_3x3& xObjectOrient,const Asura_Vector_3& xObjectPosition,Asura_Guid uObjectGuid ,u_int uObjectChecksum,const Asura_Vector_3& xScale,u_int uPolygonFlagsToAdd,u_int uMergeFlags)
{
	bool bAnyMerged = false;
	bool bAllMerged = true;

	if(pxSkin->GetCollisionMesh())
	{
		//skin has a seperate collision mesh
		//Create a dummy mesh with the collision mesh attached.
		Asura_Mesh xDummyMesh("AMesh");
		xDummyMesh.m_pxCollisionMesh = pxSkin->GetCollisionMesh();

		u_int uCollMergeFlags = uMergeFlags | TOOLS_MERGEOBJECT_IGNORERENDERMESH;

		bool bMerged = MergeObjectIntoModule(iModuleIndex, &xDummyMesh, xObjectOrient, xObjectPosition, uObjectGuid, uObjectChecksum, xScale, uPolygonFlagsToAdd, uCollMergeFlags);
		bAnyMerged |= bMerged;
		bAllMerged &= bMerged;

		//clear the collision mesh from the mesh so it doesn't get deleted with the mesh
		xDummyMesh.m_pxCollisionMesh = 0;

		///make sure the subsequent merging of render meshes doesn't add any more collision mesh
		uMergeFlags |= TOOLS_MERGEOBJECT_IGNORECOLLISIONMESH;		
	}
	
	if(bAllMerged)
	{
		if(pxSkin->IsSkinnedModel())
		{
			Asura_Assert(pxSkin->GetMesh(),"");
			if(!pxSkin->GetMesh()) return false;

			//I think the mesh should already be in the rest pose , so just merge it as is
			bool bMerged = MergeObjectIntoModule(iModuleIndex, pxSkin->GetMesh(), xObjectOrient, xObjectPosition, uObjectGuid, uObjectChecksum, xScale, uPolygonFlagsToAdd, uMergeFlags);

			bAnyMerged |= bMerged;
			bAllMerged &= bMerged;
		}
		else
		{
			Asura_Assert(pxSkin->SectionData,"");

			//need to merge each object from the skin
			Asura_Hierarchy_Bone_RestFrame* pxRestPose = new Asura_Hierarchy_Bone_RestFrame[pxSkin->NumberOfBones];
			for(int i=0; i<pxSkin->NumberOfBones && bAllMerged; i++)
			{
				if(i)
				{
					int iParent = pxSkin->ParentBone[i];

					pxRestPose[i].Quat = pxRestPose[iParent].Quat * pxSkin->BoneRestPose[i].Quat;

					pxRestPose[i].Position = pxRestPose[iParent].Quat.RotateVector(pxSkin->BoneRestPose[i].Position);
					pxRestPose[i].Position += pxRestPose[iParent].Position;
		 		}
				else
				{
					pxRestPose[i].Quat = pxSkin->BoneRestPose[i].Quat;
					pxRestPose[i].Position = pxSkin->BoneRestPose[i].Position;
				}

				Asura_Mesh* pxMesh = pxSkin->SectionData[i].Mesh;
				if(pxMesh)
				{
					Asura_Matrix_3x3 xBoneOrient;
					pxRestPose[i].Quat.ToMatrix(xBoneOrient);

					Asura_Matrix_3x3 xWorldOrient = xObjectOrient*xBoneOrient;

					Asura_Vector_3 xScaledBonePos = pxRestPose[i].Position;
					xScaledBonePos.x *= xScale.x;
					xScaledBonePos.y *= xScale.y;
					xScaledBonePos.z *= xScale.z;

					Asura_Vector_3 xWorldPos = xObjectPosition + (xScaledBonePos*xObjectOrient);

					bool bMerged = MergeObjectIntoModule(iModuleIndex, pxMesh, xWorldOrient, xWorldPos, uObjectGuid, uObjectChecksum, xScale, uPolygonFlagsToAdd, uMergeFlags);

					bAnyMerged |= bMerged;
					bAllMerged &= bMerged;

				}
			}
			delete [] pxRestPose;
		}
	}

	if(bAnyMerged && !bAllMerged)
	{
		//Merged some but not all of the required meshes.
		//Delete those that were merged
		if(uObjectGuid != ASURA_GUID_UNREGISTERED)
		{
			DeleteEntityPolygons(uObjectGuid);
		}
	}

	return bAllMerged;
}


static bool DoesModuleHaveAnyRenderedPolygons(int iModuleIndex)
{
	Asura_Assert(iModuleIndex>=0 && iModuleIndex<Asura_Module_System::GetNumberOfModules(),"");
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleIndex);

	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		if(!(pxMesh->GetPolygonFlags(i) & (AsuraPolygonFlag_DontRender | AsuraPolygonFlag_DontExport)))
		{
			//found a visible polygon
			return true;
		}
	}
	return false;
}

int Tools_Mode_MeshEdit::GetModuleForObjectMerge(const Asura_Vector_3& xPosition,const Asura_Vector_3& xCentre)
{
	//first check the module beneath the object
	Asura_Vector_3 xDirection(0,1,0);
	int iModule = -1;
	int iModuleBelow = -1;

	int iJunk;
	float fJunk;
	Tools_LOS::SetPolygonFlagsToIgnore(AsuraPolygonFlag_DontRender);
	Tools_LOS::FindPolyInLOS(xCentre,xDirection,iJunk,iModuleBelow,fJunk,true);
	Tools_LOS::SetPolygonFlagsToIgnore(0);
	if(iModuleBelow != -1 )
	{
		//is this module okay...
		if(Asura_Environment_System::GetEnvironment()->IsSceneryModule(iModuleBelow))
		{
			//skip scenery modules
			iModuleBelow = -1;
		}
		else
		{
			//check to see if the position is in the module
			Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModuleBelow);
			if(!pxMesh->BoundingBoxDataValid) pxMesh->CalculateBoundingBox();

			Asura_Bounding_Box xBox = pxMesh->BoundingBox;
			xBox.Displace(Asura_Module_System::GetPositionOfModule(iModuleBelow));

			if(xBox.Contains(xPosition))
			{
				iModule = iModuleBelow;
			}
		}
	}


	if(iModule == -1)
	{
		//if there was no module below , or the object wasn't within it , check
		//to see if there are any other modules that the position is within
		iModule = Asura_Module_System::GetModuleIndexFromPosition(xPosition,-1);

		if(iModule != -1 && !DoesModuleHaveAnyRenderedPolygons(iModule))
		{
			iModule = -1;//avoid picking collision modules
		}
	}
	
	if(iModule == -1)
	{
		//not in any module.... 
		//If there was a module below , go with that after all
		if(iModuleBelow != -1)
		{
			iModule = iModuleBelow;
		}
		else
		{
			//try to find a nearby non-scenery module instead
			float fNearDist = 10.0f;

			//Do 2 passes... first time through ignoring any modules
			//that appear to be entirely collision mesh
			for(int iPass = 0;iPass<2 && iModule==-1;iPass++)
			{
				for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
				{
					if(Asura_Environment_System::GetEnvironment()->IsSceneryModule(i)) continue;

					Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(i);
					if(!pxMesh->BoundingBoxDataValid) pxMesh->CalculateBoundingBox();

					Asura_Bounding_Box xBox = pxMesh->BoundingBox;
					xBox.Displace(Asura_Module_System::GetPositionOfModule(i));

					float fDist = 0;
					if(xPosition.x>xBox.MaxX) fDist += xPosition.x-xBox.MaxX;
					if(xPosition.y>xBox.MaxY) fDist += xPosition.y-xBox.MaxY;
					if(xPosition.z>xBox.MaxZ) fDist += xPosition.z-xBox.MaxZ;
					if(xPosition.x<xBox.MinX) fDist -= xPosition.x-xBox.MinX;
					if(xPosition.y<xBox.MinY) fDist -= xPosition.y-xBox.MinY;
					if(xPosition.z<xBox.MinZ) fDist -= xPosition.z-xBox.MinZ;

					if(iPass==0 && !DoesModuleHaveAnyRenderedPolygons(i))
					{
						//appears to be a collision module , so skip
						continue;
					}

					if(fDist<fNearDist)
					{
						fNearDist = fDist;
						iModule = i;
					}

				}
			}

		}
	}

	return iModule;
	
}


void Tools_Mode_MeshEdit::MergeSelectedModules()
{
	//get a list of completely selected modules
	List<int> xModuleList;

	for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
	{
		if(Tools_Polygon_Selection::IsModuleCompletelySelected(i))
		{
			xModuleList.add_entry(i);
		}
	}

	if(xModuleList.size()<2)
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"Completely select 2 or more modules to be merged first");
		return;
	}

	//confirm merge
	if(!s_bSuppressConfirmation)
	{
		char szMessage[200];
		Asura_PrintfToBfr(szMessage,"Merge the %d selected modules together?",200,true,xModuleList.size());

		YesNo_Overlay xOverlay(szMessage);
		if(!xOverlay.DoModal()) return;
	}


	//Remove the first module from the list , this will be the one everything gets merged to
	int iMainModule = xModuleList.first_entry();
	xModuleList.delete_first_entry();

	Asura_Mesh* pxMainModule = Asura_Module_System::GetMeshForModule(iMainModule);

	int iFailedModules = 0;

	//merge the polygons of all the other modules to the first one
	for(LIF<int> xModuleLIF(&xModuleList);!xModuleLIF.done();)
	{
		int iIndex = xModuleLIF();
		Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iIndex);
		
		Asura_Vector_3 xPosition;
		Asura_Module_System::GetPositionOfModule(iIndex,xPosition);
		
		if(MergeObjectIntoModule(iMainModule,pxMesh,Asura_Maths::IdentityMatrix3x3,xPosition))
		{
			xModuleLIF.next();
		}
		else
		{
			xModuleLIF.delete_current();
			iFailedModules++;
		}

	}

	//select all the polygons in the new merged super module
	Tools_Polygon_Selection::SelectObject(iMainModule);

	//now delete all the modules that have just been merged
	//(in reverse order ,to avoid index numbers changing)
	for(LIB<int> xModuleLIB(&xModuleList);!xModuleLIB.done();xModuleLIB.next())
	{
		DeleteModule(xModuleLIB());
	}


	//undo for selection will no longer be valid
	Tools_Polygon_Selection::ClearUndo();
	Tools_Vertex_Selection::ClearUndo();
	Tools_Adjacency::Invalidate();

	#if defined( ASURA_USE_CARPET)
	//the carpet data refers to polygon/vertex indeces for lighting.
	Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
	#endif



	Tools_Timed_Message_System::AddMessage(0xffffff,"Modules merged into %s",pxMainModule->Name.GetString());

	if(iFailedModules)
	{
		ShowMessageOverlay("Failed to merge %d module(s) , because it would have resulted in a module with too many polygons or vertices.",iFailedModules);
	}

}

void Tools_Mode_MeshEdit::TurnEdge()
{
	Tools_Prompted_Selection::ChooseEdge("Turn Edge:\nClick on polygon edge to be turned.\nThe edge must be between two triangles.",TurnEdge_EdgeSelected, true);
}


void Tools_Mode_MeshEdit::TurnEdge_EdgeSelected(Tools_LOS_EdgeOutput xEdge)
{
	if(xEdge.m_bDiagonalInQuad)
		TurnEdgeWithinQuad(xEdge);
	else
		TurnEdgeBetweenPolys(xEdge);
}

void Tools_Mode_MeshEdit::TurnEdgeBetweenPolys(const Tools_LOS_EdgeOutput& xEdge)
{
	//Edge needs to be between polygons in the same module
	int iModule1 = xEdge.m_axEdge[0].m_iModuleIndex;
	int iModule2 = xEdge.m_axEdge[1].m_iModuleIndex;
	if(iModule1 != iModule2)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"The edge to be turned must be between two triangles in the same mesh");
		return;
	}


	//get the mesh
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModule1);

	int iPoly1 = xEdge.m_axEdge[0].m_iPolygonIndex;
	int iPoly2 = xEdge.m_axEdge[1].m_iPolygonIndex;

	Asura_Mesh_Polygon* pxPoly1 = (iPoly1>=0) ? &pxMesh->PolygonDataPtr[iPoly1] : 0;
	Asura_Mesh_Polygon* pxPoly2 = (iPoly2>=0) ? &pxMesh->PolygonDataPtr[iPoly2] : 0;

	//The edge needs to between 2 triangles
	if(!pxPoly1 || !pxPoly2 || pxPoly1->NumberOfVertices()!=3 || pxPoly2->NumberOfVertices()!=3)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"The edge to be turned must be within a quad or between two triangles in the same mesh");
		return;
	}

	//find the index of the unshared vertex in each polygon
	int iUnshared1 = -1;
	int iUnshared2 = -1;

	const int* piEdgeVert = &xEdge.m_axEdge[0].m_aiVertexIndex[0];

	for(int i=0;i<3;i++)
	{
		if(pxPoly1->VertexID[i] != piEdgeVert[0] && pxPoly1->VertexID[i] != piEdgeVert[1])
		{
			Asura_Assert(iUnshared1 == -1,"Found more than 1 unshared vertex?");
			iUnshared1 = i;
		}
		if(pxPoly2->VertexID[i] != piEdgeVert[0] && pxPoly2->VertexID[i] != piEdgeVert[1])
		{
			Asura_Assert(iUnshared2 == -1,"Found more than 1 unshared vertex?");
			iUnshared2 = i;
		}
	}
	Asura_Assert(iUnshared1!=-1 && iUnshared1!=-1,"Failed to find unshared vertices in the triangles");

	//Get the index of each triangle that will be changed (take the vertex just before the unshared one in each case)
	int iAlterVert1 = (iUnshared1 + 2)%3;
	int iAlterVert2 = (iUnshared2 + 2)%3;

	//Update the Polygon data
	pxPoly1->VertexID[iAlterVert1] = pxPoly2->VertexID[iUnshared2];
	pxPoly2->VertexID[iAlterVert2] = pxPoly1->VertexID[iUnshared1];

	//Update texturing
	if(pxMesh->TextureDataValid)
	{
		Asura_Mesh_TextureInfo* pxTex1 = &pxMesh->TextureDataPtr[iPoly1];
		Asura_Mesh_TextureInfo* pxTex2 = &pxMesh->TextureDataPtr[iPoly2];

		for(int i=0;i<ASURA_MATERIAL_MAXTEXTURES;i++)
		{
			pxTex1->U[i][iAlterVert1] = pxTex2->U[i][iUnshared2];
			pxTex1->V[i][iAlterVert1] = pxTex2->V[i][iUnshared2];

			pxTex2->U[i][iAlterVert2] = pxTex1->U[i][iUnshared1];
			pxTex2->V[i][iAlterVert2] = pxTex1->V[i][iUnshared1];
		}

		pxTex1->m_afAlpha[iAlterVert1] = pxTex2->m_afAlpha[iUnshared2];
		pxTex2->m_afAlpha[iAlterVert2] = pxTex1->m_afAlpha[iUnshared1];

	}
	pxMesh->DeleteProcessedTextureInfo();


	//Update painted colour
	if ( pxMesh->m_bPaintedColourDataValid )
	{
		Asura_Mesh_PaintedColours* pxPaintedColour1 = &pxMesh->m_pxPaintedColourDataPtr[ iPoly1 ];
		Asura_Mesh_PaintedColours* pxPaintedColour2 = &pxMesh->m_pxPaintedColourDataPtr[ iPoly2 ];

		pxPaintedColour1->Colour[ iAlterVert1 ] = pxPaintedColour2->Colour[ iUnshared2 ];
		pxPaintedColour2->Colour[ iAlterVert2 ] = pxPaintedColour1->Colour[ iUnshared1 ];

	}

	//update normals.
	if(pxMesh->m_bVertexNormalsValid)
	{
		pxMesh->NormalsDataPtr[iPoly1].VertexNormal[iAlterVert1] = pxMesh->NormalsDataPtr[iPoly2].VertexNormal[iUnshared2];
		pxMesh->NormalsDataPtr[iPoly2].VertexNormal[iAlterVert2] = pxMesh->NormalsDataPtr[iPoly1].VertexNormal[iUnshared1];
	}

	//adjacencies will no longer be valid
	Tools_Adjacency::InvalidateModule(iModule1);
	Tools_SaveAsura::SetMeshModified(pxMesh);

	InvalidateModuleCollisionMesh(iModule1);
	Asura_Mesh_System::PlatformResetRenderMesh(pxMesh);
	Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);
}
void Tools_Mode_MeshEdit::TurnEdgeWithinQuad(const Tools_LOS_EdgeOutput& xEdge)
{
	Asura_Assert(xEdge.m_bDiagonalInQuad, "");

	//get the mesh
	int iModule = xEdge.m_axEdge[0].m_iModuleIndex;
	Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iModule);

	int iPoly = xEdge.m_axEdge[0].m_iPolygonIndex;

	Asura_Mesh_Polygon* pxPoly = (iPoly>=0) ? &pxMesh->PolygonDataPtr[iPoly] : 0;

	//Make sure this is a quad
	if(!pxPoly || pxPoly->NumberOfVertices()!=4)
	{
		Asura_Assert(0,"Expecting to have a quad for use with turn edge if got to here.");			
		return;
	}

	//Turning the edge within a quad involves cycling the vertex order.

	//Update the Polygon data
	for(int i=0; i<3; i++)
	{
		Asura_Maths::Swap(pxPoly->VertexID[i], pxPoly->VertexID[i+1]);
	}

	//Update texturing
	if(pxMesh->TextureDataValid)
	{
		Asura_Mesh_TextureInfo* pxTex = &pxMesh->TextureDataPtr[iPoly];

		for(int iChan=0;iChan<ASURA_MATERIAL_MAXTEXTURES;iChan++)
		{
			for(int i=0; i<3; i++)
			{
				Asura_Maths::Swap(pxTex->U[iChan][i], pxTex->U[iChan][i+1]);
				Asura_Maths::Swap(pxTex->V[iChan][i], pxTex->V[iChan][i+1]);
			}
		}

		for(int i=0; i<3; i++)
		{
			Asura_Maths::Swap(pxTex->m_afAlpha[i], pxTex->m_afAlpha[i+1]);
		}
	}
	pxMesh->DeleteProcessedTextureInfo();


	//Update painted colour
	if ( pxMesh->m_bPaintedColourDataValid )
	{
		Asura_Mesh_PaintedColours* pxPaintedColour = &pxMesh->m_pxPaintedColourDataPtr[ iPoly ];
		for(int i=0; i<3; i++)
		{
			Asura_Maths::Swap(pxPaintedColour->Colour[i], pxPaintedColour->Colour[i+1]);
		}

	}

	//update normals.
	if(pxMesh->m_bVertexNormalsValid)
	{
		for(int i=0; i<3; i++)
		{
			Asura_Maths::Swap(pxMesh->NormalsDataPtr[iPoly].VertexNormal[i], pxMesh->NormalsDataPtr[iPoly].VertexNormal[i+1]);
		}
	}

	//adjacencies will no longer be valid
	Tools_Adjacency::InvalidateModule(iModule);
	Tools_SaveAsura::SetMeshModified(pxMesh);

	InvalidateModuleCollisionMesh(iModule);
	Asura_Mesh_System::PlatformResetRenderMesh(pxMesh);
	Tools_RenderModule_System::UpdateModuleMesh(pxMesh->Index);
}


void Tools_Mode_MeshEdit::InvalidateModuleCollisionMesh(int iModuleIndex)
{
	Asura_Module_System::InvalidateCollisionMesh(iModuleIndex);
	Tools_RenderCollisionMesh_System::InvalidateModule(iModuleIndex);
}

// DLH - 09 May 2006 13:04:22 - Does not work :(
#if 0
bool Tools_Mode_MeshEdit::DeleteDegenerateTriangles(Asura_Mesh* pxMesh)
{
	const u_int uOldPolygonCount = pxMesh->NumberOfPolygons;
	bool* pbDeletePolygonArray = new bool[ uOldPolygonCount];
	
	for( u_int uPolygonIndex = 0; uPolygonIndex < uOldPolygonCount; uPolygonIndex += 1)
	{
		bool& bDeletePolygon = pbDeletePolygonArray[ uPolygonIndex];
		
		bDeletePolygon = false;
		
		Asura_Mesh_Polygon& xPolygonData = pxMesh->PolygonDataPtr[ uPolygonIndex];
		
		if( 
			xPolygonData.VertexID[ 0] != xPolygonData.VertexID[ 1]
		&&	xPolygonData.VertexID[ 0] != xPolygonData.VertexID[ 2]
		&&	xPolygonData.VertexID[ 1] != xPolygonData.VertexID[ 2]
		&&	( 
				false == xPolygonData.IsQuad() 
			||
				(
						xPolygonData.VertexID[ 0] != xPolygonData.VertexID[ 3]
					&&	xPolygonData.VertexID[ 1] != xPolygonData.VertexID[ 3]
					&&	xPolygonData.VertexID[ 2] != xPolygonData.VertexID[ 3]
				)
			)
		)
		{
			continue;
		}
		
		bDeletePolygon = true;
	}
	
	int* piPolyConv = 0;
	
	DeletePolygons( pxMesh, pbDeletePolygonArray, &piPolyConv);
	
	delete[] piPolyConv;
	delete[] pbDeletePolygonArray;
	
	return( false);
}
#endif

static Asura_Vector_3 g_xCreateVertexPosition;

void Tools_Mode_MeshEdit::CreateVertex()
{
	g_xCreateVertexPosition = *Asura_Camera::GetPosition();
	Tools_Prompted_Selection::ChooseModule("Create vertex at camera.\nSelect a module.",CreateVertex_ModuleSelected);
}

void Tools_Mode_MeshEdit::CreateVertex_ModuleSelected(int iModuleIndex)
{
	if(!AddVerticesToModule(iModuleIndex,1,&g_xCreateVertexPosition))
	{
		ShowMessageOverlay("Failed to create vertex. The mesh already has as many as allowed.");
	}
}

void Tools_Mode_MeshEdit::DeleteSelectedVertices()
{
	if(!Tools_Vertex_Selection::GetNumberOfSelectedVertices())
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"No vertices selected");
	}


	//confirm deletion
	if(!s_bSuppressConfirmation)
	{
		if(!ShowYesNoOverlay("Delete the selected vertices?\nVertices that are still used by polygons won't be affected"))return;
	}


	int iCurrentModule = -1;
	int iDeleted = 0;
	bool* pbDeleteArray = 0;
	
	ModuleVertexPair xVert;
	Tools_Vertex_Selection::StartIteration();
	while(Tools_Vertex_Selection::GetNextSelected(&xVert))
	{
		//moved onto a new module?
		if(iCurrentModule != xVert.iModuleIndex)
		{
			if(pbDeleteArray)
			{
				iDeleted += DeleteVerticesFromModule(iCurrentModule,pbDeleteArray);
				delete [] pbDeleteArray;
			}

			//initialse new deletion array
			iCurrentModule = xVert.iModuleIndex;
			Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(iCurrentModule);

			pbDeleteArray = new bool[pxMesh->NumberOfVertices];
			Asura_Mem::Set(pbDeleteArray,0,sizeof(bool)*pxMesh->NumberOfVertices);
		}

		//mark vert for deletion
		pbDeleteArray[xVert.iVertexIndex] = true;
	}

	//delete verts from final module
	if(pbDeleteArray)
	{
		iDeleted += DeleteVerticesFromModule(iCurrentModule,pbDeleteArray);
		delete [] pbDeleteArray;
	}

	Tools_Timed_Message_System::AddMessage(0xffffff,"%d vertices deleted",iDeleted);
}

void Tools_Mode_MeshEdit::HandleMeshChange(int iModuleIndex)
{
	//delete vertex movement objects to be safe
	Tools_VertexMove_Object::DeleteAll();
}
