code for review: a mod of Julians's UVtoPosition

Discussions concerning programming of SOFTIMAGE©
User avatar
rray
Moderator
Posts: 1537
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany

code for review: a mod of Julians's UVtoPosition

Post by rray » 12 Feb 2014, 01:28

I was trying to connect Julian Johnson's "Texture UV to Location" node to a per point input (needed an array of UVs per point that's why Build Array from Set wouldn't work for me).
This didn't work, so I applied some hacks to his code (see below) to have per point context for the UV input.

I'm just beginning to learn to code ICE nodes and don't really have anything close to a full understanding of the concepts here .. so I'm 9999% sure I did something wrong, also considering that it's running rather slow and that it's not working with some comiler optimization settings (only settings that worked were all /Ox /Ob0 /Oi /Ot /GL enabled)

Main thing I changed from Julian's code was to move the main evaluation from BeginEvaluate to Evaluate so the multithreading indexSet thing could be used for the points set. My questions are marked with //??? ... if anyone would enjoy having a quick look over what I did here, I would appreciate any comments or concerns about it

Thanks to Julian for sharing this plugin and the code!
Cheers
-rr

Code: Select all

// TextureUVToPosition Plugin
// Initial code generated by Softimage SDK Wizard
// Executed Thu Jun 13 16:57:49 UTC+0100 2013 by julian
// 
// 
// Tip: You need to compile the generated code before you can load the plug-in.
// After you compile the plug-in, you can load it by clicking Update All in the Plugin Manager.
#include <xsi_application.h>
#include <xsi_context.h>
#include <xsi_pluginregistrar.h>
#include <xsi_status.h>

#include <xsi_icenodecontext.h>
#include <xsi_icenodedef.h>
#include <xsi_command.h>
#include <xsi_factory.h>
#include <xsi_longarray.h>
#include <xsi_doublearray.h>
#include <xsi_math.h>
#include <xsi_vector2f.h>
#include <xsi_vector3f.h>
#include <xsi_vector4f.h>
#include <xsi_matrix3f.h>
#include <xsi_matrix4f.h>
#include <xsi_rotationf.h>
#include <xsi_quaternionf.h>
#include <xsi_color4f.h>
#include <xsi_shape.h>
#include <xsi_icegeometry.h>
#include <xsi_iceportstate.h>
#include <xsi_indexset.h>
#include <xsi_dataarray.h>
#include <xsi_dataarray2D.h>

#include <vector>

// Defines port, group and map identifiers used for registering the ICENode

enum IDs
{
	ID_IN_InPort = 0,
	ID_IN_UVs,
	ID_IN_P2NODES,
	ID_IN_SEARCH_UVS,
	ID_G_100 = 100,
	ID_OUT_OutPort = 200,
	ID_TYPE_CNS = 400,
	ID_STRUCT_CNS,
	ID_CTXT_CNS,
	ID_CTXT_CNS2,
	ID_UNDEF = ULONG_MAX
};

XSI::CStatus RegisterTextureUVToPosition( XSI::PluginRegistrar& in_reg );

using namespace XSI; 

class CUserData
{
public:
	CDoubleArray* points;
	ULONG polycount;
	CLongArray* tri_indices;
	CDataArray2DLong* polytonodes;
	CDataArrayVector2f* uvs;
	CDataArray2DVector2f* search_uvs;
};	

//prototypes
XSI::MATH::CVector3f& FindPosition(XSI::MATH::CVector2f &in_pos, ULONG &in_polycount, CDataArray2DLong &in_polytonodes, LONG &in_operating_triangle, CDoubleArray &in_points, CLongArray &in_tri_indices, CDataArrayVector2f &in_uvs   );

bool PointInUVPoly(XSI::MATH::CVector2f &, XSI::MATH::CVector2f &, XSI::MATH::CVector2f &,  XSI::MATH::CVector2f &, float &, float & );

SICALLBACK XSILoadPlugin( PluginRegistrar& in_reg )
{
	in_reg.PutAuthor(L"Julian Johnson");
	in_reg.PutName(L"TextureUVToPosition Plugin");
	in_reg.PutVersion(1,1);

	RegisterTextureUVToPosition( in_reg );
	return CStatus::OK;
}
SICALLBACK XSIUnloadPlugin( const PluginRegistrar& in_reg )
{
	CString strPluginName;
	strPluginName = in_reg.GetName();

	return CStatus::OK;
}

CStatus RegisterTextureUVToPosition( PluginRegistrar& in_reg )
{
	ICENodeDef nodeDef;
	nodeDef = Application().GetFactory().CreateICENodeDef(L"TextureUVToPosition",L"TextureUVToPosition");

	CStatus st;
	st = nodeDef.PutColor(154,188,102);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddPortGroup(ID_G_100);
	st.AssertSucceeded( ) ;
	
	//Set threading mode
	st = nodeDef.PutThreadingModel(XSI::siICENodeMultiEvaluationPhase);
	st.AssertSucceeded( ) ;

	//??? I don't understand the ID_UNDEF,ID_STRUCT_CNS,ID_CTXT_CNS constraints here, is this for stopping the users from making wrong connections?
	//??? left everything in its original state

	st = nodeDef.AddInputPort(ID_IN_SEARCH_UVS,ID_G_100,siICENodeDataVector2,siICENodeStructureAny,siICENodeContextSingleton|siICENodeContextComponent0D,L"Input UVs",L"UV_Search",CValue(),CValue(),CValue(),ID_UNDEF,ID_STRUCT_CNS,ID_CTXT_CNS);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_UVs,ID_G_100,siICENodeDataVector2,siICENodeStructureSingle,siICENodeContextComponent0D2D,L"Projection (UVs attribute)",L"ProjectionUVs",CValue(),CValue(),CValue(),ID_UNDEF,ID_UNDEF,ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_InPort,ID_G_100,siICENodeDataGeometry,siICENodeStructureSingle,siICENodeContextSingleton,L"Geometry",L"Geometry",CValue(),CValue(),CValue(),ID_UNDEF,ID_UNDEF,ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_P2NODES,ID_G_100,siICENodeDataLong,siICENodeStructureArray,siICENodeContextComponent2D,L"PolygonToNodes Array",L"PolygonToNodes",CValue(),CValue(),CValue(),ID_UNDEF,ID_UNDEF,ID_UNDEF);
	st.AssertSucceeded( ) ;

	/*NOTE----------------------------------------------------------------------------------------------------------
	These two ports, i.e. the UV_Search and Output ports are constrained by structure type. I'm not sure if this is 
	necessary but the thinking is that if you alternate between array and single then inputs and outputs must match. 
	I haven't tried NOT doing this but maybe it will work anyway.
	---------------------------------------------------------------------------------------------------------------*/

	//Add output ports.
	st = nodeDef.AddOutputPort(ID_OUT_OutPort,siICENodeDataVector3,siICENodeStructureAny,siICENodeContextSingleton|siICENodeContextComponent0D,L"Position",L"OutPort",ID_UNDEF,ID_STRUCT_CNS,ID_CTXT_CNS);
	st.AssertSucceeded( ) ;

	PluginItem nodeItem = in_reg.RegisterICENode(nodeDef);
	nodeItem.PutCategories(L"Custom");

	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus TextureUVToPosition_SubmitEvaluationPhaseInfo( ICENodeContext& in_ctxt )
{
	ULONG nPhase = in_ctxt.GetEvaluationPhaseIndex( );
	switch( nPhase )
	{
		case 0:
		{
			in_ctxt.AddEvaluationPhaseInputPort( ID_IN_P2NODES );
		}
		break;
		case 1:
		{
			in_ctxt.AddEvaluationPhaseInputPort( ID_IN_UVs );
		}
		break;
		case 2:
		{
			in_ctxt.AddEvaluationPhaseInputPort( ID_IN_InPort );
			in_ctxt.AddEvaluationPhaseInputPort( ID_IN_SEARCH_UVS );
		
			in_ctxt.SetLastEvaluationPhase();
		}
		break;
	}
	return CStatus::OK;
}

SICALLBACK TextureUVToPosition_BeginEvaluate( ICENodeContext& in_ctxt )
{
	//CREATE USER DATA
	CUserData *pUserData = new CUserData;

	//ICE GEO INFORMATION
 	CICEGeometry geom( in_ctxt, ID_IN_InPort );
	
	//??? can i do this here?
	pUserData->points = new CDoubleArray();
	geom.GetPointPositions( *pUserData->points ); 

	pUserData->polycount = geom.GetPolygonCount();

	pUserData->tri_indices = new CLongArray;
	geom.GetTrianglePointIndices(*pUserData->tri_indices);

	in_ctxt.PutUserData(  (CValue::siPtrType)pUserData  );
	return CStatus::OK;
}

SICALLBACK TextureUVToPosition_Evaluate( ICENodeContext& in_ctxt )
{
	ULONG nPhase = in_ctxt.GetEvaluationPhaseIndex( );
	switch( nPhase )
	{
		case 0:
		{
			CDataArray2DLong in_p2nodes(in_ctxt, ID_IN_P2NODES);
			return CStatus::OK;
		}
		break;
		case 1:
		{
			CDataArrayVector2f in_uvs( in_ctxt, ID_IN_UVs );
			return CStatus::OK;
		}
		break;
	};
	
	ULONG out_portID = in_ctxt.GetEvaluatedOutputPortID( );

	switch( out_portID )
	{
		case ID_OUT_OutPort :
		{
			//GET USER DATA
			CUserData *pUserData = (CUserData*)(CValue::siPtrType)in_ctxt.GetUserData( );				
			if ( !pUserData )
				break;
		
			//??? pulling the input ports again is that the right way?
			//??? because using the ones pulled to userdata didn't work (accessor errors in multithreading)
			CDataArray2DVector2f search_uvs( in_ctxt, ID_IN_SEARCH_UVS);
			CDataArray2DLong p2nodes(in_ctxt, ID_IN_P2NODES); 
			CDataArrayVector2f uvs( in_ctxt, ID_IN_UVs ); 

			//CHECK WHETHER OUTPUT PORT IS SINGLE OR ARRAY
			siICENodeDataType dt;
			siICENodeStructureType st;
			siICENodeContextType ct;

			CIndexSet indexSet( in_ctxt );

			in_ctxt.GetPortInfo(ID_OUT_OutPort, dt, st, ct);
			if ( st == XSI::siICENodeStructureSingle )
			{
				CDataArrayVector3f outData( in_ctxt );

				for(CIndexSet::Iterator it = indexSet.Begin(); it.HasNext(); it.Next())
				{
					CDataArray2DVector2f::Accessor inAccessor = search_uvs[it];
					//??? not sure what operating_triangle does
					LONG operating_triangle = 0;
					outData[it] = FindPosition(		inAccessor[0], 
													pUserData->polycount, 
													p2nodes, 
													operating_triangle, 
													*(pUserData->points), 
													*(pUserData->tri_indices), 
													uvs );
				}
			}
			else
			{
				CDataArray2DVector3f outData( in_ctxt );			

				for(CIndexSet::Iterator it = indexSet.Begin(); it.HasNext(); it.Next())
				{
					CDataArray2DVector2f::Accessor inAccessor = search_uvs[it];
					CDataArray2DVector3f::Accessor outAccessor = outData.Resize( it, inAccessor.GetCount() );

					for (unsigned int i = 0; i < inAccessor.GetCount(); i++) //loop over each point's input uv(s)
					{
						LONG operating_triangle = 0;
						outAccessor[i] = FindPosition(	inAccessor[i], 
														pUserData->polycount, 
														p2nodes, 
														operating_triangle, 
														*(pUserData->points), 
														*(pUserData->tri_indices), 
														uvs );
					}
				}
			}
		}
		break;
	};
	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus TextureUVToPosition_EndEvaluate( ICENodeContext& in_ctxt )
{
	// Release memory allocated in BeginEvaluate
	CValue userData = in_ctxt.GetUserData( );
	if ( userData.IsEmpty() )
	{
		return CStatus::OK;
	}
	CUserData *pUserData = (CUserData*)(CValue::siPtrType)in_ctxt.GetUserData( );

	delete pUserData;
	
	// Clear user data
	in_ctxt.PutUserData( CValue() );
	
	return CStatus::OK;
} 

XSI::MATH::CVector3f& FindPosition(XSI::MATH::CVector2f &in_pos, ULONG &in_polycount, CDataArray2DLong &in_polytonodes, LONG &in_operating_triangle, CDoubleArray &in_points, CLongArray &in_tri_indices, CDataArrayVector2f &in_uvs   )
{
	//SETUP VARIABLES
	XSI::MATH::CVector2f uv1, uv2, uv3;
	XSI::MATH::CVector3f pos1, pos2, pos3;
	XSI::MATH::CVector3f result(0. ,0. ,0.);
	float r, t;

	for (ULONG i=0; i < in_polycount; i++)//loop over each poly, the MAIN loop
	{
		CDataArray2DLong::Accessor subarray = in_polytonodes[i];
		if(!subarray.GetCount())
			return result;
		for (ULONG j=1; j < subarray.GetCount() -1; j++)//loop over each triangle sub array
		{
			uv1 = in_uvs[subarray[0]];
			uv2 = in_uvs[subarray[j]];
			uv3 = in_uvs[subarray[j+1]];
			
			if ( PointInUVPoly(uv1, uv2, uv3, in_pos, r, t ) )
			{
				LONG idx1, idx2, idx3;
				idx1 = in_operating_triangle * 3;
				idx2 = in_operating_triangle * 3 + 1;
				idx3 = in_operating_triangle * 3 + 2;

				pos1.Set(in_points[in_tri_indices[idx1]*3], in_points[in_tri_indices[idx1]*3+1], in_points[in_tri_indices[idx1]*3+2]);
				pos2.Set(in_points[in_tri_indices[idx2]*3], in_points[in_tri_indices[idx2]*3+1], in_points[in_tri_indices[idx2]*3+2]);
				pos3.Set(in_points[in_tri_indices[idx3]*3], in_points[in_tri_indices[idx3]*3+1], in_points[in_tri_indices[idx3]*3+2]);

				XSI::MATH::CVector3f e1;
				XSI::MATH::CVector3f e2;
				
				e1.Sub(pos2,pos1);
				e2.Sub(pos3,pos1);

				e1.ScaleInPlace(r);
				e2.ScaleInPlace(t);

				e1.AddInPlace(e2);
				e1.AddInPlace(pos1);	
				result = e1;
				return result;
			}
			in_operating_triangle += 1;
		}
	}
	return result;
}

bool PointInUVPoly(XSI::MATH::CVector2f &v1, XSI::MATH::CVector2f &v2, XSI::MATH::CVector2f &v3,  XSI::MATH::CVector2f &pos, float &r, float &t )
{
	/*NOTE----------------------------------------------------------------------------------------------------------
	I found a ton of different point in poly algorithms and tested them. None seemed appreciably quicker than the other.
	In fact, this one seemed the most long winded but was one of the quickest. It's a 3D 
	algorithm so I have this really inelegant conversion from the 2D vectors to 3D vectors right at the beginning.
	Not sure of the implication of rewriting to use UVWs but as this is working fine it seemed like a good
	place to stop fussing for now. I'm sure there's a quicker way in pure 2D which would also give me the 
	r and t weights but I didn't have time to look further. Suggestions welcome.

	#http://blogs.msdn.com/b/rezanour/archive/2011/08/07/barycentric-coordinates-and-point-in-triangle-tests.aspx
	---------------------------------------------------------------------------------------------------------------*/
	XSI::MATH::CVector3f in_v1, in_v2, in_v3, in_pos;
	in_v1.Set(v1.GetX(), v1.GetY(), 0.0);
	in_v2.Set(v2.GetX(), v2.GetY(), 0.0);
	in_v3.Set(v3.GetX(), v3.GetY(), 0.0);
	in_pos.Set(pos.GetX(), pos.GetY(), 0.0);
	
	XSI::MATH::CVector3f u, v, w, vCrossW, vCrossU, uCrossW, uCrossV;
	
	u.Sub(in_v2,in_v1);
	v.Sub(in_v3,in_v1);
	w.Sub(in_pos,in_v1);

	vCrossW.Cross(v,w);
	vCrossU.Cross(v,u);
	
	if (vCrossW.Dot(vCrossU) < 0) return false;
	
	uCrossW.Cross(u,w);
	uCrossV.Cross(u,v);
	
	if (uCrossW.Dot(uCrossV) < 0) return false;
	
	float denom = uCrossV.GetLength();
	r = vCrossW.GetLength()/denom;
	t = uCrossW.GetLength()/denom;
	
	if (r <= 1.0 && t <= 1.0 && r + t <= 1.0) 
	{
		return true;
	}
	return false;
}




Softimage Resources section (formerly known as rray.de/xsi) updated Oct 01 2017

scaron
Posts: 114
Joined: 08 Jul 2009, 05:16

Re: code for review: a mod of Julians's UVtoPosition

Post by scaron » 15 Feb 2014, 00:17

//??? I don't understand the ID_UNDEF,ID_STRUCT_CNS,ID_CTXT_CNS constraints here, is this for stopping the users from making wrong connections?
//??? left everything in its original state

if you had a polymorphic port. ie. take random value factory node. if you wanted it to be scalar when the user plugged in a scalar node the output port would need to be scalar also. so you can use these to 'constrain' ports to each other. another example is not constraining structure... when you plug in an scalar array the other port might output a single scalar, so only need to constrain type and context but not structure.

julian johnson
Posts: 9
Joined: 27 Apr 2010, 16:10

Re: code for review: a mod of Julians's UVtoPosition

Post by julian johnson » 10 Mar 2014, 13:37

Hi rray,

I don't think this answers any of your questions. I feel as though the available documentation on when, why and how to pull nodes so that you get a full dataarray is incomplete but here's my version with per point input/output. The biggest discovery I made was that I couldn't get valid data from CICEGeometry in Begin Evaluate. I had to call the data in a particular evaluation phase and store it in user data:

Code: Select all

// TextureUVToPosition Plugin
// Initial code generated by Softimage SDK Wizard
// Executed Thu Jun 13 16:57:49 UTC+0100 2013 by julian
//
//
// Tip: You need to compile the generated code before you can load the plug-in.
// After you compile the plug-in, you can load it by clicking Update All in the Plugin Manager.
#include <xsi_application.h>
#include <xsi_context.h>
#include <xsi_pluginregistrar.h>
#include <xsi_status.h>

#include <xsi_icenodecontext.h>
#include <xsi_icenodedef.h>
#include <xsi_command.h>
#include <xsi_factory.h>
#include <xsi_longarray.h>
#include <xsi_doublearray.h>
#include <xsi_math.h>
#include <xsi_vector2f.h>
#include <xsi_vector3f.h>
#include <xsi_vector4f.h>
#include <xsi_matrix3f.h>
#include <xsi_matrix4f.h>
#include <xsi_rotationf.h>
#include <xsi_quaternionf.h>
#include <xsi_color4f.h>
#include <xsi_shape.h>
#include <xsi_icegeometry.h>
#include <xsi_iceportstate.h>
#include <xsi_indexset.h>
#include <xsi_dataarray.h>
#include <xsi_dataarray2D.h>

#include <vector>
#include <math.h>

// Defines port, group and map identifiers used for registering the ICENode

enum IDs
{
	ID_IN_InPort = 0,
	ID_IN_UVs,
	ID_IN_P2NODES,
	ID_IN_SEARCH_UVS,
	ID_G_100 = 100,
	ID_OUT_OutPort = 200,
	ID_TYPE_CNS = 400,
	ID_STRUCT_CNS,
	ID_CTXT_CNS,
	ID_UNDEF = ULONG_MAX
};

XSI::CStatus RegisterTextureUVToPosition( XSI::PluginRegistrar &in_reg );

using namespace XSI;

class CData
{
public:
	std::vector<XSI::MATH::CVector3f> pos_array;
	XSI::CDoubleArray points;
	XSI::CLongArray tri_indices;
	ULONG polycount;
	LONG foundsum;
};


bool PointInUVPoly(XSI::MATH::CVector2f &, XSI::MATH::CVector2f &, XSI::MATH::CVector2f &,  XSI::MATH::CVector2f &, float &, float &);

SICALLBACK XSILoadPlugin( PluginRegistrar &in_reg )
{
	in_reg.PutAuthor(L"julian");
	in_reg.PutName(L"TextureUVToPosition Plugin");
	in_reg.PutVersion(1, 1);

	RegisterTextureUVToPosition( in_reg );
	return CStatus::OK;
}
SICALLBACK XSIUnloadPlugin( const PluginRegistrar &in_reg )
{
	CString strPluginName;
	strPluginName = in_reg.GetName();
	Application().LogMessage(strPluginName + L" has been unloaded.", siVerboseMsg);
	return CStatus::OK;
}

CStatus RegisterTextureUVToPosition( PluginRegistrar &in_reg )
{
	ICENodeDef nodeDef;
	nodeDef = Application().GetFactory().CreateICENodeDef(L"TextureUVToPosition", L"TextureUVToPosition");

	CStatus st;
	st = nodeDef.PutColor(154, 188, 102);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddPortGroup(ID_G_100);
	st.AssertSucceeded( ) ;

	//Set threading mode
	st = nodeDef.PutThreadingModel(XSI::siICENodeMultiEvaluationPhase);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_UVs, ID_G_100, siICENodeDataVector2, siICENodeStructureSingle, siICENodeContextComponent0D2D, L"TextureUVs", L"TextureUVs", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_InPort, ID_G_100, siICENodeDataGeometry, siICENodeStructureSingle, siICENodeContextSingleton, L"Geometry", L"Geometry", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_P2NODES, ID_G_100, siICENodeDataLong, siICENodeStructureArray, siICENodeContextComponent2D, L"PolygonToNodes", L"PolygonToNodes", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_SEARCH_UVS, ID_G_100, siICENodeDataVector2, siICENodeStructureSingle, siICENodeContextComponent0D, L"UV_Search", L"UV_Search", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	//Add output ports.
	st = nodeDef.AddOutputPort(ID_OUT_OutPort, siICENodeDataVector3, siICENodeStructureSingle, siICENodeContextComponent0D, L"OutPort", L"OutPort", ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	PluginItem nodeItem = in_reg.RegisterICENode(nodeDef);
	nodeItem.PutCategories(L"Custom ICENode");

	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus TextureUVToPosition_SubmitEvaluationPhaseInfo( ICENodeContext &in_ctxt )
{
	ULONG nPhase = in_ctxt.GetEvaluationPhaseIndex( );
	switch ( nPhase )
	{
	case 0:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_P2NODES );
	}
	break;
	case 1:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_UVs );
	}
	break;
	case 2:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_InPort );
	}
	break;
	case 3:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_SEARCH_UVS );
		// This phase is the last one. All ports specified for phase 1 will be evaluated in multi-threaded batches.
		in_ctxt.SetLastEvaluationPhase();
	}
	break;
	}
	return CStatus::OK;
}


SICALLBACK TextureUVToPosition_Evaluate( ICENodeContext &in_ctxt )
{
	ULONG out_portID = in_ctxt.GetEvaluatedOutputPortID( );
	ULONG nPhase = in_ctxt.GetEvaluationPhaseIndex( );

	switch ( nPhase )
	{
		case 0:
		{
			CDataArray2DLong in_p2nodes(in_ctxt, ID_IN_P2NODES);
			return CStatus::OK;
		}
		break;
		case 1:
		{
			CDataArrayVector2f in_uvs( in_ctxt, ID_IN_UVs );
			return CStatus::OK;
		}
		break;
		case 2:
		{
			CICEGeometry geom( in_ctxt, ID_IN_InPort );

			CData *pData = new CData;
			geom.GetPointPositions( pData->points );
			geom.GetTrianglePointIndices(pData->tri_indices);
			pData->polycount = geom.GetPolygonCount();

			in_ctxt.PutUserData(  (CValue::siPtrType)pData  );

			//Application().LogMessage(L"no.points: " + CString(pData->points.GetCount()));
			//Application().LogMessage(L"no.polys: " + CString(pData->polycount));
			//Application().LogMessage(L"no.tris: " + CString(pData->tri_indices.GetCount()));

			return CStatus::OK;
		}
		break;
	};

	XSI::MATH::CVector2f uv1, uv2, uv3, pos_2f;
	XSI::MATH::CVector3f pos1, pos2, pos3;

	CDataArray2DLong polytonodes( in_ctxt, ID_IN_P2NODES );
	CDataArrayVector2f uvs( in_ctxt, ID_IN_UVs);
	CDataArrayVector2f search_uvs( in_ctxt, ID_IN_SEARCH_UVS);

	MATH::CVector3f pos(0, 0, 0);
	//GET USER DATA
	CData *pData = (CData *)(CValue::siPtrType)in_ctxt.GetUserData( );
	pData->foundsum = 0;

	CDataArrayVector3f outData( in_ctxt );
	CIndexSet indexSet( in_ctxt);

	for ( CIndexSet::Iterator it = indexSet.Begin(); it.HasNext(); it.Next() )
	{
		LONG operating_triangle = 0;
		pos_2f = search_uvs[it];

		XSI::MATH::CVector2f uv1, uv2, uv3;
		XSI::MATH::CVector3f pos1, pos2, pos3;
		float r, t;
		LONG found = 0;

		for (ULONG i = 0; i < pData->polycount; i++) //loop over each poly, the MAIN loop
		{
			if (found) break;
			{
				CDataArray2DLong::Accessor subarray = polytonodes[i];
				for (ULONG j = 1; j < subarray.GetCount() - 1; j++) //loop over each triangle sub array
				{
					uv1 = uvs[subarray[0]];
					uv2 = uvs[subarray[j]];
					uv3 = uvs[subarray[j + 1]];
					if ( PointInUVPoly(uv1, uv2, uv3, pos_2f, r, t ) )
					{
						LONG idx1, idx2, idx3;
						idx1 = operating_triangle * 3;
						idx2 = operating_triangle * 3 + 1;
						idx3 = operating_triangle * 3 + 2;

						pos1.Set(pData->points[pData->tri_indices[idx1] * 3], pData->points[pData->tri_indices[idx1] * 3 + 1], pData->points[pData->tri_indices[idx1] * 3 + 2]);
						pos2.Set(pData->points[pData->tri_indices[idx2] * 3], pData->points[pData->tri_indices[idx2] * 3 + 1], pData->points[pData->tri_indices[idx2] * 3 + 2]);
						pos3.Set(pData->points[pData->tri_indices[idx3] * 3], pData->points[pData->tri_indices[idx3] * 3 + 1], pData->points[pData->tri_indices[idx3] * 3 + 2]);

						XSI::MATH::CVector3f e1;
						XSI::MATH::CVector3f e2;

						e1.Sub(pos2, pos1);
						e2.Sub(pos3, pos1);

						e1.ScaleInPlace(r);
						e2.ScaleInPlace(t);

						e1.AddInPlace(e2);
						e1.AddInPlace(pos1);

						outData[it] = e1;

						found = 1;

					}
					operating_triangle += 1;
				}
			}
		}
		if (found == 0)
		{
			pData->foundsum += 1;
			Application().LogMessage(L"Not Found: " + CString(it.GetAbsoluteIndex()));
			outData[it] = XSI::MATH::CVector3f(0, 0, 0);
		}
	}
	

	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus TextureUVToPosition_EndEvaluate( ICENodeContext &in_ctxt )
{

	CValue userData = in_ctxt.GetUserData( );
	if ( userData.IsEmpty() )
	{
		return CStatus::OK;
	}
	CData *pData = (CData *)(CValue::siPtrType)in_ctxt.GetUserData( );
	delete pData;

	// Clear user data
	in_ctxt.PutUserData( CValue() );

	return CStatus::OK;
}

bool PointInUVPoly(XSI::MATH::CVector2f &v1, XSI::MATH::CVector2f &v2, XSI::MATH::CVector2f &v3,  XSI::MATH::CVector2f &pos, float &r, float &t )
{
	XSI::MATH::CVector3f in_v1, in_v2, in_v3, in_pos;
	in_v1.Set(v1.GetX(), v1.GetY(), 0.0);
	in_v2.Set(v2.GetX(), v2.GetY(), 0.0);
	in_v3.Set(v3.GetX(), v3.GetY(), 0.0);
	in_pos.Set(pos.GetX(), pos.GetY(), 0.0);

	XSI::MATH::CVector3f u, v, w, vCrossW, vCrossU, uCrossW, uCrossV;


	u.Sub(in_v2, in_v1);
	v.Sub(in_v3, in_v1);
	w.Sub(in_pos, in_v1);

	vCrossW.Cross(v, w);
	vCrossU.Cross(v, u);

	if (vCrossW.Dot(vCrossU) < 0) return false;

	uCrossW.Cross(u, w);
	uCrossV.Cross(u, v);

	if (uCrossW.Dot(uCrossV) < 0) return false;

	float denom = uCrossV.GetLength();
	r = vCrossW.GetLength() / denom;
	t = uCrossW.GetLength() / denom;

	if (r <= 1 && t <= 1 && r + t <= 1)
	{
		return true;
	}
	return false;
}


User avatar
rray
Moderator
Posts: 1537
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany

Re: code for review: a mod of Julians's UVtoPosition

Post by rray » 10 Mar 2014, 15:05

Thank you for the feedbacks Steven and Julian!

That's weird about the BeginEvaluate not giving you valid data, wasn't it working in your previous version? maybe some ICE optimization hitting?

Really cool plugin, btw I'm using it for hair (converting guide strands from 3D to a "UV&height" coordinate system. Interpolation is done in this system then and for the conversion back to XYZ I need the UV to position lookup)

It's a bit slow of course, going through each of 100.000 points, each has 40 strandpositions (=4.000.000) and then for each of these it has to test each uv triangle (8000*4 mil = 32 billion checks) ... still usable speed though.

this is only using 1 guide curve:
Image
Softimage Resources section (formerly known as rray.de/xsi) updated Oct 01 2017

julian johnson
Posts: 9
Joined: 27 Apr 2010, 16:10

Re: code for review: a mod of Julians's UVtoPosition

Post by julian johnson » 10 Mar 2014, 15:17

What a great application! Looks superb :-)

User avatar
rray
Moderator
Posts: 1537
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany

Re: code for review: a mod of Julians's UVtoPosition

Post by rray » 12 Mar 2014, 02:36

Cheers man. This approach looks quite promising, at least for this kind of hairstlye..

3 guide curves and some randomizations with kHair:

Image
Softimage Resources section (formerly known as rray.de/xsi) updated Oct 01 2017

julian johnson
Posts: 9
Joined: 27 Apr 2010, 16:10

Re: code for review: a mod of Julians's UVtoPosition

Post by julian johnson » 13 Mar 2014, 13:42

Looks amazing :-)

I'm just pasting some code that seems to make the plugin approx 20% faster. It might be too small an improvement for you to bother with but given the number of queries you're doing it might be worth investigating. Essentially I've rewritten the point in uv poly test which gains about 10% and I've removed some of the CICEGeometry queries which are inherently slow but now you have to plug in node positions (this gains another 10%)...

Code: Select all

// TextureUVToPosition Plugin
// Initial code generated by Softimage SDK Wizard
// Executed Thu Jun 13 16:57:49 UTC+0100 2013 by julian
//
//
// Tip: You need to compile the generated code before you can load the plug-in.
// After you compile the plug-in, you can load it by clicking Update All in the Plugin Manager.
#include <xsi_application.h>
#include <xsi_context.h>
#include <xsi_pluginregistrar.h>
#include <xsi_status.h>

#include <xsi_icenodecontext.h>
#include <xsi_icenodedef.h>
#include <xsi_command.h>
#include <xsi_factory.h>
#include <xsi_longarray.h>
#include <xsi_doublearray.h>
#include <xsi_math.h>
#include <xsi_vector2f.h>
#include <xsi_vector3f.h>
#include <xsi_vector4f.h>
#include <xsi_matrix3f.h>
#include <xsi_matrix4f.h>
#include <xsi_rotationf.h>
#include <xsi_quaternionf.h>
#include <xsi_color4f.h>
#include <xsi_shape.h>
#include <xsi_icegeometry.h>
#include <xsi_iceportstate.h>
#include <xsi_indexset.h>
#include <xsi_dataarray.h>
#include <xsi_dataarray2D.h>

#include <vector>
#include <math.h>

// Defines port, group and map identifiers used for registering the ICENode

enum IDs
{
	ID_IN_InPort = 0,
	ID_IN_UVs,
	ID_IN_P2NODES,
	ID_IN_SEARCH_UVS,
	ID_IN_NODEPOS,
	ID_G_100 = 100,
	ID_OUT_OutPort = 200,
	ID_TYPE_CNS = 400,
	ID_STRUCT_CNS,
	ID_CTXT_CNS,
	ID_UNDEF = ULONG_MAX
};

XSI::CStatus RegisterTextureUVToPosition( XSI::PluginRegistrar &in_reg );

using namespace XSI;

class CData
{
public:
	std::vector<XSI::MATH::CVector3f> pos_array;
	XSI::CDoubleArray points;
	XSI::CLongArray tri_indices;
	ULONG polycount;
	LONG foundsum;
};


bool PointInUVPoly(XSI::MATH::CVector2f &, XSI::MATH::CVector2f &, XSI::MATH::CVector2f &,  XSI::MATH::CVector2f &, float &, float &);

SICALLBACK XSILoadPlugin( PluginRegistrar &in_reg )
{
	in_reg.PutAuthor(L"julian");
	in_reg.PutName(L"TextureUVToPosition Plugin");
	in_reg.PutVersion(1, 1);

	RegisterTextureUVToPosition( in_reg );
	return CStatus::OK;
}
SICALLBACK XSIUnloadPlugin( const PluginRegistrar &in_reg )
{
	CString strPluginName;
	strPluginName = in_reg.GetName();
	Application().LogMessage(strPluginName + L" has been unloaded.", siVerboseMsg);
	return CStatus::OK;
}

CStatus RegisterTextureUVToPosition( PluginRegistrar &in_reg )
{
	ICENodeDef nodeDef;
	nodeDef = Application().GetFactory().CreateICENodeDef(L"TextureUVToPosition", L"TextureUVToPosition");

	CStatus st;
	st = nodeDef.PutColor(154, 188, 102);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddPortGroup(ID_G_100);
	st.AssertSucceeded( ) ;

	//Set threading mode
	st = nodeDef.PutThreadingModel(XSI::siICENodeMultiEvaluationPhase);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_UVs, ID_G_100, siICENodeDataVector2, siICENodeStructureSingle, siICENodeContextComponent0D2D, L"TextureUVs", L"TextureUVs", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_NODEPOS, ID_G_100, siICENodeDataVector3, siICENodeStructureSingle, siICENodeContextComponent0D2D, L"NodePositions", L"NodePositions", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_InPort, ID_G_100, siICENodeDataGeometry, siICENodeStructureSingle, siICENodeContextSingleton, L"Geometry", L"Geometry", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_P2NODES, ID_G_100, siICENodeDataLong, siICENodeStructureArray, siICENodeContextComponent2D, L"PolygonToNodes", L"PolygonToNodes", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	st = nodeDef.AddInputPort(ID_IN_SEARCH_UVS, ID_G_100, siICENodeDataVector2, siICENodeStructureSingle, siICENodeContextComponent0D, L"UV_Search", L"UV_Search", CValue(), CValue(), CValue(), ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	//Add output ports.
	st = nodeDef.AddOutputPort(ID_OUT_OutPort, siICENodeDataVector3, siICENodeStructureSingle, siICENodeContextComponent0D, L"OutPort", L"OutPort", ID_UNDEF, ID_UNDEF, ID_UNDEF);
	st.AssertSucceeded( ) ;

	PluginItem nodeItem = in_reg.RegisterICENode(nodeDef);
	nodeItem.PutCategories(L"Custom ICENode");

	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus TextureUVToPosition_SubmitEvaluationPhaseInfo( ICENodeContext &in_ctxt )
{
	ULONG nPhase = in_ctxt.GetEvaluationPhaseIndex( );
	switch ( nPhase )
	{
	case 0:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_P2NODES );
	}
	break;
	case 1:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_UVs );
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_NODEPOS );
	}
	break;
	case 2:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_InPort );
	}
	break;
	case 3:
	{
		in_ctxt.AddEvaluationPhaseInputPort( ID_IN_SEARCH_UVS );
		// This phase is the last one. All ports specified for phase 1 will be evaluated in multi-threaded batches.
		in_ctxt.SetLastEvaluationPhase();
	}
	break;
	}
	return CStatus::OK;
}


SICALLBACK TextureUVToPosition_Evaluate( ICENodeContext &in_ctxt )
{
	ULONG out_portID = in_ctxt.GetEvaluatedOutputPortID( );
	ULONG nPhase = in_ctxt.GetEvaluationPhaseIndex( );

	switch ( nPhase )
	{
		case 0:
		{
			CDataArray2DLong in_p2nodes(in_ctxt, ID_IN_P2NODES);
			return CStatus::OK;
		}
		break;
		case 1:
		{
			CDataArrayVector2f in_uvs( in_ctxt, ID_IN_UVs );
			CDataArrayVector3f in_nodepos( in_ctxt, ID_IN_NODEPOS );
			return CStatus::OK;
		}
		break;
		case 2:
		{
			CICEGeometry geom( in_ctxt, ID_IN_InPort );
			CData *pData = new CData;
			pData->polycount = geom.GetPolygonCount();
			in_ctxt.PutUserData(  (CValue::siPtrType)pData  );
			return CStatus::OK;
		}
		break;
	};

	XSI::MATH::CVector2f uv1, uv2, uv3, pos_2f;
	XSI::MATH::CVector3f pos1, pos2, pos3;

	CDataArray2DLong polytonodes( in_ctxt, ID_IN_P2NODES );
	CDataArrayVector2f uvs( in_ctxt, ID_IN_UVs);
	CDataArrayVector2f search_uvs( in_ctxt, ID_IN_SEARCH_UVS);
	CDataArrayVector3f nodepos(in_ctxt, ID_IN_NODEPOS );

	MATH::CVector3f pos(0, 0, 0);
	//GET USER DATA
	CData *pData = (CData *)(CValue::siPtrType)in_ctxt.GetUserData( );
	pData->foundsum = 0;

	CDataArrayVector3f outData( in_ctxt );
	CIndexSet indexSet( in_ctxt);

	for ( CIndexSet::Iterator it = indexSet.Begin(); it.HasNext(); it.Next() )
	{
		pos_2f = search_uvs[it];
		XSI::MATH::CVector2f uv1, uv2, uv3;
		float r, t;
		LONG found = 0;

		for (ULONG i = 0; i < pData->polycount; i++) //loop over each poly, the MAIN loop
		{
			if (found) break;
			{
				CDataArray2DLong::Accessor subarray = polytonodes[i];
				for (ULONG j = 1; j < subarray.GetCount() - 1; j++) //loop over each triangle sub array
				{
					uv1 = uvs[subarray[0]];
					uv2 = uvs[subarray[j]];
					uv3 = uvs[subarray[j + 1]];

					if ( PointInUVPoly(uv1, uv2, uv3, pos_2f, r, t ) )
					{
						XSI::MATH::CVector3f e1;
						XSI::MATH::CVector3f e2;

						e1.Sub(nodepos[subarray[j]], nodepos[subarray[0]]);
						e2.Sub(nodepos[subarray[j+1]], nodepos[subarray[0]]);

						e1.ScaleInPlace(r);
						e2.ScaleInPlace(t);

						e1.AddInPlace(e2);
						
						e1.AddInPlace(nodepos[subarray[0]]);

						outData[it] = e1;

						found = 1;
					}
				}
			}
		}
		if (found == 0)
		{
			pData->foundsum += 1;
			Application().LogMessage(L"Not Found: " + CString(it.GetAbsoluteIndex()));
			outData[it] = XSI::MATH::CVector3f(0, 0, 0);
		}
	}
	return CStatus::OK;
}

XSIPLUGINCALLBACK CStatus TextureUVToPosition_EndEvaluate( ICENodeContext &in_ctxt )
{

	CValue userData = in_ctxt.GetUserData( );
	if ( userData.IsEmpty() )
	{
		return CStatus::OK;
	}
	CData *pData = (CData *)(CValue::siPtrType)in_ctxt.GetUserData( );
	delete pData;

	// Clear user data
	in_ctxt.PutUserData( CValue() );

	return CStatus::OK;
}


bool PointInUVPoly(XSI::MATH::CVector2f &v1, XSI::MATH::CVector2f &v2, XSI::MATH::CVector2f &v3,  XSI::MATH::CVector2f &pos, float &r, float &t )
{
	float area = 0.5 * (     -v2.GetY() * v3.GetX() + v1.GetY() * (-v2.GetX() + v3.GetX()) + v1.GetX() * (v2.GetY() - v3.GetY()) + v2.GetX() * v3.GetY()        );
	r = 1.0/(2*area)*(v1.GetY()*v3.GetX() - v1.GetX()*v3.GetY() + (v3.GetY() - v1.GetY()) * pos.GetX() + (v1.GetX() - v3.GetX()) * pos.GetY());
	t = 1.0/(2*area)*(v1.GetX()*v2.GetY() - v1.GetY()*v2.GetX() + (v1.GetY() - v2.GetY()) * pos.GetX() + (v2.GetX() - v1.GetX()) * pos.GetY());
	if (r>=0 && t>=0 && 1-r-t>=0) 
	{
		return true;
	}
	return false;
}

anhungxadieu
Posts: 175
Joined: 17 Apr 2014, 10:39
Skype: nguyenvuducthuy

Re: code for review: a mod of Julians's UVtoPosition

Post by anhungxadieu » 24 Mar 2016, 16:38

how can i compile that code ... sorry for poor question :ymblushing:

User avatar
rray
Moderator
Posts: 1537
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany

Re: code for review: a mod of Julians's UVtoPosition

Post by rray » 24 Mar 2016, 18:23

lost track of this thread! btw 2-year belated thanks to Julian for posting the speed improvement update :ymblushing:

I was using visual studio express for compiling, which is not available anymore, but it should be possible to use visual studio community edition which seems to be its successor .. cloud based nowadays ofc. =p~
At least you won't have problems anymore setting it up for 64 bit compiling.

steps are like this: 1) download & installl visual studio
2) copy one of the customICEnode examples from the installation directory to you VS projects directory
3) start VS from a softimage command prompt (for the environment variables)
4) open the project, set to 64 bit, and rename it to TextureUVToPosition
5) paste julien's code above replacing the source code
6) compile, then move the generated dll to a Application/plugins folder
Softimage Resources section (formerly known as rray.de/xsi) updated Oct 01 2017

User avatar
FXDude
Posts: 923
Joined: 19 Jun 2012, 21:59

Re: code for review: a mod of Julians's UVtoPosition

Post by FXDude » 24 Mar 2016, 21:58

That looks awesome! *-:)
Image

User avatar
rray
Moderator
Posts: 1537
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany

Re: code for review: a mod of Julians's UVtoPosition

Post by rray » 25 Mar 2016, 16:32

Cheers dude .D
Btw for the groomtools plugin I ended up using a morphed uv helper object instead of this plugin, the closest location lookups are much faster than checking each uv one by one.
Softimage Resources section (formerly known as rray.de/xsi) updated Oct 01 2017

anhungxadieu
Posts: 175
Joined: 17 Apr 2014, 10:39
Skype: nguyenvuducthuy

Re: code for review: a mod of Julians's UVtoPosition

Post by anhungxadieu » 26 Mar 2016, 04:22

rray wrote:Cheers dude .D
Btw for the groomtools plugin I ended up using a morphed uv helper object instead of this plugin, the closest location lookups are much faster than checking each uv one by one.
correct, i also try it before when i do a "deform by uv"