载入中...
搜索中...
未找到
SurfaceField.cpp
浏览该文件的文档.
2
3#include "common/Assert.h"
4
5#include <algorithm>
6#include <cmath>
7
8namespace eve::gpuagents {
9
10void SurfaceField::initFlat(const glm::vec3& originMin, float size, int res, float planeY) {
11 EV_PARAM_CHECK(res > 1);
12 EV_PARAM_CHECK(size > 0.f);
13 origin = originMin;
15 resolution = res;
16 const size_t n = static_cast<size_t>(res) * static_cast<size_t>(res);
17 height.assign(n, planeY);
18 normals.assign(n, glm::vec3(0.f, 1.f, 0.f));
19 lifeField.assign(n, glm::vec4(0.f));
20}
21
22glm::vec2 SurfaceField::worldToUv(const glm::vec3& p) const {
23 return glm::vec2((p.x - origin.x) / worldSize, (p.z - origin.z) / worldSize);
24}
25
26int SurfaceField::index(int x, int z) const {
27 return x + resolution * z;
28}
29
30bool SurfaceField::inBounds(int x, int z) const {
31 return x >= 0 && z >= 0 && x < resolution && z < resolution;
32}
33
34float SurfaceField::sampleHeight(float x, float z) const {
35 if (height.empty()) return 0.f;
36 const glm::vec2 uv = worldToUv(glm::vec3(x, 0.f, z));
37 const float fx = uv.x * static_cast<float>(resolution - 1);
38 const float fz = uv.y * static_cast<float>(resolution - 1);
39 const int x0 = std::clamp(static_cast<int>(std::floor(fx)), 0, resolution - 1);
40 const int z0 = std::clamp(static_cast<int>(std::floor(fz)), 0, resolution - 1);
41 const int x1 = std::min(x0 + 1, resolution - 1);
42 const int z1 = std::min(z0 + 1, resolution - 1);
43 const float tx = fx - static_cast<float>(x0);
44 const float tz = fz - static_cast<float>(z0);
45 const float h00 = height[static_cast<size_t>(index(x0, z0))];
46 const float h10 = height[static_cast<size_t>(index(x1, z0))];
47 const float h01 = height[static_cast<size_t>(index(x0, z1))];
48 const float h11 = height[static_cast<size_t>(index(x1, z1))];
49 return glm::mix(glm::mix(h00, h10, tx), glm::mix(h01, h11, tx), tz);
50}
51
52glm::vec3 SurfaceField::sampleNormal(float x, float z) const {
53 if (normals.empty()) return glm::vec3(0.f, 1.f, 0.f);
54 const glm::vec2 uv = worldToUv(glm::vec3(x, 0.f, z));
55 const int ix = std::clamp(static_cast<int>(std::floor(uv.x * resolution)), 0, resolution - 1);
56 const int iz = std::clamp(static_cast<int>(std::floor(uv.y * resolution)), 0, resolution - 1);
57 return normals[static_cast<size_t>(index(ix, iz))];
58}
59
60glm::vec4 SurfaceField::sampleLifeTexel(float u, float v) const {
61 const float fx = u * static_cast<float>(resolution - 1);
62 const float fz = v * static_cast<float>(resolution - 1);
63 const int x0 = std::clamp(static_cast<int>(std::floor(fx)), 0, resolution - 1);
64 const int z0 = std::clamp(static_cast<int>(std::floor(fz)), 0, resolution - 1);
65 const int x1 = std::min(x0 + 1, resolution - 1);
66 const int z1 = std::min(z0 + 1, resolution - 1);
67 const float tx = fx - static_cast<float>(x0);
68 const float tz = fz - static_cast<float>(z0);
69 const auto at = [&](int x, int z) { return lifeField[static_cast<size_t>(index(x, z))]; };
70 return glm::mix(glm::mix(at(x0, z0), at(x1, z0), tx), glm::mix(at(x0, z1), at(x1, z1), tx), tz);
71}
72
73glm::vec4 SurfaceField::sampleLife(float x, float z) const {
74 if (lifeField.empty()) return glm::vec4(0.f);
75 const glm::vec2 uv = worldToUv(glm::vec3(x, 0.f, z));
76 return sampleLifeTexel(std::clamp(uv.x, 0.f, 1.f), std::clamp(uv.y, 0.f, 1.f));
77}
78
79glm::vec3 SurfaceField::project(const glm::vec3& p) const {
80 return glm::vec3(p.x, sampleHeight(p.x, p.z), p.z);
81}
82
83void SurfaceField::stepLife(float dt, float decayRate, float halfLife, float diffusion,
84 const std::vector<glm::vec3>& deposits, float depositStrength) {
85 if (lifeField.empty()) return;
86 EV_PARAM_CHECK(dt >= 0.f);
87 const float decay = std::exp(-decayRate * dt);
88 const float freshDecay =
89 halfLife > 1e-4f ? std::exp(-std::log(2.f) * dt / halfLife) : 0.f;
90
91 std::vector<glm::vec4> next = lifeField;
92 const int res = resolution;
93 for (int z = 0; z < res; ++z) {
94 for (int x = 0; x < res; ++x) {
95 const size_t i = static_cast<size_t>(index(x, z));
96 glm::vec4 c = lifeField[i];
97 c.r *= decay;
98 c.a *= freshDecay;
99 if (diffusion > 0.f) {
100 glm::vec4 sum(0.f);
101 int count = 0;
102 for (int dz = -1; dz <= 1; ++dz) {
103 for (int dx = -1; dx <= 1; ++dx) {
104 if (dx == 0 && dz == 0) continue;
105 const int nx = x + dx;
106 const int nz = z + dz;
107 if (!inBounds(nx, nz)) continue;
108 sum += lifeField[static_cast<size_t>(index(nx, nz))];
109 ++count;
110 }
111 }
112 if (count > 0) {
113 const glm::vec4 avg = sum / static_cast<float>(count);
114 c.r = glm::mix(c.r, avg.r, diffusion);
115 c.g = glm::mix(c.g, avg.g, diffusion * 0.5f);
116 c.b = glm::mix(c.b, avg.b, diffusion * 0.25f);
117 }
118 }
119 next[i] = c;
120 }
121 }
122
123 for (const glm::vec3& p : deposits) {
124 const glm::vec2 uv = worldToUv(p);
125 if (uv.x < 0.f || uv.y < 0.f || uv.x >= 1.f || uv.y >= 1.f) continue;
126 const int x = std::clamp(static_cast<int>(uv.x * res), 0, res - 1);
127 const int z = std::clamp(static_cast<int>(uv.y * res), 0, res - 1);
128 auto& c = next[static_cast<size_t>(index(x, z))];
129 c.r = std::min(c.r + depositStrength, 1.f);
130 c.a = 1.f;
131 }
132 lifeField.swap(next);
133}
134
135} // namespace eve::gpuagents
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
EVEngine assertion entry point, backed by zeroerr.
#define EV_PARAM_CHECK(cond,...)
Validate a function parameter / public API precondition.
Definition Assert.h:30
float uv
float nx
float nz
glm::vec4 p[6]
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t c
float dz
float dx
std::uint32_t count
float size
Definition TreeMesh.cpp:156
uint32_t index
std::size_t at
std::vector< glm::vec4 > lifeField
Life field RGBA: R=trail, G=nutrient, B=danger, A=freshness.
glm::vec3 sampleNormal(float x, float z) const
Bilinear sample of surface normal.
glm::vec3 project(const glm::vec3 &p) const
Project a world point onto the surface (Y from height).
void stepLife(float dt, float decayRate, float halfLife, float diffusion, const std::vector< glm::vec3 > &deposits, float depositStrength)
Advance trail deposit / decay / diffusion / danger for one fixed step.
float sampleHeight(float x, float z) const
Bilinear sample of height at world XZ.
std::vector< glm::vec3 > normals
Packed normals as vec3 per texel.
std::vector< float > height
Height samples, resolution^2, row-major X then Z.
void initFlat(const glm::vec3 &originMin, float size, int res, float planeY=0.f)
Allocate grids and fill a flat plane at y = planeY.
glm::vec2 worldToUv(const glm::vec3 &p) const
World XZ → texel UV in [0,1].
glm::vec4 sampleLife(float x, float z) const
Bilinear sample of life RGBA.