
////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002/02/20 Yosiyuki Nisino
// File: CreateFur.cpp
// MEL File: CreateFur.mel
// MEL Command: CreateFur
// /////////////////////////////////////////////////////////////////////

#include <maya/MFnDagNode.h>
#include <maya/MItDag.h>
#include <maya/MPxCommand.h>
#include <maya/MStatus.h>
#include <maya/MString.h>
#include <maya/MArgList.h>
#include <maya/MFnCamera.h>
#include <maya/MMatrix.h>
#include <maya/MTransformationMatrix.h>
#include <maya/MFnLight.h>
#include <maya/MColor.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MDagPath.h>
#include <maya/MDrawData.h>
#include <maya/MDrawRequest.h>
#include <maya/MFnCamera.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>
#include <maya/MFnNurbsSurface.h>
#include <maya/MFnNurbsSurfaceData.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnPlugin.h>
#include <maya/MGlobal.h>
#include <maya/MObject.h>
#include <maya/MItSelectionList.h>
#include <maya/MMaterial.h>
#include <maya/MPoint.h>
#include <maya/MFloatPointArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MPointArray.h>
#include <maya/MPlug.h>
#include <maya/MPxNode.h>
#include <maya/MPxSurfaceShape.h>
#include <maya/MPxSurfaceShapeUI.h>
#include <maya/MSelectionList.h>
#include <maya/MSelectionMask.h>
#include <maya/MTime.h>
#include <maya/MTypeId.h>
#include <maya/MVector.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include <iostream.h>
#include <string.h>
#include <math.h>

#define ERR( s, r )	{ if( !s ) return r; }
#define MCHECKERROR(STAT,MSG)       \
    if ( MS::kSuccess != STAT ) {   \
        cerr << MSG << endl;        \
        return MS::kFailure;        \
    }

#define MAKE_NUMERIC_ATTR( NAME, SHORTNAME, TYPE, DEFAULT, KEYABLE ) 	\
	MStatus NAME##_stat;                                            \
	MFnNumericAttribute NAME##_fn;                                  \
	NAME = NAME##_fn.create( #NAME, SHORTNAME, TYPE, DEFAULT );     \
	MCHECKERROR(NAME##_stat, "numeric attr create error");		\
	NAME##_fn.setHidden( !KEYABLE );				\
	NAME##_fn.setKeyable( KEYABLE );			        \
	NAME##_fn.setInternal( true );				        \
	NAME##_stat = addAttribute( NAME );                             \
	MCHECKERROR(NAME##_stat, "addAttribute error");

#define DORMANT_COLOR			4	// blue
#define ACTIVE_AFFECTED_COLOR		8	// purple
#define ACTIVE_COLOR			15	// white
#define HILITE_COLOR			17	// pale blue
#define LEAD_COLOR			18	// green

typedef struct _Vector
{
	float x;
	float y;
	float z;
}Vector;

typedef struct _Point
{
	float x;
	float y;
	float z;
}Point;

int flag;
int cnt1=0;
int cnt2=0;
short U_samples = 32;
short V_samples = 32;
double baseWidth = 0.05;
double tipWidth = 0.03;
MVector viewDirection, upDirection;
Vector *normal;
Point *start1, *end1;
Point *start2, *end2;
Point *A, *B, *C, *D;

class furFeedbackGeom 
{
public:
	short Usamples;
	short Vsamples;
};

class furFeedbackShape : public MPxSurfaceShape
{
public:
		 furFeedbackShape();
	virtual ~furFeedbackShape(); 

	virtual void			postConstructor();
	virtual MStatus			compute( const MPlug&, MDataBlock& );
        virtual bool			getInternalValue( const MPlug&, MDataHandle& );
        virtual bool			setInternalValue( const MPlug&, const MDataHandle& );
					  
	virtual bool            isBounded() const;
	virtual MBoundingBox    boundingBox() const;

	static  void *		creator();
	static  MStatus		initialize();
	furFeedbackGeom*	geometry();

private:
	furFeedbackGeom*	fGeometry;

	static	MObject		Usamples;
	static	MObject		Vsamples;
 
public:
	static	MTypeId		id;
	static 	MObject		inputSurface;
	static  MObject		outputFur;
};

class furFeedbackShapeUI : public MPxSurfaceShapeUI
{
public:
		 furFeedbackShapeUI();
	virtual ~furFeedbackShapeUI(); 

	virtual void	getDrawRequests( const MDrawInfo & info,
									 bool objectAndActiveOnly,
									 MDrawRequestQueue & requests );
	virtual void	draw( const MDrawRequest & request,
						  M3dView & view ) const;
	virtual bool	select( MSelectInfo &selectInfo,
							MSelectionList &selectionList,
							MPointArray &worldSpaceSelectPts ) const;

	void		getDrawRequestsWireframe( MDrawRequest&,
											  const MDrawInfo& );
	void		getDrawRequestsShaded(	  MDrawRequest&,
											  const MDrawInfo&,
											  MDrawRequestQueue&,
											  MDrawData& data );

	static  void *  creator();

private:
	enum 
	{
		kDrawfurFeedback,
		kLastToken
	};
};

MObject furFeedbackShape::Usamples;
MObject furFeedbackShape::Vsamples;
MTypeId furFeedbackShape::id( 0x77777 );
MObject furFeedbackShape::inputSurface;
MObject furFeedbackShape::outputFur;

furFeedbackShape::furFeedbackShape()
{
	fGeometry = new furFeedbackGeom;
	fGeometry->Usamples = 32;
	fGeometry->Vsamples = 32;
}

furFeedbackShape::~furFeedbackShape()
{
	delete fGeometry;
}

void furFeedbackShape::postConstructor()
{ 
	setRenderable( false );
}

MStatus furFeedbackShape::compute( const MPlug& plug, MDataBlock& data )
{ 
	cout << "In furFeedbackShape::compute" << endl;
	MStatus status;
	
	if( plug == outputFur )
	{
		cnt1=0;
		MDataHandle inputData = data.inputValue( inputSurface, &status );

		if( status != MS::kSuccess )
		{
			cerr << "ERROR getting data" << endl;
		}
		else 
		{
			MObject surfaceinfo = inputData.asNurbsSurfaceTransformed();
			MFnNurbsSurface fnNurbs( surfaceinfo, &status );
			
			if( status != MS::kSuccess )
			{
				cerr << "ERROR creating nurbs function set" << endl;
			}
			else 
			{
				int U_span, V_span;
				double i, j;
				MPoint pnt;
				MPointArray cvArray1, cvArray2;
				MVector normal;
				ERR( status, status );

				U_span = fnNurbs.numSpansInU();
				V_span = fnNurbs.numSpansInV();

				start1 = new Point [10000];
				end1 = new Point [10000];
				start2 = new Point [100000];
				end2 = new Point [100000];
				
				for( i=0.0; i <= U_span; i+=(double)U_span / U_samples )
				{
					for( j=0.0; j <= V_span; j+=(double)V_span / V_samples )
			       		{
						normal = fnNurbs.normal( i, j );
						ERR( fnNurbs.getPointAtParam( i, j, pnt ), MS::kFailure );
						cvArray1.append( pnt );
						cvArray1.append( pnt + normal );
			
						start1[cnt1].x = cvArray1[0][0];
						start1[cnt1].y = cvArray1[0][1];
						start1[cnt1].z = cvArray1[0][2];
						end1[cnt1].x = cvArray1[1][0];
						end1[cnt1].y = cvArray1[1][1];
						end1[cnt1].z = cvArray1[1][2];

						cvArray1.clear();
						cnt1++;
					}
				}

				for( i=0.0; i <= U_span; i+=(double)U_span / 100 )
				{
					for( j=0.0; j <=  V_span; j+=(double)V_span / 100 )
					{
						normal = fnNurbs.normal( i, j );
						ERR( fnNurbs.getPointAtParam( i, j, pnt ), MS::kFailure );
						cvArray2.append( pnt );
						cvArray2.append( pnt + normal );
			
						start2[cnt2].x = cvArray2[0][0];
						start2[cnt2].y = cvArray2[0][1];
						start2[cnt2].z = cvArray2[0][2];
						end2[cnt2].x = cvArray2[1][0];
						end2[cnt2].y = cvArray2[1][1];
						end2[cnt2].z = cvArray2[1][2];

						cvArray2.clear();
						cnt2++;
					}
				}
				
				short result1 = U_samples;
				short result2 = V_samples;
				MDataHandle nurbsHandle = data.outputValue( furFeedbackShape::outputFur );
				nurbsHandle.set( result1, result2 );
				data.setClean(plug);
			}
		}
	}
	else 
	{
		return MS::kUnknownParameter;
	}

	return MS::kSuccess;
}

bool furFeedbackShape::getInternalValue( const MPlug& plug, MDataHandle& datahandle )
{
	cout << "In furFeedbackShape::getInternalValue" << endl;
	bool isOk = true;
	MObject this_object = thisMObject();

	if ( plug == Usamples ) 
	{
		datahandle.set( fGeometry->Usamples );
		isOk = true;
	}
	else if ( plug == Vsamples ) 
	{
		datahandle.set( fGeometry->Vsamples );
		isOk = true;
	}
	else 
	{
		isOk = MPxSurfaceShape::getInternalValue( plug, datahandle );
	}

	return isOk;
}
bool furFeedbackShape::setInternalValue( const MPlug& plug, const MDataHandle& datahandle )
{
	cout << "In furFeedbackShape::setInternalValue" << endl;
	bool isOk = true;

	if ( plug == Usamples ) 
	{
		short usamples = datahandle.asShort();
		U_samples = usamples;
		cout << "U_samples" << U_samples << endl;

		if ( usamples < 0 ) 
		{
			usamples = 1;
		}
		else if( usamples > 100 )
		{
			usamples = 100;
		}
		
		fGeometry->Usamples = usamples;
		isOk = true;
	}
	else if ( plug == Vsamples ) 
	{
		short vsamples = datahandle.asShort();
		V_samples = vsamples;
		cout << "V_samples" << V_samples << endl;

		if ( vsamples < 0 ) 
		{
			vsamples = 1;
		}
		else if( vsamples > 100 )
		{
			vsamples = 100;
		}

		fGeometry->Vsamples = vsamples;
		isOk = true;
	}
	else 
	{
		isOk = MPxSurfaceShape::setInternalValue( plug, datahandle );
	}

	return isOk;
}

bool furFeedbackShape::isBounded() const { return true; }

MBoundingBox furFeedbackShape::boundingBox() const
{
	MBoundingBox result;	
	furFeedbackShape* nonConstThis = const_cast <furFeedbackShape*> (this);
	furFeedbackGeom* geom = nonConstThis->geometry();

	double r = 1.0;
	result.expand( MPoint(r,r,r) );	result.expand( MPoint(-r,-r,-r) );

    return result;
}

void* furFeedbackShape::creator()
{
	return new furFeedbackShape();
}

MStatus furFeedbackShape::initialize()
{
	cout << "In furFeedbackShape::initialize" << endl;
	MStatus			status;
   	MFnNumericAttribute	numericAttr;
	MFnTypedAttribute 	typedAttr;
	
	inputSurface = typedAttr.create( "inputSurface", "input", MFnData::kNurbsSurface, &status );

	if( status != MS::kSuccess )
	{
		cerr << "ERROR creating inputSurface attribute" << endl;
		return status;
	}

	outputFur = numericAttr.create( "outputFur", "output", MFnNumericData::k2Short, 0, &status );

	if( status != MS::kSuccess )
	{
		cerr << "ERROR creating outputFur attribute" << endl;
		return status;
	}
	numericAttr.setWritable(false);

	status = addAttribute( inputSurface );
	if( status != MS::kSuccess )
	{
		cerr << "addAttribute(inputSurface)" << endl;
		return status;
	}

	status = addAttribute( outputFur );
	if( status != MS::kSuccess )
	{
		cerr << "addAttribute(outputFur)" << endl;
		return status;
	}

	status = attributeAffects( inputSurface, outputFur );
	if( status != MS::kSuccess )
	{
		cerr << "attributeAffects(inputSurface, outputFur)" << endl;
		return status;
	}
	
	MAKE_NUMERIC_ATTR( Usamples, "usamples", MFnNumericData::kShort, 32, true );
	MAKE_NUMERIC_ATTR( Vsamples, "vsamples", MFnNumericData::kShort, 32, true );

	return status;
}

furFeedbackGeom* furFeedbackShape::geometry()
{
	MObject this_object = thisMObject();
	MPlug plug( this_object, Usamples );	plug.getValue( fGeometry->Usamples );
	plug.setAttribute( Vsamples );		plug.getValue( fGeometry->Vsamples );

	return fGeometry;
}

furFeedbackShapeUI::furFeedbackShapeUI() {}
furFeedbackShapeUI::~furFeedbackShapeUI() {}

void* furFeedbackShapeUI::creator()
{
	return new furFeedbackShapeUI();
}

void furFeedbackShapeUI::getDrawRequests( const MDrawInfo & info, bool objectAndActiveOnly, MDrawRequestQueue & queue )
{
	cout << "In furFeedbackShapeUI::getDrawRequests" << endl;
	MDrawData data;
	MDrawRequest request = info.getPrototype( *this );
	furFeedbackShape* shapeNode = (furFeedbackShape*)surfaceShape();
	furFeedbackGeom* geom = shapeNode->geometry();
	getDrawData( geom, data );
	request.setDrawData( data );

	switch ( info.displayStyle() )
	{		 
		case M3dView::kWireFrame :
			getDrawRequestsWireframe( request, info );
			queue.add( request );
			break;
		
		case M3dView::kGouraudShaded :
			getDrawRequestsWireframe( request, info );
			queue.add( request );
			break;
		
		case M3dView::kFlatShaded :
			getDrawRequestsWireframe( request, info );
			queue.add( request );
			break;
	}
}

void furFeedbackShapeUI::draw( const MDrawRequest & request, M3dView & view ) const
{
	cout << "In furFeedbackShapeUI::draw" << endl;
	int i;
	MDrawData data = request.drawData();
	furFeedbackGeom * geom = (furFeedbackGeom*)data.geometry();
	short token = request.token();
	bool drawTexture = false;

	view.beginGL(); 

	if( token == kDrawfurFeedback )
	{
			glLineWidth(1.0);
			glBegin(GL_LINES);
			{
				for(i=0; i < cnt1; i++ )
				{
				glVertex3d(start1[i].x, start1[i].y, start1[i].z);
				glVertex3d(end1[i].x, end1[i].y, end1[i].z);
				}
			}
			glEnd();
	}

	view.endGL(); 
}

bool furFeedbackShapeUI::select( MSelectInfo &selectInfo, MSelectionList &selectionList, MPointArray &worldSpaceSelectPts ) const
{
	flag++;
	cout << "In furFeedbackShapeUI::select" << endl;
	MSelectionMask priorityMask( MSelectionMask::kSelectObjectsMask );
	MSelectionList item;
	item.add( selectInfo.selectPath() );
	MPoint xformedPt;
	selectInfo.addSelection( item, xformedPt, selectionList,
							 worldSpaceSelectPts, priorityMask, false );
	return true;
}

void furFeedbackShapeUI::getDrawRequestsWireframe( MDrawRequest& request, const MDrawInfo& info )
{
	cout << "In furFeedbackShapeUI::getDrawRequestsWireframe" << endl;
	request.setToken( kDrawfurFeedback );
	M3dView::DisplayStatus displayStatus = info.displayStatus();
	M3dView::ColorTable activeColorTable = M3dView::kActiveColors;
	M3dView::ColorTable dormantColorTable = M3dView::kDormantColors;
	
	if( flag==0 )
	{
		displayStatus = M3dView::kDormant;
	}
	
	switch ( displayStatus )
	{
		case M3dView::kLead :
			request.setColor( LEAD_COLOR, activeColorTable );
			break;
		case M3dView::kActive :
			request.setColor( ACTIVE_COLOR, activeColorTable );
			break;
		case M3dView::kActiveAffected :
			request.setColor( ACTIVE_AFFECTED_COLOR, activeColorTable );
			break;
		case M3dView::kDormant :
			request.setColor( ACTIVE_AFFECTED_COLOR, dormantColorTable );
			break;
		case M3dView::kHilite :
			request.setColor( HILITE_COLOR, activeColorTable );
			break;
	}
}

class furRender : public MPxNode 
{
public:
				 furRender();
	virtual			~furRender();

	virtual MStatus 	compute( const MPlug& plug, MDataBlock& data );
	
	static void*		creator();
	static MStatus 		initialize();
	
	static MObject		basewidth;
	static MObject		tipwidth;
	static MTypeId		id;
	static MObject 		time;
	static MObject     	outputMesh;
protected:
	MObject			createMesh( const MTime& time, MObject &outData, MStatus &status );
};

MObject		furRender::basewidth;
MObject		furRender::tipwidth;
MTypeId		furRender::id( 0x88888 );
MObject		furRender::time;
MObject		furRender::outputMesh;

furRender::furRender() {}
furRender::~furRender() {}

void* furRender::creator()
{
	return new furRender();
}

MStatus furRender::initialize()
{
	cout << "In furRender::initialize" << endl;
	MStatus 		status;
	MFnNumericAttribute 	numericAttr;
	MFnUnitAttribute 	unitAttr;
	MFnTypedAttribute	typedAttr;

	time = unitAttr.create( "time", "tm", MFnUnitAttribute::kTime, 0.0, &status );

	if( status != MS::kSuccess )
	{
		cerr << "ERROR creating input attribute" << endl;
		return status;
	}
	outputMesh = typedAttr.create( "outputMesh", "out", MFnData::kMesh, &status );

	if( status != MS::kSuccess )
	{
		cerr << "ERROR creating output attribute" << endl;
		return status;
	}
	typedAttr.setStorable(false);
	
	status = addAttribute( time );
	if( status != MS::kSuccess )
	{
		cerr << "addAttribute(input)" << endl;
		return status;
	}
	status = addAttribute( outputMesh );
	if( status != MS::kSuccess )
	{
		cerr << "addAttribute(output)" << endl;
		return status;
	}

	status = attributeAffects( time, outputMesh );
	if( status != MS::kSuccess )
	{
		cerr << "attributeAffects(time, outputMesh)" << endl;
	}
	
	MAKE_NUMERIC_ATTR( basewidth, "base", MFnNumericData::kDouble, 0.05, true );
	MAKE_NUMERIC_ATTR( tipwidth, "tip", MFnNumericData::kDouble, 0.03, true );

	return status;
}

MObject furRender::createMesh( const MTime& time, MObject& outData, MStatus &status )
{
	cout << "In furRender::createMesh" << endl;
	int i;
	int *num;
	double k, scalar, dot;
	Point stock, *down, *top;
	Vector view, up, cross, fur;
	MFloatPointArray points;
	MFnMesh meshFS;
	
	num = new int [1000];
	normal = new Vector [1000];
	down = new Point [1000];
	top = new Point [1000];
	A = new Point [1000];
	B = new Point [1000];
	C = new Point [1000];
	D = new Point [1000];
	
	for( i=0; i < 1000; i++ )
	{
		num[i] = rand() % cnt2;
		view.x = viewDirection.x;
		view.y = viewDirection.y;
		view.z = viewDirection.z;
		
		up.x = upDirection.x;	
		up.y = upDirection.y;	
		up.z = upDirection.z;
		
		down[i].x = start2[num[i]].x;
		down[i].y = start2[num[i]].y;
		down[i].z = start2[num[i]].z;
		
		top[i].x = end2[num[i]].x;
		top[i].y = end2[num[i]].y;
		top[i].z = end2[num[i]].z;

		fur.x = top[i].x - down[i].x;
		fur.y = top[i].y - down[i].y;
		fur.z = top[i].z - down[i].z;

		dot = fur.x*view.x+fur.y*view.y+fur.z*view.z;
		
		/*if( dot == 1.0 )
		{
			normal[i].x = -up.x;
			normal[i].y = -up.y;
			normal[i].z = -up.z;
		}
		else if( dot == -1.0 )
		{
			normal[i].x = up.x;
			normal[i].y = up.y;
			normal[i].z = up.z;
		}
		else if( dot == 0.0 )
		{
			normal[i].x = -view.x;
			normal[i].y = -view.y;
			normal[i].z = -view.z;
			
			scalar = normal[i].x*normal[i].x+normal[i].y*normal[i].y+normal[i].z*normal[i].z;
			normal[i].x /= scalar;
			normal[i].y /= scalar;
			normal[i].z /= scalar;
		}
		else if( dot < 0.0 )
		{
			
			k = -( (view.x*fur.x)+(view.y*fur.y)+(view.z*fur.z) ) / ( (fur.x*fur.x)+(fur.y*fur.y)+(fur.z*fur.z) );
		
			normal[i].x = -k*fur.x - view.x;
			normal[i].y = -k*fur.y - view.y;
			normal[i].z = -k*fur.z - view.z;
			
			scalar = normal[i].x*normal[i].x+normal[i].y*normal[i].y+normal[i].z*normal[i].z;
			normal[i].x /= scalar;
			normal[i].y /= scalar;
			normal[i].z /= scalar;
		}
		else if( dot > 0.0 )
		{
			k = ( (view.x*fur.x)+(view.y*fur.y)+(view.z*fur.z) ) / ( (fur.x*fur.x)+(fur.y*fur.y)+(fur.z*fur.z) );
			
			normal[i].x = k*fur.x - view.x;
			normal[i].y = k*fur.y - view.y;
			normal[i].z = k*fur.z - view.z;
			
			scalar = normal[i].x*normal[i].x+normal[i].y*normal[i].y+normal[i].z*normal[i].z;
			normal[i].x /= scalar;
			normal[i].y /= scalar;
			normal[i].z /= scalar;
		}*/
		
		cross.x = (fur.y*view.z)-(fur.z*view.y);
		cross.y = (fur.z*view.x)-(fur.x*view.z);
		cross.z = (fur.x*view.y)-(fur.y*view.x);
		
		//scalar = cross.x*cross.x+cross.y*cross.y+cross.z*cross.z;
		
		//cross.x /= scalar;
		//cross.y /= scalar;
		//cross.x /= scalar;
	
		k = sqrt( baseWidth*baseWidth / 4.0*(cross.x*cross.x+cross.y*cross.y+cross.z*cross.z) );

		stock.x = down[i].x + k*cross.x;
		stock.y = down[i].y + k*cross.y;
		stock.z = down[i].z + k*cross.z;
		
		if( ( ( stock.x - down[i].x ) / cross.x ) >= 0.0 )
		{
			C[i].x = stock.x;
			C[i].y = stock.y;
			C[i].z = stock.z;
			D[i].x = down[i].x - k*cross.x;
			D[i].y = down[i].y - k*cross.y;
			D[i].z = down[i].z - k*cross.z;
		}
		else if( ( ( stock.x - down[i].x ) / cross.x ) < 0.0 )
		{
			C[i].x = down[i].x - k*cross.x;
			C[i].y = down[i].y - k*cross.y;
			C[i].z = down[i].z - k*cross.z;
			D[i].x = stock.x;
			D[i].y = stock.y;
			D[i].z = stock.z;
		}

		k = sqrt( tipWidth*tipWidth / 4.0*(cross.x*cross.x+cross.y*cross.y+cross.z*cross.z) );

		stock.x = top[i].x + k*cross.x;
		stock.y = top[i].y + k*cross.y;
		stock.z = top[i].z + k*cross.z;

		if( ( ( stock.x - top[i].x ) / cross.x ) >= 0.0 )
		{
			B[i].x = stock.x;
			B[i].y = stock.y;
			B[i].z = stock.z;
			A[i].x = top[i].x - k*cross.x;
			A[i].y = top[i].y - k*cross.y;
			A[i].z = top[i].z - k*cross.z;
		}
		else if( ( ( stock.x - top[i].x ) / cross.x ) < 0.0 )
		{
			B[i].x = top[i].x - k*cross.x;
			B[i].y = top[i].y - k*cross.y;
			B[i].z = top[i].z - k*cross.z;
			A[i].x = stock.x;
			A[i].y = stock.y;
			A[i].z = stock.z;
		}
	}
	
	int numFaces = 1000;
	int numVertices = 4000;
	int numFaceConnects = 4000;
	int face_counts[1000];
	int face_connects[4000];

	for( i=0; i < 1000; i++ )
	{
		face_counts[i] = 4;
	}
	
	for( i=0; i < 4000; i++ )
	{
		face_connects[i] = i;
	}
	
	for( i=0; i < 1000; i++ )
	{
		MFloatPoint vtx_1( A[i].x, A[i].y, A[i].z );
		MFloatPoint vtx_2( B[i].x, B[i].y, B[i].z );
		MFloatPoint vtx_3( C[i].x, C[i].y, C[i].z );
		MFloatPoint vtx_4( D[i].x, D[i].y, D[i].z );
		points.append( vtx_1 );	
		points.append( vtx_2 );	
		points.append( vtx_3 );	
		points.append( vtx_4 );
	}

	MIntArray faceCounts( face_counts, numFaces );
	MIntArray faceConnects( face_connects, numFaceConnects );
			
	MObject newMesh = meshFS.create( numVertices, numFaces, points, faceCounts, faceConnects, outData, &status );
		
	return newMesh;
}

MStatus furRender::compute( const MPlug& plug, MDataBlock& data )
{
	MStatus status;

	if( plug == outputMesh )
	{
		cout << "In furRender::compute" << endl;
		MDataHandle timeData = data.inputValue( time, &status );

		if( status != MS::kSuccess )
		{
			cerr << "ERROR getting time data handle" << endl;
		}
		MTime time = timeData.asTime();

		MDataHandle outputHandle = data.outputValue( outputMesh, &status );

		if( status != MS::kSuccess )
		{
			cerr << "ERROR getting polygon dataHandle" << endl;
			return status;
		}
		MFnMeshData dataCreator;
		MObject newOutputData = dataCreator.create( &status );

		if( status != MS::kSuccess )
		{
			cerr << "ERROR creating outputData" << endl;
			return status;
		}
		MObject newMesh = createMesh( time, newOutputData, status );

		if( status != MS::kSuccess )
		{
			cerr << "ERROR creating new Mesh" << endl;
		}
		outputHandle.set(newOutputData);
		data.setClean( plug );
	}
	else 
	{
		return MS::kUnknownParameter;
	}

	return MS::kSuccess;
}
class scanDag: public MPxCommand
{
public:
					scanDag() {};
	virtual			~scanDag();
	static void*	creator();
	virtual MStatus	doIt( const MArgList& );

private:
	MStatus			parseArgs( const MArgList& args,
							   MItDag::TraversalType& traversalType,
							   MFn::Type& filter, bool & quiet);
	MStatus			doScan( const MItDag::TraversalType traversalType,
							MFn::Type filter, bool quiet);
	void			printTransformData(const MDagPath& dagPath, bool quiet);
};

scanDag::~scanDag() {}

void* scanDag::creator()
{
	return new scanDag;
}

MStatus	scanDag::doIt( const MArgList& args )
{
	MItDag::TraversalType	traversalType = MItDag::kDepthFirst;
	MFn::Type				filter        = MFn::kInvalid;
	MStatus					status;
	bool					quiet = false;

	status = parseArgs ( args, traversalType, filter, quiet );
	if (!status)
		return status;

	return doScan( traversalType, filter, quiet);
};

MStatus scanDag::parseArgs( const MArgList& args,
						  MItDag::TraversalType& traversalType,
						  MFn::Type& filter,
						  bool & quiet)
{
	MStatus     	stat;
	MString     	arg;
	const MString	breadthFlag				("-b");
	const MString	breadthFlagLong			("-breadthFirst");
	const MString	depthFlag				("-d");
	const MString	depthFlagLong			("-depthFirst");
	const MString	cameraFlag				("-c");
	const MString	cameraFlagLong			("-cameras");
	const MString	lightFlag				("-l");
	const MString	lightFlagLong			("-lights");
	const MString	nurbsSurfaceFlag		("-n");
	const MString	nurbsSurfaceFlagLong	("-nurbsSurfaces");
	const MString	quietFlag				("-q");
	const MString	quietFlagLong			("-quiet");

	// Parse the arguments.
	for ( size_t i = 0; i < args.length(); i++ ) {
		arg = args.asString( i, &stat );
		if (!stat)              
			continue;
				
		if ( arg == breadthFlag || arg == breadthFlagLong )
			traversalType = MItDag::kBreadthFirst;
		else if ( arg == depthFlag || arg == depthFlagLong )
			traversalType = MItDag::kDepthFirst;
		else if ( arg == cameraFlag || arg == cameraFlagLong )
			filter = MFn::kCamera;
		else if ( arg == lightFlag || arg == lightFlagLong )
			filter = MFn::kLight;
		else if ( arg == nurbsSurfaceFlag || arg == nurbsSurfaceFlagLong )
			filter = MFn::kNurbsSurface;
		else if ( arg == quietFlag || arg == quietFlagLong )
			quiet = true;
		else {
			arg += ": unknown argument";
			displayError(arg);
			return MS::kFailure;
		}
	}
	return stat;
}

MStatus scanDag::doScan( const MItDag::TraversalType traversalType,
					   MFn::Type filter,
					   bool quiet)
{   
	MStatus status;

	MItDag dagIterator( traversalType, filter, &status);

	if ( !status) {
		status.perror("MItDag constructor");
		return status;
	}

	//	Scan the entire DAG and output the name and depth of each node

	if (traversalType == MItDag::kBreadthFirst)
		if (!quiet)
			cout << endl << "Starting Breadth First scan of the Dag";
	else
		if (!quiet)
			cout << endl << "Starting Depth First scan of the Dag";

	switch (filter) {
		case MFn::kCamera:
			if (!quiet)
				cout << ": Filtering for Cameras\n";
			break;
		case MFn::kLight:
			if (!quiet)
				cout << ": Filtering for Lights\n";
			break;
		case MFn::kNurbsSurface:
			if (!quiet)
				cout << ": Filtering for Nurbs Surfaces\n";
			break;
		default:
			cout << endl;
	}
	
	int objectCount = 0;
	for ( ; !dagIterator.isDone(); dagIterator.next() ) {

		MDagPath dagPath;

		status = dagIterator.getPath(dagPath);
		if ( !status ) {
			status.perror("MItDag::getPath");
			continue;
		}

		MFnDagNode dagNode(dagPath, &status);
		if ( !status ) {
			status.perror("MFnDagNode constructor");
			continue;
		}

		if (!quiet)
			cout << dagNode.name() << ": " << dagNode.typeName() << endl;

		if (!quiet)
			cout << "  dagPath: " << dagPath.fullPathName() << endl;

		objectCount += 1;
		if (dagPath.hasFn(MFn::kCamera)) {
			MFnCamera camera (dagPath, &status);
			if ( !status ) {
				status.perror("MFnCamera constructor");
				continue;
			}

			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);

			// Extract some interesting Camera data
			if (!quiet)
			{
				cout << "  eyePoint: "
					 << camera.eyePoint(MSpace::kWorld) << endl;
				cout << "  upDirection: "
					 << camera.upDirection(MSpace::kWorld) << endl;
					upDirection = camera.upDirection(MSpace::kWorld);
				cout << "  viewDirection: "
					 << camera.viewDirection(MSpace::kWorld) << endl;
					viewDirection = camera.viewDirection(MSpace::kWorld);
				cout << "  aspectRatio: " << camera.aspectRatio() << endl;
				cout << "  horizontalFilmAperture: "
					 << camera.horizontalFilmAperture() << endl;
				cout << "  verticalFilmAperture: "
					 << camera.verticalFilmAperture() << endl;
			}
		} else if (dagPath.hasFn(MFn::kLight)) {
			MFnLight light (dagPath, &status);
			if ( !status ) {
				status.perror("MFnLight constructor");
				continue;
			}

			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);

			// Extract some interesting Light data
			MColor color;

			color = light.color();
			if (!quiet)
			{
				cout << "  color: ["
					 << color.r << ", "
					 << color.g << ", "
					 << color.b << "]\n";
			}
			color = light.shadowColor();
			if (!quiet)
			{
				cout << "  shadowColor: ["
					 << color.r << ", "
					 << color.g << ", "
					 << color.b << "]\n";

				cout << "  intensity: " << light.intensity() << endl;
			}
		} else if (dagPath.hasFn(MFn::kNurbsSurface)) {
			MFnNurbsSurface surface (dagPath, &status);
			if ( !status ) {
				status.perror("MFnNurbsSurface constructor");
				continue;
			}

			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);

			// Extract some interesting Surface data
			if (!quiet)
			{
				cout << "  numCVs: "
					 << surface.numCVsInU()
					 << " * "
					 << surface.numCVsInV()
					 << endl;
				cout << "  numKnots: "
					 << surface.numKnotsInU()
					 << " * "
					 << surface.numKnotsInV()
					 << endl;
				cout << "  numSpans: "
					 << surface.numSpansInU()
					 << " * "
					 << surface.numSpansInV()
					 << endl;
			}
		} else {
			// Get the translation/rotation/scale data
			printTransformData(dagPath, quiet);
		}
	}

	if (!quiet)
	{
		cout.flush();
	}
	setResult(objectCount);
	return MS::kSuccess;
}

void scanDag::printTransformData(const MDagPath& dagPath, bool quiet)
{
	MStatus		status;
	MObject		transformNode = dagPath.transform(&status);
	// This node has no transform - i.e., it's the world node
	if (!status && status.statusCode () == MStatus::kInvalidParameter)
		return;
	MFnDagNode	transform (transformNode, &status);
	if (!status) {
		status.perror("MFnDagNode constructor");
		return;
	}
	MTransformationMatrix	matrix (transform.transformationMatrix());

	if (!quiet)
	{
		cout << "  translation: " << matrix.translation(MSpace::kWorld)
			 << endl;
	}
	double									threeDoubles[3];
	MTransformationMatrix::RotationOrder	rOrder;

	matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld);
	if (!quiet)
	{
		cout << "  rotation: ["
			 << threeDoubles[0] << ", "
			 << threeDoubles[1] << ", "
			 << threeDoubles[2] << "]\n";
	}
	matrix.getScale (threeDoubles, MSpace::kWorld);
	if (!quiet)
	{
		cout << "  scale: ["
			 << threeDoubles[0] << ", "
			 << threeDoubles[1] << ", "
			 << threeDoubles[2] << "]\n";
	}
}

MStatus initializePlugin( MObject obj )
{ 
	MStatus status;
	MFnPlugin plugin( obj, "Yosiyuki Nisino", "1.0", "Any" );

	status = plugin.registerCommand( "scanDag", scanDag::creator );
	status = plugin.registerNode( "furRender", furRender::id, furRender::creator, furRender::initialize );
	status = plugin.registerShape( "furFeedbackShape", furFeedbackShape::id, &furFeedbackShape::creator, &furFeedbackShape::initialize, &furFeedbackShapeUI::creator  );

	return status;
}

MStatus uninitializePlugin( MObject obj)
{
	MStatus status;
	MFnPlugin plugin( obj );

	status = plugin.deregisterCommand( "scanDag" );
	status = plugin.deregisterNode( furRender::id );
	status = plugin.deregisterNode( furFeedbackShape::id );

	return status;
}

