载入中...
搜索中...
未找到
Guides.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4
5#include <algorithm>
6#include <cstddef>
7#include <cmath>
8#include <limits>
9#include <utility>
10
11#include <glm/geometric.hpp>
12
13namespace eve::graphics::hair {
14namespace {
15
16glm::vec3 rootOf(const StrandsDatas &datas, size_t curveIndex) {
17 const auto pts = datas.curvePoints(curveIndex);
18 return pts.empty() ? glm::vec3(0.f) : pts[0].position;
19}
20
21glm::vec3 sampleCurveAtU(const StrandsDatas &datas, size_t curveIndex, float u) {
22 const auto pts = datas.curvePoints(curveIndex);
23 if (pts.empty()) return {};
24 if (pts.size() == 1) return pts[0].position;
25 const float t = std::clamp(u, 0.f, 1.f);
26 // Prefer explicit per-point u; fall back to index parameterization.
27 size_t i1 = 1;
28 while (i1 + 1 < pts.size() && pts[i1].u < t) ++i1;
29 const size_t i0 = i1 - 1;
30 const float u0 = pts[i0].u;
31 const float u1 = pts[i1].u;
32 float alpha = 0.f;
33 if (std::fabs(u1 - u0) > 1e-8f) {
34 alpha = (t - u0) / (u1 - u0);
35 } else {
36 alpha = float(i1 - i0) > 0.f ? 1.f : 0.f;
37 }
38 alpha = std::clamp(alpha, 0.f, 1.f);
39 return pts[i0].position * (1.f - alpha) + pts[i1].position * alpha;
40}
41
42float weightExponent(InterpolationMode mode) {
43 switch (mode) {
45 return 4.f; // strongly prefer nearest guide
47 return 1.f;
49 default:
50 return 2.f;
51 }
52}
53
54} // namespace
55
57 auto ok = strands.validate();
58 if (!ok.ok()) return Result<StrandsDatas>::failure(ok.status());
59 if (strands.curveCount() == 0) {
61 DiagnosticCode::InvalidArgument, "extractGuides: empty strands", "hair.guides"));
62 }
63
64 const float frac = std::clamp(guideFraction, 0.01f, 1.f);
65 const size_t keep =
66 std::max<size_t>(1, size_t(std::ceil(double(strands.curveCount()) * double(frac))));
67 const size_t step = std::max<size_t>(1, strands.curveCount() / keep);
68
69 std::vector<StrandPoint> points;
70 std::vector<StrandCurve> curves;
71 points.reserve(strands.pointCount());
72 curves.reserve(keep);
73
74 for (size_t ci = 0; ci < strands.curveCount() && curves.size() < keep; ci += step) {
75 const auto pts = strands.curvePoints(ci);
76 if (pts.size() < 2) continue;
78 curve.pointOffset = uint32_t(points.size());
79 curve.pointCount = uint32_t(pts.size());
80 float len = 0.f;
81 for (size_t pi = 0; pi < pts.size(); ++pi) {
82 points.push_back(pts[pi]);
83 if (pi > 0) len += glm::length(pts[pi].position - pts[pi - 1].position);
84 }
85 curve.length = len;
86 curves.push_back(curve);
87 }
88
89 if (curves.empty()) {
91 DiagnosticCode::Failed, "extractGuides: no usable curves", "hair.guides"));
92 }
93
94 StrandsDatas out;
95 out.setPoints(std::move(points));
96 out.setCurves(std::move(curves));
97 auto valid = out.validate();
98 if (!valid.ok()) return Result<StrandsDatas>::failure(valid.status());
99 return Result<StrandsDatas>::success(std::move(out));
100}
101
103buildGuideWeights(const StrandsDatas &strands, const StrandsDatas &guides, int maxInfluences,
104 InterpolationMode mode) {
105 auto sOk = strands.validate();
106 if (!sOk.ok()) return Result<std::vector<StrandGuideWeights>>::failure(sOk.status());
107 auto gOk = guides.validate();
108 if (!gOk.ok()) return Result<std::vector<StrandGuideWeights>>::failure(gOk.status());
109 if (guides.curveCount() == 0) {
111 DiagnosticCode::InvalidArgument, "buildGuideWeights: no guides", "hair.guides"));
112 }
113
114 const int k = std::clamp(maxInfluences, 1, StrandGuideWeights::kMaxInfluences);
115 const float exp = weightExponent(mode);
116 std::vector<StrandGuideWeights> out(strands.curveCount());
117
118 for (size_t si = 0; si < strands.curveCount(); ++si) {
119 const glm::vec3 root = rootOf(strands, si);
120 struct Cand {
121 uint32_t index;
122 float dist2;
123 };
124 std::vector<Cand> cands;
125 cands.reserve(guides.curveCount());
126 for (size_t gi = 0; gi < guides.curveCount(); ++gi) {
127 const glm::vec3 d = rootOf(guides, gi) - root;
128 cands.push_back({uint32_t(gi), glm::dot(d, d)});
129 }
130 const size_t take = std::min(size_t(k), cands.size());
131 std::partial_sort(cands.begin(), cands.begin() + std::ptrdiff_t(take), cands.end(),
132 [](const Cand &a, const Cand &b) { return a.dist2 < b.dist2; });
133
135 float wSum = 0.f;
136 for (size_t i = 0; i < take; ++i) {
137 const float dist = std::sqrt(std::max(cands[i].dist2, 0.f));
138 const float w = 1.f / std::pow(std::max(dist, 1e-4f), exp);
139 row.influencers[row.count] = {cands[i].index, w};
140 wSum += w;
141 ++row.count;
142 }
143 if (wSum > 0.f) {
144 for (int i = 0; i < row.count; ++i) row.influencers[i].weight /= wSum;
145 } else if (row.count > 0) {
146 row.influencers[0].weight = 1.f;
147 for (int i = 1; i < row.count; ++i) row.influencers[i].weight = 0.f;
148 }
149 out[si] = row;
150 }
151 return Result<std::vector<StrandGuideWeights>>::success(std::move(out));
152}
153
155 const StrandsDatas &guidesRest,
156 const StrandsDatas &guidesDeformed,
157 const std::vector<StrandGuideWeights> &weights,
158 InterpolationMode mode) {
159 auto sOk = strandsRest.validate();
160 if (!sOk.ok()) return Result<StrandsDatas>::failure(sOk.status());
161 auto g0 = guidesRest.validate();
162 if (!g0.ok()) return Result<StrandsDatas>::failure(g0.status());
163 auto g1 = guidesDeformed.validate();
164 if (!g1.ok()) return Result<StrandsDatas>::failure(g1.status());
165 if (guidesRest.curveCount() != guidesDeformed.curveCount()) {
168 "interpolateStrands: guide rest/deformed curve count mismatch", "hair.guides"));
169 }
170 if (weights.size() != strandsRest.curveCount()) {
173 "interpolateStrands: weights size mismatch", "hair.guides.weights"));
174 }
175
176 std::vector<StrandPoint> points(strandsRest.points().begin(), strandsRest.points().end());
177 std::vector<StrandCurve> curves(strandsRest.curves().begin(), strandsRest.curves().end());
178
179 for (size_t si = 0; si < strandsRest.curveCount(); ++si) {
180 const StrandCurve &curve = curves[si];
181 const StrandGuideWeights &row = weights[si];
182 if (curve.pointCount == 0 || row.count <= 0) continue;
183
184 if (mode == InterpolationMode::Rigid) {
185 glm::vec3 delta(0.f);
186 for (int i = 0; i < row.count; ++i) {
187 const auto &inf = row.influencers[i];
188 const glm::vec3 rest = rootOf(guidesRest, inf.guideIndex);
189 const glm::vec3 def = rootOf(guidesDeformed, inf.guideIndex);
190 delta += (def - rest) * inf.weight;
191 }
192 for (uint32_t pi = 0; pi < curve.pointCount; ++pi) {
193 points[curve.pointOffset + pi].position += delta;
194 }
195 continue;
196 }
197
198 // Offset / Smooth: per-point guide deltas at matching u.
199 for (uint32_t pi = 0; pi < curve.pointCount; ++pi) {
200 StrandPoint &pt = points[curve.pointOffset + pi];
201 glm::vec3 delta(0.f);
202 for (int i = 0; i < row.count; ++i) {
203 const auto &inf = row.influencers[i];
204 const glm::vec3 restP = sampleCurveAtU(guidesRest, inf.guideIndex, pt.u);
205 const glm::vec3 defP = sampleCurveAtU(guidesDeformed, inf.guideIndex, pt.u);
206 delta += (defP - restP) * inf.weight;
207 }
208 pt.position += delta;
209 }
210 }
211
212 StrandsDatas out;
213 out.setPoints(std::move(points));
214 out.setCurves(std::move(curves));
215 auto valid = out.validate();
216 if (!valid.ok()) return Result<StrandsDatas>::failure(valid.status());
217 return Result<StrandsDatas>::success(std::move(out));
218}
219
220} // namespace eve::graphics::hair
float w
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
int strands
Stable, structured diagnostics shared by engine modules.
uint32_t i1
Definition Grass.cpp:61
uint32_t i0
Definition Grass.cpp:61
float u
Definition Grass.cpp:233
std::array< float, 3 > position
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::shared_ptr< const std::vector< glm::vec2 > > points
float d
float t
Heightmap curve
float weights[3]
float step
Definition TreeMesh.cpp:314
uint32_t index
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
CPU-authoritative strand buffer (UE FHairStrandsDatas analogue).
std::span< const StrandCurve > curves() const
Borrowed curves; invalidated by mutation.
Result< void > validate() const
Verify offsets, counts, and finite values.
void setCurves(std::vector< StrandCurve > curves)
Sets the curves.
void setPoints(std::vector< StrandPoint > points)
Sets the points.
std::span< const StrandPoint > points() const
Borrowed points; invalidated by mutation.
Procedural hair/fur card meshes + material/LOD helpers (no strand simulation).
Definition Graphics.h:58
InterpolationMode
How render strands follow deformed guides (UE interpolation analogue).
Definition Guides.h:19
Result< StrandsDatas > extractGuides(const StrandsDatas &strands, float guideFraction)
Pick a deterministic subset of curves as guides (density thinning).
Definition Guides.cpp:56
Result< StrandsDatas > interpolateStrands(const StrandsDatas &strandsRest, const StrandsDatas &guidesRest, const StrandsDatas &guidesDeformed, const std::vector< StrandGuideWeights > &weights, InterpolationMode mode)
Drive rest render strands with deformed guides using precomputed weights. @ownership Returned datas u...
Definition Guides.cpp:154
Result< std::vector< StrandGuideWeights > > buildGuideWeights(const StrandsDatas &strands, const StrandsDatas &guides, int maxInfluences, InterpolationMode mode)
Build k-nearest guide weights for every render strand (by root distance).
Definition Guides.cpp:103
One strand as a contiguous point range. @ownership Value type owned by StrandsDatas.
Up to kMaxInfluences guides influencing one render strand. @ownership Owned by the weights table retu...
Definition Guides.h:31
static constexpr int kMaxInfluences
Definition Guides.h:32
One control point on a hair curve (UE FHairStrandsDatas point analogue). @ownership Value type owned ...
float u
Root-to-tip parameter in [0, 1].