435 lines
11 KiB
C++
435 lines
11 KiB
C++
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Engine Source File.
|
|
// Copyright (C), Crytek Studios, 2002.
|
|
// -------------------------------------------------------------------------
|
|
// File name: bugsflock.cpp
|
|
// Version: v1.00
|
|
// Created: 11/4/2003 by Timur.
|
|
// Compilers: Visual Studio.NET
|
|
// Description:
|
|
// -------------------------------------------------------------------------
|
|
// History:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "StdAfx.h"
|
|
#include "BugsFlock.h"
|
|
|
|
#include <CryCharAnimationParams.h>
|
|
|
|
#define BUGS_SCARE_DISTANCE 3.0f
|
|
|
|
enum EBugsFlockBehaviors
|
|
{
|
|
EBUGS_BUG,
|
|
EBUGS_DRAGONFLY,
|
|
EBUGS_FROG,
|
|
};
|
|
|
|
//! Return random value in [-1,1] range.
|
|
inline float frand()
|
|
{
|
|
return ((float)rand()*2.0f / RAND_MAX) - 1.0f;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBugsFlock::CBugsFlock( int id,CFlockManager *flockMgr )
|
|
: CFlock( id,flockMgr )
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBugsFlock::~CBugsFlock()
|
|
{
|
|
ReleaseObjects();
|
|
}
|
|
|
|
void CBugsFlock::ReleaseObjects()
|
|
{
|
|
for (unsigned int i = 0; i < m_objects.size(); i++)
|
|
{
|
|
if (m_objects[i])
|
|
m_bc.engine->ReleaseObject( m_objects[i] );
|
|
}
|
|
m_objects.clear();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBugsFlock::CreateBoids( SBoidsCreateContext &ctx )
|
|
{
|
|
int i;
|
|
|
|
ClearBoids();
|
|
ReleaseObjects();
|
|
|
|
AABB bbox;
|
|
bbox.min.Set(0,0,0);
|
|
bbox.max.Set(0,0,0);
|
|
for (i = 0; i < (int)ctx.models.size(); i++)
|
|
{
|
|
IStatObj *pObj = m_bc.engine->MakeObject( ctx.models[i].c_str() );
|
|
if (pObj)
|
|
{
|
|
bbox.Add( pObj->GetBoxMin() );
|
|
bbox.Add( pObj->GetBoxMax() );
|
|
m_objects.push_back( pObj );
|
|
}
|
|
}
|
|
|
|
int numObj = m_objects.size();
|
|
|
|
// Different boids.
|
|
for (i = 0; i < ctx.boidsCount; i++)
|
|
{
|
|
CBoidBug *boid = new CBoidBug( m_bc );
|
|
float radius = m_bc.MaxAttractDistance;
|
|
boid->m_pos = m_origin + Vec3(radius*frand(),radius*frand(),
|
|
m_bc.MinHeight+(m_bc.MaxHeight-m_bc.MinHeight)*frand() );
|
|
|
|
boid->m_heading = GetNormalized( Vec3(frand(),frand(),0) );
|
|
boid->m_speed = m_bc.MinSpeed + (m_bc.MaxSpeed-m_bc.MinSpeed)*frand();
|
|
|
|
//boid->CalcRandomTarget( GetPos(),m_bc );
|
|
if (!ctx.characterModel.empty())
|
|
{
|
|
if (numObj == 0 || (i % (numObj+1) == 0))
|
|
{
|
|
boid->m_object = m_flockMgr->GetSystem()->GetIAnimationSystem()->MakeCharacter( ctx.characterModel.c_str() );
|
|
if (boid->m_object)
|
|
{
|
|
Vec3 mins,maxs;
|
|
boid->m_object->GetBBox(mins,maxs);
|
|
bbox.Add( mins );
|
|
bbox.Add( maxs );
|
|
if (!ctx.animation.empty())
|
|
{
|
|
// Play animation in loop.
|
|
boid->m_object->StartAnimation( ctx.animation.c_str(), CryCharAnimationParams() );
|
|
ICryAnimationSet *animSet = boid->m_object->GetModel()->GetAnimationSet();
|
|
if (animSet)
|
|
{
|
|
animSet->SetLoop( animSet->Find(ctx.animation.c_str()),true );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (numObj > 0)
|
|
boid->m_objectId = (i % numObj);
|
|
else
|
|
boid->m_objectId = 0;
|
|
|
|
//boid->m_p = m_flockMgr->GetSystem()->GetI3DEngine()->MakeCharacter( model.c_str() );
|
|
/*
|
|
if (boid->m_object)
|
|
{
|
|
boid->Physicalize(m_bc);
|
|
}
|
|
*/
|
|
AddBoid(boid);
|
|
}
|
|
m_bc.fBoidRadius = GetLength(bbox.max - bbox.min) * m_bc.boidScale;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CBoidBug::CBoidBug( SBoidContext &bc )
|
|
: CBoidObject( bc )
|
|
{
|
|
m_objectId = 0;
|
|
m_onGround = 0;
|
|
}
|
|
|
|
void CBoidBug::UpdateBugsBehavior( float dt,SBoidContext &bc )
|
|
{
|
|
if ((rand() % 10) == 0)
|
|
{
|
|
// Randomally modify heading vector.
|
|
m_heading.x += frand()*0.2f*bc.factorAlignment; // Used as random movement.
|
|
m_heading.y += frand()*0.2f*bc.factorAlignment;
|
|
m_heading.z += frand()*0.1f*bc.factorAlignment;
|
|
m_heading = GetNormalized(m_heading);
|
|
if (bc.behavior == EBUGS_DRAGONFLY)
|
|
m_speed = bc.MinSpeed + (bc.MaxSpeed - bc.MinSpeed)*frand();
|
|
}
|
|
|
|
// Avoid player.
|
|
Vec3 fromPlayerVec = Vec3( m_pos.x-bc.playerPos.x, m_pos.y-bc.playerPos.y, 0 );
|
|
if (GetLengthSquared(fromPlayerVec) < BUGS_SCARE_DISTANCE*BUGS_SCARE_DISTANCE) // 2 meters.
|
|
{
|
|
float d = (BUGS_SCARE_DISTANCE - fromPlayerVec.Length());
|
|
m_accel += 5.0f * fromPlayerVec * d;
|
|
}
|
|
|
|
// Maintain average speed.
|
|
float targetSpeed = (bc.MaxSpeed + bc.MinSpeed)/2;
|
|
m_accel -= m_heading*(m_speed-targetSpeed)*0.5f;
|
|
|
|
//m_accel = (m_targetPos - m_pos)*bc.factorAttractToOrigin;
|
|
|
|
if (m_pos.z < bc.terrainZ+bc.MinHeight)
|
|
{
|
|
m_accel.z = (bc.terrainZ+bc.MinHeight-m_pos.z)*bc.factorAttractToOrigin;
|
|
}
|
|
else if (m_pos.z > bc.terrainZ+bc.MaxHeight)
|
|
{
|
|
m_accel.z = -(m_pos.z-bc.terrainZ+bc.MinHeight)*bc.factorAttractToOrigin;
|
|
}
|
|
else
|
|
{
|
|
// Allways try to accelerate in direction oposite to current in Z axis.
|
|
m_accel.z += -m_heading.z * 0.2f;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBoidBug::UpdateDragonflyBehavior( float dt,SBoidContext &bc )
|
|
{
|
|
UpdateBugsBehavior( dt,bc );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBoidBug::UpdateFrogsBehavior( float dt,SBoidContext &bc )
|
|
{
|
|
if (m_onGround)
|
|
{
|
|
if (((rand() % 100) == 1) ||
|
|
(GetLengthSquared(Vec3(bc.playerPos.x-m_pos.x,bc.playerPos.y-m_pos.y,0)) < BUGS_SCARE_DISTANCE*BUGS_SCARE_DISTANCE))
|
|
{
|
|
// Sacred by player or random jump.
|
|
m_onGround = false;
|
|
m_heading = m_pos - bc.playerPos;
|
|
if (m_heading != Vec3(0,0,0))
|
|
m_heading = GetNormalized(m_heading);
|
|
else
|
|
m_heading = GetNormalized(Vec3(frand(),frand(),frand()));
|
|
m_heading.z = 0.2f + (frand()+1.0f)*0.5f;
|
|
m_heading += Vec3(frand()*0.3f,frand()*0.3f,0 );
|
|
if (m_heading != Vec3(0,0,0))
|
|
m_heading = GetNormalized(m_heading);
|
|
else
|
|
m_heading = GetNormalized(Vec3(frand(),frand(),frand()));
|
|
m_speed = bc.MaxSpeed;
|
|
}
|
|
}
|
|
|
|
bc.terrainZ = bc.engine->GetTerrainElevation(m_pos.x,m_pos.y);
|
|
|
|
float range = bc.MaxAttractDistance;
|
|
|
|
Vec3 origin = bc.flockPos;
|
|
|
|
if (bc.followPlayer)
|
|
{
|
|
origin = bc.playerPos;
|
|
}
|
|
|
|
// Keep in range.
|
|
if (bc.followPlayer)
|
|
{
|
|
if (m_pos.x < origin.x - range)
|
|
m_pos.x = origin.x + range;
|
|
if (m_pos.y < origin.y - range)
|
|
m_pos.y = origin.y + range;
|
|
if (m_pos.x > origin.x + range)
|
|
m_pos.x = origin.x - range;
|
|
if (m_pos.y > origin.y + range)
|
|
m_pos.y = origin.y - range;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
if (bc.behavior == EBUGS_BUG || bc.behavior == EBUGS_DRAGONFLY)
|
|
{
|
|
if (m_pos.x < origin.x-range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
if (m_pos.y < origin.y-range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
if (m_pos.x > origin.x+range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
if (m_pos.y > origin.y+range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
}
|
|
*/
|
|
}
|
|
m_accel.Set( 0,0,-10 );
|
|
|
|
bool bBanking = m_object != 0;
|
|
CalcMovement( dt,bc,bBanking );
|
|
|
|
if (m_pos.z < bc.terrainZ+0.1f)
|
|
{
|
|
// Land.
|
|
m_pos.z = bc.terrainZ+0.1f;
|
|
m_onGround = true;
|
|
m_speed = 0;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBoidBug::Update( float dt,SBoidContext &bc )
|
|
{
|
|
if (bc.behavior == EBUGS_FROG)
|
|
{
|
|
UpdateFrogsBehavior( dt,bc );
|
|
return;
|
|
}
|
|
|
|
if (m_onGround)
|
|
{
|
|
if (GetLengthSquared(Vec3(bc.playerPos.x-m_pos.x,bc.playerPos.y-m_pos.y,0)) < BUGS_SCARE_DISTANCE*BUGS_SCARE_DISTANCE)
|
|
{
|
|
// Sacred by player, fast takeoff.
|
|
m_onGround = false;
|
|
m_heading = GetNormalized(m_pos - bc.playerPos);
|
|
m_heading.z = 0.3f;
|
|
m_heading = GetNormalized(m_heading);
|
|
m_speed = 1;
|
|
}
|
|
else if ((rand() % 50) == 0)
|
|
{
|
|
// take off.
|
|
m_onGround = false;
|
|
m_heading.z = 0.2f;
|
|
m_heading = GetNormalized(m_heading);
|
|
}
|
|
return;
|
|
}
|
|
// Keep in range.
|
|
bc.terrainZ = bc.engine->GetTerrainElevation(m_pos.x,m_pos.y);
|
|
|
|
float range = bc.MaxAttractDistance;
|
|
|
|
Vec3 origin = bc.flockPos;
|
|
|
|
if (bc.followPlayer)
|
|
{
|
|
origin = bc.playerPos;
|
|
}
|
|
|
|
if (bc.followPlayer)
|
|
{
|
|
if (m_pos.x < origin.x - range)
|
|
m_pos.x = origin.x + range;
|
|
if (m_pos.y < origin.y - range)
|
|
m_pos.y = origin.y + range;
|
|
if (m_pos.x > origin.x + range)
|
|
m_pos.x = origin.x - range;
|
|
if (m_pos.y > origin.y + range)
|
|
m_pos.y = origin.y - range;
|
|
}
|
|
else
|
|
{
|
|
if (bc.behavior == EBUGS_BUG || bc.behavior == EBUGS_DRAGONFLY)
|
|
{
|
|
if (m_pos.x < origin.x-range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
if (m_pos.y < origin.y-range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
if (m_pos.x > origin.x+range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
if (m_pos.y > origin.y+range)
|
|
m_accel = (origin - m_pos)*bc.factorAttractToOrigin;
|
|
}
|
|
}
|
|
|
|
if (bc.behavior == EBUGS_BUG)
|
|
{
|
|
UpdateBugsBehavior( dt,bc );
|
|
}
|
|
else if (bc.behavior == EBUGS_DRAGONFLY)
|
|
{
|
|
UpdateDragonflyBehavior( dt,bc );
|
|
}
|
|
else if (bc.behavior == EBUGS_FROG)
|
|
{
|
|
UpdateFrogsBehavior( dt,bc );
|
|
}
|
|
else
|
|
{
|
|
UpdateBugsBehavior( dt,bc );
|
|
}
|
|
|
|
bool bBanking = m_object != 0;
|
|
CalcMovement( dt,bc,bBanking );
|
|
|
|
if (m_pos.z < bc.terrainZ+0.1f)
|
|
{
|
|
// Land.
|
|
m_pos.z = bc.terrainZ+0.1f;
|
|
if (!bc.noLanding && (rand()%10) == 0)
|
|
{
|
|
m_onGround = true;
|
|
m_speed = 0;
|
|
m_accel.Set(0,0,0);
|
|
}
|
|
}
|
|
|
|
if (m_pos.z < bc.waterLevel)
|
|
m_pos.z = bc.waterLevel;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CBoidBug::Render( SRendParams &rp,CCamera &cam,SBoidContext &bc )
|
|
{
|
|
// Cull boid.
|
|
if (!cam.IsSphereVisibleFast( Sphere(m_pos,bc.fBoidRadius*bc.boidScale) ))
|
|
return;
|
|
|
|
Vec3 oldheading;
|
|
if (bc.behavior == EBUGS_FROG)
|
|
{
|
|
// Frogs/grasshopers do not change z orientation.
|
|
oldheading = m_heading;
|
|
if (fabsf(m_heading.x > 0.0001f) || fabsf(m_heading.y > 0.0001f))
|
|
m_heading = GetNormalized( Vec3(m_heading.x,m_heading.y,0 ) );
|
|
else
|
|
{
|
|
m_heading = GetNormalized(Vec3(frand(),frand(),frand()));
|
|
oldheading = m_heading;
|
|
}
|
|
}
|
|
|
|
Matrix44 mtx;
|
|
CalcMatrix( mtx );
|
|
mtx.ScaleMatRow( Vec3(bc.boidScale,bc.boidScale,bc.boidScale) );
|
|
|
|
if (bc.behavior == EBUGS_FROG)
|
|
{
|
|
m_heading = oldheading;
|
|
}
|
|
|
|
rp.pMatrix = &mtx;
|
|
|
|
if (m_object)
|
|
{
|
|
m_object->Update();
|
|
m_object->Draw( rp, Vec3(zero) );
|
|
}
|
|
else
|
|
{
|
|
//pStatObj
|
|
CBugsFlock *flock = (CBugsFlock*)m_flock;
|
|
int numo = flock->m_objects.size();
|
|
if (numo == 0)
|
|
return;
|
|
IStatObj *pObj = flock->m_objects[m_objectId % numo];
|
|
if (!pObj)
|
|
return;
|
|
|
|
pObj->Render( rp, Vec3(zero),0);
|
|
}
|
|
}
|
|
|
|
void CBugsFlock::PreloadInstanceResources(Vec3d vPrevPortalPos, float fPrevPortalDistance, float fTime)
|
|
{
|
|
for (unsigned int i = 0; i < m_objects.size(); i++)
|
|
{
|
|
IStatObj * pStatObj = m_objects[i];
|
|
float fDistance = fPrevPortalDistance + vPrevPortalPos.GetDistance((m_bounds.min+m_bounds.min)*0.5f);
|
|
pStatObj->PreloadResources(fDistance, fTime, 0);
|
|
}
|
|
} |