Normals from extruded curves

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

 
Thread Tools Display Modes
  01 January 2018
Normals from extruded curves

I'm trying to extrude a pipe from a curve using extrude and nurbsTessellate, like the helper in bonus tools. A problem is that I often end up with a mesh with inverted normals. Every thread about this is just somebody saying "just flip the mesh normals", which isn't much help since I want this to work automatically as the curve deforms.

I tried orienting the profile curve to the normal of the start of the curve (using a pointOnCurveInfo and an aim constraint), but that only half-works and still flips if I move CVs around.

Does anyone know how the extrude node decides which direction to point normals, or another generic way to fix this? It's definitely related to the orientation of the profile curve (if it's flipped, rotating the profile curve always fixes it), but I'm not sure what the other piece is.
 
  01 January 2018
I don't have a real answer, but the workaround I finally found was to sample the normal with a closestPointOnMesh node and see if it's flipped (pointing towards the curve instead of away from it). That goes through a condition node and sets the nodeState of a polyNormal node to turn it on only if needed. I'm not sure about using nodeState that way, but it seems to work so far.

It doesn't work if the mesh is self-intersecting, since closestPointOnMesh can find the wrong point, but that's not a problem for me.
 
  01 January 2018
Ugh. Yeah, I run into this problem all the time, when I'm even bothering with Maya's NURBS. It can be really tedious if you're doing some complex modeling, too.

In Rhino, the normals are determined by the direction of the input curves. It's predictable and always works this way, plus of course Rhino's NURBS are far better implemented and easier to work with. Maya seems to be the same at first, but like you said, in practice it's never really predictable.

Glad you devised a workaround, even if it is a pain in the ass.
__________________
Commodore 64 @ 1MHz
64KB RAM
1541 Floppy Drive


"Like stone we battle the wind... Beat down and strangle the rains..."
 
  01 January 2018
In case it's useful to anyone, here's what I ended up with. It's originally based on the "curve to tube" tool in bonus tools, though not much is left (stripped almost everything I don't need away). "pointOnCurveInfo" is where the actual normals wrangling begins (which takes as much code as creating the tube in the first place).


import pymel.core as pm

def go(curve):
 # Create the profile curve.
 profile_curve = pm.circle(
 normal=(0,1,0), radius=1, degree=3,
 constructionHistory=True,
 sections=8,
 name='profileCurve#')[0]
 profile_curve.attr('visibility').set(False)
 
 # Extrude the curve.
 extruded_surface, extrude_node = pm.extrude(profile_curve, curve,
 constructionHistory=True,
 range=0, polygon=1, extrudeType=2,
 useComponentPivot=1, # component pivot
 fixedPath=True, useProfileNormal=True,
 reverseSurfaceIfPathReversed=True,
 name='tube#')
 extruded_surface = extruded_surface.getShape()
 
 # Configure the nurbsTesselate node.
 nurbs_tessellate = pm.listConnections(extrude_node)[0]
 nurbs_tessellate.attr('format').set(2)
 nurbs_tessellate.attr('uType').set(1) # uniform
 nurbs_tessellate.attr('polygonType').set(1)

 extruded_surface.addAttr('widthDivisions', min=4, at='long', defaultValue=5)
 extruded_surface.setAttr('widthDivisions',e=1, k=1)
 extruded_surface.attr('widthDivisions').connect(nu  rbs_tessellate.attr('uNumber'))

 extruded_surface.addAttr('lengthDivisions', min=1, at='long', defaultValue=7) 
 extruded_surface.setAttr('lengthDivisions', e=1, k=1)
 extruded_surface.attr('lengthDivisions').connect(n  urbs_tessellate.attr('vNumber'))

 extruded_surface.addAttr('lengthDivisionSpacing', at='enum', enumName='uniform=1:non-uniform=2', dv=1) 
 extruded_surface.setAttr('lengthDivisionSpacing', e=1, k=1)
 extruded_surface.attr('lengthDivisionSpacing').con  nect(nurbs_tessellate.attr('vType'))

 # The extrude node often creates flipped normals, and it's not clear when this
 # happens. Work around this by looking at the normal at the mesh at the start of
 # the curve, and enabling a polyNormal node only if it appears to be flipped.
 #
 # Find the position at the start of the curve.
 point_info = pm.createNode('pointOnCurveInfo')
 point_info.attr('parameter').set(0)
 curve.getShape().attr('worldSpace[0]').connect(point_info.attr('inputCurve'))
 
 # Find the closest point on the mesh we just generated to that point. Connect
 # directly to the nurbsTesselate output, not to the output mesh.
 point_on_mesh = pm.createNode('closestPointOnMesh')
 point_info.attr('position').connect(point_on_mesh.  attr('inPosition'))
 nurbs_tessellate.attr('outputPolygon').connect(poi  nt_on_mesh.attr('inMesh'))

 # Subtract (curve point) - (closest mesh point) to get a vector from the mesh
 # towards the center.
 vector_towards_center = pm.createNode('plusMinusAverage', n='vectorTowardsCenter')
 vector_towards_center.attr('operation').set(2) # subtract
 point_on_mesh.attr('inPosition').connect(vector_to  wards_center.attr('input3D[0]')) # curve point
 point_on_mesh.attr('position').connect(vector_towa  rds_center.attr('input3D[1]')) # mesh point

 # Find the angle between the vectors (mesh -> center point) and (mesh -> normal).
 angle_between = pm.createNode('angleBetween', name='normalAngle')
 vector_towards_center.attr('output3D').connect(ang  le_between.attr('vector1'))
 point_on_mesh.attr('result').attr('normal').connec  t(angle_between.attr('vector2'))

 # If the normals are correct, the angle should be near 180. If they're reversed, it'll be
 # near 0 instead. Create a condition node to check this.
 condition = pm.createNode('condition', name='checkNormalAngle')
 angle_between.attr('angle').connect(condition.attr  ('firstTerm'))
 condition.attr('secondTerm').set(90)
 condition.attr('operation').set(2)

 # Create a polyNormal node to reverse the normals.
 reverse_normals = pm.polyNormal(extruded_surface)[0]
 output_mesh = reverse_normals.attr('output').listConnections(p=T  rue)[0].node()

 # We only want to reverse the normals if they're flipped. Connect the condition node to
 # the polyNormal node and only enable it if needed.
 condition.attr('colorIfTrue').set((1,0,0))
 condition.attr('colorIfFalse').set((0,0,0))
 condition.attr('outColorR').connect(reverse_normal  s.attr('nodeState'))

 return output_mesh
 
reply share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 12:03 AM.


Powered by vBulletin
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.