function XSILoadPlugin( in_reg ){
	in_reg.Author = "Helge Mathee";
	in_reg.Name = "mt_feathers";
	in_reg.Major = 1;
	in_reg.Minor = 0 ;
	in_reg.RegisterCommand( "mt_createFeatherCloud", "mt_createFeatherCloud" );
	in_reg.RegisterCommand( "mt_addFeathersToCloud", "mt_addFeathersToCloud" );
	in_reg.RegisterCommand( "mt_createCloudClusters", "mt_createCloudClusters" );
	in_reg.RegisterCommand( "mt_createFeatherNull", "mt_createFeatherNull");
	in_reg.RegisterCommand( "mt_createFeatherMesh", "mt_createFeatherMesh");
	LogMessage( in_reg.Name + " has been loaded." );

	return true;
}
function XSIUnloadPlugin( in_reg ){
	Logmessage( in_reg.name + " has been unloaded." );
	return true;
}

function mt_createFeatherCloud_Init( ctxt ){
	ctxt.Source.Description = "Creates an empty feather pointcloud.";
	ctxt.Source.ReturnValue = true;
	ctxt.Source.Arguments.Add("cloudName",siArgumentInput,"featherCloud");
	return true;
}

function mt_createFeatherCloud_Execute(cloudName)
{
	var points = [0,0,0,0,0,0,0,0,0];
	var polies = [3,0,0,0];
	var mesh = ActiveSceneRoot.AddPolygonMesh(points,polies);
	mesh.name = cloudName;
	MakeLocal(mesh+".display", siNodePropagation);
	SetValue(mesh+".display.wirecol", 367);
	return mesh;
}

function mt_addFeathersToCloud_Init( ctxt ){
	ctxt.Source.Description = "Adds a list of feathers to a feather point cloud.";
	ctxt.Source.ReturnValue = true;
	ctxt.Source.Arguments.AddObjectArgument("cloud");
	ctxt.Source.Arguments.AddWithHandler("feathers",siArgHandlerCollection);
	ctxt.Source.Arguments.Add("cnsScale",siArgumentInput,1);
	ctxt.Source.Arguments.Add("upvScale",siArgumentInput,1);
	return true;
}

function mt_addFeathersToCloud_Execute(cloud,feathers,cnsScale,upvScale)
{
	for(var i=0;i<feathers.count;i++)
	{
		var op = XSIFactory.CreateScriptedOp( "MT_addFeather", MT_addFeather_Update.toString(),"JScript" );
		var pdef = XSIFactory.CreateParamDef("cnsScale", siDouble, 0, siAnimatable, "", "", cnsScale, 0.01, 10,0.01,10);
  	op.AddParameter( pdef );
		var pdef = XSIFactory.CreateParamDef("upvScale", siDouble, 0, siAnimatable, "", "", upvScale, 0.01, 10,0.01,10);
  	op.AddParameter( pdef );

		op.AddOutputPort( cloud.ActivePrimitive, "out");
		op.AddInputPort( cloud.ActivePrimitive, "in_mesh");
		op.AddInputPort( feathers(i).kinematics.global, "in_xf" );
		op.Connect( );
	}		
	return true;
}

function MT_addFeather_Update( ctx, out, in_mesh, in_xf )
{
	var geo = in_mesh.value.Geometry;
	var points = geo.points.positionarray.toArray();
	if(points.length<12)
		points = [];

	var xf = in_xf.value.transform;
	
	var pos = XSIMath.CreateVector3();
	var cns = XSIMath.CreateVector3();
	var upv = XSIMath.CreateVector3();
	var bnd = XSIMath.CreateVector3();
	cns.z = ctx.parameters("cnsScale").value;
	upv.y = ctx.parameters("upvScale").value;
	bnd.z = 1;
	bnd.y = upv.y;
	pos = XSIMath.MapObjectPositionToWorldSpace(xf,pos);
	cns = XSIMath.MapObjectPositionToWorldSpace(xf,cns);
	upv = XSIMath.MapObjectPositionToWorldSpace(xf,upv);
	bnd = XSIMath.MapObjectPositionToWorldSpace(xf,bnd);
		
	points.push(pos.x,pos.y,pos.z);
	points.push(cns.x,cns.y,cns.z);
	points.push(upv.x,upv.y,upv.z);
	points.push(bnd.x,bnd.y,bnd.z);
	
	var polies = [];
	for(var i=0;i<points.length/3;i+=4)
	{
		polies.push(4);
		polies.push(i+1);
		polies.push(i);
		polies.push(i+2);
		polies.push(i+3);
	}
	
	out.value.geometry.Set(points,polies);
}

function mt_createFeatherNull_Init(ctxt)
{
	ctxt.Source.Description = "Creates an empty feather null.";
	ctxt.Source.ReturnValue = true;
	ctxt.Source.Arguments.Add("featherName",siArgumentInput,"featherNull");
	return true;
}

function mt_createFeatherNull_Execute(featherName)
{
	var feather = GetPrim("Null", featherName, null, null);
	SetValue(feather+".null.primary_icon", 0, null);
	SetValue(feather+".null.shadow_icon", 10, null);
	SetValue(feather+".null.shadow_offsetZ", 1, null);
	MakeLocal(feather+".Display");
	SetValue(feather+".display.wirecol", 47, null);
	return feather;
}

function mt_createCloudClusters_Init( ctxt ){
	ctxt.Source.Description = "Adds the pos, cns, upv and bnd clusters to a feather cloud.";
	ctxt.Source.ReturnValue = true;
	ctxt.Source.Arguments.AddObjectArgument("cloud");
	return true;
}

function mt_createCloudClusters_Execute(cloud)
{
	var geo = cloud.ActivePrimitive.Geometry;
	var pos = [];
	var cns = [];
	var upv = [];
	var bnd = [];
	
	for(var i=0;i<geo.points.count;i+=4)
	{
		pos.push(i);
		cns.push(i+1);
		upv.push(i+2);
		bnd.push(i+3);
	}

	var coll = new ActiveXObject("XSI.Collection");
	coll.Add(geo.AddCluster(siVertexCluster,"pos",pos));
	coll.Add(geo.AddCluster(siVertexCluster,"cns",cns));
	coll.Add(geo.AddCluster(siVertexCluster,"upv",upv));
	coll.Add(geo.AddCluster(siVertexCluster,"bnd",bnd));
	
	return coll;
}

function mt_createFeatherMesh_Init( ctxt ){
	ctxt.Source.Description = "Creates a feather mesh out of two feathers and a base mesh.";
	ctxt.Source.ReturnValue = true;
	ctxt.Source.Arguments.AddObjectArgument("cloud");
	ctxt.Source.Arguments.AddObjectArgument("feather");
	ctxt.Source.Arguments.Add("meshName",siArgumentInput,"feathers");
	return true;
}

function mt_createFeatherMesh_Execute( cloud, feather, meshName ){
	
	var mesh = mt_createFeatherCloud_Execute(meshName);
	SetValue(mesh+".display.wirecol", 656);
	var op = XSIFactory.CreateScriptedOp( "MT_feathers", MT_feathers_Update.toString(),"JScript" );
	var pdef = XSIFactory.CreateParamDef("zScale", siDouble, 0, siAnimatable, "", "", 3, 0.01, 100,0.01,10);
	op.AddParameter( pdef );
	var pdef = XSIFactory.CreateParamDef("bndScale", siDouble, 0, siAnimatable, "", "", 1, -100, 100,0.01,10);
	op.AddParameter( pdef );
	op.AddOutputPort( mesh.ActivePrimitive, "out");
	op.AddInputPort( feather.ActivePrimitive, "in_feather" );
	op.AddInputPort( cloud.ActivePrimitive, "in_cloud" );
	op.Connect( );
	return mesh;
	
}

function MT_feathers_Update( ctx, out, in_feather, in_cloud )
{
	var geoA = in_feather.value.geometry;
	var cloud = in_cloud.value.geometry;
	var zScale = ctx.parameters("zScale").value;
	var bndScale = ctx.parameters("bndScale").value;

	var valuesA = geoA.Get2().toArray();
	var posA = valuesA[0].toArray();
	var poliesA = valuesA[1].toArray();

	var points = [];
	var polies = [];
	var offset = 0;
	
	var instanceCount = cloud.points.count / 4;
	for(var i=0;i<instanceCount;i++)
	{
		var xf = XSIMath.CreateTransform();
		var x = XSIMath.CreateVector3();
		var y = XSIMath.CreateVector3();
		var z = XSIMath.CreateVector3();
		var bnd = XSIMath.CreateVector3();
		z.Sub(cloud.points(i*4+1).position,cloud.points(i*4).position);
		z.NormalizeInPlace();
		y.Sub(cloud.points(i*4+2).position,cloud.points(i*4).position);
		x.Cross(y,z);
		y.Cross(z,x);
		y.NormalizeInPlace();
		x.NormalizeInPlace();
		var m = XSIMath.CreateMatrix3();
		m.Set(x.x,x.y,x.z,y.x,y.y,y.z,z.x,z.y,z.z);
		xf.SetRotationFromMatrix3(m);
		xf.SetTranslation(cloud.points(i*4).position);
		
		bnd.Sub(cloud.points(i*4+3).position,cloud.points(i*4+2).position);
		bnd = XSIMath.MapWorldOrientationToObjectSpace(xf,bnd);

		for(var j=0;j<geoA.points.count;j++)
		{
			var newPos = geoA.points(j).position;
			var zPos = newPos.z;
			if(newPos.z > 0)
			{
				newPos.x += bndScale * bnd.x * (1-Math.cos(zPos/zScale));
				newPos.y += bndScale * bnd.y * (1-Math.cos(zPos/zScale));
			}
			newPos = XSIMath.MapObjectPositionToWorldSpace(xf,newPos);
			points.push(newPos.x,newPos.y,newPos.z);
		}

		var pointCount = 0;
		for(var j=0;j<poliesA.length;j++)
		{
			if(pointCount==0)
			{
				pointCount = poliesA[j];
				polies.push(pointCount);
			}
			else
			{
				pointCount--;
				polies.push(poliesA[j]+offset);
			}
		}

		offset += posA.length/3;
	}

out.value.geometry.set(points,polies);

}