boidsflyer.cpp

来自「3D的Boids效果演示源程序」· C++ 代码 · 共 1,091 行 · 第 1/3 页

CPP
1,091
字号
	{
		return;
	}

	D3DVALUE tempX, tempY, tempZ;
	bool newVelocity = false;
	D3DVALUE distance;
	D3DVALUE shortestDistance = win -> getFlockFormingDistance( );
	D3DVALUE hHeadingTo, vHeadingTo;
	D3DVALUE newSpeed, newHHeading, newVHeading;

	// Find out the number of Boids present by calling the parent window.
	int &numFlyers = win -> getNumObjects( );

	// Get a reference to the window's vector of boids.
	vector< BoidsFlyer * > &objects = win -> getFlyersRef( );

	// Iterate through all the boids ignoring the current one.
	for ( int index = 0; index < numFlyers; index++ )
	{
		// Skip the current boid.
		if ( objects[ index ] != this )
		{
			// Shift the other boid so that the current boid is on the origin.
			// This prevents the screen wrap around interfering.
			tempX = objects[ index ] -> x - x;
			tempY = objects[ index ] -> y - y;
			tempZ = objects[ index ] -> z - z;
			wrapAround( &tempX, &tempY, &tempZ );

			// Do a distance check on the boids.
			distance = sqrt( pow( tempX, 2 ) + pow( tempY, 2 )
													+ pow( tempZ, 2 ) );

			if ( distance <= win -> getFlockFormingDistance( ) )
			{
				// Check if the boid is in the angle of vision.
				// Find the headings to the other boid.
				// First find the vertical heading.
				if ( distance < 0.5 )  // Prevent division by zero.
				{
					distance = D3DVALUE( 0.5 );
				}

				vHeadingTo = asin( tempY / distance ) * 180.0 / PI;


				// Then find the horizontal heading.
				// Prevent division by zero.
				if ( ( tempZ > -0.5 ) && ( tempZ < 0.5 ) )
				{
					tempZ = D3DVALUE( 0.5 );
				}

				hHeadingTo = atan( tempX / tempZ ) * 180.0 / PI;


				// Correct the headings if z is less than zero.
				if ( ( tempX > 0.0 ) && ( tempZ < 0.0 ) )
				{
					hHeadingTo += 180.0;
				}

				if ( ( tempX < 0.0 ) && ( tempZ < 0.0 ) )
				{
					hHeadingTo -= 180.0;
				}


				// Normalize the headings, so that -
				// - they can be properly compared.
				hHeadingTo -= hHeading;
				vHeadingTo -= vHeading;
				maintainHeadings( &hHeadingTo, &vHeadingTo );

				if ( ( hHeadingTo <= win -> getAngleOfVision( ) )
					&& ( hHeadingTo >= -( win -> getAngleOfVision( ) ) )
					&& ( vHeadingTo <= win -> getAngleOfVision( ) )
					&& ( vHeadingTo >= -( win -> getAngleOfVision( ) ) ) )
				{
					if ( distance < shortestDistance )
					{
						// Restore the headings so that -
						// - they can be used properly.
						hHeadingTo += hHeading;
						vHeadingTo += vHeading;
						maintainHeadings( &hHeadingTo, &vHeadingTo );

						// Set the new required velocity.
						newVelocity = true;
						shortestDistance = distance;
						newHHeading = hHeadingTo;
						newVHeading = vHeadingTo;
						newSpeed = objects[ index ] ->
										speed + CATCH_UP_INCREASE;

						// Limit the speed.
						if ( newSpeed > win -> getMaximumSpeed( ) )
						{
							newSpeed = win -> getMaximumSpeed( );
						}
					}
				}
			}
		}
	}

	if ( newVelocity )
	{
		setReqVelocity( newSpeed, newHHeading, newVHeading );
	}
}


// Function to enable a boid to match it's flockmates velocites.
void BoidsFlyer::velocityMatching( )
{
	BoidsWin *win = static_cast< BoidsWin * >( window );

	// Only perform velocity matching if the flag is set.
	if ( win -> getVelocityMatchingActive( ) == false )
	{
		return;
	}

	D3DVALUE tempX, tempY, tempZ;
	bool newVelocity = false;
	D3DVALUE distance;
	D3DVALUE hHeadingOf, vHeadingOf;
	D3DVALUE totalSpeed = 0, totalHHeading = 0, totalVHeading = 0;
	int flockmates = 0;
	D3DVALUE newSpeed, newHHeading, newVHeading;

	// Find out the number of Boids present by calling the parent window.
	int &numFlyers = win -> getNumObjects( );

	// Get a reference to the window's vector of boids.
	vector< BoidsFlyer * > &objects = win -> getFlyersRef( );

	// Iterate through all the boids ignoring the current one.
	for ( int index = 0; index < numFlyers; index++ )
	{
		// Skip the current boid.
		if ( objects[ index ] != this )
		{
			// Shift the other boid so that the current boid is on the origin.
			// This prevents the screen wrap around from interfering.
			tempX = objects[ index ] -> x - x;
			tempY = objects[ index ] -> y - y;
			tempZ = objects[ index ] -> z - z;
			wrapAround( &tempX, &tempY, &tempZ );

			// Do a distance check on the boids.
			distance = sqrt( pow( tempX, 2 ) +
							pow( tempY, 2 ) + pow( tempZ, 2 ) );

			// If the distance is in the flocking radius.
			if ( distance <= win -> getFlockingRadius( ) )
			{
				// Check that the headings are similar.
				// Get the headings of the other boid.
				hHeadingOf = objects[ index ] -> hHeading;
				vHeadingOf = objects[ index ] -> vHeading;

				// Normalise the headings, so that -
				// - they can be properly compared.
				hHeadingOf -= hHeading;
				vHeadingOf -= vHeading;
				maintainHeadings( &hHeadingOf, &vHeadingOf );

				if ( ( hHeadingOf <= +( win ->
									getRangeOfFlockHeadings( ) ) )
					&& ( hHeadingOf >= -( win ->
									getRangeOfFlockHeadings( ) ) )
					&& ( vHeadingOf <= +( win ->
									getRangeOfFlockHeadings( ) ) )
					&& ( vHeadingOf >= -( win ->
									getRangeOfFlockHeadings( ) ) ) )
				{
					// Restore the headings so that they can be used properly.
					hHeadingOf += hHeading;
					vHeadingOf += vHeading;
					maintainHeadings( &hHeadingOf, &vHeadingOf );

					// Take the boids velocity into account ( in the flock ).
					// Add the components to the totals -
					// - to find an average later on.
					totalSpeed += objects[ index ] -> speed;
					totalHHeading += hHeadingOf;
					totalVHeading += vHeadingOf;
					flockmates++;
					newVelocity = true;
				}
			}
		}
	}

	if ( newVelocity )
	{
		newSpeed = totalSpeed / flockmates;
		newHHeading = totalHHeading / flockmates;
		newVHeading = totalVHeading / flockmates;
		setReqVelocity( newSpeed, newHHeading, newVHeading );
	}
}


// Function to detect and prevent any boid collisions.
void BoidsFlyer::collisionAvoidance( )
{
	BoidsWin *win = static_cast< BoidsWin * >( window );

	// Only perform collision avoidance if the flag is set.
	if ( win -> getCollisionAvoidanceActive( ) == false )
	{
		return;
	}

	D3DVALUE tempX, tempY, tempZ;
	bool newVelocity = false;
	D3DVALUE distance;
	D3DVALUE shortestDistance = win -> getCollisionDistance( );
	D3DVALUE hHeadingTo, vHeadingTo;
	D3DVALUE newSpeed, newHHeading, newVHeading;

	// Find out the number of Boids present by calling the parent window.
	int &numFlyers = win -> getNumObjects( );

	// Get a reference to the window's vector of boids.
	vector< BoidsFlyer * > &objects = win -> getFlyersRef( );

	// Iterate through all the boids ignoring the current one.
	for ( int index = 0; index < numFlyers; index++ )
	{
		// Skip the current boid.
		if ( objects[ index ] != this )
		{
			// Shift the other boid so that the current boid is on the origin.
			// This prevents the screen wrap around interfering.
			tempX = objects[ index ] -> x - x;
			tempY = objects[ index ] -> y - y;
			tempZ = objects[ index ] -> z - z;
			wrapAround( &tempX, &tempY, &tempZ );

			// Do a distance check on the boids.
			distance = sqrt( pow( tempX, 2 ) +
							pow( tempY, 2 ) + pow( tempZ, 2 ) );

			if ( distance <= win -> getCollisionDistance( ) )
			{
				// Find the headings to the other boid.
				// First find the vertical heading.
				if ( distance < 0.5 )  // Prevent division by zero.
				{
					distance = D3DVALUE( 0.5 );
				}

				vHeadingTo = asin( tempY / distance ) * 180.0 / PI;


				// Then find the horizontal heading.
				// Prevent division by zero.
				if ( ( tempZ > -0.5 ) && ( tempZ < 0.5 ) )
				{
					tempZ = D3DVALUE( 0.5 );
				}

				hHeadingTo = atan( tempX / tempZ ) * 180.0 / PI;


				// Correct the headings if z is less than zero.
				if ( ( tempX > 0.0 ) && ( tempZ < 0.0 ) )
				{
					hHeadingTo += 180.0;
				}

				if ( ( tempX < 0.0 ) && ( tempZ < 0.0 ) )
				{
					hHeadingTo -= 180.0;
				}

				if ( distance < shortestDistance )
				{
					newVelocity = true;
					shortestDistance = distance;
					// Set the new headings.
					// Normalise the headings, so that -
					// - they can be properly calculated.
					hHeadingTo -= hHeading;
					vHeadingTo -= vHeading;
					maintainHeadings( &hHeadingTo, &vHeadingTo );
					if ( hHeadingTo >= 0.0 )  // Turn left.
					{
						newHHeading = hHeading + ( hHeadingTo - 90 )
							* ( distance / win -> getCollisionDistance( ) );
					}
					if ( hHeadingTo < 0.0 )  // Turn right.
					{
						newHHeading = hHeading + ( hHeadingTo + 90 )
							* ( distance / win -> getCollisionDistance( ) );
					}
					if ( vHeadingTo >= 0.0 )  // Turn down.
					{
						newVHeading = vHeading + ( vHeadingTo - 90 )
							* ( distance / win -> getCollisionDistance( ) );
					}
					if ( vHeadingTo < 0.0 )  // Turn up.
					{
						newVHeading = vHeading + ( vHeadingTo + 90 )
							* ( distance / win -> getCollisionDistance( ) );
					}

					// Set the new speed.
					newSpeed = objects[ index ] -> speed - (
						SLOW_DOWN_DECREASE * ( distance / win ->
											getCollisionDistance( ) ) );

					// Limit the speed.
					if ( newSpeed < win -> getMinimumSpeed( ) )
					{
						newSpeed = win -> getMinimumSpeed( );
					}
				}
			}
		}
	}

	if ( newVelocity )
	{
		setReqVelocity( newSpeed, newHHeading, newVHeading );
	}
}


// Calculate a position relative to a boid & the -
// - scene, given the distance and direction.
D3DVECTOR BoidsFlyer::calculateOffset( D3DVALUE distance,
									D3DVALUE hAngle, D3DVALUE vAngle )
{
	D3DVECTOR position;

	// 1. Find the Y component of the points position.
	position.y = distance * D3DVALUE( sin( vAngle / 180.0 * PI ) );
	position.y += y;  // Make relative to the boid's position.

	// 2. Find the horizontal distance to points position
	//     ( intermediate calculation ).
	D3DVALUE horizontalDistance = distance * D3DVALUE(
									cos( vAngle / 180.0 * PI ) );

	// 3. Find the X component of the points position.
	position.x = horizontalDistance * D3DVALUE(
									sin( hAngle / 180.0 * PI ) );
	position.x += x;  // Make relative to the boid's position.

	// 4. Find the Z component of the points position.
	position.z = horizontalDistance * D3DVALUE(
									cos( hAngle / 180.0 * PI ) );
	position.z += z;  // Make relative to the boid's position.
	
	// 5. Perform a wrap around on the position to ensure it is within range.
	wrapAround( &position.x, &position.y, &position.z );

	return position;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?