Files
2025-09-29 00:52:08 +02:00

645 lines
22 KiB
Plaintext
Executable File

-- UltimateTerrainMapper.ms
-- 2012 Andy Davis
-- Rockstar London
-- UV-mapping tool allowing the user to map geometry that aligns to a univeral grid
if (not gRsIsOutsource) do
(
filein (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms") --wildwest header
)
filein (RsConfigGetWildWestDir() + "script/3dsMax/_common_functions/RSL_dotNetUIOps.ms") --RS_dotNetPreset structure
if (not gRsIsOutsource) do
(
RsCollectToolUsageData (getThisScriptFilename())
)
global UltimateTerrainMapper
------------------------------------------------------------------------------------------------------------------------------------------
--MAIN TOOL STRUCT
------------------------------------------------------------------------------------------------------------------------------------------
struct UltimateTerrainMapperStruct
(
Form,
ToolTip,
InfoPanel,
ApplyMappingButton,
MapPanel1,
MapPanel2,
MapPanelTable1,
MapPanelTable2,
ActiveCB1,
ActiveCB2,
NumBoxSet1,
NumBoxSet2,
PeelCB,
EditCB,
CollapseCB,
ProgBar,
ProgBarValue,
IniFilePath = (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/RSL_Tools.ini"),
FaceMode,
------------------------------------------------------------------------------------------------------------------------------------------
--GENERAL FUNCTIONS
------------------------------------------------------------------------------------------------------------------------------------------
fn ApplyMapping s e =
(
max modify mode
local objset = for this in selection where superClassOf this == GeometryClass collect this
if (subObjectLevel > 3) then UltimateTerrainMapper.FaceMode = true
else UltimateTerrainMapper.FaceMode = false
if objset.count > 0 then
(
local active1 = UltimateTerrainMapper.ActiveCB1.Checked
local active2 = UltimateTerrainMapper.ActiveCB2.Checked
local peel = UltimateTerrainMapper.PeelCB.Checked
local collapseState = UltimateTerrainMapper.CollapseCB.Checked
local channel1 = UltimateTerrainMapper.NumBoxSet1[1].Value
local channel2 = UltimateTerrainMapper.NumBoxSet2[1].Value
local width1 = UltimateTerrainMapper.NumBoxSet1[2].Text as float
local width2 = UltimateTerrainMapper.NumBoxSet2[2].Text as float
local length1 = UltimateTerrainMapper.NumBoxSet1[3].Text as float
local length2 = UltimateTerrainMapper.NumBoxSet2[3].Text as float
local originalSelection = selection as array
local transformedObjectSet = #()
--check for objects with rotations or scaling
for obj in objset do
(
if (obj.rotation != (quat 0 0 0 1)) or (obj.scale != [1,1,1]) then
append transformedObjectSet obj
)
if (transformedObjectSet.count > 0) then
(
UltimateTerrainMapper.IssueWarning transformedObjectSet
)
else
(
UltimateTerrainMapper.ProgBarValue = 0
UltimateTerrainMapper.ProgBar.value = 0
for i = 1 to objset.count do
(
UltimateTerrainMapper.ProgBarValue += (100 / objset.count)
UltimateTerrainMapper.ProgBar.Value = UltimateTerrainMapper.ProgBarValue
select objset[i]
if (active1) then UltimateTerrainMapper.MapObject objset[i] channel1 width1 length1
--if (peel and active1) then UltimateTerrainMapper.PeelObject objset[i] channel1
if (active2) then UltimateTerrainMapper.MapObject objset[i] channel2 width2 length2
--if (peel and active2) then UltimateTerrainMapper.PeelObject objset[i] channel2
if (peel and not active1 and not active2) then UltimateTerrainMapper.PeelObject objset[i] 1
)
if (collapseState) then convertToPoly objSet
if (objset.count > 1) then select originalSelection
UltimateTerrainMapper.ProgBar.value = 0
)
)
else
(
messagebox "No compatible object selected" title:"Ultimate Terrain Mapper: Warning"
)
),
fn IssueWarning transformedObjectSet =
(
local infoText = ""
if (transformedObjectSet.count == 1) then
infoText += (transformedObjectSet[1].name + " has transforms.")
else
(
infoText +=("The following objects have transforms: " + transformedObjectSet[1].name)
for obj = 2 to transformedObjectSet.count do
(
infoText += ", " + transformedObjectSet[obj].name
)
)
infoText += "\nPlease reset before mapping."
messagebox infoText title:"Ultimate Terrain Mapper: Warning"
),
fn IntegerToBitArray num =
(
ba = #{}
for i = 1 to num do
append ba i
return ba
),
fn EditChecked s e =
(
if (s.Checked == true) then
UltimateTerrainMapper.CollapseCB.Checked = false
),
fn CollapseChecked s e =
(
if (s.Checked == true) then
UltimateTerrainMapper.EditCB.Checked = false
),
fn PeelChecked s e =
(
UltimateTerrainMapper.EditCB.Visible = s.Checked
),
fn ActiveChecked s e =
(
UltimateTerrainMapper.ManageMapPanel s.Parent s.Checked
),
fn ManageMapPanel table state =
(
for i = 2 to 7 do
table.Controls.Item[i].Visible = state
),
fn BuildMapPanel panelName &activeCheckBox &numBoxArray &mapPanelTable =
(
numBoxArray = #()
numBoxArray[3] = undefined
mapPanelTable = dotNetObject "TableLayoutPanel"
mapPanelTable.Dock = RS_dotNetPreset.DS.Fill
mapPanelTable.RowCount = 3
mapPanelTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33)
mapPanelTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33)
mapPanelTable.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 33)
mapPanelTable.ColumnCount = 3
mapPanelTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 100)
mapPanelTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "absolute" 80)
mapPanelTable.ColumnStyles.add (RS_dotNetObject.columnStyleObject "absolute" 70)
mapPanelTable.Margin = dotNetObject "System.Windows.Forms.Padding" 4
mapPanelTable.BackColor = RS_dotNetPreset.ARGB 90 90 90
mapPanelTable.Controls.Add (RS_dotNetUI.InitLabel panelName RS_dotNetPreset.TA.MiddleLeft) 0 0
activeCheckBox = RS_dotNetUI.InitCheckBox "Active"
dotNet.AddEventHandler activeCheckBox "CheckedChanged" ActiveChecked
mapPanelTable.Controls.Add activeCheckBox 0 1
mapPanelTable.Controls.Add (RS_dotNetUI.InitLabel "Map Channel:" RS_dotNetPreset.TA.MiddleRight) 1 0
mapPanelTable.Controls.Add (RS_dotNetUI.InitLabel "Map Width:" RS_dotNetPreset.TA.MiddleRight) 1 1
mapPanelTable.Controls.Add (RS_dotNetUI.InitLabel "Map Height:" RS_dotNetPreset.TA.MiddleRight) 1 2
numBoxArray[1] = RS_dotNetUI.InitNumericUpDown 1 99 1
numBoxArray[2] = RS_dotNetUI.InitNumericUpDown 0.1 1000 100
numBoxArray[2].DecimalPlaces = 1
numBoxArray[3] = RS_dotNetUI.InitNumericUpDown 0.1 1000 100
numBoxArray[3].DecimalPlaces = 1
mapPanelTable.Controls.Add numBoxArray[1] 2 0
mapPanelTable.Controls.Add numBoxArray[2] 2 1
mapPanelTable.Controls.Add numBoxArray[3] 2 2
),
fn UVWindowCallback =
(
local hwnd = dialogMonitorOps.getWindowHandle()
print (hwnd) as string
if (uiAccessor.getWindowText hwnd == "Edit UVWs") then
(
print "found edit window"
)
true
),
fn PeelObject obj mapChannel =
(
local geometryType = classOf obj
local openUVEdges
local editChecked = UltimateTerrainMapper.EditCB.Checked
max modify mode
if (UltimateTerrainMapper.FaceMode) then
(
modPanel.AddModToSelection (Unwrap_UVW())
)
else
(
addModifier obj (Unwrap_UVW())
)
obj.Unwrap_UVW.setMapChannel mapChannel
obj.Unwrap_UVW.Edit()
subObjectLevel = 2
openUVEdges = UltimateTerrainMapper.GetOpenUVEdges obj
obj.Unwrap_UVW.SelectEdges openUVEdges
obj.Unwrap_UVW.edgeToVertSelect()
obj.Unwrap_UVW.PinSelected obj
obj.Unwrap_UVW.Fit()
obj.Unwrap_UVW.LSCMSolve()
if (not editChecked) then
(
local hwnd = windows.getChildHWND 0 "Edit UVWs"
if hwnd != undefined then windows.sendMessage hwnd[1] 0x0010 0 0
)
),
fn GetOpenUVEdges obj =
(
local openedgelist = #{}
local facecount = obj.faces.count
local facebitarray = UltimateTerrainMapper.IntegerToBitarray facecount
obj.Unwrap_UVW.SelectFaces facebitarray
obj.Unwrap_UVW.faceToEdgeSelect()
local edgebitarray = obj.Unwrap_UVW.getSelectedEdges()
local edgecount = edgebitarray.count
local edgecountarray = #()
for i = 1 to edgecount do append edgecountarray 0
for face in facebitarray do
(
obj.Unwrap_UVW.SelectFaces #{face}
obj.Unwrap_UVW.faceToEdgeSelect()
local faceedges = obj.Unwrap_UVW.getSelectedEdges()
for thisedge in faceedges do
(
edgecountarray[thisedge] += 1
)
)
for i = 1 to edgecount do
if (edgecountarray[i] == 1) then append openedgelist i
return openedgelist
),
-- Round the supplied number to the nearest multiple.
fn roundToNearestMultiple numToRound multiple = (
local result = undefined
-- Negate the multiple if the number to round is a negative.
if numToRound < 0 and multiple > 0 then (
multiple = -multiple
)
-- Do nothing.
if multiple == 0 then
(
result = numToRound
)
else
(
local remainder = mod numToRound multiple
-- Already at the multiple.
if remainder == 0 then
(
result = numToRound
-- Round up to the multiple.
)
else
(
result = numToRound + multiple - remainder
)
)
result
),
-- Return the world=space position of the selected faces.
fn getFaceSelectionPosition obj =
(
local center = undefined
in coordsys #world
(
if ( getSelectionLevel obj ) == #face then
(
local x = #()
local y = #()
if classof obj.baseobject == Editable_Poly then
(
local faces = ( polyop.getFaceSelection obj.baseobject ) as array
for faceIdx in faces do
(
local verts = ( polyop.getVertsUsingFace obj.baseobject faceIdx ) as array
for vertIdx in verts do
(
local vert = polyop.getVert obj.baseobject vertIdx node:obj
append x vert.x
append y vert.y
)
)
)
else if classof obj.baseobject == Editable_Mesh then
(
local faces = ( getFaceSelection obj.baseobject ) as array
for faceIdx in faces do
(
local verts = ( meshop.getVertsUsingFace obj.baseobject faceIdx ) as array
for vertIdx in verts do
(
local vert = meshop.getVert obj.baseobject vertIdx node:obj
append x vert.x
append y vert.y
)
)
)
local minX = amin x
local minY = amin y
local maxX = amax x
local maxY = amax y
if minX != undefined then (
local bboxMin = [ minX, minY, 0 ]
local bboxMax = [ maxX, maxY, 0 ]
center = ( bboxMin + bboxMax ) / 2.0
)
)
)
center
),
-- MapObject applies a planar map modifier according to values specified in arguments
fn MapObject obj mapChannel mapwidth maplength =
(
local parentPos = obj.pos
-- If any faces are selected, this will return their position in world space.
local faceSelectionPos = getFaceSelectionPosition obj
local uvMod = Uvwmap()
uvMod.mapChannel = mapChannel
uvMod.length = mapLength
uvMod.width = mapWidth
uvMod.height = mapWidth
modPanel.addModToSelection uvMod ui:on
local newGizmoTransform = matrix3 1
local gizmoPos = undefined
-- Faces are currently selected, so use their position.
if faceSelectionPos != undefined then
(
gizmoPos = faceSelectionPos
print gizmoPos
)
else
(
gizmoPos = parentPos
)
if gizmoPos != undefined then
(
local nearestX = roundToNearestMultiple gizmoPos.x mapWidth
local nearestY = roundToNearestMultiple gizmoPos.y mapLength
--newGizmoTransform.row4 = [ 0, 0, 0]
format "gizmoPos: % \n" gizmoPos
--newGizmoTransform.row4 = [ parentPos.x - (parentPos.x + gizmoPos.x), parentPos.y - (parentPos.y + gizmoPos.y), 0]
newGizmoTransform.row4 = [ nearestX - gizmoPos.x, nearestY - gizmoPos.y, 0 ]
uvMod.gizmo.transform = newGizmoTransform
)
),
-- GetGeometryNode returns the id of the highest geometry node in an object's modifier stack
fn GetGeometryNode obj otherModifiers:#() =
(
local geometryNode
local modifierCount = obj.modifiers.count
local modList = #(Edit_Poly, Edit_Mesh)
join modList otherModifiers
if (modifierCount > 0) then
(
for i = 1 to modifierCount do
(
for thisModifier in modList do
(
local testmod = toLower (UltimateTerrainMapper.SwapToken (thisModifier as string) "_" " ")
if (toLower (obj.modifiers[modifierCount - i + 1].name as string) == testmod) then
geometryNode = (modifierCount - i + 1)
)
)
)
if (geometryNode == undefined) then
geometryNode = 0
return geometryNode
),
fn SwapToken instring oldToken newToken =
(
local tokens = filterString instring oldToken
local outstring = tokens[1]
local tokenCount = tokens.count
if tokenCount > 1 then
(
for i = 2 to tokenCount do
outstring += (newToken + tokens[i])
)
return outstring
),
------------------------------------------------------------------------------------------------------------------------------------------
--LOAD/SAVE FUNCTIONS
------------------------------------------------------------------------------------------------------------------------------------------
fn SaveIniFile =
(
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "WinLocX" (UltimateTerrainMapper.Form.Location.x as string)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "WinLocY" (UltimateTerrainMapper.Form.Location.y as string)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Active1" (UltimateTerrainMapper.ActiveCB1.Checked as string)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Active2" (UltimateTerrainMapper.ActiveCB2.Checked as string)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Channel1" (UltimateTerrainMapper.NumBoxSet1[1].Text)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Channel2" (UltimateTerrainMapper.NumBoxSet2[1].Text)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Width1" (UltimateTerrainMapper.NumBoxSet1[2].Text)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Width2" (UltimateTerrainMapper.NumBoxSet2[2].Text)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Height1" (UltimateTerrainMapper.NumBoxSet1[3].Text)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Height2" (UltimateTerrainMapper.NumBoxSet2[3].Text)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "PeelState" (UltimateTerrainMapper.PeelCB.Checked as string)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "EditState" (UltimateTerrainMapper.EditCB.Checked as string)
setINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "CollapseState" (UltimateTerrainMapper.CollapseCB.Checked as string)
),
fn LoadIniFile =
(
--default values
local WinLocX = 100
local WinLocY = 100
local Channel1 = 1
local Channel2 = 2
local Width1 = 100
local Width2 = 100
local Height1 = 100
local Height2 = 100
local Active1 = true
local Active2 = true
local PeelState = true
local EditState = true
local CollapseState = false
try
(
WinLocX = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "WinLocX" as integer
WinLocY = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "WinLocY" as integer
Active1 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Active1" as BooleanClass
Active2 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Active2" as BooleanClass
Channel1 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Channel1" as integer
Channel2 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Channel2" as integer
Width1 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Width1" as integer
Width2 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Width2" as integer
Height1 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Height1" as integer
Height2 = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "Height2" as integer
PeelState = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "PeelState" as BooleanClass
EditState = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "EditState" as BooleanClass
CollapseState = getINISetting UltimateTerrainMapper.IniFilePath "UltimateTerrainMapper" "CollapseState" as BooleanClass
)
catch (print "Failed to read value")
UltimateTerrainMapper.Form.Location = dotNetObject "System.Drawing.Point" WinLocX WinLocY
UltimateTerrainMapper.NumBoxSet1[1].Value = Channel1
UltimateTerrainMapper.NumBoxSet1[2].Value = Width1
UltimateTerrainMapper.NumBoxSet1[3].Value = Height1
UltimateTerrainMapper.NumBoxSet2[1].Value = Channel2
UltimateTerrainMapper.NumBoxSet2[2].Value = Width2
UltimateTerrainMapper.NumBoxSet2[3].Value = Height2
UltimateTerrainMapper.ActiveCB1.Checked = Active1
UltimateTerrainMapper.ActiveCB2.Checked = Active2
UltimateTerrainMapper.PeelCB.Checked = PeelState
UltimateTerrainMapper.EditCB.Checked = EditState
UltimateTerrainMapper.CollapseCB.Checked = CollapseState
UltimateTerrainMapper.ManageMapPanel MapPanel1 Active1
UltimateTerrainMapper.ManageMapPanel MapPanel2 Active2
UltimateTerrainMapper.EditCB.Visible = UltimateTerrainMapper.PeelCB.Checked
),
------------------------------------------------------------------------------------------------------------------------------------------
--UI
------------------------------------------------------------------------------------------------------------------------------------------
fn CreateUI =
(
-- RS_dotNetPreset.Font_Main = dotNetObject "System.Drawing.Font" "Calibri" 9
-- form setup
Form = dotNetObject "maxCustomControls.maxForm"
Form.Text = "Ultimate Terrain Mapper"
Form.StartPosition = (dotNetClass "System.Windows.Forms.FormStartPosition").manual
Form.Location = dotNetObject "system.drawing.point" 0 80
Form.MaximumSize = dotNetObject "System.Drawing.Size" 280 320
Form.MinimumSize = dotNetObject "System.Drawing.Size" 280 320
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 = 5
Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 40)
Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 80)
Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 80)
Table.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 100)
Table.RowStyles.add (RS_dotNetObject.rowStyleObject "absolute" 20)
Table.ColumnCount = 1
Table.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 100)
Table.Margin = RS_dotNetPreset.Padding_None
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 wiki:"Map_Art_Tech" versionNum:1.10 versionName:"Ossified Bean" filename:(getThisScriptFilename())
banner.setup()
Table.Controls.Add RSBannerPanel 0 0
BuildMapPanel "Mapping Operation 1" &ActiveCB1 &NumBoxSet1 &MapPanel1
Table.Controls.Add MapPanel1 0 1
BuildMapPanel "Mapping Operation 2" &ActiveCB2 &NumBoxSet2 &MapPanel2
Table.Controls.Add MapPanel2 0 2
ApplyPanel = dotNetObject "TableLayoutPanel"
ApplyPanel.Dock = RS_dotNetPreset.DS.Fill
ApplyPanel.RowCount = 3
ApplyPanel.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 30)
ApplyPanel.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 30)
ApplyPanel.RowStyles.add (RS_dotNetObject.rowStyleObject "percent" 30)
ApplyPanel.ColumnCount = 2
ApplyPanel.ColumnStyles.add (RS_dotNetObject.columnStyleObject "absolute" 100)
ApplyPanel.ColumnStyles.add (RS_dotNetObject.columnStyleObject "percent" 100)
ApplyPanel.Margin = RS_dotNetPreset.Padding_None
Table.Controls.Add ApplyPanel 0 2
ApplyMappingButton = RS_dotNetUI.InitButton "Apply Mapping"
dotNet.AddEventHandler ApplyMappingButton "Click" ApplyMapping
ApplyMappingButton.Margin = dotNetObject "System.Windows.Forms.Padding" 8
ApplyPanel.SetRowSpan ApplyMappingButton 3
ApplyPanel.Controls.Add ApplyMappingButton 1 0
PeelCB = RS_dotNetUI.InitCheckBox "Peel Edge UVs"
dotNet.AddEventHandler PeelCB "CheckedChanged" PeelChecked
--ApplyPanel.Controls.Add PeelCB 0 0
EditCB = RS_dotNetUI.InitCheckBox "Edit After Peel"
dotNet.AddEventHandler EditCB "CheckedChanged" EditChecked
--ApplyPanel.Controls.Add EditCB 0 1
CollapseCB = RS_dotNetUI.InitCheckBox "Collapse Stack"
dotNet.AddEventHandler CollapseCB "CheckedChanged" CollapseChecked
ApplyPanel.Controls.Add CollapseCB 0 2
Progbar = dotNetObject "ProgressBar"
Progbar.Minimum = 0
Progbar.Maximum = 100
Progbar.Value = 0
Progbar.Dock = RS_dotNetPreset.DS.Fill
Table.Controls.Add ProgBar 0 3
--draw form
Form.ShowModeless()
Form
)
)
if UltimateTerrainMapper != undefined then
(
UltimateTerrainMapper.Form.Close()
)
UltimateTerrainMapper = UltimateTerrainMapperStruct()
UltimateTerrainMapper.CreateUI()