--SeamFixer.ms filein (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms") --wildwest header filein (RsConfigGetWildWestDir() + "script/3dsMax/_common_functions/RSL_dotNetUIOps.ms") --RS_dotNetPreset structure filein (RsConfigGetWildWestDir() + "script/3dsMax/_common_functions/COVERT.ms") --coincident vertex functions RsCollectToolUsageData (getThisScriptFilename()) global SeamFixer struct NormalData ( vertID, normals = #(), vectors = #() ) struct ObjectNormalSettings ( obj, normalSettingList = #() ) struct NormalSetting ( vector, vertList = #{} ) struct SeamFixerStruct ( Form, ToolTip, InfoPanel, PositionThreshold, AngleThreshold, ProgBar, ProgressValue = 0, SeamFixerButton, PreserveCheckBox, IniFilePath = (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/RSL_Tools.ini"), NormalList = #(), ------------------------------------------------------------------------------------------------------------------------------------------ --GENERAL FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------ fn FixSeam s e = ( local infoString = "" local meshObjects = for this in selection where superClassOf this == GeometryClass collect this local originalSelection = selection as array if (meshObjects.Count > 1) then ( local thistime = timestamp() local objectProcessingList = #() --array of values to set normals to on a per object level local editNorms = Edit_Normals() --edit normals modifier rollout temprollout "Finding matching vertices" ( label myLabel "Processing" ) --find the border vertices theNewFloater = createDialog temprollout 200 30 COVERT = COVERT_List tolerance:(SeamFixer.PositionThreshold.Text as float) COVERT.InitCOVERT_List meshObjects destroyDialog temprollout meshObjects = COVERT.objList --reduced set of objects that have coincident vertices infoString = "Coincident vertices found on " + meshObjects.count as string + " objects." addModifier meshObjects editNorms max modify mode --neccessary or the modifier operations have no effect select meshObjects --required to refresh the scene for match in COVERT.matchList do ( --get the average vector local vector = [0,0,0] for object in match.objectList do ( for vert in object.vertList do ( local normal = RSNorm_GetVertexNormal object.obj vert vector += normal ) ) local averageVector = (normalize vector) --set the vectors to the average value for object in match.objectList do ( local normalIDs = #{} editNorms.ConvertVertexSelection object.vertList normalIDs node:object.obj --adjust the averageVector to account for objects with rotaton values local outputVector = averageVector * (object.obj.rotation as matrix3) for normalID in normalIDs as array do editNorms.SetNormal normalID outputVector node:object.obj --make normals explicit to preserve the normals after stack collapse editNorms.MakeExplicit selection:normalIDs node:object.obj true ) SeamFixer.ProgressValue += 100.0 / COVERT.matchList.count SeamFixer.ProgBar.Value = SeamFixer.ProgressValue as integer ) if (not SeamFixer.PreserveCheckBox.Checked) then convertToPoly meshObjects infoString = "Normals averaged at " + (COVERT.matchList.count as string) + " locations." ) else ( infoString = "Please select two or more objects for seam matching." ) SeamFixer.InfoPanel.Text = infoString SeamFixer.ProgressValue = 0 SeamFixer.ProgBar.Value = 0 local finalTime = timeStamp() - thisTime local timeString = ("Operation took " + (finalTime/1000.0) as string + " seconds") format "%\n" timeString select originalSelection ), ------------------------------------------------------------------------------------------------------------------------------------------ --LOAD/SAVE FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------ fn SaveIniFile = ( setINISetting SeamFixer.IniFilePath "SeamFixer" "WinLocX" (SeamFixer.Form.Location.x as string) setINISetting SeamFixer.IniFilePath "SeamFixer" "WinLocY" (SeamFixer.Form.Location.y as string) setINISetting SeamFixer.IniFilePath "SeamFixer" "WinWidth" (SeamFixer.Form.Width as string) setINISetting SeamFixer.IniFilePath "SeamFixer" "WinHeight" (SeamFixer.Form.Height as string) setINISetting SeamFixer.IniFilePath "SeamFixer" "PositionThreshold" (SeamFixer.PositionThreshold.Text as string) setINISetting SeamFixer.IniFilePath "SeamFixer" "PreserveCheckState" (SeamFixer.PreserveCheckBox.Checked as string) --nb: have to use .Text property for numericUpDown controls because of http://forums.cgsociety.org/archive/index.php/t-763277.html --max converts the numeric values to floats, so we have to grab the .Text value instead of .Value ), fn LoadIniFile = ( --default values local WinLocX = 0 local WinLocY = 40 local WinWidth = 230 local WinHeight = 210 local PThresh = 0.001 local PreserveCheckState = false try ( WinLocX = getINISetting SeamFixer.IniFilePath "SeamFixer" "WinLocX" as integer WinLocY = getINISetting SeamFixer.IniFilePath "SeamFixer" "WinLocY" as integer WinWidth = getINISetting SeamFixer.IniFilePath "SeamFixer" "WinWidth" as integer WinHeight = getINISetting SeamFixer.IniFilePath "SeamFixer" "WinHeight" as integer PThresh = getINISetting SeamFixer.IniFilePath "SeamFixer" "PositionThreshold" as float PreserveCheckState = getINISetting SeamFixer.IniFilePath "SeamFixer" "PreserveCheckState" as BooleanClass ) catch() SeamFixer.Form.Location = dotNetObject "system.drawing.point" WinLocX WinLocY SeamFixer.Form.Size = dotNetObject "System.Drawing.Size" WinWidth WinHeight SeamFixer.PositionThreshold.Value = PThresh SeamFixer.PreserveCheckBox.Checked = PreserveCheckState ), ------------------------------------------------------------------------------------------------------------------------------------------ --UI ------------------------------------------------------------------------------------------------------------------------------------------ fn CreateUI = ( -- form setup Form = dotNetObject "maxCustomControls.maxForm" Form.Text = "Seam Fixer" Form.StartPosition = (dotNetClass "System.Windows.Forms.FormStartPosition").manual Form.Location = dotNetObject "system.drawing.point" 0 80 Form.MaximumSize = dotNetObject "System.Drawing.Size" 1600 1200 Form.MinimumSize = dotNetObject "System.Drawing.Size" 230 210 Form.FormBorderStyle = RS_dotNetPreset.FBS.Sizable dotNet.AddEventHandler Form "Load" LoadIniFile dotNet.AddEventHandler Form "Closing" SaveIniFile --content ToolTip = dotnetobject "ToolTip" Table = dotNetObject "TableLayoutPanel" Table.Dock = RS_dotNetPreset.DS.Fill Table.RowCount = 6 Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 40) --banner Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 32) --input table Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 30) --preserve modifier stack Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 30) --SeamFixer button Table.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 100) --infoPanel Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 20) --progress bar Table.ColumnCount = 1 Table.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 100) Form.Controls.Add Table RSBannerPanel = dotNetObject "System.Windows.Forms.Panel" RSBannerPanel.BorderStyle = (dotnetClass "System.Windows.Forms.BorderStyle").FixedSingle RSBannerPanel.Dock = RS_dotNetPreset.DS.Fill local banner = makeRsBanner dn_Panel:RSBannerPanel width:395 studio:"london" mail:"andy.davis@rockstarlondon.com" wiki:"Map_Art_Tech" banner.setup() Table.Controls.Add RSBannerPanel 0 0 InputTable = dotNetObject "TableLayoutPanel" InputTable.Dock = RS_dotNetPreset.DS.Fill InputTable.RowCount = 1 InputTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 100) InputTable.ColumnCount = 2 InputTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "absolute" 150) InputTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 100) Table.Controls.Add InputTable 0 1 PositionLabel = RS_dotNetUI.InitLabel "Vertex position threshold:" RS_dotNetPreset.TA_Left InputTable.Controls.Add PositionLabel 0 0 PositionThreshold = RS_dotNetUI.InitNumericUpDown 0 1.0 0.001 PositionThreshold.Increment = 0.001 PositionThreshold.DecimalPlaces = 3 InputTable.Controls.Add PositionThreshold 1 0 PreserveCheckBox = RS_dotNetUI.InitCheckBox "Preserve modifier stack" Table.Controls.Add PreserveCheckBox 0 2 SeamFixerButton = RS_dotNetUI.InitButton "Fix Seam" dotNet.AddEventHandler SeamFixerButton "Click" FixSeam Table.Controls.Add SeamFixerButton 0 3 InfoPanel = RS_dotNetUI.InitLabel "Select two objects for seam fixing." RS_dotNetPreset.TA.MiddleLeft Table.Controls.Add InfoPanel 0 4 ProgBar = RS_dotNetUI.InitProgressBar() Table.Controls.Add ProgBar 0 5 --draw form Form.ShowModeless() Form ) ) if SeamFixer != undefined then ( SeamFixer.Form.Close() ) SeamFixer = SeamFixerStruct() SeamFixer.CreateUI()