Copy bone anim script: Euler trouble

Posted By: Superku

Copy bone anim script: Euler trouble - 04/12/17 19:12

Hello!
Every now and then I need to copy the current bones animation of one entity to that of a "clone" entity. Therefore I've written a basic script which tries to do so, yet a few bones are rotating weirdly at some points in the animation.
Either there's something wrong with my code/ concept or it's an Euler angle issue - hate those!

The script:
Code:
///////////////////////////////
#include <acknex.h>
#include <default.c>
///////////////////////////////

MATERIAL* anim_copy_mat =
{
	effect = "anim_copy_mat.fx";
	flags = AUTORELOAD;
}

void ent_anim_copy(ENTITY* eTarget, ENTITY* eSource)
{
	int i,numBones,hparent;
	ANGLE angle,angle2,angle3,eSourceOldPan;

	numBones = ent_bones(eSource);
	ent_bonereset_all(eTarget);
	vec_set(&eSourceOldPan,&(eSource->pan));
	vec_set(&(eSource->pan),nullvector); // ang_for_bone returns global angles
	for(i = 0; i < numBones; i++)
	{
		ang_for_bone(&angle,eSource,i+1); // use bone handles/ indices instead of names
		hparent = ent_boneparent(eSource,NULL,i+1);
		if(hparent > 0)
		{
			ang_for_bone(&angle2,eSource,hparent); // eTarget's bone is already rotated by parent bone's rotation => undo it!
			ang_diff(&angle3,nullvector,&angle2); // best way to do this? I doubt it.
			ang_add(&angle,&angle3);
		}
		ent_bonerotate(eTarget,i+1,&angle);
	}
	vec_set(&(eSource->pan),&eSourceOldPan);
}

void main()
{
	fps_max = 60;
	video_mode = 10;
	d3d_antialias = 9;
	level_load(NULL);
	me = ent_create("robot.mdl",vector(450,120,0),NULL);
	my.material = anim_copy_mat;
	you = ent_create("robot.mdl",vector(450,-120,0),NULL);
	your.material = anim_copy_mat;

	while(1)
	{
		my.pan += time_step;
		my.skill20 += 10*time_step;
		my.skill20 %= 360;
		my.skill21 = sinv(my.skill20)*50+50;
		ent_animate(me,"bjump",my.skill20,0);
		my.skill22 += 30*time_step;
		my.skill22 %= 360;
		my.skill23 = sinv(my.skill22)*50;
		ent_bonerotate(my,"Bone6",vector(0,my.skill23,0));
		ent_bonerotate(my,"Bone8",vector(0,-my.skill23,0));
		ent_bonerotate(my,"Bone1",vector(my.skill20,0,0));
		
		your.pan = my.pan;
		ent_anim_copy(you,me);
		
		time_factor = 1-0.9*key_ctrl;
		
		wait(1);
	}
}



If you don't have a lot of things better to do than to give this a try I'd like to hear your thoughts on this. Thanks!
Posted By: alibaba

Re: Copy bone anim script: Euler trouble - 04/15/17 11:23

Well testing with some models of mine does not cause any unwanted behaviour, the bones are copied as expected. The code looks good as well. I could not reproduce the problem.

If I had to take a guess I'd say it's an euler problem caused by the animation of your model, which somehow ends in a gimbal lock.
Posted By: Superku

Re: Copy bone anim script: Euler trouble - 04/15/17 12:59

Alright, thanks for your feedback and testing!

However, I noticed a problem with that code, that is temporarily setting eSource's orientation to zero. Subsequent ang_for_bone instructions can fail then, seemingly at random (they take the wrong pan value in consideration).
I'm 99% sure I've once read a post by jcl telling someone that he should not change pan multiple times per frame, as some stuff is based on matrices or whatever internally and information can get lost then. I could not find that post anymore though.

As a workaround or fix I need a few ang_add() operations sadly (those which add eSourcePanInv):

Code:
void ent_anim_copy(ENTITY* eTarget, ENTITY* eSource)
{
	int i,numBones,hparent;
	ANGLE angle,angle2,angle3,eSourcePanInv;

	numBones = ent_bones(eSource);
	ent_bonereset_all(eTarget);
	ang_diff(&eSourcePanInv,nullvector,&(eSource->pan));
	for(i = 0; i < numBones; i++)
	{
		ang_for_bone(&angle,eSource,i+1); // use bone handles/ indices instead of names
		ang_add(&angle,&eSourcePanInv);
		hparent = ent_boneparent(eSource,NULL,i+1);
		if(hparent > 0)
		{
			ang_for_bone(&angle2,eSource,hparent); // eTarget's bone is already rotated by parent bone's rotation => undo it!
			ang_add(&angle2,&eSourcePanInv);
			ang_diff(&angle3,nullvector,&angle2); // best way to do this? I doubt it.
			ang_add(&angle,&angle3);
		}
		ent_bonerotate(eTarget,i+1,&angle);
	}
}

© 2024 lite-C Forums