Sign in to follow this  
Daniel E

Bird movement

Recommended Posts

Hi i'm working on implementing some kind of flocking behaviour so far it works alright Here's a video: video but a major problem is, that the birds arent rotating like real birds would :/ I've been reading a lot about quaternion rotations and flight kinetics and even looked at source codes but they usually involve TONS of math. is this really necessary? Because so far my movement routines are really simple and simple math usually turned out to be the best usually :) here's my simple code for movement and rotation: (it only rotates on the y-axis)
void MhBird::move(D3DXVECTOR3 v)
{

	position += v;
	rotation = atan2(v.x, v.z);
	D3DXMatrixRotationY(&rotationmatrix, rotation);
        D3DXMatrixTranslation( &worldmatrix, v.x, v.y, v.z);
	D3DXMatrixMultiply(&worldmatrix, &rotationmatrix, &worldmatrix);
}





is there any way to advance this simple function to do the pitch and roll stuff a bird would do? it doesnt have to be absolutely physically correct. just something to get started with. here's my bird class btw if anyone is interested. I used some stuff from this open source project as a foundation http://www.koders.com/cpp/fid0041061C06005834B647EA36B1E18686547EB489.aspx

#pragma once

#include <list>
#include <vector>


class MhBird : public MhObject
{
public:
	MhBird();
	~MhBird();

	void Update(double dTimeDelta);
	inline void setLeader(MhObject* _leader) { leader = _leader; }
	inline MhObject* getLeader() { return leader; }
	void move(D3DXVECTOR3 v);
private:

	bool FindClosest();

	D3DXVECTOR3 Orientation();
	D3DXVECTOR3 Separation();

	MhObject* leader;
	MhObject* nearest;
	std::list<MhObject*> closestList;

	float nearestDist;

	long time_elapsed;

	// static variables to tweak
	static float preferred_separation_;
	static float minUrgency_;
	static float maxUrgency_;
	static float cohesionFactor_;
	static float separationFactor_;
	static float alignmentFactor_;
	static float allowedDistToPredator_;

	D3DXQUATERNION quatrot;

	std::vector<MhObject*>::iterator sibling_it;

public:
	inline long getTimeElapsed() { return time_elapsed; }
	inline void resetTime() { time_elapsed = 0; }
	static float predatorFactor_;
	static float velMax_;
	static float velDrag_;
};


float MhBird::preferred_separation_ = 30.0f;
float MhBird::minUrgency_ = 0.05f;
float MhBird::maxUrgency_ = 20.15f;
float MhBird::cohesionFactor_ = 0.015f;
float MhBird::separationFactor_ = 0.01f;
float MhBird::alignmentFactor_ = 0.01f;
float MhBird::allowedDistToPredator_ = 128.0f;

float MhBird::predatorFactor_ = 0.001f;
float MhBird::velMax_ = 1.7f;
float MhBird::velDrag_ = 0.97f;

int maxClosest = 8;

MhBird::MhBird()
{
	nearest = NULL;
	nearestDist = 0.0f;
	time_elapsed = 0;
	leader = NULL;
}

MhBird::~MhBird()
{
}

void MhBird::move(D3DXVECTOR3 v)
{

	position += v;
	rotation = atan2(v.x, v.z);
	D3DXMatrixRotationY(&rotationmatrix, rotation);
        D3DXMatrixTranslation( &worldmatrix, v.x, v.y, v.z);
	D3DXMatrixMultiply(&worldmatrix, &rotationmatrix, &worldmatrix);
}

void MhBird::Update(double dTimeDelta)
{

	time_elapsed += dTimeDelta;

	D3DXVECTOR3 accel(0,0,0);

	if (FindClosest())
	{
		accel += Orientation();
		accel += Separation();
	}

	D3DXVECTOR3 vel = *this->getVel();

	vel = velDrag_ * vel + accel;

	if (D3DXVec3Length(&vel) > velMax_)
	 vel = velMax_ * *D3DXVec3Normalize(&vel, &vel);

	float len = D3DXVec3Length(&vel);

	D3DXVECTOR3 dir = *D3DXVec3Normalize(&vel, &vel);

	dir *= len;

	D3DXVec3Scale( &dir, &dir, float( 0.1f * dTimeDelta));

	this->setVel( dir );

	move(dir);
}


bool MhBird::FindClosest()
{

	float closest = float(INFINITE);

	for (sibling_it=GetSiblingList()->begin(); sibling_it != GetSiblingList()->end(); ++sibling_it)
	{

		if (*sibling_it == this) continue;

		float dist = D3DXVec3Length(& (this->getPos() - (*sibling_it)->getPos()) );
		//float rad = 1500.0f;

		if(dist < closest)
		{
			closest = dist;
			nearest = *sibling_it;
		}

	}

	nearestDist = closest;

	return true;
}

D3DXVECTOR3 MhBird::Orientation()
{
	D3DXVECTOR3 change(0,0,0);

	if (leader)

		change = (leader->getPos()- getPos()) + (nearest->getPos() - getPos()) / 2.0f;

	else

		change = *this->getTargetPos()- getPos();

	D3DXVec3Normalize(&change, &change);

	change *= 0.05f;

	return change;
}

D3DXVECTOR3 MhBird::Separation()
{

	D3DXVECTOR3 change(0,0,0);

	if (!nearest) return change;

	D3DXVECTOR3 change_total(0,0,0);

	for (sibling_it=GetSiblingList()->begin(); sibling_it != GetSiblingList()->end(); ++sibling_it)
	{

		if((*sibling_it) == this)	continue;

		D3DXVECTOR3 diff = (*sibling_it)->getPos() - this->getPos();

		float nearDist = D3DXVec3Length(&diff);
		float ratio = nearDist / preferred_separation_;

		if(nearDist == 0.0f)
		{
			ratio = 0.1f;
			diff = MhMath::getRandomVector(MhMath::ST_SPHERE);
		}
		else if(nearDist < 1.0f)
		{
			ratio = 0.2f;
			diff = MhMath::getRandomVector(MhMath::ST_SPHERE);
		}

		if (nearDist < preferred_separation_) 
		{
			D3DXVec3Normalize(&change, &(change));
			diff *= -ratio;
		} 
		else 
		{
			diff *= 0.0f;
		}
		change_total += diff;
	}

	return change_total *= separationFactor_;
}








[Edited by - Daniel E on September 17, 2009 5:52:44 PM]

Share this post


Link to post
Share on other sites
You should be able to compute a pitch value from the velocity vector (in addition to yaw) fairly easily, e.g.:
float pitch = atan2(v.y, length(v.x, v.z));
For roll, you might try computing the value based on the last yaw delta (that is, how much the yaw value changed during the last update). This might help give the illusion of more natural motion, in that it would cause the birds to bank while turning.

Share this post


Link to post
Share on other sites
Thanks, that already helped a lot. The pitch part works good as far as i can tell.

I'm not sure about the roll though and how to compute it. I literally compute the difference between the values. The results look correct for the most part but sometimes
the birds rotate very strangly, which could be caused by my velocity computations though. Mind taking a look at the new code?


void MhBird::move(D3DXVECTOR3 v)
{
static float lastYaw = 0;

setPos(*position + v); // translate worldmatrix

yaw = atan2(v.x, v.z);

float roll = yaw - lastYaw;

float pitch = atan2(v.y, sqrtf(v.x * v.x + v.z * v.z));

lastYaw = yaw;

D3DXMatrixRotationYawPitchRoll(&rotationmatrix, yaw, pitch, roll);

D3DXMatrixMultiply(&worldmatrix, &rotationmatrix, &worldmatrix);
}



Share this post


Link to post
Share on other sites
the above code wasn working well btw

here's something that works if you ever want to move some birds in a decent looking way:

(new video)


void MhBird::move(D3DXVECTOR3 v)
{
float yaw = atan2(v.x, v.z);

float yawDelta = abs(yaw - lastYaw);

if(yawDelta > D3DX_PI)
float roll = (2.0f * D3DX_PI - yawDelta) * 5.0f; // 5.0f = roll factor, to do: calculate
else
float roll = yawDelta * 5.0f;

float pitch = atan2(v.y, sqrtf(v.x * v.x + v.z * v.z));

lastYaw = yaw;

D3DXMatrixTranslation( &worldmatrix, v.x, v.y, v.z);

D3DXMatrixRotationYawPitchRoll(&rotationmatrix, yaw, pitch, roll);
D3DXMatrixMultiply(&worldmatrix, &rotationmatrix, &worldmatrix);
}




Share this post


Link to post
Share on other sites
when adding pitch using the tangens formula, you have to be carefull. this works fine as long as you dont turn 180 degrees (otherwise, your left and right will change sides)
using quaternions can solve this.


following is C++

D3DXQUATERNION tmp; //makes quaternion
D3DXQuaternionRotationYawPitchRoll(&tmp, yaw, pitch, roll); //rotates quaternion (object)
D3DXMatrixRotationQuaternion(&tmp, &rotationmatrix); //make a matrix from a quaternion

now you have turned using a quaternion and you have your rotationmatrix back.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this