glm::vec3 ControlForce(const Entity &, const EntityState &) const override;
- static glm::vec3 Heading(const EntityState &) noexcept;
-
/// get the closest player that given entity can see
/// returns nullptr if none are in sight
Player *ClosestVisiblePlayer(const Entity &) noexcept;
void ExitHalt() noexcept;
bool IsHalted() const noexcept;
void SetHaltSpeed(float) noexcept;
- glm::vec3 GetHaltForce(const EntityState &) const noexcept;
+ glm::vec3 GetHaltForce(const Entity &, const EntityState &) const noexcept;
void StartFleeing() noexcept;
void StopFleeing() noexcept;
void SetFleeSpeed(float) noexcept;
Entity &GetFleeTarget() noexcept;
const Entity &GetFleeTarget() const noexcept;
- glm::vec3 GetFleeForce(const EntityState &) const noexcept;
+ glm::vec3 GetFleeForce(const Entity &, const EntityState &) const noexcept;
void StartSeeking() noexcept;
void StopSeeking() noexcept;
void SetSeekSpeed(float) noexcept;
Entity &GetSeekTarget() noexcept;
const Entity &GetSeekTarget() const noexcept;
- glm::vec3 GetSeekForce(const EntityState &) const noexcept;
+ glm::vec3 GetSeekForce(const Entity &, const EntityState &) const noexcept;
void StartEvading() noexcept;
void StopEvading() noexcept;
void SetEvadeSpeed(float) noexcept;
Entity &GetEvadeTarget() noexcept;
const Entity &GetEvadeTarget() const noexcept;
- glm::vec3 GetEvadeForce(const EntityState &) const noexcept;
+ glm::vec3 GetEvadeForce(const Entity &, const EntityState &) const noexcept;
void StartPursuing() noexcept;
void StopPursuing() noexcept;
void SetPursuitSpeed(float) noexcept;
Entity &GetPursuitTarget() noexcept;
const Entity &GetPursuitTarget() const noexcept;
- glm::vec3 GetPursuitForce(const EntityState &) const noexcept;
+ glm::vec3 GetPursuitForce(const Entity &, const EntityState &) const noexcept;
/// start wandering randomly
void StartWandering() noexcept;
float radius = 1.0f,
float displacement = 1.0f
) noexcept;
- glm::vec3 GetWanderForce(const EntityState &) const noexcept;
+ glm::vec3 GetWanderForce(const Entity &, const EntityState &) const noexcept;
private:
World &world;
if (e.Moving()) {
// orient head towards heading
- glm::vec3 heading(Heading(e.GetState()));
+ 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);
e.SetHead(tgt_pitch, tgt_yaw);
glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const {
if (IsHalted()) {
- return GetHaltForce(state);
+ return GetHaltForce(entity, state);
}
glm::vec3 force(0.0f);
if (IsFleeing()) {
- if (MaxOutForce(force, GetFleeForce(state), entity.MaxControlForce())) {
+ if (MaxOutForce(force, GetFleeForce(entity, state), entity.MaxControlForce())) {
return force;
}
}
if (IsSeeking()) {
- if (MaxOutForce(force, GetSeekForce(state), entity.MaxControlForce())) {
+ if (MaxOutForce(force, GetSeekForce(entity, state), entity.MaxControlForce())) {
return force;
}
}
if (IsEvading()) {
- if (MaxOutForce(force, GetEvadeForce(state), entity.MaxControlForce())) {
+ if (MaxOutForce(force, GetEvadeForce(entity, state), entity.MaxControlForce())) {
return force;
}
}
if (IsPursuing()) {
- if (MaxOutForce(force, GetPursuitForce(state), entity.MaxControlForce())) {
+ if (MaxOutForce(force, GetPursuitForce(entity, state), entity.MaxControlForce())) {
return force;
}
}
if (IsWandering()) {
- if (MaxOutForce(force, GetWanderForce(state), entity.MaxControlForce())) {
+ if (MaxOutForce(force, GetWanderForce(entity, state), entity.MaxControlForce())) {
return force;
}
}
return force;
}
-glm::vec3 AIController::Heading(const EntityState &state) noexcept {
- if (dot(state.velocity, state.velocity) > std::numeric_limits<float>::epsilon()) {
- return normalize(state.velocity);
- } else {
- float cp = std::cos(state.pitch);
- return glm::vec3(std::cos(state.yaw) * cp, std::sin(state.yaw) * cp, std::sin(state.pitch));
- }
-}
-
Player *AIController::ClosestVisiblePlayer(const Entity &e) noexcept {
Player *target = nullptr;
float distance = sight_dist;
// distance test
const glm::vec3 diff(pe.AbsoluteDifference(e));
- float dist = length_squared(diff);
+ float dist = length(diff);
if (dist > distance) continue;
// FOV test, 45° in each direction
- if (dot(normalize(diff), aim.dir) < sight_angle) {
+ if (dot(diff / dist, aim.dir) < sight_angle) {
continue;
}
// LOS test, assumes all entities are see-through
WorldCollision col;
- if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth * col.depth < dist) {
+ if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth < dist) {
continue;
}
halt_speed = speed;
}
-glm::vec3 AIController::GetHaltForce(const EntityState &state) const noexcept {
+glm::vec3 AIController::GetHaltForce(const Entity &, const EntityState &state) const noexcept {
return Halt(state, halt_speed);
}
return *flee_target;
}
-glm::vec3 AIController::GetFleeForce(const EntityState &state) const noexcept {
+glm::vec3 AIController::GetFleeForce(const Entity &, const EntityState &state) const noexcept {
return Flee(state, GetFleeTarget().GetState(), flee_speed, 2.0f);
}
return *seek_target;
}
-glm::vec3 AIController::GetSeekForce(const EntityState &state) const noexcept {
+glm::vec3 AIController::GetSeekForce(const Entity &, const EntityState &state) const noexcept {
return Seek(state, GetSeekTarget().GetState(), seek_speed, 2.0f);
}
return *evade_target;
}
-glm::vec3 AIController::GetEvadeForce(const EntityState &state) const noexcept{
+glm::vec3 AIController::GetEvadeForce(const Entity &, const EntityState &state) const noexcept{
glm::vec3 cur_diff(state.Diff(GetEvadeTarget().GetState()));
float time_estimate = length(cur_diff) / evade_speed;
EntityState pred_state(GetEvadeTarget().GetState());
return *pursuit_target;
}
-glm::vec3 AIController::GetPursuitForce(const EntityState &state) const noexcept {
+glm::vec3 AIController::GetPursuitForce(const Entity &, const EntityState &state) const noexcept {
glm::vec3 cur_diff(state.Diff(GetPursuitTarget().GetState()));
float time_estimate = length(cur_diff) / pursuit_speed;
EntityState pred_state(GetPursuitTarget().GetState());
wander_disp = displacement;
}
-glm::vec3 AIController::GetWanderForce(const EntityState &state) const noexcept {
- glm::vec3 wander_target(normalize(Heading(state) * wander_dist + wander_pos) * wander_speed);
+glm::vec3 AIController::GetWanderForce(const Entity &e, const EntityState &state) const noexcept {
+ glm::vec3 wander_target(normalize(e.Heading() * wander_dist + wander_pos) * wander_speed);
return TargetVelocity(wander_target, state, 0.5f);
}
vector<WorldCollision> col;
while (entry != end) {
- replay.Velocity(entry->state.velocity);
SetMovement(entry->movement);
GetWorld().Update(replay, entry->delta_t);
entry->state.chunk_pos = replay.GetState().chunk_pos;
glm::vec3 ControlForce(const EntityState &) const noexcept;
const glm::vec3 &Velocity() const noexcept { return state.velocity; }
- void Velocity(const glm::vec3 &v) noexcept { state.velocity = v; }
bool Moving() const noexcept {
return dot(Velocity(), Velocity()) > std::numeric_limits<float>::epsilon();
/// orientation of local coordinate system
const glm::quat &Orientation() const noexcept { return state.orient; }
- void Orientation(const glm::quat &o) noexcept { state.orient = o; }
/// orientation of head within local coordinate system, in radians
float Pitch() const noexcept { return state.pitch; }
/// get a ray in entity's face direction originating from center of vision
Ray Aim(const Chunk::Pos &chunk_offset) const noexcept;
+ const glm::vec3 &Heading() const noexcept { return heading; }
+
void SetState(const EntityState &s) noexcept { state = s; UpdateModel(); }
const EntityState &GetState() const noexcept { return state; }
private:
void UpdateModel() noexcept;
+ void UpdateView() noexcept;
+ void UpdateHeading() noexcept;
private:
EntityController *ctrl;
AABB bounds;
EntityState state;
+ /// local transform of eyes
+ /// if this entity has no model, the eyes are assumed to
+ /// be at local origin and oriented towards -Z
+ glm::mat4 view_local;
+ /// normalized velocity or heading if standing still
+ glm::vec3 heading;
+
// TODO: I'd prefer a drag solution
float max_vel;
float max_force;
, name("anonymous")
, bounds()
, state()
+, heading(0.0f, 0.0f, -1.0f)
, max_vel(5.0f)
, max_force(25.0f)
, ref_count(0)
}
glm::mat4 Entity::ViewTransform(const glm::ivec3 &reference) const noexcept {
- glm::mat4 transform = Transform(reference);
- if (model) {
- transform *= model.EyesTransform();
- }
+ glm::mat4 transform = view_local;
+ transform[3] += glm::vec4(state.RelativePosition(reference), 0.0f);
return transform;
}
Ray Entity::Aim(const Chunk::Pos &chunk_offset) const noexcept {
glm::mat4 transform = ViewTransform(chunk_offset);
- glm::vec4 from = transform * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
- from /= from.w;
- glm::vec4 to = transform * glm::vec4(0.0f, 0.0f, -1.0f, 1.0f);
- to /= to.w;
- return Ray{ glm::vec3(from), glm::normalize(glm::vec3(to - from)) };
+ return Ray{ glm::vec3(transform[3]), -glm::vec3(transform[2]) };
}
void Entity::UpdateModel() noexcept {
}
void Entity::Update(float dt) {
+ UpdateView();
+ UpdateHeading();
if (HasController()) {
GetController().Update(*this, dt);
}
}
+void Entity::UpdateView() noexcept {
+ // create local transform
+ view_local = Transform(ChunkCoords());
+ // clear the translation part
+ view_local[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
+ // add the model's eyes translation, if any
+ if (model) {
+ view_local *= model.EyesTransform();
+ }
+}
+
+void Entity::UpdateHeading() noexcept {
+ if (Moving()) {
+ heading = normalize(Velocity());
+ } else {
+ // use -Z (forward axis) of local view transform
+ heading = -glm::vec3(view_local[2]);
+ }
+}
+
EntityController::~EntityController() {
}
void EntityState::AdjustHeading() noexcept {
- while (pitch > PI / 2) {
- pitch = PI / 2;
- }
- while (pitch < -PI / 2) {
- pitch = -PI / 2;
- }
+ glm::clamp(pitch, -PI_0p5, PI_0p5);
while (yaw > PI) {
- yaw -= PI * 2;
+ yaw -= PI_2p0;
}
while (yaw < -PI) {
- yaw += PI * 2;
+ yaw += PI_2p0;
}
}
glm::mat4 EntityState::Transform(const glm::ivec3 &reference) const noexcept {
const glm::vec3 translation = RelativePosition(reference);
glm::mat4 transform(toMat4(orient));
- transform[3].x = translation.x;
- transform[3].y = translation.y;
- transform[3].z = translation.z;
+ transform[3] = glm::vec4(translation, 1.0f);
return transform;
}