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:
    1. fn fn_34__split_uv_by_smoothing_groups =(
    2.  
    3. function split_smoothingGroups obj uv=(
    4. print("process. "+(classOf obj.baseObject) as string);
    5. if (classOf obj.baseObject == Editable_Poly)then(
    6. --format "continue. n";
    7. function getSmoothGroups _obj =(
    8. local numFaces = _obj.EditablePoly.GetNumFaces();
    9. local numSmoothGroups = 0;
    10. local notfound = true;
    11. for i in 1 to numFaces while notfound do(
    12. local face_SG = polyop.getFaceSmoothGroup _obj i;
    13. if face_SG == 0 then(
    14. notfound = false;
    15. )
    16. numSmoothGroups = amax #(face_SG, numSmoothGroups);
    17. )
    18. local SmoothElements = #();
    19. if (notfound == true)then(
    20. --setup the group arrays
    21. for i in 1 to numSmoothGroups do(
    22. SmoothElements[i]= #{};
    23. )
    24. ----
    25. for i in 1 to numFaces do(
    26. local face_SG = polyop.getFaceSmoothGroup _obj i;
    27. SmoothElements[face_SG]+= #{i};
    28. )
    29. )
    30. return SmoothElements;
    31. )
    32. --set the smoothing groups
    33. local fcs = obj.EditablePoly.GetNumFaces();
    34. local groups= getSmoothGroups obj;
    35. if (groups.count == 0)then(
    36. --no existing smoothing groups, so define some in the editAble Poly Object
    37. messagebox "You dont have smoothing groups on your model to split into. So it will be split with a default of 60 degrees"
    38. --format "no smoothing groups!n";
    39. modPanel.addModToSelection (Edit_Poly ()) ui:on;
    40. local ePoly = obj.modifiers[#Edit_Poly];
    41. fcs = ePoly.GetNumFaces();
    42. ePoly.SetSelection #Face #{1..fcs}
    43. ePoly.autoSmoothThreshold = 60.00;
    44. ePoly.ButtonOp #Autosmooth
    45. ePoly.SetSelection #Face #{}--select nothing
    46. groups= getSmoothGroups obj;
    47. deleteModifier obj 1;--delete the top modifier
    48. uv.unwrap.edit();--back to the UV editor
    49. subobjectLevel = 3;
    50. uv.unwrap2.setTVSubObjectMode 3;
    51. )
    52. for sel in groups do(
    53. if ((maxVersion())[1] >= 10000 )then(
    54. uv.unwrap6.selectFacesByNode sel obj;
    55. )else(
    56. uv.unwrap2.selectFaces sel;
    57. )
    58. uv.unwrap5.quickPlanarMap();--quick flatten
    59. --uv.relaxByEdgeAngle 1 0 1 false;
    60. uv.unwrap5.relaxByFaceAngle 1 0 1 false;
    61. if ((maxVersion())[1] >= 10000 )then(--de select any faces, max2008 multi obj bug I guess
    62. uv.unwrap6.selectFacesByNode #{} obj;
    63. )
    64. )
    65. uv.unwrap2.selectFaces #{1..fcs};
    66.  
    67. --fn_21__normalize_uv_shells();
    68. uv.unwrap2.pack 1 (0.01) true true false;
    69. uv.unwrap2.selectFaces #{};
    70. )
    71. )
    72. if (selection.count > 0)then(--at least an object selected
    73. --local obj = selection[1];
    74. local uv = modPanel.getCurrentObject();
    75. if( classof(uv) == Unwrap_UVW)then(
    76. if ((maxVersion())[1] >= 10000 )then(
    77. for sel in selection do(
    78. split_smoothingGroups sel uv;
    79. )
    80. )else(
    81. split_smoothingGroups selection[1] uv;--only do this with the first object in the selection
    82. )
    83. )
    84. )
    85. )
  • 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:
    1. function smoothByUVShells=(
    2. clearListener();
    3. if (getCommandPanelTaskMode() != #modify)then(--make sure we are in the modify panel section
    4. setCommandPanelTaskMode #modify;
    5. )
    6. if (selection.count == 1)then(--at least an object selected
    7. local obj = selection[1];
    8. local uv = modPanel.getCurrentObject();
    9. if (classof(uv) != Unwrap_UVW)then(
    10. modPanel.addModToSelection (Unwrap_UVW ()) ui:on;
    11. uv = modPanel.getCurrentObject();
    12. )
    13. uv.unwrap.edit();
    14. uv.unwrap.edit();
    15. uv.unwrap2.setTVSubObjectMode(3);
    16.  
    17. local totalFaces = uv.unwrap.numberPolygons();
    18. local faceElemArray = #();
    19. for f=1 to totalFaces do (
    20. faceElemArray[ f ] = 0;
    21. )
    22. local elem = #();
    23. --with redraw off;
    24. for f=1 to totalFaces do (
    25. if faceElemArray[ f ] == 0 then (
    26. uv.unwrap2.selectFaces #{ f };
    27. uv.unwrap2.selectElement();
    28. local elemFaces = uv.unwrap2.getSelectedFaces() as array;
    29. append elem (uv.unwrap2.getSelectedFaces());
    30. for i in elemFaces do (
    31. faceElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
    32. )
    33. )
    34. )
    35. print("num shells: "+elem.count as string+"t"+totalFaces as string);
    36. modPanel.addModToSelection (Edit_Poly ()) ui:on;
    37. obj.modifiers[#Edit_Poly].autoSmoothThreshold = 180
    38. for e in elem do(
    39. obj.modifiers[#Edit_Poly].SetSelection #Face e;
    40. obj.modifiers[#Edit_Poly].ButtonOp #Autosmooth
    41. )
    42. )
    43. )
    44. 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.
    1. local CurSelLevel = selection[1].Unwrap_UVW.getTVSubObjectMode()
    2. uv = modPanel.getCurrentObject();
    3. local uvFaces = uv.unwrap2.getSelectedFaces() as array
    4. if CurSelLevel == 3 and uvFaces.count > 0 then
    5. (
    6. uv.unwrap2.selectElement();
    7.  
    8. )
    9.  

    thanks :)
  • kurt_hectic
    Offline / Send Message
    kurt_hectic polycounter lvl 13
    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:
    1. function smoothByUVShells=(
    2. clearListener();
    3. if (getCommandPanelTaskMode() != #modify)then(--make sure we are in the modify panel section
    4. setCommandPanelTaskMode #modify;
    5. )
    6. if (selection.count == 1)then(--at least an object selected
    7. local obj = selection[1];
    8. local uv = modPanel.getCurrentObject();
    9. if (classof(uv) != Unwrap_UVW)then(
    10. modPanel.addModToSelection (Unwrap_UVW ()) ui:on;
    11. uv = modPanel.getCurrentObject();
    12. )
    13. uv.unwrap.edit();
    14. uv.unwrap.edit();
    15. uv.unwrap2.setTVSubObjectMode(3);
    16.  
    17. local totalFaces = uv.unwrap.numberPolygons();
    18. local faceElemArray = #();
    19. for f=1 to totalFaces do (
    20. faceElemArray[ f ] = 0;
    21. )
    22. local elem = #();
    23. --with redraw off;
    24. for f=1 to totalFaces do (
    25. if faceElemArray[ f ] == 0 then (
    26. uv.unwrap2.selectFaces #{ f };
    27. uv.unwrap2.selectElement();
    28. local elemFaces = uv.unwrap2.getSelectedFaces() as array;
    29. append elem (uv.unwrap2.getSelectedFaces());
    30. for i in elemFaces do (
    31. faceElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
    32. )
    33. )
    34. )
    35. print("num shells: "+elem.count as string+"t"+totalFaces as string);
    36. modPanel.addModToSelection (Edit_Poly ()) ui:on;
    37. obj.modifiers[#Edit_Poly].autoSmoothThreshold = 180
    38. for e in elem do(
    39. obj.modifiers[#Edit_Poly].SetSelection #Face e;
    40. obj.modifiers[#Edit_Poly].ButtonOp #Autosmooth
    41. )
    42. )
    43. )
    44. 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.
    1. macroScript UV_SHELL_BORDER
    2. category:"XSI2MAX_TRANS"
    3. ButtonText:"UV2HE"
    4. toolTip:"Convert selected UV shells to borders"
    5. (
    6. ----------------------------------------------------------------------
    7. ------------ Pier Janssen's Hard/Soft edge scripts -------------
    8. ----------------------------------------------------------------------
    9.  
    10. struct hardSoftEdge (
    11. --
    12. --Get smoothing group integer as bitarray.
    13. --Not used in this script, but can be useful for testing.
    14. --
    15. function getBitsAsArray bits = (
    16. arr = #{}
    17. for i = 1 to 32 do (
    18. arr[i] = bit.get bits i;
    19. )
    20. return arr;
    21. ),
    22. --
    23. --Get shared smoothing groups between two faces.
    24. --
    25. function getSharedSmGroups obj faces = (
    26. local smGroups = #();
    27. for f in faces do (
    28. append smGroups (polyOp.getFaceSmoothGroup obj f);
    29. )
    30. local sharedSmGroups = (bit.and smGroups[1] smGroups[2]);
    31. return sharedSmGroups;
    32. ),
    33. --
    34. --Get adjacent faces.
    35. --
    36. function getAdjacentFaces obj face = (
    37. local vertList = polyOp.getVertsUsingFace obj face;
    38. local adjacentFaces = polyOp.getFacesUsingVert obj vertList;
    39. deleteItem adjacentFaces face;
    40. return adjacentFaces;
    41. ),
    42. --
    43. --Removes any smoothing groups that are not shared between the given
    44. --face or any of the adjacent faces.
    45. --
    46. function removeRedundantGroups obj face = (
    47. local adjacentFaces = hardSoftEdge.getAdjacentFaces obj face;
    48. local nonRedundantGroups = 0;
    49. for a in adjacentFaces do (
    50. nonRedundantGroups = (bit.or nonRedundantGroups (hardSoftEdge.getSharedSmGroups obj #{face, a}));
    51. )
    52. polyOp.setFaceSmoothGroup obj face nonRedundantGroups;
    53. ),
    54. --
    55. --SET SOFT EDGE
    56. --
    57. function setSoftEdge obj faces = (
    58. --Get shared smoothing groups.
    59. local sharedSmGroups = (hardSoftEdge.getSharedSmGroups obj faces);
    60.  
    61. --If there are no shared smoothing groups, continue. Otherwise, edge is already soft.
    62. if sharedSmGroups == 0 do (
    63. --Get adjacent faces.
    64. local adjacentFaces = #();
    65. for f in faces do (
    66. append adjacentFaces (hardSoftEdge.getAdjacentFaces obj f);
    67. )
    68. adjacentFaces[1] = adjacentFaces[1] - faces;
    69. adjacentFaces[2] = adjacentFaces[2] - faces;
    70.  
    71. --Determine impossible smoothing groups.
    72. local impSmGrps = 0;
    73. local c = 1;
    74. for f in faces do (
    75. --Get current face smoothing group.
    76. local smGrpCurFace = (polyOp.getFaceSmoothGroup obj f);
    77.  
    78. --Get adjacent non-shared smoothing groups and add them to impSmGrps.
    79. for a in adjacentFaces[c] do (
    80. local smGrpAdjFace = (polyOp.getFaceSmoothGroup obj a);
    81. local nonShared = (bit.and smGrpAdjFace (bit.not smGrpCurFace));
    82. impSmGrps = (bit.or impSmGrps nonShared);
    83. )
    84. c += 1;
    85. )
    86. --Check for first available smoothing group.
    87. for i = 1 to 32 do (
    88. if (bit.get impSmGrps i) == false do (
    89. --Add smoothing group.
    90. polyOp.setFaceSmoothGroup obj faces (2 ^ (i - 1)) add:true;
    91. exit;
    92. )
    93. )
    94. for f in faces do (
    95. hardSoftEdge.removeRedundantGroups obj f;
    96. )
    97. )
    98. ),
    99. --
    100. --SET HARD EDGE
    101. --
    102. function setHardEdge obj faces = (
    103. --Get shared smoothing groups.
    104. local sharedSmGroups = (hardSoftEdge.getSharedSmGroups obj faces);
    105.  
    106. --If there are shared smoothing groups, continue. Otherwise, edge is already hard.
    107. if sharedSmGroups != 0 do (
    108. --Detect which edges will need resmoothing.
    109. local resmoothEdges = #();
    110. for f in faces do (
    111. --Get edges using face.
    112. local faceEdges = (polyOp.getEdgesUsingFace obj f);
    113. for e in faceEdges do (
    114. --Get faces using edge.
    115. local edgeFaces = (polyOp.getFacesUsingEdge obj e);
    116. --Only use non-open edges.
    117. if edgeFaces.numberSet > 1 do (
    118. for ef in edgeFaces do (
    119. --Only look at faces that are not the faces to smooth initially.
    120. if faces[ef] == false do (
    121. --Get face smoothing group.
    122. local efSmGroups = (polyOp.getFaceSmoothGroup obj ef);
    123. --Compare with smoothing groups that will be removed.
    124. --If there are shared smoothing groups between these faces, the edge
    125. --will need resmoothing.
    126. if (bit.and efSmGroups sharedSmGroups) != 0 do (
    127. append resmoothEdges edgeFaces;
    128. )
    129. )
    130. )
    131. )
    132. )
    133. )
    134.  
    135. --Remove shared smoothing groups.
    136. for f in faces do (
    137. polyOp.setFaceSmoothGroup obj f (bit.xor (polyOp.getFaceSmoothGroup obj f) sharedSmGroups);
    138. )
    139. --Resmooth surrounding edges.
    140. for e in resmoothEdges do (
    141. hardSoftEdge.setSoftEdge obj e;
    142. )
    143. for f in faces do (
    144. hardSoftEdge.removeRedundantGroups obj f;
    145. )
    146. )
    147. ),
    148. --
    149. --SET EDGE
    150. --
    151. function setEdge mode = (
    152. local obj = $;
    153. local objClass = (classOf obj);
    154. if (objClass == Editable_Poly) then (
    155. --Get edge selection.
    156. local selEdges = (polyOp.getEdgeSelection obj);
    157.  
    158. print selEdges.numberSet;
    159. print (polyOp.getNumEdges obj)
    160. if selEdges.numberSet == (polyOp.getNumEdges obj) then (
    161. --All edges selected, so assign one smoothing group to all faces.
    162. if mode == "soft" then (
    163. polyOp.setFaceSmoothGroup obj #{1..(polyOp.getNumFaces obj)} 1;
    164. ) else if mode == "hard" do (
    165. polyOp.setFaceSmoothGroup obj #{1..(polyOp.getNumFaces obj)} 0;
    166. )
    167. ) else (
    168. for e in selEdges do (
    169. --Get faces used by selected edges.
    170. local edgeFaces = (polyOp.getFacesUsingEdge obj e);
    171. --If not an open edge, continue.
    172. if edgeFaces.numberSet > 1 then (
    173. if mode == "soft" then (
    174. --Soft edge.
    175. hardSoftEdge.setSoftEdge obj edgeFaces;
    176. ) else if mode == "hard" do (
    177. --Hard edge.
    178. hardSoftEdge.setHardEdge obj edgeFaces;
    179. )
    180. ) else (
    181. print ("Set " + mode + " edge warning: Open edge!");
    182. )
    183. )
    184. )
    185. ) else if (objClass == PolyMeshObject) then (
    186. MessageBox "Sorry, Edit Poly modifiers are not supported." title:"Error";
    187. ) else if (objClass == Editable_Mesh) then (
    188. MessageBox "Editable Mesh is not supported.\nUse Editable Poly instead, it's better." title:"Error";
    189. ) else (
    190. print ("Set " + mode + " edge error: No Editable Poly selected!");
    191. )
    192. )
    193.  
    194. )
    195. ----------------------------------------------------------------------
    196. -----------------END OF HARD/SOFT EDGE---------------------
    197. ----------------------------------------------------------------------
    198.  
    199. convertTo selection[1] PolyMeshObject
    200. modPanel.addModToSelection (Unwrap_UVW ()) ui:on
    201.  
    202. local obj = selection[1];
    203. local uv = obj.modifiers[ #unwrap_uvw ] ;
    204. uv.unwrap2.setTVSubObjectMode 2;
    205. -----------------------------------------------------------------------------------------------------
    206. ----- Part of 'get open edges' function from TexTools' Roadkill script - by Renderhjs ------
    207. -----------------------------------------------------------------------------------------------------
    208. --get a list of all Shells filled with the Bitarary selections for each shell
    209. uv.selectFaces #{1..uv.unwrap.numberPolygons()};
    210. uv.facetoedgeselect();--all edges are selected
    211. local allEdgesSelection = uv.unwrap2.getSelectedEdges();
    212. local edgeElemArray = #();
    213. for ed in allEdgesSelection do (
    214. edgeElemArray[ ed ] = 0;
    215. )
    216. local elem = #();
    217. with redraw off;
    218. for ed in allEdgesSelection do (
    219. if edgeElemArray[ ed ] == 0 then (
    220. uv.unwrap2.selectEdges #{ ed };
    221. uv.unwrap2.selectElement();
    222. local elemEdges = uv.unwrap2.getSelectedEdges() as array;
    223. if elemEdges.count > 2 then (-- Ignore elements with less than 3 UV vertices.
    224. append elem (uv.unwrap2.getSelectedEdges());
    225. for i in elemEdges do (
    226. edgeElemArray[ i ] = elem.count; -- Mark these vertices with their element number in vertElemArray.
    227. )
    228. )
    229. )
    230. )
    231.  
    232. --now get all the open Edges for all shells (elem = shell)
    233. local openEdgesSelection = #{};--the final open Edges selection
    234. for e in elem do(
    235. local selections = #();
    236. local selectionsNum = #();
    237. local maxNum = 0;
    238. for ed in e do(
    239. uv.unwrap2.selectEdges #{ ed };
    240. uv.unwrap2.openEdgeSelect();
    241. if ((uv.getselectededges()).numberset > 1)then(
    242. local sel = ((uv.unwrap2.getSelectedEdges()) - #{ ed });--substract the original edge, kinda a bug in the openEdgeSelect method
    243. openEdgesSelection+= sel;--add it to the bitarray (only not yet switched bits will be drawn)
    244. )
    245. )
    246. )
    247. uv.unwrap2.selectEdges openEdgesSelection;
    248. local vertEdges = #();
    249. if (openEdgesSelection.numberset > 0) then
    250. (
    251. uv.unwrap2.setTVSubObjectMode 1;--otherwise Geo Verts are not recognized
    252. for e in openEdgesSelection do
    253. (
    254. uv.unwrap2.selectEdges #{ e };
    255. uv.edgeToVertSelect();
    256. local vertList = uv.unwrap5.getSelectedGeomVerts();
    257. if ( vertList.numberSet == 2 ) then
    258. (
    259. append vertEdges (vertList as array);
    260. )
    261. else
    262. (
    263. exit;--error, some messy faces somewhere, where 1 edge connect with more than 2 geo verts
    264. )
    265. )
    266. --print("edges saved : "+openEdgesSelection.numberset as string+"x");
    267. )
    268. ------------------------------------------------------
    269. ----- END OF Roadkill script - by Renderhjs ------
    270. ------------------------------------------------------
    271. --print vertEdges;
    272. --print "-----------"
    273. modPanel.addModToSelection (Edit_Poly ()) ui:on;
    274. local epoly = obj.modifiers[#Edit_Poly];
    275. local edgeSelSet=#{}
    276. fn edge_from_verts v=
    277. (
    278. ( polyOp.getEdgesUsingVert obj #{v[1]} ) - ( ( polyOp.getEdgesUsingVert obj #{v[1]} ) - ( polyOp.getEdgesUsingVert obj #{v[2]} ) )
    279. )
    280. for v = 1 to vertEdges.count do edgeSelSet += ( edge_from_verts vertEdges[v] );
    281. modPanel.setCurrentObject epoly
    282. subobjectLevel = 2
    283. epoly.Select #Edge edgeSelSet;
    284. --print edgeSelSet;
    285. maxOps.CollapseNodeTo obj 1 off
    286. hardSoftEdge.setEdge "hard";
    287.  
    288. )
    289.  

    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.
    1. ------------------------------------- UV Islands To Smoothing Groups -------------------------------------
    2. -- By Martin Palko
    3. -- [url]www.martinpalko.com[/url]
    4. --
    5. --
    6. -- Tested: - Max 2014
    7. --
    8. -- Install: - Drag script into max, then assign it to a hotkey or menu bar in "Customize -> User Interface"
    9. -- - Can be found by setting the category to "MP_Tools" while in the "Main UI" group
    10. --
    11. -- Usage: - Select any mesh object, and activate the script. Choose a UV channel and hit run.
    12. --
    13. -- Notes: - This script will not affect your modifier stack, it will simply put an edit poly with the
    14. -- smoothing groups at the top.
    15. -- - Undo is not currently supported, so save before, just in case. You can however, just delete
    16. -- the edit poly modifier to get your old smoothing groups back.
    17. -- - Selecting any channel other than UV channel 1 will show a dialogue box asking if you want to
    18. -- reset UVs. You must hit yes for it to work based on the channel you've specified. This
    19. -- will NOT affect the model's existing UV set.
    20.  
    21. macroscript UVIslandsToSmoothing
    22. category:"MP_Tools"
    23. buttontext:"UV 2 Smooth"
    24. tooltip:"Convert UV islands to smoothing groups"
    25. autoUndoEnabled:false
    26. (
    27. -- Helper function to get the index of the first true element in a bit array
    28. function getFirstActiveInBitarray aBitArray =
    29. (
    30. for i = 1 to aBitArray.count do
    31. (
    32. if aBitArray[i] == true do return i
    33. )
    34. -- return 0 if none are found active
    35. return 0
    36. )
    37.  
    38. -- Actually performs the operation on the currently selected object
    39. function ConvertUVIslandsToSmoothingGroups aUVChannel =
    40. (
    41. if $ != undefined then
    42. (
    43. modPanel.addModToSelection(Edit_Poly()) ui:on
    44. local editPoly = $.modifiers[#edit_poly]
    45. local facesDone = #{} -- empty bit array since no faces are done
    46. local allFaces = #{1.. polyop.getNumFaces $}
    47. local facesNotDone = allFaces
    48. -- Stick on a UVW modifier
    49. modPanel.addModToSelection (Unwrap_UVW ()) ui:on
    50. local uv_modifier = $.modifiers[#unwrap_uvw]
    51. uv_modifier.unwrap2.setTVSubObjectMode 3 -- Use face selection
    52. if (aUVChannel != 1) then -- Only need to mess with this if it's not default
    53. (
    54. uv_modifier.unwrap.setMapChannel aUVChannel
    55. uv_modifier.unwrap.reset()
    56. forcecompleteredraw dodisabled:true -- Hacky fix for a bug, see [url]http://www.polycount.com/forum/showthread.php?t=97059[/url]
    57. )
    58. local uv_islands = #() -- Empty array that will store bitarrays of all our UV islands
    59. local abort = false -- Abort boolean for breaking out of the loop and avoid the performance penalty of using break
    60. -- Build array of UV islands
    61. while (facesNotDone.isEmpty == false and abort == false) do
    62. (
    63. nextFace = getFirstActiveInBitarray facesNotDone -- Get next face that hasn't been processed yet
    64. uv_modifier.unwrap2.selectFaces #{nextFace} -- Select that face
    65. uv_modifier.unwrap2.selectElement() -- Grow selection to element
    66. uv_island = uv_modifier.unwrap2.getSelectedFaces() -- Get a bitaray of all those faces (representing a UV island)
    67. -- Update faces done/not done bit masks
    68. facesDone += uv_island
    69. facesNotDone -= uv_island
    70. insertItem uv_island uv_islands (uv_islands.count + 1) -- Add that bitarray to our array of UV islands
    71. 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
    72. (
    73. abort = true
    74. print ("Error, calculated too many islands, something went wrong")
    75. )
    76. )
    77. deletemodifier $ uv_modifier -- Don't need the UV modifier anymore
    78. editPoly.autoSmoothThreshold = 180.0 -- If we auto smooth, it should always be in the same smoothing group
    79. for island = 1 to uv_islands.count do -- Select and auto smooth each UV island
    80. (
    81. editPoly.SetSelection #Face uv_islands[island]
    82. editPoly.ButtonOp #Autosmooth
    83. )
    84. )
    85. )
    86. local isOpen = false -- Store if the rollout is open or closed
    87. rollout UV2SmoothRollout "UV_2_Smooth"
    88. (
    89. spinner UVChannelSpinner "UV Channel" range:[1,99,1] type:#integer
    90. button GoBtn " Run "
    91. on GoBtn pressed do
    92. (
    93. ConvertUVIslandsToSmoothingGroups (UVChannelSpinner.value)
    94. destroyDialog UV2SmoothRollout -- Close rollout after running
    95. )
    96. on UV2SmoothRollout close do
    97. (
    98. isOpen = false
    99. updateToolbarButtons() -- Update the toolbar icon when closing
    100. )
    101. )
    102. on execute do
    103. (
    104. if isOpen then --if open, close it
    105. (
    106. destroyDialog UV2SmoothRollout
    107. )
    108. else --if closed, open it
    109. (
    110. createDialog UV2SmoothRollout
    111. isOpen = true
    112. )
    113. )
    114. on isChecked return isOpen --return the flag
    115. on isEnabled do
    116. (
    117. -- Need an editable poly selected to work
    118. if $ == undefined then
    119. (
    120. -- Close the window if it's open and it shouldn't be
    121. if (isOpen) then
    122. destroyDialog UV2SmoothRollout
    123. return false
    124. )
    125. else
    126. return true
    127. )
    128. )
  • 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.