Files
gtav-src/tools_ng/dcc/current/max2012/scripts/pipeline/util/cloth.ms
T
2025-09-29 00:52:08 +02:00

575 lines
18 KiB
Plaintext
Executable File

--
-- File:: cloth.ms
-- Description:: Cloth support functions.
--
-- Author:: David Muir <david.muir@rockstarnorth.com>
-- 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 <number of channels on object>
),
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