1087 lines
36 KiB
Plaintext
Executable File
1087 lines
36 KiB
Plaintext
Executable File
-- 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")) (@"(?<!^)(?=[A-Z])")
|
|
local stringArray = regexObject.split instring
|
|
local returnString = stringArray[1]
|
|
|
|
if (capstart == true) then
|
|
returnString[1] = toUpper returnString[1]
|
|
|
|
if stringArray.count > 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() |