IK with Ogre

Discussion area about developing or extending OGRE, adding plugins for it or building applications on it. No newbie questions please, use the Help forum for that.
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Post by noche »

xavier wrote:If you need help analyzing the problem, go ahead and post again some of the code in question.
Thanks I'll surely post some more question.
xavier wrote:I hope in the end that you plan to submit a patch to Ogre for this -- it really does solve a whole set of problems that the current Ogre animation system cannot address.
Yes, I certainly will for the procedural animation track as I posted above. But the i'm not sure my IK solver worst to be in an Ogre patch. It a very simple IK Limb solver inspiered from 3dsmax and this nice article :
http://www.3dkingdoms.com/ik.htm

It only resolves arms and legs IK but it was enough for me.
It plan to add other type of controllers as LookAt, Orientation and Position constraint.

Falagard wrote:So, for each bone you have to set up joint constraints - only rotate along a certain axis within certain angles, etc.[/qutote]
I assume this could be used for walking on uneven ground so the feet touch the ground at the proper place, as well as for things like turning the head in a direction of interest, such as something you can pick up, or turning the whole torso for something like.. aiming a gun?
That's exactly why i'm doing this :)
Kencho wrote::cry: Can't see the video... What codec have you used?
Sorry I used Cam Studio and didnt change the codec (Microsoft video 1)
Next time I will.
User avatar
tuan kuranes
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 2653
Joined: Wed Sep 24, 2003 8:07 am
Location: Haute Garonne, France
x 4
Contact:

Post by tuan kuranes »

Nice results !

I was hoping ... store the initial world transformation
Bone::_getBindingPoseInverseScale,
Bone::_getBindingPoseInversePosition, Bone::_getBindingPoseInverseOrientation ?


Here's some recent and nice post about mixing procedural and modeled animation, IK and so on from a very talented game industry expert :

http://www.codercorner.com/IK.htm

About skeletal animation, just for pleasure eyes, a nice paper + code + source on a nice and new way to do Skeletal animation :

Deformation Styles for Spline-based Skeletal Animation

Note that R2VB is not supported currently in ogre, as prior nvidia drivers didnt support it, but that may be a good argument ;)
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Post by noche »

What is R2VB ?
User avatar
xavier
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 9481
Joined: Fri Feb 18, 2005 2:03 am
Location: Dublin, CA, US
x 22

Post by xavier »

Do you need help? What have you tried?

Image

Angels can fly because they take themselves lightly.
User avatar
Alexander
Gremlin
Posts: 175
Joined: Sat Aug 05, 2006 3:55 am

Post by Alexander »

Just found this thread but great work!
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Post by noche »

Hello,

The way Prodedural Animation track works have some benefits like being able to blend IK and animation using animation state weights (to smooth transition from IK to aimation), but the main drawback is when you want the IK to work exactly over a standard animation.

Let's say I have 2 animations :
- A walk animation.
- A procedural IK animation that calculate the keyframe for the arm to reach a destination point.

Both animations are based on the initial skeleton pose, and thats the main issue. The IK animation doesn't know that another animation is played (the walk animation). The IK calculate the shoulder and elbow angle for the arm to reach the target, based on the initial pose, as if no other animation was played.

The 2 animations keyframes are added together (in cumulative mode usualy). That's why the arm will not reach the destination while walking.

So I wonder if someone would have any idea on how to do this ?

To be more precise the main goal is to get the global orientation and global position after the walk animation is played, and before the IK animation is calculated.

Thanks.
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Post by noche »

Actually I'm not right.

if the Walk animation state as been enable before the IK animation state, I'm able to get the global orientation and position (updated by the walk animation) inside the IK callback.
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Post by noche »

Here is a little update : video
mklann
Kobold
Posts: 33
Joined: Sun Jan 21, 2007 7:24 pm

Santos

Post by mklann »

Hi noche and all,

I really like this IK work and I hope we can all try it out some time soon :-)

In case you haven't heared of it, I wanted to point out the digital human project, more specifically Santos: http://www.digital-humans.org/santos/

Maybe you find some good inspiration there.

One question: what's the relationship with a physically simulated ragdoll? I guess what would be good is if there was such a ragdoll defined for the character and then one could choose to define additional constraints for IK, right? In the end it should be as easy as possible to programmatically switch between IK, animations and ragdoll behavior.

Anyways, we just started recently extending our characters and I'd be happy to learn :-)

Markus
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Re: Santos

Post by noche »

mklann wrote:Maybe you find some good inspiration there.
It's seems to be a nice project. I'll keep a look on this.
Thanks
User avatar
Kencho
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4011
Joined: Fri Sep 19, 2003 6:28 pm
Location: Burgos, Spain
x 2
Contact:

Post by Kencho »

noche, an impressive video! Congrats
Image
User avatar
PolyVox
OGRE Contributor
OGRE Contributor
Posts: 1316
Joined: Tue Nov 21, 2006 11:28 am
Location: Groningen, The Netherlands
x 18
Contact:

Post by PolyVox »

Damn, that video is cool! I hope this stuff makes it into Ogre proper at some point!
KuRi
Goblin
Posts: 242
Joined: Wed Jul 05, 2006 4:19 pm

Post by KuRi »

What a great video... Just what i would like to have in my game :D

Please integrate it with ogre skeleteon system and post it here :)

Cheers.
User avatar
Alexander
Gremlin
Posts: 175
Joined: Sat Aug 05, 2006 3:55 am

Post by Alexander »

Agreed on that. Amazing video. :D
User avatar
oddrose
Orc
Posts: 470
Joined: Thu Feb 15, 2007 2:08 pm
Location: Gothenburg, Sweden
Contact:

Post by oddrose »

Really nice work!
User avatar
Jules Robichaud Gagnon
Goblin
Posts: 227
Joined: Thu Jan 18, 2007 2:13 pm
Location: Chicoutimi, Québec

Post by Jules Robichaud Gagnon »

What is the current status of this project? I am very interested in this.
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Post by noche »

here is an update : here or youtube
Maybe Jules Robichaud Gagnon will be able to help since I gave him the code ;)
Last edited by noche on Fri Mar 14, 2008 12:33 am, edited 2 times in total.
User avatar
Jules Robichaud Gagnon
Goblin
Posts: 227
Joined: Thu Jan 18, 2007 2:13 pm
Location: Chicoutimi, Québec

Post by Jules Robichaud Gagnon »

Since it is a very promising addon, i will make sure to provide this to the community in the name of noche.

I should have something by the end of next week. I am busy on something else for the moment.
User avatar
Falagard
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 2060
Joined: Thu Feb 26, 2004 12:11 am
Location: Toronto, Canada
x 3
Contact:

Post by Falagard »

noche wrote:here is an update : here or youtube
Maybe Jules Robichaud Gagnon will be able to since I gave him the code ;)
Awesome video, very impressive!
User avatar
Jules Robichaud Gagnon
Goblin
Posts: 227
Joined: Thu Jan 18, 2007 2:13 pm
Location: Chicoutimi, Québec

Post by Jules Robichaud Gagnon »

Hello everyone, i send you now what noche gave me.
Here's the patch i made :
http://sourceforge.net/tracker/index.ph ... tid=302997


He had binded the code to Ogre with Lua but you should get the idea of what happens:

First take a look at "BipedController.lua"

To create a procedural track you have to create first a ProceduralTrackController, the method virtual void getInterpolatedKeyFrame( ) will be called when the animation will need to be rendered. You need to fill the value of the keyframe when this is called.

Then you can create an animation Track with ProceduralAnimationTrack.

Important thing: You must create the controllers in the orders of the Parents to childs. Spine -> Arm -> lower hand -> hand because they will be called in the order they are created.

Those are a resume of the notes i took when he explained to me. I will now try this out and add more details.

Stay tuned.


BipedController.lua

Code: Select all

-------------------------------------------------------------------------------
Class "BipedController"

BipedController.XyzToYzxRotation = Quaternion.FromAngleAxis(HalfPi, Vector3(0, 0, 1)) * Quaternion.FromAngleAxis(Pi, Vector3(1, 0, 0))

-------------------------------------------------------------------------------
function BipedController:Construct(sceneManager, entity)
	self.entity				= entity	
	self.node				= entity:GetParentNode()
	self.skeleton			= entity:GetSkeleton()
	
	self.skeleton:SetBlendMode("AnimblendCumulative")
	
	self.bones  =
	{
		LeftUpperarm	= self.skeleton:GetBone("BipedLeftUpperarm"),
		LeftForearm		= self.skeleton:GetBone("BipedLeftForearm"),
		LeftHand		= self.skeleton:GetBone("BipedLeftHand"),
		RightUpperarm	= self.skeleton:GetBone("BipedRightUpperarm"),
		RightForearm	= self.skeleton:GetBone("BipedRightForearm"),
		RightHand		= self.skeleton:GetBone("BipedRightHand"),
		LeftThigh		= self.skeleton:GetBone("BipedLeftThigh"),
		LeftCalf		= self.skeleton:GetBone("BipedLeftCalf"),
		LeftFoot		= self.skeleton:GetBone("BipedLeftFoot"),
		RightThigh		= self.skeleton:GetBone("BipedRightThigh"),
		RightCalf		= self.skeleton:GetBone("BipedRightCalf"),
		RightFoot		= self.skeleton:GetBone("BipedRightFoot"),
		head			= self.skeleton:GetBone("BipedHead"),
		pelvis			= self.skeleton:GetBone("BipedPelvis"),		
		spine1			= self.skeleton:GetBone("BipedSpine1"),
		spine2			= self.skeleton:GetBone("BipedSpine2"),
		spine3			= self.skeleton:GetBone("BipedSpine3"),
	}
--	self.bones  =
--	{
--		LeftUpperarm	= self.skeleton:GetBone("R65_BA_L_UpperArm"),
--		LeftForearm		= self.skeleton:GetBone("R65_BA_L_Forearm"),
--		LeftHand		= self.skeleton:GetBone("R65_BA_L_Hand"),
--		RightUpperarm	= self.skeleton:GetBone("R65_BA_R_UpperArm"),
--		RightForearm	= self.skeleton:GetBone("R65_BA_R_Forearm"),
--		RightHand		= self.skeleton:GetBone("R65_BA_R_Hand"),
--		LeftThigh		= self.skeleton:GetBone("R65_BA_L_Thigh"),
--		LeftCalf		= self.skeleton:GetBone("R65_BA_L_Calf"),
--		LeftFoot		= self.skeleton:GetBone("R65_BA_L_Foot"),
--		RightThigh		= self.skeleton:GetBone("R65_BA_R_Thigh"),
--		RightCalf		= self.skeleton:GetBone("R65_BA_R_Calf"),
--		RightFoot		= self.skeleton:GetBone("R65_BA_R_Foot"),
--		head			= self.skeleton:GetBone("R65_BA_Head"),
--		pelvis			= self.skeleton:GetBone("R65_BA_Pelvis"),		
--		spine1			= self.skeleton:GetBone("R65_BA_Spine"),
--		spine2			= self.skeleton:GetBone("R65_BA_Spine1"),
--		spine3			= self.skeleton:GetBone("R65_BA_Spine2"),
--	}	
	self.manipulators = {}
	self.manipulators.leftLegTarget 	= sceneManager:CreateSceneNode()
	self.manipulators.rightLegTarget 	= sceneManager:CreateSceneNode()
	self.manipulators.leftLegSwivel 	= sceneManager:CreateSceneNode()
	self.manipulators.rightLegSwivel 	= sceneManager:CreateSceneNode()
	self.manipulators.spineTarget 		= sceneManager:CreateSceneNode()
	self.manipulators.headTarget 		= sceneManager:CreateSceneNode()
	self.manipulators.leftArmTarget 	= sceneManager:CreateSceneNode()
	self.manipulators.rightArmTarget 	= sceneManager:CreateSceneNode()
	self.manipulators.leftArmSwivel 	= sceneManager:CreateSceneNode()
	self.manipulators.rightArmSwivel 	= sceneManager:CreateSceneNode()
	self.manipulators.leftFootTarget	= sceneManager:CreateSceneNode()
	self.manipulators.rightFootTarget	= sceneManager:CreateSceneNode()
	self.manipulators.leftHandTarget	= sceneManager:CreateSceneNode()
	self.manipulators.rightHandTarget	= sceneManager:CreateSceneNode()

	self.leftArmController 	= LimbController:New(self.node, 
												self.bones.LeftUpperarm, 
												self.bones.LeftForearm, 
												self.bones.LeftHand, 
												Vector3.UnitY, 
												self.manipulators.leftArmTarget, 
												self.manipulators.leftArmSwivel,
												Quaternion.Identity,
												Vector3(1, 0, 0))
												
	self.rightArmController = LimbController:New(self.node, 
												 self.bones.RightUpperarm, 
												 self.bones.RightForearm, 
												 self.bones.RightHand, 
												 Vector3.UnitY, 
												 self.manipulators.rightArmTarget, 
												 self.manipulators.rightArmSwivel, 
												 Quaternion.FromAngleAxis(Pi, Vector3(0, 0, 1)),
												 Vector3(-1, 0, 0))
												 
	self.leftLegController 	= LimbController:New(self.node, 
												 self.bones.LeftThigh, 
												 self.bones.LeftCalf, 
												 self.bones.LeftFoot, 
												 Vector3.UnitY, 
												 self.manipulators.leftLegTarget, 
												 self.manipulators.leftLegSwivel, 
												 Quaternion.FromAngleAxis(Pi, Vector3(0, 1, 0)) * Quaternion.FromAngleAxis(-Pi / 2, Vector3(0, 0, 1)),
												 Vector3(0, -1, 0))
												 
	self.rightLegController = LimbController:New(self.node, 
												 self.bones.RightThigh, 
												 self.bones.RightCalf, 
												 self.bones.RightFoot, 
												 Vector3.UnitY, 
												 self.manipulators.rightLegTarget, 
												 self.manipulators.rightLegSwivel,
												 Quaternion.FromAngleAxis(Pi, Vector3(0, 1, 0)) * Quaternion.FromAngleAxis(-Pi / 2, Vector3(0, 0, 1)),
												 Vector3(0, -1, 0))
												 
	self.headController 	= LookAtController:New(self.node, 
												   self.bones.head, 
												   self.manipulators.headTarget,  
												   0, 
												   true, 
												   "yxz")
												   
	self.spineController 	= SpineController:New(self.node, 
												  self.bones.pelvis, 
												  self.bones.spine1, 
												  self.bones.spine2, 
												  self.bones.spine3, 
												  self.manipulators.spineTarget, 
												  0, 
												  false, 
												  "zxy")
	
	self.leftFootController = OrientationController:New(self.node, 
												  self.bones.LeftFoot, 
												  self.manipulators.leftFootTarget, 
												  false, 
												  "xyz")

	self.rightFootController = OrientationController:New(self.node, 
												  self.bones.RightFoot, 
												  self.manipulators.rightFootTarget, 
												  false, 
												  "xyz")
												  
	self.leftHandController = OrientationController:New(self.node, 
												  self.bones.LeftHand, 
												  self.manipulators.leftHandTarget, 
												  false, 
												  "xyz")
												  
	self.rightHandController = OrientationController:New(self.node, 
												  self.bones.RightHand, 
												  self.manipulators.rightHandTarget, 
												  false, 
												  "xyz")

	self.leftFootAnimation 	= self.skeleton:CreateAnimation("BipedControllerLeftFoot", 1)
	self.rightFootAnimation = self.skeleton:CreateAnimation("BipedControllerRightFoot", 1)
	self.leftHandAnimation 	= self.skeleton:CreateAnimation("BipedControllerLeftHand", 1)
	self.rightHandAnimation = self.skeleton:CreateAnimation("BipedControllerRightHand", 1)
	self.BRAND NAME 	= self.skeleton:CreateAnimation("BipedControllerLeftArm", 1)
	self.BRAND NAME 	= self.skeleton:CreateAnimation("BipedControllerRightArm", 1)
	self.leftLegAnimation 	= self.skeleton:CreateAnimation("BipedControllerLeftLeg", 1)
	self.rightLegAnimation	= self.skeleton:CreateAnimation("BipedControllerRightLeg", 1)
	self.headAnimation		= self.skeleton:CreateAnimation("BipedControllerHead", 1)
	self.spineAnimation		= self.skeleton:CreateAnimation("BipedControllerSpine", 1)

	self.entity:RefreshAvailableAnimationState()

	self.leftFootAnimationState 	= self.entity:GetAnimationState("BipedControllerLeftFoot")
	self.rightFootAnimationState 	= self.entity:GetAnimationState("BipedControllerRightFoot")	
	self.leftHandAnimationState 	= self.entity:GetAnimationState("BipedControllerLeftHand")
	self.rightHandAnimationState 	= self.entity:GetAnimationState("BipedControllerRightHand")	
	self.BRAND NAME 		= self.entity:GetAnimationState("BipedControllerLeftArm")
	self.BRAND NAME 	= self.entity:GetAnimationState("BipedControllerRightArm")
	self.leftLegAnimationState 		= self.entity:GetAnimationState("BipedControllerLeftLeg")
	self.rightLegAnimationState 	= self.entity:GetAnimationState("BipedControllerRightLeg")
	self.headAnimationState 		= self.entity:GetAnimationState("BipedControllerHead")
	self.spineAnimationState 		= self.entity:GetAnimationState("BipedControllerSpine")
	
	self.spineAnimationState:SetEnabled(true)
	self.BRAND NAME:SetEnabled(true)
	self.BRAND NAME:SetEnabled(true)
	self.leftLegAnimationState:SetEnabled(true)
	self.rightLegAnimationState:SetEnabled(true)
	self.headAnimationState:SetEnabled(true)
	self.leftFootAnimationState:SetEnabled(true)
	self.rightFootAnimationState:SetEnabled(true)
	self.leftHandAnimationState:SetEnabled(true)
	self.rightHandAnimationState:SetEnabled(true)
	
	self.spineAnimationState:SetWeight(1.0)	
	self.leftFootAnimationState:SetWeight(1.0)
	self.rightFootAnimationState:SetWeight(1.0)
	self.leftHandAnimationState:SetWeight(1.0)
	self.rightHandAnimationState:SetWeight(1.0)
	self.BRAND NAME:SetWeight(1.0)
	self.BRAND NAME:SetWeight(1.0)
	self.leftLegAnimationState:SetWeight(1.0)
	self.rightLegAnimationState:SetWeight(1.0)	
	self.headAnimationState:SetWeight(1.0)	

		
	self.rightFootAnimation:CreateProceduralTrack(self.rightFootController.bone, self.rightFootController)
	self.leftFootAnimation:CreateProceduralTrack(self.leftFootController.bone, self.leftFootController)
	
	self.rightHandAnimation:CreateProceduralTrack(self.rightHandController.bone, self.rightHandController)
	self.leftHandAnimation:CreateProceduralTrack(self.leftHandController.bone, self.leftHandController)
	
	self.BRAND NAME:CreateProceduralTrack(self.leftArmController.boneStart, self.leftArmController)
	self.BRAND NAME:CreateProceduralTrack(self.leftArmController.boneMid, self.leftArmController)
	self.BRAND NAME:CreateProceduralTrack(self.rightArmController.boneStart, self.rightArmController)
	self.BRAND NAME:CreateProceduralTrack(self.rightArmController.boneMid, self.rightArmController)	
	
	self.leftLegAnimation:CreateProceduralTrack(self.leftLegController.boneStart, self.leftLegController)
	self.leftLegAnimation:CreateProceduralTrack(self.leftLegController.boneMid, self.leftLegController)
	self.rightLegAnimation:CreateProceduralTrack(self.rightLegController.boneStart, self.rightLegController)
	self.rightLegAnimation:CreateProceduralTrack(self.rightLegController.boneMid, self.rightLegController)
	
	self.headAnimation:CreateProceduralTrack(self.headController.bone, self.headController)
	self.spineAnimation:CreateProceduralTrack(self.spineController.bonePelvis, self.spineController)
	self.spineAnimation:CreateProceduralTrack(self.spineController.boneSpine1, self.spineController)
	self.spineAnimation:CreateProceduralTrack(self.spineController.boneSpine2, self.spineController)
	self.spineAnimation:CreateProceduralTrack(self.spineController.boneSpine3, self.spineController)
	
	-- manipulators for debug

	self:ResetManipulators()
	
end


-------------------------------------------------------------------------------
function BipedController:Destroy()

	self.leftFootAnimation 	= self.skeleton:RemoveAnimation("BipedControllerLeftFoot")
	self.rightFootAnimation = self.skeleton:RemoveAnimation("BipedControllerRightFoot")
	self.leftHandAnimation 	= self.skeleton:RemoveAnimation("BipedControllerLeftHand")
	self.rightHandAnimation = self.skeleton:RemoveAnimation("BipedControllerRightHand")
	self.BRAND NAME 	= self.skeleton:RemoveAnimation("BipedControllerLeftArm")
	self.BRAND NAME 	= self.skeleton:RemoveAnimation("BipedControllerRightArm")
	self.leftLegAnimation 	= self.skeleton:RemoveAnimation("BipedControllerLeftLeg")
	self.rightLegAnimation	= self.skeleton:RemoveAnimation("BipedControllerRightLeg")
	self.headAnimation		= self.skeleton:RemoveAnimation("BipedControllerHead")
	self.spineAnimation		= self.skeleton:RemoveAnimation("BipedControllerSpine")
	
	self.entity:RefreshAvailableAnimationState()
	
	self.manipulators.leftLegTarget:RemoveAndDestroy()
	self.manipulators.rightLegTarget:RemoveAndDestroy()
	self.manipulators.leftLegSwivel:RemoveAndDestroy()
	self.manipulators.rightLegSwivel:RemoveAndDestroy()
	self.manipulators.spineTarget:RemoveAndDestroy()
	self.manipulators.headTarget:RemoveAndDestroy()
	self.manipulators.leftArmTarget:RemoveAndDestroy()
	self.manipulators.rightArmTarget:RemoveAndDestroy()
	self.manipulators.leftArmSwivel:RemoveAndDestroy()
	self.manipulators.rightArmSwivel:RemoveAndDestroy()
	self.manipulators.leftFootTarget:RemoveAndDestroy()
	self.manipulators.rightFootTarget:RemoveAndDestroy()
	self.manipulators.leftHandTarget:RemoveAndDestroy()
	self.manipulators.rightHandTarget:RemoveAndDestroy()
end

-------------------------------------------------------------------------------
function BipedController:Update(dt)
	self.leftFootAnimationState:AddTime(dt)
	self.rightFootAnimationState:AddTime(dt)
	self.leftHandAnimationState:AddTime(dt)
	self.rightHandAnimationState:AddTime(dt)
	self.BRAND NAME:AddTime(dt)
	self.BRAND NAME:AddTime(dt)
	self.leftLegAnimationState:AddTime(dt)
	self.rightLegAnimationState:AddTime(dt)
	self.headAnimationState:AddTime(dt)	
	self.spineAnimationState:AddTime(dt)
	
	self.leftFootAnimationState:SetEnabled(true)
	self.rightFootAnimationState:SetEnabled(true)
	self.spineAnimationState:SetEnabled(true)
	self.BRAND NAME:SetEnabled(true)
	self.BRAND NAME:SetEnabled(true)
	self.leftHandAnimationState:SetEnabled(true)
	self.rightHandAnimationState:SetEnabled(true)
	self.leftLegAnimationState:SetEnabled(true)
	self.rightLegAnimationState:SetEnabled(true)
	self.headAnimationState:SetEnabled(true)
end

-------------------------------------------------------------------------------
function BipedController:SetWeight(weight)
--	self.leftFootAnimationState:SetWeight(weight)
--	self.rightFootAnimationState:SetWeight(weight)
	self.leftHandAnimationState:SetWeight(weight)
	self.rightHandAnimationState:SetWeight(weight)
	self.BRAND NAME:SetWeight(weight)
	self.BRAND NAME:SetWeight(weight)
	self.leftLegAnimationState:SetWeight(weight)
	self.rightLegAnimationState:SetWeight(weight)	
	self.spineAnimationState:SetWeight(weight)
	self.headAnimationState:SetWeight(weight)
end

-------------------------------------------------------------------------------
function BipedController:ResetManipulators()
	local offset = self.node:GetWorldPosition()
	local offsetRot = self.node:GetWorldOrientation()
	
	self.manipulators.leftFootTarget:SetWorldOrientation(Quaternion.FromAngleAxis(DegToRad(20), Vector3(0, 1, 0)) * self.bones.LeftFoot:GetWorldOrientation())
	self.manipulators.rightFootTarget:SetWorldOrientation(Quaternion.FromAngleAxis(DegToRad(-20), Vector3(0, 1, 0)) * self.bones.RightFoot:GetWorldOrientation())
	self.manipulators.leftHandTarget:SetWorldOrientation(Quaternion.FromAngleAxis(DegToRad(20), Vector3(0, 1, 0)) * self.bones.LeftHand:GetWorldOrientation())
	self.manipulators.rightHandTarget:SetWorldOrientation(Quaternion.FromAngleAxis(DegToRad(-20), Vector3(0, 1, 0)) * self.bones.RightHand:GetWorldOrientation())
	
	self.manipulators.leftLegTarget:SetWorldPosition(self.bones.LeftFoot:GetWorldPosition() + offset)
	self.manipulators.rightLegTarget:SetWorldPosition(self.bones.RightFoot:GetWorldPosition() + offset)
	self.manipulators.leftLegSwivel:SetWorldPosition(self.bones.LeftCalf:GetWorldPosition() + Vector3(1, 0, 1) + offset)
	self.manipulators.rightLegSwivel:SetWorldPosition(self.bones.RightCalf:GetWorldPosition() + Vector3(-1, 0, 1) + offset)
	self.manipulators.leftArmTarget:SetWorldPosition(self.bones.LeftHand:GetWorldPosition() + offset)
	self.manipulators.rightArmTarget:SetWorldPosition(self.bones.RightHand:GetWorldPosition() + offset)
	self.manipulators.leftArmSwivel:SetWorldPosition(self.bones.LeftForearm:GetWorldPosition() + Vector3(1, 0, -1) + offset)
	self.manipulators.rightArmSwivel:SetWorldPosition(self.bones.RightForearm:GetWorldPosition() + Vector3(-1, 0, -1) + offset)
	self.manipulators.spineTarget:SetWorldPosition(self.bones.head:GetWorldPosition() + offset)
	self.manipulators.headTarget:SetWorldPosition(self.bones.head:GetWorldPosition() + Vector3(0, 0, 5) + offset)
end




LimbController.lua

Code: Select all

-------------------------------------------------------------------------------
Class "LimbController"

-------------------------------------------------------------------------------
function LimbController:Construct(sceneNode, boneStart, boneMid, boneEnd, axis, targetNode, swivelNode, offset, shoulderAxis)
	self.sceneNode	= sceneNode
	self.boneStart 	= boneStart
	self.boneMid  	= boneMid
	self.boneEnd  	= boneEnd
	self.axis 		= axis
	
	self.targetNode		= targetNode
	self.swivelNode		= swivelNode
	self.swivelAngle 	= 0
	self.useAngle 		= true
	self.offset			= offset
	
	self.shoulderAxis 		= shoulderAxis or Vector3(0, 1, 0)
	
	self.boneStartRotation 	= Quaternion.Identity
	self.boneMidRotation 	= Quaternion.Identity
end

-------------------------------------------------------------------------------
function LimbController:OnGetInterpolatedKeyFrame(key, node)
	self:CalcRotation()
	
	if (node == self.boneStart) then
		key:SetRotation(self.boneStartRotation)
	elseif (node == self.boneMid) then
		key:SetRotation(self.boneMidRotation)
	end

end

-------------------------------------------------------------------------------
function LimbController:CalcStartRotation(origin, target, constraint)
	local at = (target - origin):Normalize()
	local up = (origin - constraint):CrossProduct(at):Normalize()
	--local up = at:Perpendicular(Vector3.UnitX)
	local right = at:CrossProduct(up):Normalize()
	return Quaternion.FromAxes(at, up, right)-- * Quaternion.FromAngleAxis(0, Vector3.UnitX)
end

-------------------------------------------------------------------------------
function LimbController:CalcRotation()
	
	local localTarget = self.targetNode:GetWorldPosition()
	local localSwivel = self.swivelNode:GetWorldPosition()
	
	-- Calc each frame the length of start and mid bones as bone may be strechable.
	local startLen = self.boneMid:GetPosition():Length()
	local midLen = self.boneEnd:GetPosition():Length()
	
	-- Get the start bone current world position.
	local boneStartWorldPosition = self.sceneNode:GetWorldTransforms() * self.boneStart:GetWorldPosition()
    local targetDistanceToStart = localTarget:Distance(boneStartWorldPosition)


	-- Calc the rotation for the mid bone	
	local boneMidRotationAngle = - (Pi - EuclidianAngle(targetDistanceToStart, startLen, midLen)) 
	self.boneMidRotation = self.boneMid:GetOrientation():Inverse()
						   * Quaternion.FromAngleAxis(boneMidRotationAngle, self.axis)

	-- Calc a first rotation for the start bone.
	-- At this point the start bone lookat directly at the target, and is constraint by the swivel target
	self.boneStartRotation = self:CalcStartRotation(boneStartWorldPosition, localTarget, localSwivel)
	
	
	-- Calc the end bone world position after applying start and mid new rotations
	local boneMidWorldPosition = boneStartWorldPosition + self.boneStartRotation * self.boneMid:GetPosition()
	local boneMidWorldRotation = self.boneStartRotation * self.boneMidRotation * self.boneMid:GetOrientation()
	local boneEndWorldPosition = boneMidWorldPosition + boneMidWorldRotation * self.boneEnd:GetPosition()
	
	-- Calc the start bone rotation offset according to the end bone global position
	local boneStartRotationOffset = EuclidianAngle(boneEndWorldPosition:Distance(localTarget), 
												   boneEndWorldPosition:Distance(boneStartWorldPosition), 
												   targetDistanceToStart)

	--tracer:Axis(self.boneStart:GetWorldPosition() + self.sceneNode:GetWorldPosition(), self.boneStartRotation, 0.8)
	
	-- Calc the final start bone rotation, by adding the offset, and by taking count of its original world orientation
	self.boneStartRotation = self.boneStart:GetWorldOrientation():Inverse() * self.sceneNode:GetWorldOrientation():Inverse()
							* self.boneStartRotation 
							* Quaternion.FromAngleAxis(boneStartRotationOffset, self.axis)

	
	--tracer:Axis(boneMidWorldPosition, boneMidWorldOrientation, 0.1)
	--tracer:Axis(boneEndWorldPosition, boneMidWorldOrientation, 0.1)
	--tracer:Axis(self.boneStart:GetWorldPosition(), self.boneStart:GetWorldOrientation(), 0.5)

end

-------------------------------------------------------------------------------
function LimbController:GetInitialTargetPosition()
	return self.boneEnd:GetPosition()
end



LookAtController.lua

Code: Select all

-------------------------------------------------------------------------------
Class "LookAtController"

-------------------------------------------------------------------------------
function LookAtController:Construct(sceneNode, bone, targetNode, rollAngle, flip, mapping)
	self.sceneNode	= sceneNode
	self.bone 		= bone
	self.sign 		= Ternary(flip, -1, 1)
	self.targetNode = targetNode
	self.rotation 	= Quaternion.Identity
	self.rollAngle	= rollAngle or 0
	self.mapping	= mapping or "xyz"
end

-------------------------------------------------------------------------------
function LookAtController:OnGetInterpolatedKeyFrame(key, node)
	self:CalcRotation()
	key:SetRotation(self.rotation)
end

-------------------------------------------------------------------------------
function LookAtController:CalcRotation()

	local inverseSceneNode = self.sceneNode:GetWorldOrientation():Inverse() 
	
	local globalBonePosition = self.sceneNode:GetWorldPosition() + self.bone:GetWorldPosition()
	local globalTargetPosition = self.targetNode:GetWorldPosition()
	local nullRotation = self.bone:GetWorldOrientation():Inverse() * inverseSceneNode

	local at = (globalTargetPosition - globalBonePosition):Normalize()	
	local left = at:Perpendicular(Vector3.UnitY)
	local up = left:CrossProduct(at)
	
	local lookat = Quaternion.FromAxes(left, up, -at)
	self.rotation = nullRotation * lookat * Quaternion.FromAngleAxis(0.0, Vector3(0, 1, 0))

--	g.tracer:Arrow(globalBonePosition,  globalBonePosition + at, 0.1, 0.3, Color.Red)
--	g.tracer:Axis(globalBonePosition, lookat, 0.8)
--	g.tracer:Cross(globalTargetPosition, 3, Color.Red)
end

-------------------------------------------------------------------------------
function LookAtController:GetInitialTargetPosition()
	return self.bone:GetPosition()
end
User avatar
Praetor
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 3335
Joined: Tue Jun 21, 2005 8:26 pm
Location: Rochester, New York, US
x 3
Contact:

Post by Praetor »

Very impressive indeed. The patch was just submitted, and unless sinbad wants to jump on this, I'll start looking over the patch tonight. I'm going on a little getaway this weekend, so I probably won't be able to apply it before next week.

Anyway, thanks for the work and the patch. I can't wait to look at it.
noche
Kobold
Posts: 29
Joined: Sun Apr 15, 2007 1:35 am

Post by noche »

Hi,
Sorry I had no time to release a patch myslef. Thanks a lot to Jules for releasing it.

Just a quick warning about the controllers (lookat and limb)
The lua code is based on how the biped is exported (from 3ds Max for me). If the bones orientations are different, it will look really strange.
So you may have issues if you port this code directly. I advice to make a simple controller step by step.
Start to create a LookAt controller for example, it should be very simple :

1/ First find the rotation to orient the bone globaly to identity :
nullRotation = bone:GetWorldOrientation():Inverse() * sceneNode:GetWorldOrientation():Inverse()

2/ Then construct a lookat quaternion (here be carfull about the way you exported bones. To help you draw the axis of the bone's quaternion, and another axis of your lookat quaternion.
lookatRotation = Quaternion.FromAxes(left, up, at)

3/ Then the final rotation is :
keyRotation = nullRotation * lookatRotation

The keyRotation is what you give to the callback :
OnGetInterpolatedKeyFrame(key, node)
key:SetRotation(keyRotation)
end



:arrow: If you do only 1/, check that if you orient the scene node or a spine bone, the head will be allways at a world identity orientation

:arrow: In 2/, check that the lookatQuaternion is really looking your target by drawing it. Also check that your lookat axes are in a coherent orientation according your bone axis orientation. Use Red Green Blue color to draw each axe of the quaternions. It will really help. (I have some debug drawing function for this, like Draw::Axis(const Quaternion& q, float size))

:arrow: With 3/ the bone should look to your target.

The nullRotation is needed because what the callback expect are relative transformations (according the parent bones transofrmations). It's all about keyframe, like a real animation, and that's why you can blend it with real animations.

The callback of the head must be called after the callback of the spine for the head to be aware of the spine orientation, and so, the head global position (which is needed to create a lookat controller). That's why as Jules said you need to Enable the animation state in the hierarchy order. (with SetEnable) This is how ogre orders the call of each bone keyframe.

The limb controller is a bit more complex. The goal is just to simulate an arm. All you need to know about this is in this article :
http://www.3dkingdoms.com/ik.htm
It's very fast and simple. It's not even CCD IK as you could expect.

My last advice. In my code, I calculate all the keyframe of the arm in the callback. (CalcRotation() ) This is dumb because the callback will be called 2 times on one frame, once for the UpperArm and once for the LowerArm.
And we only need to do it one time since CalcRotation() calculates at the same time the UpperArm and LowerArm orientation. So you may just check that it has not been allready calculated.

Thanks again Jules.
Cheers
User avatar
iloseall
Gremlin
Posts: 156
Joined: Sun Sep 14, 2003 3:54 am
Location: Beijing China
Contact:

Post by iloseall »

I go to watch the video, It's very Cool!

and The patch code in sf is very clear .
I think ,we have a very good start for procedural animation.

Thanks a lot.
User avatar
Praetor
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 3335
Joined: Tue Jun 21, 2005 8:26 pm
Location: Rochester, New York, US
x 3
Contact:

Post by Praetor »

I decided to tackle the animation blend masks patch first, so I've just started reviewing this one now. I moved this topic to Developer forum since this is now an active development project. It was getting buried in the Help forum.
DDd
Kobold
Posts: 30
Joined: Thu Feb 28, 2008 9:04 pm

Post by DDd »

Wow :o i didn't even noticed all the progress that had been done. Very cool stuff in that patch... has anyone done any ragdool <-> control blend examples?
Post Reply