8#include <unordered_map>
18 static FrustumPlanes fromViewProjColumnMajor(
const float *m16) {
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);
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;
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));
44 bool intersectsAABB(
const glm::vec3 &bmin,
const glm::vec3 &bmax)
const {
45 for (
int i = 0; i < 6; ++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;
60 bool operator==(
const CellKey &o)
const {
return x == o.x &&
y == o.y &&
z == o.z; }
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;
78 if (!(cellSize > 0.f) || !std::isfinite(cellSize)) {
81 "hair.cluster.cellSize"));
84 std::unordered_map<CellKey, size_t, CellKeyHash> cellToCluster;
85 cellToCluster.reserve(
strands.curveCount());
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;
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));
96 size_t clusterIndex = 0;
97 auto it = cellToCluster.find(
key);
98 if (it == cellToCluster.end()) {
99 clusterIndex = clusters_.size();
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);
106 clusterIndex = it->second;
117 if (clusters_.empty()) {
127 if (
index >= clusters_.size())
return nullptr;
128 return &clusters_[
index];
135 "hair.cluster.viewProj"));
137 for (
int i = 0; i < 16; ++i) {
138 if (!std::isfinite(viewProj16[i])) {
141 "hair.cluster.viewProj"));
145 const FrustumPlanes
frustum = FrustumPlanes::fromViewProjColumnMajor(viewProj16);
147 visible.reserve(clusters_.size());
148 for (
size_t i = 0; i < clusters_.size(); ++i) {
150 if (
frustum.intersectsAABB(
c.aabbMin,
c.aabbMax)) {
151 visible.push_back(uint32_t(i));
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"));
167 const auto &src = clusters_[ci].curveIndices;
168 curves.insert(curves.end(), src.begin(), src.end());
170 std::sort(curves.begin(), curves.end());
171 curves.erase(std::unique(curves.begin(), curves.end()), curves.end());
176 if (clusters_.empty()) {
177 outMin = outMax = glm::vec3(0.f);
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);
189 const std::vector<uint32_t> &curveIndices) {
193 std::vector<StrandPoint>
points;
194 std::vector<StrandCurve> curves;
196 curves.reserve(curveIndices.size());
198 for (uint32_t ci : curveIndices) {
202 "hair.filter.curve"));
205 if (pts.size() < 2)
continue;
208 curve.pointCount = uint32_t(pts.size());
210 for (
size_t pi = 0; pi < pts.size(); ++pi) {
211 points.push_back(pts[pi]);
215 curves.push_back(
curve);
Stable, structured diagnostics shared by engine modules.
std::array< float, 3 > position
std::shared_ptr< const std::vector< glm::vec2 > > points
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.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
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.
size_t curveCount() const
std::span< const StrandPoint > curvePoints(size_t curveIndex) const
Borrowed points of one curve.
size_t pointCount() const
Procedural hair/fur card meshes + material/LOD helpers (no strand simulation).
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.
One spatial cluster of strand curves (UE cluster-culling analogue). @ownership Owned by ClusterGrid.
std::vector< uint32_t > curveIndices
Curve indices into the source StrandsDatas.
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 ...