载入中...
搜索中...
未找到
BirdSolver.cpp
浏览该文件的文档.
2
4
5#include "common/Assert.h"
6
7#include <algorithm>
8#include <cmath>
9
10namespace eve::gpuagents {
11
12void BirdSolver::step(std::span<const AgentState> read, std::span<AgentState> write, const EffectProfile& profile,
13 const EnvironmentSnapshot& env, float dt) {
15 EV_PARAM_CHECK(read.size() == write.size());
16 const BirdProfile& p = profile.bird;
17
18 for (size_t i = 0; i < read.size(); ++i) {
19 const AgentState& in = read[i];
20 if (in.alive == 0) {
21 write[i] = in;
22 continue;
23 }
24
25 glm::vec3 forward = in.velocity;
26 if (glm::length(forward) < 1e-4f) forward = glm::vec3(0.f, 0.f, 1.f);
27 else
28 forward = glm::normalize(forward);
29
30 const auto acc = detail::gatherNeighbors(read, static_cast<int>(i), forward, p.separationRadius,
31 p.cohesionRadius, p.alignmentRadius, p.perceptionFovDegrees,
32 p.maxNeighborSamples);
33
34 glm::vec3 steer(0.f);
35 if (acc.sepWeightSum > 0.f) steer += (acc.separation / acc.sepWeightSum) * p.separationWeight;
36 if (acc.cohWeightSum > 0.f) {
37 const glm::vec3 center = acc.cohesionPos / acc.cohWeightSum;
38 const glm::vec3 toC = center - in.position;
39 const float len = glm::length(toC);
40 if (len > 1e-4f) steer += (toC / len) * p.cohesionWeight;
41 }
42 if (acc.aliWeightSum > 0.f) {
43 steer += (acc.alignmentVel / acc.aliWeightSum - in.velocity) * p.alignmentWeight;
44 }
45 steer += detail::markerForce(in.position, env.goals, true) * p.goalWeight;
46
47 // Wind response (blend toward uniform + gust).
48 const glm::vec3 windTarget = p.uniformWindLocal + env.windVelocity;
49 const float windAlpha = 1.f - std::exp(-dt / std::max(p.windResponseTime, 1e-3f));
50 glm::vec3 windVel = glm::mix(glm::vec3(in.customData[1], in.customData[2], in.customData[3]),
51 windTarget, windAlpha);
52
53 // Flight dynamics.
54 glm::vec3 vel = in.velocity;
55 float speed = glm::length(vel);
56 if (speed < 1e-4f) {
57 vel = forward * p.cruiseSpeed;
58 speed = p.cruiseSpeed;
59 }
60 const glm::vec3 dir = vel / speed;
61
62 const glm::vec3 relAir = vel - windVel;
63 const float airSpeed = std::max(glm::length(relAir), 1e-3f);
64 const glm::vec3 liftDir =
65 glm::normalize(glm::cross(glm::cross(dir, glm::vec3(0.f, 1.f, 0.f)), dir) + glm::vec3(0.f, 1e-4f, 0.f));
66 const float liftMag = p.liftCoefficient * airSpeed * airSpeed * 0.05f;
67 glm::vec3 accel = steer + liftDir * liftMag;
68 accel += glm::vec3(0.f, -9.8f * p.gravityScale, 0.f);
69 accel -= relAir * (p.airDrag * airSpeed);
70 accel += glm::normalize(windTarget - windVel + glm::vec3(1e-5f, 0.f, 0.f)) * p.gustAcceleration * 0.1f;
71
72 // Stall recovery: pitch up when below stall speed.
73 if (speed < p.stallSpeed) {
74 accel += glm::vec3(0.f, (p.stallSpeed - speed) * 4.f, 0.f);
75 accel += dir * ((p.cruiseSpeed - speed) * 2.f);
76 }
77
78 // Predictive clearance vs ground / ceiling.
79 const float ttc = p.timeToCollision;
80 const float predY = in.position.y + vel.y * ttc;
81 if (predY < env.groundY + p.groundClearance) {
82 accel.y += (env.groundY + p.groundClearance - predY) * 6.f;
83 }
84 if (predY > env.ceilingY - p.ceilingClearance) {
85 accel.y -= (predY - (env.ceilingY - p.ceilingClearance)) * 6.f;
86 }
87
88 vel += accel * dt;
89 speed = glm::length(vel);
90 if (speed > 1e-6f) {
91 // Turn-rate limit.
92 glm::vec3 newDir = vel / speed;
93 const float ang = std::acos(std::clamp(glm::dot(dir, newDir), -1.f, 1.f));
94 const float maxA = p.maxTurnRate * dt;
95 if (ang > maxA && ang > 1e-5f) newDir = glm::normalize(glm::mix(dir, newDir, maxA / ang));
96 speed = std::clamp(speed, p.stallSpeed * 0.5f, p.maxSpeed);
97 vel = newDir * speed;
98 vel.y = std::clamp(vel.y, -p.maxDescentSpeed, p.maxClimbSpeed);
99 }
100
101 // Bank from horizontal turn (stored in customData[0]).
102 glm::vec3 prevH = glm::normalize(glm::vec3(dir.x, 0.f, dir.z) + glm::vec3(1e-5f, 0.f, 0.f));
103 glm::vec3 newH = glm::normalize(glm::vec3(vel.x, 0.f, vel.z) + glm::vec3(1e-5f, 0.f, 0.f));
104 const float crossY = prevH.x * newH.z - prevH.z * newH.x;
105 float bank = std::clamp(crossY * 4.f, -p.maxBankAngle, p.maxBankAngle);
106
107 AgentState out = in;
108 out.velocity = vel;
109 out.position = in.position + vel * dt;
110 out.rotation = lookRotation(vel, glm::vec3(std::sin(bank), std::cos(bank), 0.f));
111 out.age = in.age + dt;
112 out.customData[0] = bank;
113 out.customData[1] = windVel.x;
114 out.customData[2] = windVel.y;
115 out.customData[3] = windVel.z;
116 out.alive = in.alive;
117
118 if (env.obstacles) {
119 env.obstacles->resolve(out.position, out.velocity, p.base.agentRadius, p.base.obstaclePredictTime);
120 }
121 write[i] = out;
122 }
123}
124
125} // namespace eve::gpuagents
EVEngine assertion entry point, backed by zeroerr.
#define EV_PARAM_CHECK(cond,...)
Validate a function parameter / public API precondition.
Definition Assert.h:30
float ang
glm::vec4 p[6]
V3 dir
Definition TreeMesh.cpp:150
void step(std::span< const AgentState > read, std::span< AgentState > write, const EffectProfile &profile, const EnvironmentSnapshot &env, float dt) override
Step.
void resolve(glm::vec3 &position, glm::vec3 &velocity, float agentRadius, float predictTime) const
Push an agent out of obstacles and keep feasible tangential velocity.
glm::vec3 markerForce(const glm::vec3 &pos, const std::vector< EnvironmentMarker > &markers, bool attract)
Marker force.
Definition BoidsCommon.h:76
NeighborAccum gatherNeighbors(std::span< const AgentState > agents, int selfIndex, const glm::vec3 &forward, float sepR, float cohR, float aliR, float fovDeg, int maxSamples)
Gather neighborhood forces with compact weights and FOV / sample caps.
Definition BoidsCommon.h:46
glm::quat lookRotation(const glm::vec3 &forward, const glm::vec3 &upHint=glm::vec3(0.f, 1.f, 0.f))
Builds a facing quaternion from a unit forward and approximate up.
Definition AgentState.h:36
Standardized per-agent simulation output shared by all effect kinds.
Definition AgentState.h:12
glm::quat rotation
wxyz storage (glm default).
Definition AgentState.h:15
std::uint32_t alive
0 = recycled / inactive slot.
Definition AgentState.h:18
Bird flock flight-dynamics parameters.
Type-erased profile bundle owned by an EffectBackend.
Read-only environment semantics prepared by GpuAgentWorld each tick.
std::vector< EnvironmentMarker > goals