mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-22 02:12:45 +01:00
Use view frustum for traffic spawning and cleanup
This commit is contained in:
parent
50eee4e5ff
commit
69e7d32f3a
@ -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;
|
||||
};
|
||||
};
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 },
|
||||
|
Loading…
Reference in New Issue
Block a user