Skin Weight code optimization


#1

This is an example of my code to rig a simple cloth to some bones. I’m looking for any tip-trick, update-upgrade to speed up or optimize my code:

delete objects
clearlistener()

(
	/*############################################*/
	/* Functions */
	/*############################################*/
	
	fn PrepareModifier TheModifier =
	(
		Obj = (refs.dependentNodes TheModifier)[1]
		if isvalidnode Obj do
		(
			setCommandPanelTaskMode mode:#modify
			classof Obj
			select Obj
			if modPanel.getCurrentObject() != TheModifier do modPanel.setCurrentObject TheModifier
		)
	)
	
	fn GetBoneIndex SkinModifier TheBone = 
	(
		if isvalidnode TheBone then
		(
			findItem (for i =1 to (skinOps.GetNumberBones SkinModifier) collect (skinOps.GetBoneName SkinModifier i 0)) TheBone.name
		)
		else 0
	)
	
	/*############################################*/
	/* Localizing SkinOps */
	/*############################################*/
	
	local GetNumberVertices = skinOps.GetNumberVertices
	local GetVertexWeightCount = skinOps.GetVertexWeightCount
	local GetVertexWeight = skinOps.GetVertexWeight
	local GetVertexWeightBoneID = skinOps.GetVertexWeightBoneID
	local SetVertexWeights = skinOps.SetVertexWeights
	local ReplaceVertexWeights = skinOps.ReplaceVertexWeights
	local SelectVertices = skinOps.SelectVertices
	local SelectBone = skinOps.SelectBone
	fn AddBone TheSkin TheBone UpdateInteger = (skinOps.addbone TheSkin TheBone 0 ; classof (refs.dependentnodes TheSkin)[1])
	local RemoveBone = skinOps.RemoveBone
	local BlendSelectedBone = skinOps.blendSelected
	local GetNumberBones = skinOps.GetNumberBones
	local GetBoneName = skinOps.GetBoneName
	
	/*############################################*/
	/* Default Variables */
	/*############################################*/
	
	Width = 100.0
	Height = 100.0
	WidthSegments = 4
	HeightSegments = 4
	WidthDistance = Width/WidthSegments
	HeightDistance = Height/HeightSegments

	/*############################################*/
	/* Skin Object */
	/*############################################*/
	
	Obj = Rectangle name:"Skin Object" length:Height width:Width transform:(matrix3 [1,0,0] [0,0,1] [0,-1,0] [Width/2,0,Height/2]) wirecolor:blue
	GarmentModifier = Garment_Maker autoMesh:true preserve:true relax:true outputType:0 figure:undefined stretchMapping:false showMesh:false showSeams:false
	addmodifier Obj GarmentModifier
	SkinModifier = skin()
	addmodifier Obj SkinModifier
	
	/*############################################*/
	/* Bones */
	/*############################################*/
	
	BoneArray = #()
	for i = 1 to WidthSegments + 1 do
	(
		for j = 1 to HeightSegments + 1 do
		(
			append BoneArray (sphere name:("Bone_"+(i as string)+"_"+(j as string)) radius:5 segments:6 pos:[(i-1)*WidthDistance,0,(j-1)*HeightDistance] wirecolor:yellow)
		)
	)
	
	/*############################################*/
	/* Skin */
	/*############################################*/
	
	PrepareModifier SkinModifier
	for TheBone in BoneArray do AddBone SkinModifier TheBone 0
	
	/*############################################*/
	/* Skin Weights */
	/*############################################*/
	
	WDis = WidthDistance/2
	HDis = HeightDistance/2
	NumberVertices = GetNumberVertices SkinModifier
	BonesTrueIndexes = for TheBone in BoneArray collect GetBoneIndex SkinModifier TheBone
	for i = 1 to NumberVertices do
	(
		VertexPos = getvert Obj.mesh i * Obj.objecttransform
		for BoneId = 1 to BoneArray.count do
		(
			TheBone = BoneArray[BoneId]
			BonePos = TheBone.pos
			if BonePos.x >= VertexPos.x - WDis and \
				BonePos.x < VertexPos.x + WDis and \
				BonePos.z >= VertexPos.z - HDis and \
				BonePos.z < VertexPos.z + HDis do ReplaceVertexWeights SkinModifier i BonesTrueIndexes[BoneId] 1
		)
	)
	
	/*############################################*/
	/* Blend Weights */
	/*############################################*/
	
	SelectVertices SkinModifier #{1..NumberVertices}
	for i = 1 to BoneArray.count do
	(
		SelectBone SkinModifier i
		BlendSelectedBone SkinModifier
	)
)

#2

Dude. Make some explanations of what it does and how you don’t like it.
Or else…


#3

It works too slow at hi-res meshes, I just want to optimize it as possible.


#4

Added a few little optimizations

Time: 0.963sec. Mem: 281152L – unoptimized

  1. 0.025
  2. 0.116
  3. 0.075
  4. 0.036
    Time: 0.284sec. Mem: 167408L – optimized
delete objects
gc();t1=timestamp();hf = heapfree

(
	
	times = [0,0,0,0]
	t2 = timestamp()
	
	fn PrepareModifier TheModifier =
	(
		Obj = (refs.dependentNodes TheModifier)[1]
		if isvalidnode Obj do
		(
			setCommandPanelTaskMode mode:#modify
			select Obj
			if modPanel.getCurrentObject() != TheModifier do modPanel.setCurrentObject TheModifier
		)
	)
	
	fn GetBoneIndex SkinModifier TheBone = 
	(
		if isvalidnode TheBone then
		(
			findItem (for i=1 to (skinOps.GetNumberBones SkinModifier) collect (skinOps.GetBoneName SkinModifier i 0)) TheBone.name
		)
		else 0
	)
	

	/* Localizing SkinOps */
	
	local GetNumberVertices = skinOps.GetNumberVertices
	local GetVertexWeightCount = skinOps.GetVertexWeightCount
	local GetVertexWeight = skinOps.GetVertexWeight
	local GetVertexWeightBoneID = skinOps.GetVertexWeightBoneID
	local SetVertexWeights = skinOps.SetVertexWeights
	local ReplaceVertexWeights = skinOps.ReplaceVertexWeights
	local SelectVertices = skinOps.SelectVertices
	local SelectBone = skinOps.SelectBone
	local SkinOpsAddBone = skinOps.addbone	
	local RemoveBone = skinOps.RemoveBone
	local BlendSelectedBone = skinOps.blendSelected
	local GetNumberBones = skinOps.GetNumberBones
	local GetBoneName = skinOps.GetBoneName
	

	/* Default Variables */
	
	Width = 100.0
	Height = 100.0
	WidthSegments = 4
	HeightSegments = 4
	WidthDistance = Width/WidthSegments
	HeightDistance = Height/HeightSegments



	/* Skin Object */	
	t2 = timestamp()
	Obj = Rectangle name:"Skin Object" length:Height width:Width transform:(matrix3 [1,0,0] [0,0,1] [0,-1,0] [Width/2,0,Height/2]) wirecolor:blue
	GarmentModifier = Garment_Maker autoMesh:true preserve:true relax:true outputType:0 figure:undefined stretchMapping:false showMesh:false showSeams:false
	addmodifier Obj GarmentModifier
	SkinModifier = skin()
	addmodifier Obj SkinModifier
	times[1] += timeStamp() - t2

	/* Bones */	
	t2 = timestamp()
	BoneArray = #()
	for i = 1 to WidthSegments + 1 do
	(
		for j = 1 to HeightSegments + 1 do
		(
			append BoneArray (sphere name:("Bone_"+(i as string)+"_"+(j as string)) radius:5 segments:6 pos:[(i-1)*WidthDistance,0,(j-1)*HeightDistance] wirecolor:yellow)
		)
	)
	

	/* Skin */	
	PrepareModifier SkinModifier
	for TheBone in BoneArray do SkinOpsAddBone SkinModifier TheBone 0
	classof (refs.dependentnodes SkinModifier)[1] -- nice trick btw
	times[2] += timeStamp() - t2

	t2 = timestamp()

	/* Skin Weights */	
	WDis = WidthDistance/2
	HDis = HeightDistance/2
	NumberVertices = GetNumberVertices SkinModifier
	BonesTrueIndexes = for TheBone in BoneArray collect GetBoneIndex SkinModifier TheBone
		
	tri = Obj.mesh
	tm  = Obj.objecttransform	
		
	for i = 1 to NumberVertices do
	(
		VertexPos = getvert tri i * tm
		
		val = [ VertexPos.x - WDis, VertexPos.x + WDis, VertexPos.z - HDis, VertexPos.z + HDis ]
		
		for BoneId = 1 to BoneArray.count do
		(			
			BonePos = BoneArray[BoneId].pos
			
			if BonePos.x >= val[1] and BonePos.x < val[2] and BonePos.z >= val[3] and BonePos.z < val[4] do 
			(
				ReplaceVertexWeights SkinModifier i BonesTrueIndexes[BoneId] 1
			)
		)
		

	)
	free tri
	times[3] += timeStamp() - t2
	
	t2 = timestamp()
	/* Blend Weights */	
	SelectVertices SkinModifier #{1..NumberVertices}
	for i = 1 to BoneArray.count do
	(
		SelectBone SkinModifier i
		BlendSelectedBone SkinModifier
	)
	times[4] += timeStamp() - t2
	
	format "1. %\n2. %\n3. %\n4. %\n" (times[1]/1000 as float) (times[2]/1000 as float) (times[3]/1000 as float) (times[4]/1000 as float)
)


	

format "Time: %sec. Mem: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree)

this new forum text formatting is just disgusting… :neutral_face:


#5

what does it do? update?


#6

you can collect bone ids first and replace vertex weights for array instead of one by one. it might be faster


#7
for k=1 to BoneArray.count do SkinOpsAddBone SkinModifier BoneArray[k] (k/BoneArray.count)

you can put the count into variable of course


#8

Yes and seems like it is faster than redrawing.


#9

Nice! Thanks


#10

It’s a bug I think and this should be called after AddBone , otherwise ReplaceVertexWeights doesn’t work correctly.


#11

would you please show me that by changing the code please?


#12

“(k/BoneArray.count)” this is trick to update only last one? Nice!


#13

I just updated my code with your suggestion:

(
	delete objects
-- 	clearlistener()
	gc()
	Time_Start = timestamp()
	Memory_Start = heapfree
	
	/* Functions */
	
	fn PrepareModifier TheModifier =
	(
		Obj = (refs.dependentNodes TheModifier)[1]
		if isvalidnode Obj do
		(
			setCommandPanelTaskMode mode:#modify
			select Obj
			if modPanel.getCurrentObject() != TheModifier do modPanel.setCurrentObject TheModifier
		)
	)
	
	fn GetBoneIndex SkinModifier TheBone = 
	(
		if isvalidnode TheBone then
		(
			findItem (for i =1 to (skinOps.GetNumberBones SkinModifier) collect (skinOps.GetBoneName SkinModifier i 0)) TheBone.name
		)
		else 0
	)
	
	/* Localizing SkinOps */
	
	local GetNumberVertices = skinOps.GetNumberVertices
	local GetVertexWeightCount = skinOps.GetVertexWeightCount
	local GetVertexWeight = skinOps.GetVertexWeight
	local GetVertexWeightBoneID = skinOps.GetVertexWeightBoneID
	local SetVertexWeights = skinOps.SetVertexWeights
	local ReplaceVertexWeights = skinOps.ReplaceVertexWeights
	local SelectVertices = skinOps.SelectVertices
	local SelectBone = skinOps.SelectBone
	local AddBone = skinOps.addbone	
	local RemoveBone = skinOps.RemoveBone
	local BlendSelectedBone = skinOps.blendSelected
	local GetNumberBones = skinOps.GetNumberBones
	local GetBoneName = skinOps.GetBoneName
	
	/* Default Variables */
	
	Width = 100.0
	Height = 100.0
	WidthSegments = 4
	HeightSegments = 4
	WidthDistance = Width/WidthSegments
	HeightDistance = Height/HeightSegments

	/* Skin Object */
	
	Obj = Rectangle name:"Skin Object" length:Height width:Width transform:(matrix3 [1,0,0] [0,0,1] [0,-1,0] [Width/2,0,Height/2]) wirecolor:blue
	GarmentModifier = Garment_Maker autoMesh:true preserve:true relax:true outputType:0 figure:undefined stretchMapping:false showMesh:false showSeams:false
	addmodifier Obj GarmentModifier
	SkinModifier = skin()
	addmodifier Obj SkinModifier
	
	/* Bones */
	
	BoneArray = #()
	for i = 1 to WidthSegments + 1 do
	(
		for j = 1 to HeightSegments + 1 do
		(
			append BoneArray (sphere name:("Bone_"+(i as string)+"_"+(j as string)) radius:5 segments:6 pos:[(i-1)*WidthDistance,0,(j-1)*HeightDistance] wirecolor:yellow)
		)
	)
	
	/* Skin */
	
	PrepareModifier SkinModifier
	for TheBone in BoneArray do AddBone SkinModifier TheBone 0
	classof (refs.dependentnodes SkinModifier)[1]
	
	/* Skin Weights */
	
	WDis = WidthDistance/2
	HDis = HeightDistance/2
	NumberVertices = GetNumberVertices SkinModifier
	BonesTrueIndexes = #()
	BonePoses = #()
	for TheBone in BoneArray do
	(
		append BonesTrueIndexes (GetBoneIndex SkinModifier TheBone)
		append BonePoses TheBone.pos
	)
	ObjMesh = Obj.mesh
	ObjTransform  = Obj.objecttransform
	for i = 1 to NumberVertices do
	(
		VertexPos = getvert ObjMesh i * ObjTransform
		Val = [ VertexPos.x - WDis, VertexPos.x + WDis, VertexPos.z - HDis, VertexPos.z + HDis ]
		for BoneId = 1 to BoneArray.count do
		(
			BonePos = BonePoses[BoneId]
			if BonePos.x >= Val[1] and BonePos.x < Val[2] and BonePos.z >= Val[3] and BonePos.z < Val[4] do
			(
				ReplaceVertexWeights SkinModifier i BonesTrueIndexes[BoneId] 1
			)
		)
	)
	free ObjMesh
	
	/* Blend Weights */
	
	SelectVertices SkinModifier #{1..NumberVertices}
	for i = 1 to BoneArray.count do
	(
		SelectBone SkinModifier i
		BlendSelectedBone SkinModifier
	)
	
	/* End */
	
	Time_End = timestamp()
	Memory_End = heapfree
	format "Time:% Memory:%\n" ((Time_End - Time_Start)/1000.0) (Memory_Start - Memory_End)
)

#14

It seems it takes more memory:

	for BoneId = 1 to BoneArray.count do
	(
		BonePos = BonePoses[BoneId]
		if BonePos.x >= Val[1] and BonePos.x < Val[2] and BonePos.z >= Val[3] and BonePos.z < Val[4] do
		(
			ReplaceVertexWeights SkinModifier i BonesTrueIndexes[BoneId] 1
		)
	)

Time:0.348 Memory:143476L


	Index_Array = #()
	Weight_Array = #()
	for BoneId = 1 to BoneArray.count do
	(
		BonePos = BonePoses[BoneId]
		if BonePos.x >= Val[1] and BonePos.x < Val[2] and BonePos.z >= Val[3] and BonePos.z < Val[4] do
		(
			append Index_Array BonesTrueIndexes[BoneId]
			append Weight_Array 1
		)
	)
	if Index_Array.count > 0 do ReplaceVertexWeights SkinModifier i Index_Array Weight_Array

Time:0.334 Memory:253656L