#include "IdleState.hpp"
#include "RoamState.hpp"
-#include "../model/geometry.hpp"
+#include "../geometry/distance.hpp"
+#include "../geometry/rotation.hpp"
#include "../rand/GaloisLFSR.hpp"
#include "../world/Entity.hpp"
#include "../world/World.hpp"
, decision_timer(1.0f)
, halted(false)
, halt_speed(1.0f)
+, avoid_obstacles(true)
+, obstacle_box{ glm::vec3(0.0f), glm::vec3(0.0f) }
+, obstacle_transform(1.0f)
, fleeing(false)
, flee_target(nullptr)
, flee_speed(5.0f)
decision_timer.Update(dt);
state->Update(*this, e, dt);
+ if (avoid_obstacles && e.Moving()) {
+ obstacle_box = e.Bounds();
+ obstacle_box.min.z = -e.Speed();
+ obstacle_box.max.x = 0.0f;
+ // our box is oriented for -Z velocity
+ obstacle_transform = glm::mat4(find_rotation(glm::vec3(0.0f, 0.0f, -1.0f), e.Heading()));
+ // and positioned relative to the entity's chunk
+ obstacle_transform[3] = glm::vec4(e.GetState().pos.block, 1.0f);
+ }
+
if (wandering) {
glm::vec3 displacement(
random.SNorm() * wander_disp,
if (e.Moving()) {
// orient head towards heading
glm::vec3 heading(e.Heading());
- float tgt_pitch = std::atan(heading.y / length(glm::vec2(heading.x, heading.z)));
- float tgt_yaw = std::atan2(-heading.x, -heading.z);
+ // only half pitch, so we don't crane our neck
+ float tgt_pitch = std::atan(heading.y / length(glm::vec2(heading.x, heading.z))) * 0.5f;
+ // always look straight ahead
+ // maybe look at the pursuit target if there is one
+ float tgt_yaw = 0.0f;
e.SetHead(tgt_pitch, tgt_yaw);
+ e.OrientBody(dt);
}
}
return GetHaltForce(entity, state);
}
glm::vec3 force(0.0f);
+ if (IsAvoidingObstacles() && entity.Moving()) {
+ if (MaxOutForce(force, GetObstacleAvoidanceForce(entity, state), entity.MaxControlForce())) {
+ return force;
+ }
+ }
if (IsFleeing()) {
if (MaxOutForce(force, GetFleeForce(entity, state), entity.MaxControlForce())) {
return force;
return Halt(state, halt_speed);
}
+// obstacle avoidance
+
+void AIController::StartAvoidingObstacles() noexcept {
+ avoid_obstacles = true;
+}
+
+void AIController::StopAvoidingObstacles() noexcept {
+ avoid_obstacles = false;
+}
+
+bool AIController::IsAvoidingObstacles() const noexcept {
+ return avoid_obstacles;
+}
+
+namespace {
+
+std::vector<WorldCollision> col;
+
+}
+
+glm::vec3 AIController::GetObstacleAvoidanceForce(const Entity &e, const EntityState &state) const noexcept {
+ if (!e.Moving()) {
+ return glm::vec3(0.0f);
+ }
+ col.clear();
+ if (!world.Intersection(obstacle_box, obstacle_transform, e.ChunkCoords(), col)) {
+ return glm::vec3(0.0f);
+ }
+ // find the nearest block
+ WorldCollision *nearest = nullptr;
+ glm::vec3 difference(0.0f);
+ float distance = std::numeric_limits<float>::infinity();
+ for (WorldCollision &c : col) {
+ // diff points from block to state
+ glm::vec3 diff = state.RelativePosition(c.ChunkPos()) - c.BlockCoords();
+ float dist = length_squared(diff);
+ if (dist < distance) {
+ nearest = &c;
+ difference = diff;
+ distance = dist;
+ }
+ }
+ if (!nearest) {
+ // intersection test lied to us
+ return glm::vec3(0.0f);
+ }
+ // and steer away from it
+ // to_go is the distance between our position and the
+ // point on the "velocity ray" closest to obstacle
+ float to_go = dot(difference, e.Heading());
+ // point is our future position if we keep going our way
+ glm::vec3 point(e.GetState().pos.block + e.Heading() * to_go);
+ // now steer away in the direction of (point - block)
+ // with a magniture proportional to speed/distance
+ return normalize(point - nearest->BlockCoords()) * (e.Speed() / std::sqrt(distance));
+}
+
// flee
void AIController::StartFleeing() noexcept {
glm::vec3 cur_diff(state.Diff(GetEvadeTarget().GetState()));
float time_estimate = length(cur_diff) / evade_speed;
EntityState pred_state(GetEvadeTarget().GetState());
- pred_state.block_pos += pred_state.velocity * time_estimate;
+ pred_state.pos.block += pred_state.velocity * time_estimate;
return Flee(state, pred_state, evade_speed, 2.0f);
}
glm::vec3 cur_diff(state.Diff(GetPursuitTarget().GetState()));
float time_estimate = length(cur_diff) / pursuit_speed;
EntityState pred_state(GetPursuitTarget().GetState());
- pred_state.block_pos += pred_state.velocity * time_estimate;
+ pred_state.pos.block += pred_state.velocity * time_estimate;
return Seek(state, pred_state, pursuit_speed, 2.0f);
}