BaoAutoChamfer a script to smooth edges using threshold angle


#1

I want give you a script I have done to smooth (chamfer) edges in a Poly Object. The
edges are selected using a Threshold angle. You can manually add or deselect edges
before chamfer them too.
Copy the code to a text file and rename it to BaoAutoChamfer.ms and then follow
the Installation and Usage that are explained in the comment header of the code.

[b]Updated: Version 1.1

[color=DarkOrange]Note: The object stack must be collapsed. This script don’t works well or fails
(script error messages) if there are modifiers in stack.
[/color][/b]


   /*
    BaoAutoChamfer 1.1: 
      Autodetects edges with a threshold angle between face normals and chamfers them, 
      welding vertices if necessary.
      
    Installation: 
      Place it in  Max/scripts/startup
      
Usage: 
    In Utilities panel open MaxScript and open the dropdown list and select BaoAutoChamfer
    and now scroll down the utilities panel because BaoAutoChamfer is opened down.
  
    Important: The object stack must be collapsed. BaoAutoChamfer runs badly if there
    are modifiers above the Editable Poly Object.
 
    Angle threshold: An edge have two faces. The faces have normals. The angle between the
 	 two normals is compared for each edge with this angle threshold.
 	 If normals angle  >= angle threshold, then chamfer the edge.
    Chamfer amount: Amount for the first chamfer iteration
    Chamfer iterations: Number of chamfers to do.
    Select an object and press Detect & Chamfer Edges button. Or press Detect Edges, now
 	 you can change the edge selection adding or deselecting edges, and then press the
 	 Chamfer them button.
 	 
    Revisions:
      1.1 - Better detection of edges to chamfer. Now edges created in first iteration that have
 		faces planar (zero degrees between its face normals) are not included in next iteration	
    	  - Less code using function calls   
    
    TO DO: Iterations > 3
 		 I have observed with iterations > 3 I have overlaping faces because no welded
 		 vertices, so I decided limit the iterations to 3 by now.	 
    */
    fn fnDetectEdges anglethreshold=
    ( polyOp.CollapseDeadStructs $	  
      facesDuplo=#()
      edgesSel=#()
      numedges=polyOp.getNumEdges $
      for i=1 to numedges do
      ( facesDuplo=polyOp.getEdgeFaces $ i
    	if(facesDuplo.count==2) do
    	( facenormal1=polyOp.getFaceNormal $ facesDuplo[1]
    		facenormal2=polyOp.getFaceNormal $ facesDuplo[2]
    	  normalsangle=acos(dot (normalize facenormal1) (normalize facenormal2))
    	  if(normalsangle>=anglethreshold) do append edgesSel i
      ) )
      polyOp.setEdgeSelection $ edgesSel
    )
    fn fnChamferThem edgesSel amount iterations smooth=
    ( $.edgeChamfer = amount 
      for i=1 to iterations do
      ( if(i==2) do $.edgeChamfer /= 2.75
    	if(i==3) do $.edgeChamfer /= 2.1
    	-- TO DO: iterations > 3 (change also range in spinner baciterations below)
    	--if(i==4) do $.edgeChamfer /= 1.45
    	--if(i==5) do $.edgeChamfer /= 0.85		
    	numedges=polyOp.getNumEdges $			
 	$.EditablePoly.buttonOp #Chamfer			
    	--append the new edges created by chamfer to edges selection array
    	numnewedges=polyOp.getNumEdges $
    	for j=(numedges+1) to numnewedges do 
    	( facesDuplo=polyOp.getEdgeFaces $ j
    	  if(facesDuplo.count==2) do
    	  ( facenormal1=polyOp.getFaceNormal $ facesDuplo[1]
    		  facenormal2=polyOp.getFaceNormal $ facesDuplo[2]
    		  normalsangle=acos(dot (normalize facenormal1) (normalize facenormal2))
    		if(normalsangle>=1) do append edgesSel j
    	) )
    	polyOp.setEdgeSelection $ edgesSel
 	--select vertices from edgeselection and weld vertices in threshold=edgeChamfer	 		
    	$.EditablePoly.ConvertSelection #Edge #Vertex
    	vertsSel=polyOp.getVertSelection $		   
    	$.weldThreshold = $.edgeChamfer*0.7
    	polyOp.weldVertsByThreshold $ vertsSel
    	--deletes dead edges and load again the edgesSel array with the new selection
    	polyOp.CollapseDeadStructs $
    	edgesSel=polyOp.getEdgeSelection $
      )--for
      if(smooth==true) do
      ( subobjectlevel=4
    	polyOp.setFaceSelection $ #all		  
    	$.EditablePoly.buttonOp #Autosmooth
    	polyOp.setFaceSelection $ #none
    	subobjectlevel=0
      ) 
      max utility mode
    )
    fn fnMessageError error:0 =
    ( if(error==1) then messagebox "Press Detect Edges first (then change selection if you want) and after Press this button."
      else
      ( if(selection.count != 1) then
    	( if(selection.count == 0) then messagebox "Select 1 object before!"
    	  else messagebox "Select only 1 object, please."
    	)
 	else if(classOf $.baseObject != editable_poly) do messagebox "This script works on Poly Objects only."
      )
    )
    
    utility BaoAutoChamfer "BaoAutoChamfer 1.1"
    ( local bacpressedfirstbut
      label baclabel1 "Jose Manuel Baleato Bao, 2005" 
    
      group ""
      ( spinner  bacangle	  "Threshold Angle:" fieldwidth:50 align:#left range:[0,359,30] 
 	spinner bacamount	 "Amount:"		 fieldwidth:50 align:#left range:[0,10000,0] scale:0.001
 	spinner baciterations "Iterations:"	 fieldwidth:30 align:#left range:[1,3,2]	 type:#integer
    	checkbox bacsmooth	 "Auto Smooth"	  checked:true
      )
      group "Automatic"
      ( button   bacbutautomat "Detect & Chamfer" 
      )
      group "Manual (you can change Sel.)"
      ( button   bacbutdetect  "Detect Edges" 
    	button   bacbutchamfer "Chamfer them" 
      )
      
      on BaoAutoChamfer open do
      ( if((selection.count == 1) and (classOf $.baseObject == editable_poly)) do
    	( bacamount.value=0.7  --bacamount.value=$.edgeChamfer  -- TO DO: Fix this
    	  bacpressedfirstbut=false
      ) )
      on bacbutdetect pressed do
      ( if((selection.count == 1) and (classOf $.baseObject == editable_poly)) then
    	( fnDetectEdges(bacangle.value)
    	  max modify mode
    	  subobjectlevel= 2 
    	  bacpressedfirstbut=true
    	) 
    	else fnMessageError()
      )
      on bacbutchamfer pressed do
      (	if(bacpressedfirstbut==false) then fnMessageError error:1
    	else
    	( undo "BaoAutoChamfer Manual" on
    	  ( max modify mode
    		subobjectlevel= 2 
    		edgesSel=polyOp.getEdgeSelection $
 		if(edgesSel.count>0) do fnChamferThem edgesSel bacamount.value baciterations.value bacsmooth.checked 
    		bacpressedfirstbut=false
    	) )
      )
      on bacbutautomat pressed do
      ( undo "BaoAutoChamfer Automatic" on
    	( if((selection.count == 1) and (classOf $.baseObject == editable_poly)) then
    	  ( fnDetectEdges(bacangle.value)
    		edgesSel=polyOp.getEdgeSelection $	  
    		if(edgesSel.count>0) do
    		( max modify mode
    		  subobjectlevel= 2 
  		 fnChamferThem edgesSel bacamount.value baciterations.value bacsmooth.checked 
    	  ) ) 
    	  else fnMessageError()
    	)
    	bacpressedfirstbut=false
      )
    ) -- end of utility definition
     

#2

Thanks. It is a very cool script :slight_smile: I also like how you made a “Edge Detect” button that shows what edges is selected. I have not studied your script in detail yet, but noticed that the script seen to chamfer edges “individually” resulting in some extra edges in places where it is just a continious line of edges. Assuming p is the object in question with the selected eges then doing the following for example, chamfers the entire edge selection in one go:

p.EditablePoly.chamferEdges 3.0
p.EditablePoly.chamferEdges 1.0

I assume that the way you do it that due to the fact that you get better control on what happens in the corners etc.?

Lars


#3

Hi Lars.
I have edited the first post and updated to 1.1 the script.
Now I think the extra vertices in long edges are no more created
so a cleaner mesh is obtained.


#4

A work around for the stack having to be colapsed and losing your UV maps is to copy the object, colapse it, run the script and leave it to just select the edges, copy paste the uv maps onto the copy, then apply and edit poly, go to edge selection and you should have the correct edges already selected so can go ahead and bevel them, only downside you dont get as nice edges but helps overcome this fantastic scripts major downside


#5

Best script ever! :thumbsup:

Thanx man!


#6

Cool script Bao, but you should perhaps put it in a floating dialog instead of the utility panel, it will make the interface a hell of a lot friendlier.

I changed it here, I changed the utility to a rollout and I killed all command panel switching.

fn fnDetectEdges anglethreshold=
 	( polyOp.CollapseDeadStructs $	  
 	  facesDuplo=#()
 	  edgesSel=#()
 	  numedges=polyOp.getNumEdges $
 	  for i=1 to numedges do
 	  ( facesDuplo=polyOp.getEdgeFaces $ i
 		if(facesDuplo.count==2) do
 		( facenormal1=polyOp.getFaceNormal $ facesDuplo[1]
 			facenormal2=polyOp.getFaceNormal $ facesDuplo[2]
 		  normalsangle=acos(dot (normalize facenormal1) (normalize facenormal2))
 		  if(normalsangle>=anglethreshold) do append edgesSel i
 	  ) )
 	  polyOp.setEdgeSelection $ edgesSel
 	)
 	fn fnChamferThem edgesSel amount iterations smooth=
 	( $.edgeChamfer = amount 
 	  for i=1 to iterations do
 	  ( if(i==2) do $.edgeChamfer /= 2.75
 		if(i==3) do $.edgeChamfer /= 2.1
 		-- TO DO: iterations > 3 (change also range in spinner baciterations below)
 		--if(i==4) do $.edgeChamfer /= 1.45
 		--if(i==5) do $.edgeChamfer /= 0.85		
 		numedges=polyOp.getNumEdges $			
 	 $.EditablePoly.buttonOp #Chamfer			
 		--append the new edges created by chamfer to edges selection array
 		numnewedges=polyOp.getNumEdges $
 		for j=(numedges+1) to numnewedges do 
 		( facesDuplo=polyOp.getEdgeFaces $ j
 		  if(facesDuplo.count==2) do
 		  ( facenormal1=polyOp.getFaceNormal $ facesDuplo[1]
 			  facenormal2=polyOp.getFaceNormal $ facesDuplo[2]
 			  normalsangle=acos(dot (normalize facenormal1) (normalize facenormal2))
 			if(normalsangle>=1) do append edgesSel j
 		) )
 		polyOp.setEdgeSelection $ edgesSel
 	 --select vertices from edgeselection and weld vertices in threshold=edgeChamfer			 
 		$.EditablePoly.ConvertSelection #Edge #Vertex
 		vertsSel=polyOp.getVertSelection $		   
 		$.weldThreshold = $.edgeChamfer*0.7
 		polyOp.weldVertsByThreshold $ vertsSel
 		--deletes dead edges and load again the edgesSel array with the new selection
 		polyOp.CollapseDeadStructs $
 		edgesSel=polyOp.getEdgeSelection $
 	  )--for
 	  if(smooth==true) do
 	  ( subobjectlevel=4
 		polyOp.setFaceSelection $ #all		  
 		$.EditablePoly.buttonOp #Autosmooth
 		polyOp.setFaceSelection $ #none
 		subobjectlevel=0
 	  ) 
 	)
 	fn fnMessageError error:0 =
 	( if(error==1) then messagebox "Press Detect Edges first (then change selection if you want) and after Press this button."
 	  else
 	  ( if(selection.count != 1) then
 		( if(selection.count == 0) then messagebox "Select 1 object before!"
 		  else messagebox "Select only 1 object, please."
 		)
 	 else if(classOf $.baseObject != editable_poly) do messagebox "This script works on Poly Objects only."
 	  )
 	)
 	
 	rollout BaoAutoChamfer "BaoAutoChamfer 1.1"
 	( local bacpressedfirstbut
 	  label baclabel1 "Jose Manuel Baleato Bao, 2005" 
 	
 	  group ""
 	  ( spinner  bacangle	  "Threshold Angle:" fieldwidth:50 align:#left range:[0,359,30] 
 	 spinner bacamount	 "Amount:"		 fieldwidth:50 align:#left range:[0,10000,0] scale:0.001
 	 spinner baciterations "Iterations:"	 fieldwidth:30 align:#left range:[1,3,2]	 type:#integer
 		checkbox bacsmooth	 "Auto Smooth"	  checked:true
 	  )
 	  group "Automatic"
 	  ( button   bacbutautomat "Detect & Chamfer" 
 	  )
 	  group "Manual (you can change Sel.)"
 	  ( button   bacbutdetect  "Detect Edges" 
 		button   bacbutchamfer "Chamfer them" 
 	  )
 	  
 	  on BaoAutoChamfer open do
 	  ( if((selection.count == 1) and (classOf $.baseObject == editable_poly)) do
 		( bacamount.value=0.7  --bacamount.value=$.edgeChamfer  -- TO DO: Fix this
 		  bacpressedfirstbut=false
 	  ) )
 	  on bacbutdetect pressed do
 	  ( if((selection.count == 1) and (classOf $.baseObject == editable_poly)) then
 		( fnDetectEdges(bacangle.value)
 		  subobjectlevel= 2 
 		  bacpressedfirstbut=true
 		) 
 		else fnMessageError()
 	  )
 	  on bacbutchamfer pressed do
 	  (	if(bacpressedfirstbut==false) then fnMessageError error:1
 		else
 		( undo "BaoAutoChamfer Manual" on
 		  (	subobjectlevel= 2 
 			edgesSel=polyOp.getEdgeSelection $
 		 if(edgesSel.count>0) do fnChamferThem edgesSel bacamount.value baciterations.value bacsmooth.checked 
 			bacpressedfirstbut=false
 		) )
 	  )
 	  on bacbutautomat pressed do
 	  ( undo "BaoAutoChamfer Automatic" on
 		( if((selection.count == 1) and (classOf $.baseObject == editable_poly)) then
 		  ( fnDetectEdges(bacangle.value)
 			edgesSel=polyOp.getEdgeSelection $	  
 			if(edgesSel.count>0) do
 			(
 			  subobjectlevel= 2 
 		   fnChamferThem edgesSel bacamount.value baciterations.value bacsmooth.checked 
 		  ) ) 
 		  else fnMessageError()
 		)
 		bacpressedfirstbut=false
 	  )
 	) -- end of utility definition
 	
 	createDialog BaoAutoChamfer
 	 

#7

I agree with your suggestion Wahooney, this makes it more easy to access and therefore even more usefull. What do you think Bao2?

Btw. I can not get the chamfering part of the script working in Max 8 SP3 … it seems to select the edges all right but never chamfers them (seems to do some autosmooth though). Do you see the same problem in Max 8 - any ideas ?


#8

thank you very much Bao2, it is really useful, this script is awesome, it will save me a lot of time, and thanks to wahooney too.

Xlars i think you should see if you have some amount, by deafult it´s set to 0, increase it, i hope it helps, i´m running 3ds max 8 SP3 without problem.


#9

Ah of course, that was it. Thanks … :blush:


#10

This is an awesome script man :slight_smile: Great job! I noticed that it adds some extra edges though when you apply smoot chamfers to edges? Is there a checkbox somewhere that I’m missing?

Thanks for the great work!
TK


#11

This is an awesome script man :slight_smile: Great job! I noticed that it adds some extra edges though when you apply smoot chamfers to edges? Is there a checkbox somewhere that I’m missing?

Thanks for the great work!
TK


#12

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.