-- -- File:: cloth.ms -- Description:: Cloth support functions. -- -- Author:: David Muir -- Date:: 24 April 2009 -- ----------------------------------------------------------------------------- -- Uses ----------------------------------------------------------------------------- filein "rockstar/util/collutil.ms" filein "rockstar/util/material.ms" filein "pipeline/helpers/cloth_helpers.ms" ----------------------------------------------------------------------------- -- Globals ----------------------------------------------------------------------------- -- None (yet) -- Channel indices may go here. -- ----------------------------------------------------------------------------- -- Struct ----------------------------------------------------------------------------- struct RsClothStruct ( bakevalue = [1,1,1], channelMap = "(pin|10|9|0.0;pinradius|11|21|0.0;pinramp|12|22|0.0;edgecomp|13|23|0.25;bslength|14|24|0.5;bsstr|15|25|0.7;weight|16|10|0.0;mapTolerance|17|26|0.001;mapError|18|27|0.001;dynPin|19|13|0;inflationScale|20|15|1.0;customEdge|21|16|0.0)", ---------------------------------------------------------------------------------------------- -- general cloth facuntions ---------------------------------------------------------------------------------------------- -- -- name: IsCloth -- desc: Return whether an object is flagged as being cloth. -- fn IsCloth obj = ( if ( "Gta Object" != GetAttrClass obj ) then return ( false ) local idxIsCloth = ( GetAttrIndex "Gta Object" "Is Cloth" ) return (GetAttr obj idxIsCloth) ), -- -- Set to minimal exported channel amount -- fn ditchChannel obj intafaze channelIndex = ( if intafaze.getVDataChannelSupport obj channelIndex then ( intafaze.freeVData obj channelIndex intafaze.setVDataChannelSupport obj channelIndex false ) ), fn SetClothMapsSupport obj ditchChannelNums = ( local intafaze = undefined case classof obj of ( Editable_Mesh:(intafaze = meshop) Editable_Poly:(intafaze = polyop) PolyMeshObject:(intafaze = polyop) Tripatch:(intafaze = patch) ) if undefined!=intafaze then ( local skinSave = obj.modifiers[#Skin] if undefined!=skinSave then deleteModifier obj skinSave for dc in ditchChannelNums do ditchChannel obj intafaze RAGEClothChannelTable[dc].vDataChn if undefined!=skinSave then addModifier obj skinSave ) else gRsUlog.LogWarning "Counldn't recognise correct interface class for cloth mesh object!" context:obj ), -- -- name: IsValidCloth -- desc: Return whether an object is valid (Less verts than limit) -- fn IsValidCloth obj = ( local isValidClothMsg = "IsValidCloth" + (obj.name as string) gRsUlog.ProfileStart isValidClothMsg local vertLimit = 1000 if (obj.numVerts > vertLimit) then ( throw (obj.name + " - Cloth (simulation) mesh has too many verts.") ) if MultiMaterial==(classof obj.material) then ( local mapList = #() RsMatGetMapIdsUsedOnObject obj mapList if mapList.count!=1 then ( throw (obj.name + " - Cloth (simulation) mesh has more than one material.") ) local arrayindex = finditem obj.material.materialIDList mapList[1] local mtl = obj.material.materiallist[arrayindex] if undefined==mtl then throw (obj.name + " - Cloth (simulation) mesh material is undefined. Empty slot?") if (RstGetIsTwoSided mtl) then throw (obj.name + " - Cloth (simulation) mesh material must not have two sided flag.") ) else if Rage_Shader==(classof obj.material) then ( local mtl = obj.material if (RstGetIsTwoSided mtl) then throw (obj.name + " - Cloth (simulation) mesh material must not have two sided flag.") ) -- hierarchy transform checks -- local allClothGroupMeshes -- if RsLodDrawable_HasRenderModel obj then -- ( -- local renderModel = (RsLodDrawable_GetRenderModel obj) -- append renderModelName renderModel.name -- ) local savesel = selection as array local groupCount = 0 local clothMeshes = RsLodDrawable_SetUserProps obj &groupCount hasCloth:true clothTypeGroup:undefined -- clothTypeGroup undefined to prevent user prop setting print ("clothMeshes:"+clothMeshes as string) for cm in clothMeshes do ( if not RsCompareMatrix obj.transform cm.transform translation:false then throw ("cloth mesh "+cm.name+" has different rotation as "+obj.name+". Please fix.") ) select savesel -- this has the cloth tag, so is the sim mesh or the only one. if not hasAnyPinnedVerts obj then throw ("Cloth mesh "+obj.name+" has no pinned vertices!") gRsUlog.ProfileEnd context:isValidClothMsg ), fn bakeVertexChannels objs = ( if (classof objs) != Array then ( objs = #(objs) ) for obj in objs do ( local interfaze = undefined case classof obj of ( Editable_mesh:(interfaze = meshop;) Editable_Poly:(interfaze = polyop;) PolyMeshObject:(interfaze = polyop;) ) try ( for channelindex = -2 to 0 do ( if interfaze.getmapsupport obj channelindex then ( local average = (color 0 0 0) local numverts = interfaze.getnummapverts obj channelindex for vertindex = 1 to numverts do average = average + interfaze.getmapvert obj channelindex vertindex average = average/numverts for vertindex = 1 to numverts do interfaze.setmapvert obj channelindex vertindex average ) ) ) catch ( gRsUlog.LogError ("Cloth colour baking failed for object "+obj.name+". Please remove invalid modifiers. \nError:"+(getCurrentException())) context:obj ) update obj ) ), fn UnifyNormals objs = ( print ("UnifyNormals "+objs as string) if (classof objs) != Array then ( objs = #(objs) ) for obj in objs do ( themesh = Editable_Mesh() themesh.mesh = copy obj.baseobject.mesh local baseObjClass = classof obj.baseobject select themesh addmodifier themesh (Edit_Normals()) numNormals =themesh.Edit_Normals.GetNumNormals() my_normals = (for vi = 1 to numNormals collect vi) as bitarray themesh.Edit_Normals.unify node:themesh selection:(my_normals) convertto themesh baseObjClass obj.baseobject = themesh delete themesh select obj update obj ) ), fn GetRenderModelName obj = ( local renderModelName = "" -- setup seelction set and user props if RsLodDrawable_HasRenderModel obj then ( local renderModel = (RsLodDrawable_GetRenderModel obj) append renderModelName renderModel.name ) return renderModelName ), fn SetupClothExpectedVertCount theMesh meshes:undefined = ( local paramString = "" local insertSeparator = false for m in meshes do ( if insertSeparator then paramString = paramString + "|" else insertSeparator = true paramString = paramString + m.name + ":" + (getnumverts m) as string ) print ("paramString:"+paramString) rexSetPropertyValue theMesh "ExpectedVertexCount" paramString rexSetPropertyValue theMesh "CullVertexChannels" "2" -- culling from 1 to ), fn SetupClothMeshesForExport meshes = ( bakeVertexChannels meshes UnifyNormals meshes ), fn BuildChannelMapStringWithVariations objs = ( local blindChannels = channelMap blindChannels = substring blindChannels 1 (blindChannels.count-1) for entry in RAGEClothChannelTable do ( local variationChannel = entry.vDataVariationOffset if undefined!=variationChannel then ( for channeloffset = 0 to gVariationChannelsPerChannel do ( local channelindex = variationChannel + channeloffset local useChannel = false for obj in objs do ( -- print ("test "+obj as string+" with channel "+channelindex as string) if GetChannelSupport obj channelindex then useChannel = true ) if useChannel then ( local entryName = ((RsFileSafeString entry.userName)+"_Variation"+(channeloffset+1) as string) append blindChannels (";"+entryName+"|"+channelindex as string+"|"+channelindex as string+"|"+entry.defaultVal as string) ) ) ) ) append blindChannels ")" print blindChannels blindChannels ), ---------------------------------------------------------------------------------------------- -- Environment cloth facuntions ---------------------------------------------------------------------------------------------- -- -- name: SetupEnvClothForExport -- desc: Setup environment cloth for export. -- fn SetupEnvClothForExport objs rexentity rexmesh fragroot: = ( -- Add the EnvCloth child local envCloth = rexAddChild rexentity "EnvCloth" "EnvCloth" local envClothPiece = rexAddChild envCloth "ClothPiece" "ClothPiece" -- Setup mesh (default setting for now) local blindChannels = BuildChannelMapStringWithVariations objs rexSetPropertyValue rexmesh "MeshBlindVertexDataChannels" blindChannels local savesel = selection as array local objToOptimise = #() + objs local objsToExport = #() for obj in objs where obj!=undefined do ( local renderModel = (RsLodDrawable_GetRenderModel obj) if undefined!=renderModel then append objToOptimise renderModel local simModel = (RsLodDrawable_GetSimulationModel obj) local isHighestLODModel = not (RsLodDrawable_HasHigherDetailModel obj) if undefined==simModel and isHighestLODModel then append objsToExport obj if unsupplied!=fragroot then ( local offset = obj.pos - fragroot.pos setuserprop obj "fragmentOffset" (offset.x as string+","+offset.y as string+","+offset.z as string) ) ) SetupClothMeshesForExport objToOptimise select objsToExport gRsUlog.LogMessage ("env CLOTHPIECE node selection: "+objsToExport as string) rexSetNodes envClothPiece select savesel true ), ---------------------------------------------------------------------------------------------- -- Character cloth facuntions ---------------------------------------------------------------------------------------------- fn SetupCharClothNodeSelection obj = ( clearSelection() if ( IsCloth obj ) then ( -- Attach bounds (optional) for child in obj.children do ( -- Ignore non-collision primitives if ( "Gta Collision" == ( GetAttrClass child ) ) then selectMore child ) ) else ( -- ped parts dont use lod groups RsUserProp_Clear obj "optimseMeshForCloth" RsUserProp_Clear obj "lodgroup" RsUserProp_Clear obj "optimize" ) -- setup seelction set and user props setuserprop obj "lodgroup" 0 if RsLodDrawable_HasRenderModel obj then ( local renderModel = (RsLodDrawable_GetRenderModel obj) local charclothsectionname = renderModel.name setuserprop renderModel "lodgroup" 0 setuserprop renderModel "optimize" false setUserProp renderModel "exclusiveTypeGroups" ("lodgroup|fragments|"+charclothsectionname) RsUserProp_Clear renderModel "optimseMeshForCloth" selectMore renderModel selectMore obj setuserprop obj "lodgroup" 0 setUserProp obj "exclusiveTypeGroups" charclothsectionname setuserprop obj "optimseMeshForCloth" true ) else if RsLodDrawable_HasSimulationModel obj then ( local simModel = (RsLodDrawable_GetSimulationModel obj) local charclothsectionname = obj.name setUserProp obj "exclusiveTypeGroups" ("lodgroup|fragments|"+charclothsectionname) setuserprop obj "lodgroup" 0 selectMore obj RsUserProp_Clear obj "optimseMeshForCloth" selectMore simModel setuserprop simModel "optimseMeshForCloth" true setuserprop simModel "lodgroup" 0 setuserprop simModel "optimize" false setUserProp simModel "exclusiveTypeGroups" charclothsectionname ) else return false return true ), ---------------------------------------------------------------------------------------------- -- Alternative to above without use of select bugstar:1880733 ---------------------------------------------------------------------------------------------- fn SetupCharClothNodeArray obj NodeArray = ( if ( IsCloth obj ) then ( -- Attach bounds (optional) for child in obj.children do ( -- Ignore non-collision primitives if ( "Gta Collision" == ( GetAttrClass child ) ) then append NodeArray child ) ) else ( -- ped parts dont use lod groups RsUserProp_Clear obj "optimseMeshForCloth" RsUserProp_Clear obj "lodgroup" RsUserProp_Clear obj "optimize" ) -- setup seelction set and user props setuserprop obj "lodgroup" 0 if RsLodDrawable_HasRenderModel obj then ( local renderModel = (RsLodDrawable_GetRenderModel obj) local charclothsectionname = renderModel.name setuserprop renderModel "lodgroup" 0 setuserprop renderModel "optimize" false setUserProp renderModel "exclusiveTypeGroups" ("lodgroup|fragments|"+charclothsectionname) RsUserProp_Clear renderModel "optimseMeshForCloth" append NodeArray renderModel append NodeArray obj setuserprop obj "lodgroup" 0 setUserProp obj "exclusiveTypeGroups" charclothsectionname setuserprop obj "optimseMeshForCloth" true ) else if RsLodDrawable_HasSimulationModel obj then ( local simModel = (RsLodDrawable_GetSimulationModel obj) local charclothsectionname = obj.name setUserProp obj "exclusiveTypeGroups" ("lodgroup|fragments|"+charclothsectionname) setuserprop obj "lodgroup" 0 append NodeArray obj RsUserProp_Clear obj "optimseMeshForCloth" append NodeArray simModel setuserprop simModel "optimseMeshForCloth" true setuserprop simModel "lodgroup" 0 setuserprop simModel "optimize" false setUserProp simModel "exclusiveTypeGroups" charclothsectionname ) else return false return true ), -- -- name: SetupCharClothForExport -- desc: Setup character cloth for export. -- fn SetupCharClothForExport obj skelroot outputdir rexentity rexmesh = ( -- Add the EnvCloth child local envCloth = rexAddChild rexentity "CharCloth" "CharCloth" local renderModelName = GetRenderModelName obj local envClothPiece = rexAddChild envCloth (if renderModelName!="" then renderModelName else obj.name) "ClothPiece" -- Setup mesh (default setting for now) local blindChannels = BuildChannelMapStringWithVariations #(obj) rexSetPropertyValue rexmesh "MeshBlindVertexDataChannels" blindChannels -- set selection to rex input for CLOTH definiition gRsULog.LogMessage ("CLOTH SELECTION: "+((selection as array) as string)) for m in (selection as array) do gRsULog.LogMessage ("lod group of "+m.name+": "+(getuserprop m "lodgroup") as string) rexSetNodes envClothPiece true ), -- -- name: SetupCharClothForExportWithoutSelect -- desc: Setup character cloth for export, don't use the current maxScript selection (which can be slow) -- fn SetupCharClothForExportWithoutSelect obj skelroot outputdir rexentity rexmesh nodes = ( -- Add the EnvCloth child local envCloth = rexAddChild rexentity "CharCloth" "CharCloth" local renderModelName = GetRenderModelName obj local envClothPiece = rexAddChild envCloth (if renderModelName!="" then renderModelName else obj.name) "ClothPiece" -- Setup mesh (default setting for now) local blindChannels = BuildChannelMapStringWithVariations #(obj) rexSetPropertyValue rexmesh "MeshBlindVertexDataChannels" blindChannels -- set selection to rex input for CLOTH definiition gRsULog.LogMessage ("CLOTH SELECTION: "+(nodes as string)) for m in nodes do gRsULog.LogMessage ("lod group of "+m.name+": "+(getuserprop m "lodgroup") as string) rexSetNodes envClothPiece exportnodes:(RsGetAnimHandleString nodes) true ), -- -- Export Cloth collision xml file -- this is to be replaced with an asset build step post GTAV -- fn ExportClothCollisionXML clothobjs assetPath = ( local idxClothindex = getAttrindex "Gta Object" "Is Cloth" local allClothObjs = #() local clothCollXml = XmlDocument() clothCollXml.Init() verletCloth = clothCollXml.createelement("rage__phVerletClothCustomBounds") clothCollXml.document.AppendChild verletCloth collisionXmlRoot = clothCollXml.createelement("CollisionData") appendTo:verletCloth for obj in clothobjs do ( local collKids = #() RsSceneLink.getChildren LinkType_ClothCollision obj &collKids local inverseItemTransform = inverse obj.transform for col in collKids where Col_Capsule == classof col or Col_Plane == classof col do ( local item = clothCollXml.createelement("Item") appendTo:collisionXmlRoot local localTrans = col.transform * inverseItemTransform clothCollXml.createelement "OwnerName" appendTo:item value:obj.name clothCollXml.createelement "Rotation" appendTo:item attrs:#( #("x", localTrans.rotation.x), #("y", localTrans.rotation.y), #("z", localTrans.rotation.z), #("w", localTrans.rotation.w) ) clothCollXml.createelement "Position" appendTo:item attrs:#( #("x", localTrans.pos.x), #("y", localTrans.pos.y), #("z", localTrans.pos.z) ) local normal = (Point3 0 0 0) if Col_Plane == classof col then ( case col.Orientation of ( 0: normal = (Point3 0 0 1) 1: normal = (Point3 0 1 0) 2: normal = (Point3 1 0 0) ) if isProperty col "rotation" then normal = normal * col.rotation ) clothCollXml.createelement "Normal" appendTo:item attrs:#( #("x", normal.x), #("y", normal.y), #("z", normal.z) ) clothCollXml.createelement "CapsuleRadius" appendTo:item attrs:#(#("value", col.radius)) clothCollXml.createelement "CapsuleLen" appendTo:item attrs:#(#("value", col.length)) clothCollXml.createelement "CapsuleHalfHeight" appendTo:item attrs:#(#("value", 0.0f)) clothCollXml.createelement "CapsuleHalfWidth" appendTo:item attrs:#(#("value", 0.0f)) ) ) local outputPath = assetPath+"/clothCollision.xml" clothCollXml.save outputPath ) ) ----------------------------------------------------------------------------- -- Struct Global Instances ----------------------------------------------------------------------------- global RsCloth = RsClothStruct() -- cloth.ms