-- -- Description:: Data driven utilities for manipulating the terrain shader -- -- Author:: Luke Openshaw -- Author:: Marissa Warner-Wu -- ----------------------------------------------------------------------------- -- HISTORY -- 11/11/2009 -- by Stuart Macdonald -- Added undo to vert and face colouring -- Fixed bitmap resize load ( line 399 ) -- -- 9/11/2009 -- by Marissa Warner-Wu -- Incorporated terrain texture palette tool from Aaron/Stu. -- -- 4/12/2008 -- by Luke Openshaw -- Update: Hack in Multisub support -- -- 16/05/2014 -- by Neal D Corbett -- Update: UI rewritten to completely use DotNet controls. -- Now compatible with RDR UberTerrain shaders. ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- -- Uses ----------------------------------------------------------------------------- try (gRsTerrainPalette.ToolForm.Close()) catch () Callbacks.RemoveScripts id:#RsTerrainPaint global gRsTerrainPalette = undefined RSTA_LoadCommonFunction #("fn_RSTA_vertexColours.ms") filein (RsConfigGetWildwestDir() + "Script/3dsMax/Maps/Materials/terrain_helperFunctions.ms") filein (RsConfigGetWildWestDir() + "/script/3dsMax/_common_functions/fn_RSTA_ThumbsMgr.ms") filein (RsConfigGetWildWestDir() + "script/3dsMax/General_tools/Selection_Funcs.ms") -- Struct for passing options to 'ev_SelSubObjsMenuItem_Clicked' struct RsTerrPal_SelSubObjOpts (SubObjName, AddSel = False, RemSel = False, IgnoreBitmaps = False, Dominant = False) -------------------------------------------------------------- -- Terrain texture palette: -------------------------------------------------------------- struct RsTerrainPalette ( RsBannerPanel = DotNetObject "Panel", BannerStruct = makeRsBanner dn_Panel:RsBannerPanel wiki:"Terrain_Paint" versionNum:1.53 versionName:"Miraculous Tern", FormSize = [574, 804], TerrainObj, -- Currently-selected mesh-object TerrainMatInfoList = #(), -- Terrain-material info-list taken from current 'TerrainObj' TerrainMatIds, -- MatId lookup-list for 'TerrainMatInfoList' SelMatInfo, -- Currently-selected material-info vPaintTool, -- Active instance of VertexPaint DoneVertChecks = #(), -- Used to track which objects/channels have had 'RsHasCompressedVertChan' run on them. -- Tool's form and defined controls (so functions can quickly find them) ToolForm = dotNetObject "MaxCustomControls.MaxForm", ObjEditPanels = #(), ScrollPanel, ShowChansPanel, TexmapPanel, MatPanels = #(), CboShaderType, ChkPickMat, SpnDeselPercent, ChkVertPaint, SpnFillStrength, SldLookupMask, SldTintMask, SldDisplaceMask, -- Texmap rightclick-menus, per shadertype: TexmapRCMenus = (DataPair ShaderTypes:#() Menus:#()), -- Mask-options control-panel is collapsed by default: MaskDropdownActive = False, MaskCtrlPanel, -- Local aliases to hold button-functions: TexmapBtnFunc, TexmapBtnPaintFunc, SetMaskBtnFunc, -- Thumbnail-manager: ToolName = #TerrainPainter, Thumbs = (RSTA_ThumbsMgr.GetThumbsStruct ToolName), dnTooltip = (dotNetObject "ToolTip"), dnColour = (dotNetClass "System.Drawing.Color"), SizeType_AutoSize = (dotNetClass "SizeType").AutoSize, SizeType_Absolute = (dotNetClass "SizeType").Absolute, SizeType_Percent = (dotNetClass "SizeType").Percent, -- Suspend/ResumeRedraw: -- Disable/Enable tool's window-redraw functionality: WM_SETREDRAW=0xB, fn SuspendRedraw = ( Windows.Sendmessage ToolForm.Handle WM_SETREDRAW 0 0 ), fn ResumeRedraw = ( Windows.Sendmessage ToolForm.Handle WM_SETREDRAW 1 0 ), ------------------------------------------------------------------------------ -- isBtnChecked: -- Returns 'True' if a button has been set to 'Flat' style: ------------------------------------------------------------------------------ fn isBtnChecked ThisBtn = ( return (ThisBtn.FlatStyle == ThisBtn.FlatStyle.Flat) ), ------------------------------------------------------------------------------ -- SetBtnChecked: -- Sets button style to make it look checked or not ------------------------------------------------------------------------------ fn SetBtnChecked ThisBtn Checked = ( if Checked then ( -- Don't allow button to autosize in this mode: local BtnWidth = ThisBtn.Width local BtnHeight = ThisBtn.Height ThisBtn.AutoSize = False ThisBtn.Width = BtnWidth ThisBtn.Height = BtnHeight -- These colours are only shown if button is set to 'Flat' style, ThisBtn.BackColor = dnColour.Orange ThisBtn.ForeColor = dnColour.Black ThisBtn.FlatStyle = ThisBtn.FlatStyle.Flat ) else ( ThisBtn.FlatStyle = ThisBtn.FlatStyle.System ThisBtn.AutoSize = True ) ThisBtn.Refresh() ), --------------------------------------------------------------------------------------- -- GetSelMatInfo: -- Returns currently-selected terrain-material (or 'undefined' if none) --------------------------------------------------------------------------------------- fn GetSelMatInfo UpdateData:False = ( -- Ensure that material-faces are still up-to-date: if UpdateData do ( ::gRsTerrainPalette.PopulateMaterialList() ) if (TexmapPanel.Tag == undefined) then undefined else ( TexmapPanel.Tag.Value.MatInfo ) ), --------------------------------------------------------------------------------------- -- GetSelTexNum: -- Returns currently-selected texmap-index (or '0' if none) --------------------------------------------------------------------------------------- fn GetSelTexNum = ( local TexNum = 0 if (TexmapPanel.Tag != undefined) do ( TexNum = (TexmapPanel.Tag.Value.TexNum) ) return TexNum ), --------------------------------------------------------------------------------------- -- GetSelShaderTypeInfo: -- Returns currently-selected shadertype-info (with material-list) --------------------------------------------------------------------------------------- fn GetSelShaderTypeInfo = ( local SelNum = (CboShaderType.SelectedIndex + 1) if (SelNum == 0) then undefined else TerrainMatInfoList[SelNum] ), --------------------------------------------------------------------------------------- -- GetSelShaderTypeDef: -- Returns currently-active shadertype-definition (or 'undefined' if none) --------------------------------------------------------------------------------------- fn GetSelShaderTypeDef = ( local TypeDef = undefined local SelTypeInfo = GetSelShaderTypeInfo() if (SelTypeInfo != undefined) do ( local MatInfo = SelTypeInfo.MatInfoList[1] if (MatInfo != undefined) do ( TypeDef = MatInfo.TypeDef ) ) return TypeDef ), --------------------------------------------------------------------------------------- -- GetSelShaderMatInfoList: -- Returns material-info list for currently-active shadertype-definition --------------------------------------------------------------------------------------- fn GetSelShaderMatInfoList = ( local MatInfoList = #() local SelTypeInfo = GetSelShaderTypeInfo() if (SelTypeInfo != undefined) do ( MatInfoList = SelTypeInfo.MatInfoList ) return MatInfoList ), --------------------------------------------------------------------------------------- -- GetTexmapsCount: -- Returns maximum texturemap-slot count for shown materials: --------------------------------------------------------------------------------------- fn GetTexmapsCount = ( -- Get current shadertype's material-info list: local MatInfoList = GetSelShaderMatInfoList() -- Collect texcounts for materials local TexCounts = for MatInfo in MatInfoList collect MatInfo.TexCount -- Return highest texcount: return (aMax TexCounts) ), -- DoWeldCheck: -- Check obj for compressed (welded) verts - these will go wrong when painted. -- tracks objects/channels this has been done for this session, as we shouldn't have to repeat) fn DoWeldCheck Obj Chans = ( for Chan in Chans do ( -- Build string from object-handle and specific channel-number: local CheckString = ((Obj.Handle as String) + "|" + (Chan as String)) -- Only run check/fix welded verts if object's channel hasn't been checked already: if (appendIfUnique DoneVertChecks CheckString) do ( RsHasCompressedVertChan Obj.BaseObject Chan fix:True ) ) return OK ), --------------------------------------------------------------------------------------- -- AddGrpBox: -- Function for adding Max-like dotnet group-boxes: --------------------------------------------------------------------------------------- fn AddGrpBox AddToPanel Title:"" Size:1 PanelType: = ( local GrpBox = dotNetObject "GroupBox" GrpBox.FlatStyle = GrpBox.FlatStyle.System GrpBox.Text = Title GrpBox.Dock = GrpBox.Dock.Fill GrpBox.AutoSize = True GrpBox.Padding = (dotNetObject "Padding" 1) -- Add groupbox-panel to parent: AddToPanel.Controls.Add GrpBox -- Add subpanel for holding controls: local SubPanel = GrpBox if (PanelType != unsupplied) do ( SubPanel = dotnetobject PanelType SubPanel.Width = 0 SubPanel.Height = 0 SubPanel.AutoSize = True SubPanel.Dock = SubPanel.Dock.Fill GrpBox.Controls.Add SubPanel ) -- Return inner-panel that'll hold grouped controls: return SubPanel ), -------------------------------------------------------------- -- GetMaskColour: -- Returns colour defined by mask-sliders -------------------------------------------------------------- fn GetMaskColour TypeDef: = ( if (TypeDef == unsupplied) do ( TypeDef = GetSelShaderTypeDef() ) -- Get mask-value from slider(s) if shown: local MaskVals = for Item in #( (dataPair Slider:SldLookupMask Default:TypeDef.DefLookupMaskVal), (dataPair Slider:SldTintMask Default:TypeDef.DefTintMaskVal), (dataPair Slider:SldDisplaceMask Default:TypeDef.DefDisplaceMaskVal) ) collect ( local MaskVal = undefined local MaskSlider = Item.Slider if (MaskSlider == undefined) then ( MaskVal = Item.Default ) else ( MaskVal = MaskSlider.Value / (Float MaskSlider.Maximum) ) MaskVal ) local MaskColour if (TypeDef.LookupMaskSubChan == undefined) then ( -- Lookup-mask using full greyscale channel: MaskColour = (White * MaskVals[1]) ) else ( -- Build mask-colour from slider-values: MaskColour = [0,0,0] MaskColour[TypeDef.LookupMaskSubChan] = MaskVals[1] * 255 MaskColour[TypeDef.TintMaskSubChan] = MaskVals[2] * 255 MaskColour[TypeDef.DisplaceMaskSubChan] = MaskVals[3] * 255 MaskColour = (MaskColour as Color) ) return MaskColour ), -- Remove/Add callbacks: fn RemoveCallbacks = ( Callbacks.RemoveScripts id:#RsTerrainPaint ), fn AddCallbacks = ( -- Set up selection-change callback: Callbacks.AddScript #SelectionSetChanged "gRsTerrainPalette.UpdateCtrls()" id:#RsTerrainPaint ), -------------------------------------------------------------- -- PaintSelection: -- Paints selected colour to selected subobjects -------------------------------------------------------------- fn PaintSelection MaskColour: MaskSubChans: = ( -- 'MaskSubChans' argument is only supplied by mask-edit buttons: local JustMask = (MaskSubChans != unsupplied) -- Set default mask-subchannels: if (not JustMask) do (MaskSubChans = #{1,2,3}) -- Get selected-texmap index from panel-tag: local TexNum = if JustMask then -1 else (GetSelTexNum()) -- Abort if no material-selection has been made: if (TexNum == undefined) or (TexNum == 0) do return False if not ((isValidNode TerrainObj) and (isEditPolyMesh TerrainObj)) do return False local TypeDef = GetSelShaderTypeDef() if (TypeDef == undefined) do return False local TexClrList = TypeDef.LookupColours local LookupChan = TypeDef.LookupClrChan local MaskChan = TypeDef.MaskChan -- Colour-edit definitions: local EditDefs = #() -- Does this shader use a lookup-texture mask-channel? local HasMask = (MaskChan != undefined) if HasMask do ( -- Get 'MaskColour' rgb values from sliders/defaults: if (not isKindOf MaskColour Color) do ( MaskColour = GetMaskColour TypeDef:TypeDef ) -- Add mask-edit to list: local ModName = ("T-Mask [chan " + (MaskChan as string) + "]") local MaskEdit = (RsVertClrsEditDef Chan:MaskChan Action:#Set InputVal:MaskColour ModName:ModName SubChans:MaskSubChans) append EditDefs MaskEdit ) -- Define colour-edit definition for lookup: if (not JustMask) do ( local ModName = ("T-Lookup [chan " + (LookupChan as string) + "]") local LookupEdit = (RsVertClrsEditDef Chan:LookupChan Action:#Set InputVal:TexClrList[TexNum] ModName:ModName) append EditDefs LookupEdit ) -- We'll trigger multiple redraws for face-edits: local DidFaces = False case (GetSelectionLevel TerrainObj) of ( #Object: ( DidFaces = True ) #face: ( DidFaces = True ) #vertex: ( -- Vertex-editing will go wrong if channels have uvs used by multiple verts - fix if requried: -- (once per channel per object per tool-session) ( local CheckChans = #(LookupChan) if HasMask do (append CheckChans MaskChan) -- Check for compressed (welded) verts: DoWeldCheck TerrainObj CheckChans ) ) ) -- Apply edits to selection: -- (with selection-change callbacks disabled) RemoveCallbacks() RsApplyVertClrChanges EditDefs doCollapse:True AddCallbacks() return OK ), -------------------------------------------------------------- -- Functions for dealing with VertexPaint modifier: -------------------------------------------------------------- -- Sets state of control 'ChkVertPaint': fn SetChkVertPaintState State = ( SetBtnChecked ChkVertPaint State ChkVertPaint.Text = if State then "Stop Vertex Paint" else "Start Vertex Paint" -- Clear link to modifier if deactivating vpaint-mode: if (not State) do ( vPaintTool = undefined ) ), fn SetVertPaintColour = ( local SelNum = GetSelTexNum() if (SelNum == 0) or (vPaintTool == undefined) do return False local SelMatInfo = GetSelMatInfo() if (SelMatInfo == undefined) do return False local TexClrList = SelMatInfo.TypeDef.LookupColours vPaintTool.PaintColor = TexClrList[SelNum] vPaintTool.CurPaintMode = 1 -- Trigger toolkit-update: vPaintTool.keepToolboxOpen = vPaintTool.keepToolboxOpen ), fn SetupVertPaintMod = ( -- Get currently-selected shadertype-definition: local TypeDef = GetSelShaderTypeDef() -- Abort if TypeDef is undefined or terrain-object is invalid: if (TypeDef == undefined) or (not (isValidNode TerrainObj)) or (not (isEditPolyMesh TerrainObj)) do ( -- Deactivate button: SetChkVertPaintState False return False ) -- Get lookup-colour channel: local LookupClrChan = TypeDef.LookupClrChan -- Make sure that object doesn't have welded lookup-verts: DoWeldCheck TerrainObj #(LookupClrChan) -- Search for lookup-vertexpaint modifier: local vPaintMod = undefined for ThisMod in TerrainObj.Modifiers while (vPaintMod == undefined) do ( -- Is this a vertexpaint modifier, set up to edit the lookup-channel? if (isKindOf ThisMod VertexPaint) and (ThisMod.MapChannel == LookupClrChan) do ( vPaintMod = ThisMod ) ) -- Add modifier if one wasn't found: if (vPaintMod == undefined) do ( vPaintMod = VertexPaint MapChannel:LookupClrChan AddModifier TerrainObj vPaintMod ) -- Get link to vertexpaint-tool: vPaintTool = VertexPaintTool() -- Set vertexpaint-colour to currently-active lookup-colour, if any: SetVertPaintColour() ), ------------------------------------------------------------------------------ -- Functions for dealing with 'Select Channel' controls: ------------------------------------------------------------------------------ -- GetShowChanCtrls: -- Get list of 'Show Channel' buttons: ------------------------------------------------------------------------------ fn GetShowChanCtrls = ( for n = 0 to (ShowChansPanel.Controls.Count - 1) collect ( ShowChansPanel.Controls.Item[n] ) ), ------------------------------------------------------------------------------ -- GetCurrShowChan: -- Get name of objects' currently-shown terrain-channel: ------------------------------------------------------------------------------ fn GetShownChan = ( local ChanNum = undefined local SelObjs = for Obj in (GetCurrentSelection()) where (isEditPolyMesh Obj) collect Obj for Obj in SelObjs do ( local ObjChan = False case of ( (DisplayColor.Shaded == #Material): ( if (not Obj.ShowVertexColors) do ( ObjChan = -3 ) ) (not Obj.ShowVertexColors):() (Obj.VertexColorType == #Alpha): ( ObjChan = -2 ) (Obj.VertexColorType == #Color): ( ObjChan = 0 ) (Obj.VertexColorType == #Map_Channel): ( ObjChan = Obj.VertexColorMapChannel ) ) case of ( -- Stop looping objects if one returns undefined channel-number: (ObjChan == undefined):(exit) -- Keep first channel-number found: (ChanNum == undefined):(ChanNum = ObjChan) -- Clear ChanNum and stop looping if objects don't share channel-number (ChanNum != ObjChan): ( ChanNum = undefined exit ) ) ) return ChanNum ), ------------------------------------------------------------------------------ -- SetShowChanClicked: -- Set matching channel-button to clicked, if applicable. ------------------------------------------------------------------------------ fn SetShowChanClicked ShownChan: = ( -- Get name of currently-shown channel: if (ShownChan == unsupplied) do ( ShownChan = GetShownChan() ) -- Uncheck all buttons, apart from one with matching name: for ThisBtn in (GetShowChanCtrls()) do ( SetBtnChecked ThisBtn (ThisBtn.Tag == ShownChan) ) ), ------------------------------------------------------------------------------ -- ShowChannel: -- Show a particular vertex-colour channel on 'TerrainObj': ------------------------------------------------------------------------------ fn ShowChannel chan = ( RsShowObjVertChannel chan ), ------------------------------------------------------------------------------ -- ev_SwapMenuItem_Clicked: -- Used by texmap-swapper menu-items -- Swaps two texmap-lookup colours for the current material: ------------------------------------------------------------------------------ fn ev_SwapMenuItem_Clicked Sender Args = ( -- Get swap-options from control's tag: local SwapOpts = Sender.Tag.Value -- Get selected material-data, with updated face-list: local TheTool = ::gRsTerrainPalette local SelMatInfo = (TheTool.GetSelMatInfo UpdateData:True) local SelTexNum = (TheTool.GetSelTexNum()) local SwapTexNum = (SwapOpts.SwapTexNum) if (SelMatInfo == undefined) or (SelTexNum == 0) or (SelTexNum == SwapTexNum) do return False -- Alter material/mesh: SelMatInfo.SwapTexmaps SelTexNum SwapTexNum SwapMatSlots:(SwapOpts.SwapMatSlots) SwapLookups:(SwapOpts.SwapLookups) -- Update UI buttons if required: if (SwapOpts.SwapMatSlots) do ( local SelMat = SelMatInfo.TerrainMat -- Find material-panel whose tag-value's material matches selected material: local MatPanel = undefined for ThisPanel in TheTool.MatPanels while (MatPanel == undefined) do ( local PanelMatInfo = ThisPanel.Tag.Value if (PanelMatInfo.TerrainMat == SelMat) do ( MatPanel = ThisPanel ) ) -- Get the buttons for the swapped textures: if (MatPanel != undefined) do ( -- Get buttons for swap's two texmaps: local Btns = for TexNum in #(SelTexNum, SwapTexNum) collect ( -- Get button's highlight-colour panel: local Ctrl = MatPanel.Controls.Item[TexNum - 1] -- Get coloured-outline panel: Ctrl = Ctrl.Controls.Item[0] -- Collect button: Ctrl.Controls.Item[0] ) -- Swap button-images: local BtnA = Btns[1] local BtnB = Btns[2] local ImgA = BtnA.BackgroundImage BtnA.BackgroundImage = BtnB.BackgroundImage BtnB.BackgroundImage = ImgA ) ) ), ---------------------------------------------------------------------------------- -- SelSubObjs: -- Triggers subobject-selection functions with passed options ---------------------------------------------------------------------------------- fn SelSubObjs Opts = ( local SubObjName = (Opts.SubObjName as Name) local TheTool = ::gRsTerrainPalette -- Get selected material-data, with updated face-list: local SelMatInfo = (TheTool.GetSelMatInfo UpdateData:True) local SelTexNum = (TheTool.GetSelTexNum()) if (SelMatInfo == undefined) do return False local BaseObj = SelMatInfo.Obj.BaseObject if not ((isKindOf BaseObj Editable_Poly) or (isKindOf BaseObj Editable_Mesh)) do return False SetWaitCursor() -- Get current subobject-selection, if it's going to be modified: local SelItems = #{} if (Opts.AddSel or Opts.RemSel) do ( local SubObjLevel = RsGetSubObjLevelName() case of ( ((SubObjName == #Verts) and (SubObjLevel == #Vertex)): ( SelItems = if (isKindOf BaseObj Editable_Poly) then (PolyOp.GetVertSelection BaseObj) else (GetVertSelection BaseObj) ) ((SubObjName == #Faces) and (SubObjLevel == #Face)): ( SelItems = if (isKindOf BaseObj Editable_Poly) then (PolyOp.GetFaceSelection BaseObj) else (GetFaceSelection BaseObj) ) ) ) -- Get verts/faces using selected texmap-index: local GetSubObjsFunc = case SubObjName of ( #Verts:(SelMatInfo.GetVertsUsingTexmap) #Faces:(SelMatInfo.GetFacesUsingTexmap) ) -- Selects all faces/verts if 'SelTexNum' is zero: local NewSelItems = GetSubObjsFunc SelTexNum IgnoreBitmaps:Opts.IgnoreBitmaps Dominant:Opts.Dominant -- Modify select-array: if Opts.RemSel then ( SelItems -= NewSelItems ) else ( SelItems += NewSelItems ) SetCommandPanelTaskMode #Modify ModPanel.SetCurrentObject BaseObj ui:True -- Select those verts/faces: case (classOf BaseObj) of ( (Editable_Poly): ( case SubObjName of ( #Verts: ( SubObjectLevel = 1 PolyOp.SetVertSelection BaseObj SelItems ) #Faces: ( SubObjectLevel = 4 PolyOp.SetFaceSelection BaseObj SelItems ) ) ) (Editable_Mesh): ( case SubObjName of ( #Verts: ( SubObjectLevel = 1 SetVertSelection BaseObj.Mesh SelItems ) #Faces: ( SubObjectLevel = 3 SetFaceSelection BaseObj.Mesh SelItems ) ) ) ) CompleteRedraw() SetArrowCursor() return OK ), ---------------------------------------------------------------------------------- -- ev_SelSubObjsMenuItem_Clicked: -- Triggers subobject-selection with options passed from control-tag ---------------------------------------------------------------------------------- fn ev_SelSubObjsMenuItem_Clicked Sender Args = ( local SelOpts = (Sender.Tag.Value) ::gRsTerrainPalette.SelSubObjs SelOpts ), -- Copies selected texmap's path to clipboard: fn ev_CopyPathMenuItem_Clicked Sender Args = ( local TheTool = ::gRsTerrainPalette local SelMatInfo = (TheTool.GetSelMatInfo()) local SelTexNum = (TheTool.GetSelTexNum()) if (SelMatInfo == undefined) or (SelTexNum < 1) do return False local ThisPath = SelMatInfo.DiffuseTexPaths[SelTexNum] SetClipboardText (ThisPath as string) ), fn ev_ApplyClrMenuItem_Clicked Sender Args = ( local TheTool = Sender.Tag.Value TheTool.PaintSelection() ), -- Select faces used by a specific material: fn ev_BtnMatSelFaces_Clicked Sender Args = ( -- Set selected-material value from button's tag: gRsTerrainPalette.SetSelLookupTex Sender.Tag local SelOpts = (RsTerrPal_SelSubObjOpts SubObjName:"Faces") ::gRsTerrainPalette.SelSubObjs SelOpts ), -- Apply selected material to selected faces: fn ev_BtnApplyMat_Clicked Sender Args = ( -- Get selected-material value from button's tag: gRsTerrainPalette.SetSelLookupTex Sender.Tag local SelMatInfo = Sender.Tag.Value.MatInfo -- Get object and MatId for selected material: local Obj = SelMatInfo.Obj local SelMatId = SelMatInfo.MatId local ObjOp = RsMeshPolyOp Obj local ObjSetFaceMatID = RsSetFaceMatIDFunc Obj -- Change face matids: for FaceNum in (Obj.SelectedFaces as BitArray) do ( ObjSetFaceMatID Obj FaceNum SelMatId ) -- Update mesh in viewport: case ObjOp of ( polyOp:(polyOp.collapseDeadStructs Obj) meshOp:(update Obj) ) ), -- Open/Close mask-sliders panel: fn ev_ChkMaskOptions_Clicked Sender Args = ( local TheTool = Sender.Tag.Value TheTool.MaskDropdownActive = (not TheTool.MaskDropdownActive) -- Stop redraws while tweaking layout: local FormHeight = TheTool.ToolForm.Height TheTool.ToolForm.Height += 1000 local ScrollPanel = TheTool.ScrollPanel ScrollPanel.Parent.SuspendLayout() ScrollPanel.SuspendLayout() -- Toggle checkbutton's appearance: TheTool.SetBtnChecked Sender TheTool.MaskDropdownActive -- Rebuild controls in masks-panel: TheTool.UpdateMaskingControls() -- Allow form to redraw: TheTool.ToolForm.Height = FormHeight ScrollPanel.ResumeLayout() ScrollPanel.Parent.ResumeLayout() ), ----------------------------------------------------------------------------------------- -- GetTexmapRCMenu: -- Generates a rightclick-menu to be applied to texmap-id buttons: ----------------------------------------------------------------------------------------- fn GetTexmapRCMenu TypeDef: = ( if (TypeDef == unsupplied) do ( TypeDef = GetSelShaderTypeDef() ) -- Have we already generated a rightclick-menu for this shadertype? local MenuNum = FindItem TexmapRCMenus.ShaderTypes TypeDef.Type if (MenuNum != 0) do ( -- Re-use pre-generated menu: return TexmapRCMenus.Menus[MenuNum] ) local NewMenu = (DotNetObject "ContextMenuStrip") -- Add 'Copy texmap path' menuitem: ( local CmdItem = DotNetObject "ToolStripMenuItem" "Copy Filename to Clipboard" NewMenu.Items.Add CmdItem CmdItem.Tag = (DotNetMxsValue This) DotNet.SetLifetimeControl CmdItem #Dotnet DotNet.AddEventHandler CmdItem "Click" ev_CopyPathMenuItem_Clicked ) NewMenu.Items.Add (DotNetObject "ToolStripSeparator") -- Add selection-menus to 'NewMenu': ( -- Does this material use a lookup-bitmap? local UseLookupMask = (TypeDef.MaskChan != undefined) for SubObjName in #("Verts", "Faces") do ( local SelSubObjText = ("Select " + SubObjName + ":") local TypeMenu = (DotNetObject "ToolStripMenuItem" SelSubObjText) NewMenu.Items.Add TypeMenu DotNet.SetLifetimeControl TypeMenu #Dotnet for DomItem in #(DataPair Text:"Using this material/texmap" Val:False, DataPair Text:"Where material/texmap is Dominant" Val:True) do ( local DomMenu = (DotNetObject "ToolStripMenuItem" DomItem.Text) TypeMenu.DropDownItems.Add DomMenu DotNet.SetLifetimeControl DomMenu #Dotnet -- Bitmap-Ignore menu is only used if shader actually uses a lookup-bitmap... local IgBmpItems = #(DataPair Text:"Just Vertex Lookup" Val:True) if UseLookupMask do ( append IgBmpItems (DataPair Text:"Include Texture Lookup" Val:False) ) local PrevMenu = DomMenu for IgBmpItem in IgBmpItems do ( -- Add Bitmap-Ignore menu, if valid: if UseLookupMask do ( PrevMenu = (DotNetObject "ToolStripMenuItem" IgBmpItem.Text) DomMenu.DropDownItems.Add PrevMenu DotNet.SetLifetimeControl PrevMenu #Dotnet ) -- Add selection-commands: local SelItems = for SelOptItem in #( DataPair Text:("Select " + SubObjName) Val:#Sel, DataPair Text:"Add to selection" Val:#Add, DataPair Text:"Remove from selection" Val:#Rem) do ( -- Create menuitem: local CmdItem = (DotNetObject "ToolStripMenuItem" SelOptItem.Text) PrevMenu.DropDownItems.Add CmdItem DotNet.SetLifetimeControl CmdItem #Dotnet -- Store options to tag: local SelOpts = (RsTerrPal_SelSubObjOpts SubObjName:SubObjName IgnoreBitmaps:IgBmpItem.Val Dominant:DomItem.Val) case SelOptItem.Val of ( #Add:(SelOpts.AddSel = True) #Rem:(SelOpts.RemSel = True) ) CmdItem.Tag = (DotNetMxsValue SelOpts) DotNet.AddEventHandler CmdItem "Click" ev_SelSubObjsMenuItem_Clicked ) ) ) ) ) NewMenu.Items.Add (DotNetObject "ToolStripSeparator") -- Add texmap-swaps submenu: ( local SubMenu = DotNetObject "ToolStripMenuItem" "Swap Texmap with:" NewMenu.Items.Add SubMenu local ClrNames = TypeDef.LookupColourNames local TexCount = GetTexmapsCount() -- Options for swap-event function: struct SwapOptsDef (Text, SwapTexNum, SwapMatSlots = True, SwapLookups = True) for SwapTexNum = 1 to TexCount do ( local ClrName = ClrNames[SwapTexNum] local MenuName = StringStream "" format "%: (%)" SwapTexNum ClrName To:MenuName -- Create menuitem: local SwapItem = DotNetObject "ToolStripMenuItem" (MenuName as String) SubMenu.DropDownItems.Add SwapItem DotNet.SetLifetimeControl SwapItem #Dotnet -- Add submenu-options: for SwapOpts in #( (SwapOptsDef Text:"Swap texmaps/vertex-colours" SwapTexNum:SwapTexNum SwapMatSlots:True SwapLookups:True), (SwapOptsDef Text:"Just swap texmap-slots" SwapTexNum:SwapTexNum SwapMatSlots:True SwapLookups:False), (SwapOptsDef Text:"Just swap vertex-colours" SwapTexNum:SwapTexNum SwapMatSlots:False SwapLookups:True) ) do ( local CmdItem = DotNetObject "ToolStripMenuItem" SwapOpts.Text SwapItem.DropDownItems.Add CmdItem CmdItem.Tag = (DotNetMxsValue SwapOpts) DotNet.AddEventHandler CmdItem "Click" ev_SwapMenuItem_Clicked DotNet.SetLifetimeControl CmdItem #Dotnet ) ) ) NewMenu.Items.Add (DotNetObject "ToolStripSeparator") -- Add 'Apply to Selection' menuitem: ( local CmdItem = DotNetObject "ToolStripMenuItem" "Apply to Selection" NewMenu.Items.Add CmdItem DotNet.SetLifetimeControl NewMenu #Dotnet CmdItem.Tag = (DotNetMxsValue This) DotNet.SetLifetimeControl CmdItem #Dotnet DotNet.AddEventHandler CmdItem "Click" ev_ApplyClrMenuItem_Clicked ) -- Store menu for later retrieval: append TexmapRCMenus.ShaderTypes TypeDef.Type append TexmapRCMenus.Menus NewMenu return NewMenu ), -- Adds/updates controls in panel 'MaskCtrlPanel' fn UpdateMaskingControls = ( MaskCtrlPanel.Controls.Clear() MaskCtrlPanel.RowCount = 0 -- Get TypeDef for currently-shown shadertype: local TypeDef = GetSelShaderTypeDef() -- Does this shader-type use a texturemap-mask? local HasMaskChan = (TypeDef.MaskChan != undefined) if (MaskDropdownActive) do ( if HasMaskChan do ( -- Collect items for deciding which mask-controls to show: local MaskItems = #() -- Collect mask-slider definitions: ( struct MaskCtrlDef (CtrlName, Text, DefaultVal, ChanText, RangeText, SubChan) local LookupMaskDef = (MaskCtrlDef CtrlName:#SldLookupMask SubChan:TypeDef.LookupMaskSubChan Text:"Lookup" DefaultVal:TypeDef.DefLookupMaskVal ChanText:TypeDef.LookupMaskChanText RangeText:TypeDef.LookupMaskRangeText) append MaskItems LookupMaskDef -- Add definition for tint-mask controls, if used: if (TypeDef.TintMaskSubChan != undefined) do ( append MaskItems (MaskCtrlDef CtrlName:#SldTintMask Text:"Tint" SubChan:TypeDef.TintMaskSubChan DefaultVal:TypeDef.DefTintMaskVal ChanText:TypeDef.TintMaskChanText RangeText:TypeDef.TintMaskRangeText) ) -- Add definition for displacement-mask controls, if used: if (TypeDef.DisplaceMaskSubChan != undefined) do ( append MaskItems (MaskCtrlDef CtrlName:#SldDisplaceMask Text:"Displacement" SubChan:TypeDef.DisplaceMaskSubChan DefaultVal:TypeDef.DefDisplaceMaskVal ChanText:TypeDef.DisplaceMaskChanText RangeText:TypeDef.DisplaceMaskRangeText) ) ) -- Set number of rows in hideable panel's table: MaskCtrlPanel.RowCount = (MaskItems.Count + 1) -- Add mask-opacity sliders: for MaskDef in MaskItems do ( -- Table to hold slider/labels: ( local MaskLblText = (MaskDef.Text + " Mask: [" + MaskDef.ChanText + "]") local MaskTable = AddGrpBox MaskCtrlPanel Title:MaskLblText PanelType:"TableLayoutPanel" MaskTable.Parent.Margin = (dotNetObject "Padding" 0) MaskTable.Padding = (dotNetObject "Padding" 0) MaskTable.Dock = MaskTable.Dock.Fill MaskTable.AutoSize = True MaskTable.ColumnCount = 3 MaskTable.RowCount = 2 MaskTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Absolute 115) MaskTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Percent 100) MaskTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Absolute 115) MaskTable.RowStyles.Add (dotNetObject "RowStyle" SizeType_Absolute 25) MaskTable.RowStyles.Add (dotNetObject "RowStyle" SizeType_Absolute 25) -- Which sub-channel (rgb) is this mask on? (greyscale if undefined) local maskSubChans = if (maskDef.subChan == Undefined) then #{1,2,3} else #{maskDef.subChan} -- Add 0%/100% buttons: for Item in #(DataPair Text:("[0% - " + MaskDef.RangeText.Min + "]") ColIdx:0, DataPair Text:("[100% - " + MaskDef.RangeText.Max + "]") ColIdx:2) do ( local NewBtn = DotNetObject "Button" NewBtn.Text = Item.Text NewBtn.Tag = (DotNetMxsValue maskSubChans) NewBtn.FlatStyle = NewBtn.FlatStyle.System NewBtn.Width = 110 dnTooltip.SetToolTip NewBtn "Set this mask-channel value for selected subobjects" MaskTable.Controls.Add NewBtn Item.ColIdx 0 dotNet.addEventHandler NewBtn "MouseUp" SetMaskBtnFunc dotNet.setLifetimeControl NewBtn #dotnet ) -- Mask-value slider: ( local MaskSlider = (dotNetObject "TrackBar") MaskSlider.Margin = (dotNetObject "Padding" 0) MaskSlider.Padding = (dotNetObject "Padding" 0) MaskSlider.Dock = MaskSlider.Dock.Fill MaskSlider.Height -= 12 MaskSlider.Minimum = 0 MaskSlider.Maximum = 4 MaskSlider.Value = (MaskSlider.Maximum * MaskDef.DefaultVal) MaskSlider.TickFrequency = 1 MaskTable.Controls.Add MaskSlider 1 0 local ToolText = ("Set proportion of " + MaskDef.Text + " that comes from vertex-colours\n(when texmap-buttons are clicked)\n\nThis masks out material's '" + MaskDef.Text + "' texture.") dnTooltip.SetToolTip MaskTable ToolText -- Store slider value to SldLookupMask or SldTintMask: SetProperty This MaskDef.CtrlName MaskSlider ) -- Add set-from-slider button: ( local NewBtn = DotNetObject "Button" NewBtn.Text = ("^ Apply slider value ^") NewBtn.Tag = (DotNetMxsValue maskSubChans) NewBtn.FlatStyle = NewBtn.FlatStyle.System NewBtn.AutoSize = True NewBtn.Dock = NewBtn.Dock.Fill NewBtn.Margin = (dotNetObject "Padding" 0 3 0 6) dnTooltip.SetToolTip NewBtn "Set slider's mask-channel value for selected subobjects" MaskTable.Controls.Add NewBtn 1 1 dotNet.addEventHandler NewBtn "MouseUp" SetMaskBtnFunc dotNet.setLifetimeControl NewBtn #dotnet ) ) ) -- Add all-subchannels mask-edit button: (centred) ( local BtnTable = AddGrpBox MaskCtrlPanel PanelType:"TableLayoutPanel" BtnTable.Parent.Margin = (dotNetObject "Padding" 0) BtnTable.Padding = (dotNetObject "Padding" 0) BtnTable.Dock = BtnTable.Dock.Fill BtnTable.AutoSize = True BtnTable.ColumnCount = 3 BtnTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Absolute 115) BtnTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Percent 100) BtnTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Absolute 115) BtnTable.RowStyles.Add (dotNetObject "RowStyle" SizeType_Absolute 25) local NewBtn = DotNetObject "Button" NewBtn.Text = "^ Apply all slider values ^" NewBtn.Tag = DotNetMxsValue #{1,2,3} NewBtn.FlatStyle = NewBtn.FlatStyle.System NewBtn.Dock = NewBtn.Dock.Fill NewBtn.Margin = (dotNetObject "Padding" 0 0 0 6) dnTooltip.SetToolTip NewBtn "Applies mask-slider values to selected subobjects' mask-channel\n(doesn't touch the Lookup channel)" BtnTable.Controls.Add NewBtn 1 0 dotNet.addEventHandler NewBtn "MouseUp" SetMaskBtnFunc dotNet.setLifetimeControl NewBtn #dotnet ) ) ) ), -------------------------------------------------------------- -- Update texmap-button selection: -------------------------------------------------------------- fn SetSelLookupTex SelTexInfoVal = ( -- Store material/texmap-index info: TexmapPanel.Tag = SelTexInfoVal if (SelTexInfoVal == undefined) do return OK -- Extract selected-material/texmap info from tag-data: local SelTexInfo = SelTexInfoVal.Value local MatInfo = SelTexInfo.MatInfo local SelNum = SelTexInfo.TexNum -- These colours are safe to use with this colour-index: local SafeBlends = undefined if (SelNum != 0) and (MatInfo != undefined) do ( SafeBlends = MatInfo.TypeDef.LookupSafeCombos[SelNum] ) if (SelNum != 0) do ( local FormBackClr = TexmapPanel.BackColor local SelClr = FormBackClr.Yellow -- Update texmap-selectedness colours: for TexmapsPanel in MatPanels do ( -- Get matinfo from this panel's tag-value: local PanelMatInfo = TexmapsPanel.Tag.Value -- Does this panel's matinfo match the newly-selected one? local isSelMat = (PanelMatInfo == SelTexInfo.MatInfo) for n = 1 to TexmapsPanel.Controls.Count do ( -- Highlight texmaps if they match selected MatInfo/TexNum: local ThisClr = if (isSelMat) and (n == SelNum) then SelClr else FormBackClr local HiliteBox = TexmapsPanel.Controls.Item[n - 1] HiliteBox.BackColor = ThisClr if (SafeBlends != undefined) do ( local ClrBtn = HiliteBox.Controls.Item[0].Controls.Item[0] ClrBtn.Text = if SafeBlends[n] then "Safe" else "Artifact" ) ) -- Make sure yellow bits aren't left behind: TexmapsPanel.Refresh() ) ) return OK ), ------------------------------------------------------------------------------ -- AddMatCmdPanel: -- Add select/apply buttons for a given MatInfo: ------------------------------------------------------------------------------ fn AddMatCmdPanel AddToPanel ThisMatInfo = ( local BtnWidth = Thumbs.ThumbSize local MatCmdPanelHeight = (BtnWidth + 3) local CmdBtnSize = [90,18] local MatBtnsPanel = (AddGrpBox AddToPanel PanelType:"TableLayoutPanel") AddToPanel.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_AutoSize) local MatBtnsOutPanel = MatBtnsPanel.Parent--.Parent MatBtnsOutPanel.Dock = MatBtnsOutPanel.Dock.None MatBtnsPanel.Margin = (dotNetObject "Padding" 2 2 2 2) struct CtrlDef (Text, Tooltip, Func, ifHasMat = False) for Item in #( CtrlDef Text:"Select Faces" Tooltip:"Select faces using this material" Func:ev_BtnMatSelFaces_Clicked, CtrlDef Text:"Apply Material" Tooltip:"Apply this material to selected faces" Func:ev_BtnApplyMat_Clicked ifHasMat:True ) do ( if (not Item.ifHasMat) or (ThisMatInfo.TerrainMat != undefined) do ( local NewBtn = DotNetObject "Button" NewBtn.Text = Item.Text NewBtn.FlatStyle = NewBtn.FlatStyle.System NewBtn.Width = CmdBtnSize.X NewBtn.Height = CmdBtnSize.Y NewBtn.Margin = (dotNetObject "Padding" 4 0 4 6) -- Tag button with material-info: NewBtn.Tag = (DotNetMxsValue (DataPair MatInfo:ThisMatInfo TexNum:0)) dnTooltip.SetToolTip NewBtn Item.Tooltip MatBtnsPanel.Controls.Add NewBtn dotNet.addEventHandler NewBtn "MouseUp" Item.Func dotNet.setLifetimeControl NewBtn #dotnet ) ) return MatBtnsOutPanel ), ------------------------------------------------------------------------------ -- UpdateTexmapCtrls: -- Set up controls in 'TexmapPanel' ------------------------------------------------------------------------------ fn UpdateTexmapCtrls = ( local TimeStart = TimeStamp() SetWaitCursor() PushPrompt "Redrawing Controls..." SuspendRedraw() ScrollPanel.SuspendLayout() -- Reset groupbox-label: TexmapPanel.Parent.Text = "Texturemaps:" -- Clear existing button-panels: ( for TexPanel in MatPanels do ( TexPanel.Controls.Clear() ) MatPanels.Count = 0 ) -- Clear panel: TexmapPanel.Controls.Clear() SldLookupMask = undefined SldTintMask = undefined SldDisplaceMask = undefined -- Tag-value is used to store active material/colour-index - clear selection: TexmapPanel.Tag = undefined -- Get shadertype: local TypeDef = GetSelShaderTypeDef() local HelperText = undefined -- Add per-material panels: if (TypeDef != undefined) do ( -- Get shadertype's material-info list: local MatInfoList = GetSelShaderMatInfoList() -- Add shader-name to groupbox-label: TexmapPanel.Parent.Text += (" [" + TypeDef.Pattern + "]") -- Does this shader-type use a texturemap-mask? local HasMaskChan = (TypeDef.MaskChan != undefined) -- This shadertype's lookup-colours: local LookupClrs = TypeDef.LookupColours local BtnWidth = Thumbs.ThumbSize local ScrollPanelHeight = 270 local ScrollPanelWidth = 10 + (BtnWidth * 4) -- Generate/get rightclick-menu to apply to texmap-buttons... local TexMapRCmenu = (GetTexmapRCMenu TypeDef:TypeDef) -- Add groupbox to surround material-panels: local MatsOuterPanel = (AddGrpBox TexmapPanel PanelType:"TableLayoutPanel") -- Collect list of texmap-panels per material: MatPanels = for ThisMatInfo in MatInfoList collect ( -- Add groupbox-panel to hold texmap-thumbnails: local MatPanel = AddGrpBox MatsOuterPanel Title:(ThisMatInfo.GetNameString()) PanelType:"TableLayoutPanel" MatsOuterPanel.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) MatPanel.Tag = (DotNetMxsValue ThisMatInfo) -- Add columns for texmap-buttons, and material-buttons panel: local TexCount = ThisMatInfo.TexCount MatPanel.ColumnCount = (TexCount + 1) -- Add per-texmap panels - background-colour will be changed if texmap is selected: for ClrNum = 1 to TexCount do ( -- Add sub-panel to hold colour's controls: local TexHilitePanel = DotNetObject "FlowLayoutPanel" TexHilitePanel.AutoSize = True TexHilitePanel.Margin = (dotNetObject "Padding" 0 0 2 0) TexHilitePanel.Padding = (dotNetObject "Padding" 2 2 2 2) -- Tag panel with material-info and texmap-index: TexHilitePanel.Tag = DotNetMxsValue (dataPair MatInfo:ThisMatInfo TexNum:ClrNum) MatPanel.Controls.Add TexHilitePanel MatPanel.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Absolute (BtnWidth + 12)) local TexPath = ThisMatInfo.DiffuseTexPaths[ClrNum] -- Get colour from list, and make colour for overlaid text: local LookupClr = LookupClrs[ClrNum] -- Add Texture-button: ( -- Add lookup-colour outline: local ClrPanel = DotNetObject "FlowLayoutPanel" TexHilitePanel.Controls.Add ClrPanel ClrPanel.AutoSize = True ClrPanel.Margin = (dotNetObject "Padding" 0 0 2 0) ClrPanel.Padding = (dotNetObject "Padding" 2 2 2 2) -- Set button-background to lookup-colour: ClrPanel.BackColor = dnColour.FromArgb LookupClr.r LookupClr.g LookupClr.b local TexBtn = DotNetObject "Button" TexBtn.FlatStyle = TexBtn.FlatStyle.Flat TexBtn.Margin = (dotNetObject "Padding" 0) TexBtn.Padding = (dotNetObject "Padding" 3 3 3 3) --TexBtn.ForeColor = TexBtn.ForeColor.Yellow -- Tag button with material-info and texmap-index: TexBtn.Tag = TexHilitePanel.Tag -- Add rightclick-menu: if (TexMapRCmenu != undefined) do ( TexBtn.ContextMenuStrip = TexMapRCmenu ) TexBtn.Width = BtnWidth TexBtn.Height = TexBtn.Width TexBtn.BackColor = ClrPanel.BackColor ClrPanel.Controls.Add TexBtn dotNet.addEventHandler TexBtn "MouseUp" TexmapBtnFunc -- Click-event dotNet.addEventHandler TexBtn "Paint" TexmapBtnPaintFunc -- Paint-event triggers thumbnail-loader thread dotNet.setLifetimeControl TexBtn #DotNet local TooltipText = "Click to apply lookup-colour" if (TexPath != undefined) and (TexPath != "") do ( TooltipText += (":\n" + TexPath) ) -- Show tooltip on texture-button: dnTooltip.SetToolTip TexBtn TooltipText ) -- Collect texmap's highlighter-panel: TexHilitePanel ) -- Add select/apply buttons for a given MatInfo: ( AddMatCmdPanel MatPanel ThisMatInfo ) -- Collect texmaps-panel: MatPanel ) -- Set up materials-list panel for scrolling, now its contents have been added: ( MatsOuterPanel.AutoScroll = True MatsOuterPanel.MaximumSize = (DotNetObject "System.Drawing.Size" 0 ScrollPanelHeight) ) -- Add mask-controls panel, and its toggle-button:: ( -- Set up toggle-button: local ToggleBtn = (dotNetObject "Button") ToggleBtn.Text = "Masking Options" ToggleBtn.AutoSize = True ToggleBtn.Location.X += 10 -- Tag button with this struct: ToggleBtn.Tag = (dotNetMxsValue This) -- Sets button's flatstyle: SetBtnChecked ToggleBtn MaskDropdownActive -- Create empty subgroup-box, with button instead of label: local MaskBoxPanel = AddGrpBox TexmapPanel MaskBoxPanel.Controls.Add ToggleBtn dnTooltip.SetToolTip ToggleBtn "Show/hide extra options for lookup/tint masking" dotNet.addEventHandler ToggleBtn "MouseUp" ev_ChkMaskOptions_Clicked dotNet.setLifetimeControl ToggleBtn #dotnet MaskCtrlPanel = dotNetObject "TableLayoutPanel" MaskCtrlPanel.AutoSize = True MaskCtrlPanel.Dock = MaskCtrlPanel.Dock.Fill MaskCtrlPanel.Padding = (dotNetObject "Padding" 3 16 3 3) MaskBoxPanel.Controls.Add MaskCtrlPanel UpdateMaskingControls() ) -- Generate text for help-label: ( HelperText = StringStream "" format "Click textures to set Lookup" To:HelperText if HasMaskChan do ( format "/Mask" To:HelperText ) format "-channel colour%.\n" (if HasMaskChan then "s" else "") To:HelperText format "Rightclick textures for more commands." To:HelperText HelperText = (HelperText as String) ) ) -- Add extra-info label: ( local SelObjs = GetCurrentSelection() -- Choose appropriate label-text: local Msg = case of ( (HelperText != undefined):(HelperText) (SelObjs.Count == 0):("NO OBJECT SELECTED") (SelObjs.Count != 1):("Please select only one object") Default:"Object doesn't use any valid terrain-shaders." ) local LblExtraInfo = dotNetObject "Label" TexmapPanel.Controls.Add LblExtraInfo LblExtraInfo.Margin = (dotNetObject "Padding" 6 6 6 6) LblExtraInfo.AutoSize = True LblExtraInfo.Text = Msg ) -- Turn dialog's redraw back on: ScrollPanel.ResumeLayout() ResumeRedraw() ScrollPanel.Refresh() PopPrompt() SetArrowCursor() --format "Took % seconds to redraw (%)\n" ((TimeStamp() - TimeStart) / 1000.0) (Val as string) return OK ), ------------------------------------------------------------------------------ -- SelListMat: -- Select combobox-item for material with matching matid : ------------------------------------------------------------------------------ fn SelListMat MatId = ( -- Skip if no matid was passed in: if (MatId == 0) do return False local SelNum = 0 for n = 1 to TerrainMatIds.Count while (SelNum == 0) do ( if (findItem TerrainMatIds[n] MatId != 0) do ( SelNum = n ) ) -- Skip if no terrain-material had this matid: if (SelNum == 0) do return False -- Set combobox to show that list-item, and update texmap buttons: CboShaderType.SelectedIndex = (SelNum - 1) UpdateTexmapCtrls() ), ------------------------------------------------------------------------------ -- PopulateMaterialList: -- Update terrain-materials list: ------------------------------------------------------------------------------ fn PopulateMaterialList = ( SetWaitCursor() -- Get the material for the previously-selected item: -- (we're going to re-select this, if it's still in the list later) local SelMatInfo = GetSelMatInfo() local SelTexNum = GetSelTexNum() local PrevSelMat = undefined if (SelMatInfo != undefined) do ( PrevSelMat = SelMatInfo.TerrainMat ) SelMatInfo = undefined local NewSelInfo = undefined -- Get data on terrain-object's compatible terrain-materials: local AllTerrainMatInfoList = gRsTerrainHelpers.GetObjTerrainFaceData TerrainObj -- Collect materials by terrainshader-type: (i.e. the name-pattern used to distinguish between them) TerrainMatInfoList = #() local ShaderTypes = #() for MatInfo in AllTerrainMatInfoList do ( local ShaderType = MatInfo.TypeDef.Pattern local TypeNum = FindItem ShaderTypes ShaderType if (TypeNum == 0) do ( append ShaderTypes ShaderType append TerrainMatInfoList (dataPair ShaderType:ShaderType MatInfoList:#()) TypeNum = ShaderTypes.Count ) append TerrainMatInfoList[TypeNum].MatInfoList MatInfo ) -- Sort shadertype-items by name: fn TypeSorter v1 v2 = (striCmp v1.ShaderType v2.ShaderType) qsort TerrainMatInfoList TypeSorter -- Collect MatId lookup-lists: TerrainMatIds = for Item in TerrainMatInfoList collect ( for MatInfo in Item.MatInfoList collect MatInfo.MatId ) -- Set combobox-items to Material Ids/Names: CboShaderType.Items.Clear() if (TerrainMatInfoList.Count != 0) do ( -- Set first selection-index by default: local SelIdx = 0 for n = 1 to TerrainMatInfoList.Count do ( local ShaderTypeItem = TerrainMatInfoList[n] local ItemText = StringStream "" format "% (% materials)" ShaderTypeItem.ShaderType ShaderTypeItem.MatInfoList.Count To:ItemText local ItemIdx = CboShaderType.Items.Add (ItemText as String) CboShaderType.Items.Item[ItemIdx] -- We'll want to select this item if it includes the previously-selected texmap's material: for MatItem in ShaderTypeItem.MatInfoList do ( if (MatItem.TerrainMat == PrevSelMat) do ( NewSelInfo = MatItem SelIdx = (n - 1) ) ) ) -- Set combobox-selection: CboShaderType.SelectedIndex = SelIdx -- Set selection-info on main panel's tag to equivalent of what it was before: if (NewSelInfo != undefined) do ( NewSelInfo = DotNetMxsValue (DataPair MatInfo:NewSelInfo TexNum:SelTexNum) ) TexmapPanel.Tag = NewSelInfo ) SetArrowCursor() return OK ), ------------------------------------------------------------------------------ -- UpdateCtrls: -- Update controls to match current selection: ------------------------------------------------------------------------------ fn UpdateCtrls = ( local SelObjs = GetCurrentSelection() -- Set 'TerrainObj' value if selection is valid: TerrainObj = if (SelObjs.Count == 1) and (isProperty SelObjs[1] #mesh) then SelObjs[1] else undefined local HasObj = (TerrainObj != undefined) -- Set enabledness of controls in panels that need a valid object to work: for ThisPanel in ObjEditPanels do ( local CtrlList = ThisPanel.Controls for n = 0 to (CtrlList.Count - 1) do ( CtrlList.Item[n].Enabled = HasObj ) ) SetShowChanClicked() PopulateMaterialList() -- Enable material-picker if object uses multiple terrain-shaders: ChkPickMat.Enabled = (HasObj and (TerrainMatInfoList.Count > 1)) -- Update texmap-buttons to match selected terrain-material: UpdateTexmapCtrls() return true ), ------------------------------------------------------------------------------ -- TexClick: -- Called when a texmap-button is left-clicked: ------------------------------------------------------------------------------ fn TexClick = ( if (isBtnChecked ChkVertPaint) then ( SetVertPaintColour() ) else ( PaintSelection() ) ), ------------------------------------------------------------------------------ -- UI EVENTS: ------------------------------------------------------------------------------ -- 'Vertex Colour Toolkit' button clicked: fn ev_BtnVertColTools_Clicked Sender Args = ( filein (::RsConfigGetWildwestDir() + "Script/3dsMax/Maps/Materials/vertexColour_ui.ms") ), -- 'Terrain Shader Editor' button clicked: fn ev_BtnTerrShadEdit_Clicked Sender Args = ( filein (::RsConfigGetWildwestDir() + "Script/3dsMax/Maps/Materials/terrain_ShaderEditor.ms") ), -- 'MapId Projector' button clicked: fn ev_BtnMapIdProj_Clicked Sender Args = ( fileIn (::RsConfigGetWildWestDir() + "script/3dsMax/Maps/Materials/RSTA_mapIDProjector.ms") ), -- 'Init Terrain Chans' clicked: fn ev_BtnInitChannels_Clicked Sender Args = ( undo "Init Terrain Channels" on ( for Obj in (GetCurrentSelection()) where (isEditPolyMesh Obj) do ( -- Collapse and convert: ConvertToPoly Obj -- Make sure channels are turned on - if not, set to black: for Chan in #(5, 9) where not (PolyOp.GetMapSupport Obj Chan) do ( PolyOp.SetFaceColor Obj Chan #all black ) ) ) ), -- 'Remove Non-Terrain Chans' clicked: fn ev_BtnDelChannels_Clicked Sender Args = ( local TerrainChans = #(-2, 0, 1, 2, 5, 9, 13) local HasNonTerrainMats = False local SelObjs = for Obj in (GetCurrentSelection()) where (isEditPolyMesh Obj) collect Obj if (SelObjs.Count == 0) do return False -- Check for non-terrain materials on object-faces: for Obj in SelObjs while (not HasNonTerrainMats) do ( -- Get descriptors for materials used on object: local ObjMatInfos = gRsMaterials.GetObjMatData Obj FlatList:True -- Do any of these materials use non-terrain shaders? for MatInfo in ObjMatInfos while (not HasNonTerrainMats) do ( HasNonTerrainMats = not (MatchPattern MatInfo.ShaderName Pattern:"*terrain*") ) ) if HasNonTerrainMats do ( local Msg = "Selection uses non-terrain materials.\nAre you sure you want to remove non-terrain channels?\n\nThese channels will NOT be removed:\n" append Msg (TerrainChans as string) if not (QueryBox Msg Title:"WARNING: Non-terrain materials found") do ( return False ) ) SetWaitCursor() local Msg = ("Removing channels from selection, keeping: " + (TerrainChans as string)) format "%\n" Msg PushPrompt Msg undo "Delete Channels" on ( gRsTerrainPalette.RemoveCallbacks() ClearSelection() RsRemoveClrChans (SelObjs) KeepChans:TerrainChans Select SelObjs gRsTerrainPalette.AddCallbacks() ) PopPrompt() DisplayTempPrompt Msg 1000 -- Leave message up a little longer... SetArrowCursor() ), -- 'Deselect % Verts' button clicked: fn ev_BtnDeselVerts_Clicked Sender Args = ( if (RsGetSubObjLevelName() != #vertex) do return False local SelObjs = GetCurrentSelection() local Obj = SelObjs[1] if (SelObjs.Count != 1) or (not isEditPolyMesh Obj) do return False local TheTool = Sender.Tag.Value local DeselPercent = TheTool.SpnDeselPercent.Value gRsSelectionTools.RandomlyDeselectVerts Obj DeselPercent ), fn ev_BtnFixSeamVerts_Clicked Sender Args = ( Filein (RsConfigGetWildwestDir() + "Script/3dsMax/Maps/Materials/RSTA_TerrainSeamFixer_UI.ms") ), -- Function used by various face-border-selection commands: fn SelBorderVerts BordersFunc UndoText:"Select Border Verts" = ( local SelObjs = GetCurrentSelection() local Obj = SelObjs[1] if (SelObjs.Count != 1) or (not isEditPolyMesh Obj) do return False local DoConvert = False if (not IsKindOf Obj Editable_Poly) do ( if (QueryBox "Selected object needs to be an Editable Poly.\n\nConvert/Collapse object to Poly?" Title:"Warning: Non-Editable Poly Selected") then ( DoConvert = True ) else ( return False ) ) undo UndoText on ( if DoConvert do ( ConvertToPoly Obj ) -- Store current face/edge selection: local FaceSel = GetFaceSelection Obj local EdgeSel = GetEdgeSelection Obj local BorderEdges = (BordersFunc Obj) -- Set vert-selection to border-verts: SetCommandPanelTaskMode #Modify SetSelectionLevel Obj #Vertex PolyOp.SetEdgeSelection Obj BorderEdges Obj.EditablePoly.ConvertSelection #Edge #Vertex -- Revert face/edge-selection: SetFaceSelection Obj FaceSel SetEdgeSelection Obj EdgeSel ) return True ), -- 'Select MatId Borders' button clicked: fn ev_BtnSelMatBorders_Clicked Sender Args = ( local selObjs = GetCurrentSelection() if (selObjs.count != 1) do return False local obj = selObjs[1] local ObjOp = (RsMeshPolyOp obj) if (ObjOp == Undefined) do return False -- Collect object's faces per matId: local faceCount = obj.numFaces local ObjGetMatID = RsGetFaceMatIDFunc obj local facesPerMatId = #() for faceNum = 1 to faceCount do ( local faceMatId = (ObjGetMatID obj faceNum) local matIdFaces = facesPerMatId[faceMatId] if (matIdFaces == Undefined) do ( matIdFaces = #{} matIdFaces.count = faceCount facesPerMatId[faceMatId] = matIdFaces ) matIdFaces[faceNum] = True ) -- Collect non-open border-edges for each matId: local borderVerts = #{} for faces in facesPerMatId where (faces != Undefined) do ( borderVerts += (RsGeom_GetFaceBorderVerts obj faces onlyBetween:True) ) -- Select verts: undo "Select MatId Border Verts" on ( local ObjSetVertSelection = if (ObjOp == MeshOp) then SetVertSelection else PolyOp.SetVertSelection ObjSetVertSelection obj borderVerts SubobjectLevel = 1 ) ), -- 'Select Borders Between Selected MatIds' button clicked: fn ev_BtnSelBetweenSelMats_Clicked Sender Args = ( local selObjs = GetCurrentSelection() if (selObjs.count != 1) do return False local obj = selObjs[1] local ObjOp = (RsMeshPolyOp obj) if (ObjOp == Undefined) do return False -- Get MatIds used on object's selected faces: local matIds = #{} local ObjGetMatID = (RsGetFaceMatIDFunc obj) for faceNum in (GetFaceSelection obj) do ( local matId = (ObjGetMatID obj faceNum) matIds[matId] = True ) -- Abort if insufficient matIds are selected: matIds = (matIds as Array) if (matIds.count < 2) do ( local msg = "Please select faces with more than one Material ID.\n\nThis tool finds verts on border between selected mat ids." Messagebox msg title:"Warning: Needs multiple MatIds selected" return False ) -- Collect object's faces per matId: local faceCount = obj.numFaces local facesPerMatId = #() for matId in matIds do ( local faces = #{} faces.count = faceCount facesPerMatId[matId] = faces ) for faceNum = 1 to faceCount do ( local faceMatId = (ObjGetMatID obj faceNum) local faces = facesPerMatId[faceMatId] if (faces != Undefined) do ( faces[faceNum] = True ) ) -- Collect verts shared by adjacent matIds: local borderVerts = #{} for idxA = 1 to (matIds.count - 1) do ( local matIdA = matIds[idxA] local matFacesA = facesPerMatId[matIdA] local matVertsA = ObjOp.GetVertsUsingFace obj matFacesA for idxB = (idxA + 1) to matIds.count do ( local matIdB = matIds[idxB] local matFacesB = facesPerMatId[matIdB] local matVertsB = ObjOp.GetVertsUsingFace obj matFacesB -- Collect verts shared by these two sets of faces: borderVerts += (matVertsA * matVertsB) ) ) -- Select verts: undo "Select Verts Between Selected MatIds" on ( local ObjSetVertSelection = if (ObjOp == MeshOp) then SetVertSelection else PolyOp.SetVertSelection ObjSetVertSelection obj borderVerts SubobjectLevel = 1 ) return OK ), -- 'Select DPM Borders' button clicked: fn ev_BtnSelDpmBorders_Clicked Sender Args = ( local TheTool = Sender.Tag.Value -- This function is passed to 'SelBorderVerts' fn GetDpmBorderEdges Obj = ( -- Get faces using DPM shaders: local Faces = gRsMaterials.GetObjFacesUsingShaders Obj #("*dpm*", "*uber*snow*") -- Select faces using this MatId: Obj.EditablePoly.SetSelection #Face Faces -- Convert matid-selection to border-edge selection: Obj.EditablePoly.ConvertSelectionToBorder #Face #Edge -- Collect selected vert-numbers: local BorderEdges = (GetEdgeSelection Obj) return BorderEdges ) TheTool.SelBorderVerts GetDpmBorderEdges UndoText:"Select DPM-Shader Border Verts" ), -- 'Select PXM Borders' button clicked: fn ev_BtnSelPxmBorders_Clicked Sender Args = ( local TheTool = Sender.Tag.Value -- This function is passed to 'SelBorderVerts' fn GetPxmBorderEdges Obj = ( -- Get faces using PXM shaders: local Faces = gRsMaterials.GetObjFacesUsingShaders Obj #("*pxm*") -- Select faces using this MatId: Obj.EditablePoly.SetSelection #Face Faces -- Convert matid-selection to border-edge selection: Obj.EditablePoly.ConvertSelectionToBorder #Face #Edge -- Collect selected vert-numbers: local BorderEdges = (GetEdgeSelection Obj) return BorderEdges ) TheTool.SelBorderVerts GetPxmBorderEdges UndoText:"Select PXM-Shader Border Verts" ), -- Combobox selection changed: fn ev_CboShaderType_Selected Sender Args = ( local TheTool = Sender.Tag.Value -- Change the button images to match the textures TheTool.UpdateTexmapCtrls() ), -- 'Refresh' button clicked: fn ev_Refresh_Clicked Sender Args = ( local TheTool = ::gRsTerrainPalette TheTool.UpdateCtrls() ), -- 'Pick' button clicked: fn ev_PickMat_Clicked Sender Args = ( local TheTool = ::gRsTerrainPalette -- Show button as checked: TheTool.SetBtnChecked Sender True -- Picker-tool: Tool RsMatIdPickerTool ( local obj, rayMesh on start do ( -- Update material/face-lists: local SelMatInfo = gRsTerrainPalette.GetSelMatInfo UpdateData:True Obj = gRsTerrainPalette.TerrainObj if (isProperty Obj #mesh) then ( replacePrompt "Pick Face" -- Set up RayMesh for object: rayMesh = RayMeshGridIntersect() rayMesh.Initialize 10 rayMesh.addNode Obj rayMesh.BuildGrid() ) else ( #stop ) ) on mousePoint clickNum do ( -- Cast ray at object: local mouseRay = (mapScreenToWorldRay mouse.pos) local hits = rayMesh.intersectRay mouseRay.pos mouseRay.dir False local hitIdx = rayMesh.getClosestHit() -- Get index of closest ray-hit: local hitFace = rayMesh.getHitFace hitIdx local matId = 0 if (hitFace != 0) do ( matId = getFaceMatID obj.mesh hitFace ) -- Tell tool to select the material with this matId: gRsTerrainPalette.SelListMat matId ) ) -- Start picker-tool: StartTool RsMatIdPickerTool -- Uncheck button once tool stops: TheTool.SetBtnChecked Sender False ), -- Texmap-button clicked: fn ev_TexmapBtn_Clicked Sender Args = ( -- Set current material/texmap: gRsTerrainPalette.SetSelLookupTex Sender.Tag -- Was this a rightclick? local RightClick = Args.Button.Equals Args.Button.Right -- Apply texmap to mesh if left-clicking: if (not rightClick) do ( gRsTerrainPalette.TexClick() ) ), -- Texmap-button paint-event: fn ev_TexmapBtn_Paint Sender Args = ( -- Request thumbnail for button if this hasn't been set yet: if (Sender.ImageIndex == -1) do ( local ButtonTag = Sender.Tag.Value local TexPath = ButtonTag.MatInfo.GetTexPath ButtonTag.TexNum -- Queue control for thumbnail-update: ::gRsTerrainPalette.Thumbs.SetThumb Sender TexPath ) ), -- Set-mask button clicked: -- (this function is added to controls as value 'SetMaskBtnFunc') fn ev_SetMaskBtn_Clicked Sender Args = ( -- Highlight button while it's working... gRsTerrainPalette.SetBtnChecked Sender True -- Button is tagged with bitarray denoting rgb-subchannel(s) to edit: local SubChans = Sender.Tag.Value -- Get table-column this control comes from: local ColIdx for n = 0 to 2 while (ColIdx == undefined) do ( if (Sender == (Sender.Parent.GetControlFromPosition n 0)) do ( ColIdx = n ) ) local MaskColour = case ColIdx of ( 0:(Black) 1:(gRsTerrainPalette.GetMaskColour()) 2:(White) ) -- Paint mask-colour to requested subchannels: gRsTerrainPalette.PaintSelection MaskSubChans:SubChans MaskColour:MaskColour -- Unhighlight button: gRsTerrainPalette.SetBtnChecked Sender False ), -- 'Show Channel' buttons clicked: fn ev_ShowChan_Clicked Sender Args = ( ::gRsTerrainPalette.ShowChannel (Sender.Tag) ), fn ev_ChkVertPaint_Clicked Sender Args = ( local TheTool = Sender.Tag.Value -- Set new checked-state: local NewState = (not TheTool.isBtnChecked Sender) TheTool.SetChkVertPaintState NewState -- If vertex painting has been switched on: if NewState do ( if (TheTool.TerrainMatInfoList.Count == 0) then ( messagebox "Object is not setup with a compatible terrain-shader." Title:"Invalid materials" ) else ( -- Add/retrieve VertexPaint modifier: TheTool.SetupVertPaintMod() ) -- Set fill-strength from spinner: if (TheTool.vPainttool != undefined) do ( TheTool.vPaintTool.BrushOpacity = TheTool.SpnFillStrength.Value ) ) ), -- Change VertexPaint brush-opacity if 'Fill Strength' spinner is changed: fn ev_SpnFillStrength_Changed Sender Args = ( local TheTool = Sender.Tag.Value if (TheTool.vPainttool != undefined) do ( TheTool.vPainttool.BrushOpacity = Sender.Value ) ), ----------------------------------------------------------------------------- fn selectBorderVerts chan = ( local obj = selection[1] if (obj != undefined) and ((isKindOf obj EditablePolyMesh) or (isKindOf obj Editable_mesh)) then ( local borderVerts = RsGeometry_GetUVBorders obj chan if (borderVerts.count > 0) then ( if(classof obj == Editable_mesh) then ( setvertselection obj borderVerts ) else ( polyop.setvertselection obj borderVerts ) subobjectlevel = 1 ) ) ), ----------------------------------------------------------------------------- fn ev_BtnSelUV1Verts_Clicked = ( ::gRsTerrainPalette.selectBorderVerts 1 ), ----------------------------------------------------------------------------- fn ev_BtnSelUV2Verts_Clicked = ( ::gRsTerrainPalette.selectBorderVerts 2 ), -- Form closed: fn ToolClose = ( -- Remove selection-change callback: RemoveCallbacks() -- Deregister tool from thumbnails-system if (gRSTA_ThumbsMgr != undefined) do ( gRSTA_ThumbsMgr.DeregisterTool ToolName ) -- Dispose of struct's dotnetobjects: for PropName in (getPropNames This) do ( local ThisVal = (getProperty This PropName) if (isKindOf ThisVal DotNetObject) and (isProperty ThisVal #Dispose) do ( ThisVal.Dispose() ) ) gRsTerrainPalette = undefined ), fn ev_ToolForm_Close Sender Args = ( Sender.Tag.Value.ToolClose() ), ------------------------------------------------------------------------------ -- InitForm: -- Initial form-setup steps ------------------------------------------------------------------------------ fn InitForm = ( PushPrompt "Initialising Terrain Painter..." -- Set local aliases for event-functions: TexmapBtnFunc = ev_TexmapBtn_Clicked TexmapBtnPaintFunc = ev_TexmapBtn_Paint SetMaskBtnFunc = ev_SetMaskBtn_Clicked -- Get dotnet colours for current Max colourscheme: FormClr = (ColorMan.GetColor #background) * 255 FormClr = (dnColour.FromArgb FormClr[1] FormClr[2] FormClr[3]) WindowClr = (ColorMan.GetColor #window) * 255 WindowClr = (dnColour.FromArgb WindowClr[1] WindowClr[2] WindowClr[3]) TextClr = (ColorMan.GetColor #windowText) * 255; TextClr = (dnColour.FromArgb TextClr[1] TextClr[2] TextClr[3]) ToolForm.Tag = (DotNetMxsValue This) ToolForm.Text = "Terrain Painter" ToolForm.Size = DotNetObject "System.Drawing.Size" FormSize.X FormSize.Y ToolForm.BackColor = FormClr ToolForm.MinimumSize = (DotNetObject "System.Drawing.Size" FormSize.X 0) -- Show the tool-form, with redraw deactivated: ( ToolForm.ShowModeless() ToolForm.Refresh() SuspendRedraw() ) DotNet.addEventHandler ToolForm "Closing" ev_ToolForm_Close -- Struct for generating lists of controls: struct CtrlDef (Name, Text, Tooltip, TagVal, Func, Width, FlowBreak = False) -- Main layout-table: ( local MainTable = dotNetObject "TableLayoutPanel" ToolForm.Controls.Add MainTable MainTable.Dock = MainTable.Dock.Fill MainTable.CellBorderStyle = MainTable.CellBorderStyle.None MainTable.BackColor = FormClr -- Add banner: ( -- Set fixed row-height: MainTable.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) RsBannerPanel.Margin = (dotNetObject "Padding" 0) RsBannerPanel.Dock = RsBannerPanel.Dock.Fill MainTable.Controls.Add RsBannerPanel ) -- Add scroll-panel: ScrollPanel = DotNetObject "TableLayoutPanel" MainTable.Controls.Add ScrollPanel ScrollPanel.Dock = ScrollPanel.Dock.Fill ScrollPanel.CellBorderStyle = ScrollPanel.CellBorderStyle.None ScrollPanel.Padding = (dotNetObject "Padding" 0 0 (dotNetClass "SystemInformation").VerticalScrollBarWidth 0) ScrollPanel.AutoScroll = True -- Add shadertype-selection panel: ( local GrpBox = AddGrpBox ScrollPanel Title:"Select Terrain Shader Type:" ScrollPanel.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) local ThisPanel = dotNetObject "FlowLayoutPanel" ThisPanel.Padding = (dotNetObject "Padding" 6 6 6 6) ThisPanel.Dock = ThisPanel.Dock.Fill ThisPanel.AutoSize = True GrpBox.Controls.Add ThisPanel -- Collect object-reliant panel: -- (controls here are disabled if terrain-object is not selected) append ObjEditPanels ThisPanel -- Add Combobox: ( CboShaderType = dotNetObject "ComboBox" CboShaderType.Margin = (dotNetObject "Padding" 1 0 4 4) CboShaderType.Width = 234 CboShaderType.DropDownWidth = 600 CboShaderType.DropDownStyle = CboShaderType.DropDownStyle.DropDownList CboShaderType.FlatStyle = CboShaderType.FlatStyle.Flat CboShaderType.BackColor = WindowClr CboShaderType.ForeColor = TextClr CboShaderType.Tag = (dotNetMxsValue This) dotNet.addEventHandler CboShaderType "SelectionChangeCommitted" ev_CboShaderType_Selected ThisPanel.Controls.Add CboShaderType ) -- Add material-buttons: for Item in #( CtrlDef Name:#ChkPickMat Text:"Pick" Tooltip:"Pick material from terrain-object" Func:ev_PickMat_Clicked Width:60, CtrlDef Name:#BtnRefresh Text:"Refresh" Tooltip:"Refresh the terrain-material list" Func:ev_Refresh_Clicked Width:60 ) do ( local NewBtn = DotNetObject "Button" NewBtn.Text = Item.Text NewBtn.FlatStyle = NewBtn.FlatStyle.System if (Item.Width == undefined) then ( NewBtn.AutoSize = True ) else ( NewBtn.Width = Item.Width ) NewBtn.Height = 22 NewBtn.Margin = (dotNetObject "Padding" 0 0 3 0) -- Tag button with this struct: NewBtn.Tag = Item.TagVal -- Store reference to pick-button: if (Item.Name == #ChkPickMat) then ( This.ChkPickMat = NewBtn ) else ( -- Non-stored buttons should have DotNet lifetime-control: dotNet.setLifetimeControl NewBtn #dotnet ) ThisPanel.Controls.Add NewBtn dnTooltip.SetToolTip NewBtn Item.Tooltip dotNet.addEventHandler NewBtn "MouseUp" Item.Func ) ) -- Add texmap-buttons panel: (filled out by 'fn UpdateTexmapCtrls') ( TexmapPanel = AddGrpBox ScrollPanel Title:"Texturemaps:" PanelType:"TableLayoutPanel" ScrollPanel.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) ) -- Add 'Show Channel' controls: ( local GrpBox = AddGrpBox ScrollPanel Title:"Show Channel:" ScrollPanel.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) local ThisPanel = dotNetObject "FlowLayoutPanel" ShowChansPanel = ThisPanel ThisPanel.Dock = ThisPanel.Dock.Fill ThisPanel.Padding = (dotNetObject "Padding" 6 6 6 6) ThisPanel.AutoSize = True GrpBox.Controls.Add ThisPanel -- Add show-channel buttons: for Item in #( CtrlDef Text:"Off" Tooltip:"Don't show vertex-colours" TagVal:-3, CtrlDef Text:"Alpha [-2]" Tooltip:"Show Vertex Alpha channel" TagVal:-2, CtrlDef Text:"VColour [0]" Tooltip:"Show default Vertex Colours channel" TagVal:0, CtrlDef Text:"Lookup [9]" Tooltip:"Show Terrain Paint lookup-colour channel" TagVal:9, CtrlDef Text:"Uber Mask [5]" Tooltip:"Show Uber Terrain Shader lookup/tint mask-channel" TagVal:5 ) do ( local NewBtn = DotNetObject "Button" NewBtn.Text = Item.Text NewBtn.Tag = Item.TagVal NewBtn.FlatStyle = NewBtn.FlatStyle.System NewBtn.AutoSize = True NewBtn.Width = 0 NewBtn.Height = 26 NewBtn.Margin = (dotNetObject "Padding" 0 0 3 0) ThisPanel.Controls.Add NewBtn dnTooltip.SetToolTip NewBtn Item.Tooltip dotNet.addEventHandler NewBtn "MouseUp" ev_ShowChan_Clicked dotNet.setLifetimeControl NewBtn #dotnet ) ) -- Add sub-table row: ( local SubTable = DotNetObject "TableLayoutPanel" SubTable.Dock = SubTable.Dock.Fill SubTable.Margin = (dotNetObject "Padding" 0) SubTable.ColumnCount = 2 SubTable.AutoSize = True ScrollPanel.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) ScrollPanel.Controls.Add SubTable SubTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Percent 50) SubTable.ColumnStyles.Add (dotNetObject "ColumnStyle" SizeType_Percent 50) SubTable.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) -- Add VertexPaint panel: ( local ThisPanel = AddGrpBox SubTable Title:"VertexPaint:" PanelType:"FlowLayoutPanel" -- Collect object-reliant panel: append ObjEditPanels ThisPanel -- Add 'Start Vertex Paint' checkbutton: ( local NewBtn = DotNetObject "Button" ChkVertPaint = NewBtn NewBtn.Text = "Start Vertex Paint" NewBtn.FlatStyle = NewBtn.FlatStyle.System NewBtn.AutoSize = True NewBtn.Width = 0 NewBtn.Height = 26 NewBtn.Margin = (dotNetObject "Padding" 6 3 0 6) -- Tag button with this struct: NewBtn.Tag = (dotNetMxsValue This) ThisPanel.Controls.Add NewBtn dnTooltip.SetToolTip NewBtn "Toggle vertex painting" dotNet.addEventHandler NewBtn "MouseUp" ev_ChkVertPaint_Clicked ) -- Add VertexPaint/mask fill-strength spinner: ( local LblFillStr = dotNetObject "Label" LblFillStr.Text = "Fill Strength:" LblFillStr.Margin = (dotNetObject "Padding" 6 8 0 6) LblFillStr.AutoSize = True SpnFillStrength = dotNetObject "NumericUpDown" SpnFillStrength.Tag = (dotNetMxsValue This) SpnFillStrength.Margin = (dotNetObject "Padding" 2 6 0 6) SpnFillStrength.Width = 50 SpnFillStrength.BackColor = WindowClr SpnFillStrength.ForeColor = TextClr SpnFillStrength.DecimalPlaces = 0 SpnFillStrength.Increment = 1 SpnFillStrength.Minimum = 1 SpnFillStrength.Maximum= 100 SpnFillStrength.Value = 100 SpnFillStrength.ReadOnly = False ThisPanel.Controls.Add LblFillStr ThisPanel.Controls.Add SpnFillStrength dotNet.addEventHandler SpnFillStrength "ValueChanged" ev_SpnFillStrength_Changed ) ) ) -- Add mesh-selection panel: ( local GrpBox = AddGrpBox ScrollPanel Title:"Vertex Selection:" SubTable.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) MeshSelectPanel = dotNetObject "FlowLayoutPanel" MeshSelectPanel.Dock = MeshSelectPanel.Dock.Fill MeshSelectPanel.Padding = (dotNetObject "Padding" 6 6 6 6) MeshSelectPanel.AutoSize = True GrpBox.Controls.Add MeshSelectPanel -- Add channel-shower buttons: for Item in #( CtrlDef Name:#BtnSelMatBorders Text:"MatId Borders (All)" Tooltip:"Select verts joining faces with different MatIds" Func:ev_BtnSelMatBorders_Clicked, CtrlDef Name:#BtnSelBetweenSelMats Text:"MatId Borders (Selected)" Tooltip:"Select verts joining faces using MatIds used on selected faces" Func:ev_BtnSelBetweenSelMats_Clicked, CtrlDef Name:#BtnSelDpmBorders Text:"DPM/Snow Borders" Tooltip:"Select verts joining non/tesselation-shader faces" Func:ev_BtnSelDpmBorders_Clicked, CtrlDef Name:#BtnSelPxmBorders Text:"PXM Borders" Tooltip:"Select verts joining non-PXM/PXM-shader faces" Func:ev_BtnSelPxmBorders_Clicked FlowBreak:True, CtrlDef Name:#BtnDeselVerts Text:"Deselect % Verts:" Tooltip:"Deselect a portion of current vertex-selection" Func:ev_BtnDeselVerts_Clicked--, --CtrlDef Name:#BtnSelUV1Verts Text:"Select UV 1 Border" Tooltip:"Select verts on the UV1 border" Func:ev_BtnSelUV1Verts_Clicked, --CtrlDef Name:#BtnSelUV2Verts Text:"Select UV 2 Border" Tooltip:"Select verts on the UV2 border" Func:ev_BtnSelUV2Verts_Clicked ) do ( local NewBtn = DotNetObject "Button" NewBtn.Text = Item.Text NewBtn.FlatStyle = NewBtn.FlatStyle.System --NewBtn.Width = 60 NewBtn.Height = 23 NewBtn.AutoSize = True NewBtn.Margin = (dotNetObject "Padding" 0 0 4 0) -- Tag button with this struct: NewBtn.Tag = (dotNetMxsValue This) dotNet.addEventHandler NewBtn "MouseUp" Item.Func MeshSelectPanel.Controls.Add NewBtn dnTooltip.SetToolTip NewBtn Item.Tooltip -- Set layout-flow break, or not: ThisPanel.SetFlowBreak NewBtn Item.FlowBreak dotNet.setLifetimeControl NewBtn #dotnet -- Add percentage-spinner after 'Deselect Verts' button: if (Item.Name == #BtnDeselVerts) do ( SpnDeselPercent = dotNetObject "NumericUpDown" SpnDeselPercent.Margin = (dotNetObject "Padding" 1 1 0 0) SpnDeselPercent.Width = 50 SpnDeselPercent.BackColor = WindowClr SpnDeselPercent.ForeColor = TextClr SpnDeselPercent.DecimalPlaces = 0 SpnDeselPercent.Increment = 1 SpnDeselPercent.Minimum = 1 SpnDeselPercent.Maximum= 100 SpnDeselPercent.Value = 50 SpnDeselPercent.ReadOnly = False MeshSelectPanel.Controls.Add SpnDeselPercent ) ) ) -- Add Tools panel: ( local GrpBox = AddGrpBox ScrollPanel Title:"More Tools:" ScrollPanel.RowStyles.Add (dotNetObject "RowStyle" SizeType_AutoSize) local ThisPanel = dotNetObject "FlowLayoutPanel" ThisPanel.Dock = ThisPanel.Dock.Fill ThisPanel.Padding = (dotNetObject "Padding" 6 6 6 6) ThisPanel.AutoSize = True GrpBox.Controls.Add ThisPanel -- Give last box a bottom-margin GrpBox.Parent.Parent.Margin = (dotNetObject "Padding" 4 4 4 4) -- Add extra tool-buttons: for Item in #( CtrlDef Name:#BtnVertColTools Text:"Vertex Colour Toolkit" Func:ev_BtnVertColTools_Clicked \ Tooltip:"Open Vertex Colour Toolkit", CtrlDef Name:#BtnTerrShadEdit Text:"Terrain Shader Editor" Func:ev_BtnTerrShadEdit_Clicked \ Tooltip:"Terrain Shader Editor\nSimplifies editing of shaders' per-texmap values.", CtrlDef Name:#BtnMapIdProj Text:"MapId Projector" Func:ev_BtnMapIdProj_Clicked FlowBreak:True \ Tooltip:"Open 'MapID Projector/Render' toolkit", CtrlDef Name:#BtnInitChannels Text:"Init Terrain Chans" Func:ev_BtnInitChannels_Clicked \ Tooltip:"Activate terrain-related vertex-colour channels for selected geometry,\nif they're not already active.\n\n(Channels 5, 9)" , CtrlDef Name:#BtnDelChannels Text:"Remove Non-Terrain Chans" Func:ev_BtnDelChannels_Clicked \ Tooltip:"Deactivate non-terrain-related vertex-colour channels for selected geometry.\nAsks user for permission if object uses non-terrain materials.\n\nKeeps channels -2, 0, 1, 2, 5, 9, 13\n\n(This command can't be undone)", CtrlDef name:#BtnFixSeamVerts Text:"Find/Fix Seams" func:ev_BtnFixSeamVerts_Clicked \ tooltip:("Find verts between faces with non-blended texmaps, and auto-fix where possible") ) do ( local NewBtn = DotNetObject "Button" NewBtn.Text = Item.Text NewBtn.FlatStyle = NewBtn.FlatStyle.System NewBtn.AutoSize = True NewBtn.Height = 30 NewBtn.Margin = (dotNetObject "Padding" 0 0 3 3) -- Add tooltip: dnTooltip.SetToolTip NewBtn Item.Tooltip dotNet.addEventHandler NewBtn "MouseUp" Item.Func -- Set layout-flow break, or not: ThisPanel.SetFlowBreak NewBtn Item.FlowBreak ThisPanel.Controls.Add NewBtn dotNet.setLifetimeControl NewBtn #dotnet ) ) ) -- Set up banner: BannerStruct.Setup() -- Update the tool-form: ( ResumeRedraw() ToolForm.Refresh() ) PopPrompt() ), ------------------------------------------------------------------------------ -- Initialise Terrain Palette tool: ------------------------------------------------------------------------------ fn Create = ( PushPrompt "Initialising Terrain Painter" -- Initial form-setup: InitForm() -- Update controls for current selection: UpdateCtrls() -- Set up selection-change callback: AddCallbacks() PopPrompt() ) ) -- Start tool: gRsTerrainPalette = RsTerrainPalette() gRsTerrainPalette.Create() --- TEST: --gRsTerrainPalette.GetTexmapVerts() --gRsTerrainPalette.DeconstructColour (128*[1,1,1]/255)