-- RS_NormalTweaker.ms -- 2012 Andy Davis -- Rockstar London -- Script provides UI to allow access to control over vertex normals filein (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms") --wildwest header filein (RsConfigGetWildWestDir() + "script/3dsMax/_common_functions/RSL_dotNetUIOps.ms") --RS_dotNetPreset structure global NormalTweaker struct NormalTweakerStruct ( --controls Form, Table, ToolTip, InfoPanel, ButtonSet = #(), ButtonTable, IniFilePath = (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/RSL_Tools.ini"), SliderLabel, SliderBar, BlankLabel, SinglePanel, MultiPanel, FlowPanel, ContentPanel, IDCheckBox, PreserveCheckBox, ProgBar, ProgressValue = 0.0, MiniMenu, FocusVector = [0,0,0], --data objList, faceList, objectList, --presets mainColor = color 100 108 132, ------------------------------------------------------------------------------------------------------------------------------------------ --EVENT HANDLERS ------------------------------------------------------------------------------------------------------------------------------------------ fn ToggleAffectAll s e = ( count = s.Parent.Controls.Count - 1 -- minus 1 because the last control item is a blank label spacer s.Parent.Controls.Item[count].Enabled = not s.Parent.Controls.Item[count].Enabled ), fn GetProcessData materialTab = ( if (materialTab.Controls.Item[0].Checked) then ( --objList stored in the objectLabel tag local objs = materialTab.Controls.Item[1].Tag.Value for this in objs do ( addModifier this (Edit_Normals()) append NormalTweaker.objList this append NormalTweaker.faceList (NormalTweaker.IntegerToBitArray (this.Faces.Count)) ) ) ), fn PickFocusObject obj = ( classOf obj == Dummy or classOf obj == Point ), fn PickStealObject obj = ( local valid = true --ensure that the steal object has verts... if not (classOf obj == Editable_mesh or classOf obj == Editable_poly or classOf obj == PolyMeshObject) then valid = false --we can't select an object in selection to steal normals from... for this in selection do ( if (obj == this) then valid = false ) return valid ), fn TweakNormals s e = ( local QueryScript = (RsConfigGetWildWestDir() + "script/3dsMax/Maps/RSL_NormalTweaker_CB.ms") local tweakValue = NormalTweaker.SliderBar.Value / 100.0 local origSelection = selection as array local mode = s.Tag --find out which faces are to be processed NormalTweaker.objList = #() --objects to be processed NormalTweaker.faceList =#() --bitarray of faces for each object --processing involves: --1) applying an edit normal modifier to objects that will get affected only --2) getting data for the list of objects --3) getting data for the respective faces on these objects --4) sending the data to functions to perform the appropriate operation --iterate through the materials local numMaterials = NormalTweaker.ContentPanel.Controls.Count - 1 for i = 1 to numMaterials do ( local thisMatTab = NormalTweaker.ContentPanel.Controls.Item[i - 1] local mat = thisMatTab.Tag.Value --process the undefined materials if (mat == undefined) then ( NormalTweaker.GetProcessData thisMatTab ) else ( --process the single materials if (classOf mat != Multimaterial) then ( NormalTweaker.GetProcessData thisMatTab ) --process the multimaterials else ( local objs = thisMatTab.Controls.Item[1].Tag.Value if (thisMatTab.Controls.Item[2].Checked) then ( for this in objs do ( addModifier this (Edit_Normals()) append NormalTweaker.objList this append NormalTweaker.faceList (NormalTweaker.IntegerToBitArray (this.Faces.Count)) ) ) else --individual material faces are to be tweaked ( local subMatList = #() --get a list of the checked submaterials for j = 1 to (thisMatTab.Controls.Item[3].Controls.Count) do ( if (thisMatTab.Controls.Item[3].Controls.Item[j - 1].Checked) then append subMatList thisMatTab.Controls.Item[3].Controls.Item[j - 1].Tag.Value ) --find the corresponding faces on the objects and note them as bitarrays if (subMatList.Count > 0) then ( for thisObj in objs do ( local matFaces = #{} addModifier thisObj (Edit_Normals()) for k = 1 to thisObj.Faces.Count do ( local thisMatID = polyop.GetFaceMatID thisObj k if ((findItem subMatList thisMatID) != 0) then ( append matFaces k ) ) if (matFaces.Count != 0) then ( append NormalTweaker.objList thisObj append NormalTweaker.faceList matFaces ) ) ) ) ) ) ) if (NormalTweaker.objList.Count > 0) then ( --process objects NormalTweaker.ProgressValue = 0.0 NormalTweaker.ProgBar.Value = NormalTweaker.ProgressValue callbacks.removeScripts #selectionSetChanged id:#NormalTweakerCallbacks if (mode == "focus") then ( NormalTweaker.InfoPanel.Text = "Select a dummy or pointer object." local pick = pickObject prompt:"Select a dummy or pointer object." filter:NormalTweaker.PickFocusObject if pick == undefined then ( NormalTweaker.InfoPanel.Text = "No helper selected" return false ) else ( NormalTweaker.FocusVector = pick.Position NormalTweaker.InfoPanel.Text = (pick.Name + " selected as focus object") print (pick.Name + " selected as focus object") ) ) if (mode == "steal") then ( NormalTweaker.InfoPanel.Text = "Select geometry to steal the normals from." local pick = pickObject prompt:"Select geometry to steal the normals from." filter:NormalTweaker.PickStealObject if pick == undefined then ( NormalTweaker.InfoPanel.Text = "No object selected." return false ) else ( NormalTweaker.InfoPanel.text = (pick.Name + " selected as source object") print (pick.Name + " selected as source object") for i = 1 to NormalTweaker.objList.Count do ( NormalTweaker.StealNormals pick NormalTweaker.objList[i] NormalTweaker.faceList[i] ) ) ) -- disableSceneRedraw() if (mode == "reset") then ( NormalTweaker.InfoPanel.Text = "Normals reset on selection." for i = 1 to NormalTweaker.objList.Count do ( NormalTweaker.ResetNormals NormalTweaker.objList[i] NormalTweaker.faceList[i] NormalTweaker.ProgressValue += (100 / NormalTweaker.objList.Count) NormalTweaker.ProgBar.Value = NormalTweaker.ProgressValue ) ) if (mode =="up" or mode == "explode" or mode == "focus") then ( NormalTweaker.InfoPanel.Text = ("Normals set to " + s.Tag + " on selection by " + NormalTweaker.SliderBar.Value as string + "%.") for i = 1 to NormalTweaker.objList.Count do NormalTweaker.SetNormalsOnObject NormalTweaker.objList[i] NormalTweaker.faceList[i] tweakValue s.Tag ) NormalTweaker.ProgressValue = 0.0 NormalTweaker.ProgBar.Value = NormalTweaker.ProgressValue subObjectLevel = 0 select origSelection -- enableSceneRedraw() callbacks.addScript #selectionSetChanged fileName:QueryScript id:#NormalTweakerCallbacks ) else NormalTweaker.InfoPanel.Text = "Please select faces from the list above." ), fn SelectListedObjects s e = ( print "asdfkasdfoasdf" ClearSelection() select NormalTweaker.objectList NormalTweaker.MiniMenu.Hide() ), fn SelectAllObjects s e = ( print "SelectAllObjects" NormalTweaker.MiniMenu.Hide() ), fn Cancel = ( NormalTweaker.MiniMenu.Hide() ), fn SetObjectList s e = ( NormalTweaker.objectList = s.Tag.Value NormalTweaker.MiniMenu.Hide() print NormalTweaker.objectList ), ------------------------------------------------------------------------------------------------------------------------------------------ --GENERAL FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------ fn IntegerToBitArray num = ( ba = #{} for i = 1 to num do append ba i return ba ), fn InitContentPanel = ( NormalTweaker.ContentPanel = dotNetObject "TableLayoutPanel" NormalTweaker.ContentPanel.Dock = RS_dotNetPreset.DS.Fill NormalTweaker.ContentPanel.ColumnCount = 1 NormalTweaker.ContentPanel.BackColor = ARGB 48 48 48 ), fn ShowMiniMenu s e = ( if (e.Button == e.Button.Right) then ( local location = [(e.x + NormalTweaker.Form.Location.x), (e.y + NormalTweaker.Form.Location.y + 70)] NormalTweaker.MiniMenu.Location = dotNetObject "system.drawing.point" location[1] location[2] NormalTweaker.MiniMenu.ShowModal() ) else print "not right button" ), fn InitUndefinedMatTable objList = ( local NewCheckBox local NewTable = dotNetObject "TableLayoutPanel" NewTable.Dock = RS_dotNetPreset.DS.Fill NewTable.RowCount = 2 NewTable.ColumnCount = 1 NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.BackColor = RS_dotNetPreset.ARGB 128 0 0 NewTable.Tag = dotNetMXSValue undefined local infoString = "Objects: " + objList[1].Name if (objList.Count > 1) then ( for i = 2 to objList.Count do infoString += ", " + objList[i].Name ) NewCheckBox = RS_dotNetUI.InitCheckBox "No Material" NewTable.Controls.Add NewCheckBox 0 0 local NewLabel = RS_dotNetUI.InitLabel infoString RS_dotNetPreset.TA.MiddleLeft NewLabel.Tag = dotNetMXSValue objList -- dotNet.AddEventHandler NewLabel "Click" SetObjectList -- dotNet.AddEventHandler NewLabel "MouseClick" ShowMiniMenu NewTable.Controls.Add NewLabel 0 1 return NewTable ), fn InitSingleMatTable mat objList = ( local NewTable = dotNetObject "TableLayoutPanel" NewTable.Dock = RS_dotNetPreset.DS.Fill NewTable.RowCount = 2 NewTable.ColumnCount = 1 NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.BackColor = RS_dotNetPreset.ARGB 0 32 128 NewTable.Tag = dotNetMXSValue mat local infoString = "Objects: " + objList[1].Name if (objList.Count > 1) then ( for i = 2 to objList.Count do infoString += ", " + objList[i].Name ) local NewLabel = RS_dotNetUI.InitLabel infoString RS_dotNetPreset.TA.MiddleLeft NewLabel.Tag = dotNetMXSValue objList local NewCheckBox = RS_dotNetUI.InitCheckBox (classOf mat as string + ": " + mat.Name) NewTable.Controls.Add NewCheckBox 0 0 NewTable.Controls.Add NewLabel 0 1 return NewTable ), fn InitMultiMatTable mat objList = ( local NewTable = dotNetObject "TableLayoutPanel" local matIDs = #() --get a list of material ID's and their materials from the objList for obj in objList do ( if (classOf obj != Editable_Poly) then addModifier obj (Edit_Poly()) for i = 1 to obj.faces.count do AppendIfUnique matIDs (polyop.GetFaceMatID obj i) ) sort matIDs --build the table NewTable.Dock = RS_dotNetPreset.DS.Fill NewTable.RowCount = 4 NewTable.ColumnCount = 1 NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) NewTable.BackColor = RS_dotNetPreset.ARGB 0 128 32 NewTable.Tag = dotNetMXSValue mat NewLabel = RS_dotNetUI.InitLabel ("Multimaterial: " + mat.name) RS_dotNetPreset.TA.MiddleLeft NewTable.Controls.Add NewLabel 0 0 local infoString = "Objects: " + objList[1].Name if (objList.Count > 1) then ( for i = 2 to objList.Count do infoString += ", " + objList[i].Name ) local NewLabel = RS_dotNetUI.InitLabel infoString RS_dotNetPreset.TA.MiddleLeft NewLabel.Tag = dotNetMXSValue objList NewTable.Controls.Add NewLabel 0 1 local NewCheckBox = RS_dotNetUI.InitCheckBox "affect all material id's" NewCheckBox.Checked = false NewTable.Controls.Add NewCheckBox 0 2 dotNet.AddEventHandler NewCheckBox "CheckedChanged" ToggleAffectAll local SubMatTable = dotNetObject "TableLayoutPanel" SubMatTable.Dock = RS_dotNetPreset.DS.Fill SubMatTable.Tag = dotNetMXSValue matIDs SubMatTable.RowCount = matIDs.Count SubMatTable.BackColor = RS_dotNetPreset.ARGB 0 108 12 for i = 1 to matIDs.Count do ( local matName local matNumber = findItem mat.MaterialIDList matIDs[i] if matNumber == 0 then matName = "(no material)" else matName = (classOf mat.MaterialList[i] as string + ": " + mat.MaterialList[i].Name) SubMatTable.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) newCheckBox = RS_dotNetUI.InitCheckBox ("[" + matIDs[i] as string + "] " + matName) newCheckBox.Tag = dotNetMXSValue matIDs[i] SubMatTable.Controls.Add newCheckBox 0 (i - 1) ) NewTable.Controls.Add SubMatTable 0 3 return NewTable ), fn SplitCasing instring capstart:true = ( local regexObject = dotNetObject (regExType = (dotNetClass "System.Text.RegularExpressions.Regex")) (@"(? 1 then ( for i = 2 to stringArray.count do ( returnString = returnString + " " + toLower (stringArray[i]) ) ) return returnString ), fn UpdateSliderValue s e = ( NormalTweaker.SliderLabel.Text = ("Tweak amount: " + s.Value as string + "%") ), fn Refresh = ( --garbage collection dgc = dotnetclass "system.gc" dgc.collect() gc() meshObjects = for this in selection where ((superClassOf this == GeometryClass) and (classOf this != XRefObject)) collect this if (meshObjects.Count == 0) then ( if (NormalTweaker.Table.Tag == "content") then ( NormalTweaker.Table.Controls.Remove NormalTweaker.ContentPanel NormalTweaker.Table.Controls.Add NormalTweaker.BlankLabel 0 1 NormalTweaker.InfoPanel.Text = "Selection: no suitable mesh object selected" for thisButton in NormalTweaker.ButtonSet do thisButton.Enabled = false NormalTweaker.Table.Tag = "blank" ) ) else ( local materialSet = #() local objectSet = #() local undefinedSet = #() local color1 = NormalTweaker.mainColor local color2 = color (NormalTweaker.mainColor.r + 8) (NormalTweaker.mainColor.g + 8) (NormalTweaker.mainColor.b + 8) --get material data for this in meshObjects do ( if this.material == undefined then append undefinedSet this else ( local matItem = findItem materialSet this.material if (matItem == 0) then ( append materialSet this.material append objectSet #(this) ) else ( append objectSet[matItem] this ) ) ) --build material table NormalTweaker.ContentPanel.Controls.Clear() NormalTweaker.ContentPanel.RowStyles.Clear() NormalTweaker.ContentPanel.RowCount = materialSet.Count if (undefinedSet.Count > 0) then ( NormalTweaker.ContentPanel.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 60) local matTable = NormalTweaker.InitUndefinedMatTable undefinedSet NormalTweaker.ContentPanel.Controls.Add matTable 0 0 ) if (materialSet.Count > 0) then ( for i = 1 to materialSet.Count do ( local matTable if (classOf materialSet[i] == MultiMaterial) then ( matTable = NormalTweaker.InitMultiMatTable materialSet[i] objectSet[i] NormalTweaker.ContentPanel.Controls.Add matTable 0 NormalTweaker.ContentPanel.Controls.Count local numControls = NormalTweaker.ContentPanel.Controls.Count local currentMat = NormalTweaker.ContentPanel.Controls.Item[numControls - 1] local numSubMats = currentMat.Controls.Item[3].Controls.Count local blockHeight = 85 + 25 * numSubMats NormalTweaker.ContentPanel.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" blockHeight) ) else -- must be a single material ( NormalTweaker.ContentPanel.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 60) local matTable = InitSingleMatTable materialSet[i] objectSet[i] NormalTweaker.ContentPanel.Controls.Add matTable 0 NormalTweaker.ContentPanel.Controls.Count ) ) ) --place spacer panel on table --annoyingly necessary or the bottom element on the table expands to fill the space NormalTweaker.ContentPanel.RowStyles.Add (RS_dotNetObject.rowStyleObject "percent" 100) spacerPanel = RS_dotNetUI.InitLabel "" RS_dotNetPreset.TA.MiddleCenter NormalTweaker.ContentPanel.Controls.Add spacerPanel 0 NormalTweaker.ContentPanel.Controls.Count if (NormalTweaker.Table.Tag == "blank") then ( NormalTweaker.Table.Controls.Remove NormalTweaker.BlankLabel NormalTweaker.Table.Controls.Add NormalTweaker.ContentPanel 0 1 for thisButton in NormalTweaker.ButtonSet do thisButton.Enabled = true NormalTweaker.Table.Tag = "content" ) local matTypes = NormalTweaker.ContentPanel.Controls.Count - 1 if (matTypes == 1) then NormalTweaker.InfoPanel.Text = "1 material detected" else NormalTweaker.InfoPanel.Text = (matTypes as string + " materials detected\nPlease select the materials that you want to affect.") ) ), fn GetNormalsFromFaces obj facelist = ( local normalList = #{} max modify mode select obj for thisFace in faceList do ( local faceDegree = obj.Edit_Normals.GetFaceDegree thisFace for thisCorner in 1 to faceDegree do ( appendIfUnique normalList (obj.Edit_Normals.GetNormalID thisFace thisCorner) ) ) return normalList ), fn ResetNormals obj faceList preserveStack:false = ( local normalSet = #{} max modify mode select obj --calculate normalSet based on faceList normalSet = NormalTweaker.GetNormalsFromFaces obj faceList obj.Edit_Normals.Select normalSet obj.Edit_Normals.Reset() if (not NormalTweaker.PreserveCheckBox.Checked and preserveStack == false) then convertToPoly obj ), fn SetNormalsOnObject obj faceList amount mode = ( local upVector = [0,0,1] max modify mode select obj for thisFace in faceList do ( local faceDegree = obj.Edit_Normals.GetFaceDegree thisFace for thisCorner in 1 to faceDegree do ( local vertexID = obj.Edit_Normals.getVertexID thisFace thisCorner local normalID = obj.Edit_Normals.getNormalID thisFace thisCorner local vertexPosition = obj.Edit_Normals.GetVertex vertexID local currentNormalVector = obj.Edit_Normals.getNormal normalID local tweakVector case mode of ( "up": ( tweakVector = upVector break ) "explode": ( tweakVector = normalize (vertexPosition + obj.position - obj.center) break ) "focus": ( tweakVector = normalize (vertexPosition + obj.position - NormalTweaker.FocusVector) break ) ) local outputVector = normalize ((amount * (tweakVector - currentNormalVector)) + currentNormalVector) obj.Edit_Normals.SetNormal normalID outputVector --we need to set the normal to explicit so that the normal tweak is preserved on collapse obj.Edit_Normals.SetNormalExplicit normalID ) NormalTweaker.ProgressValue += (100.0 / NormalTweaker.objList.Count) / faceList.Count NormalTweaker.ProgBar.Value = NormalTweaker.ProgressValue ) --collapse the stack if required if (not NormalTweaker.PreserveCheckBox.Checked) then convertToPoly obj ), fn StealNormals source obj faceList = -- this script needs a different approach because the reference normal vector changes for each point ( local sourceFaces = (NormalTweaker.IntegerToBitArray (source.Faces.Count)) local objFaces = (NormalTweaker.IntegerToBitArray (obj.Faces.Count)) local normalVectorList = #() --list of normals vectors for each vertex on the source object local normalPositionList = #() local normalBuffer = #{} local sourceVertMax = 200 local amount = NormalTweaker.SliderBar.Value / 100.0 if (source.verts.count > sourceVertMax) then ( if not (queryBox ("Source object (" + source.Name + ") contains more than " + sourceVertMax as string + " vertices.\nDo you wish to continue?") title:"RSL Normal Tweaker - Steal Normals") then ( NormalTweaker.InfoPanel.Text = "Operation aborted." return false ) ) --reset normals on source object and get a list of the normals and their locations select source addModifier source (Edit_Normals()) NormalTweaker.ResetNormals source sourceFaces preserveStack:true max modify mode for i = 1 to source.Verts.Count do ( --find the normal vector for each vertex source.Edit_Normals.ConvertVertexSelection #{i} normalBuffer append normalVectorList (source.Edit_Normals.GetNormal (normalBuffer as array)[1]) append normalPositionList source.Verts[i].position ) deleteModifier source source.Edit_Normals --reset normals on target object select obj NormalTweaker.ResetNormals obj objFaces preserveStack:true NormalTweaker.ProgressValue = 0.0 NormalTweaker.ProgBar.Value = NormalTweaker.ProgressValue faceList = faceList as array --need to do this due to 3ds Max bug concerning accessing normal information when normals are set to explicit --this bodge involves creating a new edit_normals modifier and deleting the previous one deleteModifier obj obj.Edit_Normals addModifier obj (Edit_Normals()) obj.Edit_Normals.Name = "Stolen Normals" for thisFace in faceList do ( local faceDegree = obj.Edit_Normals.GetFaceDegree thisFace for thisCorner in 1 to faceDegree do ( local vertexID = obj.Edit_Normals.getVertexID thisFace thisCorner local normalID = obj.Edit_Normals.getNormalID thisFace thisCorner local currentNormalVector = obj.Edit_Normals.getNormal normalID local distanceArray = #() --array of distance between the target vertex and each vertex in the source object local finalVector = [0,0,0] for j = 1 to normalPositionList.Count do ( append distanceArray (distance obj.Verts[vertexID].position normalPositionList[j]) ) -- local influenceArray = NormalTweaker.ProcessDistanceArray distanceArray "linear" local influenceArray = NormalTweaker.ProcessDistanceArray distanceArray "exponential" for j = 1 to normalVectorList.Count do ( finalVector += (normalVectorList[j] * influenceArray[j]) ) finalVector = normalize finalVector --get and set current normal local tweakVector = normalize ((amount * (finalVector - currentNormalVector)) + currentNormalVector) obj.Edit_Normals.SetNormal normalID tweakVector obj.Edit_Normals.SetNormalExplicit normalID ) NormalTweaker.ProgressValue += (100.0 / NormalTweaker.objList.Count) / faceList.Count NormalTweaker.ProgBar.Value = NormalTweaker.ProgressValue ) ), fn ProcessDistanceArray inArray mode = --function takes an array of distances and returns an array of respective influence from 0 to 1 --where the smallest distance has a value of 1 --and the largest distance has a value of 0 --exponential mode may be more reflective of an intricate source object shape than linear mode ( local hival = inArray[1] local lowval = inArray[1] local outArray = #() for i = 2 to inArray.Count do ( if (inArray[i] > hival) then hival = inArray[i] if (inArray[i] < lowval) then lowval = inArray[i] ) for i = 1 to inArray.Count do ( local fieldValue = (hival - inArray[i]) / (hival - lowval) case mode of ( "linear": ( append outArray fieldValue break ) "exponential": ( -- --high powers yield more accurate results as the closest vertices have much greater relative influence append outArray (fieldValue ^ 16) break ) ) ) return outArray ), ------------------------------------------------------------------------------------------------------------------------------------------ --LOAD/SAVE/CALLBACK FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------ fn SaveIniFile = ( setINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinLocX" (NormalTweaker.Form.Location.x as string) setINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinLocY" (NormalTweaker.Form.Location.y as string) setINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinWidth" (NormalTweaker.Form.Width as string) setINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinHeight" (NormalTweaker.Form.Height as string) setINISetting NormalTweaker.IniFilePath "NormalTweaker" "TweakValue" (NormalTweaker.SliderBar.Value as string) setINISetting NormalTweaker.IniFilePath "NormalTweaker" "IDCheckState" (NormalTweaker.IDCheckBox.Checked as string) setINISetting NormalTweaker.IniFilePath "NormalTweaker" "PreserveCheckState" (NormalTweaker.PreserveCheckBox.Checked as string) callbacks.removeScripts #selectionSetChanged id:#NormalTweakerCallbacks NormalTweaker.MiniMenu.Close() ), fn LoadIniFile = ( --default values local WinLocX = 100 local WinLocY = 100 local WinWidth = 200 local WinHeight = 200 local TweakValue = 25 local IDCheckState = false local PreserveCheckState = false local QueryScript = (RsConfigGetWildWestDir() + "script/3dsMax/Maps/RSL_NormalTweaker_CB.ms") try ( WinLocX = getINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinLocX" as Integer WinLocY = getINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinLocY" as Integer WinWidth = getINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinWidth" as Integer WinHeight = getINISetting NormalTweaker.IniFilePath "NormalTweaker" "WinHeight" as Integer TweakValue = getINISetting NormalTweaker.IniFilePath "NormalTweaker" "TweakValue" as Float IDCheckState = getINISetting NormalTweaker.IniFilePath "NormalTweaker" "IDCheckState" as BooleanClass PreserveCheckState = getINISetting NormalTweaker.IniFilePath "NormalTweaker" "PreserveCheckState" as BooleanClass ) catch() Form.Location = dotNetObject "system.drawing.point" WinLocX WinLocY Form.Size = dotNetObject "System.Drawing.Size" WinWidth WinHeight NormalTweaker.SliderBar.Value = TweakValue NormalTweaker.SliderLabel.Text = ("Tweak amount: " + TweakValue as string + "%") NormalTweaker.IDCheckBox.Checked = IDCheckState NormalTweaker.PreserveCheckBox.Checked = PreserveCheckState NormalTweaker.FlowPanel.Enabled = not NormalTweaker.IDCheckBox.Checked callbacks.addScript #selectionSetChanged fileName:QueryScript id:#NormalTweakerCallbacks NormalTweaker.Refresh() ), ------------------------------------------------------------------------------------------------------------------------------------------ --UI ------------------------------------------------------------------------------------------------------------------------------------------ fn CreateUI = ( -- form setup Form = dotNetObject "maxCustomControls.maxForm" Form.Text = "RSL Normal Tweaker" 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" 320 480 Form.FormBorderStyle = RS_dotNetPreset.FBS_Sizable Form.SizeGripStyle = (dotNetClass "SizeGripStyle").show 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 = 8 Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 40) --RSBannerPanel Table.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 100) --ContentPanel Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 15) --SliderLabel Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 30) --SliderBar Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 25) --PreserveCheckBox Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 75) --ButtonTable Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 50) --InfoPanel Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 15) --ProgressBar 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 versionNum:1.0 versionName:"Unkle Munkle" studio:"london" mail:"andy.davis@rockstarlondon.com" wiki:"Map_Art_Tech" banner.setup() Table.Controls.Add RSBannerPanel 0 0 BlankLabel = RS_dotNetUI.InitLabel "Object selected needs to be an editable poly or editable mesh object." RS_dotNetPreset.TA.MiddleLeft BlankLabel.Dock = RS_dotNetPreset.DS_Fill BlankLabel.BackColor = RS_dotNetPreset.ARGB 48 48 48 Table.Tag = "blank" Table.Controls.Add BlankLabel 0 1 ContentPanel = dotNetObject "TableLayoutPanel" ContentPanel.Dock = RS_dotNetPreset.DS_Fill ContentPanel.ColumnCount = 1 ContentPanel.AutoScroll = true ContentPanel.BackColor = RS_dotNetPreset.ARGB 48 48 48 SliderLabel = RS_dotNetUI.InitLabel "Tweak amount: 50%" RS_dotNetPreset.TA.MiddleLeft Table.Controls.Add SliderLabel 0 2 SliderBar = dotNetObject "TrackBar" SliderBar.Dock = RS_dotNetPreset.DS_Fill SliderBar.Minimum = 0 SliderBar.Maximum = 100 SliderBar.Value = 50 SliderBar.TickFrequency = 10 ToolTip.SetToolTip SliderBar "Set the amount to affect the normals by" dotNet.AddEventHandler SliderBar "Scroll" UpdateSliderValue Table.Controls.Add SliderBar 0 3 PreserveCheckBox = RS_dotNetUI.InitCheckBox "Preserve Modifier Stack" ToolTip.SetToolTip PreserveCheckBox "Leave the edit normals modifier in the stack without collapsing" Table.Controls.Add PreserveCheckBox 0 4 -- BUTTONS ButtonTable = dotNetObject "TableLayoutPanel" ButtonTable.Dock = RS_dotNetPreset.DS_Fill ButtonTable.RowCount = 3 ButtonTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33) ButtonTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33) ButtonTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33) ButtonTable.ColumnCount = 2 ButtonTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 50) ButtonTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 50) Table.Controls.Add ButtonTable 0 5 RefreshButton = RS_dotNetUI.InitButton "Refresh" ToolTip.SetToolTip RefreshButton "Refresh tool to reflect changes" dotNet.AddEventHandler RefreshButton "Click" Refresh ButtonTable.Controls.Add RefreshButton 0 0 AlignUpButton = RS_dotNetUI.InitButton "Align Normals Up" AlignUpButton.Tag = "up" ToolTip.SetToolTip AlignUpButton "Align normals based on Z-axis" dotNet.AddEventHandler AlignUpButton "Click" TweakNormals append NormalTweaker.ButtonSet AlignUpButton ButtonTable.Controls.Add AlignUpButton 1 0 ExplodeButton = RS_dotNetUI.InitButton "Explode Normals" ExplodeButton.Tag = "explode" ToolTip.SetToolTip ExplodeButton "Align normals based on the object centre" dotNet.AddEventHandler ExplodeButton "Click" TweakNormals append NormalTweaker.ButtonSet ExplodeButton ButtonTable.Controls.Add ExplodeButton 0 1 ResetButton = RS_dotNetUI.InitButton "Reset Normals" ResetButton.Tag = "reset" ToolTip.SetToolTip ResetButton "Reset vertex normals to default value" dotNet.AddEventHandler ResetButton "Click" TweakNormals append NormalTweaker.ButtonSet ResetButton ButtonTable.Controls.Add ResetButton 1 1 FocusButton = RS_dotNetUI.InitButton "Focus Normals" FocusButton.Tag= "focus" ToolTip.SetToolTip FocusButton "Align normals based on a point helper" dotNet.AddEventHandler FocusButton "Click" TweakNormals append NormalTweaker.ButtonSet FocusButton ButtonTable.Controls.Add FocusButton 0 2 StealButton = RS_dotNetUi.InitButton "Steal Normals" StealButton.Tag= "steal" ToolTip.SetToolTip StealButton "Align normals based on a source object" dotNet.AddEventHandler StealButton "Click" TweakNormals append NormalTweaker.ButtonSet StealButton ButtonTable.Controls.Add StealButton 1 2 -- OTHER STUFF InfoPanel = RS_dotNetUI.InitLabel "Selection: none" RS_dotNetPreset.TA.MiddleLeft Table.Controls.Add InfoPanel 0 6 ProgBar = dotNetObject "ProgressBar" ProgBar.Dock = RS_dotNetPreset.DS.Fill ProgBar.Value = 0 ProgBar.Minimum = 0 ProgBar.Maximum = 100 Table.Controls.Add ProgBar 0 7 SinglePanel = dotNetObject "TableLayoutPanel" SinglePanel.Dock = RS_dotNetPreset.DS.Fill SinglePanel.RowCount = 2 SinglePanel.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 20) SinglePanel.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 100) SinglePanel.Tag = "single" IDCheckBox = dotNetObject "CheckBox" IDCheckBox.Text = "affect all material id's" IDCheckBox.Checked = false IDCheckBox.Dock = RS_dotNetPreset.DS.Fill dotNet.AddEventHandler IDCheckBox "CheckedChanged" ToggleAffectAll SinglePanel.Controls.Add IDCheckBox 0 0 FlowPanel = dotNetObject "FlowLayoutPanel" FlowPanel.Dock = RS_dotNetPreset.DS.Fill FlowPanel.FlowDirection = FlowPanel.FlowDirection.TopDown SinglePanel.Controls.Add FlowPanel 0 1 MultiPanel = dotNetObject "ListBox" MultiPanel.Dock = RS_dotNetPreset.DS.Fill MultiPanel.Tag = "multi" --mini menu - currently not plugged in MiniMenu = dotNetObject "maxCustomControls.maxForm" MiniMenu.StartPosition = (dotNetClass "System.Windows.Forms.FormStartPosition").manual MiniMenu.FormBorderStyle = RS_dotNetPreset.FBS_None MiniMenu.Size = dotNetObject "System.Drawing.Size" 160 120 MiniTable = dotNetObject "TableLayoutPanel" MiniTable.Dock = RS_dotNetPreset.DS.Fill MiniTable.RowCount = 3 MiniTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33) MiniTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33) MiniTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33) MiniTable.ColumnCount = 1 MiniTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 100) MiniMenu.Controls.Add MiniTable SelectListedObjectsButton = RS_dotNetUI.InitButton "Select Listed Objects" dotNet.AddEventHandler SelectListedObjectsButton "Click" SelectListedObjects MiniTable.Controls.Add SelectListedObjectsButton 0 0 SelectAllObjectsButton = RS_dotNetUI.InitButton "Select All Objects" dotNet.AddEventHandler SelectAllObjectsButton "Click" SelectAllObjects MiniTable.Controls.Add SelectAllObjectsButton 0 1 CancelButton = RS_dotNetUI.InitButton "Cancel" dotNet.AddEventHandler CancelButton "Click" Cancel MiniTable.Controls.Add CancelButton 0 2 --draw form Form.ShowModeless() Form ) ) if NormalTweaker != undefined then ( NormalTweaker.Form.Close() ) NormalTweaker = NormalTweakerStruct() NormalTweaker.CreateUI()