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;
}