X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fai%2Fai.cpp;h=4b76856ea9664db0bb5dc17681155bfe501090be;hb=f5de855fbd4bf5b0df1cad950cbe9069e41369ca;hp=ff39127151332dc544f23ae6784f719ec7d6bd6c;hpb=8e9e2bb4b2dd5a4100f4531628ab58002fe253c1;p=blank.git diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index ff39127..4b76856 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -4,7 +4,8 @@ #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" @@ -36,6 +37,9 @@ AIController::AIController(World &world, GaloisLFSR &rand) , 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) @@ -73,6 +77,16 @@ void AIController::Update(Entity &e, float dt) { 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, @@ -87,9 +101,13 @@ void AIController::Update(Entity &e, float dt) { 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); } } @@ -98,6 +116,11 @@ glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &st 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; @@ -146,7 +169,7 @@ Player *AIController::ClosestVisiblePlayer(const Entity &e) noexcept { // LOS test, assumes all entities are see-through WorldCollision col; - if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth < dist) { + if (world.Intersection(aim, reference, col) && col.depth < dist) { continue; } @@ -166,7 +189,7 @@ bool AIController::LineOfSight(const Entity &from, const Entity &to) const noexc return false; } WorldCollision col; - if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth < dist) { + if (world.Intersection(aim, reference, col) && col.depth < dist) { return false; } return true; @@ -223,6 +246,63 @@ glm::vec3 AIController::GetHaltForce(const Entity &, const EntityState &state) c 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 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::infinity(); + for (WorldCollision &c : col) { + // diff points from block to state + glm::vec3 diff = state.RelativePosition(c.ChunkPos()) - c.BlockCoords(); + float dist = length2(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 { @@ -349,7 +429,7 @@ glm::vec3 AIController::GetEvadeForce(const Entity &, const EntityState &state) 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); } @@ -395,7 +475,7 @@ glm::vec3 AIController::GetPursuitForce(const Entity &, const EntityState &state 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); } @@ -450,7 +530,7 @@ void ChaseState::Update(AIController &ctrl, Entity &e, float dt) const { return; } // halt if we're close enough, flee if we're too close - float dist_sq = length_squared(e.AbsoluteDifference(ctrl.GetPursuitTarget())); + float dist_sq = length2(e.AbsoluteDifference(ctrl.GetPursuitTarget())); if (dist_sq < 8.0f) { ctrl.SetFleeTarget(ctrl.GetPursuitTarget()); ctrl.SetState(flee);