Home Technical Talk

[3dsmax] UVW seams to Smoothing Groups?

Hey polycount community!

I was wondering if there is a script/tool which allows the user to automatically generate smoothing groups from UVW seams in 3ds max. That should help to quickly bake normalmaps without weird seams.

I hope you can help ;)

Cheers sebi

Replies

  • leechdemon
    Offline / Send Message
    leechdemon polycounter lvl 11
    I normally do this in reverse; I separate UV chunks into separate elements in Edit Poly, that way they're easier to identify in UVW Unwrap, and the smoothing doesn't cross multiple UV chunks (in case you're trying to bake normal maps).
  • PredatorGSR
    Offline / Send Message
    PredatorGSR polycounter lvl 14
    TexTools does the opposite, it will create separate uv shells based on your smoothing groups.
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    yep TexTools reads out the smoothing groups and create UV shells based on that - which is a great start to unwrap.

    The opposite should be possible as well, I will have a look at it later and see if I can wrap together a quick script that does it the other way around.

    In TexTools its the subscript 'fn_34__split_uv_by_smoothing_groups.ms' does it the other way you want it- I'll post it here as a reference for me later:
    fn fn_34__split_uv_by_smoothing_groups =(
    
    	function split_smoothingGroups obj uv=(
    		print("process. "+(classOf obj.baseObject) as string);
    		if (classOf obj.baseObject == Editable_Poly)then(
    			--format "continue. n";
    			function getSmoothGroups _obj =(
    				local numFaces = _obj.EditablePoly.GetNumFaces();
    				local numSmoothGroups = 0;
    				local notfound = true;
    				for i in 1 to numFaces while notfound do(
    					local face_SG = polyop.getFaceSmoothGroup _obj i;
    					if face_SG == 0 then(
    						notfound = false;
    					)
    					numSmoothGroups = amax #(face_SG, numSmoothGroups);
    				)
    				local SmoothElements = #();
    				if (notfound == true)then(
    					--setup the group arrays
    					for i in 1 to numSmoothGroups do(
    						SmoothElements[i]= #{};
    					)
    					----
    					for i in 1 to numFaces do(
    						local face_SG = polyop.getFaceSmoothGroup _obj i;
    						SmoothElements[face_SG]+= #{i};
    					)
    				)
    				return SmoothElements;
    			)
    			
    			--set the smoothing groups
    			local fcs = obj.EditablePoly.GetNumFaces();
    			local groups= getSmoothGroups obj;
    			
    			if (groups.count == 0)then(
    				--no existing smoothing groups, so define some in the editAble Poly Object
    				messagebox "You dont have smoothing groups on your model to split into. So it will be split with a default of 60 degrees"
    				--format "no smoothing groups!n";
    				
    				modPanel.addModToSelection (Edit_Poly ()) ui:on;
    				local ePoly = obj.modifiers[#Edit_Poly];
    					
    				fcs = ePoly.GetNumFaces();
    				ePoly.SetSelection #Face #{1..fcs}
    				ePoly.autoSmoothThreshold = 60.00;
    				ePoly.ButtonOp #Autosmooth
    				ePoly.SetSelection #Face #{}--select nothing	
    					
    				groups= getSmoothGroups obj;	
    				
    				deleteModifier obj 1;--delete the top modifier	
    				uv.unwrap.edit();--back to the UV editor
    				subobjectLevel = 3;
    				uv.unwrap2.setTVSubObjectMode 3;
    			)	
    				
    			for sel in groups do(
    				if ((maxVersion())[1] >= 10000 )then(
    					uv.unwrap6.selectFacesByNode sel obj;
    				)else(
    					uv.unwrap2.selectFaces sel;
    				)
    				
    				uv.unwrap5.quickPlanarMap();--quick flatten
    				--uv.relaxByEdgeAngle 1 0 1 false;
    				uv.unwrap5.relaxByFaceAngle 1 0 1 false;
    				if ((maxVersion())[1] >= 10000 )then(--de select any faces, max2008 multi obj bug I guess
    					uv.unwrap6.selectFacesByNode #{} obj;
    				)
    			)
    			
    			uv.unwrap2.selectFaces #{1..fcs};
    
    			--fn_21__normalize_uv_shells();
    			
    			uv.unwrap2.pack 1 (0.01) true true false;
    			uv.unwrap2.selectFaces #{};
    		)
    	)
    	
    	if (selection.count > 0)then(--at least an object selected
    		--local obj = selection[1]; 
    		local uv = modPanel.getCurrentObject();
    		if( classof(uv) == Unwrap_UVW)then(
    			if ((maxVersion())[1] >= 10000 )then(
    				for sel in selection do(
    					split_smoothingGroups sel uv;
    				)
    			)else(
    				split_smoothingGroups selection[1] uv;--only do this with the first object in the selection
    			)
    		)
    	)
    )
    
  • Ghostscape
    Offline / Send Message
    Ghostscape polycounter lvl 13
    We've got a tool that does this at work and its pretty dope.

    Prior to having it I'd use TexTool's UV->3d and then manually go and assign smoothing groups to each UV piece, then use it again to put it back together.
  • sebi3110
    renderhjs wrote: »
    yep TexTools reads out the smoothing groups and create UV shells based on that - which is a great start to unwrap.

    The opposite should be possible as well, I will have a look at it later and see if I can wrap together a quick script that does it the other way around.

    Hey,

    thanks in advance! Sounds like a plan =)
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    Done :)

    here is the script:
    function smoothByUVShells=(
    	clearListener();
    	if (getCommandPanelTaskMode() != #modify)then(--make sure we are in the modify panel section
    		setCommandPanelTaskMode #modify;
    	)
    	if (selection.count == 1)then(--at least an object selected
    		local obj = selection[1]; 
    		local uv = modPanel.getCurrentObject();
    		
    		if (classof(uv) != Unwrap_UVW)then(
    			modPanel.addModToSelection (Unwrap_UVW ()) ui:on;
    			uv = modPanel.getCurrentObject();
    		)
    		
    		uv.unwrap.edit();
    		uv.unwrap.edit();
    		uv.unwrap2.setTVSubObjectMode(3);
    
    		local totalFaces = uv.unwrap.numberPolygons();
    		
    		local faceElemArray = #();
    		for f=1 to totalFaces do (
    			faceElemArray[ f ] = 0;
    		)
    		local elem = #();
    		--with redraw off;
    		for f=1 to totalFaces do (
    			if faceElemArray[ f ] == 0 then (
    				uv.unwrap2.selectFaces  #{ f };
    				uv.unwrap2.selectElement();
    				local elemFaces = uv.unwrap2.getSelectedFaces() as array;
    				
    				append elem (uv.unwrap2.getSelectedFaces());
    				for i in elemFaces do (
    					faceElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
    				)
    			)
    		)
    		
    		print("num shells: "+elem.count as string+"t"+totalFaces as string);
    		
    		modPanel.addModToSelection (Edit_Poly ()) ui:on;
    		obj.modifiers[#Edit_Poly].autoSmoothThreshold = 180	
    		for e in elem do(
    			obj.modifiers[#Edit_Poly].SetSelection #Face e;	
    			obj.modifiers[#Edit_Poly].ButtonOp #Autosmooth	
    		)	
    	)
    )
    smoothByUVShells();
    

    just open the maxscript Listener (F11 in 3dsmax) and go: File > New Script...
    within that new script document paste the listed script and then go:
    Tools > Evaluate All
    or hit alternatively CTRL + E

    To use it for objects just make sure:
    • you only select one Object a time
    • the object can carry a UVunwrap modifier and editAble Poly modifier

    The way it works right now is that it will add a UVunwrapModifier (if not already on top of the stack) to determine the UV shells (which faces belong in a group) and after that it adds a editPoly modifier to assign the smoothing groups for the face selections.

    Will add it in the next release of TexTools, until then and otherwise simply use this script and or extend it to a macroscript.
  • sebi3110
    Great! Thanks for the work man =)
    PS: Would you mind if i send you a pm here at polycount if I have other ideas for your textool plugin? :)
  • mLichy
    Hey... would I be able to add that into my toolbar? Giving you credit of course.
  • carlo_c
    renderhjs your scripts are always so useful, thanks for sharing this is a feature I've wanted for a while!
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    mLichy wrote: »
    Hey... would I be able to add that into my toolbar? Giving you credit of course.
    yeah sure, feel free to pick stuff up from textools as well and perhaps improve it :)
  • mLichy
    Awesome, thanks man :).

    I won't take your other stuff, hehe. I did look at your Edit UV code last night though, to see how max 2008+ handles multi object unwrap. I made an Edit UV button for my bar too, and tried to make it pretty smart.

    It was breaking in my 1.2.4 build, so I got that all good now I think.
  • mLichy
    Hey render... do you know how to say getSelectedFaces when doing multiple object unwraps?

    I've tried this.. and it doesn't work for 1 of the object of the 2 in the unwrap, but if I select faces on both, it will.
                   local CurSelLevel = selection[1].Unwrap_UVW.getTVSubObjectMode()
                    uv = modPanel.getCurrentObject();
                   local uvFaces = uv.unwrap2.getSelectedFaces() as array
     if CurSelLevel == 3 and uvFaces.count > 0 then
                        (        
                        
                    
                            uv.unwrap2.selectElement();
    
            
                        )
    
    

    thanks :)
  • kurt_hectic
    Offline / Send Message
    kurt_hectic polycounter lvl 10
    It works perfect!!!

    It help you to bake perfect normals. When uv is broken/splited there MUST also be break/split on smoothing groups.

    thx
  • Sean VanGorder
    Renderhjs - I <3 you. Script works great, thanks.
  • pasha_sevez
    Offline / Send Message
    pasha_sevez polycounter lvl 13
    Textools' "smooth by UV shells" algorithm can't handle arbitrary hard edges - the smoothing solution used in Maya, Softimage and other apps. Now I'm working on the script that will make hard edges by UV borders absolutely precisely. But my original goal was to make tool to transfer hard edges from XSI to MAX - there's no solution to make it correctly - neither OBJ, nor FBX, nor Collada support it fine.
    When finished - I'll share for sure!
  • AlecMoody
    Offline / Send Message
    AlecMoody ngon master
    Yeah, I found this script on polycount about a month ago and use it on all my bakes. really good stuff
  • AnimeAngel
    renderhjs wrote: »
    Done :)

    here is the script:
    function smoothByUVShells=(
        clearListener();
        if (getCommandPanelTaskMode() != #modify)then(--make sure we are in the modify panel section
            setCommandPanelTaskMode #modify;
        )
        if (selection.count == 1)then(--at least an object selected
            local obj = selection[1]; 
            local uv = modPanel.getCurrentObject();
            
            if (classof(uv) != Unwrap_UVW)then(
                modPanel.addModToSelection (Unwrap_UVW ()) ui:on;
                uv = modPanel.getCurrentObject();
            )
            
            uv.unwrap.edit();
            uv.unwrap.edit();
            uv.unwrap2.setTVSubObjectMode(3);
    
            local totalFaces = uv.unwrap.numberPolygons();
            
            local faceElemArray = #();
            for f=1 to totalFaces do (
                faceElemArray[ f ] = 0;
            )
            local elem = #();
            --with redraw off;
            for f=1 to totalFaces do (
                if faceElemArray[ f ] == 0 then (
                    uv.unwrap2.selectFaces  #{ f };
                    uv.unwrap2.selectElement();
                    local elemFaces = uv.unwrap2.getSelectedFaces() as array;
                    
                    append elem (uv.unwrap2.getSelectedFaces());
                    for i in elemFaces do (
                        faceElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
                    )
                )
            )
            
            print("num shells: "+elem.count as string+"t"+totalFaces as string);
            
            modPanel.addModToSelection (Edit_Poly ()) ui:on;
            obj.modifiers[#Edit_Poly].autoSmoothThreshold = 180    
            for e in elem do(
                obj.modifiers[#Edit_Poly].SetSelection #Face e;    
                obj.modifiers[#Edit_Poly].ButtonOp #Autosmooth    
            )    
        )
    )
    smoothByUVShells();
    
    just open the maxscript Listener (F11 in 3dsmax) and go: File > New Script...
    within that new script document paste the listed script and then go:
    Tools > Evaluate All
    or hit alternatively CTRL + E

    To use it for objects just make sure:
    • you only select one Object a time
    • the object can carry a UVunwrap modifier and editAble Poly modifier

    The way it works right now is that it will add a UVunwrapModifier (if not already on top of the stack) to determine the UV shells (which faces belong in a group) and after that it adds a editPoly modifier to assign the smoothing groups for the face selections.

    Will add it in the next release of TexTools, until then and otherwise simply use this script and or extend it to a macroscript.

    Pure win, as always Renderhjs. thanks
  • pasha_sevez
    Offline / Send Message
    pasha_sevez polycounter lvl 13
    Here's a script "UV borders to hard edges". The algorithm is quite simple:
    1) Select UV borders (used part of Renderhjs' script for Roadkill uwrapping)
    2) Convert UV edges selection to EditPoly edges selection (Max is a real pain in the ass!) - my part of the script
    3) Make smoothing groups in hard edges style (used script by Pier Janssen).

    That's it ;) Works pretty simple - select your mesh and press UV2HE button. Script is not perfect - in the places where too many hard edges meet there may be some smoothing defects because Max offers only 32 flags to make mesh look smoothed - and it is actually sometimes not enough.
    macroScript UV_SHELL_BORDER
    category:"XSI2MAX_TRANS"	
    ButtonText:"UV2HE" 
    toolTip:"Convert selected UV shells to borders"
    (
    	
    	----------------------------------------------------------------------
    	------------ Pier Janssen's Hard/Soft edge scripts -------------
    	----------------------------------------------------------------------
    
    	struct hardSoftEdge (
    		--
    		--Get smoothing group integer as bitarray.
    		--Not used in this script, but can be useful for testing.
    		--
    		function getBitsAsArray bits = (
    			arr = #{}
    			for i = 1 to 32 do (
    				arr[i] = bit.get bits i;
    			)
    			return arr;
    		),
    		
    		--
    		--Get shared smoothing groups between two faces.
    		--
    		function getSharedSmGroups obj faces = (
    			local smGroups = #();
    			for f in faces do (
    				append smGroups (polyOp.getFaceSmoothGroup obj f);
    			)
    			local sharedSmGroups = (bit.and smGroups[1] smGroups[2]);
    			return sharedSmGroups;
    		),
    		
    		--
    		--Get adjacent faces.
    		--
    		function getAdjacentFaces obj face = (
    			local vertList = polyOp.getVertsUsingFace obj face;
    			local adjacentFaces = polyOp.getFacesUsingVert obj vertList;
    			deleteItem adjacentFaces face;
    			return adjacentFaces;
    		),
    			
    		--
    		--Removes any smoothing groups that are not shared between the given
    		--face or any of the adjacent faces.
    		--
    		function removeRedundantGroups obj face = (
    			local adjacentFaces = hardSoftEdge.getAdjacentFaces obj face;
    			local nonRedundantGroups = 0;
    			for a in adjacentFaces do (
    				nonRedundantGroups = (bit.or nonRedundantGroups (hardSoftEdge.getSharedSmGroups obj #{face, a}));
    			)
    			polyOp.setFaceSmoothGroup obj face nonRedundantGroups;
    		),
    		
    		--
    		--SET SOFT EDGE
    		--
    		function setSoftEdge obj faces = (
    			--Get shared smoothing groups.
    			local sharedSmGroups = (hardSoftEdge.getSharedSmGroups obj faces);
    
    			--If there are no shared smoothing groups, continue. Otherwise, edge is already soft.
    			if sharedSmGroups == 0 do (
    				--Get adjacent faces.
    				local adjacentFaces = #();
    				for f in faces do (
    					append adjacentFaces (hardSoftEdge.getAdjacentFaces obj f);
    				)
    				adjacentFaces[1] = adjacentFaces[1] - faces;
    				adjacentFaces[2] = adjacentFaces[2] - faces;
    
    				
    				--Determine impossible smoothing groups.
    				local impSmGrps = 0;
    				local c = 1;
    				for f in faces do (
    					--Get current face smoothing group.
    					local smGrpCurFace = (polyOp.getFaceSmoothGroup obj f);
    
    					--Get adjacent non-shared smoothing groups and add them to impSmGrps.
    					for a in adjacentFaces[c] do (
    						local smGrpAdjFace = (polyOp.getFaceSmoothGroup obj a);
    						local nonShared = (bit.and smGrpAdjFace (bit.not smGrpCurFace));
    						impSmGrps = (bit.or impSmGrps nonShared);
    					)
    					c += 1;
    				)
    			
    				--Check for first available smoothing group.
    				for i = 1 to 32 do (
    					if (bit.get impSmGrps i) == false do (
    						--Add smoothing group.
    						polyOp.setFaceSmoothGroup obj faces (2 ^ (i - 1)) add:true;
    						exit;
    					)
    				)
    				
    				for f in faces do (
    					hardSoftEdge.removeRedundantGroups obj f;
    				)
    			)
    			
    		),
    			
    		--
    		--SET HARD EDGE
    		--
    		function setHardEdge obj faces = (
    			--Get shared smoothing groups.
    			local sharedSmGroups = (hardSoftEdge.getSharedSmGroups obj faces);
    
    			
    			--If there are shared smoothing groups, continue. Otherwise, edge is already hard.
    			if sharedSmGroups != 0 do (
    				--Detect which edges will need resmoothing.
    				local resmoothEdges = #();
    				for f in faces do (
    					--Get edges using face.
    					local faceEdges = (polyOp.getEdgesUsingFace obj f);
    				
    					for e in faceEdges do (
    						--Get faces using edge.
    						local edgeFaces = (polyOp.getFacesUsingEdge obj e);
    						--Only use non-open edges.
    						if edgeFaces.numberSet > 1 do (
    							
    							for ef in edgeFaces do (
    								--Only look at faces that are not the faces to smooth initially.
    								if faces[ef] == false do (
    									--Get face smoothing group.
    									local efSmGroups = (polyOp.getFaceSmoothGroup obj ef);
    									--Compare with smoothing groups that will be removed. 
    									--If there are shared smoothing groups between these faces, the edge 
    									--will need resmoothing.
    									if (bit.and efSmGroups sharedSmGroups) != 0 do (
    										append resmoothEdges edgeFaces;
    									)
    								)
    							)
    							
    						)
    					)
    				)
    
    				--Remove shared smoothing groups.
    				for f in faces do (
    					polyOp.setFaceSmoothGroup obj f (bit.xor (polyOp.getFaceSmoothGroup obj f) sharedSmGroups);				
    				)
    				
    				--Resmooth surrounding edges.
    				for e in resmoothEdges do (
    					hardSoftEdge.setSoftEdge obj e;
    				)
    				
    				for f in faces do (
    					hardSoftEdge.removeRedundantGroups obj f;
    				)
    			)
    		),
    			
    		--
    		--SET EDGE
    		--
    		function setEdge mode = (
    			local obj = $;
    			local objClass = (classOf obj);
    			
    			if (objClass == Editable_Poly) then (
    				--Get edge selection.
    				local selEdges = (polyOp.getEdgeSelection obj);
    
    				print selEdges.numberSet;
    				print (polyOp.getNumEdges obj)
    				
    				if selEdges.numberSet == (polyOp.getNumEdges obj) then (
    					--All edges selected, so assign one smoothing group to all faces.
    					if mode == "soft" then (
    						polyOp.setFaceSmoothGroup obj #{1..(polyOp.getNumFaces obj)} 1;
    					) else if mode == "hard" do (
    						polyOp.setFaceSmoothGroup obj #{1..(polyOp.getNumFaces obj)} 0;
    					)
    				) else (
    					for e in selEdges do (
    						--Get faces used by selected edges.
    						local edgeFaces = (polyOp.getFacesUsingEdge obj e);
    						--If not an open edge, continue.
    						if edgeFaces.numberSet > 1 then (
    							if mode == "soft" then (
    								--Soft edge.
    								hardSoftEdge.setSoftEdge obj edgeFaces;
    							) else if mode == "hard" do (
    								--Hard edge.
    								hardSoftEdge.setHardEdge obj edgeFaces;
    							)
    						) else (
    							print ("Set " + mode + " edge warning: Open edge!");
    						)
    					)
    				)
    			) else if (objClass == PolyMeshObject) then (
    				MessageBox "Sorry, Edit Poly modifiers are not supported." title:"Error";
    			) else if (objClass == Editable_Mesh) then (
    				MessageBox "Editable Mesh is not supported.\nUse Editable Poly instead, it's better." title:"Error";
    			) else (
    				print ("Set " + mode + " edge error: No Editable Poly selected!");
    			)
    			
    		)
    
    	)
    	----------------------------------------------------------------------
    	-----------------END OF HARD/SOFT EDGE---------------------
    	----------------------------------------------------------------------
    
    		convertTo selection[1] PolyMeshObject
    		modPanel.addModToSelection (Unwrap_UVW ()) ui:on
    
    	
    		local obj = selection[1];
    		local uv = 	obj.modifiers[ #unwrap_uvw ] ;
    	
    		uv.unwrap2.setTVSubObjectMode 2;
    			
    		-----------------------------------------------------------------------------------------------------
    		----- Part of 'get open edges' function from TexTools' Roadkill script - by Renderhjs ------
    		-----------------------------------------------------------------------------------------------------
    			
    		--get a list of all Shells filled with the Bitarary selections for each shell
    		uv.selectFaces #{1..uv.unwrap.numberPolygons()};
    		uv.facetoedgeselect();--all edges are selected
    		local allEdgesSelection = uv.unwrap2.getSelectedEdges();
    		local edgeElemArray = #();
    		for ed in allEdgesSelection do (
    			edgeElemArray[ ed ] = 0;
    		)
    		local elem = #();
    		with redraw off;
    		for ed in allEdgesSelection do (
    			if edgeElemArray[ ed ] == 0 then (
    				uv.unwrap2.selectEdges  #{ ed };
    				uv.unwrap2.selectElement();
    				local elemEdges = uv.unwrap2.getSelectedEdges() as array;
    				if elemEdges.count > 2 then (-- Ignore elements with less than 3 UV vertices.
    					append elem (uv.unwrap2.getSelectedEdges());
    					for i in elemEdges do (
    						edgeElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
    					)
    				)
    			)
    		)
    
    		--now get all the open Edges for all shells (elem = shell)
    		local openEdgesSelection = #{};--the final open Edges selection
    		for e in elem do(
    			local selections = #();
    			local selectionsNum = #();
    			local maxNum = 0;
    			for ed in e do(
    				uv.unwrap2.selectEdges  #{ ed };
    				uv.unwrap2.openEdgeSelect();
    				if ((uv.getselectededges()).numberset > 1)then(
    					local sel = ((uv.unwrap2.getSelectedEdges()) - #{ ed });--substract the original edge, kinda a bug in the openEdgeSelect method
    					openEdgesSelection+= sel;--add it to the bitarray (only not yet switched bits will be drawn)
    				)
    			)	 
    		)
    		
    		uv.unwrap2.selectEdges openEdgesSelection;
    		
    		local vertEdges = #();
    		
    		if (openEdgesSelection.numberset > 0) then
    		(
    			uv.unwrap2.setTVSubObjectMode 1;--otherwise Geo Verts are not recognized
    			for e in openEdgesSelection do
    			(
    				uv.unwrap2.selectEdges  #{ e };
    				uv.edgeToVertSelect();
    				local vertList = uv.unwrap5.getSelectedGeomVerts();
    				if ( vertList.numberSet == 2 ) then 
    				(
    					append vertEdges (vertList as array);
    				)
    				else
    				(
    					exit;--error, some messy faces somewhere, where 1 edge connect with more than 2 geo verts
    				)
    			)
    			--print("edges saved : "+openEdgesSelection.numberset as string+"x");
    		)
    		
    		------------------------------------------------------
    		----- END OF Roadkill script - by Renderhjs ------
    		------------------------------------------------------
    		
    		--print vertEdges;
    		--print "-----------"
    		modPanel.addModToSelection (Edit_Poly ()) ui:on;
    		local epoly = obj.modifiers[#Edit_Poly];
    		
    		local	edgeSelSet=#{}
    		
    		fn edge_from_verts v=
    		(
    			( polyOp.getEdgesUsingVert obj #{v[1]} ) - ( ( polyOp.getEdgesUsingVert obj #{v[1]} ) - ( polyOp.getEdgesUsingVert obj #{v[2]} ) )
    		)	
    			
    		for v = 1 to vertEdges.count do edgeSelSet += ( edge_from_verts vertEdges[v] );
    		modPanel.setCurrentObject epoly 
    		subobjectLevel = 2
    		epoly.Select #Edge edgeSelSet;
    		--print edgeSelSet;
    		
    		maxOps.CollapseNodeTo obj 1 off
    		
    		hardSoftEdge.setEdge "hard";
    
    		
    )
    
    

    It's important to notice that script gives best results on hard surface models but for organic models this solution isn't a good idea - because of an undesired hard edges over the model. Use similar TexTools' function instead.

    As I told above - this script was meant to be a part of XSI to Max exporter - to handle hard edges correctly.

    Looking forward to your comments.
  • ZacD
    Offline / Send Message
    ZacD ngon master
    @renderhjs

    When I tried that script it made every edge a hard edge, any ideas?
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    Faceted material?
    not sure has been a long while since I wrote that. I never had my hands on 3dsMax 2012 or 2011 so I don't know if something is different in those versions. Maybe a wacky modifier stack that might not ideal for what this script does. Try it on a snapshot of your model - aka copy it and merge it down to just the base poly object. Otherwise is it possible to show a screen of the model, or modifier stack
  • Stromberg90
    Offline / Send Message
    Stromberg90 polycounter lvl 11
    I was looking for this myself and found out that turbo tools has this under the uv section.
    It has some other cool things to :)

    http://www.matthewlichy.com/turboTools.html
  • Cordell Felix
    Offline / Send Message
    Cordell Felix polycounter lvl 9
    That script worked great. Thanks renderhjs!
  • - Wraith -
    Offline / Send Message
    - Wraith - polycounter lvl 5
    you can use PolyUnwrapper plugin, on polytools3d.com, but it's not free.
  • M.Palko
    Offline / Send Message
    M.Palko polycounter lvl 4
    I had some problems with the script posted above, It seemed to give me some incorrect results on more complex models. I decided to have a go at it, so here's my take.

    Basically I build a list of all the UV islands using a UV modifier, then set the auto smooth threshold to 180 and run auto smooth on each island.

    I'll post the source here too incase my website ever goes down in the future.
    ------------------------------------- UV Islands To Smoothing Groups -------------------------------------
    -- By Martin Palko 
    -- [url]www.martinpalko.com[/url]
    --
    --
    -- Tested:	- Max 2014
    -- 
    -- Install:	- Drag script into max, then assign it to a hotkey or menu bar in "Customize -> User Interface"
    --			- Can be found by setting the category to "MP_Tools" while in the "Main UI" group
    --
    -- Usage:	- Select any mesh object, and activate the script. Choose a UV channel and hit run.
    --
    -- Notes:	- This script will not affect your modifier stack, it will simply put an edit poly with the 
    --				smoothing groups at the top.
    --			- Undo is not currently supported, so save before, just in case. You can however, just delete 
    --				the edit poly modifier to get your old smoothing groups back.
    --			- Selecting any channel other than UV channel 1 will show a dialogue box asking if you want to 
    --				reset UVs. You must hit yes for it to work based on the channel you've specified. This 
    --				will NOT affect the model's existing UV set.
    
    macroscript UVIslandsToSmoothing 
    category:"MP_Tools"
    buttontext:"UV 2 Smooth"
    tooltip:"Convert UV islands to smoothing groups"
    autoUndoEnabled:false
    (	
    	-- Helper function to get the index of the first true element in a bit array
    	function getFirstActiveInBitarray aBitArray =
    	(
    		for i = 1 to aBitArray.count do
    		(
    			if aBitArray[i] == true do return i
    		)
    		-- return 0 if none are found active
    		return 0
    	)
    
    	-- Actually performs the operation on the currently selected object
    	function ConvertUVIslandsToSmoothingGroups aUVChannel =
    	(
    		if $ != undefined then
    		(			
    			modPanel.addModToSelection(Edit_Poly()) ui:on
    			local editPoly = $.modifiers[#edit_poly]
    			
    			local facesDone = #{} -- empty bit array since no faces are done
    			local allFaces = #{1.. polyop.getNumFaces $}
    			local facesNotDone = allFaces
    			
    			-- Stick on a UVW modifier
    			modPanel.addModToSelection (Unwrap_UVW ()) ui:on
    			local uv_modifier = $.modifiers[#unwrap_uvw]				
    			uv_modifier.unwrap2.setTVSubObjectMode 3 -- Use face selection
    				
    			if (aUVChannel != 1) then -- Only need to mess with this if it's not default
    			(
    				uv_modifier.unwrap.setMapChannel aUVChannel
    				uv_modifier.unwrap.reset()
    				forcecompleteredraw dodisabled:true -- Hacky fix for a bug, see [url]http://www.polycount.com/forum/showthread.php?t=97059[/url]
    			)
    				
    			local uv_islands = #() -- Empty array that will store bitarrays of all our UV islands
    			local abort = false -- Abort boolean for breaking out of the loop and avoid the performance penalty of using break
    			
    			-- Build array of UV islands
    			while (facesNotDone.isEmpty == false and abort == false) do
    			(				
    				nextFace = getFirstActiveInBitarray facesNotDone -- Get next face that hasn't been processed yet
    				
    				uv_modifier.unwrap2.selectFaces #{nextFace} -- Select that face
    				uv_modifier.unwrap2.selectElement() -- Grow selection to element
    				uv_island = uv_modifier.unwrap2.getSelectedFaces()	-- Get a bitaray of all those faces (representing a UV island)
    				
    				-- Update faces done/not done bit masks
    				facesDone += uv_island
    				facesNotDone -= uv_island
    				
    				insertItem uv_island uv_islands (uv_islands.count + 1) -- Add that bitarray to our array of UV islands
    				
    				if uv_islands.count > allFaces.count then -- this should never happen, if it does means we are in an infinite loop and will crash max, so bail
    				(
    					abort = true
    					print ("Error, calculated too many islands, something went wrong")
    				)
    			)
    			
    			deletemodifier $ uv_modifier -- Don't need the UV modifier anymore
    			
    			editPoly.autoSmoothThreshold = 180.0 -- If we auto smooth, it should always be in the same smoothing group
    			
    			for island = 1 to uv_islands.count do -- Select and auto smooth each UV island
    			(
    				editPoly.SetSelection #Face uv_islands[island]
    				editPoly.ButtonOp #Autosmooth
    			)
    		)
    	)
    	
    	local isOpen = false -- Store if the rollout is open or closed
    	
    	rollout UV2SmoothRollout "UV_2_Smooth"
    	(
    		spinner UVChannelSpinner "UV Channel" range:[1,99,1] type:#integer
    		button GoBtn "        Run        "
    		
    		on GoBtn pressed do
    		(
    			ConvertUVIslandsToSmoothingGroups (UVChannelSpinner.value)
    			destroyDialog UV2SmoothRollout -- Close rollout after running
    		)
    		
    		on UV2SmoothRollout close do
    		(
    			isOpen = false
    			updateToolbarButtons() -- Update the toolbar icon when closing
    		)
    	)
    	
    	on execute do
    	(
    		if isOpen then --if open, close it
    		(
    			destroyDialog UV2SmoothRollout
    		)
    		
    		else --if closed, open it
    		(
    			createDialog UV2SmoothRollout
    			isOpen = true
    		)
    	)
      
    	on isChecked return isOpen --return the flag
    	
    	on isEnabled do
    	(
    		-- Need an editable poly selected to work
    		if $ == undefined then
    		(
    			-- Close the window if it's open and it shouldn't be
    			if (isOpen) then
    				destroyDialog UV2SmoothRollout
    			
    			return false
    		)
    		else
    			return true
    	)
    )
    
  • Waz
    Offline / Send Message
    Waz polycounter lvl 17
    M.Palko Thanks for that script! That has really helped lower vertex counts for me automatically! I'll buy you a beer if we every meetup.
  • jackrabbit82
    Offline / Send Message
    jackrabbit82 polycounter lvl 6
    I know this an old thread, but does anyone know why in 3dsmax 2012 the function getSmoothGroups from Textool seems to be failing ? i've been trying to wrap my head around what might be causing Textool to ignore my SM Group and make a default one by angle instead? I wish i was still in 2010 only for this tool haha !  
Sign In or Register to comment.