1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 15:02:34 +02:00

Use view frustum for traffic spawning and cleanup

This commit is contained in:
Daniel Evans 2016-06-26 03:55:31 +01:00
parent 50eee4e5ff
commit 69e7d32f3a
7 changed files with 93 additions and 52 deletions

View File

@ -9,6 +9,7 @@
class GameObject;
class GameWorld;
class ViewCamera;
class TrafficDirector
{
@ -16,17 +17,17 @@ public:
TrafficDirector(AIGraph* graph, GameWorld* world);
std::vector< AIGraphNode* > findAvailableNodes(AIGraphNode::NodeType type, const glm::vec3& near, float radius);
std::vector< AIGraphNode* > findAvailableNodes(AIGraphNode::NodeType type, const ViewCamera& camera, float radius);
void setDensity(AIGraphNode::NodeType type, float density);
/**
* Creates new traffic at available locations.
* @param center The area to spawn around
* @param camera The camera to spawn around
* @param radius the maximum distance to spawn in
* @param max The maximum number of traffic to create.
*/
std::vector<GameObject*> populateNearby( const glm::vec3& center, float radius, int maxSpawn = -1 );
std::vector<GameObject*> populateNearby( const ViewCamera& camera, float radius, int maxSpawn = -1 );
/**
* Sets the maximum number of pedestrians and cars in the traffic system
@ -40,4 +41,4 @@ private:
float carDensity;
int maximumPedestrians;
int maximumCars;
};
};

View File

@ -21,6 +21,7 @@ class CharacterObject;
class InstanceObject;
class VehicleObject;
class ViewCamera;
#include <render/VisualFX.hpp>
#include <data/ObjectData.hpp>
@ -76,10 +77,23 @@ public:
* @param name The name of the IPL as it appears in the games' gta.dat
*/
bool placeItems(const std::string& name);
void createTraffic(const glm::vec3& near);
void cleanupTraffic(const glm::vec3& focus);
/**
* @brief createTraffic spawn transitory peds and vehicles
* @param viewCamera The camera to create traffic near
*
* The position and frustum of the passed in camera is used to determine
* the radius where traffic can be spawned, and the frustum is used to avoid
* spawning traffic in view of the player.
*/
void createTraffic(const ViewCamera& viewCamera);
/**
* @brief cleanupTraffic Cleans up traffic too far away from the given camera
* @param viewCamera
*/
void cleanupTraffic(const ViewCamera& viewCamera);
/**
* Creates an instance
*/

View File

@ -6,6 +6,7 @@
#include <objects/GameObject.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/VehicleObject.hpp>
#include <render/ViewCamera.hpp>
#include <core/Logger.hpp>
#include <glm/gtx/string_cast.hpp>
@ -18,36 +19,39 @@ maximumPedestrians(20), maximumCars(10)
}
std::vector< AIGraphNode* > TrafficDirector::findAvailableNodes(AIGraphNode::NodeType type, const glm::vec3& near, float radius)
std::vector< AIGraphNode* > TrafficDirector::findAvailableNodes(AIGraphNode::NodeType type, const ViewCamera& camera, float radius)
{
std::vector<AIGraphNode*> available;
available.reserve(20);
graph->gatherExternalNodesNear(near, radius, available);
graph->gatherExternalNodesNear(camera.position, radius, available);
float density = type == AIGraphNode::Vehicle ? carDensity : pedDensity;
float minDist = (10.f / density) * (10.f / density);
float halfRadius2 = std::pow(radius / 2.f, 2.f);
// Determine if anything in the open set is blocked
for ( auto it = available.begin(); it != available.end(); )
{
// Check if any of the nearby nodes are blocked by a pedestrian standing on it
// or because it's inside the view frustum
for (auto it = available.begin(); it != available.end();) {
bool blocked = false;
for ( auto obj : world->pedestrianPool.objects )
{
// Sanity check
if ( glm::distance2( (*it)->position, obj.second->getPosition() ) <= minDist )
{
float dist2 = glm::distance2(camera.position, (*it)->position);
for (auto& obj : world->pedestrianPool.objects) {
if (glm::distance2( (*it)->position, obj.second->getPosition() ) <= minDist) {
blocked = true;
break;
}
}
// Check that we're not going to spawn something right where the player is looking
if (dist2 <= halfRadius2 && camera.frustum.intersects((*it)->position, 1.f)) {
blocked = true;
}
if ( blocked )
{
if (blocked) {
it = available.erase( it );
}
else
{
else {
it++;
}
}
@ -68,17 +72,28 @@ void TrafficDirector::setDensity(AIGraphNode::NodeType type, float density)
}
}
std::vector<GameObject*> TrafficDirector::populateNearby(const glm::vec3& center, float radius, int maxSpawn)
std::vector<GameObject*> TrafficDirector::populateNearby(const ViewCamera& camera, float radius, int maxSpawn)
{
int availablePeds = maximumPedestrians - world->pedestrianPool.objects.size();
std::vector<GameObject*> created;
// Spawn vehicles at vehicle generators
/// @todo avoid spawning in view frustum unless alwaysSpawn == true
/// @todo Check how "in player view" should be determined.
// Don't check the frustum for things more than 1/2 of the radius away
// so that things will spawn as you drive towards them
float halfRadius2 = std::pow(radius/ 2.f, 2.f);
// Spawn vehicles at vehicle generators
for (auto& gen : world->state->vehicleGenerators) {
if (glm::distance2(center, gen.position) < radius * radius) {
float dist2 = glm::distance2(camera.position, gen.position);
if (dist2 < radius * radius) {
if (dist2 <= halfRadius2 && camera.frustum.intersects(gen.position, 1.f)) {
if (! gen.alwaysSpawn) {
// Don't spawn in the view frustum unless we're forced to
continue;
}
}
auto spawned = world->tryToSpawnVehicle(gen);
if (spawned) {
created.push_back(spawned);
@ -87,7 +102,7 @@ std::vector<GameObject*> TrafficDirector::populateNearby(const glm::vec3& center
}
auto type = AIGraphNode::Pedestrian;
auto available = findAvailableNodes(type, center, radius);
auto available = findAvailableNodes(type, camera, radius);
if( availablePeds <= 0 )
{

View File

@ -23,6 +23,12 @@
#include <data/CutsceneData.hpp>
#include <loaders/LoaderCutsceneDAT.hpp>
#include <render/ViewCamera.hpp>
// Behaviour Tuning
constexpr float kMaxTrafficSpawnRadius = 100.f;
constexpr float kMaxTrafficCleanupRadius = kMaxTrafficSpawnRadius * 1.25f;
class WorldCollisionDispatcher : public btCollisionDispatcher
{
public:
@ -218,39 +224,38 @@ InstanceObject *GameWorld::createInstance(const uint16_t id, const glm::vec3& po
return nullptr;
}
void GameWorld::createTraffic(const glm::vec3& near)
void GameWorld::createTraffic(const ViewCamera& viewCamera)
{
TrafficDirector director(&aigraph, this);
director.populateNearby( near, 100, 5 );
director.populateNearby( viewCamera, kMaxTrafficSpawnRadius, 5 );
}
void GameWorld::cleanupTraffic(const glm::vec3& focus)
void GameWorld::cleanupTraffic(const ViewCamera& focus)
{
for ( auto& p : pedestrianPool.objects )
{
if ( p.second->getLifetime() != GameObject::TrafficLifetime )
{
for (auto& p : pedestrianPool.objects) {
if (p.second->getLifetime() != GameObject::TrafficLifetime) {
continue;
}
if ( glm::distance( focus, p.second->getPosition() ) >= 100.f )
{
destroyObjectQueued( p.second );
if (glm::distance( focus.position, p.second->getPosition() ) >= kMaxTrafficCleanupRadius) {
if (! focus.frustum.intersects(p.second->getPosition(), 1.f)) {
destroyObjectQueued( p.second );
}
}
}
for ( auto& p : vehiclePool.objects )
{
if ( p.second->getLifetime() != GameObject::TrafficLifetime )
{
for ( auto& p : vehiclePool.objects ) {
if (p.second->getLifetime() != GameObject::TrafficLifetime) {
continue;
}
if ( glm::distance( focus, p.second->getPosition() ) >= 100.f )
{
destroyObjectQueued( p.second );
if (glm::distance( focus.position, p.second->getPosition() ) >= kMaxTrafficCleanupRadius) {
if (! focus.frustum.intersects(p.second->getPosition(), 1.f)) {
destroyObjectQueued( p.second );
}
}
}
destroyQueuedObjects();
}

View File

@ -463,13 +463,13 @@ void RWGame::tick(float dt)
throw;
}
}
if ( state->playerObject )
{
/// @todo this doesn't make sense as the condition
if ( state->playerObject ) {
nextCam.frustum.update(nextCam.frustum.projection() * nextCam.getView());
// Use the current camera position to spawn pedestrians.
auto p = nextCam.position;
world->cleanupTraffic(p);
world->createTraffic(p);
world->cleanupTraffic(nextCam);
world->createTraffic(nextCam);
}
}

View File

@ -1,6 +1,7 @@
#include <boost/test/unit_test.hpp>
#include <glm/gtx/string_cast.hpp>
#include <objects/InstanceObject.hpp>
#include <render/ViewCamera.hpp>
#include "test_globals.hpp"
BOOST_AUTO_TEST_SUITE(LifetimeTests)
@ -19,7 +20,9 @@ BOOST_AUTO_TEST_CASE(test_cleanup)
BOOST_CHECK( search != objects.end() );
}
Global::get().e->cleanupTraffic(glm::vec3(0.f, 0.f, 0.f));
ViewCamera testCamera;
testCamera.position = glm::vec3(0.f, 0.f, 0.f);
Global::get().e->cleanupTraffic(testCamera);
{
auto search = objects.find(id);

View File

@ -5,6 +5,7 @@
#include <ai/AIGraph.hpp>
#include <objects/InstanceObject.hpp>
#include <objects/CharacterObject.hpp>
#include <render/ViewCamera.hpp>
bool operator!=(const AIGraphNode* lhs, const glm::vec3 &rhs) { return lhs->position != rhs; }
std::ostream &operator<<(std::ostream &os, const AIGraphNode* yt) { os << glm::to_string(yt->position); return os; }
@ -68,8 +69,10 @@ BOOST_AUTO_TEST_CASE(test_available_nodes)
graph.createPathNodes(glm::vec3(), glm::quat(), path);
TrafficDirector director(&graph, Global::get().e);
ViewCamera testCamera(glm::vec3(-5.f, -5.f, 0.f));
auto open = director.findAvailableNodes(AIGraphNode::Pedestrian, glm::vec3(-5.f, -5.f, 0.f), 10.f);
auto open = director.findAvailableNodes(AIGraphNode::Pedestrian, testCamera, 10.f);
std::vector<glm::vec3> expected {
{ 0.f,-10.f, 0.f },