--rsl_lodeditor.ms --Andy Davis --version 1.1 --introduction of tabbed section to distinguish between scene lods and drawable lods --version 1.2 --ammendment of the original code to account for the variation of scene LOD occurences in interior scenes --version 1.3 --ammendment to include MILO LOD occurences for interior scenes --edit 09/02/2011 --reduced object list by discounting XRef objects --window resizable --size and position of window saved in ini file --edit 18/02/2011: visible only filter added, reformatted buttons --edit 19/07/2011: url:bugstar:158316 user defined properties for LOD values set up to work ---------------------------------------------------------------------------------------------------------------- --HEADER INFO ---------------------------------------------------------------------------------------------------------------- /* DEVELOPMENT STATUS: bug.... UpdateTreeList doesn't work if you open a new scene file FUTURE IMPLEMENTATIONS: drag and drop for LOD parenting highlighting for the current tab change cursor to crosshair when in pick parent mode - setSysCur #select implement the new ValidateSelection() function into other functions to simplify the code possible introduction of an UpdateFormHeight() function?? highlight objects that are both scene and drawable lods MILO integration for interior maps NOTES: The main difference between Scene LODs and Drawable LODs with reference to this script is that a Scene LOD can have multiple children A drawable LOD can only have one child. This is reflected in the branching loops The branches are distinguished using the variable, currentMode, which is set to either "scene" or "drawable" by the tabPanel eventHandler, TabClicked The two LOD systems use similar functions to manage LODs: Scene LODs Drawable LODs RsSceneLink.GetParent LinkType_LOD RsLodDrawable_GetLowerDetailModel RsSceneLink.SetParent LinkType_LOD RsLodDrawable_SetLowerDetailModel getLODChildren RsLodDrawable_GetHigherDetailModel RsSceneLink.RemoveContainer LinkType_LOD RsSceneLink.RemoveContainer linkType_DRAWLOD The LOD parenting script error checks the prospective new Scene LOD hierarchy for 3 cases that would result in a bad hierarchy. There is an extra case for Drawable LODs. These cases are as follows: --VALIDATION ALGORITHMS --Case Solution --CASE 1: parentObject is in selectedObjects warning message and abort --CASE 2: parentObject is a child of selectedObjects orphan the parent --CASE 3: parentObject has a grandparent orphan the parent's parent --CASE 4: selectedObjects have grandchildren orphan the grandchildren --CASE 5: parentObject has a parent AND selectedObjects have children orphan the parent --CASE 6: parentObject has other children resulting in an illegal sibling orphan the illegal sibling (drawable LOD only) */ ---------------------------------------------------------------------------------------------------------------- --FILE INs ---------------------------------------------------------------------------------------------------------------- filein "pipeline/util/file.ms" filein "rockstar/export/settings.ms" filein "pipeline/util/drawablelod.ms" filein "pipeline/ui/treelodeditor.ms" filein (RsConfigGetWildWestDir()+"script/3dsMax/_common_functions/RSL_dotNetUIOps.ms") filein (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms") RsCollectToolUsageData (getThisScriptFilename()) ---------------------------------------------------------------------------------------------------------------- --GLOBAL INSTANCE ---------------------------------------------------------------------------------------------------------------- global LODEditor --global LinkType_LOD = 0 ---------------------------------------------------------------------------------------------------------------- --MAIN STRUCTURE ---------------------------------------------------------------------------------------------------------------- struct LodListStruct ( ---------------------------------------------------------------------------------------------------------------- --VARIABLES ---------------------------------------------------------------------------------------------------------------- --scene variables objectList, --all gta objects in the scene selectedObjects, --selected objects from the scene nodeList, --the tree list nodes in the order they appear in the tree list selectedNodes, --selected nodes within the tree list daddyList, --tree list nodes at the root of the list parentObject, --buffer for selecting LOD parent pickLodParentMode = false, --flag set to true when picking a LOD parent showNonLODObjects = true, --set to false to show only LOD objects in the tree list VisibleOnly = false, IniFilePath = (RsConfigGetWildWestDir() + "script/max/rockstar_london/config/RSL_LodEditor.ini"), --UI variables lodListForm, borderHeight = 98, --don't modify... this is the fixed height of form border elements formHeight, formWidth = 470, lodListTable, tabPanel, tab1, tab2, tab3, treeListPanel, treeListRowHeight = 19, buttonPanel, buttonHeight = 20, numButtonColumns = 4, numRows = 3, buttonPanelHeight = 120, buttonWidth = (formWidth - 38)/numButtonColumns, listPanel, parentingPanel, attributePanel, splayPanel, infoPanel, infoPanelHeight = 35, actionPanel, --table container used for lod and txd panels lodDistanceLabel, --label to provide info regarding applying lod distances lodDistanceInput, --control used to set new lod values txdLabel, txdInput, MILOHelpForm, ToolTip, showNonLODsCheckBox, showVisibleOnlyCheckBox, textCol = (colorMan.getColor #window) * 255, textColour = (dotNetClass "System.Drawing.Color").FromArgb 63 63 63, --choose the starting LOD mode as being "scene" or "drawable": -- currentMode = "scene", currentMode = "scene", ---------------------------------------------------------------------------------------------------------------- --PRESETS ---------------------------------------------------------------------------------------------------------------- DS_Fill = (dotNetClass "System.Windows.Forms.DockStyle").Fill, Font_Small = dotNetObject "System.Drawing.Font" "Microsoft sans serif" 7, Font_Calibri = dotNetObject "System.Drawing.Font" "Calibri" 8, CA_Center = (dotNetClass "System.Drawing.ContentAlignment").MiddleCenter, RGB_NeonGreen = (dotNetClass "System.Drawing.Color").FromARGB 204 255 0, RGB_MidGrey = (dotNetClass "System.Drawing.Color").fromARGB 153 153 153, RGB_MediumBlue = (dotNetClass "System.Drawing.Color").MediumBlue, RGB_CornflowerBlue = (DotNetClass "System.Drawing.Color").CornflowerBlue, RGB_LightGrey = (dotNetClass "System.Drawing.Color").fromARGB 204 204 204, RGB_OffWhite = (dotNetClass "System.Drawing.Color").fromARGB 232 232 232, RGB_Gainsboro = (dotNetClass "System.Drawing.Color").Gainsboro, RGB_GhostWhite = (dotNetClass "System.Drawing.Color").GhostWhite, RGB_WhiteSmoke = (dotNetClass "System.Drawing.Color").WhiteSmoke, RGB_White = (dotNetClass "System.Drawing.Color").White, RGB_PaleOrange = (dotNetClass "System.Drawing.Color").fromARGB 255 245 225, RGB_PaleBlue = (dotNetClass "System.Drawing.Color").fromARGB 235 245 255, RGB_YellowOrange = (dotNetClass "System.Drawing.Color").fromARGB 255 204 0, RGB_Pink = (dotNetClass "System.Drawing.Color").fromARGB 255 216 216, RGB_BabyBlue = (dotNetClass "System.Drawing.Color").fromARGB 204 224 255, RGB_Green = (dotNetClass "System.Drawing.Color").fromARGB 64 255 64, RGB_Red = (dotNetClass "System.Drawing.Color").fromARGB 255 32 32, RGB_Transparent = (dotNetClass "System.Drawing.Color").transparent, Padding_None = dotNetObject "System.Windows.Forms.Padding" 0, TA_Center = (dotNetClass "System.Drawing.ContentAlignment").MiddleCenter, TA_Left = (dotNetClass "System.Drawing.ContentAlignment").MiddleLeft, ------------------------------------------------------------------------------------------------------------------------------------------ --LOAD/SAVE FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------ fn SaveIniFile = ( setINISetting LODEditor.IniFilePath "LODEditor" "WinLocX" (LODEditor.lodListForm.Location.x as string) setINISetting LODEditor.IniFilePath "LODEditor" "WinLocY" (LODEditor.lodListForm.Location.y as string) setINISetting LODEditor.IniFilePath "LODEditor" "WinWidth" (LODEditor.lodListForm.Width as string) setINISetting LODEditor.IniFilePath "LODEditor" "WinHeight" (LODEditor.lodListForm.Height as string) setINISetting LODEditor.IniFilePath "LODEditor" "ShowNonLOD" (LODEditor.showNonLODsCheckBox.Checked as string) setINISetting LODEditor.IniFilePath "LODEditor" "VisibleOnly" (LODEditor.showVisibleOnlyCheckBox.Checked as string) ), fn LoadIniFile = ( --default values local WinLocX = 0 local WinLocY = 40 local WinWidth = 470 local WinHeight = 800 local ShowNonLOD = true local VisibleOnly = true try ( WinLocX = getINISetting LODEditor.IniFilePath "LODEditor" "WinLocX" as integer WinLocY = getINISetting LODEditor.IniFilePath "LODEditor" "WinLocY" as integer WinWidth = getINISetting LODEditor.IniFilePath "LODEditor" "WinWidth" as integer WinHeight = getINISetting LODEditor.IniFilePath "LODEditor" "WinHeight" as integer ShowNonLOD = getINISetting LODEditor.IniFilePath "LODEditor" "ShowNonLOD" as booleanClass VisibleOnly = getINISetting LODEditor.IniFilePath "LODEditor" "VisibleOnly" as booleanClass ) catch() lodListForm.Location = dotNetObject "system.drawing.point" WinLocX WinLocY lodListForm.Size = dotNetObject "System.Drawing.Size" WinWidth WinHeight LODEditor.showNonLODsCheckBox.Checked = ShowNonLOD LODEditor.showVisibleOnlyCheckBox.Checked = VisibleOnly ), ---------------------------------------------------------------------------------------------------------------- --UI FUNCTIONS ---------------------------------------------------------------------------------------------------------------- fn UpdateObjectList = --function updates the objectList array based on valid Gta objects in the scene ( if LODEditor.VisibleOnly == true then ( objectList = for this in objects where (getAttrClass this == "Gta Object") and (classOf this != XrefObject) and (this.isHidden == false) collect this MILOObjects = for this in objects where (getAttrClass this == "Gta MILO") and (classOfThis != XrefObject) and (this.isHidden == false) collect this ) else ( objectList = for this in objects where (getAttrClass this == "Gta Object") and (classOf this != XrefObject) collect this MILOObjects = for this in objects where (getAttrClass this == "Gta MILO") and (classOfThis != XrefObject) collect this ) join objectList MILOObjects ), ---------------------------------------------------------------------------------------------------------------- fn UpdateSelectedObjects = --function updates the selectedObjects array from objects selected in the scene as opposed to objects selected in the nodeList ( selectedObjects = for this in selection where (getAttrClass this == "Gta Object") and (classOf this != XrefObject) collect this MILOObjects = for this in selection where (getAttrClass this == "Gta MILO") and (classOf this != XrefObject) collect this join selectedObjects MILOObjects ), ---------------------------------------------------------------------------------------------------------------- fn UpdateSelectedObjectsFromSelectedNodes = --function updates the selectedObjects array from nodes selected in the treelist ( selectedObjects = for this in nodeList where (this.Selected == true) and (IsValidNode this.Tag.Value == true) collect this.Tag.Value if (selectedObjects.Count > 0) then ( select selectedObjects ) ), ---------------------------------------------------------------------------------------------------------------- fn UpdateSelectedNodes = --function updates the selectedNodes array from objects selected in the treelist ( selectedNodes = for this in nodeList where (this.Selected == true) and (IsValidNode this.Tag.Value == true) collect this ), ---------------------------------------------------------------------------------------------------------------- fn DeselectMILOObjects = -- function removes MILO objects from selectedNodes and objects ( --remove from nodes for this in nodeList where (this.Selected == true) do ( if (getAttrClass this.Tag.Value == "Gta MILO") then ( messagebox ("Removing MILO object " + (this.Tag.Value.name) as string + " from selection") this.Selected = false ) ) --remove from scene selection buffer = for this in selection where (getattrclass this != "Gta MILO") collect this clearSelection() -- for some weird reason, MILO objects do not deselect unless I clearSelection first selection = buffer ), fn CheckForMILONodes = -- function checks selection for MILO nodes ( nodeFound = false UpdateSelectedNodes() --check nodes if (selectedNodes.Count > 0) then ( for this in selectedNodes do ( if (getAttrClass this.Tag.Value == "Gta MILO") then ( nodeFound = true ) ) ) else -- no nodes selected, so check objects ( for this in selection do ( if (getAttrClass this == "Gta MILO") then ( nodeFound = true ) ) ) return nodeFound ), ---------------------------------------------------------------------------------------------------------------- fn UpdateSelectedNodesFromSelection = --selects nodes in the treelist based on the selection ( selectedNodes = #() for this in nodeList do ( for thisObject in selection do ( if (this.Tag.Value == thisObject) then ( this.Selected = true append selectedNodes this ) ) ) ), fn UpdateListNodesFromSelection = --selects nodes in the treelist based on the selection ( selectedNodes = treeListPanel.selection treeNodes = for i = 0 to (treeListPanel.nodes.count - 1) collect treeListPanel.nodes.Item[i] do ( tempArr = #() for node in treeNodes do ( node.selected = false if node.HasChildren then ( kids = for n = 0 to (node.nodes.count - 1) collect node.nodes.item[n] join tempArr kids ) ) if tempArr.count > 0 then treeNodes = tempArr else treeNodes = #() ) while treeNodes.count > 0 treeListPanel.Update() if selection.count > 0 then ( visibleNodes = #() for thisObject in selection do ( for n = 0 to (treeListPanel.nodes.count - 1) do ( thisNode = treeListPanel.nodes.Item[n] if (thisNode.Tag.Value == thisObject) then ( thisNode.Selected = true append visibleNodes thisNode ) ) ) --focus on first selected item if visibleNodes.count > 0 then ( treeListPanel.MakeNodeVisible visibleNodes[1] ) ) ), ---------------------------------------------------------------------------------------------------------------- --nb: there is no UpdateNodeList function as this operation is carried out in the function, UpdateTreeList() ---------------------------------------------------------------------------------------------------------------- fn ValidateSelection = --function checks whether the user has any list nodes selected, and if not, checks for selected scene objects ( UpdateSelectedNodes() if (selectedNodes.Count == 0) then ( --no nodes selected so check for objects instead UpdateSelectedObjects() if (selectedObjects.Count == 0) then ( --no selected objects either return false ) else ( --no selected nodes, but selected objects UpdateSelectedNodesFromSelection() return true ) ) else ( --we have selected nodes UpdateSelectedObjectsFromSelectedNodes() return true ) ), ---------------------------------------------------------------------------------------------------------------- fn GetBiggestDimension thisObject = --function returns the largest dimension of the object passed to it -- as used in the splay functions ( if (thisObject != undefined) then ( local xdim = thisObject.max.x - thisObject.min.x local ydim = thisObject.max.y - thisObject.min.y local zdim = thisObject.max.z - thisObject.min.z if (xdim > ydim) then ( if (xdim > zdim) then ( biggestDimension = xdim ) else ( biggestDimension = zdim ) ) else ( if (ydim > zdim) then ( biggestDimension = ydim ) else ( biggestDimension = zdim ) ) return biggestDimension ) else ( return undefined ) ), ---------------------------------------------------------------------------------------------------------------- fn UpdateInfoPanel = --function sets up the info panel at the bottom with the latest information ( if lodListTable.Controls.Item[2].Name != "infoPanel" then ( lodListTable.Controls.Remove lodListTable.Controls.Item[2] LODEditor.InitInfoPanel() ) if (currentMode == "scene") then ( modeText = "Scene LOD Mode" ) else --currentMode == "drawable" ( modeText = "Drawable LOD Mode" ) if (showNonLODObjects == false) then ( modeText = modeText + " (non-LODs hidden)" ) if (objectList.count > 1) then ( objectPlural = "s" ) else objectPlural = "" if (selectedObjects.Count > 1) then ( selPlural = "s" ) else selPlural = "" infoPanel.Text = modeText + "\n" + (nodeList.Count as string) + " object" + objectPlural + " in list, " infoPanel.Text = infoPanel.Text + (selectedObjects.Count as string) + " object" + selPlural + " selected" ), ---------------------------------------------------------------------------------------------------------------- fn UpdateTreeList = --function picks up all the objects in the scene, reads the LOD hierarchy and then places puts the data into the treelist --this function is the main function for managing the data in the xtratreelist --selected objects highlighted are those whose scene objects are selected --objectList updated --nodeList updated --selectedObjects updated from their state in the scene --selected nodes updated as from the selectedObjects ( UpdateObjectList() LODObjectList = #() --init the daddyList according to the currentMode case currentMode of ( "scene": ( daddyList = for this in objectList where (RsSceneLink.GetParent LinkType_LOD this == undefined) collect this --if we are on drawable tab, need different search pattern if (showNonLODObjects == false) then ( LODOnly = for this in daddyList where (getLODChildren this).Count > 0 collect this daddyList = LODOnly ) ) "drawable": ( daddyList = for this in objectList where (RsLodDrawable_GetLowerDetailModel this == undefined) collect this if (showNonLODObjects == false) then ( LODOnly = for this in daddyList where (RsLodDrawable_GetHigherDetailModel this != undefined) collect this daddyList = LODOnly ) ) "tree": ( daddyList = for this in objectList where (RsSceneLink.GetParent LinkType_CombinerMesh this == undefined) collect this if (showNonLODObjects == false) then ( LODOnly = for this in daddyList where (RsSceneLink.GetParent LinkType_CombinerMesh this != undefined) collect this daddyList = LODOnly ) ) ) nodeList = #() --clear node array for updated nodes treeListPanel.ClearNodes() --clear the nodes from the tree list, should they exist --add the root objects treeListPanel.BeginUnboundLoad() for this in daddyList do ( if (getattrclass this == "Gta MILO") then --no LOD distance or txd values for MILO object ( distanceValue = txdValue = "MILO" ) else ( distanceValue = getattr this (getattrindex "Gta Object" "LOD distance") txdValue = getattr this (getattrindex "Gta Object" "TXD") ) gdaddyNode = treeListPanel.AppendNode #(this.name, distanceValue, txdValue) -1 -- value of -1 sets root object gdaddyNode.tag = dotNetMXSValue this append nodeList gdaddyNode --init childList according to currentMode case currentMode of ( "scene": ( childList = getLODChildren this if (childList.count > 0) then ( for thisChild in childList do ( if (getattrclass thisChild == "Gta MILO") then ( distanceValue = txdValue = "MILO" ) else ( distanceValue = getattr thisChild (getattrindex "Gta Object" "LOD distance") txdValue = getattr thisChild (getattrindex "Gta Object" "TXD") ) daddyNode = treeListPanel.AppendNode #(thisChild.name, distanceValue, txdvalue) gdaddyNode daddyNode.tag = dotNetMXSValue thisChild append nodeList daddyNode gchildList = getLODChildren thisChild if gchildList.count > 0 then ( for thisgchild in gchildlist do ( if (getattrclass thisgchild == "Gta MILO") then ( distanceValue = txdValue = "MILO" ) else ( distanceValue = getattr thisgchild (getattrindex "Gta Object" "LOD distance") txdValue = getattr thisgchild (getattrindex "Gta Object" "TXD") ) childNode = treeListPanel.AppendNode #(thisgchild.name, distanceValue, txdvalue) daddyNode childNode.tag = dotNetMXSValue thisgchild append nodeList childNode ) ) ) ) ) "drawable": ( child = RsLodDrawable_GetHigherDetailModel this --we only have one child for Drawable LOD --so we don't loop through like we did for Scene Lod if (child != undefined) then ( distanceValue = getattr child (getattrindex "Gta Object" "LOD distance") txdValue = getattr child (getattrindex "Gta Object" "TXD") daddyNode = treeListPanel.AppendNode #(child.name, distanceValue, txdvalue) gdaddyNode daddynode.tag = dotNetMXSValue child append nodeList daddyNode gchild = RsLodDrawable_GetHigherDetailModel child if (gchild != undefined) then ( distanceValue = getattr gchild (getattrindex "Gta Object" "LOD distance") txdValue = getattr gchild (getattrindex "Gta Object" "TXD") childNode = treeListPanel.AppendNode #(gchild.name, distanceValue, txdvalue) daddyNode childNode.tag = dotNetMXSValue gchild append nodeList childNode ) ) ) "tree": ( child = (RsSceneLink.GetChildren LinkType_CombinerMesh this &kids)[1] --child = RsLodDrawable_GetHigherDetailModel this --we only have one child for Drawable LOD --so we don't loop through like we did for Scene Lod if (child != undefined) then ( distanceValue = getattr child (getattrindex "Gta Object" "LOD distance") txdValue = getattr child (getattrindex "Gta Object" "TXD") daddyNode = treeListPanel.AppendNode #(child.name, distanceValue, txdvalue) gdaddyNode daddynode.tag = dotNetMXSValue child append nodeList daddyNode gchild = RsLodDrawable_GetHigherDetailModel child if (gchild != undefined) then ( distanceValue = getattr gchild (getattrindex "Gta Object" "LOD distance") txdValue = getattr gchild (getattrindex "Gta Object" "TXD") childNode = treeListPanel.AppendNode #(gchild.name, distanceValue, txdvalue) daddyNode childNode.tag = dotNetMXSValue gchild append nodeList childNode ) ) ) ) ) treeListPanel.EndUnboundLoad() --when the table inits, one node is incorrectly selected, so need to deselect all the nodes for this in nodeList do ( this.Selected = false this.Expanded = true ) --select all nodes whose scene objects are selected UpdateSelectedObjects() selectedNodes = #() for this in nodeList do ( for thisObject in selectedObjects do ( if this.Tag.Value == thisObject then ( this.Selected = true append selectedNodes this ) ) ) --resize form to match new treelist size formHeight = borderHeight + (objectList.count * treeListRowHeight) + buttonPanelHeight + infoPanelHeight lodListForm.height = formHeight-- - 14 --nb: this is a hack, don't know why I have to adjust the figure by 14... but it works... ), ---------------------------------------------------------------------------------------------------------------- --EVENT HANDLERS ---------------------------------------------------------------------------------------------------------------- --TAB CONTROL ---------------------------------------------------------------------------------------------------------------- fn DispatchTabClicked s e = ( LODEditor.TabClicked s e ), fn TabClicked s e = --this function is triggered on a .SelectedIndexChanged event ( case tabPanel.selectedIndex of ( 0: ( currentMode = "scene" lodListForm.text = "RS LOD Editor - Scene LODs" tab1.Controls.Add treeListPanel infoPanel.BackColor = RGB_Pink UpdateSelectedObjectsFromSelectedNodes() UpdateTreeList() ) 1: ( currentMode = "drawable" lodListForm.text = "RS LOD Editor - Drawable LODs" tab2.Controls.Add treeListPanel infoPanel.BackColor = RGB_BabyBlue UpdateSelectedObjectsFromSelectedNodes() UpdateTreeList() ) 2: ( currentMode = "tree" lodListForm.text = "RS LOD Editor - Tree LODs" tab3.Controls.Add treeListPanel infoPanel.BackColor = RGB_Green UpdateSelectedObjectsFromSelectedNodes() UpdateTreeList() ) ) UpdateInfoPanel() ), ---------------------------------------------------------------------------------------------------------------- --BUTTON PANEL ---------------------------------------------------------------------------------------------------------------- --LIST COMMANDS ---------------------------------------------------------------------------------------------------------------- --CLEAR LIST ---------------------------------------------------------------------------------------------------------------- fn ClearList s e = ( ClearSelection() LODEditor.UpdateTreeList() LODEditor.UpdateInfoPanel() ), ---------------------------------------------------------------------------------------------------------------- fn SelectAll = ( select objects LODEditor.UpdateTreeList() LODEditor.UpdateInfoPanel() ), ---------------------------------------------------------------------------------------------------------------- --function updates list based on scene objects and scene selection fn SceneToList = ( LODEditor.UpdateTreeList() LODEditor.UpdateInfoPanel() ), ---------------------------------------------------------------------------------------------------------------- --function selects scene objects based on list selection fn ListToScene = ( LODEditor.UpdateSelectedObjectsFromSelectedNodes() LODEditor.UpdateTreeList() LODEditor.UpdateInfoPanel() ), --select list item when selectionChanged callbakc is fired fn selObjListItemFocus = ( if $selection[1] != undefined then --find the item in the list and focus on it ( ) ), ---------------------------------------------------------------------------------------------------------------- --LIST EXPANSION/CONTRACTION ---------------------------------------------------------------------------------------------------------------- --exapand all fn Expand = ( for this in LODEditor.nodeList do ( this.expanded = true ) ), ---------------------------------------------------------------------------------------------------------------- --contract all fn Contract= ( for this in LODEditor.nodeList do ( this.expanded = false ) ), ---------------------------------------------------------------------------------------------------------------- --SET ATTRIBUTES PANEL ---------------------------------------------------------------------------------------------------------------- --LOD DISTANCE ---------------------------------------------------------------------------------------------------------------- --set lod distance fn DispatchSetLodDistance s e = ( LODEditor.DeselectMILOObjects() LODEditor.UpdateSelectedNodes() if LODEditor.selectedNodes.count == 0 then ( --there are no nodes selected, so check to see if objects are selected instead LODEditor.UpdateSelectedObjects() if (LODEditor.selectedObjects.count == 0) then ( --there are no objects selected either, so provide feedback case (LODEditor.lodListTable.Controls.Item[2].Name) of ( "txdPanel": LODEditor.lodDistanceLabel.text = "no nodes selected" "infoPanel": LODEditor.infoPanel.text = "no nodes selected" default: messagebox "error - unknown control" ) ) else ( if (LODEditor.lodListTable.Controls.Item[2].Name != "lodDistancePanel") then ( LODEditor.lodListTable.Controls.Remove LODEditor.lodListTable.Controls.Item[2] LODEditor.InitLodDistancePanel() ) ) LODEditor.UpdateTreeList() ) else ( LODEditor.selectedObjects = for this in LODEditor.selectedNodes collect this.Tag.value select LODEditor.selectedObjects if (LODEditor.lodListTable.Controls.Item[2].Name != "lodDistancePanel") then ( LODEditor.lodListTable.Controls.Remove LODEditor.lodListTable.Controls.Item[2] LODEditor.InitLodDistancePanel() ) LODEditor.UpdateTreeList() ) ), ---------------------------------------------------------------------------------------------------------------- --apply LOD distance fn DispatchApplyLD s e = ( LODEditor.ApplyLD s e ), --function applies the lod distance stored in the variable newLodDistanceValue to any selected nodes fn ApplyLD s e = ( --find selected nodes and select their objects UpdateSelectedNodes() UpdateSelectedObjectsFromSelectedNodes() if selectedNodes.count == 0 then ( lodDistanceLabel.text = "no nodes selected" ) else ( --apply new value newLodDistance = lodDistanceInput.Value for this in selectedNodes do ( setAttr this.Tag.value (getattrindex "Gta Object" "LOD distance") newLodDistance ) --update treelist UpdateTreeList() formHeight = borderHeight + (objectList.count * treeListRowHeight) + buttonPanelHeight + infoPanelHeight lodListForm.height = formHeight - 14 --hack for this in selectedNodes do ( this.Selected = true ) ) ), ---------------------------------------------------------------------------------------------------------------- --TEXTURE DICTIONARY ---------------------------------------------------------------------------------------------------------------- fn DispatchSetTxd s e = ( LODEditor.SetTxd s e ), fn SetTxd s e = ( DeselectMILOObjects() UpdateSelectedNodes() if selectedNodes.count == 0 then ( --there are no nodes selected, so check to see if objects are selected instead UpdateSelectedObjects() if (selectedObjects.count == 0) then ( --there are no objects selected either, so provide feedback case (lodListTable.Controls.Item[2].Name) of ( "txdPanel": lodDistanceLabel.text = "no nodes selected" "infoPanel": infoPanel.text = "no nodes selected" default: messagebox "error - unknown control" ) ) else ( if (lodListTable.Controls.Item[2].Name != "txdPanel") then ( lodListTable.Controls.Remove lodListTable.Controls.Item[2] LODEditor.InitTxdPanel() ) ) UpdateTreeList() ) else ( selectedObjects = for this in selectedNodes collect this.Tag.Value select selectedObjects if (lodListTable.Controls.Item[2].Name != "txdPanel") then ( lodListTable.Controls.Remove lodListTable.Controls.Item[2] LODEditor.InitTxdPanel() ) UpdateTreeList() ) ), ---------------------------------------------------------------------------------------------------------------- --function applies the txd stored in the variable newTxd to any selected nodes fn ApplyTxd = ( --find selected nodes and select their objects LODEditor.UpdateSelectedNodes() LODEditor.UpdateSelectedObjectsFromSelectedNodes() if LODEditor.selectedNodes.count == 0 then ( txdLabel.text = "no nodes selected" ) else ( --apply new value newTxd = LODEditor.txdInput.Text for this in LODEditor.selectedNodes do ( setAttr this.Tag.Value (getattrindex "Gta Object" "TXD") newTxd ) --update treelist LODEditor.UpdateTreeList() LODEditor.UpdateObjectList() LODEditor.formHeight = LODEditor.borderHeight + (LODEditor.objectList.count * LODEditor.treeListRowHeight) + LODEditor.buttonPanelHeight + LODEditor.infoPanelHeight LODEditor.lodListForm.height = LODEditor.formHeight - 14 --hack for this in LODEditor.selectedNodes do ( this.Selected = true ) ) ), ---------------------------------------------------------------------------------------------------------------- --LOD PARENTING ---------------------------------------------------------------------------------------------------------------- fn DispatchSetLodParent s e = ( LODEditor.SetLodParent s e ), fn SetLodParent s e = --note: this handler only activates if pickLodParentMode is set to true ( if pickLodParentMode == true then ( parentObject = treeListPanel.FocusedNode.Tag.Value infoPanel.text =(parentObject.name as string) + " picked as LOD parent" ---------------------------------------------------------------------------------------------------------------- --VALIDATION ALGORITHMS -- Case Solution -- CASE 1: parentObject is in selectedObjects warning message and abort -- CASE 2: parentObject is a child of selectedObjects orphan the parent -- CASE 3: parentObject has a grandparent orphan the parent's parent -- CASE 4: selectedObjects have grandchildren orphan the grandchildren -- CASE 5: parentObject has a parent AND selectedObjects have children orphan the parent -- CASE 6: parentObject has other children resulting in an illegal sibling orphan the illegal sibling (drawable LOD only) -- MILO CASES: -- -- MILOCASE A: parentObject is a MILO object warn and abort -- MILOCASE B: selectedObjects cannot be linked to different MILO rooms warn and abort (scene LOD only) -- MILOCASE C: MILO objects cannot be linked as drawable LOD parents warn and abort (drawable LOD only) ---------------------------------------------------------------------------------------------------------------- --CASE 1: the parentObject is in selectedObjects warning message and abort validParent = true for this in selectedObjects do ( if (this == parentObject) then ( validParent = false ) ) --end of CASE 1 if (validParent == false) then --abort ( --infoPanel.text = "CASE 1: parentObject is in selectedObjects\nObject cannot be it's own LOD" messagebox "The parent is in the selected objects; object cannot be it's own LOD." ) else --run the remaining validation algorithms and apply parenting ( -- MILOCASE A: parentObject is a MILO object warn and abort if (getAttrClass parentObject == "Gta MILO") then ( --dialog box: cannot LOD parent to this MILO object because connected to room, break connection to room? messagebox (parentObject.Name as string + " selected as LOD parent: aborting LOD setup") ) --end of MILOCASE A else ( ---------------------------------------------------------------------------------------------------------------- --BRANCHING FOR LOD MODES STARTS HERE case currentMode of ( "scene": ( infoPanel.BackColor = RGB_Pink --note that the case code algorithms are different depending on currentMode --CASE 2: parentObject is a child of selectedObjects orphan the parent for this in selectedObjects do ( childrenBuffer = getLODChildren this if (childrenBuffer.Count > 0) then ( for thisChild in childrenBuffer do ( if (thisChild == parentObject) then ( RsSceneLink.RemoveContainer LinkType_LOD parentObject infoPanel.Text = "Warning: LOD parent is a child of a selected object, orphaning to maintain valid LOD hierarchy" ) ) ) ) --end of CASE 2 --CASE 3: parentObject has a grandparent orphan the parent's parent parentObjectParent = RsSceneLink.GetParent LinkType_LOD parentObject if (parentObjectParent != undefined) then ( if (RsSceneLink.GetParent LinkType_LOD parentObjectParent != undefined) then ( RsSceneLink.RemoveContainer LinkType_LOD parentObjectParent infoPanel.Text = "Warning: LOD parent has parent, orphaning to maintain valid LOD hierarchy" ) ) -- end of CASE 3 --CASE 4: selectedObjects have grandchildren orphan the grandchildren for this in selectedObjects do ( childrenBuffer = getLODChildren this if (childrenBuffer.Count > 0) then ( for thisChild in childrenBuffer do ( gchildrenBuffer = getLODChildren thisChild if (gchildrenBuffer.Count > 0) then ( for thisGchild in gchildrenBuffer do ( RsSceneLink.RemoveContainer LinkType_LOD thisGchild ) infoPanel.Text = "Warning: LOD children of selected objects orphaned to maintain valid LOD hierarchy" ) ) ) ) -- end of CASE 4 --CASE 5: parentObject has a parent AND selectedObjects have children orphan the parent parentObjectParent = RsSceneLink.GetParent LinkType_LOD parentObject if (parentObjectParent != undefined) then ( for this in selectedObjects do ( childrenBuffer = getLODChildren this if (childrenBuffer.Count > 0) then ( RsSceneLink.RemoveContainer LinkType_LOD parentObject infoPanel.text = "Warning: LOD parent orphaned to maintain valid LOD hierarchy" ) ) )--end of CASE 5 -- MILOCASE B: selectedObjects cannot be linked to different MILO rooms warn and abort (scene LOD only) local roomBuffer local newRoom local validSetup = true if (parentObject.Parent != undefined) then ( if (getAttrClass parentObject.Parent == "Gta MloRoom") then ( roomBuffer = parentObject.Parent ) ) for this in selectedObjects do ( if (this.parent != undefined) then ( if (getAttrClass this.parent == "Gta MloRoom") then ( newRoom = this.Parent if (roomBuffer != undefined and roomBuffer != newRoom) then ( validSetup = false ) else roomBuffer = newRoom ) ) --also: selected object's LOD children can't be in different rooms ) if (validSetup == false) then ( messagebox "Warning: selected objects belong to different MILO rooms: aborting" ) --end of MILOCASE B else --valid candidates for LOD setup ( for this in selectedObjects do ( RsSceneLink.RemoveContainer LinkType_LOD this RsSceneLink.AddContainer LinkType_LOD this RsSceneLink.SetParent LinkType_LOD this parentObject ) ) ) "drawable": ( infoPanel.BackColor = RGB_BabyBlue -- MILOCASE C: MILO objects cannot be linked as drawable LOD parents warn and abort (drawable LOD only) if (getAttrClass parentObject == "Gta MILO" or getAttrClass selectedObjects[1] == "Gta MILO") then ( messagebox "MILO objects cannot be set up as drawable LODs" ) else --valid non-MILO parent so can procede ( child = selectedObjects[1] -- CASE 2: parentObject is a child of selectedObjects orphan the parent if (parentObject == RsLodDrawable_GetHigherDetailModel child) then ( RsSceneLink.RemoveContainer linkType_DRAWLOD parentObject --infoPanel.text = "CASE 2: parentObject is a child of selectedObjects" ) -- CASE 3: parentObject has a grandparent orphan the parent's parent parentObjectParent = RsLodDrawable_GetLowerDetailModel parentObject if (parentObjectParent != undefined) then ( if (RsLodDrawable_GetLowerDetailModel parentObjectParent != undefined) then ( RsSceneLink.RemoveContainer linkType_DRAWLOD parentObjectParent --infoPanel.text = "CASE 3: parentObject has a grandparent" ) ) --CASE 4: selectedObjects have grandchildren orphan the grandchildren childChild = RsLodDrawable_GetHigherDetailModel child if (childChild != undefined) then ( gChild = RsLodDrawable_GetHigherDetailModel childChild if (gChild != undefined) then ( RsSceneLink.RemoveContainer linkType_DRAWLOD gChild infoPanel.Text = "Warning: selected objects base models orphaned to maintain valid LOD hierarchy" ) ) --CASE 5: parentObject has a parent AND selectedObjects have children orphan the parent parentObjectParent = RsLodDrawable_GetLowerDetailModel parentObject if (parentObjectParent != undefined) then ( if (RsLodDrawable_GetHigherDetailModel child != undefined) then ( RsSceneLink.RemoveContainer linkType_DRAWLOD parentObject infoPanel.text = "Warning: parent orphaned to maintain valid LOD hierarchy" ) ) --CASE 6: parentObject has other children resulting in an illegal sibling orphan the illegal sibling (drawable LOD only) parentObjectChild = RsLodDrawable_GetHigherDetailModel parentObject if (parentObjectChild != undefined and parentObjectChild != child) then ( RsSceneLink.RemoveContainer linkType_DRAWLOD parentObjectChild infoPanel.text = "Warning: parent LOD's other base model orphaned to maintain valid LOD hierarchy" ) --hook up the selected object to the parent RsLodDrawable_SetLowerDetailModel child parentObject ) ) "tree": ( infoPanel.BackColor = RGB_Green local selobj = selection[1] local maps = RsMapGetMapContainers() -- Make sure the two objects are in the same container selObjCont = RsMapObjectGetMapContainerFor selobj maps:maps parentObjectCont = RsMapObjectGetMapContainerFor parentObject maps:maps if ( selObjCont != undefined and selHiDefCont != undefined and selObjCont.name == selHiDefCont.name ) then ( RsSceneLink.RemoveContainer LinkType_CombinerMesh selobj RsSceneLink.AddContainer LinkType_CombinerMesh selobj RsSceneLink.SetParent LinkType_CombinerMesh parentObjectCont selobj ) else ( MessageBox ( parentObjectCont.name + " is not in the same map container so can't be added to the LOD hierarchy" ) ) ) ) ) --end of else after MILOCASE A ) --end of else after CASE 1 pickLodParentMode = false UpdateTreeList() ) else --just select the object in the scene ( select treeListPanel.FocusedNode.Tag.Value max zoomext sel ) ), --pick parent mode fn EnterPickParentMode = ( pickLodParentMode = true infoPanel.BackColor = infoPanel.BackColor.WhiteSmoke --cursor change to cross hair case currentMode of ( "scene": infoPanel.text = "Pick the Scene LOD parent" "drawable": infoPanel.text = "Pick the Drawable LOD parent" "tree": infoPanel.text = "Pick the Tree LOD parent" ) ), --parent lod fn DispatchParentLod s e = ( LODEditor.ParentLod s e ), fn ParentLod s e = ( --init info panel if not there if lodListTable.Controls.Item[2].Name != "infoPanel" then ( lodListTable.Controls.Remove lodListTable.Controls.Item[2] LODEditor.InitInfoPanel() ) --check for selected nodes UpdateSelectedNodes() if (selectedNodes.Count == 0) then --we have no selected nodes, so check for selected objects ( UpdateSelectedObjects() if (selectedObjects.Count == 0) then ( infoPanel.Text = "no nodes selected" ) else --we have objects selected but no nodes ( UpdateSelectedNodesFromSelection() if (currentMode == "drawable") then ( if (SelectedNodes.Count > 1) then ( infoPanel.Text = "Drawable LOD Mode: please select one node only" UpdateTreeList() ) else ( EnterPickParentMode() ) ) else --currentMode == "scene" ( EnterPickParentMode() ) ) ) else --we have selected nodes ( UpdateSelectedObjectsFromSelectedNodes() if (currentMode == "drawable") then ( if (selectedNodes.Count > 1) then ( infoPanel.Text = "Drawable LOD Mode: please select one node only" UpdateTreeList() ) else ( EnterPickParentMode() ) ) else --currentMode == "scene" ( EnterPickParentMode() ) ) ), ---------------------------------------------------------------------------------------------------------------- --unparent lod fn DispatchUnparentLod s e = ( LODEditor.UnparentLod s e ), fn UnparentLod s e = ( --init info panel if not there if lodListTable.Controls.Item[2].Name != "infoPanel" then ( lodListTable.Controls.Remove lodListTable.Controls.Item[2] LODEditor.InitInfoPanel() ) --check for selection UpdateSelectedNodes() if (selectedNodes.Count == 0) then ( UpdateSelectedObjects() if (selectedObjects.Count ==0) then ( infoPanel.Text = "no nodes selected" ) else -- we have selected objects, so update the nodes ( UpdateSelectedNodesFromSelection() for this in selectedObjects do ( case currentMode of ( "scene": ( RsSceneLink.RemoveContainer LinkType_LOD this ) "drawable": ( RsSceneLink.RemoveContainer linkType_DRAWLOD this ) "tree": ( RsSceneLink.RemoveContainer LinkType_CombinerMesh this ) ) ) infoPanel.Text = "LOD parents disconnected" ) ) else --we have selected nodes ( UpdateSelectedObjectsFromSelectedNodes() for this in selectedObjects do ( case currentMode of ( "scene": ( RsSceneLink.RemoveContainer LinkType_LOD this ) "drawable": ( RsSceneLink.RemoveContainer linkType_DRAWLOD this ) "tree": ( RsSceneLink.RemoveContainer LinkType_CombinerMesh this ) ) ) infoPanel.Text = "LOD parents disconnected" ) UpdateTreeList() ), ---------------------------------------------------------------------------------------------------------------- --SPLAY FUNCTIONS ---------------------------------------------------------------------------------------------------------------- fn GetLODDaddyListFromSelection = ( daddyList = #() UpdateSelectedObjects() if (selectedObjects.Count > 0) then ( for this in selectedObjects do ( --if LOD hierarchy present...add root to daddyList if (RsSceneLink.GetParent LinkType_LOD this != undefined) then ( parentBuffer = RsSceneLink.GetParent LinkType_LOD this if (RsSceneLink.GetParent LinkType_LOD parentBuffer != undefined) then ( --grandparent is the daddy appendIfUnique daddyList (RsSceneLink.GetParent LinkType_LOD parentBuffer) ) else ( --parent is the daddy appendIfUnique daddyList parentBuffer ) ) else --this has no LOD parent... check for LOD children ( if ((getLODChildren this).Count > 0) then --for some reason (getLODChildren $ != undefined) is always true!! ( --this must be the daddy... it has no parent but has children appendIfUnique daddyList this ) --otherwise, no parent no children, therefore no valid LOD hierarchy ) ) if (daddyList.Count > 0) then ( return daddyList ) else ( return undefined ) ) else ( --no objects in selection return undefined ) ), fn SplayAlongAxis axis = --expands LOD objects along the axis member given by the argument, axis --currently the axis values are x and y, but could be extended to include z axis too ( --init info panel if not there if lodListTable.Controls.Item[2].Name != "infoPanel" then ( lodListTable.Controls.Remove lodListTable.Controls.Item[2] LODEditor.InitInfoPanel() ) --check for selection if (ValidateSelection() == true) then ( local LODDaddyList = #() infoPanel.Text = "Expanding selected objects" LODDaddyList = GetLODDaddyListFromSelection() if (LODDaddyList != undefined) then ( --mode split if (currentMode == "scene") then ( local children local gchildren --for loop to run through daddyList for this in LODDaddyList do ( --work out largest xyz dimension of daddy: expansionValue expansionValue = GetBiggestDimension this children = getLODChildren this for thisChild in children do --loop through children ( --expand children by expansionValue if (axis == "x") then ( thisChild.pos.x += (1.2 * expansionValue) -1.2 --used to make a slight gap between models ) else --axis == "y" ( thisChild.pos.y += (1.2 * expansionValue) ) --expand grandchildren by 2 * expansionValue gchildren = getLODChildren thisChild if (gchildren.Count > 0) then ( for thisgchild in gchildren do ( if (axis == "x") then ( thisgchild.pos.x += (2.4 * expansionValue) ) else --axis == "y" ( thisgchild.pos.y += (2.4 * expansionValue) ) ) ) ) ) ) --end of scene mode split else --currentMode == "drawable" ( local child local gchild --for loop to run through daddyList for this in LODDaddyList do ( --work out largest xyz dimension of daddy: expansionValue expansionValue = GetBiggestDimension this child = RsLodDrawable_GetHigherDetailModel this --expand child by expansionValue if (axis == "x") then ( child.pos.x += expansionValue ) else --axis == "y" ( child.pos.y += expansionValue ) --expand grandchild by 2 * expansionValue gchild = RsLodDrawable_GetHigherDetailModel child if (gchild != undefined) then ( if (axis == "x") then ( gchild.pos.x += (2 * expansionValue) ) else --axis == "y" ( gchild.pos.y += (2 * expansionValue) ) ) ) ) ) else ( infoPanel.Text = "no valid LOD hierarchies selected" ) ) else --nothing selected ( infoPanel.Text = "no valid nodes/objects selected" ) UpdateTreeList() ), fn DispatchSplayX s e = ( LODEditor.SplayX s e ), fn SplayX s e = ( SplayAlongAxis "x" ), fn DispatchSplayY s e = ( LODEditor.SplayY s e ), fn SplayY s e = ( SplayAlongAxis "y" ), fn DispatchSplayCollapse s e = ( LODEditor.SplayCollapse s e ), fn SplayCollapse s e = ( --init info panel if not there if lodListTable.Controls.Item[2].Name != "infoPanel" then ( lodListTable.Controls.Remove lodListTable.Controls.Item[2] LODEditor.InitInfoPanel() ) if (ValidateSelection() == true) then ( local LODDaddyList = #() infoPanel.Text = "Collapsing selected LOD objects" LODDaddyList = GetLODDaddyListFromSelection() --for loop to run through each selected object if (LODDaddyList != undefined) then ( --mode split if (currentMode == "scene") then ( local children local gchildren --for loop to run through daddyList for thisDaddy in LODDaddyList do ( children = getLODChildren thisDaddy for thisChild in children do ( thisChild.pos = thisDaddy.pos gchildren = getLODChildren thisChild if (gchildren.Count > 0) then ( for thisGchild in gchildren do ( thisGchild.pos = thisDaddy.pos ) ) ) ) ) else --currentMode == "drawable" ( local child local gchild --for loop to run through daddyList for thisDaddy in LODDaddyList do ( child = RsLodDrawable_GetHigherDetailModel thisDaddy child.pos = thisDaddy.pos gchild = RsLodDrawable_GetHigherDetailModel child if (gchild != undefined) then ( gchild.pos = thisDaddy.pos ) ) ) ) else ( infoPanel.Text = "no valid LOD hierarchies selected" ) ) ), ---------------------------------------------------------------------------------------------------------------- --SHOW NON LODS TOGGLE ---------------------------------------------------------------------------------------------------------------- fn DispatchToggleShowNonLODs s e = LODEditor.ToggleShowNonLODs s e, fn ToggleShowNonLODs s e = ( if showNonLODObjects == true then ( showNonLODObjects = false ) else ( showNonLODObjects = true ) UpdateTreeList() UpdateInfoPanel() ), fn ToggleVisibleOnly = ( if LODEditor.VisibleOnly == true then LODEditor.VisibleOnly = false else LODEditor.VisibleOnly = true LODEditor.UpdateTreeList() LODEditor.UpdateInfoPanel() ), ---------------------------------------------------------------------------------------------------------------- --HELP ---------------------------------------------------------------------------------------------------------------- fn DispatchHelpLink s e = ( LODEditor.HelpLink s e ), --button to open browser fn HelpLink s e = ( ShellLaunch "https://mp.rockstargames.com/index.php/LOD_Editor" "" ), ---------------------------------------------------------------------------------------------------------------- --MILO Help Window ---------------------------------------------------------------------------------------------------------------- fn CreateMILOHelp = ( ), fn DispatchMILOHelp s e = ( LODEditor.MILOHelp s e ), fn MILOHelp s e = ( if (MILOHelpForm != undefined) then ( MILOHelpForm.Close() ) MILOHelpForm = dotNetObject "maxCustomControls.maxForm" MILOHelpForm.Size = dotNetObject "System.Drawing.Size" 490 660 MILOHelpForm.Text = "R* MILO LOD Hierarchies" MILOHelpForm.startPosition = (dotNetClass "System.Windows.Forms.FormStartPosition").manual MILOHelpForm.Location = dotNetObject "system.drawing.point" FormWidth 120 MILOHelpForm.FormBorderStyle = (dotNetClass "System.Windows.Forms.FormBorderStyle").FixedToolWindow MILOHelpForm.BackgroundImage = (dotNetClass "System.Drawing.Image").FromFile("X:\\tools\\dcc\current\\max2009\\scripts\\Payne\\images\\MILO_LODs.jpg") MILOHelpForm.MaximumSize = dotNetObject "System.Drawing.Size" 490 660 MILOHelpForm.ShowModeless() -- MILOHelpForm.ShowModal() ), ---------------------------------------------------------------------------------------------------------------- --USER INTERFACE ---------------------------------------------------------------------------------------------------------------- --BUTTONS ---------------------------------------------------------------------------------------------------------------- fn InitLabel Tag Align = ( local newLabel = dotNetObject "Label" newLabel.Text = Tag newLabel.Font = Font_Calibri newLabel.TextAlign = Align return newLabel ), fn InitButton Tag = ( local newButton = dotNetObject "Button" newButton.FlatStyle = (dotNetClass "System.Windows.Forms.FlatStyle").Standard newButton.Margin = Padding_None newButton.Padding = Padding_None newButton.Text = Tag newButton.Dock = DS_Fill newButton.BackColor = RGB_Transparent newButton.Font = Font_Calibri ToolTip.SetToolTip newButton Tag return newButton ), fn InitButtons = ( --List buttons listButton1 = InitButton "Scene > List" listPanel.Controls.Add listButton1 0 1 dotNet.addEventHandler listButton1 "Click" SceneToList listButton2 = InitButton "List > Scene" listPanel.Controls.Add listButton2 0 2 dotNet.addEventHandler listButton2 "Click" ListToScene listButton3 = InitButton "Clear Selection" listPanel.Controls.Add listButton3 1 3 dotNet.addEventHandler listButton3 "Click" ClearList listButton4 = InitButton "Expand List" listPanel.Controls.Add listButton4 1 1 dotNet.addEventHandler listButton4 "Click" Expand listButton5 = InitButton "Contract List" listPanel.Controls.Add listButton5 1 2 dotNet.addEventHandler listButton5 "Click" Contract listButton6 = InitButton "Select All" listPanel.Controls.Add listButton6 0 3 dotNet.addEventHandler listButton6 "Click" SelectAll --Parenting buttons parentingButton1 = InitButton "Pick Parent" parentingPanel.Controls.Add parentingButton1 0 1 dotNet.addEventHandler parentingButton1 "Click" DispatchParentLod parentingButton2 = InitButton "Remove Parent" parentingPanel.Controls.Add parentingButton2 0 2 dotNet.addEventHandler parentingButton2 "Click" DispatchUnparentLod --Attribute buttons attributeButton1 = InitButton "LOD Distance" attributePanel.Controls.Add attributeButton1 0 1 dotNet.addEventHandler attributeButton1 "Click" DispatchSetLODDistance attributeButton2 = InitButton "TXD" attributePanel.Controls.Add attributeButton2 0 2 dotNet.addEventHandler attributeButton2 "Click" DispatchSetTXD --Splay buttons splayButton1 = InitButton "Along X-Axis" splayPanel.Controls.Add splayButton1 0 1 dotNet.addEventHandler splayButton1 "Click" DispatchSplayX splayButton2 = InitButton "Along Y-Axis" splayPanel.Controls.Add splayButton2 0 2 dotNet.addEventHandler splayButton2 "Click" DispatchSplayY splayButton3 = InitButton "Collapse" splayPanel.Controls.Add splayButton3 0 3 dotNet.addEventHandler splayButton3 "Click" DispatchSplayCollapse ), ---------------------------------------------------------------------------------------------------------------- --INFO PANEL ---------------------------------------------------------------------------------------------------------------- --this panel initialises at the bottom if it isn't present fn InitInfoPanel = ( infoPanel = dotnetobject "Label" infoPanel.ForeColor = (dotNetClass "System.Drawing.Color").FromArgb 0 0 0 infoPanel.Dock = DS_Fill infoPanel.TextAlign = TA_Left infoPanel.Name = "infoPanel" infoPanel.Font = Font_Calibri lodListTable.Controls.Add infoPanel 0 2 case (currentMode) of ( "scene": infoPanel.BackColor = RGB_Pink "drawable": infoPanel.BackColor = RGB_BabyBlue ) ), ---------------------------------------------------------------------------------------------------------------- --ACTION PANEL ---------------------------------------------------------------------------------------------------------------- --this table is used for the txd and lod distance panels fn InitActionPanel = ( actionPanel = dotNetObject "TableLayoutPanel" actionPanel.Dock = DS_Fill actionPanel.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").none actionPanel.ColumnCount = 3 actionPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 60) actionPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 25) actionPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 15) actionPanel.RowCount = 1 actionPanel.RowStyles.Add (RS_dotNetObject.rowStyleObject "percent" 100) lodListTable.Controls.Add actionPanel 0 2 ), ---------------------------------------------------------------------------------------------------------------- --INIT UI FUNCTIONS ---------------------------------------------------------------------------------------------------------------- --TEXTURE DICTIONARY PANEL ---------------------------------------------------------------------------------------------------------------- fn InitTxdPanel = ( InitActionPanel() actionPanel.Name = "txdPanel" --get the txd of the first selected object as the default value newTxd = getAttr selectedObjects[1] (getattrindex "Gta Object" "TXD") txdLabel = dotNetObject "Label" txdLabel.Text = "Set new texture dictionary:" txdLabel.Dock = DS_Fill txdLabel.TextAlign = CA_Center actionPanel.Controls.Add txdLabel txdInput = dotNetObject "TextBox" txdInput.MaxLength = 12 txdInput.Dock = DS_Fill txdInput.Text = newTxd actionPanel.Controls.Add txdInput txdButton = dotNetObject "Button" txdButton.Dock = DS_Fill -- txdButton.BackColor = RGB_YellowOrange txdButton.BackColor = RGB_Transparent txdButton.ForeColor = textColour txdButton.Text = "Apply" actionPanel.Controls.Add txdButton dotNet.addEventHandler txdButton "Click" ApplyTxd ), ---------------------------------------------------------------------------------------------------------------- --LOD DISTANCE PANEL ---------------------------------------------------------------------------------------------------------------- --this panel initialises at the bottom when the lod distance button is triggered fn InitLodDistancePanel = ( InitActionPanel() actionPanel.Name = "lodDistancePanel" --get the lod distance of the first selected object as the default value newLodDistance = getAttr selectedObjects[1] (getattrindex "Gta Object" "LOD distance") lodDistanceLabel = dotNetObject "Label" lodDistanceLabel.Text = "Set new LOD distance:" lodDistanceLabel.Dock = DS_Fill lodDistanceLabel.TextAlign =CA_Center actionPanel.Controls.Add lodDistanceLabel lodDistanceInput = dotNetObject "NumericUpDown" lodDistanceInput.Minimum = 0 lodDistanceInput.Maximum = 9999 lodDistanceInput.Value = newLodDistance lodDistanceInput.Increment = 10 lodDistanceInput.Dock = DS_Fill actionPanel.Controls.Add lodDistanceInput lodDistanceButton = dotNetObject "Button" lodDistanceButton.Dock = DS_Fill lodDistanceButton.BackColor = RGB_Transparent lodDistanceButton.ForeColor = textColour lodDistanceButton.Text = "Apply" actionPanel.Controls.Add lodDistanceButton dotNet.addEventHandler lodDistanceButton "Click" DispatchApplyLD ), ---------------------------------------------------------------------------------------------------------------- --TREE LIST ---------------------------------------------------------------------------------------------------------------- fn InitTreeList = --function sets up the initial tree list when the script is triggered --at the moment, both the scene lod tab and the drawable lod tab can use this same control --only one tab at a time can store the control ( treeListPanel = dotNetObject "DevExpress.XtraTreeList.TreeList" treeListPanel.Dock = DS_Fill treeListPanel.OptionsSelection.MultiSelect = true treeListPanel.OptionsView.ShowRoot = true treeListPanel.OptionsView.ShowIndicator = false treeListPanel.OptionsBehavior.Editable = false -- set to true on right click... treeListPanel.OptionsView.EnableAppearanceEvenRow = true --colours the rows alternately treeListPanel.OptionsView.EnableAppearanceOddRow = true --colours the rows alternately treeListPanel.Appearance.FocusedCell.BackColor = RGB_CornflowerBlue treeListPanel.BeginUpdate() --first column for tree nodes treeListPanel.Columns.Add() treeListPanel.Columns.Item[0].Caption = "Object Tree" treeListPanel.Columns.Item[0].Name = "tree" treeListPanel.Columns.Item[0].FieldName = "tree" treeListPanel.Columns.Item[0].VisibleIndex = 0 treeListPanel.Columns.Item[0].Width = 140 treeListPanel.columns.item[0].sortorder = (dotNetClass "SortOrder").ascending --second column for LOD distances treeListPanel.Columns.Add() treeListPanel.Columns.Item[1].Caption = "LOD Distance" treeListPanel.Columns.Item[1].Name = "distance" treeListPanel.Columns.Item[1].VisibleIndex = 1 treeListPanel.Columns.Item[1].Width = 25 --third column for TXD treeListPanel.Columns.Add() treeListPanel.Columns.Item[2].Caption = "TXD" treeListPanel.Columns.Item[2].Name = "TXD" treeListPanel.Columns.Item[2].VisibleIndex = 2 treeListPanel.Columns.Item[2].Width = 35 treeListPanel.EndUpdate() ), ---------------------------------------------------------------------------------------------------------------- --FORM, TABLE AND TABS ---------------------------------------------------------------------------------------------------------------- fn InitLodList = ( -- Form Setup UpdateObjectList() formHeight = borderHeight + (objectList.count * treeListRowHeight) + buttonPanelHeight + infoPanelHeight lodListForm = RS_dotNetUI.maxForm "LodTL" text:"RS LOD Editor - Scene LODs" size:[formWidth,formHeight] min:[320,100] max:[800,1150] lodListForm.sizeGripStyle = (dotNetClass "SizeGripStyle").Show lodListForm.StartPosition = lodListForm.StartPosition.Manual lodListForm.Location.X = 0 lodListForm.Location.Y = 120 lodListForm.FormBorderStyle = lodListForm.FormBorderStyle.SizableToolWindow dotNet.AddEventHandler lodListForm "Load" LoadIniFile dotNet.AddEventHandler lodListForm "Closing" SaveIniFile ToolTip = dotnetobject "ToolTip" --Table lodListTable = dotNetObject "TableLayoutPanel" lodListTable.Dock = DS_Fill lodListTable.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").single lodListTable.ColumnCount = 1 lodListTable.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 100) lodListTable.RowCount = 3 lodListTable.RowStyles.Add (RS_dotNetObject.rowStyleObject "percent" 100) lodListTable.RowStyles.Add (RS_dotNetObject.rowStyleObject "absolute" buttonPanelHeight) lodListTable.RowStyles.Add (RS_dotNetObject.rowStyleObject "absolute" infoPanelHeight) lodListForm.Controls.Add lodListTable --Tab Panel tabPanel = dotNetObject "System.Windows.Forms.TabControl" tabPanel.Dock = DS_Fill tabPanel.Font = Font_Calibri tab1 = dotNetObject "System.Windows.Forms.TabPage" tab1.text = "Scene LODs" tab2 = dotNetObject "System.Windows.Forms.TabPage" tab2.text = "Drawable LODs" tab3 = dotNetObject "System.Windows.Forms.TabPage" tab3.text = "Tree LODs" dotNet.AddEventHandler tabPanel "SelectedIndexChanged" DispatchTabClicked tabPanel.Controls.Add tab1 tabPanel.Controls.Add tab2 tabPanel.Controls.Add tab3 lodListTable.Controls.Add tabPanel 0 0 case currentMode of ( "scene": tabPanel.SelectTab tab1 "drawable": tabPanel.SelectTab tab2 "tree": tabPanel.SelectTab tab3 ) --Tree List View InitTreeList() --sets up treeListPanel with the XtraTreeList case currentMode of ( "scene": tab1.Controls.Add treeListPanel --set up tab1 with the tree list "drawable": tab2.Controls.Add treeListPanel "tree": tab3.Controls.Add treeListPanel ) --need to add the treeListPanel control to the other tab should that be selected UpdateTreeList() dotNet.AddEventHandler treeListPanel "Click" DispatchSetLodParent --behaviour to track list selection of lod parent --Button Panel local titleHeight = 12 buttonPanel = dotNetObject "TableLayoutPanel" buttonPanel.Dock = DS_Fill buttonPanel.BackColor = RGB_LightGrey buttonPanel.ForeColor = textColour buttonPanel.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").none buttonPanel.ColumnCount = 1 buttonPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 100) buttonPanel.RowCount = 2 buttonPanel.RowStyles.Add (RS_dotNetObject.rowStyleObject "percent" 100) buttonPanel.RowStyles.Add (RS_dotNetObject.rowStyleObject "absolute" 25) lodListTable.Controls.Add buttonPanel 0 1 --Button Strip: this is the top section of the button panel where the main function buttons are buttonStrip = dotNetObject "TableLayoutPanel" buttonStrip.Dock = DS_Fill buttonStrip.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").Inset buttonStrip.ColumnCount = 4 buttonStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 40) buttonStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 20) buttonStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 20) buttonStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 20) buttonStrip.RowCount = 1 buttonStrip.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 100) buttonPanel.Controls.Add buttonStrip 0 0 --List Buttons: these control the viewing of objects in the treelist listPanel = dotNetObject"TableLayoutPanel" listPanel.Dock = DS_Fill listPanel.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").none listPanel.ColumnCount = 2 listPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 50) listPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 50) listPanel.RowCount = 4 listPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "absolute" titleHeight) listPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) listPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) listPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) buttonStrip.Controls.Add listPanel 0 0 listLabel = dotNetObject "Label" listLabel.Text = "List Commands" listLabel.Font = Font_Small listLabel.Dock = DS_Fill listLabel.TextAlign = listLabel.TextAlign.MiddleCenter listPanel.Controls.Add listLabel 0 0 listPanel.SetColumnSpan listLabel 2 --Parenting Buttons: control the LOD hierarchies parentingPanel = dotNetObject "TableLayoutPanel" parentingPanel.Dock = DS_Fill parentingPanel.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").none parentingPanel.ColumnCount = 1 parentingPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 100) parentingPanel.RowCount = 4 parentingPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "absolute" titleHeight) parentingPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) parentingPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) parentingPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) buttonStrip.Controls.Add parentingPanel 1 0 parentingLabel = dotNetObject "Label" parentingLabel.Text = "LOD Parenting" parentingLabel.Font = Font_Small parentingLabel.TextAlign = listLabel.TextAlign.MiddleCenter parentingPanel.Controls.Add parentingLabel 0 0 --Attribute Buttons: control the LOD hierarchies attributePanel = dotNetObject "TableLayoutPanel" attributePanel.Dock = DS_Fill attributePanel.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").none attributePanel.ColumnCount = 1 attributePanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 100) attributePanel.RowCount = 4 attributePanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "absolute" titleHeight) attributePanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) attributePanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) attributePanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) buttonStrip.Controls.Add attributePanel 2 0 attributeLabel = dotNetObject "Label" attributeLabel.Text = "Set Attributes" attributeLabel.Font = Font_Small attributeLabel.TextAlign = listLabel.TextAlign.MiddleCenter attributePanel.Controls.Add attributeLabel 0 0 --Splay Buttons: these control the position of the LOD objects in the scene, splaying them for easier viewing splayPanel = dotNetObject "TableLayoutPanel" splayPanel.Dock = DS_Fill splayPanel.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").none splayPanel.ColumnCount = 1 splayPanel.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 100) splayPanel.RowCount = 4 splayPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "absolute" titleHeight) splayPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) splayPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) splayPanel.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 33) buttonStrip.Controls.Add splayPanel 3 0 splayLabel = InitLabel "Expand LODs" TA_Center splayPanel.Controls.Add splayLabel 0 0 InitButtons() --the help strip contains checkboxes and help controls helpStrip = dotNetObject "TableLayoutPanel" helpStrip.Dock = DS_Fill helpStrip.CellBorderStyle = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").none helpStrip.ColumnCount = 4 helpStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "absolute" 110) helpStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "absolute" 90) helpStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "percent" 100) helpStrip.ColumnStyles.Add (RS_dotNetObject.columnStyleObject "absolute" 40) helpStrip.RowCount = 1 helpStrip.RowStyles.Add (RS_dotNetObject.RowStyleObject "percent" 100) buttonPanel.Controls.Add helpStrip 1 0 showNonLODsCheckBox = dotNetObject "CheckBox" showNonLODsCheckBox.Dock = DS_Fill showNonLODsCheckBox.Text = "Show non-LODs" showNonLODsCheckBox.Font = Font_Calibri showNonLODsCheckBox.Checked = showNonLODObjects dotNet.addEventHandler showNonLODsCheckBox "CheckedChanged" DispatchToggleShowNonLODs helpStrip.Controls.Add showNonLODsCheckBox 0 0 showVisibleOnlyCheckBox = dotNetObject "CheckBox" showVisibleOnlyCheckBox.Dock = DS_Fill showVisibleOnlyCheckBox.Text = "Visible Only" showVisibleOnlyCheckBox.Font = Font_Calibri showVisibleOnlyCheckBox.Checked = VisibleOnly dotNet.addEventHandler showVisibleOnlyCheckBox "CheckedChanged" ToggleVisibleOnly helpStrip.Controls.Add showVisibleOnlyCheckBox 1 0 MILODiagram = dotNetObject "LinkLabel" MILODiagram.Dock = DS_Fill MILODiagram.Text = "MILO LOD diagram" MILODiagram.Font = Font_Calibri MILODiagram.LinkBehavior = (dotNetClass "System.Windows.Forms.LinkBehavior").NeverUnderline MILODiagram.TextAlign = MILODiagram.TextAlign.MiddleCenter dotNet.addEventHandler MILODiagram "LinkClicked" DispatchMILOHelp helpStrip.Controls.Add MILODiagram 2 0 helpLabel = dotNetObject "Linklabel" helpLabel.Dock = DS_Fill helpLabel.Text = "help" helpLabel.Font = Font_Calibri helpLabel.LinkBehavior = (dotNetClass "System.Windows.Forms.LinkBehavior").NeverUnderline helpLabel.TextAlign = helpLabel.TextAlign.MiddleRight dotNet.addEventHandler helpLabel "LinkClicked" DispatchHelpLink helpStrip.Controls.Add helpLabel 3 0 --informationPanel InitInfoPanel() UpdateInfoPanel() --draw form lodListForm.showModeless() lodListForm --setup a selection callback to select the object in the LodList automatically. callbacks.removeScripts id:#LODEditor --callbacks.addScript #selectionSetChanged "LODEditor.UpdateListNodesFromSelection()" id:#LODEditor ) ) ---------------------------------------------------------------------------------------------------------------- --STARTUP OPERATIONS ---------------------------------------------------------------------------------------------------------------- try( if LODEditor != undefined then ( LODEditor.lodListForm.close() ) ) catch() LODEditor = LodListStruct() LODEditor.InitLodList() ---------------------------------------------------------------------------------------------------------------- --END OF SCRIPT ----------------------------------------------------------------------------------------------------------------