载入中...
搜索中...
未找到
VegetationWind.cpp
浏览该文件的文档.
2#include <algorithm>
3#include <cmath>
4#include <limits>
5namespace eve::graphics {
6namespace {
7bool finite(glm::dvec3 value) { return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z); }
8bool representable(glm::dvec3 value) {
9 const double limit = std::numeric_limits<float>::max();
10 return finite(value) && std::abs(value.x) <= limit && std::abs(value.y) <= limit && std::abs(value.z) <= limit;
11}
12bool valid(const VegetationWindState& state) {
13 return finite(state.direction) && std::isfinite(state.strength) && state.strength >= 0 && state.strength <= 1.2F &&
14 std::isfinite(state.phase) && state.phase >= 0 && std::isfinite(state.updatePhase) && state.updatePhase >= 0;
15}
16double saturate(double x) { return std::clamp(x, 0.0, 1.0); }
17} // namespace
19 double seconds) {
20 if (!valid(state) || !finite(profile.flex) || !finite(profile.frequency) ||
21 !std::isfinite(profile.maximumDistance) || profile.maximumDistance <= 0 || !std::isfinite(seconds))
22 return Result<std::array<float, 14>>::failure(
24 "vegetation.wind: finite valid globals, dimensions and transform required"));
25 const float distance =
26 !profile.enabled ? 0 : (profile.billboard ? -profile.maximumDistance : profile.maximumDistance);
27 return Result<std::array<float, 14>>::success(
28 {state.direction.x, state.direction.y, state.direction.z, state.strength, state.phase, distance, profile.flex.x,
29 profile.flex.y, profile.alphaTest ? profile.flex.z : 0, profile.frequency.x, profile.frequency.y,
30 profile.frequency.z, float(std::sin(seconds * 0.25)), float(std::sin(seconds))});
31}
33 if (!finite(forward) || !std::isfinite(strength))
36 "vegetation.wind: finite valid globals, dimensions and transform required"));
37 const double main = std::clamp(double(strength), 0.0, double(1.2F));
38 const glm::dvec3 direction{
39 forward.x, -std::max(double(forward.y), std::hypot(double(forward.x), double(forward.z)) * 0.5), forward.z};
40 const double phase = std::pow(main * 0.5 + 0.5, 3) * 0.1;
41 if (!representable(direction) || !std::isfinite(phase))
44 "vegetation.wind: finite valid globals, dimensions and transform required"));
45 state = {glm::vec3(direction), float(main), float(phase), state.updatePhase};
46 return Result<void>::success();
47}
48Result<void> advanceVegetationWind(VegetationWindState& state, glm::vec3 forward, float strength, float dt) {
49 if (!valid(state) || !finite(forward) || !std::isfinite(strength) || !std::isfinite(dt) || dt < 0)
52 "vegetation.wind: finite valid globals, dimensions and transform required"));
53 const double alpha = saturate(double(dt) * 0.25);
54 const glm::dvec3 target{
55 forward.x, -std::max(double(forward.y), std::hypot(double(forward.x), double(forward.z)) * 0.5), forward.z};
56 const auto direction = glm::mix(glm::dvec3(state.direction), target, alpha);
57 const double main =
58 double(state.strength) + (std::clamp(double(strength), 0.0, double(1.2F)) - state.strength) * alpha;
59 double phase = double(state.updatePhase) + double(dt) * std::pow(main * 0.5 + 0.5, 3) * 0.1;
60 if (phase > 100) phase -= 100;
61 if (!representable(direction) || !std::isfinite(phase) || phase > std::numeric_limits<float>::max())
64 "vegetation.wind: finite valid globals, dimensions and transform required"));
65 state = {glm::vec3(direction), float(main), float(phase), float(phase)};
66 return Result<void>::success();
67}
69 float dt, bool enabled, bool clipAvailable) {
70 if (!std::isfinite(state.currentWindSpeed) || !std::isfinite(state.anchorVolume) ||
71 !std::isfinite(state.volume) || state.volume < 0 || !std::isfinite(windStrength) ||
72 !std::isfinite(transitionTime) || transitionTime <= 0 || !std::isfinite(dt) || dt < 0)
75 "vegetation.wind: finite valid globals, dimensions and transform required"));
76 if (!enabled) return Result<void>::success();
77 auto next = state;
78 if (windStrength != next.currentWindSpeed) {
79 next.anchorVolume = next.volume;
80 next.processing = true;
81 }
82 if (next.processing && clipAvailable) {
83 next.playing = true;
84 const double target = saturate(windStrength);
85 const double alpha = saturate(double(dt) / transitionTime);
86 next.volume = float(double(next.anchorVolume) + (target - next.anchorVolume) * alpha);
87 if (next.anchorVolume >= target) {
88 if (next.volume <= target - 0.05) next.volume = float(target), next.processing = false;
89 } else if (next.volume >= target - 0.05) {
90 next.volume = float(target);
91 next.processing = false;
92 }
93 }
94 state = next;
95 return Result<void>::success();
96}
98 if (!valid(state) || !finite(in.position) || !finite(in.worldOffset) || !finite(in.cameraPosition) ||
99 !finite(in.flex) || !finite(in.frequency) || !std::isfinite(in.widthHeight.x) || in.widthHeight.x <= 0 ||
100 !std::isfinite(in.widthHeight.y) || in.widthHeight.y <= 0 || !std::isfinite(in.maximumDistance) ||
101 in.maximumDistance <= 0 || !std::isfinite(in.sinTimeQuarter) || std::abs(in.sinTimeQuarter) > 1 ||
102 !std::isfinite(in.sinTimeFull) || std::abs(in.sinTimeFull) > 1)
105 "vegetation.wind: finite valid globals, dimensions and transform required"));
106 glm::dmat3 matrix(in.objectToWorld);
107 for (int c = 0; c < 3; ++c)
108 if (!finite(matrix[c]))
111 "vegetation.wind: finite valid globals, dimensions and transform required"));
112 const double determinant = glm::determinant(matrix);
113 if (!std::isfinite(determinant) || determinant == 0)
116 "vegetation.wind: finite valid globals, dimensions and transform required"));
117 if (!in.enabled) return Result<glm::vec3>::success(in.position);
118 const auto distance = (glm::dvec3(in.worldOffset) - glm::dvec3(in.cameraPosition)) / double(in.maximumDistance);
119 double attenuation = 1 - saturate(glm::dot(distance, distance));
120 attenuation *= attenuation;
121 if (attenuation == 0 || state.strength == 0) return Result<glm::vec3>::success(in.position);
122 const double main = state.strength;
123 glm::dvec3 flex = glm::dvec3(in.flex) * glm::dvec3(saturate(main * 3), saturate(main * 2), 1 - main * main * 0.5) *
124 main * attenuation;
125 const auto up = matrix * glm::dvec3(0, 1, 0);
126 const double length = glm::length(up), scale = std::max(length, 0.4), upDot = saturate(up.y / length);
127 const auto dimensions =
128 glm::mix(glm::dvec2(double(in.widthHeight.y) * 2), glm::dvec2(in.widthHeight), upDot * upDot) * scale;
129 flex *= scale;
130 const auto frequency = glm::dvec3(in.frequency) * scale;
131 auto local = matrix * glm::dvec3(in.position);
132 const auto world = local + glm::dvec3(in.worldOffset);
133 const double phase = double(state.phase) * 6, time = -(phase - std::floor(phase)) * 6.283185;
134 const auto norm = local / glm::dvec3(dimensions.x, dimensions.y, dimensions.x);
135 const double branch = norm.x * norm.x + norm.z * norm.z, stem = norm.y, lengthA = glm::dot(local, local);
136 double gust =
137 ((std::sin(time + frequency.x * (double(in.worldOffset.x) + in.worldOffset.y + in.worldOffset.z)) * 0.3 +
138 main * 0.5) +
139 (double(in.sinTimeQuarter) * 0.4 + main) * main) *
140 (double(in.sinTimeFull) * 0.3 + 0.7);
141 const auto direction = glm::dvec3(state.direction);
142 glm::dvec3 tally{direction.x * stem * stem * gust * flex.x, 0, direction.z * stem * stem * gust * flex.x};
143 gust = gust * 0.7 + 0.3;
144 if (!in.billboard) {
145 const auto displaced = world + tally * 0.25;
146 tally += direction * stem * stem *
147 (std::sin(time * 2 + (displaced.x + displaced.y + displaced.z) * frequency.y) * branch * 0.7 + 0.3) *
148 gust * flex.y;
149 }
150 const auto offset = local + tally;
151 const double denominator = glm::dot(offset, offset);
152 const double normalization = denominator == 0 ? 0 : saturate(lengthA / denominator);
153 tally = offset * normalization;
154 local = tally;
155 if (!in.billboard && in.alphaTest) {
156 const auto offsets = (world + tally) * frequency.z;
157 glm::dvec3 wave{std::sin(time * 5 + offsets.x), std::sin(time * 5 + offsets.y), std::sin(time * 5 + offsets.z)};
158 local += (wave * direction + direction) * glm::dvec3(branch, branch * 0.75, branch) * (stem * gust * flex.z) *
159 (normalization + 0.5);
160 }
161 const auto result = glm::inverse(matrix) * local;
162 if (!representable(result))
165 "vegetation.wind: finite valid globals, dimensions and transform required"));
166 return Result<glm::vec3>::success(glm::vec3(result));
167}
168} // namespace eve::graphics
LogicalId target
double value
float x
Definition AnimClip.cpp:738
float phase
Definition CaveMesh.cpp:58
float length
Definition CaveMesh.cpp:94
float windStrength
HexVec3 up
std::int32_t c
std::string local
size_t offset
std::array< float, 3 > scale
bool valid
float distance
bool finite
World3D * world
RoadLaneDirection direction
int limit
Definition TreeMesh.cpp:164
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
int main(int argc, char **argv)
Definition main.cpp:46
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
Result< std::array< float, 14 > > packVegetationWind(const VegetationWindState &state, const VegetationWindProfile &profile, double seconds)
Pack a wind snapshot into the grass shader's final 14 float slots.
Result< void > advanceVegetationWind(VegetationWindState &state, glm::vec3 forward, float strength, float dt)
Advance source WindManager smoothing and phase using explicit nonnegative dt. Direction Y follows -ma...
Result< void > initializeVegetationWind(VegetationWindState &state, glm::vec3 forward, float strength)
Apply Pcg WindManager.InstantWindApply semantics without smoothing.
Result< glm::vec3 > evaluateVegetationWind(const VegetationWindInput &in, const VegetationWindState &state)
Evaluate the source main/branch/leaf deformation for one vertex without mutation. Requires finite inp...
Result< void > advanceVegetationWindAudio(VegetationWindAudioState &state, float windStrength, float transitionTime, float dt, bool enabled, bool clipAvailable)
Advance Pcg WindManager CheckWindVolume and ProcessWindAudio as a pure state transition.
bool enabled
Caller-owned state for Pcg WindManager's looping wind-audio volume controller.
Explicit source PW_GeneralWind inputs in object/world coordinates.
float sinTimeQuarter
Inject Unity-equivalent sin(time/4) and sin(time) values.
Caller-owned material wind controls; billboard suppresses source branch and leaf motion....
Caller-owned wind globals; no hidden clock, singleton or retained scene link.