⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 boidsflyer.cpp

📁 3D的Boids效果演示源程序
💻 CPP
📖 第 1 页 / 共 3 页
字号:
}


// Function to prevent any boid collisions with the landscape.
void BoidsFlyer::landscapeAvoidance( )
{
	BoidsWin *win = static_cast< BoidsWin * >( window );

	// Abort the function if the landscape is not present.
	if ( win -> getLandExists( ) == false )
	{
		return;
	}

	// Continue with the function if the lanscape is present.
	// If the position of the boid is over the height limit.
	if ( y > Y_LIMIT )
	{
		// Turn the boid downwards to keep in it's correct area.
		reqVHeading = DOWNWARD_ANGLE_FROM_CEILING;
	}

	// Make the boid avoid any collision with the landscape.
	// Find the distance to the nearest collision in the path of the boid.
	D3DVECTOR offset;
	bool collisionDetected = false;
	D3DVALUE collisionDistance = 0;

	// Scan ahead of the boid using fine -
	// - increments to catch the jagged landscape.
	for ( collisionDistance; ( ( !collisionDetected ) && 
		( collisionDistance <= LAND_COLLISION_DISTANCE ) ); /* see below */ )
	{
		// 1. Find the position of the point, in the current direction
		//    of the current boid and also at the given collision
		//    distance away from it.
		offset = calculateOffset( collisionDistance, hHeading, vHeading );

		// 2. Check if the position of the point is below the landscape.
		collisionDetected = win -> getBelowLandscapeSurface(
										offset.x, offset.y, offset.z );

		// 3. Increment the collision distance proportional -
		// - to the distance from the boid.
		if ( collisionDistance < FINE_SCAN_LIMIT )
		{
			// Fine scan for short range.
			collisionDistance += D3DVALUE( FINE_SCAN_RATE );
		}
		else if ( collisionDistance < MEDIUM_SCAN_LIMIT )
		{
			// Medium scan for medium range.
			collisionDistance += D3DVALUE( MEDIUM_SCAN_RATE );
		}
		else
		{
			// Fast scan for long range.
			collisionDistance += D3DVALUE( FAST_SCAN_RATE );
		}
	}

	// 4.  Manouver the boid so that it avoids -
	//     - collision with the landscape if necessary.
	if ( collisionDetected )
	{
		landCollisionAvoidance( collisionDistance );
	}
}


// Calculate the X Z distance from a boid, given the distance and direction.
D3DVECTOR BoidsFlyer::calculateClearance( D3DVALUE length, D3DVALUE hAngle )
{
	D3DVECTOR distance;

	// Find the X component of the points distance.
	distance.x = length * D3DVALUE( sin( hAngle / 180.0 * PI ) );

	distance.y = 0;  // Not used in the X Z plane.

	// Find the Z component of the points distance.
	distance.z = length * D3DVALUE( cos( hAngle / 180.0 * PI ) );

	return distance;
}


// Finds the safe route to take to avoid collision with the landscape.
void BoidsFlyer::landCollisionAvoidance( D3DVALUE distance )
{
	BoidsWin *win = static_cast< BoidsWin * >( window );

	D3DVECTOR offset;
	bool safePath = false;
	// Number of scan increments.
	D3DVALUE accuracy = LAND_COLLISION_AVOIDANCE_ACCURACY;


	// Scan arround the collsion point to find a safe path.
	for ( int factor = 1; factor <= (int)accuracy; factor++ )
	{
		// Turn the boid left.
		offset = calculateOffset( distance, hHeading - 60.0 /
										accuracy * factor, vHeading );
		safePath = !( win -> getBelowLandscapeSurface( offset.x,
							offset.y - LANDSCAPE_CLEARANCE, offset.z ) );
		if ( safePath )
		{
			reqHHeading = hHeading - 60.0 / accuracy *
									factor - SIDE_CLEARANCE_ANGLE;
			return;
		}

		// Turn the boid right.
		offset = calculateOffset( distance, hHeading + 60.0 /
										accuracy * factor, vHeading );
		safePath = !( win -> getBelowLandscapeSurface( offset.x,
							offset.y - LANDSCAPE_CLEARANCE, offset.z ) );
		if ( safePath )
		{
			reqHHeading = hHeading + 60.0 / accuracy *
									factor + SIDE_CLEARANCE_ANGLE;
			return;
		}

		// Turn the boid up and left.
		offset = calculateOffset( distance, hHeading - 45.0 / accuracy *
								factor, vHeading + 45.0 / accuracy * factor );
		safePath = !( win -> getBelowLandscapeSurface( offset.x,
							offset.y - LANDSCAPE_CLEARANCE, offset.z ) );
		if ( safePath )
		{
			reqHHeading = hHeading - 45.0 / accuracy *
										factor - SIDE_CLEARANCE_ANGLE;
			reqVHeading = vHeading + 45.0 / accuracy * factor;
			return;
		}

		// Turn the boid up and right.
		offset = calculateOffset( distance, hHeading + 45.0 / accuracy *
								factor, vHeading + 45.0 / accuracy * factor );
		safePath = !( win -> getBelowLandscapeSurface( offset.x,
							offset.y - LANDSCAPE_CLEARANCE, offset.z ) );
		if ( safePath )
		{
			reqHHeading = hHeading + 45.0 / accuracy *
										factor + SIDE_CLEARANCE_ANGLE;
			reqVHeading = vHeading + 45.0 / accuracy * factor;
			return;
		}

		// Turn the boid upwards.
		offset = calculateOffset( distance, hHeading,
									vHeading + 60.0 / accuracy * factor );
		safePath = !( win -> getBelowLandscapeSurface( offset.x,
								offset.y - LANDSCAPE_CLEARANCE, offset.z ) );
		if ( safePath )
		{
			reqVHeading = vHeading + 60.0 / accuracy * factor;
			return;
		}
	}

	// If no safe route was found, just turn the boid upwards.
	reqVHeading = UPWARD_ANGLE_FROM_COLLISION;
}


// This function converts a speed & heading vector to an x, y, z vector.
void BoidsFlyer::convertVector( D3DVALUE *xi, D3DVALUE *yi, D3DVALUE *zi )
{
	// 1. Calculate the Y velocity component.
	*yi = speed * D3DVALUE( sin( vHeading / 180.0 * PI ) );

	// 2. Calculate the horizontal speed ( intermediate calcuation ).
	D3DVALUE horizontalSpeed = D3DVALUE( sqrt( pow( speed, 2 )
													- pow( *yi, 2 ) ) );

	// 3. Calculate the Z velocity component.
	*zi = horizontalSpeed * D3DVALUE( cos( hHeading / 180.0 * PI ) );

	// 4. Calculate the X velocity component.
	*xi = horizontalSpeed * D3DVALUE( sin( hHeading / 180.0 * PI ) );
}


// Make sure that the headings are in the correct ranges.
void BoidsFlyer::maintainHeadings( D3DVALUE *horiz, D3DVALUE *vert )
{
	while( *horiz > 180.0 )
	{
		*horiz -= 360.0;
	}
	while( *horiz < -180.0 )
	{
		*horiz += 360.0;
	}
	while( *vert > 90.0 )
	{
		*vert -= 90.0;
	}
	while( *vert < -90.0 )
	{
		*vert += 90.0;
	}
}


// Function to make the object tend towards it's required velocity.
void BoidsFlyer::updateVelocity( double time )
{
	maintainHeadings( &reqHHeading, &reqVHeading );

	// Calculate the differences in the velocity values.
	D3DVALUE difSpeed = reqSpeed - speed;
	D3DVALUE difHHeading = reqHHeading - hHeading;
	D3DVALUE difVHeading = reqVHeading - vHeading;

	// If the horizontal turn is greater than 180 degrees, turn the other way.
	if ( difHHeading < -180.0 )
	{
		difHHeading = ( reqHHeading + 360.0 ) - hHeading;
	}

	BoidsWin *win = static_cast< BoidsWin * >( window );

	// Update the boid.
	if ( time <= MAXIMUM_TIMESPAN )  // This prevents any jumping.
	{
		// Update the flyer's speed.
		speed += D3DVALUE( difSpeed * win -> getAccelerationRate( ) * time );

		// Apply some rotation to the flyer's orientation.
		hHeading += D3DVALUE( difHHeading * win ->
								getAccelerationRate( ) * time );
		vHeading += D3DVALUE( difVHeading * win ->
								getAccelerationRate( ) * time );
	}


	// Update the flyer's visual orientation.
	D3DVALUE xi, yi, zi;
	convertVector( &xi, &yi, &zi );
	meshFrame -> SetOrientation( scene, xi, yi, zi, 0, 1, 0 );
}


// Make the flyer re-appear on the other side of it's space.
void BoidsFlyer::wrapAround( D3DVALUE *xPtr, D3DVALUE *yPtr, D3DVALUE *zPtr )
{
	// Loop around the six sides of the cubed area.
	if ( *xPtr > X_LIMIT )
	{
		*xPtr = -X_LIMIT;
	}

	if ( *xPtr < -X_LIMIT )
	{
		*xPtr = X_LIMIT;
	}

	// If the position of the boid is over the height limit.
	if ( *yPtr > Y_LIMIT )
	{
		// If the landscape is not present.
		if ( static_cast< BoidsWin * >( window ) ->
										getLandExists( ) == false )
		{
			// Perform wrap around on the boid.
			*yPtr = -Y_LIMIT;
		}
	}

	// If the position of the boid is under the height limit.
	if ( *yPtr < -Y_LIMIT )
	{
		// If the landscape is not present.
		if ( static_cast< BoidsWin * >( window ) ->
										getLandExists( ) == false )
		{
			// Perform wrap around on the boid.
			*yPtr = Y_LIMIT;
		}
	}

	if ( *zPtr > Z_LIMIT )
	{
		*zPtr = -Z_LIMIT;
	}

	if ( *zPtr < -Z_LIMIT )
	{
		*zPtr = Z_LIMIT;
	}
}


// Move the flyer while the application is running.
void BoidsFlyer::moveObject( )
{
	BoidsWin *win = static_cast< BoidsWin * >( window );

	// Read the end time for the flyers motion.
	finishTime = clock( );

	// Calculate the time in seconds since the last update.
	double timeSpan = (double)( finishTime - startTime ) /
											(double)CLOCKS_PER_SEC;
	// Stop the boid's motion if the stopped flag is set.
	bool stopped = win -> getStopped( );
	if ( stopped )
	{
		timeSpan = 0;  // Update time interval equal to zero.
	}

	// Update the flyer's velocity to it's required velocity.
	updateVelocity( timeSpan );

	// Convert the flyer's vector before applying it.
	D3DVALUE xi, yi, zi;
	convertVector( &xi, &yi, &zi );


	// Increment the new position of the boid.
	// This prevents the boid from jumping.
	if ( timeSpan <= MAXIMUM_TIMESPAN )
	{
		x += xi * D3DVALUE( timeSpan );
		y += yi * D3DVALUE( timeSpan );
		z += zi * D3DVALUE( timeSpan );
	}

	// Reset the start time for the flyers motion.
	startTime = clock( );

	wrapAround( &x, &y, &z );  // Make the screen loop around.

	// Set the new position of the flyer.
	meshFrame -> SetPosition( scene, x, y, z );

	// Animate the boids wings if any.
	// Only if the selected mesh is the animated -
	// - one and the simulation is running.
	if ( leftWingFrame && !stopped && ( timeSpan <= MAXIMUM_TIMESPAN ) )
	{
		// Get the system time in milliseconds.
		ULONG milliTime = clock( ) * 1000 / CLOCKS_PER_SEC *
									win -> getWingStrokesPerSecond( );
		// Remove the seconds, just keeping the milliseconds.
		milliTime %= 1000;
		// Convert the milliseconds into radians.
		D3DVALUE radians = milliTime / 159.1549431;
		// Calculate the X vector component.
		D3DVALUE xVector = D3DVALUE( sin( radians ) );

		// Set the new orientation of the wings.
		leftWingFrame -> SetOrientation( meshFrame, 0, 0, 1,
													xVector, 2, 0 );
		rightWingFrame -> SetOrientation( meshFrame, 0, 0, 1,
													-xVector, 2, 0 );
	}
}

⌨️ 快捷键说明

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