mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-23 12:26:59 +01:00
202 lines
6.9 KiB
C++
202 lines
6.9 KiB
C++
/*************************************************************************/
|
|
/* gsai_path.cpp */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* PANDEMONIUM ENGINE */
|
|
/* https://github.com/Relintai/pandemonium_engine */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2022-present Péter Magyar. */
|
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
/* a copy of this software and associated documentation files (the */
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
/* the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be */
|
|
/* included in all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
/*************************************************************************/
|
|
|
|
#include "gsai_path.h"
|
|
|
|
bool GSAIPath::get_is_open() const {
|
|
return is_open;
|
|
}
|
|
|
|
void GSAIPath::set_is_open(const bool val) {
|
|
is_open = val;
|
|
}
|
|
|
|
float GSAIPath::get_length() const {
|
|
return length;
|
|
}
|
|
|
|
void GSAIPath::set_length(const float val) {
|
|
length = val;
|
|
}
|
|
|
|
void GSAIPath::initialize(const PoolVector3Array &waypoints, const bool _is_open) {
|
|
is_open = _is_open;
|
|
|
|
create_path(waypoints);
|
|
|
|
if (waypoints.size() > 0) {
|
|
_nearest_point_on_segment = waypoints[0];
|
|
_nearest_point_on_path = waypoints[0];
|
|
} else {
|
|
_nearest_point_on_segment = Vector3();
|
|
_nearest_point_on_path = Vector3();
|
|
}
|
|
}
|
|
|
|
void GSAIPath::create_path(const PoolVector3Array &waypoints) {
|
|
ERR_FAIL_COND_MSG(waypoints.size() < 2, "Waypoints must contain at least two (2) waypoints.");
|
|
|
|
_segments.clear();
|
|
length = 0;
|
|
|
|
Vector3 current = waypoints[0];
|
|
Vector3 previous;
|
|
|
|
for (int i = 1; i < waypoints.size(); ++i) {
|
|
previous = current;
|
|
|
|
if (i < waypoints.size()) {
|
|
current = waypoints[i];
|
|
} else if (is_open) {
|
|
break;
|
|
} else {
|
|
current = waypoints[0];
|
|
}
|
|
|
|
GSAISegment segment(previous, current);
|
|
length += segment.length;
|
|
segment.cumulative_length = length;
|
|
_segments.push_back(segment);
|
|
}
|
|
}
|
|
|
|
float GSAIPath::calculate_distance(const Vector3 &agent_current_position) {
|
|
if (_segments.size() == 0) {
|
|
return 0;
|
|
}
|
|
|
|
float smallest_distance_squared = Math_INF;
|
|
const GSAISegment *nearest_segment = NULL;
|
|
|
|
for (int i = 0; i < _segments.size(); ++i) {
|
|
const GSAISegment &segment = _segments[i];
|
|
float distance_squared = _calculate_point_segment_distance_squared(segment.begin, segment.end, agent_current_position);
|
|
|
|
if (distance_squared < smallest_distance_squared) {
|
|
_nearest_point_on_path = _nearest_point_on_segment;
|
|
smallest_distance_squared = distance_squared;
|
|
nearest_segment = &segment;
|
|
}
|
|
}
|
|
|
|
float length_on_path = nearest_segment->cumulative_length - _nearest_point_on_path.distance_to(nearest_segment->end);
|
|
|
|
return length_on_path;
|
|
}
|
|
|
|
Vector3 GSAIPath::calculate_target_position(float target_distance) {
|
|
if (_segments.size() == 0) {
|
|
return Vector3();
|
|
}
|
|
|
|
if (is_open) {
|
|
target_distance = CLAMP(target_distance, 0, length);
|
|
} else {
|
|
if (target_distance < 0) {
|
|
target_distance = length + fmod(target_distance, length);
|
|
} else if (target_distance > length) {
|
|
target_distance = fmod(target_distance, length);
|
|
}
|
|
}
|
|
|
|
const GSAISegment *desired_segment = NULL;
|
|
|
|
for (int i = 0; i < _segments.size(); ++i) {
|
|
const GSAISegment &segment = _segments[i];
|
|
|
|
if (segment.cumulative_length >= target_distance) {
|
|
desired_segment = &segment;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!desired_segment) {
|
|
desired_segment = &_segments[_segments.size() - 1];
|
|
}
|
|
|
|
float distance = desired_segment->cumulative_length - target_distance;
|
|
return (((desired_segment->begin - desired_segment->end) * (distance / desired_segment->length)) + desired_segment->end);
|
|
}
|
|
|
|
Vector3 GSAIPath::get_start_point() {
|
|
if (_segments.size() == 0) {
|
|
return Vector3();
|
|
}
|
|
|
|
return _segments[0].begin;
|
|
}
|
|
|
|
Vector3 GSAIPath::get_end_point() {
|
|
if (_segments.size() == 0) {
|
|
return Vector3();
|
|
}
|
|
|
|
return _segments[_segments.size() - 1].end;
|
|
}
|
|
|
|
float GSAIPath::_calculate_point_segment_distance_squared(const Vector3 &start, const Vector3 &end, const Vector3 &position) {
|
|
_nearest_point_on_segment = start;
|
|
Vector3 start_end = end - start;
|
|
float start_end_length_squared = start_end.length_squared();
|
|
|
|
if (start_end_length_squared != 0) {
|
|
float t = (position - start).dot(start_end) / start_end_length_squared;
|
|
_nearest_point_on_segment += start_end * CLAMP(t, 0, 1);
|
|
}
|
|
|
|
return _nearest_point_on_segment.distance_squared_to(position);
|
|
}
|
|
|
|
GSAIPath::GSAIPath() {
|
|
is_open = false;
|
|
length = 0;
|
|
}
|
|
|
|
GSAIPath::~GSAIPath() {
|
|
}
|
|
|
|
void GSAIPath::_bind_methods() {
|
|
ClassDB::bind_method(D_METHOD("get_is_open"), &GSAIPath::get_is_open);
|
|
ClassDB::bind_method(D_METHOD("set_is_open", "value"), &GSAIPath::set_is_open);
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_open"), "set_is_open", "get_is_open");
|
|
|
|
ClassDB::bind_method(D_METHOD("get_length"), &GSAIPath::get_length);
|
|
ClassDB::bind_method(D_METHOD("set_length", "value"), &GSAIPath::set_length);
|
|
ADD_PROPERTY(PropertyInfo(Variant::REAL, "length"), "set_length", "get_length");
|
|
|
|
ClassDB::bind_method(D_METHOD("initialize", "waypoints", "is_open"), &GSAIPath::initialize, false);
|
|
ClassDB::bind_method(D_METHOD("create_path", "waypoints"), &GSAIPath::create_path);
|
|
ClassDB::bind_method(D_METHOD("calculate_distance", "agent_current_position"), &GSAIPath::calculate_distance);
|
|
ClassDB::bind_method(D_METHOD("calculate_target_position", "target_distance"), &GSAIPath::calculate_target_position);
|
|
ClassDB::bind_method(D_METHOD("get_start_point"), &GSAIPath::get_start_point);
|
|
ClassDB::bind_method(D_METHOD("get_end_point"), &GSAIPath::get_end_point);
|
|
}
|