-- Fragment tuning glabals filein "rockstar/export/settings.ms" filein "pipeline/helpers/physics.ms" filein "pipeline/util/xml.ms" global gFragEditMode = #default global gCpyChildren = false global gReloadFileMode = true global gPrvntCollSel = true global copyPasteErrors = "" global gPresetElem = undefined fn getPresetNames = ( local presetNames = #("none") local xmlDoc = XmlDocument() xmlDoc.load (RsProjectGetCommonDir() + "/data/fragment.xml") xmlDoc.ParseIntoMaxscripthierarchy() gPresetElem = xmlDoc.maxXmlObject.children[2].findChild withName:"BreakPresets" if undefined!=gPresetElem then for presetItem in gPresetElem.children do ( local nameChild = presetItem.children[1] append presetNames nameChild.textValue ) return presetNames ) fn updatePresetProps theRollout presetName groupName = ( if undefined==theRollout or undefined==gPresetElem then return false local thePreset = undefined for presetItem in gPresetElem.children while undefined==thePreset do ( local nameChild = presetItem.children[1] if nameChild.textValue==presetName then ( thePreset = presetItem ) ) if undefined!=thePreset then ( for presetProp in thePreset.children do ( -- first the control local theControl = undefined for ctrl in theRollout.controls while undefined==theControl do ( if 0!=findItem #(GroupStartControl, GroupEndControl) (classof ctrl) then continue if (0==stricmp ctrl.name presetProp.tagname) then theControl = ctrl ) if undefined!=theControl then ( try ( theControl.value = (presetProp.attrs.item "value") as float case classof theControl of ( SpinnerControl:theControl.changed theControl.value CheckboxControl:theControl.changed theControl.value ) ) catch ( print (getCurrentException()) ) ) ) ) ) struct sRsFragAttribLUT ( ------------------------------------------------------------------------ -- FRAG ATTRIBS ------------------------------------------------------------------------ -- identifier --standard export --readable name -default value selection-data change-callback RsFragAttribLUT = #( #("minMoveForce", true, "Min force to move me", 0), #("artAssetID", true, undefined, -1), #("attachBottomEnd", true, undefined, 0), #("estimatedCacheSize", true, undefined, 0), #("estimatedArticulatedCacheSize", true, undefined, 0), #("cloneBoundPartsInCache", true, undefined, 0), #("unbrokenCGOffset", true, undefined, #(0,0,0)), #("unbrokenElasticity", true, "Unbroken elasticity", 0), #("gravityFactor", true, undefined, 1.0), #("buoyancyFactor", true, undefined, 1.0), #("breakImpulseSourceTypeFlags", true, undefined, 0), #("dampingLinearC", true, undefined, #(0.02,0.02,0.02)), #("dampingLinearV", true, undefined, #(0.02,0.02,0.02)), #("dampingLinearV2", true, undefined, #(0.01,0.01,0.01)), #("dampingAngularC", true, undefined, #(0.02,0.02,0.02)), #("dampingAngularV", true, undefined, #(0.02,0.02,0.02)), #("dampingAngularV2", true, undefined, #(0.01,0.01,0.01)), #("selfCollisionCount", true, undefined, 0) ), ------------------------------------------------------------------------ -- GROUP ATTRIBS ------------------------------------------------------------------------ RsGroupAttribLUT = #( #("strength", true, "Break Strength", 100.0), #("forceTransmissionScaleUp", true, "ForceTransmission Up", 0.25), #("forceTransmissionScaleDown", true, "ForceTransmission Down", 0.25), #("jointStiffness", true, undefined, 0), #("minSoftAngle1", true, undefined, -1), #("maxSoftAngle1", true, undefined, 1), #("maxSoftAngle2", true, undefined, 1), #("maxSoftAngle3", true, undefined, 1), #("rotationSpeed", true, undefined, 0), #("rotationStrength", true, undefined, 0), #("restoringStrength", true, undefined, 0), #("restoringMaxTorque", true, undefined, 0), #("latchStrength", true, "Contraint latch strength", 0), #("disappearsWhenDead", true, "Disappear when dead", false), #("madeOfGlass", true, "Made of glass", false), #("minDamageForce", true, "MinDamage force", 0), #("damageHealth", true, "Intial damage health", 0), #("weaponHealth", true, "Weapon health", 0), #("weaponScale", true, "Weapons", 1.0), #("vehicleScale", true, "Vehicles", 1.0), #("pedScale", true, "Peds", 1.0), #("ragdollScale", true, "Ragdolls", 1.0), #("explosionScale", true, "Explosions", 1.0), #("objectScale", true, "Objects", 1.0), #("preset", true, "Preset", "none", ::getPresetNames, "updatePresetProps") ), ------------------------------------------------------------------------ -- CHILD ATTRIBS ------------------------------------------------------------------------ RsChildAttribLUT = #( #("pristineMass", true, "Pristine mass", calculateMass), #("damagedMass", true, "Damaged mass", calculateMass) ), RsAttribLUT = RsFragAttribLUT + RsGroupAttribLUT + RsChildAttribLUT, -- accessors fn getDefault key = ( for tuple in RsAttribLUT do ( if (tuple[1]==key) then return tuple[3] ) return undefined ), fn getDefaultExport key = ( for tuple in RsAttribLUT do ( if (tuple[1]==key) then return tuple[2] ) return false ), fn getDataFunc key = ( for tuple in RsAttribLUT do ( if (tuple[1]==key) then return tuple[5] ) return undefined ), fn getCallback key = ( for tuple in RsAttribLUT do ( if (tuple[1]==key) then return tuple[6] ) return undefined ), fn getValue key = ( if gFragEditMode==#verbose then return key for tuple in RsAttribLUT do ( if (tuple[1]==key) then ( return tuple[3] ) ) return undefined ) ) global gRsFragAttribLUT = sRsFragAttribLUT() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- data holding/processing structs ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ struct sFragGroup ( groupName = "fragments", parentGroup = undefined, groupValues = #(), groupArray = #(), childArray = #() ) struct sFragChild ( childName = "dummyname", parentGroup = undefined, childValues = #() ) #typeBool #typeScalar #typeArray #typeString #typeScope struct sAttrValue ( attrName, attrValue, attrType, attrExportFlag = false, fn copyme = ( local newOne = sAttrValue() newOne.attrName = attrName newOne.attrValue = attrValue newOne.attrType = attrType newOne.attrExportFlag = attrExportFlag return newOne ) ) struct sFragTuner ( tuneArray = #(), currFrag = 0.0, currFragName = "initial", currFragRoot = sFragGroup(), currFragGroup = currFragRoot, currObj, copyFragHier = undefined, childObj, groupObj, fragObj, isFrag, isGroup, currHirarchyErrors = "", -- fn RsTrimAndChopLine idx currLine = ( -- filteredLine = filterstring currLine " " -- -- if filteredLine[idx] != undefined then ( -- --currTrimLine = RsGetTrimParamFromString filteredLine[idx] -- currLine = RsRemoveSpacesFromString filteredLine[idx] -- currLine = RsRemoveTabsFromString currLine -- ) -- -- return currLine -- ), -- fn RsGetTrimParamFromString currLine = ( -- -- currLine = RsRemoveSpacesFromString currLine -- currLine = RsRemoveTabsFromString currLine -- -- return currLine -- ), fn SetReloadFileMode val = ( gReloadFileMode = val if undefined!=::RsButtonRoll then ( ::RsButtonRoll.checkReloadFile.checked = val ) ), fn ResetFragTune = ( currHirarchyErrors = undefined currFragRoot = sFragGroup() currFragGroup = currFragRoot currFragname = "" SetReloadFileMode true ), fn GetTunePath = ( RsConfigGetAssetsDir() + "fragments/" + currFragName + ".tune" ), fn RetrieveIndentation obj = ( indentation = 1 while classof(obj.parent) != UndefinedClass do ( obj = obj.parent indentation = indentation + 1 ) return indentation + 1 ), fn isObjInCurrHierarchy obj = ( do ( if (matchpattern obj.name pattern:("*"+currFragName+"*")) then return true obj = obj.parent ) while undefined!=obj return false ), -------------------------------------------------------------------------- -- Extract hierarchical information from selected object -------------------------------------------------------------------------- fn DetermineObject = ( isFrag = false isGroup = false sel = selection as array obj = sel[1] if obj != Undefined then ( if "GtaObject"==(getattrclass obj) or Point == (classof obj) and obj.parent != undefined then ( groupObj = obj isGroup = true groupName = obj.name ) else if "GtaObject"==(getattrclass obj) then ( fragIndex = getattrindex "Gta Object" "Is Fragment" if getattr obj fragIndex == true then ( isFrag = true isGroup = false ) ) -- get the absolute parent while classof(obj.parent) != UndefinedClass do ( obj = obj.parent ) fragObjName = tolower obj.name -- Get rid of any of the skinned helper nodes if matchPattern fragObjName pattern:"dummy_*" then fragObjName = substring fragObjName 7 fragObjName.count if matchPattern fragObjName pattern:"mover_*" then fragObjName = substring fragObjName 7 fragObjName.count if matchPattern fragObjName pattern:"root_*" then fragObjName = substring fragObjName 7 fragObjName.count if matchPattern fragObjName pattern:"main_*" then fragObjName = substring fragObjName 7 fragObjName.count --getting rid of suffix local fragStringIndex = findString fragObjName "_frag_" if fragStringIndex!=undefined then fragObjName = substring fragObjName 1 (fragStringIndex-1) currFragName = fragObjName ) ), fn dumpGroup theGroup theFile indentDepth isRoot = ( local indent = "" local borderindent = "" local i=0 while i\n") to:theFile ) ) ) for c in theGroup.childArray do ( format (indent+"child\t\t\t\t{\n") to:theFile local childIndent = "" i=0 while i\n") to:theFile ) ) ) format (indent+"}\n") to:theFile ) for grp in theGroup.groupArray do ( dumpGroup grp theFile (indentDepth+1) false ) format (borderindent+"}\n") to:theFile ), fn WriteOutAttrib tunePath = ( sel = selection as array try ( if tunePath != "" then ( tuneFile = openFile tunePath mode:"w+" gRsPerforce.add_or_edit tunePath if tuneFile != undefined and sel != undefined then ( dumpGroup currFragRoot tuneFile 0 true close tuneFile SetReloadFileMode true return true ) messagebox ("Error writing to " + tunePath + ". Check to see file is writable.") close tuneFile return false --commandLine = "attrib +r " + tunePath --doscommand commandLine ) messagebox "no valid path" close tuneFile ) catch( messagebox ("Fragment save error: "+getCurrentException()) ) return false ), -------------------------------------------------------------------------------------------------------------------------------------------------- -- Parsing attributes -------------------------------------------------------------------------------------------------------------------------------------------------- fn ParseNextSubString str = ( if str[1]=="<" then return #(substring str 2 (str.count-2)) --find first intersector local intersector = " " local firstWhite = findString str " " local firstTab = findString str "\t" if firstWhite==undefined or (firstTab!=undefined and firstTab*" then -- ended = true -- deleteItem back (i=1) -- ) -- found = true -- ) return back ), fn ParseNameAndValueFromString str = ( while str[1]=="\t" or str[1]==" " do str = substring str 2 str.count back = sAttrValue() -- parse name local nameParseStep = ParseNextSubString str back.attrName = nameParseStep[1] -- parse Value back.attrValue = undefined back.attrType = #typeScope if nameParseStep[2]!=undefined then ( local valueParseStep = ParseNextSubString (substring str (nameParseStep[1].count+2) str.count) if valueParseStep.count>0 then ( if (valueParseStep[1] as number)!=undefined then ( if valueParseStep.count>1 then ( back.attrType = #typeArray back.attrValue = valueParseStep ) else ( back.attrType = #typeScalar back.attrValue = valueParseStep[1] ) ) else ( if valueParseStep.count>1 and valueParseStep[1]=="all" then -- masses on children have the all suffix ( back.attrType = #typeScalar back.attrValue = valueParseStep[2] ) else ( back.attrType = #typeString back.attrValue = valueParseStep[1] ) ) ) ) else ( back.attrType = #typeBool back.attrValue =true ) back.attrExportFlag = true return back ), -------------------------------------------------------------------------- -- Stream in tune data -------------------------------------------------------------------------- fn ReadInAttrib tunePath = ( local currScope = #("root") sel = selection as array currObj = sel[1] tuneFile = openFile tunePath local addedGroupValues = #() if tuneFile != undefined then ( while eof tuneFile == false do ( currentLine = readLine tuneFile -- remove trailing spaces local nameAndValueTuple = ParseNameAndValueFromString currentLine prefix = nameAndValueTuple.attrName value = nameAndValueTuple.attrValue if prefix == "}" then ( if currScope.count>0 then ( if currScope[currScope.count]=="groupScope" then ( for groupVal in gRsFragAttribLUT.RsGroupAttribLUT do ( if 0== (for v in currFragGroup.groupValues where v.attrName==groupVal[1] collect v).count then ( local defaultNameAndValueTuple = sAttrValue attrName:groupVal[1] attrValue:groupVal[4] attrExportFlag:false if (classof groupVal[4] == number) then defaultNameAndValueTuple.attrType = #typeScalar else if (classof groupVal[4] == booleanclass) then defaultNameAndValueTuple.attrType = #typeBool else if (classof groupVal[4] == array) then defaultNameAndValueTuple.attrType = #typeArray else if (classof groupVal[4] == string) then defaultNameAndValueTuple.attrType = #typeString append currFragGroup.groupValues defaultNameAndValueTuple ) ) addedGroupValues = #() currFragGroup = currFragGroup.parentGroup ) deleteItem currScope currScope.count ) -- try( print ("< "+currScope[currScope.count]+", "+(currScope.count as string)) )catch() continue ) else if prefix == "group" then ( groupName = value append currScope "groupScope" local formerFragGroup = currFragGroup currFragGroup = sFragGroup() currFragGroup.parentGroup = formerFragGroup currFragGroup.groupName = groupName append formerFragGroup.groupArray currFragGroup ) else if prefix == "child" then ( currChild = sFragChild() append currScope "childScope" currentLine = readLine tuneFile local nameAndValueTuple = ParseNameAndValueFromString currentLine append currChild.childValues nameAndValueTuple currentLine = readLine tuneFile local nameAndValueTuple = ParseNameAndValueFromString currentLine append currChild.childValues nameAndValueTuple append currFragGroup.childArray currChild ) else if not matchPattern prefix pattern:"fragments" and not matchPattern prefix pattern:"{" then ( -- tis a value append addedGroupValues nameAndValueTuple.attrName append currFragGroup.groupValues nameAndValueTuple ) ) close tuneFile return true ) else return false ), -------------------------------------------------------------------------- -- Create the tune hierarchy to show from actual scene data -------------------------------------------------------------------------- fn ParseNameAndValueFromDefault val obj = ( back = sAttrValue() back.attrName = val[1] back.attrValue = val[4] if MAXScriptFunction == (classof back.attrValue) then back.attrValue = back.attrValue(obj) back.attrType = \ if (classof back.attrValue) == BooleanClass then #typeBool \ else if (superClassOf back.attrValue)==number then #typeScalar \ else if (classof back.attrValue)==array then #typeArray \ else #typeString back.attrExportFlag = val[2] return back ), fn createGroup sceneNode = ( local formerFragGroup = currFragGroup currFragGroup = sFragGroup() currFragGroup.parentGroup = formerFragGroup currFragGroup.groupName = sceneNode.name if matchPattern currFragGroup.groupName pattern:"*_frag_*" then ( local index = findString currFragGroup.groupName "_frag_" currFragGroup.groupName = replace currFragGroup.groupName index 6 "" ) -- print currFragGroup.groupName if undefined!=formerFragGroup then ( append formerFragGroup.groupArray currFragGroup -- print formerFragGroup.groupName -- print formerFragGroup.groupArray.count ) -- create attributes for val in gRsFragAttribLUT.RsGroupAttribLUT do ( local nameAndValueTuple = ParseNameAndValueFromDefault val sceneNode append currFragGroup.groupValues nameAndValueTuple ) -- bounds data local childBounds = for c in sceneNode.children where "Gta Collision"==(getAttrClass c) collect c for b in childBounds do ( currChild = sFragChild() for val in gRsFragAttribLUT.RsChildAttribLUT do ( local nameAndValueTuple = ParseNameAndValueFromDefault val b append currChild.childValues nameAndValueTuple ) append currFragGroup.childArray currChild ) -- child data local childMeshes = for c in sceneNode.children where "Gta Object"==(getAttrClass c) or Point == (classof c) collect c for c in childMeshes do ( createGroup c ) if undefined!=formerFragGroup then currFragGroup = formerFragGroup return true ), fn CreateHierarachyFromNodes saveImmediately:false overWriteIfExistent:false = ( local obj = selection[1] if obj == undefined then ( messagebox "Select an object!" return false ) local fragName = obj.name local fragNode = getNodeByName (obj.name+"_frag_") local foundRootFragmentBone = false if undefined!=fragNode then ( obj = fragNode if (undefined != obj.modifiers[#Skin]) then ( rootBone = rexGetSkinRootBone obj if rootBone != undefined then ( obj = rootBone local mainRootBone = obj.children[1] if (mainRootBone != undefined and matchPattern mainRootBone.name pattern:"main_*") then ( obj = mainRootBone.children[1] if (obj == undefined) then messagebox "Frag is not setup correctly with skinning!" else foundRootFragmentBone = true ) ) ) ) -- If we found the root fragment bone then we don't need to try to find the parent in the hierarchy if not foundRootFragmentBone then ( while obj.parent != undefined do obj = obj.parent ) select obj local tunePath = GetTunePath() if doesFileExist tunePath then ( print ("file already exists:"+tunePath) if not overWriteIfExistent then return false ) ResetFragTune() DetermineObject() currFragGroup = sFragGroup() currFragGroup.parentGroup = undefined currFragGroup.groupName = "root" currFragRoot = currFragGroup for val in gRsFragAttribLUT.RsFragAttribLUT do ( local nameAndValueTuple = ParseNameAndValueFromDefault val fragNode append currFragGroup.groupValues nameAndValueTuple ) print "create hierarchy" if not (createGroup obj) then return false SetReloadFileMode false if saveImmediately then ::gFragTuner.WriteFragTuneData() return true ), -------------------------------------------------------------------------- -- Hierarchy manipulation -------------------------------------------------------------------------- fn findGroup root grpName = ( if matchPattern grpName pattern:"*_frag_*" then ( grpName = substring grpName 1 ((findString grpName "_frag_")-1) ) -- if currFragname == grpName then return currFragRoot if root.groupName==grpName then return root for grp in root.groupArray do ( local back = findGroup grp grpName if back != undefined then return back ) return undefined ), fn setGroupValueDirect grp valName newVal arrayIndex:undefined setAllChildren:false = ( if grp!=undefined then ( for val in grp.groupValues do ( if val.attrName==valname then ( -- print (val.attrName+" set to "+newVal as string) if arrayindex!=undefined then ( val.attrValue[arrayIndex] = newVal ) else if -- Array==(classof val.attrValue) and Array==(classof newVal) then ( val.attrValue = deepCopy newVal ) else ( val.attrValue = newVal ) ) ) if setAllChildren then for g in grp.groupArray do setGroupValueDirect g valName newVal arrayIndex:arrayIndex setAllChildren:setAllChildren ) ), fn setGroupValue grpName valName newVal arrayIndex setAllChildren:false = ( local grp = findGroup currFragRoot grpName setGroupValueDirect grp valName newVal arrayIndex:arrayIndex ), fn setGroupExportFlag grpName valName newVal = ( local grp = findGroup currFragRoot grpName if grp!=undefined then ( for val in grp.groupValues do if val.attrName==valname then ( print (val.attrName+" exportflag set to "+newVal as string) val.attrExportFlag = newVal ) ) ), fn setChildValueDirect grp childIndex valName newVal = ( childIndex = childIndex as integer if grp!=undefined and grp.childArray.count>=childIndex then ( for val in grp.childArray[childIndex].childValues do if val.attrName==valname then val.attrValue = newVal ) ), fn setChildValue grpName childIndex valName newVal = ( local grp = findGroup currFragRoot grpName setChildValueDirect grp childIndex valName newVal ), fn checkGroup theGroup sceneNode = ( -- if theGroup.groupName=="fragments" then -- if theGroup.childArray.count != sceneNode then local childMeshes = for c in sceneNode.children where "Gta Object"==(getAttrClass c) or Point == (classof c) collect c local childBounds = for c in sceneNode.children where "Gta Collision"==(getAttrClass c) collect c if theGroup.groupArray.count != childMeshes.count then ( currHirarchyErrors = (sceneNode.name+"'s child mesh count "+(childMeshes.count as string)+" not equal to "+(theGroup.groupName)+"'s group count "+(theGroup.groupArray.count as string)) return false ) if theGroup.childArray.count != childBounds.count then ( currHirarchyErrors = (sceneNode.name+"'s child collision count "+(childBounds.count as string)+" not equal to "+(theGroup.groupName)+"'s child count "+(theGroup.childArray.count as string)) return false ) for grp in theGroup.groupArray do ( if not (checkGroup grp (getNodeByName grp.groupName)) then return false ) return true ), fn checkFragHirarchy obj = ( local fragNode = getNodeByName (obj.name+"_frag_") if undefined!=fragNode then obj = fragNode return (checkGroup currFragGroup obj) ), fn PopulateFragTuner = ( if $selection.count<=0 then ( ::ResetRollouts undefined return false ) local isSameFragment = false local obj = selection[1] if gPrvntCollSel and "Gta Collision"==(getAttrClass obj) and undefined!=obj.parent then ( obj = obj.parent select obj return false ) if undefined!=obj then ( currFragGroup = findGroup currFragRoot obj.name isSameFragment = undefined!=currFragGroup ) local isHierarchyValid = true if gReloadFileMode or not isSameFragment then ( ResetFragTune() -- create new DetermineObject() -- currFragRoot.groupName = currFragName local tunePath = GetTunePath() if ReadInAttrib tunePath then ( currFragGroup = findGroup currFragRoot obj.name ) else isHierarchyValid = false ) if isHierarchyValid then ( if undefined==currFragGroup then currFragGroup = currFragRoot checkFragHirarchy obj if undefined!=ResetRollouts then ResetRollouts currFragGroup.groupName ) else ( if undefined!=ResetRollouts then ResetRollouts undefined ) return true ), fn RefreshUI = ( if undefined!=::RsTreeViewRoll then ::RsTreeViewRoll.reset() local selectionValid = PopulateFragTuner() if selectionValid and undefined!=::RsTreeViewRoll then ( ::RsTreeViewRoll.updateTV currFragRoot currFragGroup ::RsFragRoll.setFragName currFragname ) ), fn WriteFragTuneData = ( tunePath = GetTunePath() WriteOutAttrib tunePath ), ------------------------------------------------------------------------------------- -- copy hierarchy ------------------------------------------------------------------------------------- fn copyGroup theGroup = ( local newGroup = sFragGroup() for val in theGroup.groupValues where val.attrExportFlag do ( append newGroup.groupValues (val.copyMe()) ) for c in theGroup.childArray do ( local newChild = sFragChild() append newGroup.childArray newChild for val in c.childvalues where val.attrExportFlag do ( append newChild.childvalues (val.copyMe()) ) ) if gCpyChildren then for grp in theGroup.groupArray do ( append newGroup.groupArray (copyGroup grp) ) return newGroup ), fn copyFragHierarchy = ( copyFragHier = copyGroup (if gCpyChildren then currFragRoot else currFragGroup) ), -------------------------------------------------------------------------- fn pasteGroup fromGroup toGroup= ( for fromVal in fromGroup.groupValues do ( setGroupValueDirect toGroup fromVal.attrName fromVal.attrValue ) for childIndex=1 to fromGroup.childArray.count do ( if toGroup.childArray.count