Here is one way using a Primitive Wrangle:
vector computePerpendicularVector ( vector v )
{
vector v0 = normalize ( v );
vector y = { 0, 1, 0 };
vector pv0 = y - ( v0 * dot ( v0, y ) );
vector z = { 0, 0, 1 };
vector pv1 = z - ( v0 * dot ( v0, z ) );
if ( length2 ( pv0 ) > length2 ( pv1 ) )
return normalize ( pv0 );
return normalize ( pv1 );
}
int vtxcount = primvertexcount ( 0, @primnum );
if ( vtxcount > 1 )
{
int pts [ ] = primpoints ( 0, @primnum );
int count = len ( pts );
int isclosed = primintrinsic ( 0, "closed", @primnum );
if ( pts [ 0 ] != pts [ -1 ] )
{
if ( isclosed )
append ( pts, pts [ 0 ] );
else
append ( pts, pts [ -2 ] );
}
else
{
removeindex ( pts, -1 );
isclosed = 1;
count -= 1;
}
vector tangents [ ] = { };
vector normals [ ] = { };
vector bitangents [ ] = { };
resize ( tangents, count );
resize ( normals, count );
resize ( bitangents, count );
for ( int i = 0; i < count; ++i )
{
vector p0 = point ( 0, "P", pts [ i ] );
vector p1 = point ( 0, "P", pts [ i + 1 ] );
vector tangent = normalize ( p1 - p0 );
if ( isclosed == 0 && i == count - 1 )
tangent *= -1;
tangents [ i ] = tangent;
}
vector n = normalize ( cross ( computePerpendicularVector ( tangents [ 0 ] ), tangents [ 0 ] ) );
normals [ 0 ] = normalize ( cross ( tangents [ 0 ], n ) );
for ( int i = 0; i < count - 1; ++i )
{
vector bitangent = cross ( tangents [ i ], tangents [ i + 1 ] );
if ( length2 ( bitangent ) < 0.0000001 )
normals [ i + 1 ] = normals [ i ];
else
{
bitangent = normalize ( bitangent );
float angle = atan2 ( length ( cross ( tangents [ i ], tangents [ i + 1 ] ) ), dot ( tangents [ i ], tangents [ i + 1 ] ) );
matrix3 rMatrix = ident ( );
rotate ( rMatrix, angle, bitangent );
normals [ i + 1 ] = rMatrix * normals [ i ];
}
bitangents [ i ] = bitangent;
}
for ( int i = 0; i < count; ++i )
{
if ( chi("normal") )
setpointattrib ( geoself ( ), chs("n"), pts [ i ], normals [ i ] );
if ( chi("tangent") )
setpointattrib ( geoself ( ), chs("t"), pts [ i ], tangents [ i ] );
if ( chi("bitangent") || ( chi("orient") || chi("rmatrix") ) )
{
bitangents [ i ] = normalize ( cross ( normals [ i ], tangents [ i ] ) );
if ( chi("bitangent") )
setpointattrib ( geoself ( ), chs("b"), pts [ i ], bitangents [ i ] );
}
matrix3 m = 0;
if ( chi("orient") || chi("rmatrix") )
{
m = set ( bitangents [ i ], normals [ i ], tangents [ i ] );
if ( chi("orient") )
setpointattrib ( geoself ( ), chs("o"), pts [ i ], quaternion ( m ) );
if ( chi("rmatrix") )
setpointattrib ( geoself ( ), chs("rm"), pts [ i ], m );
}
}
}
chi parameters are toggles, chs parameters are attribute names.