📄 boidsflyer.cpp
字号:
}
// 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 + -