载入中...
搜索中...
未找到
VegetationMotion.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <cmath>
4#include <glm/common.hpp>
5#include <glm/geometric.hpp>
6#include <glm/matrix.hpp>
7#include <limits>
8#include <new>
9
10namespace eve::graphics {
11namespace {
12bool finite(glm::vec2 p) { return std::isfinite(p.x) && std::isfinite(p.y); }
13bool finite(glm::vec3 p) { return finite(glm::vec2(p)) && std::isfinite(p.z); }
14bool finite(glm::vec4 p) { return finite(glm::vec3(p)) && std::isfinite(p.w); }
15bool unit(float p) { return std::isfinite(p) && p >= 0.f && p <= 1.f; }
16bool nonnegative(float p) { return std::isfinite(p) && p >= 0.f && p <= 1000000.f; }
17float saturate(float v) { return std::clamp(v, 0.f, 1.f); }
18double fract(double v) { return v - std::floor(v); }
20 return Diagnostic::error(DiagnosticCode::InvalidArgument, "invalid vegetation motion input or output overflow", {},
21 {}, "graphics.vegetation");
22}
23glm::vec4 repeatNoise(const VegetationMask& texture, glm::dvec2 uv) {
24 if (!std::isfinite(uv.x) || !std::isfinite(uv.y)) return glm::vec4(std::numeric_limits<float>::quiet_NaN());
25 const double x = fract(uv.x) * texture.width - 0.5;
26 const double y = fract(uv.y) * texture.height - 0.5;
27 const int x0 = int(std::floor(x)), y0 = int(std::floor(y));
28 auto pixel = [&](int px, int py) {
29 const auto ix = uint32_t((px + int(texture.width)) % int(texture.width));
30 const auto iy = uint32_t((py + int(texture.height)) % int(texture.height));
31 return texture.pixels[size_t(iy) * texture.width + ix];
32 };
33 return glm::mix(glm::mix(pixel(x0, y0), pixel(x0 + 1, y0), float(fract(x))),
34 glm::mix(pixel(x0, y0 + 1), pixel(x0 + 1, y0 + 1), float(fract(x))), float(fract(y)));
35}
36glm::vec3 rotate(glm::vec3 position, glm::vec3 axis, float angle) {
37 const auto onAxis = axis * glm::dot(axis, position);
38 const auto other = position - onAxis;
39 return onAxis + other * std::cos(angle) + glm::cross(axis, other) * std::sin(angle);
40}
41struct Frame {
44 double time;
45};
46Frame makeFrame(const VegetationMotion& motion, VegetationGlobals globals) {
47 Frame frame;
48 frame.inverseModel = glm::inverse(glm::mat3(motion.objectToWorld));
49 frame.normalMatrix = glm::transpose(frame.inverseModel);
50 for (int i = 0; i < 3; ++i)
51 frame.parentScale[i] =
52 1.f / glm::length(glm::vec3(frame.inverseModel[0][i], frame.inverseModel[1][i], frame.inverseModel[2][i]));
53 frame.globalDirection = {globals.motion.x, 0.f, globals.motion.y};
54 frame.time =
55 std::lerp(motion.time, motion.time * motion.timeScale + motion.timeOffset, double(motion.timeOverride));
56 return frame;
57}
58glm::vec3 directionInObject(const Frame& frame, glm::vec3 worldDirection) {
59 return (frame.inverseModel * worldDirection) * frame.parentScale;
60}
61struct LocalDeformation {
62 glm::vec3 position;
63 float highlight;
64};
65LocalDeformation deformLocal(const VegetationVertex& v, glm::vec3 position, const VegetationSample& field,
66 const VegetationMotion& motion, const Frame& frame) {
67 const bool batched = motion.batchMode == VegetationBatchMode::Batched;
68 const auto pivot = motion.usePivots ? v.pivot : glm::vec3(0.f);
69 const auto world = glm::vec3(motion.objectToWorld * glm::vec4(position, 1.f));
70 const auto objectWorld = batched ? world : glm::vec3(motion.objectToWorld * glm::vec4(pivot, 1.f));
71 const auto shifted = world - motion.worldOrigin;
72 const auto objectShifted = objectWorld - motion.worldOrigin;
73 const double positionHash = std::sin(double(objectShifted.x) * 12.9898 + double(objectShifted.z) * 78.233);
74 const float variation = std::clamp(
75 batched ? v.variation : float(fract(positionHash * (1.f - motion.dynamicMode) + v.variation)), 0.01f, 0.99f);
76 const auto noisePosition = glm::mix(shifted, objectShifted, motion.rigidity);
77 const glm::dvec2 uv = glm::dvec2(noisePosition.x, noisePosition.z) *
78 double((motion.bendingScale + 0.2f) * motion.noiseTiling * 0.0075f);
79 const double flowTime = (frame.time * motion.bendingSpeed + motion.bendingVariation * variation) * 0.03;
80 const double cycle = fract(flowTime);
81 const glm::dvec2 wind(frame.globalDirection.x, frame.globalDirection.z);
82 const auto noise =
83 glm::mix(repeatNoise(motion.noise, uv - wind * cycle),
84 repeatNoise(motion.noise, uv - wind * fract(flowTime + 0.5)), float(std::abs(cycle - 0.5) / 0.5));
85 const auto noiseDirection = directionInObject(frame, {noise.r * 2.f - 1.f, 0.f, noise.g * 2.f - 1.f});
86 const auto windDirection = directionInObject(frame, frame.globalDirection);
87 const auto reactDirection = directionInObject(frame, {field.motion.x, 0.f, field.motion.y});
88 const float rawPower = saturate(field.motion.z);
89 const float power = 1.f - (1.f - rawPower) * (1.f - rawPower);
90 const float bendingMask = 2.f * (batched ? v.bending * v.bending * v.boundsHeight : v.bending);
91 const auto bendDirection = glm::mix(noiseDirection, windDirection, power * 0.6f);
92 const auto bending = bendDirection * (bendingMask * motion.bending * power * power * motion.globalBending);
93 const float interactionRemap =
94 saturate(std::clamp(v.bending, 0.0001f, 0.9999f) / (motion.interactionMask + 0.0001f));
95 const float interactionMask =
96 2.f * (batched ? interactionRemap * interactionRemap * v.boundsHeight : interactionRemap);
97 const auto interaction = reactDirection * (interactionMask * motion.interaction);
98 const float interactionStrength = saturate(field.motion.w);
99 const float interactionWeight = saturate(motion.interaction * std::pow(interactionStrength, 4.f));
100 const auto angles = glm::mix(bending, interaction, interactionWeight);
101 const double sum = double(shifted.x) + shifted.y + shifted.z;
102 const double branchPhase = sum * motion.branchScale * 0.1;
103 const float sineA =
104 float(std::sin(branchPhase + motion.branchVariation * variation + frame.time * motion.branchSpeed));
105 const float sineB = float(std::sin(frame.time * motion.branchSpeed * 0.6842 + branchPhase));
106 const float localLength = glm::length(position);
107 const auto normalPosition = localLength > 1e-8f ? position / localLength : glm::vec3(0.f);
108 const float facing =
109 batched
110 ? 1.f
111 : std::max(std::lerp(1.f, glm::dot(normalPosition, -reactDirection) * 0.5f + 0.5f, motion.facing), 0.001f);
112 const float noiseBlue = std::abs(noise.b);
113 const float branchAmplitude = facing * power * std::pow(noiseBlue, std::lerp(1.8f, 0.4f, power));
114 const float squashScale = (std::max(sineA, sineB) * 0.5f + 0.5f) * branchAmplitude * motion.branch * v.branch *
115 v.boundsRadius * motion.globalBranch;
116 const glm::vec3 squash = glm::vec3(reactDirection.x, sineA * 0.3f, reactDirection.z) * squashScale;
117 const float rolling = sineA * branchAmplitude * motion.rolling * v.branch * motion.globalBranch;
118 const glm::dvec2 flutterUv =
119 glm::dvec2(shifted.x, shifted.z) * double(motion.flutterScale * 0.03f) +
120 glm::dvec2(motion.flutterVariation * variation + frame.time * motion.flutterSpeed * 0.02);
121 const auto flutterNoise = glm::vec3(repeatNoise(motion.noise, flutterUv)) * 2.f - 1.f;
122 const float fadeEnd = motion.fadeDistance + 0.01f;
123 const float fade = saturate((glm::distance(world, motion.camera) - fadeEnd) / (-fadeEnd * 0.5f + 0.0001f));
124 const float flutterAmplitude = motion.flutter * facing * fade * v.flutter * power * motion.globalFlutter *
125 std::pow(noiseBlue, std::lerp(2.4f, 0.6f, power));
126 const auto flutter = flutterNoise * flutterAmplitude;
127 auto result = position - pivot;
128 if (batched)
129 result += glm::vec3(angles.x, 0.f, angles.z) + squash + flutter;
130 else {
131 result = rotate(result, {1, 0, 0}, angles.z);
132 result = rotate(result, {0, 0, 1}, -angles.x);
133 result = rotate(result + squash, {0, 1, 0}, rolling) + flutter;
134 }
135 auto view = motion.camera - world;
136 const float viewLength = glm::length(view);
137 view = viewLength > 1e-8f ? view / viewLength : glm::vec3(0.f);
138 const auto cross = glm::cross(view, glm::vec3(0, 1, 0));
139 const auto push = frame.inverseModel * glm::vec3(-cross.z, 0, cross.x) * motion.perspectivePush;
140 const auto jitter = glm::vec3(variation * 2.f - 1.f, 0, variation * 2.f - 1.f) * motion.perspectiveNoise;
141 result += (push + jitter) * v.bending * std::pow(std::abs(view.y), motion.perspectiveAngle);
142 if (!batched) {
143 const float size = std::lerp(1.f, saturate(field.vertex.w), motion.globalSize);
144 const float fadeSize =
145 saturate((glm::distance(motion.camera, objectWorld) / motion.distanceFadeBias - motion.sizeFadeEnd) /
146 (motion.sizeFadeStart - motion.sizeFadeEnd + 0.0001f));
147 result *= size * fadeSize;
148 }
149 return {result + pivot, std::abs(noise.a) * power * fade * v.bending};
150}
151bool validMotion(const VegetationMotion& m) {
152 if (!std::isfinite(m.time) || std::abs(m.time) > 1e12 || !std::isfinite(m.timeScale) ||
153 std::abs(m.timeScale) > 1e6 || !std::isfinite(m.timeOffset) || std::abs(m.timeOffset) > 1e12 ||
154 !unit(m.timeOverride) || !finite(m.camera) || !finite(m.worldOrigin) || !unit(m.dynamicMode) ||
155 !unit(m.rigidity) || !unit(m.facing) || !unit(m.interactionMask) || !unit(m.globalSize) || m.motionLayer > 8 ||
156 m.vertexLayer > 8 ||
157 (m.batchMode != VegetationBatchMode::Object && m.batchMode != VegetationBatchMode::Batched))
158 return false;
159 for (float value :
160 {m.bending, m.bendingSpeed, m.bendingScale, m.bendingVariation, m.branch, m.rolling,
161 m.branchSpeed, m.branchScale, m.branchVariation, m.flutter, m.flutterSpeed, m.flutterScale,
162 m.flutterVariation, m.globalBending, m.globalBranch, m.globalFlutter, m.noiseTiling, m.interaction,
163 m.fadeDistance, m.perspectivePush, m.perspectiveNoise, m.perspectiveAngle})
164 if (!nonnegative(value)) return false;
165 if (!std::isfinite(m.sizeFadeStart) || !std::isfinite(m.sizeFadeEnd) || m.sizeFadeStart < 0 ||
166 m.sizeFadeEnd - m.sizeFadeStart < 0.001f || !std::isfinite(m.distanceFadeBias) || m.distanceFadeBias < 0.0001f)
167 return false;
168 for (int column = 0; column < 4; ++column)
169 if (!finite(m.objectToWorld[column])) return false;
170 if (m.objectToWorld[0][3] != 0.f || m.objectToWorld[1][3] != 0.f || m.objectToWorld[2][3] != 0.f ||
171 m.objectToWorld[3][3] != 1.f)
172 return false;
173 const float determinant = glm::determinant(glm::mat3(m.objectToWorld));
174 if (!std::isfinite(determinant) || std::abs(determinant) < 1e-12f) return false;
175 const auto& texture = m.noise;
176 if (texture.width == 0 || texture.height == 0 || texture.width > 2048 || texture.height > 2048 ||
177 texture.pixels.size() != size_t(texture.width) * texture.height)
178 return false;
179 for (auto pixel : texture.pixels)
180 if (!finite(pixel) || !unit(pixel.r) || !unit(pixel.g) || !unit(pixel.b) || !unit(pixel.a)) return false;
181 return true;
182}
183} // namespace
184
185Result<VegetationGeometry> deformVegetation(const VegetationField& field, std::span<const VegetationVertex> vertices,
186 const VegetationMotion& motion) {
187 if (!validMotion(motion) || vertices.size() > 4 * 1024 * 1024)
188 return Result<VegetationGeometry>::failure(invalid());
189 const bool authored = !vertices.empty() && vertices.front().tangent.has_value();
190 for (const auto& v : vertices) {
191 if (v.tangent.has_value() != authored) return Result<VegetationGeometry>::failure(invalid());
192 if (v.tangent) {
193 const auto t = glm::vec3(*v.tangent);
194 const auto cross = glm::cross(v.normal, t);
195 if (!finite(t) || (v.tangent->w != 1.f && v.tangent->w != -1.f) || !finite(cross) ||
196 glm::length(cross) < 1e-6f)
197 return Result<VegetationGeometry>::failure(invalid());
198 }
199 }
200 for (const auto& v : vertices)
201 if (!finite(v.position) || !finite(v.normal) || !finite(v.pivot) || !unit(v.bending) || !unit(v.branch) ||
202 !unit(v.flutter) || !unit(v.variation) || !unit(v.occlusion) || !unit(v.detail) ||
203 !nonnegative(v.boundsHeight) || !nonnegative(v.boundsRadius) || !finite(v.texcoord) ||
204 !finite(v.secondaryTexcoord) || !finite(v.detailCoord) || glm::length(v.normal) < 1e-6f ||
205 !std::isfinite(glm::length(v.normal)))
206 return Result<VegetationGeometry>::failure(invalid());
207 const auto frame = makeFrame(motion, field.globalValues());
208 try {
209 VegetationGeometry geometry;
210 geometry.positions.reserve(vertices.size() * 3);
211 geometry.normals.reserve(vertices.size() * 3);
212 geometry.vegetationFactors.reserve(vertices.size() * 5);
213 geometry.motionHighlights.reserve(vertices.size());
214 if (authored) {
215 geometry.tangents.reserve(vertices.size() * 3);
216 geometry.bitangents.reserve(vertices.size() * 3);
217 }
218 for (const auto& v : vertices) {
219 const auto samplePosition = motion.batchMode == VegetationBatchMode::Batched
220 ? v.position
221 : (motion.usePivots ? v.pivot : glm::vec3(0.f));
222 auto sample = field.sample(glm::vec3(motion.objectToWorld * glm::vec4(samplePosition, 1.f)),
223 {0, 0, motion.motionLayer, motion.vertexLayer});
224 if (!sample.ok()) return Result<VegetationGeometry>::failure(sample.status());
225 const auto& f = sample.value();
226 const auto local = deformLocal(v, v.position, f, motion, frame);
227 const auto p = glm::vec3(motion.objectToWorld * glm::vec4(local.position, 1.f));
228 const auto baseNormal = glm::normalize(v.normal);
229 const auto axis = std::abs(baseNormal.y) < 0.9f ? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0);
230 const auto tangent =
231 authored
232 ? glm::normalize(glm::vec3(*v.tangent) - baseNormal * glm::dot(baseNormal, glm::vec3(*v.tangent)))
233 : glm::normalize(glm::cross(axis, baseNormal));
234 const auto bitangent = glm::cross(baseNormal, tangent);
235 constexpr float epsilon = 0.001f;
236 const auto dp = deformLocal(v, v.position + tangent * epsilon, f, motion, frame).position -
237 deformLocal(v, v.position - tangent * epsilon, f, motion, frame).position;
238 const auto dq = deformLocal(v, v.position + bitangent * epsilon, f, motion, frame).position -
239 deformLocal(v, v.position - bitangent * epsilon, f, motion, frame).position;
240 const auto cross = glm::cross(dp, dq);
241 const auto n =
242 glm::normalize(frame.normalMatrix * (glm::length(cross) > 1e-12f ? glm::normalize(cross) : baseNormal));
243 if (!finite(p) || !finite(n) || !std::isfinite(local.highlight))
244 return Result<VegetationGeometry>::failure(invalid());
245 geometry.motionHighlights.push_back(local.highlight);
246 geometry.vegetationFactors.insert(geometry.vegetationFactors.end(),
247 {v.variation, v.occlusion, v.detail, v.detailCoord.x, v.detailCoord.y});
248 geometry.positions.insert(geometry.positions.end(), {p.x, p.y, p.z});
249 geometry.normals.insert(geometry.normals.end(), {n.x, n.y, n.z});
250 if (authored) {
251 // A collapsed surface has no derivative frame; retain its transformed rest directions.
252 const auto model = glm::mat3(motion.objectToWorld);
253 const auto t = glm::normalize(model * (glm::length(dp) > 1e-12f ? dp : tangent));
254 const auto b = glm::normalize(model * (glm::length(dq) > 1e-12f ? dq : bitangent)) * v.tangent->w;
255 if (!finite(t) || !finite(b)) return Result<VegetationGeometry>::failure(invalid());
256 geometry.tangents.insert(geometry.tangents.end(), {t.x, t.y, t.z});
257 geometry.bitangents.insert(geometry.bitangents.end(), {b.x, b.y, b.z});
258 }
259 }
260 return Result<VegetationGeometry>::success(std::move(geometry));
261 } catch (const std::bad_alloc&) {
263 DiagnosticCode::Failed, "vegetation geometry allocation failed", {}, {}, "graphics.vegetation"));
264 }
265}
266} // namespace eve::graphics
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float uv
Vec3 tangent
Definition CaveMesh.cpp:80
float py
glm::vec4 p[6]
int column
tensor::Graph g
Definition GpuGraph.cpp:7
glm::vec3 n
Definition Grass.cpp:63
double r
HexDirection windDirection
float v
std::vector< Colorf > px
std::string local
std::array< float, 3 > position
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
ModuleFacing facing
bool finite
float f
World3D * world
std::vector< Point > vertices
float t
uint8_t * pixels
glm::mat4 view
glm::mat4 model
float power
Definition RockMesh.cpp:23
TacticalUnit * unit
float size
Definition TreeMesh.cpp:156
float highlight
glm::vec3 parentScale
glm::vec3 globalDirection
glm::mat3 inverseModel
glm::mat3 normalMatrix
float m[16]
float angle
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Backend-independent, owning vegetation influence field. Single writer; concurrent const evaluation is...
eve::Diagnostic Diagnostic
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
Result< VegetationGeometry > deformVegetation(const VegetationField &field, std::span< const VegetationVertex > vertices, const VegetationMotion &motion)
Evaluate plant bending, squash, rolling, flutter, interaction and perspective. Worker-safe with immut...
Result< int > invalid(std::string message)
Invalid.
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36
const EditorValue * field(const EditorValue &value, const char *name)
int axis(int64_t a, size_t rank)
Axis.
Owning geometry projection, packed world XYZ positions and unit normals.
std::vector< float > vegetationFactors
Interleaved variation, occlusion, detail mask and detail UV, five floats per vertex.
std::vector< float > motionHighlights
Owning per-rest-vertex motion highlight scalar, parallel to positions/3. Evaluated before raster inte...
std::vector< float > tangents
Owning deformed world XYZ frame streams; both empty when source tangents are absent.
Source-equivalent plant motion parameters with explicit time and texture inputs. All data is owned....