Posing - Bone rotation with Gizmos similar to 3D Studio Max

Posted By: EpsiloN

Posing - Bone rotation with Gizmos similar to 3D Studio Max - 07/30/15 08:22

Here's a script to rotate bone joints with gizmos (like in 3D Studio Max for example).

Before anything, you should know this...
All bones rotate with local angles, which means, if you rotate the shoulder, the elbow points in a different direction, but its local orientation remains the same! There is a function to get the bone orientation, but it returns the absolute orientation, not the local one!
By using local_pan/tilt/roll skills and manipulating them each time we rotate a bone, we can keep track of the local angles of a joint.
As a side note, its not implemented yet, but we could use a temp vector to store the local bone orientation before the wait(1) in the while loop of the gizmo. Then, when the loop begins again, we can check if this local angle changed and update the bone local angle too. This way we can set a bone's local orientation to a certain value outside of the gizmo function (for example, writing rShoulderGizmo.local_pan = 45, which will set the gizmo and bone local orientation to 45 degrees.) Just bonerotate if the old (before wait(1)) angles are different than the next iteration...

In the main function, create an entity with puppet_act function, start the gizmoController() function and start propagate_rotation() which will update the gizmo orientation and local angles down the chain for every joint.

The puppet_act function initializes the gizmos, meaning, it saves all joint positions, all bone handles and 'bias' between actual bone orientation and local bone orientation. It is not currently used, but you can use it to have the real bone orientation (where the bone is pointing).

In there we create the gizmos at the specific joint position, we save the parent entity of the gizmo (puppet_act) and we save the bone handle that the gizmo controls. We also save the parent gizmo for that given gizmo, in order to orient it correctly.

Function get_gizmo_parent() returns the handle of the parent gizmo if the current gizmo has a parent gizmo, otherwise 0.

Function propagate_rotation() orients all gizmos to the real local angles of the bones, by adding the parent gizmos local angles up the chain. There is some problem with this, but I just noticed it, so its not fixed. When you rotate a gizmo, its child gizmos will rotate at a strange axis not following the mouse any more...

Function torsoRotator() is the gizmo function (it was originally for the torso, so its not renamed yet to a more general name grin ). It just follows its joint x coordinates each frame and controls its FLAG2 to be ignored if another gizmo is being controlled right now...

Function gizmoController() is the function that controls the gizmos when you press the left mouse button.

If you hold the mouse button over the gizmo it traces to find the new point (where the mouse is) and calculates an axis perpendicular to new point and the original point (where you first clicked). It then rotates around that axis by the angle between the new and original points...
If you go outside the gizmo the function builds a vector with magnitude = the distance to the last gizmo controlled (which is somewhere around the gizmo...) and then traces a vector from the gizmo's origin (x/y/z) to the mouse vector that was build. Then, it scales that new vector with magnitude gizmo.max_x (which is the radius of the gizmo if c_setminmax was called) and this last vector is the new position on the visible edge of the gizmo...

Here's the code:

Function main() :
Code:
...
	puppet = ent_create( "HumanCasualRigged.mdl" , NULLVECTOR , puppet_act );

	gizmoController();

	mouse_mode = 2;
	mouse_map = arrow_bmp;
	while(1) {
		if(mouse_right == 1) { if(mouse_mode == 2) { mouse_mode = 0; } } else { mouse_mode = 2; }

		vec_set( mouse_pos.x , mouse_cursor );

		propagate_rotation();
		wait(1);
	}
...



File def.c :
Code:
// -------------------------
// --- PUPPET AND GIZMOS ---
// -------------------------
ENTITY* puppet;
ENTITY* gizmo;


ENTITY* neckGizmoX;
ENTITY* chestGizmoX;
// Right arm
ENTITY* rsGizmoX;
ENTITY* reGizmoX;
ENTITY* rwGizmoX;
// Left arm
ENTITY* lsGizmoX;
ENTITY* leGizmoX;
ENTITY* lwGizmoX;

// Right leg
ENTITY* rhGizmoX;
ENTITY* rkGizmoX;
ENTITY* raGizmoX;
// Left leg
ENTITY* lhGizmoX;
ENTITY* lkGizmoX;
ENTITY* laGizmoX;


// Objects definitions
#define gizmoObject 1

// Gizmo properties
#define object_type skill4
#define parent_entity skill5
#define bone_handle skill6
#define local_pan skill7
#define local_tilt skil8
#define local_roll skil9

#define parent_gizmo skill40



File gizmo.c :
Code:
ENTITY* currentGizmoControlled;

function get_gizmo_parent( ENTITY* childEnt ) {
	me = childEnt;
	you = ent_next( NULL );
	while( you ) {
		if( handle( you ) == my.parent_gizmo ) {
			return you;
		}
		you = ent_next( you );
	}
	return 0;
}

function propagate_rotation() {

	ENTITY* parentGizmo;
	ENTITY* currentGizmo;

	currentGizmo = ent_next( NULL );

	while( currentGizmo ) {
		if( currentGizmo.object_type == gizmoObject ) {
			vec_set( currentGizmo.pan , NULLVECTOR );
			parentGizmo = get_gizmo_parent( currentGizmo );
			while( parentGizmo ) {
				ang_rotate( currentGizmo.pan , parentGizmo.local_pan );
				parentGizmo = get_gizmo_parent( parentGizmo);
			}
		}
		ang_rotate( currentGizmo.pan , currentGizmo.local_pan );
		currentGizmo = ent_next( currentGizmo );
	}
}

function torsoRotator() {

	my.object_type = gizmoObject;

	wait(1);

	my.alpha = 30;
	set( my , TRANSLUCENT );
	set( my , UNLIT );
	my.ambient = 100;
	set( my , POLYGON );

	vec_set( my.scale_x , vector( 0.33 , 0.33 , 0.33 ) );
	wait(1);
	c_setminmax( me );

	ENTITY* parentEnt;
	
	while(1) {
		if( mouse_right == 1 ) { set( my , INVISIBLE ); }
		else { reset( my , INVISIBLE ); }

		parentEnt = ptr_for_handle( my.parent_entity );
		if( parentEnt != NULL ) {
			vec_for_bone( my.x , parentEnt , (long)my.bone_handle );
		}

		if( currentGizmoControlled != NULL ) {
			if( currentGizmoControlled != me ) {
				set( my , FLAG2 );
			}
			else {
				reset( my , FLAG2 );
			}
		}
		else {
			reset( my , FLAG2 );
		}
		wait(1);
	}
}

function gizmoController()
{
	VECTOR vPos, vTarget;

	ENTITY* entParent;

	VECTOR vDir1;
	VECTOR vDir2;

	ANGLE angOld;
	ANGLE localAngOld;

	VECTOR vAxis;

	ANGLE angRot;

	while(1) {
		if(mouse_left == 1) {
	
			vec_set( vPos, camera.x );
			vec_set( vTarget, mouse_dir3d );
			vec_scale( vTarget, 1000 );
			vec_add( vTarget, vPos );
			c_trace( vPos, vTarget, IGNORE_FLAG2 );
			if ( HIT_TARGET ) {

				currentGizmoControlled = you;
				vec_diff ( vDir1, hit.x, you.x );
				vec_normalize ( vDir1, 1 );
				vec_set ( angOld, you.pan );
				vec_set ( localAngOld , you.local_pan );

				you.alpha = 90;

				while ( mouse_left ) {

					vec_set ( vPos, camera.x );
					vec_set ( vTarget, mouse_dir3d );
					vec_scale ( vTarget, 1000 );
					vec_add ( vTarget, vPos );
					c_trace ( vPos, vTarget, IGNORE_FLAG2 );
					if ( HIT_TARGET ) {

						if ( you == currentGizmoControlled ) {

							draw_point3d ( hit.x, COLOR_WHITE, 100, 1 );
							vec_diff ( vDir2, hit.x, you.x );
							vec_normalize ( vDir2, 1 );
							vec_cross ( vAxis , vDir1 , vDir2 );
		
							ang_for_axis( angRot, vAxis, acosv ( vec_dot ( vDir1 , vDir2 ) ) );
							vec_set ( you.pan, angOld );
							ang_add( you.pan, angRot );

							vec_set ( you.local_pan , localAngOld );
							ang_add( you.local_pan , angRot );

							entParent = ptr_for_handle( you.parent_entity );
							if( entParent != NULL ) {
								ent_bonereset( entParent , (long)you.bone_handle );
								ent_bonerotate( entParent , (long)you.bone_handle , you.local_pan );
							}
						}
						else {
							vec_set ( vPos, camera.x );
							vec_set ( vTarget, mouse_dir3d );
							vec_scale ( vTarget, vec_dist( camera.x , currentGizmoControlled.x ) );
							vec_add ( vTarget, vPos );

							vec_set( vDir2 , vTarget );
							vec_sub( vDir2 , currentGizmoControlled.x );
							vec_normalize( vDir2 , currentGizmoControlled.max_x );
							vec_add( vDir2 , currentGizmoControlled.x );

							draw_point3d ( vDir2 , COLOR_RED , 100, 1 );

							vec_diff ( vDir2, vDir2 , currentGizmoControlled.x );
							vec_normalize ( vDir2, 1 );
							vec_cross ( vAxis , vDir1 , vDir2 );
		
							ang_for_axis( angRot, vAxis, acosv ( vec_dot ( vDir1 , vDir2 ) ) );
							vec_set ( currentGizmoControlled.pan, angOld );
							ang_add( currentGizmoControlled.pan, angRot );

							vec_set ( currentGizmoControlled.local_pan , localAngOld );
							ang_add( currentGizmoControlled.local_pan , angRot );

							entParent = ptr_for_handle( currentGizmoControlled.parent_entity );
							if( entParent != NULL ) {
								ent_bonereset( entParent , (long)currentGizmoControlled.bone_handle );
								ent_bonerotate( entParent , (long)currentGizmoControlled.bone_handle , currentGizmoControlled.local_pan );
							}
						}
					}
					else {
						vec_set ( vPos, camera.x );
						vec_set ( vTarget, mouse_dir3d );
						vec_scale ( vTarget, vec_dist( camera.x , currentGizmoControlled.x ) );
						vec_add ( vTarget, vPos );

						vec_set( vDir2 , vTarget );
						vec_sub( vDir2 , currentGizmoControlled.x );
						vec_normalize( vDir2 , currentGizmoControlled.max_x );
						vec_add( vDir2 , currentGizmoControlled.x );

						draw_point3d ( vDir2 , COLOR_RED , 100, 1 );

						vec_diff ( vDir2, vDir2 , currentGizmoControlled.x );
						vec_normalize ( vDir2, 1 );
						vec_cross ( vAxis , vDir1 , vDir2 );
		
						ang_for_axis( angRot, vAxis, acosv ( vec_dot ( vDir1 , vDir2 ) ) );
						vec_set ( currentGizmoControlled.pan, angOld );
						ang_add( currentGizmoControlled.pan, angRot );

						vec_set ( currentGizmoControlled.local_pan , localAngOld );
						ang_add( currentGizmoControlled.local_pan , angRot );

						entParent = ptr_for_handle( currentGizmoControlled.parent_entity );
						if( entParent != NULL ) {
							ent_bonereset( entParent , (long)currentGizmoControlled.bone_handle );
							ent_bonerotate( entParent , (long)currentGizmoControlled.bone_handle , currentGizmoControlled.local_pan );
						}
					}
					
					DEBUG_VAR ( currentGizmoControlled.pan, 10 );
					DEBUG_VAR ( currentGizmoControlled.tilt, 40 );
					DEBUG_VAR ( currentGizmoControlled.roll, 70 );
					wait(1);
				}

				currentGizmoControlled.alpha = 30;
				currentGizmoControlled = NULL;
			}
		}
		else {

			vec_set( vPos, camera.x );
			vec_set( vTarget, mouse_dir3d );
			vec_scale( vTarget, 1000 );
			vec_add( vTarget, vPos );
			c_trace( vPos, vTarget, IGNORE_FLAG2 );
			if ( HIT_TARGET ) {

				if( currentGizmoControlled != NULL ) {
					currentGizmoControlled.alpha = 30;
				}
				currentGizmoControlled = you;
				you.alpha = 90;
			}
			else {
				if( currentGizmoControlled != NULL ) {
					currentGizmoControlled.alpha = 30;
					currentGizmoControlled = NULL;
				}
			}
		}
		wait(1);
	}
}



File puppet.c :
Code:
STRING* boneName_str = "";

function puppet_act() {

	set( my , UNTOUCHABLE );
	my.flags2 |= UNTOUCHABLE;
	my.alpha = 50;
	set( my , TRANSLUCENT );

	// -------------------------
	// -- GET JOINT POSITIONS --
	// -------------------------
	// 

	// *** Right body ***

	// . Upper
	VECTOR* rShoulder[3];
	vec_for_bone( rShoulder.x , my , "RShoulder" );
	VECTOR* rElbow[3];
	vec_for_bone( rElbow.x , my , "RElbow" );
	VECTOR* rWrist[3];
	vec_for_bone( rWrist.x , my , "RWrist" );
	VECTOR* rMidFingerBase[3];
	vec_for_bone( rMidFingerBase.x , my , "RMidFinger" );

	// . Lower
	VECTOR* rHip[3];
	vec_for_bone( rHip.x , my , "RHip" );
	VECTOR* rKnee[3];
	vec_for_bone( rKnee.x , my , "RKnee" );
	VECTOR* rAnkle[3];
	vec_for_bone( rAnkle.x , my , "RAnkle" );
	VECTOR* rFoot[3];
	vec_for_bone( rFoot.x , my , "RFoot" );

	// *** Left Body ***
	// . Upper
	VECTOR* lShoulder[3];
	vec_for_bone( lShoulder.x , my , "LShoulder" );
	VECTOR* lElbow[3];
	vec_for_bone( lElbow.x , my , "LElbow" );
	VECTOR* lWrist[3];
	vec_for_bone( lWrist.x , my , "LWrist" );
	VECTOR* lMidFingerBase[3];
	vec_for_bone( lMidFingerBase.x , my , "LMidFinger" );

	// . Lower
	VECTOR* lHip[3];
	vec_for_bone( lHip.x , my , "LHip" );
	VECTOR* lKnee[3];
	vec_for_bone( lKnee.x , my , "LKnee" );
	VECTOR* lAnkle[3];
	vec_for_bone( lAnkle.x , my , "LAnkle" );
	VECTOR* lFoot[3];
	vec_for_bone( lFoot.x , my , "LFoot" );

	// * Middle Body	
	VECTOR* Neck[3];
	vec_for_bone( Neck.x , my , "Neck" );
	VECTOR* SkullBase[3];
	vec_for_bone( SkullBase.x , my , "SkullBase" );
	VECTOR* Chest[3];
	vec_for_bone( Chest.x , my , "Chest" );
	VECTOR* Base[3];
	vec_for_bone( Base.x , my , "Base" );

	// ----------------------------
	// -- GET JOINT ORIENTATIONS --
	// ----------------------------
	VECTOR* tempBiasVec[3];

	// *** Right Upper Body ***
	// rShoulder
	ANGLE* rShoulderBias[3];
	vec_set( tempBiasVec.x , rElbow.x );
	vec_sub( tempBiasVec.x , rShoulder.x );
	vec_to_angle( rShoulderBias.pan , tempBiasVec.x );
	// rElbow
	ANGLE* rElbowBias[3];
	vec_set( tempBiasVec.x , rWrist.x );
	vec_sub( tempBiasVec.x , rElbow.x );
	vec_to_angle( rElbowBias.pan , tempBiasVec.x );
	// rWrist
	ANGLE* rWristBias[3];
	vec_set( tempBiasVec.x , rMidFingerBase.x );
	vec_sub( tempBiasVec.x , rWrist.x );
	vec_to_angle( rWristBias.pan , tempBiasVec.x );

	// *** Left Upper Body ***
	// lShoulder
	ANGLE* lShoulderBias[3];
	vec_set( tempBiasVec.x , lElbow.x );
	vec_sub( tempBiasVec.x , lShoulder.x );
	vec_to_angle( lShoulderBias.pan , tempBiasVec.x );
	// lElbow
	ANGLE* lElbowBias[3];
	vec_set( tempBiasVec.x , lWrist.x );
	vec_sub( tempBiasVec.x , lElbow.x );
	vec_to_angle( lElbowBias.pan , tempBiasVec.x );
	// lWrist
	ANGLE* lWristBias[3];
	vec_set( tempBiasVec.x , lMidFingerBase.x );
	vec_sub( tempBiasVec.x , lWrist.x );
	vec_to_angle( lWristBias.pan , tempBiasVec.x );

	// *** Lower Body ***
	// Right side
	ANGLE* rHipBias[3];
	vec_set( tempBiasVec.x , rKnee.x );
	vec_sub( tempBiasVec.x , rHip.x );
	vec_to_angle( rHipBias.pan , tempBiasVec.x );
	ANGLE* rKneeBias[3];
	vec_set( tempBiasVec.x , rAnkle.x );
	vec_sub( tempBiasVec.x , rKnee.x );
	vec_to_angle( rKneeBias.pan , tempBiasVec.x );
	ANGLE* rAnkleBias[3];
	vec_set( tempBiasVec.x , rFoot.x );
	vec_sub( tempBiasVec.x , rAnkle.x );
	vec_to_angle( rAnkleBias.pan , tempBiasVec.x );
	// Left Side
	ANGLE* lHipBias[3];
	vec_set( tempBiasVec.x , lKnee.x );
	vec_sub( tempBiasVec.x , lHip.x );
	vec_to_angle( lHipBias.pan , tempBiasVec.x );
	ANGLE* lKneeBias[3];
	vec_set( tempBiasVec.x , lAnkle.x );
	vec_sub( tempBiasVec.x , lKnee.x );
	vec_to_angle( lKneeBias.pan , tempBiasVec.x );
	ANGLE* lAnkleBias[3];
	vec_set( tempBiasVec.x , lFoot.x );
	vec_sub( tempBiasVec.x , lAnkle.x );
	vec_to_angle( lAnkleBias.pan , tempBiasVec.x );	

	// *** Middle Body ***
	// Neck
	ANGLE* NeckBias[3];
	vec_set( tempBiasVec.x , SkullBase.x );
	vec_sub( tempBiasVec.x , Neck.x );
	vec_to_angle( NeckBias.pan , tempBiasVec.x );
	// Chest
	ANGLE* ChestBias[3];
	vec_set( tempBiasVec.x , Chest.x );
	vec_sub( tempBiasVec.x , Base.x );
	vec_to_angle( ChestBias.pan , tempBiasVec.x );

	// ------------------
	// -- BONE HANDLES --
	// ------------------
	//
	// *** Upper Body ***
	// . Right side
	var rShoulderHandle = 0;
	var rElbowHandle = 0;
	var rWristHandle = 0;
	var rMidFingerBaseHandle = 0;
	// . Left Side
	var lShoulderHandle = 0;
	var lElbowHandle = 0;
	var lWristHandle = 0;
	var lMidFingerBaseHandle = 0;

	// *** Middle Body ***
	var neckHandle = 0;
	var chestHandle = 0;

	// *** Lower Body ***
	// . Right Side
	var rHipHandle = 0;
	var rKneeHandle = 0;
	var rAnkleHandle = 0;
	var rFootHandle = 0;
	// . Left Side
	var lHipHandle = 0;
	var lKneeHandle = 0;
	var lAnkleHandle = 0;
	var lFootHandle = 0;


	var boneHandle;
	var i = 0;
	for(i = 0; i < ent_bones( my ); i++ ) {
		boneHandle = ent_bonehandle( my , boneName_str , i );

		// Right Upper
		if(str_cmpi(boneName_str,"RElbow") == 1) { rElbowHandle = boneHandle; }
		if(str_cmpi(boneName_str,"RShoulder") == 1) { rShoulderHandle = boneHandle; }
		if(str_cmpi(boneName_str,"RWrist") == 1) { rWristHandle = boneHandle; }
		if(str_cmpi(boneName_str,"RMidFinger") == 1) { rMidFingerBaseHandle = boneHandle; }
		// Left Upper
		if(str_cmpi(boneName_str,"LElbow") == 1) { lElbowHandle = boneHandle; }
		if(str_cmpi(boneName_str,"LShoulder") == 1) { lShoulderHandle = boneHandle; }
		if(str_cmpi(boneName_str,"LWrist") == 1) { lWristHandle = boneHandle; }
		if(str_cmpi(boneName_str,"LMidFinger") == 1) { lMidFingerBaseHandle = boneHandle; }

		// Middle Body
		if(str_cmpi(boneName_str,"Neck") == 1) { neckHandle = boneHandle; }
		if(str_cmpi(boneName_str,"Chest") == 1) { chestHandle = boneHandle; }

		// Lower Body
		// Right Side
		if(str_cmpi(boneName_str,"RHip") == 1) { rHipHandle = boneHandle; }
		if(str_cmpi(boneName_str,"RKnee") == 1) { rKneeHandle = boneHandle; }
		if(str_cmpi(boneName_str,"RAnkle") == 1) { rAnkleHandle = boneHandle; }
		if(str_cmpi(boneName_str,"RFoot") == 1) { rFootHandle = boneHandle; }

		// Left Side
		if(str_cmpi(boneName_str,"LHip") == 1) { lHipHandle = boneHandle; }
		if(str_cmpi(boneName_str,"LKnee") == 1) { lKneeHandle = boneHandle; }
		if(str_cmpi(boneName_str,"LAnkle") == 1) { lAnkleHandle = boneHandle; }
		if(str_cmpi(boneName_str,"LFoot") == 1) { lFootHandle = boneHandle; }

	}

	chestGizmoX = ent_create( "Sphere.mdl" , Chest.x , torsoRotator );
	chestGizmoX.parent_entity = handle(me);
	chestGizmoX.bone_handle = chestHandle;

	neckGizmoX = ent_create( "Sphere.mdl" , Neck.x , torsoRotator );
	neckGizmoX.parent_entity = handle(me);
	neckGizmoX.parent_gizmo = handle(chestGizmoX);
	neckGizmoX.bone_handle = neckHandle;

	rsGizmoX = ent_create( "Sphere.mdl" , rShoulder.x , torsoRotator );
	rsGizmoX.parent_entity = handle(me);
	rsGizmoX.parent_gizmo = handle(neckGizmoX);
	rsGizmoX.bone_handle = rShoulderHandle;

	reGizmoX = ent_create( "Sphere.mdl" , rElbow.x , torsoRotator );
	reGizmoX.parent_entity = handle(me);
	reGizmoX.parent_gizmo = handle(rsGizmoX);
	reGizmoX.bone_handle = rElbowHandle;

	rwGizmoX = ent_create( "Sphere.mdl" , rWrist.x , torsoRotator );
	rwGizmoX.parent_entity = handle(me);
	rwGizmoX.parent_gizmo = handle(reGizmoX);
	rwGizmoX.bone_handle = rWristHandle;



	while(1) {
		if( mouse_right == 1 ) {	reset( my , TRANSLUCENT ); my.alpha = 100; }
		else {												set( my , TRANSLUCENT ); my.alpha = 30; }
		wait(1);
	}

}



My next goal is to implement an animation system with interpolation between key frames (saved from all local gizmo angles), but first I have to fix the rotation problem with the propagation function.
When I fix it, if I dont forget, I will post the update.
Posted By: CodeMaster

Re: Posing - Bone rotation with Gizmos similar to 3D Studio Max - 08/08/15 10:25

thanks
Posted By: DLively

Re: Posing - Bone rotation with Gizmos similar to 3D Studio Max - 08/08/15 14:58

Wow!
do you think you could put it in a zip file so i'd just have to run it to test it out? laugh

Thanks for this contribution laugh
Posted By: exile

Re: Posing - Bone rotation with Gizmos similar to 3D Studio Max - 08/09/15 08:52

Great contribution as always EpsiloN. Thanks and keep up the good work!
Posted By: EpsiloN

Re: Posing - Bone rotation with Gizmos similar to 3D Studio Max - 08/20/15 19:41

I am sorry, I couldn't zip it all, because I'm using models which I don't have permission to share.

By the way, I'm working on the keyframe system so I have something goin on, but development has been really slowed down due to me moving in a new apartment and the daily job...

There is also another method of rotation, with mouse movement relative to the screen orientation, but I will post it when I have more free time to strip down the junk code laugh
Posted By: txesmi

Re: Posing - Bone rotation with Gizmos similar to 3D Studio Max - 08/21/15 00:39

Hi,
there is a big missunderstanding on your code.

Code:
VECTOR* rShoulder[3];
vec_for_bone( rShoulder.x , my , "RShoulder" );



'rShoulder' is an array of vector pointers. You should declare a vector struct instead.

Code:
VECTOR rShoulder; // A vector struct
vec_for_bone( rShoulder , my , "RShoulder" );



You can improve the code a lot by using data structs to allocate all the needed data. Since the bones of an entity are allocated into an array you can continue treating the data as arrays for ease and forget about specific pointers for each gizmo so you can modify any kind of bone hierarchy with a common code.

Code:
// -------------------------------------------------------------------------
// GIZMO SKILLS
// -------------------------------------------------------------------------
#define skGizmoContainer         skill1
#define skEntity                 skill2
#define skBoneName               skill3
#define skBoneHandle             skill4
#define skLocalPan               skill5
#define skLocalTilt              skill6
#define skLocalRoll              skill7

#define skChildrenCount          skill19
#define MC_CHILDREN_ARRAY_BASE   20
//#define skChildren             skill20<->skill92 (max of 72 bones)

// -------------------------------------------------------------------------
// GIZMOS CONTAINER
// -------------------------------------------------------------------------
typedef struct
{
	int iGizmoCount; 
	ENTITY **entGizmo; 
} ENT_BONE_GIZMOS;

ENT_BONE_GIZMOS *gizmosCreate ( ENTITY *ent )
{
	if ( !ent )
		return NULL;
	int iBoneCount = ent_status ( ent, 14 );
	if ( !iBoneCount )
		return NULL;
	ENT_BONE_GIZMOS *ebg = sys_malloc ( sizeof(ENT_BONE_GIZMOS) );
	ebg->iGizmoCount = iBoneCount;
	ebg->entGizmo = sys_malloc ( sizeof(ENTITY*) * iBoneCount );
	ent_bonereset_all ( ent );
	
	int iBoneIndex = 0;
	for ( ; iBoneIndex<iBoneCount; iBoneIndex+=1 )
	{
		ENTITY *entGizmo = ent_create ( "gizmo.mdl", nullvector, NULL );
		*(ebg->entGizmo+iBoneIndex) = entGizmo;
		STRING *strBoneName = str_create ( "" );
		long hdlBone = ent_bonehandle ( ent, strBoneName, iBoneIndex+1 );
		
		entGizmo->skGizmoContainer = (ENT_BONE_GIZMOS*)ebg;
		entGizmo->skEntity = (ENTITY*)ent;
		entGizmo->skBoneName = (STRING*)strBoneName;
		entGizmo->skBoneHandle = (long)hdlBone;
		vec_set ( &entGizmo->skLocalPan, nullvector );
		entGizmo->skChildrenCount = 0;
		entGizmo->flags |= ZNEAR;
		vec_for_bone ( &entGizmo->x, ent, hdlBone );
		ang_for_bone ( &entGizmo->pan, ent, hdlBone );
		
		long hdlParent = ent_boneparent ( ent, NULL, hdlBone );
		if ( hdlParent )
		{
			int iParentIndex = 0;
			for ( ; iParentIndex<iBoneIndex; iParentIndex+=1 )
			{
				ENTITY *entParent = *(ebg->entGizmo+iParentIndex);
				if ( entParent->skBoneHandle != hdlParent )
					continue;
				entGizmo->parent = entParent;
				entParent->skill[MC_CHILDREN_ARRAY_BASE+entParent->skChildrenCount] = (ENTITY*)entGizmo;
				entParent->skChildrenCount += 1;
				break;
			}
		}
		else
		{
			entGizmo->parent = NULL;
		}
	}
	return ebg;
}

void gizmosRemove ( ENT_BONE_GIZMOS* ebg )
{
	if ( !ebg )
		return;
	int iBoneIndex = 0;
	for ( ; iBoneIndex<ebg->iGizmoCount; iBoneIndex+=1 )
	{
		ENTITY *entGizmo = *(ebg->entGizmo+iBoneIndex);
		str_remove ( (STRING*)entGizmo->skBoneName );
		ent_remove ( entGizmo );
	}
	sys_free ( ebg->entGizmo );
	sys_free ( ebg );
}

// -------------------------------------------------------------------------
// GIZMOS ROTATION
// -------------------------------------------------------------------------
void gizmoChildrenUpdate ( ENTITY *entGizmo, ENTITY *ent )
{
	int iChildIndex = 0;
	for ( ; iChildIndex<entGizmo->skChildrenCount; iChildIndex+=1 )
	{
		ENTITY *entChild = (ENTITY*)entGizmo->skill[MC_CHILDREN_ARRAY_BASE+iChildIndex];
		long hdlChild = (long)entChild->skBoneHandle;
		vec_for_bone ( &entChild->x, ent, hdlChild );
		ang_for_bone ( &entChild->pan, ent, hdlChild );
		gizmoChildrenUpdate ( entChild, ent );
	}
}

void gizmoRotate ( ENTITY *entGizmo, ANGLE *angRotation )
{
	ang_add ( &entGizmo->pan, angRotation );
	ang_add ( &entGizmo->skLocalPan, angRotation );
	ENTITY *ent = (ENTITY*)entGizmo->skEntity;
	long hdlBone = (long)entGizmo->skBoneHandle;
	ent_bonereset ( ent, hdlBone );
	ent_bonerotate ( ent, hdlBone, &entGizmo->skLocalPan );
	gizmoChildrenUpdate ( entGizmo, ent );
}

// -------------------------------------------------------------------------
// MAIN
// -------------------------------------------------------------------------
void main ()
{
	video_mode = 10;
	mouse_mode = 4;
	wait(1);
	level_load ( "" );
	camera->x = -200;
	camera->bg = pixel_for_vec ( COLOR_BLACK, 100, 8888 );
	
	ENTITY *entModel1 = ent_create ( "knight.mdl", nullvector, NULL );
	ENT_BONE_GIZMOS *ebg1 = gizmosCreate ( entModel1 );
	ENTITY* entGizmo1 = *(ebg1->entGizmo+2);
	gizmoRotate ( entGizmo1, vector(0,45,0) );
	
	while ( !key_esc )
	{
		gizmoRotate ( entGizmo1, vector(5*time_step,0,0) );
		wait(1);
	}
	
	gizmosRemove ( ebg1 );
	ent_remove ( entModel1 );
	sys_exit ( NULL );
}



Maybe I went too far with the example xP
Hope it helps
Posted By: EpsiloN

Re: Posing - Bone rotation with Gizmos similar to 3D Studio Max - 08/21/15 21:11

I'm still learning the syntax of Lite-C grin

I know I could have used a more generic approach (with arrays), but my first goal was to get a prototype working for a standard 3DSMax biped. I am also still (just) learning about structs, so I posted it as is while it still works laugh

I am still learning structs, so its far from done, but my goal will be to build a system with animation templates that can be plug & played on any biped model and possibly tweaked with ease in-game.

Thanks for your example (I haven't tested it yet) and lets hope we could all use this for animation some day. laugh
Posted By: txesmi

Re: Posing - Bone rotation with Gizmos similar to 3D Studio Max - 08/21/15 21:56

Go hard! grin

My best wishes on your way,
txes
© 2024 lite-C Forums