From fed12caf68fb8b8f821b79a79c4fd25375890435 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 30 Dec 2009 18:23:58 +0000 Subject: [PATCH] Add PandAI (and contribbase) --- contrib/src/ai/aiBehaviors.cxx | 1463 ++++++++++++++++++++++ contrib/src/ai/aiBehaviors.h | 193 +++ contrib/src/ai/aiCharacter.cxx | 127 ++ contrib/src/ai/aiCharacter.h | 76 ++ contrib/src/ai/aiGlobals.h | 28 + contrib/src/ai/aiNode.cxx | 53 + contrib/src/ai/aiNode.h | 85 ++ contrib/src/ai/aiPathFinder.cxx | 377 ++++++ contrib/src/ai/aiPathFinder.h | 61 + contrib/src/ai/aiWorld.cxx | 258 ++++ contrib/src/ai/aiWorld.h | 87 ++ contrib/src/ai/ai_composite.cxx | 1 + contrib/src/ai/ai_composite1.cxx | 20 + contrib/src/ai/arrival.cxx | 120 ++ contrib/src/ai/arrival.h | 49 + contrib/src/ai/config_ai.cxx | 55 + contrib/src/ai/config_ai.h | 26 + contrib/src/ai/evade.cxx | 86 ++ contrib/src/ai/evade.h | 48 + contrib/src/ai/flee.cxx | 104 ++ contrib/src/ai/flee.h | 52 + contrib/src/ai/flock.cxx | 42 + contrib/src/ai/flock.h | 62 + contrib/src/ai/globals.h | 33 + contrib/src/ai/meshNode.cxx | 43 + contrib/src/ai/meshNode.h | 67 + contrib/src/ai/obstacleAvoidance.cxx | 119 ++ contrib/src/ai/obstacleAvoidance.h | 42 + contrib/src/ai/pathFind.cxx | 413 ++++++ contrib/src/ai/pathFind.h | 70 ++ contrib/src/ai/pathFollow.cxx | 138 ++ contrib/src/ai/pathFollow.h | 50 + contrib/src/ai/pursue.cxx | 60 + contrib/src/ai/pursue.h | 42 + contrib/src/ai/seek.cxx | 66 + contrib/src/ai/seek.h | 44 + contrib/src/ai/wander.cxx | 140 +++ contrib/src/ai/wander.h | 40 + contrib/src/contribbase/contribbase.h | 26 + contrib/src/contribbase/contribsymbols.h | 42 + 40 files changed, 4908 insertions(+) create mode 100644 contrib/src/ai/aiBehaviors.cxx create mode 100644 contrib/src/ai/aiBehaviors.h create mode 100644 contrib/src/ai/aiCharacter.cxx create mode 100644 contrib/src/ai/aiCharacter.h create mode 100644 contrib/src/ai/aiGlobals.h create mode 100644 contrib/src/ai/aiNode.cxx create mode 100644 contrib/src/ai/aiNode.h create mode 100644 contrib/src/ai/aiPathFinder.cxx create mode 100644 contrib/src/ai/aiPathFinder.h create mode 100644 contrib/src/ai/aiWorld.cxx create mode 100644 contrib/src/ai/aiWorld.h create mode 100644 contrib/src/ai/ai_composite.cxx create mode 100644 contrib/src/ai/ai_composite1.cxx create mode 100644 contrib/src/ai/arrival.cxx create mode 100644 contrib/src/ai/arrival.h create mode 100644 contrib/src/ai/config_ai.cxx create mode 100644 contrib/src/ai/config_ai.h create mode 100644 contrib/src/ai/evade.cxx create mode 100644 contrib/src/ai/evade.h create mode 100644 contrib/src/ai/flee.cxx create mode 100644 contrib/src/ai/flee.h create mode 100644 contrib/src/ai/flock.cxx create mode 100644 contrib/src/ai/flock.h create mode 100644 contrib/src/ai/globals.h create mode 100644 contrib/src/ai/meshNode.cxx create mode 100644 contrib/src/ai/meshNode.h create mode 100644 contrib/src/ai/obstacleAvoidance.cxx create mode 100644 contrib/src/ai/obstacleAvoidance.h create mode 100644 contrib/src/ai/pathFind.cxx create mode 100644 contrib/src/ai/pathFind.h create mode 100644 contrib/src/ai/pathFollow.cxx create mode 100644 contrib/src/ai/pathFollow.h create mode 100644 contrib/src/ai/pursue.cxx create mode 100644 contrib/src/ai/pursue.h create mode 100644 contrib/src/ai/seek.cxx create mode 100644 contrib/src/ai/seek.h create mode 100644 contrib/src/ai/wander.cxx create mode 100644 contrib/src/ai/wander.h create mode 100644 contrib/src/contribbase/contribbase.h create mode 100644 contrib/src/contribbase/contribsymbols.h diff --git a/contrib/src/ai/aiBehaviors.cxx b/contrib/src/ai/aiBehaviors.cxx new file mode 100644 index 0000000000..c590598e6c --- /dev/null +++ b/contrib/src/ai/aiBehaviors.cxx @@ -0,0 +1,1463 @@ +// Filename: aiBehaviors.cxx +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "aiBehaviors.h" + +static const float _PI = 3.14; + +AIBehaviors::AIBehaviors() { + _steering_force = LVecBase3f(0.0, 0.0, 0.0); + _behaviors_flags = _behaviors_flags & BT_none; + _previous_conflict = false; + _conflict = false; + + _seek_obj = NULL; + _flee_obj = NULL; + _pursue_obj = NULL; + _evade_obj = NULL; + _arrival_obj = NULL; + _wander_obj = NULL; + _flock_group = NULL; + _path_follow_obj = NULL; + _path_find_obj = NULL; + _obstacle_avoidance_obj = NULL; + + turn_off("seek"); + turn_off("flee"); + turn_off("pursue"); + turn_off("evade"); + turn_off("arrival"); + turn_off("flock"); + turn_off("wander"); + turn_off("obstacle_avoidance"); +} + +AIBehaviors::~AIBehaviors() { + +} + +/////////////////////////////////////////////////////////////////// +// Function: is_conflict +// Description: Checks for conflict between steering forces. +// If there is a conflict it returns 'true' +// and sets _conflict to 'true'. +// If there is no conflict it returns 'false' +// and sets _conflict to 'false'. +/////////////////////////////////////////////////////////////////// +bool AIBehaviors::is_conflict() { + int value = int(is_on(BT_seek)) + int(is_on(BT_flee)) + int(is_on(BT_pursue)) + + int(is_on(BT_evade)) + int(is_on(BT_wander)) + int(is_on(BT_flock)) + + int(is_on(BT_obstacle_avoidance)); + + if (value > 1) { + if (_previous_conflict == false) { + if (is_on(BT_seek)) { + _seek_force *= _seek_obj->_seek_weight; + } + + if (is_on(BT_flee)) { + LVecBase3f dirn = _flee_force; + dirn.normalize(); + _flee_force = _steering_force.length() * dirn * _flee_obj->_flee_weight; + } + + if (is_on(BT_pursue)) { + _pursue_force *= _pursue_obj->_pursue_weight; + } + + if (is_on(BT_evade)) { + LVecBase3f dirn = _evade_force; + dirn.normalize(); + _evade_force = _steering_force.length() * dirn * _evade_obj->_evade_weight; + } + + if (is_on(BT_flock)) { + _flock_force *= _flock_weight; + } + + if (is_on(BT_wander)) { + _wander_force *= _wander_obj->_wander_weight; + } + + _previous_conflict = true; + } + + _conflict = true; + return true; + } + + _conflict = false; + _previous_conflict = false; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: accumulate_force +// Description: This function updates the individual steering forces +// for each of the ai characters. +// These accumulated forces are eventually what +// comprise the resultant steering force of the +// character. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::accumulate_force(string force_type, LVecBase3f force) { + + LVecBase3f old_force; + + if (force_type == "seek") { + old_force = _seek_force; + _seek_force = old_force + force; + } + + if (force_type == "flee") { + old_force = _flee_force; + _flee_force = old_force + force; + } + + if (force_type == "pursue") { + old_force = _pursue_force; + double new_force = old_force.length() + force.length(); + _pursue_force = new_force * _pursue_obj->_pursue_direction; + } + + if (force_type == "evade") { + old_force = _evade_force; + double new_force = old_force.length() + force.length(); + force.normalize(); + _evade_force = new_force * force; + } + + if (force_type == "arrival") { + _arrival_force = force; + } + + if (force_type == "flock") { + old_force = _flock_force; + _flock_force = old_force + force; + } + + if (force_type == "wander") { + old_force = _wander_force; + _wander_force = old_force + force; + } + + if (force_type == "obstacle_avoidance") { + old_force = _obstacle_avoidance_force; + _obstacle_avoidance_force = old_force +force; + } + +} + +//////////////////////////////////////////////////////////////////// +// Function: calculate_prioritized +// Description: This function updates the main steering force +// for the ai character using the accumulate function +// and checks for max force and arrival force. +// It finally returns this steering force which is +// accessed by the update function in the AICharacter +// class. +//////////////////////////////////////////////////////////////////// +LVecBase3f AIBehaviors::calculate_prioritized() { + LVecBase3f force; + + if (is_on(BT_seek)) { + if (_conflict) { + force = _seek_obj->do_seek() * _seek_obj->_seek_weight; + } + else { + force = _seek_obj->do_seek(); + } + accumulate_force("seek",force); + } + + if (is_on(BT_flee_activate)) { + for (_flee_itr = _flee_list.begin(); _flee_itr != _flee_list.end(); _flee_itr++) { + _flee_itr->flee_activate(); + } + } + + if (is_on(BT_flee)) { + for (_flee_itr = _flee_list.begin(); _flee_itr != _flee_list.end(); _flee_itr++) { + if (_flee_itr->_flee_activate_done) { + if (_conflict) { + force = _flee_itr->do_flee() * _flee_itr->_flee_weight; + } + else { + force = _flee_itr->do_flee(); + } + accumulate_force("flee",force); + } + } + } + + if (is_on(BT_pursue)) { + if (_conflict) { + force = _pursue_obj->do_pursue() * _pursue_obj->_pursue_weight; + } + else { + force = _pursue_obj->do_pursue(); + } + accumulate_force("pursue",force); + } + + if (is_on(BT_evade_activate)) { + for (_evade_itr = _evade_list.begin(); _evade_itr != _evade_list.end(); _evade_itr++) { + _evade_itr->evade_activate(); + } + } + + if (is_on(BT_evade)) { + for (_evade_itr = _evade_list.begin(); _evade_itr != _evade_list.end(); _evade_itr++) { + if (_evade_itr->_evade_activate_done) { + if (_conflict) { + force = (_evade_itr->do_evade()) * (_evade_itr->_evade_weight); + } + else { + force = _evade_itr->do_evade(); + } + accumulate_force("evade",force); + } + } + } + + if (is_on(BT_arrival_activate)) { + _arrival_obj->arrival_activate(); + } + + if (is_on(BT_arrival)) { + force = _arrival_obj->do_arrival(); + accumulate_force("arrival",force); + } + + if (is_on(BT_flock_activate)) { + flock_activate(); + } + + if (is_on(BT_flock)) { + if (_conflict) { + force = do_flock() * _flock_weight; + } + else { + force = do_flock(); + } + accumulate_force("flock",force); + } + + if (is_on(BT_wander)) { + if (_conflict) { + force = _wander_obj->do_wander() * _wander_obj->_wander_weight; + } + else { + force = _wander_obj->do_wander(); + } + accumulate_force("wander", force); + } + + if (is_on(BT_obstacle_avoidance_activate)) { + _obstacle_avoidance_obj->obstacle_avoidance_activate(); + } + + if (is_on(BT_obstacle_avoidance)) { + if (_conflict) { + force = _obstacle_avoidance_obj->do_obstacle_avoidance(); + } + else { + force = _obstacle_avoidance_obj->do_obstacle_avoidance(); + } + accumulate_force("obstacle_avoidance", force); + } + + if (_path_follow_obj!=NULL) { + if (_path_follow_obj->_start) { + _path_follow_obj->do_follow(); + } + } + + is_conflict(); + + _steering_force += _seek_force * int(is_on(BT_seek)) + _flee_force * int(is_on(BT_flee)) + + _pursue_force * int(is_on(BT_pursue)) + _evade_force * int(is_on(BT_evade)) + + _flock_force * int(is_on(BT_flock)) + _wander_force * int(is_on(BT_wander)) + + _obstacle_avoidance_force * int(is_on(BT_obstacle_avoidance)); + + if (_steering_force.length() > _ai_char->get_max_force()) { + _steering_force.normalize(); + _steering_force = _steering_force * _ai_char->get_max_force(); + } + + if (is_on(BT_arrival)) { + if (_seek_obj != NULL) { + LVecBase3f dirn = _steering_force; + dirn.normalize(); + _steering_force = ((_steering_force.length() - _arrival_force.length()) * dirn); + } + + if (_pursue_obj != NULL) { + LVecBase3f dirn = _steering_force; + dirn.normalize(); + _steering_force = ((_steering_force.length() - _arrival_force.length()) * _arrival_obj->_arrival_direction); + } + } + return _steering_force; +} + +//////////////////////////////////////////////////////////////////// +// Function: remove_ai +// Description: This function removes individual or all the AIs. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::remove_ai(string ai_type) { + switch (char_to_int(ai_type)) { + case 0: { + remove_ai("seek"); + remove_ai("flee"); + remove_ai("pursue"); + remove_ai("evade"); + remove_ai("arrival"); + remove_ai("flock"); + remove_ai("wander"); + remove_ai("obstacle_avoidance"); + remove_ai("pathfollow"); + break; + } + + case 1: { + if (_seek_obj != NULL) { + turn_off("seek"); + delete _seek_obj; + _seek_obj = NULL; + } + break; + } + + case 2: { + while (!_flee_list.empty()) { + turn_off("flee"); + turn_off("flee_activate"); + _flee_list.pop_front(); + } + break; + } + + case 3: { + if (_pursue_obj != NULL) { + turn_off("pursue"); + delete _pursue_obj; + _pursue_obj = NULL; + } + break; + } + + case 4: { + while (!_evade_list.empty()) { + turn_off("evade"); + turn_off("evade_activate"); + _evade_list.pop_front(); + } + break; + } + + case 5: { + if (_arrival_obj != NULL) { + turn_off("arrival"); + turn_off("arrival_activate"); + delete _arrival_obj; + _arrival_obj = NULL; + } + break; + } + + case 6: { + if (_flock_group != NULL) { + turn_off("flock"); + turn_off("flock_activate"); + _flock_group = NULL; + } + break; + } + + case 7: { + if (_wander_obj != NULL) { + turn_off("wander"); + delete _wander_obj; + _wander_obj = NULL; + } + break; + } + + case 8: { + if (_obstacle_avoidance_obj !=NULL) { + turn_off("obstacle_avoidance"); + delete _obstacle_avoidance_obj; + _obstacle_avoidance_obj = NULL; + } + break; + } + + case 9: { + if (_pursue_obj != NULL && _path_follow_obj != NULL) { + turn_off("pursue"); + delete _pursue_obj; + _pursue_obj = NULL; + delete _path_follow_obj; + _path_follow_obj = NULL; + } + break; + } + + case 16: { + if (_pursue_obj != NULL && _path_follow_obj != NULL) { + turn_off("pursue"); + delete _pursue_obj; + _pursue_obj = NULL; + delete _path_follow_obj; + _path_follow_obj = NULL; + } + break; + } + default: + cout << "Invalid option!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: pause_ai +// Description: This function pauses individual or all the AIs. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::pause_ai(string ai_type) { + switch (char_to_int(ai_type)) { + case 0: { + pause_ai("seek"); + pause_ai("flee"); + pause_ai("pursue"); + pause_ai("evade"); + pause_ai("arrival"); + pause_ai("flock"); + pause_ai("wander"); + pause_ai("obstacle_avoidance"); + pause_ai("pathfollow"); + break; + } + + case 1: { + if (_seek_obj != NULL) { + turn_off("seek"); + } + break; + } + + case 2: { + for (_flee_itr = _flee_list.begin(); _flee_itr != _flee_list.end(); _flee_itr++) { + turn_off("flee"); + turn_off("flee_activate"); + } + break; + } + + case 3: { + if (_pursue_obj != NULL) { + turn_off("pursue"); + } + break; + } + + case 4: { + for (_evade_itr = _evade_list.begin(); _evade_itr != _evade_list.end(); _evade_itr++) { + turn_off("evade"); + turn_off("evade_activate"); + } + break; + } + + case 5: { + if (_arrival_obj != NULL) { + turn_off("arrival"); + turn_off("arrival_activate"); + } + break; + } + + case 6: { + if (_flock_group != NULL) { + turn_off("flock"); + turn_off("flock_activate"); + } + break; + } + + case 7: { + if (_wander_obj != NULL) { + turn_off("wander"); + } + break; + } + + case 8: { + if (BT_obstacle_avoidance != NULL) { + turn_off("obstacle_avoidance"); + turn_off("obstacle_avoidance_activate"); + } + break; + } + + case 9: { + if (_pursue_obj != NULL && _path_follow_obj != NULL) { + turn_off("pursue"); + _path_follow_obj->_start = false; + } + break; + } + case 16: { + if (_pursue_obj != NULL && _path_follow_obj != NULL) { + turn_off("pursue"); + _path_follow_obj->_start = false; + } + break; + } + default: + cout << "Invalid option!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: resume_ai +// Description: This function resumes individual or all the AIs +//////////////////////////////////////////////////////////////////// +void AIBehaviors::resume_ai(string ai_type) { + switch (char_to_int(ai_type)) { + case 0: { + resume_ai("seek"); + resume_ai("flee"); + resume_ai("pursue"); + resume_ai("evade"); + resume_ai("arrival"); + resume_ai("flock"); + resume_ai("wander"); + resume_ai("obstacle_avoidance"); + resume_ai("pathfollow"); + break; + } + + case 1: { + if (_seek_obj != NULL) { + turn_on("seek"); + } + break; + } + + case 2: { + for (_flee_itr = _flee_list.begin(); _flee_itr != _flee_list.end(); _flee_itr++) { + turn_on("flee"); + } + break; + } + + case 3: { + if (_pursue_obj != NULL) { + turn_on("pursue"); + } + break; + } + + case 4: { + for (_evade_itr = _evade_list.begin(); _evade_itr != _evade_list.end(); _evade_itr++) { + turn_on("evade"); + } + break; + } + + case 5: { + if (_arrival_obj != NULL) { + turn_on("arrival"); + } + break; + } + + case 6: { + if (_flock_group != NULL) { + turn_on("flock"); + } + break; + } + + case 7: { + if (_wander_obj != NULL) { + turn_on("wander"); + } + break; + } + + case 8: { + if (_obstacle_avoidance_obj != NULL) { + turn_on("obstacle_avoidance"); + } + break; + } + + case 9: { + if (_pursue_obj != NULL && _path_follow_obj != NULL) { + turn_on("pursue"); + _path_follow_obj->_start = true; + } + break; + } + case 16: { + if (_pursue_obj != NULL && _path_follow_obj != NULL) { + turn_off("pursue"); + _path_follow_obj->_start = false; + } + break; + } + default: + cout << "Invalid option!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: seek +// Description: This function activates seek and makes an object +// of the Seek class. +// This is the function we want the user to call for +// seek to be done. +// This function is overloaded to accept a NodePath or +// an LVecBase3f. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::seek(NodePath target_object, float seek_wt) { + _seek_obj = new Seek(_ai_char, target_object, seek_wt); + turn_on("seek"); +} + +void AIBehaviors::seek(LVecBase3f pos, float seek_wt) { + _seek_obj = new Seek(_ai_char, pos, seek_wt); + turn_on("seek"); +} + +//////////////////////////////////////////////////////////////////// +// Function: flee +// Description: This function activates flee_activate and creates +// an object of the Flee class. +// This function is overloaded to accept a NodePath or +// an LVecBase3f. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::flee(NodePath target_object, double panic_distance, + double relax_distance, float flee_wt) { + _flee_obj = new Flee(_ai_char, target_object, panic_distance, + relax_distance, flee_wt); + _flee_list.insert(_flee_list.end(), *_flee_obj); + + turn_on("flee_activate"); +} + +void AIBehaviors::flee(LVecBase3f pos, double panic_distance, + double relax_distance, float flee_wt) { + _flee_obj = new Flee(_ai_char, pos, panic_distance, + relax_distance, flee_wt); + _flee_list.insert(_flee_list.end(), *_flee_obj); + + turn_on("flee_activate"); +} + +//////////////////////////////////////////////////////////////////// +// Function: pursue +// Description: This function activates pursue. +// This is the function we want the user to call +// for pursue to be done. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::pursue(NodePath target_object, float pursue_wt) { + _pursue_obj = new Pursue(_ai_char, target_object, pursue_wt); + + turn_on("pursue"); +} + +//////////////////////////////////////////////////////////////////// +// Function: evade +// Description: This function activates evade_activate. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::evade(NodePath target_object, double panic_distance, + double relax_distance, float evade_wt) { + _evade_obj = new Evade(_ai_char, target_object, panic_distance, relax_distance, evade_wt); + _evade_list.insert(_evade_list.end(), *_evade_obj); + + turn_on("evade_activate"); +} + +//////////////////////////////////////////////////////////////////// +// Function: arrival +// Description: This function activates arrival. +// This is the function we want the user to call for +// arrival to be done. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::arrival(double distance) { + if (_pursue_obj) { + _arrival_obj = new Arrival(_ai_char, distance); + _arrival_obj->_arrival_type = true; + turn_on("arrival_activate"); + } + else if (_seek_obj) { + _arrival_obj = new Arrival(_ai_char, distance); + _arrival_obj->_arrival_type = false; + turn_on("arrival_activate"); + } + else { + cout << "Note: A Seek or Pursue behavior is required to use Arrival behavior.\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: flock +// Description: This function activates flock. +// This is the function we want the user to call for +// flock to be done. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::flock(float flock_wt) { + _flock_weight = flock_wt; + + _flock_done = false; + turn_on("flock_activate"); +} + +//////////////////////////////////////////////////////////////////// +// Function: flock_activate +// Description: This function checks whether any other behavior +// exists to work with flock. +// When this is true, it calls the do_flock function. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::flock_activate() { + if (is_on(BT_seek) || is_on(BT_flee) || is_on(BT_pursue) || is_on(BT_evade) || is_on(BT_wander)) { + turn_off("flock_activate"); + turn_on("flock"); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: do_flock +// Description: This function contains the logic for flocking +// behavior. This is an emergent behavior and is +// obtained by combining three other behaviors which +// are separation, cohesion and alignment based on +// Craig Reynold's algorithm. Also, this behavior does +// not work by itself. It works only when combined with +// other steering behaviors such as wander, pursue, +// evade, seek and flee. +//////////////////////////////////////////////////////////////////// +LVecBase3f AIBehaviors::do_flock() { + + // Initialize variables required to compute the flocking force + // on the ai char. + unsigned int neighbor_count = 0; + LVecBase3f separation_force = LVecBase3f(0.0, 0.0, 0.0); + LVecBase3f alignment_force = LVecBase3f(0.0, 0.0, 0.0); + LVecBase3f cohesion_force = LVecBase3f(0.0, 0.0, 0.0); + LVecBase3f avg_neighbor_heading = LVecBase3f(0.0, 0.0, 0.0); + LVecBase3f total_neighbor_heading = LVecBase3f(0.0, 0.0, 0.0); + LVecBase3f avg_center_of_mass = LVecBase3f(0.0, 0.0, 0.0); + LVecBase3f total_center_of_mass = LVecBase3f(0.0, 0.0, 0.0); + + // Loop through all the other AI units in the flock to check + // if they are neigbours. + for (unsigned int i = 0; i < _flock_group->_ai_char_list.size(); i++) { + if (_flock_group->_ai_char_list[i]->_name != _ai_char->_name) { + + // Using visibilty cone to detect neighbors. + LVecBase3f dist_vect = _flock_group->_ai_char_list[i]->_ai_char_np.get_pos() + - _ai_char->_ai_char_np.get_pos(); + LVecBase3f ai_char_heading = _ai_char->get_velocity(); + ai_char_heading.normalize(); + + // Check if the current unit is a neighbor. + if (dist_vect.dot(ai_char_heading) > ((dist_vect.length()) * (ai_char_heading.length()) + * cos(_flock_group->_flock_vcone_angle * (_PI / 180))) + && (dist_vect.length() < _flock_group->_flock_vcone_radius)) { + // Separation force calculation. + LVecBase3f ai_char_to_units = _ai_char->_ai_char_np.get_pos() + - _flock_group->_ai_char_list[i]->_ai_char_np.get_pos(); + float to_units_dist = ai_char_to_units.length(); + ai_char_to_units.normalize(); + separation_force += (ai_char_to_units / to_units_dist); + + // Calculating the total heading and center of mass of all the neighbors. + LVecBase3f neighbor_heading = _flock_group->_ai_char_list[i]->get_velocity(); + neighbor_heading.normalize(); + total_neighbor_heading += neighbor_heading; + total_center_of_mass += _flock_group->_ai_char_list[i]->_ai_char_np.get_pos(); + + // Update the neighbor count. + ++neighbor_count; + } + } + } + + if (neighbor_count > 0) { + // Alignment force calculation + avg_neighbor_heading = total_neighbor_heading / neighbor_count; + LVector3f ai_char_heading = _ai_char->get_velocity(); + ai_char_heading.normalize(); + avg_neighbor_heading -= ai_char_heading; + avg_neighbor_heading.normalize(); + alignment_force = avg_neighbor_heading; + + // Cohesion force calculation + avg_center_of_mass = total_center_of_mass / neighbor_count; + LVecBase3f cohesion_dir = avg_center_of_mass - _ai_char->_ai_char_np.get_pos(); + cohesion_dir.normalize(); + cohesion_force = cohesion_dir * _ai_char->_movt_force; + } + else if (is_on(BT_seek) || is_on(BT_flee) || is_on(BT_pursue) || + is_on(BT_evade) || is_on(BT_wander)) { + _flock_done = true; + turn_off("flock"); + turn_on("flock_activate"); + return (LVecBase3f(0.0, 0.0, 0.0)); + } + + // Calculate the resultant force on the ai character by taking into + // account the separation, alignment and cohesion + // forces along with their corresponding weights. + return (separation_force * _flock_group->_separation_wt + + avg_neighbor_heading * _flock_group->_alignment_wt + + cohesion_force * _flock_group->_cohesion_wt); +} + +//////////////////////////////////////////////////////////////////// +// Function: wander +// Description: This function activates wander. +// This is the function we want the user to call +// for flock to be done. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::wander(double wander_radius, int flag, double aoe, + float wander_weight) { + _wander_obj = new Wander(_ai_char, wander_radius, flag, aoe, + wander_weight); + turn_on("wander"); +} + +//////////////////////////////////////////////////////////////////// +// Function: obstacle avoidance +// Description: This function activates obstacle avoidance for a +// given character. +// This is the function we want the user to call for +// obstacle avoidance to be performed. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::obstacle_avoidance(float obstacle_avoidance_weight) { + _obstacle_avoidance_obj = new ObstacleAvoidance(_ai_char, obstacle_avoidance_weight); + turn_on("obstacle_avoidance_activate"); +} + +//////////////////////////////////////////////////////////////////// +// Function: path_follow +// Description: This function activates path following. +// This is the function we want the user to call for +// path following. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::path_follow(float follow_wt) { + _path_follow_obj = new PathFollow(_ai_char, follow_wt); +} + +//////////////////////////////////////////////////////////////////// +// Function: add_to_path +// Description: This function adds positions to the path to follow. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::add_to_path(LVecBase3f pos) { + _path_follow_obj->add_to_path(pos); +} + +//////////////////////////////////////////////////////////////////// +// Function: start_follow +// Description: This function starts the path follower. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::start_follow(string type) { + _path_follow_obj->start(type); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_path_find +// Description: This function activates path finding in the character. +// This function accepts the meshdata in .csv format. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::init_path_find(const char* navmesh_filename) { + _path_find_obj = new PathFind(_ai_char); + _path_find_obj->set_path_find(navmesh_filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: path_find_to (for pathfinding towards a +// static position) +// Description: This function checks for the source and target in +// the navigation mesh for its availability and then +// finds the best path via the A* algorithm +// Then it calls the path follower to make the object +// follow the path. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::path_find_to(LVecBase3f pos, string type) { + _path_find_obj->path_find(pos, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: path_find_to (for pathfinding towards a moving +// target (a NodePath)) +// Description: This function checks for the source and target in +// the navigation mesh for its availability and then +// finds the best path via the A* algorithm +// Then it calls the path follower to make the object +// follow the path. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::path_find_to(NodePath target, string type) { + _path_find_obj->path_find(target, type); +} + +//////////////////////////////////////////////////////////////////// +// Function: add_static_obstacle +// Description: This function allows the user to dynamically add +// obstacles to the +// game environment. The function will update the nodes +// within the bounding volume of the obstacle as non- +// traversable. Hence will not be considered by the +// pathfinding algorithm. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::add_static_obstacle(NodePath obstacle) { + _path_find_obj->add_obstacle_to_mesh(obstacle); +} + +//////////////////////////////////////////////////////////////////// +// Function: add_dynamic_obstacle +// Description: This function starts the pathfinding obstacle +// navigation for the passed in obstacle. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::add_dynamic_obstacle(NodePath obstacle) { + _path_find_obj->dynamic_avoid(obstacle); +} + +//////////////////////////////////////////////////////////////////// +// Function: behavior_status +// Description: This function returns the status of an AI Type +// whether it is active, paused or disabled. It returns +// -1 if an invalid string is passed. +//////////////////////////////////////////////////////////////////// +string AIBehaviors::behavior_status(string ai_type) { + switch (char_to_int(ai_type)) { + case 1: + if (_seek_obj) { + if (is_on(BT_seek)) { + return "active"; + } + else { + if (_seek_obj->_seek_done) { + return "done"; + } + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 2: + if (_flee_obj) { + if (is_on(BT_flee)) { + unsigned int i = 0; + for (_flee_itr = _flee_list.begin(); _flee_itr != _flee_list.end(); _flee_itr++) { + if (_flee_itr->_flee_done == true) { + ++i; + } + } + if (i == _flee_list.size()) { + return "done"; + } + else { + return "active"; + } + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 3: + if (_pursue_obj) { + if (is_on(BT_pursue)) { + if (_pursue_obj->_pursue_done) { + return "done"; + } + else { + return "active"; + } + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 4: + if (_evade_obj) { + if (is_on(BT_evade)) { + unsigned int i = 0; + for (_evade_itr = _evade_list.begin(); _evade_itr != _evade_list.end(); _evade_itr++) { + if (_evade_itr->_evade_done == true) { + ++i; + } + } + if (i == _evade_list.size()) { + return "done"; + } + else { + return "active"; + } + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 5: + if (_arrival_obj) { + if (is_on(BT_arrival)) { + if (_arrival_obj->_arrival_done) { + return "done"; + } + else { + return "active"; + } + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 6: + if (_flock_group) { + if (is_on(BT_flock)) { + if (_flock_done) { + return "done"; + } + else { + return "active"; + } + return "active"; + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 7: + if (_wander_obj) { + if (is_on(BT_wander)) { + return "active"; + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 8: + if (_obstacle_avoidance_obj) { + if (is_on(BT_obstacle_avoidance)) { + return "active"; + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 9: + if (_path_follow_obj) { + if (is_on("pathfollow")) { + if (_pursue_obj->_pursue_done) { + return "done"; + } + else { + return "active"; + } + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 16: + if (_path_find_obj) { + if (is_on("pathfind")) { + if (_pursue_obj->_pursue_done) { + return "done"; + } + else { + return "active"; + } + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + case 10: + if (_seek_obj || _flee_obj || _pursue_obj || _evade_obj || _arrival_obj + || _flock_group || _wander_obj || _obstacle_avoidance_obj || + _path_follow_obj) { + if (is_on(BT_seek) || is_on(BT_flee) || is_on(BT_pursue)|| + is_on(BT_evade) || is_on(BT_arrival) || is_on(BT_flock) + || is_on(BT_wander) || is_on(BT_obstacle_avoidance) || + is_on("pathfollow") || is_on("pathfinding")) { + return "active"; + } + else { + return "paused"; + } + } + else { + return "disabled"; + } + break; + + default: + cout << "Invalid value!\n"; + } + } + +//////////////////////////////////////////////////////////////////// +// Function: char_to_int +// Description: This function is used to derive int values from the +// ai types strings. +// Returns -1 if an invalid string is passed. +//////////////////////////////////////////////////////////////////// +int AIBehaviors::char_to_int(string ai_type) { + if (ai_type == "all") { + return 0; + } + else if (ai_type == "seek") { + return 1; + } + else if (ai_type == "flee") { + return 2; + } + else if (ai_type == "pursue") { + return 3; + } + else if (ai_type == "evade") { + return 4; + } + else if (ai_type == "arrival") { + return 5; + } + else if (ai_type == "flock") { + return 6; + } + else if (ai_type == "wander") { + return 7; + } + else if (ai_type == "obstacle_avoidance") { + return 8; + } + else if (ai_type == "pathfollow") { + return 9; + } + else if (ai_type == "any") { + return 10; + } + else if (ai_type == "flee_activate") { + return 11; + } + else if (ai_type == "evade_activate") { + return 12; + } + else if (ai_type == "arrival_activate") { + return 13; + } + else if (ai_type == "flock_activate") { + return 14; + } + else if (ai_type == "obstacle_avoidance_activate") { + return 15; + } + else if (ai_type == "path_finding") { + return 16; + } + + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: turn_on +// Description: This function turns on any aiBehavior which is +// passed as a string. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::turn_on(string ai_type) { + switch (char_to_int(ai_type)) { + case 1: { + _behaviors_flags |= BT_seek; + break; + } + case 2: { + _behaviors_flags |= BT_flee; + break; + } + case 3: { + _behaviors_flags |= BT_pursue; + break; + } + case 4: { + _behaviors_flags |= BT_evade; + break; + } + case 5: { + _behaviors_flags |= BT_arrival; + break; + } + case 6: { + _behaviors_flags |= BT_flock; + break; + } + case 7: { + _behaviors_flags |= BT_wander; + break; + } + case 8: { + _behaviors_flags |= BT_obstacle_avoidance; + break; + } + case 11:{ + _behaviors_flags |= BT_flee_activate; + break; + } + case 12:{ + _behaviors_flags |= BT_evade_activate; + break; + } + case 13:{ + _behaviors_flags |= BT_arrival_activate; + break; + } + case 14:{ + _behaviors_flags |= BT_flock_activate; + break; + } + case 15:{ + _behaviors_flags |= BT_obstacle_avoidance_activate; + break; + } + default: + cout << "Invalid option!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: turn_off +// Description: This function turns off any aiBehavior which is +// passed as a string. +//////////////////////////////////////////////////////////////////// +void AIBehaviors::turn_off(string ai_type) { +switch (char_to_int(ai_type)) { + case 1: { + if (is_on(BT_seek)) { + _behaviors_flags ^= BT_seek; + } + _seek_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 2: { + if (is_on(BT_flee)) { + _behaviors_flags ^= BT_flee; + } + _flee_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 3: { + if (is_on(BT_pursue)) { + _behaviors_flags ^= BT_pursue; + } + _pursue_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 4: { + if (is_on(BT_evade)) { + _behaviors_flags ^= BT_evade; + } + _evade_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 5: { + if (is_on(BT_arrival)) { + _behaviors_flags ^= BT_arrival; + } + _arrival_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 6: { + if (is_on(BT_flock)) { + _behaviors_flags ^= BT_flock; + } + _flock_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 7: { + if (is_on(BT_wander)) { + _behaviors_flags ^= BT_wander; + } + _wander_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 8: { + if (is_on(BT_obstacle_avoidance)) { + _behaviors_flags ^= BT_obstacle_avoidance; + } + _obstacle_avoidance_force = LVecBase3f(0.0f, 0.0f, 0.0f); + break; + } + case 9: { + turn_off("pursue"); + break; + } + case 11: { + if (is_on(BT_flee_activate)) { + _behaviors_flags ^= BT_flee_activate; + } + break; + } + case 12:{ + if (is_on(BT_evade_activate)) { + _behaviors_flags ^= BT_evade_activate; + } + break; + } + case 13:{ + if (is_on(BT_arrival_activate)) { + _behaviors_flags ^= BT_arrival_activate; + } + break; + } + case 14:{ + if (is_on(BT_flock_activate)) { + _behaviors_flags ^= BT_flock_activate; + } + break; + } + case 15:{ + if (is_on(BT_obstacle_avoidance_activate)) { + _behaviors_flags ^= BT_obstacle_avoidance_activate; + } + break; + } + case 16:{ + turn_off("pathfollow"); + break; + } + default: + cout << "Invalid option!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: is_on +// Description: This function returns true if an aiBehavior is on +//////////////////////////////////////////////////////////////////// +bool AIBehaviors::is_on(BehaviorType bt) { + return (_behaviors_flags & bt) == bt; +} + +//////////////////////////////////////////////////////////////////// +// Function: is_on +// Description: This function returns true if pathfollow or +// pathfinding is on +//////////////////////////////////////////////////////////////////// +bool AIBehaviors::is_on(string ai_type) { + if (ai_type == "pathfollow") { + if (_path_follow_obj) { + return (is_on(BT_pursue) && _path_follow_obj->_start); + } + else { + return false; + } + } + + if (ai_type == "pathfinding") { + if (_path_follow_obj && _path_find_obj) { + return (is_on(BT_pursue) && _path_follow_obj->_start); + } + else { + return false; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: is_off +// Description: This function returns true if an aiBehavior is off +//////////////////////////////////////////////////////////////////// +bool AIBehaviors::is_off(BehaviorType bt) { + return ((_behaviors_flags | bt) == bt); +} + +//////////////////////////////////////////////////////////////////// +// Function: is_off +// Description: This function returns true if pathfollow or +// pathfinding is off +//////////////////////////////////////////////////////////////////// +bool AIBehaviors::is_off(string ai_type) { + if (ai_type == "pathfollow") { + if (_path_follow_obj && _path_follow_obj->_start) { + return true; + } + else { + return false; + } + } + + if (ai_type == "pathfinding") { + if (_path_find_obj && _path_follow_obj && _path_follow_obj->_start) { + return true; + } + else { + return false; + } + } + + cout << "You passed an invalid string, defaulting return value to false!\n"; + return false; +} diff --git a/contrib/src/ai/aiBehaviors.h b/contrib/src/ai/aiBehaviors.h new file mode 100644 index 0000000000..45c922b281 --- /dev/null +++ b/contrib/src/ai/aiBehaviors.h @@ -0,0 +1,193 @@ +// Filename: aiBehaviors.h +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef AIBEHAVIORS_H +#define AIBEHAVIORS_H + +#include "aiGlobals.h" +#include "aiCharacter.h" + +#include "seek.h" +#include "flee.h" +#include "pursue.h" +#include "evade.h" +#include "arrival.h" +#include "flock.h" +#include "wander.h" +#include "pathFollow.h" +#include "pathFind.h" +#include "obstacleAvoidance.h" + +class AICharacter; +class Seek; +class Flee; +class Pursue; +class Evade; +class Arrival; +class Flock; +class Wander; +class PathFollow; +class PathFind; +class ObstacleAvoidance; + +typedef list > ListFlee; +typedef list > ListEvade; + +//////////////////////////////////////////////////////////////////// +// Class : AIBehaviors +// Description : This class implements all the steering behaviors +// of the AI framework, such as seek, flee, pursue, +// evade, wander and flock. Each steering behavior +// has a weight which is used when more than one type +// of steering behavior is acting on the same AI +// character. The weight decides the contribution of +// each type of steering behavior. The AICharacter +// class has a handle to an object of this class and +// this allows to invoke the steering behaviors via +// the AICharacter. This class also provides +// functionality such as pausing, resuming and +// removing the AI behaviors of an AI character at +// any time. +/////////////////////////////////////////////////////////////////// +class AIBehaviors { + +public: + enum BehaviorType { + BT_none = 0x00000, + BT_seek = 0x00002, + BT_flee = 0x00004, + BT_flee_activate = 0x00100, + BT_arrival = 0x00008, + BT_arrival_activate = 0x01000, + BT_wander = 0x00010, + BT_pursue = 0x00040, + BT_evade = 0x00080, + BT_evade_activate = 0x00800, + BT_flock = 0x00200, + BT_flock_activate = 0x00400, + BT_obstacle_avoidance = 0x02000, + BT_obstacle_avoidance_activate = 0x04000, + }; + + AICharacter *_ai_char; + Flock *_flock_group; + + int _behaviors_flags; + LVecBase3f _steering_force; + + Seek *_seek_obj; + LVecBase3f _seek_force; + + Flee *_flee_obj; + LVecBase3f _flee_force; + + // This list is used if the ai character needs to flee from + // multiple onjects. + ListFlee _flee_list; + ListFlee::iterator _flee_itr; + + Pursue *_pursue_obj; + LVecBase3f _pursue_force; + + Evade *_evade_obj; + LVecBase3f _evade_force; + + // This list is used if the ai character needs to evade from + // multiple onjects. + ListEvade _evade_list; + ListEvade::iterator _evade_itr; + + Arrival *_arrival_obj; + LVecBase3f _arrival_force; + + // Since Flock is a collective behavior the variables are + // declared within the AIBehaviors class. + float _flock_weight; + LVecBase3f _flock_force; + bool _flock_done; + + Wander * _wander_obj; + LVecBase3f _wander_force; + + ObstacleAvoidance *_obstacle_avoidance_obj; + LVecBase3f _obstacle_avoidance_force; + + PathFollow *_path_follow_obj; + + PathFind *_path_find_obj; + + bool _conflict, _previous_conflict; + + AIBehaviors(); + ~AIBehaviors(); + + bool is_on(BehaviorType bt); + bool is_off(BehaviorType bt); + // special cases for pathfollow and pathfinding + bool is_on(string ai_type); + bool is_off(string ai_type); + void turn_on(string ai_type); + void turn_off(string ai_type); + + bool is_conflict(); + + void accumulate_force(string force_type, LVecBase3f force); + LVecBase3f calculate_prioritized(); + + void flock_activate(); + LVecBase3f do_flock(); + + int char_to_int(string ai_type); + +PUBLISHED: + void seek(NodePath target_object, float seek_wt = 1.0); + void seek(LVecBase3f pos, float seek_wt = 1.0); + + void flee(NodePath target_object, double panic_distance = 10.0, + double relax_distance = 10.0, float flee_wt = 1.0); + void flee(LVecBase3f pos, double panic_distance = 10.0, + double relax_distance = 10.0, float flee_wt = 1.0); + + void pursue(NodePath target_object, float pursue_wt = 1.0); + + void evade(NodePath target_object, double panic_distance = 10.0, + double relax_distance = 10.0, float evade_wt = 1.0); + + void arrival(double distance = 10.0); + + void flock(float flock_wt); + + void wander(double wander_radius = 5.0, int flag =0, + double aoe = 0.0, float wander_weight = 1.0); + + void obstacle_avoidance(float feeler_length = 1.0); + + void path_follow(float follow_wt = 1.0); + void add_to_path(LVecBase3f pos); + void start_follow(string type = "normal"); + + void init_path_find(const char* navmesh_filename); + void path_find_to(LVecBase3f pos, string type = "normal"); + void path_find_to(NodePath target, string type = "normal"); + void add_static_obstacle(NodePath obstacle); + void add_dynamic_obstacle(NodePath obstacle); + + void remove_ai(string ai_type); + void pause_ai(string ai_type); + void resume_ai(string ai_type); + + string behavior_status(string ai_type); +}; + +#endif diff --git a/contrib/src/ai/aiCharacter.cxx b/contrib/src/ai/aiCharacter.cxx new file mode 100644 index 0000000000..fecb869196 --- /dev/null +++ b/contrib/src/ai/aiCharacter.cxx @@ -0,0 +1,127 @@ +// Filename: aiCharacter.cxx +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "aiCharacter.h" + +AICharacter::AICharacter(string model_name, NodePath model_np, double mass, + double movt_force, double max_force) { + _name = model_name; + _ai_char_np = model_np; + + _mass = mass; + _max_force = max_force; + _movt_force = movt_force; + + _velocity = LVecBase3f(0.0, 0.0, 0.0); + _steering_force = LVecBase3f(0.0, 0.0, 0.0); + + _steering = new AIBehaviors(); + _steering->_ai_char = this; + + _pf_guide = false; +} + +AICharacter::~AICharacter() { +} + +//////////////////////////////////////////////////////////////////// +// Function: update +// Description: Each character's update will update its ai and +// physics based on his resultant steering force. +// This also makes the character look in the +// direction of the force. +//////////////////////////////////////////////////////////////////// +void AICharacter::update() { + +if (!_steering->is_off(_steering->BT_none)) { + + LVecBase3f old_pos = _ai_char_np.get_pos(); + + LVecBase3f steering_force = _steering->calculate_prioritized(); + + LVecBase3f acceleration = steering_force / _mass; + + _velocity = acceleration; + + LVecBase3f direction = _steering->_steering_force; + direction.normalize(); + + _ai_char_np.set_pos(old_pos + _velocity) ; + + if (steering_force.length() > 0) { + _ai_char_np.look_at(old_pos + (direction * 5)); + _ai_char_np.set_h(_ai_char_np.get_h() + 180); + _ai_char_np.set_p(-_ai_char_np.get_p()); + _ai_char_np.set_r(-_ai_char_np.get_r()); + } + } + else { + _steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _steering->_seek_force = LVecBase3f(0.0, 0.0, 0.0); + _steering->_flee_force = LVecBase3f(0.0, 0.0, 0.0); + _steering->_pursue_force = LVecBase3f(0.0, 0.0, 0.0); + _steering->_evade_force = LVecBase3f(0.0, 0.0, 0.0); + _steering->_arrival_force = LVecBase3f(0.0, 0.0, 0.0); + _steering->_flock_force = LVecBase3f(0.0, 0.0, 0.0); + _steering->_wander_force = LVecBase3f(0.0, 0.0, 0.0); + } +} + +LVecBase3f AICharacter::get_velocity() { + return _velocity; +} + +void AICharacter::set_velocity(LVecBase3f velocity) { + _velocity = velocity; +} + +double AICharacter::get_mass() { + return _mass; +} + +void AICharacter::set_mass(double m) { + _mass = m; +} + +double AICharacter::get_max_force() { + return _max_force; +} + +void AICharacter::set_max_force(double max_force) { + _max_force = max_force; +} + +NodePath AICharacter::get_node_path() { + return _ai_char_np; +} + +void AICharacter::set_node_path(NodePath np) { + _ai_char_np = np; +} + +AIBehaviors * AICharacter::get_ai_behaviors() { + return _steering; +} + +void AICharacter::set_char_render(NodePath render) { + _window_render = render; +} + +NodePath AICharacter::get_char_render() { + return _window_render; +} + +void AICharacter::set_pf_guide(bool pf_guide) { + _pf_guide = pf_guide; +} diff --git a/contrib/src/ai/aiCharacter.h b/contrib/src/ai/aiCharacter.h new file mode 100644 index 0000000000..d6d4ac07b3 --- /dev/null +++ b/contrib/src/ai/aiCharacter.h @@ -0,0 +1,76 @@ +// Filename: aiCharacter.h +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef AICHARACTER_H +#define AICHARACTER_H + +#include "aiBehaviors.h" +#include "aiWorld.h" + +class AIBehaviors; +class AIWorld; + +//////////////////////////////////////////////////////////////////// +// Class : AICharacter +// Description : This class is used for creating the ai characters. +// It assigns both physics and ai attributes to the +// character. It also has an update function which +// updates the physics and ai of the character. +// This update function is called by the AIWorld +// update. +//////////////////////////////////////////////////////////////////// +class AICharacter { + public: + double _mass; + double _max_force; + LVecBase3f _velocity; + LVecBase3f _steering_force; + string _name; + double _movt_force; + unsigned int _ai_char_flock_id; + AIWorld *_world; + AIBehaviors *_steering; + NodePath _window_render; + NodePath _ai_char_np; + bool _pf_guide; + + void update(); + void set_velocity(LVecBase3f vel); + void set_char_render(NodePath render); + NodePath get_char_render(); + +PUBLISHED: + double get_mass(); + void set_mass(double m); + + LVecBase3f get_velocity(); + + double get_max_force(); + void set_max_force(double max_force); + + NodePath get_node_path(); + void set_node_path(NodePath np); + + AIBehaviors * get_ai_behaviors(); + + // This function is used to enable or disable the guides + // for path finding. + void set_pf_guide(bool pf_guide); + + AICharacter(string model_name, NodePath model_np, double mass, + double movt_force, double max_force); + ~AICharacter(); +}; + +#endif diff --git a/contrib/src/ai/aiGlobals.h b/contrib/src/ai/aiGlobals.h new file mode 100644 index 0000000000..83d01ccde5 --- /dev/null +++ b/contrib/src/ai/aiGlobals.h @@ -0,0 +1,28 @@ +// Filename: aiGlobals.h +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef AIGLOBALS_H +#define AIGLOBALS_H + +#include "pandaFramework.h" +#include "textNode.h" +#include "pandaSystem.h" + +#include "lvecBase3.h" +#include "nodePath.h" + +#include "genericAsyncTask.h" +#include "asyncTaskManager.h" + +#endif diff --git a/contrib/src/ai/aiNode.cxx b/contrib/src/ai/aiNode.cxx new file mode 100644 index 0000000000..9696c1f32a --- /dev/null +++ b/contrib/src/ai/aiNode.cxx @@ -0,0 +1,53 @@ +// Filename: aiNode.cxx +// Created by: Deepak, John, Navin (19Nov2009) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "aiNode.h" + +AINode::AINode(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h) { + for (int i = 0; i < 8; ++i) { + _neighbours[i] = NULL; + } + + _position = pos; + _width = w; + _length = l; + _height = h; + _grid_x = grid_x; + _grid_y = grid_y; + _status = ST_neutral; + _type = true; + _score = 0; + _cost = 0; + _heuristic = 0; + _next = NULL; + _prv_node = NULL; +} + +AINode::~AINode() { +} + +//////////////////////////////////////////////////////////////////// +// Function: contains +// Description: This is a handy function which returns true if the +// passed position is within the node's dimensions. +//////////////////////////////////////////////////////////////////// +bool AINode::contains(float x, float y) { + if (_position.get_x() - _width / 2 <= x && _position.get_x() + _width / 2 >= x && + _position.get_y() - _length / 2 <= y && _position.get_y() + _length / 2 >= y) { + return true; + } + else { + return false; + } +} diff --git a/contrib/src/ai/aiNode.h b/contrib/src/ai/aiNode.h new file mode 100644 index 0000000000..083ff245c3 --- /dev/null +++ b/contrib/src/ai/aiNode.h @@ -0,0 +1,85 @@ +// Filename: aiNode.h +// Created by: Deepak, John, Navin (18Nov2009) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef AINODE_H +#define AINODE_H + +#include "aiGlobals.h" + +//////////////////////////////////////////////////////////////////// +// Class : AINode +// Description : This class is used to assign the nodes on the mesh. +// It holds all the data necessary to compute A* +// algorithm. It also maintains a lot of vital +// information such as the neighbor nodes of each +// node and also its position on the mesh. +// Note: The Mesh Generator which is a standalone +// tool makes use of this class to generate the nodes +// on the mesh. +//////////////////////////////////////////////////////////////////// +class AINode { +public: + // This variable specifies whether the node is an obtacle or not. + // Used for dynamic obstacle addition to the environment. + // obstacle = false + // navigational = true + bool _type; + + // This variable specifies the node status whether open, close + // or neutral. + // open = belongs to _open_list. + // close = belongs to _closed_list. + // neutral = unexamined node. + enum Status { + ST_open, + ST_close, + ST_neutral + }; + Status _status; + + // The score is used to compute the traversal expense to nodes + // when using A*. + // _score = _cost + heuristic + int _score; + int _cost; + int _heuristic; + + // Used to trace back the path after it is generated using A*. + AINode *_prv_node; + + // Position of the node in the 2d grid. + int _grid_x, _grid_y; + + // Position of the node in 3D space. + LVecBase3f _position; + + // Dimensions of each face / cell on the mesh. + // Height is given in case of expansion to a 3d mesh. Currently + // not used. + float _width, _length ,_height; + AINode *_neighbours[8]; // anti-clockwise from top left corner. + + // The _next pointer is used for traversal during mesh + // generation from the model. + // Note: The data in this member is discarded when mesh data + // is written into navmesh.csv file. + AINode *_next; + + AINode(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h); + ~AINode(); + + bool contains(float x, float y); +}; + +#endif diff --git a/contrib/src/ai/aiPathFinder.cxx b/contrib/src/ai/aiPathFinder.cxx new file mode 100644 index 0000000000..8b2d364a2d --- /dev/null +++ b/contrib/src/ai/aiPathFinder.cxx @@ -0,0 +1,377 @@ +// Filename: aiPathfinder.cxx +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "aiPathFinder.h" + +PathFinder::PathFinder(NavMesh nav_mesh) { + _grid = nav_mesh; +} + +PathFinder::~PathFinder() { +} + +//////////////////////////////////////////////////////////////////// +// Function: find_path +// Description: This function initializes the pathfinding process +// by accepting the source and destination nodes. +// It then calls the generate_path(). +//////////////////////////////////////////////////////////////////// +void PathFinder::find_path(AINode *src_node, AINode *dest_node) { + _src_node = src_node; + _dest_node = dest_node; + + // Add a dummy node as the first element of the open list with score + // = -1. + // Inorder to implement a binary heap the index of the elements + // should never be 0. + AINode *_dummy_node = new AINode(-1, -1, LVecBase3f(0.0, 0.0, 0.0), 0, 0, 0); + _dummy_node->_status = _dummy_node->ST_open; + _dummy_node->_score = -1; + _open_list.push_back(_dummy_node); + + // Add the source node to the open list. + add_to_olist(_src_node); + + // Generate the path. + generate_path(); +} + +//////////////////////////////////////////////////////////////////// +// Function: generate_path +// Description: This function performs the pathfinding process +// using the A* algorithm. +// It updates the openlist and closelist. +//////////////////////////////////////////////////////////////////// +void PathFinder::generate_path() { + // All the A* algorithm is implemented here. + // The check is > 1 due to the existence of the dummy node. + while (_open_list.size() > 1) { + // The first element of the open list will always be the optimal + // node. + // This is because the open list is a binary heap with element + // having the smallest score at the top of the heap. + AINode* nxt_node = _open_list[1]; + + if (nxt_node->_grid_x == _dest_node->_grid_x && + nxt_node->_grid_y == _dest_node->_grid_y) { + // Remove the optimal node from the top of the heap. + remove_from_olist(); + + // add the used node to the closed list. + add_to_clist(nxt_node); + + // At this point the destination is reached. + return; + } + else { + identify_neighbors(nxt_node); + + // add the used node to the closed list. + add_to_clist(nxt_node); + } + } + cout << "DESTINATION NOT REACHABLE MATE!\n"; + _closed_list.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: identify_neighbors +// Description: This function traverses through the 8 neigbors of +// the parent node and then adds the neighbors to the +// _open_list based on A* criteria. +//////////////////////////////////////////////////////////////////// +void PathFinder::identify_neighbors(AINode *parent_node) { + // Remove the parent node from the open_list so that it is not considered + // while adding new nodes to the open list heap. + remove_from_olist(); + for (int i = 0; i < 8; ++i) { + if (parent_node->_neighbours[i] != NULL) { + if (parent_node->_neighbours[i]->_status == parent_node->_neighbours[i]->ST_neutral + && parent_node->_neighbours[i]->_type == true) { + // Link the neighbor to the parent node. + parent_node->_neighbours[i]->_prv_node = parent_node; + // Calculate and update the score for the node. + calc_node_score(parent_node->_neighbours[i]); + // Add the neighbor to the open list. + add_to_olist(parent_node->_neighbours[i]); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: calc_node_score +// Description: This function calculates the score of each node. +// Score = Cost + Heuristics. +//////////////////////////////////////////////////////////////////// +void PathFinder::calc_node_score(AINode *nd) { + nd->_cost = calc_cost_frm_src(nd); + nd->_heuristic = calc_heuristic(nd); + nd->_score = nd->_cost + nd->_heuristic; +} + +//////////////////////////////////////////////////////////////////// +// Function: calc_cost_frm_src +// Description: This function calculates the cost of each node by +// finding out the number of node traversals required +// to reach the source node. +// Diagonal traversals have have cost = 14. +// Horizontal / Vertical traversals have cost = 10. +//////////////////////////////////////////////////////////////////// +int PathFinder::calc_cost_frm_src(AINode *nd) { + int cost = 0; + AINode *start_node = nd; + while (start_node->_prv_node != _src_node) { + if (is_diagonal_node(start_node)) { + cost += 14; + } + else { + cost += 10; + } + start_node = start_node->_prv_node; + } + // Add the cost of traversal to the source node also. + if (is_diagonal_node(start_node)) { + cost += 14; + } + else { + cost += 10; + } + return cost; +} + +//////////////////////////////////////////////////////////////////// +// Function: calc_heuristic +// Description: This function calculates the heuristic of the nodes +// using Manhattan method. All it does is predict the +// number of node traversals required to reach the +// target node. No diagonal traversals are allowed +// in this technique. +//////////////////////////////////////////////////////////////////// +int PathFinder::calc_heuristic(AINode *nd) { + int row_diff = abs(_dest_node->_grid_x - nd->_grid_x); + int col_diff = abs(_dest_node->_grid_y - nd->_grid_y); + + int heuristic = 10 * (row_diff + col_diff); + return heuristic; +} + +//////////////////////////////////////////////////////////////////// +// Function: is_diagonal_node +// Description: This function checks if the traversal from a +// node is diagonal. +//////////////////////////////////////////////////////////////////// +bool PathFinder::is_diagonal_node(AINode *nd) { + // Calculate the row and column differences between child and parent + // nodes. + float row_diff = nd->_grid_x - nd->_prv_node->_grid_x; + float col_diff = nd->_grid_y - nd->_prv_node->_grid_y; + + // Check if the relationship between child and parent node is + // diagonal. + if (row_diff == 0 || col_diff == 0) { + return false; + } + else { + return true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: add_to_olist +// Description: This function adds a node to the open list heap. +// A binay heap is maintained to improve the search. +//////////////////////////////////////////////////////////////////// +void PathFinder::add_to_olist(AINode *nd) { + // Variables required to search the binary heap. + AINode *child_node, *parent_node; + int child_idx, parent_idx; + + // Set the status as open. + nd->_status = nd->ST_open; + // Add the node to the open list. + _open_list.push_back(nd); + + // Find the parent and child nodes and create temporary nodes out + // of them. In a binary heap the children of a parent node are + // always i*2 and i*2 + 1, where i is the index of the parent node + // in the heap. And hence, the parent of a node can be easily found + // out by dividing by 2 and rounding it. + child_idx = _open_list.size() - 1; + parent_idx = child_idx / 2; + child_node = _open_list[child_idx]; + parent_node = _open_list[parent_idx]; + + // Keep traversing the heap until the lowest element in the list is + // bubbled + // to the top of the heap. + while (_open_list[child_idx]->_score <= _open_list[parent_idx]->_score) { + // Swap the parent node and the child node. + _open_list[parent_idx] = child_node; + _open_list[child_idx] = parent_node; + + // Update the new child and parent indices. + child_idx = parent_idx; + parent_idx = child_idx / 2; + + // Update the new child and parent nodes. + child_node = _open_list[child_idx]; + parent_node = _open_list[parent_idx]; + } + + // At this point the AINode with the smallest score will be at the + // top of the heap. +} + +//////////////////////////////////////////////////////////////////// +// Function: remove_from_olist +// Description: This function removes a node from the open list. +// During the removal the binary heap is maintained. +//////////////////////////////////////////////////////////////////// +void PathFinder::remove_from_olist() { + // Variables for maintaining the binary heap. + AINode *child_node, *child_node_1, *child_node_2; + int child_idx, child_idx_1, child_idx_2; + + // Remove the AINode at index 1 from the open list binary heap. + // Note: AINode at index 0 of open list is a dummy node. + _open_list.erase(_open_list.begin() + 1); + + if (_open_list.size() > 1) { + // Store the last element in the open list to a temp_node. + AINode *temp_node = _open_list[_open_list.size() - 1]; + + // Shift the elements of the open list to the right by 1 element + // circularly, excluding element at 0 index. + for (int i = _open_list.size() - 1; i > 1; --i) { + _open_list[i] = _open_list[i - 1]; + } + + // Assign the temp_node to 1st element in the open list. + _open_list[1] = temp_node; + + // Set the iterator for traversing the node from index 1 in + // the heap. + unsigned int k = 1; + + // This loop traverses down the open list till the node reaches + // the correct position in the binary heap. + while (true) { + if ((k * 2 + 1) < _open_list.size()) { + // Two children exists for the parent node. + child_idx_1 = k * 2; + child_idx_2 = k * 2 + 1; + child_node_1 = _open_list[child_idx_1]; + child_node_2 = _open_list[child_idx_2]; + + if (_open_list[child_idx_1]->_score < _open_list[child_idx_2]->_score) { + if (_open_list[k]->_score > _open_list[child_idx_1]->_score) { + // Swap the parent node and the child node. + _open_list[child_idx_1] = _open_list[k]; + _open_list[k] = child_node_1; + + // Update the parent node index. + k = child_idx_1; + } + else { + break; + } + } + else { + if (_open_list[k]->_score > _open_list[child_idx_2]->_score) { + // Swap the parent node and the child node. + _open_list[child_idx_2] = _open_list[k]; + _open_list[k] = child_node_2; + + // Update the parent node index. + k = child_idx_2; + } + else { + break; + } + } + } + else if ((k * 2) < _open_list.size()) { + // Only one child exists for the parent node. + child_idx = k * 2; + child_node = _open_list[child_idx]; + + if (_open_list[k]->_score > _open_list[child_idx]->_score) { + // Swap the parent node and the child node. + _open_list[child_idx] = _open_list[k]; + _open_list[k] = child_node; + + // Update the parent node index. + k = child_idx; + } + else { + break; + } + } + else { + // No children exists. + break; + } + } + } + + // At this point the AINode was succesfully removed and the binary + // heap re-arranged. +} + +//////////////////////////////////////////////////////////////////// +// Function: add_to_clist +// Description: This function adds a node to the closed list. +//////////////////////////////////////////////////////////////////// +void PathFinder::add_to_clist(AINode *nd) { + // Set the status as closed. + nd->_status = nd->ST_close; + // Add the node to the close list. + _closed_list.push_back(nd); +} + +//////////////////////////////////////////////////////////////////// +// Function: remove_from_clist +// Description: This function removes a node from the closed list. +//////////////////////////////////////////////////////////////////// +void PathFinder::remove_from_clist(int r, int c) { + for (unsigned int i = 0; i < _closed_list.size(); ++i) { + if (_closed_list[i]->_grid_x == r && _closed_list[i]->_grid_y == c) { + _closed_list.erase(_closed_list.begin() + i); + break; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: find_in_mesh +// Description: This function allows the user to pass a position +// and it returns the corresponding node on the +// navigation mesh. A very useful function as it allows +// for dynamic updation of the mesh based on position. +//////////////////////////////////////////////////////////////////// +AINode* find_in_mesh(NavMesh nav_mesh, LVecBase3f pos, int grid_size) { + int size = grid_size; + float x = pos[0]; + float y = pos[1]; + + for (int i = 0; i < size; ++i) { + for (int j = 0; j < size; ++j) { + if (nav_mesh[i][j] != NULL && nav_mesh[i][j]->contains(x, y)) { + return (nav_mesh[i][j]); + } + } + } + return NULL; +} diff --git a/contrib/src/ai/aiPathFinder.h b/contrib/src/ai/aiPathFinder.h new file mode 100644 index 0000000000..654240d913 --- /dev/null +++ b/contrib/src/ai/aiPathFinder.h @@ -0,0 +1,61 @@ +// Filename: aiPathfinder.h +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef PATHFINDER_H +#define PATHFINDER_H + +#include "aiNode.h" +#include "cmath.h" +#include "lineSegs.h" + +typedef vector NodeArray; +typedef vector NavMesh; + +AINode* find_in_mesh(NavMesh nav_mesh, LVecBase3f pos, int grid_size); + +//////////////////////////////////////////////////////////////////// +// Class : PathFinder +// Description : This class implements pathfinding using the A* +// algorithm. It also uses a Binary Heap search to +// search the open list. The heuristics are +// calculated using the manhattan method. +//////////////////////////////////////////////////////////////////// +class PathFinder { +public: + AINode *_src_node; + AINode *_dest_node; + vector _open_list; + vector _closed_list; + + NavMesh _grid; + + void identify_neighbors(AINode *nd); + int calc_cost_frm_src(AINode *nd); + int calc_heuristic(AINode *nd); + void calc_node_score(AINode *nd); + bool is_diagonal_node(AINode *nd); + + void add_to_olist(AINode *nd); + void remove_from_olist(); + + void add_to_clist(AINode *nd); + void remove_from_clist(int r, int c); + + void generate_path(); + void find_path(AINode *src_node, AINode *dest_node); + PathFinder(NavMesh nav_mesh); + ~PathFinder(); +}; + +#endif diff --git a/contrib/src/ai/aiWorld.cxx b/contrib/src/ai/aiWorld.cxx new file mode 100644 index 0000000000..bb6fa90804 --- /dev/null +++ b/contrib/src/ai/aiWorld.cxx @@ -0,0 +1,258 @@ +// Filename: aiWorld.cxx +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "aiWorld.h" + +AIWorld::AIWorld(NodePath render) { + _ai_char_pool = new AICharPool(); + _render = render; +} + +AIWorld::~AIWorld() { +} + +void AIWorld::add_ai_char(AICharacter *ai_char) { + _ai_char_pool->append(ai_char); + ai_char->_window_render = _render; + ai_char->_world = this; +} + +void AIWorld::remove_ai_char(string name) { + _ai_char_pool->del(name); + remove_ai_char_from_flock(name); +} + +void AIWorld::remove_ai_char_from_flock(string name) { + AICharPool::node *ai_pool; + ai_pool = _ai_char_pool->_head; + while ((ai_pool) != NULL) { + for (unsigned int i = 0; i < _flock_pool.size(); ++i) { + if (ai_pool->_ai_char->_ai_char_flock_id == _flock_pool[i]->get_id()) { + for (unsigned int j = 0; j<_flock_pool[i]->_ai_char_list.size(); ++j) { + if (_flock_pool[i]->_ai_char_list[j]->_name == name) { + _flock_pool[i]->_ai_char_list.erase(_flock_pool[i]->_ai_char_list.begin() + j); + return; + } + } + } + } + ai_pool = ai_pool->_next; + } +} + +void AIWorld::print_list() { + _ai_char_pool->print_list(); +} + +//////////////////////////////////////////////////////////////////// +// Function: update +// Description: The AIWorld update function calls the update +// function of all the AI characters which have been +// added to the AIWorld. +//////////////////////////////////////////////////////////////////// +void AIWorld::update() { + AICharPool::node *ai_pool; + ai_pool = _ai_char_pool->_head; + + while ((ai_pool)!=NULL) { + ai_pool->_ai_char->update(); + ai_pool = ai_pool->_next; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: add_flock +// Description: This function adds all the AI characters in the +// Flock object to the AICharPool. This function +// allows adding the AI characetrs as part of a flock. +//////////////////////////////////////////////////////////////////// +void AIWorld::add_flock(Flock *flock) { + // Add all the ai_characters in the flock to the AIWorld. + for (unsigned int i = 0; i < flock->_ai_char_list.size(); ++i) { + this->add_ai_char(flock->_ai_char_list[i]); + } + // Add the flock to the flock pool. + _flock_pool.push_back(flock); +} + +//////////////////////////////////////////////////////////////////// +// +// Function: get_flock +// Description: This function returns a handle to the Flock whose +// id is passed. +//////////////////////////////////////////////////////////////////// + +Flock AIWorld::get_flock(unsigned int flock_id) { + for (unsigned int i=0; i < _flock_pool.size(); ++i) { + if (_flock_pool[i]->get_id() == flock_id) { + return *_flock_pool[i]; + } + } + Flock *null_flock = NULL; + return *null_flock; +} + +//////////////////////////////////////////////////////////////////// +// Function: remove_flock +// Description: This function removes the flock behavior completely. +//////////////////////////////////////////////////////////////////// +void AIWorld::remove_flock(unsigned int flock_id) { + for (unsigned int i = 0; i < _flock_pool.size(); ++i) { + if (_flock_pool[i]->get_id() == flock_id) { + for (unsigned int j = 0; j < _flock_pool[i]->_ai_char_list.size(); ++j) { + _flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock_activate"); + _flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock"); + _flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->_flock_group = NULL; + } + _flock_pool.erase(_flock_pool.begin() + i); + break; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: flock_off +// Description: This function turns off the flock behavior +// temporarily. Similar to pausing the behavior. +//////////////////////////////////////////////////////////////////// +void AIWorld::flock_off(unsigned int flock_id) { + for (unsigned int i = 0; i < _flock_pool.size(); ++i) { + if (_flock_pool[i]->get_id() == flock_id) { + for (unsigned int j = 0; j < _flock_pool[i]->_ai_char_list.size(); ++j) { + _flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock_activate"); + _flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_off("flock"); + } + break; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: flock_on +// Description: This function turns on the flock behavior. +//////////////////////////////////////////////////////////////////// +void AIWorld::flock_on(unsigned int flock_id) { + for (unsigned int i = 0; i < _flock_pool.size(); ++i) { + if (_flock_pool[i]->get_id() == flock_id) { + for (unsigned int j = 0; j < _flock_pool[i]->_ai_char_list.size(); ++j) { + _flock_pool[i]->_ai_char_list[j]->get_ai_behaviors()->turn_on("flock_activate"); + } + break; + } + } +} + +AICharPool::AICharPool() { + _head = NULL; +} + +AICharPool::~AICharPool() { +} + +void AICharPool::append(AICharacter *ai_ch) { + node *q; + node *t; + + if (_head == NULL) { + q = new node(); + q->_ai_char = ai_ch; + q->_next = NULL; + _head = q; + } + else { + q = _head; + while ( q->_next != NULL) { + q = q->_next; + } + + t = new node(); + t->_ai_char = ai_ch; + t->_next = NULL; + q->_next = t; + } +} + +void AICharPool::del(string name) { + node *q; + node *r; + q = _head; + + if (_head == NULL) { + return; + } + + // Only one node in the linked list + if (q->_next == NULL) { + if (q->_ai_char->_name == name) { + _head = NULL; + delete q; + } + return; + } + + r = q; + while ( q != NULL) { + if ( q->_ai_char->_name == name) { + // Special case + if (q == _head) { + _head = q->_next; + delete q; + return; + } + + r->_next = q->_next; + delete q; + return; + } + r = q; + q = q->_next; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: print_list +// Description: This function prints the ai characters in the +// AICharPool. Used for debugging purposes. +//////////////////////////////////////////////////////////////////// +void AICharPool::print_list() { + node* q; + q = _head; + while (q != NULL) { + cout << q->_ai_char->_name << "\n"; + q = q->_next; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: add_obstacle +// Description: This function adds the nodepath as an obstacle that +// is needed by the obstacle avoidance behavior. +//////////////////////////////////////////////////////////////////// +void AIWorld::add_obstacle(NodePath obstacle) { + _obstacles.push_back(obstacle); +} + +//////////////////////////////////////////////////////////////////// +// Function: remove_obstacle +// Description: This function removes the nodepath from the +// obstacles list that is needed by the obstacle +// avoidance behavior. +//////////////////////////////////////////////////////////////////// +void AIWorld::remove_obstacle(NodePath obstacle) { + for (unsigned int i = 0; i <= _obstacles.size(); ++i) { + if (_obstacles[i] == obstacle) { + _obstacles.erase(_obstacles.begin() + i); + } + } +} diff --git a/contrib/src/ai/aiWorld.h b/contrib/src/ai/aiWorld.h new file mode 100644 index 0000000000..49a7a2da3f --- /dev/null +++ b/contrib/src/ai/aiWorld.h @@ -0,0 +1,87 @@ +// Filename: aiWorld.h +// Created by: Deepak, John, Navin (08Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef AIWORLD_H +#define AIWORLD_H + +#include "aiGlobals.h" +#include "aiCharacter.h" +#include "flock.h" + +class AICharacter; +class Flock; + +//////////////////////////////////////////////////////////////////// +// Class : AICharPool +// Description : This class implements a linked list of AI +// Characters allowing the user to add and delete +// characters from the linked list. +// This will be used in the AIWorld class. +//////////////////////////////////////////////////////////////////// +class AICharPool { +public: + struct node { + AICharacter * _ai_char; + node * _next; + }; + + node* _head; + AICharPool(); + ~AICharPool(); + void append(AICharacter *ai_ch); + void del(string name); + void print_list(); +}; + +//////////////////////////////////////////////////////////////////// +// Class : AIWorld +// Description : A class that implements the virtual AI world which +// keeps track of the AI characters active at any +// given time. It contains a linked list of AI +// characters, obstactle data and unique name for each +// character. It also updates each characters state. +// The AI characters can also be added to the world +// as flocks. +//////////////////////////////////////////////////////////////////// +class AIWorld { +private: + AICharPool * _ai_char_pool; + NodePath _render; +public: + vector _obstacles; + typedef std::vector FlockPool; + FlockPool _flock_pool; + void remove_ai_char_from_flock(string name); + +PUBLISHED: + AIWorld(NodePath render); + ~AIWorld(); + + void add_ai_char(AICharacter *ai_ch); + void remove_ai_char(string name); + + void add_flock(Flock *flock); + void flock_off(unsigned int flock_id); + void flock_on(unsigned int flock_id); + void remove_flock(unsigned int flock_id); + Flock get_flock(unsigned int flock_id); + + void add_obstacle(NodePath obstacle); + void remove_obstacle(NodePath obstacle); + + void print_list(); + void update(); +}; + +#endif diff --git a/contrib/src/ai/ai_composite.cxx b/contrib/src/ai/ai_composite.cxx new file mode 100644 index 0000000000..cd584a1060 --- /dev/null +++ b/contrib/src/ai/ai_composite.cxx @@ -0,0 +1 @@ +#include "ai_composite1.cxx" \ No newline at end of file diff --git a/contrib/src/ai/ai_composite1.cxx b/contrib/src/ai/ai_composite1.cxx new file mode 100644 index 0000000000..25a0755143 --- /dev/null +++ b/contrib/src/ai/ai_composite1.cxx @@ -0,0 +1,20 @@ +#include "config_ai.cxx" +#include "aiWorld.cxx" +#include "aiCharacter.cxx" +#include "aiBehaviors.cxx" +#include "seek.cxx" +#include "flee.cxx" +#include "pursue.cxx" +#include "evade.cxx" +#include "arrival.cxx" +#include "flock.cxx" +#include "wander.cxx" + +#include "pathFollow.cxx" +#include "obstacleAvoidance.cxx" + +#include "pathFind.cxx" + +#include "aiNode.cxx" + +#include "aiPathFinder.cxx" diff --git a/contrib/src/ai/arrival.cxx b/contrib/src/ai/arrival.cxx new file mode 100644 index 0000000000..ee2d2827bb --- /dev/null +++ b/contrib/src/ai/arrival.cxx @@ -0,0 +1,120 @@ +// Filename: arrival.cxx +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "arrival.h" + +Arrival::Arrival(AICharacter *ai_ch, double distance) { + _ai_char = ai_ch; + + _arrival_distance = distance; + _arrival_done = false; +} + +Arrival::~Arrival() { +} + +//////////////////////////////////////////////////////////////////// +// Function: do_arrival +// Description: This function performs the arrival and returns an +// arrival force which is used in the +// calculate_prioritized function. In case the steering +// force is zero, it resets to arrival_activate. The +// arrival behavior works only when seek or pursue is +// active. This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +LVecBase3f Arrival::do_arrival() { + LVecBase3f direction_to_target; + double distance; + + if (_arrival_type) { + direction_to_target = _ai_char->get_ai_behaviors()->_pursue_obj->_pursue_target.get_pos(_ai_char->_window_render) + - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); + } + else { + direction_to_target = _ai_char->get_ai_behaviors()->_seek_obj->_seek_position + - _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); + } + distance = direction_to_target.length(); + + _arrival_direction = direction_to_target; + _arrival_direction.normalize(); + + if (int(distance) == 0) { + _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->_arrival_force = LVecBase3f(0.0, 0.0, 0.0); + + if (_ai_char->_steering->_seek_obj != NULL) { + _ai_char->_steering->turn_off("arrival"); + _ai_char->_steering->turn_on("arrival_activate"); + } + _arrival_done = true; + return (LVecBase3f(0.0, 0.0, 0.0)); + } + else { + _arrival_done = false; + } + + double u = _ai_char->get_velocity().length(); + LVecBase3f desired_force = ((u * u) / (2 * distance)) * _ai_char->get_mass(); + + if (_ai_char->_steering->_seek_obj != NULL) { + return (desired_force); + } + + if (_ai_char->_steering->_pursue_obj != NULL) { + + if (distance > _arrival_distance) { + _ai_char->_steering->turn_off("arrival"); + _ai_char->_steering->turn_on("arrival_activate"); + _ai_char->_steering->resume_ai("pursue"); + } + + return (desired_force); + } + + cout << "Arrival works only with seek and pursue\n"; + return (LVecBase3f(0.0, 0.0, 0.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: arrival_activate +// Description: This function checks for whether the target is +// within the arrival distance. When this is true, +// it calls the do_arrival function and sets the +// arrival direction. +// This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +void Arrival::arrival_activate() { + LVecBase3f dirn; + if (_arrival_type) { + dirn = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _ai_char->get_ai_behaviors()->_pursue_obj->_pursue_target.get_pos(_ai_char->_window_render)); + } + else { + dirn = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _ai_char->get_ai_behaviors()->_seek_obj->_seek_position); + } + double distance = dirn.length(); + + if (distance < _arrival_distance && _ai_char->_steering->_steering_force.length() > 0) { + _ai_char->_steering->turn_off("arrival_activate"); + _ai_char->_steering->turn_on("arrival"); + + if (_ai_char->_steering->is_on(_ai_char->_steering->BT_seek)) { + _ai_char->_steering->turn_off("seek"); + } + + if (_ai_char->_steering->is_on(_ai_char->_steering->BT_pursue)) { + _ai_char->_steering->pause_ai("pursue"); + } + } +} diff --git a/contrib/src/ai/arrival.h b/contrib/src/ai/arrival.h new file mode 100644 index 0000000000..8ec90fab6d --- /dev/null +++ b/contrib/src/ai/arrival.h @@ -0,0 +1,49 @@ +// Filename: arrival.h +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// +#ifndef ARRIVAL_H +#define ARRIVAL_H + +#include "aiGlobals.h" +#include "aiCharacter.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : Arrival +// Description : This class handles all calls to the arrival behavior +//////////////////////////////////////////////////////////////////// +class Arrival { + +public: + AICharacter *_ai_char; + + NodePath _arrival_target; + LVecBase3f _arrival_target_pos; + double _arrival_distance; + LVecBase3f _arrival_direction; + bool _arrival_done; + + // This flag specifies if the arrival behavior is being used with + // seek or pursue behavior. + // True = used with pursue. + // False = used with seek. + bool _arrival_type; + + Arrival(AICharacter *ai_ch, double distance = 10.0); + ~Arrival(); + LVecBase3f do_arrival(); + void arrival_activate(); +}; + +#endif diff --git a/contrib/src/ai/config_ai.cxx b/contrib/src/ai/config_ai.cxx new file mode 100644 index 0000000000..4831a1c6d9 --- /dev/null +++ b/contrib/src/ai/config_ai.cxx @@ -0,0 +1,55 @@ +// Filename: config_ai.cxx +// Created by: Pandai (13Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "config_ai.h" +#include "aiWorld.h" +#include "aiCharacter.h" +#include "aiBehaviors.h" +#include "seek.h" +#include "flee.h" +#include "pursue.h" +#include "evade.h" +#include "arrival.h" +#include "flock.h" +#include "wander.h" +#include "pathFollow.h" +#include "obstacleAvoidance.h" +#include "pathFind.h" +#include "aiNode.h" +#include "aiPathFinder.h" +#include "dconfig.h" + +Configure(config_ai); +NotifyCategoryDef(ai, ""); + +ConfigureFn(config_ai) { + init_libai(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libai +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libai() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; +} diff --git a/contrib/src/ai/config_ai.h b/contrib/src/ai/config_ai.h new file mode 100644 index 0000000000..eb4928e353 --- /dev/null +++ b/contrib/src/ai/config_ai.h @@ -0,0 +1,26 @@ +// Filename: config_ai.h +// Created by: Pandai (13Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_AI_H +#define CONFIG_AI_H + +#include "contribbase.h" +#include "notifyCategoryProxy.h" + +NotifyCategoryDecl(ai, EXPCL_PANDAAI, EXPTP_PANDAAI); + +extern EXPCL_PANDAAI void init_libai(); + +#endif + diff --git a/contrib/src/ai/evade.cxx b/contrib/src/ai/evade.cxx new file mode 100644 index 0000000000..61cbaf4d12 --- /dev/null +++ b/contrib/src/ai/evade.cxx @@ -0,0 +1,86 @@ +// Filename: evade.cxx +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "evade.h" + +Evade:: +Evade(AICharacter *ai_ch, NodePath target_object, + double panic_distance, double relax_distance, float evade_wt) { + _ai_char = ai_ch; + + _evade_target = target_object; + _evade_distance = panic_distance; + _evade_relax_distance = relax_distance; + _evade_weight = evade_wt; + + _evade_done = true; + _evade_activate_done = false; +} + +Evade::~Evade() { +} + +//////////////////////////////////////////////////////////////////// +// Function: do_evade +// Description: This function performs the evade and returns an +// evade force which is used in the +// calculate_prioritized function. In case the +// AICharacter is past the (panic + relax) distance, +// it resets to evade_activate. This function is not +// to be used by the user. +//////////////////////////////////////////////////////////////////// +LVecBase3f Evade::do_evade() { + assert(_evade_target && "evade target not assigned"); + + _evade_direction = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) + - _evade_target.get_pos(_ai_char->_window_render); + double distance = _evade_direction.length(); + + _evade_direction.normalize(); + LVecBase3f desired_force = _evade_direction * _ai_char->_movt_force; + + if (distance > (_evade_distance + _evade_relax_distance)) { + if ((_ai_char->_steering->_behaviors_flags | + _ai_char->_steering->BT_evade) == _ai_char->_steering->BT_evade) { + _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + } + _ai_char->_steering->turn_off("evade"); + _ai_char->_steering->turn_on("evade_activate"); + _evade_done = true; + return (LVecBase3f(0.0, 0.0, 0.0)); + } else { + _evade_done = false; + return (desired_force); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: evade_activate +// Description: This function checks for whether the target is +// within the panic distance. +// When this is true, it calls the do_evade function +// and sets the evade direction. +// This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +void Evade::evade_activate() { + _evade_direction = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _evade_target.get_pos(_ai_char->_window_render)); + double distance = _evade_direction.length(); + _evade_activate_done = false; + + if (distance < _evade_distance) { + _ai_char->_steering->turn_off("evade_activate"); + _ai_char->_steering->turn_on("evade"); + _evade_activate_done = true; + } +} diff --git a/contrib/src/ai/evade.h b/contrib/src/ai/evade.h new file mode 100644 index 0000000000..017ee45c9a --- /dev/null +++ b/contrib/src/ai/evade.h @@ -0,0 +1,48 @@ +// Filename: evade.h +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef EVADE_H +#define EVADE_H + +#include "aiGlobals.h" +#include "aiCharacter.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : Evade +// Description : This class handles all calls to the evade behavior +//////////////////////////////////////////////////////////////////// +class Evade { + +public: + AICharacter *_ai_char; + + NodePath _evade_target; + float _evade_weight; + LVecBase3f _evade_direction; + double _evade_distance; + double _evade_relax_distance; + bool _evade_done; + bool _evade_activate_done; + + Evade(AICharacter *ai_ch, NodePath target_object, double panic_distance, + double relax_distance, float evade_wt); + + ~Evade(); + LVecBase3f do_evade(); + void evade_activate(); +}; + +#endif diff --git a/contrib/src/ai/flee.cxx b/contrib/src/ai/flee.cxx new file mode 100644 index 0000000000..9245d0e039 --- /dev/null +++ b/contrib/src/ai/flee.cxx @@ -0,0 +1,104 @@ +// Filename: flee.cxx +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "flee.h" + +Flee::Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance, + double relax_distance, float flee_wt){ + + _ai_char = ai_ch; + + _flee_position = target_object.get_pos(_ai_char->_window_render); + _flee_distance = panic_distance; + _flee_weight = flee_wt; + _flee_relax_distance = relax_distance; + + _flee_done = false; + _flee_activate_done = false; +} + +Flee::Flee(AICharacter *ai_ch, LVecBase3f pos, double panic_distance, + double relax_distance, float flee_wt){ + + _ai_char = ai_ch; + + _flee_position = pos; + _flee_distance = panic_distance; + _flee_weight = flee_wt; + _flee_relax_distance = relax_distance; + + _flee_done = false; + _flee_activate_done = false; +} + +Flee::~Flee() { +} + +//////////////////////////////////////////////////////////////////// +// Function: do_flee +// Description: This function performs the flee and returns a flee +// force which is used in the calculate_prioritized +// function. In case the AICharacter is past the +// (panic + relax) distance, it resets to flee_activate. +// This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +LVecBase3f Flee::do_flee() { + LVecBase3f dirn; + double distance; + LVecBase3f desired_force; + + dirn = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_present_pos; + distance = dirn.length(); + desired_force = _flee_direction * _ai_char->_movt_force; + + if (distance > (_flee_distance + _flee_relax_distance)) { + if ((_ai_char->_steering->_behaviors_flags | _ai_char->_steering->BT_flee) == _ai_char->_steering->BT_flee) { + _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + } + _flee_done = true; + _ai_char->_steering->turn_off("flee"); + _ai_char->_steering->turn_on("flee_activate"); + return (LVecBase3f(0.0, 0.0, 0.0)); + } + else { + return (desired_force); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: flee_activate +// Description: This function checks for whether the target is +// within the panic distance. When this is true, +// it calls the do_flee function and sets the flee +// direction. +// This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +void Flee::flee_activate() { + LVecBase3f dirn; + double distance; + + _flee_activate_done = false; + + dirn = (_ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_position); + distance = dirn.length(); + + if (distance < _flee_distance) { + _flee_direction = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render) - _flee_position; + _flee_direction.normalize(); + _flee_present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); + _ai_char->_steering->turn_off("flee_activate"); + _ai_char->_steering->turn_on("flee"); + _flee_activate_done = true; + } +} diff --git a/contrib/src/ai/flee.h b/contrib/src/ai/flee.h new file mode 100644 index 0000000000..9b0ae8c978 --- /dev/null +++ b/contrib/src/ai/flee.h @@ -0,0 +1,52 @@ +// Filename: flee.h +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef FLEE_H +#define FLEE_H + +#include "aiGlobals.h" +#include "aiCharacter.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : Flee +// Description : This class handles all calls to the flee behavior +//////////////////////////////////////////////////////////////////// +class Flee { + +public: + AICharacter *_ai_char; + + LVecBase3f _flee_position; + float _flee_weight; + LVecBase3f _flee_direction; + double _flee_distance; + double _flee_relax_distance; + LVecBase3f _flee_present_pos; + bool _flee_done; + bool _flee_activate_done; + + Flee(AICharacter *ai_ch, NodePath target_object, double panic_distance = 10.0, + double relax_distance = 10.0, float flee_wt = 1.0); + + Flee(AICharacter *ai_ch, LVecBase3f pos, double panic_distance = 10.0, + double relax_distance = 10.0, float flee_wt = 1.0); + + ~Flee(); + LVecBase3f do_flee(); + void flee_activate(); +}; + +#endif diff --git a/contrib/src/ai/flock.cxx b/contrib/src/ai/flock.cxx new file mode 100644 index 0000000000..36445b208f --- /dev/null +++ b/contrib/src/ai/flock.cxx @@ -0,0 +1,42 @@ +// Filename: flock.cxx +// Created by: Deepak, John, Navin (12Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "flock.h" + +Flock::Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt, + unsigned int cohesion_wt, unsigned int alignment_wt) { + _flock_id = flock_id; + _flock_vcone_angle = vcone_angle; + _flock_vcone_radius = vcone_radius; + _separation_wt = separation_wt; + _cohesion_wt = cohesion_wt; + _alignment_wt = alignment_wt; +} + +Flock::~Flock() { +} + +//////////////////////////////////////////////////////////////////// +// Function: add_ai_char +// Description: This function adds AI characters to the flock. +//////////////////////////////////////////////////////////////////// +void Flock::add_ai_char(AICharacter *ai_char) { + ai_char->_ai_char_flock_id = _flock_id; + ai_char->_steering->_flock_group = this; + _ai_char_list.push_back(ai_char); +} + +unsigned int Flock::get_id() { + return _flock_id; +} diff --git a/contrib/src/ai/flock.h b/contrib/src/ai/flock.h new file mode 100644 index 0000000000..7e900631d3 --- /dev/null +++ b/contrib/src/ai/flock.h @@ -0,0 +1,62 @@ +// Filename: flock.h +// Created by: Deepak, John, Navin (12Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef FLOCK_H +#define FLOCK_H + +#include "aiGlobals.h" +#include "aiCharacter.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : Flock +// Description : This class is used to define the flock attributes +// and the AI characters which are part of the flock. +//////////////////////////////////////////////////////////////////// +class Flock { +private: + unsigned int _flock_id; + +public: + // Variables which will hold the parameters of the ai character's + // visibility cone. + double _flock_vcone_angle; + double _flock_vcone_radius; + + // Variables to specify weights of separation, cohesion and + // alignment behaviors and thus create variable flock behavior. + unsigned int _separation_wt; + unsigned int _cohesion_wt; + unsigned int _alignment_wt; + + // This vector will hold all the ai characters which belong to + // this flock. + typedef std::vector AICharList; + AICharList _ai_char_list; + +PUBLISHED: + Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, + unsigned int separation_wt = 2, unsigned int cohesion_wt = 4, + unsigned int alignment_wt = 1); + ~Flock(); + + // Function to add the ai characters to _ai_char_list. + void add_ai_char(AICharacter *ai_char); + + // Function to access the private member flock_id. + unsigned int get_id(); +}; + +#endif diff --git a/contrib/src/ai/globals.h b/contrib/src/ai/globals.h new file mode 100644 index 0000000000..46d0a64b5b --- /dev/null +++ b/contrib/src/ai/globals.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////// +//! Filename : globals.h +//! Created by : Deepak, John, Navin +//! Date: 8 Sep 09 +//! +//////////////////////////////////////////////////////////////////////// +//! +//! PANDA3D SOFTWARE +//! Copyright(c) Carnegie Mellon University. All rights reserved. +//! +//! All use of this software is subjest to the terms of the revised BSD +//! license. You should have received a copy of this license along with this source code in a file named "LICENSE" + +//////////////////////////////////////////////////////////////////////// + +#pragma warning (disable:4996) +#pragma warning (disable:4005) +#pragma warning(disable:4275) + +#ifndef _GLOBALS_H +#define _GLOBALS_H + +#include "pandaFramework.h" +#include "textNode.h" +#include "pandaSystem.h" + +#include "lvecBase3.h" +#include "nodePath.h" + +#include "genericAsyncTask.h" +#include "asyncTaskManager.h" + +#endif \ No newline at end of file diff --git a/contrib/src/ai/meshNode.cxx b/contrib/src/ai/meshNode.cxx new file mode 100644 index 0000000000..7fc0711f69 --- /dev/null +++ b/contrib/src/ai/meshNode.cxx @@ -0,0 +1,43 @@ + +#include "meshNode.h" + +Node::Node(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h) { + for(int i = 0; i < 8; ++i) { + _neighbours[i] = NULL; + } + + _position = pos; + _width = w; + _length = l; + _height = h; + _grid_x = grid_x; + _grid_y = grid_y; + _status = neutral; + _type = true; + _score = 0; + _cost = 0; + _heuristic = 0; + _next = NULL; + _prv_node = NULL; +} + +Node::~Node() { +} + +///////////////////////////////////////////////////////////////////////////////////////// +//! +//! Function : contains +//! Description : This is a handy function which returns true if the passed position is +//! within the node's dimensions. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool Node::contains(float x, float y) { + if(_position.get_x() - _width / 2 <= x && _position.get_x() + _width / 2 >= x && + _position.get_y() - _length / 2 <= y && _position.get_y() + _length / 2 >= y) { + return true; + } + else { + return false; + } +} \ No newline at end of file diff --git a/contrib/src/ai/meshNode.h b/contrib/src/ai/meshNode.h new file mode 100644 index 0000000000..7c4a67a8d4 --- /dev/null +++ b/contrib/src/ai/meshNode.h @@ -0,0 +1,67 @@ + +#ifndef _MESHNODE_H +#define _MESHNODE_H + +#include "globals.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//! +//! Class : Node +//! Description : This class is used to assign the nodes on the mesh. It holds all the data necessary to +//! compute A* algorithm. It also maintains a lot of vital information such as the neighbor +//! nodes of each node and also its position on the mesh. +//! Note: The Mesh Generator which is a stand alone tool makes use of this class to generate the nodes on the +//! mesh. + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class Node { +public: + // This variable specifies whether the node is an obtacle or not. + // Used for dynamic obstacle addition to the environment. + // obstacle = false + // navigational = true + bool _type; + + // This variable specifies the node status whether open, close or neutral. + // open = belongs to _open_list. + // close = belongs to _closed_list. + // neutral = unexamined node. + enum Status { + open, + close, + neutral + }; + Status _status; + + // The score is used to compute the traversal expense to nodes when using A*. + // _score = _cost + heuristic + int _score; + int _cost; + int _heuristic; + + // Used to trace back the path after it is generated using A*. + Node *_prv_node; + + // Position of the node in the 2d grid. + int _grid_x, _grid_y; + + // Position of the node in 3D space. + LVecBase3f _position; + + // Dimensions of each face / cell on the mesh. + // Height is given in case of expansion to a 3d mesh. Currently not used. + float _width, _length ,_height; + Node *_neighbours[8]; // anti-clockwise from top left corner. + + // The _next pointer is used for traversal during mesh generation from the model. + // Note: The data in this member is discarded when mesh data is written into navmesh.csv file. + Node *_next; + + Node(int grid_x, int grid_y, LVecBase3f pos, float w, float l, float h); + ~Node(); + + bool contains(float x, float y); +}; + +#endif \ No newline at end of file diff --git a/contrib/src/ai/obstacleAvoidance.cxx b/contrib/src/ai/obstacleAvoidance.cxx new file mode 100644 index 0000000000..98632fd575 --- /dev/null +++ b/contrib/src/ai/obstacleAvoidance.cxx @@ -0,0 +1,119 @@ +// Filename: obstacleAvoidance.cxx +// Created by: Deepak, John, Navin (10Nov09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "obstacleAvoidance.h" + +ObstacleAvoidance::ObstacleAvoidance(AICharacter *ai_char, float feeler_length) { + _ai_char = ai_char; + _feeler = feeler_length; +} + +ObstacleAvoidance::~ObstacleAvoidance() { +} + +//////////////////////////////////////////////////////////////////// +// Function: obstacle_detection +// Description: This function checks if an obstacle is near to the +// AICharacter and if an obstacle is detected returns +// true. +//////////////////////////////////////////////////////////////////// +bool ObstacleAvoidance::obstacle_detection() { + // Calculate the volume of the AICharacter with respect to render + PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds(); + CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere(); + LVecBase3f avoidance(0.0, 0.0, 0.0); + double distance = 0x7fff ; + double expanded_radius; + LVecBase3f to_obstacle; + LVecBase3f prev_avoidance; + for (unsigned int i = 0; i < _ai_char->_world->_obstacles.size(); ++i) { + PT(BoundingVolume) bounds = _ai_char->_world->_obstacles[i].get_bounds(); + CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere(); + LVecBase3f near_obstacle = _ai_char->_world->_obstacles[i].get_pos() + - _ai_char->get_node_path().get_pos(); + // Check if it's the nearest obstacle, If so initialize as the nearest + // obstacle + if ((near_obstacle.length() < distance) && + (_ai_char->_world->_obstacles[i].get_pos() != _ai_char->get_node_path().get_pos())) { + _nearest_obstacle = _ai_char->_world->_obstacles[i]; + distance = near_obstacle.length(); + expanded_radius = bsphere->get_radius() + np_sphere->get_radius(); + } + } + LVecBase3f feeler = _feeler * _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward()); + feeler.normalize(); + feeler *= (expanded_radius + np_sphere->get_radius()) ; + to_obstacle = _nearest_obstacle.get_pos() - _ai_char->get_node_path().get_pos(); + LVector3f line_vector = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward()); + LVecBase3f project = (to_obstacle.dot(line_vector) * line_vector) / line_vector.length_squared(); + LVecBase3f perp = project - to_obstacle; + // If the nearest obstacle will collide with our AICharacter then + // send obstacle detection as true + if ((_nearest_obstacle) && + (perp.length() < expanded_radius - np_sphere->get_radius()) && + (project.length() < feeler.length())) { + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: obstacle_avoidance_activate +// Description: This function activates obstacle_avoidance if an +// obstacle is detected +//////////////////////////////////////////////////////////////////// +void ObstacleAvoidance::obstacle_avoidance_activate() { + if (obstacle_detection()) { + _ai_char->_steering->turn_off("obstacle_avoidance_activate"); + _ai_char->_steering->turn_on("obstacle_avoidance"); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: do_obstacle_avoidance +// Description: This function returns the force necessary by the +// AICharacter to avoid the nearest obstacle detected +// by obstacle_detection function +// NOTE : This assumes the obstacles are spherical +//////////////////////////////////////////////////////////////////// +LVecBase3f ObstacleAvoidance::do_obstacle_avoidance() { + LVecBase3f offset = _ai_char->get_node_path().get_pos() + - _nearest_obstacle.get_pos(); + PT(BoundingVolume) bounds =_nearest_obstacle.get_bounds(); + CPT(BoundingSphere) bsphere = bounds->as_bounding_sphere(); + PT(BoundingVolume) np_bounds = _ai_char->get_node_path().get_bounds(); + CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere(); + double distance_needed = offset.length() - bsphere->get_radius() + - np_sphere->get_radius(); + + if (obstacle_detection()) { + LVecBase3f direction = _ai_char->get_char_render().get_relative_vector( + _ai_char->get_node_path(), LVector3f::forward()); + direction.normalize(); + float forward_component = offset.dot(direction); + LVecBase3f projection = forward_component * direction; + LVecBase3f perpendicular_component = offset - projection; + double p = perpendicular_component.length(); + perpendicular_component.normalize(); + LVecBase3f avoidance = perpendicular_component; + + // The closer the obstacle, the more force it generates + avoidance = (avoidance * _ai_char->get_max_force() * + _ai_char->_movt_force) / (p + 0.01); + return avoidance; + } + _ai_char->_steering->turn_on("obstacle_avoidance_activate"); + _ai_char->_steering->turn_off("obstacle_avoidance"); + return LVecBase3f(0, 0, 0); +} diff --git a/contrib/src/ai/obstacleAvoidance.h b/contrib/src/ai/obstacleAvoidance.h new file mode 100644 index 0000000000..e4e17cd66d --- /dev/null +++ b/contrib/src/ai/obstacleAvoidance.h @@ -0,0 +1,42 @@ +// Filename: obstacleAvoidance.h +// Created by: Deepak, John, Navin (10Nov2009) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// +#ifndef OBSTACLE_AVOIDANCE_H +#define OBSTACLE_AVOIDANCE_H + +#include "aiCharacter.h" +#include "boundingSphere.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : ObstacleAvoidance +// Description : This class handles all calls to the obstacle +// avoidance behavior +//////////////////////////////////////////////////////////////////// +class ObstacleAvoidance { + public : + AICharacter *_ai_char; + float _obstacle_avoidance_weight; + NodePath _nearest_obstacle; + bool _obstacle_avoidance_done; + float _feeler; + + ObstacleAvoidance(AICharacter *ai_char, float feeler_length); + LVecBase3f do_obstacle_avoidance(); + ~ObstacleAvoidance(); + void obstacle_avoidance_activate(); + bool obstacle_detection(); +}; + +#endif diff --git a/contrib/src/ai/pathFind.cxx b/contrib/src/ai/pathFind.cxx new file mode 100644 index 0000000000..df32791992 --- /dev/null +++ b/contrib/src/ai/pathFind.cxx @@ -0,0 +1,413 @@ + +// Filename: pathFind.cxx +// Created by: Deepak, John, Navin (12Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "pathFind.h" + +PathFind::PathFind(AICharacter *ai_ch) { + _ai_char = ai_ch; + + _parent = new GeomNode("parent"); + _ai_char->_window_render.attach_new_node(_parent); + + _pen = new LineSegs("pen"); + _pen->set_color(1.0, 0.0, 0.0); + _pen->set_thickness(2.0); + + _path_finder_obj = NULL; + _dynamic_avoid = false; +} + +PathFind::~PathFind() { +} + +//////////////////////////////////////////////////////////////////// +// Function: create_nav_mesh +// Description: This function recreates the navigation mesh from +// the .csv file. +//////////////////////////////////////////////////////////////////// +void PathFind::create_nav_mesh(const char* navmesh_filename) { + // Stage variables. + int grid_x, grid_y; + float l, w, h; + LVecBase3f position; + + // Variable to hold line data read from file. + string line; + + // Array for storing data members obtained from each line of the + // file. + string fields[10]; + + // Open data file for reading. + ifstream nav_mesh_file (navmesh_filename); + + if (nav_mesh_file.is_open()) { + // Capture the grid size from the file. + getline(nav_mesh_file, line); + int pos = line.find(","); + _grid_size = atoi((line.substr(pos + 1)).c_str()); + + // Initialize the stage mesh with NULL nodes. + for (int r = 0; r < _grid_size; ++r) { + _nav_mesh.push_back(vector()); + for (int c = 0; c < _grid_size; ++c) { + _nav_mesh[r].push_back(NULL); + } + } + + // Ignore the header of the navmesh.csv file. + getline(nav_mesh_file, line); + + // Begin reading data from the file. + while (!nav_mesh_file.eof()) { + getline(nav_mesh_file, line); + stringstream linestream (line); + + // Stores all the data members in the line to the array. + // Data structure: NULL,NodeType,GridX,GridY,Length,Width, + // Height,PosX,PosY,PosZ + for (int i = 0; i < 10; ++i) { + getline(linestream, fields[i], ','); + } + + // Populate the main nodes into stage mesh. + if (fields[0] == "0" && fields[1] == "0") { + grid_x = atoi(fields[2].c_str()); + grid_y = atoi(fields[3].c_str()); + l = atof(fields[4].c_str()); + w = atof(fields[5].c_str()); + h = atof(fields[6].c_str()); + position = LVecBase3f(atof(fields[7].c_str()), + atof(fields[8].c_str()), atof(fields[9].c_str())); + + AINode *stage_node = new AINode(grid_x, grid_y, position, w, l, h); + + _nav_mesh[grid_y][grid_x] = stage_node; + } + else if (fields[0] == "") { + // End of file reached at this point. + nav_mesh_file.close(); + + // Assign the neighbor nodes for each of the main nodes + // that just got populated into the stage mesh. + assign_neighbor_nodes(navmesh_filename); + } + } + } + else { + cout << "error opening navmesh.csv file!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: assign_neighbor_nodes +// Description: This function assigns the neighbor nodes for each +// main node present in _nav_mesh. +//////////////////////////////////////////////////////////////////// +void PathFind::assign_neighbor_nodes(const char* navmesh_filename){ + ifstream nav_mesh_file (navmesh_filename); + + // Stage variables. + int gd_x, gd_y, gd_xn, gd_yn; + string ln; + string fields[10]; + string fields_n[10]; + + if (nav_mesh_file.is_open()) { + getline(nav_mesh_file, ln); // Get rid of grid size line. + getline(nav_mesh_file, ln); // Get rid of the header. + + while (!nav_mesh_file.eof()) { + getline(nav_mesh_file, ln); // Gets main node data only. No neighbor nodes. + stringstream linestream (ln); + for (int i = 0; i < 10; ++i) { + getline(linestream, fields[i], ','); + } + if (fields[0] == "0" && fields[1] == "0") { + // Usable main node. + gd_x = atoi(fields[2].c_str()); + gd_y = atoi(fields[3].c_str()); + for (int i = 0; i < 8; ++i) { + getline(nav_mesh_file, ln); // Gets neighbor node data only. No main nodes. + stringstream linestream_n (ln); + for (int j = 0; j < 10; ++j) { + getline(linestream_n, fields_n[j], ','); + } + gd_xn = atoi(fields_n[2].c_str()); + gd_yn = atoi(fields_n[3].c_str()); + + if (fields_n[0] == "0" && fields_n[1] == "1") { + // Usable neighbor for main node. + // Note: The indices of the vector are inverted + // when compared to the values of the nodes on actual grid. + _nav_mesh[gd_y][gd_x]->_neighbours[i] = _nav_mesh[gd_yn][gd_xn]; + } + else if (fields_n[0] == "1" && fields_n[1] == "1") { + // NULL neighbor. + _nav_mesh[gd_y][gd_x]->_neighbours[i] = NULL; + } + else { + cout << "Warning: Corrupt data!\n"; + } + } + } + else if (fields[0] == "") { + // End of file reached at this point. + nav_mesh_file.close(); + } + } + } + else { + cout << "error opening navmesh.csv file!\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: set_path_find +// Description: This function starts the path finding process after +// reading the given navigation mesh. +//////////////////////////////////////////////////////////////////// +void PathFind::set_path_find(const char* navmesh_filename) { + create_nav_mesh(navmesh_filename); + + if (_ai_char->_steering->_path_follow_obj) { + _ai_char->_steering->remove_ai("pathfollow"); + } + + _ai_char->_steering->path_follow(1.0f); + + if (_path_finder_obj) { + delete _path_finder_obj; + _path_finder_obj = NULL; + } + + _path_finder_obj = new PathFinder(_nav_mesh); +} + +//////////////////////////////////////////////////////////////////// +// Function: path_find (for pathfinding towards a static position) +// Description: This function checks for the source and target in +// the navigation mesh for its availability and then +// finds the best path via the A* algorithm Then it +// calls the path follower to make the object follow +// the path. +//////////////////////////////////////////////////////////////////// +void PathFind::path_find(LVecBase3f pos, string type) { + if (type == "addPath") { + if (_ai_char->_steering->_path_follow_obj) { + _ai_char->_steering->remove_ai("pathfollow"); + } + + _ai_char->_steering->path_follow(1.0f); + } + + clear_path(); + + AINode* src = find_in_mesh(_nav_mesh, + _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _grid_size); + + if (src == NULL) { + cout << "couldnt find source\n"; + } + + AINode* dst = find_in_mesh(_nav_mesh, pos, _grid_size); + + if (dst == NULL) { + cout << "couldnt find destination\n"; + } + + if (src != NULL && dst != NULL) { + _path_finder_obj->find_path(src, dst); + trace_path(src); + } + + if (!_ai_char->_steering->_path_follow_obj->_start) { + _ai_char->_steering->start_follow(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: path_find (for pathfinding towards a moving target +// (a NodePath)) +// Description: This function checks for the source and target in +// the navigation mesh for its availability and then +// finds the best path via the A* algorithm +// Then it calls the path follower to make the object +// follow the path. +//////////////////////////////////////////////////////////////////// +void PathFind::path_find(NodePath target, string type) { + if (type == "addPath") { + if (_ai_char->_steering->_path_follow_obj) { + _ai_char->_steering->remove_ai("pathfollow"); + } + + _ai_char->_steering->path_follow(1.0f); + } + + clear_path(); + + _path_find_target = target; + _prev_position = target.get_pos(_ai_char->_window_render); + + AINode* src = find_in_mesh(_nav_mesh, + _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _grid_size); + + if (src == NULL) { + cout << "couldnt find source\n"; + } + + AINode* dst = find_in_mesh(_nav_mesh, _prev_position, _grid_size); + + if (dst == NULL) { + cout << "couldnt find destination\n"; + } + + if (src != NULL && dst != NULL) { + _path_finder_obj->find_path(src, dst); + trace_path(src); + } + + if (_ai_char->_steering->_path_follow_obj!=NULL) { + if (!_ai_char->_steering->_path_follow_obj->_start) { + _ai_char->_steering->start_follow("pathfind"); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: clear_path +// Description: Helper function to restore the path and mesh +// to its initial state +//////////////////////////////////////////////////////////////////// +void PathFind::clear_path() { + // Initialize to zero + for (int i = 0; i < _grid_size; ++i) { + for (int j = 0; j < _grid_size; ++j) { + if (_nav_mesh[i][j] != NULL) { + _nav_mesh[i][j]->_status = _nav_mesh[i][j]->ST_neutral; + _nav_mesh[i][j]->_cost = 0; + _nav_mesh[i][j]->_heuristic = 0; + _nav_mesh[i][j]->_score = 0; + _nav_mesh[i][j]->_prv_node = NULL; + } + } + } + + if (_path_finder_obj) { + _path_finder_obj->_open_list.clear(); + _path_finder_obj->_closed_list.clear(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: trace_path +// Description: This function is the function which sends the path +// information one by one to the path follower so that +// it can store the path needed to be +// traversed by the pathfinding object +//////////////////////////////////////////////////////////////////// +void PathFind::trace_path(AINode* src) { + if (_ai_char->_pf_guide) { + _parent->remove_all_children(); + } + else { + _parent->remove_all_children(); + } + + if (_path_finder_obj->_closed_list.size() > 0) { + AINode *traversor = _path_finder_obj->_closed_list[_path_finder_obj->_closed_list.size() - 0.5]; + while (traversor != src) { + if (_ai_char->_pf_guide) { + _pen->move_to(traversor->_position.get_x(), traversor->_position.get_y(), 1); + _pen->draw_to(traversor->_prv_node->_position.get_x(), + traversor->_prv_node->_position.get_y(), 0.5); + PT(GeomNode) gnode = _pen->create(); + _parent->add_child(gnode); + } + _ai_char->_steering->add_to_path(traversor->_position); + traversor = traversor->_prv_node; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: add_obstacle_to_mesh +// Description: This function allows the user to dynamically add +// obstacles to the game environment. The function +// will update the nodes within the +// bounding volume of the obstacle as non-traversable. +// Hence will not be considered by the pathfinding +// algorithm. +//////////////////////////////////////////////////////////////////// +void PathFind::add_obstacle_to_mesh(NodePath obstacle) { + PT(BoundingVolume) np_bounds = obstacle.get_bounds(); + CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere(); + + AINode* temp = find_in_mesh(_nav_mesh, obstacle.get_pos(), _grid_size); + + if (temp != NULL) { + float left = temp->_position.get_x() - np_sphere->get_radius(); + float right = temp->_position.get_x() + np_sphere->get_radius(); + float top = temp->_position.get_y() + np_sphere->get_radius(); + float down = temp->_position.get_y() - np_sphere->get_radius(); + + for (int i = 0; i < _grid_size; ++i) { + for (int j = 0; j < _grid_size; ++j) { + if (_nav_mesh[i][j] != NULL && _nav_mesh[i][j]->_type == true) { + if (_nav_mesh[i][j]->_position.get_x() >= left && _nav_mesh[i][j]->_position.get_x() <= right && + _nav_mesh[i][j]->_position.get_y() >= down && _nav_mesh[i][j]->_position.get_y() <= top) { + _nav_mesh[i][j]->_type = false; + _previous_obstacles.insert(_previous_obstacles.end(), i); + _previous_obstacles.insert(_previous_obstacles.end(), j); + } + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: do_dynamic_avoid() +// Description: This function does the updation of the collisions to +// the mesh based on the new positions of the obstacles. +//////////////////////////////////////////////////////////////////// +void PathFind::do_dynamic_avoid() { + clear_previous_obstacles(); + _previous_obstacles.clear(); + for (unsigned int i = 0; i < _dynamic_obstacle.size(); ++i) { + add_obstacle_to_mesh(_dynamic_obstacle[i]); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: clear_previous_obstacles() +// Description: Helper function to reset the collisions if the +// obstacle is not on the node anymore. +//////////////////////////////////////////////////////////////////// +void PathFind::clear_previous_obstacles(){ + for (unsigned int i = 0; i < _previous_obstacles.size(); i = i + 2) { + _nav_mesh[_previous_obstacles[i]][_previous_obstacles[i + 1]]->_type = true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: dynamic_avoid +// Description: This function starts the pathfinding obstacle +// navigation for the passed in obstacle. +//////////////////////////////////////////////////////////////////// +void PathFind::dynamic_avoid(NodePath obstacle) { + _dynamic_avoid = true; + _dynamic_obstacle.insert(_dynamic_obstacle.end(), obstacle); +} diff --git a/contrib/src/ai/pathFind.h b/contrib/src/ai/pathFind.h new file mode 100644 index 0000000000..2d33d46b2a --- /dev/null +++ b/contrib/src/ai/pathFind.h @@ -0,0 +1,70 @@ +// Filename: pathFind.h +// Created by: Deepak, John, Navin (12Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef PATHFIND_H +#define PATHFIND_H + +#include "aiGlobals.h" +#include "aiCharacter.h" +#include "aiPathFinder.h" +#include "boundingSphere.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : PathFind +// Description : This class contains all the members and functions +// that are required to form an interface between +// the AIBehaviors class and the PathFinder class. +// An object (pointer) of this class is provided in +// the AIBehaviors class. It is only via this object +// that the user can activate pathfinding. +//////////////////////////////////////////////////////////////////// +class PathFind { +public: + AICharacter *_ai_char; + PathFinder *_path_finder_obj; + + NavMesh _nav_mesh; + NavMesh _stage_mesh; + + int _grid_size; + NodePath _path_find_target; + LVecBase3f _prev_position; + PT(GeomNode) _parent; + LineSegs *_pen; + vector _previous_obstacles; + bool _dynamic_avoid; + vector _dynamic_obstacle; + + PathFind(AICharacter *ai_ch); + ~PathFind(); + + void clear_path(); + void trace_path(AINode* src); + + void create_nav_mesh(const char* navmesh_filename); + void assign_neighbor_nodes(const char* navmesh_filename); + void do_dynamic_avoid(); + void clear_previous_obstacles(); + + void set_path_find(const char* navmesh_filename); + void path_find(LVecBase3f pos, string type = "normal"); + void path_find(NodePath target, string type = "normal"); + void add_obstacle_to_mesh(NodePath obstacle); + void dynamic_avoid(NodePath obstacle); +}; + +#endif + diff --git a/contrib/src/ai/pathFollow.cxx b/contrib/src/ai/pathFollow.cxx new file mode 100644 index 0000000000..de2e79b99f --- /dev/null +++ b/contrib/src/ai/pathFollow.cxx @@ -0,0 +1,138 @@ +// Filename: pathFollow.cxx +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "pathFollow.h" + +PathFollow::PathFollow(AICharacter *ai_ch, float follow_wt) { + _follow_weight = follow_wt; + _curr_path_waypoint = -1; + _start = false; + _ai_char = ai_ch; + _myClock = ClockObject::get_global_clock(); +} + +PathFollow::~PathFollow() { +} + +//////////////////////////////////////////////////////////////////// +// Function: add_to_path +// Description: This function adds the positions generated from a +// pathfind or a simple path follow behavior to the +// _path list. +//////////////////////////////////////////////////////////////////// +void PathFollow::add_to_path(LVecBase3f pos) { + _path.push_back(pos); +} + +//////////////////////////////////////////////////////////////////// +// Function: start +// Description: This function initiates the path follow behavior. +//////////////////////////////////////////////////////////////////// +void PathFollow::start(string type) { + _type = type; + _start = true; + if (_path.size() > 0) { + _curr_path_waypoint = _path.size() - 1; + _dummy = _ai_char->_window_render.attach_new_node("dummy"); + _dummy.set_pos(_path[_curr_path_waypoint]); + _ai_char->_steering->pursue(_dummy, _follow_weight); + _time = _myClock->get_real_time(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: do_follow +// Description: This function allows continuous path finding by AI +// chars. There are 2 ways in which this is implemented. +// 1. The character re-calculates the optimal path +// everytime the target changes its position. +// Less computationally expensive. +// 2. The character continuosly re-calculates its +// optimal path to the target. This is used in a +// scenario where the ai chars have to avoid other AI +// chars. More computationally expensive. +//////////////////////////////////////////////////////////////////// +void PathFollow::do_follow() { + if ((_myClock->get_real_time() - _time) > 0.5) { + if (_type == "pathfind") { + // This 'if' statement when 'true' causes the path to be + // re-calculated irrespective of target position. + // This is done when _dynamice_avoid is active. More + // computationally expensive. + if (_ai_char->_steering->_path_find_obj->_dynamic_avoid) { + _ai_char->_steering->_path_find_obj->do_dynamic_avoid(); + if (check_if_possible()) { + _path.clear(); + _ai_char->_steering->_path_find_obj->path_find(_ai_char->_steering->_path_find_obj->_path_find_target); + // Ensure that the path size is not 0. + if (_path.size() > 0) { + _curr_path_waypoint = _path.size() - 1; + _dummy.set_pos(_path[_curr_path_waypoint]); + } + else { + // Refresh the _curr_path_waypoint value if path size is <= 0. + _curr_path_waypoint = -1; + } + } + } + // This 'if' statement causes the path to be re-calculated only + // when there is a change in target position. + // Less computationally expensive. + else if (_ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render) + != _ai_char->_steering->_path_find_obj->_prev_position) { + if (check_if_possible()) { + _path.clear(); + _ai_char->_steering->_path_find_obj->path_find(_ai_char->_steering->_path_find_obj->_path_find_target); + // Ensure that the path size is not 0. + if (_path.size() > 0) { + _curr_path_waypoint = _path.size() - 1; + _dummy.set_pos(_path[_curr_path_waypoint]); + } + else { + // Refresh the _curr_path_waypoint value if path size is 0. + _curr_path_waypoint = -1; + } + } + } + _time = _myClock->get_real_time(); + } + } + + if (_curr_path_waypoint > 0) { + double distance = (_path[_curr_path_waypoint] - + _ai_char->_ai_char_np.get_pos(_ai_char->_window_render)).length(); + + if (distance < 5) { + _curr_path_waypoint--; + _dummy.set_pos(_path[_curr_path_waypoint]); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: check_if_possible +// Description: This function checks if the current positions of +// the ai char and the target char can be used to +// generate an optimal path. +//////////////////////////////////////////////////////////////////// +bool PathFollow::check_if_possible() { + AINode* src = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, + _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), + _ai_char->_steering->_path_find_obj->_grid_size); + LVecBase3f _prev_position = _ai_char->_steering->_path_find_obj->_path_find_target.get_pos(_ai_char->_window_render); + AINode* dst = find_in_mesh(_ai_char->_steering->_path_find_obj->_nav_mesh, + _prev_position, _ai_char->_steering->_path_find_obj->_grid_size); + + return (src && dst); +} diff --git a/contrib/src/ai/pathFollow.h b/contrib/src/ai/pathFollow.h new file mode 100644 index 0000000000..70d454d6eb --- /dev/null +++ b/contrib/src/ai/pathFollow.h @@ -0,0 +1,50 @@ +// Filename: pathFollow.h +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef PATHFOLLOW_H +#define PATHFOLLOW_H + +#include "aiGlobals.h" +#include "aiCharacter.h" +#include "aiNode.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : PathFollow +// Description : This class handles all calls to the path follow +// behavior and has functions to handle pathfinding +//////////////////////////////////////////////////////////////////// +class PathFollow { + +public: + AICharacter *_ai_char; + float _follow_weight; + vector _path; + int _curr_path_waypoint; + bool _start; + NodePath _dummy; + string _type; + ClockObject *_myClock; + float _time; + + PathFollow(AICharacter *ai_ch, float follow_wt); + ~PathFollow(); + void add_to_path(LVecBase3f pos); + void start(string type); + void do_follow(); + bool check_if_possible(); +}; + +#endif diff --git a/contrib/src/ai/pursue.cxx b/contrib/src/ai/pursue.cxx new file mode 100644 index 0000000000..5ff8be0d29 --- /dev/null +++ b/contrib/src/ai/pursue.cxx @@ -0,0 +1,60 @@ +// Filename: pursue.cxx +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "pursue.h" + +Pursue::Pursue(AICharacter *ai_ch, NodePath target_object, float pursue_wt) { + _ai_char = ai_ch; + + _pursue_target = target_object; + _pursue_weight = pursue_wt; + + _pursue_done = false; +} + +Pursue::~Pursue() { +} + +//////////////////////////////////////////////////////////////////// +// Function: do_pursue +// Description: This function performs the pursue and returns a +// pursue force which is used +// in the calculate_prioritized function. +// In case the target has been reached it resets the +// forces to 0 so that the character stops. +// This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +LVecBase3f Pursue::do_pursue() { + assert(_pursue_target && "pursue target not assigned"); + + LVecBase3f present_pos = _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); + double target_distance = (_pursue_target.get_pos(_ai_char->_window_render) - + present_pos).length(); + + if (int(target_distance) == 0) { + _pursue_done = true; + _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->_pursue_force = LVecBase3f(0.0, 0.0, 0.0); + return (LVecBase3f(0.0, 0.0, 0.0)); + } + else { + _pursue_done = false; + } + + _pursue_direction = _pursue_target.get_pos(_ai_char->_window_render) - present_pos; + _pursue_direction.normalize(); + + LVecBase3f desired_force = _pursue_direction * _ai_char->_movt_force; + return (desired_force); +} diff --git a/contrib/src/ai/pursue.h b/contrib/src/ai/pursue.h new file mode 100644 index 0000000000..0a1f471b69 --- /dev/null +++ b/contrib/src/ai/pursue.h @@ -0,0 +1,42 @@ +// Filename: pursue.h +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef PURSUE_H +#define PURSUE_H + +#include "aiGlobals.h" +#include "aiCharacter.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : Pursue +// Description : This class handles all calls to the pursue +// behavior +//////////////////////////////////////////////////////////////////// +class Pursue { + +public: + AICharacter *_ai_char; + + NodePath _pursue_target; + float _pursue_weight; + LVecBase3f _pursue_direction; + bool _pursue_done; + + Pursue(AICharacter *ai_ch, NodePath target_object, float pursue_wt); + ~Pursue(); + LVecBase3f do_pursue(); +}; +#endif diff --git a/contrib/src/ai/seek.cxx b/contrib/src/ai/seek.cxx new file mode 100644 index 0000000000..9afaf91058 --- /dev/null +++ b/contrib/src/ai/seek.cxx @@ -0,0 +1,66 @@ +// Filename: seek.cxx +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "seek.h" + +Seek::Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt) { + _ai_char = ai_ch; + + _seek_position = target_object.get_pos(_ai_char->_window_render); + _seek_weight = seek_wt; + + _seek_direction = _seek_position - + _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); + _seek_direction.normalize(); + + _seek_done = false; +} + +Seek::Seek(AICharacter *ai_ch, LVecBase3f pos, float seek_wt) { + _ai_char = ai_ch; + + _seek_position = pos; + _seek_weight = seek_wt; + + _seek_direction = _seek_position - + _ai_char->_ai_char_np.get_pos(_ai_char->_window_render); + _seek_direction.normalize(); + + _seek_done = false; +} + +Seek::~Seek() { +} + +//////////////////////////////////////////////////////////////////// +// Function: do_seek +// Description: This function performs the seek and returns a seek +// force which is used +// in the calculate_prioritized function. +// This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +LVecBase3f Seek::do_seek() { + double target_distance = (_seek_position - + _ai_char->_ai_char_np.get_pos(_ai_char->_window_render)).length(); + + if (int(target_distance) == 0) { + _seek_done = true; + _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + _ai_char->_steering->turn_off("seek"); + return (LVecBase3f(0.0, 0.0, 0.0)); + } + + LVecBase3f desired_force = _seek_direction * _ai_char->_movt_force; + return (desired_force); +} diff --git a/contrib/src/ai/seek.h b/contrib/src/ai/seek.h new file mode 100644 index 0000000000..df40bfaac3 --- /dev/null +++ b/contrib/src/ai/seek.h @@ -0,0 +1,44 @@ +// Filename: seek.h +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef SEEK_H +#define SEEK_H + +#include "aiGlobals.h" +#include "aiCharacter.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : Seek +// Description : This class handles all calls to the seek behavior +//////////////////////////////////////////////////////////////////// +class Seek { + +public: + AICharacter *_ai_char; + + LVecBase3f _seek_position; + float _seek_weight; + LVecBase3f _seek_direction; + bool _seek_done; + LVecBase3f _seek_accum_force; + + Seek(AICharacter *ai_ch, NodePath target_object, float seek_wt = 1.0); + Seek(AICharacter *ai_ch, LVecBase3f pos, float seek_wt = 1.0); + ~Seek(); + LVecBase3f do_seek(); +}; + +#endif diff --git a/contrib/src/ai/wander.cxx b/contrib/src/ai/wander.cxx new file mode 100644 index 0000000000..e1bc7154ba --- /dev/null +++ b/contrib/src/ai/wander.cxx @@ -0,0 +1,140 @@ +// Filename: wander.cxx +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "wander.h" + +//////////////////////////////////////////////////////////////////// +// Function: rand_float +// Description: This function creates a random float point number +//////////////////////////////////////////////////////////////////// +double rand_float() { + const static double rand_max = 0x7fff; + return ((rand()) / (rand_max + 1.0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: random_clamped +// Description: This function returns a random floating point number +// in the range -1 to 1. +//////////////////////////////////////////////////////////////////// +double random_clamped() { + return (rand_float() - rand_float()); +} + +Wander::Wander(AICharacter *ai_ch, double wander_radius,int flag, + double aoe, float wander_weight) { + _ai_char = ai_ch; + _wander_radius = wander_radius ; + _wander_weight = wander_weight; + double theta = rand_float() * 2 * 3.14159; + double si = rand_float() * 3.14159; + _flag = flag; + // Area around which the character should wander + _area_of_effect = aoe; + _init_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render()); + // _flag is used by Wander to wander in a given axis + // Value 0 - XY axes wander + // Value 1 - YZ axes wander + // Value 2 - XZ axes wander + // Value 3 - XYZ axes wander + // default is XY axes + switch (_flag) { + case 0: { + _wander_target = LVecBase3f(_wander_radius * cos(theta), + _wander_radius * sin(theta),0); + break; + } + case 1: { + _wander_target = LVecBase3f(0, _wander_radius * cos(theta), + _wander_radius * sin(theta)); + break; + } + case 2: { + _wander_target = LVecBase3f(_wander_radius * cos(theta), 0, + _wander_radius * sin(theta)); + break; + } + case 3: { + _wander_target = LVecBase3f(_wander_radius * sin(theta) * cos(si), + _wander_radius * sin(theta) * sin(si), _wander_radius * cos(theta)); + break; + } + default: { + _wander_target = LVecBase3f(_wander_radius * cos(theta), + _wander_radius * sin(theta),0); + break; + } + } +} + +Wander::~Wander() { +} + +//////////////////////////////////////////////////////////////////// +// Function: do_wander +// Description: This function performs the wander and returns the +// wander force which is used +// in the calculate_prioritized function. +// This function is not to be used by the user. +//////////////////////////////////////////////////////////////////// +LVecBase3f Wander::do_wander() { + LVecBase3f present_pos = _ai_char->get_node_path().get_pos(_ai_char->get_char_render()); + // Create the random slices to enable random movement of wander + // for x,y,z respectively + double time_slice_1 = random_clamped() * 1.5; + double time_slice_2 = random_clamped() * 1.5; + double time_slice_3 = random_clamped() * 1.5; + switch (_flag) { + case 0: { + _wander_target += LVecBase3f(time_slice_1, time_slice_2, 0); + break; + } + case 1: { + _wander_target += LVecBase3f(0, time_slice_1, time_slice_2); + break; + } + case 2: { + _wander_target += LVecBase3f(time_slice_1, 0, time_slice_2); + break; + } + case 3: { + _wander_target += LVecBase3f(time_slice_1, time_slice_2, time_slice_3); + break; + } + + default: { + _wander_target = LVecBase3f(time_slice_1, time_slice_2, 0); + } + } + _wander_target.normalize(); + _wander_target *= _wander_radius; + LVecBase3f target = _ai_char->get_char_render().get_relative_vector(_ai_char->get_node_path(), LVector3f::forward()); + target.normalize(); + // Project wander target onto global space + target = _wander_target + target; + LVecBase3f desired_target = present_pos + target; + LVecBase3f desired_force = desired_target - _ai_char->get_node_path().get_pos() ; + desired_force.normalize(); + desired_force *= _ai_char->_movt_force; + double distance = (present_pos - _init_pos).length(); + if (_area_of_effect > 0 && distance > _area_of_effect) { + LVecBase3f direction = present_pos - _init_pos; + direction.normalize(); + desired_force = - direction * _ai_char->_movt_force; + LVecBase3f dirn = _ai_char->_steering->_steering_force; + dirn.normalize(); + _ai_char->_steering->_steering_force = LVecBase3f(0.0, 0.0, 0.0); + } + return desired_force; +} diff --git a/contrib/src/ai/wander.h b/contrib/src/ai/wander.h new file mode 100644 index 0000000000..e58ce23f13 --- /dev/null +++ b/contrib/src/ai/wander.h @@ -0,0 +1,40 @@ +// Filename: wander.h +// Created by: Deepak, John, Navin (24Oct09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised +// BSD license. You should have received a copy of this license +// along with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// +#ifndef WANDER_H +#define WANDER_H + +#include "aiCharacter.h" + +class AICharacter; + +//////////////////////////////////////////////////////////////////// +// Class : Wander +// Description : This class handles all calls to the wander behavior +//////////////////////////////////////////////////////////////////// +class Wander { +public: + AICharacter *_ai_char; + double _wander_radius; + LVecBase3f _wander_target; + float _wander_weight; + int _flag; + LVecBase3f _init_pos; + double _area_of_effect; + + Wander(AICharacter *ai_ch, double wander_radius, int flag, double aoe, float wander_weight); + LVecBase3f do_wander(); + ~Wander(); +}; + +#endif diff --git a/contrib/src/contribbase/contribbase.h b/contrib/src/contribbase/contribbase.h new file mode 100644 index 0000000000..037846da2a --- /dev/null +++ b/contrib/src/contribbase/contribbase.h @@ -0,0 +1,26 @@ +/* Filename: contribbase.h + * Created by: rdb (30Dec09) + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* This file is included at the beginning of every header file and/or + C or C++ file. It must be compilable for C as well as C++ files, + so no C++-specific code or syntax can be put here. */ + +#ifndef CONTRIBBASE_H +#define CONTRIBBASE_H + +#include "pandabase.h" +#include "contribsymbols.h" + +#endif + diff --git a/contrib/src/contribbase/contribsymbols.h b/contrib/src/contribbase/contribsymbols.h new file mode 100644 index 0000000000..e55c535331 --- /dev/null +++ b/contrib/src/contribbase/contribsymbols.h @@ -0,0 +1,42 @@ +/* Filename: contribsymbols.h + * Created by: rdb (30Dec09) + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef CONTRIBSYMBOLS_H +#define CONTRIBSYMBOLS_H + +/* See dtoolsymbols.h for a rant on the purpose of this file. */ + +/* Note that the symbols declared in this file appear in alphabetical + order. Also note that we must use C-style comments only here, not + C++-style comments, since this file is occasionally included by a C + file. */ + +#if defined(WIN32_VC) && !defined(CPPPARSER) && !defined(LINK_ALL_STATIC) + +#ifdef BUILDING_PANDAAI + #define EXPCL_PANDAAI __declspec(dllexport) + #define EXPTP_PANDAAI +#else + #define EXPCL_PANDAAI __declspec(dllimport) + #define EXPTP_PANDAAI extern +#endif + +#else /* !WIN32_VC */ + +#define EXPCL_PANDAAI +#define EXPTP_PANDAAI + +#endif /* WIN32_VC */ + +#endif