载入中...
搜索中...
未找到
ClusterGrid.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4
5#include <algorithm>
6#include <cmath>
7#include <limits>
8#include <unordered_map>
9
10#include <glm/glm.hpp>
11
12namespace eve::graphics::hair {
13namespace {
14
15struct FrustumPlanes {
16 float planes[6][4]{};
17
18 static FrustumPlanes fromViewProjColumnMajor(const float *m16) {
19 FrustumPlanes f;
20 auto m = [&](int row, int col) -> float { return m16[col * 4 + row]; };
21 auto set = [&](int i, float a, float b, float c, float d) {
22 const float len = std::sqrt(a * a + b * b + c * c);
23 if (len > 1e-8f) {
24 f.planes[i][0] = a / len;
25 f.planes[i][1] = b / len;
26 f.planes[i][2] = c / len;
27 f.planes[i][3] = d / len;
28 } else {
29 f.planes[i][0] = a;
30 f.planes[i][1] = b;
31 f.planes[i][2] = c;
32 f.planes[i][3] = d;
33 }
34 };
35 set(0, m(3, 0) + m(0, 0), m(3, 1) + m(0, 1), m(3, 2) + m(0, 2), m(3, 3) + m(0, 3));
36 set(1, m(3, 0) - m(0, 0), m(3, 1) - m(0, 1), m(3, 2) - m(0, 2), m(3, 3) - m(0, 3));
37 set(2, m(3, 0) + m(1, 0), m(3, 1) + m(1, 1), m(3, 2) + m(1, 2), m(3, 3) + m(1, 3));
38 set(3, m(3, 0) - m(1, 0), m(3, 1) - m(1, 1), m(3, 2) - m(1, 2), m(3, 3) - m(1, 3));
39 set(4, m(3, 0) + m(2, 0), m(3, 1) + m(2, 1), m(3, 2) + m(2, 2), m(3, 3) + m(2, 3));
40 set(5, m(3, 0) - m(2, 0), m(3, 1) - m(2, 1), m(3, 2) - m(2, 2), m(3, 3) - m(2, 3));
41 return f;
42 }
43
44 bool intersectsAABB(const glm::vec3 &bmin, const glm::vec3 &bmax) const {
45 for (int i = 0; i < 6; ++i) {
46 const float *p = planes[i];
47 const float x = p[0] >= 0.f ? bmax.x : bmin.x;
48 const float y = p[1] >= 0.f ? bmax.y : bmin.y;
49 const float z = p[2] >= 0.f ? bmax.z : bmin.z;
50 if (p[0] * x + p[1] * y + p[2] * z + p[3] < 0.f) return false;
51 }
52 return true;
53 }
54};
55
56struct CellKey {
57 int x = 0;
58 int y = 0;
59 int z = 0;
60 bool operator==(const CellKey &o) const { return x == o.x && y == o.y && z == o.z; }
61};
62
63struct CellKeyHash {
64 size_t operator()(const CellKey &k) const {
65 const size_t hx = size_t(uint32_t(k.x)) * 73856093u;
66 const size_t hy = size_t(uint32_t(k.y)) * 19349663u;
67 const size_t hz = size_t(uint32_t(k.z)) * 83492791u;
68 return hx ^ hy ^ hz;
69 }
70};
71
72} // namespace
73
75 clear();
76 auto ok = strands.validate();
77 if (!ok.ok()) return Result<void>::failure(ok.status());
78 if (!(cellSize > 0.f) || !std::isfinite(cellSize)) {
80 DiagnosticCode::InvalidArgument, "ClusterGrid::build: cellSize must be positive",
81 "hair.cluster.cellSize"));
82 }
83
84 std::unordered_map<CellKey, size_t, CellKeyHash> cellToCluster;
85 cellToCluster.reserve(strands.curveCount());
86
87 for (size_t ci = 0; ci < strands.curveCount(); ++ci) {
88 const auto pts = strands.curvePoints(ci);
89 if (pts.empty()) continue;
90 const glm::vec3 &root = pts[0].position;
91 CellKey key;
92 key.x = int(std::floor(root.x / cellSize));
93 key.y = int(std::floor(root.y / cellSize));
94 key.z = int(std::floor(root.z / cellSize));
95
96 size_t clusterIndex = 0;
97 auto it = cellToCluster.find(key);
98 if (it == cellToCluster.end()) {
99 clusterIndex = clusters_.size();
100 HairCluster cluster;
101 cluster.aabbMin = glm::vec3(std::numeric_limits<float>::max());
102 cluster.aabbMax = glm::vec3(std::numeric_limits<float>::lowest());
103 clusters_.push_back(std::move(cluster));
104 cellToCluster.emplace(key, clusterIndex);
105 } else {
106 clusterIndex = it->second;
107 }
108
109 HairCluster &cluster = clusters_[clusterIndex];
110 cluster.curveIndices.push_back(uint32_t(ci));
111 for (const StrandPoint &p : pts) {
112 cluster.aabbMin = glm::min(cluster.aabbMin, p.position);
113 cluster.aabbMax = glm::max(cluster.aabbMax, p.position);
114 }
115 }
116
117 if (clusters_.empty()) {
119 DiagnosticCode::Failed, "ClusterGrid::build: no clusters produced", "hair.cluster"));
120 }
121 return Result<void>::success();
122}
123
124void ClusterGrid::clear() { clusters_.clear(); }
125
127 if (index >= clusters_.size()) return nullptr;
128 return &clusters_[index];
129}
130
132 if (!viewProj16) {
134 DiagnosticCode::InvalidArgument, "ClusterGrid::cullClusters: null viewProj",
135 "hair.cluster.viewProj"));
136 }
137 for (int i = 0; i < 16; ++i) {
138 if (!std::isfinite(viewProj16[i])) {
140 DiagnosticCode::InvalidArgument, "ClusterGrid::cullClusters: non-finite matrix",
141 "hair.cluster.viewProj"));
142 }
143 }
144
145 const FrustumPlanes frustum = FrustumPlanes::fromViewProjColumnMajor(viewProj16);
146 std::vector<uint32_t> visible;
147 visible.reserve(clusters_.size());
148 for (size_t i = 0; i < clusters_.size(); ++i) {
149 const HairCluster &c = clusters_[i];
150 if (frustum.intersectsAABB(c.aabbMin, c.aabbMax)) {
151 visible.push_back(uint32_t(i));
152 }
153 }
154 return Result<std::vector<uint32_t>>::success(std::move(visible));
155}
156
158ClusterGrid::collectCurveIndices(const std::vector<uint32_t> &clusterIndices) const {
159 std::vector<uint32_t> curves;
160 for (uint32_t ci : clusterIndices) {
161 if (ci >= clusters_.size()) {
164 "ClusterGrid::collectCurveIndices: cluster index out of range",
165 "hair.cluster.index"));
166 }
167 const auto &src = clusters_[ci].curveIndices;
168 curves.insert(curves.end(), src.begin(), src.end());
169 }
170 std::sort(curves.begin(), curves.end());
171 curves.erase(std::unique(curves.begin(), curves.end()), curves.end());
172 return Result<std::vector<uint32_t>>::success(std::move(curves));
173}
174
175void ClusterGrid::computeBounds(glm::vec3 &outMin, glm::vec3 &outMax) const {
176 if (clusters_.empty()) {
177 outMin = outMax = glm::vec3(0.f);
178 return;
179 }
180 outMin = clusters_[0].aabbMin;
181 outMax = clusters_[0].aabbMax;
182 for (size_t i = 1; i < clusters_.size(); ++i) {
183 outMin = glm::min(outMin, clusters_[i].aabbMin);
184 outMax = glm::max(outMax, clusters_[i].aabbMax);
185 }
186}
187
189 const std::vector<uint32_t> &curveIndices) {
190 auto ok = src.validate();
191 if (!ok.ok()) return Result<StrandsDatas>::failure(ok.status());
192
193 std::vector<StrandPoint> points;
194 std::vector<StrandCurve> curves;
195 points.reserve(src.pointCount());
196 curves.reserve(curveIndices.size());
197
198 for (uint32_t ci : curveIndices) {
199 if (ci >= src.curveCount()) {
201 DiagnosticCode::InvalidArgument, "filterStrandsByCurves: curve index out of range",
202 "hair.filter.curve"));
203 }
204 const auto pts = src.curvePoints(ci);
205 if (pts.size() < 2) continue;
207 curve.pointOffset = uint32_t(points.size());
208 curve.pointCount = uint32_t(pts.size());
209 float len = 0.f;
210 for (size_t pi = 0; pi < pts.size(); ++pi) {
211 points.push_back(pts[pi]);
212 if (pi > 0) len += glm::length(pts[pi].position - pts[pi - 1].position);
213 }
214 curve.length = len;
215 curves.push_back(curve);
216 }
217
218 StrandsDatas out;
219 out.setPoints(std::move(points));
220 out.setCurves(std::move(curves));
221 auto valid = out.validate();
222 if (!valid.ok()) return Result<StrandsDatas>::failure(valid.status());
223 return Result<StrandsDatas>::success(std::move(out));
224}
225
226} // namespace eve::graphics::hair
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
int strands
float planes[6][4]
glm::vec4 p[6]
Stable, structured diagnostics shared by engine modules.
std::uint32_t key
std::int32_t c
std::array< float, 3 > position
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float f
std::shared_ptr< const std::vector< glm::vec2 > > points
float d
FrustumPlanes frustum
Heightmap curve
bool visible
uint32_t index
float m[16]
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
Result< std::vector< uint32_t > > collectCurveIndices(const std::vector< uint32_t > &clusterIndices) const
Collect unique curve indices from the given clusters (sorted ascending).
void computeBounds(glm::vec3 &outMin, glm::vec3 &outMax) const
Overall AABB of all clusters; empty grid → zero box at origin.
const HairCluster * clusterAt(size_t index) const
Cluster at.
Result< void > build(const StrandsDatas &strands, float cellSize)
Build clusters from validated strands.
Result< std::vector< uint32_t > > cullClusters(const float *viewProj16) const
Frustum-cull clusters against a column-major view-projection matrix.
CPU-authoritative strand buffer (UE FHairStrandsDatas analogue).
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 > curvePoints(size_t curveIndex) const
Borrowed points of one curve.
Procedural hair/fur card meshes + material/LOD helpers (no strand simulation).
Definition Graphics.h:58
Result< StrandsDatas > filterStrandsByCurves(const StrandsDatas &src, const std::vector< uint32_t > &curveIndices)
Copy a subset of curves into a new StrandsDatas (preserves point order).
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
Definition Widget.cpp:679
One spatial cluster of strand curves (UE cluster-culling analogue). @ownership Owned by ClusterGrid.
Definition ClusterGrid.h:18
std::vector< uint32_t > curveIndices
Curve indices into the source StrandsDatas.
Definition ClusterGrid.h:22
One strand as a contiguous point range. @ownership Value type owned by StrandsDatas.
One control point on a hair curve (UE FHairStrandsDatas point analogue). @ownership Value type owned ...