Animation strange issue.

Blitz3D Forums/Blitz3D Programming/Animation strange issue.

Vertigo(Posted 2007) [#1]
I could really use some help. My animation engine loads multiple objects and then rotates them based on the snapshot of the animation. It runs great, with a couple of objects, when 4 or more are introduced it lags really badly. Any ideas on this, as this machine + blitz should allow 100 objects to rotate around easily without this kind of slow down.


Dim AnimArray(10000)

Type animation

Field name$
Field frame%
Field max_frames%
Field start_frame%
Field end_frame%
Field looping  ;stored on load
	Field head_px#,head_py#,head_pz#
	Field head_rx#,head_ry#,head_rz#
	Field chest_px#,chest_py#,chest_pz#
	Field chest_rx#,chest_ry#,chest_rz#
	Field hip_px#,hip_py#,hip_pz#
	Field hip_rx#,hip_ry#,hip_rz#
	Field L_arm_px#,L_arm_py#,L_arm_pz#
	Field L_arm_rx#,L_arm_ry#,L_arm_rz#
	Field R_arm_px#,R_arm_py#,R_arm_pz#
	Field R_arm_rx#,R_arm_ry#,R_arm_rz#
	Field L_forearm_px#,L_forearm_py#,L_forearm_pz#
	Field L_forearm_rx#,L_forearm_ry#,L_forearm_rz#
	Field R_forearm_px#,R_forearm_py#,R_forearm_pz#
	Field R_forearm_rx#,R_forearm_ry#,R_forearm_rz#
	Field L_hand_px#,L_hand_py#,L_hand_pz#
	Field L_hand_rx#,L_hand_ry#,L_hand_rz#
	Field R_hand_px#,R_hand_py#,R_hand_pz#
	Field R_hand_rx#,R_hand_ry#,R_hand_rz#
	Field L_leg_px#,L_leg_py#,L_leg_pz#
	Field L_leg_rx#,L_leg_ry#,L_leg_rz#
	Field R_leg_px#,R_leg_py#,R_leg_pz#
	Field R_leg_rx#,R_leg_ry#,R_leg_rz#
	Field L_shin_px#,L_shin_py#,L_shin_pz#
	Field L_shin_rx#,L_shin_ry#,L_shin_rz#
	Field R_shin_px#,R_shin_py#,R_shin_pz#
	Field R_shin_rx#,R_shin_ry#,R_shin_rz#
	Field L_foot_px#,L_foot_py#,L_foot_pz#
	Field L_foot_rx#,L_foot_ry#,L_foot_rz#
	Field R_foot_px#,R_foot_py#,R_foot_pz#
	Field R_foot_rx#,R_foot_ry#,R_foot_rz#

End Type

Function do_animations(animation_delay)
	
	animation_frameskip=animation_frameskip + 1

	If animation_frameskip=>animation_delay Then 

			For b.model=Each model
				
				looping%=1

				B\on_frame%=B\on_frame%+1
				

					If looping%=1 Then
					
						If B\on_frame% > B\end_frame% Then B\on_frame%=B\start_frame%
					Else
						If B\on_frame% > B\end_frame% Then B\on_frame%=B\end_frame%-1
					End If

				
				playframe(b\name$,B\on_frame%)

			Next
	

	animation_frameskip=0

	End If

End Function


Function playframe(model$,frame%)


	For M.model=Each model

		If M\name$=model$ Then

				Local a.animation
				a = Object.animation( AnimArray(frame%) )
				
					PositionEntity M\head,A\head_px#,A\head_py#,A\head_pz#
					RotateEntity M\head,A\head_rx#,A\head_ry#,A\head_rz#
					PositionEntity M\chest,A\chest_px#,A\chest_py#,A\chest_pz#
					RotateEntity M\chest,A\chest_rx#,A\chest_ry#,A\chest_rz#
					PositionEntity M\hip,A\hip_px#,A\hip_py#,A\hip_pz#
					RotateEntity M\hip,A\hip_rx#,A\hip_ry#,A\hip_rz#

					PositionEntity M\l_arm,A\l_arm_px#,A\l_arm_py#,A\l_arm_pz#
					RotateEntity M\l_arm,A\l_arm_rx#,A\l_arm_ry#,A\l_arm_rz#
					PositionEntity M\l_forearm,A\l_forearm_px#,A\l_forearm_py#,A\l_forearm_pz#
					RotateEntity M\l_forearm,A\l_forearm_rx#,A\l_forearm_ry#,A\l_forearm_rz#
					PositionEntity M\l_hand,A\l_hand_px#,A\l_hand_py#,A\l_hand_pz#
					RotateEntity M\l_hand,A\l_hand_rx#,A\l_hand_ry#,A\l_hand_rz#

					PositionEntity M\r_arm,A\r_arm_px#,A\r_arm_py#,A\r_arm_pz#
					RotateEntity M\r_arm,A\r_arm_rx#,A\r_arm_ry#,A\r_arm_rz#
					PositionEntity M\r_forearm, A\r_forearm_px#,A\r_forearm_py#,A\r_forearm_pz#
					RotateEntity M\r_forearm,A\r_forearm_rx#,A\r_forearm_ry#,A\r_forearm_rz#
					PositionEntity M\r_hand,A\r_hand_px#,A\r_hand_py#,A\r_hand_pz#
					RotateEntity M\r_hand,A\r_hand_rx#,A\r_hand_ry#,A\r_hand_rz#

					PositionEntity M\l_leg,A\l_leg_px#,A\l_leg_py#,A\l_leg_pz#
					RotateEntity M\l_leg,A\l_leg_rx#,A\l_leg_ry#,A\l_leg_rz#
					PositionEntity M\l_shin,A\l_shin_px#,A\l_shin_py#,A\l_shin_pz#
					RotateEntity M\l_shin,A\l_shin_rx#,A\l_shin_ry#,A\l_shin_rz#
					PositionEntity M\l_foot,A\l_foot_px#,A\l_foot_py#,A\l_foot_pz#
					RotateEntity M\l_foot,A\l_foot_rx#,A\l_foot_ry#,A\l_foot_rz#

					PositionEntity M\r_leg,A\r_leg_px#,A\r_leg_py#,A\r_leg_pz#
					RotateEntity M\r_leg,A\r_leg_rx#,A\r_leg_ry#,A\r_leg_rz#
					PositionEntity M\r_shin,A\r_shin_px#,A\r_shin_py#,A\r_shin_pz#
					RotateEntity M\r_shin,A\r_shin_rx#,A\r_shin_ry#,A\r_shin_rz#
					PositionEntity M\r_foot,A\r_foot_px#,A\r_foot_py#,A\r_foot_pz#
					RotateEntity M\r_foot,A\r_foot_rx#,A\r_foot_ry#,A\r_foot_rz#

		End If
	Next

End Function

Function loadanimations(filename$)


		filein=ReadFile("animations\" + filename$ + ".ani")
		tot_anims%=ReadInt(filein)
		total_frames%=tot_anims%

		start_frame%=animations_loaded
		end_frame%=animations_loaded + tot_anims%

	For loadcount = 0 To tot_anims
		
		A.animation=New animation
		
		a\start_frame%=start_frame%
		a\end_frame%=end_frame%
		max_frames%=ReadInt(filein)
		A\name$=ReadString (filein)
		A\looping%=looping%
		
		A\frame%=ReadInt (filein)
		;head
		A\head_px#=ReadFloat(filein)
		A\head_py#=ReadFloat(filein)
		A\head_pz#=ReadFloat(filein)
		A\head_rx#=ReadFloat(filein)
		A\head_ry#=ReadFloat(filein)
		A\head_rz#=ReadFloat(filein)
		;chest
		A\chest_px#=ReadFloat(filein)
		A\chest_py#=ReadFloat(filein)
		A\chest_pz#=ReadFloat(filein)
		A\chest_rx#=ReadFloat(filein)
		A\chest_ry#=ReadFloat(filein)
		A\chest_rz#=ReadFloat(filein)

		;hip
		A\hip_px#=ReadFloat(filein)
		A\hip_py#=ReadFloat(filein)
		A\hip_pz#=ReadFloat(filein)
		A\hip_rx#=ReadFloat(filein)
		A\hip_ry#=ReadFloat(filein)
		A\hip_rz#=ReadFloat(filein)

		;left arm
		A\l_arm_px#=ReadFloat(filein)
		A\l_arm_py#=ReadFloat(filein)
		A\l_arm_pz#=ReadFloat(filein)
		A\l_arm_rx#=ReadFloat(filein)
		A\l_arm_ry#=ReadFloat(filein)
		A\l_arm_rz#=ReadFloat(filein)
		A\l_forearm_px#=ReadFloat(filein)
		A\l_forearm_py#=ReadFloat(filein)
		A\l_forearm_pz#=ReadFloat(filein)
		A\l_forearm_rx#=ReadFloat(filein)
		A\l_forearm_ry#=ReadFloat(filein)
		A\l_forearm_rz#=ReadFloat(filein)
		A\l_hand_px#=ReadFloat(filein)
		A\l_hand_py#=ReadFloat(filein)
		A\l_hand_pz#=ReadFloat(filein)
		A\l_hand_rx#=ReadFloat(filein)
		A\l_hand_ry#=ReadFloat(filein)
		A\l_hand_rz#=ReadFloat(filein)

		;right arm
		A\r_arm_px#=ReadFloat(filein)
		A\r_arm_py#=ReadFloat(filein)
		A\r_arm_pz#=ReadFloat(filein)
		A\r_arm_rx#=ReadFloat(filein)
		A\r_arm_ry#=ReadFloat(filein)
		A\r_arm_rz#=ReadFloat(filein)
		A\r_forearm_px#=ReadFloat(filein)
		A\r_forearm_py#=ReadFloat(filein)
		A\r_forearm_pz#=ReadFloat(filein)
		A\r_forearm_rx#=ReadFloat(filein)
		A\r_forearm_ry#=ReadFloat(filein)
		A\r_forearm_rz#=ReadFloat(filein)
		A\r_hand_px#=ReadFloat(filein)
		A\r_hand_py#=ReadFloat(filein)
		A\r_hand_pz#=ReadFloat(filein)
		A\r_hand_rx#=ReadFloat(filein)
		A\r_hand_ry#=ReadFloat(filein)
		A\r_hand_rz#=ReadFloat(filein)

		;left leg
		A\l_leg_px#=ReadFloat(filein)
		A\l_leg_py#=ReadFloat(filein)
		A\l_leg_pz#=ReadFloat(filein)
		A\l_leg_rx#=ReadFloat(filein)
		A\l_leg_ry#=ReadFloat(filein)
		A\l_leg_rz#=ReadFloat(filein)
		A\l_shin_px#=ReadFloat(filein)
		A\l_shin_py#=ReadFloat(filein)
		A\l_shin_pz#=ReadFloat(filein)
		A\l_shin_rx#=ReadFloat(filein)
		A\l_shin_ry#=ReadFloat(filein)
		A\l_shin_rz#=ReadFloat(filein)
		A\l_foot_px#=ReadFloat(filein)
		A\l_foot_py#=ReadFloat(filein)
		A\l_foot_pz#=ReadFloat(filein)
		A\l_foot_rx#=ReadFloat(filein)
		A\l_foot_ry#=ReadFloat(filein)
		A\l_foot_rz#=ReadFloat(filein)

		;right leg
		A\r_leg_px#=ReadFloat(filein)
		A\r_leg_py#=ReadFloat(filein)
		A\r_leg_pz#=ReadFloat(filein)
		A\r_leg_rx#=ReadFloat(filein)
		A\r_leg_ry#=ReadFloat(filein)
		A\r_leg_rz#=ReadFloat(filein)
		A\r_shin_px#=ReadFloat(filein)
		A\r_shin_py#=ReadFloat(filein)
		A\r_shin_pz#=ReadFloat(filein)
		A\r_shin_rx#=ReadFloat(filein)
		A\r_shin_ry#=ReadFloat(filein)
		A\r_shin_rz#=ReadFloat(filein)
		A\r_foot_px#=ReadFloat(filein)
		A\r_foot_py#=ReadFloat(filein)
		A\r_foot_pz#=ReadFloat(filein)
		A\r_foot_rx#=ReadFloat(filein)
		A\r_foot_ry#=ReadFloat(filein)
		A\r_foot_rz#=ReadFloat(filein)

		AnimArray(animations_loaded) = Handle(a)
		animations_loaded=animations_loaded+1

	Next

	CloseFile(filein)

End Function





Vertigo(Posted 2007) [#2]
Well, I am amazed at how slow blitz's Text function is. I had a series of text displaced on screen per object for debug purposes which when removed increased speed 10 fold however... I still think I might need to fix my render tweening for performance increase... also, does anyone have any ideas on how to make this sytem more effecient?

Thanks!


Danny(Posted 2007) [#3]
Hi vertigo,

Use the UltraFastText library to get rid of that blitz text slowdown. The library replaces blitz's original Text command with something.... seriously faster (50 to 70 times).

check here: http://www.blitzbasic.com/Community/posts.php?topic=68216

Your code is pretty minimal so I don't see anything particular that could be made faster.
If you use this on larger amounts of characters though, it might be worth checking if a characters is visible to the camera before applying the animation. And if it's not visible to simply skip it.

Also the way your character is skinned can greatly affect the speed it takes UpdateWorld to update your character's mesh. The obvious Less bones, Less weightmaps, Less poly's, smaller weightmaps, etc. all help..

d.


Vertigo(Posted 2007) [#4]
I knew about that text lib thanks though.. Like I said it was just for debug purposes I was just amazed at how slow that was. Thanks for the input. Yeah I was going to check if a model was in the players line of sight before animating it. Also I have a threading system setup so it skips 5 render cycles and alternates which objects it iterates through for movement. I just assumed moving a few entire meshes would be faster than mesh deformation animation and I think I was wrong. All while testing I was only using one or two animated objects, I guess the lesson learned here is to test your system from creation with what you expect in the end result haha. I was just hoping there was a faster way of picking and rotating the parts around.


Danny(Posted 2007) [#5]
When you're talking 100's of characters, I think it's important considering what's happening when it comes to Surfaces. I'm sure you know about many surfaces will get Blitz down to it's knees. I'm not sure if you do a CopyEntity on an animMesh if that creates a NEW surface or just another instance of the same surface?! Might be worth checking out..

You could also seriously simplify your animation system. Since it looks like you are hard-coding the fact your characters have 2 arms, legs, etc.
I would therefor use a memorybank to store animation data, instead of in dedicated fields like \r_arm, \l_arm, \l_leg, etc. etc. It might not gain you additional FPS's but at least makes coding/changing it less painfull on the fingers :)

hope this helps,

d.


Vertigo(Posted 2007) [#6]
Danny, thank you for your reply. As far as the surfaces go, each "body part" gets loaded in at the start into an indexed array. For instance if you equip a different type of armor, it frees the current model\part and copies a new one from the index. Most objects are pretty low poly and contain 128x128 textures for each part. The textures are also indexed and loaded only once. Total polys per complete character with all body parts loaded is around 1,500 - 1,800 depending on whats equipped. When you suggested using banks for animation data, how would you go about implementing that? And how would it be easier on my fingers? haha And even though they are labled "hand foot" etc... they arent always used for that. Depending on how you parent them. For example if I wanted a character of mine to have animated wings, I could create another model that has 7 parts and animate them using whatever names I wanted. Eg. Hip for the base, and use arm forearm and hand for the wing span. Maybe im just going about this all wrong.


Danny(Posted 2007) [#7]
ok, here's a quick (non complete) example of what i'm talking about. The benefit of this approach is that you can vary the amount of objects that make up a character. So you wont waist memory if you don't need it (by leaving Field's empty/unused). Also, you're more flexible in using the same functions for different types of characters. So you wont have to write a new system when you need a character with 6 arms for example.

OR, you can use your animation system on entities other than characters like a rigged 'mechanical machine' for example, or whatever..

You'll notice in this way functions like PlayFrame() and LoadAnimation() are seriously shorter since you can use a simple for..next loop to read/write/load/save ALL keyframes instead of manually having to type each limb's name (head\foot\etc).

Const animation_keyBankSize = (30 * 6) * 4		; 30 limbs, 6 coordinates each (3 for pos, 3 for rot), 4 bytes per float

Type animation

	Field name$
	Field frame%
	Field max_frames%
	Field start_frame%
	Field end_frame%
	Field looping  ;stored on load

	Field keyBank								; memory bank holding keyframes for (30 objects * 6 coordinates)
	
End Type

Type model
	Field meshFilename$
	Field textureFilename$
	Field numMeshes
	Field meshBank								; memory bank holding 30 entity handles
End Type

Const model_meshBankSize = 30 * 4				; 30 objects, 4 bytes for (integer) entity handle

;-----

Function playframe(model$,frame)


	keyOffset = 0

	For m.model = Each model
		If m\name = model Then
			a.animation = Object.animation( animArray(frame) )
			;for every limb in a model
			For numMesh = 0 To m\numMeshes-1
				;get mesh handle from model's meshbank
				mesh = PeekInt(m\meshBank, numMesh * 4)
				;apply coordinates from animation's keybank
				PositionEntity mesh, PeekFloat(a\keyBank, keyOffset), PeekFloat(a\keyBank, keyOffset+4), PeekFloat(a\keyBank, keyOffset+8), 1
				RotateEntity mesh, PeekFloat(a\keyBank, keyOffset+12), PeekFloat(a\keyBank, keyOffset+16), PeekFloat(a\keyBank, keyOffset+20), 1
				;increment offset for next record/entity
				keyOffset = keyOffset + 24
			Next
			;model was found, exit function
			Return
		EndIf
	Next

End Function

Function addMeshToModel( m.model, meshHandle )

	If m=Null Then RuntimeError "addMeshToModel: Invalid mesh!"

	If m\meshBank = 0 Then m\meshBank = CreateBank()
	
	;increase bank size
	m\numMeshes = m\numMeshes + 1
	ResizeBank m\meshBank, m\numMeshes * 4
	
	;store mesh handle
	PokeInt m\meshBank, m\numMeshes*4, meshHandle

End Function

Function loadAnimations(filename$)

	filein = ReadFile("animations\"+filename$+".ani")
	tot_anims = ReadInt(filein)
	tot_entities = ReadInt(filein)		; NEW!
	total_frames = tot_anims
	
	start_frame = animations_loaded
	end_frame = animations_loaded + tot_anims
	
	For i = 0 To tot_anims
		;add anim
		a.animation = New animation
		a\startframe = start_frame
		a\end_frame = end_frame
		max_frames = ReadInt(filein)
		a\name$ = ReadString(filein)
		a\looping = looping
		a\frame = ReadInt(filein)
		
		;create bank to hold keyframes
		a\keybank = CreateBank( (tot_entities * 6) * 4 )
		;read keyframes
		For j = 0 To tot_entities
			offset = (j * 6) * 4		; 6 coordinates per object, 4 bytes per float
			PokeFloat a\keybank, offset, ReadFloat(filein)
		Next
		
		animArray(animations_loaded) = Handle(a)
		animations_loaded = animations_loaded+1
		
	Next

	CloseFile filein

End Function


Danny
Finger Liberation Front


Danny(Posted 2007) [#8]
What will also gain you a tiny bit of speed (but can seriously ADD up with 100's of models) is by using a model's Type HANDLE() in stead of manually searching (!) the model by comparing each name with strings..

Currently, to find the 100th model, this for..next loop has to go through 99 models before it finally FINDS the one you want (wasting 99 loops).
function playframe(modelname$,frame)

   for m.model = each model
      if m\modelname$ = modelname$ then

         ... anim code here ...

      end if
   next

end function
Instead, you can simply access the model DIRECTLY and INSTANTLY! (no loops!) by using the model type's Handle() like this:
function playframe (modelHandle, frame)

   m.model = object.model(modelHandle)
   if m = null then runtimeerror "playFrame: Invalid model handle passed: "+modelhandle ; (validattion)

   ... anim code here ...

end function

D.