载入中...
搜索中...
未找到
CrowdSimulation.cpp
浏览该文件的文档.
2
3namespace eve::crowd {
4namespace {
5enum Action : int32_t { kIdle = 0, kFlow = 1, kSeek = 2, kBoids = 3 };
6float wrapPi(float a) {
7 const float kTwoPi = 6.28318530717958647692f;
8 while (a > 3.14159265358979323846f) a -= kTwoPi;
9 while (a < -3.14159265358979323846f) a += kTwoPi;
10 return a;
11}
12
13} // namespace
14
16 const int n = int(actions.size());
17 if (n == 0) {
18 gridW = gridH = 0;
19 return;
20 }
21 gridCell = std::max(std::max(sepRadius, perceiveRadius), 1.f);
22 float minX = xs[0], maxX = xs[0], minY = ys[0], maxY = ys[0];
23 for (int i = 1; i < n; ++i) {
24 minX = std::min(minX, xs[size_t(i)]);
25 maxX = std::max(maxX, xs[size_t(i)]);
26 minY = std::min(minY, ys[size_t(i)]);
27 maxY = std::max(maxY, ys[size_t(i)]);
28 }
29 minX -= gridCell;
30 minY -= gridCell;
31 maxX += gridCell;
32 maxY += gridCell;
33 gridOriginX = minX;
34 gridOriginY = minY;
35 gridW = int((maxX - minX) / gridCell) + 1;
36 gridH = int((maxY - minY) / gridCell) + 1;
37
38 const size_t cells = size_t(gridW) * size_t(gridH);
39 cellCount.assign(cells, 0);
40 for (int i = 0; i < n; ++i) {
41 int cx = int((xs[size_t(i)] - minX) / gridCell);
42 int cy = int((ys[size_t(i)] - minY) / gridCell);
43 cx = std::clamp(cx, 0, gridW - 1);
44 cy = std::clamp(cy, 0, gridH - 1);
45 ++cellCount[size_t(cy * gridW + cx)];
46 }
47 cellStart.assign(cells, 0);
48 int running = 0;
49 for (size_t c = 0; c < cells; ++c) {
50 cellStart[c] = running;
51 running += cellCount[c];
52 }
54 sorted.resize(size_t(n));
55 for (int i = 0; i < n; ++i) {
56 int cx = int((xs[size_t(i)] - minX) / gridCell);
57 int cy = int((ys[size_t(i)] - minY) / gridCell);
58 cx = std::clamp(cx, 0, gridW - 1);
59 cy = std::clamp(cy, 0, gridH - 1);
60 const int c = cy * gridW + cx;
61 sorted[size_t(cursor[size_t(c)]++)] = i;
62 }
63}
64
66 const int n = int(actions.size());
67 const bool hasField = field.isBuilt();
68 if (avoidance.enabled) {
69 avoidanceMaxSpeed = n == 0 ? 0.f : *std::max_element(maxSpeeds.begin(), maxSpeeds.end());
70 avoidanceMaxRadius = n == 0 ? 0.f : *std::max_element(radii.begin(), radii.end());
71 }
72 nextVxs.resize(size_t(n));
73 nextVys.resize(size_t(n));
74 preferredVxs.resize(size_t(n));
75 preferredVys.resize(size_t(n));
76 arriveFactors.resize(size_t(n), 1.f);
77 for (int i = 0; i < n; ++i) {
78 if (interactions[size_t(i)].holdPosition) {
79 preferredVxs[size_t(i)] = preferredVys[size_t(i)] = 0.f;
80 arriveFactors[size_t(i)] = 1.f;
81 continue;
82 }
83 const float x = xs[size_t(i)];
84 const float y = ys[size_t(i)];
85 const float maxSpeed = maxSpeeds[size_t(i)];
86 const int action = actions[size_t(i)];
87 float arriveK = 1.f; // flow 模式到达减速系数(1=全速,0=停)
88
89 // 基础期望方向。
90 float desX = 0.f, desY = 0.f;
91 if (action == kFlow) {
92 float fx = 0.f, fy = 0.f;
93 if (hasField) field.flowAtWorld(x, y, fx, fy);
94 // 接近目标时按积分代价线性减速(arrive),避免在目标格附近振荡。
95 float k = 1.f;
96 if (hasField) {
97 const float c = field.costAtWorld(x, y);
98 const float cell = std::max(field.getCellSize(), 1e-6f);
99 const float arriveCells = arriveRadius / cell;
100 if (c < CrowdField::kUnreachable && arriveCells > 0.f && c < arriveCells) {
101 k = c / arriveCells;
102 arriveK = k;
103 }
104 }
105 desX = fx * k;
106 desY = fy * k;
107 } else if (action == kSeek && hasTargets[size_t(i)]) {
108 const float tx = targetXs[size_t(i)] - x;
109 const float ty = targetYs[size_t(i)] - y;
110 const float d = std::sqrt(tx * tx + ty * ty);
111 if (d > 1e-3f) {
112 desX = tx / d;
113 desY = ty / d;
114 if (d < arriveRadius) {
115 const float k = d / arriveRadius;
116 desX *= k;
117 desY *= k;
118 }
119 }
120 } else if (action == kBoids && hasTargets[size_t(i)] && goalWeight > 0.f) {
121 const float tx = targetXs[size_t(i)] - x;
122 const float ty = targetYs[size_t(i)] - y;
123 const float d = std::sqrt(tx * tx + ty * ty);
124 if (d > 1e-3f) {
125 desX = tx / d * goalWeight;
126 desY = ty / d * goalWeight;
127 }
128 }
129 desX *= maxSpeed;
130 desY *= maxSpeed;
131
132 // Boids 三力。
133 if (sepWeight > 0.f || alignWeight > 0.f || cohesionWeight > 0.f) {
134 const float queryR = std::max(sepRadius, perceiveRadius);
135 float sx = 0.f, sy = 0.f;
136 float axv = 0.f, ayv = 0.f;
137 float cxv = 0.f, cyv = 0.f;
138 int alignN = 0, cohN = 0;
139 forEachNeighbor(x, y, queryR, [&](int j) {
140 if (!canInteract(size_t(i), size_t(j))) return;
141 const float dx = xs[size_t(j)] - x;
142 const float dy = ys[size_t(j)] - y;
143 const float d2 = dx * dx + dy * dy;
144 if (d2 <= 0.f) return;
145 if (sepWeight > 0.f && d2 <= sepRadius * sepRadius) {
146 const float d = std::sqrt(d2);
147 const float fall = 1.f - d / sepRadius;
148 sx += (x - xs[size_t(j)]) / d * fall;
149 sy += (y - ys[size_t(j)]) / d * fall;
150 }
151 if (alignWeight > 0.f && d2 <= perceiveRadius * perceiveRadius) {
152 axv += vxs[size_t(j)];
153 ayv += vys[size_t(j)];
154 ++alignN;
155 }
156 if (cohesionWeight > 0.f && d2 <= perceiveRadius * perceiveRadius) {
157 cxv += xs[size_t(j)];
158 cyv += ys[size_t(j)];
159 ++cohN;
160 }
161 });
162 desX += sx * sepWeight * maxSpeed;
163 desY += sy * sepWeight * maxSpeed;
164 if (alignN > 0) {
165 const float al = std::sqrt(axv * axv + ayv * ayv);
166 if (al > 1e-4f) {
167 desX += axv / al * alignWeight * maxSpeed;
168 desY += ayv / al * alignWeight * maxSpeed;
169 }
170 }
171 if (cohN > 0) {
172 const float cmx = cxv / float(cohN) - x;
173 const float cmy = cyv / float(cohN) - y;
174 const float cl = std::sqrt(cmx * cmx + cmy * cmy);
175 if (cl > 1e-4f) {
176 desX += cmx / cl * cohesionWeight * maxSpeed;
177 desY += cmy / cl * cohesionWeight * maxSpeed;
178 }
179 }
180 }
181
182 // Boids wander。
183 if (action == kBoids && wanderWeight > 0.f) {
184 wanderPhases[size_t(i)] += dt * 2.5f;
185 desX += std::cos(wanderPhases[size_t(i)]) * wanderWeight * maxSpeed;
186 desY += std::sin(wanderPhases[size_t(i)]) * wanderWeight * maxSpeed;
187 }
188
189 // 期望速度限幅。
190 float dl = std::sqrt(desX * desX + desY * desY);
191 if (dl > maxSpeed && dl > 1e-6f) {
192 desX *= maxSpeed / dl;
193 desY *= maxSpeed / dl;
194 }
195
196 preferredVxs[size_t(i)] = desX;
197 preferredVys[size_t(i)] = desY;
198 arriveFactors[size_t(i)] = arriveK;
199 }
200 for (int i = 0; i < n; ++i) {
201 if (interactions[size_t(i)].holdPosition) {
202 nextVxs[size_t(i)] = nextVys[size_t(i)] = speeds[size_t(i)] = 0.f;
203 continue;
204 }
205 const float desX = preferredVxs[size_t(i)], desY = preferredVys[size_t(i)];
206 const float maxSpeed = maxSpeeds[size_t(i)];
207 const float arriveK = arriveFactors[size_t(i)];
208 // 加速度限幅。
209 float avx = vxs[size_t(i)];
210 float avy = vys[size_t(i)];
211 float ddx = desX - avx;
212 float ddy = desY - avy;
213 const float ddl = std::sqrt(ddx * ddx + ddy * ddy);
214 const float maxDelta = std::max(maxAccels[size_t(i)] * dt, 0.f);
215 if (ddl > maxDelta && ddl > 1e-6f) {
216 ddx *= maxDelta / ddl;
217 ddy *= maxDelta / ddl;
218 }
219 avx += ddx;
220 avy += ddy;
221
222 if (avoidance.enabled) selectAvoidanceVelocity(size_t(i), dt, avx, avy);
223
224 // 速度限幅。
225 float sp = std::sqrt(avx * avx + avy * avy);
226 if (sp > maxSpeed && sp > 1e-6f) {
227 avx *= maxSpeed / sp;
228 avy *= maxSpeed / sp;
229 sp = maxSpeed;
230 }
231 nextVxs[size_t(i)] = avx;
232 nextVys[size_t(i)] = avy;
233 speeds[size_t(i)] = sp;
234
235 // 接近目标时对速度做阻尼,快速消除目标点附近的来回振荡。
236 if (arriveK < 1.f) {
237 const float damp = 1.f - (1.f - arriveK) * std::min(1.f, dt * 4.f);
238 nextVxs[size_t(i)] *= damp;
239 nextVys[size_t(i)] *= damp;
240 speeds[size_t(i)] *= damp;
241 }
242
243 // 转向:heading 以 turnRate·dt 为上限向速度方向收敛。
244 const float spAfter = speeds[size_t(i)];
245 if (spAfter > 1e-4f) {
246 const float targetAng = std::atan2(nextVys[size_t(i)], nextVxs[size_t(i)]);
247 const float delta = wrapPi(targetAng - headings[size_t(i)]);
248 const float maxTurn = std::max(turnRates[size_t(i)] * dt, 0.f);
249 headings[size_t(i)] += std::clamp(delta, -maxTurn, maxTurn);
250 }
251 }
252 vxs.swap(nextVxs);
253 vys.swap(nextVys);
254 for (int i = 0; i < n; ++i) {
255 xs[size_t(i)] += vxs[size_t(i)] * dt;
256 ys[size_t(i)] += vys[size_t(i)] * dt;
257 }
258}
259
261 const int n = int(actions.size());
262 correctionXs.assign(size_t(n), 0.f);
263 correctionYs.assign(size_t(n), 0.f);
264 const float maxRadius = radii.empty() ? 0.f : *std::max_element(radii.begin(), radii.end());
265 float maxPenetration = 0.f;
266 for (int i = 0; i < n; ++i) {
267 const float xi = xs[size_t(i)];
268 const float yi = ys[size_t(i)];
269 forEachNeighbor(xi, yi, radii[size_t(i)] + maxRadius, [&](int j) {
270 if (j <= i || !canInteract(size_t(i), size_t(j))) return;
271 float dx = xs[size_t(j)] - xi;
272 float dy = ys[size_t(j)] - yi;
273 float d2 = dx * dx + dy * dy;
274 const float minD = radii[size_t(i)] + radii[size_t(j)];
275 if (d2 >= minD * minD) return;
276 const bool exactOverlap = d2 <= 1e-9f;
277 if (exactOverlap) {
278 const std::string left = stableIds[size_t(i)].empty() ? std::to_string(i) : stableIds[size_t(i)];
279 const std::string right = stableIds[size_t(j)].empty() ? std::to_string(j) : stableIds[size_t(j)];
280 const std::string& first = left < right ? left : right;
281 const std::string& second = left < right ? right : left;
282 std::uint64_t hash = 1469598103934665603ULL;
283 for (const unsigned char value : first + "\n" + second) {
284 hash ^= value;
285 hash *= 1099511628211ULL;
286 }
287 const float angle = static_cast<float>(hash % 104729ULL) / 104729.0f * 6.28318530717958647692f;
288 dx = std::cos(angle);
289 dy = std::sin(angle);
290 if (left > right) {
291 dx = -dx;
292 dy = -dy;
293 }
294 d2 = 1.0f;
295 }
296 const float d = std::sqrt(d2);
297 const float push = minD - (exactOverlap ? 0.0f : d);
298 maxPenetration = std::max(maxPenetration, push);
299 const float nx = dx / d;
300 const float ny = dy / d;
301 float leftShare = 0.5f;
302 float rightShare = 0.5f;
303 const auto& leftPolicy = interactions[size_t(i)];
304 const auto& rightPolicy = interactions[size_t(j)];
305 const float leftMobility = leftPolicy.holdPosition ? 0.f : leftPolicy.pushability;
306 const float rightMobility = rightPolicy.holdPosition ? 0.f : rightPolicy.pushability;
307 if (leftMobility + rightMobility == 0.f) return;
308 leftShare = leftMobility / (leftMobility + rightMobility);
309 rightShare = 1.f - leftShare;
310 // Preserve the existing priority preference only when both can yield.
311 // Priority never overrides an explicit immovable/hold policy.
312 if (leftMobility > 0.f && rightMobility > 0.f &&
313 avoidancePriorities[size_t(i)] > avoidancePriorities[size_t(j)]) {
314 leftShare = 0.0f;
315 rightShare = 1.0f;
316 } else if (leftMobility > 0.f && rightMobility > 0.f &&
317 avoidancePriorities[size_t(j)] > avoidancePriorities[size_t(i)]) {
318 leftShare = 1.0f;
319 rightShare = 0.0f;
320 }
321 correctionXs[size_t(i)] -= nx * push * leftShare;
322 correctionYs[size_t(i)] -= ny * push * leftShare;
323 correctionXs[size_t(j)] += nx * push * rightShare;
324 correctionYs[size_t(j)] += ny * push * rightShare;
325 });
326 }
327 for (int i = 0; i < n; ++i) {
328 // Bound dense-cluster corrections so a single pass cannot eject an agent.
329 const float length = std::hypot(correctionXs[size_t(i)], correctionYs[size_t(i)]);
330 const float limit = std::max(radii[size_t(i)], 1e-4f);
331 const float scale = length > limit ? limit / length : 1.f;
332 xs[size_t(i)] += correctionXs[size_t(i)] * scale;
333 ys[size_t(i)] += correctionYs[size_t(i)] * scale;
334 }
335 return maxPenetration;
336}
337
339 if (!field.valid()) return;
340 const float width = float(field.getWidth()) * field.getCellSize();
341 const float height = float(field.getHeight()) * field.getCellSize();
342 for (size_t i = 0; i < xs.size(); ++i) {
343 for (int pass = 0; pass < 4; ++pass) {
344 const float oldX = xs[i], oldY = ys[i];
345 field.resolvePenetration(xs[i], ys[i], radii[i]);
346 if (clampToField) {
347 // Field origin is the lower cell corner, consistently with sampling.
348 const float rx = std::min(radii[i], width * 0.5f);
349 const float ry = std::min(radii[i], height * 0.5f);
350 xs[i] = std::clamp(xs[i], field.getOriginX() + rx, field.getOriginX() + width - rx);
351 ys[i] = std::clamp(ys[i], field.getOriginY() + ry, field.getOriginY() + height - ry);
352 }
353 const float dx = xs[i] - oldX, dy = ys[i] - oldY;
354 const float length2 = dx * dx + dy * dy;
355 if (length2 == 0.f) break;
356 // Remove velocity into the constraint; retaining it causes permanent
357 // walking-in-place when a requested destination lies outside the field.
358 const float inward = vxs[i] * dx + vys[i] * dy;
359 if (inward < 0.f) {
360 vxs[i] -= dx * inward / length2;
361 vys[i] -= dy * inward / length2;
362 speeds[i] = std::hypot(vxs[i], vys[i]);
363 }
364 }
365 }
366}
367
368} // namespace eve::crowd
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
float length
Definition CaveMesh.cpp:94
Vec3 radii
Definition CaveMesh.cpp:56
float nx
float ny
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
glm::vec3 n
Definition Grass.cpp:63
HexVec3 left
HexVec3 right
std::int32_t second
std::int32_t c
std::int32_t first
std::uint32_t height
std::uint32_t width
std::array< float, 3 > scale
MeleePoint3 a
Definition MeleeHit.cpp:40
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
std::string action
Definition PlayHost.cpp:117
std::vector< ActionSpec > actions
Definition PlayHost.cpp:126
float d
float dy
float dx
Cell cell
int limit
Definition TreeMesh.cpp:164
float angle
std::vector< int32_t > cellCount
Per-frame counting-sort broadphase workspace. @cost Linear in grid cells plus agent count when rebuil...
std::vector< float > xs
SOA agent storage, indexed by compact slot id. @cost Linear in agent count; mutation keeps all owned ...
std::vector< int32_t > cellStart
std::vector< float > ys
std::vector< int32_t > sorted
std::vector< int32_t > cursor
std::vector< int32_t > actions